/* SNX Solutions — statische Fassung des Vite-Builds (Design unverändert)
   + neue Sektionen: Komplettpaket, Shop-Case Plotter-FIX, Footer. */

@font-face{font-family:Geist;src:url(/assets/fonts/geist-sans.woff2) format("woff2");font-style:normal;font-weight:100 900;font-display:swap}
@font-face{font-family:"Geist Mono";src:url(/assets/fonts/geist-mono.woff2) format("woff2");font-style:normal;font-weight:100 900;font-display:swap}

:root{
  --color-navy:#020914;
  --color-navy-raised:#07111f;
  --color-paper:#f5f8fc;
  --color-muted:#aeb9c7;
  --color-cyan:#19bff4;
  --color-magenta:#ff2e88;
  --color-line:rgba(174,185,199,.2);
  --page-gutter:clamp(24px,4.2vw,68px);
  --header-h:80px;
  color:var(--color-paper);
  background:var(--color-navy);
  font-family:Geist,Arial,sans-serif;
  font-synthesis:none;
  text-rendering:optimizeLegibility;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth;background:var(--color-navy)}
body{margin:0;min-width:320px;min-height:100vh;color:var(--color-paper);background:var(--color-navy);-webkit-font-smoothing:antialiased}
button,a{font:inherit}
a{color:inherit;text-decoration:none}
img{display:block;max-width:100%}
:focus-visible{outline:3px solid var(--color-paper);outline-offset:4px}
/* Sprungziele halten Abstand zum fixierten Kopf. Fest 90px war falsch:
   auf Mobil scrollt der Kopf weg, dort blieb dadurch ein 90px-Loch über
   jeder Überschrift stehen. Jetzt an --header-h gekoppelt, mobil klein. */
/* Sprungziele rasten mit ihrer OBERKANTE exakt am Viewport-Rand ein — die
   Farbkante der Sektion liegt danach genau oben. Der fixe Kopf darf sie
   überlagern, weil er sich über .site-header--invert der Sektionsfarbe
   anpasst; die Sektionen haben zudem 88-150px Innenabstand oben, es wird
   also kein Inhalt verdeckt. */
[id]{scroll-margin-top:0}

.site-shell{--page-accent:var(--color-cyan);min-height:100vh;overflow:clip;background:var(--color-navy)}

/* ---- Scroll-Fortschritt -------------------------------------------------
   Desktop: schlanke Schiene rechts am Rand. Mobile: Balken oben.
   Die Füllung wird per transform:scale bewegt (GPU, kein Layout). Wo der
   Browser scroll-driven animations kann, läuft das komplett ohne JS und
   ohne Main-Thread; sonst setzt site-static.js --sp im rAF.            */
/* Desktop: nur die leuchtende Fülllinie, KEINE graue Schiene (die wirkt am
   Rand unruhig). Liegt über dem Header (z-index > 20), läuft also lückenlos
   über die volle Höhe — auch wenn der Header steht. */
/* Eine Linie, links, bündig am Rand. Startet unter dem Header (der steht
   dauerhaft, daher entsteht dort keine Lücke). Keine Spur — nur die Füllung. */
.scroll-rail{position:fixed;z-index:19;top:var(--header-h);bottom:0;left:0;width:3px;background:transparent;pointer-events:none}
.scroll-rail__fill{display:block;width:100%;height:100%;background:linear-gradient(to bottom,rgba(25,191,244,.4),var(--color-cyan));box-shadow:0 0 14px rgba(25,191,244,.55);transform:scaleY(var(--sp,0));transform-origin:top center;will-change:transform}

/* Untere Linie: wächst von RECHTS nach LINKS, trifft sich bei 100 % mit dem
   Kopf der senkrechten Linie exakt in der Ecke unten links.
   `bottom:0` ohne jede Korrektur — bewusst:
   - Die ein-/ausblendbare Browserleiste rechnen wir nicht nach, das macht der
     Browser bei fixed selbst (frühere Handkorrektur ließ die Linie beim
     Hochscrollen springen, Begründung in site-static.js).
   - Auch KEIN env(safe-area-inset-bottom): Android meldet dort die Höhe der
     Systemnavigation, wodurch die Linie mitten im Bild schwebte statt am
     Rand zu liegen. Sie ist 3px hoch und rein dekorativ, sie darf in den
     Bereich der Systemleiste reichen. Interaktives (das Kontakt-Dock) hält
     dagegen weiterhin Abstand über env(). */
.scroll-rail--bottom{top:auto;bottom:0;right:0;left:0;width:auto;height:3px}
.scroll-rail--bottom .scroll-rail__fill{background:linear-gradient(to left,rgba(25,191,244,.4),var(--color-cyan));transform:scaleX(var(--sp,0));transform-origin:right center}

/* Leuchtender Kopf an der Spitze der Fortschrittslinie. Bewusst OHNE
   Leerlauf-Animation: der Kopf markiert die Scrollposition, ein
   eigenständiges Schweben würde diese Aussage verfälschen — und auf dieser
   Leiste liegt bereits der Kapitelwechsel-Impuls, der Information trägt.
   --rail-len (Weglänge) misst site-static.js; translate statt top/left,
   damit es auf dem Compositor läuft. */
/* Das Element selbst trägt NUR die Position — Aussehen liegt im ::before,
   damit der Kern unabhängig von der scroll-getriebenen Verschiebung
   skaliert werden kann (zwei transforms auf einem Element gehen nicht). */
/* Bündig an die Innenkante statt mittig auf die Leiste: ein 5px-Punkt kann
   auf einer 3px-Leiste am Bildschirmrand nicht gleichzeitig zentriert UND
   vollständig sichtbar sein. Zentriert ragte er 1px über die Kante hinaus
   und wurde beschnitten — dadurch wirkte er kleiner als der Ankerpunkt oben
   rechts, der als einziger komplett im Bild lag. Alle Punkte sind gleich
   groß, sie müssen nur alle ganz zu sehen sein. */
.rail-head{position:absolute;top:0;left:0;width:5px;height:5px;margin:0;border-radius:50%;box-shadow:0 0 8px 2px rgba(25,191,244,.9),0 0 20px 7px rgba(25,191,244,.4);transition:box-shadow .55s cubic-bezier(.32,0,.16,1);transform:translateY(calc(var(--sp,0)*var(--rail-len,0px)));pointer-events:none;will-change:transform}
/* Ganz oben sitzen die Köpfe als DREIECKE exakt in ihrer Ecke und runden sich
   beim Losscrollen zu Punkten. Beide Formen sind 12-Punkt-Polygone in
   derselben Reihenfolge — nur so kann clip-path überhaupt weich überblenden;
   zwischen polygon() und circle() gibt es keine Interpolation, und bei
   ungleicher Punktzahl springt es.
   Schein und Form liegen auf ZWEI Ebenen: clip-path würde einen box-shadow
   auf demselben Element mit wegschneiden. Der Schein sitzt deshalb unverändert
   auf dem Träger (.rail-head bzw. .rail-anchor__core), die beschnittene Form
   auf dessen :before. So bleiben die ursprünglichen Schein-Werte exakt
   erhalten — ein drop-shadow-Ersatz war deutlich dünner, weil er die
   Aufweitung (2px und 7px) der box-shadows nicht nachbilden kann. */
.rail-head:before{position:absolute;inset:0;content:"";background:#eafaff;
  clip-path:polygon(50% 0%,75% 6.7%,93.3% 25%,100% 50%,93.3% 75%,75% 93.3%,50% 100%,25% 93.3%,6.7% 75%,0% 50%,6.7% 25%,25% 6.7%);
  transform:scale(1);
  transition:clip-path .55s cubic-bezier(.32,0,.16,1),transform .55s cubic-bezier(.32,0,.16,1)}
/* Das Dreieck wächst über die Kreisgröße hinaus. Grund ist Fläche, nicht
   Geschmack: in derselben 5x5-Box hat das Eckdreieck 12,5px², der Kreis
   19,6px² — bei gleicher Boxgröße wirkte es also rund ein Drittel schwächer.
   1,5 statt 1,8: Schenkel 7,5px statt 9px. 1,8 war anderthalb Pixel zu wuchtig.
   Der Ursprung liegt jeweils IN der Ecke, dadurch wächst es in die Ecke
   hinein, statt sich von ihr wegzuschieben. Die Box selbst bleibt 5x5: an ihr
   hängt die Wegberechnung des Kopfes (--rail-len zieht die Kopfgröße ab). */
/* Der Schein wächst mit: sonst säße ein 7,5px-Dreieck in einem Hof, der für
   5px gerechnet ist. Gleiche Kurve wie die Form, damit beides eine Bewegung
   bleibt. */
.is-at-top [data-rail] .rail-head,
.is-at-top [data-rail-b] .rail-head{box-shadow:0 0 11px 3px rgba(25,191,244,.9),0 0 25px 9px rgba(25,191,244,.4)}
.is-at-top [data-rail] .rail-head:before{transform:scale(1.5);transform-origin:0 0}
.is-at-top [data-rail-b] .rail-head:before{transform:scale(1.5);transform-origin:100% 100%}
.is-at-top [data-rail] .rail-head:before{clip-path:polygon(50% 0%,75% 0%,100% 0%,75% 25%,50% 50%,25% 75%,0% 100%,0% 80%,0% 60%,0% 40%,0% 20%,0% 0%)}
.is-at-top [data-rail-b] .rail-head:before{clip-path:polygon(100% 0%,100% 20%,100% 40%,100% 60%,100% 80%,100% 100%,50% 100%,25% 100%,0% 100%,25% 75%,50% 50%,75% 25%)}

/* Seitenleisten gibt es nur mobil (siehe Media Query) — auf Desktop würden
   sie mit der senkrechten Hauptleiste kollidieren. */
.scroll-rail--right,.scroll-rail--left{display:none}

/* Fester Ankerpunkt in der oberen rechten Ecke der Navbar — sieht aus wie
   der Kopf der Leisten, bewegt sich aber nicht. Eigenes Element statt Teil
   von .rail-close, weil deren overflow:hidden den Schein abschneiden würde.
   Sitzt mittig auf der 3px-Linie (top = Headerhöhe minus 1px). */
/* Element trägt nur die Position; Aussehen liegt im __core, damit der Kern
   unabhängig von der Laufbewegung skalieren kann (ein Element kann nicht
   zwei transforms haben). */
.rail-anchor{position:fixed;z-index:19;top:calc(var(--header-h) - 1px);right:0;width:5px;height:5px;pointer-events:none}
/* Der Anker oben rechts bleibt Dreieck, bis man wirklich unten ist — er
   markiert das Ziel und rundet sich erst, wenn die Linie dort ankommt. */
/* Der Kern behält seinen box-shadow, weil @keyframes anchorCore ihn beim
   Zünden ANIMIERT — läge er woanders, liefe die Zündung von nichts aus. Die
   beschnittene Form sitzt deshalb auf seinem :before. */
.rail-anchor__core{position:absolute;inset:0;border-radius:50%;box-shadow:0 0 8px 2px rgba(25,191,244,.9),0 0 20px 7px rgba(25,191,244,.4)}
.rail-anchor__core:before{position:absolute;inset:0;content:"";background:#eafaff;
  clip-path:polygon(50% 0%,100% 0%,100% 20%,100% 40%,100% 60%,100% 80%,100% 100%,75% 75%,50% 50%,25% 25%,0% 0%,25% 0%);
  transform:scale(1.5);transform-origin:100% 0;
  transition:clip-path .55s cubic-bezier(.32,0,.16,1),transform .55s cubic-bezier(.32,0,.16,1)}
.is-line-running .rail-anchor__core:before,
.is-framed .rail-anchor__core:before{transform:scale(1)}
.is-line-running .rail-anchor__core:before,
.is-framed .rail-anchor__core:before{clip-path:polygon(50% 0%,75% 6.7%,93.3% 25%,100% 50%,93.3% 75%,75% 93.3%,50% 100%,25% 93.3%,6.7% 75%,0% 50%,6.7% 25%,25% 6.7%)}

/* Finale: der Ankerpunkt wandert NICHT mehr quer über den Schirm. Er bleibt
   in seiner Ecke und schießt dort das Licht heraus, aus dem die Aurora wird —
   oben rechts Violett, unten links (an den Leistenköpfen) gleichzeitig Cyan.
   Zwei Quellen statt einer Reise: der Effekt entsteht sichtbar an genau den
   Punkten, die vorher die ganze Seite über den Fortschritt getragen haben.
   Start .8s = genau dann, wenn die Querlinie den rechten Rand erreicht hat. */

/* Der Kern selbst bleibt klein und hart — er ist der Punkt, nicht der Schein.
   Er lädt sich nur leicht auf, damit die Quelle erkennbar ist. */
.is-energized .rail-anchor__core{animation:anchorCore 4.4s cubic-bezier(.3,0,.3,1) .8s forwards}
@keyframes anchorCore{
  0%{transform:scale(1)}
  18%{transform:scale(1.9);box-shadow:0 0 18px 6px rgba(190,240,255,.95),0 0 44px 16px rgba(150,110,255,.55)}
  100%{transform:scale(1.2);box-shadow:0 0 12px 4px rgba(190,175,255,.85),0 0 30px 11px rgba(150,110,255,.4)}
}

/* Das Herausschießen: eine reine Schein-Ebene im Punkt, die aufreißt und
   sich als weicher Hof auflöst. Getrennt vom Kern, weil der Punkt stehen
   bleiben soll, während das Licht ihn verlässt.
   --burst trägt die Farbe, damit oben rechts und unten links dieselbe
   Mechanik mit unterschiedlichem Ton fahren können. */
/* Der Schein ist ein radialer Verlauf, KEIN box-shadow: ein Schatten wird
   laut Spezifikation nicht innerhalb der eigenen Box gezeichnet. Beim
   Aufskalieren blieb dadurch mitten im Leuchten ein dunkler Kreis stehen —
   der sah aus wie ein schwarzer Impuls. Der Verlauf füllt die Fläche bis zur
   Mitte und skaliert sauber mit. Nebeneffekt: nur noch opacity + transform,
   also reine Compositor-Arbeit statt Neuzeichnen des Schattens pro Frame. */
.rail-burst{--burst:25,191,244;position:absolute;inset:0;border-radius:50%;opacity:0;pointer-events:none;
  background:radial-gradient(circle,rgba(var(--burst),.8) 0%,rgba(var(--burst),.42) 26%,rgba(var(--burst),.12) 48%,rgba(var(--burst),0) 68%)}
.rail-anchor .rail-burst{--burst:150,110,255}
.is-energized .rail-burst{animation:burstOut 4.6s cubic-bezier(.14,.66,.24,1) .8s forwards}
/* Größere Skalen als früher: der Verlauf endet bei 68 % seines Radius, der
   frühere Schatten strahlte über die Boxkante hinaus. Gleiche Reichweite
   braucht deshalb mehr Skalierung. */
@keyframes burstOut{
  0%{opacity:0;transform:scale(1)}
  12%{opacity:1;transform:scale(7)}
  55%{opacity:.55;transform:scale(26)}
  100%{opacity:0;transform:scale(52)}
}
/* Auf Desktop enden BEIDE Leistenköpfe in derselben Ecke unten links —
   dort darf nur einmal gezündet werden, sonst ist die Stelle doppelt hell. */
.scroll-rail--bottom .rail-burst{display:none}
/* ...das gilt aber NUR ganz unten. Seit die Choreografie einen Rückscroll
   überlebt, kann sie zünden, während man schon weiter oben steht — dann sind
   die beiden Köpfe weit auseinander und jeder zündet an seiner eigenen Stelle.
   Der Burst sitzt im wandernden Kopf, wird also von selbst dorthin getragen;
   nötig ist allein, den unteren wieder freizugeben.
   Die Klasse setzt site-static.js im Moment des Zündens anhand des dann
   aktuellen Scrollstands — bewusst dort und nicht per :not(.is-line-running),
   weil ein Wechsel von display mitten in der Animation sie neu starten ließe
   und der Burst dann verspätet käme. */
