PageSourceSearch

https://www.getfranklin.com/blog/

html getfranklin.com collected 2026-10-04 21:08:08 UTC 82,753 bytes, 1,569 lines download raw bytes

1<!DOCTYPE html>
2
3<html lang='en'>
4<head>
5    <meta charset='utf-8'>
6    <meta name='viewport' content='width=device-width, initial-scale=1'>
7    <meta http-equiv='X-UA-Compatible' content='IE=edge'>
8    <title>Blog | Franklin</title>
9    <meta name='description' content="The Franklin blog — straight talk on HVAC, plumbing, and electrical work in Northwest Arkansas. Tips, rebates, and the occasional rant on industry nonsense."/>
10    <link rel="canonical" href="https://www.getfranklin.com/blog">
11
12    <meta property="og:title" content="Blog | Franklin">
13    <meta property="og:description" content="The Franklin blog — straight talk on HVAC, plumbing, and electrical work in Northwest Arkansas.">
14    <meta property="og:image" content="https://franklinair-assets.s3.amazonaws.com/static/media/share_image.jpg">
15    <meta property="og:url" content="https://www.getfranklin.com/blog">
16    <meta name="twitter:card" content="summary_large_image">
17    <meta property="og:site_name" content="Franklin AC, Plumbing, & Electrical">
18    <meta name="twitter:image:alt" content="Franklin AC, Plumbing, & Electrical">
19    <meta property="og:type" content="website">
20    <meta property="og:locale" content="en_US">
21
22    
23    
24<script type="application/ld+json">
25{
26  "@context": "https://schema.org",
27  "@graph": [
28    {
29      "@type": ["LocalBusiness", "HVACBusiness", "Plumber", "Electrician"],
30      "@id": "https://www.getfranklin.com/#business",
31      "name": "Franklin AC, Plumbing, & Electrical",
32      "alternateName": "Franklin",
33      "url": "https://www.getfranklin.com",
34      "logo": "https://s3.amazonaws.com/franklinair-assets/static/media/logo.png",
35      "image": "https://s3.amazonaws.com/franklinair-assets/static/media/franklin_team.jpg",
36      "telephone": "+1-479-282-0003",
37      "email": "[email protected]",
38      "priceRange": "$$",
39      "description": "Family-owned HVAC, plumbing, and electrical services for Northwest Arkansas. Home of the $29 service call. Honest, upfront pricing — no surprises.",
40      "slogan": "Pricing is an open book.",
41      "address": {
42        "@type": "PostalAddress",
43        "streetAddress": "1500 S Thompson St, Suite A",
44        "addressLocality": "Springdale",
45        "addressRegion": "AR",
46        "postalCode": "72764",
47        "addressCountry": "US"
48      },
49      "geo": {
50        "@type": "GeoCoordinates",
51        "latitude": 36.1867,
52        "longitude": -94.1288
53      },
54      "openingHoursSpecification": [
55        {
56          "@type": "OpeningHoursSpecification",
57          "dayOfWeek": ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
58          "opens": "07:00",
59          "closes": "19:00"
60        }
61      ],
62      "areaServed": [
63        {"@type": "City", "name": "Fayetteville", "containedInPlace": {"@type":"State","name":"Arkansas"}},
64        {"@type": "City", "name": "Bentonville", "containedInPlace": {"@type":"State","name":"Arkansas"}},
65        {"@type": "City", "name": "Rogers", "containedInPlace": {"@type":"State","name":"Arkansas"}},
66        {"@type": "City", "name": "Springdale", "containedInPlace": {"@type":"State","name":"Arkansas"}},
67        {"@type": "City", "name": "Bella Vista", "containedInPlace": {"@type":"State","name":"Arkansas"}},
68        {"@type": "City", "name": "Farmington", "containedInPlace": {"@type":"State","name":"Arkansas"}},
69        {"@type": "City", "name": "Centerton", "containedInPlace": {"@type":"State","name":"Arkansas"}},
70        {"@type": "City", "name": "Cave Springs", "containedInPlace": {"@type":"State","name":"Arkansas"}},
71        {"@type": "City", "name": "Lowell", "containedInPlace": {"@type":"State","name":"Arkansas"}},
72        {"@type": "City", "name": "Tontitown", "containedInPlace": {"@type":"State","name":"Arkansas"}},
73        {"@type": "City", "name": "Siloam Springs", "containedInPlace": {"@type":"State","name":"Arkansas"}}
74      ],
75      "aggregateRating": {
76        "@type": "AggregateRating",
77        "ratingValue": "5",
78        "reviewCount": "1361",
79        "bestRating": "5",
80        "worstRating": "1"
81      },
82      "review": [
83        {
84          "@type": "Review",
85          "author": {"@type": "Person", "name": "Hannah Dow"},
86          "reviewRating": {"@type": "Rating", "ratingValue": "5", "bestRating": "5", "worstRating": "1"},
87          "reviewBody": "Axell was really helpful, knowledgable, and took his time finding answers and solutions to the problems he uncovered in our system.",
88          "datePublished": "2026-10-02",
89          "publisher": {"@type": "Organization", "name": "Google"}
90        },
91        {
92          "@type": "Review",
93          "author": {"@type": "Person", "name": "Yoga Candice"},
94          "reviewRating": {"@type": "Rating", "ratingValue": "5", "bestRating": "5", "worstRating": "1"},
95          "reviewBody": "Adrian was very professional and polite! Answered all my questions kindly and got us all fixed up!",
96          "datePublished": "2026-10-02",
97          "publisher": {"@type": "Organization", "name": "Google"}
98        },
99        {
100          "@type": "Review",
101          "author": {"@type": "Person", "name": "Joseph Proto"},
102          "reviewRating": {"@type": "Rating", "ratingValue": "5", "bestRating": "5", "worstRating": "1"},
103          "reviewBody": "Axell was awesome",
104          "datePublished": "2026-10-01",
105          "publisher": {"@type": "Organization", "name": "Google"}
106        },
107        {
108          "@type": "Review",
109          "author": {"@type": "Person", "name": "Saul Ruvalcaba"},
110          "reviewRating": {"@type": "Rating", "ratingValue": "5", "bestRating": "5", "worstRating": "1"},
111          "reviewBody": "All the technicians who came to service my house today were very professional and able to answer any questions I had.",
112          "datePublished": "2026-10-01",
113          "publisher": {"@type": "Organization", "name": "Google"}
114        }
115      ],
116      "hasOfferCatalog": {
117        "@type": "OfferCatalog",
118        "name": "Home services",
119        "itemListElement": [
120          {"@type":"Offer","itemOffered":{"@type":"Service","name":"AC repair","url":"https://www.getfranklin.com/services/ac-repair"}},
121          {"@type":"Offer","itemOffered":{"@type":"Service","name":"AC installation & replacement","url":"https://www.getfranklin.com/services/ac-install"}},
122          {"@type":"Offer","itemOffered":{"@type":"Service","name":"AC maintenance","url":"https://www.getfranklin.com/services/ac-maintenance"}},
123          {"@type":"Offer","itemOffered":{"@type":"Service","name":"Furnace repair","url":"https://www.getfranklin.com/services/furnace-repair"}},
124          {"@type":"Offer","itemOffered":{"@type":"Service","name":"Furnace installation","url":"https://www.getfranklin.com/services/furnace-install"}},
125          {"@type":"Offer","itemOffered":{"@type":"Service","name":"Mini-split installation","url":"https://www.getfranklin.com/services/mini-split-install"}},
126          {"@type":"Offer","itemOffered":{"@type":"Service","name":"HVAC inspections","url":"https://www.getfranklin.com/services/hvac-inspections"}},
127          {"@type":"Offer","itemOffered":{"@type":"Service","name":"Aeroseal duct sealing","url":"https://www.getfranklin.com/services/duct-sealing"}},
128          {"@type":"Offer","itemOffered":{"@type":"Service","name":"Indoor air quality","url":"https://www.getfranklin.com/services/indoor-air-quality"}},
129          {"@type":"Offer","itemOffered":{"@type":"Service","name":"Plumbing services","url":"https://www.getfranklin.com/services/plumbing"}},
130          {"@type":"Offer","itemOffered":{"@type":"Service","name":"Water heater repair","url":"https://www.getfranklin.com/services/water-heater-repair"}},
131          {"@type":"Offer","itemOffered":{"@type":"Service","name":"Water heater replacement","url":"https://www.getfranklin.com/services/water-heater-replacement"}},
132          {"@type":"Offer","itemOffered":{"@type":"Service","name":"Electrical services","url":"https://www.getfranklin.com/services/electrical"}}
133        ]
134      },
135      "makesOffer": {
136        "@type": "Offer",
137        "name": "$29 Service Call",
138        "description": "Every in-home visit starts with a flat $29 service call. Your tech provides an all-in price before any work begins.",
139        "price": "29.00",
140        "priceCurrency": "USD"
141      },
142      "knowsAbout": [
143        "HVAC repair", "Air conditioning installation", "Furnace repair",
144        "Heat pump installation", "Mini-split systems", "Duct sealing",
145        "Indoor air quality", "Plumbing leak repair", "Drain cleaning",
146        "Water heater installation", "Water heater repair", "Sewer repair",
147        "Electrical troubleshooting", "Electrical panel upgrades",
148        "Outlet installation", "Lighting installation", "EV charger installation"
149      ],
150      "hasCredential": [
151        {
152          "@type": "EducationalOccupationalCredential",
153          "credentialCategory": "license",
154          "name": "Arkansas HVAC License",
155          "identifier": "0003484",
156          "recognizedBy": {"@type": "GovernmentOrganization", "name": "Arkansas HVACR Licensing Board"}
157        },
158        {
159          "@type": "EducationalOccupationalCredential",
160          "credentialCategory": "license",
161          "name": "Arkansas Electrical License",
162          "identifier": "RM-1111",
163          "recognizedBy": {"@type": "GovernmentOrganization", "name": "Arkansas Board of Electrical Examiners"}
164        },
165        {
166          "@type": "EducationalOccupationalCredential",
167          "credentialCategory": "license",
168          "name": "Arkansas Master Plumber License",
169          "identifier": "MP6911",
170          "recognizedBy": {"@type": "GovernmentOrganization", "name": "Arkansas Department of Health Plumbing Division"}
171        }
172      ]
173    },
174    {
175      "@type": "WebSite",
176      "@id": "https://www.getfranklin.com/#website",
177      "url": "https://www.getfranklin.com",
178      "name": "Franklin AC, Plumbing, & Electrical",
179      "publisher": {"@id": "https://www.getfranklin.com/#business"},
180      "inLanguage": "en-US"
181    }
182  ]
183}
184</script>
184
185
186    
187<script type="application/ld+json">
188{
189  "@context": "https://schema.org",
190  "@type": "Blog",
191  "@id": "https://www.getfranklin.com/blog/",
192  "url": "https://www.getfranklin.com/blog/",
193  "name": "Franklin Blog",
194  "description": "The Franklin blog — HVAC, plumbing, and electrical guidance for Northwest Arkansas homeowners.",
195  "publisher": {"@id": "https://www.getfranklin.com/#business"},
196  "isPartOf": {"@id": "https://www.getfranklin.com/#website"}
197}
198</script>
198
199
200
201    
202    <link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>
203    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
204    <link rel="preconnect" href="https://www.googletagmanager.com">
205    <link rel="preconnect" href="https://dashboard.retellai.com" crossorigin>
206    <link rel="preconnect" href="https://load.usefomo.com">
207    <link rel="dns-prefetch" href="https://franklinair-assets.s3.amazonaws.com">
208
209    
210    
211
212    <link href='https://fonts.googleapis.com/css?family=Libre+Franklin:400,600,700,800|Vollkorn:400,600,700&display=swap' rel='stylesheet'>
213    
214    <link rel='stylesheet' href="https://franklinair-assets.s3.amazonaws.com/static/css/blog.css" type='text/css'/>
215
216    
216<script defer src='https://code.jquery.com/jquery-1.11.3.min.js'></script>
216
217
218    <link rel="shortcut icon" type="image/png" href="https://franklinair-assets.s3.amazonaws.com/static/media/favicon.ico"/>
219
220    <meta name="google-site-verification" content="rTNGzJRVNNnKASl1OO8n3wgubGlKUNGsRVb4-8MAp8Y" />
221
222    
222<script>
vendor: 343 bytes, lines 222-226
222(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
223    new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
224    j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
225    'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
226    })(window,document,'script','dataLayer','
226GTM-MPWMNC3
vendor: 3 bytes, line 226
226');
226</script>
226
227
228    
228<script src="https://load.usefomo.com/api/v1/nRCa_CTvdW953-0Q4Dtp5Q/load.js" async></script>
228
229
230    
230<script
231        id="retell-widget"
232        src="https://dashboard.retellai.com/retell-widget.js"
233        type="module"
234        async
235        data-public-key="public_key_b7511d40a1fa09a5e7fa7"
236        data-agent-id="agent_331eaef75e6c5c3cba5da7b88c"
237        data-title="Chat with Halo"
238        data-logo-url="https://franklinair-assets.s3.us-east-1.amazonaws.com/static/media/halo_avatar.jpg"
239        data-color="#3EC1CD"
240        data-bot-name="Halo"
241        data-popup-message="Hi there! My name is Halo. How can I help?"
242        data-show-ai-popup="true"
243        data-show-ai-popup-time="5"
244        data-auto-open="false"
245    ></script>
245
246
247    
248    
248<script>
249    (function() {
250        var AVATAR_URL = "https://franklinair-assets.s3.us-east-1.amazonaws.com/static/media/halo_avatar.jpg";
251        var STYLE_ID = "fr-retell-overrides";
252        var OVERRIDE_CSS = [
253            ".retell-fab {",
254            "  background-image: url('" + AVATAR_URL + "') !important;",
255            "  background-size: cover !important;",
256            "  background-position: center center !important;",
257            "  background-repeat: no-repeat !important;",
258            "  box-shadow: 0 0 0 3px #3EC1CD, 0 6px 20px rgba(0,0,0,0.25) !important;",
259            "}",
260            ".retell-fab > svg { display: none !important; }",
261            ".retell-agent-logo img {",
262            "  border-radius: 50% !important;",
263            "  object-fit: cover !important;",
264            "  width: 28px !important;",
265            "  height: 28px !important;",
266            "}",
267            // Pre-click popup message ("Hi there! My name is Halo..."):
268            // bump from default 14px to a more readable 17px and widen the
269            // bubble slightly so the longer text doesn't reflow awkwardly.
270            ".retell-ai-message-content {",
271            "  font-size: 16px !important;",
272            "  line-height: 26px !important;",
273            "}",
274            ".retell-ai-message-bubble {",
275            "  max-width: 310px !important;",
276            "  width: 310px !important;",
277            "}"
278        ].join("\n");
279
280        function injectInto(shadowRoot) {
281            if (!shadowRoot || shadowRoot.getElementById(STYLE_ID)) return;
282            var styleEl = document.createElement("style");
283            styleEl.id = STYLE_ID;
284            styleEl.textContent = OVERRIDE_CSS;
285            shadowRoot.appendChild(styleEl);
286        }
287
288        function findAndInject() {
289            // Retell's host element is an anonymous <div> appended to <body>
290            // with an open shadow root containing .retell-fab. Walk top-level
291            // body children and find the one whose shadow root has the FAB.
292            var nodes = document.body ? document.body.children : [];
293            for (var i = 0; i < nodes.length; i++) {
294                var sr = nodes[i].shadowRoot;
295                if (sr && sr.querySelector(".retell-fab")) {
296                    injectInto(sr);
297                    return true;
298                }
299            }
300            return false;
301        }
302
303        // Try immediately, then watch for the widget to mount asynchronously.
304        if (findAndInject()) return;
305        var observer = new MutationObserver(function() {
306            if (findAndInject()) observer.disconnect();
307        });
308        if (document.body) {
309            observer.observe(document.body, { childList: true, subtree: true });
310        } else {
311            document.addEventListener("DOMContentLoaded", function() {
312                observer.observe(document.body, { childList: true, subtree: true });
313                findAndInject();
314            });
315        }
316        // Stop watching after 20s; widget should be mounted long before then.
317        setTimeout(function() { observer.disconnect(); }, 20000);
318    })();
319    </script>
319
320
321    
322</head>
323
324<body class="fr2025" data-fr-page="wagtail_serve">
325
326
327<script>
328window.FR_HOURS = {"open_min": 420, "regular_close_min": 1020, "after_hours_close_min": 1140, "tz": "America/Chicago"};
329window.frChicagoNow = function() {
330    try {
331        var H = window.FR_HOURS || { tz: 'America/Chicago' };
332        var parts = new Intl.DateTimeFormat('en-US', {
333            timeZone: H.tz || 'America/Chicago',
334            weekday: 'short',
335            hour: 'numeric',
336            minute: 'numeric',
337            hour12: false
338        }).formatToParts(new Date());
339        var day = '', h = 0, m = 0;
340        for (var i = 0; i < parts.length; i++) {
341            var p = parts[i];
342            if (p.type === 'weekday') day = p.value;
343            if (p.type === 'hour') h = parseInt(p.value, 10);
344            if (p.type === 'minute') m = parseInt(p.value, 10);
345        }
346        if (h === 24) h = 0;
347        return { day: day, minutes: h * 60 + m };
348    } catch (e) { return null; }
349};
350window.frIsAfterHoursCentral = function() {
351    var H = window.FR_HOURS || {};
352    var t = window.frChicagoNow();
353    if (!t || !H) return false;
354    if (['Mon','Tue','Wed','Thu','Fri'].indexOf(t.day) !== -1) {
355        return t.minutes >= H.regular_close_min && t.minutes < H.after_hours_close_min;
356    }
357    if (t.day === 'Sat') {
358        return t.minutes >= H.open_min && t.minutes < H.after_hours_close_min;
359    }
360    return false;
361};
362window.frIsClosedCentral = function() {
363    var H = window.FR_HOURS || {};
364    var t = window.frChicagoNow();
365    if (!t || !H) return false;
366    if (t.day === 'Sun') return true;
367    return t.minutes < H.open_min || t.minutes >= H.after_hours_close_min;
368};
369</script>
369
370
371
372<script>
373window.FR_LEAD_VALUES = {
374    DEFAULT: 300,
375    by_trade_issue: {
376        "hvac|install": 5000,
377        "hvac|repair": 300,
378        "hvac|maintenance": 69,
379        "plumbing|install": 1000,
380        "plumbing|repair": 400,
381        "electrical|install": 1000,
382        "electrical|repair": 200
383    },
384    // intent_weight applied to booking_started (partial credit before submit)
385    intent_weight: 0.3,
386    // phone_intent_weight applied to phone_click events. Lower than the form
387    // intent_weight because a tel: click is "I might call" — many never actually
388    // dial, and of those who do, many are tire-kickers. Tune as we gather data.
389    phone_intent_weight: 0.3
390};
391function frEstimateLeadValue(trade, issueType /*, serviceDetails (unused) */) {
392    var t = (window.FR_LEAD_VALUES || {});
393    var key = ((trade || '') + '|' + (issueType || '')).trim().toLowerCase();
394    if (key !== '|' && t.by_trade_issue && t.by_trade_issue[key] != null) {
395        return t.by_trade_issue[key];
396    }
397    return t.DEFAULT != null ? t.DEFAULT : 400;
398}
399</script>
vendor: 69 bytes, lines 399-401
399
400
401<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
401GTM-MPWMNC3
vendor: 84 bytes, lines 401-403
401"
402height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
403
404<a class="fr-skip" href="#main">Skip to content</a>
405
406
407<header class="fr-header">
408    
409    <div class="fr-header-stripe" aria-hidden="true"></div>
410    <div class="fr-header-inner">
411        <a href='/' class="fr-logo" aria-label="Franklin home">
412            <img alt='Franklin AC, Plumbing & Electrical' src="https://franklinair-assets.s3.amazonaws.com/static/media/logo.png">
413        </a>
414
415        <nav class="fr-header-nav" aria-label="Primary">
416            
417            <div class="fr-nav-item fr-nav-item--has-dropdown" data-fr-dropdown>
418                <button type="button"
419                        class="fr-nav-link "
420                        aria-haspopup="true"
421                        aria-expanded="false"
422                        data-fr-dropdown-toggle>
423                    <span>Services</span>
424                    <svg class="fr-nav-chevron" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
425                </button>
426                <div class="fr-dropdown" role="menu">
427                    <div class="fr-dropdown-grid">
428                        <div class="fr-dropdown-col">
429                            <div class="fr-dropdown-col-title fr-dropdown-col-title--hvac">HVAC</div>
430                            <a href='/services/ac-repair' role="menuitem">AC Repair</a>
431                            <a href='/services/ac-install' role="menuitem">AC Installation</a>
432                            <a href='/services/ac-maintenance' role="menuitem">AC Maintenance</a>
433                            <a href='/services/furnace-repair' role="menuitem">Furnace Repair</a>
434                            <a href='/services/furnace-install' role="menuitem">Furnace Installation</a>
435                            <a href='/services/mini-split-install' role="menuitem">Mini-Split Installation</a>
436                            <a href='/services/hvac-inspections' role="menuitem">HVAC Inspections</a>
437                            <a href='/services/duct-sealing' role="menuitem">Duct Sealing</a>
438                            <a href='/services/indoor-air-quality' role="menuitem">Indoor Air Quality</a>
439                        </div>
440                        <div class="fr-dropdown-col">
441                            <div class="fr-dropdown-col-title fr-dropdown-col-title--plumbing">Plumbing</div>
442                            <a href='/services/plumbing' role="menuitem">Plumbing Services</a>
443                            <a href='/services/water-heater-repair' role="menuitem">Water Heater Repair</a>
444                            <a href='/services/water-heater-replacement' role="menuitem">Water Heater Replacement</a>
445                            <a href='/services/drain-cleaning' role="menuitem">Drain Cleaning</a>
446                            <a href='/services/emergency-plumbing' role="menuitem">Emergency Plumbing</a>
447                            <a href='/services/garbage-disposal' role="menuitem">Garbage Disposal</a>
448                            <a href='/services/sewer-line-repair' role="menuitem">Sewer Line Repair</a>
449                            <a href='/services/water-leak-repair' role="menuitem">Water Leak Repair</a>
450                            <a href='/services/water-filtration' role="menuitem">Water Filtration</a>
451                        </div>
452                        <div class="fr-dropdown-col">
453                            <div class="fr-dropdown-col-title fr-dropdown-col-title--electrical">Electrical</div>
454                            <a href='/services/electrical' role="menuitem">Electrical Services</a>
455                            <a href='/services/panel-upgrade' role="menuitem">Panel Upgrade</a>
456                            <a href='/services/outlet-installation' role="menuitem">Outlet & Switch Installation</a>
457                            <a href='/services/ev-charger-install' role="menuitem">EV Charger Install</a>
458                        </div>
459                    </div>
460                </div>
461            </div>
462
463            <a href='/locations' class="fr-nav-link ">Locations</a>
464            <a href='/autopilot' class="fr-nav-link ">Autopilot</a>
465            <a href='/pricing' class="fr-nav-link ">Pricing</a>
466            <a href='/financing' class="fr-nav-link ">Financing</a>
467            <a href='/careers' class="fr-nav-link ">Careers</a>
468        </nav>
469
470        <div class="fr-header-actions">
471            <a href='tel:+14792820003' class="fr-header-phone number_html" aria-label="Call (479) 282-0003">
472                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg>
473                <span class="number_link">(479) 282-0003</span>
474            </a>
475            <a href='tel:+14792820003' class="fr-icon-btn fr-icon-btn--mobile-phone number_html" aria-label="Call Franklin">
476                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg>
477            </a>
478            <button type="button" class="fr-header-cta open-scheduler-btn">Schedule</button>
479            <button type="button" class="fr-icon-btn fr-menu-toggle" id="fr-menu-toggle" aria-label="Open menu" aria-expanded="false" aria-controls="fr-mobile-drawer">
480                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
481            </button>
482        </div>
483    </div>
484</header>
485
486
487<div class="fr-mobile-drawer" id="fr-mobile-drawer" aria-hidden="true">
488    <div class="fr-mobile-drawer__backdrop" data-fr-drawer-close></div>
489    <aside class="fr-mobile-drawer__panel" role="dialog" aria-modal="true" aria-label="Site navigation">
490        <div class="fr-mobile-drawer__header">
491            <a href='/' class="fr-logo" data-fr-drawer-close-on-click>
492                <img alt='Franklin' src="https://franklinair-assets.s3.amazonaws.com/static/media/logo.png">
493            </a>
494            <button type="button" class="fr-icon-btn" data-fr-drawer-close aria-label="Close menu">
495                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
496            </button>
497        </div>
498
499        <nav class="fr-mobile-drawer__nav" aria-label="Mobile navigation">
500            <details class="fr-mobile-drawer__group" >
501                <summary>Services</summary>
502                <div class="fr-mobile-drawer__sublist">
503                    <span class="fr-mobile-drawer__subhead">HVAC</span>
504                    <a href='/services/ac-repair'>AC Repair</a>
505                    <a href='/services/ac-install'>AC Installation</a>
506                    <a href='/services/ac-maintenance'>AC Maintenance</a>
507                    <a href='/services/furnace-repair'>Furnace Repair</a>
508                    <a href='/services/furnace-install'>Furnace Installation</a>
509                    <a href='/services/mini-split-install'>Mini-Split Installation</a>
510                    <a href='/services/hvac-inspections'>HVAC Inspections</a>
511                    <a href='/services/duct-sealing'>Duct Sealing</a>
512                    <a href='/services/indoor-air-quality'>Indoor Air Quality</a>
513
514                    <span class="fr-mobile-drawer__subhead">Plumbing</span>
515                    <a href='/services/plumbing'>Plumbing Services</a>
516                    <a href='/services/water-heater-repair'>Water Heater Repair</a>
517                    <a href='/services/water-heater-replacement'>Water Heater Replacement</a>
518                    <a href='/services/drain-cleaning'>Drain Cleaning</a>
519                    <a href='/services/emergency-plumbing'>Emergency Plumbing</a>
520                    <a href='/services/garbage-disposal'>Garbage Disposal</a>
521                    <a href='/services/sewer-line-repair'>Sewer Line Repair</a>
522                    <a href='/services/water-leak-repair'>Water Leak Repair</a>
523                    <a href='/services/water-filtration'>Water Filtration</a>
524
525                    <span class="fr-mobile-drawer__subhead">Electrical</span>
526                    <a href='/services/electrical'>Electrical Services</a>
527                    <a href='/services/panel-upgrade'>Panel Upgrade</a>
528                    <a href='/services/outlet-installation'>Outlet & Switch Installation</a>
529                    <a href='/services/ev-charger-install'>EV Charger Install</a>
530                </div>
531            </details>
532
533            <a href='/locations' class="">Locations</a>
534            <a href='/autopilot' class="">Autopilot</a>
535            <a href='/pricing' class="">Pricing</a>
536            <a href='/financing' class="">Financing</a>
537            <a href='/free-second-opinion' class="">Free 2nd Opinion</a>
538            <a href='/pay' class="">Make a Payment</a>
539            <a href='/calculator' class="">Rebate Calculator</a>
540            <a href='/blog/'>Blog</a>
541            <a href='/careers' class="">Careers</a>
542
543        </nav>
544
545        <div class="fr-mobile-drawer__cta">
546            <button type="button" class="fr-btn-primary open-scheduler-btn" data-fr-drawer-close-on-click>Schedule online</button>
547            <a class="fr-mobile-drawer__phone number_html" href="tel:+14792820003">
548                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg>
549                <span class="number_link">(479) 282-0003</span>
550            </a>
551        </div>
552    </aside>
553</div>
554
555
556<main>
557    
558
559
560
561
562<section class="fr-hero fr-hero--simple" id="main">
563    <span class="fr-eyebrow">The Franklin Blog</span>
564    <h1 class="fr-hero-title">Blog</h1>
565    <p class="fr-hero-sub">Straight talk on HVAC, plumbing, and electrical work in Northwest Arkansas &mdash; tips, rebates, and the occasional rant on industry nonsense.</p>
566</section>
567
568
569<section class="fr-blog-list">
570    <div class="fr-blog-list-inner">
571        
572        <article class="fr-blog-card">
573            <p class="fr-blog-card__meta">April 30, 2026</p>
574            <h2 class="fr-blog-card__title">
575                <a href="/blog/why-franklin">Why customers buy from Franklin</a>
576            </h2>
577            
578                <p class="fr-blog-card__intro">Choosing a home service company is hard. Here&#x27;s a few reasons customers choose Franklin:</p>
579            
580            <p class="fr-blog-card__excerpt"><h3 data-block-key="55qcp"><b>1. We charge what we need to,</b> <b><i>not</i></b><b> what we want to</b></h3><p data-block-key="dlv5f">There are two pricing philosophies when it comes to business. One says to take as much as you can from the customer. Basically if the customer has $100 in their wallet, you&#x27;ve failed if…</p></p>
581            <a class="fr-blog-card__read-more" href="/blog/why-franklin">Read more &rarr;</a>
582        </article>
583        
584        <article class="fr-blog-card">
585            <p class="fr-blog-card__meta">September 14, 2026</p>
586            <h2 class="fr-blog-card__title">
587                <a href="/blog/understanding-hvac-systems">Understanding the differences between HVAC systems</a>
588            </h2>
589            
590                <p class="fr-blog-card__intro">Allow us to simplify the confusing world of air conditioning</p>
591            
592            <p class="fr-blog-card__excerpt"><p data-block-key="9f4hm">If you&#x27;re reading this blog post, the chances are good that you’re in the market for a new HVAC system or soon will be. More than likely you&#x27;ve done research on systems, options, manufacturers, efficiency ratings, etc. and are st…</p></p>
593            <a class="fr-blog-card__read-more" href="/blog/understanding-hvac-systems">Read more &rarr;</a>
594        </article>
595        
596        <article class="fr-blog-card">
597            <p class="fr-blog-card__meta">April 30, 2026</p>
598            <h2 class="fr-blog-card__title">
599                <a href="/blog/heater-blowing-cold-air">Heater blowing cold air: step by step DIY repair guide</a>
600            </h2>
601            
602                <p class="fr-blog-card__intro">Heater not blowing hot air? Let&#x27;s get it fixed.</p>
603            
604            <p class="fr-blog-card__excerpt"><p data-block-key="0r94g">Is your furnace or house heater not working? You&#x27;re not alone, which is why we wrote this guide and filled it with DIY heater troubleshooting steps. Do your best to methodically work through each step and there&#x27;s a decent chance …</p></p>
605            <a class="fr-blog-card__read-more" href="/blog/heater-blowing-cold-air">Read more &rarr;</a>
606        </article>
607        
608        <article class="fr-blog-card">
609            <p class="fr-blog-card__meta">April 30, 2026</p>
610            <h2 class="fr-blog-card__title">
611                <a href="/blog/ac-not-blowing-cold-air">AC not blowing cold air: step by step DIY repair guide</a>
612            </h2>
613            
614                <p class="fr-blog-card__intro">Because nobody likes a hot house...</p>
615            
616            <p class="fr-blog-card__excerpt"><p data-block-key="j4qxd">Is your air conditioner running but not blowing cold air? Good news, this guide was written just for you. In it, you’ll find seven DIY ways to troubleshoot and fix the most common AC issues. If you’ll take your time and methodically work t…</p></p>
617            <a class="fr-blog-card__read-more" href="/blog/ac-not-blowing-cold-air">Read more &rarr;</a>
618        </article>
619        
620        <article class="fr-blog-card">
621            <p class="fr-blog-card__meta">April 30, 2026</p>
622            <h2 class="fr-blog-card__title">
623                <a href="/blog/how-clean-your-ac-coils">How to clean your AC coils like an HVAC technician</a>
624            </h2>
625            
626                <p class="fr-blog-card__intro">Cleaning your AC coils is easy, effective, and saves you money.</p>
627            
628            <p class="fr-blog-card__excerpt"><p data-block-key="c27w3">Is your AC running all of the time or not cooling as well as it should? There’s a good chance you have dirty AC coils. As you run your AC, debris slowly builds up on your coils, making it harder for your AC to cool your home. A quick clean…</p></p>
629            <a class="fr-blog-card__read-more" href="/blog/how-clean-your-ac-coils">Read more &rarr;</a>
630        </article>
631        
632
633        
634        <nav class="fr-blog-pagination" aria-label="Blog pagination">
635            
636            
637                
638                    <span class="fr-blog-pagination__link is-active" aria-current="page">1</span>
639                
640            
641                
642                    <a class="fr-blog-pagination__link" href="?page=2">2</a>
643                
644            
645            
646                <a class="fr-blog-pagination__link" href="?page=2" aria-label="Next page">&raquo;</a>
647            
648        </nav>
649        
650    </div>
651</section>
652
653
654</main>
655
656<footer class="fr-footer">
657    
658    <div class="fr-footer-cta">
659        <div class="fr-footer-cta-inner">
660            <div class="fr-footer-cta-text">
661                <span class="fr-footer-eyebrow">Family owned &middot; 5.0 ★ &middot; 1,361 reviews</span>
662                <h2 class="fr-footer-cta-title">Need service? We&rsquo;re just a tap away.</h2>
663            </div>
664            <div class="fr-footer-cta-actions">
665                <button type="button" class="fr-btn-primary open-scheduler-btn">Schedule online</button>
666                <a class="fr-footer-phone-cta number_html" href="tel:+14792820003">
667                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg>
668                    <span class="number_link">(479) 282-0003</span>
669                </a>
670            </div>
671        </div>
672    </div>
673
674    
675    <div class="fr-footer-main">
676        <div class="fr-footer-grid">
677            <div class="fr-footer-brand-col">
678                <img class="fr-footer-logo" alt="Franklin AC, Plumbing & Electrical" src="https://franklinair-assets.s3.amazonaws.com/static/media/logo.png">
679                <p class="fr-footer-tagline">Family-owned and operated home services for Northwest Arkansas. HVAC, plumbing, and electrical — three trades, one local company. Past winner of Best of HomeAdvisor and the Angi Super Service Award.</p>
680                <div class="fr-footer-trust" aria-label="Trust badges">
681                    <img src="https://franklinair-assets.s3.amazonaws.com/static/media/bbb_trust.png" alt="BBB Accredited Business" width="80" height="56" loading="lazy" decoding="async">
682                    <img src="https://franklinair-assets.s3.amazonaws.com/static/media/google_trust.png" alt="Google Reviews" width="94" height="56" loading="lazy" decoding="async">
683                    <!-- <img src="https://franklinair-assets.s3.amazonaws.com/static/media/ha_trust.png" alt="HomeAdvisor Top Rated" width="57" height="56" loading="lazy" decoding="async"> -->
684                    <!-- <img src="https://franklinair-assets.s3.amazonaws.com/static/media/super_service.png" alt="Angi Super Service" width="64" height="56" loading="lazy" decoding="async"> -->
685                </div>
686            </div>
687
688            <div class="fr-footer-col">
689                <h3>Popular Services</h3>
690                <a href='/services/ac-repair'>AC Repair</a>
691                <a href='/services/ac-install'>AC Installation</a>
692                <a href='/services/furnace-repair'>Furnace Repair</a>
693                <a href='/services/plumbing'>Plumbing</a>
694                <a href='/services/electrical'>Electrical</a>
695                <a href='/services/water-heater-replacement'>Water Heaters</a>
696                <a href='/services/water-filtration'>Water Filtration</a>
697            </div>
698
699            <div class="fr-footer-col">
700                <h3>Company</h3>
701                <a href='/pricing'>Pricing</a>
702                <a href='/autopilot'>Autopilot Membership</a>
703                <a href='/guarantee'>Service Guarantee</a>
704                <a href='/free-second-opinion'>Free 2nd Opinion</a>
705                <a href='/careers'>Careers</a>
706                <a href='/blog/'>Blog</a>
707            </div>
708
709            <div class="fr-footer-col">
710                <h3>Customer Tools</h3>
711                <a href='/financing'>Apply for Financing</a>
712                <a href='/pay'>Make a Payment</a>
713                <a href='/calculator'>Rebate Calculator</a>
714            </div>
715
716            <div class="fr-footer-col fr-footer-col--contact">
717                <h3>Contact</h3>
718                <address class="fr-footer-address">
719                    1500 S Thompson St, Suite A<br>
720                    Springdale, AR 72764
721                </address>
722                <p class="fr-footer-hours"><strong>Mon&ndash;Sat</strong> &middot; 7am &ndash; 7pm</p>
723                <a class="fr-footer-phone-bold number_link" href="tel:+14792820003">(479) 282-0003</a>
724                <a class="fr-footer-email" href="/cdn-cgi/l/email-protection#533b363f3f3c133436273521323d383f3a3d7d303c3e"><span class="__cf_email__" data-cfemail="7119141d1d1e311614051703101f1a1d181f5f121e1c">[email&#160;protected]</span></a>
725            </div>
726        </div>
727
728        
729        <div class="fr-footer-areas">
730            <span class="fr-footer-areas-label">Proudly serving <a href='/locations'>Northwest Arkansas</a>:</span>
731            <ul class="fr-footer-areas-list">
732                <li><a href='/locations/fayetteville'>Fayetteville</a></li>
733                <li><a href='/locations/bentonville'>Bentonville</a></li>
734                <li><a href='/locations/rogers'>Rogers</a></li>
735                <li><a href='/locations/springdale'>Springdale</a></li>
736                <li><a href='/locations/bella-vista'>Bella Vista</a></li>
737                <li><a href='/locations/farmington'>Farmington</a></li>
738                <li><a href='/locations/centerton'>Centerton</a></li>
739                <li><a href='/locations/cave-springs'>Cave Springs</a></li>
740                <li><a href='/locations/lowell'>Lowell</a></li>
741                <li><a href='/locations/tontitown'>Tontitown</a></li>
742                <li><a href='/locations/siloam-springs'>Siloam Springs</a></li>
743            </ul>
744        </div>
745    </div>
746
747    
748    <div class="fr-footer-bottom">
749        <div class="fr-footer-bottom-inner">
750            <span>&copy; 2026 Franklin AC, Plumbing &amp; Electrical. All rights reserved.</span>
751            <span class="fr-footer-licenses">AR HVAC #0003484 &middot; AR Electrical #RM-1111 &middot; AR Plumbing #MP6911</span>
752            <a href='/privacy-policy'>Privacy Policy</a>
753            <a href='/terms'>Terms</a>
754            <a href='/accessibility'>Accessibility</a>
755        </div>
756    </div>
757</footer>
758
759
760
761<div id="scheduler-modal" class="modal-overlay" style="display: none;">
762    <div class="modal-content">
763        <span class="close-btn">&times;</span>
764
765        <div class="modal-header-custom">
766            <h2>Schedule Online</h2>
767            <div class="progress-bar">
768                <div class="progress-step active" data-step="1">
769                    <div class="step-circle">1</div>
770                    <div class="step-label">Service</div>
771                </div>
772                <div class="progress-step" data-step="2">
773                    <div class="step-circle">2</div>
774                    <div class="step-label">Urgency</div>
775                </div>
776                <div class="progress-step" data-step="3">
777                    <div class="step-circle">3</div>
778                    <div class="step-label">Contact</div>
779                </div>
780                <div class="progress-step" data-step="4">
781                    <div class="step-circle">4</div>
782                    <div class="step-label">Review</div>
783                </div>
784            </div>
785        </div>
786
787        <form id="scheduler-form" novalidate action='/submit-request' method="POST">
788            <input type="hidden" name="csrfmiddlewaretoken" value="XZAcQboHnT1rhumVkQtsdh52g7l5b3qe8fJZlXs2Sn1rkdJv7qh8fQd74lRrXxEj">
789
790            <div class="honeypot-field" aria-hidden="true">
791                <label for="id_website">Do not fill out this field</label>
792                <input type="text" name="website" id="id_website" tabindex="-1" autocomplete="off">
793            </div>
794
795            
796            <input type="hidden" name="utm_source" data-fr-attr="utm_source">
797            <input type="hidden" name="utm_medium" data-fr-attr="utm_medium">
798            <input type="hidden" name="utm_campaign" data-fr-attr="utm_campaign">
799            <input type="hidden" name="utm_content" data-fr-attr="utm_content">
800            <input type="hidden" name="utm_term" data-fr-attr="utm_term">
801            <input type="hidden" name="gclid" data-fr-attr="gclid">
802            <input type="hidden" name="fbclid" data-fr-attr="fbclid">
803            <input type="hidden" name="msclkid" data-fr-attr="msclkid">
804            <input type="hidden" name="referrer" data-fr-attr="referrer">
805            <input type="hidden" name="landing_page" data-fr-attr="landing_page">
806            <input type="hidden" name="submission_page" data-fr-attr="submission_page">
807            <input type="hidden" name="submission_path" value="modal">
808            <input type="hidden" id="scheduler-address-zip" name="address_zip" value="">
809            <input type="hidden" id="scheduler-address-city" name="address_city" value="">
810            
811            <input type="hidden" name="is_after_hours" value="false">
812            <input type="hidden" name="is_closed" value="false">
813
814            <div id="form-page-1-service" class="form-page">
815                <div class="page-content-wrapper">
816                    <div class="form-section">
817                        <h2>What service do you need?</h2>
818                        <div class="radio-group" data-next-section="section-issue-type">
819                            <label><input type="radio" name="service_trade" value="hvac" required> HVAC</label>
820                            <label><input type="radio" name="service_trade" value="plumbing" required> Plumbing</label>
821                            <label><input type="radio" name="service_trade" value="electrical" required>
821 Electrical</label>
822                        </div>
823                        <div class="error-message radio-error"></div>
824                    </div>
825
826                    <div id="section-issue-type" class="form-section">
827                        <h2>Please select your issue:</h2>
828                        <div class="radio-group" data-next-section="section-service-list">
829                            <label><input type="radio" name="issue_type" value="repair" required> Repair</label>
830                            <label><input type="radio" name="issue_type" value="install" required> Install</label>
831                            <label id="maintenance-option" class="hidden"><input type="radio" name="issue_type" value="maintenance" required> Maintenance</label>
832                            <label><input type="radio" name="issue_type" value="other" required> Other</label>
833                        </div>
834                        <div class="error-message radio-error"></div>
835                    </div>
836
837                    <div id="section-service-list" class="form-section">
838                        <h2 id="details-title">What needs to be repaired/installed?</h2>
839                        <div id="service-options-container" class="radio-group vertical"></div>
840                        <div id="other-issue-container" class="hidden">
841                            <textarea id="other-issue-description" name="other_issue_description" rows="4" placeholder="Please tell us what issue you are needing help with."></textarea>
842                            <div class="error-message"></div>
843                        </div>
844                        <div class="error-message radio-error"></div>
845                    </div>
846                </div>
847
848                <div class="navigation-btns">
849                    <button type="button" class="next-btn">Next &rarr;</button>
850                </div>
851            </div>
852
853            <div id="form-page-2-timing" class="form-page hidden">
854                <div class="page-content-wrapper">
855                    <div id="section-timing" class="form-section">
856                        <h2>When would you like service?</h2>
857                        <div class="radio-group" data-next-section="section-notes">
858                            <label><input type="radio" name="timing" value="asap" required> ASAP (emergency)</label>
859                            <label><input type="radio" name="timing" value="next_few_days" required> Next few days</label>
860                            <label><input type="radio" name="timing" value="next_few_weeks" required> Next few weeks</label>
861                        </div>
862                        <div class="error-message radio-error"></div>
863                    </div>
864
865                    <div id="section-emergency-confirm" class="form-section">
866                        <h2>It&rsquo;s currently outside our standard business hours.</h2>
867                        <p class="emergency-fee-note">We&rsquo;re happy to help you right away! Emergency after-hours service is fully available &mdash; just note that there&rsquo;s an additional <strong>$60 after-hours fee</strong> on top of the standard <strong>$29 service call fee</strong>.</p>
868                        <div class="radio-group" data-next-section="section-notes">
869                            <label><input type="radio" name="after_hours_emergency" value="yes" required> Submit an emergency after-hours request</label>
870                            <label><input type="radio" name="after_hours_emergency" value="no" required> I&rsquo;ll wait for the next business day</label>
871                        </div>
872                        <div class="error-message radio-error"></div>
873                    </div>
874
875                    <div id="section-notes" class="form-section">
876                        <h2>Would you like to add any notes? (optional)</h2>
877                        <textarea id="notes-input" name="notes" rows="4" placeholder="e.g., scheduling preferences, details about the issue, gate codes, etc."></textarea>
878                    </div>
879                </div>
880
881                <div class="navigation-btns">
882                    <button type="button" class="back-btn">&larr; Back</button>
883                    <button type="button" class="next-btn">Next &rarr;</button>
884                </div>
885            </div>
886
887            <div id="form-page-3-contact" class="form-page hidden">
888                <div class="page-content-wrapper">
889                    <div class="form-section">
890                        <h2>What is the address for service?</h2>
891
892                        <input id="scheduler-address-input" name="service_address" type="text" placeholder="Enter service address" required data-next-section="section-contact-details">
893                        <input type="hidden" id="google-address-selected" name="google_address_selected" value="">
894
895                        <div class="error-message"></div>
896                    </div>
897
898                    <div id="section-contact-details" class="form-section">
899                        <h2>Who should we contact about the appointment?</h2>
900
901                        <div class="form-field-group">
902                            <label for="first-name-input">First name </label>
903                            <input type="text" id="first-name-input" name="first_name" required>
904                            <div class="error-message"></div>
905                        </div>
906
907                        <div class="form-field-group">
908                            <label for="last-name-input">Last name </label>
909                            <input type="text" id="last-name-input" name="last_name" required>
910                            <div class="error-message"></div>
911                        </div>
912
913                        <div class="form-field-group">
914                            <label for="email-input">Email </label>
915                            <input type="email" id="email-input" name="email" required>
916                            <div class="error-message"></div>
917                        </div>
918
919                        <div class="form-field-group">
920                            <label for="phone-number-input">Phone number</label>
921                            <input type="tel" id="phone-number-input" name="phone_number" required placeholder="(555) 555-5555">
922                            <div class="error-message"></div>
923                            <div class="sms-consent">
924                                <label>
924By providing your phone number, you agree to receive texts from Franklin AC, Plumbing, & Electrical with appointment reminders and updates relating to past service. We promise to never text you for marketing purposes. Message and data rates may apply. You can reply STOP at any time to unsubscribe.</label>
925                            </div>
926                        </div>
927
928
929                    </div>
930                </div>
931
932                <div class="navigation-btns">
933                    <button type="button" class="back-btn">&larr; Back</button>
934                    <button type="button" class="next-btn">Next &rarr;</button>
935                </div>
936            </div>
937
938            <div id="form-page-4-review" class="form-page hidden">
939                <div class="page-content-wrapper">
940                    <h2>Confirm your booking request</h2>
941                    <div class="review-summary">
942                        <p><strong>Service:</strong> <span id="review-service"></span></p>
943                        <p><strong>Issue:</strong> <span id="review-issue-detail"></span></p>
944                        <p><strong>When:</strong> <span id="review-timing"></span></p>
945                        <p><strong>Where:</strong> <span id="review-address"></span></p>
946                        <p><strong>Contact:</strong> <span id="review-name"></span></p>
947                        <p><strong>Email:</strong> <span id="review-email"></span></p>
948                        <p><strong>Phone:</strong> <span id="review-phone"></span></p>
949                        <p><strong>Notes:</strong> <span id="review-notes"></span></p>
950                    </div>
951                    <div class="error-message checkbox-error"></div>
952                </div>
953                <div class="navigation-btns">
954                    <button type="button" class="back-btn">&larr; Back</button>
955                    <button type="submit" id="book-appointment-btn">Submit &rarr;</button>
956                </div>
957            </div>
958
959            <div id="scheduler-form-page-confirmation" class="form-page hidden">
960                <div class="page-content-wrapper">
961                    <h2>Thank you!</h2>
962                    <p>Your appointment request has been submitted successfully.</p>
963                    <p>
964                        <strong>During business hours</strong> (Mon&ndash;Fri, 7am&ndash;5pm), our team will reach out to confirm your booking within a few minutes.<br><br>
965                        <strong>After hours</strong> (Mon&ndash;Fri 5pm&ndash;7pm, or Saturday 7am&ndash;7pm), our on-call dispatcher will follow up shortly if you selected emergency service; otherwise we&rsquo;ll be in touch first thing the next business day.<br><br>
966                        <strong>Overnight or on Sunday</strong>, we&rsquo;ll reach out first thing the next business morning. Thank you for the opportunity!
967                    </p>
968                </div>
969                <div class="navigation-btns">
970                    <button type="button" id="scheduler-close-confirmation-btn">Close</button>
971                </div>
972            </div>
973
974        </form>
975    </div>
976</div>
vendor: 111 bytes, lines 976-979
976
977
978
979<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script>
979<script defer src="https://franklinair-assets.s3.amazonaws.com/static/js/fr-attribution.js"></script>
vendor: 1 bytes, line 979
979
980<script defer src="https://franklinair-assets.s3.amazonaws.com/static/js/blog.js"></script>
vendor: 1 bytes, line 980
980
981<script defer src="https://franklinair-assets.s3.amazonaws.com/static/js/scheduler_widget.js"></script>
vendor: 1 bytes, line 981
981
982<script defer src="https://franklinair-assets.s3.amazonaws.com/static/js/scheduler_inline_widget.js"></script>
982
983
984
985<script>
986// Pull the postal_code and locality out of a Google Place result so we can
987// expose non-PII geo signals (ZIP + city only — NEVER street/full address)
988// to GA4 for value-based bidding and heat-mapping.
989function _frExtractZipCity(place) {
990    var out = { zip: '', city: '' };
991    var comps = (place && place.address_components) || [];
992    for (var i = 0; i < comps.length; i++) {
993        var types = comps[i].types || [];
994        if (types.indexOf('postal_code') !== -1 && !out.zip) {
995            out.zip = comps[i].short_name || comps[i].long_name || '';
996        }
997        if (types.indexOf('locality') !== -1 && !out.city) {
998            out.city = comps[i].long_name || comps[i].short_name || '';
999        }
1000        // Fallback if locality is missing (some addresses use sublocality)
1001        if (!out.city && types.indexOf('sublocality') !== -1) {
1002            out.city = comps[i].long_name || comps[i].short_name || '';
1003        }
1004    }
1005    return out;
1006}
1007function _frWriteZipCity(zipInputId, cityInputId, place) {
1008    var zc = _frExtractZipCity(place);
1009    var zEl = document.getElementById(zipInputId);
1010    var cEl = document.getElementById(cityInputId);
1011    if (zEl) zEl.value = zc.zip;
1012    if (cEl) cEl.value = zc.city;
1013}
1014
1015function initAutocomplete() {
1016    const selectedFlag = document.getElementById('google-address-selected');
1017    if (!selectedFlag) return;
1018
1019    // --- Scheduler modal address autocomplete ---
1020    const schedulerInput = document.getElementById('scheduler-address-input');
1021    if (schedulerInput && window.google && google.maps) {
1022        const autocomplete = new google.maps.places.Autocomplete(schedulerInput, {
1023            types: ['address'],
1024            componentRestrictions: { 'country': 'us' },
1025            fields: ['formatted_address', 'address_components']
1026        });
1027        autocomplete.addListener('place_changed', () => {
1028            const place = autocomplete.getPlace();
1029            if (place && place.formatted_address) {
1030                selectedFlag.value = 'true';
1031                _frWriteZipCity('scheduler-address-zip', 'scheduler-address-city', place);
1032                if (typeof unfoldSection === 'function') unfoldSection(schedulerInput);
1033            }
1034        });
1035        schedulerInput.addEventListener('input', () => {
1036            selectedFlag.value = '';
1037            // Stale ZIP/city — clear so we don't ship the previous pick's geo.
1038            var z = document.getElementById('scheduler-address-zip');
1039            var c = document.getElementById('scheduler-address-city');
1040            if (z) z.value = '';
1041            if (c) c.value = '';
1042        });
1043    }
1044
1045    // --- Inline scheduler address autocomplete (per-page hero form) ---
1046    const inlineInput = document.getElementById('inline-scheduler-address-input');
1047    const inlineFlag = document.getElementById('inline-google-address-selected');
1048    if (inlineInput && inlineFlag && window.google && google.maps) {
1049        const autocomplete = new google.maps.places.Autocomplete(inlineInput, {
1050            types: ['address'],
1051            componentRestrictions: { 'country': 'us' },
1052            fields: ['formatted_address', 'address_components']
1053        });
1054        autocomplete.addListener('place_changed', () => {
1055            const place = autocomplete.getPlace();
1056            if (place && place.formatted_address) {
1057                inlineFlag.value = 'true';
1058                _frWriteZipCity('inline-scheduler-address-zip', 'inline-scheduler-address-city', place);
1059                // Reveal the next inline step (contact info).
1060                const addrStep = inlineInput.closest('.fr-inline-sched__step');
1061                const nextName = addrStep?.dataset.next;
1062                if (nextName) {
1063                    const nextStep = document.querySelector(`.fr-inline-sched__step[data-step="${nextName}"]`);
1064                    if (nextStep && !nextStep.classList.contains('is-active')) {
1065                        nextStep.classList.add('is-active');
1066                        const heading = nextStep.querySelector('.fr-inline-sched__q');
1067                        if (heading) heading.scrollIntoView({ behavior: 'smooth', block: 'center' });
1068                    }
1069                }
1070            }
1071        });
1072        inlineInput.addEventListener('input', () => {
1073            inlineFlag.value = '';
1074            var z = document.getElementById('inline-scheduler-address-zip');
1075            var c = document.getElementById('inline-scheduler-address-city');
1076            if (z) z.value = '';
1077            if (c) c.value = '';
1078        });
1079    }
1080
1081    // --- Quote-request modal address autocomplete (free 2nd opinion form) ---
1082    const quoteInput = document.getElementById('quote-request-address-input');
1083    if (quoteInput && window.google && google.maps) {
1084        const autocomplete = new google.maps.places.Autocomplete(quoteInput, {
1085            types: ['address'],
1086            componentRestrictions: { 'country': 'us' },
1087            fields: ['formatted_address']
1088        });
1089        autocomplete.addListener('place_changed', () => {
1090            const place = autocomplete.getPlace();
1091            if (place && place.formatted_address) {
1092                selectedFlag.value = 'true';
1093            }
1094        });
1095        quoteInput.addEventListener('input', () => { selectedFlag.value = ''; });
1096    }
1097}
1098</script>
1098
1099
1100
1101
1102
1103<script>
1104(function() {
1105    var badges = document.querySelectorAll('[data-fr-hours-badge]');
1106    if (!badges.length) return;
1107    function refresh() {
1108        var fmt = new Intl.DateTimeFormat('en-US', {
1109            timeZone: 'America/Chicago',
1110            weekday: 'short',
1111            hour: 'numeric',
1112            minute: 'numeric',
1113            hour12: false
1114        });
1115        var parts = {};
1116        fmt.formatToParts(new Date()).forEach(function(p) { parts[p.type] = p.value; });
1117        var day = parts.weekday;
1118        var minutes = (parseInt(parts.hour, 10) % 24) * 60 + parseInt(parts.minute, 10);
1119        var open = ['Mon','Tue','Wed','Thu','Fri','Sat'].indexOf(day) !== -1
1120                    && minutes >= 7 * 60 && minutes < 19 * 60;
1121        var openHtml = 'OPEN NOW &middot; Mon&ndash;Sat 7am&ndash;7pm';
1122        var closedHtml = 'CLOSED &mdash; Open Mon&ndash;Sat 7am&ndash;7pm';
1123        badges.forEach(function(badge) {
1124            var statusEl = badge.querySelector('[data-fr-hours-status]');
1125            if (!statusEl) return;
1126            statusEl.innerHTML = open ? openHtml : closedHtml;
1127            badge.classList.toggle('is-closed', !open);
1128        });
1129    }
1130    refresh();
1131    setInterval(refresh, 60 * 1000);
1132})();
1133
1134// FAQ accordion — clicking one open item closes the others in the same group.
1135document.addEventListener('DOMContentLoaded', function() {
1136    document.querySelectorAll('[data-fr-faq-accordion]').forEach(function(group) {
1137        var items = group.querySelectorAll('details.fr-faq-item');
1138        items.forEach(function(item) {
1139            item.addEventListener('toggle', function() {
1140                if (item.open) {
1141                    items.forEach(function(other) {
1142                        if (other !== item) other.open = false;
1143                    });
1144                }
1145            });
1146        });
1147    });
1148});
1149</script>
1149
1150
1151
1152<script>
1153document.addEventListener('DOMContentLoaded', function() {
1154    if (window.location.hash === '#schedule-online') {
1155        const schedulerModal = document.getElementById('scheduler-modal');
1156        if (schedulerModal) schedulerModal.style.display = 'flex';
1157    }
1158});
1159</script>
vendor: 1 bytes, line 1159
1159
1160<script async src="https://maps.googleapis.com/maps/api/js?key=&libraries=places&callback=initAutocomplete"></script>
1160
1161
1162<!-- Whitelisted phone numbers — swap visible phone links to the source-tracked
1163call-tracking number when the visitor's cookie matches a whitelisted number,
1164otherwise default to the main line. updatePhoneLinks() is defined in blog.js
1165(loaded with `defer`), so this call must wait for DOMContentLoaded — by then
1166all deferred scripts have executed and the function is defined. -->
1167<script type='text/javascript'>
1168document.addEventListener('DOMContentLoaded', function() {
1169    const whitelistedNumbers = [4792820003, 4793268236, 4793268255, 4793268239, 4793268240, 4793268245, 4793268247, 4793268250, 4793268248, 4793268251, 4793268252, 4793268253, 4793268254, 4793268256, 4793268257, 4793268258, 4793268259, 4793268260, 4793268262, 4793268264, 4793268517, 4793268518, 4793268636, 4792798231, 4792087666, 4792352329, 4792352347, 4793231590, 4793439974, 4792087520, 4792068196, 4792629561, 4792629675, 4792352892, 4792356299, 4792354278, 4792354695, 4792629766, 4792629748, 4792620593,
1170        4792354761];
1171    const defaultNumber = '4792820003';
1172
1173    const commonOptions = {
1174        whitelistedNumbers: whitelistedNumbers,
1175        defaultNumber: defaultNumber
1176    };
1177
1178    updatePhoneLinks('.number_link', commonOptions);                          // changes link and text
1179    updatePhoneLinks('.number_html', { ...commonOptions, setInnerHTML: false }); // changes link only
1180});
1181</script>
1181
1182
1183<!-- phone_click GTM tracking. Single delegated listener on every tel: link.
1184     Fires once per click (de-duped within 1.5s to ignore double-taps). Pushes
1185     trade + issue_type derived from data-fr-page so value-based bidding can
1186     weight calls from high-value service pages (AC install, panel upgrade)
1187     above low-value ones (drain clear). -->
1188<script>
1189(function() {
1190    // Map of page url_name (data-fr-page) → derived booking context.
1191    // Used to assign a value estimate to phone_click events. Pages not in
1192    // this map fall through to trade='unknown' → DEFAULT lookup value.
1193    var PAGE_CONTEXT = {
1194        // HVAC
1195        ac_repair: { trade: 'hvac', issue_type: 'repair' },
1196        ac_install: { trade: 'hvac', issue_type: 'install' },
1197        ac_maintenance: { trade: 'hvac', issue_type: 'maintenance' },
1198        furnace_repair: { trade: 'hvac', issue_type: 'repair' },
1199        furnace_install: { trade: 'hvac', issue_type: 'install' },
1200        minisplit_install: { trade: 'hvac', issue_type: 'install' },
1201        hvac_inspections: { trade: 'hvac', issue_type: 'repair' },
1202        duct_sealing: { trade: 'hvac', issue_type: 'install' },
1203        iaq: { trade: 'hvac', issue_type: 'repair' },
1204        // Plumbing
1205        plumbing: { trade: 'plumbing', issue_type: 'repair' },
1206        water_heater_install: { trade: 'plumbing', issue_type: 'install' },
1207        water_heater_repair: { trade: 'plumbing', issue_type: 'repair' },
1208        drain_cleaning: { trade: 'plumbing', issue_type: 'repair' },
1209        emergency_plumbing: { trade: 'plumbing', issue_type: 'repair' },
1210        garbage_disposal: { trade: 'plumbing', issue_type: 'repair' },
1211        sewer_line_repair: { trade: 'plumbing', issue_type: 'repair' },
1212        leak_repair: { trade: 'plumbing', issue_type: 'repair' },
1213        water_filtration: { trade: 'plumbing', issue_type: 'install' },
1214        // Electrical
1215        electrical: { trade: 'electrical', issue_type: 'repair' },
1216        panel_upgrade: { trade: 'electrical', issue_type: 'install' },
1217        outlet_installation: { trade: 'electrical', issue_type: 'repair' },
1218        ev_charger_install: { trade: 'electrical', issue_type: 'install' }
1219        // Non-trade pages (index, financing, autopilot, careers, terms,
1220        // privacy, accessibility, service_guarantee, free_second_opinion,
1221        // pricing) intentionally omitted — they get trade='unknown' →
1222        // DEFAULT lookup value.
1223    };
1224
1225    // Infer where on the page the tel: link sits. Pure CSS-class heuristic so
1226    // we don't have to label every anchor in every template. Stable enough
1227    // for GA4 funnel slicing (hero vs. footer vs. mobile_strip vs. ...).
1228    function inferLinkLocation(anchor) {
1229        if (anchor.dataset.frPhoneLocation) return anchor.dataset.frPhoneLocation;
1230        var el = anchor;
1231        for (var i = 0; el && i < 12; i++, el = el.parentElement) {
1232            var cls = el.classList;
1233            if (!cls) continue;
1234            if (cls.contains('fr-schedule-strip')) return 'mobile_strip';
1235            if (cls.contains('fr-hero-ctas') || cls.contains('fr-hero-text')) return 'hero';
1236            if (cls.contains('fr-header')) return 'header';
1237            if (cls.contains('fr-footer')) return 'footer';
1238            if (cls.contains('fr-mobile-drawer__panel')) return 'mobile_drawer';
1239            if (cls.contains('fr-faq')) return 'faq';
1240            if (cls.contains('fr-section--blue')) return 'timeline';
1241            if (cls.contains('fr-why')) return 'why_block';
1242            if (cls.contains('fr-financing')) return 'financing_callout';
1243            if (cls.contains('fr-other-services')) return 'other_services';
1244            if (cls.contains('fr-reviews')) return 'reviews';
1245            if (cls.contains('fr-article')) return 'article';
1246        }
1247        return 'unknown';
1248    }
1249
1250    // Hours helpers are defined globally by base.html (window.frChicagoNow /
1251    // frIsAfterHoursCentral / frIsClosedCentral). They read window.FR_HOURS,
1252    // which is rendered from backoffice/hours.py — single source of truth.
1253    var isAfterHoursCentral = window.frIsAfterHoursCentral || function() { return false; };
1254    var isClosedCentral = window.frIsClosedCentral || function() { return false; };
1255
1256    function randId() {
1257        if (window.crypto && crypto.randomUUID) return crypto.randomUUID();
1258        // Fallback for very old browsers
1259        return 'pc_' + Math.random().toString(36).slice(2) + Date.now().toString(36);
1260    }
1261
1262    // Compute the pre-weight estimated value for a given page slug. Used both
1263    // to update the session-max store on page load AND to compare candidates
1264    // at click time. Returns 0 when frEstimateLeadValue isn't loaded yet.
1265    function pageValueFor(slug) {
1266        var c = PAGE_CONTEXT[slug] || { trade: 'unknown', issue_type: 'unknown' };
1267        return (typeof frEstimateLeadValue === 'function')
1268            ? frEstimateLeadValue(c.trade, c.issue_type)
1269            : 0;
1270    }
1271
1272    // Session-max attribution. Real users often land on a high-value page
1273    // (e.g. /services/ac-install via Google Ads) and then wander to a lower-
1274    // value page (/financing, /autopilot) before tapping the phone link. We
1275    // want bidding credit to go to the highest-intent page they s
1275aw, not
1276    // their last hop. Stored in sessionStorage so it resets cleanly on a new
1277    // visit. Shape: { slug, trade, issue_type, value }.
1278    var SESSION_MAX_KEY = 'fr_session_max_value';
1279    function readSessionMax() {
1280        try {
1281            var raw = sessionStorage.getItem(SESSION_MAX_KEY);
1282            return raw ? JSON.parse(raw) : null;
1283        } catch (e) { return null; }
1284    }
1285    function writeSessionMax(obj) {
1286        try { sessionStorage.setItem(SESSION_MAX_KEY, JSON.stringify(obj)); }
1287        catch (e) { /* private mode / storage quota — silently ignore */ }
1288    }
1289    (function bumpSessionMaxFromCurrentPage() {
1290        var slug = document.body.dataset.frPage || 'unknown';
1291        var ctx = PAGE_CONTEXT[slug] || { trade: 'unknown', issue_type: 'unknown' };
1292        var v = pageValueFor(slug);
1293        var prev = readSessionMax();
1294        if (!prev || v > (prev.value || 0)) {
1295            writeSessionMax({ slug: slug, trade: ctx.trade, issue_type: ctx.issue_type, value: v });
1296        }
1297    })();
1298
1299    var pageLoadedAt = Date.now();
1300    var lastClickAt = 0;  // de-dupe rapid double-taps
1301
1302    document.addEventListener('click', function(e) {
1303        // Walk up from the click target to find the closest tel: anchor
1304        // (clicks often land on a nested <span> or <svg> inside the link).
1305        var target = e.target;
1306        var anchor = null;
1307        while (target && target !== document.body) {
1308            if (target.tagName === 'A' && target.getAttribute('href') && target.getAttribute('href').indexOf('tel:') === 0) {
1309                anchor = target;
1310                break;
1311            }
1312            target = target.parentElement;
1313        }
1314        if (!anchor) return;
1315
1316        // De-dupe double-taps within 1.5s on the same anchor.
1317        var now = Date.now();
1318        if (now - lastClickAt < 1500) return;
1319        lastClickAt = now;
1320
1321        var pageSlug = document.body.dataset.frPage || 'unknown';
1322        var ctx = PAGE_CONTEXT[pageSlug] || { trade: 'unknown', issue_type: 'unknown' };
1323        var clickPageValue = pageValueFor(pageSlug);
1324
1325        // Pick the higher-value context: current click page vs. the highest-
1326        // value page we've seen in this session. If the user landed on AC
1327        // install ($5,000) and tapped the call link from /financing ($300),
1328        // we credit the bid signal to AC install.
1329        var sessionMax = readSessionMax();
1330        var attributedSlug = pageSlug;
1331        var attributedCtx = ctx;
1332        var attributedValue = clickPageValue;
1333        if (sessionMax && (sessionMax.value || 0) > clickPageValue) {
1334            attributedSlug = sessionMax.slug || 'unknown';
1335            attributedCtx = { trade: sessionMax.trade || 'unknown', issue_type: sessionMax.issue_type || 'unknown' };
1336            attributedValue = sessionMax.value || 0;
1337        }
1338
1339        var weight = (window.FR_LEAD_VALUES && window.FR_LEAD_VALUES.phone_intent_weight) || 0.1;
1340        var partialValue = Math.round(attributedValue * weight);
1341        var isSessionMaxPage = (attributedSlug === pageSlug);
1342        var clickId = randId();
1343        var phoneNumber = (anchor.getAttribute('href') || '').replace('tel:', '');
1344        var linkLocation = inferLinkLocation(anchor);
1345        var afterHours = isAfterHoursCentral();
1346        var closed = isClosedCentral();
1347        var timeOnPage = Math.round((now - pageLoadedAt) / 1000);
1348
1349        // GTM event for GA4 / Google Ads value-based bidding.
1350        // `trade` / `issue_type` describe the ATTRIBUTED page (session max).
1351        // `click_page_template` / `click_page_trade` / `click_page_issue_type`
1352        // describe where the click physically happened. Same when the user
1353        // never left the high-value page; different when they wandered.
1354        window.dataLayer = window.dataLayer || [];
1355        window.dataLayer.push({
1356            event: 'phone_click',
1357            page_path: location.pathname,
1358            page_template: attributedSlug,
1359            click_page_template: pageSlug,
1360            click_page_trade: ctx.trade,
1361            click_page_issue_type: ctx.issue_type,
1362            link_location: linkLocation,
1363            phone_number: phoneNumber,
1364            trade: attributedCtx.trade,
1365            issue_type: attributedCtx.issue_type,
1366            is_session_max_page: isSessionMaxPage,
1367            is_after_hours: afterHours,
1368            is_closed: closed,
1369            time_on_page_seconds: timeOnPage,
1370            phone_click_id: clickId,
1371            value: partialValue,
1372            currency: 'USD'
1373        });
1374
1375        // Server-side log so the match_phone_clicks command can pair this
1376        // click against an incoming RingCentral call (no CallRail needed).
1377        // Use sendBeacon when available — fire-and-forget, survives page
1378        // unload (which happens immediately on iOS when the dialer opens).
1379        // Falls back to fetch with keepalive on older browsers.
1380        try {
1381            // Read attribution from cookies/localStorage if fr-attribution.js
1382            // populated them; otherwise from URL params on this nav.
1383            function _read(name) {
1384                try {
1385                    var v = (window.localStorage && localStorage.getItem('fr_' + name)) || '';
1386                    if (v) return v;
1387                } catch (e) {}
1388                var m = document.cookie.match('(?:^|; )' + name + '=([^;]+)');
1389                if (m) return decodeURIComponent(m[1]);
1390                var qs = new URLSearchParams(location.search);
1391                return qs.get(name) || '';
1392            }
1393            var payload = {
1394                phone_click_id: clickId,
1395                page_path: location.pathname,
1396                // page_template reflects the ATTRIBUTED page (session max).
1397                // click_page_template records where the click actually fired.
1398                page_template: attributedSlug,
1399                click_page_template: pageSlug,
1400                link_location: linkLocation,
1401                phone_number: phoneNumber,
1402                trade: attributedCtx.trade,
1403                issue_type: attributedCtx.issue_type,
1404                is_session_max_page: isSessionMaxPage,
1405                is_after_hours: afterHours,
1406                is_closed: closed,
1407                time_on_page_seconds: timeOnPage,
1408                value: partialValue,
1409                gclid: _read('gclid'),
1410                fbclid: _read('fbclid'),
1411                msclkid: _read('msclkid'),
1412                utm_source: _read('utm_source'),
1413                utm_medium: _read('utm_medium'),
1414                utm_campaign: _read('utm_campaign'),
1415                utm_content: _read('utm_content'),
1416                utm_term: _read('utm_term'),
1417                referrer: document.referrer || ''
1418            };
1419            var body = JSON.stringify(payload);
1420
1421            // Fire the same click at both systems.
1422            //
1423            // Backoffice is same-origin and unchanged. FranklinOS is a
1424            // different application on a different domain, and while it
1425            // mirrors ServiceTitan both need the data — sending to one and
1426            // cutting the other over later would leave a hole in whichever
1427            // was not receiving on the day somebody looked.
1428            //
1429            // Content type is the whole trick on the cross-origin one.
1430            // 'text/plain' is one of the three a browser will send without a
1431            // CORS preflight, and a preflight is fatal here: iOS opens the
1432            // dialer and tears this page down in the same instant, so a
1433            // beacon needing a round trip before the real request is a beacon
1434            // that never leaves. The body is still JSON; only the header is
1435            // the shape CORS wants to see.
1436            function post(url, type, sameOrigin) {
1437                var sent = false;
1438                if (navigator.sendBeacon) {
1439                    try {
1440                        sent = navigator.sendBeacon(
1441                            url, new Blob([body], { type: type }));
1442                    } catch (e) { sent = false; }
1443                }
1444                if (!sent && window.fetch) {
1445                    // keepalive, so the request outlives the page. Without it
1446                    // the dialer opening cancels it and the tap is lost —
1447                    // which is every tap on an iPhone.
1448                    fetch(url, {
1449                        method: 'POST',
1450                        body: body,
1451                        headers: { 'Content-Type': type },
1452                        keepalive: true,
1453                        credentials: sameOrigin ? 'same-origin' : 'omit'
1454                    }).catch(function() {});
1455                }
1456            }
1457
1458            post('https://www.franklinos.net/webhooks/phone-click',
1459                 'text/plain', false);
1460        } catch (e) { /* never block the dialer */ }
1461
1462        // Don't preventDefault — let the dialer open as normal.
1463    }, true); // capture phase so we win against any future per-link handlers
1464})();
1465</script>
1465
1466
1467<!-- Header navigation behaviors: services dropdown + mobile drawer -->
1468<script>
1469(function() {
1470    // ---- Services dropdown ----
1471    // Opens on hover (desktop) and click (touch / keyboard).
1472    // A small close delay lets the cursor cross the gap between the toggle
1473    // and the dropdown panel without the menu vanishing.
1474    var HOVER_CLOSE_DELAY = 180;
1475    var hoverSupported = window.matchMedia('(hover: hover) and (pointer: fine)').matches;
1476
1477    document.querySelectorAll('[data-fr-dropdown]').forEach(function(wrap) {
1478        var toggle = wrap.querySelector('[data-fr-dropdown-toggle]');
1479        if (!toggle) return;
1480
1481        var closeTimer = null;
1482        function open() {
1483            if (closeTimer) { clearTimeout(closeTimer); closeTimer = null; }
1484            wrap.classList.add('is-open');
1485            toggle.setAttribute('aria-expanded', 'true');
1486        }
1487        function close() {
1488            wrap.classList.remove('is-open');
1489            toggle.setAttribute('aria-expanded', 'false');
1490        }
1491        function scheduleClose() {
1492            if (closeTimer) clearTimeout(closeTimer);
1493            closeTimer = setTimeout(close, HOVER_CLOSE_DELAY);
1494        }
1495
1496        // Click toggle (works for touch + keyboard).
1497        toggle.addEventListener('click', function(e) {
1498            e.preventDefault();
1499            if (wrap.classList.contains('is-open')) close(); else open();
1500        });
1501
1502        // Hover open/close (desktop pointer only).
1503        if (hoverSupported) {
1504            wrap.addEventListener('mouseenter', open);
1505            wrap.addEventListener('mouseleave', scheduleClose);
1506        }
1507
1508        // Keyboard focus inside the menu keeps it open.
1509        wrap.addEventListener('focusin', open);
1510        wrap.addEventListener('focusout', function(e) {
1511            // If focus leaves the wrap entirely, close.
1512            if (!wrap.contains(e.relatedTarget)) scheduleClose();
1513        });
1514
1515        // Click outside closes
1516        document.addEventListener('click', function(e) {
1517            if (!wrap.contains(e.target)) close();
1518        });
1519        // Escape closes
1520        document.addEventListener('keydown', function(e) {
1521            if (e.key === 'Escape') close();
1522        });
1523    });
1524
1525    // ---- Scroll-elevation on the header ----
1526    var header = document.querySelector('.fr-header');
1527    if (header) {
1528        var onScroll = function() {
1529            if (window.scrollY > 4) header.classList.add('is-scrolled');
1530            else header.classList.remove('is-scrolled');
1531        };
1532        window.addEventListener('scroll', onScroll, { passive: true });
1533        onScroll();
1534    }
1535
1536    // ---- Mobile drawer ----
1537    var drawer = document.getElementById('fr-mobile-drawer');
1538    var menuBtn = document.getElementById('fr-menu-toggle');
1539    if (drawer && menuBtn) {
1540        function openDrawer() {
1541            drawer.classList.add('is-open');
1542            drawer.setAttribute('aria-hidden', 'false');
1543            menuBtn.setAttribute('aria-expanded', 'true');
1544            document.body.style.overflow = 'hidden';
1545        }
1546        function closeDrawer() {
1547            drawer.classList.remove('is-open');
1548            drawer.setAttribute('aria-hidden', 'true');
1549            menuBtn.setAttribute('aria-expanded', 'false');
1550            document.body.style.overflow = '';
1551        }
1552        menuBtn.addEventListener('click', openDrawer);
1553        drawer.querySelectorAll('[data-fr-drawer-close]').forEach(function(el) {
1554            el.addEventListener('click', closeDrawer);
1555        });
1556        drawer.querySelectorAll('[data-fr-drawer-close-on-click]').forEach(function(el) {
1557            el.addEventListener('click', closeDrawer);
1558        });
1559        document.addEventListener('keydown', function(e) {
1560            if (e.key === 'Escape' && drawer.classList.contains('is-open')) closeDrawer();
1561        });
1562    }
1563})();
1564</script>
1564
1565
1566
1567
1568<script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a45728ff3dd3cf03',t:'MTc5MTE0ODA4OA=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>
1568</body>
1569</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.