Built out the Services, Projects and Contact Page. Ensure that the links in the footer work and are properly routed.
Browse files- index.html +174 -44
- script.js +40 -9
- style.css +20 -2
index.html
CHANGED
|
@@ -112,14 +112,14 @@
|
|
| 112 |
<h1 class="text-5xl md:text-7xl font-extrabold mb-6 leading-tight">Safe & Professional Tree Removal</h1>
|
| 113 |
<p class="text-xl md:text-2xl mb-8 text-gray-200">Expert care for your property. Precision cutting, stump grinding, and 24/7 emergency storm services.</p>
|
| 114 |
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
| 115 |
-
<a href="
|
| 116 |
Get Free Estimate
|
| 117 |
</a>
|
| 118 |
-
<a href="
|
| 119 |
Our Services
|
| 120 |
</a>
|
| 121 |
</div>
|
| 122 |
-
|
| 123 |
</div>
|
| 124 |
|
| 125 |
<!-- Trust Indicators -->
|
|
@@ -168,8 +168,8 @@
|
|
| 168 |
</div>
|
| 169 |
<h4 class="text-xl font-bold mb-2">Tree Removal</h4>
|
| 170 |
<p class="text-gray-600 mb-4">Safe, strategic removal of dangerous or unwanted trees with minimal impact on your landscape.</p>
|
| 171 |
-
<a href="
|
| 172 |
-
|
| 173 |
</div>
|
| 174 |
<!-- Service Card 2 -->
|
| 175 |
<div class="bg-white rounded-xl shadow-lg overflow-hidden hover:shadow-2xl transition duration-300 group">
|
|
@@ -182,8 +182,8 @@
|
|
| 182 |
</div>
|
| 183 |
<h4 class="text-xl font-bold mb-2">Stump Grinding</h4>
|
| 184 |
<p class="text-gray-600 mb-4">Professional grinding equipment to remove unsightly stumps and prevent regrowth or pest issues.</p>
|
| 185 |
-
<a href="
|
| 186 |
-
|
| 187 |
</div>
|
| 188 |
<!-- Service Card 3 -->
|
| 189 |
<div class="bg-white rounded-xl shadow-lg overflow-hidden hover:shadow-2xl transition duration-300 group">
|
|
@@ -196,8 +196,8 @@
|
|
| 196 |
</div>
|
| 197 |
<h4 class="text-xl font-bold mb-2">Trimming & Pruning</h4>
|
| 198 |
<p class="text-gray-600 mb-4">Expert cutting to improve tree health, aesthetics, and safety around power lines and structures.</p>
|
| 199 |
-
<a href="
|
| 200 |
-
|
| 201 |
</div>
|
| 202 |
</div>
|
| 203 |
</div>
|
|
@@ -320,7 +320,6 @@
|
|
| 320 |
|
| 321 |
<!-- Footer Component -->
|
| 322 |
<custom-footer></custom-footer>
|
| 323 |
-
|
| 324 |
<!-- Web Components Scripts -->
|
| 325 |
<script>
|
| 326 |
// HEADER COMPONENT
|
|
@@ -354,6 +353,7 @@
|
|
| 354 |
display: flex;
|
| 355 |
align-items: center;
|
| 356 |
gap: 0.5rem;
|
|
|
|
| 357 |
}
|
| 358 |
.nav-desktop {
|
| 359 |
display: none;
|
|
@@ -383,6 +383,7 @@
|
|
| 383 |
font-weight: 600;
|
| 384 |
text-decoration: none;
|
| 385 |
transition: background-color 0.2s;
|
|
|
|
| 386 |
}
|
| 387 |
.btn-cta:hover {
|
| 388 |
background-color: #b45309;
|
|
@@ -420,39 +421,92 @@
|
|
| 420 |
text-decoration: none;
|
| 421 |
font-weight: 600;
|
| 422 |
border-bottom: 1px solid #f1f5f9;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 423 |
}
|
| 424 |
</style>
|
| 425 |
<header>
|
| 426 |
<div class="container">
|
| 427 |
-
<
|
| 428 |
-
<
|
| 429 |
-
|
|
|
|
| 430 |
|
| 431 |
<nav class="nav-desktop">
|
| 432 |
-
<
|
| 433 |
-
<
|
| 434 |
-
<
|
| 435 |
-
<
|
| 436 |
-
<
|
| 437 |
</nav>
|
| 438 |
|
| 439 |
-
<button class="mobile-menu-btn"
|
| 440 |
-
<
|
| 441 |
</button>
|
| 442 |
</div>
|
| 443 |
<div class="mobile-menu">
|
| 444 |
-
<
|
| 445 |
-
<
|
| 446 |
-
<
|
| 447 |
-
<
|
| 448 |
</div>
|
| 449 |
</header>
|
| 450 |
`;
|
| 451 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 452 |
}
|
| 453 |
}
|
| 454 |
customElements.define('custom-header', CustomHeader);
|
| 455 |
-
|
| 456 |
// FOOTER COMPONENT
|
| 457 |
class CustomFooter extends HTMLElement {
|
| 458 |
connectedCallback() {
|
|
@@ -491,14 +545,27 @@
|
|
| 491 |
.footer-links li {
|
| 492 |
margin-bottom: 0.75rem;
|
| 493 |
}
|
| 494 |
-
.footer-
|
| 495 |
color: #94a3b8;
|
| 496 |
-
|
| 497 |
transition: color 0.2s;
|
|
|
|
| 498 |
}
|
| 499 |
-
.footer-
|
| 500 |
color: white;
|
| 501 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 502 |
.copyright {
|
| 503 |
text-align: center;
|
| 504 |
border-top: 1px solid #334155;
|
|
@@ -506,49 +573,112 @@
|
|
| 506 |
padding-top: 2rem;
|
| 507 |
font-size: 0.875rem;
|
| 508 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 509 |
</style>
|
| 510 |
<footer>
|
| 511 |
<div class="footer-grid">
|
| 512 |
<div class="footer-col">
|
| 513 |
<h3>TimberTitan</h3>
|
| 514 |
<p>Professional tree care services dedicated to safety, quality, and customer satisfaction. Serving the community since 1995.</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 515 |
</div>
|
| 516 |
<div class="footer-col">
|
| 517 |
<h3>Services</h3>
|
| 518 |
<ul class="footer-links">
|
| 519 |
-
<li><
|
| 520 |
-
<li><
|
| 521 |
-
<li><
|
| 522 |
-
<li><
|
|
|
|
|
|
|
| 523 |
</ul>
|
| 524 |
</div>
|
| 525 |
<div class="footer-col">
|
| 526 |
<h3>Quick Links</h3>
|
| 527 |
<ul class="footer-links">
|
| 528 |
-
<li><
|
| 529 |
-
<li><
|
| 530 |
-
<li><
|
| 531 |
-
<li><
|
|
|
|
| 532 |
</ul>
|
| 533 |
</div>
|
| 534 |
<div class="footer-col">
|
| 535 |
-
<h3>Contact</h3>
|
| 536 |
-
<ul class="
|
| 537 |
-
<li>
|
| 538 |
-
|
| 539 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 540 |
</ul>
|
| 541 |
</div>
|
| 542 |
</div>
|
| 543 |
<div class="copyright">
|
| 544 |
-
<p>© 2023 TimberTitan Services. All rights reserved.</p>
|
| 545 |
</div>
|
| 546 |
</footer>
|
| 547 |
`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 548 |
}
|
| 549 |
}
|
| 550 |
customElements.define('custom-footer', CustomFooter);
|
| 551 |
-
|
| 552 |
|
| 553 |
<!-- Main Logic Script -->
|
| 554 |
<script src="script.js"></script>
|
|
|
|
| 112 |
<h1 class="text-5xl md:text-7xl font-extrabold mb-6 leading-tight">Safe & Professional Tree Removal</h1>
|
| 113 |
<p class="text-xl md:text-2xl mb-8 text-gray-200">Expert care for your property. Precision cutting, stump grinding, and 24/7 emergency storm services.</p>
|
| 114 |
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
| 115 |
+
<a href="javascript:void(0)" onclick="window.router.navigate('contact')" class="bg-secondary-600 hover:bg-secondary-700 text-white font-bold py-4 px-8 rounded-lg shadow-lg transition transform hover:-translate-y-1 cursor-pointer">
|
| 116 |
Get Free Estimate
|
| 117 |
</a>
|
| 118 |
+
<a href="javascript:void(0)" onclick="window.router.navigate('services')" class="bg-white/10 hover:bg-white/20 backdrop-blur-md text-white font-bold py-4 px-8 rounded-lg border border-white/30 transition cursor-pointer">
|
| 119 |
Our Services
|
| 120 |
</a>
|
| 121 |
</div>
|
| 122 |
+
</div>
|
| 123 |
</div>
|
| 124 |
|
| 125 |
<!-- Trust Indicators -->
|
|
|
|
| 168 |
</div>
|
| 169 |
<h4 class="text-xl font-bold mb-2">Tree Removal</h4>
|
| 170 |
<p class="text-gray-600 mb-4">Safe, strategic removal of dangerous or unwanted trees with minimal impact on your landscape.</p>
|
| 171 |
+
<a href="javascript:void(0)" onclick="window.router.navigate('services')" class="text-secondary-600 font-bold hover:text-secondary-800 inline-flex items-center cursor-pointer">Learn More <i data-feather="arrow-right" class="w-4 h-4 ml-1"></i></a>
|
| 172 |
+
</div>
|
| 173 |
</div>
|
| 174 |
<!-- Service Card 2 -->
|
| 175 |
<div class="bg-white rounded-xl shadow-lg overflow-hidden hover:shadow-2xl transition duration-300 group">
|
|
|
|
| 182 |
</div>
|
| 183 |
<h4 class="text-xl font-bold mb-2">Stump Grinding</h4>
|
| 184 |
<p class="text-gray-600 mb-4">Professional grinding equipment to remove unsightly stumps and prevent regrowth or pest issues.</p>
|
| 185 |
+
<a href="javascript:void(0)" onclick="window.router.navigate('services')" class="text-secondary-600 font-bold hover:text-secondary-800 inline-flex items-center cursor-pointer">Learn More <i data-feather="arrow-right" class="w-4 h-4 ml-1"></i></a>
|
| 186 |
+
</div>
|
| 187 |
</div>
|
| 188 |
<!-- Service Card 3 -->
|
| 189 |
<div class="bg-white rounded-xl shadow-lg overflow-hidden hover:shadow-2xl transition duration-300 group">
|
|
|
|
| 196 |
</div>
|
| 197 |
<h4 class="text-xl font-bold mb-2">Trimming & Pruning</h4>
|
| 198 |
<p class="text-gray-600 mb-4">Expert cutting to improve tree health, aesthetics, and safety around power lines and structures.</p>
|
| 199 |
+
<a href="javascript:void(0)" onclick="window.router.navigate('services')" class="text-secondary-600 font-bold hover:text-secondary-800 inline-flex items-center cursor-pointer">Learn More <i data-feather="arrow-right" class="w-4 h-4 ml-1"></i></a>
|
| 200 |
+
</div>
|
| 201 |
</div>
|
| 202 |
</div>
|
| 203 |
</div>
|
|
|
|
| 320 |
|
| 321 |
<!-- Footer Component -->
|
| 322 |
<custom-footer></custom-footer>
|
|
|
|
| 323 |
<!-- Web Components Scripts -->
|
| 324 |
<script>
|
| 325 |
// HEADER COMPONENT
|
|
|
|
| 353 |
display: flex;
|
| 354 |
align-items: center;
|
| 355 |
gap: 0.5rem;
|
| 356 |
+
cursor: pointer;
|
| 357 |
}
|
| 358 |
.nav-desktop {
|
| 359 |
display: none;
|
|
|
|
| 383 |
font-weight: 600;
|
| 384 |
text-decoration: none;
|
| 385 |
transition: background-color 0.2s;
|
| 386 |
+
cursor: pointer;
|
| 387 |
}
|
| 388 |
.btn-cta:hover {
|
| 389 |
background-color: #b45309;
|
|
|
|
| 421 |
text-decoration: none;
|
| 422 |
font-weight: 600;
|
| 423 |
border-bottom: 1px solid #f1f5f9;
|
| 424 |
+
cursor: pointer;
|
| 425 |
+
}
|
| 426 |
+
.mobile-link:hover {
|
| 427 |
+
color: #059669;
|
| 428 |
}
|
| 429 |
</style>
|
| 430 |
<header>
|
| 431 |
<div class="container">
|
| 432 |
+
<div class="logo" data-navigate="home">
|
| 433 |
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#059669" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg>
|
| 434 |
+
TimberTitan
|
| 435 |
+
</div>
|
| 436 |
|
| 437 |
<nav class="nav-desktop">
|
| 438 |
+
<span class="nav-link active" data-target="home">Home</span>
|
| 439 |
+
<span class="nav-link" data-target="services">Services</span>
|
| 440 |
+
<span class="nav-link" data-target="projects">Projects</span>
|
| 441 |
+
<span class="nav-link" data-target="contact">Contact</span>
|
| 442 |
+
<span class="btn-cta" data-navigate="contact">Get Quote</span>
|
| 443 |
</nav>
|
| 444 |
|
| 445 |
+
<button class="mobile-menu-btn">
|
| 446 |
+
<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"><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
|
| 447 |
</button>
|
| 448 |
</div>
|
| 449 |
<div class="mobile-menu">
|
| 450 |
+
<span class="mobile-link" data-navigate="home">Home</span>
|
| 451 |
+
<span class="mobile-link" data-navigate="services">Services</span>
|
| 452 |
+
<span class="mobile-link" data-navigate="projects">Projects</span>
|
| 453 |
+
<span class="mobile-link" data-navigate="contact">Contact</span>
|
| 454 |
</div>
|
| 455 |
</header>
|
| 456 |
`;
|
| 457 |
+
|
| 458 |
+
// Add click handlers after DOM is ready
|
| 459 |
+
setTimeout(() => {
|
| 460 |
+
const logo = this.shadowRoot.querySelector('.logo');
|
| 461 |
+
const navLinks = this.shadowRoot.querySelectorAll('.nav-link');
|
| 462 |
+
const mobileLinks = this.shadowRoot.querySelectorAll('.mobile-link');
|
| 463 |
+
const ctaBtn = this.shadowRoot.querySelector('.btn-cta');
|
| 464 |
+
const menuBtn = this.shadowRoot.querySelector('.mobile-menu-btn');
|
| 465 |
+
const mobileMenu = this.shadowRoot.querySelector('.mobile-menu');
|
| 466 |
+
|
| 467 |
+
// Logo click
|
| 468 |
+
if (logo) {
|
| 469 |
+
logo.addEventListener('click', () => {
|
| 470 |
+
if (window.router) window.router.navigate('home');
|
| 471 |
+
});
|
| 472 |
+
}
|
| 473 |
+
|
| 474 |
+
// Nav links
|
| 475 |
+
navLinks.forEach(link => {
|
| 476 |
+
link.addEventListener('click', () => {
|
| 477 |
+
const target = link.getAttribute('data-target');
|
| 478 |
+
if (window.router) window.router.navigate(target);
|
| 479 |
+
});
|
| 480 |
+
});
|
| 481 |
+
|
| 482 |
+
// Mobile links
|
| 483 |
+
mobileLinks.forEach(link => {
|
| 484 |
+
link.addEventListener('click', () => {
|
| 485 |
+
const target = link.getAttribute('data-navigate');
|
| 486 |
+
if (window.router) {
|
| 487 |
+
window.router.navigate(target);
|
| 488 |
+
mobileMenu.classList.remove('open');
|
| 489 |
+
}
|
| 490 |
+
});
|
| 491 |
+
});
|
| 492 |
+
|
| 493 |
+
// CTA button
|
| 494 |
+
if (ctaBtn && ctaBtn.getAttribute('data-navigate')) {
|
| 495 |
+
ctaBtn.addEventListener('click', () => {
|
| 496 |
+
if (window.router) window.router.navigate('contact');
|
| 497 |
+
});
|
| 498 |
+
}
|
| 499 |
+
|
| 500 |
+
// Mobile menu toggle
|
| 501 |
+
if (menuBtn && mobileMenu) {
|
| 502 |
+
menuBtn.addEventListener('click', () => {
|
| 503 |
+
mobileMenu.classList.toggle('open');
|
| 504 |
+
});
|
| 505 |
+
}
|
| 506 |
+
}, 0);
|
| 507 |
}
|
| 508 |
}
|
| 509 |
customElements.define('custom-header', CustomHeader);
|
|
|
|
| 510 |
// FOOTER COMPONENT
|
| 511 |
class CustomFooter extends HTMLElement {
|
| 512 |
connectedCallback() {
|
|
|
|
| 545 |
.footer-links li {
|
| 546 |
margin-bottom: 0.75rem;
|
| 547 |
}
|
| 548 |
+
.footer-link {
|
| 549 |
color: #94a3b8;
|
| 550 |
+
cursor: pointer;
|
| 551 |
transition: color 0.2s;
|
| 552 |
+
display: inline-block;
|
| 553 |
}
|
| 554 |
+
.footer-link:hover {
|
| 555 |
color: white;
|
| 556 |
}
|
| 557 |
+
.contact-info {
|
| 558 |
+
list-style: none;
|
| 559 |
+
padding: 0;
|
| 560 |
+
margin: 0;
|
| 561 |
+
}
|
| 562 |
+
.contact-info li {
|
| 563 |
+
margin-bottom: 0.75rem;
|
| 564 |
+
color: #94a3b8;
|
| 565 |
+
display: flex;
|
| 566 |
+
align-items: center;
|
| 567 |
+
gap: 0.5rem;
|
| 568 |
+
}
|
| 569 |
.copyright {
|
| 570 |
text-align: center;
|
| 571 |
border-top: 1px solid #334155;
|
|
|
|
| 573 |
padding-top: 2rem;
|
| 574 |
font-size: 0.875rem;
|
| 575 |
}
|
| 576 |
+
.social-links {
|
| 577 |
+
display: flex;
|
| 578 |
+
gap: 1rem;
|
| 579 |
+
margin-top: 1rem;
|
| 580 |
+
}
|
| 581 |
+
.social-link {
|
| 582 |
+
width: 40px;
|
| 583 |
+
height: 40px;
|
| 584 |
+
background-color: #334155;
|
| 585 |
+
border-radius: 50%;
|
| 586 |
+
display: flex;
|
| 587 |
+
align-items: center;
|
| 588 |
+
justify-content: center;
|
| 589 |
+
cursor: pointer;
|
| 590 |
+
transition: background-color 0.2s;
|
| 591 |
+
}
|
| 592 |
+
.social-link:hover {
|
| 593 |
+
background-color: #d97706;
|
| 594 |
+
}
|
| 595 |
+
.social-link svg {
|
| 596 |
+
width: 18px;
|
| 597 |
+
height: 18px;
|
| 598 |
+
}
|
| 599 |
</style>
|
| 600 |
<footer>
|
| 601 |
<div class="footer-grid">
|
| 602 |
<div class="footer-col">
|
| 603 |
<h3>TimberTitan</h3>
|
| 604 |
<p>Professional tree care services dedicated to safety, quality, and customer satisfaction. Serving the community since 1995.</p>
|
| 605 |
+
<div class="social-links">
|
| 606 |
+
<div class="social-link">
|
| 607 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"/></svg>
|
| 608 |
+
</div>
|
| 609 |
+
<div class="social-link">
|
| 610 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2" width="20" height="20" rx="5" ry="5"/><path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"/><line x1="17.5" y1="6.5" x2="17.51" y2="6.5"/></svg>
|
| 611 |
+
</div>
|
| 612 |
+
<div class="social-link">
|
| 613 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M23 3a10.9 10.9 0 0 1-3.14 1.53 4.48 4.48 0 0 0-7.86 3v1A10.66 10.66 0 0 1 3 4s-4 9 5 13a11.64 11.64 0 0 1-7 2c9 5 20 0 20-11.5a4.5 4.5 0 0 0-.08-.83A7.72 7.72 0 0 0 23 3z"/></svg>
|
| 614 |
+
</div>
|
| 615 |
+
</div>
|
| 616 |
</div>
|
| 617 |
<div class="footer-col">
|
| 618 |
<h3>Services</h3>
|
| 619 |
<ul class="footer-links">
|
| 620 |
+
<li><span class="footer-link" data-navigate="services">Tree Removal</span></li>
|
| 621 |
+
<li><span class="footer-link" data-navigate="services">Stump Grinding</span></li>
|
| 622 |
+
<li><span class="footer-link" data-navigate="services">Emergency Services</span></li>
|
| 623 |
+
<li><span class="footer-link" data-navigate="services">Land Clearing</span></li>
|
| 624 |
+
<li><span class="footer-link" data-navigate="services">Tree Pruning</span></li>
|
| 625 |
+
<li><span class="footer-link" data-navigate="services">Cabling & Bracing</span></li>
|
| 626 |
</ul>
|
| 627 |
</div>
|
| 628 |
<div class="footer-col">
|
| 629 |
<h3>Quick Links</h3>
|
| 630 |
<ul class="footer-links">
|
| 631 |
+
<li><span class="footer-link" data-navigate="home">Home</span></li>
|
| 632 |
+
<li><span class="footer-link" data-navigate="services">All Services</span></li>
|
| 633 |
+
<li><span class="footer-link" data-navigate="projects">Recent Projects</span></li>
|
| 634 |
+
<li><span class="footer-link" data-navigate="contact">Get Free Quote</span></li>
|
| 635 |
+
<li><span class="footer-link">Privacy Policy</span></li>
|
| 636 |
</ul>
|
| 637 |
</div>
|
| 638 |
<div class="footer-col">
|
| 639 |
+
<h3>Contact Us</h3>
|
| 640 |
+
<ul class="contact-info">
|
| 641 |
+
<li>
|
| 642 |
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg>
|
| 643 |
+
(555) 123-4567
|
| 644 |
+
</li>
|
| 645 |
+
<li>
|
| 646 |
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>
|
| 647 |
+
help@timbertitan.com
|
| 648 |
+
</li>
|
| 649 |
+
<li>
|
| 650 |
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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>
|
| 651 |
+
123 Forest Drive, Woodland Hills, CA
|
| 652 |
+
</li>
|
| 653 |
+
<li>
|
| 654 |
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
| 655 |
+
Mon - Sat: 7AM - 6PM
|
| 656 |
+
</li>
|
| 657 |
</ul>
|
| 658 |
</div>
|
| 659 |
</div>
|
| 660 |
<div class="copyright">
|
| 661 |
+
<p>© 2023 TimberTitan Services. All rights reserved. | Licensed & Insured | Lic# 123456</p>
|
| 662 |
</div>
|
| 663 |
</footer>
|
| 664 |
`;
|
| 665 |
+
|
| 666 |
+
// Add click handlers
|
| 667 |
+
setTimeout(() => {
|
| 668 |
+
const footerLinks = this.shadowRoot.querySelectorAll('.footer-link');
|
| 669 |
+
footerLinks.forEach(link => {
|
| 670 |
+
link.addEventListener('click', () => {
|
| 671 |
+
const target = link.getAttribute('data-navigate');
|
| 672 |
+
if (window.router && target) {
|
| 673 |
+
window.router.navigate(target);
|
| 674 |
+
}
|
| 675 |
+
});
|
| 676 |
+
});
|
| 677 |
+
}, 0);
|
| 678 |
}
|
| 679 |
}
|
| 680 |
customElements.define('custom-footer', CustomFooter);
|
| 681 |
+
</script>
|
| 682 |
|
| 683 |
<!-- Main Logic Script -->
|
| 684 |
<script src="script.js"></script>
|
script.js
CHANGED
|
@@ -57,7 +57,6 @@ const projectsData = [
|
|
| 57 |
{ title: "Hazardous Limb Removal", location: "Suburbia", img: "http://static.photos/nature/640x360/14" },
|
| 58 |
{ title: "Commercial Clearing", location: "Industrial Park", img: "http://static.photos/nature/640x360/15" },
|
| 59 |
];
|
| 60 |
-
|
| 61 |
class Router {
|
| 62 |
constructor() {
|
| 63 |
this.routes = ['home', 'services', 'projects', 'contact'];
|
|
@@ -107,6 +106,9 @@ class Router {
|
|
| 107 |
// Load specific page data if needed
|
| 108 |
if (pageId === 'services') this.loadServices();
|
| 109 |
if (pageId === 'projects') this.loadProjects();
|
|
|
|
|
|
|
|
|
|
| 110 |
}
|
| 111 |
|
| 112 |
updateNavState(activeId) {
|
|
@@ -123,9 +125,21 @@ class Router {
|
|
| 123 |
});
|
| 124 |
}
|
| 125 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
|
| 127 |
async loadServices() {
|
| 128 |
const grid = document.getElementById('services-grid');
|
|
|
|
|
|
|
| 129 |
// Check if already loaded to prevent re-render
|
| 130 |
if (grid.children.length > 1 && !grid.querySelector('.spinner')) return;
|
| 131 |
|
|
@@ -141,22 +155,38 @@ class Router {
|
|
| 141 |
</div>
|
| 142 |
<div class="p-8 flex-grow flex flex-col">
|
| 143 |
<div class="w-12 h-12 bg-primary-100 rounded-lg flex items-center justify-center text-primary-600 mb-4">
|
| 144 |
-
<
|
|
|
|
|
|
|
| 145 |
</div>
|
| 146 |
<h4 class="text-xl font-bold mb-2">${service.title}</h4>
|
| 147 |
<p class="text-gray-600 mb-4 flex-grow">${service.desc}</p>
|
| 148 |
-
<
|
| 149 |
-
Request Service
|
| 150 |
-
|
|
|
|
| 151 |
</div>
|
| 152 |
</div>
|
| 153 |
`).join('');
|
| 154 |
-
feather.replace();
|
| 155 |
}, 600);
|
| 156 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
|
| 158 |
loadProjects() {
|
| 159 |
const grid = document.getElementById('gallery-grid');
|
|
|
|
|
|
|
| 160 |
if (grid.children.length > 0) return;
|
| 161 |
|
| 162 |
const html = projectsData.map(proj => `
|
|
@@ -164,15 +194,16 @@ class Router {
|
|
| 164 |
<img src="${proj.img}" alt="${proj.title}" class="w-full h-full object-cover transition duration-500 group-hover:scale-110">
|
| 165 |
<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">
|
| 166 |
<h4 class="text-white font-bold text-xl">${proj.title}</h4>
|
| 167 |
-
<p class="text-gray-300 text-sm
|
|
|
|
|
|
|
|
|
|
| 168 |
</div>
|
| 169 |
</div>
|
| 170 |
`).join('');
|
| 171 |
grid.innerHTML = html;
|
| 172 |
-
feather.replace();
|
| 173 |
}
|
| 174 |
}
|
| 175 |
-
|
| 176 |
// Initialize Router when DOM is ready
|
| 177 |
let router;
|
| 178 |
document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
| 57 |
{ title: "Hazardous Limb Removal", location: "Suburbia", img: "http://static.photos/nature/640x360/14" },
|
| 58 |
{ title: "Commercial Clearing", location: "Industrial Park", img: "http://static.photos/nature/640x360/15" },
|
| 59 |
];
|
|
|
|
| 60 |
class Router {
|
| 61 |
constructor() {
|
| 62 |
this.routes = ['home', 'services', 'projects', 'contact'];
|
|
|
|
| 106 |
// Load specific page data if needed
|
| 107 |
if (pageId === 'services') this.loadServices();
|
| 108 |
if (pageId === 'projects') this.loadProjects();
|
| 109 |
+
|
| 110 |
+
// Close mobile menu if open
|
| 111 |
+
this.closeMobileMenu();
|
| 112 |
}
|
| 113 |
|
| 114 |
updateNavState(activeId) {
|
|
|
|
| 125 |
});
|
| 126 |
}
|
| 127 |
}
|
| 128 |
+
|
| 129 |
+
closeMobileMenu() {
|
| 130 |
+
const header = document.querySelector('custom-header');
|
| 131 |
+
if (header && header.shadowRoot) {
|
| 132 |
+
const mobileMenu = header.shadowRoot.querySelector('.mobile-menu');
|
| 133 |
+
if (mobileMenu) {
|
| 134 |
+
mobileMenu.classList.remove('open');
|
| 135 |
+
}
|
| 136 |
+
}
|
| 137 |
+
}
|
| 138 |
|
| 139 |
async loadServices() {
|
| 140 |
const grid = document.getElementById('services-grid');
|
| 141 |
+
if (!grid) return;
|
| 142 |
+
|
| 143 |
// Check if already loaded to prevent re-render
|
| 144 |
if (grid.children.length > 1 && !grid.querySelector('.spinner')) return;
|
| 145 |
|
|
|
|
| 155 |
</div>
|
| 156 |
<div class="p-8 flex-grow flex flex-col">
|
| 157 |
<div class="w-12 h-12 bg-primary-100 rounded-lg flex items-center justify-center text-primary-600 mb-4">
|
| 158 |
+
<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">
|
| 159 |
+
${this.getIconPath(service.icon)}
|
| 160 |
+
</svg>
|
| 161 |
</div>
|
| 162 |
<h4 class="text-xl font-bold mb-2">${service.title}</h4>
|
| 163 |
<p class="text-gray-600 mb-4 flex-grow">${service.desc}</p>
|
| 164 |
+
<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">
|
| 165 |
+
Request Service
|
| 166 |
+
<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>
|
| 167 |
+
</button>
|
| 168 |
</div>
|
| 169 |
</div>
|
| 170 |
`).join('');
|
|
|
|
| 171 |
}, 600);
|
| 172 |
}
|
| 173 |
+
|
| 174 |
+
getIconPath(iconName) {
|
| 175 |
+
const icons = {
|
| 176 |
+
'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"/>',
|
| 177 |
+
'disc': '<circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/>',
|
| 178 |
+
'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"/>',
|
| 179 |
+
'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"/>',
|
| 180 |
+
'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"/>',
|
| 181 |
+
'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"/>'
|
| 182 |
+
};
|
| 183 |
+
return icons[iconName] || icons['log-out'];
|
| 184 |
+
}
|
| 185 |
|
| 186 |
loadProjects() {
|
| 187 |
const grid = document.getElementById('gallery-grid');
|
| 188 |
+
if (!grid) return;
|
| 189 |
+
|
| 190 |
if (grid.children.length > 0) return;
|
| 191 |
|
| 192 |
const html = projectsData.map(proj => `
|
|
|
|
| 194 |
<img src="${proj.img}" alt="${proj.title}" class="w-full h-full object-cover transition duration-500 group-hover:scale-110">
|
| 195 |
<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">
|
| 196 |
<h4 class="text-white font-bold text-xl">${proj.title}</h4>
|
| 197 |
+
<p class="text-gray-300 text-sm flex items-center">
|
| 198 |
+
<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>
|
| 199 |
+
${proj.location}
|
| 200 |
+
</p>
|
| 201 |
</div>
|
| 202 |
</div>
|
| 203 |
`).join('');
|
| 204 |
grid.innerHTML = html;
|
|
|
|
| 205 |
}
|
| 206 |
}
|
|
|
|
| 207 |
// Initialize Router when DOM is ready
|
| 208 |
let router;
|
| 209 |
document.addEventListener('DOMContentLoaded', () => {
|
style.css
CHANGED
|
@@ -46,8 +46,26 @@ body {
|
|
| 46 |
.nav-link.active::after {
|
| 47 |
width: 100%;
|
| 48 |
}
|
| 49 |
-
|
| 50 |
/* Utility for shadow root link handling (if needed) */
|
| 51 |
a {
|
| 52 |
text-decoration: none;
|
| 53 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
.nav-link.active::after {
|
| 47 |
width: 100%;
|
| 48 |
}
|
|
|
|
| 49 |
/* Utility for shadow root link handling (if needed) */
|
| 50 |
a {
|
| 51 |
text-decoration: none;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
/* Button reset for inline-flex buttons */
|
| 55 |
+
button.bg-transparent {
|
| 56 |
+
background: transparent;
|
| 57 |
+
border: none;
|
| 58 |
+
padding: 0;
|
| 59 |
+
margin: 0;
|
| 60 |
+
font: inherit;
|
| 61 |
+
color: inherit;
|
| 62 |
+
cursor: pointer;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
button.bg-transparent:hover {
|
| 66 |
+
text-decoration: none;
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
button.bg-transparent svg {
|
| 70 |
+
pointer-events: none;
|
| 71 |
+
}
|