@font-face{font-family:Geist;src:url(/assets/fonts/geist-sans.woff2?v=20260903) format("woff2");font-weight:100 900;font-display:swap}
@font-face{font-family:"Geist Mono";src:url(/assets/fonts/geist-mono.woff2?v=20260903) format("woff2");font-weight:100 900;font-display:swap}
/* ERSATZSCHRIFT MIT ANGEGLICHENEN MASSEN — gegen den Sprung beim Schriftwechsel.
   font-display:swap zeigt zuerst die Systemschrift und tauscht dann. Weil deren
   Masse andere sind, rueckt in diesem Moment JEDER Textblock — Lighthouse zaehlt
   das als Cumulative Layout Shift.

   Gemessen aus den Dateien, nicht geschaetzt:
     Geist     Oberlaenge 100,50%  Unterlaenge 29,50%  Zeilenzwischenraum 0%
               mittlere Vorbreite a-z 53,38%
     Arial     Oberlaenge  90,53%  Unterlaenge 21,19%  Zwischenraum 3,27%
               mittlere Vorbreite a-z 48,95%
     Segoe UI  Oberlaenge 107,91%  Unterlaenge 25,10%  Vorbreite 49,33%

   Geists Zeilenkasten ist 130,0% vom Geviert, Arials 115,0% — 13% Unterschied
   auf JEDER Zeile. Die Ueberschreibungen geben der Ersatzschrift exakt Geists
   senkrechte Masse; size-adjust gleicht die Breite an, damit auch die Zahl der
   Zeilen gleich bleibt (sonst bricht der Text vor und nach dem Tausch anders um).

   KEIN size-adjust — und das ist ein korrigierter Irrtum. Zuerst stand hier
   109,1%, hergeleitet aus dem Verhaeltnis der mittleren Vorbreite der
   Kleinbuchstaben (Geist 53,38%, Arial 48,95%). Der Wert war falsch, weil die
   Groesse nicht die richtige ist: was zaehlt, ist die Zahl der ZEILEN bei
   echtem Text — und der besteht aus Versalien, Leerzeichen, Umlauten und
   Ziffern, nicht aus Kleinbuchstaben allein.

   Nachgemessen an 14 echten Textbausteinen dieser Seite, jeweils in ihrer
   tatsaechlichen Breite und Groesse, Summe der Hoehenabweichung gegen Geist:

     Arial ohne alles          0 px
     nur senkrechte Angleichung 0 px
     + size-adjust 102%         0 px
     + size-adjust 104%        24 px
     + size-adjust 106 bis 109% 50 px

   Arial trifft Geists Umbruch also von allein; jedes Hochskalieren erzwingt
   zusaetzliche Zeilen und macht den Sprung GROESSER statt kleiner.

   Was bleibt, sind die senkrechten Masse. Sie kosten nichts und wirken dort,
   wo kein line-height gesetzt ist und der Zeilenkasten aus der Schrift kommt —
   Geist 130,0% vom Geviert, Arial 115,0%.
   Geprueft mit Arial, weil die hier vorliegt. Auf Android faellt der Stapel
   auf Roboto, auf Apple-Geraeten auf -apple-system; die Systemschrift laesst
   sich per local() nicht ansprechen, dort wirkt die Angleichung nicht. */
@font-face{font-family:"Geist Ersatz";src:local("Arial"),local("Roboto"),local("Helvetica Neue");
  ascent-override:100.5%;descent-override:29.5%;line-gap-override:0%}

