/* ============ QUOTE FORM + MODAL ============ */

/* ---- Address autocomplete ----
   Uses Google Places if window.GOOGLE_MAPS_API_KEY is set,
   otherwise falls back to Photon (free OpenStreetMap-based geocoder, no key needed).
   Both are biased to LA + OC area. */
const LA_OC_BIAS = { lat: 34.05, lon: -118.05 };

function loadGooglePlaces() {
  if (typeof window === 'undefined') return Promise.resolve(null);
  if (window.google && window.google.maps && window.google.maps.places) return Promise.resolve(window.google);
  if (window.__googlePlacesLoading) return window.__googlePlacesLoading;
  const key = window.GOOGLE_MAPS_API_KEY;
  if (!key) return Promise.resolve(null);
  window.__googlePlacesLoading = new Promise((resolve) => {
    const s = document.createElement('script');
    s.src = `https://maps.googleapis.com/maps/api/js?key=${encodeURIComponent(key)}&libraries=places&v=weekly&loading=async`;
    s.async = true; s.defer = true;
    s.onload = () => resolve(window.google || null);
    s.onerror = () => resolve(null);
    document.head.appendChild(s);
  });
  return window.__googlePlacesLoading;
}

// Tiny debounce
function useDebounced(value, delay) {
  const [d, setD] = useState(value);
  useEffect(() => {
    const t = setTimeout(() => setD(value), delay);
    return () => clearTimeout(t);
  }, [value, delay]);
  return d;
}