.is-burst-getrennt .scroll-rail--bottom .rail-burst{display:block}

/* Nach dem Finale sind die Checkpoint-Impulse violett statt cyan — der
   Rückweg fühlt sich dadurch anders an als der Hinweg. Nur der Impuls-Ring:
   die Köpfe selbst bleiben cyan, sonst widerspräche das der Cyan-Quelle
   unten links. */
.is-finaled .rail-head:after{border-color:rgba(150,110,255,.95)}

/* Nach dem Finale bekommen ALLE dunklen Sektionen ihren Hintergrund-Glow —
   der Rückweg nach oben führt durch eine veränderte Seite. Bewusst OHNE
   eigenen Impuls und mit langem, weichem Übergang: das soll man beim
   Hochscrollen vorfinden, nicht als Ereignis erleben.
   Bewusst nur in den Ecken und schwach — der Text darf nichts verlieren. */
.section-aurora{position:absolute;z-index:-1;inset:0;opacity:0;pointer-events:none;transition:opacity 1.9s ease}
.is-finaled .section-aurora{opacity:1}
.hero .section-aurora{
  background:
    radial-gradient(46% 40% at 97% 14%,rgba(124,92,255,.17),transparent 72%),
    radial-gradient(38% 36% at 90% 74%,rgba(25,191,244,.12),transparent 70%)}
.shopcase .section-aurora{
  background:
    radial-gradient(44% 42% at 2% 97%,rgba(25,191,244,.17),transparent 70%),
    radial-gradient(40% 38% at 99% 6%,rgba(124,92,255,.13),transparent 72%)}

/* Zündstelle: dort, wo der Kopf der Leiste steht, geht im Hintergrund der
   Sektion ein Schein auf. Der Kopf wandert mit dem Scrollstand, also zieht er
   den Schein durch die Seite — jede Sektion, durch die er läuft, bekommt ihren
   eigenen Fleck und behält ihn. Zwei Durchgänge sehen dadurch nie gleich aus.
   JEDER Punkt bekommt seinen eigenen Schein, nicht nur der wandernde Kopf:
   auf Desktop sind das der Kopf der linken Leiste, der Kopf der Fußleiste und
   der feste Anker oben rechts (der immer aus derselben Ecke schießt); mobil
   der obere und der untere Kopf, den Anker gibt es dort nicht.
   Deshalb ist .zuend-glow nur noch die HÜLLE über der ganzen Sektion, und die
   einzelnen Flecken sind .zuend-punkt darin — eine Sektion kann so mehrere
   gleichzeitig tragen. Angelegt werden sie von site-static.js, Position per
   --zg-x/--zg-y in Prozent der Sektion.
   JEDE Sektion hat eine Hülle, auch die hellen — sonst blieben beim
   Hochscrollen Lücken, in denen ein Punkt sichtbar über totem Grund läuft.
   z-index:-1 setzt die Ebene über den Sektionshintergrund, aber unter den
   Text — dieselbe Mechanik wie bei .section-aurora. Das setzt voraus, dass die
   Sektion einen eigenen Stapelkontext aufmacht (isolation:isolate); bei proof
   und about steht das jetzt eigens dafür in der Sektionsregel. */
/* overflow:hidden auf der Hülle, nicht auf der Sektion: der Schein ist bis zu
   880px groß und ragte gemessen um mehrere hundert Pixel über seine Sektion
   hinaus. Nach unten deckt die nächste Sektion das zwar zu (sie zeichnet
   später und ist deckend), nach oben aber malte er über die vorherige —
   Licht, das im falschen Container landet. Nur .contact hatte overflow:hidden
   und war deshalb als einzige dicht.
   Der Beschnitt fällt nicht auf, weil die Sektionsgrenze ohnehin eine harte
   Farbkante ist: das Licht endet dort, wo der Container endet — genau das ist
   die Idee. Auf der Sektion selbst würde overflow:hidden dagegen die
   überstehenden Karten im Hero abschneiden. */
.zuend-glow{position:absolute;z-index:-1;inset:0;overflow:hidden;pointer-events:none}
/* Der Schein BREITET SICH AUS, er blendet nicht bloß auf: er startet klein am
   Punkt und wächst auf volle Größe. Nur so liest er sich als Licht, das aus
   dem Punkt kommt — ein Fleck, der einfach in voller Größe da ist und heller
   wird, wirkt wie eingeblendet statt ausgestrahlt.
   Deckkraft und Größe laufen gemeinsam über 2,2s; die Größe mit weichem
   Ausklang, damit das Wachsen am Ende ausläuft statt zu stoppen. */
.zuend-punkt{position:absolute;top:var(--zg-y,50%);left:var(--zg-x,0%);
  width:clamp(360px,56vw,880px);aspect-ratio:1;opacity:0;pointer-events:none;
  transform:translate(-50%,-50%) scale(.32);
  transition:opacity 2.2s ease,transform 2.2s cubic-bezier(.19,.72,.26,1);
  background:radial-gradient(circle,
    rgba(var(--zg-farbe,25,191,244),.20) 0%,
    rgba(var(--zg-farbe,25,191,244),.085) 34%,
    rgba(var(--zg-farbe,25,191,244),0) 66%)}
/* Der Anker oben rechts schießt violett heraus — sein Schein trägt dieselbe
   Farbe, sonst käme aus einem violetten Punkt ein cyanes Leuchten. */
.zuend-punkt[data-q="anker"]{--zg-farbe:150,110,255}
.is-finaled .zuend-punkt{opacity:1;transform:translate(-50%,-50%) scale(1)}
/* Beim Zurücksetzen des Finales gehen die Flecken wieder auf null. Ohne das
   standen sie beim ZWEITEN Anlauf schon ab dem ersten Frame auf voller
   Deckkraft — die Aurora war also fertig da, bevor die Choreografie sie
   überhaupt erreicht hatte, und das las sich als harter Sprung.
   Sie verschwinden nicht, sie blenden über dieselben 2,2s aus wie sie kommen;
   beim erneuten Zünden wachsen sie wieder von vorn heraus.
   is-finaled bleibt davon unberührt — die Sektions-Auroras sollen ja stehen
   bleiben, der Rückweg führt weiter durch eine veränderte Seite.
   MUSS nach der is-finaled-Regel stehen: gleiche Spezifität, das spätere
   Vorkommen entscheidet. */
.is-zuend-aus .zuend-punkt{opacity:0;transform:translate(-50%,-50%) scale(.32)}
/* Auf hellem Grund würde additives Cyan nur ausbleichen. multiply legt die
   Farbe darüber wie Licht durch einen Filter. Der eigene Stapelkontext der
   Sektion hält die Mischung in ihr drin.
   DEUTLICH schwächer als auf dunklem Grund, und das ist keine Geschmacksfrage:
   dort steht dunkler Text, jede Tönung senkt also den Kontrast. Maßgeblich ist
   die kleine Schrift der Augenbrauen-Zeile — sie hat auf hellem Grund den
   knappsten Kontrast. Gerechnet für den ungünstigsten Fall, den Mittelpunkt
   des Flecks direkt unter der Schrift (WCAG AA verlangt 4,5:1):
     alte Farbe #00719a auf .about, ohne Fleck   4,26:1   durchgefallen
     alte Farbe #00719a auf .about, mit .20      3,74:1   durchgefallen
     neue Farbe #005f82 auf .about, mit .20      4,73:1   bestanden
     neue Farbe #005f82 auf .proof, mit .20      5,64:1   bestanden
   Die alte Schriftfarbe fiel auf .about also schon OHNE Fleck durch — ein
   bestehender Fehler, mit #005f82 gleich mit behoben.
   Mehr als .20 darf hier trotzdem nicht stehen, sonst rutscht .about wieder
   unter die Schwelle. */
/* Auf hellem Grund bleibt es IMMER cyan, auch beim Anker: Violett ist dunkler
   und würde den Kontrast weiter drücken als die oben gerechneten Werte.
   [data-q] hebt die Spezifität an, damit die Quellenfarben es nicht kippen. */
.proof .zuend-punkt[data-q],.about .zuend-punkt[data-q]{
  --zg-farbe:25,191,244;
  mix-blend-mode:multiply;
  background:radial-gradient(circle,
    rgba(25,191,244,.20) 0%,rgba(25,191,244,.09) 34%,rgba(25,191,244,0) 66%)}

/* Untere Leiste füllt sich rechts -> links, ihr Kopf läuft entsprechend
   entgegengesetzt und startet an der rechten Kante. */
.scroll-rail--bottom .rail-head{top:auto;bottom:0;left:auto;right:0;margin:0;transform:translateX(calc(var(--sp,0)*var(--rail-len,0px)*-1))}

@supports (animation-timeline:scroll()){
  .scroll-rail__fill{animation:railY linear both;animation-timeline:scroll(root block)}
  .scroll-rail--bottom .scroll-rail__fill{animation:railXRev linear both;animation-timeline:scroll(root block)}
  .rail-head{animation:headY linear both;animation-timeline:scroll(root block)}
  .scroll-rail--bottom .rail-head{animation:headXRev linear both;animation-timeline:scroll(root block)}
  @keyframes railY{from{transform:scaleY(0)}to{transform:scaleY(1)}}
  @keyframes railXRev{from{transform:scaleX(0)}to{transform:scaleX(1)}}
  @keyframes headY{from{transform:translateY(0)}to{transform:translateY(var(--rail-len))}}
  @keyframes headXRev{from{transform:translateX(0)}to{transform:translateX(calc(var(--rail-len)*-1))}}
}

@media(max-width:768px){
  /* Mobile: Balken ganz oben (der Header scrollt dort weg), ebenfalls ohne Spur */
  .scroll-rail{z-index:30;top:0;bottom:auto;right:0;left:0;width:auto;height:3px}
  /* Schlusslinie ist an den fixierten Header gebunden — den gibt es mobil
     nicht (er scrollt weg), darum entfällt sie hier */
  /* Beide hängen am fixierten Header — den gibt es mobil nicht */
  .rail-close,.rail-anchor{display:none}
  /* Mobil nur WhatsApp — E-Mail ist über den Kontakt-Button erreichbar */
  .contact-dock .dock-btn:not([data-wa-widget]){display:none}
  /* Position des Docks steht im ZWEITEN 768er-Block weiter unten — dieser
     hier läuft vor den Basisregeln und würde bei gleicher Spezifität
     verlieren. */
  /* Die untere Linie behält ihre Position und Richtung auch mobil */
  .scroll-rail--bottom{z-index:30;top:auto;bottom:0}
  .scroll-rail__fill{background:linear-gradient(to right,rgba(25,191,244,.35),var(--color-cyan));transform:scaleX(var(--sp,0));transform-origin:left center}
  /* Waagerechte Leiste -> der Kopf wandert in X statt in Y */
  /* Mobil liegt diese Leiste am oberen Rand — Punkt entsprechend nach
     unten bündig statt darüber hinaus. */
  .rail-head{top:0;left:0;margin:0;transform:translateX(calc(var(--sp,0)*var(--rail-len,0px)))}

  /* Rahmen ums Display: die beiden Seitenleisten schließen oben und unten an.
     top/bottom 3px = Höhe der waagerechten Balken -> sie stoßen an, statt sich
     in den Ecken zu überlagern (doppelt gezeichnetes Cyan wäre dort heller).
     MUSS nach der .scroll-rail-Regel oben stehen (gleiche Spezifität). */
  .scroll-rail--right,.scroll-rail--left{display:block;z-index:30;top:3px;bottom:3px;width:3px;height:auto}
  .scroll-rail--right{right:0;left:auto}
  .scroll-rail--left{left:0;right:auto}
  /* Die Seitenleisten hängen NICHT am Scrollfortschritt — sie zeichnen sich
     erst, wenn das Seitenende erreicht ist, und schließen dann den Rahmen.
     Bis dahin stehen sie auf scaleY(0), sind also unsichtbar. */
  /* Durchgehend volles Cyan, KEIN Verlauf. Vorher liefen beide von 35 % auf
     volle Deckkraft, damit ihr blasses Ende weich an die waagerechte Leiste
     in der Ecke anschließt. Das kostete aber genau da Helligkeit, wo der
     Rahmen sich schließt: die Seiten wirkten matter als oben und unten,
     obwohl sie denselben Lauf abschließen. Die Ecken stoßen ohnehin sauber
     an (top/bottom 3px), ein Übergang ist dafür nicht nötig. */
  .scroll-rail--right .scroll-rail__fill{background:var(--color-cyan);transform:scaleY(0);transform-origin:top center}
  .scroll-rail--left .scroll-rail__fill{background:var(--color-cyan);transform:scaleY(0);transform-origin:bottom center}
  /* rechts von oben runter (Fortsetzung des oberen Balkens),
     links von unten hoch (Fortsetzung des unteren) */
  /* :not(.is-framed) ist wesentlich: war der Rahmen schon einmal zu und man
     erreicht das Ende erneut, stehen kurzzeitig beide Klassen. Dann darf der
     Aufbau NICHT nochmal laufen — die Leisten sind an der Stelle bereits zu
     99 % gefüllt, ein Neustart bei scaleY(0) wäre ein sichtbarer Sprung. */
  .is-line-running:not(.is-framed) .scroll-rail--right .scroll-rail__fill,
  .is-line-running:not(.is-framed) .scroll-rail--left .scroll-rail__fill{animation:railGrowY 2s cubic-bezier(.35,0,.15,1) forwards}
  @keyframes railGrowY{from{transform:scaleY(0)}to{transform:scaleY(1)}}
  /* Rückweg: Sobald man das Ende verlässt, hängen die Seitenleisten am
     Scrollfortschritt statt einfach zu verschwinden — sie ziehen sich mit
     dem Hochscrollen zurück und sind oben angekommen genau leer.
     (Fallback ohne scroll-driven animations; die native Variante steht im
     @supports-Block darunter.) */
  .is-framed .scroll-rail--right .scroll-rail__fill,
  .is-framed .scroll-rail--left .scroll-rail__fill{animation:none;transform:scaleY(var(--sp,0))}
  @supports (animation-timeline:scroll()){
    .is-framed .scroll-rail--right .scroll-rail__fill,
    .is-framed .scroll-rail--left .scroll-rail__fill{
      animation:railGrowY linear both;animation-timeline:scroll(root block)}
  }

  @supports (animation-timeline:scroll()){
    .scroll-rail__fill{animation:railX linear both;animation-timeline:scroll(root block)}
    .rail-head{animation:headX linear both;animation-timeline:scroll(root block)}
    /* Seitenleisten bewusst NICHT an die Scroll-Timeline hängen */
    .scroll-rail--right .scroll-rail__fill,
    .scroll-rail--left .scroll-rail__fill{animation:none}
    .is-line-running:not(.is-framed) .scroll-rail--right .scroll-rail__fill,
    .is-line-running:not(.is-framed) .scroll-rail--left .scroll-rail__fill{animation:railGrowY 2s cubic-bezier(.35,0,.15,1) forwards}
    /* Rückweg direkt auf der Scroll-Timeline: läuft off-main-thread und
       folgt dem Finger ohne Nachlauf. */
    @keyframes railX{from{transform:scaleX(0)}to{transform:scaleX(1)}}
    @keyframes headX{from{transform:translateX(0)}to{transform:translateX(var(--rail-len))}}
  }
}

