PageSourceSearch

https://www.stmartinschurch.gg/159/Welcome

html stmartinschurch.gg collected 2026-10-04 02:07:37 UTC 74,605 bytes, 1,308 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en-GB">
3<head>
4<!-- start of head-->
5    <title>About St Martin's Church Guernsey</title>
6	<link rel="shortcut icon" href="/_data/site/102/templates/current/favicon.ico" type="image/x-icon" />
7    <meta charset="utf-8">
8    <meta name="dcterms.rightsHolder" content="Khoo Systems Limited">
9    <meta name="dcterms.dateCopyrighted" content="2014">
10    <meta name="robots" content="all">
11    <meta name="description" content="We are a warm and friendly, active church and invite you to join us on our journey through faith discovering and worshipping God">
12    <meta name="csrf_token" content="stmartinsguernsey:0xB21492B7847B11F19A5EF23C9435931F:::1791079665:5ab7d6d4669d28a7da69c5fdd559a676">
13    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
14
15<meta name="viewport" content="width=device-width, initial-scale=1.0">
16<meta property="og:type" content="website">
17<meta property="og:site_name" content="St Martin's Parish Church Guernsey">
18<meta property="og:image" content="https://www.stmartinschurch.gg/cache/img/102/folder|159&sz500x500&cp&tn&ql&fm&bo&bc&sg05d861764a&ft1674495236&bannerWhatsOn.jpg">
19<meta property="og:title" content="About St Martin&#039;s Church Guernsey">
20<meta property="og:description" content="We are a warm and friendly, active church and invite you to join us on our journey through faith discovering and worshipping God">
21<meta property="og:url" content="https://www.stmartinschurch.gg/159/Welcome">
22<meta name="twitter:card" content="summary_large_image">
23<meta name="twitter:title" content="About St Martin&#039;s Church Guernsey">
24<meta name="twitter:description" content="We are a warm and friendly, active church and invite you to join us on our journey through faith discovering and worshipping God">
25<meta name="twitter:image" content="https://www.stmartinschurch.gg/cache/img/102/folder|159&sz500x500&cp&tn&ql&fm&bo&bc&sg05d861764a&ft1674495236&bannerWhatsOn.jpg">
26<script type='application/ld+json'>
27{"@type":"Organization","@context":"https://schema.org","@id":"https://www.stmartinschurch.gg/#organization","sameAs":["https://www.facebook.com/StMartinsGuernsey/","https://www.youtube.com/@stmartinschurchguernsey8426/videos"],"name":"St Martin's Parish Church Guernsey","url":"https://www.stmartinschurch.gg/","contactPoint":[{"@type":"ContactPoint","telephone":"+441481231197","contactType":"customer support"}],"logo":"https://www.stmartinschurch.gg/_data/site/102/templates/current/mobile_logo.svg"}
28</script>
28
29
30<script type='application/ld+json'>
31{"@type":"WebSite","@context":"https://schema.org","@id":"https://www.stmartinschurch.gg/#website","url":"https://www.stmartinschurch.gg/","name":"St Martin's Parish Church Guernsey","publisher":{"@id":"https://www.stmartinschurch.gg/#organization"}}
32</script>
32
33<!-- Start_Google_structured_data_breadcrumb -->
34<script type="application/ld+json">
35{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"https://www.stmartinschurch.gg/","name":"Home"}},{"@type":"ListItem","position":2,"item":{"@id":"https://www.stmartinschurch.gg/159/Welcome","name":"Welcome","image":"https://www.stmartinschurch.gg/cache/img/102/folder|159&sz200x200&cp&tn&ql&fm&bo&bc&sg4a2521b319&ft1674495236&bannerWhatsOn.jpg"}}]}
36</script>
36
37<!-- End_Google_structured_data_breadcrumb -->
38<link rel='canonical' href='https://www.stmartinschurch.gg/159/Welcome'>
39
40
41
42<link rel='stylesheet' type='text/css' href='/js/plugins/cookieconsent/dist/cookieconsent.css?v=5'>
43<link rel='stylesheet' type='text/css' href='/js/jquery-plugins/jquery-ui/jquery-ui.custom.css?v=5'>
44<link rel='stylesheet' type='text/css' href='https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.theme.css'>
45<link rel='stylesheet' type='text/css' href='https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css'>
46<link rel='stylesheet' type='text/css' href='https://cdn.jsdelivr.net/npm/shortcut-buttons-flatpickr/dist/themes/light.min.css'>
47<link rel='stylesheet' type='text/css' href='/css/default.css?v=5'>
48<link rel='stylesheet' type='text/css' href='/modules/folder/css/folder.css?v=5'>
49<link rel='stylesheet' type='text/css' href='/modules/contact/css/contact.css?v=5'>
50<link rel='stylesheet' type='text/css' href='/modules/news/css/news.css?v=5'>
51<link rel='stylesheet' type='text/css' href='/modules/pg/css/pg.css?v=5'>
52<link rel='stylesheet' type='text/css' href='/modules/search/css/search.css?v=5'>
53<link rel='stylesheet' type='text/css' href='/modules/user/css/user.css?v=5'>
54<link rel='stylesheet' type='text/css' href='/modules/attribute/css/attribute.css?v=5'>
55<link rel='stylesheet' type='text/css' href='/modules/calendar/css/calendar.css?v=5'>
56<link rel='stylesheet' type='text/css' href='/modules/directory/css/directory.css?v=5'>
57<link rel='stylesheet' type='text/css' href='/modules/crm/css/crm.css?v=5'>
58<link rel='stylesheet' type='text/css' href='/modules/sermon/css/sermon.css?v=5'>
59<link rel='stylesheet' type='text/css' href='/css/search-autocomplete.css?v=5'>
60<link rel='stylesheet' type='text/css' href='/js/plugins/swiper/swiper-bundle.min.css?v=5'>
61
62<link rel='stylesheet' type='text/css' href='/cache/css/102/modern-form.css?v=5.1781178477'>
63<link rel='stylesheet' type='text/css' href='/cache/css/102/responsive.css?v=5.1781178477'>
64<link rel='stylesheet' type='text/css' href='/cache/css/102/menu.css?v=5.1781178477'>
65<link rel='stylesheet' type='text/css' href='/cache/css/102/ecampaign.css?v=5.1781178477'>
66<link rel='stylesheet' type='text/css' href='/cache/css/102/calevent.css?v=5.1781178477'>
67<link rel='stylesheet' type='text/css' href='/cache/css/102/template_fonts.css?v=5.1781178477'>
68<link rel='stylesheet' type='text/css' href='/cache/css/102/template.css?v=5.1781178477'>
69<link rel='stylesheet' type='text/css' href='/cache/css/102/template_custom.css?v=5.1781178477'>
70
71
72<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js'></script>
vendor: 1 bytes, line 72
72
73<script defer src='https://cdn.jsdelivr.net/npm/[email protected]/dist/purify.min.js'></script>
vendor: 1 bytes, line 73
73
74<script defer src='https://unpkg.com/[email protected]/dist/htmx.min.js'></script>
vendor: 1 bytes, line 74
74
75<script defer src='https://cdn.jsdelivr.net/npm/@alpinejs/[email protected]/dist/cdn.min.js'></script>
vendor: 1 bytes, line 75
75
76<script defer src='https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js'></script>
vendor: 1 bytes, line 76
76
77<script defer src='https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js'></script>
vendor: 1 bytes, line 77
77
78<script defer src='https://cdn.jsdelivr.net/npm/tippy.js@6/dist/tippy-bundle.umd.js'></script>
78
79<link rel="stylesheet" href="https://use.typekit.net/vdl8nqz.css">
80<script src='https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js'></script>
vendor: 1 bytes, line 80
80
81<script defer src='/js/jquery-plugins/jquery.validate/jquery.validate.min.js?v=5'></script>
vendor: 1 bytes, line 81
81
82<script defer src='/js/jquery-plugins/jquery.validate/additional/creditcard.js?v=5'></script>
vendor: 1 bytes, line 82
82
83<script defer src='/js/component/matchHeight.js?v=5'></script>
vendor: 1 bytes, line 83
83
84<script defer src='/js/plugins/headroom/headroom.min.js?v=5'></script>
vendor: 1 bytes, line 84
84
85<script defer src='/js/plugins/lazyload/lazyload.js?v=5'></script>
vendor: 1 bytes, line 85
85
86<script>
87document.addEventListener("DOMContentLoaded",function(){lazyload();});
88</script>
vendor: 1 bytes, line 88
88
89<script defer src='https://cdn.jsdelivr.net/npm/flatpickr'></script>
vendor: 1 bytes, line 89
89
90<script defer src='https://cdn.jsdelivr.net/npm/shortcut-buttons-flatpickr/dist/shortcut-buttons-flatpickr.min.js'></script>
vendor: 1 bytes, line 90
90
91<script defer src='/js/plugins/base64.js?v=5'></script>
vendor: 1 bytes, line 91
91
92<script src='/js/default.js?v=5'></script>
vendor: 1 bytes, line 92
92
93<script defer src='/modules/menu/js/menu_h.js?v=5'></script>
vendor: 1 bytes, line 93
93
94<script defer src='/modules/menu/js/menu_hamb.js?v=5'></script>
vendor: 1 bytes, line 94
94
95<script defer src='/modules/menu/js/menu_mega.js?v=5'></script>
vendor: 1 bytes, line 95
95
96<script defer src='/js/component/searchAutocomplete.js?v=5'></script>
vendor: 1 bytes, line 96
96
97<script defer src='/js/plugins/swiper/swiper-bundle.min.js?v=5'></script>
vendor: 1 bytes, line 97
97
98<script defer src='/js/component/swiperLegacyAlias.js?v=5'></script>
98
99
100
101
102<link rel='alternate' type='application/rss+xml' href='/159/xml/' title='RSS feed'>
103<link rel='alternate' type='application/rss+xml' href='/159/podfeed/' title='Podcast feed'>
104<!--end of head -->
105</head>
106<body class="body-template body-159 _body-159 body-folder body-layout-preset-848 browser-chrome engine-webkit majorver-128 ver-128.0.0.0 is-desktop geo-CA  body-menu-hamb body-menu-hamb-left body-menu-hamb-slide   ">
107	
108		<div id="q-app">
109		<div id="shim"></div>
110		<div id='menu-hamb' class='left slide'><a id='menu-icon-close'>		<svg style="fill:#fff" version="1.1" id="hamb-icon" class="hamb-icon-svg" 
111			xmlns="http://www.w3.org/2000/svg" 
112			xmlns:xlink="http://www.w3.org/1999/xlink" 
113			x="0px" y="0px" width="45px" height="45px" 
114			viewBox="0 0 128 128" 
115			enable-background="new 0 0 128 128" xml:space="preserve">
116		<g>
117			<rect style="fill:#5CA4A9;" id="hamb-icon-close-box" x="0" y="0" width="128" height="128"/>
118			<g transform="matrix(0.5, 0, 0, 0.5, 32,32)">
119				<g id="close" transform="translate(18.000000, 18.000000)">
120					<path d="M92,9.19938255 L82.8006174,0 L46,36.8006174 L9.19938255,0 L0,9.19938255 L36.8006174,46 L0,82.8006174 L9.19938255,92 L46,55.1993826 L82.8006174,92 L92,82.8006174 L55.1993826,46 L92,9.19938255 Z" id="Shape"></path>
121				</g>
122			</g>
123		</g>
124		</svg></a><ul class=' n0'><li class='n159 selected open show-items folder' data-nav-id='159'><div><a class='' href='/159/Welcome' >Welcome</a><a class='menu-branch ' href='/159/ajax/menu_branch.php' rel='nofollow'>&nbsp;</a></div><ul class=' n159'><li class='n91 hide-items folder' data-nav-id='91'><div><a class='' href='/91/Our-Team' >Who's Who</a></div></li><li class='n1 contact' data-nav-id='1'><div><a class='' href='/1/Im-New' >I'm New!</a></div></li><li class='n501 pg' data-nav-id='501'><div><a class='' href='/501/Our-Vision' >Our Vision</a></div></li><li class='n601 hide-items folder' data-nav-id='601'><div><a class='' href='/601/Life-Events' >Life Events</a></div></li></ul></li><li class='n13 show-items folder' data-nav-id='13'><div><a class=' ajax-branch' href='/13/Whats-On' >What's On</a><a class='menu-branch ajax' href='/13/ajax/menu_branch.php' rel='nofollow'>&nbsp;</a></div></li><li class='n350 show-items folder' data-nav-id='350'><div><a class=' ajax-branch' href='/350/Community' >Community</a><a class='menu-branch ajax' href='/350/ajax/menu_branch.php' rel='nofollow'>&nbsp;</a></div></li><li class='n602 show-items folder' data-nav-id='602'><div><a class=' ajax-branch' href='/602/Safeguarding' >Safeguarding</a><a class='menu-branch ajax' href='/602/ajax/menu_branch.php' rel='nofollow'>&nbsp;</a></div></li><li class='n422 show-items folder' data-nav-id='422'><div><a class=' ajax-branch' href='/422/Resources' >Resources</a><a class='menu-branch ajax' href='/422/ajax/menu_branch.php' rel='nofollow'>&nbsp;</a></div></li><li class='n359 pg' data-nav-id='359'><div><a class='' href='/359/Give' >Give</a></div></li><li class='n311 hide-items folder' data-nav-id='311'><div><a class='' href='/311/Contact-Us' >Contact</a></div></li></ul></div>
125		<div id="header-fixed" class="header--fixed">
126				  	
126<script>
127	  		$(function(){
128				var header = document.querySelector("#header-fixed");
129				if (window.location.hash) {
130				  header.classList.add("slide--up");
131				}
132				var headroom = new Headroom(header, {
133					tolerance: {
134					  down : 10,
135					  up : 20
136					},
137					offset: 100
138				}).init();
139	  		})
140	  	</script>
140	  	<header id="header-mob">
141	  		<div id="header-mob-box">
142	  			<a id="header-mob-logo"  href="/"></a>
143				<div id="mobile-actions">
144	
145	
146	<svg id='mobile-search-icon' viewBox='0 0 100 100' x-data @click="$dispatch('toggle-mobile-search')"><path d='M94.5,88.6L76.8,70.9c6.6-7.4,10.7-17.2,10.7-27.9c0-23.1-18.8-41.9-41.9-41.9S3.7,19.9,3.7,43s18.8,41.9,41.9,41.9c8.2,0,15.8-2.4,22.2-6.4l18.4,18.4c1.1,1.1,2.6,1.7,4.1,1.7c1.5,0,3-0.6,4.1-1.7C96.8,94.6,96.8,90.9,94.5,88.6z M15.4,43c0-16.6,13.5-30.2,30.2-30.2S75.7,26.4,75.7,43S62.2,73.2,45.6,73.2S15.4,59.7,15.4,43z'/></svg>
147</div>
148<div id="mobile-search"
149     x-data="{ open: false }"
150     :class="{ 'is-open': open }"
151     @toggle-mobile-search.window="open = !open">
152	<form action="/search/index.htm" method="post">
153		<img id="icon-search" src="/media/icons/search.svg" alt="" width="18" height="18">
154		<input name="p_search"
155		       value=""
156		       type="text"
157		       placeholder="Search"
158		       autocomplete="off"
159		       data-autocomplete-url="/search/ajax/search_select.php"
160		       data-autocomplete-min="3"
161		       data-autocomplete-delay="100" />
162		<img id="close-search" src="/media/icons/close.svg" alt="Close" width="18" height="18" @click="open = false">
163	</form>
164</div>
165
166<div id='menu-icon-wrapper'><div id='menu-icon'>		<svg style="fill:#fff" version="1.1" id="hamb-icon" class="hamb-icon-svg" 
167			xmlns="http://www.w3.org/2000/svg" 
168			xmlns:xlink="http://www.w3.org/1999/xlink" 
169			x="0px" y="0px" width="45px" height="45px" 
170			viewBox="0 0 128 128" 
171			enable-background="new 0 0 128 128" xml:space="preserve">
172		<g>
173			<rect style="fill:#5CA4A9;" id="hamb-icon-box" x="0" y="0" width="128" height="128"/>
174			<g transform="matrix(0.6, 0, 0, 0.6, 25.6,25.6)">
175				<rect x="24" y="60" width="80" height="8"/>
176				<rect x="24" y="36" width="80" height="8"/>
177				<rect x="24" y="84" width="80" height="8"/>
178			</g>
179		</g>
180		</svg></div></div>
181			</div>
182			<div id='mobile-trolley-notification'></div>
183		</header>
184		</div>
185
186		<div id="template-content" class="template template-box">
187			<a id="staging-box" href="/_default/staging.htm">You are viewing this site in staging mode. Click in this bar to return to normal site.</a>
188			<div id="svg-icons"><!-- --></div>
189			<div id="notify-container"><div id="basic-template"><a class="ui-notify-close ui-notify-cross" href="#">x</a>#{notify_img}<div class="notify-title">#{notify_title}</div><div class="notify-text">#{notify_text}</div></div></div>
190			<div class="modal-dlg" id="modal-dlg"><!-- --></div>
191			<div class="modal-dlg" id="modal-dlg1"><!-- --></div>
192			<div class="modal-dlg" id="modal-dlg2"><!-- --></div>
193
194			<div id="browser-box">
195				<div id="page-box">
196					<div id="header-nav">
197						  			<header id="header">
198				<div id="header-inner" class="section-inner"><!-- -->
199				<div id="header-box">
200					<div class="header-logo"><a href="/"><img src="/_data/site/102/_default/stMartinsLogoDark2.png" alt="St Martins Church" width="400" height="109" /></a></div>
201<div class="header-search"><form id="search" class="search" action="/search/index.htm" method="post">
202	<input id="p-search"
203	       name="p_search"
204	       value=""
205	       type="text"
206	       placeholder="Search"
207	       autocomplete="off"
208	       data-autocomplete-url="/search/ajax/search_select.php"
209	       data-autocomplete-min="3"
210	       data-autocomplete-delay="100" />
211</form>
212</div>
213				</div>
214				</div>
215			</header>
216										<nav id="h-nav">
217					<div id="h-nav-inner" class="section-inner">
218						<div id="h-nav-box">
219							<div id='menu-h'><ul class=' n0'><li class='n159 selected open show-items folder' data-nav-id='159'><div><a class='' href='/159/Welcome' >Welcome</a><a class='menu-branch ' href='/159/ajax/menu_branch.php' rel='nofollow'>&nbsp;</a></div><ul class=' n159'><li class='n91 hide-items folder' data-nav-id='91'><div><a class='' href='/91/Our-Team' >Who's Who</a></div></li><li class='n1 contact' data-nav-id='1'><div><a class='' href='/1/Im-New' >I'm New!</a></div></li><li class='n501 pg' data-nav-id='501'><div><a class='' href='/501/Our-Vision' >Our Vision</a></div></li><li class='n601 hide-items folder' data-nav-id='601'><div><a class='' href='/601/Life-Events' >Life Events</a></div></li></ul></li><li class='n13 show-items folder' data-nav-id='13'><div><a class=' ajax-branch' href='/13/Whats-On' >What's On</a><a class='menu-branch ajax' href='/13/ajax/menu_branch.php' rel='nofollow'>&nbsp;</a></div></li><li class='n350 show-items folder' data-nav-id='350'><div><a class=' ajax-branch' href='/350/Community' >Community</a><a class='menu-branch ajax' href='/350/ajax/menu_branch.php' rel='nofollow'>&nbsp;</a></div></li><li class='n602 show-items folder' data-nav-id='602'><div><a class=' ajax-branch' href='/602/Safeguarding' >Safeguarding</a><a class='menu-branch ajax' href='/602/ajax/menu_branch.php' rel='nofollow'>&nbsp;</a></div></li><li class='n422 show-items folder' data-nav-id='422'><div><a class=' ajax-branch' href='/422/Resources' >Resources</a><a class='menu-branch ajax' href='/422/ajax/menu_branch.php' rel='nofollow'>&nbsp;</a></div></li><li class='n359 pg' data-nav-id='359'><div><a class='' href='/359/Give' >Give</a></div></li><li class='n311 hide-items folder' data-nav-id='311'><div><a class='' href='/311/Contact-Us' >Contact</a></div></li></ul></div>
220						</div>
221					</div>
222				</nav>
223				
224						
225					</div>
226								<section id="body-header"><!--  -->
227				<div id="body-header-inner"  class="section-inner" ><!--  -->
228					<div id="body-header-box">
229					<div><div class='pg-gallery-fw'><div id="gallery-fw-wrapper-159" class='gallery-fw-wrapper-preset-493 breakout'>
230	
231	<div id="gallery-fw-159" class='swiper slick-slider cycle-slideshow gallery-fw-preset-493'>
232		<div class='swiper-wrapper slick-list slick-track'>
233			<div class='swiper-slide slick-slide cycle-slide im-wrapper ' style='background-image:url(/cache/img/102/folder|159&sz3000x3000&cp&tn&ql&fm&bo&bc&sg39b3ec2a71&ft1674495236&bannerWhatsOn.jpg)' data-title='' data-description='&lt;h1 class=&#039;pg-title&#039;&gt;Welcome&lt;/h1&gt;' data-link=""></div>
234		</div>
235		
236		
237	</div>
238	
239</div>
240
241<style>
242	.gallery-fw-wrapper-preset-493 {min-height:500px}
243.gallery-fw-wrapper-preset-493 .im-wrapper {background-size:cover;background-position:center center;width:100%;height:500px}
244#gallery-fw-159 {width:100%;min-width:0px;max-width:100%;overflow:hidden}
245.gallery-fw-wrapper-preset-493 .swiper-button-prev {background:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='15 6 9 12 15 18'/></svg>") no-repeat scroll 20% 50% rgba(0, 0, 0, 0);background-size:30px 30px;left:0px;margin-top:0px}
246.gallery-fw-wrapper-preset-493 .swiper-button-next {background:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='9 6 15 12 9 18'/></svg>") no-repeat scroll 80% 50% rgba(0, 0, 0, 0);background-size:30px 30px;right:0px;margin-top:0px}
247.gallery-fw-wrapper-preset-493 .swiper-button-prev:hover, #gallery-fw-159 .swiper-button-next:hover {opacity:1}
248.gallery-fw-wrapper-preset-493 .swiper-button-prev, #gallery-fw-159 .swiper-button-next {cursor:pointer;position:absolute;height:100%;opacity:0;top:0px;width:20%;z-index:800}
249.gallery-fw-wrapper-preset-493 .swiper-pagination {text-align:center;position:absolute;overflow:hidden;z-index:500}
250.gallery-fw-wrapper-preset-493 .swiper-pagination-bullet {font-family:arial;font-size:50px;width:16px;height:16px;display:inline-block;color:#ddd;cursor:pointer}
251.gallery-fw-wrapper-preset-493 .swiper-pagination-bullet-active {color:#D69746}
252.gallery-fw-wrapper-preset-493 .gallery-fw-caption {pointer-events:none}
253.gallery-fw-wrapper-preset-493 .gallery-fw-caption .caption-description {pointer-events:none;background-color:rgba(78,81,102,0.75);color:#ffffff;height:100%}
254.gallery-fw-wrapper-preset-493 .im-wrapper.video {overflow:hidden}
255.gallery-fw-wrapper-preset-493 .im-wrapper.video video {width:100%;object-fit:cover;min-height:500px;max-height:500px}
256@media only screen and (max-width: 767px ) {
257#gallery-fw-wrapper-159 {min-height:500px}
258.gallery-fw-wrapper-preset-493 .im-wrapper {height:500px}
259.gallery-fw-wrapper-preset-493 .im-wrapper.video video {object-fit:cover;min-height:500px;max-height:500px}
260}
261
262</style>
263<script>
264(function(){
265	var navId = 159;
266	function init() {
267		var root = document.getElementById('gallery-fw-' + navId);
268		if (!root || typeof Swiper === 'undefined') return;
269
270		var swiper = new Swiper(root, {"speed":1000,"effect":"fade","direction":"horizontal","fadeEffect":{"crossFade":true},"autoplay":false});
271		if (typeof window.swiperLegacyAlias === 'function') window.swiperLegacyAlias(swiper);
272
273		/* Swiper auto-attaches click listeners to navigation buttons,
274		 * but on opacity:0 buttons inside our breakout/table-cell
275		 * layout those clicks were observed not firing. Bind
276		 * explicit handlers as a guarantee. Calling slideNext/Prev
277		 * while a transition is in flight is safe (Swiper's own
278		 * isLocked/animating guards handle the no-op). */
279		var prevBtn = root.querySelector('.swiper-button-prev');
280		var nextBtn = root.querySelector('.swiper-button-next');
281		if (prevBtn) prevBtn.addEventListener('click', function(e){ e.stopPropagation(); swiper.slidePrev(); });
282		if (nextBtn) nextBtn.addEventListener('click', function(e){ e.stopPropagation(); swiper.slideNext(); });
283
284		var captionEnabled = (0 === 1);
285		if (captionEnabled) {
286			var caption = root.parentNode.querySelector('.gallery-fw-caption .caption-description');
287			if (caption) {
288				/* The first slide's caption is rendered server-side (FwImage.php),
289				 * so interactive content in it (e.g. a lightbox CTA) is present at
290				 * parse time and bound by page-level init like any static markup —
291				 * no initial JS injection here. On slide change we swap in the new
292				 * slide's caption from data-description and fire a bubbling
293				 * 'gallery:caption-changed' event so any plugin that binds to
294				 * injected markup can rescan. The widget stays framework-agnostic
295				 * and never hard-codes a plugin selector. */
296				var setCaption = function(s) {
297					var slides = root.querySelectorAll('.swiper-slide:not(.swiper-slide-duplicate)');
298					var active = slides[s.realIndex];
299					caption.innerHTML = (active && active.dataset.description) ? active.dataset.description : '';
300					caption.dispatchEvent(new CustomEvent('gallery:caption-changed', { bubbles: true, detail: { slide: s.realIndex } }));
301				};
302				swiper.on('slideChangeTransitionStart', setCaption);
303			}
304		}
305
306		root.querySelectorAll('video.bgvid').forEach(function(v){
307			var p = v.play();
308			if (p && p.catch) p.catch(function(){});
309		});
310
311		root.addEventListener('click', function(e){
312			if (e.target.closest('.swiper-button-prev, .swiper-button-next, .swiper-pagination')) return;
313			var link = e.target.closest('.linkable');
314			if (!link) return;
315			var href = link.dataset.link;
316			if (href) window.location = href;
317		});
318	}
319
320	if (document.readyState === 'loading') {
321		document.addEventListener('DOMContentLoaded', init);
322	} else {
323		init();
324	}
325})();
326</script>
326
327</div></div>
328					</div>
329				</div>
330			</section>
331
332					<main id="main-content">
333						<div id="main-content-inner" class="section-inner">
334							<div id="main-content-box">
335								
336												<div id="c-col" class='no-lc no-rc '>
337					<div id="c-col-inner">
338					<div id="c-col-box">
339									<div id="content-header"><!--  -->
340				
341			</div>
342				
343						<div id="content-box" class="layout-0xDCE258AC847B11F19A5EF23C9435931F">
344							<!-- content -->
345<div class="item-folder ">
346<h1 class='pg-title'>Welcome</h1>
347
348
349<div class='pg-gallery-av'></div>
350<div class='pg-body'><p>We would like to welcome you to St Martin&rsquo;s Parish Church. We are a warm and friendly, active church and invite you to join us on our journey through faith discovering and worshiping God</p>
351<p>&nbsp;</p></div>
352<div class='pg-folder '>
352<script>
353function ajax_load_more(scroll){
354	$.ajax({
355		url: '/159/ajax/folder_page.php',
356		data: {'idx': '0xB21492B7847B11F19A5EF23C9435931F', 'npage': npage },
357		type: "GET",
358		dataType: "html",
359		success: function(data) {
360			if (data) { scroll.replaceWith(data);}
361			lazyload();
362			if (typeof folder_tile_match_height === 'function'){
363				folder_tile_match_height();
364			}
365		}
366	})
367}
368</script>
368
369
370<!-- BEGIN scriptBlock -->
371<script>
372	$(function(){
373		
374	})
375</script>
375
376<!-- END scriptBlock -->
377
378<style>
379.folder-preset-563 .folder-view li .caption {color:#fff;min-height:100%}
380.folder-preset-563 .folder-view li .caption h3 {color:#fff}
381.folder-preset-563 .folder-view li .caption.over.top {top:0px}
382.folder-preset-563 .folder-view li .caption.over.bottom {bottom:0px}
383.folder-preset-563 .folder-view li .caption.fade {visibility:hidden;opacity:0;-webkit-transition:-webkit-visibility 0s linear 0.5s,opacity 0.5s linear;-moz-transition:-moz-visibility 0s linear 0.5s,opacity 0.5s linear;transition:visibility 0s linear 0.5s,opacity 0.5s linear}
384.folder-preset-563 .folder-view li:hover .caption.fade {visibility:visible;opacity:1;transition-delay:0s}
385.folder-preset-563 .folder-view li:hover .caption.over.slide,
386					  .folder-preset-563 .folder-view li.mobi-clicked .caption {height:100%}
387.mobile .folder-preset-563 .folder-view li:hover .caption.over.slide {height:0px}
388.mobile .folder-preset-563 .folder-view li.mobi-clicked .caption,
389					  .mobile .folder-preset-563 .folder-view li.mobi-clicked .caption.over.slide {height:100%}
390.folder-preset-563 .folder-tile {margin-right:-50px}
391.folder-preset-563 .folder-tile .img-block {border-style:solid}
392.folder-preset-563 .folder-tile li {width:100%;padding-right:50px}
393.folder-preset-563 .folder-tile li.item::after {content:"";display:block;height:50px;width:100%}
394
395@media screen and (min-width: 670px) {
396.folder-preset-563 .folder-tile li {width:50%}
397.folder-preset-563 .folder-tile li:nth-of-type(1n+1) {clear:none}
398.folder-preset-563 .folder-tile li:nth-of-type(2n+1) {clear:both}
399}
400@media screen and (min-width: 768px) {
401.folder-preset-563 .folder-tile li {width:33.333333333333%}
402.folder-preset-563 .folder-tile li:nth-of-type(2n+1) {clear:none}
403.folder-preset-563 .folder-tile li:nth-of-type(3n+1) {clear:both}
404}
405
406.folder-preset-563 .folder-list li .column-image {order:0}
407.folder-preset-563 .folder-list li.img-inline .img-wrapper {margin-bottom:0px}
408.folder-preset-563 .folder-list .column-image.hidden {display:none}
409.folder-preset-563 .folder-list li div.img-block {border-style:solid}
410.folder-preset-563 .folder-list li.img-inline div.img-block {width:100%;border-style:solid}
411.folder-preset-563 .folder-list li.img-inline div.img-block img {max-height:0px}
412.folder-preset-563 .folder-list li.item::after {height:0px}
413
414
415@media screen and (max-width: 767px) {
416.folder-preset-563 .folder-list li div.img-block {width:100%;height:auto}
417.folder-preset-563 .folder-list li div.img-block .img {background-size:contain}
418}
419
420</style>
421
422<div class="folder-wrapper folder-wrapper-159  folder-preset-563 ">
423<div class="folder-options ">
424
425
426</div>
427<ul class="folder-view folder-folder folder-tile ">
428	<li id="nav_91" class="item item-91 item-folder img-use-crop  item-idx-0xB214A404847B11F19A5EF23C9435931F" data-idx="0xB214A404847B11F19A5EF23C9435931F">
429		
430<div class="fg-row">
431	<div class="columns medium-3 large-3 column-image ">
432		<!-- BEGIN imgBlock -->
433		<div class="img-wrapper">
434			<div class="img-block"  href="/lib/static/img.php?im=/_data/site/102/folder/91/bannerTeam.jpg&sz=1500x1500&cp=1">
435	<a class="pg-link" href="/91/Our-Team" >
436	<div class="img img-alt   lazyload" style="" data-src=""></div>
437	<div class="img img-main  lazyload" style="background-image:url(&apos;/_.gif&apos;);" data-src="/cache/img/102/folder|91&sz520x480&cp1&tn&ql&fm&bo&bc&sgb69affd8af&ft1671464957&bannerTeam.jpg"></div>
438	<img src='/cache/img/102/folder|91&sz520x480&cp1&tn&ql1&fmjpg&bo&bc&sgb69affd8af&ft1671464957&bannerTeam.jpg' alt='Meet the Team'>
439	</a>
440</div>
441			<div class="caption over top" onclick="window.location.href='/91/Our-Team'">
442				<div class="caption-text-wrapper">
443					<a class='pg-title-link pg-link' href='/91/Our-Team' ><h3 class='pg-title-list'>Meet the Team</h3></a>
444					
445					<a href="/91/Our-Team">Find out more</a>
446				</div>
447			</div>
448		</div>
449		<!-- END imgBlock -->
450	</div>
451	<div class="folder-details columns medium-9 large-9 column-body">
452		<a class='pg-title-link pg-link' href='/91/Our-Team' ><h3 class='pg-title-list'>Meet the Team</h3></a>
453		
454		
455	</div>
456</div>
457	</li>
458	<li id="nav_1" class="item item-1 item-contact img-use-crop  item-idx-0xB214D8AF847B11F19A5EF23C9435931F" data-idx="0xB214D8AF847B11F19A5EF23C9435931F">
459		
460<div class="fg-row">
461	<div class="columns medium-3 large-3 column-image ">
462		<!-- BEGIN imgBlock -->
463		<div class="img-wrapper">
464			<div class="img-block"  href="/lib/static/img.php?im=/_data/site/102/contact/1/bannerNew.jpg&sz=1500x1500&cp=1">
465	<a class="pg-link" href="/1/Im-New" >
466	<div class="img img-alt   lazyload" style="" data-src=""></div>
467	<div class="img img-main  lazyload" style="background-image:url(&apos;/_.gif&apos;);" data-src="/cache/img/102/contact|1&sz520x480&cp1&tn&ql&fm&bo&bc&sg73d6e27f98&ft1674144086&bannerNew.jpg"></div>
468	<img src='/cache/img/102/contact|1&sz520x480&cp1&tn&ql1&fmjpg&bo&bc&sg73d6e27f98&ft1674144086&bannerNew.jpg' alt='I&#039;m New!'>
469	</a>
470</div>
471			<div class="caption over top" onclick="window.location.href='/1/Im-New'">
472				<div class="caption-text-wrapper">
473					<a class='pg-title-link pg-link' href='/1/Im-New' ><h3 class='pg-title-list'>I'm New!</h3></a>
474					
475					<a href="/1/Im-New">Find out more</a>
476				</div>
477			</div>
478		</div>
479		<!-- END imgBlock -->
480	</div>
481	<div class="folder-details columns medium-9 large-9 column-body">
482		<a class='pg-title-link pg-link' href='/1/Im-New' ><h3 class='pg-title-list'>I'm New!</h3></a>
483		
484		
485	</div>
486</div>
487	</li>
488	<li id="nav_501" class="item item-501 item-pg img-use-crop  item-idx-0xB214A5FC847B11F19A5EF23C9435931F" data-idx="0xB214A5FC847B11F19A5EF23C9435931F">
489		
490<div class="fg-row">
491	<div class="columns medium-3 large-3 column-image ">
492		<!-- BEGIN imgBlock -->
493		<div class="img-wrapper">
494			<div class="img-block"  href="/lib/static/img.php?im=/_data/site/102/pg/501/bannerBoat2.jpg&sz=1500x1500&cp=1">
495	<a class="pg-link" href="/501/Our-Vision" >
496	<div class="img img-alt   lazyload" style="" data-src=""></div>
497	<div class="img img-main  lazyload" style="background-image:url(&apos;/_.gif&apos;);" data-src="/cache/img/102/pg|501&sz520x480&cp1&tn&ql&fm&bo&bc&sg06b28b7c20&ft16741451
49750&bannerBoat2.jpg"></div>
498	<img src='/cache/img/102/pg|501&sz520x480&cp1&tn&ql1&fmjpg&bo&bc&sg06b28b7c20&ft1674145150&bannerBoat2.jpg' alt='Our Vision'>
499	</a>
500</div>
501			<div class="caption over top" onclick="window.location.href='/501/Our-Vision'">
502				<div class="caption-text-wrapper">
503					<a class='pg-title-link pg-link' href='/501/Our-Vision' ><h3 class='pg-title-list'>Our Vision</h3></a>
504					
505					<a href="/501/Our-Vision">Find out more</a>
506				</div>
507			</div>
508		</div>
509		<!-- END imgBlock -->
510	</div>
511	<div class="folder-details columns medium-9 large-9 column-body">
512		<a class='pg-title-link pg-link' href='/501/Our-Vision' ><h3 class='pg-title-list'>Our Vision</h3></a>
513		
514		
515	</div>
516</div>
517	</li>
518	<li id="nav_601" class="item item-601 item-folder img-use-crop  item-idx-0xB21505DB847B11F19A5EF23C9435931F" data-idx="0xB21505DB847B11F19A5EF23C9435931F">
519		
520<div class="fg-row">
521	<div class="columns medium-3 large-3 column-image ">
522		<!-- BEGIN imgBlock -->
523		<div class="img-wrapper">
524			<div class="img-block"  href="/lib/static/img.php?im=/_data/site/102/folder/601/bannerLifeEvents.jpg&sz=1500x1500&cp=1">
525	<a class="pg-link" href="/601/Life-Events" >
526	<div class="img img-alt   lazyload" style="" data-src=""></div>
527	<div class="img img-main  lazyload" style="background-image:url(&apos;/_.gif&apos;);" data-src="/cache/img/102/folder|601&sz520x480&cp1&tn&ql&fm&bo&bc&sgadfd90ac7c&ft1674471147&bannerLifeEvents.jpg"></div>
528	<img src='/cache/img/102/folder|601&sz520x480&cp1&tn&ql1&fmjpg&bo&bc&sgadfd90ac7c&ft1674471147&bannerLifeEvents.jpg' alt='Life Events'>
529	</a>
530</div>
531			<div class="caption over top" onclick="window.location.href='/601/Life-Events'">
532				<div class="caption-text-wrapper">
533					<a class='pg-title-link pg-link' href='/601/Life-Events' ><h3 class='pg-title-list'>Life Events</h3></a>
534					
535					<a href="/601/Life-Events">Find out more</a>
536				</div>
537			</div>
538		</div>
539		<!-- END imgBlock -->
540	</div>
541	<div class="folder-details columns medium-9 large-9 column-body">
542		<a class='pg-title-link pg-link' href='/601/Life-Events' ><h3 class='pg-title-list'>Life Events</h3></a>
543		
544		
545	</div>
546</div>
547	</li>
548
549</ul>
550</div>
551</div>
552</div>
553<!-- end of content -->
554
555						</div>
556				
557									<div id="content-footer"><!--  -->
558				
559			</div>
560					</div>
561					</div>
562				</div>
563								
564							</div>
565						</div>
566					</main>
567								<section id="body-footer">
568				<div id="body-footer-inner"  class="section-inner" ><!--  -->
569					<div id="body-footer-box">
570					
571					</div>
572				</div>
573			</section>
574								<footer id="footer">
575				<div id="footer-inner" class="section-inner">
576					<div id="footer-box">
577						<div class="block-fw footer-bg">
578<div class="section-inner">
579<div class="section-upper">
580<div class="footer-logo"><a href="/"><img src="/_data/site/102/_default/stMartinsWhite2.png" alt="St Martin's Church Guernsey Logo" /></a></div>
581</div>
582<p class="social-icons"><a title="facebook" href="https://www.facebook.com/StMartinsGuernsey/" target="_blank" rel="noopener noreferrer"></a> <a title="youtube" href="https://www.youtube.com/@stmartinschurchguernsey8426" target="_blank" rel="noopener noreferrer"></a></p>
583<p style="text-align: center;"><a href="/649/Vacancies">Vacancies</a> | <a href="/639/Contact-Us">Contact</a> | <a href="/603/Safeguarding">Safeguarding</a> | <a href="/634/Privacy-Policy">Privacy</a> | <a href="/8/Sitemap">Sitemap</a></p>
584<hr />
585<div class="section-lower"><h6>Copyright &copy; 
585<script type="text/javascript">// <![CDATA[
586document.write(new Date().getFullYear());
587// ]]></script>
587 St Martin's Church Guernsey. All Rights Reserved. Home page banner image by <a href="https://nickrowepoetry.rocks/" target="_blank" ref="noreferrer noopener">@nickrowepoetry</a>. Designed &amp; Powered by the <a href="https://www.churchpages.co.uk" target="_blank" ref="noreferrer noopener" title="creator link">Church Pages Initiative</a></h6></div>
588</div>
589</div>
590						<span class="powered">Powered by 
591							<a href='https://www.khooseller.co.uk' target='_blank'>Khooweb e-commerce website specialists</a>
592						</span>
593					</div>
594				</div>
595			</footer>
596				</div>
597			</div>
598		</div>
599
600	</div>
601
602	
603	
603<script src='/js/plugins/cookieconsent/dist/cookieconsent.umd.js?v=5'></script>
vendor: 1 bytes, line 603
603
604<script src='/js/alpine-init.js?v=5'></script>
vendor: 1 bytes, line 604
604
605<script>
606$(function() {
607$("#h-nav").sticky({ topSpacing: 0 });
608});
609
610</script>
vendor: 1 bytes, line 610
610
611<script defer src='/js/plugins/popupwindow.js?v=5'></script>
vendor: 1 bytes, line 611
611
612<script src='/js/stores/modals.js?v=5'></script>
vendor: 1 bytes, line 612
612
613<script src='/js/stores/slideout.js?v=5'></script>
vendor: 1 bytes, line 613
613
614<script src='/js/component/slideoutForm.js?v=5'></script>
vendor: 1 bytes, line 614
614
615<script src='/js/component/fieldValidator.js?v=5'></script>
vendor: 1 bytes, line 615
615
616<script src='/js/component/suggestionBox.js?v=5'></script>
vendor: 1 bytes, line 616
616
617<script src='/js/component/progressBar.js?v=5'></script>
vendor: 1 bytes, line 617
617
618<script src='/js/component/successRedirect.js?v=5'></script>
vendor: 1 bytes, line 618
618
619<script src='/js/component/tinyMceModalForm.js?v=5'></script>
vendor: 1 bytes, line 619
619
620<script defer src='/js/default_body.js?v=5'></script>
vendor: 1 bytes, line 620
620
621<script type="text/plain" data-category="analytics">
622<!-- Matomo -->
623  var _paq = _paq || [];
624  _paq.push(['trackPageView']);
625  _paq.push(['enableLinkTracking']);
626  (function() {
627    var u="https://stats.khoosys.net/";
628    _paq.push(['setTrackerUrl', u+'piwik.php']);
629    _paq.push(['setSiteId', '348']);
630    
631    var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
632    g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
633  })();
634<!-- End Matomo Code -->
635</script>
635
636
637	
637<script src="/_default/js/cookie_consent.htm"></script>
637
638    <!-- ========================================================================= -->
639    <!-- Modal HTML. We wrap it in a simple x-data                               -->
640    <!-- to ensure it's part of Alpine's initialization process.                 -->
641    <!-- ========================================================================= -->
642    <div id="alpine-modal-root" class="modern-form" 
643        x-data
644        @htmx:before-request="$store.modals.clearTopModalErrors()"
645        @htmx:after-request="$store.modals.handleHtmxResponse(event)"
646        @htmx:after-swap="$store.modals.bindFormValidation(event)"
647        @close-top-modal="$store.modals.close()"
648        > 
649        <!-- The x-data makes this an official Alpine component -->
650
651        <!-- BEGIN: Global Alpine/HTMX Modal Stack -->
652        <template x-if="$store.modals.stack.length > 0"> <!-- THE CRITICAL FIX IS HERE -->
653            <div class="modal-stack-container" 
654                 @keydown.escape="$store.modals.close()"
655                 x-cloak>
656        
657                <!-- The blackout background. Closes the topmost modal when clicked.
658                     Tracks the topmost modal's z-base (minus 1) so that a modal
659                     elevated above an "-above-modal" slideout (z-base 2200) dims
660                     the slideout too; the default case stays at 1999 via the CSS. -->
661                <div class="blackout-bg" @click="$store.modals.close()"
662                     :style="$store.modals.top && $store.modals.top.zBase ? { zIndex: $store.modals.top.zBase - 1 } : {}"></div>
663
664                <!-- This loops through each open modal and "stacks" them visually.
665                     Each modal carries its own z-base (2000 by default, 2200 when
666                     opened from inside an elevated slideout — see modals.js open()),
667                     keeping the dialog above the modal blackout, the slideout panel
668                     (1100 / 2100 elevated), and the legacy \Core\Dialog stack (1001). -->
669                <template x-for="(modal, index) in $store.modals.stack" :key="index">
670                    <div class="dialog-wrapper" :style="{ zIndex: (modal.zBase || 2000) + index }">
671                        <div class="dialog" :style="{ maxWidth: modal.width || '600px' }">
672                            
673                            <!-- ======================================================= -->
674                            <!-- BEGIN: "HTML" Dynamic Content from HTMX PRESET          -->
675                            <!-- ======================================================= -->
676                            <template x-if="modal.type === 'html'">
677                                <div x-html="modal.content"></div>
678                            </template>
679    
680                            <!-- ======================================================= -->
681                            <!-- BEGIN: "FORM" PRESET                                    -->
682                            <!-- ======================================================= -->
683                            <template x-if="modal.type === 'form'">
684                                <div class="dialog-content-wrapper" x-data="{
685                                        title: modal.title || 'Dialog',
686                                        confirmText: modal.confirmText || 'Save Changes',
687                                        loadUrl: modal.loadUrl || '',
688                                        postUrl: modal.postUrl || '',
689                                        context: modal.context || '',
690                                        clearError(event) {
691                                            const fieldName = event.target.name;
692                                            // If there's a name and a corresponding error exists, delete it.
693                                            if (fieldName && $store.modals.top?.errors?.[fieldName]) {
694                                                delete $store.modals.top.errors[fieldName];
695                                                // If it was a field-specific error, the form is no longer fatal.
696                                                // A general fatal error will remain.
697                                                if ($store.modals.top.isFatal && !$store.modals.top.errors.general) {
698                                                    $store.modals.top.isFatal = false;
699                                                }
700                                                $store.modals._renderErrors();
701                                            }
702                                        }
703                                    }"
704                                   x-init="Alpine.nextTick(() => htmx.process($el))">
705                                   
706                                    <!-- 1. The Header (Now part of the shell) -->
707                                    <div class="dialog-header">
708                                        <div class="dialog-title" x-text="title"></div>
709                                        <button class="dialog-close" @click="$store.modals.close()">&times;</button>
710                                    </div>
711
712                                    <!-- 2. The Form -->
713                                    <!-- Bind fieldValidator once the body's htmx content lands so
714                                         .required / .digits / min / max are enforced client-side,
715                                         mirroring the slideout and legacy Core\Dialog. Without this
716                                         a `max` on a text/digits input is never checked (native
717                                         HTML5 max only applies to type=number). bind() self-guards
718                                         against the repeat afterSwaps of nested panel loads.
719
720                                         `context` (which can be a JSON array of every ticked row id)
721                                         travels as a hidden input, NOT hx-vals: hx-vals is inherited
722                                         by descendants, so the .dialog-body loader GET below would
723                                         carry the whole id list in its query string and 414 on a
724                                         large selection. htmx collects enclosing-form inputs on the
725                                         form's own POST only. -->
726                                    <form
727                                        :hx-post="postUrl"
728                                        hx-swap="none"
729                                        hx-disabled-elt="find .button-submit"
730                                        x-init="$el.addEventListener('htmx:afterSwap', () => window.fieldValidator && window.fieldValidator.bind($el))"
731                                        @input.debounce.250ms="clearError($event)"
732                                    >
733                                        <input type="hidden" name="context" :value="context" />
734                                        <div class="validation-error -general"
735                                            data-error-general
736                                            style="display:none">
737                                        </div>
738                                        <div class="dialog-body modern-form"
739                                            :hx-get="loadUrl"
740                                            hx-trigger="load"
741                                            hx-swap="innerHTML"
742                                            hx-indicator="#loading-spinner" 
743                                            >                                            
744                                            <div id="loading-spinner" class="htmx-indicator">
745                                                <div class="loader"></div>
746                                            </div>
747                                        </div>
748
749                                        <div class="dialog-buttons">
750                                            <button type="button" class="button-cancel" @click="$store.modals.close()">Cancel</button>
751                                            <button type="submit" class="button-submit" :disabled="$store.modals.top?.isFatal" x-text="confirmText"></button>
752                                        </div>
753                                    </form>
754                                </div>
755                            </template>
756
757                            <!-- ======================================================= -->
758                            <!-- BEGIN: "CONFIRM" PRESET                                 -->
759                            <!-- ======================================================= -->
760                            <template x-if="modal.type === 'confirm'">
761                                <div class="dialog-content-wrapper">
762                                    <div class="dialog-header">
763                                        <div class="dialog-title" x-text="modal.title || 'Confirm'"></div>
764                                        <button class="dialog-close" @click="$store.modals.close()">&times;</button>
765                                    </div>
766                                    <div class="dialog-body">
767                                        <template x-if="modal.bodyHtml"><div x-html="modal.bodyHtml"></div></template>
768                                        <template x-if="!modal.bodyHtml"><div class="info-box" x-text="modal.message || modal.description || 'Are you sure?'"></div></template>
769                                    </div>
770                                    <div class="dialog-buttons">
771                                        <button class="button-cancel" @click="$store.modals.close()">
772                                            <span x-text="modal.cancelText || 'Cancel'"></span>
773                                        </button>
774                                        <button :class="modal.destructive ? 'button-destructive' : 'button-submit'"
775                                                @click="
776                                                    if (modal.okUrl) { window.location.assign(modal.okUrl); return; }
777                                                    // Function-valued onOk (inline callers) or string method on targetId (Node::modalConfirm)
778                                                    if (typeof modal.onOk === 'function') {
779                                                        modal.onOk(...(modal.okArgs || []));
780                                                    } else if (modal.targetId && modal.onOk) {
781                                                        const targetEl = document.getElementById(modal.targetId);
782                                                        if (targetEl && Alpine.$data(targetEl) && typeof Alpine.$data(targetEl)[modal.onOk] === 'function') {
783                                                            Alpine.$data(targetEl)[modal.onOk](...(modal.okArgs || []));
784                                                        }
785                                                    }
786                                                    $store.modals.close();
787                                                ">
788                                            <span x-text="modal.okText || modal.confirmText || 'OK'"></span>
789                                        </button>
790                                    </div>
791                                </div>
792                            </template>
793
794                            <!-- ======================================================= -->
795                            <!-- BEGIN: "INPUT CONFIRM" PRESET                           -->
796                            <!-- ======================================================= -->
797                            <template x-if="modal.type === 'inputConfirm'">
798                                <!-- This component has its own internal state for the user's input -->
799                                <div class="dialog-content-wrapper" x-data="{ userInput: '' }">
800                                    <div class="dialog-header">
801                                        <div class="dialog-title" x-text="modal.title || 'Confirm Action'"></div>
802                                        <button class="dialog-close" @click="$store.modals.close()">&times;</button>
803                                    </div>
804                                    <div class="dialog-body modern-form">
805                                        <!-- The main instructional message -->
806                                        <div class="info-box" x-text="modal.message || 'Please confirm your action.'"></div>
807
808                                        <!-- The input field for validation -->
809                                        <div class="form-section">
810                                            <div class="form-group">
811                                                <input type="text"
812                                                    x-model="userInput"
813                                                    :placeholder="modal.requiredText"
814                                                    autocomplete="off"
815                                                    @keydown.enter.prevent="if (userInput === modal.requiredText) $refs.submitButton.click()"
816                                                />
817                                                <label x-text="modal.label || 'Please type the confirmation text'"></label>
818                                            </div>
819                                        </div>
820                                    </div>
821                                    <div class="dialog-buttons">
822                                        <button class="button-cancel" @click="$store.modals.close()">
823                                            <span x-text="modal.cancelText || 'Cancel'"></span>
824                                        </button>
825                                        <button class="button-submit"
826                                                x-ref="submitButton"
827                                                :disabled="userInput !== modal.requiredText"
828                                                @click="
829                                                    if (modal.targetId && modal.onOk) {
830                                                        const targetEl = document.getElementById(modal.targetId);
831                                                        if (targetEl) Alpine.$data(targetEl)[modal.onOk](...(modal.okArgs || []));
832                                                    }
833                                                    $store.modals.close();
834                                                ">
835                                            <span x-text="modal.okText || 'OK'"></span>
836                                        </button>
837                                    </div>
838                                </div>
839                            </template>
840
841                            <!-- ======================================================= -->
842                            <!-- BEGIN: "PROGRESS" PRESET (NEW!)                         -->
843                            <!-- ======================================================= -->
844                            <template x-if="modal.type === 'progress'">
845                                <!--
846                                This block is now pure structure. The logic comes from the progressBar component.
847                                It's initialized with the taskId passed in the modal data object.
848                                -->
849                                 <div class="dialog-content-wrapper" 
850                                    x-data="progressBar(modal.taskId, modal.stoppable)" 
851                                    x-cloak
852                                >
853                                    <!-- The rest of your template remains exactly the same! -->
854                                    <div class="dialog-header"><div class="dialog-title">Processing...</div></div>
855                                    <div class="dialog-body">
856                                        <p class="progress-status" x-text="statusText"
857                                        :class="hasFailed ? 'validation-error' : ''"></p>
858                                        <div style="background: #eee; border-radius: 5px; overflow: hidden;">
859                                            <div style="width: 0%; background: #3498db; height: 20px; text-align: center; color: white; transition: width 0.3s ease;"
860                                                :style="{ width: percent + '%' }"
861                                                x-text="percent > 0 ? percent + '%' : ''"
862                                                :class="hasFailed ? 'bg-fail' : ''"
863                                            >
864                                            </div>
865                                        </div>
866                                    </div>
867                                    <div class="dialog-buttons">
868                                        <!-- The new, optional Stop button -->
869                                        <button type="button" class="button-destructive" 
870                                                x-show="isStoppable && !isComplete && !hasFailed && !isStopping" 
871                                                @click="stopTask()">
872                                            Stop
873                                        </button>
874
875                                        <!-- A disabled version for better UX after clicking stop -->
876                                        <button type="button" class="button-destructive" disabled
877                                                x-show="isStopping">
878                                            Stopping...
879                                        </button>
880
881                                        <!-- Your existing close button for failures -->
882                                        <button type="button" class="button-cancel" 
883                                                x-show="hasFailed" 
884                                                @click="closeAndCleanup()">
885                                            Close
886                                        </button>
887                                    </div>
888                                </div>
889                            </template>
890
891                            <!-- ======================================================= -->
892                            <!-- BEGIN: "SUCCESS REDIRECT" PRESET (NEW!)                 -->
893                            <!-- ======================================================= -->
894                            <template x-if="modal.type === 'success'">
895                                <!--
896                                The component is initialized with the message and URL from the modal data.
897                                The @destroy directive ensures we clean up timers if the user hits Escape.
898                                -->
899                                <div class="dialog-content-wrapper" 
900                                    x-data="successRedirect(modal.message, modal.redirectUrl)" 
901                                    @destroy="destroy()"
902                                    x-cloak>
903                                    
904                                    <div class="dialog-header"><div class="dialog-title">Success</div></div>
905                                    <div class="dialog-body" style="text-align: center;">
906                                        <!-- You can add a success icon here if you like -->
907                                        <p style="margin: 1rem 0;" x-text="message"></p>
908                                    </div>
909                                    <div class="dialog-buttons">
910                                        <!-- The OK button calls redirectNow() and shows the countdown -->
911                                        <button type="button" class="button-submit" @click="redirectNow()">
912                                            OK <span x-text="'(' + countdown + ')'"></span>
913                                        </button>
914                                    </div>
915                                </div>
916                            </template>
917
918                        </div>
919                    </div>
920                </template>
921            </div>
922        </template>
923        <!-- END: Global Alpine/HTMX Modal Stack -->
924
925    </div> <!-- End of #alpine-modal-root -->
926
927    <!-- ======================================================= -->
928    <!-- SLIDE-OUT PANEL COMPONENT (Styled for .modern-form)   -->
929    <!-- ======================================================= -->
930    <div id="slideout-panel-root" class="modern-form"
931        x-data="slideoutForm()"
932    >
933        <!-- Overlay -->
934        <div 
935            class="slideout-overlay"
936            :class="{ '-below-topbar': $store.slideout.panelClass && $store.slideout.panelClass.indexOf('-below-topbar') !== -1, '-above-modal': $store.slideout.panelClass && $store.slideout.panelClass.indexOf('-above-modal') !== -1 }"
937            @click="handleOverlayClick()"
938            x-show="$store.slideout.isOpen && (!$store.slideout.panelClass || $store.slideout.panelClass.indexOf('-object-map-list') === -1)"
939            x-cloak
940            x-transition:enter="transition-opacity ease-in-out duration-300"
941            x-transition:enter-start="opacity-0"
942            x-transition:enter-end="opacity-100"
943            x-transition:leave="transition-opacity ease-in-out duration-300"
944            x-transition:leave-start="opacity-100"
945            x-transition:leave-end="opacity-0"
946        ></div>
947
948        <!-- Panel -->
949        <div 
950            class="slideout-panel"
951            :class="$store.slideout.panelClass.split(' ').filter(function (c) { return c; })"
952            x-show="$store.slideout.isOpen"
953            x-cloak
954            x-transition:enter="transition ease-in-out duration-300 transform"
955            x-transition:enter-start="translate-x-full"
956            x-transition:enter-end="translate-x-0"
957            x-transition:leave="transition ease-in-out duration-300 transform"
958            x-transition:leave-start="translate-x-0"
959            x-transition:leave-end="translate-x-full"
960            :style="$store.slideout.currentWidth ? { width: $store.slideout.currentWidth } : {}"
961        >
962            <!-- Header -->
963            <div class="slideout-header">
964                <div class="slideout-header-text">
965                    <h2 class="slideout-title" x-text="$store.slideout.title"></h2>
966                    <p class="slideout-subtitle" x-show="$store.slideout.subt
966itle" x-text="$store.slideout.subtitle" x-cloak></p>
967                </div>
968                <div id="slideout-head-slot" class="slideout-head-slot"></div>
969                <button @click="handleCloseClick()" class="slideout-close" aria-label="Close panel">&times;</button>
970            </div>
971
972            <!-- Content shell: teleports (e.g. object-map list) mount as direct children;
973                 htmx fragments load into #slideout-htmx-body so innerHTML swaps never
974                 wipe Alpine teleport anchors on this container. -->
975            <div id="slideout-content" class="slideout-body">
976                <div id="slideout-htmx-body"
977                     hx-trigger="load-slideout-content"
978                     hx-swap="innerHTML"></div>
979            </div>
980
981            <!-- Footer (form type only) -->
982            <div class="slideout-footer"
983                 x-show="$store.slideout.type === 'form' && !$store.slideout.hideFooter"
984                 x-cloak>
985                <div id="slideout-foot-left" class="slideout-foot-left"></div>
986                <div class="slideout-foot-actions">
987                    <button type="button" class="btn button-cancel" @click="cancelExplicit()">Cancel</button>
988                    <div id="slideout-foot-actions"></div>
989                </div>
990            </div>
991        </div>
992
993        <iframe id="slideout-save-frame" name="slideout-save-frame" class="slideout-save-frame"
994                title="slideout save target" tabindex="-1" aria-hidden="true"></iframe>
995    </div>
996
997    <!-- ======================================================= -->
998    <!-- GUIDED TUTORIAL OVERLAY (engine mount point)            -->
999    <!-- The player builds its dim-backdrop + popover in <body>; -->
1000    <!-- this stable root just hosts the Alpine component.       -->
1001    <!-- Only mounted where the tutorial JS bundle is registered  -->
1002    <!-- (admin) — the slot is left empty otherwise; see _foot.php -->
1003    <!-- ======================================================= -->
1004
1005    <!-- Hidden form – submits same data as original -->
1006    <form id="advanced-search-hidden-form" x-data
1007        hx-post="/index.php?url=/159/index.htm"
1008        hx-target="#search-results-content"
1009        hx-select="#search-results-content"
1010        hx-swap="innerHTML"
1011        style="display:none;"
1012        @htmx:after-request="handleSearchSuccess(event, 'advanced-search-template')"
1013        >
1014        <div id="advanced-search-hidden-fields"></div>
1015    </form>
1016
1017    <template id="show-loader-loader-template">
1018        <div style="padding:80px 0; text-align:center; font-size:1.1em; color:#666;">
1019            <div class="loader" style="display:inline-block; vertical-align:middle;"></div>
1020        </div>
1021    </template>
1022<script>
1023    document.body.addEventListener('htmx:beforeRequest', function(evt) {
1024        // Get the element that will be updated (the swap target)
1025        let target = evt.detail.target;
1026
1027        // If no explicit target, fall back to the requesting element
1028        if (!target) {
1029            const requestConfig = evt.detail.xhr; // not directly available, so use the elt
1030            target = evt.target;
1031        }
1032
1033        // Also check if the actual swap target (from hx-target on trigger) has the class
1034        // HTMX sets evt.detail.target to the swap target element
1035        if (target && target.classList && target.classList.contains('show-loader')) {
1036            const loaderTemplate = document.getElementById('show-loader-loader-template');
1037            
1038            if (loaderTemplate && !target.querySelector('.htmx-loader-overlay')) { // prevent duplicates
1039                const overlay = document.createElement('div');
1040                overlay.className = 'htmx-loader-overlay';
1041                
1042                Object.assign(overlay.style, {
1043                    position: 'absolute',
1044                    top: '0',
1045                    left: '0',
1046                    width: '100%',
1047                    height: '100%',
1048                    zIndex: '100',
1049                    display: 'flex',
1050                    alignItems: 'center',
1051                    justifyContent: 'center',
1052                    backgroundColor: 'rgba(255,255,255,0.8)',
1053                    backdropFilter: 'blur(2px)'
1054                });
1055
1056                const loaderClone = loaderTemplate.content.cloneNode(true);
1057                overlay.appendChild(loaderClone);
1058
1059                // Ensure relative positioning
1060                const computedStyle = window.getComputedStyle(target);
1061                if (computedStyle.position === 'static') {
1062                    target.style.position = 'relative';
1063                }
1064
1065                target.appendChild(overlay);
1066            }
1067        }
1068    });
1069</script>
1069
1070    
1070<script>
1071    /**
1072     * Creates a reusable object with logic for managing a selection.
1073     * This is a "mixin" that we will add to our main Alpine component.
1074     * @param {string} initialId - The ID of the item that should be selected on page load.
1075     */
1076    function selectionManagerMixin(initialId) {
1077        return {
1078            // --- Properties ---
1079            initialSelectedId: initialId,
1080
1081            // --- Methods ---
1082            initializeSelection() {
1083                // Explicit initial id (detail-context pages) — select unconditionally.
1084                if (this.initialSelectedId && this.initialSelectedId !== '0') {
1085                    setTimeout(() => {
1086                        Alpine.store('selection').select(this.initialSelectedId);
1087                    }, 0);
1088                    return;
1089                }
1090                // Read the URL-persisted selection first: clear() strips ?selrow from the
1091                // URL synchronously (history.replaceState), so reading it after is too late.
1092                let selrow = '';
1093                try { selrow = new URL(window.location.href).searchParams.get('selrow') || ''; } catch (e) {}
1094
1095                // No server-preselected row on this page. The selection store is a
1096                // global singleton shared across pages, so a selection set on another
1097                // screen (e.g. a product-variant idx picked on the custom-price page)
1098                // survives an admin partial-swap navigation. Clear it so detail panes
1099                // keyed on $store.selection.selectedId don't fetch with a foreign id.
1100                Alpine.store('selection').clear();
1101
1102                // Then restore that selection so a refresh keeps the same row — but only
1103                // if that row is actually present in the current list.
1104                if (selrow) this._restorePersistedSelection(selrow);
1105            },
1106
1107            // A single setTimeout(0) races row render: on list pages where htmx/Alpine
1108            // reprocess the table after load, the row can appear a tick or two later, so
1109            // the one-shot lookup missed it and the selection was silently dropped (looked
1110            // like the selection "jumped" away on refresh). Retry across a few frames until
1111            // the row exists, then select it; give up quietly if it never shows (e.g. it's
1112            // on another paginated page).
1113            _restorePersistedSelection(selrow, attempt = 0) {
1114                const sel = (window.CSS && CSS.escape) ? CSS.escape(selrow) : selrow;
1115                if (document.querySelector('tr[data-selected-id="' + sel + '"]')) {
1116                    Alpine.store('selection').select(selrow);
1117                    return;
1118                }
1119                if (attempt < 20) {
1120                    setTimeout(() => this._restorePersistedSelection(selrow, attempt + 1), 30);
1121                }
1122            }
1123        };
1124    }
1125
1126    /**
1127     * The primary handler for after ANY search request completes.
1128     * It updates the search tags and resets the selection pane.
1129     */
1130    function handleSearchSuccess(event, templateId) {
1131        if (event.detail.successful) {
1132            
1133            // Get the raw payload from the request
1134            const payload = event.detail.requestConfig.parameters;
1135
1136
1137            // Set the tags using the payload
1138            Alpine.store('searchTags').setFromPayload(payload, templateId);
1139            
1140            Alpine.store('selection').clear();
1141        } else {
1142            console.error('[handleSearchSuccess] A search request was not successful.');
1143        }
1144    }
1145
1146
1147    window.handleAdvancedSearchSuccess = (event, templateId) => {
1148        if (event.detail.successful) {
1149            Alpine.store('slideout').close();
1150        }
1151        handleSearchSuccess(event, templateId);
1152    };
1153
1154    /**
1155     * openBulkModal — click handler for Node::modalBulkForm bulk-action menus.
1156     *
1157     * Three scopes:
1158     *   - 'selection' : collect .p-check:checked values from #list_form, bail
1159     *                   out with a notify if none selected, open a form modal
1160     *                   with the checked-id count in the loadUrl query string
1161     *                   and the ids JSON-encoded in `context` for the action
1162     *                   handler.
1163     *   - 'selection-into' : like 'selection', but also needs the single
1164     *                   highlighted row (`$store.selection.selectedId`) as a
1165     *                   *target*. Bails with a notify if nothing is highlighted,
1166     *                   substitutes the `__TARGET__` sentinel in loadUrl/postUrl
1167     *                   with the target idx (so it lands in the URL path segment
1168     *                   the action handler reads), and excludes the target from
1169     *                   the checked-id sources. Used by the customer "Merge
1170     *                   into selected" action.
1171     *   - 'results'   : no id collection; the current URL's query string is
1172     *                   appended to both loadUrl and postUrl so the action
1173     *                   handler can re-run the current search. `context` is
1174     *                   the literal string 'results'.
1175     *
1176     * Pairs with:
1177     *   - Node::modalBulkForm() — renders the trigger @click handler.
1178     *   - fetch/html/*_bulk.php loader — reads ?count= / query-string
1179     *     params to render the confirmation message.
1180     *   - fetch/action/*_bulk.php handler — reads $oREQUEST->context to
1181     *     distinguish selection (JSON array of ids) vs results (literal
1182     *     'results' plus query-string search params).
1183     */
1184    window.openBulkModal = function(title, loadUrl, postUrl, confirmText, scope, width) {
1185        scope = scope || 'selection';
1186
1187        if (scope === 'selection' || scope === 'selection-into') {
1188            // 'selection-into' also needs the highlighted row as a target: inject
1189            // it into the URL path (__TARGET__ sentinel) up front, then fall
1190            // through to the same checked-source collection as plain 'selection'.
1191            let target = null;
1192            if (scope === 'selection-into') {
1193                target = Alpine.store('selection')?.selectedId;
1194                if (!target) {
1195                    notify('No target customer', 'Click a customer row to pick the merge target first.', 'info');
1196                    return;
1197                }
1198                loadUrl = loadUrl.replaceAll('__TARGET__', target);
1199                postUrl = postUrl.replaceAll('__TARGET__', target);
1200            }
1201
1202            const listForm = document.getElementById('list_form');
1203            const rootSel  = listForm ? listForm.querySelectorAll('input.p-check:checked')
1204                                      : document.querySelectorAll('#list_form input.p-check:checked');
1205            let checked = Array.from(rootSel).map(el => el.value);
1206            // the target can't be a source (can't merge a customer into itself)
1207            if (target) checked = checked.filter(id => id !== target);
1208            if (checked.length === 0) {
1209                notify('Nothing selected', 'Tick a checkbox on at least one row first.', 'info');
1210                return;
1211            }
1212            const sep = loadUrl.includes('?') ? '&' : '?';
1213            Alpine.store('modals').open({
1214                type:        'form',
1215                title:       title,
1216                loadUrl:     loadUrl + sep + 'count=' + checked.length,
1217                postUrl:     postUrl,
1218                confirmText: confirmText || 'Confirm',
1219                context:     JSON.stringify(checked),
1220                width:       width || ''
1221            });
1222            return;
1223        }
1224
1225        if (scope === 'results') {
1226            // The active filters live in the searchTags store — they're posted
1227            // to the list via htmx.ajax() without ever being written to the URL
1228            // (only chip *removal* prunes the URL). So window.location.search is
1229            // stale and would make the bulk action operate on a different set
1230            // than the one on screen. Prefer the store's authoritative filter
1231            // set; fall back to the URL for legacy pages that don't use tags.
1232            const tagStore = Alpine.store('searchTags');
1233            let extra;
1234            if (tagStore && Array.isArray(tagStore.tags) && tagStore.tags.length) {
1235                const params = new URLSearchParams();
1236                tagStore.tags.forEach(t => {
1237                    if (t.value !== '' && t.value != null) params.append(t.name, t.value);
1238                });
1239                extra = params.toString();
1240            } else {
1241                const qs = window.location.search || '';
1242                extra = qs ? qs.replace(/^\?/, '') : '';
1243            }
1244            const loadSep = loadUrl.includes('?') ? '&' : '?';
1245            const postSep = postUrl.includes('?') ? '&' : '?';
1246            Alpine.store('modals').open({
1247                type:        'form',
1248                title:       title,
1249                loadUrl:     loadUrl + (extra ? loadSep + extra : ''),
1250                postUrl:     postUrl + (extra ? postSep + extra : ''),
1251                confirmText: confirmText || 'Confirm',
1252                context:     'results',
1253                width:       width || ''
1254            });
1255            return;
1256        }
1257
1258        console.error('openBulkModal: unknown scope', scope);
1259    };
1260
1261    /**
1262     * Global submit_list bridge for legacy Jaxon list-action dialogs.
1263     *
1264     * Pages converted to the modern htmx + modern-form blueprint no longer
1265     * include the old hidden `<form id="unisearch-form">` that the legacy
1266     * `submit_list({list_action, list_dlg_data})` callback used to populate
1267     * and submit. This shim is invoked by the Jaxon dialog `cbfunction` and
1268     * builds a temporary POST form on the fly so existing PHP `list_action`
1269     * handlers (delete / merge / email / set_group / etc.) keep working
1270     * without per-page rewires.
1271     *
1272     * If a page defines its own `submit_list` (e.g. orders.htm, which has
1273     * its own hidden legacy form for backwards compat), that wins.
1274     */
1275    if (typeof window.submit_list !== 'function') {
1276        window.submit_list = function(data) {
1277            const checked = [];
1278            document.querySelectorAll('input.p-check:checked').forEach(el => {
1279                checked.push(el.value);
1280            });
1281
1282            const form = document.createElement('form');
1283            form.method = 'post';
1284            form.action = location.pathname + location.search;
1285            form.style.display = 'none';
1286
1287            const addField = (name, value) => {
1288                const input = document.createElement('input');
1289                input.type = 'hidden';
1290                input.name = name;
1291                input.value = value;
1292                form.appendChild(input);
1293            };
1294
1295            addField('list_action',  data.list_action || '');
1296            addField('list_dlg_data', data.list_dlg_data || '');
1297            addField('p_checked',    checked.join(','));
1298
1299            document.body.appendChild(form);
1300            form.submit();
1301        };
1302    }
1303    </script>
1303
1304    <!-- alpine-init.js is served via AssetCatalog ('alpine.init', registered in
1305         _default ModuleInterface::registerIncludes) so it gets the cache-buster
1306         version query. Do not re-add a hardcoded script tag here. -->
1307</body>
1308</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.