function PlacesAutocompleteInput({ id, name, placeholder, value, onChange }) {
  const inputRef = useRef(null);
  const wrapRef = useRef(null);
  const googleAcRef = useRef(null);
  const [usingGoogle, setUsingGoogle] = useState(false);
  const [suggestions, setSuggestions] = useState([]);
  const [open, setOpen] = useState(false);
  const [highlight, setHighlight] = useState(-1);
  const [loading, setLoading] = useState(false);
  const debouncedQuery = useDebounced(value, 200);

  // --- Google Places path (if API key set) ---
  useEffect(() => {
    let listener;
    loadGooglePlaces().then((g) => {
      if (!g || !inputRef.current) return;
      googleAcRef.current = new g.maps.places.Autocomplete(inputRef.current, {
        componentRestrictions: { country: 'us' },
        bounds: new g.maps.LatLngBounds(
          new g.maps.LatLng(33.45, -118.95),
          new g.maps.LatLng(34.45, -117.55)
        ),
        fields: ['formatted_address', 'name'],
        types: ['address']
      });
      listener = googleAcRef.current.addListener('place_changed', () => {
        const p = googleAcRef.current.getPlace();
        const addr = p.formatted_address || p.name || '';
        if (addr) onChange(addr);
      });
      setUsingGoogle(true);
    });
    return () => {
      if (listener && window.google) window.google.maps.event.removeListener(listener);
      document.querySelectorAll('.pac-container').forEach(el => el.remove());
    };
  }, []);

  // --- Photon fallback (no key needed) ---
  useEffect(() => {
    if (usingGoogle) return;                            // Google handles its own dropdown
    const q = (debouncedQuery || '').trim();
    if (q.length < 3) { setSuggestions([]); return; }
    let cancelled = false;
    setLoading(true);
    const url = `https://photon.komoot.io/api/?q=${encodeURIComponent(q)}` +
                `&lat=${LA_OC_BIAS.lat}&lon=${LA_OC_BIAS.lon}&limit=6&lang=en`;
    fetch(url)
      .then(r => r.ok ? r.json() : null)
      .then(data => {
        if (cancelled || !data) return;
        const feats = (data.features || [])
          .filter(f => {
            const p = f.properties || {};
            // Prefer US results, ideally California
            return p.country === 'United States' || p.countrycode === 'US' || !p.country;
          })
          .map(f => {
            const p = f.properties || {};
            const street = [p.housenumber, p.street].filter(Boolean).join(' ');
            const cityState = [p.city || p.town || p.village || p.county, p.state, p.postcode].filter(Boolean).join(', ');
            const display = [p.name && p.name !== p.street ? p.name : null, street, cityState].filter(Boolean).join(', ').replace(/, ,/g, ',');
            return { display: display || p.name || '', sub: p.country || '' };
          })
          .filter(s => s.display);
        setSuggestions(feats);
        setOpen(feats.length > 0);
        setLoading(false);
      })
      .catch(() => { if (!cancelled) { setLoading(false); setSuggestions([]); } });
    return () => { cancelled = true; };
  }, [debouncedQuery, usingGoogle]);

  // Click-outside to close
  useEffect(() => {
    if (usingGoogle) return;
    const onDoc = (e) => {
      if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
    };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [usingGoogle]);

  const choose = (s) => {
    onChange(s.display);
    setOpen(false);
    setSuggestions([]);
    setHighlight(-1);
  };

  const onKeyDown = (e) => {
    if (usingGoogle || !open || !suggestions.length) return;
    if (e.key === 'ArrowDown') { e.preventDefault(); setHighlight(h => Math.min(h + 1, suggestions.length - 1)); }
    else if (e.key === 'ArrowUp') { e.preventDefault(); setHighlight(h => Math.max(h - 1, 0)); }
    else if (e.key === 'Enter' && highlight >= 0) { e.preventDefault(); choose(suggestions[highlight]); }
    else if (e.key === 'Escape') { setOpen(false); }
  };

  return (
    <div className="addr-ac" ref={wrapRef}>
      <input
        ref={inputRef}
        id={id} name={name} type="text" autoComplete="street-address"
        placeholder={placeholder}
        className="q-input q-input-lg"
        value={value}
        onChange={(e) => onChange(e.target.value)}
        onFocus={() => suggestions.length && setOpen(true)}
        onKeyDown={onKeyDown}
        role="combobox"
        aria-expanded={open}
        aria-autocomplete="list"
        aria-controls={`${id}-listbox`}
      />
      {!usingGoogle && open && (
        <ul id={`${id}-listbox`} className="addr-ac-list" role="listbox">
          {loading && <li className="addr-ac-loading">Searching…</li>}
          {!loading && suggestions.map((s, i) => (
            <li
              key={i}
              role="option"
              aria-selected={highlight === i}
              className={`addr-ac-item ${highlight === i ? 'on' : ''}`}
              onMouseDown={(e) => { e.preventDefault(); choose(s); }}
              onMouseEnter={() => setHighlight(i)}
            >
              <svg viewBox="0 0 20 20" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.6" className="addr-ac-pin" aria-hidden="true">
                <path d="M10 2c3 0 5.5 2.4 5.5 5.5 0 4-5.5 10.5-5.5 10.5S4.5 11.5 4.5 7.5C4.5 4.4 7 2 10 2z"/>
                <circle cx="10" cy="7.5" r="2"/>
              </svg>
              <span className="addr-ac-text">{s.display}</span>
            </li>
          ))}
          {!loading && !suggestions.length && (
            <li className="addr-ac-empty">No matches — keep typing or just enter the address manually.</li>
          )}
        </ul>
      )}
    </div>
  );
}

/* ---- Square footage slider with synced numeric input ----
   Range chosen to cover ~95% of commercial buildings. Sub-3000 is rare; >150k
   is mostly warehousing — both still typeable, the slider just stops at the edges. */
function SqftSlider({ value, onChange }) {
  const MIN = 1000, MAX = 200000, STEP = 500;
  const numeric = parseInt(String(value).replace(/[^0-9]/g, ''), 10) || 0;
  const sliderVal = Math.max(MIN, Math.min(MAX, numeric || MIN));
  const fmt = (n) => n ? n.toLocaleString('en-US') : '';
  const fillPct = ((sliderVal - MIN) / (MAX - MIN)) * 100;
  return (
    <div className="quote-slider">
      <div className="quote-slider-row">
        <input
          type="range"
          id="q-sqft"
          min={MIN} max={MAX} step={STEP}
          value={sliderVal}
          onChange={(e) => onChange(fmt(parseInt(e.target.value, 10)))}
          aria-label="Square footage slider"
          className="quote-slider-range"
          style={{'--fill': fillPct + '%'}}
        />
        <div className="quote-slider-input">
          <input
            type="text"
            inputMode="numeric"
            value={fmt(numeric)}
            onChange={(e) => {
              const n = parseInt(e.target.value.replace(/[^0-9]/g, ''), 10);
              onChange(isNaN(n) ? '' : fmt(n));
            }}
            placeholder="25,000"
            aria-label="Square footage"
          />
          <span className="quote-slider-unit">sq ft</span>
        </div>
      </div>
      <div className="quote-slider-scale">
        <span>1k</span><span>25k</span><span>50k</span><span>100k</span><span>200k+</span>
      </div>
    </div>
  );
}

/* ---- Frequency slider (1–7 days/week) ---- */
function FrequencySlider({ value, onChange }) {
  const MIN = 1, MAX = 7;
  // Parse "5x per week" → 5
  const m = (value || '').match(/^(\d)/);
  const current = m ? parseInt(m[1], 10) : 5;
  const fillPct = ((current - MIN) / (MAX - MIN)) * 100;
  const labels = {1:'Once', 2:'Twice', 3:'3x', 4:'4x', 5:'5x', 6:'6x', 7:'Daily'};
  const subs   = {1:'a week', 2:'a week', 3:'Mon · Wed · Fri', 4:'4 days/week', 5:'Standard, Mon–Fri', 6:'Mon–Sat', 7:'Every day'};
  const onSlide = (n) => onChange(`${n}x per week`);
  return (
    <div className="quote-slider">
      <div className="quote-slider-row">
        <input
          type="range"
          id="q-freq"
          min={MIN} max={MAX} step={1}
          value={current}
          onChange={(e) => onSlide(parseInt(e.target.value, 10))}
          aria-label="Cleaning frequency per week"
          className="quote-slider-range"
          style={{'--fill': fillPct + '%'}}
        />
        <div className="quote-slider-pill">
          <strong>{labels[current]}</strong>
          <span>{subs[current]}</span>
        </div>
      </div>
      <div className="quote-slider-scale quote-slider-scale-7">
        {[1,2,3,4,5,6,7].map(n => <span key={n}>{n}</span>)}
      </div>
    </div>
  );
}

/* ---- Building-type icons (small, inline SVG) ---- */
const BLDG_ICON = {
  'Office': (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"><rect x="4" y="3" width="16" height="18"/><path d="M8 7h2M14 7h2M8 11h2M14 11h2M8 15h2M14 15h2"/></svg>),
  'Medical / healthcare': (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"><rect x="3" y="3" width="18" height="18"/><path d="M12 8v8M8 12h8"/></svg>),
  'Retail / shopping center': (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M3 9l1.5-5h15L21 9M3 9v11h18V9M3 9h18M9 13h6"/></svg>),
  'Warehouse / industrial': (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M3 21V9l9-5 9 5v12M3 21h18M9 21v-7h6v7"/></svg>),
  'School / daycare': (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M2 8l10-4 10 4-10 4L2 8zM6 10v5c0 2 3 3 6 3s6-1 6-3v-5"/></svg>),
  'Bank / financial': (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M3 9l9-6 9 6v2H3V9zM5 11v8M9 11v8M15 11v8M19 11v8M3 21h18"/></svg>),
  'Gym / fitness': (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M4 9v6M8 7v10M12 9v6M16 7v10M20 9v6M2 12h2M20 12h2"/></svg>),
  'Mixed-use': (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"><rect x="3" y="9" width="8" height="12"/><rect x="13" y="3" width="8" height="18"/><path d="M5 13h2M5 17h2M15 7h2M15 11h2M15 15h2"/></svg>),
  'Other': (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"><circle cx="12" cy="12" r="9"/><path d="M9 9c0-1.5 1.3-3 3-3s3 1.5 3 3c0 1.5-1 2-2 2.5s-1 1-1 2M12 17h0"/></svg>),
};

/* ---- Submission endpoint ----
   Set window.QUOTE_FORM_ENDPOINT to a Formspree/Web3Forms URL to receive submissions.
   Without it, falls back to opening the user's email client (mailto:). */
const QUOTE_RECIPIENT = 'info@californiajanitor.com';

/* ---- The form itself, reusable in modal or page ---- */
function QuoteForm({ onSubmitted, compact = false }) {
  const [step, setStep] = useState(1);
  const TOTAL = 3;
  const [submitted, setSubmitted] = useState(false);
  const [submitError, setSubmitError] = useState('');
  const [submitting, setSubmitting] = useState(false);
  const [form, setForm] = useState({
    name: '', company: '', email: '', phone: '',
    address: '', sqft: '', buildingType: '',
    frequency: '', schedule: '', startTiming: '',
    services: [], notes: ''
  });

  const BLDG = ['Office', 'Medical / healthcare', 'Retail / shopping center', 'Warehouse / industrial', 'School / daycare', 'Bank / financial', 'Gym / fitness', 'Mixed-use', 'Other'];
  // Schedule (when crews come) — janitorial is included by default; this is the timing.
  const SCHEDULE = [
    { v: 'Nightly (after-hours)', sub: 'Most common — crews after closing' },
    { v: 'Day porter (business hours)', sub: 'On-site during the day' },
    { v: 'Weekends only', sub: 'Sat/Sun cleanings' },
    { v: 'One-time / project', sub: 'Move-in, event, post-construction' },
  ];
  const FREQ = [
    { v: '3x per week', sub: 'Mon · Wed · Fri' },
    { v: '5x per week', sub: 'Standard, Mon–Fri' },
    { v: '6x per week', sub: 'Mon–Sat' },
    { v: '7x per week', sub: 'Daily' },
  ];
  const TIMING = [
    { v: 'ASAP / emergency', sub: 'Within 72 hours' },
    { v: 'Within 30 days', sub: 'Most common' },
    { v: 'Next quarter', sub: 'Planning ahead' },
    { v: 'Just gathering info', sub: 'No timeline yet' },
  ];

  // Optional add-on services — janitorial is always included, these are extras.
  const SERVICES = [
    { v: 'Floor care (strip & wax)', sub: 'VCT, polished concrete, vinyl' },
    { v: 'Carpet cleaning', sub: 'Hot-water extraction' },
    { v: 'Window cleaning', sub: 'Interior + exterior' },
    { v: 'Pressure washing', sub: 'Sidewalks, façade, lots' },
    { v: 'Post-construction cleanup', sub: 'Punch-list, final clean' },
    { v: 'Green / LEED protocol', sub: 'Certified products' },
    { v: 'Restroom deep clean', sub: 'Periodic disinfection' },
    { v: 'Handyman / light maintenance', sub: 'Lightbulbs, touch-ups' },
  ];

  const toggleService = (s) => setForm(f => ({
    ...f,
    services: f.services.includes(s) ? f.services.filter(x => x !== s) : [...f.services, s]
  }));

  const update = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const successRef = useRef(null);
  useEffect(() => {
    if (submitted) {
      if (successRef.current) successRef.current.focus();
      if (onSubmitted) onSubmitted();
    }
  }, [submitted]);

  // Move scroll to top of form when step changes
  const formTopRef = useRef(null);
  useEffect(() => {
    if (formTopRef.current) {
      const scroller = formTopRef.current.closest('.quote-modal-body, .quote-page-body') || window;
      try {
        if (scroller === window) {
          window.scrollTo({ top: 0, behavior: 'smooth' });
        } else {
          scroller.scrollTo({ top: 0, behavior: 'smooth' });
        }
      } catch (e) {}
    }
  }, [step]);

  // Validation — only contact info is required.
  const isEmailValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email.trim());
  const canContinue = (() => {
    if (step === 1) return true;  // all optional
    if (step === 2) return true;  // all optional — janitorial is included by default
    // Step 3: name + valid email required (phone optional)
    return form.name.trim().length >= 2 && isEmailValid;
  })();

  if (submitted) {
    return (
      <div
        ref={successRef}
        tabIndex={-1}
        role="status"
        aria-live="polite"
        className="quote-success"
      >
        <div className="quote-success-check" aria-hidden="true">
          <svg viewBox="0 0 24 24" width="36" height="36" fill="none" stroke="currentColor" strokeWidth="2">
            <path d="M5 12.5l4.5 4.5L19 7.5" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
        </div>
        <div className="eyebrow" style={{marginTop: 24}}>Received</div>
        <h2 style={{marginTop: 12, fontSize: compact ? 30 : 38}}>Thanks — we'll be in touch.</h2>
        <p style={{marginTop: 16, maxWidth: 480, marginInline: 'auto'}}>A member of the team will reply within two business hours with a walkthrough time and a scope confirmation. If it's urgent, call <a href="tel:3232647800">(323) 264-7800</a>.</p>
        <div className="quote-success-summary">
          <div><span>Contact</span><strong>{form.name}{form.company ? ` · ${form.company}` : ''} · {form.email}</strong></div>
          {form.address && <div><span>Address</span><strong>{form.address}</strong></div>}
          {(form.sqft || form.buildingType) && <div><span>Building</span><strong>{[form.sqft && `${form.sqft} sqft`, form.buildingType].filter(Boolean).join(' · ')}</strong></div>}
          <div><span>Service</span><strong>Janitorial included{form.services.length ? `, plus: ${form.services.join(', ')}` : ''}</strong></div>
          {(form.schedule || form.frequency) && <div><span>Schedule</span><strong>{[form.schedule, form.frequency].filter(Boolean).join(' · ')}</strong></div>}
          {form.startTiming && <div><span>Start</span><strong>{form.startTiming}</strong></div>}
        </div>
      </div>
    );
  }

  const buildSummary = () => {
    const lines = [];
    lines.push('NEW QUOTE REQUEST');
    lines.push('');
    lines.push('Name: ' + form.name);
    if (form.company) lines.push('Company: ' + form.company);
    lines.push('Email: ' + form.email);
    if (form.phone) lines.push('Phone: ' + form.phone);
    lines.push('');
    lines.push('Building:');
    if (form.address) lines.push('  Address: ' + form.address);
    if (form.sqft) lines.push('  Square footage: ' + form.sqft);
    if (form.buildingType) lines.push('  Type: ' + form.buildingType);
    lines.push('');
    lines.push('Cleaning:');
    lines.push('  Janitorial: included (default)');
    if (form.schedule) lines.push('  Schedule: ' + form.schedule);
    if (form.frequency) lines.push('  Frequency: ' + form.frequency);
    if (form.services.length) lines.push('  Add-ons: ' + form.services.join(', '));
    if (form.startTiming) lines.push('  Start timing: ' + form.startTiming);
    if (form.notes) {
      lines.push('');
      lines.push('Notes:');
      lines.push(form.notes);
    }
    return lines.join('\n');
  };

  const submitForm = async () => {
    setSubmitting(true);
    setSubmitError('');
    const endpoint = window.QUOTE_FORM_ENDPOINT;
    const payload = { ...form, services: form.services.join(', '), _subject: `New quote request from ${form.name}` };
    let posted = false;
    if (endpoint) {
      try {
        const res = await fetch(endpoint, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
          body: JSON.stringify(payload)
        });
        posted = res.ok;
      } catch (err) { posted = false; }
    }
    if (!posted) {
      // Fallback: open user's email client with prefilled message.
      const subject = encodeURIComponent('Quote request: ' + (form.company || form.name));
      const body = encodeURIComponent(buildSummary());
      try {
        window.location.href = `mailto:${QUOTE_RECIPIENT}?subject=${subject}&body=${body}`;
      } catch (e) {}
    }
    setSubmitting(false);
    setSubmitted(true);
  };

  const handleNext = (e) => {
    if (e) e.preventDefault();
    if (!canContinue) return;
    if (step < TOTAL) setStep(step + 1);
    else submitForm();
  };
  const handleBack = () => { if (step > 1) setStep(step - 1); };

  return (
    <form
      onSubmit={handleNext}
      aria-labelledby="quote-form-title"
      className="quote-wizard"
      ref={formTopRef}
    >
      <h2 id="quote-form-title" className="visually-hidden">Quote request — step {step} of {TOTAL}</h2>

      {/* ===== Stepper ===== */}
      <ol className="quote-stepper" aria-label="Progress">
        {[
          { n: 1, label: 'Your building' },
          { n: 2, label: 'What you need' },
          { n: 3, label: 'How to reach you' },
        ].map(s => {
          const state = s.n < step ? 'done' : s.n === step ? 'current' : 'pending';
          return (
            <li key={s.n} className={`quote-step quote-step-${state}`} aria-current={state === 'current' ? 'step' : undefined}>
              <button
                type="button"
                className="quote-step-btn"
                onClick={() => { if (s.n < step) setStep(s.n); }}
                disabled={s.n > step}
                aria-label={`Step ${s.n}: ${s.label}${state === 'done' ? ' (completed)' : ''}`}
              >
                <span className="quote-step-bubble">
                  {state === 'done' ? (
                    <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2">
                      <path d="M3 8.5l3 3 7-7" strokeLinecap="round" strokeLinejoin="round"/>
                    </svg>
                  ) : s.n}
                </span>
                <span className="quote-step-label">
                  <span className="quote-step-num">Step {s.n}</span>
                  <span className="quote-step-name">{s.label}</span>
                </span>
              </button>
            </li>
          );
        })}
      </ol>

      <div className="quote-step-panels">

        {/* ===== STEP 1: BUILDING (all optional) ===== */}
        {step === 1 && (
          <div className="quote-step-panel" key="step-1">
            <div className="quote-step-head">
              <div className="eyebrow">Step 1 of 3 · Optional</div>
              <h3 className="quote-step-h">Your building</h3>
              <p className="quote-step-sub">A few details help us tailor the quote — but skip anything you don't have handy.</p>
            </div>

            <div className="field">
              <label htmlFor="q-address" className="q-label">Building address <span className="q-optional">optional</span></label>
              <PlacesAutocompleteInput
                id="q-address"
                name="address"
                placeholder="Start typing your address — Los Angeles, CA…"
                value={form.address}
                onChange={(v) => update('address', v)}
              />
              <p className="q-help">Pick from the list as you type.</p>
            </div>

            <div className="field">
              <label htmlFor="q-sqft" className="q-label">
                Approximate square footage <span className="q-optional">optional</span>
              </label>
              <SqftSlider value={form.sqft} onChange={(v) => update('sqft', v)}/>
              <p className="q-help">Drag the slider or type a number — rough estimate is fine.</p>
            </div>

            <div className="field">
              <span className="q-label">Building type <span className="q-optional">optional</span></span>
              <div className="quote-tile-grid quote-tile-grid-bldg" role="radiogroup" aria-label="Building type">
                {BLDG.map(b => (
                  <label key={b}
                    className={`quote-tile ${form.buildingType === b ? 'on' : ''}`}>
                    <input
                      type="radio"
                      name="buildingType"
                      value={b}
                      checked={form.buildingType === b}
                      onChange={() => update('buildingType', b)}
                      className="quote-tile-input"
                    />
                    <span className="quote-tile-icon" aria-hidden="true">{BLDG_ICON[b] || BLDG_ICON['Other']}</span>
                    <span className="quote-tile-title">{b}</span>
                  </label>
                ))}
              </div>
            </div>
          </div>
        )}

        {/* ===== STEP 2: SCHEDULE + ADD-ONS (all optional) ===== */}
        {step === 2 && (
          <div className="quote-step-panel" key="step-2">
            <div className="quote-step-head">
              <div className="eyebrow">Step 2 of 3 · Optional</div>
              <h3 className="quote-step-h">When &amp; what to clean</h3>
              <p className="quote-step-sub">Janitorial cleaning is included — these details help us tailor the quote. Skip anything you're not sure about.</p>
            </div>

            <div className="quote-included-banner" aria-hidden="true">
              <span className="quote-included-icon">
                <svg viewBox="0 0 20 20" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2">
                  <path d="M5 10.5l3 3 7-7" strokeLinecap="round" strokeLinejoin="round"/>
                </svg>
              </span>
              <span><strong>Janitorial cleaning included</strong> — trash, restrooms, vacuuming, dust, surfaces, kitchen.</span>
            </div>

            <div className="field">
              <span className="q-label">When should we clean? <span className="q-optional">optional</span></span>
              <div className="quote-pill-grid quote-pill-grid-2" role="radiogroup" aria-label="Schedule">
                {SCHEDULE.map(s => (
                  <label key={s.v}
                    className={`quote-pill ${form.schedule === s.v ? 'on' : ''}`}>
                    <input
                      type="radio"
                      name="schedule"
                      value={s.v}
                      checked={form.schedule === s.v}
                      onChange={() => update('schedule', s.v)}
                      className="quote-tile-input"
                    />
                    <span className="quote-pill-title">{s.v}</span>
                    <span className="quote-pill-sub">{s.sub}</span>
                  </label>
                ))}
              </div>
            </div>

            <div className="field">
              <label htmlFor="q-freq" className="q-label">
                How often? <span className="q-optional">optional</span>
              </label>
              <FrequencySlider value={form.frequency} onChange={(v) => update('frequency', v)}/>
            </div>

            <div className="field">
              <span className="q-label">Add specialty services <span className="q-optional">optional · pick any</span></span>
              <div className="quote-tile-grid quote-tile-grid-svc" role="group" aria-label="Add-on services">
                {SERVICES.map(s => {
                  const id = 'svc-' + s.v.replace(/[^a-z0-9]+/gi, '-').toLowerCase();
                  const checked = form.services.includes(s.v);
                  return (
                    <label key={s.v} htmlFor={id}
                      className={`quote-tile quote-tile-multi ${checked ? 'on' : ''}`}>
                      <input
                        id={id}
                        type="checkbox"
                        checked={checked}
                        onChange={() => toggleService(s.v)}
                        className="quote-tile-input"
                      />
                      <span className="quote-tile-check" aria-hidden="true">
                        {checked && (
                          <svg viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2.5">
                            <path d="M3 8.5l3 3 7-7" strokeLinecap="round" strokeLinejoin="round"/>
                          </svg>
                        )}
                      </span>
                      <span className="quote-tile-body">
                        <span className="quote-tile-title">{s.v}</span>
                        <span className="quote-tile-sub">{s.sub}</span>
                      </span>
                    </label>
                  );
                })}
              </div>
            </div>

            <div className="field">
              <span className="q-label">When would you want to start? <span className="q-optional">optional</span></span>
              <div className="quote-pill-grid quote-pill-grid-2" role="radiogroup" aria-label="Start timing">
                {TIMING.map(t => (
                  <label key={t.v}
                    className={`quote-pill ${form.startTiming === t.v ? 'on' : ''}`}>
                    <input
                      type="radio"
                      name="startTiming"
                      value={t.v}
                      checked={form.startTiming === t.v}
                      onChange={() => update('startTiming', t.v)}
                      className="quote-tile-input"
                    />
                    <span className="quote-pill-title">{t.v}</span>
                    <span className="quote-pill-sub">{t.sub}</span>
                  </label>
                ))}
              </div>
            </div>
          </div>
        )}

        {/* ===== STEP 3: CONTACT ===== */}
        {step === 3 && (
          <div className="quote-step-panel" key="step-3">
            <div className="quote-step-head">
              <div className="eyebrow">Step 3 of 3 · Almost done</div>
              <h3 className="quote-step-h">How to reach you</h3>
              <p className="quote-step-sub">A real person replies within two business hours. No auto-responders.</p>
            </div>

            <div className="quote-row-2">
              <div className="field">
                <label htmlFor="q-name" className="q-label">Your name <span className="req" aria-hidden="true">*</span></label>
                <input id="q-name" name="name" type="text" required autoComplete="name"
                  placeholder="Jane Smith"
                  className="q-input q-input-lg" value={form.name} onChange={e => update('name', e.target.value)}/>
              </div>
              <div className="field">
                <label htmlFor="q-email" className="q-label">Email <span className="req" aria-hidden="true">*</span></label>
                <input id="q-email" name="email" type="email" required autoComplete="email"
                  placeholder="you@company.com"
                  aria-describedby="q-email-help"
                  aria-invalid={form.email.length > 0 && !isEmailValid}
                  className="q-input q-input-lg" value={form.email} onChange={e => update('email', e.target.value)}/>
                <p id="q-email-help" className="q-help">{form.email.length > 0 && !isEmailValid ? 'Please enter a valid email.' : "We'll send your proposal here."}</p>
              </div>
              <div className="field">
                <label htmlFor="q-phone" className="q-label">Phone <span className="q-optional">optional</span></label>
                <input id="q-phone" name="phone" type="tel" autoComplete="tel"
                  placeholder="(323) 555-0100"
                  className="q-input q-input-lg" value={form.phone} onChange={e => update('phone', e.target.value)}/>
              </div>
              <div className="field">
                <label htmlFor="q-company" className="q-label">Company / building name <span className="q-optional">optional</span></label>
                <input id="q-company" name="company" type="text" autoComplete="organization"
                  placeholder="Acme Properties"
                  className="q-input q-input-lg" value={form.company} onChange={e => update('company', e.target.value)}/>
              </div>
            </div>

            <div className="field">
              <label htmlFor="q-notes" className="q-label">Anything else we should know? <span className="q-optional">optional</span></label>
              <p id="q-notes-help" className="q-help" style={{marginTop: 0, marginBottom: 8}}>
                Specialty needs, existing vendor, compliance requirements, gate codes, etc.
              </p>
              <textarea id="q-notes" name="notes" className="q-input" rows={3}
                placeholder="e.g. green-only products, after 10pm only, security gate code 1234…"
                aria-describedby="q-notes-help"
                value={form.notes} onChange={e => update('notes', e.target.value)}/>
            </div>

            {/* Inline review of what they'll send */}
            <div className="quote-review">
              <div className="quote-review-head">Here's what we'll send</div>
              <dl className="quote-review-list">
                <div><dt>Building</dt><dd>{form.address || <em>address tbd</em>} · {form.sqft ? `${form.sqft} sqft` : <em>sqft tbd</em>}{form.buildingType ? ` · ${form.buildingType}` : ''}</dd></div>
                <div><dt>Service</dt><dd>Janitorial included{form.services.length ? `, plus: ${form.services.join(', ')}` : ''}</dd></div>
                <div><dt>Schedule</dt><dd>{[form.schedule, form.frequency].filter(Boolean).join(' · ') || <em>tbd</em>}{form.startTiming ? ` · start ${form.startTiming.toLowerCase()}` : ''}</dd></div>
              </dl>
              <button type="button" className="quote-review-edit" onClick={() => setStep(1)}>Edit building</button>
              <button type="button" className="quote-review-edit" onClick={() => setStep(2)}>Edit cleaning</button>
            </div>
          </div>
        )}

      </div>

      {/* ===== Action bar ===== */}
      <div className="quote-actions">
        <div className="quote-actions-left">
          {step > 1 && (
            <button type="button" className="btn btn-ghost btn-lg" onClick={handleBack}>
              <span aria-hidden="true" style={{marginRight: 6}}>←</span> Back
            </button>
          )}
        </div>
        <div className="quote-actions-right">
          {step < TOTAL ? (
            <button
              type="submit"
              className="btn btn-accent btn-xl"
              disabled={!canContinue}
            >
              Continue <span aria-hidden="true" style={{marginLeft: 6}}>→</span>
            </button>
          ) : (
            <button type="submit" className="btn btn-accent btn-xl" disabled={!canContinue || submitting}>
              {submitting ? 'Sending…' : 'Send Quote Request'}
            </button>
          )}
        </div>
      </div>

      {step === TOTAL && !submitError && (
        <p style={{fontSize: 12, color: 'var(--muted)', marginTop: 14, marginBottom: 0, textAlign: 'right'}}>
          A real person replies within two business hours. Required: name + email.
        </p>
      )}
      {submitError && (
        <p role="alert" style={{fontSize: 13, color: '#b91c1c', marginTop: 14, marginBottom: 0}}>
          {submitError}
        </p>
      )}
    </form>
  );
}

/* ---- Modal wrapper with focus trap, ESC, scroll lock ---- */
function QuoteModal({ open, onClose }) {
  const dialogRef = useRef(null);
  const lastFocusRef = useRef(null);
  const titleId = 'quote-modal-title';

  // Save the previously-focused element on open, restore on close.
  useEffect(() => {
    if (open) {
      lastFocusRef.current = document.activeElement;
      // Lock body scroll
      const prev = document.body.style.overflow;
      document.body.style.overflow = 'hidden';
      // Focus the dialog itself (the wizard manages internal focus per step).
      const focusTimer = setTimeout(() => {
        const closeBtn = dialogRef.current?.querySelector('[data-quote-close]');
        (closeBtn || dialogRef.current)?.focus();
      }, 50);
      return () => {
        clearTimeout(focusTimer);
        document.body.style.overflow = prev;
        if (lastFocusRef.current && lastFocusRef.current.focus) {
          try { lastFocusRef.current.focus(); } catch (e) {}
        }
      };
    }
  }, [open]);

  // ESC + focus trap
  useEffect(() => {
    if (!open) return;
    const onKey = (e) => {
      if (e.key === 'Escape') {
        e.stopPropagation();
        onClose();
        return;
      }
      if (e.key === 'Tab' && dialogRef.current) {
        const focusables = [...dialogRef.current.querySelectorAll(
          'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
        )].filter(el => el.offsetParent !== null || el === document.activeElement);
        if (focusables.length === 0) return;
        const first = focusables[0];
        const last = focusables[focusables.length - 1];
        if (e.shiftKey && document.activeElement === first) {
          e.preventDefault();
          last.focus();
        } else if (!e.shiftKey && document.activeElement === last) {
          e.preventDefault();
          first.focus();
        }
      }
    };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [open, onClose]);

  if (!open) return null;

  return (
    <div
      className="quote-modal-backdrop"
      onMouseDown={(e) => {
        // Click on backdrop only (not bubbled from dialog)
        if (e.target === e.currentTarget) onClose();
      }}
    >
      <div
        ref={dialogRef}
        className="quote-modal-dialog"
        role="dialog"
        aria-modal="true"
        aria-labelledby={titleId}
        aria-describedby="quote-modal-desc"
        tabIndex={-1}
      >
        <header className="quote-modal-header">
          <div>
            <div className="label-mono" style={{color: 'var(--accent)', marginBottom: 6}}>REQUEST A QUOTE</div>
            <h2 id={titleId} className="quote-modal-title">Tell us about your building.</h2>
            <p id="quote-modal-desc" className="quote-modal-sub">
              Three quick steps. A real person replies within two business hours.
            </p>
          </div>
          <button
            type="button"
            data-quote-close
            className="quote-modal-close"
            onClick={onClose}
            aria-label="Close quote request"
          >
            <svg width="20" height="20" viewBox="0 0 20 20" aria-hidden="true">
              <path d="M5 5l10 10M15 5L5 15" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/>
            </svg>
          </button>
        </header>

        <div className="quote-modal-body">
          <QuoteForm onSubmitted={() => {/* keep modal open so user reads success */}} compact/>
        </div>

        <div className="quote-modal-footer">
          <span>Or call <a href="tel:3232647800">(323) 264-7800</a> · Mon–Fri 7am–6pm</span>
          <button type="button" className="btn btn-ghost" onClick={onClose}>Close</button>
        </div>
      </div>
    </div>
  );
}

/* ---- Page-level wrapper (route /quote opens modal + minimal landing) ---- */
function QuotePage() {
  // /quote route auto-opens the modal on mount.
  useEffect(() => {
    if (window.__openQuoteModal) {
      window.__openQuoteModal();
    } else {
      // QuoteContext not ready yet — try once on next tick.
      const t = setTimeout(() => window.__openQuoteModal && window.__openQuoteModal(), 50);
      return () => clearTimeout(t);
    }
  }, []);

  return (
    <div>
      <section className="page-header no-border">
        <div className="container">
          <Breadcrumb items={[{ label: 'Home', href: '/' }, { label: 'Get a Quote' }]}/>
          <div className="label-mono" style={{marginBottom: 20}}>REQUEST A QUOTE · RESPONSE WITHIN 2 BUSINESS HOURS</div>
          <h1>Tell us about your building.</h1>
          <p className="lead">Three quick steps. A real person replies within two business hours.</p>
          <div style={{marginTop: 32}}>
            <button
              type="button"
              className="btn btn-accent btn-xl"
              onClick={() => window.__openQuoteModal && window.__openQuoteModal()}
            >
              Open Quote Form
            </button>
            <a href="tel:3232647800" className="btn btn-secondary btn-xl" style={{marginLeft: 10}}>Call (323) 264-7800</a>
          </div>
        </div>
      </section>
      <TrustBar/>
      <FinalCTA/>
    </div>
  );
}

/* ---- Floating "Get a Quote" launcher (visible on every page) ---- */
function QuoteFab({ onClick, hidden }) {
  if (hidden) return null;
  return (
    <button
      type="button"
      className="quote-fab"
      onClick={onClick}
      aria-label="Open quote request"
    >
      <span className="quote-fab-icon" aria-hidden="true">
        <svg width="18" height="18" viewBox="0 0 20 20">
          <path d="M3 5h14M3 10h14M3 15h9" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"/>
        </svg>
      </span>
      <span>Get a Quote</span>
    </button>
  );
}

Object.assign(window, { QuotePage, QuoteForm, QuoteModal, QuoteFab });
