/** * Main Application Logic * Handles routing, data fetching, and component interaction. */ // Data Mockups (Simulating Public API response) const servicesData = [ { id: 1, title: "Tree Removal", desc: "Complete removal of hazardous or unwanted trees using advanced rigging techniques to protect your property.", icon: "log-out", image: "http://static.photos/nature/640x360/1" }, { id: 2, title: "Stump Grinding", desc: "Eliminate tripping hazards and improve curb appeal by grinding stumps below ground level.", icon: "disc", image: "http://static.photos/nature/640x360/2" }, { id: 3, title: "Tree Pruning", desc: "Selective branch removal to improve structure, health, and aesthetics of your trees.", icon: "scissors", image: "http://static.photos/nature/640x360/3" }, { id: 4, title: "Emergency Storm Care", desc: "24/7 rapid response for fallen trees or branches threatening your home or power lines.", icon: "alert-triangle", image: "http://static.photos/nature/640x360/4" }, { id: 5, title: "Land Clearing", desc: "Preparing lots for construction or renovation by removing vegetation efficiently.", icon: "map", image: "http://static.photos/nature/640x360/5" }, { id: 6, title: "Cabling & Bracing", desc: "Installing support systems to preserve structurally weak trees and extend their lifespan.", icon: "anchor", image: "http://static.photos/nature/640x360/6" } ]; const projectsData = [ { title: "Oak Tree Removal", location: "Downtown", img: "http://static.photos/nature/640x360/10" }, { title: "Storm Damage Cleanup", location: "Westside", img: "http://static.photos/nature/640x360/11" }, { title: "Stump Grinding Project", location: "Hillside", img: "http://static.photos/nature/640x360/12" }, { title: "Palm Tree Trimming", location: "Beach Blvd", img: "http://static.photos/nature/640x360/13" }, { title: "Hazardous Limb Removal", location: "Suburbia", img: "http://static.photos/nature/640x360/14" }, { title: "Commercial Clearing", location: "Industrial Park", img: "http://static.photos/nature/640x360/15" }, ]; class Router { constructor() { this.routes = ['home', 'services', 'projects', 'contact']; this.init(); } init() { // Handle initial load based on hash or default to home const hash = window.location.hash.replace('#', '') || 'home'; this.navigate(hash); // Handle browser back/forward buttons window.addEventListener('popstate', (event) => { if(event.state && event.state.page) { this.renderPage(event.state.page); } }); } navigate(pageId) { // Update URL hash without reload if (window.location.hash !== `#${pageId}`) { history.pushState({ page: pageId }, null, `#${pageId}`); } this.renderPage(pageId); } renderPage(pageId) { // Hide all sections document.querySelectorAll('.page-section').forEach(section => { section.classList.remove('active'); }); // Show target section const target = document.getElementById(pageId); if (target) { target.classList.add('active'); window.scrollTo(0, 0); } else { // Fallback to home document.getElementById('home').classList.add('active'); } // Update Nav Active State (works inside Shadow DOM via querySelector logic below) this.updateNavState(pageId); // Load specific page data if needed if (pageId === 'services') this.loadServices(); if (pageId === 'projects') this.loadProjects(); // Close mobile menu if open this.closeMobileMenu(); } updateNavState(activeId) { // Since header is shadow DOM, we need to access it specifically const header = document.querySelector('custom-header'); if (header && header.shadowRoot) { const links = header.shadowRoot.querySelectorAll('.nav-link'); links.forEach(link => { if (link.getAttribute('data-target') === activeId) { link.classList.add('active'); } else { link.classList.remove('active'); } }); } } closeMobileMenu() { const header = document.querySelector('custom-header'); if (header && header.shadowRoot) { const mobileMenu = header.shadowRoot.querySelector('.mobile-menu'); if (mobileMenu) { mobileMenu.classList.remove('open'); } } } async loadServices() { const grid = document.getElementById('services-grid'); if (!grid) return; // Check if already loaded to prevent re-render if (grid.children.length > 1 && !grid.querySelector('.spinner')) return; // Simulate API delay grid.innerHTML = '
${service.desc}
${proj.location}