/* Schlusslinie: sobald beide Linien voll sind (Seitenende erreicht), läuft
   unter dem Header eine Linie von rechts nach links und schließt den Rahmen. */
/* left:3px = Breite der senkrechten Linie. Ohne den Versatz überlagern sich
   beide in der Ecke (0/80) und Cyan wird dort doppelt gezeichnet -> heller
   Punkt. So stößt die Querlinie sauber an die senkrechte an.
   KEIN overflow:hidden: das schnitt den eigenen box-shadow auf 3px Höhe ab.
   Die untere und die linke Leiste strahlen frei aus und haben dadurch diesen
   weichen Schein über sich — oben fehlte er komplett, die Linie wirkte
   trocken. Die Füllung skaliert sich selbst per transform, ein Beschnitt ist
   dafür nicht nötig. */
.rail-close{position:fixed;z-index:19;top:var(--header-h);right:0;left:3px;height:3px;pointer-events:none}
/* Verlauf: links blass, rechts kräftig — so trifft die Querlinie in der
   oberen linken Ecke auf das ebenfalls blasse obere Ende der senkrechten
   Linie und die beiden gehen sauber ineinander über. */
.rail-close__fill{display:block;width:100%;height:100%;background:linear-gradient(to right,rgba(25,191,244,.35),var(--color-cyan));box-shadow:0 0 14px rgba(25,191,244,.5);transform:scaleX(0);transform-origin:left center}
@keyframes railClose{from{transform:scaleX(0)}to{transform:scaleX(1)}}
/* Rückweg: statt am Ende schlagartig zu verschwinden, zieht sich die Querlinie
   mit dem Hochscrollen zurück — dieselbe Kopplung wie bei den Seitenleisten.
   html:not(.is-framed) an der Zeichenregel ist nötig, weil .rail-close.is-closed
   (0,3,0) sonst die is-framed-Regel (0,2,0) schlägt und die Linie beim erneuten
   Erreichen des Endes von vorn gezeichnet würde. */
.rail-close.is-closed .rail-close__fill{animation:railClose 2.4s cubic-bezier(.35,0,.15,1) forwards}
/* Rückweg der QUERLINIE unter der Navigation: sie BLENDET AUS (Deckkraft
   1->0), sie zieht sich NICHT zurück. Ein Zusammenziehen quer unter der
   Navigation wirkte zu unruhig — die Linie soll dort still verlöschen.
   Das ist bewusst anders als bei der linken und unteren Leiste, die sich mit
   dem Hochscrollen zurückziehen (die haben eine Richtung, an der das Auge
   entlangläuft; die Querlinie unter der Navi hätte keine).
   Eigene Animation statt Transition: fällt eine forwards-Animation weg,
   springt der Wert ohne Übergang — eine Transition greift dort nicht.
   Der Glow zieht 0,4s später nach, damit der Schein nicht gleichzeitig mit der
   Linie verschwindet — sonst wirkt es wie ein Lichtschalter.
   :not(.is-closed) ist wesentlich: erreicht man das Ende erneut, muss die
   Linie wieder erscheinen. */
.is-framed .rail-close:not(.is-closed) .rail-close__fill{
  animation:railFadeOut 2.2s cubic-bezier(.35,.02,.5,1) both;transform:scaleX(1)}
.is-framed .rail-close:not(.is-closed) .rail-glow{
  animation:railFadeOut 2.2s cubic-bezier(.35,.02,.5,1) .4s both}
/* Flacher Ausklang statt linear: Deckkraft nimmt das Auge nicht linear wahr,
   der Verlauf hält die Linie erst fast unverändert und gibt sie dann langsam
   ab. (War versehentlich verloren gegangen, als der Rückzug-Versuch die alte
   Fade-Definition mitentfernt hatte — die Linie blieb dadurch stur stehen.) */
@keyframes railFadeOut{
  0%{opacity:1}
  22%{opacity:.9}
  60%{opacity:.42}
  100%{opacity:0}
}

/* Rückweg des Glows an Seiten- und Fußleiste: KEIN Ausblenden (Deckkraft),
   sondern ein Lauf IN DIE ECKE. Der Schein bleibt beim Hochscrollen zunächst
   voll stehen (is-energized), während die Füllung (Linie) schon scroll-gebunden
   schrumpft. Fällt is-energized bei ~40 % Rückscroll weg, skaliert der Glow über
   die Basisregel smooth in seinen Ursprung: senkrechte Leiste nach unten
   (scaleY(0), origin bottom), untere Leiste nach links (scaleX(0), origin left)
   — beide in die untere linke Ecke, 4,6s, scroll-unabhängig.
   Also: was ÜBER dem Scrollpunkt liegt, schrumpft scroll-gebunden mit der Linie;
   was DARUNTER bleibt, läuft als Ganzes weich in die Ecke — kein Fade.
   Bewusst KEINE is-framed-Überlagerung mehr hier: die hatte den Eck-Lauf
   entweder ausgeblendet (railFadeOut) oder an den Scroll gekoppelt (scaleY(--sp))
   und damit genau diese Bewegung aufgehoben. */

/* ---- „Unter Strom": Helligkeit wandert die Linien entlang -----------------
   Sobald alle drei Linien voll sind, breitet sich von den hellsten Stellen
   (unten links, wo sich zwei Linien treffen; oben rechts, wo die Querlinie
   endet) volles Cyan aus, bis der ganze Rahmen gleichmäßig leuchtet.
   Bewusst als transition (nicht animation): beim Zurückscrollen fährt der
   Zustand von selbst weich zurück.
   ABSCHALTEN: in site-static.js ENERGIZE auf false setzen. */
/* Zwei Schichten: ::before ist die wandernde Welle mit weich auslaufender
   Vorderkante, ::after legt danach die gleichmäßige Endhelligkeit darunter.
   Dadurch wirkt es fließend statt wie eine harte Kante, die vorbeischiebt. */
.rail-glow{position:absolute;inset:0;pointer-events:none}
/* Den Schein NICHT an den Scrollstand koppeln: er hat seinen eigenen
   Rückweg. Fällt is-energized bei 60% Scrollstand weg, zieht sich die Welle
   über 4,6s zu ihrem Ursprung zurück — bei der senkrechten Leiste nach
   unten, bei der unteren nach links. Eine Container-Skalierung überschrieb
   das und nahm dem Glow diese Bewegung. */
.rail-glow:before,.rail-glow:after{position:absolute;inset:0;content:""}
/* Desktop bewusst langsam: die Helligkeit soll durchziehen, während man den
   Abschlusstext liest — die Icons rechts kommen erst danach. */
.rail-glow:before{box-shadow:0 0 24px rgba(25,191,244,.7);transition:transform 4.6s cubic-bezier(.25,.55,.2,1)}
/* Zweite Glühschicht: legt sich erst im Endzustand dazu, dadurch wiegt das
   fertige Bild etwas schwerer als die vorbeiziehende Welle. Bewusst
   moderat — der Cyan-Button darf nicht im Leuchten versinken. */
.rail-glow:after{background:var(--color-cyan);box-shadow:0 0 20px rgba(25,191,244,.5);opacity:0;transition:opacity 2s ease}
/* Spezifität muss die Richtungsregeln unten schlagen: die sind (0,3,1),
   ein schlichtes `.is-energized .rail-glow:before` wäre nur (0,2,1) und
   würde verlieren — dann bliebe die Welle für immer auf scale(0) stehen. */
.is-energized .scroll-rail:not(.scroll-rail--bottom) .rail-glow:before,
.is-energized .scroll-rail--bottom .rail-glow:before,
.is-energized .rail-close .rail-glow:before{transform:none}
/* Verzögerungen nur im EIN-Zustand -> beim Zurückscrollen blendet alles
   sofort ab, statt erst zu warten. */
.is-energized .rail-glow:after{opacity:1;transition-delay:2.4s}
/* Die Querlinie oben zeichnet 2,4s (Start bei 1,7s). Ihr Glow darf erst
   loslaufen, wenn die Linie durch ist — sonst leuchtet es dort, wo noch gar
   keine Linie liegt. Dieser Startversatz von 0,8s wird über die kürzere
   Laufzeit ausgeglichen: 0,8 + 3,8 = 4,6s, also derselbe Ankunftsmoment wie
   bei der linken Leiste, die ohne Versatz 4,6s braucht. Beide Wellen treffen
   sich dadurch gleichzeitig in der oberen linken Ecke.
   Gleiche Geschwindigkeit UND gleichzeitige Ankunft geht nicht beides — mit
   dem Startversatz ist das eine gegen das andere getauscht. */
.is-energized .rail-close .rail-glow:before{transition-delay:.8s}

/* Senkrechte Linie: hellster Punkt unten -> Welle zieht nach oben */
.scroll-rail:not(.scroll-rail--bottom) .rail-glow:before{background:linear-gradient(to top,var(--color-cyan) 0%,var(--color-cyan) 48%,rgba(25,191,244,0) 100%);transform:scaleY(0);transform-origin:center bottom}
/* Untere Linie: hellster Punkt links -> zieht nach rechts */
.scroll-rail--bottom .rail-glow:before{background:linear-gradient(to right,var(--color-cyan) 0%,var(--color-cyan) 48%,rgba(25,191,244,0) 100%);transform:scaleX(0);transform-origin:left center}
/* Querlinie: hellster Punkt rechts -> zieht nach links. Gleiche Dauer wie
   die übrigen Leisten (4,6s aus der Basisregel). */
.rail-close .rail-glow:before{background:linear-gradient(to left,var(--color-cyan) 0%,var(--color-cyan) 48%,rgba(25,191,244,0) 100%);transform:scaleX(0);transform-origin:right center;transition-duration:3.8s}

/* ---- Kontakt-Dock (Mail + WhatsApp) --------------------------------------
   Erscheint erst, wenn alle drei Linien durchgelaufen sind. Danach umrundet
   ein Punkt jeden Kreis einmal — als Fortsetzung der Navbar-Linie. */
/* Sitzt oberhalb des Footers (--footer-h wird gemessen), damit es den
   Footer-Text nie überdeckt. */
/* Grundposition ist die Bildschirmecke. Steht der Footer im Bild, hebt
   --dock-lift (site-static.js) das Dock um exakt dessen sichtbare Höhe an,
   sodass es nie über dem Footer-Text liegt. Angehoben wird per transform,
   nicht über bottom: das läuft auf dem Compositor und erzeugt beim Scrollen
   kein Layout. */
.contact-dock{position:fixed;z-index:31;right:clamp(18px,2.2vw,34px);bottom:calc(clamp(16px,1.8vw,26px) + env(safe-area-inset-bottom,0px));display:flex;gap:14px;pointer-events:none;transform:translateY(calc(var(--dock-lift,0px) * -1));will-change:transform}
/* Kein CSS-Rand — der Ring wird als SVG gezeichnet (siehe .draw-ring) */
.dock-btn{position:relative;display:grid;width:64px;height:64px;place-items:center;color:var(--color-cyan);background:rgba(2,9,20,.94);border-radius:50%;box-shadow:0 0 20px rgba(25,191,244,.26),0 10px 28px rgba(2,9,20,.55);opacity:0;transform:translateY(12px) scale(.9);visibility:hidden;pointer-events:auto;transition:opacity .38s ease,transform .5s cubic-bezier(.2,.8,.3,1),visibility 0s linear .38s,color .18s ease,box-shadow .18s ease}
.contact-dock.is-visible .dock-btn{opacity:1;transform:none;visibility:visible;transition-delay:0s}
/* Mobil im Hero weggeblendet (iOS-Toolbar schiebt fixe Elemente dort hoch).
   Muss NACH der is-visible-Regel stehen (gleiche Spezifitaet, spaeter gewinnt). */
