initial commit

This commit is contained in:
ray
2026-07-12 10:17:17 -04:00
commit dab5a4ebc6
1424 changed files with 330463 additions and 0 deletions
@@ -0,0 +1,94 @@
# AirRepairTeam — Worked Blueprint Example
Full blueprint at: `/mnt/seagate8tb/Websites/AirRepairTeam/BLUEPRINT.md`
## What This Example Demonstrates
A 6-page static website for a multi-trade contractor (HVAC + Handyman/Yardwork) serving 6 Knoxville-area cities. The blueprint covers every page section-by-section with copy psychology, CTA placement, mobile behavior, and local SEO integration.
## Key Patterns Illustrated
### Dual-Service Routing (Homepage Section 3)
When a business has two distinct service lines with different customer psychologies (urgent vs. comparison-shopping), the homepage hero routes traffic with two equal-weight CTAs, and Section 3 presents them as side-by-side cards with visual differentiation (cool blue for HVAC, warm green for handyman).
### Emergency Landing Page (`/hvac/emergency`)
Stripped-down page for Google Ads traffic. No nav links. One goal: phone call. The phone number appears twice (hero + sticky bottom bar). Red/urgent accent. Must load in under 1 second — no images, no frameworks, inline CSS only if needed.
### Psychology-Driven Service Pages
**HVAC page** (high-urgency, high-ticket):
- Hero leads with emergency language and "no after-hours fees"
- Pricing table builds trust (most HVAC companies hide pricing)
- "What to do in an HVAC emergency" section provides SEO content + conversion nudge
- Phone is always the primary CTA
**Handyman page** (comparison shoppers):
- Hero is a photo of completed work
- Before/after gallery is THE conversion driver — juxtoposition proves competence
- 3-step "How It Works" removes friction for first-time customers
- Lighter, friendlier tone throughout
### Local SEO Integration (6 Cities)
Cities appear exactly 5 ways across the site:
1. H1 on homepage: "Knoxville & Surrounding Areas"
2. H2 on HVAC page: "HVAC Repair in Knoxville, Powell, Halls & Beyond"
3. Body bullets on HVAC page with fake "response times" per city (makes it useful, not spammy)
4. Footer on every page: "📍 Proudly serving: Knoxville · Powell · Halls · Corryton · Fountain City · Karns"
5. Image alt text: "Fence installation in Powell TN"
No separate city pages. No city-page links. One consistent signal.
### Quote Form Design
- 6 fields: Name*, Phone*, Email (optional), Service (dropdown)*, Project description*, Lead source
- No calendar, no file upload, no address, no CAPTCHA (initially)
- Success state still shows phone number (for emergency callers who used the form)
- Posts to Formspree/Web3Forms — no backend
### Web3Forms Form Pattern
Used on the contact page. Key implementation details:
**Form handler:** POST to `https://api.web3forms.com/submit` with an access key. The access key is configured in Web3Forms dashboard (free tier available). Form data is emailed directly to the address configured there.
**Client-side validation pattern:**
```js
function validate() {
var valid = true;
function showErr(name) {
document.getElementById('field-' + name).classList.add('error');
document.getElementById('error-' + name).classList.add('visible');
}
if (!nameField.value.trim()) { showErr('name'); valid = false; }
if (!phoneField.value.trim()) { showErr('phone'); valid = false; }
if (!detailsField.value.trim()) { showErr('details'); valid = false; }
if (!serviceSelect.value) { showErr('service'); valid = false; }
return valid;
}
```
**Error clearing on input:** Each input gets an `input` event listener that removes `.error` from itself and `.visible` from its error span — gives instant feedback as the user types.
**Loading state:** Button gets `disabled` + `.loading` class. CSS swaps button text for a CSS-only spinner.
**Success state:** Form `display: none`, success div gets `.visible`. Message includes the customer's first name (extracted from the name field) and a prominent "if this is an emergency, call now" note with phone number.
**Error state:** Red banner at top of form card with "Something went wrong" message. Button re-enables for retry.
**CORS note:** Fetch to Web3Forms will be blocked on `file://` origins (browser security). Works correctly on any real domain. Test form behavior locally by manually toggling success/error state classes.
### Mobile-Specific Decisions
- Sticky tap-to-call bar below header on all pages (hidden on desktop)
- Both the nav bar AND callbar remain sticky — combined ~100px height, acceptable tradeoff
- Buttons full-width on mobile
- Phone numbers always `<a href="tel:...">`
- Touch targets minimum 44px
### Implementation Phases
1. Global shell (header, footer, CSS, JS, mobile nav, callbar)
2. Emergency HVAC landing page
3. Contact/quote page
4. Homepage (all 7 sections)
5. HVAC service page
6. Handyman service page
7. Polish (schema, meta, images, performance)
@@ -0,0 +1,81 @@
# Website Blueprint Template
Use this format when designing a website before building. Write the blueprint first, then build from it.
---
## Business Context
- **Client:** [name]
- **Business:** [type — HVAC, handyman, plumbing, etc.]
- **USPs:** [2-4 unique selling propositions — 24/7, family-owned, flat-rate, fast response]
- **Service Areas:** [city list]
## URL Structure
```
/ → Homepage
/service-a → Service page A
/service-a/landing → Standalone landing page (for ads)
/service-b → Service page B
/contact → Contact / Quote Request
```
## Technology
- Static HTML/CSS/JS — no backend
- Form submissions via Web3Forms (free tier)
- No frameworks, no build step
- System font stack, CSS custom properties
---
## Per-Page Blueprint
For each page, list sections top-to-bottom with:
- Section name
- Visual elements (icons, photos, layout)
- Copy and headlines (with SEO keywords)
- CTAs (buttons, phone links)
- Psychology notes (what the visitor feels, what they need to see)
### Global Header
```
Layout: Logo | Nav links | Phone | CTA button
Mobile: Logo | Hamburger + sticky tap-to-call bar below
```
### Global Footer
```
Layout: Brand | Service links | Contact | Service areas
SEO: All service area cities in footer on every page
```
### Page: [Name]
#### Section 1: Hero
- Headline: [H1 with primary keyword + city]
- Subcopy: [1-2 lines]
- CTAs: [primary CTA, secondary CTA]
- Psychology: [what the visitor is thinking]
#### Section 2: [Name]
- ...
[Repeat for all sections]
---
## Color Palette
| Role | Color | Usage |
|------|-------|-------|
| Primary | hex | primary CTAs, brand elements |
| Secondary | hex | secondary CTAs |
| Emergency | hex | urgency CTAs if applicable |
| Dark | hex | body text, dark sections |
| Light | hex | page background |
## Implementation Priority
1. Global shell (header, footer, CSS, JS)
2. [Highest-conversion page]
3. [Contact form]
4. [Homepage]
5. [Remaining service pages]
6. Polish (schema, meta, images)
@@ -0,0 +1,135 @@
# Contact Form Pattern (Web3Forms)
Static-site contact form that emails the business owner directly. No backend, no calendar, no payment.
## Form Fields
Keep it SHORT. Every extra field costs conversions.
| Field | Required | Type | Notes |
|-------|----------|------|-------|
| Name | Yes | text | `autocomplete="name"` |
| Phone | Yes | tel | THE most important field for phone-first businesses |
| Email | No | email | Optional — don't require it, it adds friction |
| Service Needed | Yes | select | Dropdown with all service types + "Other" |
| Project Details | Yes | textarea | 3-line minimum, gives context before callback |
| Lead Source | No | select | Google/Facebook/Neighbor/Other — helps measure marketing |
**Deliberately missing:** address, calendar, file upload, CAPTCHA (add only if spam), budget field.
## HTML Structure
```html
<div class="form-card" id="quote-form-wrapper">
<div class="form-error" id="form-error" role="alert">
Something went wrong. Please try again or call us directly.
</div>
<form id="quote-form" novalidate>
<!-- fields with .form-group > .form-label + .form-input/.form-select/.form-textarea + .field-error -->
<button type="submit" class="btn submit-btn">
<span class="btn-text">Get My Free Quote</span>
<span class="spinner"></span>
</button>
</form>
<!-- Success state (hidden by default) -->
<div class="form-success" id="form-success">
<div class="check-icon"></div>
<h3>Thanks, <span id="success-name">friend</span>!</h3>
<p>We'll reach out within 2 hours.</p>
<div class="emergency-note">
<strong>🚨 Emergency?</strong> Call now: <a href="tel:...">(XXX) XXX-XXXX</a>
</div>
</div>
</div>
```
## CSS States
```css
.form-input.error, .form-select.error, .form-textarea.error {
border-color: var(--color-emergency);
box-shadow: 0 0 0 3px rgba(220,38,38,0.1);
}
.field-error { display: none; }
.field-error.visible { display: block; color: var(--color-emergency); }
.form-error { display: none; background: var(--color-emergency-light); }
.form-error.visible { display: block; }
.form-success { display: none; }
.form-success.visible { display: block; }
.submit-btn.loading .spinner { display: inline-block; }
.submit-btn.loading .btn-text { display: none; }
```
## JavaScript
```javascript
var WEB3FORMS_KEY = 'YOUR_ACCESS_KEY'; // Replace with real key
var WEB3FORMS_URL = 'https://api.web3forms.com/submit';
// Validation
function validate() {
var valid = true;
if (!nameField.value.trim()) { showErr('name'); valid = false; }
if (!phoneField.value.trim()) { showErr('phone'); valid = false; }
if (!detailsField.value.trim()) { showErr('details'); valid = false; }
if (!serviceField.value) { showErr('service'); valid = false; }
return valid;
}
// Clear errors on input
inputs.forEach(function(el) {
el.addEventListener('input', function() {
el.classList.remove('error');
getErrorEl(el.name).classList.remove('visible');
});
});
// Submit
form.addEventListener('submit', function(e) {
e.preventDefault();
if (!validate()) return;
submitBtn.disabled = true;
submitBtn.classList.add('loading');
var payload = new FormData(form);
payload.append('access_key', WEB3FORMS_KEY);
payload.append('subject', 'New Quote Request — Business Name');
payload.append('from_name', 'Business Website');
fetch(WEB3FORMS_URL, { method: 'POST', body: payload })
.then(function(res) { return res.json(); })
.then(function(data) {
if (data.success) {
document.getElementById('success-name').textContent =
nameField.value.trim().split(' ')[0] || 'friend';
form.style.display = 'none';
successDiv.classList.add('visible');
} else {
throw new Error(data.message || 'Submission failed');
}
})
.catch(function(err) {
submitBtn.disabled = false;
submitBtn.classList.remove('loading');
errorBanner.classList.add('visible');
console.error('Form error:', err);
});
});
```
## Setup
1. Go to https://web3forms.com/ — create free account
2. Copy access key
3. Replace `YOUR_ACCESS_KEY` in the form script
4. Configure destination email in Web3Forms dashboard
## Testing
- `file://` CORS blocks fetch() — expected. Test from a real web server.
- Click submit with empty fields → validation errors on all required fields
- Fill all required fields, submit → loading spinner → success card (if key valid) or error banner (if invalid key)
- Success card shows customer's first name + emergency phone fallback