/* ============================================================
   Entwurf 01 — "Grid"
   Alle Werte aus tokens.css. Keine rohen Farbwerte hier.
   ============================================================ */

/* --- Reset ---------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  background: var(--c-bg);
  color: var(--c-ink);
  font-family: var(--f-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  /* `clip` statt `hidden`: beides schneidet seitlichen Überstand ab, aber
     `hidden` macht die Seite zu einem scrollbaren Kasten — dadurch hört
     `position: sticky` (Hero-Zeile, Sektionstitel) in Safari auf zu haften.
     `clip` schneidet nur ab und lässt das Haften unangetastet. */
  overflow-x: clip;
}

img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
ul { list-style: none; padding: 0; }

::selection { background: var(--c-accent); color: var(--c-bg); }
:focus-visible { outline: 2px solid var(--c-accent); outline-offset: 3px; }

[id] { scroll-margin-block-start: calc(var(--header-h) + 1rem); }
/* Auf schmalen Bildschirmen hängt unter der Kopfleiste noch die Sektionsanzeige.
   Ohne diesen Zuschlag landet ein Sprungziel genau dahinter. */
@media (max-width: 1024px) {
  [id] { scroll-margin-block-start: calc(var(--header-h) + 3rem); }
}

/* --- Typografie-Grundregeln ------------------------------
   .mono ist die EINZIGE Stelle, an der die Monospace erscheint:
   Aufzählungen/Labels, KPI-Zahlen, CTAs. Sonst immer Inter.
   -------------------------------------------------------- */

.mono {
  font-family: var(--f-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-mono);
  letter-spacing: var(--tr-mono);
  text-transform: uppercase;
  color: var(--c-ink);
}
/* Nur echte Signale sind orange — sonst gibt es KEINE Farbabweichung.
   Absichtlich kein „graues Mono": Mono ist immer schwarz (Schriftregel). */
.mono--accent { color: var(--c-accent); }

/* .mono-lead — Mono-Fließzeile (Hero-Unterzeile): etwas größer, gleiche Regeln */
.mono-lead {
  font-family: var(--f-mono);
  font-size: var(--fs-mono-lead);
  font-weight: var(--fw-mono);
  letter-spacing: var(--tr-mono);
  text-transform: uppercase;
  color: var(--c-ink);
  line-height: 1.75;
}

/* .kpi — Kennzahl, ebenfalls Monospace (Regel) */
.kpi {
  font-family: var(--f-mono);
  font-size: var(--fs-kpi);
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--c-ink);
}

/* .dash — die Trenn-Ornamente: ··· ––– ///
   Werden als Textinhalt gesetzt, nicht als Grafik. */
.dash {
  font-family: var(--f-mono);
  font-size: var(--fs-mono);
  letter-spacing: var(--tr-mono);
  color: var(--c-line);
  user-select: none;
}

/* --- Layout ---------------------------------------------- */

/* .shell — zentrierter Container mit Außenrand (Header + Hero, breit) */
.shell {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
/* .shell--narrow — für die Inhaltssektionen unter dem Hero */
.shell--narrow { max-width: var(--container-body); }

/* .inset — der eingerückte Hauptinhalt.
   An seiner LINKEN Kante läuft der rote Faden (.pipe). */
.inset {
  /* WICHTIG: der Einzug kommt ZUSÄTZLICH zum Außenrand des .shell —
     sonst überschreibt er ihn und der rote Faden landet im Text. */
  padding-inline-start: calc(var(--gutter) + var(--inset));
  position: relative;
}

/* --- Der rote Faden (Pipe) -------------------------------
   Läuft senkrecht durch die ganze Seite an der Einzugskante.
   Aufbau: dünne Grundlinie + laufender Orange-Impuls darüber.
   -------------------------------------------------------- */

/* Die Striche stehen still — durch sie läuft ein Farbimpuls nach unten.
   Ebene 1: ruhige, helle Strichlinie. Ebene 2: dieselbe Linie in Orange,
   über eine wandernde Maske nur abschnittsweise sichtbar. */
.pipe {
  position: absolute;
  inset-block: 0;
  inset-inline-start: var(--pipe-x);
  inline-size: var(--pipe-w);
  pointer-events: none;
  background-image: repeating-linear-gradient(
    to bottom,
    var(--c-line) 0 8px,
    transparent 8px 16px
  );
  background-size: 100% 16px;
  opacity: 0.6;   /* bewusst zurückgenommen — er führt, er dominiert nicht */
}
.pipe::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background-image: repeating-linear-gradient(
    to bottom,
    var(--c-accent) 0 8px,
    transparent 8px 16px
  );
  background-size: 100% 16px;
  mask-image: linear-gradient(to bottom, transparent, var(--mask-solid) 45%, var(--mask-solid) 55%, transparent);
  -webkit-mask-image: linear-gradient(to bottom, transparent, var(--mask-solid) 45%, var(--mask-solid) 55%, transparent);
  mask-size: 100% 45%;
  -webkit-mask-size: 100% 45%;
  mask-repeat: no-repeat;
  -webkit-mask-repeat: no-repeat;
  animation: pipe-pulse 4.5s linear infinite;
}
@keyframes pipe-pulse {
  from { mask-position: 0 -50%;  -webkit-mask-position: 0 -50%; }
  to   { mask-position: 0 150%;  -webkit-mask-position: 0 150%; }
}
@media (prefers-reduced-motion: reduce) {
  .pipe::after { animation: none; opacity: 0.5; mask-image: none; -webkit-mask-image: none; }
}


/* --- Rasterhintergrund ----------------------------------- */

/* .grid-lines — senkrechte Hilfslinien über die ganze Seite */
.grid-lines {
  position: fixed;
  inset: 0;
  /* MUSS negativ sein. Mit `z-index: 0` ist das Raster zwar „hinten", aber die
     Malreihenfolge stellt positionierte Elemente über den Fließtext nicht
     positionierter Blöcke — die gestrichelten Linien lagen dadurch sichtbar auf
     der Schrift. Nur Sektionen, die selbst `position: relative; z-index: 1`
     tragen, waren davon verschont; der Rest nicht. */
  z-index: -1;
  pointer-events: none;
  display: grid;
  grid-template-columns: repeat(var(--grid-cols), 1fr);
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.grid-lines span { border-inline-start: var(--hairline) dashed var(--c-line-soft); }
/* Auch die beiden Außenkanten bekommen einen Balken — sonst ist das Raster
   links und rechts offen und wirkt nicht wie ein Kasten. */
.grid-lines span:last-of-type { border-inline-end: var(--hairline) dashed var(--c-line-soft); }

/* Mobil nur vier Rasterbalken — acht wären auf der schmalen Breite Streifenmuster.
   Da die Spalten 5–8 ausgeblendet sind, muss die vierte den rechten Außenbalken
   übernehmen — sonst fehlt die Kante. */
@media (max-width: 760px) {
  .grid-lines { grid-template-columns: repeat(4, 1fr); }
  .grid-lines span:nth-child(n + 5) { display: none; }
  .grid-lines span:nth-child(4) { border-inline-end: var(--hairline) dashed var(--c-line-soft); }
}

/* --- Kopfleiste ------------------------------------------
   Freistehend: kein Hintergrund, keine Trennlinie.
   Links Wortmarke, rechts Navigation + CTA.
   Beim Scrollen klappt die Navigation zum Burger.
   -------------------------------------------------------- */

.header {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 40;
  block-size: var(--header-h);
  display: flex;
  align-items: center;
}
/* Milchglas an der Kopfleiste — NUR zusammen mit der Sektionsanzeige.
   Die beiden gehören zusammen: Ohne Fläche scrollt der Inhalt sichtbar zwischen
   Kopf und Anzeige hindurch. Ab 1025px gibt es die Anzeige nicht (dort bleibt
   der Sektionskopf beim Scrollen von selbst stehen) — also darf dort auch der
   Kopf nicht anlaufen. Er ist bewusst transparent und schwebt.

   Die Begrenzung steht in BEIDEN Ebenen: hier in der Mediaquery und in app.js,
   das `data-label` nur unterhalb der Grenze setzt. Eine Ebene allein reichte
   nicht — das Merkmal wurde vorher auf jeder Breite gesetzt, der Desktop lief
   also mit an, obwohl die Anzeige gar nicht sichtbar war. */
@media (max-width: 1024px) {
  .header[data-label="true"] {
    /* Die Farbe kommt aus --c-bg, sie wechselt also mit dem Farbmodus von allein.
       `-webkit-` zuerst — Safari kennt die Eigenschaft nur mit Präfix.

       ZWEI Angaben, und das mit Absicht: Die erste ist deckend und gilt für
       alles, was `color-mix` nicht kennt. Die zweite überschreibt sie dort,
       wo es geht. Ohne die erste stünde bei einem Ausfall gar keine Fläche da.

       Die Deckkraft bleibt bei 72 % — das Milchglas funktioniert auf dem
       Gerät, eine Erhöhung wäre nur Kosmetik ohne Anlass gewesen. */
    background: var(--c-bg);
    background: color-mix(in srgb, var(--c-bg) 72%, transparent);
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
    transition: background var(--dur) var(--ease);
  }
  /* Kennt der Browser kein Milchglas, bleibt die volle Fläche — sonst stünde
     Text auf Text. */
  @supports not (backdrop-filter: blur(1px)) {
    .header[data-label="true"] { background: var(--c-bg); }
  }
}

.header__inner {
  /* Der Header läuft bündig bis an beide Ränder — nicht auf --container begrenzt */
  max-width: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  width: 100%;
}

/* .brand — bewusst leise: kleines Quadrat + Name, sonst nichts */
.brand { display: inline-flex; align-items: center; gap: 0.5rem; }
.brand__mark { width: 8px; height: 8px; background: var(--c-accent); }
.brand__name {
  font-weight: 500;
  letter-spacing: -0.015em;
  font-size: 0.9375rem;
}

/* --- Der Übergang oben → kompakt ------------------------
   Kein harter Umschalter: Die Navigation fährt nach rechts in den Button
   hinein, gleichzeitig schrumpft der Button und tauscht seinen Text.
   Die Breitenänderung läuft über `grid-template-columns` mit fr-Werten —
   die lassen sich animieren, `width: auto` nicht.
   -------------------------------------------------------- */

.header__end { display: flex; align-items: center; gap: var(--sp-3); }

/* .navbar — klappt seitlich zu, statt zu verschwinden */
.navbar {
  display: grid;
  grid-template-columns: 1fr;
  transition: grid-template-columns var(--dur) var(--ease),
              opacity var(--dur) var(--ease),
              translate var(--dur) var(--ease);
}
.navbar__links {
  display: flex;
  gap: var(--sp-3);
  white-space: nowrap;
  min-inline-size: 0;
  /* WICHTIG: hier NICHT `overflow: hidden` — das würde das Dropdown
     abschneiden. Beschnitten wird nur im kompakten Zustand, wo die Liste
     seitlich zuklappt (siehe unten). */
  overflow: visible;
}
.header[data-compact="true"] .navbar {
  /* minmax(0, 0fr): siehe ausführliche Begründung bei .action__swap.
     Nacktes `0fr` kollabiert nur in Chrome; Safari behält die Mindestbreite
     des Inhalts — hier also die komplette Hauptnavigation. Der Kopf wurde
     dadurch auf dem iPhone breiter als der Bildschirm. */
  grid-template-columns: minmax(0, 0fr);
  opacity: 0;
  translate: var(--sp-3) 0;   /* fährt nach rechts in den Button */
  pointer-events: none;
}
/* Erst beim Zuklappen beschneiden — vorher würde es das Dropdown kappen */
.header[data-compact="true"] .navbar__links { overflow: hidden; }

/* .action — EIN Button für beide Zustände */
.action { cursor: pointer; }

/* --- Farbmodus-Umschalter --------------------------------
   Bewusst kantig statt rund — er soll wie ein Schalter im Raster wirken.
   -------------------------------------------------------- */

.theme-switch {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0;          /* kein Eigenabstand — sonst sitzt er tiefer als die Navi */
  line-height: 1;
  cursor: pointer;
}
.theme-switch__label { line-height: 1; }
.theme-switch__track {
  position: relative;
  inline-size: 36px;
  block-size: 18px;
  border: var(--hairline) solid var(--c-line);
  background: var(--c-paper);
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.theme-switch__knob {
  position: absolute;
  inset-block: 2px;
  inset-inline-start: 2px;
  inline-size: 14px;
  background: var(--c-ink);
  transition: translate var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.theme-switch:hover .theme-switch__track { border-color: var(--c-accent); }
.theme-switch__label { color: var(--c-ink); }

/* Dunkelmodus: Knopf wandert nach rechts und trägt die Signalfarbe */
:root[data-theme="dark"] .theme-switch__knob {
  translate: 16px 0;
  background: var(--c-accent);
}

@media (max-width: 480px) { .theme-switch__label { display: none; } }

/* Burger-Striche wachsen erst im kompakten Zustand ein */
.action__bars {
  display: grid;
  gap: 3px;
  overflow: hidden;
  max-inline-size: 0;
  margin-inline-end: 0;
  opacity: 0;
  transition: max-inline-size var(--dur) var(--ease),
              margin-inline-end var(--dur) var(--ease),
              opacity var(--dur) var(--ease);
}
.action__bars i { display: block; inline-size: 14px; block-size: var(--hairline); background: currentColor; }
.header[data-compact="true"] .action__bars {
  max-inline-size: 14px;
  margin-inline-end: 0.55rem;
  opacity: 1;
}

/* Textwechsel: Beide Beschriftungen stehen nebeneinander; die gerade nicht
   gebrauchte wird auf Breite 0 zusammengezogen. Dadurch wächst und schrumpft
   der Button.

   FRÜHER lief das über zwei Rasterspalten, die von `1fr 0fr` auf `0fr 1fr`
   wechselten. Das war in Safari kaputt — in echtem WebKit gemessen:
   die Menütaste war 181 px statt 91 px, weil die `1fr`-Spalte dort 124 px
   belegte, obwohl „Menü" nur 32 px Text ist.

   Grund: Eine `fr`-Spalte teilt den VERFÜGBAREN Platz auf. Was das in einem
   Element ist, das sich selbst erst an seinen Inhalt anpasst (der Button ist
   `inline-flex`), legen die Browser unterschiedlich aus — Chrome nimmt die
   Textbreite, WebKit den Platz des Elternelements. Ein Zirkelschluss, auf den
   man sich nicht verlassen kann.

   JETZT über `max-inline-size` — dieselbe Mechanik, die die beiden Balken
   daneben schon benutzen und die in WebKit nachweislich richtig rechnet.
   Eine Obergrenze ist eindeutig: Sie beschreibt kein Verhältnis, das erst
   aufgelöst werden muss. */
.action__swap {
  display: flex;
  align-items: center;
  transition: translate var(--dur) var(--ease);
}
.action__swap > span {
  overflow: hidden;
  white-space: nowrap;
  min-inline-size: 0;
  /* Obergrenze, keine Vorgabe: Die Beschriftung bleibt so breit wie ihr Text,
     die Grenze greift erst beim Zuklappen. Bewusst großzügig — zu knapp würde
     sie den Text abschneiden. */
  max-inline-size: 12rem;
  transition: max-inline-size var(--dur) var(--ease),
              opacity var(--dur) var(--ease);
}
.action__swap > span:last-child { max-inline-size: 0; opacity: 0; }
.header[data-compact="true"] .action__swap > span:first-child { max-inline-size: 0; opacity: 0; }
.header[data-compact="true"] .action__swap > span:last-child { max-inline-size: 12rem; opacity: 1; }

/* Im kompakten Zustand ist der Button eine Menütaste: kein Pfeil — und damit
   auch KEIN Textversatz, sonst wandert „Menü" beim Hovern aus der Mitte. */
.header[data-compact="true"] .action .cta__label::before { content: none; }
.header[data-compact="true"] .action:hover .cta__label,
.header[data-compact="true"] .action:focus-visible .cta__label { translate: none; }

@media (max-width: 900px) {
  .header .navbar { grid-template-columns: minmax(0, 0fr); opacity: 0; pointer-events: none; }
  .header .navbar__links { overflow: hidden; }
  .header .action__bars { max-inline-size: 14px; margin-inline-end: 0.55rem; opacity: 1; }
  .header .action__swap > span:first-child { max-inline-size: 0; opacity: 0; }
  .header .action__swap > span:last-child { max-inline-size: 12rem; opacity: 1; }
  /* auch hier: Menütaste ohne Pfeil und ohne Textversatz */
  .header .action .cta__label::before { content: none; }
  .header .action:hover .cta__label,
  .header .action:focus-visible .cta__label { translate: none; }
}

.navbar__links a {
  font-family: var(--f-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-mono);
  letter-spacing: var(--tr-mono);
  text-transform: uppercase;
  position: relative;
  padding-block: 3px;
  transition: color var(--dur) var(--ease);
}
.navbar__links a::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  block-size: var(--hairline);
  background: var(--c-accent);
  scale: 0 1;
  transform-origin: left;
  transition: scale var(--dur) var(--ease);
}
.navbar__links a:hover::after { scale: 1 1; }

/* --- Dropdown in der Hauptnavigation ---------------------
   Öffnet bei Hover UND bei Tastaturfokus (`:focus-within`).
   -------------------------------------------------------- */

.has-drop { position: relative; }
/* Abstand über margin, NICHT über ein Leerzeichen in `content` —
   das wird beim Rendern verschluckt. */
.has-drop > a::before {
  content: "▾";
  color: var(--c-accent);
  margin-inline-end: 0.45em;
}

.drop {
  position: absolute;
  inset-block-start: calc(100% + 0.6rem);
  inset-inline-start: 50%;
  translate: -50% 0;
  z-index: 60;
  inline-size: 15rem;
  /* wie das Menü-Panel: in beiden Farbmodi dunkel */
  background: var(--c-bg-invert);
  color: var(--c-ink-invert);
  padding: var(--sp-1);
  display: grid;
  opacity: 0;
  visibility: hidden;
  scale: 0.97;
  transform-origin: top center;
  transition: opacity var(--dur) var(--ease),
              visibility var(--dur) var(--ease),
              scale var(--dur) var(--ease);
}
.has-drop:hover .drop,
.has-drop:focus-within .drop { opacity: 1; visibility: visible; scale: 1; }

/* Pfeil dreht sich, solange das Untermenü offen ist */
.has-drop > a::before { display: inline-block; transition: rotate var(--dur) var(--ease); }
.has-drop:hover > a::before,
.has-drop:focus-within > a::before { rotate: 180deg; }

/* unsichtbare Brücke, damit der Zeiger die Lücke überqueren kann */
.has-drop::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  inset-block-start: 100%;
  block-size: 0.7rem;
}

.drop a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2);
  padding: 0.55rem 0.7rem;
  font-family: var(--f-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-mono);
  letter-spacing: var(--tr-mono);
  text-transform: uppercase;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.drop a::after { display: none; }   /* kein Unterstrich wie in der Navi */
