# 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
