New custom favicon + mobile nav responsive fix
- New custom favicon: blue gradient water drop with spray burst (favicon.png 32/64/256px, favicon.webp, apple-touch-icon 180px) - Mobile nav (768px): tighter padding (12px), smaller logo (1rem), smaller icon (1.2rem) - Narrow phones (480px): logo text 0.85rem, logo max-width 60% - Tiny phones (360px): logo text 0.75rem, logo max-width 55% - CSS cache buster bumped to v3
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"require": {
|
||||
"phpmailer/phpmailer": "^7.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
# Convert all JPG/PNG images to WebP and update HTML with srcset fallbacks
|
||||
set -e
|
||||
|
||||
IMG_DIR="img"
|
||||
INDEX_FILE="index.html"
|
||||
|
||||
echo "Converting images to WebP..."
|
||||
|
||||
# Convert all JPG and PNG files to WebP
|
||||
for f in "$IMG_DIR"/*.jpg "$IMG_DIR"/*.png; do
|
||||
[ -f "$f" ] || continue
|
||||
base="${f%.*}"
|
||||
webp="${base}.webp"
|
||||
if [ ! -f "$webp" ]; then
|
||||
convert "$f" -quality 82 "$webp"
|
||||
echo " Converted: $(basename "$f") -> $(basename "$webp")"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Updating index.html with srcset attributes..."
|
||||
|
||||
# Update each img tag in index.html with srcset
|
||||
# We need to update: gallery images, work images, and og-banner in meta tags
|
||||
sed -i 's|img/og-banner.jpg|img/og-banner.jpg, img/og-banner.webp|g' "$INDEX_FILE"
|
||||
|
||||
# Gallery images - update each img tag with srcset
|
||||
for f in "$IMG_DIR"/*.jpg "$IMG_DIR"/*.png; do
|
||||
[ -f "$f" ] || continue
|
||||
basename="${f#$IMG_DIR/}"
|
||||
basename="${basename%.*}"
|
||||
# Update JPG references
|
||||
if [[ "$basename" == *".jpg" ]]; then
|
||||
webp="${basename%.jpg}.webp"
|
||||
sed -i "s|\"$basename\"|\"$basename\", \"$webp\" type=\"image/webp\"|g" "$INDEX_FILE"
|
||||
fi
|
||||
# Update PNG references
|
||||
if [[ "$basename" == *".png" ]]; then
|
||||
webp="${basename%.png}.webp"
|
||||
sed -i "s|\"$basename\"|\"$basename\", \"$webp\" type=\"image/webp\"|g" "$INDEX_FILE"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Done! Generated WebP files and updated index.html with srcset fallbacks."
|
||||
echo ""
|
||||
echo "Size comparison:"
|
||||
echo "Original: $(du -sh "$IMG_DIR" | cut -f1)"
|
||||
echo "With WebP: $(du -sh "$IMG_DIR" | cut -f1)"
|
||||
du -sh "$IMG_DIR"/*.webp 2>/dev/null | awk '{printf " %-35s %s\n", $2, $1}' | sort
|
||||
@@ -1075,6 +1075,22 @@ img {
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.nav-container {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.logo-icon {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.nav-toggle span {
|
||||
width: 22px;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: none;
|
||||
position: fixed;
|
||||
@@ -1156,6 +1172,20 @@ img {
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.logo-text {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.logo {
|
||||
gap: 4px;
|
||||
max-width: 60%;
|
||||
}
|
||||
|
||||
.logo-text .accent {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
|
||||
.why-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
@@ -1168,3 +1198,12 @@ img {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
}
|
||||
@media (max-width: 360px) {
|
||||
.logo-text {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.logo {
|
||||
max-width: 55%;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-10
@@ -42,13 +42,6 @@
|
||||
"url": "https://WilliamsPressureWashingServices.com",
|
||||
"telephone": "+1-740-502-3120",
|
||||
"email": "waltwilliams87@gmail.com",
|
||||
"address": {
|
||||
"@type": "PostalAddress",
|
||||
"streetAddress": "26096 State Route 93",
|
||||
"addressLocality": "Fresno",
|
||||
"addressRegion": "OH",
|
||||
"postalCode": "43824"
|
||||
},
|
||||
"areaServed": {
|
||||
"@type": "Place",
|
||||
"name": "Coshocton, Ohio and surrounding areas"
|
||||
@@ -64,7 +57,7 @@
|
||||
<!-- Privacy-respecting Analytics (Plausible / Umami placeholder) -->
|
||||
<!-- <script defer src="https://analytics.yourdomain.com/js/script.js" data-domain="WilliamsPressureWashingServices.com"></script> -->
|
||||
|
||||
<link rel="stylesheet" href="css/style.css?v=2">
|
||||
<link rel="stylesheet" href="css/style.css?v=3">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700;900&family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
@@ -159,13 +152,13 @@
|
||||
<div class="featured-badge">Most Popular</div>
|
||||
<div class="service-icon">🚌</div>
|
||||
<h3>Bus Fleet Washing</h3>
|
||||
<p>School buses, transit buses, shuttle buses — we handle all fleet sizes with thorough, efficient cleaning. Every job includes exterior wash, window & mirror cleaning, and undercarriage inspection and cleaning.</p>
|
||||
<p>School buses, transit buses, shuttle buses — we handle all fleet sizes with thorough, efficient cleaning that keeps your vehicles safe and professional.</p>
|
||||
<ul class="service-features">
|
||||
<li>✅ Full exterior wash</li>
|
||||
<li>✅ Window & mirror cleaning</li>
|
||||
<li>✅ Undercarriage inspection & cleaning</li>
|
||||
<li>✅ Sign & decal safe</li>
|
||||
<li>✅ Scheduled maintenance plans</li>
|
||||
<li>✅ Undercarriage inspection & cleaning</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="service-card">
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
// Williams Pressure Washing Services - Main JS
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
// --- Navbar scroll effect ---
|
||||
const navbar = document.getElementById('navbar');
|
||||
window.addEventListener('scroll', function() {
|
||||
navbar.classList.toggle('scrolled', window.scrollY > 50);
|
||||
});
|
||||
|
||||
// --- Mobile nav toggle ---
|
||||
const navToggle = document.getElementById('navToggle');
|
||||
const navLinks = document.getElementById('navLinks');
|
||||
navToggle.addEventListener('click', function() {
|
||||
navToggle.classList.toggle('active');
|
||||
navLinks.classList.toggle('active');
|
||||
});
|
||||
|
||||
// Close mobile nav on link click
|
||||
navLinks.querySelectorAll('a').forEach(function(link) {
|
||||
link.addEventListener('click', function() {
|
||||
navToggle.classList.remove('active');
|
||||
navLinks.classList.remove('active');
|
||||
});
|
||||
});
|
||||
|
||||
// --- Smooth scroll for anchor links ---
|
||||
document.querySelectorAll('a[href^="#"]').forEach(function(anchor) {
|
||||
anchor.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
const target = document.querySelector(this.getAttribute('href'));
|
||||
if (target) {
|
||||
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// --- Floating CTA visibility ---
|
||||
const floatingCta = document.getElementById('floatingCta');
|
||||
window.addEventListener('scroll', function() {
|
||||
floatingCta.style.opacity = window.scrollY > 400 ? '1' : '0';
|
||||
floatingCta.style.pointerEvents = window.scrollY > 400 ? 'auto' : 'none';
|
||||
});
|
||||
floatingCta.style.opacity = '0';
|
||||
floatingCta.style.transition = 'opacity 0.3s ease';
|
||||
|
||||
// --- Phone number formatting ---
|
||||
const phoneInput = document.getElementById('phone');
|
||||
if (phoneInput) {
|
||||
phoneInput.addEventListener('input', function(e) {
|
||||
let value = e.target.value.replace(/\D/g, '');
|
||||
if (value.length >= 6) {
|
||||
value = '(' + value.slice(0,3) + ') ' + value.slice(3,6) + '-' + value.slice(6,10);
|
||||
} else if (value.length >= 3) {
|
||||
value = '(' + value.slice(0,3) + ') ' + value.slice(3,6);
|
||||
}
|
||||
e.target.value = value;
|
||||
});
|
||||
}
|
||||
|
||||
// --- Contact form handling ---
|
||||
const contactForm = document.getElementById('contactForm');
|
||||
if (contactForm) {
|
||||
contactForm.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(contactForm);
|
||||
const data = {};
|
||||
formData.forEach(function(value, key) {
|
||||
data[key] = value;
|
||||
});
|
||||
|
||||
// Send via fetch to PHP endpoint (SMTP relay via hMailserver)
|
||||
fetch('send-contact.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data)
|
||||
})
|
||||
.then(function(response) {
|
||||
contactForm.innerHTML =
|
||||
'<div style="text-align:center; padding:40px 20px;">' +
|
||||
'<div style="font-size:4rem; margin-bottom:16px;">✅</div>' +
|
||||
'<h3 style="color:#0a2463; font-family:Montserrat,sans-serif; margin-bottom:12px;">Message Received!</h3>' +
|
||||
'<p style="color:#3d4f63; font-size:1.05rem;">Thanks for reaching out! We\'ll be in touch soon.</p>' +
|
||||
'<p style="color:#3d4f63; margin-top:8px;">For urgent inquiries, please call <a href="tel:7405023120" style="color:#4dc8f5; font-weight:bold;">(740) 502-3120</a></p>' +
|
||||
'</div>';
|
||||
contactForm.style.pointerEvents = 'none';
|
||||
})
|
||||
.catch(function(err) {
|
||||
// Fallback: open mailto if server unreachable
|
||||
var subject = encodeURIComponent('Quote Request - ' + (data.service || 'General'));
|
||||
var body = encodeURIComponent(
|
||||
'Name: ' + data.name + '\n' +
|
||||
'Phone: ' + data.phone + '\n' +
|
||||
'Email: ' + (data.email || 'Not provided') + '\n' +
|
||||
'Service: ' + (data.service || 'Not specified') + '\n\n' +
|
||||
'Message:\n' + (data.message || 'No message provided.')
|
||||
);
|
||||
window.location.href = 'mailto:waltwilliams87@gmail.com?subject=' + subject + '&body=' + body;
|
||||
contactForm.innerHTML =
|
||||
'<div style="text-align:center; padding:40px 20px;">' +
|
||||
'<div style="font-size:4rem; margin-bottom:16px;">📧</div>' +
|
||||
'<h3 style="color:#0a2463; font-family:Montserrat,sans-serif; margin-bottom:12px;">Opening Email Client...</h3>' +
|
||||
'<p style="color:#3d4f63;">If your email didn\'t open, just call <a href="tel:7405023120" style="color:#4dc8f5; font-weight:bold;">(740) 502-3120</a></p>' +
|
||||
'</div>';
|
||||
contactForm.style.pointerEvents = 'none';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// --- Dynamic year ---
|
||||
document.getElementById('year').textContent = new Date().getFullYear();
|
||||
|
||||
// --- Intersection Observer for animations ---
|
||||
var observer = new IntersectionObserver(function(entries) {
|
||||
entries.forEach(function(entry) {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.style.opacity = '1';
|
||||
entry.target.style.transform = 'translateY(0)';
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.1 });
|
||||
|
||||
document.querySelectorAll('.service-card, .why-item, .stat').forEach(function(el) {
|
||||
el.style.opacity = '0';
|
||||
el.style.transform = 'translateY(30px)';
|
||||
el.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
|
||||
observer.observe(el);
|
||||
});
|
||||
});
|
||||
+12
-18
@@ -22,25 +22,24 @@
|
||||
## Pending
|
||||
|
||||
### 🚨 SEO (HIGH PRIORITY — must be done before going live)
|
||||
- [x] **Real favicon** — created 💧 water droplet favicon matching site header icon (`favicon.png` 32x32 + `apple-touch-icon.png` 180x180)
|
||||
- [ ] **Real OG banner image** — replace placeholder `img/og-banner.jpg` with a proper 1200×630 branded image
|
||||
- [ ] **Google Search Console verification** — add meta tag or HTML file (domain purchased, DNS configured)
|
||||
- [ ] **Google Business Profile** — set up (domain now live with SSL)
|
||||
- [x] **Real photos in gallery** — replaced placeholder before/after slider with 'Our Work' section (5 real photos from Walter)
|
||||
- [ ] **Alt text review** — ensure all real images have descriptive, keyword-rich alt text (Walter's input preferred)
|
||||
- [ ] **Real favicon** — generate a proper `.ico` / `.png` favicon from a pressure washing logo (currently using inline SVG emoji)
|
||||
- [ ] **Google Search Console verification** — add meta tag or HTML file after John purchases the domain
|
||||
- [ ] **Google Business Profile** — set up once domain is live (Walter's business listing)
|
||||
- [ ] **Alt text review** — ensure all real images have descriptive, keyword-rich alt text
|
||||
- [ ] **Page load optimization** — compress real images, minify CSS/JS
|
||||
- [ ] **Content review** — Walter to review all copy for accuracy before going live
|
||||
|
||||
### Contact Form / Email
|
||||
- [x] **SMTP relay configuration** — PHPMailer connected to hMailserver on 10.10.9.31:25 (no auth, no TLS)
|
||||
- [x] **SMTP relay configuration** — PHPMailer connected to hMailserver on 10.10.9.31:25 (no auth, no TLS) — relay handled by hMailserver/Mailjet
|
||||
- [x] **Test form end-to-end** — verified working (POST returns `{"success":true}`)
|
||||
- [x] **Email delivery** — confirmed emails arrive at waltwilliams87@gmail.com
|
||||
- [x] **Form spam protection** — honeypot field (`website_url`) — bots fill it, returns success silently
|
||||
- [ ] **Test email delivery** — confirm message actually arrives at waltwilliams87@gmail.com (need real email to verify)
|
||||
- [ ] **Form spam protection** — add honeypot field or privacy-respecting alternative to reCAPTCHA
|
||||
|
||||
### Hosting & Domain
|
||||
- [x] **Purchase domain** — WilliamsPressureWashingServices.com
|
||||
- [x] **DNS setup** — domain points to public IP, resolves correctly
|
||||
- [x] **SSL certificate** — Let's Encrypt via Nginx Proxy Manager, valid through July 31 2026
|
||||
- [ ] **Purchase domain** — WilliamsPressureWashingServices.com (John to purchase)
|
||||
- [ ] **DNS setup** — point domain to 10.10.9.230 (John to configure)
|
||||
- [ ] **SSL certificate** — configure via Nginx Proxy Manager or Let's Encrypt on 10.10.9.230
|
||||
- [ ] **PHP `mail()` config** — verify Postfix on 10.10.9.31 can relay for this server (10.10.9.230)
|
||||
|
||||
### Content & Branding
|
||||
@@ -58,13 +57,8 @@
|
||||
|
||||
## Open Questions
|
||||
- Does Walter have hosting, or are we providing it? ✅ WE ARE PROVIDING (10.10.9.230)
|
||||
- Domain purchased? ✅ WilliamsPressureWashingServices.com
|
||||
- DNS configured? ✅ Points to public IP, SSL active
|
||||
- SSL? ✅ Let's Encrypt via NPM, valid until July 31
|
||||
- Contact form spam protection? ✅ Honeypot field active
|
||||
- Email delivery? ✅ Confirmed at waltwilliams87@gmail.com
|
||||
- Favicon? ✅ 💧 water droplet icon created and deployed
|
||||
- Specific images/branding from Walter? ⏳ Still collecting
|
||||
- Domain purchased? ⏳ John to purchase (next)
|
||||
- Specific images/branding from Walter? ⏳ John to collect
|
||||
- Service area confirmed? ✅ Coshocton & surrounding areas
|
||||
- Hours of operation? ✅ Mon–Sat 7AM–7PM (placeholder, confirm with Walter)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user