.drop a span { color: var(--c-accent); }
.drop a:hover { background: var(--c-accent); color: var(--c-bg); }
.drop a:hover span { color: var(--c-bg); }

/* --- Menü-Panel ------------------------------------------
   Kein Vollbild-Overlay: die Fläche wächst aus dem Button heraus.
   -------------------------------------------------------- */

.menu-wrap { position: relative; }
.action[aria-expanded="true"] { background: var(--c-accent); }

.menu {
  position: absolute;
  inset-inline-end: 0;
  inset-block-start: calc(100% + 6px);
  z-index: 60;
  inline-size: 17rem;
  max-inline-size: calc(100vw - 2 * var(--gutter));
  /* bleibt in BEIDEN Farbmodi dunkel — auf einer hellen Fläche wäre die
     Limette des Dunkelmodus nicht lesbar */
  background: var(--c-bg-invert);
  color: var(--c-ink-invert);
  padding: var(--sp-2);

  transform-origin: top right;
  scale: 0.94;
  opacity: 0;
  visibility: hidden;
  transition: scale var(--dur) var(--ease),
              opacity var(--dur) var(--ease),
              visibility var(--dur) var(--ease);
}
.menu[data-open="true"] { scale: 1; opacity: 1; visibility: visible; }

.menu__list { display: grid; }
.menu__list a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2);
  padding-block: 0.7rem;
  border-block-end: var(--hairline) dashed color-mix(in srgb, var(--c-ink-invert) 28%, transparent);
  font-family: var(--f-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-mono);
  letter-spacing: var(--tr-mono);
  text-transform: uppercase;
  transition: color var(--dur) var(--ease), padding-inline-start var(--dur) var(--ease);
}
.menu__list a:last-of-type { border-block-end: 0; }
/* Ein langes Etikett bricht um, statt in die Nummer zu laufen. Der Text ist
   ein namenloses Flex-Element und kann von sich aus nicht unter seine
   Mindestbreite; `min-width: 0` erlaubt ihm genau das. Die Nummer rechts
   behält ihre Breite. */
.menu__list a { min-inline-size: 0; }
.menu__list a span { flex: 0 0 auto; }
.menu__list a:hover { color: var(--c-accent); padding-inline-start: 0.4rem; }
.menu__list a span { color: var(--c-accent); }

.menu__cta {
  display: block;
  margin-block-start: var(--sp-2);
  text-align: center;
  background: var(--c-ink-invert);
  color: var(--c-bg-invert);
  padding: 0.7rem 1rem;
  font-family: var(--f-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-mono);
  letter-spacing: var(--tr-mono);
  text-transform: uppercase;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.menu__cta:hover { background: var(--c-accent); color: var(--c-bg); }

/* --- Untermenü im Burger-Menü ----------------------------
   Als <details>/<summary> gebaut: klappt auch ohne JavaScript auf.
   -------------------------------------------------------- */

.menu__sub summary {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2);
  padding-block: 0.7rem;
  border-block-end: var(--hairline) dashed color-mix(in srgb, var(--c-ink-invert) 28%, transparent);
  font-family: var(--f-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-mono);
  letter-spacing: var(--tr-mono);
  text-transform: uppercase;
  cursor: pointer;
  list-style: none;
  transition: color var(--dur) var(--ease);
}
.menu__sub summary::-webkit-details-marker { display: none; }
.menu__sub summary:hover { color: var(--c-accent); }

/* rechte Seite: Pfeil + Nummer — wie bei allen anderen Menüeinträgen */
.menu__sub-right { display: inline-flex; align-items: baseline; gap: 0.5rem; color: var(--c-accent); }
.menu__sub-arrow { font-style: normal; display: inline-block; transition: rotate var(--dur) var(--ease); }
.menu__sub[open] .menu__sub-arrow { rotate: 180deg; }

.menu__sub ul {
  display: grid;
  padding-block: 0.35rem;
  padding-inline-start: 0.8rem;
  border-inline-start: var(--pipe-w) solid var(--c-accent);
  margin-block: 0.5rem;
  margin-inline-start: 0.2rem;
}
.menu__sub ul a {
  padding-block: 0.45rem;
  border-block-end: 0;
  font-size: var(--fs-mono);
}

/* --- CTA -------------------------------------------------
   .cta ist der EINZIGE Button-Stil. Immer Monospace (Regel).
   -------------------------------------------------------- */

/* ---------------------------------------------------------
   .cta — DIE Button-Komponente.
   Drei Größen (--sm / --md / --lg) × zwei Rollen (--primary / --secondary).
   Immer Monospace, Versalien (Schriftregel).

   Verhalten beim Hovern:
   1. Die Füllung läuft von links nach rechts auf.
   2. Ein Pfeil erscheint direkt LINKS VOM TEXT — nicht am Buttonrand.
      Er hängt am Label (`::before` mit `inset-inline-end: 100%`), nicht am
      Buttonkasten. Dadurch sitzt er in jeder Größe gleich und klemmt nie.
   3. Der Text rückt um die halbe Pfeilbreite nach rechts, damit die Gruppe
      aus Pfeil + Text weiterhin mittig steht.
   Die Buttonbreite ändert sich dabei NIE — der Pfeil liegt absolut.
   --------------------------------------------------------- */

.cta {
  --cta-arrow: 1.7em;          /* Platz, den der Pfeil einnimmt */
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--cta-py, 0.75rem) var(--cta-px, 1.4rem);
  font-family: var(--f-mono);
  font-size: var(--cta-fs, var(--fs-mono));
  font-weight: var(--fw-mono);
  letter-spacing: var(--tr-mono);
  text-transform: uppercase;
  border: 0;                   /* flächig, wie die großen CTAs im Hero */
  background: var(--c-paper);
  color: var(--c-ink);
  transition: color var(--dur) var(--ease);
}

/* Füllung baut sich von links nach rechts auf */
.cta::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--c-accent);
  scale: 0 1;
  transform-origin: left center;
  transition: scale var(--dur) var(--ease);
}
.cta:hover::after, .cta:focus-visible::after { scale: 1 1; }

/* .cta__label — trägt Text UND Pfeil, damit beide zusammengehören */
.cta__label {
  position: relative;
  transition: translate var(--dur) var(--ease);
}
.cta__label::before {
  content: "→";
  position: absolute;
  inset-inline-end: 100%;              /* direkt links vom Text */
  inset-block-start: 50%;
  margin-inline-end: calc(var(--cta-arrow) / 3);
  font-size: 1.35em;
  line-height: 1;
  opacity: 0;
  translate: -0.35rem -50%;
  transition: opacity var(--dur) var(--ease), translate var(--dur) var(--ease);
}
.cta:hover .cta__label, .cta:focus-visible .cta__label {
  translate: calc(var(--cta-arrow) / 2) 0;
}
.cta:hover .cta__label::before, .cta:focus-visible .cta__label::before {
  opacity: 1;
  translate: 0 -50%;
}

.cta:hover, .cta:focus-visible { color: var(--c-bg); }

/* --- Größen --- */
.cta--sm { --cta-py: 0.55rem; --cta-px: 1.15rem; }
.cta--md { --cta-py: 0.75rem; --cta-px: 1.4rem; }
.cta--lg { --cta-py: 1.35rem; --cta-px: 1.75rem; --cta-fs: var(--fs-mono-lead); }

/* --- Rollen --- */
.cta--primary   { background: var(--c-ink);   color: var(--c-on-ink); }
/* Der Rahmen ist nicht Zierde, sondern nötig: Im Dunkelmodus liegt die helle
   Kartenfläche (--c-paper) fast auf dem Seitenhintergrund — gemessen
   rgb(25,28,22) auf #121411. Ohne Kante war dieser Button dort praktisch
   unsichtbar und las sich als Lücke. */
.cta--secondary {
  background: var(--c-paper);
  color: var(--c-ink);
  box-shadow: inset 0 0 0 var(--hairline) var(--c-line);
}
/* Sonderfall: bereits orange Fläche — dann läuft die Füllung schwarz auf */
.cta--accent { background: var(--c-accent); color: var(--c-bg); }
.cta--accent::after { background: var(--c-ink); }
.cta--accent:hover, .cta--accent:focus-visible { color: var(--c-on-ink); }

/* --- Leise Variante --------------------------------------
   Für Wege, die angeboten werden, ohne dass jemand sie gehen MUSS:
   weiterlesen, nachschlagen, woanders hinschauen. Keine Fläche, kein
   Aufbau von links — nur Schrift und ein kleiner Pfeil schräg nach oben.

   Dieses Muster gab es vorher schon, aber nur ausgeschrieben im Untermenü
   („Übersicht ↗"). Hier ist es einmal als Variante festgehalten, damit es
   nicht jedes Mal neu erfunden wird.
   -------------------------------------------------------- */
