PageSourceSearch

https://www.jtnelectrical.com/request-service-form

html jtnelectrical.com collected 2026-10-09 01:37:17 UTC 433,350 bytes, 10,623 lines download raw bytes

1<!doctype html >
2<html xmlns="http://www.w3.org/1999/xhtml" lang="en"
3      class="ios-preview-native-scroll">
4<head>
5    <meta charset="utf-8">
6    
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43<script type="text/javascript">
44    window._currentDevice = 'desktop';
45    window.Parameters = window.Parameters || {
46        HomeUrl: 'https://www.jtnelectrical.com/',
47
48        SystemID: 'US_HIBU_PRODUCTION',
49        SiteAlias: 'f6cdfabba1c749a1a3d3b7a9f4c30c76',
50        SiteType: atob('RFVEQU9ORQ=='),
51        PublicationDate: 'Oct 07, 2026',
52        ExternalUid: '700161096',
53        IsSiteMultilingual: false,
54        InitialPostAlias: '',
55        InitialPostPageUuid: '',
56        InitialDynamicItem: '',
57        DynamicPageInfo: {
58            isDynamicPage: false,
59            base64JsonRowData: 'null',
60        },
61        InitialPageAlias: 'request-service-form',
62        InitialPageUuid: '8d67b06083f34b4a8a328c95f017fec2',
63        InitialPageId: '117870664',
64        InitialEncodedPageAlias: 'cmVxdWVzdC1zZXJ2aWNlLWZvcm0=',
65        InitialHeaderUuid: '3dd8cf0da1894ab5a9043c416f0c4aca',
66        CurrentPageUrl: '',
67        IsCurrentHomePage: false,
68        ThemeVersion: '500000',
69        DefaultPageAlias: '',
70        RemoveDID: true,
71        WidgetStyleID: null,
72        IsHeaderFixed: false,
73        IsHeaderSkinny: false,
74        StorePagesUrls: 'e30=',
75        IsNewStore: 'false',
76        StorePath: '',
77        StoreId: 'null',
78        StoreVersion: 0,
79        StoreBaseUrl: '',
80        StoreCleanUrl: true,
81        StoreDisableScrolling: true,
82        IsStoreSuspended: false,
83        showCookieNotification: false,
84        cookiesNotificationMarkup: 'null',
85        translatedPageUrl: '',
86        sidebarPosition: 'NA',
87        currentLanguage: 'en',
88        currentLocale: 'en',
89        NavItems: '{}',
90        
91        mapConsent: {
92            message: 'This content is served by a third party, {0}. If enabled, {0} may collect information about your activity.',
93            enable: 'Enable'
94        },
95        NavigationAreaParams: {
96            ShowBackToHomeOnInnerPages: true,
97            NavbarSize: -1,
98            NavbarLiveHomePage: 'https://www.jtnelectrical.com/',
99            NavbarSelector: '#dmNav:has(a)',
100            SubNavbarSelector: '#subnav_main'
101        },
102        planID: '770',
103        disableTracking: false,
104        pageType: 'FROM_SCRATCH',
105        isRuntimeServer: true,
106        isInEditor: false,
107        isInPreview: false,
108        hasNativeStore: false,
109        defaultLang: 'en',
110        hamburgerMigration: null,
111        isFlexSite: true
112    };
113
114    window.Parameters.LayoutID = {};
115    window.Parameters.LayoutID[window._currentDevice] = 30;
116    window.Parameters.LayoutVariationID = {};
117    window.Parameters.LayoutVariationID[window._currentDevice] = 5;
118</script>
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141<!-- Injecting site-wide to the head -->
142
143
144
145
146
147<!-- End Injecting site-wide to the head -->
148
149
150
151
152
153<meta name="d-service-worker" content="register">
154
155
156
157<link rel="canonical" href="https://www.jtnelectrical.com/request-service-form">
158
159<meta id="view" name="viewport" content="initial-scale=1, minimum-scale=1, maximum-scale=5, viewport-fit=cover">
160
161<meta name="apple-mobile-web-app-capable" content="yes">
162
163
164
165<link rel="icon" type="image/x-icon" href="https://cdn.hibuwebsites.com/f6cdfabba1c749a1a3d3b7a9f4c30c76/site_favicon_16_1790751430838.ico"/>
166
167
168<link rel="preconnect" href="https://le-cdn.hibuwebsites.com"/>
169<link rel="preconnect" href="https://cdn.hibuwebsites.com" crossorigin/>
170<link rel="preconnect" href="https://static-res-cdn.websites.hibu.com"/>
171<link rel="preconnect" href="https://cdn.hibuwebsites.com"/>
172
173
174
175
176<script id='d-js-dmapi'>
177    window.SystemID = 'US_HIBU_PRODUCTION';
178
179    if (!window.dmAPI) {
180        window.dmAPI = {
181            registerExternalRuntimeComponent: function () {
182            },
183            getCurrentDeviceType: function () {
184                return window._currentDevice;
185            },
186            runOnReady: (ns, fn) => {
187                const safeFn = dmAPI.toSafeFn(fn);
188                ns = ns || 'global_' + Math.random().toString(36).slice(2, 11);
189                const eventName = 'afterAjax.' + ns;
190
191                if (document.readyState === 'complete') {
192                    $.DM.events.off(eventName).on(eventName, safeFn);
193                    setTimeout(function () {
194                        safeFn({
195                            isAjax: false,
196                        });
197                    }, 0);
198                } else {
199                    window?.waitForDeferred?.('dmAjax', () => {
200                        $.DM.events.off(eventName).on(eventName, safeFn);
201                        safeFn({
202                            isAjax: false,
203                        });
204                    });
205                }
206            },
207            toSafeFn: (fn) => {
208                if (fn?.safe) {
209                    return fn;
210                }
211                const safeFn = function (...args) {
212                    try {
213                        return fn?.apply(null, args);
214                    } catch (e) {
215                        console.log('function failed ' + e.message);
216                    }
217                };
218                safeFn.safe = true;
219                return safeFn;
220            }
221        };
222    }
223
224    if (!window.requestIdleCallback) {
225        window.requestIdleCallback = function (fn) {
226            setTimeout(fn, 0);
227        }
228    }
229</script>
229
230
231
232
233
234
235
236
237
238<script id="d-js-load-css">
239/**
240 * There are a few <link> tags with CSS resource in them that are preloaded in the page
241 * in each of those there is a "onload" handler which invokes the loadCSS callback
242 * defined here.
243 * We are monitoring 3 main CSS files - the runtime, the global and the page.
244 * When each load we check to see if we can append them all in a batch. If threre
245 * is no page css (which may happen on inner pages) then we do not wait for it
246 */
247(function () {
248  let cssLinks = {};
249  function loadCssLink(link) {
250    link.onload = null;
251    link.rel = "stylesheet";
252    link.type = "text/css";
253  }
254  
255    function checkCss() {
256      const pageCssLink = document.querySelector("[id*='CssLink']");
257      const widgetCssLink = document.querySelector("[id*='widgetCSS']");
258
259        if (cssLinks && cssLinks.runtime && cssLinks.global && (!pageCssLink || cssLinks.page) && (!widgetCssLink || cssLinks.widget)) {
260            const storedRuntimeCssLink = cssLinks.runtime;
261            const storedPageCssLink = cssLinks.page;
262            const storedGlobalCssLink = cssLinks.global;
263            const storedWidgetCssLink = cssLinks.widget;
264
265            storedGlobalCssLink.disabled = true;
266            loadCssLink(storedGlobalCssLink);
267
268            if (storedPageCssLink) {
269                storedPageCssLink.disabled = true;
270                loadCssLink(storedPageCssLink);
271            }
272
273            if(storedWidgetCssLink) {
274                storedWidgetCssLink.disabled = true;
275                loadCssLink(storedWidgetCssLink);
276            }
277
278            storedRuntimeCssLink.disabled = true;
279            loadCssLink(storedRuntimeCssLink);
280
281            requestAnimationFrame(() => {
282                setTimeout(() => {
283                    storedRuntimeCssLink.disabled = false;
284                    storedGlobalCssLink.disabled = false;
285                    if (storedPageCssLink) {
286                      storedPageCssLink.disabled = false;
287                    }
288                    if (storedWidgetCssLink) {
289                      storedWidgetCssLink.disabled = false;
290                    }
291                    // (SUP-4179) Clear the accumulated cssLinks only when we're
292                    // sure that the document has finished loading and the document 
293                    // has been parsed.
294                    if(document.readyState === 'interactive') {
295                      cssLinks = null;
296                    }
297                }, 0);
298            });
299        }
300    }
301  
302
303  function loadCSS(link) {
304    try {
305      var urlParams = new URLSearchParams(window.location.search);
306      var noCSS = !!urlParams.get("nocss");
307      var cssTimeout = urlParams.get("cssTimeout") || 0;
308
309      if (noCSS) {
310        return;
311      }
312      if (link.href && link.href.includes("d-css-runtime")) {
313        cssLinks.runtime = link;
314        checkCss();
315      } else if (link.id === "siteGlobalCss") {
316        cssLinks.global = link;
317        checkCss();
318      } 
319      
320      else if (link.id && link.id.includes("CssLink")) {
321        cssLinks.page = link;
322        checkCss();
323      } else if (link.id && link.id.includes("widgetCSS")) {
324        cssLinks.widget = link;
325        checkCss();
326      }
327      
328      else {
329        requestIdleCallback(function () {
330          window.setTimeout(function () {
331            loadCssLink(link);
332          }, parseInt(cssTimeout, 10));
333        });
334      }
335    } catch (e) {
336      throw e
337    }
338  }
339  window.loadCSS = window.loadCSS || loadCSS;
340})();
341</script>
341
342
343
344
345<script data-role="deferred-init" type="text/javascript">
346    /* usage: window.getDeferred(<deferred name>).resolve() or window.getDeferred(<deferred name>).promise.then(...)*/
347    function Def() {
348        this.promise = new Promise((function (a, b) {
349            this.resolve = a, this.reject = b
350        }).bind(this))
351    }
352
353    const defs = {};
354    window.getDeferred = function (a) {
355        return null == defs[a] && (defs[a] = new Def), defs[a]
356    }
357    window.waitForDeferred = function (b, a, c) {
358        let d = window?.getDeferred?.(b);
359        d
360            ? d.promise.then(a)
361            : c && ["complete", "interactive"].includes(document.readyState)
362                ? setTimeout(a, 1)
363                : c
364                    ? document.addEventListener("DOMContentLoaded", a)
365                    : console.error(`Deferred  does not exist`);
366    };
367</script>
367
368<style id="forceCssIncludes">
369    /* This file is auto-generated from a `scss` file with the same name */
370
371.videobgwrapper{overflow:hidden;position:absolute;z-index:0;width:100%;height:100%;top:0;left:0;pointer-events:none;border-radius:inherit}.videobgframe{position:absolute;width:101%;height:100%;top:50%;left:50%;transform:translateY(-50%) translateX(-50%);object-fit:fill}#dm video.videobgframe{margin:0}@media (max-width:767px){.dmRoot .dmPhotoGallery.newPhotoGallery:not(.photo-gallery-done){min-height:80vh}}@media (min-width:1025px){.dmRoot .dmPhotoGallery.newPhotoGallery:not(.photo-gallery-done){min-height:45vh}}@media (min-width:768px) and (max-width:1024px){.responsiveTablet .dmPhotoGallery.newPhotoGallery:not(.photo-gallery-done){min-height:45vh}}#dm [data-show-on-page-only]{display:none!important}#dmRoot div.stickyHeaderFix div.site_content{margin-top:0!important}#dmRoot div.stickyHeaderFix div.hamburger-header-container{position:relative}@media (min-width:768px) and (max-width:1024px){.responsiveTablet #dm .dmInner .hide-for-medium,[data-hidden-on-tablet]{display:none!important}}
372    
373
374</style>
375<style id="cssVariables" type="text/css">
376    :root {
377  --color_1: rgba(0, 0, 0, 1);
378  --color_10: rgba(52, 73, 135, 1);
379  --color_11: rgba(255, 255, 255, 1);
380  --color_12: rgba(204, 54, 79, 1);
381  --color_13: rgba(52, 73, 135, 1);
382  --color_14: rgba(255, 200, 52, 1);
383  --color_15: rgba(52, 73, 135, 1);
384  --color_16: rgba(255, 255, 255, 1);
385  --color_17: rgba(255, 255, 255, 1);
386  --color_18: rgba(52, 73, 135, 1);
387  --color_19: rgba(255, 255, 255, 1);
388  --color_2: rgba(255, 255, 255, 1);
389  --color_20: rgba(204, 54, 79, 1);
390  --color_21: rgba(255, 255, 255, 1);
391  --color_22: rgba(255, 255, 255, 1);
392  --color_3: rgba(52, 73, 135, 1);
393  --color_4: rgba(204, 54, 79, 1);
394  --color_5: rgba(255, 255, 255, 1);
395  --color_6: rgba(52, 73, 135, 1);
396  --color_7: rgba(255, 255, 255, 1);
397  --color_8: rgba(255, 255, 255, 1);
398  --color_9: rgba(0, 0, 0, 1);
399  --max-content-width_1: 1200px;
400}
401</style>
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445<link type="text/css" rel="stylesheet" href="https://cdn.hibuwebsites.com/fonts/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Be+Vietnam:ital,wght@0,100;0,200;0,300;0,400;0,600;0,700;0,800;0,900&family=Kumbh+Sans:ital,wght@0,100..900;1,100..900&family=Racing+Sans+One:ital,wght@0,400&amp;subset=latin-ext&amp;display=swap"  />
446
447
448
449
450
451
452<link rel="stylesheet" type="text/css" href="https://static-res-cdn.websites.hibu.com/mnlt/production/6835/_dm/s/rt/dist/css/d-css-runtime-flex-new-nav.min.css" />
453
454
455
456<link type="text/css" rel="stylesheet" href="https://cdn.hibuwebsites.com/WIDGET_CSS/f7506721e8d798cf78d4fdf7a9a1e5aa.css" id="widgetCSS" />
457
458
459<style>img[width][height] {
460  height: auto;
461}</style>
462
463
464<style>
465  body[data-page-alias="request-service-form"] #dm [data-show-on-page-only="request-service-form"] {
466    display: block !important;
467  }
468</style>
469
470
471<style id="pageAdditionalWidgetsCss" type="text/css">
472</style>
473
474
475
476
477
478<link type="text/css" rel="stylesheet" href="https://cdn.hibuwebsites.com/f6cdfabba1c749a1a3d3b7a9f4c30c76/files/f6cdfabba1c749a1a3d3b7a9f4c30c76_1.min.css?v=da826725d62916479411bfcc83d35978" id="siteGlobalCss" />
479
480
481
482<link
483        type="text/css" rel="stylesheet"
484        href="/_dm/s/rt/css/hibu/hibu-runtime.css?version=2026-10-07T13_19_14"
485/>
486
487
488<style id="customWidgetStyle" type="text/css">
489    
490</style>
491<style id="innerPagesStyle" type="text/css">
492    
493</style>
494
495
496<style
497        id="additionalGlobalCss" type="text/css"
498>
499</style>
500
501<style id="pagestyle" type="text/css">
502    @media all {
503  *#dm .dmInner nav.u_1998592992 { color:black !important; }
504
505  *#dm .dmInner div.u_1638807636 .dmforminput input {
506    font-size:14px !important;
507    color:rgba(0,0,0,1) !important;
508  }
509
510  *#dm .dmInner div.u_1638807636 .dmforminput textarea {
511    font-size:14px !important;
512    color:rgba(0,0,0,1) !important;
513  }
514
515  *#dm .dmInner div.u_1638807636 .dmforminput .fileUploadLink {
516    font-size:14px !important;
517    color:rgba(0,0,0,1) !important;
518  }
519
520  *#dm .dmInner div.u_1638807636 .checkboxwrapper span {
521    font-size:14px !important;
522    color:rgba(0,0,0,1) !important;
523  }
524
525  *#dm .dmInner div.u_1638807636 .radiowrapper span {
526    font-size:14px !important;
527    color:rgba(0,0,0,1) !important;
528  }
529
530  *#dm .dmInner div.u_1638807636 .optinwrapper div {
531    font-size:14px !important;
532    color:rgba(0,0,0,1) !important;
533  }
534
535  *#dm .dmInner div.u_1638807636 .freetextwrapper {
536    font-size:14px !important;
537    color:rgba(0,0,0,1) !important;
538  }
539
540  *#dm .dmInner div.u_1638807636 .dmforminput select {
541    font-size:14px !important;
542    color:rgba(0,0,0,1) !important;
543  }
544
545  *#dm .dmInner div.u_1638807636 .dmforminput option {
546    font-size:14px !important;
547    color:rgba(0,0,0,1) !important;
548  }
549
550  *#dm .dmInner div.u_1638807636 .dmforminput label:not(.for-checkable):not(.custom-contact-checkable) {
551    font-weight:700 !important;
552    font-size:14px !important;
553  }
554
555  *#dm .dmInner div.u_1638807636 .m-recaptcha {
556    font-weight:700 !important;
557    font-size:14px !important;
558  }
559
560  *#dm .dmInner div.u_1638807636 input:not([type="submit"]) {
561    background-color:rgba(255,255,255,1) !important;
562    background-image:initial !important;
563    border-style:solid !important;
564    border-color:var(--color_1) var(--color_1) var(--color_1) var(--color_1) !important;
565    border-width:0px 0px 1px 0px !important;
566  }
567
568  *#dm .dmInner div.u_1638807636 textarea {
569    background-color:rgba(255,255,255,1) !important;
570    background-image:initial !important;
571    border-style:solid !important;
572    border-color:var(--color_1) var(--color_1) var(--color_1) var(--color_1) !important;
573    border-width:0px 0px 1px 0px !important;
574  }
575
576  *#dm .dmInner div.u_1638807636 select {
577    background-color:rgba(255,255,255,1) !important;
578    background-image:initial !important;
579    border-style:solid !important;
580    border-color:var(--color_1) var(--color_1) var(--color_1) var(--color_1) !important;
581    border-width:0px 0px 1px 0px !important;
582  }
583
584  *#dm .dmInner div.u_1638807636 .dmformsubmit {
585    margin-left:0 !important;
586    margin-right:auto !important;
587    height:50px !important;
588    float:left !important;
589    display:block !important;
590    width:250px !important;
591  }
592
593  *#dm .dmInner div.u_1359807854 p {
594    color:var(--color_22) !important;
595    direction:ltr !important;
596    text-transform:none !important;
597    text-shadow:none !important;
598    font-weight:bold !important;
599    text-decoration:rgb(255,255,255) !important;
600    letter-spacing:normal !important;
601    font-family:Poppins !important;
602    line-height:normal !important;
603    font-style:normal !important;
604    text-align:left !important;
605  }
606
607  *#dm .dmInner div.u_1359807854 li {
608    color:var(--color_22) !important;
609    direction:ltr !important;
610    text-transform:none !important;
611    text-shadow:none !important;
612    font-weight:bold !important;
613    text-decoration:rgb(255,255,255) !important;
614    letter-spacing:normal !important;
615    font-family:Poppins !important;
616    line-height:normal !important;
617    font-style:normal !important;
618    text-align:left !important;
619  }
620
621  *#dm .dmInner div.u_1359807854 {
622    color:var(--color_22) !important;
623    direction:ltr !important;
624    text-transform:none !important;
625    text-shadow:none !important;
626    font-weight:bold !important;
627    text-decoration:rgb(255,255,255) !important;
628    letter-spacing:normal !important;
629    font-family:Poppins !important;
630    line-height:normal !important;
631    font-style:normal !important;
632    text-align:left !important;
633  }
634}
635
636@media (min-width:0) and (max-width:767px) {
637  *#dm .dmInner div.u_1359807854 p { font-size:18px !important; }
638
639  *#dm .dmInner div.u_1359807854 li { font-size:18px !important; }
640
641  *#dm .dmInner div.u_1359807854 { font-size:18px !important; }
642}
643
644@media (min-width:1025px) {
645  *#dm .dmInner div.u_1359807854 p { font-size:20px !important; }
646
647  *#dm .dmInner div.u_1359807854 li { font-size:20px !important; }
648
649  *#dm .dmInner div.u_1359807854 { font-size:20px !important; }
650}
651
652@media (min-width:768px) and (max-width:1024px) {
653  *#dm .dmInner div.u_1359807854 p { font-size:20px !important; }
654
655  *#dm .dmInner div.u_1359807854 li { font-size:20px !important; }
656
657  *#dm .dmInner div.u_1359807854 { font-size:20px !important; }
658}
659
660</style>
661
662<style id="pagestyleDevice" type="text/css">
663    
664</style>
665
666
667
668 <style data-page-uuid="8d67b06083f34b4a8a328c95f017fec2" id="grid-styles-common-24b6b13b-8d67b06083f34b4a8a328c95f017fec2">@media all {
669    
670#\36 3c7b8b76c596a2813341bc5 {
671    height: auto;
672}
673    
674#\31 3869723 {
675    width: 100%;
676    height: auto;
677    max-width: 100%;
678}
679    
680#d4329249 {
681    min-height: 8px;
682    column-gap: 4%;
683    row-gap: 0px;
684    width: 100%;
685    min-width: 4%;
686    background-color: rgba(0, 0, 0, 0);
687    padding: 0% 0% 0% 0%;
688    margin: 0% 0% 0% 0%;
689}
690    
691#b3d39244 {
692    width: 100%;
693    height: auto;
694    max-width: 100%;
695    margin-bottom: -4px;
696}
697    
698#\35 30811d6 {
699    border-width: 0px;
700    border-color: rgba(0, 0, 0, 1);
701    border-style: solid;
702    background-color: var(--color_12);
703    background-image: initial;
704}
705    
706#\33 4c91571 {
707    min-height: 100px;
708    column-gap: 4%;
709    row-gap: 24px;
710    width: 100%;
711    flex-wrap: nowrap;
712    max-width: none;
713    padding: 0% 4% 4px 4%;
714    margin: 0% 0% 0% 0%;
715}
716}</style> 
717 <style data-page-uuid="8d67b06083f34b4a8a328c95f017fec2" id="grid-styles-common-7ef32d84-8d67b06083f34b4a8a328c95f017fec2">@media all {
718    
719#\37 9342a6b {
720    width: 100%;
721    height: auto;
722    max-width: 100%;
723    justify-self: auto;
724    align-self: auto;
725    order: 0;
726    grid-area: auto / auto / auto / auto;
727}
728    
729#\36 a108e83 {
730    min-height: 0px;
731    column-gap: 4%;
732    row-gap: 24px;
733    width: 100%;
734    flex-wrap: nowrap;
735    max-width: none;
736    padding: 10px 4% 10px 4%;
737    margin-left: 0%;
738    margin-top: 0%;
739    margin-right: 0%;
740    margin-bottom: 0%;
741}
742    
743#\38 c30d83a {
744    min-height: 8px;
745    column-gap: 4%;
746    row-gap: 24px;
747    width: 100%;
748    min-width: 4%;
749    padding-top: 0%;
750    padding-right: 0%;
751    padding-bottom: 0%;
752    padding-left: 0%;
753    margin-left: 0%;
754    margin-top: 0%;
755    margin-right: 0%;
756    margin-bottom: 0%;
757}
758}</style> 
759 <style data-page-uuid="8d67b06083f34b4a8a328c95f017fec2" id="grid-styles-common-5c4456af-8d67b06083f34b4a8a328c95f017fec2">@media all {
760    
761#\35 20e9609 {
762    min-height: 240px;
763    column-gap: 4%;
764    row-gap: 24px;
765    width: 100%;
766    flex-wrap: nowrap;
767    padding: 1% 0% 4% 0%;
768    margin: 0% 0% 0% 0%;
769}
770    
771#ffec1cdf {
772    width: 91%;
773    height: auto;
774    max-width: 100%;
775    justify-self: auto;
776    align-self: auto;
777    order: 0;
778    margin-bottom: 0px;
779    margin-right: 6%;
780    margin-left: 6%;
781    grid-area: auto / auto / auto / auto;
782}
783    
784#\36 8fb7ec9 {
785    min-height: 8px;
786    column-gap: 4%;
787    row-gap: 24px;
788    width: 100%;
789    min-width: 4%;
790    padding: 16px 40px 16px 40px;
791    margin: 0% 0% 0% 0%;
792}
793    
794#b6321765 {
795    align-items: flex-start;
796    width: 100%;
797    min-height: 160px;
798    max-height: 100%;
799    row-gap: 24px;
800    column-gap: 4%;
801    background-color: rgba(255, 255, 255, 1);
802    background-image: initial;
803    box-shadow: rgba(0, 0, 0, 0.25) 3px 0px 11px 0px;
804    border-radius: 30px 30px 30px 30px;
805    justify-self: auto;
806    align-self: auto;
807    order: 0;
808    padding: 40px 0% 40px 0%;
809    margin: 0% 0% 0% 0%;
810    grid-area: auto / auto / auto / auto;
811}
812    
813#\39 8f8bad2 {
814    width: 88%;
815    height: auto;
816    max-width: 100%;
817    order: 1;
818    justify-self: auto;
819    align-self: center;
820    grid-area: auto / auto / auto / auto;
821}
822}</style> 
823 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-common-flex-footer-3dd8cf0da1894ab5a9043c416f0c4aca">@media all {
824    
825#flex-footer {
826    background-color: var(--color_3);
827    margin-left: 0px;
828    padding-top: 0px;
829    padding-left: 0px;
830    padding-bottom: 0px;
831    margin-top: 0px;
832    margin-bottom: 0px;
833    margin-right: 0px;
834    padding-right: 0px;
835}
836}</style> 
837 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-common-flex-header-3dd8cf0da1894ab5a9043c416f0c4aca">@media all {
838    
839#flex-header[data-changeable-on-scroll][data-scrolling] [data-widget-type="image"] img {
840    transform: scale(1);
841}
842#flex-header {
843    background-color: var(--color_6);
844    
845    box-shadow: rgba(0, 0, 0, 0.25) 0px 0px 11px 0px;
846    background-image: initial;
847    border-style: solid;
848    border-color: var(--color_10) #000000 rgba(203, 203, 203, 1) #000000;
849    border-width: 4px 0px 0px 0px;
850    padding: 0% 0% 0% 0%;
851}
852#flex-header[data-changeable-on-scroll] {
853    --header-on-scroll-text-color: var(--color_7);
854}
855#flex-header[data-changeable-on-scroll][data-scrolling] {
856    padding-top: 0px;
857    padding-bottom: 0px;
858    background-color: var(--color_6);
859    background-image: initial;
860}
861}</style> 
862 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-common-405ddf11-3dd8cf0da1894ab5a9043c416f0c4aca">@media all {
863    
864#\31 d3acdb9 {
865    min-height: 80px;
866    column-gap: 0%;
867    row-gap: 0px;
868    width: 100%;
869    flex-wrap: nowrap;
870    padding: 0% 2% 0% 2%;
871    margin: 0% 0% 0% 0%;
872}
873    
874#\39 f3f031f {
875    min-height: 8px;
876    column-gap: 4%;
877    row-gap: 24px;
878    flex-direction: row;
879    width: 100%;
880    justify-content: flex-start;
881    align-items: center;
882    min-width: 4%;
883    background-color: rgba(0, 0, 0, 0);
884    background-image: initial;
885    order: 0;
886    padding: 16px 16px 16px 16px;
887    margin: 0% 0% 0% 0%;
888}
889    
890#\31 1d216b4 {
891    flex-direction: row;
892    justify-content: flex-start;
893    align-items: center;
894    width: 33%;
895    min-height: 75px;
896    max-height: 100%;
897    row-gap: 24px;
898    column-gap: 4%;
899    order: 0;
900    padding: 0% 0% 0% 0%;
901    margin: 0% 0% 0% 0%;
902}
903    
904#a052d7aa {
905    flex-direction: row;
906    justify-content: flex-end;
907    align-items: center;
908    width: 50%;
909    min-height: 60px;
910    max-height: 100%;
911    align-self: auto;
912    order: 1;
913    justify-self: auto;
914    padding: 0% 4% 0% 0%;
915    margin: 0% 0% 0% 0%;
916    grid-area: auto / auto / auto / auto;
917}
918    
919#\30 7327faa {
920    width: 100%;
921    height: 8px;
922    max-width: 100%;
923}
924    
925#\39 e5ef93d {
926    flex-direction: row;
927    align-items: center;
928    width: 50%;
929    min-height: 40px;
930    max-height: 100%;
931    order: 0;
932    justify-self: auto;
933    align-self: auto;
934    padding: 0% 0% 0% 4%;
935    margin: 0% 0% 0% 0%;
936    grid-area: auto / auto / auto / auto;
937}
938    
939#\35 801a4e8 svg {
940    fill: var(--color_7);
941}
942#\35 801a4e8 {
943    width: 30px;
944    height: 30px;
945    max-width: 100%;
946}
947    
948#\33 aa37ebb {
949    flex-direction: row;
950    justify-content: flex-end;
951    align-items: center;
952    width: 33%;
953    min-height: 75px;
954    max-height: 100%;
955    row-gap: 24px;
956    column-gap: 4%;
957    order: 2;
958    justify-self: auto;
959    align-self: auto;
960    padding: 0% 0% 0% 0%;
961    margin: 0% 0% 0% 0%;
962    grid-area: auto / auto / auto / auto;
963}
964    
965#\30 ece3d12 {
966    flex-direction: row;
967    justify-content: flex-start;
968    align-items: center;
969    width: 50%;
970    min-height: 60px;
971    max-height: 100%;
972    order: 0;
973    padding: 0% 0% 0% 4%;
974    margin: 0% 0% 0% 0%;
975}
976    
977#daf81bcc {
978    width: 280px;
979    height: auto;
980    max-width: 100%;
981}
982    
983#\31 48da2d9 {
984    flex-direction: row;
985    justify-content: space-between;
986    align-items: flex-start;
987    width: 100%;
988    min-height: 50px;
989    max-height: 100%;
990    order: 2;
991    justify-self: auto;
992    align-self: auto;
993    background-color: rgba(0, 0, 0, 0);
994    background-image: initial;
995    padding: 0% 0% 0% 0%;
996    margin: 0px 0% 0px 0%;
997    grid-area: auto / auto / auto / auto;
998}
999    
1000#ec5d46f2 {
1001    width: auto;
1002    height: auto;
1003    max-width: 100%;
1004}
1005    
1006#bff1bb66 {
1007    width: 180px;
1008    height: auto;
1009    max-width: 100%;
1010    margin: 0px 0% 0px 0%;
1011}
1012    
1013#\34 cb86e4c {
1014    flex-direction: row;
1015    align-items: flex-start;
1016    width: 100%;
1017    min-height: 40px;
1018    max-height: 100%;
1019    order: 0;
1020    justify-self: auto;
1021    align-self: center;
1022    background-color: rgba(0, 0, 0, 0);
1023    background-image: initial;
1024    padding: 0% 0% 0% 0%;
1025    margin: 0px 0% 0px 0%;
1026    grid-area: auto / auto / auto / auto;
1027}
1028    
1029#df845449 {
1030    width: 250px;
1031    height: auto;
1032    max-width: 100%;
1033}
1034    
1035#\39 8702d80 {
1036    align-items: flex-start;
1037    width: 100%;
1038    min-height: 230px;
1039    max-height: 100%;
1040    row-gap: 0px;
1041    column-gap: 4%;
1042    justify-self: auto;
1043    align-self: auto;
1044    order: 0;
1045    padding: 0% 0% 0% 0%;
1046    margin: 0px 0% 0px 0%;
1047    grid-area: auto / auto / auto / auto;
1048}
1049    
1050#da0131a7 {
1051    align-items: center;
1052    width: 35.64%;
1053    min-height: 90px;
1054    max-height: 100%;
1055    align-self: auto;
1056    justify-self: auto;
1057    order: 1;
1058    flex-wrap: nowrap;
1059    background-color: var(--color_2);
1060    background-image: none;
1061    border-radius: 10px 10px 10px 10px;
1062    padding: 0% 0% 0% 0%;
1063    margin: 0% 0% 0% 0%;
1064    grid-area: auto / auto / auto / auto;
1065}
1066    
1067#\32 e9284f4 {
1068    flex-direction: row;
1069    align-items: flex-start;
1070    width: 100%;
1071    min-height: 90px;
1072    max-height: 100%;
1073    justify-self: auto;
1074    align-self: auto;
1075    order: 1;
1076    flex-wrap: nowrap;
1077    padding: 0% 1% 0% 1%;
1078    margin: 0% 0% 0% 0%;
1079    grid-area: auto / auto / auto / auto;
1080}
1081}</style> 
1082 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-common-157b4fff-3dd8cf0da1894ab5a9043c416f0c4aca">@media all {
1083    
1084#\32 fd7b414 {
1085    min-height: 8px;
1086    column-gap: 4%;
1087    row-gap: 24px;
1088    flex-direction: row;
1089    width: 25%;
1090    justify-content: flex-start;
1091    align-items: center;
1092    min-width: 4%;
1093    background-color: rgba(0, 0, 0, 0);
1094    background-image: initial;
1095    padding: 16px 16px 16px 16px;
1096    margin: 0% 0% 0% 0%;
1097}
1098    
1099#\33 2767c2f {
1100    min-height: 80px;
1101    column-gap: 0%;
1102    row-gap: 0px;
1103    width: 100%;
1104    flex-wrap: nowrap;
1105    padding: 0% 2% 0% 2%;
1106    margin: 0% 0% 0% 0%;
1107}
1108    
1109#group_7in {
1110    flex-direction: row;
1111    justify-content: flex-start;
1112    align-items: center;
1113    width: 25%;
1114    min-height: 8px;
1115    padding: 0% 0% 0% 0%;
1116    margin: 0% 0% 0% 0%;
1117}
1118    
1119#group_jjh {
1120    flex-direction: row;
1121    justify-content: flex-start;
1122    align-items: center;
1123    width: 25%;
1124    min-height: 8px;
1125    background-color: var(--color_2);
1126    background-image: none;
1127    border-radius: 10px 10px 10px 10px;
1128    padding: 0% 0% 0% 0%;
1129    margin: 0% 0% 0% 0%;
1130}
1131    
1132#widget_ie6 {
1133    width: auto;
1134    height: auto;
1135    max-width: 100%;
1136}
1137    
1138#widget_5h5 {
1139    width: 80px;
1140    height: auto;
1141    max-width: 100%;
1142}
1143    
1144#widget_vej {
1145    width: 280px;
1146    height: auto;
1147    max-width: 100%;
1148}
1149    
1150#group_23b {
1151    flex-direction: row;
1152    justify-content: flex-start;
1153    align-items: center;
1154    width: 25%;
1155    min-height: 8px;
1156    padding: 0% 0% 0% 0%;
1157    margin: 0% 0% 0% 0%;
1158}
1159    
1160#\34 598469b svg {
1161    fill: var(--color_7);
1162}
1163#\34 598469b {
1164    width: 30px;
1165    height: 30px;
1166    max-width: 100%;
1167    justify-self: auto;
1168    align-self: auto;
1169    order: 0;
1170    margin: 0px 0% 0px 0%;
1171    grid-area: auto / auto / auto / auto;
1172}
1173    
1174#group_tbn {
1175    flex-direction: row;
1176    justify-content: flex-start;
1177    align-items: center;
1178    padding: 0% 0% 0% 0%;
1179    margin: 0% 0% 0% 0%;
1180}
1181    
1182#widget_qp9 {
1183    width: 100%;
1184    height: 200px;
1185    max-width: 100%;
1186}
1187}</style> 
1188 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-common-c0ae99ba-3dd8cf0da1894ab5a9043c416f0c4aca">@media all {
1189    
1190#\33 710bc01 {
1191    min-height: 8px;
1192    column-gap: 4%;
1193    row-gap: 0px;
1194    width: 100%;
1195    justify-content: flex-start;
1196    align-items: center;
1197    min-width: 4%;
1198    flex-wrap: nowrap;
1199    background-color: rgba(0, 0, 0, 0);
1200    background-image: initial;
1201    padding: 0% 0% 0% 0%;
1202    margin: 0% 0% 0% 0%;
1203}
1204    
1205#\39 360b347 {
1206    min-height: 144px;
1207    column-gap: 0%;
1208    row-gap: 0px;
1209    max-width: none;
1210    width: 100%;
1211    flex-wrap: nowrap;
1212    padding: 0% 0% 0% 0%;
1213    margin: 0% 0% 0% 0%;
1214}
1215    
1216#\35 1d6dafc {
1217    width: 100%;
1218    height: auto;
1219    max-width: 100%;
1220    align-self: stretch;
1221}
1222    
1223#bd946f29 {
1224    align-items: flex-start;
1225    width: 100%;
1226    min-height: 60px;
1227    max-height: 100%;
1228    justify-self: auto;
1229    align-self: auto;
1230    order: 1;
1231    background-color: var(--color_10);
1232    background-image: initial;
1233    border-width: 1px 0 1px 0;
1234    border-color: var(--color_2) #000000 var(--color_2) #000000;
1235    border-style: solid;
1236    padding: 0% 0% 0% 0%;
1237    margin: 0px 0% 0px 0%;
1238    grid-area: auto / auto / auto / auto;
1239}
1240    
1241#b7dee1fb {
1242    box-shadow: rgba(0, 0, 0, 0.25) 0px 0px 11px 0px;
1243}
1244    
1245#\37 46860ee {
1246    width: 100%;
1247    height: auto;
1248    max-width: 100%;
1249    order: 0;
1250    align-self: auto;
1251    margin-right: 0%;
1252    margin-bottom: -9px;
1253    margin-left: 0%;
1254    grid-area: auto / auto / auto / auto;
1255}
1256    
1257#\36 a5bfb4c {
1258    justify-content: flex-end;
1259    align-items: flex-start;
1260    width: 100%;
1261    min-height: 80px;
1262    max-height: 100%;
1263    row-gap: 4px;
1264    column-gap: 4%;
1265    align-self: auto;
1266    order: 1;
1267    justify-self: auto;
1268    padding: 0% 0% 7px 0%;
1269    margin: 0% 0% 0% 0%;
1270    grid-area: auto / auto / auto / auto;
1271}
1272    
1273#d3a805c8 {
1274    align-items: flex-start;
1275    width: 100%;
1276    min-height: 75px;
1277    max-height: 100%;
1278    row-gap: 24px;
1279    column-gap: 4%;
1280    align-self: flex-end;
1281    order: 0;
1282    padding: 0% 0% 0% 0%;
1283    margin: 0px 0% 0px 0%;
1284    grid-area: auto / auto / auto / auto;
1285}
1286    
1287#\33 ad8c19c.flex-element.widget-wrapper > [data-element-type] {
1288    padding-top: 0px;
1289    padding-bottom: 7px;
1290}
1291#\33 ad8c19c {
1292    width: 100.07%;
1293    height: auto;
1294    max-width: 100%;
1295    align-self: auto;
1296    order: 0;
1297    margin: 0px 0% 0px 0%;
1298    grid-area: auto / auto / auto / auto;
1299}
1300    
1301#e77f39e9 {
1302    width: 250px;
1303    height: auto;
1304    max-width: 100%;
1305    min-height: 50px;
1306    justify-self: auto;
1307    align-self: flex-end;
1308    order: 0;
1309    margin-top: 0px;
1310    margin-bottom: 0px;
1311    grid-area: auto / auto / auto / auto;
1312}
1313    
1314#\35 19ddbdc {
1315    width: 376px;
1316    height: auto;
1317    max-width: 100%;
1318    justify-self: auto;
1319    align-self: auto;
1320    order: 0;
1321    margin: 0px 0% 0px 0%;
1322    grid-area: auto / auto / auto / auto;
1323}
1324    
1325#\33 a2d0b90 {
1326    flex-direction: row;
1327    align-items: center;
1328    width: 100%;
1329    min-height: 80px;
1330    max-height: 100%;
1331    justify-self: auto;
1332    align-self: auto;
1333    order: 0;
1334    flex-wrap: nowrap;
1335    background-color: rgba(0, 0, 0, 0);
1336    background-image: initial;
1337    padding: 0% 2% 0% 2%;
1338    margin: 0px 0% 0px 0%;
1339    grid-area: auto / auto / auto / auto;
1340}
1341    
1342#\33 b22d9b0 {
1343    align-items: flex-start;
1344    width: 100%;
1345    min-height: 75px;
1346    max-height: 100%;
1347    row-gap: 24px;
1348    column-gap: 4%;
1349    align-self: auto;
1350    order: 0;
1351    padding: 0% 0% 0% 0%;
1352    margin: 0px 0% 0px 0%;
1353    grid-area: auto / auto / auto / auto;
1354}
1355    
1356#a8c6072d {
1357    width: 99.7%;
1358    height: auto;
1359    max-width: 100%;
1360    order: 0;
1361    align-self: auto;
1362    margin: 0px 0% 0px 0%;
1363    grid-area: auto / auto / auto / auto;
1364}
1365    
1366#e3dfb55d {
1367    flex-direction: row;
1368    align-items: center;
1369    width: 33.09%;
1370    min-height: 149px;
1371    max-height: 100%;
1372    row-gap: 24px;
1373    column-gap: 4%;
1374    justify-self: auto;
1375    align-self: auto;
1376    order: 1;
1377    height: auto;
1378    padding: 0% 0% 0% 0%;
1379    margin: 0% 0% 0% 0%;
1380    grid-area: auto / auto / auto / auto;
1381}
1382    
1383#\34 23fc4a2 {
1384    justify-content: space-between;
1385    align-items: center;
1386    width: 32.45%;
1387    min-height: 160px;
1388    max-height: 100%;
1389    row-gap: 0px;
1390    column-gap: 4%;
1391    flex-wrap: nowrap;
1392    order: 0;
1393    justify-self: auto;
1394    align-self: auto;
1395    padding: 0% 0% 0% 0%;
1396    margin: 0% 0% 0% 0%;
1397    grid-area: auto / auto / auto / auto;
1398}
1399    
1400#\35 bfb3718.flex-element.widget-wrapper > [data-element-type] {
1401    padding-bottom: 0px;
1402    padding-top: 0px;
1403}
1404#\35 bfb3718 {
1405    width: 572px;
1406    height: auto;
1407    max-width: 100%;
1408    justify-self: auto;
1409    align-self: center;
1410    order: 0;
1411    margin-left: 0px;
1412    margin-right: 0px;
1413    grid-area: auto / auto / auto / auto;
1414}
1415    
1416#\35 cbebde5 {
1417    justify-content: space-between;
1418    align-items: center;
1419    width: 32.51%;
1420    min-height: 160px;
1421    max-height: 100%;
1422    row-gap: 0px;
1423    column-gap: 4%;
1424    justify-self: auto;
1425    align-self: auto;
1426    order: 2;
1427    flex-wrap: nowrap;
1428    padding: 0% 0% 0% 0%;
1429    margin: 0% 0% 0% 0%;
1430    grid-area: auto / auto / auto / auto;
1431}
1432    
1433#\36 881434d {
1434    align-items: flex-start;
1435    width: 574px;
1436    min-height: 113px;
1437    max-height: 100%;
1438    row-gap: 24px;
1439    column-gap: 4%;
1440    background-color: var(--color_2);
1441    background-image: none;
1442    border-radius: 8px 8px 8px 8px;
1443    height: auto;
1444    padding: 8px 0% 8px 0%;
1445    margin: 0% 0% 0% 0%;
1446}
1447    
1448#\30 ad35782 {
1449    width: 99.99%;
1450    height: auto;
1451    max-width: 100%;
1452    align-self: auto;
1453    order: 1;
1454    justify-self: auto;
1455    margin-bottom: 3px;
1456    grid-area: auto / auto / auto / auto;
1457}
1458    
1459#be1b9f91 {
1460    flex-direction: row;
1461    align-items: flex-start;
1462    width: 99.26%;
1463    min-height: 160px;
1464    max-height: 100%;
1465    row-gap: 24px;
1466    column-gap: 1%;
1467    order: 0;
1468    justify-self: auto;
1469    align-self: auto;
1470    padding: 0% 0% 0% 0%;
1471    margin: 0% 0% 0% 0%;
1472    grid-area: auto / auto / auto / auto;
1473}
1474    
1475#\38 caf0a70 {
1476    justify-content: flex-end;
1477    align-items: flex-start;
1478    width: 100%;
1479    min-height: 80px;
1480    max-height: 100%;
1481    row-gap: 0px;
1482    column-gap: 4%;
1483    padding: 0% 0% 10px 0%;
1484    margin: 0px 0% 0px 0%;
1485}
1486    
1487#a8f32aec {
1488    align-items: flex-start;
1489    width: 100%;
1490    min-height: 60px;
1491    max-height: 100%;
1492    justify-self: auto;
1493    align-self: auto;
1494    order: 2;
1495    background-color: var(--color_10);
1496    background-image: initial;
1497    padding: 0% 0% 0% 0%;
1498    margin: 0px 0% 0px 0%;
1499    grid-area: auto / auto / auto / auto;
1500}
1501    
1502#\33 92df140 {
1503    width: 100%;
1504    height: auto;
1505    max-width: 100%;
1506    align-self: stretch;
1507}
1508    
1509#widget_7d2 {
1510    width: 100%;
1511    height: auto;
1512    max-width: 100%;
1513    align-self: stretch;
1514    margin-left: 0px;
1515    margin-right: 0px;
1516}
1517}</style> 
1518 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-common-4a710abd-3dd8cf0da1894ab5a9043c416f0c4aca">@media all {
1519    
1520#a7708a16 {
1521    min-height: 8px;
1522    column-gap: 4%;
1523    row-gap: 24px;
1524    width: 100%;
1525    min-width: 4%;
1526    align-items: flex-end;
1527    padding: 16px 16px 16px 16px;
1528    margin-left: 0%;
1529    margin-top: 0%;
1530    margin-right: 0%;
1531    margin-bottom: 0%;
1532}
1533    
1534#\37 8db3927 {
1535    padding-top: 2%;
1536    padding-bottom: 2%;
1537    min-height: 100px;
1538    column-gap: 4%;
1539    row-gap: 24px;
1540    width: 100%;
1541    padding-right: 0%;
1542    padding-left: 0%;
1543    margin-left: 0%;
1544    margin-top: 0%;
1545    margin-right: 0%;
1546    margin-bottom: 0%;
1547}
1548    
1549#\38 e45e82b svg {
1550    fill: var(--color_11);
1551}
1552#\38 e45e82b .hamburgerButton {
1553    background-color: rgba(0,0,0,0);
1554}
1555#\38 e45e82b {
1556    width: 30px;
1557    height: 30px;
1558}
1559}</style> 
1560 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-common-63a049320d35b55b4ef2a372-3dd8cf0da1894ab5a9043c416f0c4aca">@media all {
1561    
1562#\36 3a049320d35b55b4ef2a374 {
1563    min-height: 8px;
1564    column-gap: 4%;
1565    row-gap: 24px;
1566    width: 100%;
1567    min-width: 4%;
1568    background-color: rgba(0, 0, 0, 0);
1569    padding: 16px 16px 16px 16px;
1570    margin: 0% 0% 0% 0%;
1571}
1572    
1573#\36 3a049320d35b55b4ef2a376 {
1574    background-color: rgba(0, 0, 0, 0);
1575}
1576    
1577#\36 3a049320d35b55b4ef2a377 {
1578    min-height: 200px;
1579    column-gap: 4%;
1580    row-gap: 24px;
1581    width: 100%;
1582    padding: 2% 0% 2% 0%;
1583    margin: 0% 0% 0% 0%;
1584}
1585    
1586#\36 2d6a167a824ca526194d9a5 {
1587    background-color: rgba(232, 255, 133, 1);
1588}
1589    
1590#widget_la2 {
1591    width: 300px;
1592    height: auto;
1593    position: relative;
1594    min-width: 10px;
1595    min-height: 10px;
1596    max-width: 100%;
1597}
1598}</style> 
1599 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-common-6406026c90e1521f6ecb0eca-3dd8cf0da1894ab5a9043c416f0c4aca">@media all {
1600    
1601#\36 406026c90e1521f6ecb0ecd {
1602    min-height: 8px;
1603    column-gap: 4%;
1604    row-gap: 24px;
1605    width: 100%;
1606    min-width: 4%;
1607    padding: 16px 16px 16px 16px;
1608    margin-left: 0%;
1609    margin-top: 0%;
1610    margin-right: 0%;
1611    margin-bottom: 0%;
1612}
1613    
1614#\36 406026c90e1521f6ecb0ecb {
1615    background-color: var(--color_10);
1616    background-image: initial;
1617}
1618    
1619#\36 406026c90e1521f6ecb0ecc {
1620    min-height: 460px;
1621    padding-top: 4%;
1622    padding-bottom: 4%;
1623    column-gap: 4%;
1624    row-gap: 24px;
1625    width: 100%;
1626    padding-right: 0%;
1627    padding-left: 0%;
1628    margin-left: 0%;
1629    margin-top: 0%;
1630    margin-right: 0%;
1631    margin-bottom: 0%;
1632}
1633}</style> 
1634 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-common-2c8e291c-3dd8cf0da1894ab5a9043c416f0c4aca">@media all {
1635    
1636#b7a700bf {
1637    min-height: 120px;
1638    column-gap: 4%;
1639    row-gap: 24px;
1640    max-width: none;
1641    width: 100%;
1642    flex-wrap: nowrap;
1643    padding: 1% 4% 1% 4%;
1644    margin: 0% 0% 0% 0%;
1645}
1646    
1647#f9c6e236 {
1648    width: 100%;
1649    height: auto;
1650    max-width: 100%;
1651}
1652    
1653#\36 405d24c {
1654    min-height: 8px;
1655    column-gap: 4%;
1656    row-gap: 24px;
1657    width: 100%;
1658    align-items: center;
1659    min-width: 4%;
1660    padding: 0% 0% 0% 0%;
1661    margin: 0% 0% 0% 0%;
1662}
1663    
1664#b5a3be9f {
1665    border-style: solid;
1666    border-color: rgba(204, 204, 204, 1) #000000 #000000 #000000;
1667    border-width: 1px 0 0 0;
1668    background-color: var(--color_2);
1669    background-image: initial;
1670}
1671}</style> 
1672 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-common-7b00a5de-3dd8cf0da1894ab5a9043c416f0c4aca">@media all {
1673    
1674#\36 3c7b8b76c596a2813341bc5 {
1675    height: auto;
1676}
1677    
1678#\39 34bad03 {
1679    width: 100%;
1680    height: auto;
1681    max-width: 100%;
1682    order: 0;
1683    justify-self: auto;
1684    align-self: auto;
1685    grid-area: auto / auto / auto / auto;
1686}
1687    
1688#\31 ebb7f19 {
1689    border-width: 0px;
1690    border-color: rgba(0, 0, 0, 1);
1691    border-style: solid;
1692    background-color: var(--color_3);
1693    background-image: initial;
1694}
1695    
1696#bd7b2692 {
1697    width: 280px;
1698    height: auto;
1699    max-width: 100%;
1700    justify-self: auto;
1701    align-self: auto;
1702    order: 2;
1703    min-height: 50px;
1704    grid-area: auto / auto / auto / auto;
1705}
1706    
1707#\36 e06962d {
1708    min-height: 300px;
1709    column-gap: 4%;
1710    row-gap: 24px;
1711    max-width: none;
1712    width: 100%;
1713    flex-wrap: nowrap;
1714    padding: 4% 4% 4% 4%;
1715    margin: 0% 0% 0% 0%;
1716}
1717    
1718#\36 f8e19aa {
1719    width: 100%;
1720    height: auto;
1721    max-width: 100%;
1722    order: 1;
1723}
1724    
1725#\30 5e0ee19::before, #\30 5e0ee19 > .bgExtraLayerOverlay {
1726    border-radius: inherit;
1727    content: '';
1728    display: block;
1729    position: absolute;
1730    width: 100%;
1731    height: 100%;
1732    top: 0;
1733    left: 0;
1734    background-color: rgba(0, 0, 0, 1);
1735    opacity: 0.3;
1736}
1737#\30 5e0ee19 {
1738    min-height: 8px;
1739    column-gap: 4%;
1740    row-gap: 24px;
1741    width: 48%;
1742    min-width: 4%;
1743    background-color: rgba(0, 0, 0, 0);
1744    padding: 50px 16px 50px 16px;
1745    margin: 0% 0% 0% 0%;
1746    background-image: url('https://le-cdn.hibuwebsites.com/f6cdfabba1c749a1a3d3b7a9f4c30c76/dms3rep/multi/opt/video-jtn-electrical-2880w.jpg') !important;
1747}
1748    
1749#a1b6269f {
1750    width: 220px;
1751    height: 198px;
1752    max-width: 100%;
1753    align-self: center;
1754}
1755    
1756#a019b36e {
1757    min-height: 8px;
1758    column-gap: 4%;
1759    row-gap: 24px;
1760    width: 48%;
1761    min-width: 4%;
1762    background-color: rgba(0, 0, 0, 0);
1763    padding: 16px 0% 16px 0%;
1764    margin: 0% 0% 0% 0%;
1765}
1766}</style> 
1767 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-common-b6b5923e-3dd8cf0da1894ab5a9043c416f0c4aca">@media all {
1768    
1769#\32 bcd41ae {
1770    background-color: var(--color_8);
1771    background-image: initial;
1772}
1773    
1774#\34 efeffee {
1775    min-height: auto;
1776    column-gap: 0%;
1777    row-gap: 0px;
1778    width: 100%;
1779    flex-wrap: nowrap;
1780    max-width: none;
1781    padding: 12px 4% 12px 4%;
1782    margin: 0% 0% 0% 0%;
1783}
1784    
1785#group_b0q {
1786    justify-content: flex-start;
1787    width: 33.33%;
1788    min-width: 4%;
1789    min-height: 8px;
1790    column-gap: 4%;
1791    row-gap: 2px;
1792    padding: 16px 2% 16px 0%;
1793    margin: 0% 0% 0% 0%;
1794}
1795    
1796#widget_1j0 {
1797    width: 100%;
1798    height: auto;
1799    max-width: 100%;
1800    order: 0;
1801}
1802    
1803#widget_p1h {
1804    width: 100%;
1805    height: 1px;
1806    max-width: 100%;
1807    min-height: 4px;
1808    justify-self: auto;
1809    align-self: auto;
1810    order: 5;
1811    grid-area: auto / auto / auto / auto;
1812}
1813    
1814#widget_mse.flex-element.widget-wrapper > [data-element-type] {
1815    padding-top: 7px;
1816    padding-bottom: 0px;
1817}
1818#widget_mse {
1819    width: 80px;
1820    height: auto;
1821    min-height: 1px;
1822    max-width: 100%;
1823    order: 1;
1824}
1825    
1826#\36 18f39ae {
1827    width: 100%;
1828    height: 1px;
1829    max-width: 100%;
1830    justify-self: auto;
1831    align-self: auto;
1832    order: 2;
1833    min-height: 4px;
1834    grid-area: auto / auto / auto / auto;
1835}
1836    
1837#c7f3ddf3.flex-element.widget-wrapper > [data-element-type] {
1838    padding-top: 7px;
1839    padding-bottom: 0px;
1840}
1841#c7f3ddf3 {
1842    width: 80px;
1843    height: auto;
1844    min-height: 1px;
1845    max-width: 100%;
1846    order: 1;
1847}
1848    
1849#f2debc4b {
1850    justify-content: flex-start;
1851    width: 33.33%;
1852    min-width: 4%;
1853    min-height: 8px;
1854    column-gap: 4%;
1855    row-gap: 2px;
1856    order: 0;
1857    padding: 16px 2% 16px 0%;
1858    margin: 0% 0% 0% 0%;
1859}
1860    
1861#\36 5e14480 {
1862    width: 100%;
1863    height: auto;
1864    max-width: 100%;
1865    order: 0;
1866    justify-self: auto;
1867    align-self: auto;
1868    grid-area: auto / auto / auto / auto;
1869}
1870    
1871#cbc8b291 {
1872    width: 100%;
1873    height: auto;
1874    max-width: 100%;
1875    justify-self: auto;
1876    align-self: auto;
1877    order: 0;
1878    grid-area: auto / auto / auto / auto;
1879}
1880    
1881#d0234170 {
1882    justify-content: flex-start;
1883    width: 33.33%;
1884    min-width: 4%;
1885    min-height: 8px;
1886    column-gap: 4%;
1887    row-gap: 2px;
1888    order: 0;
1889    padding: 16px 2% 16px 0%;
1890    margin: 0% 0% 0% 0%;
1891}
1892    
1893#\35 02c2597 {
1894    width: 100%;
1895    height: 1px;
1896    max-width: 100%;
1897    order: 2;
1898    justify-self: auto;
1899    align-self: auto;
1900    min-height: 4px;
1901    grid-area: auto / auto / auto / auto;
1902}
1903    
1904#\30 b55734b.flex-element.widget-wrapper > [data-element-type] {
1905    padding-top: 7px;
1906    padding-bottom: 0px;
1907}
1908#\30 b55734b {
1909    width: 80px;
1910    height: auto;
1911    min-height: 1px;
1912    max-width: 100%;
1913    order: 1;
1914}
1915    
1916#widget_ipg {
1917    width: 180px;
1918    height: auto;
1919    max-width: 100%;
1920    order: 6;
1921    margin-top: 5px;
1922}
1923    
1924#widget_k5l.flex-element.widget-wrapper > [data-element-type] {
1925    padding: 0px 0px 0px 0px;
1926}
1927#widget_k5l {
1928    width: 99.83%;
1929    height: auto;
1930    max-width: 100%;
1931    order: 2;
1932    justify-self: auto;
1933    align-self: auto;
1934    margin: 6px 0% 0px 0%;
1935    grid-area: auto / auto / auto / auto;
1936}
1937    
1938#\31 db7e0c7 {
1939    width: 180px;
1940    height: auto;
1941    max-width: 100%;
1942    justify-self: auto;
1943    align-self: auto;
1944    order: 5;
1945    margin-right: 0%;
1946    margin-left: 0%;
1947    margin-top: 10px;
1948    grid-area: auto / auto / auto / auto;
1949}
1950    
1951#widget_6mu.flex-element.widget-wrapper > [data-element-type] {
1952    padding: 0px 0px 0px 0px;
1953}
1954#widget_6mu {
1955    width: 99.76%;
1956    height: auto;
1957    max-width: 100%;
1958    order: 3;
1959    justify-self: auto;
1960    align-self: auto;
1961    margin: 1px 0% 0px 0%;
1962    grid-area: auto / auto / auto / auto;
1963}
1964    
1965#widget_391 {
1966    width: 71.45%;
1967    height: auto;
1968    max-width: 100%;
1969    align-self: flex-start;
1970    margin-top: 10px;
1971    order: 6;
1972}
1973    
1974#widget_umk.flex-element.widget-wrapper > [data-element-type] {
1975    padding: 0px 0px 0px 0px;
1976}
1977#widget_umk {
1978    width: 83.63%;
1979    height: auto;
1980    max-width: 100%;
1981    order: 2;
1982    margin-top: 6px;
1983}
1984    
1985#widget_9dv {
1986    width: 100%;
1987    height: auto;
1988    max-width: 100%;
1989    order: 5;
1990    margin-top: 15px;
1991}
1992    
1993#\38 7a53ad2 {
1994    width: 99.8%;
1995    height: auto;
1996    max-width: 100%;
1997    justify-self: auto;
1998    align-self: auto;
1999    order: 3;
2000    grid-area: auto / auto / auto / auto;
2001}
2002    
2003#\36 efb2802 {
2004    width: 99.8%;
2005    height: auto;
2006    max-width: 100%;
2007    justify-self: auto;
2008    align-self: auto;
2009    order: 4;
2010    grid-area: auto / auto / auto / auto;
2011}
2012}</style> 
2013 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-common-2f831c0f-3dd8cf0da1894ab5a9043c416f0c4aca">@media all {
2014    
2015#\30 0841c9d {
2016    min-height: 8px;
2017    column-gap: 4%;
2018    row-gap: 24px;
2019    flex-direction: row;
2020    width: 100%;
2021    justify-content: flex-start;
2022    align-items: center;
2023    min-width: 4%;
2024    padding: 0px 16px 0px 16px;
2025    margin: 0px 0% 0px 0%;
2026}
2027    
2028#d027d36f {
2029    min-height: auto;
2030    column-gap: 0%;
2031    row-gap: 0px;
2032    width: 100%;
2033    flex-wrap: nowrap;
2034    padding: 1% 2% 0% 2%;
2035}
2036    
2037#\30 253b4c6 {
2038    background-color: var(--color_2);
2039    background-image: none;
2040}
2041    
2042#\37 1171ca4 {
2043    margin: 0px 0% 0px 0%;
2044}
2045    
2046#\37 e326d80 {
2047    width: 100%;
2048    height: auto;
2049    max-width: 100%;
2050    order: 0;
2051    align-self: auto;
2052    justify-self: auto;
2053    margin: 0px 0% 0px 0%;
2054    grid-area: auto / auto / auto / auto;
2055}
2056}</style> 
2057 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-common-54290af5-3dd8cf0da1894ab5a9043c416f0c4aca">@media all {
2058    
2059#\37 ca01c83 {
2060    min-height: 8px;
2061    column-gap: 4%;
2062    row-gap: 24px;
2063    width: 100%;
2064    min-width: 4%;
2065    padding: 0% 0% 0% 0%;
2066    margin: 0% 0% 0% 0%;
2067}
2068    
2069#\32 4eff763 {
2070    min-height: 0px;
2071    column-gap: 4%;
2072    row-gap: 24px;
2073    width: 100%;
2074    flex-wrap: nowrap;
2075    max-width: none;
2076    padding: 5px 4% 5px 4%;
2077    margin: 0% 0% 0% 0%;
2078}
2079    
2080#\33 4b9f983 {
2081    width: 100%;
2082    height: auto;
2083    max-width: 100%;
2084    justify-self: auto;
2085    align-self: flex-start;
2086    order: 0;
2087    grid-area: auto / auto / auto / auto;
2088}
2089    
2090#\33 989af5b {
2091    background-color: var(--color_2);
2092    background-image: initial;
2093}
2094}</style> 
2095 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-common-4dc8a903-3dd8cf0da1894ab5a9043c416f0c4aca">@media all {
2096    
2097#\30 190983d {
2098    justify-content: flex-start;
2099    width: 22.75%;
2100    min-width: 4%;
2101    min-height: 8px;
2102    column-gap: 4%;
2103    row-gap: 32px;
2104    border-style: solid;
2105    border-color: rgba(0, 0, 0, 1) rgba(0, 0, 0, 1) rgba(0, 0, 0, 1) rgba(0, 0, 0, 1);
2106    border-width: 0px 0px 0px 0px;
2107    padding: 2% 1% 2% 2%;
2108    margin: 0% 0% 0% 0%;
2109}
2110    
2111#\36 c5257e3 {
2112    margin: 0px 0% 0px 0%;
2113}
2114    
2115#d200b2f7 {
2116    justify-content: flex-start;
2117    width: 22.75%;
2118    min-width: 4%;
2119    min-height: 8px;
2120    column-gap: 4%;
2121    row-gap: 32px;
2122    border-style: solid;
2123    border-color: rgba(0, 0, 0, 1) rgba(0, 0, 0, 1) rgba(0, 0, 0, 1) rgba(0, 0, 0, 1);
2124    border-width: 0px 0px 0px 0px;
2125    padding: 2% 1% 2% 2%;
2126    margin: 0% 0% 0% 0%;
2127}
2128    
2129#\32 cb3e9f9 {
2130    background-color: var(--color_10);
2131    background-image: url();
2132    border-style: solid;
2133    border-color: rgba(0, 0, 0, 1) rgba(0, 0, 0, 1) rgba(0, 0, 0, 1) rgba(0, 0, 0, 1);
2134    border-width: 0px 0px 0px 0px;
2135}
2136    
2137#\32 7581a62 {
2138    min-height: auto;
2139    flex-wrap: nowrap;
2140    column-gap: 3%;
2141    row-gap: 18px;
2142    max-width: none;
2143    width: 100%;
2144    padding: 0% 0% 0% 0%;
2145    margin: 0px 0% 0px 0%;
2146    grid-area: 1 / 1 / span 1 / span 1;
2147}
2148    
2149#ad9cb284 {
2150    justify-content: flex-start;
2151    width: 22.75%;
2152    min-width: 4%;
2153    min-height: 8px;
2154    column-gap: 4%;
2155    row-gap: 32px;
2156    border-style: solid;
2157    border-color: rgba(0, 0, 0, 1) rgba(0, 0, 0, 1) rgba(0, 0, 0, 1) rgba(0, 0, 0, 1);
2158    border-width: 0px 0px 0px 0px;
2159    order: 0;
2160    padding: 2% 1% 2% 2%;
2161    margin: 0% 0% 0% 0%;
2162}
2163    
2164#widget_cq9 {
2165    width: 100%;
2166    height: auto;
2167    max-width: 100%;
2168    align-self: stretch;
2169}
2170    
2171#group_9n6 {
2172    width: 22.75%;
2173    min-width: 4%;
2174    min-height: 8px;
2175    column-gap: 4%;
2176    row-gap: 32px;
2177    justify-content: flex-start;
2178    padding: 2% 1% 2% 2%;
2179    margin: 0% 0% 0% 0%;
2180}
2181    
2182#b51d88ee {
2183    width: 100%;
2184    height: auto;
2185    max-width: 100%;
2186    align-self: auto;
2187    justify-self: auto;
2188    order: 0;
2189    margin: 0px 0% 0px 0%;
2190    grid-area: auto / auto / auto / auto;
2191}
2192    
2193#\35 aa033c8 {
2194    width: 100%;
2195    height: auto;
2196    max-width: 100%;
2197    align-self: auto;
2198    justify-self: auto;
2199    order: 0;
2200    margin: 0px 0% 0px 0%;
2201    grid-area: auto / auto / auto / auto;
2202}
2203    
2204#\30 4df1da2 {
2205    width: 100%;
2206    height: auto;
2207    max-width: 100%;
2208    align-self: auto;
2209    justify-self: auto;
2210    order: 0;
2211    margin: 0px 0% 0px 0%;
2212    grid-area: auto / auto / auto / auto;
2213}
2214    
2215#f09d8243 {
2216    width: 100%;
2217    height: auto;
2218    max-width: 100%;
2219    align-self: auto;
2220    justify-self: auto;
2221    order: 0;
2222    margin: 0px 0% 0px 0%;
2223    grid-area: auto / auto / auto / auto;
2224}
2225    
2226#\38 99c2eb2 {
2227    width: 100%;
2228    height: auto;
2229    max-width: 100%;
2230    align-self: auto;
2231    justify-self: auto;
2232    order: 0;
2233    margin: 0px 0% 0px 0%;
2234    grid-area: auto / auto / auto / auto;
2235}
2236    
2237#\37 b991c9b {
2238    width: 100%;
2239    height: auto;
2240    max-width: 100%;
2241    align-self: auto;
2242    justify-self: auto;
2243    order: 0;
2244    margin: 0px 0% 0px 0%;
2245    grid-area: auto / auto / auto / auto;
2246}
2247    
2248#\33 9a585e9 {
2249    width: 100%;
2250    height: auto;
2251    max-width: 100%;
2252    align-self: auto;
2253    justify-self: auto;
2254    order: 0;
2255    margin: 0px 0% 0px 0%;
2256    grid-area: auto / auto / auto / auto;
2257}
2258    
2259#\34 310a1b0 {
2260    width: 100%;
2261    height: auto;
2262    max-width: 100%;
2263    align-self: auto;
2264    justify-self: auto;
2265    order: 0;
2266    margin: 0px 0% 0px 0%;
2267    grid-area: auto / auto / auto / auto;
2268}
2269    
2270#fe7f7167 {
2271    width: 100%;
2272    height: auto;
2273    max-width: 100%;
2274    align-self: auto;
2275    order: 0;
2276    margin: 0px 0% 0px 0%;
2277    grid-area: auto / auto / auto / auto;
2278}
2279}</style> 
2280 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-common-2a
2280501ecf-3dd8cf0da1894ab5a9043c416f0c4aca">@media all {
2281    
2282#\30 8465346 {
2283    min-height: auto;
2284    column-gap: 3%;
2285    row-gap: 18px;
2286    width: 100%;
2287    flex-wrap: nowrap;
2288    max-width: var(--max-content-width_1, 1200px);
2289    padding: 2% 0% 2% 0%;
2290}
2291    
2292#\35 a572945 {
2293    min-height: 8px;
2294    column-gap: 4%;
2295    row-gap: 0px;
2296    width: 100%;
2297    min-width: 4%;
2298    padding: 0px 0px 0px 0px;
2299    margin: 0px 0% 0px 0%;
2300}
2301    
2302#\35 7b153fb {
2303    background-color: var(--color_10);
2304    background-image: none;
2305}
2306    
2307#c55100d8 {
2308    margin: 0px 0% 0px 0%;
2309}
2310    
2311#widget_gf3 {
2312    width: auto;
2313    height: auto;
2314    max-width: 100%;
2315}
2316    
2317#undefined {
2318    width: 50%;
2319}
2320}</style> 
2321 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-common-8a9cfcfc-3dd8cf0da1894ab5a9043c416f0c4aca">@media all {
2322    
2323#\37 2168835 {
2324    grid-template-rows: minmax(300px, max-content);
2325    width: 100%;
2326    max-width: 100%;
2327}
2328    
2329#\37 5ba1c41 {
2330    min-height: 240px;
2331    column-gap: 4%;
2332    row-gap: 24px;
2333    max-width: none;
2334    width: 100%;
2335    flex-wrap: nowrap;
2336    padding: 0% 0% 0% 0%;
2337    margin: 0% 0% 0% 0%;
2338}
2339    
2340#\34 f4d3298 {
2341    min-height: 8px;
2342    column-gap: 4%;
2343    row-gap: 24px;
2344    width: 100%;
2345    min-width: 4%;
2346    padding: 0% 0% 0% 0%;
2347    margin: 0% 0% 0% 0%;
2348}
2349    
2350#widget_21v {
2351    width: 60px;
2352    height: auto;
2353    max-width: 100%;
2354    order: 5;
2355    justify-self: end;
2356    align-self: end;
2357    margin: 40px 40px 40px 40px;
2358}
2359    
2360#widget_fpt {
2361    width: auto;
2362    height: auto;
2363    max-width: 100%;
2364    justify-self: end;
2365    align-self: end;
2366    margin: 40px 40px 40px 40px;
2367}
2368    
2369#widget_mnf {
2370    width: auto;
2371    height: auto;
2372    min-width: auto;
2373    min-height: auto;
2374    justify-self: end;
2375    align-self: end;
2376    order: 0;
2377    max-width: 100%;
2378    margin: 377px 0% 0px 0%;
2379}
2380    
2381#widget_c5v {
2382    width: auto;
2383    height: auto;
2384    min-width: auto;
2385    min-height: auto;
2386    justify-self: end;
2387    align-self: end;
2388    order: 0;
2389    max-width: 100%;
2390    max-height: none;
2391    margin: 377px 0% 0px 0%;
2392}
2393    
2394#widget_doa {
2395    width: auto;
2396    height: auto;
2397    min-width: auto;
2398    min-height: auto;
2399    justify-self: end;
2400    align-self: end;
2401    order: 0;
2402    max-width: 100%;
2403    margin: 377px 0% 0px 0%;
2404}
2405    
2406#widget_301 {
2407    width: auto;
2408    height: auto;
2409    max-width: 100%;
2410    justify-self: end;
2411    align-self: end;
2412    min-height: auto;
2413    min-width: auto;
2414    margin: 40px 40px 40px 40px;
2415}
2416    
2417#widget_61p {
2418    width: auto;
2419    height: auto;
2420    position: relative;
2421    min-width: auto;
2422    min-height: auto;
2423    justify-self: end;
2424    align-self: end;
2425    order: 0;
2426    max-width: 100%;
2427    margin: 377px 0% 0px 0%;
2428}
2429}</style> 
2430 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-desktop-flex-header-3dd8cf0da1894ab5a9043c416f0c4aca">@media (min-width: 1025px) {
2431}</style> 
2432 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-tablet_implicit-flex-header-3dd8cf0da1894ab5a9043c416f0c4aca">@media (min-width: 768px) and (max-width: 1024px) {
2433}</style> 
2434 <style data-page-uuid="8d67b06083f34b4a8a328c95f017fec2" id="grid-styles-tablet-7ef32d84-8d67b06083f34b4a8a328c95f017fec2">@media (min-width: 768px) and (max-width: 1024px) {
2435    
2436#\36 a108e83 {
2437    padding-left: 4%;
2438    padding-right: 4%;
2439}
2440}</style> 
2441 <style data-page-uuid="8d67b06083f34b4a8a328c95f017fec2" id="grid-styles-tablet-5c4456af-8d67b06083f34b4a8a328c95f017fec2">@media (min-width: 768px) and (max-width: 1024px) {
2442    
2443#\35 20e9609 {
2444    padding-left: 0%;
2445    padding-right: 0%;
2446}
2447    
2448#b6321765 {
2449    justify-self: auto;
2450    align-self: auto;
2451    width: 100%;
2452    order: 0;
2453    margin: 0px 0% 0px 0%;
2454    grid-area: auto / auto / auto / auto;
2455}
2456    
2457#\36 8fb7ec9 {
2458    padding-left: 20px;
2459    padding-right: 20px;
2460}
2461}</style> 
2462 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-tablet-flex-footer-3dd8cf0da1894ab5a9043c416f0c4aca">@media (min-width: 768px) and (max-width: 1024px) {
2463    
2464}</style> 
2465 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-tablet-flex-header-3dd8cf0da1894ab5a9043c416f0c4aca">@media (min-width: 768px) and (max-width: 1024px) {
2466    
2467#flex-header {
2468    
2469    padding-top: 0%;
2470    margin-top: 0px;
2471}
2472}</style> 
2473 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-tablet-405ddf11-3dd8cf0da1894ab5a9043c416f0c4aca">@media (min-width: 768px) and (max-width: 1024px) {
2474    
2475#\31 d3acdb9 {
2476    padding-left: 4%;
2477    padding-right: 4%;
2478    flex-direction: row;
2479    flex-wrap: nowrap;
2480    min-height: 80px;
2481}
2482}</style> 
2483 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-tablet-157b4fff-3dd8cf0da1894ab5a9043c416f0c4aca">@media (min-width: 768px) and (max-width: 1024px) {
2484    
2485#\33 2767c2f {
2486    padding-left: 1%;
2487    padding-right: 1%;
2488    min-height: 92px;
2489    flex-direction: row;
2490    flex-wrap: nowrap;
2491    column-gap: 1%;
2492}
2493    
2494#\32 fd7b414 {
2495    width: 21.2%;
2496    min-height: 8px;
2497    order: 1;
2498    column-gap: 0%;
2499    justify-content: center;
2500    padding-left: 0px;
2501    padding-right: 0px;
2502}
2503    
2504#group_7in {
2505    width: 21.2%;
2506    min-height: 8px;
2507    order: 3;
2508    justify-content: center;
2509}
2510    
2511#group_jjh {
2512    width: 34.5%;
2513    min-height: 8px;
2514    justify-content: center;
2515    order: 2;
2516    margin: 10px 2% 10px 2%;
2517}
2518    
2519#widget_ie6 {
2520    align-self: auto;
2521    margin: 0px 0% 0px 0%;
2522    grid-area: auto / auto / auto / auto;
2523}
2524    
2525#widget_5h5 {
2526    width: 220px;
2527    height: auto;
2528    align-self: auto;
2529    margin: 0px 0% 0px 0%;
2530    grid-area: auto / auto / auto / auto;
2531}
2532    
2533#widget_vej {
2534    width: 130px;
2535    align-self: auto;
2536    min-height: 40px;
2537    margin: 0px 0% 0px 0%;
2538    grid-area: auto / auto / auto / auto;
2539}
2540    
2541#group_23b {
2542    width: 8.33%;
2543    min-height: 8px;
2544    order: 4;
2545    justify-content: center;
2546}
2547    
2548#group_tbn {
2549    order: 0;
2550    width: 8.33%;
2551    min-height: 8px;
2552}
2553    
2554#widget_qp9 {
2555    height: 6px;
2556}
2557}</style> 
2558 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-tablet-c0ae99ba-3dd8cf0da1894ab5a9043c416f0c4aca">@media (min-width: 768px) and (max-width: 1024px) {
2559    
2560#\39 360b347 {
2561    padding-left: 2%;
2562    padding-right: 2%;
2563    flex-direction: row;
2564    flex-wrap: nowrap;
2565    min-height: 100px;
2566    padding-bottom: 12px;
2567}
2568    
2569#\33 710bc01 {
2570    width: 100%;
2571    min-height: 8px;
2572}
2573    
2574#ac810934 {
2575    margin-top: 0px;
2576}
2577}</style> 
2578 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-tablet-4a710abd-3dd8cf0da1894ab5a9043c416f0c4aca">@media (min-width: 768px) and (max-width: 1024px) {
2579    
2580#\38 e45e82b {
2581    justify-self: auto;
2582    align-self: auto;
2583    order: 0;
2584    margin: 0px 0% 0px 0%;
2585    grid-area: auto / auto / auto / auto;
2586}
2587}</style> 
2588 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-tablet-63a049320d35b55b4ef2a372-3dd8cf0da1894ab5a9043c416f0c4aca">@media (min-width: 768px) and (max-width: 1024px) {
2589    
2590#\36 3a049320d35b55b4ef2a374 {
2591    justify-content: flex-start;
2592    padding-top: 10px;
2593    padding-left: 8px;
2594    padding-right: 8px;
2595}
2596    
2597#\36 3a049320d35b55b4ef2a377 {
2598    min-height: 672px;
2599    padding: 0% 4% 0% 4%;
2600}
2601    
2602#widget_la2 {
2603    width: auto;
2604    height: auto;
2605}
2606}</style> 
2607 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-tablet-6406026c90e1521f6ecb0eca-3dd8cf0da1894ab5a9043c416f0c4aca">@media (min-width: 768px) and (max-width: 1024px) {
2608    
2609#\36 406026c90e1521f6ecb0ecc {
2610    min-height: 228px;
2611    padding: 0% 4% 0% 4%;
2612}
2613    
2614#\36 406026c90e1521f6ecb0ecd {
2615    justify-content: center;
2616    padding-bottom: 6%;
2617    padding-top: 6%;
2618}
2619}</style> 
2620 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-tablet-2c8e291c-3dd8cf0da1894ab5a9043c416f0c4aca">@media (min-width: 768px) and (max-width: 1024px) {
2621    
2622#b7a700bf {
2623    padding-left: 2%;
2624    padding-right: 2%;
2625}
2626}</style> 
2627 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-tablet-7b00a5de-3dd8cf0da1894ab5a9043c416f0c4aca">@media (min-width: 768px) and (max-width: 1024px) {
2628    
2629#\30 5e0ee19::before, #\30 5e0ee19 > .bgExtraLayerOverlay {
2630    
2631}
2632#\30 5e0ee19 {
2633    background-image: url('https://le-cdn.hibuwebsites.com/f6cdfabba1c749a1a3d3b7a9f4c30c76/dms3rep/multi/opt/video-jtn-electrical-2880w.jpg') !important;
2634}
2635}</style> 
2636 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-tablet-b6b5923e-3dd8cf0da1894ab5a9043c416f0c4aca">@media (min-width: 768px) and (max-width: 1024px) {
2637    
2638#\34 efeffee {
2639    padding-left: 4%;
2640    padding-right: 4%;
2641    flex-direction: row;
2642    flex-wrap: wrap;
2643    column-gap: 0%;
2644    min-height: 240px;
2645}
2646    
2647#widget_391 {
2648    width: 71.00%;
2649}
2650    
2651#widget_1j0 {
2652    justify-self: auto;
2653    align-self: auto;
2654    width: 100%;
2655    order: 0;
2656    margin: 0px 0% 0px 0%;
2657    grid-area: auto / auto / auto / auto;
2658}
2659    
2660#widget_mse {
2661    order: 1;
2662}
2663    
2664#widget_p1h {
2665    order: 2;
2666}
2667    
2668#widget_ipg {
2669    order: 5;
2670}
2671    
2672#group_b0q {
2673    width: 50%;
2674    flex: 1 1 auto;
2675    margin: 0% 0% 0% 0%;
2676}
2677    
2678#d0234170 {
2679    width: 50%;
2680    flex: 1 1 auto;
2681    margin: 0% 0% 0% 0%;
2682}
2683    
2684#f2debc4b {
2685    width: 50%;
2686    flex: 1 1 auto;
2687    margin: 0% 0% 0% 0%;
2688}
2689    
2690#widget_6mu {
2691    width: 50%;
2692}
2693}</style> 
2694 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-tablet-2f831c0f-3dd8cf0da1894ab5a9043c416f0c4aca">@media (min-width: 768px) and (max-width: 1024px) {
2695    
2696#d027d36f {
2697    padding-left: 4%;
2698    padding-right: 4%;
2699}
2700}</style> 
2701 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-tablet-54290af5-3dd8cf0da1894ab5a9043c416f0c4aca">@media (min-width: 768px) and (max-width: 1024px) {
2702    
2703#\32 4eff763 {
2704    padding-left: 4%;
2705    padding-right: 4%;
2706}
2707}</style> 
2708 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-tablet-4dc8a903-3dd8cf0da1894ab5a9043c416f0c4aca">@media (min-width: 768px) and (max-width: 1024px) {
2709    
2710#\30 190983d {
2711    order: 0;
2712    width: 25%;
2713    min-height: 8px;
2714}
2715    
2716#\36 c5257e3 {
2717    margin-left: 0%;
2718    margin-right: 0%;
2719}
2720    
2721#d200b2f7 {
2722    order: 0;
2723    width: 25%;
2724    min-height: 8px;
2725}
2726    
2727#\32 7581a62 {
2728    flex-direction: row;
2729    min-height: 240px;
2730    flex-wrap: nowrap;
2731    row-gap: 0px;
2732    column-gap: 0%;
2733    padding: 0% 0% 0% 0%;
2734}
2735    
2736#ad9cb284 {
2737    width: 25%;
2738    min-height: 8px;
2739}
2740    
2741#group_9n6 {
2742    width: 25%;
2743    min-height: 8px;
2744}
2745}</style> 
2746 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-tablet-2a501ecf-3dd8cf0da1894ab5a9043c416f0c4aca">@media (min-width: 768px) and (max-width: 1024px) {
2747    
2748#\30 8465346 {
2749    padding-left: 2%;
2750    padding-right: 2%;
2751}
2752}</style> 
2753 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-tablet-8a9cfcfc-3dd8cf0da1894ab5a9043c416f0c4aca">@media (min-width: 768px) and (max-width: 1024px) {
2754    
2755#\37 5ba1c41 {
2756    padding-left: 2%;
2757    padding-right: 2%;
2758}
2759}</style> 
2760
2761
2762
2763
2764
2765
2766<style id="globalFontSizeStyle" type="text/css">
2767    @media (min-width: 1025px) { [data-version] .size-13, [data-version] .font-size-13 {--font-size: 13;}[data-version] .size-24, [data-version] .font-size-24 {--font-size: 24;}[data-version] .size-18, [data-version] .font-size-18 {--font-size: 18;}[data-version] .size-15, [data-version] .font-size-15 {--font-size: 15;} }@media (min-width: 768px) and (max-width: 1024px) { [data-version] .t-size-13, [data-version] .t-font-size-13 {--font-size: 13;}[data-version] .t-size-20, [data-version] .t-font-size-20 {--font-size: 20;}[data-version] .t-size-16, [data-version] .t-font-size-16 {--font-size: 16;}[data-version] .t-size-15, [data-version] .t-font-size-15 {--font-size: 15;} }@media (max-width: 767px) { [data-version] .m-size-13, [data-version] .m-font-size-13 {--font-size: 13;}[data-version] .m-size-19, [data-version] .m-font-size-19 {--font-size: 19;}[data-version] .m-size-16, [data-version] .m-font-size-16 {--font-size: 16;}[data-version] .m-size-15, [data-version] .m-font-size-15 {--font-size: 15;} }
2768</style>
2769<style id="pageFontSizeStyle" type="text/css">
2770</style>
2771
2772
2773
2774
2775<style id="hideAnimFix">
2776  .dmDesktopBody:not(.editGrid) [data-anim-desktop]:not([data-anim-desktop='none']), .dmDesktopBody:not(.editGrid) [data-anim-extended] {
2777    visibility: hidden;
2778  }
2779
2780  .dmDesktopBody:not(.editGrid) .dmNewParagraph[data-anim-desktop]:not([data-anim-desktop='none']), .dmDesktopBody:not(.editGrid) .dmNewParagraph[data-anim-extended] {
2781    visibility: hidden !important;
2782  }
2783
2784  #dmRoot:not(.editGrid) .flex-element [data-anim-extended] {
2785    visibility: hidden;
2786  }
2787
2788</style>
2789
2790
2791
2792
2793<style id="fontFallbacks">
2794    @font-face {
2795  font-family: "Poppins Fallback";
2796  src: local('Arial');
2797  ascent-override: 92.326%;
2798  descent-override: 30.7753%;
2799  line-gap-override: 8.793%;
2800  size-adjust: 113.7274%;
2801}@font-face {
2802  font-family: "Kumbh Sans Fallback";
2803  src: local('Arial');
2804  ascent-override: 93.9558%;
2805  descent-override: 24.1866%;
2806  size-adjust: 104.9779%;
2807  line-gap-override: 0%;
2808 }@font-face {
2809  font-family: "Racing Sans One Fallback";
2810  src: local('Arial');
2811  ascent-override: 93.8589%;
2812  descent-override: 27.3114%;
2813  size-adjust: 103.9858%;
2814  line-gap-override: 0%;
2815 }
2816</style>
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828<meta property="og:type" content="website">
2829<meta property="og:url" content="https://www.jtnelectrical.com/request-service-form">
2830
2831  <title>
2832    Request Service | JTN Electrical
2833  </title>
2834  <meta name="description" content="Request a service from JTN Electrical. Emergency electrical services available. Quick response times. Free estimates."/>
2835
2836  <meta name="twitter:card" content="summary"/>
2837  <meta name="twitter:title" content="Request Service | JTN Electrical"/>
2838  <meta name="twitter:description" content="Request a service from JTN Electrical. Emergency electrical services available. Quick response times. Free estimates."/>
2839  <meta name="twitter:image" content="https://le-cdn.hibuwebsites.com/f6cdfabba1c749a1a3d3b7a9f4c30c76/dms3rep/multi/opt/social-image-jtn-electrical-1920w.jpg"/>
2840  <meta property="og:description" content="Request a service from JTN Electrical. Emergency electrical services available. Quick response times. Free estimates."/>
2841  <meta property="og:title" content="Request Service | JTN Electrical"/>
2842  <meta property="og:image" content="https://le-cdn.hibuwebsites.com/f6cdfabba1c749a1a3d3b7a9f4c30c76/dms3rep/multi/opt/social-image-jtn-electrical-1920w.jpg"/>
2843
2844
2845
2846
2847<!-- SYS- VVNfSElCVV9QUk9EVUNUSU9O -->
2848</head>
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870<body id="dmRoot" data-page-alias="request-service-form"  class="dmRoot fix-mobile-scrolling flex-site dmResellerSite "
2871      style="padding:0;margin:0;"
2872      
2873     data-flex-site >
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890<div id="dm" class='dmwr'>
2891    
2892    <div class="dm_wrapper -var5 null ">
2893         <div dmwrapped="true" id="1901957768" class="dm-home-page" themewaschanged="true"> <div dmtemplateid="FlexHeader" data-responsive-name="FlexLayout" class="runtime-module-container dm-bfs dm-layout-sec hasAnimations hasStickyHeader inMiniHeaderMode rows-1200 hamburger-reverse dmPageBody d-page-1716942098 inputs-css-clean dmDHeader" id="dm-outer-wrapper" data-page-class="1716942098" data-soch="true" data-background-parallax-selector=".dmHomeSection1, .dmSectionParallex"> <div id="dmStyle_outerContainer" class="dmOuter"> <div id="dmStyle_innerContainer" class="dmInner" data-auto="page-inner"> <div class="dmLayoutWrapper standard-var dmStandardDesktop"> <div id="site_content"> <div id="dmFlexHeaderContainer" class="flex_hfcontainer"> <div id="flex-header" data-variation-id="variation0" data-sticky="true" data-changeable-on-scroll-properties="|text-color|" data-changeable-on-scroll="true" role="banner"> <div data-layout-section="" data-auto="flex-section" id="1956345911" data-flex-id="405ddf11" class="" data-hidden-on-tablet="true" data-hidden-on-desktop="true"> <div class="flex-widgets-container" id="1211899340"></div> 
2894 <section id="6c317a78" class="flex-element section" data-auto="flex-element-section"> <div id="11fd5064" class="flex-element grid" data-auto="flex-element-grid" data-layout-grid=""> <div id="1d3acdb9" class="flex-element group" data-auto="flex-element-group" data-hidden-on-tablet="" data-hidden-on-desktop=""> <div id="9f3f031f" class="flex-element group" data-auto="flex-element-group" data-hidden-on-desktop="" data-hidden-on-tablet=""> <div id="98702d80" class="flex-element group" data-auto="flex-element-group"> <div id="4cb86e4c" class="flex-element group" data-auto="flex-element-group"> <div id="9e5ef93d" class="flex-element group" data-auto="flex-element-group"> <div id="ec5d46f2" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="custom" data-external-id="1652217484"> <div class="widget-9015bc u_1652217484 dmCustomWidget" data-lazy-load="true" data-title="" id="1652217484" dmle_extension="custom_extension" data-element-type="custom_extension" data-binding="W3siYmluZGluZ05hbWUiOiJtZXJjaGFudElkIiwidmFsdWUiOiJTVEFUSUNfUEFHRV9DT0xMRUNUSU9OLntcImNuXCI6XCJSZXZpZXdzIFdpZGdldHNcIixcImZuXCI6XCJNZXJjaGFudCBJRFwifSJ9XQ==" icon="false" surround="false" data-widget-id="9015bcbf1ace437aa6f0e66d5bb2e7d3" data-widget-version="25" data-widget-config="eyJtZXJjaGFudElkIjoiODA3OTgiLCJtaW4tcmV2aWV3cyI6IjMiLCJtaW4tcmF0aW5nIjoiNCIsInNob3ctd3JpdGUiOnRydWUsImhpZGUtc3VtbWFyeSI6ZmFsc2UsInRoZW1lIjoibGlnaHQifQ=="> <div class="headerWidget" data-merchant="80798" data-theme="light" data-break="" data-count-limit="3" data-rating-limit="4" data-show-write="true" data-hide-summary="false"></div> 
2895</div> 
2896</div> 
2897</div> 
2898</div> 
2899 <div id="2e9284f4" class="flex-element group" data-auto="flex-element-group"> <div id="da0131a7" class="flex-element group" data-auto="flex-element-group"> <div id="bff1bb66" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="image" data-keep-proportion="true" data-external-id="1301876968"> <div class="imageWidget align-center" data-element-type="image" data-widget-type="image" id="1301876968" data-binding="W3siYmluZGluZ05hbWUiOiJpbWFnZSIsInZhbHVlIjoic2l0ZV9pbWFnZXMucGVyc2lzdGVkLmRhcmstbG9nbyJ9XQ=="> <a href="/" id="1415691439"><img src="https://le-cdn.hibuwebsites.com/f6cdfabba1c749a1a3d3b7a9f4c30c76/dms3rep/multi/opt/logo-jtn-electrical-1920w.jpg" alt="JTN Electrical Logo" id="1635795816" class="" width="529" height="170" data-dm-image-path="https://cdn.hibuwebsites.com/f6cdfabba1c749a1a3d3b7a9f4c30c76/dms3rep/multi/logo-jtn-electrical.jpg" data-diy-image="" onerror="handleImageLoadError(this)"/></a> 
2900</div> 
2901</div> 
2902</div> 
2903 <div id="11d216b4" class="flex-element group" data-auto="flex-element-group"> <div id="07327faa" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="spacer" data-external-id="1269358429"> <div data-element-type="spacer" class="dmSpacer" id="1269358429"></div> 
2904</div> 
2905</div> 
2906 <div id="3aa37ebb" class="flex-element group" data-auto="flex-element-group"> <div id="5801a4e8" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="hamburgerButton" data-external-id="1460227856"> <button class="hamburgerButton" data-element-type="hamburgerButton" id="1460227856" aria-label="Open menu" aria-controls="hamburger-drawer" aria-expanded="false"> <svg width="100pt" height="100pt" version="1.1" viewbox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" id="1111394517"> <path d="m3.6172 10.754h-0.050782c-1.9727 0-3.5703 1.5977-3.5703 3.5703 0 1.9727 1.5977 3.5703 3.5703 3.5703h0.054688 92.758 0.050782c1.9727 0 3.5703-1.5977 3.5703-3.5703 0-1.9727-1.5977-3.5703-3.5703-3.5703h-0.054688zm0 35.676h-0.050782c-1.9727 0-3.5703 1.5977-3.5703 3.5703s1.5977 3.5703 3.5703 3.5703h0.054688 92.758 0.050782c1.9727 0 3.5703-1.5977 3.5703-3.5703s-1.5977-3.5703-3.5703-3.5703h-0.054688zm0 35.68h-0.050782c-1.9727 0-3.5703 1.5977-3.5703 3.5703 0 1.9727 1.5977 3.5703 3.5703 3.5703h0.054688 92.758 0.050782c1.9727 0 3.5703-1.5977 3.5703-3.5703 0-1.9727-1.5977-3.5703-3.5703-3.5703h-0.054688z"></path> 
2907</svg> 
2908</button> 
2909</div> 
2910</div> 
2911</div> 
2912 <div id="148da2d9" class="flex-element group" data-auto="flex-element-group"> <div id="0ece3d12" class="flex-element group" data-auto="flex-element-group"> <div id="daf81bcc" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="link" data-external-id="1105358164"> <a data-display-type="block" class="align-center dmButtonLink dmWidget dmWwr default dmOnlyButton dmDefaultGradient flexButton u_1105358164 button_2" file="false" href="tel:(860) 215-2254" data-element-type="dButtonLinkId" id="1105358164"> <span class="iconBg" aria-hidden="true" id="1528562180"> <span class="icon hasFontIcon icon-star" id="1809587092"></span> 
2913</span> 
2914 <span class="text" id="1035871641">(860) 215-2254</span> 
2915</a> 
2916</div> 
2917</div> 
2918 <div id="a052d7aa" class="flex-element group" data-auto="flex-element-group"> <div id="df845449" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="link" data-external-id="1524906790"> <a data-display-type="block" class="align-center dmButtonLink dmWidget dmWwr default dmOnlyButton dmDefaultGradient flexButton u_1524906790 button_2 align-center dmButtonLink dmWidget dmWwr default dmOnlyButton dmDefaultGradient flexButton u_1524906790 button_2" file="false" href="/request-service-form" data-element-type="dButtonLinkId" id="1524906790" data-binding="W3siYmluZGluZ05hbWUiOiJidXR0b25fdGV4dCIsInZhbHVlIjoiU1RBVElDX1BBR0VfQ09MTEVDVElPTi57XCJjblwiOlwiYnV0dG9uc1wiLFwiZm5cIjpcIk1vYmlsZSBCdXR0b25cIn0ifSx7ImJpbmRpbmdOYW1lIjoibGluayIsInZhbHVlIjoiU1RBVElDX1BBR0VfQ09MTEVDVElPTi57XCJjblwiOlwiYnV0dG9uc1wiLFwiZm5cIjpcIlJlcXVlc3QgQ1RBIExpbmtcIn0ifV0=" data-diy-link=""> <span class="iconBg" aria-hidden="true" id="1847377451"> <span class="icon hasFontIcon icon-star" id="1804309602"></span> 
2919</span> 
2920 <span class="text" id="1149277214" data-diy-text="">Request Service</span> 
2921</a> 
2922</div> 
2923</div> 
2924</div> 
2925</div> 
2926</div> 
2927</div> 
2928</div> 
2929</section> 
2930</div> 
2931 <div data-layout-section="" data-auto="flex-section" id="1368876750" data-flex-id="157b4fff" class="" data-hidden-on-desktop="true" data-hidden-on-mobile="true"> <div class="flex-widgets-container" id="1825413659"></div> 
2932 <section id="726ad2b1" class="flex-element section" data-auto="flex-element-section"> <div id="30295aaf" class="flex-element grid" data-auto="flex-element-grid" data-layout-grid=""> <div id="32767c2f" class="flex-element group" data-auto="flex-element-group" data-hidden-on-desktop="" data-hidden-on-mobile=""> <div id="2fd7b414" class="flex-element group" data-auto="flex-element-group" data-hidden-on-desktop=""> <div id="widget_ie6" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="custom" data-hidden-on-mobile="" data-external-id="1288312793"> <div class="widget-9015bc u_1288312793 dmCustomWidget" data-lazy-load="true" data-title="" id="1288312793" dmle_extension="custom_extension" data-element-type="custom_extension" data-binding="W3siYmluZGluZ05hbWUiOiJtZXJjaGFudElkIiwidmFsdWUiOiJTVEFUSUNfUEFHRV9DT0xMRUNUSU9OLntcImNuXCI6XCJSZXZpZXdzIFdpZGdldHNcIixcImZuXCI6XCJNZXJjaGFudCBJRFwifSJ9XQ==" icon="false" surround="false" data-widget-id="9015bcbf1ace437aa6f0e66d5bb2e7d3" data-widget-version="25" data-widget-config="eyJtZXJjaGFudElkIjoiODA3OTgiLCJtaW4tcmV2aWV3cyI6IjMiLCJtaW4tcmF0aW5nIjoiNCIsInNob3ctd3JpdGUiOnRydWUsImhpZGUtc3VtbWFyeSI6dHJ1ZSwidGhlbWUiOiJsaWdodCJ9"> <div class="headerWidget" data-merchant="80798" data-theme="light" data-break="" data-count-limit="3" data-rating-limit="4" data-show-write="true" data-hide-summary="true"></div> 
2933</div> 
2934</div> 
2935</div> 
2936 <div id="group_jjh" class="flex-element group" data-auto="flex-element-group"> <div id="widget_5h5" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="image" data-keep-proportion="true" data-external-id="1303640445"> <div class="imageWidget align-center u_1303640445" data-element-type="image" data-widget-type="image" id="1303640445" data-binding="W3siYmluZGluZ05hbWUiOiJpbWFnZSIsInZhbHVlIjoic2l0ZV9pbWFnZXMucGVyc2lzdGVkLmRhcmstbG9nbyJ9XQ=="> <a href="/" id="1035792501"><img src="https://le-cdn.hibuwebsites.com/f6cdfabba1c749a1a3d3b7a9f4c30c76/dms3rep/multi/opt/logo-jtn-electrical-1920w.jpg" alt="JTN Electrical Logo" id="1107341754" class="" width="529" height="170" data-dm-image-path="https://cdn.hibuwebsites.com/f6cdfabba1c749a1a3d3b7a9f4c30c76/dms3rep/multi/logo-jtn-electrical.jpg" data-diy-image="" onerror="handleImageLoadError(this)"/></a> 
2937</div> 
2938</div> 
2939</div> 
2940 <div id="group_7in" class="flex-element group" data-auto="flex-element-group"> <div id="widget_vej" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="link" data-external-id="1997567173"> <a data-display-type="block" class="align-center dmButtonLink dmWidget dmWwr default dmOnlyButton dmDefaultGradient flexButton u_1997567173 button_2 align-center dmButtonLink dmWidget dmWwr default dmOnlyButton dmDefaultGradient flexButton u_1997567173 button_2" file="false" href="/contact" data-element-type="dButtonLinkId" id="1997567173" data-binding="W3siYmluZGluZ05hbWUiOiJsaW5rIiwidmFsdWUiOiJTVEFUSUNfUEFHRV9DT0xMRUNUSU9OLntcImNuXCI6XCJidXR0b25zXCIsXCJmblwiOlwiVGFiIC0gQ29udCAtIExpbmtcIn0ifSx7ImJpbmRpbmdOYW1lIjoiYnV0dG9uX3RleHQiLCJ2YWx1ZSI6IlNUQVRJQ19QQUdFX0NPTExFQ1RJT04ue1wiY25cIjpcImJ1dHRvbnNcIixcImZuXCI6XCJUYWIgLSBDb250IEJ1dHRvblwifSJ9XQ==" data-diy-link=""> <span class="iconBg" 
2940aria-hidden="true" id="1066072120"> <span class="icon hasFontIcon icon-star" id="1327803529"></span> 
2941</span> 
2942 <span class="text" id="1641422126" data-diy-text="">Contact Us</span> 
2943</a> 
2944</div> 
2945</div> 
2946 <div id="group_23b" class="flex-element group" data-auto="flex-element-group"> <div id="4598469b" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="hamburgerButton" data-external-id="1761070024"> <button class="hamburgerButton" data-element-type="hamburgerButton" id="1761070024" aria-label="Open menu" aria-controls="hamburger-drawer" aria-expanded="false"> <svg width="100pt" height="100pt" version="1.1" viewbox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" id="1639062494"> <path d="m3.6172 10.754h-0.050782c-1.9727 0-3.5703 1.5977-3.5703 3.5703 0 1.9727 1.5977 3.5703 3.5703 3.5703h0.054688 92.758 0.050782c1.9727 0 3.5703-1.5977 3.5703-3.5703 0-1.9727-1.5977-3.5703-3.5703-3.5703h-0.054688zm0 35.676h-0.050782c-1.9727 0-3.5703 1.5977-3.5703 3.5703s1.5977 3.5703 3.5703 3.5703h0.054688 92.758 0.050782c1.9727 0 3.5703-1.5977 3.5703-3.5703s-1.5977-3.5703-3.5703-3.5703h-0.054688zm0 35.68h-0.050782c-1.9727 0-3.5703 1.5977-3.5703 3.5703 0 1.9727 1.5977 3.5703 3.5703 3.5703h0.054688 92.758 0.050782c1.9727 0 3.5703-1.5977 3.5703-3.5703 0-1.9727-1.5977-3.5703-3.5703-3.5703h-0.054688z"></path> 
2947</svg> 
2948</button> 
2949</div> 
2950</div> 
2951 <div id="group_tbn" class="flex-element group" data-auto="flex-element-group"> <div id="widget_qp9" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="spacer" data-external-id="1685550797"> <div data-element-type="spacer" class="dmSpacer" id="1685550797"></div> 
2952</div> 
2953</div> 
2954</div> 
2955</div> 
2956</section> 
2957</div> 
2958 <div data-layout-section="" data-auto="flex-section" id="1058362846" data-flex-id="c0ae99ba" class="" data-hidden-on-mobile="true" data-hidden-on-tablet="true"> <div class="flex-widgets-container" id="1227336095"></div> 
2959 <section id="ac810934" class="flex-element section" data-auto="flex-element-section"> <div id="b7dee1fb" class="flex-element grid" data-auto="flex-element-grid" data-layout-grid=""> <div id="9360b347" class="flex-element group" data-auto="flex-element-group" data-hidden-on-mobile="" data-hidden-on-tablet=""> <div id="3710bc01" class="flex-element group" data-auto="flex-element-group" data-hidden-on-tablet=""> <div id="3a2d0b90" class="flex-element group" data-auto="flex-element-group" data-hidden-on-mobile="" data-hidden-on-tablet=""> <div id="be1b9f91" class="flex-element group" data-auto="flex-element-group"> <div id="423fc4a2" class="flex-element group" data-auto="flex-element-group"> <div id="3b22d9b0" class="flex-element group" data-auto="flex-element-group"> <div id="519ddbdc" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="custom" data-external-id="1642529323"> <div class="widget-9015bc u_1642529323 dmCustomWidget" data-lazy-load="true" data-title="" id="1642529323" dmle_extension="custom_extension" data-element-type="custom_extension" data-binding="W3siYmluZGluZ05hbWUiOiJtZXJjaGFudElkIiwidmFsdWUiOiJTVEFUSUNfUEFHRV9DT0xMRUNUSU9OLntcImNuXCI6XCJSZXZpZXdzIFdpZGdldHNcIixcImZuXCI6XCJNZXJjaGFudCBJRFwifSJ9XQ==" icon="false" surround="false" data-widget-id="9015bcbf1ace437aa6f0e66d5bb2e7d3" data-widget-version="25" data-widget-config="eyJtZXJjaGFudElkIjoiODA3OTgiLCJtaW4tcmV2aWV3cyI6IjMiLCJtaW4tcmF0aW5nIjoiNCIsInNob3ctd3JpdGUiOnRydWUsImhpZGUtc3VtbWFyeSI6ZmFsc2UsInRoZW1lIjoibGlnaHQifQ=="> <div class="headerWidget" data-merchant="80798" data-theme="light" data-break="" data-count-limit="3" data-rating-limit="4" data-show-write="true" data-hide-summary="false"></div> 
2960</div> 
2961</div> 
2962</div> 
2963 <div id="8caf0a70" class="flex-element group" data-auto="flex-element-group"> <div id="a8c6072d" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="paragraph" data-external-id="1857856301"> <div class="dmNewParagraph u_1857856301" data-element-type="paragraph" data-version="5" id="1857856301" style=""><p class="m-size-13 t-size-13 text-align-left size-13"><span class="m-font-size-13 t-font-size-13 font-size-13" style="color: var(--color_7); font-style: normal; display: unset;">Serving</span></p></div> 
2964</div> 
2965 <div id="3ad8c19c" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="paragraph" data-external-id="1153192226"> <div class="dmNewParagraph" data-element-type="paragraph" data-version="5" id="1153192226" data-diy-text="" style=""><p class="text-align-left size-24 t-size-20 m-size-19" style="line-height: 1.1;"><strong class="font-size-24 t-font-size-20 m-font-size-19" style="display: initial; color: var(--color_7); font-weight: bold;">Hartford County and</strong></p><p class="text-align-left size-24 t-size-20 m-size-19" style="line-height: 1.1;"><strong class="font-size-24 t-font-size-20 m-font-size-19" style="display: initial; color: var(--color_7); font-weight: bold;">Western Massachusetts</strong></p></div> 
2966</div> 
2967</div> 
2968</div> 
2969 <div id="e3dfb55d" class="flex-element group" data-auto="flex-element-group"> <div id="6881434d" class="flex-element group" data-auto="flex-element-group"> <div id="5bfb3718" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="image" data-keep-proportion="true" data-external-id="1220116170"> <div class="imageWidget align-center" data-element-type="image" data-widget-type="image" id="1220116170" data-binding="W3siYmluZGluZ05hbWUiOiJpbWFnZSIsInZhbHVlIjoic2l0ZV9pbWFnZXMucGVyc2lzdGVkLmRhcmstbG9nbyJ9XQ=="> <a href="/" id="1909084297"><img src="https://le-cdn.hibuwebsites.com/f6cdfabba1c749a1a3d3b7a9f4c30c76/dms3rep/multi/opt/logo-jtn-electrical-1920w.jpg" alt="JTN Electrical Logo" id="1563401796" class="" width="529" height="170" data-dm-image-path="https://cdn.hibuwebsites.com/f6cdfabba1c749a1a3d3b7a9f4c30c76/dms3rep/multi/logo-jtn-electrical.jpg" data-diy-image="" onerror="handleImageLoadError(this)"/></a> 
2970</div> 
2971</div> 
2972</div> 
2973</div> 
2974 <div id="5cbebde5" class="flex-element group" data-auto="flex-element-group"> <div id="d3a805c8" class="flex-element group" data-auto="flex-element-group"> <div id="e77f39e9" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="link" data-external-id="1234177366"> <a data-display-type="block" class="align-center dmButtonLink dmWidget dmWwr default dmOnlyButton dmDefaultGradient flexButton u_1234177366 button_2 align-center dmButtonLink dmWidget dmWwr default dmOnlyButton dmDefaultGradient flexButton u_1234177366 button_2" file="false" href="/request-service-form" data-element-type="dButtonLinkId" id="1234177366" data-binding="W3siYmluZGluZ05hbWUiOiJidXR0b25fdGV4dCIsInZhbHVlIjoiU1RBVElDX1BBR0VfQ09MTEVDVElPTi57XCJjblwiOlwiYnV0dG9uc1wiLFwiZm5cIjpcIkRlc2sgLyBUYWIgQnV0dG9uXCJ9In0seyJiaW5kaW5nTmFtZSI6ImxpbmsiLCJ2YWx1ZSI6IlNUQVRJQ19QQUdFX0NPTExFQ1RJT04ue1wiY25cIjpcImJ1dHRvbnNcIixcImZuXCI6XCJSZXF1ZXN0IENUQSBMaW5rXCJ9In1d" data-diy-link=""> <span class="iconBg" aria-hidden="true" id="1281457075"> <span class="icon hasFontIcon icon-star" id="1446615084"></span> 
2975</span> 
2976 <span class="text" id="1422705019" data-diy-text="">Request Service</span> 
2977</a> 
2978</div> 
2979</div> 
2980 <div id="6a5bfb4c" class="flex-element group" data-auto="flex-element-group"> <div id="746860ee" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="paragraph" data-external-id="1791170611"> <div class="dmNewParagraph" data-element-type="paragraph" data-version="5" id="1791170611" style=""><p class="text-align-right m-size-13 t-size-13 size-13"><span class="m-font-size-13 t-font-size-13 font-size-13" style="color: var(--color_7); font-style: normal; display: unset;">Call Us Today</span></p></div> 
2981</div> 
2982 <div id="0ad35782" class="flex-element widget-wrapper YXTPH" data-auto="flex-element-widget-wrapper" data-widget-type="paragraph" data-external-id="1127636214"> <div class="dmNewParagraph u_1127636214" data-element-type="paragraph" data-version="5" id="1127636214" data-diy-text="" style="" data-binding="W3siYmluZGluZ05hbWUiOiJ0ZXh0IiwidmFsdWUiOiJjb250ZW50X2xpYnJhcnkuZ2xvYmFsLnBob25lLmxvYy0wIC0gbWFpbiJ9XQ==">(860) 215-2254</div> 
2983</div> 
2984</div> 
2985</div> 
2986</div> 
2987</div> 
2988 <div id="bd946f29" class="flex-element group" data-auto="flex-element-group" data-hidden-on-mobile="" data-hidden-on-tablet=""> <div id="51d6dafc" class="flex-element widget-wrapper navmasterdefault" data-auto="flex-element-widget-wrapper" data-widget-type="ONELinksMenu" data-hidden-on-tablet="" data-hidden-on-mobile="" data-hidden-on-desktop="" data-external-id="1398948466"> <nav class="effect-bottom2 u_1398948466 main-navigation unifiednav dmLinksMenu" data-auto="links-menu" role="navigation" layout-main="horizontal_nav_layout_1" layout-sub="submenu_horizontal_1" data-show-vertical-sub-items="HOVER" id="1398948466" dmle_extension="onelinksmenu" data-element-type="onelinksmenu" data-logo-src="" alt="" data-nav-structure="HORIZONTAL" wr="true" icon="true" surround="true" adwords="" navigation-id="unifiedNav"> <ul role="menubar" class="unifiednav__container unav-top  " data-auto="navigation-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/" class="unifiednav__item unav-item unav-top-item  dmUDNavigationItem_00  " target="" data-target-page-alias=""> <span class="nav-item-text " data-link-text="
2989         Home
2990        " data-auto="page-text-style">Home<span class="icon icon-angle-down"></span> 
2991</span> 
2992</a> 
2993</li> 
2994 <li role="menuitem" aria-haspopup="true" aria-expanded="false" data-sub-nav-menu="true" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/about" class="unifiednav__item unav-item unav-top-item  dmUDNavigationItem_010101599305  unifiednav__item_has-sub-nav" target="" data-target-page-alias=""> <span class="nav-item-text " data-link-text="About" data-auto="page-text-style">About<span class="icon icon-angle-down"></span> 
2995</span> 
2996</a> 
2997 <ul role="menu" class="unifiednav__container unifiednav__container_sub-nav unav-sub" data-depth="0" data-auto="sub-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/faqs" class="unifiednav__item unav-item   dmUDNavigationItem_010101347615  " target="" data-target-page-alias=""> <span class="nav-item-text " data-link-text="FAQs" data-auto="page-text-style">FAQs<span class="icon icon-angle-right"></span> 
2998</span> 
2999</a> 
3000</li> 
3001 <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/reviews" class="unifiednav__item unav-item   dmUDNavigationItem_010101840192  " target="" data-target-page-alias=""> <span class="nav-item-text " data-link-text="Reviews" data-auto="page-text-style">Reviews<span class="icon icon-angle-right"></span> 
3002</span> 
3003</a> 
3004</li> 
3005</ul> 
3006</li> 
3007 <li role="menuitem" aria-haspopup="true" aria-expanded="false" data-sub-nav-menu="true" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/contact" class="unifiednav__item unav-item unav-top-item  dmUDNavigationItem_010101152840  unifiednav__item_has-sub-nav" target="" data-target-page-alias=""> <span class="nav-item-text " data-link-text="Contact" data-auto="page-text-style">Contact<span class="icon icon-angle-down"></span> 
3008</span> 
3009</a> 
3010 <ul role="menu" class="unifiednav__container unifiednav__container_sub-nav unav-sub" data-depth="0" data-auto="sub-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/request-service-form" class="unifiednav__item unav-item   dmNavItemSelected  dmUDNavigationItem_010101150108  " target="" data-target-page-alias="" aria-current="page" data-auto="selected-page"> <span class="nav-item-text " data-link-text="Request Service" data-auto="page-text-style">Request Service<span class="icon icon-angle-right"></span> 
3011</span> 
3012</a> 
3013</li> 
3014 <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/contact#service-areas" class="unifiednav__item unav-item   dmUDNavigationItem_010101813309  " target="" data-target-page-alias=""> <span class="nav-item-text " data-link-text="Areas Served" data-auto="page-text-style">Areas Served<span class="icon icon-angle-right"></span> 
3015</span> 
3016</a> 
3017</li> 
3018</ul> 
3019</li> 
3020</ul> 
3021</nav> 
3022</div> 
3023 <div id="widget_7d2" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="custom" data-external-id="1963014488"> <div class="widget-5b093d u_1963014488 dmCustomWidget" data-lazy-load="true" data-title="" id="1963014488" dmle_extension="custom_extension" data-element-type="custom_extension" icon="false" surround="false" data-widget-id="5b093d0fb9474c1dabfa7807be463b12" data-widget-version="52" data-widget-config="eyJjaGlsZFVybFByZWZpeCI6InNlcnZpY2VzIiwidXNlVW5pdmVyc2FsQ29sb3JzIjp0cnVlLCJsb2FkaW5nRGlzcGxheUdyb3VwIjpudWxsLCJ1c2VVbml2ZXJzYWxUeXBvZ3JhcGh5Ijp0cnVlLCJtZWdhQ2hpbGRGb250U2l6ZSI6IjE0IiwicmVzdG9yZUZ1bGxEZWZhdWx0cyI6ZmFsc2UsIm1lZ2EzTGFiZWwiOiJNZWdhIDMiLCJ0b3BMZXZlbEdhcCI6IjE1IiwibWVnYVRyaWdnZXJDaGV2cm9uR2FwIjoiMTIiLCJtZWdhMkFscGhhYmV0aXplIjpmYWxzZSwibWVnYTFHcm91cCI6bnVsbCwiYW5pbWF0ZVN1Yk5hdiI6ZmFsc2UsIm1lZ2EyTGFiZWwiOiJNZWdhIDIiLCJtZWdhNExhYmVsIjoiTWVnYSA0IiwibWVnYTJMYXlvdXQiOiJncmlkIiwibWVnYTFMYWJlbCI6IkVsZWN0cmljYWwgU2VydmljZXMiLCJhbmltYXRlSG92ZXJFZmZlY3RzIjpmYWxzZSwiY2FwaXRhbGl6ZU5hdiI6ZmFsc2UsInVuaXZlcnNhbEZvbnRXZWlnaHQiOiI1MDAiLCJtZWdhMk1heFdpZHRoIjoiMTYwMCIsInVuaXZlcnNhbEZvbnRTaXplIjoiMTYiLCJhbmltYXRlVG9wQ2hldnJvbnMiOmZhbHNlLCJsb2FkaW5nSXRlbUNvdW50IjoiNCIsImFuaW1hdGVNaXJyb3IiOmZhbHNlLCJ0b3BOYXZCYWNrZ3JvdW5k
3023Q29sb3IiOiIiLCJtYW51YWxOYXZpZ2F0aW9uSXRlbXMiOltdLCJtZWdhRmFtaWx5QmFja2dyb3VuZENvbG9yIjoidmFyKC0tY29sb3JfMTApIiwiYWN0aXZlVGV4dENvbG9yIjoidmFyKC0tY29sb3JfMTEpIiwidG9wQ2hldnJvblN0eWxlIjoiZmlsbGVkIiwiYW5pbWF0ZVRvcE5hdiI6ZmFsc2UsImFuaW1hdGVNZWdhRmFtaWxpZXMiOmZhbHNlLCJ0b3BDaGV2cm9uQ29sb3IiOiIjMjIyMjIyIiwibWVnYUNoZXZyb25Db2xvciI6IiMyMjIyMjIiLCJtZWdhQ2hpbGRGb250V2VpZ2h0IjoiNDAwIiwic3ViU3ViRm9udFdlaWdodCI6IjQwMCIsIm5hdGl2ZVBhcmVudHMiOlt7InNob3dQYXJlbnQiOnRydWUsInB1bGxDaGlsZHJlbiI6dHJ1ZSwibGFiZWwiOiIiLCJ1cmwiOnsidmFsdWUiOiJhYm91dCIsImxhYmVsIjoiIiwidHlwZSI6InBhZ2UiLCJocmVmIjoiL2Fib3V0IiwicmF3X3VybCI6Ii9zaXRlLzUyYWQxOTM1ZTQ1NjRiMjRhNzgyNDBiMzgyZjQxYTM5L2Fib3V0P25lZT10cnVlJmVkPXRydWUmc2hvd09yaWdpbmFsPXRydWUmcHJldmlldz10cnVlJmRtX3RyeV9tb2RlPXRydWUmZG1fY2hlY2tTeW5jPTEifX0seyJzaG93UGFyZW50Ijp0cnVlLCJwdWxsQ2hpbGRyZW4iOnRydWUsImxhYmVsIjoiIiwidXJsIjp7InZhbHVlIjoiY29udGFjdCIsImxhYmVsIjoiIiwidHlwZSI6InBhZ2UiLCJocmVmIjoiL2NvbnRhY3QiLCJyYXdfdXJsIjoiL3NpdGUvNTJhZDE5MzVlNDU2NGIyNGE3ODI0MGIzODJmNDFhMzkvY29udGFjdD9uZWU9dHJ1ZSZlZD10cnVlJnNob3dPcmlnaW5hbD10cnVlJnByZXZpZXc9dHJ1ZSZkbV90cnlfbW9kZT10cnVlJmRtX2NoZWNrU3luYz0xIn19XSwidXNlVW5pdmVyc2FsQmFja2dyb3VuZHMiOnRydWUsInVzZUN1c3RvbU5hdk9ubHkiOmZhbHNlLCJzdWJTdWJUZXh0Q29sb3IiOiIjMjIyMjIyIiwibWVnYTFNYXhXaWR0aCI6IjE2MDAiLCJtZWdhNEdyb3VwIjpudWxsLCJ0b3BGb250U2l6ZSI6IjE2IiwibWlycm9yQWxpZ25tZW50Ijoic3RhcnQiLCJtZWdhNEV4cGFuZEFsbCI6ZmFsc2UsInRleHRDb2xvcnNHcm91cCI6bnVsbCwic3ViRm9udFNpemUiOiIxNCIsInR5cG9ncmFwaHlHcm91cCI6bnVsbCwibWlycm9ySXRlbUdhcCI6IjAiLCJhbmltYXRlTWVnYU1lbnUiOmZhbHNlLCJtZWdhR3JhbmRjaGlsZFRleHRDb2xvciI6IiMyMjIyMjIiLCJtZWdhUGFyZW50VGV4dENvbG9yIjoiIzIyMjIyMiIsIm1pcnJvclN0eWxlcyI6bnVsbCwibWVnYTFMYXlvdXQiOiJtYXNvbnJ5IiwibWVnYUdyYW5kY2hpbGRGb250U2l6ZSI6IjE0IiwiaG92ZXJUZXh0Q29sb3IiOiJ2YXIoLS1jb2xvcl8xMSkiLCJtZWdhNENvbHVtbnMiOiIzIiwibWVnYTJFeHBhbmRBbGwiOmZhbHNlLCJtZWdhM0NvbHVtbnMiOiIzIiwiY2hldnJvbnNHcm91cCI6bnVsbCwibWVnYTFDb2x1bW5zIjoiMyIsIm1lZ2EyQ29sdW1ucyI6IjMiLCJsYXlvdXRQcmVzZXQiOiJjbGFzc2ljIiwic3ViU3ViTmF2QmFja2dyb3VuZENvbG9yIjoidmFyKC0tY29sb3JfMTApIiwicmVzdG9yZU1pcnJvckRlZmF1bHRzIjpmYWxzZSwic3ViRm9udFdlaWdodCI6IjQwMCIsInRvcEZvbnRXZWlnaHQiOiI1MDAiLCJtZWdhMUV4cGFuZEFsbCI6dHJ1ZSwibmF2QWxpZ25tZW50IjoiY2VudGVyIiwiYW5pbWF0ZU1lZ2FDaGV2cm9ucyI6ZmFsc2UsIndpZGdldE1vZGUiOiJmdWxsIiwic3ViVGV4dENvbG9yIjoiIzIyMjIyMiIsInN1Yk5hdkJhY2tncm91bmRDb2xvciI6InZhcigtLWNvbG9yXzEwKSIsIm1lZ2E0TWF4V2lkdGgiOiIxNjAwIiwiYmFja2dyb3VuZENvbG9yc0dyb3VwIjpudWxsLCJ0b3BUZXh0Q29sb3IiOiIjMjIyMjIyIiwibWlycm9yT3Blbk1lbnVzT25Ib3ZlciI6IiIsIm1lZ2FQYXJlbnRGb250U2l6ZSI6IjE2IiwibWVudUludGVyYWN0aW9uR3JvdXAiOm51bGwsIm1pcnJvclN0eWxpbmdHcm91cCI6bnVsbCwiZW5hYmxlQ3VzdG9tTmF2aWdhdGlvbkJ1aWxkZXIiOmZhbHNlLCJtZWdhMUFscGhhYmV0aXplIjpmYWxzZSwibWVnYTNHcm91cCI6bnVsbCwibWVnYTRBbHBoYWJldGl6ZSI6ZmFsc2UsImFuaW1hdGlvbnNFbmFibGVkIjpmYWxzZSwiY2F0ZWdvcnlVcmxQcmVmaXgiOiJ3aGF0LXdlLWRvIiwidW5pdmVyc2FsQ2hldnJvblN0eWxlIjoiZmlsbGVkIiwidW5pdmVyc2FsVGV4dENvbG9yIjoidmFyKC0tY29sb3JfMTEpIiwidXNlU2VwYXJhdGVNaXJyb3JTdHlsaW5nIjpmYWxzZSwibG9hZGluZ0FuaW1hdGlvblN0eWxlIjoic2hpbW1lciIsIm1lZ2EyR3JvdXAiOm51bGwsIm1lZ2FDaGV2cm9uU3R5bGUiOiJmaWxsZWQiLCJ1bml2ZXJzYWxDaGV2cm9uQ29sb3IiOiJ2YXIoLS1jb2xvcl8xMSkiLCJob3ZlckJhY2tncm91bmRDb2xvciI6InZhcigtLWNvbG9yXzEwKSIsIm1lZ2FNZW51QmFja2dyb3VuZENvbG9yIjoidmFyKC0tY29sb3JfMTApIiwidW5pdmVyc2FsQmFja2dyb3VuZENvbG9yIjoidmFyKC0tY29sb3JfMTApIiwibWVnYTNMYXlvdXQiOiJncmlkIiwibmF2aWdhdGlvblRvb2xzR3JvdXAiOm51bGwsIm1lZ2FQYXJlbnRGb250V2VpZ2h0IjoiNjAwIiwibWVnYUNoaWxkVGV4dENvbG9yIjoiIzIyMjIyMiIsInN1YlN1YkZvbnRTaXplIjoiMTQiLCJtZWdhM0FscGhhYmV0aXplIjpmYWxzZSwibGF5b3V0R3JvdXAiOm51bGwsInVuaXZlcnNhbFVuZGVybGluZSI6dHJ1ZSwiZW5hYmxlTmF2aWdhdGlvblRyZWVQcmV2aWV3IjpmYWxzZSwibWVnYUdyYW5kY2hpbGRGb250V2VpZ2h0IjoiNDAwIiwid2lkZ2V0TW9kZVVybHNHcm91cCI6bnVsbCwiYW5pbWF0aW9uc0dyb3VwIjpudWxsLCJvcGVuTWVudXNPbkhvdmVyIjp0cnVlLCJ1bml2ZXJzYWxDaGV2cm9uV2VpZ2h0IjoiMiIsIm1lZ2EzTWF4V2lkdGgiOiIxNjAwIiwibWVnYTRMYXlvdXQiOiJncmlkIiwiYW5pbWF0ZVN1YlN1Yk5hdiI6ZmFsc2UsIm1lZ2EzRXhwYW5kQWxsIjpmYWxzZSwidW5pdmVyc2FsQ2hldnJvblNpemUiOiIxMCIsInVzZVVuaXZlcnNhbENoZXZyb25TZXR0aW5ncyI6dHJ1ZX0="><!-- NMW Full Feature Stable: Manual Navigation Items live-update fix --><!--
3024  Use this same HTML for both widget instances.
3025
3026  Content Panel widgetMode:
3027    full   = loads Collection, Native, CUSTOMNAV, and Manual navigation data
3028    mirror = renders the Full widget's shared navigation vertically
3029
3030  The hidden manual source is required because Duda link inputs must be output
3031  through the custom_link helper. JavaScript reads the Duda-generated anchor
3032  attributes and moves them into the rendered navigation.
3033
3034  Manual Folder items do not require a link. An empty manual Folder is shown as
3035  a disabled preview inside the Duda editor, then omitted from preview/live pages
3036  until a valid child is assigned.
3037--> <div class="nmw-root" data-nmw-mode="full"> <a class="nmw-skip-link" href="#nmw-full-main-nav">Skip to navigation</a> 
3038 <div class="nmw-loading" data-nmw-loading-view="" role="status" aria-l
3038ive="polite" aria-label="Loading navigation" hidden=""></div> 
3039 <div class="nmw-mobile-nav-wrapper nmw-render" data-nmw-render=""></div> 
3040 <div class="nmw-tools" data-nmw-tools="" aria-live="polite"></div> 
3041 <div class="nmw-manual-items-source" data-nmw-manual-source="" aria-hidden="true" hidden=""></div> 
3042</div> 
3043</div> 
3044</div> 
3045</div> 
3046 <div id="a8f32aec" class="flex-element group" data-auto="flex-element-group" data-hidden-on-mobile="" data-hidden-on-tablet="" data-hidden-on-desktop=""> <div id="392df140" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="ONELinksMenu" data-hidden-on-tablet="" data-hidden-on-desktop="" data-external-id="1915042179"> <nav class="effect-bottom2 u_1915042179 main-navigation unifiednav dmLinksMenu" data-auto="links-menu" role="navigation" layout-main="horizontal_nav_layout_1" layout-sub="submenu_horizontal_1" data-show-vertical-sub-items="HOVER" id="1915042179" dmle_extension="onelinksmenu" data-element-type="onelinksmenu" data-logo-src="" alt="" data-nav-structure="HORIZONTAL" wr="true" icon="true" surround="true" adwords="" navigation-id="unifiedNav"> <ul role="menubar" class="unifiednav__container unav-top  " data-auto="navigation-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/" class="unifiednav__item unav-item unav-top-item    " target="" data-target-page-alias="home"> <span class="nav-item-text " data-link-text="
3047         Home
3048        " data-auto="page-text-style">Home<span class="icon icon-angle-down" data-hidden-on-mobile="" data-hidden-on-desktop="" data-hidden-on-tablet=""></span> 
3049</span> 
3050</a> 
3051</li> 
3052 <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <button type="button" class="unifiednav__item unav-item unav-top-item    " data-target-page-alias="mega-menu-4dc8a903"> <span class="nav-item-text " data-link-text="Services" data-auto="page-text-style">Services<span class="icon icon-angle-down" data-hidden-on-mobile="" data-hidden-on-desktop="" data-hidden-on-tablet=""></span> 
3053</span> 
3054</button> 
3055</li> 
3056 <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/reviews" class="unifiednav__item unav-item unav-top-item    " target="" data-target-page-alias="reviews"> <span class="nav-item-text " data-link-text="Reviews" data-auto="page-text-style">Reviews<span class="icon icon-angle-down" data-hidden-on-mobile="" data-hidden-on-desktop="" data-hidden-on-tablet=""></span> 
3057</span> 
3058</a> 
3059</li> 
3060 <li role="menuitem" aria-haspopup="true" aria-expanded="false" data-sub-nav-menu="true" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/about" class="unifiednav__item unav-item unav-top-item    unifiednav__item_has-sub-nav" target="" data-target-page-alias="about"> <span class="nav-item-text " data-link-text="About" data-auto="page-text-style">About<span class="icon icon-angle-down"></span> 
3061</span> 
3062</a> 
3063 <ul role="menu" class="unifiednav__container unifiednav__container_sub-nav unav-sub" data-depth="0" data-auto="sub-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/faqs" class="unifiednav__item unav-item     " target="" data-target-page-alias="faqs"> <span class="nav-item-text " data-link-text="FAQs" data-auto="page-text-style">FAQs<span class="icon icon-angle-right" data-hidden-on-mobile="" data-hidden-on-desktop="" data-hidden-on-tablet=""></span> 
3064</span> 
3065</a> 
3066</li> 
3067</ul> 
3068</li> 
3069 <li role="menuitem" aria-haspopup="true" aria-expanded="false" data-sub-nav-menu="true" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/contact" class="unifiednav__item unav-item unav-top-item    unifiednav__item_has-sub-nav" target="" data-target-page-alias="contact"> <span class="nav-item-text " data-link-text="Contact" data-auto="page-text-style">Contact<span class="icon icon-angle-down"></span> 
3070</span> 
3071</a> 
3072 <ul role="menu" class="unifiednav__container unifiednav__container_sub-nav unav-sub" data-depth="0" data-auto="sub-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/request-service-form" class="unifiednav__item unav-item   dmNavItemSelected    " target="" data-target-page-alias="request-form-page" aria-current="page" data-auto="selected-page"> <span class="nav-item-text " data-link-text="Request Service" data-auto="page-text-style">Request Service<span class="icon icon-angle-right" data-hidden-on-mobile="" data-hidden-on-desktop="" data-hidden-on-tablet=""></span> 
3073</span> 
3074</a> 
3075</li> 
3076 <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/service-area" class="unifiednav__item unav-item     " target="" data-target-page-alias="service-area"> <span class="nav-item-text " data-link-text="Serving Area" data-auto="page-text-style">Serving Area<span class="icon icon-angle-right" data-hidden-on-mobile="" data-hidden-on-desktop="" data-hidden-on-tablet=""></span> 
3077</span> 
3078</a> 
3079</li> 
3080</ul> 
3081</li> 
3082 <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/locations" class="unifiednav__item unav-item unav-top-item    " target="" data-target-page-alias="locations"> <span class="nav-item-text " data-link-text="Locations" data-auto="page-text-style">Locations<span class="icon icon-angle-down" data-hidden-on-mobile="" data-hidden-on-desktop="" data-hidden-on-tablet=""></span> 
3083</span> 
3084</a> 
3085</li> 
3086</ul> 
3087</nav> 
3088</div> 
3089</div> 
3090</div> 
3091</div> 
3092</div> 
3093</section> 
3094</div> 
3095</div> 
3096 <div id="hamburger-drawer" class="hamburger-drawer layout-drawer" layout="5ff40d2f36ae4e5ea1bc96b2e2fcf67e===header" data-origin="side-reverse"> <div data-layout-section="" data-auto="flex-section" id="1825997552" data-flex-id="4a710abd"> <div class="flex-widgets-container" id="1217094164"></div> 
3097 <section id="bb7ac507" class="flex-element section" data-auto="flex-element-section"> <div id="d3f5447c" class="flex-element grid" data-auto="flex-element-grid" data-layout-grid=""> <div id="78db3927" class="flex-element group" data-auto="flex-element-group"> <div id="a7708a16" class="flex-element group" data-auto="flex-element-group"> <div id="8e45e82b" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="hamburgerButton" data-external-id="1202219406"> <button class="hamburgerButton" data-element-type="hamburgerButton" id="1202219406" aria-label="Close menu" aria-controls="hamburger-drawer" aria-expanded="false"> <svg width="100pt" height="100pt" version="1.1" viewbox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" id="1928507629"> <path d="m82 87.883c1.625 1.625 4.2656 1.625 5.8906 0 1.6289-1.6289 1.6289-4.2656 0-5.8945l-32.414-32.41 32.406-32.406c1.6289-1.625 1.6289-4.2656
3098 0-5.8906-1.6289-1.6289-4.2656-1.6289-5.8906 0l-32.406 32.402-32.406-32.406c-1.625-1.625-4.2656-1.625-5.8906 0-1.6289 1.6289-1.6289 4.2656 0
3099 5.8945l32.402 32.406-32.41 32.414c-1.6289 1.625-1.6289 4.2617 0 5.8906 1.625 1.6289 4.2656 1.6289 5.8906 0l32.414-32.414z" fill-rule="evenodd"></path> 
3100</svg> 
3101</button> 
3102</div> 
3103</div> 
3104</div> 
3105</div> 
3106</section> 
3107</div> 
3108 <div data-auto="flex-section" id="1099442592" data-flex-id="63a049320d35b55b4ef2a372" data-layout-section=""> <div class="flex-widgets-container" id="1247649854"></div> 
3109 <section id="63a049320d35b55b4ef2a373" class="flex-element section" data-auto="flex-element-section"> <div id="63a049320d35b55b4ef2a376" class="flex-element grid" data-auto="flex-element-grid" data-layout-grid=""> <div id="63a049320d35b55b4ef2a377" class="flex-element group" data-auto="flex-element-group"> <div id="63a049320d35b55b4ef2a374" class="flex-element group" data-auto="flex-element-group"> <div id="widget_la2" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="custom" data-external-id="4003453145"> <div class="widget-5b093d dmCustomWidget" data-lazy-load="true" data-title="" id="4003453145" dmle_extension="custom_extension" data-element-type="custom_extension" icon="false" surround="false" data-widget-id="5b093d0fb9474c1dabfa7807be463b12" data-widget-version="52" data-widget-config="eyJjaGlsZFVybFByZWZpeCI6InNlcnZpY2VzIiwidXNlVW5pdmVyc2FsQ29sb3JzIjp0cnVlLCJsb2FkaW5nRGlzcGxheUdyb3VwIjpudWxsLCJ1c2VVbml2ZXJzYWxUeXBvZ3JhcGh5Ijp0cnVlLCJtZWdhQ2hpbGRGb250U2l6ZSI6IjE0IiwicmVzdG9yZUZ1bGxEZWZhdWx0cyI6ZmFsc2UsIm1lZ2EzTGFiZWwiOiJNZWdhIDMiLCJ0b3BMZXZlbEdhcCI6IjMyIiwibWVnYVRyaWdnZXJDaGV2cm9uR2FwIjoiMTIiLCJtZWdhMkFscGhhYmV0aXplIjpmYWxzZSwibWVnYTFHcm91cCI6bnVsbCwiYW5pbWF0ZVN1Yk5hdiI6dHJ1ZSwibWVnYTJMYWJlbCI6Ik1lZ2EgMiIsIm1lZ2E0TGFiZWwiOiJNZWdhIDQiLCJtZWdhMkxheW91dCI6ImdyaWQiLCJtZWdhMUxhYmVsIjoiTWVnYSAxIiwiYW5pbWF0ZUhvdmVyRWZmZWN0cyI6dHJ1ZSwiY2FwaXRhbGl6ZU5hdiI6ZmFsc2UsInVuaXZlcnNhbEZvbnRXZWlnaHQiOiI1MDAiLCJtZWdhMk1heFdpZHRoIjoiMTYwMCIsInVuaXZlcnNhbEZvbnRTaXplIjoiMTYiLCJhbmltYXRlVG9wQ2hldnJvbnMiOnRydWUsImxvYWRpbmdJdGVtQ291bnQiOiI0IiwiYW5pbWF0ZU1pcnJvciI6dHJ1ZSwidG9wTmF2QmFja2dyb3VuZENvbG9yIjoiIiwibWFudWFsTmF2aWdhdGlvbkl0ZW1zIjpbXSwibWVnYUZhbWlseUJhY2tncm91bmRDb2xvciI6IiIsImFjdGl2ZVRleHRDb2xvciI6ImN1cnJlbnRDb2xvciIsInRvcENoZXZyb25TdHlsZSI6ImZpbGxlZCIsImFuaW1hdGVUb3BOYXYiOnRydWUsImFuaW1hdGVNZWdhRmFtaWxpZXMiOnRydWUsInRvcENoZXZyb25Db2xvciI6IiMyMjIyMjIiLCJtZWdhQ2hldnJvbkNvbG9yIjoiIzIyMjIyMiIsIm1lZ2FDaGlsZEZvbnRXZWlnaHQiOiI0MDAiLCJzdWJTdWJGb250V2VpZ2h0IjoiNDAwIiwibmF0aXZlUGFyZW50cyI6W10sInVzZVVuaXZlcnNhbEJhY2tncm91bmRzIjp0cnVlLCJ1c2VDdXN0b21OYXZPbmx5IjpmYWxzZSwic3ViU3ViVGV4dENvbG9yIjoiIzIyMjIyMiIsIm1lZ2ExTWF4V2lkdGgiOiIxNjAwIiwibWVnYTRHcm91cCI6bnVsbCwidG9wRm9udFNpemUiOiIxNiIsIm1pcnJvckFsaWdubWVudCI6InN0YXJ0IiwibWVnYTRFeHBhbmRBbGwiOmZhbHNlLCJ0ZXh0Q29sb3JzR3JvdXAiOm51bGwsInN1YkZvbnRTaXplIjoiMTQiLCJ0eXBvZ3JhcGh5R3JvdXAiOm51bGwsIm1pcnJvckl0ZW1HYXAiOiIwIiwiYW5pbWF0ZU1lZ2FNZW51Ijp0cnVlLCJtZWdhR3JhbmRjaGlsZFRleHRDb2xvciI6IiMyMjIyMjIiLCJtZWdhUGFyZW50VGV4dENvbG9yIjoiIzIyMjIyMiIsIm1pcnJvclN0eWxlcyI6bnVsbCwibWVnYTFMYXlvdXQiOiJncmlkIiwibWVnYUdyYW5kY2hpbGRGb250U2l6ZSI6IjE0IiwiaG92ZXJUZXh0Q29sb3IiOiJjdXJyZW50Q29sb3IiLCJtZWdhNENvbHVtbnMiOiIzIiwibWVnYTJFeHBhbmRBbGwiOmZhbHNlLCJtZWdhM0NvbHVtbnMiOiIzIiwiY2hldnJvbnNHcm91cCI6bnVsbCwibWVnYTFDb2x1bW5zIjoiMyIsIm1lZ2EyQ29sdW1ucyI6IjMiLCJsYXlvdXRQcmVzZXQiOiJjbGFzc2ljIiwic3ViU3ViTmF2QmFja2dyb3VuZENvbG9yIjoiI2ZmZmZmZiIsInJlc3RvcmVNaXJyb3JEZWZhdWx0cyI6ZmFsc2UsInN1YkZvbnRXZWlnaHQiOiI0MDAiLCJ0b3BGb250V2VpZ2h0IjoiNTAwIiwibWVnYTFFeHBhbmRBbGwiOmZhbHNlLCJuYXZBbGlnbm1lbnQiOiJjZW50ZXIiLCJhbmltYXRlTWVnYUNoZXZyb25zIjp0cnVlLCJ3aWRnZXRNb2RlIjoibWlycm9yIiwic3ViVGV4dENvbG9yIjoiIzIyMjIyMiIsInN1Yk5hdkJhY2tncm91bmRDb2xvciI6IiNmZmZmZmYiLCJtZWdhNE1heFdpZHRoIjoiMTYwMCIsImJhY2tncm91bmRDb2xvcnNHcm91cCI6bnVsbCwidG9wVGV4dENvbG9yIjoiIzIyMjIyMiIsIm1pcnJvck9wZW5NZW51c09uSG92ZXIiOiIiLCJtZWdhUGFyZW50Rm9udFNpemUiOiIxNiIsIm1lbnVJbnRlcmFjdGlvbkdyb3VwIjpudWxsLCJtaXJyb3JTdHlsaW5nR3JvdXAiOm51bGwsImVuYWJsZUN1c3RvbU5hdmlnYXRpb25CdWlsZGVyIjpmYWxzZSwibWVnYTFBbHBoYWJldGl6ZSI6ZmFsc2UsIm1lZ2EzR3JvdXAiOm51bGwsIm1lZ2E0QWxwaGFiZXRpemUiOmZ
3109hbHNlLCJhbmltYXRpb25zRW5hYmxlZCI6dHJ1ZSwiY2F0ZWdvcnlVcmxQcmVmaXgiOiJ3aGF0LXdlLWRvIiwidW5pdmVyc2FsQ2hldnJvblN0eWxlIjoiZmlsbGVkIiwidW5pdmVyc2FsVGV4dENvbG9yIjoiIzExMTExMSIsInVzZVNlcGFyYXRlTWlycm9yU3R5bGluZyI6ZmFsc2UsImxvYWRpbmdBbmltYXRpb25TdHlsZSI6InNoaW1tZXIiLCJtZWdhMkdyb3VwIjpudWxsLCJtZWdhQ2hldnJvblN0eWxlIjoiZmlsbGVkIiwidW5pdmVyc2FsQ2hldnJvbkNvbG9yIjoiIzIyMjIyMiIsImhvdmVyQmFja2dyb3VuZENvbG9yIjoiIiwibWVnYU1lbnVCYWNrZ3JvdW5kQ29sb3IiOiIjZmZmZmZmIiwidW5pdmVyc2FsQmFja2dyb3VuZENvbG9yIjoiI2ZmZmZmZiIsIm1lZ2EzTGF5b3V0IjoiZ3JpZCIsIm5hdmlnYXRpb25Ub29sc0dyb3VwIjpudWxsLCJtZWdhUGFyZW50Rm9udFdlaWdodCI6IjYwMCIsIm1lZ2FDaGlsZFRleHRDb2xvciI6IiMyMjIyMjIiLCJzdWJTdWJGb250U2l6ZSI6IjE0IiwibWVnYTNBbHBoYWJldGl6ZSI6ZmFsc2UsImxheW91dEdyb3VwIjpudWxsLCJ1bml2ZXJzYWxVbmRlcmxpbmUiOmZhbHNlLCJlbmFibGVOYXZpZ2F0aW9uVHJlZVByZXZpZXciOmZhbHNlLCJtZWdhR3JhbmRjaGlsZEZvbnRXZWlnaHQiOiI0MDAiLCJ3aWRnZXRNb2RlVXJsc0dyb3VwIjpudWxsLCJhbmltYXRpb25zR3JvdXAiOm51bGwsIm9wZW5NZW51c09uSG92ZXIiOmZhbHNlLCJ1bml2ZXJzYWxDaGV2cm9uV2VpZ2h0IjoiMiIsIm1lZ2EzTWF4V2lkdGgiOiIxNjAwIiwibWVnYTRMYXlvdXQiOiJncmlkIiwiYW5pbWF0ZVN1YlN1Yk5hdiI6dHJ1ZSwibWVnYTNFeHBhbmRBbGwiOmZhbHNlLCJ1bml2ZXJzYWxDaGV2cm9uU2l6ZSI6IjE0IiwidXNlVW5pdmVyc2FsQ2hldnJvblNldHRpbmdzIjp0cnVlfQ=="><!-- NMW Full Feature Stable: Manual Navigation Items live-update fix --><!--
3110  Use this same HTML for both widget instances.
3111
3112  Content Panel widgetMode:
3113    full   = loads Collection, Native, CUSTOMNAV, and Manual navigation data
3114    mirror = renders the Full widget's shared navigation vertically
3115
3116  The hidden manual source is required because Duda link inputs must be output
3117  through the custom_link helper. JavaScript reads the Duda-generated anchor
3118  attributes and moves them into the rendered navigation.
3119
3120  Manual Folder items do not require a link. An empty manual Folder is shown as
3121  a disabled preview inside the Duda editor, then omitted from preview/live pages
3122  until a valid child is assigned.
3123--> <div class="nmw-root" data-nmw-mode="full"> <a class="nmw-skip-link" href="#nmw-full-main-nav">Skip to navigation</a> 
3124 <div class="nmw-loading" data-nmw-loading-view="" role="status" aria-live="polite" aria-label="Loading navigation" hidden=""></div> 
3125 <div class="nmw-mobile-nav-wrapper nmw-render" data-nmw-render=""></div> 
3126 <div class="nmw-tools" data-nmw-tools="" aria-live="polite"></div> 
3127 <div class="nmw-manual-items-source" data-nmw-manual-source="" aria-hidden="true" hidden=""></div> 
3128</div> 
3129</div> 
3130</div> 
3131</div> 
3132</div> 
3133</div> 
3134</section> 
3135</div> 
3136 <div data-auto="flex-section" id="1029527021" data-flex-id="6406026c90e1521f6ecb0eca" data-layout-section=""> <div class="flex-widgets-container" id="1659590659"></div> 
3137 <section id="6406026c90e1521f6ecb0ec9" class="flex-element section" data-auto="flex-element-section"> <div id="6406026c90e1521f6ecb0ecb" class="flex-element grid" data-auto="flex-element-grid" data-layout-grid=""> <div id="6406026c90e1521f6ecb0ecc" class="flex-element group" data-auto="flex-element-group"> <div id="6406026c90e1521f6ecb0ecd" class="flex-element group" data-auto="flex-element-group"></div> 
3138</div> 
3139</div> 
3140</section> 
3141</div> 
3142</div> 
3143 <div class="layout-drawer-overlay" id="layout-drawer-overlay"></div> 
3144</div> 
3145 <div dmwrapped="true" id="dmFirstContainer" class="dmBody u_dmStyle_template_request-service-form dm-home-page" themewaschanged="true"> <div id="allWrapper" class="allWrapper" data-auto="all-wrapper"><!-- navigation placeholders --> <div id="dm_content" class="dmContent" role="main"> <div dm:templateorder="170" class="dmHomeRespTmpl mainBorder dmRespRowsWrapper dmFullRowRespTmpl" id="1716942098"> <div data-auto="flex-section" id="1899014018" data-flex-id="24b6b13b" data-layout-section="" class=""> <div class="flex-widgets-container" id="1800163092"></div> 
3146 <section id="3c671c77" class="flex-element section" data-auto="flex-element-section"> <div id="530811d6" class="flex-element grid" data-auto="flex-element-grid" data-layout-grid=""> <div id="34c91571" class="flex-element group" data-auto="flex-element-group"> <div id="d4329249" class="flex-element group" data-auto="flex-element-group" data-anim-extended="eyJkZXNrdG9wIjp7InRyaWdnZXIiOiJlbnRyYW5jZSIsImFuaW1hdGlvbiI6ImZhZGVJbkNvbWJvIiwiZHVyYXRpb24iOjAuNSwiZGVsYXkiOjAsImludGVuc2l0eSI6MSwiZGlyIjoiaW4tcGxhY2UifX0="> <div id="b3d39244" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="paragraph" data-external-id="1243815943"> <div class="dmNewParagraph" data-element-type="paragraph" data-version="5" id="1243815943" style=""> <h1><span style="text-shadow: none; display: initial;">Request Service</span></h1> 
3147</div> 
3148</div> 
3149 <div id="13869723" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="paragraph" data-external-id="1359807854"> <div class="dmNewParagraph u_1359807854" data-element-type="paragraph" data-version="5" id="1359807854" data-diy-text="" style="" data-binding="W3siYmluZGluZ05hbWUiOiJ0ZXh0IiwidmFsdWUiOiJjb250ZW50X2xpYnJhcnkuZ2xvYmFsLmNvbXBhbnlfbmFtZSJ9XQ==">JTN Electrical</div> 
3150</div> 
3151</div> 
3152</div> 
3153</div> 
3154</section> 
3155</div> 
3156 <div data-layout-section="" data-auto="flex-section" id="1479948
3156179" data-flex-id="7ef32d84" class="" data-hidden-on-mobile="true"> <div class="flex-widgets-container" id="1830000386"></div> 
3157 <section id="12933559" class="flex-element section" data-auto="flex-element-section"> <div id="d80bb809" class="flex-element grid" data-auto="flex-element-grid" data-layout-grid=""> <div id="6a108e83" class="flex-element group" data-auto="flex-element-group" data-hidden-on-mobile=""> <div id="8c30d83a" class="flex-element group" data-auto="flex-element-group"> <div id="79342a6b" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="ssrbreadcrumbs" data-external-id="1795077117"> <div id="1795077117" dmle_extension="ssrbreadcrumbs" data-element-type="ssrbreadcrumbs" class=""><span id="ssrWrap-1795077117" ><style data-styled="true" data-styled-version="5.3.11">#dm .euwdXq.euwdXq{color:var(--color_1);}/*!sc*/
3158data-styled.g17[id="sc-cPiKLX"]{content:"euwdXq,"}/*!sc*/
3159#dm .faqkoQ.faqkoQ{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:10px;}/*!sc*/
3160@media (min-width:768px){#dm .faqkoQ.faqkoQ{font-size:11px;}}/*!sc*/
3161@media (max-width:767px){#dm .faqkoQ.faqkoQ{font-size:12px;}}/*!sc*/
3162data-styled.g19[id="sc-eDPEul"]{content:"faqkoQ,"}/*!sc*/
3163#dm .evEVyk.evEVyk{-webkit-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0);display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:16px;}/*!sc*/
3164#dm .evEVyk.evEVyk svg{width:100%;height:100%;fill:currentColor;}/*!sc*/
3165data-styled.g20[id="sc-eldPxv"]{content:"evEVyk,"}/*!sc*/
3166</style><div data-auto="runtime-breadcrumbs-widget">
3166<script data-auto="schema" type="application/ld+json">{"@context":"http://schema.org/","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"name":"Home","id":"/"}},{"@type":"ListItem","position":2,"item":{"name":"Contact","id":"/contact"}},{"@type":"ListItem","position":3,"item":{"name":"Request Service"}}]}</script>
3166<nav class="sc-eDPEul faqkoQ bc-root dmBlockElement" aria-label="Breadcrumbs"><a href="/" class="sc-cPiKLX euwdXq bc-item"><div class="sc-dLMFU">Home</div></a><span direction="ltr" class="sc-eldPxv evEVyk bc-separator" aria-hidden="true"><svg data-auto="arrow" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M5.46967 3.46967C5.76256 3.17678 6.23744 3.17678 6.53033 3.46967L10.5303 7.46967C10.8232 7.76256 10.8232 8.23744 10.5303 8.53033L6.53033 12.5303C6.23744 12.8232 5.76256 12.8232 5.46967 12.5303C5.17678 12.2374 5.17678 11.7626 5.46967 11.4697L8.93934 8L5.46967 4.53033C5.17678 4.23744 5.17678 3.76256 5.46967 3.46967Z" fill="currentColor"></path></svg></span><a href="/contact" class="sc-cPiKLX euwdXq bc-item"><div class="sc-dLMFU">Contact</div></a><span direction="ltr" class="sc-eldPxv evEVyk bc-separator" aria-hidden="true"><svg data-auto="arrow" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M5.46967 3.46967C5.76256 3.17678 6.23744 3.17678 6.53033 3.46967L10.5303 7.46967C10.8232 7.76256 10.8232 8.23744 10.5303 8.53033L6.53033 12.5303C6.23744 12.8232 5.76256 12.8232 5.46967 12.5303C5.17678 12.2374 5.17678 11.7626 5.46967 11.4697L8.93934 8L5.46967 4.53033C5.17678 4.23744 5.17678 3.76256 5.46967 3.46967Z" fill="currentColor"></path></svg></span><div class="sc-dLMFU bc-current-item" data-auto="bc-current-item">Request Service</div></nav></div></span></div> 
3167</div> 
3168</div> 
3169</div> 
3170</div> 
3171</section> 
3172</div> 
3173 <div data-layout-section="" data-auto="flex-section" id="1391831113" data-flex-id="5c4456af" data-page-element-type="section" data-page-element-id="1273255118"> <div class="flex-widgets-container" id="1237728867"></div> 
3174 <section id="f0d2e0ac" class="flex-element section" data-auto="flex-element-section"> <div id="8ac9db8b" class="flex-element grid" data-auto="flex-element-grid" data-layout-grid=""> <div id="520e9609" class="flex-element group" data-auto="flex-element-group"> <div id="68fb7ec9" class="flex-element group" data-auto="flex-element-group"> <div id="b6321765" class="flex-element group" data-auto="flex-element-group"> <div id="ffec1cdf" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="paragraph" data-external-id="1177470371"> <div class="dmNewParagraph" data-element-type="paragraph" data-version="5" id="1177470371" style="" new-inline-bind-applied="true" data-diy-text=""><p class="text-align-left m-text-align-left"><span style="display: initial;">Fill out this short form and a <span class="inline-data-binding" data-encoded-value="SlROIEVsZWN0cmljYWw=" data-inline-binding="" data-inline-binding-encoded="Y29udGVudF9saWJyYXJ5Lmdsb2JhbC5jb21wYW55X25hbWU=">JTN Electrical</span> 
3175representative will contact you within 24 hours to discuss your service request.</span></p></div> 
3176</div> 
3177 <div id="98f8bad2" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="form" data-external-id="1638807636"> <div class="dmform default native-inputs u_1638807636" data-element-type="dContactUsRespId" captcha="true" data-require-captcha="true" data-captcha-position="bottomleft" id="1638807636" data-captcha-message="PHAgY2xhc3M9InJ0ZUJsb2NrIj5UaGlzIHNpdGUgaXMgcHJvdGVjdGVkIGJ5IHJlQ0FQVENIQS48L3A+" data-layout="layout-1" data-binding="W3siYmluZGluZ05hbWUiOiJlbWFpbCIsInZhbHVlIjoiY29udGVudF9saWJyYXJ5Lmdsb2JhbC5lbWFpbC5lbWFpbCAtIGxvYyAxIn1d"> <h3 class="dmform-title dmwidget-title" id="1960386174" hide="true">Request Service Form</h3> 
3178 <div class="dmform-wrapper" id="1074145252" captcha-lang="en"> <form method="post" class="dmRespDesignRow" locale="ENGLISH" id="1775640877"> <div class="spacing-container" id="1844431240"> <div class="dmforminput required  small-12 dmRespDesignCol medium-6 large-6" id="1037458903"> <label for="1619570817" id="1960303221" data-dm-for="dmform-0">Full Name*</label> 
3179 <input type="text" class="" name="dmform-0" id="1619570817"/>  <input type="hidden" name="label-dmform-0" value="Full Name*" id="1423678312" class=""/> </div> 
3180 <div class="dmforminput required  small-12 dmRespDesignCol medium-6 large-6" id="1228931911"> <label for="1954896164" id="1383818621" data-dm-for="dmform-2">Phone*</label> 
3181 <input type="tel" class="" name="dmform-2" id="1954896164"/>  <input type="hidden" name="label-dmform-2" value="Phone*" id="1309416493" class=""/> </div> 
3182 <div class="dmforminput required  small-12 medium-4 large-4 dmRespDesignCol newLine" id="1015672356"> <label for="1584259392" id="1629508607" data-dm-for="dmform-1">Email*</label> 
3183 <input type="email" class="" name="dmform-1" id="1584259392"/>  <input type="hidden" name="label-dmform-1" value="Email*" id="1009507548" class=""/> </div> 
3184 <div class="dmforminput small-12 medium-4 large-4 dmRespDesignCol" id="1426506771"> <label for="1365400678" id="1091567039" data-dm-for="dmform-5">Contact Method*</label> 
3185 <select name="dmform-5" id="1365400678"> <option selected="" disabled="" hidden="" value="" id="1490390531"></option> 
3186 <option id="1031555580">Email</option> 
3187 <option id="1938379120">Phone</option> 
3188</select> 
3189 <input type="hidden" name="label-dmform-5" value="Contact Method*" id="1372293369" class=""/> </div> 
3190 <div class="dmforminput small-12 dmRespDesignCol required medium-4 large-4" id="1116687880"> <label for="1110605039" id="1304437688" data-dm-for="dmform-7">Services*</label> 
3191<input type="text" name="dmform-7" placeholder="" id="1110605039"/><input type="hidden" name="label-dmform-7" value="Services*" id="1889619553" class=""/></div> 
3192 <div class="dmforminput large-12 medium-12 dmRespDesignCol" id="1469959121"> <label for="1896362933" id="1048395248" data-dm-for="dmform-3">Message</label> 
3193 <textarea name="dmform-3" id="1896362933" class=""></textarea> 
3194 <input type="hidden" name="label-dmform-3" value="Message" id="1880951334" class=""/> </div> 
3195</div> 
3196 <div class="dmformsubmit dmWidget R" id="1363170918"> <input class="" name="submit" type="submit" value="Request Service" id="1487091419"/> </div> 
3197 <input name="dmformsendto" type="hidden" id="1572965529" class="" value="O0wrseIfFLsS3rYZJm4ScOK8+BPBIktPNlH8uTOA3M/nZvgTXmfYrnLGKsXSZRIU73kO4KQzX2o=" data-dec="true"/>  <input class="dmActionInput" type="hidden" name="action" value="/_dm/s/rt/widgets/dmform.submit.jsp" id="1278708902"/>  <input name="dmformsubject" type="hidden" id="1144743687" class=""/>  <input name="dmformfrom" type="hidden" value="" id="1512941668"/>  <input name="dmformautoreplyenabled" type="hidden" value="false" id="1885237052" class=""/>  <input name="dmformautoreplyfrom" type="hidden" value="NOB" id="1345104531" class=""/>  <input name="dmformautoreplysubject" type="hidden" value="Client Provided Email Subject Line" id="1783791813" class=""/>  <input name="dmformautoreplymsg" type="hidden" value="PHAgY2xhc3M9InJ0ZUJsb2NrIj5DbGllbnQgcHJvdmlkZWQgYXV0byByZXNwb25zZSBlbWFpbCBjb3B5LjwvcD4=" id="1146584700" class=""/>  <input name="dmformautoreplyincludeformcopy" type="hidden" value="true" id="1080179296"/> <input name="dmformsubmitparams" type="hidden" value="8mpKnCSiNQXK/d9M7IDrSxJJnhBlHGKyYHAeNdkm917QQ7YczOvxrHCQLkvDvi7nrz90/AiWb36ZTEM0BsBl+e6biYRh3kpZrhpU2pYWFBcJH4lY+hgQtxSdqk06xMzLJhcKxjf2PMUVXvyhc20mVbEmuqt0CxC3/1EoHDWgQof9twwj16TbiEhPywzY052PYnu3UgKLBgKi4RXqyPl9M2Z0Jbmysd3+A5TJwXpMwAZ5jklP6MeAydrFvUDkU47Ac4Kmk/RE7Icy3HcTf+BbPVv9MEhalgfNkEikJfArWQlx4B37c0n9+ioZuhlP53Xspir+wysry/TbjPHhOWYL54WuFW2vJDsb6A8nZWZbrQSxcfPK5kPa8m58uVwq0EGRbxMDCzFZxTwRwmsS/768kzh5pml5l1Tz5Ner1oefi3TFbjcPvOFGPWKK4L9QEx8Qe9XyfMTi1LToO03FpoTIig==" data-dec="true"/><input type="hidden" name="page_uuid" value="8d67b06083f34b4a8a328c95f017fec2"/></form> 
3198</div> 
3199 <div class="dmform-success" style="display:none" id="1772601894" data-success-page="thank-you"><br/><p class="rteBlock"><strong>We appreciate you contacting us.</strong>&nbsp; &nbsp; One of our colleagues will get back in touch with you soon!&nbsp;</p><br/><p class="rteBlock">Have a great day!</p></div> 
3200 <div class="dmform-error" style="display:none" id="1134856273"><p class="rteBlock">Oops, there was an error sending your message.&nbsp;</p><br/><p class="rteBlock">Please try again later.</p></div> 
3201</div> 
3202</div> 
3203</div> 
3204</div> 
3205</div> 
3206</div> 
3207</section> 
3208</div> 
3209</div> 
3210</div> 
3211</div> 
3212</div> 
3213 <div class="dmFooterContainer flex_hfcontainer"> <div id="flex-footer" role="contentinfo"> <div data-layout-section="" data-auto="flex-section" id="1959439239" data-flex-id="2c8e291c" data-page-element-type="section" data-page-element-id="1601394852" class="" data-hidden-on-mobile="true"> <div class="flex-widgets-container" id="1357377095"></div> 
3214 <section id="21379086" class="flex-element section" data-auto="flex-element-section"> <div id="b5a3be9f" class="flex-element grid" data-auto="flex-element-grid" data-layout-grid=""> <div id="b7a700bf" class="flex-element group" data-auto="flex-element-group" data-hidden-on-desktop="" data-hidden-on-tablet="" data-hidden-on-mobile=""> <div id="6405d24c" class="flex-element group" data-auto="flex-element-group"> <div id="f9c6e236" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="photoGallery" data-external-id="1289582650"> <div class="dmPhotoGallery newPhotoGallery dmPhotoGalleryResp u_1289582650 photo-gallery-done text-layout-over captionAlignment-center_center photoGallery" galleryoptionsparams="{thumbnailsPerRow: 3, rowsToShow: 3, imageScaleMethod: true}" data-desktop-layout="square" data-desktop-columns="4" data-element-type="dPhotoGalleryId" data-desktop-text-layout="over" id="1289582650" data-auto-adjust-columns="true" data-image-hover-effect="zoomout" data-binding="W3siYmluZGluZ05hbWUiOiJwaG90b19nYWxsZXJ5IiwidmFsdWUiOiJzaXRlX2NvbGxlY3Rpb24uQnJhbmQgSWNvbnMifV0=" data-binding-metadata="eyJsaW5rIjoiIiwiaW1hZ2UiOiJpbWFnZSIsImRlc2NyaXB0aW9uIjoiIiwidGl0bGUiOiIiLCJhbHRfdGV4dCI6IiIsImxpbmtfdGV4dCI6IiJ9" data-paginate-total-elements="4"> <div class="dmPhotoGalleryHolder clearfix gallery shadowEffectToChildren gallery4inArow" id="1362986591"></div> 
3215 <div class="layout-container square"> <div class="photogallery-row " data-index=""> <div class="photogallery-column column-4" data-index="0"> <div index="0" class="photoGalleryThumbs animated  " data-index="0"> <div class="thumbnailInnerWrapper" style="opacity: 1;"> <div class="image-container revealed"> <a data-dm-multisize-attr="temp" data-dm-force-device="mobile" class="u_" data-image-url="https://cdn.hibuwebsites.com/33453d75968e405abe6f06a3faaa502b/dms3rep/multi/brand-logo-02.png" style="background-image: url('https://le-cdn.hibuwebsites.com/33453d75968e405abe6f06a3faaa502b/dms3rep/multi/opt/brand-logo-02-640w.png');"><img id="" data-src="https://le-cdn.hibuwebsites.com/33453d75968e405abe6f06a3faaa502b/dms3rep/multi/opt/brand-logo-02-1920w.png" alt="" onerror="handleImageLoadError(this)"/></a> 
3216</div> 
3217</div> 
3218</div> 
3219</div> 
3220 <div class="photogallery-column column-4" data-index="1"> <div index="1" class="photoGalleryThumbs animated  " data-index="1"> <div class="thumbnailInnerWrapper" style="opacity: 1;"> <div class="image-container revealed"> <a data-dm-multisize-attr="temp" data-dm-force-device="mobile" class="u_" data-image-url="https://cdn.hibuwebsites.com/33453d75968e405abe6f06a3faaa502b/dms3rep/multi/brand-logo-01.png" style="background-image: url('https://le-cdn.hibuwebsites.com/33453d75968e405abe6f06a3faaa502b/dms3rep/multi/opt/brand-logo-01-640w.png');"><img id="" data-src="https://le-cdn.hibuwebsites.com/33453d75968e405abe6f06a3faaa502b/dms3rep/multi/opt/brand-logo-01-1920w.png" alt="" onerror="handleImageLoadError(this)"/></a> 
3221</div> 
3222</div> 
3223</div> 
3224</div> 
3225 <div class="photogallery-column column-4" data-index="2"> <div index="2" class="photoGalleryThumbs animated  " data-index="2"> <div class="thumbnailInnerWrapper" style="opacity: 1;"> <div class="image-container revealed"> <a data-dm-multisize-attr="temp" data-dm-force-device="mobile" class="u_" data-image-url="https://cdn.hibuwebsites.com/33453d75968e405abe6f06a3faaa502b/dms3rep/multi/brand-logo-02.png" style="background-image: url('https://le-cdn.hibuwebsites.com/33453d75968e405abe6f06a3faaa502b/dms3rep/multi/opt/brand-logo-02-640w.png');"><img id="" data-src="https://le-cdn.hibuwebsites.com/33453d75968e405abe6f06a3faaa502b/dms3rep/multi/opt/brand-logo-02-1920w.png" alt="" onerror="handleImageLoadError(this)"/></a> 
3226</div> 
3227</div> 
3228</div> 
3229</div> 
3230 <div class="photogallery-column column-4" data-index="3"> <div index="3" class="photoGalleryThumbs animated  " data-index="3"> <div class="thumbnailInnerWrapper" style="opacity: 1;"> <div class="image-container revealed"> <a data-dm-multisize-attr="temp" data-dm-force-device="mobile" class="u_" data-image-url="https://cdn.hibuwebsites.com/33453d75968e405abe6f06a3faaa502b/dms3rep/multi/brand-logo-01.png" style="background-image: url('https://le-cdn.hibuwebsites.com/33453d75968e405abe6f06a3faaa502b/dms3rep/multi/opt/brand-logo-01-640w.png');"><img id="" data-src="https://le-cdn.hibuwebsites.com/33453d75968e405abe6f06a3faaa502b/dms3rep/multi/opt/brand-logo-01-1920w.png" alt="" onerror="handleImageLoadError(this)"/></a> 
3231</div> 
3232</div> 
3233</div> 
3234</div> 
3235</div> 
3236</div> 
3237</div> 
3238</div> 
3239</div> 
3240</div> 
3241</div> 
3242</section> 
3243</div> 
3244 <div data-auto="flex-section" id="1518098037" data-flex-id="7b00a5de" data-layout-section="" data-page-element-type="section" data-page-element-id="1761552858"> <div class="flex-widgets-container" id="1613662762"></div> 
3245 <section id="9328cbef" class="flex-element section" data-auto="flex-element-section"> <div id="1ebb7f19" class="flex-element grid" data-auto="flex-element-grid" data-layout-grid=""> <div id="6e06962d" class="flex-element group" data-auto="flex-element-group"> <div id="a019b36e" class="flex-element group" data-auto="flex-element-group"> <div id="934bad03" class="flex-element widget-wrapper YXTBUSNAME" data-auto="flex-element-widget-wrapper" data-widget-type="paragraph" data-external-id="1506966255"> <div class="dmNewParagraph" data-element-type="paragraph" data-version="5" id="1506966255" new-inline-bind-applied="true" data-diy-text="" style=""> <h3 class="m-size-16 t-size-16 size-18"><span class="m-font-size-16 t-font-size-16 font-size-18" style="display: unset; color: var(--color_2);">Learn more about</span></h3> 
3246 <h3><span style="color: var(--color_2); display: initial;"><span class="inline-data-binding" data-encoded-value="SGlidSBQbHVtYmVycw==" data-inline-binding="content_library.global.company_name" data-inline-binding-encoded="Y29udGVudF9saWJyYXJ5Lmdsb2JhbC5jb21wYW5
32465X25hbWU=">JTN Electrical</span></span></h3> 
3247</div> 
3248</div> 
3249 <div id="6f8e19aa" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="paragraph" data-external-id="1661506068"> <div class="dmNewParagraph" data-element-type="paragraph" data-version="5" id="1661506068" new-inline-bind-applied="true" data-diy-text="" style=""><p class="m-text-align-left text-align-left"><span style="color: var(--color_2); display: initial;"><span class="inline-data-binding" data-encoded-value="VGhpcyBpcyB0aGUgVmlkZW8gVHJhbnNjcmlwdC4gRWRpdCBpbiB0aGUgQ29udGVudCBMaWJyYXJ5Lg==" data-inline-binding="site_text.video transcript" data-inline-binding-encoded="c2l0ZV90ZXh0LnZpZGVvIHRyYW5zY3JpcHQ=">Serving the Windsor Locks, CT area, JTN Electrical specializes in residential and commercial electrical services. Emergency electrical services. Quick response times. Free, in-person estimates available. Call today.</span></span></p></div> 
3250</div> 
3251 <div id="bd7b2692" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="link" data-external-id="1035845042"> <a data-display-type="block" class="align-center dmButtonLink dmWidget dmWwr default dmOnlyButton dmDefaultGradient flexButton u_1035845042 button_2" file="false" href="/hibu-video-splash" data-element-type="dButtonLinkId" id="1035845042" link_type="popup" popup_target="hibu-video-splash"> <span class="iconBg" aria-hidden="true" id="1299251327"> <span class="icon hasFontIcon icon-star" id="1881817341"></span> 
3252</span> 
3253 <span class="text" id="1018125776">Watch Video</span> 
3254</a> 
3255</div> 
3256</div> 
3257 <div id="05e0ee19" class="flex-element group" data-auto="flex-element-group" data-binding="eyJiaW5kaW5nTmFtZSI6ImJhY2tncm91bmQiLCJ2YWx1ZSI6InNpdGVfaW1hZ2VzLnBlcnNpc3RlZC5WaWRlbyBTcGxhc2ggSW1hZ2UifQ==" style="background-image: url('https://le-cdn.hibuwebsites.com/f6cdfabba1c749a1a3d3b7a9f4c30c76/dms3rep/multi/opt/video-jtn-electrical-1920w.jpg') !important;"> <div id="a1b6269f" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="custom" data-external-id="1518332866"> <div class="widget-ca4188 u_1518332866 dmCustomWidget" data-lazy-load="" data-title="" id="1518332866" dmle_extension="custom_extension" data-element-type="custom_extension" icon="false" surround="false" data-widget-id="ca4188fe92024514a23a858f63ab6658" data-widget-version="18" data-widget-config="eyJ2aWRUaXRsZTEiOiIiLCJ2aWRUaXRsZTIiOiIiLCJwbGF5SWNvbiI6IjxzdmcgeG1sbnM9XCJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Z1wiIHdpZHRoPVwiMTAwXCIgaGVpZ2h0PVwiMTAwXCIgdmlld0JveD1cIjAgMCAxMDAgMTAwXCI+XG4gIDxwYXRoIGlkPVwiSWNvbl9mYS1yZWd1bGFyLWNpcmNsZS1wbGF5XCIgZGF0YS1uYW1lPVwiSWNvbiBmYS1yZWd1bGFyLWNpcmNsZS1wbGF5XCIgZD1cIk05MC42MjUsNTBBNDAuNjI1LDQwLjYyNSwwLDEsMCw1MCw5MC42MjUsNDAuNjI1LDQwLjYyNSwwLDAsMCw5MC42MjUsNTBaTTAsNTBhNTAsNTAsMCwxLDEsNTAsNTBBNTAsNTAsMCwwLDEsMCw1MFpNMzYuNzc3LDI4LjczYTQuNjkzLDQuNjkzLDAsMCwxLDQuNzQ2LjFMNjkuNjQ4LDQ2LjAxNmE0LjY5Miw0LjY5MiwwLDAsMSwwLDguMDA4TDQxLjUyMyw3MS4yMTFhNC43MzgsNC43MzgsMCwwLDEtNC43NDYuMSw0LjY1OCw0LjY1OCwwLDAsMS0yLjQtNC4wODJWMzIuODEzQTQuNjU4LDQuNjU4LDAsMCwxLDM2Ljc3NywyOC43M1pcIi8+XG48L3N2Zz5cbiIsInBsYXlMaW5rIjp7InZhbHVlIjoiaGlidS12aWRlby1zcGxhc2giLCJ0eXBlIjoicG9wdXAiLCJocmVmIjoiL2hpYnUtdmlkZW8tc3BsYXNoIiwicmF3X3VybCI6Ii9zaXRlL2Y2Y2RmYWJiYTFjNzQ5YTFhM2QzYjdhOWY0YzMwYzc2L2hpYnUtdmlkZW8tc3BsYXNoP25lZT10cnVlJmVkPXRydWUmc2hvd09yaWdpbmFsPXRydWUmcHJldmlldz10cnVlJmRtX3RyeV9tb2RlPXRydWUmZG1fY2hlY2tTeW5jPTEiLCJwb3B1cF90YXJnZXQiOiJoaWJ1LXZpZGVvLXNwbGFzaCIsImxpbmtfdHlwZSI6InBvcHVwIn0sInRyYW5zY3JpcHRpb24iOiJTZXJ2aW5nIHRoZSBXaW5kc29yIExvY2tzLCBDVCBhcmVhLCBKVE4gRWxlY3RyaWNhbCBzcGVjaWFsaXplcyBpbiByZXNpZGVudGlhbCBhbmQgY29tbWVyY2lhbCBlbGVjdHJpY2FsIHNlcnZpY2VzLiBFbWVyZ2VuY3kgZWxlY3RyaWNhbCBzZXJ2aWNlcy4gUXVpY2sgcmVzcG9uc2UgdGltZXMuIEZyZWUsIGluLXBlcnNvbiBlc3RpbWF0ZXMgYXZhaWxhYmxlLiBDYWxsIHRvZGF5LiIsIm5hbWUiOiJKVE4gRWxlY3RyaWNhbCIsImRlc2NyaXB0aW9uIjoiV2Ugb2ZmZXIgZWxlY3RyaWNhbCBpbnN0YWxsYXRpb25zLCBlbGVjdHJpY2FsIGV2YWx1YXRpb25zLCAmIGVsZWN0cmljYWwgcmVwYWlycy4gRW1lcmdlbmN5IGVsZWN0cmljYWwgc2VydmljZXMuIFF1aWNrIHJlc3BvbnNlIHRpbWVzLiBGcmVlIGVzdGltYXRlcyBhdmFpbGFibGUuIiwidGh1bWJuYWlsVXJsIjoiaHR0cHM6Ly9jZG4uaGlidXdlYnNpdGVzLmNvbS9mNmNkZmFiYmExYzc0OWExYTNkM2I3YTlmNGMzMGM3Ni9kbXMzcmVwL211bHRpL3ZpZGVvLWp0bi1lbGVjdHJpY2FsLmpwZyIsImNvbn
3257RlbnRVcmwiOiJ2aWRlb3MuaGlidXN0dWRpby5jb20vMjAyNi9qdG4tZWxlY3RyaWNhbC03MDAzNzkyNTEwLm1wNCIsInVwbG9hZERhdGUiOiIyMDI2LTA5LTI0In0="> <div class="vidWrapper" itemprop="video" itemscope="" itemtype="http://schema.org/VideoObject"> <meta itemprop="name" content="JTN Electrical"></meta> 
3258 <meta itemprop="duration" content="T1M36S"></meta> 
3259 <meta itemprop="uploadDate" content="2026-09-24"></meta> 
3260 <meta itemprop="thumbnailUrl" content="https://cdn.hibuwebsites.com/f6cdfabba1c749a1a3d3b7a9f4c30c76/dms3rep/multi/video-jtn-electrical.jpg"></meta> 
3261 <meta itemprop="contentURL" content="videos.hibustudio.com/2026/jtn-electrical-7003792510.mp4"></meta> 
3262 <meta itemprop="description" content="We offer electrical installations, electrical evaluations, & electrical repairs. Emergency electrical services. Quick response times. Free estimates available."></meta> 
3263 <meta itemprop="transcript" content=""></meta> 
3264 <div class="vidInner"> <center> <p class="vidTitle1"></p> 
3265 <p class="vidTitle2"></p> 
3266 <div class="playIcon"> <a value="hibu-video-splash" type="popup" href="/hibu-video-splash" raw_url="/site/f6cdfabba1c749a1a3d3b7a9f4c30c76/hibu-video-splash?nee=true&ed=true&showOriginal=true&preview=true&dm_try_mode=true&dm_checkSync=1" popup_target="hibu-video-splash" link_type="popup"> <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewbox="0 0 100 100"> <path id="Icon_fa-regular-circle-play" data-name="Icon fa-regular-circle-play" d="M90.625,50A40.625,40.625,0,1,0,50,90.625,40.625,40.625,0,0,0,90.625,50ZM0,50a50,50,0,1,1,50,50A50,50,0,0,1,0,50ZM36.777,28.73a4.693,4.693,0,0,1,4.746.1L69.648,46.016a4.692,4.692,0,0,1,0,8.008L41.523,71.211a4.738,4.738,0,0,1-4.746.1,4.658,4.658,0,0,1-2.4-4.082V32.813A4.658,4.658,0,0,1,36.777,28.73Z"></path> 
3267</svg> 
3268</a> 
3269</div> 
3270</center> 
3271</div> 
3272 <div class="vidOverlay"></div> 
3273</div> 
3274</div> 
3275</div> 
3276</div> 
3277</div> 
3278</div> 
3279</section> 
3280</div> 
3281 <div data-layout-section="" data-auto="flex-section" id="1248306277" data-flex-id="b6b5923e"> <div class="flex-widgets-container" id="1193597235"></div> 
3282 <section id="e4ad708c" class="flex-element section" data-auto="flex-element-section"> <div id="2bcd41ae" class="flex-element grid" data-auto="flex-element-grid" data-layout-grid=""> <div id="4efeffee" class="flex-element group" data-auto="flex-element-group"> <div id="group_b0q" class="flex-element group" data-auto="flex-element-group"> <div id="widget_1j0" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="paragraph" data-external-id="1411819092"> <div class="dmNewParagraph" data-element-type="paragraph" data-version="5" id="1411819092" style=""> <h6><span style="color: var(--color_9); display: initial;">VISIT US</span></h6> 
3283</div> 
3284</div> 
3285 <div id="widget_mse" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="divider" data-external-id="1644223884"> <div class="dmDividerWrapper clearfix u_1644223884" data-element-type="dDividerId" data-layout="divider-style-1" data-widget-version="2" id="1644223884" layout="divider-gradient-line"><hr class="dmDivider" style="border-width:2px; border-top-style:solid; color:grey;" id="1089896382"/></div> 
3286</div> 
3287 <div id="widget_umk" class="flex-element widget-wrapper YXTLOCATION" data-auto="flex-element-widget-wrapper" data-widget-type="paragraph" data-external-id="3884292117"> <div class="dmNewParagraph u_3884292117" data-element-type="paragraph" data-version="5" id="3884292117" data-binding="W3siYmluZGluZ05hbWUiOiJ0ZXh0IiwidmFsdWUiOiJTVEFUSUNfUEFHRV9DT0xMRUNUSU9OLntcImNuXCI6XCJMb2MtMFwiLFwiZm5cIjpcIkFkZHJlc3Nlc1wifSJ9XQ==" data-diy-text=""> <div class="rteBlock">50C King Spring Rd<br/>Windsor Locks,&nbsp;CT&nbsp;06096</div> 
3288</div> 
3289</div> 
3290 <div id="87a53ad2" class="flex-element widget-wrapper YXTSERVICEAREA" data-auto="flex-element-widget-wrapper" data-widget-type="paragraph" data-hidden-on-desktop="" data-hidden-on-tablet="" data-hidden-on-mobile="" data-external-id="1928174169"> <div class="dmNewParagraph u_1928174169" data-element-type="paragraph" data-version="5" id="1928
3290174169" data-binding="W3siYmluZGluZ05hbWUiOiJ0ZXh0IiwidmFsdWUiOiJTVEFUSUNfUEFHRV9DT0xMRUNUSU9OLntcImNuXCI6XCJMb2MtMFwiLFwiZm5cIjpcIkZvb3Rlcl9TZXJ2aWNlX0FyZWFzXCJ9In1d" data-diy-text=""><p class="rteBlock">Agawam, MA</p><p class="rteBlock">East Longmeadow, MA</p><p class="rteBlock">Longmeadow, MA</p></div> 
3291</div> 
3292 <div id="6efb2802" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="paragraph" data-hidden-on-desktop="" data-hidden-on-tablet="" data-hidden-on-mobile="" data-external-id="1673689127"> <div class="dmNewParagraph" data-element-type="paragraph" data-version="5" id="1673689127" style=""><p class="text-align-left m-text-align-left t-size-15 m-size-15 size-15"><span class="font-size-15 t-font-size-15 m-font-size-15" style="color: var(--color_9); display: unset;">and surrounding areas</span></p></div> 
3293</div> 
3294 <div id="widget_p1h" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="spacer" data-external-id="1070152015"> <div data-element-type="spacer" class="dmSpacer" id="1070152015"></div> 
3295</div> 
3296 <div id="widget_ipg" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="clicktomap" data-external-id="1178389292"> <a class="default align-center flexButton u_1178389292 dmMap dmWidget dmNoMark dmWwr" href="https://maps.google.com/maps?q=50C+King+Spring+Rd%2C+Windsor+Locks%2C+CT+06096%2C+US&sll=41.9448313,-72.66665" contenteditable="false" onclick=";return dm_gaq_push_event('ClickToMap', 'Map',null,'f6cdfabba1c749a1a3d3b7a9f4c30c76', this,{hitCallBack:true});" id="1178389292" dmle_extension="mapextension" data-element-type="mapextension" modedesktop="" data-popup-title="" data-binding="W3siYmluZGluZ05hbWUiOiJhZGRyZXNzIiwidmFsdWUiOiJjb250ZW50X2xpYnJhcnkuZ2xvYmFsIn1d" data-popup-description-visible="false" modemobile="" addresstodisplay="50C King Spring Rd, Windsor Locks, CT 06096, US" geocompleteaddress="50C King Spring Rd, Windsor Locks, CT 06096, US" data-popup-display-desktop="" data-popup-display-mobile="" data-display-type="block" data-popup-title-visible="false" data-popup-description="" dmmapsource="dm" modetablet="" wr="true" data-popup-show="false" icon="true" surround="true" description="Get Directions" adwords="" icon-name="icon-map-marker" street="" city="" country="US" country_full="United States" state="" zip="" business="" provider="mapbox" lon="-72.66665" lat="41.9448313" faddr="50C King Spring Rd, Windsor Locks, CT 06096, US"> <span class="iconBg" data-auto="button-icon-bg" aria-hidden="true"> <span class="icon hasFontIcon icon-map-marker" data-auto="button-icon"></span> 
3297</span> 
3298 <span class="text" data-auto="button-text">Get Directions</span> 
3299</a> 
3300</div> 
3301</div> 
3302 <div id="d0234170" class="flex-element group" data-auto="flex-element-group"> <div id="cbc8b291" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="paragraph" data-external-id="1563947725"> <div class="dmNewParagraph" data-element-type="paragraph" data-version="5" id="1563947725" style=""> <h6><span style="display: unset; color: var(--color_9);">CONTACT US</span></h6> 
3303</div> 
3304</div> 
3305 <div id="0b55734b" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="divider" data-external-id="1078075131"> <div class="dmDividerWrapper clearfix u_1078075131" data-element-type="dDividerId" data-layout="divider-style-1" data-widget-version="2" id="1078075131" layout="divider-gradient-line"><hr class="dmDivider" style="border-width:2px; border-top-style:solid; color:grey;" id="1927534912"/></div> 
3306</div> 
3307 <div id="widget_k5l" class="flex-element widget-wrapper YXTPH" data-auto="flex-element-widget-wrapper" data-widget-type="paragraph" data-external-id="1675397205"> <div class="dmNewParagraph u_1675397205" data-element-type="paragraph" data-version="5" id="1675397205" data-diy-text="" style="" data-binding="W3siYmluZGluZ05hbWUiOiJ0ZXh0IiwidmFsdWUiOiJjb250ZW50X2xpYnJhcnkuZ2xvYmFsLnBob25lLmxvYy0wIC0gbWFpbiJ9XQ==">(860) 215-2254</div> 
3308</div> 
3309 <div id="502c2597" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="spacer" data-external-id="1918773725"> <div data-element-type="spacer" class="dmSpacer" id="1918773725"></div> 
3310</div> 
3311 <div id="1db7e0c7" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="link" data-external-id="1199369758"> <a data-display-type="block" class="align-center dmButtonLink dmWidget dmWwr default dmOnlyButton dmDefaultGradient flexButton u_1199369758" file="false" href="/contact#ICcontactform" data-element-type="dButtonLinkId" id="1199369758"> <span class="iconBg" aria-hidden="true" id="1508234354"> <span class="icon hasFontIcon icon-star" id="1201354356"></span> 
3312</span> 
3313 <span class="text" id="1706213487">Send a Message</span> 
3314</a> 
3315</div> 
3316 <div id="widget_391" class="flex-element widget-wrapper YXTSOCIAL" data-auto="flex-element-widget-wrapper" data-widget-type="socialhub" data-external-id="1064639846"> <div class="align-center text-align-center u_1064639846 dmSocialHub gapSpacing" id="1064639846" dmle_extension="social_hub" data-element-type="social_hub" data-binding="W3siYmluZGluZ05hbWUiOiJzb2NpYWwiLCJ2YWx1ZSI6ImNvbnRlbn
3316RfbGlicmFyeS5nbG9iYWwuc29jaWFsIn1d" wr="true" networks="" icon="true" surround="true" adwords=""> <div class="socialHubWrapper"> <div class="socialHubInnerDiv " data-auto="social-hub-inner"> <a href="https://facebook.com/JTNelectrical" target="_blank" dm_dont_rewrite_url="true" aria-label="facebook" onclick="dm_gaq_push_event &amp;&amp; dm_gaq_push_event(&apos;socialLink&apos;, &apos;click&apos;, &apos;Facebook&apos;)"> <span class="dmSocialFacebook dm-social-icons-facebook oneIcon socialHubIcon style5" aria-hidden="true" data-hover-effect="zoomout"></span> 
3317</a> 
3318 <a href="https://instagram.com/jtnelectric" target="_blank" dm_dont_rewrite_url="true" aria-label="instagram" onclick="dm_gaq_push_event &amp;&amp; dm_gaq_push_event(&apos;socialLink&apos;, &apos;click&apos;, &apos;Instagram&apos;)"> <span class="dmSocialInstagram dm-social-icons-instagram oneIcon socialHubIcon style5" aria-hidden="true" data-hover-effect="zoomout"></span> 
3319</a> 
3320</div> 
3321</div> 
3322</div> 
3323</div> 
3324</div> 
3325 <div id="f2debc4b" class="flex-element group" data-auto="flex-element-group"> <div id="65e14480" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="paragraph" data-external-id="1332286019"> <div class="dmNewParagraph" data-element-type="paragraph" data-version="5" id="1332286019" style=""> <h6><span style="display: unset; color: var(--color_9);">BUSINESS HOURS</span></h6> 
3326</div> 
3327</div> 
3328 <div id="c7f3ddf3" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="divider" data-external-id="1463975724"> <div class="dmDividerWrapper clearfix u_1463975724" data-element-type="dDividerId" data-layout="divider-style-1" data-widget-version="2" id="1463975724" layout="divider-gradient-line"><hr class="dmDivider" style="border-width:2px; border-top-style:solid; color:grey;" id="1907202694"/></div> 
3329</div> 
3330 <div id="618f39ae" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="spacer" data-external-id="1700615418"> <div data-element-type="spacer" class="dmSpacer" id="1700615418"></div> 
3331</div> 
3332 <div id="widget_6mu" class="flex-element widget-wrapper YXTBUSHOURS" data-auto="flex-element-widget-wrapper" data-widget-type="hoursOfOperation" data-external-id="1185800986"> <div class="default u_1185800986 dmHoursOfOperation" id="1185800986" dmle_extension="open_hours" data-element-type="open_hours" data-binding="W3siYmluZGluZ05hbWUiOiJidXNpbmVzc19ob3VycyIsInZhbHVlIjoiY29udGVudF9saWJyYXJ5Lmdsb2JhbC5idXNpbmVzc19ob3VycyJ9XQ==" time_format="12" wr="true" displaytitle="false" lang="" forcedisplay="false" icon="true" surround="true" adwords=""> <dl class="open-hours-data"> <div class="open-hours-item"> <dt day="0">Mon - Fri</dt> 
3333 <dd> <time>7:30 am</time> 
3334- <time>4:00 pm</time> 
3335</dd> 
3336</div> 
3337 <div class="open-hours-item"> <dt day="5">Sat - Sun</dt> 
3338 <dd>Closed</dd> 
3339</div> 
3340</dl> 
3341</div> 
3342</div> 
3343</div> 
3344</div> 
3345</div> 
3346</section> 
3347</div> 
3348 <div data-layout-section="" data-auto="flex-section" id="1344609592" data-flex-id="2f831c0f"> <div class="flex-widgets-container" id="1374258866"></div> 
3349 <section id="71171ca4" class="flex-element section" data-auto="flex-element-section"> <div id="0253b4c6" class="flex-element grid" data-auto="flex-element-grid" data-layout-grid=""> <div id="d027d36f" class="flex-element group" data-auto="flex-element-group" data-hidden-on-tablet="" data-hidden-on-mobile=""> <div id="00841c9d" class="flex-element group" data-auto="flex-element-group"> <div id="7e326d80" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="ONELinksMenu" data-hidden-on-desktop="" data-external-id="1869341664"> <nav class="effect-bottom2 u_1869341664 main-navigation unifiednav dmLinksMenu" data-auto="links-menu" role="navigation" layout-main="horizontal_nav_layout_1" layout-sub="submenu_horizontal_1" data-show-vertical-sub-items="HOVER" id="1869341664" dmle_extension="onelinksmenu" data-element-type="onelinksmenu" data-logo-src="" alt="" data-nav-structure="HORIZONTAL" wr="true" icon="true" surround="true" adwords="" navigation-id="unifiedNav"> <ul role="menubar" class="unifiednav__container unav-top  " data-auto="navigation-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/" class="unifiednav__item unav-item unav-top-item  dmUDNavigationItem_00  " target="" data-target-page-alias=""> <span class="nav-item-text " data-link-text="
3350         Home
3351        " data-auto="page-text-style">Home<span class="icon icon-angle-down"></span> 
3352</span> 
3353</a> 
3354</li> 
3355 <li role="menuitem" aria-haspopup="true" aria-expanded="false" data-sub-nav-menu="true" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/about" class="unifiednav__item unav-item unav-top-item  dmUDNavigationItem_010101599305  unifiednav__item_has-sub-nav" target="" data-target-page-alias=""> <span class="nav-item-text " data-link-text="About" data-auto="page-text-style">About<span class="icon icon-angle-down"></span> 
3356</span> 
3357</a> 
3358 <ul role="menu" class="unifiednav__container unifiednav__container_sub-nav unav-sub" data-depth="0" data-auto="sub-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/faqs" class="unifiednav__item unav-item   dmUDNavigationItem_010101347615  " target="" data-target-page-alias=""> <span class="nav-item-text " data-link-text="FAQs" data-auto="page-text-style">FAQs<span class="icon icon-angle-right"></span> 
3359</span> 
3360</a> 
3361</li> 
3362 <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/reviews" class="unifiednav__item unav-item   dmUDNavigationItem_010101840192  " target="" data-target-page-alias=""> <span class="nav-item-text " data-link-text="Reviews" data-auto="page-text-style">Reviews<span class="icon icon-angle-right"></span> 
3363</span> 
3364</a> 
3365</li> 
3366</ul> 
3367</li> 
3368 <li role="menuitem" aria-haspopup="true" aria-expanded="false" data-sub-nav-menu="true" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/contact" class="unifiednav__item unav-item unav-top-item  dmUDNavigationItem_010101152840  unifiednav__item_has-sub-nav" target="" data-target-page-alias=""> <span class="nav-item-text " data-link-text="Contact" data-auto="page-text-style">Contact<span class="icon icon-angle-down"></span> 
3369</span> 
3370</a> 
3371 <ul role="menu" class="unifiednav__container unifiednav__container_sub-nav unav-sub" data-depth="0" data-auto="sub-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/request-service-form" class="unifiednav__item unav-item   dmNavItemSelected  dmUDNavigationItem_010101150108  " target="" data-target-page-alias="" aria-current="page" data-auto="selected-page"> <span class="nav-item-text " data-link-text="Request Service" data-auto="page-text-style">Request Service<span class="icon icon-angle-right"></span> 
3372</span> 
3373</a> 
3374</li> 
3375 <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/contact#service-areas" class="unifiednav__item unav-item   dmUDNavigationItem_010101813309  " target="" data-target-page-alias=""> <span class="nav-item-text " data-link-text="Areas Served" data-auto="page-text-style">Areas Served<span class="icon icon-angle-right"></span> 
3376</span> 
3377</a> 
3378</li> 
3379</ul> 
3380</li> 
3381</ul> 
3382</nav> 
3383</div> 
3384</div> 
3385</div> 
3386</div> 
3387</section> 
3388</div> 
3389 <div data-layout-section="" data-auto="flex-section" id="1741943752" data-flex-id="54290af5" class="" data-hidden-on-desktop="true"> <div class="flex-widgets-container" id="1020340060"></div> 
3390 <section id="0c6cf26f" class="flex-element section" data-auto="flex-element-section"> <div id="3989af5b" class="flex-element grid" data-auto="flex-element-grid" data-layout-grid=""> <div id="24eff763" class="flex-element group" data-auto="flex-element-group" data-hidden-on-tablet="" data-hidden-on-desktop="">
3390 <div id="7ca01c83" class="flex-element group" data-auto="flex-element-group"> <div id="34b9f983" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="ssrbreadcrumbs" data-external-id="1424178368"> <div id="1424178368" dmle_extension="ssrbreadcrumbs" data-element-type="ssrbreadcrumbs" class=""><span id="ssrWrap-1424178368" ><style data-styled="true" data-styled-version="5.3.11">#dm .hwJXuA.hwJXuA{color:rgba(102,102,102,1);}/*!sc*/
3391data-styled.g17[id="sc-cPiKLX"]{content:"hwJXuA,"}/*!sc*/
3392.gCAEgf{color:rgba(102,102,102,1);}/*!sc*/
3393data-styled.g18[id="sc-dLMFU"]{content:"gCAEgf,"}/*!sc*/
3394#dm .dewijn.dewijn{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:10px;}/*!sc*/
3395@media (min-width:768px){#dm .dewijn.dewijn{font-size:11px;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;}}/*!sc*/
3396@media (max-width:767px){#dm .dewijn.dewijn{font-size:11px;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;}}/*!sc*/
3397data-styled.g19[id="sc-eDPEul"]{content:"dewijn,"}/*!sc*/
3398#dm .cZcQVQ.cZcQVQ{color:rgba(102,102,102,1);-webkit-transform:rotate(0);-ms-transform:rotate(0);transform:rotate(0);display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:16px;}/*!sc*/
3399#dm .cZcQVQ.cZcQVQ svg{width:100%;height:100%;fill:currentColor;}/*!sc*/
3400data-styled.g20[id="sc-eldPxv"]{content:"cZcQVQ,"}/*!sc*/
3401</style><div data-auto="runtime-breadcrumbs-widget">
3401<script data-auto="schema" type="application/ld+json">{"@context":"http://schema.org/","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"name":"Home","id":"/"}},{"@type":"ListItem","position":2,"item":{"name":"Contact","id":"/contact"}},{"@type":"ListItem","position":3,"item":{"name":"Request Service"}}]}</script>
3401<nav class="sc-eDPEul dewijn bc-root dmBlockElement" aria-label="Breadcrumbs"><a href="/" class="sc-cPiKLX hwJXuA bc-item"><div class="sc-dLMFU">Home</div></a><span direction="ltr" class="sc-eldPxv cZcQVQ bc-separator" aria-hidden="true"><svg data-auto="arrow" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M5.46967 3.46967C5.76256 3.17678 6.23744 3.17678 6.53033 3.46967L10.5303 7.46967C10.8232 7.76256 10.8232 8.23744 10.5303 8.53033L6.53033 12.5303C6.23744 12.8232 5.76256 12.8232 5.46967 12.5303C5.17678 12.2374 5.17678 11.7626 5.46967 11.4697L8.93934 8L5.46967 4.53033C5.17678 4.23744 5.17678 3.76256 5.46967 3.46967Z" fill="currentColor"></path></svg></span><a href="/contact" class="sc-cPiKLX hwJXuA bc-item"><div class="sc-dLMFU">Contact</div></a><span direction="ltr" class="sc-eldPxv cZcQVQ bc-separator" aria-hidden="true"><svg data-auto="arrow" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M5.46967 3.46967C5.76256 3.17678 6.23744 3.17678 6.53033 3.46967L10.5303 7.46967C10.8232 7.76256 10.8232 8.23744 10.5303 8.53033L6.53033 12.5303C6.23744 12.8232 5.76256 12.8232 5.46967 12.5303C5.17678 12.2374 5.17678 11.7626 5.46967 11.4697L8.93934 8L5.46967 4.53033C5.17678 4.23744 5.17678 3.76256 5.46967 3.46967Z" fill="currentColor"></path></svg></span><div class="sc-dLMFU gCAEgf bc-current-item" data-auto="bc-current-item">Request Service</div></nav></div></span></div> 
3402</div> 
3403</div> 
3404</div> 
3405</div> 
3406</section> 
3407</div> 
3408</div> 
3409 <div id="poweredBy" class="poweredBy poweredByHibu"> <div class="poweredLine1"> <a href="http://budurl.com/hibuprivacy" target="_blank">Privacy Policy</a>| <a href="http://budurl.com/hibucookie" target="_blank">Do Not Share My Information</a>| <a href="http://budurl.com/hibuconditionsofuse" target="_blank">Conditions of Use</a>| <a href="http://budurl.com/hibunotice" target="_blank">Notice and Take Down Policy</a>| <a href="http://b.link/accessibility" target="_blank">Website Accessibility Policy</a> 
3410</div> 
3411 <div class="poweredLine2">&copy; <span id="hibuYear">2026</span> 
3412The content on this website is owned by us and our licensors. Do not copy any content (including images) without our consent.</div> 
3413</div> 
3414</div> 
3415 <div id="dmFlexStickyContainer" class="flex_hfcontainer"> <div id="flex-sticky"> <div data-layout-section="" data-auto="flex-section" id="1801865596" data-flex-id="8a9cfcfc"> <div class="flex-widgets-container" id="1548435334"></div> 
3416 <section id="4a1fde56" class="flex-element section" data-auto="flex-element-section"> <div id="381d99f1" class="flex-element grid" data-auto="flex-element-grid" data-layout-grid=""> <div id="75ba1c41" class="flex-element group" data-auto="flex-element-group"> <div id="4f4d3298" class="flex-element group" data-auto="flex-element-group"> <div id="72168835" class="flex-element inner-grid" data-auto="flex-element-inner-grid"> <div id="widget_21v" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="graphic" data-keep-proportion="true" data-hidden-on-tablet="" data-hidden-on-desktop="" data-external-id="1389791254"> <div class="graphicWidget graphicWidgetV2 graphicWidgetV3 u_1389791254" data-element-type="graphic" data-widget-type="graphic" id="1389791254" data-layout="graphic-style-5"> <a href="/hibu-eng-menu" id="1685777839" link_type="popup" file="false" popup_target="hibu-eng-menu" aria-label="Contact Us"> <svg width="100%" height="100%" version="1.1" viewbox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" id="1047488693" class="svg u_1047488693" data-icon-custom="true" aria-labelledby="1665903033"> <title id="1665903033">Contact Us</title> 
3417 <path d="m90.793 43.289h-34.086v-34.078c0-3.707-3.0039-6.707-6.707-6.707s-6.707 3-6.707 6.707v34.078h-34.086c-3.7031 0-6.707 3.0039-6.707 6.707s3.0039 6.707 6.707 6.707h34.086v34.086c0 3.707 3.0039 6.707 6.707 6.707s6.707-3 6.707-6.707v-34.086h34.086c3.7031 0 6.707-3 6.707-6.707 0-3.707-3.0039-6.707-6.707-6.707z"></path> 
3418</svg> 
3419</a> 
3420</div> 
3421</div> 
3422 <div id="widget_fpt" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="custom" data-external-id="3053959938"> <div class="widget-0c06e7 dmCustomWidget" data-lazy-load="true" data-title="" id="3053959938" dmle_extension="custom_extension" data-element-type="custom_extension" icon="false" surround="false" data-widget-id="0c06e70da5474879a21daa0d3b1192c0" data-widget-version="3" data-widget-config="e30="> <div class="cb1-theme-injector"></div> 
3423</div> 
3424</div> 
3425 <div id="widget_mnf" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="custom" data-external-id="3931136021"> <div class="widget-8eedf9 dmCustomWidget" data-lazy-load="true" data-title="" id="3931136021" dmle_extension="custom_extension" data-element-type="custom_extension" icon="false" surround="false" data-widget-id="8eedf98f346a42e0a55f9d6152653187" data-widget-version="2" data-widget-config="e30="> <div class="cb0-theme-injector"></div> 
3426</div> 
3427</div> 
3428 <div id="widget_c5v" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="custom" data-external-id="3659398620"> <div class="widget-1c20f5 dmCustomWidget" data-lazy-load="true" data-title="" id="3659398620" dmle_extension="custom_extension" data-element-type="custom_extension" icon="false" surround="false" data-widget-id="1c20f508b5474ea2b929959f7d5eff3d" data-widget-version="3" data-widget-config="e30="> <div class="dcbh3-theme-injector"></div> 
3429</div> 
3430</div> 
3431 <div id="widget_doa" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="custom" data-external-id="1889303332"> <div class="widget-403a95 dmCustomWidget" data-lazy-load="true" data-title="" id="1889303332" dmle_extension="custom_extension" data-element-type="custom_extension" icon="false" surround="false" data-widget-id="403a957a34f84bc8a342137c59c93b31" data-widget-version="3" data-widget-config="e30="> <div class="cb2-theme-injector"></div> 
3432</div> 
3433</div> 
3434 <div id="widget_301" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="custom" data-external-id="986065271"> <div class="widget-e0b1e5 dmCustomWidget" data-lazy-load="true" data-title="" id="986065271" dmle_extension="custom_extension" data-element-type="custom_extension" icon="false" surround="false" data-widget-id="e0b1e5b2d4394e69a98bd276fd6afe8b" data-widget-version="8" data-widget-config="e30="> <div class="cb1-theme-injector"></div> 
3435</div> 
3436</div> 
3437 <div id="widget_61p" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="custom" data-external-id="3078440210"> <div class="widget-2133bf dmCustomWidget" data-lazy-load="true" data-title="" id="3078440210" dmle_extension="custom_extension" data-element-type="custom_extension" icon="false" surround="false" data-widget-id="2133bfee2cb24e0c9db9468154881b94" data-widget-version="3" data-widget-config="e30="> <div class="cb1-theme-injector"></div> 
3438</div> 
3439</div> 
3440</div> 
3441</div> 
3442</div> 
3443</div> 
3444</section> 
3445</div> 
3446</div> 
3447</div> 
3448 <div id="dmFlexMegaMenuContainer" class="flex_hfcontainer"> <div id="multi-mega-menu"> <div id="mega-menu-4dc8a903" name="Services"> <div data-flex-id="4dc8a903" data-auto="flex-section" id="1276875426" data-layout-section="" data-page-element-type="section" data-page-element-id="1232093299" class="" data-hidden-on-desktop="true"> <div class="flex-widgets-container" id="1497300766"></div> 
3449 <section id="6c5257e3" class="flex-element section" data-auto="flex-element-section"> <div id="2cb3e9f9" class="flex-element grid" data-auto="flex-element-grid" data-layout-grid=""> <div id="27581a62" class="flex-element group" data-auto="flex-element-group" data-hidden-on-desktop=""> <div id="d200b2f7" class="flex-element group" data-auto="flex-element-group"> <div id="widget_cq9" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="ONELinksMenu" data-external-id="1459911792"> <nav class="u_1459911792 unifiednav_vertical effect-bottom2 main-navigation unifiednav dmLinksMenu" data-auto="links-menu" role="navigation" layout-main="vertical_nav_layout_2" layout-sub="" data-show-vertical-sub-items="SHOW" id="1459911792" dmle_extension="onelinksmenu" data-element-type="onelinksmenu" data-logo-src="" alt="" data-nav-structure="VERTICAL" wr="true" icon="true" surround="true" adwords="" navigation-id="unifiedNav"> <ul role="menubar" class="unifiednav__container unav-top  " data-auto="navigation-pages"> <li role="menuitem" aria-haspopup="true" aria-expanded="false" data-sub-nav-menu="true" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/what-we-do/~page-item~" class="unifiednav__item unav-item unav-top-item    unifiednav__item_has-sub-nav" target="" data-target-page-alias="cat-landing"> <span class="nav-item-text " data-link-text="Cat-Landing" data-auto="page-text-style">Cat-Landing<span class="icon icon-angle-down"></span> 
3450</span> 
3451</a> 
3452 <ul role="menu" class="unifiednav__container unifiednav__container_sub-nav unav-sub" data-depth="0" data-auto="sub-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/services/~page-item~" class="unifiednav__item unav-item     " target="" data-target-page-alias="core-page"> <span class="nav-item-text " data-link-text="Core Page" data-auto="page-text-style">
3452Core Page<span class="icon icon-angle-right" data-hidden-on-mobile="" data-hidden-on-desktop="" data-hidden-on-tablet=""></span> 
3453</span> 
3454</a> 
3455</li> 
3456</ul> 
3457</li> 
3458</ul> 
3459</nav> 
3460</div> 
3461 <div id="b51d88ee" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="ONELinksMenu" data-external-id="1723750523"> <nav class="u_1723750523 unifiednav_vertical effect-bottom2 main-navigation unifiednav dmLinksMenu" data-auto="links-menu" role="navigation" layout-main="vertical_nav_layout_2" layout-sub="" data-show-vertical-sub-items="SHOW" id="1723750523" dmle_extension="onelinksmenu" data-element-type="onelinksmenu" data-logo-src="" alt="" data-nav-structure="VERTICAL" wr="true" icon="true" surround="true" adwords="" navigation-id="unifiedNav"> <ul role="menubar" class="unifiednav__container unav-top  " data-auto="navigation-pages"> <li role="menuitem" aria-haspopup="true" aria-expanded="false" data-sub-nav-menu="true" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/what-we-do/~page-item~" class="unifiednav__item unav-item unav-top-item    unifiednav__item_has-sub-nav" target="" data-target-page-alias="cat-landing"> <span class="nav-item-text " data-link-text="Cat-Landing" data-auto="page-text-style">Cat-Landing<span class="icon icon-angle-down"></span> 
3462</span> 
3463</a> 
3464 <ul role="menu" class="unifiednav__container unifiednav__container_sub-nav unav-sub" data-depth="0" data-auto="sub-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/services/~page-item~" class="unifiednav__item unav-item     " target="" data-target-page-alias="core-page"> <span class="nav-item-text " data-link-text="Core Page" data-auto="page-text-style">Core Page<span class="icon icon-angle-right" data-hidden-on-mobile="" data-hidden-on-desktop="" data-hidden-on-tablet=""></span> 
3465</span> 
3466</a> 
3467</li> 
3468</ul> 
3469</li> 
3470</ul> 
3471</nav> 
3472</div> 
3473 <div id="5aa033c8" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="ONELinksMenu" data-external-id="1610592242"> <nav class="u_1610592242 unifiednav_vertical effect-bottom2 main-navigation unifiednav dmLinksMenu" data-auto="links-menu" role="navigation" layout-main="vertical_nav_layout_2" layout-sub="" data-show-vertical-sub-items="SHOW" id="1610592242" dmle_extension="onelinksmenu" data-element-type="onelinksmenu" data-logo-src="" alt="" data-nav-structure="VERTICAL" wr="true" icon="true" surround="true" adwords="" navigation-id="unifiedNav"> <ul role="menubar" class="unifiednav__container unav-top  " data-auto="navigation-pages"> <li role="menuitem" aria-haspopup="true" aria-expanded="false" data-sub-nav-menu="true" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/what-we-do/~page-item~" class="unifiednav__item unav-item unav-top-item    unifiednav__item_has-sub-nav" target="" data-target-page-alias="cat-landing"> <span class="nav-item-text " data-link-text="Cat-Landing" data-auto="page-text-style">Cat-Landing<span class="icon icon-angle-down"></span> 
3474</span> 
3475</a> 
3476 <ul role="menu" class="unifiednav__container unifiednav__container_sub-nav unav-sub" data-depth="0" data-auto="sub-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/services/~page-item~" class="unifiednav__item unav-item     " target="" data-target-page-alias="core-page"> <span class="nav-item-text " data-link-text="Core Page" data-auto="page-text-style">Core Page<span class="icon icon-angle-right" data-hidden-on-mobile="" data-hidden-on-desktop="" data-hidden-on-tablet=""></span> 
3477</span> 
3478</a> 
3479</li> 
3480</ul> 
3481</li> 
3482</ul> 
3483</nav> 
3484</div> 
3485</div> 
3486 <div id="ad9cb284" class="flex-element group" data-auto="flex-element-group"> <div id="04df1da2" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="ONELinksMenu" data-external-id="1136789265"> <nav class="u_1136789265 unifiednav_vertical effect-bottom2 main-navigation unifiednav dmLinksMenu" data-auto="links-menu" role="navigation" layout-main="vertical_nav_layout_2" layout-sub="" data-show-vertical-sub-items="SHOW" id="1136789265" dmle_extension="onelinksmenu" data-element-type="onelinksmenu" data-logo-src="" alt="" data-nav-structure="VERTICAL" wr="true" icon="true" surround="true" adwords="" navigation-id="unifiedNav"> <ul role="menubar" class="unifiednav__container unav-top  " data-auto="navigation-pages"> <li role="menuitem" aria-haspopup="true" aria-expanded="false" data-sub-nav-menu="true" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/what-we-do/~page-item~" class="unifiednav__item unav-item unav-top-item    unifiednav__item_has-sub-nav" target="" data-target-page-alias="cat-landing"> <span class="nav-item-text " data-link-text="Cat-Landing" data-auto="page-text-style">Cat-Landing<span class="icon icon-angle-down"></span> 
3487</span> 
3488</a> 
3489 <ul role="menu" class="unifiednav__container unifiednav__container_sub-nav unav-sub" data-depth="0" data-auto="sub-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/services/~page-item~" class="unifiednav__item unav-item     " target="" data-target-page-alias="core-page"> <span class="nav-item-text " data-link-text="Core Page" data-auto="page-text-style">
3489Core Page<span class="icon icon-angle-right" data-hidden-on-mobile="" data-hidden-on-desktop="" data-hidden-on-tablet=""></span> 
3490</span> 
3491</a> 
3492</li> 
3493</ul> 
3494</li> 
3495</ul> 
3496</nav> 
3497</div> 
3498 <div id="f09d8243" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="ONELinksMenu" data-external-id="1968129260"> <nav class="u_1968129260 unifiednav_vertical effect-bottom2 main-navigation unifiednav dmLinksMenu" data-auto="links-menu" role="navigation" layout-main="vertical_nav_layout_2" layout-sub="" data-show-vertical-sub-items="SHOW" id="1968129260" dmle_extension="onelinksmenu" data-element-type="onelinksmenu" data-logo-src="" alt="" data-nav-structure="VERTICAL" wr="true" icon="true" surround="true" adwords="" navigation-id="unifiedNav"> <ul role="menubar" class="unifiednav__container unav-top  " data-auto="navigation-pages"> <li role="menuitem" aria-haspopup="true" aria-expanded="false" data-sub-nav-menu="true" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/what-we-do/~page-item~" class="unifiednav__item unav-item unav-top-item    unifiednav__item_has-sub-nav" target="" data-target-page-alias="cat-landing"> <span class="nav-item-text " data-link-text="Cat-Landing" data-auto="page-text-style">Cat-Landing<span class="icon icon-angle-down"></span> 
3499</span> 
3500</a> 
3501 <ul role="menu" class="unifiednav__container unifiednav__container_sub-nav unav-sub" data-depth="0" data-auto="sub-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/services/~page-item~" class="unifiednav__item unav-item     " target="" data-target-page-alias="core-page"> <span class="nav-item-text " data-link-text="Core Page" data-auto="page-text-style">Core Page<span class="icon icon-angle-right" data-hidden-on-mobile="" data-hidden-on-desktop="" data-hidden-on-tablet=""></span> 
3502</span> 
3503</a> 
3504</li> 
3505</ul> 
3506</li> 
3507</ul> 
3508</nav> 
3509</div> 
3510 <div id="899c2eb2" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="ONELinksMenu" data-external-id="1507374835"> <nav class="u_1507374835 unifiednav_vertical effect-bottom2 main-navigation unifiednav dmLinksMenu" data-auto="links-menu" role="navigation" layout-main="vertical_nav_layout_2" layout-sub="" data-show-vertical-sub-items="SHOW" id="1507374835" dmle_extension="onelinksmenu" data-element-type="onelinksmenu" data-logo-src="" alt="" data-nav-structure="VERTICAL" wr="true" icon="true" surround="true" adwords="" navigation-id="unifiedNav"> <ul role="menubar" class="unifiednav__container unav-top  " data-auto="navigation-pages"> <li role="menuitem" aria-haspopup="true" aria-expanded="false" data-sub-nav-menu="true" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/what-we-do/~page-item~" class="unifiednav__item unav-item unav-top-item    unifiednav__item_has-sub-nav" target="" data-target-page-alias="cat-landing"> <span class="nav-item-text " data-link-text="Cat-Landing" data-auto="page-text-style">Cat-Landing<span class="icon icon-angle-down"></span> 
3511</span> 
3512</a> 
3513 <ul role="menu" class="unifiednav__container unifiednav__container_sub-nav unav-sub" data-depth="0" data-auto="sub-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/services/~page-item~" class="unifiednav__item unav-item     " target="" data-target-page-alias="core-page"> <span class="nav-item-text " data-link-text="Core Page" data-auto="page-text-style">Core Page<span class="icon icon-angle-right" data-hidden-on-mobile="" data-hidden-on-desktop="" data-hidden-on-tablet=""></span> 
3514</span> 
3515</a> 
3516</li> 
3517</ul> 
3518</li> 
3519</ul> 
3520</nav> 
3521</div> 
3522</div> 
3523 <div id="0190983d" class="flex-element group" data-auto="flex-element-group"> <div id="7b991c9b" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="ONELinksMenu" data-external-id="1801032432"> <nav class="u_1801032432 unifiednav_vertical effect-bottom2 main-navigation unifiednav dmLinksMenu" data-auto="links-menu" role="navigation" layout-main="vertical_nav_layout_2" layout-sub="" data-show-vertical-sub-items="SHOW" id="1801032432" dmle_extension="onelinksmenu" data-element-type="onelinksmenu" data-logo-src="" alt="" data-nav-structure="VERTICAL" wr="true" icon="true" surround="true" adwords="" navigation-id="unifiedNav"> <ul role="menubar" class="unifiednav__container unav-top  " data-auto="navigation-pages"> <li role="menuitem" aria-haspopup="true" aria-expanded="false" data-sub-nav-menu="true" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/what-we-do/~page-item~" class="unifiednav__item unav-item unav-top-item    unifiednav__item_has-sub-nav" target="" data-target-page-alias="cat-landing"> <span class="nav-item-text " data-link-text="Cat-Landing" data-auto="page-text-style">Cat-Landing<span class="icon icon-angle-down"></span> 
3524</span> 
3525</a> 
3526 <ul role="menu" class="unifiednav__container unifiednav__container_sub-nav unav-sub" data-depth="0" data-auto="sub-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/services/~page-item~" class="unifiednav__item unav-item     " target="" data-target-page-alias="core-page"> <span class="nav-item-text " data-link-text="Core Page" data-auto="page-text-style">
3526Core Page<span class="icon icon-angle-right" data-hidden-on-mobile="" data-hidden-on-desktop="" data-hidden-on-tablet=""></span> 
3527</span> 
3528</a> 
3529</li> 
3530</ul> 
3531</li> 
3532</ul> 
3533</nav> 
3534</div> 
3535 <div id="39a585e9" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="ONELinksMenu" data-external-id="1338154768"> <nav class="u_1338154768 unifiednav_vertical effect-bottom2 main-navigation unifiednav dmLinksMenu" data-auto="links-menu" role="navigation" layout-main="vertical_nav_layout_2" layout-sub="" data-show-vertical-sub-items="SHOW" id="1338154768" dmle_extension="onelinksmenu" data-element-type="onelinksmenu" data-logo-src="" alt="" data-nav-structure="VERTICAL" wr="true" icon="true" surround="true" adwords="" navigation-id="unifiedNav"> <ul role="menubar" class="unifiednav__container unav-top  " data-auto="navigation-pages"> <li role="menuitem" aria-haspopup="true" aria-expanded="false" data-sub-nav-menu="true" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/what-we-do/~page-item~" class="unifiednav__item unav-item unav-top-item    unifiednav__item_has-sub-nav" target="" data-target-page-alias="cat-landing"> <span class="nav-item-text " data-link-text="Cat-Landing" data-auto="page-text-style">Cat-Landing<span class="icon icon-angle-down"></span> 
3536</span> 
3537</a> 
3538 <ul role="menu" class="unifiednav__container unifiednav__container_sub-nav unav-sub" data-depth="0" data-auto="sub-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/services/~page-item~" class="unifiednav__item unav-item     " target="" data-target-page-alias="core-page"> <span class="nav-item-text " data-link-text="Core Page" data-auto="page-text-style">Core Page<span class="icon icon-angle-right" data-hidden-on-mobile="" data-hidden-on-desktop="" data-hidden-on-tablet=""></span> 
3539</span> 
3540</a> 
3541</li> 
3542</ul> 
3543</li> 
3544</ul> 
3545</nav> 
3546</div> 
3547 <div id="4310a1b0" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="ONELinksMenu" data-external-id="1201338092"> <nav class="u_1201338092 unifiednav_vertical effect-bottom2 main-navigation unifiednav dmLinksMenu" data-auto="links-menu" role="navigation" layout-main="vertical_nav_layout_2" layout-sub="" data-show-vertical-sub-items="SHOW" id="1201338092" dmle_extension="onelinksmenu" data-element-type="onelinksmenu" data-logo-src="" alt="" data-nav-structure="VERTICAL" wr="true" icon="true" surround="true" adwords="" navigation-id="unifiedNav"> <ul role="menubar" class="unifiednav__container unav-top  " data-auto="navigation-pages"> <li role="menuitem" aria-haspopup="true" aria-expanded="false" data-sub-nav-menu="true" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/what-we-do/~page-item~" class="unifiednav__item unav-item unav-top-item    unifiednav__item_has-sub-nav" target="" data-target-page-alias="cat-landing"> <span class="nav-item-text " data-link-text="Cat-Landing" data-auto="page-text-style">Cat-Landing<span class="icon icon-angle-down"></span> 
3548</span> 
3549</a> 
3550 <ul role="menu" class="unifiednav__container unifiednav__container_sub-nav unav-sub" data-depth="0" data-auto="sub-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="1"> <a href="/services/~page-item~" class="unifiednav__item unav-item     " target="" data-target-page-alias="core-page"> <span class="nav-item-text " data-link-text="Core Page" data-auto="page-text-style">Core Page<span class="icon icon-angle-right" data-hidden-on-mobile="" data-hidden-on-desktop="" data-hidden-on-tablet=""></span> 
3551</span> 
3552</a> 
3553</li> 
3554</ul> 
3555</li> 
3556</ul> 
3557</nav> 
3558</div> 
3559</div> 
3560 <div id="group_9n6" class="flex-element group" data-auto="flex-element-group"> <div id="fe7f7167" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="ONELinksMenu" data-external-id="1401180572"> <nav class="u_1401180572 unifiednav_vertical effect-bottom2 main-navigation unifiednav dmLinksMenu" data-auto="links-menu" role="navigation" layout-main="vertical_nav_layout_2" layout-sub="" data-show-vertical-sub-items="SHOW" id="1401180572" dmle_extension="onelinksmenu" data-element-type="onelinksmenu" data-logo-src="" alt="" data-nav-structure="VERTICAL" wr="true" icon="true" surround="true" adwords="" navigation-id="unifiedNav"> <ul role="menubar" class="unifiednav__container unav-top  " data-auto="navigation-pages"> <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/services/~page-item~" class="unifiednav__item unav-item unav-top-item    " target="" data-target-page-alias="core-page"> <span class="nav-item-text " data-link-text="Core Page" data-auto="page-text-style">
3560Core Page<span class="icon icon-angle-down" data-hidden-on-mobile="" data-hidden-on-desktop="" data-hidden-on-tablet=""></span> 
3561</span> 
3562</a> 
3563</li> 
3564 <li role="menuitem" class=" unifiednav__item-wrap " data-auto="more-pages" data-depth="0"> <a href="/services/~page-item~" class="unifiednav__item unav-item unav-top-item    " target="" data-target-page-alias="core-page"> <span class="nav-item-text " data-link-text="Core Page" data-auto="page-text-style">Core Page<span class="icon icon-angle-down" data-hidden-on-mobile="" data-hidden-on-desktop="" data-hidden-on-tablet=""></span> 
3565</span> 
3566</a> 
3567</li> 
3568</ul> 
3569</nav> 
3570</div> 
3571</div> 
3572</div> 
3573</div> 
3574</section> 
3575</div> 
3576 <div data-layout-section="" data-auto="flex-section" id="1582186687" data-flex-id="2a501ecf"> <div class="flex-widgets-container" id="1924641454"></div> 
3577 <section id="c55100d8" class="flex-element section" data-auto="flex-element-section"> <div id="57b153fb" class="flex-element grid" data-auto="flex-element-grid" data-layout-grid=""> <div id="08465346" class="flex-element group" data-auto="flex-element-group"> <div id="5a572945" class="flex-element group" data-auto="flex-element-group"> <div id="widget_gf3" class="flex-element widget-wrapper" data-auto="flex-element-widget-wrapper" data-widget-type="custom" data-external-id="224877366"> <div class="widget-3e38ec u_224877366 dmCustomWidget" data-lazy-load="true" data-title="" id="224877366" dmle_extension="custom_extension" data-element-type="custom_extension" icon="false" surround="false" data-widget-id="3e38ec713838428586c343d139994b6f" data-widget-version="58" data-widget-config="eyJjb2x1bW5Db3VudCI6IjMiLCJ1bmRlcmxpbmVTZWxlY3RlZCI6dHJ1ZSwic2VsZWN0ZWRCZ0NvbG9yIjoidHJhbnNwYXJlbnQiLCJleGNsdWRlUGFyZW50cyI6ImhvbWUsIGFib3V0LCBjb250YWN0LCBnYWxsZXJ5IiwiaW5jbHVkZVBhcmVudHMiOiIiLCJjaGV2cm9uSWNvbiI6IjxzdmcgeG1sbnM9XCJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Z1wiIHZpZXdCb3g9XCIwIDAgMjQgMjRcIiBmaWxsPVwiY3VycmVudENvbG9yXCIgd2lkdGg9XCIxNlwiIGhlaWdodD1cIjE2XCI+PHBhdGggZD1cIk03LjQxIDguNTlMMTIgMTMuMTdsNC41OS00LjU4TDE4IDEwbC02IDYtNi02IDEuNDEtMS40MXpcIi8+PC9zdmc+IiwiZXhwYW5kQWxsIjp0cnVlfQ=="><!-- === Real-Data Navigation Grid Widget (parent remains a hyperlink) === --> <div id="nav-grid-widget" data-columns="3" data-source="auto"></div> 
3578</div> 
3579</div> 
3580</div> 
3581</div> 
3582</div> 
3583</section> 
3584</div> 
3585</div> 
3586</div> 
3587</div> 
3588 <div id="swh-492bf3ca8d6f4982917ac4c553c6727d"> <div data-wrapper-source="appstore"><!--audioeye-managed:accessibility--> 
3588<script data-audioeye-managed="accessibility">    if (window.location.search.match(/[?,&]preview=true/) && window.top !== window) {      window.__AudioEyePreviewMode=true;    }  </script>
3588 
3589 
3589<script type="text/javascript">!function(){    var b=function(){      window.__AudioEyeSiteHash = "c450edbb1099533195e4a26d444b2b1b";      window.__AudioEyeInstallSource = "duda";      var a=document.createElement("script");      a.src="https://wsv3cdn.audioeye-services.com/aem.js";      a.type="text/javascript";      a.setAttribute("async","");      document.getElementsByTagName("body")[0].appendChild(a)};      "complete"!==document.readyState?window.addEventListener?window.addEventListener("load",b):window.attachEvent&&window.attachEvent("onload",b):b()}();    </script>
3589 
3590</div> 
3591</div> 
3592</div> 
3593</div> 
3594</div> 
3595</div> 
3596</div> 
3597</div> 
3598
3599    </div>
3600</div>
3601 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-mobile_implicit-flex-header-3dd8cf0da1894ab5a9043c416f0c4aca">@media (max-width: 767px) {
3602}</style> 
3603 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-mobile_implicit-4dc8a903-3dd8cf0da1894ab5a9043c416f0c4aca">@media (max-width: 767px) {
3604    
3605#\32 cb3e9f9 {
3606    grid-template-rows: minmax(8px, max-content);
3607}
3608    
3609#\32 7581a62 {
3610    grid-area: 1 / 1 / span 1 / span 1;
3611}
3612}</style> 
3613 <style data-page-uuid="8d67b06083f34b4a8a328c95f017fec2" id="grid-styles-mobile-24b6b13b-8d67b06083f34b4a8a328c95f017fec2">@media (max-width: 767px) {
3614    
3615#d4329249 {
3616    min-height: 80px;
3617    align-items: center;
3618    width: 100%;
3619}
3620    
3621#\33 4c91571 {
3622    min-height: 0;
3623    flex-direction: column;
3624    padding-left: 4%;
3625    padding-right: 4%;
3626    flex-wrap: nowrap;
3627}
3628}</style> 
3629 <style data-page-uuid="8d67b06083f34b4a8a328c95f017fec2" id="grid-styles-mobile-7ef32d84-8d67b06083f34b4a8a328c95f017fec2">@media (max-width: 767px) {
3630    
3631#\36 a108e83 {
3632    min-height: 0;
3633    flex-direction: column;
3634    padding-left: 4%;
3635    padding-right: 4%;
3636    flex-wrap: nowrap;
3637}
3638    
3639#\38 c30d83a {
3640    width: 100%;
3641    min-height: 0px;
3642    align-items: flex-start;
3643}
3644}</style> 
3645 <style data-page-uuid="8d67b06083f34b4a8a328c95f017fec2" id="grid-styles-mobile-5c4456af-8d67b06083f34b4a8a328c95f017fec2">@media (max-width: 767px) {
3646    
3647#\35 20e9609 {
3648    min-height: 0;
3649    flex-direction: column;
3650    padding-left: 4%;
3651    padding-right: 4%;
3652    flex-wrap: nowrap;
3653}
3654    
3655#\36 8fb7ec9 {
3656    width: 100%;
3657    min-height: 80px;
3658    align-items: center;
3659    padding-left: 1px;
3660    padding-right: 1px;
3661}
3662}</style> 
3663 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-mobile-flex-footer-3dd8cf0da1894ab5a9043c416f0c4aca">@media (max-width: 767px) {
3664    
3665}</style> 
3666 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-mobile-flex-header-3dd8cf0da1894ab5a9043c416f0c4aca">@media (max-width: 767px) {
3667    
3668#flex-header[data-changeable-on-scroll][data-scrolling] [data-widget-type="image"] img {
3669    transform: scale(1);
3670}
3671#flex-header {
3672    
3673    padding-top: 0%;
3674    padding-bottom: 0%;
3675}
3676}</style> 
3677 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-mobile-405ddf11-3dd8cf0da1894ab5a9043c416f0c4aca">@media (max-width: 767px) {
3678    
3679#\31 d3acdb9 {
3680    flex-direction: row;
3681    padding-left: 0%;
3682    padding-right: 0%;
3683    flex-wrap: nowrap;
3684    min-height: 60px;
3685}
3686    
3687#\39 f3f031f {
3688    width: 100%;
3689    align-items: center;
3690    min-height: 8px;
3691    column-gap: 0%;
3692    justify-content: center;
3693    flex-direction: column;
3694    flex-wrap: nowrap;
3695    row-gap: 0px;
3696    padding: 0px 0px 0px 0px;
3697}
3698    
3699#\31 1d216b4 {
3700    width: 15%;
3701    order: 0;
3702    justify-self: auto;
3703    align-self: auto;
3704    margin: 0px 0% 0px 0%;
3705    grid-area: auto / auto / auto / auto;
3706}
3707    
3708#bff1bb66 {
3709    height: auto;
3710    width: 227px;
3711}
3712    
3713#a052d7aa {
3714    order: 0;
3715    justify-self: auto;
3716    align-self: center;
3717    width: 50%;
3718    justify-content: center;
3719    padding-right: 1%;
3720    padding-left: 1%;
3721    margin: 0px 0% 0px 0%;
3722    grid-area: auto / auto / auto / auto;
3723}
3724    
3725#\39 8702d80 {
3726    min-height: 170px;
3727}
3728    
3729#da0131a7 {
3730    width: 70%;
3731    min-height: 70px;
3732    justify-self: auto;
3733    align-self: auto;
3734    order: 1;
3735    margin: 0px 0% 0px 0%;
3736    grid-area: auto / auto / auto / auto;
3737}
3738    
3739#\39 e5ef93d {
3740    justify-self: auto;
3741    width: 100%;
3742    order: 0;
3743    min-height: 32px;
3744    align-self: auto;
3745    padding: 0% 4% 0% 4%;
3746    margin: 0px 0% 0px 0%;
3747    grid-area: auto / auto / auto / auto;
3748}
3749    
3750#\33 aa37ebb {
3751    width: 15%;
3752    justify-content: center;
3753    order: 2;
3754}
3755    
3756#\32 e9284f4 {
3757    align-self: center;
3758    margin-left: 0px;
3759    margin-right: 0px;
3760    align-items: center;
3761    min-height: 80px;
3762}
3763    
3764#\30 ece3d12 {
3765    justify-self: auto;
3766    align-self: center;
3767    width: 50%;
3768    order: 1;
3769    justify-content: center;
3770    padding-left: 1%;
3771    padding-right: 1%;
3772    margin: 0px 0% 0px 0%;
3773    grid-area: auto / auto / auto / auto;
3774}
3775    
3776#daf81bcc.flex-element.widget-wrapper >
3776 [data-element-type].flexButton.dmWidget {
3777    padding: 10px 0px 10px 0px;
3778}
3779#daf81bcc {
3780    width: 250px;
3781    min-height: 40px;
3782    margin: 0px 0% 0px 0%;
3783}
3784    
3785#\34 cb86e4c {
3786    align-items: center;
3787    min-height: 32px;
3788}
3789    
3790#df845449 {
3791    min-height: 40px;
3792}
3793    
3794#ec5d46f2 {
3795    width: 340px;
3796    height: auto;
3797}
3798}</style> 
3799 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-mobile-157b4fff-3dd8cf0da1894ab5a9043c416f0c4aca">@media (max-width: 767px) {
3800    
3801#\32 fd7b414 {
3802    width: 25%;
3803    align-items: center;
3804    min-height: 8px;
3805    justify-content: center;
3806    padding-top: 4px;
3807    padding-bottom: 4px;
3808    column-gap: 2%;
3809}
3810    
3811#\33 2767c2f {
3812    flex-direction: row;
3813    padding-left: 0%;
3814    padding-right: 0%;
3815    flex-wrap: nowrap;
3816    min-height: 60px;
3817}
3818    
3819#group_7in {
3820    width: 25%;
3821    min-height: 8px;
3822}
3823    
3824#group_jjh {
3825    width: 25%;
3826    min-height: 8px;
3827}
3828    
3829#group_23b {
3830    width: 25%;
3831    min-height: 8px;
3832}
3833}</style> 
3834 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-mobile-c0ae99ba-3dd8cf0da1894ab5a9043c416f0c4aca">@media (max-width: 767px) {
3835    
3836#\33 710bc01 {
3837    width: 100%;
3838    align-items: center;
3839    min-height: 8px;
3840}
3841    
3842#\39 360b347 {
3843    flex-direction: row;
3844    padding-left: 0%;
3845    padding-right: 0%;
3846    flex-wrap: nowrap;
3847    min-height: 60px;
3848}
3849    
3850#bd946f29 {
3851    order: 2;
3852}
3853    
3854#\35 bfb3718 {
3855    height: auto;
3856}
3857    
3858#\33 a2d0b90 {
3859    order: 4;
3860}
3861    
3862#a8f32aec {
3863    order: 2;
3864}
3865}</style> 
3866 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-mobile-4a710abd-3dd8cf0da1894ab5a9043c416f0c4aca">@media (max-width: 767px) {
3867    
3868#a7708a16 {
3869    width: 100%;
3870    min-height: 80px;
3871}
3872    
3873#\37 8db3927 {
3874    min-height: unset;
3875    flex-direction: column;
3876}
3877}</style> 
3878 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-mobile-63a049320d35b55b4ef2a372-3dd8cf0da1894ab5a9043c416f0c4aca">@media (max-width: 767px) {
3879    
3880#\36 3a049320d35b55b4ef2a374 {
3881    min-height: 695px;
3882    justify-content: flex-start;
3883    padding-top: 10px;
3884    padding-left: 8px;
3885    padding-right: 0px;
3886}
3887    
3888#\36 3a049320d35b55b4ef2a377 {
3889    min-height: unset;
3890    flex-direction: column;
3891    padding: 0% 4% 0% 4%;
3892}
3893}</style> 
3894 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-mobile-6406026c90e1521f6ecb0eca-3dd8cf0da1894ab5a9043c416f0c4aca">@media (max-width: 767px) {
3895    
3896#\36 406026c90e1521f6ecb0ecd {
3897    min-height: 209px;
3898    justify-content: center;
3899    padding-bottom: 8%;
3900    padding-top: 8%;
3901}
3902    
3903#\36 406026c90e1521f6ecb0ecc {
3904    min-height: unset;
3905    flex-direction: column;
3906    padding: 0% 4% 0% 4%;
3907}
3908}</style> 
3909 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-mobile-2c8e291c-3dd8cf0da1894ab5a9043c416f0c4aca">@media (max-width: 767px) {
3910    
3911#b7a700bf {
3912    min-height: 0;
3913    flex-direction: column;
3914    padding-left: 4%;
3915    padding-right: 4%;
3916    flex-wrap: nowrap;
3917}
3918    
3919#\36 405d24c {
3920    width: 100%;
3921    min-height: 80px;
3922    align-items: center;
3923    padding: 0px 0px 0px 0px;
3924}
3925}</style> 
3926 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-mobile-7b00a5de-3dd8cf0da1894ab5a9043c416f0c4aca">@media (max-width: 767px) {
3927    
3928#\39 34bad03 {
3929    width: 100.00%;
3930}
3931    
3932#\36 e06962d {
3933    min-height: unset;
3934    flex-direction: column;
3935    padding-left: 4%;
3936    padding-right: 4%;
3937}
3938    
3939#\30 5e0ee19::before, #\30 5e0ee19 > .bgExtraLayerOverlay {
3940    
3941}
3942#\30 5e0ee19 {
3943    min-height: 160px;
3944    align-items: center;
3945    width: 100%;
3946    background-image: url('https://le-cdn.hibuwebsites.com/f6cdfabba1c749a1a3d3b7a9f4c30c76/dms3rep/multi/opt/video-jtn-electrical-2880w.jpg') !important;
3947}
3948    
3949#a019b36e {
3950    min-height: 160px;
3951    align-items: center;
3952    width: 100%;
3953    padding-left: 0px;
3954    padding-right: 0px;
3955}
3956}</style> 
3957 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-mobile-b6b5923e-3dd8cf0da1894ab5a9043c416f0c4aca">@media (max-width: 767px) {
3958    
3959#\34 efeffee {
3960    flex-direction: column;
3961    padding-left: 4%;
3962    padding-right: 4%;
3963    flex-wrap: nowrap;
3964    min-height: 0;
3965}
3966    
3967#group_b0q {
3968    width: 100%;
3969    min-height: 80px;
3970}
3971    
3972#f2debc4b {
3973    width: 100%;
3974    min-height: 80px;
3975}
3976    
3977#d0234170 {
3978    width: 100%;
3979    min-height: 80px;
3980}
3981}</style> 
3982 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-mobile-2f831c0f-3dd8cf0da1894ab5a9043c416f0c4aca">@media (max-width: 767px) {
3983    
3984#\30 0841c9d {
3985    width: 100%;
3986    align-items: center;
3987    min-height: 8px;
3988}
3989    
3990#d027d36f {
3991    flex-direction: row;
3992    padding-left: 4%;
3993    padding-right: 4%;
3994    flex-wrap: nowrap;
3995    min-height: 80px;
3996}
3997}</style> 
3998 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-mobile-54290af5-3dd8cf0da1894ab5a9043c416f0c4aca">@media (max-width: 767px) {
3999    
4000#\37 ca01c83 {
4001    width: 100%;
4002    min-height: 0px;
4003    align-items: flex-start;
4004}
4005    
4006#\32 4eff763 {
4007    min-height: 0;
4008    flex-direction: column;
4009    flex-wrap: nowrap;
4010    padding: 0px 4% 0px 4%;
4011}
4012    
4013#\30 c6cf26f {
4014    margin: 0px 0% 0px 0%;
4015}
4016}</style> 
4017 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-mobile-4dc8a903-3dd8cf0da1894ab5a9043c416f0c4aca">@media (max-width: 767px) {
4018    
4019#\30 190983d {
4020    width: 100%;
4021    min-height: 80px;
4022    align-items: center;
4023}
4024    
4025#d200b2f7 {
4026    width: 100%;
4027    min-height: 80px;
4028    align-items: center;
4029}
4030    
4031#\32 7581a62 {
4032    flex-direction: column;
4033    min-height: 0;
4034    flex-wrap: nowrap;
4035    row-gap: 0px;
4036    column-gap: 0%;
4037    padding: 0px 0px 0px 0px;
4038}
4039    
4040#ad9cb284 {
4041    width: 100%;
4042    min-height: 80px;
4043    align-items: center;
4044}
4045    
4046#group_9n6 {
4047    width: 100%;
4048    min-height: 80px;
4049    align-items: center;
4050}
4051}</style> 
4052 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-mobile-2a501ecf-3dd8cf0da1894ab5a9043c416f0c4aca">@media (max-width: 767px) {
4053    
4054#\30 8465346 {
4055    min-height: 0;
4056    flex-direction: column;
4057    padding-left: 4%;
4058    padding-right: 4%;
4059    flex-wrap: nowrap;
4060}
4061    
4062#\35 a572945 {
4063    width: 100%;
4064    min-height: 80px;
4065    align-items: center;
4066}
4067}</style> 
4068 <style data-page-uuid="3dd8cf0da1894ab5a9043c416f0c4aca" data-is-header="true" id="grid-styles-mobile-8a9cfcfc-3dd8cf0da1894ab5a9043c416f0c4aca">@media (max-width: 767px) {
4069    
4070#\37 5ba1c41 {
4071    min-height: 0;
4072    flex-direction: column;
4073    padding-left: 4%;
4074    padding-right: 4%;
4075    flex-wrap: nowrap;
4076}
4077    
4078#\34 f4d3298 {
4079    width: 100%;
4080    min-height: 80px;
4081    align-items: center;
4082}
4083    
4084#widget_21v {
4085    max-width: 100%;
4086    width: 55px;
4087    margin: 20px 20px 20px 20px;
4088}
4089    
4090#widget_fpt {
4091    max-width: 100%;
4092    margin: 20px 20px 20px 20px;
4093}
4094    
4095#widget_301 {
4096    max-width: 100%;
4097    margin: 20px 20px 20px 20px;
4098}
4099}</style> 
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139<script>
4140    var isWLR = true;
4141
4142    window.customWidgetsFunctions = {};
4143    window.customWidgetsStrings = {};
4144    window.collections = {};
4145    window.currentLanguage = "ENGLISH"
4146    window.isSitePreview = false;
4147</script>
4147
4148
4149
4150
4151<script>
4152    window.customWidgetsFunctions["5b093d0fb9474c1dabfa7807be463b12~52"] = function (element, data, api) {
4153        window.dmAPI.runOnReady('nmw-mega-nav', () => {
4154  'use strict';
4155
4156  /*
4157    NMW NAVIGATION ARCHITECTURE
4158
4159    FULL WIDGET
4160    - Fetches 06 NAV MASTER, 02 CATEGORY, 03 CORE, and optionally CUSTOMNAV.
4161    - Reads configured native pages from the hidden .navmasterdefault Duda nav.
4162    - Builds one normalized three-level navigation model.
4163    - Renders the Full navigation and publishes the shared model through window.NMW.
4164
4165    MIRROR WIDGET
4166    - Never fetches collections.
4167    - Waits for the Full widget to publish shared data.
4168    - Renders the same data vertically and stays synchronized.
4169
4170    SHARED BEHAVIOR
4171    - Click and optional hover opening.
4172    - 200 ms hover-close delay.
4173    - Escape, focus-leave, link-click, and outside-click closing.
4174    - Active-page detection from the actual current URL.
4175    - Prefix-aware dynamic matching (for example /category/x and /services/x).
4176    - JavaScript owns menu state; CSS owns appearance, positioning, and animation.
4177  */
4178
4179  const root = element.querySelector('.nmw-root');
4180  if (!root) return;
4181
4182  /*
4183    DUDA EDITOR LIFECYCLE
4184
4185    Duda can execute the widget JavaScript again against the same DOM root when
4186    a panel value changes. The prior closure is not automatically destroyed.
4187    Without explicit cleanup, the old instance continues observing/fetching and
4188    can overwrite the navigation built with the new configuration.
4189  */
4190  window.NMW_RUNTIME = window.NMW_RUNTIME || { instances: new WeakMap() };
4191  const previousInstanceCleanup = window.NMW_RUNTIME.instances.get(root);
4192  if (typeof previousInstanceCleanup === 'function') {
4193    try {
4194      previousInstanceCleanup('reinitialized');
4195    } catch (cleanupError) {
4196      console.warn('[NMW] Previous instance cleanup failed', cleanupError);
4197    }
4198  }
4199
4200  // Support both the rebuilt HTML and the original widget HTML.
4201  // Rebuilt HTML uses [data-nmw-render]; the original widget uses
4202  // .nmw-mobile-nav-wrapper as its render container.
4203  let renderHost = root.querySelector('[data-nmw-render]');
4204  if (!renderHost) renderHost = root.querySelector('.nmw-mobile-nav-wrapper');
4205  if (!renderHost) {
4206    renderHost = document.createElement('div');
4207    renderHost.className = 'nmw-render';
4208    renderHost.setAttribute('data-nmw-render', '');
4209    root.appendChild(renderHost);
4210  }
4211
4212  // Startup marker used to confirm that Duda executed the runOnReady callback.
4213  root.dataset.nmwScriptStarted = 'true';
4214
4215  // Show an immediate status only when the HTML does not provide a separate
4216  // loading host. The supplied Full/feature HTML uses [data-nmw-loading-view].
4217  const separateLoadingHost = root.querySelector('[data-nmw-loading-view]');
4218  if (renderHost && !renderHost.innerHTML.trim() && !separateLoadingHost) {
4219    renderHost.innerHTML = '<div class="nmw-runtime-status" data-nmw-runtime-status>Loading navigation…</div>';
4220  }
4221
4222  const loadingHost = separateLoadingHost || root.querySelector('.nmw-skeleton-nav');
4223  const toolsHost = root.querySelector('[data-nmw-tools]');
4224  const cfg = data.config || {};
4225  const widgetMode = readString(configValue('widgetMode'), 'full').toLowerCase() === 'mirror' ? 'mirror' : 'full';
4226  const instanceId = createInstanceId(widgetMode);
4227  const HOVER_CLOSE_DELAY = 200;
4228  const REBUILD_DEBOUNCE = 200;
4229  const NATIVE_RETRY_INTERVAL = 500;
4230  const NATIVE_MAX_WAIT = 15000;
4231  const MAX_DEPTH = 2;
4232
4233  // Dynamic page path controls from the widget panel.
4234  // Example: childUrlPrefix = "services" resolves HVAC against /services/hvac.
4235  // Multiple aliases are supported so existing or AI-created panel fields work.
4236  const CATEGORY_URL_PREFIX = normalizeUrlPrefix(
4237    configValue('categoryUrlPrefix', 'categoryPrefix', 'categoryPagePrefix'),
4238    'category'
4239  );
4240  const CHILD_URL_PREFIX = normalizeUrlPrefix(
4241    configValue('childUrlPrefix', 'childPrefix', 'childPagePrefix', 'coreUrlPrefix', 'corePrefix'),
4242    'services'
4243  );
4244
4245  root.dataset.nmwMode = widgetMode;
4246  root.classList.toggle('nmw-mirror', widgetMode === 'mirror');
4247  root.classList.toggle('nmw-vertical', widgetMode === 'mirror');
4248  root.dataset.nmwCategoryPrefix = CATEGORY_URL_PREFIX;
4249  root.dataset.nmwChildPrefix = CHILD_URL_PREFIX;
4250
4251  // The shared namespace survives Duda widget rerenders so Mirror can keep using
4252  // the last valid navigation while Full reinitializes.
4253  window.NMW = window.NMW || {};
4254  Object.assign(window.NMW, {
4255    loading: Boolean(window.NMW.loading),
4256    ready: Boolean(window.NMW.ready),
4257    version: Number(window.NMW.version) || 0,
4258    data: Array.isArray(window.NMW.data) ? window.NMW.data : null,
4259    error: Boolean(window.NMW.error),
4260    warnings: Array.isArray(window.NMW.warnings) ? window.NMW.warnings : [],
4261    ownerId: window.NMW.ownerId || null,
4262    mirrorOwnerId: window.NMW.mirrorOwnerId || null,
4263    fullConfig: window.NMW.fullConfig || null,
4264    fullConfigSignature: window.NMW.fullConfigSignature || '',
4265    presentationVersion: Number(window.NMW.presentationVersion) || 0,
4266    megaGroupsUsed: Array.isArray(window.NMW.megaGroupsUsed) ? window.NMW.megaGroupsUsed : [],
4267    megaGroupCount: Number(window.NMW.megaGroupCount) || 0,
4268    megaLabels: window.NMW.megaLabels || {},
4269    slugWarnings: Array.isArray(window.NMW.slugWarnings) ? window.NMW.slugWarnings : []
4270  });
4271
4272  const local = {
4273    cleaned: false,
4274    listenersAttached: false,
4275    hoverTimers: new Map(),
4276    anchorTimers: new Set(),
4277    closeJobs: new WeakMap(),
4278    closeTimers: new Set(),
4279    rebuildTimer: null,
4280    rebuildRunning: false,
4281    pendingRebuild: false,
4282    nativeObserver: null,
4283    nativeFinderObserver: null,
4284    manualObserver: null,
4285    nativeRetryTimer: null,
4286    nativeRetryStarted: 0,
4287    historyCleanup: null,
4288    customDraft: null,
4289    latestBuildWarnings: [],
4290    slugValidationWarnings: [],
4291    eventCleanups: [],
4292    removalObserver: null
4293  };
4294
4295  function listen(targetNode, eventName, handler, options) {
4296    if (!targetNode || typeof targetNode.addEventListener !== 'function') return;
4297    targetNode.addEventListener(eventName, handler, options);
4298    local.eventCleanups.push(() => {
4299      try { targetNode.removeEventListener(eventName, handler, options); }
4300      catch (error) {}
4301    });
4302  }
4303
4304  // ---------------------------------------------------------------------------
4305  // Generic helpers
4306  // ---------------------------------------------------------------------------
4307
4308  function readString(value, fallback = '') {
4309    if (value == null) return fallback;
4310    return String(value).trim() || fallback;
4311  }
4312
4313  function readBoolean(value, fallback = false) {
4314    if (value == null || value === '') return fallback;
4315    if (typeof value === 'boolean') return value;
4316    const normalized = String(value).trim().toLowerCase();
4317    if (['true', '1', 'yes', 'on'].includes(normalized)) return true;
4318    if (['false', '0', 'no', 'off'].includes(normalized)) return false;
4319    return fallback;
4320  }
4321
4322  function readNumber(value, fallback, min = -Infinity, max = Infinity) {
4323    const parsed = Number(value);
4324    if (!Number.isFinite(parsed)) return fallback;
4325    return Math.min(max, Math.max(min, parsed));
4326  }
4327
4328  function escapeHtml(value) {
4329    return String(value == null ? '' : value)
4330      .replace(/&/g, '&amp;')
4331      .replace(/</g, '&lt;')
4332      .replace(/>/g, '&gt;')
4333      .replace(/"/g, '&quot;')
4334      .replace(/'/g, '&#39;');
4335  }
4336
4337  function cssValue(value, fallback) {
4338    let candidate = value;
4339    if (candidate && typeof candidate === 'object') {
4340      candidate = candidate.value
4341        ?? candidate.css
4342        ?? candidate.color
4343        ?? candidate.hex
4344        ?? candidate.fontFamily
4345        ?? candidate.family
4346        ?? candidate.name
4347        ?? '';
4348    }
4349    const result = readString(candidate, '');
4350    return result || fallback;
4351  }
4352
4353  function cssLength(value, fallback) {
4354    const result = cssValue(value, '');
4355    if (!result) return fallback;
4356    return /^-?(?:\d+|\d*\.\d+)$/.test(result) ? `${result}px` : result;
4357  }
4358
4359  // Duda font controls may return a plain string, a number, or a nested
4360  // typography object. Read the requested part without assuming one exact shape.
4361  function readFontPart(value, part, fallback) {
4362    const visited = new Set();
4363
4364    function parse(candidate, depth = 0) {
4365      if (candidate == null || depth > 6) return undefined;
4366
4367      if (typeof candidate === 'string') {
4368        const text = candidate.trim();
4369        if (!text) return undefined;
4370
4371        // Some Duda controls serialize their configuration as JSON text.
4372        if ((text.startsWith('{') && text.endsWith('}')) || (text.startsWith('[') && text.endsWith(']'))) {
4373          try {
4374            const parsed = JSON.parse(text);
4375            const nested = parse(parsed, depth + 1);
4376            if (nested !== undefined) return nested;
4377          } catch (error) {}
4378        }
4379
4380        if (part === 'size') {
4381          const directLength = text.match(/^-?(?:\d+|\d*\.\d+)(?:px|rem|em|%|vw|vh|pt)?$/i);
4382          if (directLength) return cssLength(text, fallback);
4383          const embeddedLength = text.match(/(?:^|\s)(\d+(?:\.\d+)?(?:px|rem|em|%|vw|vh|pt))(?:\s|\/|$)/i);
4384          return embeddedLength ? embeddedLength[1] : undefined;
4385        }
4386
4387        if (part === 'weight') {
4388          if (/^(?:normal|bold|bolder|lighter|[1-9]00)$/i.test(text)) return text;
4389          return undefined;
4390        }
4391
4392        // A family string should not be mistaken for a size or weight.
4393        if (/^-?(?:\d+|\d*\.\d+)(?:px|rem|em|%|vw|vh|pt)?$/i.test(text)) return undefined;
4394        if (/^(?:normal|bold|bolder|lighter|[1-9]00)$/i.test(text)) return undefined;
4395        return text;
4396      }
4397
4398      if (typeof candidate === 'number') {
4399        if (part === 'size') return `${candidate}px`;
4400        if (part === 'weight') return String(candidate);
4401        return undefined;
4402      }
4403
4404      if (typeof candidate !== 'object') return undefined;
4405      if (visited.has(candidate)) return undefined;
4406      visited.add(candidate);
4407
4408      const keyMap = {
4409        family: ['fontFamily', 'font-family', 'family', 'fontName', 'font', 'name'],
4410        size: ['fontSize', 'font-size', 'size', 'textSize', 'text-size'],
4411        weight: ['fontWeight', 'font-weight', 'weight']
4412      };
4413
4414      for (const key of keyMap[part] || []) {
4415        if (Object.prototype.hasOwnProperty.call(candidate, key)) {
4416          const found = parse(candidate[key], depth + 1);
4417          if (found !== undefined) return found;
4418        }
4419      }
4420
4421      // Duda controls may wrap the typography value inside one of these keys.
4422      for (const key of ['value', 'typography', 'style', 'styles', 'settings', 'fontSettings', 'data']) {
4423        if (Object.prototype.hasOwnProperty.call(candidate, key)) {
4424          const found = parse(candidate[key], depth + 1);
4425          if (found !== undefined) return found;
4426        }
4427      }
4428
4429      // Last-resort recursive scan for an unfamiliar wrapper object.
4430      for (const child of Object.values(candidate)) {
4431        if (child && typeof child === 'object') {
4432          const found = parse(child, depth + 1);
4433          if (found !== undefined) return found;
4434        }
4435      }
4436
4437      return undefined;
4438    }
4439
4440    const result = parse(value);
4441    return result === undefined || result === '' ? fallback : result;
4442  }
4443
4444  function fontFamilyValue(value, fallback = 'inherit') {
4445    return readFontPart(value, 'family', fallback);
4446  }
4447
4448  function fontSizeValue(value, fallback = '16px') {
4449    return readFontPart(value, 'size', fallback);
4450  }
4451
4452  function fontWeightValue(value, fallback = '400') {
4453    return readFontPart(value, 'weight', fallback);
4454  }
4455
4456  function normalizeChevronStyle(value, fallback = 'filled') {
4457    const normalized = readString(value, fallback).toLowerCase();
4458    if (['none', 'off', 'hidden'].includes(normalized)) return 'none';
4459    if (['chevron', 'outline'].includes(normalized)) return 'chevron';
4460    if (normalized === 'caret') return 'caret';
4461    if (normalized === 'triangle') return 'triangle';
4462    return 'filled';
4463  }
4464
4465  function createInstanceId(mode) {
4466    return `nmw-${mode}-${Math.random().toString(36).slice(2, 9)}`;
4467  }
4468
4469  function field(row, ...names) {
4470    if (!row || typeof row !== 'object') return '';
4471    for (const name of names) {
4472      if (Object.prototype.hasOwnProperty.call(row, name)) return row[name];
4473      const key = Object.keys(row).find(item => item.toLowerCase() === String(name).toLowerCase());
4474      if (key) return row[key];
4475    }
4476    return '';
4477  }
4478
4479  // Read a widget configuration value by key, including values placed inside
4480  // Duda panel groups. This makes fields such as childUrlPrefix work whether the
4481  // panel stores them directly on data.config or inside a grouped object.
4482  function configValue(...names) {
4483    const wanted = names.map(name => String(name).toLowerCase());
4484    const visited = new Set();
4485
4486    function search(value, depth) {
4487      if (!value || typeof value !== 'object' || Array.isArray(value) || depth > 6 || visited.has(value)) {
4488        return undefined;
4489      }
4490      visited.add(value);
4491
4492      for (const key of Object.keys(value)) {
4493        if (wanted.includes(key.toLowerCase()) && value[key] !== undefined) {
4494          return value[key];
4495        }
4496      }
4497
4498      for (const key of Object.keys(value)) {
4499        const found = search(value[key], depth + 1);
4500        if (found !== undefined) return found;
4501      }
4502
4503      return undefined;
4504    }
4505
4506    return search(cfg, 0);
4507  }
4508
4509
4510  function configValueFrom(source, ...names) {
4511    const wanted = names.map(name => String(name).toLowerCase());
4512    const visited = new Set();
4513
4514    function search(value, depth) {
4515      if (!value || typeof value !== 'object' || depth > 8 || visited.has(value)) return undefined;
4516      visited.add(value);
4517
4518      if (!Array.isArray(value)) {
4519        for (const key of Object.keys(value)) {
4520          if (wanted.includes(key.toLowerCase()) && value[key] !== undefined) return value[key];
4521        }
4522      }
4523
4524      for (const child of Object.values(value)) {
4525        if (child && typeof child === 'object') {
4526          const found = search(child, depth + 1);
4527          if (found !== undefined) return found;
4528        }
4529      }
4530      return undefined;
4531    }
4532
4533    return search(source, 0);
4534  }
4535
4536  // Search a configuration object while refusing to descend into named groups.
4537  // This is important for Duda Design Panels because Full and Mirror can contain
4538  // identical child keys (for example universalFontSize). Without excluding
4539  // mirrorStyles, the Full widget may accidentally read the hidden Mirror default.
4540  function configValueFromExcluding(source, excludedNames, ...names) {
4541    const wanted = names.map(name => String(name).toLowerCase());
4542    const excluded = new Set((excludedNames || []).map(name => String(name).toLowerCase()));
4543    const visited = new Set();
4544
4545    function search(value, depth) {
4546      if (!value || typeof value !== 'object' || depth > 8 || visited.has(value)) return undefined;
4547      visited.add(value);
4548
4549      if (!Array.isArray(value)) {
4550        for (const key of Object.keys(value)) {
4551          const lower = key.toLowerCase();
4552          if (excluded.has(lower)) continue;
4553          if (wanted.includes(lower) && value[key] !== undefined) return value[key];
4554        }
4555      }
4556
4557      for (const [key, child] of Object.entries(value)) {
4558        if (excluded.has(String(key).toLowerCase())) continue;
4559        if (child && typeof child === 'object') {
4560          const found = search(child, depth + 1);
4561          if (found !== undefined) return found;
4562        }
4563      }
4564      return undefined;
4565    }
4566
4567    return search(source, 0);
4568  }
4569
4570  function cloneSerializable(value) {
4571    try { return JSON.parse(JSON.stringify(value)); }
4572    catch (error) { return value; }
4573  }
4574
4575  function fullConfigRoot() {
4576    return widgetMode === 'mirror' && window.NMW.fullConfig ? window.NMW.fullConfig : cfg;
4577  }
4578
4579  function fullConfigValue(...names) {
4580    const found = configValueFrom(fullConfigRoot(), ...names);
4581    return found;
4582  }
4583
4584  function mirrorUsesSeparateStyling() {
4585    return readBoolean(configValue('useSeparateMirrorStyling'), false);
4586  }
4587
4588  function designConfigRoot(mode) {
4589    if (mode === 'mirror' && mirrorUsesSeparateStyling()) {
4590      // Search only inside the dedicated Mirror group when separate styling is on.
4591      return configValueFrom(cfg, 'mirrorStyles') || {};
4592    }
4593
4594    // Full styling, and Mirror inheritance, must never descend into mirrorStyles.
4595    const base = mode === 'mirror' ? fullConfigRoot() : cfg;
4596    const fullStyles = configValueFromExcluding(base, ['mirrorStyles'], 'fullStyles');
4597    return fullStyles && typeof fullStyles === 'object' ? fullStyles : base;
4598  }
4599
4600  function designValue(mode, names, fallback) {
4601    const list = Array.isArray(names) ? names : [names];
4602
4603    if (mode === 'mirror' && mirrorUsesSeparateStyling()) {
4604      const mirrorRoot = designConfigRoot(mode);
4605      const mirrorFound = configValueFrom(mirrorRoot, ...list);
4606      return mirrorFound === undefined ? fallback : mirrorFound;
4607    }
4608
4609    // Exclude mirrorStyles so identically named Mirror fields cannot shadow the
4610    // active Full Design Panel values. This applies to Full and inherited Mirror.
4611    const base = mode === 'mirror' ? fullConfigRoot() : cfg;
4612    const designRoot = designConfigRoot(mode);
4613    let found = configValueFromExcluding(designRoot, ['mirrorStyles'], ...list);
4614    if (found === undefined && designRoot !== base) {
4615      found = configValueFromExcluding(base, ['mirrorStyles'], ...list);
4616    }
4617    return found === undefined ? fallback : found;
4618  }
4619
4620  // URL prefixes are entered in the Duda panel as simple values such as
4621  // "category" or "services". Leading/trailing slashes are removed, but an
4622  // intentionally blank prefix is allowed and resolves pages from the site root.
4623  function normalizeUrlPrefix(value, fallback) {
4624    const supplied = value === undefined || value === null ? fallback : String(value).trim();
4625    return supplied.replace(/^\/+|\/+$/g, '');
4626  }
4627
4628  function unique(values) {
4629    return [...new Set(values.filter(Boolean))];
4630  }
4631
4632  // Display labels preserve their original punctuation. Slug candidates are used
4633  // only for collection matching and stable keys.
4634  function createSlugCandidates(value) {
4635    const original = readString(value, '').toLowerCase().replace(/\//g, '-');
4636    const withAnd = original
4637      .replace(/\s*&\s*/g, '-and-')
4638      .replace(/[^a-z0-9\s-]/g, '')
4639      .replace(/\s+/g, '-')
4640      .replace(/-+/g, '-')
4641      .replace(/^-+|-+$/g, '');
4642    const withoutAnd = original
4643      .replace(/\s*&\s*/g, '-')
4644      .replace(/[^a-z0-9\s-]/g, '')
4645      .replace(/\s+/g, '-')
4646      .replace(/-+/g, '-')
4647      .replace(/^-+|-+$/g, '');
4648    return unique([withAnd, withoutAnd]);
4649  }
4650
4651  function slugify(value) {
4652    return createSlugCandidates(value)[0] || '';
4653  }
4654
4655  function canonicalPath(value) {
4656    const raw = readString(value, '');
4657    if (!raw) return '';
4658    if (raw.startsWith('#')) return `${canonicalPathWithoutHash(window.location.href)}${raw}`;
4659    try {
4660      const url = new URL(raw, window.location.origin);
4661      let path = url.pathname || '/';
4662      path = path.replace(/^\/(?:home\/)?site\/[^/]+/i, '') || '/';
4663      path = path.replace(/\/+$/, '') || '/';
4664      return `${path.toLowerCase()}${url.hash || ''}`;
4665    } catch (error) {
4666      const [pathPart, hashPart = ''] = raw.split('#');
4667      let path = pathPart.split('?')[0].replace(/^\/(?:home\/)?site\/[^/]+/i, '').replace(/\/+$/, '') || '/';
4668      return `${path.toLowerCase()}${hashPart ? `#${hashPart}` : ''}`;
4669    }
4670  }
4671
4672  function canonicalPathWithoutHash(value) {
4673    return canonicalPath(value).split('#')[0] || '/';
4674  }
4675
4676  function getDudaBasePath() {
4677    try {
4678      const match = (window.location.pathname || '').match(/^\/site\/([0-9a-f]{32})(?:\/.*)?$/i);
4679      return match ? `/site/${match[1]}` : '';
4680    } catch (error) {
4681      return '';
4682    }
4683  }
4684
4685  function getDudaPreviewQuery() {
4686    try {
4687      const input = new URLSearchParams(window.location.search || '');
4688      const output = new URLSearchParams();
4689      ['showOriginal', 'preview', 'insitepreview', 'dm_device', 't', 'dm_try_mode', 'dm_checkSync'].forEach(key => {
4690        if (input.has(key)) output.set(key, input.get(key));
4691      });
4692      const value = output.toString();
4693      return value ? `?${value}` : '';
4694    } catch (error) {
4695      return '';
4696    }
4697  }
4698
4699  function buildRuntimeFallbackUrl(pathValue) {
4700    const raw = readString(pathValue, '/') || '/';
4701    if (/^(https?:)?\/\//i.test(raw) || raw.startsWith('#')) return raw;
4702    const hashIndex = raw.indexOf('#');
4703    const hash = hashIndex >= 0 ? raw.slice(hashIndex) : '';
4704    const withoutHash = hashIndex >= 0 ? raw.slice(0, hashIndex) : raw;
4705    const queryIndex = withoutHash.indexOf('?');
4706    const existingQuery = queryIndex >= 0 ? withoutHash.slice(queryIndex) : '';
4707    const pathOnly = queryIndex >= 0 ? withoutHash.slice(0, queryIndex) : withoutHash;
4708    const normalized = pathOnly.startsWith('/') ? pathOnly : `/${pathOnly}`;
4709    const basePath = getDudaBasePath();
4710    const prefix = basePath && !normalized.startsWith(basePath + '/') && normalized !== basePath ? basePath 
4710: '';
4711    const previewQuery = existingQuery ? '' : getDudaPreviewQuery();
4712    return `${prefix}${normalized}${existingQuery || previewQuery}${hash}`;
4713  }
4714
4715  function getLastPathSegment(value) {
4716    const path = canonicalPathWithoutHash(value);
4717    return path.split('/').filter(Boolean).pop() || '';
4718  }
4719
4720  function isExternalUrl(value) {
4721    const raw = readString(value, '');
4722    if (!raw || raw.startsWith('/') || raw.startsWith('#')) return false;
4723    try {
4724      return new URL(raw, window.location.origin).origin !== window.location.origin;
4725    } catch (error) {
4726      return false;
4727    }
4728  }
4729
4730  function stableStringify(value) {
4731    return JSON.stringify(value);
4732  }
4733
4734  function warn(code, message, context) {
4735    const warning = { code, message, context: context || null };
4736    local.latestBuildWarnings.push(warning);
4737    console.warn(`[NMW:${code}] ${message}`, context || '');
4738  }
4739
4740  // ---------------------------------------------------------------------------
4741  // Editor, preview, and Site Comments environment handling
4742  // ---------------------------------------------------------------------------
4743
4744  function isEditorCanvas() {
4745    try {
4746      const topHref = window.top && window.top.location ? window.top.location.href : '';
4747      return topHref.includes('/home/site/') && !topHref.includes('#preview');
4748    } catch (error) {
4749      return false;
4750    }
4751  }
4752
4753  /*
4754    Site Comments customer state may be written into the parent editor
4755    document after this widget has already rendered. Inspect the current
4756    document and every same-origin ancestor document each time the state
4757    is requested.
4758
4759    Do not depend on console output such as "Customer Detected". Console
4760    messages are not a reliable application-state API and may be emitted
4761    before this widget initializes.
4762  */
4763  function isSiteCommentsCustomerMode() {
4764    try {
4765      const documents = [];
4766      const seenDocuments = new Set();
4767
4768      function addDocument(candidateDocument) {
4769        if (
4770          !candidateDocument ||
4771          !candidateDocument.documentElement ||
4772          seenDocuments.has(candidateDocument)
4773        ) {
4774          return;
4775        }
4776
4777        seenDocuments.add(candidateDocument);
4778        documents.push(candidateDocument);
4779      }
4780
4781      addDocument(document);
4782
4783      /*
4784        Walk upward only while the parent documents are same-origin.
4785        Cross-origin access is safely ignored.
4786      */
4787      try {
4788        let currentWindow = window;
4789
4790        while (
4791          currentWindow.parent &&
4792          currentWindow.parent !== currentWindow
4793        ) {
4794          currentWindow = currentWindow.parent;
4795          addDocument(currentWindow.document);
4796        }
4797      } catch (error) {
4798        // A cross-origin ancestor cannot be inspected.
4799      }
4800
4801      const html = documents
4802        .map(candidateDocument => {
4803          try {
4804            return (
4805              candidateDocument.documentElement.innerHTML ||
4806              ''
4807            );
4808          } catch (error) {
4809            return '';
4810          }
4811        })
4812        .join('\n');
4813
4814      const hasSiteCommentsMarker =
4815        /SITE[_\s-]?COMMENTS/i.test(html);
4816
4817      const isCustomer =
4818        /["']?isCustomer["']?\s*:\s*true/i
4819          .test(html);
4820
4821      const isNonStaff =
4822        /["']?isStaffMember["']?\s*:\s*false/i
4823          .test(html);
4824
4825      const isStaff =
4826        /["']?isStaffMember["']?\s*:\s*true/i
4827          .test(html);
4828
4829      return (
4830        hasSiteCommentsMarker &&
4831        isCustomer &&
4832        isNonStaff &&
4833        !isStaff
4834      );
4835    } catch (error) {
4836      return false;
4837    }
4838  }
4839
4840  function navigateSiteComments(link, event) {
4841    if (!isSiteCommentsCustomerMode()) return false;
4842    const href = link.getAttribute('href') || '';
4843    const target = (link.getAttribute('target') || '_self').toLowerCase();
4844    if (!href || href.startsWith('#') || target === '_blank' || /^(?:mailto:|tel:|sms:|javascript:|data:|blob:)/i.test(href)) return false;
4845    try {
4846      const parsed = new URL(href, window.location.origin);
4847      let topOrigin = window.location.origin;
4848      let topPath = '';
4849      try {
4850        topOrigin = window.top.location.origin || topOrigin;
4851        topPath = window.top.location.pathname || '';
4852      } catch (error) {}
4853      // External web addresses must retain their original domain. Only rewrite
4854      // same-site links that need to escape the Site Comments preview iframe.
4855      if (parsed.origin !== window.location.origin && parsed.origin !== topOrigin) return false;
4856      const needsHomePrefix = parsed.pathname.startsWith('/site/') && topPath.startsWith('/home/site/');
4857      const destination = `${topOrigin}${needsHomePrefix ? '/home' : ''}${parsed.pathname}${parsed.hash || ''}`;
4858      event.preventDefault();
4859      event.stopPropagation();
4860      try {
4861        window.top.location.href = destination;
4862      } catch (error) {
4863        window.location.href = destination;
4864      }
4865      return true;
4866    } catch (error) {
4867      return false;
4868    }
4869  }
4870
4871  // ---------------------------------------------------------------------------
4872  // Same-page anchor navigation
4873  // ---------------------------------------------------------------------------
4874
4875  /*
4876    Duda's native navigation can contain links to anchors on the current page.
4877    When the hash changes for the first time, normal browser navigation usually
4878    scrolls correctly. Once the visitor is already on that page, however, Duda's
4879    copied/native link behavior is not guaranteed to re-scroll for a second hash
4880    selection (or for the same hash selected again).
4881
4882    NMW therefore owns same-page anchor clicks. Cross-page anchor links continue
4883    to use normal navigation so Duda can load the destination page first.
4884  */
4885  function decodeAnchorHash(hash) {
4886    const raw = readString(hash, '').replace(/^#/, '');
4887    if (!raw) return '';
4888    try {
4889      return decodeURIComponent(raw);
4890    } catch (error) {
4891      return raw;
4892    }
4893  }
4894
4895  function findAnchorTarget(hash) {
4896    const decoded = decodeAnchorHash(hash);
4897    if (!decoded) return null;
4898
4899    // IDs are the modern/default anchor mechanism. Check the decoded value first
4900    // and the literal hash value second in case an authored ID contains % signs.
4901    let target = document.getElementById(decoded);
4902    if (!target) {
4903      const literal = readString(hash, '').replace(/^#/, '');
4904      if (literal && literal !== decoded) target = document.getElementById(literal);
4905    }
4906
4907    // Legacy named anchors are still supported by browsers and may exist on
4908    // older Duda sites.
4909    if (!target) {
4910      const named = document.getElementsByName(decoded);
4911      if (named && named.length) target = named[0];
4912    }
4913
4914    return target || null;
4915  }
4916
4917  function collectTopOverlayCandidates() {
4918    const candidates = new Set();
4919
4920    // Common Duda/site header selectors plus generic semantic header selectors.
4921    document.querySelectorAll([
4922      'header',
4923      '[role="banner"]',
4924      '.dmHeader',
4925      '.dmHeaderContainer',
4926      '[data-element-type="header"]',
4927      '[class*="sticky"]',
4928      '[class*="fixed"]'
4929    ].join(','))
4930      .forEach(node => candidates.add(node));
4931
4932    // Also inspect what is physically covering the top-center of the viewport.
4933    // This catches custom sticky/fixed wrappers whose class names are unknown.
4934    try {
4935      const viewportWidth = document.documentElement.clientWidth || window.innerWidth || 0;
4936      const x = Math.max(1, Math.min(viewportWidth - 1, Math.round(viewportWidth / 2)));
4937      [1, 8, 16, 32].forEach(y => {
4938        const stack = typeof document.elementsFromPoint === 'function'
4939          ? document.elementsFromPoint(x, y)
4940          : [];
4941
4942        stack.forEach(node => {
4943          let current = node;
4944          while (current && current !== document.documentElement) {
4945            candidates.add(current);
4946            current = current.parentElement;
4947          }
4948        });
4949      });
4950    } catch (error) {}
4951
4952    return candidates;
4953  }
4954
4955  function getAnchorScrollOffset(target) {
4956    const viewportWidth = document.documentElement.clientWidth || window.innerWidth || 0;
4957    const viewportHeight = document.documentElement.clientHeight || window.innerHeight || 0;
4958    let overlayBottom = 0;
4959
4960    collectTopOverlayCandidates().forEach(node => {
4961      if (!node || node === target || node.contains?.(target)) return;
4962
4963      let style;
4964      let rect;
4965      try {
4966        style = window.getComputedStyle(node);
4967        rect = node.getBoundingClientRect();
4968      } catch (error) {
4969        return;
4970      }
4971
4972      if (!style || !rect) return;
4973      if (!['fixed', 'sticky'].includes(style.position)) return;
4974      if (style.display === 'none' || style.visibility === 'hidden') return;
4975      if (Number(style.opacity) === 0) return;
4976      if (rect.height <= 0 || rect.width <= 0) return;
4977      if (rect.bottom <= 0 || rect.top > 12) return;
4978
4979      // Ignore small floating controls at the top edge; a navigation/header
4980      // overlay should span a meaningful portion of the viewport.
4981      if (viewportWidth > 0 && rect.width < viewportWidth * 0.30) return;
4982
4983      // Ignore pathological full-screen overlays. Those are not header offsets.
4984      if (viewportHeight > 0 && rect.height > viewportHeight * 0.60) return;
4985
4986      overlayBottom = Math.max(overlayBottom, Math.max(0, rect.bottom));
4987    });
4988
4989    let scrollMarginTop = 0;
4990    try {
4991      const targetStyle = window.getComputedStyle(target);
4992      scrollMarginTop = parseFloat(targetStyle.scrollMarginTop) || 0;
4993    } catch (error) {}
4994
4995    // Respect an authored scroll-margin if it is larger than the detected header.
4996    // Add a small breathing room so headings do not sit flush against the header.
4997    return Math.max(0, Math.max(overlayBottom, scrollMarginTop) + 8);
4998  }
4999
5000  function scrollAnchorTargetIntoPlace(target) {
5001    if (!target || !document.documentElement.contains(target)) return false;
5002
5003    const rect = target.getBoundingClientRect();
5004    const currentScroll = window.pageYOffset
5005      || document.documentElement.scrollTop
5006      || document.body.scrollTop
5007      || 0;
5008    const offset = getAnchorScrollOffset(target);
5009    const absoluteTargetTop = currentScroll + rect.top;
5010    const scrollingElement = document.scrollingElement || document.documentElement;
5011    const maxScroll = Math.max(0, scrollingElement.scrollHeight - window.innerHeight);
5012    const desiredTop = Math.max(0, Math.min(maxScroll, absoluteTargetTop - offset));
5013
5014    try {
5015      window.scrollTo({ top: desiredTop, left: window.pageXOffset || 0, behavior: 'auto' });
5016    } catch (error) {
5017      window.scrollTo(window.pageXOffset || 0, desiredTop);
5018    }
5019
5020    return true;
5021  }
5022
5023  function scheduleAnchorCorrection(target) {
5024    // First pass occurs immediately after the click. A second pass happens after
5025    // menu close animations/layout changes so Mirror/mobile menus cannot shift the
5026    // target after it was measured. A final lightweight pass handles late sticky
5027    // header changes that appear only after scrolling.
5028    const delays = [0, 260, 520];
5029
5030    delays.forEach(delay => {
5031      const timer = setTimeout(() => {
5032        local.anchorTimers.delete(timer);
5033        if (local.cleaned || !document.documentElement.contains(target)) return;
5034        scrollAnchorTargetIntoPlace(target);
5035      }, delay);
5036
5037      local.anchorTimers.add(timer);
5038    });
5039  }
5040
5041  function samePageAnchorInfo(link) {
5042    if (!link) return null;
5043
5044    const href = readString(link.getAttribute('href'), '');
5045    const target = readString(link.getAttribute('target'), '_self').toLowerCase();
5046
5047    if (
5048      !href ||
5049      target === '_blank' ||
5050      link.hasAttribute('download') ||
5051      /^(?:mailto:|tel:|sms:|javascript:|data:|blob:)/i.test(href)
5052    ) {
5053      return null;
5054    }
5055
5056    let destination;
5057    try {
5058      destination = new URL(href, window.location.href);
5059    } catch (error) {
5060      return null;
5061    }
5062
5063    if (destination.origin !== window.location.origin || !destination.hash) return null;
5064
5065    const destinationPath = canonicalPathWithoutHash(destination.href);
5066    const currentPath = canonicalPathWithoutHash(window.location.href);
5067    if (!destinationPath || destinationPath !== currentPath) return null;
5068
5069    const anchorTarget = findAnchorTarget(destination.hash);
5070    if (!anchorTarget) return null;
5071
5072    return {
5073      destination,
5074      hash: destination.hash,
5075      target: anchorTarget
5076    };
5077  }
5078
5079  function handleSamePageAnchorClick(link, event) {
5080    // Preserve normal browser behavior for modified clicks such as Ctrl/Cmd-click.
5081    if (
5082      event &&
5083      (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || (typeof event.button === 'number' && event.button !== 0))
5084    ) {
5085      return false;
5086    }
5087
5088    const info = samePageAnchorInfo(link);
5089    if (!info) return false;
5090
5091    event.preventDefault();
5092    event.stopPropagation();
5093
5094    closeAllMenus(root, false);
5095
5096    // pushState changes the URL without triggering the browser's unreliable
5097    // one-time hash scrolling. Selecting the same anchor again still scrolls
5098    // because the explicit scroll routine below always runs.
5099    try {
5100      if (window.location.hash !== info.hash) {
5101        history.pushState(history.state, '', info.destination.href);
5102      } else {
5103        refreshActiveStates(root);
5104      }
5105    } catch (error) {
5106      // If history manipulation is unavailable, keep the current URL and still
5107      // perform the requested scroll.
5108      refreshActiveStates(root);
5109    }
5110
5111    scheduleAnchorCorrection(info.target);
5112    return true;
5113  }
5114
5115  // ---------------------------------------------------------------------------
5116  // Configuration model
5117  // ---------------------------------------------------------------------------
5118
5119  function getModeConfig(mode) {
5120    return designConfigRoot(mode);
5121  }
5122
5123  function behaviorValue(mode, names, fallback) {
5124    const list = Array.isArray(names) ? names : [names];
5125    const fullFound = configValueFrom(fullConfigRoot(), ...list);
5126
5127    if (mode === 'mirror' && mirrorUsesSeparateStyling()) {
5128      const mirrorFound = configValueFrom(designConfigRoot('mirror'), ...list);
5129      if (mirrorFound !== undefined) return mirrorFound;
5130    }
5131
5132    return fullFound === undefined ? fallback : fullFound;
5133  }
5134
5135  function getBehaviorConfig(mode) {
5136    const fullHover = readBoolean(behaviorValue('full', 'openMenusOnHover', false), false);
5137    const mirrorOverride = mode === 'mirror'
5138      ? configValue('mirrorOpenMenusOnHover')
5139      : undefined;
5140
5141    return {
5142      hoverEnabled: mode === 'mirror' && mirrorOverride !== undefined && mirrorOverride !== null && mirrorOverride !== ''
5143        ? readBoolean(mirrorOverride, fullHover)
5144        : fullHover,
5145      animationsEnabled: readBoolean(behaviorValue(mode, ['animationsEnabled', 'animatedEffectsEnabled'], true), true),
5146      animateTopNav: readBoolean(behaviorValue(mode, 'animateTopNav', true), true),
5147      animateSubNav: readBoolean(behaviorValue(mode, 'animateSubNav', true), true),
5148      animateSubSubNav: readBoolean(behaviorValue(mode, 'animateSubSubNav', true), true),
5149      animateMegaMenu: readBoolean(behaviorValue(mode, 'animateMegaMenu', true), true),
5150      animateMegaFamilies: readBoolean(behaviorValue(mode, 'animateMegaFamilies', true), true),
5151      animateMirror: readBoolean(behaviorValue(mode, 'animateMirror', true), true),
5152      animateTopChevrons: readBoolean(behaviorValue(mode, 'animateTopChevrons', true), true),
5153      animateMegaChevrons: readBoolean(behaviorValue(mode, 'animateMegaChevrons', true), true),
5154      animateHoverEffects: readBoolean(behaviorValue(mode, 'animateHoverEffects', true), true)
5155    };
5156  }
5157
5158  function getMegaGroupObject(groupNumber) {
5159    const safeGroup = Math.min(4, Math.max(1, Math.round(Number(groupNumber) || 1)));
5160    const key = `mega${safeGroup}`;
5161    const rootConfig = fullConfigRoot();
5162    const direct = rootConfig && typeof rootConfig[key] === 'object' ? rootConfig[key] : null;
5163    return direct || configValueFrom(rootConfig, key) || {};
5164  }
5165
5166  function getMegaLabel(groupNumber) {
5167    const safeGroup = Math.min(4, Math.max(1, Math.round(Number(groupNumber) || 1)));
5168    const key = `mega${safeGroup}`;
5169    const rootConfig = fullConfigRoot();
5170    const group = getMegaGroupObject(safeGroup);
5171    const configured = group.label
5172      ?? configValueFrom(rootConfig, `${key}Label`, `megaLabel${safeGroup}`, `mega${safeGroup}Name`)
5173      ?? (safeGroup === 1 ? configValueFrom(rootConfig, 'servicesLabel') : undefined);
5174
5175    return readString(configured, `Mega ${safeGroup}`) || `Mega ${safeGroup}`;
5176  }
5177
5178  function getMegaExpandAll(groupNumber) {
5179    const safeGroup = Math.min(4, Math.max(1, Math.round(Number(groupNumber) || 1)));
5180    const key = `mega${safeGroup}`;
5181    const rootConfig = fullConfigRoot();
5182    const group = getMegaGroupObject(safeGroup);
5183    const candidates = [
5184      group.expandAll,
5185      group.childrenExpanded,
5186      group.childrenOpen,
5187      group.openChildren,
5188      configValueFrom(rootConfig,
5189        `${key}ExpandAll`, `${key}Expanded`, `${key}ChildrenExpanded`,
5190        `${key}ChildrenOpen`, `${key}OpenChildren`,
5191        `expandMega${safeGroup}`, `expandedMega${safeGroup}`
5192      )
5193    ];
5194
5195    if (safeGroup === 1) candidates.push(configValueFrom(rootConfig, 'expandedServices'));
5196    const configured = candidates.find(value => value !== undefined && value !== null && value !== '');
5197    return readBoolean(configured, false);
5198  }
5199
5200  function getMegaConfig(groupNumber) {
5201    const safeGroup = Math.min(4, Math.max(1, Math.round(Number(groupNumber) || 1)));
5202    const key = `mega${safeGroup}`;
5203    const rootConfig = fullConfigRoot();
5204    const group = getMegaGroupObject(safeGroup);
5205
5206    const columnsValue = group.columns ?? group.columnCount ?? configValueFrom(
5207      rootConfig,
5208      `${key}Columns`, `${key}ColumnCount`, `megaColumns${safeGroup}`,
5209      safeGroup === 1 ? 'compactColumnCount' : '__unused__',
5210      'megaColumns'
5211    );
5212    const layoutValue = group.layout ?? group.layoutMode ?? configValueFrom(
5213      rootConfig,
5214      `${key}Layout`, `${key}LayoutMode`, `megaLayout${safeGroup}`, 'megaLayoutMode'
5215    );
5216    const maxWidthValue = group.maxWidth ?? group.menuMaxWidth ?? configValueFrom(
5217      rootConfig,
5218      `${key}MaxWidth`, `${key}MenuMaxWidth`, `megaMaxWidth${safeGroup}`, 'megaMenuMaxWidth'
5219    );
5220    const alphabetizeValue = group.alphabetize ?? configValueFrom(
5221      rootConfig,
5222      `${key}Alphabetize`, `alphabetizeMega${safeGroup}`, 'alphabetizeMega'
5223    );
5224    const layout = readString(layoutValue, 'grid').toLowerCase();
5225
5226    return {
5227      label: getMegaLabel(safeGroup),
5228      columns: Math.round(readNumber(columnsValue, 3, 1, 6)),
5229      layout: ['grid', 'masonry'].includes(layout) ? layout : 'grid',
5230      expandAll: getMegaExpandAll(safeGroup),
5231      maxWidth: readNumber(maxWidthValue, 1600, 320, 3000),
5232      alphabetize: readBoolean(alphabetizeValue, false)
5233    };
5234  }
5235
5236  function updateMegaIndicators(items) {
5237    const groups = new Set();
5238
5239    function walk(list) {
5240      (list || []).forEach(item => {
5241        if (item && item.megaGroup >= 1 && item.megaGroup <= 4) groups.add(item.megaGroup);
5242        walk(item && item.children);
5243      });
5244    }
5245
5246    walk(items);
5247    const used = [...groups].sort((a, b) => a - b);
5248    const labels = Object.fromEntries([1, 2, 3, 4].map(number => [number, getMegaLabel(number)]));
5249
5250    window.NMW.megaGroupsUsed = used;
5251    window.NMW.megaGroupCount = used.length;
5252    window.NMW.megaLabels = labels;
5253
5254    root.dataset.nmwMegaCount = String(used.length);
5255    root.dataset.nmwMegaGroups = used.map(number => `mega${number}`).join(',');
5256    root.dataset.nmwMegaLabels = used.map(number => labels[number]).join(',');
5257
5258    [1, 2, 3, 4].forEach(number => {
5259      root.dataset[`nmwMega${number}Label`] = labels[number];
5260    });
5261  }
5262
5263  function applyDesignSettings(mode) {
5264    const behavior = getBehaviorConfig(mode);
5265    const universalColors = readBoolean(designValue(mode, 'useUniversalColors', false), false);
5266    // A separate background toggle is required because Duda color controls often
5267    // save individual default values (for example #ffffff). Without this switch,
5268    // those saved defaults always override universalBackgroundColor.
5269    const universalBackgrounds = readBoolean(
5270      designValue(mode, ['useUniversalBackgrounds', 'useUniversalBackground'], true),
5271      true
5272    );
5273    const universalTypography = readBoolean(designValue(mode, 'useUniversalTypography', true), true);
5274    const universalChevrons = readBoolean(designValue(mode, 'useUniversalChevronSettings', false), false);
5275
5276    root.dataset.nmwUniversalColors = universalColors ? 'true' : 'false';
5277    root.dataset.nmwUniversalBackgrounds = universalBackgrounds ? 'true' : 'false';
5278    root.dataset.nmwUniversalTypography = universalTypography ? 'true' : 'false';
5279    root.dataset.nmwUniversalChevrons = universalChevrons ? 'true' : 'false';
5280    root.dataset.nmwAnimations = behavior.animationsEnabled ? 'on' : 'off';
5281    root.dataset.nmwAnimateTop = behavior.animateTopNav ? 'on' : 'off';
5282    root.dataset.nmwAnimateSub = behavior.animateSubNav ? 'on' : 'off';
5283    root.dataset.nmwAnimateSubsub = behavior.animateSubSubNav ? 'on' : 'off';
5284    root.dataset.nmwAnimateMega = behavior.animateMegaMenu ? 'on' : 'off';
5285    root.dataset.nmwAnimateMegaFamilies = behavior.animateMegaFamilies ? 'on' : 'off';
5286    root.dataset.nmwAnimateMirror = behavior.animateMirror ? 'on' : 'off';
5287    root.dataset.nmwAnimateTopChevrons = behavior.animateTopChevrons ? 'on' : 'off';
5288    root.dataset.nmwAnimateMegaChevrons = behavior.animateMegaChevrons ? 'on' : 'off';
5289    root.dataset.nmwAnimateHover = behavior.animateHoverEffects ? 'on' : 'off';
5290    root.dataset.nmwHoverEnabled = behavior.hoverEnabled ? 'true' : 'false';
5291    root.dataset.nmwAlignment = mode === 'mirror'
5292      ? readString(designValue(mode, 'mirrorAlignment', 'start'), 'start')
5293      : readString(designValue(mode, 'navAlignment', 'center'), 'center');
5294    root.dataset.nmwPreset = readString(
5295      designValue(
5296        mode,
5297        mode === 'mirror'
5298          ? ['layoutPreset', 'mirrorLayoutPreset', 'mirrorPreset']
5299          : ['layoutPreset', 'desktopPresetValue', 'desktopPreset'],
5300        mode === 'mirror' ? 'vertical-standard' : 'classic'
5301      ),
5302      mode === 'mirror' ? 'vertical-standard' : 'classic'
5303    ).toLowerCase();
5304    root.dataset.nmwSeparateMirrorStyling = mode === 'mirror' && mirrorUsesSeparateStyling() ? 'true' : 'false';
5305    root.classList.toggle('nmw-capitalize', readBoolean(behaviorValue(mode, 'capitalizeNav', false), false));
5306    root.dataset.nmwUniversalUnderline = readBoolean(
5307      designValue(
5308        mode,
5309        ['universalUnderline', 'universalAutomaticUnderline', 'underlineLinksOnHover'],
5310        false
5311      ),
5312      false
5313    ) ? 'true' : 'false';
5314
5315    const universalChevronStyle = normalizeChevronStyle(
5316      designValue(mode, ['universalChevronStyle', 'universalChevronType', 'chevronStyle', 'chevronType'], 'filled'),
5317      'filled'
5318    );
5319    const topChevronStyle = universalChevrons
5320      ? universalChevronStyle
5321      : normalizeChevronStyle(designValue(mode, ['topChevronStyle', 'topChevronType', 'chevronType'], universalChevronStyle), universalChevronStyle);
5322    const megaChevronStyle = universalChevrons
5323      ? universalChevronStyle
5324      : normalizeChevronStyle(designValue(mode, ['megaChevronStyle', 'megaChevronType'], universalChevronStyle), universalChevronStyle);
5325    root.dataset.nmwTopChevronStyle = topChevronStyle;
5326    root.dataset.nmwMegaChevronStyle = megaChevronStyle;
5327
5328    const universalText = cssValue(designValue(mode, 'universalTextColor', '#111111'), '#111111');
5329    const universalBg = cssValue(designValue(mode, 'universalBackgroundColor', '#ffffff'), '#ffffff');
5330
5331    // The universal background controls every dropdown surface, including
5332    // Mega family/cards. The top navigation background remains independent.
5333    const subBackground = universalBackgrounds
5334      ? universalBg
5335      : cssValue(designValue(mode, 'subNavBackgroundColor', '#ffffff'), '#ffffff');
5336    const subSubBackground = universalBackgrounds
5337      ? universalBg
5338      : cssValue(designValue(mode, 'subSubNavBackgroundColor', '#ffffff'), '#ffffff');
5339    const megaBackground = universalBackgrounds
5340      ? universalBg
5341      : cssValue(designValue(mode, 'megaMenuBackgroundColor', '#ffffff'), '#ffffff');
5342    const megaFamilyBackground = universalBackgrounds
5343      ? universalBg
5344      : cssValue(designValue(mode, 'megaFamilyBackgroundColor', 'transparent'), 'transparent');
5345
5346    const universalFontSource = designValue(
5347      mode,
5348      ['universalFontFamily', 'universalFont', 'universalTypographyFont'],
5349      'inherit'
5350    );
5351    const universalSizeSource = designValue(
5352      mode,
5353      ['universalFontSize', 'universalTextSize', 'universalSize', 'universalFontFamily', 'universalFont'],
5354      '16px'
5355    );
5356    const universalWeightSource = designValue(
5357      mode,
5358      ['universalFontWeight', 'universalWeight', 'universalFontFamily', 'universalFont'],
5359      '500'
5360    );
5361
5362    const universalFont = fontFamilyValue(universalFontSource, 'inherit');
5363    const universalSize = fontSizeValue(universalSizeSource, '16px');
5364    const universalWeight = fontWeightValue(universalWeightSource, '500');
5365    const universalChevronColor = cssValue(designValue(mode, 'universalChevronColor', universalText), universalText);
5366    const universalChevronSize = cssLength(designValue(mode, ['universalChevronSize', 'chevronSize'], '10px'), '10px');
5367    const universalChevronWeight = cssLength(designValue(mode, ['universalChevronWeight', 'chevronWeight'], '1.5px'), '1.5px');
5368
5369    const variables = {
5370      '--nmw-universal-text': universalText,
5371      '--nmw-top-text': universalColors ? universalText : cssValue(designValue(mode, 'topTextColor', universalText), universalText),
5372      '--nmw-sub-text': universalColors ? universalText : cssValue(designValue(mode, 'subTextColor', universalText), universalText),
5373      '--nmw-subsub-text': universalColors ? universalText : cssValue(designValue(mode, 'subSubTextColor', universalText), universalText),
5374      '--nmw-mega-parent-text': universalColors ? universalText : cssValue(designValue(mode, 'megaParentTextColor', universalText), universalText),
5375      '--nmw-mega-child-text': universalColors ? universalText : cssValue(designValue(mode, 'megaChildTextColor', universalText), universalText),
5376      '--nmw-mega-grandchild-text': universalColors ? universalText : cssValue(designValue(mode, 'megaGrandchildTextColor', universalText), universalText),
5377      '--nmw-top-bg': cssValue(designValue(mode, 'topNavBackgroun
5377dColor', 'transparent'), 'transparent'),
5378      '--nmw-universal-bg': universalBg,
5379      '--nmw-sub-bg': subBackground,
5380      '--nmw-subsub-bg': subSubBackground,
5381      '--nmw-mega-bg': megaBackground,
5382      '--nmw-mega-family-bg': megaFamilyBackground,
5383      '--nmw-hover-text': cssValue(designValue(mode, ['hoverTextColor', 'globalHoverTextColor'], 'currentColor'), 'currentColor'),
5384      '--nmw-hover-bg': cssValue(designValue(mode, ['hoverBackgroundColor', 'globalSubNavHoverBg'], 'transparent'), 'transparent'),
5385      '--nmw-active-text': cssValue(designValue(mode, 'activeTextColor', 'currentColor'), 'currentColor'),
5386      '--nmw-universal-font': universalFont,
5387      '--nmw-universal-size': universalSize,
5388      '--nmw-universal-weight': universalWeight,
5389      '--nmw-top-font': universalTypography ? universalFont : fontFamilyValue(designValue(mode, ['topFontFamily', 'topFont'], universalFont), universalFont),
5390      '--nmw-top-size': universalTypography ? universalSize : fontSizeValue(designValue(mode, ['topFontSize', 'topTextSize', 'topFontFamily', 'topFont'], universalSize), universalSize),
5391      '--nmw-top-weight': universalTypography ? universalWeight : fontWeightValue(designValue(mode, ['topFontWeight', 'topWeight', 'topFontFamily', 'topFont'], universalWeight), universalWeight),
5392      '--nmw-sub-font': universalTypography ? universalFont : fontFamilyValue(designValue(mode, ['subFontFamily', 'subFont'], universalFont), universalFont),
5393      '--nmw-sub-size': universalTypography ? universalSize : fontSizeValue(designValue(mode, ['subFontSize', 'subTextSize', 'subFontFamily', 'subFont'], universalSize), universalSize),
5394      '--nmw-sub-weight': universalTypography ? universalWeight : fontWeightValue(designValue(mode, ['subFontWeight', 'subWeight', 'subFontFamily', 'subFont'], universalWeight), universalWeight),
5395      '--nmw-subsub-font': universalTypography ? universalFont : fontFamilyValue(designValue(mode, ['subSubFontFamily', 'subSubFont'], universalFont), universalFont),
5396      '--nmw-subsub-size': universalTypography ? universalSize : fontSizeValue(designValue(mode, ['subSubFontSize', 'subSubTextSize', 'subSubFontFamily', 'subSubFont'], universalSize), universalSize),
5397      '--nmw-subsub-weight': universalTypography ? universalWeight : fontWeightValue(designValue(mode, ['subSubFontWeight', 'subSubWeight', 'subSubFontFamily', 'subSubFont'], universalWeight), universalWeight),
5398      '--nmw-mega-parent-font': universalTypography ? universalFont : fontFamilyValue(designValue(mode, ['megaParentFontFamily', 'megaParentFont'], universalFont), universalFont),
5399      '--nmw-mega-parent-size': universalTypography ? universalSize : fontSizeValue(designValue(mode, ['megaParentFontSize', 'megaParentTextSize', 'megaParentFontFamily', 'megaParentFont'], universalSize), universalSize),
5400      '--nmw-mega-parent-weight': universalTypography ? universalWeight : fontWeightValue(designValue(mode, ['megaParentFontWeight', 'megaParentWeight', 'megaParentFontFamily', 'megaParentFont'], universalWeight), universalWeight),
5401      '--nmw-mega-child-font': universalTypography ? universalFont : fontFamilyValue(designValue(mode, ['megaChildFontFamily', 'megaChildFont'], universalFont), universalFont),
5402      '--nmw-mega-child-size': universalTypography ? universalSize : fontSizeValue(designValue(mode, ['megaChildFontSize', 'megaChildTextSize', 'megaChildFontFamily', 'megaChildFont'], universalSize), universalSize),
5403      '--nmw-mega-child-weight': universalTypography ? universalWeight : fontWeightValue(designValue(mode, ['megaChildFontWeight', 'megaChildWeight', 'megaChildFontFamily', 'megaChildFont'], universalWeight), universalWeight),
5404      '--nmw-mega-grandchild-font': universalTypography ? universalFont : fontFamilyValue(designValue(mode, ['megaGrandchildFontFamily', 'megaGrandchildFont'], universalFont), universalFont),
5405      '--nmw-mega-grandchild-size': universalTypography ? universalSize : fontSizeValue(designValue(mode, ['megaGrandchildFontSize', 'megaGrandchildTextSize', 'megaGrandchildFontFamily', 'megaGrandchildFont'], universalSize), universalSize),
5406      '--nmw-mega-grandchild-weight': universalTypography ? universalWeight : fontWeightV
5406alue(designValue(mode, ['megaGrandchildFontWeight', 'megaGrandchildWeight', 'megaGrandchildFontFamily', 'megaGrandchildFont'], universalWeight), universalWeight),
5407      '--nmw-top-chevron-color': universalChevrons ? universalChevronColor : cssValue(designValue(mode, 'topChevronColor', universalChevronColor), universalChevronColor),
5408      '--nmw-mega-chevron-color': universalChevrons ? universalChevronColor : cssValue(designValue(mode, 'megaChevronColor', universalChevronColor), universalChevronColor),
5409      '--nmw-chevron-size': universalChevronSize,
5410      '--nmw-chevron-weight': universalChevronWeight,
5411      '--nmw-top-gap': `${readNumber(designValue(mode, 'topLevelGap', 32), 32, 0, 120)}px`,
5412      // Controls the spacing between a synthetic Mega 1–4 label and its chevron.
5413      // Duda Design Panel field: megaTriggerChevronGap
5414      '--nmw-mega-trigger-chevron-gap': `${readNumber(designValue(mode, 'megaTriggerChevronGap', 12), 12, 0, 60)}px`,
5415      '--nmw-mirror-gap': `${readNumber(designValue(mode, 'mirrorItemGap', 0), 0, 0, 80)}px`
5416    };
5417
5418    Object.entries(variables).forEach(([name, value]) => root.style.setProperty(name, value));
5419  }
5420
5421  // ---------------------------------------------------------------------------
5422  // Shared state and root status
5423  // ---------------------------------------------------------------------------
5424
5425  function updateRootStatus() {
5426    const state = window.NMW;
5427    root.dataset.nmwLoading = state.loading ? 'true' : 'false';
5428    root.dataset.nmwReady = state.ready ? 'true' : 'false';
5429    root.dataset.nmwError = state.error ? 'true' : 'false';
5430    root.dataset.nmwEmpty = Array.isArray(state.data) && state.data.length === 0 ? 'true' : 'false';
5431    updateLoadingView();
5432  }
5433
5434  function dispatchStateChanged() {
5435    window.dispatchEvent(new CustomEvent('nmw:state-changed', {
5436      detail: { loading: window.NMW.loading, ready: window.NMW.ready, error: window.NMW.error }
5437    }));
5438  }
5439
5440  function dispatchDataChanged() {
5441    window.dispatchEvent(new CustomEvent('nmw:data-changed', {
5442      detail: { version: window.NMW.version }
5443    }));
5444  }
5445
5446  function updateLoadingView() {
5447    if (!loadingHost) return;
5448    const initialLoading = window.NMW.loading && !window.NMW.ready;
5449    loadingHost.hidden = !initialLoading;
5450    if (!initialLoading) {
5451      loadingHost.innerHTML = '';
5452      return;
5453    }
5454    const style = readString(configValueFrom(fullConfigRoot(), 'loadingAnimationStyle') ?? 'shimmer', 'shimmer').toLowerCase();
5455    const count = Math.round(readNumber(configValueFrom(fullConfigRoot(), 'loadingItemCount'), 4, 1, 12));
5456    if (style === 'spinner') {
5457      loadingHost.innerHTML = '<span class="nmw-loading-spinner" aria-hidden="true"></span>';
5458      return;
5459    }
5460    loadingHost.innerHTML = `<div class="nmw-loading-items nmw-loading-${escapeHtml(style)}">${Array.from({ length: count }, (_, index) => `<span class="nmw-loading-item" style="--nmw-load-width:${55 + ((index * 17) % 40)}px"></span>`).join('')}</div>`;
5461  }
5462
5463  function showEditorMessage(message, duplicate = false) {
5464    if (!renderHost) return;
5465    if (!isEditorCanvas()) {
5466      renderHost.innerHTML = '';
5467      return;
5468    }
5469    renderHost.innerHTML = `<div class="nmw-editor-warning${duplicate ? ' nmw-editor-warning-duplicate' : ''}">${escapeHtml(message)}</div>`;
5470  }
5471
5472  // ---------------------------------------------------------------------------
5473  // Ownership: exactly one Full and one Mirror are expected on a page
5474  // ---------------------------------------------------------------------------
5475
5476  function claimOwnership() {
5477    const key = widgetMode === 'full' ? 'ownerId' : 'mirrorOwnerId';
5478    const existingId = window.NMW[key];
5479
5480    // Duda can rerun widget JavaScript against the same existing DOM root when
5481    // code or panel settings change. In that case the old instance ID remains
5482    // on this same root even though its previous script lifecycle is obsolete.
5483    // Treat that as a replacement, not as a duplicate widget.
5484    if (existingId && existingId !== instanceId) {
5485      const existingRoot = Array.from(document.querySelectorAll('[data-nm
5485w-instance]'))
5486        .find(node => node.dataset.nmwInstance === existingId);
5487
5488      if (existingRoot && existingRoot !== root && document.documentElement.contains(existingRoot)) {
5489        warn('DUPLICATE_INSTANCE', `A second ${widgetMode} widget was found. Only one is allowed.`);
5490        showEditorMessage(`Duplicate ${widgetMode} navigation widget. Only one instance is allowed.`, true);
5491        return false;
5492      }
5493
5494      // The recorded owner is stale or belongs to this same widget root.
5495      // Clear it so the newly executed script can initialize normally.
5496      window.NMW[key] = null;
5497    }
5498
5499    window.NMW[key] = instanceId;
5500    root.dataset.nmwInstance = instanceId;
5501    return true;
5502  }
5503
5504  function releaseOwnership() {
5505    const key = widgetMode === 'full' ? 'ownerId' : 'mirrorOwnerId';
5506    if (window.NMW[key] === instanceId) window.NMW[key] = null;
5507  }
5508
5509  // ---------------------------------------------------------------------------
5510  // Duda collection fetching
5511  // ---------------------------------------------------------------------------
5512
5513  let collectionsApiPromise = null;
5514
5515  /*
5516    COLLECTION REQUEST CACHE
5517
5518    A single Full-widget lifecycle can rebuild several times because Duda changes
5519    native-nav attributes or the hidden manual-item source. Those rebuilds do not
5520    mean the underlying collections changed, so repeating the same collection API
5521    requests only adds latency.
5522
5523    Cache the request Promise per collection for this JavaScript instance. Duda
5524    reruns the widget JavaScript when its panel/configuration lifecycle is replaced,
5525    which naturally creates a fresh cache. Failed requests are removed immediately
5526    so a later rebuild can retry. The debug helper can explicitly force a refresh.
5527  */
5528  const collectionRequestCache = new Map();
5529
5530  // Load Duda's collection API once and reuse the same API object for every
5531  // collection request. The original working widget followed this pattern.
5532  function getCollectionsApi() {
5533    if (!collectionsApiPromise) {
5534      collectionsApiPromise = dmAPI.loadCollectionsAPI();
5535    }
5536    return collectionsApiPromise;
5537  }
5538
5539  // Preserve both layers of a Duda collection result. Dynamic collection
5540  // identifiers such as item, slug, uuid, and page_item_url may live on the
5541  // outer entry while editable collection fields live inside entry.data.
5542  // Flattening only entry.data discards the slug metadata and causes every
5543  // Navigation Tree validation check to report a false missing-slug warning.
5544  function normalizeFetchedCollectionEntry(entry) {
5545    if (!entry || typeof entry !== 'object') return entry;
5546    const nestedData = entry.data;
5547    if (!nestedData || typeof nestedData !== 'object' || Array.isArray(nestedData)) {
5548      return entry;
5549    }
5550
5551    // Nested collection fields are copied first and outer Duda metadata wins
5552    // when the same key exists at both levels. Keep data itself as well so any
5553    // future code can still inspect the original nested object.
5554    return { ...nestedData, ...entry };
5555  }
5556
5557  async function fetchCollection(name, options = {}) {
5558    const forceRefresh = Boolean(options && options.forceRefresh);
5559
5560    if (!forceRefresh && collectionRequestCache.has(name)) {
5561      return collectionRequestCache.get(name);
5562    }
5563
5564    const request = (async () => {
5565      const api = await getCollectionsApi();
5566
5567      // Duda allows a maximum page size of 100. 02 CATEGORY intentionally
5568      // stays capped at 100 rows. Other collections may load a second page,
5569      // giving them a maximum of 200 rows without an unnecessary extra request.
5570      const maxRows = name === '02 CATEGORY' ? 100 : 200;
5571
5572      const firstResult = await api
5573        .data(name)
5574        .pageSize(100)
5575        .pageNumber(0)
5576        .get();
5577
5578      const firstRows = firstResult && Array.isArray(firstResult.values)
5579        ? firstResult.values
5580        : [];
5581
5582      let rows = [...firstRows];
5583
5584      if (maxRows > 100 && firstRows.length === 100) {
5585        const secondResult = await api
5586          .data(name)
5587          .pageSize(100)
5588          .pageNumber(1)
5589          .get();
5590
5591        const secondRows = secondResult && Array.isArray(secondResult.values)
5592          ? secondResult.values
5593          : [];
5594
5595        rows = rows.concat(secondRows);
5596      }
5597
5598      return rows
5599        .slice(0, maxRows)
5600        .map(normalizeFetchedCollectionEntry)
5601        .filter(Boolean);
5602    })();
5603
5604    // Cache the Promise itself. This also coalesces two overlapping requests for
5605    // the same collection into one network request.
5606    collectionRequestCache.set(name, request);
5607
5608    try {
5609      return await request;
5610    } catch (error) {
5611      // Never cache a failed request. The next rebuild should be able to retry.
5612      if (collectionRequestCache.get(name) === request) {
5613        collectionRequestCache.delete(name);
5614      }
5615      throw error;
5616    }
5617  }
5618
5619  /*
5620    PROGRESSIVE FIRST RENDER
5621
5622    On the first Full-widget build, 06 NAV MASTER is enough to construct a
5623    working provisional navigation model. Render that model as soon as NAV
5624    MASTER is available instead of making the visitor wait for 02 CATEGORY and
5625    03 CORE validation to finish.
5626
5627    The provisional model is intentionally NOT published to window.NMW.data.
5628    Mirror therefore continues to wait for the final validated model. Once all
5629    required collections finish loading, runRebuild() replaces this provisional
5630    render with the normal final navigation.
5631  */
5632  function renderProvisionalNavigation(navMasterRows) {
5633    if (
5634      widgetMode !== 'full' ||
5635      window.NMW.ready ||
5636      !isCurrentInstance() ||
5637      !Array.isArray(navMasterRows) ||
5638      !navMasterRows.length
5639    ) {
5640      return;
5641    }
5642
5643    try {
5644      let provisionalModel = normalizeNavMasterFallback(navMasterRows);
5645
5646      // Include any configured native navigation that is already available.
5647      // If Duda inserts the native wrapper later, the existing discovery
5648      // observer will schedule the normal rebuild and the final model will pick
5649      // it up without delaying this first visible render.
5650      const nativeItems = normalizeConfiguredNativeNavigation();
5651      provisionalModel = [...provisionalModel, ...nativeItems];
5652
5653      // Manual Content Panel items are available without waiting for Category
5654      // or Core and should appear in the same first render when configured.
5655      provisionalModel = layerManualNavigationItems(provisionalModel, false);
5656
5657      // Preserve Mega grouping and the configured top-line presentation so the
5658      // provisional navigation visually matches the final navigation.
5659      provisionalModel = groupMegaNavigationItems(provisionalModel);
5660
5661      if (
5662        !provisionalModel.length ||
5663        !isCurrentInstance() ||
5664        window.NMW.ready
5665      ) {
5666        return;
5667      }
5668
5669      root.dataset.nmwProvisional = 'true';
5670      renderNavigation(provisionalModel, 'full');
5671    } catch (error) {
5672      // A provisional-render failure must never prevent the normal final build.
5673      console.warn('[NMW] Provisional navigation render failed', error);
5674    }
5675  }
5676
5677  async function fetchRequiredCollections() {
5678    const useCustomNavOnly = readBoolean(
5679      configValueFrom(cfg, 'useCustomNavOnly'),
5680      false
5681    );
5682
5683    const output = {};
5684    let hadError = false;
5685
5686    // CUSTOMNAV mode retains its existing loading behavior because 06 NAV
5687    // MASTER is not the active source in that mode.
5688    if (useCustomNavOnly) {
5689      try {
5690        output.CUSTOMNAV = await fetchCollection('CUSTOMNAV');
5691      } catch (error) {
5692        output.CUSTOMNAV = [];
5693        hadError = true;
5694        warn(
5695          'COLLECTION_FETCH_FAILED',
5696          'Could not fetch collection "CUSTOMNAV".',
5697          error
5698        );
5699      }
5700
5701      return { collections: output, hadError };
5702    }
5703
5704    // Normal mode deliberately loads NAV MASTER first. As soon as it resolves,
5705    // render a provisional top navigation before waiting for validation data.
5706    try {
5707      output['06 NAV MASTER'] = await fetchCollection('06 NAV MASTER');
5708    } catch (error) {
5709      output['06 NAV MASTER'] = [];
5710      hadError = true;
5711      warn(
5712        'COLLECTION_FETCH_FAILED',
5713        'Could not fetch collection "06 NAV MASTER".',
5714        error
5715      );
5716    }
5717
5718    if (output['06 NAV MASTER'].length) {
5719      renderProvisionalNavigation(output['06 NAV MASTER']);
5720    }
5721
5722    // Preserve the existing sequential loading behavior for Category and Core.
5723    // This remains safer in the Duda editor than starting simultaneous loaders.
5724    for (const name of ['02 CATEGORY', '03 CORE']) {
5725      try {
5726        output[name] = await fetchCollection(name);
5727      } catch (error) {
5728        output[name] = [];
5729        hadError = true;
5730        warn(
5731          'COLLECTION_FETCH_FAILED',
5732          `Could not fetch collection "${name}".`,
5733          error
5734        );
5735      }
5736    }
5737
5738    return { collections: output, hadError };
5739  }
5740
5741
5742  // Lightweight runtime diagnostic available from the browser console.
5743  // Run nmwDebug() to confirm startup, render-host detection, shared state,
5744  // and whether the required Duda collections can be loaded.
5745  const runtimeDebug = async function nmwRuntimeDebug() {
5746    const report = {
5747      widgetMode,
5748      instanceId,
5749      rootFound: Boolean(root),
5750      renderHostFound: Boolean(renderHost),
5751      renderHostClass: renderHost ? renderHost.className : '',
5752      categoryUrlPrefix: CATEGORY_URL_PREFIX,
5753      childUrlPrefix: CHILD_URL_PREFIX,
5754      design: {
5755        useUniversalBackgrounds: readBoolean(
5756          designValue(widgetMode, ['useUniversalBackgrounds', 'useUniversalBackground'], true),
5757          true
5758        ),
5759        universalBackgroundColor: designValue(widgetMode, 'universalBackgroundColor', '#ffffff'),
5760        resolvedSubmenuBackground: root.style.getPropertyValue('--nmw-sub-bg'),
5761        resolvedThirdLevelBackground: root.style.getPropertyValue('--nmw-subsub-bg'),
5762        resolvedMegaBackground: root.style.getPropertyValue('--nmw-mega-bg'),
5763        resolvedTopBackground: root.style.getPropertyValue('--nmw-top-bg'),
5764        resolvedMegaFamilyBackground: root.style.getPropertyValue('--nmw-mega-family-bg'),
5765        useUniversalTypography: readBoolean(
5766          designValue(widgetMode, 'useUniversalTypography', true),
5767          true
5768        ),
5769        universalFontFamily: designValue(widgetMode, ['universalFontFamily', 'universalFont'], 'inherit'),
5770        universalFontSize: designValue(widgetMode, ['universalFontSize', 'universalTextSize', 'universalFontFamily', 'universalFont'], '16px'),
5771        universalFontWeight: designValue(widgetMode, ['universalFontWeight', 'universalFontFamily', 'universalFont'], '500'),
5772        resolvedUniversalFontFamily: root.style.getPropertyValue('--nmw-universal-font'),
5773        resolvedUniversalFontSize: root.style.getPropertyValue('--nmw-universal-size'),
5774        resolvedUniversalFontWeight: root.style.getPropertyValue('--nmw-universal-weight')
5775      },
5776      sharedState: window.NMW
5777    };
5778
5779    console.group(`NMW Runtime Debug (${widgetMode})`);
5780    console.log('Startup report:', report);
5781
5782    // Full is the collection owner. Mirror intentionally does not fetch.
5783    if (widgetMode === 'full') {
5784      const collectionNames = readBoolean(configValueFrom(cfg, 'useCustomNavOnly'), false)
5785        ? ['CUSTOMNAV']
5786        : ['06 NAV MASTER', '02 CATEGORY', '03 CORE'];
5787
5788      for (const name of collectionNames) {
5789        try {
5790          const rows = await fetchCollection(name, { forceRefresh: true });
5791          const sampleSlugs = name === '02 CATEGORY' || name === '03 CORE'
5792            ? rows.slice(0, 10).flatMap(extractCollectionSlugs)
5793            : [];
5794          console.log(`${name}: ${rows.length} row(s) loaded`, {
5795            sampleRows: rows.slice(0, 3),
5796            sampleSlugs
5797          });
5798        } catch (error) {
5799          console.error(`${name}: FAILED`, error);
5800        }
5801      }
5802    } else {
5803      console.log('Mirror uses Full shared data and does not fetch collections.');
5804    }
5805
5806    console.groupEnd();
5807    return report;
5808  };
5809
5810  window[`nmwDebug${widgetMode === 'full' ? 'Full' : 'Mirror'}`] = runtimeDebug;
5811  // Do not let a later Mirror initialization replace the primary Full debug helper.
5812  if (widgetMode === 'full' || typeof window.nmwDebug !== 'function') {
5813    window.nmwDebug = runtimeDebug;
5814  }
5815
5816  // ---------------------------------------------------------------------------
5817  // Dynamic collection indexes
5818  // ---------------------------------------------------------------------------
5819
5820  function extractPageItemUrl(row) {
5821    const raw = field(row, 'page_item_url', 'Page Item URL', 'PAGE ITEM URL', 'item_url', 'URL');
5822    if (raw && typeof raw === 'object') return readString(raw.href || raw.url || '', '');
5823    return readString(raw, '');
5824  }
5825
5826  function buildDynamicIndex(rows) {
5827    const items = (rows || []).map((row, order) => {
5828      const url = extractPageItemUrl(row);
5829      return {
5830        row,
5831        order,
5832        url,
5833        canonicalPath: canonicalPathWithoutHash(url),
5834        slug: getLastPathSegment(url)
5835      };
5836    }).filter(item => item.url);
5837
5838    /*
5839      Keep the original ordered items for compatibility, but also build an O(1)
5840      path lookup. If duplicate canonical paths exist, preserve the first row just
5841      as Array.find() did in the original implementation.
5842    */
5843    const byCanonicalPath = new Map();
5844    items.forEach(item => {
5845      if (item.canonicalPath && !byCanonicalPath.has(item.canonicalPath)) {
5846        byCanonicalPath.set(item.canonicalPath, item);
5847      }
5848    });
5849
5850    return { items, byCanonicalPath };
5851  }
5852
5853
5854  // Build a simple combined slug inventory for Navigation Tree warnings.
5855  // This intentionally ignores which collection the slug is stored in. A slug
5856  // is considered valid when it exists in either 02 CATEGORY or 03 CORE.
5857  function normalizeCollectionSlug(value) {
5858    let raw = value;
5859    if (raw && typeof raw === 'object') {
5860      raw = raw.item
5861        ?? raw.slug
5862        ?? raw.value
5863        ?? raw.href
5864        ?? raw.url
5865        ?? raw.name
5866        ?? '';
5867    }
5868
5869    const textValue = readString(raw, '');
5870    if (!textValue) return '';
5871
5872    // URLs and paths use their final segment. Bare collection item values are
5873    // already slugs and are normalized directly.
5874    if (/^(https?:)?\/\//i.test(textValue) || textValue.includes('/') || textValue.includes('?') || textValue.includes('#')) {
5875      const segment = getLastPathSegment(textValue);
5876      if (segment) return segment.toLowerCase();
5877    }
5878
5879    return textValue
5880      .toLowerCase()
5881      .split(/[?#]/)[0]
5882      .replace(/^\/+|\/+$/g, '')
5883      .split('/')
5884      .filter(Boolean)
5885      .pop() || '';
5886  }
5887
5888  function extractCollectionSlugs(row) {
5889    if (!row || typeof row !== 'object') return [];
5890
5891    // Inspect both the flattened row and the original nested data object. This
5892    // supports either Duda response shape without depending on where the item
5893    // slug or dynamic page URL happens to be stored.
5894    const sources = [row];
5895    if (row.data && typeof row.data === 'object' && !Array.isArray(row.data)) {
5896      sources.push(row.data);
5897    }
5898
5899    const rawValues = [];
5900    sources.forEach(source => {
5901      rawValues.push(
5902        field(source, 'item', 'Item', 'ITEM'),
5903        field(source, 'slug', 'Slug', 'SLUG'),
5904        field(source, 'name', 'Name', 'NAME'),
5905        extractPageItemUrl(source)
5906      );
5907    });
5908
5909    return unique(rawValues.map(normalizeCollectionSlug));
5910  }
5911
5912  function buildCombinedCollectionSlugSet(categoryRows, coreRows) {
5913    const slugs = new Set();
5914    [...(categoryRows || []), ...(coreRows || [])].forEach(row => {
5915      extractCollectionSlugs(row).forEach(slug => {
5916        if (slug) slugs.add(slug);
5917      });
5918    });
5919    return slugs;
5920  }
5921
5922  function buildSlugValidationWarnings(tree, categoryRows, coreRows) {
5923    const availableSlugs = buildCombinedCollectionSlugSet(categoryRows, coreRows);
5924    const warnings = [];
5925
5926    function walk(nodes) {
5927      (nodes || []).forEach(node => {
5928        if (!node || node.invalid || node.depth > MAX_DEPTH) return;
5929
5930        const isHome = node.depth === 0 && node.label.toLowerCase() === 'home';
5931        if (!isHome) {
5932          const candidates = createSlugCandidates(node.label);
5933          const found = candidates.some(slug => availableSlugs.has(slug));
5934
5935          if (!found) {
5936            const validChildren = (node.children || []).filter(child => child && !child.invalid && child.depth <= MAX_DEPTH);
5937            const suggestedCollection = usesCategorySource(node.depth, validChildren)
5938              ? '02 CATEGORY'
5939              : '03 CORE';
5940
5941            warnings.push({
5942              label: node.label,
5943              slugs: candidates,
5944              suggestedCollection
5945            });
5946          }
5947        }
5948
5949        walk(node.children || []);
5950      });
5951    }
5952
5953    walk(tree || []);
5954    return warnings;
5955  }
5956
5957  // Build complete expected paths from Page Name and the configured prefix.
5958  // The display label is never changed. Only lookup candidates are normalized.
5959  // "Heating/Cooling" with prefix "services" becomes /services/heating-cooling.
5960  // "Boats & Cars" checks both /.../boats-and-cars and /.../boats-cars.
5961  function buildExpectedDynamicPaths(pageName, prefix) {
5962    return createSlugCandidates(pageName).map(slug => {
5963      const path = prefix ? `/${prefix}/${slug}` : `/${slug}`;
5964      return canonicalPathWithoutHash(path);
5965    });
5966  }
5967
5968  // Match only the complete expected dynamic-page path. The configured prefix
5969  // is authoritative. A row with the same final slug under a different prefix
5970  // must not be selected, because that would make categoryUrlPrefix and
5971  // childUrlPrefix appear to be ignored. The Map lookup keeps the original
5972  // collection-order behavior when both supported ampersand variants exist.
5973  function findDynamicMatch(index, pageName, prefix) {
5974    const expectedPaths = buildExpectedDynamicPaths(pageName, prefix);
5975    if (!index || !expectedPaths.length) return null;
5976
5977    if (index.byCanonicalPath instanceof Map) {
5978      let earliest = null;
5979
5980      expectedPaths.forEach(path => {
5981        const match = index.byCanonicalPath.get(path);
5982        if (match && (!earliest || match.order < earliest.order)) {
5983          earliest = match;
5984        }
5985      });
5986
5987      return earliest;
5988    }
5989
5990    // Defensive compatibility fallback for any older array-shaped index.
5991    const items = Array.isArray(index)
5992      ? index
5993      : (Array.isArray(index.items) ? index.items : []);
5994
5995    return items.find(item => expectedPaths.includes(item.canonicalPath)) || null;
5996  }
5997
5998  // Build the internal dynamic URL dictated by the configured Category/Core
5999  // prefix. This is the fallback when the corresponding collection does not yet
6000  // contain an exact page_item_url. It keeps the item visible without allowing a
6001  // legacy Page Link or a same-slug row under another prefix to override the panel.
6002  function buildConfiguredDynamicUrl(pageName, prefix) {
6003    const slug = slugify(pageName);
6004    const path = `/${prefix ? `${prefix}/` : ''}${slug}`;
6005    return buildRuntimeFallbackUrl(path);
6006  }
6007
6008  // Category pages are structural navigation parents. Any navigation item with
6009  // no valid children is treated as a Core page, even when it is top-level or
6010  // assigned to a Mega group. Mega controls presentation only.
6011  function usesCategorySource(depth, children) {
6012    const childCount = Array.isArray(children) ? children.length : 0;
6013    return Number(depth) < MAX_DEPTH && childCount > 0;
6014  }
6015
6016  // ---------------------------------------------------------------------------
6017  // 06 NAV MASTER normalization
6018  // ---------------------------------------------------------------------------
6019
6020  function parseMegaGroup(value) {
6021    // Duda collection fields may arrive as strings, booleans, or small objects
6022    // depending on whether the Mega column is configured as text, select, or switch.
6023    let raw = value;
6024    if (raw && typeof raw === 'object') {
6025      raw = raw.value
6026        ?? raw.label
6027        ?? raw.name
6028        ?? raw.text
6029        ?? raw.selected
6030        ?? raw.checked
6031        ?? raw.id
6032        ?? '';
6033    }
6034
6035    if (raw === true) return 1;
6036    if (raw === false || raw == null) return 0;
6037
6038    const normalized = readString(raw, 'No')
6039      .toLowerCase()
6040      .replace(/[\s_-]+/g, '');
6041
6042    if (['yes', 'true', 'on', 'mega', 'mega1', '1'].includes(normalized)) return 1;
6043    if (['mega2', '2'].includes(normalized)) return 2;
6044    if (['mega3', '3'].includes(normalized)) return 3;
6045    if (['mega4', '4'].includes(normalized)) return 4;
6046    return 0;
6047  }
6048
6049  function extractNavMasterPageLink(row) {
6050    const raw = field(
6051      row,
6052      'Page Link',
6053      'page_link',
6054      'PAGE LINK',
6055      'Page URL',
6056      'page_url',
6057      'Link'
6058    );
6059
6060    if (raw && typeof raw === 'object') {
6061      return readString(raw.href ?? raw.url ?? raw.value ?? '', '');
6062    }
6063
6064    return readString(raw, '');
6065  }
6066
6067  function buildCollectionTree(navRows) {
6068    const nodes = [];
6069    const byName = new Map();
6070
6071    (navRows || []).forEach((row, order) => {
6072      const label = readString(field(row, 'Page Name'), '');
6073      if (!label) return;
6074      const nameKey = label.toLowerCase();
6075      if (byName.has(nameKey)) {
6076        warn('DUPLICATE_PAGE_NAME', `Duplicate Page Name "${label}" was ignored.`, row);
6077        return;
6078      }
6079      const node = {
6080        raw: row,
6081        label,
6082        parentName: readString(field(row, 'Parent 1'), ''),
6083        topMegaValue: field(row, 'Mega'),
6084        pageLink: extractNavMasterPageLink(row),
6085        order,
6086        children: [],
6087        parent: null
6088      };
6089      nodes.push(node);
6090      byName.set(nameKey, node);
6091    });
6092
6093    const roots = [];
6094    nodes.forEach(node => {
6095      if (!node.parentName) {
6096        roots.push(node);
6097        return;
6098      }
6099      const parent = byName.get(node.parentName.toLowerCase());
6100      if (!parent || parent === node) {
6101        warn('MISSING_PARENT', `"${node.label}" was skipped because Parent 1 "${node.parentName}" was not found.`, node.raw);
6102        node.invalid = true;
6103        return;
6104      }
6105      node.parent = parent;
6106      parent.children.push(node);
6107    });
6108
6109    function sortAndValidate(list, depth) {
6110      list.sort((a, b) => a.order - b.order);
6111      list.forEach(node => {
6112        node.depth = depth;
6113        if (depth > MAX_DEPTH) {
6114          node.invalid = true;
6115          warn('MAX_DEPTH_EXCEEDED', `"${node.label}" was ignored because navigation supports only three levels.`, node.raw);
6116        }
6117        sortAndValidate(node.children, depth + 1);
6118      });
6119    }
6120    sortAndValidate(roots, 0);
6121    return roots.filter(node => !node.invalid);
6122  }
6123
6124  function normalizeCollectionNavigation(navRows, categoryRows, coreRows) {
6125    const tree = buildCollectionTree(navRows);
6126    const categoryIndex = buildDynamicIndex(categoryRows);
6127    const coreIndex = buildDynamicIndex(coreRows);
6128
6129    // Navigation Tree warnings use one combined slug inventory. The actual
6130    // collection only matters as a suggestion when a slug is absent from both.
6131    local.slugValidationWarnings = buildSlugValidationWarnings(tree, categoryRows, coreRows);
6132
6133    function normalizeNode(node, ancestors, inheritedMegaGroup) {
6134      if (node.invalid || node.depth > MAX_DEPTH) return null;
6135      const isRoot = node.depth === 0;
6136      const megaGroup = isRoot ? parseMegaGroup(node.topMegaValue) : inheritedMegaGroup;
6137      if (!isRoot && parseMegaGroup(node.topMegaValue) && parseMegaGroup(node.topMegaValue) !== inheritedMegaGroup) {
6138        warn('DESCENDANT_MEGA_IGNORED', `Mega value on descendant "${node.label}" was ignored; descendants inherit the top-level mega group.`, node.raw);
6139      }
6140
6141      const validChildren = node.children
6142        .map(child => normalizeNode(child, [...ancestors, node], megaGroup))
6143        .filter(Boolean);
6144      const hasGrandchildren = node.depth === 1 && validChildren.length > 0;
6145
6146      const isHome = node.depth === 0 && node.label.toLowerCase() === 'home';
6147      const expectsCategory = usesCategorySource(node.depth, validChildren);
6148      const expectedIndex = expectsCategory ? categoryIndex : coreIndex;
6149      const expectedPrefix = expectsCategory ? CATEGORY_URL_PREFIX : CHILD_URL_PREFIX;
6150      const match = isHome ? null : findDynamicMatch(expectedIndex || [], node.label, expectedPrefix);
6151      const navMasterUrl = node.pageLink ? buildRuntimeFallbackUrl(node.pageLink) : '';
6152      let type = 'page';
6153      let url = '';
6154
6155      if (isHome) {
6156        const nativeHome = findTreeItem(parseNativeNavigation(), item => item.type === 'page' && item.canonicalPath === '/');
6157        url = nativeHome && nativeHome.url
6158          ? nativeHome.url
6159          : (navMasterUrl || buildRuntimeFallbackUrl('/'));
6160      } else {
6161        // Prefix controls are authoritative for collection navigation. Prefer the
6162        // exact page_item_url only when it matches the configured full path. If
6163        // that row is missing, construct the URL from the configured prefix.
6164        // Page Link remains structure/reference data and no longer overrides the
6165        // Category/Core prefix fields for internal dynamic pages.
6166        url = match ? match.url : buildConfiguredDynamicUrl(node.label, expectedPrefix);
6167      }
6168
6169      if (!isHome && !match) {
6170        warn('DYNAMIC_PREFIX_CONSTRUCTED_FALLBACK', `"${node.label}" used the configured prefix because no exact dynamic collection path was found.`, {
6171          depth: node.depth,
6172          expectedCollection: expectsCategory ? '02 CATEGORY' : '03 CORE',
6173          configuredPrefix: expectedPrefix,
6174          ignoredPageLink: node.pageLink || '',
6175          expectedPaths: buildExpectedDynamicPaths(node.label, expectedPrefix),
6176          constructedUrl: url
6177        });
6178      }
6179
6180      // Standard top-level structural parents without a matching Category page
6181      // may still act as folders when they have valid children and no explicit
6182      // Mega assignment. Mega families remain clickable as Category parents.
6183      if (!isHome && node.depth === 0 && megaGroup === 0 && !match && validChildren.length && !node.pageLink) {
6184        type = 'folder';
6185        url = '';
6186      }
6187
6188      const hierarchySlugs = [...ancestors.map(item => slugify(item.label)), slugify(node.label)];
6189      const key = `collection:${hierarchySlugs.join('/')}`;
6190      const presentation = megaGroup ? 'mega' : 'standard';
6191      return {
6192        key,
6193        parentKey: ancestors.length ? `collection:${ancestors.map(item => slugify(item.label)).join('/')}` : null,
6194        source: 'collection',
6195        type,
6196        presentation,
6197        megaGroup,
6198        depth: node.depth,
6199        order: node.order,
6200        label: node.label,
6201        slug: slugify(node.label),
6202        canonicalPath: url ? canonicalPath(url) : '',
6203        url,
6204        target: '_self',
6205        isExternal: isExternalUrl(url),
6206        hasChildren: validChildren.length > 0,
6207        children: validChildren
6208      };
6209    }
6210
6211    let normalized = tree.map(rootNode =>
6211 normalizeNode(rootNode, [], 0)).filter(Boolean);
6212
6213    // Mega family alphabetization is applied only after top-level families are
6214    // grouped under their synthetic Mega trigger. Child and grandchild order is
6215    // always preserved from 06 NAV MASTER.
6216    return normalized;
6217  }
6218
6219  // ---------------------------------------------------------------------------
6220  // Native Duda navigation from .navmasterdefault
6221  // ---------------------------------------------------------------------------
6222
6223  function getNativeWrapper() {
6224    return document.querySelector('.navmasterdefault');
6225  }
6226
6227  function nativeAnchorLabel(anchor) {
6228    if (!anchor) return '';
6229    const textNode = anchor.querySelector('[data-link-text]');
6230    return readString(textNode?.getAttribute('data-link-text') || textNode?.textContent || anchor.textContent, '');
6231  }
6232
6233  function nativeAnchorHref(anchor) {
6234    if (!anchor) return '';
6235    const values = [
6236      anchor.getAttribute('href'),
6237      anchor.getAttribute('runtime_url'),
6238      anchor.getAttribute('data-href'),
6239      anchor.getAttribute('raw_url')
6240    ].map(value => readString(value, ''));
6241
6242    // Preserve Duda's current rendered href whenever it is usable. If href is
6243    // only a folder placeholder, fall through to runtime_url/raw_url.
6244    return values.find(value => value && value !== '#' && !value.toLowerCase().startsWith('javascript:'))
6245      || values.find(Boolean)
6246      || '';
6247  }
6248
6249  function nativeAnchorRawUrl(anchor) {
6250    if (!anchor) return '';
6251    return readString(
6252      anchor.getAttribute('raw_url') ||
6253      anchor.getAttribute('runtime_url') ||
6254      anchor.getAttribute('data-href') ||
6255      anchor.getAttribute('href'),
6256      ''
6257    );
6258  }
6259
6260  function readNativeNode(li, depth, parentKey) {
6261    if (!li || depth > MAX_DEPTH) return null;
6262    const anchor = li.querySelector(':scope > a, :scope > span > a, :scope > div > a');
6263    const href = nativeAnchorHref(anchor);
6264    const rawUrl = nativeAnchorRawUrl(anchor);
6265    // Preserve Duda-generated link metadata/classes from the native navigation.
6266    // Anchor links can rely on these attributes for Duda's own scrolling and
6267    // fixed-header compensation behavior. linkHtml() already knows how to carry
6268    // linkAttributes/linkClassName into the rebuilt NMW anchor.
6269    const nativeLinkData = anchor ? manualLinkFromAnchor(anchor) : { attributes: {}, className: '' };
6270    const label = nativeAnchorLabel(anchor) || readString(li.getAttribute('data-link-text'), '');
6271    const isFolder = !href || href === '#' || !anchor;
6272    const canonical = isFolder ? '' : canonicalPath(rawUrl || href);
6273    const key = isFolder
6274      ? `native-folder:${parentKey || 'root'}/${slugify(label)}`
6275      : `native:${canonical || canonicalPath(href)}`;
6276    const childList = li.querySelector(':scope > ul[data-auto="sub-pages"], :scope > ul.unav-sub, :scope > ul');
6277    const childLis = childList ? Array.from(childList.querySelectorAll(':scope > li')) : [];
6278    const children = childLis.map(child => readNativeNode(child, depth + 1, key)).filter(Boolean);
6279    return {
6280      key,
6281      parentKey,
6282      source: 'native',
6283      type: isFolder ? 'folder' : 'page',
6284      presentation: 'standard',
6285      megaGroup: 0,
6286      depth,
6287      order: 0,
6288      label,
6289      slug: slugify(label),
6290      canonicalPath: canonical,
6291      url: isFolder ? '' : href,
6292      target: anchor ? readString(anchor.getAttribute('target'), '_self') : '_self',
6293      isExternal: isExternalUrl(href),
6294      linkAttributes: nativeLinkData.attributes || {},
6295      linkClassName: readString(nativeLinkData.className, ''),
6296      hasChildren: children.length > 0,
6297      children
6298    };
6299  }
6300
6301  function parseNativeNavigation() {
6302    const wrapper = getNativeWrapper();
6303    if (!wrapper) return [];
6304    const nav = wrapper.querySelector('nav[data-element-type="onelinksmenu"], nav.unifiednav, nav');
6305    if (!nav) return [];
6306    const topList = nav.querySelector(':scope > ul[data-auto="navigation-pages"], :scope > ul');
6307    if (!topList) return [];
6308    return Array.from(topList.querySelectorAll(':scope > li')).map(li =>
6308 readNativeNode(li, 0, null)).filter(Boolean);
6309  }
6310
6311  function configuredNativeParents() {
6312    const configured = configValueFrom(cfg, 'nativeParents');
6313    const list = Array.isArray(configured) ? configured : [];
6314    return list.map((item, order) => ({
6315      url: readString(item.url?.href || item.url, ''),
6316      canonicalPath: canonicalPath(item.url?.href || item.url),
6317      label: readString(item.label, ''),
6318      showParent: readBoolean(item.showParent, false),
6319      pullChildren: readBoolean(item.pullChildren, false),
6320      order
6321    }));
6322  }
6323
6324  function cloneNativeBranch(node, config, depth = 0, parentKey = null) {
6325    if (!node || depth > MAX_DEPTH) return null;
6326    const children = config.pullChildren
6327      ? node.children.map(child => cloneNativeBranch(child, { ...config, label: '', showParent: true, pullChildren: true }, depth + 1, node.key)).filter(Boolean)
6328      : [];
6329
6330    // A folder without pulled valid children has no useful action and is omitted.
6331    if (node.type === 'folder' && !children.length) return null;
6332
6333    return {
6334      ...node,
6335      parentKey,
6336      depth,
6337      order: config.order,
6338      label: depth === 0 && config.label ? config.label : node.label,
6339      hasChildren: children.length > 0,
6340      children
6341    };
6342  }
6343
6344  function normalizeConfiguredNativeNavigation() {
6345    const parsed = parseNativeNavigation();
6346    const configs = configuredNativeParents();
6347    const result = [];
6348    const seen = new Set();
6349
6350    configs.forEach(config => {
6351      if (!config.showParent) return;
6352      if (!config.canonicalPath || seen.has(config.canonicalPath)) {
6353        if (seen.has(config.canonicalPath)) warn('DUPLICATE_NATIVE_CONFIG', `Native URL "${config.url}" was configured more than once; the first entry was kept.`);
6354        return;
6355      }
6356      seen.add(config.canonicalPath);
6357      const match = parsed.find(item => item.canonicalPath === config.canonicalPath);
6358      if (!match) {
6359        warn('NATIVE_URL_NOT_FOUND', `Configured native URL "${config.url}" was not found in .navmasterdefault.`);
6360        return;
6361      }
6362      const normalized = cloneNativeBranch(match, config, 0, null);
6363      if (normalized) result.push(normalized);
6364    });
6365
6366    return result;
6367  }
6368
6369  // ---------------------------------------------------------------------------
6370  // Manual navigation items from the Duda Content Panel
6371  // ---------------------------------------------------------------------------
6372
6373  /*
6374    Manual items are layered onto either navigation source:
6375    - Normal mode: 06 NAV MASTER + configured native pages + manual items.
6376    - CUSTOMNAV-only mode: CUSTOMNAV collection + manual items.
6377
6378    Duda link inputs must be rendered with the custom_link Handlebars helper so
6379    page, dynamic-page, popup, anchor, email, telephone, and download behavior
6380    retains the attributes Duda generates. The matching HTML writes those links
6381    into a hidden manual-item source. JavaScript reads the generated anchors and
6382    carries their attributes into the final navigation markup.
6383  */
6384
6385  function normalizeManualLevel(value) {
6386    const normalized = readString(value, 'top').toLowerCase().replace(/[\s_-]+/g, '');
6387    if (['2', 'grandchild', 'subsub', 'third', 'thirdlevel'].includes(normalized)) return 2;
6388    if (['1', 'child', 'sub', 'second', 'secondlevel'].includes(normalized)) return 1;
6389    return 0;
6390  }
6391
6392  function normalizeManualPlacement(value) {
6393    const normalized = readString(value, 'after-native').toLowerCase().replace(/[\s_]+/g, '-');
6394    if (['afterhome', 'after-home', 'home'].includes(normalized)) return 'after-home';
6395    if (['aftercollection', 'after-collection', 'collection'].includes(normalized)) return 'after-collection';
6396    return 'after-native';
6397  }
6398
6399  function resolveManualRuntimeHref(value) {
6400    const raw = readString(value, '');
6401    if (!raw) return '';
6402
6403    // Duda-generated special links already contain the required scheme/action.
6404    if (/^(?:https?:|mailto:|tel:|sms:|javascript:|data:|blob:)/i.test(raw) || raw.startsWith('//') || raw.startsWith('#')) {
6405      return raw;
6406    }
6407
6408    return buildRuntimeFallbackUrl(raw);
6409  }
6410
6411  function manualLinkValue(value) {
6412    if (value == null) return { href: '', target: '_self', attributes: {}, className: '' };
6413    if (typeof value === 'string') {
6414      return { href: resolveManualRuntimeHref(value), target: '_self', attributes: {}, className: '' };
6415    }
6416
6417    if (typeof value !== 'object') {
6418      return { href: '', target: '_self', attributes: {}, className: '' };
6419    }
6420
6421    const href = readString(
6422      value.href
6423        ?? value.url
6424        ?? value.runtime_url
6425        ?? value.runtimeUrl
6426        ?? value.raw_url
6427        ?? value.rawUrl
6428        ?? value.value
6429        ?? value.path
6430        ?? '',
6431      ''
6432    );
6433    const opensNewWindow = readBoolean(value.newWindow ?? value.openInNewWindow ?? value.new_window, false);
6434    const target = readString(value.target, opensNewWindow ? '_blank' : '_self');
6435    const attributes = {};
6436
6437    // Preserve common Duda link metadata when the configuration object exposes it.
6438    Object.entries(value).forEach(([name, attributeValue]) => {
6439      const lower = String(name).toLowerCase();
6440      if (!attributeValue && attributeValue !== 0) return;
6441      if (['href', 'url', 'runtime_url', 'runtimeurl', 'raw_url', 'rawurl', 'value', 'path', 'target', 'class', 'classname', 'style', 'id'].includes(lower)) return;
6442      if (lower.startsWith('data-') || ['rel', 'download', 'title', 'onclick'].includes(lower)) {
6443        attributes[name] = String(attributeValue);
6444      }
6445    });
6446
6447    return {
6448      href: resolveManualRuntimeHref(href),
6449      target,
6450      attributes,
6451      className: readString(value.className ?? value.class, '')
6452    };
6453  }
6454
6455  function manualLinkFromAnchor(anchor) {
6456    if (!anchor) return { href: '', target: '_self', attributes: {}, className: '' };
6457    const attributes = {};
6458
6459    Array.from(anchor.attributes || []).forEach(attribute => {
6460      const name = attribute.name;
6461      const lower = name.toLowerCase();
6462      if (['href', 'target', 'class', 'style', 'id', 'role', 'aria-current'].includes(lower)) return;
6463      // Retain Duda's link behavior metadata, including popup/download handlers.
6464      if (lower.startsWith('data-') || lower.startsWith('aria-') || lower.startsWith('on') || ['rel', 'download', 'title', 'name', 'type'].includes(lower)) {
6465        attributes[name] = attribute.value;
6466      }
6467    });
6468
6469    return {
6470      href: readString(anchor.getAttribute('href'), ''),
6471      target: readString(anchor.getAttribute('target'), '_self'),
6472      attributes,
6473      className: readString(anchor.getAttribute('class'), '')
6474    };
6475  }
6476
6477  function manualRowFromDom(node, index) {
6478    const anchor = node.querySelector('a');
6479    return {
6480      manualItemKey: node.getAttribute('data-manual-item-key') || '',
6481      manualLabel: node.getAttribute('data-manual-label') || '',
6482      manualType: node.getAttribute('data-manual-type') || 'page',
6483      manualLevel: node.getAttribute('data-manual-level') || 'top',
6484      manualParent: node.getAttribute('data-manual-parent') || '',
6485      manualPlacement: node.getAttribute('data-manual-placement') || 'after-native',
6486      manualOrder: node.getAttribute('data-manual-order') || index,
6487      manualMegaGroup: node.getAttribute('data-manual-mega-group') || '0',
6488      manualShowItem: node.getAttribute('data-manual-show-item'),
6489      // Return null until Duda's custom_link helper has produced a real anchor.
6490      // This allows normalizeManualPanelItems() to fall back to data.config.
6491      __manualLink: anchor ? manualLinkFromAnchor(anchor) : null
6492    };
6493  }
6494
6495  function configuredManualRows() {
6496    const configured = configValueFrom(cfg, 'manualNavigationItems');
6497    const configRows = Array.isArray(configured) ? configured : [];
6498    const domNodes = Array.from(root.querySelectorAll('[data-nmw-manual-item]'));
6499    const domRows = domNodes.map(manualRowFromDom);
6500
6501    if (!domRows.length) return configRows;
6502    if (!configRows.length) return domRows;
6503
6504    // Duda can update the repeat-group configuration and the hidden Handlebars
6505    // output a few milliseconds apart. Merge both representations so a new row
6506    // can appear from data.config immediately, then receive the final custom_link
6507    // attributes as soon as Duda writes them into the hidden source.
6508    const configByKey = new Map();
6509    configRows.forEach((row, index) => {
6510      const key = readString(row && (row.manualItemKey ?? row.itemKey ?? row.key), '').toLowerCase();
6511      if (key && !configByKey.has(key)) configByKey.set(key, { row, index });
6512    });
6513
6514    const usedConfigIndexes = new Set();
6515    const merged = domRows.map((domRow, index) => {
6516      const key = readString(domRow.manualItemKey, '').toLowerCase();
6517      const keyed = key ? configByKey.get(key) : null;
6518      const configIndex = keyed ? keyed.index : index;
6519      const configRow = keyed ? keyed.row : (configRows[configIndex] || {});
6520      usedConfigIndexes.add(configIndex);
6521
6522      // DOM attributes are the newest visible Handlebars output. Config values
6523      // supply fields that are not yet represented in the DOM, especially links.
6524      return { ...configRow, ...domRow };
6525    });
6526
6527    configRows.forEach((row, index) => {
6528      if (!usedConfigIndexes.has(index)) merged.push(row);
6529    });
6530
6531    return merged;
6532  }
6533
6534  function normalizeManualPanelItems() {
6535    const output = [];
6536    const usedKeys = new Set();
6537
6538    configuredManualRows().forEach((row, index) => {
6539      const showValue = row.manualShowItem ?? row.showItem ?? row.show;
6540      if (showValue !== undefined && showValue !== null && showValue !== '' && !readBoolean(showValue, true)) return;
6541
6542      const label = readString(row.manualLabel ?? row.label ?? row.pageName, '');
6543      if (!label) return;
6544
6545      const type = readString(row.manualType ?? row.type, 'page').toLowerCase() === 'folder' ? 'folder' : 'page';
6546      const requestedDepth = normalizeManualLevel(row.manualLevel ?? row.level);
6547      const parentReference = readString(row.manualParent ?? row.parentReference ?? row.parent, '');
6548      const placement = normalizeManualPlacement(row.manualPlacement ?? row.placement);
6549      const order = readNumber(row.manualOrder ?? row.order, index, -100000, 100000);
6550      const configuredMegaGroup = parseMegaGroup(row.manualMegaGroup ?? row.megaGroup ?? 0);
6551      const configuredLinkData = manualLinkValue(row.manualLink ?? row.link ?? row.url);
6552      const domLinkData = row.__manualLink && readString(row.__manualLink.href, '')
6553        ? row.__manualLink
6554        : null;
6555      const linkData = domLinkData || configuredLinkData;
6556      // Folder items are structural controls and never require or output a link.
6557      const url = type === 'folder' ? '' : resolveManualRuntimeHref(linkData.href);
6558
6559      if (type === 'page' && !url) {
6560        warn('MANUAL_LINK_MISSING', `Manual navigation item "${label}" was skipped because it has no configured link.`);
6561        return;
6562      }
6563
6564      const suppliedKey = readString(row.manualItemKey ?? row.itemKey ?? row.key, '');
6565      let key = suppliedKey || `manual:${slugify(label) || 'item'}-${index + 1}`;
6566      if (!key.toLowerCase().startsWith('manual:')) key = `manual:${key}`;
6567      if (usedKeys.has(key.toLowerCase())) {
6568        warn('MANUAL_DUPLICATE_KEY', `Manual navigation key "${key}" was used more than once; the later item was skipped.`);
6569        return;
6570      }
6571      usedKeys.add(key.toLowerCase());
6572
6573      output.push({
6574        key,
6575        parentKey: null,
6576        source: 'manual',
6577        type,
6578        presentation: configuredMegaGroup ? 'mega' : 'standard',
6579        megaGroup: configuredMegaGroup,
6580        depth: requestedDepth,
6581        order,
6582        label,
6583        slug: slugify(label),
6584        canonicalPath: url && !/^(?:mailto:|tel:|sms:|javascript:)/i.test(url) ? canonicalPath(url) : '',
6585        url,
6586        target: readString(linkData.target, '_self'),
6587        isExternal: isExternalUrl(url),
6588        hasChildren: false,
6589        children: [],
6590        linkAttributes: linkData.attributes || {},
6591        linkClassName: readString(linkData.className, ''),
6592        _manualRequestedDepth: requestedDepth,
6593        _manualParentReference: parentReference,
6594        _manualPlacement: placement
6595      });
6596    });
6597
6598    return output;
6599  }
6600
6601  function buildManualParentIndex(baseItems, manualItems) {
6602    const byKey = new Map();
6603    const byLabel = new Map();
6604    const bySlug = new Map();
6605
6606    function add(item) {
6607      if (!item) return;
6608      const key = readString(item.key, '').toLowerCase();
6609      const label = readString(item.label, '').toLowerCase();
6610      const slug = readString(item.slug, slugify(item.label)).toLowerCase();
6611      if (key && !byKey.has(key)) byKey.set(key, item);
6612      if (label) {
6613        if (!byLabel.has(label)) byLabel.set(label, []);
6614        byLabel.get(label).push(item);
6615      }
6616      if (slug) {
6617        if (!bySlug.has(slug)) bySlug.set(slug, []);
6618        bySlug.get(slug).push(item);
6619      }
6620      (item.children || []).forEach(add);
6621    }
6622
6623    (baseItems || []).forEach(add);
6624    (manualItems || []).forEach(add);
6625    return { byKey, byLabel, bySlug };
6626  }
6627
6628  function resolveManualParent(reference, index) {
6629    const raw = readString(reference, '');
6630    if (!raw) return null;
6631    const lower = raw.toLowerCase();
6632    const keyCandidates = unique([lower, lower.startsWith('manual:') ? lower : `manual:${lower}`]);
6633    for (const key of keyCandidates) {
6634      if (index.byKey.has(key)) return index.byKey.get(key);
6635    }
6636
6637    const labelMatches = index.byLabel.get(lower) || [];
6638    if (labelMatches.length) {
6639      if (labelMatches.length > 1) {
6640        warn('MANUAL_PARENT_AMBIGUOUS', `Parent reference "${raw}" matched more than one Page Name. The first match was used; use an Item Key for an exact match.`);
6641      }
6642      return labelMatches[0];
6643    }
6644
6645    const slugMatches = index.bySlug.get(slugify(raw)) || [];
6646    if (slugMatches.length) {
6647      if (slugMatches.length > 1) {
6648        warn('MANUAL_PARENT_AMBIGUOUS', `Parent reference "${raw}" matched more than one slug. The first match was used; use an Item Key for an exact match.`);
6649      }
6650      return slugMatches[0];
6651    }
6652
6653    return null;
6654  }
6655
6656  function manualParentCreatesCycle(item, parent, manualByKey) {
6657    let current = parent;
6658    const seen = new Set([item.key]);
6659    while (current) {
6660      if (seen.has(current.key)) return true;
6661      seen.add(current.key);
6662      const parentReference = current._manualParentReference;
6663      if (!parentReference) return false;
6664      current = manualByKey.get(readString(parentReference, '').toLowerCase())
6665        || manualByKey.get(`manual:${readString(parentReference, '').toLowerCase()}`)
6666        || null;
6667    }
6668    return false;
6669  }
6670
6671  function finalizeManualLayer(items, depth = 0, parentKey = null, inheritedMegaGroup = 0) {
6672    const sourceItems = (items || []).filter(Boolean);
6673    // Top-level placement is already explicitly assembled as After Home,
6674    // After Collection, and After Native. Preserve that order. Child and
6675    // grandchild siblings use the numeric Order field.
6676    if (depth > 0) sourceItems.sort((a, b) => Number(a.order || 0) - Number(b.order || 0));
6677    return sourceItems.map(item => {
6678        item.depth = depth;
6679        item.parentKey = parentKey;
6680        const group = depth === 0 ? Math.round(Number(item.megaGroup) || 0) : inheritedMegaGroup;
6681        item.megaGroup = group >= 1 && group <= 4 ? group : 0;
6682        item.presentation = item.megaGroup ? 'mega' : 'standard';
6683        item.children = finalizeManualLayer(item.children || [], depth + 1, item.key, item.megaGroup);
6684        item.hasChildren = item.children.length > 0;
6685        delete item._manualRequestedDepth;
6686        delete item._manualParentReference;
6687        delete item._manualPlacement;
6688        return item;
6689      });
6690  }
6691
6692  function insertManualRoots(baseItems, manualRoots, customNavMode) {
6693    const afterHome = manualRoots.filter(item => item._manualPlacement === 'after-home').sort((a, b) => a.order - b.order);
6694    const afterCollection = manualRoots.filter(item => item._manualPlacement === 'after-collection').sort((a, b) => a.order - b.order);
6695    const afterNative = manualRoots.filter(item => item._manualPlacement === 'after-native').sort((a, b) => a.order - b.order);
6696
6697    // CUSTOMNAV is itself the collection-backed base. Preserve its authored order
6698    // and layer the panel items around that complete base rather than re-sorting it
6699    // by each row's SOURCE metadata.
6700    if (customNavMode) {
6701      const base = [...baseItems];
6702      const homeIndex = base.findIndex(item => readString(item.label, '').toLowerCase() === 'home' || item.canonicalPath === '/');
6703      if (afterHome.length) base.splice(homeIndex >= 0 ? homeIndex + 1 : 0, 0, ...afterHome);
6704      return [...base, ...afterCollection, ...afterNative];
6705    }
6706
6707    const firstNativeIndex = baseItems.findIndex(item => item.source === 'native');
6708    const collectionBlock = firstNativeIndex >= 0 ? baseItems.slice(0, firstNativeIndex) : [...baseItems];
6709    const nativeBlock = firstNativeIndex >= 0 ? baseItems.slice(firstNativeIndex) : [];
6710    const homeIndex = collectionBlock.findIndex(item => readString(item.label, '').toLowerCase() === 'home' || item.canonicalPath === '/');
6711    if (afterHome.length) collectionBlock.splice(homeIndex >= 0 ? homeIndex + 1 : 0, 0, ...afterHome);
6712    return [...collectionBlock, ...afterCollection, ...nativeBlock, ...afterNative];
6713  }
6714
6715  function layerManualNavigationItems(baseItems, customNavMode) {
6716    const manualItems = normalizeManualPanelItems();
6717    if (!manualItems.length) return baseItems;
6718
6719    const manualByKey = new Map(manualItems.map(item => [item.key.toLowerCase(), item]));
6720    const index = buildManualParentIndex(baseItems, manualItems);
6721    const roots = [];
6722
6723    manualItems.forEach(item => {
6724      const requestedDepth = item._manualRequestedDepth;
6725      if (requestedDepth === 0) {
6726        if (item._manualParentReference) {
6727          warn('MANUAL_TOP_PARENT_IGNORED', `Manual top-level item "${item.label}" has a Parent value; the Parent value was ignored.`);
6728        }
6729        roots.push(item);
6730        return;
6731      }
6732
6733      const parent = resolveManualParent(item._manualParentReference, index);
6734      if (!parent) {
6735        warn('MANUAL_PARENT_NOT_FOUND', `Manual navigation item "${item.label}" was skipped because parent "${item._manualParentReference}" was not found.`);
6736        item.invalid = true;
6737        return;
6738      }
6739      if (manualParentCreatesCycle(item, parent, manualByKey)) {
6740        warn('MANUAL_PARENT_CYCLE', `Manual navigation item "${item.label}" was skipped because its parent configuration creates a cycle.`);
6741        item.invalid = true;
6742        return;
6743      }
6744
6745      const requiredParentDepth = requestedDepth - 1;
6746      const parentDepth = Number(parent._manualRequestedDepth ?? parent.depth ?? 0);
6747      if (parentDepth !== requiredParentDepth) {
6748        warn('MANUAL_LEVEL_MISMATCH', `Manual navigation item "${item.label}" was skipped because its selected level does not immediately follow parent "${parent.label}".`);
6749        item.invalid = true;
6750        return;
6751      }
6752
6753      item.parentKey = parent.key;
6754      parent.children = parent.children || [];
6755      parent.children.push(item);
6756      parent.hasChildren = true;
6757    });
6758
6759    const validRoots = roots.filter(item =>
6759 !item.invalid);
6760    let merged = insertManualRoots(baseItems, validRoots, customNavMode);
6761    merged = finalizeManualLayer(merged, 0, null, 0);
6762
6763    // On live/preview pages an empty folder has no useful navigation action, so
6764    // omit it. In the editor, retain it as a disabled preview row so changing a
6765    // manual item from Page to Folder produces an immediate visible response and
6766    // clearly indicates that a child still needs to be assigned.
6767    function removeEmptyFolders(items) {
6768      return (items || []).filter(item => {
6769        item.children = removeEmptyFolders(item.children || []);
6770        item.hasChildren = item.children.length > 0;
6771        if (item.source === 'manual' && item.type === 'folder' && !item.hasChildren) {
6772          if (isEditorCanvas()) {
6773            item._manualEmptyFolderPreview = true;
6774            return true;
6775          }
6776          warn('MANUAL_EMPTY_FOLDER', `Manual folder "${item.label}" was skipped because it has no valid children.`);
6777          return false;
6778        }
6779        return true;
6780      });
6781    }
6782
6783    return removeEmptyFolders(merged);
6784  }
6785
6786  // ---------------------------------------------------------------------------
6787  // CUSTOMNAV mode and export-compatible normalization
6788  // ---------------------------------------------------------------------------
6789
6790  function normalizeCustomNavRows(rows) {
6791    const flat = [];
6792    const byKey = new Map();
6793    (rows || []).forEach((row, index) => {
6794      const showRaw = field(row, 'SHOW ITEM', 'show_item', 'SHOW', 'show');
6795      if (showRaw !== '' && showRaw !== undefined && showRaw !== null && !readBoolean(showRaw, true)) return;
6796
6797      const key = readString(field(row, 'ITEM KEY', 'item_key'), `custom:${index}`);
6798      if (byKey.has(key)) {
6799        warn('CUSTOMNAV_DUPLICATE_KEY', `CUSTOMNAV key "${key}" was used more than once; the later row was skipped.`);
6800        return;
6801      }
6802      const source = readString(field(row, 'SOURCE', 'source'), 'manual').toLowerCase();
6803      const type = readString(field(row, 'TYPE', 'type'), 'page').toLowerCase() === 'folder' ? 'folder' : 'page';
6804      const label = readString(field(row, 'PAGE NAME', 'LABEL', 'page_name', 'label'), '');
6805      if (!label) return;
6806      const rawUrl = type === 'folder' ? '' : readString(field(row, 'URL', 'url'), '');
6807      const url = source === 'manual' && rawUrl ? buildRuntimeFallbackUrl(rawUrl) : rawUrl;
6808      const item = {
6809        key,
6810        parentKey: readString(field(row, 'PARENT KEY', 'parent_key'), '') || null,
6811        source: ['native', 'collection', 'manual'].includes(source) ? source : 'manual',
6812        type,
6813        presentation: readString(field(row, 'PRESENTATION', 'presentation'), 'standard').toLowerCase() === 'mega' ? 'mega' : 'standard',
6814        megaGroup: Math.round(readNumber(field(row, 'MEGA GROUP', 'mega_group'), 0, 0, 4)),
6815        depth: Math.round(readNumber(field(row, 'DEPTH', 'depth'), 0, 0, 2)),
6816        order: Math.round(readNumber(field(row, 'ORDER', 'order'), index, 0, 100000)),
6817        label,
6818        slug: readString(field(row, 'SLUG', 'slug'), slugify(label)),
6819        canonicalPath: readString(field(row, 'CANONICAL PATH', 'canonical_path'), canonicalPath(url)),
6820        url,
6821        target: readString(field(row, 'TARGET', 'target'), '_self'),
6822        isExternal: readBoolean(field(row, 'IS EXTERNAL', 'is_external'), isExternalUrl(url)),
6823        hasChildren: false,
6824        children: []
6825      };
6826      flat.push(item);
6827      byKey.set(key, item);
6828    });
6829
6830    const roots = [];
6831    flat.sort((a, b) => a.order - b.order).forEach(item => {
6832      if (!item.parentKey) {
6833        roots.push(item);
6834        return;
6835      }
6836      const parent = byKey.get(item.parentKey);
6837      if (!parent || parent === item) {
6838        warn('CUSTOMNAV_PARENT_INVALID', `CUSTOMNAV item "${item.label}" was skipped because its parent is invalid.`);
6839        item.invalid = true;
6840        return;
6841      }
6842      parent.children.push(item);
6843      parent.hasChildren = true;
6844    });
6845
6846    function normalizeDepth(items, depth = 0, parentKey = null) {
6847      return items
6848        .filter(item =>
6848 !item.invalid)
6849        .sort((a, b) => a.order - b.order)
6850        .map(item => {
6851          if (depth > MAX_DEPTH) {
6852            warn('CUSTOMNAV_DEPTH_INVALID', `CUSTOMNAV item "${item.label}" was skipped because navigation supports three content levels.`);
6853            return null;
6854          }
6855          item.depth = depth;
6856          item.parentKey = parentKey;
6857          item.children = normalizeDepth(item.children || [], depth + 1, item.key);
6858          item.hasChildren = item.children.length > 0;
6859          return item;
6860        })
6861        .filter(Boolean);
6862    }
6863
6864    return normalizeDepth(roots);
6865  }
6866
6867  // Re-resolve source-backed CUSTOMNAV links so preview/live URLs do not become stale.
6868  function refreshCustomUrls(items, categoryRows, coreRows) {
6869    const native = parseNativeNavigation();
6870    const categoryIndex = buildDynamicIndex(categoryRows || []);
6871    const coreIndex = buildDynamicIndex(coreRows || []);
6872    function walk(item) {
6873      const copy = { ...item, children: item.children.map(walk) };
6874      if (item.source === 'native' && item.canonicalPath) {
6875        const match = findTreeItem(native, candidate => candidate.canonicalPath === item.canonicalPath);
6876        if (match && match.url) copy.url = match.url;
6877      }
6878      if (item.source === 'collection' && item.slug) {
6879        const isHome = item.depth === 0 && (
6880          readString(item.label, '').toLowerCase() === 'home' ||
6881          canonicalPathWithoutHash(item.canonicalPath || item.url) === '/'
6882        );
6883
6884        if (isHome) {
6885          copy.url = item.url || buildRuntimeFallbackUrl('/');
6886          copy.canonicalPath = '/';
6887        } else {
6888          const expectsCategory = usesCategorySource(item.depth, item.children || []);
6889          const index = expectsCategory ? categoryIndex : coreIndex;
6890          const prefix = expectsCategory ? CATEGORY_URL_PREFIX : CHILD_URL_PREFIX;
6891          const match = findDynamicMatch(index, item.label || item.slug, prefix);
6892          copy.url = match
6893            ? match.url
6894            : buildConfiguredDynamicUrl(item.label || item.slug, prefix);
6895          copy.canonicalPath = canonicalPath(copy.url);
6896        }
6897      }
6898      copy.hasChildren = copy.children.length > 0;
6899      return copy;
6900    }
6901    return items.map(walk);
6902  }
6903
6904  function findTreeItem(items, predicate) {
6905    for (const item of items || []) {
6906      if (predicate(item)) return item;
6907      const nested = findTreeItem(item.children, predicate);
6908      if (nested) return nested;
6909    }
6910    return null;
6911  }
6912
6913
6914  // Build a guaranteed fallback from 06 NAV MASTER alone. Structural items
6915  // with children use the Category prefix; leaf items use the Core prefix, even
6916  // when they are top-level or assigned to a Mega group. Category/Core
6917  // collections are still preferred for exact page_item_url values, but a
6918  // temporarily unavailable validation collection no longer blanks the nav.
6919  function normalizeNavMasterFallback(navRows) {
6920    const tree = buildCollectionTree(navRows || []);
6921
6922    function walk(node, ancestors, inheritedMegaGroup) {
6923      if (!node || node.invalid || node.depth > MAX_DEPTH) return null;
6924
6925      const megaGroup = node.depth === 0
6926        ? parseMegaGroup(node.topMegaValue)
6927        : inheritedMegaGroup;
6928
6929      const children = node.children
6930        .map(child => walk(child, [...ancestors, node], megaGroup))
6931        .filter(Boolean);
6932
6933      const slug = slugify(node.label);
6934      const isHome = node.depth === 0 && node.label.toLowerCase() === 'home';
6935      const expectsCategory = usesCategorySource(node.depth, children);
6936      const prefix = expectsCategory ? CATEGORY_URL_PREFIX : CHILD_URL_PREFIX;
6937      const configuredPageLink = node.pageLink ? buildRuntimeFallbackUrl(node.pageLink) : '';
6938      const url = isHome
6939        ? (configuredPageLink || buildRuntimeFallbackUrl('/'))
6940        : buildConfiguredDynamicUrl(node.label, prefix);
6941      const hierarchySlugs = [...ancestors.map(item => slugify(item.label)), slug];
6942
6943      return {
6944        key: `collection:${hierarchySlugs.join('/')}`,
6945        parentKey: ancestors.length
6946          ? `collection:${ancestors.map(item => slugify(item.label)).join('/')}`
6947          : null,
6948        source: 'collection',
6949        type: 'page',
6950        presentation: megaGroup ? 'mega' : 'standard',
6951        megaGroup,
6952        depth: node.depth,
6953        order: node.order,
6954        label: node.label,
6955        slug,
6956        canonicalPath: canonicalPath(url),
6957        url,
6958        target: '_self',
6959        isExternal: false,
6960        hasChildren: children.length > 0,
6961        children
6962      };
6963    }
6964
6965    return tree.map(rootNode =>
6965 walk(rootNode, [], 0)).filter(Boolean);
6966  }
6967
6968  // ---------------------------------------------------------------------------
6969  // Group top-level mega families under one synthetic trigger per Mega group
6970  // ---------------------------------------------------------------------------
6971
6972  function shiftMegaFamilyDepth(item, parentKey, depthOffset = 1) {
6973    const nextDepth = Math.max(0, Number(item.depth) || 0) + depthOffset;
6974    const shifted = {
6975      ...item,
6976      parentKey,
6977      depth: nextDepth,
6978      presentation: 'mega',
6979      children: []
6980    };
6981
6982    shifted.children = (item.children || []).map(child =>
6983      shiftMegaFamilyDepth(child, shifted.key, depthOffset)
6984    );
6985    shifted.hasChildren = shifted.children.length > 0;
6986    return shifted;
6987  }
6988
6989  function groupMegaNavigationItems(items) {
6990    const sourceItems = Array.isArray(items) ? items : [];
6991    const groupedFamilies = new Map();
6992    const firstIndexes = new Map();
6993
6994    sourceItems.forEach((item, index) => {
6995      const groupNumber = Math.round(Number(item && item.megaGroup) || 0);
6996      const isSyntheticGroup = readString(item && item.key, '').startsWith('mega-group:');
6997      if (groupNumber < 1 || groupNumber > 4 || isSyntheticGroup) return;
6998
6999      if (!groupedFamilies.has(groupNumber)) {
7000        groupedFamilies.set(groupNumber, []);
7001        firstIndexes.set(groupNumber, index);
7002      }
7003      groupedFamilies.get(groupNumber).push(item);
7004    });
7005
7006    if (!groupedFamilies.size) return sourceItems;
7007
7008    const output = [];
7009    const insertedGroups = new Set();
7010
7011    sourceItems.forEach((item, index) => {
7012      const groupNumber = Math.round(Number(item && item.megaGroup) || 0);
7013      const isSyntheticGroup = readString(item && item.key, '').startsWith('mega-group:');
7014
7015      if (groupNumber >= 1 && groupNumber <= 4 && !isSyntheticGroup) {
7016        if (insertedGroups.has(groupNumber)) return;
7017        if (firstIndexes.get(groupNumber) !== index) return;
7018
7019        const key = `mega-group:${groupNumber}`;
7020        const megaCfg = getMegaConfig(groupNumber);
7021        const sourceFamilies = [...(groupedFamilies.get(groupNumber) || [])];
7022        if (megaCfg.alphabetize) {
7023          sourceFamilies.sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }));
7024        }
7025        const families = sourceFamilies.map(family =>
7026          shiftMegaFamilyDepth(family, key, 1)
7027        );
7028
7029        output.push({
7030          key,
7031          parentKey: null,
7032          source: 'generated',
7033          type: 'folder',
7034          presentation: 'mega',
7035          megaGroup: groupNumber,
7036          depth: 0,
7037          order: index,
7038          label: megaCfg.label,
7039          slug: slugify(megaCfg.label),
7040          canonicalPath: '',
7041          url: '',
7042          target: '_self',
7043          isExternal: false,
7044          hasChildren: families.length > 0,
7045          children: families
7046        });
7047        insertedGroups.add(groupNumber);
7048        return;
7049      }
7050
7051      output.push(item);
7052    });
7053
7054    return output;
7055  }
7056
7057  // ---------------------------------------------------------------------------
7058  // Build and publish the final shared model
7059  // ---------------------------------------------------------------------------
7060
7061  async function buildNavigationModel() {
7062    local.latestBuildWarnings = [];
7063    local.slugValidationWarnings = [];
7064    const fetched = await fetchRequiredCollections();
7065    const collections = fetched.collections;
7066    let hadError = fetched.hadError;
7067    let model;
7068
7069    if (readBoolean(configValueFrom(cfg, 'useCustomNavOnly'), false)) {
7070      model = normalizeCustomNavRows(collections.CUSTOMNAV || []);
7071      // Optional URL refresh data is fetched when any CUSTOMNAV item at any
7072      // hierarchy level is backed by a native or collection source.
7073      const needsRefresh = Boolean(findTreeItem(model, item => item.source !== 'manual'));
7074      if (needsRefresh) {
7075        const secondary = await Promise.allSettled([fetchCollection('02 CATEGORY'), fetchCollection('03 CORE')]);
7076        const categories = secondary[0].status === 'fulfilled' ? secondary[0].value : [];
7077        const cores = secondary[1].status === 'fulfilled' ? secondary[1].value : [];
7078        if (secondary.some(result => result.status === 'rejected')) {
7079          hadError = true;
7080          warn('CUSTOMNAV_URL_REFRESH_FAILED', 'One or more source collections could not be loaded while refreshing CUSTOMNAV URLs.');
7081        }
7082        model = refreshCustomUrls(model, categories, cores);
7083      }
7084    } else {
7085      const navMasterRows = collections['06 NAV MASTER'] || [];
7086      let collectionItems;
7087
7088      if (hadError && navMasterRows.length) {
7089        // Do not report missing slugs when one of the source collections failed
7090        // to load; absence cannot be confirmed until both collections are available.
7091        local.slugValidationWarnings = [];
7092        warn(
7093          'COLLECTION_VALIDATION_UNAVAILABLE',
7094          'One or more validation collections could not be loaded. Rendering NAV MASTER with constructed URLs for this build.'
7095        );
7096        collectionItems = normalizeNavMasterFallback(navMasterRows);
7097      } else {
7098        collectionItems = normalizeCollectionNavigation(
7099          navMasterRows,
7100          collections['02 CATEGORY'] || [],
7101          collections['03 CORE'] || []
7102        );
7103
7104        if (!collectionItems.length && navMasterRows.length) {
7105          warn(
7106            'VALIDATION_FALLBACK',
7107            'Category/Core validation produced no items. Rendering NAV MASTER with constructed URLs instead.'
7108          );
7109          collectionItems = normalizeNavMasterFallback(navMasterRows);
7110        }
7111      }
7112
7113      const nativeItems = normalizeConfiguredNativeNavigation();
7114      model = [...collectionItems, ...nativeItems];
7115    }
7116
7117    // Panel-created manual items are additive in both modes. In CUSTOMNAV-only
7118    // mode they are layered on top of the CUSTOMNAV collection; in normal mode
7119    // they are layered on top of Collection + Native navigation.
7120    model = layerManualNavigationItems(
7121      model,
7122      readBoolean(configValueFrom(cfg, 'useCustomNavOnly'), false)
7123    );
7124
7125    // A Mega value assigns a top-level family to a shared group. The visible
7126    // trigger is the configured Mega label, while assigned parents become
7127    // families inside that single dropdown. Yes and Mega1 both map to group 1.
7128    model = groupMegaNavigationItems(model);
7129
7130    return {
7131      model,
7132      hadError,
7133      warnings: local.latestBuildWarnings,
7134      slugWarnings: local.slugValidationWarnings
7135    };
7136  }
7137
7138  function isCurrentInstance() {
7139    const ownerKey = widgetMode === 'full' ? 'ownerId' : 'mirrorOwnerId';
7140    return !local.cleaned
7141      && root.dataset.nmwInstance === instanceId
7142      && window.NMW[ownerKey] === instanceId;
7143  }
7144
7145  async function runRebuild() {
7146    if (!isCurrentInstance()) return;
7147    if (local.rebuildRunning) {
7148      local.pendingRebuild = true;
7149      return;
7150    }
7151
7152    local.rebuildRunning = true;
7153    window.NMW.loading = true;
7154    updateRootStatus();
7155    dispatchStateChanged();
7156
7157    try {
7158      const result = await buildNavigationModel();
7159
7160      // A newer Duda panel execution may have replaced this instance while the
7161      // asynchronous collection requests were running. Never allow a stale
7162      // build to render or alter shared state.
7163      if (!isCurrentInstance()) return;
7164
7165      const nextData = result.model || [];
7166      const previousSignature = stableStringify(window.NMW.data || []);
7167      const nextSignature = stableStringify(nextData);
7168      const dataChanged = previousSignature !== nextSignature;
7169
7170      const nextFullConfig = cloneSerializable(cfg) || {};
7171      let nextConfigSignature = '';
7172      try { nextConfigSignature = stableStringify(nextFullConfig); }
7173      catch (error) { nextConfigSignature = String(Date.now()); }
7174      const presentationChanged = window.NMW.fullConfigSignature !== nextConfigSignature;
7175
7176      window.NMW.error = result.hadError;
7177      window.NMW.warnings = result.warnings;
7178      window.NMW.slugWarnings = Array.isArray(result.slugWarnings) ? result.slugWarnings : [];
7179      window.NMW.ready = true;
7180      window.NMW.data = nextData;
7181      window.NMW.fullConfig = nextFullConfig;
7182      window.NMW.fullConfigSignature = nextConfigSignature;
7183
7184      // Always render the current Full instance. Duda Design/Content Panel
7185      // changes such as Mega columns, expanded/collapsed families, max width,
7186      // hover behavior, or tool visibility may not alter the normalized model.
7187      // Skipping render when data is unchanged is what previously caused panel
7188      // changes to appear stuck until a refresh. This also replaces any
7189      // NAV-MASTER-only provisional render with the final validated model.
7190      root.removeAttribute('data-nmw-provisional');
7191      renderNavigation(nextData, 'full');
7192
7193      if (dataChanged || presentationChanged) {
7194        window.NMW.version += 1;
7195        if (presentationChanged) window.NMW.presentationVersion += 1;
7196        dispatchDataChanged();
7197      }
7198    } catch (error) {
7199      if (!isCurrentInstance()) return;
7200      window.NMW.error = true;
7201      window.NMW.ready = true;
7202      if (!Array.isArray(window.NMW.data)) window.NMW.data = [];
7203      warn('BUILD_FAILED', 'Navigation build failed.', error);
7204      if (!window.NMW.data.length && renderHost) {
7205        renderHost.innerHTML = `<div class="nmw-empty">Navigation build failed: ${escapeHtml(error && error.message ? error.message : 
7205String(error))}</div>`;
7206      }
7207    } finally {
7208      local.rebuildRunning = false;
7209
7210      // Only the currently registered instance may clear the shared loading
7211      // state or launch a queued rebuild. This prevents a replaced instance
7212      // from leaving the editor stuck on Loading navigation….
7213      if (isCurrentInstance()) {
7214        window.NMW.loading = false;
7215        updateRootStatus();
7216        updateTools();
7217        dispatchStateChanged();
7218        if (local.pendingRebuild) {
7219          local.pendingRebuild = false;
7220          runRebuild();
7221        }
7222      }
7223    }
7224  }
7225
7226  function scheduleRebuild(immediate = false) {
7227    if (local.cleaned) return;
7228    clearTimeout(local.rebuildTimer);
7229    if (immediate) {
7230      runRebuild();
7231      return;
7232    }
7233    local.rebuildTimer = setTimeout(runRebuild, REBUILD_DEBOUNCE);
7234  }
7235
7236  // ---------------------------------------------------------------------------
7237  // Renderers
7238  // ---------------------------------------------------------------------------
7239
7240  function itemClasses(item) {
7241    return [
7242      'nmw-item',
7243      `nmw-source-${item.source}`,
7244      `nmw-type-${item.type}`,
7245      `nmw-presentation-${item.presentation}`,
7246      `nmw-depth-${item.depth}`,
7247      item.megaGroup ? `nmw-mega-group-${item.megaGroup}` : ''
7248    ].filter(Boolean).join(' ');
7249  }
7250
7251  function linkHtml(item, mode, extraClass = '') {
7252    const editorDisabled = isEditorCanvas() && !isSiteCommentsCustomerMode();
7253    const inheritedClasses = readString(item.linkClassName, '')
7254      .split(/\s+/)
7255      .filter(className => className && className !== 'nmw-link')
7256      .join(' ');
7257    const attributes = item.linkAttributes && typeof item.linkAttributes === 'object'
7258      ? Object.entries(item.linkAttributes).map(([name, value]) => {
7259          const safeName = String(name).trim();
7260          if (!/^(?:data-[\w:-]+|aria-[\w:-]+|rel|download|title|name|type|onclick)$/i.test(safeName)) return '';
7261          return value === '' ? ` ${safeName}` : ` ${safeName}="${escapeHtml(value)}"`;
7262        }).join('')
7263      : '';
7264    const className = ['nmw-link', extraClass, inheritedClasses].filter(Boolean).join(' ');
7265    return `<a class="${escapeHtml(className)}" href="${escapeHtml(item.url)}" target="${escapeHtml(item.target || '_self')}" data-nmw-key="${escapeHtml(item.key)}" data-nmw-path="${escapeHtml(item.canonicalPath)}"${attributes}${editorDisabled ? ' data-nmw-editor-disabled="true"' : ''}><span class="nmw-label">${escapeHtml(item.label)}</span></a>`;
7266  }
7267
7268  function toggleHtml(item, submenuId, folder = false, megaFamily = false) {
7269    const label = folder ? item.label : `${item.label} submenu`;
7270    const className = folder ? 'nmw-folder-button' : `nmw-toggle${megaFamily ? ' nmw-mega-family-toggle' : ''}`;
7271    return `<button class="${className}" type="button" data-nmw-toggle data-nmw-key="${escapeHtml(item.key)}" aria-label="Toggle ${escapeHtml(label)}" aria-expanded="false" aria-controls="${escapeHtml(submenuId)}"><span class="nmw-label">${folder ? escapeHtml(item.label) : ''}</span><span class="nmw-toggle-icon" aria-hidden="true"></span></button>`;
7272  }
7273
7274  function emptyFolderPreviewHtml(item) {
7275    return `<button class="nmw-folder-button nmw-empty-folder-preview" type="button" disabled aria-disabled="true" title="Add a valid child item to this folder"><span class="nmw-label">${escapeHtml(item.label)}</span><span class="nmw-empty-folder-note" aria-hidden="true">Add child</span></button>`;
7276  }
7277
7278  function renderStandardItem(item, mode, idState) {
7279    const submenuId = item.hasChildren ? `nmw-${mode}-submenu-${++idState.value}` : '';
7280    let row;
7281    if (item.type === 'folder' && item._manualEmptyFolderPreview) {
7282      row = `<div class="nmw-row">${emptyFolderPreviewHtml(item)}</div>`;
7283    } else if (item.type === 'folder') {
7284      row = `<div class="nmw-row">${toggleHtml(item, submenuId, true)}</div>`;
7285    } else if (item.hasChildren) {
7286      row = `<div class="nmw-row">${linkHtml(item, mode)}${toggleHtml(item, submenuId, false)}</div>`;
7287    } else {
7288      row = `<div class="nmw-row">${linkHtml(item, mode)}</div>`;
7289    }
7290
7291    const children = item.hasChildren
7292      ? `<ul class="nmw-submenu nmw-standard-submenu nmw-depth-${item.depth + 1}" id="${submenuId}" data-nmw-parent-key="${escapeHtml(item.key)}" hidden>${item.children.map(child => renderStandardItem(child, mode, idState)).join('')}</ul>`
7293      : '';
7294    return `<li class="${itemClasses(item)}" data-nmw-item data-nmw-key="${escapeHtml(item.key)}">${row}${children}</li>`;
7295  }
7296
7297  function familySize(item) {
7298    return 1 + (item.children || []).reduce((sum, child) => sum + familySize(child), 0);
7299  }
7300
7301  function distributeFamilies(families, columns) {
7302    const output = Array.from({ length: columns }, () => ({ weight: 0, items: [] }));
7303    families.forEach(family => {
7304      let targetIndex = 0;
7305      for (let index = 1; index < output.length; index += 1) {
7306        if (output[index].weight < output[targetIndex].weight) targetIndex = index;
7307      }
7308      output[targetIndex].items.push(family);
7309      output[targetIndex].weight += familySize(family);
7310    });
7311    return output.map(column => column.items);
7312  }
7313
7314  function renderMegaGrandchild(item, mode) {
7315    const content = item.type === 'folder'
7316      ? `<span class="nmw-mega-grandchild-label"><span class="nmw-label">${escapeHtml(item.label)}</span></span>`
7317      : linkHtml(item, mode, 'nmw-mega-grandchild-link');
7318    return `<li class="${itemClasses(item)} nmw-mega-grandchild" data-nmw-item data-nmw-key="${escapeHtml(item.key)}"><div class="nmw-row">${content}</div></li>`;
7319  }
7320
7321  function renderMegaChild(item, mode) {
7322    const grandchildren = item.children || [];
7323    const content = item.type === 'folder'
7324      ? `<span class="nmw-mega-child-label"><span class="nmw-label">${escapeHtml(item.label)}</span></span>`
7325      : linkHtml(item, mode, 'nmw-mega-child-link');
7326    const nested = grandchildren.length
7327      ? `<ul class="nmw-mega-grandchildren" data-nmw-parent-key="${escapeHtml(item.key)}">${grandchildren.map(child => renderMegaGrandchild(child, mode)).join('')}</ul>`
7328      : '';
7329    return `<li class="${itemClasses(item)} nmw-mega-child${grandchildren.length ? ' nmw-has-grandchildren' : ''}" data-nmw-item data-nmw-key="${escapeHtml(item.key)}"><div class="nmw-row">${content}</div>${nested}</li>`;
7330  }
7331
7332  function renderMegaFamily(family, mode, idState, expandAll) {
7333    const hasChildren = family.children.length > 0;
7334    const familyId = hasChildren ? `nmw-${mode}-mega-family-${++idState.value}` : '';
7335    const familyLink = family.type === 'page' ? linkHtml(family, mode, 'nmw-mega-family-link') : '';
7336    let row;
7337
7338    if (!hasChildren || expandAll) {
7339      row = `<div class="nmw-row">${family.type === 'folder' ? `<span class="nmw-folder-label"><span class="nmw-label">${escapeHtml(family.label)}</span></span>` : familyLink}</div>`;
7340    } else if (family.type === 'folder') {
7341      row = `<div class="nmw-row">${toggleHtml(family, familyId, true, true)}</div>`;
7342    } else {
7343      row = `<div class="nmw-row">${familyLink}${toggleHtml(family, familyId, false, true)}</div>`;
7344    }
7345
7346    const children = hasChildren
7347      ? `<ul class="nmw-mega-family-children${expandAll ? ' nmw-open' : ''}" id="${familyId}" data-nmw-parent-key="${escapeHtml(family.key)}"${expandAll ? '' : ' hidden'}>${family.children.map(child => renderMegaChild(child, mode)).join('')}</ul>`
7348      : '';
7349
7350    return `<div class="nmw-mega-family${expandAll ? ' nmw-open' : ''}" data-nmw-item data-nmw-key="${escapeHtml(family.key)}">${row}${children}</div>`;
7351  }
7352
7353  function renderMegaItem(item, mode, idState) {
7354    const submenuId = `nmw-${mode}-mega-${++idState.value}`;
7355    const row = item.type === 'folder'
7356      ? `<div class="nmw-row">${toggleHtml(item, submenuId, true)}</div>`
7357      : `<div class="nmw-row">${linkHtml(item, mode)}${toggleHtml(item, submenuId, false)}</div>`;
7358    const megaCfg = getMegaConfig(item.megaGroup || 1);
7359
7360    let content;
7361    if (mode === 'mirror') {
7362      // Mirror always uses a single vertical list — layout setting does not apply.
7363      content = `<div class="nmw-mega-vertical">${item.children.map(family => renderMegaFamily(family, mode, idState, megaCfg.expandAll)).join('')}</div>`;
7364    } else if (megaCfg.layout === 'masonry') {
7365      // Masonry: shortest-column balancing. distributeFamilies() assigns each
7366      // family to the column with the lowest accumulated navigation weight so
7367      // differently sized families remain visually balanced across columns.
7368      // Each resulting group is wrapped in a .nmw-mega-column flex container.
7369      const columns = distributeFamilies(item.children, megaCfg.columns);
7370      content = `<div class="nmw-mega-inner" style="--nmw-mega-max-width:${megaCfg.maxWidth}px;--nmw-mega-columns:${megaCfg.columns}" data-nmw-mega-layout="masonry"><div class="nmw-mega-columns">${columns.map(column => `<div class="nmw-mega-column">${column.map(family => renderMegaFamily(family, mode, idState, megaCfg.expandAll)).join('')}</div>`).join('')}</div></div>`;
7371    } else {
7372      // Grid: families are placed directly inside the CSS Grid container in
7373      // their current (or alphabetized) source order. CSS grid-auto-flow: row
7374      // fills each row from left to right before beginning the next row.
7375      // No column wrappers are emitted — each .nmw-mega-family is a direct
7376      // grid child and occupies exactly one grid cell.
7377      const families = item.children
7378        .map(family => renderMegaFamily(family, mode, idState, megaCfg.expandAll))
7379        .join('');
7380      content = `<div class="nmw-mega-inner" style="--nmw-mega-max-width:${megaCfg.maxWidth}px;--nmw-mega-columns:${megaCfg.columns}" data-nmw-mega-layout="grid"><div class="nmw-mega-columns">${families}</div></div>`;
7381    }
7382
7383    return `<li class="${itemClasses(item)}" data-nmw-item data-nmw-key="${escapeHtml(item.key)}" data-nmw-mega-label="${escapeHtml(megaCfg.label)}">${row}<div class="nmw-submenu nmw-mega-menu" id="${submenuId}" data-nmw-parent-key="${escapeHtml(item.key)}" data-nmw-mega-group="${item.megaGroup || 1}" data-nmw-mega-label="${escapeHtml(megaCfg.label)}" aria-label="${escapeHtml(megaCfg.label)}" hidden>${content}</div></li>`;
7384  }
7385
7386  function renderNavigation(items, mode) {
7387    if (!renderHost) return;
7388    applyDesignSettings(mode);
7389    updateMegaIndicators(items);
7390    if (!Array.isArray(items) || !items.length) {
7391      renderHost.innerHTML = '<div class="nmw-empty">Navigation could not be built. Check collection data and native navigation configuration.</div>';
7392      updateTools();
7393      return;
7394    }
7395    const idState = { value: 0 };
7396    const navId = mode === 'full' ? 'nmw-full-main-nav' : `nmw-mirror-main-nav-${instanceId}`;
7397    const list = items.map(item => item.presentation === 'mega'
7398      ? renderMegaItem(item, mode, idState)
7399      : renderStandardItem(item, mode, idState)).join('');
7400    if (loadingHost && loadingHost !== renderHost) loadingHost.hidden = true;
7401    renderHost.innerHTML = `<nav class="nmw-nav nmw-topnav" id="${escapeHtml(navId)}" aria-label="Main navigation"><ul class="nmw-list nmw-top-list">${list}</ul></nav>`;
7402    closeAllMenus(root, false);
7403    refreshActiveStates(root);
7404    updateTools();
7405  }
7406
7407  // ---------------------------------------------------------------------------
7408  // Menu opening, closing, animation, hover, and delegated interaction
7409  // ---------------------------------------------------------------------------
7410
7411  function findControlledSubmenu(toggle) {
7412    const id = toggle?.getAttribute('aria-controls');
7413    return id ? root.querySelector(`#${CSS.escape(id)}`) : null;
7414  }
7415
7416  function getTransitionMs(elementNode) {
7417    if (!elementNode || getBehaviorConfig(widgetMode).animationsEnabled === false) return 0;
7418    const style = window.getComputedStyle(elementNode);
7419    if (style.transitionProperty === 'none') return 0;
7420    const durations = style.transitionDuration.split(',').map(value => parseFloat(value) * (value.includes('ms') ? 1 : 1000));
7421    const delays = style.transitionDelay.split(',').map(value => parseFloat(value) * (value.includes('ms') ? 1 : 1000));
7422    const measured = Math.max(0, ...durations.map((duration, index) => duration + (delays[index] || delays[0] || 0)));
7423    return measured > 0 ? measured : 0;
7424  }
7425
7426  function cancelCloseJob(submenu) {
7427    const job = local.closeJobs.get(submenu);
7428    if (!job) return;
7429    clearTimeout(job.timer);
7430    local.closeTimers.delete(job.timer);
7431    submenu.removeEventListener('transitionend', job.finish);
7432    local.closeJobs.delete(submenu);
7433  }
7434
7435  function updateFlyoutDirection(submenu) {
7436    if (!submenu || widgetMode === 'mirror' || !submenu.classList.contains('nmw-standard-submenu')) return;
7437    const nested = submenu.parentElement?.closest?.('.nmw-standard-submenu');
7438    if (!nested) {
7439      submenu.classList.remove('nmw-flip-left');
7440      return;
7441    }
7442
7443    submenu.classList.remove('nmw-flip-left');
7444    const rect = submenu.getBoundingClientRect();
7445    const viewportWidth = document.documentElement.clientWidth || window.innerWidth || 0;
7446    if (rect.right > viewportWidth - 8) submenu.classList.add('nmw-flip-left');
7447  }
7448
7449  function openMenu(item, toggle, submenu) {
7450    if (!item || !toggle || !submenu) return;
7451    cancelCloseJob(submenu);
7452    closeSiblingMenus(item);
7453    item.classList.remove('nmw-closing');
7454    submenu.classList.remove('nmw-closing');
7455    submenu.hidden = false;
7456    requestAnimationFrame(() => {
7457      if (local.cleaned || !document.documentElement.contains(submenu)) return;
7458      updateFlyoutDirection(submenu);
7459      item.classList.add('nmw-open');
7460      submenu.classList.add('nmw-open');
7461      toggle.setAttribute('aria-expanded', 'true');
7462    });
7463  }
7464
7465  function closeMenu(item, toggle, submenu, returnFocus = false) {
7466    if (!item || !toggle || !submenu || submenu.hidden) return;
7467    cancelCloseJob(submenu);
7468    item.classList.remove('nmw-open');
7469    submenu.classList.remove('nmw-open');
7470    item.classList.add('nmw-closing');
7471    submenu.classList.add('nmw-closing');
7472    toggle.setAttribute('aria-expanded', 'false');
7473
7474    let jobTimer = null;
7475    const finish = event => {
7476      if (event && event.target !== submenu) return;
7477      submenu.hidden = true;
7478      item.classList.remove('nmw-closing');
7479      submenu.classList.remove('nmw-closing');
7480      submenu.removeEventListener('transitionend', finish);
7481      local.closeJobs.delete(submenu);
7482      if (jobTimer) local.closeTimers.delete(jobTimer);
7483      if (returnFocus) toggle.focus();
7484    };
7485    const duration = getTransitionMs(submenu);
7486    if (!duration) {
7487      finish();
7488      return;
7489    }
7490    submenu.addEventListener('transitionend', finish);
7491    jobTimer = setTimeout(finish, duration + 50);
7492    local.closeTimers.add(jobTimer);
7493    local.closeJobs.set(submenu, { timer: jobTimer, finish });
7494  }
7495
7496  function closeSiblingMenus(item) {
7497    let siblings = [];
7498
7499    if (item.classList.contains('nmw-mega-family')) {
7500      const megaMenu = item.closest('.nmw-mega-menu');
7501      siblings = megaMenu
7502        ? Array.from(megaMenu.querySelectorAll('.nmw-mega-family[data-nmw-item]'))
7503        : [];
7504    } else {
7505      const parent = item.parentElement;
7506      siblings = parent ? Array.from(parent.children).filter(node => node.matches?.('[data-nmw-item]')) : [];
7507    }
7508
7509    siblings.forEach(sibling => {
7510      if (sibling === item) return;
7511      const toggle = sibling.querySelector(':scope > .nmw-row [data-nmw-toggle]');
7512      const submenu = toggle ? findControlledSubmenu(toggle) : null;
7513      if (toggle && submenu) closeMenu(sibling, toggle, submenu, false);
7514    });
7515  }
7516
7517  function closeAllMenus(scope, returnFocus = false) {
7518    scope.querySelectorAll('[data-nmw-toggle][aria-expanded="true"]').forEach(toggle => {
7519      const item = toggle.closest('[data-nmw-item]');
7520      const submenu = findControlledSubmenu(toggle);
7521      if (item && submenu) closeMenu(item, toggle, submenu, returnFocus);
7522    });
7523    local.hoverTimers.forEach(timer => clearTimeout(timer));
7524    local.hoverTimers.clear();
7525  }
7526
7527  function toggleMenu(toggle) {
7528    const item = toggle.closest('[data-nmw-item]');
7529    const submenu = findControlledSubmenu(toggle);
7530    if (!item || !submenu) return;
7531    const isOpen = toggle.getAttribute('aria-expanded') === 'true';
7532    if (isOpen) closeMenu(item, toggle, submenu, false);
7533    else openMenu(item, toggle, submenu);
7534  }
7535
7536  function nearestOpenToggle(target) {
7537    let node = target && target.closest ? target.closest('[data-nmw-item], .nmw-submenu, .nmw-mega-family-children') : null;
7538
7539    while (node && root.contains(node)) {
7540      if (node.matches?.('[data-nmw-item]')) {
7541        const ownToggle = node.querySelector(':scope > .nmw-row [data-nmw-toggle][aria-expanded="true"]');
7542        if (ownToggle) return ownToggle;
7543      }
7544
7545      const submenu = node.matches?.('.nmw-submenu, .nmw-mega-family-children')
7546        ? node
7547        : node.closest?.('.nmw-submenu, .nmw-mega-family-children');
7548      if (!submenu) return null;
7549
7550      const parentKey = submenu.getAttribute('data-nmw-parent-key');
7551      if (parentKey) {
7552        const parentToggle = root.querySelector(`[data-nmw-toggle][data-nmw-key="${CSS.escape(parentKey)}"][aria-expanded="true"]`);
7553        if (parentToggle) return parentToggle;
7554      }
7555
7556      node = submenu.parentElement?.closest?.('[data-nmw-item], .nmw-submenu, .nmw-mega-family-children') || null;
7557    }
7558
7559    return null;
7560  }
7561
7562  function attachDelegatedInteractions() {
7563    if (local.listenersAttached) return;
7564    local.listenersAttached = true;
7565
7566    listen(root, 'click', event => {
7567      const toggle = event.target.closest('[data-nmw-toggle]');
7568      if (toggle && root.contains(toggle)) {
7569        event.preventDefault();
7570        toggleMenu(toggle);
7571        return;
7572      }
7573
7574      const link = event.target.closest('a.nmw-link');
7575      if (!link || !root.contains(link)) return;
7576
7577      /*
7578        Recheck Site Comments at click time.
7579
7580        The navigation may have rendered before Duda finished identifying
7581        the visitor as a customer. In that case the link can still contain
7582        a stale data-nmw-editor-disabled attribute even though the console
7583        later reports "Customer Detected".
7584      */
7585      if (isSiteCommentsCustomerMode()) {
7586        /*
7587          Remove stale editor-disabled markers from every rendered link so
7588          the Full and Mirror navigation instances immediately reflect the
7589          confirmed customer state.
7590        */
7591        root
7592          .querySelectorAll(
7593            'a.nmw-link[data-nmw-editor-disabled="true"]'
7594          )
7595          .forEach(customerLink => {
7596            customerLink.removeAttribute(
7597              'data-nmw-editor-disabled'
7598            );
7599          });
7600
7601        /*
7602          Same-site links use the existing Site Comments top-window
7603          navigation. Links intentionally excluded by that function—
7604          anchors, target="_blank", external sites, telephone, email,
7605          SMS, downloads, and similar links—retain normal browser behavior.
7606        */
7607        // Same-page anchors are handled inside the current site document so
7608        // repeated anchor selections work without reloading the Site Comments shell.
7609        if (handleSamePageAnchorClick(link, event)) return;
7610
7611        if (navigateSiteComments(link, event)) return;
7612
7613        closeAllMenus(root, false);
7614        return;
7615      }
7616
7617      /*
7618        Outside confirmed customer Site Comments mode, preserve the
7619        original editor safety behavior.
7620      */
7621      if (link.dataset.nmwEditorDisabled === 'true') {
7622        event.preventDefault();
7623        return;
7624      }
7625
7626      // Explicitly own same-page anchor navigation. This fixes repeated anchor
7627      // selections on the same page and corrects the final position for sticky
7628      // or fixed headers. Cross-page anchors continue with normal navigation.
7629      if (handleSamePageAnchorClick(link, event)) return;
7630
7631      closeAllMenus(root, false);
7632    });
7633
7634    listen(root, 'keydown', event => {
7635      if (event.key !== 'Escape') return;
7636      const toggle = nearestOpenToggle(event.target);
7637      if (!toggle) return;
7638      event.preventDefault();
7639      const item = toggle.closest('[data-nmw-item]');
7640      const submenu = findControlledSubmenu(toggle);
7641      if (item && submenu) closeMenu(item, toggle, submenu, true);
7642    });
7643
7644    listen(root, 'focusout', event => {
7645      requestAnimationFrame(() => {
7646        if (local.cleaned) return;
7647        if (!root.contains(document.activeElement)) closeAllMenus(root, false);
7648      });
7649    });
7650
7651    // Hover uses the same JavaScript-owned state as click. CSS never opens menus
7652    // independently, which keeps hidden and aria-expanded synchronized.
7653    listen(root, 'pointerover', event => {
7654      if (!getBehaviorConfig(widgetMode).hoverEnabled || event.pointerType === 'touch') return;
7655      const item = event.target.closest('[data-nmw-item]');
7656      if (!item || !root.contains(item)) return;
7657      const toggle = item.querySelector(':scope > .nmw-row [data-nmw-toggle]');
7658      const submenu = toggle ? findControlledSubmenu(toggle) : null;
7659      if (!toggle || !submenu) return;
7660      const timer = local.hoverTimers.get(item);
7661      if (timer) clearTimeout(timer);
7662      local.hoverTimers.delete(item);
7663      openMenu(item, toggle, submenu);
7664    });
7665
7666    listen(root, 'pointerout', event => {
7667      if (!getBehaviorConfig(widgetMode).hoverEnabled || event.pointerType === 'touch') return;
7668      const item = event.target.closest('[data-nmw-item]');
7669      if (!item || !root.contains(item)) return;
7670      const related = event.relatedTarget;
7671      if (related && item.contains(related)) return;
7672      const toggle = item.querySelector(':scope > .nmw-row [data-nmw-toggle]');
7673      const submenu = toggle ? findControlledSubmenu(toggle) : null;
7674      if (!toggle || !submenu) return;
7675      const timer = setTimeout(() => {
7676        if (!item.matches(':hover')) closeMenu(item, toggle, submenu, false);
7677        local.hoverTimers.delete(item);
7678      }, HOVER_CLOSE_DELAY);
7679      local.hoverTimers.set(item, timer);
7680    });
7681
7682    listen(document, 'pointerdown', handleOutsidePointer, true);
7683  }
7684
7685  function handleOutsidePointer(event) {
7686    if (!root.contains(event.target)) closeAllMenus(root, false);
7687  }
7688
7689  // ---------------------------------------------------------------------------
7690  // Active-page state from the actual current URL
7691  // ---------------------------------------------------------------------------
7692
7693  function currentComparablePath() {
7694    return canonicalPath(window.location.href);
7695  }
7696
7697  function refreshActiveStates(scope) {
7698    const current = currentComparablePath();
7699    scope.querySelectorAll('a[data-nmw-path]').forEach(link => {
7700      const path = link.getAttribute('data-nmw-path') || '';
7701      const active = path && path === current;
7702      link.classList.toggle('nmw-active', active);
7703      if (active) link.setAttribute('aria-current', 'page');
7704      else link.removeAttribute('aria-current');
7705    });
7706    scope.querySelectorAll('[data-nmw-item]').forEach(item => {
7707      const ownLink = item.querySelector(':scope > .nmw-row a.nmw-active');
7708      const activeDescendant = item.querySelector('.nmw-submenu a.nmw-active, .nmw-mega-family-children a.nmw-active');
7709      item.classList.toggle('nmw-child-active', !ownLink && Boolean(activeDescendant));
7710    });
7711  }
7712
7713  function observeHistoryChanges() {
7714    const marker = '__nmwHistoryWrapped';
7715    if (history[marker]) return () => {};
7716    history[marker] = true;
7717    const originalPush = history.pushState;
7718    const originalReplace = history.replaceState;
7719    const notify = () => window.dispatchEvent(new Event('nmw:url-changed'));
7720    history.pushState = function (...args) {
7721      const result = originalPush.apply(this, args);
7722      notify();
7723      return result;
7724    };
7725    history.replaceState = function (...args) {
7726      const result = originalReplace.apply(this, args);
7727      notify();
7728      return result;
7729    };
7730    return () => {};
7731  }
7732
7733  function handleUrlChanged() {
7734    refreshActiveStates(root);
7735  }
7736
7737  // ---------------------------------------------------------------------------
7738  // Manual-item source observation
7739  // ---------------------------------------------------------------------------
7740
7741  function attachManualSourceObserver() {
7742    const source = root.querySelector('[data-nmw-manual-source]');
7743    if (!source || local.manualObserver) return false;
7744
7745    local.manualObserver = new MutationObserver(mutations => {
7746      const relevant = mutations.some(mutation => {
7747        if (mutation.type === 'childList') return true;
7748        if (mutation.type !== 'attributes') return false;
7749        const name = mutation.attributeName || '';
7750        return name === 'href'
7751          || name === 'target'
7752          || name === 'class'
7753          || name.startsWith('data-manual-')
7754          || name.startsWith('data-')
7755          || name.startsWith('aria-');
7756      });
7757
7758      if (relevant) scheduleRebuild(false);
7759    });
7760
7761    local.manualObserver.observe(source, {
7762      childList: true,
7763      subtree: true,
7764      attributes: true
7765    });
7766
7767    return true;
7768  }
7769
7770  // ---------------------------------------------------------------------------
7771  // Native-nav observation and startup discovery
7772  // ---------------------------------------------------------------------------
7773
7774  function attachNativeObserver() {
7775    const wrapper = getNativeWrapper();
7776    if (!wrapper || local.nativeObserver) return false;
7777    local.nativeObserver = new MutationObserver(() => scheduleRebuild(false));
7778    local.nativeObserver.observe(wrapper, {
7779      childList: true,
7780      subtree: true,
7781      attributes: true,
7782      attributeFilter: ['href', 'raw_url', 'runtime_url', 'aria-current', 'class']
7783    });
7784    if (local.nativeFinderObserver) {
7785      local.nativeFinderObserver.disconnect();
7786      local.nativeFinderObserver = null;
7787    }
7788    return true;
7789  }
7790
7791  function beginNativeDiscovery() {
7792    local.nativeRetryStarted = Date.now();
7793    let isFirstAttempt = true;
7794
7795    const attempt = () => {
7796      if (local.cleaned) return;
7797
7798      // If the document-level finder already attached the native observer, the
7799      // retry loop has nothing left to do. This prevents a later timeout rebuild.
7800      if (local.nativeObserver) return;
7801
7802      if (attachNativeObserver()) {
7803        /*
7804          initializeFull() performs an immediate rebuild right after starting
7805          discovery. When .navmasterdefault already exists on the first attempt,
7806          scheduling another rebuild here caused the collections to be processed
7807          twice during startup. Only rebuild here when native navigation appeared
7808          after that initial attempt.
7809        */
7810        if (!isFirstAttempt) scheduleRebuild(false);
7811        isFirstAttempt = false;
7812        return;
7813      }
7814
7815      isFirstAttempt = false;
7816
7817      if (Date.now() - local.nativeRetryStarted >= NATIVE_MAX_WAIT) {
7818        warn('NATIVE_NAV_TIMEOUT', '.navmasterdefault was not found within 15 seconds. Collection navigation will still render.');
7819        scheduleRebuild(true);
7820        return;
7821      }
7822
7823      local.nativeRetryTimer = setTimeout(attempt, NATIVE_RETRY_INTERVAL);
7824    };
7825
7826    attempt();
7827
7828    // If the native wrapper already existed, no document-wide finder is needed.
7829    if (local.nativeObserver || local.cleaned) return;
7830
7831    // During delayed Duda insertion, this lightweight observer notices the
7832    // wrapper, attaches the targeted observer, rebuilds once, then disconnects.
7833    local.nativeFinderObserver = new MutationObserver(() => {
7834      if (local.cleaned || local.nativeObserver) return;
7835      if (attachNativeObserver()) scheduleRebuild(false);
7836    });
7837    local.nativeFinderObserver.observe(document.body, { childList: true, subtree: true });
7838  }
7839
7840  // ---------------------------------------------------------------------------
7841  // Tree preview and CUSTOMNAV builder
7842  // ---------------------------------------------------------------------------
7843
7844  function updateTools() {
7845    if (!toolsHost) return;
7846    const treeEnabled = readBoolean(configValueFrom(cfg, 'enableNavigationTreePreview'), false);
7847    const builderEnabled = readBoolean(configValueFrom(cfg, 'enableCustomNavigationBuilder'), false);
7848    const visibleEnvironment = isEditorCanvas() || location.href.includes('/preview/') || location.hash.includes('preview');
7849    if (!visibleEnvironment || (!treeEnabled && !builderEnabled)) {
7850      toolsHost.innerHTML = '';
7851      return;
7852    }
7853    toolsHost.innerHTML = [
7854      treeEnabled ? '<button type="button" class="nmw-tool-button" data-nmw-open-tree>View Navigation Tree</button>' : '',
7855      builderEnabled ? '<button type="button" class="nmw-tool-button" data-nmw-open-builder>Open Custom Navigation Builder</button>' : ''
7856    ].join('');
7857  }
7858
7859  function treeLines(items, prefix = '') {
7860    const output = [];
7861    (items || []).forEach(item => {
7862      output.push(`${prefix}${item.label}`);
7863      output.push(...treeLines(item.children || [], `${prefix}  `));
7864    });
7865    return output;
7866  }
7867
7868  function renderSlugWarning(item) {
7869    const slugText = (item.slugs || []).join(' or ');
7870    return `<li><strong>${escapeHtml(item.label)}</strong><br>` +
7871      `Slug not found: ${escapeHtml(slugText)}<br>` +
7872      `Suggested collection: ${escapeHtml(item.suggestedCollection)}</li>`;
7873  }
7874
7875  function openTreePreview() {
7876    const warnings = Array.isArray(window.NMW.slugWarnings) ? window.NMW.slugWarnings : [];
7877    const treeOutput = treeLines(window.NMW.data || []).join('\n') || 'No navigation items.';
7878    const warningSection = warnings.length
7879      ? `<section class="nmw-tree-warnings"><h3>Warnings</h3><ul>${warnings.map(renderSlugWarning).join('')}</ul></section>`
7880      : '';
7881    const body = `<pre class="nmw-tree-output">${escapeHtml(treeOutput)}</pre>${warningSection}`;
7882    openModal('Navigation Tree Preview', body, 'tree');
7883  }
7884
7885  function cloneData(value) {
7886    return JSON.parse(JSON.stringify(value || []));
7887  }
7888
7889  function shiftDraftDepth(item, offset, parentKey = null) {
7890    const copy = cloneData(item);
7891    copy.depth = Math.max(0, (Number(copy.depth) || 0) + offset);
7892    copy.parentKey = parentKey;
7893    copy.children = (copy.children || []).map(child => shiftDraftDepth(child, offset, copy.key));
7894    copy.hasChildren = copy.children.length > 0;
7895    return copy;
7896  }
7897
7898  // The runtime model contains synthetic Mega trigger rows. CUSTOMNAV should
7899  // store the actual family roots with MEGA GROUP values so loading CUSTOMNAV
7900  // can recreate the trigger without adding an extra persisted hierarchy level.
7901  function draftSourceFromSharedModel() {
7902    const output = [];
7903    (window.NMW.data || []).forEach(item => {
7904      if (readString(item.key, '').startsWith('mega-group:')) {
7905        (item.children || []).forEach(family => {
7906          const copy = shiftDraftDepth(family, -1, null);
7907          copy.megaGroup = item.megaGroup || copy.megaGroup || 0;
7908          copy.presentation = copy.megaGroup ? 'mega' : copy.presentation;
7909          output.push(copy);
7910        });
7911      } else {
7912        output.push(shiftDraftDepth(item, 0, null));
7913      }
7914    });
7915    return output;
7916  }
7917
7918  function flattenDraft(items, output = []) {
7919    items.forEach(item => {
7920      output.push(item);
7921      flattenDraft(item.children, output);
7922    });
7923    return output;
7924  }
7925
7926  function renderBuilderRows(items) {
7927    return items.map(item => `<div class="nmw-builder-row" draggable="true" data-builder-key="${escapeHtml(item.key)}" style="--nmw-builder-depth:${item.depth}"><span class="nmw-builder-handle" aria-hidden="true">⋮⋮</span><span class="nmw-builder-label">${escapeHtml(item.label)}</span><span class="nmw-builder-meta">${escapeHtml(item.source)} · ${escapeHtml(item.type)}${item.megaGroup ? ` · ${escapeHtml(getMegaLabel(item.megaGroup))}` : ''}</span><button type="button" data-builder-left data-key="${escapeHtml(item.key)}" aria-label="Move ${escapeHtml(item.label)} left">←</button><button type="button" data-builder-right data-key="${escapeHtml(item.key)}" aria-label="Move ${escapeHtml(item.label)} right">→</button><button type="button" data-builder-remove data-key="${escapeHtml(item.key)}" aria-label="Remove ${escapeHtml(item.label)}">Remove</button></div>`).join('');
7928  }
7929
7930  function openBuilder() {
7931    local.customDraft = draftSourceFromSharedModel();
7932    const body = `<div class="nmw-builder-toolbar"><button type="button" data-builder-add>Add Manual Item</button><button type="button" data-builder-reset>Reset Draft</button><button type="button" data-builder-validate>Validate</button><button type="button" data-builder-export="json">Export JSON</button><button type="button" data-builder-export="csv">Export CSV</button><button type="button" data-builder-export="payload">Export Duda Payload</button></div><p class="nmw-builder-help">Drag rows to reorder. Use ← and → to promote or demote. Maximum depth is three levels.</p><div class="nmw-builder-list" data-builder-list>${renderBuilderRows(flattenDraft(local.customDraft))}</div><div class="nmw-builder-status" data-builder-status></div>`;
7933    openModal('Custom Navigation Builder', body, 'builder');
7934  }
7935
7936  function openModal(title, body, type) {
7937    closeModal();
7938    const modal = document.createElement('div');
7939    modal.className = 'nmw-modal-backdrop';
7940    modal.dataset.nmwModal = type;
7941    modal.innerHTML = `<section class="nmw-modal" role="dialog" aria-modal="true" aria-labelledby="nmw-modal-title-${instanceId}"><header class="nmw-modal-header"><h2 id="nmw-modal-title-${instanceId}">${escapeHtml(title)}</h2><button type="button" class="nmw-modal-close" data-nmw-close-modal aria-label="Close">×</button></header><div class="nmw-modal-body">${body}</div></section>`;
7942    document.body.appendChild(modal);
7943    modal.querySelector('[data-nmw-close-modal]').focus();
7944    if (type === 'builder') attachBuilderInteractions(modal);
7945  }
7946
7947  function closeModal() {
7948    document.querySelectorAll('.nmw-modal-backdrop[data-nmw-modal]').forEach(node => node.remove());
7949  }
7950
7951  function findDraftLocation(key, items = local.customDraft, parent = null) {
7952    for (let index = 0; index < items.length; index += 1) {
7953      if (items[index].key === key) return { item: items[index], items, index, parent };
7954      const found = findDraftLocation(key, items[index].children, items[index]);
7955      if (found) return found;
7956    }
7957    return null;
7958  }
7959
7960  function recalculateDraft(items = local.customDraft, depth = 0, parentKey = null) {
7961    items.forEach((item, order) => {
7962      item.depth = depth;
7963      item.parentKey = parentKey;
7964      item.order = order;
7965      item.hasChildren = item.children.length > 0;
7966      recalculateDraft(item.children, depth + 1, item.key);
7967    });
7968  }
7969
7970  function refreshBuilder(modal, message = '') {
7971    recalculateDraft();
7972    const list = modal.querySelector('[data-builder-list]');
7973    if (list) list.innerHTML = renderBuilderRows(flattenDraft(local.customDraft));
7974    const status = modal.querySelector('[data-builder-status]');
7975    if (status) status.textContent = message;
7976  }
7977
7978  function validateDraft() {
7979    const errors = [];
7980    const keys = new Set();
7981    function walk(items, ancestors = []) {
7982      items.forEach(item => {
7983        if (keys.has(item.key)) errors.push(`Duplicate key: ${item.key}`);
7984        keys.add(item.key);
7985        if (item.depth > MAX_DEPTH) errors.push(`${item.label} exceeds three levels.`);
7986        if (ancestors.includes(item.key)) errors.push(`${item.label} is nested beneath itself.`);
7987        if (item.type === 'page' && !item.url) errors.push(`${item.label} is a page without a URL.`);
7988        walk(item.children, [...ancestors, item.key]);
7989      });
7990    }
7991    walk(local.customDraft || []);
7992    return errors;
7993  }
7994
7995  function addManualDraftItem() {
7996    const label = window.prompt('Manual item label:');
7997    if (!readString(label, '')) return;
7998    const type = window.prompt('Type "page" or "folder":', 'page');
7999    const isFolder = readString(type, 'page').toLowerCase() === 'folder';
8000    const url = isFolder ? '' : readString(window.prompt('URL:', '/'), '');
8001    const key = `manual:${slugify(label)}-${Math.random().toString(36).slice(2, 6)}`;
8002    local.customDraft.push({
8003      key,
8004      parentKey: null,
8005      source: 'manual',
8006      type: isFolder ? 'folder' : 'page',
8007      presentation: 'standard',
8008      megaGroup: 0,
8009      depth: 0,
8010      order: local.customDraft.length,
8011      label,
8012      slug: slugify(label),
8013      canonicalPath: isFolder ? '' : canonicalPath(url),
8014      url,
8015      target: '_self',
8016      isExternal: isExternalUrl(url),
8017      hasChildren: false,
8018      children: []
8019    });
8020  }
8021
8022  function moveDraftLeft(key) {
8023    const location = findDraftLocation(key);
8024    if (!location || !location.parent) return;
8025    const parentLocation = findDraftLocation(location.parent.key);
8026    location.items.splice(location.index, 1);
8027    parentLocation.items.splice(parentLocation.index + 1, 0, location.item);
8028  }
8029
8030  function moveDraftRight(key) {
8031    const location = findDraftLocation(key);
8032    if (!location || location.index === 0 || location.item.depth >= MAX_DEPTH) return;
8033    const previousSibling = location.items[location.index - 1];
8034    location.items.splice(location.index, 1);
8035    previousSibling.children.push(location.item);
8036  }
8037
8038  function removeDraftItem(key) {
8039    const location = findDraftLocation(key);
8040    if (!location) return;
8041    location.items.splice(location.index, 1);
8042  }
8043
8044  function reorderDraft(dragKey, targetKey) {
8045    if (!dragKey || dragKey === targetKey) return;
8046    const drag = findDraftLocation(dragKey);
8047    const target = findDraftLocation(targetKey);
8048    if (!drag || !target || drag.items !== target.items) return;
8049    drag.items.splice(drag.index, 1);
8050    const newTarget = findDraftLocation(targetKey);
8051    newTarget.items.splice(newTarget.index, 0, drag.item);
8052  }
8053
8054  function customRowsFromDraft() {
8055    recalculateDraft();
8056    return flattenDraft(local.customDraft).map(item => ({
8057      'ITEM KEY': item.key,
8058      'PARENT KEY': item.parentKey || '',
8059      'PAGE NAME': item.label,
8060      SOURCE: item.source,
8061      TYPE: item.type,
8062      PRESENTATION: item.presentation,
8063      'MEGA GROUP': item.megaGroup,
8064      DEPTH: item.depth,
8065      ORDER: item.order,
8066      URL: item.url || '',
8067      'CANONICAL PATH': item.canonicalPath || '',
8068      SLUG: item.slug || '',
8069      TARGET: item.target || '_self',
8070      'IS EXTERNAL': item.isExternal,
8071      'SHOW ITEM': true
8072    }));
8073  }
8074
8075  function downloadText(filename, content, mime = 'text/plain') {
8076    const blob = new Blob([content], { type: `${mime};charset=utf-8` });
8077    const url = URL.createObjectURL(blob);
8078    const anchor = document.createElement('a');
8079    anchor.href = url;
8080    anchor.download = filename;
8081    document.body.appendChild(anchor);
8082    anchor.click();
8083    anchor.remove();
8084    URL.revokeObjectURL(url);
8085  }
8086
8087  function exportDraft(format) {
8088    const errors = validateDraft();
8089    if (errors.length) {
8090      window.alert(`Fix these issues before exporting:\n\n${errors.join('\n')}`);
8091      return;
8092    }
8093    const rows = customRowsFromDraft();
8094    if (format === 'json') {
8095      downloadText('CUSTOMNAV.json', JSON.stringify(rows, null, 2), 'application/json');
8096      return;
8097    }
8098    if (format === 'payload') {
8099      downloadText('CUSTOMNAV-duda-payload.json', JSON.stringify({ data: rows }, null, 2), 'application/json');
8100      return;
8101    }
8102    const headers = Object.keys(rows[0] || {});
8103    const escapeCsv = value => `"${String(value == null ? '' : value).replace(/"/g, '""')}"`;
8104    const csv = [headers.map(escapeCsv).join(','), ...rows.map(row => headers.map(header => escapeCsv(row[header])).join(','))].join('\n');
8105    downloadText('CUSTOMNAV.csv', csv, 'text/csv');
8106  }
8107
8108  function attachBuilderInteractions(modal) {
8109    let draggedKey = '';
8110    modal.addEventListener('dragstart', event => {
8111      const row = event.target.closest('[data-builder-key]');
8112      if (!row) return;
8113      draggedKey = row.dataset.builderKey;
8114      event.dataTransfer.effectAllowed = 'move';
8115    });
8116    modal.addEventListener('dragover', event => {
8117      if (event.target.closest('[data-builder-key]')) event.preventDefault();
8118    });
8119    modal.addEventListener('drop', event => {
8120      const target = event.target.closest('[data-builder-key]');
8121      if (!target) return;
8122      event.preventDefault();
8123      reorderDraft(draggedKey, target.dataset.builderKey);
8124      refreshBuilder(modal, 'Draft reordered.');
8125    });
8126    modal.addEventListener('click', event => {
8127      const left = event.target.closest('[data-builder-left]');
8128      const right = event.target.closest('[data-builder-right]');
8129      const remove = event.target.closest('[data-builder-remove]');
8130      if (left) { moveDraftLeft(left.dataset.key); refreshBuilder(modal, 'Item promoted.'); return; }
8131      if (right) { moveDraftRight(right.dataset.key); refreshBuilder(modal, 'Item demoted.'); return; }
8132      if (remove) { removeDraftItem(remove.dataset.key); refreshBuilder(modal, 'Item removed from draft.'); return; }
8133      if (event.target.closest('[data-builder-add]')) { addManualDraftItem(); refreshBuilder(modal, 'Manual item added.'); return; }
8134      if (event.target.closest('[data-builder-reset]')) { local.customDraft = draftSourceFromSharedModel(); refreshBuilder(modal, 'Draft reset.'); return; }
8135      if (event.target.closest('[data-builder-validate]')) {
8136        const errors = validateDraft();
8137        refreshBuilder(modal, errors.length ? errors.join(' ') : 'Structure is valid.');
8138        return;
8139      }
8140      const exportButton = event.target.closest('[data-builder-export]');
8141      if (exportButton) exportDraft(exportButton.dataset.builderExport);
8142    });
8143  }
8144
8145  function handleToolClick(event) {
8146    if (event.target.closest('[data-nmw-open-tree]')) openTreePreview();
8147    if (event.target.closest('[data-nmw-open-builder]')) openBuilder();
8148  }
8149
8150  function handleModalClick(event) {
8151    const backdrop = event.target.closest('.nmw-modal-backdrop');
8152    if (!backdrop) return;
8153    if (event.target.matches('.nmw-modal-backdrop') || event.target.closest('[data-nm
8153w-close-modal]')) closeModal();
8154  }
8155
8156  function handleModalKeydown(event) {
8157    if (event.key === 'Escape' && document.querySelector('.nmw-modal-backdrop')) closeModal();
8158  }
8159
8160  // ---------------------------------------------------------------------------
8161  // Reset action hooks
8162  // ---------------------------------------------------------------------------
8163
8164  // Duda panel switches cannot directly erase saved panel values. These hooks
8165  // expose one-shot reset requests as data attributes/classes so the panel or a
8166  // future config migration can repopulate defaults without touching structure.
8167  function applyResetRequests() {
8168    if (readBoolean(configValueFrom(cfg, 'restoreFullDefaults'), false) && widgetMode === 'full') root.dataset.nmwRestoreDefaults = 'full-requested';
8169    if (readBoolean(configValueFrom(cfg, 'restoreMirrorDefaults'), false) && widgetMode === 'mirror') root.dataset.nmwRestoreDefaults = 'mirror-requested';
8170  }
8171
8172  // ---------------------------------------------------------------------------
8173  // Cleanup
8174  // ---------------------------------------------------------------------------
8175
8176  function cleanup(reason = 'removed') {
8177    if (local.cleaned) return;
8178    local.cleaned = true;
8179    clearTimeout(local.rebuildTimer);
8180    clearTimeout(local.nativeRetryTimer);
8181    local.hoverTimers.forEach(timer => clearTimeout(timer));
8182    local.anchorTimers.forEach(timer => clearTimeout(timer));
8183    local.anchorTimers.clear();
8184    local.closeTimers.forEach(timer => clearTimeout(timer));
8185    local.closeTimers.clear();
8186    local.nativeObserver?.disconnect();
8187    local.nativeFinderObserver?.disconnect();
8188    local.manualObserver?.disconnect();
8189    local.removalObserver?.disconnect();
8190    if (typeof local.historyCleanup === 'function') local.historyCleanup();
8191    local.eventCleanups.splice(0).forEach(remove => {
8192      try { remove(); } catch (error) {}
8193    });
8194    window.removeEventListener('nmw:data-changed', handleMirrorDataChanged);
8195    window.removeEventListener('nmw:state-changed', handleSharedStateChanged);
8196    window.removeEventListener('nmw:url-changed', handleUrlChanged);
8197    window.removeEventListener('popstate', handleUrlChanged);
8198    window.removeEventListener('hashchange', handleUrlChanged);
8199    document.removeEventListener('pointerdown', handleOutsidePointer, true);
8200    document.removeEventListener('click', handleModalClick);
8201    document.removeEventListener('keydown', handleModalKeydown);
8202    toolsHost?.removeEventListener('click', handleToolClick);
8203    closeModal();
8204    releaseOwnership();
8205
8206    // Remove this closure only when it is still the registered lifecycle for
8207    // the root. A newer execution may already have replaced it.
8208    if (window.NMW_RUNTIME?.instances?.get(root) === cleanup) {
8209      window.NMW_RUNTIME.instances.delete(root);
8210    }
8211
8212    console.info('[NMW] Cleaned up instance', { widgetMode, instanceId, reason });
8213  }
8214
8215  // Duda does not provide a universal destroy callback for all widget contexts,
8216  // so this observer performs cleanup if the widget root is removed.
8217  local.removalObserver = new MutationObserver(() => {
8218    if (!document.documentElement.contains(root)) {
8219      local.removalObserver.disconnect();
8220      cleanup();
8221    }
8222  });
8223  local.removalObserver.observe(document.documentElement, { childList: true, subtree: true });
8224
8225  // ---------------------------------------------------------------------------
8226  // Full and Mirror initialization
8227  // ---------------------------------------------------------------------------
8228
8229  function handleMirrorDataChanged() {
8230    if (widgetMode !== 'mirror') return;
8231    // Full may have changed Design Panel values without changing collection
8232    // structure. Refresh inherited Mirror classes and CSS variables before
8233    // rendering the shared model.
8234    applyDesignSettings('mirror');
8235    renderNavigation(window.NMW.data || [], 'mirror');
8236    updateRootStatus();
8237  }
8238
8239  function handleSharedStateChanged() {
8240    updateRootStatus();
8241  }
8242
8243  function initializeFull() {
8244    // This instance now owns the Full widget. Reset stale editor state left by
8245    // an interrupted prior execution, then rebuild with the current panel data.
8246    window.NMW.loading = false;
8247    applyDesignSettings('full');
8248    applyResetRequests();
8249    attachDelegatedInteractions();
8250    attachManualSourceObserver();
8251    beginNativeDiscovery();
8252    local.historyCleanup = observeHistoryChanges();
8253    listen(window, 'nmw:url-changed', handleUrlChanged);
8254    listen(window, 'popstate', handleUrlChanged);
8255    listen(window, 'hashchange', handleUrlChanged);
8256    if (toolsHost) listen(toolsHost, 'click', handleToolClick);
8257    listen(document, 'click', handleModalClick);
8258    listen(document, 'keydown', handleModalKeydown);
8259    scheduleRebuild(true);
8260  }
8261
8262  function initializeMirror() {
8263    applyDesignSettings('mirror');
8264    applyResetRequests();
8265    attachDelegatedInteractions();
8266    local.historyCleanup = observeHistoryChanges();
8267    listen(window, 'nmw:data-changed', handleMirrorDataChanged);
8268    listen(window, 'nmw:state-changed', handleSharedStateChanged);
8269    listen(window, 'nmw:url-changed', handleUrlChanged);
8270    listen(window, 'popstate', handleUrlChanged);
8271    listen(window, 'hashchange', handleUrlChanged);
8272    if (toolsHost) listen(toolsHost, 'click', handleToolClick);
8273    listen(document, 'click', handleModalClick);
8274    listen(document, 'keydown', handleModalKeydown);
8275    updateRootStatus();
8276    if (window.NMW.ready) renderNavigation(window.NMW.data || [], 'mirror');
8277  }
8278
8279  try {
8280    if (!claimOwnership()) return;
8281    root.dataset.nmwInstance = instanceId;
8282
8283    // Register this lifecycle only after ownership succeeds. The next Duda
8284    // panel execution on the same root will call this cleanup before starting.
8285    window.NMW_RUNTIME.instances.set(root, cleanup);
8286    console.info('[NMW] Initialized', { widgetMode, instanceId, renderHost });
8287    updateRootStatus();
8288    if (widgetMode === 'full') initializeFull();
8289    else initializeMirror();
8290  } catch (error) {
8291    console.error('[NMW] Startup failed', error);
8292    if (renderHost) {
8293      renderHost.innerHTML = `<div class="nmw-empty">Navigation startup failed: ${escapeHtml(error && error.message ? error.message : String(error))}</div>`;
8294    }
8295  }
8296});
8297    };
8298</script>
8298
8299
8300<script>
8301    window.customWidgetsFunctions["e0b1e5b2d4394e69a98bd276fd6afe8b~8"] = function (element, data, api) {
8302        window.dmAPI.runOnReady('HH1', () => {
8303
8304  const STYLE_ID = 'HH1-theme-style';
8305  const HH1_SELECTOR = '.widget-wrapper.HH1';
8306
8307  let updateTimer = null;
8308  let lastSignature = '';
8309
8310  /**
8311   * Find a usable H1 that is NOT inside the HH1 widget.
8312   */
8313  function findThemeH1() {
8314    const candidates = Array.from(
8315      document.querySelectorAll('#dm h1, h1')
8316    );
8317
8318    for (const el of candidates) {
8319
8320      // Do not use our own HH1 widget as the source
8321      if (el.closest(HH1_SELECTOR)) {
8322        continue;
8323      }
8324
8325      // Ignore disconnected elements
8326      if (!el.isConnected) {
8327        continue;
8328      }
8329
8330      const computed = window.getComputedStyle(el);
8331
8332      // Ignore elements that are completely unavailable
8333      if (
8334        computed.display === 'none' ||
8335        computed.visibility === 'hidden'
8336      ) {
8337        continue;
8338      }
8339
8340      // Make sure we actually got useful typography
8341      if (!computed.fontFamily || !computed.fontSize) {
8342        continue;
8343      }
8344
8345      return el;
8346    }
8347
8348    return null;
8349  }
8350
8351
8352  /**
8353   * Read H1 typography and apply it to HH1 widgets.
8354   */
8355  function applyTheme() {
8356
8357    const h1Sample = findThemeH1();
8358
8359    if (!h1Sample) {
8360      return false;
8361    }
8362
8363    const computed = window.getComputedStyle(h1Sample);
8364
8365    const styles = {
8366      fontFamily: computed.fontFamily,
8367      fontSize: computed.fontSize,
8368      fontWeight: computed.fontWeight,
8369      lineHeight: computed.lineHeight,
8370      letterSpacing: computed.letterSpacing,
8371      textTransform: computed.textTransform,
8372      fontStyle: computed.fontStyle,
8373      fontVariant: computed.fontVariant,
8374      fontStretch: computed.fontStretch
8375    };
8376
8377    /*
8378     * Create a signature so we're not constantly rewriting
8379     * the stylesheet when nothing has actually changed.
8380     */
8381    const signature = JSON.stringify(styles);
8382
8383    if (signature === lastSignature) {
8384      return true;
8385    }
8386
8387    lastSignature = signature;
8388
8389
8390    /*
8391     * Target the widget plus all common Duda RTE descendants.
8392     * The wildcard selector is intentional so inline/nested
8393     * typography cannot override the inherited H1 theme.
8394     */
8395    const selectors = [
8396      '#dm .dmInner .widget-wrapper.HH1 .dmNewParagraph',
8397      '#dm .dmInner .widget-wrapper.HH1 .dmNewParagraph p',
8398      '#dm .dmInner .widget-wrapper.HH1 .dmNewParagraph span',
8399      '#dm .dmInner .widget-wrapper.HH1 .dmNewParagraph .rteBlock',
8400      '#dm .dmInner .widget-wrapper.HH1 .dmNewParagraph .rteBlock *',
8401
8402      '.dmInner .widget-wrapper.HH1 .dmNewParagraph',
8403      '.dmInner .widget-wrapper.HH1 .dmNewParagraph p',
8404      '.dmInner .widget-wrapper.HH1 .dmNewParagraph span',
8405      '.dmInner .widget-wrapper.HH1 .dmNewParagraph .rteBlock',
8406      '.dmInner .widget-wrapper.HH1 .dmNewParagraph .rteBlock *',
8407
8408      '.widget-wrapper.HH1 .dmNewParagraph',
8409      '.widget-wrapper.HH1 .dmNewParagraph p',
8410      '.widget-wrapper.HH1 .dmNewParagraph span',
8411      '.widget-wrapper.HH1 .dmNewParagraph .rteBlock',
8412      '.widget-wrapper.HH1 .dmNewParagraph .rteBlock *'
8413    ];
8414
8415    const css = `
8416${selectors.join(',\n')} {
8417  font-family: ${styles.fontFamily} !important;
8418  font-size: ${styles.fontSize} !important;
8419  font-weight: ${styles.fontWeight} !important;
8420  line-height: ${styles.lineHeight} !important;
8421  letter-spacing: ${styles.letterSpacing} !important;
8422  text-transform: ${styles.textTransform} !important;
8423  font-style: ${styles.fontStyle} !important;
8424  font-variant: ${styles.fontVariant} !important;
8425  font-stretch: ${styles.fontStretch} !important;
8426}
8427`;
8428
8429    let styleTag = document.getElementById(STYLE_ID);
8430
8431    if (!styleTag) {
8432      styleTag = document.createElement('style');
8433      styleTag.id = STYLE_ID;
8434      styleTag.setAttribute('data-HH1', 'true');
8435      document.head.appendChild(styleTag);
8436    }
8437
8438    styleTag.textContent = css;
8439
8440    return true;
8441  }
8442
8443
8444  /**
8445   * Debounce updates so Duda can make several DOM changes
8446   * without us recalculating after every individual mutation.
8447   */
8448  function scheduleUpdate(delay = 100) {
8449
8450    clearTimeout(updateTimer);
8451
8452    updateTimer = setTimeout(() => {
8453      applyTheme();
8454    }, delay);
8455  }
8456
8457
8458  /**
8459   * INITIAL LOAD
8460   *
8461   * Run several times because Duda/theme/font loading can occur
8462   * after dmAPI.runOnReady().
8463   */
8464  applyTheme();
8465
8466  setTimeout(applyTheme, 250);
8467  setTimeout(applyTheme, 500);
8468  setTimeout(applyTheme, 1000);
8469  setTimeout(applyTheme, 2000);
8470  setTimeout(applyTheme, 4000);
8471
8472
8473  /**
8474   * WATCH DUDA DOM CHANGES
8475   *
8476   * Dynamic collections, responsive rendering, widgets, and
8477   * editor/preview changes can alter the page after initial load.
8478   */
8479  const observer = new MutationObserver((mutations) => {
8480
8481    let relevantChange = false;
8482
8483    for (const mutation of mutations) {
8484
8485      /*
8486       * Ignore mutations caused only by our own style tag.
8487       */
8488      if (
8489        mutation.target &&
8490        (
8491          mutation.target.id === STYLE_ID ||
8492          mutation.target.closest?.(`#${STYLE_ID}`)
8493        )
8494      ) {
8495        continue;
8496      }
8497
8498      relevantChange = true;
8499      break;
8500    }
8501
8502    if (relevantChange) {
8503      scheduleUpdate(150);
8504    }
8505
8506  });
8507
8508  observer.observe(document.body, {
8509    childList: true,
8510    subtree: true,
8511    attributes: true,
8512    attributeFilter: [
8513      'class',
8514      'style'
8515    ]
8516  });
8517
8518
8519  /**
8520   * RESPONSIVE CHANGES
8521   *
8522   * H1 typography may change between desktop/tablet/mobile.
8523   */
8524  window.addEventListener('resize', () => {
8525    scheduleUpdate(150);
8526  });
8527
8528
8529  /**
8530   * ORIENTATION CHANGE
8531   */
8532  window.addEventListener('orientationchange', () => {
8533    scheduleUpdate(250);
8534  });
8535
8536
8537  /**
8538   * FONT LOADING
8539   *
8540   * Reapply after web fonts finish loading.
8541   */
8542  if (document.fonts && document.fonts.ready) {
8543
8544    document.fonts.ready.then(() => {
8545
8546      // Force refresh even if we previously captured fallback font
8547      lastSignature = '';
8548
8549      applyTheme();
8550
8551    }).catch(() => {
8552      // Nothing required
8553    });
8554
8555  }
8556
8557});
8558    };
8559</script>
8559
8560
8561<script>
8562    window.customWidgetsFunctions["9015bcbf1ace437aa6f0e66d5bb2e7d3~25"] = function (element, data, api) {
8563        $(document).ready(function(){
8564    $.getScript('https://widget.hibu.us/static/js/index.js')
8565    //$.getScript('//survlywidget-uat.firebaseapp.com/static/js/index.js')
8566    .done(function(){
8567        dmAPI.runOnReady('hibu_header_reviews',function(){
8568            window.renderApp();
8569        });
8570        
8571    })
8572    .fail(function(){
8573        console.log("failed to load header reviews js file")
8574    });
8575    
8576    element.children[0].removeAttribute(`rteblock"`)
8577    var merchantId = data.config.merchantId
8578    merchantId = merchantId.replace(/<p.*?>/g, '')
8579    merchantId = merchantId.replace(/&quot;/g, '"')
8580    merchantId = merchantId.replace(/\<\/p\>/g, '')
8581    
8582    element.children[0].setAttribute('data-merchant', merchantId)
8583})
8584
8585
8586    };
8587</script>
8587
8588
8589<script>
8590    window.customWidgetsFunctions["ca4188fe92024514a23a858f63ab6658~18"] = function (element, data, api) {
8591        null
8592    };
8593</script>
8593
8594
8595<script>
8596    window.customWidgetsFunctions["2133bfee2cb24e0c9db9468154881b94~3"] = function (element, data, api) {
8597        window.dmAPI.runOnReady('HH2', () => {
8598
8599  const STYLE_ID = 'HH2-theme-style';
8600  const HH2_SELECTOR = '.widget-wrapper.HH2';
8601
8602  let updateTimer = null;
8603  let lastSignature = '';
8604
8605  /**
8606   * Find a usable H2 that is NOT inside the HH2 widget.
8607   */
8608  function findThemeH2() {
8609    const candidates = Array.from(
8610      document.querySelectorAll('#dm h2, h2')
8611    );
8612
8613    for (const el of candidates) {
8614
8615      // Do not use our own HH2 widget as the source
8616      if (el.closest(HH2_SELECTOR)) {
8617        continue;
8618      }
8619
8620      // Ignore disconnected elements
8621      if (!el.isConnected) {
8622        continue;
8623      }
8624
8625      const computed = window.getComputedStyle(el);
8626
8627      // Ignore elements that are completely unavailable
8628      if (
8629        computed.display === 'none' ||
8630        computed.visibility === 'hidden'
8631      ) {
8632        continue;
8633      }
8634
8635      // Make sure we actually got useful typography
8636      if (!computed.fontFamily || !computed.fontSize) {
8637        continue;
8638      }
8639
8640      return el;
8641    }
8642
8643    return null;
8644  }
8645
8646
8647  /**
8648   * Read H2 typography and apply it to HH2 widgets.
8649   */
8650  function applyTheme() {
8651
8652    const h2Sample = findThemeH2();
8653
8654    if (!h2Sample) {
8655      return false;
8656    }
8657
8658    const computed = window.getComputedStyle(h2Sample);
8659
8660    const styles = {
8661      fontFamily: computed.fontFamily,
8662      fontSize: computed.fontSize,
8663      fontWeight: computed.fontWeight,
8664      lineHeight: computed.lineHeight,
8665      letterSpacing: computed.letterSpacing,
8666      textTransform: computed.textTransform,
8667      fontStyle: computed.fontStyle,
8668      fontVariant: computed.fontVariant,
8669      fontStretch: computed.fontStretch
8670    };
8671
8672    /*
8673     * Create a signature so we're not constantly rewriting
8674     * the stylesheet when nothing has actually changed.
8675     */
8676    const signature = JSON.stringify(styles);
8677
8678    if (signature === lastSignature) {
8679      return true;
8680    }
8681
8682    lastSignature = signature;
8683
8684
8685    /*
8686     * Target the widget plus all common Duda RTE descendants.
8687     * The wildcard selector is intentional so inline/nested
8688     * typography cannot override the inherited H2 theme.
8689     */
8690    const selectors = [
8691      '#dm .dmInner .widget-wrapper.HH2 .dmNewParagraph',
8692      '#dm .dmInner .widget-wrapper.HH2 .dmNewParagraph p',
8693      '#dm .dmInner .widget-wrapper.HH2 .dmNewParagraph span',
8694      '#dm .dmInner .widget-wrapper.HH2 .dmNewParagraph .rteBlock',
8695      '#dm .dmInner .widget-wrapper.HH2 .dmNewParagraph .rteBlock *',
8696
8697      '.dmInner .widget-wrapper.HH2 .dmNewParagraph',
8698      '.dmInner .widget-wrapper.HH2 .dmNewParagraph p',
8699      '.dmInner .widget-wrapper.HH2 .dmNewParagraph span',
8700      '.dmInner .widget-wrapper.HH2 .dmNewParagraph .rteBlock',
8701      '.dmInner .widget-wrapper.HH2 .dmNewParagraph .rteBlock *',
8702
8703      '.widget-wrapper.HH2 .dmNewParagraph',
8704      '.widget-wrapper.HH2 .dmNewParagraph p',
8705      '.widget-wrapper.HH2 .dmNewParagraph span',
8706      '.widget-wrapper.HH2 .dmNewParagraph .rteBlock',
8707      '.widget-wrapper.HH2 .dmNewParagraph .rteBlock *'
8708    ];
8709
8710    const css = `
8711${selectors.join(',\n')} {
8712  font-family: ${styles.fontFamily} !important;
8713  font-size: ${styles.fontSize} !important;
8714  font-weight: ${styles.fontWeight} !important;
8715  line-height: ${styles.lineHeight} !important;
8716  letter-spacing: ${styles.letterSpacing} !important;
8717  text-transform: ${styles.textTransform} !important;
8718  font-style: ${styles.fontStyle} !important;
8719  font-variant: ${styles.fontVariant} !important;
8720  font-stretch: ${styles.fontStretch} !important;
8721}
8722`;
8723
8724    let styleTag = document.getElementById(STYLE_ID);
8725
8726    if (!styleTag) {
8727      styleTag = document.createElement('style');
8728      styleTag.id = STYLE_ID;
8729      styleTag.setAttribute('data-HH2', 'true');
8730      document.head.appendChild(styleTag);
8731    }
8732
8733    styleTag.textContent = css;
8734
8735    return true;
8736  }
8737
8738
8739  /**
8740   * Debounce updates so Duda can make several DOM changes
8741   * without us recalculating after every individual mutation.
8742   */
8743  function scheduleUpdate(delay = 100) {
8744
8745    clearTimeout(updateTimer);
8746
8747    updateTimer = setTimeout(() => {
8748      applyTheme();
8749    }, delay);
8750  }
8751
8752
8753  /**
8754   * INITIAL LOAD
8755   *
8756   * Run several times because Duda/theme/font loading can occur
8757   * after dmAPI.runOnReady().
8758   */
8759  applyTheme();
8760
8761  setTimeout(applyTheme, 250);
8762  setTimeout(applyTheme, 500);
8763  setTimeout(applyTheme, 1000);
8764  setTimeout(applyTheme, 2000);
8765  setTimeout(applyTheme, 4000);
8766
8767
8768  /**
8769   * WATCH DUDA DOM CHANGES
8770   */
8771  const observer = new MutationObserver((mutations) => {
8772
8773    let relevantChange = false;
8774
8775    for (const mutation of mutations) {
8776
8777      /*
8778       * Ignore mutations caused only by our own style tag.
8779       */
8780      if (
8781        mutation.target &&
8782        (
8783          mutation.target.id === STYLE_ID ||
8784          mutation.target.closest?.(`#${STYLE_ID}`)
8785        )
8786      ) {
8787        continue;
8788      }
8789
8790      relevantChange = true;
8791      break;
8792    }
8793
8794    if (relevantChange) {
8795      scheduleUpdate(150);
8796    }
8797
8798  });
8799
8800  observer.observe(document.body, {
8801    childList: true,
8802    subtree: true,
8803    attributes: true,
8804    attributeFilter: [
8805      'class',
8806      'style'
8807    ]
8808  });
8809
8810
8811  /**
8812   * RESPONSIVE CHANGES
8813   *
8814   * H2 typography may change between desktop/tablet/mobile.
8815   */
8816  window.addEventListener('resize', () => {
8817    scheduleUpdate(150);
8818  });
8819
8820
8821  /**
8822   * ORIENTATION CHANGE
8823   */
8824  window.addEventListener('orientationchange', () => {
8825    scheduleUpdate(250);
8826  });
8827
8828
8829  /**
8830   * FONT LOADING
8831   *
8832   * Reapply after web fonts finish loading.
8833   */
8834  if (document.fonts && document.fonts.ready) {
8835
8836    document.fonts.ready.then(() => {
8837
8838      // Force refresh even if we previously captured fallback font
8839      lastSignature = '';
8840
8841      applyTheme();
8842
8843    }).catch(() => {
8844      // Nothing required
8845    });
8846
8847  }
8848
8849});
8850    };
8851</script>
8851
8852
8853<script>
8854    window.customWidgetsFunctions["0c06e70da5474879a21daa0d3b1192c0~3"] = function (element, data, api) {
8855        window.dmAPI.runOnReady('cb1-h1-theme', () => {
8856
8857  function applyTheme() {
8858    // Try multiple candidate selectors for the h1 style source
8859    // to work in both the editor and the published/preview environment
8860    const candidates = [
8861      '#dm h1',
8862      'h1',
8863    ];
8864
8865    let h1Sample = null;
8866    for (const sel of candidates) {
8867      h1Sample = document.querySelector(sel);
8868      if (h1Sample) break;
8869    }
8870
8871    if (!h1Sample) return false;
8872
8873    const computed = window.getComputedStyle(h1Sample);
8874
8875    const fontFamily    = computed.fontFamily;
8876    const fontSize      = computed.fontSize;
8877    const fontWeight    = computed.fontWeight;
8878    const lineHeight    = computed.lineHeight;
8879    const color         = computed.color;
8880    const letterSpacing = computed.letterSpacing;
8881    const textTransform = computed.textTransform;
8882    const fontStyle     = computed.fontStyle;
8883
8884    // Skip if nothing meaningful was computed yet
8885    if (!fontFamily || fontFamily === '') return false;
8886
8887    const baseSelectors = [
8888      `#dm .dmInner .widget-wrapper.DCBH1 .dmNewParagraph`,
8889      `#dm .dmInner .widget-wrapper.DCBH1 .dmNewParagraph p`,
8890      `#dm .dmInner .widget-wrapper.DCBH1 .dmNewParagraph .rteBlock`,
8891      `.dmInner .widget-wrapper.DCBH1 .dmNewParagraph`,
8892      `.dmInner .widget-wrapper.DCBH1 .dmNewParagraph p`,
8893      `.dmInner .widget-wrapper.DCBH1 .dmNewParagraph .rteBlock`,
8894      `.widget-wrapper.DCBH1 .dmNewParagraph`,
8895      `.widget-wrapper.DCBH1 .dmNewParagraph p`,
8896      `.widget-wrapper.DCBH1 .dmNewParagraph .rteBlock`,
8897    ];
8898
8899    const allSelectors = baseSelectors.join(',\n    ');
8900
8901    const css = `
8902    ${allSelectors} {
8903      font-family: ${fontFamily} !important;
8904      font-size: ${fontSize} !important;
8905      font-weight: ${fontWeight} !important;
8906      line-height: ${lineHeight} !important;
8907      color: ${color} !important;
8908      letter-spacing: ${letterSpacing} !important;
8909      text-transform: ${textTransform} !important;
8910      font-style: ${fontStyle} !important;
8911    }
8912  `;
8913
8914    // Remove any previously injected tag before re-injecting
8915    const existing = document.querySelector('style[data-cb1-h1-theme]');
8916    if (existing) existing.remove();
8917
8918    const styleTag = document.createElement('style');
8919    styleTag.setAttribute('data-cb1-h1-theme', 'true');
8920    styleTag.textContent = css;
8921    document.head.appendChild(styleTag);
8922
8923    return true;
8924  }
8925
8926  // Try immediately
8927  if (applyTheme()) return;
8928
8929  // If not ready yet, retry with increasing delays to handle
8930  // async rendering in preview / published environments
8931  let attempts = 0;
8932  const maxAttempts = 20;
8933  const interval = setInterval(() => {
8934    attempts++;
8935    if (applyTheme() || attempts >= maxAttempts) {
8936      clearInterval(interval);
8937    }
8938  }, 300);
8939});
8940
8941    };
8942</script>
8942
8943
8944<script>
8945    window.customWidgetsFunctions["3e38ec713838428586c343d139994b6f~58"] = function (element, data, api) {
8946        window.dmAPI.runOnReady('navGridWidget', () => {
8947  const container = element.querySelector('#nav-grid-widget');
8948  if(!container) return;
8949
8950  const cleanText = (el)=> (el?.textContent||'').trim().replace(/\s+/g,' ');
8951  const cleanUrl  = (href)=>{
8952    try{
8953      const u=new URL(href,location.origin);
8954      const path = u.pathname.replace(/\/+$/,'')||'/';
8955      return path + u.search; // Preserve query parameters for preview mode
8956    }
8957    catch{ return '#'; }
8958  };
8959
8960  // Get column count from config (grid uses this; masonry uses this)
8961  const columnCount = parseInt(data.config['columnCount'] || '3', 10);
8962
8963  // Get exclusion list from config
8964  const excludeList = (data.config['excludeParents'] || '')
8965    .split(',')
8966    .map(s => s.trim().toLowerCase())
8967    .filter(s => s.length > 0);
8968
8969  // Get inclusion list from config
8970  const includeList = (data.config['includeParents'] || '')
8971    .split(',')
8972    .map(s => s.trim().toLowerCase())
8973    .filter(s => s.length > 0);
8974
8975  // Get chevron icon from config (Option B default: right-pointing)
8976  const chevronIcon = data.config['chevronIcon'] || '▸';
8977
8978  // Get hover setting from config
8979  const openOnHover = data.config['openOnHover'] || false;
8980
8981  // Get expand all setting from config
8982  const expandAll = data.config['expandAll'] || false;
8983
8984  // Get masonry setting from config (NEW - separate from expandAll)
8985  const useMasonry = data.config['useMasonry'] || false;
8986
8987  // Set CSS custom property for column count - this works for both grid AND masonry
8988  container.style.setProperty('--column-count', String(columnCount));
8989
8990  // Masonry is now controlled by its own toggle
8991  if(useMasonry) {
8992    container.setAttribute('data-masonry', 'true');
8993  } else {
8994    container.removeAttribute('data-masonry');
8995  }
8996
8997  // Set uppercase data attributes from config
8998  const uppercaseParents = data.config['uppercaseParents'] || false;
8999  const uppercaseChildren = data.config['uppercaseChildren'] || false;
9000
9001  // Get selected item styling from config
9002  const underlineSelected = data.config['underlineSelected'] !== false;
9003  const selectedBgColor = data.config['selectedBgColor'] || 'transparent';
9004
9005  if(uppercaseParents) container.setAttribute('data-uppercase-parents', 'true');
9006  else container.removeAttribute('data-uppercase-parents');
9007
9008  if(uppercaseChildren) container.setAttribute('data-uppercase-children', 'true');
9009  else container.removeAttribute('data-uppercase-children');
9010
9011  if(underlineSelected) container.setAttribute('data-underline-selected', 'true');
9012  else container.removeAttribute('data-underline-selected');
9013
9014  container.style.setProperty('--selected-bg-color', selectedBgColor);
9015
9016  // Helpful attribute only
9017  container.setAttribute('data-columns', String(columnCount));
9018
9019  // Get current page URL for active state detection
9020  const currentPagePath = cleanUrl(window.location.href);
9021
9022  // Recursive function to extract nested children
9023  function extractChildren(parentLi, level = 1) {
9024    const childUL = parentLi.querySelector(':scope > ul, :scope .sub-nav ul, :scope .submenu ul, :scope .dropdown ul, :scope .children, :scope ul');
9025    const children = [];
9026
9027    if(childUL){
9028      Array.from(childUL.querySelectorAll(':scope > li')).forEach(cli => {
9029        const a = cli.querySelector('a');
9030        if(!a) return;
9031        const title = cleanText(a);
9032        const url = cleanUrl(a.getAttribute('href')||'#');
9033        if(!title || !url || url === '#') return;
9034
9035        const nestedChildren = extractChildren(cli, level + 1);
9036
9037        children.push({
9038          title,
9039          url,
9040          children: nestedChildren,
9041          level
9042        });
9043      });
9044    }
9045
9046    return children;
9047  }
9048
9049  function extractNavFromDOM(){
9050    const navCandidates = [
9051      'nav[role="navigation"]','.dmNavWrapper','.dmNav','.navigation','nav','header nav'
9052    ];
9053    let navRoot=null;
9054
9055    for(const sel of navCandidates){
9056      const el=document.querySelector(sel);
9057      if(!el) continue;
9058      if(el.querySelector('li ul, li .sub-nav, li .submenu, li .dropdown')){ navRoot=el; break; }
9059      if(!navRoot) navRoot=el;
9060    }
9061    if(!navRoot) return [];
9062
9063    const topUL = navRoot.querySelector('ul, .menu, .dmNav');
9064    const topLIs = topUL
9065      ? Array.from(topUL.children).filter(li=>li.tagName==='LI')
9066      : Array.from(navRoot.querySelectorAll(':scope > li, :scope ul > li'));
9067
9068    const parents=[];
9069    topLIs.forEach(li=>{
9070      const parentLink =
9071        li.querySelector(':scope > a, :scope > .nav-item > a, :scope > .menu-item > a') ||
9072        li.querySelector('a');
9073
9074      const parentTitle = cleanText(parentLink);
9075      const parentUrl   = cleanUrl(parentLink?.getAttribute('href')||'#');
9076
9077      if(!parentTitle) return;
9078
9079      // Skip if parent title is in exclusion list
9080      if(excludeList.includes(parentTitle.toLowerCase())) return;
9081
9082      const children = extractChildren(li, 1);
9083
9084      // Include if: has children OR is in inclusion list
9085      const isInIncludeList = includeList.length > 0 && includeList.includes(parentTitle.toLowerCase());
9086
9087      if(children.length > 0 || isInIncludeList) {
9088        parents.push({ title: parentTitle, url: parentUrl, children, level: 0 });
9089      }
9090    });
9091
9092    return parents;
9093  }
9094
9095  // Recursive function to render nested children
9096  function renderNestedChildren(children, parentPath = '') {
9097    return children.map((child, childIndex) => {
9098      const nodePath = `${parentPath}-${childIndex}`;
9099      const hasChildren = child.children && child.children.length > 0;
9100      const level = child.level || 1;
9101      const isActive = child.url === currentPagePath;
9102      const activeClass = isActive ? ' ngw-active' : '';
9103
9104      if(hasChildren) {
9105        return `
9106          <div class="ngw-nested-group" data-level="${level}" data-path="${nodePath}">
9107            <div class="ngw-nested-parent" data-path="${nodePath}">
9108              <a href="${child.url}" class="ngw-child-link${activeClass}">${child.title}</a>
9109              <button class="ngw-nested-toggle" type="button" aria-label="Toggle ${child.title}">
9110                <span class="ngw-chevron" aria-hidden="true">${chevronIcon}</span>
9111              </button>
9112            </div>
9113            <div class="ngw-nested-children" data-path="${nodePath}">
9114              ${renderNestedChildren(child.children, nodePath)}
9115            </div>
9116          </div>
9117        `;
9118      }
9119
9120      return `<a href="${child.url}" class="ngw-child-link${activeClass}" data-level="${level}">${child.title}</a>`;
9121    }).join('');
9122  }
9123
9124  function render(gridData){
9125    container.innerHTML = gridData.map((parent, idx) => {
9126      const kids = renderNestedChildren(parent.children, `${idx}`);
9127      const hasChildren = parent.children && parent.children.length > 0;
9128
9129      const isParentActive = parent.url === currentPagePath;
9130      const activeClass = isParentActive ? ' ngw-active' : '';
9131
9132      if(!hasChildren) {
9133        return `
9134          <section class="ngw-group ngw-no-children" role="group" data-index="${idx}">
9135            <div class="ngw-parent" data-index="${idx}">
9136              <a href="${parent.url}" class="ngw-parent-link${activeClass}">${parent.title}</a>
9137            </div>
9138          </section>
9139        `;
9140      }
9141
9142      const initialExpanded = expandAll ? 'true' : 'false';
9143      return `
9144        <section class="ngw-group" role="group" aria-expanded="${initialExpanded}" data-index="${idx}">
9145          <div class="ngw-parent" data-index="${idx}" aria-controls="ngw-children-${idx}">
9146            <a href="${parent.url}" class="ngw-parent-link${activeClass}">${parent.title}</a>
9147            <button class="ngw-toggle" type="button" aria-label="Toggle ${parent.title}">
9148              <span class="ngw-chevron" aria-hidden="true">${chevronIcon}</span>
9149            </button>
9150          </div>
9151          <div id="ngw-children-${idx}" class="ngw-children" role="region" aria-label="${parent.title} subpages">
9152            ${kids}
9153          </div>
9154        </section>
9155      `;
9156    }).join('');
9157
9158    // If expandAll is enabled, also expand all nested groups
9159    if(expandAll) {
9160      container.querySelectorAll('.ngw-nested-group').forEach(g => {
9161        g.setAttribute('aria-expanded', 'true');
9162      });
9163    }
9164
9165    function collapseAll(){
9166      container.querySelectorAll('.ngw-group').forEach(g=>g.setAttribute('aria-expanded','false'));
9167      container.querySelectorAll('.ngw-nested-group').forEach(g=>g.removeAttribute('aria-expanded'));
9168    }
9169    function expandOnly(index){
9170      container.querySelectorAll('.ngw-group').forEach((g)=> {
9171        const i = parseInt(g.getAttribute('data-index')||'-1',10);
9172        g.setAttribute('aria-expanded', String(i===index));
9173      });
9174    }
9175
9176    // Hover functionality - only if not in expandAll mode
9177    if(openOnHover && !expandAll) {
9178      container.addEventListener('mouseenter', (e) => {
9179        const group = e.target.closest('.ngw-group');
9180        if(!group || group.classList.contains('ngw-no-children')) return;
9181        const index = parseInt(group.querySelector('.ngw-parent')?.getAttribute('data-index') || '-1', 10);
9182        if(index >= 0) expandOnly(index);
9183      }, true);
9184
9185      container.addEventListener('mouseleave', (e) => {
9186        if(e.target.closest('.ngw-group')) return;
9187        collapseAll();
9188      }, true);
9189
9190      // Nested hover
9191      container.addEventListener('mouseenter', (e) => {
9192        const nestedGroup = e.target.closest('.ngw-nested-group');
9193        if(!nestedGroup) return;
9194        nestedGroup.setAttribute('aria-expanded', 'true');
9195      }, true);
9196
9197      container.addEventListener('mouseleave', (e) => {
9198        const nestedGroup = e.target.closest('.ngw-nested-group');
9199        if(!nestedGroup) return;
9200        nestedGroup.removeAttribute('aria-expanded');
9201      }, true);
9202    }
9203
9204    // ----------------------------
9205    // Toggle behavior (works in BOTH modes)
9206    // - expandAll=false: row click toggles + only one open at a time
9207    // - expandAll=true : ONLY chevron buttons toggle; everything starts expanded
9208    // ----------------------------
9209    container.addEventListener('click', (e) => {
9210      // 1) Parent chevron button toggles its group (works even when expandAll=true)
9211      const parentToggleBtn = e.target.closest('.ngw-toggle');
9212      if (parentToggleBtn) {
9213        const group = parentToggleBtn.closest('.ngw-group');
9214        if (!group || group.classList.contains('ngw-no-children')) return;
9215
9216        const isOpen = group.getAttribute('aria-expanded') === 'true';
9217
9218        // In expandAll mode: toggle ONLY this group
9219        if (expandAll) {
9220          group.setAttribute('aria-expanded', String(!isOpen));
9221        } else {
9222          // In normal mode: preserve "only one open at a time"
9223          const index = parseInt(group.getAttribute('data-index') || '-1', 10);
9224          if (index >= 0) {
9225            if (isOpen) collapseAll();
9226            else expandOnly(index);
9227          }
9228        }
9229
9230        e.preventDefault();
9231        e.stopPropagation();
9232        return;
9233      }
9234
9235      // 2) Nested chevron button toggles its nested group (works even when expandAll=true)
9236      const nestedToggleBtn = e.target.closest('.ngw-nested-toggle');
9237      if (nestedToggleBtn) {
9238        const nestedGroup = nestedToggleBtn.closest('.ngw-nested-group');
9239        if (!nestedGroup) return;
9240
9241        const isOpen = nestedGroup.getAttribute('aria-expanded') === 'true';
9242        if (isOpen) nestedGroup.removeAttribute('aria-expanded');
9243        else nestedGroup.setAttribute('aria-expanded', 'true');
9244
9245        e.preventDefault();
9246        e.stopPropagation();
9247        return;
9248      }
9249
9250      // 3) In expandAll mode, do NOT allow row-click toggling (only chevrons toggle)
9251      if (expandAll) return;
9252
9253      // 4) Normal mode: clicking anywhere on parent row toggles (except the parent hyperlink)
9254      const parentRow = e.target.closest('.ngw-parent');
9255      if (!parentRow) return;
9256
9257      // If they clicked the parent hyperlink, let the browser navigate naturally
9258      if (e.target.closest('.ngw-parent-link')) return;
9259
9260      const index = parseInt(parentRow.getAttribute('data-index') || '-1', 10);
9261      if (index < 0) return;
9262
9263      const group = parentRow.closest('.ngw-group');
9264      if (!group || group.classList.contains('ngw-no-children')) return;
9265
9266      const isOpen = group.getAttribute('aria-expanded') === 'true';
9267      if (isOpen) collapseAll();
9268      else expandOnly(index);
9269    });
9270
9271    // Keyboard support
9272    container.addEventListener('keydown', (e) => {
9273      if (expandAll) return; // chevron buttons already work via native button behavior
9274
9275      const parentRow = e.target.closest('.ngw-parent');
9276      const nestedParent = e.target.closest('.ngw-nested-parent');
9277
9278      if (parentRow && !nestedParent) {
9279        if (document.activeElement && document.activeElement.classList.contains('ngw-parent-link') && e.key === 'Enter') {
9280          return;
9281        }
9282        const group = parentRow.closest('.ngw-group');
9283        if (group && group.classList.contains('ngw-no-children')) return;
9284
9285        if (e.key === ' ' || e.key === 'Enter') {
9286          e.preventDefault();
9287          const index = parseInt(parentRow.getAttribute('data-index') || '-1', 10);
9288          if (index < 0) return;
9289          const isOpen = group.getAttribute('aria-expanded') === 'true';
9290          if (isOpen) collapseAll();
9291          else expandOnly(index);
9292        }
9293      }
9294
9295      if (nestedParent) {
9296        if (document.activeElement && document.activeElement.classList.contains('ngw-child-link') && e.key === 'Enter') {
9297          return;
9298        }
9299        if (e.key === ' ' || e.key === 'Enter') {
9300          e.preventDefault();
9301          const nestedGroup = nestedParent.closest('.ngw-nested-group');
9302          if (!nestedGroup) return;
9303          const isOpen = nestedGroup.getAttribute('aria-expanded') === 'true';
9304          if (isOpen) nestedGroup.removeAttribute('aria-expanded');
9305          else nestedGroup.setAttribute('aria-expanded', 'true');
9306        }
9307      }
9308    });
9309  }
9310
9311  function init(){
9312    const build = ()=>{
9313      const navData = extractNavFromDOM();
9314      if(navData && navData.length){
9315        render(navData);
9316        return true;
9317      }
9318      return false;
9319    };
9320    const okNow = build();
9321    if(!okNow){ setTimeout(build, 350); } // retry once for late-loaded nav
9322  }
9323
9324  init();
9325});
9326    };
9327</script>
9327
9328
9329<script>
9330    window.customWidgetsFunctions["8eedf98f346a42e0a55f9d6152653187~2"] = function (element, data, api) {
9331        window.dmAPI.runOnReady('dcbh2-h2-theme', () => {
9332
9333  const STYLE_ID = 'dcbh2-h2-theme-style';
9334  const TARGET_SELECTOR = '.widget-wrapper.DCBH2';
9335
9336  let updateTimer = null;
9337  let lastSignature = '';
9338
9339  /**
9340   * Find a usable H2 that is NOT inside the DCBH2 widget.
9341   */
9342  function findThemeH2() {
9343    const candidates = Array.from(
9344      document.querySelectorAll('#dm h2, h2')
9345    );
9346
9347    for (const el of candidates) {
9348
9349      // Do not use our own DCBH2 widget as the source
9350      if (el.closest(TARGET_SELECTOR)) {
9351        continue;
9352      }
9353
9354      // Ignore disconnected elements
9355      if (!el.isConnected) {
9356        continue;
9357      }
9358
9359      const computed = window.getComputedStyle(el);
9360
9361      // Ignore unavailable / hidden elements
9362      if (
9363        computed.display === 'none' ||
9364        computed.visibility === 'hidden'
9365      ) {
9366        continue;
9367      }
9368
9369      // Make sure useful typography has loaded
9370      if (!computed.fontFamily || !computed.fontSize) {
9371        continue;
9372      }
9373
9374      return el;
9375    }
9376
9377    return null;
9378  }
9379
9380
9381  /**
9382   * Read H2 typography and apply it to DCBH2 widgets.
9383   */
9384  function applyTheme() {
9385
9386    const h2Sample = findThemeH2();
9387
9388    if (!h2Sample) {
9389      return false;
9390    }
9391
9392    const computed = window.getComputedStyle(h2Sample);
9393
9394    const styles = {
9395      fontFamily: computed.fontFamily,
9396      fontSize: computed.fontSize,
9397      fontWeight: computed.fontWeight,
9398      lineHeight: computed.lineHeight,
9399      color: computed.color,
9400      letterSpacing: computed.letterSpacing,
9401      textTransform: computed.textTransform,
9402      fontStyle: computed.fontStyle,
9403      fontVariant: computed.fontVariant,
9404      fontStretch: computed.fontStretch
9405    };
9406
9407    /**
9408     * Only rewrite the stylesheet if something actually changed.
9409     */
9410    const signature = JSON.stringify(styles);
9411
9412    if (signature === lastSignature) {
9413      return true;
9414    }
9415
9416    lastSignature = signature;
9417
9418
9419    /**
9420     * Target Duda RTE elements, including nested spans.
9421     */
9422    const selectors = [
9423      '#dm .dmInner .widget-wrapper.DCBH2 .dmNewParagraph',
9424      '#dm .dmInner .widget-wrapper.DCBH2 .dmNewParagraph p',
9425      '#dm .dmInner .widget-wrapper.DCBH2 .dmNewParagraph span',
9426      '#dm .dmInner .widget-wrapper.DCBH2 .dmNewParagraph .rteBlock',
9427      '#dm .dmInner .widget-wrapper.DCBH2 .dmNewParagraph .rteBlock *',
9428
9429      '.dmInner .widget-wrapper.DCBH2 .dmNewParagraph',
9430      '.dmInner .widget-wrapper.DCBH2 .dmNewParagraph p',
9431      '.dmInner .widget-wrapper.DCBH2 .dmNewParagraph span',
9432      '.dmInner .widget-wrapper.DCBH2 .dmNewParagraph .rteBlock',
9433      '.dmInner .widget-wrapper.DCBH2 .dmNewParagraph .rteBlock *',
9434
9435      '.widget-wrapper.DCBH2 .dmNewParagraph',
9436      '.widget-wrapper.DCBH2 .dmNewParagraph p',
9437      '.widget-wrapper.DCBH2 .dmNewParagraph span',
9438      '.widget-wrapper.DCBH2 .dmNewParagraph .rteBlock',
9439      '.widget-wrapper.DCBH2 .dmNewParagraph .rteBlock *'
9440    ];
9441
9442    const css = `
9443${selectors.join(',\n')} {
9444  font-family: ${styles.fontFamily} !important;
9445  font-size: ${styles.fontSize} !important;
9446  font-weight: ${styles.fontWeight} !important;
9447  line-height: ${styles.lineHeight} !important;
9448  color: ${styles.color} !important;
9449  letter-spacing: ${styles.letterSpacing} !important;
9450  text-transform: ${styles.textTransform} !important;
9451  font-style: ${styles.fontStyle} !important;
9452  font-variant: ${styles.fontVariant} !important;
9453  font-stretch: ${styles.fontStretch} !important;
9454}
9455`;
9456
9457    let styleTag = document.getElementById(STYLE_ID);
9458
9459    if (!styleTag) {
9460      styleTag = document.createElement('style');
9461      styleTag.id = STYLE_ID;
9462      styleTag.setAttribute('data-dcbh2-h2-theme', 'true');
9463      document.head.appendChild(styleTag);
9464    }
9465
9466    styleTag.textContent = css;
9467
9468    return true;
9469  }
9470
9471
9472  /**
9473   * Debounce repeated Duda DOM changes.
9474   */
9475  function scheduleUpdate(delay = 100) {
9476
9477    clearTimeout(updateTimer);
9478
9479    updateTimer = setTimeout(() => {
9480      applyTheme();
9481    }, delay);
9482  }
9483
9484
9485  /**
9486   * Initial passes.
9487   *
9488   * Duda/theme/font rendering can continue after runOnReady.
9489   */
9490  applyTheme();
9491
9492  setTimeout(applyTheme, 250);
9493  setTimeout(applyTheme, 500);
9494  setTimeout(applyTheme, 1000);
9495  setTimeout(applyTheme, 2000);
9496  setTimeout(applyTheme, 4000);
9497
9498
9499  /**
9500   * Watch for Duda re-rendering or dynamic content changes.
9501   */
9502  const observer = new MutationObserver((mutations) => {
9503
9504    let relevantChange = false;
9505
9506    for (const mutation of mutations) {
9507
9508      // Ignore our own injected stylesheet
9509      if (
9510        mutation.target &&
9511        (
9512          mutation.target.id === STYLE_ID ||
9513          mutation.target.closest?.(`#${STYLE_ID}`)
9514        )
9515      ) {
9516        continue;
9517      }
9518
9519      relevantChange = true;
9520      break;
9521    }
9522
9523    if (relevantChange) {
9524      scheduleUpdate(150);
9525    }
9526
9527  });
9528
9529  observer.observe(document.body, {
9530    childList: true,
9531    subtree: true,
9532    attributes: true,
9533    attributeFilter: [
9534      'class',
9535      'style'
9536    ]
9537  });
9538
9539
9540  /**
9541   * Responsive breakpoint changes.
9542   */
9543  window.addEventListener('resize', () => {
9544    scheduleUpdate(150);
9545  });
9546
9547
9548  /**
9549   * Mobile orientation changes.
9550   */
9551  window.addEventListener('orientationchange', () => {
9552    scheduleUpdate(250);
9553  });
9554
9555
9556  /**
9557   * Reapply after web fonts finish loading.
9558   */
9559  if (document.fonts && document.fonts.ready) {
9560
9561    document.fonts.ready.then(() => {
9562
9563      lastSignature = '';
9564
9565      applyTheme();
9566
9567    }).catch(() => {
9568      // Nothing required
9569    });
9570
9571  }
9572
9573});
9574    };
9575</script>
9575
9576
9577<script>
9578    window.customWidgetsFunctions["403a957a34f84bc8a342137c59c93b31~3"] = function (element, data, api) {
9579        window.dmAPI.runOnReady('dcbp-p-theme', () => {
9580  'use strict';
9581
9582  const STYLE_ID = 'dcbp-p-theme-styles';
9583
9584  function getParagraphFontFamily() {
9585    const source =
9586      document.querySelector(
9587        '.widget-403a95 .cb2-theme-injector .dmNewParagraph p'
9588      ) ||
9589      document.querySelector(
9590        '.widget-403a95 .cb2-theme-injector p'
9591      ) ||
9592      document.querySelector(
9593        '.widget-403a95 .dmNewParagraph p'
9594      ) ||
9595      document.querySelector(
9596        '.widget-403a95 p'
9597      );
9598
9599    if (!source) {
9600      return null;
9601    }
9602
9603    const computed = window.getComputedStyle(source);
9604    const fontFamily = computed.fontFamily;
9605
9606    if (!fontFamily || fontFamily.trim() === '') {
9607      return null;
9608    }
9609
9610    return fontFamily;
9611  }
9612
9613  function applyTheme() {
9614    const dcbpElements = document.querySelectorAll(
9615      '#dm .DCBP, ' +
9616      '.DCBP, ' +
9617      '#dm .widget-wrapper.DCBP, ' +
9618      '.widget-wrapper.DCBP'
9619    );
9620
9621    if (!dcbpElements.length) {
9622      return false;
9623    }
9624
9625    const fontFamily = getParagraphFontFamily();
9626
9627    if (!fontFamily) {
9628      return false;
9629    }
9630
9631    const uSelectors = new Set();
9632
9633    const paragraphElements = document.querySelectorAll(
9634      '#dm .DCBP .dmNewParagraph, ' +
9635      '.DCBP .dmNewParagraph'
9636    );
9637
9638    paragraphElements.forEach(element => {
9639      element.classList.forEach(className => {
9640        if (!className.startsWith('u_')) {
9641          return;
9642        }
9643
9644        uSelectors.add(`div.${className}`);
9645        uSelectors.add(`div.${className} p`);
9646        uSelectors.add(`div.${className} li`);
9647        uSelectors.add(`div.${className} span`);
9648
9649        uSelectors.add(`#dm .dmInner div.${className}`);
9650        uSelectors.add(`#dm .dmInner div.${className} p`);
9651        uSelectors.add(`#dm .dmInner div.${className} li`);
9652        uSelectors.add(`#dm .dmInner div.${className} span`);
9653      });
9654    });
9655
9656    const baseSelectors = [
9657      '#dm .dmInner .widget-wrapper.DCBP .dmNewParagraph',
9658      '#dm .dmInner .widget-wrapper.DCBP .dmNewParagraph p',
9659      '#dm .dmInner .widget-wrapper.DCBP .dmNewParagraph li',
9660      '#dm .dmInner .widget-wrapper.DCBP .dmNewParagraph span',
9661      '#dm .dmInner .widget-wrapper.DCBP .dmNewParagraph .rteBlock',
9662
9663      '.dmInner .widget-wrapper.DCBP .dmNewParagraph',
9664      '.dmInner .widget-wrapper.DCBP .dmNewParagraph p',
9665      '.dmInner .widget-wrapper.DCBP .dmNewParagraph li',
9666      '.dmInner .widget-wrapper.DCBP .dmNewParagraph span',
9667      '.dmInner .widget-wrapper.DCBP .dmNewParagraph .rteBlock',
9668
9669      '.widget-wrapper.DCBP .dmNewParagraph',
9670      '.widget-wrapper.DCBP .dmNewParagraph p',
9671      '.widget-wrapper.DCBP .dmNewParagraph li',
9672      '.widget-wrapper.DCBP .dmNewParagraph span',
9673      '.widget-wrapper.DCBP .dmNewParagraph .rteBlock',
9674
9675      '#dm .DCBP .dmNewParagraph',
9676      '#dm .DCBP .dmNewParagraph p',
9677      '#dm .DCBP .dmNewParagraph li',
9678      '#dm .DCBP .dmNewParagraph span',
9679
9680      '.DCBP .dmNewParagraph',
9681      '.DCBP .dmNewParagraph p',
9682      '.DCBP .dmNewParagraph li',
9683      '.DCBP .dmNewParagraph span'
9684    ];
9685
9686    const allSelectors = [
9687      ...baseSelectors,
9688      ...uSelectors
9689    ].join(',\n');
9690
9691    const css = `
9692${allSelectors} {
9693  font-family: ${fontFamily} !important;
9694}
9695`;
9696
9697    let styleTag = document.getElementById(STYLE_ID);
9698
9699    if (!styleTag) {
9700      styleTag = document.createElement('style');
9701      styleTag.id = STYLE_ID;
9702      styleTag.setAttribute('data-dcbp-p-theme', 'true');
9703      document.head.appendChild(styleTag);
9704    }
9705
9706    styleTag.textContent = css;
9707
9708    return true;
9709  }
9710
9711  if (applyTheme()) {
9712    return;
9713  }
9714
9715  let attempts = 0;
9716  const maxAttempts = 30;
9717
9718  const interval = window.setInterval(() => {
9719    attempts += 1;
9720
9721    if (applyTheme() || attempts >= maxAttempts) {
9722      window.clearInterval(interval);
9723    }
9724  }, 300);
9725});
9726    };
9727</script>
9727
9728
9729<script>
9730    window.customWidgetsFunctions["1c20f508b5474ea2b929959f7d5eff3d~3"] = function (element, data, api) {
9731        window.dmAPI.runOnReady('dcbh3-h3-theme', () => {
9732
9733  function applyTheme() {
9734    // Try multiple candidate selectors for the H3 style source
9735    // to work in both the editor and the published/preview environment
9736    const candidates = [
9737      '#dm h3',
9738      'h3',
9739    ];
9740
9741    let h3Sample = null;
9742    for (const sel of candidates) {
9743      h3Sample = document.querySelector(sel);
9744      if (h3Sample) break;
9745    }
9746
9747    if (!h3Sample) return false;
9748
9749    const computed = window.getComputedStyle(h3Sample);
9750
9751    const fontFamily    = computed.fontFamily;
9752    const fontSize      = computed.fontSize;
9753    const fontWeight    = computed.fontWeight;
9754    const lineHeight    = computed.lineHeight;
9755    const color         = computed.color;
9756    const letterSpacing = computed.letterSpacing;
9757    const textTransform = computed.textTransform;
9758    const fontStyle     = computed.fontStyle;
9759
9760    // Skip if nothing meaningful was computed yet
9761    if (!fontFamily || fontFamily === '') return false;
9762
9763    // Build selectors for every u_ class found on CB1 dmNewParagraph divs
9764    const uSelectors = [];
9765    const paragraphEls = document.querySelectorAll(
9766      '#dm .dmInner .widget-wrapper.DCBH3 .dmNewParagraph, ' +
9767      '.dmInner .widget-wrapper.DCBH3 .dmNewParagraph, ' +
9768      '.widget-wrapper.DCBH3 .dmNewParagraph'
9769    );
9770    paragraphEls.forEach(el => {
9771      el.classList.forEach(cls => {
9772        if (cls.startsWith('u_')) {
9773          uSelectors.push(`div.${cls} p`);
9774          uSelectors.push(`div.${cls} li`);
9775          uSelectors.push(`*#dm .dmInner div.${cls} p`);
9776          uSelectors.push(`*#dm .dmInner div.${cls} li`);
9777        }
9778      });
9779    });
9780
9781    const baseSelectors = [
9782      `#dm .dmInner .widget-wrapper.DCBH3 .dmNewParagraph`,
9783      `#dm .dmInner .widget-wrapper.DCBH3 .dmNewParagraph p`,
9784      `#dm .dmInner .widget-wrapper.DCBH3 .dmNewParagraph li`,
9785      `#dm .dmInner .widget-wrapper.DCBH3 .dmNewParagraph .rteBlock`,
9786      `.dmInner .widget-wrapper.DCBH3 .dmNewParagraph`,
9787      `.dmInner .widget-wrapper.DCBH3 .dmNewParagraph p`,
9788      `.dmInner .widget-wrapper.DCBH3 .dmNewParagraph li`,
9789      `.dmInner .widget-wrapper.DCBH3 .dmNewParagraph .rteBlock`,
9790      `.widget-wrapper.DCBH3 .dmNewParagraph`,
9791      `.widget-wrapper.DCBH3 .dmNewParagraph p`,
9792      `.widget-wrapper.DCBH3 .dmNewParagraph li`,
9793      `.widget-wrapper.DCBH3 .dmNewParagraph .rteBlock`,
9794    ];
9795
9796    const allSelectors = [...baseSelectors, ...uSelectors].join(',\n    ');
9797
9798    const css = `
9799    ${allSelectors} {
9800      font-family: ${fontFamily} !important;
9801      font-size: ${fontSize} !important;
9802      font-weight: ${fontWeight} !important;
9803      line-height: ${lineHeight} !important;
9804      color: ${color} !important;
9805      letter-spacing: ${letterSpacing} !important;
9806      text-transform: ${textTransform} !important;
9807      font-style: ${fontStyle} !important;
9808    }
9809  `;
9810
9811    // Remove any previously injected tag before re-injecting
9812    const existing = document.querySelector('style[data-dcbh3-h3-theme]');
9813    if (existing) existing.remove();
9814
9815    const styleTag = document.createElement('style');
9816    styleTag.setAttribute('data-dcbh3-h3-theme', 'true');
9817    styleTag.textContent = css;
9818    document.head.appendChild(styleTag);
9819
9820    return true;
9821  }
9822
9823  // Try immediately
9824  if (applyTheme()) return;
9825
9826  // If not ready yet, retry with increasing delays to handle
9827  // async rendering in preview / published environments
9828  let attempts = 0;
9829  const maxAttempts = 40;
9830  const interval = setInterval(() => {
9831    attempts++;
9832    if (applyTheme() || attempts >= maxAttempts) {
9833      clearInterval(interval);
9834    }
9835  }, 500);
9836});
9837
9838    };
9839</script>
9839
9840
9841
9842<script type="text/javascript">
9843
9844    var d_version = "production_6835";
9845    var build = "2026-10-07T13_19_14";
9846    window['v' + 'ersion'] = d_version;
9847
9848    function buildEditorParent() {
9849        window.isMultiScreen = true;
9850        window.editorParent = {};
9851        window.previewParent = {};
9852        try {
9853            var _p = window.parent;
9854            if (_p && _p.document && _p.$ && _p.$.dmfw) {
9855                window.editorParent = _p;
9856            } else if (_p.isSitePreview) {
9857                window.previewParent = _p;
9858            }
9859        } catch (e) {
9860
9861        }
9862    }
9863
9864    buildEditorParent();
9865</script>
9865
9866
9867
9868
9869<!-- Injecting site-wide before scripts -->
9870
9871
9872<script src="https://app.termly.io/resource-blocker/f83e824a-6087-4027-807f-ad630395035c?autoBlock=on" data-swh-uuid="b147975169d64f0592785eac7c26b01a">
9873</script>
9873
9874
9875
9876
9877
9878
9879
9880
9881
9882
9883
9884
9885
9886
9887
9888
9889
9890
9891
9892
9893<script type="text/javascript" id="d_track_campaign" data-entry-referrer data-utm-cookies></script>
9893
9894
9895<script type="text/javascript" >
9896    var _dm_gaq = {};
9897    var _gaq = _gaq || [];
9898    var _dm_insite = [];
9899</script>
9899
9900
9901
9902
9903
9904<script async src="https://www.googletagmanager.com/gtm.js?id=GTM-W487CGFD" id="d-js-gtm"
9905    onload="window.dispatchEvent(new CustomEvent('gtmLoaded', { bubbles: true }))"></script>
9905
9906
9907<script type="text/javascript" id="d_track_ga">
9908    window.dataLayer = window.dataLayer || [];
9909    _dm_gaq.siteAlias = 'f6cdfabba1c749a1a3d3b7a9f4c30c76';
9910    _dm_gaq.externalGaqID = 'non-empty';
9911
9912    function gtag() { dataLayer.push(arguments); }
9913
9914    function pushInsiteImpressions(id) {
9915        _dm_insite.forEach((rule) => {
9916            _dm_gaq.pushEvent(id, 'insite_impression', 'insite', rule.ruleType + '__' + rule.ruleId);
9917        });
9918    }
9919
9920    _dm_gaq.pushEvent = function(trackingId, event, category, label, value, additionalParams = {}) {
9921        const eventProps = {
9922            event_category: category,
9923            event_label: label,
9924            value,
9925            ...additionalParams
9926        };
9927        gtag('event', event, eventProps);
9928    }
9929
9930    gtag('js', new Date())
9931
9932    
9933gtag('set', 'developer_id.dMDU5Nj', true);
9934
9935    _dm_gaq.pushEvent('', 'page_view');
9936
9937    pushInsiteImpressions(_dm_gaq.externalGaqID);
9938</script>
9938
9939
9940
9941
9942
9943
9944
9945<!-- End Injecting site-wide to the head -->
9946
9947
9948
9949
9950
9951
9952<script>
9953    window.cookiesNotificationMarkupPreview = 'null';
9954</script>
9954
9955
9956
9957<script id='d-js-params'>
9958    window.INSITE = window.INSITE || {};
9959    window.INSITE.device = "desktop";
9960
9961    window.rtCommonProps = {};
9962    rtCommonProps["runtimecollector.url"] = 'https://rtc.hibuwebsites.com';
9963    rtCommonProps["performance.tabletPreview.removeScroll"] = 'false';
9964    rtCommonProps["inlineEditGrid.snap"] =true;
9965    rtCommonProps["popup.insite.cookie.ttl"] = '0.5';
9966    rtCommonProps["common.mapbox.token"] = 'pk.eyJ1IjoiZGFubnliMTIzIiwiYSI6ImNqMGljZ256dzAwMDAycXBkdWxwbDgzeXYifQ.Ck5P-0NKPVKAZ6SH98gxxw';
9967    rtCommonProps["common.mapbox.js.override"] =false;
9968    rtCommonProps["common.here.appId"] = 'iYvDjIQ2quyEu0rg0hLo';
9969    rtCommonProps["common.here.appCode"] = '1hcIxLJcbybmtBYTD9Z1UA';
9970    rtCommonProps["ecommerce.ecwid.script"] = 'https://app.multiscreenstore.com/script.js';
9971    rtCommonProps["common.build.dist.folder"] = 'production/6835';
9972    rtCommonProps["common.resources.cdn.host"] = 'https://static-res-cdn.websites.hibu.com';
9973    rtCommonProps["common.resources.folder"] = 'https://static-res-cdn.websites.hibu.com/mnlt/production/6835';
9974    rtCommonProps["common.fonts.api.v2.url"] = 'https://cdn.hibuwebsites.com/fonts/css2?family=';
9975    rtCommonProps["feature.flag.runtime.backgroundSlider.preload.slowly"] =true;
9976    rtCommonProps["feature.flag.runtime.newAnimation.jitAnimation.enabled"] =true;
9977    rtCommonProps["feature.flag.sites.google.analytics.gtag"] =true;
9978    rtCommonProps["feature.flag.runOnReadyNewTask"] =true;
9979    rtCommonProps["isAutomation.test"] =false;
9980    rtCommonProps["booking.cal.api.domain"] = 'api.cal.com';
9981
9982    
9983    rtCommonProps['common.mapsProvider'] = 'mapbox';
9984    
9985    rtCommonProps['common.mapsProvider.version'] = '0.52.0';
9986    rtCommonProps['common.geocodeProvider'] = 'mapbox';
9987    rtCommonProps['server.for.resources'] = '';
9988    rtCommonProps['feature.flag.lazy.widgets'] = true;
9989    rtCommonProps['feature.flag.single.wow'] = false;
9990    rtCommonProps['feature.flag.disallowPopupsInEditor'] = true;
9991    rtCommonProps['feature.flag.mark.anchors'] = true;
9992    rtCommonProps['captcha.public.key'] = '6LffcBsUAAAAAMU-MYacU-6QHY4iDtUEYv_Ppwlz';
9993    rtCommonProps['captcha.invisible.public.key'] = '6LeiWB8UAAAAAHYnVJM7_-7ap6bXCUNGiv7bBPME';
9994    rtCommonProps["images.sizes.small"] =160;
9995    rtCommonProps["images.sizes.mobile"] =640;
9996    rtCommonProps["images.sizes.tablet"] =1280;
9997    rtCommonProps["images.sizes.desktop"] =1920;
9998    rtCommonProps["import.images.storage.imageCDN"] = 'https://le-cdn.hibuwebsites.com/';
9999    rtCommonProps["feature.flag.performance.logs"] =false;
10000    rtCommonProps["site.widget.form.captcha.type"] = 'g_recaptcha';
10001    rtCommonProps["friendly.captcha.site.key"] = 'FCMGSQG9GVNMFS8K';
10002    rtCommonProps["cookiebot.mapbox.consent.category"] = 'marketing';
10003    rtCommonProps["termly.mapbox.consent.category"] = 'performance';
10004    // feature flags that's used out of runtime module (in  legacy files)
10005    rtCommonProps["platform.monolith.personalization.dateTimeCondition.popupMsgAction.moveToclient.enabled"] =true;
10006
10007    window.rtFlags = {};
10008    rtFlags["unsuspendEcwidStoreOnRuntime.enabled"] =false;
10009    rtFlags["scripts.widgetCount.enabled"] =true;
10010    rtFlags["fnb.animations.tracking.enabled"] =true;
10011    rtFlags["ecom.ecwidNewUrlStructure.enabled"] = false;
10012    rtFlags["ecom.ecwid.accountPage.emptyBaseUrl.enabled"] = true;
10013    rtFlags["ecom.ecwid.accountPage.repeatOrderRedirect.enabled"] = false;
10014    rtFlags["ecom.ecwid.oldStore.keepScrollOnLoad.enabled"] = true;
10015    rtFlags["ecom.ecwid.storefrontV3.enabled"] = false;
10016    rtFlags["ecom.monolith.legacy.js.api.disabled.for.new.sites"] = false;
10017    rtFlags["geocode.search.localize"] =false;
10018    rtFlags["feature.flag.runtime.newAnimation.asyncInit.setTimeout.enabled"] =false;
10019    rtFlags["twitter.heightLimit.enabled"] = true;
10020    rtFlags["streamline.monolith.personalization.supportMultipleConditions.enabled"] =false;
10021    rtFlags["flex.animation.design.panel.layout"] =true;
10022    rtFlags["runtime.cwv.report.cls.enabled"] =false;
10023    rtFlags["runtime.cwv.report.lcp.enabled"] =false;
10024    rtFlags["runtime.lcp.preload.selfheal.enabled"] =true;
10025    rtFlags["runtime.lcp.preload.bootstrap.enabled"] =true;
10026    rtFlags["runtime.lcp.preload.monitor.enabled"] =false;
10027    rtFlags["runtime.customwidget.heightpin.enabled"] =true;
10028    rtFlags["runtime.video.widget.lazy.enabled"] =true;
10029    rtFlags["contact.form.useActiveForm"] =true;
10030    rtFlags["contact.form.custom.errors.enabled"] =false;
10031    rtFlags["runtime.ssr.productStore.internal.observer"] =true;
10032    rtFlags["editor.widget.form.a11y"] =true;
10033</script>
vendor: 1 bytes, line 10033
10033
10034<script src="https://static-res-cdn.websites.hibu.com/mnlt/production/6835/_dm/s/rt/dist/scripts/d-js-runtime-flex-package.min.js"  id="d-js-core" defer></script>
10034
10035
10036
10037
10038
10039
10040
10041
10042
10043
10044
10045
10046
10047
10048
10049
10050
10051<script>
10052    document.addEventListener('DOMContentLoaded', () =>
10053        window.runtime.initLayout()
10054    );
10055</script>
10055
10056
10057
10058
10059
10060
10061
10062
10063
10064
10065
10066
10067
10068
10069
10070
10071
10072
10073
10074
10075<script type="text/javascript" id="d_track_sp">
10076    ;(function(p,l,o,w,i,n,g){if(!p[i]){p.GlobalSnowplowNamespace=p.GlobalSnowplowNamespace||[];
10077        p.GlobalSnowplowNamespace.push(i);p[i]=function(){(p[i].q=p[i].q||[]).push(arguments)
10078        };p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1;
10079        n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","//d32hwlnfiv2gyn.cloudfront.net/sp-2.0.0-dm-0.1.min.js","snowplow"));
10080    window.dmsnowplow  = window.snowplow;
10081
10082    dmsnowplow('newTracker', 'cf', 'd32hwlnfiv2gyn.cloudfront.net', { // Initialise a tracker
10083        appId: 'f6cdfabba1c749a1a3d3b7a9f4c30c76'
10084    });
10085
10086    // snowplow queries element styles so we wait until CSS calculations are done.
10087    requestAnimationFrame(() => {
10088        dmsnowplow('trackPageView');
10089        _dm_insite.forEach((rule) => {
10090            // Specifically in popup only the client knows if it is shown or not so we don't always want to track its impression here
10091            // the tracking is in popup.js
10092            if (rule.actionName !== "popup") {
10093                dmsnowplow('trackStructEvent', 'insite', 'impression', rule.ruleType, rule.ruleId);
10094            }
10095            window?.waitForDeferred?.('dmAjax', () => {
10096                $.DM.events.trigger('event-ruleTriggered', {value: rule});
10097            });
10098        });
10099    });
10100</script>
10100
10101
10102
10103
10104<div style="display:none;" id="P6iryBW0Wu"></div>
10105
10106
10107
10108
10109
10110
10111
10112
10113
10114
10115
10116<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
10117
10118    
10119    <div class="pswp__bg"></div>
10120
10121    
10122    <div class="pswp__scroll-wrap">
10123
10124        
10125        <div class="pswp__container">
10126            <div class="pswp__item"></div>
10127            <div class="pswp__item"></div>
10128            <div class="pswp__item"></div>
10129        </div>
10130
10131        
10132        <div class="pswp__ui pswp__ui--hidden">
10133
10134            <div class="pswp__top-bar">
10135
10136                
10137
10138                <div class="pswp__counter"></div>
10139
10140                <button class="pswp__button pswp__button--close" data-auto="pswp-close" title="Close (Esc)"></button>
10141
10142                <button class="pswp__button pswp__button--share" data-auto="pswp-share" title="Share"></button>
10143
10144                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
10145
10146                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
10147
10148                
10149                
10150                <div class="pswp__preloader">
10151                    <div class="pswp__preloader__icn">
10152                      <div class="pswp__preloader__cut">
10153                        <div class="pswp__preloader__donut"></div>
10154                      </div>
10155                    </div>
10156                </div>
10157            </div>
10158
10159            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
10160                <div class="pswp__share-tooltip"></div> 
10161            </div>
10162
10163            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
10164            </button>
10165
10166            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
10167            </button>
10168
10169            <div class="pswp__caption">
10170                <div class="pswp__caption__center"></div>
10171            </div>
10172
10173        </div>
10174
10175    </div>
10176
10177</div>
10178<div id="fb-root"
10179        data-locale="en"></div>
10180<!-- Alias: f6cdfabba1c749a1a3d3b7a9f4c30c76 -->
10181<script id="d_track_personalization">
10182	window?.waitForDeferred?.('dmAjax', () => {
10183		// Collects client data and updates cookies used by smart sites
10184		window.expireDays = 365;
10185		window.visitLength = 30 * 60000;
10186		$.setCookie("dm_timezone_offset", (new Date()).getTimezoneOffset(), window.expireDays);
10187			setSmartSiteCookiesInternal("dm_this_page_view","dm_last_page_view","dm_total_visits","dm_last_visit");
10188	});
10189</script>
vendor: 1 bytes, line 10189
10189
10190<script type="text/javascript">
10191    
10192    Parameters.NavigationAreaParams.MoreButtonText = 'MORE';
10193    
10194    Parameters.NavigationAreaParams.LessButtonText = 'LESS';
10195    </script>
vendor: 1 bytes, line 10195
10195
10196<script id="ssr-empty-script" type="text/javascript"></script>
10196<!--  Begin Body End Markup -->
10197<!-- Begin Body End Markup --><!-- Begin Name of Code --><!-- End Name of Code --><div id="placeholder_retargeting_pixel" style="display: none;" data-inject="placeholder_retargeting_pixel">
10198 
10199 
10200 
10201 {{placeholder_retargeting_pixel}}
10202</div>
10203<div id="placeholder_dpni" style="display: none;" data-inject="placeholder_dpni">
10204 <!-- phone insertion script begins --> 
10204<script type="text/javascript" src="https://reports.hibu.com/analytics/js/ybDynamicPhoneInsertion.js">
10205//<![CDATA[
10206
10207 
10208//]]>
10209 </script>
10209
10210 
10210<script>
10211
10212//<![CDATA[
10213
10214
10215ybFindPhNums = ['18602152254'];
10216ybReplacePhNums = ['18602220150'];
10217
10218	document.addEventListener("YextPhoneChangeEvent", yextPhoneChangeEventHandler, false);
10219
10220	function yextPhoneChangeEventHandler(e) {
10221		e.preventDefault();
10222		ybFun_ReplaceText();
10223	}
10224
10225	if (typeof dmAPI != 'undefined') {
10226		dmAPI.runOnReady('dpni', function() {
10227			setTimeout(ybFun_ReplaceText, 500);
10228		});
10229		window.addEventListener('DOMContentLoaded', function() {
10230			dmAPI.subscribeEvent(dmAPI.EVENTS.SHOW_POPUP, 
10231				function(data) {
10232					setTimeout(ybFun_ReplaceText, 500);
10233				});
10234		});
10235	} else {
10236		window.onload = function() {
10237			setTimeout(ybFun_ReplaceText, 500);
10238		}
10239	}
10240
10241
10242//]]>
10243 
10244 </script>
10244
10245 <!-- phone insertion script ends -->
10246</div>
10247<div id="placeholder_analytics" style="display: none;" data-inject="placeholder_analytics">
10248 
10248<script>
10249
10250
10251//<![CDATA[
10252
10253//yellAnalyticsConfig need to set before s_code.js
10254var yellAnalyticsConfig = {
10255"global" : {
10256 "trackDownloadLinks": true,
10257 "downloadFileTypes": "exe,zip,wav,mp3,mov,mpg,avi,wmv,pdf,doc,docx,xls,xlsx,ppt,pptx",
10258 "currency": "USD",
10259 "trackExternalLinks": true,
10260 "internalDomains": "javascript:",
10261 "charset": "UTF-8"
10262},
10263"omniture" : {
10264 "enabled": true,
10265 "server": "yellgroup.122.2o7.net",
10266 "serverSecure": "yellgroup.122.2o7.net",
10267 "namespace": "yellgroup",
10268 "account": "yelldudalive",
10269 "campaign": "CID|cam"
10270}}
10271var hibuWebsiteConfig={
10272 "prodSKU":"WBS-PRO-01",
10273 "assetID":"700161096",
10274 "customerID":"7003792510"
10275}
10276
10277//]]>
10278 
10279 
10280 </script>
10280
10281 
10281<script type="text/javascript" src="https://dh-static-files.s3.amazonaws.com/prod/AppMeasurement.js">
10282
10283//<![CDATA[
10284
10285 
10286//]]>
10287 
10288 </script>
10288
10289 
10289<script type="text/javascript" src="https://dh-static-files.s3.amazonaws.com/prod/hibu-analytics.min.js" defer="">
10290
10291//<![CDATA[
10292
10293 
10294//]]>
10295 
10296 </script>
10296
10297 
10297<script type="text/javascript" src="https://dh-static-files.s3.amazonaws.com/prod/omn_setting.js" defer="">
10298
10299//<![CDATA[
10300
10301 
10302//]]>
10303 
10304 </script>
10304
10305</div>
10306<div id="placeholder_footer_reserve1" style="display: none;" data-inject="placeholder_footer_reserve1">
10307 
10307<script>
10308
10309
10310//<![CDATA[
10311
10312document.addEventListener("DOMContentLoaded", function () {
10313 function deoptimizeImageUrl(url) {
10314 // Only process if URL contains both /opt/ and -640w
10315 if (!url.includes('/opt/') || !url.includes('-640w')) return url;
10316
10317 return url
10318 .replace('https://le-cdn.hibuwebsites.com/', 'https://cdn.hibuwebsites.com/')
10319 .replace('/opt/', '/')
10320 .replace(/-640w(?=\.\w+$)/, '');
10321 }
10322
10323 // Select all elements with style attributes containing '-640w'
10324 document.querySelectorAll('[style*="-640w"]').forEach(el => {
10325 const originalStyle = el.getAttribute('style');
10326
10327 // Replace each background-image URL that matches our pattern
10328 const updatedStyle = originalStyle.replace(
10329 /url\(["']?(https:\/\/le-cdn\.hibuwebsites\.com[^"')]+)["']?\)/g,
10330 (match, url) => {
10331 const newUrl = deoptimizeImageUrl(url);
10332 return `url('${newUrl}')`;
10333 }
10334 );
10335
10336 // Update the element's style attribute
10337 el.setAttribute('style', updatedStyle);
10338 });
10339});
10340
10341//]]>
10342 
10343 
10344 </script>
10344
10345</div>
10346<div id="placeholder_footer_reserve2" style="display: none;" data-inject="placeholder_footer_reserve2">
10347 <!-- Facebook Pixel Code --> 
10347<script nonce="yV7Whw1O">
10348//<![CDATA[
vendor: 398 bytes, lines 10348-10356
10348
10349
10350!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
10351n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
10352n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
10353t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
10354document,'script','https://connect.facebook.net/en_US/fbevents.js');
10355
10356fbq('init', '
103561382224864124864
vendor: 31 bytes, lines 10356-10357
10356');
10357fbq('track', "PageView");
10358//]]>
10359 </script>
10359
10360 <noscript>
10361  <img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=1382224864124864&amp;ev=PageView&amp;noscript=1"/>
10362 </noscript>
10363 <!-- End Facebook Pixel Code -->
10364</div>
10365<div id="placeholder_footer_reserve3" style="display: none;" data-inject="placeholder_footer_reserve3">
10366 
10366<script>
10367
10368
10369//<![CDATA[
10370
10371 (() => {
10372 // Hide any flex wrapper whose button text contains "do not use"
10373 function hideDoNotUse(root = document) {
10374 const wrappers = root.querySelectorAll('.flex-element.widget-wrapper');
10375 wrappers.forEach(wrap => {
10376 const btn = wrap.querySelector('a.dmButtonLink, a.flexButton, a[data-element-type="dButtonLinkId"]');
10377 if (!btn) return;
10378 const text = (btn.textContent || '').toLowerCase().trim();
10379 if (text.includes('do not use')) {
10380 wrap.style.display = 'none';
10381 wrap.setAttribute('data-hidden-reason', 'do-not-use');
10382 }
10383 });
10384 }
10385 // Run once on ready
10386 if (document.readyState === 'loading') {
10387 document.addEventListener('DOMContentLoaded', () => hideDoNotUse());
10388 } else {
10389 hideDoNotUse();
10390 }
10391 // Watch for dynamically inserted content or text changes
10392 const mo = new MutationObserver(mutations => {
10393 for (const m of mutations) {
10394 m.addedNodes.forEach(node => {
10395 if (node.nodeType !== 1) return;
10396 if (node.matches && node.matches('.flex-element.widget-wrapper')) {
10397  hideDoNotUse(node);
10398 } else {
10399  // Check if a new/changed node sits inside a wrapper
10400  const wrap = node.closest && node.closest('.flex-element.widget-wrapper');
10401  if (wrap) hideDoNotUse(wrap);
10402  else hideDoNotUse(node);
10403 }
10404 });
10405 if (m.type === 'characterData' || m.type === 'childList') {
10406 const wrap = m.target.closest && m.target.closest('.flex-element.widget-wrapper');
10407 if (wrap) hideDoNotUse(wrap);
10408 }
10409 }
10410 });
10411 mo.observe(document.documentElement, {
10412 childList: true,
10413 subtree: true,
10414 characterData: true
10415 });
10416})();
10417 
10418//]]>
10419 
10420 
10421 </script>
10421
10422</div>
10423<div id="placeholder_footer_reserve4" style="display: none;" data-inject="placeholder_footer_reserve4">
10424 
10425 
10426 
10427 {{placeholder_footer_reserve4}}
10428</div>
10429<div id="placeholder_footer_reserve5" style="display: none;" data-inject="placeholder_footer_reserve5">
10430 
10431 
10432 
10433 {{placeholder_footer_reserve5}}
10434</div>
10435<div id="placeholder_footer_reserve6" style="display: none;" data-inject="placeholder_footer_reserve6">
10436 
10437 
10438 
10439 {{placeholder_footer_reserve6}}
10440</div>
10441<div id="placeholder_footer_reserve7" style="display: none;" data-inject="placeholder_footer_reserve7">
10442 
10443 
10444 
10445 {{placeholder_footer_reserve7}}
10446</div>
10447<!-- End Body End Markup --><!-- form dropdown code begin -->
10447<script>
10448//<![CDATA[
10449function waitForDmAPI(callback) {
10450 if (typeof dmAPI !== 'undefined') {
10451 callback();
10452 } else {
10453 setTimeout(() => waitForDmAPI(callback), 100);
10454 }
10455}
10456
10457document.addEventListener("DOMContentLoaded", function () {
10458 waitForDmAPI(async function () {
10459 async function getServicesAndProducts() {
10460 try {
10461 var collection = await dmAPI.loadCollectionsAPI();
10462 var formData = await collection.data("form").get();
10463
10464 if (formData?.values?.length > 0) {
10465  let servicesHtml = formData.values[0].data["Services & Products"];
10466
10467  let tempDiv = document.createElement("div");
10468  tempDiv.innerHTML = servicesHtml;
10469
10470  return Array.from(tempDiv.querySelectorAll("p")).map(p =>
10471  p.textContent.trim()
10472  );
10473 }
10474 } catch (err) {
10475 console.error("Failed to load form data:", err);
10476 }
10477
10478 return [];
10479 }
10480
10481 async function replaceInputWithDropdown() {
10482 let servicesArray = await getServicesAndProducts();
10483 if (servicesArray.length === 0) return;
10484
10485 // Add "Services" to the beginning and "Other" to the end
10486 servicesArray = [...servicesArray, "Other"];
10487
10488 let labels = document.querySelectorAll("label");
10489 labels.forEach(label => {
10490 let labelText = label.textContent.trim();
10491 if (
10492  labelText.includes("Services") ||
10493  labelText.includes("Products") ||
10494  labelText.includes("Case Study")
10495 ) {
10496  let input = label.parentElement.querySelector("input[type='text']");
10497  if (input) {
10498  let select = document.createElement("select");
10499  select.name = input.name;
10500  select.id = input.id;
10501
10502  servicesArray.forEach(service => {
10503  let option = document.createElement("option");
10504  option.value = service;
10505  option.textContent = service;
10506  select.appendChild(option);
10507  });
10508
10509  input.replaceWith(select);
10510  }
10511 }
10512 });
10513 }
10514
10515 await replaceInputWithDropdown();
10516 });
10517});
10518//]]>
10519</script>
10519
10520<!-- form dropdown code end --><!-- form button code begin -->
10520<script>
10521//<![CDATA[
10522
10523 function waitForDmAPI(callback) {
10524 if (typeof dmAPI !== 'undefined') {
10525 callback();
10526 } else {
10527 setTimeout(() => waitForDmAPI(callback), 100);
10528 }
10529 }
10530
10531 async function getFormText() {
10532 var collection = await dmAPI.loadCollectionsAPI();
10533 var buttonsData = await collection.data("buttons").get();
10534
10535 if (buttonsData.values.length > 0) {
10536 let formHtml = buttonsData.values[0].data.Form_Button;
10537 let tempDiv = document.createElement("div");
10538 tempDiv.innerHTML = formHtml;
10539 return tempDiv.textContent || tempDiv.innerText;
10540 }
10541
10542 return null;
10543 }
10544
10545 async function updateSubmitButton() {
10546 try {
10547 let formText = await getFormText();
10548 if (!formText) {
10549 console.warn("No form text found.");
10550 return;
10551 }
10552
10553 let buttons = document.querySelectorAll('input[type="submit"][value="Form Button"]');
10554 buttons.forEach(button => {
10555 button.value = formText;
10556 });
10557
10558 console.log("Updated submit buttons with new value:", formText);
10559 } catch (error) {
10560 console.error("Error updating submit buttons:", error);
10561 }
10562 }
10563
10564 document.addEventListener("DOMContentLoaded", function () {
10565 waitForDmAPI(updateSubmitButton);
10566 });
10567
10568//]]>
10569</script>
10569
10570<!-- form button code end --><!--  End Body End Markup -->
10571<!--  Site Wide Html Markup -->
10572<!--  Site Wide Html Markup -->
10573<!--  Begin Product Custom HTML Markup -->
10574<div id="fb-root"></div> 
10575<script>(function(d, s, id) { 
10576var js, fjs = d.getElementsByTagName(s)[0]; 
10577if (d.getElementById(id)) return; 
10578js = d.createElement(s); js.id = id; 
10579js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.8&appId=1822295708016537"; 
10580fjs.parentNode.insertBefore(js, fjs); 
10581}(document, 'script', 'facebook-jssdk'));</script>
vendor: 2 bytes, line 10581
10581
10582<script>
10583//make sure video's on iOS load first screen of the video.
10584dmAPI.runOnReady('russTest',function() {
10585    var _hibu_vid = document.getElementsByTagName('video');
10586    for (var i = 0; i < _hibu_vid.length; i++) {
10587        _hibu_vid[i].load();
10588    }
10589})
10590</script>
vendor: 2 bytes, line 10590
10590
10591<script>
10592//simple addition by Russ to add consent banner link into footer - updated by Adrian to account for Termly installs.
10593(function () {
10594  var poweredByFirstAnchor = document.querySelector('.poweredBy a:first-child');
10595  if (!poweredByFirstAnchor) return;
10596
10597  if (window.Termly) {
10598    var termlySpan = document.createElement('span');
10599    termlySpan.className = 'termly-display-preferences';
10600    termlySpan.textContent = 'Manage Consent Preferences';
10601    poweredByFirstAnchor.insertAdjacentElement('afterend', termlySpan);
10602  } else {
10603    var spSpan = document.createElement('span');
10604    spSpan.id = 'spConsentWrapper';
10605    poweredByFirstAnchor.insertAdjacentElement('afterend', spSpan);
10606    window.localStorage.setItem('__springtime__repop__target', '#spConsentWrapper');
10607  }
10608})();
10609</script>
10609
10610<style>
10611  #spConsentWrapper:before,
10612  .poweredBy .termly-display-preferences:before { content: " | " }
10613  .poweredBy .termly-display-preferences { cursor: pointer; }
10614</style>
10615<script>
10616if (window.location.search.indexOf('preview') > 0) {
10617  window.localStorage.setItem('ae_user_consent_decision_has_been_made', true);
10618  var spEl = document.getElementById('spConsentWrapper');
10619  if (spEl) spEl.remove();
10620}
10621</script>
10621<!--  End Product Custom HTML Markup -->
10622</body>
10623</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.