// shared.jsx — common data, hooks, and small primitives used by all 3 site variations. const PHONE_DISPLAY = '587-409-6268'; const PHONE_HREF = 'tel:+15874096268'; const EMAIL = 'afarah@farahwindowcleaning.com'; const LOCATION = 'Edmonton, AB'; const SERVICES = [ { key: 'recurring', title: 'Recurring Storefront Service', short: 'Weekly · bi-weekly · monthly', blurb: 'A standing route that keeps your storefront sharp without you ever having to call. Same crew, same day each cycle, before your doors open.', bullets: ['Locked-in pricing, no surprise upcharges', 'Before-hours or after-close slots', 'COI on file for your landlord'], slotId: 'service-recurring', placeholder: 'Photo: crew squeegeeing a retail storefront before opening', src: 'service-recurring-storefront.png', }, { key: 'onetime', title: 'One-Time & Grand Opening Cleans', short: 'Pop-ups, openings, post-construction', blurb: 'Opening a new location, post-renovation, or hosting an event? We do single-visit deep cleans on tight timelines — usually within 48 hours of your call.', bullets: ['48-hour turnaround on most jobs', 'Construction overspray & sticker removal', 'Inside + outside, frames, tracks, sills'], slotId: 'service-onetime', placeholder: 'Photo: crew member doing a one-time grand-opening clean', src: 'service-recurring-storefront.png', }, { key: 'signage', title: 'Signage, Awnings & Frames', short: 'Channel letters, vinyl, fabric awnings', blurb: 'Faded signage and grimy awnings age a storefront faster than the windows do. We bring them back without damaging vinyl, channel letters, or fabric.', bullets: ['Soft-wash safe on vinyl & fabric', 'Channel-letter & light-box cleaning', 'Bundle with your window route'], slotId: 'service-signage', placeholder: 'Photo: brightening up a fabric awning over a Whyte Ave shop', src: 'service-recurring-storefront.png', }, { key: 'interior', title: 'Interior Glass & Partitions', short: 'Office dividers, glass doors, vestibules', blurb: 'Glass dividers, conference-room walls, vestibule doors — all the interior glass your team and customers actually touch every day.', bullets: ['Streak-free interior & exterior', 'After-hours office service', 'Fingerprint + smudge removal'], slotId: 'service-interior', placeholder: 'Photo: crew member detailing interior glass partitions in an office', src: 'service-recurring-storefront.png', }, ]; const DIFFERENTIATORS = [ { icon: 'window', title: 'Storefront-only', note: 'We don’t do houses or high-rises. Storefront glass is what we do all day, every day.' }, { icon: 'calendar', title: 'Before-hours service', note: 'We arrive at 5am, 6am, or after close — your customers never see a squeegee.' }, { icon: 'shield', title: 'Fully insured + WCB', note: '$2M liability and WCB coverage. COIs emailed to your landlord on request.' }, { icon: 'pin', title: 'Edmonton-based', note: 'We live and work here. No franchise, no head office — you talk to the owner.' }, { icon: 'check', title: 'Recurring routes', note: 'Same crew, same day each cycle. Predictable for your team and ours.' }, { icon: 'sparkle', title: 'No-streak guarantee', note: 'See a streak after we leave? We come back same-day, no charge.' }, { icon: 'price', title: 'Locked-in pricing', note: 'Flat per-visit rate on contracts. No surprise upcharges, no per-pane shell game.' }, { icon: 'mail', title: 'Multiple-location billing', note: 'Run a chain? One invoice, one POC, all your locations on one schedule.' }, ]; const FAQS = [ { q: 'How often should I have my storefront windows cleaned?', a: 'For most Edmonton storefronts, every 2–4 weeks is the sweet spot — enough to keep glass and signage looking sharp without overdoing it. High-traffic spots (cafes, restaurants, dispensaries) usually want weekly. We’ll walk the property once and recommend a cadence you can actually budget against.', }, { q: 'Do you work before-hours so we don’t disturb customers?', a: 'Yes — the majority of our route runs between 5am and 9am, before any customer walks past your storefront. We can also book after-close slots for late-evening venues. Whichever doesn’t cost you a single foot of foot traffic.', }, { q: 'Are you insured and WCB-covered? Can we get a COI on file?', a: 'Yes — $2 million liability coverage, WCB on every crew member, and a Certificate of Insurance we’ll send straight to your landlord or property manager so it stays on file before our first visit.', }, { q: 'Do you service multiple locations under one contract?', a: 'Absolutely. We service multi-location retailers, restaurant groups, and dental/medical chains across Greater Edmonton on a single contract — one POC, one invoice, every location on the same recurring schedule.', }, { q: 'How do you price recurring contracts?', a: 'Flat per-visit rate, locked in for the term. We walk your property once, count the panes, factor in any awnings or signage, and quote a single number you’ll see on every invoice. No surprise upcharges, no per-pane gymnastics.', }, { q: 'What’s your guarantee if a window streaks?', a: 'Text us a photo within 48 hours. We’ll be back same-day or next-business-day at no charge — you only pay when the glass is perfect.', }, ]; const SERVICE_AREAS = [ 'Downtown', 'Old Strathcona', '124 Street', 'Whyte Avenue', 'Jasper Avenue', 'Sherwood Park', 'St. Albert', 'South Common', 'West Edmonton', 'Leduc', ]; // ── Theme presets ──────────────────────────────────────────────────────── // Each preset overrides the primary accent on every variation. Variation // chrome (background, secondary palette, typography) stays distinct. const THEMES = { sky: { label: 'Sky', primary: '#0EA5E9', primaryDeep: '#0369A1', primarySoft: '#E0F2FE', accent: '#F59E0B', }, ocean: { label: 'Ocean', primary: '#0F4C81', primaryDeep: '#082F4C', primarySoft: '#E2ECF6', accent: '#D4A24C', }, spring: { label: 'Spring', primary: '#16A34A', primaryDeep: '#14532D', primarySoft: '#DCFCE7', accent: '#F97316', }, sunrise: { label: 'Sunrise', primary: '#F97316', primaryDeep: '#9A3412', primarySoft: '#FFEDD5', accent: '#0EA5E9', }, }; // ── Small shared SVG icons ─────────────────────────────────────────────── const Icon = ({ name, size = 20, stroke = 'currentColor', strokeWidth = 1.8, fill = 'none' }) => { const p = { width: size, height: size, viewBox: '0 0 24 24', fill, stroke, strokeWidth, strokeLinecap: 'round', strokeLinejoin: 'round' }; switch (name) { case 'phone': return (); case 'arrow': return (); case 'check': return (); case 'sparkle': return (); case 'shield': return (); case 'home': return (); case 'pin': return (); case 'leaf': return (); case 'price': return (); case 'calendar': return (); case 'window': return (); case 'squeegee': return (); case 'star': return (); case 'menu': return (); case 'plus': return (); case 'minus': return (); case 'mail': return (); default: return null; } }; // ── Quote form (used by all 3 variations, styled by container) ────────── // External CRM-hosted form embedded via iframe. The form_embed.js script // from the same vendor wires up resize/visibility messaging. function QuoteForm({ accent = '#0EA5E9', dark = false, compact = false }) { React.useEffect(() => { const SRC = 'https://links.amanahcrm.com/js/form_embed.js'; if (document.querySelector(`script[src="${SRC}"]`)) return; const s = document.createElement('script'); s.src = SRC; s.async = true; document.body.appendChild(s); }, []); return (