.cta--quiet {
  padding: 0;
  background: none;
  color: var(--c-ink-soft);
}
.cta--quiet::after { content: none; }         /* keine Füllung */
.cta--quiet .cta__label::before { content: none; }   /* kein Pfeil von links */
.cta--quiet .cta__label::after {
  content: "↗";
  margin-inline-start: 0.4em;
  color: var(--c-accent);
  /* etwas kleiner als der Text: Er weist hin, er ruft nicht. */
  font-size: 0.9em;
  transition: translate var(--dur) var(--ease);
  display: inline-block;
}
.cta--quiet:hover, .cta--quiet:focus-visible { color: var(--c-ink); }
.cta--quiet:hover .cta__label, .cta--quiet:focus-visible .cta__label { translate: none; }
.cta--quiet:hover .cta__label::after,
.cta--quiet:focus-visible .cta__label::after { translate: 0.15em -0.15em; }

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

.hero {
  position: relative;
  z-index: 1;
  min-block-size: 100svh;
  padding-block: calc(var(--header-h) + var(--sp-4)) 0;
  display: flex;
  flex-direction: column;
  /* Textblock oben, Kennzahlen-Band bündig am unteren Rand */
  justify-content: space-between;
}

/* --- Wechselndes Bild im Hero ------------------------------
   Ein zufälliges Motiv aus der Gallery, geliefert von `hero-bild.php`.
   Es liegt GANZ HINTEN: hinter der Punktlandschaft und hinter dem Inhalt.

   Die Deckkraft ist bewusst niedrig und je Farbmodus verschieden — auf
   hellem Grund muss ein Foto stärker zurückgenommen werden als auf
   dunklem, sonst frisst es die Schrift. Zusätzlich liegt ein Verlauf
   darüber, der zur Textseite hin dichter wird: Der Text steht links,
   das Motiv darf rechts kräftiger sein.
   -------------------------------------------------------- */
.hero__bild,
.pagehead__bild {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

.hero__bild img,
.pagehead__bild img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  display: block;
  opacity: 0.22;
  /* Ein Foto bringt eigene Farben mit; entsättigt fügt es sich in die
     Palette ein, statt gegen die Signalfarbe zu arbeiten.

     Der leichte Weichzeichner kaschiert das Hochrechnen: Die Bilder liegen
     mit höchstens 1600 px Breite im Ordner (`GALLERY_BREITE`), ein Hero auf
     einem großen Bildschirm ist breiter. Ohne ihn sieht man die Unschärfe
     des Vergrößerns als Kantenflimmern. */
  filter: grayscale(1) contrast(0.9) blur(3px);

  /* Zwingend zum Weichzeichner: Der Filter tastet über den Bildrand hinaus,
     die äußeren Pixel werden dadurch durchsichtig — es entstünde ein weicher
     heller Saum ringsum. Die Vergrößerung schiebt diesen Saum aus der Hülle
     heraus (die `overflow: hidden` hat).

     5 Prozent, nicht 2: Ein Weichzeichner mit 3 px braucht rund 9 px, um
     vollständig auszulaufen. Bei 2 Prozent waren das oben und unten genau
     9 px — auf einem niedrigeren Hero (Handy) hätte es nicht mehr gereicht.
     Der Anteil rechnet mit der Höhe, die Reserve muss also relativ genug
     sein, nicht nur auf dem großen Bildschirm. */
  transform: scale(1.05);
}

:root[data-theme="dark"] .hero__bild img,
:root[data-theme="dark"] .pagehead__bild img { opacity: 0.34; }

/* Gleichmäßiger Schleier statt Verlauf.

   Hier stand ein `linear-gradient` mit `color-mix()`. Zwei Fehler: Der
   `@supports`-Ausdruck war syntaktisch falsch (`@supports` erwartet eine
   Deklaration, keine bloße Funktion), und ein Verlauf machte die Lesbarkeit
   vom Motiv abhängig — bei einem hellen Himmel links stand die Überschrift
   plötzlich auf Weiß. Eine gleichmäßige Fläche ist berechenbar. */
.hero__bild::after,
.pagehead__bild::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--c-bg);
  opacity: 0.60;
}

@media (prefers-reduced-motion: no-preference) {
  /* Sanft einblenden, damit das Bild nicht hart aufpoppt, wenn es
     nachgeladen wird. */
  .hero__bild img,
  .pagehead__bild img { animation: heroBildAuf 0.6s var(--ease) both; }
  @keyframes heroBildAuf { from { opacity: 0; } }
}

/* Inhalt liegt vor der Landschaft */
.hero__body > .shell { position: relative; z-index: 1; }

/* --- Rasterplatzierung ----------------------------------
   Der Hero nutzt DASSELBE 8-Spalten-Raster wie der Hintergrund.
   Der Inhaltsblock sitzt bewusst in den Feldern 3–8; die linken
   beiden Felder bleiben frei. Querlinien rahmen den Block.
   -------------------------------------------------------- */

.hero__cols {
  display: grid;
  grid-template-columns: repeat(var(--grid-cols), 1fr);
  align-items: start;
}

/* .chipnav — die Kärtchen-Navigation, LINKS vom roten Faden.
   Eigene Komponente, weil sie an zwei Stellen gebraucht wird: im Hero der
   Startseite und im Seitenkopf der Unterseiten. Die Platzierung im Raster
   macht der jeweilige Ort (.hero__aside / .pagehead__aside), das Aussehen hier. */
.chipnav {
  align-self: start;
  display: grid;
  grid-template-columns: repeat(2, 2.75rem);
  gap: 0.4rem;
  justify-content: end;
  /* endet vor dem roten Faden — der liegt auf der Kante von Feld 3 */
  padding-inline-end: var(--sp-3);
  /* Bleibt beim Scrollen stehen — und zwar nur so lange, wie der Bereich
     daneben läuft. Danach schiebt ihn die nächste Sektion einfach weg;
     das macht der Rasterplatz von allein, dafür braucht es kein Skript. */
  position: sticky;
  inset-block-start: calc(var(--header-h) + var(--sp-2));
}
@media (max-width: 1200px) { .chipnav { display: none; } }

.hero__aside { grid-column: 1 / 3; }

/* .chip — nummeriertes Kärtchen, Ecke oben rechts abgeschnitten */
.chip {
  display: grid;
  place-items: center;
  block-size: 2.75rem;
  background: var(--c-line-soft);
  color: var(--c-ink-faint);
  font-family: var(--f-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-mono);
  clip-path: polygon(0 0, 74% 0, 100% 26%, 100% 100%, 0 100%);
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.chip:hover, .chip:focus-visible { background: var(--c-accent); color: var(--c-bg); }

/* .hero__block — der eigentliche Inhaltsblock im Raster */
.hero__block {
  grid-column: 3 / -1;
  position: relative;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  align-items: center;
  /* Querlinie oben — sie rahmt den Block im Raster.
     Unten KEINE Linie und kein Abstand: das CTA-Band schließt direkt an
     das Kennzahlen-Band an, dazwischen soll kein Streifen stehen. */
  border-block-start: var(--hairline) solid var(--c-line);
  padding-block: var(--sp-5) 0;
  row-gap: var(--sp-5);
  block-size: 100%;
  align-content: space-between;
}
@media (max-width: 1024px) {
  /* Der Block nimmt die volle Breite — die zwei freien Rasterfelder links kann
     sich das Tablet nicht mehr leisten. Die SECHS Innenspalten bleiben aber:
     Nur dadurch steht die Chip-Grafik weiter neben der Headline und nicht
     darunter. */
  .hero__block { grid-column: 1 / -1; padding-block: var(--sp-4) 0; }
}
@media (max-width: 620px) {
  /* Erst auf dem Handy untereinander — nebeneinander bliebe für beides zu eng. */
  .hero__block { grid-template-columns: 1fr; }
}

/* .tag — das Sektions-Abzeichen „01 INTRO": orange Nummer + Label.
   Überall im Projekt derselbe Baustein. */
.tag {
  display: inline-flex;
  align-items: stretch;
  background: var(--c-bg);
}
.tag b {
  display: grid;
  place-items: center;
  padding-inline: 0.5rem;
  background: var(--c-accent);
  color: var(--c-bg);
  font-family: var(--f-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-mono);
}
.tag .mono {
  display: grid;
  place-items: center;
  padding-inline: 0.7rem;
  background: var(--c-line-soft);
}

/* .block__tag — dasselbe Abzeichen, aber mittig AUF der oberen Querlinie.
   Wiederholt die Grundwerte bewusst, damit es auch ohne `.tag` funktioniert. */
.block__tag {
  display: inline-flex;
  align-items: stretch;
  background: var(--c-bg);
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  translate: 0 -50%;
}
.block__tag b {
  display: grid;
  place-items: center;
  padding-inline: 0.5rem;
  background: var(--c-accent);
  color: var(--c-bg);
  font-family: var(--f-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-mono);
}
.block__tag .mono {
  display: grid;
  place-items: center;
  padding-inline: 0.7rem;
  background: var(--c-line-soft);
}

/* --- Inhaltssektionen unter dem Hero ---------------------
   Gleiches 8-Spalten-Raster. Links vom roten Faden (Felder 1–2)
   steht der Kopf: Abzeichen, Überschrift, kurzer Text. Rechts davon
   (Felder 3–8) der Inhalt.
   -------------------------------------------------------- */

.section { padding-block: var(--sp-5) 0; }

.sec {
  display: grid;
  grid-template-columns: repeat(var(--grid-cols), 1fr);
  column-gap: 0;
}
@media (max-width: 1024px) { .sec { grid-template-columns: 1fr; row-gap: var(--sp-4); } }

/* Kopf links vom roten Faden — bleibt beim Scrollen stehen */
.sec__head {
  grid-column: 1 / 3;
  position: sticky;
  inset-block-start: calc(var(--header-h) + var(--sp-2));
  align-self: start;
  padding-inline-end: var(--sp-3);
  padding-block-start: var(--sp-4);
  display: grid;
  gap: var(--sp-2);
  justify-items: start;
}
@media (max-width: 1024px) { .sec__head { grid-column: 1 / -1; position: static; padding-inline-end: 0; } }

.sec__title {
  font-size: clamp(1.5rem, 1.6vw + 1rem, 2.25rem);
  font-weight: 500;
  letter-spacing: var(--tr-hero);
  line-height: 1.05;
}
/* Farbe NICHT überschreiben: Mono ist immer schwarz (Schriftregel) */
.sec__note { line-height: 1.75; max-inline-size: 34ch; }

/* Querlinien laufen vom roten Faden bis an die ÄUSSERE Rasterkante —
   also über die volle Breite der Inhaltsspalte, aber nie über die
   Textspalte links davon. */
.rule-bleed { position: relative; }
.rule-bleed::before,
.cv::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  pointer-events: none;
}
.rule-bleed::before {
  inset-block-start: 0;
  border-block-start: var(--hairline) solid var(--c-line);
}
.cv::after {
  inset-block-end: 0;
  border-block-end: var(--hairline) dashed var(--c-line);
}

/* Inhalt rechts vom roten Faden, mit durchlaufender Querlinie oben */
.sec__body {
  grid-column: 3 / -1;
  padding-block-start: var(--sp-4);
  /* Ein Rasterelement bekommt von sich aus `min-width: auto` — es wird also
     nie schmaler als sein Mindestinhalt. Im Slider steckt eine Kartenreihe,
     deren Mindestinhalt mit jeder Karte wächst; die Spalte wurde dadurch auf
     dem Handy 1595px breit und die ganze Seite ließ sich seitwärts schieben.
     Aufgefallen ist das erst bei 13 Karten. */
  min-inline-size: 0;
}
@media (max-width: 1024px) { .sec__body { grid-column: 1 / -1; } }

/* --- Invertierte Sektion ---------------------------------
   Die dunkle Fläche tauscht nur die TOKEN aus. Dadurch stimmen Linien,
   Mono-Text, Abzeichen und Buttons darin automatisch — ohne dass eine
   einzige Komponente eine Sonderregel braucht.
   -------------------------------------------------------- */

.section--invert {
  --c-bg:         var(--c-bg-invert);
  --c-ink:        var(--c-ink-invert);
  --c-ink-soft:   var(--c-ink-soft-invert);
  --c-ink-faint:  var(--c-ink-faint-invert);
  --c-line:       var(--c-line-invert);
  --c-line-soft:  var(--c-line-soft-invert);
  --c-paper:      var(--c-paper-invert);
  --c-on-ink:     var(--c-bg-invert);

  position: relative;
  background: var(--c-bg);
  color: var(--c-ink);
  padding-block: var(--sp-5);
  margin-block-start: var(--sp-5);
}

/* Eigenes Raster + eigener roter Faden: die feste Hintergrundebene der Seite
   liegt hinter der dunklen Fläche und wäre sonst verdeckt. */
