/* Startseite „Software-Reise“ (ADR-008) – der Faden nach dem Kapitel: KI im Prozess,
   Produkte, Kontakt; dazu runde KI-Knoten im Faden. Ergänzt home.css
   (Tokens aus /styles/main.css, .ki-badge aus home.css).
   Ohne JS bzw. bei reduzierter Bewegung statisch und lesbar; bewegte Zustände nur
   unter .thread-live (Faden aktiv, story-thread.js), .ki-net-on (ki-net.js), .wall-armed
   (customer-wall.js) und .cta-armed (contact-signal.js).
   --thread-arrive setzt story-thread.js am Ziel: Ankunft des Zweigs in ms. */

/* ---------- Faden: Knoten ---------- */
/* Balken zeichnen sich, wenn der (S-förmige) Zweig ankommt. */
.thread-live:not(.no-anim) .lime-bar.is-reached[data-thread-node] { transition-delay: var(--thread-arrive, 0ms); }

/* Über uns: Erreicht der Faden den Abschnitt, nimmt der Bildrahmen den Lime-Ton an – wie die
   Bühne im Leistungsspektrum (.chapter-stage.is-powered). Nur Farbe, keine Bewegung. */
.thread-live .about-media { transition: border-color 0.6s ease; }
.thread-live .about-grid:has(.lime-bar.is-reached) .about-media { border-color: color-mix(in srgb, var(--lime) 38%, var(--line-2)); transition-delay: 0.4s; }

/* Nach einem Neuaufbau bleiben erreichte Knoten an: nur Atmen, kein erneuter Pop. */
.thread-node.is-kept .thread-node-dot::after { animation: node-breathe 3.4s ease-in-out infinite; animation-delay: calc(var(--i, 0) * 300ms); }

/* Angehalten: Atmen der erreichten Knoten steht (die animation-Kurzform in .is-hit/.is-kept setzt
   animation-play-state sonst zurück – die Regel in home.css verliert an Spezifität). */
.motion-paused .thread-node:is(.is-hit, .is-kept) .thread-node-dot::after { animation-play-state: paused; }

/* Endknoten am Kontakt: kein eigenes Atmen – seinen Takt gibt der Herzschlag vor (contact-signal.js). */
.thread-live .thread-node--end .thread-node-dot::after { animation: none; }

/* ---------- KI im Prozess: Netz (ki-net.js) ---------- */
/* Stationen: gedimmt, bis die Front sie erreicht – dann Punkt, Ring, Beschriftung. .is-ping
   (Rückkopplung geschlossen) startet den Ring in der Planung neu. Statisch (reduzierte
   Bewegung) setzt ki-net.js alles auf an. */
.ki-net-on .ki-step { color: var(--text-2); transition: color 0.4s ease; }
.ki-net-on .ki-step.is-lit { color: var(--text); }
.ki-net-on .ki-step::after { content: ""; position: absolute; left: var(--dot-l); top: var(--dot-t, auto); width: 9px; height: 9px; border-radius: 50%; border: 1.5px solid var(--lime); opacity: 0; pointer-events: none; }
.ki-net-on:not(.ki-net-static) .ki-step.is-lit::after { animation: ki-step-ping 1s var(--ease-settle); }
/* ki-step-ping bleibt in der Liste: Das Entfernen von .is-ping startet dann keinen zweiten Ring. */
.ki-net-on:not(.ki-net-static) .ki-step.is-ping::after { animation: ki-step-ping 1s var(--ease-settle), ki-step-ping-again 1s var(--ease-settle); }

/* KI-Badge: umrandet, bis die Station erreicht ist – dann gefüllt. */
.ki-net-on .ki-step .ki-badge { background: transparent; color: var(--lime); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--lime) 65%, transparent); transition: background-color 0.4s ease, color 0.4s ease, box-shadow 0.4s ease; }
.ki-net-on .ki-step.is-lit .ki-badge { background: var(--lime); color: var(--on-lime); box-shadow: none; }
.ki-net-on:not(.ki-net-static) .ki-step.is-lit .ki-badge { animation: ki-badge-glow 1.4s ease-out; }

/* Lichtpunkt an der Spitze wachsender Linien (Lage per transform aus ki-net.js). */
.net-head { opacity: 0; }
.net-head-glow { fill: var(--lime); opacity: 0.35; }
.net-head-core { fill: var(--packet-core); }
/* Strom: Pakete wie das Signalpaket (--packet-*), etwas kleiner, damit sie durch die Netze passen.
   Kopf mit Lichtschein (Lage per transform), heller Schweif als Strich auf der Route
   (stroke-dashoffset). Auf der Rückkopplung bleibt ein Lime-Punkt (Kern --loop; weißer Kern und
   Schweif blenden aus). Deckkraft je Paket und den Schein der Stationen, Badges und Netzknoten
   schreibt ki-net.js (nur opacity). */
