diff --git a/website/database/systades.db b/website/database/systades.db index eb737e2..7ceb056 100644 Binary files a/website/database/systades.db and b/website/database/systades.db differ diff --git a/website/static/background.js b/website/static/background.js deleted file mode 100644 index bbab5ef..0000000 --- a/website/static/background.js +++ /dev/null @@ -1,108 +0,0 @@ -// Background animation with Three.js -let scene, camera, renderer, stars = []; - -function initBackground() { - // Setup scene - scene = new THREE.Scene(); - - // Setup camera - camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); - camera.position.z = 100; - - // Setup renderer - renderer = new THREE.WebGLRenderer({ alpha: true }); - renderer.setSize(window.innerWidth, window.innerHeight); - renderer.setClearColor(0x000000, 0); // Transparent background - - // Append renderer to DOM - const backgroundContainer = document.getElementById('background-container'); - if (backgroundContainer) { - backgroundContainer.appendChild(renderer.domElement); - } - - // Add stars - for (let i = 0; i < 1000; i++) { - const geometry = new THREE.SphereGeometry(0.2, 8, 8); - const material = new THREE.MeshBasicMaterial({ color: 0xffffff, transparent: true, opacity: Math.random() * 0.5 + 0.1 }); - const star = new THREE.Mesh(geometry, material); - - // Random position - star.position.x = Math.random() * 600 - 300; - star.position.y = Math.random() * 600 - 300; - star.position.z = Math.random() * 600 - 300; - - // Store reference to move in animation - star.velocity = Math.random() * 0.02 + 0.005; - stars.push(star); - - scene.add(star); - } - - // Add large glowing particles - for (let i = 0; i < 15; i++) { - const size = Math.random() * 5 + 2; - const geometry = new THREE.SphereGeometry(size, 16, 16); - - // Create a glowing material - const color = new THREE.Color(); - color.setHSL(Math.random(), 0.7, 0.5); // Random hue - - const material = new THREE.MeshBasicMaterial({ - color: color, - transparent: true, - opacity: 0.2 - }); - - const particle = new THREE.Mesh(geometry, material); - - // Random position but further away - particle.position.x = Math.random() * 1000 - 500; - particle.position.y = Math.random() * 1000 - 500; - particle.position.z = Math.random() * 200 - 400; - - // Store reference to move in animation - particle.velocity = Math.random() * 0.01 + 0.002; - stars.push(particle); - - scene.add(particle); - } - - // Handle window resize - window.addEventListener('resize', onWindowResize); - - // Start animation - animate(); -} - -function animate() { - requestAnimationFrame(animate); - - // Move stars - stars.forEach(star => { - star.position.z += star.velocity; - - // Reset position if star moves too close - if (star.position.z > 100) { - star.position.z = -300; - } - }); - - // Rotate the entire scene slightly for a dreamy effect - scene.rotation.y += 0.0003; - scene.rotation.x += 0.0001; - - renderer.render(scene, camera); -} - -function onWindowResize() { - camera.aspect = window.innerWidth / window.innerHeight; - camera.updateProjectionMatrix(); - renderer.setSize(window.innerWidth, window.innerHeight); -} - -// Initialize background when the DOM is loaded -if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initBackground); -} else { - initBackground(); -} \ No newline at end of file diff --git a/website/static/background.mp4 b/website/static/background.mp4 deleted file mode 100644 index 050b743..0000000 --- a/website/static/background.mp4 +++ /dev/null @@ -1 +0,0 @@ -C:\Users\firem\Downloads\background.mp4 diff --git a/website/static/css/base-styles.css b/website/static/css/base-styles.css index f7c70e9..e1c593b 100644 --- a/website/static/css/base-styles.css +++ b/website/static/css/base-styles.css @@ -1,426 +1,421 @@ -/* Globale Variablen */ -:root { - --dark-bg: #0e1220; - --dark-card-bg: rgba(24, 28, 45, 0.8); - --dark-element-bg: rgba(24, 28, 45, 0.8); - --light-bg: #f0f4f8; - --light-card-bg: rgba(255, 255, 255, 0.85); - --accent-color: #b38fff; - --accent-gradient: linear-gradient(135deg, #b38fff, #58a9ff); - --accent-gradient-hover: linear-gradient(135deg, #c7a8ff, #70b5ff); - --blur-amount: 20px; - --border-radius: 28px; - --card-border-radius: 24px; - --button-radius: 18px; - --nav-item-radius: 14px; -} +/* Base Styles - Dark, Mystical Theme */ -/* Dark Mode Einstellungen */ -html.dark { - color-scheme: dark; +/* Global Variables */ +:root { + --font-sans: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + --font-mono: 'JetBrains Mono', SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + + /* Light Theme */ + --bg-primary-light: #f8fafc; + --bg-secondary-light: #f1f5f9; + --bg-tertiary-light: #e2e8f0; + --text-primary-light: #1e293b; + --text-secondary-light: #475569; + --accent-primary-light: #7c3aed; + --accent-secondary-light: #8b5cf6; + --accent-tertiary-light: #a78bfa; + --border-light: #e2e8f0; + --shadow-light: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + --glow-light: 0 0 15px rgba(139, 92, 246, 0.3); + + /* Dark Theme */ + --bg-primary-dark: #0a0e19; + --bg-secondary-dark: #111827; + --bg-tertiary-dark: #1f2937; + --text-primary-dark: #f9fafb; + --text-secondary-dark: #e5e7eb; + --accent-primary-dark: #6d28d9; + --accent-secondary-dark: #8b5cf6; + --accent-tertiary-dark: #a78bfa; + --border-dark: #1f2937; + --shadow-dark: 0 4px 6px -1px rgba(0, 0, 0, 0.5), 0 2px 4px -1px rgba(0, 0, 0, 0.3); + --glow-dark: 0 0 15px rgba(124, 58, 237, 0.5); + + /* Transitions */ + --transition-fast: 150ms ease-in-out; + --transition-normal: 300ms ease-in-out; + --transition-slow: 500ms ease-in-out; } /* Base Styles */ -html, body { - background-color: var(--dark-bg) !important; - min-height: 100vh; - width: 100%; - color: #ffffff; - margin: 0; - padding: 0; - font-family: 'Inter', system-ui, -apple-system, sans-serif; +html { + font-family: var(--font-sans); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + scroll-behavior: smooth; +} + +body { + transition: background-color var(--transition-normal), color var(--transition-normal); overflow-x: hidden; - transition: background-color 0.5s ease, color 0.5s ease; + min-height: 100vh; + font-family: var(--font-sans); + line-height: 1.5; } -/* Sicherstellen, dass der dunkle Hintergrund die gesamte Seite abdeckt */ -#app-container, .container, main, .mx-auto, .py-12, #content-wrapper { - background-color: transparent !important; - width: 100%; +/* Dark Mode */ +html.dark body { + background-color: var(--bg-primary-dark); + color: var(--text-primary-dark); } -/* Light Mode Einstellungen */ -html.light, html.light body { - background-color: var(--light-bg) !important; - color: #1a202c; +/* Light Mode */ +body { + background-color: var(--bg-primary-light); + color: var(--text-primary-light); } -/* Große Headings mit verbesserten Stilen */ -h1.hero-heading { - font-size: clamp(2.5rem, 8vw, 5rem); - line-height: 1.1; - font-weight: 800; - letter-spacing: -0.03em; - margin-bottom: 1.5rem; -} - -h2.section-heading { - font-size: clamp(1.75rem, 5vw, 3rem); +/* Typography */ +h1, h2, h3, h4, h5, h6 { + font-weight: 600; line-height: 1.2; +} + +.hero-heading { + font-size: 2.75rem; font-weight: 700; letter-spacing: -0.02em; - margin-bottom: 1.25rem; + line-height: 1.1; } -/* Verbesserte Glasmorphismus-Stile */ -.glass-morphism { - background: var(--dark-card-bg); - backdrop-filter: blur(var(--blur-amount)); - -webkit-backdrop-filter: blur(var(--blur-amount)); - border: 1px solid rgba(255, 255, 255, 0.12); - border-radius: var(--card-border-radius); - box-shadow: 0 12px 36px rgba(0, 0, 0, 0.35); - transition: all 0.3s ease; +.section-heading { + font-size: 2rem; + font-weight: 700; + letter-spacing: -0.01em; } -.glass-morphism:hover { - border: 1px solid rgba(255, 255, 255, 0.2); - box-shadow: 0 16px 48px rgba(0, 0, 0, 0.45); - transform: translateY(-2px); +.gradient-text { + background-clip: text; + -webkit-background-clip: text; + color: transparent; + display: inline-block; } -.glass-morphism-light { - background: var(--light-card-bg); - backdrop-filter: blur(var(--blur-amount)); - -webkit-backdrop-filter: blur(var(--blur-amount)); - border: 1px solid rgba(0, 0, 0, 0.08); - border-radius: var(--card-border-radius); - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12); - transition: all 0.3s ease; +html.dark .gradient-text { + background-image: linear-gradient(135deg, var(--accent-primary-dark), var(--accent-secondary-dark)); + text-shadow: var(--glow-dark); } -.glass-morphism-light:hover { - border: 1px solid rgba(0, 0, 0, 0.15); - box-shadow: 0 16px 40px rgba(0, 0, 0, 0.18); - transform: translateY(-2px); +.gradient-text { + background-image: linear-gradient(135deg, var(--accent-primary-light), var(--accent-secondary-light)); + text-shadow: var(--glow-light); } -/* Verbesserte Navbar-Styles */ -.glass-navbar-dark { - background: rgba(14, 18, 32, 0.85); - backdrop-filter: blur(15px); - -webkit-backdrop-filter: blur(15px); - border-color: rgba(255, 255, 255, 0.1); - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25); - border-radius: 0 0 20px 20px; -} - -.glass-navbar-light { - background: rgba(255, 255, 255, 0.85); - backdrop-filter: blur(15px); - -webkit-backdrop-filter: blur(15px); - border-color: rgba(0, 0, 0, 0.05); - box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08); - border-radius: 0 0 20px 20px; -} - -/* Verbesserte Button-Stile mit besserer Lesbarkeit und stärkeren Farbverläufen */ -.btn, button, .button, [type="button"], [type="submit"] { - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - border-radius: var(--button-radius); - padding: 0.75rem 1.5rem; - font-weight: 600; - letter-spacing: 0.4px; - color: rgba(255, 255, 255, 1); - background: linear-gradient(135deg, rgba(99, 102, 241, 0.8), rgba(168, 85, 247, 0.8)); - border: 1px solid rgba(255, 255, 255, 0.15); - box-shadow: 0 6px 14px rgba(0, 0, 0, 0.2); - text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); - backdrop-filter: blur(15px); - -webkit-backdrop-filter: blur(15px); +/* Mystical elements */ +.mystical-border { position: relative; - overflow: hidden; - outline: none; } -.btn:hover, button:hover, .button:hover, [type="button"]:hover, [type="submit"]:hover { - background: linear-gradient(135deg, rgba(129, 140, 248, 0.9), rgba(192, 132, 252, 0.9)); - transform: translateY(-3px); - border: 1px solid rgba(255, 255, 255, 0.3); - box-shadow: 0 8px 22px rgba(0, 0, 0, 0.25), 0 0 12px rgba(179, 143, 255, 0.35); - color: white; +.mystical-border::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border: 1px solid; + border-radius: inherit; + pointer-events: none; + opacity: 0.3; } -.btn:active, button:active, .button:active, [type="button"]:active, [type="submit"]:active { - transform: translateY(1px); - box-shadow: 0 3px 8px rgba(0, 0, 0, 0.2); +html.dark .mystical-border::before { + border-color: var(--accent-primary-dark); + box-shadow: var(--glow-dark); } -/* Navigation Stile mit verbesserten Farbverläufen */ +.mystical-border::before { + border-color: var(--accent-primary-light); + box-shadow: var(--glow-light); +} + +/* Navigation Links */ .nav-link { - transition: all 0.25s ease; - border-radius: var(--nav-item-radius); - padding: 0.625rem 1rem; - font-weight: 500; - letter-spacing: 0.3px; - color: rgba(255, 255, 255, 0.9); position: relative; - overflow: visible; + padding: 0.5rem 0.75rem; + border-radius: 0.5rem; + transition: var(--transition-normal); +} + +html.dark .nav-link { + color: var(--text-secondary-dark); +} + +.nav-link { + color: var(--text-secondary-light); +} + +html.dark .nav-link:hover { + color: var(--text-primary-dark); + background-color: rgba(31, 41, 55, 0.5); } .nav-link:hover { - background: rgba(179, 143, 255, 0.2); - color: white; - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + color: var(--text-primary-light); + background-color: rgba(241, 245, 249, 0.5); } -.nav-link-active { - background: linear-gradient(135deg, rgba(124, 58, 237, 0.3), rgba(139, 92, 246, 0.3)); - color: white; - font-weight: 600; - box-shadow: 0 6px 16px rgba(0, 0, 0, 0.18); -} - -.nav-link-active::after { - content: ''; - position: absolute; - bottom: 0; - left: 10%; - width: 80%; - height: 2px; - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.7), transparent); -} - -/* Light-Mode Navigation Stile */ -.nav-link-light { - color: rgba(26, 32, 44, 0.85); -} - -.nav-link-light:hover { - background: rgba(179, 143, 255, 0.15); - color: rgba(26, 32, 44, 1); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); +html.dark .nav-link-active { + color: var(--accent-tertiary-dark); + background-color: rgba(109, 40, 217, 0.15); } .nav-link-light-active { - background: linear-gradient(135deg, rgba(124, 58, 237, 0.2), rgba(139, 92, 246, 0.2)); - color: rgba(26, 32, 44, 1); - font-weight: 600; - box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1); + color: var(--accent-primary-light); + background-color: rgba(139, 92, 246, 0.1); } -.nav-link-light-active::after { - background: linear-gradient(90deg, transparent, rgba(26, 32, 44, 0.5), transparent); +/* Glass Morphism Effects */ +.glass-navbar-dark { + background-color: rgba(10, 14, 25, 0.8); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border-color: rgba(255, 255, 255, 0.1); + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3); } -/* Entfernung von Gradient-Hintergrund überall */ -.gradient-bg, .purple-gradient, .gradient-purple-bg { - background: var(--dark-bg) !important; - background-image: none !important; +.glass-navbar-light { + background-color: rgba(255, 255, 255, 0.8); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border-color: rgba(226, 232, 240, 0.5); + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); } -/* Verbesserte Light-Mode-Stile für Buttons */ -html.light .btn, html.light button, html.light .button, -html.light [type="button"], html.light [type="submit"] { - background: linear-gradient(135deg, rgba(124, 58, 237, 0.7), rgba(139, 92, 246, 0.7)); - color: #ffffff; - border: 1px solid rgba(0, 0, 0, 0.1); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); - text-shadow: none; +.glass-morphism { + transition: background-color var(--transition-normal), backdrop-filter var(--transition-normal); } -html.light .btn:hover, html.light button:hover, html.light .button:hover, -html.light [type="button"]:hover, html.light [type="submit"]:hover { - background: linear-gradient(135deg, rgba(139, 92, 246, 0.85), rgba(168, 85, 247, 0.85)); - color: #ffffff; - border: 1px solid rgba(0, 0, 0, 0.08); - box-shadow: 0 8px 22px rgba(0, 0, 0, 0.12), 0 0 12px rgba(179, 143, 255, 0.2); - text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); +/* Cards */ +.mystical-card { + border-radius: 0.75rem; + overflow: hidden; + transition: var(--transition-normal); } -/* Verbesserte Buttons mit Glasmorphismus */ -.btn-primary { - background: linear-gradient(135deg, rgba(179, 143, 255, 0.8), rgba(88, 169, 255, 0.8)); - backdrop-filter: blur(var(--blur-amount)); - -webkit-backdrop-filter: blur(var(--blur-amount)); - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: var(--button-radius); - color: white !important; - font-weight: 600; - padding: 0.75rem 1.5rem; - transition: all 0.3s ease; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25); +html.dark .mystical-card { + background-color: var(--bg-secondary-dark); + border: 1px solid var(--border-dark); + box-shadow: var(--shadow-dark); } -.btn-primary:hover { - transform: translateY(-3px); - box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35); - background: linear-gradient(135deg, rgba(190, 160, 255, 0.9), rgba(100, 180, 255, 0.9)); +.mystical-card { + background-color: var(--bg-secondary-light); + border: 1px solid var(--border-light); + box-shadow: var(--shadow-light); } -.btn-secondary { - background: rgba(32, 36, 55, 0.8); - backdrop-filter: blur(var(--blur-amount)); - -webkit-backdrop-filter: blur(var(--blur-amount)); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: var(--button-radius); - color: white; +html.dark .mystical-card:hover { + box-shadow: var(--glow-dark), var(--shadow-dark); + border-color: var(--accent-primary-dark); +} + +.mystical-card:hover { + box-shadow: var(--glow-light), var(--shadow-light); + border-color: var(--accent-primary-light); +} + +/* Buttons */ +.mystical-button { + padding: 0.625rem 1.25rem; + border-radius: 0.5rem; font-weight: 500; - padding: 0.75rem 1.5rem; - transition: all 0.3s ease; - box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2); + transition: var(--transition-normal); + position: relative; + overflow: hidden; } -.btn-secondary:hover { - transform: translateY(-3px); - box-shadow: 0 10px 28px rgba(0, 0, 0, 0.3); - background: rgba(38, 42, 65, 0.9); - border: 1px solid rgba(255, 255, 255, 0.15); -} - -/* Steuerungsbutton-Stil */ -.control-btn { - padding: 0.5rem 1rem; - background: rgba(32, 36, 55, 0.8); - color: rgba(255, 255, 255, 0.9); - border: 1px solid rgba(255, 255, 255, 0.12); - border-radius: 14px; - font-size: 0.875rem; - font-weight: 500; - transition: all 0.25s ease; - backdrop-filter: blur(15px); - -webkit-backdrop-filter: blur(15px); -} - -.control-btn:hover { - background: rgba(38, 42, 65, 0.9); - border: 1px solid rgba(255, 255, 255, 0.2); - transform: translateY(-2px); - box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25); -} - -/* Verbesserter Farbverlauf-Text */ -.gradient-text { - background: linear-gradient(135deg, rgba(200, 170, 255, 1), rgba(100, 180, 255, 1)); - -webkit-background-clip: text; - background-clip: text; - color: transparent; - font-weight: 700; - letter-spacing: -0.02em; - text-shadow: 0 2px 10px rgba(179, 143, 255, 0.3); - filter: drop-shadow(0 2px 6px rgba(179, 143, 255, 0.3)); -} - -/* Globaler Hintergrund */ -.full-page-bg { - position: fixed; +.mystical-button::before { + content: ''; + position: absolute; top: 0; left: 0; - width: 100vw; - height: 100vh; - background-color: var(--dark-bg); - z-index: -10; + width: 100%; + height: 100%; + background: linear-gradient(135deg, transparent, rgba(255, 255, 255, 0.05), transparent); + transform: translateX(-100%); + transition: transform 0.8s ease-in-out; } -html.light .full-page-bg { - background-color: var(--light-bg); +.mystical-button:hover::before { + transform: translateX(100%); } -/* Animationen für Hintergrundeffekte */ -@keyframes float { +html.dark .mystical-button-primary { + background-color: var(--accent-primary-dark); + color: white; +} + +.mystical-button-primary { + background-color: var(--accent-primary-light); + color: white; +} + +html.dark .mystical-button-primary:hover { + background-color: var(--accent-secondary-dark); + box-shadow: var(--glow-dark); +} + +.mystical-button-primary:hover { + background-color: var(--accent-secondary-light); + box-shadow: var(--glow-light); +} + +html.dark .mystical-button-secondary { + background-color: var(--bg-tertiary-dark); + color: var(--text-primary-dark); + border: 1px solid var(--border-dark); +} + +.mystical-button-secondary { + background-color: var(--bg-tertiary-light); + color: var(--text-primary-light); + border: 1px solid var(--border-light); +} + +html.dark .mystical-button-secondary:hover { + background-color: var(--bg-secondary-dark); + border-color: var(--accent-tertiary-dark); +} + +.mystical-button-secondary:hover { + background-color: var(--bg-secondary-light); + border-color: var(--accent-tertiary-light); +} + +/* Inputs */ +.mystical-input { + padding: 0.5rem 0.75rem; + border-radius: 0.5rem; + transition: var(--transition-normal); + width: 100%; + outline: none; +} + +html.dark .mystical-input { + background-color: var(--bg-tertiary-dark); + border: 1px solid var(--border-dark); + color: var(--text-primary-dark); +} + +.mystical-input { + background-color: var(--bg-tertiary-light); + border: 1px solid var(--border-light); + color: var(--text-primary-light); +} + +html.dark .mystical-input:focus { + border-color: var(--accent-tertiary-dark); + box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.25); +} + +.mystical-input:focus { + border-color: var(--accent-tertiary-light); + box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.15); +} + +/* Animations */ +@keyframes floatAnimation { 0% { transform: translateY(0); } - 50% { transform: translateY(-12px); } + 50% { transform: translateY(-5px); } 100% { transform: translateY(0); } } -@keyframes pulse { - 0% { opacity: 0.7; transform: scale(1); } - 50% { opacity: 1; transform: scale(1.05); } - 100% { opacity: 0.7; transform: scale(1); } -} - .animate-float { - animation: float 6s ease-in-out infinite; + animation: floatAnimation 3s ease-in-out infinite; } -.animate-pulse { - animation: pulse 3s ease-in-out infinite; +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } } -/* Verbesserter Container für konsistente Layouts */ -.page-container { - max-width: 1200px; - margin: 0 auto; - padding: 2rem 1rem; +.animate-fade-in { + animation: fadeIn 0.5s ease-out forwards; } -/* Dark Mode Toggle Stile */ -.dot { - transform: translateX(0); - transition: transform 0.3s ease-in-out, background-color 0.3s ease; +/* Scroll Bars */ +::-webkit-scrollbar { + width: 0.5rem; + height: 0.5rem; } -input:checked ~ .dot { - transform: translateX(100%); - background-color: #58a9ff; +html.dark ::-webkit-scrollbar-track { + background: var(--bg-secondary-dark); } -input:checked ~ .block { - background-color: rgba(88, 169, 255, 0.4); +::-webkit-scrollbar-track { + background: var(--bg-secondary-light); } -/* Feature Cards mit Glasmorphismus und Farbverlauf */ -.feature-card { - border-radius: var(--card-border-radius); - padding: 2rem; - transition: all 0.3s ease; - background: linear-gradient(145deg, rgba(32, 36, 55, 0.7), rgba(24, 28, 45, 0.9)); - backdrop-filter: blur(var(--blur-amount)); - -webkit-backdrop-filter: blur(var(--blur-amount)); - border: 1px solid rgba(255, 255, 255, 0.1); - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); +html.dark ::-webkit-scrollbar-thumb { + background: var(--accent-primary-dark); + border-radius: 0.25rem; } -.feature-card:hover { - transform: translateY(-5px); - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3); - border: 1px solid rgba(255, 255, 255, 0.15); - background: linear-gradient(145deg, rgba(40, 44, 65, 0.8), rgba(28, 32, 50, 0.95)); +::-webkit-scrollbar-thumb { + background: var(--accent-primary-light); + border-radius: 0.25rem; } -html.light .feature-card { - background: linear-gradient(145deg, rgba(255, 255, 255, 0.8), rgba(240, 240, 250, 0.9)); - border: 1px solid rgba(0, 0, 0, 0.05); - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08); +html.dark ::-webkit-scrollbar-thumb:hover { + background: var(--accent-secondary-dark); } -html.light .feature-card:hover { - background: linear-gradient(145deg, rgba(255, 255, 255, 0.9), rgba(245, 245, 255, 0.95)); - border: 1px solid rgba(0, 0, 0, 0.08); - box-shadow: 0 12px 32px rgba(0, 0, 0, 0.12); +::-webkit-scrollbar-thumb:hover { + background: var(--accent-secondary-light); } -.feature-card .icon { - width: 60px; - height: 60px; - border-radius: 18px; - display: flex; - align-items: center; - justify-content: center; - font-size: 1.5rem; - margin-bottom: 1.25rem; - background: linear-gradient(135deg, rgba(124, 58, 237, 0.8), rgba(139, 92, 246, 0.6)); - color: white; - box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15); +/* Responsive Utilities */ +@media (max-width: 640px) { + .hero-heading { + font-size: 2rem; + } + + .section-heading { + font-size: 1.5rem; + } } -.feature-card h3 { - font-size: 1.25rem; - font-weight: 600; - margin-bottom: 0.75rem; - color: rgba(255, 255, 255, 0.95); +/* Additional background elements */ +.mystical-dot { + position: absolute; + border-radius: 50%; + opacity: 0.15; + filter: blur(3px); + z-index: -1; + transition: opacity var(--transition-normal); } -html.light .feature-card h3 { - color: rgba(26, 32, 44, 0.95); +html.dark .mystical-dot { + background-color: var(--accent-primary-dark); + box-shadow: 0 0 15px var(--accent-primary-dark); } -.feature-card p { - color: rgba(255, 255, 255, 0.75); - line-height: 1.6; +.mystical-dot { + background-color: var(--accent-primary-light); + box-shadow: 0 0 15px var(--accent-primary-light); } -html.light .feature-card p { - color: rgba(26, 32, 44, 0.75); +/* Accessibility */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +/* Focus styles for keyboard navigation */ +:focus-visible { + outline: 2px solid var(--accent-primary-light); + outline-offset: 2px; +} + +html.dark :focus-visible { + outline-color: var(--accent-primary-dark); } \ No newline at end of file diff --git a/website/static/network-animation.js b/website/static/network-animation.js deleted file mode 100644 index aab0172..0000000 --- a/website/static/network-animation.js +++ /dev/null @@ -1,88 +0,0 @@ -// Network Animation Effect -document.addEventListener('DOMContentLoaded', function() { - // Check if we're on the mindmap page - const mindmapContainer = document.getElementById('mindmap-container'); - if (!mindmapContainer) return; - - // Add enhanced animations for links and nodes - setTimeout(function() { - // Get all SVG links (connections between nodes) - const links = document.querySelectorAll('.link'); - const nodes = document.querySelectorAll('.node'); - - // Add animation to links - links.forEach(link => { - // Create random animation duration between 15 and 30 seconds - const duration = 15 + Math.random() * 15; - link.style.animation = `dash ${duration}s linear infinite`; - link.style.strokeDasharray = '5, 5'; - - // Add pulse effect on hover - link.addEventListener('mouseover', function() { - this.classList.add('highlighted'); - this.style.animation = 'dash 5s linear infinite'; - }); - - link.addEventListener('mouseout', function() { - this.classList.remove('highlighted'); - this.style.animation = `dash ${duration}s linear infinite`; - }); - }); - - // Add effects to nodes - nodes.forEach(node => { - node.addEventListener('mouseover', function() { - this.querySelector('circle').style.filter = 'drop-shadow(0 0 15px rgba(179, 143, 255, 0.8))'; - - // Highlight connected links - const nodeId = this.getAttribute('data-id') || this.id; - links.forEach(link => { - const source = link.getAttribute('data-source'); - const target = link.getAttribute('data-target'); - - if (source === nodeId || target === nodeId) { - link.classList.add('highlighted'); - link.style.animation = 'dash 5s linear infinite'; - } - }); - }); - - node.addEventListener('mouseout', function() { - this.querySelector('circle').style.filter = 'drop-shadow(0 0 8px rgba(179, 143, 255, 0.5))'; - - // Remove highlight from connected links - const nodeId = this.getAttribute('data-id') || this.id; - links.forEach(link => { - const source = link.getAttribute('data-source'); - const target = link.getAttribute('data-target'); - - if (source === nodeId || target === nodeId) { - link.classList.remove('highlighted'); - const duration = 15 + Math.random() * 15; - link.style.animation = `dash ${duration}s linear infinite`; - } - }); - }); - }); - }, 1000); // Wait for the mindmap to be fully loaded - - // Add network background effect - const networkBackground = document.createElement('div'); - networkBackground.className = 'network-background'; - networkBackground.style.cssText = ` - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(179, 143, 255, 0.05); - background-size: cover; - background-position: center; - opacity: 0.15; - z-index: -1; - pointer-events: none; - animation: pulse 10s ease-in-out infinite alternate; - `; - - mindmapContainer.appendChild(networkBackground); -}); \ No newline at end of file diff --git a/website/static/network-background.js b/website/static/network-background.js deleted file mode 100644 index 5ab35e2..0000000 --- a/website/static/network-background.js +++ /dev/null @@ -1,232 +0,0 @@ -// Animated Network Background -let canvas, ctx, networkImage; -let isImageLoaded = false; -let animationSpeed = 0.0003; // Reduzierte Geschwindigkeit für sanftere Rotation -let scaleSpeed = 0.0001; // Reduzierte Geschwindigkeit für sanftere Skalierung -let opacitySpeed = 0.0002; // Reduzierte Geschwindigkeit für sanftere Opazitätsänderung -let rotation = 0; -let scale = 1; -let opacity = 0.7; // Höhere Basisopazität für bessere Sichtbarkeit -let scaleDirection = 1; -let opacityDirection = 1; -let animationFrameId = null; -let isDarkMode = document.documentElement.classList.contains('dark'); -let loadAttempts = 0; -const MAX_LOAD_ATTEMPTS = 2; - -// Initialize the canvas and load the image -function initNetworkBackground() { - // Create canvas element if it doesn't exist - if (!document.getElementById('network-background')) { - canvas = document.createElement('canvas'); - canvas.id = 'network-background'; - canvas.style.position = 'fixed'; - canvas.style.top = '0'; - canvas.style.left = '0'; - canvas.style.width = '100%'; - canvas.style.height = '100%'; - canvas.style.zIndex = '-5'; // Höher als -10 für den full-page-bg - canvas.style.pointerEvents = 'none'; // Stellt sicher, dass der Canvas keine Mausinteraktionen blockiert - document.body.appendChild(canvas); - } else { - canvas = document.getElementById('network-background'); - } - - // Set canvas size to window size with pixel ratio consideration - resizeCanvas(); - - // Get context with alpha enabled - ctx = canvas.getContext('2d', { alpha: true }); - - // Load the network image - versuche zuerst die SVG-Version - networkImage = new Image(); - networkImage.crossOrigin = "anonymous"; // Vermeidet CORS-Probleme - - // Keine Bilder laden, direkt Fallback-Hintergrund verwenden - console.log("Verwende einfachen Hintergrund ohne Bilddateien"); - isImageLoaded = true; // Animation ohne Hintergrundbild starten - startAnimation(); - - // Handle window resize - window.addEventListener('resize', debounce(resizeCanvas, 250)); - - // Überwache Dark Mode-Änderungen - document.addEventListener('darkModeToggled', function(event) { - isDarkMode = event.detail.isDark; - }); -} - -// Hilfsfunktion zur Reduzierung der Resize-Event-Aufrufe -function debounce(func, wait) { - let timeout; - return function() { - const context = this; - const args = arguments; - clearTimeout(timeout); - timeout = setTimeout(function() { - func.apply(context, args); - }, wait); - }; -} - -// Resize canvas to match window size with proper pixel ratio -function resizeCanvas() { - if (!canvas) return; - - const pixelRatio = window.devicePixelRatio || 1; - const width = window.innerWidth; - const height = window.innerHeight; - - // Set display size (css pixels) - canvas.style.width = width + 'px'; - canvas.style.height = height + 'px'; - - // Set actual size in memory (scaled for pixel ratio) - canvas.width = width * pixelRatio; - canvas.height = height * pixelRatio; - - // Scale context to match pixel ratio - if (ctx) { - ctx.scale(pixelRatio, pixelRatio); - } - - // Wenn Animation läuft und Bild geladen, zeichne erneut - if (isImageLoaded && animationFrameId) { - drawNetworkImage(); - } -} - -// Start animation -function startAnimation() { - if (animationFrameId) { - cancelAnimationFrame(animationFrameId); - } - - // Start animation loop - animate(); -} - -// Draw network image -function drawNetworkImage() { - if (!ctx) return; - - // Clear canvas with proper clear method - ctx.clearRect(0, 0, canvas.width / (window.devicePixelRatio || 1), canvas.height / (window.devicePixelRatio || 1)); - - // Save context state - ctx.save(); - - // Move to center of canvas - ctx.translate(canvas.width / (2 * (window.devicePixelRatio || 1)), canvas.height / (2 * (window.devicePixelRatio || 1))); - - // Rotate - ctx.rotate(rotation); - - // Scale - ctx.scale(scale, scale); - - // Set global opacity, angepasst für Dark Mode - ctx.globalAlpha = isDarkMode ? opacity : opacity * 0.8; - - if (isImageLoaded && networkImage.complete) { - // Bildgröße berechnen, um den Bildschirm abzudecken - const imgAspect = networkImage.width / networkImage.height; - const canvasAspect = canvas.width / canvas.height; - - let drawWidth, drawHeight; - - if (canvasAspect > imgAspect) { - drawWidth = canvas.width / (window.devicePixelRatio || 1); - drawHeight = drawWidth / imgAspect; - } else { - drawHeight = canvas.height / (window.devicePixelRatio || 1); - drawWidth = drawHeight * imgAspect; - } - - // Draw image centered - ctx.drawImage( - networkImage, - -drawWidth / 2, - -drawHeight / 2, - drawWidth, - drawHeight - ); - } else { - // Fallback: Zeichne einen einfachen Hintergrund mit Punkten - drawFallbackBackground(); - } - - // Restore context state - ctx.restore(); -} - -// Fallback-Hintergrund mit Punkten und Linien -function drawFallbackBackground() { - const width = canvas.width / (window.devicePixelRatio || 1); - const height = canvas.height / (window.devicePixelRatio || 1); - - // Zeichne einige zufällige Punkte - ctx.fillStyle = isDarkMode ? 'rgba(139, 92, 246, 0.2)' : 'rgba(139, 92, 246, 0.1)'; - - for (let i = 0; i < 50; i++) { - const x = Math.random() * width; - const y = Math.random() * height; - const radius = Math.random() * 3 + 1; - - ctx.beginPath(); - ctx.arc(x - width/2, y - height/2, radius, 0, Math.PI * 2); - ctx.fill(); - } -} - -// Animation loop -function animate() { - // Update animation parameters - rotation += animationSpeed; - - // Update scale with oscillation - scale += scaleSpeed * scaleDirection; - if (scale > 1.05) { // Kleinerer Skalierungsbereich für weniger starke Größenänderung - scaleDirection = -1; - } else if (scale < 0.95) { - scaleDirection = 1; - } - - // Update opacity with oscillation - opacity += opacitySpeed * opacityDirection; - if (opacity > 0.75) { // Kleinerer Opazitätsbereich für subtilere Änderungen - opacityDirection = -1; - } else if (opacity < 0.65) { - opacityDirection = 1; - } - - // Draw the image - drawNetworkImage(); - - // Request next frame - animationFrameId = requestAnimationFrame(animate); -} - -// Cleanup Funktion für Speicherbereinigung -function cleanupNetworkBackground() { - if (animationFrameId) { - cancelAnimationFrame(animationFrameId); - animationFrameId = null; - } - - if (canvas && canvas.parentNode) { - canvas.parentNode.removeChild(canvas); - } - - window.removeEventListener('resize', resizeCanvas); -} - -// Führe Initialisierung aus, wenn DOM geladen ist -if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initNetworkBackground); -} else { - initNetworkBackground(); -} - -// Führe Cleanup durch, wenn das Fenster geschlossen wird -window.addEventListener('beforeunload', cleanupNetworkBackground); \ No newline at end of file diff --git a/website/static/neural-network-background.js b/website/static/neural-network-background.js new file mode 100644 index 0000000..a8abd2e --- /dev/null +++ b/website/static/neural-network-background.js @@ -0,0 +1,566 @@ +/** + * Neural Network Background Animation + * Modern, darker, mystical theme using WebGL + */ + +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 = '-5'; + this.canvas.style.pointerEvents = 'none'; + + // Append to body + 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.animationFrameId = null; + this.isDarkMode = document.documentElement.classList.contains('dark'); + + // Colors - using hex values for better control + this.darkModeColors = { + background: '#0a0e19', + nodeColor: '#6d28d9', + nodePulse: '#8b5cf6', + connectionColor: '#4c1d95', + glowColor: '#7c3aed' + }; + + this.lightModeColors = { + background: '#f9fafb', + nodeColor: '#7c3aed', + nodePulse: '#8b5cf6', + connectionColor: '#a78bfa', + glowColor: '#c4b5fd' + }; + + // Config + this.config = { + nodeCount: 100, + nodeSize: 2, + nodeVariation: 1.5, + connectionDistance: 150, + connectionOpacity: 0.2, + animationSpeed: 0.3, + pulseSpeed: 0.02 + }; + + // Initialize + this.init(); + + // Event listeners + window.addEventListener('resize', this.resizeCanvas.bind(this)); + document.addEventListener('darkModeToggled', (event) => { + this.isDarkMode = event.detail.isDark; + }); + } + + 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 + const fsSource = ` + precision mediump float; + uniform vec4 uColor; + + void main() { + float distance = length(gl_PointCoord - vec2(0.5, 0.5)); + + // Soft circle with glow + float alpha = 1.0 - smoothstep(0.3, 0.5, distance); + + // Add glow + if (distance > 0.3) { + alpha *= 0.7; + } + + 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.isDarkMode + ? this.hexToRgb(this.darkModeColors.background) + : this.hexToRgb(this.lightModeColors.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); + + // Create nodes with random positions and properties + for (let i = 0; i < this.config.nodeCount; i++) { + const node = { + x: Math.random() * width, + y: Math.random() * height, + size: this.config.nodeSize + Math.random() * this.config.nodeVariation, + 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: [] + }; + + this.nodes.push(node); + } + } + + createConnections() { + this.connections = []; + + // Create connections between nearby nodes + for (let i = 0; i < this.nodes.length; i++) { + const nodeA = this.nodes[i]; + nodeA.connections = []; + + for (let j = i + 1; j < this.nodes.length; j++) { + 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); + + if (distance < this.config.connectionDistance) { + // Create connection + const connection = { + from: i, + to: j, + distance: distance, + opacity: Math.max(0, 1 - distance / this.config.connectionDistance) * this.config.connectionOpacity + }; + + this.connections.push(connection); + 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); + + // Update node positions + for (let i = 0; i < this.nodes.length; i++) { + const node = this.nodes[i]; + + // Move node + node.x += node.speed.x; + node.y += node.speed.y; + + // Boundary check with bounce + if (node.x < 0 || node.x > width) { + node.speed.x *= -1; + node.x = Math.max(0, Math.min(node.x, width)); + } + + if (node.y < 0 || node.y > height) { + node.speed.y *= -1; + node.y = Math.max(0, Math.min(node.y, height)); + } + + // Update pulse phase + node.pulsePhase += this.config.pulseSpeed; + if (node.pulsePhase > Math.PI * 2) { + node.pulsePhase -= Math.PI * 2; + } + } + + // Recalculate connections dynamically + if (Math.random() < 0.05) { // Only recalculate 5% of the time for performance + this.createConnections(); + } + + // Render + if (this.useWebGL) { + this.renderWebGL(); + } else { + this.renderCanvas(); + } + + // Continue animation + this.animationFrameId = requestAnimationFrame(this.animate.bind(this)); + } + + renderWebGL() { + this.gl.clear(this.gl.COLOR_BUFFER_BIT); + + const width = this.canvas.width / (window.devicePixelRatio || 1); + const height = this.canvas.height / (window.devicePixelRatio || 1); + + // Select shader program + this.gl.useProgram(this.programInfo.program); + + // Set resolution uniform + this.gl.uniform2f(this.programInfo.uniformLocations.resolution, width, height); + + // Draw connections + this.renderConnectionsWebGL(); + + // Draw nodes + this.renderNodesWebGL(); + } + + renderNodesWebGL() { + // 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; + + // Size with pulse effect + const pulse = Math.sin(node.pulsePhase) * 0.3 + 1; + sizes[i] = node.size * pulse * (node.connections.length > 3 ? 1.5 : 1); + } + + // 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); + + // Set node color + const colorObj = this.isDarkMode ? this.darkModeColors : this.lightModeColors; + const nodeColor = this.hexToRgb(colorObj.nodeColor); + this.gl.uniform4f( + this.programInfo.uniformLocations.color, + nodeColor.r / 255, + nodeColor.g / 255, + nodeColor.b / 255, + 0.8 // Alpha + ); + + // Draw nodes + this.gl.enable(this.gl.BLEND); + this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA); + this.gl.drawArrays(this.gl.POINTS, 0, this.nodes.length); + } + + renderConnectionsWebGL() { + // For each connection, draw a line + for (const connection of this.connections) { + const fromNode = this.nodes[connection.from]; + const toNode = this.nodes[connection.to]; + + // Line positions + const positions = new Float32Array([ + fromNode.x, fromNode.y, + toNode.x, toNode.y + ]); + + // 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); + + // Disable point size attribute for lines + this.gl.disableVertexAttribArray(this.programInfo.attribLocations.pointSize); + + // Set line color with connection opacity + const colorObj = this.isDarkMode ? this.darkModeColors : this.lightModeColors; + const lineColor = this.hexToRgb(colorObj.connectionColor); + this.gl.uniform4f( + this.programInfo.uniformLocations.color, + lineColor.r / 255, + lineColor.g / 255, + lineColor.b / 255, + connection.opacity + ); + + // Data pulse animation along connection + if (Math.random() < 0.01 && fromNode.connections.length > 2) { + // Draw data pulse (slightly different color) + const pulseColor = this.hexToRgb(colorObj.nodePulse); + this.gl.uniform4f( + this.programInfo.uniformLocations.color, + pulseColor.r / 255, + pulseColor.g / 255, + pulseColor.b / 255, + 0.8 + ); + } + + // Draw the line + this.gl.enable(this.gl.BLEND); + this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE); + this.gl.lineWidth(1); + this.gl.drawArrays(this.gl.LINES, 0, 2); + } + } + + renderCanvas() { + // Clear canvas + const width = this.canvas.width / (window.devicePixelRatio || 1); + const height = this.canvas.height / (window.devicePixelRatio || 1); + + 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 + const connectionColor = this.isDarkMode + ? this.darkModeColors.connectionColor + : this.lightModeColors.connectionColor; + + for (const connection of this.connections) { + const fromNode = this.nodes[connection.from]; + const toNode = this.nodes[connection.to]; + + this.ctx.beginPath(); + this.ctx.moveTo(fromNode.x, fromNode.y); + this.ctx.lineTo(toNode.x, toNode.y); + + const rgbColor = this.hexToRgb(connectionColor); + this.ctx.strokeStyle = `rgba(${rgbColor.r}, ${rgbColor.g}, ${rgbColor.b}, ${connection.opacity})`; + this.ctx.stroke(); + } + + // Draw nodes + 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) { + // Node with glow effect + const pulse = Math.sin(node.pulsePhase) * 0.3 + 1; + const nodeSize = node.size * pulse * (node.connections.length > 3 ? 1.5 : 1); + + // Glow effect + const glow = this.ctx.createRadialGradient( + node.x, node.y, 0, + node.x, node.y, nodeSize * 2 + ); + + const rgbNodeColor = this.hexToRgb(nodeColor); + const rgbPulseColor = this.hexToRgb(nodePulse); + + glow.addColorStop(0, `rgba(${rgbPulseColor.r}, ${rgbPulseColor.g}, ${rgbPulseColor.b}, 0.8)`); + glow.addColorStop(0.5, `rgba(${rgbNodeColor.r}, ${rgbNodeColor.g}, ${rgbNodeColor.b}, 0.2)`); + glow.addColorStop(1, `rgba(${rgbNodeColor.r}, ${rgbNodeColor.g}, ${rgbNodeColor.b}, 0)`); + + this.ctx.beginPath(); + this.ctx.arc(node.x, node.y, nodeSize * 2, 0, Math.PI * 2); + this.ctx.fillStyle = glow; + this.ctx.fill(); + + // Main node + this.ctx.beginPath(); + this.ctx.arc(node.x, node.y, nodeSize, 0, Math.PI * 2); + this.ctx.fillStyle = nodeColor; + this.ctx.fill(); + } + } + + // Helper method to convert hex to RGB + hexToRgb(hex) { + // Remove # if present + hex = hex.replace(/^#/, ''); + + // 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 }; + } + + // Cleanup method + destroy() { + if (this.animationFrameId) { + cancelAnimationFrame(this.animationFrameId); + } + + window.removeEventListener('resize', this.resizeCanvas.bind(this)); + + if (this.canvas && this.canvas.parentNode) { + this.canvas.parentNode.removeChild(this.canvas); + } + + if (this.gl) { + // Clean up WebGL resources + this.gl.deleteBuffer(this.positionBuffer); + this.gl.deleteBuffer(this.sizeBuffer); + this.gl.deleteProgram(this.shaderProgram); + } + } +} + +// Initialize when DOM is loaded +document.addEventListener('DOMContentLoaded', () => { + window.neuralNetworkBackground = new NeuralNetworkBackground(); +}); + +// Clean up when window is closed +window.addEventListener('beforeunload', () => { + if (window.neuralNetworkBackground) { + window.neuralNetworkBackground.destroy(); + } +}); \ No newline at end of file diff --git a/website/static/style.css b/website/static/style.css new file mode 100644 index 0000000..c387931 --- /dev/null +++ b/website/static/style.css @@ -0,0 +1,484 @@ +/* Main Systades Styles - Dark Mystical Theme */ + +/* Import Fonts */ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap'); + +/* Root Variables */ +:root { + /* Light Theme Colors */ + --light-bg-primary: #f8fafc; + --light-bg-secondary: #f1f5f9; + --light-text-primary: #1e293b; + --light-text-secondary: #475569; + --light-accent-primary: #7c3aed; + --light-accent-secondary: #8b5cf6; + --light-border: #e2e8f0; + + /* Dark Theme Colors */ + --dark-bg-primary: #0a0e19; + --dark-bg-secondary: #111827; + --dark-text-primary: #f9fafb; + --dark-text-secondary: #e5e7eb; + --dark-accent-primary: #6d28d9; + --dark-accent-secondary: #8b5cf6; + --dark-border: #1f2937; + + /* Common */ + --font-sans: 'Inter', system-ui, -apple-system, sans-serif; + --font-mono: 'JetBrains Mono', monospace; + --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + --shadow-md: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + --shadow-lg: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + + /* Transitions */ + --transition-fast: 150ms ease-in-out; + --transition-normal: 300ms ease-in-out; + --transition-slow: 500ms ease-in-out; +} + +/* Base Elements */ +body { + font-family: var(--font-sans); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + transition: background-color var(--transition-normal), color var(--transition-normal); +} + +/* Theme Specific */ +body { + background-color: var(--light-bg-primary); + color: var(--light-text-primary); +} + +body.dark { + background-color: var(--dark-bg-primary); + color: var(--dark-text-primary); +} + +/* Typography */ +h1, h2, h3, h4, h5, h6 { + font-weight: 600; + line-height: 1.2; +} + +p { + line-height: 1.6; +} + +.gradient-text { + background-clip: text; + -webkit-background-clip: text; + color: transparent; + position: relative; +} + +body .gradient-text { + background-image: linear-gradient(135deg, var(--light-accent-primary), var(--light-accent-secondary)); +} + +body.dark .gradient-text { + background-image: linear-gradient(135deg, var(--dark-accent-primary), var(--dark-accent-secondary)); +} + +/* Subtle glow for dark mode gradient text */ +body.dark .gradient-text::after { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + filter: blur(8px); + opacity: 0.3; + background-image: inherit; + z-index: -1; + pointer-events: none; +} + +/* Containers and Layout */ +.container { + width: 100%; + margin-left: auto; + margin-right: auto; + padding-left: 1rem; + padding-right: 1rem; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +/* Glass Morphism */ +.glass-morphism { + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); +} + +body .glass-navbar-light { + background-color: rgba(255, 255, 255, 0.8); + border-color: rgba(226, 232, 240, 0.5); + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); +} + +body.dark .glass-navbar-dark { + background-color: rgba(10, 14, 25, 0.8); + border-color: rgba(31, 41, 55, 0.5); + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3); +} + +/* Navigation */ +.nav-link { + padding: 0.5rem 0.75rem; + border-radius: 0.5rem; + transition: all var(--transition-normal); +} + +body .nav-link { + color: var(--light-text-secondary); +} + +body.dark .nav-link { + color: var(--dark-text-secondary); +} + +body .nav-link:hover { + color: var(--light-text-primary); + background-color: rgba(241, 245, 249, 0.5); +} + +body.dark .nav-link:hover { + color: var(--dark-text-primary); + background-color: rgba(31, 41, 55, 0.5); +} + +body .nav-link-light-active { + color: var(--light-accent-primary); + background-color: rgba(139, 92, 246, 0.1); +} + +body.dark .nav-link-active { + color: var(--dark-accent-secondary); + background-color: rgba(109, 40, 217, 0.15); +} + +/* Buttons */ +.btn { + padding: 0.5rem 1rem; + border-radius: 0.5rem; + font-weight: 500; + transition: all var(--transition-normal); + display: inline-flex; + align-items: center; + justify-content: center; +} + +body .btn-primary { + background-color: var(--light-accent-primary); + color: white; +} + +body.dark .btn-primary { + background-color: var(--dark-accent-primary); + color: white; +} + +body .btn-primary:hover { + background-color: var(--light-accent-secondary); + box-shadow: 0 0 15px rgba(124, 58, 237, 0.3); +} + +body.dark .btn-primary:hover { + background-color: var(--dark-accent-secondary); + box-shadow: 0 0 15px rgba(109, 40, 217, 0.5); +} + +body .btn-secondary { + background-color: transparent; + border: 1px solid var(--light-border); + color: var(--light-text-primary); +} + +body.dark .btn-secondary { + background-color: transparent; + border: 1px solid var(--dark-border); + color: var(--dark-text-primary); +} + +body .btn-secondary:hover { + background-color: var(--light-bg-secondary); + border-color: var(--light-accent-secondary); +} + +body.dark .btn-secondary:hover { + background-color: var(--dark-bg-secondary); + border-color: var(--dark-accent-secondary); +} + +/* Cards */ +.card { + border-radius: 0.75rem; + overflow: hidden; + transition: all var(--transition-normal); +} + +body .card { + background-color: white; + border: 1px solid var(--light-border); + box-shadow: var(--shadow); +} + +body.dark .card { + background-color: var(--dark-bg-secondary); + border: 1px solid var(--dark-border); + box-shadow: var(--shadow); +} + +body .card:hover { + transform: translateY(-5px); + box-shadow: var(--shadow-md); +} + +body.dark .card:hover { + transform: translateY(-5px); + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2); +} + +/* Form Elements */ +.form-input { + width: 100%; + padding: 0.5rem 0.75rem; + border-radius: 0.5rem; + transition: all var(--transition-normal); +} + +body .form-input { + background-color: white; + border: 1px solid var(--light-border); + color: var(--light-text-primary); +} + +body.dark .form-input { + background-color: var(--dark-bg-secondary); + border: 1px solid var(--dark-border); + color: var(--dark-text-primary); +} + +body .form-input:focus { + outline: none; + border-color: var(--light-accent-secondary); + box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.15); +} + +body.dark .form-input:focus { + outline: none; + border-color: var(--dark-accent-secondary); + box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.25); +} + +/* Animations */ +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +.animate-fade-in { + animation: fadeIn 0.5s ease-out forwards; +} + +@keyframes float { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-10px); } +} + +.animate-float { + animation: float 5s ease-in-out infinite; +} + +@keyframes pulse { + 0%, 100% { opacity: 0.7; } + 50% { opacity: 1; } +} + +.animate-pulse { + animation: pulse 3s ease-in-out infinite; +} + +/* Utilities */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +.shadow-elevation { + transition: box-shadow var(--transition-normal), transform var(--transition-normal); +} + +body .shadow-elevation { + box-shadow: var(--shadow); +} + +body.dark .shadow-elevation { + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2); +} + +body .shadow-elevation:hover { + box-shadow: var(--shadow-md); + transform: translateY(-2px); +} + +body.dark .shadow-elevation:hover { + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.4), 0 4px 6px -2px rgba(0, 0, 0, 0.3); + transform: translateY(-2px); +} + +/* Tooltips */ +.tooltip { + position: relative; +} + +.tooltip:hover::before { + content: attr(data-tooltip); + position: absolute; + bottom: 100%; + left: 50%; + transform: translateX(-50%); + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; + font-size: 0.75rem; + white-space: nowrap; + z-index: 10; + opacity: 0; + animation: fadeIn 0.3s ease-out forwards; +} + +body .tooltip:hover::before { + background-color: var(--light-text-primary); + color: white; +} + +body.dark .tooltip:hover::before { + background-color: var(--dark-text-primary); + color: var(--dark-bg-primary); +} + +/* Mystical elements */ +.mystical-border { + position: relative; + overflow: hidden; +} + +.mystical-border::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border: 1px solid; + border-radius: inherit; + pointer-events: none; + opacity: 0.3; + transition: opacity var(--transition-normal); +} + +body .mystical-border::after { + border-color: var(--light-accent-primary); +} + +body.dark .mystical-border::after { + border-color: var(--dark-accent-primary); +} + +.mystical-border:hover::after { + opacity: 0.6; +} + +/* Responsive Design Helpers */ +@media (max-width: 640px) { + .container { + padding-left: 1rem; + padding-right: 1rem; + } + + .hero-heading { + font-size: 2rem; + } + + .section-heading { + font-size: 1.5rem; + } +} + +/* Accessibility */ +:focus-visible { + outline: 2px solid; + outline-offset: 2px; +} + +body :focus-visible { + outline-color: var(--light-accent-primary); +} + +body.dark :focus-visible { + outline-color: var(--dark-accent-primary); +} + +/* Scrollbar styling */ +::-webkit-scrollbar { + width: 0.5rem; + height: 0.5rem; +} + +body ::-webkit-scrollbar-track { + background: var(--light-bg-secondary); +} + +body.dark ::-webkit-scrollbar-track { + background: var(--dark-bg-secondary); +} + +body ::-webkit-scrollbar-thumb { + background: var(--light-accent-primary); + border-radius: 0.25rem; +} + +body.dark ::-webkit-scrollbar-thumb { + background: var(--dark-accent-primary); + border-radius: 0.25rem; +} + +body ::-webkit-scrollbar-thumb:hover { + background: var(--light-accent-secondary); +} + +body.dark ::-webkit-scrollbar-thumb:hover { + background: var(--dark-accent-secondary); +} \ No newline at end of file diff --git a/website/templates/base.html b/website/templates/base.html index df00ce3..35664a3 100644 --- a/website/templates/base.html +++ b/website/templates/base.html @@ -83,8 +83,8 @@ - - + + {% endblock %} \ No newline at end of file diff --git a/website/templates/mindmap.html b/website/templates/mindmap.html index 91b9d35..f9adaed 100644 --- a/website/templates/mindmap.html +++ b/website/templates/mindmap.html @@ -3,1049 +3,611 @@ {% block title %}Mindmap{% endblock %} {% block extra_css %} - {% endblock %} {% block content %} -
Interagiere mit der Mindmap, um Verbindungen zu entdecken und neue Ideen hinzuzufügen
-Wissenslandschaft wird geladen...
-Daten werden aus der Datenbank abgerufen
-Lass dir von künstlicher Intelligenz helfen, neue Zusammenhänge zu entdecken, Inhalte zusammenzufassen und Fragen zu beantworten.
+Finde genau die Informationen, die du suchst, mit fortschrittlichen Such- und Filterfunktionen für eine präzise Navigation durch das Wissen.
+ +