.section__grid {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  display: grid;
  grid-template-columns: repeat(var(--grid-cols), 1fr);
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.section__grid span { border-inline-start: var(--hairline) dashed var(--c-line-soft); }
.section__grid span:last-of-type { border-inline-end: var(--hairline) dashed var(--c-line-soft); }
@media (max-width: 760px) {
  .section__grid { grid-template-columns: repeat(4, 1fr); }
  .section__grid span:nth-child(n + 5) { display: none; }
  .section__grid span:nth-child(4) { border-inline-end: var(--hairline) dashed var(--c-line-soft); }
}
.section--invert > .shell { position: relative; z-index: 1; }

/* Im Hero liegt das Raster über dem wechselnden Bild — sonst verdeckt das
   Foto die Linien, und der Hero fällt aus dem Raster der übrigen Seite. */
.hero > .section__grid,
.pagehead > .section__grid { z-index: 0; }
.hero__bild,
.pagehead__bild { z-index: -1; }

/* --- Lebenslauf ------------------------------------------ */

/* .cv — eine Station: Zeitraum links, Inhalt rechts */
.cv {
  display: grid;
  grid-template-columns: 11rem minmax(0, 1fr);
  gap: var(--sp-3);
  padding-block: var(--sp-4);
  /* Einzug am Inhalt, NICHT am Container — so beginnt die Trennlinie
     exakt am roten Faden, der Text aber erst dahinter. */
  padding-inline-start: var(--sp-4);
  position: relative;   /* Trennlinie kommt aus ::after */
}
@media (max-width: 1024px) { .cv { padding-inline-start: 0; } }
.cv:last-child::after { display: none; }
@media (max-width: 760px) { .cv { grid-template-columns: 1fr; gap: var(--sp-2); } }

.cv__when { display: grid; gap: 0.35rem; align-content: start; }
.cv__role { font-size: var(--fs-h3, 1.25rem); font-weight: 500; letter-spacing: -0.02em; }
.cv__org { color: var(--c-ink-soft); margin-block-start: 0.2rem; }

/* Aufzählungen laufen in Mono (Schriftregel) */
.cv__list { display: grid; gap: 0.45rem; margin-block-start: var(--sp-2); }
.cv__list li { display: grid; grid-template-columns: 1rem 1fr; align-items: start; }
.cv__list li::before { content: "+"; color: var(--c-accent); }

/* Textspalte: nimmt vier der sechs Blockspalten ein */
.hero__text { grid-column: 1 / 5; }
/* Erst auf dem Handy über die volle Breite. Auf dem Tablet steht die
   Chip-Grafik daneben — dafür muss die Textspalte schmal bleiben. */
@media (max-width: 620px) { .hero__text { grid-column: 1 / -1; } }

/* Headline, Unterzeile und CTA-Band stehen auf DERSELBEN linken Kante —
   und genau auf dieser Kante liegt auch der rote Faden (--pipe-x). */

/* .hero__body — alles über dem Kennzahlen-Band.
   Bezugsrahmen für die Landschaft: sie endet exakt an seiner Unterkante. */
.hero__body {
  position: relative;
  isolation: isolate;   /* eigener Stapel: Landschaft hinten, Inhalt vorn */
  flex: 1;
  display: flex;
  flex-direction: column;
  /* Inhalt steht OBEN, nicht mittig — sonst schiebt die Zentrierung den
     Block in die Mitte und über „01 INTRO" entsteht eine große Lücke. */
  justify-content: flex-start;
  /* KEIN `min-block-size: 0`: sonst darf dieser Flex-Bereich unter seine
     Inhaltshöhe schrumpfen und der Text läuft oben aus dem Hero heraus —
     auf schmalen Bildschirmen landet er dann unter der Kopfleiste. */
  min-block-size: auto;
}

.hero__title {
  font-size: var(--fs-hero);
  line-height: var(--lh-hero);
  letter-spacing: var(--tr-hero);
  font-weight: 500;
  max-inline-size: 14ch;
}
.hero__title em { font-style: normal; color: var(--c-accent); }

/* Hero-Unterzeile: Mono, Versalien, schwarz (Regel) */
.hero__sub { max-inline-size: 42ch; margin-block-start: var(--sp-3); }

/* .ctabar — zwei bündige Flächen, exakt so breit wie der Inhaltsblock */
.ctabar {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: 1fr 1fr;
}
@media (max-width: 620px) { .ctabar { grid-template-columns: 1fr; } }

/* --- Das Diagramm rechts --------------------------------- */

.diagram {
  grid-column: 5 / -1;
  position: relative;
  display: grid;
  place-items: center;
}
.diagram svg { inline-size: 100%; max-inline-size: 28rem; height: auto; }
/* Bis 620px ist die Grafik ohnehin ausgeblendet; darüber bleibt sie in ihrer
   eigenen Spalte neben der Headline statt darunter. */
@media (max-width: 620px) { .diagram { grid-column: 1 / -1; } }

/* Farbrollen im SVG — so bleibt im Markup kein Hex */
.d-line   { stroke: var(--c-line); }
.d-accent { stroke: var(--c-accent); }
.d-fill-accent { fill: var(--c-accent); }
.d-fill-paper  { fill: var(--c-paper); }
.d-fill-ink    { fill: var(--c-ink); }
.d-fill-line   { fill: var(--c-line); }

/* Der Datenfluss auf den gestrichelten Pfeilen läuft */
.d-flow { stroke-dasharray: 5 5; animation: flow 1.4s linear infinite; }
@keyframes flow { to { stroke-dashoffset: -20; } }
@media (prefers-reduced-motion: reduce) { .d-flow { animation: none; } }

/* --- Die Punktlandschaft --------------------------------- */

/* .terrain — EINE Punktraster-Ebene, durch die Wolkenmaske beschnitten.
   Bewusst nur eine: zwei Ebenen ergeben zwei sichtbare Silhouetten. */
.terrain {
  position: absolute;
  inset-block-end: 0;
  inset-inline: 0;
  /* 21.25rem = 340px = exakte Höhe von terrain.svg */
  block-size: clamp(12rem, 30vh, 21.25rem);
  /* reine Hintergrundebene — liegt hinter allem Inhalt */
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
.terrain span {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  /* eine Kachelbreite breiter als der Rahmen — genau darum wandert sie */
  inline-size: calc(100% + var(--terrain-tile));
  /* Ganz normales Hintergrundbild — KEINE Maske.
     Masken werden längst nicht überall gezeichnet; die Landschaft war damit
     in mehreren Browsern unsichtbar. Für den Dunkelmodus gibt es deshalb
     eine zweite Datei, umgeschaltet über --img-terrain-file. */
  background-image: var(--img-terrain-file);
  background-repeat: repeat-x;
  /* Höhe ausgeschrieben: verzerrt man das Muster, fällt das 8px-Raster
     auseinander und es entstehen sichtbare Abrisse. */
  background-size: var(--terrain-tile) var(--terrain-h);
  background-position: 0 100%;
  animation: drift var(--terrain-speed, 90s) linear infinite;
}
@keyframes drift {
  from { transform: translateX(calc(-1 * var(--terrain-tile))); }
  to   { transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce) { .terrain span { animation: none; } }
/* Bewegt wird die ganze Ebene per `transform` — nicht per `mask-position`,
   das animieren nicht alle Browser (u. a. Safari) zuverlässig. */

/* --- Kennzahlen-Band am unteren Hero-Rand -----------------
   Über die volle Breite verteilt, gestrichelte Spaltentrenner,
   je Feld ein oranger Eckwinkel oben links. (Referenz-Screenshot)
   -------------------------------------------------------- */

.factbar {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  /* gleicher Außenrand wie Header und Hero — sonst klebt der erste
     Eckwinkel am Bildschirmrand */
  padding-inline: var(--gutter);
  border-block-start: var(--hairline) dashed var(--c-line);
  /* Das Bild im Hero läuft dahinter durch, soll hier aber nicht durchscheinen —
     das Band steht auf normalem Seitenhintergrund. `position: relative` und
     `z-index: 1` hatte es schon; nötig ist nur die Fläche. */
  background: var(--c-bg);
}
@media (max-width: 1100px) { .factbar { grid-template-columns: repeat(2, 1fr); } }
/* Bewusst KEIN Umbruch auf eine Spalte mehr.
   Untereinander ergaben die vier Punkte vier fast leere Zeilen und schoben den
   Rest weit nach unten. Als 2×2-Feld stehen sie als Block zusammen und lesen
   sich als das, was sie sind: vier gleichrangige Aussagen, keine Liste.
   Dafür rücken Polsterung und Zeilenabstand etwas enger. */
@media (max-width: 560px) {
  .factbar .fact { padding: var(--sp-2) var(--sp-2) var(--sp-3) var(--sp-3); }
  .factbar .fact .mono { font-size: var(--fs-mono); line-height: 1.5; }
}

/* .fact — ein Feld des Bands.
   BEWUSST ohne eigene Trennlinien: die senkrechten Linien im Band sind die
   durchlaufenden Rasterlinien des Hintergrunds (.grid-lines), nicht eigene Ränder. */
.fact {
  position: relative;
  padding: var(--sp-3) var(--sp-3) var(--sp-3) var(--sp-4);
}
/* etwas größer als die übrigen Mono-Labels — das Band trägt Kennzahlen */
.fact .mono { font-size: var(--fs-mono-lead); }

/* Eckwinkel oben links am Feld */
.fact::before {
  content: "";
  position: absolute;
  inset-block-start: var(--sp-2);
  inset-inline-start: 0;
  width: 9px;
  height: 9px;
  border-block-start: 2px solid var(--c-accent);
  border-inline-start: 2px solid var(--c-accent);
}

/* ============================================================
   INHALTSBAUSTEINE DER SEKTIONEN
   Alle Sektionen folgen demselben Gerüst:
   .sec__head (Rasterfelder 1–2, links vom roten Faden, sticky)
   .sec__body (Rasterfelder 3–8) mit Querlinie oben
   Darin wechseln die Bausteine je nach Inhalt.
   ============================================================ */

/* .lead — einleitender Fließtext (Inter, kein Mono) */
.lead {
  font-size: var(--fs-lead);
  color: var(--c-ink-soft);
  max-inline-size: 62ch;
  padding-inline-start: var(--sp-4);
  margin-block-end: var(--sp-5);
}
.lead strong { color: var(--c-ink); font-weight: 500; }
@media (max-width: 1024px) { .lead { padding-inline-start: 0; } }

/* .sec__foot — Abschluss jeder Sektion: Hinweis links, Button rechts.
   Verweist auf die jeweilige Übersichtsseite. */
.sec__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-block-start: var(--sp-5);
  padding-block-start: var(--sp-3);
  padding-inline-start: var(--sp-4);
  border-block-start: var(--hairline) solid var(--c-line);
}
@media (max-width: 1024px) { .sec__foot { padding-inline-start: 0; } }
@media (max-width: 560px) {
  .sec__foot { flex-direction: column; align-items: stretch; }
  .sec__foot .cta { justify-content: center; }
}

/* --- Schritte (About) -------------------------------------
   Vier gleich hohe Felder in einer Reihe. Die Nummer sitzt MITTIG auf der
   oberen Linie, alle auf derselben Höhe — der frühere Versatz ließ die
   Felder unruhig und zufällig wirken.
   -------------------------------------------------------- */

.steps {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0;
  margin: var(--sp-4) 0 0;
  padding: 0;
  list-style: none;
  border-block-start: var(--hairline) dashed var(--c-line);
}

.step {
  position: relative;
  display: grid;
  align-content: start;
  gap: var(--sp-2);
  padding: var(--sp-5) var(--sp-3) var(--sp-4);
  border-inline-start: var(--hairline) dashed var(--c-line);
}
.step:first-child { border-inline-start: 0; }

/* Nummer auf der Linie — bei allen vier auf gleicher Höhe */
.step__num {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: var(--sp-3);
  translate: 0 -50%;
  padding: 0.3rem 0.55rem;
  background: var(--c-bg);
  color: var(--c-accent);
  font-size: var(--fs-mono-lead);
}

.step__title { line-height: 1.5; }
.step p { font-size: var(--fs-small); color: var(--c-ink-soft); }

@media (max-width: 1024px) {
  .steps { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .step:nth-child(3) { border-inline-start: 0; }
  .step:nth-child(n + 3) { border-block-start: var(--hairline) dashed var(--c-line); }
}
@media (max-width: 620px) {
  .steps { grid-template-columns: 1fr; }
  .step { border-inline-start: 0; }
  .step:nth-child(n + 2) { border-block-start: var(--hairline) dashed var(--c-line); }
  /* Nebeneinander trägt der große Kopfabstand die Nummer; untereinander reißt
     er Nummer und Titel auseinander — vier Kacheln ergaben so 64px Luft je
     Stück. Weniger, aber genug, damit die Nummer auf der Linie frei steht. */
  .step { padding: var(--sp-4) var(--sp-3) var(--sp-3); }
}

/* --- Karten (Expertise) ----------------------------------
   Gestricheltes Raster ohne eigene Kästen — die Linien bilden die Felder.
   -------------------------------------------------------- */

.cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  border-block-start: var(--hairline) dashed var(--c-line);
}
.card {
  display: grid;
  align-content: start;
  gap: var(--sp-2);
  block-size: 100%;
  padding: var(--sp-4) var(--sp-3);
  border-inline-start: var(--hairline) dashed var(--c-line);
  border-block-end: var(--hairline) dashed var(--c-line);
  transition: background-color var(--dur) var(--ease);
}
.cards > li:nth-child(3n + 1) .card { border-inline-start: 0; }
.card:hover { background: var(--c-paper); }
.card:hover .card__title { color: var(--c-accent); }
.card:hover .card__go { color: var(--c-accent); }

.card__num { color: var(--c-accent); }
.card__title { line-height: 1.5; transition: color var(--dur) var(--ease); }
.card p { font-size: var(--fs-small); color: var(--c-ink-soft); }
.card__go { color: var(--c-ink-faint); margin-block-start: auto; transition: color var(--dur) var(--ease); }

@media (max-width: 1100px) {
  .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cards > li:nth-child(3n + 1) .card { border-inline-start: var(--hairline) dashed var(--c-line); }
  .cards > li:nth-child(2n + 1) .card { border-inline-start: 0; }
}
@media (max-width: 620px) {
  .cards { grid-template-columns: 1fr; }
  .cards > li .card { border-inline-start: 0; }
}

/* --- Case Studies ----------------------------------------
   Karte mit Bildfläche, Kurzfassung aus Problem und Ergebnis.
   Die vollständige Fallstudie steht auf der Detailseite.
   -------------------------------------------------------- */

/* .works — dieselben Karten wie im Slider der Startseite, nur als Raster.
   Sie sitzen EXAKT in den Rasterspalten der Seite: Der Inhaltsbereich ist sechs
   Spalten breit, drei Karten belegen also genau zwei davon.

   Deshalb KEIN Abstand zwischen den Spalten — der würde die Kanten von den
   Rasterlinien wegschieben, und „fast auf der Linie" sieht schlechter aus als
   gar keine Ausrichtung. Getrennt werden die Karten von den gestrichelten
   Hintergrundlinien der Seite selbst; Luft entsteht durch die Polsterung nach
   rechts. Jede Karte beginnt damit auf einer echten Rasterlinie. */
.works {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: 0;
  row-gap: var(--sp-5);
  margin: 0;
  padding: 0;
  list-style: none;
}
/* Das `li` ist selbst ein Raster. Grund steht bei `.work` gleich darunter. */
.works > li { padding-inline-end: var(--sp-4); display: grid; }
.work {
  display: grid;
  align-content: start;
  gap: var(--sp-2);
  /* KEIN `block-size: 100%`.
     Die Karte soll so hoch sein wie ihr Rasterfeld — das Feld ist aber selbst
     nur so hoch wie sein Inhalt. Prozenthöhe von einer Höhe, die erst aus der
     Höhe entsteht: ein Kreis. Chrome bricht ihn ab, Safari auf dem iPhone
     rechnet bei jedem Neuzeichnen mit dem letzten Ergebnis weiter — deshalb
     wuchsen die Cover dort immer weiter in die Länge.
     Das `li` ist jetzt ein Raster, die Karte darin füllt es von allein aus. */
  block-size: auto;
  padding-block-end: var(--sp-3);
  transition: opacity var(--dur) var(--ease);
}
.work:hover .work__title { color: var(--c-accent); }
.work:hover .work__go { color: var(--c-accent); }
.work:hover .work__media { border-color: var(--c-accent); }

/* Bildfläche — bis echtes Material da ist, das Rastermuster des Entwurfs */
.work__media {
  position: relative;
  display: block;
  /* Breite ausdrücklich festnageln: Das Seitenverhältnis rechnet die Höhe aus
     der Breite. Steht die Breite nicht fest, hat Safari zwei Unbekannte und
     rät — die Fläche wird bei jedem Neuzeichnen anders hoch. */
  inline-size: 100%;
  block-size: auto;
  aspect-ratio: 4 / 3;
  border: var(--hairline) solid var(--c-line);
  background-color: var(--c-paper);
  /* Punktraster rein aus CSS — die SVG-Datei wurde teils erst beim Hover
     gezeichnet, das Muster hier steht sofort. */
  background-image: var(--img-dots);
  background-size: 8px 8px;
  -webkit-mask-image: linear-gradient(to top, var(--mask-solid) 25%, transparent 92%);
  mask-image: linear-gradient(to top, var(--mask-solid) 25%, transparent 92%);
  transition: border-color var(--dur) var(--ease);
}
.work__media--alt { background-position: 4px 4px; }

/* Kachel MIT Cover: Das erste Bild der Fallstudie füllt die Fläche.
   Das Punktraster darunter bleibt als Grundfarbe stehen — es ist sichtbar,
   solange das Bild noch lädt, und bei den drei Fallstudien ohne Bildmaterial
   ist es weiterhin die ganze Gestaltung. */
.work__media--cover img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  display: block;
}

/* Die Nummer liegt jetzt auf einem Bild und braucht eigenen Grund,
   sonst verschwindet sie je nach Motiv. */
.work__media--cover i {
  background: var(--c-bg);
  color: var(--c-ink);
  padding: 0.15rem 0.4rem;
}

/* Beim Überfahren tritt das Bild leicht zurück — dieselbe Regung wie bei den
   Galeriebildern, nur andersherum: dort hellt der Rahmen auf, hier das Motiv. */
.work:hover .work__media--cover img,
.work:focus-visible .work__media--cover img { opacity: 0.82; }
.work__media--cover img { transition: opacity var(--dur) var(--ease); }
.work__media i {
  position: absolute;
  inset-block-start: var(--sp-2);
  inset-inline-start: var(--sp-2);
  font-style: normal;
  color: var(--c-accent);
}

.work__meta { color: var(--c-ink-faint); margin-block-start: var(--sp-1); }
.work__title {
  font-size: clamp(1.25rem, 1vw + 1rem, 1.625rem);
  font-weight: 500;
  letter-spacing: -0.025em;
  transition: color var(--dur) var(--ease);
}

.work__facts { display: grid; gap: 0.3rem; margin: 0; }
.work__facts dt { color: var(--c-accent); margin-block-start: var(--sp-1); }
.work__facts dd { margin: 0; font-size: var(--fs-small); color: var(--c-ink-soft); }
.work__go { color: var(--c-ink-faint); margin-block-start: auto; padding-block-start: var(--sp-2); transition: color var(--dur) var(--ease); }

@media (max-width: 1024px) { .works { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px)  { .works { grid-template-columns: 1fr; } }

/* --- Kontakt --------------------------------------------- */

.contact { padding-inline-start: var(--sp-4); margin-block-end: var(--sp-5); }
@media (max-width: 1024px) { .contact { padding-inline-start: 0; } }

.contact__mail {
  display: inline-block;
  font-size: clamp(1.5rem, 3.4vw, 3rem);
  font-weight: 500;
  letter-spacing: -0.035em;
  padding-block-end: 0.1em;
  border-block-end: var(--pipe-w) solid var(--c-line);
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.contact__mail:hover { color: var(--c-accent); border-block-end-color: var(--c-accent); }

.contact__meta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, max-content));
  gap: var(--sp-4);
  margin-block-start: var(--sp-4);
}
.contact__meta p { font-size: var(--fs-small); margin-block-start: 0.25rem; }

