Migrate to Stack A: PHP-fpm + nginx + supervisord, drop flat HTML + Python API

- Remove old flat HTML pages (index, about, blog, contact, reviews, services/*, locations/*)
- Remove Python/Flask API container (api/)
- Remove old root nginx.conf and components/
- Add infra/: full nginx.conf (http block at /etc/nginx/nginx.conf), php-fpm-pool.conf (TCP listen), supervisord.conf, entrypoint.sh (auto-generates ALTCHA_HMAC_KEY)
- Add src/: PHP router, page/service/location/blog templates, contact handler, altcha handler, promo endpoint, SQLite data files
- Rewrite Dockerfile: single container, tini PID 1, healthcheck, all env vars declared
- Update docker-compose.yml: port 8096, env_file, healthcheck
- Update .dockerignore: exclude .env.*, include robots.txt/sitemap.xml/404.html/500.html
- Update assets: tokens.css, promo-popup.css/js, altcha.min.js, refactored form.js/main.js

Verified: all 17 routes 200, protection audit PASS, Resend confirmed working

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Concept Agent
2026-05-29 18:56:56 +02:00
parent 88ed4e6bda
commit 81feccdc1a
61 changed files with 2460 additions and 5747 deletions
+12
View File
@@ -7,6 +7,18 @@ __pycache__
*.pyc
*.md
*.txt
.env
.env.*
!robots.txt
*.xml
!sitemap.xml
review_*.png
docker-compose.yml
.DS_Store
.planning/README.md
.planning/build_locations.py
.planning/build_services.py
.planning/__pycache__
.planning/review_*.png
.planning/DNS_*.txt
.planning/planning.db
+2
View File
@@ -0,0 +1,2 @@
RESEND_API_KEY=your_resend_api_key_here
ALTCHA_HMAC_KEY=your_altcha_hmac_key_here
+50
View File
@@ -0,0 +1,50 @@
# Floor It Hardwood Floors: Project Notes
## Client
Floor It Hardwood Floors
Buffalo, NY
Phone: (716) 602-1429
Email: floorithardwoods@gmail.com
## Stack
Stack B: Static HTML + vanilla JS + Python API (Resend) + Docker/nginx
This site predates the AM programmatic stack and is maintained as static per SOP.
## Dev
Port: 8096
Run: docker compose up -d
URL: http://localhost:8096
## Status (2026-05-27)
Launch Prep: in progress. SOP compliance fixes complete. Awaiting Docker smoke test and production deploy.
## Pages
- / index.html
- /about/
- /contact/
- /reviews/
- /blog/
- /services/ (floor-installation, refinishing, restoration, sanding)
- /locations/ (amherst, buffalo, clarence, east-amherst, lancaster, williamsville)
## API
Python form service at /api/. Handles contact form POST, sends via Resend.
API key in api/.env (gitignored).
## Key Decisions
- nginx: robots.txt and sitemap.xml served via exact-match location blocks before deny-all regex (2026-05-27)
- nginx: error_page 404 -> /404.html not /index.html (2026-05-27)
- nginx: _template.html files denied at the nginx layer (2026-05-27)
- Mobile nav breakpoint raised from 768px to 1023px. Header has 6 nav links + phone + CTA (2026-05-27)
- 360px ultra-narrow media query added for iPhone SE (2026-05-27)
- overflow-x: clip on html/body to prevent horizontal scroll (2026-05-27)
- residential.png converted to residential.webp via PIL. Original kept (2026-05-27)
## DNS (Cloudflare)
DMARC TXT record: v=DMARC1; p=none; rua=mailto:dev@arisingmedia.us
See DNS_DMARC_RECORD.txt for full instructions.
## Open Tasks
- Docker build + smoke test port 8096
- Production deploy: remove auth_basic from nginx.conf, update DNS
- tokens.css split from main.css (low priority)
+31 -14
View File
@@ -1,18 +1,35 @@
FROM nginx:alpine
FROM php:8.3-fpm-alpine
# nginx config (server-only, not served as a static file)
COPY nginx.conf /etc/nginx/conf.d/default.conf
RUN apk add --no-cache nginx supervisor curl openssl tini \
&& mkdir -p /run/nginx /var/cache/nginx /var/log/nginx /run/supervisord
# Copy only public website assets — everything else (api/, build scripts,
# Dockerfile, .env, docs, screenshots) stays out of the web root.
COPY index.html /usr/share/nginx/html/
COPY assets /usr/share/nginx/html/assets/
COPY components /usr/share/nginx/html/components/
COPY about /usr/share/nginx/html/about/
COPY blog /usr/share/nginx/html/blog/
COPY contact /usr/share/nginx/html/contact/
COPY locations /usr/share/nginx/html/locations/
COPY reviews /usr/share/nginx/html/reviews/
COPY services /usr/share/nginx/html/services/
COPY infra/php-fpm-pool.conf /usr/local/etc/php-fpm.d/www.conf
COPY infra/supervisord.conf /etc/supervisord.conf
COPY infra/nginx.conf /etc/nginx/nginx.conf
COPY infra/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
COPY assets /var/www/html/assets/
COPY src /var/www/html/src/
COPY robots.txt /var/www/html/robots.txt
COPY sitemap.xml /var/www/html/sitemap.xml
COPY 404.html /var/www/html/404.html
COPY 500.html /var/www/html/500.html
RUN chown -R www-data:www-data /var/www/html
ENV RESEND_API_KEY="" \
FROM_EMAIL="" \
TO_EMAIL="" \
ALTCHA_HMAC_KEY="" \
RATE_LIMIT_PER_IP_PER_10MIN=5 \
TIME_MIN_SECONDS=3 \
TRUST_PROXY=1
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -fsS http://127.0.0.1/ > /dev/null || exit 1
ENTRYPOINT ["/entrypoint.sh", "/sbin/tini", "--"]
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf", "-n"]
-139
View File
@@ -1,139 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>About Floor It Hardwood Floors | Buffalo, NY</title>
<meta name="description" content="75 years of combined experience serving Western New York. Learn about Floor It Hardwood Floors, licensed, insured, and dedicated to quality in Buffalo and Erie County.">
<link rel="canonical" href="https://floorithardwoodfloors.com/about/">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
</head>
<body>
<div id="site-header"></div>
<main>
<section class="page-hero">
<div class="container page-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">/</span>
<span>About</span>
</nav>
<span class="eyebrow">Our Story</span>
<h1>About Floor It Hardwood Floors</h1>
<p class="lead">Three decades of expertise, 75 years of combined experience, and an unwavering commitment to craftsmanship throughout Western New York.</p>
</div>
</section>
<!-- STORY -->
<section class="section section--light">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,5rem);align-items:center;">
<div data-animate="left">
<span class="eyebrow">Who We Are</span>
<h2>Craftsmanship is the Heart of Our Operation</h2>
<div class="divider"></div>
<p class="lead">We believe true craftsmanship combines skill, passion, and an unrelenting attention to detail.</p>
<p style="margin-top:1.25rem;color:var(--smoke);">At Floor It Hardwood Floors, we understand that every scratch has its story. After three decades in this business, we are experts at turning back the pages, breathing new life into your Buffalo home's flooring.</p>
<p style="margin-top:1rem;color:var(--smoke);">Our team brings 75 years of combined experience to every job. We are relentless in our pursuit of perfection, continually refining our skills and staying abreast of the latest trends and techniques in hardwood floor care.</p>
<p style="margin-top:1rem;color:var(--smoke);">We shine as the best pick for hardwood floor refinishing in Western New York. Your floors look new again, more beautiful and strong. When you choose us, you get a smooth process that boosts your home's worth and appearance.</p>
</div>
<div data-animate="right" style="background:var(--grain);border-radius:var(--radius-xl);overflow:hidden;aspect-ratio:4/5;display:flex;align-items:center;justify-content:center;">
<img src="/assets/images/refinishing-machine.webp" alt="Floor It professional equipment" style="width:100%;height:100%;object-fit:cover;" loading="lazy" onerror="this.style.display='none'">
</div>
</div>
</div>
</section>
<!-- CREDENTIALS -->
<section class="section section--mid">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Our Credentials</span>
<h2>Why Western NY Homeowners Trust Us</h2>
</div>
<div class="grid grid--auto-4">
<div style="text-align:center;padding:2rem;" data-animate="up">
<div style="font-size:3rem;font-weight:800;color:var(--amber);line-height:1;"><span data-count="75" data-suffix="+">75+</span></div>
<div style="font-size:var(--text-xs);font-weight:700;letter-spacing:0.1em;text-transform:uppercase;color:var(--ash);margin-top:0.5rem;">Years Combined Experience</div>
</div>
<div style="text-align:center;padding:2rem;" data-animate="up" data-delay="2">
<div style="font-size:3rem;font-weight:800;color:var(--amber);line-height:1;"><span data-count="500" data-suffix="+">500+</span></div>
<div style="font-size:var(--text-xs);font-weight:700;letter-spacing:0.1em;text-transform:uppercase;color:var(--ash);margin-top:0.5rem;">Projects Completed</div>
</div>
<div style="text-align:center;padding:2rem;" data-animate="up" data-delay="3">
<div style="font-size:3rem;font-weight:800;color:var(--amber);line-height:1;"><span data-count="4.9" data-suffix="/5">4.9/5</span></div>
<div style="font-size:var(--text-xs);font-weight:700;letter-spacing:0.1em;text-transform:uppercase;color:var(--ash);margin-top:0.5rem;">Google Rating</div>
</div>
<div style="text-align:center;padding:2rem;" data-animate="up" data-delay="4">
<div style="font-size:3rem;font-weight:800;color:var(--amber);line-height:1;">30+</div>
<div style="font-size:var(--text-xs);font-weight:700;letter-spacing:0.1em;text-transform:uppercase;color:var(--ash);margin-top:0.5rem;">Years Serving Buffalo</div>
</div>
</div>
</div>
</section>
<!-- SERVICE AREA -->
<section class="section section--alt">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Where We Work</span>
<h2>Serving Buffalo and Erie County</h2>
<p>We serve residential homeowners throughout Western New York with the same professional standards and care at every location.</p>
</div>
<div class="location-grid" data-animate="up">
<a href="/locations/buffalo/" class="location-card">
<div class="location-card-icon"><svg viewBox="0 0 20 20"><path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/></svg></div>
<div class="location-card-text"><strong>Buffalo, NY</strong><span>Primary hub</span></div>
</a>
<a href="/locations/amherst/" class="location-card">
<div class="location-card-icon"><svg viewBox="0 0 20 20"><path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/></svg></div>
<div class="location-card-text"><strong>Amherst, NY</strong><span>Erie County</span></div>
</a>
<a href="/locations/williamsville/" class="location-card">
<div class="location-card-icon"><svg viewBox="0 0 20 20"><path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/></svg></div>
<div class="location-card-text"><strong>Williamsville, NY</strong><span>Erie County</span></div>
</a>
<a href="/locations/east-amherst/" class="location-card">
<div class="location-card-icon"><svg viewBox="0 0 20 20"><path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/></svg></div>
<div class="location-card-text"><strong>East Amherst, NY</strong><span>Erie County</span></div>
</a>
<a href="/locations/clarence/" class="location-card">
<div class="location-card-icon"><svg viewBox="0 0 20 20"><path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/></svg></div>
<div class="location-card-text"><strong>Clarence, NY</strong><span>Erie County</span></div>
</a>
<a href="/locations/lancaster/" class="location-card">
<div class="location-card-icon"><svg viewBox="0 0 20 20"><path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/></svg></div>
<div class="location-card-text"><strong>Lancaster, NY</strong><span>Erie County</span></div>
</a>
</div>
</div>
</section>
<!-- CTA -->
<div class="cta-strip">
<div class="container">
<h2>Ready to Restore Your Floors?</h2>
<p>Contact our team today and take the first step toward beautiful hardwood floors.</p>
<div class="cta-group" style="justify-content:center;">
<a href="/contact/" class="btn btn--outline-dark btn--lg">Request an Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-dark btn--lg">Call (716) 602-1429</a>
</div>
</div>
</div>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
</body>
</html>
-6
View File
@@ -1,6 +0,0 @@
RESEND_API_KEY=re_your_key_here
RECAPTCHA_SECRET=your_recaptcha_v3_secret_here
TO_EMAIL=floorithardwoods@gmail.com
FROM_EMAIL=estimates@floorithardwoodfloors.com
RECAPTCHA_MIN=0.5
PORT=3001
-5
View File
@@ -1,5 +0,0 @@
FROM python:3.13-alpine
WORKDIR /app
COPY server.py .
EXPOSE 3001
CMD ["python3", "-u", "server.py"]
-225
View File
@@ -1,225 +0,0 @@
#!/usr/bin/env python3
"""
Floor It estimate-form API.
Pure Python 3 standard library only — no pip install, no packages.
Handles POST /estimate from the static site, validates fields,
verifies reCAPTCHA, sends via Resend, with idempotency + rate limiting.
"""
import hashlib
import http.server
import json
import os
import re
import socketserver
import time
import urllib.parse
import urllib.request
PORT = int(os.environ.get("PORT", "3001"))
RESEND_API_KEY = os.environ.get("RESEND_API_KEY", "")
RECAPTCHA_SECRET = os.environ.get("RECAPTCHA_SECRET", "")
TO_EMAIL = os.environ.get("TO_EMAIL", "floorithardwoodfloors@gmail.com")
FROM_EMAIL = os.environ.get("FROM_EMAIL", "Floor It Hardwood Floors <webleads@floorithardwoods.com>")
RECAPTCHA_MIN = float(os.environ.get("RECAPTCHA_MIN", "0.5"))
PHONE_RE = re.compile(r"^\(?\d{3}\)?[\s.\-]?\d{3}[\s.\-]?\d{4}$")
EMAIL_RE = re.compile(r"^[^\s@]+@[^\s@]+\.[^\s@]+$")
# In-memory rate limit: 5 requests / IP / 15 minutes
RATE_MAP = {}
RATE_WINDOW = 15 * 60
RATE_MAX = 5
def sanitize(s):
if not isinstance(s, str):
return ""
s = s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace('"', "&quot;")
return s.strip()[:2000]
def validate_fields(body):
errors = []
if not body.get("name") or len((body.get("name") or "").strip()) < 2:
errors.append("name")
if not body.get("email") or not EMAIL_RE.match((body.get("email") or "").strip()):
errors.append("email")
phone_clean = (body.get("phone") or "").replace(" ", "")
if not phone_clean or not PHONE_RE.match(phone_clean):
errors.append("phone")
if not body.get("address") or len((body.get("address") or "").strip()) < 3:
errors.append("address")
if not (body.get("service") or "").strip():
errors.append("service")
return errors
def verify_recaptcha(token):
if not RECAPTCHA_SECRET or not token:
return 0.0
data = urllib.parse.urlencode({"secret": RECAPTCHA_SECRET, "response": token}).encode()
req = urllib.request.Request(
"https://www.google.com/recaptcha/api/siteverify",
data=data,
headers={"Content-Type": "application/x-www-form-urlencoded"},
)
try:
with urllib.request.urlopen(req, timeout=8) as resp:
return float(json.loads(resp.read()).get("score", 0))
except Exception:
return 0.0
def send_via_resend(fields):
safe = {k: sanitize(fields.get(k, "")) for k in
["name", "email", "phone", "address", "city", "zip", "service", "condition", "message"]}
html = f"""<!DOCTYPE html>
<html><body style="font-family:Arial,sans-serif;color:#222;max-width:600px;margin:0 auto;padding:24px;">
<p>A new estimate request was submitted on floorithardwoodfloors.com.</p>
<table cellpadding="8" style="border-collapse:collapse;border:1px solid #e5e5e5;width:100%;">
<tr><td style="border:1px solid #e5e5e5;background:#f7f7f7;"><strong>Name</strong></td><td style="border:1px solid #e5e5e5;">{safe['name']}</td></tr>
<tr><td style="border:1px solid #e5e5e5;background:#f7f7f7;"><strong>Email</strong></td><td style="border:1px solid #e5e5e5;">{safe['email']}</td></tr>
<tr><td style="border:1px solid #e5e5e5;background:#f7f7f7;"><strong>Phone</strong></td><td style="border:1px solid #e5e5e5;">{safe['phone']}</td></tr>
<tr><td style="border:1px solid #e5e5e5;background:#f7f7f7;"><strong>Address</strong></td><td style="border:1px solid #e5e5e5;">{safe['address']}</td></tr>
<tr><td style="border:1px solid #e5e5e5;background:#f7f7f7;"><strong>City</strong></td><td style="border:1px solid #e5e5e5;">{safe['city']}</td></tr>
<tr><td style="border:1px solid #e5e5e5;background:#f7f7f7;"><strong>Zip</strong></td><td style="border:1px solid #e5e5e5;">{safe['zip']}</td></tr>
<tr><td style="border:1px solid #e5e5e5;background:#f7f7f7;"><strong>Service</strong></td><td style="border:1px solid #e5e5e5;">{safe['service']}</td></tr>
<tr><td style="border:1px solid #e5e5e5;background:#f7f7f7;"><strong>Floor Condition</strong></td><td style="border:1px solid #e5e5e5;">{safe['condition']}</td></tr>
<tr><td style="border:1px solid #e5e5e5;background:#f7f7f7;vertical-align:top;"><strong>Message</strong></td><td style="border:1px solid #e5e5e5;white-space:pre-wrap;">{safe['message']}</td></tr>
</table>
<p style="margin-top:24px;color:#666;font-size:13px;">Reply directly to this email to respond to {safe['name']}.</p>
</body></html>"""
text = (
"New estimate request from floorithardwoodfloors.com\n\n"
f"Name: {safe['name']}\n"
f"Email: {safe['email']}\n"
f"Phone: {safe['phone']}\n"
f"Address: {safe['address']}\n"
f"City: {safe['city']}\n"
f"Zip: {safe['zip']}\n"
f"Service: {safe['service']}\n"
f"Floor Condition: {safe['condition']}\n\n"
"Message:\n"
f"{safe['message']}\n\n"
f"Reply directly to this email to respond to {safe['name']}."
)
payload_obj = {
"from": FROM_EMAIL,
"to": [TO_EMAIL],
"reply_to": (fields.get("email") or "").strip(),
"subject": f"New estimate request: {safe['name']} ({safe['city'] or 'Buffalo'})",
"html": html,
"text": text,
}
payload = json.dumps(payload_obj).encode("utf-8")
idem_key = hashlib.sha256(payload).hexdigest()[:64]
req = urllib.request.Request(
"https://api.resend.com/emails",
data=payload,
headers={
"Authorization": f"Bearer {RESEND_API_KEY}",
"Content-Type": "application/json",
"Idempotency-Key": idem_key,
"User-Agent": "FloorIt-Estimate-Form/1.0",
},
)
try:
with urllib.request.urlopen(req, timeout=10) as resp:
if resp.status < 200 or resp.status >= 300:
raise RuntimeError(f"Resend {resp.status}: {resp.read().decode('utf-8', 'ignore')}")
except urllib.error.HTTPError as e:
body = e.read().decode("utf-8", "ignore")
raise RuntimeError(f"Resend {e.code}: {body}") from None
def rate_limit(ip):
now = time.time()
entry = RATE_MAP.get(ip, {"count": 0, "start": now})
if now - entry["start"] > RATE_WINDOW:
entry = {"count": 0, "start": now}
entry["count"] += 1
RATE_MAP[ip] = entry
return entry["count"] > RATE_MAX
class Handler(http.server.BaseHTTPRequestHandler):
def log_message(self, fmt, *args):
print(f"[api] {fmt % args}", flush=True)
def _json(self, code, obj):
body = json.dumps(obj).encode("utf-8")
self.send_response(code)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.send_header("Access-Control-Allow-Origin", "https://floorithardwoodfloors.com")
self.send_header("Access-Control-Allow-Methods", "POST, OPTIONS")
self.send_header("Access-Control-Allow-Headers", "Content-Type")
self.end_headers()
self.wfile.write(body)
def do_OPTIONS(self):
self.send_response(204)
self.send_header("Access-Control-Allow-Origin", "https://floorithardwoodfloors.com")
self.send_header("Access-Control-Allow-Methods", "POST, OPTIONS")
self.send_header("Access-Control-Allow-Headers", "Content-Type")
self.end_headers()
def do_GET(self):
if self.path == "/health":
self._json(200, {"ok": True})
return
self._json(404, {"error": "not found"})
def do_POST(self):
if self.path != "/estimate":
self._json(404, {"error": "not found"})
return
ip = self.headers.get("x-forwarded-for", "").split(",")[0].strip() or self.client_address[0]
if rate_limit(ip):
self._json(429, {"error": "Too many requests. Please call us at (716) 602-1429."})
return
length = int(self.headers.get("Content-Length", "0"))
if length > 16384:
self._json(413, {"error": "Payload too large."})
return
try:
raw = self.rfile.read(length).decode("utf-8")
body = json.loads(raw)
except Exception:
self._json(400, {"error": "Invalid request."})
return
errors = validate_fields(body)
if errors:
self._json(422, {"error": "Validation failed.", "fields": errors})
return
score = verify_recaptcha(body.get("token", ""))
if RECAPTCHA_SECRET and score < RECAPTCHA_MIN:
self._json(403, {"error": "Request could not be verified. Please try again or call us directly."})
return
try:
send_via_resend(body)
self._json(200, {"ok": True})
except Exception as e:
print(f"[estimate] {e}", flush=True)
self._json(500, {"error": "Could not send your message. Please call us at (716) 602-1429."})
class ThreadedHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
daemon_threads = True
allow_reuse_address = True
if __name__ == "__main__":
print(f"[api] listening on :{PORT}", flush=True)
ThreadedHTTPServer(("0.0.0.0", PORT), Handler).serve_forever()
+313 -18
View File
@@ -1,5 +1,5 @@
/* ============================================================
FLOOR IT HARDWOOD FLOORS Component Styles
FLOOR IT HARDWOOD FLOORS : Component Styles
components.css: header, footer, hero, cards, faq, gallery
============================================================ */
@@ -580,6 +580,8 @@
border: 1px solid var(--border-light);
transition: transform var(--transition), box-shadow var(--transition);
position: relative;
display: flex;
flex-direction: column;
}
.service-card:hover {
@@ -616,6 +618,7 @@
.service-card-body {
padding: var(--space-6);
flex: 1;
}
.service-card-body h3 {
@@ -680,7 +683,8 @@
z-index: 1;
}
.process-step-num {
.process-step-num,
.process-num {
width: 64px;
height: 64px;
background: var(--amber);
@@ -1228,6 +1232,213 @@
box-shadow: var(--shadow-lg);
}
.contact-detail {
display: flex;
align-items: baseline;
gap: var(--space-3);
padding: var(--space-3) 0;
border-bottom: 1px solid var(--border);
font-size: var(--text-sm);
}
.contact-detail:last-of-type { border-bottom: none; }
.contact-detail strong {
min-width: 130px;
font-weight: 700;
color: var(--ink);
flex-shrink: 0;
}
.contact-detail a { color: var(--bark); font-weight: 600; }
.contact-detail span { color: var(--smoke); }
/* About preview two-column layout */
.about-preview-inner {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-16);
align-items: center;
}
.about-preview-text .divider {
width: 48px;
height: 3px;
background: var(--amber);
border-radius: 2px;
margin: var(--space-4) 0 var(--space-5);
}
.about-preview-img {
border-radius: var(--radius-lg);
overflow: hidden;
aspect-ratio: 4/3;
}
.about-preview-img img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
/* About story two-column layout */
.about-story {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-16);
align-items: center;
}
.about-story-img {
border-radius: var(--radius-lg);
overflow: hidden;
aspect-ratio: 4/3;
}
.about-story-img img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
/* Service intro two-column layout */
.service-intro {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-12);
align-items: center;
}
.service-intro-img {
border-radius: var(--radius-lg);
overflow: hidden;
aspect-ratio: 4/3;
}
.service-intro-img img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
/* Location service cards */
.card--service-local {
padding: var(--space-8) var(--space-6);
}
.card--service-local h3 {
font-size: var(--text-xl);
color: var(--ink);
margin-bottom: var(--space-3);
}
.card--service-local p {
font-size: var(--text-sm);
color: var(--smoke);
line-height: 1.7;
}
/* Benefit item (service pages) */
.benefit-item {
background: var(--white);
border-radius: var(--radius-lg);
border: 1px solid var(--border-light);
padding: var(--space-8) var(--space-6);
transition: box-shadow var(--transition), transform var(--transition);
}
.benefit-item:hover {
box-shadow: var(--shadow);
transform: translateY(-2px);
}
.benefit-item h3 {
font-size: var(--text-xl);
color: var(--ink);
margin-bottom: var(--space-3);
}
.benefit-item p {
font-size: var(--text-sm);
color: var(--smoke);
line-height: 1.7;
}
/* Native details/summary FAQ */
details.faq-item {
border: 1px solid var(--border-light);
border-radius: var(--radius);
background: var(--white);
overflow: hidden;
}
details.faq-item + details.faq-item { margin-top: var(--space-2); }
details.faq-item summary {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-5) var(--space-6);
font-size: var(--text-base);
font-weight: 600;
color: var(--charcoal);
cursor: pointer;
list-style: none;
gap: var(--space-4);
}
details.faq-item summary::-webkit-details-marker { display: none; }
details.faq-item summary::after {
content: '+';
font-size: 1.25rem;
font-weight: 400;
color: var(--amber);
flex-shrink: 0;
transition: transform 0.2s ease;
}
details.faq-item[open] summary::after { transform: rotate(45deg); }
details.faq-item p {
padding: 0 var(--space-6) var(--space-5);
font-size: var(--text-sm);
color: var(--smoke);
line-height: 1.7;
margin: 0;
}
/* --- Location pills --------------------------------------- */
.locations-pill-list {
display: flex;
flex-wrap: wrap;
gap: var(--space-3);
justify-content: center;
margin-top: var(--space-10);
}
.location-pill {
display: inline-flex;
align-items: center;
padding: var(--space-3) var(--space-6);
background: var(--white);
border: 1.5px solid var(--border-light);
border-radius: 999px;
font-size: var(--text-sm);
font-weight: 600;
color: var(--bark);
box-shadow: 0 1px 4px rgba(0,0,0,0.07);
transition: background var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition);
}
.location-pill:hover {
background: var(--bark);
border-color: var(--bark);
color: var(--white);
box-shadow: 0 4px 12px rgba(0,0,0,0.12);
}
/* --- Credential stats ------------------------------------- */
.credential-item {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: var(--space-2);
padding: var(--space-8) var(--space-4);
}
.credential-item strong {
font-size: var(--text-4xl);
font-weight: 800;
color: var(--amber);
line-height: 1;
}
.credential-item span {
font-size: var(--text-sm);
color: var(--ash);
letter-spacing: 0.04em;
}
/* --- Responsive ------------------------------------------ */
@media (max-width: 1024px) {
.footer-grid { grid-template-columns: 1fr 1fr; gap: var(--space-8); }
@@ -1236,7 +1447,7 @@
.contact-layout { grid-template-columns: 1fr; }
}
/* Header: switch to mobile menu earlier desktop nav with logo + 6 links
/* Header: switch to mobile menu earlier : desktop nav with logo + 6 links
+ phone + CTA needs ~1024px to fit without overflowing. */
@media (max-width: 1023px) {
.header-nav { display: none; }
@@ -1268,7 +1479,7 @@
.header-logo-sub { display: none; }
}
/* Ultra-narrow phones (iPhone SE portrait, 320px) tighten header */
/* Ultra-narrow phones (iPhone SE portrait, 320px) : tighten header */
@media (max-width: 360px) {
.header-cta .btn--sm {
padding-inline: 0.75rem;
@@ -1326,7 +1537,7 @@
background: var(--border-dark);
}
/* Luxury about split larger image aspect */
/* Luxury about split : larger image aspect */
.about-img-wrap {
border-radius: var(--radius-xl);
overflow: hidden;
@@ -1363,7 +1574,7 @@
line-height: 1.1;
}
/* Process step numbers larger */
/* Process step numbers : larger */
.process-step-num {
width: 72px;
height: 72px;
@@ -1371,7 +1582,7 @@
box-shadow: 0 0 0 6px rgba(200,139,42,0.15);
}
/* FAQ premium border treatment */
/* FAQ : premium border treatment */
.faq-item {
border-left: 3px solid transparent;
transition: border-color var(--transition), box-shadow var(--transition);
@@ -1392,13 +1603,13 @@
border-color: rgba(200,139,42,0.25);
}
/* Contact form wrap elevated shadow */
/* Contact form wrap : elevated shadow */
.contact-form-wrap {
box-shadow: 0 32px 80px rgba(0,0,0,0.14), 0 8px 24px rgba(0,0,0,0.08);
}
/* ============================================================
LUXURY ELEVATION v2
LUXURY ELEVATION : v2
============================================================ */
/* Services 2×2 grid */
@@ -1419,7 +1630,7 @@
height: 300px;
}
/* Service card luxury border accent on hover */
/* Service card : luxury border accent on hover */
.services-grid-2x2 .service-card {
border-top: 3px solid transparent;
transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
@@ -1430,7 +1641,7 @@
box-shadow: 0 24px 60px rgba(0,0,0,0.12);
}
/* Testimonial cards richer treatment */
/* Testimonial cards : richer treatment */
.testimonial-card {
background: var(--white);
border-radius: var(--radius-lg);
@@ -1512,7 +1723,7 @@
letter-spacing: 0.05em;
}
/* Gallery grid full-bleed dramatic layout */
/* Gallery grid : full-bleed dramatic layout */
.gallery-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
@@ -1559,7 +1770,7 @@
color: var(--ink);
}
/* Location card luxury elevated */
/* Location card : luxury elevated */
.location-card {
display: flex;
align-items: center;
@@ -1598,7 +1809,7 @@
letter-spacing: 0.06em;
}
/* Section headers more dramatic */
/* Section headers : more dramatic */
.section-header h2,
.section-header--center h2 {
font-size: clamp(2rem, 4.5vw, 3.25rem);
@@ -1615,7 +1826,7 @@
color: var(--white);
}
/* Benefit icon amber filled circle */
/* Benefit icon : amber filled circle */
.benefit-icon {
width: 52px;
height: 52px;
@@ -1667,7 +1878,7 @@
}
/* ============================================================
Mobile responsive overrides inline grids must collapse
Mobile responsive overrides : inline grids must collapse
to single column on narrow viewports. Inline styles win over
CSS unless we use !important inside media queries.
============================================================ */
@@ -1705,7 +1916,7 @@ html, body {
gap: 1.5rem !important;
}
/* Order overrides when 2-col uses order:1/2 to flip image/content,
/* Order overrides : when 2-col uses order:1/2 to flip image/content,
reset on mobile so content always reads top-to-bottom */
[style*="order:1"],
[style*="order: 1"] {
@@ -1747,7 +1958,7 @@ html, body {
width: 100%;
}
/* Form/contact layout prevent intrinsic input width from blowing out
/* Form/contact layout : prevent intrinsic input width from blowing out
the grid track. Without min-width:0, <select> and <input> defaults push
the parent column wider than the viewport, causing horizontal scroll. */
.contact-layout { min-width: 0; }
@@ -1794,3 +2005,87 @@ html, body {
font-size: 1rem;
}
}
/* CTA banner sections with photo background */
.cta-section {
position: relative;
background-image: url('/assets/images/refinishing-machine.webp');
background-size: cover;
background-position: center;
color: var(--text-on-dark);
}
.cta-section::before {
content: '';
position: absolute;
inset: 0;
background: rgba(12, 8, 5, 0.78);
}
.cta-section-inner {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-8);
flex-wrap: wrap;
}
.cta-section-text h2 { color: var(--white); }
.cta-section-text p { color: rgba(240,232,218,0.8); max-width: 52ch; }
.cta-section .btn--primary {
flex-shrink: 0;
box-shadow: 0 4px 20px rgba(200,139,42,0.4);
}
@media (max-width: 768px) {
.cta-section-inner { flex-direction: column; text-align: center; }
.cta-section-text p { margin-inline: auto; }
}
/* Service grid cards (home page) */
.card--service {
display: flex;
flex-direction: column;
text-decoration: none;
color: var(--text-on-light);
}
.card--service .card-img-wrap {
height: 220px;
overflow: hidden;
flex-shrink: 0;
}
.card--service .card-img-wrap img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.card--service:hover .card-img-wrap img { transform: scale(1.05); }
.card--service .card-body {
padding: var(--space-6);
flex: 1;
display: flex;
flex-direction: column;
}
.card--service .card-body h3 {
font-size: var(--text-xl);
color: var(--charcoal);
margin-bottom: var(--space-3);
}
.card--service .card-body p {
font-size: var(--text-sm);
color: var(--smoke);
line-height: 1.65;
max-width: none;
flex: 1;
}
.card--service .card-link {
display: inline-flex;
align-items: center;
gap: var(--space-2);
font-size: var(--text-sm);
font-weight: 700;
color: var(--amber);
letter-spacing: 0.04em;
margin-top: var(--space-4);
transition: gap var(--transition);
}
.card--service:hover .card-link { gap: var(--space-3); }
+20 -118
View File
@@ -1,106 +1,9 @@
/* ============================================================
FLOOR IT HARDWOOD FLOORS Design System
main.css: variables, reset, typography, layout, utilities
============================================================ */
@import url('tokens.css');
/* --- Custom Properties ------------------------------------ */
:root {
/* Color palette — warm wood tones, no gradients */
--ink: #0c0805;
--charcoal: #1c1208;
--bark: #2e1d0a;
--bark-mid: #3d2710;
--amber: #c88b2a;
--amber-dark: #a87220;
--amber-light: #e8aa48;
--parchment: #f5f0e8;
--cream: #faf8f5;
--grain: #ede5d8;
--smoke: #7a6a56;
--ash: #b8a898;
--white: #ffffff;
/* FLOOR IT HARDWOOD FLOORS: Design System
main.css: reset, typography, layout, utilities */
/* Semantic aliases */
--bg-dark: var(--ink);
--bg-dark-alt: var(--charcoal);
--bg-mid-dark: var(--bark);
--bg-light: var(--cream);
--bg-light-alt: var(--parchment);
--bg-warm: var(--grain);
--text-on-dark: #f0e8da;
--text-muted-dark:var(--ash);
--text-on-light: var(--charcoal);
--text-muted-light:var(--smoke);
--cta: var(--amber);
--cta-hover: var(--amber-dark);
--cta-text: var(--ink);
--border-dark: rgba(255,255,255,0.08);
--border-light: rgba(0,0,0,0.08);
/* Typography */
--font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--font-display: 'Inter', Georgia, serif;
/* Scale */
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-md: 1.125rem;
--text-lg: 1.25rem;
--text-xl: 1.5rem;
--text-2xl: 2rem;
--text-3xl: 2.5rem;
--text-4xl: 3.25rem;
--text-5xl: 4.25rem;
--text-6xl: 5.5rem;
/* Spacing */
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 0.75rem;
--space-4: 1rem;
--space-5: 1.25rem;
--space-6: 1.5rem;
--space-8: 2rem;
--space-10: 2.5rem;
--space-12: 3rem;
--space-16: 4rem;
--space-20: 5rem;
--space-24: 6rem;
--space-32: 8rem;
/* Section rhythm */
--section-py: clamp(4rem, 8vw, 8rem);
--section-py-sm: clamp(2.5rem, 5vw, 5rem);
/* Layout */
--container-max: 1200px;
--container-wide: 1380px;
--container-px: clamp(1.25rem, 5vw, 2.5rem);
/* Effects */
--radius-sm: 4px;
--radius: 8px;
--radius-lg: 16px;
--radius-xl: 24px;
--radius-full: 9999px;
--shadow-sm: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.08);
--shadow: 0 4px 16px rgba(0,0,0,0.12), 0 2px 6px rgba(0,0,0,0.08);
--shadow-lg: 0 12px 40px rgba(0,0,0,0.18), 0 4px 12px rgba(0,0,0,0.10);
--shadow-xl: 0 24px 64px rgba(0,0,0,0.22);
--transition: 0.25s ease;
--transition-slow: 0.5s ease;
/* Header height */
--header-h: 72px;
}
/* --- Reset ------------------------------------------------ */
/* Reset */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
@@ -148,7 +51,7 @@ input, textarea, select {
font-size: inherit;
}
/* --- Typography ------------------------------------------- */
/* Typography */
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-display);
font-weight: 800;
@@ -215,9 +118,9 @@ p { max-width: 68ch; }
gap: var(--space-8);
}
.grid--2 { grid-template-columns: repeat(2, 1fr); }
.grid--3 { grid-template-columns: repeat(3, 1fr); }
.grid--4 { grid-template-columns: repeat(4, 1fr); }
.grid--2, .grid--2col { grid-template-columns: repeat(2, 1fr); }
.grid--3, .grid--3col { grid-template-columns: repeat(3, 1fr); }
.grid--4, .grid--4col { grid-template-columns: repeat(4, 1fr); }
.grid--auto-2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); }
.grid--auto-3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); }
@@ -253,7 +156,7 @@ p { max-width: 68ch; }
.section--mid .lead,
.section--bark .lead { color: var(--text-muted-dark); }
/* --- Buttons --------------------------------------------- */
/* Buttons */
.btn {
display: inline-flex;
align-items: center;
@@ -328,7 +231,7 @@ p { max-width: 68ch; }
align-items: center;
}
/* --- Forms ----------------------------------------------- */
/* Forms */
.form-grid {
display: grid;
gap: var(--space-5);
@@ -439,7 +342,7 @@ p { max-width: 68ch; }
.form-status--success { background: #d4edda; color: #155724; display: block; }
.form-status--error { background: #f8d7da; color: #721c24; display: block; }
/* --- Cards ----------------------------------------------- */
/* Cards */
.card {
background: var(--white);
border-radius: var(--radius-lg);
@@ -459,7 +362,7 @@ p { max-width: 68ch; }
color: var(--text-on-dark);
}
/* --- Scroll Animations ----------------------------------- */
/* Scroll Animations */
[data-animate] {
opacity: 0;
transition: opacity 0.7s ease, transform 0.7s ease;
@@ -482,7 +385,7 @@ p { max-width: 68ch; }
[data-delay="5"] { transition-delay: 0.5s; }
[data-delay="6"] { transition-delay: 0.6s; }
/* --- Utility Classes ------------------------------------- */
/* Utility Classes */
.text-center { text-align: center; }
.text-amber { color: var(--amber); }
.text-muted { color: var(--smoke); }
@@ -512,7 +415,7 @@ p { max-width: 68ch; }
border: 0;
}
/* --- Dividers -------------------------------------------- */
/* Dividers */
.divider {
width: 60px;
height: 3px;
@@ -524,22 +427,21 @@ p { max-width: 68ch; }
.section--mid .divider,
.section--bark .divider { background: var(--amber); }
/* --- Responsive ------------------------------------------ */
/* Responsive */
@media (max-width: 1024px) {
.grid--4 { grid-template-columns: repeat(2, 1fr); }
.grid--3 { grid-template-columns: repeat(2, 1fr); }
.grid--4, .grid--4col { grid-template-columns: repeat(2, 1fr); }
.grid--3, .grid--3col { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 768px) {
.grid--2,
.grid--3,
.grid--4 { grid-template-columns: 1fr; }
.grid--2, .grid--2col,
.grid--3, .grid--3col,
.grid--4, .grid--4col { grid-template-columns: 1fr; }
.cta-group { justify-content: center; }
.cta-group .btn { min-width: 220px; }
}
@media (max-width: 480px) {
:root { --section-py: clamp(3rem, 8vw, 4rem); }
p { max-width: 100%; }
}
+189
View File
@@ -0,0 +1,189 @@
/* FLOOR IT HARDWOOD FLOORS. Summer Promo Topbar + Popup */
/* Topbar */
#promo-topbar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1001;
height: 44px;
background: #c8a96e;
color: #1a1207;
display: none;
align-items: center;
justify-content: center;
gap: 0.75rem;
padding: 0 1rem;
font-size: 0.82rem;
font-weight: 600;
}
#promo-topbar.visible { display: flex; }
#promo-topbar-text { flex: 1; text-align: center; }
#promo-topbar-btn {
background: #1a1207;
color: #c8a96e;
border: none;
padding: 0.3rem 0.85rem;
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
border-radius: 2px;
cursor: pointer;
white-space: nowrap;
flex-shrink: 0;
}
#promo-topbar-btn:hover { background: #0c0805; }
#promo-topbar-close {
background: none;
border: none;
color: #1a1207;
font-size: 1.3rem;
cursor: pointer;
line-height: 1;
padding: 0;
opacity: 0.6;
flex-shrink: 0;
}
#promo-topbar-close:hover { opacity: 1; }
body.has-topbar #site-header { top: 44px; }
@media (max-width: 600px) {
#promo-topbar-text { font-size: 0.72rem; }
#promo-topbar { gap: 0.5rem; }
}
/* Popup */
#promo-overlay {
position: fixed;
inset: 0;
background: rgba(30, 20, 10, 0.72);
z-index: 9000;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
}
#promo-overlay.visible {
opacity: 1;
pointer-events: all;
}
#promo-box {
background: #fff;
border-radius: 4px;
max-width: 480px;
width: 100%;
padding: 2.5rem 2rem 2rem;
position: relative;
box-shadow: 0 8px 40px rgba(0,0,0,0.3);
}
#promo-box .promo-badge {
display: inline-block;
background: #c8a96e;
color: #fff;
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
padding: 0.25rem 0.75rem;
border-radius: 2px;
margin-bottom: 0.75rem;
}
#promo-box h2 {
font-size: 1.45rem;
line-height: 1.25;
color: #1a1207;
margin: 0 0 0.4rem;
}
#promo-box .promo-sub {
font-size: 0.9rem;
color: #6b5c3e;
margin: 0 0 1.5rem;
}
#promo-box .promo-field {
margin-bottom: 0.85rem;
}
#promo-box .promo-field label {
display: block;
font-size: 0.8rem;
font-weight: 600;
color: #1a1207;
margin-bottom: 0.3rem;
text-transform: uppercase;
letter-spacing: 0.06em;
}
#promo-box .promo-field input {
width: 100%;
padding: 0.65rem 0.85rem;
border: 1.5px solid #d5c9b6;
border-radius: 3px;
font-size: 0.95rem;
color: #1a1207;
background: #faf8f5;
box-sizing: border-box;
transition: border-color 0.2s;
}
#promo-box .promo-field input:focus {
outline: none;
border-color: #c8a96e;
}
#promo-box .promo-submit {
width: 100%;
padding: 0.85rem;
background: #c8a96e;
color: #fff;
border: none;
border-radius: 3px;
font-size: 1rem;
font-weight: 700;
letter-spacing: 0.04em;
cursor: pointer;
margin-top: 0.5rem;
transition: background 0.2s;
}
#promo-box .promo-submit:hover { background: #b5923d; }
#promo-box .promo-submit:disabled { background: #ccc; cursor: default; }
#promo-box .promo-error {
font-size: 0.85rem;
color: #b91c1c;
margin-top: 0.5rem;
display: none;
}
#promo-box .promo-success {
text-align: center;
padding: 1rem 0 0.5rem;
display: none;
}
#promo-box .promo-success p {
font-size: 1.05rem;
color: #1a1207;
margin: 0.5rem 0 0;
}
#promo-close {
position: absolute;
top: 0.85rem;
right: 1rem;
background: none;
border: none;
font-size: 1.5rem;
color: #9a8a72;
cursor: pointer;
line-height: 1;
padding: 0;
}
#promo-close:hover { color: #1a1207; }
#promo-box .promo-fine {
font-size: 0.72rem;
color: #9a8a72;
text-align: center;
margin-top: 1rem;
}
@media (max-width: 480px) {
#promo-box { padding: 2rem 1.25rem 1.5rem; }
#promo-box h2 { font-size: 1.2rem; }
}
+105
View File
@@ -0,0 +1,105 @@
/* ============================================================
FLOOR IT HARDWOOD FLOORS: Design Tokens
tokens.css: CSS custom properties only. No rules, no selectors.
============================================================ */
/* Custom Properties */
:root {
/* Color palette: warm wood tones, no gradients */
--ink: #0c0805;
--charcoal: #1c1208;
--bark: #2e1d0a;
--bark-mid: #3d2710;
--amber: #c88b2a;
--amber-dark: #a87220;
--amber-light: #e8aa48;
--parchment: #f5f0e8;
--cream: #faf8f5;
--grain: #ede5d8;
--smoke: #7a6a56;
--ash: #b8a898;
--white: #ffffff;
/* Semantic aliases */
--bg-dark: var(--ink);
--bg-dark-alt: var(--charcoal);
--bg-mid-dark: var(--bark);
--bg-light: var(--cream);
--bg-light-alt: var(--parchment);
--bg-warm: var(--grain);
--text-on-dark: #f0e8da;
--text-muted-dark:var(--ash);
--text-on-light: var(--charcoal);
--text-muted-light:var(--smoke);
--cta: var(--amber);
--cta-hover: var(--amber-dark);
--cta-text: var(--ink);
--border-dark: rgba(255,255,255,0.08);
--border-light: rgba(0,0,0,0.08);
/* Typography */
--font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--font-display: 'Inter', Georgia, serif;
/* Scale */
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-md: 1.125rem;
--text-lg: 1.25rem;
--text-xl: 1.5rem;
--text-2xl: 2rem;
--text-3xl: 2.5rem;
--text-4xl: 3.25rem;
--text-5xl: 4.25rem;
--text-6xl: 5.5rem;
/* Spacing */
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 0.75rem;
--space-4: 1rem;
--space-5: 1.25rem;
--space-6: 1.5rem;
--space-8: 2rem;
--space-10: 2.5rem;
--space-12: 3rem;
--space-16: 4rem;
--space-20: 5rem;
--space-24: 6rem;
--space-32: 8rem;
/* Section rhythm */
--section-py: clamp(4rem, 8vw, 8rem);
--section-py-sm: clamp(2.5rem, 5vw, 5rem);
/* Layout */
--container-max: 1200px;
--container-wide: 1380px;
--container-px: clamp(1.25rem, 5vw, 2.5rem);
/* Effects */
--radius-sm: 4px;
--radius: 8px;
--radius-lg: 16px;
--radius-xl: 24px;
--radius-full: 9999px;
--shadow-sm: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.08);
--shadow: 0 4px 16px rgba(0,0,0,0.12), 0 2px 6px rgba(0,0,0,0.08);
--shadow-lg: 0 12px 40px rgba(0,0,0,0.18), 0 4px 12px rgba(0,0,0,0.10);
--shadow-xl: 0 24px 64px rgba(0,0,0,0.22);
--transition: 0.25s ease;
--transition-slow: 0.5s ease;
/* Header height */
--header-h: 72px;
}
@media (max-width: 480px) {
:root { --section-py: clamp(3rem, 8vw, 4rem); }
}
+8
View File
File diff suppressed because one or more lines are too long
+87 -179
View File
@@ -1,207 +1,115 @@
/* ============================================================
form.js Estimate form validation + submission
Real-time validation, phone formatting, reCAPTCHA v3 hook
============================================================ */
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('contactForm');
const formLoadedAtInput = document.getElementById('form_loaded_at');
const formStatusDiv = document.getElementById('formStatus');
(function () {
'use strict';
const PHONE = /^\(?\d{3}\)?[\s.\-]?\d{3}[\s.\-]?\d{4}$/;
const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const RECAPTCHA_SITE_KEY = '6LdqrB8rAAAAAOrBCYmtk43IzemkiK_Fb2EYU5q2';
/* --- Helpers -------------------------------------------- */
function field(el) {
return el.closest('.form-field');
// Set form_loaded_at to current timestamp in milliseconds
if (formLoadedAtInput) {
formLoadedAtInput.value = Date.now().toString();
}
function setValid(el) {
const f = field(el);
if (!f) return;
f.classList.remove('has-error');
el.classList.add('valid');
el.classList.remove('invalid');
}
function setInvalid(el, msg) {
const f = field(el);
if (!f) return;
f.classList.add('has-error');
el.classList.add('invalid');
el.classList.remove('valid');
const errEl = f.querySelector('.err-msg');
if (errEl && msg) errEl.textContent = msg;
}
function clearState(el) {
const f = field(el);
if (!f) return;
f.classList.remove('has-error');
el.classList.remove('valid', 'invalid');
}
/* --- Phone formatter ------------------------------------ */
function formatPhone(raw) {
const digits = raw.replace(/\D/g, '').slice(0, 10);
if (digits.length < 4) return digits;
if (digits.length < 7) return '(' + digits.slice(0,3) + ') ' + digits.slice(3);
return '(' + digits.slice(0,3) + ') ' + digits.slice(3,6) + '-' + digits.slice(6);
}
/* --- Validators ----------------------------------------- */
function validateRequired(el) {
if (!el.value.trim()) {
setInvalid(el, 'This field is required.');
return false;
}
setValid(el);
return true;
}
function validateEmail(el) {
if (!el.value.trim()) {
setInvalid(el, 'Email address is required.');
return false;
}
if (!EMAIL.test(el.value.trim())) {
setInvalid(el, 'Please enter a valid email address.');
return false;
}
setValid(el);
return true;
}
function validatePhone(el) {
const val = el.value.replace(/\D/g, '');
if (!val) {
setInvalid(el, 'Phone number is required.');
return false;
}
if (val.length !== 10) {
setInvalid(el, 'Please enter a 10-digit phone number.');
return false;
}
setValid(el);
return true;
}
/* --- reCAPTCHA v3 token --------------------------------- */
function getRecaptchaToken(action) {
return new Promise((resolve) => {
if (typeof grecaptcha === 'undefined') {
resolve('');
return;
}
grecaptcha.ready(() => {
grecaptcha.execute(RECAPTCHA_SITE_KEY, { action }).then(resolve);
});
// Initialize Altcha
const altchaElement = document.getElementById('altcha-widget');
if (altchaElement) {
window.altcha = new Altcha({
challengeUrl: '/altcha-challenge/',
element: altchaElement
});
}
/* --- Form handler --------------------------------------- */
function initForm(form) {
const nameEl = form.querySelector('#name');
const emailEl = form.querySelector('#email');
const phoneEl = form.querySelector('#phone');
const addrEl = form.querySelector('#address');
const serviceEl = form.querySelector('#service');
const msgEl = form.querySelector('#message');
const submit = form.querySelector('[type="submit"]');
const status = form.querySelector('.form-status');
if (!submit) return;
/* Phone real-time format */
if (phoneEl) {
phoneEl.addEventListener('input', () => {
const pos = phoneEl.selectionStart;
const prev = phoneEl.value;
phoneEl.value = formatPhone(prev);
/* restore cursor roughly */
const diff = phoneEl.value.length - prev.length;
try { phoneEl.setSelectionRange(pos + diff, pos + diff); } catch (_) {}
});
phoneEl.addEventListener('blur', () => validatePhone(phoneEl));
}
/* Blur-time validation for other fields */
if (nameEl) nameEl.addEventListener('blur', () => validateRequired(nameEl));
if (emailEl) emailEl.addEventListener('blur', () => validateEmail(emailEl));
if (addrEl) addrEl.addEventListener('blur', () => validateRequired(addrEl));
if (serviceEl) serviceEl.addEventListener('change', () => validateRequired(serviceEl));
/* Submit */
form.addEventListener('submit', async (e) => {
// Form submit handler
if (form) {
form.addEventListener('submit', async function(e) {
e.preventDefault();
const checks = [
nameEl ? validateRequired(nameEl) : true,
emailEl ? validateEmail(emailEl) : true,
phoneEl ? validatePhone(phoneEl) : true,
addrEl ? validateRequired(addrEl) : true,
serviceEl ? validateRequired(serviceEl) : true,
];
// Clear previous status messages
formStatusDiv.innerHTML = '';
formStatusDiv.className = '';
if (checks.includes(false)) {
const firstErr = form.querySelector('.invalid');
if (firstErr) firstErr.focus();
// Validate required fields
const name = form.elements['name']?.value.trim();
const email = form.elements['email']?.value.trim();
if (!name || !email) {
formStatusDiv.className = 'form-status form-status--error';
formStatusDiv.innerHTML = '<p>Please fill in all required fields.</p>';
return;
}
const origText = submit.textContent;
submit.disabled = true;
submit.textContent = 'Sending...';
if (status) { status.className = 'form-status'; status.textContent = ''; }
if (!email.includes('@')) {
formStatusDiv.className = 'form-status form-status--error';
formStatusDiv.innerHTML = '<p>Please enter a valid email address.</p>';
return;
}
const token = await getRecaptchaToken('estimate_form');
// Check honeypot
const honeypot = form.elements['website']?.value;
if (honeypot) {
formStatusDiv.className = 'form-status form-status--error';
formStatusDiv.innerHTML = '<p>Form validation failed.</p>';
return;
}
// Solve Altcha if available
let altchaPayload = '';
if (window.altcha && !window.altcha.didSubmit) {
try {
await window.altcha.solve();
altchaPayload = window.altcha.getFormData().altcha;
} catch (err) {
formStatusDiv.className = 'form-status form-status--error';
formStatusDiv.innerHTML = '<p>Spam check failed. Please try again.</p>';
return;
}
} else if (window.altcha) {
const formData = window.altcha.getFormData();
altchaPayload = formData.altcha || '';
}
// Build JSON payload
const payload = {
name: nameEl ? nameEl.value.trim() : '',
email: emailEl ? emailEl.value.trim() : '',
phone: phoneEl ? phoneEl.value.trim() : '',
address: addrEl ? addrEl.value.trim() : '',
service: serviceEl ? serviceEl.value : '',
message: msgEl ? msgEl.value.trim() : '',
token,
name: form.elements['name'].value.trim(),
email: form.elements['email'].value.trim(),
phone: form.elements['phone']?.value.trim() || '',
message: form.elements['message']?.value.trim() || '',
website: form.elements['website']?.value || '',
form_loaded_at: form.elements['form_loaded_at']?.value || '',
altcha: altchaPayload
};
// POST to /contact/
try {
const res = await fetch('/api/estimate', {
const response = await fetch('/contact/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
if (!status) { submit.disabled = false; submit.textContent = origText; return; }
const data = await response.json();
if (res.ok) {
status.className = 'form-status form-status--success';
status.textContent = 'Thank you! We will get back to you within 1 business hour.';
if (data.ok) {
formStatusDiv.className = 'form-status form-status--success';
formStatusDiv.innerHTML = '<p>Thank you! Your message has been sent. We\'ll be in touch soon.</p>';
form.reset();
form.querySelectorAll('input, textarea, select').forEach(clearState);
if (formLoadedAtInput) {
formLoadedAtInput.value = Date.now().toString();
}
if (window.altcha) {
window.altcha = new Altcha({
challengeUrl: '/altcha-challenge/',
element: document.getElementById('altcha-widget')
});
}
} else {
throw new Error(res.status);
formStatusDiv.className = 'form-status form-status--error';
formStatusDiv.innerHTML = '<p>' + (data.error || 'An error occurred. Please try again.') + '</p>';
}
} catch (_) {
if (status) {
status.className = 'form-status form-status--error';
status.textContent = 'Something went wrong. Please call us directly at (716) 602-1429.';
}
} finally {
submit.disabled = false;
submit.textContent = origText;
} catch (err) {
formStatusDiv.className = 'form-status form-status--error';
formStatusDiv.innerHTML = '<p>Network error. Please try again.</p>';
}
});
}
function boot() {
document.querySelectorAll('.estimate-form').forEach(initForm);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', boot);
} else {
boot();
}
})();
});
+37 -3
View File
@@ -1,5 +1,5 @@
/* ============================================================
main.js Scroll animations, counters, FAQ, BA slider
main.js: Scroll animations, counters, FAQ, BA slider
============================================================ */
(function () {
@@ -128,7 +128,7 @@
const video = document.querySelector('.hero-video-wrap video');
if (!video) return;
video.play().catch(() => {
// autoplay blocked poster image is visible; nothing to do
// autoplay blocked: poster image is visible; nothing to do
});
}
@@ -145,8 +145,42 @@
track.setAttribute('tabindex', '0');
}
/* --- Boot ---------------------------------------------- */
/* --- Header scroll + mobile nav ------------------------- */
function initNav() {
var header = document.getElementById('site-header');
var mobileNav = document.getElementById('mobileNav');
var menuBtn = document.querySelector('.header-menu-btn');
var closeBtn = document.getElementById('mobileNavClose');
var overlay = document.getElementById('mobileNavOverlay');
if (!header) return;
window.addEventListener('scroll', function () {
header.classList.toggle('scrolled', window.scrollY > 40);
}, { passive: true });
function openNav() {
mobileNav.classList.add('open');
mobileNav.setAttribute('aria-hidden', 'false');
if (menuBtn) menuBtn.setAttribute('aria-expanded', 'true');
document.body.style.overflow = 'hidden';
}
function closeNav() {
mobileNav.classList.remove('open');
mobileNav.setAttribute('aria-hidden', 'true');
if (menuBtn) menuBtn.setAttribute('aria-expanded', 'false');
document.body.style.overflow = '';
}
if (menuBtn) menuBtn.addEventListener('click', openNav);
if (closeBtn) closeBtn.addEventListener('click', closeNav);
if (overlay) overlay.addEventListener('click', closeNav);
}
/* --- Boot: initialize all modules ------------------------ */
function boot() {
initNav();
initScrollAnimations();
initCounters();
initFAQ();
+123
View File
@@ -0,0 +1,123 @@
(function () {
var POPUP_KEY = 'flooritPromo2026';
var TOPBAR_KEY = 'flooritTopbar2026';
var DELAY_MS = 5000;
var EXPIRY_MS = 7 * 24 * 60 * 60 * 1000;
function isStored(key) {
try {
var val = localStorage.getItem(key);
return val && Date.now() < parseInt(val, 10);
} catch (e) { return false; }
}
function store(key) {
try { localStorage.setItem(key, String(Date.now() + EXPIRY_MS)); } catch (e) {}
}
/* --- Topbar -------------------------------------------- */
function showTopbar() {
var bar = document.getElementById('promo-topbar');
if (!bar) return;
bar.classList.add('visible');
document.body.classList.add('has-topbar');
}
function hideTopbar() {
var bar = document.getElementById('promo-topbar');
if (!bar) return;
bar.classList.remove('visible');
document.body.classList.remove('has-topbar');
store(TOPBAR_KEY);
}
function initTopbar() {
if (isStored(TOPBAR_KEY) || isStored(POPUP_KEY)) return;
showTopbar();
var closeBtn = document.getElementById('promo-topbar-close');
var offerBtn = document.getElementById('promo-topbar-btn');
if (closeBtn) closeBtn.addEventListener('click', hideTopbar);
if (offerBtn) offerBtn.addEventListener('click', function () {
hideTopbar();
openPopup();
});
}
/* --- Popup --------------------------------------------- */
function openPopup() {
var overlay = document.getElementById('promo-overlay');
if (!overlay) return;
overlay.style.display = 'flex';
requestAnimationFrame(function () {
requestAnimationFrame(function () { overlay.classList.add('visible'); });
});
}
function closePopup() {
var overlay = document.getElementById('promo-overlay');
if (overlay) {
overlay.classList.remove('visible');
setTimeout(function () { overlay.style.display = 'none'; }, 350);
}
store(POPUP_KEY);
hideTopbar();
}
function initPopup() {
if (isStored(POPUP_KEY)) return;
var closeBtn = document.getElementById('promo-close');
var overlay = document.getElementById('promo-overlay');
var form = document.getElementById('promo-form');
var submit = document.getElementById('promo-submit');
var errEl = document.getElementById('promo-error');
var success = document.getElementById('promo-success');
if (!overlay || !form) return;
if (closeBtn) closeBtn.addEventListener('click', closePopup);
overlay.addEventListener('click', function (e) {
if (e.target === overlay) closePopup();
});
form.addEventListener('submit', function (e) {
e.preventDefault();
errEl.style.display = 'none';
submit.disabled = true;
submit.textContent = 'Sending...';
var data = new FormData(form);
fetch('/promo/', { method: 'POST', body: data })
.then(function (r) { return r.json(); })
.then(function (res) {
if (res.ok) {
form.style.display = 'none';
success.style.display = 'block';
store(POPUP_KEY);
hideTopbar();
} else {
errEl.textContent = res.error || 'Something went wrong.';
errEl.style.display = 'block';
submit.disabled = false;
submit.textContent = 'Claim My Discount';
}
})
.catch(function () {
errEl.textContent = 'Network error. Please try again.';
errEl.style.display = 'block';
submit.disabled = false;
submit.textContent = 'Claim My Discount';
});
});
setTimeout(openPopup, DELAY_MS);
}
function init() {
initTopbar();
initPopup();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
-110
View File
@@ -1,110 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>Hardwood Floor Tips & Guides | Floor It Blog</title>
<meta name="description" content="Expert hardwood floor care tips and guides for Buffalo, NY homeowners. Learn practical advice about floor maintenance, refinishing, and restoration from Floor It.">
<link rel="canonical" href="https://floorithardwoodfloors.com/blog/">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "Floor It Hardwood Floors",
"url": "https://floorithardwoodfloors.com",
"telephone": "+17166021429",
"email": "floorithardwoods@gmail.com",
"address": { "@type": "PostalAddress", "addressLocality": "Buffalo", "addressRegion": "NY", "addressCountry": "US" },
"areaServed": ["Buffalo", "Amherst", "Williamsville", "East Amherst", "Clarence", "Lancaster"],
"openingHours": "Mo-Sa 08:00-17:00",
"aggregateRating": { "@type": "AggregateRating", "ratingValue": "4.9", "reviewCount": "50" }
}
</script>
</head>
<body>
<div id="site-header"></div>
<main>
<section class="page-hero">
<div class="container page-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">/</span>
<span>Blog</span>
</nav>
<span class="eyebrow">From the Floor It Team</span>
<h1>Hardwood Floor Tips & Guides</h1>
<p class="lead">Practical advice and expert tips for Buffalo, NY homeowners. Learn how to maintain, protect, and care for your hardwood floors with guidance from the Western New York refinishing specialists.</p>
</div>
</section>
<!-- ARTICLE CARDS -->
<section class="section section--light">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Latest Articles</span>
<h2>Hardwood Floor Care Resources</h2>
</div>
<div class="grid grid--auto-3">
<div class="article-card" data-animate="up">
<div class="article-card-body">
<h3>How to Tell If Your Floors Need Refinishing</h3>
<p>Learn the warning signs that indicate your hardwood floors are ready for a professional refinish. From visible scratches to dull finishes, we explain what to look for and when to act.</p>
</div>
<div class="article-card-footer">
<a href="#" class="btn btn--primary btn--sm">Read More</a>
</div>
</div>
<div class="article-card" data-animate="up" data-delay="2">
<div class="article-card-body">
<h3>Hardwood vs. Engineered: Which Is Right for Your Home?</h3>
<p>Considering a new floor installation or replacement? Discover the pros and cons of solid hardwood and engineered hardwood to make the best choice for your Buffalo home.</p>
</div>
<div class="article-card-footer">
<a href="#" class="btn btn--primary btn--sm">Read More</a>
</div>
</div>
<div class="article-card" data-animate="up" data-delay="3">
<div class="article-card-body">
<h3>What to Expect During a Floor Refinishing Project</h3>
<p>Wondering what happens during a professional floor refinishing? Get a detailed walkthrough of the timeline, process, and what to expect from start to finish.</p>
</div>
<div class="article-card-footer">
<a href="#" class="btn btn--primary btn--sm">Read More</a>
</div>
</div>
</div>
</div>
</section>
<div class="cta-strip">
<div class="container">
<h2>Ready to Transform Your Floors?</h2>
<p>Request a free estimate and let Floor It help restore your hardwood floors to their original beauty.</p>
<div class="cta-group" style="justify-content:center;">
<a href="/contact/" class="btn btn--outline-dark btn--lg">Request an Estimate</a>
</div>
</div>
</div>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
</body>
</html>
-94
View File
@@ -1,94 +0,0 @@
<footer class="site-footer" role="contentinfo">
<div class="footer-top">
<div class="container">
<div class="footer-grid">
<div class="footer-brand">
<img src="/assets/images/logo-footer.png" alt="Floor It Hardwood Floors" style="height:52px;width:auto;object-fit:contain;margin-bottom:0.75rem;" onerror="this.style.display='none';this.nextElementSibling.style.display='block'">
<div class="footer-logo-text" style="display:none;">Floor <span>It</span></div>
<p>Western New York's trusted hardwood floor refinishing, restoration, and installation specialists. Serving Buffalo and Erie County with 75 years of combined experience.</p>
<div style="margin-top:1.5rem;display:flex;gap:0.75rem;flex-wrap:wrap;">
<a href="/contact/" class="btn btn--ghost btn--sm">Get an Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-light btn--sm">(716) 602-1429</a>
</div>
</div>
<div class="footer-col">
<h5>Services</h5>
<ul>
<li><a href="/services/floor-refinishing.html">Floor Refinishing</a></li>
<li><a href="/services/floor-restoration.html">Floor Restoration</a></li>
<li><a href="/services/floor-sanding.html">Floor Sanding</a></li>
<li><a href="/services/floor-installation.html">Floor Installation</a></li>
</ul>
</div>
<div class="footer-col">
<h5>Locations</h5>
<ul>
<li><a href="/locations/buffalo.html">Buffalo, NY</a></li>
<li><a href="/locations/amherst.html">Amherst, NY</a></li>
<li><a href="/locations/williamsville.html">Williamsville, NY</a></li>
<li><a href="/locations/east-amherst.html">East Amherst, NY</a></li>
<li><a href="/locations/clarence.html">Clarence, NY</a></li>
<li><a href="/locations/lancaster.html">Lancaster, NY</a></li>
<li><a href="/locations/">All Service Areas</a></li>
</ul>
</div>
<div class="footer-col">
<h5>Contact</h5>
<div class="footer-contact-list">
<div class="footer-contact-item">
<div class="footer-contact-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2 3a1 1 0 0 1 1-1h2.153a1 1 0 0 1 .986.836l.74 4.435a1 1 0 0 1-.54 1.06l-1.548.773a11.037 11.037 0 0 0 6.105 6.105l.774-1.548a1 1 0 0 1 1.059-.54l4.435.74a1 1 0 0 1 .836.986V17a1 1 0 0 1-1 1h-2C7.82 18 2 12.18 2 5V3z"/></svg>
</div>
<div>
<span>Phone</span>
<a href="tel:+17166021429">(716) 602-1429</a>
</div>
</div>
<div class="footer-contact-item">
<div class="footer-contact-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0 0 16 4H4a2 2 0 0 0-1.997 1.884z"/><path d="M18 8.118l-8 4-8-4V14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8.118z"/></svg>
</div>
<div>
<span>Email</span>
<a href="mailto:floorithardwoods@gmail.com">floorithardwoods@gmail.com</a>
</div>
</div>
<div class="footer-contact-item">
<div class="footer-contact-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M5.05 4.05a7 7 0 1 1 9.9 9.9L10 18.9l-4.95-4.95a7 7 0 0 1 0-9.9zM10 11a2 2 0 1 0 0-4 2 2 0 0 0 0 4z" clip-rule="evenodd"/></svg>
</div>
<div>
<span>Service Area</span>
<a href="https://maps.app.goo.gl/6HLbMaQuXVBy6rWB9" target="_blank" rel="noopener">Western NY &amp; Erie County</a>
</div>
</div>
<div class="footer-contact-item">
<div class="footer-contact-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm1-12a1 1 0 1 0-2 0v4a1 1 0 0 0 .293.707l2.828 2.829a1 1 0 1 0 1.415-1.415L11 9.586V6z" clip-rule="evenodd"/></svg>
</div>
<div>
<span>Hours</span>
<p style="font-size:0.875rem;font-weight:600;color:#f0e8da;max-width:none;">Monday to Saturday: 8 AM to 5 PM</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container">
<div class="footer-bottom">
<p>&copy; 2026 Floor It Hardwood Floors. All rights reserved.</p>
<nav class="footer-legal" aria-label="Legal">
<a href="/privacy/">Privacy Policy</a>
<a href="/terms/">Terms of Service</a>
</nav>
</div>
</div>
</footer>
-60
View File
@@ -1,60 +0,0 @@
<header class="site-header" role="banner">
<div class="container">
<div class="header-inner">
<a href="/" class="header-logo" aria-label="Floor It Hardwood Floors">
<img src="/assets/images/logo-header.png" alt="Floor It Hardwood Floors" style="height:42px;width:auto;object-fit:contain;" onerror="this.style.display='none';this.nextElementSibling.style.display='block'">
<div style="display:none;">
<div class="header-logo-wordmark">Floor <span>It</span></div>
<div class="header-logo-sub">Hardwood Floors</div>
</div>
</a>
<nav class="header-nav" aria-label="Main navigation">
<a href="/">Home</a>
<a href="/services/">Services</a>
<a href="/locations/">Locations</a>
<a href="/about/">About</a>
<a href="/reviews/">Reviews</a>
<a href="/blog/">Blog</a>
</nav>
<div class="header-cta">
<a href="tel:+17166021429" class="header-phone" aria-label="Call us at (716) 602-1429">(716) 602-1429</a>
<a href="/contact/" class="btn btn--primary btn--sm">Get Estimate</a>
<button class="header-menu-btn" aria-label="Open menu" aria-expanded="false">
<span></span>
<span></span>
<span></span>
</button>
</div>
</div>
</div>
</header>
<div class="mobile-nav" aria-hidden="true">
<div class="mobile-nav-overlay"></div>
<div class="mobile-nav-panel" role="dialog" aria-label="Mobile navigation">
<button class="mobile-nav-close" aria-label="Close menu">&times;</button>
<nav class="mobile-nav-links" aria-label="Mobile navigation">
<a href="/">Home</a>
<a href="/services/">Services</a>
<a href="/services/floor-refinishing/">Floor Refinishing</a>
<a href="/services/floor-restoration/">Floor Restoration</a>
<a href="/services/floor-sanding/">Floor Sanding</a>
<a href="/services/floor-installation/">Floor Installation</a>
<a href="/locations/">Locations</a>
<a href="/about/">About Us</a>
<a href="/reviews/">Reviews</a>
<a href="/blog/">Blog</a>
<a href="/contact/">Contact</a>
</nav>
<div class="mobile-nav-cta">
<a href="tel:+17166021429" class="mobile-nav-phone">(716) 602-1429</a>
<a href="/contact/" class="btn btn--primary btn--full">Request Free Estimate</a>
</div>
</div>
</div>
-200
View File
@@ -1,200 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>Contact Floor It Hardwood Floors | Buffalo, NY (716) 602-1429</title>
<meta name="description" content="Request a free estimate or call (716) 602-1429. Floor It Hardwood Floors serves Buffalo and Erie County, NY. Mon-Sat 8AM-5PM.">
<link rel="canonical" href="https://floorithardwoodfloors.com/contact/">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
</head>
<body>
<div id="site-header"></div>
<main>
<section class="page-hero">
<div class="container page-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">/</span>
<span>Contact</span>
</nav>
<span class="eyebrow">Get in Touch</span>
<h1>Request Your Estimate</h1>
<p class="lead">Fill out the form below or call us directly. We respond within 1 business hour, Monday through Saturday.</p>
</div>
</section>
<section class="section contact-section">
<div class="container">
<div class="contact-layout">
<div>
<span class="eyebrow">Contact Information</span>
<h2>Reach Our Team</h2>
<div class="divider"></div>
<div class="contact-info-list">
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z"/></svg>
</div>
<div>
<h5>Phone</h5>
<a href="tel:+17166021429">(716) 602-1429</a>
</div>
</div>
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z"/><path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z"/></svg>
</div>
<div>
<h5>Email</h5>
<a href="mailto:floorithardwoods@gmail.com">floorithardwoods@gmail.com</a>
</div>
</div>
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/></svg>
</div>
<div>
<h5>Service Area</h5>
<a href="https://maps.app.goo.gl/6HLbMaQuXVBy6rWB9" target="_blank" rel="noopener">Western NY &amp; Erie County</a>
</div>
</div>
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/></svg>
</div>
<div>
<h5>Business Hours</h5>
<p>Monday to Saturday: 8:00 AM to 5:00 PM<br>Sunday: Closed</p>
</div>
</div>
</div>
<div style="margin-top:2.5rem;padding:1.5rem;background:var(--charcoal);border-radius:var(--radius-lg);color:var(--text-on-dark);">
<p style="font-size:var(--text-sm);font-weight:600;color:var(--amber);letter-spacing:0.08em;text-transform:uppercase;margin-bottom:0.75rem;">What Happens Next</p>
<ol style="display:flex;flex-direction:column;gap:0.75rem;max-width:none;">
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--ash);"><span style="color:var(--amber);font-weight:800;flex-shrink:0;min-width:1.25rem;">1.</span> We call or email you within 1 business hour</li>
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--ash);"><span style="color:var(--amber);font-weight:800;flex-shrink:0;min-width:1.25rem;">2.</span> We schedule a free onsite visit at your convenience</li>
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--ash);"><span style="color:var(--amber);font-weight:800;flex-shrink:0;min-width:1.25rem;">3.</span> You receive a detailed, multi-option quote within 2 business days</li>
</ol>
</div>
</div>
<div class="contact-form-wrap" data-animate="right">
<h3 style="margin-bottom:1.5rem;font-size:var(--text-xl);">Send Us a Message</h3>
<form class="estimate-form" novalidate>
<div class="form-grid form-grid--2">
<div class="form-field">
<label for="name">Full Name <span aria-hidden="true">*</span></label>
<input type="text" id="name" name="name" autocomplete="name" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="phone">Phone Number <span aria-hidden="true">*</span></label>
<input type="tel" id="phone" name="phone" placeholder="(716) 000-0000" autocomplete="tel" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="email">Email Address <span aria-hidden="true">*</span></label>
<input type="email" id="email" name="email" autocomplete="email" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="address">Property Address <span aria-hidden="true">*</span></label>
<input type="text" id="address" name="address" autocomplete="street-address" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="city">City <span aria-hidden="true">*</span></label>
<input type="text" id="city" name="city" autocomplete="address-level2" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="zip">Zip Code</label>
<input type="text" id="zip" name="zip" autocomplete="postal-code" inputmode="numeric" maxlength="5">
</div>
</div>
<div class="form-grid form-grid--2" style="margin-top:1.25rem;">
<div class="form-field">
<label for="service">Service Needed <span aria-hidden="true">*</span></label>
<select id="service" name="service" required>
<option value="">Select a service</option>
<option value="refinishing">Floor Refinishing</option>
<option value="restoration">Floor Restoration</option>
<option value="sanding">Floor Sanding</option>
<option value="installation">Floor Installation</option>
<option value="unsure">Not Sure</option>
</select>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="condition">Floor Condition</label>
<select id="condition" name="condition">
<option value="">Select condition</option>
<option value="good">Good: Minor scratches</option>
<option value="fair">Fair: Visible wear</option>
<option value="poor">Poor: Major damage</option>
</select>
</div>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="message">Describe the Issue</label>
<textarea id="message" name="message" placeholder="In a few sentences, describe the issue your floors are experiencing or what service you are looking for."></textarea>
</div>
<div class="form-trust">
<div class="form-trust-item">1 business hour response</div>
<div class="form-trust-item">No obligation</div>
<div class="form-trust-item">Licensed &amp; insured</div>
</div>
<button type="submit" class="btn btn--primary btn--full btn--lg" style="margin-top:1.25rem;">Send My Request</button>
<div class="form-status" role="status" aria-live="polite"></div>
<p style="text-align:center;margin-top:1rem;font-size:var(--text-sm);color:var(--smoke);">Or call us directly: <a href="tel:+17166021429" style="color:var(--amber);font-weight:700;">(716) 602-1429</a></p>
</form>
</div>
</div>
</div>
</section>
<!-- MAP -->
<section class="section section--dark" style="padding-block:0;">
<iframe
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d185000!2d-78.85!3d42.89!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x89d312819d5e0e05%3A0xe0f822ff0f825c28!2sErie%20County%2C%20NY!5e0!3m2!1sen!2sus!4v1"
width="100%"
height="400"
style="display:block;border:none;filter:grayscale(0.5) contrast(1.1);"
loading="lazy"
title="Floor It Hardwood Floors service area in Buffalo, NY"
referrerpolicy="no-referrer-when-downgrade"
aria-label="Map showing Floor It service area in Buffalo, NY">
</iframe>
</section>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/form.js"></script>
</body>
</html>
+6 -16
View File
@@ -1,27 +1,17 @@
services:
web:
image: floorithardwoodfloors-static
image: floorithardwoodfloors
container_name: floorit-hardwood-web
build:
context: .
dockerfile: Dockerfile
ports:
- "8096:80"
depends_on:
api:
condition: service_healthy
env_file: .env
restart: unless-stopped
api:
image: floorithardwoodfloors-api
build:
context: ./api
dockerfile: Dockerfile
env_file: ./api/.env
expose:
- "3001"
healthcheck:
test: ["CMD", "python3", "-c", "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:3001/health',timeout=3).status==200 else 1)"]
interval: 10s
test: ["CMD", "curl", "-fsS", "http://127.0.0.1/"]
interval: 30s
timeout: 5s
start_period: 10s
retries: 3
restart: unless-stopped
-633
View File
@@ -1,633 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>Buffalo, NY Hardwood Floor Refinishing &amp; Restoration | Floor It</title>
<meta name="description" content="Western New York's trusted hardwood floor refinishing, restoration, and installation specialists. 75 years combined experience. Serving Buffalo and Erie County. Request an estimate today.">
<link rel="canonical" href="https://floorithardwoodfloors.com/">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "Floor It Hardwood Floors",
"url": "https://floorithardwoodfloors.com",
"telephone": "+17166021429",
"email": "floorithardwoods@gmail.com",
"address": { "@type": "PostalAddress", "addressLocality": "Buffalo", "addressRegion": "NY", "addressCountry": "US" },
"areaServed": ["Buffalo", "Amherst", "Williamsville", "East Amherst", "Clarence", "Lancaster"],
"openingHours": "Mo-Sa 08:00-17:00",
"aggregateRating": { "@type": "AggregateRating", "ratingValue": "4.9", "reviewCount": "50" }
}
</script>
</head>
<body>
<div id="site-header"></div>
<main>
<!-- HERO -->
<section class="hero" aria-label="Hero">
<div class="hero-video-wrap">
<video autoplay muted loop playsinline poster="/assets/images/hero-poster.jpg" aria-hidden="true">
<source src="/assets/videos/hero-video-background.webm" type="video/webm">
<source src="/assets/videos/hero-video-background.mp4" type="video/mp4">
</video>
</div>
<div class="container">
<div class="hero-content">
<div class="hero-eyebrow">
<div class="hero-eyebrow-line"></div>
<span>Serving Western New York Since 1994</span>
</div>
<h1>Floor Refinishing &amp; Restoration in Buffalo, NY</h1>
<p class="hero-sub">Whether you need refinishing, restoration, or new installation, our team brings 75 years of combined expertise to every project in Erie County.</p>
<div class="hero-actions">
<a href="/contact/" class="btn btn--primary btn--lg">Request an Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-light btn--lg">Call (716) 602-1429</a>
</div>
<div class="hero-trust">
<div class="hero-trust-stat">
<strong><span data-count="75" data-suffix="+">75+</span></strong>
<span>Years Combined Experience</span>
</div>
<div class="hero-trust-stat">
<strong><span data-count="500" data-suffix="+">500+</span></strong>
<span>Projects Completed</span>
</div>
<div class="hero-trust-stat">
<strong><span data-count="4.9" data-suffix="/5">4.9/5</span></strong>
<span>Customer Rating</span>
</div>
</div>
</div>
</div>
</section>
<!-- TRUST BAR -->
<div class="trust-bar">
<div class="container">
<div class="trust-bar-inner">
<div class="trust-stat" data-animate="up">
<span class="trust-stat-number" data-count="75" data-suffix="+">75+</span>
<span class="trust-stat-label">Years Combined Experience</span>
</div>
<div class="trust-stat" data-animate="up" data-delay="2">
<span class="trust-stat-number" data-count="500" data-suffix="+">500+</span>
<span class="trust-stat-label">Local Projects</span>
</div>
<div class="trust-stat" data-animate="up" data-delay="3">
<span class="trust-stat-number" data-count="4.9" data-suffix=" Stars">4.9</span>
<span class="trust-stat-label">Google Rating</span>
</div>
<div class="trust-stat" data-animate="up" data-delay="4">
<span class="trust-stat-number">24hr</span>
<span class="trust-stat-label">Response Time</span>
</div>
</div>
</div>
</div>
<!-- SERVICES -->
<section class="section section--light" id="services">
<div class="container">
<div class="section-header">
<span class="eyebrow">What We Do</span>
<h2>Flooring Services We Offer</h2>
<p class="lead">Complete hardwood floor solutions for Buffalo-area homes, delivered with professional-grade equipment and care.</p>
</div>
<div class="services-grid-2x2">
<div class="service-card" data-animate="up">
<div class="service-card-img">
<img src="/assets/images/project-1-after.webp" alt="Beautifully refinished hardwood floor restored to original beauty" loading="lazy">
</div>
<div class="service-card-body">
<h3>Floor Refinishing</h3>
<p>Multi-stage sanding, custom staining, and professional sealing that restores your floors to their original beauty. Over 100 stain color options.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-refinishing.html" class="service-card-link">Learn More &rarr;</a>
<a href="/contact/" class="btn btn--primary btn--sm">Get Estimate</a>
</div>
</div>
<div class="service-card" data-animate="up" data-delay="2">
<div class="service-card-img">
<img src="/assets/images/project-2-before.webp" alt="Water damaged hardwood floor awaiting professional restoration" loading="lazy">
</div>
<div class="service-card-body">
<h3>Floor Restoration</h3>
<p>Water damage, deep scratches, warping, and structural damage repaired with precision. We also provide documentation for insurance claims.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-restoration.html" class="service-card-link">Learn More &rarr;</a>
<a href="/contact/" class="btn btn--primary btn--sm">Get Estimate</a>
</div>
</div>
<div class="service-card" data-animate="up" data-delay="3">
<div class="service-card-img">
<img src="/assets/images/refinishing-machine.webp" alt="Professional floor sanding equipment used by Floor It Hardwood Floors" loading="lazy">
</div>
<div class="service-card-body">
<h3>Floor Sanding</h3>
<p>Commercial-grade HEPA dust containment equipment. Multi-grit sanding process delivers a perfectly prepared surface every time.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-sanding.html" class="service-card-link">Learn More &rarr;</a>
<a href="/contact/" class="btn btn--primary btn--sm">Get Estimate</a>
</div>
</div>
<div class="service-card" data-animate="up" data-delay="4">
<div class="service-card-img">
<img src="/assets/images/project-3-before.webp" alt="Hardwood floor in Buffalo area home ready for professional installation and transformation" loading="lazy">
</div>
<div class="service-card-body">
<h3>Floor Installation</h3>
<p>Beautiful new hardwood floors installed professionally. Quality materials, precise installation, built to last generations in your home.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-installation.html" class="service-card-link">Learn More &rarr;</a>
<a href="/contact/" class="btn btn--primary btn--sm">Get Estimate</a>
</div>
</div>
</div>
</div>
</section>
<!-- 3-STEP PROCESS -->
<section class="section section--dark" id="process">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">How It Works</span>
<h2>Get Started in 3 Easy Steps</h2>
</div>
<div class="process-steps">
<div class="process-step" data-animate="up">
<div class="process-step-num">1</div>
<h3>Schedule Your Estimate</h3>
<p>Call (716) 602-1429 or fill out our form. We respond within 24 hours to arrange your free onsite visit.</p>
</div>
<div class="process-step" data-animate="up" data-delay="2">
<div class="process-step-num">2</div>
<h3>Onsite Consultation</h3>
<p>We measure your floor, discuss all options, and answer every question so you know exactly what to expect.</p>
</div>
<div class="process-step" data-animate="up" data-delay="3">
<div class="process-step-num">3</div>
<h3>Receive Your Quote</h3>
<p>Within 2 business days you receive a detailed, multi-option quote with clear pricing and timeline.</p>
</div>
</div>
<div class="text-center mt-12" data-animate="up" data-delay="4">
<a href="/contact/" class="btn btn--primary btn--lg">Schedule Your Estimate</a>
</div>
</div>
</section>
<!-- ABOUT / CRAFTSMANSHIP -->
<section class="section section--alt" id="about-preview">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,5rem);align-items:center;">
<div data-animate="left">
<span class="eyebrow">Breathe with Assurance</span>
<h2>Professional Floor Refinishing in Western NY</h2>
<div class="divider"></div>
<p class="lead">Craftsmanship is the heart of our operation. We believe true craftsmanship combines skill, passion, and attention to detail.</p>
<p style="margin-top:1.25rem;color:var(--smoke);">Our team brings 75 years of combined experience to every job. We work not only in Buffalo but also in East Amherst, Amherst, Clarence, Williamsville, and Lancaster, serving homeowners with dedication across Western New York.</p>
<p style="margin-top:1rem;color:var(--smoke);">At Floor It Hardwood Floors, we understand that every scratch has its story. After three decades in this business, we are experts at breathing new life into your Buffalo home's flooring.</p>
<div class="cta-group mt-8">
<a href="/about/" class="btn btn--primary">Our Story</a>
<a href="/contact/" class="btn btn--outline-dark">Get Started</a>
</div>
</div>
<div data-animate="right">
<div style="background:var(--grain);border-radius:var(--radius-xl);overflow:hidden;aspect-ratio:4/5;display:flex;align-items:center;justify-content:center;">
<img src="/assets/images/refinishing-machine.webp" alt="Floor It professional refinishing equipment" style="width:100%;height:100%;object-fit:cover;" loading="lazy">
</div>
</div>
</div>
</div>
</section>
<!-- GALLERY -->
<section class="section section--bark" id="gallery">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Our Work</span>
<h2>Recent Restorations</h2>
<p class="lead">Before and after results from projects completed throughout Buffalo and Erie County.</p>
</div>
<div class="gallery-grid" data-animate="up">
<div class="gallery-item">
<img src="/assets/images/project-1-before.webp" alt="Hardwood floor before refinishing, worn surface" loading="lazy">
<div class="gallery-item-label">Before</div>
</div>
<div class="gallery-item">
<img src="/assets/images/project-1-after.webp" alt="Hardwood floor after professional refinishing, restored" loading="lazy">
<div class="gallery-item-label gallery-item-label--after">After</div>
</div>
<div class="gallery-item">
<img src="/assets/images/project-2-before.webp" alt="Water damaged hardwood floor before restoration" loading="lazy">
<div class="gallery-item-label">Before</div>
</div>
<div class="gallery-item">
<img src="/assets/images/project-3-before.webp" alt="Dull worn hardwood floor before sanding" loading="lazy">
<div class="gallery-item-label">Before</div>
</div>
</div>
<div class="text-center mt-12">
<a href="tel:+17166021429" class="btn btn--primary">Call to Discuss Your Project</a>
</div>
</div>
</section>
<!-- WHY CHOOSE US -->
<section class="section section--mid" id="why">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Our Difference</span>
<h2>Why Floor It Hardwood Floors</h2>
</div>
<div style="display:grid;grid-template-columns:repeat(2,1fr);gap:2.5rem;">
<div class="benefit-card" data-animate="left">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
</div>
<div class="benefit-body">
<h4>Revitalize Your Space</h4>
<p>Our refinishing process begins with a detailed assessment of your floors to determine the best course of action. Nothing is assumed.</p>
</div>
</div>
<div class="benefit-card" data-animate="right">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 0 0 .95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 0 0-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 0 0-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 0 0-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 0 0 .951-.69l1.519-4.674z"/></svg>
</div>
<div class="benefit-body">
<h4>Customized Solutions</h4>
<p>Every floor is unique. We provide tailored solutions with over 100 stain color options, matching your specific vision for your home.</p>
</div>
</div>
<div class="benefit-card" data-animate="left" data-delay="2">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
</div>
<div class="benefit-body">
<h4>HEPA Dustless Sanding</h4>
<p>We utilize the latest dustless sanding technology to minimize mess, protect your family's air quality, and ensure a clean, safe environment.</p>
</div>
</div>
<div class="benefit-card" data-animate="right" data-delay="2">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
</div>
<div class="benefit-body">
<h4>Lasting Durable Finishes</h4>
<p>We apply durable topcoats that protect your floors from scratches, moisture, and UV damage. Built for long-lasting results.</p>
</div>
</div>
</div>
</div>
</section>
<!-- TESTIMONIALS -->
<section class="section section--light" id="reviews">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Customer Reviews</span>
<h2>What Our Customers Say</h2>
<p>Rated 4.9 out of 5 across Google Reviews. Buffalo and Erie County homeowners trust Floor It.</p>
</div>
<div class="grid grid--auto-3">
<div class="testimonial-card" data-animate="up">
<div class="testimonial-stars">
<span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span>
</div>
<p class="testimonial-text">"Excellent work! They transformed our tired, worn floors into something beautiful. Professional, efficient, and honest with their pricing. Will absolutely use them again."</p>
<div class="testimonial-author">
<div class="testimonial-avatar" aria-hidden="true">J</div>
<div class="testimonial-info">
<strong>Jennifer M.</strong>
<span>Buffalo, NY</span>
</div>
</div>
</div>
<div class="testimonial-card" data-animate="up" data-delay="2">
<div class="testimonial-stars">
<span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span>
</div>
<p class="testimonial-text">"Highly recommend! The team was professional, clean, and completed everything on schedule. Our floors look incredible, like they are brand new."</p>
<div class="testimonial-author">
<div class="testimonial-avatar" aria-hidden="true">S</div>
<div class="testimonial-info">
<strong>Sarah K.</strong>
<span>Amherst, NY</span>
</div>
</div>
</div>
<div class="testimonial-card" data-animate="up" data-delay="3">
<div class="testimonial-stars">
<span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span>
</div>
<p class="testimonial-text">"Professional service from start to finish. They genuinely cared about the quality of the work and it shows. Absolutely worth the investment for any homeowner."</p>
<div class="testimonial-author">
<div class="testimonial-avatar" aria-hidden="true">M</div>
<div class="testimonial-info">
<strong>Michael D.</strong>
<span>Hamburg, NY</span>
</div>
</div>
</div>
</div>
<div class="text-center mt-10">
<a href="/reviews/" class="btn btn--outline-dark">Read All Reviews</a>
</div>
</div>
</section>
<!-- FAQ -->
<section class="section section--alt" id="faq">
<div class="container">
<div style="display:grid;grid-template-columns:5fr 7fr;gap:clamp(2rem,5vw,5rem);align-items:start;">
<div data-animate="left">
<span class="eyebrow">Common Questions</span>
<h2>Floor Refinishing FAQs</h2>
<div class="divider"></div>
<p style="color:var(--smoke);line-height:1.75;">Still have questions? Call us at <a href="tel:+17166021429" style="color:var(--amber);font-weight:700;">(716) 602-1429</a> and we will walk you through everything.</p>
</div>
<div class="faq-list" data-animate="up">
<div class="faq-item">
<div class="faq-question">
<h4>How does humidity affect the floor refinishing process?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Humidity plays a significant role in refinishing. High humidity prolongs drying time and can cause cloudy or uneven finishes. Low humidity causes finishes to dry too quickly, leading to brush marks. Floor It monitors humidity levels throughout the process and advises clients on maintaining optimal conditions after completion.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>How do I know if my floors need refinishing or just re-coating?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">If your floors have minor surface scratches and the finish is dull but intact, a re-coat may suffice. If you see deep scratches, wear through to the wood, discoloration, or significant damage, full refinishing is necessary. We can assess your floors during the free onsite visit and recommend the right approach.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>What safety measures does Floor It take during refinishing?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">We use proper PPE including masks, gloves, and eye protection. Our equipment is regularly maintained. We use HEPA dust containment systems to reduce airborne particles and use low-VOC products when possible. We follow all local regulations and ensure proper ventilation throughout every project.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>What types of wood floors can Floor It refinish?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">We refinish solid hardwood, engineered hardwood, and parquet floors. We work with a wide range of species including oak, maple, and other hardwoods. Our team is skilled in handling the unique characteristics of each wood type.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>Can you provide custom stain colors?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Yes. We offer over 100 stain colors and can mix custom colors to match your vision. During the consultation we will apply a sample directly to your floor for approval before proceeding with the full project. Your satisfaction with the color is confirmed before we begin.</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- LOCATIONS -->
<section class="section section--dark" id="locations">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Service Area</span>
<h2>Areas We Serve</h2>
<p>Proudly serving homeowners throughout Buffalo and Erie County, Western New York.</p>
</div>
<div class="location-grid" data-animate="up">
<a href="/locations/buffalo.html" class="location-card">
<div class="location-card-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/></svg>
</div>
<div class="location-card-text">
<strong>Buffalo, NY</strong>
<span>Primary Service Hub</span>
</div>
</a>
<a href="/locations/amherst.html" class="location-card">
<div class="location-card-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/></svg>
</div>
<div class="location-card-text">
<strong>Amherst, NY</strong>
<span>Erie County</span>
</div>
</a>
<a href="/locations/williamsville.html" class="location-card">
<div class="location-card-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/></svg>
</div>
<div class="location-card-text">
<strong>Williamsville, NY</strong>
<span>Erie County</span>
</div>
</a>
<a href="/locations/east-amherst.html" class="location-card">
<div class="location-card-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/></svg>
</div>
<div class="location-card-text">
<strong>East Amherst, NY</strong>
<span>Erie County</span>
</div>
</a>
<a href="/locations/clarence.html" class="location-card">
<div class="location-card-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/></svg>
</div>
<div class="location-card-text">
<strong>Clarence, NY</strong>
<span>Erie County</span>
</div>
</a>
<a href="/locations/lancaster.html" class="location-card">
<div class="location-card-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/></svg>
</div>
<div class="location-card-text">
<strong>Lancaster, NY</strong>
<span>Erie County</span>
</div>
</a>
</div>
<div class="text-center mt-10">
<a href="/locations/" class="btn btn--outline-light">View All Service Areas</a>
</div>
</div>
</section>
<!-- ESTIMATE FORM -->
<section class="section contact-section" id="estimate">
<div class="container">
<div class="contact-layout">
<div>
<span class="eyebrow">Free Estimate</span>
<h2>Request a Free Estimate</h2>
<div class="divider"></div>
<p class="lead">Fill out the form and we will respond within 1 business hour. No obligation.</p>
<div class="contact-info-list" style="margin-top:2.5rem;">
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z"/></svg>
</div>
<div>
<h5>Call Us Directly</h5>
<a href="tel:+17166021429">(716) 602-1429</a>
</div>
</div>
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z"/><path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z"/></svg>
</div>
<div>
<h5>Email</h5>
<a href="mailto:floorithardwoods@gmail.com">floorithardwoods@gmail.com</a>
</div>
</div>
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/></svg>
</div>
<div>
<h5>Hours</h5>
<p>Monday to Saturday: 8 AM to 5 PM</p>
</div>
</div>
</div>
</div>
<div class="contact-form-wrap" data-animate="right">
<form class="estimate-form" novalidate>
<div class="form-grid form-grid--2">
<div class="form-field">
<label for="name">Full Name <span aria-hidden="true">*</span></label>
<input type="text" id="name" name="name" autocomplete="name" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="phone">Phone Number <span aria-hidden="true">*</span></label>
<input type="tel" id="phone" name="phone" placeholder="(716) 000-0000" autocomplete="tel" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="email">Email Address <span aria-hidden="true">*</span></label>
<input type="email" id="email" name="email" autocomplete="email" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="address">Address <span aria-hidden="true">*</span></label>
<input type="text" id="address" name="address" autocomplete="street-address" required>
<span class="err-msg" role="alert"></span>
</div>
</div>
<div class="form-grid form-grid--2" style="margin-top:1.25rem;">
<div class="form-field">
<label for="service">Service Needed <span aria-hidden="true">*</span></label>
<select id="service" name="service" required>
<option value="">Select a service</option>
<option value="refinishing">Floor Refinishing</option>
<option value="restoration">Floor Restoration</option>
<option value="sanding">Floor Sanding</option>
<option value="installation">Floor Installation</option>
<option value="unsure">Not Sure</option>
</select>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="condition">Floor Condition</label>
<select id="condition" name="condition">
<option value="">Select condition</option>
<option value="good">Good: Minor scratches</option>
<option value="fair">Fair: Visible wear</option>
<option value="poor">Poor: Major damage</option>
</select>
</div>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="message">Additional Details</label>
<textarea id="message" name="message" placeholder="Describe the floor issue, room size, or anything else that helps us prepare for the visit."></textarea>
</div>
<div class="form-trust">
<div class="form-trust-item">1 business hour response</div>
<div class="form-trust-item">No obligation</div>
<div class="form-trust-item">Licensed &amp; insured</div>
</div>
<button type="submit" class="btn btn--primary btn--full btn--lg" style="margin-top:1.25rem;">Schedule My Estimate</button>
<div class="form-status" role="status" aria-live="polite"></div>
</form>
</div>
</div>
</div>
</section>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/form.js"></script>
</body>
</html>
+7
View File
@@ -0,0 +1,7 @@
#!/bin/sh
set -e
if [ -z "$ALTCHA_HMAC_KEY" ]; then
export ALTCHA_HMAC_KEY="$(openssl rand -hex 32)"
echo "Generated ALTCHA_HMAC_KEY" >&2
fi
exec "$@"
+131
View File
@@ -0,0 +1,131 @@
user www-data;
worker_processes auto;
error_log /dev/stderr warn;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent"';
access_log /dev/stdout main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
gzip on;
gzip_types text/html text/css application/javascript image/svg+xml;
gzip_min_length 1024;
limit_req_zone $binary_remote_addr zone=contact_limit:10m rate=5r/m;
server {
listen 80 default_server;
server_name _;
root /var/www/html;
index index.php;
server_tokens off;
client_max_body_size 16k;
location = /robots.txt { access_log off; try_files $uri =404; }
location = /sitemap.xml { access_log off; try_files $uri =404; }
location = /404.html { internal; }
location = /500.html { internal; }
location ~ /\. {
deny all;
return 404;
}
location ~* \.(env|conf|yml|yaml|py|pyc|sh|sql|log|bak|swp|sqlite)$ {
deny all;
return 404;
}
location ~* \.(css|js|webp|jpg|jpeg|png|svg|ico|woff2?|mp4|webm)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
try_files $uri =404;
}
location = /promo/ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/html/src/api/promo.php;
fastcgi_param QUERY_STRING "";
fastcgi_pass 127.0.0.1:9000;
}
location = /altcha-challenge/ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/html/src/api/altcha-challenge.php;
fastcgi_param QUERY_STRING "";
fastcgi_pass 127.0.0.1:9000;
}
location = /contact/ {
limit_req zone=contact_limit burst=3 nodelay;
limit_req_status 429;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/html/src/api/router.php;
fastcgi_param QUERY_STRING type=page&slug=contact;
fastcgi_pass 127.0.0.1:9000;
}
set $router /var/www/html/src/api/router.php;
location = / {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $router;
fastcgi_param QUERY_STRING type=page&slug=home;
fastcgi_pass 127.0.0.1:9000;
}
location ~ ^/(about|reviews|blog|services|locations)/$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $router;
fastcgi_param QUERY_STRING type=page&slug=$1;
fastcgi_pass 127.0.0.1:9000;
}
location ~ ^/services/([a-z0-9-]+)/$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $router;
fastcgi_param QUERY_STRING type=service&slug=$1;
fastcgi_pass 127.0.0.1:9000;
}
location ~ ^/locations/([a-z0-9-]+)/$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $router;
fastcgi_param QUERY_STRING type=location&slug=$1;
fastcgi_pass 127.0.0.1:9000;
}
location ~ ^/blog/([a-z0-9-]+)/$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $router;
fastcgi_param QUERY_STRING type=blog&slug=$1;
fastcgi_pass 127.0.0.1:9000;
}
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
error_page 404 /404.html;
error_page 500 502 503 504 /500.html;
}
}
+14
View File
@@ -0,0 +1,14 @@
[www]
user = www-data
group = www-data
listen = 127.0.0.1:9000
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
clear_env = no
access.log = /dev/null
+25
View File
@@ -0,0 +1,25 @@
[supervisord]
nodaemon=true
logfile=/dev/null
logfile_maxbytes=0
pidfile=/var/run/supervisord.pid
[program:php-fpm]
command=/usr/local/sbin/php-fpm --nodaemonize
autostart=true
autorestart=true
priority=10
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
autostart=true
autorestart=true
priority=20
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
-258
View File
@@ -1,258 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>{{title}}</title>
<meta name="description" content="{{meta_description}}">
<link rel="canonical" href="{{canonical}}">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "Floor It Hardwood Floors",
"url": "{{canonical}}",
"telephone": "+17166021429",
"email": "floorithardwoods@gmail.com",
"address": {
"@type": "PostalAddress",
"addressLocality": "{{city}}",
"addressRegion": "{{state}}",
"addressCountry": "US"
},
"areaServed": "{{schema_area}}",
"openingHours": "Mo-Sa 08:00-17:00",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.9",
"reviewCount": "50"
}
}
</script>
</head>
<body>
<div id="site-header"></div>
<main>
<!-- HERO -->
<section class="page-hero">
<div class="container page-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">/</span>
<a href="/locations/">Locations</a>
<span class="breadcrumb-sep">/</span>
<span>{{city}}, {{state}}</span>
</nav>
<span class="eyebrow">{{hero_eyebrow}}</span>
<h1>{{hero_h1}}</h1>
<p class="lead">{{hero_lead}}</p>
<div class="cta-group" style="margin-top:2rem;">
<a href="/contact/" class="btn btn--primary btn--lg">Request an Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-light btn--lg">Call (716) 602-1429</a>
</div>
</div>
</section>
<!-- OVERVIEW -->
<section class="section section--light">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,5rem);align-items:center;">
<div data-animate="left">
<span class="eyebrow">{{overview_eyebrow}}</span>
<h2>{{overview_h2}}</h2>
<div class="divider"></div>
<p style="color:var(--smoke);">{{overview_body_1}}</p>
<p style="margin-top:1rem;color:var(--smoke);">{{overview_body_2}}</p>
<div class="cta-group mt-8">
<a href="/contact/" class="btn btn--primary">Request Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-dark">(716) 602-1429</a>
</div>
</div>
<div data-animate="right">
<div style="display:flex;flex-direction:column;gap:1rem;">
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">{{stat_1_num}}</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">{{stat_1_label}}</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">{{stat_1_sub}}</span>
</div>
</div>
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">{{stat_2_num}}</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">{{stat_2_label}}</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">{{stat_2_sub}}</span>
</div>
</div>
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">{{stat_3_num}}</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">{{stat_3_label}}</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">{{stat_3_sub}}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- SERVICES -->
<section class="section section--alt">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">{{city}} Services</span>
<h2>Hardwood Floor Services in {{city}}, {{state}}</h2>
<p>{{services_intro}}</p>
</div>
<div class="grid grid--auto-3">
<div class="service-card" data-animate="up">
<div class="service-card-body">
<h3>{{service_1_title}}</h3>
<p>{{service_1_body}}</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-refinishing/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
<div class="service-card" data-animate="up" data-delay="2">
<div class="service-card-body">
<h3>{{service_2_title}}</h3>
<p>{{service_2_body}}</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-restoration/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
<div class="service-card" data-animate="up" data-delay="3">
<div class="service-card-body">
<h3>{{service_3_title}}</h3>
<p>{{service_3_body}}</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-sanding/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ -->
<section class="section section--dark">
<div class="container">
<div style="display:grid;grid-template-columns:5fr 7fr;gap:clamp(2rem,5vw,5rem);align-items:start;">
<div data-animate="left">
<span class="eyebrow">{{city}} FAQ</span>
<h2>Common Questions from {{city}} Homeowners</h2>
<div class="divider"></div>
<p style="color:var(--ash);">Have a question specific to your {{city}} home? Call us at <a href="tel:+17166021429" style="color:var(--amber);font-weight:700;">(716) 602-1429</a></p>
</div>
<div class="faq-list" data-animate="up">
{{faq_items}}
</div>
</div>
</div>
</section>
<!-- ESTIMATE FORM -->
<section class="section contact-section">
<div class="container">
<div class="contact-layout">
<div>
<span class="eyebrow">{{city}} Estimate</span>
<h2>{{form_h2}}</h2>
<div class="divider"></div>
<p class="lead">Tell us about your floors and we will respond within 1 business hour.</p>
<div class="contact-info-list" style="margin-top:2rem;">
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z"/></svg>
</div>
<div><h5>Phone</h5><a href="tel:+17166021429">(716) 602-1429</a></div>
</div>
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/></svg>
</div>
<div><h5>Hours</h5><p>Monday to Saturday: 8:00 AM to 5:00 PM</p></div>
</div>
</div>
</div>
<div class="contact-form-wrap" data-animate="right">
<h3 style="margin-bottom:1.5rem;font-size:var(--text-xl);">{{form_h2}}</h3>
<form class="estimate-form" novalidate>
<input type="hidden" name="location" value="{{city}}, {{state}}">
<div class="form-grid form-grid--2">
<div class="form-field">
<label for="name">Full Name <span aria-hidden="true">*</span></label>
<input type="text" id="name" name="name" autocomplete="name" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="phone">Phone Number <span aria-hidden="true">*</span></label>
<input type="tel" id="phone" name="phone" placeholder="(716) 000-0000" autocomplete="tel" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="email">Email Address <span aria-hidden="true">*</span></label>
<input type="email" id="email" name="email" autocomplete="email" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="address">{{form_address_label}} <span aria-hidden="true">*</span></label>
<input type="text" id="address" name="address" autocomplete="street-address" required>
<span class="err-msg" role="alert"></span>
</div>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="service">Service Needed <span aria-hidden="true">*</span></label>
<select id="service" name="service" required>
<option value="">Select a service</option>
<option value="refinishing">Floor Refinishing</option>
<option value="restoration">Floor Restoration</option>
<option value="sanding">Floor Sanding</option>
<option value="installation">Floor Installation</option>
<option value="unsure">Not Sure</option>
</select>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="message">Additional Details</label>
<textarea id="message" name="message" placeholder="Describe your floors or the issue you are experiencing."></textarea>
</div>
<div class="form-trust">
<div class="form-trust-item">1 business hour response</div>
<div class="form-trust-item">No obligation</div>
<div class="form-trust-item">Licensed &amp; insured</div>
</div>
<button type="submit" class="btn btn--primary btn--full btn--lg" style="margin-top:1.25rem;">{{form_submit}}</button>
<div class="form-status" role="status" aria-live="polite"></div>
</form>
</div>
</div>
</div>
</section>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/form.js"></script>
</body>
</html>
-284
View File
@@ -1,284 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>Hardwood Floor Refinishing in Amherst, NY | Floor It</title>
<meta name="description" content="Professional hardwood floor refinishing and restoration in Amherst, NY. Floor It serves Erie County homeowners with 75 years combined experience. Call (716) 602-1429.">
<link rel="canonical" href="https://floorithardwoodfloors.com/locations/amherst.html">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "Floor It Hardwood Floors",
"url": "https://floorithardwoodfloors.com/locations/amherst.html",
"telephone": "+17166021429",
"email": "floorithardwoods@gmail.com",
"address": {
"@type": "PostalAddress",
"addressLocality": "Amherst",
"addressRegion": "NY",
"addressCountry": "US"
},
"areaServed": "Amherst, NY",
"openingHours": "Mo-Sa 08:00-17:00",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.9",
"reviewCount": "50"
}
}
</script>
</head>
<body>
<div id="site-header"></div>
<main>
<!-- HERO -->
<section class="page-hero">
<div class="container page-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">/</span>
<a href="/locations/">Locations</a>
<span class="breadcrumb-sep">/</span>
<span>Amherst, NY</span>
</nav>
<span class="eyebrow">Amherst, New York</span>
<h1>Hardwood Floor Refinishing in Amherst, NY</h1>
<p class="lead">Trusted hardwood floor refinishing, restoration, and installation for Amherst homeowners. Professional results, 24-hour response time, serving the entire Amherst community.</p>
<div class="cta-group" style="margin-top:2rem;">
<a href="/contact/" class="btn btn--primary btn--lg">Request an Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-light btn--lg">Call (716) 602-1429</a>
</div>
</div>
</section>
<!-- OVERVIEW -->
<section class="section section--light">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,5rem);align-items:center;">
<div data-animate="left">
<span class="eyebrow">Amherst Specialists</span>
<h2>Floor Refinishing Services in Amherst, NY</h2>
<div class="divider"></div>
<p style="color:var(--smoke);">Amherst homeowners deserve hardwood floor specialists who understand the local community and the homes in it. Floor It brings 75 years of combined experience and commercial-grade equipment to every Amherst project.</p>
<p style="margin-top:1rem;color:var(--smoke);">From residential refinishing to water damage restoration, we handle every aspect of hardwood floor care. Amherst is part of our core Erie County service area, and we respond to all inquiries within 24 hours and schedule onsite visits at your convenience.</p>
<div class="cta-group mt-8">
<a href="/contact/" class="btn btn--primary">Request Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-dark">(716) 602-1429</a>
</div>
</div>
<div data-animate="right">
<div style="display:flex;flex-direction:column;gap:1rem;">
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">75+</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">Years Combined Experience</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">Across our full team</span>
</div>
</div>
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">24hr</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">Response Time</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">For all Amherst inquiries</span>
</div>
</div>
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">4.9</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">Google Rating</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">Verified customer reviews</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- SERVICES -->
<section class="section section--alt">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Amherst Services</span>
<h2>Hardwood Floor Services in Amherst, NY</h2>
<p>All three of our core hardwood floor services are available throughout Amherst, delivered with the same professional equipment and standards we bring to every Erie County project.</p>
</div>
<div class="grid grid--auto-3">
<div class="service-card" data-animate="up">
<div class="service-card-body">
<h3>Floor Refinishing Amherst</h3>
<p>Multi-stage sanding, multiple stain options, and professional-grade sealing for Amherst homes. Restore beauty and durability.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-refinishing/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
<div class="service-card" data-animate="up" data-delay="2">
<div class="service-card-body">
<h3>Floor Restoration Amherst</h3>
<p>Water damage, warping, and deep scratch repair for Amherst homes. Full restoration with insurance documentation support where needed.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-restoration/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
<div class="service-card" data-animate="up" data-delay="3">
<div class="service-card-body">
<h3>Floor Sanding Amherst</h3>
<p>Commercial dustless sanding for Amherst residents. Clean, safe, and effective. Multi-grit process for a perfect surface.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-sanding/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ -->
<section class="section section--dark">
<div class="container">
<div style="display:grid;grid-template-columns:5fr 7fr;gap:clamp(2rem,5vw,5rem);align-items:start;">
<div data-animate="left">
<span class="eyebrow">Amherst FAQ</span>
<h2>Common Questions from Amherst Homeowners</h2>
<div class="divider"></div>
<p style="color:var(--ash);">Have a question specific to your Amherst home? Call us at <a href="tel:+17166021429" style="color:var(--amber);font-weight:700;">(716) 602-1429</a></p>
</div>
<div class="faq-list" data-animate="up">
<div class="faq-item">
<div class="faq-question">
<h4>How quickly can you respond to Amherst inquiries?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">We respond to all Amherst estimate requests within 24 hours. Amherst is within our core Erie County service area, so scheduling is fast. We can typically arrange an onsite visit within a few days of initial contact.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>What types of floors do you refinish in Amherst?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">We refinish all types of hardwood in Amherst homes including solid oak, maple, cherry, walnut, engineered hardwood, and parquet. We assess each floor individually and recommend the appropriate approach based on wood species, thickness, and current condition.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>Are you licensed and insured to work in Amherst?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Yes. Floor It is fully licensed and insured to work throughout Erie County including Amherst. We carry all required insurance and adhere to all New York State regulations for contractor work in residential homes.</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ESTIMATE FORM -->
<section class="section contact-section">
<div class="container">
<div class="contact-layout">
<div>
<span class="eyebrow">Amherst Estimate</span>
<h2>Request an Amherst Floor Estimate</h2>
<div class="divider"></div>
<p class="lead">Tell us about your floors and we will respond within 1 business hour.</p>
<div class="contact-info-list" style="margin-top:2rem;">
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z"/></svg>
</div>
<div><h5>Phone</h5><a href="tel:+17166021429">(716) 602-1429</a></div>
</div>
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/></svg>
</div>
<div><h5>Hours</h5><p>Monday to Saturday: 8:00 AM to 5:00 PM</p></div>
</div>
</div>
</div>
<div class="contact-form-wrap" data-animate="right">
<h3 style="margin-bottom:1.5rem;font-size:var(--text-xl);">Request an Amherst Floor Estimate</h3>
<form class="estimate-form" novalidate>
<input type="hidden" name="location" value="Amherst, NY">
<div class="form-grid form-grid--2">
<div class="form-field">
<label for="name">Full Name <span aria-hidden="true">*</span></label>
<input type="text" id="name" name="name" autocomplete="name" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="phone">Phone Number <span aria-hidden="true">*</span></label>
<input type="tel" id="phone" name="phone" placeholder="(716) 000-0000" autocomplete="tel" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="email">Email Address <span aria-hidden="true">*</span></label>
<input type="email" id="email" name="email" autocomplete="email" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="address">Amherst Address <span aria-hidden="true">*</span></label>
<input type="text" id="address" name="address" autocomplete="street-address" required>
<span class="err-msg" role="alert"></span>
</div>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="service">Service Needed <span aria-hidden="true">*</span></label>
<select id="service" name="service" required>
<option value="">Select a service</option>
<option value="refinishing">Floor Refinishing</option>
<option value="restoration">Floor Restoration</option>
<option value="sanding">Floor Sanding</option>
<option value="installation">Floor Installation</option>
<option value="unsure">Not Sure</option>
</select>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="message">Additional Details</label>
<textarea id="message" name="message" placeholder="Describe your floors or the issue you are experiencing."></textarea>
</div>
<div class="form-trust">
<div class="form-trust-item">1 business hour response</div>
<div class="form-trust-item">No obligation</div>
<div class="form-trust-item">Licensed &amp; insured</div>
</div>
<button type="submit" class="btn btn--primary btn--full btn--lg" style="margin-top:1.25rem;">Send Amherst Estimate Request</button>
<div class="form-status" role="status" aria-live="polite"></div>
</form>
</div>
</div>
</div>
</section>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/form.js"></script>
</body>
</html>
-293
View File
@@ -1,293 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>Hardwood Floor Refinishing in Buffalo, NY | Floor It</title>
<meta name="description" content="Professional hardwood floor refinishing, restoration, and sanding in Buffalo, NY. 30+ years serving Erie County. 75 years combined experience. Call (716) 602-1429.">
<link rel="canonical" href="https://floorithardwoodfloors.com/locations/buffalo.html">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "Floor It Hardwood Floors",
"url": "https://floorithardwoodfloors.com/locations/buffalo.html",
"telephone": "+17166021429",
"email": "floorithardwoods@gmail.com",
"address": {
"@type": "PostalAddress",
"addressLocality": "Buffalo",
"addressRegion": "NY",
"addressCountry": "US"
},
"areaServed": "Buffalo, NY",
"openingHours": "Mo-Sa 08:00-17:00",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.9",
"reviewCount": "50"
}
}
</script>
</head>
<body>
<div id="site-header"></div>
<main>
<!-- HERO -->
<section class="page-hero">
<div class="container page-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">/</span>
<a href="/locations/">Locations</a>
<span class="breadcrumb-sep">/</span>
<span>Buffalo, NY</span>
</nav>
<span class="eyebrow">Buffalo, New York</span>
<h1>Hardwood Floor Refinishing in Buffalo, NY</h1>
<p class="lead">Western New York's most experienced hardwood floor specialists. 30+ years serving Buffalo homeowners, from historic Elmwood Village homes to modern neighborhoods throughout Erie County.</p>
<div class="cta-group" style="margin-top:2rem;">
<a href="/contact/" class="btn btn--primary btn--lg">Request an Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-light btn--lg">Call (716) 602-1429</a>
</div>
</div>
</section>
<!-- OVERVIEW -->
<section class="section section--light">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,5rem);align-items:center;">
<div data-animate="left">
<span class="eyebrow">Serving Buffalo Since 1994</span>
<h2>Buffalo's Trusted Floor Refinishing Specialists</h2>
<div class="divider"></div>
<p style="color:var(--smoke);">Buffalo's climate presents unique challenges for hardwood floors. High humidity in summer, dry winters, and the dramatic temperature swings that come with Western New York weather require expertise that only comes from decades of local experience.</p>
<p style="margin-top:1rem;color:var(--smoke);">Our team has worked on hundreds of Buffalo homes, from the historic older homes of the West Side and Elmwood Village to newer construction throughout the suburbs. We understand the wood species common to this region and how to bring out the best in each floor.</p>
<div class="cta-group mt-8">
<a href="/contact/" class="btn btn--primary">Request Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-dark">(716) 602-1429</a>
</div>
</div>
<div data-animate="right">
<div style="display:flex;flex-direction:column;gap:1rem;">
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">30+</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">Years Serving Buffalo</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">Our primary market since we launched</span>
</div>
</div>
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">500+</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">Buffalo Projects Completed</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">Homes across every Buffalo neighborhood</span>
</div>
</div>
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">24hr</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">Response Time</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">We get back to every Buffalo inquiry</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- SERVICES -->
<section class="section section--alt">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Buffalo Services</span>
<h2>Hardwood Floor Services in Buffalo, NY</h2>
<p>We provide all three core hardwood floor services throughout Buffalo and Erie County, with the same commercial-grade equipment and professional team on every project.</p>
</div>
<div class="grid grid--auto-3">
<div class="service-card" data-animate="up">
<div class="service-card-body">
<h3>Floor Refinishing Buffalo</h3>
<p>Multi-stage sanding, multiple stain options, and professional-grade sealing for Buffalo homes. Restore beauty and durability.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-refinishing/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
<div class="service-card" data-animate="up" data-delay="2">
<div class="service-card-body">
<h3>Floor Restoration Buffalo</h3>
<p>Water damage, warping, and deep scratch repair common in Buffalo's older homes. Full restoration with insurance documentation support.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-restoration/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
<div class="service-card" data-animate="up" data-delay="3">
<div class="service-card-body">
<h3>Floor Sanding Buffalo</h3>
<p>Commercial dustless sanding throughout Buffalo. Multi-grit process for perfect surface preparation before staining and sealing.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-sanding/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ -->
<section class="section section--dark">
<div class="container">
<div style="display:grid;grid-template-columns:5fr 7fr;gap:clamp(2rem,5vw,5rem);align-items:start;">
<div data-animate="left">
<span class="eyebrow">Buffalo FAQ</span>
<h2>Common Questions from Buffalo Homeowners</h2>
<div class="divider"></div>
<p style="color:var(--ash);">Have a question specific to your Buffalo home? Call us at <a href="tel:+17166021429" style="color:var(--amber);font-weight:700;">(716) 602-1429</a></p>
</div>
<div class="faq-list" data-animate="up">
<div class="faq-item">
<div class="faq-question">
<h4>How does Buffalo's humidity affect hardwood floors?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Buffalo's climate swings create real challenges. High summer humidity causes floors to expand and absorb moisture, while dry winters cause contraction. Our team accounts for seasonal conditions when scheduling refinishing projects and advises homeowners on maintaining proper humidity levels (35 to 55 percent) year-round to protect their investment.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>Can you restore old hardwood floors in historic Buffalo homes?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Yes. Many Buffalo homes have original hardwood floors that are 80 to 100 years old or more. These floors are often superior to modern wood in terms of density and character. We have extensive experience restoring original oak, maple, and other species common in Buffalo's historic housing stock, bringing them back to their original beauty.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>How long does floor refinishing take in my Buffalo home?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Standard refinishing takes 7 to 10 business days from project start to final coat cure. Restoration projects involving water damage or structural repair take 10 to 14 business days. We provide a detailed timeline during the onsite estimate and keep you informed throughout the project.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>Do you serve all Buffalo neighborhoods?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Yes. We serve all Buffalo neighborhoods including Elmwood Village, Allentown, North Buffalo, South Buffalo, West Side, the East Side, and all surrounding suburbs throughout Erie County. Response time is the same 24 hours across all areas.</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ESTIMATE FORM -->
<section class="section contact-section">
<div class="container">
<div class="contact-layout">
<div>
<span class="eyebrow">Buffalo Estimate</span>
<h2>Request a Buffalo Floor Estimate</h2>
<div class="divider"></div>
<p class="lead">Tell us about your floors and we will respond within 1 business hour.</p>
<div class="contact-info-list" style="margin-top:2rem;">
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z"/></svg>
</div>
<div><h5>Phone</h5><a href="tel:+17166021429">(716) 602-1429</a></div>
</div>
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/></svg>
</div>
<div><h5>Hours</h5><p>Monday to Saturday: 8:00 AM to 5:00 PM</p></div>
</div>
</div>
</div>
<div class="contact-form-wrap" data-animate="right">
<h3 style="margin-bottom:1.5rem;font-size:var(--text-xl);">Request a Buffalo Floor Estimate</h3>
<form class="estimate-form" novalidate>
<input type="hidden" name="location" value="Buffalo, NY">
<div class="form-grid form-grid--2">
<div class="form-field">
<label for="name">Full Name <span aria-hidden="true">*</span></label>
<input type="text" id="name" name="name" autocomplete="name" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="phone">Phone Number <span aria-hidden="true">*</span></label>
<input type="tel" id="phone" name="phone" placeholder="(716) 000-0000" autocomplete="tel" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="email">Email Address <span aria-hidden="true">*</span></label>
<input type="email" id="email" name="email" autocomplete="email" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="address">Buffalo Address <span aria-hidden="true">*</span></label>
<input type="text" id="address" name="address" autocomplete="street-address" required>
<span class="err-msg" role="alert"></span>
</div>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="service">Service Needed <span aria-hidden="true">*</span></label>
<select id="service" name="service" required>
<option value="">Select a service</option>
<option value="refinishing">Floor Refinishing</option>
<option value="restoration">Floor Restoration</option>
<option value="sanding">Floor Sanding</option>
<option value="installation">Floor Installation</option>
<option value="unsure">Not Sure</option>
</select>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="message">Additional Details</label>
<textarea id="message" name="message" placeholder="Describe your floors or the issue you are experiencing."></textarea>
</div>
<div class="form-trust">
<div class="form-trust-item">1 business hour response</div>
<div class="form-trust-item">No obligation</div>
<div class="form-trust-item">Licensed &amp; insured</div>
</div>
<button type="submit" class="btn btn--primary btn--full btn--lg" style="margin-top:1.25rem;">Send Buffalo Estimate Request</button>
<div class="form-status" role="status" aria-live="polite"></div>
</form>
</div>
</div>
</div>
</section>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/form.js"></script>
</body>
</html>
-284
View File
@@ -1,284 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>Hardwood Floor Refinishing in Clarence, NY | Floor It</title>
<meta name="description" content="Trusted hardwood floor refinishing and restoration in Clarence, NY. Floor It serves Erie County with 75 years combined experience. Licensed and insured. (716) 602-1429.">
<link rel="canonical" href="https://floorithardwoodfloors.com/locations/clarence.html">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "Floor It Hardwood Floors",
"url": "https://floorithardwoodfloors.com/locations/clarence.html",
"telephone": "+17166021429",
"email": "floorithardwoods@gmail.com",
"address": {
"@type": "PostalAddress",
"addressLocality": "Clarence",
"addressRegion": "NY",
"addressCountry": "US"
},
"areaServed": "Clarence, NY",
"openingHours": "Mo-Sa 08:00-17:00",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.9",
"reviewCount": "50"
}
}
</script>
</head>
<body>
<div id="site-header"></div>
<main>
<!-- HERO -->
<section class="page-hero">
<div class="container page-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">/</span>
<a href="/locations/">Locations</a>
<span class="breadcrumb-sep">/</span>
<span>Clarence, NY</span>
</nav>
<span class="eyebrow">Clarence, New York</span>
<h1>Hardwood Floor Refinishing in Clarence, NY</h1>
<p class="lead">Hardwood floor refinishing, restoration, and installation for Clarence homeowners. Reliable scheduling, professional results, and a team that Erie County has trusted for over 30 years.</p>
<div class="cta-group" style="margin-top:2rem;">
<a href="/contact/" class="btn btn--primary btn--lg">Request an Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-light btn--lg">Call (716) 602-1429</a>
</div>
</div>
</section>
<!-- OVERVIEW -->
<section class="section section--light">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,5rem);align-items:center;">
<div data-animate="left">
<span class="eyebrow">Clarence Service Area</span>
<h2>Floor Refinishing Services in Clarence, NY</h2>
<div class="divider"></div>
<p style="color:var(--smoke);">Clarence homeowners trust Floor It for professional hardwood floor care. Our team brings 75 years of combined experience to every project in Clarence, delivering the same quality results we provide throughout Erie County.</p>
<p style="margin-top:1rem;color:var(--smoke);">We respond to all Clarence inquiries within 24 hours. From initial estimate to completed project, our process is straightforward and transparent: detailed quotes, clear timelines, and professional workmanship every step of the way.</p>
<div class="cta-group mt-8">
<a href="/contact/" class="btn btn--primary">Request Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-dark">(716) 602-1429</a>
</div>
</div>
<div data-animate="right">
<div style="display:flex;flex-direction:column;gap:1rem;">
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">75+</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">Years Combined Experience</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">Serving Western New York</span>
</div>
</div>
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">24hr</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">Response Time</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">For Clarence inquiries</span>
</div>
</div>
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">4.9</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">Google Rating</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">Verified customer reviews</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- SERVICES -->
<section class="section section--alt">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Clarence Services</span>
<h2>Hardwood Floor Services in Clarence, NY</h2>
<p>Complete hardwood floor services throughout Clarence, with the same professional team and equipment on every project.</p>
</div>
<div class="grid grid--auto-3">
<div class="service-card" data-animate="up">
<div class="service-card-body">
<h3>Floor Refinishing Clarence</h3>
<p>Multi-stage sanding, multiple stain options, and durable professional sealing for Clarence homes.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-refinishing/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
<div class="service-card" data-animate="up" data-delay="2">
<div class="service-card-body">
<h3>Floor Restoration Clarence</h3>
<p>Water damage, scratch, and structural floor repair for Clarence homeowners. Insurance documentation available.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-restoration/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
<div class="service-card" data-animate="up" data-delay="3">
<div class="service-card-body">
<h3>Floor Sanding Clarence</h3>
<p>Commercial dustless sanding for Clarence homes. Safe, clean, thorough preparation for refinishing.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-sanding/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ -->
<section class="section section--dark">
<div class="container">
<div style="display:grid;grid-template-columns:5fr 7fr;gap:clamp(2rem,5vw,5rem);align-items:start;">
<div data-animate="left">
<span class="eyebrow">Clarence FAQ</span>
<h2>Common Questions from Clarence Homeowners</h2>
<div class="divider"></div>
<p style="color:var(--ash);">Have a question specific to your Clarence home? Call us at <a href="tel:+17166021429" style="color:var(--amber);font-weight:700;">(716) 602-1429</a></p>
</div>
<div class="faq-list" data-animate="up">
<div class="faq-item">
<div class="faq-question">
<h4>How long has Floor It been serving the Clarence area?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Floor It has been serving Erie County, including Clarence, for over 30 years. Clarence is within our core service area and we have completed projects throughout the community.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>What is the timeline for a refinishing project in Clarence?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Standard refinishing typically takes 7 to 10 business days. Restoration projects can take 10 to 14 business days depending on the extent of damage. We provide a full timeline during the onsite estimate visit before any work begins.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>Do you provide written quotes for Clarence projects?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Yes. Every project receives a detailed written quote within 2 business days of the onsite estimate visit. The quote includes multiple options, clear pricing, and a project timeline, so you can make an informed decision with no pressure.</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ESTIMATE FORM -->
<section class="section contact-section">
<div class="container">
<div class="contact-layout">
<div>
<span class="eyebrow">Clarence Estimate</span>
<h2>Request a Clarence Floor Estimate</h2>
<div class="divider"></div>
<p class="lead">Tell us about your floors and we will respond within 1 business hour.</p>
<div class="contact-info-list" style="margin-top:2rem;">
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z"/></svg>
</div>
<div><h5>Phone</h5><a href="tel:+17166021429">(716) 602-1429</a></div>
</div>
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/></svg>
</div>
<div><h5>Hours</h5><p>Monday to Saturday: 8:00 AM to 5:00 PM</p></div>
</div>
</div>
</div>
<div class="contact-form-wrap" data-animate="right">
<h3 style="margin-bottom:1.5rem;font-size:var(--text-xl);">Request a Clarence Floor Estimate</h3>
<form class="estimate-form" novalidate>
<input type="hidden" name="location" value="Clarence, NY">
<div class="form-grid form-grid--2">
<div class="form-field">
<label for="name">Full Name <span aria-hidden="true">*</span></label>
<input type="text" id="name" name="name" autocomplete="name" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="phone">Phone Number <span aria-hidden="true">*</span></label>
<input type="tel" id="phone" name="phone" placeholder="(716) 000-0000" autocomplete="tel" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="email">Email Address <span aria-hidden="true">*</span></label>
<input type="email" id="email" name="email" autocomplete="email" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="address">Clarence Address <span aria-hidden="true">*</span></label>
<input type="text" id="address" name="address" autocomplete="street-address" required>
<span class="err-msg" role="alert"></span>
</div>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="service">Service Needed <span aria-hidden="true">*</span></label>
<select id="service" name="service" required>
<option value="">Select a service</option>
<option value="refinishing">Floor Refinishing</option>
<option value="restoration">Floor Restoration</option>
<option value="sanding">Floor Sanding</option>
<option value="installation">Floor Installation</option>
<option value="unsure">Not Sure</option>
</select>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="message">Additional Details</label>
<textarea id="message" name="message" placeholder="Describe your floors or the issue you are experiencing."></textarea>
</div>
<div class="form-trust">
<div class="form-trust-item">1 business hour response</div>
<div class="form-trust-item">No obligation</div>
<div class="form-trust-item">Licensed &amp; insured</div>
</div>
<button type="submit" class="btn btn--primary btn--full btn--lg" style="margin-top:1.25rem;">Send Clarence Estimate Request</button>
<div class="form-status" role="status" aria-live="polite"></div>
</form>
</div>
</div>
</div>
</section>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/form.js"></script>
</body>
</html>
-284
View File
@@ -1,284 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>Hardwood Floor Refinishing in East Amherst, NY | Floor It</title>
<meta name="description" content="Professional hardwood floor refinishing and restoration in East Amherst, NY. Erie County specialists with 75 years combined experience. Call (716) 602-1429.">
<link rel="canonical" href="https://floorithardwoodfloors.com/locations/east-amherst.html">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "Floor It Hardwood Floors",
"url": "https://floorithardwoodfloors.com/locations/east-amherst.html",
"telephone": "+17166021429",
"email": "floorithardwoods@gmail.com",
"address": {
"@type": "PostalAddress",
"addressLocality": "East Amherst",
"addressRegion": "NY",
"addressCountry": "US"
},
"areaServed": "East Amherst, NY",
"openingHours": "Mo-Sa 08:00-17:00",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.9",
"reviewCount": "50"
}
}
</script>
</head>
<body>
<div id="site-header"></div>
<main>
<!-- HERO -->
<section class="page-hero">
<div class="container page-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">/</span>
<a href="/locations/">Locations</a>
<span class="breadcrumb-sep">/</span>
<span>East Amherst, NY</span>
</nav>
<span class="eyebrow">East Amherst, New York</span>
<h1>Hardwood Floor Refinishing in East Amherst, NY</h1>
<p class="lead">Professional hardwood floor services for East Amherst homeowners. The same expert team, commercial equipment, and quality results we bring to every Erie County home.</p>
<div class="cta-group" style="margin-top:2rem;">
<a href="/contact/" class="btn btn--primary btn--lg">Request an Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-light btn--lg">Call (716) 602-1429</a>
</div>
</div>
</section>
<!-- OVERVIEW -->
<section class="section section--light">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,5rem);align-items:center;">
<div data-animate="left">
<span class="eyebrow">East Amherst Service Area</span>
<h2>Floor Refinishing Services in East Amherst, NY</h2>
<div class="divider"></div>
<p style="color:var(--smoke);">East Amherst homeowners trust Floor It for hardwood floor refinishing, restoration, and sanding. We bring 75 years of combined experience and commercial-grade equipment to every project, applying the same professional standards throughout Erie County.</p>
<p style="margin-top:1rem;color:var(--smoke);">We respond to all East Amherst inquiries within 24 hours and schedule onsite visits at your convenience. Our team is familiar with the home styles, wood species, and floor conditions common throughout the East Amherst area.</p>
<div class="cta-group mt-8">
<a href="/contact/" class="btn btn--primary">Request Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-dark">(716) 602-1429</a>
</div>
</div>
<div data-animate="right">
<div style="display:flex;flex-direction:column;gap:1rem;">
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">75+</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">Years Combined Experience</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">Serving Western New York</span>
</div>
</div>
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">24hr</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">Response Time</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">For East Amherst inquiries</span>
</div>
</div>
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">4.9</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">Google Rating</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">Verified customer reviews</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- SERVICES -->
<section class="section section--alt">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">East Amherst Services</span>
<h2>Hardwood Floor Services in East Amherst, NY</h2>
<p>All core hardwood floor services available throughout East Amherst, delivered with the same professional equipment and team on every project.</p>
</div>
<div class="grid grid--auto-3">
<div class="service-card" data-animate="up">
<div class="service-card-body">
<h3>Floor Refinishing East Amherst</h3>
<p>Multi-stage sanding, multiple stain options, and professional sealing for East Amherst homes. Restore beauty and durability.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-refinishing/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
<div class="service-card" data-animate="up" data-delay="2">
<div class="service-card-body">
<h3>Floor Restoration East Amherst</h3>
<p>Water damage, deep scratch, and structural floor repair for East Amherst homeowners. Full restoration with insurance documentation available.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-restoration/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
<div class="service-card" data-animate="up" data-delay="3">
<div class="service-card-body">
<h3>Floor Sanding East Amherst</h3>
<p>Commercial dustless sanding for East Amherst homes. Safe, clean, effective surface preparation.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-sanding/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ -->
<section class="section section--dark">
<div class="container">
<div style="display:grid;grid-template-columns:5fr 7fr;gap:clamp(2rem,5vw,5rem);align-items:start;">
<div data-animate="left">
<span class="eyebrow">East Amherst FAQ</span>
<h2>Common Questions from East Amherst Homeowners</h2>
<div class="divider"></div>
<p style="color:var(--ash);">Have a question specific to your East Amherst home? Call us at <a href="tel:+17166021429" style="color:var(--amber);font-weight:700;">(716) 602-1429</a></p>
</div>
<div class="faq-list" data-animate="up">
<div class="faq-item">
<div class="faq-question">
<h4>Do you serve East Amherst as part of your regular route?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Yes. East Amherst is within our core Erie County service area. We schedule visits to East Amherst regularly and can typically arrange an onsite estimate within a few days of initial contact.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>What hardwood floor services do you offer in East Amherst?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">We offer full hardwood floor refinishing, restoration, and sanding throughout East Amherst. This covers everything from surface re-coating to complete restoration of water-damaged or structurally compromised floors.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>Is your dustless sanding safe for families with children and pets?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Yes. Our dustless sanding system captures fine particles at the source, dramatically reducing airborne dust. We also use low-VOC finishes when possible. We recommend vacating the work area during sanding and for 24 to 48 hours after final coat application.</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ESTIMATE FORM -->
<section class="section contact-section">
<div class="container">
<div class="contact-layout">
<div>
<span class="eyebrow">East Amherst Estimate</span>
<h2>Request an East Amherst Floor Estimate</h2>
<div class="divider"></div>
<p class="lead">Tell us about your floors and we will respond within 1 business hour.</p>
<div class="contact-info-list" style="margin-top:2rem;">
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z"/></svg>
</div>
<div><h5>Phone</h5><a href="tel:+17166021429">(716) 602-1429</a></div>
</div>
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/></svg>
</div>
<div><h5>Hours</h5><p>Monday to Saturday: 8:00 AM to 5:00 PM</p></div>
</div>
</div>
</div>
<div class="contact-form-wrap" data-animate="right">
<h3 style="margin-bottom:1.5rem;font-size:var(--text-xl);">Request an East Amherst Floor Estimate</h3>
<form class="estimate-form" novalidate>
<input type="hidden" name="location" value="East Amherst, NY">
<div class="form-grid form-grid--2">
<div class="form-field">
<label for="name">Full Name <span aria-hidden="true">*</span></label>
<input type="text" id="name" name="name" autocomplete="name" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="phone">Phone Number <span aria-hidden="true">*</span></label>
<input type="tel" id="phone" name="phone" placeholder="(716) 000-0000" autocomplete="tel" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="email">Email Address <span aria-hidden="true">*</span></label>
<input type="email" id="email" name="email" autocomplete="email" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="address">East Amherst Address <span aria-hidden="true">*</span></label>
<input type="text" id="address" name="address" autocomplete="street-address" required>
<span class="err-msg" role="alert"></span>
</div>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="service">Service Needed <span aria-hidden="true">*</span></label>
<select id="service" name="service" required>
<option value="">Select a service</option>
<option value="refinishing">Floor Refinishing</option>
<option value="restoration">Floor Restoration</option>
<option value="sanding">Floor Sanding</option>
<option value="installation">Floor Installation</option>
<option value="unsure">Not Sure</option>
</select>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="message">Additional Details</label>
<textarea id="message" name="message" placeholder="Describe your floors or the issue you are experiencing."></textarea>
</div>
<div class="form-trust">
<div class="form-trust-item">1 business hour response</div>
<div class="form-trust-item">No obligation</div>
<div class="form-trust-item">Licensed &amp; insured</div>
</div>
<button type="submit" class="btn btn--primary btn--full btn--lg" style="margin-top:1.25rem;">Send East Amherst Estimate Request</button>
<div class="form-status" role="status" aria-live="polite"></div>
</form>
</div>
</div>
</div>
</section>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/form.js"></script>
</body>
</html>
-181
View File
@@ -1,181 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>Hardwood Floor Refinishing Service Areas | Western NY | Floor It</title>
<meta name="description" content="Floor It Hardwood Floors serves Buffalo, Amherst, Williamsville, East Amherst, Clarence, Lancaster, and surrounding Erie County communities. Professional hardwood floor services throughout Western New York.">
<link rel="canonical" href="https://floorithardwoodfloors.com/locations/">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
</head>
<body>
<div id="site-header"></div>
<main>
<section class="page-hero">
<div class="container page-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">/</span>
<span>Locations</span>
</nav>
<span class="eyebrow">Where We Work</span>
<h1>Hardwood Floor Services Across Western New York</h1>
<p class="lead">Floor It serves homeowners throughout Buffalo and Erie County with the same professional standards, equipment, and care at every location.</p>
</div>
</section>
<!-- LOCATION CARDS DETAIL -->
<section class="section section--light">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Our Service Areas</span>
<h2>Cities We Serve</h2>
<p>Select a city to learn more about our hardwood floor services in your area.</p>
</div>
<div class="grid grid--auto-3">
<div class="card" data-animate="up">
<div style="height:160px;background:var(--bark-mid);border-radius:var(--radius-lg) var(--radius-lg) 0 0;overflow:hidden;">
<img src="/assets/images/location-buffalo.jpg" alt="Buffalo, NY skyline" style="width:100%;height:100%;object-fit:cover;" loading="lazy" onerror="this.parentElement.style.background='var(--bark-mid)'">
</div>
<div style="padding:1.75rem;">
<span class="eyebrow" style="margin-bottom:0.5rem;">Primary Hub</span>
<h3 style="font-size:var(--text-xl);margin-bottom:0.75rem;">Buffalo, NY</h3>
<p style="font-size:var(--text-sm);color:var(--smoke);max-width:none;">Our primary service area. Expert hardwood floor refinishing, restoration, and installation throughout Buffalo, including older homes with historic hardwood floors.</p>
<div style="margin-top:1.25rem;display:flex;gap:0.75rem;flex-wrap:wrap;">
<a href="/locations/buffalo.html" class="btn btn--primary btn--sm">View Buffalo Services</a>
</div>
</div>
</div>
<div class="card" data-animate="up" data-delay="2">
<div style="height:160px;background:var(--bark-mid);border-radius:var(--radius-lg) var(--radius-lg) 0 0;overflow:hidden;">
<img src="/assets/images/location-amherst.jpg" alt="Amherst, NY neighborhood" style="width:100%;height:100%;object-fit:cover;" loading="lazy" onerror="this.parentElement.style.background='var(--bark-mid)'">
</div>
<div style="padding:1.75rem;">
<span class="eyebrow" style="margin-bottom:0.5rem;">Erie County</span>
<h3 style="font-size:var(--text-xl);margin-bottom:0.75rem;">Amherst, NY</h3>
<p style="font-size:var(--text-sm);color:var(--smoke);max-width:none;">Residential hardwood floor refinishing and restoration for Amherst homeowners. Community-focused service with fast scheduling and 24-hour response times.</p>
<div style="margin-top:1.25rem;display:flex;gap:0.75rem;flex-wrap:wrap;">
<a href="/locations/amherst.html" class="btn btn--primary btn--sm">View Amherst Services</a>
</div>
</div>
</div>
<div class="card" data-animate="up" data-delay="3">
<div style="height:160px;background:var(--bark-mid);border-radius:var(--radius-lg) var(--radius-lg) 0 0;overflow:hidden;">
<img src="/assets/images/location-williamsville.jpg" alt="Williamsville, NY homes" style="width:100%;height:100%;object-fit:cover;" loading="lazy" onerror="this.parentElement.style.background='var(--bark-mid)'">
</div>
<div style="padding:1.75rem;">
<span class="eyebrow" style="margin-bottom:0.5rem;">Erie County</span>
<h3 style="font-size:var(--text-xl);margin-bottom:0.75rem;">Williamsville, NY</h3>
<p style="font-size:var(--text-sm);color:var(--smoke);max-width:none;">Premium hardwood floor refinishing for Williamsville homes. Upscale residential expertise with attention to the highest finish standards.</p>
<div style="margin-top:1.25rem;display:flex;gap:0.75rem;flex-wrap:wrap;">
<a href="/locations/williamsville.html" class="btn btn--primary btn--sm">View Williamsville Services</a>
</div>
</div>
</div>
<div class="card" data-animate="up">
<div style="height:160px;background:var(--bark-mid);border-radius:var(--radius-lg) var(--radius-lg) 0 0;"></div>
<div style="padding:1.75rem;">
<span class="eyebrow" style="margin-bottom:0.5rem;">Erie County</span>
<h3 style="font-size:var(--text-xl);margin-bottom:0.75rem;">East Amherst, NY</h3>
<p style="font-size:var(--text-sm);color:var(--smoke);max-width:none;">Professional hardwood floor services for East Amherst homeowners. Comprehensive service coverage with the same expert team and professional equipment.</p>
<div style="margin-top:1.25rem;display:flex;gap:0.75rem;flex-wrap:wrap;">
<a href="/locations/east-amherst.html" class="btn btn--primary btn--sm">View East Amherst Services</a>
</div>
</div>
</div>
<div class="card" data-animate="up" data-delay="2">
<div style="height:160px;background:var(--bark-mid);border-radius:var(--radius-lg) var(--radius-lg) 0 0;"></div>
<div style="padding:1.75rem;">
<span class="eyebrow" style="margin-bottom:0.5rem;">Erie County</span>
<h3 style="font-size:var(--text-xl);margin-bottom:0.75rem;">Clarence, NY</h3>
<p style="font-size:var(--text-sm);color:var(--smoke);max-width:none;">Hardwood floor refinishing and restoration for Clarence residents. Trusted by the local community with reliable scheduling and professional results.</p>
<div style="margin-top:1.25rem;display:flex;gap:0.75rem;flex-wrap:wrap;">
<a href="/locations/clarence.html" class="btn btn--primary btn--sm">View Clarence Services</a>
</div>
</div>
</div>
<div class="card" data-animate="up" data-delay="3">
<div style="height:160px;background:var(--bark-mid);border-radius:var(--radius-lg) var(--radius-lg) 0 0;"></div>
<div style="padding:1.75rem;">
<span class="eyebrow" style="margin-bottom:0.5rem;">Erie County</span>
<h3 style="font-size:var(--text-xl);margin-bottom:0.75rem;">Lancaster, NY</h3>
<p style="font-size:var(--text-sm);color:var(--smoke);max-width:none;">Professional hardwood floor services for Lancaster homeowners. The same expert standards and care that Buffalo homeowners have trusted for over 30 years.</p>
<div style="margin-top:1.25rem;display:flex;gap:0.75rem;flex-wrap:wrap;">
<a href="/locations/lancaster.html" class="btn btn--primary btn--sm">View Lancaster Services</a>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- SERVICES AT ALL LOCATIONS -->
<section class="section section--mid">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Universal Service Standards</span>
<h2>The Same Quality at Every Location</h2>
<p>No matter where you are in Erie County, you receive the same professional service, equipment, and results.</p>
</div>
<div class="grid grid--auto-3">
<div class="benefit-card" data-animate="up">
<div class="benefit-icon"><svg viewBox="0 0 24 24"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg></div>
<div class="benefit-body">
<h4>Floor Refinishing</h4>
<p>Multi-stage sanding, multiple stain options, and durable sealing. 7 to 10 business day timeline.</p>
</div>
</div>
<div class="benefit-card" data-animate="up" data-delay="2">
<div class="benefit-icon"><svg viewBox="0 0 24 24"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg></div>
<div class="benefit-body">
<h4>Floor Restoration</h4>
<p>Water damage, deep scratches, and structural repairs. Insurance documentation available. 10 to 14 days.</p>
</div>
</div>
<div class="benefit-card" data-animate="up" data-delay="3">
<div class="benefit-icon"><svg viewBox="0 0 24 24"><path d="M4 6h16M4 12h16M4 18h7"/></svg></div>
<div class="benefit-body">
<h4>Floor Sanding</h4>
<p>Commercial-grade HEPA dustless equipment. Multi-grit process for a perfectly prepared surface.</p>
</div>
</div>
</div>
</div>
</section>
<div class="cta-strip">
<div class="container">
<h2>Serving Your Community in Western New York</h2>
<p>Not sure if we cover your area? Call us. We serve all of Erie County and surrounding communities.</p>
<div class="cta-group" style="justify-content:center;">
<a href="/contact/" class="btn btn--outline-dark btn--lg">Request an Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-dark btn--lg">Call (716) 602-1429</a>
</div>
</div>
</div>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
</body>
</html>
-284
View File
@@ -1,284 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>Hardwood Floor Refinishing in Lancaster, NY | Floor It</title>
<meta name="description" content="Professional hardwood floor refinishing and restoration in Lancaster, NY. Serving Erie County with 75 years combined experience. Licensed and insured. Call (716) 602-1429.">
<link rel="canonical" href="https://floorithardwoodfloors.com/locations/lancaster.html">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "Floor It Hardwood Floors",
"url": "https://floorithardwoodfloors.com/locations/lancaster.html",
"telephone": "+17166021429",
"email": "floorithardwoods@gmail.com",
"address": {
"@type": "PostalAddress",
"addressLocality": "Lancaster",
"addressRegion": "NY",
"addressCountry": "US"
},
"areaServed": "Lancaster, NY",
"openingHours": "Mo-Sa 08:00-17:00",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.9",
"reviewCount": "50"
}
}
</script>
</head>
<body>
<div id="site-header"></div>
<main>
<!-- HERO -->
<section class="page-hero">
<div class="container page-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">/</span>
<a href="/locations/">Locations</a>
<span class="breadcrumb-sep">/</span>
<span>Lancaster, NY</span>
</nav>
<span class="eyebrow">Lancaster, New York</span>
<h1>Hardwood Floor Refinishing in Lancaster, NY</h1>
<p class="lead">Professional hardwood floor refinishing and restoration for Lancaster homeowners. The same expert team and commercial equipment that Buffalo homeowners have trusted for over 30 years.</p>
<div class="cta-group" style="margin-top:2rem;">
<a href="/contact/" class="btn btn--primary btn--lg">Request an Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-light btn--lg">Call (716) 602-1429</a>
</div>
</div>
</section>
<!-- OVERVIEW -->
<section class="section section--light">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,5rem);align-items:center;">
<div data-animate="left">
<span class="eyebrow">Lancaster Service Area</span>
<h2>Floor Refinishing Services in Lancaster, NY</h2>
<div class="divider"></div>
<p style="color:var(--smoke);">Lancaster homeowners trust Floor It for professional hardwood floor care. We bring 75 years of combined experience and commercial-grade equipment to every Lancaster project, maintaining the same standards throughout Erie County.</p>
<p style="margin-top:1rem;color:var(--smoke);">Our team responds to all Lancaster inquiries within 24 hours. We schedule onsite visits at your convenience and provide detailed written quotes within 2 business days of the visit. No obligation, no pressure.</p>
<div class="cta-group mt-8">
<a href="/contact/" class="btn btn--primary">Request Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-dark">(716) 602-1429</a>
</div>
</div>
<div data-animate="right">
<div style="display:flex;flex-direction:column;gap:1rem;">
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">75+</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">Years Combined Experience</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">Serving Western New York</span>
</div>
</div>
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">24hr</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">Response Time</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">For Lancaster inquiries</span>
</div>
</div>
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">4.9</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">Google Rating</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">Verified customer reviews</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- SERVICES -->
<section class="section section--alt">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Lancaster Services</span>
<h2>Hardwood Floor Services in Lancaster, NY</h2>
<p>Full hardwood floor services available throughout Lancaster: refinishing, restoration, and sanding with professional-grade equipment.</p>
</div>
<div class="grid grid--auto-3">
<div class="service-card" data-animate="up">
<div class="service-card-body">
<h3>Floor Refinishing Lancaster</h3>
<p>Multi-stage sanding, multiple stain options, and durable professional sealing for Lancaster homes.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-refinishing/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
<div class="service-card" data-animate="up" data-delay="2">
<div class="service-card-body">
<h3>Floor Restoration Lancaster</h3>
<p>Water damage, scratch, and structural floor repair for Lancaster homeowners. Insurance documentation support available.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-restoration/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
<div class="service-card" data-animate="up" data-delay="3">
<div class="service-card-body">
<h3>Floor Sanding Lancaster</h3>
<p>Commercial dustless sanding for Lancaster homes. Safe for families and pets, thorough preparation.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-sanding/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ -->
<section class="section section--dark">
<div class="container">
<div style="display:grid;grid-template-columns:5fr 7fr;gap:clamp(2rem,5vw,5rem);align-items:start;">
<div data-animate="left">
<span class="eyebrow">Lancaster FAQ</span>
<h2>Common Questions from Lancaster Homeowners</h2>
<div class="divider"></div>
<p style="color:var(--ash);">Have a question specific to your Lancaster home? Call us at <a href="tel:+17166021429" style="color:var(--amber);font-weight:700;">(716) 602-1429</a></p>
</div>
<div class="faq-list" data-animate="up">
<div class="faq-item">
<div class="faq-question">
<h4>Do you serve Lancaster as part of your regular route?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Yes. Lancaster is within our Erie County service area and we schedule visits there regularly. We can typically arrange an onsite estimate within a few days of your initial inquiry.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>What hardwood species do you work with in Lancaster homes?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">We work with all common hardwood species found in Lancaster homes: oak, maple, cherry, walnut, ash, and engineered hardwood. We also work with less common domestic and exotic species. Each floor is assessed individually before we recommend an approach.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>How do I prepare my Lancaster home before your team arrives?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Remove furniture and area rugs from the rooms being worked on. Secure or cover any valuables in adjacent areas. We handle all floor preparation, sanding, and finishing. We advise keeping pets and children out of the work area and for 24 to 48 hours after final coat application.</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ESTIMATE FORM -->
<section class="section contact-section">
<div class="container">
<div class="contact-layout">
<div>
<span class="eyebrow">Lancaster Estimate</span>
<h2>Request a Lancaster Floor Estimate</h2>
<div class="divider"></div>
<p class="lead">Tell us about your floors and we will respond within 1 business hour.</p>
<div class="contact-info-list" style="margin-top:2rem;">
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z"/></svg>
</div>
<div><h5>Phone</h5><a href="tel:+17166021429">(716) 602-1429</a></div>
</div>
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/></svg>
</div>
<div><h5>Hours</h5><p>Monday to Saturday: 8:00 AM to 5:00 PM</p></div>
</div>
</div>
</div>
<div class="contact-form-wrap" data-animate="right">
<h3 style="margin-bottom:1.5rem;font-size:var(--text-xl);">Request a Lancaster Floor Estimate</h3>
<form class="estimate-form" novalidate>
<input type="hidden" name="location" value="Lancaster, NY">
<div class="form-grid form-grid--2">
<div class="form-field">
<label for="name">Full Name <span aria-hidden="true">*</span></label>
<input type="text" id="name" name="name" autocomplete="name" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="phone">Phone Number <span aria-hidden="true">*</span></label>
<input type="tel" id="phone" name="phone" placeholder="(716) 000-0000" autocomplete="tel" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="email">Email Address <span aria-hidden="true">*</span></label>
<input type="email" id="email" name="email" autocomplete="email" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="address">Lancaster Address <span aria-hidden="true">*</span></label>
<input type="text" id="address" name="address" autocomplete="street-address" required>
<span class="err-msg" role="alert"></span>
</div>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="service">Service Needed <span aria-hidden="true">*</span></label>
<select id="service" name="service" required>
<option value="">Select a service</option>
<option value="refinishing">Floor Refinishing</option>
<option value="restoration">Floor Restoration</option>
<option value="sanding">Floor Sanding</option>
<option value="installation">Floor Installation</option>
<option value="unsure">Not Sure</option>
</select>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="message">Additional Details</label>
<textarea id="message" name="message" placeholder="Describe your floors or the issue you are experiencing."></textarea>
</div>
<div class="form-trust">
<div class="form-trust-item">1 business hour response</div>
<div class="form-trust-item">No obligation</div>
<div class="form-trust-item">Licensed &amp; insured</div>
</div>
<button type="submit" class="btn btn--primary btn--full btn--lg" style="margin-top:1.25rem;">Send Lancaster Estimate Request</button>
<div class="form-status" role="status" aria-live="polite"></div>
</form>
</div>
</div>
</div>
</section>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/form.js"></script>
</body>
</html>
-284
View File
@@ -1,284 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>Hardwood Floor Refinishing in Williamsville, NY | Floor It</title>
<meta name="description" content="Expert hardwood floor refinishing and restoration in Williamsville, NY. Serving Erie County with 75 years combined experience. Licensed and insured. Call (716) 602-1429.">
<link rel="canonical" href="https://floorithardwoodfloors.com/locations/williamsville.html">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "Floor It Hardwood Floors",
"url": "https://floorithardwoodfloors.com/locations/williamsville.html",
"telephone": "+17166021429",
"email": "floorithardwoods@gmail.com",
"address": {
"@type": "PostalAddress",
"addressLocality": "Williamsville",
"addressRegion": "NY",
"addressCountry": "US"
},
"areaServed": "Williamsville, NY",
"openingHours": "Mo-Sa 08:00-17:00",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.9",
"reviewCount": "50"
}
}
</script>
</head>
<body>
<div id="site-header"></div>
<main>
<!-- HERO -->
<section class="page-hero">
<div class="container page-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">/</span>
<a href="/locations/">Locations</a>
<span class="breadcrumb-sep">/</span>
<span>Williamsville, NY</span>
</nav>
<span class="eyebrow">Williamsville, New York</span>
<h1>Hardwood Floor Refinishing in Williamsville, NY</h1>
<p class="lead">Premium hardwood floor refinishing and restoration for Williamsville homes. Upscale residential expertise with the highest finish standards in Erie County.</p>
<div class="cta-group" style="margin-top:2rem;">
<a href="/contact/" class="btn btn--primary btn--lg">Request an Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-light btn--lg">Call (716) 602-1429</a>
</div>
</div>
</section>
<!-- OVERVIEW -->
<section class="section section--light">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,5rem);align-items:center;">
<div data-animate="left">
<span class="eyebrow">Williamsville Specialists</span>
<h2>Expert Floor Refinishing in Williamsville, NY</h2>
<div class="divider"></div>
<p style="color:var(--smoke);">Williamsville homes deserve a level of craftsmanship that matches their quality. Floor It brings 75 years of combined experience to every Williamsville project, applying the same professional-grade equipment and attention to detail that has made us Western New York's most trusted floor refinishing team.</p>
<p style="margin-top:1rem;color:var(--smoke);">Whether you have original hardwood floors in a classic Williamsville home or newer engineered wood in a modern build, our team assesses each floor individually and recommends the right approach for lasting, beautiful results.</p>
<div class="cta-group mt-8">
<a href="/contact/" class="btn btn--primary">Request Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-dark">(716) 602-1429</a>
</div>
</div>
<div data-animate="right">
<div style="display:flex;flex-direction:column;gap:1rem;">
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">75+</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">Years Combined Experience</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">Serving Western New York</span>
</div>
</div>
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">24hr</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">Response Time</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">For Williamsville inquiries</span>
</div>
</div>
<div style="background:var(--grain);border-radius:var(--radius-lg);padding:1.5rem;display:flex;gap:1rem;align-items:center;">
<div style="font-size:2rem;font-weight:800;color:var(--amber);flex-shrink:0;line-height:1;">4.9</div>
<div>
<strong style="display:block;font-size:var(--text-sm);">Google Rating</strong>
<span style="font-size:var(--text-xs);color:var(--smoke);">Verified customer reviews</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- SERVICES -->
<section class="section section--alt">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Williamsville Services</span>
<h2>Hardwood Floor Services in Williamsville, NY</h2>
<p>Complete hardwood floor services available throughout Williamsville: refinishing, restoration, and sanding with commercial-grade equipment.</p>
</div>
<div class="grid grid--auto-3">
<div class="service-card" data-animate="up">
<div class="service-card-body">
<h3>Floor Refinishing Williamsville</h3>
<p>Premium multi-stage sanding, staining, and sealing for Williamsville homes. Stain sample approval before full project begins.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-refinishing/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
<div class="service-card" data-animate="up" data-delay="2">
<div class="service-card-body">
<h3>Floor Restoration Williamsville</h3>
<p>Water damage, deep scratch, and structural floor repair. We restore Williamsville floors to like-new condition with insurance documentation available.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-restoration/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
<div class="service-card" data-animate="up" data-delay="3">
<div class="service-card-body">
<h3>Floor Sanding Williamsville</h3>
<p>Dustless sanding for Williamsville homes. Clean, safe process that prepares your floor perfectly for staining and sealing.</p>
</div>
<div class="service-card-footer">
<a href="/services/floor-sanding/" class="service-card-link">Learn More</a>
<a href="/contact/" class="btn btn--ghost btn--sm">Book Now</a>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ -->
<section class="section section--dark">
<div class="container">
<div style="display:grid;grid-template-columns:5fr 7fr;gap:clamp(2rem,5vw,5rem);align-items:start;">
<div data-animate="left">
<span class="eyebrow">Williamsville FAQ</span>
<h2>Common Questions from Williamsville Homeowners</h2>
<div class="divider"></div>
<p style="color:var(--ash);">Have a question specific to your Williamsville home? Call us at <a href="tel:+17166021429" style="color:var(--amber);font-weight:700;">(716) 602-1429</a></p>
</div>
<div class="faq-list" data-animate="up">
<div class="faq-item">
<div class="faq-question">
<h4>Do you work on upscale homes in Williamsville?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Yes. We regularly work on premium homes throughout Williamsville and have extensive experience with high-end wood species, custom stain colors, and finish options that match the quality of the home. We treat every project with the same professional care regardless of home value.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>Can you match the stain on my existing Williamsville floors?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">In most cases, yes. We apply a test sample directly on your floor before proceeding so you can verify the color match. For custom blends, our team mixes stains on-site to get the closest possible match to your existing floors.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>How much disruption should I expect during the project?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">We use dustless sanding equipment that dramatically reduces airborne particles. You will need to vacate the rooms being worked on during sanding and for 24 to 48 hours after final coat application. We work efficiently to minimize disruption to your daily routine.</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ESTIMATE FORM -->
<section class="section contact-section">
<div class="container">
<div class="contact-layout">
<div>
<span class="eyebrow">Williamsville Estimate</span>
<h2>Request a Williamsville Floor Estimate</h2>
<div class="divider"></div>
<p class="lead">Tell us about your floors and we will respond within 1 business hour.</p>
<div class="contact-info-list" style="margin-top:2rem;">
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z"/></svg>
</div>
<div><h5>Phone</h5><a href="tel:+17166021429">(716) 602-1429</a></div>
</div>
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/></svg>
</div>
<div><h5>Hours</h5><p>Monday to Saturday: 8:00 AM to 5:00 PM</p></div>
</div>
</div>
</div>
<div class="contact-form-wrap" data-animate="right">
<h3 style="margin-bottom:1.5rem;font-size:var(--text-xl);">Request a Williamsville Floor Estimate</h3>
<form class="estimate-form" novalidate>
<input type="hidden" name="location" value="Williamsville, NY">
<div class="form-grid form-grid--2">
<div class="form-field">
<label for="name">Full Name <span aria-hidden="true">*</span></label>
<input type="text" id="name" name="name" autocomplete="name" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="phone">Phone Number <span aria-hidden="true">*</span></label>
<input type="tel" id="phone" name="phone" placeholder="(716) 000-0000" autocomplete="tel" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="email">Email Address <span aria-hidden="true">*</span></label>
<input type="email" id="email" name="email" autocomplete="email" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="address">Williamsville Address <span aria-hidden="true">*</span></label>
<input type="text" id="address" name="address" autocomplete="street-address" required>
<span class="err-msg" role="alert"></span>
</div>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="service">Service Needed <span aria-hidden="true">*</span></label>
<select id="service" name="service" required>
<option value="">Select a service</option>
<option value="refinishing">Floor Refinishing</option>
<option value="restoration">Floor Restoration</option>
<option value="sanding">Floor Sanding</option>
<option value="installation">Floor Installation</option>
<option value="unsure">Not Sure</option>
</select>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="message">Additional Details</label>
<textarea id="message" name="message" placeholder="Describe your floors or the issue you are experiencing."></textarea>
</div>
<div class="form-trust">
<div class="form-trust-item">1 business hour response</div>
<div class="form-trust-item">No obligation</div>
<div class="form-trust-item">Licensed &amp; insured</div>
</div>
<button type="submit" class="btn btn--primary btn--full btn--lg" style="margin-top:1.25rem;">Send Williamsville Estimate Request</button>
<div class="form-status" role="status" aria-live="polite"></div>
</form>
</div>
</div>
</div>
</section>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/form.js"></script>
</body>
</html>
-63
View File
@@ -1,63 +0,0 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# Deny dotfiles, configs, scripts, source defense in depth
location ~ /\. {
deny all;
return 404;
}
location ~* \.(env|env\.example|conf|yml|yaml|py|pyc|md|sh|sql|log|bak|old|swp|dockerfile)$ {
deny all;
return 404;
}
location = /Dockerfile {
deny all;
return 404;
}
location ~* /_template\.html$ {
deny all;
return 404;
}
location = /robots.txt { access_log off; }
location = /sitemap.xml { access_log off; }
# API proxy strip /api/ prefix, forward to Python API service
location /api/ {
proxy_pass http://api:3001/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 10s;
proxy_connect_timeout 5s;
}
# Flat HTML serve /locations/buffalo as /locations/buffalo.html
location / {
try_files $uri $uri/ $uri.html =404;
}
# Cache static assets
location ~* \.(jpg|jpeg|png|webp|svg|ico|css|js|woff2?|mp4|webm)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "strict-origin-when-cross-origin";
# Gzip
gzip on;
gzip_types text/html text/css application/javascript image/svg+xml;
gzip_min_length 1024;
error_page 404 /404.html;
}
-145
View File
@@ -1,145 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>Customer Reviews | Floor It Hardwood Floors | Buffalo, NY</title>
<meta name="description" content="Read what Buffalo and Erie County homeowners say about Floor It Hardwood Floors. Rated 4.9/5 stars on Google Reviews. Trusted hardwood floor refinishing in Western NY.">
<link rel="canonical" href="https://floorithardwoodfloors.com/reviews/">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
</head>
<body>
<div id="site-header"></div>
<main>
<section class="page-hero">
<div class="container page-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">/</span>
<span>Reviews</span>
</nav>
<span class="eyebrow">Customer Testimonials</span>
<h1>What Our Customers Say</h1>
<p class="lead">Rated 4.9 out of 5 stars across Google Reviews. Buffalo and Erie County homeowners trust Floor It for professional hardwood floor refinishing.</p>
</div>
</section>
<!-- RATING SUMMARY -->
<div class="trust-bar">
<div class="container">
<div class="trust-bar-inner">
<div class="trust-stat">
<span class="trust-stat-number"><span data-count="4.9" data-suffix="">4.9</span></span>
<span class="trust-stat-label">Out of 5 Stars</span>
</div>
<div class="trust-stat">
<span class="trust-stat-number" style="font-size:var(--text-xl);letter-spacing:0.05em;">&#9733;&#9733;&#9733;&#9733;&#9733;</span>
<span class="trust-stat-label">Google Reviews</span>
</div>
<div class="trust-stat">
<span class="trust-stat-number"><span data-count="500" data-suffix="+">500+</span></span>
<span class="trust-stat-label">Projects Completed</span>
</div>
<div class="trust-stat">
<span class="trust-stat-number">100%</span>
<span class="trust-stat-label">Satisfaction Goal</span>
</div>
</div>
</div>
</div>
<!-- REVIEWS GRID -->
<section class="section section--light">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Verified Reviews</span>
<h2>Buffalo Homeowners Trust Floor It</h2>
</div>
<div class="grid grid--auto-3">
<div class="testimonial-card" data-animate="up">
<div class="testimonial-stars"><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span></div>
<p class="testimonial-text">"Excellent work. They transformed our tired, worn floors into something beautiful. Professional, efficient, and honest with their pricing. Will absolutely use them again for future projects."</p>
<div class="testimonial-author">
<div class="testimonial-avatar">J</div>
<div class="testimonial-info"><strong>Jennifer M.</strong><span>Buffalo, NY</span></div>
</div>
</div>
<div class="testimonial-card" data-animate="up" data-delay="2">
<div class="testimonial-stars"><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span></div>
<p class="testimonial-text">"Highly recommend. The team was professional, clean, and completed everything on schedule. Our floors look incredible, like brand new. The dustless sanding was a huge plus."</p>
<div class="testimonial-author">
<div class="testimonial-avatar">S</div>
<div class="testimonial-info"><strong>Sarah K.</strong><span>Amherst, NY</span></div>
</div>
</div>
<div class="testimonial-card" data-animate="up" data-delay="3">
<div class="testimonial-stars"><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span></div>
<p class="testimonial-text">"Professional service from start to finish. They genuinely cared about the quality of the work and it shows. Absolutely worth the investment for any homeowner with hardwood floors."</p>
<div class="testimonial-author">
<div class="testimonial-avatar">M</div>
<div class="testimonial-info"><strong>Michael D.</strong><span>Hamburg, NY</span></div>
</div>
</div>
<div class="testimonial-card" data-animate="up">
<div class="testimonial-stars"><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span></div>
<p class="testimonial-text">"I had significant water damage on my oak floors and was worried they would need full replacement. Floor It assessed the situation and restored them completely. I could not believe how good they look now."</p>
<div class="testimonial-author">
<div class="testimonial-avatar">R</div>
<div class="testimonial-info"><strong>Robert T.</strong><span>Williamsville, NY</span></div>
</div>
</div>
<div class="testimonial-card" data-animate="up" data-delay="2">
<div class="testimonial-stars"><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span></div>
<p class="testimonial-text">"The custom stain color matching was exactly what I wanted. They did a sample on my floor before committing to the full project. That extra step really set them apart from other companies I contacted."</p>
<div class="testimonial-author">
<div class="testimonial-avatar">L</div>
<div class="testimonial-info"><strong>Linda C.</strong><span>Clarence, NY</span></div>
</div>
</div>
<div class="testimonial-card" data-animate="up" data-delay="3">
<div class="testimonial-stars"><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span></div>
<p class="testimonial-text">"Called on a Monday, had a free estimate visit by Wednesday, and floors were done by the following Friday. Fast, clean, and the results are stunning. Our 100-year-old oak floors look better than they ever have."</p>
<div class="testimonial-author">
<div class="testimonial-avatar">D</div>
<div class="testimonial-info"><strong>David P.</strong><span>Lancaster, NY</span></div>
</div>
</div>
</div>
</div>
</section>
<div class="cta-strip">
<div class="container">
<h2>Ready to Join Our Satisfied Customers?</h2>
<p>Request an estimate today and experience the Floor It difference in your own home.</p>
<div class="cta-group" style="justify-content:center;">
<a href="/contact/" class="btn btn--outline-dark btn--lg">Request an Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-dark btn--lg">Call (716) 602-1429</a>
</div>
</div>
</div>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
</body>
</html>
-276
View File
@@ -1,276 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>{{title}}</title>
<meta name="description" content="{{meta_description}}">
<link rel="canonical" href="{{canonical}}">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Service",
"name": "{{service_name}}",
"provider": {
"@type": "LocalBusiness",
"name": "Floor It Hardwood Floors",
"telephone": "+17166021429",
"email": "floorithardwoods@gmail.com",
"url": "https://floorithardwoodfloors.com",
"areaServed": "Erie County, NY"
},
"url": "{{canonical}}"
}
</script>
</head>
<body>
<div id="site-header"></div>
<main>
<!-- HERO -->
<section class="page-hero">
<div class="container page-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">/</span>
<a href="/services/">Services</a>
<span class="breadcrumb-sep">/</span>
<span>{{service_name}}</span>
</nav>
<span class="eyebrow">{{hero_eyebrow}}</span>
<h1>{{hero_h1}}</h1>
<p class="lead">{{hero_lead}}</p>
<div class="cta-group" style="margin-top:2rem;">
<a href="/contact/" class="btn btn--primary btn--lg">Get a Free Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-light btn--lg">Call (716) 602-1429</a>
</div>
</div>
</section>
<!-- INTRO -->
<section class="section section--light">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,5rem);align-items:center;">
<div data-animate="left">
<span class="eyebrow">What We Do</span>
<h2>{{intro_h2}}</h2>
<div class="divider"></div>
<p style="color:var(--smoke);">{{intro_body_1}}</p>
<p style="margin-top:1rem;color:var(--smoke);">{{intro_body_2}}</p>
<div class="cta-group mt-8">
<a href="/contact/" class="btn btn--primary">Request Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-dark">(716) 602-1429</a>
</div>
</div>
<div data-animate="right">
<div style="border-radius:var(--radius-xl);overflow:hidden;aspect-ratio:4/5;background:var(--grain);">
<img src="{{hero_image}}" alt="{{service_name}} | Floor It Hardwood Floors" style="width:100%;height:100%;object-fit:cover;" loading="lazy">
</div>
</div>
</div>
</div>
</section>
<!-- PROCESS -->
<section class="section section--dark">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">How It Works</span>
<h2>Our {{service_name}} Process</h2>
<p>{{process_intro}}</p>
</div>
<div class="process-steps">
<div class="process-step" data-animate="up">
<div class="process-step-num">1</div>
<h3>{{step_1_title}}</h3>
<p>{{step_1_body}}</p>
</div>
<div class="process-step" data-animate="up" data-delay="2">
<div class="process-step-num">2</div>
<h3>{{step_2_title}}</h3>
<p>{{step_2_body}}</p>
</div>
<div class="process-step" data-animate="up" data-delay="3">
<div class="process-step-num">3</div>
<h3>{{step_3_title}}</h3>
<p>{{step_3_body}}</p>
</div>
</div>
</div>
</section>
<!-- BENEFITS -->
<section class="section section--alt">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Why Choose Us</span>
<h2>What Sets Our {{service_name}} Apart</h2>
</div>
<div style="display:grid;grid-template-columns:repeat(2,1fr);gap:2rem;">
<div class="benefit-card" data-animate="left">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
</div>
<div class="benefit-body">
<h4>{{benefit_1_title}}</h4>
<p>{{benefit_1_body}}</p>
</div>
</div>
<div class="benefit-card" data-animate="right">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 0 0 .95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 0 0-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 0 0-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 0 0-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 0 0 .951-.69l1.519-4.674z"/></svg>
</div>
<div class="benefit-body">
<h4>{{benefit_2_title}}</h4>
<p>{{benefit_2_body}}</p>
</div>
</div>
<div class="benefit-card" data-animate="left" data-delay="2">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
</div>
<div class="benefit-body">
<h4>{{benefit_3_title}}</h4>
<p>{{benefit_3_body}}</p>
</div>
</div>
<div class="benefit-card" data-animate="right" data-delay="2">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
</div>
<div class="benefit-body">
<h4>{{benefit_4_title}}</h4>
<p>{{benefit_4_body}}</p>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ -->
<section class="section section--dark">
<div class="container">
<div style="display:grid;grid-template-columns:5fr 7fr;gap:clamp(2rem,5vw,5rem);align-items:start;">
<div data-animate="left">
<span class="eyebrow">{{service_name}} FAQ</span>
<h2>Common Questions</h2>
<div class="divider"></div>
<p style="color:var(--ash);">Have a question? Call us at <a href="tel:+17166021429" style="color:var(--amber);font-weight:700;">(716) 602-1429</a> and we will answer it directly.</p>
</div>
<div class="faq-list" data-animate="up">
<div class="faq-item">
<div class="faq-question">
<h4>{{faq_1_q}}</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">{{faq_1_a}}</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>{{faq_2_q}}</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">{{faq_2_a}}</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>{{faq_3_q}}</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">{{faq_3_a}}</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ESTIMATE FORM -->
<section class="section contact-section">
<div class="container">
<div class="contact-layout">
<div>
<span class="eyebrow">Free Estimate</span>
<h2>{{form_h2}}</h2>
<div class="divider"></div>
<p class="lead">Tell us about your floors and we will respond within 1 business hour.</p>
<div class="contact-info-list" style="margin-top:2rem;">
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z"/></svg>
</div>
<div><h5>Phone</h5><a href="tel:+17166021429">(716) 602-1429</a></div>
</div>
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/></svg>
</div>
<div><h5>Hours</h5><p>Monday to Saturday: 8:00 AM to 5:00 PM</p></div>
</div>
</div>
</div>
<div class="contact-form-wrap" data-animate="right">
<h3 style="margin-bottom:1.5rem;font-size:var(--text-xl);">{{form_h2}}</h3>
<form class="estimate-form" novalidate>
<input type="hidden" name="service_page" value="{{service_name}}">
<div class="form-grid form-grid--2">
<div class="form-field">
<label for="name">Full Name <span aria-hidden="true">*</span></label>
<input type="text" id="name" name="name" autocomplete="name" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="phone">Phone Number <span aria-hidden="true">*</span></label>
<input type="tel" id="phone" name="phone" placeholder="(716) 000-0000" autocomplete="tel" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="email">Email Address <span aria-hidden="true">*</span></label>
<input type="email" id="email" name="email" autocomplete="email" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="address">Property Address <span aria-hidden="true">*</span></label>
<input type="text" id="address" name="address" autocomplete="street-address" required>
<span class="err-msg" role="alert"></span>
</div>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="message">Additional Details</label>
<textarea id="message" name="message" placeholder="Describe your floors or the issue you are experiencing."></textarea>
</div>
<div class="form-trust">
<div class="form-trust-item">1 business hour response</div>
<div class="form-trust-item">No obligation</div>
<div class="form-trust-item">Licensed &amp; insured</div>
</div>
<button type="submit" class="btn btn--primary btn--full btn--lg" style="margin-top:1.25rem;">{{form_submit}}</button>
<div class="form-status" role="status" aria-live="polite"></div>
</form>
</div>
</div>
</div>
</section>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/form.js"></script>
</body>
</html>
-276
View File
@@ -1,276 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>Hardwood Floor Installation in Buffalo, NY | Floor It Hardwood Floors</title>
<meta name="description" content="New hardwood floor installation in Buffalo and Erie County, NY. Solid and engineered hardwood, proper subfloor prep, professional installation. Free estimate. Call (716) 602-1429.">
<link rel="canonical" href="https://floorithardwoodfloors.com/services/floor-installation.html">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Service",
"name": "Floor Installation",
"provider": {
"@type": "LocalBusiness",
"name": "Floor It Hardwood Floors",
"telephone": "+17166021429",
"email": "floorithardwoods@gmail.com",
"url": "https://floorithardwoodfloors.com",
"areaServed": "Erie County, NY"
},
"url": "https://floorithardwoodfloors.com/services/floor-installation.html"
}
</script>
</head>
<body>
<div id="site-header"></div>
<main>
<!-- HERO -->
<section class="page-hero">
<div class="container page-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">/</span>
<a href="/services/">Services</a>
<span class="breadcrumb-sep">/</span>
<span>Floor Installation</span>
</nav>
<span class="eyebrow">Buffalo and Erie County</span>
<h1>Hardwood Floor Installation</h1>
<p class="lead">New hardwood floors installed with precision. Quality materials, proper subfloor preparation, and expert craftsmanship built to last.</p>
<div class="cta-group" style="margin-top:2rem;">
<a href="/contact/" class="btn btn--primary btn--lg">Get a Free Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-light btn--lg">Call (716) 602-1429</a>
</div>
</div>
</section>
<!-- INTRO -->
<section class="section section--light">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,5rem);align-items:center;">
<div data-animate="left">
<span class="eyebrow">What We Do</span>
<h2>Expert Hardwood Floor Installation in Western New York</h2>
<div class="divider"></div>
<p style="color:var(--smoke);">Installing new hardwood floors is an investment that lasts decades, but only if installed correctly. Improper subfloor preparation, inadequate acclimation, or poor fastening technique shows up years later as squeaks, gaps, and movement. We do it right the first time.</p>
<p style="margin-top:1rem;color:var(--smoke);">We work with solid hardwood, engineered hardwood, and parquet in a wide range of species and widths. Our installation process begins with a subfloor assessment and moisture testing, followed by a proper acclimation period for your new material.</p>
<div class="cta-group mt-8">
<a href="/contact/" class="btn btn--primary">Request Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-dark">(716) 602-1429</a>
</div>
</div>
<div data-animate="right">
<div style="border-radius:var(--radius-xl);overflow:hidden;aspect-ratio:4/5;background:var(--grain);">
<img src="/assets/images/project-1-after.webp" alt="Floor Installation | Floor It Hardwood Floors" style="width:100%;height:100%;object-fit:cover;" loading="lazy">
</div>
</div>
</div>
</div>
</section>
<!-- PROCESS -->
<section class="section section--dark">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">How It Works</span>
<h2>Our Floor Installation Process</h2>
<p>Proper hardwood installation requires careful preparation before a single board goes down.</p>
</div>
<div class="process-steps">
<div class="process-step" data-animate="up">
<div class="process-step-num">1</div>
<h3>Material Selection</h3>
<p>We help you select the right species, width, and grade for your space based on your subfloor type, lifestyle, and existing flooring if matching is needed.</p>
</div>
<div class="process-step" data-animate="up" data-delay="2">
<div class="process-step-num">2</div>
<h3>Subfloor Preparation</h3>
<p>We assess subfloor flatness, moisture content, and structural integrity. Any issues are addressed before installation begins. This is where most problems start.</p>
</div>
<div class="process-step" data-animate="up" data-delay="3">
<div class="process-step-num">3</div>
<h3>Installation and Finish</h3>
<p>Boards are installed with proper fastening pattern and spacing. We complete with the full sand, stain, and seal process for a finished result.</p>
</div>
</div>
</div>
</section>
<!-- BENEFITS -->
<section class="section section--alt">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Why Choose Us</span>
<h2>What Sets Our Floor Installation Apart</h2>
</div>
<div style="display:grid;grid-template-columns:repeat(2,1fr);gap:2rem;">
<div class="benefit-card" data-animate="left">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
</div>
<div class="benefit-body">
<h4>All Wood Species</h4>
<p>Oak, maple, hickory, cherry, walnut, and more. We source quality material from trusted suppliers and can match existing floors in your home.</p>
</div>
</div>
<div class="benefit-card" data-animate="right">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 0 0 .95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 0 0-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 0 0-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 0 0-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 0 0 .951-.69l1.519-4.674z"/></svg>
</div>
<div class="benefit-body">
<h4>Subfloor Expertise</h4>
<p>The subfloor makes or breaks an installation. We assess, level, and prepare every subfloor to manufacturer specifications before any boards go down.</p>
</div>
</div>
<div class="benefit-card" data-animate="left" data-delay="2">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
</div>
<div class="benefit-body">
<h4>Moisture Testing</h4>
<p>Wood and subfloor moisture content must be within range of each other before installation. We test and acclimate all material on-site.</p>
</div>
</div>
<div class="benefit-card" data-animate="right" data-delay="2">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
</div>
<div class="benefit-body">
<h4>Built to Last</h4>
<p>Properly installed hardwood floors last for decades. We stand behind our installation workmanship and ensure every board is set correctly.</p>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ -->
<section class="section section--dark">
<div class="container">
<div style="display:grid;grid-template-columns:5fr 7fr;gap:clamp(2rem,5vw,5rem);align-items:start;">
<div data-animate="left">
<span class="eyebrow">Floor Installation FAQ</span>
<h2>Common Questions</h2>
<div class="divider"></div>
<p style="color:var(--ash);">Have a question? Call us at <a href="tel:+17166021429" style="color:var(--amber);font-weight:700;">(716) 602-1429</a> and we will answer it directly.</p>
</div>
<div class="faq-list" data-animate="up">
<div class="faq-item">
<div class="faq-question">
<h4>How long does hardwood floor installation take?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Most rooms take two to three days including subfloor prep, installation, then sand and finish. Larger projects or those requiring extensive subfloor work take longer. We provide a detailed schedule during the estimate.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>Solid versus engineered hardwood, which is right for me?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Solid hardwood is ideal for above-grade installations on wood subfloors. Engineered hardwood is better for concrete subfloors, basements, or high-humidity areas. We help you make the right choice for your situation.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>Can you match my existing hardwood floors?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">In most cases, yes. We sample your existing floor and source matching material by species, width, and grade. After installation and finishing, new and existing sections blend well.</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ESTIMATE FORM -->
<section class="section contact-section">
<div class="container">
<div class="contact-layout">
<div>
<span class="eyebrow">Free Estimate</span>
<h2>Get Your Free Installation Estimate</h2>
<div class="divider"></div>
<p class="lead">Tell us about your floors and we will respond within 1 business hour.</p>
<div class="contact-info-list" style="margin-top:2rem;">
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z"/></svg>
</div>
<div><h5>Phone</h5><a href="tel:+17166021429">(716) 602-1429</a></div>
</div>
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/></svg>
</div>
<div><h5>Hours</h5><p>Monday to Saturday: 8:00 AM to 5:00 PM</p></div>
</div>
</div>
</div>
<div class="contact-form-wrap" data-animate="right">
<h3 style="margin-bottom:1.5rem;font-size:var(--text-xl);">Get Your Free Installation Estimate</h3>
<form class="estimate-form" novalidate>
<input type="hidden" name="service_page" value="Floor Installation">
<div class="form-grid form-grid--2">
<div class="form-field">
<label for="name">Full Name <span aria-hidden="true">*</span></label>
<input type="text" id="name" name="name" autocomplete="name" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="phone">Phone Number <span aria-hidden="true">*</span></label>
<input type="tel" id="phone" name="phone" placeholder="(716) 000-0000" autocomplete="tel" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="email">Email Address <span aria-hidden="true">*</span></label>
<input type="email" id="email" name="email" autocomplete="email" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="address">Property Address <span aria-hidden="true">*</span></label>
<input type="text" id="address" name="address" autocomplete="street-address" required>
<span class="err-msg" role="alert"></span>
</div>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="message">Additional Details</label>
<textarea id="message" name="message" placeholder="Describe your floors or the issue you are experiencing."></textarea>
</div>
<div class="form-trust">
<div class="form-trust-item">1 business hour response</div>
<div class="form-trust-item">No obligation</div>
<div class="form-trust-item">Licensed &amp; insured</div>
</div>
<button type="submit" class="btn btn--primary btn--full btn--lg" style="margin-top:1.25rem;">Request Installation Estimate</button>
<div class="form-status" role="status" aria-live="polite"></div>
</form>
</div>
</div>
</div>
</section>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/form.js"></script>
</body>
</html>
-276
View File
@@ -1,276 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>Hardwood Floor Refinishing in Buffalo, NY | Floor It Hardwood Floors</title>
<meta name="description" content="Professional hardwood floor refinishing in Buffalo and Erie County, NY. Multi-stage sanding, custom stain colors, dustless process. Free estimate. Call (716) 602-1429.">
<link rel="canonical" href="https://floorithardwoodfloors.com/services/floor-refinishing.html">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Service",
"name": "Floor Refinishing",
"provider": {
"@type": "LocalBusiness",
"name": "Floor It Hardwood Floors",
"telephone": "+17166021429",
"email": "floorithardwoods@gmail.com",
"url": "https://floorithardwoodfloors.com",
"areaServed": "Erie County, NY"
},
"url": "https://floorithardwoodfloors.com/services/floor-refinishing.html"
}
</script>
</head>
<body>
<div id="site-header"></div>
<main>
<!-- HERO -->
<section class="page-hero">
<div class="container page-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">/</span>
<a href="/services/">Services</a>
<span class="breadcrumb-sep">/</span>
<span>Floor Refinishing</span>
</nav>
<span class="eyebrow">Buffalo and Erie County</span>
<h1>Hardwood Floor Refinishing</h1>
<p class="lead">Restore worn, dull, or damaged hardwood floors to their original beauty with our multi-stage refinishing process. Custom stain colors available.</p>
<div class="cta-group" style="margin-top:2rem;">
<a href="/contact/" class="btn btn--primary btn--lg">Get a Free Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-light btn--lg">Call (716) 602-1429</a>
</div>
</div>
</section>
<!-- INTRO -->
<section class="section section--light">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,5rem);align-items:center;">
<div data-animate="left">
<span class="eyebrow">What We Do</span>
<h2>Expert Floor Refinishing in Western New York</h2>
<div class="divider"></div>
<p style="color:var(--smoke);">Floor refinishing is the most effective way to transform tired, worn hardwood without the cost of full replacement. Our process begins with a thorough assessment of your floors, including wood species, existing finish condition, and depth of scratches, to determine the exact approach your floors need.</p>
<p style="margin-top:1rem;color:var(--smoke);">We use dustless sanding equipment that minimizes dust throughout the project, protecting your home and family. After sanding, we apply your chosen stain and seal with a professional-grade topcoat built for long-lasting durability.</p>
<div class="cta-group mt-8">
<a href="/contact/" class="btn btn--primary">Request Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-dark">(716) 602-1429</a>
</div>
</div>
<div data-animate="right">
<div style="border-radius:var(--radius-xl);overflow:hidden;aspect-ratio:4/5;background:var(--grain);">
<img src="/assets/images/project-1-after.webp" alt="Floor Refinishing | Floor It Hardwood Floors" style="width:100%;height:100%;object-fit:cover;" loading="lazy">
</div>
</div>
</div>
</div>
</section>
<!-- PROCESS -->
<section class="section section--dark">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">How It Works</span>
<h2>Our Floor Refinishing Process</h2>
<p>Our refinishing process follows a proven multi-stage system that delivers consistent, beautiful results on every project.</p>
</div>
<div class="process-steps">
<div class="process-step" data-animate="up">
<div class="process-step-num">1</div>
<h3>Floor Assessment</h3>
<p>We inspect wood species, thickness, existing finish, and damage level to determine the right sanding grit sequence and stain approach.</p>
</div>
<div class="process-step" data-animate="up" data-delay="2">
<div class="process-step-num">2</div>
<h3>Dustless Sanding</h3>
<p>Our sanding equipment removes the existing finish and opens the wood grain. Multiple grit passes deliver a perfectly smooth, even surface.</p>
</div>
<div class="process-step" data-animate="up" data-delay="3">
<div class="process-step-num">3</div>
<h3>Stain and Seal</h3>
<p>We apply a test patch for your approval before proceeding with the full stain. Finish with your selected topcoat: oil-based, water-based, or wax.</p>
</div>
</div>
</div>
</section>
<!-- BENEFITS -->
<section class="section section--alt">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Why Choose Us</span>
<h2>What Sets Our Floor Refinishing Apart</h2>
</div>
<div style="display:grid;grid-template-columns:repeat(2,1fr);gap:2rem;">
<div class="benefit-card" data-animate="left">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
</div>
<div class="benefit-body">
<h4>Custom Stain Colors</h4>
<p>From natural clear finishes to deep espresso tones. We apply samples directly to your floor during the estimate visit so you can approve before we begin.</p>
</div>
</div>
<div class="benefit-card" data-animate="right">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 0 0 .95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 0 0-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 0 0-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 0 0-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 0 0 .951-.69l1.519-4.674z"/></svg>
</div>
<div class="benefit-body">
<h4>Dustless Sanding</h4>
<p>Our sanding equipment captures dust at the source, keeping your home clean and your family comfortable throughout the project.</p>
</div>
</div>
<div class="benefit-card" data-animate="left" data-delay="2">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
</div>
<div class="benefit-body">
<h4>Long-Lasting Finish</h4>
<p>Professional-grade polyurethane, oil-modified, and water-based topcoats that stand up to daily wear, pets, and Buffalo winters.</p>
</div>
</div>
<div class="benefit-card" data-animate="right" data-delay="2">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
</div>
<div class="benefit-body">
<h4>Free On-Site Estimate</h4>
<p>We visit your home, assess the floors, and provide a detailed written estimate before any work begins. No guessing, no surprises.</p>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ -->
<section class="section section--dark">
<div class="container">
<div style="display:grid;grid-template-columns:5fr 7fr;gap:clamp(2rem,5vw,5rem);align-items:start;">
<div data-animate="left">
<span class="eyebrow">Floor Refinishing FAQ</span>
<h2>Common Questions</h2>
<div class="divider"></div>
<p style="color:var(--ash);">Have a question? Call us at <a href="tel:+17166021429" style="color:var(--amber);font-weight:700;">(716) 602-1429</a> and we will answer it directly.</p>
</div>
<div class="faq-list" data-animate="up">
<div class="faq-item">
<div class="faq-question">
<h4>How long does floor refinishing take?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Most rooms take two to three days: one day for sanding, one for stain and first coat, one for final coats. We provide a precise timeline during the estimate.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>How many times can a hardwood floor be refinished?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Solid hardwood can typically be refinished multiple times over its lifetime depending on the wood thickness. Engineered hardwood depends on the wear layer. We measure your floor during the assessment.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>Do I need to leave my home during refinishing?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">With our dustless sanding system you can usually remain in your home. During staining and sealing we recommend staying out for several hours per coat for ventilation. We advise based on your specific layout.</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ESTIMATE FORM -->
<section class="section contact-section">
<div class="container">
<div class="contact-layout">
<div>
<span class="eyebrow">Free Estimate</span>
<h2>Get Your Free Refinishing Estimate</h2>
<div class="divider"></div>
<p class="lead">Tell us about your floors and we will respond within 1 business hour.</p>
<div class="contact-info-list" style="margin-top:2rem;">
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z"/></svg>
</div>
<div><h5>Phone</h5><a href="tel:+17166021429">(716) 602-1429</a></div>
</div>
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/></svg>
</div>
<div><h5>Hours</h5><p>Monday to Saturday: 8:00 AM to 5:00 PM</p></div>
</div>
</div>
</div>
<div class="contact-form-wrap" data-animate="right">
<h3 style="margin-bottom:1.5rem;font-size:var(--text-xl);">Get Your Free Refinishing Estimate</h3>
<form class="estimate-form" novalidate>
<input type="hidden" name="service_page" value="Floor Refinishing">
<div class="form-grid form-grid--2">
<div class="form-field">
<label for="name">Full Name <span aria-hidden="true">*</span></label>
<input type="text" id="name" name="name" autocomplete="name" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="phone">Phone Number <span aria-hidden="true">*</span></label>
<input type="tel" id="phone" name="phone" placeholder="(716) 000-0000" autocomplete="tel" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="email">Email Address <span aria-hidden="true">*</span></label>
<input type="email" id="email" name="email" autocomplete="email" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="address">Property Address <span aria-hidden="true">*</span></label>
<input type="text" id="address" name="address" autocomplete="street-address" required>
<span class="err-msg" role="alert"></span>
</div>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="message">Additional Details</label>
<textarea id="message" name="message" placeholder="Describe your floors or the issue you are experiencing."></textarea>
</div>
<div class="form-trust">
<div class="form-trust-item">1 business hour response</div>
<div class="form-trust-item">No obligation</div>
<div class="form-trust-item">Licensed &amp; insured</div>
</div>
<button type="submit" class="btn btn--primary btn--full btn--lg" style="margin-top:1.25rem;">Request Refinishing Estimate</button>
<div class="form-status" role="status" aria-live="polite"></div>
</form>
</div>
</div>
</div>
</section>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/form.js"></script>
</body>
</html>
-276
View File
@@ -1,276 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>Hardwood Floor Restoration in Buffalo, NY | Floor It Hardwood Floors</title>
<meta name="description" content="Hardwood floor restoration for water damage, deep scratches, warping, and structural damage in Buffalo, NY. Insurance documentation provided. Call (716) 602-1429.">
<link rel="canonical" href="https://floorithardwoodfloors.com/services/floor-restoration.html">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Service",
"name": "Floor Restoration",
"provider": {
"@type": "LocalBusiness",
"name": "Floor It Hardwood Floors",
"telephone": "+17166021429",
"email": "floorithardwoods@gmail.com",
"url": "https://floorithardwoodfloors.com",
"areaServed": "Erie County, NY"
},
"url": "https://floorithardwoodfloors.com/services/floor-restoration.html"
}
</script>
</head>
<body>
<div id="site-header"></div>
<main>
<!-- HERO -->
<section class="page-hero">
<div class="container page-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">/</span>
<a href="/services/">Services</a>
<span class="breadcrumb-sep">/</span>
<span>Floor Restoration</span>
</nav>
<span class="eyebrow">Buffalo and Erie County</span>
<h1>Hardwood Floor Restoration</h1>
<p class="lead">Water damage, deep scratches, warping, cupping, and structural damage. We diagnose, repair, and restore your hardwood floors completely.</p>
<div class="cta-group" style="margin-top:2rem;">
<a href="/contact/" class="btn btn--primary btn--lg">Get a Free Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-light btn--lg">Call (716) 602-1429</a>
</div>
</div>
</section>
<!-- INTRO -->
<section class="section section--light">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,5rem);align-items:center;">
<div data-animate="left">
<span class="eyebrow">What We Do</span>
<h2>Full-Service Floor Restoration in Western New York</h2>
<div class="divider"></div>
<p style="color:var(--smoke);">Restoration goes beyond refinishing. When floors have suffered water damage, deep gouging, board warping, nail pops, or subfloor issues, a simple sand and seal is not enough. Our restoration process starts with a structural diagnosis to identify every issue before any work begins.</p>
<p style="margin-top:1rem;color:var(--smoke);">We repair or replace damaged boards, address subfloor problems, re-nail loose planks, and eliminate gaps, then complete the process with our full refinishing system. We also provide written documentation for insurance claims with before-and-after photography and a detailed scope-of-work report.</p>
<div class="cta-group mt-8">
<a href="/contact/" class="btn btn--primary">Request Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-dark">(716) 602-1429</a>
</div>
</div>
<div data-animate="right">
<div style="border-radius:var(--radius-xl);overflow:hidden;aspect-ratio:4/5;background:var(--grain);">
<img src="/assets/images/project-2-before.webp" alt="Floor Restoration | Floor It Hardwood Floors" style="width:100%;height:100%;object-fit:cover;" loading="lazy">
</div>
</div>
</div>
</div>
</section>
<!-- PROCESS -->
<section class="section section--dark">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">How It Works</span>
<h2>Our Floor Restoration Process</h2>
<p>Restoration requires a thorough assessment and multi-phase repair process before refinishing can begin.</p>
</div>
<div class="process-steps">
<div class="process-step" data-animate="up">
<div class="process-step-num">1</div>
<h3>Damage Assessment</h3>
<p>We inspect for water damage, structural movement, subfloor issues, and board condition. All findings are documented with photography for insurance purposes.</p>
</div>
<div class="process-step" data-animate="up" data-delay="2">
<div class="process-step-num">2</div>
<h3>Structural Repair</h3>
<p>Replace damaged boards, re-nail loose planks, fill gaps, address subfloor problems, and eliminate squeaks before any finishing work begins.</p>
</div>
<div class="process-step" data-animate="up" data-delay="3">
<div class="process-step-num">3</div>
<h3>Refinish and Seal</h3>
<p>Once the floor is structurally sound, we complete the full refinishing process including sanding, stain, and topcoat for a seamless final result.</p>
</div>
</div>
</div>
</section>
<!-- BENEFITS -->
<section class="section section--alt">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Why Choose Us</span>
<h2>What Sets Our Floor Restoration Apart</h2>
</div>
<div style="display:grid;grid-template-columns:repeat(2,1fr);gap:2rem;">
<div class="benefit-card" data-animate="left">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
</div>
<div class="benefit-body">
<h4>Insurance Documentation</h4>
<p>Written scope-of-work reports and before and after photography for your insurance adjuster. We work with your timeline to support the claims process.</p>
</div>
</div>
<div class="benefit-card" data-animate="right">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 0 0 .95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 0 0-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 0 0-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 0 0-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 0 0 .951-.69l1.519-4.674z"/></svg>
</div>
<div class="benefit-body">
<h4>Board Replacement</h4>
<p>We source matching replacement boards for damaged sections by species, grain, and width so repairs are invisible after finishing.</p>
</div>
</div>
<div class="benefit-card" data-animate="left" data-delay="2">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
</div>
<div class="benefit-body">
<h4>Subfloor Repair</h4>
<p>Water damage often affects the subfloor. We assess and repair subfloor issues to ensure your finished floor is structurally sound.</p>
</div>
</div>
<div class="benefit-card" data-animate="right" data-delay="2">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
</div>
<div class="benefit-body">
<h4>Cupping and Warping</h4>
<p>Cupped or warped boards from humidity changes can often be corrected without replacement. We assess each board individually to minimize material cost.</p>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ -->
<section class="section section--dark">
<div class="container">
<div style="display:grid;grid-template-columns:5fr 7fr;gap:clamp(2rem,5vw,5rem);align-items:start;">
<div data-animate="left">
<span class="eyebrow">Floor Restoration FAQ</span>
<h2>Common Questions</h2>
<div class="divider"></div>
<p style="color:var(--ash);">Have a question? Call us at <a href="tel:+17166021429" style="color:var(--amber);font-weight:700;">(716) 602-1429</a> and we will answer it directly.</p>
</div>
<div class="faq-list" data-animate="up">
<div class="faq-item">
<div class="faq-question">
<h4>Can water-damaged floors be saved?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">In most cases, yes, if the damage is addressed promptly. Floors that have dried out and cupped can often be sanded flat. Severely swollen or buckled boards may need replacement, but we always maximize what can be saved.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>Do you provide insurance estimates?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Yes. We provide written damage assessments, scope-of-work documentation, and before-and-after photography suitable for insurance claims. We have experience working alongside adjusters.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>How long does restoration take?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Restoration timelines vary by damage extent. Minor repairs and refinishing can be completed in three to four days. Extensive structural work with board replacement may take longer. We provide a detailed timeline during the assessment.</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ESTIMATE FORM -->
<section class="section contact-section">
<div class="container">
<div class="contact-layout">
<div>
<span class="eyebrow">Free Estimate</span>
<h2>Get Your Free Restoration Assessment</h2>
<div class="divider"></div>
<p class="lead">Tell us about your floors and we will respond within 1 business hour.</p>
<div class="contact-info-list" style="margin-top:2rem;">
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z"/></svg>
</div>
<div><h5>Phone</h5><a href="tel:+17166021429">(716) 602-1429</a></div>
</div>
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/></svg>
</div>
<div><h5>Hours</h5><p>Monday to Saturday: 8:00 AM to 5:00 PM</p></div>
</div>
</div>
</div>
<div class="contact-form-wrap" data-animate="right">
<h3 style="margin-bottom:1.5rem;font-size:var(--text-xl);">Get Your Free Restoration Assessment</h3>
<form class="estimate-form" novalidate>
<input type="hidden" name="service_page" value="Floor Restoration">
<div class="form-grid form-grid--2">
<div class="form-field">
<label for="name">Full Name <span aria-hidden="true">*</span></label>
<input type="text" id="name" name="name" autocomplete="name" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="phone">Phone Number <span aria-hidden="true">*</span></label>
<input type="tel" id="phone" name="phone" placeholder="(716) 000-0000" autocomplete="tel" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="email">Email Address <span aria-hidden="true">*</span></label>
<input type="email" id="email" name="email" autocomplete="email" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="address">Property Address <span aria-hidden="true">*</span></label>
<input type="text" id="address" name="address" autocomplete="street-address" required>
<span class="err-msg" role="alert"></span>
</div>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="message">Additional Details</label>
<textarea id="message" name="message" placeholder="Describe your floors or the issue you are experiencing."></textarea>
</div>
<div class="form-trust">
<div class="form-trust-item">1 business hour response</div>
<div class="form-trust-item">No obligation</div>
<div class="form-trust-item">Licensed &amp; insured</div>
</div>
<button type="submit" class="btn btn--primary btn--full btn--lg" style="margin-top:1.25rem;">Request Restoration Estimate</button>
<div class="form-status" role="status" aria-live="polite"></div>
</form>
</div>
</div>
</div>
</section>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/form.js"></script>
</body>
</html>
-276
View File
@@ -1,276 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>Hardwood Floor Sanding in Buffalo, NY | Floor It Hardwood Floors</title>
<meta name="description" content="Professional hardwood floor sanding in Buffalo and Erie County, NY. Dustless equipment, multi-grit process for a perfect surface. Free estimate. Call (716) 602-1429.">
<link rel="canonical" href="https://floorithardwoodfloors.com/services/floor-sanding.html">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Service",
"name": "Floor Sanding",
"provider": {
"@type": "LocalBusiness",
"name": "Floor It Hardwood Floors",
"telephone": "+17166021429",
"email": "floorithardwoods@gmail.com",
"url": "https://floorithardwoodfloors.com",
"areaServed": "Erie County, NY"
},
"url": "https://floorithardwoodfloors.com/services/floor-sanding.html"
}
</script>
</head>
<body>
<div id="site-header"></div>
<main>
<!-- HERO -->
<section class="page-hero">
<div class="container page-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">/</span>
<a href="/services/">Services</a>
<span class="breadcrumb-sep">/</span>
<span>Floor Sanding</span>
</nav>
<span class="eyebrow">Buffalo and Erie County</span>
<h1>Professional Hardwood Floor Sanding</h1>
<p class="lead">Commercial-grade dustless sanding equipment and a precise multi-grit process deliver the perfect surface every time.</p>
<div class="cta-group" style="margin-top:2rem;">
<a href="/contact/" class="btn btn--primary btn--lg">Get a Free Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-light btn--lg">Call (716) 602-1429</a>
</div>
</div>
</section>
<!-- INTRO -->
<section class="section section--light">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,5rem);align-items:center;">
<div data-animate="left">
<span class="eyebrow">What We Do</span>
<h2>Professional Floor Sanding in Western New York</h2>
<div class="divider"></div>
<p style="color:var(--smoke);">Sanding is the foundation of any successful floor refinishing project. Our sanding technicians use professional belt sanders paired with dust containment systems for clean, precise results across every project.</p>
<p style="margin-top:1rem;color:var(--smoke);">Our multi-grit process starts aggressive to remove the existing finish and flatten the surface, then moves through progressively finer grits to deliver a surface that is perfectly smooth and ready for stain. We edge-sand and handle corners with care so there are no swirl marks or chatter marks.</p>
<div class="cta-group mt-8">
<a href="/contact/" class="btn btn--primary">Request Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-dark">(716) 602-1429</a>
</div>
</div>
<div data-animate="right">
<div style="border-radius:var(--radius-xl);overflow:hidden;aspect-ratio:4/5;background:var(--grain);">
<img src="/assets/images/refinishing-machine.webp" alt="Floor Sanding | Floor It Hardwood Floors" style="width:100%;height:100%;object-fit:cover;" loading="lazy">
</div>
</div>
</div>
</div>
</section>
<!-- PROCESS -->
<section class="section section--dark">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">How It Works</span>
<h2>Our Floor Sanding Process</h2>
<p>Proper sanding requires the right sequence of grits and careful technique for each section of your floor.</p>
</div>
<div class="process-steps">
<div class="process-step" data-animate="up">
<div class="process-step-num">1</div>
<h3>Initial Assessment</h3>
<p>We check moisture content, identify problem areas like cupping or high-grain boards, and select the correct starting grit for your specific floor condition.</p>
</div>
<div class="process-step" data-animate="up" data-delay="2">
<div class="process-step-num">2</div>
<h3>Multi-Grit Sanding</h3>
<p>We progress through multiple grit sequences for a consistently smooth surface across the entire floor, from the field to the edges.</p>
</div>
<div class="process-step" data-animate="up" data-delay="3">
<div class="process-step-num">3</div>
<h3>Edge and Detail Work</h3>
<p>Edges are sanded with an edger, corners done by hand. Every square inch is sanded to the same standard as the center of the room.</p>
</div>
</div>
</div>
</section>
<!-- BENEFITS -->
<section class="section section--alt">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Why Choose Us</span>
<h2>What Sets Our Floor Sanding Apart</h2>
</div>
<div style="display:grid;grid-template-columns:repeat(2,1fr);gap:2rem;">
<div class="benefit-card" data-animate="left">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
</div>
<div class="benefit-body">
<h4>Dustless Equipment</h4>
<p>Our sanding equipment captures dust at the source, keeping your home cleaner and your family more comfortable throughout the process.</p>
</div>
</div>
<div class="benefit-card" data-animate="right">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 0 0 .95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 0 0-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 0 0-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 0 0-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 0 0 .951-.69l1.519-4.674z"/></svg>
</div>
<div class="benefit-body">
<h4>No Swirl Marks</h4>
<p>We use the correct grit sequence and orbital finishing to eliminate chatter and swirl marks that are common with inexperienced operators.</p>
</div>
</div>
<div class="benefit-card" data-animate="left" data-delay="2">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
</div>
<div class="benefit-body">
<h4>Moisture Control</h4>
<p>We check wood moisture content before sanding and monitor throughout. Sanding wood at the wrong moisture level causes problems that show up months later.</p>
</div>
</div>
<div class="benefit-card" data-animate="right" data-delay="2">
<div class="benefit-icon">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>
</div>
<div class="benefit-body">
<h4>Clean Results</h4>
<p>Our process and equipment leave your floor ready for stain the same day, keeping your project on schedule with minimal disruption.</p>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ -->
<section class="section section--dark">
<div class="container">
<div style="display:grid;grid-template-columns:5fr 7fr;gap:clamp(2rem,5vw,5rem);align-items:start;">
<div data-animate="left">
<span class="eyebrow">Floor Sanding FAQ</span>
<h2>Common Questions</h2>
<div class="divider"></div>
<p style="color:var(--ash);">Have a question? Call us at <a href="tel:+17166021429" style="color:var(--amber);font-weight:700;">(716) 602-1429</a> and we will answer it directly.</p>
</div>
<div class="faq-list" data-animate="up">
<div class="faq-item">
<div class="faq-question">
<h4>How many times can a floor be sanded?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Solid hardwood can typically be sanded multiple times over its lifetime. Engineered hardwood depends on the wear layer thickness. We measure your floor before committing to sanding.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>Will sanding remove deep scratches?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">Yes. Sanding removes the surface layer of wood, taking most scratches with it. Very deep gouges may require wood filler before sanding. We assess each area during the estimate.</div>
</div>
</div>
<div class="faq-item">
<div class="faq-question">
<h4>How dusty is the sanding process?</h4>
<div class="faq-icon" aria-hidden="true"></div>
</div>
<div class="faq-answer">
<div class="faq-answer-inner">With our dust containment system, very little dust escapes into your home. We seal off doorways and use containment equipment so the rest of your home stays clean.</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ESTIMATE FORM -->
<section class="section contact-section">
<div class="container">
<div class="contact-layout">
<div>
<span class="eyebrow">Free Estimate</span>
<h2>Get Your Free Sanding Estimate</h2>
<div class="divider"></div>
<p class="lead">Tell us about your floors and we will respond within 1 business hour.</p>
<div class="contact-info-list" style="margin-top:2rem;">
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z"/></svg>
</div>
<div><h5>Phone</h5><a href="tel:+17166021429">(716) 602-1429</a></div>
</div>
<div class="contact-info-item">
<div class="contact-info-icon">
<svg viewBox="0 0 20 20" aria-hidden="true"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/></svg>
</div>
<div><h5>Hours</h5><p>Monday to Saturday: 8:00 AM to 5:00 PM</p></div>
</div>
</div>
</div>
<div class="contact-form-wrap" data-animate="right">
<h3 style="margin-bottom:1.5rem;font-size:var(--text-xl);">Get Your Free Sanding Estimate</h3>
<form class="estimate-form" novalidate>
<input type="hidden" name="service_page" value="Floor Sanding">
<div class="form-grid form-grid--2">
<div class="form-field">
<label for="name">Full Name <span aria-hidden="true">*</span></label>
<input type="text" id="name" name="name" autocomplete="name" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="phone">Phone Number <span aria-hidden="true">*</span></label>
<input type="tel" id="phone" name="phone" placeholder="(716) 000-0000" autocomplete="tel" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="email">Email Address <span aria-hidden="true">*</span></label>
<input type="email" id="email" name="email" autocomplete="email" required>
<span class="err-msg" role="alert"></span>
</div>
<div class="form-field">
<label for="address">Property Address <span aria-hidden="true">*</span></label>
<input type="text" id="address" name="address" autocomplete="street-address" required>
<span class="err-msg" role="alert"></span>
</div>
</div>
<div class="form-field" style="margin-top:1.25rem;">
<label for="message">Additional Details</label>
<textarea id="message" name="message" placeholder="Describe your floors or the issue you are experiencing."></textarea>
</div>
<div class="form-trust">
<div class="form-trust-item">1 business hour response</div>
<div class="form-trust-item">No obligation</div>
<div class="form-trust-item">Licensed &amp; insured</div>
</div>
<button type="submit" class="btn btn--primary btn--full btn--lg" style="margin-top:1.25rem;">Request Sanding Estimate</button>
<div class="form-status" role="status" aria-live="polite"></div>
</form>
</div>
</div>
</div>
</section>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
<script src="/assets/js/form.js"></script>
</body>
</html>
-167
View File
@@ -1,167 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="site-root" content="/">
<title>Hardwood Floor Services | Refinishing, Restoration, Sanding | Floor It</title>
<meta name="description" content="Professional hardwood floor refinishing, restoration, sanding, and installation in Buffalo, NY and Erie County. Over 75 years of combined experience.">
<link rel="canonical" href="https://floorithardwoodfloors.com/services/">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
</head>
<body>
<div id="site-header"></div>
<main>
<section class="page-hero">
<div class="container page-hero-inner">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">/</span>
<span>Services</span>
</nav>
<span class="eyebrow">What We Do</span>
<h1>Hardwood Floor Services in Buffalo, NY</h1>
<p class="lead">Complete hardwood floor solutions: refinishing, restoration, sanding, and installation, delivered by Western New York's most experienced team.</p>
</div>
</section>
<!-- FLOOR REFINISHING -->
<section class="section section--light" id="floor-refinishing">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,5rem);align-items:center;">
<div data-animate="left">
<span class="eyebrow">Service 01</span>
<h2>Floor Refinishing</h2>
<div class="divider"></div>
<p class="lead">Restore your floors to their original beauty with professional sanding, staining, and sealing.</p>
<p style="margin-top:1rem;color:var(--smoke);">Our multi-stage refinishing process sands away years of wear and damage, then applies your choice of stain from over 100 color options, and seals with a durable topcoat that lasts 10 or more years.</p>
<ul style="margin-top:1.25rem;display:flex;flex-direction:column;gap:0.5rem;">
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--smoke);"><span style="color:var(--amber);font-weight:700;flex-shrink:0;">&#10003;</span> Multiple stain color options</li>
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--smoke);"><span style="color:var(--amber);font-weight:700;flex-shrink:0;">&#10003;</span> Dustless sanding system</li>
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--smoke);"><span style="color:var(--amber);font-weight:700;flex-shrink:0;">&#10003;</span> Long-lasting professional finish</li>
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--smoke);"><span style="color:var(--amber);font-weight:700;flex-shrink:0;">&#10003;</span> Sample approval before full project</li>
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--smoke);"><span style="color:var(--amber);font-weight:700;flex-shrink:0;">&#10003;</span> Timeline: 7 to 10 business days</li>
</ul>
<div class="cta-group mt-8">
<a href="/contact/" class="btn btn--primary">Get an Estimate</a>
<a href="/services/floor-refinishing/" class="btn btn--outline-dark">Learn More</a>
</div>
</div>
<div data-animate="right" style="background:var(--grain);border-radius:var(--radius-xl);aspect-ratio:4/3;overflow:hidden;display:flex;align-items:center;justify-content:center;">
<img src="/assets/images/service-refinishing.webp" alt="Hardwood floor refinishing service" style="width:100%;height:100%;object-fit:cover;" loading="lazy" onerror="this.style.display='none'">
</div>
</div>
</div>
</section>
<!-- FLOOR RESTORATION -->
<section class="section section--alt" id="floor-restoration">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,5rem);align-items:center;">
<div data-animate="left" style="background:var(--grain);border-radius:var(--radius-xl);aspect-ratio:4/3;overflow:hidden;display:flex;align-items:center;justify-content:center;order:1;">
<img src="/assets/images/service-restoration.webp" alt="Hardwood floor restoration service" style="width:100%;height:100%;object-fit:cover;" loading="lazy" onerror="this.style.display='none'">
</div>
<div data-animate="right" style="order:2;">
<span class="eyebrow">Service 02</span>
<h2>Floor Restoration</h2>
<div class="divider"></div>
<p class="lead">Water damage, deep scratches, warping, and buckling repaired with expert precision.</p>
<p style="margin-top:1rem;color:var(--smoke);">When damage goes beyond surface wear, restoration is the answer. We repair structural damage, replace boards when needed, and restore your floor to like-new condition. We also provide documentation to support insurance claims.</p>
<ul style="margin-top:1.25rem;display:flex;flex-direction:column;gap:0.5rem;">
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--smoke);"><span style="color:var(--amber);font-weight:700;flex-shrink:0;">&#10003;</span> Water and moisture damage repair</li>
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--smoke);"><span style="color:var(--amber);font-weight:700;flex-shrink:0;">&#10003;</span> Warping and buckling correction</li>
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--smoke);"><span style="color:var(--amber);font-weight:700;flex-shrink:0;">&#10003;</span> Deep scratch and gouge restoration</li>
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--smoke);"><span style="color:var(--amber);font-weight:700;flex-shrink:0;">&#10003;</span> Insurance documentation support</li>
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--smoke);"><span style="color:var(--amber);font-weight:700;flex-shrink:0;">&#10003;</span> Timeline: 10 to 14 business days</li>
</ul>
<div class="cta-group mt-8">
<a href="/contact/" class="btn btn--primary">Get an Estimate</a>
<a href="/services/floor-restoration/" class="btn btn--outline-dark">Learn More</a>
</div>
</div>
</div>
</div>
</section>
<!-- FLOOR SANDING -->
<section class="section section--light" id="floor-sanding">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,5rem);align-items:center;">
<div data-animate="left">
<span class="eyebrow">Service 03</span>
<h2>Floor Sanding</h2>
<div class="divider"></div>
<p class="lead">Commercial-grade equipment and dust containment for a perfectly prepared surface.</p>
<p style="margin-top:1rem;color:var(--smoke);">Professional sanding is the foundation of any successful refinishing project. Our multi-grit sanding process removes old finishes, levels imperfections, and creates the ideal surface for staining and sealing, while containing dust throughout the process.</p>
<ul style="margin-top:1.25rem;display:flex;flex-direction:column;gap:0.5rem;">
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--smoke);"><span style="color:var(--amber);font-weight:700;flex-shrink:0;">&#10003;</span> Commercial-grade equipment (not rentals)</li>
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--smoke);"><span style="color:var(--amber);font-weight:700;flex-shrink:0;">&#10003;</span> Dustless sanding system</li>
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--smoke);"><span style="color:var(--amber);font-weight:700;flex-shrink:0;">&#10003;</span> Multi-grit sanding process</li>
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--smoke);"><span style="color:var(--amber);font-weight:700;flex-shrink:0;">&#10003;</span> Safe for family and pets</li>
</ul>
<div class="cta-group mt-8">
<a href="/contact/" class="btn btn--primary">Get an Estimate</a>
<a href="/services/floor-sanding/" class="btn btn--outline-dark">Learn More</a>
</div>
</div>
<div data-animate="right" style="background:var(--grain);border-radius:var(--radius-xl);aspect-ratio:4/3;overflow:hidden;display:flex;align-items:center;justify-content:center;">
<img src="/assets/images/service-sanding.webp" alt="Hardwood floor sanding service" style="width:100%;height:100%;object-fit:cover;" loading="lazy" onerror="this.style.display='none'">
</div>
</div>
</div>
</section>
<!-- FLOOR INSTALLATION -->
<section class="section section--alt" id="floor-installation">
<div class="container">
<div style="display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,5rem);align-items:center;">
<div data-animate="left" style="background:var(--grain);border-radius:var(--radius-xl);aspect-ratio:4/3;overflow:hidden;display:flex;align-items:center;justify-content:center;order:1;">
<img src="/assets/images/service-installation.webp" alt="New hardwood floor installation" style="width:100%;height:100%;object-fit:cover;" loading="lazy" onerror="this.style.display='none'">
</div>
<div data-animate="right" style="order:2;">
<span class="eyebrow">Service 04</span>
<h2>Floor Installation</h2>
<div class="divider"></div>
<p class="lead">New hardwood floors installed with precision. Quality materials, expert finish, built to last generations.</p>
<p style="margin-top:1rem;color:var(--smoke);">Whether you are renovating a room or building new, our installation team brings the same care and attention to new floors as we do to restoration. We source quality materials and ensure a flawless finished result.</p>
<ul style="margin-top:1.25rem;display:flex;flex-direction:column;gap:0.5rem;">
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--smoke);"><span style="color:var(--amber);font-weight:700;flex-shrink:0;">&#10003;</span> Solid and engineered hardwood</li>
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--smoke);"><span style="color:var(--amber);font-weight:700;flex-shrink:0;">&#10003;</span> Wide range of wood species</li>
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--smoke);"><span style="color:var(--amber);font-weight:700;flex-shrink:0;">&#10003;</span> Residential and commercial</li>
<li style="display:flex;gap:0.75rem;align-items:flex-start;font-size:var(--text-sm);color:var(--smoke);"><span style="color:var(--amber);font-weight:700;flex-shrink:0;">&#10003;</span> Professional site preparation</li>
</ul>
<div class="cta-group mt-8">
<a href="/contact/" class="btn btn--primary">Get an Estimate</a>
<a href="/services/floor-installation/" class="btn btn--outline-dark">Learn More</a>
</div>
</div>
</div>
</div>
</section>
<div class="cta-strip">
<div class="container">
<h2>Not Sure Which Service You Need?</h2>
<p>Call us and we will help you determine the right approach for your floors at no obligation.</p>
<div class="cta-group" style="justify-content:center;">
<a href="/contact/" class="btn btn--outline-dark btn--lg">Request an Estimate</a>
<a href="tel:+17166021429" class="btn btn--outline-dark btn--lg">Call (716) 602-1429</a>
</div>
</div>
</div>
</main>
<div id="site-footer"></div>
<script src="/assets/js/components.js"></script>
<script src="/assets/js/main.js"></script>
</body>
</html>
+12
View File
@@ -0,0 +1,12 @@
<?php
require_once __DIR__ . '/altcha.php';
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store');
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
http_response_code(405);
exit;
}
echo json_encode(VYC_Altcha::create_challenge());
+45
View File
@@ -0,0 +1,45 @@
<?php
class VYC_Altcha {
private static function key(): string {
return getenv('ALTCHA_HMAC_KEY') ?: '';
}
public static function is_enabled(): bool {
return !empty(self::key());
}
public static function create_challenge(int $maxnumber = 100000): array {
$salt = bin2hex(random_bytes(12));
$number = random_int(0, $maxnumber);
$challenge = hash('sha256', $salt . $number);
$signature = hash_hmac('sha256', $challenge, self::key());
return [
'algorithm' => 'SHA-256',
'challenge' => $challenge,
'maxnumber' => $maxnumber,
'salt' => $salt,
'signature' => $signature,
];
}
public static function verify(string $payload): bool {
if (!self::is_enabled()) return true;
if (empty($payload)) return false;
$data = json_decode(base64_decode($payload, true), true);
if (!is_array($data)) return false;
foreach (['algorithm', 'challenge', 'number', 'salt', 'signature'] as $key) {
if (!isset($data[$key])) return false;
}
if ($data['algorithm'] !== 'SHA-256') return false;
$expected_sig = hash_hmac('sha256', $data['challenge'], self::key());
if (!hash_equals($expected_sig, $data['signature'])) return false;
$expected_challenge = hash('sha256', $data['salt'] . $data['number']);
return hash_equals($expected_challenge, $data['challenge']);
}
}
+102
View File
@@ -0,0 +1,102 @@
</main>
<footer class="site-footer">
<div class="footer-top">
<div class="container">
<div class="footer-grid">
<div class="footer-brand">
<div class="footer-logo-text">Floor It<span>.</span></div>
<p>Western New York's hardwood floor specialists. Serving Buffalo and surrounding communities since 1994.</p>
<div class="footer-contact-list">
<div class="footer-contact-item">
<div class="footer-contact-icon">
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z"/></svg>
</div>
<div>
<a href="tel:+17166021429">(716) 602-1429</a>
</div>
</div>
<div class="footer-contact-item">
<div class="footer-contact-icon">
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>
</div>
<div>
<a href="mailto:floorithardwoods@gmail.com">floorithardwoods@gmail.com</a>
</div>
</div>
</div>
</div>
<div class="footer-col">
<h5>Services</h5>
<ul>
<li><a href="/services/floor-refinishing/">Floor Refinishing</a></li>
<li><a href="/services/floor-restoration/">Floor Restoration</a></li>
<li><a href="/services/floor-sanding/">Floor Sanding</a></li>
<li><a href="/services/floor-installation/">Floor Installation</a></li>
</ul>
</div>
<div class="footer-col">
<h5>Locations</h5>
<ul>
<li><a href="/locations/buffalo/">Buffalo</a></li>
<li><a href="/locations/amherst/">Amherst</a></li>
<li><a href="/locations/williamsville/">Williamsville</a></li>
<li><a href="/locations/clarence/">Clarence</a></li>
<li><a href="/locations/east-amherst/">East Amherst</a></li>
<li><a href="/locations/lancaster/">Lancaster</a></li>
</ul>
</div>
<div class="footer-col">
<h5>Company</h5>
<ul>
<li><a href="/about/">About Us</a></li>
<li><a href="/reviews/">Reviews</a></li>
<li><a href="/blog/">Blog</a></li>
<li><a href="/contact/">Contact</a></li>
</ul>
</div>
</div>
</div>
</div>
<div class="footer-bottom">
<div class="container">
<p>&copy; <?= date('Y') ?> Floor It Hardwood Floors. All rights reserved. Buffalo, NY.</p>
</div>
</div>
</footer>
<link rel="stylesheet" href="/assets/css/promo-popup.css">
<div id="promo-overlay" style="display:none">
<div id="promo-box">
<button id="promo-close" aria-label="Close">&times;</button>
<span class="promo-badge">Limited Time</span>
<h2>Summer Refinishing Special: Up to 15% Off Through June 30, 2026</h2>
<p class="promo-sub">Save up to 15% on hardwood floor refinishing. Book by June 30 to lock in your discount.</p>
<form id="promo-form" novalidate>
<div class="promo-field">
<label for="promo-email">Email Address</label>
<input type="email" id="promo-email" name="email" placeholder="you@email.com" required>
</div>
<div class="promo-field">
<label for="promo-phone">Phone Number</label>
<input type="tel" id="promo-phone" name="phone" placeholder="(716) 000-0000" required>
</div>
<div class="promo-field">
<label for="promo-sqft">Square Footage (approx.)</label>
<input type="number" id="promo-sqft" name="sqft" placeholder="e.g. 800" min="50" max="50000" required>
</div>
<button type="submit" id="promo-submit" class="promo-submit">Claim My Discount</button>
<p class="promo-error" id="promo-error"></p>
</form>
<div class="promo-success" id="promo-success">
<p><strong>You are locked in.</strong><br>We will reach out within 24 hours to schedule your estimate.</p>
</div>
<p class="promo-fine">Offer valid through June 30, 2026. Buffalo, NY area only.</p>
</div>
</div>
<script src="/assets/js/main.js" defer></script>
<script src="/assets/js/form.js" defer></script>
<script src="/assets/js/promo-popup.js" defer></script>
</body>
</html>
+75
View File
@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
$db_path = __DIR__ . '/../data/pages.sqlite';
$db = new SQLite3($db_path, SQLITE3_OPEN_READONLY);
$db->busyTimeout(3000);
$nav_result = $db->query("SELECT slug, nav_label FROM pages WHERE in_nav=1 ORDER BY nav_order");
$nav_items = [];
while ($row = $nav_result->fetchArray(SQLITE3_ASSOC)) {
$nav_items[] = $row;
}
$db->close();
$canonical = $canonical ?? '';
$page_title = htmlspecialchars($page_title ?? 'Floor It Hardwood Floors', ENT_QUOTES);
$page_meta = htmlspecialchars($page_meta ?? '', ENT_QUOTES);
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= $page_title ?></title>
<meta name="description" content="<?= $page_meta ?>">
<?php if ($canonical): ?><link rel="canonical" href="<?= htmlspecialchars($canonical, ENT_QUOTES) ?>">
<?php endif; ?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/tokens.css">
<link rel="stylesheet" href="/assets/css/main.css">
<link rel="stylesheet" href="/assets/css/components.css">
</head>
<body>
<div id="promo-topbar" role="complementary" aria-label="Summer promotion">
<span id="promo-topbar-text">Summer Refinishing Savings: Save up to 15% through June 30, 2026</span>
<button id="promo-topbar-btn" type="button">Get Offer</button>
<button id="promo-topbar-close" type="button" aria-label="Dismiss promotion">&times;</button>
</div>
<header id="site-header" class="site-header">
<div class="container">
<div class="header-inner">
<a href="/" class="header-logo" aria-label="Floor It Hardwood Floors">
<img src="/assets/images/logo-header.png" alt="Floor It Hardwood Floors" style="height:42px;width:auto;object-fit:contain;">
</a>
<nav class="header-nav" aria-label="Main navigation">
<?php foreach ($nav_items as $item):
$href = $item['slug'] === 'home' ? '/' : '/' . $item['slug'] . '/';
?><a href="<?= htmlspecialchars($href, ENT_QUOTES) ?>"><?= htmlspecialchars($item['nav_label'], ENT_QUOTES) ?></a>
<?php endforeach; ?>
</nav>
<div class="header-cta">
<a href="tel:+17166021429" class="header-phone" aria-label="Call (716) 602-1429">(716) 602-1429</a>
<a href="/contact/" class="btn btn--primary btn--sm">Get Estimate</a>
<button class="header-menu-btn" aria-label="Open menu" aria-expanded="false">
<span></span><span></span><span></span>
</button>
</div>
</div>
</div>
<div class="mobile-nav" id="mobileNav" aria-hidden="true">
<div class="mobile-nav-overlay" id="mobileNavOverlay"></div>
<div class="mobile-nav-panel">
<button class="mobile-nav-close" aria-label="Close menu" id="mobileNavClose">&#x2715;</button>
<nav class="mobile-nav-links">
<?php foreach ($nav_items as $item):
$href = $item['slug'] === 'home' ? '/' : '/' . $item['slug'] . '/';
?><a href="<?= htmlspecialchars($href, ENT_QUOTES) ?>"><?= htmlspecialchars($item['nav_label'], ENT_QUOTES) ?></a>
<?php endforeach; ?>
</nav>
<div class="mobile-nav-cta">
<a href="tel:+17166021429" class="mobile-nav-phone">(716) 602-1429</a>
<a href="/contact/" class="btn btn--primary btn--full">Request Free Estimate</a>
</div>
</div>
</div>
</header>
<main>
+196
View File
@@ -0,0 +1,196 @@
<?php
/**
* Floor It Hardwood Floors contact form handler.
*
* Pipeline:
* 1. Read JSON body (32KB cap)
* 2. Validate required fields
* 3. Honeypot + time-on-page checks
* 4. Altcha server-side verify
* 5. Sliding-window per-IP rate limit (file-backed in /var/www/html/src/api/data/rate-limits/)
* 6. POST to Resend email to contact address
* 7. JSON response
*
* Configuration is read entirely from environment variables set these in
* .env or the runtime environment. No hardcoded keys in this file.
*/
declare(strict_types=1);
require_once __DIR__ . '/altcha.php';
header('Content-Type: application/json; charset=utf-8');
header('X-Content-Type-Options: nosniff');
// ─── Config from environment ────────────────────────────────────────
$RESEND_API_KEY = getenv('RESEND_API_KEY') ?: '';
$FROM_EMAIL = getenv('FROM_EMAIL') ?: 'noreply@floorithardwoods.com';
$TO_EMAIL = getenv('TO_EMAIL') ?: 'floorithardwoodfloors@gmail.com';
$RATE_LIMIT = (int)(getenv('RATE_LIMIT_PER_IP_PER_10MIN') ?: 5);
$TIME_MIN_SECONDS = (int)(getenv('TIME_MIN_SECONDS') ?: 3);
// ─── Helpers ────────────────────────────────────────────────────────
function send_json(array $data, int $status = 200): void {
http_response_code($status);
echo json_encode($data, JSON_UNESCAPED_SLASHES);
exit;
}
function fail(string $msg, int $status = 200): void {
send_json(['ok' => false, 'error' => $msg], $status);
}
function client_ip(): string {
$trust = (getenv('TRUST_PROXY') === '1');
if ($trust) {
$fwd = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '';
if ($fwd) return trim(explode(',', $fwd)[0]);
}
return $_SERVER['REMOTE_ADDR'] ?? 'unknown';
}
function rate_limit_check(string $ip, int $max, int $window_seconds): bool {
$dir = '/var/www/html/src/api/data/rate-limits';
if (!is_dir($dir)) @mkdir($dir, 0700, true);
$file = $dir . '/' . preg_replace('/[^a-zA-Z0-9._:-]/', '_', $ip);
$now = time();
$events = [];
if (is_file($file)) {
$raw = @file_get_contents($file);
$events = $raw ? array_filter(array_map('intval', explode("\n", $raw))) : [];
$events = array_filter($events, fn($t) => $now - $t <= $window_seconds);
}
if (count($events) >= $max) return false;
$events[] = $now;
@file_put_contents($file, implode("\n", $events), LOCK_EX);
return true;
}
function http_post_json(string $url, array $headers, array $body, int $timeout = 15): array {
$ch = curl_init($url);
$payload = json_encode($body, JSON_UNESCAPED_SLASHES);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => array_merge(['Content-Type: application/json'], $headers),
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_TIMEOUT => $timeout,
]);
$resp = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$err = curl_error($ch);
curl_close($ch);
return ['status' => $code, 'body' => $resp, 'err' => $err];
}
// ─── Method gate ────────────────────────────────────────────────────
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
fail('Method Not Allowed', 405);
}
// ─── Read + decode JSON body (capped to prevent DoS via huge payloads) ─
$MAX_BODY_BYTES = 32 * 1024; // 32 KB is plenty for this form
$raw = '';
$in = fopen('php://input', 'rb');
if ($in) {
$raw = stream_get_contents($in, $MAX_BODY_BYTES + 1);
fclose($in);
}
if (strlen($raw) > $MAX_BODY_BYTES) {
fail('Payload too large.', 413);
}
$body = json_decode($raw, true);
if (!is_array($body)) {
fail('Invalid request payload.');
}
$request_id = bin2hex(random_bytes(6));
$ip = client_ip();
// ─── Rate limit ─────────────────────────────────────────────────────
if (!rate_limit_check($ip, $RATE_LIMIT, 600)) {
error_log("[floorit.form] rate_limited request_id=$request_id ip=$ip");
fail('Too many requests. Please wait a few minutes.', 429);
}
// ─── Field extraction + validation ──────────────────────────────────
$name = trim((string)($body['name'] ?? ''));
$email = trim((string)($body['email'] ?? ''));
$phone = trim((string)($body['phone'] ?? ''));
$message = trim((string)($body['message'] ?? ''));
$website = trim((string)($body['website'] ?? '')); // honeypot
$form_loaded_at = trim((string)($body['form_loaded_at'] ?? ''));
$altcha_payload = trim((string)($body['altcha'] ?? ''));
$errors = [];
if (mb_strlen($name) < 2 || mb_strlen($name) > 80) $errors[] = 'name';
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) $errors[] = 'email';
if (mb_strlen($phone) > 20) $errors[] = 'phone';
if (mb_strlen($message) > 2000) $errors[] = 'message';
if ($errors) {
error_log("[floorit.form] validation_error request_id=$request_id fields=" . implode(',', $errors));
fail('Please check the form fields and try again.');
}
// ─── Honeypot ───────────────────────────────────────────────────────
if ($website !== '') {
error_log("[floorit.form] honeypot_triggered request_id=$request_id ip=$ip");
send_json(['ok' => true, 'ref' => $request_id]); // pretend success
}
// ─── Time-on-page ───────────────────────────────────────────────────
$flagged_review = false;
if ($form_loaded_at !== '' && ctype_digit(ltrim($form_loaded_at, '-'))) {
$loaded_ms = (int)$form_loaded_at;
$elapsed = (microtime(true) * 1000 - $loaded_ms) / 1000.0;
if ($elapsed < $TIME_MIN_SECONDS) $flagged_review = true;
}
// ─── Altcha verify ──────────────────────────────────────────────────
if (!VYC_Altcha::verify($altcha_payload)) {
error_log("[floorit.form] altcha_verification_failed request_id=$request_id");
fail('Spam check failed.');
}
// ─── Compose email body ─────────────────────────────────────────────
$subject_name = preg_replace('/[\x00-\x1F\x7F]/u', ' ', $name);
$subject_prefix = $flagged_review ? '[REVIEW] ' : '';
$subject = "{$subject_prefix}New estimate request from {$subject_name}";
$text_body =
"A new estimate request came in through floorithardwoods.com.\n\n" .
"Name: {$name}\n" .
"Email: {$email}\n" .
"Phone: " . ($phone ?: 'not provided') . "\n\n" .
"Message:\n" . ($message ?: '(no message)') . "\n\n" .
"Submitted at: " . gmdate('Y-m-d\TH:i:s\Z') . "\n" .
"Request id: {$request_id}\n";
$html_body = nl2br(htmlspecialchars($text_body, ENT_QUOTES, 'UTF-8'));
// ─── Send via Resend ────────────────────────────────────────────────
if ($RESEND_API_KEY !== '') {
$r = http_post_json(
'https://api.resend.com/emails',
["Authorization: Bearer {$RESEND_API_KEY}"],
[
'from' => $FROM_EMAIL,
'to' => [$TO_EMAIL],
'reply_to' => $email,
'subject' => $subject,
'text' => $text_body,
'html' => $html_body,
]
);
if ($r['status'] >= 300) {
error_log("[floorit.form] resend_send_failed request_id=$request_id status={$r['status']} body=" . substr($r['body'] ?? '', 0, 300));
fail('Could not send the message. Please call (716) 602-1429 directly.');
}
$resend_id = (json_decode($r['body'] ?? '', true)['id'] ?? '');
error_log("[floorit.form] resend_send_ok request_id=$request_id resend_id=$resend_id");
} else {
// Pre-launch / no Resend key: log only, return success
error_log("[floorit.form] resend_skipped_no_key request_id=$request_id\nSubject: $subject\n$text_body");
}
send_json(['ok' => true, 'ref' => $request_id]);
Binary file not shown.
Binary file not shown.
View File
Binary file not shown.
View File
View File
Binary file not shown.
Binary file not shown.
+73
View File
@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
exit;
}
header('Content-Type: application/json');
$email = trim(strip_tags($_POST['email'] ?? ''));
$phone = trim(strip_tags($_POST['phone'] ?? ''));
$sqft = trim(strip_tags($_POST['sqft'] ?? ''));
if (!$email || !$phone || !$sqft) {
http_response_code(400);
echo json_encode(['ok' => false, 'error' => 'All fields are required.']);
exit;
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
http_response_code(400);
echo json_encode(['ok' => false, 'error' => 'Invalid email address.']);
exit;
}
$sqft_int = (int) preg_replace('/[^0-9]/', '', $sqft);
if ($sqft_int < 50 || $sqft_int > 50000) {
http_response_code(400);
echo json_encode(['ok' => false, 'error' => 'Please enter a valid square footage.']);
exit;
}
$api_key = getenv('RESEND_API_KEY');
$from = getenv('FROM_EMAIL') ?: 'Floor It Hardwood Floors <webleads@floorithardwoods.com>';
$to_email = getenv('TO_EMAIL') ?: 'floorithardwoods@gmail.com';
if (!$api_key) {
http_response_code(500);
echo json_encode(['ok' => false, 'error' => 'Server configuration error.']);
exit;
}
$body = "Summer Refinishing Savings Lead\n\nEmail: {$email}\nPhone: {$phone}\nSquare Footage: {$sqft_int} sq ft\n\nOffer: Save up to 15% off through June 30, 2026.";
$payload = json_encode([
'from' => $from,
'to' => [$to_email],
'subject' => "Summer Promo Lead: {$sqft_int} sq ft from {$email}",
'text' => $body,
]);
$ch = curl_init('https://api.resend.com/emails');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $api_key,
'Content-Type: application/json',
],
CURLOPT_TIMEOUT => 10,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status >= 200 && $status < 300) {
echo json_encode(['ok' => true]);
} else {
http_response_code(502);
echo json_encode(['ok' => false, 'error' => 'Something went wrong. Please call (716) 602-1429.']);
}
+31
View File
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
$type = preg_replace('/[^a-z_]/', '', strtolower($_GET['type'] ?? 'page'));
$slug = preg_replace('/[^a-z0-9-]/', '', strtolower($_GET['slug'] ?? 'home'));
$allowed = ['page', 'service', 'location', 'blog', 'blog_post'];
if (!in_array($type, $allowed, true)) {
$type = 'page';
$slug = 'home';
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $type === 'page' && $slug === 'contact') {
require __DIR__ . '/contact.php';
exit;
}
$template = match($type) {
'service' => __DIR__ . '/templates/service.php',
'location' => __DIR__ . '/templates/location.php',
'blog', 'blog_post' => __DIR__ . '/templates/blog.php',
default => __DIR__ . '/templates/page.php',
};
if (!file_exists($template)) {
http_response_code(404);
require __DIR__ . '/templates/page.php';
exit;
}
require $template;
+102
View File
@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
$db = new SQLite3(__DIR__ . '/../data/blog.sqlite', SQLITE3_OPEN_READONLY);
$db->busyTimeout(3000);
if ($slug === 'blog') {
// Blog listing
$posts_result = $db->query("SELECT slug, title, excerpt, created_at FROM posts WHERE published=1 ORDER BY created_at DESC");
$posts = [];
while ($row = $posts_result->fetchArray(SQLITE3_ASSOC)) { $posts[] = $row; }
$db->close();
$page_title = 'Hardwood Floor Tips & Advice | Floor It Hardwood Floors Blog';
$page_meta = 'Expert hardwood floor tips from Floor It Hardwood Floors. Refinishing, restoration, care advice for Buffalo and Erie County homeowners.';
require __DIR__ . '/../components/_header.php';
?>
<section class="page-hero section section--dark">
<div class="container">
<div class="page-hero-content">
<span class="eyebrow">Hardwood Floor Tips</span>
<h1>The Floor It Blog</h1>
<p class="lead">Expert advice on hardwood floor refinishing, restoration, and care from the Buffalo area's most experienced team.</p>
</div>
</div>
</section>
<section class="section section--light">
<div class="container">
<?php if ($posts): ?>
<div class="grid grid--3col blog-grid">
<?php foreach ($posts as $post): ?>
<article class="card card--blog">
<div class="card-body">
<h2><a href="/blog/<?= htmlspecialchars($post['slug'], ENT_QUOTES) ?>/"><?= htmlspecialchars($post['title'], ENT_QUOTES) ?></a></h2>
<p><?= htmlspecialchars($post['excerpt'] ?? '', ENT_QUOTES) ?></p>
<a href="/blog/<?= htmlspecialchars($post['slug'], ENT_QUOTES) ?>/" class="card-link">Read more &rarr;</a>
</div>
</article>
<?php endforeach; ?>
</div>
<?php else: ?>
<p>No posts yet. Check back soon.</p>
<?php endif; ?>
</div>
</section>
<section class="section section--amber cta-section">
<div class="container cta-section-inner">
<div class="cta-section-text">
<h2>Questions About Your Floors?</h2>
<p>Contact our team for a free estimate and expert advice.</p>
</div>
<a href="/contact/" class="btn btn--primary btn--lg">Request an Estimate</a>
</div>
</section>
<?php
} else {
// Individual post
$post = $db->querySingle("SELECT * FROM posts WHERE slug='" . SQLite3::escapeString($slug) . "' AND published=1", true);
$db->close();
if (!$post) {
http_response_code(404);
$page_title = '404: Post Not Found | Floor It Hardwood Floors';
$page_meta = '';
require __DIR__ . '/../components/_header.php';
echo '<section class="section section--light"><div class="container"><h1>Post Not Found</h1><p><a href="/blog/">Back to blog</a></p></div></section>';
require __DIR__ . '/../components/_footer.php';
exit;
}
$page_title = $post['title'] . ' | Floor It Hardwood Floors';
$page_meta = $post['excerpt'] ?? '';
require __DIR__ . '/../components/_header.php';
?>
<section class="page-hero section section--dark">
<div class="container">
<div class="page-hero-content">
<span class="eyebrow">Floor It Blog</span>
<h1><?= htmlspecialchars($post['title'], ENT_QUOTES) ?></h1>
<p class="lead"><?= htmlspecialchars($post['excerpt'] ?? '', ENT_QUOTES) ?></p>
</div>
</div>
</section>
<section class="section section--light">
<div class="container blog-post-body">
<?= $post['body_html'] ?>
<p style="margin-top:2rem;"><a href="/blog/">Back to blog</a></p>
</div>
</section>
<section class="section section--amber cta-section">
<div class="container cta-section-inner">
<div class="cta-section-text">
<h2>Ready to Restore Your Floors?</h2>
<p>Request a free estimate from our Buffalo team.</p>
</div>
<a href="/contact/" class="btn btn--primary btn--lg">Get a Free Estimate</a>
</div>
</section>
<?php
}
require __DIR__ . '/../components/_footer.php'; ?>
+122
View File
@@ -0,0 +1,122 @@
<?php
declare(strict_types=1);
$db = new SQLite3(__DIR__ . '/../data/locations.sqlite', SQLITE3_OPEN_READONLY);
$db->busyTimeout(3000);
$loc = $db->querySingle("SELECT * FROM locations WHERE slug='" . SQLite3::escapeString($slug) . "'", true);
$db->close();
if (!$loc) {
http_response_code(404);
$page_title = '404: Location Not Found | Floor It Hardwood Floors';
$page_meta = '';
require __DIR__ . '/../components/_header.php';
echo '<section class="section section--light"><div class="container"><h1>Location Not Found</h1><p><a href="/locations/">View all locations</a></p></div></section>';
require __DIR__ . '/../components/_footer.php';
exit;
}
$page_title = $loc['title'];
$page_meta = $loc['meta_description'];
$body = json_decode($loc['body_json'] ?? '{}', true) ?? [];
$faqs = json_decode($loc['faqs_json'] ?? '[]', true) ?? [];
$city = $loc['city'];
require __DIR__ . '/../components/_header.php';
?>
<section class="page-hero section section--dark">
<div class="container">
<div class="page-hero-content">
<span class="eyebrow"><?= htmlspecialchars($loc['hero_eyebrow'] ?? '', ENT_QUOTES) ?></span>
<h1><?= htmlspecialchars($loc['hero_h1'] ?? '', ENT_QUOTES) ?></h1>
<p class="lead"><?= htmlspecialchars($loc['hero_lead'] ?? '', ENT_QUOTES) ?></p>
<a href="/contact/" class="btn btn--primary btn--lg">Request a <?= htmlspecialchars($city, ENT_QUOTES) ?> Estimate</a>
</div>
</div>
</section>
<?php if (!empty($body['overview_h2'])): ?>
<section class="section section--light">
<div class="container">
<div class="section-header">
<?php if (!empty($body['overview_eyebrow'])): ?><span class="eyebrow"><?= htmlspecialchars($body['overview_eyebrow'], ENT_QUOTES) ?></span><?php endif; ?>
<h2><?= htmlspecialchars($body['overview_h2'], ENT_QUOTES) ?></h2>
<div class="divider"></div>
<?php if (!empty($body['overview_body_1'])): ?><p><?= htmlspecialchars($body['overview_body_1'], ENT_QUOTES) ?></p><?php endif; ?>
<?php if (!empty($body['overview_body_2'])): ?><p style="margin-top:1rem;color:var(--smoke);"><?= htmlspecialchars($body['overview_body_2'], ENT_QUOTES) ?></p><?php endif; ?>
</div>
<?php
$stats = [];
for ($i = 1; $i <= 3; $i++) {
if (!empty($body["stat_{$i}_num"])) {
$stats[] = ['num' => $body["stat_{$i}_num"], 'label' => $body["stat_{$i}_label"] ?? '', 'sub' => $body["stat_{$i}_sub"] ?? ''];
}
}
if ($stats): ?>
<div class="stats-row">
<?php foreach ($stats as $stat): ?>
<div class="stat-item">
<strong><?= htmlspecialchars($stat['num'], ENT_QUOTES) ?></strong>
<span><?= htmlspecialchars($stat['label'], ENT_QUOTES) ?></span>
<?php if ($stat['sub']): ?><small><?= htmlspecialchars($stat['sub'], ENT_QUOTES) ?></small><?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</section>
<?php endif; ?>
<?php if (!empty($body['services_intro'])): ?>
<section class="section section--dark">
<div class="container">
<div class="section-header">
<span class="eyebrow">Services in <?= htmlspecialchars($city, ENT_QUOTES) ?></span>
<h2>Hardwood Floor Services in <?= htmlspecialchars($city, ENT_QUOTES) ?>, NY</h2>
<p class="lead"><?= htmlspecialchars($body['services_intro'], ENT_QUOTES) ?></p>
</div>
<div class="grid grid--3col services-local-grid">
<?php for ($i = 1; $i <= 3; $i++): $tk = "service_{$i}_title"; $bk = "service_{$i}_body"; ?>
<?php if (!empty($body[$tk])): ?>
<div class="card card--service-local">
<h3><?= htmlspecialchars($body[$tk], ENT_QUOTES) ?></h3>
<p><?= htmlspecialchars($body[$bk] ?? '', ENT_QUOTES) ?></p>
</div>
<?php endif; ?>
<?php endfor; ?>
</div>
</div>
</section>
<?php endif; ?>
<?php if ($faqs): ?>
<section class="section section--light">
<div class="container faq-section">
<div class="section-header">
<span class="eyebrow"><?= htmlspecialchars($city, ENT_QUOTES) ?> FAQs</span>
<h2>Common Questions from <?= htmlspecialchars($city, ENT_QUOTES) ?> Homeowners</h2>
</div>
<div class="faq-list">
<?php foreach ($faqs as $faq): ?>
<details class="faq-item">
<summary><?= htmlspecialchars($faq['q'] ?? '', ENT_QUOTES) ?></summary>
<p><?= htmlspecialchars($faq['a'] ?? '', ENT_QUOTES) ?></p>
</details>
<?php endforeach; ?>
</div>
</div>
</section>
<?php endif; ?>
<section class="section section--amber cta-section">
<div class="container cta-section-inner">
<div class="cta-section-text">
<h2><?= htmlspecialchars($body['form_h2'] ?? "Request a {$city} Floor Estimate", ENT_QUOTES) ?></h2>
<p>We respond to all <?= htmlspecialchars($city, ENT_QUOTES) ?> inquiries within 24 hours.</p>
</div>
<a href="/contact/" class="btn btn--primary btn--lg"><?= htmlspecialchars($body['form_submit'] ?? 'Send Estimate Request', ENT_QUOTES) ?></a>
</div>
</section>
<?php require __DIR__ . '/../components/_footer.php'; ?>
+391
View File
@@ -0,0 +1,391 @@
<?php
declare(strict_types=1);
$db = new SQLite3(__DIR__ . '/../data/pages.sqlite', SQLITE3_OPEN_READONLY);
$db->busyTimeout(3000);
$page = $db->querySingle("SELECT * FROM pages WHERE slug='" . SQLite3::escapeString($slug) . "'", true);
$db->close();
if (!$page) {
http_response_code(404);
$page_title = '404: Page Not Found | Floor It Hardwood Floors';
$page_meta = '';
require __DIR__ . '/../components/_header.php';
echo '<section class="section section--light"><div class="container"><h1>Page Not Found</h1><p><a href="/">Return home</a></p></div></section>';
require __DIR__ . '/../components/_footer.php';
exit;
}
$page_title = $page['title'];
$page_meta = $page['meta_description'];
$sections = json_decode($page['sections_json'], true) ?? [];
require __DIR__ . '/../components/_header.php';
foreach ($sections as $s) {
$t = $s['type'] ?? '';
switch ($t) {
case 'hero_video':
$stats = $s['stats'] ?? [];
?>
<section class="hero" aria-label="Hero">
<div class="hero-video-wrap">
<video autoplay muted loop playsinline poster="<?= htmlspecialchars($s['poster'] ?? '', ENT_QUOTES) ?>" aria-hidden="true">
<source src="<?= htmlspecialchars($s['video_webm'] ?? '', ENT_QUOTES) ?>" type="video/webm">
<source src="<?= htmlspecialchars($s['video_mp4'] ?? '', ENT_QUOTES) ?>" type="video/mp4">
</video>
<div class="hero-overlay"></div>
</div>
<div class="container">
<div class="hero-content">
<div class="hero-eyebrow">
<div class="hero-eyebrow-line"></div>
<span><?= htmlspecialchars($s['eyebrow'] ?? '', ENT_QUOTES) ?></span>
</div>
<h1><?= htmlspecialchars($s['h1'] ?? '', ENT_QUOTES) ?></h1>
<p class="hero-sub"><?= htmlspecialchars($s['lead'] ?? '', ENT_QUOTES) ?></p>
<div class="hero-actions">
<a href="<?= htmlspecialchars($s['cta_primary_href'] ?? '/contact/', ENT_QUOTES) ?>" class="btn btn--primary btn--lg"><?= htmlspecialchars($s['cta_primary'] ?? 'Get Estimate', ENT_QUOTES) ?></a>
<?php if (!empty($s['cta_secondary'])): ?>
<a href="<?= htmlspecialchars($s['cta_secondary_href'] ?? '#', ENT_QUOTES) ?>" class="btn btn--ghost btn--lg"><?= htmlspecialchars($s['cta_secondary'], ENT_QUOTES) ?></a>
<?php endif; ?>
</div>
<?php if ($stats): ?>
<div class="hero-trust">
<?php foreach ($stats as $stat): ?>
<div class="hero-trust-stat">
<strong><?= htmlspecialchars($stat['num'] ?? '', ENT_QUOTES) ?></strong>
<span><?= htmlspecialchars($stat['label'] ?? '', ENT_QUOTES) ?></span>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</section>
<?php
break;
case 'hero_simple':
?>
<section class="page-hero section section--dark">
<div class="container">
<div class="page-hero-content">
<?php if (!empty($s['eyebrow'])): ?><span class="eyebrow"><?= htmlspecialchars($s['eyebrow'], ENT_QUOTES) ?></span><?php endif; ?>
<h1><?= htmlspecialchars($s['h1'] ?? '', ENT_QUOTES) ?></h1>
<?php if (!empty($s['lead'])): ?><p class="lead"><?= htmlspecialchars($s['lead'], ENT_QUOTES) ?></p><?php endif; ?>
</div>
</div>
</section>
<?php
break;
case 'services_grid':
$sdb = new SQLite3(__DIR__ . '/../data/services.sqlite', SQLITE3_OPEN_READONLY);
$svc_result = $sdb->query("SELECT slug, service_name, hero_eyebrow, hero_lead, hero_image FROM services ORDER BY id");
$services = [];
while ($row = $svc_result->fetchArray(SQLITE3_ASSOC)) { $services[] = $row; }
$sdb->close();
$svc_images = [
'floor-refinishing' => '/assets/images/project-1-after.webp',
'floor-restoration' => '/assets/images/project-2-before.webp',
'floor-sanding' => '/assets/images/project-3-before.webp',
'floor-installation' => '/assets/images/project-1-before.webp',
];
?>
<section class="section section--light" id="services">
<div class="container">
<div class="section-header">
<?php if (!empty($s['eyebrow'])): ?><span class="eyebrow"><?= htmlspecialchars($s['eyebrow'], ENT_QUOTES) ?></span><?php endif; ?>
<?php if (!empty($s['h2'])): ?><h2><?= htmlspecialchars($s['h2'], ENT_QUOTES) ?></h2><?php endif; ?>
<?php if (!empty($s['lead'])): ?><p class="lead"><?= htmlspecialchars($s['lead'], ENT_QUOTES) ?></p><?php endif; ?>
</div>
<div class="grid grid--4col services-grid">
<?php foreach ($services as $svc): ?>
<a href="/services/<?= htmlspecialchars($svc['slug'], ENT_QUOTES) ?>/" class="card card--service">
<?php $img = $svc_images[$svc['slug']] ?? '/assets/images/project-1-after.webp'; ?>
<div class="card-img-wrap"><img src="<?= $img ?>" alt="<?= htmlspecialchars($svc['service_name'], ENT_QUOTES) ?>" loading="lazy"></div>
<div class="card-body">
<h3><?= htmlspecialchars($svc['service_name'], ENT_QUOTES) ?></h3>
<p><?= htmlspecialchars($svc['hero_lead'], ENT_QUOTES) ?></p>
<span class="card-link">Learn more &rarr;</span>
</div>
</a>
<?php endforeach; ?>
</div>
</div>
</section>
<?php
break;
case 'process_steps':
$steps = $s['steps'] ?? [];
?>
<section class="section section--dark" id="process">
<div class="container">
<div class="section-header section-header--center">
<?php if (!empty($s['eyebrow'])): ?><span class="eyebrow"><?= htmlspecialchars($s['eyebrow'], ENT_QUOTES) ?></span><?php endif; ?>
<h2><?= htmlspecialchars($s['h2'] ?? '', ENT_QUOTES) ?></h2>
</div>
<div class="grid grid--3col process-grid">
<?php foreach ($steps as $step): ?>
<div class="process-step">
<div class="process-num"><?= htmlspecialchars($step['num'] ?? '', ENT_QUOTES) ?></div>
<h3><?= htmlspecialchars($step['title'] ?? '', ENT_QUOTES) ?></h3>
<p><?= htmlspecialchars($step['body'] ?? '', ENT_QUOTES) ?></p>
</div>
<?php endforeach; ?>
</div>
</div>
</section>
<?php
break;
case 'about_preview':
?>
<section class="section section--alt" id="about-preview">
<div class="container about-preview-inner">
<div class="about-preview-text">
<?php if (!empty($s['eyebrow'])): ?><span class="eyebrow"><?= htmlspecialchars($s['eyebrow'], ENT_QUOTES) ?></span><?php endif; ?>
<h2><?= htmlspecialchars($s['h2'] ?? '', ENT_QUOTES) ?></h2>
<div class="divider"></div>
<?php if (!empty($s['lead'])): ?><p class="lead"><?= htmlspecialchars($s['lead'], ENT_QUOTES) ?></p><?php endif; ?>
<?php if (!empty($s['body'])): ?><p style="margin-top:1.25rem;color:var(--smoke);"><?= htmlspecialchars($s['body'], ENT_QUOTES) ?></p><?php endif; ?>
<?php if (!empty($s['cta'])): ?>
<div class="cta-group mt-8">
<a href="<?= htmlspecialchars($s['cta_href'] ?? '/about/', ENT_QUOTES) ?>" class="btn btn--primary"><?= htmlspecialchars($s['cta'], ENT_QUOTES) ?></a>
</div>
<?php endif; ?>
</div>
<div class="about-preview-img">
<img src="/assets/images/project-1-after.webp" alt="Hardwood floor refinishing in Western New York" loading="lazy">
</div>
</div>
</section>
<?php
break;
case 'gallery':
?>
<section class="section section--bark" id="gallery">
<div class="container">
<div class="section-header section-header--center">
<?php if (!empty($s['eyebrow'])): ?><span class="eyebrow"><?= htmlspecialchars($s['eyebrow'], ENT_QUOTES) ?></span><?php endif; ?>
<h2><?= htmlspecialchars($s['h2'] ?? '', ENT_QUOTES) ?></h2>
<?php if (!empty($s['lead'])): ?><p class="lead"><?= htmlspecialchars($s['lead'], ENT_QUOTES) ?></p><?php endif; ?>
</div>
<div class="gallery-grid">
<div class="gallery-item"><img src="/assets/images/project-1-before.webp" alt="Before refinishing" loading="lazy"><span class="gallery-item-label">Before</span></div>
<div class="gallery-item"><img src="/assets/images/project-1-after.webp" alt="After refinishing" loading="lazy"><span class="gallery-item-label gallery-item-label--after">After</span></div>
<div class="gallery-item"><img src="/assets/images/project-2-before.webp" alt="Before restoration" loading="lazy"><span class="gallery-item-label">Before</span></div>
<div class="gallery-item"><img src="/assets/images/project-3-before.webp" alt="Refinishing in progress" loading="lazy"><span class="gallery-item-label">In Progress</span></div>
</div>
</div>
</section>
<?php
break;
case 'cta_banner':
?>
<section class="section section--amber cta-section">
<div class="container cta-section-inner">
<div class="cta-section-text">
<h2><?= htmlspecialchars($s['h2'] ?? '', ENT_QUOTES) ?></h2>
<?php if (!empty($s['body'])): ?><p><?= htmlspecialchars($s['body'], ENT_QUOTES) ?></p><?php endif; ?>
</div>
<a href="<?= htmlspecialchars($s['cta_href'] ?? '/contact/', ENT_QUOTES) ?>" class="btn btn--primary btn--lg"><?= htmlspecialchars($s['cta'] ?? 'Get Estimate', ENT_QUOTES) ?></a>
</div>
</section>
<?php
break;
case 'contact_form':
?>
<section class="section section--light" id="contact">
<div class="container contact-layout">
<div class="contact-info">
<h2><?= htmlspecialchars($s['h2'] ?? 'Send Us a Message', ENT_QUOTES) ?></h2>
<div class="contact-detail"><strong>Phone</strong><a href="tel:+17166021429"><?= htmlspecialchars($s['phone'] ?? '(716) 602-1429', ENT_QUOTES) ?></a></div>
<div class="contact-detail"><strong>Email</strong><a href="mailto:<?= htmlspecialchars($s['email'] ?? '', ENT_QUOTES) ?>"><?= htmlspecialchars($s['email'] ?? '', ENT_QUOTES) ?></a></div>
<div class="contact-detail"><strong>Response Time</strong><span>Within 24 hours</span></div>
<div class="contact-detail"><strong>Service Area</strong><span>Buffalo and Erie County, NY</span></div>
</div>
<form class="contact-form" id="contactForm" action="/contact/" method="POST" novalidate>
<div class="form-group">
<label for="name">Full Name <span aria-hidden="true">*</span></label>
<input type="text" id="name" name="name" required autocomplete="name" placeholder="Your name">
</div>
<div class="form-group">
<label for="email">Email Address <span aria-hidden="true">*</span></label>
<input type="email" id="email" name="email" required autocomplete="email" placeholder="your@email.com">
</div>
<div class="form-group">
<label for="phone">Phone Number</label>
<input type="tel" id="phone" name="phone" autocomplete="tel" placeholder="(716) 555-0000">
</div>
<div class="form-group">
<label for="message">Message <span aria-hidden="true">*</span></label>
<textarea id="message" name="message" required rows="5" placeholder="Tell us about your floors and what you need..."></textarea>
</div>
<input type="text" name="website" style="display:none;" tabindex="-1" autocomplete="off">
<input type="hidden" name="form_loaded_at" id="form_loaded_at">
<div id="altcha-widget"></div>
<button type="submit" id="contactFormSubmit" class="btn btn--primary btn--full">Send Message</button>
<div id="formStatus" role="status" aria-live="polite"></div>
</form>
</div>
</section>
<script src="/assets/js/altcha.min.js"></script>
<script src="/assets/js/form.js"></script>
<?php
break;
case 'story':
?>
<section class="section section--light">
<div class="container about-story">
<div class="about-story-text">
<?php if (!empty($s['eyebrow'])): ?><span class="eyebrow"><?= htmlspecialchars($s['eyebrow'], ENT_QUOTES) ?></span><?php endif; ?>
<h2><?= htmlspecialchars($s['h2'] ?? '', ENT_QUOTES) ?></h2>
<div class="divider"></div>
<?php if (!empty($s['lead'])): ?><p class="lead"><?= htmlspecialchars($s['lead'], ENT_QUOTES) ?></p><?php endif; ?>
<?php if (!empty($s['body'])): ?><p style="margin-top:1.25rem;color:var(--smoke);"><?= htmlspecialchars($s['body'], ENT_QUOTES) ?></p><?php endif; ?>
</div>
<div class="about-story-img">
<img src="/assets/images/refinishing-machine.webp" alt="Floor It refinishing equipment" loading="lazy">
</div>
</div>
</section>
<?php
break;
case 'credentials':
?>
<section class="section section--dark">
<div class="container">
<div class="section-header section-header--center">
<?php if (!empty($s['eyebrow'])): ?><span class="eyebrow"><?= htmlspecialchars($s['eyebrow'], ENT_QUOTES) ?></span><?php endif; ?>
<h2><?= htmlspecialchars($s['h2'] ?? '', ENT_QUOTES) ?></h2>
</div>
<div class="grid grid--4col credentials-grid">
<div class="credential-item"><strong>30+</strong><span>Years in Business</span></div>
<div class="credential-item"><strong>75+</strong><span>Years Combined Experience</span></div>
<div class="credential-item"><strong>500+</strong><span>Projects Completed</span></div>
<div class="credential-item"><strong>4.9/5</strong><span>Google Rating</span></div>
</div>
</div>
</section>
<?php
break;
case 'service_areas':
$ldb = new SQLite3(__DIR__ . '/../data/locations.sqlite', SQLITE3_OPEN_READONLY);
$loc_result = $ldb->query("SELECT slug, city FROM locations ORDER BY id");
$locations = [];
while ($row = $loc_result->fetchArray(SQLITE3_ASSOC)) { $locations[] = $row; }
$ldb->close();
?>
<section class="section section--light">
<div class="container">
<div class="section-header section-header--center">
<?php if (!empty($s['eyebrow'])): ?><span class="eyebrow"><?= htmlspecialchars($s['eyebrow'], ENT_QUOTES) ?></span><?php endif; ?>
<h2><?= htmlspecialchars($s['h2'] ?? '', ENT_QUOTES) ?></h2>
<?php if (!empty($s['body'])): ?><p class="lead"><?= htmlspecialchars($s['body'], ENT_QUOTES) ?></p><?php endif; ?>
</div>
<div class="locations-pill-list">
<?php foreach ($locations as $loc): ?>
<a href="/locations/<?= htmlspecialchars($loc['slug'], ENT_QUOTES) ?>/" class="location-pill"><?= htmlspecialchars($loc['city'], ENT_QUOTES) ?>, NY</a>
<?php endforeach; ?>
</div>
</div>
</section>
<?php
break;
case 'testimonials_grid':
case 'testimonials_preview':
$tdb = new SQLite3(__DIR__ . '/../data/testimonials.sqlite', SQLITE3_OPEN_READONLY);
$limit = ($t === 'testimonials_preview') ? 3 : 20;
$featured = ($t === 'testimonials_preview') ? 'WHERE featured=1' : '';
$t_result = $tdb->query("SELECT * FROM testimonials {$featured} ORDER BY id LIMIT {$limit}");
$testimonials = [];
while ($row = $t_result->fetchArray(SQLITE3_ASSOC)) { $testimonials[] = $row; }
$tdb->close();
?>
<section class="section section--alt" id="reviews">
<div class="container">
<div class="section-header section-header--center">
<?php if (!empty($s['eyebrow'])): ?><span class="eyebrow"><?= htmlspecialchars($s['eyebrow'], ENT_QUOTES) ?></span><?php endif; ?>
<h2><?= htmlspecialchars($s['h2'] ?? '', ENT_QUOTES) ?></h2>
</div>
<div class="grid grid--3col testimonials-grid">
<?php foreach ($testimonials as $review): ?>
<div class="testimonial">
<div class="testimonial-stars"><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span><span>&#9733;</span></div>
<p class="testimonial-text">"<?= htmlspecialchars($review['quote'], ENT_QUOTES) ?>"</p>
<div class="testimonial-author">
<strong><?= htmlspecialchars($review['author'], ENT_QUOTES) ?></strong>
<span><?= htmlspecialchars($review['location'] ?? '', ENT_QUOTES) ?></span>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</section>
<?php
break;
case 'blog_listing':
$bdb = new SQLite3(__DIR__ . '/../data/blog.sqlite', SQLITE3_OPEN_READONLY);
$b_result = $bdb->query("SELECT slug, title, excerpt, created_at FROM posts WHERE published=1 ORDER BY created_at DESC");
$posts = [];
while ($row = $b_result->fetchArray(SQLITE3_ASSOC)) { $posts[] = $row; }
$bdb->close();
?>
<section class="section section--light">
<div class="container">
<div class="grid grid--3col blog-grid">
<?php foreach ($posts as $post): ?>
<article class="card card--blog">
<div class="card-body">
<h2><a href="/blog/<?= htmlspecialchars($post['slug'], ENT_QUOTES) ?>/"><?= htmlspecialchars($post['title'], ENT_QUOTES) ?></a></h2>
<p><?= htmlspecialchars($post['excerpt'] ?? '', ENT_QUOTES) ?></p>
<a href="/blog/<?= htmlspecialchars($post['slug'], ENT_QUOTES) ?>/" class="card-link">Read more &rarr;</a>
</div>
</article>
<?php endforeach; ?>
</div>
</div>
</section>
<?php
break;
case 'locations_grid':
$ldb2 = new SQLite3(__DIR__ . '/../data/locations.sqlite', SQLITE3_OPEN_READONLY);
$loc2_result = $ldb2->query("SELECT slug, city, hero_lead FROM locations ORDER BY id");
$locs = [];
while ($row = $loc2_result->fetchArray(SQLITE3_ASSOC)) { $locs[] = $row; }
$ldb2->close();
?>
<section class="section section--light">
<div class="container">
<div class="grid grid--3col locations-grid">
<?php foreach ($locs as $loc): ?>
<a href="/locations/<?= htmlspecialchars($loc['slug'], ENT_QUOTES) ?>/" class="card card--location">
<div class="card-body">
<h3><?= htmlspecialchars($loc['city'], ENT_QUOTES) ?>, NY</h3>
<p><?= htmlspecialchars(substr($loc['hero_lead'] ?? '', 0, 120), ENT_QUOTES) ?>...</p>
<span class="card-link">View service area &rarr;</span>
</div>
</a>
<?php endforeach; ?>
</div>
</div>
</section>
<?php
break;
}
}
require __DIR__ . '/../components/_footer.php';
+131
View File
@@ -0,0 +1,131 @@
<?php
declare(strict_types=1);
$db = new SQLite3(__DIR__ . '/../data/services.sqlite', SQLITE3_OPEN_READONLY);
$db->busyTimeout(3000);
$svc = $db->querySingle("SELECT * FROM services WHERE slug='" . SQLite3::escapeString($slug) . "'", true);
$db->close();
if (!$svc) {
http_response_code(404);
$page_title = '404: Service Not Found | Floor It Hardwood Floors';
$page_meta = '';
require __DIR__ . '/../components/_header.php';
echo '<section class="section section--light"><div class="container"><h1>Service Not Found</h1><p><a href="/services/">View all services</a></p></div></section>';
require __DIR__ . '/../components/_footer.php';
exit;
}
$page_title = $svc['title'];
$page_meta = $svc['meta_description'];
$body = json_decode($svc['body_json'] ?? '{}', true) ?? [];
$faqs = json_decode($svc['faqs_json'] ?? '[]', true) ?? [];
require __DIR__ . '/../components/_header.php';
?>
<section class="page-hero section section--dark">
<div class="container">
<div class="page-hero-content">
<span class="eyebrow"><?= htmlspecialchars($svc['hero_eyebrow'] ?? '', ENT_QUOTES) ?></span>
<h1><?= htmlspecialchars($svc['hero_h1'] ?? '', ENT_QUOTES) ?></h1>
<p class="lead"><?= htmlspecialchars($svc['hero_lead'] ?? '', ENT_QUOTES) ?></p>
<a href="/contact/" class="btn btn--primary btn--lg">Get a Free Estimate</a>
</div>
</div>
</section>
<?php if (!empty($body['intro_h2'])): ?>
<section class="section section--light">
<div class="container service-intro">
<div class="service-intro-text">
<h2><?= htmlspecialchars($body['intro_h2'], ENT_QUOTES) ?></h2>
<?php if (!empty($body['intro_body_1'])): ?><p><?= htmlspecialchars($body['intro_body_1'], ENT_QUOTES) ?></p><?php endif; ?>
<?php if (!empty($body['intro_body_2'])): ?><p><?= htmlspecialchars($body['intro_body_2'], ENT_QUOTES) ?></p><?php endif; ?>
</div>
<?php if (!empty($svc['hero_image'])): ?>
<div class="service-intro-img"><img src="<?= htmlspecialchars($svc['hero_image'], ENT_QUOTES) ?>" alt="<?= htmlspecialchars($svc['service_name'] ?? '', ENT_QUOTES) ?>" loading="lazy"></div>
<?php endif; ?>
</div>
</section>
<?php endif; ?>
<?php if (!empty($body['process_intro'])): ?>
<section class="section section--dark">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Our Process</span>
<h2>How We Do It</h2>
<p class="lead"><?= htmlspecialchars($body['process_intro'], ENT_QUOTES) ?></p>
</div>
<div class="grid grid--3col process-grid">
<?php for ($i = 1; $i <= 3; $i++): $title_key = "step_{$i}_title"; $body_key = "step_{$i}_body"; ?>
<?php if (!empty($body[$title_key])): ?>
<div class="process-step">
<div class="process-num">0<?= $i ?></div>
<h3><?= htmlspecialchars($body[$title_key], ENT_QUOTES) ?></h3>
<p><?= htmlspecialchars($body[$body_key] ?? '', ENT_QUOTES) ?></p>
</div>
<?php endif; ?>
<?php endfor; ?>
</div>
</div>
</section>
<?php endif; ?>
<?php
$benefits = [];
for ($i = 1; $i <= 4; $i++) {
if (!empty($body["benefit_{$i}_title"])) {
$benefits[] = ['title' => $body["benefit_{$i}_title"], 'body' => $body["benefit_{$i}_body"] ?? ''];
}
}
if ($benefits): ?>
<section class="section section--alt">
<div class="container">
<div class="section-header section-header--center">
<span class="eyebrow">Why Choose Us</span>
<h2>What Sets Us Apart</h2>
</div>
<div class="grid grid--4col benefits-grid">
<?php foreach ($benefits as $b): ?>
<div class="benefit-item">
<h3><?= htmlspecialchars($b['title'], ENT_QUOTES) ?></h3>
<p><?= htmlspecialchars($b['body'], ENT_QUOTES) ?></p>
</div>
<?php endforeach; ?>
</div>
</div>
</section>
<?php endif; ?>
<?php if ($faqs): ?>
<section class="section section--light">
<div class="container faq-section">
<div class="section-header">
<span class="eyebrow">Common Questions</span>
<h2>Frequently Asked Questions</h2>
</div>
<div class="faq-list">
<?php foreach ($faqs as $faq): ?>
<details class="faq-item">
<summary><?= htmlspecialchars($faq['q'] ?? '', ENT_QUOTES) ?></summary>
<p><?= htmlspecialchars($faq['a'] ?? '', ENT_QUOTES) ?></p>
</details>
<?php endforeach; ?>
</div>
</div>
</section>
<?php endif; ?>
<section class="section section--amber cta-section">
<div class="container cta-section-inner">
<div class="cta-section-text">
<h2><?= htmlspecialchars($body['form_h2'] ?? 'Get Your Free Estimate', ENT_QUOTES) ?></h2>
<p>Contact our team and we will respond within 24 hours.</p>
</div>
<a href="/contact/" class="btn btn--primary btn--lg"><?= htmlspecialchars($body['form_submit'] ?? 'Request Estimate', ENT_QUOTES) ?></a>
</div>
</section>
<?php require __DIR__ . '/../components/_footer.php'; ?>