PageSourceSearch

https://swvasun.com/ContactUs

html swvasun.com collected 2026-09-29 05:14:46 UTC 85,177 bytes, 1,575 lines download raw bytes

1<!doctype html>
2<html lang="en">
3
4<head>
5    <!-- Performance Optimizations -->
6    <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
7    <link rel="preconnect" href="https://code.jquery.com" crossorigin>
8    <link rel="dns-prefetch" href="https://cdn.jsdelivr.net">
9    <link rel="dns-prefetch" href="https://code.jquery.com">
10    <link rel="dns-prefetch" href="https://maxcdn.bootstrapcdn.com">
11    <link rel="preload" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" as="style">
12    <!-- Required meta tags -->
13    <meta charset="utf-8">
14    <title>Contact Us</title>
15    <meta name="viewport" content="width=device-width, initial-scale=1">
16    
17     
18    <meta name="keywords" content="SWVA sun, Local News, Swva Sports, Breaking News, Obituaries, Public Notices"> 
19    <meta name="description" content="SWVA Local Online Newspaper"> 
20    
21    <link rel="shortcut icon" href="/favicon.ico?v=1790187371">
22    
22<script type="text/javascript" language="javascript" src="/lib/bl2.js"></script>
22
23
24    <!-- bootstrap -->
25    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
26    
26<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
26
27    <link type="text/css" rel="stylesheet" href="https://bulletlink.com/bootstrap.css">
28    <!-- /bootstrap -->
29
30    <!-- jQuery -->
31    
31<script src="https://code.jquery.com/jquery-1.7.min.js" integrity="sha256-/05Jde9AMAT4/o5ZAI23rUf1SxDYTHLrkOco0eyRV84=" crossorigin="anonymous"></script>
31
32
33    <!-- system -->
34    
34<script type="text/javascript" language="javascript" src="/settings.js?v=1790187371"></script>
34
35    
35<script>function SParameters(){return '1';}</script>
35
36    
36<script type="text/javascript" language="javascript" src="/articles.js?v=1790187371"></script>
36
37    
37<script type="text/javascript" language="javascript" src="/ads.js?v=1790187371"></script>
37
38    
38<script type="text/javascript" language="javascript" src="/menus.js?v=1790187371"></script>
38
39    
39<script type="text/javascript" language="javascript" src="/sections.js?v=1790187371"></script>
39
40    
40<script type="text/javascript" language="javascript" src="/polls.js?v=1790187371"></script>
40
41    
41<script type="text/javascript" language="javascript" src="/menu_horizontal.js?v=1790187371"></script>
41
42    <!-- /system -->
43
44    <!-- fontAwesome-->
45    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
46    <!-- /fontAwesome-->
47
48    <!-- general -->
49    
49<script type="text/javascript" language="javascript" src="/jscript1.js?v=1790187371"></script>
49
50    <link type="text/css" rel="stylesheet" href="/custom.css?v=1790187371">
51    
51<script type="text/javascript" language="javascript" src="/custom.js?v=1790187371"></script>
51
52    <!-- /general -->
53
54    <!--StartMH-->
55    <!--MetaSharing-->
56    <!--EndMH-->
57
58    <!-- live-Builder --> 
59    
59<script type="text/javascript" src="/ufields.js"></script>
59
60    <!-- /live-Builder -->
61    <meta property="og:image" content="https://swvasun.com/logo.jpg"/>
62<!-- Google tag (gtag.js) -->
vendor: 65 bytes, lines 62-63
62
63<script async src="https://www.googletagmanager.com/gtag/js?id=
63G-WJW2XMLVK9
vendor: 19 bytes, line 63
63" defer></script>
64<script>
vendor: 130 bytes, lines 65-68
65window.dataLayer = window.dataLayer || [];
66function gtag(){dataLayer.push(arguments);}
67gtag('js', new Date());
68gtag('config', '
68G-WJW2XMLVK9
vendor: 5 bytes, line 68
68');
69</script>
vendor: 2 bytes, line 69
69
70<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-1521277811821952"
71crossorigin="anonymous" defer></script>
71
72<meta name="google-site-verification" content="ZWleqlOYPt0wm05jtSHIjposIojO4ID1mzFoLwoHm8M" />
73<!-- This site is converting visitors into subscribers and customers with OptinMonster - https://optinmonster.com -->
74<script>(function(d,u,ac){var s=d.createElement('script');s.type='text/javascript';s.src='https://a.omappapi.com/app/js/api.min.js';s.async=true;s.dataset.user=u;s.dataset.account=ac;d.getElementsByTagName('head')[0].appendChild(s);})(document,171153,185538);</script>
74
75<!-- / https://optinmonster.com -->
76    
77<style>:root{--cms-theme-color: #555;}</style><link rel="stylesheet" href="/sys-popup.css?v=1790187371"><style id="universal-submit-spinner">
78.form-submit-spinner{display:inline-block;width:16px;height:16px;border:2px solid rgba(255,255,255,.4);border-top-color:#fff;border-radius:50%;animation:frmSpin .7s linear infinite;vertical-align:middle;margin-right:6px}
79@keyframes frmSpin{to{transform:rotate(360deg)}}
80</style></head>
81
82<body id="blbodymain" class="blbody" data-submenu="{{SubMenuId}}"><a href="https://swvasun.com/cdn-cgi/content?id=tq3ehKJP5JSzaoki7KRORT4VIDd1gx2tRQw_Cxal_2k-1790658882.4701972-1.2.1.1-ublGHNrAq4l7JMRrmr2TjZSer0HMBBqi3qFoLVTdSO_9mDEZXxEPGKCCQ1qqWzQX" aria-hidden="true" rel="nofollow noopener" style="display: none !important; visibility: hidden !important"></a>
83    <div class="mainbody">
84
85<!-- Popup - start -->
86<div class="Ad22"><div data-cms-block="adgroup-22" data-cms-block-label="Advertisement 22" data-cms-el="adgroup" data-cms-adgroup="22">
86<script type="text/javascript" language="JavaScript">AdGroup22();</script>
86</div></div>
87<script>
88if (readCookie("subs-popup-" + ReadSubmenu())) {
89 // document.querySelector("iframe[src*='&pform={{NewsViaEmail2}}']").closest("#f_Popup4").classList.add("d-none");
90 // document.body.style.overflow = "visible";
91} else {
92  createCookie("subs-popup-" + ReadSubmenu(), "Yes");
93}
94</script>
94
95<!-- Popup - end -->
96
97        <!-- stycky ads-->
98        <span class="cus-sticky-ads"><div data-cms-block="adgroup-1" data-cms-block-label="Advertisement 1" data-cms-el="adgroup" data-cms-adgroup="1">
98<script type="text/javascript" language="JavaScript">AdGroup1();</script>
98</div><div data-cms-block="adgroup-2" data-cms-block-label="Advertisement 2" data-cms-el="adgroup" data-cms-adgroup="2">
98<script type="text/javascript" language="JavaScript">AdGroup2();</script>
98</div><div data-cms-block="adgroup-3" data-cms-block-label="Advertisement 3" data-cms-el="adgroup" data-cms-adgroup="3">
98<script type="text/javascript" language="JavaScript">AdGroup3();</script>
98</div></span>
99        <!-- /stycky ads-->
100        <div class="container-xxl mt-2">
101            <!-- top-links -->
102            <div class="row mb-3 border-bottom pb-2 border-2">
103                <div class="bl-top-links col-sm-4 float-start">
104                    <div class="float-start">
105                        <!-- search-->
106                        
106<script type="text/javascript" language="JavaScript">GetGoogleCustomSearch();</script>
106
107                        <a class="search-link text-decoration-none text-black fs-6 fw-bold me-3 border rounded p-1 px-2 bl-toplink-1-133-searchlink" href="javascript:void(0)" data-bs-toggle="tooltip" data-bs-placement="right" title="Click to Search Articles..."><i class="fa fa-search" aria-hidden="true"></i> SEARCH</a>
108                        <div class="search_content">
109                            <div class="search_box"><gcse:searchbox></gcse:searchbox></div>
110                            <div class="search_results"><gcse:searchresults></gcse:searchresults></div>
111                            <div class="search_remove">
112                                <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-x-lg" viewBox="0 0 16 16">
113                                    <path fill-rule="evenodd" d="M13.854 2.146a.5.5 0 0 1 0 .708l-11 11a.5.5 0 0 1-.708-.708l11-11a.5.5 0 0 1 .708 0Z"/>
114                                    <path fill-rule="evenodd" d="M2.146 2.146a.5.5 0 0 0 0 .708l11 11a.5.5 0 0 0 .708-.708l-11-11a.5.5 0 0 0-.708 0Z"/>
115                              </svg>
116                            </div>
117                        </div>
118                        
118<script>
119                            document.getElementsByClassName('search-link')[0].addEventListener("click", function() {
120                                document.getElementById("gsc-i-id1").setAttribute("placeholder", "Search for...");
121                                document.getElementsByClassName('search_content')[0].classList.add("search-flex");
122                                document.getElementsByClassName('search_remove')[0].style.display = "block";
123                                document.getElementsByClassName('gsc-results-close-btn')[0].addEventListener("click", function() {
124                                    document.getElementsByClassName('search_content')[0].classList.remove("search-flex");
125                                });
126                            });
127                            document.getElementsByClassName('search_remove')[0].addEventListener("click", function() {
128                                document.getElementsByClassName('search_content')[0].classList.remove("search-flex");
129                                document.getElementsByClassName('search_remove')[0].style.display = "none";                            });
130                        </script>
130
131                        <style>
132                            .gsc-adBlock {display: none !important;}
133                            #gsc-i-id1{color:#000!important}
134                            #gsc-i-id1::placeholder{color:#555!important;opacity:1;font-size:inherit!important}
135                            .search_content{display:flex;display:-webkit-flex;-ms-display:flex;align-items:center;-webkit-align-items:center;-ms-align-items:center;justify-content:center;-webkit-justify-content:center;-ms-justify-content:center;background-color:rgba(255,255,255,.98);height:100%;position:fixed;top:100%;opacity:0;left:0;width:100%;z-index:2147483647;transition: 400ms linear;}
136                            .search-flex{top: 0;opacity: 1;display:flex;display:-webkit-flex;-ms-display:flex;align-items:center;-webkit-align-items:center;-ms-align-items:center;justify-content:center;-webkit-justify-content:center;-ms-justify-content:center;transition: 300ms linear;}
137                            .search_remove{cursor:pointer;user-select:none;position:absolute;top:10px;left:3%;}
138                            .search_remove:hover{color:gray}
139                            .gsc-control-cse.gsc-control-cse-en{background-color:transparent;border-width:0;position:absolute}.search_box{border-bottom:2px solid #000;width:70%;padding-bottom:6px}
140                            .gsib_b,.gsst_a{display:none!important}
141                            .gscb_a{display:none!important}
142                            .gsc-input{background-image:none!important}
143                            .gsc-input{background-color:transparent!important}
144                            .gsc-input-box{background-color:transparent!important;border-bottom:0 solid #ff4500!important;border-width:0!important}
145                            .gsc-input{border-width:0!important;color:#fff!important;font-size:20px!important}
146                            .gsc-adBlock{display:none!important}
147                            .search-icon-desk{cursor:pointer}
148                            .search-icon-desk{float:right;height:54px;margin-right:10px}
149                            .search-icon-desk i{border:1px solid #ddd;border-radius:4px;color:#000;display:block;line-height:35px;margin-top:9px;padding:0 12px}
150                            .search-icon-desk i:hover{color:#79655d}
151                            .gsc-results-wrapper-overlay.gsc-results-wrapper-visible {width: 100%;left: 0;top: 0;height: 100%;}
152                            .gcsc-branding {display: none;}
153                            .gsc-search-button.gsc-search-button-v2{padding:0 18px;line-height:35px;display:inline-block!important;background:#ddd;border-radius:5px;border-color:#555}
154                            .gsc-search-button{width:auto!important;float:right!important}.gsc-search-button.gsc-search-button-v2:hover{background:#ddd!important}
155                            .gsc-search-button-v2 svg{fill:#000!important}
156                            .gsc-search-button.gsc-search-button-v2:hover{background:#05a2d3!important;border-color:#006899!important}
157                            .gsc-search-button.gsc-search-button-v2:hover svg{fill:#fff!important}
158                        </style>
159                        <!-- /search-->
160                        <a class="text-decoration-none text-black fs-6 me-3 bl-toplink-1-133-link" href="/about-us-p374-25.htm">ABOUT</a>
161                        <a class="text-decoration-none text-black fs-6 me-3 bl-toplink-1-133-link" href="/ContactUs">CONTACT</a>
162                        <a class="text-decoration-none text-black fs-6 me-3 bl-toplink-1-133-link" href="/SWVAsun">SIGN ME UP</a>
163                         
164
165                    </div>
166                </div>
167                <div class="cus-social-links col-sm-4 float-sm-end d-flex justify-content-center d-sm-inline-block">
168                    <div class="float-sm-start">
169                        <a class="text-decoration-none text-dark rounded ms-3 fs-4 d-inline-block lh-1 cus-social-1-133-icon" href="https://www.facebook.com/swvasun/" target="_blank"><i class="fa fa-facebook-square" aria-hidden="true"></i></a>
170                        <a class="text-decoration-none text-dark rounded ms-3 fs-4 d-inline-block lh-1 cus-social-1-133-icon" href="https://twitter.com/swvasun" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-twitter-x" viewBox="0 0 16 16" style="padding: 3px;background: #212529;fill: #fff;border-radius: 4px;display: inline-block;transform: translateY(-2px);" width="20" height="22"><path d="M12.6.75h2.454l-5.36 6.142L16 15.25h-4.937l-3.867-5.07-4.425 5.07H.316l5.733-6.57L0 .75h5.063l3.495 4.633L12.601.75Zm-.86 13.028h1.36L4.323 2.145H2.865z"></path></svg></a>
171                        <a class="text-decoration-none text-dark rounded ms-3 fs-4 d-inline-block lh-1 cus-social-1-133-icon" href="https://www.linkedin.com/company/swva-media-llc/" target="_blank"><i class="fa fa-linkedin-square" aria-hidden="true"></i></a>
172                        <a class="text-decoration-none text-dark rounded ms-3 fs-4 d-inline-block lh-1 cus-social-1-133-icon" href="https://youtube.com/@southwestvirginiasun8601" target="_blank"><i class="fa fa-youtube-play" aria-hidden="true"></i></a>
173                    </div>
174                </div>
175            </div>
176            <!-- /top-links -->
177            <!-- logo-->
178            <!-- Logo + AdGroup16 (centered) -->
179<div class="row mb-3 align-items-center">
180  <!-- Left section: Logo + Ad16 -->
181  <div class="col-md-12 text-center">
182    <div class="mb-2">
183      <div data-cms-el="logo">
183<script type="text/javascript" language="JavaScript">GetLogo();</script>
183</div>
184    </div>
185    <div>
186      <div data-cms-block="adgroup-16" data-cms-block-label="Advertisement 16" data-cms-el="adgroup" data-cms-adgroup="16">
186<script type="text/javascript" language="JavaScript">AdGroup16();</script>
186</div>
187    </div>
188  </div>
189
190  <!-- Right section: Ad29 -->
191
192</div>
193
194
195            <div class="row mb-3">
196                <div class="col-12 px-0 d-flex justify-content-center  border-top border-bottom border-2">
197                    
197<script type="text/javascript" language="JavaScript">
198                        GetMenuH();
199                    </script>
199
200                </div>
201            </div>
202            <!-- /menu -->
203<div class="cus-column-133-row row mb-3 border-bottom">
204                <div class="cus-column-133-1 col-lg-9 pe-lg-3">
205<div data-cms-popup-root="1"><style>
206#CustomForm {background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='32' viewBox='0 0 16 32'%3E%3Cg fill='%23c9d1cb' fill-opacity='0.16'%3E%3Cpath fill-rule='evenodd' d='M0 24h4v2H0v-2zm0 4h6v2H0v-2zm0-8h2v2H0v-2zM0 0h4v2H0V0zm0 4h2v2H0V4zm16 20h-6v2h6v-2zm0 4H8v2h8v-2zm0-8h-4v2h4v-2zm0-20h-6v2h6V0zm0 4h-4v2h4V4zm-2 12h2v2h-2v-2zm0-8h2v2h-2V8zM2 8h10v2H2V8zm0 8h10v2H2v-2zm-2-4h14v2H0v-2zm4-8h6v2H4V4zm0 16h6v2H4v-2zM6 0h2v2H6V0zm0 24h2v2H6v-2z'/%3E%3C/g%3E%3C/svg%3E");background-attachment: fixed;}
207.form-check-label:hover {color: #0d6efd;cursor: pointer;}
208.cus-form-loader .cus-form-loading {display: block !important;}
209.cus-form-loader .formbody fieldset, .cus-form-loader .system_text.footer_text {opacity: 0.5;pointer-events: none;}
210#cus-form-button-loader {display:none;}
211.cus-form-loader footer, .cus-form-loader button {cursor: no-drop;}
212.fast-spin {-webkit-animation: fa-spin 1s infinite linear;animation: fa-spin 1s infinite linear;}
213.cus-form-loader #SUBMIT1 {display:none;}
214.cus-form-loader #cus-form-button-loader {display:inline-block;}
215.form-floating > .form-control:focus ~ label {color: #0d6efd !important;}
216.form-control:not(:disabled):not([readonly])::file-selector-button {position: absolute;right: 0;border-radius: 5px !important;}
217.input-file-preview {display: none;}
218.input-file-preview img {max-width: 30%;max-height: 200px;border: 1px solid #ced4da;border-radius: 5px;padding: 5px;box-sizing: border-box;cursor: zoom-in;}
219.w-96 {width: 96% !important;}
220.p-valid {color: green;}
221.p-valid:before {position: relative;left: -10px;content: "✔";}
222.p-invalid {color: red;}
223.p-invalid:before {position: relative;left: -10px;content: "✖";font-size: 0.875rem !important;}
224.open-eye, .close-eye {position: absolute;right: 2%;top: 50%;transform: translate(0,-50%);cursor: pointer;}
225.open-eye i:hover, .close-eye i:hover {color: gray !important;}
226.cus-form-help:before {content: "\f05a";font-family: FontAwesome;padding-left: 0.5rem;padding-right: 0.5rem;}
227.cus-form-help {display: none;}
228.cus-form-help:empty {display: none !important;}
229.form-control:focus ~ .cus-form-help {display: block;}
230.formdivider.formdivider_custom {border-left: 5px solid #ffc107;}
231.form-floating > .form-control {caret-color: #0d6efd;}
232</style>
233<style id="force-form-styles" data-version="2026-01-26-v2">
234      textarea, textarea.form-control, textarea.formfieldinput { min-height: 120px !important; height: 120px !important; }
235
236      /* Enhanced Spinner Styles - More visible and animated */
237      .form-spinner-ring { 
238        display: inline-block; 
239        width: 18px; 
240        height: 18px; 
241        border: 3px solid rgba(255,255,255,0.4); 
242        border-top-color: #fff; 
243        border-radius: 50%; 
244        animation: formSpin 0.7s linear infinite; 
245        margin-right: 8px; 
246        vertical-align: middle; 
247      }
248      @keyframes formSpin { 
249        0% { transform: rotate(0deg); }
250        100% { transform: rotate(360deg); } 
251      }
252      
253      /* Submit Button Loading State */
254      button.btn-loading, input.btn-loading {
255        position: relative;
256        pointer-events: none;
257        opacity: 0.85;
258      }
259      button.btn-loading::after {
260        content: '';
261        position: absolute;
262        width: 18px;
263        height: 18px;
264        top: 50%;
265        left: 12px;
266        margin-top: -9px;
267        border: 3px solid rgba(255,255,255,0.4);
268        border-top-color: #fff;
269        border-radius: 50%;
270        animation: formSpin 0.7s linear infinite;
271      }
272      
273      /* Form Overlay during submission */
274      .form-loading-overlay {
275        position: absolute;
276        top: 0;
277        left: 0;
278        right: 0;
279        bottom: 0;
280        background: rgba(255,255,255,0.7);
281        display: flex;
282        align-items: center;
283        justify-content: center;
284        z-index: 9999;
285        border-radius: inherit;
286      }
287      .form-loading-overlay .spinner-container {
288        display: flex;
289        flex-direction: column;
290        align-items: center;
291        gap: 12px;
292        padding: 30px;
293        background: white;
294        border-radius: 12px;
295        box-shadow: 0 4px 20px rgba(0,0,0,0.15);
296      }
297      .form-loading-overlay .spinner-large {
298        width: 40px;
299        height: 40px;
300        border: 4px solid #e0e0e0;
301        border-top-color: #007bff;
302        border-radius: 50%;
303        animation: formSpin 0.8s linear infinite;
304      }
305      .form-loading-overlay .loading-text {
306        color: #333;
307        font-size: 14px;
308        font-weight: 500;
309      }
310      
311      /* Bootstrap 5 Floating Label Fix - Force proper behavior */
312      .form-floating > .form-control:focus ~ label,
313      .form-floating > .form-control:not(:placeholder-shown) ~ label,
314      .form-floating > .form-select ~ label {
315        opacity: 0.65 !important;
316        transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem) !important;
317      }
318      .form-floating > .form-control::placeholder {
319        color: transparent !important;
320      }
321      .form-floating > label {
322        position: absolute !important;
323        top: 0 !important;
324        left: 0 !important;
325        height: 100% !important;
326        padding: 1rem 0.75rem !important;
327        pointer-events: none !important;
328        border: 1px solid transparent !important;
329        transform-origin: 0 0 !important;
330        transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out !important;
331      }
332    </style>
333<style>.grecaptcha-badge{visibility:hidden!important;position:fixed!important;bottom:14px!important;right:14px!important;}</style>
334<script src="/iframe-resizer-master/js/iframeResizer.contentWindow.js"></script>
vendor: 1 bytes, line 334
334
335<script type="text/javascript" language="javascript" src="https://swvasun.com/lib/bl.js"></script>
vendor: 1 bytes, line 335
335
336<script>
337function ChangeInputFocus(obj, evt, cname) {
338/*if (evt.type == "focus")
339obj.className = cname;
340else if (evt.type == "blur")
341/*obj.className = cname;*/
342}
343</script>
vendor: 1 bytes, line 343
343
344<script src="https://recaptcha.net/recaptcha/api.js?render=6LdXmfgUAAAAAJNuEHBxVCY0w9eA73hvaWsdgPlM"></script>
vendor: 1 bytes, line 344
344
345<script>
346(function(){
347  function setToken(t){
348    var els = document.querySelectorAll('.g-recaptcha-token');
349    for (var i = 0; i < els.length; i++) els[i].value = t;
350  }
351  function refresh(){
352    if (typeof grecaptcha === 'undefined') return;
353    grecaptcha.ready(function(){
354      grecaptcha.execute('6LdXmfgUAAAAAJNuEHBxVCY0w9eA73hvaWsdgPlM', {action: 'submit'}).then(setToken);
355    });
356  }
357  // Submit-time minter. Login pages call this in their submit handler and await it,
358  // so the request always carries a freshly-minted token instead of reading the
359  // pre-filled hidden field — which may still be empty when a user with saved
360  // credentials presses Enter before the page-load mint resolves (-> recaptcha_missing,
361  // a hard block). Resolves to a fresh token, or falls back to whatever's already in
362  // the field (and never hangs the form: 3s timeout, and resolves if grecaptcha is
363  // unavailable). Also updates the hidden field so FormData-based submits pick it up.
364  window.cmsMintCaptchaToken = function(){
365    return new Promise(function(resolve){
366      var els = document.querySelectorAll('.g-recaptcha-token');
367      var fallback = (els[0] && els[0].value) || '';
368      var done = false;
369      var to = setTimeout(function(){ if(!done){ done = true; resolve(fallback); } }, 3000);
370      function fin(v){ if(done) return; done = true; clearTimeout(to); setToken(v); resolve(v); }
371      function attempt(){
372        if (typeof grecaptcha === 'undefined' || !grecaptcha.execute) return false;
373        try {
374          grecaptcha.ready(function(){
375            grecaptcha.execute('6LdXmfgUAAAAAJNuEHBxVCY0w9eA73hvaWsdgPlM', {action: 'submit'})
376              .then(function(t){ fin(t || fallback); })
377              .catch(function(){ fin(fallback); });
378          });
379          return true;
380        } catch(_) { return false; }
381      }
382      // grecaptcha may still be loading -- common when the form is rendered
383      // inside a popup, where api.js was injected only moments ago. Poll until
384      // it's ready (or the 3s timeout fires) instead of bailing immediately.
385      if (!attempt()) {
386        var iv = setInterval(function(){
387          if (done) { clearInterval(iv); return; }
388          if (attempt()) clearInterval(iv);
389        }, 150);
390      }
391    });
392  };
393  refresh();
394  setTimeout(refresh, 5000);
395  setInterval(refresh, 120000);
396})();
397</script>
397
398<div class="formbody">
399<div class="body p-2">
400<!--FormError-->
401<!--FormMessage-->
402<form class="custom-form p-3 mx-0 border rounded" enctype="multipart/form-data" autocomplete="off" id="CustomForm" method="post" name="CustomForm" onsubmit="return unifiedSubmitForm(event)">
403<div aria-hidden="true" style="position:absolute;left:-9999px;top:-9999px;height:0;width:0;overflow:hidden;">
404  <label for="website_url_hp">Website (leave blank)</label>
405  <input type="text" name="website_url" id="website_url_hp" tabindex="-1" autocomplete="off" value="">
406</div>
407<input type="hidden" name="token" class="g-recaptcha-token" value="">
408<!--FKeys-->
409<input type="hidden" name="SystemEmailSubject" value="SWVAsun.com: Contact Us"> 
410<input type="hidden" name="SystemEmailConfirmMessage" value="&lt;center&gt;&lt;h2&gt;Your message has been forwarded to appropriate personnel. &lt;br&gt;&lt;br&gt;Thank you.&lt;/h2&gt;&lt;/center&gt;"> 
411<input type="hidden" name="SystemEmailFormat" value="HTML"> 
412<input type="hidden" name="SystemEmailTo" value="[email protected]"> 
413<input type="hidden" name="SystemEmailCC" value="">
414<center class="fw-bold mb-3 fs-3 text-secondary">Contact Us</center>
415<fieldset>
416<div class="system_text Header_text mb-5 text-center"><CENTER><B>Use the form below for comments or questions.</B></CENTER></div>
417
418
419
420<section class="w-96 mb-3" style="margin-left:2%" data-bs-toggle="tooltip" data-bs-placement="auto" title="">
421<div class="input w-100 form-floating">
422<INPUT NAME="Subject" VALUE="" id="Subject"  maxlength="" placeholder=" " type="Text"  class="formfieldinput formfieldinput_custom form-control" required>
423<label for="Subject" class="form-label text-muted">* Subject</label>
424</div>
425</section>
426 
427
428
429
430<section class="w-96 mb-3" style="margin-left:2%" data-bs-toggle="tooltip" data-bs-placement="auto" title="Be sure to enter proper email so we can contact you.">
431<div class="input w-100 form-floating">
432<INPUT NAME="Email" VALUE="" id="Email"  maxlength="" placeholder=" " type="Email"  class="formfieldinput formfieldinput_custom form-control" required>
433<label for="Email" class="form-label text-muted">* Email</label>
434</div>
435</section>
436 
437
438
439
440<section class="w-96 mb-3" style="margin-left:2%" data-bs-toggle="tooltip" data-bs-placement="auto" title="">
441<div class="input w-100 form-floating">
442<textarea  name="Message" id="Message" placeholder=" " class="formfieldinput formfieldinput_custom form-control" required rows="3" style="min-height: 120px !important; height: 120px !important; line-height: 1.5 !important; display: block !important; width: 100% !important; padding-top: 1.5rem !important;; min-height: 120px !important; height: 120px !important;" rows="3"></textarea>
443<label for="Message" class="form-label text-muted">* Message</label>
444</div>
445</section>
446 
447
448
449
450</fieldset>
451<footer class="text-center mt-3">
452<button class="button btn btn-primary shadow text-capitalize" id="SUBMIT1" type="submit" onclick="document.getElementById('CustomForm').classList.add('was-validated');"><span class="py-1 px-3 d-inline-block">submit</span></button>
453<i id="cus-form-button-loader" class='fa fa-circle-o-notch fa-spin fast-spin fs-1 text-primary'></i>
454</footer>
455<div class="system_text footer_text mt-5 mb-3 text-center"></div>
456</form>
457</div>
458<script language="javascript">GetGoogleAnalytics('FORM:ContactUs');</script>
458
459<!-- Tooltip -->
460<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
vendor: 2 bytes, line 460
460
461<script>
462var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
463var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
464return new bootstrap.Tooltip(tooltipTriggerEl)
465})
466</script>
466
467<!-- /Tooltip -->
468</div>
469
470<!-- Form Script v2026-01-26-v2 -->
471<!-- Modern Popup Modal for Messages -->
472<div id="form-message-modal" style="display:none; position:fixed; inset:0; background:rgba(0,0,0,0.7); z-index:99999; align-items:center; justify-content:center; padding:20px;">
473  <div style="background:#fff; min-width:320px; max-width:400px; border-radius:12px; padding:24px; box-shadow:0 25px 50px -12px rgba(0,0,0,0.5);">
474    <div id="form-message-text" style="margin-bottom:20px; font-size:15px; color:#333; text-align:center; line-height:1.5;"></div>
475    <button id="form-message-ok" style="width:100%; padding:12px 20px; font-size:14px; font-weight:600; background:#007bff; color:white; border:none; border-radius:8px; cursor:pointer; transition:all 0.2s ease;" onmouseover="this.style.background='#0056b3'" onmouseout="this.style.background='#007bff'">OK</button>
476  </div>
477</div>
478
479<script>
480// Resolve the URL to POST the form to. On a normal page load the form lives at
481// its own URL, so window.location.pathname is correct. But when the form is
482// opened inside a popup (sys-popup.js), the page URL is the HOST page, not the
483// form's URL -- sys-popup stamps the real form path onto the form as
484// data-form-action, which we prefer when present.
485function cmsResolvePostUrl(form) {
486  var f = form || document.querySelector('form');
487  var a = f && f.getAttribute && f.getAttribute('data-form-action');
488  return a || window.location.pathname;
489}
490
491// Modern message popup function
492// onClose callback is called when user clicks OK (useful for resetting button on error)
493function showFormMessage(text, isSuccess = false, onClose = null) {
494  const modal = document.getElementById('form-message-modal');
495  const modalMsg = document.getElementById('form-message-text');
496  const modalOk = document.getElementById('form-message-ok');
497  
498  if (modal && modalMsg && modalOk) {
499    modalMsg.innerHTML = isSuccess 
500      ? '<div style="display:flex;align-items:center;justify-content:center;gap:12px;margin-bottom:20px;"><div style="width:50px;height:50px;background:linear-gradient(135deg,#28a745,#20c997);border-radius:50%;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 15px rgba(40,167,69,0.3);flex-shrink:0;"><span style="color:white;font-size:28px;font-weight:bold;">✓</span></div><span style="color:#28a745;font-size:28px;font-weight:600;">Success</span></div>' + text
501      : text;
502    modal.style.display = 'flex';
503    modalOk.onclick = function() { 
504      modal.style.display = 'none';
505      if (isSuccess) {
506        (window.top || window).location.href = '/';
507      } else if (onClose) {
508        onClose();
509      }
510    };
511    // Also close on backdrop click
512    modal.onclick = function(e) {
513      if (e.target === modal) {
514        modal.style.display = 'none';
515        if (isSuccess) {
516          (window.top || window).location.href = '/';
517        } else if (onClose) {
518          onClose();
519        }
520      }
521    };
522  } else {
523    // Fallback to alert if modal not found
524    alert(text);
525    if (isSuccess) {
526      (window.top || window).location.href = '/';
527    } else if (onClose) {
528      onClose();
529    }
530  }
531}
532
533// Accept.js card input modal for Authorize.net
534async function showAcceptJsCardInput(clientConfig, form, originalFormData, paymentResult) {
535  console.log('[forms] Loading Accept.js for card tokenization');
536  
537  // Load Accept.js script dynamically
538  if (!window.Accept) {
539    await new Promise((resolve, reject) => {
540      const script = document.createElement('script');
541      script.src = clientConfig.jsUrl;
542      script.onload = resolve;
543      script.onerror = () => reject(new Error('Failed to load Accept.js'));
544      document.head.appendChild(script);
545    });
546  }
547  
548  // Create card input modal
549  const modalHtml = `
550    <div id="acceptjs-modal" style="position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:99999;display:flex;align-items:center;justify-content:center;padding:20px;">
551      <div style="background:#fff;max-width:400px;width:100%;border-radius:12px;padding:30px;box-shadow:0 25px 50px -12px rgba(0,0,0,0.5);">
552        <h3 style="margin:0 0 20px;color:#333;">Payment Information</h3>
553        <p style="margin:0 0 20px;color:#666;">Amount: <strong>$${paymentResult.amount}</strong></p>
554        
555        <div style="margin-bottom:15px;">
556          <label style="display:block;margin-bottom:5px;font-weight:600;color:#333;">Card Number *</label>
557          <input type="text" id="ajs-card-number" placeholder="4111 1111 1111 1111" maxlength="19"
558                 style="width:100%;padding:12px;border:1px solid #ccc;border-radius:4px;font-size:16px;box-sizing:border-box;">
559        </div>
560        
561        <div style="display:flex;gap:15px;margin-bottom:15px;">
562          <div style="flex:1;">
563            <label style="display:block;margin-bottom:5px;font-weight:600;color:#333;">Exp Month *</label>
564            <select id="ajs-exp-month" style="width:100%;padding:12px;border:1px solid #ccc;border-radius:4px;font-size:16px;">
565              <option value="">MM</option>
566              ${[...Array(12)].map((_,i) => '<option value="'+String(i+1).padStart(2,'0')+'">'+String(i+1).padStart(2,'0')+'</option>').join('')}
567            </select>
568          </div>
569          <div style="flex:1;">
570            <label style="display:block;margin-bottom:5px;font-weight:600;color:#333;">Exp Year *</label>
571            <select id="ajs-exp-year" style="width:100%;padding:12px;border:1px solid #ccc;border-radius:4px;font-size:16px;">
572              <option value="">YYYY</option>
573              ${[...Array(15)].map((_,i) => { const y=new Date().getFullYear()+i; return '<option value="'+y+'">'+y+'</option>'; }).join('')}
574            </select>
575          </div>
576          <div style="width:100px;">
577            <label style="display:block;margin-bottom:5px;font-weight:600;color:#333;">CVV *</label>
578            <input type="text" id="ajs-cvv" placeholder="123" maxlength="4"
579                   style="width:100%;padding:12px;border:1px solid #ccc;border-radius:4px;font-size:16px;box-sizing:border-box;">
580          </div>
581        </div>
582        
583        <div id="ajs-error" style="color:#dc3545;margin-bottom:15px;
583display:none;"></div>
584        
585        <div style="display:flex;gap:10px;">
586          <button type="button" id="ajs-cancel" style="flex:1;padding:12px;background:#6c757d;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:16px;">Cancel</button>
587          <button type="button" id="ajs-submit" style="flex:1;padding:12px;background:#28a745;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:16px;">Pay Now</button>
588        </div>
589      </div>
590    </div>
591  `;
592  
593  document.body.insertAdjacentHTML('beforeend', modalHtml);
594  
595  const modal = document.getElementById('acceptjs-modal');
596  const cardInput = document.getElementById('ajs-card-number');
597  const cvvInput = document.getElementById('ajs-cvv');
598  const errorDiv = document.getElementById('ajs-error');
599  
600  // Format card number
601  cardInput.addEventListener('input', function(e) {
602    let v = e.target.value.replace(/\s+/g, '').replace(/[^0-9]/g, '');
603    let f = '';
604    for (let i = 0; i < v.length; i++) { if (i > 0 && i % 4 === 0) f += ' '; f += v[i]; }
605    e.target.value = f;
606  });
607  
608  // Only numbers in CVV
609  cvvInput.addEventListener('input', function(e) {
610    e.target.value = e.target.value.replace(/[^0-9]/g, '');
611  });
612  
613  // Cancel button - also reset the submit button
614  document.getElementById('ajs-cancel').onclick = function() {
615    modal.remove();
616    // Reset the original form's submit button
617    const submitBtn = form.querySelector('button[type="submit"], #SUBMIT1, input[type="submit"]');
618    if (submitBtn) {
619      submitBtn.disabled = false;
620      if (submitBtn.tagName === 'INPUT') {
621        submitBtn.value = 'Submit';
622      } else {
623        submitBtn.innerHTML = 'Submit';
624      }
625    }
626  };
627  
628  // Submit button
629  document.getElementById('ajs-submit').onclick = async function() {
630    const cardNumber = cardInput.value.replace(/\s+/g, '');
631    const expMonth = document.getElementById('ajs-exp-month').value;
632    const expYear = document.getElementById('ajs-exp-year').value;
633    const cvv = cvvInput.value;
634    
635    // Validate
636    if (!cardNumber || cardNumber.length < 13) {
637      errorDiv.textContent = 'Please enter a valid card number';
638      errorDiv.style.display = 'block';
639      return;
640    }
641    if (!expMonth || !expYear) {
642      errorDiv.textContent = 'Please select expiration date';
643      errorDiv.style.display = 'block';
644      return;
645    }
646    if (!cvv || cvv.length < 3) {
647      errorDiv.textContent = 'Please enter CVV';
648      errorDiv.style.display = 'block';
649      return;
650    }
651    
652    errorDiv.style.display = 'none';
653    this.disabled = true;
654    this.innerHTML = '<span class="form-spinner-ring" style="border-color:rgba(255,255,255,0.4);border-top-color:#fff;"></span> Processing...';
655
656    // Test card detection - skip Accept.js tokenization
657    var TEST_CARDS_V3 = ['4111111111111111', '4000000000000000', '5500000000000004', '370000000000002', '6011000000000004'];
658    if (TEST_CARDS_V3.indexOf(cardNumber) !== -1) {
659      console.log('[forms] Test card detected - skipping tokenization');
660      originalFormData.append('dataDescriptor', 'TEST_CARD');
661      originalFormData.append('dataValue', 'TEST');
662      originalFormData.append('pf-card-number-input', cardNumber);
663
664      try {
665        const testResp = await fetch(cmsResolvePostUrl(), {
666          method: 'POST',
667          body: originalFormData,
668          headers: { 'Accept': 'application/json' }
669        });
670        const testResult = await testResp.json();
671        modal.remove();
672        if (testResult.success && testResult.isPaymentComplete) {
673          if (testResult.subscriberToken) {
674            try {
675              var tp2 = JSON.parse(atob(testResult.subscriberToken.split('.')[1]));
676              var ma2 = tp2.exp ? (tp2.exp - Math.floor(Date.now() / 1000)) : (30 * 24 * 60 * 60);
677              document.cookie = 'subscriber_token=' + encodeURIComponent(testResult.subscriberToken) + '; path=/; max-age=' + ma2 + '; SameSite=Lax';
678            } catch (e) { /* ignore */ }
679          }
680          var msg = testResult.confirmMessage
681            ? testResult.confirmMessage + '<br><br><small>Transaction ID: ' + testResult.transactionId + '</small>'
682            : 'Test payment successful! Transaction ID: ' + testResult.transactionId;
683          var retUrl = null;
684          try { retUrl = localStorage.getItem('subscriber_return_url'); } catch (e) { /* ignore */ }
685          if (!retUrl) { try { retUrl = new URLSearchParams(window.location.search).get('return_url'); } catch (e) { /* ignore */ } }
686          if (retUrl) {
687            try { localStorage.removeItem('subscriber_return_url'); } catch (e) { /* ignore */ }
688            msg += '<p style="margin-top:15px;color:#666;">Redirecting you back to your article...</p>';
689            showFormMessage(msg, true);
690            setTimeout(function() { window.location.href = retUrl; }, 2000);
691          } else {
692            showFormMessage(msg, true);
693          }
694          form.reset();
695        } else {
696          showFormMessage(testResult.error || 'Payment failed.', false);
697        }
698      } catch (e) {
699        modal.remove();
700        showFormMessage('Network error. Please try again.', false);
701      }
702      return;
703    } else {
704    // Build Accept.js request
705    const secureData = {
706      authData: {
707        clientKey: clientConfig.clientKey,
708        apiLoginID: clientConfig.apiLoginId
709      },
710      cardData: {
711        cardNumber: cardNumber,
712        month: expMonth,
713        year: expYear,
714        cardCode: cvv
715      }
716    };
717
718    // Tokenize via Accept.js
719    Accept.dispatchData(secureData, async function(response) {
720      if (response.messages.resultCode === 'Error') {
721        const errorText = response.messages.message.map(m => m.text).join(', ');
722        errorDiv.textContent = errorText;
723        errorDiv.style.display = 'block';
724        const ajsBtn = document.getElementById('ajs-submit');
725        ajsBtn.disabled = false;
726        ajsBtn.innerHTML = 'Pay Now';
727        return;
728      }
729
730      console.log('[forms] Card tokenized successfully, resubmitting form');
731
732      // Add token to form data and resubmit
733      originalFormData.append('dataDescriptor', response.opaqueData.dataDescriptor);
734      originalFormData.append('dataValue', response.opaqueData.dataValue);
735      
736      try {
737        const response2 = await fetch(cmsResolvePostUrl(), {
738          method: 'POST',
739          body: originalFormData,
740          headers: { 'Accept': 'application/json' }
741        });
742        const result2 = await response2.json();
743        
744        modal.remove();
745        
746        // Get the submit button to reset on error
747        const submitBtn = form.querySelector('button[type="submit"], #SUBMIT1, input[type="submit"]');
748        const resetButton = function() {
749          if (submitBtn) {
750            submitBtn.disabled = false;
751            if (submitBtn.tagName === 'INPUT') {
752              submitBtn.value = 'Submit';
753            } else {
754              submitBtn.innerHTML = 'Submit';
755            }
756          }
757        };
758        
759        if (result2.success && result2.isPaymentComplete) {
760          // Auto-login: set subscriber_token cookie if provided
761          if (result2.subscriberToken) {
762            try {
763              var tp = JSON.parse(atob(result2.subscriberToken.split('.')[1]));
764              var ma = tp.exp ? (tp.exp - Math.floor(Date.now() / 1000)) : (30 * 24 * 60 * 60);
765              document.cookie = 'subscriber_token=' + encodeURIComponent(result2.subscriberToken) + '; path=/; max-age=' + ma + '; SameSite=Lax';
766              console.log('[forms] Auto-login: subscriber_token cookie set (Accept.js path)');
767            } catch (e) { console.warn('[forms] Auto-login cookie error', e); }
768          }
769          // Build success message with custom confirm message if available
770          let successMsg = 'Payment successful! Transaction ID: ' + result2.transactionId;
771          if (result2.confirmMessage) {
772            successMsg = result2.confirmMessage + '<br><br><small>Transaction ID: ' + result2.transactionId + '</small>';
773          }
774          var retUrl2 = null;
775          try { retUrl2 = localStorage.getItem('subscriber_return_url'); } catch (e) { /* ignore */ }
776          if (!retUrl2) { try { retUrl2 = new URLSearchParams(window.location.search).get('return_url'); } catch (e) { /* ignore */ } }
777          if (retUrl2) {
778            try { localStorage.removeItem('subscriber_return_url'); } catch (e) { /* ignore */ }
779            successMsg += '<p style="margin-top:15px;color:#666;">Redirecting you back to your article...</p>';
780            showFormMessage(successMsg, true);
781            setTimeout(function() { window.location.href = retUrl2; }, 2000);
782          } else {
783            showFormMessage(successMsg, true);
784          }
785          form.reset();
786        } else {
787          showFormMessage(result2.error || 'Payment failed. Please try again.', false, resetButton);
788        }
789      } catch (e) {
790        modal.remove();
791        const submitBtn = form.querySelector('button[type="submit"], #SUBMIT1, input[type="submit"]');
792        showFormMessage('Network error. Please try again.', false, function() {
793          if (submitBtn) {
794            submitBtn.disabled = false;
795            if (submitBtn.tagName === 'INPUT') {
796              submitBtn.value = 'Submit';
797            } else {
798              submitBtn.innerHTML = 'Submit';
799            }
800          }
801        });
802      }
803    });
804    } // end else (real card tokenization)
805  };
806}
807
808// Initialize File Uploader on load to handle image cropping/uploading.
809// Defined as a named function (not an inline DOMContentLoaded callback) so it can
810// also run immediately when the DOM is ALREADY parsed -- e.g. when this form is
811// injected into a popup after DOMContentLoaded has already fired. Without that,
812// the uploader (image cropper) never initializes inside the modal.
813async function cmsInitFormUploader() {
814  // Restore form data if user cancelled payment
815  try {
816    const pendingData = sessionStorage.getItem('_pendingFormData');
817    if (pendingData) {
818      const formData = JSON.parse(pendingData);
819      const form = document.getElementById('CustomForm') || document.querySelector('form');
820      if (form) {
821        for (const [key, value] of Object.entries(formData)) {
822          if (value && typeof value === 'string') {
823            const field = form.querySelector('[name="' + key + '"]');
824            if (field) {
825              if (field.tagName === 'SELECT') {
826                field.value = value;
827              } else if (field.type !== 'file' && field.type !== 'hidden') {
828                field.value = value;
829              }
830            }
831          }
832        }
833        console.log('[forms] Restored form data from cancelled payment');
834      }
835      // Clear the stored data
836      sessionStorage.removeItem('_pendingFormData');
837      sessionStorage.removeItem('_pendingFormUrl');
838    }
839  } catch (e) {
840    console.error('[forms] Error restoring form data:', e);
841  }
842  
843  try {
844    const { UnifiedFileUploader } = await import('/sys-components-UnifiedFileUploader.js?v=' + Date.now());
845    window.unifiedUploader = new UnifiedFileUploader();
846    window.unifiedUploader.init();
847  } catch (e) {
848    console.error('Failed to init uploader:', e);
849  }
850}
851// Normal page load: wait for DOMContentLoaded. Popup: the form is injected after
852// DOMContentLoaded already fired, so run immediately when the DOM is already
853// parsed -- otherwise the uploader (image cropper) never initializes in the
854// modal. The earlier empty-box / stuck-button symptom was FontAwesome 4 vs 6
855// (the FA6 spinner glyph was missing); the form now injects FA6, so the cropper
856// controls and spinner render and the uploader completes normally here too.
857if (document.readyState === 'loading') {
858  document.addEventListener('DOMContentLoaded', cmsInitFormUploader);
859} else {
860  cmsInitFormUploader();
861}
862
863async function unifiedSubmitForm(event) {
864  console.log('[forms] unifiedSubmitForm called v5 - 2026-01-26 Enhanced spinner');
865  if (event) { event.preventDefault(); event.stopPropagation(); }
866  
867  // Use event.target if available (most reliable)
868  const form = (event && event.target && event.target.tagName === 'FORM') ? event.target : (document.getElementById('CustomForm') || document.querySelector('form'));
869
870  // Enforce native constraint validation (required / type=email / pattern ...)
871  // BEFORE anything else. This handler preventDefault()s the native submit on its
872  // first line, which also bypasses the browser's own required-field check -- so
873  // without this call a blank required field would fetch-POST straight through
874  // (legacy custom forms had no client-side guard at all). reportValidity() focuses
875  // the first invalid field, shows the browser's validation bubble, and bails.
876  // Runs before the submit button is put into its spinner/disabled state so a
877  // blocked submit leaves the form fully usable.
878  if (form && typeof form.reportValidity === 'function' && !form.reportValidity()) {
879    return false;
880  }
881
882  // Ensure any processed files are in the form before submission
883  if (window.unifiedUploader && window.unifiedUploader.ensureProcessedFilesInForm) {
884    window.unifiedUploader.ensureProcessedFilesInForm();
885  }
886  
887  const btn = form.querySelector('button[type="submit"], #SUBMIT1, input[type="submit"]');
888  const originalHtml = btn ? (btn.tagName === 'INPUT' ? btn.value : btn.innerHTML) : 'Submit';
889  
890  // Show loading state with spinner
891  if (btn) {
892    btn.disabled = true;
893    if (btn.tagName === 'INPUT') {
894      // Inputs can't hold an inline span, so use the class-based ::after spinner.
895      btn.classList.add('btn-loading');
896      btn.value = '  Processing...';
897      btn.style.paddingLeft = '35px';
898    } else {
899      // Buttons: the inline ring span is the single spinner. Do NOT add the
900      // btn-loading class here -- button.btn-loading::after renders a SECOND
901      // spinner, which produced two spinning icons at once on submit. Keep the
902      // disabled look via inline styles instead (robust even in popups where the
903      // injected spinner CSS may be absent).
904      btn.style.pointerEvents = 'none';
905      btn.style.opacity = '0.85';
906      btn.innerHTML = '<span class="form-spinner-ring"></span> Processing...';
907    }
908  }
909  
910  // Single spinner only: the disabled submit button already shows a spinner
911  // ("Processing..."), so we intentionally do NOT add the full-form overlay
912  // spinner here -- it produced two spinning icons at once (most visible in the
913  // popup). overlay stays null; the cleanup checks below are guarded for it.
914  let overlay = null;
915  try {
916    // Mint a fresh reCAPTCHA token before reading the form. On popup-rendered
917    // forms the on-load mint may not have fired yet (api.js was injected only
918    // moments ago by sys-popup's executeScripts), so without this the hidden
919    // token field is still empty and the server blocks as recaptcha_missing.
920    if (typeof window.cmsMintCaptchaToken === 'function') {
921      try { await window.cmsMintCaptchaToken(); } catch (e) {}
922    }
923    const formData = new FormData(form);
924
925    // Capture display TEXT for all select, radio, and checkbox elements
926    // Add as fieldname_text so email shows human-readable labels instead of raw values
927    form.querySelectorAll('select').forEach(function(select) {
928      if (select.name && select.selectedIndex >= 0) {
929        if (select.multiple) {
930          // Multi-select: join the text of EVERY selected option, not just the first
931          var multiTexts = Array.prototype.map.call(select.selectedOptions, function(o) { return o.text; });
932          formData.append(select.name + '_text', multiTexts.join(', '));
933        } else {
934          const selectedOption = select.options[select.selectedIndex];
935          if (selectedOption) {
936            formData.append(select.name + '_text', selectedOption.text);
937          }
938        }
939      }
940    });
941    // Capture checked radio button labels
942    var radiosDone = {};
943    form.querySelectorAll('input[type="radio"]:checked').forEach(function(radio) {
944      if (radio.name && !radiosDone[radio.name]) {
945        radiosDone[radio.name] = true;
946        var label = form.querySelector('label[for="' + radio.id + '"]');
947        if (label) {
948          formData.append(radio.name + '_text', label.textContent.trim());
949        }
950      }
951    });
952    // Capture checked checkbox labels
953    var checkboxTexts = {};
954    form.querySelectorAll('input[type="checkbox"]:checked').forEach(function(cb) {
955      if (cb.name) {
956        var cleanName = cb.name.replace('[]', '');
957        var label = form.querySelector('label[for="' + cb.id + '"]');
958        var text = label ? label.textContent.trim() : (cb.value === 'on' ? 'Yes' : cb.value);
959        if (!checkboxTexts[cleanName]) checkboxTexts[cleanName] = [];
960        checkboxTexts[cleanName].push(text);
961      }
962    });
963    Object.keys(checkboxTexts).forEach(function(name) {
964      formData.append(name + '_text', checkboxTexts[name].join(', '));
965    });
966    const response = await fetch(cmsResolvePostUrl(form), {
967      method: 'POST',
968      body: formData,
969      headers: { 'Accept': 'application/json' }
970    });
971    const result = await response.json();
972    console.log('[forms] v3 Server response:', JSON.stringify(result));
973    console.log('[forms] v3 Keys in result:', Object.keys(result).join(', '));
974    console.log('[forms] v3 requiresCardInput:', result.requiresCardInput, 'clientConfig:', !!result.clientConfig);
975    console.log('[forms] v3 isPaymentRedirect:', result.isPaymentRedirect, 'redirectUrl:', result.redirectUrl);
976    if (result.success) {
977      // PRIORITY 0: Check if payment is already complete (Authorize.net Accept.js)
978      if (result.isPaymentComplete) {
979        console.log('[forms] Payment complete! Transaction:', result.transactionId);
980
981        // Auto-login: set subscriber_token cookie if provided
982        if (result.subscriberToken) {
983          try {
984            var tokenPayload = JSON.parse(atob(result.subscriberToken.split('.')[1]));
985            var maxAge = tokenPayload.exp ? (tokenPayload.exp - Math.floor(Date.now() / 1000)) : (30 * 24 * 60 * 60);
986            document.cookie = 'subscriber_token=' + encodeURIComponent(result.subscriberToken) + '; path=/; max-age=' + maxAge + '; SameSite=Lax';
987            console.log('[forms] Auto-login: subscriber_token cookie set');
988          } catch (e) {
989            console.warn('[forms] Auto-login: failed to set cookie', e);
990          }
991        }
992
993        // Build success message with custom confirm message if available
994        let successMsg = 'Payment successful! Transaction ID: ' + result.transactionId;
995        if (result.confirmMessage) {
996          successMsg = result.confirmMessage + '<br><br><small>Transaction ID: ' + result.transactionId + '</small>';
997        }
998
999        // Redirect back to article if return URL is available (from paywall subscribe flow)
1000        var returnUrl = null;
1001        try {
1002          returnUrl = localStorage.getItem('subscriber_return_url');
1003        } catch (e) { /* ignore */ }
1004        // Also check URL query param
1005        if (!returnUrl) {
1006          try {
1007            var params = new URLSearchParams(window.location.search);
1008            returnUrl = params.get('return_url');
1009          } catch (e) { /* ignore */ }
1010        }
1011
1012        if (returnUrl) {
1013          try { localStorage.removeItem('subscriber_return_url'); } catch (e) { /* ignore */ }
1014          // Show brief success message then redirect
1015          successMsg += '<p style="margin-top:15px;color:#666;">Redirecting you back to your article...</p>';
1016          showFormMessage(successMsg, true);
1017          setTimeout(function() { window.location.href = returnUrl; }, 2000);
1018        } else {
1019          showFormMessage(successMsg, true);
1020        }
1021        form.reset();
1022        return;
1023      }
1024      
1025      // PRIORITY 1: Check for payment redirect FIRST (Stripe Checkout)
1026      // Payment forms MUST redirect to the payment gateway before showing any message
1027      if (result.isPaymentRedirect && result.redirectUrl) {
1028        console.log('[forms] Payment redirect detected, redirecting to:', result.redirectUrl);
1029        window.location.href = result.redirectUrl;
1030        return;
1031      }
1032      
1033      // PRIORITY 1.5: Check if card input is required (Authorize.net Accept.js)
1034      console.log('[forms] v3 Checking requiresCardInput:', result.requiresCardInput, 'clientConfig exists:', !!result.clientConfig);
1035      if (result.requiresCardInput && result.clientConfig) {
1036        console.log('[forms] v3 Card input required! Showing Accept.js modal...');
1037        try {
1038          await showAcceptJsCardInput(result.clientConfig, form, formData, result);
1039          console.log('[forms] v3 showAcceptJsCardInput completed');
1040        } catch (modalError) {
1041          console.error('[forms] v3 showAcceptJsCardInput error:', modalError);
1042        }
1043        return;
1044      }
1045      
1046      // PRIORITY 2: Check for regular redirect URL
1047      if (result.redirectUrl && result.redirectUrl !== window.location.href) {
1048        console.log('[forms] Redirecting to:', result.redirectUrl);
1049        window.location.href = result.redirectUrl;
1050        return;
1051      }
1052      
1053      // PRIORITY 3: Show thanks content (email_confirm_message, etc.)
1054      if (result.thanksContent) {
1055        // CRITICAL: Scroll to top BEFORE replacing content to avoid layout shifts
1056        window.scrollTo({ top: 0, behavior: 'smooth' });
1057        
1058        // Check if standalone thank you page is requested
1059        if (result.standaloneThankYou) {
1060          // Replace entire page content with standalone thank you page
1061          document.body.innerHTML = '<div style="min-height:100vh;display:flex;align-items:center;justify-content:center;background:#f5f5f5;padding:20px;"><div style="max-width:800px;width:100%;background:#fff;border-radius:12px;box-shadow:0 4px 20px rgba(0,0,0,0.1);padding:40px;">' + result.thanksContent + '</div></div>';
1062          window.scrollTo({ top: 0, behavior: 'instant' });
1063          return;
1064        }
1065        
1066        // Replace form content with thanks message (embedded mode)
1067        form.innerHTML = '<div id="form-thanks-message" style="padding:40px;text-align:center;min-height:200px">' + result.thanksContent + '</div>';
1068        
1069        // CRITICAL: Force scroll to top using multiple methods for maximum compatibility
1070        const forceScrollToTop = () => {
1071          console.log('[forms] Executing robust scroll-to-top');
1072          
1073          // 1. Standard window scroll
1074          window.scrollTo({ top: 0, behavior: 'smooth' });
1075          
1076          // 2. Element-based scroll (most reliable when embedded)
1077          const thanksEl = document.getElementById('form-thanks-message');
1078          if (thanksEl) {
1079            thanksEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
1080          }
1081          
1082          // 3. Recursive parent scroll (for nested scrollable containers)
1083          try {
1084            let parent = form.parentElement;
1085            while (parent && parent !== document.body) {
1086              if (parent.scrollHeight > parent.clientHeight) {
1087                console.log('[forms] Resetting parent scroll:', parent);
1088                parent.scrollTop = 0;
1089              }
1090              parent = parent.parentElement;
1091            }
1092          } catch (e) {}
1093          
1094          // 4. Fallback for older/mobile browsers
1095          document.body.scrollTop = 0;
1096          document.documentElement.scrollTop = 0;
1097          
1098          // 5. Hash trick to force top
1099          if (window.location.hash !== '') {
1100            window.location.hash = '';
1101          }
1102          
1103          // 6. If inside iframe, try to scroll parent window
1104          try {
1105            if (window.parent && window.parent !== window) {
1106              window.parent.scrollTo({ top: 0, behavior: 'smooth' });
1107            }
1108          } catch (e) {}
1109        };
1110
1111        // Execute in multiple phases to ensure layout is settled
1112        forceScrollToTop();
1113        requestAnimationFrame(forceScrollToTop);
1114        setTimeout(forceScrollToTop, 100);
1115        setTimeout(forceScrollToTop, 500);
1116        setTimeout(forceScrollToTop, 1000);
1117        setTimeout(forceScrollToTop, 2000);
1118      } else {
1119        // Default: redirect to home
1120        window.location.href = "/";
1121      }
1122    } else {
1123      // Remove overlay and reset button on error
1124      if (overlay && overlay.parentNode) overlay.remove();
1125      showFormMessage(result.error || "Submission failed");
1126      if (btn) {
1127        btn.disabled = false;
1128        btn.classList.remove('btn-loading');
1129        btn.style.paddingLeft = '';
1130        btn.style.pointerEvents = '';
1131        btn.style.opacity = '';
1132        if (btn.tagName === 'INPUT') {
1133          btn.value = originalHtml;
1134        } else {
1135          btn.innerHTML = originalHtml;
1136        }
1137      }
1138    }
1139  } catch (e) {
1140    console.error(e);
1141    // Remove overlay and reset button on network error
1142    if (overlay && overlay.parentNode) overlay.remove();
1143    showFormMessage("Network error. Please check your connection and try again.");
1144    if (btn) {
1145      btn.disabled = false;
1146      btn.classList.remove('btn-loading');
1147      btn.style.paddingLeft = '';
1148      btn.style.pointerEvents = '';
1149      btn.style.opacity = '';
1150      if (btn.tagName === 'INPUT') {
1151        btn.value = originalHtml;
1152      } else {
1153        btn.innerHTML = originalHtml;
1154      }
1155    }
1156  }
1157  return false;
1158}
1159
1160// ========== STRIPE PAYMENT SUCCESS HANDLER ==========
1161// Check for payment success redirect from Stripe on page load
1162(function checkStripePaymentSuccess() {
1163  const urlParams = new URLSearchParams(window.location.search);
1164  const paymentStatus = urlParams.get('payment');
1165  const sessionId = urlParams.get('session_id');
1166  const returnUrl = urlParams.get('return_url');
1167  
1168  if (paymentStatus === 'success' && sessionId) {
1169    console.log('[forms] Detected Stripe payment success, session:', sessionId);
1170    console.log('[forms] Return URL:', returnUrl || 'none');
1171    
1172    // Show loading message
1173    showFormMessage('Processing payment confirmation...');
1174    
1175    // Fetch session details from our API
1176    fetch((typeof __CMS_BASE_PATH !== 'undefined' ? __CMS_BASE_PATH : '') + '/api/payment/session/' + encodeURIComponent(sessionId))
1177      .then(response => response.json())
1178      .then(data => {
1179        if (data.success) {
1180          const amount = data.amount ? '$' + parseFloat(data.amount).toFixed(2) : '';
1181          const txId = data.transaction_id || sessionId;
1182          
1183          // Build success message
1184          let msg = '<div style="margin-bottom:15px;font-size:16px;">Your subscription has been activated!</div>';
1185          msg += '<div style="margin-bottom:15px;font-size:18px;font-weight:600;">Thank you for subscribing.</div>';
1186          if (amount) {
1187            msg += '<div style="margin-bottom:10px;color:#666;">Amount: <strong>' + amount + '</strong></div>';
1188          }
1189          if (data.description && data.description !== 'Payment') {
1190            msg += '<div style="margin-bottom:10px;color:#666;">Plan: <strong>' + data.description + '</strong></div>';
1191          }
1192          msg += '<div style="background:#e8f5e9;padding:10px;border-radius:4px;margin-top:15px;"><strong>Transaction ID:</strong> <span style="font-family:monospace;user-select:all;">' + txId + '</span></div>';
1193          
1194          // If there's a return URL, add redirect notice
1195          if (returnUrl) {
1196            msg += '<div style="margin-top:15px;color:#666;font-size:14px;">Redirecting you back to your article in 3 seconds...</div>';
1197          }
1198          
1199          showFormMessage(msg, true);
1200          
1201          // Clean up URL (remove query params)
1202          if (window.history && window.history.replaceState) {
1203            const cleanUrl = window.location.pathname;
1204            window.history.replaceState({}, document.title, cleanUrl);
1205          }
1206          
1207          // Redirect to return URL after 3 seconds (back to the article)
1208          if (returnUrl) {
1209            setTimeout(function() {
1210              // Ensure return URL is same origin for security
1211              try {
1212                const returnUrlObj = new URL(returnUrl, window.location.origin);
1213                if (returnUrlObj.origin === window.location.origin) {
1214                  window.location.href = returnUrl;
1215                }
1216              } catch (e) {
1217                // If it starts with /, treat as relative URL
1218                if (returnUrl.startsWith('/')) {
1219                  window.location.href = returnUrl;
1220                }
1221              }
1222            }, 3000);
1223          }
1224        } else {
1225          showFormMessage('Payment was processed but we could not retrieve details. Your payment was successful.', true);
1226          // Still redirect if return URL exists
1227          if (returnUrl) {
1228            setTimeout(function() { window.location.href = returnUrl; }, 3000);
1229          }
1230        }
1231      })
1232      .catch(err => {
1233        console.error('[forms] Error fetching session:', err);
1234        // Still show success even if API fails - payment went through
1235        showFormMessage('<div style="margin-bottom:15px;">Payment successful!</div><div style="background:#e8f5e9;padding:10px;border-radius:4px;"><strong>Session ID:</strong> <span style="font-family:monospace;">' + sessionId + '</span></div>', true);
1236        // Still redirect if return URL exists
1237        if (returnUrl) {
1238          setTimeout(function() { window.location.href = returnUrl; }, 3000);
1239        }
1240      });
1241  } else if (paymentStatus === 'cancelled') {
1242    showFormMessage('Payment was cancelled. You can try again when ready.');
1243    // Clean up URL
1244    if (window.history && window.history.replaceState) {
1245      const cleanUrl = window.location.pathname;
1246      window.history.replaceState({}, document.title, cleanUrl);
1247    }
1248  }
1249})();
1250</script>
1250</div>
1251<div class="w-100 d-block"><div data-cms-block="adgroup-30" data-cms-block-label="Advertisement 30" data-cms-el="adgroup" data-cms-adgroup="30">
1251<script type="text/javascript" language="JavaScript">AdGroup30();</script>
1251</div></div>
1252</div>
1253    <div class="cus-column-133-2 col-lg-3 ps-lg-3 border-start">
1254 <div data-cms-block="adgroup-29" data-cms-block-label="Advertisement 29" data-cms-el="adgroup" data-cms-adgroup="29">
1254<script type="text/javascript" language="JavaScript">AdGroup29();</script>
1254</div><div data-cms-block="adgroup-20" data-cms-block-label="Advertisement 20" data-cms-el="adgroup" data-cms-adgroup="20">
1254<script type="text/javascript" language="JavaScript">AdGroup20();</script>
1254</div><div data-cms-block="poll-1" data-cms-block-label="Poll 1" data-cms-el="poll" data-poll-id="1">
1254<script type="text/javascript" language="JavaScript">GetPoll1();</script>
1254</div>
1255<div data-cms-block="section-file-29" data-cms-block-label="Section 29" data-cms-el="section" data-cms-section="29"><div class="cus-photogallery-133"> 
1256 <div class="cus-default-menu-bar">
1257 <div class="text-uppercase fw-bold px-0 mb-2 text-black cus-133-label">
1258 <span>
1259 <span class="cus-default-submenu">Swva Sun Video</span>
1260 </span>
1261 </div>
1262 </div> 
1263 <div class="row mx-0 hp-parent"> 
1264 
1265 <div class="col-sm-12 col-lg-4 px-1 mb-2 hpe-in-video-g-yt-id" data-article="15589" data-cms-article-id="15589" data-url="sun-spotlight-for-feb-p15589-29.htm" data-menu="GALLERY" data-submenu="Swva Sun Video" data-date="02_07_2026"> 
1266 <div class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden"> 
1267 <a class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden" href="javascript:void(0);" onclick="OpenYouTube(`https://open.substack.com/pub/swvasun/p/sun-spotlight-for-the-week-of-feb?r=7ctp54&utm_campaign=post&utm_medium=web&showWelcomeOnShare=true`.trim());"> 
1268 <div class="ratio-16-9"> 
1269 <div class="img-content cus-dark-img"> 
1270 <img class="cus-videogallery-29-img img-fluid lazy loaded" src="https://img.youtube.com/vi/https://open.substack.com/pub/swvasun/p/sun-spotlight-for-the-week-of-feb?r=7ctp54&utm_campaign=post&utm_medium=web&showWelcomeOnShare=true/maxresdefault.jpg" data-body="https://open.substack.com/pub/swvasun/p/sun-spotlight-for-the-week-of-feb?r=7ctp54&utm_campaign=post&utm_medium=web&showWelcomeOnShare=true" data-src="/05-02-2026-01-26-25-pm-3456539.jpg" alt="Sun Spotlight for Feb. 1-7" data-was-processed="true" loading="lazy"> 
1271 </div> 
1272 </div> 
1273 <span class="cus-videogallery-29-play position-absolute translate-middle top-50 start-50">
1274 <i class="fa fa-play fs-1 text-white text-shadow-1" aria-hidden="true"></i>
1275 </span> 
1276 <p class="cus-videogallery-29-heading mb-0 bg-dark text-white p-2 fs-6 lh-lg fw-bold" data-bs-toggle="tooltip" data-bs-placement="bottom" title="" data-bs-original-title="Sun Spotlight for Feb. 1-7">
1277 <span class="line-1">Sun Spotlight for Feb. 1-7</span>
1278 </p> 
1279 </a> 
1280 </div> 
1281 </div> 
1282
1283 <div class="col-sm-12 col-lg-4 px-1 mb-2 hpe-in-video-g-yt-id" data-article="8871" data-cms-article-id="8871" data-url="wytheville-parks-and-rec-maroon-silver-vs-rural-retreat-black-p8871-29.htm" data-menu="GALLERY" data-submenu="Swva Sun Video" data-date="
128306_10_2024"> 
1284 <div class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden"> 
1285 <a class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden" href="javascript:void(0);" onclick="OpenYouTube(`-2rZwiaTiQ0`.trim());"> 
1286 <div class="ratio-16-9"> 
1287 <div class="img-content cus-dark-img"> 
1288 <img class="cus-videogallery-29-img img-fluid lazy loaded" src="https://img.youtube.com/vi/-2rZwiaTiQ0/maxresdefault.jpg" data-body="-2rZwiaTiQ0" data-src="/10-06-2024-08-51-49-pm-2030388.jpg" alt="5/6/7 Wytheville Parks and Rec Maroon Silver vs. Rural Retreat Black" data-was-processed="true" loading="lazy"> 
1289 </div> 
1290 </div> 
1291 <span class="cus-videogallery-29-play position-absolute translate-middle top-50 start-50">
1292 <i class="fa fa-play fs-1 text-white text-shadow-1" aria-hidden="true"></i>
1293 </span> 
1294 <p class="cus-videogallery-29-heading mb-0 bg-dark text-white p-2 fs-6 lh-lg fw-bold" data-bs-toggle="tooltip" data-bs-placement="bottom" title="" data-bs-original-title="5/6/7 Wytheville Parks and Rec Maroon Silver vs. Rural Retreat Black">
1295 <span class="line-1">5/6/7 Wytheville Parks and Rec Maroon Silver vs. Rural Retreat Black</span>
1296 </p> 
1297 </a> 
1298 </div> 
1299 </div> 
1300
1301 <div class="col-sm-12 col-lg-4 px-1 mb-2 hpe-in-video-g-yt-id" data-article="111" data-cms-article-id="111" data-url="the-fourth-is-with-wytheville-p111-29.htm" data-menu="GALLERY" data-submenu="Swva Sun Video" data-date="
130104_22_2024"> 
1302 <div class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden"> 
1303 <a class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden" href="javascript:void(0);" onclick="OpenYouTube(`rnh5RDc7usk`.trim());"> 
1304 <div class="ratio-16-9"> 
1305 <div class="img-content cus-dark-img"> 
1306 <img class="cus-videogallery-29-img img-fluid lazy loaded" src="https://img.youtube.com/vi/rnh5RDc7usk/maxresdefault.jpg" data-body="rnh5RDc7usk" data-src="/no-image.png" alt="The Fourth is with Wytheville" data-was-processed="true" loading="lazy"> 
1307 </div> 
1308 </div> 
1309 <span class="cus-videogallery-29-play position-absolute translate-middle top-50 start-50">
1310 <i class="fa fa-play fs-1 text-white text-shadow-1" aria-hidden="true"></i>
1311 </span> 
1312 <p class="cus-videogallery-29-heading mb-0 bg-dark text-white p-2 fs-6 lh-lg fw-bold" data-bs-toggle="tooltip" data-bs-placement="bottom" title="" data-bs-original-title="The Fourth is with Wytheville">
1313 <span class="line-1">The Fourth is with Wytheville</span>
1314 </p> 
1315 </a> 
1316 </div> 
1317 </div> 
1318
1319 <div class="col-sm-12 col-lg-4 px-1 mb-2 hpe-in-video-g-yt-id" data-article="112" data-cms-article-id="112" data-url="trail-talk-chasing-waterfalls-in-smyth-county-p112-29.htm" data-menu="GALLERY" data-submenu="Swva Sun Video" data-date="
131904_22_2024"> 
1320 <div class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden"> 
1321 <a class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden" href="javascript:void(0);" onclick="OpenYouTube(`UmbIwjjST8E`.trim());"> 
1322 <div class="ratio-16-9"> 
1323 <div class="img-content cus-dark-img"> 
1324 <img class="cus-videogallery-29-img img-fluid lazy loaded" src="https://img.youtube.com/vi/UmbIwjjST8E/maxresdefault.jpg" data-body="UmbIwjjST8E" data-src="/no-image.png" alt="Trail Talk: Chasing Waterfalls in Smyth County" data-was-processed="true" loading="lazy"> 
1325 </div> 
1326 </div> 
1327 <span class="cus-videogallery-29-play position-absolute translate-middle top-50 start-50">
1328 <i class="fa fa-play fs-1 text-white text-shadow-1" aria-hidden="true"></i>
1329 </span> 
1330 <p class="cus-videogallery-29-heading mb-0 bg-dark text-white p-2 fs-6 lh-lg fw-bold" data-bs-toggle="tooltip" data-bs-placement="bottom" title="" data-bs-original-title="Trail Talk: Chasing Waterfalls in Smyth County">
1331 <span class="line-1">Trail Talk: Chasing Waterfalls in Smyth County</span>
1332 </p> 
1333 </a> 
1334 </div> 
1335 </div> 
1336
1337 <div class="col-sm-12 col-lg-4 px-1 mb-2 hpe-in-video-g-yt-id" data-article="62" data-cms-article-id="62" data-url="wytheville-nd-and-rd-grade-flag-football-p62-29.htm" data-menu="GALLERY" data-submenu="Swva Sun Video" data-date="
133704_15_2024"> 
1338 <div class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden"> 
1339 <a class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden" href="javascript:void(0);" onclick="OpenYouTube(`DZXFNIbWTRQ`.trim());"> 
1340 <div class="ratio-16-9"> 
1341 <div class="img-content cus-dark-img"> 
1342 <img class="cus-videogallery-29-img img-fluid lazy loaded" src="https://img.youtube.com/vi/DZXFNIbWTRQ/maxresdefault.jpg" data-body="DZXFNIbWTRQ" data-src="/no-image.png" alt="Wytheville 2nd and 3rd Grade Flag Football" data-was-processed="true" loading="lazy"> 
1343 </div> 
1344 </div> 
1345 <span class="cus-videogallery-29-play position-absolute translate-middle top-50 start-50">
1346 <i class="fa fa-play fs-1 text-white text-shadow-1" aria-hidden="true"></i>
1347 </span> 
1348 <p class="cus-videogallery-29-heading mb-0 bg-dark text-white p-2 fs-6 lh-lg fw-bold" data-bs-toggle="tooltip" data-bs-placement="bottom" title="" data-bs-original-title="Wytheville 2nd and 3rd Grade Flag Football">
1349 <span class="line-1">Wytheville 2nd and 3rd Grade Flag Football</span>
1350 </p> 
1351 </a> 
1352 </div> 
1353 </div> 
1354 
1355 </div> 
1356</div> 
1357<style> 
1358@import url("https://fonts.googleapis.com/css2?family=PT+Sans:wght@700&display=swap");
1359
1360.cus-29-label {
1361 font-family: pt sans, sans-serif;
1362 border-bottom: 4px solid #bdcde3;
1363}
1364
1365.cus-29-label>span {
1366 position: relative;
1367}
1368
1369.cus-29-label>span:before {
1370 background: #dc3545;
1371 position: absolute;
1372 display: inline-block;
1373 content: "";
1374 width: 100%;
1375 height: 4px;
1376 bottom: -6px;
1377}
1378
1379.cus-videogallery-29-link:hover .cus-videogallery-29-play i {
1380 transform: scale(1.2);
1381 transition: 200ms linear;
1382}
1383
1384.cus-videogallery-29-play i {
1385 transition: 100ms linear;
1386} 
1387</style> 
1388<script> 
1389/* SubMenu Title */
1390if (ReadSubmenu() == 29) {
1391 document.title = "Swva Sun Video";
1392} /* If Full URL used instead of just ID*/
1393var vu = document.getElementsByClassName("cus-videogallery-29-img");
1394for (i = 0; i < vu.length; i++) {
1395 if (vu[i].src.split("http").length > 2) {
1396 /*Full URL*/
1397 var tu = vu[i].getAttribute("data-body");
1398 if (tu.indexOf("embed") != -1 && tu.indexOf("you") != -1) {
1399 /*YouTube*/
1400 tu = tu.split("/")[4].slice(0, 11);
1401 vu[i].src = `https://img.youtube.com/vi/${tu}/maxresdefault.jpg`;
1402 } else if (tu.indexOf("watch") != -1 && tu.indexOf("you") != -1) {
1403 /*YouTube*/
1404 var bx = new URL(tu);
1405 tu = bx.searchParams.get(`v`).slice(0, 11);
1406 vu[i].src = `https://img.youtube.com/vi/${tu}/maxresdefault.jpg`;
1407 } else if (tu.indexOf("vimeo") != -1 && tu.indexOf("player.vimeo") == -1) {
1408 /*Vimeo*/
1409 tu = tu.split("/")[3];
1410 vu[i].src = `https://vumbnail.com/${tu}.jpg`;
1411 } else {
1412 /*Other*/
1413 vu[i].src = vu[i].getAttribute("data-src");
1414 }
1415 }
1416}
1417
1418function OpenYouTube(id) {
1419 if (!document.getElementById('f_YT_button')) {
1420 if (id.indexOf("/") != -1) {
1421 /*Full URL*/
1422 if (id.indexOf("embed") != -1 && tu.indexOf("you") != -1) {
1423 /*Youtube Video*/
1424 id = id.split("/")[4].slice(0, 11);
1425 var msg = `<iframe id="f-mfp-iframe" class="f-mfp-iframe" allowfullscreen="" src="https://www.youtube.com/embed/${id}?autoplay=1" scrolling="no" frameborder="0"></iframe>`;
1426 } else if (id.indexOf("watch") != -1 && tu.indexOf("you") != -1) {
1427 /*Youtube Video*/
1428 var bx = new URL(id);
1429 id = bx.searchParams.get(`v`).slice(0, 11);
1430 var msg = `<iframe id="f-mfp-iframe" class="f-mfp-iframe" allowfullscreen="" src="https://www.youtube.com/embed/${id}?autoplay=1" scrolling="no" frameborder="0"></iframe>`;
1431 } else if (id.indexOf("vimeo") != -1 && id.indexOf("player.vimeo") == -1) {
1432 /*Vimeo Video*/
1433 id = id.split("/")[3];
1434 var msg = `<iframe id="f-mfp-iframe" class="f-mfp-iframe" allowfullscreen="" src="https://player.vimeo.com/video/${id}?autoplay=1" scrolling="no" frameborder="0"></iframe>`;
1435 } else {
1436 /*Other Video*/
1437 var msg = `<iframe id="f-mfp-iframe" class="f-mfp-iframe" allowfullscreen="" src="${id}?autoplay=1" scrolling="no" frameborder="0"></iframe>`;
1438 }
1439 } else {
1440 var msg = `<iframe id="f-mfp-iframe" class="f-mfp-iframe" allowfullscreen="" src="https://www.youtube.com/embed/${id}?autoplay=1" scrolling="no" frameborder="0"></iframe>`;
1441 }
1442 document.getElementsByTagName("body")[0].insertAdjacentHTML(`beforeend`, `<button id='f_YT_button' type='button' data-bs-toggle='modal' data-bs-target='#f_YT' style='display:none;'></button><div class='f_YT_box modal fade' id='f_YT' data-bs-backdrop='static' data-bs-keyboard='false' tabindex='-1' aria-labelledby='f_YTLabel' aria-hidden='true'><div class='modal-dialog modal-dialog-centered mx-auto'><div class='modal-content' id='YT_parent'><button type='button' class='f_YT_close btn-close position-absolute' data-bs-dismiss='modal' aria-label='Close'><svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='#fff' class='bi bi-x-lg' viewBox='0 0 16 16'><path d='M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8 2.146 2.854Z'/></svg></button><div class='modal-body text-center' style='white-space: pre-wrap;'><p id='f_loader'><i class='fa fa-spinner fa-spin' aria-hidden='true'></i></p>${msg}</div></div></div><style>#YT_parent{width:70vw;height:39.43vw}.modal-dialog{max-width:none;display:flex;align-items:center;justify-content:center}
1442.modal-dialog-scrollable .modal-content {max-height: none;overflow:visible;}.modal-body {padding:0;} .f_YT_close{right:-1rem;z-index:999;top:-1rem;background-color:red;border-radius:50px;padding:.5rem;opacity:1;box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.2);background-image:none;color:#fff;display:flex;justify-content:center;align-items:center;font-weight:700;font-size:18px}.f_YT_close:hover{color:#fff;background-color: #333;opacity:1} .f-mfp-iframe {width:100%;height:100%;}#f_loader{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%)}#f_loader i{font-size:4rem;color:red;opacity:0.8}@media (max-width:576px){#YT_parent{width:85vw;height:48vw}}</style></div>`);
1443 document.getElementById('f-mfp-iframe').onload = function () {
1444 document.getElementById("f_loader").remove();
1445 };
1446 document.getElementById('f_YT_button').click();
1447 document.getElementsByClassName('f_YT_close')[0].addEventListener('click', function () {
1448 document.getElementById('f_YT_button').remove();
1449 document.getElementsByClassName('f_YT_box')[0].remove();
1450 });
1451 }
1452} 
1453</script>
1453</div>
1454    <div data-cms-block="adgroup-11" data-cms-block-label="Advertisement 11" data-cms-el="adgroup" data-cms-adgroup="11">
1454<script type="text/javascript" language="JavaScript">AdGroup11();</script>
1454</div>
1455    <div data-cms-block="adgroup-28" data-cms-block-label="Advertisement 28" data-cms-el="adgroup" data-cms-adgroup="28">
1455<script type="text/javascript" language="JavaScript">AdGroup28();</script>
1455</div>
1456    <div data-cms-block="adgroup-13" data-cms-block-label="Advertisement 13" data-cms-el="adgroup" data-cms-adgroup="13">
1456<script type="text/javascript" language="JavaScript">AdGroup13();</script>
1456</div>
1457    </div>
1458</div>
1459
1460<footer class="row py-3 mt-3" style="border-top: 2px dotted #dedede; color: #000;">
1461    <div class="col-3 cus-social-links d-flex justify-content-left position-relative mb-2">
1462         <a class="text-decoration-none text-dark rounded ms-3 fs-4 d-inline-block lh-1 cus-social-1-133-icon" href="https://www.facebook.com/swvasun/" target="_blank"><i class="fa fa-facebook-square" aria-hidden="true"></i></a>
1463            <a class="text-decoration-none text-dark rounded ms-3 fs-4 d-inline-block lh-1 cus-social-1-133-icon" href="https://twitter.com/swvasun" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-twitter-x" viewBox="0 0 16 16" style="padding: 3px;background: #212529;fill: #fff;border-radius: 4px;display: inline-block;transform: translateY(-2px);" width="20" height="22"><path d="M12.6.75h2.454l-5.36 6.142L16 15.25h-4.937l-3.867-5.07-4.425 5.07H.316l5.733-6.57L0 .75h5.063l3.495 4.633L12.601.75Zm-.86 13.028h1.36L4.323 2.145H2.865z"></path></svg></a>
1464            <a class="text-decoration-none text-dark rounded ms-3 fs-4 d-inline-block lh-1 cus-social-1-133-icon" href="https://www.linkedin.com/company/swva-media-llc/" target="_blank"><i class="fa fa-linkedin-square" aria-hidden="true"></i></a>
1465            <a class="text-decoration-none text-dark rounded ms-3 fs-4 d-inline-block lh-1 cus-social-1-133-icon" href="https://youtube.com/@southwestvirginiasun8601" target="_blank"><i class="fa fa-youtube-play" aria-hidden="true"></i></a>
1466    </div>
1467    <div class="col-lg-8 col-12  position-relative mb-2">
1468<div class="col-12 ms-4">Swva Media is a proud member of the following organizations:</div>
1469<div class="col-12">               <div data-cms-block="adgroup-21" data-cms-block-label="Advertisement 21" data-cms-el="adgroup" data-cms-adgroup="21">
1469<script type="text/javascript" language="JavaScript">AdGroup21();</script>
1469</div></div>
1470    </div>
1471    <div class="col-12 d-flex justify-content-center position-relative mb-2">
1472        <div data-cms-block="section-4" data-cms-block-label="Section 4" data-cms-el="section" data-cms-section="4">
1472<script type="text/javascript">Section4();</script>
1472</div>      
1473    </div>
1474    <div class="col-12 d-flex justify-content-center position-relative mb-2">
1475<script type="text/javascript">Section5();</script>
1475
1476    </div>
1477</footer>
1478
1479
1480        </div>
1481    </div>
1482    <!-- go to top -->
1483    <div class="cus-scroll-top position-fixed end-0 bg-dark text-white fs-4 px-2" style="cursor: pointer;bottom: auto;transition: 400ms linear;" data-bs-toggle="tooltip" data-bs-placement="top" title="Go to top"><i class="fa fa-angle-up"></i></div>
1484    
1484<script>
1485        if (document.getElementsByClassName("cus-scroll-top")) {
1486            window.addEventListener("scroll", function() {
1487                if (document.body.scrollTop > 600 || document.documentElement.scrollTop > 600) {
1488                    document.getElementsByClassName('cus-scroll-top')[0].style.bottom = 0;
1489                } else {
1490                    document.getElementsByClassName('cus-scroll-top')[0].style.bottom = "-100px";
1491                }
1492            });
1493            document.getElementsByClassName('cus-scroll-top')[0].addEventListener('click', function() {
1494                window.scrollTo(0, 0);
1495            });
1496        }
1497    </script>
1497
1498    <!-- /go to top -->
1499    <div class="cus-g-analytics-133">
1499<script language="javascript">GetGoogleAnalytics('');</script>
1499</div>
1500    <!-- tooltip -->
1501    
1501<script>
1502        var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
1503        var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
1504            return new bootstrap.Tooltip(tooltipTriggerEl)
1505        });
1506    </script>
1506
1507    <!-- /tooltip -->
1508<script defer src="/sys-popup.js?v=1790187371"></script>
vendor: 1 bytes, line 1508
1508
1509<script>
1510// Form data restoration (runs on page load) - reads from COOKIE
1511(function() {
1512  console.log('[forms] Restoration script running...');
1513  console.log('[forms] Current cookies:', document.cookie);
1514  try {
1515    var cookies = document.cookie.split('; ');
1516    var pendingCookie = null;
1517    for (var i = 0; i < cookies.length; i++) {
1518      if (cookies[i].indexOf('_pendingFormData=') === 0) {
1519        pendingCookie = cookies[i];
1520        break;
1521      }
1522    }
1523    console.log('[forms] Found pending cookie:', !!pendingCookie);
1524    if (pendingCookie) {
1525      var cookieValue = pendingCookie.substring('_pendingFormData='.length);
1526      console.log('[forms] Cookie value length:', cookieValue.length);
1527      var formData = JSON.parse(decodeURIComponent(cookieValue));
1528      console.log('[forms] Parsed form data:', Object.keys(formData));
1529      var form = document.getElementById('CustomForm') || document.querySelector('form');
1530      console.log('[forms] Found form:', !!form, form ? form.id : 'no id');
1531      if (form) {
1532        Object.keys(formData).forEach(function(key) {
1533          var value = formData[key];
1534          if (value && typeof value === 'string') {
1535            var field = form.querySelector('[name="' + key + '"]');
1536            if (field) {
1537              console.log('[forms] Restoring field:', key, '=', value.substring(0, 20));
1538              if (field.tagName === 'SELECT') {
1539                field.value = value;
1540              } else if (field.type !== 'file' && field.type !== 'hidden') {
1541                field.value = value;
1542              }
1543              // Trigger change event for floating labels
1544              field.dispatchEvent(new Event('input', { bubbles: true }));
1545              field.dispatchEvent(new Event('change', { bubbles: true }));
1546            }
1547          }
1548        });
1549        console.log('[forms] Form data restored successfully');
1550      }
1551      // Clear the cookies
1552      document.cookie = '_pendingFormData=; path=/; max-age=0';
1553      document.cookie = '_pendingFormUrl=; path=/; max-age=0';
1554      console.log('[forms] Cookies cleared');
1555    }
1556  } catch (e) {
1557    console.error('[forms] Error restoring form data:', e);
1558  }
1559})();
1560</script>
vendor: 1 bytes, line 1560
1560
1561<script>
1562(function(){
1563  document.addEventListener('submit',function(e){
1564    var form=e.target;if(!form||form.tagName!=='FORM')return;
1565    var btn=form.querySelector('button[type="submit"],input[type="submit"],.survey-btn-submit,#SUBMIT1');
1566    if(!btn||btn.disabled)return;
1567    btn.disabled=true;
1568    if(btn.tagName==='INPUT'){btn.value='  Submitting...';}
1569    else{btn.innerHTML='<span class="form-submit-spinner"></span> Submitting...';}
1570  },true);
1571})();
1572</script>
1572</body>
1573<!--Template-130-->
1574
1575</html>

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