/* --- FAQ -------------------------------------------------
   Zwei Spalten im gestrichelten Raster, Antworten klappen auf.
   Als <details> gebaut — funktioniert auch ohne JavaScript.
   -------------------------------------------------------- */

.faq {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  border-block-start: var(--hairline) dashed var(--c-line);
}
.faq > li { border-block-end: var(--hairline) dashed var(--c-line); }
.faq > li:nth-child(even) { border-inline-start: var(--hairline) dashed var(--c-line); }

.faq__item summary {
  display: flex;
  align-items: start;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-3);
  cursor: pointer;
  list-style: none;
  transition: background-color var(--dur) var(--ease);
}
.faq__item summary::-webkit-details-marker { display: none; }
.faq__item summary:hover { background: var(--c-paper); }
.faq__item summary h3 {
  font-size: var(--fs-lead);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.3;
}
.faq__item summary i {
  font-style: normal;
  font-family: var(--f-mono);
  font-size: 1.25rem;
  line-height: 1;
  color: var(--c-accent);
  transition: rotate var(--dur) var(--ease);
}
.faq__item[open] summary i { rotate: 45deg; }
.faq__item > p {
  padding: 0 var(--sp-3) var(--sp-3) var(--sp-3);
  font-size: var(--fs-small);
  color: var(--c-ink-soft);
  max-inline-size: 60ch;
}

@media (max-width: 760px) {
  .faq { grid-template-columns: 1fr; }
  .faq > li:nth-child(even) { border-inline-start: 0; }
}

/* --- Fließtext in Versal-Mono ------------------------------
   Mono läuft laut Regelwerk immer in Versalien. Für Labels stimmt das; für
   ganze Sätze nicht. Auf dem Handy stand die Hero-Unterzeile in SECHS Zeilen
   Großbuchstaben, auf dem iPad hochkant in einer schmalen Spalte daneben
   ebenfalls sechs. Versalien haben keine Ober- und Unterlängen, an denen das
   Auge Wörter erkennt — in schmalem Satz wird daraus Arbeit statt Lesen.

   Bewusste Ausnahme vom Regelwerk: gleiche Rolle, gleiche Farbe — aber
   Fließtextschrift und gemischte Schreibweise. Ab 1025px greift wieder Mono,
   dort steht der Satz breit genug.
   -------------------------------------------------------- */
@media (max-width: 1024px) {
  .hero__sub,
  .pagehead__sub,
  .sec__note {
    font-family: var(--f-sans);
    text-transform: none;
    letter-spacing: 0;
    line-height: var(--lh-body);
  }
  /* Die Unterzeile trägt die Headline mit — 13px unter einer 42px-Zeile reißt
     die Stufe zu weit auf. Auf Fließtextgröße steht beides in einem Verhältnis.
     Die Sektionsnotizen bleiben klein: Sie sind Beiwerk, keine zweite Stimme. */
  .hero__sub,
  .pagehead__sub { font-size: var(--fs-lead); }
}

/* --- Hero auf schmalen Bildschirmen -----------------------
   Dort darf er höher als der Bildschirm werden. Eine erzwungene
   Bildschirmhöhe presst den Inhalt sonst zusammen.

   Die Grenze liegt bei 1024px, nicht bei 860: Auf dem iPad hochkant zwang
   `min-block-size: 100svh` den Hero auf 812px, obwohl sein Inhalt nur 480px
   hoch ist — über 300px erzwungene Leere zwischen den Schaltflächen und dem
   Kennzahlen-Band. Ohne Mindesthöhe ist er so hoch wie das, was drinsteht.
   -------------------------------------------------------- */
@media (max-width: 1024px) {
  .hero {
    min-block-size: auto;
    padding-block-start: calc(var(--header-h) + var(--sp-3));
  }
  .hero__block { block-size: auto; row-gap: var(--sp-4); }
  .diagram { padding-block: var(--sp-3); }
  .diagram svg { max-inline-size: 20rem; margin-inline: auto; }
}
@media (max-width: 560px) {
  .hero__title { max-inline-size: none; }
  .factbar .fact { padding-inline-start: var(--sp-3); }
}

/* --- Roter Faden auf schmalen Bildschirmen ----------------
   Dort läuft er an der äußeren Kante des Inhaltsbereichs, nicht mittendrin.

   Die Grenze liegt bewusst HÖHER als die des Rasters (760px): Schon auf dem
   iPad hochkant wirkt ein Faden im zweiten Rasterfeld wie ein Strich quer
   durch die Seite. 1024px deckt alle iPad-Breiten im Hochformat ab
   (768, 810, 820, 834 und 1024 beim 12,9-Zoll).
   -------------------------------------------------------- */
@media (max-width: 1024px) {
  /* Ganz an die Kante — nicht auf den Gutter.
     Auf dem Gutter lag der Faden genau auf der Textkante: mal lief er unter dem
     Text, mal sprangen Kanten gegeneinander, weil randbündige Bänder (das
     Kennzahlen-Band) anders beginnen als der Inhalt im .shell. Am Bildschirmrand
     ist er eindeutig eine Kante der Seite — und der Inhalt behält seine volle
     Breite, ohne ihm ausweichen zu müssen. */
  :root { --pipe-x: 0px; }

  /* Inhalt und Faden liegen hier BEWUSST auf derselben Kante.
     Ein Versuch, den Inhalt um 16px abzurücken, brachte die Kanten
     durcheinander: Das Kennzahlen-Band läuft randbündig, seine Eckwinkel saßen
     dadurch links vom CTA-Band darüber. Auf dieser Breite ist der Faden keine
     Führungslinie neben dem Text mehr, sondern seine linke Kante — und alles
     dockt daran an. */

  /* Auf dem Desktop schließen CTA-Band und Kennzahlen-Band bewusst bündig
     aneinander an. Sobald die CTAs untereinander stehen, stoßen beide Blöcke
     direkt zusammen (gemessen: 1px) und lesen sich als einer — hier braucht es
     eine Fuge. */
  .factbar { margin-block-start: var(--sp-5); }
}

/* --- Fließtext auf dem Handy ------------------------------
   Mono läuft laut Regelwerk immer in Versalien. Für Labels ist das richtig,
   für ganze Sätze auf 390px nicht: Die Hero-Unterzeile stand in SECHS Zeilen
   Großbuchstaben, die Sektionsnotizen in drei. Versalien haben keine
   Ober- und Unterlängen, an denen das Auge Wörter erkennt — auf dieser
   Breite wird daraus Arbeit statt Lesen.

   Deshalb hier bewusst die Ausnahme: gleiche Rolle, gleiche Farbe, gleiche
   Größe — aber in der Fließtextschrift und gemischter Schreibweise.
   -------------------------------------------------------- */
@media (max-width: 620px) {
  /* Tippflächen vergrößern.
     Gemessen: Footer-Links 21px hoch, der Farbschalter 18px. Mit dem Finger
     trifft man das nicht zuverlässig — Faustregel sind rund 44px. Der Abstand
     kommt als Polsterung dazu, die Schrift bleibt unverändert; dadurch wächst
     die Fläche, nicht die Optik. */
  .footer__col a { padding-block: 0.65rem; }
  .footer__col { gap: 0; }
  .theme-switch { padding-block: 0.75rem; }
  /* Die Menütaste ist auf dem Handy die wichtigste Fläche der Seite —
     35px waren dafür zu wenig. */
  .header .action { --cta-py: 0.85rem; }
  .footer__recht a { padding-block: 0.65rem; }

  /* Die Chip-Grafik im Hero fliegt raus.
     Sie ist Schmuck, kostet aber 282px genau zwischen Headline und den
     Schaltflächen — dadurch lagen die CTAs bei 708px und damit unterhalb des
     ersten Bildschirms. Ohne sie beginnen sie bei rund 430px und sind sofort
     zu sehen. Auf breiteren Schirmen bleibt sie, dort stört sie nicht. */
  .hero .diagram { display: none; }

}

/* --- Case Studies als Slider -----------------------------
   Waagerecht scrollbar mit Einrasten. Ohne JavaScript bleibt es ein
   normal scrollbarer Streifen — die Pfeile sind nur Komfort.
   -------------------------------------------------------- */

.slider { position: relative; }

