File size: 9,861 Bytes
1652593
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2917b05
 
 
1652593
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2917b05
 
 
 
 
 
 
 
 
 
1652593
 
 
2917b05
 
1652593
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2917b05
 
 
1652593
 
 
2917b05
 
 
 
1652593
 
 
 
 
2917b05
 
 
 
 
 
 
 
 
 
 
 
1652593
 
 
2917b05
 
1652593
 
 
 
 
 
 
2917b05
 
 
 
1652593
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
/**
 * 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 = '<div class="col-span-full flex justify-center py-10"><div class="spinner"></div></div>';
        
        setTimeout(() => {
            grid.innerHTML = servicesData.map(service => `
                <div class="bg-white rounded-xl shadow-lg overflow-hidden hover:shadow-2xl transition duration-300 group flex flex-col">
                    <div class="h-48 overflow-hidden relative">
                        <img src="${service.image}" alt="${service.title}" class="w-full h-full object-cover group-hover:scale-110 transition duration-500">
                        <div class="absolute inset-0 bg-black/20 group-hover:bg-black/10 transition"></div>
                    </div>
                    <div class="p-8 flex-grow flex flex-col">
                        <div class="w-12 h-12 bg-primary-100 rounded-lg flex items-center justify-center text-primary-600 mb-4">
                            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                ${this.getIconPath(service.icon)}
                            </svg>
                        </div>
                        <h4 class="text-xl font-bold mb-2">${service.title}</h4>
                        <p class="text-gray-600 mb-4 flex-grow">${service.desc}</p>
                        <button onclick="window.router.navigate('contact')" class="text-secondary-600 font-bold hover:text-secondary-800 inline-flex items-center self-start cursor-pointer bg-transparent border-0 p-0">
                            Request Service 
                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="ml-1"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>
                        </button>
                    </div>
                </div>
            `).join('');
        }, 600);
    }
    
    getIconPath(iconName) {
        const icons = {
            'log-out': '<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/>',
            'disc': '<circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/>',
            'scissors': '<circle cx="6" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><line x1="20" y1="4" x2="8.12" y2="15.88"/><line x1="14.47" y1="14.48" x2="20" y2="20"/><line x1="8.12" y1="8.12" x2="12" y2="12"/>',
            'alert-triangle': '<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/>',
            'map': '<polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"/><line x1="8" y1="2" x2="8" y2="18"/><line x1="16" y1="6" x2="16" y2="22"/>',
            'anchor': '<circle cx="12" cy="5" r="3"/><line x1="12" y1="22" x2="12" y2="8"/><path d="M5 12H2a10 10 0 0 0 20 0h-3"/>'
        };
        return icons[iconName] || icons['log-out'];
    }

    loadProjects() {
        const grid = document.getElementById('gallery-grid');
        if (!grid) return;
        
        if (grid.children.length > 0) return;

        const html = projectsData.map(proj => `
            <div class="relative group overflow-hidden rounded-lg cursor-pointer aspect-[4/3]">
                <img src="${proj.img}" alt="${proj.title}" class="w-full h-full object-cover transition duration-500 group-hover:scale-110">
                <div class="absolute inset-0 bg-gradient-to-t from-black/80 to-transparent opacity-0 group-hover:opacity-100 transition duration-300 flex flex-col justify-end p-6">
                    <h4 class="text-white font-bold text-xl">${proj.title}</h4>
                    <p class="text-gray-300 text-sm flex items-center">
                        <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-1"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>
                        ${proj.location}
                    </p>
                </div>
            </div>
        `).join('');
        grid.innerHTML = html;
    }
}
// Initialize Router when DOM is ready
let router;
document.addEventListener('DOMContentLoaded', () => {
    router = new Router();
    // Expose to window for onclick handlers
    window.router = router;
});