diff --git a/images/android-chrome-192x192.png b/images/android-chrome-192x192.png new file mode 100644 index 0000000..ae134f4 Binary files /dev/null and b/images/android-chrome-192x192.png differ diff --git a/images/android-chrome-512x512.png b/images/android-chrome-512x512.png new file mode 100644 index 0000000..65ec24d Binary files /dev/null and b/images/android-chrome-512x512.png differ diff --git a/images/apple-touch-icon.png b/images/apple-touch-icon.png new file mode 100644 index 0000000..188a172 Binary files /dev/null and b/images/apple-touch-icon.png differ diff --git a/images/favicon-16x16.png b/images/favicon-16x16.png new file mode 100644 index 0000000..a4b69e5 Binary files /dev/null and b/images/favicon-16x16.png differ diff --git a/images/favicon-32x32.png b/images/favicon-32x32.png new file mode 100644 index 0000000..c23887b Binary files /dev/null and b/images/favicon-32x32.png differ diff --git a/images/favicon.ico b/images/favicon.ico new file mode 100644 index 0000000..9f3652c Binary files /dev/null and b/images/favicon.ico differ diff --git a/images/site.webmanifest b/images/site.webmanifest new file mode 100644 index 0000000..45dc8a2 --- /dev/null +++ b/images/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/index.html b/index.html index dcd3b56..35ca1dd 100644 --- a/index.html +++ b/index.html @@ -12,13 +12,20 @@ - + - + + + + + + + + @@ -34,7 +41,7 @@
@@ -47,7 +54,7 @@ Contact
@@ -579,7 +586,7 @@

Email

-

daniel.laforce@argobox.com

+

daniel@laforceit.com