.slider__track {
  display: grid;
  grid-auto-flow: column;
  /* Feste Spaltenbreite: mit `minmax(0, …)` schrumpfen die Karten auf die
     Containerbreite zusammen, statt überzulaufen — dann scrollt nichts. */
  grid-auto-columns: calc((100% - 2 * var(--sp-3)) / 3);
  gap: var(--sp-3);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-inline-start: 0;
  padding-block-end: var(--sp-2);
  scrollbar-width: none;
}
.slider__track::-webkit-scrollbar { display: none; }
.slider__track > li { scroll-snap-align: start; }

@media (max-width: 1024px) { .slider__track { grid-auto-columns: calc((100% - var(--sp-3)) / 2); } }
@media (max-width: 680px)  { .slider__track { grid-auto-columns: 82%; } }

/* Steuerung: Pfeile + Fortschritt, oben rechts über dem Streifen */
.slider__nav {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  justify-content: flex-end;
  margin-block-end: var(--sp-2);
}
.slider__btn {
  inline-size: 2.5rem;
  block-size: 2.5rem;
  display: grid;
  place-items: center;
  border: var(--hairline) solid var(--c-line);
  color: var(--c-ink);
  font-family: var(--f-mono);
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.slider__btn:hover:not(:disabled) { background: var(--c-accent); border-color: var(--c-accent); color: var(--c-bg); }
.slider__btn:disabled { opacity: 0.3; cursor: default; }
.slider__count { margin-inline-end: auto; color: var(--c-ink-faint); }

/* Schlagworte statt ganzer Absätze */
.tags { display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 0; padding: 0; list-style: none; }
.tags li {
  font-family: var(--f-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-mono);
  letter-spacing: var(--tr-mono);
  text-transform: uppercase;
  color: var(--c-ink-soft);
  border: var(--hairline) dashed var(--c-line);
  padding: 0.3rem 0.6rem;
}
.work:hover .tags li { border-color: var(--c-line-strong, var(--c-line)); }

/* Abschluss ohne Hinweistext — nur der Button */
.sec__foot--solo { justify-content: flex-end; }

/* ============================================================
   FOOTER
   Kein Link-Friedhof: erst eine große Aufforderung, dann die Spalten,
   darunter der über die volle Breite gesperrte Schriftzug — davor läuft
   die Punktlandschaft durch. Dunkel in BEIDEN Farbmodi.
   ============================================================ */

.footer {
  position: relative;
  z-index: 1;
  overflow: hidden;
  background: var(--c-bg-invert);
  color: var(--c-ink-invert);
  --c-line: var(--c-line-invert);
  --c-ink-soft: var(--c-ink-soft-invert);
  padding-block-start: var(--sp-5);
  margin-block-start: var(--sp-5);
}

/* Landschaft als Textur hinter dem Footer */
.footer__terrain {
  position: absolute;
  inset-block-end: 0;
  inset-inline: 0;
  block-size: var(--terrain-h);
  z-index: 0;
  pointer-events: none;
  /* pro Farbmodus unterschiedlich dosiert — siehe tokens.css */
  opacity: var(--terrain-footer-o);
  overflow: hidden;
}
.footer__terrain span {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  inline-size: calc(100% + var(--terrain-tile));
  background-image: var(--img-terrain-file);
  background-repeat: repeat-x;
  background-size: var(--terrain-tile) var(--terrain-h);
  background-position: 0 100%;
  animation: drift 120s linear infinite;
}
@media (prefers-reduced-motion: reduce) { .footer__terrain span { animation: none; } }

.footer > .shell { position: relative; z-index: 1; }

/* --- Aufforderung ganz oben ------------------------------ */
.footer__call {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  justify-content: space-between;
  gap: var(--sp-4);
  padding-block-end: var(--sp-5);
  border-block-end: var(--hairline) dashed var(--c-line);
}
.footer__call h2 {
  font-size: clamp(1.75rem, 3.4vw, 3.25rem);
  font-weight: 500;
  letter-spacing: var(--tr-hero);
  line-height: 1.05;
  max-inline-size: 16ch;
}
.footer__call em { font-style: normal; color: var(--c-accent); }

/* --- Spalten --------------------------------------------- */
.footer__grid {
  display: grid;
  grid-template-columns: 1.3fr repeat(3, minmax(0, 1fr));
  gap: var(--sp-4);
  padding-block: var(--sp-5);
}
@media (max-width: 900px) { .footer__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .footer__grid { grid-template-columns: 1fr; gap: var(--sp-3); } }

.footer__brand { display: grid; gap: var(--sp-2); align-content: start; }
.footer__brand .brand__name { color: var(--c-ink-invert); }

.footer__col { display: grid; gap: 0.55rem; align-content: start; }
.footer__head {
  font-size: var(--fs-mono);
  color: var(--c-accent);
  padding-block-end: var(--sp-1);
  border-block-end: var(--hairline) dashed var(--c-line);
  margin-block-end: var(--sp-1);
}
.footer__col a {
  font-size: var(--fs-small);
  color: color-mix(in srgb, var(--c-ink-invert) 78%, transparent);
  transition: color var(--dur) var(--ease), padding-inline-start var(--dur) var(--ease);
}
.footer__col a:hover { color: var(--c-accent); padding-inline-start: 0.3rem; }

/* --- Der gesperrte Schriftzug ---------------------------- */
.footer__mark {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  inline-size: 100%;
  font-weight: 600;
  line-height: 0.78;
  font-size: clamp(2.25rem, 13.5vw, 13rem);
  letter-spacing: 0;
  user-select: none;
  padding-block: var(--sp-4) var(--sp-3);
}
.footer__mark span { transition: color var(--dur) var(--ease), translate var(--dur) var(--ease); }
.footer__mark:hover span { color: var(--c-accent); }
.footer__mark span:hover { translate: 0 -0.05em; }

.footer__bar {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--sp-2);
  padding-block: var(--sp-3);
  border-block-start: var(--hairline) dashed var(--c-line);
}
.footer__bar .mono { color: color-mix(in srgb, var(--c-ink-invert) 55%, transparent); }

/* --- Sprunglink -------------------------------------------
   Für Tastaturbedienung: erster Tabstopp, springt über Kopfleiste
   und Navigation direkt in den Inhalt. Sichtbar nur bei Fokus.
   -------------------------------------------------------- */
.skip {
  position: fixed;
  inset-block-start: 0;
  inset-inline-start: 0;
  z-index: 100;
  background: var(--c-accent);
  color: var(--c-bg);
  font-family: var(--f-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-mono);
  letter-spacing: var(--tr-mono);
  text-transform: uppercase;
  white-space: nowrap;

  /* Im Ruhezustand hat der Link KEINE Fläche.
     Vorher wurde er mit `translate: 0 -110%` nach oben geschoben. Das setzt
     voraus, dass die Verschiebung wirklich greift — auf einem iPhone stand er
     sonst als grüner Kasten über der Statusleiste. Ohne Fläche kann das nicht
     mehr passieren: Er ist dann weder zu sehen noch zu treffen. */
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
}

.skip:focus-visible {
  inline-size: auto;
  block-size: auto;
  padding: 0.75rem 1.25rem;
  overflow: visible;
  clip-path: none;
  /* Auf Geräten mit Aussparung sonst halb unter der Statusleiste */
  margin-block-start: env(safe-area-inset-top, 0px);
}

/* ============================================================
   UNTERSEITEN
   Bausteine, die es nur unterhalb der Startseite gibt. Sie sind
   bewusst allgemein gehalten — dieselben Teile tragen später auch
   die Expertise- und Case-Study-Seiten.

   Alles andere (Kopfleiste, Sektionsgerüst, Karten, Lebenslauf,
   Kontakt, Fußzeile) wird UNVERÄNDERT von der Startseite übernommen.
   ============================================================ */

/* --- Seitenkopf -------------------------------------------
   Kein zweiter Hero: flach, ruhig, ohne Bildschirmhöhe und ohne
   Landschaft. Der Inhalt beginnt im Rasterfeld 3 — also exakt auf
   der Kante, an der auch der rote Faden läuft.
   -------------------------------------------------------- */

.pagehead {
  /* Der untere Abstand sitzt bewusst INNEN am Block (siehe .pagehead__block),
     nicht hier: nur so gehört er zum Rasterfeld der Kärtchen-Navigation und
     gibt ihr Weg zum Mitlaufen. Optisch macht das keinen Unterschied. */
  padding-block: calc(var(--header-h) + var(--sp-4)) 0;
  /* Wie im Hero der Startseite: eigener Stapel, damit die Punktlandschaft
     dahinter liegt und der Inhalt davor — ohne dass irgendwo ein z-index
     gegen den Rest der Seite gesetzt werden muss. */
  position: relative;
  isolation: isolate;
}
.pagehead > .shell { position: relative; z-index: 1; }
/* Das Bild darf nicht in den ersten Abschnitt darunter auslaufen. */
.pagehead { overflow: hidden; }
.pagehead__inner {
  display: grid;
  grid-template-columns: repeat(var(--grid-cols), 1fr);
  row-gap: var(--sp-3);
}
/* Alles im Seitenkopf sitzt rechts vom roten Faden … */
.pagehead__inner > * { grid-column: 3 / -1; }

/* … nur die Kärtchen-Navigation nicht: die gehört in die freien linken Felder,
   auf Höhe des Inhaltsblocks. Nicht sticky wie im Hero — der Seitenkopf ist zu
   flach, das Stehenbleiben käme dort gar nicht zur Geltung. */
.pagehead__inner > .pagehead__aside {
  grid-column: 1 / 3;
  grid-row: 2;
  /* Oberkante = die Querlinie des Blocks daneben. Genau wie im Hero der
     Startseite: die Kärtchen hängen an der Linie, nicht an der Überschrift.
     Der Strich ist der Bezugspunkt — er hält die ganze Reihe zusammen. */
}

/* .pagehead__block — derselbe Rahmen wie der Hero-Block: Querlinie oben,
   das Abzeichen sitzt mittig darauf. Ohne ihn hängen Titel und Unterzeile
   frei im Raum und die Ausrichtung wirkt zufällig. */
.pagehead__block {
  position: relative;
  display: grid;
  justify-items: start;
  gap: var(--sp-3);
  border-block-start: var(--hairline) solid var(--c-line);
  padding-block: var(--sp-5) var(--sp-4);
}

@media (max-width: 1024px) {
  .pagehead__inner { grid-template-columns: 1fr; }
  .pagehead__inner > * { grid-column: 1 / -1; }
  .pagehead__block { padding-block-start: var(--sp-4); }
}

.pagehead__title {
  font-size: clamp(1.85rem, 3.4vw, 3.5rem);
  font-weight: 500;
  letter-spacing: var(--tr-hero);
  line-height: 1.04;
  max-inline-size: 22ch;
}
.pagehead__title em { font-style: normal; color: var(--c-accent); }

.pagehead__sub { max-inline-size: 46ch; }

/* --- Pfadanzeige ------------------------------------------
   Zeigt, wo man ist, und führt mit einem Klick zurück. Trägt die
   Klasse `.mono` im Markup — hier nur Anordnung, keine Schriftregeln.
   -------------------------------------------------------- */

.crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; }
.crumbs a { color: var(--c-ink-soft); transition: color var(--dur) var(--ease); }
.crumbs a:hover { color: var(--c-accent); }
.crumbs__sep { color: var(--c-ink-faint); }
.crumbs [aria-current="page"] { color: var(--c-accent); }

/* --- Fließtext-Block ---------------------------------------
   Mehrere Absätze am Stück. `.lead` ist die EINE hervorgehobene
   Einleitung, `.copy` alles danach.
   -------------------------------------------------------- */

.copy {
  display: grid;
  gap: var(--sp-3);
  max-inline-size: 68ch;
  padding-inline-start: var(--sp-4);
  color: var(--c-ink-soft);
}
.copy strong { color: var(--c-ink); font-weight: 500; }
@media (max-width: 1024px) { .copy { padding-inline-start: 0; } }

/* --- Ablaufkette -------------------------------------------
   Schritte untereinander, die Nummern sitzen MITTIG auf dem roten Faden.
   Bewusst senkrecht: die Reihenfolge ist der Inhalt.

   Die Kette bringt KEINE eigene Linie mit. Sie beginnt auf `--pipe-x`, also
   genau dort, wo der Faden der Seite ohnehin durchläuft — eine zweite Linie
   an derselben Stelle würde ihn nur verdecken. Der Faden ist die Kette.
   -------------------------------------------------------- */

.chain { display: grid; margin: var(--sp-4) 0 0; padding: 0; list-style: none; }

.chain__step {
  position: relative;
  display: grid;
  gap: 0.4rem;
  padding: 0 0 var(--sp-4) var(--sp-4);
}
.chain__step:last-child { padding-block-end: 0; }

.chain__num {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  translate: -50% 0;
  padding: 0.3rem 0.5rem;
  background: var(--c-bg);
  color: var(--c-accent);
  font-size: var(--fs-mono-lead);
}

.chain__text { display: grid; gap: 0.5rem; max-inline-size: 62ch; }
.chain__title { line-height: 1.5; }
.chain__text p { font-size: var(--fs-small); color: var(--c-ink-soft); }

@media (max-width: 560px) { .chain__step { padding-inline-start: var(--sp-3); } }

/* --- Haltungssatz -------------------------------------------
   Ein einziger Absatz, größer gesetzt. Steht im Kontrastblock und
   ist der inhaltliche Höhepunkt der Seite — deshalb kein Aufzählen.
   -------------------------------------------------------- */

.creed {
  margin: 0;
  /* KEINE eigene Linie links. An dieser Kante läuft bereits der rote Faden der
     Seite (`.section__grid > .pipe` liegt auf `--pipe-x`) — ein zweiter Strich
     daneben verdoppelt ihn nur. Der Absatz rückt einfach ein, der Faden führt. */
  padding-inline-start: var(--sp-4);
}
.creed p {
  font-size: clamp(1.2rem, 1.9vw, 1.8rem);
  font-weight: 500;
  line-height: 1.35;
  letter-spacing: -0.02em;
  max-inline-size: 42ch;
}
.creed em { font-style: normal; color: var(--c-accent); }

