commit 35ffe75572191547a0c4409c7d12a1dd714b49dd Author: Daniel LaForce Date: Tue Apr 22 16:01:08 2025 -0600 Initial commit diff --git a/assets/css/style.css b/assets/css/style.css new file mode 100644 index 0000000..08961ef --- /dev/null +++ b/assets/css/style.css @@ -0,0 +1,49 @@ +@keyframes pulse-ring { + 0% { + transform: scale(0.33); + opacity: 0; + } + 80%, 100% { + opacity: 0; + } + 40% { + opacity: 0.3; + } +} + +/* Particles */ +.particles-container { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; + pointer-events: none; + z-index: 0; +} + +.particle { + position: absolute; + background-color: rgba(59, 130, 246, 0.2); + border-radius: 50%; + pointer-events: none; +} + +@keyframes float-particle { + 0% { + transform: translate(0, 0); + } + 25% { + transform: translate(50px, -50px); + } + 50% { + transform: translate(100px, 0); + } + 75% { + transform: translate(50px, 50px); + } + 100% { + transform: translate(0, 0); + } +} \ No newline at end of file diff --git a/assets/js/script.js b/assets/js/script.js new file mode 100644 index 0000000..b3401f4 --- /dev/null +++ b/assets/js/script.js @@ -0,0 +1,50 @@ +function initFormHandling() { + const form = document.getElementById('contact-form'); + if (!form) return; + + form.addEventListener('submit', async (e) => { + e.preventDefault(); + + const submitBtn = form.querySelector('button[type="submit"]'); + const originalBtnText = submitBtn.innerHTML; + submitBtn.innerHTML = ' Sending...'; + submitBtn.disabled = true; + + const formData = new FormData(form); + const data = { + name: formData.get('name'), + email: formData.get('email'), + subject: formData.get('subject'), + message: formData.get('message') + }; + + try { + const response = await fetch('/api/send-email', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(data) + }); + + const result = await response.json(); + + if (!response.ok) { + throw new Error(result.details || result.error || 'Failed to send message'); + } + + // Show success message + showAlert('success', 'Message sent successfully! I will get back to you soon.'); + form.reset(); + + } catch (error) { + console.error('Form submission error:', error); + showAlert('error', `Something went wrong: ${error.message}`); + } finally { + submitBtn.innerHTML = originalBtnText; + submitBtn.disabled = false; + } + }); +} + +// ... existing code ... \ No newline at end of file diff --git a/casestudy.html b/casestudy.html new file mode 100644 index 0000000..2e626d3 --- /dev/null +++ b/casestudy.html @@ -0,0 +1,135 @@ + + + + + + Case Study: SharePoint & M365 Migrations | Daniel LaForce + + + + + + +
+
+
+ + + + +
+
+

Case Study: SharePoint & M365 Migrations

+

Modernizing enterprise collaboration and storage through Microsoft 365

+
+
+ +
+
+
+
+

Client Overview

+

+ A mid-sized professional services firm relied on Dropbox, Egnyte, Google Workspace, and on-prem file servers to manage daily operations. The IT environment was fragmented, increasing operational complexity and impeding secure collaboration. They needed a scalable solution to unify infrastructure, reduce support load, and enhance productivity. +

+ +

Objectives

+
    +
  • Consolidate file and email services under Microsoft 365
  • +
  • Minimize business disruption during migration
  • +
  • Strengthen compliance, backup, and access control
  • +
  • Enable centralized collaboration with Microsoft Teams
  • +
+
+

Solution Strategy

+
    +
  • Provisioned a temporary VM to act as a synchronized landing zone for Egnyte and local file server data
  • +
  • Used Egnyte’s API and command-line tools to batch-export permissions and files to the sync VM
  • +
  • Ran Microsoft’s SharePoint Migration Tool (SPMT) with JSON-based mapping to import content into SharePoint document libraries
  • +
  • Automated pre/post migration checks and permissions auditing via PowerShell and Graph API
  • +
  • Staged migration of mail and calendar data using MigrationWiz with coexistence enabled between Google Workspace and Exchange Online
  • +
  • Provided Teams onboarding with channel templates and cross-platform training
  • +
+
+

Results

+
    +
  • 12.4 TB of content migrated across four platforms in under 90 days
  • +
  • Zero data loss or permission mismatches confirmed via script-based audits
  • +
  • Helpdesk load cut by 40% within the first month post-migration
  • +
  • Fully adopted Microsoft Teams structure with defined department-based channels and integrated file repositories
  • +
  • End-user training boosted post-migration satisfaction to 97%
  • +
+ +
+ SharePoint Online + OneDrive + Teams + Exchange Online + PowerShell + Migration Tools + Egnyte API + SPMT +
+ + +
+
+
+
+ + + + + diff --git a/construction.html b/construction.html new file mode 100644 index 0000000..040b2c3 --- /dev/null +++ b/construction.html @@ -0,0 +1,170 @@ + + + + + + Under Construction | LaForceIT + + + + + + + + +
+
+
+

UNDER CONSTRUCTION

+

This service is currently being deployed and configured

+ +
+
Initializing deployment sequence...
+
Configuring infrastructure components...
+
Deploying service containers...
+
Establishing secure connections...
+
Optimizing performance parameters...
+
+ +
+
+
+ + + + Return to LaForceIT + +
+
+ + + + \ No newline at end of file diff --git a/files/daniel-laforce-resume.pdf b/files/daniel-laforce-resume.pdf new file mode 100644 index 0000000..0effeb9 Binary files /dev/null and b/files/daniel-laforce-resume.pdf differ diff --git a/functions/api/send-email.js b/functions/api/send-email.js new file mode 100644 index 0000000..bf817e6 --- /dev/null +++ b/functions/api/send-email.js @@ -0,0 +1,76 @@ +/** + * Cloudflare Pages Function - /api/send-email + */ + +export async function onRequestPost(context) { + const { name, email, subject, message } = await context.request.json(); + + try { + // Check if API key is configured + if (!context.env.MAILERSEND_API_KEY) { + throw new Error('MailerSend API key is not configured'); + } + + console.log('Attempting to send email with MailerSend...'); + + const response = await fetch("https://api.mailersend.com/v1/email", { + method: "POST", + headers: { + Authorization: "Bearer " + context.env.MAILERSEND_API_KEY, + "Content-Type": "application/json" + }, + body: JSON.stringify({ from: { + email: email, + name: name + }, + to: [ + { + email: "upwork@example.com", + name: "Contact Form" + } + ], + subject: `LaForceIT Contact Form: ${subject}`, + html: ` +

New Contact Message on LaForceIT.com

+

Name: ${name}

+

Email: ${email}

+

Message:
${message.replace(/\n/g, "
")}

+ `, + reply_to: [ + { + email, + name + } + ] + }) + }); + + const responseData = await response.text(); + console.log("MailerSend Response:", responseData); + + if (!response.ok) { + const errorDetail = responseData ? JSON.parse(responseData) : {}; + throw new Error(`MailerSend API error (${response.status}): ${JSON.stringify(errorDetail)}`); + } + + return new Response(JSON.stringify({ success: true }), { + status: 200, + headers: { + 'Content-Type': 'application/json' + } + }); + + } catch (err) { + console.error("Email Send Error:", err); + return new Response(JSON.stringify({ + error: "Failed to send email", + details: err.message, + timestamp: new Date().toISOString() + }), { + status: 500, + headers: { + 'Content-Type': 'application/json' + } + }); + } +} diff --git a/images/.gitkeep b/images/.gitkeep new file mode 100644 index 0000000..70d5beb --- /dev/null +++ b/images/.gitkeep @@ -0,0 +1,18 @@ +# This file ensures the images directory exists in the repository. +# You should add the following images: + +# Infrastructure Dashboard Images +# - argobox-dashboard.jpg - Screenshot of your ArgoBox dashboard +# - ansible-automation.jpg - Visualization of Ansible automation workflows +# - zero-trust.jpg - Diagram or screenshot of zero trust architecture +# - kubernetes-lab.jpg - Screenshot of K3s cluster dashboard + +# Project Portfolio Images +# - prospector-pulse.jpg - Screenshot or mockup of Prospector Pulse mapping interface +# - terramappix.jpg - Screenshot or mockup of TerraMappix interface +# - cloudflare-ddns.jpg - Visual representation of the DDNS workflow +# - car-price-ml.jpg - Screenshot of the car price prediction model interface +# - wifi-cage.jpg - Photo or diagram of the Wi-Fi testing cage +# - vsadeployer.jpg - Screenshot of the VSADeployer interface + +# These images should be approximately 800x450px in size for optimal display in the portfolio. \ No newline at end of file diff --git a/images/android-chrome-192x192.png b/images/android-chrome-192x192.png new file mode 100644 index 0000000..1775ef7 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..86e3368 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..ab1f1e5 Binary files /dev/null and b/images/apple-touch-icon.png differ diff --git a/images/argobox/dashboard.jpg b/images/argobox/dashboard.jpg new file mode 100644 index 0000000..bd98aef Binary files /dev/null and b/images/argobox/dashboard.jpg differ diff --git a/images/argobox/dashboard.png b/images/argobox/dashboard.png new file mode 100644 index 0000000..6e609a1 Binary files /dev/null and b/images/argobox/dashboard.png differ diff --git a/images/argobox/sandbox.jpg b/images/argobox/sandbox.jpg new file mode 100644 index 0000000..3cb7bcb Binary files /dev/null and b/images/argobox/sandbox.jpg differ diff --git a/images/argobox/sandbox.png b/images/argobox/sandbox.png new file mode 100644 index 0000000..ae8f3ce Binary files /dev/null and b/images/argobox/sandbox.png differ diff --git a/images/cpe-vault/build.jpeg b/images/cpe-vault/build.jpeg new file mode 100644 index 0000000..9c72594 Binary files /dev/null and b/images/cpe-vault/build.jpeg differ diff --git a/images/cpe-vault/cover.jpg b/images/cpe-vault/cover.jpg new file mode 100644 index 0000000..bb81201 Binary files /dev/null and b/images/cpe-vault/cover.jpg differ diff --git a/images/cpe-vault/cpe-vault-wifi-cages.jpeg b/images/cpe-vault/cpe-vault-wifi-cages.jpeg new file mode 100644 index 0000000..bed3e17 Binary files /dev/null and b/images/cpe-vault/cpe-vault-wifi-cages.jpeg differ diff --git a/images/cpe-vault/page1.jpg b/images/cpe-vault/page1.jpg new file mode 100644 index 0000000..0b299a3 Binary files /dev/null and b/images/cpe-vault/page1.jpg differ diff --git a/images/cpe-vault/page10.jpg b/images/cpe-vault/page10.jpg new file mode 100644 index 0000000..4b74b0f Binary files /dev/null and b/images/cpe-vault/page10.jpg differ diff --git a/images/cpe-vault/page2.jpg b/images/cpe-vault/page2.jpg new file mode 100644 index 0000000..9597d5d Binary files /dev/null and b/images/cpe-vault/page2.jpg differ diff --git a/images/cpe-vault/page3.jpg b/images/cpe-vault/page3.jpg new file mode 100644 index 0000000..9176fb3 Binary files /dev/null and b/images/cpe-vault/page3.jpg differ diff --git a/images/cpe-vault/page4.jpg b/images/cpe-vault/page4.jpg new file mode 100644 index 0000000..e103375 Binary files /dev/null and b/images/cpe-vault/page4.jpg differ diff --git a/images/cpe-vault/page5.jpg b/images/cpe-vault/page5.jpg new file mode 100644 index 0000000..21748c9 Binary files /dev/null and b/images/cpe-vault/page5.jpg differ diff --git a/images/cpe-vault/page6.jpg b/images/cpe-vault/page6.jpg new file mode 100644 index 0000000..1cb1ea6 Binary files /dev/null and b/images/cpe-vault/page6.jpg differ diff --git a/images/cpe-vault/page7.jpg b/images/cpe-vault/page7.jpg new file mode 100644 index 0000000..c066f00 Binary files /dev/null and b/images/cpe-vault/page7.jpg differ diff --git a/images/cpe-vault/page8.jpg b/images/cpe-vault/page8.jpg new file mode 100644 index 0000000..8422316 Binary files /dev/null and b/images/cpe-vault/page8.jpg differ diff --git a/images/cpe-vault/page9.jpg b/images/cpe-vault/page9.jpg new file mode 100644 index 0000000..9b13a8f Binary files /dev/null and b/images/cpe-vault/page9.jpg differ diff --git a/images/data-center/cages.jpeg b/images/data-center/cages.jpeg new file mode 100644 index 0000000..bed3e17 Binary files /dev/null and b/images/data-center/cages.jpeg differ diff --git a/images/data-center/datacenter-before.jpeg b/images/data-center/datacenter-before.jpeg new file mode 100644 index 0000000..9c67366 Binary files /dev/null and b/images/data-center/datacenter-before.jpeg differ diff --git a/images/data-center/datacenter-cover.jpeg b/images/data-center/datacenter-cover.jpeg new file mode 100644 index 0000000..9f2f96d Binary files /dev/null and b/images/data-center/datacenter-cover.jpeg differ diff --git a/images/data-center/rack-wiring.jpeg b/images/data-center/rack-wiring.jpeg new file mode 100644 index 0000000..07c8047 Binary files /dev/null and b/images/data-center/rack-wiring.jpeg differ diff --git a/images/data-center/shipment.jpeg b/images/data-center/shipment.jpeg new file mode 100644 index 0000000..3de3019 Binary files /dev/null and b/images/data-center/shipment.jpeg differ diff --git a/images/favicon-16x16.png b/images/favicon-16x16.png new file mode 100644 index 0000000..9acbaf5 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..79a05b3 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..12d1439 Binary files /dev/null and b/images/favicon.ico differ diff --git a/images/full-stack/main.jpg b/images/full-stack/main.jpg new file mode 100644 index 0000000..52e69fb Binary files /dev/null and b/images/full-stack/main.jpg differ diff --git a/images/full-stack/main.png b/images/full-stack/main.png new file mode 100644 index 0000000..4c2af22 Binary files /dev/null and b/images/full-stack/main.png differ diff --git a/images/migrations/sharepoint-m365.jpg b/images/migrations/sharepoint-m365.jpg new file mode 100644 index 0000000..590c926 Binary files /dev/null and b/images/migrations/sharepoint-m365.jpg differ diff --git a/images/migrations/sharepoint-m365.png b/images/migrations/sharepoint-m365.png new file mode 100644 index 0000000..13c9d32 Binary files /dev/null and b/images/migrations/sharepoint-m365.png differ diff --git a/images/og-preview.png b/images/og-preview.png new file mode 100644 index 0000000..cc42648 Binary files /dev/null and b/images/og-preview.png differ diff --git a/images/site.webmanifest b/images/site.webmanifest new file mode 100644 index 0000000..ecb9516 --- /dev/null +++ b/images/site.webmanifest @@ -0,0 +1 @@ +{"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/images/terramappix/website.jpg b/images/terramappix/website.jpg new file mode 100644 index 0000000..c139a9f Binary files /dev/null and b/images/terramappix/website.jpg differ diff --git a/images/terramappix/website.png b/images/terramappix/website.png new file mode 100644 index 0000000..c7463d9 Binary files /dev/null and b/images/terramappix/website.png differ diff --git a/images/terratracer/claims.jpg b/images/terratracer/claims.jpg new file mode 100644 index 0000000..3cdfc0f Binary files /dev/null and b/images/terratracer/claims.jpg differ diff --git a/images/terratracer/claims.png b/images/terratracer/claims.png new file mode 100644 index 0000000..9ce3df1 Binary files /dev/null and b/images/terratracer/claims.png differ diff --git a/images/used-car-prediction/jupyter-notebook.jpg b/images/used-car-prediction/jupyter-notebook.jpg new file mode 100644 index 0000000..acfbfe8 Binary files /dev/null and b/images/used-car-prediction/jupyter-notebook.jpg differ diff --git a/images/used-car-prediction/jupyter-notebook.png b/images/used-car-prediction/jupyter-notebook.png new file mode 100644 index 0000000..19ef5f8 Binary files /dev/null and b/images/used-car-prediction/jupyter-notebook.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..3eaf4e2 --- /dev/null +++ b/index.html @@ -0,0 +1,1367 @@ + + + + + + Daniel LaForce | Infrastructure & Systems Architect | DevOps Engineer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ +
+ +
+
+
+
+

Daniel LaForce

+
+ +

+ + + Systems Administrator & IT Expert + + + Network & Cyber Security Professional + + + Infrastructure & Systems Architect + + + DevOps & Automation Expert + + +

+ +

+ Helping businesses run faster, safer, and smarter—through automated infrastructure, secure architecture, and real-world engineering. +

+ +
+
> Ready for deployment...
+
+
+ +
+
+
+ Lab Status: Online +
+ +
+
+
+
+
+
+
+
+
+
> Monitoring infrastructure...
+
> CPU load: Normal
+
> RAM usage: 42%
+
> Network: Stable
+
> Containers: 23 running
+
> Backing up critical data...
+
> Status: All systems operational
+
+
+
+ + +
+
+
+ + +
+
+
+

Solving Complex IT Challenges

+
+ + + +
+ + + + + + + + +
+
+
+ + +
+ + +
+
+

Professional Portfolio

+

+ A selection of my most impactful technical work across various domains and technologies. +

+ +
+ + + + + + + + + + + + + + + + + +
+
+
+ + +
+
+
+ +
+
+ Data Center Deployment +
+
+

Enterprise Data Center Deployment

+
Novus Professional Services
+
June 2023 - December 2023
+

+ Complete data center build-out from ground up, including rack design, power distribution, cooling optimization, precise cable management using CAD-designed pathways, and redundant network topology implementation. Integrated custom-fabricated components using mechanical engineering principles and 3D-printed mounting solutions for specialized equipment. +

+
+ Infrastructure Design + Network Engineering + CAD/3D Modeling + Linux + Mechanical Engineering + Soldering/Fabrication + Thermal Management +
+ +
+
+ +
+
+ TerraMappix Land Claim Mapping +
+
+

TerraMappix Land Claim Mapping Service

+
Inovin LLC
+
January 2023 - Present
+

+ Developed TerraTracer, a Python/Node.js geospatial engine powering TerraMappix.com. Converts BLM coordinates and legal metes-and-bounds into precise, GIS-ready property maps with accuracy down to 1.08 inches. Built for mining firms, prospectors, and land managers, featuring topo overlays and automated claim boundary validation. +

+
+ Custom GIS Solutions + Land Survey Data + BLM Integration + Geospatial Analysis + Python + Web Development +
+ +
+
+ + +
+
+ SharePoint and M365 Migrations +
+
+

SharePoint & M365 Migrations

+
Enterprise Migrations
+
March 2022 - November 2023
+

+ Seamless migration of enterprise data from legacy systems to Microsoft 365 environments. Successfully transitioned organizations from on-premises file servers, Dropbox, Google Workspace, and Egnyte to SharePoint Online and OneDrive for Business, with minimal disruption and comprehensive user adoption training. Utilized PowerShell automation for large-scale transfers. +

+
+ SharePoint Online + OneDrive + Teams + Exchange Online + PowerShell + Migration Tools + Systems Administration +
+ +
+
+ +
+
+ TerraTracer +
+
+

TerraTracer Geospatial Engine

+
Inovin LLC
+
October 2023 - Present
+

+ A Python-based geospatial mapping solution focused on lode/placer claim mapping, property delineation, and GIS integration. Leveraging powerful libraries to provide efficient mapping capabilities for mining, civil engineering, and land management applications, automating complex coordinate transformations. +

+
+ Python + GeoPandas + Matplotlib + GIS + GDAL/OGR +
+ +
+
+ + +
+
+ Full-Stack Web Development +
+
+

Full-Stack Web Development Solutions

+
LaForceIT
+
2021 - Present
+

+ End-to-end web solutions for businesses of all sizes, from responsive frontend designs to robust backend systems deployed on containerized infrastructure. Specializing in interactive dashboards, e-commerce platforms, and custom web applications with seamless API integrations. +

+
+ JavaScript/TypeScript + React/Vue/Svelte + Node.js/Python + API Integration + Docker + SQL/NoSQL +
+ +
+
+ + + +
+
+ Car Price Prediction Model +
+
+

AI-Driven Used Car Price Prediction Model

+
Western Governors University (Capstone)
+
January 2023 - March 2023
+

+ An AI-powered car price estimator combining extensive data analysis with machine learning. Features comprehensive data preprocessing, a Random Forest Regression model with over 90% accuracy, and an interactive Jupyter Notebook interface for exploration and prediction. +

+
+ Python + Machine Learning + Scikit-Learn + Pandas + Data Analysis + Jupyter +
+ +
+
+ +
+
+ ArgoBox Dashboard +
+
+

Infrastructure Monitoring Dashboard

+
Personal Project / Live Lab
+
2022 - Present
+

+ A custom, real-time monitoring solution for my home lab infrastructure. Features metrics visualization (via Grafana), service status tracking, and resource allocation management, built with web technologies and pulling data from Prometheus/other sources. +

+
+ Kubernetes (K3s) + Prometheus + Grafana + JavaScript + Docker + Ansible +
+ +
+
+ +
+
+ Ansible Automation +
+
+

Enterprise Ansible Automation

+
Infrastructure Automation Projects
+
2021 - Present
+

+ Developing and managing comprehensive Ansible implementations. Designing reusable roles, complex playbooks, and integrating with CI/CD (GitLab CI, GitHub Actions) for automated infrastructure deployment, configuration management (Linux/Windows), and application orchestration. +

+
+ Ansible + AWX/Tower + YAML + GitLab CI/GitHub Actions + Jinja2 + Infrastructure as Code +
+ +
+
+ +
+
+ CPE Vault Dev Testing Prototype +
+
+

CPE WiFi Firmware Test Bed Prototype

+
Infrastructure Engineering Project
+
October 2022 - February 2023
+

+ Designed and built a sophisticated testing environment for customer premises equipment (CPE) with full remote access capabilities for firmware development and QA testing. Includes thermal safeguards, optimized wiring structure (Visio diagrams), and robust equipment integration with WAN-facing accessibility. +

+
+ Visio/Diagramming + Hardware Integration + Network Engineering + Systems Administration + Linux + Remote Access (VPN/SSH) + Firmware Testing + Electrical Engineering +
+ +
+
+
+
+
+ + + +
+ + + + +
+
+
+

Expert Services

+

+ Comprehensive IT solutions tailored to your business needs, from network infrastructure to cloud migration. +

+
+ +
+ +
+
+ +
+

System Administration

+

+ Expert management of Windows & Linux servers, virtualization, and cloud infrastructure. Ensuring your systems remain secure, optimized and reliable. +

+
+ Windows Server + Linux + VMware + Proxmox +
+
+ + +
+
+ +
+

Network Engineering

+

+ Design and implementation of robust network infrastructures. Specializing in routing, switching, firewalls (pfSense/OPNsense), and secure network solutions (VPN, VLANs). +

+
+ pfSense + OPNsense + VPN + VLAN +
+
+ + +
+
+ +
+

Cloud & Virtualization

+

+ Implementation and management of virtualization (VMware, Proxmox) and containerization (Docker, Kubernetes/K3s) technologies. Cloud integration & migration (Azure focus). +

+
+ Docker + Kubernetes + K3s + Azure +
+
+ + +
+
+ +
+

M365 Migrations & Management

+

+ Seamless migration to Microsoft 365, tenant administration, and automation. Expertise in Exchange Online, SharePoint, Teams, OneDrive, and Azure AD integration. +

+
+ Exchange Online + SharePoint + Azure AD + Teams +
+
+ + +
+
+ +
+

IT Security

+

+ Implementing robust security measures: Zero Trust architecture (Cloudflare), firewall configuration, VPN setup, endpoint security hardening, vulnerability assessment, and security audits. +

+
+ Zero Trust + Cloudflare + Endpoint Security + Auditing +
+
+ + +
+
+ +
+

DevOps & Automation

+

+ Streamlining operations with Infrastructure as Code (Ansible), CI/CD pipelines (GitLab CI, GitHub Actions), and scripting (Python, PowerShell, Bash) for efficient workflows. +

+
+ Ansible + Python + PowerShell + Bash +
+
+ + + + + +
+
+
+ + +
+
+
+

Live Infrastructure Lab

+

+ I maintain a production-grade homelab that serves as both a testing ground and showcase for my technical skills. View real-time stats and deployed services. +

+
+ +
+
+

Lab Architecture

+

+ My lab runs on Proxmox VE hypervisors hosting VMs and LXC containers. Workloads are managed via Docker Swarm and a K3s Kubernetes cluster, exposed securely through Cloudflare Zero Trust tunnels and an OPNsense firewall. Infrastructure is managed via Ansible. +

+ +

Core Components:

+
    +
  • Proxmox VE Cluster
  • +
  • OPNsense Firewall (HA Pair)
  • +
  • K3s Kubernetes Cluster
  • +
  • Docker Swarm
  • +
  • Traefik Ingress / Load Balancer
  • +
  • Gitea, TrueNAS SCALE, Monitoring Stack
  • +
  • Cloudflare Zero Trust Tunnels
  • +
  • Ansible Automation (AWX)
  • +
+ + +
+ +
+

Real-Time Metrics

+ +
+
+ Cluster Status + ONLINE +
+
+ +
+
+ CPU Usage + --% +
+
+
+
+
+
+
+ Memory + --% +
+
+
+
+
+
+
+ Storage (ZFS) + --% +
+
+
+
+
+
+
+ K3s Nodes + -- +
+ +
+
+
+ +

Key Active Services:

+ +
+
Gitea
+
VSCode Server
+
Monitoring (Prom/Graf)
+
Synology NAS 1
+
Synology NAS 2
+
Proxmox Hypervisor
+
Argobox Node 1
+
Argobox Node 2
+
Traefik
+
K3s API
+
Cloudflared
+
FileBrowser
+
OpenWebUI/Ollama
+
Obsidian LiveSync
+
Ansible Sandbox (On Demand)
+
Plex
+
AdGuard Home
+
+
+
+ + +
+

Example Deployment (GitOps via Flux)

+

+ Services are deployed declaratively to Kubernetes using FluxCD, pulling manifests from Gitea: +

+
+ + # flux/apps/gitea/kustomization.yaml
+ apiVersion: kustomize.toolkit.fluxcd.io/v1
+ kind: Kustomization
+ metadata:
+   name: gitea
+   namespace: flux-system
+ spec:
+   interval: 10m0s
+   path: ./kubernetes/apps/default/gitea/app
+   prune: true
+   sourceRef:
+     kind: GitRepository
+     name: home-ops
+   dependsOn:
+   - name: longhorn # Ensure storage is ready
+ # --- Corresponding Deployment/Service/Ingress manifests in path --- +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resume.html b/resume.html new file mode 100644 index 0000000..0161206 --- /dev/null +++ b/resume.html @@ -0,0 +1,927 @@ + + + + + + Daniel LaForce | Enterprise IT Solutions Architect & Security Expert + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+ + + +
+ +
+
+

Daniel LaForce

+

Enterprise IT Solutions Architect & Security Expert

+
+ + +
+

Professional Experience

+ +
+
+

Freelance IT Consultant – Infrastructure, Security & GIS Solutions

+
Inovin LLC
+
April 2023 – Present | Colorado Springs, CO
+
+
+
+ + Developed TerraTracer, a GIS mapping platform used by independent mining claim prospectors to automate boundary plotting, terrain-informed site selection, and BLM/state compliance, reducing manual research by 85%. +
+
+ + Consulted on virtualization and DevOps projects for SMB clients, implementing Proxmox/VMware solutions, containerizing legacy apps with Docker, and orchestrating microservices with Kubernetes. +
+
+ + Built secure cloud environments integrating Azure AD, SSO, and VPN/firewall solutions, increasing operational resilience while aligning with HIPAA and zero-trust principles. +
+
+ + 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). +
+
+
+ +
+
+

Project Engineer

+
Anchor Point IT Solutions
+
December 2023 - June 2024
+
+
+
+ + Executed 5 Microsoft 365 and SharePoint migrations with downtime reduced by 60%. +
+
+ + Implemented comprehensive security stack including BlueMira & DUO (Identity), SentinelOne (Endpoint), ConnectSecure (Network), and Email Mesh Security. +
+
+ + Reduced security incidents by 83% through security implementations and improved system updates by 60%. +
+
+ + Managed approximately 235 clients with environment sizes ranging from 30 to 1000 people. +
+
+
+ +
+
+

Network Engineer II

+
NOVUS Professional Services, Inc.
+
August 2022 - March 2023
+
+
+
+ + Designed and implemented a WiFi testing cage for remote firmware testing of router models (Wave2, WiFi6). +
+
+ + Developed Python automation for Plume device registration and Ansible scripts for network tasks. +
+
+ + Managed RF testing and signal verification across 9 headends and maintained network connectivity for ~100 routers across two sites. +
+
+
+ +
+
+

Field Engineer

+
COMPUTEK Dental Systems
+
December 2021 - August 2022
+
+
+
+ + Managed IT infrastructure for 300+ dental practices (3700+ workstations, ~300 Windows Servers). +
+
+ + Developed VSA Deployer application (Kaseya VSA RMM) for agent installation, reducing deployment time by 80%. +
+
+ + Maintained server, Hyper-V VM, and MySQL/MSSQL backups using N-Able Backups, Windows Server Backups and AIS. +
+
+
+ +
+
+

End-to-End Systems Engineer

+
Lockheed Martin
+
June 2018 - December 2019
+
+
+
+ + Maintained 2 IRON databases (AIMS and RAPID) with ~1800 configurations each, ensuring 100% database accuracy. +
+
+ + Collaborated with engineering team to implement Remote Tracking Station (RTS) configurations. +
+
+ + Identified and documented an average of 27 critical defects every month. +
+
+
+
+ + +
+

Technical Skills

+ +
+
+

Cloud & Infrastructure

+
+ Microsoft Azure + Azure AD + SharePoint + Teams + AWS + EC2 + S3 + GCP + VMware ESXi/vSphere + Hyper-V + Docker + Kubernetes (K3s) + ProxMox + Traefik + CI/CD +
+
+ +
+

Networking

+
+ LAN/WAN + VPN + OSPF + EIGRP + STP + HSRP + VTP + IPv4/IPv6 + ACLs + NAT/PAT + DNS/DHCP + VLANs + QoS + Cisco Routers + Catalyst Switches + ASA Firewalls + UniFi Systems + pfSense + OPNsense +
+
+ +
+

Programming/Scripting

+
+ Python + JavaScript (Node.js) + PowerShell + Bash + C/C++ + SQL + MySQL/MSSQL + AutoHotkey + Ansible +
+
+ +
+

Security

+
+ Penetration Testing + BURP Professional + SentinelOne + Blackpoint + Webroot + ThreatLocker + Zero-Trust Architecture + HIPAA Compliance + BlueMira + DUO + DNSFilter + Email Mesh Security + MFA +
+
+ +
+

Operating Systems

+
+ Windows Server + Active Directory + Group Policy + Exchange Server + Linux + Ubuntu + CentOS + Debian + Fedora + Kali Linux + Cisco IOS +
+
+ +
+

GIS & Data Science

+
+ Geospatial Mapping + GeoPandas + GDAL/OGR + KML Generation + Data Visualization + Machine Learning + TensorFlow + PyTorch + Scikit-learn + Pandas + NumPy + Matplotlib + Seaborn +
+
+ +
+

Management & PSA Tools

+
+ N-able N-central + Kaseya VSA + ConnectWise Automate + ConnectWise PSA + PRTG + SolarWinds + Addigy + ImmyBot + Commit Ticket CRM +
+
+
+
+ + +
+

Education

+ +
+

Bachelor of Science, Computer Science

+
Western Governors University
+
June 2019 - April 2023
+
+
+ + Activities and societies: Sigma Alpha Pi Honors Society (The National Society of Leadership and Success) +
+
+
+ Skills: Python (Programming Language) · Project Management · Machine Learning · Team Leadership +
+
+ +
+

Cyber Ops

+
Cisco Networking Academy
+
2018
+
+ Skills: Networking · Cyber-security · Cisco Technologies +
+
+ +
+

Computer Science and Innovation

+
University of Colorado Colorado Springs (UCCS)
+
January 2017 - December 2017
+
Grade: 4.0
+
+ Skills: C++ · Team Leadership +
+
+ +
+

Associate's degree, General Studies

+
Pikes Peak State College
+
2015 - 2018
+
Grade: 4.0
+
+
+ + Activities and societies: Phi Theta Kappa Honor Society +
+
+
+ Skills: Linux · Virtual Server · C++ · System Administration · Wireless +
+
+ +
+

Associate's Degree, Computer Wide Area Networks

+
Front Range Community College
+
2014 - 2016
+
Grade: 3.87
+
+
+ + Activities and societies: Phi Theta Kappa Honor Society +
+
+ + Magna Cum Laude honors +
+
+
+ Skills: Virtual Server · C++ · Networking · Wireless +
+
+
+ + +
+

Certifications

+ +
+
+ + Project+ (CompTIA) +
+
+ + Network+ (FRCC) +
+
+ + A+ (FRCC) +
+
+ + CCNA Cyber Ops (2021) +
+
+ + CCNA Routing & Switching (2021) +
+
+ + Virtual Cyber Security Officer Training (vCSO Advanced) +
+
+
+ + +
+

Notable Projects

+ +
+
+

Enterprise Data Center Deployment

+

Complete data center build-out from ground up, including rack design, power distribution, cooling optimization, and redundant network topology implementation. Integrated custom-fabricated components using mechanical engineering principles and 3D-printed mounting solutions.

+
+ Infrastructure Design + Network Engineering + CAD/3D Modeling + Thermal Management +
+
+ +
+

TerraMappix Land Claim Mapping Service

+

Developed a Python/Node.js geospatial engine powering TerraMappix that converts BLM coordinates and legal metes-and-bounds into precise, GIS-ready property maps with accuracy down to 1.08 inches, built for mining firms and land managers.

+
+ Python + Node.js + Geospatial Analysis + GIS +
+
+ +
+

Infrastructure Monitoring Dashboard

+

A custom, real-time monitoring solution for home lab infrastructure featuring metrics visualization via Grafana, service status tracking, and resource allocation management, built with web technologies and pulling data from Prometheus.

+
+ Kubernetes (K3s) + Prometheus + Grafana + Docker +
+
+ +
+

CPE WiFi Firmware Test Bed Prototype

+

Designed and built a sophisticated testing environment for customer premises equipment (CPE) with remote access capabilities for firmware development and QA testing, including thermal safeguards and optimized wiring structure with Visio diagrams.

+
+ Network Engineering + Visio/Diagramming + Hardware Integration + Firmware Testing +
+
+
+
+ +
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 0000000..2d4efc7 --- /dev/null +++ b/script.js @@ -0,0 +1,600 @@ +/** + * Main JavaScript file for argobox.com + * Handles animations, interactions, and dynamic content + */ + +document.addEventListener('DOMContentLoaded', function() { + // Initialize all website functionality + initNavigation(); + initParticlesAndIcons(); + initRoleAnimation(); + initTerminalTyping(); + initSolutionsCarousel(); + initScrollReveal(); + updateMetrics(); + updateYear(); + + // Initialize form handling + const contactForm = document.getElementById('contact-form'); + if (contactForm) { + initFormHandling(contactForm); + } + + // Highlight navigation on scroll + setupScrollSpy(); + + // Setup role switching with improved timing + setupRoleSwitching(); + + // Other initializations... + setupFloatingIcons(); + setupParticles(); + setupRevealAnimations(); + initializeSlider(); + + // Contact form handling + setupContactForm(); + + // Mobile menu toggle + document.querySelector('.menu-toggle').addEventListener('click', function() { + document.querySelector('.nav-menu').classList.toggle('active'); + }); + + // Navbar effects on scroll + window.addEventListener('scroll', function() { + const navbar = document.querySelector('.navbar'); + + if (window.scrollY > 50) { + navbar.classList.add('scrolled'); + } else { + navbar.classList.remove('scrolled'); + } + }); +}); + +/** + * 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 + const duration = Math.random() * 20 + 10; + particle.style.animationDuration = `${duration}s`; + particle.style.animationDelay = `${Math.random() * -duration}s`; + + // Add particle animation + particle.style.animation = `float-particle ${duration}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 initRoleAnimation() { + const roles = document.querySelectorAll('.role'); + const description = document.getElementById('role-description'); + let currentIndex = 0; + + function updateRole() { + roles.forEach(role => role.classList.remove('active')); + roles[currentIndex].classList.add('active'); + description.textContent = roles[currentIndex].getAttribute('data-description'); + description.style.opacity = '0'; + setTimeout(() => { + description.style.opacity = '1'; + }, 100); + currentIndex = (currentIndex + 1) % roles.length; + } + + updateRole(); // Set initial state + setInterval(updateRole, 5000); // Switch roles every 5 seconds +} + +/** + * 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) { + // Prevent multiple initializations + if (form.hasAttribute('data-initialized')) return; + form.setAttribute('data-initialized', 'true'); + + 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; + + const notification = document.getElementById('form-notification'); + 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, + subject: form.elements['subject'].value, + message: form.elements['message'].value + }; + + try { + const res = await fetch("/api/send-email", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(formData) + }); + + const data = await res.json(); + + notification.style.display = 'flex'; + + if (res.ok) { + 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.classList.remove('success'); + notification.classList.add('error'); + notificationIcon.className = 'fas fa-exclamation-circle'; + notificationText.textContent = data.error + (data.details ? `: ${data.details}` : ''); + } + + } catch (error) { + console.error("Form submission error:", error); + notification.style.display = 'flex'; + notification.classList.remove('success'); + notification.classList.add('error'); + notificationIcon.className = 'fas fa-exclamation-circle'; + notificationText.textContent = `Error: ${error.message}`; + } finally { + submitButton.innerHTML = originalButtonText; + submitButton.disabled = false; + + // Hide notification after 5 seconds with animation + setTimeout(() => { + notification.classList.add('hiding'); + setTimeout(() => { + notification.classList.remove('hiding'); + notification.style.display = 'none'; + }, 300); + }, 5000); + } + }); +} + +/** + * Update copyright year in the footer + */ +function updateYear() { + const yearElement = document.getElementById('current-year'); + if (yearElement) { + yearElement.textContent = new Date().getFullYear(); + } +} + +/** + * Sets up scroll spy functionality to highlight nav links + * as the user scrolls through different sections + */ +function setupScrollSpy() { + const sections = document.querySelectorAll('section[id]'); + const navLinks = document.querySelectorAll('.nav-link'); + + window.addEventListener('scroll', function() { + let current = ''; + + sections.forEach(section => { + const sectionTop = section.offsetTop - 100; + const sectionHeight = section.offsetHeight; + + if (window.scrollY >= sectionTop && window.scrollY < sectionTop + sectionHeight) { + current = section.getAttribute('id'); + } + }); + + navLinks.forEach(link => { + link.classList.remove('active'); + if (link.getAttribute('href') === `#${current}`) { + link.classList.add('active'); + } + }); + }); +} + +/** + * Sets up automated role switching with better timing and transitions + */ +function setupRoleSwitching() { + const roles = document.querySelectorAll('.role'); + const roleDescription = document.getElementById('role-description'); + let currentRoleIndex = 0; + + // Initial role description + if (roles.length > 0 && roleDescription) { + roleDescription.textContent = roles[0].getAttribute('data-description'); + roles[0].classList.add('active'); + } + + // Switch roles every 5 seconds with fade transition + setInterval(() => { + // Remove active class from current role + roles[currentRoleIndex].classList.remove('active'); + + // Move to the next role or back to the first + currentRoleIndex = (currentRoleIndex + 1) % roles.length; + + // Add active class to new role + roles[currentRoleIndex].classList.add('active'); + + // Update the role description with fade effect + if (roleDescription) { + roleDescription.style.opacity = '0'; + + setTimeout(() => { + roleDescription.textContent = roles[currentRoleIndex].getAttribute('data-description'); + roleDescription.style.opacity = '1'; + }, 300); + } + }, 5000); +} + +// Replace the existing setupFloatingIcons with this improved version +function setupFloatingIcons() { + const container = document.getElementById('floating-icons'); + if (!container) return; + + // Clear existing icons + container.innerHTML = ''; + + // Tech icons to display + const icons = [ + 'fa-server', 'fa-network-wired', 'fa-shield-alt', + 'fa-cloud', 'fa-code-branch', 'fa-database', + 'fa-laptop-code', 'fa-lock', 'fa-terminal' + ]; + + // Create floating icons + icons.forEach(icon => { + const iconElement = document.createElement('div'); + iconElement.className = 'floating-icon'; + iconElement.innerHTML = ``; + + // Random starting position + const randomX = Math.floor(Math.random() * 100); + const randomY = Math.floor(Math.random() * 100); + const randomDelay = Math.random() * 5; + const randomDuration = 20 + Math.random() * 10; + + // Apply styles + iconElement.style.left = `${randomX}%`; + iconElement.style.top = `${randomY}%`; + iconElement.style.animationDelay = `${randomDelay}s`; + iconElement.style.animationDuration = `${randomDuration}s`; + + // Add to container + container.appendChild(iconElement); + }); +} + +// More existing code... \ No newline at end of file diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..227e131 --- /dev/null +++ b/styles.css @@ -0,0 +1,2066 @@ +/* +* Main stylesheet for laforceit.com +*/ + +:root { + /* Main Colors */ + --primary-bg: #0f172a; + --secondary-bg: #1e293b; + --accent: #3b82f6; + --accent-darker: #2563eb; + --accent-gradient: linear-gradient(135deg, #2563eb, #3b82f6); + --accent-glow: 0 0 15px rgba(59, 130, 246, 0.5); + + /* Text Colors */ + --text-primary: #e2e8f0; + --text-secondary: #94a3b8; + --text-accent: #3b82f6; + + /* Status Colors */ + --success: #10b981; + --warning: #f59e0b; + --error: #ef4444; + --info: #0ea5e9; + + /* UI Elements */ + --border: rgba(71, 85, 105, 0.5); + --card-bg: rgba(30, 41, 59, 0.8); + --card-hover-bg: rgba(30, 41, 59, 0.95); + --card-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3); + --glass-effect: saturate(180%) blur(10px); + + /* Animation Timing */ + --transition-fast: 0.2s ease; + --transition-normal: 0.3s ease; + --transition-slow: 0.5s ease; +} + +/* Reset & Base Styles */ +*, *::before, *::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; + scroll-padding-top: 80px; +} + +body { + font-family: 'Inter', system-ui, -apple-system, sans-serif; + background-color: var(--primary-bg); + color: var(--text-primary); + line-height: 1.6; + overflow-x: hidden; + min-height: 100vh; + background-image: + radial-gradient(circle at 20% 35%, rgba(29, 78, 216, 0.15) 0%, transparent 50%), + radial-gradient(circle at 75% 60%, rgba(14, 165, 233, 0.1) 0%, transparent 50%); +} + +/* Typography */ +h1, h2, h3, h4, h5, h6 { + font-weight: 700; + line-height: 1.2; +} + +a { + text-decoration: none; + color: var(--accent); + transition: color var(--transition-normal); +} + +a:hover { + color: var(--accent-darker); +} + +/* Layout */ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 1.5rem; +} + +section { + padding: 6rem 0; + position: relative; +} + +/* Button Styles */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0.75rem 1.5rem; + font-weight: 600; + border-radius: 0.5rem; + transition: all var(--transition-normal); + cursor: pointer; + position: relative; + overflow: hidden; + border: none; + outline: none; + gap: 0.75rem; +} + +.btn::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(255, 255, 255, 0.1); + transform: translateX(-100%) rotate(45deg); + transition: transform 0.6s; +} + +.btn:hover::before { + transform: translateX(100%) rotate(45deg); +} + +.btn-primary { + background: var(--accent-gradient); + color: white; + box-shadow: 0 4px 6px -1px rgba(59, 130, 246, 0.2), 0 2px 4px -1px rgba(59, 130, 246, 0.1); +} + +.btn-primary:hover { + box-shadow: var(--accent-glow), 0 10px 15px -3px rgba(59, 130, 246, 0.3); + transform: translateY(-2px); +} + +.btn-outline { + background-color: transparent; + border: 2px solid var(--accent); + color: var(--accent); +} + +.btn-outline:hover { + background-color: var(--accent); + color: white; + box-shadow: var(--accent-glow); + transform: translateY(-2px); +} + +.btn-icon { + font-size: 0.9rem; + transition: transform 0.3s ease; +} + +.btn:hover .btn-icon { + transform: translateX(4px); +} + +/* Featured button with pulse effect */ +.btn-featured { + position: relative; +} + +.pulse-ring { + position: absolute; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + border-radius: 0.5rem; + border: 2px solid var(--accent); + opacity: 0; + animation: pulse-border 2s infinite; +} + +@keyframes pulse-border { + 0% { + transform: scale(1); + opacity: 1; + } + 100% { + transform: scale(1.1); + opacity: 0; + } +} + +/* Navigation */ +.navbar { + position: fixed; + top: 0; + left: 0; + width: 100%; + z-index: 1000; + backdrop-filter: var(--glass-effect); + -webkit-backdrop-filter: var(--glass-effect); + background-color: rgba(15, 23, 42, 0.8); + border-bottom: 1px solid var(--border); + transition: all var(--transition-normal); +} + +.navbar.scrolled { + background-color: rgba(15, 23, 42, 0.95); +} + +.navbar .container { + display: flex; + justify-content: space-between; + align-items: center; + height: 4.5rem; +} + +.logo { + 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; + background-clip: text; + text-shadow: 0 0 8px rgba(59, 130, 246, 0.5); +} + +.logo a { + text-decoration: none; +} + +.logo-text { + margin-right: 4px; + color: inherit; /* inherit gradient */ +} + +.logo-dot { + color: var(--text-primary); +} + + +.nav-menu { + display: flex; + align-items: center; + gap: 2rem; +} + +.nav-link { + position: relative; + color: var(--text-secondary); + font-weight: 500; + padding: 0.5rem; + transition: color var(--transition-normal); +} + +.nav-link::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 0; + height: 2px; + background: var(--accent-gradient); + transition: width var(--transition-normal); + border-radius: 1px; +} + +.nav-link:hover, .nav-link.active { + color: var(--text-primary); +} + +.nav-link:hover::after, .nav-link.active::after { + width: 100%; +} + +.nav-buttons { + display: flex; + align-items: center; + gap: 1rem; +} + +.dashboard-link { + display: flex; + align-items: center; + gap: 0.5rem; + background: rgba(59, 130, 246, 0.15); + color: var(--text-primary); + padding: 0.5rem 1rem; + border-radius: 9999px; + font-weight: 500; + transition: all var(--transition-normal); +} + +.dashboard-link:hover { + background: rgba(59, 130, 246, 0.3); + color: var(--text-primary); + transform: translateY(-1px); +} + +.menu-toggle { + display: none; + background: transparent; + border: none; + color: var(--text-primary); + cursor: pointer; + font-size: 1.5rem; +} + +/* Hero Section */ +.hero { + min-height: 100vh; + display: flex; + align-items: center; + position: relative; + overflow: hidden; + padding-top: 6rem; + padding-bottom: 0; +} + +.hero-content { + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-areas: + "intro dashboard" + "cta cta"; + gap: 2.5rem; + align-items: start; +} + +/* Background particles and floating icons */ +.particles-container, .floating-icons { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; + z-index: -1; +} + +.particle { + position: absolute; + background-color: rgba(59, 130, 246, 0.2); + border-radius: 50%; + pointer-events: none; +} + +.floating-icon { + position: absolute; + color: rgba(59, 130, 246, 0.05); + animation: float-icon 15s linear infinite; + opacity: 0.3; +} + +@keyframes float-icon { + 0% { + transform: translateY(100vh) rotate(0deg); + } + 100% { + transform: translateY(-100px) rotate(360deg); + } +} + +/* Name and role section styling */ +.intro-section { + grid-area: intro; + animation: fadeIn 1s ease forwards; +} + +.name-section { + display: flex; + align-items: baseline; + margin-bottom: 0.5rem; + flex-wrap: wrap; /* Allow wrapping on small screens */ +} + +.name { + font-size: 2.5rem; + font-weight: 700; + color: var(--text-primary); + margin-right: 0.5rem; + position: relative; + display: inline-block; + background: linear-gradient(to right, #fff, #e2e8f0); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} + +.your-text { + font-size: 1.25rem; + font-weight: 400; + color: var(--text-secondary); + font-style: italic; + opacity: 0.8; +} + +.hero-title { + font-size: 3.25rem; + margin-bottom: 2.5rem; /* Increase from 1.5rem to 2.5rem for more space */ + line-height: 1.2; /* Slightly increased from 1.1 for better spacing */ + min-height: 9rem; /* Increased from 7rem to provide more space */ +} + +.role-wrapper { + position: relative; + display: block; + height: 100%; + width: 100%; +} + +.role { + position: absolute; + top: 0; + left: 0; + width: 100%; + opacity: 0; + transform: translateY(20px); + transition: opacity 0.8s ease, transform 0.8s ease; + padding-bottom: 1rem; /* Add padding at the bottom of each role */ +} + +.role.active { + opacity: 1; + transform: translateY(0); +} + +.highlight { + background: var(--accent-gradient); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + position: relative; +} + +.highlight::after { + content: ''; + position: absolute; + width: 100%; + height: 5px; + bottom: 5px; + left: 0; + background: var(--accent-gradient); + border-radius: 5px; + opacity: 0.3; + z-index: -1; +} + +.hero-description { + font-size: 1.1rem; + color: var(--text-secondary); + margin-bottom: 2rem; + max-width: 90%; + line-height: 1.6; + position: relative; + z-index: 10; + transition: opacity 0.3s ease; +} + +/* Terminal text that appears directly on the page */ +.inline-terminal { + display: block; + margin-bottom: 2rem; + font-family: 'JetBrains Mono', monospace; + font-size: 1.1rem; + color: var(--accent); + background-color: rgba(15, 23, 42, 0.2); + padding: 0.75rem 1rem; + border-radius: 0.5rem; + border-left: 3px solid var(--accent); + position: relative; + width: 100%; /* Full width */ + height: 3.5rem; /* Fixed height */ + overflow: hidden; /* Hide overflow */ +} + +.terminal-text { + display: inline-block; + position: relative; + white-space: nowrap; + width: 100%; /* Make it take up full width */ + overflow: hidden; + text-overflow: ellipsis; +} + +.terminal-text::after { + content: '|'; + position: absolute; + right: -0.5rem; + top: 0; + animation: cursor-blink 1s step-end infinite; +} + +@keyframes cursor-blink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } +} + +/* Dashboard section styling */ +.dashboard-section { + grid-area: dashboard; + position: relative; + animation: fadeIn 1s ease 0.3s forwards; + opacity: 0; +} + +.status-pill { + position: absolute; + top: -0.5rem; + right: 0.5rem; + display: flex; + align-items: center; + background: rgba(16, 185, 129, 0.15); + padding: 0.4rem 0.8rem; + border-radius: 9999px; + font-size: 0.85rem; + z-index: 10; + box-shadow: 0 0 15px rgba(16, 185, 129, 0.3); +} + +.live-indicator { + width: 10px; + height: 10px; + background-color: var(--success); + border-radius: 50%; + display: inline-block; + margin-right: 0.5rem; + position: relative; +} + +.live-indicator::after { + content: ''; + position: absolute; + top: -3px; + left: -3px; + width: 16px; + height: 16px; + border-radius: 50%; + background-color: var(--success); + animation: pulse 2s infinite; + opacity: 0.7; + z-index: -1; +} + +@keyframes pulse { + 0% { + transform: scale(1); + opacity: 0.7; + } + 70% { + transform: scale(1.5); + opacity: 0; + } + 100% { + transform: scale(1); + opacity: 0; + } +} + +.tech-dashboard { + max-width: 100%; + border-radius: 0.75rem; + overflow: hidden; + border: 1px solid var(--border); + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.3), 0 10px 10px -5px rgba(0, 0, 0, 0.1); + position: relative; + transform: perspective(1000px) rotateY(-5deg) rotateX(5deg); + transition: all 0.5s ease; + background-color: var(--card-bg); + animation: float 6s ease-in-out infinite; +} + +.tech-dashboard:hover { + transform: perspective(1000px) rotateY(0deg) rotateX(0deg); + box-shadow: 0 25px 30px -5px rgba(0, 0, 0, 0.4), 0 10px 10px -5px rgba(0, 0, 0, 0.1), 0 0 15px 5px rgba(59, 130, 246, 0.15); +} + +@keyframes float { + 0% { transform: perspective(1000px) rotateY(-5deg) rotateX(5deg) translateY(0px); } + 50% { transform: perspective(1000px) rotateY(-5deg) rotateX(5deg) translateY(-10px); } + 100% { transform: perspective(1000px) rotateY(-5deg) rotateX(5deg) translateY(0px); } +} + +.data-stream { + height: 60px; + position: relative; + overflow: hidden; + background-color: var(--secondary-bg); + background-image: + linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px), + linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px); + background-size: 20px 20px; +} + +.data-line { + position: absolute; + height: 1px; + background: linear-gradient(90deg, var(--accent), transparent); + opacity: 0.7; + top: 50%; + left: -100%; + width: 50%; + animation: streamData 3s linear infinite; +} + +.data-line:nth-child(1) { + animation-duration: 3s; + animation-delay: 0s; + width: 70%; +} +.data-line:nth-child(2) { + animation-duration: 2.5s; + animation-delay: 0.5s; + opacity: 0.5; + top: 30%; + width: 40%; +} +.data-line:nth-child(3) { + animation-duration: 2s; + animation-delay: 1s; + top: 70%; + width: 60%; +} +.data-line:nth-child(4) { + animation-duration: 4s; + animation-delay: 1.5s; + opacity: 0.6; + top: 20%; + width: 50%; +} +.data-line:nth-child(5) { + animation-duration: 3.5s; + animation-delay: 2s; + top: 80%; + width: 30%; +} + +@keyframes streamData { + 0% { left: -100%; } + 100% { left: 100%; } +} + +.monitoring-terminal { + padding: 0.75rem; + background-color: rgba(11, 17, 32, 0.98); + max-height: 200px; + overflow: hidden; + font-family: 'JetBrains Mono', monospace; + font-size: 0.8rem; + color: var(--text-secondary); + line-height: 1.6; +} + +.terminal-line { + margin-bottom: 0.4rem; + white-space: nowrap; +} + +.terminal-line:first-child { + color: var(--accent); + font-weight: 500; +} + +.success { + color: var(--success); +} + +.warning { + color: var(--warning); +} + +.info { + color: var(--info); +} + +/* Call to action buttons */ +.cta-buttons { + grid-area: cta; + display: flex; + flex-wrap: wrap; + gap: 1rem; + margin-top: 1rem; + animation: fadeIn 1s ease 0.6s forwards; + opacity: 0; +} + +/* Solutions Showcase Section */ +.solutions-showcase { + background: linear-gradient(180deg, var(--primary-bg) 0%, rgba(30, 41, 59, 0.8) 100%); + padding: 3rem 0; + margin-top: -1rem; + border-top: 1px solid rgba(59, 130, 246, 0.1); + border-bottom: 1px solid rgba(59, 130, 246, 0.1); + position: relative; +} + +.solutions-showcase::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 100px; + background: linear-gradient(to bottom, rgba(59, 130, 246, 0.05), transparent); + opacity: 0.5; +} + +.solutions-header { + text-align: center; + margin-bottom: 2rem; +} + +.solutions-title { + font-size: 1.5rem; + color: var(--text-primary); + position: relative; + display: inline-block; + margin-bottom: 0.5rem; +} + +.solutions-title::after { + content: ''; + position: absolute; + bottom: -0.5rem; + left: 10%; + width: 80%; + height: 3px; + background: var(--accent-gradient); + border-radius: 3px; +} + +.solutions-carousel { + position: relative; + height: 160px; /* Increased height to accommodate larger content */ + margin: 0 auto; + max-width: 700px; +} + +.solution-slide { + background: var(--card-bg); + border: 1px solid var(--border); + border-radius: 1rem; + padding: 1.5rem 2rem; + position: absolute; + top: 0; + left: 0; + right: 0; + opacity: 0; + transform: translateX(50px); + transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1); + display: flex; + flex-direction: column; + justify-content: center; + height: 100%; + text-align: left; /* Ensure text alignment is consistent */ +} + +.solution-slide.active { + opacity: 1; + transform: translateX(0); +} + +.solution-slide h4 { + font-size: 1.2rem; + margin-bottom: 0.5rem; + color: var(--text-primary); +} + +.solution-slide p { + color: var(--text-secondary); + font-size: 0.95rem; +} + +.solution-icon { + font-size: 1.5rem; + color: var(--accent); + margin-bottom: 0.75rem; +} + +.slider-controls { + display: flex; + justify-content: center; + flex-wrap: wrap; /* Allow dots to wrap on small screens */ + gap: 0.75rem; + margin-top: 1.5rem; + padding: 0 0.5rem; /* Add some padding for mobile */ +} + +.slider-dot { + width: 12px; + height: 12px; + border-radius: 50%; + background-color: rgba(59, 130, 246, 0.3); + border: none; + cursor: pointer; + transition: all 0.3s ease; + padding: 0; +} + +.slider-dot.active { + background-color: var(--accent); + transform: scale(1.2); +} + +/* Fade-in animation */ +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +/* Services Section */ +.services { + background-color: var(--secondary-bg); + position: relative; + isolation: isolate; +} + +.services::before { + content: ''; + position: absolute; + width: 200%; + height: 200px; + top: -100px; + left: -50%; + background-color: var(--secondary-bg); + transform: rotate(-2deg); + z-index: -1; +} + +.section-header { + text-align: center; + margin-bottom: 4rem; +} + +.section-title { + font-size: 2.5rem; + margin-bottom: 1rem; + position: relative; + display: inline-block; +} + +.section-title::after { + content: ''; + position: absolute; + bottom: -0.5rem; + left: 50%; + transform: translateX(-50%); + width: 80px; + height: 4px; + background: var(--accent-gradient); + border-radius: 2px; +} + +.section-description { + max-width: 700px; + margin: 0 auto; + color: var(--text-secondary); + font-size: 1.1rem; +} + +.services-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 2rem; +} + +.service-card { + background-color: var(--card-bg); + border-radius: 1rem; + border: 1px solid var(--border); + padding: 2rem; + transition: all var(--transition-normal); + position: relative; + overflow: hidden; + z-index: 1; +} + +.service-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 0; + background: var(--accent-gradient); + opacity: 0.1; + transition: height var(--transition-slow); + z-index: -1; +} + +.service-card:hover { + transform: translateY(-10px); + box-shadow: var(--card-shadow); + border-color: var(--accent); +} + +.service-card:hover::before { + height: 100%; +} + +.service-icon { + font-size: 2.5rem; + color: var(--accent); + margin-bottom: 1.5rem; + transition: all var(--transition-normal); +} + +.service-card:hover .service-icon { + transform: scale(1.1); + text-shadow: var(--accent-glow); +} + +.service-title { + font-size: 1.5rem; + margin-bottom: 1rem; +} + +.service-description { + color: var(--text-secondary); + margin-bottom: 1.5rem; + transition: color var(--transition-normal); +} + +.service-card:hover .service-description { + color: var(--text-primary); +} + +.tech-badges { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.tech-badge { + background-color: rgba(59, 130, 246, 0.1); + border: 1px solid rgba(59, 130, 246, 0.3); + color: var(--accent); + font-size: 0.8rem; + font-weight: 500; + padding: 0.25rem 0.75rem; + border-radius: 9999px; + transition: all var(--transition-normal); +} + +.service-card:hover .tech-badge { + background-color: rgba(59, 130, 246, 0.2); + border-color: var(--accent); +} + +/* Lab Section */ +.lab-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; + margin-bottom: 3rem; +} + +.lab-card { + background-color: var(--card-bg); + border-radius: 1rem; + border: 1px solid var(--border); + padding: 2rem; + height: 100%; + transition: all var(--transition-normal); +} + +.lab-card:hover { + transform: translateY(-5px); + box-shadow: var(--card-shadow); + border-color: var(--accent); +} + +.lab-card-title { + font-size: 1.5rem; + margin-bottom: 1rem; + color: var(--text-primary); +} + +.lab-card-description { + color: var(--text-secondary); + margin-bottom: 1.5rem; +} + +.lab-subtitle { + font-size: 1.1rem; + font-weight: 600; + color: var(--accent); + margin-bottom: 1rem; +} + +.lab-list { + margin-bottom: 1.5rem; + list-style-type: none; +} + +.lab-list li { + display: flex; + align-items: center; + margin-bottom: 0.75rem; + color: var(--text-secondary); +} + +.bullet { + display: inline-block; + width: 6px; + height: 6px; + border-radius: 50%; + background-color: var(--accent); + margin-right: 0.75rem; +} + +.lab-buttons { + display: flex; + gap: 1rem; + flex-wrap: wrap; + margin-top: 2rem; +} + +.flex-center { + display: flex; + align-items: center; + gap: 0.5rem; +} + +/* Metrics */ +.metric { + margin-bottom: 1.5rem; +} + +.metric-header { + display: flex; + justify-content: space-between; + margin-bottom: 0.5rem; +} + +.metric-name { + color: var(--text-secondary); +} + +.metric-value { + color: var(--accent); + font-weight: 500; +} + +.metric-bar { + height: 0.5rem; + background-color: rgba(59, 130, 246, 0.1); + border-radius: 9999px; + overflow: hidden; +} + +.metric-progress { + height: 100%; + background: var(--accent-gradient); + border-radius: 9999px; + position: relative; + overflow: hidden; +} + +.metric-progress::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(90deg, + transparent 0%, + rgba(255, 255, 255, 0.2) 50%, + transparent 100%); + animation: shimmer 2s infinite; +} + +@keyframes shimmer { + 0% { transform: translateX(-100%); } + 100% { transform: translateX(100%); } +} + +.services-grid-small { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); + gap: 0.75rem; +} + +.service-tag { + display: flex; + align-items: center; + gap: 0.5rem; + background-color: rgba(59, 130, 246, 0.1); + padding: 0.5rem; + border-radius: 0.5rem; + font-size: 0.85rem; +} + +.status-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background-color: var(--success); + margin-right: 0.5rem; +} + +/* Code Section */ +.code-card { + background-color: var(--card-bg); + border-radius: 1rem; + border: 1px solid var(--border); + padding: 2rem; + margin-bottom: 2rem; + transition: all var(--transition-normal); +} + +.code-card:hover { + box-shadow: var(--card-shadow); + border-color: var(--accent); +} + +.code-card-title { + font-size: 1.5rem; + margin-bottom: 1rem; +} + +.code-card-description { + color: var(--text-secondary); + margin-bottom: 1.5rem; +} + +.code-block { + background-color: rgb(15, 20, 30); + border-radius: 0.5rem; + padding: 1.5rem; + font-family: 'JetBrains Mono', monospace; + font-size: 0.85rem; + line-height: 1.6; + overflow-x: auto; +} + +.comment { color: #8b949e; } +.keyword { color: #ff7b72; } +.function { color: #d2a8ff; } +.string { color: #a5d6ff; } + +/* Projects Section */ +.projects { + background-color: var(--secondary-bg); +} + +.projects-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 2rem; +} + +.project-card { + background-color: var(--card-bg); + border-radius: 1rem; + border: 1px solid var(--border); + padding: 2rem; + transition: all var(--transition-normal); + height: 100%; +} + +.project-card:hover { + transform: translateY(-10px); + box-shadow: var(--card-shadow); + border-color: var(--accent); +} + +.project-icon { + font-size: 2.5rem; + color: var(--accent); + margin-bottom: 1.5rem; +} + +.project-title { + font-size: 1.5rem; + margin-bottom: 1rem; +} + +.project-description { + color: var(--text-secondary); + margin-bottom: 1.5rem; + flex-grow: 1; +} + +/* Experience Section */ +.timeline { + position: relative; + max-width: 800px; + margin: 0 auto; +} + +.timeline::before { + content: ''; + position: absolute; + top: 0; + bottom: 0; + width: 2px; + background-color: var(--border); + left: 25px; +} + +.timeline-item { + position: relative; + padding-left: 4rem; + margin-bottom: 3rem; +} + +.timeline-dot { + position: absolute; + left: 0; + top: 0; + width: 16px; + height: 16px; + background: var(--accent-gradient); + border-radius: 50%; + transform: translateX(-7px); + z-index: 1; +} + +.timeline-content { + background-color: var(--card-bg); + border-radius: 1rem; + border: 1px solid var(--border); + padding: 1.5rem; + transition: all var(--transition-normal); +} + +.timeline-content:hover { + transform: translateY(-5px); + box-shadow: var(--card-shadow); + border-color: var(--accent); +} + +.timeline-title { + font-size: 1.25rem; + margin-bottom: 0.5rem; +} + +.timeline-subtitle { + color: var(--accent); + font-weight: 500; + margin-bottom: 0.5rem; +} + +.timeline-date { + font-size: 0.85rem; + color: var(--text-secondary); + margin-bottom: 1rem; +} + +/* Contact Section */ +.contact-grid { + display: grid; + grid-template-columns: 1fr 2fr; + gap: 3rem; +} + +.contact-info { + display: flex; + flex-direction: column; + gap: 2rem; +} + +.contact-item { + background-color: var(--card-bg); + border-radius: 1rem; + border: 1px solid var(--border); + padding: 1.5rem; + transition: all var(--transition-normal); +} + +.contact-item:hover { + transform: translateY(-5px); + box-shadow: var(--card-shadow); + border-color: var(--accent); +} + +.contact-icon { + font-size: 2rem; + color: var(--accent); + margin-bottom: 1rem; +} + +.contact-title { + font-size: 1.2rem; + margin-bottom: 0.5rem; +} + +.contact-form-container { + background-color: var(--card-bg); + border-radius: 1rem; + border: 1px solid var(--border); + padding: 2rem; + transition: all var(--transition-normal); +} + +.contact-form-container:hover { + box-shadow: var(--card-shadow); + border-color: var(--accent); +} + +.contact-form { + display: grid; + gap: 1.5rem; +} + +.form-group { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.form-group label { + font-weight: 500; + color: var(--text-primary); +} + +.form-group input, +.form-group textarea { + padding: 0.75rem; + background-color: rgba(15, 23, 42, 0.6); + border: 1px solid var(--border); + border-radius: 0.5rem; + color: var(--text-primary); + font-family: 'Inter', sans-serif; + transition: all var(--transition-normal); +} + +.form-group input:focus, +.form-group textarea:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3); +} + +/* Footer styles */ +.footer { + padding: 2rem 0; + background: transparent; + border-top: 1px solid var(--border); + margin-top: 4rem; +} + +.footer .container { + display: flex; + flex-direction: column; + align-items: center; +} + +.footer-content { + display: flex; + width: 100%; + align-items: center; + margin-bottom: 1.5rem; + position: relative; +} + +.footer-logo { + position: absolute; + left: 0; + font-weight: 700; + font-size: 1.5rem; +} + +.footer-links { + display: flex; + gap: 1.5rem; + margin: 0 auto; +} + +.footer-links a { + color: var(--text-secondary); + text-decoration: none; + transition: color 0.3s; +} + +.footer-links a:hover { + color: var(--accent); +} + +.footer-bottom { + text-align: center; + color: var(--text-secondary); + font-size: 0.9rem; + width: 100%; +} + +.footer-bottom .disclaimer { + margin-top: 0.25rem; +} + +/* Responsive behavior */ +@media (max-width: 768px) { + .footer-content { + flex-direction: column; + padding-top: 2rem; + gap: 1.5rem; + } + + .footer-logo { + position: static; + margin-bottom: 1rem; + } + + .footer-links { + margin: 0; + } +} + +/* Reveal animations on scroll */ +.reveal { + opacity: 0; + transform: translateY(30px); + transition: all 0.8s ease; +} + +.reveal.active { + opacity: 1; + transform: translateY(0); +} + +/* Responsive Styles */ +@media (max-width: 1024px) { + .hero-content { + grid-template-columns: 1fr; + grid-template-areas: + "intro" + "dashboard" + "cta"; + } + + .hero-title { + font-size: 2.75rem; + min-height: auto; /* Let it adjust naturally on smaller screens */ + margin-bottom: 2rem; + } + + .role { + position: relative; + } + + .solutions-carousel { + height: 180px; /* More space on tablets */ + } +} + +@media (max-width: 768px) { + .hero-title { + font-size: 2.3rem; + } + + .name { + font-size: 2rem; + } + + .solutions-carousel { + height: 200px; /* More space on mobile */ + } + + .solution-slide { + padding: 1.25rem; /* Less padding on mobile */ + } +} + +@media (max-width: 480px) { + .hero-title { + font-size: 1.9rem; /* Smaller on phones */ + } + + .hero-description { + font-size: 1rem; + } + + .name { + font-size: 1.7rem; + } + + .your-text { + font-size: 1rem; + } + + .terminal-text { + font-size: 0.9rem; /* Smaller terminal text on phones */ + } + + .solutions-carousel { + height: 220px; /* Even more space on small phones */ + } +} + +/* Accessibility Enhancements */ +@media (prefers-reduced-motion: reduce) { + * { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} + +/* Print styles for resume/CV page */ +@media print { + .navbar, .footer, .cta-buttons { + display: none !important; + } + + body { + background-color: white; + color: black; + } + + .container { + width: 100%; + max-width: none; + } + + a { + color: black; + text-decoration: none; + } + + .section { + break-inside: avoid; + page-break-inside: avoid; + } +} + +/* ============================ + LaForceIT.com Logo Styling + ============================ */ + +.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; +} + +.logo-dot-glow { +color: white; +font-weight: 600; +font-size: 1.6rem; +margin-left: 2px; +text-shadow: 0 0 3px rgba(255, 255, 255, 0.3); +transition: all 0.3s ease-in-out; +} + +.logo-text-glow:hover, +.logo-dot-glow:hover { +text-shadow: 0 0 10px rgba(59, 130, 246, 0.8), 0 0 8px rgba(255, 255, 255, 0.3); +transform: scale(1.02); +} + +.form-notification { + margin-top: 1rem; + padding: 1rem 1.5rem; + border-radius: 0.5rem; + display: flex; + align-items: center; + gap: 0.75rem; + animation: slideIn 0.3s ease-out; + font-weight: 500; +} + +.form-notification.success { + background-color: rgba(16, 185, 129, 0.1); + color: #10b981; + border: 1px solid #10b981; +} + +.form-notification.error { + background-color: rgba(239, 68, 68, 0.1); + color: #ef4444; + border: 1px solid #ef4444; +} + +.form-notification i { + font-size: 1.25rem; +} + +@keyframes slideIn { + from { + transform: translateY(-10px); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } +} + +/* Bubble Animation */ +.bubbles { + position: fixed; + width: 100%; + height: 100%; + top: 0; + left: 0; + pointer-events: none; + z-index: -1; + overflow: hidden; +} + +.bubble { + position: absolute; + background: radial-gradient(circle at center, rgba(59, 130, 246, 0.1) 0%, rgba(37, 99, 235, 0.05) 100%); + border-radius: 50%; + animation: float-bubble 8s infinite ease-in-out; + opacity: 0; +} + +.bubble:nth-child(1) { + width: 80px; + height: 80px; + left: 10%; + animation-delay: 0s; +} + +.bubble:nth-child(2) { + width: 120px; + height: 120px; + left: 25%; + animation-delay: 2s; +} + +.bubble:nth-child(3) { + width: 100px; + height: 100px; + left: 40%; + animation-delay: 4s; +} + +.bubble:nth-child(4) { + width: 150px; + height: 150px; + left: 65%; + animation-delay: 6s; +} + +.bubble:nth-child(5) { + width: 90px; + height: 90px; + left: 80%; + animation-delay: 8s; +} + +@keyframes float-bubble { + 0% { + transform: translateY(100vh) scale(0); + opacity: 0; + } + 20% { + opacity: 0.5; + } + 50% { + opacity: 0.3; + } + 80% { + opacity: 0.5; + } + 100% { + transform: translateY(-100vh) scale(1.2); + opacity: 0; + } +} + +/* Add this to both index.html and resume.html body */ +body::before { + content: ''; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: radial-gradient(circle at 20% 35%, rgba(29, 78, 216, 0.15) 0%, transparent 50%), + radial-gradient(circle at 75% 60%, rgba(14, 165, 233, 0.1) 0%, transparent 50%); + pointer-events: none; + z-index: -2; +} + +/* Slogan Styles */ +.slogan-wrapper { + text-align: center; + margin: 1.5rem 0; + animation: fadeIn 1s ease-out; +} + +.slogan { + font-size: 2.5rem; + font-weight: 700; + background: linear-gradient(135deg, #3b82f6, #2563eb); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + margin-bottom: 0.5rem; +} + +.slogan-sub { + font-size: 1.25rem; + color: var(--text-secondary); + font-weight: 500; +} + +/* CTA Section Styles */ +.cta-section { + background: linear-gradient(135deg, rgba(59, 130, 246, 0.1), rgba(37, 99, 235, 0.2)); + padding: 6rem 0; + position: relative; + overflow: hidden; +} + +.cta-grid { + display: flex; + justify-content: center; + align-items: center; + gap: 4rem; +} + +.cta-content { + max-width: 800px; + text-align: center; +} + +.cta-title { + font-size: 2.5rem; + margin-bottom: 1.5rem; + background: linear-gradient(135deg, #3b82f6, #2563eb); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} + +.cta-description { + font-size: 1.25rem; + color: var(--text-secondary); + margin-bottom: 3rem; + max-width: 600px; + margin-left: auto; + margin-right: auto; +} + +.cta-stats { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 2rem; + margin-bottom: 3rem; +} + +.cta-stat { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; +} + +.stat-number { + font-size: 2.5rem; + font-weight: 700; + color: var(--accent); +} + +.stat-label { + font-size: 1rem; + color: var(--text-secondary); + text-align: center; +} + +.cta-features { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 2rem; + margin-bottom: 3rem; +} + +.cta-feature { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + padding: 1.5rem; + background: rgba(59, 130, 246, 0.1); + border-radius: 1rem; + border: 1px solid rgba(59, 130, 246, 0.2); + transition: all var(--transition-normal); +} + +.cta-feature:hover { + transform: translateY(-5px); + background: rgba(59, 130, 246, 0.15); + border-color: rgba(59, 130, 246, 0.3); +} + +.cta-feature i { + font-size: 2rem; + color: var(--accent); +} + +.cta-feature span { + font-weight: 500; + color: var(--text-primary); + text-align: center; +} + +.cta-buttons { + display: flex; + justify-content: center; + gap: 1rem; +} + +@media (max-width: 768px) { + .slogan { + font-size: 2rem; + } + + .slogan-sub { + font-size: 1.1rem; + } + + .cta-stats, .cta-features { + grid-template-columns: 1fr; + } + + .cta-buttons { + flex-direction: column; + gap: 1rem; + } + + .cta-title { + font-size: 2rem; + } + + .cta-description { + font-size: 1.1rem; + } +} + +/* Particles */ +.particles-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; + pointer-events: none; + z-index: 0; +} + +.particle { + position: absolute; + background-color: rgba(59, 130, 246, 0.2); + border-radius: 50%; + pointer-events: none; +} + +@keyframes float-particle { + 0% { + transform: translate(0, 0); + } + 25% { + transform: translate(50px, -50px); + } + 50% { + transform: translate(100px, 0); + } + 75% { + transform: translate(50px, 50px); + } + 100% { + transform: translate(0, 0); + } +} + +/* Highlight navigation links on scroll */ +.nav-link.active { + color: var(--accent); + font-weight: 600; +} + +.nav-link.active::after { + width: 100%; + opacity: 1; +} + +/* Terminal Card Styles for Lab Section */ +.terminal-card { + background-color: rgba(15, 23, 42, 0.95); + border: 1px solid var(--border); + border-radius: 8px; + overflow: hidden; + margin-bottom: 1.5rem; + box-shadow: var(--card-shadow); + transition: all var(--transition-normal); +} + +.terminal-card:hover { + transform: translateY(-5px); + box-shadow: 0 15px 30px -10px rgba(0, 0, 0, 0.4); +} + +.terminal-card-header { + background-color: rgba(30, 41, 59, 0.95); + padding: 0.75rem 1rem; + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid var(--border); +} + +.terminal-title { + font-family: 'JetBrains Mono', monospace; + font-size: 0.85rem; + font-weight: 500; + color: var(--text-primary); +} + +.terminal-status { + font-family: 'JetBrains Mono', monospace; + font-size: 0.75rem; + font-weight: 700; + padding: 0.25rem 0.5rem; + border-radius: 4px; + background-color: rgba(15, 23, 42, 0.7); +} + +.terminal-status.online { + color: var(--success); +} + +.terminal-card-content { + padding: 1rem; +} + +/* Service tags with active indicators */ +.status-dot.active { + background-color: var(--success); + box-shadow: 0 0 5px var(--success); + animation: pulse 2s infinite; +} + +/* Service tags with error indicators */ +.status-dot.error { + background-color: var(--error); + box-shadow: 0 0 5px var(--error); +} + +/* Project Cards with Images */ +.project-card { + display: flex; + flex-direction: column; + background-color: var(--card-bg); + border-radius: 12px; + overflow: hidden; + transition: all var(--transition-normal); + border: 1px solid var(--border); + box-shadow: var(--card-shadow); +} + +.project-card:hover { + transform: translateY(-5px); + box-shadow: 0 15px 30px -10px rgba(0, 0, 0, 0.4); + border-color: var(--accent); +} + +.project-image { + position: relative; + height: 180px; + overflow: hidden; + background-color: rgba(15, 23, 42, 0.8); +} + +.project-thumbnail { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.5s ease; + opacity: 0.7; +} + +.project-card:hover .project-thumbnail { + transform: scale(1.05); + opacity: 0.9; +} + +.project-icon { + position: absolute; + bottom: -25px; + right: 20px; + width: 50px; + height: 50px; + border-radius: 50%; + background: var(--accent-gradient); + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: 1.5rem; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + border: 3px solid var(--primary-bg); + z-index: 1; +} + +.project-content { + padding: 1.5rem; + padding-top: 1rem; + flex: 1; + display: flex; + flex-direction: column; +} + +.project-links { + margin-top: auto; + padding-top: 1rem; + display: flex; + gap: 1rem; +} + +.project-link { + font-size: 0.85rem; + font-weight: 500; + color: var(--accent); + transition: all var(--transition-normal); + display: inline-flex; + align-items: center; + gap: 0.5rem; +} + +.project-link:hover { + color: var(--accent-darker); + text-decoration: underline; +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .project-card { + flex-direction: column; + } + + .project-image { + height: 160px; + } +} + +.btn-offline { + border-color: var(--error); + color: var(--error); + cursor: not-allowed; + opacity: 0.8; +} + +.btn-offline:hover { + background-color: rgba(239, 68, 68, 0.1); + color: var(--error); + box-shadow: 0 0 10px rgba(239, 68, 68, 0.3); + transform: translateY(-2px); +} + +.offline-indicator { + width: 10px; + height: 10px; + background-color: var(--error); + border-radius: 50%; + display: inline-block; + margin-right: 0.5rem; +} + +