From e4f6a2542f7f830d35882798776798519c904cd9 Mon Sep 17 00:00:00 2001 From: Daniel LaForce Date: Thu, 10 Apr 2025 00:47:07 -0600 Subject: [PATCH] Update contact details, enhance footer and resume sections, improve form notification styles, and fix container metrics in index.html --- images/site.webmanifest | 2 +- index.html | 35 +++++++++----- resume.html | 103 +++++++++++++++++++++------------------- script.js | 30 ++++++++---- style.css | 101 +++++++++++++++++++++++++++++++++++++++ styles.css | 40 +++++++++++----- 6 files changed, 231 insertions(+), 80 deletions(-) create mode 100644 style.css diff --git a/images/site.webmanifest b/images/site.webmanifest index 45dc8a2..ecb9516 100644 --- a/images/site.webmanifest +++ b/images/site.webmanifest @@ -1 +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 +{"name":"LaForceIT","short_name":"LaForceIT","icons":[{"src":"/images/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/images/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#1e293b","background_color":"#0f172a","display":"standalone"} \ No newline at end of file diff --git a/index.html b/index.html index 178f86c..ba83b2d 100644 --- a/index.html +++ b/index.html @@ -124,7 +124,7 @@
> CPU load: Normal
> RAM usage: 42%
> Network: Stable
-
> Containers: 16 running
+
> Containers: 23 running
> Backing up critical data...
> Status: All systems operational
@@ -589,7 +589,7 @@

Email

-

daniel.laforce@argobox.com

+ daniel@laforceit.com
@@ -597,7 +597,7 @@

LinkedIn

-

linkedin.com/in/danlaforce

+

linkedin.com/in/daniellaforce

@@ -605,7 +605,7 @@

GitHub

-

github.com/keyargo

+

github.com/daniellaforce

@@ -650,27 +650,40 @@
diff --git a/resume.html b/resume.html index 253cd02..cf2dcbb 100644 --- a/resume.html +++ b/resume.html @@ -3,10 +3,10 @@ - Daniel LaForce | Resume + Daniel LaForce | Enterprise IT Solutions Architect & Security Expert - + @@ -486,7 +486,7 @@ Schedule a Free Consultation - + Discuss Employment Opportunities @@ -496,29 +496,38 @@

Daniel LaForce

-

Network & Cyber Security Professional

+

Enterprise IT Solutions Architect & Security Expert

+
- Colorado Springs, Colorado + Colorado Springs, CO
- -
+ daniel.laforce@laforceit.com + + - linkedin.com/in/danlaforce -
- + linkedin.com/in/danlaforce + + + + www.laforceit.com + + + + www.argobox.com + + + + git.argobox.com +
@@ -528,7 +537,7 @@
-

Freelance Consultant – GIS, Infrastructure & Security Solutions

+

Freelance IT Consultant – Infrastructure, Security & GIS Solutions

Inovin LLC
April 2023 – Present | Colorado Springs, CO
@@ -549,6 +558,14 @@ Automated infrastructure tasks via PowerShell, Python, and Bash, optimizing system deployment, backup routines, and monitoring pipelines.
+
+ + Administered Azure AD security policies and MFA enforcement, reducing monthly vulnerabilities by at least 20% across client environments. +
+
+ + Served as Interim Virtual Chief Security Officer for 11 clients, conducting penetration testing using BURP Professional and managing enterprise security stack deployment including Blackpoint & Webroot (Endpoint), DNSFilter (Network), and ThreatLocker (Application Control). +
@@ -578,28 +595,6 @@ -
-
-

Interim Virtual Chief Security Officer

-
Technology Response Team, LLC
-
August 2023 - September 2023
-
-
-
- - Served as temporary VCSO for ~120 clients, conducting penetration testing using BURP Professional. -
-
- - Managed enterprise security stack deployment including Blackpoint & Webroot (Endpoint), DNSFilter (Network), and ThreatLocker (Application Control). -
-
- - Administered Azure AD security policies and MFA enforcement, reducing monthly vulnerabilities by at least 20%. -
-
-
-

Network Engineer II

@@ -953,14 +948,13 @@
-

AI-Driven Car Price Prediction

-

Developed machine learning model for predicting used car prices with over 90% accuracy using Random Forest Regression and comprehensive feature engineering.

+

ArgoBox Lab

+

A production-grade Kubernetes homelab environment featuring zero-trust architecture, complete automation, and enterprise-class infrastructure. Running 32+ CPU cores, 64GB RAM, and 12TB storage, hosting 16+ containerized services.

- Python - Scikit-learn - Pandas - Random Forest - Jupyter + Kubernetes + Proxmox + Ansible + Zero Trust
@@ -993,13 +987,26 @@ diff --git a/script.js b/script.js index 809a613..d20434a 100644 --- a/script.js +++ b/script.js @@ -372,6 +372,10 @@ function updateMetrics() { * Initialize contact form handling */ function initFormHandling(form) { + // Prevent multiple initializations + if (form.hasAttribute('data-initialized')) return; + form.setAttribute('data-initialized', 'true'); + form.addEventListener('submit', async function (e) { e.preventDefault(); @@ -384,6 +388,14 @@ function initFormHandling(form) { const notificationIcon = notification.querySelector('i'); const notificationText = notification.querySelector('.notification-text'); + // Hide any existing notification with animation + if (notification.style.display === 'flex') { + notification.classList.add('hiding'); + await new Promise(resolve => setTimeout(resolve, 300)); + notification.classList.remove('hiding'); + notification.style.display = 'none'; + } + const formData = { name: form.elements['name'].value, email: form.elements['email'].value, @@ -392,7 +404,6 @@ function initFormHandling(form) { }; try { - console.log('Sending form data...'); const res = await fetch("/api/send-email", { method: "POST", headers: { @@ -402,21 +413,20 @@ function initFormHandling(form) { }); const data = await res.json(); - console.log('Response:', data); + notification.style.display = 'flex'; + if (res.ok) { - notification.style.display = 'flex'; notification.classList.remove('error'); notification.classList.add('success'); notificationIcon.className = 'fas fa-check-circle'; notificationText.textContent = "Message sent successfully! We'll get back to you soon."; form.reset(); } else { - notification.style.display = 'flex'; notification.classList.remove('success'); notification.classList.add('error'); notificationIcon.className = 'fas fa-exclamation-circle'; - notificationText.textContent = `Error: ${data.error}${data.details ? ` - ${data.details}` : ''}`; + notificationText.textContent = data.error + (data.details ? `: ${data.details}` : ''); } } catch (error) { @@ -430,10 +440,14 @@ function initFormHandling(form) { submitButton.innerHTML = originalButtonText; submitButton.disabled = false; - // Hide notification after 10 seconds + // Hide notification after 5 seconds with animation setTimeout(() => { - notification.style.display = 'none'; - }, 10000); + notification.classList.add('hiding'); + setTimeout(() => { + notification.classList.remove('hiding'); + notification.style.display = 'none'; + }, 300); + }, 5000); } }); } diff --git a/style.css b/style.css new file mode 100644 index 0000000..2858142 --- /dev/null +++ b/style.css @@ -0,0 +1,101 @@ +.form-notification { + margin-top: 1rem; + padding: 1rem 1.5rem; + border-radius: 0.5rem; + display: none; + align-items: center; + gap: 0.75rem; + animation: slideIn 0.3s ease-out; + font-weight: 500; + position: fixed; + bottom: 2rem; + right: 2rem; + z-index: 1000; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); +} + +.form-notification.success { + background-color: var(--success); + color: white; + border: none; +} + +.form-notification.error { + background-color: var(--error); + color: white; + border: none; +} + +.form-notification i { + font-size: 1.25rem; +} + +@keyframes slideIn { + from { + transform: translateX(100%); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} + +@keyframes slideOut { + from { + transform: translateX(0); + opacity: 1; + } + to { + transform: translateX(100%); + opacity: 0; + } +} + +.form-notification.hiding { + animation: slideOut 0.3s ease-in forwards; +} + +@keyframes float-particle { + 0% { + transform: translate(0, 0); + } + 25% { + transform: translate(20px, -20px); + } + 50% { + transform: translate(40px, 0); + } + 75% { + transform: translate(20px, 20px); + } + 100% { + transform: translate(0, 0); + } +} + +:root { + --success: #10b981; + --error: #ef4444; +} + +.logo-text-glow { + font-weight: 800; + font-size: 1.6rem; + background: linear-gradient(90deg, var(--accent), var(--accent-darker)); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + text-shadow: 0 0 6px rgba(59, 130, 246, 0.5); + transition: all 0.3s ease-in-out; + letter-spacing: -0.02em; +} + +.logo-dot-glow { + color: white; + font-weight: 600; + font-size: 1.6rem; + margin-left: -1px; + text-shadow: 0 0 3px rgba(255, 255, 255, 0.3); + transition: all 0.3s ease-in-out; +} \ No newline at end of file diff --git a/styles.css b/styles.css index 1d5875d..6f755d2 100644 --- a/styles.css +++ b/styles.css @@ -1333,18 +1333,19 @@ background-clip: text; /* Footer */ .footer { - background-color: var(--secondary-bg); - padding: 4rem 0 1rem; - margin-top: 6rem; + width: 100%; + padding: 2rem 0; + background: transparent; + border-top: 1px solid var(--border); + margin-top: 2rem; } .footer-content { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 3rem; flex-wrap: wrap; - gap: 2rem; + gap: 1rem; } .footer-logo { @@ -1354,40 +1355,55 @@ background-clip: text; .footer-links { display: flex; - gap: 2rem; + gap: 1.5rem; + flex-wrap: wrap; } .footer-links a { color: var(--text-secondary); + text-decoration: none; transition: color var(--transition-normal); } .footer-links a:hover { - color: var(--text-primary); + color: var(--accent); } .footer-social { display: flex; - gap: 1.5rem; + gap: 1rem; } .footer-social a { color: var(--text-secondary); font-size: 1.25rem; - transition: all var(--transition-normal); + transition: color var(--transition-normal); } .footer-social a:hover { color: var(--accent); - transform: translateY(-3px); } .footer-bottom { + margin-top: 1.5rem; text-align: center; color: var(--text-secondary); font-size: 0.9rem; - padding-top: 2rem; - border-top: 1px solid var(--border); +} + +@media (max-width: 768px) { + .footer-content { + flex-direction: column; + text-align: center; + } + + .footer-links { + justify-content: center; + } + + .footer-social { + justify-content: center; + } } /* Reveal animations on scroll */