.contact-dock.is-dock-hidden .dock-btn{opacity:0;transform:translateY(12px) scale(.9);pointer-events:none}
.dock-btn:hover,.dock-btn:focus-visible{color:#7ddcff;box-shadow:0 0 30px rgba(25,191,244,.5),0 10px 28px rgba(2,9,20,.55)}
.dock-btn:focus-visible{outline:3px solid var(--color-paper);outline-offset:4px}

/* Ring UND Symbol werden gezeichnet; der leuchtende Punkt läuft als Stift
   auf dem Ring mit. Ringumfang 2·π·31 = 194.78. */
.dock-ring{position:absolute;inset:0;width:100%;height:100%;overflow:visible;fill:none;stroke:currentColor;stroke-width:1.4}
.draw-ring{stroke-dasharray:194.78;stroke-dashoffset:194.78;transform:rotate(-90deg);transform-origin:32px 32px;opacity:.6}
.contact-dock.is-visible .draw-ring{animation:drawRing 1.5s cubic-bezier(.45,0,.25,1) .15s forwards}
@keyframes drawRing{to{stroke-dashoffset:0}}

/* Gefüllte Glyphen statt Konturen: eine Outline-Variante kostet gerade bei
   WhatsApp die Wiedererkennung. Das Symbol blendet auf, während der Ring
   noch gezogen wird. */
/* will-change: beide Glyphen bekommen eine eigene Compositor-Ebene, damit
   sie trotz unterschiedlicher Pfadkomplexität exakt gleichzeitig aufblenden. */
.dock-icon{position:relative;width:27px;height:27px;opacity:0;transform:scale(.82);will-change:transform}
/* Symbol bleibt ruhig stehen — jede Bewegung gegen den stehenden Ring
   liest sich als Ruckeln. */
.contact-dock.is-visible .dock-icon{animation:iconIn .5s cubic-bezier(.2,.8,.3,1) .92s forwards}
@keyframes iconIn{to{opacity:1;transform:none}}

/* --- Mobile Navigation ("Magnetic Handle") -------------------------------
   Steht seit 26.07.2026 in assets/css/edge-nav.css und wird von ALLEN Seiten
   geteilt (Startseite, /websites, /shops, /software). Was hier stand, war die
   Erstfassung und ist dort 1:1 uebernommen; die Farben laufen dort ueber
   Fallback-Ketten, damit dieselbe Datei im dunklen und im hellen System passt.
   HIER NICHTS NACHBAUEN: site.css wird nach edge-nav.css geladen, doppelte
   Regeln wuerden die geteilte Datei stillschweigend ueberschreiben. */

/* Lichtwelle: ein feiner Ring läuft nach außen und verliert sich.
   Zwei versetzte Wellen ergeben ein ruhiges Nachlaufen statt eines Blinkens.
   Nur transform + opacity -> reine Compositor-Arbeit, kein Neuzeichnen.
   Takt und Frequenz sind bewusst der Ursprungsstand. Zwischenzeitlich lagen
   hier ein Herzschlag-Rhythmus (zwei Schläge, dann Ruhe) und ein dichterer
   Desktop-Takt samt Beschleunigung beim Hovern — beides war unruhiger als
   diese gleichmäßige Welle. Wer es nochmal versuchen will, findet die
   Messwerte in der Sitzung; hier steht bewusst wieder der ruhige Stand. */
.dock-btn:before,.dock-btn:after{position:absolute;inset:0;content:"";border:1px solid rgba(25,191,244,.55);border-radius:50%;opacity:0;pointer-events:none}
.contact-dock.is-visible .dock-btn:before{animation:dockWave 5s cubic-bezier(.15,.5,.25,1) 1.8s infinite}
.contact-dock.is-visible .dock-btn:after{animation:dockWave 5s cubic-bezier(.15,.5,.25,1) 3.3s infinite}
@keyframes dockWave{
  0%{opacity:.5;transform:scale(1)}
  55%{opacity:0;transform:scale(1.42)}
  100%{opacity:0;transform:scale(1.42)}
}

/* Stift auf dem Ring — Startpunkt oben, einmal herum */
.pen{fill:var(--color-cyan);stroke:none;opacity:0}
@supports (offset-path:path("M0 0")){
  .pen{offset-path:path("M32 1 A31 31 0 1 1 31.99 1");offset-rotate:0deg}
  .contact-dock.is-visible .pen{animation:penDraw 1.5s cubic-bezier(.45,0,.25,1) .15s}
  @keyframes penDraw{
    0%{opacity:1;offset-distance:0%}
    88%{opacity:1}
    100%{opacity:0;offset-distance:100%}
  }
}
.dock-btn.is-placeholder{cursor:not-allowed}

/* Belohnung beim Hintergrund-Farbwechsel: klare Kante statt weichem Schein.
   Ein dünner Ring schießt heraus (Energie), der Kern schlägt kurz auf
   (Rückstoß). Nur transform + opacity — kein Neuzeichnen pro Frame, anders
   als beim vorherigen animierten box-shadow. */
.rail-head:after{position:absolute;inset:-1px;content:"";border-radius:50%;border:1.5px solid rgba(170,238,255,.95);opacity:0;pointer-events:none}
.rail-head.is-hit:after{animation:headRing .55s cubic-bezier(.1,.72,.2,1)}
.rail-head.is-hit:before{animation:headBump .45s cubic-bezier(.2,.8,.3,1)}
@keyframes headRing{
  0%{opacity:.95;transform:scale(1)}
  100%{opacity:0;transform:scale(4.6)}
}
@keyframes headBump{
  0%{transform:scale(1)}
  32%{transform:scale(1.85)}
  100%{transform:scale(1)}
}

/* Kein Glühen bei Kontrast-/Transparenzpräferenzen */
@media(prefers-reduced-transparency:reduce),(prefers-contrast:more){
  .scroll-rail__fill{box-shadow:none;background:var(--color-cyan)}
}

.skip-link{position:fixed;z-index:100;top:12px;left:12px;padding:12px 16px;color:var(--color-navy);background:var(--color-paper);transform:translateY(-160%);transition:transform .16s ease}
.skip-link:focus{transform:translateY(0)}

/* ---- Kopfleiste ---------------------------------------------------------
   Seit 26.07.2026 trägt die Startseite dieselbe Leiste wie /websites, /shops
   und /software: Raster, Maße und Verhalten kommen aus assets/css/site-nav.css
   (.topnav) plus assets/js/site-nav.js. Hier steht nur noch, was WIRKLICH nur
   die Startseite hat — Farben des dunklen Systems, die Stapelung und der
   Umschlag über hellen Sektionen. Der alte .site-header-Block ist damit weg;
   nichts davon hier nachbauen, site.css wird nach site-nav.css geladen und
   würde die geteilte Datei stillschweigend überschreiben. */

/* --page ist die Variable, mit der site-nav.css die Leiste einrückt. Ohne
   diese Zuweisung fällt sie auf ihren eigenen Rückfallwert zurück und das Logo
   stünde auf breiten Schirmen ~46px weiter innen als der Inhalt darunter. */
:root{--page:var(--page-gutter);
  --nav-bg:rgba(2,9,20,.97);
  --nav-linie:rgba(174,185,199,.16);
  /* Logo WEISS — ausdrücklicher Wunsch (Basti, 26.07.): „das logo in weiß, in
     blau sieht nicht so cool aus, also mit dem blauen X". Gilt für ALLE
     dunklen Leisten; der Rückfallwert in site-nav.css steht deshalb ebenfalls
     auf brightness(0) invert(1). Hier trotzdem ausgeschrieben, damit die
     Startseite weiß bleibt, falls jemand den geteilten Rückfall noch einmal
     dreht (das ist am 26.07. schon zweimal passiert).
     Über hellen Sektionen zieht die Kopie sie auf brightness(0) = flach dunkel,
     sonst stünde Weiß auf Weiß (siehe .topnav--invert weiter unten). */
  --nav-logo:brightness(0) invert(1);
  --nav-ruhe:#f5f8fccc;
  --nav-hell:var(--color-paper);
  --nav-akzent:var(--color-cyan);
  --nav-glow:rgba(25,191,244,.7)}

/* Stapelung wie beim alten Kopf. site-nav.css setzt 30 — damit läge die Leiste
   mobil VOR den Rahmenleisten (.scroll-rail, dort ebenfalls 30, aber früher im
   Markup) und verdeckte den oberen Fortschrittsbalken. */
.topnav{z-index:20}

/* Der Kopf über hellen Sektionen. Statt ihn umzufärben liegt eine zweite,
   helle Ausfertigung deckungsgleich darüber, die genau auf den Teil
   freigeschnitten wird, der schon über hellem Grund steht. Dadurch kippt er
   nicht auf einen Schlag, sondern die Kante wandert Stück für Stück durch —
   und beim Einrasten per Menüklick steht er sofort in der richtigen Farbe.
   Erzeugt wird die Kopie in site-static.js (cloneNode), angemeldet per
   data-topnav-light am Kopf. Ohne dieses Attribut passiert nichts — genau
   deshalb bleiben die Unterseiten von der Mechanik unberührt.
   Beschnitt über clip-path, nicht über Deckkraft: so gibt es keine Zone, in
   der beide Fassungen halbdurchsichtig übereinanderliegen und matschen. */
.topnav--invert{z-index:21;
  clip-path:inset(var(--kopf-hell-oben,0px) 0 var(--kopf-hell-unten,100%) 0);
  pointer-events:none}
.topnav--invert.is-scrolled{background:rgba(245,248,252,.97);border-bottom-color:rgba(2,9,20,.12)}
.topnav--invert .topnav__worlds a{color:rgba(2,9,20,.84)}
.topnav--invert .topnav__worlds a.is-current{color:var(--color-navy)}
/* Auf Weiß ist das helle Cyan zu schwach — der dunklere Ton der hellen Seiten. */
.topnav--invert .topnav__worlds a::after{background:#0784b0;box-shadow:none}
.topnav--invert .topnav__lang a{color:rgba(2,9,20,.55)}
.topnav--invert .topnav__lang a.is-current{color:var(--color-navy)}
/* brightness(0) legt die Marke geschlossen dunkel — sauberer als ein Filter,
   der das Blau in eine Fremdfarbe drehen würde. */
.topnav--invert .topnav__brand img{filter:brightness(0)}
/* Der Schein am CTA läuft hier BEWUSST mit. Über hellen Sektionen sieht man
   ausschließlich diese Kopie; ihr deckender heller Grund verdeckt den Schein
   des echten Kopfes darunter. Abgeschaltet hieße also: über Hell gar kein
   Schein. Doppeln kann er nicht, weil beide Fassungen einander per clip-path
   ergänzen — wo die eine sichtbar ist, ist die andere weggeschnitten. */
/* Mobil MUSS die Kopie weg: dort trägt die Leiste nur noch Logo und Griff,
   der Umschlag hat kein Ziel mehr und die Kopie liefe unsichtbar mit. */
@media(max-width:768px){.topnav--invert{display:none}}
/* Sobald die Querlinie steht, verschwindet der Rand der Leiste. Er liegt exakt
   auf ihrer Höhe und läuft über die volle Breite weiter — dadurch las man ihn
   als blasse Fortsetzung, und die Linie wirkte voll, obwohl ihr cyan Teil
   längst kurz war. Unten gibt es das Problem nicht, dort steht die Leiste
   frei. Gleiche Spezifität wie die is-scrolled-Regel, die Reihenfolge
   entscheidet. */
.is-line-running .topnav,
.is-framed .topnav{border-bottom-color:transparent}

/* Die Punkt-Schiene setzt die Sprungziele in site-nav.css auf 96px Abstand
   (.snav-an [id]). Auf der Startseite rasten die Sektionen aber bewusst mit
   ihrer OBERKANTE am Viewportrand ein — der Kopf darf sie überlagern, weil er
   sich über .topnav--invert der Sektionsfarbe anpasst und die Sektionen
   ohnehin 88-150px Innenabstand oben haben. Gleiche Spezifität, site.css wird
   später geladen und gewinnt. */
.snav-an [id]{scroll-margin-top:0}
.brand{display:inline-flex;width:150px;align-items:center}
.brand img{width:100%;height:auto}
.site-nav{display:flex;gap:clamp(24px,2.6vw,44px);align-items:center;justify-content:center;color:#f5f8fccc;font-size:.96rem;font-weight:580}
.site-nav a,.text-link{position:relative}
/* Kleiner Pfeil hinter dem Textlink: er zeigt, wohin der Klick führt — nach
   unten zu den Projekten. Als SVG statt Schriftzeichen, weil ein ↓ je nach
   Schrift unterschiedlich hoch sitzt und in Geist Mono gar nicht enthalten
   ist. Beim Hovern rutscht er ein Stück nach unten und kommt zurück; die
   Bewegung ist kurz genug, um nicht mit der Randnavigation zu konkurrieren. */
.text-link{display:inline-flex;align-items:center;gap:9px}
.text-link__pfeil{width:11px;height:14px;flex:none;overflow:visible;
  fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;
  transition:transform .32s cubic-bezier(.22,.75,.28,1)}
.text-link:hover .text-link__pfeil,
.text-link:focus-visible .text-link__pfeil{transform:translateY(3px)}
/* Der Unterstrich folgt der Zeigerrichtung: --ul-ein ist die Seite, an der die
   Maus eintritt (dort wächst er los), --ul-aus die, an der sie hinausgeht
   (dorthin zieht er sich zusammen). Beide setzt site-static.js.
   Fallback links/rechts = das frühere feste Verhalten, greift auch bei
   Tastaturfokus, wo es keine Zeigerrichtung gibt. */
.site-nav a:after,.text-link:after{position:absolute;right:0;bottom:-7px;left:0;height:1px;content:"";background:var(--color-cyan);transform:scaleX(0);transform-origin:var(--ul-aus,right);transition:transform .22s ease}
.site-nav a:hover:after,.site-nav a:focus-visible:after,.text-link:hover:after,.text-link:focus-visible:after{transform:scaleX(1);transform-origin:var(--ul-ein,left)}
.header-actions{display:flex;align-items:center;justify-content:flex-end;gap:26px}
.language{color:#f5f8fcad;font-family:"Geist Mono",monospace;font-size:.76rem;letter-spacing:.08em}
.language strong{color:var(--color-cyan);font-weight:700}

.button{display:inline-flex;min-height:54px;padding:0 28px;align-items:center;justify-content:center;color:#001019;background:var(--color-cyan);border:1px solid var(--color-cyan);font-weight:720;transition:color .18s ease,background-color .18s ease,transform .18s ease}
.button:hover,.button:focus-visible{color:var(--color-paper);background:transparent;transform:translateY(-2px)}
/* Ziel der Querlinie: sie endet genau hier oben rechts.
   Der frühere Schein (inset:-12px) und der Ring (scale 1.36) liefen ins
   „DE / EN" hinein — der neue Saum bleibt mit 3px + 8px Blur darunter. */
/* Zwei volle Cyan-Flächen gleichzeitig auf dem Schirm konkurrieren — beide
   schreien gleich laut, also führt keiner. Der Button in der Navigation ist
   deshalb dauerhaft der ruhigere: dunkel gefüllt mit cyanem Rand und cyaner
   Schrift. Der große Aufruf im Kontaktbereich bleibt der einzige, der als
   volle Fläche auftritt — dort wird geklickt.
   Der Grund ist DECKEND, nicht durchscheinend: über den hellen Sektionen kam
   sonst der helle Hintergrund durch und der Button sah ausgewaschen aus.
   Deckend bleibt er auf jedem Untergrund derselbe Chip — und der Außenglow
   des Finales legt sich sauber darum, statt durch ihn hindurchzuscheinen.
   Hier lief zwischenzeitlich eine scrollgekoppelte Füllung, die dem Wechsel
   von hell zu dunkel folgte. Sie funktionierte, war aber mehr Bewegung, als
   die Stelle verträgt. */
.button--header{position:relative;min-height:48px;padding-inline:22px;font-size:.9rem;
  color:var(--color-cyan);background:var(--color-navy);border-color:rgba(25,191,244,.55)}
.button--header:hover,.button--header:focus-visible{
  /* Grund bleibt DECKEND dunkel, auch im Hover. Vorher lief er auf
     rgba(...,.16) — halbdurchsichtig, und der kreisende Außenglow (liegt
     z-index:-1 hinter dem Button) schien mitten durch die Fläche durch, was
     im Inneren violett waberte. Deckend deckt den Glow-Kern ab: sichtbar
     bleibt nur der Hof außen herum. Feedback trägt jetzt die hellere Schrift
     und der volle Rand. */
  color:#8fe3ff;background:var(--color-navy);border-color:var(--color-cyan);
  /* KEIN transform-Lift wie beim Basis-Button: transform erzeugt einen
     Stapelkontext, und dann malt der Außenglow (z-index:-1) plötzlich ÜBER
     den Button-Grund statt dahinter — er waberte dadurch durchs Innere.
     Ohne Lift bleibt der Grund die unterste Ebene und deckt den Glow-Kern ab,
     sichtbar bleibt nur der Hof außen herum. Feedback trägt Rand und Schrift. */
  transform:none}
/* Fortsetzung der ankommenden Linie: ihre Energie zieht von links nach
   rechts durch den Button hindurch, statt außen herum zu blühen.
   Nur transform + opacity -> reine Compositor-Arbeit, kein Neuzeichnen. */
/* Außenglow statt Kantenlauf: ein farbiger Schein wandert dauerhaft um den
   Button. Er zündet NICHT beim Hover, sondern genau dann, wenn die Querlinie
   rechts oben ankommt — die ankommende Energie geht in den Button über.
   Der Verlauf liegt hinter dem Button (z-index:-1) und ragt nur 3px hervor,
   sichtbar ist also allein der weichgezeichnete Saum.
   Drei Farben, alle kalt — Magenta ist raus, mit ihm sprang der Saum quer
   über den Farbkreis und wirkte bunt statt edel:
     #19bff4 SNX-Cyan       Basis, dieselbe Farbe wie der Button selbst
     #0e3fb5 Deep Blue      dunkelste Stelle, bringt Tiefe und Kontrast
     #7c5cff Electric Purple  liegt direkt neben Blau, bleibt also kalt
   Erste und letzte Stufe sind identisch (Cyan), sonst hätte der Verlauf
   beim Umlauf eine sichtbare Naht.
   overflow:hidden musste weichen — ein Außenglow braucht Platz außerhalb.
   Die 26px Abstand zum „DE / EN" reichen dafür: 3px Überstand plus 8px
   Weichzeichnung bleiben deutlich darunter.

   KREISEN statt SCHIEBEN: vorher lag ein linear-gradient darunter, dessen
   background-position hin und her lief. Der Schein wanderte damit immer nur
   quer über den Button — links raus, rechts rein — und las sich dadurch flach
   und vorhersehbar. Jetzt liegt ein conic-gradient darunter, dessen Startwinkel
   rotiert: das Licht läuft tatsächlich AUSSEN HERUM, so wie die Energie, die
   aus der Leiste in den Button übergegangen ist.
   Zwei Ebenen, gegenläufig und mit unterschiedlicher Dauer (7s / 11s): die
   Grundfarbe kreist ruhig, darüber zieht ein einzelner heller Bogen in die
   Gegenrichtung. Weil sich 7 und 11 erst nach 77s wieder decken, wiederholt
   sich das Bild innerhalb der Lebensdauer nie sichtbar — genau das nimmt dem
   Effekt das Mechanische. */
@property --cta-dreh{syntax:"<angle>";inherits:false;initial-value:0deg}
.button--header,.contact .button{position:relative}
.button--header:before,.contact .button:before,
.button--header:after,.contact .button:after{
  content:"";position:absolute;inset:-3px;z-index:-1;
  filter:blur(8px);opacity:0;pointer-events:none}
.button--header:before,.contact .button:before{
  background:conic-gradient(from var(--cta-dreh),#19bff4,#0e3fb5,#7c5cff,#19bff4,#0e3fb5,#7c5cff,#19bff4)}
/* Der helle Bogen: nur ein Ausschnitt des Kreises trägt Farbe, der Rest ist
   durchsichtig. Dadurch läuft eine erkennbare Lichtspitze um den Button,
   statt dass sich die Fläche gleichmäßig umfärbt. */
.button--header:after,.contact .button:after{
  background:conic-gradient(from var(--cta-dreh),
    rgba(124,92,255,0) 0deg,rgba(124,92,255,.85) 26deg,
    rgba(25,191,244,.9) 54deg,rgba(124,92,255,0) 88deg,
    rgba(124,92,255,0) 360deg)}
/* Beim Seitenende laeuft EIN schmaler Lichtimpuls ~1,6x um den Button und
   verlischt dann wieder — bewusst dezent: nur der schmale Bogen (:after), kein
   dauerhaft leuchtender Vollring mehr (:before bleibt aus), weniger Blur (5
   statt 8px), geringere Deckkraft (.6 statt .9) und kurze Lebensdauer (~2,3s
   statt 36s). is-cta-glow haengt allein an der Uhr (JS-Timeout), nicht am
   Scrollstand — der Moment am Ende kann sich so erneut wiederholen. */
.is-cta-glow .button--header:after{
  filter:blur(5px);
  /* .8s Zuendverzoegerung: der Impuls startet erst, wenn die Querlinie oben
     rechts am Button ankommt — sonst leuchtet es, bevor die Linie da ist, und
     wirkt unharmonisch. Rotation und Deckkraft teilen dieselbe Verzoegerung. */
  animation:ctaDreh 1.4s linear .8s 1.6,ctaGlowPulse 2.3s ease .8s forwards}
@keyframes ctaDreh{to{--cta-dreh:360deg}}
@keyframes ctaGlowPulse{0%{opacity:0}16%{opacity:.6}58%{opacity:.6}100%{opacity:0}}

/* Höhen-Vertrag: --tri-h ist die EINE Wahrheit über die Kartenhöhe. Die
   Karten bekommen sie als Höhe, der Hero reserviert exakt denselben Betrag
   als Puffer unten — Textblock und Karten können sich damit geometrisch
   nicht mehr überlappen. Vorher hatten die Karten min 500px bei 820px Hero:
   auf 800er-Laptops blieben 320px für einen ~480px-Textstapel, die Buttons
   lagen gemessen 81px auf den Karten.
   --hero-pad federt oben mit: flache Screens geben Kopfraum ab, statt ihn
   dem Inhalt zu stehlen. */
.hero{--tri-h:clamp(380px,50vh,650px);--hero-pad:clamp(88px,13vh,118px);
  position:relative;min-height:max(100svh,820px);
  padding:var(--hero-pad) var(--page-gutter) var(--tri-h);
  isolation:isolate;background-color:var(--color-navy);background-image:url(/assets/signal-texture.webp);background-position:center;background-size:cover}
.hero:before{position:absolute;z-index:-1;inset:0;content:"";background:#02091475;pointer-events:none}
.hero__intro{position:relative;z-index:4;width:min(1060px,78vw)}
.eyebrow{margin:0 0 clamp(10px,1.8vh,16px);color:var(--color-cyan);font-family:"Geist Mono",monospace;font-size:.78rem;font-weight:700;letter-spacing:.16em}
h1,h2,h3,p{margin-top:0}
/* Der vh-Deckel ist der Laptop-Fix: die Breitenkurve allein machte die
   Headline auf 1280x800 gleich groß wie auf 1440x900 — nur ohne den Platz
   dafür. 8.8vh = zwei Zeilen belegen maximal ~17vh. */
h1{margin-bottom:clamp(10px,1.8vh,16px);font-size:min(clamp(3.4rem,5.45vw,5.55rem),8.8vh);font-weight:680;letter-spacing:-.058em;line-height:.96}
h1 span{display:block;opacity:0;transform:translateY(26px);transition:opacity .52s ease,transform .72s cubic-bezier(.2,.85,.25,1)}
h1 span:nth-child(2){transition-delay:90ms}
.is-ready h1 span{opacity:1;transform:translateY(0)}
.hero__support{margin-bottom:clamp(14px,2.6vh,24px);color:var(--color-muted);font-size:clamp(1rem,1.35vw,1.25rem);letter-spacing:-.01em}
.hero__actions{display:flex;align-items:center;gap:34px}
.text-link{color:var(--color-paper);font-weight:670}
/* Bündig mit dem Eyebrow statt fester 250px — es liest sich als rechte
   Spalte desselben Rasters. Der alte Pixelwert hing auf Laptops mitten im
   Kartenbereich. */
.hero__manifesto{position:absolute;z-index:4;top:calc(var(--hero-pad) + 2px);right:var(--page-gutter);display:grid;max-width:220px;margin:0;gap:8px;color:#f5f8fcad;font-family:"Geist Mono",monospace;font-size:.7rem;font-weight:600;letter-spacing:.13em;line-height:1.45}
/* Deko-Text kämpft nie mit Funktion um Platz: auf schmalen ODER flachen
   Screens verschwindet er ganz. Er sagt zudem fast dasselbe wie das Eyebrow —
   eng gestellt wäre er doppelt. */
@media(max-width:1199px),(max-height:859px){.hero__manifesto{display:none}}
.hero__manifesto:before{width:22px;height:2px;margin-bottom:4px;content:"";background:var(--page-accent);transition:background-color .24s ease}

.triptych{position:absolute;z-index:3;right:0;bottom:0;left:0;display:flex;height:var(--tri-h);padding-top:26px;align-items:stretch;overflow:visible;isolation:isolate}
/* Hier lag eine zweite Auflage der signal-texture über den Karten
   (z-index:8, mix-blend-mode:screen). Entfernt, weil sie kein gestaltetes
   Element war, sondern ein Skalierungsunfall: background-size:100% auto zog
   sie auf die volle Fensterbreite, wodurch sie je nach Fenster 1,8x bis 3,7x
   höher wurde als die Kartenreihe. Welcher Streifen sichtbar war, entschied
   allein die Fensterbreite — auf 1780px eine Linie mit Punkten, auf großen
   Schirmen gar nichts. Zudem hellte sie als screen-Ebene die Case-Bilder auf,
   also ausgerechnet den stärksten Inhalt der Seite. Mobil war sie ohnehin
   display:none. Die Textur bleibt als Hero-Hintergrund erhalten, dort sitzt
   sie kontrolliert. */
.project-panel{position:relative;z-index:1;display:flex;min-width:0;padding:0 clamp(22px,3vw,46px) 36px;flex:0 0 calc((100% + 7vw) / 3);align-items:flex-end;overflow:hidden;color:var(--color-paper);background:var(--color-navy-raised);clip-path:polygon(7% 7%,100% 0,93% 100%,0 100%);opacity:0;transform:translateY(68px);transition:flex-basis .62s cubic-bezier(.22,.61,.24,1) 0s,margin-left .62s cubic-bezier(.22,.61,.24,1) 0s,margin-right .62s cubic-bezier(.22,.61,.24,1) 0s,clip-path .62s cubic-bezier(.22,.61,.24,1) 0s,opacity .52s ease var(--reveal-delay,0s),transform .72s cubic-bezier(.18,.75,.16,1) var(--reveal-delay,0s)}
.project-panel+.project-panel{margin-left:-3.5vw}
/* Karte 1 ist DAUERHAFT 1,71% höher als die Reihe und ragt entsprechend nach
   oben — im Ruhezustand schneidet der clip-path diesen Überstand einfach weg.
   Grund: beim Aufziehen soll die obere Schräge im ORIGINALWINKEL nach oben
   rechts weiterlaufen. Am rechten Rand liegt sie schon auf y=0, darüber
   existierte die Karte nicht — der Platz muss also her. Ihn erst beim Hover
   zu schaffen hieße, top und height mitzuanimieren: zwei Layout-Eigenschaften
   pro Frame extra. So bleibt die Box konstant und es wandert NUR die Maske.
   Die 1,71% sind fensterunabhängig, weil Breite und Zuwachs beide vw-basiert
   sind: 7% × 8,7vw / 35,67vw. Umgerechnet auf die höhere Box liegt der linke
   Schrägenpunkt bei 8,564%, die Ruhe-Oberkante bei 1,681%. Nur Letztere
   wandert beim Hover auf 0 — die sichtbare Geometrie im Ruhezustand bleibt
   dadurch exakt wie bei den anderen Karten.
   Bei Karte 2/3 dürfen top/height NICHT gesetzt werden: deren negative
   margin-top (-24/-48px) verlängert die gestreckte Höhe, damit alle unten
   bündig enden — eine feste Höhe bricht genau das. */
.project-panel:first-child{padding-left:var(--page-gutter);top:-1.71%;height:calc(100% + 1.71%);
  clip-path:polygon(0 8.564%,100% 1.681%,93% 100%,0 100%)}
.project-panel:first-child.is-active{clip-path:polygon(0 8.564%,100% 0,93% 100%,0 100%)}
.project-panel:last-child{padding-right:var(--page-gutter);clip-path:polygon(7% 7%,100% 0,100% 100%,0 100%)}
/* Staffelung gilt nur fuer das Einblenden beim Laden. Als transition-delay
   verzoegerte sie auch die Hover-Bewegung: Karte 2 stand 90ms still, Karte 3
   sogar 180ms, waehrend die verlassene Karte schon zurueckfuhr. Genau das
   las sich als Stocken. */
.project-panel:nth-child(2){margin-top:-24px;--reveal-delay:90ms}
/* -48px setzt die Treppe fort: Naht 2→3 fällt damit exakt so weit ab wie 1→2 */
.project-panel:nth-child(3){margin-top:-48px;--reveal-delay:.18s}
.is-ready .project-panel{opacity:1;transform:translateY(0)}
.project-panel__shade{position:absolute;inset:0;width:100%;height:100%}
/* Bild hat eine FESTE Breite und hängt an der linken Kartenkante — es ist auf
   die Karte "gedruckt", nicht durch ein mitwachsendes Fenster gecroppt.
   Sonst verschiebt object-position beim Breiterwerden das Motiv (~73 px),
   was wie ein Zoom aussieht. Breite > max. Panelbreite, damit nie eine
   Lücke entsteht. */
.project-panel__image{position:absolute;z-index:-3;top:0;left:0;width:clamp(520px,52vw,1200px);min-width:100%;height:100%;max-width:none;object-fit:cover;transform:scale(1.02)}
.project-panel--reifenservice .project-panel__image{object-position:60% center}
/* Auf schmalen Fenstern rutscht der Reifen aus der Karte. Das Bild ist
   bewusst breiter als die Karte (bei 1280px: 665px Bild gegen 456px Karte) —
   so gibt das Aufziehen beim Hover mehr Bild frei, statt zu verzerren. Die
   Karte schneidet also rechts ab, und genau dort liegt der Reifen.
   Verschoben wird deshalb der ganze Bildkasten nach links, nicht seine
   Größe: das Sichtfenster wandert dadurch nach rechts ins Bild hinein.
   Der Kasten bleibt dabei breit genug, die Karte deckend zu füllen. */
@media(max-width:1439px){
  /* min-width MUSS die Verschiebung ausgleichen: 100% bezieht sich auf die
     Karte und wächst beim Aufziehen mit, der Bildkasten reicht damit immer
     genau bis zur rechten Kartenkante. Ohne das klaffte im aufgezogenen
     Zustand gemessen eine Lücke von 11-12px. Die 4px sind Reserve gegen
     Rundung bei gebrochenen Gerätepixeln. */
  .project-panel--reifenservice .project-panel__image{
    left:-9vw;min-width:calc(100% + 9vw + 4px)}
}
/* GWS: Motiv (Mitarbeiter mit Weste) sitzt rechts in der Quelle -> Bild rechts
   verankern. Beim Wachsen kommt links Kontext dazu, das Motiv bleibt stehen. */
/* Die Shop-Karte trägt eine Kachel-Collage (1600x775, also doppelt so breit
   wie hoch). In der hochformatigen Karte bleibt davon nur ein Ausschnitt —
   links steht die große Aufnahme der Tintenkartuschen samt Kategorie-Pille,
   die drei kleinen Kacheln rechts liegen ohnehin außerhalb. Deshalb links
   ausgerichtet und leicht nach oben, damit die Pille nicht unter die
   Textspalte rutscht. */
.project-panel--shop .project-panel__image{object-position:left 34%}
/* Die Collage ist hell und kleinteilig — dieselbe stärkere Abdunklung wie beim
   Dashboard-Motiv, sonst kämpft die Beschriftung gegen die Kachel-Pillen. */
.project-panel--shop .project-panel__shade{background:#020914a8}
.project-panel--shop.is-active .project-panel__shade{background:#02091480}
/* Bild nach rechts aus der Clip-Schnittzone schieben, damit die komplette
   Feature-Sidebar sichtbar bleibt (links füllt der Panel-Hintergrund auf) */
/* left bewusst NICHT versetzt: ein Versatz legte links einen Streifen nackte
   Kartenfläche frei, und weil die Dashboard-Navigation im Bild einen anderen
   Dunkelton hat als die Karte, entstand dort eine sichtbare senkrechte Kante.
   Das Bild ist ohnehin breiter als die Karte — bündig am Rand deckt es alles
   ab, der Ausschnitt kommt über object-position. */
.project-panel--flowsnx .project-panel__image{object-position:left center}
.project-panel__shade{z-index:-2;background:#0209147a;transition:background-color .28s ease}
/* Helles Dashboard-Motiv braucht mehr Abdunklung, damit Label und Aktion lesbar bleiben */
.project-panel--flowsnx .project-panel__shade{background:#020914a8}
.project-panel--flowsnx.is-active .project-panel__shade{background:#02091480}
.project-panel__number{position:absolute;z-index:-1;left:clamp(26px,4vw,72px);bottom:100px;color:transparent;font-size:clamp(8rem,13vw,13rem);font-weight:690;letter-spacing:-.08em;line-height:.72;-webkit-text-stroke:2px rgba(245,248,252,.2)}
.project-panel__copy{position:relative;z-index:10;display:grid;width:min(100%,430px);padding:16px 18px 14px;gap:8px;text-shadow:0 2px 18px rgba(2,9,20,.9)}
.project-panel__category,.project-panel__action{color:var(--accent);font-family:"Geist Mono",monospace;font-size:.69rem;font-weight:720;letter-spacing:.12em}
/* EINE Größe für alle drei Karten. Vorher war GWS per Sonderregel auf 59 %
   heruntergesetzt, weil "Sicherheitsdienst" (17 Zeichen) sonst aus der Karte
   läuft — der Größenunterschied war also ein Notbehelf gegen Überlauf, keine
   Absicht. Nebeneinander gelesen wirkt das aber wie Hierarchie: die kleinste
   Karte liest sich als das kleinere Projekt.
   Der Faktor ist am längsten Wort bemessen: 2,65vw ist die größte lineare
   Kurve, bei der "SICHERHEITSDIENST" von 769px bis 1920px in seiner Karte
   bleibt (gemessen an der echten Glyphenkante, nicht an der Textbox). */
.project-panel__title{max-width:15ch;font-size:clamp(1.2rem,2.65vw,3rem);font-weight:720;letter-spacing:-.045em;line-height:.94;text-transform:uppercase}
.project-panel__title>span{display:block}
.project-panel__action{width:fit-content;margin-top:5px;padding-top:10px;border-top:1px solid var(--accent)}
/* Kein Bild-Zoom im Hover: die Karte wächst nur (flex), das Motiv bleibt ruhig */
/* Karte 2 und 3 verdrängen niemanden — sie ziehen sich nach links auf und
   legen sich über ihren linken Nachbarn. Die Karte wird dabei ECHT breiter
   (flex-basis), nicht skaliert: dadurch wird schlicht mehr vom Bild sichtbar,
   statt es zu strecken.
   Der zusätzliche Betrag wandert 1:1 in den negativen Außenabstand. Weil alle
   Karten eine feste Basis haben und flex-grow 0 ist, bleibt kein Restraum
   übrig, den der Container umverteilen könnte — die Nachbarn stehen exakt.
   KEIN z-index: die Karten stapeln sich in DOM-Reihenfolge, jede liegt also
   ohnehin über ihrer linken Nachbarin und bleibt unter der rechten. Genau so
   soll es sein. */
.project-panel:nth-child(2).is-active,
.project-panel:nth-child(3).is-active{
  flex-basis:calc((100% + 7vw) / 3 + 8.7vw);margin-left:calc(-3.5vw - 8.7vw)}


/* Karte 1 verdrängt: eigene Basis hoch, die beiden anderen entsprechend
   runter, Summe bleibt konstant.
   Bewusst NICHT wie Karte 2/3 über den Nachbarn gelegt: dafür bräuchte sie
   einen z-index-Wechsel, weil sie in DOM-Reihenfolge unter Karte 2 liegt.
   z-index ist diskret und damit nie weich — der Umschlag war als Ploppen
   sichtbar, beim Verlassen erschien Karte 2s Kante schlagartig wieder. Und
   sie fuhr dabei über den Text von Karte 2. Verdrängen hat beide Probleme
   nicht. */
.project-panel:first-child.is-active{flex-basis:calc((100% + 7vw) / 3 + 8.7vw)}
.triptych:has(.project-panel:first-child.is-active) .project-panel:not(:first-child){
  flex-basis:calc((100% + 7vw) / 3 - 4.35vw)}
.project-panel.is-active .project-panel__shade{background:#02091447}
.triptych:has(.project-panel.is-active) .project-panel:not(.is-active) .project-panel__image{filter:saturate(.55) brightness(.78)}

/* content-visibility:auto entfernt: falsche Platzhalterhöhen ließen Anker-Sprünge
   (#services/#shops) um mehr als eine Bildschirmhöhe überschießen. */
.proof,.about,.contact,.shopcase{padding:clamp(88px,10vw,150px) var(--page-gutter)}
/* position + isolation für die Zündstelle: ohne eigenen Stapelkontext landet
   ein Kind mit z-index:-1 hinter dem Hintergrund der Sektion und ist damit
   unsichtbar. Die dunklen Sektionen tragen isolation ohnehin schon. */
.proof{position:relative;isolation:isolate;color:var(--color-navy);background:var(--color-paper)}
.proof .eyebrow{color:#005f82}
.proof h2,.about h2,.contact h2,.shopcase h2{max-width:980px;margin-bottom:60px;font-size:clamp(2.8rem,5vw,5rem);font-weight:650;letter-spacing:-.055em;line-height:.98}
.proof__list{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));border-top:1px solid rgba(2,9,20,.2)}
.proof__list article{min-height:270px;padding:28px clamp(20px,2.7vw,42px) 24px 0}
.proof__list article+article{padding-left:clamp(20px,2.7vw,42px);border-left:1px solid rgba(2,9,20,.2)}
.proof__number{color:#00719a;font-family:"Geist Mono",monospace;font-size:.78rem;letter-spacing:.12em}
.proof h3{margin:0 0 18px;font-size:clamp(1.8rem,2.7vw,2.8rem);letter-spacing:-.045em}
.proof__list article>p:not(.proof__number),.about>p:last-child{max-width:42ch;color:#020914cc;font-size:1.08rem;line-height:1.55}
/* Kopf / Beschreibung / Keywords der drei Karten auf gleiche Ebene bringen —
   egal ob eine Beschreibung auf 2 oder 3 Zeilen umbricht. Subgrid teilt die
   Zeilen der proof__list mit den Karten; die Zeilenhoehe ist jeweils die des
   groessten Inhalts, also fluchten die Trennlinien + Keyword-Listen. */
@media(min-width:769px){
  .proof__list{grid-template-rows:auto auto auto}
  .proof__list>article{grid-row:span 3;display:grid;grid-template-rows:subgrid;min-height:0}
}
/* Karten-Kopf (Nummer + Titel) klickbar, subtil interaktiv: beim Hover leichter
   Versatz, Nummer wird helleres Cyan, ein kleiner Pfeil erscheint rechts. */
.proof__link{display:block;width:fit-content;max-width:100%;text-decoration:none;color:inherit}
.proof__link h3,.proof__link .proof__number{transition:transform .2s ease,color .2s ease}
.proof__arrow{display:inline-block;width:.4em;height:.4em;margin-left:.42em;border-top:2px solid #00719a;border-right:2px solid #00719a;transform:translateX(-5px) rotate(45deg);opacity:0;transition:opacity .2s ease,transform .2s ease;vertical-align:middle}
@media(hover:hover){
  .proof__link:hover .proof__number{color:var(--color-cyan)}
  .proof__link:hover h3{transform:translateX(4px)}
  .proof__link:hover .proof__arrow{opacity:1;transform:translateX(0) rotate(45deg)}
}

/* Leistungs-Keywords je Karte (in die Karten verschmolzen). Desktop stehen sie
   offen unter der Beschreibung; mobil per nativem <details> einklappbar. */
.proof__tags{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:10px}
.proof__tags li{position:relative;padding-left:19px;color:#243542;font-size:.96rem;font-weight:500;letter-spacing:-.01em;transition:color .16s ease,transform .16s ease}
.proof__tags li:before{content:"";position:absolute;left:0;top:.5em;width:6px;height:6px;background:#0a86b0;transform:rotate(45deg);transition:transform .16s ease,background .16s ease}
.proof__tags li:hover{color:#041020;transform:translateX(3px)}
.proof__tags li:hover:before{background:var(--color-cyan);transform:rotate(45deg) scale(1.3)}
.proof__more-toggle{list-style:none;display:flex;align-items:center;justify-content:space-between;gap:12px;cursor:pointer;padding:15px 0;color:#00465f;font-family:"Geist Mono",monospace;font-size:.8rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;-webkit-tap-highlight-color:transparent}
.proof__more-toggle::-webkit-details-marker{display:none}
.proof__more-toggle::after{content:"";flex:none;width:9px;height:9px;border-right:2px solid #00719a;border-bottom:2px solid #00719a;transform:rotate(45deg) translateY(-2px);transition:transform .22s ease}
.proof__more[open] .proof__more-toggle::after{transform:rotate(-135deg) translateY(-2px)}
/* Desktop: Klapp-Mechanik aus — Keywords immer sichtbar unter der Beschreibung. */
@media(min-width:769px){
  .proof__more{margin-top:24px;padding-top:22px;border-top:1px solid rgba(2,9,20,.14)}
  .proof__more>.proof__more-toggle{display:none}
  .proof__more>.proof__tags{display:flex}
}

.shopcase{--page-accent:var(--color-magenta);position:relative;isolation:isolate;background:var(--color-navy-raised)}
.shopcase .eyebrow{color:var(--color-magenta)}
.shopcase__intro{max-width:980px}
.shopcase h2{margin-bottom:22px}
.shopcase__support{max-width:62ch;margin-bottom:52px;color:var(--color-muted);font-size:clamp(1rem,1.3vw,1.18rem);line-height:1.6}
.shopcase__stage{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(260px,1fr);gap:clamp(28px,4vw,64px);align-items:start}
.shopcase__screen{margin:0;border:1px solid var(--color-line);background:var(--color-navy);overflow:hidden}
.shopcase__chrome{display:flex;align-items:center;gap:7px;padding:11px 16px;border-bottom:1px solid var(--color-line)}
.shopcase__chrome i{width:9px;height:9px;border-radius:50%;background:rgba(174,185,199,.35)}
.shopcase__chrome i:first-child{background:var(--color-magenta)}
.shopcase__chrome b{margin-left:10px;color:var(--color-muted);font-family:"Geist Mono",monospace;font-size:.72rem;font-weight:600;letter-spacing:.08em}
.shopcase__screen img{width:100%;height:auto;transform:scale(1.001);transition:transform .7s cubic-bezier(.18,.75,.16,1)}
.shopcase__screen:hover img{transform:scale(1.035)}
/* Highlighter-Sweep jetzt auf ALLEN Breiten (auch mobil). white-space:nowrap
   haelt "gefunden werden" als Einheit auf einer Zeile, damit der Strich nicht
   ueber einen Umbruch bricht — die Phrase wandert bei Bedarf als Ganzes in die
   naechste Zeile. Passt auf gaengigen Handybreiten (>=360px). */
.hl{font-style:normal;position:relative;display:inline-block;white-space:nowrap}
  /* HIGHLIGHTER, flach gehalten: der Strich sitzt so tief, dass im Wesentlichen
     die Unterlänge des g darauf liegt und er nur knapp ins untere Drittel der
     Kleinbuchstaben reicht — rund 21% der Versalhöhe statt 36%.
     Unterkante bleibt auf .17em, sonst rutschten die gemessenen 12px Unterlänge
     wieder heraus.
     Ragt links und rechts über das Wort hinaus, rechts weiter.
     Die Form läuft nach hinten zusammen: links 14 von 40 Einheiten hoch, Mitte
     35, rechts nur noch 24. Die DECKUNG bleibt dabei voll — Ausfransen über
     Deckkraftsprünge sah nach Rendering-Fehler aus, Unregelmäßigkeit steckt
     deshalb in der Kontur (ungleiche Wellenlängen, wellige Absetzkante). */
  .hl:after{position:absolute;right:-.14em;bottom:-.15em;left:-.05em;z-index:-1;height:.13em;content:"";background:var(--color-magenta);
    -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;
    transform:scaleX(0);transform-origin:left;transition:transform 1.75s cubic-bezier(.24,.62,.2,1) .35s}
  /* Eigene Klasse statt .is-in der Sektion: die ist bei 20% Sichtbarkeit schon
     gesetzt, da war der Strich längst gezogen, bevor man die Überschrift
     richtig im Blick hatte. is-drawn kommt deutlich später (site-static.js). */
  .hl.is-drawn:after{transform:scaleX(1)}
.shopcase__stats{display:grid;margin:0 0 14px;grid-template-columns:1fr 1fr;border:1px solid var(--color-line)}
.shopcase__stats>div{padding:20px 22px;border:0 solid var(--color-line)}
.shopcase__stats>div:nth-child(odd){border-right-width:1px}
.shopcase__stats>div:nth-child(-n+2){border-bottom-width:1px}
.shopcase__stats dt{margin:0 0 6px;color:var(--color-muted);font-family:"Geist Mono",monospace;font-size:.7rem;font-weight:600;letter-spacing:.13em;text-transform:uppercase}
.shopcase__stats dd{margin:0;color:var(--color-magenta);font-size:clamp(1.9rem,2.6vw,2.7rem);font-weight:700;letter-spacing:-.04em}
.shopcase__source{margin:0 0 30px;color:#8fa0b3;font-family:"Geist Mono",monospace;font-size:.7rem;letter-spacing:.06em;line-height:1.5}

/* Die Sektion füllt den Schirm MINDESTENS ganz aus (vorher 70vh).
   Grund: sie war mit 847px kleiner als ein typisches Fenster (890px) — es gab
   damit keine einzige Scrollposition, an der sie den Schirm allein belegt.
   Immer schaute oben oder unten ein Streifen der dunklen Nachbarsektion herein,
   und weil die Kopfleiste sich der Sektion darunter anpasst, bekam sie dadurch
   ZWEI Farben. Unruhig, ohne dass ein Fehler vorlag.
   120svh statt exakt 100: Basti wollte die helle Fläche bewusst GRÖSSER als
   den Schirm (26.07.). Der Inhalt rutscht dadurch nach unten und sein Ende
   liegt beim Landen unter der Kante — das ist so gewollt und kostet nichts:
   eine Sektion, die höher als der Schirm ist, deckt ihn an JEDER
   Scrollposition komplett ab, die dunklen Streifen und die dadurch
   zweifarbige Leiste können also erst recht nicht zurückkommen.
   Der Inhalt wird dabei senkrecht ZENTRIERT statt nach oben gedrückt — auf
   hohen Schirmen (1117px+) läge sonst der ganze Überschuss als totes Feld
   unter dem Diagramm. Ist der Inhalt höher als der Schirm, wächst die Box wie
   bisher mit; justify-content greift dann nicht, es geht nichts verloren. */
.about{position:relative;isolation:isolate;min-height:120svh;
  display:flex;flex-direction:column;justify-content:center;
  background:#dce4ea;color:var(--color-navy)}
/* #005f82 statt #00719a: auf #dce4ea kam die alte Farbe auf 4,26:1 und lag
   damit schon OHNE jede Tönung unter der WCAG-AA-Schwelle von 4,5 — ein
   Fehler, der vor der Zündstelle da war. Jetzt 5,52:1 blank und 4,73:1 unter
   dem Fleck. Dieselbe Farbe steht aus Gleichklang auch in .proof. */
.about .eyebrow{color:#005f82}
/* Finale + Footer ergeben zusammen GENAU eine Bildschirmhöhe: kein heller
   Rest oben, kein Überstand unten. --footer-h misst site-static.js (der
   Footer bricht auf schmalen Fenstern um); der Startwert trägt ohne JS. */
/* Kontakt + Footer = exakt ein Bildschirm, nie mehr. `svh` statt `lvh`, damit
   der Block auch bei eingeblendeter Mobile-Browserleiste nicht überläuft;
   knapperes Innenabstandsmaß, damit der Inhalt auch auf flachen Fenstern
   hineinpasst und die Sektion nicht über die Mindesthöhe hinauswächst. */
/* Auch die Höhe des fixierten Headers abziehen: er verdeckt die obersten
   80 px, dort ist die helle Sektion also schon unsichtbar. Ohne den Abzug
   wirkt der Bildschirm 80 px vor dem Seitenende bereits komplett schwarz. */
.contact{position:relative;display:grid;min-height:calc(100vh - var(--footer-h,74px));min-height:calc(100svh - var(--footer-h,74px));align-content:center;padding-top:clamp(124px,14vh,152px);padding-bottom:clamp(44px,5vh,64px);isolation:isolate;overflow:hidden;background:var(--color-navy)}

/* Aurora im Finale: quillt vom hellen rechten Ende der oberen Leiste nach
   unten und bleibt danach als ruhiger Hintergrund stehen. Bewusst rechts —
   links steht die Überschrift, dort würde jeder Schein Kontrast kosten. */
/* Zwei Herde auf der Diagonalen, farblich getrennt (Violett oben rechts,
   Cyan unten links) und zeitlich gestaffelt:
   ::after kommt direkt mit der vollen Querlinie, ::before folgt, wenn die
   Leisten ihre Endhelligkeit erreicht haben.
   Verzögerung steht nur im EIN-Zustand -> beim Zurückscrollen sofort weg. */
.contact-aurora{position:absolute;z-index:-1;inset:0;pointer-events:none}
.contact-aurora:before,.contact-aurora:after{position:absolute;inset:0;content:"";opacity:0}

/* oben rechts — Violett. Wächst aus dem Ankerpunkt heraus, darum Ursprung
   exakt in dessen Ecke (100% 0) und Start klein statt eingeblendet. */
.contact-aurora:after{
  background:
    radial-gradient(52% 46% at 94% 2%,rgba(124,92,255,.24),transparent 70%),
    radial-gradient(44% 46% at 106% 40%,rgba(158,74,214,.13),transparent 72%);
  transform:scale(.3);transform-origin:100% 0;
  transition:opacity 2.6s ease,transform 3.4s cubic-bezier(.16,.62,.2,1)}
.is-energized .contact-aurora:after{opacity:1;transform:none;transition-delay:.85s}

/* unten links — Cyan, sobald der Treffpunkt der beiden Leisten voll ist:
   das passiert schon beim Erreichen des Seitenendes, also ohne Verzögerung
   und damit vor dem violetten Herd oben rechts. */
/* Wird vom Einschlag ERZEUGT, nicht vorher: Ursprung unten links ist exakt
   der Landepunkt des Ankers. Schnellerer Aufbau als die anderen Flächen,
   damit es wie herausgeschossen wirkt statt wie langsam eingeblendet. */
.contact-aurora:before{
  background:
    /* Herd unten links — die eigentliche Quelle, aus der es herausschießt. */
    radial-gradient(46% 44% at 0% 102%,rgba(25,191,244,.26),transparent 68%),
    /* Bodenlicht: breit und flach über die ganze Breite. Ohne das fiel alles
       rechts der Quelle auf reines Navy ab — gemessen 28 von 765 möglichen
       Helligkeitspunkten, was als schwarzes Loch unter dem Finale las.
       Bewusst schwach: es soll den Boden anheben, nicht leuchten. */
    radial-gradient(135% 30% at 50% 104%,rgba(25,191,244,.115),rgba(25,191,244,0) 72%);
  /* Startet KLEIN am Ursprung unten links und wächst nach außen. Vorher
     lief es andersherum (scale 1.09 -> 1), also nach innen — das konnte
     gar nicht wie Entstehen wirken. */
  transform:scale(.3);transform-origin:0 100%;
  transition:opacity 2.6s ease,transform 3.4s cubic-bezier(.16,.62,.2,1)}
/* Gleichzeitig mit Violett oben rechts: zwei Quellen, ein Moment. */
.is-energized .contact-aurora:before{opacity:1;transform:none;transition-delay:.85s}

.contact h2{max-width:850px;margin-bottom:42px}
.contact .button{width:fit-content}

/* Der Glow des Finales endete hart an der Unterkante von .contact — die
   Sektion klippt per overflow:hidden. Gemessen an der Naht: links darüber
   rgb(10,72,98), einen Pixel darunter rgb(2,9,20). Ein voller Helligkeits-
   sprung, genau der Bruch, der komisch aussah.
   Der Footer führt den Verlauf deshalb fort: gleiche Farbe, Ursprung links
   oben (dort landet der Anker) und nach unten auslaufend. Die .35 sind aus
   der gemessenen Deckkraft an der Kante zurückgerechnet, nicht geschätzt. */
.site-footer{position:relative;isolation:isolate;display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;padding:26px var(--page-gutter);border-top:1px solid rgba(174,185,199,.12);color:#8fa0b3;font-family:"Geist Mono",monospace;font-size:.72rem;letter-spacing:.08em}
/* Die Stufen sind aus dem gemessenen Profil direkt über der Naht abgeleitet,
   nicht geschätzt. Deckkraft des Cyan bei x = 0/45/81/130/180px:
   .39 / .28 / .175 / .04 / 0 — das ergibt bei 46% Radius (= 180px auf 393px
   Breite) genau die Stopps unten. Erster Versuch mit 150% Radius war viel zu
   breit: der Footer war in der Mitte gemessen HELLER als die Sektion darüber
   (5,37,54 gegen 2,9,20), also eine neue Naht in der Gegenrichtung.
   Senkrecht 150%, damit der Schein zum Seitenende hin sanft ausläuft. */
.site-footer:before{position:absolute;z-index:-1;inset:0;content:"";opacity:0;
  background:
    radial-gradient(var(--fuss-weite,46%) 150% at 0% 0%,
      rgba(25,191,244,.39) 0%,rgba(25,191,244,.28) 25%,rgba(25,191,244,.175) 45%,
      rgba(25,191,244,.04) 72%,rgba(25,191,244,0) 100%),
    /* Fortsetzung des Bodenlichts. Die Höhe steht in var, weil sie sich aus
       der KONTAKTHÖHE ableitet, nicht aus der des Footers — auf Mobil sind
       das 30% von ~646px, umgerechnet auf 114px Footer also rund 170%. */
    radial-gradient(135% var(--fuss-boden,170%) at 50% 0%,
      rgba(25,191,244,.115),rgba(25,191,244,0) 72%);
  transition:opacity 2.6s ease}
/* Auf Desktop reicht der Schein über der Naht WENIGER weit als der reine
   Prozentwert vermuten lässt: dort liegt neben der Aurora noch eine
   Zündstelle, und beide zusammen laufen früher aus. Gemessen war der Footer
   bei x=320 um .066 heller als die Sektion darüber. 34% trifft die
   tatsächliche Kurve. */
/* Desktop: Kontaktsektion ist ~732px hoch, Footer nur ~68px — dasselbe
   absolute Bodenlicht braucht dort entsprechend mehr Prozent. */
@media(min-width:769px){.site-footer{--fuss-weite:34%;--fuss-boden:320%}}
/* Dieselbe Uhr wie die Aurora darüber, sonst käme die Fortsetzung später
   als das, was sie fortsetzt. */
.is-energized .site-footer:before{opacity:1;transition-delay:.85s}

/* Scroll-Reveals: nur aktiv, wenn JS einen IntersectionObserver armiert hat.
   Ohne JS (oder ohne IO) bleibt alles sofort sichtbar. */
.has-reveal .reveal{opacity:0;transform:translateY(18px);transition:opacity .5s ease,transform .65s cubic-bezier(.25,.6,.3,1);transition-delay:calc(var(--ri,0)*80ms)}
.has-reveal .reveal.is-in{opacity:1;transform:none}

@media(max-width:1200px){
  .site-header{grid-template-columns:minmax(150px,1fr) auto minmax(220px,1fr)}
  .brand{width:132px}
  .site-nav{gap:22px}
  .hero__intro{width:min(960px,79vw)}
  /* Manifesto ist unter 1200px komplett ausgeblendet (Regel an der
     Basisdefinition); die Kartenhöhe regelt --tri-h überall. */
  /* Keine eigene Titelgröße mehr nötig: die 2,65vw-Kurve der Basisregel
     trägt bis hinunter zu 769px, dort werden die Karten gestapelt. */
}
@media(max-width:900px){
  .site-header{grid-template-columns:1fr auto}
  .site-nav,.language,.hero__manifesto{display:none}
  .hero__intro{width:100%}
  h1{font-size:clamp(3rem,7.8vw,4.7rem)}
  .shopcase__stage{grid-template-columns:1fr}
}
@media(max-width:768px){
  :root{--page-gutter:clamp(20px,5vw,34px)}
  /* Mobil zieht sich auch der SCHEIN direkt mit dem Scrollstand zurück — hier
     sollen die Leisten unmittelbar reagieren. Auf Desktop behält der Glow
     dagegen seinen eigenen langen Rückzug (4,6s ab 60% Scrollstand).
     Beide Leisten sind hier waagerecht: oben füllt von links, unten von
     rechts — Ursprung entsprechend. */
  .is-framed .rail-glow{transform:scaleX(var(--sp,0));transform-origin:left center}
  .is-framed .scroll-rail--bottom .rail-glow{transform:scaleX(var(--sp,0));transform-origin:right center}
  @supports (animation-timeline:scroll()){
    .is-framed .rail-glow{animation:railX linear both;animation-timeline:scroll(root block)}
    .is-framed .scroll-rail--bottom .rail-glow{animation:railXRev linear both;animation-timeline:scroll(root block)}
  }
  .button--header{min-height:42px;padding-inline:16px;font-size:.8rem}
  /* Die Leiste steht auf der Startseite mobil im FLUSS und scrollt weg — anders
     als auf /websites, /shops und /software, wo sie fixiert bleibt. Bewusster
     Unterschied (Basti, 26.07.): mobil zählt jeder Pixel über dem Falz, und die
     Startseite hat mit dem Griff ohnehin eine dauerhaft erreichbare Navigation.
     Der Griff selbst bleibt fixiert (edge-nav.css) und wandert beim Scrollen in
     die Drei-Punkte-Form — er ist also weiterhin da, wenn die Leiste weg ist.
     site-nav.css setzt position:fixed; diese Regel steht später und gewinnt. */
  .topnav{position:relative}
  /* Weil die Leiste jetzt wieder 64px Höhe im Fluss belegt, braucht der Hero
     nur noch 24px eigenes Polster: 64 + 24 = 88px, exakt der Wert, an dem der
     Text vorher begann (72px Kopf + 16px). Die erste Projektkarte schneidet
     damit unverändert an. */
  .hero{min-height:auto;padding:24px var(--page-gutter) 0;background-size:auto 760px;background-position:center top}
  .hero__intro{padding-top:0}
  h1{margin-bottom:14px;font-size:clamp(2rem,10.4vw,4.5rem)}
  .hero__support{margin-bottom:18px;max-width:32ch;line-height:1.45}
  .triptych{position:relative;inset:auto;display:grid;height:auto;margin:22px calc(var(--page-gutter)*-1) 0;padding:0;grid-template-columns:1fr}
  .project-panel,.project-panel:first-child,.project-panel:last-child{min-height:440px;height:auto;top:auto;margin:0;padding:0 var(--page-gutter) 32px;clip-path:polygon(0 4%,100% 0,100% 100%,0 100%)}
  /* Kein Verschieben beim Erscheinen. Auf Desktop fahren die Karten beim Laden
     von unten herein (translateY 68px). Mobil kommen sie beim Durchscrollen
     einzeln ins Bild, und dann sah dieses Hochrutschen ruckelig aus. Hier nur
     noch AUFBLENDEN über die Deckkraft — kein transform, nichts bewegt sich.
     Der Übergang trägt nur noch opacity, sonst würde die is-ready-Regel oben
     wieder ein translateY(0) animieren wollen. */
  .project-panel{transform:none;transition:opacity .5s ease var(--reveal-delay,0s)}
  .is-ready .project-panel{transform:none}
  .project-panel:nth-child(2),.project-panel:nth-child(3){margin-top:-16px}
  .project-panel+.project-panel{margin-left:0}
  .project-panel--flowsnx .project-panel__image{left:0}
  .project-panel__copy{width:min(100%,430px)}
  /* Gestapelt hat jede Karte die volle Breite — eigene, steilere Kurve.
     Auch hier eine Größe für alle; der Deckel hält den Titel unter der
     Wirkung der H1 darüber. */
  .project-panel__title{font-size:clamp(1.5rem,7.5vw,2.6rem)}
  /* Mobil enger an die Ecke als auf Desktop. Der Freiraum rechts im Footer
     ist nicht mehr nötig: das Dock weicht dem Footer jetzt aus (--dock-lift)
     und liegt nie mehr auf seinem Text. */
  /* Stabiler svh-Anker statt bottom: der Button haengt NICHT am dynamischen
     Visual-Viewport (den iOS-Safaris Toolbar staucht), sondern sitzt am unteren
     Rand eines fix an den STABILEN oberen Rand verankerten 100svh-Kastens.
     svh = kleinstmoegliche Viewporthoehe (Toolbar ausgefahren) -> die Unterkante
     liegt an einem stabilen Punkt; die Toolbar schiebt sich drunter durch, statt
     den Button mitzunehmen. (Aufs Pixel festnageln geht nicht -- Browser-Chrome
     liegt ausserhalb der Seite -- aber kein Wandern mehr.) Der Full-Bleed-Kasten
     ist click-transparent (pointer-events:none aus der Basis), nur der Button faengt Klicks. */
  .contact-dock{top:0;bottom:auto;left:0;right:0;height:100vh;height:100svh;align-items:flex-end;justify-content:flex-end;padding:0 16px calc(16px + env(safe-area-inset-bottom,0px)) 0}
  /* Ring und Leisten-Wellen sind mobil DIESELBE Bewegung: gleicher Start
     (das Dock erscheint dafür sofort mit dem Zünden), gleiche Dauer, gleiche
     Kurve. Nur so liest es sich als eine Geste.
     Ein bloß gleiches ENDE reicht nicht — das war der Fehler davor. Gemessen
     mit angeglichenem Endzeitpunkt, aber alter Kurve:
        Fortschritt   Welle    Ring     Versatz
        50 %           467ms   1533ms   +1066ms
        90 %          1200ms   1883ms    +683ms
        99,9 %        2250ms   2266ms     +16ms
     Die Welle schießt vorn raus und kriecht aus, der Ring startet träge und
     holt am Schluss auf. Sie trafen sich nur in der letzten Millisekunde,
     dazwischen lagen sie bis zu eine Sekunde auseinander — genau das wirkte
     unharmonisch.
     Der Stift MUSS dieselben Werte tragen, sonst läuft der Punkt nicht mehr
     auf dem Ende der Linie mit. */
  .contact-dock.is-visible .draw-ring,
  .contact-dock.is-visible .pen{
    animation-duration:2.4s;animation-delay:0s;
    animation-timing-function:cubic-bezier(.25,.55,.2,1)}
  /* Das Symbol sitzt jetzt früher, sonst käme es erst, wenn der Ring schon
     fast zu ist. Anteilig dieselbe Stelle wie vorher (~40 % der Ringdauer). */
  .contact-dock.is-visible .dock-icon{animation-delay:.95s}
  /* Die Pulswellen setzen erst ein, wenn der Ring geschlossen ist — vorher
     liefen sie mitten in den Aufbau hinein. */
  /* Die Wellen setzen erst ein, wenn der Ring des Icons bei 2,25s
     geschlossen ist — vorher liefen sie mitten in den Aufbau hinein.
     Der Versatz der zweiten Welle rechnet sich aus der Umlaufzeit. */
  .contact-dock.is-visible .dock-btn:before{animation-delay:2.5s}
  .contact-dock.is-visible .dock-btn:after{animation-delay:4s}
  /* gestapelt: Bild wieder volle Kartenbreite */
  .project-panel__image{width:100%}
  .proof__list{grid-template-columns:1fr}
  .proof__list article{min-height:0;padding:28px 0 38px}
  .proof__list article+article{padding-left:0;border-top:1px solid rgba(2,9,20,.2);border-left:0}
  .proof__more{border-top:0}
  /* Mobil scrollt der Header mit weg, verdeckt also nichts — nur Footer abziehen.
     Bewusst lvh statt svh: svh ist die Höhe MIT ausgefahrener Browserleiste.
     Am Seitenende fährt die Leiste aber ein, der sichtbare Viewport wird also
     größer als svh — die Sektion war dann zu kurz und oben schaute die helle
     Sektion darüber ins Bild (Firefox Android). lvh rechnet mit der
     eingefahrenen Leiste; steht sie doch mal, ist die Sektion nur minimal
     höher als das Fenster, was folgenlos ist. */
  .contact{min-height:calc(100vh - var(--footer-h,74px));min-height:calc(100lvh - var(--footer-h,74px))}
  /* Mobil bleibt der Ablauf zügig — dort läuft keine Querlinie davor.
     MUSS nach den Desktop-Regeln stehen, sonst gewinnen die (gleiche
     Spezifität -> späteres Vorkommen entscheidet). */
  /* Mobil ist der Header längst weggescrollt — der Impuls liefe ins Nichts */
  .rail-glow:before{transition-duration:2.4s}
  .rail-glow:after{transition:opacity 1.4s ease}
  .is-energized .rail-glow:after{transition-delay:.9s}
  /* Beide Leisten sind mobil waagerecht — der Glow muss also in X laufen,
     nicht in Y (sonst passiert auf 3 px Höhe sichtbar nichts).
     Beide starten gleichzeitig und laufen aufeinander zu:
     oben ist das helle Ende rechts, unten links. */
  .scroll-rail:not(.scroll-rail--bottom) .rail-glow:before{
    background:linear-gradient(to left,var(--color-cyan) 0%,var(--color-cyan) 48%,rgba(25,191,244,0) 100%);
    transform:scaleX(0);transform-origin:right center}
  .scroll-rail--bottom .rail-glow:before{
    background:linear-gradient(to right,var(--color-cyan) 0%,var(--color-cyan) 48%,rgba(25,191,244,0) 100%);
    transform:scaleX(0);transform-origin:left center}
  /* Der Hintergrund setzt mobil erst ein, wenn der Rahmen WIRKLICH geschlossen
     ist — also wenn beide Lichtwellen an den Enden der Leisten angekommen sind
     und die Punkte verbunden haben. Das ist gemessen 2250ms nach dem Zünden;
     dieselbe Millisekunde, in der auch der Ring des Dock-Icons zugeht.
     Vorher stand hier 0s: die Flächen kamen sofort mit dem Zünden, also
     mitten in den laufenden Aufbau hinein, und liefen gegen ihn statt mit ihm.
     Der Wert MUSS mit der Verzögerung von is-finaled in site-static.js
     zusammenbleiben — beide tragen denselben Moment. */
  .is-energized .contact-aurora:before,
  .is-energized .contact-aurora:after,
  .is-energized .site-footer:before{transition-delay:2.25s}

  /* Mobil gibt es den fixen Anker nicht (er hängt am Header, der wegscrollt).
     Dafür ist die obere Leiste hier waagerecht und ihr Kopf endet oben
     RECHTS — also genau dort, wo auf Desktop der Anker sitzt. Er übernimmt
     das Violett; die untere Leiste (Kopf unten links) zündet das Cyan.
     Beide Bursts starten sofort, passend zu den Flächen. */
  .scroll-rail:not(.scroll-rail--bottom) .rail-burst{--burst:150,110,255}
  .scroll-rail--bottom .rail-burst{display:block}
  .is-energized .rail-burst{animation-delay:0s}
  /* Derselbe Rollentausch beim Hintergrund-Schein: mobil trägt der obere Kopf
     das Violett, weil er hier die Aufgabe des Ankers übernimmt. Auf hellen
     Sektionen sticht die Regel mit [data-q] das wieder aus. */
  .zuend-punkt[data-q="kopf"]{--zg-farbe:150,110,255}
}
@media(max-width:520px){
  .header-actions{gap:12px}
  .button--header{min-height:40px;padding-inline:13px}
  .hero__actions{align-items:flex-start;flex-direction:column;gap:20px}
  .hero__actions .button{width:100%}
  .project-panel,.project-panel:first-child,.project-panel:last-child{min-height:390px;clip-path:none}
  /* Lead-Case fuellt den ersten Viewport exakt aus (pro Geraet via svh),
     damit Panel 02 sauber unter den Fold rutscht statt zufaellig durchzublitzen.
     440px = Header + Intro + Triptychon-Abstand (gemessen, stabil). Floor 390. */
  .project-panel:first-child{min-height:max(390px,calc(100svh - 430px))}
  .project-panel:nth-child(2),.project-panel:nth-child(3){margin-top:0}
  .proof,.about,.contact,.shopcase{padding-block:76px}
  .proof h2,.about h2,.contact h2,.shopcase h2{margin-bottom:38px;font-size:2.7rem}
  .shopcase h2{margin-bottom:22px}
  .shopcase__stats dd{font-size:1.8rem}
}
@media(prefers-reduced-motion:reduce){
  /* Der Köder am Bildrand ist reine Bewegung — genau das, was hier weg soll.
     Der Grip bleibt sichtbar, er hört nur auf zu zucken. */
  .edge-handle i{animation:none}
  html{scroll-behavior:auto}
  *,*:before,*:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}
  h1 span,.project-panel,.is-ready h1 span,.is-ready .project-panel{opacity:1;transform:none}
  .has-reveal .reveal{opacity:1;transform:none}
  .shopcase__screen:hover img{transform:none}
  .rail-head.is-hit:after,.rail-head.is-hit:before{animation:none}
  .is-energized .rail-anchor__core,.is-energized .rail-burst{animation:none}
  .rail-burst{display:none}
  .rail-close.is-closed .rail-close__fill{animation:none;transform:scaleX(1)}
  .is-line-running .scroll-rail--right .scroll-rail__fill,
  .is-line-running .scroll-rail--left .scroll-rail__fill{animation:none;transform:scaleY(1)}
  .dock-btn{transition:none}
  .contact-dock.is-visible .draw-ring{animation:none;stroke-dashoffset:0}
  .contact-dock.is-visible .dock-icon{animation:none;opacity:1;transform:none}
  .contact-dock.is-visible .dock-btn:before,.contact-dock.is-visible .dock-btn:after{animation:none;opacity:0}
  .contact-dock.is-visible .pen{animation:none;opacity:0}
  .rail-glow:before,.rail-glow:after{transition:none}
  .contact-aurora:before,.contact-aurora:after,.section-aurora{transition:none}
}

/* === Reduzierte Atmosphäre =================================================
   Feedback: die Hintergrund-Glows waren zu viel. Entfernt sind die
   ATMOSPHÄRE-Ebenen, die auf der Linien-Choreografie LIEGEN — nicht die
   Choreografie selbst (Leisten, wandernder Punkt, Rahmenschluss, Burst,
   Rückzug bleiben):
     · alle Hintergrund-Auroras (Kontakt, Sektionen, Zündflecken, Fußlicht,
       Bodenlicht)
     · der Außenglow des UNTEREN CTA (Kontakt-Button)
   Der Glow des Header-Buttons bleibt bewusst — er konkurriert dort nicht als
   zweite volle Fläche. Neutralisiert per opacity/animation statt gelöscht,
   damit sich einzelne Ebenen bei Bedarf gezielt wieder aktivieren lassen. */
.contact-aurora:before,.contact-aurora:after,
.is-energized .contact-aurora:before,.is-energized .contact-aurora:after{
  opacity:0!important;transition:none!important;animation:none!important}
.section-aurora,.is-finaled .section-aurora{opacity:0!important;transition:none!important}
.zuend-glow{display:none!important}
.site-footer:before,.is-energized .site-footer:before{opacity:0!important}

/* Kompakter, damit beide Gründer-Karten mit in den Viewport passen.
   Nachgezogen am 26.07.2026: der Lead nennt jetzt auch Sebastian und ist
   dadurch eine Zeile länger (+29px). Gemessen ragte die Sektion bei 1440x900
   danach 68px über den Schirm — und schon vorher 39px, das Ziel des Blocks
   war also ohnehin nicht erreicht. Der Platz kommt aus den Abständen, nicht
   aus dem Text: Polster, Rasterabstand und die Innenabstände der beiden
   Karten. Schriftgrößen bleiben unangetastet. */
.about{padding-top:clamp(62px,5.8vw,80px);padding-bottom:clamp(36px,3.6vw,50px)}
.about h2{font-size:clamp(2.3rem,3.8vw,3.6rem);margin-bottom:clamp(12px,1.4vw,20px)}
/* Zahl, Subline, Bildunterschrift und Chart-Boden werden auf die Kante des
   1. Balkens bzw. auf die Achsen ausgerichtet (Desktop). Die Headline
   "Jetzt eures." bleibt bewusst normal linksbuendig unter "Erst". */
@media(min-width:900px){
  /* Zahl + Subline UND die Bildunterschrift um denselben Balken-Einzug nach
     rechts (100% = Spaltenbreite) -> alles auf der Kante des 1. Balkens. */
  .track__stat{padding-left:calc(min(100%,780px) * 0.1494)}
  .growth__note{padding-left:calc(min(100%,780px) * 0.1494)}
  /* "1,5 Mio EUR" auf Text-Hoehe der Lead-Zeile ("David Kresinski ..."). Nicht
     Container-, sondern GLYPHEN-Oberkante ausrichten: die grosse Schrift sitzt
     ~15px tiefer im Kasten als die kleine Lead-Schrift, daher 23 statt 38px.
     Hoehere Spezifitaet, da Basis-.track__stat{margin:...} spaeter im CSS steht. */
  .about .track__stat{margin-top:23px}
  /* Chart-Boden (Grundlinie) auf die Trennlinie ueber "02 Sebastian" ziehen:
     nach dem 23px-Nudge liegt die Grundlinie noch ~16px darunter. */
  .about .growth{margin-top:-16px}
}
.about__grid{margin-top:clamp(16px,2vw,26px)}
.about__lead{margin:0}
.founders{margin-top:clamp(14px,1.9vw,24px);gap:clamp(12px,1.4vw,17px)}
.founder{padding:14px 0 0}
.founder__name{margin:8px 0 5px}

/* === Gründer / Track-Record (nur index2, testweise) ======================
   Wachstumskurve auf hellem about-Grund (#dce4ea). Balken dunkelcyan #0f7ba0
   (Kontrast 3,74 zum Grund), 2025 über Höhe + helle Cyan-Kappe hervorgehoben;
   Wertlabels stehen ÜBER den Balken (dort 10,85 Kontrast), nicht drauf. Balken
   wachsen beim Reinscrollen aus der Grundlinie (koppelt an das reveal-System). */
/* Breite bewusst bei 1180px und gleichen Spalten. Eine breitere Fassung
   (1500px, 1.25fr/1fr) war am 26.07. testweise drin und wurde von Basti
   verworfen — schmaler gefällt ihm besser, obwohl rechts dadurch mehr Fläche
   frei bleibt als bei .proof/.shopcase. Nicht erneut vorschlagen. */
.about__grid{display:grid;grid-template-columns:1fr 1fr;gap:clamp(34px,5vw,64px);align-items:start;margin-top:clamp(20px,2.2vw,32px);max-width:1180px}
.about__col{min-width:0}
.about__lead{max-width:56ch;margin:16px 0 0;color:#2b3a47;font-size:clamp(1rem,1.35vw,1.2rem);line-height:1.5}
.growth{margin:0;padding:0;--grow:1}
.growth__svg{width:100%;max-width:780px;height:auto;overflow:visible}
.growth__base{stroke:rgba(2,9,20,.18);stroke-width:1}
/* Balken scroll-gebunden: JS setzt --grow 0..1 auf .growth (per rAF an die
   Scroll-Position der Sektion gekoppelt), voll bei ~vollem Viewport. Kein
   transition -> folgt dem Scroll direkt. --i staffelt die Balken minimal,
   *1.14 laesst am Ende alle sicher die Vollhoehe treffen. Default --grow:1 =>
   bei reduce-motion / ohne JS stehen die Balken schlicht voll. */
.growth__bar{fill:#0f7ba0;transform-box:fill-box;transform-origin:center bottom;
  transform:scaleY(clamp(0,calc((var(--grow) - var(--i,0)*0.06)*1.14),1))}
.growth__bar.is-last{fill:#0b6d90}


/* Wertlabels durchgehend deckend — kein Fade-in (die Balken wachsen scroll-
   gebunden, das reicht als Bewegung). */
.growth__val{fill:#08303f;font-family:"Geist Mono",monospace;font-size:14px;font-weight:600}
.growth__val.is-last{font-size:17px;font-weight:720}
.growth__yr{fill:#46586a;font-family:"Geist Mono",monospace;font-size:12px;letter-spacing:.08em}
.growth__note{margin-top:18px;max-width:62ch;color:#4a5b6a;font-family:"Geist Mono",monospace;
  font-size:.72rem;line-height:1.5;letter-spacing:.02em}
.founders{display:grid;grid-template-columns:1fr;gap:clamp(16px,2vw,24px);margin-top:clamp(22px,2.4vw,30px)}
.founder{position:relative;padding:20px 0 0;border-top:1px solid rgba(2,9,20,.16)}
.founder__no{font-family:"Geist Mono",monospace;font-size:.7rem;font-weight:600;letter-spacing:.13em;color:#005f82}
.founder__name{margin:9px 0 5px;font-size:clamp(1.5rem,2.4vw,2rem);font-weight:700;letter-spacing:-.02em;color:var(--color-navy)}
.founder__role{margin:0 0 8px;font-size:1.05rem;font-weight:600;color:#0f5f7d}
.founder__qual{margin:0;color:#4a5b6a;font-size:.92rem;line-height:1.45}
@media(max-width:768px){
  .about__grid{grid-template-columns:1fr;gap:34px;align-items:start}
  /* Reihenfolge mobil: Lead -> Zahl+Diagramm -> Gruender. about__col loest sich
     per display:contents auf, damit Lead und founders als eigene Grid-Items
     nach order sortierbar sind (track sitzt dann dazwischen). */
  .about__col{display:contents}
  .about__lead,.founders{min-width:0}
  .about__lead{order:1}
  .track{order:2}
  .founders{order:3;gap:26px}
  .growth__svg{max-width:none}
}

/* Kennzahl über dem Chart — Fokuspunkt der Track-Record-Sektion. */
.track__stat{margin:0 0 clamp(18px,2.4vw,30px);display:flex;flex-direction:column;gap:7px}
.track__num{font-size:clamp(2.7rem,5vw,4.4rem);font-weight:720;letter-spacing:-.03em;line-height:.95;color:#0a5f7d;font-variant-numeric:tabular-nums}
.track__sub{font-family:"Geist Mono",monospace;font-size:.72rem;font-weight:600;letter-spacing:.06em;color:#4a5b6a;text-transform:uppercase}

/* === Rechtsseiten (Impressum etc.) — schlichte, gut lesbare Textspalte ===== */
.legal-page{background:var(--color-paper);color:var(--color-navy)}
.legal-head{display:flex;align-items:center;justify-content:space-between;gap:20px;
  padding:clamp(16px,2.6vw,26px) var(--page-gutter);border-bottom:1px solid rgba(2,9,20,.1)}
.legal-head .brand{display:inline-flex}
.legal-head .brand img{height:32px;width:auto;filter:brightness(0)}
.legal-head__home{flex:none;font-family:"Geist Mono",monospace;font-size:.78rem;font-weight:600;
  letter-spacing:.06em;color:#00719a;text-decoration:none}
.legal-head__home:hover{color:#00465f}
.legal{max-width:760px;margin:0 auto;
  padding:clamp(44px,7vw,84px) var(--page-gutter) clamp(64px,10vw,120px)}
.legal h1{margin:0 0 clamp(26px,4vw,44px);font-size:clamp(2.3rem,5vw,3.4rem);
  font-weight:650;letter-spacing:-.04em;line-height:1;color:var(--color-navy)}
.legal h2{margin:clamp(28px,3.6vw,40px) 0 10px;font-family:"Geist Mono",monospace;
  font-size:.8rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:#00719a}
.legal p,.legal address{margin:0 0 6px;font-size:1.05rem;line-height:1.65;color:#1c2733;font-style:normal}
.legal a{color:#00719a}
.legal a:hover{color:#00465f}
.legal-page .site-footer{color:#5f7183;border-top-color:rgba(2,9,20,.1)}
/* Footer-Link (Impressum) dezent: erbt Farbe, feiner Unterstrich, Hover heller. */
.site-footer a{color:inherit;text-decoration:none;border-bottom:1px solid rgba(143,160,179,.4);
  transition:color .18s ease,border-color .18s ease}
.site-footer a:hover{color:#cfe0f0;border-color:#cfe0f0}
.legal-page .site-footer a:hover{color:#00465f;border-color:#00465f}

/* CTA nur auf Desktop im Header — mobil raus (macht Platz fuer die Nav). */
@media(max-width:768px){.button--header{display:none}}