@@ -636,7 +643,7 @@
diff --git a/script.js b/script.js new file mode 100644 index 0000000..fe6c277 --- /dev/null +++ b/script.js @@ -0,0 +1,459 @@ +/** + * Main JavaScript file for argobox.com + * Handles animations, interactions, and dynamic content + */ + +document.addEventListener('DOMContentLoaded', function() { + // Initialize all website functionality + initNavigation(); + initParticlesAndIcons(); + initRoleRotation(); + initTerminalTyping(); + initSolutionsCarousel(); + initScrollReveal(); + updateMetrics(); + updateYear(); + + // Initialize form handling + const contactForm = document.getElementById('contact-form'); + if (contactForm) { + initFormHandling(contactForm); + } +}); + +/** + * Set up navigation functionality - mobile menu and scroll spy + */ +function initNavigation() { + // Mobile menu toggle + const menuToggle = document.querySelector('.menu-toggle'); + const navMenu = document.querySelector('.nav-menu'); + + if (menuToggle && navMenu) { + menuToggle.addEventListener('click', function() { + navMenu.classList.toggle('active'); + menuToggle.setAttribute('aria-expanded', + menuToggle.getAttribute('aria-expanded') === 'true' ? 'false' : 'true'); + }); + } + + // Navigation scroll spy + const sections = document.querySelectorAll('section[id]'); + const navLinks = document.querySelectorAll('.nav-link'); + + function updateActiveNavLink() { + let scrollPosition = window.scrollY + 100; + + sections.forEach(section => { + const sectionTop = section.offsetTop; + const sectionHeight = section.offsetHeight; + const sectionId = section.getAttribute('id'); + + if (scrollPosition >= sectionTop && scrollPosition < sectionTop + sectionHeight) { + navLinks.forEach(link => { + link.classList.remove('active'); + if (link.getAttribute('href') === `#${sectionId}`) { + link.classList.add('active'); + } + }); + } + }); + } + + // Navbar style change on scroll + const navbar = document.querySelector('.navbar'); + + function updateNavbarStyle() { + if (window.scrollY > 50) { + navbar?.classList.add('scrolled'); + } else { + navbar?.classList.remove('scrolled'); + } + } + + window.addEventListener('scroll', () => { + updateActiveNavLink(); + updateNavbarStyle(); + }); + + // Initial call to set correct states + updateActiveNavLink(); + updateNavbarStyle(); +} + +/** + * Create background particles and floating tech icons + */ +function initParticlesAndIcons() { + createBackgroundParticles(); + createFloatingIcons(); +} + +/** + * Create animated background particles + */ +function createBackgroundParticles() { + const particlesContainer = document.getElementById('particles-container'); + + if (!particlesContainer) return; + + particlesContainer.innerHTML = ''; + + for (let i = 0; i < 40; i++) { + const particle = document.createElement('div'); + particle.classList.add('particle'); + + // Random size, opacity, and position + const size = Math.random() * 4 + 1; + const opacity = Math.random() * 0.3 + 0.1; + + particle.style.width = `${size}px`; + particle.style.height = `${size}px`; + particle.style.opacity = opacity; + + // Position randomly with some clustering toward top areas + const xPos = Math.random() * 100; + const yPos = Math.random() * 100; + + particle.style.left = `${xPos}%`; + particle.style.top = `${yPos}%`; + + // Animation properties + particle.style.animationDuration = `${Math.random() * 20 + 10}s`; + particle.style.animationDelay = `${Math.random() * 5}s`; + + // Add particle animation + particle.style.animation = `particle-float ${Math.random() * 20 + 10}s linear infinite`; + + particlesContainer.appendChild(particle); + } +} + +/** + * Create floating tech icons in the background + */ +function createFloatingIcons() { + const iconContainer = document.getElementById('floating-icons'); + + if (!iconContainer) return; + + iconContainer.innerHTML = ''; + + // Tech-related unicode symbols and fontawesome classes + const icons = [ + '⚙️', '💻', '🔒', '🔌', '🌐', '☁️', '📊', + 'fa-server', 'fa-network-wired', 'fa-database', + 'fa-code-branch', 'fa-cloud', 'fa-shield-alt' + ]; + + for (let i = 0; i < 12; i++) { + const icon = document.createElement('div'); + icon.classList.add('floating-icon'); + + const iconType = icons[Math.floor(Math.random() * icons.length)]; + + // Handle both unicode and font awesome + if (iconType.startsWith('fa-')) { + const faIcon = document.createElement('i'); + faIcon.className = `fas ${iconType}`; + icon.appendChild(faIcon); + } else { + icon.textContent = iconType; + } + + // Random size and position + const size = Math.random() * 24 + 16; + icon.style.fontSize = `${size}px`; + + // Position + icon.style.left = `${Math.random() * 100}%`; + icon.style.bottom = `-50px`; + + // Animation + icon.style.animationDuration = `${Math.random() * 30 + 20}s`; + icon.style.animationDelay = `${Math.random() * 10}s`; + + iconContainer.appendChild(icon); + } +} + +/** + * Initialize role rotation in the hero section + */ +function initRoleRotation() { + const roles = document.querySelectorAll('.role'); + const descriptionElement = document.getElementById('role-description'); + + if (roles.length === 0 || !descriptionElement) return; + + let currentRole = 0; + + function rotateRoles() { + // Hide current role + roles[currentRole].classList.remove('active'); + + // Move to next role + currentRole = (currentRole + 1) % roles.length; + + // Show new role + roles[currentRole].classList.add('active'); + + // Update description text + const newDescription = roles[currentRole].getAttribute('data-description'); + if (newDescription) { + descriptionElement.textContent = newDescription; + + // Animate the description change + descriptionElement.style.opacity = '0'; + descriptionElement.style.transform = 'translateY(10px)'; + + setTimeout(() => { + descriptionElement.style.opacity = '1'; + descriptionElement.style.transform = 'translateY(0)'; + }, 50); + } + } + + // Set initial role to active + roles[0].classList.add('active'); + + // Initialize with the first description + const initialDescription = roles[0].getAttribute('data-description'); + if (initialDescription && descriptionElement) { + descriptionElement.textContent = initialDescription; + } + + // Start rotation with delay + setInterval(rotateRoles, 5000); +} + +/** + * Initialize terminal typing animation in the hero section + */ +function initTerminalTyping() { + const terminalText = document.getElementById('terminal-text'); + if (!terminalText) return; + + const terminalMessages = [ + "> Ready for deployment...", + "> Reducing operational costs by 30%", + "> Improving system reliability to 99.9%", + "> Accelerating digital transformation", + "> Enhancing security compliance", + "> Streamlining IT workflows", + "> Optimizing infrastructure performance", + "> Implementing best practices", + "> Supporting business objectives" + ]; + + let currentMessage = 0; + + function typeMessage(message, index = 0) { + if (index < message.length) { + terminalText.textContent = message.substring(0, index + 1); + setTimeout(() => typeMessage(message, index + 1), 50 + Math.random() * 50); + } else { + // Wait before clearing and typing next message + setTimeout(clearAndTypeNext, 3000); + } + } + + function clearAndTypeNext() { + // Clear the current text with a backspace effect + const currentText = terminalText.textContent; + + function backspace(length = currentText.length) { + if (length > 0) { + terminalText.textContent = currentText.substring(0, length - 1); + setTimeout(() => backspace(length - 1), 20); + } else { + // Move to next message + currentMessage = (currentMessage + 1) % terminalMessages.length; + setTimeout(() => typeMessage(terminalMessages[currentMessage]), 500); + } + } + + backspace(); + } + + // Start the typing animation with the first message + typeMessage(terminalMessages[0]); +} + +/** + * Initialize the solutions carousel + */ +function initSolutionsCarousel() { + const slides = document.querySelectorAll('.solution-slide'); + const dots = document.querySelectorAll('.slider-dot'); + + if (slides.length === 0 || dots.length === 0) return; + + let currentSlide = 0; + let slideInterval; + + function showSlide(index) { + // Hide all slides + slides.forEach(slide => slide.classList.remove('active')); + dots.forEach(dot => dot.classList.remove('active')); + + // Show selected slide + slides[index].classList.add('active'); + dots[index].classList.add('active'); + + currentSlide = index; + } + + function nextSlide() { + const next = (currentSlide + 1) % slides.length; + showSlide(next); + } + + // Add click events to dots + dots.forEach((dot, index) => { + dot.addEventListener('click', () => { + clearInterval(slideInterval); + showSlide(index); + + // Restart automatic rotation + slideInterval = setInterval(nextSlide, 5000); + }); + }); + + // Start automatic rotation + slideInterval = setInterval(nextSlide, 5000); + + // Show first slide initially + showSlide(0); +} + +/** + * Add scroll reveal animations to elements + */ +function initScrollReveal() { + const revealElements = document.querySelectorAll('.section-header, .service-card, .project-card, .lab-card, .timeline-item, .contact-item'); + + revealElements.forEach(element => { + element.classList.add('reveal'); + }); + + function checkReveal() { + revealElements.forEach(element => { + const elementTop = element.getBoundingClientRect().top; + const elementVisible = 150; + + if (elementTop < window.innerHeight - elementVisible) { + element.classList.add('active'); + } + }); + } + + // Initial check + checkReveal(); + + // Check on scroll + window.addEventListener('scroll', checkReveal); +} + +/** + * Update metrics values periodically to simulate live data + */ +function updateMetrics() { + const metrics = { + 'CPU Usage': { min: 30, max: 60, element: null }, + 'Memory': { min: 45, max: 70, element: null }, + 'Storage': { min: 60, max: 75, element: null }, + 'Network': { min: 15, max: 40, element: null } + }; + + // Get all metric elements + document.querySelectorAll('.metric').forEach(metric => { + const nameElement = metric.querySelector('.metric-name'); + if (nameElement && metrics[nameElement.textContent]) { + metrics[nameElement.textContent].element = metric; + } + }); + + function updateMetricValues() { + Object.keys(metrics).forEach(key => { + const metric = metrics[key]; + if (!metric.element) return; + + const valueEl = metric.element.querySelector('.metric-value'); + const progressEl = metric.element.querySelector('.metric-progress'); + + if (valueEl && progressEl) { + const newValue = Math.floor(Math.random() * (metric.max - metric.min)) + metric.min; + valueEl.textContent = `${newValue}%`; + progressEl.style.width = `${newValue}%`; + } + }); + } + + // Update metrics every 5 seconds + setInterval(updateMetricValues, 5000); +} + +/** + * Initialize contact form handling + */ +function initFormHandling(form) { + form.addEventListener('submit', async function(e) { + e.preventDefault(); + + const submitButton = form.querySelector('button[type="submit"]'); + const originalButtonText = submitButton.innerHTML; + submitButton.innerHTML = ' Sending...'; + submitButton.disabled = true; + + try { + // Simulated form submission - in production replace with actual API call + // const formData = new FormData(form); + // const formValues = Object.fromEntries(formData.entries()); + + // Simulated API response delay + await new Promise(resolve => setTimeout(resolve, 1500)); + + // Success message + alert('Thank you for your message! I will get back to you soon.'); + form.reset(); + } catch (error) { + console.error('Error:', error); + alert('Failed to send message. Please try again or contact me directly via email.'); + } finally { + submitButton.innerHTML = originalButtonText; + submitButton.disabled = false; + } + }); +} + +/** + * Update copyright year in the footer + */ +function updateYear() { + const yearElement = document.getElementById('current-year'); + if (yearElement) { + yearElement.textContent = new Date().getFullYear(); + } +} + +/** + * Utility function to add particle float animation + */ +document.addEventListener('DOMContentLoaded', function() { + // Initialize all website functionality + initNavigation(); + initParticlesAndIcons(); + initRoleRotation(); // Updated function + initTerminalTyping(); // Updated function + initSolutionsCarousel(); // Updated function + initScrollReveal(); + updateMetrics(); + updateYear(); + + // Initialize form handling + const contactForm = document.getElementById('contact-form'); + if (contactForm) { + initFormHandling(contactForm); + } +}); \ No newline at end of file diff --git a/styles.css b/styles.css index 3083be5..bfc10d3 100644 --- a/styles.css +++ b/styles.css @@ -209,20 +209,31 @@ section { } .logo { - display: flex; - align-items: center; - font-weight: 700; - font-size: 1.5rem; + display: flex; + align-items: center; + font-weight: 700; + font-size: 1.5rem; + color: transparent; + background: linear-gradient(135deg, #3b82f6, #2563eb); + -webkit-background-clip: text; + background-clip: text; + text-shadow: 0 0 8px rgba(59, 130, 246, 0.5); +} + +.logo a { + text-decoration: none; } .logo-text { - color: var(--accent); + margin-right: 4px; + color: inherit; /* inherit gradient */ } .logo-dot { - color: var(--text-primary); + color: var(--text-primary); } + .nav-menu { display: flex; align-items: center; diff --git a/test-styles.css b/test-styles.css new file mode 100644 index 0000000..62cd17c --- /dev/null +++ b/test-styles.css @@ -0,0 +1,57 @@ +/* Test CSS file to confirm changes are working */ + +/* Save this as test-styles.css in your repository root */ + +/* Logo Styles - Enhanced Version */ +.logo { + position: relative; + display: flex; + align-items: center; +} + +.logo a { + display: flex; + align-items: center; + text-decoration: none; + position: relative; +} + +.logo-text { + font-family: 'Inter', sans-serif; + font-weight: 700; + font-size: 1.5rem; + background: linear-gradient(90deg, #ff0000 0%, #ff6e00 50%, #ffa500 100%); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + position: relative; + text-shadow: 0 0 15px rgba(255, 150, 100, 0.2); + letter-spacing: 0.5px; +} + +.logo-dot { + font-family: 'Inter', sans-serif; + font-weight: 700; + font-size: 1.5rem; + color: #ff6e00; + position: relative; +} + +/* Logo with tech icon detail - using different brackets to be obviously visible */ +.logo a::before { + content: "[ "; + font-family: 'JetBrains Mono', monospace; + color: #ff6e00; + font-weight: bold; + opacity: 0.8; + margin-right: 2px; +} + +.logo a::after { + content: " ]"; + font-family: 'JetBrains Mono', monospace; + color: #ff6e00; + font-weight: bold; + opacity: 0.8; + margin-left: 2px; +} \ No newline at end of file