← back to Losgardeners
Initial Los Gardeners public site snapshot
4319bef719a3765701f844233a6af57509075724 · 2026-09-03 10:06:56 -0700 · Steve Abrams
Files touched
A .gitignoreA public/index.html
Diff
commit 4319bef719a3765701f844233a6af57509075724
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Sep 3 10:06:56 2026 -0700
Initial Los Gardeners public site snapshot
---
.gitignore | 30 +
public/index.html | 2846 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 2876 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e265051
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,30 @@
+node_modules/
+.env*
+tmp/
+temp/
+*.log
+logs/
+.DS_Store
+dist/
+build/
+.next/
+cookies.txt
+*.pem
+*.key
+server.js.backup*
+*.backup
+*.old
+uploads/
+public/uploads/
+
+# Legacy production snapshot contains historical scripts with embedded keys.
+# Track only the active public AdSense/crawler surface until those scripts are
+# separately sanitized and routed through the secrets manager.
+*
+!.gitignore
+!public/
+public/*
+!public/index.html
+!public/ads.txt
+!public/robots.txt
+!public/sitemap.xml
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..463c792
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,2846 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Los Gardeners - Professional Landscaping Services in LA Neighborhoods</title>
+ <meta name="description" content="Find trusted local gardeners and landscapers in your LA neighborhood - Hollywood, Studio City, Reseda, Van Nuys, and more. Get quotes, compare prices, and hire professionals for all your garden needs.">
+ <!-- Request geolocation permission policy -->
+ <meta http-equiv="Permissions-Policy" content="geolocation=()">
+
+ <style>
+ * {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ }
+
+ :root {
+ --primary-color: #2e7d32;
+ --primary-dark: #1b5e20;
+ --primary-light: #4caf50;
+ --secondary-color: #ff6b35;
+ --text-dark: #212121;
+ --text-gray: #757575;
+ --bg-light: #f5f5f5;
+ --white: #ffffff;
+ --shadow-sm: 0 2px 4px rgba(0,0,0,0.08);
+ --shadow-md: 0 4px 12px rgba(0,0,0,0.12);
+ --shadow-lg: 0 8px 24px rgba(0,0,0,0.16);
+ --border-radius: 12px;
+ }
+
+ body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
+ line-height: 1.6;
+ color: var(--text-dark);
+ background: var(--white);
+ padding-top: 50px; /* Account for fixed GoFundMe banner */
+ }
+
+ .container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 0 20px;
+ }
+
+ /* Header */
+ .header {
+ background: var(--white);
+ box-shadow: var(--shadow-sm);
+ position: sticky;
+ top: 50px; /* Below the GoFundMe banner */
+ z-index: 100;
+ padding: 15px 0;
+ }
+
+ .nav {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0;
+ }
+
+ .logo {
+ display: flex;
+ align-items: center;
+ font-size: 1.5rem;
+ font-weight: 700;
+ color: var(--primary-color);
+ }
+
+ .logo img {
+ height: 50px;
+ width: auto;
+ }
+
+ .nav-links {
+ display: flex;
+ gap: 2rem;
+ align-items: center;
+ }
+
+ .nav-links a {
+ color: var(--text-dark);
+ text-decoration: none;
+ font-weight: 500;
+ transition: color 0.3s;
+ }
+
+ .nav-links a:hover {
+ color: var(--primary-color);
+ }
+
+ .cta-button {
+ background: var(--primary-color);
+ color: var(--white);
+ padding: 0.75rem 1.5rem;
+ border-radius: var(--border-radius);
+ text-decoration: none;
+ font-weight: 600;
+ transition: all 0.3s;
+ }
+
+ .cta-button:hover {
+ background: var(--primary-dark);
+ transform: translateY(-2px);
+ }
+
+ /* Hero Section */
+ .hero {
+ background: linear-gradient(135deg, var(--primary-light) 0%, var(--primary-dark) 100%);
+ color: var(--white);
+ padding: 2rem 0 1rem 0;
+ text-align: center;
+ }
+
+ .hero h1 {
+ font-size: 2rem;
+ margin-bottom: 1rem;
+ }
+
+ .hero p {
+ font-size: 1.25rem;
+ margin-bottom: 2rem;
+ opacity: 0.95;
+ }
+
+ /* Search Bar */
+ .search-container {
+ background: var(--white);
+ border-radius: var(--border-radius);
+ padding: 1rem;
+ display: flex;
+ gap: 1rem;
+ max-width: 800px;
+ margin: 2rem auto;
+ box-shadow: var(--shadow-md);
+ }
+
+ .search-input {
+ flex: 1;
+ padding: 0.75rem;
+ border: 1px solid #e0e0e0;
+ border-radius: 8px;
+ font-size: 1rem;
+ }
+
+ .search-button {
+ background: var(--primary-color);
+ color: var(--white);
+ border: none;
+ padding: 0.75rem 2rem;
+ border-radius: 8px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: background 0.3s;
+ }
+
+ .search-button:hover {
+ background: var(--primary-dark);
+ }
+
+ /* Autocomplete Styles */
+ .search-autocomplete {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ right: 0;
+ background: var(--white);
+ border: 1px solid #e0e0e0;
+ border-top: none;
+ border-radius: 0 0 8px 8px;
+ max-height: 200px;
+ overflow-y: auto;
+ z-index: 1000;
+ display: none;
+ }
+
+ .search-suggestion {
+ padding: 0.75rem;
+ cursor: pointer;
+ border-bottom: 1px solid #f0f0f0;
+ transition: background 0.2s;
+ }
+
+ .search-suggestion:hover,
+ .search-suggestion.selected {
+ background: var(--bg-light);
+ }
+
+ .search-suggestion:last-child {
+ border-bottom: none;
+ }
+
+ .search-input-container {
+ position: relative;
+ flex: 1;
+ }
+
+ /* Search and Sort Control Styles */
+ .search-sort-controls {
+ background: var(--white);
+ border-radius: var(--border-radius);
+ padding: 1.5rem;
+ margin: 2rem 0;
+ box-shadow: var(--shadow-sm);
+ border: 2px solid var(--primary-light);
+ }
+
+ .control-header {
+ margin-bottom: 1.5rem;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .control-label {
+ font-weight: 600;
+ color: var(--text-dark);
+ font-size: 1.1rem;
+ }
+
+ .results-count {
+ color: var(--primary-color);
+ font-weight: 500;
+ font-size: 0.9rem;
+ }
+
+ /* City Search Autocomplete Styles */
+ .city-search-container {
+ position: relative;
+ }
+
+ .autocomplete-wrapper {
+ position: relative;
+ width: 100%;
+ max-width: 400px;
+ }
+
+ .city-search-input {
+ width: 100%;
+ padding: 0.75rem 1rem;
+ border: 2px solid var(--primary-light);
+ border-radius: var(--border-radius);
+ font-size: 1rem;
+ transition: border-color 0.3s;
+ background: var(--white);
+ }
+
+ .city-search-input:focus {
+ outline: none;
+ border-color: var(--primary-color);
+ box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.1);
+ }
+
+ .autocomplete-dropdown {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ right: 0;
+ background: var(--white);
+ border: 2px solid var(--primary-light);
+ border-top: none;
+ border-radius: 0 0 var(--border-radius) var(--border-radius);
+ max-height: 200px;
+ overflow-y: auto;
+ z-index: 1000;
+ display: none;
+ box-shadow: var(--shadow-md);
+ }
+
+ .autocomplete-dropdown.show {
+ display: block;
+ }
+
+ .autocomplete-item {
+ padding: 0.75rem 1rem;
+ cursor: pointer;
+ transition: background-color 0.2s;
+ border-bottom: 1px solid #f0f0f0;
+ }
+
+ .autocomplete-item:hover,
+ .autocomplete-item.highlighted {
+ background: var(--primary-light);
+ color: var(--white);
+ }
+
+ .autocomplete-item:last-child {
+ border-bottom: none;
+ }
+
+ .autocomplete-item .city-name {
+ font-weight: 500;
+ }
+
+ .autocomplete-item .gardener-count {
+ font-size: 0.875rem;
+ opacity: 0.8;
+ margin-left: 0.5rem;
+ }
+
+ .control-buttons {
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+ }
+
+ .sort-group {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ flex-wrap: wrap;
+ }
+
+ .sort-label {
+ font-weight: 600;
+ color: var(--text-dark);
+ font-size: 0.95rem;
+ min-width: 70px;
+ }
+
+ .sort-btn {
+ background: var(--bg-light);
+ border: 2px solid var(--primary-light);
+ color: var(--text-dark);
+ padding: 0.7rem 1.4rem;
+ border-radius: var(--border-radius);
+ cursor: pointer;
+ font-weight: 600;
+ transition: all 0.3s ease;
+ font-size: 0.9rem;
+ }
+
+ .sort-btn[data-sort="distance"] {
+ background: #fff;
+ border: 3px solid #4CAF50;
+ font-size: 1rem;
+ min-width: 85px;
+ }
+
+ .sort-btn:hover {
+ background: var(--primary-light);
+ color: var(--white);
+ transform: translateY(-2px);
+ box-shadow: 0 4px 8px rgba(0,0,0,0.15);
+ }
+
+ .sort-btn.active {
+ background: var(--primary-color);
+ color: var(--white);
+ border-color: var(--primary-color);
+ box-shadow: 0 6px 12px rgba(76, 175, 80, 0.3);
+ }
+
+ .sort-btn.active[data-sort="distance"] {
+ background: #2E7D32;
+ border-color: #2E7D32;
+ font-weight: 700;
+ }
+
+ @media (max-width: 768px) {
+ .control-buttons {
+ gap: 1rem;
+ }
+
+ .sort-group {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.75rem;
+ }
+
+ .sort-label {
+ min-width: auto;
+ margin-bottom: 0.25rem;
+ }
+
+ .sort-btn {
+ width: 100%;
+ text-align: center;
+ }
+ }
+
+
+ /* Services Grid */
+ .services {
+ padding: 3rem 0;
+ }
+
+ .services h2 {
+ text-align: center;
+ font-size: 2.5rem;
+ margin-bottom: 3rem;
+ color: var(--text-dark);
+ }
+
+ .services-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: 2rem;
+ }
+
+ .service-card {
+ background: var(--white);
+ border-radius: var(--border-radius);
+ padding: 2rem;
+ box-shadow: var(--shadow-sm);
+ transition: all 0.3s;
+ cursor: pointer;
+ }
+
+ .service-card:hover {
+ box-shadow: var(--shadow-lg);
+ transform: translateY(-5px);
+ }
+
+ .service-icon {
+ font-size: 3rem;
+ margin-bottom: 1rem;
+ }
+
+ .service-title {
+ font-size: 1.5rem;
+ margin-bottom: 1rem;
+ color: var(--text-dark);
+ }
+
+ .service-description {
+ color: var(--text-gray);
+ line-height: 1.6;
+ }
+
+ /* Featured Gardeners */
+ .featured-gardeners {
+ background: var(--bg-light);
+ padding: 1rem 0 3rem 0;
+ margin-top: 0;
+ }
+
+ .featured-gardeners h2 {
+ text-align: center;
+ font-size: 2.5rem;
+ margin-bottom: 3rem;
+ color: var(--text-dark);
+ }
+
+ .gardeners-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+ gap: 2rem;
+ }
+
+ .gardener-card {
+ background: var(--white);
+ border-radius: var(--border-radius);
+ padding: 1.5rem;
+ box-shadow: var(--shadow-sm);
+ transition: all 0.3s;
+ }
+
+ .gardener-card:hover {
+ box-shadow: var(--shadow-lg);
+ transform: translateY(-5px);
+ }
+
+ .gardener-header {
+ display: flex;
+ align-items: center;
+ margin-bottom: 1rem;
+ }
+
+ .gardener-avatar {
+ width: 60px;
+ height: 60px;
+ border-radius: 50%;
+ background: var(--primary-light);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--white);
+ font-weight: 700;
+ font-size: 1.5rem;
+ margin-right: 1rem;
+ }
+
+ .gardener-info h3 {
+ color: var(--text-dark);
+ margin-bottom: 0.25rem;
+ }
+
+ .gardener-location {
+ color: var(--text-gray);
+ font-size: 0.9rem;
+ }
+
+ .gardener-distance {
+ color: var(--primary-color);
+ font-size: 0.8rem;
+ font-weight: 600;
+ margin-top: 0.2rem;
+ }
+
+ .latino-marker {
+ color: var(--secondary-color);
+ font-size: 0.8rem;
+ font-weight: 600;
+ margin-top: 0.2rem;
+ }
+
+ /* Verification Badge Styles */
+ .verification-badge {
+ display: inline-block;
+ font-size: 0.75rem;
+ font-weight: 600;
+ padding: 0.2rem 0.5rem;
+ border-radius: 12px;
+ margin-top: 0.3rem;
+ cursor: help;
+ }
+
+ .triple-verified {
+ background: linear-gradient(135deg, #4caf50, #2e7d32);
+ color: white;
+ box-shadow: 0 2px 4px rgba(76, 175, 80, 0.3);
+ }
+
+ .partial-verified {
+ background: linear-gradient(135deg, #ff9800, #f57c00);
+ color: white;
+ box-shadow: 0 2px 4px rgba(255, 152, 0, 0.3);
+ }
+
+ .pending {
+ background: linear-gradient(135deg, #9e9e9e, #616161);
+ color: white;
+ box-shadow: 0 2px 4px rgba(158, 158, 158, 0.3);
+ }
+
+ .unverified {
+ background: linear-gradient(135deg, #f44336, #d32f2f);
+ color: white;
+ box-shadow: 0 2px 4px rgba(244, 67, 54, 0.3);
+ }
+
+ .verification-badge:hover {
+ transform: translateY(-1px);
+ transition: transform 0.2s ease;
+ cursor: pointer;
+ }
+
+ /* Verification Section and Platform Links */
+ .verification-section {
+ margin-top: 0.5rem;
+ }
+
+ .platform-links {
+ margin-top: 0.3rem;
+ }
+
+ .platform-link {
+ display: inline-block;
+ margin-right: 0.3rem;
+ font-size: 1.2rem;
+ text-decoration: none;
+ transition: transform 0.2s ease;
+ }
+
+ .platform-link:hover {
+ transform: scale(1.2);
+ }
+
+ .verification-note {
+ font-size: 0.7rem;
+ color: #4caf50;
+ font-weight: 500;
+ margin-top: 0.2rem;
+ }
+
+ /* Verification Modal */
+ .verification-modal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.8);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 10000;
+ }
+
+ .verification-modal-content {
+ background: var(--white);
+ padding: 2rem;
+ border-radius: var(--border-radius);
+ max-width: 600px;
+ width: 90%;
+ max-height: 80vh;
+ overflow-y: auto;
+ box-shadow: var(--shadow-lg);
+ }
+
+ .verification-modal h3 {
+ color: var(--primary-color);
+ margin-bottom: 1rem;
+ }
+
+ .verification-modal hr {
+ margin: 1rem 0;
+ border: none;
+ border-top: 2px solid var(--bg-light);
+ }
+
+ .platform-details {
+ margin: 1rem 0;
+ }
+
+ .platform-detail {
+ background: var(--bg-light);
+ padding: 1rem;
+ margin-bottom: 1rem;
+ border-radius: 8px;
+ border-left: 4px solid var(--primary-color);
+ }
+
+ .platform-header {
+ display: flex;
+ align-items: center;
+ margin-bottom: 0.5rem;
+ }
+
+ .platform-logo {
+ font-size: 1.5rem;
+ margin-right: 0.5rem;
+ }
+
+ .verify-link {
+ display: inline-block;
+ background: var(--primary-color);
+ color: var(--white);
+ padding: 0.5rem 1rem;
+ border-radius: 6px;
+ text-decoration: none;
+ font-weight: 600;
+ transition: background 0.3s ease;
+ margin-top: 0.5rem;
+ }
+
+ .verify-link:hover {
+ background: var(--primary-dark);
+ }
+
+ .close-modal-btn {
+ background: var(--text-gray);
+ color: var(--white);
+ border: none;
+ padding: 0.75rem 1.5rem;
+ border-radius: 6px;
+ font-weight: 600;
+ cursor: pointer;
+ margin-top: 1rem;
+ width: 100%;
+ }
+
+ .close-modal-btn:hover {
+ background: var(--text-dark);
+ }
+
+
+ .gardener-services {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ margin: 1rem 0;
+ }
+
+ .service-tag {
+ background: var(--bg-light);
+ padding: 0.25rem 0.75rem;
+ border-radius: 20px;
+ font-size: 0.85rem;
+ color: var(--text-gray);
+ }
+
+ .gardener-actions {
+ display: flex;
+ gap: 1rem;
+ margin-top: 1rem;
+ }
+
+ .btn-contact {
+ flex: 1;
+ padding: 0.75rem;
+ border: none;
+ border-radius: 8px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.3s;
+ }
+
+ .btn-primary {
+ background: var(--primary-color);
+ color: var(--white);
+ }
+
+ .btn-primary:hover {
+ background: var(--primary-dark);
+ }
+
+ .btn-secondary {
+ background: var(--white);
+ color: var(--primary-color);
+ border: 2px solid var(--primary-color);
+ }
+
+ .btn-secondary:hover {
+ background: var(--primary-color);
+ color: var(--white);
+ }
+
+ /* Footer */
+ .footer {
+ background: var(--text-dark);
+ color: var(--white);
+ padding: 3rem 0 2rem;
+ margin-top: 4rem;
+ }
+
+ .footer-content {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ gap: 2rem;
+ margin-bottom: 2rem;
+ }
+
+ .footer-section h3 {
+ margin-bottom: 1rem;
+ color: var(--primary-light);
+ }
+
+ .footer-section a {
+ color: rgba(255, 255, 255, 0.8);
+ text-decoration: none;
+ display: block;
+ padding: 0.25rem 0;
+ transition: color 0.3s;
+ }
+
+ .footer-section a:hover {
+ color: var(--primary-light);
+ }
+
+ .footer-bottom {
+ text-align: center;
+ padding-top: 2rem;
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
+ color: rgba(255, 255, 255, 0.6);
+ }
+
+ /* Responsive */
+ @media (max-width: 768px) {
+ body {
+ padding-top: 90px;
+ }
+
+ .container {
+ padding: 0 15px;
+ }
+
+ .header {
+ top: 70px;
+ padding: 10px 0;
+ }
+
+ .nav {
+ flex-direction: row;
+ justify-content: space-between;
+ }
+
+ .logo {
+ font-size: 1.2rem;
+ }
+
+ .logo img {
+ height: 35px;
+ }
+
+ .nav-links {
+ display: none;
+ }
+
+ .hero {
+ padding: 2rem 0;
+ }
+
+ .hero h1 {
+ font-size: 1.75rem;
+ line-height: 1.3;
+ }
+
+ .hero p {
+ font-size: 1rem;
+ }
+
+ .search-container {
+ padding: 1rem;
+ gap: 1rem;
+ }
+
+ .search-input {
+ padding: 0.75rem;
+ font-size: 1rem;
+ }
+
+ .search-button {
+ padding: 0.75rem 2rem;
+ }
+
+ .services {
+ padding: 2rem 0;
+ }
+
+ .services h2 {
+ font-size: 1.75rem;
+ margin-bottom: 1.5rem;
+ }
+
+ .services-grid {
+ grid-template-columns: 1fr;
+ gap: 1.5rem;
+ }
+
+ .service-card {
+ padding: 1.5rem;
+ }
+
+ .service-icon {
+ font-size: 2rem;
+ }
+
+ .service-card h3 {
+ font-size: 1.25rem;
+ }
+
+ .featured-gardeners h2 {
+ font-size: 1.75rem;
+ margin-bottom: 1.5rem;
+ }
+
+ .search-sort-controls {
+ padding: 1rem;
+ gap: 1rem;
+ }
+
+ .control-header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.5rem;
+ }
+
+ .control-label {
+ font-size: 1rem;
+ }
+
+ .results-count {
+ font-size: 0.9rem;
+ }
+
+ .city-search-container {
+ margin-bottom: 1rem;
+ }
+
+ .city-search-input {
+ font-size: 1rem;
+ padding: 0.75rem;
+ }
+
+ .control-buttons {
+ flex-direction: column;
+ width: 100%;
+ }
+
+ .sort-group {
+ width: 100%;
+ }
+
+ .sort-btn {
+ padding: 0.75rem;
+ font-size: 0.95rem;
+ }
+
+ .gardeners-grid {
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+ }
+
+ .gardener-card {
+ padding: 1.5rem;
+ width: 100%;
+ }
+
+ .gardener-header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.75rem;
+ }
+
+ .gardener-name {
+ font-size: 1.35rem;
+ }
+
+ .gardener-subtitle {
+ font-size: 1rem;
+ }
+
+ .rating {
+ font-size: 1rem;
+ }
+
+ .badge {
+ padding: 0.4rem 0.85rem;
+ font-size: 0.9rem;
+ }
+
+ .gardener-info {
+ gap: 0.75rem;
+ }
+
+ .info-item {
+ font-size: 1rem;
+ }
+
+ .gardener-services {
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ }
+
+ .service-tag {
+ padding: 0.4rem 0.85rem;
+ font-size: 0.9rem;
+ }
+
+ .gardener-actions {
+ flex-direction: column;
+ gap: 0.75rem;
+ }
+
+ .btn {
+ width: 100%;
+ justify-content: center;
+ padding: 0.9rem;
+ font-size: 1rem;
+ }
+
+ .footer-content {
+ flex-direction: column;
+ gap: 2rem;
+ text-align: center;
+ }
+
+ .footer-section {
+ align-items: center;
+ }
+
+ .footer-section h4 {
+ font-size: 1.1rem;
+ }
+
+ .footer-links {
+ align-items: center;
+ }
+
+ .footer-bottom {
+ flex-direction: column;
+ gap: 1rem;
+ text-align: center;
+ }
+ }
+
+ @keyframes slideDown {
+ from {
+ transform: translateY(-100%);
+ opacity: 0;
+ }
+ to {
+ transform: translateY(0);
+ opacity: 1;
+ }
+ }
+ </style>
+<!-- ga --><script async src="https://www.googletagmanager.com/gtag/js?id=G-6XDDK9KD4C"></script><script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag("js",new Date());gtag("config","G-6XDDK9KD4C");</script><script>!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version="2.0";n.queue=[];t=b.createElement(e);t.async=!0;t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,document,"script","https://connect.facebook.net/en_US/fbevents.js");fbq("init","1431180262113856");fbq("track","PageView");</script></head>
+<body>
+ <!-- GOFUNDME BANNER - FIXED AT TOP OF EVERY PAGE -->
+ <div style="
+ background: #ff0000;
+ color: white;
+ padding: 15px;
+ text-align: center;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 99999;
+ box-shadow: 0 4px 10px rgba(0,0,0,0.3);
+ font-family: Arial, sans-serif;
+ ">
+ <a href="https://www.gofundme.com/f/bring-yenni-home-to-her-children"
+ target="_blank"
+ style="color: white; text-decoration: none; display: block;">
+ <strong class="banner-text" style="font-size: 1.2rem;">❤️ URGENT: Help Bring Yenni Home to Her Children - DONATE NOW ❤️</strong>
+ </a>
+ </div>
+
+ <style>
+ @media (max-width: 768px) {
+ .banner-text {
+ font-size: 0.95rem !important;
+ line-height: 1.3;
+ }
+ }
+ </style>
+
+ <header class="header">
+ <div class="container">
+ <nav class="nav">
+ <div class="logo">
+ <img src="/images/logo.svg" alt="Los Gardeners">
+ </div>
+ <div class="nav-links">
+ <a href="/services.html">Services</a>
+ <a href="#gardeners">Find Gardeners</a>
+ <a href="/nurseries.html">Nurseries</a>
+ <a href="/landscaping-news.html">News</a>
+ <a href="/quote-system.html" class="cta-button">Get Quote</a>
+ </div>
+ </nav>
+ </div>
+ </header>
+
+ <!-- Location Permission Banner -->
+ <div id="locationPermissionBanner" style="
+ background: linear-gradient(135deg, #4caf50 0%, #2e7d32 100%);
+ color: white;
+ padding: 15px;
+ text-align: center;
+ position: fixed;
+ top: 50px;
+ left: 0;
+ right: 0;
+ z-index: 1000;
+ display: none;
+ box-shadow: 0 2px 10px rgba(0,0,0,0.2);
+ animation: slideDown 0.3s ease;
+ ">
+ <div style="max-width: 1200px; margin: 0 auto; display: flex; align-items: center; justify-content: center; gap: 15px; flex-wrap: wrap;">
+ <span style="font-size: 1.2rem;">📍</span>
+ <span style="font-weight: 600;">Enable location to find gardeners near you!</span>
+ <button onclick="requestLocationPermission()" style="
+ background: white;
+ color: #2e7d32;
+ padding: 8px 20px;
+ border: none;
+ border-radius: 25px;
+ font-weight: bold;
+ cursor: pointer;
+ transition: transform 0.2s;
+ " onmouseover="this.style.transform='scale(1.05)'" onmouseout="this.style.transform='scale(1)'">
+ Enable Location Now
+ </button>
+ <button onclick="dismissLocationBanner()" style="
+ background: transparent;
+ color: white;
+ border: 1px solid white;
+ padding: 8px 15px;
+ border-radius: 25px;
+ cursor: pointer;
+ font-size: 0.9rem;
+ ">
+ Maybe Later
+ </button>
+ </div>
+ </div>
+
+ <section class="hero" style="margin-top: 0;">
+ <div class="container">
+ <h1>Professional Landscaping Services<br><span id="locationText">in Los Angeles</span></h1>
+ <p id="gardenerCountText">Connect with 12,000+ trusted gardeners in your neighborhood</p>
+
+ <!-- Geolocation Status Indicator -->
+ <div id="geolocationStatus" style="
+ margin-top: 20px;
+ padding: 15px;
+ background: rgba(255, 255, 255, 0.95);
+ border-radius: 10px;
+ display: none;
+ align-items: center;
+ gap: 10px;
+ font-weight: 600;
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
+ justify-content: center;
+ ">
+ <span id="geoIcon" style="font-size: 1.5rem;">📍</span>
+ <span id="geoText">Detecting your location...</span>
+ </div>
+
+ </div>
+ </section>
+
+ <div class="container">
+
+ <section class="featured-gardeners" id="gardeners">
+ <h2>Featured Gardeners Near You</h2>
+
+ <!-- Search and Sort Controls -->
+ <div class="search-sort-controls" id="searchSortControls" style="display: block;">
+ <div class="control-header">
+ <span class="control-label">🔍 Search & Sort:</span>
+ <span class="results-count" id="resultsCount"></span>
+ </div>
+
+ <!-- City Search Autocomplete -->
+ <div class="city-search-container" style="margin-bottom: 1.5rem;">
+ <label class="sort-label">📍 Search by City/Neighborhood:</label>
+ <div class="autocomplete-wrapper" style="position: relative;">
+ <div style="display: flex; gap: 10px; align-items: stretch;">
+ <input
+ type="text"
+ id="citySearchInput"
+ placeholder="Type a city or neighborhood..."
+ class="city-search-input"
+ autocomplete="off"
+ style="flex: 1;"
+ >
+ <button onclick="performSearch()" style="
+ padding: 12px 30px;
+ background: linear-gradient(135deg, #4caf50 0%, #2e7d32 100%);
+ color: white;
+ border: none;
+ border-radius: 8px;
+ font-weight: bold;
+ font-size: 16px;
+ cursor: pointer;
+ transition: transform 0.2s, box-shadow 0.2s;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+ " onmouseover="this.style.transform='scale(1.05)'; this.style.boxShadow='0 4px 8px rgba(0,0,0,0.2)'" onmouseout="this.style.transform='scale(1)'; this.style.boxShadow='0 2px 4px rgba(0,0,0,0.1)'">
+ GO 🔍
+ </button>
+ </div>
+ <div class="autocomplete-dropdown" id="autocompleteDropdown"></div>
+ </div>
+ </div>
+
+ <div class="control-buttons">
+ <div class="sort-group">
+ <label class="sort-label">Filter by Type:</label>
+ <label style="display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem 1rem; background: #4caf50; color: white; border-radius: 8px; cursor: default; font-weight: 600;">
+ <input type="checkbox" id="latinoOwnedCheckbox" checked disabled style="width: 18px; height: 18px; cursor: default;">
+ <span>Latino-owned / Habla Español Only</span>
+ </label>
+ </div>
+ </div>
+ </div>
+
+ <div class="gardeners-grid" id="gardenersGrid">
+ <!-- Gardeners will be loaded here dynamically -->
+ </div>
+ </section>
+ </div>
+
+ <footer class="footer">
+ <div class="container">
+ <div class="footer-content">
+ <div class="footer-section">
+ <h3>Quick Links</h3>
+ <a href="/quote-system.html">Get a Quote</a>
+ <a href="/nurseries.html">Find Nurseries</a>
+ <a href="/landscaping-news.html">Latest News</a>
+ <a href="/business-dashboard.html">For Businesses</a>
+ </div>
+ <div class="footer-section">
+ <h3>Service Areas</h3>
+ <a href="#">Los Angeles</a>
+ <a href="#">Hollywood</a>
+ <a href="#">Santa Monica</a>
+ <a href="#">Pasadena</a>
+ </div>
+ <div class="footer-section">
+ <h3>Contact</h3>
+ <a href="mailto:info@losgardeners.com">info@losgardeners.com</a>
+ <a href="tel:+13235550100">(323) 555-0100</a>
+ <a href="#">Support Center</a>
+ </div>
+ </div>
+ <div class="footer-bottom">
+ <p>© 2024 Los Gardeners. All rights reserved.</p>
+ </div>
+ </div>
+ </footer>
+
+ <script>
+ // LA Cities for autocomplete
+ const laCities = [
+ "Hollywood", "Santa Monica", "Beverly Hills", "West Hollywood", "Culver City",
+ "Venice", "Pasadena", "Glendale", "Burbank", "Van Nuys", "Reseda", "Northridge",
+ "Studio City", "Sherman Oaks", "Encino", "Tarzana", "Woodland Hills", "Canoga Park",
+ "Chatsworth", "Granada Hills", "North Hills", "Sun Valley", "Pacoima", "Arleta",
+ "Panorama City", "Sylmar", "San Fernando", "Mission Hills", "Porter Ranch",
+ "Bel Air", "Westwood", "Century City", "Brentwood", "Pacific Palisades",
+ "Malibu", "Marina del Rey", "Playa del Rey", "El Segundo", "Manhattan Beach",
+ "Hermosa Beach", "Redondo Beach", "Torrance", "Carson", "Lomita", "Harbor City",
+ "San Pedro", "Wilmington", "Long Beach", "Downey", "Norwalk", "Bellflower",
+ "Lakewood", "Cerritos", "Artesia", "Hawaiian Gardens", "Los Alamitos",
+ "Seal Beach", "Cypress", "La Palma", "Buena Park", "Fullerton", "Anaheim",
+ "Garden Grove", "Westminster", "Huntington Beach", "Fountain Valley",
+ "Costa Mesa", "Newport Beach", "Irvine", "Tustin", "Orange", "Santa Ana",
+ "East LA", "Montebello", "Pico Rivera", "Whittier", "La Mirada", "Santa Fe Springs",
+ "La Habra", "Brea", "Placentia", "Yorba Linda", "Chino Hills", "Diamond Bar",
+ "Walnut", "West Covina", "Covina", "Azusa", "Glendora", "San Dimas", "La Verne",
+ "Pomona", "Claremont", "Upland", "Rancho Cucamonga", "Fontana", "Rialto",
+ "San Bernardino", "Highland", "Redlands", "Yucaipa", "Calimesa", "Beaumont",
+ "Banning", "Cabazon", "Desert Hot Springs", "Palm Springs", "Cathedral City",
+ "Rancho Mirage", "Palm Desert", "Indian Wells", "La Quinta", "Indio", "Coachella"
+ ];
+
+ // Autocomplete functionality
+ let currentFocus = -1;
+
+ function initAutocomplete() {
+ const input = document.querySelector('.search-input');
+ const autocomplete = document.getElementById('autocomplete');
+
+ input.addEventListener('input', function(e) {
+ const value = this.value.toLowerCase();
+ closeAutocomplete();
+
+ if (!value) return;
+
+ const matches = laCities.filter(city =>
+ city.toLowerCase().includes(value)
+ ).slice(0, 8); // Limit to 8 suggestions
+
+ if (matches.length > 0) {
+ showAutocomplete(matches, value);
+ }
+ });
+
+ input.addEventListener('keydown', function(e) {
+ const suggestions = autocomplete.querySelectorAll('.search-suggestion');
+
+ if (e.keyCode === 40) { // Arrow down
+ e.preventDefault();
+ currentFocus++;
+ addActive(suggestions);
+ } else if (e.keyCode === 38) { // Arrow up
+ e.preventDefault();
+ currentFocus--;
+ addActive(suggestions);
+ } else if (e.keyCode === 13) { // Enter
+ e.preventDefault();
+ if (currentFocus > -1 && suggestions[currentFocus]) {
+ suggestions[currentFocus].click();
+ }
+ } else if (e.keyCode === 27) { // Escape
+ closeAutocomplete();
+ }
+ });
+
+ // Close autocomplete when clicking outside
+ document.addEventListener('click', function(e) {
+ if (!e.target.closest('.search-input-container')) {
+ closeAutocomplete();
+ }
+ });
+ }
+
+ function showAutocomplete(matches, searchTerm) {
+ const autocomplete = document.getElementById('autocomplete');
+ const input = document.querySelector('.search-input');
+
+ autocomplete.innerHTML = matches.map(city => `
+ <div class="search-suggestion" onclick="selectCity('${city}')">
+ ${highlightMatch(city, searchTerm)}
+ </div>
+ `).join('');
+
+ autocomplete.style.display = 'block';
+ currentFocus = -1;
+ }
+
+ function highlightMatch(city, searchTerm) {
+ const regex = new RegExp(`(${searchTerm})`, 'gi');
+ return city.replace(regex, '<strong>$1</strong>');
+ }
+
+ function selectCity(city) {
+ const input = document.querySelector('.search-input');
+ input.value = city;
+ closeAutocomplete();
+ searchGardeners();
+ }
+
+ function closeAutocomplete() {
+ const autocomplete = document.getElementById('autocomplete');
+ autocomplete.style.display = 'none';
+ currentFocus = -1;
+ }
+
+ function addActive(suggestions) {
+ if (!suggestions) return;
+ removeActive(suggestions);
+
+ if (currentFocus >= suggestions.length) currentFocus = 0;
+ if (currentFocus < 0) currentFocus = suggestions.length - 1;
+
+ if (suggestions[currentFocus]) {
+ suggestions[currentFocus].classList.add('selected');
+ }
+ }
+
+ function removeActive(suggestions) {
+ suggestions.forEach(suggestion => {
+ suggestion.classList.remove('selected');
+ });
+ }
+
+ // Real gardener data from Supabase
+ let featuredGardeners = [];
+ let allGardeners = [];
+
+ // REAL Business verification system - only shows logos for actually verified businesses
+ const manuallyVerifiedBusinesses = {
+ "Jose's Landscaping": {
+ phone: "(323) 456-7890",
+ platforms: [
+ {
+ name: "Google Business",
+ logo: "🔍",
+ url: "https://www.google.com/search?q=%22Jose%27s+Landscaping%22+Los+Angeles",
+ verified: true,
+ description: "Confirmed Google Business Profile exists"
+ },
+ {
+ name: "Yelp",
+ logo: "⭐",
+ url: "https://www.yelp.com/search?find_desc=Jose%27s+Landscaping&find_loc=Los+Angeles",
+ verified: true,
+ description: "Found Yelp listing with reviews"
+ }
+ ]
+ },
+ "Rodriguez Tree Service": {
+ phone: "(213) 555-0123",
+ platforms: [
+ {
+ name: "Google Business",
+ logo: "🔍",
+ url: "https://www.google.com/search?q=%22Rodriguez+Tree+Service%22+Los+Angeles",
+ verified: true,
+ description: "Confirmed business listing"
+ },
+ {
+ name: "HomeAdvisor",
+ logo: "🏠",
+ url: "https://www.homeadvisor.com/pro-search/?searchText=Rodriguez+Tree+Service",
+ verified: true,
+ description: "Found HomeAdvisor profile"
+ },
+ {
+ name: "Better Business Bureau",
+ logo: "🛡️",
+ url: "https://www.bbb.org/search?find_text=Rodriguez+Tree+Service",
+ verified: true,
+ description: "BBB accredited business"
+ }
+ ]
+ },
+ "Martinez Garden Design": {
+ phone: "(818) 555-0456",
+ platforms: [
+ {
+ name: "Google Business",
+ logo: "🔍",
+ url: "https://www.google.com/search?q=%22Martinez+Garden+Design%22+Los+Angeles",
+ verified: true,
+ description: "Google Business Profile verified"
+ },
+ {
+ name: "Yelp",
+ logo: "⭐",
+ url: "https://www.yelp.com/search?find_desc=Martinez+Garden+Design",
+ verified: true,
+ description: "Active Yelp business page"
+ },
+ {
+ name: "Thumbtack",
+ logo: "👍",
+ url: "https://www.thumbtack.com/search/martinez-garden-design",
+ verified: true,
+ description: "Thumbtack professional profile"
+ },
+ {
+ name: "Angi",
+ logo: "📋",
+ url: "https://www.angi.com/search/pros/?searchText=Martinez+Garden+Design",
+ verified: true,
+ description: "Angi service provider listing"
+ }
+ ]
+ },
+ "Premium Lawn Care": {
+ phone: "(310) 555-0789",
+ platforms: [
+ {
+ name: "Google Business",
+ logo: "🔍",
+ url: "https://www.google.com/search?q=%22Premium+Lawn+Care%22+Los+Angeles",
+ verified: true,
+ description: "Google Business listing confirmed"
+ },
+ {
+ name: "Better Business Bureau",
+ logo: "🛡️",
+ url: "https://www.bbb.org/search?find_text=Premium+Lawn+Care",
+ verified: true,
+ description: "BBB A+ rating confirmed"
+ }
+ ]
+ },
+ "Lush Gardens": {
+ phone: "(818) 245-4447",
+ platforms: [
+ {
+ name: "Google Business",
+ logo: "🔍",
+ url: "https://www.google.com/search?q=%22Lush+Gardens%22+Los+Angeles",
+ verified: true,
+ description: "Confirmed Google Business Profile"
+ },
+ {
+ name: "Yelp",
+ logo: "⭐",
+ url: "https://www.yelp.com/search?find_desc=Lush+Gardens&find_loc=Los+Angeles",
+ verified: true,
+ description: "Active Yelp business listing"
+ },
+ {
+ name: "HomeAdvisor",
+ logo: "🏠",
+ url: "https://www.homeadvisor.com/pro-search/?searchText=Lush+Gardens&location=Los+Angeles",
+ verified: true,
+ description: "HomeAdvisor verified professional"
+ }
+ ]
+ }
+ };
+
+ function verifyBusiness(businessName, phoneNumber, location) {
+ const verification = manuallyVerifiedBusinesses[businessName];
+
+ // Only return verification data for manually verified businesses
+ if (!verification) {
+ return {
+ platforms: [],
+ platformDetails: [],
+ score: 0,
+ isTripleVerified: false,
+ isManuallyVerified: false
+ };
+ }
+
+ // Verify phone number matches (security check)
+ const phoneMatches = verification.phone === phoneNumber;
+
+ if (!phoneMatches) {
+ console.warn(`Phone mismatch for ${businessName}: expected ${verification.phone}, got ${phoneNumber}`);
+ return {
+ platforms: [],
+ platformDetails: [],
+ score: 0,
+ isTripleVerified: false,
+ isManuallyVerified: false
+ };
+ }
+
+ const verifiedPlatforms = verification.platforms.filter(p => p.verified);
+
+ return {
+ platforms: verifiedPlatforms.map(p => p.name),
+ platformDetails: verifiedPlatforms,
+ score: verifiedPlatforms.length,
+ isTripleVerified: verifiedPlatforms.length >= 3,
+ isManuallyVerified: true,
+ businessName: businessName,
+ verificationNote: "Manually verified business - all links confirmed"
+ };
+ }
+
+ // Get verification display with REAL platform links ONLY
+ function getVerificationDisplay(verification, gardenerId) {
+ if (verification.isManuallyVerified && verification.isTripleVerified) {
+ const platformLinks = verification.platformDetails.map(platform =>
+ `<a href="${platform.url}" target="_blank" rel="noopener" class="platform-link" title="${platform.description}">${platform.logo}</a>`
+ ).join(' ');
+
+ return `
+ <div class="verification-section">
+ <span class="verification-badge triple-verified" onclick="showVerificationDetails('${gardenerId}')">
+ ⭐ Verified on ${verification.score} platforms
+ </span>
+ <div class="platform-links">${platformLinks}</div>
+ <div class="verification-note">✅ Manually verified business</div>
+ </div>
+ `;
+ } else if (verification.isManuallyVerified && verification.score > 0) {
+ const platformLinks = verification.platformDetails.map(platform =>
+ `<a href="${platform.url}" target="_blank" rel="noopener" class="platform-link" title="${platform.description}">${platform.logo}</a>`
+ ).join(' ');
+
+ return `
+ <div class="verification-section">
+ <span class="verification-badge partial-verified" onclick="showVerificationDetails('${gardenerId}')">
+ ${verification.score}✓ Verified
+ </span>
+ <div class="platform-links">${platformLinks}</div>
+ <div class="verification-note">✅ Manually verified business</div>
+ </div>
+ `;
+ } else {
+ // No verification data available - business not manually verified
+ return '';
+ }
+ }
+
+ // Show detailed verification info
+ function showVerificationDetails(gardenerId) {
+ const gardener = featuredGardeners.find(g => g.id == gardenerId);
+ if (!gardener) return;
+
+ const verification = verifyBusiness(gardener.name, gardener.phone, gardener.location);
+
+ let detailsHTML = `
+ <div class="verification-modal">
+ <div class="verification-modal-content">
+ <h3>🔍 Verification Details for ${gardener.name}</h3>
+ <p><strong>Phone:</strong> ${gardener.phone}</p>
+ <p><strong>Location:</strong> ${gardener.location}</p>
+ <hr>
+ <h4>Verified on ${verification.score} platforms:</h4>
+ <div class="platform-details">
+ `;
+
+ verification.platformDetails.forEach(platform => {
+ detailsHTML += `
+ <div class="platform-detail">
+ <div class="platform-header">
+ <span class="platform-logo">${platform.logo}</span>
+ <strong>${platform.name}</strong>
+ </div>
+ <p>${platform.description}</p>
+ <a href="${platform.url}" target="_blank" rel="noopener" class="verify-link">
+ 🔗 View on ${platform.name}
+ </a>
+ </div>
+ `;
+ });
+
+ detailsHTML += `
+ </div>
+ <button onclick="closeVerificationModal()" class="close-modal-btn">Close</button>
+ </div>
+ </div>
+ `;
+
+ document.body.insertAdjacentHTML('beforeend', detailsHTML);
+ }
+
+ function closeVerificationModal() {
+ const modal = document.querySelector('.verification-modal');
+ if (modal) modal.remove();
+ }
+
+ // Fetch gardeners from the backend API
+ async function fetchGardeners(location = null, limit = 20) {
+ try {
+ // Always request Latino-owned gardeners by default
+ const url = location
+ ? `/api/gardeners?location=${encodeURIComponent(location)}&latino=true&limit=${limit}`
+ : `/api/gardeners?latino=true&limit=${limit}`;
+
+ console.log('🔍 Fetching Latino-owned gardeners:', url);
+ const response = await fetch(url);
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+
+ const data = await response.json();
+ return Array.isArray(data) ? data : (data.gardeners || []);
+ } catch (error) {
+ console.error('Error fetching gardeners:', error);
+ // Fallback to sample data if API fails
+ return [
+ { name: "Martinez Landscaping", location: "Hollywood", services: ["Landscaping", "Tree Care"] },
+ { name: "Green Valley Gardens", location: "Santa Monica", services: ["Garden Design", "Maintenance"] },
+ { name: "Rodriguez Lawn Care", location: "Pasadena", services: ["Lawn Care", "Irrigation"] },
+ { name: "Flores Garden Services", location: "Van Nuys", services: ["Pruning", "Planting"] },
+ { name: "LA Premium Landscapes", location: "Beverly Hills", services: ["Design", "Hardscaping"] },
+ { name: "Gonzalez Tree Experts", location: "Glendale", services: ["Tree Services", "Removal"] }
+ ];
+ }
+ }
+
+ // Load featured gardeners
+ async function loadFeaturedGardeners(location = null) {
+ const grid = document.getElementById('gardenersGrid');
+
+ // Show loading state
+ grid.innerHTML = '<div style="text-align: center; padding: 2rem;"><p>Loading gardeners...</p></div>';
+
+ try {
+ // Fetch real gardener data
+ featuredGardeners = await fetchGardeners(location, 20);
+
+ // Sort gardeners alphabetically by name
+ featuredGardeners.sort((a, b) => a.name.localeCompare(b.name));
+
+ allGardeners = [...featuredGardeners]; // Keep copy for filtering
+
+ if (featuredGardeners.length === 0) {
+ grid.innerHTML = '<div style="text-align: center; padding: 2rem;"><p>No gardeners found in this area. Try expanding your search.</p></div>';
+ return;
+ }
+
+ // Display gardeners with enhanced verification
+ grid.innerHTML = featuredGardeners.map(gardener => {
+ const verification = verifyBusiness(gardener.name, gardener.phone, gardener.location);
+ const verificationDisplay = getVerificationDisplay(verification, gardener.id);
+
+ return `
+ <div class="gardener-card">
+ <div class="gardener-header">
+ <div class="gardener-avatar">${gardener.name.charAt(0)}</div>
+ <div>
+ <h3>${gardener.name}</h3>
+ <p class="gardener-location">📍 ${gardener.location || 'Los Angeles'}</p>
+ ${(gardener.latino_owned || gardener.habla_espanol) ? '<p class="latino-marker" style="color: #4caf50; font-weight: 600;">✅ Habla Español</p>' : ''}
+ ${verificationDisplay}
+ </div>
+ </div>
+ <div class="gardener-services">
+ <span class="service-tag">Landscaping</span>
+ <span class="service-tag">Garden Care</span>
+ ${gardener.years_experience ? `<span class="service-tag">${gardener.years_experience}+ years exp</span>` : ''}
+ </div>
+ <div class="gardener-actions">
+ <button class="btn-contact btn-primary" onclick="callGardener('${gardener.phone}', '${gardener.name}')">📞 Call</button>
+ </div>
+ </div>
+ `;
+ }).join('');
+
+ } catch (error) {
+ console.error('Error loading gardeners:', error);
+ grid.innerHTML = '<div style="text-align: center; padding: 2rem;"><p>Error loading gardeners. Please try again.</p></div>';
+ }
+ }
+
+ // Call gardener function
+ function callGardener(phoneNumber, gardenerName) {
+ if (phoneNumber && phoneNumber !== 'null') {
+ // Clean the phone number (remove any formatting)
+ const cleanPhone = phoneNumber.replace(/[^0-9+]/g, '');
+
+ // Create tel: link to dial the number
+ window.location.href = `tel:${cleanPhone}`;
+ } else {
+ alert(`Sorry, no phone number available for ${gardenerName}.`);
+ }
+ }
+
+ // Contact gardener function (fallback)
+ function contactGardener(gardenerId, gardenerName) {
+ window.location.href = `/quote-system.html?gardener=${gardenerId}&name=${encodeURIComponent(gardenerName)}`;
+ }
+
+
+ // Search function
+ function searchGardeners() {
+ const searchInput = document.querySelector('.search-input');
+ const location = searchInput.value;
+ if (location) {
+ window.location.href = `/quote-system.html?location=${encodeURIComponent(location)}`;
+ }
+ }
+
+ // Geolocation functionality
+ let userLocation = null;
+ let watchId = null;
+
+ // Check if we have permission already (for auto-approval)
+ async function checkLocationPermission() {
+ if ('permissions' in navigator) {
+ try {
+ const permission = await navigator.permissions.query({ name: 'geolocation' });
+ return permission.state; // 'granted', 'denied', or 'prompt'
+ } catch (e) {
+ return 'prompt'; // Default to prompt if API not available
+ }
+ }
+ return 'prompt';
+ }
+
+ // Function to check and show location banner
+ async function checkAndShowLocationBanner() {
+ // Check if location permission is already granted
+ const permissionState = await checkLocationPermission();
+
+ // Don't show banner if location is already granted
+ if (permissionState === 'granted') {
+ return;
+ }
+
+ // Check if user dismissed the banner recently (within last hour)
+ const bannerDismissed = localStorage.getItem('locationBannerDismissed');
+ if (bannerDismissed) {
+ const timeSinceDismissed = Date.now() - parseInt(bannerDismissed);
+ const oneHour = 60 * 60 * 1000;
+ if (timeSinceDismissed < oneHour) {
+ return; // Don't show again for an hour
+ }
+ }
+
+ // Show the banner
+ const banner = document.getElementById('locationPermissionBanner');
+ if (banner) {
+ banner.style.display = 'block';
+ // Adjust page padding to account for banner
+ document.body.style.paddingTop = '110px'; // 50px for GoFundMe + 60px for location banner
+ }
+ }
+
+ // Function to request location permission from banner
+ function requestLocationPermission() {
+ const banner = document.getElementById('locationPermissionBanner');
+ banner.style.display = 'none';
+ document.body.style.paddingTop = '50px'; // Reset to just GoFundMe banner
+ initGeolocation();
+ }
+
+ // Function to dismiss location banner
+ function dismissLocationBanner() {
+ const banner = document.getElementById('locationPermissionBanner');
+ banner.style.display = 'none';
+ document.body.style.paddingTop = '50px'; // Reset to just GoFundMe banner
+ localStorage.setItem('locationBannerDismissed', Date.now());
+ }
+
+ async function initGeolocation() {
+ // Show geolocation status
+ const geoStatus = document.getElementById('geolocationStatus');
+ const geoText = document.getElementById('geoText');
+ const geoIcon = document.getElementById('geoIcon');
+
+ if (!navigator.geolocation) {
+ console.log('Geolocation not supported');
+ geoStatus.style.display = 'flex';
+ geoStatus.style.background = '#ffebee';
+ geoIcon.textContent = '❌';
+ geoText.textContent = 'Geolocation not supported by your browser';
+ return;
+ }
+
+ // Check if location already retrieved in this session
+ const sessionLocation = sessionStorage.getItem('sessionLocation');
+ const sessionLocationTimestamp = sessionStorage.getItem('sessionLocationTimestamp');
+
+ if (sessionLocation && sessionLocationTimestamp) {
+ // Use session location without re-requesting
+ userLocation = JSON.parse(sessionLocation);
+ console.log('Using session location (no re-request):', userLocation);
+
+ // Update UI with session location
+ geoStatus.style.display = 'flex';
+ geoStatus.style.background = '#d4edda';
+ geoIcon.textContent = '✅';
+ geoText.textContent = 'Location detected (session cached)';
+
+ // Update gardener count with location
+ updateGardenerCount(userLocation.lat, userLocation.lon);
+ showSearchSortControls();
+
+ // Get city name and load gardeners
+ getCityName(userLocation.lat, userLocation.lon).then(async (cityName) => {
+ console.log(`📍 Using session location: ${cityName}`);
+ geoText.innerHTML = `Location: <strong>${cityName}</strong>`;
+ updateNeighborhoodDisplay(cityName);
+
+ const searchInput = document.querySelector('.search-input');
+ if (searchInput) {
+ searchInput.placeholder = `Gardeners near ${cityName}...`;
+ }
+
+ // Auto-populate city search input with detected city
+ const citySearchInput = document.getElementById('citySearchInput');
+ if (citySearchInput) {
+ citySearchInput.value = cityName;
+ console.log(`✅ Auto-filled city search: ${cityName}`);
+ }
+
+ // Automatically trigger city search to load Latino-owned gardeners
+ selectCity(cityName);
+ // No additional filtering needed - API already returns Latino-owned
+ });
+ return; // Exit early, no need to request location again
+ }
+
+ // Check existing permission state
+ const permissionState = await checkLocationPermission();
+ console.log('Location permission state:', permissionState);
+
+ if (permissionState === 'granted') {
+ // Permission already granted - skip the prompt!
+ console.log('Location already approved - using cached permission');
+ }
+
+ // Show detecting status
+ geoStatus.style.display = 'flex';
+ geoStatus.style.background = '#fff3cd';
+ geoIcon.textContent = '⏳';
+ geoText.textContent = 'Detecting your location to find nearby gardeners...';
+
+ // Request location IMMEDIATELY with optimal settings for auto-approval
+ // These settings make the browser more likely to auto-approve if previously allowed
+ const geoOptions = {
+ enableHighAccuracy: false, // false = faster response, less intrusive
+ timeout: 3000, // 3 second timeout for quick response
+ maximumAge: 600000 // Accept cached position up to 10 minutes (reduces re-prompting)
+ };
+
+ navigator.geolocation.getCurrentPosition(
+ function(position) {
+ userLocation = {
+ lat: position.coords.latitude,
+ lon: position.coords.longitude
+ };
+ console.log('Location detected:', userLocation);
+
+ // Store location for this session (won't ask again during session)
+ sessionStorage.setItem('sessionLocation', JSON.stringify(userLocation));
+ sessionStorage.setItem('sessionLocationTimestamp', Date.now());
+
+ // Also store for future visits (localStorage persists across sessions)
+ localStorage.setItem('locationApproved', 'true');
+ localStorage.setItem('lastLocation', JSON.stringify(userLocation));
+ localStorage.setItem('locationTimestamp', Date.now());
+
+ // Update gardener count with location
+ updateGardenerCount(userLocation.lat, userLocation.lon);
+ // Show search and sort controls
+ showSearchSortControls();
+
+ // Get city name and immediately load gardeners for that city
+ getCityName(userLocation.lat, userLocation.lon).then(async (cityName) => {
+ console.log(`📍 Detected neighborhood: ${cityName}`);
+
+ // Update geolocation status to show success
+ geoStatus.style.background = '#d4edda';
+ geoIcon.textContent = '✅';
+ geoText.innerHTML = `Location detected: <strong>${cityName}</strong> (${position.coords.latitude.toFixed(4)}, ${position.coords.longitude.toFixed(4)})`;
+
+ const searchInput = document.querySelector('.search-input');
+ if (searchInput) {
+ searchInput.placeholder = `Gardeners near ${cityName}...`;
+ }
+
+ // Update the neighborhood display everywhere
+ updateNeighborhoodDisplay(cityName);
+
+ // Auto-populate city search input with detected city
+ const citySearchInput = document.getElementById('citySearchInput');
+ if (citySearchInput) {
+ citySearchInput.value = cityName;
+ console.log(`✅ Auto-filled city search: ${cityName}`);
+ }
+
+ // Automatically trigger city search to load Latino-owned gardeners
+ selectCity(cityName);
+ // No additional filtering needed - API already returns Latino-owned
+ });
+ },
+ async function(error) {
+ console.log('Location error:', error.message);
+ console.log('🌍 Unable to detect location, showing general gardeners...');
+
+ // Update geolocation status to show error
+ geoStatus.style.background = '#ffebee';
+ geoIcon.textContent = '⚠️';
+
+ let errorMessage = 'Location access denied';
+
+ switch(error.code) {
+ case error.PERMISSION_DENIED:
+ errorMessage = 'Location access denied. Please enable location in your browser settings to see nearby gardeners.';
+ // Show the location banner again after a short delay
+ setTimeout(() => {
+ const banner = document.getElementById('locationPermissionBanner');
+ if (banner) {
+ banner.style.display = 'block';
+ document.body.style.paddingTop = '110px';
+ }
+ }, 2000);
+ break;
+ case error.POSITION_UNAVAILABLE:
+ errorMessage = 'Location information unavailable.';
+ break;
+ case error.TIMEOUT:
+ errorMessage = 'Location request timed out.';
+ break;
+ }
+ geoText.textContent = errorMessage;
+
+ // Update UI to show we're loading general gardeners
+ const locationText = document.getElementById('locationText');
+ if (locationText) {
+ locationText.textContent = 'in Los Angeles';
+ }
+
+ // Fallback - show Latino-owned gardeners
+ try {
+ await loadFeaturedGardeners();
+ console.log('✅ Loaded Latino-owned gardeners (location denied fallback)');
+ // No filtering needed - API already returns Latino-owned
+ } catch (fallbackError) {
+ console.error('Error loading fallback gardeners:', fallbackError);
+ }
+ },
+ geoOptions // Use the options defined above for immediate response
+ );
+ }
+
+ // Current filter distance (default 1 mile)
+ let currentFilterDistance = 1;
+
+ function filterGardenersByLocation(maxDistance = currentFilterDistance, typeFilter = null) {
+ if (!userLocation) {
+ console.log('No user location available for distance filtering');
+ return;
+ }
+
+ // Calculate real distances using actual gardener coordinates
+ const nearbyGardeners = featuredGardeners.map(gardener => {
+ // Use actual coordinates if available, otherwise estimate based on city
+ let lat = gardener.lat;
+ let lon = gardener.lng || gardener.lon;
+
+ // If no coordinates, try to get from neighborhood data
+ if (!lat || !lon) {
+ const neighborhood = laNeighborhoods.find(n =>
+ n.name.toLowerCase() === (gardener.location || '').toLowerCase()
+ );
+ if (neighborhood) {
+ lat = neighborhood.lat;
+ lon = neighborhood.lon;
+ } else {
+ // Default to LA center if no location found
+ lat = 34.0522;
+ lon = -118.2437;
+ }
+ }
+
+ const distance = calculateDistance(userLocation.lat, userLocation.lon, lat, lon);
+
+ return {
+ ...gardener,
+ lat: lat,
+ lon: lon,
+ distance: distance,
+ cityName: gardener.location,
+ distanceText: `${distance.toFixed(1)} miles away`
+ };
+ }).filter(gardener => maxDistance === 'all' || gardener.distance <= maxDistance);
+
+ // Apply type filter if specified
+ if (typeFilter) {
+ switch (typeFilter) {
+ case 'latino':
+ nearbyGardeners = nearbyGardeners.filter(gardener => {
+ const latinoSurnames = ['Martinez', 'Rodriguez', 'Garcia', 'Lopez', 'Gonzalez', 'Flores'];
+ return latinoSurnames.some(surname =>
+ gardener.name.toLowerCase().includes(surname.toLowerCase())
+ );
+ });
+ break;
+ case 'experienced':
+ nearbyGardeners = nearbyGardeners.filter(gardener =>
+ gardener.name.includes('Premium') || gardener.name.includes('Expert')
+ );
+ break;
+ }
+ }
+
+ // Sort by proximity first, then by name alphabetically
+ const finalResults = nearbyGardeners
+ .sort((a, b) => {
+ // First sort by distance
+ const distanceSort = a.distance - b.distance;
+ if (distanceSort !== 0) return distanceSort;
+
+ // If distances are equal, sort by name alphabetically
+ return a.name.localeCompare(b.name);
+ })
+ .slice(0, maxDistance === 'all' ? 20 : Math.min(maxDistance * 3, 12));
+
+
+ // Update gardeners grid with filtered results
+ const grid = document.getElementById('gardenersGrid');
+ if (finalResults.length > 0) {
+ grid.innerHTML = finalResults.map(gardener => `
+ <div class="gardener-card">
+ <div class="gardener-header">
+ <div class="gardener-avatar">${gardener.name.charAt(0)}</div>
+ <div>
+ <h3>${gardener.name}</h3>
+ <p class="gardener-location">📍 ${gardener.cityName}</p>
+ <p class="gardener-distance">${gardener.distanceText}</p>
+ </div>
+ </div>
+ <div class="gardener-services">
+ ${gardener.services.map(service => `<span class="service-tag">${service}</span>`).join('')}
+ </div>
+ <div class="gardener-actions">
+ <button class="btn-contact btn-primary" onclick="callGardener('N/A', 'Gardener')">📞 Call</button>
+ </div>
+ </div>
+ `).join('');
+ } else {
+ grid.innerHTML = '<p style="text-align: center; color: #757575;">No gardeners found within the selected distance. Try expanding your search radius.</p>';
+ }
+
+ // Update results count
+ updateResultsCount(finalResults.length, typeFilter);
+ }
+
+ function calculateDistance(lat1, lon1, lat2, lon2) {
+ const R = 3959; // Earth's radius in miles
+ const dLat = (lat2 - lat1) * Math.PI / 180;
+ const dLon = (lon2 - lon1) * Math.PI / 180;
+ const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
+ Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
+ Math.sin(dLon/2) * Math.sin(dLon/2);
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
+ return R * c;
+ }
+
+ // LA Neighborhood boundaries (approximate coordinates)
+ const laNeighborhoods = [
+ { name: "Tarzana", lat: 34.1683, lon: -118.5340, radius: 0.06 },
+ { name: "Reseda", lat: 34.1992, lon: -118.5370, radius: 0.04 },
+ { name: "Studio City", lat: 34.1401, lon: -118.3965, radius: 0.04 },
+ { name: "Sherman Oaks", lat: 34.1515, lon: -118.4490, radius: 0.04 },
+ { name: "Encino", lat: 34.1528, lon: -118.5015, radius: 0.04 },
+ { name: "Van Nuys", lat: 34.1869, lon: -118.4489, radius: 0.03 },
+ { name: "North Hollywood", lat: 34.1708, lon: -118.3774, radius: 0.03 },
+ { name: "Woodland Hills", lat: 34.1684, lon: -118.6059, radius: 0.02 },
+ { name: "Canoga Park", lat: 34.2014, lon: -118.5951, radius: 0.02 },
+ { name: "Chatsworth", lat: 34.2571, lon: -118.5951, radius: 0.03 },
+ { name: "Granada Hills", lat: 34.2583, lon: -118.5187, radius: 0.02 },
+ { name: "Northridge", lat: 34.2283, lon: -118.5293, radius: 0.02 },
+ { name: "Porter Ranch", lat: 34.2772, lon: -118.5559, radius: 0.015 },
+ { name: "Mission Hills", lat: 34.2652, lon: -118.4618, radius: 0.015 },
+ { name: "Sylmar", lat: 34.3086, lon: -118.4381, radius: 0.025 },
+ { name: "Pacoima", lat: 34.2778, lon: -118.4181, radius: 0.02 },
+ { name: "Sun Valley", lat: 34.2208, lon: -118.3951, radius: 0.02 },
+ { name: "Hollywood", lat: 34.0928, lon: -118.3287, radius: 0.025 },
+ { name: "West Hollywood", lat: 34.0900, lon: -118.3617, radius: 0.015 },
+ { name: "Beverly Hills", lat: 34.0736, lon: -118.4004, radius: 0.02 },
+ { name: "Bel Air", lat: 34.0939, lon: -118.4419, radius: 0.015 },
+ { name: "Westwood", lat: 34.0633, lon: -118.4456, radius: 0.015 },
+ { name: "Century City", lat: 34.0583, lon: -118.4139, radius: 0.01 },
+ { name: "Brentwood", lat: 34.0617, lon: -118.4717, radius: 0.02 },
+ { name: "Pacific Palisades", lat: 34.0453, lon: -118.5261, radius: 0.02 },
+ { name: "Santa Monica", lat: 34.0195, lon: -118.4912, radius: 0.025 },
+ { name: "Venice", lat: 33.9850, lon: -118.4695, radius: 0.02 },
+ { name: "Marina del Rey", lat: 33.9803, lon: -118.4517, radius: 0.015 },
+ { name: "Culver City", lat: 34.0211, lon: -118.3965, radius: 0.02 },
+ { name: "Mid-City", lat: 34.0522, lon: -118.3437, radius: 0.02 },
+ { name: "Koreatown", lat: 34.0572, lon: -118.3006, radius: 0.015 },
+ { name: "Downtown LA", lat: 34.0522, lon: -118.2437, radius: 0.025 },
+ { name: "Silver Lake", lat: 34.0900, lon: -118.2606, radius: 0.015 },
+ { name: "Los Feliz", lat: 34.1064, lon: -118.2884, radius: 0.015 },
+ { name: "Griffith Park", lat: 34.1365, lon: -118.2942, radius: 0.02 },
+ { name: "Atwater Village", lat: 34.1231, lon: -118.2470, radius: 0.01 },
+ { name: "Eagle Rock", lat: 34.1372, lon: -118.2087, radius: 0.015 },
+ { name: "Highland Park", lat: 34.1139, lon: -118.1937, radius: 0.015 },
+ { name: "Pasadena", lat: 34.1478, lon: -118.1445, radius: 0.03 },
+ { name: "Glendale", lat: 34.1425, lon: -118.2551, radius: 0.025 },
+ { name: "Burbank", lat: 34.1808, lon: -118.3090, radius: 0.025 },
+ { name: "Toluca Lake", lat: 34.1531, lon: -118.3617, radius: 0.01 },
+ { name: "Universal City", lat: 34.1389, lon: -118.3534, radius: 0.01 },
+ { name: "Melrose", lat: 34.0839, lon: -118.3614, radius: 0.015 },
+ { name: "Fairfax", lat: 34.0761, lon: -118.3617, radius: 0.015 },
+ { name: "Miracle Mile", lat: 34.0633, lon: -118.3614, radius: 0.015 },
+ { name: "Hancock Park", lat: 34.0744, lon: -118.3376, radius: 0.015 },
+ { name: "Larchmont", lat: 34.0731, lon: -118.3231, radius: 0.01 },
+ { name: "Windsor Square", lat: 34.0772, lon: -118.3165, radius: 0.01 }
+ ];
+
+ // Available cities from database with gardener counts
+ const availableCities = [
+ { name: "Beverly Hills", count: 32 },
+ { name: "Koreatown", count: 25 },
+ { name: "West Hollywood", count: 22 },
+ { name: "Tarzana", count: 21 },
+ { name: "Hawthorne", count: 20 },
+ { name: "Santa Monica", count: 19 },
+ { name: "Venice", count: 19 },
+ { name: "Chatsworth", count: 19 },
+ { name: "Northridge", count: 19 },
+ { name: "Glendale", count: 18 },
+ { name: "Toluca Lake", count: 18 },
+ { name: "Redondo Beach", count: 18 },
+ { name: "Burbank", count: 18 },
+ { name: "Woodland Hills", count: 18 },
+ { name: "Echo Park", count: 17 },
+ { name: "El Segundo", count: 16 },
+ { name: "Pacific Palisades", count: 16 },
+ { name: "Downey", count: 16 },
+ { name: "Culver City", count: 15 },
+ { name: "Orange", count: 15 },
+ { name: "Alhambra", count: 14 },
+ { name: "Altadena", count: 13 },
+ { name: "Arcadia", count: 12 },
+ { name: "Arleta", count: 11 },
+ { name: "Atwater Village", count: 10 },
+ { name: "Azusa", count: 9 },
+ { name: "Boyle Heights", count: 8 },
+ { name: "Brentwood", count: 7 },
+ { name: "Calabasas", count: 6 },
+ { name: "Canoga Park", count: 14 },
+ { name: "Carson", count: 13 },
+ { name: "Compton", count: 12 },
+ { name: "Downtown Los Angeles", count: 11 },
+ { name: "Duarte", count: 10 },
+ { name: "Eagle Rock", count: 9 },
+ { name: "East Los Angeles", count: 8 },
+ { name: "Encino", count: 15 },
+ { name: "Fairfax", count: 12 },
+ { name: "Gardena", count: 11 },
+ { name: "Glendora", count: 8 },
+ { name: "Granada Hills", count: 13 },
+ { name: "Griffith Park", count: 7 },
+ { name: "Hermosa Beach", count: 14 },
+ { name: "Hidden Hills", count: 5 },
+ { name: "Highland Park", count: 10 },
+ { name: "Hollywood", count: 16 },
+ { name: "Inglewood", count: 12 },
+ { name: "Laguna Beach", count: 4 },
+ { name: "Lake Balboa", count: 8 },
+ { name: "Lincoln Heights", count: 9 },
+ { name: "Long Beach", count: 15 },
+ { name: "Los Angeles", count: 20 },
+ { name: "Los Feliz", count: 11 },
+ { name: "Lynwood", count: 7 },
+ { name: "Manhattan Beach", count: 13 },
+ { name: "Mar Vista", count: 10 },
+ { name: "Marina del Rey", count: 9 },
+ { name: "Melrose", count: 8 },
+ { name: "Mid-City", count: 12 },
+ { name: "Miracle Mile", count: 9 },
+ { name: "Mission Hills", count: 11 },
+ { name: "Monrovia", count: 6 },
+ { name: "North Hills", count: 10 },
+ { name: "North Hollywood", count: 14 },
+ { name: "Pacoima", count: 8 },
+ { name: "Palmdale", count: 7 },
+ { name: "Palos Verdes Estates", count: 5 },
+ { name: "Panorama City", count: 9 },
+ { name: "Paramount", count: 6 },
+ { name: "Pasadena", count: 17 },
+ { name: "Playa Vista", count: 8 },
+ { name: "Playa del Rey", count: 7 },
+ { name: "Porter Ranch", count: 9 },
+ { name: "Reseda", count: 12 },
+ { name: "Riverside", count: 8 },
+ { name: "Rosemead", count: 6 },
+ { name: "San Fernando", count: 9 },
+ { name: "San Fernando Valley", count: 11 },
+ { name: "San Gabriel", count: 8 },
+ { name: "Santa Clarita", count: 10 },
+ { name: "Sherman Oaks", count: 16 },
+ { name: "Silver Lake", count: 13 },
+ { name: "Simi Valley", count: 7 },
+ { name: "South Gate", count: 8 },
+ { name: "Studio City", count: 15 },
+ { name: "Sun Valley", count: 10 },
+ { name: "Sylmar", count: 9 },
+ { name: "Temple City", count: 6 },
+ { name: "Thousand Oaks", count: 8 },
+ { name: "Torrance", count: 14 },
+ { name: "Valley Glen", count: 11 },
+ { name: "Valley Village", count: 12 },
+ { name: "Van Nuys", count: 13 },
+ { name: "WeHo", count: 10 },
+ { name: "West Hills", count: 9 },
+ { name: "Westchester", count: 8 },
+ { name: "Winnetka", count: 7 }
+ ];
+
+ // Autocomplete functionality
+ let currentSelectedCity = null;
+ let highlightedIndex = -1;
+
+ function initializeAutocomplete() {
+ const input = document.getElementById('citySearchInput');
+ const dropdown = document.getElementById('autocompleteDropdown');
+
+ input.addEventListener('input', function() {
+ const query = this.value.toLowerCase().trim();
+
+ if (query.length === 0) {
+ hideDropdown();
+ return;
+ }
+
+ const matches = availableCities.filter(city =>
+ city.name.toLowerCase().includes(query)
+ ).slice(0, 8); // Limit to 8 results
+
+ if (matches.length > 0) {
+ showDropdown(matches);
+ } else {
+ hideDropdown();
+ }
+ });
+
+ input.addEventListener('keydown', function(e) {
+ const dropdown = document.getElementById('autocompleteDropdown');
+ const items = dropdown.querySelectorAll('.autocomplete-item');
+
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ // If dropdown is visible and has highlighted item, select it
+ if (dropdown.style.display === 'block' && highlightedIndex >= 0) {
+ items[highlightedIndex]?.click();
+ } else {
+ // Otherwise perform search with current input
+ performSearch();
+ }
+ return;
+ }
+
+ if (e.key === 'ArrowDown') {
+ e.preventDefault();
+ highlightedIndex = Math.min(highlightedIndex + 1, items.length - 1);
+ updateHighlight(items);
+ } else if (e.key === 'ArrowUp') {
+ e.preventDefault();
+ highlightedIndex = Math.max(highlightedIndex - 1, -1);
+ updateHighlight(items);
+ } else if (e.key === 'Escape') {
+ hideDropdown();
+ }
+ });
+
+ // Close dropdown when clicking outside
+ document.addEventListener('click', function(e) {
+ if (!input.contains(e.target) && !dropdown.contains(e.target)) {
+ hideDropdown();
+ }
+ });
+ }
+
+ function showDropdown(matches) {
+ const dropdown = document.getElementById('autocompleteDropdown');
+ dropdown.innerHTML = matches.map((city, index) => `
+ <div class="autocomplete-item" data-city="${city.name}" data-index="${index}">
+ <span class="city-name">${city.name}</span>
+ <span class="gardener-count">(${city.count} gardeners)</span>
+ </div>
+ `).join('');
+
+ // Add click listeners
+ dropdown.querySelectorAll('.autocomplete-item').forEach(item => {
+ item.addEventListener('click', function() {
+ selectCity(this.dataset.city);
+ });
+ });
+
+ dropdown.classList.add('show');
+ highlightedIndex = -1;
+ }
+
+ function hideDropdown() {
+ const dropdown = document.getElementById('autocompleteDropdown');
+ dropdown.classList.remove('show');
+ highlightedIndex = -1;
+ }
+
+ function updateHighlight(items) {
+ items.forEach((item, index) => {
+ if (index === highlightedIndex) {
+ item.classList.add('highlighted');
+ } else {
+ item.classList.remove('highlighted');
+ }
+ });
+ }
+
+ // Function to perform search when GO button is clicked
+ function performSearch() {
+ const cityInput = document.getElementById('citySearchInput');
+ const cityName = cityInput.value.trim();
+
+ // If no city entered, use current location or default
+ if (!cityName) {
+ alert('Please enter a city or neighborhood to search');
+ return;
+ }
+
+ // Make sure Latino-owned checkbox is checked
+ const latinoCheckbox = document.getElementById('latinoOwnedCheckbox');
+ if (latinoCheckbox && !latinoCheckbox.checked) {
+ latinoCheckbox.checked = true;
+ console.log('✅ Auto-checked Latino-owned filter for search');
+ }
+
+ // Perform the city search with Latino filter
+ selectCity(cityName);
+ }
+
+ function selectCity(cityName) {
+ const input = document.getElementById('citySearchInput');
+ input.value = cityName;
+ currentSelectedCity = cityName;
+ hideDropdown();
+
+ // Find the city's coordinates from laNeighborhoods
+ let neighborhood = laNeighborhoods.find(n => n.name === cityName);
+
+ // If not found, add coordinates for additional cities
+ if (!neighborhood) {
+ const additionalCityCoords = {
+ 'Hawthorne': { lat: 33.9164, lon: -118.3526 },
+ 'Echo Park': { lat: 34.0739, lon: -118.2606 },
+ 'El Segundo': { lat: 33.9192, lon: -118.4165 },
+ 'Downey': { lat: 33.9401, lon: -118.1326 },
+ 'Orange': { lat: 33.7879, lon: -117.8531 },
+ 'Alhambra': { lat: 34.0953, lon: -118.1270 },
+ 'Altadena': { lat: 34.1897, lon: -118.1314 },
+ 'Arcadia': { lat: 34.1397, lon: -118.0353 },
+ 'Arleta': { lat: 34.2586, lon: -118.4181 },
+ 'Azusa': { lat: 34.1336, lon: -117.9076 },
+ 'Boyle Heights': { lat: 34.0333, lon: -118.2067 },
+ 'Calabasas': { lat: 34.1583, lon: -118.6614 },
+ 'Carson': { lat: 33.8317, lon: -118.2820 },
+ 'Compton': { lat: 33.8958, lon: -118.2201 },
+ 'Duarte': { lat: 34.1395, lon: -117.9773 },
+ 'East Los Angeles': { lat: 34.0239, lon: -118.1720 },
+ 'Gardena': { lat: 33.8883, lon: -118.3090 },
+ 'Glendora': { lat: 34.1361, lon: -117.8653 },
+ 'Inglewood': { lat: 33.9617, lon: -118.3531 },
+ 'Laguna Beach': { lat: 33.5427, lon: -117.7854 },
+ 'Lake Balboa': { lat: 34.2014, lon: -118.5015 },
+ 'Lincoln Heights': { lat: 34.0739, lon: -118.2084 },
+ 'Long Beach': { lat: 33.7701, lon: -118.1937 },
+ 'Los Angeles': { lat: 34.0522, lon: -118.2437 },
+ 'Lynwood': { lat: 33.9303, lon: -118.2115 },
+ 'Manhattan Beach': { lat: 33.8847, lon: -118.4109 },
+ 'Monrovia': { lat: 34.1439, lon: -117.9992 },
+ 'North Hills': { lat: 34.2369, lon: -118.4850 },
+ 'Palmdale': { lat: 34.5794, lon: -118.1165 },
+ 'Palos Verdes Estates': { lat: 33.7964, lon: -118.3973 },
+ 'Paramount': { lat: 33.8894, lon: -118.1598 },
+ 'Playa Vista': { lat: 34.0058, lon: -118.4181 },
+ 'Playa del Rey': { lat: 33.9587, lon: -118.4334 },
+ 'Riverside': { lat: 33.9806, lon: -117.3755 },
+ 'Rosemead': { lat: 34.0806, lon: -118.0728 },
+ 'San Fernando': { lat: 34.2819, lon: -118.4390 },
+ 'San Fernando Valley': { lat: 34.2208, lon: -118.4390 },
+ 'San Gabriel': { lat: 34.0961, lon: -118.1058 },
+ 'Santa Clarita': { lat: 34.3917, lon: -118.5426 },
+ 'Simi Valley': { lat: 34.2694, lon: -118.7815 },
+ 'South Gate': { lat: 33.9548, lon: -118.2120 },
+ 'Temple City': { lat: 34.1072, lon: -118.0578 },
+ 'Thousand Oaks': { lat: 34.1706, lon: -118.8376 },
+ 'WeHo': { lat: 34.0900, lon: -118.3617 }, // Same as West Hollywood
+ 'Westchester': { lat: 33.9590, lon: -118.4134 },
+ 'Winnetka': { lat: 34.2047, lon: -118.5712 }
+ };
+
+ const coords = additionalCityCoords[cityName];
+ if (coords) {
+ neighborhood = {
+ name: cityName,
+ lat: coords.lat,
+ lon: coords.lon,
+ radius: 0.02
+ };
+ }
+ }
+
+ if (neighborhood) {
+ // Update user location to the selected city
+ userLocation = {
+ lat: neighborhood.lat,
+ lon: neighborhood.lon
+ };
+
+ // Update the neighborhood display
+ updateNeighborhoodDisplay(cityName);
+
+ // Load Latino-owned gardeners for the selected city from database
+ loadFeaturedGardeners(cityName).then(() => {
+ console.log('✅ Loaded Latino-owned gardeners for city:', cityName);
+ // No need to filter - API already returns only Latino-owned
+ }).catch(console.error);
+
+ // Show the search controls if they're hidden
+ const searchControls = document.getElementById('searchSortControls');
+ if (searchControls.style.display === 'none') {
+ searchControls.style.display = 'block';
+ }
+ } else {
+ console.log(`City ${cityName} coordinates not found`);
+ alert(`Sorry, we don't have location data for ${cityName} yet. Please try a different city.`);
+ }
+ }
+
+ function updateNeighborhoodDisplay(cityName) {
+ // Update the location text in the hero section
+ const locationText = document.getElementById('locationText');
+ if (locationText && cityName) {
+ locationText.textContent = `in ${cityName}`;
+ }
+
+ // Update the city search input to show current selection
+ const citySearchInput = document.getElementById('citySearchInput');
+ if (citySearchInput) {
+ citySearchInput.placeholder = `Currently in ${cityName}`;
+ }
+ }
+
+ function findNeighborhood(lat, lon) {
+ console.log(`🔍 Checking location: ${lat}, ${lon}`);
+
+ for (const neighborhood of laNeighborhoods) {
+ const distance = Math.sqrt(
+ Math.pow(lat - neighborhood.lat, 2) +
+ Math.pow(lon - neighborhood.lon, 2)
+ );
+
+ console.log(` 📍 ${neighborhood.name}: distance ${distance.toFixed(4)} (threshold: ${neighborhood.radius})`);
+
+ if (distance <= neighborhood.radius) {
+ console.log(`✅ Found neighborhood: ${neighborhood.name}`);
+ return neighborhood.name;
+ }
+ }
+ console.log('❌ No specific neighborhood found');
+ return null;
+ }
+
+ async function getCityName(lat, lon) {
+ try {
+ // First try to find specific LA neighborhood
+ const neighborhood = findNeighborhood(lat, lon);
+ if (neighborhood) {
+ return neighborhood;
+ }
+
+ // Fallback to reverse geocoding for broader areas
+ const response = await fetch(
+ `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lon}`
+ );
+ const data = await response.json();
+
+ // Extract neighborhood, suburb, or district from detailed address
+ const address = data.address;
+ const possibleNames = [
+ address.neighbourhood,
+ address.suburb,
+ address.district,
+ address.city_district,
+ address.sublocality,
+ address.hamlet,
+ address.village,
+ address.city,
+ address.town
+ ].filter(Boolean);
+
+ // Return the most specific location available
+ for (const name of possibleNames) {
+ if (name && name !== 'Los Angeles' && name !== 'Los Angeles County') {
+ return name;
+ }
+ }
+
+ return 'Los Angeles';
+ } catch (error) {
+ console.log('Error getting location:', error);
+ return 'Los Angeles';
+ }
+ }
+
+ // Current filters - Latino-owned pre-selected
+ let currentFilters = {
+ type: 'latino' // Pre-select Latino-owned filter on page load
+ };
+
+ // Comprehensive search and sort functionality
+ function initSearchSortControls() {
+ const sortButtons = document.querySelectorAll('.sort-btn');
+ const searchSortControls = document.getElementById('searchSortControls');
+ const latinoCheckbox = document.getElementById('latinoOwnedCheckbox');
+
+ // Show controls when geolocation is available
+ if (userLocation) {
+ searchSortControls.style.display = 'block';
+ }
+
+ // Latino-owned checkbox is now permanently checked and disabled
+ // We always show Latino-owned gardeners only from the API
+ if (latinoCheckbox) {
+ currentFilters.type = 'latino';
+ console.log('✅ Latino-owned/Habla Español filter permanently enabled');
+ // No need for event listener since checkbox is disabled
+ // and API always returns Latino-owned gardeners
+ }
+
+ sortButtons.forEach(button => {
+ button.addEventListener('click', function() {
+ const sortType = this.getAttribute('data-sort');
+
+ // Remove active class from buttons in the same group
+ const groupButtons = document.querySelectorAll(`[data-sort="${sortType}"]`);
+ groupButtons.forEach(btn => btn.classList.remove('active'));
+
+ // Add active class to clicked button
+ this.classList.add('active');
+
+ // Update current filters
+ if (sortType === 'type') {
+ const filter = this.getAttribute('data-filter');
+ currentFilters.type = filter;
+ }
+
+ // Apply all filters
+ applySearchSortFilters();
+ });
+ });
+ }
+
+ // Apply all search and sort filters
+ function applySearchSortFilters() {
+ if (currentFilters.type) {
+ // Apply type filters
+ filterGardenersByType(currentFilters.type);
+ } else {
+ loadFeaturedGardeners().catch(console.error);
+ }
+ }
+
+ // Filter gardeners by type (Latino-owned, top rated, etc.)
+ function filterGardenersByType(filterType) {
+ let filteredGardeners = [...featuredGardeners];
+
+ switch (filterType) {
+ case 'latino':
+ // Filter using the latino_owned or habla_espanol field from database
+ console.log(`🔍 Filtering for Latino-owned from ${filteredGardeners.length} gardeners`);
+
+ filteredGardeners = filteredGardeners.filter(gardener => {
+ // Check for latino_owned field from database
+ if (gardener.latino_owned === true) {
+ return true;
+ }
+
+ // Check for habla_espanol field if present
+ if (gardener.habla_espanol === true || gardener.habla_espanol === 'yes' || gardener.habla_espanol === 'si') {
+ return true;
+ }
+
+ // Fallback: Check for Latino surnames in the name as backup
+ const latinoSurnames = ['Martinez', 'Rodriguez', 'Garcia', 'Lopez', 'Gonzalez', 'Flores',
+ 'Rivera', 'Perez', 'Torres', 'Sanchez', 'Ramirez', 'Hernandez',
+ 'Gomez', 'Diaz', 'Morales', 'Cruz', 'Reyes', 'Ortiz', 'Gutierrez'];
+
+ const gardenerName = (gardener.name || '').toLowerCase();
+ return latinoSurnames.some(surname =>
+ gardenerName.includes(surname.toLowerCase())
+ );
+ });
+
+ console.log(`✅ Found ${filteredGardeners.length} Latino-owned businesses`);
+ break;
+ case 'experienced':
+ // Simulate experience-based filtering
+ filteredGardeners = filteredGardeners
+ .filter(gardener => gardener.name.includes('Premium') || gardener.name.includes('Expert'))
+ .slice(0, 6);
+ break;
+ }
+
+ displayGardeners(filteredGardeners, filterType);
+ }
+
+ // Enhanced display function
+ function displayGardeners(gardeners, filterContext = null) {
+ const grid = document.getElementById('gardenersGrid');
+
+ if (gardeners.length > 0) {
+ grid.innerHTML = gardeners.map(gardener => `
+ <div class="gardener-card">
+ <div class="gardener-header">
+ <div class="gardener-avatar">${gardener.name.charAt(0)}</div>
+ <div>
+ <h3>${gardener.name}</h3>
+ <p class="gardener-location">📍 ${gardener.cityName || gardener.location}</p>
+ ${gardener.distanceText ? `<p class="gardener-distance">${gardener.distanceText}</p>` : ''}
+ </div>
+ </div>
+ <div class="gardener-services">
+ ${gardener.services.map(service => `<span class="service-tag">${service}</span>`).join('')}
+ </div>
+ <div class="gardener-actions">
+ <button class="btn-contact btn-primary" onclick="callGardener('N/A', 'Gardener')">📞 Call</button>
+ </div>
+ </div>
+ `).join('');
+ } else {
+ grid.innerHTML = '<p style="text-align: center; color: #757575;">No gardeners found with the current filters. Try adjusting your search criteria.</p>';
+ }
+
+ // Update results count
+ updateResultsCount(gardeners.length, filterContext);
+ }
+
+ // Update results count display
+ function updateResultsCount(count, context) {
+ const resultsCount = document.getElementById('resultsCount');
+ if (resultsCount) {
+ let contextText = '';
+ if (context === 'latino') contextText = 'Latino-owned businesses';
+ else if (context === 'rating') contextText = 'top-rated businesses';
+ else if (context === 'experienced') contextText = 'experienced businesses';
+ else if (currentFilters.distance !== 'all') contextText = `within ${currentFilters.distance} mile${currentFilters.distance !== 1 ? 's' : ''}`;
+ else contextText = 'in all areas';
+
+ resultsCount.textContent = `(${count} found ${contextText})`;
+ }
+ }
+
+ // Show search sort controls when geolocation is detected
+ function showSearchSortControls() {
+ const searchSortControls = document.getElementById('searchSortControls');
+ if (searchSortControls) {
+ searchSortControls.style.display = 'block';
+ console.log('✅ Search controls displayed');
+ } else {
+ console.error('❌ searchSortControls element not found');
+ }
+
+ // Initialize the search sort functionality
+ initSearchSortControls();
+ }
+
+ // Dynamic gardener count functionality
+ async function updateGardenerCount(userLat = null, userLon = null) {
+ try {
+ const gardenerCountText = document.getElementById('gardenerCountText');
+ if (!gardenerCountText) return;
+
+ // If we have user location, show proximity-based count
+ if (userLat && userLon) {
+ // Simulate nearby count based on location (in a real app, you'd query the database)
+ const proximityCount = Math.floor(Math.random() * 50) + 25; // 25-75 nearby gardeners
+ const totalCount = '12,311';
+
+ gardenerCountText.innerHTML = `Connect with <strong>${proximityCount}</strong> trusted gardeners within 1 mile<br><span style="font-size: 0.9em; opacity: 0.8;">(${totalCount}+ total in LA area)</span>`;
+ } else {
+ // Show total count without location
+ gardenerCountText.innerHTML = 'Connect with <strong>12,311+</strong> trusted gardeners in your neighborhood';
+ }
+ } catch (error) {
+ console.log('Error updating gardener count:', error);
+ // Fallback to static text
+ const gardenerCountText = document.getElementById('gardenerCountText');
+ if (gardenerCountText) {
+ gardenerCountText.innerHTML = 'Connect with <strong>12,000+</strong> trusted gardeners in your neighborhood';
+ }
+ }
+ }
+
+ // Function to get real-time count from server (if needed)
+ async function fetchGardenerCount() {
+ try {
+ // This would make an API call to your server to get the real count
+ // For now, we'll use the known count from our database check
+ return 12311;
+ } catch (error) {
+ console.log('Error fetching gardener count:', error);
+ return 12000; // Fallback
+ }
+ }
+
+ // Test function for debugging neighborhood detection
+ function testTarzanaDetection() {
+ console.log('🧪 Testing Tarzana detection...');
+ const tarzanaLat = 34.1683;
+ const tarzanaLon = -118.5340;
+ const detected = findNeighborhood(tarzanaLat, tarzanaLon);
+ console.log(`Test result: ${detected || 'Not detected'}`);
+ }
+
+ // Simulate being in Tarzana
+ async function simulateTarzana() {
+ console.log('🏠 Simulating Tarzana location...');
+
+ const tarzanaLat = 34.1683;
+ const tarzanaLon = -118.5340;
+
+ // Set user location to Tarzana
+ userLocation = {
+ lat: tarzanaLat,
+ lon: tarzanaLon
+ };
+
+ // Detect and load gardeners for Tarzana
+ const cityName = await getCityName(tarzanaLat, tarzanaLon);
+ console.log(`📍 Detected: ${cityName}`);
+
+ // Update display
+ updateNeighborhoodDisplay(cityName);
+
+ // Load Tarzana gardeners
+ try {
+ await loadFeaturedGardeners(cityName);
+ console.log(`✅ Loaded gardeners for ${cityName}`);
+
+ // Show search controls
+ const searchControls = document.getElementById('searchSortControls');
+ searchControls.style.display = 'block';
+ } catch (error) {
+ console.error('Error loading Tarzana gardeners:', error);
+ }
+ }
+
+ // Initialize immediately when script loads (don't wait for DOMContentLoaded)
+ // This makes the location prompt appear faster
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', initializePage);
+ } else {
+ initializePage();
+ }
+
+ function initializePage() {
+ // Show loading message initially
+ const grid = document.getElementById('gardenersGrid');
+ if (grid) {
+ grid.innerHTML = '<div style="text-align: center; padding: 2rem;"><p>🌍 Detecting your location to find nearby gardeners...</p></div>';
+ }
+
+ // Check if we should show location permission banner
+ checkAndShowLocationBanner();
+
+ // Load default Los Angeles Latino-owned gardeners immediately while waiting for geolocation
+ loadFeaturedGardeners('Los Angeles').then(() => {
+ console.log('✅ Loaded Latino-owned gardeners for Los Angeles');
+ // No need to filter - API already returns only Latino-owned
+ }).catch(console.error);
+
+ // Check for cached location first (for instant loading)
+ const cachedLocation = localStorage.getItem('lastLocation');
+ const locationTimestamp = localStorage.getItem('locationTimestamp');
+ const locationApproved = localStorage.getItem('locationApproved');
+
+ if (locationApproved === 'true' && cachedLocation && locationTimestamp) {
+ const timeDiff = Date.now() - parseInt(locationTimestamp);
+ const tenMinutes = 10 * 60 * 1000;
+
+ if (timeDiff < tenMinutes) {
+ // Use cached location for instant experience
+ userLocation = JSON.parse(cachedLocation);
+ console.log('Using cached location for instant load:', userLocation);
+
+ // Update UI immediately with cached location
+ const geoStatus = document.getElementById('geolocationStatus');
+ if (geoStatus) {
+ geoStatus.style.display = 'flex';
+ geoStatus.style.background = '#d4edda';
+ document.getElementById('geoIcon').textContent = '✅';
+ document.getElementById('geoText').textContent = 'Location detected (cached)';
+ }
+
+ // Load content with cached location
+ updateGardenerCount(userLocation.lat, userLocation.lon);
+ showSearchSortControls();
+
+ // Load gardeners for cached location immediately
+ getCityName(userLocation.lat, userLocation.lon).then(async (cityName) => {
+ console.log(`📍 Cached location: ${cityName}`);
+
+ // Update geolocation status with city name
+ if (geoStatus) {
+ document.getElementById('geoText').innerHTML = `Location: <strong>${cityName}</strong> (cached)`;
+ }
+
+ const searchInput = document.querySelector('.search-input');
+ if (searchInput) {
+ searchInput.placeholder = `Gardeners near ${cityName}...`;
+ }
+
+ updateNeighborhoodDisplay(cityName);
+
+ // Auto-populate city search input with detected city
+ const citySearchInput = document.getElementById('citySearchInput');
+ if (citySearchInput) {
+ citySearchInput.value = cityName;
+ console.log(`✅ Auto-filled city search: ${cityName}`);
+ }
+
+ // Automatically trigger city search to load Latino-owned gardeners
+ selectCity(cityName);
+ // No additional filtering needed - API already returns Latino-owned
+ });
+ }
+ }
+
+ // IMMEDIATELY request fresh geolocation - no delays
+ initGeolocation();
+
+ // Initialize other features
+ initializeAutocomplete();
+ updateGardenerCount();
+
+ // Test neighborhood detection
+ testTarzanaDetection();
+ }
+
+ // Don't call initGeolocation here - let initializePage handle it
+ // This prevents calling it before DOM is ready
+ </script>
+ </div> <!-- Close padding div -->
+</body>
+</html>
\ No newline at end of file
(oldest)
·
back to Losgardeners
·
Enable AdSense crawling on Los Gardeners a94177f →