/**
 * Neural Network Background Animation
 * Modern, darker, mystical theme using WebGL
 * Subtle flowing network aesthetic
 */

class NeuralNetworkBackground {
  constructor() {
    // Canvas setup
    this.canvas = document.createElement('canvas');
    this.canvas.id = 'neural-network-background';
    this.canvas.style.position = 'fixed';
    this.canvas.style.top = '0';
    this.canvas.style.left = '0';
    this.canvas.style.width = '100%';
    this.canvas.style.height = '100%';
    this.canvas.style.zIndex = '-10'; // Ensure it's behind content but visible
    this.canvas.style.pointerEvents = 'none';
    this.canvas.style.opacity = '1'; // Force visibility
    this.canvas.style.transition = 'opacity 3.5s ease-in-out'; // Längere Übergangsanimation (von 1.5s auf 3.5s)
    
    // If canvas already exists, remove it first
    const existingCanvas = document.getElementById('neural-network-background');
    if (existingCanvas) {
      existingCanvas.remove();
    }
    
    // Append to body as first child to ensure it's behind everything
    if (document.body.firstChild) {
      document.body.insertBefore(this.canvas, document.body.firstChild);
    } else {
      document.body.appendChild(this.canvas);
    }
    
    // WebGL context
    this.gl = this.canvas.getContext('webgl') || this.canvas.getContext('experimental-webgl');
    if (!this.gl) {
      console.warn('WebGL not supported, falling back to canvas rendering');
      this.gl = null;
      this.ctx = this.canvas.getContext('2d');
      this.useWebGL = false;
    } else {
      this.useWebGL = true;
    }
    
    // Animation properties
    this.nodes = [];
    this.connections = [];
    this.flows = []; // Flow animations along connections
    this.animationFrameId = null;
    this.isDarkMode = true; // Always use dark mode for the background
    this.isDestroying = false; // Flag für den Ausblendeprozess
    
    // Colors - Lila Farbpalette mit intensiveren Effekten
    this.darkModeColors = {
      background: '#040215', // Dunklerer Hintergrund mit leichtem Violett-Anteil
      nodeColor: '#6a5498',  // Lila-basierte Knotenfarbe
      nodePulse: '#9c7fe0',  // Helleres Lila für Pulsieren
      connectionColor: '#4a3870', // Dunklere Lila-Verbindungen
      flowColor: '#b47fea'   // Lebendiges Lila für Blitze
    };
    
    // Farben für Light Mode auch mit Lila-Akzenten
    this.lightModeColors = {
      background: '#f8f6fc',      // Sehr heller Lila-Hintergrund
      nodeColor: '#6a5498',       // Gleiche Lila-Töne wie im Dark Mode
      nodePulse: '#9c7fe0',       // für konsistentes Erscheinungsbild
      connectionColor: '#7e70a5',  // Helleres Lila für Verbindungen
      flowColor: '#a06cd5'        // Sanfteres Lila für Blitze
    };
    
    // Konfigurationsobjekt für schlangenartige, leuchtende Blitze und permanentes Netzwerk
    this.config = {
      nodeCount: 100,              // Weniger Knoten für klarere Strukturen
      nodeSize: 3.2,               // Etwas größere Knoten für bessere Sichtbarkeit
      nodeVariation: 8,         // Reduzierte Varianz für konsistenteres Erscheinungsbild
      connectionDistance: 100,    // Größere Verbindungsdistanz für weitläufigeres Netzwerk
      connectionOpacity: 0.3,     // Erhöhte Opazität für sichtbarere dauerhafte Verbindungen
      animationSpeed: 0.1,      // Noch langsamere Animation für sanftere Bewegung
      pulseSpeed: 0,          // Sehr langsames Pulsieren für ruhige Animation
      flowSpeed: 0.15,            // Deutlich langsamere Blitze für schlangenartige Bewegung
      flowDensity: 2,            // Weniger gleichzeitige Blitze für bessere Verteilung
      flowLength: 0.05,           // Längere einzelne Flows für sichtbare "Schlangen"
      maxConnections: 12,         // Mehr Verbindungen pro Neuron für dichtere Netzwerke
      clusteringFactor: 0.005,      // Stärkeres Clustering für deutlichere Strukturen
      linesFadeDuration: 600,   // Noch längere Dauer fürs Ein-/Ausblenden für permanentes Netzwerk (ms)
      linesWidth: 1.0,            // Etwas dickere Linien für bessere Sichtbarkeit
      linesOpacity: 0.75,         // Höhere Opazität für das permanente Netzwerk
      maxFlowCount: 1,          // Weniger gleichzeitige Flows für bessere Übersicht
      glowIntensity: 1.0,         // Verstärkter Glow-Effekt
      sparkCount: 1,              // Mehr Funken
      sparkSize: 1,             // Größere Funken
      blurRadius: 10,             // Blur-Radius für Glow-Effekte
      minActiveFlows: 1,          // Minimale Anzahl aktiver Flows
      flowForwardingRate: 1.0,    // 100% Weiterleitungsrate für endlose Flows
      maxFlowGenerations: 10     // Sehr hohe Generationsgrenze für endlose Animation
    };
    
    // Initialize
    this.init();
    
    // Event listeners
    window.addEventListener('resize', this.resizeCanvas.bind(this));
    document.addEventListener('darkModeToggled', (event) => {
      this.isDarkMode = event.detail.isDark;
    });
    
    // Log that the background is initialized
    console.log('Neural Network Background initialized');
  }
  
  init() {
    this.resizeCanvas();
    
    if (this.useWebGL) {
      this.initWebGL();
    }
    
    this.createNodes();
    this.createConnections();
    this.startAnimation();
  }
  
  resizeCanvas() {
    const pixelRatio = window.devicePixelRatio || 1;
    const width = window.innerWidth;
    const height = window.innerHeight;
    
    this.canvas.style.width = width + 'px';
    this.canvas.style.height = height + 'px';
    this.canvas.width = width * pixelRatio;
    this.canvas.height = height * pixelRatio;
    
    if (this.useWebGL) {
      this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
    } else if (this.ctx) {
      this.ctx.scale(pixelRatio, pixelRatio);
    }
    
    // Recalculate node positions after resize
    if (this.nodes.length) {
      this.createNodes();
      this.createConnections();
    }
  }
  
  initWebGL() {
    // Vertex shader
    const vsSource = `
      attribute vec2 aVertexPosition;
      attribute float aPointSize;
      uniform vec2 uResolution;
      
      void main() {
        // Convert from pixel to clip space
        vec2 position = (aVertexPosition / uResolution) * 2.0 - 1.0;
        // Flip Y coordinate
        position.y = -position.y;
        
        gl_Position = vec4(position, 0, 1);
        gl_PointSize = aPointSize;
      }
    `;
    
    // Fragment shader - Softer glow effect
    const fsSource = `
      precision mediump float;
      uniform vec4 uColor;
      
      void main() {
        float distance = length(gl_PointCoord - vec2(0.5, 0.5));
        
        // Softer glow with smoother falloff
        float alpha = 1.0 - smoothstep(0.1, 0.5, distance);
        alpha = pow(alpha, 1.5); // Make the glow even softer
        
        gl_FragColor = vec4(uColor.rgb, uColor.a * alpha);
      }
    `;
    
    // Initialize shaders
    const vertexShader = this.loadShader(this.gl.VERTEX_SHADER, vsSource);
    const fragmentShader = this.loadShader(this.gl.FRAGMENT_SHADER, fsSource);
    
    // Create shader program
    this.shaderProgram = this.gl.createProgram();
    this.gl.attachShader(this.shaderProgram, vertexShader);
    this.gl.attachShader(this.shaderProgram, fragmentShader);
    this.gl.linkProgram(this.shaderProgram);
    
    if (!this.gl.getProgramParameter(this.shaderProgram, this.gl.LINK_STATUS)) {
      console.error('Unable to initialize the shader program: ' + 
                   this.gl.getProgramInfoLog(this.shaderProgram));
      return;
    }
    
    // Get attribute and uniform locations
    this.programInfo = {
      program: this.shaderProgram,
      attribLocations: {
        vertexPosition: this.gl.getAttribLocation(this.shaderProgram, 'aVertexPosition'),
        pointSize: this.gl.getAttribLocation(this.shaderProgram, 'aPointSize')
      },
      uniformLocations: {
        resolution: this.gl.getUniformLocation(this.shaderProgram, 'uResolution'),
        color: this.gl.getUniformLocation(this.shaderProgram, 'uColor')
      }
    };
    
    // Create buffers
    this.positionBuffer = this.gl.createBuffer();
    this.sizeBuffer = this.gl.createBuffer();
    
    // Set clear color for WebGL context
    const bgColor = this.hexToRgb(this.darkModeColors.background);
    
    this.gl.clearColor(bgColor.r/255, bgColor.g/255, bgColor.b/255, 1.0);
  }
  
  loadShader(type, source) {
    const shader = this.gl.createShader(type);
    this.gl.shaderSource(shader, source);
    this.gl.compileShader(shader);
    
    if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
      console.error('An error occurred compiling the shaders: ' + 
                  this.gl.getShaderInfoLog(shader));
      this.gl.deleteShader(shader);
      return null;
    }
    
