domma-cms 1.8.34 → 1.8.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/admin/index.html +1 -1
  3. package/admin/js/app.js +1 -1
  4. package/admin/js/lib/seo-analyse.js +1 -1
  5. package/admin/js/lib/seo-settings.js +2 -2
  6. package/admin/js/templates/docs/usage-search-engines.html +84 -0
  7. package/admin/js/templates/documentation.html +4 -0
  8. package/admin/js/views/doc-pages.js +1 -1
  9. package/admin/js/views/seo.js +15 -14
  10. package/docs/DommaFeatures.md +9 -2
  11. package/docs/plugin-development.md +1 -1
  12. package/package.json +1 -1
  13. package/plugins/booking/CLAUDE.md +5 -0
  14. package/plugins/booking/admin/views/settings.js +1 -1
  15. package/plugins/booking/docs/guide.md +2 -0
  16. package/plugins/booking/plugin.json +2 -2
  17. package/plugins/booking/plugin.public.js +15 -2
  18. package/plugins/booking/tests/seo.test.js +71 -0
  19. package/plugins/free-tier.lock.json +18 -16
  20. package/plugins/shopping-cart/CLAUDE.md +9 -0
  21. package/plugins/shopping-cart/admin/views/settings.js +1 -1
  22. package/plugins/shopping-cart/docs/guide.md +2 -1
  23. package/plugins/shopping-cart/plugin.js +18 -0
  24. package/plugins/shopping-cart/plugin.json +2 -2
  25. package/plugins/shopping-cart/plugin.public.js +39 -4
  26. package/plugins/shopping-cart/tests/seo.test.js +95 -0
  27. package/public/js/site.js +1 -1
  28. package/public/js/vendor/dompurify/LICENSE +202 -0
  29. package/public/js/vendor/dompurify/LICENSE-MPL +373 -0
  30. package/public/js/vendor/dompurify/purify.min.js +7 -0
  31. package/scripts/build.js +3 -0
  32. package/server/routes/api/collections.js +2 -0
  33. package/server/routes/api/effects.js +5 -1
  34. package/server/routes/api/forms.js +2 -0
  35. package/server/routes/api/views.js +2 -0
  36. package/server/routes/public.js +36 -1
  37. package/server/server.js +1 -0
  38. package/server/services/docs.js +1 -0
  39. package/server/services/hooks.js +3 -2
  40. package/server/services/markdown.js +200 -120
  41. package/server/services/nojsLinks.js +130 -0
  42. package/server/services/renderer.js +23 -6
  43. package/server/services/sitemap.js +100 -2
  44. package/server/templates/page.html +17 -12