:root{
  --white:#ffffff; --paper:#f5f8fc;
  /* --muted von #6b7d8f auf #627486 verdunkelt: auf dem Kartengrund #f5f8fc
     kam es auf 3,98 zu 1 und verfehlte die geforderten 4,5 knapp. Jetzt 4,52.
     Betroffen waren u.a. die Beschriftungen der Messwerte und die Quellzeile. */
  --ink:#0d1826; --ink-soft:#41566b; --muted:#627486; --line:#e4eaf1;
  --navy:#020914;
  /* --cyan bleibt die MARKENFARBE (Balken, Marker, Knopf). Geaendert ist nur
     --cyan-ink, die Textvariante: #0784b0 kam auf dem Kartengrund #f5f8fc auf
     3,99 zu 1 und verfehlte die 4,5. #007aa6 erreicht 4,55.
     Das betraf rund hundert Textstellen auf der Seite — Eyebrows, die
     Case-Kennzeichnung, "Live ansehen", die Schrittnummern. Der sichtbare
     Unterschied ist minimal, der Unterschied im Audit ist bestanden gegen
     durchgefallen. */
  --cyan:#19bff4; --cyan-ink:#007aa6; --cyan-soft:#e8f7fe;
  --bezel:#0b1422; --chrome:#101c2e; --chrome2:#0c1626;
  --page:clamp(22px,5vw,80px);
  --wrap:1180px;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body.web{margin:0;background:var(--white);color:var(--ink);
  font-family:Geist,"Geist Ersatz",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
img{max-width:100%;display:block}
h1,h2,h3{margin:0}
.skip{position:absolute;left:-999px;top:auto}
.skip:focus{left:16px;top:16px;z-index:99;padding:10px 16px;background:var(--navy);color:#fff;border-radius:8px}
@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

/* ---- Bausteine ---- */
.eyebrow{margin:0 0 14px;font-size:.8rem;font-weight:680;letter-spacing:.14em;text-transform:uppercase;color:var(--cyan-ink)}
.eyebrow--light{color:#6fd3f5}
/* ECKIG, wie jeder andere Knopf des Hauses. Die Startseite (.button in
   site.css) deklariert gar keinen Radius, die Leiste (.topnav__cta) und das
   Finale (finale.css) setzen ihn ausdruecklich auf 0 — mit Begruendung: die
   Pille war die Erfindung der Unterseiten und genau der sichtbare Unterschied
   beim Seitenwechsel. Dieser Knopf hier war der letzte Rest davon.
   NICHT betroffen: die Knoepfe INNERHALB der Shop-Attrappe auf /shops. Die
   sollen wie ein echter Shop aussehen, nicht wie das Knopfsystem dieser
   Seite — dort bleibt die Rundung richtig. */
.web-btn{display:inline-flex;align-items:center;justify-content:center;height:52px;padding:0 26px;border-radius:0;
  background:var(--cyan);color:#00202e;font-weight:700;font-size:1rem;text-decoration:none;border:0;cursor:pointer;
  transition:transform .15s ease,background-color .15s ease,box-shadow .18s ease}
.web-btn:hover{transform:translateY(-1px);background:#43ccfb;box-shadow:0 12px 28px rgba(25,191,244,.32)}
.web-btn--lg{height:58px;padding:0 34px;font-size:1.06rem}
.web-link{display:inline-flex;align-items:center;gap:8px;color:var(--cyan-ink);font-weight:640;text-decoration:none}
.web-link::after{content:"→";transition:transform .2s ease}
.web-link:hover::after{transform:translateX(4px)}
.web-link--light{color:#8fdcf7}

/* ---- Fokus: sichtbar, einheitlich, nur bei Tastaturbedienung ----
   Ein sauberer Fokusring ist kein Detail fuer Spezialfaelle — er ist das,
   woran man eine sorgfaeltig gebaute Seite erkennt. */
:where(a,button,summary,[tabindex]):focus-visible{outline:2.5px solid var(--cyan-ink);outline-offset:3px;border-radius:5px}
.web-btn:focus-visible{outline-offset:4px}
.faq__item summary:focus-visible{outline-offset:-2px}

/* ---- Kopfleiste ------------------------------------------------------------
   Das Markup und die Mechanik liefern site-nav.css / site-nav.js — dieselbe
   Datei wie auf Startseite und /software, damit die Leiste ueberall gleich
   liegt und gleich heisst. Hier stehen nur die Farben.

   Die geteilte Datei setzt jede Farbe ueber eine Variable mit DUNKLEM
   Rueckfallwert. Diese Seite ist hell, also werden die Variablen hier
   umgesetzt — und nur hier, unter body.web. Die geteilte Datei anzufassen
   wuerde den hellen Kopf auch auf die dunklen Seiten ziehen. */
body.web{
  --nav-bg:rgba(255,255,255,.92);
  --nav-linie:var(--line);
  /* Helle Leiste: die farbige Fassung zeigen, die dunkle ausblenden.
     Siehe site-nav.css, dort liegt die Mechanik. */
  --nav-marke-dunkel:0;
  --nav-ruhe:var(--ink-soft);
  --nav-hell:var(--ink);
  --nav-akzent:var(--cyan);
  --nav-akzent-hell:#43ccfb;
  --nav-akzent-text:#00202e;
  /* Auf Weiss reicht ein leiser Schein; der dunkle Rueckfallwert (.7) wirkt
     hier wie ein Leuchtstift. */
  --nav-glow:rgba(25,191,244,.42);
  /* Abschnitts-Schiene rechts: benutzt die Mechanik der Punkt-Schiene aus
     site-nav.css, sieht hier aber anders aus (unterteiltes Rechteck, weiter
     unten in dieser Datei). Diese Werte versorgen beide Fassungen. */
  --rail-ruhe:rgba(65,86,107,.32);
  --rail-hell:var(--ink);
  --rail-akzent:var(--cyan);
  --rail-glow:rgba(25,191,244,.55);
  --rail-chip:rgba(255,255,255,.96);
  --pill-linie:var(--line);
}
/* DAUERHAFT SICHTBAR statt transparent-bis-gescrollt.
   Die geteilte Datei laesst die Leiste ueber dem Hero transparent starten — das
   passt zu den dunklen Seiten, wo sie auf einem Bild liegt. Diese Seite ist hell
   und der Hero ist eine helle Szene: dort verschwindet eine transparente Leiste
   und der Kopf wirkt unfertig. So sah der alte Kopf dieser Seite aus, und so
   bleibt er. Der Schatten kommt dazu, weil eine weisse Leiste auf weissem Grund
   sonst nur an der Haarlinie haengt.
   box-shadow steht nicht in der transition der geteilten Datei, deshalb hier
   mitgegeben — sonst schnappt er beim Scrollen. */
body.web .topnav{
  background:rgba(255,255,255,.88);
  -webkit-backdrop-filter:blur(12px) saturate(1.1);backdrop-filter:blur(12px) saturate(1.1);
  border-bottom-color:var(--line);
  box-shadow:0 6px 24px -18px rgba(13,24,38,.5);
  transition:background-color .28s ease,border-color .28s ease,box-shadow .28s ease}
/* Gescrollt nur ETWAS praesenter — kein zweiter Zustand, nur mehr Tiefe. */
body.web .topnav.is-scrolled{background:rgba(255,255,255,.94);
  box-shadow:0 10px 30px -20px rgba(13,24,38,.62)}
/* Mobil faehrt die Leiste beim Runterscrollen nach oben aus dem Bild; die
   Klasse setzt hero.js. 64px sind auf einem Handy im Hero der Unterschied
   zwischen „Chip-Reihe im Bild" und „Chip-Reihe darunter" — und sie kommen
   hier her, statt das Geraet zu kuerzen (das war der vorherige Weg und hat
   das Handy auf kleinen Schirmen um 8 bis 15px gestaucht).
   Die drei Punkte bleiben stehen: der Griff ist position:fixed und haengt
   nicht an der Leiste.
   NUR mobil — auf dem Desktop ist die Leiste die Navigation der Seite und das
   Platzproblem gibt es dort nicht. */
@media(max-width:768px){
  body.web .topnav{transition:background-color .28s ease,border-color .28s ease,
    box-shadow .28s ease,transform .3s cubic-bezier(.4,0,.2,1)}
  body.web .topnav.is-weg{transform:translateY(-100%)}
}
@media(prefers-reduced-motion:reduce){
  body.web .topnav,body.web .topnav.is-weg{transition:none}
}

/* ------------------------------------------------- Abschnitts-Schiene ---
   Rechts am Rand: EIN Rechteck, quer unterteilt in ein Feld je Abschnitt.
   Markup, Mechanik und JS sind die der Punkt-Schiene aus site-nav.css --
   derselbe [data-snav]-Block, dieselbe Datei, keine zweite Zustaendigkeit
   fuer dieselbe Sache. Geaendert ist ausschliesslich die Optik, und zwar aus
   einem Grund: diese Seite spricht scharfe Kanten und Haarlinien (die
   Branchen-Karten, der Strich darunter). Runde Punkte wie auf /software sind
   hier Fremdsprache -- der Zusammenhang bleibt in der Position, der Mechanik
   und der Beschriftung, die Form darf die der Seite sein.

   Zwei Entscheidungen bewusst gegen die erste Skizze („ganz am Rand"):
   - NICHT buendig an der Kante und NICHT ueber die ganze Hoehe. Ein
     unterteilter Balken direkt am Fensterrand liest sich als Scrollleiste,
     und dann versuchen Leute zu ziehen. 38px breit (eine Scrollleiste ist
     ~12) und rund 200px hoch statt voller Hoehe nehmen die Verwechslung raus.
   - Jedes Feld traegt seine Haarlinie SELBST und ueberlappt den Nachbarn um
     1px, statt dass ein Rahmen um alles liegt. Dadurch entsteht das Rechteck
     beim Hereinfahren Feld fuer Feld, und im Hero steht nie ein leerer
     Rahmen herum. Mit Rahmen haette es einen :has()-Umweg gebraucht.

   Ausgeblendet wird sie nicht hier: site-nav.css nimmt .dotrail unter 768px
   und unter 520px Fensterhoehe schon raus. Auf dem Handy stehen die
   Abschnitte im Menue -- das JS laeuft weiter und versorgt es. */
/* Breite und Abstand sind GEMESSEN, nicht geschaetzt: der rechte Slider-Pfeil
   der Auftritte haengt per .arrow--r{right:-14px} absichtlich ueber die Kante
   seines Rahmens und ragt damit in denselben Seitenrand. Mit 38px Feldbreite
   und clamp(10px,1.5vw,22px) Abstand ueberlappten die beiden bei 1280/1366/
   1440px um 7/4/2px -- nie die Mitte des 44px-Knopfes, aber sein aeusserer
   Rand, und beim Vorbeiscrollen frisst das einen Klick. Der Pfeil war zuerst
   da, also weicht die Schiene: 34px breit, 10px Abstand. Nachgemessen bleiben
   damit bei 1280..1920px 8..22px Luft, auch wenn der Pfeil beim Hover auf
   scale(1.16) waechst (+3,5px je Seite). 34px sind immer noch mehr als das
   Doppelte einer Scrollleiste — die Verwechslung kommt dadurch nicht zurueck. */
body.web .dotrail--felder{
  right:10px;
  align-items:stretch;
  gap:0}

/* DIE KANTE IST EIN INSET-SCHATTEN, KEIN border — und das ist Voraussetzung
   fuer den Farbwechsel weiter unten, nicht Geschmack. Die Deckebene muss das
   Feld inklusive seiner Kante ueberdecken. Mit einem echten border liegt sie
   entweder darin (dann bleibt aussen eine helle Haarlinie stehen) oder sie
   ragt mit inset:-1px darueber hinaus — dann ueberlappen sich benachbarte
   Deckebenen um 3px und an jeder Naht stehen zwei Linien mit einem Pixel
   Abstand. Als Innenschatten liegt die Kante INNERHALB des Kastens, die
   Deckebene sitzt buendig auf inset:0 und beides passt genau aufeinander. */
body.web .dotrail--felder a{
  width:34px;height:40px;justify-content:center;
  background:var(--white);border:0;box-shadow:inset 0 0 0 1px var(--line);
  transition:opacity .32s ease,transform .44s cubic-bezier(.22,.7,.3,1),
             background-color .2s ease,box-shadow .2s ease}
/* Zwei Haarlinien zwischen zwei Feldern waeren eine doppelte Linie. */
body.web .dotrail--felder a + a{margin-top:-1px}
body.web .dotrail--felder a:hover{background:var(--paper);box-shadow:inset 0 0 0 1px rgba(65,86,107,.3)}
/* Das aktive Feld liegt VOR seinen Nachbarn: sonst schneidet die Haarlinie des
   naechsten Feldes seine Marke an der Unterkante an.
   Es behaelt seine HAARLINIE. Erster Versuch war ein Navy-Rahmen — angesehen
   liest man dann oben und unten zwei fette Querbalken, und aus dem einen
   unterteilten Rechteck wird eine Zelle, die aus dem Verbund herausspringt.
   Markiert wird stattdessen mit der Marke links und einer Spur Cyan im
   Hintergrund; die Ziffer geht ohnehin auf volles --ink. */
body.web .dotrail--felder a[aria-current="true"]{
  position:relative;z-index:1;background:var(--cyan-soft);box-shadow:inset 0 0 0 1px var(--line)}

/* ::after ist hier nicht der Punkt, sondern die Marke an der LINKEN Kante des
   Feldes: sie zeigt in die Seite hinein, nicht aus ihr heraus. Die Masse der
   Punkt-Fassung (7x7, rund, margin, scale-transform) muessen alle zurueck. */
/* Die Marke sass auf der AUSSENKANTE des frueheren border, also bei -1px. Seit
   die Kante ein Innenschatten ist, liegt dieselbe Stelle bei 0. */
body.web .dotrail--felder a::after{
  position:absolute;z-index:2;left:0;top:0;bottom:0;
  width:2px;height:auto;margin:0;border-radius:0;
  background:var(--rail-akzent);box-shadow:none;
  transform:scaleY(0);transform-origin:50% 50%;
  transition:transform .28s cubic-bezier(.22,.7,.3,1),opacity .28s ease}
body.web .dotrail--felder a[aria-current="true"]::after{transform:scaleY(1)}

/* ---- Fortschritt: EINE durchgezogene Linie ueber die ganze Schiene -------
   Ein Zwischenstand hat die Marke am aktiven Feld zum Fortschrittsbalken
   gemacht — die faengt dann aber in jedem Abschnitt wieder bei null an und
   liest sich als fuenf kleine Balken statt als ein Weg durch die Seite.
   Jetzt eine Linie ueber die volle Hoehe der Schiene, gefuellt von oben nach
   dem Stand der GANZEN Seite.
   SIE STEHT AUF DER ANDEREN SEITE. Die Abschnittsmarke sitzt links am Feld;
   beide an derselben Kante waeren zwei Linien uebereinander. Rechts ist
   ausserdem die richtige Seite: dort liegt der Fensterrand, und ein
   Fortschritt gehoert an den Rand, nicht in die Sache hinein.
   Zwei Elemente, weil die Bahn ueber die volle Hoehe steht und die Fuellung
   nur bis --seite. Beide ohne Transition — der Stand soll exakt mitgehen und
   nicht nachlaufen. */
body.web .dotrail--felder::before,
body.web .dotrail--felder::after{content:"";position:absolute;right:-5px;top:0;width:2px;
  z-index:2;pointer-events:none;
  /* NEBEN der Schiene, nicht auf ihr, und darueber. Auf right:0 lag die Linie
     genau auf der rechten Kante der Felder — die sind deckend, und das aktive
     traegt z-index:1, also war sie ausgerechnet an der interessanten Stelle
     verdeckt. Jetzt sitzt sie in der Luecke zwischen Schiene und Fensterrand
     (die Schiene steht auf right:10px, die Linie belegt davon 3 bis 5px) und
     liegt mit z-index:2 ueber allem, was in der Schiene passiert. */
  /* MUSS mit der Schiene kommen, nicht vorher. Die Felder stehen beim Laden auf
     opacity:0 und blenden sich einzeln ein, sobald ihr Abschnitt dran ist — eine
     Linie, die sofort da ist, haengt bis dahin allein am Fensterrand und sieht
     nach Fehler aus. :has() fragt, ob ueberhaupt schon ein Feld eingeblendet
     wurde; die Klasse dafuer setzt site-nav.js ohnehin. */
  opacity:0;transition:opacity .32s ease}
body.web .dotrail--felder:has(a.is-da)::before,
body.web .dotrail--felder:has(a.is-da)::after{opacity:1}
body.web .dotrail--felder::before{bottom:0;background:rgba(25,191,244,.16)}
body.web .dotrail--felder::after{height:calc(var(--seite,0) * 100%);background:var(--rail-akzent)}

/* site-nav.css versteckt <b> — die Punkt-Fassung braucht keine Nummer. Hier
   traegt jedes Feld seine, sonst stehen dort fuenf leere Kaestchen. Farbe
   NICHT --rail-ruhe: das ist ein Punktgrau (rund 1,6:1 auf Weiss) und als
   Ziffer unlesbar. --ink-soft liegt bei etwa 7:1. */
/* GROESSE UND GEWICHT MUESSEN HIER UND AN DER DECKEBENE GLEICH SEIN. Die Zahl
   wird an der Naht aus zwei Haelften zusammengesetzt — unten die gezeichnete,
   oben diese. Weichen sie ab, sitzt die Naht in zwei verschieden grossen
   Ziffern und es sieht aus wie ein Darstellungsfehler.
   Von .62rem/600 auf .72rem/700 hochgesetzt: auf Dunkel war die Zahl trotz
   Weiss nicht zu entziffern, und das lag nicht am Kontrast (15,5:1), sondern
   an 9,9px feiner Mono. Helle Striche auf dunklem Grund brauchen mehr Flaeche,
   nicht mehr Kontrast. Das Feld ist 34px breit, zwei Ziffern auf 11,5px
   brauchen rund 14px — Platz ist reichlich. */
body.web .dotrail--felder a b{
  display:block;position:static;right:auto;opacity:1;pointer-events:none;
  font:700 .72rem/1 "Geist Mono","Geist",ui-monospace,monospace;
  letter-spacing:.02em;color:var(--ink-soft);
  /* --mx ist die Anziehung zum Zeiger, die site-nav.js je Bild schreibt. In
     der Punkt-Fassung bewegt sie den Punkt; hier darf sich der Rahmen nicht
     verziehen — also geht nur die Ziffer ein Stueck mit, gedaempft auf ein
     Drittel. Das Rechteck bleibt starr, lebendig ist der Inhalt. */
  transform:translateX(calc(var(--mx,0px) * .34));
  transition:color .2s ease,transform .18s cubic-bezier(.22,.7,.3,1)}
body.web .dotrail--felder a:hover b,
body.web .dotrail--felder a[aria-current="true"] b{color:var(--ink)}

/* Die Beschriftung faehrt links aus dem Feld. Zwei Aenderungen gegenueber der
   Punkt-Fassung:
   - right:20px ist dort auf einen 7px-Punkt gerechnet; am 34px-Feld laege der
     Chip zur Haelfte darauf.
   - Das Aussehen. Diese Seite hat schon eine Tooltip-Sprache: .arrow__tip an
     den Slider-Pfeilen der Auftritte — dunkles Navy, helle Schrift, 7px
     Radius. Die geteilte Datei macht daraus eine weisse Pille mit 999px. Zwei
     verschiedene Tooltips auf derselben Seite waere einer zu viel, also folgt
     die Schiene dem, was hier schon steht. */
body.web .dotrail--felder a span{
  right:calc(100% + 9px);
  height:auto;padding:5px 10px;border-radius:7px;
  font-size:.76rem;font-weight:600;letter-spacing:0;
  color:#eaf3fb;background:var(--navy);
  box-shadow:0 6px 18px rgba(2,9,20,.4)}

/* Die Welle der Punkt-Fassung schiebt jeden Punkt waagerecht heraus. In einem
   starren Rechteck waere genau das ein Defekt. Also laeuft statt der Bewegung
   ein Licht durch die Marken, von oben nach unten — derselbe Gedanke („ein
   Ruck laeuft durch"), in der Form, die diese Schiene erlaubt. Das aktive
   Feld bleibt aussen vor: seine Marke soll nicht kurz verschwinden. */
@keyframes feldWelle{
  0%,100%{transform:scaleY(0);opacity:0}
  32%    {transform:scaleY(1);opacity:1}
  66%    {transform:scaleY(1);opacity:.4}
}
body.web .dotrail--felder.is-welle a.is-da::after{animation:none}
body.web .dotrail--felder.is-welle a.is-da:not([aria-current="true"])::after{
  animation:feldWelle .8s cubic-bezier(.25,.6,.3,1) calc(var(--i,0) * 85ms)}

/* Sprungreserve. site-nav.js schaltet mit der Schiene html.snav-an, und
   site-nav.css setzt darueber scroll-margin-top: nav-hoehe + 16px. Die 16px
   rechnen mit Sektionen ohne eigenes Polster -- diese hier haben 100px
   (Desktop) bzw. 52px (mobil) oben. Zusammen standen nach einem Sprung 116px
   bzw. 68px Leere ueber der Ueberschrift.
   Richtig ist genau die Leistenhoehe: dann beginnt die Sektion unmittelbar
   unter der Leiste und ihr eigenes Polster macht den Abstand, so wie ohne
   Leiste auch. Ohne diese Zeile war es umgekehrt falsch -- ohne Schiene gab es
   gar keine Reserve, und gemessen verschwanden auf 390px 12 von 17px der
   Eyebrow-Zeile hinter der Leiste. Nur fuer diese Seite: /software hat andere
   Polster und behaelt den geteilten Wert. */
/* NUR DESKTOP, und die Grenze ist gemessen statt gesetzt. site-nav.css nimmt
   .dotrail erst unter 768px raus -- das passt zu /software, nicht zu dieser
   Seite: hier steht im Rand schon der rechte Slider-Pfeil der Auftritte. Von
   769 bis 1279px reicht der Rand fuer beide nicht. Gemessen (Luft zwischen
   Pfeil und Schiene / zwischen Schiene und Case-Karte):
        769px  -20px / -6px      1024px  -7px /  7px
        900px  -13px /  1px      1100px  -3px / 11px
       1000px   -8px /  6px      1180px   1px / 15px
       1280px    6px / 20px  <-- erste Breite, bei der beides frei liegt
   Bei 769px lag die Schiene also auf der Karte. Ab 1280px hat der Pfeil auch
   im Hover (scale 1.16, +3,5px je Seite) noch Luft. Darunter uebernimmt das
   Menue die Abschnitte -- genau wie auf dem Handy.
   Das JS laeuft in jedem Fall weiter: html.snav-an und damit die Sprungreserve
   unten haengen an der Schiene im Markup, nicht an ihrer Sichtbarkeit. */
/* ---- Farbwechsel PIXELGENAU an der Sektionskante ------------------------
   Die Schiene haengt fest am Fensterrand und belegt dort 44px (10px Abstand
   plus 34px Breite). Mit weissem Grund stanzte sie ein helles Loch in den
   dunklen Takt und in den Abschluss — es wirkte, als waere die Sektion rechts
   abgeschnitten. (Nachgemessen ist sie das nicht: .takt ist auf 1920px genau
   1920px breit. Es war die Schiene.)

   Eine erste Fassung hat die ganze Schiene umgeschaltet, sobald eine dunkle
   Sektion ihr Band erreichte. Jetzt wechselt sie an der KANTE: liegt die Naht
   zwischen hell und dunkel mitten in einem Feld, ist der obere Teil des Feldes
   hell gestaltet und der untere dunkel — auf den Pixel.

   WIE: jedes Feld traegt eine zweite, rein zeichnerische Ebene (::before) in
   der dunklen Fassung, buendig darueber, und die wird beschnitten. Sichtbar ist
   von ihr genau der Teil, der ueber Dunkel liegt; darueber und darunter ist sie
   weggeschnitten. clip-path ist dabei nicht animiert — der Sinn der Sache ist,
   dass sie exakt mitgeht und nicht nachlaeuft.
   Sie zeichnet die Ziffer NEU (content:attr(data-nr)), weil auch die Zahl an der
   Naht umschlagen muss; ein Pseudoelement kann den echten <b> nicht umfaerben.
   Deshalb steht die Nummer im Markup zweimal — einmal als Text fuer Vorlese-
   programme und Auswahl, einmal als Attribut fuer die Deckebene.
   Die Werte --dk-o (Schnitt von oben) und --dk-u (von unten) setzt hero.js.
   Ohne JS bleibt --dk-o auf 100%: dann ist die Deckebene komplett weg und die
   Schiene sieht aus wie immer.
   Die Deckebene liegt UNTER dem Tooltip und unter der Cyan-Marke: beide sind
   spaeter im Baum (span) bzw. spaeter als Pseudoelement (::after) und malen
   damit darueber. Genau so soll es sein. */
body.web .dotrail--felder a::before{
  content:attr(data-nr);
  /* z-index IST NOETIG, und der Grund ist unauffaellig: die echte Ziffer traegt
     ein transform (die Zeiger-Anziehung, --mx). Transformierte Elemente malen
     in DERSELBEN Ebene wie positionierte, und darin nach Baumreihenfolge — das
     <b> steht nach dem ::before, also lag die dunkle Ziffer ueber der weissen.
     Am Pixel gemessen: der hellste Punkt in der Ziffer war (136,145,155) statt
     Weiss, weil vom weissen Glyph nur der Kantensaum durchkam. Mit z-index
     liegt die Deckebene wieder oben. */
  position:absolute;inset:0;z-index:1;box-sizing:border-box;
  display:grid;place-items:center;
  /* NAVY ZUERST, dann derselbe helle Schleier darueber. Die Deckebene liegt auf
     dem WEISSEN Feld — ein blosses rgba(255,255,255,.055) waere dort Weiss auf
     Weiss und damit unsichtbar. Erst deckt sie das Feld mit der Sektionsfarbe
     zu, dann kommt der Schleier, der die Kachel vom Grund abhebt. So steht die
     Rechnung im Code statt eines von Hand ausgemischten Hexwerts: aendert sich
     --navy, geht die Kachel automatisch mit. */
  background:linear-gradient(rgba(255,255,255,.10),rgba(255,255,255,.10)),var(--navy);
  /* Echter Rahmen statt Innenschatten: der Hintergrund laeuft unter ihm durch,
     also mischt sich sein Alpha mit dem Navy darunter und nicht mit dem hellen
     Feld. Mit border-box deckt er genau die Stelle, an der das Feld selbst
     seine Kante zeichnet. */
  border:1px solid rgba(255,255,255,.28);
  /* WEISS. Die helle Seite stellt die Ziffer in --ink-soft auf Weiss, also
     auf 7,6:1 — dieselbe Zahl umgekehrt ergibt eine Ziffer, die duenner WIRKT
     als sie ist: helle Striche auf dunklem Grund werden vom Auge schmaler
     gelesen, und bei 9,9px Mono kippt das ins Unleserliche. Zwei
     Zwischenstaende belegen es: #8fa6bd lag bei 6,4:1 und war nicht zu
     entziffern, #d6e3f0 bei 12,3:1 und ging gerade so. Weiss auf der Fuellung
     sind 15,5:1, und mehr geht nicht — hier ist kein Spielraum zu holen, also
     nimmt man das Ende der Skala. */
  color:#fff;
  font:700 .72rem/1 "Geist Mono","Geist",ui-monospace,monospace;
  letter-spacing:.02em;
  pointer-events:none;
  clip-path:inset(var(--dk-o,100%) 0 var(--dk-u,0px) 0);
  transition:background-color .2s ease,border-color .2s ease,color .2s ease}
body.web .dotrail--felder a:hover::before{
  background:linear-gradient(rgba(255,255,255,.16),rgba(255,255,255,.16)),var(--navy);
  border-color:rgba(255,255,255,.42);color:#fff}
body.web .dotrail--felder a[aria-current="true"]::before{
  background:linear-gradient(rgba(25,191,244,.24),rgba(25,191,244,.24)),var(--navy);
  border-color:rgba(255,255,255,.28);color:#fff}
/* Die Zeiger-Anziehung schiebt die ECHTE Ziffer waagerecht (--mx aus
   site-nav.js), die gezeichnete auf der Deckebene aber nicht — sie kann sich
   nicht getrennt von ihrem Kasten bewegen. In einem GETEILTEN Feld liefen die
   beiden Haelften derselben Ziffer damit um bis zu 4px auseinander. In genau
   diesem Fall steht die Ziffer still; hero.js setzt die Klasse. Ausserhalb der
   Naht bleibt die Anziehung wie sie war. */
body.web .dotrail--felder a.is-geteilt b{transform:none}
/* Der Tooltip ist selbst navy und steht neben der Schiene, also immer ueber
   derselben Flaeche wie das Feld, aus dem er kommt. Eine Kante bekommt er
   pauschal — auf Hell stoert sie nicht, auf Navy traegt sie die Form. */
body.web .dotrail--felder a span{border:1px solid rgba(255,255,255,.15)}

@media(max-width:1279px){
  body.web .dotrail--felder{display:none}
}

html.snav-an body.web [id]{scroll-margin-top:var(--nav-hoehe,80px)}

@media(prefers-reduced-motion:reduce){
  body.web .dotrail--felder a,body.web .dotrail--felder a::after,
  body.web .dotrail--felder a b{transition:none}
  body.web .dotrail--felder.is-welle a.is-da:not([aria-current="true"])::after{
    animation:none}
}

/* ======================================================================
   HERO — Monitor-Raum (Live-Vorschau, Deviceswitch, Feierabend-Egg)
   portiert aus raum-prototype.html · self-contained, unter .stage gescoped
   ====================================================================== */
/* min-height ZWEIMAL: svh gibt es erst ab Safari 15.4 / iOS 15.4. Aeltere
   Browser verwerfen die zweite Zeile und behalten 100vh, neuere nehmen svh
   (die kleine Ansicht mit ausgefahrener Adressleiste — die stabile). Ohne
   den Rueckfall haette die Stage dort gar keine Mindesthoehe. */
.stage{position:relative;min-height:100vh;min-height:100svh;overflow:hidden;
  display:grid;grid-template-columns:.68fr 1.32fr;gap:clamp(20px,2.4vw,36px);align-items:end;
  /* Oben die Leistenhoehe mitrechnen: .topnav ist FIXED und belegt keinen
     Platz im Fluss (die alte .web-head war sticky und tat es). Ohne das laege
     der Hero unter der Leiste. --nav-hoehe kommt aus site-nav.css und ist dort
     unter 768px selbst schon kleiner. */
  padding:calc(var(--nav-hoehe,80px) + clamp(14px,2vw,26px)) clamp(22px,5vw,80px) clamp(48px,6vw,80px);
  background:
    radial-gradient(1000px 540px at 78% 12%,rgba(140,175,210,.10),transparent 60%),
    linear-gradient(180deg,#fdfdfe 0%,#f7f9fc 55%,#eef2f7 100%)}
.stage__text{align-self:center}
.rig{align-self:end;margin-bottom:clamp(14px,2.5vh,32px)}
/* Full-bleed-Szene: cozy Grossraumbuero in Tiefenebenen */
.szene{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.layer{position:absolute;left:0;right:0;bottom:0;will-change:transform}
.layer svg{display:block;width:100%;height:auto}
.layer--wand{top:-24px;bottom:clamp(100px,14vh,160px)}
.layer--wand svg{width:100%;height:100%}
.pendant{position:absolute;top:-24px;width:92px;transform:translateX(-50%);z-index:0;pointer-events:none}
.pendant svg{display:block;width:100%;height:auto}
/* ===== Feierabend-Easter-Egg: Tasse klicken -> Nacht + Lampe an -> Sonnenaufgang ===== */
.prop--mug{cursor:pointer;pointer-events:auto}
.night-veil{position:absolute;inset:0;z-index:0;pointer-events:none;opacity:0;transition:opacity 2.4s ease;
  background:radial-gradient(1100px 640px at 58% 4%, rgba(30,140,200,.16), transparent 62%),
             linear-gradient(180deg,#060e1c 0%,#0a1526 52%,#0b1832 100%)}
.stage.is-night .night-veil{opacity:1}
.stage.is-sunrise .night-veil{transition:opacity 3.2s ease}
/* Sonnenaufgang: das warme Licht STEIGT (background-position wandert hoch), am Fenster verankert */
.sun{position:absolute;inset:0;z-index:0;pointer-events:none;opacity:0;
  background:radial-gradient(1080px 820px at 54% 16%, rgba(255,196,118,.66), rgba(255,168,92,.22) 43%, transparent 65%);
  background-size:100% 175%;background-position:50% 92%;background-repeat:no-repeat;
  transition:opacity 2.6s ease,background-position 4.2s cubic-bezier(.32,.12,.22,1)}
.stage.is-sunrise .sun{opacity:1;background-position:50% 0%}
.eyebrow,.stage__text h1,.h1promise,.lead,.trustmarks li,.dsw__hint{transition:color 1.6s ease}
.stage.is-night .eyebrow,.stage.is-night .stage__text h1,.stage.is-night .h1promise,.stage.is-night .lead,.stage.is-night .trustmarks li,.stage.is-night .dsw__hint{transition-delay:.9s}
.stage.is-night .eyebrow{color:#63d2f8}
.stage.is-night .stage__text h1{color:#eff6fd}
.stage.is-night .h1promise{color:#96acc1}
.stage.is-night .lead,.stage.is-night .trustmarks li{color:#b0c4d8}
.stage.is-night .dsw__hint{color:#8298ae}
/* Bunter Eyebrow, nur im Hero (Test 25.09.2026). Nur dieses eine Eyebrow, aus
   demselben Grund wie beim Titel-Marker: ueberall verloere es die Wirkung.
   Orange, Rot, Magenta, Violett, Blau. Kleiner Text braucht 4,5:1, auf dem
   hellen Grund geht das nur um oklch-Helligkeit 0,55. Dort bleiben Cyan und
   Petrol matt, Gelb wird oliv (erster Versuch mit den Reiterfarben aus dem
   Monitor: vorne blass, in der Mitte oliv). Orange bis Blau behaelt dort die
   volle Saettigung, jede Stufe haelt mindestens 4,6:1 gegen #f5f8fc. Gerechnet
   wird in oklch, in sRGB liefe der Verlauf durchs Grau.
   Nachts (Tasse) hellere Fassungen derselben Toene, alle ueber 5,5:1 gegen den
   Schleier. Registrierte Properties, damit die Farben mit dem uebrigen Text
   ueberblenden statt zu springen. Die Werte stehen trotzdem am Element: ohne
   @property waere var() leer und die Schrift unsichtbar.
   DER VERLAUF GLEITET MIT DEM ZEIGER (--ev-x aus hero.js, 0 bis 1). Er ist
   doppelt so breit wie die Schrift und gespiegelt, Orange bis Blau und
   zurueck. Die Schrift zeigt immer ein Fenster von halber Laenge: Zeiger
   rechts Orange bis Blau (so steht er auch ohne JS), Zeiger mittig die
   Umkehr um Blau, Zeiger links Blau bis Orange. Die Farben wandern mit dem
   Zeiger nach rechts, jede behaelt ihre Breite von rund fuenf Buchstaben.
   Vorher wanderte nur die Mitte (Magenta) und stauchte dabei eine Seite:
   bei 5 bis 95 % blieb Blau mit der Maus ueber dem Monitor auf einem
   Buchstaben stehen, bei 30 bis 70 % war die Bewegung kaum zu sehen
   (Basti: einmal uncool, einmal zu schwach). Gleiten hat das Problem nicht,
   darum darf die Bewegung gross sein.
   Verschoben wird nur, nie der Farbton gedreht: jede Stelle bleibt eine der
   geprueften Farben. Touch-Geraete und reduzierte Bewegung bekommen kein
   --ev-x und damit den Ruhezustand Orange bis Blau. */
@property --ev1{syntax:"<color>";inherits:false;initial-value:#c14b03}
@property --ev2{syntax:"<color>";inherits:false;initial-value:#df0036}
@property --ev3{syntax:"<color>";inherits:false;initial-value:#d4029b}
@property --ev4{syntax:"<color>";inherits:false;initial-value:#8300fe}
@property --ev5{syntax:"<color>";inherits:false;initial-value:#016be6}
@supports ((-webkit-background-clip:text) or (background-clip:text)){
  .eyebrow--bunt{width:fit-content;
    --ev1:#c14b03;--ev2:#df0036;--ev3:#d4029b;--ev4:#8300fe;--ev5:#016be6;
    background-image:linear-gradient(to right,var(--ev1),var(--ev2),var(--ev3),var(--ev4),var(--ev5),var(--ev4),var(--ev3),var(--ev2),var(--ev1));
    background-image:linear-gradient(to right in oklch,var(--ev1),var(--ev2),var(--ev3),var(--ev4),var(--ev5),var(--ev4),var(--ev3),var(--ev2),var(--ev1));
    /* 200 % breit: position 0 % zeigt die erste Haelfte (Orange bis Blau),
       100 % die zweite (Blau bis Orange). */
    background-size:200% 100%;background-position:calc((1 - var(--ev-x,1)) * 100%) 0;
    -webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;
    transition:--ev1 1.6s ease,--ev2 1.6s ease,--ev3 1.6s ease,--ev4 1.6s ease,--ev5 1.6s ease}
  .stage.is-night .eyebrow--bunt{--ev1:#ff7738;--ev2:#ff525e;--ev3:#ff49c0;--ev4:#a37aff;--ev5:#5c9eff}
  /* Tasse: das Eyebrow zuendet wie die Pendelleuchte, mit demselben Flackern
     (lampFlick), nur spaeter, wenn die Schrift schon hell ist. Das Leuchten
     ist eine weichgezeichnete Kopie der Schrift, jeder Buchstabe glueht in
     seiner eigenen Farbe. z-index:-1 landet im Stapel von .stage__text, also
     hinter der Schrift und vor der Szene. Der Screenreader liest die Kopie
     nicht mit (Alternativtext "").
     NUR EIN WEITER HOF, gedaempft auf 70 %. Eine zweite, enge Kopie (2px)
     lag im Test direkt hinter den Buchstaben und machte bei 12,8px ihre
     Kanten weich, dazu stand der Hof voll deckend als Farbwolke dahinter
     (Basti: unsauber). Die Schrift selbst bleibt scharf, das Licht liegt
     daneben. nowrap, damit die Kopie nie anders umbricht als die Schrift.
     Beim Sonnenaufgang geht das Leuchten mit der Lampe aus. */
  .stage.is-night .eyebrow--bunt{position:relative}
  .stage.is-night .eyebrow--bunt::before{content:attr(data-glow);content:attr(data-glow) / "";
    position:absolute;inset:0;z-index:-1;pointer-events:none;white-space:nowrap;
    background-image:inherit;background-size:inherit;background-position:inherit;
    -webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;
    filter:blur(5px) opacity(.7);animation:lampFlick 1.05s linear 1.5s both}
}
@media(forced-colors:active){
  .eyebrow--bunt{-webkit-text-fill-color:currentColor;background:none}
  .eyebrow--bunt::before{content:none}
}
.szene__nebel,.layer--wand,.szene__boden,.szene__licht{transition:opacity 1.9s ease}
.stage.is-night .szene__nebel{opacity:0}
.stage.is-night .layer--wand{opacity:.42}
.stage.is-night .szene__boden{opacity:.22}
.stage.is-night .szene__licht{opacity:0}
/* Fenster: draussen wird es dunkel */
.layer--wand rect[fill]{transition:fill 2.2s ease,fill-opacity 2.2s ease}
.stage.is-night .layer--wand rect[fill]{fill:#0b1c31;fill-opacity:.85}
/* Moebel gehorchen dem Licht: Tische/Umschalter dimmen mit */
.deviceswitch{transition:filter 2s ease}
.stage.is-night .desk{filter:brightness(.5) saturate(.7)}
.stage.is-night .deviceswitch{filter:brightness(.62)}
/* ---- Der BILDSCHIRM ist nachts die Lichtquelle ----
   Einspaltig gibt es keine Pendelleuchte mehr (sie ist ein Zweispalten-Element).
   Was einen abends anleuchtet, ist ohnehin der Monitor — erzaehlerisch passender
   fuer eine Web-Agentur als eine Lampe. Der Screen glueht und wirft Licht nach
   unten auf die Tischplatte. */
.stage.is-night .monitor{box-shadow:14px 42px 90px -24px rgba(2,9,20,.8),
  0 0 74px -6px rgba(31,155,208,.42),0 0 0 1px rgba(70,170,220,.20)}
.monitor::after{content:"";position:absolute;left:50%;top:100%;width:190%;height:clamp(90px,16vw,170px);
  transform:translateX(-50%);pointer-events:none;opacity:0;
  background:radial-gradient(58% 100% at 50% 0%,rgba(126,208,246,.34),rgba(126,208,246,.12) 46%,transparent 74%);
  transition:opacity 1.3s ease}
.stage.is-night .monitor::after{opacity:1;transition-delay:.45s}
@media(prefers-reduced-motion:reduce){.monitor::after{transition:none}}
/* Lampe schnappt AN (kurz nach der Daemmerung), statt traege zu faden */
.pendant svg{transition:stroke .45s ease}
.pendant svg path{transition:fill .45s ease}
.pendant{transition:filter .6s ease}
.stage.is-night .pendant svg{stroke:var(--cyan);transition-delay:1.05s}
.stage.is-night .pendant svg path{fill:rgba(25,191,244,.5);transition-delay:1.05s}
.stage.is-night .pendant{filter:drop-shadow(0 3px 15px rgba(25,191,244,.55));transition-delay:1.05s}
/* Einschalt-Flackern: Glow-Schicht zuckt 2x, dann steht sie */
.pendant__glow{position:absolute;left:50%;top:126px;width:132px;height:66px;transform:translateX(-50%);border-radius:50%;pointer-events:none;
  opacity:0;background:radial-gradient(closest-side,rgba(25,191,244,.55),rgba(25,191,244,.16) 62%,transparent);filter:blur(11px);transition:opacity .7s ease}
.stage.is-night .pendant__glow{animation:lampFlick 1.05s linear .82s both}
@keyframes lampFlick{0%,50%{opacity:0}56%{opacity:.75}63%{opacity:.12}72%{opacity:.9}79%{opacity:.38}88%,100%{opacity:1}}
/* Lichtkegel waechst nach unten, sobald die Lampe an ist */
.pendant::after{content:"";position:absolute;left:50%;top:168px;transform:translateX(-50%) scaleY(.5);transform-origin:top center;width:380px;height:400px;pointer-events:none;
  opacity:0;transition:opacity .9s ease,transform 1.2s cubic-bezier(.3,.7,.25,1);
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 380 400' preserveAspectRatio='none'%3E%3Cdefs%3E%3ClinearGradient id='v' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%2319bff4' stop-opacity='.34'/%3E%3Cstop offset='.5' stop-color='%2319bff4' stop-opacity='.11'/%3E%3Cstop offset='1' stop-color='%2319bff4' stop-opacity='0'/%3E%3C/linearGradient%3E%3Cfilter id='f' x='-30%25' y='-15%25' width='160%25' height='130%25'%3E%3CfeGaussianBlur stdDeviation='10'/%3E%3C/filter%3E%3C/defs%3E%3Cpolygon points='146,4 234,4 380,396 0,396' fill='url(%23v)' filter='url(%23f)'/%3E%3Cpolygon points='168,4 212,4 306,396 74,396' fill='url(%23v)' filter='url(%23f)' opacity='.6'/%3E%3C/svg%3E") center/100% 100% no-repeat}
.stage.is-night .pendant::after{opacity:1;transform:translateX(-50%) scaleY(1);transition-delay:1.15s}
.layer--buero{bottom:clamp(74px,10.5vh,124px);transform:scale(.9);transform-origin:bottom center}
.szene__boden{position:absolute;left:0;right:0;bottom:0;height:clamp(110px,15vh,170px);
  background:linear-gradient(180deg,#e9eef5,#dde4ed);border-top:1px solid #dbe3ed}
.szene__licht{position:absolute;left:27%;bottom:clamp(110px,15vh,170px);width:300px;height:46%;
  transform:skewX(-13deg);transform-origin:bottom left;
  background:linear-gradient(180deg,rgba(175,205,235,0),rgba(175,205,235,.22));filter:blur(2px)}
/* Licht-Nebel unter der Textspalte: traegt die Lesbarkeit ueber der Szene */
.szene__nebel{position:absolute;left:-6%;top:6%;width:52%;height:82%;
  background:radial-gradient(ellipse at 38% 46%,rgba(252,253,255,.94) 0%,rgba(252,253,255,.6) 46%,transparent 72%)}
.stage__text,.rig{position:relative;z-index:1}
/* Szene ist statisch — Ruhe. Nur Miniscreens dezent gedimmt. */
.miniscreen{opacity:.82}
.stage__text{max-width:470px}
.stage__text h1{margin:0 0 18px;font-size:clamp(2.1rem,3.6vw,3.2rem);line-height:1.05;letter-spacing:-.035em;font-weight:720}
/* Eyebrow dichter an der Headline (25.09.2026): 6px statt 14. Sichtbar bleiben
   rund 18px, weil unter den Versalien des Eyebrows und ueber denen der
   Headline Zeilenraum steht. Mobil steht der Wert im 700er-Block. */
.stage__text .eyebrow{margin-bottom:6px}
/* EYEBROW IN DER H1 (25.09.2026), wie auf Start, Shops, Software und
   Gestaltung: der Suchbegriff steht als <span> am Anfang der H1, die grosse
   Zeile bleibt kreativ. Der Span wird Block und behaelt die Masse des
   Absatzes, der er vorher war (Zeilenhoehe normal statt der 1,05 der H1).
   .sr-only traegt den unsichtbaren Doppelpunkt, damit ein Screenreader
   "Individuelles Webdesign: Eine Website, die ..." liest. */
.stage__text h1 > .eyebrow{display:block;line-height:normal}
.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.h1promise{display:block;font-size:.5em;font-weight:600;color:var(--ink-soft);letter-spacing:-.01em;line-height:1.15;margin-top:.42em}
/* ---- Ansicht-Umschalter: kleine Desk-Illustration (Monitor=Desktop, Handy davor=Mobil) ---- */
.deviceswitch{position:absolute;z-index:4;right:calc(100% + clamp(8px,1.6vw,30px));bottom:31px;
  display:flex;flex-direction:column;align-items:center;gap:2px}
.dsw__stage{position:relative;width:186px;height:140px}
.dsw__desk{position:absolute;left:0;bottom:0;width:100%}
.dsw__dev{position:absolute;padding:0;border:0;background:none;cursor:pointer;line-height:0;opacity:.7;transform-origin:center bottom;
  transition:opacity .18s ease,transform .18s ease,filter .2s ease;-webkit-tap-highlight-color:transparent}
.dsw__dev svg{display:block;width:100%;height:auto;overflow:visible}
.dsw__dev--desk{left:22px;bottom:53px;width:112px}
.dsw__dev--phone{left:124px;bottom:52px;width:42px}
.dsw__frame{stroke:#8397ab;stroke-width:2.4;fill:none;stroke-linejoin:round;stroke-linecap:round}
.dsw__screen{fill:rgba(25,191,244,.12)}
.dsw__dev:hover{opacity:1;transform:scale(1.02);filter:drop-shadow(0 0 7px rgba(25,191,244,.4))}
.dsw__dev:hover .dsw__frame{stroke:var(--ink)}
.dsw__dev.is-active{opacity:1}
.dsw__dev.is-active .dsw__frame{stroke:var(--cyan-ink)}
.dsw__dev.is-active .dsw__screen{fill:var(--cyan)}
/* Einmaliger Cyan-Lichtstreifen am Rahmen beim ersten Erscheinen (kein Dauer-Puls) */
.dsw__beam{fill:none;stroke:var(--cyan);stroke-width:2.8;stroke-linejoin:round;stroke-linecap:round;opacity:0;stroke-dasharray:15 85;stroke-dashoffset:15}
.deviceswitch.dsw-lit-desk .dsw__dev--desk .dsw__beam{animation:frameBeam 1.35s ease-in-out 1}
.deviceswitch.dsw-lit-phone .dsw__dev--phone .dsw__beam{animation:frameBeam 1.2s ease-in-out 1}
@keyframes frameBeam{0%{opacity:0;stroke-dashoffset:15}12%{opacity:.95}86%{opacity:.9}100%{opacity:0;stroke-dashoffset:-85}}
.dsw__hint{font-size:.78rem;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--muted);margin-bottom:-4px}
.dsw__txt{display:none}     /* Beschriftung nur mobil, wo die Tasten Text brauchen */
.hl{position:relative;white-space:nowrap}
.hl::after{content:"";position:absolute;left:-.05em;right:-.13em;bottom:.03em;height:.34em;background:var(--cyan);opacity:.34;z-index:-1;
  -webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 40' preserveAspectRatio='none'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='0'%3E%3Cstop offset='0' stop-color='%23fff' stop-opacity='0'/%3E%3Cstop offset='0.007' stop-color='%23fff' stop-opacity='0.65'/%3E%3Cstop offset='0.018' stop-color='%23fff' stop-opacity='1'/%3E%3Cstop offset='0.24' stop-color='%23fff' stop-opacity='0.94'/%3E%3Cstop offset='0.42' stop-color='%23fff' stop-opacity='1'/%3E%3Cstop offset='0.58' stop-color='%23fff' stop-opacity='0.93'/%3E%3Cstop offset='0.74' stop-color='%23fff' stop-opacity='1'/%3E%3Cstop offset='0.88' stop-color='%23fff' stop-opacity='0.97'/%3E%3Cstop offset='1' stop-color='%23fff' stop-opacity='1'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath fill='url(%23g)' d='M3 13C6 9.5 12 7.8 20 7.5 33 7 44 5.8 55 5.2 70 4.4 82 3.4 95 3 112 2.5 126 4 140 4.4 156 4.8 170 3 185 2.6 202 2.2 216 3.6 230 4 244 4.4 256 2.9 268 2.6 280 3.4 290 5.4 297.4 8.2C299.4 11.6 298.4 16.4 299.5 20.8C299.9 24.4 298.6 28.6 296.8 31.6C288 34.6 276 36.4 264 37 242 38 218 36.3 194 37.1 170 37.9 148 36.2 124 37 100 37.8 78 36.3 56 35.7 42 35.2 30 33.2 20 32 12 31 6 29.5 3 27Z'/%3E%3C/svg%3E");
  mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 40' preserveAspectRatio='none'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='0'%3E%3Cstop offset='0' stop-color='%23fff' stop-opacity='0'/%3E%3Cstop offset='0.007' stop-color='%23fff' stop-opacity='0.65'/%3E%3Cstop offset='0.018' stop-color='%23fff' stop-opacity='1'/%3E%3Cstop offset='0.24' stop-color='%23fff' stop-opacity='0.94'/%3E%3Cstop offset='0.42' stop-color='%23fff' stop-opacity='1'/%3E%3Cstop offset='0.58' stop-color='%23fff' stop-opacity='0.93'/%3E%3Cstop offset='0.74' stop-color='%23fff' stop-opacity='1'/%3E%3Cstop offset='0.88' stop-color='%23fff' stop-opacity='0.97'/%3E%3Cstop offset='1' stop-color='%23fff' stop-opacity='1'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath fill='url(%23g)' d='M3 13C6 9.5 12 7.8 20 7.5 33 7 44 5.8 55 5.2 70 4.4 82 3.4 95 3 112 2.5 126 4 140 4.4 156 4.8 170 3 185 2.6 202 2.2 216 3.6 230 4 244 4.4 256 2.9 268 2.6 280 3.4 290 5.4 297.4 8.2C299.4 11.6 298.4 16.4 299.5 20.8C299.9 24.4 298.6 28.6 296.8 31.6C288 34.6 276 36.4 264 37 242 38 218 36.3 194 37.1 170 37.9 148 36.2 124 37 100 37.8 78 36.3 56 35.7 42 35.2 30 33.2 20 32 12 31 6 29.5 3 27Z'/%3E%3C/svg%3E");
  -webkit-mask-size:100% 100%;mask-size:100% 100%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}
/* Marker faengt das Lampenlicht: glueht, sobald die Leuchte an ist.
   Die 1,1s-Transition steht JETZT HIER statt in der Grundregel. Dort galt sie
   fuer jede Aenderung von opacity — also auch fuer die des Zeiger-Glows, den
   hero.js Bild fuer Bild schreibt. Jede dieser Schreibungen wurde damit ueber
   1,1 Sekunden gezogen, oben auf die Daempfung im JS. Das war die Ursache
   dafuer, dass das Leuchten dem Zeiger hinterherlief.
   Das Nachtlicht braucht die Transition, der Glow darf sie nicht sehen. */
.stage.is-night .hl::after{opacity:.88;
  transition:opacity 1.1s ease,filter 1.1s ease;transition-delay:1.15s;
  filter:drop-shadow(0 0 9px rgba(25,191,244,.65)) drop-shadow(0 0 24px rgba(25,191,244,.38))}
.lead{margin:0 0 24px;max-width:40ch;font-size:clamp(1.05rem,1.35vw,1.22rem);line-height:1.55;color:var(--ink-soft)}
/* margin-top ist PFLICHT, seit die Trust-Zusagen VOR dem Button stehen: die
   Liste hat nur oben Abstand (margin:22px 0 0), vorher lag sie hinter dem
   Button und das ging auf. Ohne das klebt der Button am letzten Bullet. */
.cta-row{display:flex;align-items:center;gap:22px;flex-wrap:wrap;margin-top:28px}
/* ---- Der Haken ist GEZEICHNET, nicht bebildert -------------------------
   Vorher stand hier ein gefuellter Kreis mit weissem Haken darin. Das ist die
   Standardform, die jede zusammengeklickte Seite benutzt — und genau deshalb
   liest sie sich wie von der Stange, egal wie gut sie sitzt.
   Diese Seite markiert sonst mit 2px-Haarlinien (Kartenoberkanten, Taktlinie,
   Schienenmarke) und mit einem gekippten Quadrat (Referenzpunkte). Ein Haken in
   derselben Strichstaerke gehoert in diese Reihe, ein Abzeichen nicht: der
   Kreis war das einzige Element auf der Seite, das seine Bedeutung in eine
   gefuellte Flaeche gepackt hat.
   STUMPFE ENDEN, SPITZE ECKE (kein stroke-linecap/linejoin) — runde Enden sind
   der zweite Teil des Von-der-Stange-Eindrucks. Die Strichstaerke 2,3 liegt
   zwischen den 2px der Kartenkanten und den 2,4px des FAQ-Winkels.
   Als MASKE statt als Hintergrundbild: damit steht die Farbe in CSS und nicht
   in einer Datei-URL, der Nachtmodus kann sie mitziehen, und dieselbe Form
   dient beiden Listen in zwei Groessen. */
.trustmarks,.zusage__punkte{
  --haken:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2 8.4 6.3 12.7 14 3.6' fill='none' stroke='%23000' stroke-width='2.3'/%3E%3C/svg%3E")}
.trustmarks{display:flex;flex-direction:column;gap:9px;margin:22px 0 0;padding:0;list-style:none}
.trustmarks li{position:relative;padding-left:22px;
  font-size:.9rem;font-weight:560;color:var(--ink-soft);white-space:nowrap;
  transition:color 1.6s ease}
.trustmarks li::before{content:"";position:absolute;left:0;top:.14em;
  width:14px;height:14px;background:var(--cyan);
  -webkit-mask:var(--haken) 0 0/14px 14px no-repeat;
          mask:var(--haken) 0 0/14px 14px no-repeat}

/* ================= Rig (Monitor + Desk) ================= */
.rig{position:relative;justify-self:end;width:min(100%,50vw,1060px)}
.monitor{position:relative;z-index:2;width:100%;margin:0 auto;border-radius:14px;background:var(--bezel);padding:9px 9px 0;
  box-shadow:14px 42px 70px -28px rgba(13,24,38,.32),0 0 0 1px rgba(13,24,38,.08),inset 0 1px 0 rgba(255,255,255,.06)}
.monitor__chin{display:flex;align-items:center;justify-content:center;height:26px}
.led{width:5px;height:5px;border-radius:50%;background:#22374f;transition:background .8s ease,box-shadow .8s ease}
.is-on .led{background:var(--cyan);box-shadow:0 0 8px rgba(25,191,244,.9)}
.monitor__hals{width:30px;height:52px;margin:0 auto;background:linear-gradient(180deg,#1a2a3d,#0e1a2a);border-radius:0 0 6px 6px}
.monitor__base{width:180px;height:11px;margin:-2px auto 0;border-radius:7px;background:linear-gradient(180deg,#22354b,#101f31);
  box-shadow:0 12px 18px -8px rgba(13,24,38,.30)}
/* ---- Vordergrund: Holztisch + Props ---- */
.desk{position:relative;z-index:1;margin-top:-7px}
.desk__top{position:relative;height:17px;border-radius:9px;background:linear-gradient(180deg,#eef2f8,#dfe6ef);
  box-shadow:0 1px 0 #d0dbe7,0 18px 28px -14px rgba(13,24,38,.20)}
.desk__legs{display:flex;justify-content:space-between;padding:0 11%}
.desk__legs i{width:12px;height:66px;border-radius:0 0 5px 5px;background:linear-gradient(180deg,#cfd9e4,#bfccda)}
.desk__teppich{position:absolute;left:-9%;right:-9%;bottom:-10px;height:26px;border-radius:50%;
  background:radial-gradient(ellipse at center,#e6ebf2 0%,#dde4ed 62%,transparent 74%)}
.prop{position:absolute;z-index:3;bottom:100%;pointer-events:none}
/* Tasse: eigenes Rig-Kind auf z3 (ueber dem Monitor, z2) — sonst verschwindet der Dampf dahinter */
/* EIN HAUCH CYAN AUCH IN RUHE (26.09.2026, Basti: "minimal auffaelliger").
   Ohne Bewegung war die Tasse zwischen zwei Winken ein weisses Ding am Rand.
   Der Hof ist dasselbe Cyan wie ihr Streifen und wie der Schein im Wink,
   also kein neues Signal, nur ein leiseres. Die Funktionsliste steht mit
   Absicht dreiteilig und in derselben Reihenfolge wie die Nachtregel: nur
   gleich gebaute filter-Listen blendet der Browser weich ineinander, sonst
   springt er nach der halben Zeit um. */
.prop--mug{right:9%;bottom:80px;width:60px;pointer-events:auto;cursor:pointer;
  filter:drop-shadow(0 0 5px rgba(25,191,244,.3)) brightness(1) saturate(1);
  transition:transform .18s ease,opacity .4s ease,filter 2s ease}
.prop--mug:hover{transform:translateY(-2px) scale(1.05)}
.stage.is-night .prop--mug{filter:drop-shadow(0 0 5px rgba(25,191,244,0)) brightness(.6) saturate(.75)}
.is-phone .prop--mug,.rig.is-morphing .prop--mug{opacity:0;pointer-events:none}
.prop--notiz{right:14%;width:92px;margin-bottom:-5px}
.prop--sukku{right:4%;width:46px;margin-bottom:-2px}
/* Dampf: steigt nur waehrend des Feierabend-Moments aus der Tasse (Klick) */
.dampf{opacity:0}
.stage.is-brewing .dampf{animation:dampfZug 2.4s ease-in-out infinite}
.stage.is-brewing .dampf--b{animation-duration:2.9s;animation-delay:.7s}
@keyframes dampfZug{0%{opacity:0;transform:translateY(5px)}30%{opacity:.85}70%{opacity:.3}100%{opacity:0;transform:translateY(-8px)}}

/* ---- Einmaliger Anstoss der Tasse (nur mobil) --------------------------
   Warum ueberhaupt: auf dem Handy gibt es keinen Cursor und kein Hover, die
   Tasse ist 44px gross und sitzt am Rand — es existiert schlicht kein Hinweis,
   dass sie etwas kann. Also meldet sie sich einmal selbst.
   EINMAL, nicht in Schleife: eine wiederkehrende Bewegung im Blickfeld liest
   sich als Werbebanner. Zwei kurze Hopser statt eines langen — ein einzelner
   Ruck sieht aus wie ein Ruckler, zwei lesen sich als Absicht (dasselbe
   Muster wie ein Kopfnicken).
   Der Schein ist Cyan, weil die Tasse selbst schon einen Cyan-Streifen traegt
   und Cyan auf dieser Seite „hier ist etwas" bedeutet.
   transform-origin unten: die Tasse kippt aus ihrem Stand, nicht aus der Mitte,
   sonst schwebt sie. */
/* Gilt auf JEDER Breite. Der Wink war zuerst nur mobil, weil dort Cursor und
   Hover fehlen — auf dem Desktop fuehrt er trotzdem zum selben Ziel: die Tasse
   ist auch dort nur eine Tasse, bis sie sich einmal meldet.
   transform-origin unten, damit sie aus ihrem Stand kippt und nicht schwebt.
   Waehrend der Nacht nie: dort hat die Szene ohnehin die Aufmerksamkeit, und
   die Nachtregel setzt einen eigenen filter, mit dem die Animation streiten
   wuerde. */
.prop--mug{transform-origin:50% 88%}
.prop--mug.is-wink{animation:tasseWink 1.55s cubic-bezier(.3,.72,.35,1) both}
.stage.is-night .prop--mug.is-wink{animation:none}

/* Anfang und Ende = der Hof in Ruhe (s. .prop--mug), in derselben
   dreiteiligen Liste. Stuenden hier 0 statt .3, blitzte der Hof bei jedem
   Wink einmal aus und am Ende wieder an. Die Spitzen seit 26.09.2026 eine
   Stufe kraeftiger (.55/.42 auf .7/.55), passend zum Hof darunter. */
@keyframes tasseWink{
  0%   {transform:translateY(0) rotate(0deg);   filter:drop-shadow(0 0 5px rgba(25,191,244,.3)) brightness(1) saturate(1)}
  12%  {transform:translateY(-7px) rotate(-5deg);filter:drop-shadow(0 3px 12px rgba(25,191,244,.7)) brightness(1) saturate(1)}
  27%  {transform:translateY(0) rotate(0deg)}
  40%  {transform:translateY(-4px) rotate(3.5deg);filter:drop-shadow(0 3px 13px rgba(25,191,244,.55)) brightness(1) saturate(1)}
  55%  {transform:translateY(0) rotate(0deg)}
  100% {transform:translateY(0) rotate(0deg);   filter:drop-shadow(0 0 5px rgba(25,191,244,.3)) brightness(1) saturate(1)}
}

/* ================= Browser im Screen ================= */
.browser{border-radius:10px;overflow:hidden;background:var(--chrome);
  opacity:0;filter:brightness(.35) saturate(.6);transition:opacity .9s ease,filter 1.1s ease}
.is-on .browser{opacity:1;filter:none}
.tabsbar{display:flex;align-items:flex-end;gap:4px;height:38px;padding:7px 10px 0;background:var(--chrome2)}
/* POLSTER 7px, NICHT 10px (12.09.2026).
   Der Eintrag vom 07.09. stand hier zuletzt so: Polster von 13 auf 10, sieben
   Reiter brauchten 728px bei 702px Leiste, und der Schlusssatz sagte voraus,
   was bei einem ACHTEN Reiter passiert — "der gap gaebe noch 6px her, danach
   ist Schluss und es muss ein Name kuerzer oder einer raus".
   Genau das ist mit GongZheng eingetreten, und zwar deutlicher als vermutet:
   acht Reiter brauchen 776px. Es fehlten nicht 6px, sondern 74.
   Gemessen bei 1440px Fenster, alle Werte mit flex-shrink:0:
     776  Ausgangslage, acht Reiter, Polster 10 / gap 5
     731  nur "Friseur IBO" -> "IBO"          — reicht nicht
     714  nur Polster 7 / gap 4               — reicht nicht
     676  beides zusammen                     — passt, 26px Luft
   Also beides. "IBO" ist der echte Salonname; der Chip unter dem Geraet und
   data-name am Reiter tragen weiter "Friseursalon IBO", verloren geht also
   Beschriftung, keine Information. Und "+ Dein Projekt" bleibt wie seit 06e
   vom Schrumpfen ausgenommen.
   Wer einen NEUNTEN Reiter dazustellt: die 26px reichen dafuer nicht, und
   Polster und gap sind jetzt ausgereizt. Dann muss einer raus. Kegema waere
   der erste Kandidat — es ist laut Reihenfolge-Kommentar in websites.html
   im Briefmarkenformat ohnehin der schwaechste.

   DIE 26px LUFT BLEIBEN NICHT STEHEN (12.09.2026, Nachtrag).
   Mit flex:0 1 auto sassen sie als Loch zwischen "Kegema" und
   "+ Dein Projekt" — die Leiste sah danach ausgeraeumt aus, wo sie vorher
   randvoll war, und das war der sichtbare Verlust gegenueber sieben Reitern.
   flex:1 1 auto verteilt den Rest auf die Kundenreiter, rund 3,7px je Reiter.
   Das ist zugleich das Verhalten, das man von einer Browserleiste erwartet:
   Tabs fuellen den Streifen. Die Einladung bleibt davon unberuehrt, sie hat
   ihr eigenes flex:0 0 auto weiter unten.
   justify-content:center, weil ein gewachsener Reiter seinen Text sonst
   links kleben laesst und der Punkt davor aus der Reihe faellt.
   max-width:198px deckelt das Wachsen weiterhin.

   UND DIE EINLADUNG WAECHST MIT (12.09.2026, Nachtrag zum Nachtrag).
   Sie stand als einzige mit flex:0 0 auto daneben, waehrend die sieben
   Kundenreiter sich den Rest teilten — ein Reiter, der bei allem zusieht.
   Jetzt flex:1 0 auto: waechst mit, schrumpft aber weiterhin NICHT. Die
   Null in der Mitte ist die ganze Zusage vom 06.09. — "+ Dein Proje…" darf
   es nicht wieder geben, ausgerechnet am Reiter, der etwas will.
   margin-left:auto bleibt als Sicherheitsnetz stehen: sobald die Reiter
   den Streifen fuellen, ist kein freier Platz mehr da, den es aufsaugen
   koennte, und es tut nichts. Wird die Leiste eines Tages breiter als
   acht gedeckelte Reiter, klebt die Einladung wieder rechts statt in der
   Mitte zu schweben. */
.btab{display:inline-flex;align-items:center;justify-content:center;gap:8px;flex:1 1 auto;max-width:198px;height:31px;padding:0 7px;border:0;cursor:pointer;
  border-radius:8px 8px 0 0;background:transparent;color:#8ea1b5;font:560 .8rem/1 Geist,sans-serif;letter-spacing:.005em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:background .15s,color .15s;-webkit-tap-highlight-color:transparent}
.btab:hover{background:#16243a66;color:#c4d2e0}
.btab[aria-selected="true"]{background:var(--chrome);color:#eaf2fa}
.btab i{width:7px;height:7px;border-radius:50%;flex:0 0 auto;background:var(--tabdot,#6b7d8f)}
/* KEIN "+" MEHR, EIN PUNKT WIE BEI DEN ANDEREN (26.09.2026). Mit dem Plus
   las sich "Dein Projekt" als Knopf "neuer Tab" neben der Leiste, nicht als
   Reiter darin (Basti: "nicht so abgetrennt, sondern wie ein eigener Tab").
   Der Punkt ist Cyan (--tabdot am Reiter), die Farbe, die auf dieser Seite
   "hier ist etwas" heisst. Vom Schrumpfen bleibt er trotzdem ausgenommen,
   s. unten. */
/* DIE EINLADUNG SCHRUMPFT NICHT MIT (06.09.2026).
   "Dein Projekt" ist kein Kunde, sondern die Aufforderung — und als einzige
   darf sie nie abgeschnitten werden. Mit sechs Reitern wurde daraus
   "+ Dein Proje…", also ausgerechnet der Reiter, der etwas will, war der
   unleserlichste.
   margin-left:auto klebt sie an die rechte Kante, flex:0 0 auto nimmt sie aus
   dem Schrumpfen heraus, max-width:none und overflow:visible nehmen ihr die
   Ellipse. z-index legt sie vor die Kundenreiter: wird es je so eng, dass die
   Flex-Zeile ueberlaeuft, wandern die Namen unter sie statt sie hinauszudraengen.
   Der eigene Grund ist dafuer noetig — ohne ihn schiene der Nachbar durch. */
.btab--neu{margin-left:auto;flex:1 0 auto;max-width:none;overflow:visible;
  position:relative;z-index:2;background:var(--chrome2)}
.btab--neu[aria-selected="true"]{background:var(--chrome)}

/* Laufendes Projekt: das Tab-Puenktchen pulst, statt still zu stehen — ein
   zweites Signal neben dem Schild in der Vorschau, damit der Tab auch auffaellt,
   waehrend ein anderer offen ist. */
/* Der Punkt selbst bleibt stehen — bewegt wird nur ein weicher Ring um ihn
   herum. Vorher liefen Deckkraft UND Skalierung: einen 7px-Punkt zu skalieren
   heisst, ihn ueber wenige Pixel zu rastern, und jeder Zwischenschritt rundet
   sichtbar anders — das flackert, statt zu atmen. Ein Ring aus box-shadow hat
   keine harte Kante, die rastern koennte, und laeuft deshalb glatt. Dazu
   langsamer (3.6s statt 2.4s) und mit flacherer Kurve. */
/* DER RING NIMMT DIE PUNKTFARBE (07.09.2026).
   Hier stand fest Visios Blau. Seit GWS pulst deshalb ein blauer Ring um einen
   gelben Punkt — 06e hat das am Schild unter dem Geraet geradegezogen und den
   Reiter uebersehen. Mit Schmidts Champagner-Gold waere es der dritte Fall.
   --wipring reicht die Farbe an die Keyframes durch, color-mix macht daraus
   halbe Deckkraft am Anfang und volle Transparenz am Ende. Direkt nach
   'transparent' zu interpolieren waere rgba(0,0,0,0) und liefe bei hellen
   Punkten sichtbar durchs Graue.
   Der Fallback bleibt das alte Blau: wer --wipring nicht setzt — die Chips —
   sieht keine Aenderung. */
.btab--wip i{--wipring:var(--tabdot,#1D9ADB);
  animation:wipAtem 3.6s cubic-bezier(.37,0,.63,1) infinite}
@keyframes wipAtem{
  0%,100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--wipring,#1D9ADB) 50%,transparent);opacity:1}
  55%    {box-shadow:0 0 0 4px color-mix(in srgb,var(--wipring,#1D9ADB) 0%,transparent);opacity:.78}
}

/* Schild UNTER dem Geraet, nicht darueber: eine Bildunterschrift verdeckt nichts
   und braucht keinen pointer-events-Trick. Erscheint nur, wenn der betreffende
   Tab offen ist (.rig.is-wip), und nur auf Desktop — dort zeigt das Mockup die
   Desktop-Ansicht, also genau das, was hier noch fehlt. Auf Mobil sieht der
   Besucher ohnehin die fertige Ansicht, da waere der Hinweis irrefuehrend. */
.wipnote{position:absolute;left:0;display:none;margin:13px 0 0;align-items:center;gap:8px;
  padding:6px 12px 6px 10px;border-radius:999px;background:#ffffffcc;border:1px solid #d7e0ea;
  color:#42586e;font:600 .78rem/1.3 Geist,sans-serif;letter-spacing:.01em;white-space:nowrap;
  -webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}
.wipnote span{color:#8ba0b5;font-weight:400}
/* Punktfarbe folgt dem gezeigten Projekt (hero.js setzt --wip-punkt vom
   Reiter). Fest verdrahtet war hier Visios Blau — mit GWS in Gelb stuende
   sonst ein blauer Punkt neben einem gelben Auftritt. */
.wipnote i{width:6px;height:6px;border-radius:50%;flex:0 0 auto;background:var(--wip-punkt,#1D9ADB);
  --wipring:var(--wip-punkt,#1D9ADB);
  animation:wipAtem 3.6s cubic-bezier(.37,0,.63,1) infinite}

@media (min-width:1001px){
  .rig.is-wip .wipnote{display:flex}
}

.chip--wip::before{content:"";display:inline-block;width:6px;height:6px;margin-right:6px;
  border-radius:50%;background:#1D9ADB;vertical-align:middle;
  animation:wipAtem 3.6s cubic-bezier(.37,0,.63,1) infinite}

@media (prefers-reduced-motion:reduce){
  .btab--wip i,.wipnote i,.chip--wip::before{animation:none}
}
.urlbar{display:flex;align-items:center;gap:10px;height:38px;padding:0 12px;background:var(--chrome)}
.urlfield{display:flex;align-items:center;gap:8px;flex:1;height:26px;min-width:0;padding:0 12px;border-radius:13px;background:#0a1322;
  color:#9fb2c6;font:400 .76rem/1 "Geist Mono",monospace;overflow:hidden;white-space:nowrap}
.urlfield svg{width:11px;height:11px;flex:0 0 auto;color:#5d7590}
.urllive{flex:0 0 auto;color:var(--cyan);font:600 .76rem/1 Geist,sans-serif;text-decoration:none;opacity:.85}
.urllive:hover{opacity:1}

/* View + Pages */
.view{position:relative;aspect-ratio:16/9;background:#0a1322;overflow:hidden;touch-action:pan-y}
.track{display:flex;height:100%;transition:transform .55s cubic-bezier(.3,.86,.28,1)}
.page{position:relative;flex:0 0 100%;height:100%}
/* STANDBILD UNTER DEM RAHMEN.
   Der leere iframe ist weiss (background:#fff in der Zeile darunter). Beim
   Laden von websites.html stand deshalb einen Moment ein weisses Rechteck im
   Monitor, am laengsten bei Erlkoenig - das ist die erste Seite und die
   schwerste. Unter dem Rahmen liegt jetzt ein Standbild derselben Seite, der
   Rahmen blendet darueber, sobald er geladen ist (hero.js setzt .is-da).
   1280 x 720 ist kein Zufall: hero.js rendert den iframe in fester Breite
   (RENDER_W_DESK = 1280) und skaliert ihn auf die Monitorbreite, der Screen
   haelt 16:9. Sichtbar ist damit genau dieser Ausschnitt der Kundenseite -
   Standbild und echte Seite sitzen deckungsgleich, der Wechsel springt nicht.
   Ohne JavaScript bleibt das Standbild stehen. Das ist die bessere Anzeige
   als ein leerer Rahmen.
   Im Handy-Morph rendert hero.js mit 402px, da passt ein 1280er Bild nicht.
   Dort braucht es auch keins: unter 700px blendet .rig.is-leer die Spur
   waehrend des Ladens ohnehin aus. */
.page__standbild{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;object-position:top left;display:block}
.is-phone .page__standbild{display:none}
.page__frame{position:absolute;top:0;left:0;border:0;display:block;background:#fff;transform-origin:top left;width:100%;height:100%;
  opacity:0;transition:opacity .3s ease}
.page__frame.is-da{opacity:1}
@media(prefers-reduced-motion:reduce){.page__frame{transition:none}}
.page__scroll{height:100%;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:none}
.page__scroll::-webkit-scrollbar{width:0;height:0;display:none}
.page__scroll img{width:100%;height:auto}
.page__fade{position:absolute;left:0;right:0;bottom:0;height:70px;pointer-events:none;
  background:linear-gradient(180deg,transparent,rgba(10,19,34,.82));display:flex;align-items:flex-end;justify-content:center;padding-bottom:12px;
  opacity:0;transition:opacity .3s}
.is-live .page.is-active .page__fade{opacity:1}
.page__fade a{color:#bfe9fb;font:600 .8rem/1 Geist,sans-serif;text-decoration:none;pointer-events:auto}
.page__fade a:hover{color:#fff}

/* Hint-Overlay (Klick aktiviert Scroll) */
.scrollcue{position:absolute;top:12px;bottom:12px;right:7px;width:4px;border-radius:3px;background:rgba(255,255,255,.1);z-index:3;opacity:0;transition:opacity .45s ease .35s;pointer-events:none}
.is-on .scrollcue{opacity:1}
.scrollcue__thumb{position:absolute;left:0;right:0;top:0;height:26%;border-radius:3px;background:rgba(127,208,242,.55);transition:top .12s linear,height .12s linear}
.hintcover,.scrollhint{position:absolute;inset:0;z-index:4;display:flex;align-items:flex-end;justify-content:center;
  padding-bottom:22px;border:0;background:linear-gradient(180deg,transparent 55%,rgba(10,19,34,.42));cursor:pointer;
  opacity:1;transition:opacity .3s;-webkit-tap-highlight-color:transparent}
.hintcover span,.scrollhint span{display:inline-flex;align-items:center;gap:9px;height:38px;padding:0 18px;border-radius:19px;
  background:rgba(10,19,34,.86);color:#eaf2fa;font:600 .85rem/1 Geist,sans-serif;
  box-shadow:0 6px 22px rgba(2,9,20,.4),inset 0 0 0 1px rgba(25,191,244,.35)}
.hintcover span::before,.scrollhint span::before{content:"";width:7px;height:11px;border-radius:4px;border:1.5px solid var(--cyan);border-bottom-width:4px}
.is-neu .hintcover,.rig.is-interactive .hintcover{opacity:0;pointer-events:none}
/* AUS DER SPERRE WIRD EIN HINWEIS. Nach dem Ausklappen ist die Vorschau sofort
   bedienbar — die Flaeche haelt niemanden mehr auf. Sichtbar bleibt sie
   trotzdem ein paar Sekunden, weil sie das Einzige ist, was sagt: hier darfst
   du selbst scrollen. Ohne sie waere die Bedienbarkeit da, aber unsichtbar.
   pointer-events:none ist der ganze Unterschied zwischen Hinweis und Sperre:
   ein Finger darauf landet jetzt im Rahmen dahinter, nicht auf dem Knopf. */
.rig.is-interactive.zeigt-hinweis .hintcover{opacity:1;pointer-events:none;
  transition:opacity .5s ease}
/* Maus-Geraete: passiver Hinweis direkt im Bildschirm. Er laesst Maus und
   Scrollrad durch und verschwindet nach der ersten Bedienung der Vorschau. */
.scrollhint{display:none;opacity:0;pointer-events:none;cursor:default}
@media(hover:hover){
  .hintcover{display:none}
  .scrollhint{display:flex}
  .rig.is-on:not(.is-neu):not(.is-leer):not(.hat-vorschau-bedient) .scrollhint{opacity:1}
}
@media(prefers-reduced-motion:reduce){.scrollhint{transition:none}}

/* =====================================================================
   AUSKLAPPEN (nur mobil) — das Handy startet eingerollt: sichtbar sind
   Tableiste, Adressleiste und der Griff. Ein Tipp fahrt den Bildschirm
   nach unten aus, der Viewport geht mit.
   WOZU: das Geraet ist ausgefahren rund 500px hoch. Eingerollt kostet es
   85px, und der Besucher sieht trotzdem sofort, WAS drin ist -- in der
   Adressleiste steht die echte Kundendomain, in der Tableiste stehen die
   Kundennamen. Der Beweis ist also da, bevor man tippt.
   Alle Regeln haengen an .rv-on (setzt das Inline-Skript im <head>):
   ohne JavaScript gibt es kein Ausklappen, dann muss das Handy offen sein.
   ===================================================================== */
.unfold{display:none}
@media(max-width:700px){
  .rv-on .rig.is-phone .unfold{display:flex;align-items:center;justify-content:center;gap:8px;
    width:100%;height:46px;padding:8px 0 0;border:0;background:var(--chrome);
    color:#dceaf5;font:620 .87rem/1 Geist,sans-serif;letter-spacing:.005em;
    cursor:pointer;-webkit-tap-highlight-color:transparent;position:relative}
  /* Greiferstrich: sagt „hier" ohne ein weiteres Wort und trennt den Griff
     optisch von der Adressleiste, die dieselbe Farbe hat. */
  .rv-on .rig.is-phone .unfold::before{content:"";position:absolute;top:5px;left:50%;
    transform:translateX(-50%);width:36px;height:3px;border-radius:2px;background:rgba(255,255,255,.16)}
  .rv-on .rig.is-phone .unfold:active{background:#1b2637}
  .rv-on .rig.is-phone .unfold__pfeil{width:17px;height:17px;color:var(--cyan);flex:0 0 auto;
    transition:transform .5s cubic-bezier(.22,.61,.36,1)}
  /* Ausgefahren zeigt der Pfeil nach oben — dieselbe Taste klappt wieder zu. */
  .rv-on .rig.is-phone:not(.is-folded) .unfold__pfeil{transform:rotate(180deg)}

  /* Die Hoehe setzt das JS in Pixeln (0 bzw. Breite * 15.5/9). Ein Wechsel
     zwischen aspect-ratio-Hoehe und fixer Hoehe laesst sich NICHT animieren --
     transition:height braucht auf beiden Seiten einen Pixelwert.

     DAUER UND KURVE STEHEN NUR HIER. Das JS liest --fahrt aus, um die
     Viewport-Fahrt und das Nachladen darauf zu synchronisieren; zwei getrennte
     Werte wuerden auseinanderlaufen.
     Die Kurve ist genau easeOutCubic — das JS rechnet fuer die Viewport-Fahrt
     1-(1-p)^3, also dieselbe Kurve. Sonst sind es zwei Bewegungen statt einer. */
  .rv-on .rig.is-phone{--fahrt:760ms}
  .rv-on .rig.is-phone .view{transition:height var(--fahrt) cubic-bezier(.215,.61,.355,1)}
  .rv-on .rig.is-phone.is-folded .view{aspect-ratio:auto}
  /* Bis die Kundenseite wirklich steht, bleibt der Bildschirm ruhig dunkel und
     blendet dann auf. Ohne das poppt der Inhalt hart herein, sobald das iframe
     fertig ist — auf einem langsamen Geraet mitten in die Bewegung. */
  .rv-on .rig.is-phone .track{transition:opacity .34s ease}
  /* .is-leer gilt in BEIDE Richtungen (25.09.2026): seit der Wechsel Handy <->
     Desktop gemorpht wird, leert setPhone den Bildschirm auch auf dem Weg zum
     Desktop. Mit .is-phone im Selektor stand dort die „Selbst scrollen"-Pille
     auf dem leeren Bildschirm. */
  .rv-on .rig.is-leer .track{opacity:0}
  .rv-on .rig.is-leer .hintcover{opacity:0;pointer-events:none}
  .rv-on .rig.is-folded .scrollcue{opacity:0}
  /* Eingerollt ist „Live ansehen" verfrueht — man hat die Seite noch nicht gesehen. */
  .rv-on .rig.is-folded .urllive{opacity:0;pointer-events:none}
  .rv-on .rig.is-folded .urlbar{transition:none}

  /* Die Szene faehrt mit auf: eingerollt ist das Geraet 98px hoch, die
     Fensterflaeche aber 420px — sie stand als leerer Rahmen hinter dem Text.
     Und die Tasse sass auf der Geraeteecke, also teilweise auf dem Tippziel.
     Beides kommt zurueck, sobald ausgefahren ist: der Raum oeffnet sich
     zusammen mit dem Handy. Das Easter Egg ist damit erst nach dem Ausfahren
     erreichbar — vorher gibt es ohnehin nichts zu sehen. */
  .rv-on .stage.is-eingerollt .layer--wand{opacity:0}
  .rv-on .stage.is-eingerollt .prop--mug{opacity:0;pointer-events:none}
  .rv-on .stage .layer--wand{transition:opacity .45s ease}
}
@media(prefers-reduced-motion:reduce){
  /* Selektoren MUESSEN so spezifisch sein wie die Regeln oben (.rv-on .rig
     .is-phone .view = vier Klassen). Mit .rig.is-phone .view hat es verloren und
     das Handy fuhr trotz reduce animiert aus — nachgemessen: nach 260ms erst
     381 von 465px. Media Queries erhoehen die Spezifitaet nicht. */
  .rv-on .rig.is-phone .unfold__pfeil,
  .rv-on .rig.is-phone .track,
  .rv-on .rig.is-phone .view{transition:none}
}
/* Touch: Vorschau erst nach Antippen bedienbar. Vorher faengt das iframe den
   vertikalen Wisch ab und der Nutzer kommt auf der Seite nicht weiter. */
@media(hover:none){
  .rig:not(.is-interactive) .page__frame{pointer-events:none}
}

/* "Dein Projekt"-Tab */
.neu{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;height:100%;text-align:center;
  background:linear-gradient(180deg,#f8fbfe,#eef4fa);color:var(--ink);padding:24px}
/* h2, nicht h3: die Gliederung sprang sonst von der h1 der Seite direkt auf
   Stufe 3 (Lighthouse: "Ueberschriften nicht fortlaufend"). Die Zeile IST die
   zweite Ebene — sie steht im Hero und gehoert unter die h1. */
.neu h2{margin:0;font-size:clamp(1.3rem,2.6vw,1.9rem);letter-spacing:-.02em;font-weight:710}
.caret{display:inline-block;width:2px;height:1em;margin-left:3px;background:var(--cyan-ink);vertical-align:-.12em;animation:blink 1.05s steps(1) infinite}
@keyframes blink{50%{opacity:0}}
.neu p{margin:0;max-width:34ch;color:var(--ink-soft);font-size:.95rem;line-height:1.5}
.neu .web-btn{height:46px;padding:0 22px;font-size:.92rem}
/* ---- Feierabend im dritten Tab: hier entsteht gerade Code ----------------
   Warum hier und nicht ueber der Live-Vorschau: die Vorschau IST das Argument
   der Seite. Dieser Tab ist leer, hier kostet der Effekt nichts.

   AUFBAU: Einladung und Editor liegen UEBEREINANDER in einem Grid-Feld, der
   Knopf darunter. Die Buehne ist immer so hoch wie ihre hoechste Ebene, also
   aendert sich beim Wechsel keine Hoehe -- vorher fiel die Einladung am Ende
   ihrer Blende aus dem Layout und der Knopf sprang um 60px.

   ALLE Transitions stehen in den BASIS-Regeln, nicht in den .is-night-Regeln.
   Sonst gelten sie nur auf dem Hinweg, und der Rueckweg zum Tag schnappt. */
.neu{position:relative;transition:color .9s ease}
/* Die Nacht als eigene Ebene DARUEBER, nur ein- und ausgeblendet. Von einem
   Gradient auf einen anderen animiert der Browser nicht zuverlaessig. Absolut
   positioniert, also kein Flex-Item -- das Layout bleibt unberuehrt. */
.neu::before{content:"";position:absolute;inset:0;pointer-events:none;opacity:0;
  background:linear-gradient(180deg,#0b1727,#050d19);
  /* HIN und ZURUECK unterschiedlich lang: die Basisregel gilt auf dem Rueckweg
     zum Tag, die .is-night-Regel auf dem Hinweg. Das Aufhellen darf laenger
     dauern — es ist die Aufloesung, nicht der Auftritt. */
  transition:opacity .9s ease}
.stage.is-night .neu::before{opacity:1;transition:opacity .55s ease}
.stage.is-night .neu{color:#dceaf5;transition:color .55s ease}
.neu > *{position:relative;z-index:1}

/* Die Buehne traegt die Monoschrift, damit sich 34ch genau EINMAL aufloest.
   Vorher hatten Dateiname (11,5px) und Code (12,5px) je eigene ch-Breiten,
   waren beide zentriert und standen dadurch 56px versetzt. */
.neu__buehne{display:grid;place-items:center;width:100%;max-width:34ch;
  font:500 clamp(10px,1.05vw,12.5px)/1.7 "Geist Mono",monospace}
.neu__tag,.neu__editor{grid-area:1/1;width:100%}
/* Die Einladung braucht ihre eigene Schrift zurueck (die Buehne ist mono). */
.neu__tag{display:flex;flex-direction:column;align-items:center;gap:13px;
  font:1rem/1.5 Geist,-apple-system,"Segoe UI",Roboto,sans-serif;
  transition:opacity .55s ease .3s}
.stage.is-night .neu__tag{opacity:0;pointer-events:none;transition:opacity .3s ease .08s}

/* visibility statt display: so bleibt die Hoehe des Editors IMMER im Layout
   und die Buehne behaelt ihr Mass. Mit display:none waere die Buehne tags
   niedriger und der Knopf wuerde beim Wechsel doch wieder wandern. */
.neu__editor{position:relative;text-align:left;padding-left:16px;
  visibility:hidden;opacity:0;transform:translateY(7px);
  transition:opacity .55s ease,transform .55s ease,visibility .55s}
.stage.is-night .neu__editor.is-offen{visibility:visible;opacity:1;transform:none;
  transition:opacity .4s ease,transform .45s cubic-bezier(.22,.61,.36,1),visibility .4s}
/* Der Bundsteg als eigene Ebene, damit er sich ZEICHNEN kann. Als border-left
   waere er sofort in ganzer Laenge da -- das Zeichnen ist der Moment, in dem
   die Datei aufgeht. */
.neu__editor::before{content:"";position:absolute;left:0;top:0;bottom:0;width:2px;
  background:rgba(25,191,244,.26);transform:scaleY(0);transform-origin:top;
  transition:transform .6s ease}
.stage.is-night .neu__editor.is-offen::before{transform:scaleY(1);
  transition:transform .55s cubic-bezier(.22,.61,.36,1) .18s}
/* Dateiname mit Punkt davor, wie ein ungespeicherter Reiter im Editor. */
.neu__datei{display:flex;align-items:center;gap:7px;margin:0 0 11px;
  font-size:.72rem;font-weight:600;letter-spacing:.1em;color:#6b8299}
.neu__datei::before{content:"";width:6px;height:6px;border-radius:50%;
  background:var(--cyan);flex:0 0 auto}
.neu__code{display:block;margin:0;color:#8fd8f5;white-space:pre;letter-spacing:.01em}

/* Waehrend der Nacht steht in der Adressleiste localhost (s. hero.js) --
   ein Link "Live ansehen" waere daneben ein Widerspruch. */
.stage.is-night .urllive{opacity:0;pointer-events:none}
/* Der Knopf steht unter der Buehne: erst die Regel, dann das Ergebnis.
   0.9s statt der 0.18s von .web-btn -- das ist Hover-Tempo und laesst das
   Leuchten schnappen statt warm werden. */
.neu .web-btn{transition:box-shadow .9s ease,transform .15s ease,background-color .15s ease}
/* DAS ERGEBNIS: sobald der Code fertig getippt ist, setzt das JS is-gluehend.
   WICHTIG: die drei Schattenwerte muessen mit dem Codetext im HTML
   uebereinstimmen -- sonst zeigt die Seite eine Regel, die sie nicht anwendet. */
.stage.is-night .neu.is-gluehend .web-btn{
  box-shadow:0 0  0 1px var(--cyan),
             0 0 22px -4px var(--cyan),
             0 0 64px -10px var(--cyan)}
@media(prefers-reduced-motion:reduce){
  .neu,.neu::before,.neu__tag,.neu__editor,.neu__editor::before,
  .neu .web-btn{transition:none}
  .stage.is-night .neu__editor{transform:none}
  .stage.is-night .neu__editor.is-offen::before{transform:scaleY(1)}
}
/* Leiser Hinweis, dass in dem Tab etwas passiert. Kein Blinken: das zieht
   mehr Aufmerksamkeit als der Effekt wert ist. */
.stage.is-night .btab--neu{color:var(--cyan)}
/* Dazu ein stiller Cyan-Schein um die Schrift (26.09.2026), damit der Reiter
   in der Nacht leuchtet wie die Tasse. Ein Glanz, der durch die Buchstaben
   lief, stand hier kurz und ist auf Bastis Wunsch wieder raus: der Schein
   bleibt, bewegt wird nichts. An der Textspanne, nicht am Knopf, weil der
   Knopf seinen eigenen Grund traegt (s. .btab--neu). */
.stage.is-night .btab--neu .btab__txt{filter:drop-shadow(0 0 6px rgba(25,191,244,.5))}

/* Pfeile + Chips */
.arrow{position:absolute;z-index:6;top:50%;transform:translateY(-50%);width:44px;height:44px;
  border:0;background:none;padding:0;cursor:pointer;display:grid;place-items:center;will-change:transform;
  filter:drop-shadow(0 2px 5px rgba(2,9,20,.55));transition:transform .16s ease,filter .16s ease;-webkit-tap-highlight-color:transparent}
.arrow:hover{transform:translateY(-50%) scale(1.16);filter:drop-shadow(0 3px 8px rgba(2,9,20,.7))}
.arrow svg{width:27px;height:27px}
.arrow svg path{fill:#fff;stroke:#fff;stroke-width:1.4;stroke-linejoin:round}
.arrow--l{left:-14px}.arrow--r{right:-14px}
.arrow__tip{position:absolute;top:50%;transform:translateY(-50%);white-space:nowrap;pointer-events:none;
  background:var(--navy);color:#eaf3fb;font-size:.76rem;font-weight:600;padding:5px 10px;border-radius:7px;
  opacity:0;transition:opacity .16s ease;box-shadow:0 6px 18px rgba(2,9,20,.4)}
.arrow--l .arrow__tip{left:calc(100% + 9px)}
.arrow--r .arrow__tip{right:calc(100% + 9px)}
.arrow:hover .arrow__tip{opacity:1}
.chips{display:none;justify-content:center;gap:8px;margin-top:18px;flex-wrap:wrap}
.chip{height:36px;padding:0 15px;border-radius:18px;border:1px solid var(--line);background:#fff;color:var(--ink-soft);
  font:600 .82rem/1 Geist,sans-serif;cursor:pointer;transition:all .15s;-webkit-tap-highlight-color:transparent}
.chip[aria-selected="true"]{background:var(--ink);border-color:var(--ink);color:#fff}

/* ---- Device-Morph: Monitor <-> Handy ---- */
.monitor{transition:width .6s cubic-bezier(.5,.02,.2,1),height .6s cubic-bezier(.5,.02,.2,1),border-radius .5s ease,padding .45s ease,box-shadow .5s ease}
.browser{display:flex;flex-direction:column}
.rig:not(.is-phone) .view{aspect-ratio:16/9;flex:0 0 auto}
.is-phone .view{flex:1 1 auto;min-height:0}
.page__scroll img{width:100%;height:auto;display:block}
.tabsbar{transition:height .35s ease,opacity .28s ease,padding .35s ease}
.is-phone .tabsbar{height:0;padding-top:0;opacity:0;overflow:hidden;pointer-events:none}
.monitor__hals,.monitor__base{transition:opacity .3s ease,height .4s ease}
.is-phone .monitor__hals{opacity:0}
.is-phone .monitor__base{opacity:0}
.is-phone .urlbar{justify-content:center}
.is-phone .urllive{display:none}
.is-phone .monitor{display:flex;flex-direction:column;border-radius:44px;padding:13px;
  box-shadow:0 46px 84px -32px rgba(13,24,38,.5),0 0 0 1px rgba(13,24,38,.10),inset 0 1px 0 rgba(255,255,255,.06)}
.is-phone .browser{flex:1 1 auto;min-height:0;border-radius:31px}
.monitor__chin{transition:height .35s ease,opacity .3s ease}
.is-phone .monitor__chin{height:0;overflow:hidden;opacity:0}
.desk{transition:opacity .4s ease,filter 2s ease}
.is-phone .desk{opacity:0}
.rig.is-morphing .monitor{display:flex;flex-direction:column}
.rig.is-morphing .browser{flex:1 1 auto;min-height:0}
.rig.is-morphing .view{flex:1 1 auto;min-height:0;aspect-ratio:auto}
.track{transition:transform .55s cubic-bezier(.3,.86,.28,1),opacity .22s ease}
.rig.is-morphing .track{opacity:0}
/* Nur fuer den Messschritt in setPhone (hero.js, mobil): den Endzustand
   anlegen und seine Masse lesen, ohne dass eine anlaufende Transition sie
   verfaelscht. Liegt nie laenger als einen synchronen Block an. */
.rig.is-messen,.rig.is-messen *{transition:none!important}
/* Ausnahme: die Deckkraft der Tasse (26.09.2026). Eingerollt ist sie
   unsichtbar, und beim Wechsel auf Desktop rechnet der Messblock kurz den
   Desktop-Zustand, in dem sie sichtbar ist. Ohne Transition sprang sie dabei
   auf volle Deckkraft und blendete unter .is-morphing wieder aus, ein
   Aufblitzen mitten im Wechsel. Mit Transition startet sie bei 0 und wird im
   selben Block auf 0 zurueckgelenkt, bevor ein Bild entsteht. */
.rig.is-messen .prop--mug{transition:opacity .4s ease!important}
/* =====================================================================
   EINSPALTIG (ab hier steht das Geraet UNTER dem Text).
   Alles, was von der Spaltenzahl abhaengt, gehoert in DIESEN Breakpoint —
   nicht in den 700er. Ich hatte Lampe und Fenster zuerst nur unter 700px
   umgestellt; zwischen 701 und 1000px war das Layout aber schon einspaltig
   und die Leuchte hing wieder 53px im Text (gemessen). Merke: Szenen-Geometrie
   folgt dem LAYOUT-Breakpoint, Geraete-/Bedien-Regeln dem 700er.
   ===================================================================== */
@media(max-width:1000px){
  /* ZWEI KORREKTUREN (25.09.2026), beide zwischen 701 und 1000px gemessen.
     padding-top stand hier fest auf 64px, noch aus der Zeit der mitlaufenden
     .web-head. Die Leiste ist fixed und 64 bis 80px hoch: das Eyebrow klebte
     bei 768px an ihrer Unterkante und lag bei 800px darunter. Jetzt dieselbe
     Rechnung wie oben, Leistenhoehe plus Luft.
     minmax(0,1fr) statt 1fr: 1fr heisst minmax(auto,1fr), und auto ist die
     Mindestbreite des Inhalts. Die Reiterleiste will ungekuerzt rund 714px,
     die Spur wuchs bei 768px darum ueber den rechten Rand hinaus und das Geraet
     stiess dort an die Kante. Jetzt passt es in die Spur, die Reiter kuerzen
     ihre Namen mit Auslassungspunkten wie in einem echten Browser. */
  .stage{grid-template-columns:minmax(0,1fr);gap:34px;align-items:start;padding-top:calc(var(--nav-hoehe,80px) + 24px)}
  .stage__text{max-width:640px}
  .rig{width:100%;justify-self:center}
  .szene__licht{display:none}
  .layer--buero{opacity:.55}

  /* Fenster/Wand in den unteren Bereich, hinter das Geraet — sonst laufen die
     Rahmenlinien quer durch Eyebrow und Lead. */
  .layer--wand{top:auto;height:min(50%,420px);bottom:clamp(92px,12vh,140px)}
  .szene__nebel{display:none}   /* war der Lesbarkeits-Nebel der Textspalte; ohne Fenster dahinter ohne Funktion */

  /* PENDELLEUCHTE RAUS (Basti: „nimmt unnoetig Platz weg, eher scheisse").
     Sie ist ein Zweispalten-Element: dort haengt sie ueber der Headline und hat
     einen Sinn. Einspaltig braeuchte sie ~78px reservierte Hoehe ueber dem
     Geraet und brachte optisch nichts. Der Platz geht ans Geraet. */
  .pendant{display:none}

  /* Nacht-Kante weich auslaufen lassen.
     Der Schleier liegt in .stage. Auf dem Desktop ist die Stage 100svh hoch und
     fuellt den Schirm — einspaltig ist der Hero ~1200px bei ~850px Fenster,
     dadurch sah man Dunkelheit und direkt darunter die naechste Sektion in
     vollem Weiss (harte Linie quer durchs Bild). Die Maske laesst Schleier und
     Sonne auf den letzten ~10% auslaufen, der Uebergang wird dadurch ein
     Verlauf statt einer Kante. */
  .night-veil,.sun{
    -webkit-mask-image:linear-gradient(180deg,#000 0%,#000 88%,transparent 100%);
    mask-image:linear-gradient(180deg,#000 0%,#000 88%,transparent 100%)}
}
/* =====================================================================
   HERO MOBIL — eigene Komposition, nicht der geschrumpfte Desktop.
   Kernentscheidungen:
   · Fenster liegt NUR hinter dem Geraet, nie hinter dem Text
   · Lampe haengt ueber dem GERAET (Position via placeLamp), nicht ueber der H1
   · Standard ist die HANDY-Ansicht: die Kundenseite ist bei ~0.67 lesbar,
     der Desktop-Screenshot waere bei 0.26 nur ein Bild
   ===================================================================== */
@media(max-width:700px){
  .stage{gap:26px;padding-top:calc(var(--nav-hoehe,64px) + 20px)}
  .layer--buero{display:none}

  /* ---- Textblock: EIN gap statt vieler Einzelabstaende ----
     Vorher trug jedes Element seine eigene margin und die Abstaende wirkten
     gequetscht und ungleich. Flex + gap regelt den Rhythmus an einer Stelle. */
  .stage__text{display:flex;flex-direction:column;gap:1.5rem}
  .stage__text>*{margin-top:0;margin-bottom:0}
  .stage__text h1{margin:0}
  /* Ausnahme vom einen gap: das Eyebrow gehoert zur Headline, nicht in den
     Rhythmus darunter, rund 10px statt 24 (25.09.2026). Es steht IN der H1
     (s. oben), der gap des Flex-Blocks greift dort also gar nicht, die
     Kante allein setzt den Abstand. Vorher, als Absatz davor, zog hier eine
     negative Kante (-.85rem) die 24px zusammen, im Ergebnis dieselben 10px. */
  .stage__text .eyebrow{margin-bottom:.65rem}
  .trustmarks{margin:0;gap:11px}
  .cta-row{margin:0}

  /* ---- CTA ueber die volle Breite ----
     Auf dem Handy ist ein breiter Button das deutlich bessere Touch-Ziel und
     liest sich als klarer naechster Schritt statt als Randnotiz. */
  .cta-row{display:block}
  .cta-row .web-btn{width:100%;height:56px;font-size:1.04rem}

  /* --- Geraet: Handy-Ansicht (Standard) --- */
  .is-phone .monitor{width:min(288px,76vw);height:auto;display:block;padding:9px;
    border-radius:32px;box-shadow:0 26px 50px -24px rgba(13,24,38,.5),0 0 0 1px rgba(13,24,38,.10)}
  .is-phone .browser{border-radius:24px}
  .is-phone .view{flex:none;aspect-ratio:9/15.5;min-height:0}
  .is-phone .urlbar{height:34px}

  /* --- Geraet: Desktop-Ansicht (per Umschalter) ---
     Feste Landschafts-Proportion statt fixer Hoehe: mit height:min(58vh,436px)
     wurde das Bild bei ~610px Breite fast quadratisch (Verhaeltnis 1.23) und las
     sich nicht mehr als Monitor. 16/10 ist eindeutig Landschaft und spart Hoehe. */
  .rig:not(.is-phone) .monitor{padding:7px 7px 0;border-radius:13px}
  .rig:not(.is-phone) .view{aspect-ratio:16/10;height:auto}
  /* Beim Morph fuellt der Bildschirm die wandernde Huelle (s. setPhone).
     Die Regel oben ist gleich spezifisch wie .rig.is-morphing .view und
     steht spaeter, ohne diese Zeile hielte sie den Bildschirm auf 16/10. */
  .rig.is-morphing .view{aspect-ratio:auto}

  /* WICHTIG: Handy ist mobil der Standard — die Desktop-Regeln blenden im
     is-phone-Zustand aber Tisch und Tasse aus. Damit waeren sie mobil DAUERHAFT
     weg: kein Tisch unter dem Geraet und das Feierabend-Egg unerreichbar.
     Hier zurueckgeholt; das Handy liegt auf dem Tisch. */
  .is-phone .desk{opacity:1}
  .is-phone .prop--mug{opacity:1;pointer-events:auto}
  /* Hals und Standfuss auf Null: ein Handy hat keinen Monitorfuss. Auf dem
     Desktop behalten sie ihre Hoehe (Reflow-Schutz beim Morph) — mobil gibt es
     keinen Morph, also darf hier kollabiert werden. */
  .is-phone .monitor__hals{height:0}
  .is-phone .monitor__base{height:0;margin-top:0}

  .tabsbar{display:none}
  /* 8 statt 14: nachgemessen war die Reihe nach dem Ausklappen auf 390x664 um
     3px angeschnitten. Zusammen mit den 5px am Monitorpolster sind es 11px —
     danach steht sie mit 8px Luft im Bild. */
  .chips{display:flex;margin-top:8px}
  /* Die Chip-Reihe stand hier zwischenzeitlich 46px HOEHER, also im Band der
     Tischbeine — das war noetig, solange die Kopfleiste 64px belegte und die
     Reihe sonst unter der Bildkante lag.
     Seit die Leiste beim Runterscrollen wegfaehrt, gibt es den Platz, und die
     Reihe steht wieder da, wo sie hingehoert: UNTER der Tisch-Illustration.
     Nebenwirkung, positiv: dadurch ist die Geometrie im eingerollten und im
     ausgefahrenen Zustand wieder gleich — und genau deren Unterschied hatte
     die Tasse 50px ueber der Tischplatte schweben lassen. */

  /* Einzeilig bleiben. Bei 360px Breite (Galaxy S23 und die ganze 360er-Klasse)
     stehen nach dem Seitenrand 316px zur Verfuegung, und mit gap 8 plus 15px
     Innenpolster brauchte die Reihe 326px — sie brach auf zwei Zeilen und riss
     44px zusaetzliche Hoehe. Zwei Zeilen sind hier ohnehin die schlechtere
     Reihe, nicht nur die hoehere. */
  @media(max-width:389px){
    .chips{gap:6px}
    .chip{padding:0 11px;font-size:.78rem}
  }
  .arrow{display:none}
  .prop--notiz,.prop--sukku{display:none}
  .desk__legs i{height:42px}
  /* Tasse an die TISCHPLATTE binden, nicht an die Rig-Unterkante: darunter liegen
     mobil Chips und Umschalter, sonst landet sie auf den Tasten. --mug-bottom
     wird gemessen (platziereTasse), der Fallback greift ohne JS. */
  /* Etwas groesser und ein Stueck weiter auf die Platte: 44px am aeussersten
     Rand sind mit dem Daumen kaum zu treffen und faellt als Motiv nicht auf. */
  .prop--mug{right:7%;width:52px;bottom:var(--mug-bottom,158px)}
  /* Mehr Farbe, aber mit dem, was schon da ist: der Cyan-Streifen geht auf
     volle Deckung und wird eine Spur hoeher, der Rand der Tasse bekommt einen
     Hauch davon ab. Auf 52px ist das der Unterschied zwischen „weisses Ding"
     und „das eine bunte Teil auf dem Tisch". Kein neuer Farbton — der Streifen
     ist im SVG schon #19bff4. */
  .prop--mug .mug__band{opacity:1}
  .prop--mug .mug__leib{stroke:#b9dcea}
}
/* --- Geraete-Umschalter einspaltig: aus der Schreibtisch-Illustration werden
   zwei beschriftete Tasten mit 44px Trefferflaeche. Die 186px breite Deko-Szene
   passt auf 390px nicht und waere mit dem Finger nicht bedienbar.
   BIS 1000px STATT BIS 700px (25.09.2026). Dazwischen ist das Layout schon
   einspaltig und das Geraet fuellt die Breite. Die Szene stand weiter links
   daneben, bei 768px also bei x = -160, halb ausserhalb des Bildes. Tablets
   sind ausserdem Touch-Geraete, die 44px-Tasten passen dort genauso.
   Wo der Umschalter steht, regeln der 700er-Block (im Fluss) und der
   Tablet-Block darunter (absolut). --- */
@media(max-width:1000px){
  /* margin-top 10 statt 16: nachgemessen war der Umschalter auf dem iPhone
     14/15 mit ausgefahrener Adressleiste nach dem Ausklappen um 3px
     angeschnitten — vom obersten Geraeterand bis zu seiner Unterkante brauchte
     es 665px, im Bild waren 662. Drei Pixel sehen nicht nach knapp aus,
     sondern nach schlampig. Die 6px hier holen sie. */
  .deviceswitch{display:flex;position:static;right:auto;bottom:auto;
    flex-direction:row;justify-content:center;gap:8px;margin:10px auto 0;filter:none}
  .dsw__hint{display:none}
  .dsw__stage{position:static;display:flex;gap:8px;width:auto;height:auto}
  .dsw__desk{display:none}
  .dsw__beam{display:none}
  .dsw__dev{position:static;left:auto;bottom:auto;width:auto;opacity:1;
    display:inline-flex;align-items:center;justify-content:center;gap:8px;
    min-height:44px;padding:0 16px;border:1px solid var(--line);border-radius:11px;
    background:var(--white);color:var(--ink-soft);font:640 .88rem/1 Geist,sans-serif;
    transform:none;transition:background .16s ease,border-color .16s ease,color .16s ease}
  .dsw__dev svg{width:15px;height:auto;flex:0 0 auto}
  .dsw__dev .dsw__frame{stroke:currentColor;stroke-width:2.2}
  .dsw__dev .dsw__screen{fill:none}
  .dsw__dev.is-active{background:var(--cyan-soft);border-color:#9adcf6;color:var(--cyan-ink)}
  .dsw__dev.is-active .dsw__screen{fill:rgba(25,191,244,.22)}
  .dsw__dev:hover{transform:none;filter:none}
  .dsw__txt{display:inline}
}
/* =====================================================================
   TABLET (701 bis 1000px): einspaltig und MITTIG (Basti, 25.09.2026).
   Das Geraet nimmt die volle Breite, die Achse des Bildes ist also die
   Mitte, und linksbuendig hing der Textblock daneben. Der Handy-Hero bleibt
   linksbuendig, er ist eine eigene Komposition mit breitem Knopf.
   ===================================================================== */
@media(min-width:701px) and (max-width:1000px){
  .stage__text{justify-self:center;text-align:center}
  /* Beide sind schmaler als die Spalte (fit-content bzw. 40ch): mittig
     kommen sie nur ueber die Kante, text-align allein reicht nicht. */
  .eyebrow--bunt,.lead{margin-left:auto;margin-right:auto}
  /* Die Liste als Block mittig, die Zeilen darin linksbuendig: mittig
     gesetzte Zeilen stellten jeden Haken an eine andere Stelle. */
  .trustmarks{width:fit-content;margin-left:auto;margin-right:auto;text-align:left}
  .cta-row{justify-content:center}
  /* Reiterleiste: die Spur ist jetzt so breit wie der Inhalt, darum muessen
     die acht Reiter in die Leiste passen statt die Spur aufzudruecken.
     Polster 5 statt 7, Punkt-Abstand 6 statt 8 und Leistenabstand 3 statt 4
     reichen ab 761px (gemessen: bei 768px 18px Luft). Darunter fehlten bei
     701px noch 3 bis 7px je Reiter, dort zusaetzlich Polster 4 und .76rem.
     safe center: reicht es doch einmal nicht, schneidet ein Reiter rechts ab
     statt beidseitig. Mittig uebergelaufen verlor er links seinen Farbpunkt. */
  .tabsbar{gap:3px}
  .btab{gap:6px;padding:0 5px;justify-content:safe center}
  @media(max-width:760px){.btab{padding:0 4px;font-size:.76rem}}
  /* Umschalter mittig unter dem Tisch, aber ABSOLUT: die Tasse haengt mit
     bottom:80px an der Rig-Unterkante, alles im Fluss darunter hebt sie vom
     Tisch. Mobil faengt das platziereTasse() in hero.js ab, auf dem Tablet
     laeuft es nicht. Den Platz reserviert margin-bottom am Rig. */
  .rig{margin-bottom:84px}
  .deviceswitch{position:absolute;left:50%;top:calc(100% + 20px);margin:0;transform:translateX(-50%)}
}
@media(prefers-reduced-motion:reduce){
  .track{transition:none}
  .browser{transition:none;opacity:1;filter:none}
  .caret{animation:none}
  .stage.is-brewing .dampf{animation:none;opacity:0}
  .dsw__beam{animation:none!important;opacity:0}
  .monitor{transform:none}
}

/* ---- Sektions-Kopf ----
   Zwei Varianten bewusst im Wechsel: zentriert = Aussage/Kapitelmarke,
   links = Arbeitsabschnitt. Über 8 Sektionen entsteht so Rhythmus statt Schema. */
.sec-head{max-width:720px;margin:0 auto clamp(40px,5vw,60px);text-align:center}
.sec-head h2{margin:0 0 14px;font-size:clamp(2rem,4vw,3rem);line-height:1.05;letter-spacing:-.03em;font-weight:710;
  text-wrap:balance}                       /* verhindert Schusterjungen in der 2. Zeile */
.sec-sub{margin:0;font-size:clamp(1.05rem,1.4vw,1.18rem);color:var(--ink-soft);line-height:1.5;text-wrap:pretty}
/* Umbruch am SINN, nicht am Platz. Der Doppelpunkt trennt Ankuendigung und
   Einloesung — dort soll die Zeile brechen und nicht dort, wo der Rand
   zufaellig erreicht ist.
   NUR AB 561px. Darunter passt der erste Teil (47 Zeichen) nicht mehr auf
   eine Zeile: auf 390px stehen rund 41 Zeichen zur Verfuegung, der erzwungene
   Bruch machte aus drei Zeilen vier, davon eine mit einem Wort. Ohne die
   Regel bricht der Satz dort normal um — das Leerzeichen hinter dem <br>
   bleibt erhalten, wenn es ausgeblendet ist. */
.br-satz{display:none}
@media(min-width:561px){ .br-satz{display:inline} }
/* Linksbündige Variante: Kopf und Subline sitzen nebeneinander, Blick läuft in den Inhalt.
   OBEN buendig, nicht unten. Vorher stand hier align-items:end: die Spalten
   schlossen unten ab, und wo sie ANFINGEN, entschied dann der Zeilenunterschied
   zwischen Ueberschrift und Subline. Gemessen auf 1440px ueber die vier Koepfe
   dieser Seite (Startlinie der Ueberschrift gegenueber der Subline):

     Branchen   H2 2 Zeilen / Sub 4   ->  H2 startet  23px tiefer
     Preise     H2 1 Zeile  / Sub 3   ->  H2 startet  41px tiefer
     Ablauf     H2 2 Zeilen / Sub 2   ->  H2 startet  31px hoeher
     FAQ        H2 2 Zeilen / Sub 1   ->  H2 startet  57px hoeher

   Eine Spanne von 98px, und keiner der vier Werte war entschieden — sie fielen
   an. Zwei Bloecke, die zusammen EINE Kopfzeile bilden, beginnen auf derselben
   Linie; wo sie enden, ist eine Folge der Textlaenge und keine Gestaltung.
   Die Subline bekommt ein kleines Polster nach oben: bei 43px Schriftgrad hat
   die Ueberschrift (Zeilenhoehe 1,05) fast keine Vorlauflinie, die Subline
   (Zeilenhoehe 1,5) dagegen rund 4,5px. Kastenbuendig waeren die BUCHSTABEN
   also versetzt — das Polster gleicht die Differenz aus. */
.sec-head--left{max-width:var(--wrap);margin-left:auto;margin-right:auto;text-align:left;
  display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);gap:clamp(18px,4vw,64px);align-items:start}
.sec-head--left h2{margin:0;font-size:clamp(1.95rem,3.5vw,2.7rem)}
.sec-head--left .sec-sub{margin:0;padding-top:.18em;font-size:clamp(1.02rem,1.25vw,1.12rem)}
.sec-head--left .eyebrow{grid-column:1/-1;margin-bottom:2px}
@media(max-width:820px){
  /* Einspaltig steht die Subline UNTER der Ueberschrift — dort gibt es nichts
     mehr auszugleichen, das Polster waere nur ein zusaetzlicher Abstand. */
  .sec-head--left{grid-template-columns:1fr;gap:14px}
  .sec-head--left .sec-sub{padding-top:0}
}
/* Titel-Marker: derselbe handgezeichnete Strich wie im Hero — einmal pro Seite,
   als Wiedererkennung. Bewusst NICHT überall, sonst verliert er seine Wirkung. */
.mark{position:relative;z-index:0;white-space:nowrap}   /* z-index:0 = eigener Stacking-Context,
     sonst rutscht das ::after mit z-index:-1 hinter den Sektionshintergrund und ist unsichtbar */
.mark::after{content:"";position:absolute;left:-.04em;right:-.08em;bottom:.05em;height:.3em;z-index:-1;
  background:var(--cyan);opacity:.32;
  -webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 40' preserveAspectRatio='none'%3E%3Cpath fill='%23fff' d='M3 13C6 9.5 12 7.8 20 7.5 33 7 44 5.8 55 5.2 70 4.4 82 3.4 95 3 112 2.5 126 4 140 4.4 156 4.8 170 3 185 2.6 202 2.2 216 3.6 230 4 244 4.4 256 2.9 268 2.6 280 3.4 290 5.4 297.4 8.2C299.4 11.6 298.4 16.4 299.5 20.8C299.9 24.4 298.6 28.6 296.8 31.6C288 34.6 276 36.4 264 37 242 38 218 36.3 194 37.1 170 37.9 148 36.2 124 37 100 37.8 78 36.3 56 35.7 42 35.2 30 33.2 20 32 12 31 6 29.5 3 27Z'/%3E%3C/svg%3E");
  mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 40' preserveAspectRatio='none'%3E%3Cpath fill='%23fff' d='M3 13C6 9.5 12 7.8 20 7.5 33 7 44 5.8 55 5.2 70 4.4 82 3.4 95 3 112 2.5 126 4 140 4.4 156 4.8 170 3 185 2.6 202 2.2 216 3.6 230 4 244 4.4 256 2.9 268 2.6 280 3.4 290 5.4 297.4 8.2C299.4 11.6 298.4 16.4 299.5 20.8C299.9 24.4 298.6 28.6 296.8 31.6C288 34.6 276 36.4 264 37 242 38 218 36.3 194 37.1 170 37.9 148 36.2 124 37 100 37.8 78 36.3 56 35.7 42 35.2 30 33.2 20 32 12 31 6 29.5 3 27Z'/%3E%3C/svg%3E");
  -webkit-mask-size:100% 100%;mask-size:100% 100%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}

/* ---- Der Strich faengt Licht, wenn der Zeiger nahe kommt ---------------
   Uebernommen von /software, damit beide Seiten dieselbe Sprache sprechen.
   --hlg (0 bis 1) ist die Naehe des Zeigers und kommt aus hero.js, dort als
   kritisch gedaempfte Feder (Begruendung dort).

   DER STRICH SELBST wird kraeftiger (.32 auf .62) und waechst um 16 %. Auf
   hellem Grund kann Licht nicht durch Aufhellen arbeiten, hell auf hell ist
   nichts, und eine Groessenaenderung sieht das Auge auf jedem Untergrund.
   Ankerpunkt unten, damit er nach oben aufgeht und nicht von der
   Schriftlinie wegrutscht. Frueher lief die Deckkraft bis .94, das las sich
   beim Ueberfahren als Abdunkeln.

   KEIN HOF MEHR (25.09.2026). Bis dahin sassen zwei drop-shadows am Strich,
   ihre Deckkraft hing an --hlg. Nachgemessen: Die Maske schneidet den Filter
   ab (erst Filter, dann Maske), bei --hlg 1 war neben dem Strich kein Hof zu
   sehen. Gekostet hat er trotzdem: jede neue Schattenfarbe hiess, Strich,
   Maske und Filter in jedem Bild neu zu rastern, daher das Ruckeln. Ein
   weichgezeichneter Hof als eigene Ebene war kurz im Test, er nahm dem
   Strich die gezeichnete Kante (Basti: das weiche Auslaufen passt nicht).
   Jetzt aendern sich nur opacity und transform, beide im Compositor, neu
   gezeichnet wird nichts.

   KEINE transition: die Weichheit macht die Feder in hero.js. Eine
   Transition darueber daempft ein zweites Mal und macht aus Reaktion
   Nachlauf. Ausserdem wuerde sie die 1,1s ueberschreiben, mit denen der
   Strich im Nachtmodus des Heros aufglueht. */
.hl::after,.mark::after{
  opacity:calc(.32 + var(--hlg,0) * .30);
  transform:scaleY(calc(1 + var(--hlg,0) * .16));transform-origin:50% 100%;
  transition:none;
  will-change:opacity,transform}

/* ---- Scroll-Reveal ----
   Armiert per JS (html.rv-on): ohne JS bleibt alles sichtbar. Bewusst knapp
   dosiert — 10px und 0.6s, das liest sich als Ruhe, nicht als Effekt. */
.rv-on .rv{opacity:0;transform:translateY(10px)}
@media(prefers-reduced-motion:no-preference){
  .rv-on .rv{transition:opacity .6s cubic-bezier(.22,.7,.3,1),transform .6s cubic-bezier(.22,.7,.3,1);
    transition-delay:var(--rvd,0ms)}
}
.rv-on .rv.is-in{opacity:1;transform:none}
@media(prefers-reduced-motion:reduce){.rv-on .rv{opacity:1;transform:none}}

/* ---- Cases ---- */
.cases{padding:clamp(52px,7vw,100px) var(--page);background:var(--paper)}
/* ---- Keine Screenshots mehr, und das ist der Kern der Sektion ----------
   Hier standen zwei Browser-Attrappen mit Screenshots der Kundenseiten —
   dieselben zwei Seiten, die der Hero 2.000px weiter oben LIVE und
   interaktiv zeigt. Gemessen: Hero 702x395px lebendig, hier 624 bzw. 490px
   statisch. Die zweite Fassung war schlicht die schwaechere; ein Screenshot
   kann nichts, was die laufende Vorschau nicht besser kann.
   An ihrer Stelle traegt jetzt die MESSLEISTE die Referenz. Sie klemmte
   vorher in der 490px-Textspalte mit 113px je Wert; ueber die volle
   Rasterbreite sind es rund 290px. Damit wird der Beleg zum Bild — was eine
   Referenz ohnehin tun soll: nicht zeigen, wie es aussieht, sondern was es
   kann.
   Der Kopf uebernimmt das linksbuendige Muster, das die Seite an vier
   weiteren Stellen benutzt (Marke und Titel links, Beschreibung rechts) —
   kein neues Layout, nur ein weiteres Vorkommen.
   assets/cases/reifenservice-web.webp und gws-web.webp liegen weiter auf dem
   Server, werden aber von keiner Seite mehr geladen. */
/* BEIDE REFERENZEN NEBENEINANDER, die Messleiste darunter ueber die volle
   Breite. Das hat zwei Gruende, und der zweite ist der wichtigere:
   - Nebeneinander liest sich als „hier sind unsere Arbeiten“ statt als zwei
     einzelne Auftritte hintereinander.
   - Es gibt bisher nur EINE Messung (Reifenservice). Untereinander stand sie
     bei der ersten Referenz und fehlte sichtbar bei der zweiten. Unter beiden
     gehoert sie zur Sektion statt zu einer Karte.
   DAFUER MUSS SIE IHR PROJEKT NENNEN. Steht sie unbeschriftet unter zwei
   Referenzen, liest sie sich als Messung fuer beide — und die Zahl fuer die
   zweite Seite haben wir nicht. Die Quellzeile sagt deshalb „Google
   Lighthouse · Reifenservice Mechernich · Test vom …“. Sobald die zweite
   Seite gemessen ist, kommt eine zweite Leiste dazu und beide sind
   beschriftet.
   Der Kopf ist in der schmalen Spalte wieder EINSPALTIG: bei rund 560px je
   Referenz haetten Titel und Beschreibung nebeneinander je 270px — zu wenig
   fuer beides. */
/* DREI SPALTEN seit der dritten Referenz (Friseursalon IBO). Der Klassenname
   sagt noch "Paar" — er bleibt, weil er an rund einem Dutzend Stellen haengt
   und ein Umbenennen nichts gewinnt, ausser Gelegenheit fuer Tippfehler.
   Der Umschaltpunkt liegt bei 1080px und damit hoeher als der alte bei 900px:
   bei drei Karten waeren darunter je rund 280px uebrig, und eine Beschreibung
   von 190 Zeichen bricht dort auf zehn Zeilen. Ein Zwischenschritt mit ZWEI
   Spalten waere falsch — er stellt die dritte Karte allein in eine zweite
   Reihe, und eine halbleere Rasterzeile liest sich als Fehler. */
.case__paar{--case-luecke:clamp(28px,4vw,56px);
  display:grid;grid-template-columns:repeat(3,1fr);
  /* Die Zeilenluecke ist zugleich der Bruch zwischen den beiden Trennlinien
     je Spalte (s. weiter unten). Sie muss deshalb gross genug sein, dass man
     sie als Absicht liest und nicht als abgerissene Linie — gemessen waren
     37px dafuer knapp. */
  column-gap:var(--case-luecke);row-gap:clamp(34px,3.6vw,54px);
  max-width:var(--wrap);margin:0 auto}
/* .projekt loest sich auf: Karte und Leiste werden selbst Rasterelemente und
   koennen in Zeile 1 und 2 derselben Spalte sitzen. Ohne display:contents
   waere jedes Projekt EIN Element und die Leisten haetten keine gemeinsame
   Grundlinie ueber die drei Spalten.
   Die Spalte steht ausdruecklich dran statt der automatischen Platzierung
   ueberlassen zu werden — bei sechs Elementen in zwei Zeilen ist Raten
   billiger als Nachlesen, aber teurer, wenn es einmal daneben geht.
   ACHTUNG: ein Element mit display:contents hat KEINE Box. Deshalb traegt
   .projekt kein .rv — das Reveal beobachtet Karten und Leisten einzeln. */
.projekt{display:contents}
.projekt > .case{grid-row:1}
.projekt > .mess{grid-row:2}
.projekt:nth-child(1) > *{grid-column:1}
.projekt:nth-child(2) > *{grid-column:2}
.projekt:nth-child(3) > *{grid-column:3}
@media(max-width:1080px){
  .case__paar{grid-template-columns:1fr;gap:clamp(38px,6vw,56px)}
}
/* TRENNLINIEN ZWISCHEN DEN SPALTEN, nur mehrspaltig.
   Dieselbe Geste wie bei den Branchenkarten weiter unten: die stehen zwischen
   zwei Haarlinien (.branche, border-width:0 1px). Drei Textbloecke ohne
   Trennung lesen sich als ein Feld mit Absaetzen; mit Linie als drei Spalten.
   Die Linie sitzt MITTIG in der Luecke, nicht an der Kartenkante — deshalb
   haengt sie an einem Pseudoelement mit halber Lueckenbreite nach links und
   nicht an border-left. Bei 56px Luecke waere ein Rand an der Kante 28px aus
   der Mitte, und das sieht man.
   Nur zwischen den Spalten, nicht aussen: .projekt + .projekt trifft die
   zweite und dritte, nie die erste. FRUEHER STAND HIER .case + .case — das
   greift nicht mehr, seit jede Karte in einem .projekt steckt und die Karten
   damit keine Nachbarn mehr im Dokument sind.
   ZWEI LINIEN JE SPALTE, eine an der Karte und eine an der Leiste — und
   ausdruecklich NICHT durchgezogen. Die Zeilenluecke bleibt frei, die Linie
   setzt darunter neu an. Eine durchgehende Linie haette Karte und Leiste zu
   einem Kasten verbunden; getrennt gelesen bleiben es zwei Aussagen
   uebereinander, die zur selben Spalte gehoeren.
   Einspaltig faellt sie weg — untereinander trennt schon der Abstand, eine
   senkrechte Linie haette dort gar keinen Bezug. */
@media(min-width:1081px){
  .case,.mess{position:relative}
  /* Oben und unten um DENSELBEN Betrag eingerueckt, um den die Linie seitlich
     von der Spalte absteht: halbe Spaltenluecke. Vorher lief sie bis an die
     Kanten und stiess unten hart auf die Zeilenluecke — seitlich stand sie
     luftig, senkrecht nicht. Mit gleichem Mass in beide Richtungen sitzt sie
     ruhig. */
  .projekt + .projekt > .case::before,
  .projekt + .projekt > .mess::before{content:"";position:absolute;
    top:calc(var(--case-luecke) / 2);bottom:calc(var(--case-luecke) / 2);
    left:calc(var(--case-luecke) / -2);width:1px;background:var(--line)}
}
/* Beide Karten sind Rasterelemente derselben Zeile und damit ohnehin gleich
   hoch. Ihr INHALT lief aber unterschiedlich weit: bei vier statt drei Zeilen
   Beschreibung rutschte "Live ansehen" um eine Zeilenhoehe nach unten, und die
   zwei Knoepfe standen sichtbar versetzt. Als Spalte gesetzt mit margin-top:auto
   an der Linkzeile faellt sie in beiden Karten auf dieselbe Grundlinie — auch
   wenn spaeter jemand einen Satz laenger schreibt. */
.case{margin:0;display:flex}
.case__body{display:flex;flex-direction:column;flex:1}
.case__kopf{margin-bottom:4px}
.case__tag{margin:0 0 8px;font-size:.78rem;font-weight:680;letter-spacing:.11em;
  text-transform:uppercase;color:var(--cyan-ink)}
.case__body h3{margin:0;font-size:clamp(1.6rem,2.6vw,2.2rem);line-height:1.05;
  letter-spacing:-.02em;font-weight:700}
/* DIE AUSNAHMEN SIND PFLICHT, nicht Kosmetik. Linkzeile UND Eyebrow sind auch
   <p>, und ".case__body p" (0,1,1) schlaegt beide (je 0,1,0) — das margin:0
   von hier gewann gegen das margin-top:auto der Linkzeile (sie blieb am Text
   kleben statt an die Kartenunterkante zu fallen) und gegen den unteren
   Abstand des Eyebrows.
   Ausgenommen statt anderswo hochspezifiziert: so behalten beide ihre eigenen
   Abstaende, und die naechste Regel dort fuehrt denselben Kampf nicht noch
   einmal. */
.case__body p:not(.case__links):not(.case__tag){margin:0;color:var(--ink-soft);
  line-height:1.55;font-size:1.06rem}
/* margin-top:auto schiebt die Linkzeile ans Kartenende. */
.case__links{display:flex;flex-wrap:wrap;gap:10px 28px;margin:auto 0 0}

/* ZWEI ZEILEN RESERVE FUER DIE UEBERSCHRIFT, FUENF FUER DIE BESCHREIBUNG —
   beides nur mehrspaltig.
   "Reifenservice Mechernich" braucht zwei Zeilen, "Kegema" und "Friseursalon
   IBO" je eine; ohne Reserve begann der graue Text in der ersten Karte eine
   Zeile tiefer. Und Kegemas Beschreibung laeuft ueber fuenf Zeilen, die der
   beiden anderen ueber vier — gemessen lagen die Aufzaehlungen dadurch 26px
   auseinander, genau eine Zeile.
   In em, damit die Reserve mitwaechst: 2 x 1.05 = 2.1em fuer die Ueberschrift,
   5 x 1.55 = 7.75em fuer den Text.
   Einspaltig gibt es nichts auszurichten, dort waeren es nur Leerraeume. */
@media(min-width:1081px){
  .case__body h3{min-height:2.1em}
  .case__kopf > p{min-height:7.75em}
}
/* ---- Kundenstimme ------------------------------------------------------
   Eigener Block hinter den zwei Case-Karten, NICHT in einer davon. In der
   Karte stand sie neben Beschreibung, Leistungspunkten und PageSpeed — vier
   Aussagen in einem Kasten, von denen jede die anderen schwaecht, und die
   Rezension war mit Abstand das groesste Element. Allein wirkt sie.
   Aufbau: Portraet links, Wortlaut rechts, Name darunter. Auf dem Handy
   wandert das Portraet nach oben und wird klein — dort zaehlt der Text.
   Wortlaut unveraendert uebernommen; Anfuehrungszeichen als Grafik statt im
   Text, damit beim Kopieren der reine Wortlaut in der Zwischenablage landet. */
.stimme{display:grid;grid-template-columns:clamp(190px,20vw,260px) minmax(0,1fr);
  grid-template-areas:"bild text" "bild wer";
  align-content:start;gap:6px clamp(26px,3.4vw,44px);
  max-width:var(--wrap);margin:clamp(46px,5.5vw,78px) auto 0;
  padding:clamp(28px,3vw,40px);background:var(--paper);border:1px solid var(--line)}
/* Das Bild FUELLT seine Spalte ueber beide Zeilen. Quadratisch gesetzt blieben
   darunter gemessen rund 180px tote Flaeche, weil der Text daneben hoeher ist.
   Die Datei ist 800x1000 und damit von Haus aus hochkant — sie wird nur
   beschnitten, nicht gestreckt. */
.stimme__bild{grid-area:bild;width:100%;height:100%;min-height:100%;
  object-fit:cover;object-position:50% 22%;display:block;border:1px solid var(--line)}
.stimme__text{grid-area:text;margin:0;position:relative}
/* Das Anfuehrungszeichen ist Deko und darf nicht mitkopiert werden. */
.stimme__text::before{content:"";position:absolute;left:0;top:-2px;width:34px;height:2px;background:var(--cyan)}
.stimme__text p{margin:18px 0 0;color:var(--ink-soft);font-size:1.02rem;line-height:1.62}
.stimme__text p:first-of-type{margin-top:22px}
/* Der letzte Satz traegt mehr Gewicht — aber NUR ueber die Farbe, nicht ueber
   die Schriftstaerke. Fett in einem Zitat liest sich als Betonung des
   Sprechers, und die hat er nicht gesetzt: wir haetten den verkaufsrelevanten
   Satz hervorgehoben. Wer das merkt, rechnet das ganze Zitat ab. Grau zu
   Textfarbe reicht fuer die Hierarchie. */
.stimme__pointe{color:var(--ink)!important}

/* Anfuehrungszeichen per CSS, nicht im Text: so landet beim Kopieren der reine
   Wortlaut in der Zwischenablage und nicht unsere Typografie.
   Nur am Anfang und am Ende des ganzen Zitats — die alte Regel, jeden Absatz
   erneut zu oeffnen, wirkt heute fussgaengerisch.
   Deutsch oeffnet unten, Englisch oben; die englische Seite traegt lang="en". */
.stimme__text p:first-of-type::before{content:"„";color:var(--ink)}
.stimme__text p:last-of-type::after{content:"“";color:var(--ink)}
html[lang="en"] .stimme__text p:first-of-type::before{content:"“"}
html[lang="en"] .stimme__text p:last-of-type::after{content:"”"}
.stimme__wer{grid-area:wer;display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 10px;
  margin:20px 0 0;padding-top:16px;border-top:1px solid var(--line)}
.stimme__wer b{font-size:1.05rem;color:var(--ink)}
.stimme__wer span{color:var(--muted);font-size:.92rem}
.stimme__hinweis{font-size:.8rem;opacity:.85}
@media(max-width:760px){
  /* KOPFZEILE: Foto und Name nebeneinander, das Zitat darunter ueber die volle
     Breite. Vorher stand das Portraet klein allein oben und der Name ganz
     unten — das Gesicht trug damit nichts, weil es weit weg von seinem Namen
     stand.
     Warum NICHT das Zitat neben das Bild: gemessen hat der Block auf 390px
     innen 302px. Mit einem 120px-Foto blieben fuer den Text 166px, also rund
     20 Zeichen je Zeile — und „Ansprechpartner" ist allein schon 123px breit.
     Angenehm liest sich Fliesstext bei 45 bis 75 Zeichen. Ein NAME dagegen
     braucht die Breite nicht, der passt dort locker. Also bekommt die schmale
     Spalte den Namen und der Fliesstext seine vollen 39 Zeichen.
     Das Bild darf dadurch von 96 auf bis zu 124px wachsen. */
  .stimme{grid-template-columns:clamp(104px,30vw,124px) minmax(0,1fr);
    grid-template-areas:"bild wer" "text text";
    column-gap:16px;row-gap:0;padding:clamp(22px,5vw,28px)}
  .stimme__bild{width:100%;height:auto;min-height:0;aspect-ratio:1;
    object-position:50% 18%;margin:0}
  /* Neben dem Foto gestapelt statt in einer Zeile, und ohne Trennlinie: die
     gehoert an einen Abschluss, nicht an einen Kopf. */
  .stimme__wer{align-self:center;flex-direction:column;align-items:flex-start;
    gap:3px;margin:0;padding:0;border-top:0}
  .stimme__wer b{font-size:1.08rem;line-height:1.2}
  .stimme__wer span{font-size:.88rem;line-height:1.35}
  .stimme__text{margin-top:20px}
  .stimme__text p{font-size:1rem}
}

/* line-height ausgeschrieben statt "normal": nur mit einem festen Wert laesst
   sich die Zeilenhoehe unten in em ausdruecken. 1.28 ist der Wert, den der
   Browser hier ohnehin gerechnet hat (35,2px Schrift -> 45px Zeile), die
   Darstellung aendert sich also nicht. */

/* Die Leiste sitzt unter IHRER Karte. Oberkante als Trennung, sonst randlos —
   sie ist ein Anhang der Karte, keine zweite Karte.
   container-type bleibt: jede Leiste fragt ihre EIGENE Breite und schaltet
   selbst auf zwei mal zwei, wenn die Spalte eng wird (in einer 356px-Spalte
   passen vier Werte nicht nebeneinander). Deshalb kostet der Umbau auf drei
   Spalten keine einzige neue Media Query. */
.mess{container-type:inline-size;
  margin:0;padding:14px 0 0;
  border-top:1px solid var(--line)}
.mess__werte{display:grid;grid-template-columns:repeat(4,1fr);
  margin:0;padding:0;list-style:none}
/* Die Trenner sitzen an der linken Kante jedes Feldes ausser dem ersten —
   keine eigenen Elemente, die man beim Umbruch einzeln nachziehen muesste. */
.mess__werte li{padding:0 10px;min-width:0}
.mess__werte li + li{border-left:1px solid var(--line)}
.mess__werte li:first-child{padding-left:0}
.mess__werte li:last-child{padding-right:0}
.mess__werte b{display:block;
  font-size:clamp(1.5rem,2.1vw,1.85rem);line-height:1.05;font-weight:660;
  letter-spacing:-.03em;color:var(--ink);font-variant-numeric:tabular-nums}
.mess__werte span{display:block;margin-top:6px;
  font-size:.74rem;line-height:1.3;font-weight:560;color:var(--muted)}
/* Quelle und Bericht teilen sich eine Zeile: links wer und wann gemessen hat,
   rechts der Weg zum Original. */
.mess__fuss{display:flex;flex-wrap:wrap;justify-content:space-between;
  align-items:baseline;gap:6px 16px;margin:12px 0 0}
.mess__quelle{font-size:.72rem;color:var(--muted)}
.messgruppe__punkte{display:none}

/* LEISTE FUER LEISTE, nicht alle auf einmal. Drei Bloecke, die gleichzeitig
   aufblenden, liest man als eine Flaeche; nacheinander als drei Belege.
   110ms Versatz je Spalte — genug, um die Reihenfolge zu sehen, zu kurz, um
   auf die dritte zu warten. */
@media(min-width:701px){
  .projekt:nth-child(2) > .mess{transition-delay:.11s}
  .projekt:nth-child(3) > .mess{transition-delay:.22s}
}

/* ---- MOBIL: EIN WISCH, EIN PROJEKT ---------------------------------------
   Gestapelt sind drei Karten mit ihren Leisten auf dem Handy ueber 1500px
   hoch. Nebeneinander mit Schnappen ist es eine Bildschirmhoehe, und man
   sieht auf einen Blick, dass es drei sind.

   .case__paar wird zur Spur, .projekt zur Wischeinheit — Text und Zahlen
   desselben Projekts wandern zusammen. Genau dafuer liegen sie im selben
   Element; zwei getrennte Spuren muesste man synchron halten.

   NATIVES SCROLLEN, kein Skript. scroll-snap macht das Wischen, die Traegheit
   kommt vom Betriebssystem. Ohne JavaScript funktioniert alles ausser den
   Punkten darunter.

   proximity, nicht mandatory: mandatory faengt auch eine Fahrt ein, die das
   Skript beim Tippen auf einen Punkt ausloest — gemessen kam ein Sprung ueber
   zwei Karten so nur 29px weit.

   position:relative macht die Spur zum offsetParent ihrer Karten. hero.js MUSS
   mit offsetLeft rechnen statt mit getBoundingClientRect, weil die Nachbarn
   per transform:scale verkleinert sind und Rechtecke die verzerrte Groesse
   melden. */
@media(max-width:700px){
  .cases .case__paar{
    /* Was vom NAECHSTEN Projekt zu sehen ist. Das ist die einzige
       Ankuendigung, dass sich hier etwas wischen laesst — ohne sie sieht die
       Leiste aus wie eine einzelne Karte.
       Sichtbar bleibt --blick MINUS die Spaltenluecke (12px), und auch das
       nur, weil hero.js die Nachbarn zur Innenkante hin verkleinert. Um die
       Kartenmitte verkleinert zoege es die Kante 3,5 % der Kartenbreite nach
       aussen und damit aus dem Bild (s. "MEHR BILD" unten).
       Zugleich die seitliche Polsterung der Spur — und daraus ergibt sich die
       Mittigkeit: bei symmetrischer Polsterung liegt die erste Karte im
       Bildschirm ZENTRIERT, obwohl sie die erste ist. Links von ihr steht nur
       Polster, kein Nachbar; nach links laesst sich also nicht wischen. */
    --blick:40px;
    position:relative;
    /* RASTER STATT FLEX, wegen subgrid weiter unten. Die Spalten sind so breit
       wie der Inhaltsbereich der Spur; die sieben Zeilen teilen sich alle drei
       Projekte. */
    display:grid;grid-auto-flow:column;grid-auto-columns:100%;
    /* Die drei EXPLIZITEN Spalten der Desktopfassung ausdruecklich abraeumen.
       Ohne das blieben sie stehen, die Projekte landeten darin statt in den
       automatischen Spalten — gemessen war die erste 198px breit und die
       beiden anderen 251px. Sichtbare Folge: die erste Karte sass 27px links
       der Bildmitte, und ihre Sockelbeschriftung brach eine Zeile frueher um
       als bei den anderen. */
    grid-template-columns:none;
    grid-template-rows:repeat(7,auto);
    column-gap:12px;row-gap:0;
    overflow-x:auto;overscroll-behavior-x:contain;
    /* MANDATORY, nicht proximity. Mandatory zieht IMMER auf eine Karte —
       zwischen zwei stehenzubleiben ist ausgeschlossen, auch bei einem ganz
       langsamen Wisch. Genau das war mit proximity moeglich.
       Der bekannte Haken von mandatory ist, dass es auch eine Fahrt einfaengt,
       die das Skript selbst ausloest (Punktklick kam so 29px weit). Das loest
       hero.js jetzt sauber: es schaltet das Schnappen fuer die Dauer der Fahrt
       ab und danach wieder ein. */
    scroll-snap-type:x mandatory;
    scroll-behavior:auto;
    /* KEIN negativer Aussenabstand mehr. Vorher lief die Spur bis ueber den
       Seitenrand hinaus, und mit ihr die beiden Linien — die stiessen dann
       hart an die Bildschirmkanten. Jetzt endet die Spur am normalen
       Satzspiegel, und die Linien enden mit ihr: links und rechts bleibt
       dieselbe Luft frei wie ueberall sonst auf der Seite. */
    /* Seitlich KEINE Polsterung: die erste Karte soll buendig am Satzspiegel
       stehen wie jeder andere Text der Seite. Bei scroll-snap-align:start ist
       die Polsterkante zugleich die Schnappkante — 26px Polster haetten jede
       Karte um 26px nach rechts versetzt. */
    padding:16px var(--blick) 18px;
    /* Die zwei Haarlinien liegen an der SPUR, nicht an den Karten. An den
       Karten braechen sie an jeder Luecke auf; hier laufen sie in einem Zug
       durch und ueber die volle Breite, weil die Spur per negativem
       Aussenabstand ohnehin bis an den Seitenrand reicht.
       Die Spur selbst scrollt nicht — nur ihr Inhalt. Die Linien stehen
       also still, waehrend die Karten darunter durchwandern. */
    border-top:1px solid var(--line);border-bottom:1px solid var(--line);
    /* KEINE MASKE MEHR. Sie stand hier, solange links UND rechts ein Nachbar
       hereinschaute und die beiden Streifen unterschiedlich aussahen. Seit nur
       noch nach rechts gewischt wird, gibt es genau einen Streifen — die
       Asymmetrie, gegen die die Maske half, kann gar nicht mehr entstehen.
       Und ohne sie bleiben die beiden Linien scharf: sie enden am
       Satzspiegel, links und rechts also mit derselben Luft wie der uebrige
       Seiteninhalt. Genau die Luecke, die gewuenscht war. */
    scrollbar-width:none;-ms-overflow-style:none}
  .cases .case__paar::-webkit-scrollbar{display:none}

  .projekt{
    /* Traegt die sieben Zeilen der Spur weiter an seine eigenen Kinder. */
    display:grid;grid-template-rows:subgrid;grid-row:1 / -1;
    /* MITTIG UND TROTZDEM VORN. center schnappt die Kartenmitte auf die
       Spurmitte; weil links der ersten Karte nur Polster liegt, ist ihre
       Mittenlage zugleich Position 0 — nach links geht es nicht weiter, nach
       rechts schon. Genau das war gewuenscht: vorn anfangen, mittig stehen,
       nur nach rechts wischen. */
    scroll-snap-align:center;
    padding:0 16px;
    /* Randlos: die Begrenzung macht die Spur oben und unten, die Trennung
       zwischen den Karten machen Luecke, Groesse und Deckkraft. Ein Kasten je
       Karte waere neben den zwei durchlaufenden Linien ein zweites
       Formenvokabular. */
    /* DIE NACHBARN TRETEN ZURUECK. Ohne das stehen drei gleichwertige Karten
       nebeneinander und keine ist die gemeinte.
       NUR transform UND opacity — das sind die beiden Eigenschaften, die der
       Browser allein auf dem Compositor erledigt, ohne neu zu zeichnen.
       Ein filter:blur() auf den Nachbarn stand hier am 03.09. kurz drin und
       ist bewusst wieder raus: filter zwingt zu einem echten Zeichendurchgang.
       Auf einem aktuellen Geraet faellt das nicht auf, auf einem drei Jahre
       alten Android schon — und ausgerechnet diese Sektion zeigt darunter
       Lighthouse-Werte zwischen 97 und 100. Ein Effekt, der die Bildrate
       kostet, ist hier am falschen Platz. scale und opacity tragen den
       Tiefeneindruck ohnehin. */
    transform:scale(.93);opacity:.5;
    transition:transform .32s cubic-bezier(.22,.7,.3,1),opacity .32s ease}
  .projekt.is-mitte{transform:none;opacity:1}

  /* WEICHE, DURCHGEHENDE WISCHBEWEGUNG statt zweier Zustaende.
     Ohne das springt eine Karte von klein auf gross, sobald das Skript die
     Klasse umhaengt — die Bewegung hat dann nichts mit dem Finger zu tun.
     Mit --n (0 = ganz aussen, 1 = genau in der Mitte) waechst der Nachbar
     GENAU SO WEIT, wie man ihn hereinzieht. Das ist der ganze Trick, und er
     kostet nichts: es bleiben transform und opacity, also reine
     Compositor-Arbeit — kein filter, kein Neuzeichnen.
     Die Klasse setzt hero.js. Ohne Skript und bei abbestellter Bewegung
     greifen weiter die zwei Zustaende darueber. */
  .case__paar.ist-fein .projekt{
    transition:none;
    /* KEIN transform AM PROJEKT (26.09.2026). Es ist der Schnapppunkt, und
       Chrome rechnet dessen Transform in die Schnapplage ein. Verkleinert
       werden in der feinen Fassung Karte und Messleiste darin, um dieselbe
       Ecke (hero.js, feinstellen). Das hier schaltet die Zwei-Zustaende-
       Verkleinerung von oben ab, die sonst zusaetzlich um die Mitte zoege. */
    transform:none;
    /* Die Karten bekommen eigene Ebenen, damit der Browser beim Wischen nur
       noch verschiebt statt neu zu zeichnen: das Projekt fuer die Deckkraft,
       seine zwei Kinder fuer die Groesse. Flaeche und Speicher bleiben
       dieselben wie vorher mit einer Ebene je Projekt, sie sind nur geteilt. */
    will-change:opacity}
  .case__paar.ist-fein .projekt > *{will-change:transform}

  /* ALLES AUF EINER HOEHE — OHNE EINE EINZIGE GEMESSENE ZAHL.
     Hier standen bis eben feste Reserven (Ueberschrift 2.32em, Eyebrow 2.72em,
     Beschreibung 15.5em, Liste 9em), bei 375px sauber ausgemessen. Sie waren
     falsch, und zwar aus einem Grund, der sich nicht nachbessern laesst: der
     Bereich reicht bis 700px. Dort ist die Karte mehr als doppelt so breit,
     die Beschreibung braucht vier statt zehn Zeilen — und die Reserve haette
     ueber 150px Weissraum in die Karte gestellt. Genau das Loch zwischen Text
     und Aufzaehlung, das im Bild zu sehen war.
     subgrid loest das an der Wurzel: die Spur haelt sieben Zeilen, jedes
     Projekt reicht sie durch bis zu den einzelnen Textbausteinen. Jede Zeile
     ist so hoch wie ihr hoechster Inhalt ueber alle drei Karten — bei jeder
     Breite neu und von allein. Eyebrow, Ueberschrift, Beschreibung,
     Aufzaehlung, Link und die beiden Sockelzeilen stehen damit auf einer
     Hoehe, ohne dass irgendwo eine Zahl steht, die jemand pflegen muss.
     Die Kette muss lueckenlos sein — jede Ebene dazwischen reicht die Zeilen
     weiter, sonst reisst sie ab:
       .case__paar  7 Zeilen
       .projekt     alle 7
       .case        1-5      .mess   6-7
       .case__body  1-5
       .case__kopf  1-3
       > div        1-2      (Eyebrow + Ueberschrift) */
  .projekt > .case{grid-row:span 5;display:grid;grid-template-rows:subgrid}
  .projekt > .mess{grid-row:span 2;display:grid;grid-template-rows:subgrid}
  .projekt .case__body{grid-row:span 5;display:grid;grid-template-rows:subgrid}
  .projekt .case__kopf{grid-row:span 3;display:grid;grid-template-rows:subgrid}
  .projekt .case__kopf > div{grid-row:span 2;display:grid;grid-template-rows:subgrid}

  /* "Live ansehen" haengt an der Liste, nicht am Kastenboden. Vorher schob
     margin-top:auto (Regel aus der Desktopfassung) ihn ganz nach unten und
     liess darueber 44px stehen. */
  .projekt .case__points li:last-child{margin-bottom:0}
  .projekt .case__links{margin:18px 0 0}

  /* KEINE order-Vertauschung mehr. Solange die mittlere Karte im Bild stand,
     musste Reifenservice per order dorthin geschoben werden. Jetzt beginnt die
     Spur vorn, und vorn steht im Markup ohnehin Reifenservice — die Anzeige
     folgt also wieder der Dokumentreihenfolge:
       Reifenservice · Kegema · Friseursalon IBO
     Dieselbe Folge wie in den Spalten auf Desktop. Nebenbei ist damit auch die
     Tabreihenfolge wieder die sichtbare; das war vorher der Preis fuer order. */

  /* MEHR DURCHSCHUSS BEI KLEINEREM GRAD. Die Ueberschrift laeuft auf 1.05 —
     richtig bei den 35px, die sie auf Desktop hat, zu eng bei den 25,6px hier.
     Je kleiner der Grad, desto mehr Zeilenabstand braucht er anteilig; 1.05
     ist ein Wert fuer Display-Groessen, nicht fuer alles.
     Dazu Luft zwischen Ueberschrift und Beschreibung: breit schafft die
     Zeilenreserve (min-height) den Abstand nebenbei, mobil gibt es sie nicht,
     und der Text klebte direkt unter dem Titel. */
  .projekt .case__body h3{font-size:1.45rem;line-height:1.14;margin-bottom:8px}
  .projekt .case__tag{margin-bottom:8px}
  .projekt .case__points{margin:16px 0 0}

  /* AUF EINEN BILDSCHIRM.
     Gemessen bei 375x812: die Spur brauchte 776px, die Punkte darunter noch
     44px — zusammen 820px gegen 748px, die neben der 64px hohen Leiste
     bleiben. Auf einem echten Handy ist es enger, weil die Adressleiste beim
     Scrollen ein- und ausfaehrt.
     Die Hoehe steckte in vier Bloecken (Beschreibung 214, Aufzaehlung 186,
     Sockel 194, Ueberschrift 69). Genommen wird aus allen vieren ein Stueck,
     nicht alles aus einem — ein einzelner Block muesste sonst so weit
     schrumpfen, dass man es ihm ansieht.
     Der Fliesstext bleibt bei 16px und 1.5 Durchschuss: das ist Lesegroesse,
     darunter faengt es an, billig auszusehen. Gespart wird ueber den
     Zeilenabstand und die Abstaende zwischen den Bloecken, nicht ueber den
     Schriftgrad des Textes. */
  /* .case__body DAZWISCHEN, nicht aus Gewohnheit: die Grundregel in Zeile
     1369 ist ".case__body p:not(.case__links):not(.case__tag)" und kommt auf
     (0,3,1), weil ":not()" die Spezifitaet seines Arguments erbt. Ein
     ".projekt .case__kopf > p" (0,2,1) verliert dagegen — die Regel stand hier
     kurz und tat nichts, die Beschreibung blieb bei 16,96px. Mit .case__body
     sind es ebenfalls (0,3,1), und bei Gleichstand gewinnt die spaetere. */
  .projekt .case__body .case__kopf > p{font-size:1rem;line-height:1.5}
  .projekt .case__points li{font-size:.95rem;line-height:1.35;margin-bottom:10px}
  /* top folgt der ZEILE, nicht der Schriftgroesse: (Zeilenhoehe - 2) / 2.
     15,2px mal 1,35 sind 20,52px Zeile, also (20,52-2)/2 = 9,26 -> 9px.
     Hier standen 7px, das hob die Marke um 2,3px ueber die Zeilenmitte -
     bei 20px Zeile gut ein Zehntel und im Auge zu sehen. Wer Zeilenhoehe
     oder Schriftgroesse hier anfasst, rechnet top neu. */
  .projekt .case__points li::before{top:9px}

  /* "LIVE ANSEHEN" BRAUCHT LUFT NACH UNTEN.
     Der Link stand mit 14px Abstand nach oben und NULL nach unten — direkt
     auf der Haarlinie, mit der der Sockel beginnt. Alles passte, sauber sah
     es trotzdem nicht aus: ein Link, der auf einer Linie aufsitzt, liest sich
     als Teil des Kastens darunter statt als Abschluss des Textes darueber.
     Unten mehr als oben, und zwar mit Absicht: der Link GEHOERT zum Text ueber
     ihm. Naeher an dem, wozu er gehoert, weiter weg von dem, was danach
     kommt. */
  .projekt .case__links{margin:14px 0 20px}
  /* Der Fliesstext bleibt bei 1.55 aus der Grundregel. Hier stand kurz ein
     1.6 — wirkungslos: ":not()" erbt die Spezifitaet seines Arguments, die
     Grundregel ".case__body p:not(.case__links):not(.case__tag)" kommt damit
     auf (0,3,1) und schlaegt ein ".projekt .case__kopf > p" (0,2,1). 1.55 ist
     fuer Fliesstext ohnehin richtig, deshalb keine Regel statt einer, die
     nichts tut. */

  /* INHALT MITTIG. Anders als auf Desktop, und mit Absicht: hier ist die
     Karte kein Lesetext in einer Spalte, sondern eine FOLIE in einem Stapel,
     die einzeln im Bild steht — Ueberschrift, vier Zeilen, vier Punkte, eine
     Zahlenreihe. Fuer diese Form ist Mittigsatz das richtige Mittel.
     Der uebliche Einwand gegen mittigen Fliesstext trifft hier nicht: die
     AUFZAEHLUNG bleibt innen linksbuendig (inline-block mit text-align:left),
     ihre Striche stehen also weiter sauber untereinander. Ausgerichtet wird
     nur der Block als ganzer.
     Historisch kam der Mittigsatz aus einem anderen Grund — linksbuendig sah
     man im rechten Nachbarstreifen Zeilenanfaenge, im linken leere Flaeche.
     Das erledigt inzwischen die Maske; geblieben ist er, weil er fuer sich
     genommen besser aussieht. */
  .projekt{text-align:center}
  .projekt .case__points{display:inline-block;text-align:left;margin-inline:auto}
  .projekt .case__links{justify-content:center}
  .projekt .mess__werte li{padding:0 6px}
  .projekt .mess__fuss{flex-direction:column;align-items:center;gap:6px}

  /* Der Sockel ist der dritte Block, aus dem Hoehe kommt. Die Zahlen bleiben
     gross genug, um die Aussage zu tragen — 21px statt 24px liest sich immer
     noch als Kennzahl und nicht als Fussnote. */
  .projekt .mess{padding-top:10px}
  .projekt .mess__werte{row-gap:12px}
  .projekt .mess__werte b{font-size:1.32rem}
  .projekt .mess__fuss{margin-top:8px}

  /* KURZE BILDSCHIRME: NOCH EINE STUFE ENGER.
     Die Klemme ist, dass ein SCHMALES Geraet MEHR Hoehe braucht (der Text
     bricht oefter um) und WENIGER hat. Gemessen:

       375 x 812   gebraucht 689, verfuegbar 748   ->  59 uebrig
       360 x 740   gebraucht 749, verfuegbar 676   ->  73 zu wenig

     Deshalb haengt diese Stufe an der HOEHE, nicht an der Breite — die Breite
     ist nicht das Problem, das Problem ist der Platz nach unten. Geraete mit
     genug Hoehe bekommen davon nichts ab und behalten den groesseren Grad.
     Auf einem kurzen Schirm ist das Geraet ohnehin kleiner; der Text wird
     kleiner in CSS-Pixeln, physisch bleibt er vergleichbar. */
  @media (max-height:780px){
    .projekt .case__body h3{font-size:1.3rem}
    .projekt .case__body .case__kopf > p{font-size:.95rem;line-height:1.4}
    .projekt .case__points li{font-size:.9rem;line-height:1.3;margin-bottom:7px}
    /* 14,4px mal 1,3 sind 18,72px Zeile, also (18,72-2)/2 = 8,36 -> 8px.
       Vorher 6px, dieselbe Verschiebung nach oben wie eine Stufe hoeher. */
    .projekt .case__points li::before{top:8px}
    .projekt .case__tag{margin-bottom:6px}
    .projekt .case__body h3{margin-bottom:6px}
    .projekt .case__points{margin-top:10px}
    /* Die Luft unter dem Link bleibt auch hier stehen — sie ist der Grund,
       warum die Karte sauber aussieht, und nicht die Stelle zum Sparen. */
    .projekt .case__links{margin:10px 0 16px}
    .projekt .mess{padding-top:6px}
    .projekt .mess__werte{row-gap:4px}
    .projekt .mess__werte b{font-size:1.2rem}
    .projekt .mess__fuss{margin-top:2px}
    .cases .case__paar{padding-top:8px;padding-bottom:10px}
    .cases .messgruppe__punkte{margin-top:2px}
  }

  /* Punkte: Stand der Spur. Gewischt wird auf der Karte; sie sind trotzdem
     anklickbar, weil sie sonst aussehen wie Knoepfe, die nicht gehen. */
  .cases .messgruppe__punkte{display:flex;justify-content:center;gap:8px;margin-top:8px}
  .messgruppe__punkte button{
    width:28px;height:28px;padding:0;border:0;background:none;
    display:grid;place-items:center;cursor:pointer}
  .messgruppe__punkte button::before{
    content:"";width:7px;height:7px;border-radius:50%;
    background:var(--line);transition:background .18s ease,transform .18s ease}
  .messgruppe__punkte button[aria-current="true"]::before{
    background:var(--cyan-ink);transform:scale(1.25)}
  .messgruppe__punkte button:focus-visible{outline:2.5px solid var(--cyan-ink);outline-offset:2px;border-radius:50%}

  @media(prefers-reduced-motion:reduce){
    .projekt{transform:none;opacity:1;transition:none}
    .messgruppe__punkte button::before{transition:none}
  }
}

/* Vier Werte nebeneinander brauchen vier Beschriftungen nebeneinander, und
   „Barrierefreiheit“ allein ist auf 12,8px rund 92px breit. Mit 2x10px Polster
   je Feld braucht eine Vierer-Reihe damit rund 450px. Darunter zwei mal zwei.
   Der Trenner sitzt dann nur zwischen den beiden Spalten, nicht vor jedem Feld.
   Nachgemessen liegt die Leiste so auf 1280 und 1440px vierspaltig (479 und
   490px), in der zweispaltigen Referenzansicht zwischen 901 und 1279px
   zweispaltig (337 bis 376px), auf dem Tablet wieder vierspaltig (691px) und
   auf dem Handy zweispaltig (316 bis 346px). */
@container (max-width:449px){
  .mess__werte{grid-template-columns:repeat(2,1fr);row-gap:18px}
  .mess__werte li:nth-child(odd){padding-left:0;border-left:0}
  .mess__werte li:nth-child(even){padding-right:0;border-left:1px solid var(--line)}
}
/* Ohne Container-Queries greift die Abfrage oben nicht und es blieben ueberall
   vier Spalten — auf dem Handy waeren die Beschriftungen dann zweizeilig in
   86px schmalen Feldern. Dann lieber die Fensterbreite als grober Ersatz. */
@supports not (container-type:inline-size){
  @media(max-width:520px){
    .mess__werte{grid-template-columns:repeat(2,1fr);row-gap:18px}
    .mess__werte li:nth-child(odd){padding-left:0;border-left:0}
    .mess__werte li:nth-child(even){padding-right:0;border-left:1px solid var(--line)}
  }
}

.case__points{margin:20px 0 22px;padding:0;list-style:none}
/* MARKE IST EINE HAARLINIE, keine gefuellte Raute. Vorher stand hier ein um
   45 Grad gekipptes Quadrat auf 9px in vollem Cyan — das war nach dem Umbau
   der Haekchen das letzte gefuellte Zeichen auf der Seite und damit das
   schwerste. Diese Seite markiert sonst mit 2px-Linien: die Oberkanten der
   Karten, der Anschlag ueber dem Takt, die Marke am aktiven Schienenfeld.
   Eine Linie sagt hier ausserdem das Richtige. Ein Haken hiesse „zugesagt“
   (den tragen die Zusagen im Hero und beim Preis), ein Punkt hiesse
   „Aufzaehlung“ (den tragen die Ortsnamen). Diese drei Zeilen sind weder das
   eine noch das andere, sondern Merkmale einer gebauten Seite — markiert,
   nicht versprochen.
   top ist GEMESSEN, nicht gerechnet: die Zeilenhoehe steht auf `normal`, also
   haengt sie an der Schrift und nicht an einer Zahl im CSS. Eine unsichtbare
   Messmarke im Textfluss ergab die Mitte der Grossbuchstaben bei 10,5px —
   abzueglich der halben Strichstaerke sind das 0,59em. Mit dem gerechneten
   Wert (0,75em aus Zeilenhoehe 1,5) sass der Strich 2,5px zu tief. */
/* GANZE PIXEL, sonst sehen die Striche unterschiedlich dick aus.
   line-height stand auf "normal", bei 16px Schrift also rund 20,8px. Mit dem
   10px-Abstand darunter stapelten sich die Punkte auf krumme Y-Positionen
   (gemessen 1469,55 / 1521,15 / 1551,95). Die Marke ist eine 2px-Linie: liegt
   sie auf einem halben Pixel, verteilt der Browser sie auf zwei Zeilen und
   zeichnet beide halbtransparent — sie wirkt duenner und blasser als die
   Nachbarin, die zufaellig glatt aufsetzt. Rechnerisch waren alle neun immer
   identisch (12x2px, gleiche Farbe, gleicher Versatz), es war reine
   Rasterung.
   1.25 x 16px = genau 20px. Der Abstand darunter ist 12px, nicht 10 — Zeile
   plus Abstand ergibt 32px, und DARAUF kommt es an:

   Der erste Anlauf rechnete in CSS-Pixeln und war trotzdem falsch, weil am
   Ende das GERAETEPIXEL zaehlt. Auf diesem Rechner laeuft Windows mit 125%,
   devicePixelRatio also 1.25. Mit 30px Rhythmus:

       30 CSS-px x 1.25 = 37,5 Geraetepixel

   Keine ganze Zahl. Jede zweite Marke landete deshalb auf einem anderen
   Subpixel — gemessen wechselten die Reste .58 / .08 / .58 ab, und genau das
   sieht man: abwechselnd dicker und duenner.

   32px gehen bei allen ueblichen Skalierungen glatt auf:
       1,00 -> 32    1,25 -> 40    1,50 -> 48    1,75 -> 56    2,00 -> 64

   Damit hat jede Marke denselben Subpixel-Versatz und wird identisch
   gerastert. Absolut glatt sitzen sie deswegen nicht — der Rest, den die
   Seite oberhalb mitbringt, bleibt. Aber er ist fuer alle neun GLEICH, und
   nur das sieht man.

   Die Hoehe von 2px ist bei 1.25 ebenfalls krumm (2,5 Geraetepixel). Das
   laesst sich nicht fuer jede Skalierung zugleich loesen: 1.6px waere bei
   1.25 glatt und bei 1.0 krumm. Weil alle neun jetzt gleich aufsetzen, werden
   sie auch alle gleich weichgezeichnet — und ungleich war die Beschwerde,
   nicht weich. */
.case__points li{position:relative;padding-left:22px;margin-bottom:12px;color:var(--ink);font-size:1rem;line-height:1.25}
/* top in px statt em: .59em ergab 9,44px und haette die Marke auch bei
   glatter Zeile wieder auf einen halben Pixel gesetzt. 9px ist die Mitte der
   20px-Zeile ((20-2)/2) und optisch derselbe Punkt. */
.case__points li::before{content:"";position:absolute;left:0;top:9px;
  width:12px;height:2px;background:var(--cyan)}

/* ---- Ablauf ---- */
.ablauf{padding:clamp(52px,7vw,100px) var(--page);background:var(--paper)}
.steps{position:relative;display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(20px,2.6vw,34px);max-width:var(--wrap);margin:0 auto;padding:0;list-style:none}
.step{position:relative;padding-top:22px;border-top:2px solid var(--line)}
.step::before{content:"";position:absolute;top:-2px;left:0;width:42px;height:2px;background:var(--cyan)}
/* Einzeilig (Desktop): die vier Schritte teilen sich EINE Zeitleiste — graue Spur,
   Cyan-Fortschritt waechst beim Scrollen mit (--p aus JS), Kerben trennen die Stationen.
   So liest es sich als Ablauf mit vier Etappen statt als Ladebalken. */
@media(min-width:901px){
  /* KEIN padding-top auf .steps: die Leiste liegt bei top:0 der Padding-Box, die
     Kerben bei top:0 ihres Schritts — jedes Padding verschiebt beide gegeneinander. */
  .step{border-top:0;padding-top:28px}
  .steps::after{content:"";position:absolute;top:0;left:0;right:0;height:2px;background:var(--line);z-index:0}
  .steps::before{content:"";position:absolute;top:0;left:0;right:0;height:2px;background:var(--cyan);z-index:1;
    transform-origin:left center;transform:scaleX(var(--p,0));will-change:transform}
  /* Kerbe in Sektionsfarbe schneidet die Leiste vor jeder Station auf.
     WICHTIG: setzt die 42px-Cyan-Basisregel zurueck — sonst bleibt bei Schritt 1
     ein Stummel ueber der leeren Spur stehen. */
  .step::before{content:"";display:block;position:absolute;top:0;left:calc(-1 * clamp(20px,2.6vw,34px) / 2 - 4px);
    width:8px;height:2px;background:var(--paper);z-index:2}
  .step:first-child::before{content:none}
  .step__no{position:relative;display:inline-block}
}
@media(min-width:901px) and (prefers-reduced-motion:reduce){
  .steps::before{transform:scaleX(1)}
}
.step__no{font-family:"Geist Mono",monospace;font-size:.92rem;font-weight:700;color:var(--cyan-ink)}
.step h3{margin:12px 0 10px;font-size:1.3rem;letter-spacing:-.015em}
.step p{margin:0;color:var(--ink-soft);line-height:1.5;font-size:1rem}

/* ---- Umfang ---- */
.umfang{padding:clamp(52px,7vw,100px) var(--page);background:var(--paper)}
.umfang__grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;max-width:var(--wrap);margin:0 auto;
  background:var(--line);border:1px solid var(--line);border-radius:0;overflow:hidden}
.feat{position:relative;background:var(--white);padding:28px 26px;transition:background .2s ease}
.feat:hover{background:#fbfdff}
/* Icons in der Linien-Sprache der Hero-Illustration (gleiche Strichstaerke, gleiche Farbe)
   — schlaegt die Bruecke zurueck in die Raum-Welt, ohne sie zu wiederholen. */
.feat__ico{display:block;width:26px;height:26px;margin:0 0 14px;color:#8397ab;transition:color .2s ease}
.feat:hover .feat__ico{color:var(--cyan-ink)}
.feat__ico svg,.treiber__ico svg,.zusage__ico svg{
  display:block;width:100%;height:100%;fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round}
.feat h3{margin:0 0 10px;font-size:1.16rem;letter-spacing:-.01em}
.feat p{margin:0;color:var(--ink-soft);line-height:1.5;font-size:.98rem}

/* ---- Pair: EINE Aussage, keine zwei -------------------------------------
   Hier standen zwei Kaesten nebeneinander. Der zweite („Gefunden werden —
   Damit Kunden dich in deiner Region finden.“) ist raus, und zwar aus zwei
   nachpruefbaren Gruenden:
   1. Er hat eine Aussage wiederholt, die 400px vorher schon steht. Im
      Umfang-Raster: „Google-Unternehmensprofil — Gepflegt und optimiert,
      damit Kunden dich in der Region finden.“ Im Kasten: „… ein gepflegtes
      Google-Unternehmensprofil — die Basis, damit Menschen dich finden, wenn
      sie suchen.“ Derselbe Beleg, fast derselbe Satz.
   2. Seine Ueberschrift stand unmittelbar ueber der Regionen-Sektion
      („Aus der Region, fuer die Region.“) — zwei Region-Ueberschriften
      hintereinander, und die untere ist die, die den Abschnitt traegt.
   Was bleibt, ist eine einzelne Aussage, mittig und auf Lesebreite begrenzt.
   Zwei Kaesten waren ein weiteres Raster; einer ist eine Pause zwischen dem
   Umfang und der dunklen Regionen-Sektion — und die tut der Strecke gut. */
.pair{max-width:var(--wrap);margin:0 auto;
  padding:clamp(24px,4vw,44px) var(--page) clamp(52px,7vw,88px)}
/* 760px sind rund 75 Zeichen je Zeile. Ueber die volle Rasterbreite (1180px)
   waeren es rund 115 — doppelt so viel, wie sich angenehm liest. */
.pair__card{max-width:760px;margin:0 auto;
  padding:clamp(28px,3vw,40px);border-radius:0;border:1px solid var(--line);background:var(--white)}
.pair__card h3{margin:8px 0 12px;font-size:clamp(1.3rem,2vw,1.55rem);letter-spacing:-.02em}
.pair__card p{margin:0;color:var(--ink-soft);line-height:1.55}

/* ---- Preis: was den Preis bestimmt + Festpreis-Zusage ---- */
.preis{padding:clamp(52px,7vw,100px) var(--page);background:var(--white)}
.preis__grid{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(18px,2.4vw,26px);max-width:var(--wrap);margin:0 auto}
/* SPALTE, damit die blaue Frage unten andocken kann. Als reiner Block folgte
   sie direkt dem Text — und weil die Texte drei oder vier Zeilen lang sind,
   stand sie in jeder Karte woanders. Die Karten selbst sind schon gleich hoch,
   das erledigt das Raster; nur ihr Inhalt lief oben zusammen und unten
   auseinander. */
.treiber{display:flex;flex-direction:column;
  padding:clamp(22px,2.2vw,28px);border:1px solid var(--line);border-radius:0;background:var(--white)}
/* Ein Icon je Karte, IN der Ueberschriftenzeile. Der Abschnitt trug sechs
   gleich gebaute Textkarten am Stueck: inhaltlich richtig, optisch eine Wand.
   Das Icon gibt jeder Karte einen Ankerpunkt oben links, an dem das Auge die
   Frage erkennt, bevor es den Absatz liest.
   Bewusst in der Zeile statt darueber (so macht es .feat weiter unten): so
   wird keine Karte hoeher - der Abschnitt soll kuerzer wirken, nicht laenger -
   und das Preis-Raster bleibt vom Leistungs-Raster 600px spaeter unterscheidbar.
   line-height fest auf 1.3 statt normal, damit die Zeilenhoehe berechenbar ist:
   1.3 x 1.1rem = 22.9px, das Icon 21px, der Rest 1px Versatz. Ohne festen Wert
   haengt die Ausrichtung an der Schrift, die gerade geladen ist. */
.treiber h3{margin:0 0 9px;font-size:1.1rem;letter-spacing:-.01em;
  display:flex;align-items:flex-start;gap:11px;line-height:1.3}
/* flex-start statt center: bricht die Frage auf zwei Zeilen um, bleibt das
   Icon an der ersten stehen - center wuerde es zwischen beide schieben. */
.treiber__ico{flex:0 0 auto;width:21px;height:21px;margin-top:1px;
  color:#8397ab;transition:color .2s ease}
.treiber:hover .treiber__ico{color:var(--cyan-ink)}
.treiber p{margin:0;color:var(--ink-soft);line-height:1.5;font-size:.97rem}
/* margin-top:auto schiebt die Frage an den unteren Rand der Karte; damit
   stehen alle Fragen einer Reihe auf einer Linie. Der Abstand nach oben liegt
   jetzt im padding statt im margin — ein margin waere von der automatischen
   Verteilung geschluckt worden und die laengste Karte haette ihren Abstand
   verloren.
   Einspaltig gibt es keinen freien Platz zu verteilen; dort wirkt auto wie 0
   und es bleiben die 10px. */
.treiber__frage{display:block;margin-top:auto;padding-top:10px;
  color:var(--cyan-ink);font-size:.9rem;font-weight:620}
/* Zusage: der Festpreis als Prozess-Versprechen (keine Zahl, aber verbindlich) */
.zusage{max-width:var(--wrap);margin:clamp(26px,3.2vw,40px) auto 0;padding:clamp(28px,3vw,42px);
  border-radius:0;background:var(--cyan-soft);border:1px solid #cbeefb;
  display:grid;grid-template-columns:1fr 1.15fr;gap:clamp(22px,3vw,44px);align-items:center}
/* Das Preisschild steht ueber der Ueberschrift, nicht in ihr: die Zusage ist
   der Schlusspunkt des Rasters und traegt eine zweizeilige Ueberschrift in
   1.65rem - dort wirkt ein Icon in der Zeile gedrungen. --cyan-ink statt des
   grauen #8397ab, weil das Grau auf dem cyanen Kastengrund abfaellt. */
.zusage__ico{display:block;width:26px;height:26px;margin:0 0 14px;color:var(--cyan-ink)}
.zusage h3{margin:0 0 10px;font-size:clamp(1.3rem,2.1vw,1.65rem);letter-spacing:-.02em}
.zusage > div > p{margin:0;color:var(--ink-soft);line-height:1.55}
.zusage__punkte{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:13px}
.zusage__punkte li{position:relative;padding-left:26px;color:var(--ink);font-size:1rem;line-height:1.45}
/* Dieselbe Form, eine Nummer groesser — hier steht Fliesstext auf 1rem, nicht
   .9rem. Die Maske kommt aus --haken weiter oben, es gibt sie nur einmal. */
.zusage__punkte li::before{content:"";position:absolute;left:0;top:.16em;
  width:17px;height:17px;background:var(--cyan);
  -webkit-mask:var(--haken) 0 0/17px 17px no-repeat;
          mask:var(--haken) 0 0/17px 17px no-repeat}
/* Ohne Maskenunterstuetzung waere der Haken eine cyane Flaeche. Dann lieber die
   Form, die die Referenzpunkte dieser Seite ohnehin benutzen: ein gekipptes
   Quadrat. Sagt dasselbe und ist auf jeden Fall kein Klotz. */
@supports not ((mask-image:none) or (-webkit-mask-image:none)){
  .trustmarks li::before,.zusage__punkte li::before{
    width:9px;height:9px;border-radius:2px;transform:rotate(45deg);
    -webkit-mask:none;mask:none}
  .trustmarks li::before{top:.42em}
  .zusage__punkte li::before{top:.4em;left:3px}
}
.zusage__punkte strong{font-weight:680}
.preis__laufend{max-width:var(--wrap);margin:clamp(20px,2.4vw,28px) auto 0;text-align:center;color:var(--muted);font-size:.98rem}
.preis__laufend b{color:var(--ink);font-weight:660}

/* =====================================================================
   KARTEN-SPRACHE
   Runde Boxen sind der Standard jedes Baukastens — die Kanten sind deshalb
   ueberall scharf (border-radius:0 steht jeweils an der Quelle der Regel).
   Eine angeschnittene Ecke mit cyaner Schnittkante war hier zwischenzeitlich
   drin und ist bewusst wieder raus: sie hat jede Karte verziert, statt eine
   Struktur zu bauen.
   Der Akzent liegt jetzt an EINER Stelle — die Branchen-Karten sind oben und
   unten offen und stehen auf einer dunklen Kante (s. .branche / .branche::after).
   Alle anderen Flaechen bleiben ruhige scharfe Rahmen. Das ist Hierarchie, keine
   zweite Sprache: eine Sektion traegt den Effekt, der Rest traegt den Inhalt.
   REGEL: Flaechen scharf, Bedienelemente (Buttons, Chips, Geraetetasten) rund —
   die Rundung ist dort Aufforderung zum Antippen.
   ===================================================================== */
.branchen{padding:clamp(52px,7vw,100px) var(--page);background:var(--white)}
/* Zeilenabstand groesser als der Spaltenabstand: die Standfuesse brauchen den
   Platz, sonst stoesst der Fuss der oberen Reihe an die Karte darunter. */
.branchen__grid{display:grid;grid-template-columns:repeat(2,1fr);
  column-gap:clamp(18px,2.4vw,26px);row-gap:clamp(46px,4.4vw,58px);
  max-width:var(--wrap);margin:0 auto}
/* OBEN UND UNTEN OFFEN: kein Kasten mehr, sondern zwei senkrechte Haarlinien als
   Kanal, der unten auf der dunklen Standkante endet (s. .branche::after). */
.branche{position:relative;--karte-pad:clamp(26px,2.6vw,34px);padding:var(--karte-pad);
  /* min-width:0 ist hier PFLICHT, nicht Kosmetik: die Karte ist ein
     Rasterelement und hat damit von Haus aus min-width:auto -- sie weigert
     sich, schmaler zu werden als ihr Inhalt. Mit der nicht umbrechenden
     Chip-Reihe darin hat das die Spalte gemessen auf 811px aufgezogen und
     470px waagerechten Ueberlauf erzeugt. */
  min-width:0;
  border:0 solid var(--line);border-width:0 1px;
  border-radius:0;background:var(--white);
  transition:border-color .18s ease}
/* Hover: die Standkante wird kraeftiger. Der frueher hier stehende Lift-Schatten
   passt nicht mehr — ein Schatten umrundet eine geschlossene Flaeche, und diese
   Karte hat keine Unterkante. Ein Farbwechsel auf zwei Haarlinien waere zu leise. */
.branche:hover::after{height:3px}
/* STANDKANTE — die Karte steht auf einer Linie, statt auf hellem Grund zu
   schweben. Gleicher Ton wie die Tischkante im Hero (--navy), also derselbe
   Tisch und kein zweites Formenvokabular.
   Nichts verbindet Karte und Linie: die offenen Kanten und die offene Luecke
   sind der Effekt. Ein Steg dazwischen wuerde die Form wieder schliessen.
   left/right mit 1px Uebermass, weil absolute Offsets ab der Padding-Box zaehlen
   — sonst waere die Linie 2px schmaler als die Karte. */
.branche::after{content:"";position:absolute;left:-1px;right:-1px;bottom:-20px;height:2px;
  pointer-events:none;background:var(--bra,var(--navy));transition:height .18s ease}
@media(prefers-reduced-motion:reduce){
  .prop--mug.is-wink{animation:none}
  .branche,.branche::after{transition:none}
}
.branche h3{margin:0 0 10px;font-size:clamp(1.2rem,1.9vw,1.45rem);letter-spacing:-.015em}
.branche > p{margin:0 0 18px;color:var(--ink-soft);line-height:1.55;font-size:1rem}
.tags{display:flex;flex-wrap:wrap;gap:7px;margin:0;padding:0;list-style:none}
.tags li{padding:6px 12px;border-radius:8px;background:var(--paper);border:1px solid var(--line);
  color:var(--ink-soft);font-size:.87rem;font-weight:520}

/* ---- Mobil: die Reihe laeuft quer und aus dem Bild ----------------------
   Vorher stapelten sich 5 bis 7 Chips SENKRECHT, weil sie nebeneinander nicht
   passen. Gemessen waren das 195 bis 273px je Karte, mal vier Karten rund
   940px -- fast ein ganzer Bildschirm, nur fuer Wortlisten, und dabei sah es
   aus wie eine Aufzaehlung statt wie ein Angebot.
   Quer sind es 44px je Karte. Und eine Reihe, die rechts aus dem Bild laeuft,
   sagt von selbst „da kommt noch was" -- Bewegung ohne Animation.
   DIE RECHNUNG: die Reihe muss aus ZWEI Polstern heraus, dem der Karte
   (--karte-pad) und dem der Sektion (--page). Negative Marge schiebt den
   Scrollkasten exakt bis zur Bildkante, das gleich grosse padding-right sorgt
   dafuer, dass der letzte Chip am Ende des Scrollwegs nicht an der Kante
   klebt. Weil beide Werte aus denselben Variablen kommen wie die Polster
   selbst, kann die Reihe nie ueber die Kante hinausragen und einen
   waagerechten Ueberlauf erzeugen. */
@media(max-width:700px){
  /* KEINE Scroll-Reihe mehr. Sie war die Antwort auf 5 bis 7 senkrecht
     gestapelte Pillen (gemessen 195 bis 273px je Karte) — aber eine Reihe, die
     an der Bildkante abgeschnitten wird, sieht auf dem Handy nach Fehler aus,
     auch mit Blende. Und Wischen in einer Liste, die man nur LIEST, ist
     Bedienung ohne Nutzen.
     Jetzt sind es keine Pillen mehr, sondern eine Zeile Fliesstext mit
     Trennpunkten. Gerechnet: sechs Begriffe sind rund 90 Zeichen, bei 39
     Zeichen je Zeile also drei Zeilen = etwa 66px. Gestapelt waren es 234px,
     die Scroll-Reihe war 36px. Nichts wird abgeschnitten, nichts muss
     gewischt werden, und die Begriffe bleiben vollstaendig im Text — sie
     tragen ja die Long-Tail-Suchbegriffe. */
  .tags{
    display:block;overflow:visible;margin-right:0;padding-right:0;
    font-size:.92rem;line-height:1.65;color:var(--muted);
    -webkit-mask-image:none;mask-image:none;min-width:0}
  /* Aus Pillen werden Woerter: kein Rahmen, keine Fuellung, kein Kasten.
     Der Trennpunkt steckt im ::after und nicht im Text — so bleibt der
     Wortlaut sauber, wenn jemand die Begriffe kopiert. */
  .tags li,
  .branche[class*="branche--"] .tags li{
    display:inline;padding:0;border:0;background:none;border-radius:0;
    color:inherit;font-size:inherit;font-weight:520}
    /* --line waere hier fast unsichtbar (ein Haarlinien-Grau auf Weiss). Der
     Punkt soll trennen, nicht verschwinden. */
  .tags li:not(:last-child)::after{content:" · ";color:rgba(107,125,143,.55)}
  /* Tastatur: eine scrollbare Flaeche muss erreichbar sein. hero.js setzt
     tabindex nur, wenn wirklich mehr drin steht als hineinpasst. */
  .tags[tabindex]:focus-visible{outline:2.5px solid var(--cyan-ink);outline-offset:4px;border-radius:4px}
}

/* ---- Farbe je Branche -------------------------------------------------
   Sechs Akzente, und sie sind NICHT ausgedacht: Orange ist aus dem Screenshot
   der Reifenservice-Seite gemessen (derselbe Ton traegt auch den Punkt in der
   Tableiste des Heros), Stahlblau aus dem Azur der Kegema-Seite, Magenta aus
   dem Verlauf der Visio-Wortmarke. Fuer die Branchen ohne Referenz kommen Gold,
   Petrol und Gruen dazu; Petrol ist absichtlich ein Verwandter des Seiten-Cyan,
   damit die Reihe wie eine Familie aussieht und nicht wie sechs zufaellige
   Farben.
   Das Gold hing frueher an der GWS-Referenz. Die ist raus (Kegema hat sie
   ersetzt, weil GWS nie auf eigener Domain lief) — die Branche Sicherheit
   bleibt aber, also bleibt auch ihre Farbe.
   Alle Werte sind auf Kontrast geprueft: Linien und Raender liegen ueber 3:1
   auf Weiss, die Schrift ueber 4.5:1 auf ihrer Fuellung. Die Rohtoene der
   Kundenseiten waeren dafuer zu hell gewesen (Kegemas Azur #0088ff kommt auf
   3,52:1 und liegt damit hart an der Grenze) — die Toene hier sind ihre
   abgedunkelten Geschwister.
   Eingesetzt an genau drei Stellen: Anschlag der Standkante, Referenz-Marke,
   Chip-Raender. Nichts grossflaechig gefuellt — vier Farben, die Flaechen
   belegen, waeren Konfetti. */
.branche--kfz      {--bra:#d8541a;--bra-tief:#a34313;--bra-hell:#fdf1ea;--bra-rand:#f0c3a8}
.branche--sicher   {--bra:#b8860b;--bra-tief:#7a5a06;--bra-hell:#fbf5e4;--bra-rand:#e6d29a}
.branche--handwerk {--bra:#0f7b8a;--bra-tief:#0a5c68;--bra-hell:#e8f6f8;--bra-rand:#a9d5dc}
.branche--praxis   {--bra:#2f7d4f;--bra-tief:#235f3c;--bra-hell:#eaf5ee;--bra-rand:#b3d8c2}
.branche--industrie{--bra:#2f6fb0;--bra-tief:#21507f;--bra-hell:#eaf2fa;--bra-rand:#b7cfe8}
.branche--werbetechnik{--bra:#c33a78;--bra-tief:#912a59;--bra-hell:#fbeaf2;--bra-rand:#eec0d6}

/* Die Standkante traegt die Branchenfarbe auf voller Breite.
   Erste Fassung liess sie navy und faerbte nur die ersten ~84px ein. Auf einer
   580px breiten Karte war das ein Siebtel — angesehen las sich das nicht als
   Akzent, sondern wie eine Linie, die nicht fertig gezeichnet wurde. Wenn
   Farbe hier Bedeutung tragen soll (welche Branche gehoert zu welcher Karte),
   dann muss sie auch zu sehen sein.
   Alle vier Toene sind fuer diese Verwendung geprueft: als grafisches Element
   braucht es 3:1 auf Weiss, das Gold liegt mit 3,25:1 am knappsten, Orange bei
   4,04, Petrol 4,98, Magenta 5,00, Gruen 5,04, Stahlblau 5,22. Der Rueckfall bleibt navy, falls eine Karte
   mal ohne Branchenklasse steht. */

/* Chips: getoenter Rand und eine Spur Fuellung. Die Schrift bleibt --ink-soft
   — sie in der Akzentfarbe zu setzen haette bei Gold und Orange den Kontrast
   gekostet, und lesbar geht vor bunt. */
/* Nur ab 701px: darunter sind die Begriffe Fliesstext ohne Kasten, und die
   Fuellung haette dort Woerter mit farbigem Hintergrund ergeben. Diese Regel
   steht spaeter in der Datei als die mobile Umstellung und wuerde sie sonst
   ueberstimmen — gleiche Spezifitaet, spaeter gewinnt. */
@media(min-width:701px){
  .branche[class*="branche--"] .tags li{border-color:var(--bra-rand);background:var(--bra-hell)}
}

/* Abstand ab dem STANDFUSS gerechnet, nicht ab der Kartenkante: der Fuss ragt
   19px unter die Karte, gemessen blieben davor nur 19px zur Notiz. */
.branchen__note{max-width:var(--wrap);margin:clamp(48px,5vw,64px) auto 0;text-align:center;color:var(--muted);font-size:.98rem}


/* ======================================================================
   DUNKLER TAKT — die Pause zwischen zwei Textstrecken
   ======================================================================
   Zweck: nach Preis folgen Umfang und Paar, noch einmal ueber 2.000px in
   derselben hellen Form. Ein lauter, randloser Bildschirm dazwischen setzt
   den Blick zurueck. Er traegt bewusst KEINEN Knopf — der Aufruf steht am
   Seitenende, ein zweiter davor wuerde ihn nur verduennen.

   OHNE FOTO, und das ist eine Entscheidung, keine Luecke. Hier stand eine
   Werkstatt-Aufnahme (werkstatt-abend.webp, zweispaltig, damit sie nicht
   hochskaliert wird). Sie sah zu deutlich nach KI aus — auf einer Seite, die
   von Vertrauen und echten Betrieben handelt, ist ein erkennbar erfundenes
   Bild schaedlicher als gar keins. Bis es eine echte Aufnahme gibt, traegt
   der Takt sich selbst: Flaeche, ein Satz, eine Uhrzeit als Grafik.
   Die Datei liegt weiterhin unter /assets/img und wird von den beiden
   Slice-Prototypen benutzt — nicht loeschen, ohne dort nachzusehen. */
.takt{position:relative;overflow:hidden;background:var(--navy);color:#e8f1fa;
  display:grid;align-items:center;isolation:isolate;
  padding:clamp(52px,7vw,104px) var(--page);
  min-height:clamp(260px,30vw,400px)}
/* Die Uhrzeit ist das, was vom Bild uebrig bleibt: gross, sehr leise, und in
   derselben Mono, die auch die Abschnitts-Schiene und das Mobilmenue
   benutzen. Sie steht hinter dem Satz und sagt dasselbe — deshalb
   aria-hidden, fuer Vorleseprogramme waere sie eine Doppelung.
   Kontrast bewusst unter jeder Lesbarkeitsschwelle: sie ist Flaeche, kein
   Text. Wer sie entziffern will, soll es koennen, wer nur liest, soll sie
   nicht bemerken.

   GANZE ZIFFERN, KOMPLETT INNERHALB DER SEKTION. Vorher lag sie mit
   bottom:-.24em und right:-.06em unter der Kante und wurde von
   overflow:hidden unten und rechts amputiert — angesehen las sich das nicht
   als Absicht, sondern als Unfall. Jetzt sitzt sie auf der Unterkante auf
   (bottom:0) und steht seitlich mit einer Winzigkeit Luft im Bild.
   Ein Zwischenstand liess sie unten aus der Sektion herausragen. Das brauchte
   zwei Farben (Weiss auf 5% ist auf der hellen Sektion unsichtbar, die Zahl
   waere unten einfach weg) und mit negativem right auch 15px waagerechten
   Ueberlauf mit sichtbarer Scrollleiste. Verworfen — beides Aufwand fuer eine
   Zahl, die leise sein soll. */
/* Sie steht STILL auf 22:00 und sitzt auf der Unterkante der Sektion.
   Zwei Zwischenstaende sind wieder ausgebaut, und beide Gruende gehoeren
   hierher, damit sie niemand nachbaut:
   - Sie hat einmal durch die Stunden gezaehlt (Start 22, eine Runde, zurueck
     auf 22). Sah gut aus, hat aber nichts gesagt, was die Zahl nicht schon
     sagt, und war eine dauerhafte Bewegung in einem Abschnitt, der von Ruhe
     lebt.
   - Darunter stand eine Statuszeile („Online · 24/7 · 365 Tage“). Die hat
     denselben Gedanken ein zweites Mal ausgesprochen, den der Satz daneben
     ohnehin macht.
   Was bleibt, ist die Zahl als Flaeche: gross, sehr leise, ganze Ziffern,
   aufsitzend auf der Kante. 22:00 ist die Stunde, die der Satz nennt — Zahl
   und Wortlaut sagen dasselbe, und mehr muss dort nicht passieren. */
.takt__uhr{position:absolute;z-index:0;right:-.06em;bottom:-.24em;
  font:700 clamp(6.5rem,20vw,16rem)/.78 "Geist Mono",ui-monospace,monospace;
  letter-spacing:-.045em;color:rgba(255,255,255,.05);
  pointer-events:none;-webkit-user-select:none;user-select:none;white-space:nowrap}

.takt__wort{position:relative;z-index:1;max-width:var(--wrap);margin:0 auto;width:100%}
.takt__zeile{margin:0;position:relative;padding-top:22px;max-width:22ch;
  font-size:clamp(1.45rem,3.1vw,2.5rem);line-height:1.18;letter-spacing:-.02em;font-weight:760}
/* Haarlinie in Cyan ueber dem Satz: dasselbe Zeichen, das auf der hellen
   Seite die Karten markiert — hier ist es der Anschlag des Taktes. */
.takt__zeile::before{content:"";position:absolute;top:0;left:0;width:44px;height:2px;background:var(--cyan)}
/* text-wrap:balance verteilt die Zeilen gleichmaessig, statt die letzte als
   Rest stehen zu lassen — ohne das stand „auch." allein auf einer Zeile.
   Aeltere Browser ignorieren die Zeile und brechen wie bisher, kein Rueckfall
   noetig. */
/* Nur die direkten Kinder werden Bloecke. Der Kindselektor stammt aus einer
   Fassung, in der die Tageszeit in einem eigenen span steckte und sonst mit
   umgebrochen waere; er bleibt, weil er ohnehin genauer ist. */
.takt__zeile > span,.takt__zeile > b{display:block;text-wrap:balance}
/* Der Abschnitt hatte zwischenzeitlich zwei bewegte Sachen: die Tageszeit im
   Satz wechselte mit der Uhr (22 abends um zehn, 6 morgens um sechs, 12 mittags
   um zwoelf), und darunter stand eine Statuszeile mit gruenem Punkt. Beides ist
   wieder raus, und der Grund gehoert hierher, damit es niemand nachbaut:
   Text, der sich aendert waehrend man ihn liest, ist die einzige Bewegung, die
   das Verstehen unterbricht — wer mitten im Satz erwischt wird, liest neu und
   haelt es fuer einen Fehler. Und die Statuszeile hat den Gedanken des Satzes
   ein zweites Mal ausgesprochen, statt ihn zu tragen.
   Der Abschnitt ist jetzt, was er sein soll: eine Flaeche, ein Satz, eine
   Uhrzeit. Nichts davon bewegt sich. */
.takt__zeile span{color:#8fa6bd;font-weight:600}
.takt__zeile b{color:#fff;font-weight:760}

@media(max-width:700px){
  .takt__zeile{max-width:none}
  /* Auf dem Handy ist die Uhrzeit kleiner und noch leiser: die Zeilen laufen
     dort ueber die ganze Breite und wuerden sie sonst kreuzen. Der Anschnitt
     ist eine Spur tiefer als auf dem Desktop, weil die Zahl im Verhaeltnis
     zur Sektionshoehe groesser ist. */
  .takt__uhr{font-size:clamp(5rem,34vw,9rem);bottom:-.28em;right:-.04em;
    color:rgba(255,255,255,.042)}
}

/* ---- Regionen (lokale Signale) ---- */
/* --fuge steht ganz oben, weil ZWEI Sachen exakt dieselbe Zahl brauchen: die
   Fugen des Kartenrasters und die Luecken in der Maske des Abschlussbands.
   Zweimal dasselbe clamp() haette beim naechsten Anfassen um einen Pixel
   auseinandergelaufen — und dann steht das Band unter den Karten schief. */
/* ---- HELL, und das ist eine Ruecknahme mit Begruendung ----------------
   Diese Sektion war am 28.07. kurzzeitig dunkel (navy), um eine gemessene
   Strecke von 3.400px ohne dunklen Anker aufzubrechen. Wieder zurueck, und
   der Grund gehoert hierher, damit es niemand als „gute Idee“ neu vorschlaegt:

   1. DIE DUNKELHEIT WAR NICHT VERDIENT. Der Takt ist dunkel, weil sein
      Inhalt Nacht IST (22:00, „abends um zehn“). Der Abschluss ist dunkel,
      weil er das Ende ist. Diese Sektion ist eine Ortsliste — sie hatte
      keinen inhaltlichen Grund. Ein Farbumschlag ohne Grund ist genau der
      Zug, an dem man ein Template erkennt.
   2. Mit 785px war sie der GROESSTE dunkle Block der Seite. Der eine dunkle
      Moment mit Bedeutung — der Takt mit 412px — stand damit im Schatten
      eines groesseren ohne Bedeutung.
   3. Die Messung dahinter war auf der falschen Achse: gezaehlt wurde nur der
      HINTERGRUND. Tatsaechlich stehen auf dieser Strecke fuenf verschiedene
      Kompositionen (Preis mit getrennten Karten und Cyan-Kasten, Umfang als
      zusammenhaengender Block mit Symbolen, Pair als einzelne mittige
      Aussage, Regionen als Dreierreihe, FAQ als Aufklappliste). Sie variiert
      ueber die FORM, nicht ueber die Flaeche.

   Was dabei verlorengeht, ist echt: auf Navy leuchten die 30 Cyan-Punkte vor
   den Ortsnamen deutlich staerker. Wer das zurueckholen will, macht die
   Ortsnamen auf hellem Grund kraeftiger — nicht den Hintergrund dunkel. */
.regionen{--fuge:clamp(18px,2.4vw,26px);
  padding:clamp(52px,7vw,100px) var(--page);background:var(--paper)}                    /*  8,0:1 */

/* Der Abstand zur Ueberschrift. Er stand frueher nur fuer den einspaltigen
   Fall und wurde ab 901px von einem negativen Rand ueberschrieben, damit die
   Karten in das Bild darueber hineinragen — das Bild ist raus, der negative
   Rand mit ihm. */
/* Vier Spalten, seit jede Region eine eigene Seite hat (Niederkassel, Bonn,
   Koeln, Eifel). Darunter zwei Spalten statt vier gequetschter — die
   Ortslisten brechen sonst nach jedem zweiten Namen um. */
.regionen__grid{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--fuge);
  max-width:var(--wrap);margin:clamp(20px,5vw,28px) auto 0}
/* Der Weiterlink sitzt unten in der Karte und ist der eigentliche Zweck des
   Abschnitts geworden: Von hier gehen die Ortsseiten ab. margin-top:auto
   haelt ihn in der Flex-Fassung unten, die Zeile 5 in der subgrid-Fassung. */
.region__mehr{margin-top:14px;align-self:end}
.region{padding:clamp(24px,2.4vw,32px);border-radius:0;background:var(--white);border:1px solid var(--line)}
.region h3{margin:0 0 8px;font-size:1.2rem;letter-spacing:-.015em}
.region p{margin:0 0 14px;color:var(--ink-soft);line-height:1.5;font-size:.98rem}
.region__orte{margin:0;padding:0;list-style:none;display:flex;flex-wrap:wrap;gap:6px 10px;font-size:.88rem;color:var(--muted)}
.region__orte li{position:relative;padding-left:13px}
.region__orte li::before{content:"";position:absolute;left:0;top:.45em;width:6px;height:6px;border-radius:50%;background:var(--cyan)}
/* Kante oben auf ALLEN drei Karten. Sie ist damit kein Zustand mehr, sondern
   die Form der Karte — und die Karten stehen als Reihe zusammen, statt dass
   eine herausfaellt.
   Die Standort-Karte war vorher komplett cyan gefuellt. Angesehen las sich das
   als AUSGEWAEHLT, als haette man geklickt. Sie soll aber nur sagen „hier
   sitzen wir“ — das macht jetzt die Marke darueber, nicht die Flaeche. */
.region{position:relative}
.region::before{content:"";position:absolute;left:-1px;right:-1px;top:-1px;
  height:2px;background:var(--cyan)}
.region--sitz{background:var(--white);border-color:var(--line)}
.region__marke{display:block;margin:0 0 10px;
  font-size:.72rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
  color:var(--cyan-ink)}

/* Gemessen stand die Zeile auf 15,68px und damit unter der Fliesstextgroesse
   der Karten — als Abschluss einer Sektion zu leise. Jetzt auf Fliesstext-
   niveau und in der normalen Textfarbe statt im Grau. */
.regionen__note{max-width:var(--wrap);margin:clamp(30px,3.4vw,44px) auto 0;
  text-align:center;color:var(--ink-soft);font-size:1.06rem;line-height:1.55}

/* ---- Ab drei Spalten fluchten die Karten ------------------------------
   Hier stand frueher ein randloses Foto (eine Daemmerungsaufnahme), ueber das
   die Karten mit ihrer Oberkante hineinragten, und darunter dasselbe Bild in
   drei Stuecken auf Kartenbreite. Beides ist raus — die Sektion ist eine
   Textsektion, und das Foto hat mit ihr um Aufmerksamkeit konkurriert, statt
   sie zu tragen.
   Was bleibt, ist die Flucht der drei Karten (subgrid, s. unten). Wer hier
   wieder ein Bild einsetzen will: die Datei eifel-daemmerung.webp liegt noch
   unter /assets/img, wird aber von keiner Seite mehr benutzt. */
/* Zwischenstufe: Zwischen 901 und 1100px stehen vier Karten zu eng — die
   Ortsnamen brechen dann nach jedem zweiten um. Zwei Spalten, und bewusst
   OHNE subgrid: Sobald die Karten auf zwei Zeilen umbrechen, reicht das
   Zeilenraster des Elternrasters (es beschreibt EINE Kartenzeile) nicht mehr.
   Die Flex-Fassung unten macht in dem Fall dasselbe, nur je Zeile. */
@media(min-width:901px) and (max-width:1100px){
  .regionen__grid{grid-template-columns:repeat(2,1fr)}
  .region{display:flex;flex-direction:column}
  .region__orte{margin-top:auto}
}

@media(min-width:1101px){
  /* Die Karten teilen sich EIN Zeilenraster, damit Titel, Absatz und Ortsliste
     ueber alle vier auf derselben Linie beginnen. Ohne das haengt jede Zeile
     an der Laenge dessen, was darueber steht: gemessen begann der Titel der
     ersten Karte 25px tiefer als die anderen zwei (sie traegt die Marke
     „Unser Standort"), und bei 901px lagen die drei Ortslisten 24px
     auseinander, weil die Ortsnamen dort unterschiedlich oft umbrechen.
     Drei Karten auf drei Hoehen lesen sich als drei einzelne Kaesten, nicht
     als eine Reihe.

     subgrid statt eigener Rechnung: die Karte uebernimmt die vier Zeilen des
     Elternrasters, statt sich eigene zu bauen. Jede Zeile ist damit so hoch
     wie ihr laengster Inhalt in ALLEN drei Karten. Die Platzierung steht
     ausdrucklich an jedem Kind, weil zwei der drei Karten keine Marke haben —
     ohne das rutschte bei ihnen alles eine Zeile hoch und der Effekt waere
     genau verkehrt.
     Die 1fr auf der Absatzzeile nimmt den Rest der Kartenhoehe auf, damit die
     Ortsliste unten steht und nicht mitten in der Karte haengt.

     Nur ab 901px — darunter steht das Raster einspaltig, dann gibt es keine
     Reihe, auf der etwas fluchten koennte. */
  /* row-gap:0 ist PFLICHT, nicht Kosmetik. gap:var(--fuge) auf dem Raster
     setzt Spalten- UND Zeilenfuge. Solange es eine Zeile gab, war die
     Zeilenfuge wirkungslos; mit vier Zeilen lagen plotzlich 3x26px INNERHALB
     jeder Karte — gemessen ein Loch von 116px zwischen Absatz und Ortsliste.
     Den Abstand machen die Margen der Elemente, so wie vorher. */
  /* Fuenf Zeilen statt vier: unten kam der Link auf die Ortsseite dazu.
     Er braucht eine eigene Zeile, sonst landet er per `.region p` in der
     Absatzzeile und liegt auf der Beschreibung. */
  .regionen__grid{grid-template-rows:auto auto 1fr auto auto;row-gap:0}
  .region{position:relative;z-index:1;box-shadow:0 14px 34px -26px rgba(2,9,20,.34);
    display:grid;grid-template-rows:subgrid;grid-row:span 5}
  .region__marke{grid-row:1}
  .region h3{grid-row:2}
  .region p{grid-row:3}
  .region__orte{grid-row:4}
  .region__mehr{grid-row:5}
  /* Ohne subgrid bekommt die Karte eigene Zeilen, dann fluchtet nichts mehr
     ueber die Karten hinweg. Dann wenigstens die Listen auf eine Linie, ueber
     die Kartenunterkante — die Karten sind als Raster-Kinder gleich hoch. */
  @supports not (grid-template-rows:subgrid){
    .region{display:flex;flex-direction:column}
    .region__orte{margin-top:auto}
  }
}

/* ---- FAQ (natives details/summary, kein JS) ---- */
.faq{padding:clamp(52px,7vw,100px) var(--page);background:var(--white)}
.faq__list{max-width:840px;margin:0 auto}
.faq__item{border-bottom:1px solid var(--line)}
.faq__item:first-child{border-top:1px solid var(--line)}
.faq__item summary{display:flex;align-items:center;justify-content:space-between;gap:18px;
  padding:22px 4px;cursor:pointer;list-style:none;font-size:clamp(1.02rem,1.4vw,1.14rem);font-weight:640;
  letter-spacing:-.01em;color:var(--ink);transition:color .15s ease}
.faq__item summary::-webkit-details-marker{display:none}
.faq__item summary:hover{color:var(--cyan-ink)}
.faq__item summary::after{content:"";flex:0 0 auto;width:12px;height:12px;margin-right:4px;
  border-right:2.4px solid var(--cyan-ink);border-bottom:2.4px solid var(--cyan-ink);
  transform:rotate(45deg) translate(-2px,-2px);transition:transform .22s ease}
.faq__item[open] summary::after{transform:rotate(225deg) translate(-2px,-2px)}
.faq__item[open] summary{color:var(--cyan-ink)}
.faq__answer{margin:0;padding:0 4px 24px;max-width:70ch;color:var(--ink-soft);line-height:1.6;font-size:1rem}
.faq__answer a{color:var(--cyan-ink);font-weight:600}
.faq__note{max-width:840px;margin:clamp(26px,3vw,36px) auto 0;text-align:center;color:var(--muted);font-size:.95rem}

/* ---- CTA + Footer ---- */
/* overflow:hidden, damit das randlose Maskenwort saeuberlich an der Sektionskante
   beschnitten wird statt eine Seitenleiste zu erzeugen. */
/* Der Abschluss-Abschnitt liegt jetzt in assets/css/finale.css und wird von
   allen Unterseiten geteilt (websites, shops, software). Bitte dort aendern —
   die Werte hier standen vorher dreimal parallel und liefen auseinander. */

.web-foot{display:flex;flex-wrap:wrap;gap:12px 26px;justify-content:space-between;align-items:center;
  padding:26px var(--page);background:var(--navy);color:#8ea1b5;font-size:.9rem;border-top:1px solid rgba(255,255,255,.07)}
.web-foot a{color:#c4d2e0;text-decoration:none}
.web-foot a:hover{color:#fff}
.web-foot__worlds a{margin:0 2px}

/* ---- Responsive ---- */
@media(max-width:900px){
  .case__paar{grid-template-columns:1fr;gap:clamp(38px,6vw,56px)}
  .steps{grid-template-columns:1fr 1fr;gap:28px}
  .umfang__grid{grid-template-columns:1fr 1fr}
  .branchen__grid{grid-template-columns:1fr}
  .regionen__grid{grid-template-columns:1fr}
  .preis__grid{grid-template-columns:1fr 1fr}
  .zusage{grid-template-columns:1fr;gap:24px}
}
@media(max-width:600px){
  .preis__grid{grid-template-columns:1fr}
}
/* Unter 768px raeumt site-nav.css Welten und Aktionen selbst aus der Leiste;
   uebernommen hat sie dann die gemeinsame Edge-Nav (edge-nav.css).
   EINE Sache muss diese Seite doch richtigstellen: edge-nav.css schiebt den
   Griff nach unten und nach rechts, sobald er sich in die drei Punkte
   verwandelt (52px statt 36px, 10px statt 18px vom Rand). Das ist auf den
   dunklen Seiten richtig — dort faehrt der Kopf beim Scrollen weg und der Griff
   steht danach frei im Bild. Hier BLEIBT die Leiste stehen, es gibt also nichts
   auszuweichen: gemessen sass er als Punkte 20px unter der Leistenmitte.
   Beide Zustaende sitzen jetzt mittig IN der Leiste, an ihre Hoehe gekoppelt
   statt fest verdrahtet. */
@media(max-width:768px){
  html body.web .edge-handle,
  .nav-compact body.web .edge-handle{top:calc(var(--nav-hoehe,64px) / 2);right:18px}
}

@media(max-width:600px){
  .steps{grid-template-columns:1fr}
  /* TREPPE: gestapelt hat jeder Schritt seine eigene Oberkante. Die Cyan-Marken
     wachsen beim Scrollen unterschiedlich weit — der unterste ganz durch, jeder
     darueber ein Stueck weniger. Ergibt eine Treppe von kurz (oben) nach lang
     (unten), also eine sichtbare Zunahme statt vier gleicher Striche.
     Startwert ist die vorhandene 42px-Marke, NICHT 0: ohne JavaScript und im
     Ausgangszustand sieht der Abschnitt genauso aus wie bisher.
     --sp (0..1) kommt aus dem JS. */
  .step{--ziel:100%}
  .step:nth-child(1){--ziel:30%}
  .step:nth-child(2){--ziel:53%}
  .step:nth-child(3){--ziel:76%}
  .step:nth-child(4){--ziel:100%}
  .step::before{width:calc(42px + (var(--ziel) - 42px) * var(--sp,0))}
  .umfang__grid{grid-template-columns:1fr}
  .cta__row{gap:16px}
}

/* ---- Belegvorschau in der Kundenstimme -------------------------------------
   "Logo" und "Briefpapier" im Zitat sind Links auf /gestaltung.html und zeigen
   bei Zeiger oder Tastaturfokus eine kleine Abbildung.

   ZURUECKHALTEND AUSGEZEICHNET, und das ist keine Kosmetik: das ist der
   Wortlaut eines Kunden. Ein Link in Markenfarbe mitten im Zitat laese sich
   als nachtraeglich eingefuegte Werbung. Der Text behaelt deshalb seine Farbe,
   und nur eine gepunktete Linie darunter sagt, dass hier etwas dahinterliegt.

   position:relative am Link macht ihn zum Bezug der Karte. Die Karte liegt IM
   Link — dadurch bleibt der Hover erhalten, wenn der Zeiger auf sie wandert
   (WCAG 1.4.13 verlangt genau das).

   KEIN Platz im Fluss: die Karte ist absolut gesetzt und veraendert die
   Textzeile nicht. Sonst rueckte beim Hinsehen der halbe Absatz. */
/* ECHTE UNTERSTREICHUNG, kein border-bottom. Der Unterschied ist an diesen
   zwei Woertern gut zu sehen: "Logo" hat ein g, "Briefpapier" ein p. Ein Rand
   ist eine gerade Linie unter dem ganzen Kasten und schneidet mitten durch
   beide Unterlaengen. text-decoration weicht ihnen aus
   (text-decoration-skip-ink steht auf auto), liegt mit 3px Abstand tiefer und
   sieht dadurch gesetzt aus statt gezeichnet. */
.beleg{position:relative;color:inherit;
  text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px;
  text-decoration-color:color-mix(in srgb,var(--cyan-ink) 55%,transparent);
  transition:text-decoration-color .18s ease,color .18s ease}
.beleg:hover,.beleg:focus-visible{color:var(--cyan-ink);text-decoration-color:var(--cyan-ink)}

/* ZWEI STAERKEN, JE NACHDEM WEM DER SATZ GEHOERT.

   Im ZITAT bleiben die Woerter in Textfarbe. Blau eingefaerbte Woerter mitten
   in einer Kundenaussage lesen sich als nachtraegliche Redaktion — als haetten
   wir in seinen Satz hineingeschrieben. Das ist die eine Stelle der Seite, an
   der wir nicht selbst reden, und das soll man sehen. Die Unterstreichung ist
   dafuer von 55 auf 75 Prozent gegangen: leise darf sie sein, uebersehbar
   nicht. Beim Draufzeigen wird ohnehin beides voll farbig.

   Im NACHTRAG darunter ist der Satz unserer. Dort gibt es keinen Grund zur
   Zurueckhaltung, und der Link sieht aus wie einer — durchgehend in der
   Textvariante der Markenfarbe. #007aa6 kommt auf dem Kartengrund auf 4,55:1
   und traegt damit auch als kleiner Grad.

   Nebeneffekt, und der ist der eigentliche Gewinn: der Unterschied zwischen
   beiden Auszeichnungen sagt ohne ein Wort, welche Zeile von Fabian stammt
   und welche von uns. */
.stimme__text .beleg{text-decoration-color:color-mix(in srgb,var(--cyan-ink) 75%,transparent)}
.stimme__nachtrag .beleg{color:var(--cyan-ink);
  text-decoration-color:color-mix(in srgb,var(--cyan-ink) 70%,transparent)}
.stimme__nachtrag .beleg:hover,.stimme__nachtrag .beleg:focus-visible{
  text-decoration-color:var(--cyan-ink)}
.beleg:focus-visible{outline:2px solid var(--cyan);outline-offset:3px;border-radius:2px}

.beleg__karte{
  position:absolute;left:50%;bottom:calc(100% + 12px);z-index:6;
  width:220px;margin-left:-110px;padding:10px;
  background:var(--white);border:1px solid var(--line);border-radius:4px;
  box-shadow:0 20px 44px -22px rgba(13,24,38,.45);
  opacity:0;transform:translateY(6px) scale(.98);pointer-events:none;
  transition:opacity .18s ease,transform .18s cubic-bezier(.22,.7,.3,1)}
.beleg.beleg--unten .beleg__karte{bottom:auto;top:calc(100% + 12px);
  transform:translateY(-6px) scale(.98)}
.beleg.is-offen .beleg__karte{opacity:1;transform:none;pointer-events:auto}
.beleg__karte img{display:block;width:100%;height:auto;border-radius:2px;background:var(--paper)}
/* Erzeugter Inhalt statt Textknoten: so bleibt der Wortlaut des Zitats
   unangetastet (s. Begruendung in beleg.js). */
.beleg__karte[data-bu]::after{content:attr(data-bu);display:block;margin-top:8px;
  font-size:.72rem;line-height:1.3;color:var(--muted)}

@media(prefers-reduced-motion:reduce){
  .beleg__karte{transition:none;transform:none}
  .beleg.beleg--unten .beleg__karte{transform:none}
}
/* Auf dem Handy gibt es keinen Zeiger — dort bleibt das Wort ein Link, und
   beleg.js baut die Karte gar nicht erst. Die Regel steht trotzdem hier,
   falls ein Geraet mit Zeiger UND schmalem Schirm auftaucht. */
@media(max-width:520px){
  .beleg__karte{width:180px;margin-left:-90px}
}

/* Der Nachtrag unter der Kundenstimme: unsere Stimme, nicht Fabians. Deshalb
   kleiner, ruhiger und ausserhalb des Zitatkastens — der Unterschied muss man
   sehen, nicht nur im Quelltext nachlesen koennen. */
.stimme__nachtrag{max-width:var(--wrap);margin:clamp(16px,2vw,22px) auto 0;
  color:var(--muted);font-size:.92rem;line-height:1.5}
@media(max-width:860px){.stimme__nachtrag{text-align:center}}

/* Zwei Seiten nebeneinander: die Karte wird breiter statt hoeher. Hochkant
   gestapelt waere sie ueber 600px hoch geworden und haette auf den meisten
   Schirmen nach oben keinen Platz mehr gefunden. */
.beleg__karte--paar{width:264px;margin-left:-132px}
.beleg__paar{display:grid;grid-template-columns:1fr 1fr;gap:8px}
@media(max-width:520px){
  .beleg__karte--paar{width:210px;margin-left:-105px}
}

/* ======================================================================
   06.09.2026 — Referenzen mit Website-Ausschnitt, als Buehne statt Spalten.
   Anlass war Feedback von aussen: die Referenzen zeigten nur Text, und wer
   den Monitor im Einstieg uebersprungen hat, konnte die Arbeit nirgends
   sehen. Gilt fuer /websites.html und /en/websites.html.
   ====================================================================== */

/* ------------------------------------------ Referenzen: Bild in der Karte ---
   16:10, von OBEN angeschnitten. Kopfleiste und Aufmacher sind der Teil, an
   dem man eine Seite wiedererkennt; was unten wegfaellt, traegt ohnehin nicht.
   Der helle Grund steht als Platzhalter, bis das Bild da ist — auf dem
   Kartengrund (--paper) waere das Feld sonst bis dahin unsichtbar. */
.case__bild{margin:0 0 clamp(16px,1.6vw,22px);overflow:hidden;
  border:1px solid var(--line);background:var(--white);
  aspect-ratio:16/10}
.case__bild img{width:100%;height:100%;display:block;
  object-fit:cover;object-position:top center}

/* ================================================ Referenzen als Spur =======
   BIS 06.09. STANDEN DIE DREI PROJEKTE BREIT NEBENEINANDER. Mit dem Bild in
   der Karte geht das nicht mehr auf: je Projekt bleibt ein Drittel der Breite,
   und ein Website-Ausschnitt in Briefmarkengroesse zeigt nichts — genau der
   Vorwurf, wegen dem das Bild ueberhaupt dazugekommen ist.
   Also dieselbe Spur wie auf dem Handy, nur auf allen Breiten: ein Projekt
   fuellt die Buehne, Bild links, Beschreibung rechts, Messwerte darunter.
   Gewischt wird weiter mit scroll-snap — das Skript liefert nur Pfeile,
   Punkte und den Stand. Faellt es aus, bleibt die Spur bedienbar.
   ========================================================================== */
.cases__buehne{position:relative;max-width:var(--wrap);margin:0 auto}

@media(min-width:701px){
  /* GETEILTE MASSE (26.09.2026). Die Pfeile liegen ausserhalb der Spur und
     koennen das Bild darin nicht messen. Sie rechnen seine Lage aus denselben
     Werten nach, mit denen das Raster unten es setzt; wer hier etwas aendert,
     aendert beides. Nicht als Variable moeglich und deshalb in der Pfeilregel
     wiederholt: das Spaltenverhaeltnis 1.08fr/1fr und das Bildformat 16/10. */
  .cases{
    --spur-rand:clamp(46px,4.4vw,68px);   /* Polster der Buehne je Seite */
    --spur-oben:4px;                      /* Innenabstand oben in der Spur */
    --bild-luecke:clamp(28px,3.4vw,58px); /* Spalte zwischen Bild und Text */
    --case-h3:clamp(1.8rem,2.9vw,2.5rem);
    /* Feste Zeilenhoehe fuer die Eyebrow, sonst kommt ihr Kasten aus der
       Schrift (line-height normal, gemessen 17px) und laesst sich nicht
       nachrechnen. 1.35 ergibt 16.85px, also optisch dasselbe. */
    --tag-zeile:calc(.78rem * 1.35);
    /* Bildoberkante auf Hoehe der Versalien der Ueberschrift, die Eyebrow
       steht darueber: ihre Zeile, ihr Abstand von 8px und der Weg von der
       Zeilenoberkante der H3 bis zu den Versalien (Geist bei line-height
       1.05: .15em, bei 40px also 6px). */
    --bild-versatz:calc(var(--tag-zeile) + 8px + var(--case-h3) * .15)}

  /* Platz fuer die Pfeile LINKS UND RECHTS NEBEN der Spur statt darueber.
     Ein Pfeil, der ueber dem Bild liegt, verdeckt genau das, was er zeigen
     soll — und rechts laege er mitten im Fliesstext. */
  .cases__buehne{padding:0 var(--spur-rand)}
  /* Senkrecht auf der BILDMITTE statt auf der Mitte der Buehne. Die Buehne
     ist so hoch wie das laengste Projekt samt Messleiste, ihre Mitte lag
     deshalb 95 bis 225px unter der Bildmitte, halb neben dem Fliesstext.
     margin-top statt top, weil Prozente dort an der BREITE haengen: 100% ist
     die Buehne samt Polster, abzueglich Polster und Spalte bleibt die Breite
     beider Spalten, davon hat das Bild 1.08 von 2.08 Teilen, und 10/16 davon
     ist seine Hoehe. */
  .cases .arrow--case{top:0;
    margin-top:calc(var(--spur-oben) + var(--bild-versatz)
      + (100% - 2 * var(--spur-rand) - var(--bild-luecke)) * 1.08 / 2.08 * 10 / 16 / 2)}

  .cases .case__paar{
    max-width:none;margin:0;
    /* Die drei Spalten der alten Fassung ausdruecklich abraeumen: sonst
       landen die Projekte darin statt in den automatischen. */
    display:grid;grid-auto-flow:column;grid-auto-columns:100%;
    grid-template-columns:none;grid-template-rows:auto;
    column-gap:clamp(24px,3vw,48px);row-gap:0;
    position:relative;
    overflow-x:auto;overscroll-behavior-x:contain;
    scroll-snap-type:x mandatory;scroll-behavior:auto;
    padding:var(--spur-oben) 0 8px;
    scrollbar-width:none;-ms-overflow-style:none}
  .cases .case__paar::-webkit-scrollbar{display:none}

  /* .projekt war breit display:contents, damit Karte und Messleiste in zwei
     Rasterzeilen fallen konnten. Als Wischeinheit braucht es wieder eine
     eigene Box — sonst gibt es nichts, worauf die Spur schnappen koennte. */
  .cases .case__paar .projekt{
    display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:auto auto;
    align-content:start;scroll-snap-align:center}
  .cases .case__paar .projekt > *{grid-column:1}
  .cases .case__paar .projekt > .case{grid-row:1}
  .cases .case__paar .projekt > .mess{grid-row:2;margin-top:clamp(18px,2vw,26px)}

  /* Bild links, Text rechts — das eigentliche Ziel der Umstellung.
     Das Bild spannt ueber alle drei Textzeilen; seine Hoehe kommt aus dem
     Seitenverhaeltnis, nicht aus der Textlaenge. Oben schliesst es seit dem
     26.09.2026 mit der Ueberschrift ab, nicht mit der Eyebrow: die steht
     darueber wie eine Dachzeile (--bild-versatz).
     Die vierte, leere Zeile (1fr) faengt ab, was das Bild laenger ist als
     der Text. Ohne sie verteilte das Raster den Ueberstand auf die drei
     Textzeilen, und bei kurzen Projekten (IBO, Kegema) rueckten Kopf,
     Liste und Link 4 bis 9px auseinander. Ein Element, das eine 1fr-Zeile
     ueberspannt, gibt seinen Ueberstand nur an diese ab. */
  .cases .case__body{
    display:grid;
    grid-template-columns:minmax(0,1.08fr) minmax(0,1fr);
    grid-template-rows:auto auto auto 1fr;
    column-gap:var(--bild-luecke);
    align-items:start}
  .cases .case__bild{grid-column:1;grid-row:1 / span 4;align-self:start;margin:var(--bild-versatz) 0 0}
  .cases .case__tag{line-height:var(--tag-zeile)}
  .cases .case__kopf{grid-column:2;grid-row:1}
  .cases .case__points{grid-column:2;grid-row:2}
  /* margin-top:auto aus der Spaltenfassung abschalten: dort schob es die
     Zeile an den Kartenboden, hier haengt sie an der Aufzaehlung. */
  .cases .case__links{grid-column:2;grid-row:3;margin:clamp(16px,1.6vw,22px) 0 0}

  /* Mehr Grad, weil jetzt die volle Breite zur Verfuegung steht. */
  .cases .case__body h3{font-size:var(--case-h3)}
  .cases .case__body .case__kopf > p{margin-top:clamp(10px,1.1vw,14px)}
}

/* Die zwei Aufraeumarbeiten der alten Spaltenfassung: Trennlinien zwischen den
   Spalten und Zeilenreserven fuer gleiche Grundlinien. Beides ergab Sinn,
   solange drei Karten nebeneinander standen. In der Spur ist immer nur eine
   zu sehen — die Linie haette keinen Nachbarn mehr, und die Reserve stellte
   nur Weissraum in die Karte. */
@media(min-width:1081px){
  .cases .projekt + .projekt > .case::before,
  .cases .projekt + .projekt > .mess::before{content:none}
  .cases .case__body h3{min-height:0}
  .cases .case__kopf > p{min-height:0}
}

/* ---- Pfeile ------------------------------------------------------------
   KEIN EIGENER KNOPF: es ist .arrow aus dem Einstieg, dieselbe Klasse, dieselbe
   Form, derselbe Merkzettel beim Darueberfahren. Zwei gleiche Bedienungen auf
   einer Seite sollen auch gleich aussehen — und die Regel .arrow{display:none}
   unter 700px erbt die Spur damit gleich mit.
   Geaendert ist nur, was sich aendern MUSS: am Monitor stehen die Pfeile weiss
   auf dunklem Grund, hier auf hellem. Weiss waere hier unsichtbar. */
.arrow--case{filter:drop-shadow(0 2px 6px rgba(13,24,38,.22))}
.arrow--case:hover{filter:drop-shadow(0 4px 10px rgba(13,24,38,.32))}
.arrow--case svg path{fill:var(--ink);stroke:var(--ink)}
/* KEIN Farbwechsel beim Hovern und KEIN Merkzettel. Der Pfeil am Monitor macht
   beides auch nicht — er wird groesser, das war es. Die Sprechblase von dort
   ist hier ausserdem doppelt: die Namensschalter unter der Spur nennen alle
   drei Projekte ohnehin, und eine dunkle Blase auf hellem Grund wiegt schwerer
   als die Bedienung, an der sie haengt. */
/* Buendig an der Buehnenkante statt der -14px des Monitors: dort haengen sie
   bewusst ueber den Rahmen, hier steht das Polster der Buehne fuer sie bereit. */
.arrow--case.arrow--l{left:0}
.arrow--case.arrow--r{right:0}
/* Am Ende der Spur abgeblendet statt versteckt: ein Pfeil, der verschwindet,
   laesst die Buehne springen. */
.arrow--case[disabled]{opacity:.26;pointer-events:none;filter:none}

/* ---- Punkte, breit mit Namen -------------------------------------------
   Drei anonyme Punkte sagen auf dem Desktop zu wenig: dort ist Platz fuer die
   Namen, und benannte Schalter zeigen zugleich, dass es DREI Referenzen gibt.
   Das Skript legt den Namen als .punkt__txt in den Knopf; mobil bleibt er aus. */
.messgruppe__punkte .punkt__txt{display:none}
@media(min-width:701px){
  .cases .messgruppe__punkte{display:flex;flex-wrap:wrap;justify-content:center;
    gap:8px;margin-top:clamp(20px,2.2vw,30px)}
  .messgruppe__punkte button{
    width:auto;height:auto;padding:8px 16px;border:1px solid var(--line);
    background:var(--white);cursor:pointer;
    display:inline-flex;align-items:center;gap:9px;
    font:inherit;font-size:.92rem;font-weight:560;color:var(--ink-soft);
    transition:border-color .18s ease,color .18s ease,background-color .18s ease}
  .messgruppe__punkte .punkt__txt{display:inline}
  .messgruppe__punkte button::before{
    content:"";width:7px;height:7px;border-radius:50%;flex:none;
    background:var(--line);transition:background .18s ease,transform .18s ease}
  .messgruppe__punkte button:hover{border-color:var(--cyan);color:var(--ink)}
  .messgruppe__punkte button[aria-current="true"]{
    border-color:var(--cyan-ink);color:var(--ink);background:var(--cyan-soft)}
  .messgruppe__punkte button[aria-current="true"]::before{
    background:var(--cyan-ink);transform:scale(1.25)}
  .messgruppe__punkte button:focus-visible{
    outline:2.5px solid var(--cyan-ink);outline-offset:2px;border-radius:0}
}
/* Desktop: die Schalter eine Spur naeher an die Messleiste (26.09.2026).
   Die Spur ist so hoch wie ihr laengstes Projekt (ab 1440px GongZheng mit
   zweizeiliger Ueberschrift), unter den kuerzeren bleibt ohnehin Luft; die 30px
   Abstand kamen noch obendrauf. 12px plus die 8px Innenabstand der Spur
   lassen unter dem laengsten Projekt 20px. Tablet behaelt den alten Wert,
   dort ist die Spur schmal und die Luft darunter klein. */
@media(min-width:1081px){
  .cases .messgruppe__punkte{margin-top:12px}
}

/* ---- Handy: Zeilenkette und Bildgroesse -------------------------------- */
@media(max-width:700px){
  /* ACHT ZEILEN STATT SIEBEN. Das Bild ist ein zusaetzlicher Baustein am
     Kartenkopf, und die subgrid-Kette muss lueckenlos bleiben — sonst landen
     Aufzaehlung und Linkzeile in impliziten Zeilen, und die drei Karten stehen
     beim Wischen nicht mehr auf einer Hoehe.
       .case__paar  8 Zeilen
       .case        1-6      .mess  7-8
       .case__body  1-6      (Bild, Eyebrow, Ueberschrift, Text, Liste, Link)
       .case__kopf  2-4      unveraendert
       > div        2-3      unveraendert */
  .cases .case__paar{grid-template-rows:repeat(8,auto)}
  .cases .projekt > .case{grid-row:span 6}
  .cases .projekt .case__body{grid-row:span 6}

  /* MEHR BILD. Bei 375px blieben dem Ausschnitt gerechnet 219x137px — darin
     ist eine Website nicht zu erkennen, und genau dafuer steht das Bild hier.
     Drei Stellschrauben, keine reicht allein:
       1. Der Blick auf den Nachbarn schrumpft von 40 auf 20px. Hier stand,
          der Streifen genuege weiter als Hinweis aufs Wischen. Nachgemessen
          am 26.09.2026 war er LEER: von den 20px gehen 12px Luecke ab, und
          die restlichen 8px nahm das Verkleinern auf 93 % um die Kartenmitte
          weg. Das Nachbarbild begann bei 390px Breite knapp 3px hinter der
          Spurkante, zu sehen war nur Weiss. Die Punkte stehen unter einer
          Karte, die hoeher ist als der Bildschirm, und der Anreisser laeuft
          einmal. Deshalb verkleinert hero.js die Nachbarn seitdem zur
          Innenkante hin, und der Blick steht auf 26px: 14px Nachbarbild.
          NICHT MEHR ALS 14px. Der Text der Nachbarkarte beginnt nach dem
          Verkleinern 16px x 0,93 = 14,9px vom Kartenrand; bei 16px Anschnitt
          (Blick 28px) ragten die Cyan-Striche der Liste 1px hinein und
          standen als Kruemel am Rand. Die Karte ist dafuer 12px schmaler
          als mit 20px Blick.
       2. Das Bild laeuft bis an die Kartenkante — die 16px Innenpolster des
          Projekts holt es sich per negativem Aussenabstand zurueck. Seitliche
          Raender fallen dabei weg, sonst stuenden sie auf der Kante.
     KEIN anderes Seitenverhaeltnis. 4:3 stand hier kurz und brachte ein
     Viertel mehr Hoehe — aber die Aufnahme ist 1280x800, also 16:10, und
     object-fit:cover haette sie dafuer links und rechts beschnitten. Ein
     angeschnittener Screenshot zeigt weniger von der Seite, nicht mehr; die
     Groesse muss aus der BREITE kommen.
     Zusammen 291x182 statt 219x137: drei Viertel mehr Flaeche, nichts fehlt.

     DIE KARTE IST DAMIT HOEHER ALS EIN BILDSCHIRM. Das war bis hierher ein
     ausdrueckliches Ziel (s. "AUF EINEN BILDSCHIRM" weiter oben) — es stammt
     aber aus der Fassung OHNE Bild. Mit Bild waere es nur zu halten, indem man
     den Fliesstext unter Lesegroesse druckt, und das ist der schlechtere
     Tausch: eine Referenz, die man nicht lesen kann, neben einem Bild, auf dem
     man nichts erkennt. */
  .cases .case__paar{--blick:26px}
  .cases .case__bild{margin-left:-16px;margin-right:-16px;
    border-left:0;border-right:0}
}

/* ---- Handy: der Anschnitt ist von Anfang an da (26.09.2026) ------------
   Die Nachbarn haengen am Reveal der ERSTEN Karte, nicht an einem eigenen:
   sobald deren Karte aufgedeckt wird, erscheinen sie im selben Moment und
   mit derselben Bewegung. Ihr eigenes Reveal griffe nie rechtzeitig: vom
   Anschnitt sind 14px im Bild, gut 5 % der Karte, verlangt sind 12 %. Er
   blendete deshalb erst mitten im Anreisser ein.
   Kurz stand hier ein zweiter Takt (.2s nach der Karte seitlich
   hereingleiten). Basti wollte den Nachbarn dann doch die ganze Zeit sehen.
   Alle Nachbarn, auch die weit rechts: wer spaeter wischt, soll keine Karte
   mehr einblenden sehen. Nur die Messleiste der ersten Karte behaelt ihr
   eigenes Reveal, sie kommt erst beim Weiterscrollen ins Bild.
   Ohne :has() (aeltere Browser) deckt hero.js die Nachbarn auf, sobald die
   Spur zu gut einem Drittel im Bild ist. */
@media(max-width:700px){
  .rv-on .case__paar:has(> .projekt:first-child > .case.is-in) .projekt:not(:first-child) > .rv{
    opacity:1;transform:none}
}