@media (max-width: 1024px) { .creed { padding-inline-start: var(--sp-3); } }

/* --- Werkzeuge ----------------------------------------------
   Zweck links, Werkzeuge rechts als Schlagworte (`.tags`).
   Bewusst keine Logo-Wand: die Kategorie ist die Aussage.
   -------------------------------------------------------- */

.tools {
  display: grid;
  margin: var(--sp-4) 0 0;
  border-block-start: var(--hairline) dashed var(--c-line);
}
.tools__group {
  display: grid;
  grid-template-columns: minmax(8rem, 1fr) 3fr;
  gap: var(--sp-3);
  align-items: start;
  padding-block: var(--sp-3);
  /* Einzug wie bei jedem anderen Fließtext: sonst klebt das Label direkt am
     roten Faden — der darf Inhalt nie berühren (Projektregel). Die Trennlinien
     bleiben davon unberührt, sie sitzen auf dem Kasten. */
  padding-inline-start: var(--sp-4);
  border-block-end: var(--hairline) dashed var(--c-line);
}
.tools__group dt { padding-block-start: 0.35rem; }
.tools__group dd { margin: 0; }
/* Rechts stehen entweder Schlagworte (`.tags`) oder ein Fließtext. Der Fließtext
   ist zurückgenommen — dasselbe Verhältnis wie bei `.contact__meta`: Mono-Label
   führt, der Wert ordnet sich unter. */
.tools__group dd p { font-size: var(--fs-small); color: var(--c-ink-soft); }

@media (max-width: 1024px) { .tools__group { padding-inline-start: 0; } }
@media (max-width: 620px) {
  .tools__group { grid-template-columns: 1fr; gap: var(--sp-2); }
  .tools__group dt { padding-block-start: 0; }
}

/* --- Schlagworte, größer -------------------------------------
   Variante von `.tags` für Stellen, an denen die Schlagworte selbst
   die Aussage sind und nicht neben einer Karte stehen.
   -------------------------------------------------------- */

/* Steht frei im Textfluss, deshalb auf derselben Kante wie `.lead`/`.copy` */
.tags--lg { gap: 0.5rem; margin-block-start: var(--sp-4); padding-inline-start: var(--sp-4); }
.tags--lg li { padding: 0.5rem 0.85rem; font-size: var(--fs-mono-lead); }
@media (max-width: 1024px) { .tags--lg { padding-inline-start: 0; } }

/* --- Kachelraster (Übersichtsseiten) -------------------------
   KEINE zweite Karte: das hier ist dieselbe `.card` wie auf der Startseite,
   nur anders platziert. Statt drei gleicher Spalten stehen verschieden breite
   Felder nebeneinander — deshalb wirkt die Übersicht kacheliger als der
   Anriss, ohne dass ein Bauteil doppelt existiert.

   Das Gitter zeichnen die Kacheln nach RECHTS und UNTEN, oben und links
   schließt der Container ab. Nur so entsteht bei JEDER Feldbreite ein
   vollständiges Gitter ohne doppelte Linien — die `nth-child`-Regeln der
   `.cards` funktionieren bei wechselnden Breiten nicht.
   -------------------------------------------------------- */

.mosaic {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 0;
  margin: var(--sp-4) 0 0;
  padding: 0;
  list-style: none;
  border-block-start: var(--hairline) dashed var(--c-line);
  border-inline-start: var(--hairline) dashed var(--c-line);
}
.mosaic > li { display: grid; }            /* damit die Karte die Kachel füllt */
.mosaic > li > .card {
  border-inline-start: 0;
  border-inline-end: var(--hairline) dashed var(--c-line);
}

/* Feldbreiten — jede Zeile ergibt zusammen 6 */
.mosaic > li:nth-child(1) { grid-column: span 4; }
.mosaic > li:nth-child(2) { grid-column: span 2; }
.mosaic > li:nth-child(3) { grid-column: span 3; }
.mosaic > li:nth-child(4) { grid-column: span 3; }
.mosaic > li:nth-child(5) { grid-column: span 2; }
.mosaic > li:nth-child(6) { grid-column: span 4; }

/* Breite Kacheln bekommen mehr Luft — aber KEINE andere Schriftgröße.

   Vorher hoben sie Überschrift und Fließtext an (11px auf ~19px). Das traf
   zwei der sechs Felder, darunter „Foto & Video" — die Größen sahen dadurch
   willkürlich aus, weil sie an der Kachelbreite hingen und nicht am Gewicht
   des Themas. Die Bedeutung tragen die Kachelflächen selbst; die Schrift
   muss das nicht doppeln. */
.card--wide { padding: var(--sp-5) var(--sp-4); }
.card--wide p { max-inline-size: 46ch; }

/* Zwei bzw. eine Spalte. `:nth-child(n)` hat dieselbe Stärke wie die Regeln
   oben und steht später — dadurch gewinnt es ohne `!important`. */
@media (max-width: 1100px) {
  .mosaic { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .mosaic > li:nth-child(n) { grid-column: span 1; }
  .card--wide { padding: var(--sp-4) var(--sp-3); }
}
@media (max-width: 620px) {
  .mosaic { grid-template-columns: 1fr; }
}

/* --- Kennzahlen einer Fallstudie -----------------------------
   Drei Felder in den Rasterspalten (sechs Spalten ÷ 3 = je zwei), also kein
   Spaltenabstand — dieselbe Regel wie bei `.works`.
   -------------------------------------------------------- */

.kpis {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0;
  margin: var(--sp-4) 0 0;
  border-block-start: var(--hairline) solid var(--c-line);
}
.kpis > div {
  display: grid;
  gap: var(--sp-1);
  align-content: start;
  padding: var(--sp-3) var(--sp-4) var(--sp-3) 0;
}
.kpis dt { color: var(--c-ink-faint); }
.kpis dd { margin: 0; display: grid; gap: 0.35rem; }
.kpis .kpi { color: var(--c-ink); }
/* Noch nicht gefüllt: erkennbar zurückgenommen, damit niemand einen Strich
   für eine Aussage hält. Verschwindet, sobald echte Zahlen drinstehen. */
.kpis .kpi--offen { color: var(--c-line); }
.kpis dd span { font-size: var(--fs-small); color: var(--c-ink-soft); }

@media (max-width: 760px) { .kpis { grid-template-columns: 1fr; } }

/* --- Bildstrecke ---------------------------------------------
   Kein neuer Slider: Das ist derselbe `.slider` wie auf der Startseite, nur mit
   größeren Feldern (`.slider--shots`) — zwei statt drei nebeneinander, weil
   Screenshots sonst nicht lesbar sind. Pfeile, Einrasten und Zähler kommen
   unverändert von dort.
   -------------------------------------------------------- */

.slider--shots .slider__track { grid-auto-columns: calc((100% - var(--sp-3)) / 2); }
@media (max-width: 900px) { .slider--shots .slider__track { grid-auto-columns: 88%; } }

.shot { margin: 0; display: grid; gap: var(--sp-2); }
.shot a {
  display: block;
  border: var(--hairline) solid var(--c-line);
  background: var(--c-paper);
  transition: border-color var(--dur) var(--ease);
}
.shot a:hover { border-color: var(--c-accent); }
.shot img { inline-size: 100%; block-size: auto; aspect-ratio: 16 / 10; object-fit: cover; }
.shot figcaption { color: var(--c-ink-soft); }

/* Der Hinweis „vergrößern" erscheint erst, wenn ein Skript läuft — ohne
   JavaScript öffnet der Klick schlicht die Bilddatei, das braucht keinen Hinweis. */
.shot__zoom { display: none; }
.js .shot__zoom { display: inline; margin-inline-start: 0.5em; color: var(--c-ink-faint); }
.shot a:hover + figcaption .shot__zoom,
.board__item a:hover + figcaption .shot__zoom { color: var(--c-accent); }
/* `.shot__zoom` wird von der Lightbox gezielt entfernt, bevor sie die
   Unterschrift übernimmt. Deshalb trägt die Pinnwand DIESELBE Klasse und
   keine eigene — sonst stünde „· vergrößern" mit im Lightbox-Text. */

/* --- Lightbox ------------------------------------------------
   Wird komplett von `app.js` erzeugt — kein Markup auf den Seiten. So braucht
   keine der kommenden Fallstudien sie zu wiederholen, und ohne JavaScript
   existiert sie gar nicht erst (der Klick öffnet dann die Bilddatei).

   Dunkel in BEIDEN Farbmodi, wie das Burger-Menü und die Fußzeile: Ein Bild
   beurteilt man ruhiger vor dunklem Grund.
   -------------------------------------------------------- */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  /* `minmax(0, 1fr)` statt `1fr`: Eine Rasterzeile wird sonst nie kleiner als
     ihr Mindestinhalt — bei einem Bild also nie kleiner als dessen natürliche
     Höhe. Ein Hochformat lief dadurch unten aus dem Bildschirm. */
  grid-template-rows: auto minmax(0, 1fr);
  background: var(--c-bg-invert);
  color: var(--c-ink-invert);
  --c-line: var(--c-line-invert);
  --c-ink: var(--c-ink-invert);
  opacity: 0;
  visibility: hidden;
  /* Nur die Deckkraft wird weich geblendet. `visibility` schaltet HART:
     beim Öffnen sofort, beim Schließen erst NACH dem Ausblenden (Verzögerung).

     Wichtig, nicht nur Kosmetik: Läge `visibility` im Übergang, wäre das Panel
     im Moment des Öffnens noch `hidden` — und ein verstecktes Element nimmt
     keinen Fokus an. Der Tastaturfokus bliebe hinter der Lightbox hängen. */
  transition: opacity var(--dur) var(--ease), visibility 0s linear var(--dur);
}
.lightbox[data-open="true"] {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--dur) var(--ease), visibility 0s;
}

.lightbox__bar {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  padding: var(--sp-2) var(--gutter);
  border-block-end: var(--hairline) dashed var(--c-line);
}
.lightbox__count { margin-inline-end: auto; color: var(--c-ink-faint-invert); }

.lightbox__btn {
  inline-size: 2.5rem;
  block-size: 2.5rem;
  display: grid;
  place-items: center;
  border: var(--hairline) solid var(--c-line);
  color: var(--c-ink-invert);
  font-family: var(--f-mono);
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.lightbox__btn:hover:not(:disabled) {
  background: var(--c-accent);
  border-color: var(--c-accent);
  color: var(--c-bg-invert);
}
.lightbox__btn:disabled { opacity: 0.3; cursor: default; }
.lightbox__close { margin-inline-start: var(--sp-2); }

.lightbox__stage {
  margin: 0;
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;   /* siehe Begründung bei .lightbox */
  gap: var(--sp-3);
  padding: var(--sp-4) var(--gutter);
  min-block-size: 0;              /* sonst wächst das Bild aus dem Bildschirm */
  justify-items: center;
}
/* Das Bild wird NICHT auf die volle Fläche gezogen: Es behält seine Proportion,
   bleibt unter einer Höchstbreite und sitzt mittig. Randlos über den ganzen
   Bildschirm gespannt kann man einen Screenshot schlechter beurteilen als mit
   etwas Luft drumherum — und auf breiten Schirmen wird er sonst unscharf groß. */
.lightbox__stage img {
  align-self: center;
  inline-size: auto;
  block-size: auto;
  max-inline-size: min(1080px, 100%);
  /* Zusätzliche Kappung am Sichtfeld — `dvh` rechnet die ein- und ausfahrende
     Adressleiste auf dem Handy mit, `vh` tut das nicht. Die erste Angabe ist
     der Rückfall für Browser ohne `dvh`. */
  max-block-size: min(100%, calc(100vh - 9rem));
  max-block-size: min(100%, calc(100dvh - 9rem));
  object-fit: contain;
  border: var(--hairline) solid var(--c-line);
}
.lightbox__caption { text-align: center; color: var(--c-ink-soft-invert); }

/* --- Filterleiste --------------------------------------------
   Nur Komfort: Ohne JavaScript bleibt sie ausgeblendet und alle Arbeiten
   stehen sichtbar da. Ein Knopf, der ohne Skript nichts tut, wäre schlimmer
   als gar keiner — deshalb hängt die Sichtbarkeit an `html.js`.
   -------------------------------------------------------- */

.filterbar { display: none; }
.js .filterbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-block-end: var(--sp-4);
}

.filterbar__btn {
  font-family: var(--f-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-mono);
  letter-spacing: var(--tr-mono);
  text-transform: uppercase;
  color: var(--c-ink);
  padding: 0.5rem 0.85rem;
  border: var(--hairline) solid var(--c-line);
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}
.filterbar__btn:hover { border-color: var(--c-accent); color: var(--c-accent); }
.filterbar__btn[aria-pressed="true"] {
  background: var(--c-ink);
  border-color: var(--c-ink);
  color: var(--c-on-ink);
}
.filterbar__count { margin-inline-start: auto; color: var(--c-ink-faint); }
@media (max-width: 560px) { .filterbar__count { margin-inline-start: 0; } }

/* Platzhalter-Karte: sichtbar, aber erkennbar ohne Ziel. Kein eigener Rahmen —
   die Karte ist dieselbe wie überall, nur zurückgenommen. */
.work--soon { opacity: 0.55; }
.work--soon .work__media i { color: var(--c-ink-faint); }
.work--soon:hover .work__title { color: var(--c-ink); }
.work--soon:hover .work__media { border-color: var(--c-line); }

/* --- Formular ------------------------------------------------
   Keine Kästen mit Rahmen ringsum. Jedes Feld ist eine Linie, auf der
   getippt wird — dasselbe Prinzip wie beim Raster der Seite. Das Label
   steht in Mono darüber, die Linie färbt sich beim Tippen orange.
   -------------------------------------------------------- */