@@ -109,7 +109,7 @@ export async function openSettings(ctx, section = 'store') {
109
109
  <input class="form-input" data-model="shopTitle.value" maxlength="80"></div>
110
110
  <div><label class="form-label">Address <span data-help="Where the shop lives on your site: /shop gives /shop, /shop/cart and a page per product. Changing it takes effect at once; links people saved to the old address stop working."></span></label>
111
111
  <input class="form-input" data-model="shopPath.value" maxlength="60"></div>
112
- <div class="shp-wide"><label class="form-label">Introduction <span data-help="A line or two under the shop's name."></span></label>
112
+ <div class="shp-wide"><label class="form-label">Introduction <span data-help="A line or two under the shop's name. Search engines show it too; left empty, they get a line made from your site name and products."></span></label>
113
113
  <textarea class="form-input" rows="2" data-model="shopIntro.value" maxlength="1000"></textarea></div>
114
114
  <div><label class="form-label">Currency <span data-help="Every price in the shop. Changing it does not convert the prices you have entered."></span></label>
115
115
  <select class="form-select" data-model="currency.value">${Object.entries(CURRENCIES).map(([c, x]) => `<option value="${c}">${c} - ${esc(x.label)}</option>`).join('')}</select></div>
@@ -120,7 +120,8 @@ Click the cog in the Shop banner to open **Shop settings**. It has four sections
120
120
 
121
121
  ### Storefront
122
122
 
123
- - **Shop name**, **Address** (default `/shop`; takes effect at once, and old links stop working), **Introduction**.
123
+ - **Shop name**, **Address** (default `/shop`; takes effect at once, and old links stop working), **Introduction**. Search engines show the introduction as the shop's description; left empty, they get a line made from your site's name and first products.
124
+ - **While nothing is on sale** the shop page tells search engines not to list it, and it stays out of the site's `sitemap.xml`. As soon as one product is active (and not unlisted), the shop and every product page are indexed and listed in the sitemap.
124
125
  - **Currency** - changing it does not convert prices you have entered.
125
126
  - **Products per row** (2, 3 or 4).
126
127
  - **Category filters**, **Search box** (shown once there are more than four products), **Basket button on every page**, **Say "Only 3 left"**.
@@ -43,6 +43,7 @@ import {
43
43
  } from './lib/shop.js';
44
44
  import {refundPlan} from './lib/refunds.js';
45
45
  import {cardHtml, esc, gridHtml} from './lib/render.js';
46
+ import {shopSitemapEntries} from './plugin.public.js';
46
47
 
47
48
  /** The Site URL setting as an origin, or ''. */
48
49
  function configuredBase() {
@@ -171,6 +172,23 @@ export default async function shoppingCart(fastify, options) {
171
172
  await refreshShelf();
172
173
  registerShortcodes(hooks);
173
174
 
175
+ // The shop in /sitemap.xml (core 0.89+; feature-detected), 1.7.2: the storefront and its
176
+ // products - nothing while nothing is on sale (the empty shop is noindex). A product or
177
+ // settings write here rebuilds it on the sitemap's next request.
178
+ if (typeof hooks.registerSitemapSource === 'function') {
179
+ try {
180
+ hooks.registerSitemapSource({id: 'shop', label: 'Shop', list: async () => {
181
+ const [entries, s] = await Promise.all([allProducts(), liveSettings()]);
182
+ return shopSitemapEntries(entries, s);
183
+ }});
184
+ fastify.addHook('onResponse', async (request, reply) => {
185
+ if (request.method !== 'GET' && request.method !== 'HEAD' && reply.statusCode < 400 && /\/(products|samples|settings)\b/.test(request.url)) {
186
+ try { hooks.sitemapChanged?.(); } catch { /* older core */ }
187
+ }
188
+ });
189
+ } catch { /* older core */ }
190
+ }
191
+
174
192
  // ---- Telling people ------------------------------------------------------------
175
193
 
176
194
  function announcePaid(order) {
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "shopping-cart",
3
3
  "displayName": "Shopping Cart",
4
- "version": "1.7.1",
4
+ "version": "1.7.2",
5
5
  "tier": "free",
6
6
  "description": "Sell from your site: products with options, stock and pictures, a storefront and a cart that follows shoppers from page to page, and checkout with Stripe, PayPal or payment offline. Orders to fulfil in the sidebar, refunds from the order.",
7
7
  "author": "Domma CMS",
8
- "date": "2026-10-15",
8
+ "date": "2026-10-22",
9
9
  "icon": "shopping-bag",
10
10
  "permissions": [
11
11
  {
@@ -17,6 +17,7 @@ import path from 'path';
17
17
  import {fileURLToPath} from 'url';
18
18
  import {renderBlogPage} from '../../server/services/renderer.js';
19
19
  import {parseMarkdown} from '../../server/services/markdown.js';
20
+ import {getConfig} from '../../server/config.js';
20
21
  import {byShelf, safeImage} from './public/lib/products.js';
21
22
  import {accountHtml, cartHtml, esc, productHtml, receiptHtml, scriptJson, storefrontHtml} from './lib/render.js';
22
23
  import {allProducts, getOrder, liveSettings, publicOrder, tokenOk} from './lib/shop.js';
@@ -40,6 +41,34 @@ export function matchShopPath(pathname, base) {
40
41
  return null;
41
42
  }
42
43
 
44
+ /** The products a shopper can see: on sale and not unlisted. */
45
+ export const onShelf = (entries) => entries.filter(e => e.data?.status === 'active' && !e.data?.unlisted);
46
+
47
+ /**
48
+ * The search-engine description (1.7.2): the owner's words (the shop's
49
+ * introduction, a product's summary), else a line made from the site's name -
50
+ * an empty one left /shop with no meta description at all.
51
+ */
52
+ export function metaDescription(text, fallback) {
53
+ const out = String(text || '').replace(/\s+/g, ' ').trim() || String(fallback || '').trim();
54
+ return out.length > 160 ? `${out.slice(0, 157).trimEnd()}...` : out;
55
+ }
56
+ const siteName = () => String(getConfig('site')?.title || '').trim();
57
+
58
+ /**
59
+ * The shop in /sitemap.xml (1.7.2): the storefront and every product a shopper
60
+ * can open - or nothing at all while there is nothing on sale, since the empty
61
+ * shop is `noindex` until then.
62
+ */
63
+ export function shopSitemapEntries(entries, s) {
64
+ const shelf = onShelf(entries);
65
+ if (!shelf.length) return [];
66
+ return [{urlPath: s.shopPath, title: s.shopTitle || 'Shop', changefreq: 'daily'},
67
+ ...shelf.filter(e => /^[a-z0-9-]{1,80}$/.test(String(e.data.slug || '')))
68
+ .map(e => ({urlPath: `${s.shopPath}/${e.data.slug}`, title: e.data.name || '', lastmod: e.meta?.updatedAt || null,
69
+ ...(safeImage(e.data.images?.[0]) && {image: safeImage(e.data.images[0])})}))];
70
+ }
71
+
43
72
  /** What the browser script needs, carried in the page. */
44
73
  function pageConfig(s, extra = {}) {
45
74
  return {
@@ -59,21 +88,27 @@ export default async function shopPublic(fastify) {
59
88
  title, description, ogImage: image, urlPath, noindex,
60
89
  baseUrl: `${request.protocol}://${request.host || request.headers.host || request.hostname}`
61
90
  });
91
+ // The header as well as the meta tag: a crawler reading only headers gets it too.
92
+ if (noindex) reply.header('X-Robots-Tag', 'noindex');
62
93
  return reply.type('text/html; charset=utf-8').header('Cache-Control', 'no-store').send(html);
63
94
  }
64
95
 
65
96
  const pages = {
66
97
  async index(request, reply, s) {
67
- const entries = (await allProducts()).filter(e => e.data?.status === 'active' && !e.data?.unlisted).sort(byShelf);
98
+ const entries = onShelf(await allProducts()).sort(byShelf);
68
99
  const category = String(request.query?.category || '').slice(0, 60);
69
100
  const query = String(request.query?.q || '').slice(0, 80);
101
+ const site = siteName();
70
102
  return send(reply, request, s, {
71
103
  page: 'index',
72
104
  body: storefrontHtml({entries, settings: s, base: s.shopPath, category, query}),
73
105
  title: s.shopTitle || 'Shop',
74
- description: s.shopIntro || '',
106
+ description: metaDescription(s.shopIntro, `Shop online${site ? ` with ${site}` : ''}`
107
+ + `${entries.length ? `: ${entries.slice(0, 3).map(e => e.data.name).filter(Boolean).join(', ')}${entries.length > 3 ? ' and more' : ''}` : ''}.`),
75
108
  image: safeImage(entries[0]?.data?.images?.[0]),
76
109
  urlPath: s.shopPath,
110
+ // An empty shop is not a page worth finding (1.7.2): out of search until something is on sale.
111
+ noindex: entries.length === 0,
77
112
  extra: {category, query}
78
113
  });
79
114
  },
@@ -83,7 +118,7 @@ export default async function shopPublic(fastify) {
83
118
  },
84
119
 
85
120
  async product(request, reply, s, {slug}) {
86
- const all = (await allProducts()).filter(e => e.data?.status === 'active' && !e.data?.unlisted);
121
+ const all = onShelf(await allProducts());
87
122
  const entry = all.find(e => e.data.slug === slug);
88
123
  if (!entry) return null; // fall through to the CMS's own 404 (or a page at that address)
89
124
  let descriptionHtml = '';
@@ -98,7 +133,7 @@ export default async function shopPublic(fastify) {
98
133
  page: 'product',
99
134
  body: productHtml({entry, settings: s, base: s.shopPath, descriptionHtml, related}),
100
135
  title: entry.data.name,
101
- description: entry.data.summary || '',
136
+ description: metaDescription(entry.data.summary || String(entry.data.description || '').replace(/!?\[([^\]]*)\]\([^)]*\)/g, '$1').replace(/^\s*>+/gm, '').replace(/[#*_`]/g, ''), `Buy ${entry.data.name}${siteName() ? ` from ${siteName()}` : ''} online.`),
102
137
  image: safeImage(entry.data.images?.[0]),
103
138
  urlPath: `${s.shopPath}/${slug}`,
104
139
  extra: {product: {id: entry.id, variants: (entry.data.variants || []).length}}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * 1.7.2: what search engines are told about the shop. An empty shop is noindex
3
+ * and out of the sitemap; once something is on sale it is indexed and listed
4
+ * with its products, and every storefront page has a meta description.
5
+ * Runs against the real collection service in the checkout `make test` runs in;
6
+ * the two collections are removed after and config/plugins.json restored.
7
+ */
8
+ import {test, before, after} from 'node:test';
9
+ import assert from 'node:assert/strict';
10
+ import fs from 'node:fs/promises';
11
+ import path from 'node:path';
12
+ import Fastify from 'fastify';
13
+ import shoppingCart from '../plugin.js';
14
+ import shopPublic, {metaDescription, shopSitemapEntries} from '../plugin.public.js';
15
+
16
+ const PLUGINS = path.resolve('config/plugins.json');
17
+ const DIRS = ['shop-products', 'shop-orders'].map(c => path.resolve('content/collections', c));
18
+ let savedPlugins;
19
+ let app;
20
+ let pub;
21
+ const sources = [];
22
+
23
+ before(async () => {
24
+ savedPlugins = await fs.readFile(PLUGINS, 'utf8').catch(() => null);
25
+ for (const d of DIRS) await fs.rm(d, {recursive: true, force: true});
26
+ app = Fastify();
27
+ const authenticate = async (req) => { req.user = {id: 'u1', name: 'Sam', role: 'admin'}; };
28
+ await app.register(shoppingCart, {
29
+ auth: {authenticate, requirePermission: () => async () => {}},
30
+ settings: {},
31
+ hooks: {registerShortcode() {}, registerSitemapSource: (s) => sources.push(s)}
32
+ });
33
+ pub = Fastify();
34
+ await pub.register(shopPublic);
35
+ });
36
+
37
+ after(async () => {
38
+ await app?.close();
39
+ await pub?.close();
40
+ for (const d of DIRS) await fs.rm(d, {recursive: true, force: true});
41
+ if (savedPlugins === null) await fs.rm(PLUGINS, {force: true});
42
+ else await fs.writeFile(PLUGINS, savedPlugins);
43
+ });
44
+
45
+ const sitemap = () => sources.find(s => s.id === 'shop').list();
46
+ const metaOf = (html, name) => (html.match(new RegExp(`<meta name="${name}" content="([^"]*)"`)) || [])[1];
47
+
48
+ test('metaDescription: the owner\'s words, else the made one; one line, 160 at most', () => {
49
+ assert.equal(metaDescription(' Hand-made\n mugs ', 'x'), 'Hand-made mugs');
50
+ assert.equal(metaDescription('', 'Shop online.'), 'Shop online.');
51
+ const long = metaDescription('a'.repeat(300), '');
52
+ assert.equal(long.length, 160);
53
+ assert.ok(long.endsWith('...'));
54
+ });
55
+
56
+ test('an empty shop is noindex and not in the sitemap; with a product on sale it is both indexed and listed', async () => {
57
+ let res = await app.inject({method: 'PUT', url: '/settings', payload: {shopIntro: ''}});
58
+ assert.equal(res.statusCode, 200, res.body);
59
+
60
+ res = await pub.inject({method: 'GET', url: '/shop'});
61
+ assert.equal(res.statusCode, 200);
62
+ assert.match(res.headers['x-robots-tag'] || '', /noindex/, 'the header');
63
+ assert.match(metaOf(res.body, 'robots') || '', /noindex/, 'and the meta tag');
64
+ assert.ok(metaOf(res.body, 'description'), 'still described');
65
+ assert.deepEqual(await sitemap(), [], 'nothing on sale: not in the sitemap');
66
+
67
+ // A draft is not on sale either (unlisted products: the pure test below).
68
+ await app.inject({method: 'POST', url: '/products', payload: {name: 'Draft mug', price: 900, status: 'draft'}});
69
+ assert.match((await pub.inject({method: 'GET', url: '/shop'})).headers['x-robots-tag'] || '', /noindex/);
70
+ assert.deepEqual(await sitemap(), []);
71
+
72
+ res = await app.inject({method: 'POST', url: '/products', payload: {name: 'Blue mug', price: 1200, status: 'active', summary: ''}});
73
+ assert.equal(res.statusCode, 201, res.body);
74
+ res = await pub.inject({method: 'GET', url: '/shop'});
75
+ assert.equal(res.headers['x-robots-tag'], undefined);
76
+ assert.equal(metaOf(res.body, 'robots'), undefined);
77
+ assert.match(metaOf(res.body, 'description'), /^Shop online.*: Blue mug\.$/);
78
+ const listed = await sitemap();
79
+ assert.deepEqual(listed.map(e => e.urlPath), ['/shop', '/shop/blue-mug']);
80
+
81
+ const product = await pub.inject({method: 'GET', url: '/shop/blue-mug'});
82
+ assert.match(metaOf(product.body, 'description'), /^Buy Blue mug/);
83
+ assert.equal(product.headers['x-robots-tag'], undefined);
84
+
85
+ // The owner's introduction wins.
86
+ await app.inject({method: 'PUT', url: '/settings', payload: {shopIntro: 'Hand-thrown mugs from Devon.'}});
87
+ assert.equal(metaOf((await pub.inject({method: 'GET', url: '/shop'})).body, 'description'), 'Hand-thrown mugs from Devon.');
88
+ });
89
+
90
+ test('shopSitemapEntries is pure: the shop\'s own path, products with a usable slug', () => {
91
+ const e = (data, updatedAt = '2031-01-01T00:00:00.000Z') => ({data: {status: 'active', ...data}, meta: {updatedAt}});
92
+ assert.deepEqual(shopSitemapEntries([e({name: 'A', slug: 'a', status: 'draft'}), e({name: 'U', slug: 'u', unlisted: true})], {shopPath: '/store'}), []);
93
+ assert.deepEqual(shopSitemapEntries([e({name: 'A', slug: 'a'}), e({name: 'B', slug: '../b'})], {shopPath: '/store', shopTitle: 'Store'}),
94
+ [{urlPath: '/store', title: 'Store', changefreq: 'daily'}, {urlPath: '/store/a', title: 'A', lastmod: '2031-01-01T00:00:00.000Z'}]);
95
+ });
package/public/js/site.js CHANGED
@@ -1 +1 @@
1
- import{applyMobileMenu as ne,initCarouselPopup as ae}from"/public/js/mobile-ui.mjs?v=20261016-mobile-options";import{installSmoothCarets as se}from"/public/js/smooth-caret.js";import{decodeBase64Json as R}from"/public/js/utf8-base64.mjs";import{makeBadgeEl as X,applyColour as U,applyPill as ie,applyFloat as re,applyHighlight as W,applyItemHighlight as ce,itemHighlightAttrs as le,isCurrentItem as G,menuAlign as de,applyClasses as O,cssClasses as V,applyEntrance as Y}from"/public/js/menu-decor.mjs?v=20261014-menu-entrance";import{initFloatingMenus as me}from"/public/js/menu-float.js?v=20260829-float-panel";import{enhancePickerFields as K,markInvalidFields as Q,showValidationPopup as Z}from"/public/js/forms.js?v=20261015-smooth-caret";import{buildWizardSteps as ue}from"/public/js/form-steps.mjs";import{applyFormClasses as ee,applySubmitButtons as pe,applyWizardButtons as fe}from"/public/js/form-submit.mjs";document.addEventListener("click",p=>{const r=p.target?.closest?.(".dm-banner__dismiss");r&&(p.preventDefault(),r.closest(".dm-banner")?.remove())}),$(()=>{se(document);const p=window.__CMS_NAV__||{},r=window.__CMS_SITE__||{};if(r.autoTheme?.enabled){let o=function(e){const a=(e||"07:00").split(":");return+a[0]*60+(+a[1]||0)},s=function(){const e=new Date,a=e.getHours()*60+e.getMinutes();return a>=o(n.dayStart)&&a<o(n.nightStart)?n.dayTheme:n.nightTheme},t=function(e){const a=n.apply&&n.apply[e]||{theme:e,custom:""};Domma.theme.set(a.theme);for(const l of[...document.body.classList])l.startsWith("dm-custom-")&&document.body.classList.remove(l);a.custom&&document.body.classList.add(a.custom)};const n=r.autoTheme;t(s()),setInterval(()=>t(s()),6e4)}const x=$("#site-navbar");if(x.length&&p.brand){let s=function(u,f=0){return Array.isArray(u)?u.filter(m=>!(m&&(m.type==="spacer"||m.type==="theme-switcher"))).filter(m=>!(m&&m.type==="separator"&&f===0)).map(m=>m&&m.type==="separator"?{divider:!0}:Array.isArray(m?.items)?{...m,items:s(m.items,f+1)}:m):u};const n={...p.brand},o=n.size&&n.size!=="md"?` navbar-brand-${n.size}`:"";if(n.logo||n.icon||n.tagline){let u="";n.logo?u+=`<img src="${n.logo}" class="navbar-brand-logo" alt="${n.text||""}">`:n.icon&&(u+=`<span data-icon="${n.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),n.text&&(u+=`<span class="navbar-brand-text${o}">${n.text}</span>`),n.tagline&&(u+=`<small class="navbar-brand-tagline">${n.tagline}</small>`),n.html=u}else o&&n.text&&(n.html=`<span class="navbar-brand-text${o}">${n.text}</span>`);const t=s(p.items||[]),e=p.position==="floating",a=p.orientation==="vertical";Domma.elements.navbar("#site-navbar",{brand:n,items:t,variant:p.variant||"dark",position:e?"static":p.position||"sticky",collapsible:!0,appearOnHover:!!p.appearOnHover}),Domma.icons.scan("#site-navbar");const l=x.get(0);ne(l,p.mobile);const h=()=>{if(!a)return;const f=l.querySelector('a[aria-current="page"]')?.closest(".navbar-dropdown");f&&!f.classList.contains("open")&&f.querySelector(":scope > .navbar-dropdown-toggle")?.click()};l.classList.add(`dm-nav-align-${de(p)||"centre"}`),a&&x.get(0).classList.add("dm-nav-vertical",p.side==="right"?"dm-nav-vertical-right":"dm-nav-vertical-left");const i=p.dock||{};a&&!e&&he(l,p.side==="right"),!a&&i.hideOnScroll&&ge(l),W(l,p),Y(l,p),O(l,p.class),e&&(re(x.get(0),p),me(x.get(0).parentNode||document));const c=l.querySelector(".navbar-toggle"),g=l.querySelector(".navbar-collapse");if(c&&g){let u=null;const f=()=>{g.classList.contains("show")&&c.click()},m=()=>{u&&(u.remove(),u=null),document.body.classList.remove("dm-drawer-open")},v=()=>{u||(u=document.createElement("div"),u.className="dm-navbar-backdrop",u.addEventListener("click",f),document.body.appendChild(u),document.body.classList.add("dm-drawer-open"))};c.addEventListener("click",()=>{requestAnimationFrame(()=>{g.classList.contains("show")?(h(),v()):m()})}),g.addEventListener("click",C=>{C.target.closest("a")&&requestAnimationFrame(m)}),document.addEventListener("keydown",C=>{C.key==="Escape"&&g.classList.contains("show")&&f()})}const b=l.querySelector(".navbar-nav");if(b){const u=Array.from(b.children);let f=0;for(const m of p.items||[])if(!(m&&m.type==="separator")){if(m&&m.type==="spacer"){const v=document.createElement("li"),C=["xs","sm","md","lg","xl","flex"].includes(m.size)?m.size:"md";v.className=`dm-menu-spacer dm-menu-spacer--${C}`,v.setAttribute("aria-hidden","true"),b.insertBefore(v,u[f]||null);continue}if(m&&m.type==="theme-switcher"){const v=document.createElement("li");v.className="dm-menu-theme-switcher";const C=document.createElement("span");C.dataset.themeSwitcher="menu",C.dataset.position="inline",v.appendChild(C),b.insertBefore(v,u[f]||null);continue}f++}window.CMSThemeSwitcher?.scan(b)}l.querySelectorAll("[data-path]").forEach(u=>{const f=u.dataset.path.split(".").map(Number),m=f.length>1,v=f.reduce((M,L)=>M?.items?.[L],{items:t});if(!v)return;v.colour&&U(u,v.colour),v.pill&&!m&&ie(u,v.pill),ce(u,v),O(u.closest("li"),v.class);const C=X(v.badge);C&&u.appendChild(C),G(v.url,location.pathname)&&(u.classList.add("active"),u.setAttribute("aria-current","page"))}),h()}const y=$("#site-footer");if(y.length){let e=function(d){return String(d??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},a=function(d){return e(d).replace(/'/g,"&#39;")},l=function(d){if(d&&d.type==="separator")return'<li class="footer-sep"><hr></li>';if(d&&d.type==="spacer")return`<li class="dm-menu-spacer dm-menu-spacer--${["xs","sm","md","lg","xl","flex"].includes(d.size)?d.size:"md"}" aria-hidden="true"></li>`;if(d&&d.type==="theme-switcher")return'<li class="dm-menu-theme-switcher"><span data-theme-switcher="menu" data-position="inline"></span></li>';const I=a(d.url||"#"),A=e(d.text||""),D=Array.isArray(d.items)&&d.items.length?`<ul>${d.items.map(l).join("")}</ul>`:"",z=le(d),te=[z.hover&&` data-menu-item-hover="${a(z.hover)}"`,z.active&&` data-menu-item-active="${a(z.active)}"`].filter(Boolean).join(""),oe=z.accent?` style="--dm-menu-highlight:${a(z.accent)}"`:"",J=V(d.class).join(" ");return`<li${J?` class="${a(J)}"`:""}><a href="${I}"${te}${oe}>${A}</a>${D}</li>`},h=function(){if(!r.footer)return"";const d=Object.keys(o).filter(A=>n[A]);if(!d.length)return"";const I=V(r.footer.socialClass).join(" ");return`<div class="footer-social">${d.map(A=>{const{label:D,svg:z}=o[A];return`<a href="${n[A]}" target="_blank" rel="noopener noreferrer" aria-label="${D}" class="footer-social-link${I?` ${a(I)}`:""}">${z}</a>`}).join("")}</div>`},b=function(d){if(!d||d.type==="separator"||d.type==="spacer")return"";if(d.type==="theme-switcher")return`<li class="cms-footer-col">${l(d).replace(/^<li[^>]*>|<\/li>$/g,"")}</li>`;const I=Array.isArray(d.items)&&d.items.length?`<ul>${d.items.map(l).join("")}</ul>`:"",A=g(d)?`<span class="cms-footer-col-title">${e(d.text||"")}</span>`:`<a class="cms-footer-col-title" href="${a(d.url||"#")}">${e(d.text||"")}</a>`,D=["cms-footer-col",...V(d.class)].join(" ");return`<li class="${a(D)}">${A}${I}</li>`},m=function(d,I,A=!1){return(d||[]).forEach(D=>{D&&(D.type==="separator"||D.type==="spacer"||D.type==="theme-switcher")||(A&&g(D)||I.push(D),Array.isArray(D.items)&&D.items.length&&m(D.items,I))}),I};const n=r.social||{},o={twitter:{label:"X / Twitter",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.742l7.73-8.835L1.254 2.25H8.08l4.259 5.629L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>'},facebook:{label:"Facebook",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>'},instagram:{label:"Instagram",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>'},linkedin:{label:"LinkedIn",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>'},github:{label:"GitHub",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'},youtube:{label:"YouTube",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.495 6.205a3.007 3.007 0 00-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 00.527 6.205a31.247 31.247 0 00-.522 5.805 31.247 31.247 0 00.522 5.783 3.007 3.007 0 002.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 002.088-2.088 31.247 31.247 0 00.5-5.783 31.247 31.247 0 00-.5-5.805zM9.609 15.601V8.408l6.264 3.602z"/></svg>'}};let s='<div class="footer-inner container">';const t=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""},i=["columns","row"].includes(r.footer?.layout)?r.footer.layout:"auto",c=i==="columns"||i==="auto"&&t.primary.some(d=>Array.isArray(d?.items)&&d.items.length),g=d=>c&&!d.url&&Array.isArray(d.items)&&d.items.length>0;if(c){const d=p.brand||{},I=d.text||r.title||"",A=d.logo?`<img src="${a(d.logo)}" class="cms-footer-brand-logo" alt="">`:d.icon?'<span class="cms-footer-brand-icon"></span>':"",D=String(r.footer?.blurb||"").trim();s='<div class="footer-inner footer-inner--columns container"><div class="cms-footer-top">',s+=`<div class="cms-footer-brand">${I||A?`<a class="cms-footer-brand-name" href="${a(d.url||"/")}">${A}<span>${e(I)}</span></a>`:""}${D?`<p class="cms-footer-blurb">${e(D)}</p>`:""}${h()}</div>`,t.primary.length&&(s+=`<nav class="footer-nav footer-nav--primary footer-nav--columns" aria-label="Footer"><ul>${t.primary.map(b).join("")}</ul></nav>`),s+='</div><div class="cms-footer-bottom">',t.copyright&&(s+=`<p class="cms-footer-copyright">${e(t.copyright)}</p>`),t.legal.length&&(s+=`<nav class="footer-nav footer-nav--legal" aria-label="Legal"><ul>${t.legal.map(l).join("")}</ul></nav>`),s+="</div>"}else t.copyright&&(s+=`<p>${e(t.copyright)}</p>`),t.primary.length&&(s+=`<nav class="footer-nav footer-nav--primary"><ul>${t.primary.map(l).join("")}</ul></nav>`),t.legal.length&&(s+=`<nav class="footer-nav footer-nav--legal"><ul>${t.legal.map(l).join("")}</ul></nav>`),s+=h();s+="</div>";const u=y.get(0).querySelector(":scope > .cms-footer-blocks");u?.remove(),y.html(s),u&&y.get(0).prepend(u),window.CMSThemeSwitcher?.scan(y.get(0));const f=y.get(0).querySelector(".cms-footer-brand-icon");f&&p.brand?.icon&&f.setAttribute("data-icon",p.brand.icon),O(y.get(0),r.footer?.class);const v=[...m(t.primary,[],!0),...m(t.legal,[])];y.get(0).querySelectorAll(".footer-nav a").forEach((d,I)=>{const A=v[I];if(!A)return;A.colour&&U(d,A.colour);const D=X(A.badge);D&&d.appendChild(D),G(A.url,location.pathname)&&(d.classList.add("is-active"),d.setAttribute("aria-current","page"))}),W(y.get(0).querySelector(".footer-nav--primary"),{highlight:t.primaryHighlight}),W(y.get(0).querySelector(".footer-nav--legal"),{highlight:t.legalHighlight}),Y(y.get(0).querySelector(".footer-nav--primary"),{entrance:t.primaryEntrance}),Y(y.get(0).querySelector(".footer-nav--legal"),{entrance:t.legalEntrance}),O(y.get(0).querySelector(".footer-nav--primary"),t.primaryClass),O(y.get(0).querySelector(".footer-nav--legal"),t.legalClass);const M=r.footer?.showMotionToggle!==!1,L=S.get("reduced_motion"),B=r.footer?.motionDefault,P=L!=null?!!L:B==="on"?!0:B==="off"?!1:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),q=r.footer?.motionControl==="checkbox",N=y.get(0).querySelector(".cms-footer-bottom")||y.get(0).querySelector(".footer-inner");if(M&&N){const d=document.createElement("input");d.type="checkbox",d.className=q?"footer-motion-checkbox":"form-switch-input",d.id="dm-motion-switch",d.checked=P,d.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const I=document.createElement("span");I.className=q?"footer-motion-label":"form-switch-label",I.textContent="Reduce motion";const A=document.createElement("label");A.className=q?"footer-motion-check":"form-switch footer-motion-switch",A.title="Reduce motion",A.appendChild(d),A.appendChild(I),N.appendChild(A)}}const k=document.getElementById("site-sidebar");k&&ye(k),Domma.icons.scan(),Domma.syntax?.scan&&Domma.syntax.scan();const w=$(".page-body");if(w.length){w.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),w.find(".tabs").each(function(){Domma.elements.tabs(this)}),w.find(".carousel").each(function(){const n={primary:"--dm-primary",secondary:"--dm-secondary",accent:"--dm-primary",text:"--dm-text",surface:"--dm-surface",background:"--dm-background",success:"--dm-success",warning:"--dm-warning",danger:"--dm-danger",info:"--dm-info"},o=h=>{const i=String(h||"").trim();return i?n[i.toLowerCase()]?`var(${n[i.toLowerCase()]})`:CSS.supports("color",i)?i:"":""},s=o(this.dataset.background),t=o(this.dataset.arrowBackground),e=o(this.dataset.arrowColor);s&&(this.style.setProperty("--dm-carousel-background",s),this.classList.add("dm-carousel-custom-background")),t&&this.style.setProperty("--dm-carousel-arrow-background",t),e&&this.style.setProperty("--dm-carousel-arrow-color",e);const a=["top","middle","bottom"].includes(this.dataset.arrowPosition)?this.dataset.arrowPosition:"middle";this.classList.add(`dm-carousel-arrows-${a}`);const l={autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide",heightMode:this.dataset.heightMode==="average"?"average":"crop"};if(this.dataset.height){const h=Number(this.dataset.height);Number.isFinite(h)&&h>0&&(l.height=h)}if(this.dataset.animationDuration){const h=parseInt(this.dataset.animationDuration,10);Number.isFinite(h)&&(l.animationDuration=h)}this.dataset.animationEasing&&(l.animationEasing=this.dataset.animationEasing),this.dataset.ctx&&(this._dmCtxSlides=Array.from(this.querySelectorAll(".carousel-slide")).map(h=>h.cloneNode(!0)),this._dmCarouselOpts=l),this._dmCarousel=Domma.elements.carousel(this,l),ae(this)}),w.find(".dm-countdown").each(function(){const n={autoStart:!0},o=String(this.dataset.format||"").trim(),s=String(this.dataset.to||"").trim(),t=/^(\d{4})-(\d{2})-(\d{2})$/.exec(s),e=t?new Date(+t[1],+t[2]-1,+t[3]).getTime():s?new Date(s).getTime():NaN;Number.isFinite(e)?n.duration=Math.max(0,e-Date.now()):this.dataset.duration&&(n.duration=(parseInt(this.dataset.duration,10)||0)*1e3);const a=/^dd:/i.test(o);n.format=a?"hh:mm:ss":o||(n.duration>=36e5?"hh:mm:ss":"mm:ss");const l=this,h=c=>String(c).padStart(2,"0"),i=c=>{const g=Math.max(0,Math.floor(c/1e3)),b=l.querySelector(".dm-timer-time");b&&(b.textContent=`${h(Math.floor(g/86400))}:${h(Math.floor(g%86400/3600))}:${h(Math.floor(g%3600/60))}:${h(g%60)}`)};a&&(n.onTick=i),Domma.elements.timer(this,n),a&&i(n.duration||0)}),w.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),w.find(".dm-progression").each(function(){const n=Array.from(this.querySelectorAll(".dm-progression-item")).map(t=>{const e={title:t.querySelector(".dm-progression-item-title")?.textContent?.trim()||"",description:t.querySelector(".dm-progression-item-body")?.innerHTML?.trim()||""};return t.dataset.date&&(e.date=t.dataset.date),t.dataset.status&&(e.status=t.dataset.status),t.dataset.eventIcon&&(e.icon=t.dataset.eventIcon),t.dataset.entryId&&(e._entryId=t.dataset.entryId),e}),o=this.dataset.layout==="centred"?"centered":this.dataset.layout||"vertical",s=Domma.elements.progression(this,{layout:o,theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0,allowHtml:!0,...n.length?{items:n}:{}});if((this.dataset.mode||"timeline")==="timeline"){const t={completed:{label:"Completed",icon:"check-circle",tone:"success"},planned:{label:"Planned",icon:"circle",tone:"secondary"},"in-progress":{label:"In progress",icon:"clock",tone:"primary"},blocked:{label:"Blocked",icon:"alert-circle",tone:"danger"}};this.querySelectorAll(".dm-progression-item").forEach((e,a)=>{const l=n[a];if(!l)return;const h=e.querySelector(".dm-progression-title");if(l.icon&&h){const c=document.createElement("span");c.dataset.icon=l.icon,c.style.cssText="display:inline-flex;vertical-align:middle;margin-right:.4em",c.setAttribute("aria-hidden","true"),h.prepend(c)}const i=t[l.status];if(i&&h){const c=document.createElement("span");c.className=`badge badge-${i.tone} cms-timeline-status`,c.style.cssText="display:inline-flex;align-items:center;gap:.35em;margin:.4rem 0";const g=document.createElement("span");g.dataset.icon=i.icon,g.dataset.iconSize="14",g.setAttribute("aria-hidden","true"),c.append(g,document.createTextNode(i.label)),h.after(c)}})}Domma.icons.scan(this),n.length&&(this._dmCtx={instance:s,items:n})});try{Domma.effects.reveal(".page-body .hero",{animation:"slide-up",duration:480,threshold:.06,stagger:60,once:!1})}catch{}document.querySelectorAll(".page-body .row[data-reveal]").forEach(n=>{const o=n.dataset.revealMode||"stagger",s=n.dataset.revealAnimation||"slide-up",t=parseInt(n.dataset.revealDuration,10)||400,e=parseInt(n.dataset.revealStagger,10)||60,a=parseInt(n.dataset.revealDelay,10)||0,l=n.dataset.revealDirection==="rtl",h=Array.from(n.children),i={"slide-up":"translateY(30px)","slide-down":"translateY(-30px)","slide-left":"translateX(30px)","slide-right":"translateX(-30px)",zoom:"scale(0.85)",flip:"perspective(600px) rotateX(15deg)"};h.forEach((c,g)=>{c.style.opacity="0",c.style.transform=i[s]||"",c.style.transition=`opacity ${t}ms ease, transform ${t}ms ease`;const b=l?h.length-1-g:g;c.style.transitionDelay=o==="stagger"?`${a+b*e}ms`:`${a}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const c=new IntersectionObserver(g=>{g.forEach(b=>{b.isIntersecting&&(b.target.offsetWidth,b.target.style.opacity="1",b.target.style.transform="none",c.unobserve(b.target))})},{threshold:.1});h.forEach(g=>c.observe(g))}))}),w.find(".card[data-collapsible]").each(function(){const n=this.querySelector(".card-header");n&&n.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),w.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const n=this.dataset.soTarget,o=document.getElementById(n);if(!o)return;const s=E.slideover({title:o.dataset.soTitle||"",size:o.dataset.soSize||"md",position:o.dataset.soPosition||"right"});o.style.display="",s.setContent(o),s.open()})})}if(typeof $.setup=="function"){const n=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(o=>{try{const s=R(o.dataset.config);Object.assign(n,s)}catch{}}),Object.keys(n).length>0){const o={};for(const[s,t]of Object.entries(n)){const e=t?.events?.click,{confirm:a,toast:l,alert:h,prompt:i,...c}=e||{};a||l||h||i?($(s).on("click",async function(b){if(b.preventDefault(),a&&!await E.confirm(a))return;let u=null;if(!(i&&(u=await E.prompt(i,{inputPlaceholder:c.promptPlaceholder||"",inputValue:c.promptDefault||""}),u===null))){if(c.target){const f=$(c.target);c.toggleClass&&f.toggleClass(c.toggleClass),c.addClass&&f.addClass(c.addClass),c.removeClass&&f.removeClass(c.removeClass),u!==null&&(c.setText&&f.text(u),c.setVal&&f.val(u),c.setAttr&&f.attr(c.setAttr,u))}c.href&&(window.location.href=c.href),l&&E.toast(l,{type:c.toastType||"success"}),h&&E.alert(h)}}),Object.keys(c).length&&(o[s]={...t,events:{...t.events,click:c}})):o[s]=t}$.setup(o)}}w.length&&j(w.get(0))}),window.wireCTAButtons=j;function j(p){p.querySelectorAll(".dm-cta-trigger").forEach(r=>{r.__ctaWired||(r.__ctaWired=!0,r.addEventListener("click",async()=>{const x=r.dataset.action,y=r.dataset.entry,k=r.dataset.confirm;let w=S.get("auth_token");if(!w){E.toast("Please log in to perform this action.",{type:"warning"});return}if(k&&!await E.confirm(k))return;const n=Array.from(r.childNodes).map(s=>s.cloneNode(!0));r.disabled=!0,r.textContent="Running\u2026";const o=s=>fetch(`/api/actions/${x}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify(r.dataset.scope?{entryId:y,scope:r.dataset.scope}:{entryId:y})});try{let s=await o(w);if(s.status===401){const e=S.get("auth_refresh_token");if(e){const a=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(a.ok){const{token:l}=await a.json();S.set("auth_token",l),w=l,s=await o(w)}}}const t=await s.json().catch(()=>({}));if(!s.ok||t.success===!1)throw new Error(t.error||t.message||`Error ${s.status}`);E.toast(t.message||"Action completed.",{type:"success"})}catch(s){E.toast(s.message||"Action failed.",{type:"error"})}finally{r.disabled=!1,r.textContent="",n.forEach(s=>r.appendChild(s)),Domma.icons.scan(r)}}))})}(function(){const r=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!r.length)return;const x=S.get("auth_token");function y(w,n){const o=document.createElement("div");o.className="dm-collection-display dm-collection-empty";const s=document.createElement("p");if(n){s.appendChild(document.createTextNode("Please "));const t=document.createElement("a");t.href="/login",t.textContent="sign in",s.appendChild(t),s.appendChild(document.createTextNode(" to view your entries."))}else s.textContent=w;return o.appendChild(s),o}function k(w,n){const o=document.createElement("template");o.innerHTML=n||"",w.replaceWith(o.content)}r.forEach(async w=>{const n=w.dataset.collectionAttrs;if(!n)return;if(!x){w.replaceChildren(y("",!0));return}const o=s=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({attrs:n})});try{let s=x,t=await o(s);if(t.status===401){const a=S.get("auth_refresh_token");if(a){const l=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:a})});if(l.ok){const{token:h}=await l.json();S.set("auth_token",h),s=h,t=await o(s)}}}if(!t.ok){const{error:a}=await t.json().catch(()=>({}));w.replaceChildren(y(a||"Unable to load entries.",!1));return}const{html:e}=await t.json();k(w,e),window.DommaCollectionBrowser?.hydrate&&window.DommaCollectionBrowser.hydrate(document)}catch{w.replaceChildren(y("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),typeof Domma<"u"&&Domma.syntax?.scan&&Domma.syntax.scan(),j(document.body)},100)})(),(function(){const r=document.querySelectorAll("[data-collection-table]");!r.length||typeof T>"u"||!T.create||r.forEach(x=>{let y;try{y=R(x.dataset.payload)}catch{return}const{columns:k,rows:w,search:n,sortable:o,exportable:s,pageSize:t,empty:e,ctaConfig:a,slug:l,exportCfg:h}=y;if(!k?.length)return;if(a){const g=k.findIndex(b=>b.key==="_cta");g!==-1&&(k[g]={key:"_cta",title:"",render:(b,u)=>{const f=document.createElement("button");if(f.className=`btn btn-${a.style||"primary"} dm-cta-trigger`,f.dataset.action=a.action||"",a.scope&&(f.dataset.scope=a.scope),f.dataset.entry=u._entryId||"",a.confirm&&(f.dataset.confirm=a.confirm),a.icon){const m=document.createElement("span");m.dataset.icon=a.icon,f.appendChild(m),f.appendChild(document.createTextNode(" "))}return f.appendChild(document.createTextNode(a.label||"Run")),f}})}const i="col-table-"+Math.random().toString(36).slice(2,7),c=document.createElement("div");c.id=i,x.replaceChildren(c),T.create("#"+i,{data:w,columns:k,search:n,sortable:o,exportable:s,pageSize:t,emptyMessage:e}),a&&j(c),x._dmTable={columns:k,rows:w,slug:l,exportCfg:h,render(g){T.create("#"+i,{data:g,columns:k,search:n,sortable:o,exportable:s,pageSize:t,emptyMessage:e}),a&&j(c)}}})})(),(function(){const r=document.querySelectorAll("[data-form-inline]");if(!r.length||typeof F>"u")return;function x(o){const s=o&&o.logic;return!!(s&&(s.visibility||s.requirement))}function y(o,s){const t={};return(o||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const a=e.type==="chooser"||!!(e.variant&&["select","radio","checkbox-group"].includes(e.type)),l=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":a?"chooser":e.type,h={...e.formConfig||{}};h.span==="full"&&s&&(h.span=s);const i={type:l,required:x(e)?!1:e.required,label:e.label,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{hint:e.helper},...e.tooltip&&{tooltip:e.tooltip},...h}};e.minLength!==void 0&&(i.minLength=e.minLength),e.maxLength!==void 0&&(i.maxLength=e.maxLength),e.min!==void 0&&(i.min=e.min),e.max!==void 0&&(i.max=e.max),a&&(e.variant&&(i.variant=e.variant),(e.multiple||e.type==="checkbox-group")&&(i.multiple=!0),e.density&&(i.density=e.density),e.columns&&(i.columns=e.columns),e.accent&&(i.accent=e.accent),e.accentStyle&&(i.accentStyle=e.accentStyle),e.glow&&(i.glow=!0),e.glowColour&&(i.glowColour=e.glowColour),e.shadow&&(i.shadow=e.shadow),e.shadowColour&&(i.shadowColour=e.shadowColour)),t[e.name]=i}),t}function k(o){const s={};return(o||[]).forEach(t=>{if(!(!t.name||t.type==="page-break"||t.type==="spacer")&&(t.type==="select"||t.type==="multiselect")&&t.required){const e=(t.options||[])[0];e&&(s[t.name]=typeof e=="object"?e.value:e)}}),s}function w(o,s){(s||[]).forEach(t=>{if(t.type!=="date"||!t.name)return;const e=o.querySelector(`[name="${t.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function n(o,s,t){let e=o.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",o.appendChild(e)),e.textContent=s,e.style.cssText=t?"color:var(--dm-danger);margin-top:.75rem;":"color:var(--dm-success);margin-top:.75rem;"}r.forEach(o=>{let s;try{s=R(o.dataset.formInline)}catch{return}const t=s.fields||[],e=s.settings||{},a=Number(e.columns)===3?3:2,l=e.layout||"grid",h=t.some(f=>f.type==="page-break"),i=async f=>{if(o.dataset.triggerBlocked==="true"){const m=o._formLogicRuntime,v=m&&m.submitBlock?m.submitBlock().reasons:[];n(o,v.join(" ")||"This form cannot be submitted yet.",!0);const C=new Error("Blocked by a form trigger");throw C.formSubmitFailed=!0,C}try{const m=o.querySelector(".fb-form-honeypot"),v=m?.querySelector('input[type="text"]')||null,C=m?.querySelector('[name="_t"]')||null,M=Object.assign({},f);delete M._t,m?.dataset.hpName&&delete M[m.dataset.hpName],v!==null&&(M._hp=v.value),C!==null&&(M._t=C.value);const L=await H.post(`/api/forms/submit/${s.slug}`,M);if(L&&L.error){n(o,L.error,!0);const B=new Error(L.error);throw B.formSubmitFailed=!0,B}if(L?.redirect){window.location.href=L.redirect;return}for(;o.firstChild;)o.removeChild(o.firstChild);n(o,L?.message||e.successMessage||"Thank you for your submission.",!1)}catch(m){throw m.formSubmitFailed||n(o,m.message||"Submission failed. Please try again.",!0),m}},c=Date.now();function g(f){const m=f.querySelector("form");if(!m||m.querySelector(".fb-form-honeypot"))return;const v=document.createElement("div");v.className="fb-form-honeypot",v.setAttribute("aria-hidden","true");const C=document.createElement("input");C.name=m.querySelector('[name="website"]')?"_website":"website",v.dataset.hpName=C.name,C.type="text",C.tabIndex=-1,C.autocomplete="url",C.placeholder="https://",v.appendChild(C);const M=document.createElement("input");M.name="_t",M.type="hidden",M.value=c,v.appendChild(M),m.appendChild(v)}function b(f){if(!window.FormLogicEngine||!t.some(v=>v.logic||Array.isArray(v.triggers)&&v.triggers.length))return null;const m=new window.FormLogicEngine.FormLogicRuntime(s,o,f?{baseValues:f}:void 0);if(m.init(),o._formLogicRuntime=m,o._formLogicObserved)return m;if(o._formLogicObserved=!0,o.parentNode&&typeof MutationObserver<"u"){const v=new MutationObserver(function(C){for(const M of C)for(const L of M.removedNodes)if(L===o||L.nodeType===1&&L.contains&&L.contains(o)){o._formLogicRuntime&&o._formLogicRuntime.destroy(),v.disconnect();return}});v.observe(o.parentNode,{childList:!0,subtree:!1}),o._formLogicObserver=v}return m}let u=!1;if(o.addEventListener("dm-form-end",f=>{const m=f.detail?.validation;if(m&&!m.valid){Q(o,m.missing),Z(m);return}u||(u=!0,i(f.detail?.values||{}).catch(()=>{}).finally(()=>{u=!1}))}),h&&F.wizard){const f=ue(t,s.title),m=f.map(q=>({title:q.title,description:q.description,fields:y(q.fields,a)})),v=f.map(q=>q.fields);let C=0,M=null,L=null;const B=q=>{const N=window.FormLogicEngine;if(!N||!N.validateFields)return;const d=N.validateFields(o,v[C]||[]);d.valid||(q.preventDefault(),q.stopImmediatePropagation(),Q(o,d.missing),Z(d))},P=()=>{const q=v[C]||t;fe(o,e),ee(o,e,q),w(o,q),K(o,q),e.honeypot!==!1&&g(o),M&&M.destroy(),M=b(L?.getStepData?L.getStepData():void 0),o.querySelectorAll(".wizard-next, .wizard-submit").forEach(N=>N.addEventListener("click",B,!0))};L=F.wizard(o,{schema:{steps:m},layout:l,columns:a,finishText:e.submitText||"Finish",onSubmit:i,onStepChange:q=>{C=q,setTimeout(P,0)}}),o.addEventListener("dm-form-trigger",q=>{if(q.detail?.type!=="goto-step"||!L?.goToStep)return;const N=Number(q.detail.action.step);!Number.isFinite(N)||N<0||N>=m.length||N!==C&&L.goToStep(N)}),Promise.resolve(L).then(P)}else if(F.render){const f=F.render(o,y(t,a),k(t),{submitText:e.submitText||"Submit",layout:l,columns:a,onSubmit:i});Promise.resolve(f).then(function(){pe(o,e,l),ee(o,e,t),w(o,t),K(o,t),e.honeypot!==!1&&g(o),b()})}})})();function he(p,r){const x=document.body,y=x.classList,k=new Set((x.dataset.railSite||"").split(" ").filter(Boolean)),w=()=>window.matchMedia("(min-width: 993px)").matches;p.querySelectorAll(".navbar-link, .navbar-dropdown-toggle").forEach(a=>{const l=a.textContent.trim();l&&(a.setAttribute("title",l),a.querySelector("svg, [data-icon]")||(a.dataset.initial=l.charAt(0).toUpperCase()))});const n=document.createElement("button");n.type="button",n.className="dm-nav-collapse-toggle";const o=()=>{const a=y.contains("dm-vnav-collapsed");n.setAttribute("aria-pressed",String(a)),n.setAttribute("aria-label",a?"Expand menu":"Collapse menu"),n.title=n.getAttribute("aria-label");const l=r?"chevrons-left":"chevrons-right",h=r?"chevrons-right":"chevrons-left";n.innerHTML=`<span data-icon="${a?l:h}"></span>`,window.Domma&&Domma.icons&&Domma.icons.scan(n)},s=a=>{y.toggle("dm-vnav-collapsed",a),S.set("nav_collapsed",a),o()};n.addEventListener("click",()=>s(!y.contains("dm-vnav-collapsed"))),o();const t=p.querySelector(".navbar-container")||p;t.insertBefore(n,t.firstChild?.nextSibling||null);const e=a=>{y.toggle("dm-vnav-autohide",a),y.toggle("dm-vnav-collapsible",!a&&k.has("collapsible")),a?y.remove("dm-vnav-collapsed"):k.has("collapsible")&&S.get("nav_collapsed")===!0&&y.add("dm-vnav-collapsed"),p.classList.remove("dm-nav-revealed"),o()};p.addEventListener("pointerdown",a=>{y.contains("dm-vnav-autohide")&&a.pointerType!=="mouse"&&!p.classList.contains("dm-nav-revealed")&&(p.classList.add("dm-nav-revealed"),a.preventDefault())}),document.addEventListener("pointerdown",a=>{p.contains(a.target)||p.classList.remove("dm-nav-revealed")}),window.E&&typeof E.contextMenu=="function"&&E.contextMenu(p,{items:()=>{const a=y.contains("dm-vnav-autohide"),l=y.contains("dm-vnav-collapsed"),h=S.has("nav_autohide")||S.has("nav_collapsed");return[{header:"Menu"},{label:l?"Expand menu":"Collapse to icons",icon:l?"maximize":"minimize",disabled:!w()||a||!k.has("collapsible"),action:()=>s(!l)},{label:a?"Keep on screen":"Hide when not in use",icon:a?"eye":"eye-off",disabled:!w(),action:()=>{S.set("nav_autohide",!a),e(!a)}},{type:"divider"},{label:"Reset to the site's setting",icon:"refresh",disabled:!h,action:()=>{S.remove("nav_autohide"),S.remove("nav_collapsed"),e(k.has("autohide")),y.toggle("dm-vnav-collapsed",!k.has("autohide")&&k.has("start-collapsed")),o()}}]}})}function ge(p){p.classList.add("dm-nav-scroll-hide");let r=window.scrollY,x=!1;window.addEventListener("scroll",()=>{x||(x=!0,requestAnimationFrame(()=>{x=!1;const y=window.scrollY,k=p.matches(":focus-within")||p.querySelector(".navbar-dropdown.open, .navbar-collapse.show");y>r&&y>p.offsetHeight&&!k?p.classList.add("dm-nav-scrolled-away"):y<r&&p.classList.remove("dm-nav-scrolled-away"),r=y}))},{passive:!0})}function ye(p){let r={};try{r=JSON.parse(p.dataset.contents||"{}")}catch{}const x=p.closest(".site-main"),y=document.querySelector(".site-content"),k=new Set(String(r.exclude||"").split(",").map(i=>i.trim().toLowerCase()).filter(Boolean)),w=i=>!i.closest(".hero, .card, .accordion, .tabs, figure, #site-sidebar"),n=i=>(y?[...y.querySelectorAll(i)]:[]).filter(w).filter(c=>!k.has(c.textContent.trim().toLowerCase())),o=r.headings==="h2"?n("h2"):r.headings==="h3"?n("h3"):r.headings==="h2h3"?n("h2, h3"):n("h2").length?n("h2"):n("h3");if(!o.length){x?.classList.add("no-sidebar-items");return}const s=new Set([...document.querySelectorAll("[id]")].map(i=>i.id)),t=document.createElement("nav");t.className="dm-contents",t.setAttribute("aria-label",r.title||"Page contents"),r.hover&&(t.dataset.menuHover=r.hover),r.selected&&(t.dataset.menuActive=r.selected),r.accent&&t.style.setProperty("--dm-menu-highlight",r.accent.startsWith("#")?r.accent:`var(--dm-${r.accent})`),r.font&&(t.style.fontFamily=r.font),r.fontSize&&(t.style.fontSize=r.fontSize),r.fontWeight&&(t.style.fontWeight=r.fontWeight);const e=document.createElement("div");e.className="dm-contents-head";const a=document.createElement("p");a.className="dm-contents-title",a.textContent=r.title||"",e.appendChild(a),t.appendChild(e);const l=document.createElement("ol");l.className="dm-contents-list"+(r.numbered?" is-numbered":"");const h=[];for(const i of o){if(!i.id){const b=(i.textContent||"").toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"section";let u=b;for(let f=2;s.has(u);f++)u=`${b}-${f}`;s.add(u),i.id=u}const c=document.createElement("li");c.className=`dm-contents-item dm-contents-${i.tagName.toLowerCase()}`;const g=document.createElement("a");g.href=`#${i.id}`,g.textContent=i.textContent.trim(),c.appendChild(g),l.appendChild(c),h.push([i,g])}if(t.appendChild(l),p.replaceChildren(t),r.scrollSpy!==!1&&"IntersectionObserver"in window){const i=new Map,c=()=>{const b=h.find(([u])=>i.get(u))||null;h.forEach(([,u])=>{const f=b&&u===b[1];u.classList.toggle("is-active",!!f),f?u.setAttribute("aria-current","location"):u.removeAttribute("aria-current")})},g=new IntersectionObserver(b=>{b.forEach(u=>i.set(u.target,u.isIntersecting)),c()},{rootMargin:"-80px 0px -60% 0px"});h.forEach(([b])=>g.observe(b))}if(x?.classList.contains("contents-collapsible")){const i=document.createElement("button");i.type="button",i.className="dm-contents-toggle";const c=()=>{const g=x.classList.contains("contents-collapsed");i.setAttribute("aria-expanded",String(!g)),i.setAttribute("aria-label",g?"Show the contents":"Hide the contents"),i.title=i.getAttribute("aria-label"),i.innerHTML=`<span data-icon="${g?"list":"x"}"></span>`,window.Domma?.icons&&Domma.icons.scan(i)};i.addEventListener("click",()=>{S.set("contents_collapsed",x.classList.toggle("contents-collapsed")),c()}),c(),e.insertBefore(i,e.firstChild)}if(x?.classList.contains("contents-floating")&&r.movable){t.classList.add("is-movable");const i=S.get("contents_position");i&&Number.isFinite(i.x)&&Object.assign(p.style,{left:`${i.x}px`,top:`${i.y}px`,right:"auto",bottom:"auto"}),e.addEventListener("pointerdown",c=>{if(c.target.closest("button"))return;const g=p.getBoundingClientRect(),b=c.clientX-g.left,u=c.clientY-g.top,f=v=>{const C=Math.max(0,Math.min(window.innerWidth-g.width,v.clientX-b)),M=Math.max(0,Math.min(window.innerHeight-40,v.clientY-u));Object.assign(p.style,{left:`${C}px`,top:`${M}px`,right:"auto",bottom:"auto"})},m=()=>{document.removeEventListener("pointermove",f),S.set("contents_position",{x:parseInt(p.style.left,10),y:parseInt(p.style.top,10)})};document.addEventListener("pointermove",f),document.addEventListener("pointerup",m,{once:!0})})}window.E&&typeof E.contextMenu=="function"&&E.contextMenu(p,{items:()=>{const i=x?.classList.contains("contents-collapsible"),c=x?.classList.contains("contents-collapsed"),g=h.find(([,b])=>b.classList.contains("is-active"));return[{header:"Contents"},...i?[{label:c?"Show the contents":"Collapse to a button",icon:c?"list":"minimize",action:()=>p.querySelector(".dm-contents-toggle")?.click()}]:[],{label:"Copy a link to this section",icon:"link",disabled:!g,action:async()=>{const b=`${location.origin}${location.pathname}#${g[0].id}`;try{await _.copyToClipboard(b),E.toast("Link copied.",{type:"success"})}catch{E.toast("Could not copy the link.",{type:"error"})}}},{label:"Back to the top",icon:"arrow-up",action:()=>window.scrollTo({top:0,behavior:"smooth"})}]}}),x?.classList.contains("contents-autohide")&&(p.addEventListener("pointerdown",i=>{i.pointerType!=="mouse"&&!p.classList.contains("is-revealed")&&(p.classList.add("is-revealed"),i.preventDefault())}),document.addEventListener("pointerdown",i=>{p.contains(i.target)||p.classList.remove("is-revealed")}))}
1
+ import{applyMobileMenu as ne,initCarouselPopup as ae}from"/public/js/mobile-ui.mjs?v=20261016-mobile-options";import{installSmoothCarets as se}from"/public/js/smooth-caret.js";import{decodeBase64Json as R}from"/public/js/utf8-base64.mjs";import{makeBadgeEl as X,applyColour as U,applyPill as ie,applyFloat as re,applyHighlight as W,applyItemHighlight as ce,itemHighlightAttrs as le,isCurrentItem as G,menuAlign as de,applyClasses as O,cssClasses as V,applyEntrance as Y}from"/public/js/menu-decor.mjs?v=20261014-menu-entrance";import{initFloatingMenus as me}from"/public/js/menu-float.js?v=20260829-float-panel";import{enhancePickerFields as K,markInvalidFields as Q,showValidationPopup as Z}from"/public/js/forms.js?v=20261015-smooth-caret";import{buildWizardSteps as ue}from"/public/js/form-steps.mjs";import{applyFormClasses as ee,applySubmitButtons as pe,applyWizardButtons as fe}from"/public/js/form-submit.mjs";document.addEventListener("click",p=>{const r=p.target?.closest?.(".dm-banner__dismiss");r&&(p.preventDefault(),r.closest(".dm-banner")?.remove())}),$(()=>{se(document);const p=window.__CMS_NAV__||{},r=window.__CMS_SITE__||{};if(r.autoTheme?.enabled){let o=function(e){const a=(e||"07:00").split(":");return+a[0]*60+(+a[1]||0)},s=function(){const e=new Date,a=e.getHours()*60+e.getMinutes();return a>=o(n.dayStart)&&a<o(n.nightStart)?n.dayTheme:n.nightTheme},t=function(e){const a=n.apply&&n.apply[e]||{theme:e,custom:""};Domma.theme.set(a.theme);for(const l of[...document.body.classList])l.startsWith("dm-custom-")&&document.body.classList.remove(l);a.custom&&document.body.classList.add(a.custom)};const n=r.autoTheme;t(s()),setInterval(()=>t(s()),6e4)}const x=$("#site-navbar");if(x.length&&p.brand){let s=function(u,f=0){return Array.isArray(u)?u.filter(m=>!(m&&(m.type==="spacer"||m.type==="theme-switcher"))).filter(m=>!(m&&m.type==="separator"&&f===0)).map(m=>m&&m.type==="separator"?{divider:!0}:Array.isArray(m?.items)?{...m,items:s(m.items,f+1)}:m):u};const n={...p.brand},o=n.size&&n.size!=="md"?` navbar-brand-${n.size}`:"";if(n.logo||n.icon||n.tagline){let u="";n.logo?u+=`<img src="${n.logo}" class="navbar-brand-logo" alt="${n.text||""}">`:n.icon&&(u+=`<span data-icon="${n.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),n.text&&(u+=`<span class="navbar-brand-text${o}">${n.text}</span>`),n.tagline&&(u+=`<small class="navbar-brand-tagline">${n.tagline}</small>`),n.html=u}else o&&n.text&&(n.html=`<span class="navbar-brand-text${o}">${n.text}</span>`);const t=s(p.items||[]),e=p.position==="floating",a=p.orientation==="vertical";x.get(0).querySelector(":scope > .cms-nojs-links")?.remove(),Domma.elements.navbar("#site-navbar",{brand:n,items:t,variant:p.variant||"dark",position:e?"static":p.position||"sticky",collapsible:!0,appearOnHover:!!p.appearOnHover}),Domma.icons.scan("#site-navbar");const l=x.get(0);ne(l,p.mobile);const h=()=>{if(!a)return;const f=l.querySelector('a[aria-current="page"]')?.closest(".navbar-dropdown");f&&!f.classList.contains("open")&&f.querySelector(":scope > .navbar-dropdown-toggle")?.click()};l.classList.add(`dm-nav-align-${de(p)||"centre"}`),a&&x.get(0).classList.add("dm-nav-vertical",p.side==="right"?"dm-nav-vertical-right":"dm-nav-vertical-left");const i=p.dock||{};a&&!e&&he(l,p.side==="right"),!a&&i.hideOnScroll&&ge(l),W(l,p),Y(l,p),O(l,p.class),e&&(re(x.get(0),p),me(x.get(0).parentNode||document));const c=l.querySelector(".navbar-toggle"),g=l.querySelector(".navbar-collapse");if(c&&g){let u=null;const f=()=>{g.classList.contains("show")&&c.click()},m=()=>{u&&(u.remove(),u=null),document.body.classList.remove("dm-drawer-open")},v=()=>{u||(u=document.createElement("div"),u.className="dm-navbar-backdrop",u.addEventListener("click",f),document.body.appendChild(u),document.body.classList.add("dm-drawer-open"))};c.addEventListener("click",()=>{requestAnimationFrame(()=>{g.classList.contains("show")?(h(),v()):m()})}),g.addEventListener("click",C=>{C.target.closest("a")&&requestAnimationFrame(m)}),document.addEventListener("keydown",C=>{C.key==="Escape"&&g.classList.contains("show")&&f()})}const b=l.querySelector(".navbar-nav");if(b){const u=Array.from(b.children);let f=0;for(const m of p.items||[])if(!(m&&m.type==="separator")){if(m&&m.type==="spacer"){const v=document.createElement("li"),C=["xs","sm","md","lg","xl","flex"].includes(m.size)?m.size:"md";v.className=`dm-menu-spacer dm-menu-spacer--${C}`,v.setAttribute("aria-hidden","true"),b.insertBefore(v,u[f]||null);continue}if(m&&m.type==="theme-switcher"){const v=document.createElement("li");v.className="dm-menu-theme-switcher";const C=document.createElement("span");C.dataset.themeSwitcher="menu",C.dataset.position="inline",v.appendChild(C),b.insertBefore(v,u[f]||null);continue}f++}window.CMSThemeSwitcher?.scan(b)}l.querySelectorAll("[data-path]").forEach(u=>{const f=u.dataset.path.split(".").map(Number),m=f.length>1,v=f.reduce((q,L)=>q?.items?.[L],{items:t});if(!v)return;v.colour&&U(u,v.colour),v.pill&&!m&&ie(u,v.pill),ce(u,v),O(u.closest("li"),v.class);const C=X(v.badge);C&&u.appendChild(C),G(v.url,location.pathname)&&(u.classList.add("active"),u.setAttribute("aria-current","page"))}),h()}const y=$("#site-footer");if(y.length){let e=function(d){return String(d??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},a=function(d){return e(d).replace(/'/g,"&#39;")},l=function(d){if(d&&d.type==="separator")return'<li class="footer-sep"><hr></li>';if(d&&d.type==="spacer")return`<li class="dm-menu-spacer dm-menu-spacer--${["xs","sm","md","lg","xl","flex"].includes(d.size)?d.size:"md"}" aria-hidden="true"></li>`;if(d&&d.type==="theme-switcher")return'<li class="dm-menu-theme-switcher"><span data-theme-switcher="menu" data-position="inline"></span></li>';const I=a(d.url||"#"),A=e(d.text||""),D=Array.isArray(d.items)&&d.items.length?`<ul>${d.items.map(l).join("")}</ul>`:"",z=le(d),te=[z.hover&&` data-menu-item-hover="${a(z.hover)}"`,z.active&&` data-menu-item-active="${a(z.active)}"`].filter(Boolean).join(""),oe=z.accent?` style="--dm-menu-highlight:${a(z.accent)}"`:"",J=V(d.class).join(" ");return`<li${J?` class="${a(J)}"`:""}><a href="${I}"${te}${oe}>${A}</a>${D}</li>`},h=function(){if(!r.footer)return"";const d=Object.keys(o).filter(A=>n[A]);if(!d.length)return"";const I=V(r.footer.socialClass).join(" ");return`<div class="footer-social">${d.map(A=>{const{label:D,svg:z}=o[A];return`<a href="${n[A]}" target="_blank" rel="noopener noreferrer" aria-label="${D}" class="footer-social-link${I?` ${a(I)}`:""}">${z}</a>`}).join("")}</div>`},b=function(d){if(!d||d.type==="separator"||d.type==="spacer")return"";if(d.type==="theme-switcher")return`<li class="cms-footer-col">${l(d).replace(/^<li[^>]*>|<\/li>$/g,"")}</li>`;const I=Array.isArray(d.items)&&d.items.length?`<ul>${d.items.map(l).join("")}</ul>`:"",A=g(d)?`<span class="cms-footer-col-title">${e(d.text||"")}</span>`:`<a class="cms-footer-col-title" href="${a(d.url||"#")}">${e(d.text||"")}</a>`,D=["cms-footer-col",...V(d.class)].join(" ");return`<li class="${a(D)}">${A}${I}</li>`},m=function(d,I,A=!1){return(d||[]).forEach(D=>{D&&(D.type==="separator"||D.type==="spacer"||D.type==="theme-switcher")||(A&&g(D)||I.push(D),Array.isArray(D.items)&&D.items.length&&m(D.items,I))}),I};const n=r.social||{},o={twitter:{label:"X / Twitter",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.742l7.73-8.835L1.254 2.25H8.08l4.259 5.629L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>'},facebook:{label:"Facebook",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>'},instagram:{label:"Instagram",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>'},linkedin:{label:"LinkedIn",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>'},github:{label:"GitHub",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>'},youtube:{label:"YouTube",svg:'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.495 6.205a3.007 3.007 0 00-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 00.527 6.205a31.247 31.247 0 00-.522 5.805 31.247 31.247 0 00.522 5.783 3.007 3.007 0 002.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 002.088-2.088 31.247 31.247 0 00.5-5.783 31.247 31.247 0 00-.5-5.805zM9.609 15.601V8.408l6.264 3.602z"/></svg>'}};let s='<div class="footer-inner container">';const t=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""},i=["columns","row"].includes(r.footer?.layout)?r.footer.layout:"auto",c=i==="columns"||i==="auto"&&t.primary.some(d=>Array.isArray(d?.items)&&d.items.length),g=d=>c&&!d.url&&Array.isArray(d.items)&&d.items.length>0;if(c){const d=p.brand||{},I=d.text||r.title||"",A=d.logo?`<img src="${a(d.logo)}" class="cms-footer-brand-logo" alt="">`:d.icon?'<span class="cms-footer-brand-icon"></span>':"",D=String(r.footer?.blurb||"").trim();s='<div class="footer-inner footer-inner--columns container"><div class="cms-footer-top">',s+=`<div class="cms-footer-brand">${I||A?`<a class="cms-footer-brand-name" href="${a(d.url||"/")}">${A}<span>${e(I)}</span></a>`:""}${D?`<p class="cms-footer-blurb">${e(D)}</p>`:""}${h()}</div>`,t.primary.length&&(s+=`<nav class="footer-nav footer-nav--primary footer-nav--columns" aria-label="Footer"><ul>${t.primary.map(b).join("")}</ul></nav>`),s+='</div><div class="cms-footer-bottom">',t.copyright&&(s+=`<p class="cms-footer-copyright">${e(t.copyright)}</p>`),t.legal.length&&(s+=`<nav class="footer-nav footer-nav--legal" aria-label="Legal"><ul>${t.legal.map(l).join("")}</ul></nav>`),s+="</div>"}else t.copyright&&(s+=`<p>${e(t.copyright)}</p>`),t.primary.length&&(s+=`<nav class="footer-nav footer-nav--primary"><ul>${t.primary.map(l).join("")}</ul></nav>`),t.legal.length&&(s+=`<nav class="footer-nav footer-nav--legal"><ul>${t.legal.map(l).join("")}</ul></nav>`),s+=h();s+="</div>",y.get(0).querySelector(":scope > .cms-nojs-links")?.remove();const u=y.get(0).querySelector(":scope > .cms-footer-blocks");u?.remove(),y.html(s),u&&y.get(0).prepend(u),window.CMSThemeSwitcher?.scan(y.get(0));const f=y.get(0).querySelector(".cms-footer-brand-icon");f&&p.brand?.icon&&f.setAttribute("data-icon",p.brand.icon),O(y.get(0),r.footer?.class);const v=[...m(t.primary,[],!0),...m(t.legal,[])];y.get(0).querySelectorAll(".footer-nav a").forEach((d,I)=>{const A=v[I];if(!A)return;A.colour&&U(d,A.colour);const D=X(A.badge);D&&d.appendChild(D),G(A.url,location.pathname)&&(d.classList.add("is-active"),d.setAttribute("aria-current","page"))}),W(y.get(0).querySelector(".footer-nav--primary"),{highlight:t.primaryHighlight}),W(y.get(0).querySelector(".footer-nav--legal"),{highlight:t.legalHighlight}),Y(y.get(0).querySelector(".footer-nav--primary"),{entrance:t.primaryEntrance}),Y(y.get(0).querySelector(".footer-nav--legal"),{entrance:t.legalEntrance}),O(y.get(0).querySelector(".footer-nav--primary"),t.primaryClass),O(y.get(0).querySelector(".footer-nav--legal"),t.legalClass);const q=r.footer?.showMotionToggle!==!1,L=S.get("reduced_motion"),B=r.footer?.motionDefault,P=L!=null?!!L:B==="on"?!0:B==="off"?!1:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),M=r.footer?.motionControl==="checkbox",N=y.get(0).querySelector(".cms-footer-bottom")||y.get(0).querySelector(".footer-inner");if(q&&N){const d=document.createElement("input");d.type="checkbox",d.className=M?"footer-motion-checkbox":"form-switch-input",d.id="dm-motion-switch",d.checked=P,d.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const I=document.createElement("span");I.className=M?"footer-motion-label":"form-switch-label",I.textContent="Reduce motion";const A=document.createElement("label");A.className=M?"footer-motion-check":"form-switch footer-motion-switch",A.title="Reduce motion",A.appendChild(d),A.appendChild(I),N.appendChild(A)}}const k=document.getElementById("site-sidebar");k&&ye(k),Domma.icons.scan(),Domma.syntax?.scan&&Domma.syntax.scan();const w=$(".page-body");if(w.length){w.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),w.find(".tabs").each(function(){Domma.elements.tabs(this)}),w.find(".carousel").each(function(){const n={primary:"--dm-primary",secondary:"--dm-secondary",accent:"--dm-primary",text:"--dm-text",surface:"--dm-surface",background:"--dm-background",success:"--dm-success",warning:"--dm-warning",danger:"--dm-danger",info:"--dm-info"},o=h=>{const i=String(h||"").trim();return i?n[i.toLowerCase()]?`var(${n[i.toLowerCase()]})`:CSS.supports("color",i)?i:"":""},s=o(this.dataset.background),t=o(this.dataset.arrowBackground),e=o(this.dataset.arrowColor);s&&(this.style.setProperty("--dm-carousel-background",s),this.classList.add("dm-carousel-custom-background")),t&&this.style.setProperty("--dm-carousel-arrow-background",t),e&&this.style.setProperty("--dm-carousel-arrow-color",e);const a=["top","middle","bottom"].includes(this.dataset.arrowPosition)?this.dataset.arrowPosition:"middle";this.classList.add(`dm-carousel-arrows-${a}`);const l={autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide",heightMode:this.dataset.heightMode==="average"?"average":"crop"};if(this.dataset.height){const h=Number(this.dataset.height);Number.isFinite(h)&&h>0&&(l.height=h)}if(this.dataset.animationDuration){const h=parseInt(this.dataset.animationDuration,10);Number.isFinite(h)&&(l.animationDuration=h)}this.dataset.animationEasing&&(l.animationEasing=this.dataset.animationEasing),this.dataset.ctx&&(this._dmCtxSlides=Array.from(this.querySelectorAll(".carousel-slide")).map(h=>h.cloneNode(!0)),this._dmCarouselOpts=l),this._dmCarousel=Domma.elements.carousel(this,l),ae(this)}),w.find(".dm-countdown").each(function(){const n={autoStart:!0},o=String(this.dataset.format||"").trim(),s=String(this.dataset.to||"").trim(),t=/^(\d{4})-(\d{2})-(\d{2})$/.exec(s),e=t?new Date(+t[1],+t[2]-1,+t[3]).getTime():s?new Date(s).getTime():NaN;Number.isFinite(e)?n.duration=Math.max(0,e-Date.now()):this.dataset.duration&&(n.duration=(parseInt(this.dataset.duration,10)||0)*1e3);const a=/^dd:/i.test(o);n.format=a?"hh:mm:ss":o||(n.duration>=36e5?"hh:mm:ss":"mm:ss");const l=this,h=c=>String(c).padStart(2,"0"),i=c=>{const g=Math.max(0,Math.floor(c/1e3)),b=l.querySelector(".dm-timer-time");b&&(b.textContent=`${h(Math.floor(g/86400))}:${h(Math.floor(g%86400/3600))}:${h(Math.floor(g%3600/60))}:${h(g%60)}`)};a&&(n.onTick=i),Domma.elements.timer(this,n),a&&i(n.duration||0)}),w.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),w.find(".dm-progression").each(function(){const n=Array.from(this.querySelectorAll(".dm-progression-item")).map(t=>{const e={title:t.querySelector(".dm-progression-item-title")?.textContent?.trim()||"",description:t.querySelector(".dm-progression-item-body")?.innerHTML?.trim()||""};return t.dataset.date&&(e.date=t.dataset.date),t.dataset.status&&(e.status=t.dataset.status),t.dataset.eventIcon&&(e.icon=t.dataset.eventIcon),t.dataset.entryId&&(e._entryId=t.dataset.entryId),e}),o=this.dataset.layout==="centred"?"centered":this.dataset.layout||"vertical",s=Domma.elements.progression(this,{layout:o,theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0,allowHtml:!0,...n.length?{items:n}:{}});if((this.dataset.mode||"timeline")==="timeline"){const t={completed:{label:"Completed",icon:"check-circle",tone:"success"},planned:{label:"Planned",icon:"circle",tone:"secondary"},"in-progress":{label:"In progress",icon:"clock",tone:"primary"},blocked:{label:"Blocked",icon:"alert-circle",tone:"danger"}};this.querySelectorAll(".dm-progression-item").forEach((e,a)=>{const l=n[a];if(!l)return;const h=e.querySelector(".dm-progression-title");if(l.icon&&h){const c=document.createElement("span");c.dataset.icon=l.icon,c.style.cssText="display:inline-flex;vertical-align:middle;margin-right:.4em",c.setAttribute("aria-hidden","true"),h.prepend(c)}const i=t[l.status];if(i&&h){const c=document.createElement("span");c.className=`badge badge-${i.tone} cms-timeline-status`,c.style.cssText="display:inline-flex;align-items:center;gap:.35em;margin:.4rem 0";const g=document.createElement("span");g.dataset.icon=i.icon,g.dataset.iconSize="14",g.setAttribute("aria-hidden","true"),c.append(g,document.createTextNode(i.label)),h.after(c)}})}Domma.icons.scan(this),n.length&&(this._dmCtx={instance:s,items:n})});try{Domma.effects.reveal(".page-body .hero",{animation:"slide-up",duration:480,threshold:.06,stagger:60,once:!1})}catch{}document.querySelectorAll(".page-body .row[data-reveal]").forEach(n=>{const o=n.dataset.revealMode||"stagger",s=n.dataset.revealAnimation||"slide-up",t=parseInt(n.dataset.revealDuration,10)||400,e=parseInt(n.dataset.revealStagger,10)||60,a=parseInt(n.dataset.revealDelay,10)||0,l=n.dataset.revealDirection==="rtl",h=Array.from(n.children),i={"slide-up":"translateY(30px)","slide-down":"translateY(-30px)","slide-left":"translateX(30px)","slide-right":"translateX(-30px)",zoom:"scale(0.85)",flip:"perspective(600px) rotateX(15deg)"};h.forEach((c,g)=>{c.style.opacity="0",c.style.transform=i[s]||"",c.style.transition=`opacity ${t}ms ease, transform ${t}ms ease`;const b=l?h.length-1-g:g;c.style.transitionDelay=o==="stagger"?`${a+b*e}ms`:`${a}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const c=new IntersectionObserver(g=>{g.forEach(b=>{b.isIntersecting&&(b.target.offsetWidth,b.target.style.opacity="1",b.target.style.transform="none",c.unobserve(b.target))})},{threshold:.1});h.forEach(g=>c.observe(g))}))}),w.find(".card[data-collapsible]").each(function(){const n=this.querySelector(".card-header");n&&n.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),w.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const n=this.dataset.soTarget,o=document.getElementById(n);if(!o)return;const s=E.slideover({title:o.dataset.soTitle||"",size:o.dataset.soSize||"md",position:o.dataset.soPosition||"right"});o.style.display="",s.setContent(o),s.open()})})}if(typeof $.setup=="function"){const n=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(o=>{try{const s=R(o.dataset.config);Object.assign(n,s)}catch{}}),Object.keys(n).length>0){const o={};for(const[s,t]of Object.entries(n)){const e=t?.events?.click,{confirm:a,toast:l,alert:h,prompt:i,...c}=e||{};a||l||h||i?($(s).on("click",async function(b){if(b.preventDefault(),a&&!await E.confirm(a))return;let u=null;if(!(i&&(u=await E.prompt(i,{inputPlaceholder:c.promptPlaceholder||"",inputValue:c.promptDefault||""}),u===null))){if(c.target){const f=$(c.target);c.toggleClass&&f.toggleClass(c.toggleClass),c.addClass&&f.addClass(c.addClass),c.removeClass&&f.removeClass(c.removeClass),u!==null&&(c.setText&&f.text(u),c.setVal&&f.val(u),c.setAttr&&f.attr(c.setAttr,u))}c.href&&(window.location.href=c.href),l&&E.toast(l,{type:c.toastType||"success"}),h&&E.alert(h)}}),Object.keys(c).length&&(o[s]={...t,events:{...t.events,click:c}})):o[s]=t}$.setup(o)}}w.length&&j(w.get(0))}),window.wireCTAButtons=j;function j(p){p.querySelectorAll(".dm-cta-trigger").forEach(r=>{r.__ctaWired||(r.__ctaWired=!0,r.addEventListener("click",async()=>{const x=r.dataset.action,y=r.dataset.entry,k=r.dataset.confirm;let w=S.get("auth_token");if(!w){E.toast("Please log in to perform this action.",{type:"warning"});return}if(k&&!await E.confirm(k))return;const n=Array.from(r.childNodes).map(s=>s.cloneNode(!0));r.disabled=!0,r.textContent="Running\u2026";const o=s=>fetch(`/api/actions/${x}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify(r.dataset.scope?{entryId:y,scope:r.dataset.scope}:{entryId:y})});try{let s=await o(w);if(s.status===401){const e=S.get("auth_refresh_token");if(e){const a=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(a.ok){const{token:l}=await a.json();S.set("auth_token",l),w=l,s=await o(w)}}}const t=await s.json().catch(()=>({}));if(!s.ok||t.success===!1)throw new Error(t.error||t.message||`Error ${s.status}`);E.toast(t.message||"Action completed.",{type:"success"})}catch(s){E.toast(s.message||"Action failed.",{type:"error"})}finally{r.disabled=!1,r.textContent="",n.forEach(s=>r.appendChild(s)),Domma.icons.scan(r)}}))})}(function(){const r=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!r.length)return;const x=S.get("auth_token");function y(w,n){const o=document.createElement("div");o.className="dm-collection-display dm-collection-empty";const s=document.createElement("p");if(n){s.appendChild(document.createTextNode("Please "));const t=document.createElement("a");t.href="/login",t.textContent="sign in",s.appendChild(t),s.appendChild(document.createTextNode(" to view your entries."))}else s.textContent=w;return o.appendChild(s),o}function k(w,n){const o=document.createElement("template");o.innerHTML=n||"",w.replaceWith(o.content)}r.forEach(async w=>{const n=w.dataset.collectionAttrs;if(!n)return;if(!x){w.replaceChildren(y("",!0));return}const o=s=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({attrs:n})});try{let s=x,t=await o(s);if(t.status===401){const a=S.get("auth_refresh_token");if(a){const l=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:a})});if(l.ok){const{token:h}=await l.json();S.set("auth_token",h),s=h,t=await o(s)}}}if(!t.ok){const{error:a}=await t.json().catch(()=>({}));w.replaceChildren(y(a||"Unable to load entries.",!1));return}const{html:e}=await t.json();k(w,e),window.DommaCollectionBrowser?.hydrate&&window.DommaCollectionBrowser.hydrate(document)}catch{w.replaceChildren(y("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),typeof Domma<"u"&&Domma.syntax?.scan&&Domma.syntax.scan(),j(document.body)},100)})(),(function(){const r=document.querySelectorAll("[data-collection-table]");!r.length||typeof T>"u"||!T.create||r.forEach(x=>{let y;try{y=R(x.dataset.payload)}catch{return}const{columns:k,rows:w,search:n,sortable:o,exportable:s,pageSize:t,empty:e,ctaConfig:a,slug:l,exportCfg:h}=y;if(!k?.length)return;if(a){const g=k.findIndex(b=>b.key==="_cta");g!==-1&&(k[g]={key:"_cta",title:"",render:(b,u)=>{const f=document.createElement("button");if(f.className=`btn btn-${a.style||"primary"} dm-cta-trigger`,f.dataset.action=a.action||"",a.scope&&(f.dataset.scope=a.scope),f.dataset.entry=u._entryId||"",a.confirm&&(f.dataset.confirm=a.confirm),a.icon){const m=document.createElement("span");m.dataset.icon=a.icon,f.appendChild(m),f.appendChild(document.createTextNode(" "))}return f.appendChild(document.createTextNode(a.label||"Run")),f}})}const i="col-table-"+Math.random().toString(36).slice(2,7),c=document.createElement("div");c.id=i,x.replaceChildren(c),T.create("#"+i,{data:w,columns:k,search:n,sortable:o,exportable:s,pageSize:t,emptyMessage:e}),a&&j(c),x._dmTable={columns:k,rows:w,slug:l,exportCfg:h,render(g){T.create("#"+i,{data:g,columns:k,search:n,sortable:o,exportable:s,pageSize:t,emptyMessage:e}),a&&j(c)}}})})(),(function(){const r=document.querySelectorAll("[data-form-inline]");if(!r.length||typeof F>"u")return;function x(o){const s=o&&o.logic;return!!(s&&(s.visibility||s.requirement))}function y(o,s){const t={};return(o||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const a=e.type==="chooser"||!!(e.variant&&["select","radio","checkbox-group"].includes(e.type)),l=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":a?"chooser":e.type,h={...e.formConfig||{}};h.span==="full"&&s&&(h.span=s);const i={type:l,required:x(e)?!1:e.required,label:e.label,options:e.options,formConfig:{...e.placeholder&&{placeholder:e.placeholder},...e.helper&&{hint:e.helper},...e.tooltip&&{tooltip:e.tooltip},...h}};e.minLength!==void 0&&(i.minLength=e.minLength),e.maxLength!==void 0&&(i.maxLength=e.maxLength),e.min!==void 0&&(i.min=e.min),e.max!==void 0&&(i.max=e.max),a&&(e.variant&&(i.variant=e.variant),(e.multiple||e.type==="checkbox-group")&&(i.multiple=!0),e.density&&(i.density=e.density),e.columns&&(i.columns=e.columns),e.accent&&(i.accent=e.accent),e.accentStyle&&(i.accentStyle=e.accentStyle),e.glow&&(i.glow=!0),e.glowColour&&(i.glowColour=e.glowColour),e.shadow&&(i.shadow=e.shadow),e.shadowColour&&(i.shadowColour=e.shadowColour)),t[e.name]=i}),t}function k(o){const s={};return(o||[]).forEach(t=>{if(!(!t.name||t.type==="page-break"||t.type==="spacer")&&(t.type==="select"||t.type==="multiselect")&&t.required){const e=(t.options||[])[0];e&&(s[t.name]=typeof e=="object"?e.value:e)}}),s}function w(o,s){(s||[]).forEach(t=>{if(t.type!=="date"||!t.name)return;const e=o.querySelector(`[name="${t.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function n(o,s,t){let e=o.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",o.appendChild(e)),e.textContent=s,e.style.cssText=t?"color:var(--dm-danger);margin-top:.75rem;":"color:var(--dm-success);margin-top:.75rem;"}r.forEach(o=>{let s;try{s=R(o.dataset.formInline)}catch{return}o.querySelector(":scope > .cms-form-embed-summary")?.remove();const t=s.fields||[],e=s.settings||{},a=Number(e.columns)===3?3:2,l=e.layout||"grid",h=t.some(f=>f.type==="page-break"),i=async f=>{if(o.dataset.triggerBlocked==="true"){const m=o._formLogicRuntime,v=m&&m.submitBlock?m.submitBlock().reasons:[];n(o,v.join(" ")||"This form cannot be submitted yet.",!0);const C=new Error("Blocked by a form trigger");throw C.formSubmitFailed=!0,C}try{const m=o.querySelector(".fb-form-honeypot"),v=m?.querySelector('input[type="text"]')||null,C=m?.querySelector('[name="_t"]')||null,q=Object.assign({},f);delete q._t,m?.dataset.hpName&&delete q[m.dataset.hpName],v!==null&&(q._hp=v.value),C!==null&&(q._t=C.value);const L=await H.post(`/api/forms/submit/${s.slug}`,q);if(L&&L.error){n(o,L.error,!0);const B=new Error(L.error);throw B.formSubmitFailed=!0,B}if(L?.redirect){window.location.href=L.redirect;return}for(;o.firstChild;)o.removeChild(o.firstChild);n(o,L?.message||e.successMessage||"Thank you for your submission.",!1)}catch(m){throw m.formSubmitFailed||n(o,m.message||"Submission failed. Please try again.",!0),m}},c=Date.now();function g(f){const m=f.querySelector("form");if(!m||m.querySelector(".fb-form-honeypot"))return;const v=document.createElement("div");v.className="fb-form-honeypot",v.setAttribute("aria-hidden","true");const C=document.createElement("input");C.name=m.querySelector('[name="website"]')?"_website":"website",v.dataset.hpName=C.name,C.type="text",C.tabIndex=-1,C.autocomplete="url",C.placeholder="https://",v.appendChild(C);const q=document.createElement("input");q.name="_t",q.type="hidden",q.value=c,v.appendChild(q),m.appendChild(v)}function b(f){if(!window.FormLogicEngine||!t.some(v=>v.logic||Array.isArray(v.triggers)&&v.triggers.length))return null;const m=new window.FormLogicEngine.FormLogicRuntime(s,o,f?{baseValues:f}:void 0);if(m.init(),o._formLogicRuntime=m,o._formLogicObserved)return m;if(o._formLogicObserved=!0,o.parentNode&&typeof MutationObserver<"u"){const v=new MutationObserver(function(C){for(const q of C)for(const L of q.removedNodes)if(L===o||L.nodeType===1&&L.contains&&L.contains(o)){o._formLogicRuntime&&o._formLogicRuntime.destroy(),v.disconnect();return}});v.observe(o.parentNode,{childList:!0,subtree:!1}),o._formLogicObserver=v}return m}let u=!1;if(o.addEventListener("dm-form-end",f=>{const m=f.detail?.validation;if(m&&!m.valid){Q(o,m.missing),Z(m);return}u||(u=!0,i(f.detail?.values||{}).catch(()=>{}).finally(()=>{u=!1}))}),h&&F.wizard){const f=ue(t,s.title),m=f.map(M=>({title:M.title,description:M.description,fields:y(M.fields,a)})),v=f.map(M=>M.fields);let C=0,q=null,L=null;const B=M=>{const N=window.FormLogicEngine;if(!N||!N.validateFields)return;const d=N.validateFields(o,v[C]||[]);d.valid||(M.preventDefault(),M.stopImmediatePropagation(),Q(o,d.missing),Z(d))},P=()=>{const M=v[C]||t;fe(o,e),ee(o,e,M),w(o,M),K(o,M),e.honeypot!==!1&&g(o),q&&q.destroy(),q=b(L?.getStepData?L.getStepData():void 0),o.querySelectorAll(".wizard-next, .wizard-submit").forEach(N=>N.addEventListener("click",B,!0))};L=F.wizard(o,{schema:{steps:m},layout:l,columns:a,finishText:e.submitText||"Finish",onSubmit:i,onStepChange:M=>{C=M,setTimeout(P,0)}}),o.addEventListener("dm-form-trigger",M=>{if(M.detail?.type!=="goto-step"||!L?.goToStep)return;const N=Number(M.detail.action.step);!Number.isFinite(N)||N<0||N>=m.length||N!==C&&L.goToStep(N)}),Promise.resolve(L).then(P)}else if(F.render){const f=F.render(o,y(t,a),k(t),{submitText:e.submitText||"Submit",layout:l,columns:a,onSubmit:i});Promise.resolve(f).then(function(){pe(o,e,l),ee(o,e,t),w(o,t),K(o,t),e.honeypot!==!1&&g(o),b()})}})})();function he(p,r){const x=document.body,y=x.classList,k=new Set((x.dataset.railSite||"").split(" ").filter(Boolean)),w=()=>window.matchMedia("(min-width: 993px)").matches;p.querySelectorAll(".navbar-link, .navbar-dropdown-toggle").forEach(a=>{const l=a.textContent.trim();l&&(a.setAttribute("title",l),a.querySelector("svg, [data-icon]")||(a.dataset.initial=l.charAt(0).toUpperCase()))});const n=document.createElement("button");n.type="button",n.className="dm-nav-collapse-toggle";const o=()=>{const a=y.contains("dm-vnav-collapsed");n.setAttribute("aria-pressed",String(a)),n.setAttribute("aria-label",a?"Expand menu":"Collapse menu"),n.title=n.getAttribute("aria-label");const l=r?"chevrons-left":"chevrons-right",h=r?"chevrons-right":"chevrons-left";n.innerHTML=`<span data-icon="${a?l:h}"></span>`,window.Domma&&Domma.icons&&Domma.icons.scan(n)},s=a=>{y.toggle("dm-vnav-collapsed",a),S.set("nav_collapsed",a),o()};n.addEventListener("click",()=>s(!y.contains("dm-vnav-collapsed"))),o();const t=p.querySelector(".navbar-container")||p;t.insertBefore(n,t.firstChild?.nextSibling||null);const e=a=>{y.toggle("dm-vnav-autohide",a),y.toggle("dm-vnav-collapsible",!a&&k.has("collapsible")),a?y.remove("dm-vnav-collapsed"):k.has("collapsible")&&S.get("nav_collapsed")===!0&&y.add("dm-vnav-collapsed"),p.classList.remove("dm-nav-revealed"),o()};p.addEventListener("pointerdown",a=>{y.contains("dm-vnav-autohide")&&a.pointerType!=="mouse"&&!p.classList.contains("dm-nav-revealed")&&(p.classList.add("dm-nav-revealed"),a.preventDefault())}),document.addEventListener("pointerdown",a=>{p.contains(a.target)||p.classList.remove("dm-nav-revealed")}),window.E&&typeof E.contextMenu=="function"&&E.contextMenu(p,{items:()=>{const a=y.contains("dm-vnav-autohide"),l=y.contains("dm-vnav-collapsed"),h=S.has("nav_autohide")||S.has("nav_collapsed");return[{header:"Menu"},{label:l?"Expand menu":"Collapse to icons",icon:l?"maximize":"minimize",disabled:!w()||a||!k.has("collapsible"),action:()=>s(!l)},{label:a?"Keep on screen":"Hide when not in use",icon:a?"eye":"eye-off",disabled:!w(),action:()=>{S.set("nav_autohide",!a),e(!a)}},{type:"divider"},{label:"Reset to the site's setting",icon:"refresh",disabled:!h,action:()=>{S.remove("nav_autohide"),S.remove("nav_collapsed"),e(k.has("autohide")),y.toggle("dm-vnav-collapsed",!k.has("autohide")&&k.has("start-collapsed")),o()}}]}})}function ge(p){p.classList.add("dm-nav-scroll-hide");let r=window.scrollY,x=!1;window.addEventListener("scroll",()=>{x||(x=!0,requestAnimationFrame(()=>{x=!1;const y=window.scrollY,k=p.matches(":focus-within")||p.querySelector(".navbar-dropdown.open, .navbar-collapse.show");y>r&&y>p.offsetHeight&&!k?p.classList.add("dm-nav-scrolled-away"):y<r&&p.classList.remove("dm-nav-scrolled-away"),r=y}))},{passive:!0})}function ye(p){let r={};try{r=JSON.parse(p.dataset.contents||"{}")}catch{}const x=p.closest(".site-main"),y=document.querySelector(".site-content"),k=new Set(String(r.exclude||"").split(",").map(i=>i.trim().toLowerCase()).filter(Boolean)),w=i=>!i.closest(".hero, .card, .accordion, .tabs, figure, #site-sidebar"),n=i=>(y?[...y.querySelectorAll(i)]:[]).filter(w).filter(c=>!k.has(c.textContent.trim().toLowerCase())),o=r.headings==="h2"?n("h2"):r.headings==="h3"?n("h3"):r.headings==="h2h3"?n("h2, h3"):n("h2").length?n("h2"):n("h3");if(!o.length){x?.classList.add("no-sidebar-items");return}const s=new Set([...document.querySelectorAll("[id]")].map(i=>i.id)),t=document.createElement("nav");t.className="dm-contents",t.setAttribute("aria-label",r.title||"Page contents"),r.hover&&(t.dataset.menuHover=r.hover),r.selected&&(t.dataset.menuActive=r.selected),r.accent&&t.style.setProperty("--dm-menu-highlight",r.accent.startsWith("#")?r.accent:`var(--dm-${r.accent})`),r.font&&(t.style.fontFamily=r.font),r.fontSize&&(t.style.fontSize=r.fontSize),r.fontWeight&&(t.style.fontWeight=r.fontWeight);const e=document.createElement("div");e.className="dm-contents-head";const a=document.createElement("p");a.className="dm-contents-title",a.textContent=r.title||"",e.appendChild(a),t.appendChild(e);const l=document.createElement("ol");l.className="dm-contents-list"+(r.numbered?" is-numbered":"");const h=[];for(const i of o){if(!i.id){const b=(i.textContent||"").toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"section";let u=b;for(let f=2;s.has(u);f++)u=`${b}-${f}`;s.add(u),i.id=u}const c=document.createElement("li");c.className=`dm-contents-item dm-contents-${i.tagName.toLowerCase()}`;const g=document.createElement("a");g.href=`#${i.id}`,g.textContent=i.textContent.trim(),c.appendChild(g),l.appendChild(c),h.push([i,g])}if(t.appendChild(l),p.replaceChildren(t),r.scrollSpy!==!1&&"IntersectionObserver"in window){const i=new Map,c=()=>{const b=h.find(([u])=>i.get(u))||null;h.forEach(([,u])=>{const f=b&&u===b[1];u.classList.toggle("is-active",!!f),f?u.setAttribute("aria-current","location"):u.removeAttribute("aria-current")})},g=new IntersectionObserver(b=>{b.forEach(u=>i.set(u.target,u.isIntersecting)),c()},{rootMargin:"-80px 0px -60% 0px"});h.forEach(([b])=>g.observe(b))}if(x?.classList.contains("contents-collapsible")){const i=document.createElement("button");i.type="button",i.className="dm-contents-toggle";const c=()=>{const g=x.classList.contains("contents-collapsed");i.setAttribute("aria-expanded",String(!g)),i.setAttribute("aria-label",g?"Show the contents":"Hide the contents"),i.title=i.getAttribute("aria-label"),i.innerHTML=`<span data-icon="${g?"list":"x"}"></span>`,window.Domma?.icons&&Domma.icons.scan(i)};i.addEventListener("click",()=>{S.set("contents_collapsed",x.classList.toggle("contents-collapsed")),c()}),c(),e.insertBefore(i,e.firstChild)}if(x?.classList.contains("contents-floating")&&r.movable){t.classList.add("is-movable");const i=S.get("contents_position");i&&Number.isFinite(i.x)&&Object.assign(p.style,{left:`${i.x}px`,top:`${i.y}px`,right:"auto",bottom:"auto"}),e.addEventListener("pointerdown",c=>{if(c.target.closest("button"))return;const g=p.getBoundingClientRect(),b=c.clientX-g.left,u=c.clientY-g.top,f=v=>{const C=Math.max(0,Math.min(window.innerWidth-g.width,v.clientX-b)),q=Math.max(0,Math.min(window.innerHeight-40,v.clientY-u));Object.assign(p.style,{left:`${C}px`,top:`${q}px`,right:"auto",bottom:"auto"})},m=()=>{document.removeEventListener("pointermove",f),S.set("contents_position",{x:parseInt(p.style.left,10),y:parseInt(p.style.top,10)})};document.addEventListener("pointermove",f),document.addEventListener("pointerup",m,{once:!0})})}window.E&&typeof E.contextMenu=="function"&&E.contextMenu(p,{items:()=>{const i=x?.classList.contains("contents-collapsible"),c=x?.classList.contains("contents-collapsed"),g=h.find(([,b])=>b.classList.contains("is-active"));return[{header:"Contents"},...i?[{label:c?"Show the contents":"Collapse to a button",icon:c?"list":"minimize",action:()=>p.querySelector(".dm-contents-toggle")?.click()}]:[],{label:"Copy a link to this section",icon:"link",disabled:!g,action:async()=>{const b=`${location.origin}${location.pathname}#${g[0].id}`;try{await _.copyToClipboard(b),E.toast("Link copied.",{type:"success"})}catch{E.toast("Could not copy the link.",{type:"error"})}}},{label:"Back to the top",icon:"arrow-up",action:()=>window.scrollTo({top:0,behavior:"smooth"})}]}}),x?.classList.contains("contents-autohide")&&(p.addEventListener("pointerdown",i=>{i.pointerType!=="mouse"&&!p.classList.contains("is-revealed")&&(p.classList.add("is-revealed"),i.preventDefault())}),document.addEventListener("pointerdown",i=>{p.contains(i.target)||p.classList.remove("is-revealed")}))}