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 — 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'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've failed ifâ¦</p></p> 581 <a class="fr-blog-card__read-more" href="/blog/why-franklin">Read more →</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'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'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 →</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'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'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's a decent chance â¦</p></p> 605 <a class="fr-blog-card__read-more" href="/blog/heater-blowing-cold-air">Read more →</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 →</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 →</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">»</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 · 5.0 â · 1,361 reviews</span> 662 <h2 class="fr-footer-cta-title">Need service? We’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–Sat</strong> · 7am – 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 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>© 2026 Franklin AC, Plumbing & Electrical. All rights reserved.</span> 751 <span class="fr-footer-licenses">AR HVAC #0003484 · AR Electrical #RM-1111 · 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">×</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 →</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’s currently outside our standard business hours.</h2> 867 <p class="emergency-fee-note">We’re happy to help you right away! Emergency after-hours service is fully available — just note that there’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’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">← Back</button> 883 <button type="button" class="next-btn">Next →</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">← Back</button> 934 <button type="button" class="next-btn">Next →</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">← Back</button> 955 <button type="submit" id="book-appointment-btn">Submit →</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–Fri, 7am–5pm), our team will reach out to confirm your booking within a few minutes.<br><br> 965 <strong>After hours</strong> (Mon–Fri 5pm–7pm, or Saturday 7am–7pm), our on-call dispatcher will follow up shortly if you selected emergency service; otherwise we’ll be in touch first thing the next business day.<br><br> 966 <strong>Overnight or on Sunday</strong>, we’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 · Mon–Sat 7am–7pm'; 1122 var closedHtml = 'CLOSED — Open Mon–Sat 7am–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.