.net-packet { opacity: 0; }
.net-packet-tail { fill: none; stroke: var(--packet-trail); stroke-width: 3px; stroke-linecap: round; }
.net-packet-core { fill: var(--packet-core); }
.net-packet-core--loop { fill: var(--lime); }
.net-glow-fill stop { stop-color: var(--lime); }
.net-glow { opacity: 0; }

@keyframes ki-step-ping { 0% { opacity: 0.9; transform: scale(1); } 100% { opacity: 0; transform: scale(3.2); } }
@keyframes ki-step-ping-again { 0% { opacity: 0.9; transform: scale(1); } 100% { opacity: 0; transform: scale(3.2); } }
@keyframes ki-badge-glow { 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--lime) 70%, transparent); } 100% { box-shadow: 0 0 0 10px color-mix(in srgb, var(--lime) 0%, transparent); } }

/* ---------- Produkte: Faden läuft an der Oberkante der Karten entlang ---------- */
/* Statuspunkt vor der Produktzeile (ohne JS und statisch: an). */
.product-tag { display: flex; align-items: center; gap: 0.55rem; }
.product-tag::before { content: ""; flex: none; width: var(--status-dot); height: var(--status-dot); border-radius: 50%; background: var(--lime); }
/* Statisch (reduzierte Bewegung): alle Karten sind angebunden. */
:is(.product, .logo-net).is-reached { border-top-color: var(--lime); }

/* Zweige zu den Karten so stark wie die Kartenkante (2 px). */
.thread-branch--card { stroke-width: 2px; }

/* Live: Oberkante gedimmt; erreicht der Faden die Karte, zeichnet sich die
   Lime-Linie mit einem Lichtpunkt an der Spitze, danach geht der Statuspunkt an. */
