PageSourceSearch

https://deepskyblue-worm-192798.hostingersite.com/wp-content/plugi…/assets/frontend-runtime.js?ver=1785261394

js deepskyblue-worm-192798.hostingersite.com collected 2026-10-03 04:22:24 UTC 95,899 bytes, 1,975 lines download raw bytes

1(function () {
2    function onReady(callback) {
3        if (document.readyState === 'loading') {
4            document.addEventListener('DOMContentLoaded', callback);
5            return;
6        }
7        callback();
8    }
9
10    function escapeHtml(value) {
11        return String(value || '').replace(/[&<>"']/g, function (char) {
12            return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;' }[char];
13        });
14    }
15
16    function restUrl() {
17        if (window.OCFRuntime && window.OCFRuntime.restUrl) {
18            return window.OCFRuntime.restUrl;
19        }
20        if (window.wpApiSettings && window.wpApiSettings.root) {
21            return window.wpApiSettings.root + 'ocf/v1/lead';
22        }
23        return '/wp-json/ocf/v1/lead';
24    }
25
26    function normalizedSectionOrderPath() {
27        var path = String(window.location.pathname || '/').replace(/\/+$/, '');
28        return path || '/';
29    }
30
31    function sectionOrderForCurrentPage() {
32        var runtime = window.OCFRuntime || {};
33        var order = runtime.sectionOrder || {};
34        if (!order || typeof order !== 'object' || Array.isArray(order)) return [];
35        var current = order[normalizedSectionOrderPath()];
36        return Array.isArray(current) ? current.filter(Boolean).map(String) : [];
37    }
38
39    function isPageSectionElement(section) {
40        if (!section || !section.getAttribute) return false;
41        var sectionId = String(section.getAttribute('data-ocf-section') || '').toLowerCase();
42        if (!sectionId || sectionId === 'header' || sectionId === 'footer') return false;
43        var instance = String(section.getAttribute('data-ocf-section-instance') || '');
44        return instance.indexOf('global:') !== 0;
45    }
46
47    function sectionInstance(section) {
48        return String(section && section.getAttribute ? section.getAttribute('data-ocf-section-instance') || '' : '');
49    }
50
51    function sectionHasImageOverlay(section) {
52        if (!section || !section.querySelector) return false;
53        if (section.getAttribute('data-ocf-bg-image-active') === '1') return true;
54        if (section.querySelector('[class*="__shade"], [class*="-shade"], [data-ocf-overlay]')) return true;
55        var computed = window.getComputedStyle(section);
56        return computed && String(computed.backgroundImage || '').indexOf('url
56(') !== -1;
57    }
58
59    function validOverlayValue(value, fallback) {
60        var parsed = parseFloat(value);
61        if (!Number.isFinite(parsed)) parsed = fallback;
62        parsed = Math.max(0, Math.min(1, parsed));
63        return String(Math.round(parsed * 100) / 100);
64    }
65
66    function applySectionOrder(order) {
67        if (!Array.isArray(order) || order.length < 2) return;
68        var sections = Array.prototype.slice.call(document.querySelectorAll('[data-ocf-section]')).filter(isPageSectionElement);
69        if (sections.length < 2) return;
70
71        var byInstance = {};
72        sections.forEach(function (section) {
73            var instance = sectionInstance(section);
74            if (instance) byInstance[instance] = section;
75        });
76
77        var ordered = order.map(function (instance) {
78            return byInstance[String(instance || '')] || null;
79        }).filter(Boolean);
80        if (ordered.length < 2) return;
81
82        var parent = ordered[0].parentNode;
83        if (!parent) return;
84        var siblings = sections.filter(function (section) {
85            return section.parentNode === parent;
86        });
87        if (siblings.length < 2) return;
88
89        var orderedLookup = {};
90        ordered.forEach(function (section) {
91            orderedLookup[sectionInstance(section)] = true;
92        });
93        var finalOrder = ordered.concat(siblings.filter(function (section) {
94            return !orderedLookup[sectionInstance(section)];
95        }));
96
97        var cursor = siblings[0];
98        finalOrder.forEach(function (section) {
99            if (section.parentNode !== parent) return;
100            if (section !== cursor) {
101                parent.insertBefore(section, cursor);
102            }
103            cursor = section.nextSibling;
104        });
105    }
106
107    function applyStoredSectionOrder() {
108        applySectionOrder(sectionOrderForCurrentPage());
109    }
110
111    function submitLead(payload) {
112        return fetch(restUrl(), {
113            method: 'POST',
114            headers: { 'Content-Type': 'application/json' },
115            body: JSON.stringify(payload)
116        }).then(function (response) {
117            return response.json().catch(function () {
118                return { ok: response.ok };
119            });
120        });
121    }
122
123    var optimisticSuccessDelay = 500;
124
125    function getPhoneOptions(combo) {
126        try {
127            return JSON.parse(combo ? combo.getAttribute('data-phone-options') || '[]' : '[]');
128        } catch (error) {
129            return [];
130        }
131    }
132
133    function sortedPhoneOptions(combo) {
134        return getPhoneOptions(combo).slice().sort(function (a, b) {
135            return String(b.dial || '').replace(/\D/g, '').length - String(a.dial || '').replace(/\D/g, '').length;
136        });
137    }
138
139    function getInternationalPhone(input) {
140        if (!input) return '';
141        var local = input.value.trim();
142        if (!local) return '';
143
144        var combo = input.closest('[data-phone-combo]');
145        var digits = local.replace(/\D/g, '');
146        if (local.charAt(0) === '+') {
147            var match = sortedPhoneOptions(combo).find(function (country) {
148                var countryDigits = String(country.dial || '').replace(/\D/g, '');
149                return countryDigits && digits.indexOf(countryDigits) === 0;
150            });
151            if (match) {
152                var dialDigits = String(match.dial || '').replace(/\D/g, '');
153                return digits.length > dialDigits.length ? '+' + digits : '';
154            }
155            return digits.length > 1 ? '+' + digits : '';
156        }
157
158        var country = combo ? combo.querySelector('[data-phone-country]') : null;
159        var dial = country ? country.value.trim() : '';
160        return dial && digits ? dial + digits : digits;
161    }
162
163    window.ocfGetInternationalPhone = getInternationalPhone;
164    window.getInternationalPhone = getInternationalPhone;
165
166    function groupPhoneDigits(value) {
167        if (value.length <= 3) return value;
168        if (value.length <= 6) return value.slice(0, 3) + '-' + value.slice(3);
169        if (value.length <= 10) return value.slice(0, 3) + '-' + value.slice(3, 6) + '-' + value.slice(6);
170
171        var groups = [];
172        for (var index = 0; index < value.length; index += 4) {
173            groups.push(value.slice(index, index + 4));
174        }
175        return groups.join('-');
176    }
177
178    function initPhoneCombos() {
179        document.querySelectorAll('[data-phone-combo]').forEach(function (combo) {
180            if (combo.getAttribute('data-ocf-phone-bound') === '1') return;
181            combo.setAttribute('data-ocf-phone-bound', '1');
182
183            var input = combo.querySelector('[data-phone-local]');
184            var flag = combo.querySelector('[data-phone-flag]');
185            if (!input || !flag) return;
186
187            var defaultDial = combo.getAttribute('data-phone-default') || '+1';
188            var defaultFlag = flag.textContent || 'US';
189            var options = sortedPhoneOptions(combo);
190
191            function formatPhoneValue(raw) {
192                var match = options.find(function (country) {
193                    var dial = String(country.dial || '');
194                    return dial && raw.indexOf(dial) === 0;
195                });
196                var dial = match ? String(match.dial || '') : defaultDial;
197                if (raw.indexOf(dial) !== 0) return raw;
198
199                var local = raw.slice(dial.length);
200                return local ? dial + '-' + groupPhoneDigits(local) : dial;
201            }
202
203            function normalizeValue() {
204                var value = input.value || '';
205                var digits = value.replace(/\D/g, '').slice(0, 15);
206                if (!value) {
207                    input.value = defaultDial;
208                    return;
209                }
210                if (!digits) {
211                    input.value = '+';
212                    return;
213                }
214                if (value.charAt(0) !== '+') {
215                    var defaultDigits = defaultDial.replace(/\D/g, '');
216                    if (digits.length <= 10 && defaultDigits) {
217                        digits = defaultDigits + digits;
218                    }
219                }
220                input.value = formatPhoneValue('+' + digits);
221            }
222
223            function updateFlag() {
224                var value = input.value || '';
225                var dialDigits = value.replace(/^\+/, '').replace(/\D/g, '');
226                var match = options.find(function (country) {
227                    var countryDigits = String(country.dial || '').replace(/\D/g, '');
228                    return countryDigits && dialDigits.indexOf(countryDigits) === 0;
229                });
230                flag.textContent = match ? match.flag : defaultFlag;
231            }
232
233            function placeCaretEnd() {
234                var end = input.value.length;
235                try {
236                    input.setSelectionRange(end, end);
237                } catch (error) {}
238            }
239
240            if (!input.value) {
241                input.value = defaultDial;
242            }
243            normalizeValue();
244            updateFlag();
245
246            input.addEventListener('focus', function () {
247                if (!input.value || input.value.charAt(0) !== '+') {
248                    input.value = defaultDial;
249                }
250                window.setTimeout(placeCaretEnd, 0);
251            });
252
253            input.addEventListener('keydown', function (event) {
254                var start = input.selectionStart || 0;
255                var end = input.selectionEnd || start;
256                if ((event.key === 'Backspace' && start <= 1 && end <= 1) || (event.key === 'Delete' && start === 0 && end <= 1)
256) {
257                    event.preventDefault();
258                    if (input.value.charAt(0) !== '+') input.value = '+';
259                    try {
260                        input.setSelectionRange(1, 1);
261                    } catch (error) {}
262                }
263            });
264
265            input.addEventListener('input', function () {
266                var before = input.value;
267                var caret = input.selectionStart || before.length;
268                normalizeValue();
269                updateFlag();
270                if (before !== input.value && document.activeElement === input) {
271                    var next = Math.max(1, caret + (input.value.length - before.length));
272                    try {
273                        input.setSelectionRange(next, next);
274                    } catch (error) {}
275                }
276            });
277        });
278    }
279
280    function initLeadForms() {
281        document.querySelectorAll('[data-ocf-lead-form], .ocf-simple-form').forEach(function (form) {
282            if (form.getAttribute('data-ocf-lead-bound') === '1') return;
283            form.setAttribute('data-ocf-lead-bound', '1');
284
285            form.addEventListener('submit', function (event) {
286                event.preventDefault();
287                var btn = form.querySelector('.form-submit-btn, [type="submit"]');
288                var status = form.querySelector('.ocf-form-status');
289                var fields = form.querySelector('.hero-form-fields');
290                var nameInput = form.querySelector('[name="name"]');
291                var emailInput = form.querySelector('[name="email"]');
292                var phoneInput = form.querySelector('[name="phone"]');
293                var descInput = form.querySelector('[name="description"]');
294                var honeypotInput = form.querySelector('[name="website_url"]');
295                var companyInput = form.querySelector('[name="company"]');
296                var name = nameInput ? nameInput.value.trim() : '';
297                var email = emailInput ? emailInput.value.trim() : '';
298                var phone = getInternationalPhone(phoneInput);
299                var defaultText = btn ? btn.textContent : '';
300                var labels = {
301                    sending: form.dataset.labelSending || '...',
302                    successTitle: form.dataset.labelSuccessTitle || '',
303                    successMessage: form.dataset.labelSuccessMessage || '',
304                    genericError: form.dataset.labelGenericError || 'Something went wrong. Please try again.',
305                    networkError: form.dataset.labelNetworkError || 'Network error. Please try again.'
306                };
307
308                if (!name) {
309                    if (nameInput) nameInput.focus();
310                    return;
311                }
312                if (!email && !phone) {
313                    if (emailInput) emailInput.focus();
314                    return;
315                }
316                if (email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
317                    if (emailInput) emailInput.focus();
318                    return;
319                }
320
321                if (btn) {
322                    btn.disabled = true;
323                    btn.textContent = labels.sending;
324                }
325                if (status) {
326                    status.classList.remove('is-visible');
327                    status.innerHTML = '';
328                }
329
330                var successShown = false;
331                var showSuccess = function () {
332                    if (successShown) return;
333                    successShown = true;
334                    if (fields) fields.style.display = 'none';
335                    (form.closest('.hero-form-card') || form).querySelectorAll('[data-form-card-heading]').forEach(function (node) {
336                        node.style.display = 'none';
337                    });
338                    if (status) {
339                        var successMessageHtml = labels.successMessage ? '<p>' + escapeHtml(labels.successMessage) + '</p>' : '';
340                        status.classList.add('is-visible');
341                        status.innerHTML = '<div class="ocf-form-success"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg><h3>' + escapeHtml(labels.successTitle) + '</h3>' + successMessageHtml + '</div>';
342                    }
343                };
344                var successTimer = setTimeout(showSuccess, optimisticSuccessDelay);
345
346                submitLead({
347                    name: name,
348                    email: email,
349                    phone: phone,
350                    description: descInput ? descInput.value.trim() : '',
351                    website_url: honeypotInput ? honeypotInput.value : '',
352                    company: companyInput ? companyInput.value : '',
353                    page_url: window.location.href
354                }).then(function (response) {
355                    if (response.ok) {
356                        showSuccess();
357                        return;
358                    }
359                    if (successShown) return;
360                    clearTimeout(successTimer);
361                    if (btn) {
362                        btn.disabled = false;
363                        btn.textContent = defaultText;
364                    }
365                    if (status) {
366                        status.classList.add('is-visible');
367                        status.innerHTML = '<p class="ocf-form-error">' + escapeHtml(response.error || labels.genericError) + '</p>';
368                        setTimeout(function () { status.classList.remove('is-visible'); }, 5000);
369                    }
370                }).catch(function () {
371                    if (successShown) return;
372                    clearTimeout(successTimer);
373                    if (btn) {
374                        btn.disabled = false;
375                        btn.textContent = defaultText;
376                    }
377                    if (status) {
378                        status.classList.add('is-visible');
379                        status.innerHTML = '<p class="ocf-form-error">' + escapeHtml(labels.networkError) + '</p>';
380                        setTimeout(function () { status.classList.remove('is-visible'); }, 5000);
381                    }
382                });
383            });
384        });
385    }
386
387    function initBookingWizard() {
388        var wizard = document.getElementById('booking-wizard');
389        if (!wizard || wizard.getAttribute('data-ocf-booking-bound') === '1') return;
390        wizard.setAttribute('data-ocf-booking-bound', '1');
391
392        var totalSteps = 3;
393        var currentStep = 1;
394        var state = {
395            services: [],
396            service_other: '',
397            description: '',
398            notes: '',
399            first_name: '',
400            last_name: '',
401            phone: '',
402            email: '',
403            address: ''
404        };
405
406        var progressBar = document.getElementById('bw-progress-bar');
407        var nextBtn = document.getElementById('bw-next');
408        var backBtn = document.getElementById('bw-back');
409        var nav = document.getElementById('bw-nav');
410        var overlay = document.getElementById('booking-wizard-overlay');
411        var closeBtn = document.getElementById('bw-close');
412        var backdrop = document.getElementById('bw-backdrop');
413        var serviceOtherWrap = document.getElementById('bw-service-other-wrap');
414        var serviceOtherInput = document.getElementById('bw-service-other');
415        var labels = {
416            services: wizard.dataset.labelServices || '',
417            contact: wizard.dataset.labelContact || '',
418            name: wizard.dataset.labelName || 'Name',
419            phone: wizard.dataset.labelPhone || 'Phone',
420            email: wizard.dataset.labelEmail || 'Email',
421            address: wizard.dataset.labelAddress || '',
422            details: wizard.dataset.labelDetails || '',
423            complete: wizard.dataset.labelComplete || '',
424            continueText: wizard.dataset.labelContinue || '',
425            sending: wizard.dataset.labelSending || '...',
426            emailError: wizard.dataset.labelEmailError || ''
427        };
428
429        if (!nextBtn || !backBtn || !nav) return;
430
431        function shake(el) {
432            if (!el) return;
433            el.classList.remove('bw-shake');
434            void el.offsetHeight;
435            el.classList.add('bw-shake');
436            setTimeout(function () { el.classList.remove('bw-shake'); }, 500);
437        }
438
439        function syncServiceOtherInput() {
440            if (!serviceOtherWrap) return;
441            var show = state.services.indexOf('Other') > -1;
442            serviceOtherWrap.style.display = show ? 'block' : 'none';
443            if (show && serviceOtherInput && serviceOtherInput.value !== state.service_other) {
444                serviceOtherInput.value = state.service_other || '';
445            }
446        }
447
448        function updateProgress() {
449            if (!progressBar) return;
450            progressBar.style.width = Math.min((currentStep / totalSteps) * 100, 100) + '%';
451        }
452
453        function updateNav() {
454            backBtn.style.visibility = currentStep > 1 ? 'visible' : 'hidden';
455            if (currentStep === totalSteps) {
456                nextBtn.innerHTML = escapeHtml(labels.complete) + ' <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>';
457            } else {
458                nextBtn.innerHTML = escapeHtml(labels.continueText) + ' <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><polyline points="12 5 19 12 12 19"/></svg>';
459            }
460        }
461
462        function goTo(step) {
463            if (step < 1 || step > totalSteps + 1) return;
464
465            var currentEl = wizard.querySelector('.bw-step.is-active');
466            if (currentEl) {
467                currentEl.classList.add('is-leaving');
468                currentEl.classList.remove('is-active');
469            }
470
471            setTimeout(function () {
472                if (currentEl) {
473                    currentEl.classList.remove('is-leaving');
474                    currentEl.style.display = 'none';
475                }
476
477                var target = step === totalSteps + 1
478                    ? wizard.querySelector('[data-step="success"]')
479                    : wizard.querySelector('[data-step="' + step + '"]');
480
481                if (step === totalSteps + 1) {
482                    nav.style.display = 'none';
483                }
484                if (target) {
485                    if (step === 1) syncServiceOtherInput();
486                    target.style.display = '';
487                    void target.offsetHeight;
488                    target.classList.add('is-active');
489                }
490
491                currentStep = step;
492                updateProgress();
493                updateNav();
494                window.scrollTo({ top: 0, behavior: 'smooth' });
495            }, 250);
496        }
497
498        wizard.querySelectorAll('.bw-service-tile').forEach(function (tile) {
499            tile.addEventListener('click', function () {
500                tile.classList.toggle('is-selected');
501                var name = tile.getAttribute('data-service-name');
502                var index = state.services.indexOf(name);
503                if (index > -1) {
504                    state.services.splice(index, 1);
505                } else {
506                    state.services.push(name);
507                }
508
509                if (tile.getAttribute('data-service-id') === 'other' && serviceOtherWrap) {
510                    var show = tile.classList.contains('is-selected');
511                    serviceOtherWrap.style.display = show ? 'block' : 'none';
512                    if (show && serviceOtherInput) serviceOtherInput.focus();
513                    if (!show) {
514                        state.service_other = '';
515                        if (serviceOtherInput) serviceOtherInput.value = '';
516                    }
517                }
518            });
519        });
520
521        if (serviceOtherInput) {
522            serviceOtherInput.addEventListener('input', function () {
523                state.service_other = serviceOtherInput.value.trim();
524            });
525        }
526
527        function validateContactForm() {
528            var valid = true;
529            var fields = [
530                { id: 'bw-fname', key: 'first_name', required: true },
531                { id: 'bw-lname', key: 'last_name', required: true },
532                { id: 'bw-phone', key: 'phone', required: true },
533                { id: 'bw-email', key: 'email', required: true },
534                { id: 'bw-address', key: 'address' }
535            ];
536
537            wizard.querySelectorAll('.bw-invalid').forEach(function (el) {
538                el.classList.remove('bw-invalid');
539            });
540            wizard.querySelectorAll('.bw-error-msg').forEach(function (el) {
541                el.remove();
542            });
543
544            fields.forEach(function (field) {
545                var input = document.getElementById(field.id);
546                if (!input) return;
547                var localVal = input.value.trim();
548                var value = field.key === 'phone' ? getInternationalPhone(input) : localVal;
549                state[field.key] = value;
550
551                if ((!localVal || (field.key === 'phone' && !value)) && field.required) {
552                    input.classList.add('bw-invalid');
553                    valid = false;
554                }
555
556                if (field.key === 'email' && value && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
557                    input.classList.add('bw-invalid');
558                    var msg = document.createElement('div');
559                    msg.className = 'bw-error-msg';
560                    msg.textContent = labels.emailError;
561                    input.parentNode.appendChild(msg);
562                    valid = false;
563                }
564            });
565
566            if (!valid) {
567                var firstInvalid = wizard.querySelector('.bw-invalid');
568                if (firstInvalid) {
569                    shake(firstInvalid);
570                    firstInvalid.focus();
571                }
572            }
573
574            state.description = (document.getElementById('bw-description') || {}).value || '';
575            return valid;
576        }
577
578        function validate(step) {
579            if (step === 1) {
580                if (state.services.length === 0) {
581                    shake(wizard.querySelector('.bw-service-grid'));
582                    return false;
583                }
584                if (state.services.indexOf('Other') > -1 && !state.service_other) {
585                    shake(serviceOtherWrap);
586                    if (serviceOtherInput) serviceOtherInput.focus();
587                    return false;
588                }
589                return true;
590            }
591            if (step === 2) {
592                return validateContactForm();
593            }
594            return true;
595        }
596
597        function buildSummary() {
598            var summary = document.getElementById('bw-summary');
599            if (!summary) return;
600            var servicesList = state.services.map(function (service) {
601                return service === 'Other' && state.service_other ? state.service_other : service;
602            }).filter(Boolean);
603
604            function summaryChip(value) {
605                return '<span class="bw-summary-chip">' + escapeHtml(value) + '</span>';
606            }
607
608            function contactItem(label, value) {
609                if (!value) return '';
610                return '<div class="bw-summary-contact-item"><span class="bw-summary-contact-label">' +
611                    escapeHtml(label) + '</span><span class="bw-summary-contact-value">' + escapeHtml(value) + '</span></div>';
612            }
613
614            var contactHtml = '<div class="bw-summary-contact-grid">' +
615                contactItem(labels.name, (state.first_name + ' ' + state.last_name).trim()) +
616                contactItem(labels.phone, state.phone) +
617                contactItem(labels.email, state.email) +
618            '</div>';
619
620            var rows = [
621                {
622                    type: 'services',
623                    icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>',
624                    label: labels.services,
625                    value: '<div class="bw-summary-chip-list">' + servicesList.map(summaryChip).join('') + '</div>'
626                },
627                {
628                    type: 'contact',
629                    icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>',
630                    label: labels.contact,
631                    value: contactHtml
632                }
633            ];
634            var insertAt = 1;
635
636            if (state.description || state.notes) {
637                var detailParts = [];
638                if (state.description) detailParts.push(escapeHtml(state.description));
639                if (state.notes) detailParts.push(escapeHtml(state.notes));
640                rows.splice(insertAt, 0, {
641                    type: 'details',
642                    icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>',
643                    label: labels.details,
644                    value: detailParts.join('<br>')
645                });
646                insertAt++;
647            }
648
649            if (state.address) {
650                rows.push({
651                    type: 'address',
652                    icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>',
653                    label: labels.address,
654                    value: escapeHtml(state.address)
655                });
656            }
657
658            summary.innerHTML = rows.map(function (row) {
659                return '<div class="bw-summary-row bw-summary-row--' + escapeHtml(row.type || 'item') + '"><div class="bw-summary-icon">' +
660                    row.icon + '</div><div class="bw-summary-body"><div class="bw-summary-label">' +
661                    row.label + '</div><div class="bw-summary-value">' + row.value + '</div></div></div>';
662            }).join('');
663        }
664
665        function submit() {
666            state.notes = (document.getElementById('bw-notes') || {}).value || '';
667            nextBtn.classList.add('is-loading');
668            nextBtn.innerHTML = escapeHtml(labels.sending) + ' <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>';
669
670            var successShown = false;
671            var showSuccess = function () {
672                if (successShown) return;
673                successShown = true;
674                goTo(totalSteps + 1);
675            };
676            var successTimer = setTimeout(showSuccess, optimisticSuccessDelay);
677
678            submitLead(Object.assign({}, state, {
679                page_url: window.location.href,
680                website_url: (document.getElementById('bw-website-url') || {}).value || '',
681                company: (document.getElementById('bw-company') || {}).value || ''
682            })).then(function (response) {
683                if (response.ok) {
684                    showSuccess();
685                    return;
686                }
687                if (successShown) return;
688                clearTimeout(successTimer);
689                nextBtn.classList.remove('is-loading');
690                nextBtn.textContent = labels.complete || labels.continueText || 'Continue';
691                window.alert(response.error || 'Something went wrong. Please try again.');
692            }).catch(function () {
693                if (successShown) return;
694                clearTimeout(successTimer);
695                nextBtn.classList.remove('is-loading');
696                nextBtn.textContent = labels.complete || labels.continueText || 'Continue';
697                window.alert('Network error. Please try again.');
698            });
699        }
700
701        nextBtn.addEventListener('click', function () {
702            if (!validate(currentStep)) return;
703            if (currentStep === totalSteps) {
704                submit();
705                return;
706            }
707            if (currentStep === totalSteps - 1) {
708                validateContactForm();
709                buildSummary();
710            }
711            goTo(currentStep + 1);
712        });
713
714        backBtn.addEventListener('click', function () {
715            goTo(currentStep - 1);
716        });
717
718        wizard.addEventListener('keydown', function (event) {
719            if (event.key !== 'Enter' || currentStep > totalSteps) return;
720            var tag = (event.target.tagName || '').toLowerCase();
721            if (tag === 'textarea') return;
722            event.preventDefault();
723            nextBtn.click();
724        });
725
726        function openWizard() {
727            if (!overlay) return;
728            overlay.classList.add('is-open');
729            overlay.setAttribute('aria-hidden', 'false');
730            document.body.style.overflow = 'hidden';
731        }
732
733        function closeWizard() {
734            if (!overlay) return;
735            overlay.classList.remove('is-open');
736            overlay.setAttribute('aria-hidden', 'true');
737            document.body.style.overflow = '';
738        }
739
740        document.addEventListener('click', function (event) {
741            var trigger = event.target.closest('[data-open-booking], [href="#book"]');
742            if (!trigger) return;
743            event.preventDefault();
744            openWizard();
745        });
746
747        if (closeBtn) closeBtn.addEventListener('click', closeWizard);
748        if (backdrop) backdrop.addEventListener('click', closeWizard);
749        if (window.location.hash === '#book') openWizard();
750
751        document.addEventListener('keydown', function (event) {
752            if (event.key === 'Escape' && overlay && overlay.classList.contains('is-open')) {
753                closeWizard();
754            }
755        });
756    }
757
758    function initEditorBridge() {
759        var params = new URLSearchParams(window.location.search);
760        if (params.get('ocf_editor') !== '1') return;
761        if (!params.get('ocf_editor_token')) return;
762        if (window.OCFRuntime && window.OCFRuntime.editor && window.OCFRuntime.editor.active === false) return;
763        if (document.documentElement.getAttribute('data-ocf-editor-bound') === '1') return;
764        document.documentElement.setAttribute('data-ocf-editor-bound', '1');
765
766        var selectedSection = null;
767        var selectedField = null;
768        var selectedPostItem = null;
769        var restoreSelectionKey = 'ocfEditor.restoreSelection';
770        var originalSectionState = new WeakMap();
771        var style = document.createElement('style');
772        style.textContent = [
773            '[data-ocf-section]{position:relative;}',
774            '[data-ocf-section].ocf-editor-selected{outline:2px solid #1f3e90;outline-offset:-2px;}',
775            '[data-ocf-section].ocf-editor-hover{outline:2px dashed rgba(31,62,144,.55);outline-offset:-2px;}',
776            '[data-ocf-field].ocf-editor-field-selected{outline:2px solid #2f7668;outline-offset:3px;border-radius:4px;}',
777            '[data-ocf-post-type][data-ocf-post-id].ocf-editor-post-selected{outline:2px solid #2f7668;outline-offset:4px;border-radius:6px;}',
778            '[data-ocf-preview-loop-hidden="1"]{display:none!important;}',
779            '[data-ocf-field][contenteditable="true"]{cursor:text;caret-color:#2f7668;user-select:text;-webkit-user-select:text;}',
780            '[data-ocf-field][contenteditable="true"]:focus{outline:2px solid #2f7668;outline-offset:3px;border-radius:4px;}',
781            '[data-ocf-bg-image-active="1"] > [class*="__media"],[data-ocf-bg-image-active="1"] > img[class*="__bg-image"],[data-ocf-bg-image-active="1"] > img[class*="__image"]{display:none!important;}'
782        ].join('');
783        document.head.appendChild(style);
784
785        function findSection(target) {
786            return target && target.closest ? target.closest('[data-ocf-section]') : null;
787        }
788
789        function findField(target, section) {
790            if (!target || !target.closest || !section) return null;
791            var field = target.closest('[data-ocf-field]');
792            if (field && isHeaderNavField(field, section)) return null;
793            return field && section.contains(field) ? field : null;
794        }
795
796        function findAction(target, section) {
797            if (!target || !target.closest || !section) return null;
798            var action = target.closest('[data-ocf-action]');
799            return action && section.contains(action) ? action : null;
800        }
801
802        function findFormSurface(target, section) {
803            if (!target || !target.closest || !section) return null;
804            var form = target.closest('[data-ocf-lead-form], .ocf-simple-form, .hero-form-card, .ocf-form-shell');
805            return form && section.contains(form) ? form : null;
806        }
807
808        function findPostItem(target, section) {
809            if (!target || !target.closest || !section) return null;
810            if (isHeaderSection(section)) return null;
811            var item = target.closest('[data-ocf-post-type][data-ocf-post-id]');
812            return item && section.contains(item) ? item : null;
813        }
814
815        function findInstance(target, section) {
816            if (!target || !target.closest || !section) return null;
817            var instance = target.closest('[data-ocf-instance]');
818            return instance && section.contains(instance) ? instance : null;
819        }
820
821        function findRepeaterItem(field, section) {
822            if (!field || !field.closest || !section) return null;
823            var item = field.closest('[data-ocf-repeater][data-ocf-index]');
824            return item && section.contains(item) ? item : null;
825        }
826
827        function setSelectedSection(section) {
828            if (selectedSection && selectedSection !== section) {
829                selectedSection.classList.remove('ocf-editor-selected');
830            }
831            selectedSection = section;
832            if (selectedSection) {
833                selectedSection.classList.add('ocf-editor-selected');
834            }
835        }
836
837        function isInlineEditableField(field) {
838            if (!field || !field.tagName) return false;
839            var tag = field.tagName.toLowerCase();
840            return ['img', 'picture', 'video', 'iframe', 'input', 'textarea', 'select', 'form'].indexOf(tag) === -1;
841        }
842
843        function isHeaderSection(section) {
844            return String(section && section.getAttribute ? section.getAttribute('data-ocf-section') || '' : '') === 'header';
845        }
846
847        function isHeaderNavField(field, section) {
848            return isHeaderSection(section) && /^nav_/.test(String(field && field.getAttribute ? field.getAttribute('data-ocf-field') || '' : ''));
849        }
850
851        function setSelectedField(field) {
852            if (selectedField && selectedField !== field && selectedField.getAttribute('contenteditable') === 'true') {
853                selectedField.removeAttribute('contenteditable');
854            }
855            if (selectedField && selectedField !== field) {
856                selectedField.classList.remove('ocf-editor-field-selected');
857            }
858            selectedField = field;
859            if (!selectedField) return;
860            selectedField.classList.add('ocf-editor-field-selected');
861            if (isInlineEditableField(selectedField)) {
862                selectedField.setAttribute('contenteditable', 'true');
863                selectedField.setAttribute('spellcheck', 'true');
864            }
865        }
866
867        function setSelectedPostItem(item) {
868            if (selectedPostItem && selectedPostItem !== item) {
869                selectedPostItem.classList.remove('ocf-editor-post-selected');
870            }
871            selectedPostItem = item;
872            if (selectedPostItem) {
873                selectedPostItem.classList.add('ocf-editor-post-selected');
874            }
875        }
876
877        function fieldValue(field) {
878            if (!field) return '';
879            var fieldKey = field.getAttribute('data-ocf-field');
880            if (fieldKey === 'hero_rating_stars') {
881                return String(starCountFromNode(field));
882            }
883            var type = fieldType(fieldKey);
884            if (type === 'richtext') {
885                return String(field.innerHTML || '').trim();
886            }
887            return (field.innerText || field.textContent || '').replace(/\u00a0/g, ' ').trim();
888        }
889
890        function fieldType(fieldKey) {
891            var editor = window.ocfEditor || {};
892            var schema = editor.schema || {};
893            var fields = schema.fields || {};
894            var def = fields[fieldKey] || {};
895            return String(def.type || '');
896        }
897
898        function setFieldRenderedValue(node, fieldKey, value) {
899            var tag = node && node.tagName ? node.tagName.toLowerCase() : '';
900            if (fieldKey === 'hero_rating_stars') {
901                updateStarNodes(node, value);
902                return;
903            }
904            if (fieldType(fieldKey) === 'richtext') {
905                node.innerHTML = value || '';
906                return;
907            }
908            if (tag === 'input' || tag === 'textarea') {
909                node.value = value || '';
910                return;
911            }
912            node.textContent = value || '';
913        }
914
915        function postFields(post) {
916            return post && post.ocf_state && typeof post.ocf_state === 'object' ? post.ocf_state : {};
917        }
918
919        function applyPostToNode(node, post) {
920            var fields = postFields(post);
921            Object.keys(fields).forEach(function (field) {
922                node.querySelectorAll('[data-ocf-field="' + CSS.escape(field) + '"]').forEach(function (fieldNode) {
923                    setFieldRenderedValue(fieldNode, field, fields[field] || '');
924                });
925                if (node.getAttribute('data-ocf-field') === field) {
926                    setFieldRenderedValue(node, field, fields[field] || '');
927                }
928            });
929        }
930
931        function resetPreviewPostNode(node, postType, postId) {
932            node.setAttribute('data-ocf-post-type', postType);
933            node.setAttribute('data-ocf-post-id', String(postId));
934            node.setAttribute('data-ocf-preview-loop-post', '1');
935            node.removeAttribute('data-ocf-preview-loop-hidden');
936            node.classList.remove('ocf-editor-post-selected');
937            node.style.removeProperty('display');
938            if (node.matches && node.matches('a[href]')) node.setAttribute('href', '#');
939            node.querySelectorAll('a[href]').forEach(function (anchor) {
940                anchor.setAttribute('href', '#');
941            });
942        }
943
944        function loopTemplateGroups(postType) {
945            var groups = [];
946            var seen = [];
947            document.querySelectorAll('[data-ocf-post-type="' + CSS.escape(postType) + '"][data-ocf-post-id]:not([data-ocf-preview-loop-post])').forEach(function (node) {
948                var parent = node.parentNode;
949                if (!parent || seen.indexOf(parent) !== -1) return;
950                seen.push(parent);
951                groups.push({ parent: parent, template: node });
952            });
953            return groups;
954        }
955
956        function postLoopNodeInParent(parent, postType, postId) {
957            var nodes = Array.prototype.slice.call(parent.children || []);
958            for (var i = 0; i < nodes.length; i++) {
959                if (
960                    nodes[i] &&
961                    nodes[i].getAttribute &&
962                    nodes[i].getAttribute('data-ocf-post-type') === postType &&
963                    String(nodes[i].getAttribute('data-ocf-post-id') || '') 
963=== String(postId)
964                ) {
965                    return nodes[i];
966                }
967            }
968            return null;
969        }
970
971        function reorderPostLoopPreviewNodes(postType, posts) {
972            loopTemplateGroups(postType).forEach(function (group) {
973                posts.forEach(function (post) {
974                    var node = postLoopNodeInParent(group.parent, postType, post.id);
975                    if (!node && Number(post.id) <= 0) {
976                        node = group.template.cloneNode(true);
977                        resetPreviewPostNode(node, postType, post.id);
978                    }
979                    if (!node) return;
980                    applyPostToNode(node, post);
981                    group.parent.appendChild(node);
982                });
983            });
984        }
985
986        function applyPostLoopPreview(postType, posts) {
987            postType = String(postType || '');
988            if (!postType || !Array.isArray(posts)) return;
989            posts = posts.filter(function (post) { return post && !post._deleted && String(post.type || '') === postType; });
990            var activeIds = posts.map(function (post) { return String(post.id); });
991            document.querySelectorAll('[data-ocf-preview-loop-post][data-ocf-post-type="' + CSS.escape(postType) + '"]').forEach(function (node) {
992                node.remove();
993            });
994            document.querySelectorAll('[data-ocf-post-type="' + CSS.escape(postType) + '"][data-ocf-post-id]:not([data-ocf-preview-loop-post])').forEach(function (node) {
995                var id = String(node.getAttribute('data-ocf-post-id') || '');
996                if (activeIds.indexOf(id) === -1) {
997                    node.setAttribute('data-ocf-preview-loop-hidden', '1');
998                    return;
999                }
1000                node.removeAttribute('data-ocf-preview-loop-hidden');
1001                node.style.removeProperty('display');
1002                var match = posts.find(function (post) { return String(post.id) === id; });
1003                if (match) applyPostToNode(node, match);
1004            });
1005            reorderPostLoopPreviewNodes(postType, posts);
1006        }
1007
1008        function applyPostListPreview(posts) {
1009            if (!Array.isArray(posts)) return;
1010            var types = [];
1011            posts.forEach(function (post) {
1012                var type = String(post && post.type || '');
1013                if (type && types.indexOf(type) === -1) types.push(type);
1014            });
1015            document.querySelectorAll('[data-ocf-post-type][data-ocf-post-id]').forEach(function (node) {
1016                var type = String(node.getAttribute('data-ocf-post-type') || '');
1017                if (type && types.indexOf(type) === -1) types.push(type);
1018            });
1019            types.forEach(function (type) {
1020                applyPostLoopPreview(type, posts);
1021            });
1022        }
1023
1024        function navFieldForItem(item) {
1025            var slug = normalizedNavSlug(item);
1026            var map = {
1027                home: 'nav_home',
1028                services: 'nav_services',
1029                company: 'nav_company',
1030                about: 'nav_about',
1031                blog: 'nav_blog',
1032                contact: 'nav_contact',
1033                industries: 'nav_industries',
1034                locations: 'nav_locations',
1035                service_areas: 'nav_service_areas'
1036            };
1037            return map[slug] || '';
1038        }
1039
1040        function normalizedNavSlug(item) {
1041            var type = String(item && item.type ? item.type : '');
1042            var postType = String(item && item.post_type ? item.post_type : '');
1043            var slug = String(item && item.slug ? item.slug : '').replace(/^archive_/, '').toLowerCase();
1044            if (type === 'archive' && postType) slug = postType.toLowerCase();
1045            var map = {
1046                service: 'services',
1047                service_area: 'service_areas',
1048                location: 'locations',
1049                industry: 'industries',
1050                post: 'blog'
1051            };
1052            return map[slug] || slug;
1053        }
1054
1055        function navTemplateForField(section, field, mode) {
1056            if (!section || !field) return null;
1057            var selector = '[data-ocf-field="' + CSS.escape(field) + '"]';
1058            var nodes = Array.prototype.slice.call(section.querySelectorAll(selector));
1059            for (var index = 0;
1059 index < nodes.length; index += 1) {
1060                var node = nodes[index];
1061                if (mode === 'desktop') {
1062                    var desktop = node.closest('.nav-dropdown') || node.closest('a.nav-link');
1063                    if (desktop) return desktop.cloneNode(true);
1064                } else {
1065                    var mobile = node.closest('.mobile-accordion') || node.closest('a.mobile-link');
1066                    if (mobile) return mobile.cloneNode(true);
1067                }
1068            }
1069            return null;
1070        }
1071
1072        function genericNavLink(item, field, mode) {
1073            var link = document.createElement('a');
1074            link.href = item.url || '#';
1075            link.className = mode === 'desktop' ? 'nav-link' : 'mobile-link';
1076            if (field) link.setAttribute('data-ocf-field', field);
1077            link.textContent = item.label || 'Page';
1078            return link;
1079        }
1080
1081        function navChildPostType(item) {
1082            if (!item || !item.show_items) return '';
1083            var slug = normalizedNavSlug(item);
1084            var map = {
1085                services: 'service',
1086                service_areas: 'service_area',
1087                locations: 'location',
1088                industries: 'industry'
1089            };
1090            return map[slug] || '';
1091        }
1092
1093        function navChildLinks(postType, maxItems) {
1094            if (!postType) return [];
1095            var max = parseInt(maxItems, 10);
1096            var seen = {};
1097            var links = [];
1098            document.querySelectorAll('[data-ocf-post-type="' + CSS.escape(postType) + '"][data-ocf-post-id]').forEach(function (node) {
1099                var anchor = node.matches && node.matches('a[href]') ? node : node.closest('a[href]');
1100                if (!anchor) return;
1101                var id = node.getAttribute('data-ocf-post-id') || anchor.getAttribute('data-ocf-post-id') || anchor.href;
1102                if (seen[id]) return;
1103                var labelNode = anchor.querySelector('h3') || anchor.querySelector('.dropdown-item-title') || anchor;
1104                var label = String(labelNode.textContent || '').replace(/\s+/g, ' ').trim();
1105                if (!label) return;
1106                seen[id] = true;
1107                links.push({ id: id, label: label, url: anchor.href });
1108            });
1109            if (Number.isFinite(max) && max > 0) return links.slice(0, max);
1110            return links;
1111        }
1112
1113        function genericNavDropdown(item, field, mode, children) {
1114            var label = String(item.label || 'Page');
1115            var url = String(item.url || '#');
1116            if (mode === 'mobile') {
1117                var accordion = document.createElement('div');
1118                accordion.className = 'mobile-accordion';
1119                var trigger = document.createElement('button');
1120                trigger.className = 'mobile-accordion-trigger';
1121                trigger.type = 'button';
1122                trigger.setAttribute('data-accordion', '');
1123                var triggerLabel = document.createElement('span');
1124                if (field) triggerLabel.setAttribute('data-ocf-field', field);
1125                triggerLabel.textContent = label;
1126                trigger.appendChild(triggerLabel);
1127                trigger.appendChild(dropdownChevron('mobile-accordion-chevron', 16));
1128                var panel = document.createElement('div');
1129                panel.className = 'mobile-accordion-panel';
1130                panel.appendChild(genericNavLink(item, field, 'mobile'));
1131                children.forEach(function (child) {
1132                    var link = document.createElement('a');
1133                    link.className = 'mobile-link';
1134                    link.href = child.url || '#';
1135                    link.textContent = child.label || 'Item';
1136                    panel.appendChild(link);
1137                });
1138                accordion.appendChild(trigger);
1139                accordion.appendChild(panel);
1140                return accordion;
1141            }
1142
1143            var dropdown = document.createElement('div');
1144            dropdown.className = 'nav-dropdown';
1145            dropdown.setAttribute('data-dropdown', '');
1146            var triggerLink = document.createElement('a');
1147            triggerLink.href = url;
1148            triggerLink.className = 'nav-link nav-dropdown-trigger';
1149            if (field) triggerLink.setAttribute('data-ocf-field', field);
1150            var span = document.createElement('span');
1151            span.textContent = label;
1152            triggerLink.appendChild(span);
1153            triggerLink.appendChild(dropdownChevron('nav-dropdown-chevron', 12));
1154            var panelWrap = document.createElement('div');
1155            panelWrap.className = 'nav-dropdown-panel';
1156            var menu = document.createElement('div');
1157            menu.className = 'dropdown-menu dropdown-menu--wide';
1158            children.forEach(function (child) {
1159                var link = document.createElement('a');
1160                link.className = 'dropdown-item dropdown-item--area';
1161                link.href = child.url || '#';
1162                link.textContent = child.label || 'Item';
1163                menu.appendChild(link);
1164            });
1165            panelWrap.appendChild(menu);
1166            dropdown.appendChild(triggerLink);
1167            dropdown.appendChild(panelWrap);
1168            return dropdown;
1169        }
1170
1171        function dropdownChevron(className, size) {
1172            var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
1173            svg.setAttribute('class', className);
1174            svg.setAttribute('width', String(size));
1175            svg.setAttribute('height', String(size));
1176            svg.setAttribute('fill', 'none');
1177            svg.setAttribute('stroke', 'currentColor');
1178            svg.setAttribute('viewBox', '0 0 24 24');
1179            svg.setAttribute('stroke-width', '2');
1180            var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
1181            path.setAttribute('d', 'M19 9l-7 7-7-7');
1182            svg.appendChild(path);
1183            return svg;
1184        }
1185
1186        function updateNavNode(node, item, field) {
1187            var label = String(item.label || 'Page');
1188            var url = String(item.url || '#');
1189            if (node.matches && node.matches('a')) {
1190                node.href = url;
1191            }
1192            node.querySelectorAll('a.nav-link, a.mobile-link').forEach(function (link) {
1193                link.href = url;
1194            });
1195            if (field) {
1196                var selector = '[data-ocf-field="' + CSS.escape(field) + '"]';
1197                var fieldNodes = [];
1198                if (node.matches && node.matches(selector)) {
1199                    fieldNodes.push(node);
1200                }
1201                fieldNodes = fieldNodes.concat(Array.prototype.slice.call(node.querySelectorAll(selector)));
1202                fieldNodes.forEach(function (fieldNode) {
1203                    if (fieldNode.matches && fieldNode.matches('a')) {
1204                        fieldNode.href = url;
1205                    }
1206                    var labelNode = fieldNode.matches && fieldNode.matches('a') ? fieldNode.querySelector('span') : null;
1207                    if (labelNode) {
1208                        labelNode.textContent = label;
1209                    } else {
1210                        fieldNode.textContent = label;
1211                    }
1212                });
1213            }
1214            if (!field) {
1215                node.textContent = label;
1216            }
1217        }
1218
1219        function updateHeaderNavPreview(items) {
1220            if (!Array.isArray(items)) return;
1221            var section = document.querySelector('[data-ocf-section="header"]') || document.querySelector('header');
1222            if (!section) return;
1223            updateHeaderNavContainer(section, items, 'desktop');
1224            updateHeaderNavContainer(section, items, 'mobile');
1225        }
1226
1227        function updateHeaderNavContainer(section, items, mode) {
1228            var container = mode === 'desktop' ? section.querySelector('.nav-links') : section.querySelector('.mobile-menu-inner');
1229            if (!container) return;
1230            var trailing = mode === 'mobile' ? Array.prototype.slice.call(container.children).filter(function (child) {
1231                return child.classList && child.classList.contains('mobile-actions');
1232            }) : [];
1233            var nextNodes = [];
1234            items.forEach(function (item) {
1235                if (!item || !item.label) return;
1236                var field = navFieldForItem(item);
1237                var children = navChildLinks(navChildPostType(item), item.max_items);
1238                var node = children.length
1239                    ? genericNavDropdown(item, field, mode, children)
1240                    : genericNavLink(item, field, mode);
1241                updateNavNode(node, item, field);
1242                nextNodes.push(node);
1243            });
1244            container.replaceChildren.apply(container, nextNodes.concat(trailing));
1245        }
1246
1247        function starCountFromNode(node) {
1248            if (!node) return 5;
1249            var rating = parseFloat(node.getAttribute('data-rating') || '');
1250            if (Number.isFinite(rating)) return Math.round(Math.max(0, Math.min(5, rating)) * 10) / 10;
1251            var customStars = node.querySelectorAll('.ocf-hero__star');
1252            if (customStars.length) {
1253                var total = 0;
1254                customStars.forEach(function (star) {
1255                    var fill = parseFloat((star.style.getPropertyValue('--star-fill') || '0').replace('%', ''));
1256                    total += Number.isFinite(fill) ? Math.max(0, Math.min(100, fill)) / 100 : 0;
1257                });
1258                return Math.round(total * 10) / 10;
1259            }
1260            var filled = node.querySelectorAll('svg.is-filled').length;
1261            if (filled || node.querySelectorAll('svg').length) return filled;
1262            return 5;
1263        }
1264
1265        function updateStarNodes(node, value) {
1266            var count = Math.max(0, Math.min(5, parseFloat(value) || 0));
1267            count = Math.round(count * 10) / 10;
1268            node.setAttribute('data-rating', String(count));
1269            var customStars = node.querySelectorAll('.ocf-hero__star');
1270            if (customStars.length) {
1271                customStars.forEach(function (star, index) {
1272                    var fill = Math.max(0, Math.min(1, count - index));
1273                    star.style.setProperty('--star-fill', String(Math.round(fill * 1000) / 10) + '%');
1274                    star.classList.toggle('is-filled', fill >= 1);
1275                    star.classList.toggle('is-empty', fill <= 0);
1276                    star.classList.toggle('is-partial', fill > 0 && fill < 1);
1277                });
1278                return;
1279            }
1280            node.querySelectorAll('svg').forEach(function (svg, index) {
1281                var fill = Math.max(0, Math.min(1, count - index));
1282                var filled = fill >= 1;
1283                svg.classList.toggle('is-filled', filled);
1284                svg.classList.toggle('is-empty', fill <= 0);
1285                if (filled) {
1286                    svg.style.opacity = '';
1287                } else {
1288                    svg.style.opacity = String(0.24 + (fill * 0.76));
1289                }
1290            });
1291        }
1292
1293        function sectionFields(section) {
1294            var fields = [];
1295            section.querySelectorAll('[data-ocf-field]').forEach(function (node) {
1296                var field = node.getAttribute('data-ocf-field');
1297                if (field && fields.indexOf(field) === -1) {
1298                    fields.push(field);
1299                }
1300            });
1301            return fields;
1302        }
1303
1304        function mediaUrl(node) {
1305            if (!node) return '';
1306            var tag = node.tagName ? node.tagName.toLowerCase() : '';
1307            if (tag === 'img') return node.getAttribute('src') || '';
1308            if (tag === 'source') return node.getAttribute('srcset') || node.getAttribute('src') || '';
1309            var background = window.getComputedStyle(node).backgroundImage || '';
1310            var match = background.match(/url\(["']?([^"')]+)["']?\)/);
1311            return match ? match[1] : '';
1312        }
1313
1314        function sectionFieldPreviews(section) {
1315            var previews = {};
1316            section.querySelectorAll('[data-ocf-field]').forEach(function (node) {
1317                var field = node.getAttribute('data-ocf-field');
1318                if (!field || previews[field]) return;
1319                var url = mediaUrl(node);
1320                var action = findAction(node, section);
1321                var instance = findInstance(node, section);
1322                previews[field] = {
1323                    url: url,
1324                    tag: node.tagName ? node.tagName.toLowerCase() : '',
1325                    className: typeof node.className === 'string' ? node.className : '',
1326                    role: fieldRole(node, field, section),
1327                    action: action ? action.getAttribute('data-ocf-action') : '',
1328                    actionSlot: instance ? instance.getAttribute('data-ocf-instance') : '',
1329                };
1330            });
1331            return previews;
1332        }
1333
1334        function sectionPostTypes(section) {
1335            var types = [];
1336            section.querySelectorAll('[data-ocf-post-type][data-ocf-post-id]').forEach(function (node) {
1337                var type = String(node.getAttribute('data-ocf-post-type') || '');
1338                if (type && types.indexOf(type) === -1) {
1339                    types.push(type);
1340                }
1341            });
1342            return types;
1343        }
1344
1345        function fieldRole(node, fieldKey, section) {
1346            var key = String(fieldKey || '').toLowerCase();
1347            var tag = node && node.tagName ? node.tagName.toLowerCase() : '';
1348            var className = node && typeof node.className === 'string' ? node.className.toLowerCase() : '';
1349            var action = findAction(node, section);
1350            var actionName = action ? String(action.getAttribute('data-ocf-action') || '').toLowerCase() : '';
1351
1352            if (actionName === 'primary_cta' || key.indexOf('cta_text') !== -1 || key.indexOf('button_text') !== -1) return 'Button Label';
1353            if (actionName === 'phone' || key.indexOf('phone') !== -1) return 'Phone';
1354            if (key.indexOf('email') !== -1) return 'Email';
1355            if (key.indexOf('address') !== -1) return 'Address';
1356            if (key.indexOf('rating_stars') !== -1 || key.indexOf('stars') !== -1) return 'Star Count';
1357            if (key.indexOf('logo') !== -1) return 'Logo';
1358            if (tag === 'img' || tag === 'source' || key.indexOf('image') !== -1) return 'Image';
1359            if (className.indexOf('eyebrow') !== -1 || key.indexOf('eyebrow') !== -1) return 'Eyebrow';
1360            if (/^h[1-6]$/.test(tag) || className.indexOf('headline') !== -1 || className.indexOf('heading') !== -1 || key.indexOf('headline') !== -1 || key.indexOf('heading') !== -1 || key.indexOf('_h1') !== -1 || key.indexOf('_h2') !== -1) return 'Heading';
1361            if (className.indexOf('nav-') !== -1 || key.indexOf('nav_') === 0) return 'Navigation Label';
1362            if (className.indexOf('footer-heading') !== -1) return 'Footer Heading';
1363            if (className.indexOf('body') !== -1 || className.indexOf('description') !== -1 || className.indexOf('intro') !== -1 || key.indexOf('description') !== -1 || key.indexOf('subheadline') !== -1 || key.indexOf('intro') !== -1 || key.indexOf('bio') !== -1 || key.indexOf('content') !== -1) return 'Body Text';
1364            if (tag === 'p') return 'Body Text';
1365            return 'Text';
1366        }
1367
1368        function fieldPayload(section, field, type) {
1369            var repeaterItem = findRepeaterItem(field, section);
1370            var action = findAction(field, section);
1371            var formSurface = findFormSurface(field, section);
1372            var postItem = findPostItem(field, section);
1373            var instance = findInstance(field, section);
1374            return {
1375                type: type,
1376                sectionId: section.getAttribute('data-ocf-section'),
1377                sectionInstance: section.getAttribute('data-ocf-section-instance') || '',
1378                field: field ? field.getAttribute('data-ocf-field') : '',
1379                value: field ? fieldValue(field) : '',
1380                fieldRole: field ? fieldRole(field, field.getAttribute('data-ocf-field'), section) : '',
1381                repeater: repeaterItem ? repeaterItem.getAttribute('data-ocf-repeater') : '',
1382                index: repeaterItem ? Number(repeaterItem.getAttribute('data-ocf-index')) : undefined,
1383                postType: postItem ? postItem.getAttribute('data-ocf-post-type') : '',
1384                postId: postItem ? Number(postItem.getAttribute('data-ocf-post-id')) : undefined,
1385                action: action ? action.getAttribute('data-ocf-action') : '',
1386                actionSlot: instance ? instance.getAttribute('data-ocf-instance') : '',
1387                gradient: section.getAttribute('data-ocf-gradient') || '',
1388                formSurface: Boolean(formSurface),
1389                fields: sectionFields(section),
1390                loopPostTypes: sectionPostTypes(section),
1391                previews: sectionFieldPreviews(section)
1392            };
1393        }
1394
1395        function sectionPayload(section, type) {
1396            return {
1397                type: type,
1398                sectionId: section.getAttribute('data-ocf-section'),
1399                sectionInstance: section.getAttribute('data-ocf-section-instance') || '',
1400                field: '',
1401                postType: '',
1402                postId: undefined,
1403                action: '',
1404                actionSlot: '',
1405                gradient: section.getAttribute('data-ocf-gradient') || '',
1406                fields: sectionFields(section),
1407                loopPostTypes: sectionPostTypes(section),
1408                previews: sectionFieldPreviews(section)
1409            };
1410        }
1411
1412        function postItemPayload(section, item, type) {
1413            return {
1414                type: type,
1415                sectionId: section.getAttribute('data-ocf-section'),
1416                sectionInstance: section.getAttribute('data-ocf-section-instance') || '',
1417                field: '',
1418                postType: item ? item.getAttribute('data-ocf-post-type') 
1418: '',
1419                postId: item ? Number(item.getAttribute('data-ocf-post-id')) : undefined,
1420                action: '',
1421                actionSlot: '',
1422                gradient: section.getAttribute('data-ocf-gradient') || '',
1423                fields: sectionFields(section),
1424                loopPostTypes: sectionPostTypes(section),
1425                previews: sectionFieldPreviews(section)
1426            };
1427        }
1428
1429        function restoredInitialSection() {
1430            if (!window.sessionStorage) return null;
1431            try {
1432                var raw = window.sessionStorage.getItem(restoreSelectionKey);
1433                if (!raw) return null;
1434                window.sessionStorage.removeItem(restoreSelectionKey);
1435                var restore = JSON.parse(raw);
1436                if (!restore || !restore.sectionId) return null;
1437                if (restore.sectionInstance) {
1438                    var byInstance = document.querySelector('[data-ocf-section-instance="' + CSS.escape(String(restore.sectionInstance)) + '"]');
1439                    if (byInstance) return byInstance;
1440                }
1441                return document.querySelector('[data-ocf-section="' + CSS.escape(String(restore.sectionId)) + '"]');
1442            } catch (error) {
1443                return null;
1444            }
1445        }
1446
1447        function findInitialSection() {
1448            var restored = restoredInitialSection();
1449            if (restored) return restored;
1450            return Array.prototype.slice.call(document.querySelectorAll('[data-ocf-section]')).filter(isPageSectionElement)[0] || null;
1451        }
1452
1453        function selectInitialSection() {
1454            if (selectedSection) return;
1455            var section = findInitialSection();
1456            if (!section) return;
1457            setSelectedSection(section);
1458            setSelectedField(null);
1459            window.parent.postMessage(sectionPayload(section, 'ocf:section-selected'), '*');
1460        }
1461
1462        function cssString(value) {
1463            return String(value || '').replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n|\r|\f/g, ' ');
1464        }
1465
1466        function fieldStyleCss(overrides) {
1467            if (!Array.isArray(overrides)) return '';
1468            return overrides.map(function (row) {
1469                if (!row || row.scope !== 'home' || !row.section || !row.field) return '';
1470                var declarations = [];
1471                var childDeclarations = [];
1472                if (typeof row.color_token === 'string' && /^[a-zA-Z0-9_]+$/.test(row.color_token)) {
1473                    declarations.push('--field-color:var(' + cssNameForToken(row.color_token) + ');');
1474                    declarations.push('color:var(--field-color);');
1475                } else if (typeof row.color === 'string' && row.color) {
1476                    declarations.push('--field-color:' + row.color + ';');
1477                    declarations.push('color:var(--field-color);');
1478                }
1479                if (typeof row.font_family === 'string' && row.font_family) {
1480                    declarations.push('--field-font-family:"' + cssString(row.font_family) + '";');
1481                    declarations.push('font-family:var(--field-font-family);');
1482                }
1483                if (typeof row.font_size === 'string' && row.font_size) {
1484                    declarations.push('--field-font-size:' + row.font_size + ';');
1485                    declarations.push('font-size:var(--field-font-size)!important;');
1486                }
1487                if (typeof row.font_weight === 'string' && row.font_weight) {
1488                    declarations.push('--field-font-weight:' + row.font_weight + ';');
1489                    declarations.push('font-weight:var(--field-font-weight)!important;');
1490                }
1491                if (typeof row.font_style === 'string' && row.font_style) {
1492                    declarations.push('--field-font-style:' + row.font_style + ';');
1493                    declarations.push('font-style:var(--field-font-style)!important;');
1494                    childDeclarations.push('font-style:var(--field-font-style)!important;');
1495                }
1496                if (typeof row.text_decoration === 'string' && row.text_decoration) {
1497                    declarations.push('--field-text-decoration:' + row.text_decoration + ';');
1498                    declarations.push('text-decoration:var(--field-text-decoration)!important;');
1499                    childDeclarations.push('text-decoration:var(--field-text-decoration)!important;');
1500                }
1501                if (!declarations.length) return '';
1502                var selector = '[data-ocf-section="' + CSS.escape(row.section) + '"] [data-ocf-field="' + CSS.escape(row.field) + '"]';
1503                return selector + '{' + declarations.join('') + '}' + (childDeclarations.length ? selector + ' *{' + childDeclarations.join('') + '}' : '');
1504            }
1504).filter(Boolean).join('\n');
1505        }
1506
1507        function updateFieldStyleOverrides(overrides) {
1508            var styleNode = document.getElementById('ocf-field-style-overrides');
1509            if (!styleNode) {
1510                styleNode = document.createElement('style');
1511                styleNode.id = 'ocf-field-style-overrides';
1512                document.head.appendChild(styleNode);
1513            }
1514            styleNode.textContent = fieldStyleCss(overrides);
1515        }
1516
1517        function hasTextSelectionInside(node) {
1518            if (!node || !window.getSelection) return false;
1519            var selection = window.getSelection();
1520            if (!selection || selection.isCollapsed || String(selection).trim() === '') return false;
1521            return node.contains(selection.anchorNode) || node.contains(selection.focusNode);
1522        }
1523
1524        function findNavigationTarget(target) {
1525            if (!target || !target.closest) return null;
1526            return target.closest('a[href], area[href], input[type="submit"], input[type="image"], button[type="submit"]');
1527        }
1528
1529        function blockEditorNavigation(event) {
1530            if (event.target && event.target.closest && event.target.closest('.ocf-editor-wrap--frontend')) return;
1531            if (!findNavigationTarget(event.target)) return;
1532            event.preventDefault();
1533        }
1534
1535        document.addEventListener('click', blockEditorNavigation, true);
1536        document.addEventListener('auxclick', blockEditorNavigation, true);
1537        document.addEventListener('submit', function (event) {
1538            event.preventDefault();
1539            event.stopPropagation();
1540        }, true);
1541
1542        document.addEventListener('mouseover', function (event) {
1543            var section = findSection(event.target);
1544            if (!section || section === selectedSection) return;
1545            section.classList.add('ocf-editor-hover');
1546        });
1547
1548        document.addEventListener('mouseout', function (event) {
1549            var section = findSection(event.target);
1550            if (!section || section === selectedSection) return;
1551            section.classList.remove('ocf-editor-hover');
1552        });
1553
1554        document.addEventListener('mousedown', function (event) {
1555            if (event.button !== 0) return;
1556            var section = findSection(event.target);
1557            if (!section) return;
1558            var field = findField(event.target, section);
1559            if (!field || !isInlineEditableField(field)) return;
1560            section.classList.remove('ocf-editor-hover');
1561            setSelectedSection(section);
1562            setSelectedField(field);
1563            window.parent.postMessage(fieldPayload(section, field, 'ocf:section-selected'), '*');
1564        }, true);
1565
1566        document.addEventListener('click', function (event) {
1567            var section = findSection(event.target);
1568            if (!section) return;
1569            var field = findField(event.target, section);
1570            var action = findAction(event.target, section);
1571            var postItem = findPostItem(event.target, section);
1572            var instance = findInstance(event.target, section);
1573            var formSurface = findFormSurface(event.target, section);
1574            var preserveTextSelection = field && isInlineEditableField(field) && hasTextSelectionInside(field);
1575
1576            if (postItem) {
1577                event.preventDefault();
1578                event.stopPropagation();
1579                section.classList.remove('ocf-editor-hover');
1580                setSelectedSection(section);
1581                setSelectedField(null);
1582                setSelectedPostItem(postItem);
1583                window.parent.postMessage(postItemPayload(section, postItem, 'ocf:section-selected'), '*');
1584                return;
1585            }
1586
1587            if (preserveTextSelection) {
1588                event.stopPropagation();
1589                return;
1590            }
1591            event.preventDefault();
1592            event.stopPropagation();
1593            section.classList.remove('ocf-editor-hover');
1594            setSelectedSection(section);
1595            setSelectedField(field);
1596            setSelectedPostItem(null);
1597
1598            window.parent.postMessage(field ? fieldPayload(section, field, 'ocf:section-selected') : {
1599                type: 'ocf:section-selected',
1600                sectionId: section.getAttribute('data-ocf-section'),
1601                sectionInstance: section.getAttribute('data-ocf-section-instance') || '',
1602                field: '',
1603                postType: postItem ? postItem.getAttribute('data-ocf-post-type') 
1603: '',
1604                postId: postItem ? Number(postItem.getAttribute('data-ocf-post-id')) : undefined,
1605                action: action ? action.getAttribute('data-ocf-action') : '',
1606                actionSlot: instance ? instance.getAttribute('data-ocf-instance') : '',
1607                gradient: section.getAttribute('data-ocf-gradient') || '',
1608                formSurface: Boolean(formSurface),
1609                fields: sectionFields(section),
1610                loopPostTypes: sectionPostTypes(section),
1611                previews: sectionFieldPreviews(section)
1612            }, '*');
1613
1614            if (field && isInlineEditableField(field)) {
1615                field.focus({ preventScroll: true });
1616            }
1617        }, true);
1618
1619        document.addEventListener('input', function (event) {
1620            var section = findSection(event.target);
1621            if (!section) return;
1622            var field = findField(event.target, section);
1623            if (!field || !isInlineEditableField(field)) return;
1624            window.parent.postMessage(fieldPayload(section, field, 'ocf:field-preview-updated'), '*');
1625        }, true);
1626
1627        document.addEventListener('paste', function (event) {
1628            var section = findSection(event.target);
1629            if (!section) return;
1630            var field = findField(event.target, section);
1631            if (!field || !isInlineEditableField(field)) return;
1632            event.preventDefault();
1633            var text = (event.clipboardData || window.clipboardData).getData('text/plain');
1634            document.execCommand('insertText', false, text);
1635        }, true);
1636
1637        var gradientOptions = {
1638            media_dark: { token: '--gradient-media-dark', tone: 'dark', overlay: 'max(0.88, var(--media-overlay-opacity))' },
1639            media_dark_reverse: { token: '--gradient-media-dark-reverse', tone: 'dark', overlay: 'max(0.88, var(--media-overlay-opacity))' },
1640            media_dark_even: { token: '--gradient-media-dark-even', tone: 'dark', overlay: 'max(0.88, var(--media-overlay-opacity))' },
1641            media_light: { token: '--gradient-media-light', tone: 'light', overlay: 'var(--media-light-overlay-opacity)' },
1642            media_light_reverse: { token: '--gradient-media-light-reverse', tone: 'light', overlay: 'var(--media-light-overlay-opacity)' },
1643            media_light_even: { token: '--gradient-media-light-even', tone: 'light', overlay: 'var(--media-light-overlay-opacity)' },
1644            media_light_transparent: { token: '--gradient-media-light-transparent', tone: 'light', overlay: '1' },
1645            hero_dark: { token: '--gradient-hero-dark', tone: 'dark', overlay: '1' },
1646            primary_fade: { token: '--gradient-primary-fade', tone: 'dark', overlay: '1' },
1647            primary: { token: '--gradient-primary', tone: 'dark', overlay: '0.9' },
1648            primary_reverse: { token: '--gradient-primary-reverse', tone: 'dark', overlay: '0.9' },
1649            primary_soft: { token: '--gradient-primary-soft', tone: 'light', overlay: '0.92' },
1650            primary_soft_reverse: { token: '--gradient-primary-soft-reverse', tone: 'light', overlay: '0.92' },
1651            primary_secondary: { token: '--gradient-primary-secondary', tone: 'dark', overlay: '0.9' },
1652            secondary_primary: { token: '--gradient-secondary-primary', tone: 'dark', overlay: '0.9' },
1653            dark_primary: { token: '--gradient-dark-primary', tone: 'dark', overlay: '0.92' },
1654            primary_dark: { token: '--gradient-primary-dark', tone: 'dark', overlay: '0.92' },
1655            surface: { token: '--gradient-surface', tone: 'light', overlay: '0.92' },
1656            surface_reverse: { token: '--gradient-surface-reverse', tone: 'light', overlay: '0.92' }
1657        };
1658
1659        function tokenCssVar(token) {
1660            token = String(token || '').replace(/[^A-Za-z0-9_]/g, '');
1661            if (!token) return '';
1662            if (token.indexOf('color_') === 0) return '--ocf-' + token.slice(6).replace(/_/g, '-');
1663            return '--ocf-' + token.replace(/_/g, '-');
1664        }
1665
1666        function applySectionSettings(section, settings) {
1667            if (!section || !settings || typeof settings !== 'object') return;
1668            if (!originalSectionState.has(section)) {
1669                originalSectionState.set(section, {
1670                    style: section.getAttribute('style'),
1671                    gradient: section.getAttribute('data-ocf-gradient'),
1672                    tone: section.getAttribute('data-ocf-tone')
1673                });
1674            }
1675            var background = settings.background || {};
1676            if (!background.mode && !background.color_token && !background.color && !background.gradient && !background.image_url) {
1677                var original = originalSectionState.get(section) || {};
1678                if (original.style == null) section.removeAttribute('style');
1679                else section.setAttribute('style', original.style);
1680                if (original.gradient == null) section.removeAttribute('data-ocf-gradient');
1681                else section.setAttribute('data-ocf-gradient', original.gradient);
1682                if (original.tone == null) section.removeAttribute('data-ocf-tone');
1683                else section.setAttribute('data-ocf-tone', original.tone);
1684                section.removeAttribute('data-ocf-bg-image-active');
1685                return;
1686            }
1687            var mode = String(background.mode || '');
1688            if (!mode) {
1689                if (background.color_token || background.color) mode = 'color';
1690                else if (background.gradient) mode = 'gradient';
1691            }
1692            if (!mode) mode = 'color';
1693            var imageUrl = /^https?:\/\//.test(String(background.image_url || '')) ? String(background.image_url || '') : '';
1694            var hasCustomImage = imageUrl !== '';
1695            var attachment = String(background.attachment || '') === 'fixed' ? 'fixed' : 'scroll';
1696            section.style.removeProperty('--section-bg');
1697            section.style.removeProperty('--section-gradient');
1698            section.style.removeProperty('--section-image');
1699            section.style.removeProperty('--section-overlay-mix');
1700            section.style.removeProperty('background');
1701            section.style.removeProperty('background-color');
1702            section.style.removeProperty('background-image');
1703            section.style.removeProperty('background-size');
1704            section.style.removeProperty('background-position');
1705            section.style.removeProperty('background-repeat');
1706            section.style.removeProperty('background-attachment');
1707            section.removeAttribute('data-ocf-gradient');
1708            section.removeAttribute('data-ocf-bg-image-active');
1709            if (hasCustomImage) {
1710                section.setAttribute('data-ocf-bg-image-active', '1');
1711                section.style.setProperty('--section-image', 'url("' + cssString(imageUrl) + '")');
1712                section.style.backgroundSize = 'cover';
1713                section.style.backgroundPosition = 'center';
1714                section.style.backgroundRepeat = 'no-repeat';
1715                section.style.backgroundAttachment = attachment;
1716            }
1717            if (mode === 'color') {
1718                var colorVar = tokenCssVar(background.color_token);
1719                var customColor = /^#[0-9a-fA-F]{6}$/.test(String(background.color || '')) ? String(background.color) : '';
1720                var overlayColor = colorVar ? 'var(' + colorVar + ')' : customColor;
1721                var imageOverlay = hasCustomImage || sectionHasImageOverlay(section);
1722                var overlayMix = String(Math.round(Number(validOverlayValue(background.overlay, 0.55)) * 100)) + '%';
1723                if (colorVar) {
1724                    section.style.setProperty('--section-bg', 'var(' + colorVar + ')');
1725                    section.style.setProperty('--section-gradient', imageOverlay ? overlayColor : 'none');
1726                    section.style.setProperty('--section-overlay-opacity', imageOverlay ? validOverlayValue(background.overlay, 0.55) : '0');
1727                    section.style.backgroundColor = 'var(--section-bg)';
1728                    if (hasCustomImage) {
1729                        section.style.setProperty('--section-overlay-mix', overlayMix);
1730                        section.style.backgroundImage = 'linear-gradient(color-mix(in srgb, var(--section-bg) var(--section-overlay-mix), transparent), color-mix(in srgb, var(--section-bg) var(--section-overlay-mix), transparent)), var(--section-image)';
1731                    } else {
1732                        section.style.background = 'var(--section-bg)';
1733                    }
1734                } else if (customColor) {
1735                    section.style.setProperty('--section-bg', customColor);
1736                    section.style.setProperty('--section-gradient', imageOverlay ? overlayColor : 'none');
1737                    section.style.setProperty('--section-overlay-opacity', imageOverlay ? validOverlayValue(background.overlay, 0.55) : '0');
1738                    section.style.backgroundColor = 'var(--section-bg)';
1739                    if (hasCustomImage) {
1740                        section.style.setProperty('--section-overlay-mix', overlayMix);
1741                        section.style.backgroundImage = 'linear-gradient(color-mix(in srgb, var(--section-bg) var(--section-overlay-mix), transparent), color-mix(in srgb, var(--section-bg) var(--section-overlay-mix), transparent)), var(--section-image)';
1742                    } else {
1743                        section.style.background = 'var(--section-bg)';
1744                    }
1745                } else if (hasCustomImage) {
1746                    section.style.backgroundImage = 'var(--section-image)';
1747                }
1748            } else if (mode === 'gradient') {
1749                if (background.gradient) {
1750                    applySectionGradient(section, background.gradient);
1751                    if (hasCustomImage) {
1752                        section.style.backgroundImage = 'var(--section-gradient), var(--section-image)';
1753                    } else {
1754                        section.style.background = 'var(--section-gradient)';
1755                    }
1756                } else {
1757                    section.style.setProperty('--section-gradient', 'none');
1758                    section.style.setProperty('--section-overlay-opacity', '0');
1759                    if (hasCustomImage) {
1760                        section.style.backgroundImage = 'var(--section-image)';
1761                    } else {
1762                        section.style.background = 'none';
1763                    }
1764                }
1765            } else if (hasCustomImage) {
1766                section.style.backgroundImage = 'var(--section-image)';
1767            }
1768            if (/^(?:0(?:\.\d+)?|1(?:\.0+)?)$/.test(String(background.overlay || ''))) {
1769                section.style.setProperty('--section-overlay-opacity', String(background.overlay));
1770            } else if (mode !== 'gradient' && !(mode === 'color' && sectionHasImageOverlay(section))) {
1771                section.style.removeProperty('--section-overlay-opacity');
1772            }
1773            section.style.removeProperty('--section-pad-top');
1774            section.style.removeProperty('--section-pad-bottom');
1775            section.style.removeProperty('--section-container');
1776            section.style.removeProperty('padding-top');
1777            section.style.removeProperty('padding-bottom');
1778        }
1779
1780        function applySectionGradient(section, value) {
1781            if (!section) return;
1782            var key = String(value || '');
1783            var gradient = gradientOptions[key] || gradientOptions.primary_fade;
1784            section.setAttribute('data-ocf-gradient', gradientOptions[key] ? key : 'primary_fade');
1785            section.setAttribute('data-ocf-tone', gradient.tone);
1786            section.style.setProperty('--section-gradient', 'var(' + gradient.token + ')');
1787            section.style.setProperty('--section-overlay-opacity', gradient.overlay);
1788        }
1789
1790        function sectionFromMessage(message) {
1791            if (message.instance || message.sectionInstance) {
1792                var instance = String(message.instance || message.sectionInstance || '');
1793                var byInstance = document.querySelector('[data-ocf-section-instance="' + CSS.escape(instance) + '"]');
1794                if (byInstance) return byInstance;
1795            }
1796            return document.querySelector('[data-ocf-section="' + CSS.escape(message.sectionId || '') + '"]');
1797        }
1798
1799        function sectionSelectorFromMessage(message) {
1800            if (message.instance || message.sectionInstance) {
1801                return '[data-ocf-section-instance="' + CSS.escape(String(message.instance || message.sectionInstance || '')) + '"]';
1802            }
1803            return '[data-ocf-section="' + CSS.escape(message.sectionId || '') + '"]';
1804        }
1805
1806        window.addEventListener('message', function (event) {
1807            var message = event.data || {};
1808            if (message.type === 'ocf:select-section') {
1809                var section = sectionFromMessage(message);
1810                if (!section) return;
1811                setSelectedSection(section);
1812                setSelectedField(null);
1813                section.scrollIntoView({ behavior: 'smooth', block: 'center' });
1814            }
1815
1816            if (message.type === 'ocf:section-order-updated') {
1817                applySectionOrder(message.order || []);
1818            }
1819
1820            if (message.type === 'ocf:preview-section-settings-updated') {
1821                applySectionSettings(sectionFromMessage(message), message.settings || {});
1822            }
1823
1824            if (message.type === 'ocf:preview-field-updated') {
1825                if (/_gradient$/.test(String(message.field || ''))) {
1826                    applySectionGradient(sectionFromMessage(message), message.value || '');
1827                }
1828                var selector = sectionSelectorFromMessage(message) + ' [data-ocf-field="' + CSS.escape(message.field) + '"]';
1829                document.querySelectorAll(selector).forEach(function (node) {
1830                    setFieldRenderedValue(node, message.field, message.value || '');
1831                });
1832            }
1833
1834            if (message.type === 'ocf:preview-global-field-updated') {
1835                document.querySelectorAll('[data-ocf-field="' + CSS.escape(message.field) + '"]').forEach(function (node) {
1836                    setFieldRenderedValue(node, message.field, message.value || '');
1837                });
1838            }
1839
1840            if (message.type === 'ocf:preview-repeater-field-updated') {
1841                var repeaterSelector = sectionSelectorFromMessage(message) + ' [data-ocf-repeater="' + CSS.escape(message.repeater) + '"][data-ocf-index="' + String(message.index) + '"] [data-ocf-field="' + CSS.escape(message.field) + '"]';
1842                document.querySelectorAll(repeaterSelector).forEach(function (node) {
1843                    setFieldRenderedValue(node, message.field, message.value || '');
1844                });
1845            }
1846
1847            if (message.type === 'ocf:preview-repeater-updated' && Array.isArray(message.items)) {
1848                message.items.forEach(function (item, index) {
1849                    Object.keys(item || {}).forEach(function (field) {
1850                        var itemSelector = '[data-ocf-section="' + CSS.escape(message.sectionId) + '"] [data-ocf-repeater="' + CSS.escape(message.repeater) + '"][data-ocf-index="' + String(index) + '"] [data-ocf-field="' + CSS.escape(field) + '"]';
1851                        document.querySelectorAll(itemSelector).forEach(function (node) {
1852                            setFieldRenderedValue(node, field, item[field] || '');
1853                        });
1854                    });
1855                });
1856            }
1857
1858            if (message.type === 'ocf:preview-post-field-updated') {
1859                var postSelector = '[data-ocf-post-type="' + CSS.escape(message.postType || '') + '"][data-ocf-post-id="' + String(message.postId || '') + '"] [data-ocf-field="' + CSS.escape(message.field || '') + '"]';
1860                document.querySelectorAll(postSelector).forEach(function (node) {
1861                    var type = fieldType(message.field || '');
1862                    if (message.url && (type === 'image' || type === 'image_id') && node.tagName && node.tagName.toLowerCase() === 'img') {
1863                        node.setAttribute('src', message.url);
1864                        return;
1865                    }
1866                    setFieldRenderedValue(node, message.field, message.value || '');
1867                });
1868            }
1869
1870            if (message.type === 'ocf:preview-post-loop-updated') {
1871                applyPostLoopPreview(message.postType || '', message.posts || []);
1872            }
1873
1874            if (message.type === 'ocf:preview-nav-menu-updated') {
1875                updateHeaderNavPreview(message.items || []);
1876            }
1877
1878            if (message.type === 'ocf:preview-image-updated') {
1879                var imageSelector = '[data-ocf-section="' + CSS.escape(message.sectionId) + '"] [data-ocf-field="' + CSS.escape(message.field) + '"]';
1880                document.querySelectorAll(imageSelector).forEach(function (node) {
1881                    if (node.tagName && node.tagName.toLowerCase() === 'img') {
1882                        node.setAttribute('src', message.url || '');
1883                    } else {
1884                        node.style.backgroundImage = message.url ? 'url("' + message.url + '")' : '';
1885                    }
1886                });
1887                if (message.field === 'page_featured_image') {
1888                    var imageSection = document.querySelector('[data-ocf-section="' + CSS.escape(message.sectionId) + '"]');
1889                    if (imageSection) {
1890                        imageSection.style.setProperty('--page-header-image', message.url ? 'url("' + message.url + '")' : '');
1891                    }
1892                }
1893            }
1894
1895            if (message.type === 'ocf:preview-global-image-updated') {
1896                document.querySelectorAll('[data-ocf-field="' + CSS.escape(message.field) + '"]').forEach(function (node) {
1897                    if (node.tagName && node.tagName.toLowerCase() === 'img') {
1898                        node.setAttribute('src', message.url || '');
1899                    } else {
1900                        node.style.backgroundImage = message.url ? 'url("' + message.url + '")' : '';
1901                    }
1902                });
1903            }
1904
1905            if (message.type === 'ocf:preview-theme-updated' && message.tokens && typeof message.tokens === 'object') {
1906                updateThemeTokens(message.tokens);
1907            }
1908
1909            if (message.type === 'ocf:preview-theme-mode-updated') {
1910                updateThemeMode(message.mode);
1911            }
1912
1913            if (message.type === 'ocf:preview-field-style-overrides-updated') {
1914                updateFieldStyleOverrides(message.overrides);
1915            }
1916
1917            if (message.type === 'ocf:preview-editor-state-restored') {
1918                if (message.design && typeof message.design === 'object') {
1919                    updateThemeTokens(message.design);
1920                }
1921                if (message.site && typeof message.site === 'object') {
1922                    Object.keys(message.site).forEach(function (fieldKey) {
1923                        var value = message.site[fieldKey];
1924                        if (typeof value === 'object') return;
1925                        document.querySelectorAll('[data-ocf-field="' + CSS.escape(fieldKey) + '"]').forEach(function (node) {
1926                            setFieldRenderedValue(node, fieldKey, value == null ? '' : String(value));
1927                        });
1928                    });
1929                    if (typeof message.site.theme_mode === 'string') {
1930                        updateThemeMode(message.site.theme_mode);
1931                    }
1932                }
1933                applyPostListPreview(message.posts || []);
1934                updateFieldStyleOverrides(message.overrides);
1935            }
1936        });
1937
1938        window.setTimeout(function () {
1939            var editor = window.ocfEditor || {};
1940            var pkg = editor.package || {};
1941            applyPostListPreview(pkg.posts || []);
1942            selectInitialSection();
1943        }, 0);
1944
1945        function updateThemeTokens(tokens) {
1946            Object.keys(tokens || {}
1946).forEach(function (key) {
1947                var value = tokens[key];
1948                if (typeof value !== 'string') return;
1949                document.documentElement.style.setProperty(cssNameForToken(key), value);
1950            });
1951        }
1952
1953        function updateThemeMode(mode) {
1954            mode = String(mode || 'light').toLowerCase();
1955            if (['light', 'dark'].indexOf(mode) === -1) mode = 'light';
1956            document.documentElement.setAttribute('data-theme-mode', mode);
1957            document.documentElement.setAttribute('data-theme', mode === 'dark' ? 'dark' : 'light');
1958        }
1959
1960        function cssNameForToken(key) {
1961            return '--ocf-' + String(key || '')
1962                .replace(/^color_/, '')
1963                .replace(/^font_/, 'font_')
1964                .replace(/_/g, '-');
1965        }
1966    }
1967
1968    onReady(function () {
1969        applyStoredSectionOrder();
1970        initPhoneCombos();
1971        initLeadForms();
1972        initBookingWizard();
1973        initEditorBridge();
1974    });
1975})();

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.