.form {
  display: grid;
  gap: var(--sp-4);
  max-inline-size: 62ch;
  padding-inline-start: var(--sp-4);
}
@media (max-width: 1024px) { .form { padding-inline-start: 0; } }

/* Zwei Felder nebeneinander (Name + E-Mail) */
.form__row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-4); }
@media (max-width: 620px) { .form__row { grid-template-columns: 1fr; } }

.field { display: grid; gap: 0.5rem; margin: 0; }
.field label { color: var(--c-ink); }

.field input,
.field select,
.field textarea {
  font-family: var(--f-sans);
  font-size: var(--fs-body);
  color: var(--c-ink);
  background: transparent;
  border: 0;
  border-block-end: var(--hairline) solid var(--c-line);
  padding-block: 0.55rem;
  padding-inline: 0;
  border-radius: 0;
  transition: border-color var(--dur) var(--ease);
}
.field textarea { resize: vertical; line-height: var(--lh-body); }

/* Auswahlfeld: eigener Pfeil, weil der des Systems nicht zum Raster passt */
.field select {
  appearance: none;
  padding-inline-end: 1.5rem;
  background-image: linear-gradient(45deg, transparent 50%, var(--c-accent) 50%),
                    linear-gradient(135deg, var(--c-accent) 50%, transparent 50%);
  background-position: right 0.55rem center, right 0.25rem center;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}

.field input::placeholder,
.field textarea::placeholder { color: var(--c-ink-faint); }

.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
  border-block-end-color: var(--c-accent);
  outline: 0;                    /* die Linie IST die Rückmeldung … */
  outline-offset: 0;
}
/* … aber nur, wenn sie auch sichtbar ist. Bei Tastaturbedienung ohne
   erkennbare Linie bliebe der Fokus sonst unsichtbar — deshalb zusätzlich
   ein Rahmen um das gesamte Feld, sobald jemand tabbt. */
.field input:focus-visible,
.field textarea:focus-visible { box-shadow: 0 2px 0 -1px var(--c-accent); }

.field__hint { color: var(--c-ink-faint); }

/* Fußzeile des Formulars: Hinweis links, Absenden rechts */
.form__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding-block-start: var(--sp-3);
  border-block-start: var(--hairline) dashed var(--c-line);
}
.form__legal { font-size: var(--fs-small); color: var(--c-ink-soft); max-inline-size: 38ch; }
@media (max-width: 560px) {
  .form__foot { flex-direction: column; align-items: stretch; }
  .form__foot .cta { justify-content: center; }
}

/* .hp — die Falle für automatisierte Einträge.
   NICHT `display: none`: manche Ausfüllhilfen überspringen versteckte Felder
   genau deshalb. Stattdessen aus dem Bild geschoben — für Menschen unsichtbar,
   für ein Skript aber ein ganz normales Feld. */
.hp {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- Rückmeldung nach dem Absenden --------------------------
   Eigene, sehr kurze Seite (kontakt.php). Nutzt den Seitenkopf der
   Unterseiten und braucht deshalb nur zwei Ergänzungen.
   -------------------------------------------------------- */

.pagehead__block > .ctabar { margin-block-start: var(--sp-3); }
.pagehead__block > .cv__list { margin-block-start: 0; }

/* --- Mitlaufende Sektionsanzeige ------------------------------
   Nur auf schmalen Bildschirmen. Dort steht der Sektionskopf ÜBER der Sektion
   statt daneben und ist deshalb nicht mehr `sticky` — beim Scrollen durch eine
   lange Unterseite weiß man dann nicht mehr, worin man liest.

   Die Leiste hängt unter der Kopfleiste und nennt den laufenden Abschnitt.
   Sie wird von app.js erzeugt und ist `aria-hidden`: Für Vorleseprogramme sind
   die Überschriften selbst die Gliederung, eine zweite Stimme daneben stört nur.
   -------------------------------------------------------------- */
.seclabel {
  position: fixed;
  inset-block-start: var(--header-h);
  inset-inline: 0;
  /* Unter dem Menü (60), über dem Seiteninhalt. Mit 40 lag sie gleichauf mit
     der Kopfleiste und deckte den oberen Rand des geöffneten Menüs ab. */
  z-index: 39;
  display: none;
  padding: 0.45rem var(--gutter);
  /* Deckende Angabe zuerst — siehe Begründung bei `.header[data-label]`. */
  background: var(--c-bg);
  background: color-mix(in srgb, var(--c-bg) 72%, transparent);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-block-end: var(--hairline) solid var(--c-line);
  font-family: var(--f-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-mono);
  letter-spacing: var(--tr-mono);
  text-transform: uppercase;
  color: var(--c-ink-soft);
  /* fährt von oben herein, statt zu blinken */
  translate: 0 -100%;
  opacity: 0;
  transition: translate var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
/* Nur mit Skript — ohne bliebe eine leere Leiste stehen. */
.js .seclabel { display: block; }
.seclabel[data-on="true"] { translate: 0 0; opacity: 1; }
/* Bei offenem Menü tritt sie zurück — zwei Leisten übereinander sind eine zu viel. */
html[data-menu="open"] .seclabel { translate: 0 -100%; opacity: 0; }
@supports not (backdrop-filter: blur(1px)) {
  .seclabel { background: var(--c-bg); }
}

/* Nummer in der Signalfarbe, dahinter der Name — wie das Abzeichen der
   Sektion selbst, nur eine Zeile hoch. */
.seclabel b { color: var(--c-accent); font-weight: inherit; }
.seclabel b::after { content: " · "; color: var(--c-ink-faint); }

@media (min-width: 1025px) { .seclabel { display: none !important; } }

/* --- Hinweisfenster unten links -------------------------------
   Bewusst KEIN Zustimmungsbanner mit Auswahlknöpfen: Diese Seite setzt keine
   Cookies, bindet nichts von Dritten ein und misst nichts. Es gibt also nichts
   zuzustimmen — und ein Dialog, der Zustimmung für etwas einholt, das gar nicht
   stattfindet, wäre eine Behauptung, keine Aufklärung.

   Was bleibt, ist ein kurzer Hinweis: dass hier ausnahmsweise nichts passiert.
   Erzeugt von `app.js`, damit er in keiner der Seiten im Markup steht.
   -------------------------------------------------------- */

.hinweis {
  position: fixed;
  inset-block-end: var(--sp-3);
  inset-inline-start: var(--sp-3);
  z-index: 80;
  inline-size: min(23rem, calc(100vw - 2 * var(--sp-3)));
  background: var(--c-bg-invert);
  color: var(--c-ink-invert);
  --c-line: var(--c-line-invert);
  border: var(--hairline) solid var(--c-line-invert);
  display: grid;
  gap: var(--sp-2);
  padding: var(--sp-3);
  opacity: 0;
  translate: 0 0.75rem;
  visibility: hidden;
  transition: opacity var(--dur) var(--ease), translate var(--dur) var(--ease),
              visibility 0s linear var(--dur);
}
.hinweis[data-open="true"] {
  opacity: 1;
  translate: 0 0;
  visibility: visible;
  transition: opacity var(--dur) var(--ease), translate var(--dur) var(--ease), visibility 0s;
}

.hinweis__kopf {
  font-family: var(--f-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-mono);
  letter-spacing: var(--tr-mono);
  text-transform: uppercase;
  color: var(--c-accent);
}
.hinweis p { font-size: var(--fs-small); color: var(--c-ink-soft-invert); }
/* `:not(.cta)`: Fließtext-Links im Kasten bleiben unterstrichen, ein CTA darin
   bringt sein eigenes Aussehen mit — sonst bekäme die leise Variante zusätzlich
   einen Unterstrich und sähe wieder aus wie ein gewöhnlicher Link. */
.hinweis a:not(.cta) { color: var(--c-ink-invert); text-decoration: underline; text-underline-offset: 0.2em; }
.hinweis a:hover { color: var(--c-accent); }

.hinweis__fuss { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.hinweis__fuss .cta { --cta-py: 0.5rem; --cta-px: 0.9rem; }

@media (prefers-reduced-motion: reduce) { .hinweis { translate: none; } }

/* Platzhalter in den Rechtstexten — sichtbar, damit nichts Unausgefülltes
   versehentlich online geht. */
.platzhalter {
  color: var(--c-accent);
  font-weight: 500;
  border-block-end: var(--pipe-w) dashed var(--c-accent);
}

/* Ein normaler Textlink im Fließtext */
.link { color: var(--c-ink); text-decoration: underline; text-underline-offset: 0.2em; }
.link:hover { color: var(--c-accent); }

/* Rechtliches am Fuß — rechts neben dem Copyright */
.footer__recht { display: flex; gap: var(--sp-3); }
.footer__recht a {
  font-family: var(--f-mono);
  font-size: var(--fs-mono);
  font-weight: var(--fw-mono);
  letter-spacing: var(--tr-mono);
  text-transform: uppercase;
  color: color-mix(in srgb, var(--c-ink-invert) 55%, transparent);
  transition: color var(--dur) var(--ease);
}
.footer__recht a:hover { color: var(--c-accent); }

/* ============================================================
   GALLERY  (gallery.php)

   Kein Kopfblock, keine Schale: Die Bilder laufen über die volle
   Breite. Masonry über CSS-SPALTEN statt Raster oder JavaScript —
   damit funktioniert die Seite ohne Skript. Der Preis ist die
   Leserichtung: Der Fluss läuft spaltenweise von oben nach unten
   statt zeilenweise. Für eine Pinnwand ohne Reihenfolge geht das
   in Ordnung; für eine sortierte Liste wäre es falsch.
   ============================================================ */

/* Nur für Vorlesewerkzeuge — die Überschrift der Sektion soll nicht
   sichtbar sein, aber vorhanden bleiben, weil `aria-labelledby`
   darauf zeigt. */
.visually-hidden {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.section--board { padding-inline: var(--gutter); }

.board {
  list-style: none;
  margin: 0;
  padding: 0;
  columns: 5;
  column-gap: var(--sp-2);
}

@media (max-width: 1400px) { .board { columns: 4; } }
@media (max-width: 1024px) { .board { columns: 3; } }
@media (max-width: 768px)  { .board { columns: 2; } }
@media (max-width: 480px)  { .board { columns: 1; } }

.board > li {
  /* Ohne das reißt eine Kachel mitten durch und läuft in der
     nächsten Spalte weiter. */
  break-inside: avoid;
  page-break-inside: avoid;
  margin-block-end: var(--sp-2);
}

.board__item { margin: 0; display: grid; gap: var(--sp-1); }

.board__item a {
  display: block;
  border: var(--hairline) solid var(--c-line);
  background: var(--c-paper);
  transition: border-color var(--dur) var(--ease);
}
.board__item a:hover,
.board__item a:focus-visible { border-color: var(--c-accent); }

/* KEIN festes Seitenverhältnis: Die Höhe kommt aus `width`/`height`
   am Bild selbst. Genau daraus entsteht das ungleiche Raster —
   ein erzwungenes 16:10 wie bei `.shot` würde daraus eine
   gewöhnliche Kachelreihe machen. */
.board__item img {
  display: block;
  inline-size: 100%;
  block-size: auto;
}

.board__item figcaption {
  color: var(--c-ink-faint);
  font-size: var(--fs-mono);
  letter-spacing: var(--tr-mono);
  text-transform: uppercase;
  font-weight: var(--fw-mono);
}

/* Video-Kacheln bekommen eine Marke, damit man sie vom Foto
   unterscheidet, bevor man klickt. */
.board__item--video a { position: relative; }
.board__item--video a::after {
  content: "▶";
  position: absolute;
  inset-block-end: var(--sp-1);
  inset-inline-end: var(--sp-1);
  padding: 0.15rem 0.4rem;
  font-size: var(--fs-mono);
  color: var(--c-on-ink);
  background: color-mix(in srgb, var(--c-ink) 78%, transparent);
}

/* Leerzustand — steht so lange, bis Bilder eingesetzt werden. */
.board__leer {
  border: var(--hairline) dashed var(--c-line);
  padding: var(--sp-4);
  color: var(--c-ink-soft);
}
.board__leer .mono {
  color: var(--c-ink);
  font-size: var(--fs-mono);
  letter-spacing: var(--tr-mono);
  text-transform: uppercase;
  font-weight: var(--fw-mono);
  margin-block-end: var(--sp-1);
}




/* --- Geschwärzte Bezeichnung — `<mark>` -------------------
   Steht dort, wo im Fließtext ein Kundenname stand. Schrift UND Fläche in
   der Signalfarbe: Der Balken sitzt auf der Zeile wie mit dem Cursor
   markierter Text, lesen lässt sich das Wort aber nicht.

   Gesetzt wird auf `mark` selbst und nicht über eine Klasse: Das CMS lässt
   nur eine kurze Liste an Auszeichnungen durch und entfernt jedes Attribut —
   eine Klasse käme dort nie an.

   Die Farbe wechselt mit dem Farbmodus (Orange hell, Grüngelb dunkel), wie
   überall sonst auch. Weil Schrift und Fläche identisch sind, bleibt es in
   beiden Modi gleich dicht.

   `box-decoration-break` sorgt dafür, dass die Fläche bei einem
   Zeilenumbruch auf beiden Teilen sitzt und nicht nur auf dem ersten.

   Der Text steht weiterhin im Quelltext und lässt sich markieren. Das ist
   hinnehmbar: Verborgen wird nur die Branchenbezeichnung — der Kundenname
   selbst steht nirgends mehr auf der Seite.
   -------------------------------------------------------- */
mark {
  background: var(--c-accent);
  color: var(--c-accent);
  padding: 0 0.22em;
  border-radius: 0.12em;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

/* Beim Markieren mit der Maus soll der Balken nicht plötzlich lesbar werden. */
mark::selection { background: var(--c-accent); color: var(--c-accent); }