.thread-live .product { position: relative; }
/* Dieselbe Kante an der Logo-Wand (Kunden), dort länger wegen der Breite: --edge-draw. */
.thread-live :is(.product, .logo-net), .thread-live :is(.product, .logo-net).is-reached { border-top-color: var(--line-2); }
.thread-live :is(.product, .logo-net)::before { content: ""; position: absolute; left: 0; right: 0; top: -2px; height: 2px; background: var(--lime); transform: scaleX(0); transform-origin: left center; transition: transform 0.3s ease; pointer-events: none; }
.thread-live :is(.product, .logo-net).is-reached::before { transform: none; transition: transform var(--edge-draw, 0.9s) cubic-bezier(0.65, 0, 0.35, 1) var(--thread-arrive, 0ms); }
.thread-live :is(.product, .logo-net)::after { content: ""; position: absolute; left: 0; top: -8px; width: 100%; height: 14px; background: radial-gradient(circle at calc(100% - 7px) 50%, #f2ffd9 0 2px, color-mix(in srgb, var(--lime) 70%, transparent) 3px, transparent 7px); opacity: 0; pointer-events: none; }
.thread-live :is(.product, .logo-net).is-reached::after { animation: card-sweep var(--edge-draw, 0.9s) cubic-bezier(0.65, 0, 0.35, 1) var(--thread-arrive, 0ms) both; }
.thread-live .product-tag::before { background: var(--line-2); transition: background-color 0.3s ease, box-shadow 0.3s ease; }
.thread-live .product.is-reached .product-tag::before { background: var(--lime); box-shadow: 0 0 8px 2px color-mix(in srgb, var(--lime) 45%, transparent); transition-delay: calc(var(--thread-arrive, 0ms) + 500ms); }
/* Jedes weitere Paket, das in die Karte läuft, lässt den Statuspunkt kurz aufglühen. */
.thread-live .product.is-reached.thread-pulse .product-tag::before { box-shadow: 0 0 14px 5px color-mix(in srgb, var(--lime) 65%, transparent); transition-delay: 0ms; }

@keyframes card-sweep { 0% { opacity: 0; transform: translateX(calc(-100% + 7px)); } 6% { opacity: 1; } 88% { opacity: 1; } 100% { opacity: 0; transform: translateX(7px); } }

/* ---------- Produkte: echter Screen in der SnapField-Karte ---------- */
/* Ausschnitt aus sf-report (dieselben Dateien wie Leistung 03): Kennzahlen und Foto-Nachweise,
   ohne die Kopfzeile mit den Exportformaten (products.md: Formate werden nicht genannt).
   Bildausschnitt 960 × 368 ab x 120 / y 118 im 1200er-Bild; Ränder in % der Ausschnittbreite.
   Standby und Einschalten übernimmt die Bühne (home.css .stage-screen/.stage-boot, is-live
   setzt home-main.js initProductScreens). */
.product-media { max-width: 44rem; margin: var(--s-5) 0 var(--s-4); }
.product-crop { display: block; aspect-ratio: 960 / 368; overflow: hidden; }
.product-crop img { display: block; width: 125%; max-width: none; height: auto; margin: -12.2917% 0 0 -12.5%; }

/* ---------- Kunden: Logo-Wand am Faden (customer-wall.js) ---------- */
/* Eigener Abschnitt mit eigener h2, kein Produktbezug. Oberkante (2 px) wie die Produktkarten –
   der Faden dockt dort als Knoten „card" an; innen ein Haarlinien-Netz. Jede Zelle zieht ihre
   rechte und untere Linie; die Außenlinien schneidet clip-path ab (keine positionsabhängigen
   Selektoren, css.md). Schmal 2 Spalten, ab 60rem 8 (16 Logos = 8 × 2 = 2 × 8, ohne Lücke). */
.logo-net { --edge-draw: 1.2s; --wall-dim: 0.18; --wall-lit: 0.86; --wall-glow: color-mix(in srgb, var(--lime) 65%, transparent); position: relative; border-top: 2px solid var(--line-2); }
.logo-wall { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 0; padding: 0; list-style: none; clip-path: inset(0 1px 1px 0); }
.logo-cell { position: relative; display: grid; place-items: center; min-height: 5.25rem; padding: var(--s-3) var(--s-2); border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.logo-img { display: block; width: min(132px, 86%); height: auto; opacity: var(--wall-lit); }
@media (min-width: 60rem) {
  .logo-wall { grid-template-columns: repeat(8, minmax(0, 1fr)); }
  .logo-cell { min-height: 6.25rem; }
}
/* Kontrastmodus: Die Logos sind helle Tinte auf transparentem Grund (optimize-customer-logos.mjs)
   und wären auf einem hellen Canvas unsichtbar. Die Wand behält deshalb ihre dunkle Fläche. */
@media (forced-colors: active) { .logo-wall { forced-color-adjust: none; background-color: var(--bg); } .wall-run, .wall-trace, .logo-glow { display: none; } }
/* Live: Logos im Standby, bis der Faden sie erreicht (Aufleuchten per WAAPI, customer-wall.js);
   nur das Dimmen beim Zurückscrollen ist ein Übergang. */
.wall-armed .logo-img { opacity: var(--wall-dim); transition: opacity var(--phase-back) var(--ease-in); }
.wall-armed .logo-cell.is-lit .logo-img { opacity: var(--wall-lit); transition: none; }
/* Lichtlauf (nur mit Bewegung): Paket wie der Herzschlag am Kontakt (.thread-packet--beat), dahinter
   eine Spur in Lime auf der Rasterlinie (Grundlänge 100 px, Länge per scaleX), in den Nachbarzellen
   ein Schein von der Seite der Linie (is-left/-right/-top/-bottom setzt customer-wall.js je Lauf).
   Animiert werden nur transform und opacity (WAAPI). Maße wie die Umgebungspakete (--packet-*,
   TAIL in customer-wall.js) – das größere Herzschlag-Paket bleibt dem Kontakt vorbehalten. */
.wall-run { z-index: 2; }
.wall-run.thread-packet--beat { --beat-tail: var(--packet-tail); --beat-size: var(--packet-size); }
.wall-trace { position: absolute; left: 0; top: -1px; z-index: 1; width: 100px; height: 2px; background: var(--lime); transform-origin: 0 50%; opacity: 0; pointer-events: none; will-change: transform, opacity; }
.logo-glow { position: absolute; inset: 0; opacity: 0; pointer-events: none; }
.logo-glow.is-left { box-shadow: inset 20px 0 24px -16px var(--wall-glow); }
.logo-glow.is-right { box-shadow: inset -20px 0 24px -16px var(--wall-glow); }
.logo-glow.is-top { box-shadow: inset 0 20px 24px -16px var(--wall-glow); }
.logo-glow.is-bottom { box-shadow: inset 0 -20px 24px -16px var(--wall-glow); }

/* ---------- Kontakt: Der Faden endet an der Kontaktaktion ---------- */
.contact-actions { margin-top: var(--s-5); }
/* Signal am Kontakt (contact-signal.js, nur mit Bewegung): Rahmen, Schein und Ring liegen außerhalb
   des Buttons, die Lichtkante auf seiner Fläche unter der Schrift – isolation macht den Button zum
   eigenen Stapelkontext, die Kante (z-index -1) liegt damit über dem Grund und unter dem Text, der
   Kontrast der Schrift steigt beim Überstreichen nur. Reine Dekoration; Fläche, Schrift, Hover und
   Fokus des Buttons bleiben unberührt. */
.cta-armed { position: relative; isolation: isolate; }
.cta-frame { position: absolute; max-width: none; overflow: visible; opacity: 0; pointer-events: none; }
.cta-frame path { fill: none; stroke: var(--lime); stroke-dasharray: 1 1; stroke-dashoffset: 1; }
.cta-frame-line { stroke-width: 2px; }
.cta-frame-glow { stroke-width: 8px; stroke-opacity: 0.26; }
/* Ring: zwei Halbkreise und zwei Kanten (Lage und Maße setzt contact-signal.js), Strich 2 px = RING_W. */
.cta-ring { position: absolute; inset: 0; opacity: 0; pointer-events: none; }
.cta-ring-left, .cta-ring-right { position: absolute; border: 2px solid var(--lime-2); border-radius: 50%; }
.cta-ring-left { clip-path: inset(0 50% 0 0); }
.cta-ring-right { clip-path: inset(0 0 0 50%); }
.cta-ring-top, .cta-ring-bottom { position: absolute; height: 2px; background: var(--lime-2); }
.cta-ring-top { transform-origin: 50% 0; }
.cta-ring-bottom { transform-origin: 50% 100%; }
.cta-glow { position: absolute; inset: 0; border-radius: inherit; box-shadow: 0 0 20px 1px color-mix(in srgb, var(--lime) 70%, transparent), 0 0 52px 10px color-mix(in srgb, var(--lime) 28%, transparent); opacity: 0; pointer-events: none; }
.cta-sheen { position: absolute; inset: 0; z-index: -1; overflow: hidden; border-radius: inherit; pointer-events: none; }
.cta-sheen-band { position: absolute; left: 0; top: -20%; width: 14px; height: 140%; background: color-mix(in srgb, var(--packet-core) 70%, transparent); box-shadow: 0 0 14px 7px color-mix(in srgb, var(--packet-core) 38%, transparent); transform: translateX(-80px) skewX(-18deg); }
.contact-cta:is(:hover, :focus-visible) :is(.cta-glow, .cta-ring, .cta-sheen), .contact-cta:focus-visible .cta-frame { visibility: hidden; }
/* Paket des Herzschlags: größer und heller als die Umgebungspakete. --beat-tail = BEAT_TAIL in thread-fx.js. */
.thread-packet--beat { --beat-tail: 80px; --beat-size: 14px; width: var(--beat-tail); height: 4px; margin: -2px 0 0 calc(var(--beat-tail) * -1); background: none; }
.thread-packet-tail { position: absolute; inset: 0; border-radius: 4px; background: linear-gradient(90deg, transparent, var(--packet-trail)); transform-origin: 100% 50%; }
.thread-packet--beat::after { z-index: 1; right: calc(var(--beat-size) / -2); top: 50%; width: var(--beat-size); height: var(--beat-size); margin-top: calc(var(--beat-size) / -2); background: var(--packet-core); box-shadow: 0 0 16px 5px color-mix(in srgb, var(--lime) 90%, transparent); }
@media (forced-colors: active) { .cta-frame, .cta-glow, .cta-ring, .cta-sheen { display: none; } }

/* ---------- Angehalten bzw. reduzierte Bewegung ---------- */
/* Themenbereich ohne harte Kante (home-main.js initToneShift): feste Ebene hinter der Seite
   im Grundton von .section-alt, die Deckkraft folgt dem Scroll. Neues Muster, weil der
   Grundton-Wechsel an einer harten Kante der einzige Schnitt im unteren Seitenteil war.
   Flache Farbe, kein Verlauf (keine dekorativen Gradients). z-index -1 setzt voraus, dass
   html keinen eigenen Hintergrund hat (der Body-Hintergrund liegt auf der Leinwand); Höhe
   100lvh statt inset: 0 gegen einen Streifen beim Ein-/Ausfahren der mobilen Adressleiste.
   Ohne JS und bei reduzierter Bewegung: die statischen Bänder. */
.tone-layer { position: fixed; left: 0; top: 0; z-index: -1; width: 100%; height: 100lvh; background: var(--bg-2); opacity: 0; pointer-events: none; }
.tone-live .section-alt { background: transparent; }

@media (prefers-reduced-motion: reduce) {
  .thread-live :is(.product, .logo-net).is-reached::after, .ki-net-on:not(.ki-net-static) .ki-step:is(.is-lit, .is-ping)::after, .ki-net-on:not(.ki-net-static) .ki-step.is-lit .ki-badge { animation: none; }
  .net-head, .net-flow, .net-glows, .wall-trace, .logo-glow, .cta-frame, .cta-glow, .cta-ring, .cta-sheen, .thread-packet--beat { display: none; }
  .wall-armed .logo-img { opacity: var(--wall-lit); transition: none; }
}