    return shader;
  }
  
  createNodes() {
    this.nodes = [];
    const width = this.canvas.width / (window.devicePixelRatio || 1);
    const height = this.canvas.height / (window.devicePixelRatio || 1);
    
    // Erstelle Cluster-Zentren für neuronale Netzwerkmuster mit erhöhter Komplexität
    const clusterCount = Math.floor(8 + Math.random() * 6); // 8-13 Cluster für mehr Dichte
    const clusters = [];
    
    for (let i = 0; i < clusterCount; i++) {
      // Dynamischere Clustergrößen mit größerer Variationsbreite
      const baseRadius = 120 + Math.random() * 200; // Größere Basisradien
      const radiusVariation = 0.3 + Math.random() * 0.4; // 30-70% Variation
      
      clusters.push({
        x: Math.random() * width,
        y: Math.random() * height,
        radius: baseRadius * (1 + radiusVariation), // Dynamischere Größen
        density: 0.7 + Math.random() * 0.3, // Dichtefaktor für Knotenverteilung
        influence: 0.5 + Math.random() * 0.5 // Einflussstärke auf umliegende Knoten
      });
    }
    
    // Create nodes with random positions and properties
    for (let i = 0; i < this.config.nodeCount; i++) {
      // Entscheide, ob dieser Knoten zu einem Cluster gehört oder nicht
      const useCluster = Math.random() < this.config.clusteringFactor;
      let x, y;
      
      if (useCluster && clusters.length > 0) {
        // Wähle ein zufälliges Cluster
        const cluster = clusters[Math.floor(Math.random() * clusters.length)];
        const angle = Math.random() * Math.PI * 2;
        const distance = Math.random() * cluster.radius;
        
        // Platziere in der Nähe des Clusters mit einiger Streuung
        x = cluster.x + Math.cos(angle) * distance;
        y = cluster.y + Math.sin(angle) * distance;
        
        // Stelle sicher, dass es innerhalb des Bildschirms bleibt
        x = Math.max(0, Math.min(width, x));
        y = Math.max(0, Math.min(height, y));
      } else {
        // Zufällige Position außerhalb von Clustern
        x = Math.random() * width;
        y = Math.random() * height;
      }
      
      // Bestimme die Knotengröße - wichtigere Knoten (in Clustern) sind deutlich größer
      const nodeImportance = useCluster ? 1.8 : 0.6; // Erhöhter Kontrast zwischen Cluster- und Nicht-Cluster-Knoten
      const size = this.config.nodeSize * nodeImportance + Math.random() * this.config.nodeVariation * 1.5; // Größere Variationsbreite
      
      const node = {
        x: x,
        y: y,
        size: size,
        speed: {
          x: (Math.random() - 0.5) * this.config.animationSpeed,
          y: (Math.random() - 0.5) * this.config.animationSpeed
        },
        pulsePhase: Math.random() * Math.PI * 2, // Random starting phase
        connections: [],
        isActive: Math.random() < 0.3, // Some nodes start active for neural firing effect
        lastFired: 0,                  // For neural firing animation
        firingRate: 1000 + Math.random() * 4000 // Random firing rate in ms
      };
      
      this.nodes.push(node);
    }
  }
  
  createConnections() {
    this.connections = [];
    this.flows = []; // Reset flows
    
    // Create connections between nearby nodes
    for (let i = 0; i < this.nodes.length; i++) {
      const nodeA = this.nodes[i];
      nodeA.connections = [];
      
      // Sortiere andere Knoten nach Entfernung für bevorzugte nahe Verbindungen
      const potentialConnections = [];
      
      for (let j = 0; j < this.nodes.length; j++) {
        if (i === j) continue;
        
        const nodeB = this.nodes[j];
        const dx = nodeB.x - nodeA.x;
        const dy = nodeB.y - nodeA.y;
        const distance = Math.sqrt(dx * dx + dy * dy);
        
        // Erhöhe die Wahrscheinlichkeit für Verbindungen
        if (distance < this.config.connectionDistance * 1.5) { // Erweiterter Verbindungsradius
          potentialConnections.push({
            index: j,
            distance: distance,
            // Zufälliger Bonus für mehr Verbindungen
            connectionBonus: Math.random() * 0.5
          });
        }
      }
      
      // Sortiere nach Entfernung und Verbindungsbonus mit erweiterten Kriterien
      potentialConnections.sort((a, b) => {
        // Basis-Score aus Entfernung und Verbindungsbonus
        const baseScoreA = a.distance * (1 - a.connectionBonus);
        const baseScoreB = b.distance * (1 - b.connectionBonus);
        
        // Zusätzliche Faktoren für dynamischere Verbindungen
        const randomFactorA = Math.random() * 0.2; // Leichte Zufallskomponente
        const randomFactorB = Math.random() * 0.2;
        
        // Kombinierter Score mit Zufallskomponente
        const scoreA = baseScoreA * (1 - randomFactorA);
        const scoreB = baseScoreB * (1 - randomFactorB);
        
        return scoreA - scoreB;
      });
      
      // Erhöhe die maximale Anzahl der Verbindungen signifikant
      const maxConn = Math.min(
        this.config.maxConnections * 2.5, // Deutlich mehr Verbindungen erlaubt
        potentialConnections.length, 
        3 + Math.floor(Math.random() * this.config.maxConnections * 2) // Noch mehr zufällige Verbindungen
      );
      for (let c = 0; c < maxConn; c++) {
        const connection = potentialConnections[c];
        const j = connection.index;
        const nodeB = this.nodes[j];
        const distance = connection.distance;
        
        // Stärkere Verbindungen durch den Bonus
        const connectionStrength = Math.max(0, 1 - distance / (this.config.connectionDistance * 1.5)) + connection.connectionBonus;
        const connOpacity = connectionStrength * this.config.connectionOpacity;
        
        // Check if connection already exists
        if (!this.connections.some(conn => 
          (conn.from === i && conn.to === j) || (conn.from === j && conn.to === i)
        )) {
          // Neue Verbindung mit Ein-/Ausblend-Status
          this.connections.push({
            from: i,
            to: j,
            distance: distance,
            opacity: connOpacity,
            strength: connectionStrength,
            hasFlow: false,
            lastActivated: 0,
            progress: 0, // Verbindung beginnt unsichtbar und baut sich auf
            fadeState: 'in', // Status: 'in' = einblenden, 'visible' = sichtbar, 'out' = ausblenden
            fadeStartTime: Date.now(), // Wann der Fade-Vorgang gestartet wurde
            fadeTotalDuration: this.config.linesFadeDuration, // Feste Dauer ohne Zufall
            visibleDuration: 10000, // Feste Sichtbarkeitsdauer ohne Zufall
            fadeProgress: 0, // Aktueller Fortschritt des Fade-Vorgangs (0-1)
            buildSpeed: 0 // Geschwindigkeit, mit der die Verbindung aufgebaut wird
          });
          nodeA.connections.push(j);
          nodeB.connections.push(i);
        }
      }
    }
  }
  
  startAnimation() {
    this.animationFrameId = requestAnimationFrame(this.animate.bind(this));
  }
  
  animate() {
    // Update nodes
    const width = this.canvas.width / (window.devicePixelRatio || 1);
    const height = this.canvas.height / (window.devicePixelRatio || 1);
    const now = Date.now();
    
    // Berechne den Fadeout-Faktor bei Deaktivierung (1.0 bis 0.0)
    const fadeoutFactor = this.isDestroying ? parseFloat(this.canvas.style.opacity) || 0.98 : 1.0;
    
    // Für jeden Flow eine eindeutige ID vergeben, falls noch nicht vorhanden
    for (const flow of this.flows) {
      if (!flow.id) {
        flow.id = now + '_' + Math.random().toString(36).substr(2, 9);
      }
    }
    
    // Setze zunächst alle Neuronen auf inaktiv
    for (let i = 0; i < this.nodes.length; i++) {
      // Update pulse phase for smoother animation
      const node = this.nodes[i];
      
      // Bei Ausblendung langsamer pulsieren lassen
      const pulseSpeed = this.isDestroying 
        ? this.config.pulseSpeed * 0.7
        : this.config.pulseSpeed * (1 + (node.connections.length * 0.04));
        
      node.pulsePhase += pulseSpeed;
      
      // Bei Ausblendung Bewegung reduzieren
      const movementFactor = this.isDestroying ? 0.3 : 1.0;
      
      // Animate node position with gentler movement
      node.x += node.speed.x * (Math.sin(now * 0.0003) * 0.4 + 0.4) * movementFactor;
      node.y += node.speed.y * (Math.cos(now * 0.0002) * 0.4 + 0.4) * movementFactor;
      
      // Keep nodes within bounds
      if (node.x < 0 || node.x > width) {
        node.speed.x *= -1;
        node.x = Math.max(0, Math.min(width, node.x));
      }
      if (node.y < 0 || node.y > height) {
        node.speed.y *= -1;
        node.y = Math.max(0, Math.min(height, node.y));
      }
      
      // Knoten bleiben länger aktiv nach dem Auslösen (2000ms für langanhaltende Aktivierung)
      if (node.lastFired && now - node.lastFired < 2000) {
        node.isActive = true;
      } else {
      node.isActive = false;
      }
    }
    
    // Aktiviere Neuronen basierend auf aktiven Flows
    for (const flow of this.flows) {
      // Aktiviere den Quellknoten (der Flow geht von ihm aus)
      if (flow.sourceNodeIdx !== undefined) {
        this.nodes[flow.sourceNodeIdx].isActive = true;
        this.nodes[flow.sourceNodeIdx].lastFired = now;
      }
      
      // Aktiviere den Zielknoten nur, wenn der Flow weit genug fortgeschritten ist
      if (flow.targetNodeIdx !== undefined && flow.progress > 0.7) {
        this.nodes[flow.targetNodeIdx].isActive = true;
        this.nodes[flow.targetNodeIdx].lastFired = now;
      }
    }
    
    // Minimale Anzahl aktiver Flows sicherstellen für endlose Animation
    const minRequiredFlows = Math.min(15, Math.floor(this.nodes.length * 0.1)); // Mindestens 10% der Knoten haben aktive Flows
    const shouldCreateNewFlows = this.flows.length < minRequiredFlows;
    
    // Chance auf neue Flows
    const flowChance = this.isDestroying ? 0.005 : (shouldCreateNewFlows ? 0.1 : 0.04);
    
    // Neue Flows mit höherer Wahrscheinlichkeit erzeugen, wenn zu wenige aktiv sind
    if (Math.random() < flowChance) {
      this.initiateMajorFlowCascade(now, fadeoutFactor);
    }
    
    // Aktualisiere die Ein-/Ausblendung von Verbindungen
    this.updateConnectionsFading(now);
    
    // Update flows with proper fading
    this.updateFlows(now);
    
    // Regelmäßig neue Flows erstellen, um das Netzwerk lebendig zu halten
    if (Math.random() < 0.07) { // Hohe Chance für kontinuierliche Animation
      this.createNewFlow(now);
    }
    
    // Rekonfiguriere Verbindungen sehr selten für ein sich langsam entwickelndes Netzwerk
    // Dies verhindert, dass das Netzwerk statisch wird
    if (Math.random() < 0.0005) { // Sehr seltene Neuverbindungen (0.05% Chance pro Frame)
      this.createConnections();
    }
    
    // Render
    if (this.useWebGL) {
      this.renderWebGL(now);
    } else {
      this.renderCanvas(now);
    }
    
    // Continue animation - garantiere endlose Animation
    this.animationFrameId = requestAnimationFrame(this.animate.bind(this));
  }
  
  // Neue Methode: Starte eine größere Kaskade von Flows an strategischen Punkten
  initiateMajorFlowCascade(now, fadeoutFactor = 1.0) {
    // Finde gut vernetzte Knoten als Startpunkte
    const topNodes = [...this.nodes]
      .map((node, index) => ({ node, index, connections: node.connections.length }))
      .filter(item => item.connections > 2) // Nur Knoten mit mindestens 3 Verbindungen
      .sort((a, b) => b.connections - a.connections); // Sortiere nach Verbindungsanzahl
      
    if (topNodes.length === 0) return;
    
    // Wähle 1-3 Startknoten basierend auf Netzwerkgröße
    const startNodeCount = Math.max(1, Math.min(3, Math.floor(topNodes.length / 20)));
    
    for (let i = 0; i < startNodeCount; i++) {
      // Wähle einen der Top-Knoten, mit Präferenz für die am besten vernetzten
      const nodeIndex = Math.floor(Math.random() * Math.min(10, topNodes.length));
      const startNodeData = topNodes[nodeIndex];
      if (!startNodeData) continue;
      
      const startNode = this.nodes[startNodeData.index];
      if (!startNode || startNode.connections.length === 0) continue;
      
      // Aktiviere den Startknoten
      startNode.isActive = true;
      startNode.lastFired = now;
      startNode.lastCascade = now; // Markiere den Zeitpunkt der letzten Kaskade
      
      // Starte Flows an mehreren Verbindungen dieses Knotens
      const connectionsToActivate = Math.min(
        Math.ceil(startNode.connections.length / 2), // Bis zu 50% der Verbindungen
        5 // Aber maximal 5
      );
      
      // Sorgfältige Auswahl der Verbindungen für eine optimale Weitergabe
      const selectedConnections = [];
      const allConnections = [...startNode.connections];
      
      // Sortiere Verbindungen nach Aktivierungszeit - bevorzuge solche, die länger nicht aktiviert wurden
      const sortedConnections = allConnections
        .map(connIdx => {
          const connectedNode = this.nodes[connIdx];
        const conn = this.connections.find(c => 
            (c.from === startNodeData.index && c.to === connIdx) || 
            (c.from === connIdx && c.to === startNodeData.index)
          );
          
          return {
            connIdx,
            nodeConnections: connectedNode ? connectedNode.connections.length : 0,
            lastActivated: conn ? conn.lastActivated || 0 : 0
          };
        })
        .sort((a, b) => {
          // Priorisiere Verbindungen, die länger nicht aktiviert wurden und zu gut vernetzten Knoten führen
          const timeFactorA = (now - a.lastActivated) / 10000; // Zeit seit letzter Aktivierung in 10-Sekunden-Einheiten
          const timeFactorB = (now - b.lastActivated) / 10000;
          
          return (timeFactorB + b.nodeConnections * 0.1) - (timeFactorA + a.nodeConnections * 0.1);
        });
        
      // Wähle die besten Verbindungen aus
      for (let j = 0; j < connectionsToActivate && j < sortedConnections.length; j++) {
        selectedConnections.push(sortedConnections[j].connIdx);
      }
      
      // Startversatz für Flows eines Knotens - Verzögere die Flows leicht für Welleneffekt
      const baseDelay = 100; // Basisverzögerung in ms
      
      // Erstelle Flows mit leichter Verzögerung zwischen ihnen
      selectedConnections.forEach((connIdx, idx) => {
        const conn = this.connections.find(c => 
          (c.from === startNodeData.index && c.to === connIdx) || 
          (c.from === connIdx && c.to === startNodeData.index)
        );
        
        if (conn) {
          // Verbindung aktivieren
          conn.lastActivated = now;
          
          // Stelle sicher, dass die Verbindung dauerhaft sichtbar bleibt
          if (conn.fadeState !== 'visible') {
            conn.fadeState = 'visible';
            conn.fadeStartTime = now;
            conn.visibleDuration = 600000 + Math.random() * 300000; // 10-15 Minuten sichtbar
          }
          
          // Verbindung aufbauen
          if (conn.progress < 1) {
            conn.buildSpeed = 0.03 + Math.random() * 0.02;
          }
          
          // Leichte Verzögerung zwischen den Flows für Welleneffekt
          const delay = baseDelay + idx * 150;
          
          setTimeout(() => {
            // Erstelle nur, wenn die Animation noch läuft
            if (!this.isDestroying && this.animationFrameId) {
              // Bestimme die Richtung des Flows
              const direction = conn.from === startNodeData.index;
              
              // Erstelle den Flow
            this.flows.push({
                id: now + '_cascade_' + idx + '_' + Math.random().toString(36).substr(2, 9),
              connection: conn,
              progress: 0,
              direction: direction,
                length: this.config.flowLength * (0.8 + Math.random() * 0.4),
                creationTime: Date.now(),
                totalDuration: 700 + Math.random() * 400,
              sourceNodeIdx: direction ? conn.from : conn.to,
                targetNodeIdx: direction ? conn.to : conn.from,
                fadeFactor: fadeoutFactor,
                isForwarded: false,
                hasForwarded: false,
                generation: 1, // Erste Generation
                isCascadeRoot: true // Markiere als Wurzel einer Kaskade
              });
            }
          }, delay);
        }
      });
    }
  }
  
  // Updated method to update flow animations with proper fading
  updateFlows(now) {
    // Track new flows to add after the loop to avoid modifying the array during iteration
    const newFlows = [];
    
    // Zähle aktive Flows
    const activeFlows = this.flows.length;
    
    // Wenn zu wenige Flows aktiv sind, starte neue Major Flow Cascades
    if (activeFlows < this.config.minActiveFlows) {
      this.initiateMajorFlowCascade(now);
    }

    // Update existing flows
    for (let i = this.flows.length - 1; i >= 0; i--) {
      const flow = this.flows[i];
      
      // Calculate flow age for fading effects
      const flowAge = now - flow.creationTime;
      const flowProgress = flowAge / flow.totalDuration;
      
      // Update flow progress with reduced speed für schlangenartige Bewegung
      // Längere Generationen bewegen sich noch langsamer für sichtbare "Schlangen"
      const generationSlowdown = Math.max(0.7, 1.0 - (flow.generation || 1) * 0.015);
      flow.progress += (this.config.flowSpeed * generationSlowdown) / flow.connection.distance;
      
      // Aktiviere Quell- und Zielknoten basierend auf Flow-Fortschritt
      if (flow.sourceNodeIdx !== undefined) {
        // Quellknoten immer aktivieren, solange der Flow aktiv ist
        this.nodes[flow.sourceNodeIdx].isActive = true;
        this.nodes[flow.sourceNodeIdx].lastFired = now;
      }
      
      // Zielknoten aktivieren und Weiterleitung starten, wenn der Flow ihn erreicht hat
      if (flow.targetNodeIdx !== undefined && flow.progress > 0.80) {
        const targetNode = this.nodes[flow.targetNodeIdx];
        targetNode.isActive = true;
        targetNode.lastFired = now;
        
        // Stelle sicher, dass die Verbindung dauerhaft sichtbar bleibt
        flow.connection.lastActivated = now;
        if (flow.connection.fadeState !== 'visible') {
          flow.connection.fadeState = 'visible';
          flow.connection.fadeStartTime = now;
          flow.connection.visibleDuration = 600000 + Math.random() * 300000; // 10-15 Minuten sichtbar
        }
        
        // Flow ist am Ziel angekommen, starte Weiterleitung zu anderen Knoten
        // Jeder Flow leitet genau einmal weiter
        if (!flow.hasForwarded && targetNode.connections.length > 0) {
          flow.hasForwarded = true;
          
          // Durch reduzierte Anzahl der Weiterleitungen schaffen wir einen endloseren Eindruck
          const connCount = Math.min(3, targetNode.connections.length);
          const availableConnections = [...targetNode.connections];
          
          // Keine Weiterleitung an die Quelle des Flows zurück
          if (flow.sourceNodeIdx !== undefined && availableConnections.length > 1) {
            const sourceIndex = availableConnections.indexOf(flow.sourceNodeIdx);
            if (sourceIndex >= 0) {
              availableConnections.splice(sourceIndex, 1);
            }
          }
          
          // Wenn noch Verbindungen übrig sind, leite weiter
          if (availableConnections.length > 0) {
            // Priorisiere ungenutzte Pfade für ein dynamischeres Netzwerk
            const sortedConnections = availableConnections.map(nodeIdx => {
              const conn = this.connections.find(c => 
                (c.from === flow.targetNodeIdx && c.to === nodeIdx) || 
                (c.from === nodeIdx && c.to === flow.targetNodeIdx)
              );
              
              const timeSinceLastActivation = conn ? now - (conn.lastActivated || 0) : 0;
              const connectionCount = this.nodes[nodeIdx].connections.length;
              
              return {
                nodeIdx,
                connectionCount,
                timeSinceLastActivation,
                score: (timeSinceLastActivation * 0.00001) +
                       (connectionCount * 0.2) +
                       (Math.random() * 0.5)
              };
            }).sort((a, b) => b.score - a.score);
            
            // Wähle priorisierte Verbindungen
            for (let j = 0; j < connCount && j < sortedConnections.length; j++) {
              const nextNodeIdx = sortedConnections[j].nodeIdx;
              const nextConn = this.connections.find(c => 
                (c.from === flow.targetNodeIdx && c.to === nextNodeIdx) || 
                (c.from === nextNodeIdx && c.to === flow.targetNodeIdx)
              );
              
              if (nextConn) {
                nextConn.lastActivated = now;
                
                if (nextConn.fadeState !== 'visible') {
                  nextConn.fadeState = 'visible';
                  nextConn.fadeStartTime = now;
                  nextConn.visibleDuration = 600000 + Math.random() * 300000;
                }
                
                if (nextConn.progress < 1) {
                  nextConn.buildSpeed = 0.04 + Math.random() * 0.02;
                }
                
                const nextGeneration = (flow.generation || 1) + 1;
                
                if (nextGeneration <= this.config.maxFlowGenerations) {
                  const direction = nextConn.from === flow.targetNodeIdx;
                  
                  // Berechne Verzögerung basierend auf Verbindungslänge
                  const connectionLength = Math.sqrt(
                    Math.pow(this.nodes[nextConn.to].x - this.nodes[nextConn.from].x, 2) +
                    Math.pow(this.nodes[nextConn.to].y - this.nodes[nextConn.from].y, 2)
                  );
                  
                  const baseDelay = 80;
                  const lengthFactor = Math.min(1.2, connectionLength / 200);
                  const genFactor = Math.max(0.2, 1.0 - (nextGeneration * 0.005));
                  const delay = baseDelay * lengthFactor * genFactor;
                  
                  setTimeout(() => {
                    if (!this.isDestroying && this.animationFrameId) {
                      newFlows.push({
                        id: now + '_' + Math.random().toString(36).substr(2, 9),
                        connection: nextConn,
                        progress: 0,
                        direction: direction,
                        length: this.config.flowLength * (0.9 + Math.random() * 0.2),
                        creationTime: Date.now(),
                        totalDuration: 600 + Math.random() * 300 + (nextGeneration * 5),
                        sourceNodeIdx: direction ? nextConn.from : nextConn.to,
                        targetNodeIdx: direction ? nextConn.to : nextConn.from,
                        fadeFactor: flow.fadeFactor || 1.0,
                        isForwarded: false,
                        generation: nextGeneration,
                        hasForwarded: false,
                        parentFlow: flow.id
                      });
                    }
                  }, delay);
                }
              }
            }
          }
        }
      }
      
      // Stellen Sie sicher, dass die Verbindung aktiv bleibt
      flow.connection.lastActivated = now;
      
      if (flow.connection.fadeState === 'visible') {
        flow.connection.visibleDuration = Math.max(
          flow.connection.visibleDuration || 300000,
          600000 + Math.random() * 300000
        );
      }
      
      // Remove completed flows, but ensure parent-child relationships for visible "snakes"
      const isComplete = flow.progress > 1.0 || flowProgress >= 1.0;
      const hasMaxGeneration = flow.generation && flow.generation > this.config.maxFlowGenerations;
      
      if (isComplete || hasMaxGeneration) {
        this.flows.splice(i, 1);
      }
    }
    
    // Füge alle neuen Flows hinzu
    this.flows.push(...newFlows);
  }
  
  // Aktualisiert die Ein-/Ausblendung von Verbindungen mit deutlich langsameren Übergängen
  updateConnectionsFading(now) {
    for (const connection of this.connections) {
      const elapsedTime = now - connection.fadeStartTime;
      
      // Update connection fade status
      if (connection.fadeState === 'in') {
        // Einblenden - langsamer für sanfteren Aufbau
        connection.fadeProgress = Math.min(1.0, elapsedTime / (connection.fadeTotalDuration * 1.5));
        if (connection.fadeProgress >= 1.0) {
          connection.fadeState = 'visible';
          connection.fadeStartTime = now;
        }
      } else if (connection.fadeState === 'visible') {
        // Verbindung ist vollständig sichtbar - EXTREM lange Sichtbarkeitsdauer
        // für permanente Netzwerkstrukturen
        if (elapsedTime > connection.visibleDuration * 3.0) { // Erhöht von 2.5 auf 3.0
          // Prüfe, ob die Verbindung kürzlich aktiviert wurde
          if (now - connection.lastActivated < 120000) { // 2 Minuten Aktivitätsschutz
            // Wenn kürzlich aktiviert, verlängere die Sichtbarkeit
            connection.fadeStartTime = now;
          } else {
            // Nur sehr alte, inaktive Verbindungen langsam ausblenden
          connection.fadeState = 'out';
          connection.fadeStartTime = now;
          connection.fadeProgress = 1.0;
          }
        }
      } else if (connection.fadeState === 'out') {
        // Ausblenden - EXTREM langsam für dauerhaft sichtbare Strukturen
        connection.fadeProgress = Math.max(0.25, 1.0 - (elapsedTime / (connection.fadeTotalDuration * 10.0))); // Erhöht von 8.0 auf 10.0
        
        // Verbindungen bleiben dauerhaft stark sichtbar
        if (connection.fadeProgress <= 0.25) { // Minimum erhöht von 0.15 auf 0.25
          // Verbindungen niemals komplett verschwinden lassen
          connection.fadeState = 'in';
          connection.fadeStartTime = now;
          connection.fadeProgress = 0.25; // Höhere Grundsichtbarkeit
          connection.visibleDuration = 600000 + Math.random() * 300000; // 10-15 Minuten sichtbar
        }
      } else if (connection.fadeState === 'hidden') {
        // Keine Verbindungen verstecken, alle sichtbar halten
        connection.fadeState = 'in';
        connection.fadeStartTime = now;
        connection.fadeProgress = 0.25; // Höhere Grundsichtbarkeit
      }
      
      // Verbindungen schneller vollständig aufbauen für permanentes Netzwerk
      if (connection.progress < 1) {
        // Erhöhte Basisgeschwindigkeit für schnelleren Aufbau
        const baseBuildSpeed = 0.008; // Erhöht für schnelleren permanenten Aufbau
        let buildSpeed = connection.buildSpeed || baseBuildSpeed;
        
        // Wenn kürzlich aktiviert, noch schneller aufbauen
        if (now - connection.lastActivated < 2000) {
          buildSpeed = Math.max(buildSpeed, 0.015); // Erhöht für schnelleren Aufbau
        }
        
        connection.progress += buildSpeed;
        
        if (connection.progress > 1) {
          connection.progress = 1;
          // Zurücksetzen der Aufbaugeschwindigkeit
          connection.buildSpeed = 0;
        }
      }
    }
  }
  
  // Updated method to create flow animations with timing info
  createNewFlow(now) {
    if (this.connections.length === 0 || this.flows.length >= 8) return;
    
    // Verbesserte Auswahl zufälliger Verbindungen mit Präferenz für hoch vernetzte Knoten
    // Sammle alle Verbindungen und bewerte sie nach der Anzahl ihrer Verbindungen
    const weightedConnections = this.connections.map((conn, index) => {
      const fromNode = this.nodes[conn.from];
      const toNode = this.nodes[conn.to];
      const connectionCount = fromNode.connections.length + toNode.connections.length;
      return { 
        index,
        connectionCount,
        weight: connectionCount + Math.random() * 5 // Füge Zufall hinzu für Variabilität
      };
    });
    
    // Sortiere nach Gewicht (Verbindungsanzahl + Zufallsfaktor)
    weightedConnections.sort((a, b) => b.weight - a.weight);
    
    // Wähle eine der Top-Verbindungen
    const connectionIdx = weightedConnections[Math.floor(Math.random() * Math.min(15, weightedConnections.length))].index;
    const connection = this.connections[connectionIdx];
    
    // Verbindung als "im Aufbau" markieren, wenn sie noch nicht vollständig ist
    if (connection.progress < 1) {
      connection.buildSpeed = 0.02 + Math.random() * 0.01; // Schnellerer Aufbau während eines Blitzes
    }
    
    // Stelle sicher, dass die Verbindung dauerhaft sichtbar bleibt
    connection.lastActivated = now;
    if (connection.fadeState !== 'visible') {
      connection.fadeState = 'visible';
      connection.fadeStartTime = now;
      connection.visibleDuration = 150000 + Math.random() * 90000; // 2.5-4 Minuten sichtbar
    }
    
    // Create a new flow along this connection
    this.flows.push({
      connection: connection,
      progress: 0,
      direction: Math.random() > 0.5, // Randomly decide direction
      length: this.config.flowLength + Math.random() * 0.1, // Slightly vary lengths
      creationTime: now,
      totalDuration: 900 + Math.random() * 500, // Längere Gesamtdauer (900-1400ms)
      sourceNodeIdx: connection.direction ? connection.from : connection.to,
      targetNodeIdx: connection.direction ? connection.to : connection.from,
      isForwarded: false,  // Dieses Flag ermöglicht Weiterleitung 
      hasForwarded: false, // Noch nicht weitergeleitet
      generation: 1        // Erste Generation
    });
  }
  
  renderWebGL(now) {
    this.gl.clear(this.gl.COLOR_BUFFER_BIT);
    
    const width = this.canvas.width / (window.devicePixelRatio || 1);
    const height = this.canvas.height / (window.devicePixelRatio || 1);
    
    // Fade-Faktor für sanftes Ausblenden
    const fadeoutFactor = this.isDestroying ? parseFloat(this.canvas.style.opacity) || 0.98 : 1.0;
    
    // Select shader program
    this.gl.useProgram(this.programInfo.program);
    
    // Set resolution uniform
    this.gl.uniform2f(this.programInfo.uniformLocations.resolution, width, height);
    
    // Draw connections first (behind nodes)
    this.renderConnectionsWebGL(now, fadeoutFactor);
    
    // Draw flows on top of connections
    this.renderFlowsWebGL(now, fadeoutFactor);
    
    // Draw nodes
    this.renderNodesWebGL(now, fadeoutFactor);
  }
  
  renderNodesWebGL(now, fadeoutFactor = 1.0) {
    // Prepare node positions for WebGL
    const positions = new Float32Array(this.nodes.length * 2);
    const sizes = new Float32Array(this.nodes.length);
    
    for (let i = 0; i < this.nodes.length; i++) {
      const node = this.nodes[i];
      positions[i * 2] = node.x;
      positions[i * 2 + 1] = node.y;
      
      // Sanftere Pulsation mit moderaterem Aktivierungsboost
      const activationBoost = node.isActive ? 1.2 : 1.0; // Reduziert von 1.3 auf 1.2
      let pulse = (Math.sin(node.pulsePhase) * 0.2 + 1.1) * activationBoost; // Reduzierte Pulsation
      
      // Größe basierend auf Konnektivität und Wichtigkeit, aber subtiler
      const connectivityFactor = 1 + (node.connections.length / this.config.maxConnections) * 0.8; // Reduziert von 1.1 auf 0.8
      sizes[i] = node.size * pulse * connectivityFactor;
    }
    
    // Bind position buffer
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
    this.gl.bufferData(this.gl.ARRAY_BUFFER, positions, this.gl.STATIC_DRAW);
    this.gl.vertexAttribPointer(
      this.programInfo.attribLocations.vertexPosition,
      2,              // components per vertex
      this.gl.FLOAT,  // data type
      false,          // normalize
      0,              // stride
      0               // offset
    );
    this.gl.enableVertexAttribArray(this.programInfo.attribLocations.vertexPosition);
    
    // Bind size buffer
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.sizeBuffer);
    this.gl.bufferData(this.gl.ARRAY_BUFFER, sizes, this.gl.STATIC_DRAW);
    this.gl.vertexAttribPointer(
      this.programInfo.attribLocations.pointSize,
      1,              // components per vertex
      this.gl.FLOAT,  // data type
      false,          // normalize
      0,              // stride
      0               // offset
    );
    this.gl.enableVertexAttribArray(this.programInfo.attribLocations.pointSize);
    
    // Enable blending for all nodes
    this.gl.enable(this.gl.BLEND);
    this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);  // Additive blending for glow
    
    // Draw each node individually with its own color
    for (let i = 0; i < this.nodes.length; i++) {
      const node = this.nodes[i];
      
      // Set node color - sanftere Hervorhebung von aktiven Knoten
      const colorObj = this.isDarkMode ? this.darkModeColors : this.lightModeColors;
      const nodeColor = this.hexToRgb(colorObj.nodeColor);
      const nodePulseColor = this.hexToRgb(colorObj.nodePulse);
      
      // Use pulse color for active nodes
      let r = nodeColor.r / 255;
      let g = nodeColor.g / 255;
      let b = nodeColor.b / 255;
      
      // Active nodes get slightly brighter color - noch subtiler
      if (node.isActive) {
        r = (r * 0.8 + nodePulseColor.r / 255 * 0.2); // Reduziert von 0.7/0.3 auf 0.8/0.2
        g = (g * 0.8 + nodePulseColor.g / 255 * 0.2);
        b = (b * 0.8 + nodePulseColor.b / 255 * 0.2);
      }
      
      // Noch subtilere Knoten mit weiter reduzierter Opazität
      // Berücksichtige fadeoutFactor für sanftes Ausblenden
      const nodeOpacity = node.isActive ? 0.65 : 0.55; // Reduziert von 0.75/0.65 auf 0.65/0.55
      this.gl.uniform4f(
        this.programInfo.uniformLocations.color,
        r, g, b,
        nodeOpacity * fadeoutFactor // Reduzierte Opazität während des Ausblendprozesses
      );
      
      // Draw each node individually for better control
      this.gl.drawArrays(this.gl.POINTS, i, 1);
    }
  }
  
  renderConnectionsWebGL(now, fadeoutFactor = 1.0) {
    for (const connection of this.connections) {
      // Überspringe Verbindungen, die komplett unsichtbar sind
      if (connection.fadeState === 'hidden' || connection.fadeProgress <= 0) continue;
      
      const fromNode = this.nodes[connection.from];
      const toNode = this.nodes[connection.to];
      const progress = connection.progress || 1;
      const x1 = fromNode.x;
      const y1 = fromNode.y;
      const x2 = fromNode.x + (toNode.x - fromNode.x) * progress;
      const y2 = fromNode.y + (toNode.y - fromNode.y) * progress;
      
      // Calculate opacity based on fade state
      let opacity = connection.opacity * connection.fadeProgress * 0.7; // Reduzierte Gesamtopazität auf 70%
      
      // Erhöhe kurzzeitig die Opazität bei kürzlich aktivierten Verbindungen
      if (connection.lastActivated && now - connection.lastActivated < 800) {
        const timeFactor = 1 - ((now - connection.lastActivated) / 800);
        opacity = Math.max(opacity, timeFactor * 0.25); // Reduzierte Highlight-Opazität auf 25%
      }
      
      // Berücksichtige fadeoutFactor für sanftes Ausblenden
      opacity *= fadeoutFactor;
      
      const positions = new Float32Array([
        x1, y1,
        x2, y2
      ]);
      
      this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
      this.gl.bufferData(this.gl.ARRAY_BUFFER, positions, this.gl.STATIC_DRAW);
      this.gl.vertexAttribPointer(
        this.programInfo.attribLocations.vertexPosition,
        2,
        this.gl.FLOAT,
        false,
        0,
        0
      );
      this.gl.enableVertexAttribArray(this.programInfo.attribLocations.vertexPosition);
      this.gl.disableVertexAttribArray(this.programInfo.attribLocations.pointSize);
      
      // Set color with calculated opacity
      const colorObj = this.isDarkMode ? this.darkModeColors : this.lightModeColors;
      const connColor = this.hexToRgb(colorObj.connectionColor);
      this.gl.uniform4f(
        this.programInfo.uniformLocations.color,
        connColor.r / 255,
        connColor.g / 255,
        connColor.b / 255,
        opacity
      );
      
      this.gl.enable(this.gl.BLEND);
      this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);
      this.gl.lineWidth(this.config.linesWidth);
      this.gl.drawArrays(this.gl.LINES, 0, 2);
    }
  }
  
  // New method to render the flowing animations with subtilerer Appearance
  renderFlowsWebGL(now, fadeoutFactor = 1.0) {
    // Für jeden Flow einen dezenten, echten Blitz als Zickzack zeichnen und Funken erzeugen
    for (const flow of this.flows) {
      const connection = flow.connection;
      const fromNode = this.nodes[connection.from];
      const toNode = this.nodes[connection.to];
      const startProgress = flow.progress;
      const endProgress = Math.min(1, startProgress + flow.length);
      if (startProgress >= 1 || endProgress <= 0) continue;
      const direction = flow.direction ? 1 : -1;
      let p1, p2;
      if (direction > 0) {
        p1 = {
          x: fromNode.x + (toNode.x - fromNode.x) * startProgress,
          y: fromNode.y + (toNode.y - fromNode.y) * startProgress
        };
        p2 = {
          x: fromNode.x + (toNode.x - fromNode.x) * endProgress,
          y: fromNode.y + (toNode.y - fromNode.y) * endProgress
        };
      } else {
        p1 = {
          x: toNode.x + (fromNode.x - toNode.x) * startProgress,
          y: toNode.y + (fromNode.y - toNode.y) * startProgress
        };
        p2 = {
          x: toNode.x + (fromNode.x - toNode.x) * endProgress,
          y: toNode.y + (fromNode.y - toNode.y) * endProgress
        };
      }
      // Zickzack-Blitz generieren
      const zigzag = this.generateZigZagPoints(p1, p2, 7, 8); // Reduzierte Zickzack-Amplitude
      for (let i = 0; i < zigzag.length - 1; i++) {
        const positions = new Float32Array([
          zigzag[i].x, zigzag[i].y,
          zigzag[i + 1].x, zigzag[i + 1].y
        ]);
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, positions, this.gl.STATIC_DRAW);
        this.gl.vertexAttribPointer(
          this.programInfo.attribLocations.vertexPosition,
          2,
          this.gl.FLOAT,
          false,
          0,
          0
        );
        this.gl.enableVertexAttribArray(this.programInfo.attribLocations.vertexPosition);
        this.gl.disableVertexAttribArray(this.programInfo.attribLocations.pointSize);
        // Dezenter, leuchtender Blitz
        const colorObj = this.isDarkMode ? this.darkModeColors : this.lightModeColors;
        const flowColor = this.hexToRgb(colorObj.flowColor);
        // Definiere fadeFactor als 1.0, falls nicht von flow definiert
        // Berücksichtige auch den fadeoutFactor für das Ausblenden der gesamten Animation
        const fadeFactor = (flow.fadeFactor || 1.0) * fadeoutFactor;
        this.gl.uniform4f(
          this.programInfo.uniformLocations.color,
          flowColor.r / 255,
          flowColor.g / 255,
          flowColor.b / 255,
          0.55 * fadeFactor  // Reduziert von 0.7 auf 0.55 für subtilere Blitze
        );
        this.gl.enable(this.gl.BLEND);
        this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);
        this.gl.lineWidth(1.2); // Schmaler Blitz
        this.gl.drawArrays(this.gl.LINES, 0, 2);
      }
      // Funken erzeugen - subtilere elektrische Funken
      const sparks = this.generateSparkPoints(zigzag, 5 + Math.floor(Math.random() * 3)); // Weniger Funken
      for (const spark of sparks) {
        // Helles Weiß-Blau für elektrische Funken, aber dezenter
        this.gl.uniform4f(
          this.programInfo.uniformLocations.color,
          0.85, 0.92, 0.98, 0.5 * fadeoutFactor // Reduzierte Opazität auf 50%
        );
        
        // Position für den Funken setzen
        const sparkPos = new Float32Array([spark.x, spark.y]);
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, sparkPos, this.gl.STATIC_DRAW);
        
        // Punktgröße setzen - kleinere Funken
        const sizes = new Float32Array([spark.size * 2.5]); // Reduziert von 3.0 auf 2.5
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.sizeBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, sizes, this.gl.STATIC_DRAW);
        
        this.gl.vertexAttribPointer(
          this.programInfo.attribLocations.vertexPosition,
          2,
          this.gl.FLOAT,
          false,
          0,
          0
        );
        this.gl.enableVertexAttribArray(this.programInfo.attribLocations.vertexPosition);
        
        this.gl.vertexAttribPointer(
          this.programInfo.attribLocations.pointSize,
          1,
          this.gl.FLOAT,
          false,
          0,
          0
        );
        this.gl.enableVertexAttribArray(this.programInfo.attribLocations.pointSize);
        
        this.gl.drawArrays(this.gl.POINTS, 0, 1);
      }
    }
  }
  
  renderCanvas(now) {
    // Clear canvas
    const width = this.canvas.width / (window.devicePixelRatio || 1);
    const height = this.canvas.height / (window.devicePixelRatio || 1);
    
    // Fade-Faktor für sanftes Ausblenden im Canvas-Modus
    const fadeoutFactor = this.isDestroying ? parseFloat(this.canvas.style.opacity) || 0.98 : 1.0;
    
    this.ctx.clearRect(0, 0, width, height);
    
    // Set background
    const backgroundColor = this.isDarkMode 
      ? this.darkModeColors.background 
      : this.lightModeColors.background;
      
    this.ctx.fillStyle = backgroundColor;
    this.ctx.fillRect(0, 0, width, height);
    
    // Draw connections with fade effects
    const connectionColor = this.isDarkMode 
      ? this.darkModeColors.connectionColor 
      : this.lightModeColors.connectionColor;
      
    for (const connection of this.connections) {
      // Überspringe Verbindungen, die komplett unsichtbar sind
      if (connection.fadeState === 'hidden' || connection.fadeProgress <= 0) continue;
      
      const fromNode = this.nodes[connection.from];
      const toNode = this.nodes[connection.to];
      
      // Zeichne nur den Teil der Verbindung, der schon aufgebaut wurde
      const progress = connection.progress || 0;
      if (progress <= 0) continue; // Skip if no progress yet
      
      const x1 = fromNode.x;
      const y1 = fromNode.y;
      
      // Endpunkt basiert auf dem aktuellen Fortschritt
      const x2 = x1 + (toNode.x - x1) * progress;
      const y2 = y1 + (toNode.y - y1) * progress;
      
      // Zeichne die unterliegende Linie mit Ein-/Ausblendung
      this.ctx.beginPath();
      this.ctx.moveTo(x1, y1);
      this.ctx.lineTo(x2, y2);
      
      const rgbColor = this.hexToRgb(connectionColor);
      
      // Calculate opacity based on fade state
      let opacity = connection.opacity * connection.fadeProgress;
      
      // Erhöhe kurzzeitig die Opazität bei kürzlich aktivierten Verbindungen
      if (connection.lastActivated && now - connection.lastActivated < 800) {
        const timeFactor = 1 - ((now - connection.lastActivated) / 800);
        opacity = Math.max(opacity, timeFactor * this.config.linesOpacity);
      }
      
      // Berücksichtige fadeoutFactor für sanftes Ausblenden
      opacity *= fadeoutFactor;
      
      this.ctx.strokeStyle = `rgba(${rgbColor.r}, ${rgbColor.g}, ${rgbColor.b}, ${opacity})`;
      this.ctx.lineWidth = this.config.linesWidth;
      
      // Leichter Glow-Effekt für die Linien
      if (opacity > 0.1) {
        this.ctx.shadowColor = `rgba(${rgbColor.r}, ${rgbColor.g}, ${rgbColor.b}, ${0.15 * fadeoutFactor})`;
        this.ctx.shadowBlur = 3;
      } else {
        this.ctx.shadowBlur = 0;
      }
      
      this.ctx.stroke();
      
      // Zeichne einen Fortschrittspunkt am Ende der sich aufbauenden Verbindung
      if (progress < 0.95 && connection.fadeProgress > 0.5) {
        this.ctx.beginPath();
        this.ctx.arc(x2, y2, 1.5, 0, Math.PI * 2);
        this.ctx.fillStyle = `rgba(${rgbColor.r}, ${rgbColor.g}, ${rgbColor.b}, ${opacity * 1.3})`;
        this.ctx.fill();
      }
      
      this.ctx.shadowBlur = 0; // Reset shadow for other elements
    }
    
    // Draw flows with fading effect
    this.renderFlowsCanvas(now, fadeoutFactor);
    
    // Draw nodes with enhanced animations
    const nodeColor = this.isDarkMode 
      ? this.darkModeColors.nodeColor 
      : this.lightModeColors.nodeColor;
      
    const nodePulse = this.isDarkMode 
      ? this.darkModeColors.nodePulse 
      : this.lightModeColors.nodePulse;
      
    for (const node of this.nodes) {
      // Verbesserte Pulsation mit Aktivierungsboost
      const activationBoost = node.isActive ? 1.7 : 1.0;
      const pulse = (Math.sin(node.pulsePhase) * 0.45 + 1.4) * activationBoost;
      
      // Größe basierend auf Konnektivität und Wichtigkeit
      const connectivityFactor = 1 + (node.connections.length / this.config.maxConnections) * 1.4;
      // Während des Ausblendprozesses die Knotengröße leicht reduzieren
      const sizeReduction = this.isDestroying ? 0.85 : 1.0;
      const nodeSize = node.size * pulse * connectivityFactor * sizeReduction;
      
      // Verbesserte Leuchtkraft und Glow-Effekt
      const rgbNodeColor = this.hexToRgb(nodeColor);
      const rgbPulseColor = this.hexToRgb(nodePulse);
      
      // Mische Farben basierend auf Aktivierung
      let r, g, b;
      if (node.isActive) {
        r = (rgbNodeColor.r * 0.3 + rgbPulseColor.r * 0.7);
        g = (rgbNodeColor.g * 0.3 + rgbPulseColor.g * 0.7);
        b = (rgbNodeColor.b * 0.3 + rgbPulseColor.b * 0.7);
      } else {
        r = rgbNodeColor.r;
        g = rgbNodeColor.g;
        b = rgbNodeColor.b;
      }
      
      // Äußerer Glow
      const glow = this.ctx.createRadialGradient(
        node.x, node.y, 0,
        node.x, node.y, nodeSize * 4.5
      );
      
      // Intensiveres Zentrum und weicherer Übergang
      // Berücksichtige fadeoutFactor für sanftes Ausblenden
      const activeOpacity = node.isActive ? 0.95 : 0.8;
      glow.addColorStop(0, `rgba(${r}, ${g}, ${b}, ${activeOpacity * fadeoutFactor})`);
      glow.addColorStop(0.4, `rgba(${r}, ${g}, ${b}, ${0.45 * fadeoutFactor})`);
      glow.addColorStop(1, `rgba(${r}, ${g}, ${b}, 0)`);
      
      this.ctx.beginPath();
      this.ctx.arc(node.x, node.y, nodeSize, 0, Math.PI * 2);
      this.ctx.fillStyle = glow;
      // Globale Transparenz reduzieren beim Ausblenden
      this.ctx.globalAlpha = (node.isActive ? 1.0 : 0.92) * fadeoutFactor;
      this.ctx.fill();
      
      // Innerer Kern für stärkeren Leuchteffekt
      if (node.isActive) {
        this.ctx.beginPath();
        this.ctx.arc(node.x, node.y, nodeSize * 0.4, 0, Math.PI * 2);
        this.ctx.fillStyle = `rgba(${rgbPulseColor.r}, ${rgbPulseColor.g}, ${rgbPulseColor.b}, ${0.9 * fadeoutFactor})`;
        this.ctx.fill();
      }
      
      this.ctx.globalAlpha = 1.0;
    }
  }
  
  // New method to render flows in Canvas mode with fading
  renderFlowsCanvas(now, fadeoutFactor = 1.0) {
    if (!this.flows.length) return;
    
    // Für jeden Flow in der Blitzanimation
    for (const flow of this.flows) {
      const connection = flow.connection;
      const fromNode = this.nodes[connection.from];
      const toNode = this.nodes[connection.to];
      
      // Berechne den Fortschritt der Connection und des Flows
      const connProgress = connection.progress || 1;
      
      // Flussrichtung bestimmen
      const [startNode, endNode] = flow.direction ? [fromNode, toNode] : [toNode, fromNode];
      
      // Berücksichtige den Verbindungs-Fortschritt
      const maxDistance = Math.min(connProgress, 1) * Math.sqrt(
        Math.pow(endNode.x - startNode.x, 2) + 
        Math.pow(endNode.y - startNode.y, 2)
      );
      
      // Berechne den aktuellen Fortschritt mit Ein- und Ausblendung
      const flowAge = now - flow.creationTime;
      const flowLifetime = flow.totalDuration;
      let fadeFactor = 1.0;
      
      // Sanftere Ein- und Ausblendung für Blitzeffekte
      if (flowAge < flowLifetime * 0.3) {
        // Einblenden - sanfter und länger
        fadeFactor = flowAge / (flowLifetime * 0.3);
      } else if (flowAge > flowLifetime * 0.7) {
        // Ausblenden - sanfter und länger
        fadeFactor = 1.0 - ((flowAge - flowLifetime * 0.7) / (flowLifetime * 0.3));
      }
      
      // Beim Ausblenden der gesamten Animation auch den Flow-Faktor anpassen
      fadeFactor *= fadeoutFactor;
      
      // Flow-Fortschritt
      const startProgress = Math.max(0.0, flow.progress - flow.length);
      const endProgress = Math.min(flow.progress, connProgress);
      
      // Start- und Endpunkte basierend auf dem Fortschritt
      const p1 = {
        x: startNode.x + (endNode.x - startNode.x) * startProgress,
        y: startNode.y + (endNode.y - startNode.y) * startProgress
      };
      
      const p2 = {
        x: startNode.x + (endNode.x - startNode.x) * endProgress,
        y: startNode.y + (endNode.y - startNode.y) * endProgress
      };
      
      if (endProgress > connProgress) continue;
      
      // Lila Gradient für den Blitz
      const gradient = this.ctx.createLinearGradient(p1.x, p1.y, p2.x, p2.y);
      gradient.addColorStop(0, 'rgba(255, 0, 255, 0.8)');
      gradient.addColorStop(0.5, 'rgba(200, 0, 255, 0.9)');
      gradient.addColorStop(1, 'rgba(255, 0, 255, 0.8)');
      
      this.ctx.save();
      
      // Untergrundspur mit stärkerem Glühen
      this.ctx.beginPath();
      this.ctx.moveTo(p1.x, p1.y);
      this.ctx.lineTo(p2.x, p2.y);
      this.ctx.strokeStyle = gradient;
      this.ctx.lineWidth = 20.0;
      this.ctx.shadowColor = 'rgba(255, 0, 255, 0.4)';
      this.ctx.shadowBlur = 25;
      this.ctx.stroke();
      
      // Abgerundeter Zickzack-Blitz mit weicheren Kurven
      const zigzag = this.generateZigZagPoints(p1, p2, 4, 6, true);
      
      // Hauptblitz mit Gradient
      this.ctx.strokeStyle = gradient;
      this.ctx.lineWidth = 1.5;
      this.ctx.shadowColor = 'rgba(255, 0, 255, 0.5)';
      this.ctx.shadowBlur = 30;
      this.ctx.beginPath();
      this.ctx.moveTo(zigzag[0].x, zigzag[0].y);
      for (let i = 1; i < zigzag.length; i++) {
        const cp1x = zigzag[i-1].x + (zigzag[i].x - zigzag[i-1].x) * 0.4;
        const cp1y = zigzag[i-1].y + (zigzag[i].y - zigzag[i-1].y) * 0.4;
        const cp2x = zigzag[i].x - (zigzag[i].x - zigzag[i-1].x) * 0.4;
        const cp2y = zigzag[i].y - (zigzag[i].y - zigzag[i-1].y) * 0.4;
        this.ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, zigzag[i].x, zigzag[i].y);
      }
      this.ctx.stroke();
      
      // Funken mit lila Glühen
      const sparks = this.generateSparkPoints(zigzag, 10 + Math.floor(Math.random() * 6));
      
      const sparkGradient = this.ctx.createRadialGradient(0, 0, 0, 0, 0, 10);
      sparkGradient.addColorStop(0, 'rgba(255, 0, 255, 0.95)');
      sparkGradient.addColorStop(0.5, 'rgba(200, 0, 255, 0.8)');
      sparkGradient.addColorStop(1, 'rgba(255, 0, 255, 0.6)');
      
      for (const spark of sparks) {
        this.ctx.beginPath();
        
        // Weichere Sternform
        const points = 4 + Math.floor(Math.random() * 3);
        const outerRadius = spark.size * 2.0;
        const innerRadius = spark.size * 0.5;
        
        for (let i = 0; i < points * 2; i++) {
          const radius = i % 2 === 0 ? outerRadius : innerRadius;
          const angle = (i * Math.PI) / points;
          const x = spark.x + Math.cos(angle) * radius;
          const y = spark.y + Math.sin(angle) * radius;
          
          if (i === 0) {
            this.ctx.moveTo(x, y);
          } else {
            this.ctx.lineTo(x, y);
          }
        }
        
        this.ctx.closePath();
        
        // Intensives lila Glühen
        this.ctx.shadowColor = 'rgba(255, 0, 255, 0.8)';
        this.ctx.shadowBlur = 25;
        this.ctx.fillStyle = sparkGradient;
        this.ctx.fill();
        // Intensiverer innerer Glüheffekt für ausgewählte Funken mit mehrfacher Schichtung
        if (spark.size > 3 && Math.random() > 0.3) {
          // Erste Glühschicht - größer und weicher
          this.ctx.beginPath();
          this.ctx.arc(spark.x, spark.y, spark.size * 0.8, 0, Math.PI * 2);
          this.ctx.fillStyle = this.isDarkMode 
            ? `rgba(245, 252, 255, ${0.85 * fadeFactor})`
            : `rgba(230, 245, 255, ${0.8 * fadeFactor})`;
        this.ctx.shadowColor = this.isDarkMode 
            ? `rgba(200, 225, 255, ${0.7 * fadeFactor})`
            : `rgba(180, 220, 255, ${0.6 * fadeFactor})`;
          this.ctx.shadowBlur = 15;
        this.ctx.fill();
        
          // Zweite Glühschicht - kleiner und intensiver
          this.ctx.beginPath();
          this.ctx.arc(spark.x, spark.y, spark.size * 0.5, 0, Math.PI * 2);
          this.ctx.fillStyle = this.isDarkMode 
            ? `rgba(255, 255, 255, ${0.95 * fadeFactor})`
            : `rgba(240, 250, 255, ${0.9 * fadeFactor})`;
          this.ctx.shadowColor = this.isDarkMode 
            ? `rgba(220, 235, 255, ${0.8 * fadeFactor})`
            : `rgba(200, 230, 255, ${0.7 * fadeFactor})`;
          this.ctx.shadowBlur = 20;
          this.ctx.fill();
          
          // Dritte Glühschicht - noch intensiverer Kern
          this.ctx.beginPath();
          this.ctx.arc(spark.x, spark.y, spark.size * 0.3, 0, Math.PI * 2);
          this.ctx.fillStyle = this.isDarkMode 
            ? `rgba(255, 255, 255, ${0.98 * fadeFactor})`
            : `rgba(245, 252, 255, ${0.95 * fadeFactor})`;
          this.ctx.shadowColor = this.isDarkMode 
            ? `rgba(230, 240, 255, ${0.9 * fadeFactor})`
            : `rgba(210, 235, 255, ${0.8 * fadeFactor})`;
          this.ctx.shadowBlur = 25;
          this.ctx.fill();
          
          // Vierte Glühschicht - pulsierender Effekt
          const pulseSize = spark.size * (0.2 + Math.sin(Date.now() * 0.01) * 0.1);
          this.ctx.beginPath();
          this.ctx.arc(spark.x, spark.y, pulseSize, 0, Math.PI * 2);
          this.ctx.fillStyle = this.isDarkMode 
            ? `rgba(255, 255, 255, ${0.99 * fadeFactor})`
            : `rgba(250, 255, 255, ${0.97 * fadeFactor})`;
          this.ctx.shadowColor = this.isDarkMode 
            ? `rgba(240, 245, 255, ${0.95 * fadeFactor})`
            : `rgba(220, 240, 255, ${0.85 * fadeFactor})`;
          this.ctx.shadowBlur = 30;
          this.ctx.fill();
        }
      }
      
      // Deutlicherer und länger anhaltender Fortschrittseffekt an der Spitze des Blitzes
      if (endProgress >= connProgress - 0.1 && connProgress < 0.98) {
        const tipGlow = this.ctx.createRadialGradient(
          p2.x, p2.y, 0,
          p2.x, p2.y, 10
        );
        tipGlow.addColorStop(0, `rgba(${rgbFlowColor.r}, ${rgbFlowColor.g}, ${rgbFlowColor.b}, ${0.85 * fadeFactor})`);
        tipGlow.addColorStop(0.5, `rgba(${rgbFlowColor.r}, ${rgbFlowColor.g}, ${rgbFlowColor.b}, ${0.4 * fadeFactor})`);
        tipGlow.addColorStop(1, `rgba(${rgbFlowColor.r}, ${rgbFlowColor.g}, ${rgbFlowColor.b}, 0)`);
        
        this.ctx.fillStyle = tipGlow;
        this.ctx.beginPath();
        this.ctx.arc(p2.x, p2.y, 10, 0, Math.PI * 2);
        this.ctx.fill();
      }
      
      this.ctx.restore();
    }
  }
  
  // Helper method to convert hex to RGB
  hexToRgb(hex) {
    // Remove # if present
    hex = hex.replace(/^#/, '');
    
    // Handle rgba hex format
    let alpha = 1;
    if (hex.length === 8) {
      alpha = parseInt(hex.slice(6, 8), 16) / 255;
      hex = hex.slice(0, 6);
    }
    
    // Parse hex values
    const bigint = parseInt(hex, 16);
    const r = (bigint >> 16) & 255;
    const g = (bigint >> 8) & 255;
    const b = bigint & 255;
    
    return { r, g, b, a: alpha };
  }
  
  // Cleanup method mit deutlich sanfterem Ausblenden
  destroy() {
    // Sanfte Ausblendanimation starten
    this.isDestroying = true;
    
    // Canvas-Element sanft ausblenden über einen längeren Zeitraum
    if (this.canvas) {
      this.canvas.style.transition = 'opacity 5s ease-in-out'; // Verlängert von 1.5s auf 5s
      this.canvas.style.opacity = '0';
      
      // Warte auf das Ende der Übergangsanimation, bevor die Ressourcen freigegeben werden
      setTimeout(() => {
        // Animation schrittweise verlangsamen statt sofort stoppen
        const slowDownAnimation = () => {
          // Schrittweise die Animation verlangsamen durch Reduzierung der FPS
    if (this.animationFrameId) {
      cancelAnimationFrame(this.animationFrameId);
            
            // Animiere mit abnehmender Framerate
            setTimeout(() => {
              this.animationFrameId = requestAnimationFrame(this.animate.bind(this));
              
              // Nach 10 weiteren Sekunden komplett beenden
              if (this._destroyStartTime && (Date.now() - this._destroyStartTime > 10000)) {
                finalCleanup();
              } else {
                slowDownAnimation();
              }
            }, 500); // Zunehmende Verzögerung zwischen Frames
          }
        };
        
        // Endgültige Bereinigung nach dem vollständigen Ausblenden
        const finalCleanup = () => {
          // Animation stoppen
          if (this.animationFrameId) {
            cancelAnimationFrame(this.animationFrameId);
            this.animationFrameId = null;
          }
          
          // Event-Listener entfernen
    window.removeEventListener('resize', this.resizeCanvas.bind(this));
    
          // Canvas-Element aus dem DOM entfernen, nachdem es ausgeblendet wurde
    if (this.canvas && this.canvas.parentNode) {
      this.canvas.parentNode.removeChild(this.canvas);
    }
    
          // WebGL-Ressourcen bereinigen
    if (this.gl) {
      this.gl.deleteBuffer(this.positionBuffer);
      this.gl.deleteBuffer(this.sizeBuffer);
      this.gl.deleteProgram(this.shaderProgram);
          }
          
          console.log('Neural Network Background erfolgreich deaktiviert');
        };
        
        // Startzeit für die schrittweise Verlangsamung setzen
        this._destroyStartTime = Date.now();
        
        // Starte die schrittweise Verlangsamung
        slowDownAnimation();
      }, 5500); // Warte länger als die CSS-Transition-Dauer
    }
  }
  
  // Hilfsfunktion: Erzeuge Zickzack-Punkte für einen Blitz mit geringerer Vibration
  generateZigZagPoints(start, end, segments = 5, amplitude = 8) {
    const points = [start];
    const mainAngle = Math.atan2(end.y - start.y, end.x - start.x);
    
    // Berechne die Gesamtlänge des Blitzes
    const totalDistance = Math.sqrt(
      Math.pow(end.x - start.x, 2) + 
      Math.pow(end.y - start.y, 2)
    );
    
    // Geringere Amplitude für subtilere Zickzack-Muster
    const baseAmplitude = totalDistance * 0.12; // Reduziert für sanfteres Erscheinungsbild
    
    for (let i = 1; i < segments; i++) {
      const t = i / segments;
      const x = start.x + (end.x - start.x) * t;
      const y = start.y + (end.y - start.y) * t;
      
      // Geringere Vibration durch kleinere Zufallsvariationen
      const perpendicularAngle = mainAngle + Math.PI/2;
      const variation = (Math.random() * 0.8 - 0.4); // Kleinere Variation für sanftere Muster
      
      // Mal Links, mal Rechts für sanften Blitz
      const directionFactor = (i % 2 === 0) ? 1 : -1;
      const offset = baseAmplitude * (Math.sin(i * Math.PI) + variation) * directionFactor;
      
      points.push({
        x: x + Math.cos(perpendicularAngle) * offset,
        y: y + Math.sin(perpendicularAngle) * offset
      });
    }
    points.push(end);
    return points;
  }
  
  // Hilfsfunktion: Erzeuge intensivere Funkenpunkte mit dynamischer Verteilung
  generateSparkPoints(zigzag, sparkCount = 15) {
    const sparks = [];
    // Mehr Funken für intensiveren Effekt
    const actualSparkCount = Math.min(sparkCount, zigzag.length * 2);
    
    // Funken an zufälligen Stellen entlang des Blitzes
    for (let i = 0; i < actualSparkCount; i++) {
      // Zufälliges Segment des Zickzacks auswählen
      const segIndex = Math.floor(Math.random() * (zigzag.length - 1));
      
      // Bestimme Richtung des Segments
      const dx = zigzag[segIndex + 1].x - zigzag[segIndex].x;
      const dy = zigzag[segIndex + 1].y - zigzag[segIndex].y;
      const segmentAngle = Math.atan2(dy, dx);
      
      // Zufällige Position entlang des Segments
      const t = Math.random();
      const x = zigzag[segIndex].x + dx * t;
      const y = zigzag[segIndex].y + dy * t;
      
      // Dynamischer Versatz für intensivere Funken
      const offsetAngle = segmentAngle + (Math.random() * Math.PI - Math.PI/2);
      const offsetDistance = Math.random() * 8 - 4; // Größerer Offset für dramatischere Funken
      
      // Zufällige Größe für variierende Intensität
      const baseSize = 3.5 + Math.random() * 3.5;
      const sizeVariation = Math.random() * 2.5;
      
      sparks.push({
        x: x + Math.cos(offsetAngle) * offsetDistance,
        y: y + Math.sin(offsetAngle) * offsetDistance,
        size: baseSize + sizeVariation // Größere und variablere Funkengröße
      });
      
      // Zusätzliche kleinere Funken in der Nähe für einen intensiveren Effekt
      if (Math.random() < 0.4) { // 40% Chance für zusätzliche Funken
        const subSparkAngle = offsetAngle + (Math.random() * Math.PI/2 - Math.PI/4);
        const subDistance = offsetDistance * (0.4 + Math.random() * 0.6);
        
        sparks.push({
          x: x + Math.cos(subSparkAngle) * subDistance,
          y: y + Math.sin(subSparkAngle) * subDistance,
          size: (baseSize + sizeVariation) * 0.6 // Kleinere Größe für sekundäre Funken
        });
      }
    }
    
    return sparks;
  }
  
  // New method to render the flowing animations with intensiveren Glow und Lila-Farbverlauf
  renderFlowsWebGL(now, fadeoutFactor = 1.0) {
    // Für jeden Flow einen leuchtenden Lila-Blitz als Schlange zeichnen und Funken erzeugen
    for (const flow of this.flows) {
      const connection = flow.connection;
      const fromNode = this.nodes[connection.from];
      const toNode = this.nodes[connection.to];
      const startProgress = flow.progress;
      const endProgress = Math.min(1, startProgress + flow.length);
      if (startProgress >= 1 || endProgress <= 0) continue;
      const direction = flow.direction ? 1 : -1;
      let p1, p2;
      if (direction > 0) {
        p1 = {
          x: fromNode.x + (toNode.x - fromNode.x) * startProgress,
          y: fromNode.y + (toNode.y - fromNode.y) * startProgress
        };
        p2 = {
          x: fromNode.x + (toNode.x - fromNode.x) * endProgress,
          y: fromNode.y + (toNode.y - fromNode.y) * endProgress
        };
      } else {
        p1 = {
          x: toNode.x + (fromNode.x - toNode.x) * startProgress,
          y: toNode.y + (fromNode.y - toNode.y) * startProgress
        };
        p2 = {
          x: toNode.x + (fromNode.x - toNode.x) * endProgress,
          y: toNode.y + (fromNode.y - toNode.y) * endProgress
        };
      }
      
      // Erzeuge Schlangenbewegung durch intensivere Zickzack-Muster
      const snakeSegments = 12; // Mehr Segmente für flüssigere Schlangenbewegung
      const snakeAmplitude = 12; // Größere Amplitude für deutlichere Schlangenbewegung
      // Einflussfaktor, der von der Generation abhängt - höhere Generationen schlängeln mehr
      const generationFactor = Math.min(1.5, (flow.generation || 1) / 10 + 0.8);
      const zigzag = this.generateZigZagPoints(
        p1, p2, 
        snakeSegments, 
        snakeAmplitude * generationFactor,
        true // Sanftere Kurven aktivieren
      );
      
      // Lila Gradient Effekt für Blitze - Multi-Pass Rendering für intensives Glühen
      
      // 1. Render outer glow - very wide and subtle
      const outerGlowWidth = 6.0 * this.config.glowIntensity;
      for (let i = 0; i < zigzag.length - 1; i++) {
        const positions = new Float32Array([
          zigzag[i].x, zigzag[i].y,
          zigzag[i + 1].x, zigzag[i + 1].y
        ]);
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, positions, this.gl.STATIC_DRAW);
        this.gl.vertexAttribPointer(
          this.programInfo.attribLocations.vertexPosition,
          2,
          this.gl.FLOAT,
          false,
          0,
          0
        );
        this.gl.enableVertexAttribArray(this.programInfo.attribLocations.vertexPosition);
        this.gl.disableVertexAttribArray(this.programInfo.attribLocations.pointSize);
        
        // Äußerer Glow in dunklerem Lila mit niedriger Opazität
        this.gl.uniform4f(
          this.programInfo.uniformLocations.color,
          0.45, 0.2, 0.7, 0.15 * fadeoutFactor // Dunkler lila Außenglow
        );
        this.gl.enable(this.gl.BLEND);
        this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);
        this.gl.lineWidth(outerGlowWidth);
        this.gl.drawArrays(this.gl.LINES, 0, 2);
      }
      
      // 2. Middle glow layer - more opaque and focused
      const middleGlowWidth = 3.5 * this.config.glowIntensity;
      for (let i = 0; i < zigzag.length - 1; i++) {
        const positions = new Float32Array([
          zigzag[i].x, zigzag[i].y,
          zigzag[i + 1].x, zigzag[i + 1].y
        ]);
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, positions, this.gl.STATIC_DRAW);
        this.gl.vertexAttribPointer(
          this.programInfo.attribLocations.vertexPosition,
          2,
          this.gl.FLOAT,
          false,
          0,
          0
        );
        
        // Mittlerer Glow in hellerem Lila mit mittlerer Opazität
        this.gl.uniform4f(
          this.programInfo.uniformLocations.color,
          0.65, 0.3, 0.85, 0.35 * fadeoutFactor // Mittleres Lila
        );
        this.gl.lineWidth(middleGlowWidth);
        this.gl.drawArrays(this.gl.LINES, 0, 2);
      }
      
      // 3. Inner core - bright and vibrant
      for (let i = 0; i < zigzag.length - 1; i++) {
        const positions = new Float32Array([
          zigzag[i].x, zigzag[i].y,
          zigzag[i + 1].x, zigzag[i + 1].y
        ]);
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, positions, this.gl.STATIC_DRAW);
        this.gl.vertexAttribPointer(
          this.programInfo.attribLocations.vertexPosition,
          2,
          this.gl.FLOAT,
          false,
          0,
          0
        );
        
        // Innerer Kern in strahlendem Lila-Weiß
        this.gl.uniform4f(
          this.programInfo.uniformLocations.color,
          0.9, 0.7, 1.0, 0.8 * fadeoutFactor // Weißlich-Lila für intensives Zentrum
        );
        this.gl.lineWidth(1.6); // Schmaler Kern
        this.gl.drawArrays(this.gl.LINES, 0, 2);
      }
      
      // Viele intensivere Funken erzeugen
      const sparkCount = this.config.sparkCount + Math.floor((flow.generation || 1) / 2); // Mehr Funken bei höheren Generationen
      const sparks = this.generateSparkPoints(zigzag, sparkCount);
      
      // Render die Funken mit mehreren Ebenen für intensiveres Glühen
      for (const spark of sparks) {
        // 1. Äußerer Glow der Funken
        this.gl.uniform4f(
          this.programInfo.uniformLocations.color,
          0.5, 0.2, 0.8, 0.3 * fadeoutFactor // Lila Glow mit niedriger Opazität
        );
        
        // Position für den Funken setzen
        const sparkPos = new Float32Array([spark.x, spark.y]);
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, sparkPos, this.gl.STATIC_DRAW);
        
        // Größerer Glow um jeden Funken
        const sizes = new Float32Array([spark.size * this.config.sparkSize * 2.0]);
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.sizeBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, sizes, this.gl.STATIC_DRAW);
        
        this.gl.vertexAttribPointer(
          this.programInfo.attribLocations.vertexPosition,
          2,
          this.gl.FLOAT,
          false,
          0,
          0
        );
        this.gl.enableVertexAttribArray(this.programInfo.attribLocations.vertexPosition);
        
        this.gl.vertexAttribPointer(
          this.programInfo.attribLocations.pointSize,
          1,
          this.gl.FLOAT,
          false,
          0,
          0
        );
        this.gl.enableVertexAttribArray(this.programInfo.attribLocations.pointSize);
        
        this.gl.drawArrays(this.gl.POINTS, 0, 1);
        
        // 2. Mittlere Schicht der Funken
        this.gl.uniform4f(
          this.programInfo.uniformLocations.color,
          0.7, 0.4, 0.95, 0.6 * fadeoutFactor // Mittleres Lila mit mittlerer Opazität
        );
        
        const middleSizes = new Float32Array([spark.size * this.config.sparkSize * 1.2]);
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.sizeBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, middleSizes, this.gl.STATIC_DRAW);
        
        this.gl.drawArrays(this.gl.POINTS, 0, 1);
        
        // 3. Innerer Kern der Funken
        this.gl.uniform4f(
          this.programInfo.uniformLocations.color,
          0.95, 0.85, 1.0, 0.9 * fadeoutFactor // Fast weißes Zentrum für intensives Leuchten
        );
        
        const innerSizes = new Float32Array([spark.size * this.config.sparkSize * 0.6]);
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.sizeBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, innerSizes, this.gl.STATIC_DRAW);
        
        this.gl.drawArrays(this.gl.POINTS, 0, 1);
      }
    }
  }
  
  // Hilfsfunktion: Erzeuge schlangenartige Zickzack-Punkte mit Glätte-Option
  generateZigZagPoints(start, end, segments = 5, amplitude = 8, smooth = false) {
    const points = [start];
    const mainAngle = Math.atan2(end.y - start.y, end.x - start.x);
    
    // Berechne die Gesamtlänge des Blitzes
    const totalDistance = Math.sqrt(
      Math.pow(end.x - start.x, 2) + 
      Math.pow(end.y - start.y, 2)
    );
    
    // Dynamische Amplitude basierend auf Distanz
    const baseAmplitude = amplitude * (totalDistance / 150);
    
    // Welle für schlangenartigen Effekt
    const waveFrequency = 2 + Math.random() * 1.5; // Zusätzlicher Welleneffekt
    
    // Vorherige Punktrichtung für glattere Übergänge
    let prevOffsetX = 0;
    let prevOffsetY = 0;
    
    for (let i = 1; i < segments; i++) {
      const t = i / segments;
      // Kubische Easing-Funktion für natürlichere Bewegung
      const easedT = smooth ? t*t*(3-2*t) : t;
      
      const x = start.x + (end.x - start.x) * easedT;
      const y = start.y + (end.y - start.y) * easedT;
      
      // Schlangenbewegung durch Sinuswelle mit zusätzlicher Zeit- und Positionsabhängigkeit
      const wavePhase = waveFrequency * Math.PI * t + (Date.now() % 1000) / 1000 * Math.PI;
      const waveAmplitude = Math.sin(wavePhase) * baseAmplitude;
      
      // Perpendikuläre Richtung zur Hauptrichtung + etwas Zufall
      const perpendicularAngle = mainAngle + Math.PI/2 + (Math.random() * 0.3 - 0.15);
      
      // Für glattere Übergänge, interpoliere zwischen vorherigem und neuem Offset
      let offsetX, offsetY;
      if (smooth && i > 1) {
        const newOffsetX = Math.cos(perpendicularAngle) * waveAmplitude;
        const newOffsetY = Math.sin(perpendicularAngle) * waveAmplitude;
        
        // Interpoliere zwischen vorherigem und neuem Offset (30% vorheriger, 70% neuer)
        offsetX = prevOffsetX * 0.3 + newOffsetX * 0.7;
        offsetY = prevOffsetY * 0.3 + newOffsetY * 0.7;
        
        // Speichere für nächsten Punkt
        prevOffsetX = offsetX;
        prevOffsetY = offsetY;
      } else {
        offsetX = Math.cos(perpendicularAngle) * waveAmplitude;
        offsetY = Math.sin(perpendicularAngle) * waveAmplitude;
        
        prevOffsetX = offsetX;
        prevOffsetY = offsetY;
      }
      
      points.push({
        x: x + offsetX,
        y: y + offsetY
      });
    }
    
    points.push(end);
    return points;
  }
  
  // Hilfsfunktion: Erzeuge leuchtende Funkenpunkte 
  generateSparkPoints(zigzag, sparkCount = 15) {
    const sparks = [];
    // Mehr Funken für intensiveren Effekt
    const actualSparkCount = Math.min(sparkCount, zigzag.length * 3);
    
    // Funken an zufälligen Stellen entlang des Blitzes mit mehr Variabilität
    for (let i = 0; i < actualSparkCount; i++) {
      // Zufälliges Segment des Zickzacks auswählen, mit Präferenz für Ecken
      let segIndex;
      if (Math.random() < 0.6 && zigzag.length > 3) {
        // 60% der Funken an Ecken platzieren (mehr Funken an Wendepunkten)
        segIndex = 1 + Math.floor(Math.random() * (zigzag.length - 3));
      } else {
        // Rest gleichmäßig verteilen
        segIndex = Math.floor(Math.random() * (zigzag.length - 1));
      }
      
      // Bestimme Richtung des Segments
      const dx = zigzag[segIndex + 1].x - zigzag[segIndex].x;
      const dy = zigzag[segIndex + 1].y - zigzag[segIndex].y;
      const segmentAngle = Math.atan2(dy, dx);
      
      // Zufällige Position entlang des Segments mit Präferenz für die Mitte
      let t;
      if (Math.random() < 0.4) {
        // 40% der Funken näher zur Mitte des Segments
        t = 0.3 + Math.random() * 0.4;
      } else {
        // Rest gleichmäßig verteilen
        t = Math.random();
      }
      
      const x = zigzag[segIndex].x + dx * t;
      const y = zigzag[segIndex].y + dy * t;
      
      // Dynamischer Versatz für intensivere Funken
      // Versatz tendenziell senkrecht zur Segmentrichtung
      const offsetAngle = segmentAngle + (Math.random() * Math.PI - Math.PI/2);
      // Größerer Versatz für Funken, die weiter vom Pfad wegfliegen
      const offsetDistance = (0.8 + Math.random() * 2.0) * 6; 
      
      // Größere und variablere Funken mit Zeit-basiertem Pulsieren
      const baseSize = 0.8 + Math.random() * 1.0;
      // Pulsierender Effekt
      const pulsePhase = (Date.now() % 1000) / 1000 * Math.PI * 2;
      const pulseFactor = 0.8 + Math.sin(pulsePhase) * 0.2;
      
      sparks.push({
        x: x + Math.cos(offsetAngle) * offsetDistance,
        y: y + Math.sin(offsetAngle) * offsetDistance,
        size: baseSize * pulseFactor,
        // Zufälliger Winkel für Funken-Rotation
        angle: Math.random() * Math.PI * 2
      });
    }
    
    return sparks;
  }
}

// Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
  // Short delay to ensure DOM is fully loaded
  setTimeout(() => {
    if (!window.neuralNetworkBackground) {
      console.log('Creating Neural Network Background');
      window.neuralNetworkBackground = new NeuralNetworkBackground();
    }
  }, 100);
});

// Re-initialize when page is fully loaded (for safety)
window.addEventListener('load', () => {
  if (!window.neuralNetworkBackground) {
    console.log('Re-initializing Neural Network Background on full load');
    window.neuralNetworkBackground = new NeuralNetworkBackground();
  }
});

// Event listener to clean up when the window is closed
window.addEventListener('beforeunload', function() {
    if (window.neuralNetworkBackground) {
        window.neuralNetworkBackground.destroy();
    }
});
