document.addEventListener('DOMContentLoaded', () => { // ============================================= // ๐Ÿง  NEURAL PARTICLE NETWORK โ€” Living Background // ============================================= const canvas = document.getElementById('particle-canvas'); const ctx = canvas.getContext('2d'); let particles = []; const PARTICLE_COUNT = window.innerWidth < 768 ? 40 : 75; const CONNECTION_DIST = 150; let mouseX = window.innerWidth / 2; let mouseY = window.innerHeight / 2; function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } resizeCanvas(); window.addEventListener('resize', resizeCanvas); class Particle { constructor() { this.reset(); } reset() { this.x = Math.random() * canvas.width; this.y = Math.random() * canvas.height; this.vx = (Math.random() - 0.5) * 0.6; this.vy = (Math.random() - 0.5) * 0.6; this.size = Math.random() * 2.5 + 1; this.opacity = Math.random() * 0.6 + 0.2; } update() { // Gentle mouse repulsion const dx = this.x - mouseX; const dy = this.y - mouseY; const dist = Math.sqrt(dx * dx + dy * dy); if (dist < 180) { const force = (180 - dist) / 180 * 0.3; this.vx += (dx / dist) * force * 0.1; this.vy += (dy / dist) * force * 0.1; } // Damping this.vx *= 0.998; this.vy *= 0.998; this.x += this.vx; this.y += this.vy; // Wrap edges if (this.x < -20) this.x = canvas.width + 20; if (this.x > canvas.width + 20) this.x = -20; if (this.y < -20) this.y = canvas.height + 20; if (this.y > canvas.height + 20) this.y = -20; } draw() { ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fillStyle = `rgba(0, 242, 254, ${this.opacity})`; ctx.fill(); } } // Initialize particles for (let i = 0; i < PARTICLE_COUNT; i++) { particles.push(new Particle()); } function animateParticles() { ctx.clearRect(0, 0, canvas.width, canvas.height); // Update & draw particles particles.forEach(p => { p.update(); p.draw(); }); // Draw connections for (let i = 0; i < particles.length; i++) { for (let j = i + 1; j < particles.length; j++) { const dx = particles[i].x - particles[j].x; const dy = particles[i].y - particles[j].y; const dist = Math.sqrt(dx * dx + dy * dy); if (dist < CONNECTION_DIST) { const alpha = (1 - dist / CONNECTION_DIST) * 0.2; ctx.beginPath(); ctx.moveTo(particles[i].x, particles[i].y); ctx.lineTo(particles[j].x, particles[j].y); ctx.strokeStyle = `rgba(0, 220, 255, ${alpha})`; ctx.lineWidth = 0.5; ctx.stroke(); } } } requestAnimationFrame(animateParticles); } animateParticles(); // --- Update mouse for particles --- document.addEventListener('mousemove', (e) => { mouseX = e.clientX; mouseY = e.clientY; document.documentElement.style.setProperty('--cursor-x', e.clientX / window.innerWidth); document.documentElement.style.setProperty('--cursor-y', e.clientY / window.innerHeight); }); // ============================================= // ๐Ÿ“œ SCROLL PROGRESS BAR // ============================================= const scrollProgress = document.getElementById('scroll-progress'); window.addEventListener('scroll', () => { const scrollTop = window.scrollY; const docHeight = document.documentElement.scrollHeight - window.innerHeight; const progress = docHeight > 0 ? (scrollTop / docHeight) * 100 : 0; scrollProgress.style.width = progress + '%'; }); // ============================================= // ๐ŸŽฏ 3D TILT EFFECT FOR CARDS // ============================================= const tiltCards = document.querySelectorAll('.tilt-card'); tiltCards.forEach(card => { card.addEventListener('mousemove', (e) => { const rect = card.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; const centerX = rect.width / 2; const centerY = rect.height / 2; const rotateX = ((y - centerY) / centerY) * -6; const rotateY = ((x - centerX) / centerX) * 6; const glareX = (x / rect.width) * 100; const glareY = (y / rect.height) * 100; card.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale3d(1.02, 1.02, 1.02)`; card.style.setProperty('--glare-x', glareX + '%'); card.style.setProperty('--glare-y', glareY + '%'); }); card.addEventListener('mouseleave', () => { card.style.transform = 'perspective(1000px) rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1)'; card.style.setProperty('--glare-x', '50%'); card.style.setProperty('--glare-y', '50%'); }); }); // ============================================= // ๐Ÿงฒ MAGNETIC BUTTONS // ============================================= const magneticBtns = document.querySelectorAll('.magnetic-btn'); magneticBtns.forEach(btn => { btn.addEventListener('mousemove', (e) => { const rect = btn.getBoundingClientRect(); const x = e.clientX - rect.left - rect.width / 2; const y = e.clientY - rect.top - rect.height / 2; const dist = Math.sqrt(x * x + y * y); const maxDist = 120; if (dist < maxDist) { const strength = (1 - dist / maxDist) * 10; btn.style.transform = `translate(${x * strength * 0.06}px, ${y * strength * 0.06}px) scale(1.05)`; } }); btn.addEventListener('mouseleave', () => { btn.style.transform = 'translate(0px, 0px) scale(1)'; }); }); // ============================================= // ๐ŸŒˆ SCROLL-DRIVEN COLOR MORPHING // ============================================= window.addEventListener('scroll', () => { const scrollTop = window.scrollY; const maxScroll = document.documentElement.scrollHeight - window.innerHeight; const scrollFraction = maxScroll > 0 ? scrollTop / maxScroll : 0; // Hue shifts from 188 (cyan) โ†’ 200 (blue) โ†’ 270 (violet) const hue = 188 + scrollFraction * 82; document.documentElement.style.setProperty('--accent-hue', hue); }); // ============================================= // ๐Ÿ‘๏ธ FADE-IN OBSERVER FOR TIMELINE // ============================================= const timelineItems = document.querySelectorAll('.timeline-item'); const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('revealed'); } }); }, { threshold: 0.2 }); timelineItems.forEach(item => observer.observe(item)); // ============================================= // ๐Ÿ”„ MODE TOGGLE: Bento Grid <-> Terminal // ============================================= const toggleBtn = document.getElementById('mode-toggle'); const mainContent = document.getElementById('main-content'); const terminalMode = document.getElementById('terminal-mode'); const terminalOutput = document.getElementById('terminal-output'); const terminalInput = document.getElementById('terminal-input'); const toggleText = document.getElementById('toggle-text'); let isTerminal = false; toggleBtn.addEventListener('click', () => { if (!isTerminal) { if (document.startViewTransition) { document.startViewTransition(() => { mainContent.classList.add('hidden'); terminalMode.classList.remove('hidden'); }); } else { mainContent.classList.add('hidden'); terminalMode.classList.remove('hidden'); } toggleText.textContent = '๐Ÿ  GUI'; printTerminal("SYSTEM > Terminal Mode Activated.", 'cyan'); printTerminal("Type 'help' for a list of commands.", 'dim'); isTerminal = true; terminalInput.focus(); } else { if (document.startViewTransition) { document.startViewTransition(() => { terminalMode.classList.add('hidden'); mainContent.classList.remove('hidden'); }); } else { terminalMode.classList.add('hidden'); mainContent.classList.remove('hidden'); } toggleText.textContent = '>_ TERMINAL'; isTerminal = false; } }); // ============================================= // ๐Ÿ’ป ENHANCED TERMINAL LOGIC // ============================================= const commands = { help: { desc: "Shows this help message", action: () => { let output = "Available Commands:\n"; for (let cmd in commands) { output += ` ${cmd.padEnd(14)} - ${commands[cmd].desc}\n`; } return output; } }, whoami: { desc: "Display current user", action: () => "๐Ÿ›ก๏ธ Sebastian F. Nestler | TryHackMe Top 7% | Annaberg-Buchholz, DE" }, skills: { desc: "List core competencies", action: () => "โšก Python | JavaScript | C | SQL | Logfile Analysis | Incident Response | Applied AI & Automation" }, contact: { desc: "Show contact info", action: () => "๐Ÿ“ง snestler110@gmail.com | ๐ŸŒ nestler.dev | ๐Ÿ… credly.com/users/sebastian-nestler" }, experience: { desc: "Show career timeline", action: () => { return `๐Ÿ“‹ CAREER TIMELINE: [2022-2023] 3rd Level Support Engineer @ GK Software SE [2022] Ethical Hacking Trainee @ T-Systems [2016] Dispatcher & Medical Assistant @ KV Brandenburg [2010-2014] Independent Entrepreneur @ Rio de Janeiro [2001-2015] Paramedic / Medical Assistant @ Multiple Orgs`; } }, education: { desc: "Show educational background", action: () => { return `๐ŸŽ“ EDUCATION: โ€ข Medical Studies (1st State Exam) โ€” Friedrich Schiller Univ. Jena โ€ข Certified Pharmaceutical Consultant (IHK) โ€” Pharmaakademie Leipzig โ€ข Paramedic Training โ€” Landesschule Sachsen Rotes Kreuz`; } }, languages: { desc: "Show spoken languages", action: () => "๐Ÿ—ฃ๏ธ German (Native) | English (Fluent) | Portuguese (Fluent - Brazilian)" }, clear: { desc: "Clear the terminal", action: () => { terminalOutput.innerHTML = ''; return ''; } }, matrix: { desc: "Fun easter egg", action: () => { let rain = ''; const chars = '01ใ‚ขใ‚คใ‚ฆใ‚จใ‚ชใ‚ซใ‚ญใ‚ฏใ‚ฑใ‚ณใ‚ตใ‚ทใ‚นใ‚ปใ‚ฝใ‚ฟใƒใƒ„ใƒ†ใƒˆ'; for (let i = 0; i < 15; i++) { let line = ''; for (let j = 0; j < 50; j++) { line += chars[Math.floor(Math.random() * chars.length)]; } rain += line + '\n'; } return rain; } } }; function printTerminal(text, color = 'green') { const span = document.createElement('span'); span.textContent = text + '\n'; if (color === 'dim') span.style.opacity = '0.5'; if (color === 'cyan') span.style.color = '#00F2FE'; if (color === 'red') span.style.color = '#ff4444'; if (color === 'white') span.style.color = '#e0ffe0'; terminalOutput.appendChild(span); terminalOutput.scrollTop = terminalOutput.scrollHeight; } terminalInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') { const input = terminalInput.value.trim().toLowerCase(); terminalInput.value = ''; if (commands[input]) { const result = commands[input].action(); printTerminal(`โฏ ${input}`, 'white'); if (result) printTerminal(result, 'white'); } else if (input !== '') { printTerminal(`โฏ ${input}`, 'white'); printTerminal(`Command not found: ${input}. Type 'help' for options.`, 'red'); } } }); function initTerminal() { const welcomeMsg = ` โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ•โ•šโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ•โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ•โ•โ•โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘ โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ• โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ• โ–ˆโ–ˆโ•”โ–ˆโ–ˆโ–ˆโ–ˆโ•”โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ•šโ–ˆโ–ˆโ•”โ• โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•”โ•โ•โ• โ–ˆโ–ˆโ•”โ•โ•โ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘โ•šโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ•”โ•โ•โ• โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ•šโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•— โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ•โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ–ˆโ–ˆโ•‘ โ•šโ•โ• โ–ˆโ–ˆโ•‘โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•—โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ•”โ• โ•šโ•โ•โ•โ•โ•โ• โ•šโ•โ• โ•šโ•โ•โ•โ•โ•โ• โ•šโ•โ•โ•โ•โ•โ•โ•โ•šโ•โ• โ•šโ•โ• โ•šโ•โ• โ•šโ•โ•โ•šโ•โ•โ•โ•โ•โ•โ•โ•šโ•โ•โ•โ•โ•โ• `; printTerminal(welcomeMsg, 'cyan'); printTerminal('โšก CYBER-MEDICAL TERMINAL v2.0 โ€” Sebastian F. Nestler', 'white'); printTerminal('๐Ÿ›ก๏ธ Cybersecurity | ๐Ÿค– AI Applications | ๐Ÿฅ Emergency Operations', 'dim'); printTerminal('\nType "help" to explore available commands.', 'dim'); } initTerminal(); });