domma-cms 1.6.13 → 1.6.15

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.
package/public/js/site.js CHANGED
@@ -1 +1 @@
1
- import{decodeBase64Json as N}from"/public/js/utf8-base64.mjs";import{makeBadgeEl as O,applyColour as j,applyPill as U,applyFloat as X,applyHighlight as B,applyItemHighlight as G,itemHighlightAttrs as K,isCurrentItem as R,menuAlign as Q,applyClasses as D,cssClasses as P}from"/public/js/menu-decor.mjs?v=20261009-hover-darken";import{initFloatingMenus as Z}from"/public/js/menu-float.js?v=20260829-float-panel";import{enhancePickerFields as V,markInvalidFields as W,showValidationPopup as J}from"/public/js/forms.js?v=20261009-form-classes";import{buildWizardSteps as ee}from"/public/js/form-steps.mjs";import{applyFormClasses as Y,applySubmitButtons as te,applyWizardButtons as ae}from"/public/js/form-submit.mjs";$(()=>{const h=window.__CMS_NAV__||{},g=window.__CMS_SITE__||{};if(g.autoTheme?.enabled){let t=function(e){const n=(e||"07:00").split(":");return+n[0]*60+(+n[1]||0)},s=function(){const e=new Date,n=e.getHours()*60+e.getMinutes();return n>=t(o.dayStart)&&n<t(o.nightStart)?o.dayTheme:o.nightTheme},i=function(e){const n=o.apply&&o.apply[e]||{theme:e,custom:""};Domma.theme.set(n.theme);for(const r of[...document.body.classList])r.startsWith("dm-custom-")&&document.body.classList.remove(r);n.custom&&document.body.classList.add(n.custom)};const o=g.autoTheme;i(s()),setInterval(()=>i(s()),6e4)}const C=$("#site-navbar");if(C.length&&h.brand){let s=function(m,l=0){return Array.isArray(m)?m.filter(a=>!(a&&(a.type==="spacer"||a.type==="theme-switcher"))).filter(a=>!(a&&a.type==="separator"&&l===0)).map(a=>a&&a.type==="separator"?{divider:!0}:Array.isArray(a?.items)?{...a,items:s(a.items,l+1)}:a):m},i=function(m){return Array.isArray(m)?m.filter(l=>!(l&&(l.type==="separator"||l.type==="spacer"||l.type==="theme-switcher"))):[]};const o={...h.brand},t=o.size&&o.size!=="md"?` navbar-brand-${o.size}`:"";if(o.logo||o.icon||o.tagline){let m="";o.logo?m+=`<img src="${o.logo}" class="navbar-brand-logo" alt="${o.text||""}">`:o.icon&&(m+=`<span data-icon="${o.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),o.text&&(m+=`<span class="navbar-brand-text${t}">${o.text}</span>`),o.tagline&&(m+=`<small class="navbar-brand-tagline">${o.tagline}</small>`),o.html=m}else t&&o.text&&(o.html=`<span class="navbar-brand-text${t}">${o.text}</span>`);const e=h.position==="floating",n=h.orientation==="vertical";Domma.elements.navbar("#site-navbar",{brand:o,items:s(h.items||[]),variant:h.variant||"dark",position:e?"static":h.position||"sticky",collapsible:!0,appearOnHover:!!h.appearOnHover}),Domma.icons.scan("#site-navbar");const r=C.get(0);r.classList.add(`dm-nav-align-${Q(h)||"centre"}`),n&&C.get(0).classList.add("dm-nav-vertical",h.side==="right"?"dm-nav-vertical-right":"dm-nav-vertical-left");const y=h.dock||{};n&&!e&&oe(r,h.side==="right"),!n&&y.hideOnScroll&&ne(r),B(r,h),D(r,h.class),e&&(X(C.get(0),h),Z(C.get(0).parentNode||document));const p=r.querySelector(".navbar-toggle"),u=r.querySelector(".navbar-collapse");if(p&&u){let m=null;const l=()=>{u.classList.contains("show")&&p.click()},a=()=>{m&&(m.remove(),m=null),document.body.classList.remove("dm-drawer-open")},d=()=>{m||(m=document.createElement("div"),m.className="dm-navbar-backdrop",m.addEventListener("click",l),document.body.appendChild(m),document.body.classList.add("dm-drawer-open"))};p.addEventListener("click",()=>{requestAnimationFrame(()=>{u.classList.contains("show")?d():a()})}),u.addEventListener("click",c=>{c.target.closest("a")&&requestAnimationFrame(a)}),document.addEventListener("keydown",c=>{c.key==="Escape"&&u.classList.contains("show")&&l()})}const b=r.querySelector(".navbar-nav");if(b){const m=Array.from(b.children);let l=0;for(const a of h.items||[])if(!(a&&a.type==="separator")){if(a&&a.type==="spacer"){const d=document.createElement("li"),c=["xs","sm","md","lg","xl","flex"].includes(a.size)?a.size:"md";d.className=`dm-menu-spacer dm-menu-spacer--${c}`,d.setAttribute("aria-hidden","true"),b.insertBefore(d,m[l]||null);continue}if(a&&a.type==="theme-switcher"){const d=document.createElement("li");d.className="dm-menu-theme-switcher";const c=document.createElement("span");c.dataset.themeSwitcher="menu",c.dataset.position="inline",d.appendChild(c),b.insertBefore(d,m[l]||null);continue}l++}window.CMSThemeSwitcher?.scan(b)}const k=i(h.items||[]);r.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(m=>{const l=k[parseInt(m.dataset.index,10)];if(!l)return;const a=m.dataset.subindex!=null,d=a&&Array.isArray(l.items)?l.items[parseInt(m.dataset.subindex,10)]:l;if(!d)return;d.colour&&j(m,d.colour),d.pill&&!a&&U(m,d.pill),G(m,d),D(m.closest("li"),d.class);const c=O(d.badge);c&&m.appendChild(c),R(d.url,location.pathname)&&(m.classList.add("active"),m.setAttribute("aria-current","page"))})}const f=$("#site-footer");if(f.length){let e=function(a){return String(a??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},n=function(a){return e(a).replace(/'/g,"&#39;")},r=function(a){if(a&&a.type==="separator")return'<li class="footer-sep"><hr></li>';if(a&&a.type==="spacer")return`<li class="dm-menu-spacer dm-menu-spacer--${["xs","sm","md","lg","xl","flex"].includes(a.size)?a.size:"md"}" aria-hidden="true"></li>`;if(a&&a.type==="theme-switcher")return'<li class="dm-menu-theme-switcher"><span data-theme-switcher="menu" data-position="inline"></span></li>';const d=n(a.url||"#"),c=e(a.text||""),A=Array.isArray(a.items)&&a.items.length?`<ul>${a.items.map(r).join("")}</ul>`:"",w=K(a),q=[w.hover&&` data-menu-item-hover="${n(w.hover)}"`,w.active&&` data-menu-item-active="${n(w.active)}"`].filter(Boolean).join(""),I=w.accent?` style="--dm-menu-highlight:${n(w.accent)}"`:"",L=P(a.class).join(" ");return`<li${L?` class="${n(L)}"`:""}><a href="${d}"${q}${I}>${c}</a>${A}</li>`},y=function(a,d){return(a||[]).forEach(c=>{c&&(c.type==="separator"||c.type==="spacer"||c.type==="theme-switcher")||(d.push(c),Array.isArray(c.items)&&c.items.length&&y(c.items,d))}),d};const o=g.social||{},t={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 i=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(i.copyright&&(s+=`<p>${e(i.copyright)}</p>`),i.primary.length&&(s+=`<nav class="footer-nav footer-nav--primary"><ul>${i.primary.map(r).join("")}</ul></nav>`),i.legal.length&&(s+=`<nav class="footer-nav footer-nav--legal"><ul>${i.legal.map(r).join("")}</ul></nav>`),g.footer){const a=Object.keys(t).filter(c=>o[c]),d=P(g.footer.socialClass).join(" ");a.length&&(s+='<div class="footer-social">',a.forEach(c=>{const{label:A,svg:w}=t[c];s+=`<a href="${o[c]}" target="_blank" rel="noopener noreferrer" aria-label="${A}" class="footer-social-link${d?` ${n(d)}`:""}">${w}</a>`}),s+="</div>")}s+="</div>",f.html(s),window.CMSThemeSwitcher?.scan(f.get(0)),D(f.get(0),g.footer?.class);const p=[...y(i.primary,[]),...y(i.legal,[])];f.get(0).querySelectorAll(".footer-nav a").forEach((a,d)=>{const c=p[d];if(!c)return;c.colour&&j(a,c.colour);const A=O(c.badge);A&&a.appendChild(A),R(c.url,location.pathname)&&(a.classList.add("is-active"),a.setAttribute("aria-current","page"))}),B(f.get(0).querySelector(".footer-nav--primary"),{highlight:i.primaryHighlight}),B(f.get(0).querySelector(".footer-nav--legal"),{highlight:i.legalHighlight}),D(f.get(0).querySelector(".footer-nav--primary"),i.primaryClass),D(f.get(0).querySelector(".footer-nav--legal"),i.legalClass);const b=g.footer?.showMotionToggle!==!1,k=S.get("reduced_motion"),m=k!==null?!!k:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),l=f.get(0).querySelector(".footer-inner");if(b&&l){const a=document.createElement("input");a.type="checkbox",a.className="form-switch-input",a.id="dm-motion-switch",a.checked=m,a.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const d=document.createElement("span");d.className="form-switch-label",d.textContent="Reduce motion";const c=document.createElement("label");c.className="form-switch footer-motion-switch",c.title="Reduce motion",c.appendChild(a),c.appendChild(d),l.appendChild(c)}}if($("#site-sidebar").length){const o=document.querySelector(".site-content"),t=r=>!r.closest(".hero, .card, .accordion, .tabs, figure, #site-sidebar"),s=r=>o?[...o.querySelectorAll(r)].filter(t):[],i=s("h2").length?s("h2"):s("h3"),e=new Set([...document.querySelectorAll("[id]")].map(r=>r.id)),n=[];for(const r of i){if(!r.id){const y=(r.textContent||"").toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"section";let p=y;for(let u=2;e.has(p);u++)p=`${y}-${u}`;e.add(p),r.id=p}n.push({text:r.textContent.trim(),url:`#${r.id}`,section:r.id})}n.length?Domma.elements.sidebar("#site-sidebar",{items:n,fixed:!1,push:!1,collapsible:!1,variant:"theme",scrollSpy:!0,position:document.querySelector(".site-main.sidebar-right")?"right":"left"}):document.querySelector(".site-main.with-sidebar")?.classList.add("no-sidebar-items")}Domma.icons.scan(),Domma.syntax?.scan&&Domma.syntax.scan();const v=$(".page-body");if(v.length){v.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),v.find(".tabs").each(function(){Domma.elements.tabs(this)}),v.find(".carousel").each(function(){const o={autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide"};if(this.dataset.animationDuration){const t=parseInt(this.dataset.animationDuration,10);Number.isFinite(t)&&(o.animationDuration=t)}this.dataset.animationEasing&&(o.animationEasing=this.dataset.animationEasing),this.dataset.ctx&&(this._dmCtxSlides=Array.from(this.querySelectorAll(".carousel-slide")).map(t=>t.cloneNode(!0)),this._dmCarouselOpts=o),this._dmCarousel=Domma.elements.carousel(this,o)}),v.find(".dm-countdown").each(function(){const o={autoStart:!0},t=String(this.dataset.format||"").trim(),s=String(this.dataset.to||"").trim(),i=/^(\d{4})-(\d{2})-(\d{2})$/.exec(s),e=i?new Date(+i[1],+i[2]-1,+i[3]).getTime():s?new Date(s).getTime():NaN;Number.isFinite(e)?o.duration=Math.max(0,e-Date.now()):this.dataset.duration&&(o.duration=(parseInt(this.dataset.duration,10)||0)*1e3);const n=/^dd:/i.test(t);o.format=n?"hh:mm:ss":t||(o.duration>=36e5?"hh:mm:ss":"mm:ss");const r=this,y=u=>String(u).padStart(2,"0"),p=u=>{const b=Math.max(0,Math.floor(u/1e3)),k=r.querySelector(".dm-timer-time");k&&(k.textContent=`${y(Math.floor(b/86400))}:${y(Math.floor(b%86400/3600))}:${y(Math.floor(b%3600/60))}:${y(b%60)}`)};n&&(o.onTick=p),Domma.elements.timer(this,o),n&&p(o.duration||0)}),v.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),v.find(".dm-progression").each(function(){const o=Array.from(this.querySelectorAll(".dm-progression-item")).map(i=>{const e={title:i.querySelector(".dm-progression-item-title")?.textContent?.trim()||"",description:i.querySelector(".dm-progression-item-body")?.innerHTML?.trim()||""};return i.dataset.date&&(e.date=i.dataset.date),i.dataset.status&&(e.status=i.dataset.status),i.dataset.entryId&&(e._entryId=i.dataset.entryId),e}),t=this.dataset.layout==="centred"?"centered":this.dataset.layout||"vertical",s=Domma.elements.progression(this,{layout:t,theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0,allowHtml:!0,...o.length?{items:o}:{}});o.length&&(this._dmCtx={instance:s,items:o})});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(o=>{const t=o.dataset.revealMode||"stagger",s=o.dataset.revealAnimation||"slide-up",i=parseInt(o.dataset.revealDuration,10)||400,e=parseInt(o.dataset.revealStagger,10)||60,n=parseInt(o.dataset.revealDelay,10)||0,r=o.dataset.revealDirection==="rtl",y=Array.from(o.children),p={"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)"};y.forEach((u,b)=>{u.style.opacity="0",u.style.transform=p[s]||"",u.style.transition=`opacity ${i}ms ease, transform ${i}ms ease`;const k=r?y.length-1-b:b;u.style.transitionDelay=t==="stagger"?`${n+k*e}ms`:`${n}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const u=new IntersectionObserver(b=>{b.forEach(k=>{k.isIntersecting&&(k.target.offsetWidth,k.target.style.opacity="1",k.target.style.transform="none",u.unobserve(k.target))})},{threshold:.1});y.forEach(b=>u.observe(b))}))}),v.find(".card[data-collapsible]").each(function(){const o=this.querySelector(".card-header");o&&o.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),v.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const o=this.dataset.soTarget,t=document.getElementById(o);if(!t)return;const s=E.slideover({title:t.dataset.soTitle||"",size:t.dataset.soSize||"md",position:t.dataset.soPosition||"right"});t.style.display="",s.setContent(t),s.open()})})}if(typeof $.setup=="function"){const o=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(t=>{try{const s=N(t.dataset.config);Object.assign(o,s)}catch{}}),Object.keys(o).length>0){const t={};for(const[s,i]of Object.entries(o)){const e=i?.events?.click,{confirm:n,toast:r,alert:y,prompt:p,...u}=e||{};n||r||y||p?($(s).on("click",async function(k){if(k.preventDefault(),n&&!await E.confirm(n))return;let m=null;if(!(p&&(m=await E.prompt(p,{inputPlaceholder:u.promptPlaceholder||"",inputValue:u.promptDefault||""}),m===null))){if(u.target){const l=$(u.target);u.toggleClass&&l.toggleClass(u.toggleClass),u.addClass&&l.addClass(u.addClass),u.removeClass&&l.removeClass(u.removeClass),m!==null&&(u.setText&&l.text(m),u.setVal&&l.val(m),u.setAttr&&l.attr(u.setAttr,m))}u.href&&(window.location.href=u.href),r&&E.toast(r,{type:u.toastType||"success"}),y&&E.alert(y)}}),Object.keys(u).length&&(t[s]={...i,events:{...i.events,click:u}})):t[s]=i}$.setup(t)}}v.length&&M(v.get(0))}),window.wireCTAButtons=M;function M(h){h.querySelectorAll(".dm-cta-trigger").forEach(g=>{g.__ctaWired||(g.__ctaWired=!0,g.addEventListener("click",async()=>{const C=g.dataset.action,f=g.dataset.entry,x=g.dataset.confirm;let v=S.get("auth_token");if(!v){E.toast("Please log in to perform this action.",{type:"warning"});return}if(x&&!await E.confirm(x))return;const o=Array.from(g.childNodes).map(s=>s.cloneNode(!0));g.disabled=!0,g.textContent="Running\u2026";const t=s=>fetch(`/api/actions/${C}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify(g.dataset.scope?{entryId:f,scope:g.dataset.scope}:{entryId:f})});try{let s=await t(v);if(s.status===401){const e=S.get("auth_refresh_token");if(e){const n=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:e})});if(n.ok){const{token:r}=await n.json();S.set("auth_token",r),v=r,s=await t(v)}}}const i=await s.json().catch(()=>({}));if(!s.ok)throw new Error(i.error||i.message||`Error ${s.status}`);E.toast(i.message||"Action completed.",{type:"success"})}catch(s){E.toast(s.message||"Action failed.",{type:"error"})}finally{g.disabled=!1,g.textContent="",o.forEach(s=>g.appendChild(s)),Domma.icons.scan(g)}}))})}(function(){const g=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!g.length)return;const C=S.get("auth_token");function f(v,o){const t=document.createElement("div");t.className="dm-collection-display dm-collection-empty";const s=document.createElement("p");if(o){s.appendChild(document.createTextNode("Please "));const i=document.createElement("a");i.href="/login",i.textContent="sign in",s.appendChild(i),s.appendChild(document.createTextNode(" to view your entries."))}else s.textContent=v;return t.appendChild(s),t}function x(v,o){const t=document.createElement("template");t.innerHTML=o||"",v.replaceWith(t.content)}g.forEach(async v=>{const o=v.dataset.collectionAttrs;if(!o)return;if(!C){v.replaceChildren(f("",!0));return}const t=s=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({attrs:o})});try{let s=C,i=await t(s);if(i.status===401){const n=S.get("auth_refresh_token");if(n){const r=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:n})});if(r.ok){const{token:y}=await r.json();S.set("auth_token",y),s=y,i=await t(s)}}}if(!i.ok){const{error:n}=await i.json().catch(()=>({}));v.replaceChildren(f(n||"Unable to load entries.",!1));return}const{html:e}=await i.json();x(v,e),window.DommaCollectionBrowser?.hydrate&&window.DommaCollectionBrowser.hydrate(document)}catch{v.replaceChildren(f("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),typeof Domma<"u"&&Domma.syntax?.scan&&Domma.syntax.scan(),M(document.body)},100)})(),(function(){const g=document.querySelectorAll("[data-collection-table]");!g.length||typeof T>"u"||!T.create||g.forEach(C=>{let f;try{f=N(C.dataset.payload)}catch{return}const{columns:x,rows:v,search:o,sortable:t,exportable:s,pageSize:i,empty:e,ctaConfig:n,slug:r,exportCfg:y}=f;if(!x?.length)return;if(n){const b=x.findIndex(k=>k.key==="_cta");b!==-1&&(x[b]={key:"_cta",title:"",render:(k,m)=>{const l=document.createElement("button");if(l.className=`btn btn-${n.style||"primary"} dm-cta-trigger`,l.dataset.action=n.action||"",n.scope&&(l.dataset.scope=n.scope),l.dataset.entry=m._entryId||"",n.confirm&&(l.dataset.confirm=n.confirm),n.icon){const a=document.createElement("span");a.dataset.icon=n.icon,l.appendChild(a),l.appendChild(document.createTextNode(" "))}return l.appendChild(document.createTextNode(n.label||"Run")),l}})}const p="col-table-"+Math.random().toString(36).slice(2,7),u=document.createElement("div");u.id=p,C.replaceChildren(u),T.create("#"+p,{data:v,columns:x,search:o,sortable:t,exportable:s,pageSize:i,emptyMessage:e}),n&&M(u),C._dmTable={columns:x,rows:v,slug:r,exportCfg:y,render(b){T.create("#"+p,{data:b,columns:x,search:o,sortable:t,exportable:s,pageSize:i,emptyMessage:e}),n&&M(u)}}})})(),(function(){const g=document.querySelectorAll("[data-form-inline]");if(!g.length||typeof F>"u")return;function C(t){const s=t&&t.logic;return!!(s&&(s.visibility||s.requirement))}function f(t,s){const i={};return(t||[]).forEach(e=>{if(e.type==="page-break"||e.type==="spacer"||!e.name)return;const n=e.type==="chooser"||!!(e.variant&&["select","radio","checkbox-group"].includes(e.type)),r=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":n?"chooser":e.type,y={...e.formConfig||{}};y.span==="full"&&s&&(y.span=s);const p={type:r,required:C(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},...y}};e.minLength!==void 0&&(p.minLength=e.minLength),e.maxLength!==void 0&&(p.maxLength=e.maxLength),e.min!==void 0&&(p.min=e.min),e.max!==void 0&&(p.max=e.max),n&&(e.variant&&(p.variant=e.variant),(e.multiple||e.type==="checkbox-group")&&(p.multiple=!0),e.density&&(p.density=e.density),e.columns&&(p.columns=e.columns),e.accent&&(p.accent=e.accent),e.accentStyle&&(p.accentStyle=e.accentStyle),e.glow&&(p.glow=!0),e.glowColour&&(p.glowColour=e.glowColour),e.shadow&&(p.shadow=e.shadow),e.shadowColour&&(p.shadowColour=e.shadowColour)),i[e.name]=p}),i}function x(t){const s={};return(t||[]).forEach(i=>{if(!(!i.name||i.type==="page-break"||i.type==="spacer")&&(i.type==="select"||i.type==="multiselect")&&i.required){const e=(i.options||[])[0];e&&(s[i.name]=typeof e=="object"?e.value:e)}}),s}function v(t,s){(s||[]).forEach(i=>{if(i.type!=="date"||!i.name)return;const e=t.querySelector(`[name="${i.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function o(t,s,i){let e=t.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",t.appendChild(e)),e.textContent=s,e.style.cssText=i?"color:var(--dm-danger);margin-top:.75rem;":"color:var(--dm-success);margin-top:.75rem;"}g.forEach(t=>{let s;try{s=N(t.dataset.formInline)}catch{return}const i=s.fields||[],e=s.settings||{},n=Number(e.columns)===3?3:2,r=e.layout||"grid",y=i.some(l=>l.type==="page-break"),p=async l=>{if(t.dataset.triggerBlocked==="true"){const a=t._formLogicRuntime,d=a&&a.submitBlock?a.submitBlock().reasons:[];o(t,d.join(" ")||"This form cannot be submitted yet.",!0);const c=new Error("Blocked by a form trigger");throw c.formSubmitFailed=!0,c}try{const a=t.querySelector(".fb-form-honeypot"),d=a?.querySelector('input[type="text"]')||null,c=a?.querySelector('[name="_t"]')||null,A=Object.assign({},l);delete A._t,a?.dataset.hpName&&delete A[a.dataset.hpName],d!==null&&(A._hp=d.value),c!==null&&(A._t=c.value);const w=await H.post(`/api/forms/submit/${s.slug}`,A);if(w&&w.error){o(t,w.error,!0);const q=new Error(w.error);throw q.formSubmitFailed=!0,q}if(w?.redirect){window.location.href=w.redirect;return}for(;t.firstChild;)t.removeChild(t.firstChild);o(t,w?.message||e.successMessage||"Thank you for your submission.",!1)}catch(a){throw a.formSubmitFailed||o(t,a.message||"Submission failed. Please try again.",!0),a}},u=Date.now();function b(l){const a=l.querySelector("form");if(!a||a.querySelector(".fb-form-honeypot"))return;const d=document.createElement("div");d.className="fb-form-honeypot",d.setAttribute("aria-hidden","true");const c=document.createElement("input");c.name=a.querySelector('[name="website"]')?"_website":"website",d.dataset.hpName=c.name,c.type="text",c.tabIndex=-1,c.autocomplete="url",c.placeholder="https://",d.appendChild(c);const A=document.createElement("input");A.name="_t",A.type="hidden",A.value=u,d.appendChild(A),a.appendChild(d)}function k(l){if(!window.FormLogicEngine||!i.some(d=>d.logic||Array.isArray(d.triggers)&&d.triggers.length))return null;const a=new window.FormLogicEngine.FormLogicRuntime(s,t,l?{baseValues:l}:void 0);if(a.init(),t._formLogicRuntime=a,t._formLogicObserved)return a;if(t._formLogicObserved=!0,t.parentNode&&typeof MutationObserver<"u"){const d=new MutationObserver(function(c){for(const A of c)for(const w of A.removedNodes)if(w===t||w.nodeType===1&&w.contains&&w.contains(t)){t._formLogicRuntime&&t._formLogicRuntime.destroy(),d.disconnect();return}});d.observe(t.parentNode,{childList:!0,subtree:!1}),t._formLogicObserver=d}return a}let m=!1;if(t.addEventListener("dm-form-end",l=>{const a=l.detail?.validation;if(a&&!a.valid){W(t,a.missing),J(a);return}m||(m=!0,p(l.detail?.values||{}).catch(()=>{}).finally(()=>{m=!1}))}),y&&F.wizard){const l=ee(i,s.title),a=l.map(L=>({title:L.title,description:L.description,fields:f(L.fields,n)})),d=l.map(L=>L.fields);let c=0,A=null,w=null;const q=L=>{const _=window.FormLogicEngine;if(!_||!_.validateFields)return;const z=_.validateFields(t,d[c]||[]);z.valid||(L.preventDefault(),L.stopImmediatePropagation(),W(t,z.missing),J(z))},I=()=>{const L=d[c]||i;ae(t,e),Y(t,e,L),v(t,L),V(t,L),e.honeypot!==!1&&b(t),A&&A.destroy(),A=k(w?.getStepData?w.getStepData():void 0),t.querySelectorAll(".wizard-next, .wizard-submit").forEach(_=>_.addEventListener("click",q,!0))};w=F.wizard(t,{schema:{steps:a},layout:r,columns:n,finishText:e.submitText||"Finish",onSubmit:p,onStepChange:L=>{c=L,setTimeout(I,0)}}),t.addEventListener("dm-form-trigger",L=>{if(L.detail?.type!=="goto-step"||!w?.goToStep)return;const _=Number(L.detail.action.step);!Number.isFinite(_)||_<0||_>=a.length||_!==c&&w.goToStep(_)}),Promise.resolve(w).then(I)}else if(F.render){const l=F.render(t,f(i,n),x(i),{submitText:e.submitText||"Submit",layout:r,columns:n,onSubmit:p});Promise.resolve(l).then(function(){te(t,e,r),Y(t,e,i),v(t,i),V(t,i),e.honeypot!==!1&&b(t),k()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();function oe(h,g){const C=document.body,f=C.classList,x=new Set((C.dataset.railSite||"").split(" ").filter(Boolean)),v=()=>window.matchMedia("(min-width: 993px)").matches;h.querySelectorAll(".navbar-link, .navbar-dropdown-toggle").forEach(n=>{const r=n.textContent.trim();r&&(n.setAttribute("title",r),n.querySelector("svg, [data-icon]")||(n.dataset.initial=r.charAt(0).toUpperCase()))});const o=document.createElement("button");o.type="button",o.className="dm-nav-collapse-toggle";const t=()=>{const n=f.contains("dm-vnav-collapsed");o.setAttribute("aria-pressed",String(n)),o.setAttribute("aria-label",n?"Expand menu":"Collapse menu"),o.title=o.getAttribute("aria-label");const r=g?"chevrons-left":"chevrons-right",y=g?"chevrons-right":"chevrons-left";o.innerHTML=`<span data-icon="${n?r:y}"></span>`,window.Domma&&Domma.icons&&Domma.icons.scan(o)},s=n=>{f.toggle("dm-vnav-collapsed",n),S.set("nav_collapsed",n),t()};o.addEventListener("click",()=>s(!f.contains("dm-vnav-collapsed"))),t();const i=h.querySelector(".navbar-container")||h;i.insertBefore(o,i.firstChild?.nextSibling||null);const e=n=>{f.toggle("dm-vnav-autohide",n),f.toggle("dm-vnav-collapsible",!n&&x.has("collapsible")),n?f.remove("dm-vnav-collapsed"):x.has("collapsible")&&S.get("nav_collapsed")===!0&&f.add("dm-vnav-collapsed"),h.classList.remove("dm-nav-revealed"),t()};h.addEventListener("pointerdown",n=>{f.contains("dm-vnav-autohide")&&n.pointerType!=="mouse"&&!h.classList.contains("dm-nav-revealed")&&(h.classList.add("dm-nav-revealed"),n.preventDefault())}),document.addEventListener("pointerdown",n=>{h.contains(n.target)||h.classList.remove("dm-nav-revealed")}),window.E&&typeof E.contextMenu=="function"&&E.contextMenu(h,{items:()=>{const n=f.contains("dm-vnav-autohide"),r=f.contains("dm-vnav-collapsed"),y=S.has("nav_autohide")||S.has("nav_collapsed");return[{header:"Menu"},{label:r?"Expand menu":"Collapse to icons",icon:r?"maximize":"minimize",disabled:!v()||n||!x.has("collapsible"),action:()=>s(!r)},{label:n?"Keep on screen":"Hide when not in use",icon:n?"eye":"eye-off",disabled:!v(),action:()=>{S.set("nav_autohide",!n),e(!n)}},{type:"divider"},{label:"Reset to the site's setting",icon:"refresh",disabled:!y,action:()=>{S.remove("nav_autohide"),S.remove("nav_collapsed"),e(x.has("autohide")),f.toggle("dm-vnav-collapsed",!x.has("autohide")&&x.has("start-collapsed")),t()}}]}})}function ne(h){h.classList.add("dm-nav-scroll-hide");let g=window.scrollY,C=!1;window.addEventListener("scroll",()=>{C||(C=!0,requestAnimationFrame(()=>{C=!1;const f=window.scrollY,x=h.matches(":focus-within")||h.querySelector(".navbar-dropdown.open, .navbar-collapse.show");f>g&&f>h.offsetHeight&&!x?h.classList.add("dm-nav-scrolled-away"):f<g&&h.classList.remove("dm-nav-scrolled-away"),g=f}))},{passive:!0})}
1
+ import{decodeBase64Json as B}from"/public/js/utf8-base64.mjs";import{makeBadgeEl as j,applyColour as P,applyPill as U,applyFloat as G,applyHighlight as O,applyItemHighlight as K,itemHighlightAttrs as Q,isCurrentItem as R,menuAlign as Z,applyClasses as N,cssClasses as W}from"/public/js/menu-decor.mjs?v=20261009-hover-darken";import{initFloatingMenus as ee}from"/public/js/menu-float.js?v=20260829-float-panel";import{enhancePickerFields as V,markInvalidFields as Y,showValidationPopup as J}from"/public/js/forms.js?v=20261009-form-classes";import{buildWizardSteps as te}from"/public/js/form-steps.mjs";import{applyFormClasses as X,applySubmitButtons as ne,applyWizardButtons as oe}from"/public/js/form-submit.mjs";$(()=>{const p=window.__CMS_NAV__||{},d=window.__CMS_SITE__||{};if(d.autoTheme?.enabled){let t=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>=t(n.dayStart)&&a<t(n.nightStart)?n.dayTheme:n.nightTheme},o=function(e){const a=n.apply&&n.apply[e]||{theme:e,custom:""};Domma.theme.set(a.theme);for(const h of[...document.body.classList])h.startsWith("dm-custom-")&&document.body.classList.remove(h);a.custom&&document.body.classList.add(a.custom)};const n=d.autoTheme;o(s()),setInterval(()=>o(s()),6e4)}const L=$("#site-navbar");if(L.length&&p.brand){let s=function(c,m=0){return Array.isArray(c)?c.filter(l=>!(l&&(l.type==="spacer"||l.type==="theme-switcher"))).filter(l=>!(l&&l.type==="separator"&&m===0)).map(l=>l&&l.type==="separator"?{divider:!0}:Array.isArray(l?.items)?{...l,items:s(l.items,m+1)}:l):c},o=function(c){return Array.isArray(c)?c.filter(m=>!(m&&(m.type==="separator"||m.type==="spacer"||m.type==="theme-switcher"))):[]};const n={...p.brand},t=n.size&&n.size!=="md"?` navbar-brand-${n.size}`:"";if(n.logo||n.icon||n.tagline){let c="";n.logo?c+=`<img src="${n.logo}" class="navbar-brand-logo" alt="${n.text||""}">`:n.icon&&(c+=`<span data-icon="${n.icon}" style="width:1.1em;height:1.1em;margin-right:.35em;vertical-align:middle;"></span>`),n.text&&(c+=`<span class="navbar-brand-text${t}">${n.text}</span>`),n.tagline&&(c+=`<small class="navbar-brand-tagline">${n.tagline}</small>`),n.html=c}else t&&n.text&&(n.html=`<span class="navbar-brand-text${t}">${n.text}</span>`);const e=p.position==="floating",a=p.orientation==="vertical";Domma.elements.navbar("#site-navbar",{brand:n,items:s(p.items||[]),variant:p.variant||"dark",position:e?"static":p.position||"sticky",collapsible:!0,appearOnHover:!!p.appearOnHover}),Domma.icons.scan("#site-navbar");const h=L.get(0);h.classList.add(`dm-nav-align-${Z(p)||"centre"}`),a&&L.get(0).classList.add("dm-nav-vertical",p.side==="right"?"dm-nav-vertical-right":"dm-nav-vertical-left");const w=p.dock||{};a&&!e&&ae(h,p.side==="right"),!a&&w.hideOnScroll&&se(h),O(h,p),N(h,p.class),e&&(G(L.get(0),p),ee(L.get(0).parentNode||document));const i=h.querySelector(".navbar-toggle"),u=h.querySelector(".navbar-collapse");if(i&&u){let c=null;const m=()=>{u.classList.contains("show")&&i.click()},l=()=>{c&&(c.remove(),c=null),document.body.classList.remove("dm-drawer-open")},f=()=>{c||(c=document.createElement("div"),c.className="dm-navbar-backdrop",c.addEventListener("click",m),document.body.appendChild(c),document.body.classList.add("dm-drawer-open"))};i.addEventListener("click",()=>{requestAnimationFrame(()=>{u.classList.contains("show")?f():l()})}),u.addEventListener("click",r=>{r.target.closest("a")&&requestAnimationFrame(l)}),document.addEventListener("keydown",r=>{r.key==="Escape"&&u.classList.contains("show")&&m()})}const y=h.querySelector(".navbar-nav");if(y){const c=Array.from(y.children);let m=0;for(const l of p.items||[])if(!(l&&l.type==="separator")){if(l&&l.type==="spacer"){const f=document.createElement("li"),r=["xs","sm","md","lg","xl","flex"].includes(l.size)?l.size:"md";f.className=`dm-menu-spacer dm-menu-spacer--${r}`,f.setAttribute("aria-hidden","true"),y.insertBefore(f,c[m]||null);continue}if(l&&l.type==="theme-switcher"){const f=document.createElement("li");f.className="dm-menu-theme-switcher";const r=document.createElement("span");r.dataset.themeSwitcher="menu",r.dataset.position="inline",f.appendChild(r),y.insertBefore(f,c[m]||null);continue}m++}window.CMSThemeSwitcher?.scan(y)}const b=o(p.items||[]);h.querySelectorAll(".navbar-link[data-index], .navbar-dropdown-item[data-index]").forEach(c=>{const m=b[parseInt(c.dataset.index,10)];if(!m)return;const l=c.dataset.subindex!=null,f=l&&Array.isArray(m.items)?m.items[parseInt(c.dataset.subindex,10)]:m;if(!f)return;f.colour&&P(c,f.colour),f.pill&&!l&&U(c,f.pill),K(c,f),N(c.closest("li"),f.class);const r=j(f.badge);r&&c.appendChild(r),R(f.url,location.pathname)&&(c.classList.add("active"),c.setAttribute("aria-current","page"))})}const v=$("#site-footer");if(v.length){let e=function(r){return String(r??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},a=function(r){return e(r).replace(/'/g,"&#39;")},h=function(r){if(r&&r.type==="separator")return'<li class="footer-sep"><hr></li>';if(r&&r.type==="spacer")return`<li class="dm-menu-spacer dm-menu-spacer--${["xs","sm","md","lg","xl","flex"].includes(r.size)?r.size:"md"}" aria-hidden="true"></li>`;if(r&&r.type==="theme-switcher")return'<li class="dm-menu-theme-switcher"><span data-theme-switcher="menu" data-position="inline"></span></li>';const x=a(r.url||"#"),g=e(r.text||""),D=Array.isArray(r.items)&&r.items.length?`<ul>${r.items.map(h).join("")}</ul>`:"",q=Q(r),A=[q.hover&&` data-menu-item-hover="${a(q.hover)}"`,q.active&&` data-menu-item-active="${a(q.active)}"`].filter(Boolean).join(""),M=q.accent?` style="--dm-menu-highlight:${a(q.accent)}"`:"",I=W(r.class).join(" ");return`<li${I?` class="${a(I)}"`:""}><a href="${x}"${A}${M}>${g}</a>${D}</li>`},w=function(r,x){return(r||[]).forEach(g=>{g&&(g.type==="separator"||g.type==="spacer"||g.type==="theme-switcher")||(x.push(g),Array.isArray(g.items)&&g.items.length&&w(g.items,x))}),x};const n=d.social||{},t={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 o=window.__CMS_FOOTER__||{primary:[],legal:[],copyright:""};if(o.copyright&&(s+=`<p>${e(o.copyright)}</p>`),o.primary.length&&(s+=`<nav class="footer-nav footer-nav--primary"><ul>${o.primary.map(h).join("")}</ul></nav>`),o.legal.length&&(s+=`<nav class="footer-nav footer-nav--legal"><ul>${o.legal.map(h).join("")}</ul></nav>`),d.footer){const r=Object.keys(t).filter(g=>n[g]),x=W(d.footer.socialClass).join(" ");r.length&&(s+='<div class="footer-social">',r.forEach(g=>{const{label:D,svg:q}=t[g];s+=`<a href="${n[g]}" target="_blank" rel="noopener noreferrer" aria-label="${D}" class="footer-social-link${x?` ${a(x)}`:""}">${q}</a>`}),s+="</div>")}s+="</div>",v.html(s),window.CMSThemeSwitcher?.scan(v.get(0)),N(v.get(0),d.footer?.class);const i=[...w(o.primary,[]),...w(o.legal,[])];v.get(0).querySelectorAll(".footer-nav a").forEach((r,x)=>{const g=i[x];if(!g)return;g.colour&&P(r,g.colour);const D=j(g.badge);D&&r.appendChild(D),R(g.url,location.pathname)&&(r.classList.add("is-active"),r.setAttribute("aria-current","page"))}),O(v.get(0).querySelector(".footer-nav--primary"),{highlight:o.primaryHighlight}),O(v.get(0).querySelector(".footer-nav--legal"),{highlight:o.legalHighlight}),N(v.get(0).querySelector(".footer-nav--primary"),o.primaryClass),N(v.get(0).querySelector(".footer-nav--legal"),o.legalClass);const y=d.footer?.showMotionToggle!==!1,b=S.get("reduced_motion"),c=d.footer?.motionDefault,m=b!=null?!!b:c==="on"?!0:c==="off"?!1:!!(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches),l=d.footer?.motionControl==="checkbox",f=v.get(0).querySelector(".footer-inner");if(y&&f){const r=document.createElement("input");r.type="checkbox",r.className=l?"footer-motion-checkbox":"form-switch-input",r.id="dm-motion-switch",r.checked=m,r.addEventListener("change",function(){S.set("reduced_motion",this.checked),window.location.reload()});const x=document.createElement("span");x.className=l?"footer-motion-label":"form-switch-label",x.textContent="Reduce motion";const g=document.createElement("label");g.className=l?"footer-motion-check":"form-switch footer-motion-switch",g.title="Reduce motion",g.appendChild(r),g.appendChild(x),f.appendChild(g)}}const k=document.getElementById("site-sidebar");k&&ie(k),Domma.icons.scan(),Domma.syntax?.scan&&Domma.syntax.scan();const C=$(".page-body");if(C.length){C.find(".accordion").each(function(){Domma.elements.accordion(this,{allowMultiple:this.dataset.multi==="true"})}),C.find(".tabs").each(function(){Domma.elements.tabs(this)}),C.find(".carousel").each(function(){const n={autoplay:this.dataset.autoplay==="true",interval:parseInt(this.dataset.interval,10)||5e3,loop:this.dataset.loop!=="false",animation:this.dataset.animation||"slide"};if(this.dataset.animationDuration){const t=parseInt(this.dataset.animationDuration,10);Number.isFinite(t)&&(n.animationDuration=t)}this.dataset.animationEasing&&(n.animationEasing=this.dataset.animationEasing),this.dataset.ctx&&(this._dmCtxSlides=Array.from(this.querySelectorAll(".carousel-slide")).map(t=>t.cloneNode(!0)),this._dmCarouselOpts=n),this._dmCarousel=Domma.elements.carousel(this,n)}),C.find(".dm-countdown").each(function(){const n={autoStart:!0},t=String(this.dataset.format||"").trim(),s=String(this.dataset.to||"").trim(),o=/^(\d{4})-(\d{2})-(\d{2})$/.exec(s),e=o?new Date(+o[1],+o[2]-1,+o[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(t);n.format=a?"hh:mm:ss":t||(n.duration>=36e5?"hh:mm:ss":"mm:ss");const h=this,w=u=>String(u).padStart(2,"0"),i=u=>{const y=Math.max(0,Math.floor(u/1e3)),b=h.querySelector(".dm-timer-time");b&&(b.textContent=`${w(Math.floor(y/86400))}:${w(Math.floor(y%86400/3600))}:${w(Math.floor(y%3600/60))}:${w(y%60)}`)};a&&(n.onTick=i),Domma.elements.timer(this,n),a&&i(n.duration||0)}),C.find("[data-tooltip]").each(function(){Domma.elements.tooltip(this,{content:$(this).data("tooltip"),position:$(this).data("tooltip-position")||"top"})}),C.find(".dm-progression").each(function(){const n=Array.from(this.querySelectorAll(".dm-progression-item")).map(o=>{const e={title:o.querySelector(".dm-progression-item-title")?.textContent?.trim()||"",description:o.querySelector(".dm-progression-item-body")?.innerHTML?.trim()||""};return o.dataset.date&&(e.date=o.dataset.date),o.dataset.status&&(e.status=o.dataset.status),o.dataset.entryId&&(e._entryId=o.dataset.entryId),e}),t=this.dataset.layout==="centred"?"centered":this.dataset.layout||"vertical",s=Domma.elements.progression(this,{layout:t,theme:this.dataset.theme||"minimal",mode:this.dataset.mode||"timeline",statusIcons:!0,allowHtml:!0,...n.length?{items:n}:{}});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 t=n.dataset.revealMode||"stagger",s=n.dataset.revealAnimation||"slide-up",o=parseInt(n.dataset.revealDuration,10)||400,e=parseInt(n.dataset.revealStagger,10)||60,a=parseInt(n.dataset.revealDelay,10)||0,h=n.dataset.revealDirection==="rtl",w=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)"};w.forEach((u,y)=>{u.style.opacity="0",u.style.transform=i[s]||"",u.style.transition=`opacity ${o}ms ease, transform ${o}ms ease`;const b=h?w.length-1-y:y;u.style.transitionDelay=t==="stagger"?`${a+b*e}ms`:`${a}ms`}),requestAnimationFrame(()=>requestAnimationFrame(()=>{const u=new IntersectionObserver(y=>{y.forEach(b=>{b.isIntersecting&&(b.target.offsetWidth,b.target.style.opacity="1",b.target.style.transform="none",u.unobserve(b.target))})},{threshold:.1});w.forEach(y=>u.observe(y))}))}),C.find(".card[data-collapsible]").each(function(){const n=this.querySelector(".card-header");n&&n.addEventListener("click",()=>this.classList.toggle("is-collapsed"))}),C.find(".dm-so-trigger").each(function(){this.addEventListener("click",()=>{const n=this.dataset.soTarget,t=document.getElementById(n);if(!t)return;const s=E.slideover({title:t.dataset.soTitle||"",size:t.dataset.soSize||"md",position:t.dataset.soPosition||"right"});t.style.display="",s.setContent(t),s.open()})})}if(typeof $.setup=="function"){const n=Object.assign({},window.__CMS_DCONFIG__||{});if(document.querySelectorAll(".dm-page-config[data-config]").forEach(t=>{try{const s=B(t.dataset.config);Object.assign(n,s)}catch{}}),Object.keys(n).length>0){const t={};for(const[s,o]of Object.entries(n)){const e=o?.events?.click,{confirm:a,toast:h,alert:w,prompt:i,...u}=e||{};a||h||w||i?($(s).on("click",async function(b){if(b.preventDefault(),a&&!await E.confirm(a))return;let c=null;if(!(i&&(c=await E.prompt(i,{inputPlaceholder:u.promptPlaceholder||"",inputValue:u.promptDefault||""}),c===null))){if(u.target){const m=$(u.target);u.toggleClass&&m.toggleClass(u.toggleClass),u.addClass&&m.addClass(u.addClass),u.removeClass&&m.removeClass(u.removeClass),c!==null&&(u.setText&&m.text(c),u.setVal&&m.val(c),u.setAttr&&m.attr(u.setAttr,c))}u.href&&(window.location.href=u.href),h&&E.toast(h,{type:u.toastType||"success"}),w&&E.alert(w)}}),Object.keys(u).length&&(t[s]={...o,events:{...o.events,click:u}})):t[s]=o}$.setup(t)}}C.length&&z(C.get(0))}),window.wireCTAButtons=z;function z(p){p.querySelectorAll(".dm-cta-trigger").forEach(d=>{d.__ctaWired||(d.__ctaWired=!0,d.addEventListener("click",async()=>{const L=d.dataset.action,v=d.dataset.entry,k=d.dataset.confirm;let C=S.get("auth_token");if(!C){E.toast("Please log in to perform this action.",{type:"warning"});return}if(k&&!await E.confirm(k))return;const n=Array.from(d.childNodes).map(s=>s.cloneNode(!0));d.disabled=!0,d.textContent="Running\u2026";const t=s=>fetch(`/api/actions/${L}/public`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify(d.dataset.scope?{entryId:v,scope:d.dataset.scope}:{entryId:v})});try{let s=await t(C);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:h}=await a.json();S.set("auth_token",h),C=h,s=await t(C)}}}const o=await s.json().catch(()=>({}));if(!s.ok)throw new Error(o.error||o.message||`Error ${s.status}`);E.toast(o.message||"Action completed.",{type:"success"})}catch(s){E.toast(s.message||"Action failed.",{type:"error"})}finally{d.disabled=!1,d.textContent="",n.forEach(s=>d.appendChild(s)),Domma.icons.scan(d)}}))})}(function(){const d=document.querySelectorAll('.dm-collection-hydrate[data-collection-scope="mine"]');if(!d.length)return;const L=S.get("auth_token");function v(C,n){const t=document.createElement("div");t.className="dm-collection-display dm-collection-empty";const s=document.createElement("p");if(n){s.appendChild(document.createTextNode("Please "));const o=document.createElement("a");o.href="/login",o.textContent="sign in",s.appendChild(o),s.appendChild(document.createTextNode(" to view your entries."))}else s.textContent=C;return t.appendChild(s),t}function k(C,n){const t=document.createElement("template");t.innerHTML=n||"",C.replaceWith(t.content)}d.forEach(async C=>{const n=C.dataset.collectionAttrs;if(!n)return;if(!L){C.replaceChildren(v("",!0));return}const t=s=>fetch("/api/collections/render-scope",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({attrs:n})});try{let s=L,o=await t(s);if(o.status===401){const a=S.get("auth_refresh_token");if(a){const h=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:a})});if(h.ok){const{token:w}=await h.json();S.set("auth_token",w),s=w,o=await t(s)}}}if(!o.ok){const{error:a}=await o.json().catch(()=>({}));C.replaceChildren(v(a||"Unable to load entries.",!1));return}const{html:e}=await o.json();k(C,e),window.DommaCollectionBrowser?.hydrate&&window.DommaCollectionBrowser.hydrate(document)}catch{C.replaceChildren(v("Unable to load entries.",!1))}}),setTimeout(()=>{typeof Domma<"u"&&Domma.icons?.scan&&Domma.icons.scan(),typeof Domma<"u"&&Domma.syntax?.scan&&Domma.syntax.scan(),z(document.body)},100)})(),(function(){const d=document.querySelectorAll("[data-collection-table]");!d.length||typeof T>"u"||!T.create||d.forEach(L=>{let v;try{v=B(L.dataset.payload)}catch{return}const{columns:k,rows:C,search:n,sortable:t,exportable:s,pageSize:o,empty:e,ctaConfig:a,slug:h,exportCfg:w}=v;if(!k?.length)return;if(a){const y=k.findIndex(b=>b.key==="_cta");y!==-1&&(k[y]={key:"_cta",title:"",render:(b,c)=>{const m=document.createElement("button");if(m.className=`btn btn-${a.style||"primary"} dm-cta-trigger`,m.dataset.action=a.action||"",a.scope&&(m.dataset.scope=a.scope),m.dataset.entry=c._entryId||"",a.confirm&&(m.dataset.confirm=a.confirm),a.icon){const l=document.createElement("span");l.dataset.icon=a.icon,m.appendChild(l),m.appendChild(document.createTextNode(" "))}return m.appendChild(document.createTextNode(a.label||"Run")),m}})}const i="col-table-"+Math.random().toString(36).slice(2,7),u=document.createElement("div");u.id=i,L.replaceChildren(u),T.create("#"+i,{data:C,columns:k,search:n,sortable:t,exportable:s,pageSize:o,emptyMessage:e}),a&&z(u),L._dmTable={columns:k,rows:C,slug:h,exportCfg:w,render(y){T.create("#"+i,{data:y,columns:k,search:n,sortable:t,exportable:s,pageSize:o,emptyMessage:e}),a&&z(u)}}})})(),(function(){const d=document.querySelectorAll("[data-form-inline]");if(!d.length||typeof F>"u")return;function L(t){const s=t&&t.logic;return!!(s&&(s.visibility||s.requirement))}function v(t,s){const o={};return(t||[]).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)),h=e.type==="checkbox"?"boolean":e.type==="date"||e.type==="icon"||e.type==="image"?"string":a?"chooser":e.type,w={...e.formConfig||{}};w.span==="full"&&s&&(w.span=s);const i={type:h,required:L(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},...w}};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)),o[e.name]=i}),o}function k(t){const s={};return(t||[]).forEach(o=>{if(!(!o.name||o.type==="page-break"||o.type==="spacer")&&(o.type==="select"||o.type==="multiselect")&&o.required){const e=(o.options||[])[0];e&&(s[o.name]=typeof e=="object"?e.value:e)}}),s}function C(t,s){(s||[]).forEach(o=>{if(o.type!=="date"||!o.name)return;const e=t.querySelector(`[name="${o.name}"]`);e&&e.type!=="date"&&(e.type="date")})}function n(t,s,o){let e=t.querySelector(".cms-form-message");e||(e=document.createElement("p"),e.className="cms-form-message",t.appendChild(e)),e.textContent=s,e.style.cssText=o?"color:var(--dm-danger);margin-top:.75rem;":"color:var(--dm-success);margin-top:.75rem;"}d.forEach(t=>{let s;try{s=B(t.dataset.formInline)}catch{return}const o=s.fields||[],e=s.settings||{},a=Number(e.columns)===3?3:2,h=e.layout||"grid",w=o.some(m=>m.type==="page-break"),i=async m=>{if(t.dataset.triggerBlocked==="true"){const l=t._formLogicRuntime,f=l&&l.submitBlock?l.submitBlock().reasons:[];n(t,f.join(" ")||"This form cannot be submitted yet.",!0);const r=new Error("Blocked by a form trigger");throw r.formSubmitFailed=!0,r}try{const l=t.querySelector(".fb-form-honeypot"),f=l?.querySelector('input[type="text"]')||null,r=l?.querySelector('[name="_t"]')||null,x=Object.assign({},m);delete x._t,l?.dataset.hpName&&delete x[l.dataset.hpName],f!==null&&(x._hp=f.value),r!==null&&(x._t=r.value);const g=await H.post(`/api/forms/submit/${s.slug}`,x);if(g&&g.error){n(t,g.error,!0);const D=new Error(g.error);throw D.formSubmitFailed=!0,D}if(g?.redirect){window.location.href=g.redirect;return}for(;t.firstChild;)t.removeChild(t.firstChild);n(t,g?.message||e.successMessage||"Thank you for your submission.",!1)}catch(l){throw l.formSubmitFailed||n(t,l.message||"Submission failed. Please try again.",!0),l}},u=Date.now();function y(m){const l=m.querySelector("form");if(!l||l.querySelector(".fb-form-honeypot"))return;const f=document.createElement("div");f.className="fb-form-honeypot",f.setAttribute("aria-hidden","true");const r=document.createElement("input");r.name=l.querySelector('[name="website"]')?"_website":"website",f.dataset.hpName=r.name,r.type="text",r.tabIndex=-1,r.autocomplete="url",r.placeholder="https://",f.appendChild(r);const x=document.createElement("input");x.name="_t",x.type="hidden",x.value=u,f.appendChild(x),l.appendChild(f)}function b(m){if(!window.FormLogicEngine||!o.some(f=>f.logic||Array.isArray(f.triggers)&&f.triggers.length))return null;const l=new window.FormLogicEngine.FormLogicRuntime(s,t,m?{baseValues:m}:void 0);if(l.init(),t._formLogicRuntime=l,t._formLogicObserved)return l;if(t._formLogicObserved=!0,t.parentNode&&typeof MutationObserver<"u"){const f=new MutationObserver(function(r){for(const x of r)for(const g of x.removedNodes)if(g===t||g.nodeType===1&&g.contains&&g.contains(t)){t._formLogicRuntime&&t._formLogicRuntime.destroy(),f.disconnect();return}});f.observe(t.parentNode,{childList:!0,subtree:!1}),t._formLogicObserver=f}return l}let c=!1;if(t.addEventListener("dm-form-end",m=>{const l=m.detail?.validation;if(l&&!l.valid){Y(t,l.missing),J(l);return}c||(c=!0,i(m.detail?.values||{}).catch(()=>{}).finally(()=>{c=!1}))}),w&&F.wizard){const m=te(o,s.title),l=m.map(A=>({title:A.title,description:A.description,fields:v(A.fields,a)})),f=m.map(A=>A.fields);let r=0,x=null,g=null;const D=A=>{const M=window.FormLogicEngine;if(!M||!M.validateFields)return;const I=M.validateFields(t,f[r]||[]);I.valid||(A.preventDefault(),A.stopImmediatePropagation(),Y(t,I.missing),J(I))},q=()=>{const A=f[r]||o;oe(t,e),X(t,e,A),C(t,A),V(t,A),e.honeypot!==!1&&y(t),x&&x.destroy(),x=b(g?.getStepData?g.getStepData():void 0),t.querySelectorAll(".wizard-next, .wizard-submit").forEach(M=>M.addEventListener("click",D,!0))};g=F.wizard(t,{schema:{steps:l},layout:h,columns:a,finishText:e.submitText||"Finish",onSubmit:i,onStepChange:A=>{r=A,setTimeout(q,0)}}),t.addEventListener("dm-form-trigger",A=>{if(A.detail?.type!=="goto-step"||!g?.goToStep)return;const M=Number(A.detail.action.step);!Number.isFinite(M)||M<0||M>=l.length||M!==r&&g.goToStep(M)}),Promise.resolve(g).then(q)}else if(F.render){const m=F.render(t,v(o,a),k(o),{submitText:e.submitText||"Submit",layout:h,columns:a,onSubmit:i});Promise.resolve(m).then(function(){ne(t,e,h),X(t,e,o),C(t,o),V(t,o),e.honeypot!==!1&&y(t),b()})}}),$(document).on("click",".dm-banner__dismiss",function(){$(this).closest(".dm-banner").remove()})})();function ae(p,d){const L=document.body,v=L.classList,k=new Set((L.dataset.railSite||"").split(" ").filter(Boolean)),C=()=>window.matchMedia("(min-width: 993px)").matches;p.querySelectorAll(".navbar-link, .navbar-dropdown-toggle").forEach(a=>{const h=a.textContent.trim();h&&(a.setAttribute("title",h),a.querySelector("svg, [data-icon]")||(a.dataset.initial=h.charAt(0).toUpperCase()))});const n=document.createElement("button");n.type="button",n.className="dm-nav-collapse-toggle";const t=()=>{const a=v.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 h=d?"chevrons-left":"chevrons-right",w=d?"chevrons-right":"chevrons-left";n.innerHTML=`<span data-icon="${a?h:w}"></span>`,window.Domma&&Domma.icons&&Domma.icons.scan(n)},s=a=>{v.toggle("dm-vnav-collapsed",a),S.set("nav_collapsed",a),t()};n.addEventListener("click",()=>s(!v.contains("dm-vnav-collapsed"))),t();const o=p.querySelector(".navbar-container")||p;o.insertBefore(n,o.firstChild?.nextSibling||null);const e=a=>{v.toggle("dm-vnav-autohide",a),v.toggle("dm-vnav-collapsible",!a&&k.has("collapsible")),a?v.remove("dm-vnav-collapsed"):k.has("collapsible")&&S.get("nav_collapsed")===!0&&v.add("dm-vnav-collapsed"),p.classList.remove("dm-nav-revealed"),t()};p.addEventListener("pointerdown",a=>{v.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=v.contains("dm-vnav-autohide"),h=v.contains("dm-vnav-collapsed"),w=S.has("nav_autohide")||S.has("nav_collapsed");return[{header:"Menu"},{label:h?"Expand menu":"Collapse to icons",icon:h?"maximize":"minimize",disabled:!C()||a||!k.has("collapsible"),action:()=>s(!h)},{label:a?"Keep on screen":"Hide when not in use",icon:a?"eye":"eye-off",disabled:!C(),action:()=>{S.set("nav_autohide",!a),e(!a)}},{type:"divider"},{label:"Reset to the site's setting",icon:"refresh",disabled:!w,action:()=>{S.remove("nav_autohide"),S.remove("nav_collapsed"),e(k.has("autohide")),v.toggle("dm-vnav-collapsed",!k.has("autohide")&&k.has("start-collapsed")),t()}}]}})}function se(p){p.classList.add("dm-nav-scroll-hide");let d=window.scrollY,L=!1;window.addEventListener("scroll",()=>{L||(L=!0,requestAnimationFrame(()=>{L=!1;const v=window.scrollY,k=p.matches(":focus-within")||p.querySelector(".navbar-dropdown.open, .navbar-collapse.show");v>d&&v>p.offsetHeight&&!k?p.classList.add("dm-nav-scrolled-away"):v<d&&p.classList.remove("dm-nav-scrolled-away"),d=v}))},{passive:!0})}function ie(p){let d={};try{d=JSON.parse(p.dataset.contents||"{}")}catch{}const L=p.closest(".site-main"),v=document.querySelector(".site-content"),k=new Set(String(d.exclude||"").split(",").map(i=>i.trim().toLowerCase()).filter(Boolean)),C=i=>!i.closest(".hero, .card, .accordion, .tabs, figure, #site-sidebar"),n=i=>(v?[...v.querySelectorAll(i)]:[]).filter(C).filter(u=>!k.has(u.textContent.trim().toLowerCase())),t=d.headings==="h2"?n("h2"):d.headings==="h3"?n("h3"):d.headings==="h2h3"?n("h2, h3"):n("h2").length?n("h2"):n("h3");if(!t.length){L?.classList.add("no-sidebar-items");return}const s=new Set([...document.querySelectorAll("[id]")].map(i=>i.id)),o=document.createElement("nav");o.className="dm-contents",o.setAttribute("aria-label",d.title||"Page contents"),d.hover&&(o.dataset.menuHover=d.hover),d.selected&&(o.dataset.menuActive=d.selected),d.accent&&o.style.setProperty("--dm-menu-highlight",d.accent.startsWith("#")?d.accent:`var(--dm-${d.accent})`),d.font&&(o.style.fontFamily=d.font),d.fontSize&&(o.style.fontSize=d.fontSize),d.fontWeight&&(o.style.fontWeight=d.fontWeight);const e=document.createElement("div");e.className="dm-contents-head";const a=document.createElement("p");a.className="dm-contents-title",a.textContent=d.title||"",e.appendChild(a),o.appendChild(e);const h=document.createElement("ol");h.className="dm-contents-list"+(d.numbered?" is-numbered":"");const w=[];for(const i of t){if(!i.id){const b=(i.textContent||"").toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"section";let c=b;for(let m=2;s.has(c);m++)c=`${b}-${m}`;s.add(c),i.id=c}const u=document.createElement("li");u.className=`dm-contents-item dm-contents-${i.tagName.toLowerCase()}`;const y=document.createElement("a");y.href=`#${i.id}`,y.textContent=i.textContent.trim(),u.appendChild(y),h.appendChild(u),w.push([i,y])}if(o.appendChild(h),p.replaceChildren(o),d.scrollSpy!==!1&&"IntersectionObserver"in window){const i=new Map,u=()=>{const b=w.find(([c])=>i.get(c))||null;w.forEach(([,c])=>{const m=b&&c===b[1];c.classList.toggle("is-active",!!m),m?c.setAttribute("aria-current","location"):c.removeAttribute("aria-current")})},y=new IntersectionObserver(b=>{b.forEach(c=>i.set(c.target,c.isIntersecting)),u()},{rootMargin:"-80px 0px -60% 0px"});w.forEach(([b])=>y.observe(b))}if(L?.classList.contains("contents-collapsible")){const i=document.createElement("button");i.type="button",i.className="dm-contents-toggle";const u=()=>{const y=L.classList.contains("contents-collapsed");i.setAttribute("aria-expanded",String(!y)),i.setAttribute("aria-label",y?"Show the contents":"Hide the contents"),i.title=i.getAttribute("aria-label"),i.innerHTML=`<span data-icon="${y?"list":"x"}"></span>`,window.Domma?.icons&&Domma.icons.scan(i)};i.addEventListener("click",()=>{S.set("contents_collapsed",L.classList.toggle("contents-collapsed")),u()}),u(),e.insertBefore(i,e.firstChild)}if(L?.classList.contains("contents-floating")&&d.movable){o.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",u=>{if(u.target.closest("button"))return;const y=p.getBoundingClientRect(),b=u.clientX-y.left,c=u.clientY-y.top,m=f=>{const r=Math.max(0,Math.min(window.innerWidth-y.width,f.clientX-b)),x=Math.max(0,Math.min(window.innerHeight-40,f.clientY-c));Object.assign(p.style,{left:`${r}px`,top:`${x}px`,right:"auto",bottom:"auto"})},l=()=>{document.removeEventListener("pointermove",m),S.set("contents_position",{x:parseInt(p.style.left,10),y:parseInt(p.style.top,10)})};document.addEventListener("pointermove",m),document.addEventListener("pointerup",l,{once:!0})})}window.E&&typeof E.contextMenu=="function"&&E.contextMenu(p,{items:()=>{const i=L?.classList.contains("contents-collapsible"),u=L?.classList.contains("contents-collapsed"),y=w.find(([,b])=>b.classList.contains("is-active"));return[{header:"Contents"},...i?[{label:u?"Show the contents":"Collapse to a button",icon:u?"list":"minimize",action:()=>p.querySelector(".dm-contents-toggle")?.click()}]:[],{label:"Copy a link to this section",icon:"link",disabled:!y,action:async()=>{const b=`${location.origin}${location.pathname}#${y[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"})}]}}),L?.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")}))}
@@ -12,6 +12,7 @@
12
12
  *
13
13
  * `_` is outside the key alphabet (slugify below), so no layout can shadow the last two.
14
14
  */
15
+ import {normaliseContents} from '../../services/pageContents.js';
15
16
  import {getConfig, saveConfig} from '../../config.js';
16
17
  import {
17
18
  authenticate as defaultAuthenticate,
@@ -77,6 +78,8 @@ function sanitisePreset(data) {
77
78
  footer: data.footer !== false,
78
79
  sidebar: data.sidebar === true,
79
80
  sidebarSide: data.sidebarSide === 'right' ? 'right' : 'left',
81
+ // The page contents list's settings (pageContents.js); empty when none are set.
82
+ ...(Object.keys(normaliseContents(data.contents)).length && {contents: normaliseContents(data.contents)}),
80
83
  width: VALID_WIDTHS.has(data.width) ? data.width : 'normal',
81
84
  bgColor: data.bgColor && BG_COLOR_RE.test(data.bgColor) ? data.bgColor : '',
82
85
  bgImage: typeof data.bgImage === 'string' ? data.bgImage.trim() : '',
@@ -13,6 +13,7 @@ import {applyTransforms, getSanitizeExtensions, getShortcodeProcessors} from './
13
13
  import {getConfig} from '../config.js';
14
14
  import {getCollection, listEntries} from './collections.js';
15
15
  import {getMenu, navbarOverrideFromAttrs, resolveLocation, resolveMenuDecorations} from './menus.js';
16
+ import {contentsOverrideFromAttrs} from './pageContents.js';
16
17
  import {buildMenuNav} from './menuRender.js';
17
18
  import {checkVisibility} from '../middleware/auth.js';
18
19
  import {getRoleHierarchy, getRoleLevel} from './roles.js';
@@ -3633,6 +3634,26 @@ function processSpacerBlocks(markdown) {
3633
3634
  * [navbar autohide="true" width="200" /]
3634
3635
  * [navbar position="floating" float-anchor="TR" float-collapsible="true" /]
3635
3636
  */
3637
+ /**
3638
+ * [contents ... /] - this page's own settings for its contents list (the
3639
+ * sidebar a layout builds from the page's headings), over the layout's. See
3640
+ * pageContents.js for the attributes. Leaves a hidden marker the renderer reads
3641
+ * (the first tag on a page counts); draws nothing itself.
3642
+ *
3643
+ * [contents side="right" numbered="true" /]
3644
+ * [contents headings="h2h3" title="On this page" exclude="API Reference" /]
3645
+ * [contents floating="true" float-anchor="BR" movable="true" /]
3646
+ * [contents hidden="true" /]
3647
+ */
3648
+ function processContentsBlocks(markdown) {
3649
+ const {scrubbed, restore} = scrubCodeRegions(markdown);
3650
+ return restore(scrubbed.replace(/\[contents(\s[^\]]*?)?\/?\]/gi, (_, attrStr) => {
3651
+ const override = contentsOverrideFromAttrs(parseShortcodeAttrs(attrStr || ''));
3652
+ if (!Object.keys(override).length) return '';
3653
+ return `<span class="dm-contents-override" hidden data-contents-override="${escapeAttr(JSON.stringify(override))}"></span>`;
3654
+ }));
3655
+ }
3656
+
3636
3657
  function processNavbarBlocks(markdown) {
3637
3658
  const {scrubbed, restore} = scrubCodeRegions(markdown);
3638
3659
  return restore(scrubbed.replace(/\[navbar(\s[^\]]*?)?\/?\]/gi, (_, attrStr) => {
@@ -4494,7 +4515,7 @@ export async function parseMarkdown(raw, opts = {}) {
4494
4515
  const withTimeline = processTimelineBlocks(withCountdown);
4495
4516
  const withListGroup = processListGroupBlocks(withTimeline);
4496
4517
  const withSpacer = processSpacerBlocks(withListGroup);
4497
- const withSwitcher = processNavbarBlocks(processThemeSwitcherBlocks(withSpacer));
4518
+ const withSwitcher = processContentsBlocks(processNavbarBlocks(processThemeSwitcherBlocks(withSpacer)));
4498
4519
  const withCenter = processCenterBlocks(withSwitcher);
4499
4520
  const withIcon = processIconBlocks(withCenter);
4500
4521
  const withDemo = processDemoBlocks(withIcon);
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Page contents - the list a layout with a sidebar builds from the page's own
3
+ * headings (the "With Sidebar" layout's left or right column).
4
+ *
5
+ * Its settings live on the layout (presets.json: `contents: {...}`, edited in
6
+ * Layouts) and a page can change any of them for itself with `[contents ... /]`.
7
+ * The renderer merges layout, page front matter (`sidebarSide`, kept for older
8
+ * pages) and the page's tag, and hands the result to site.js as __CMS_CONTENTS__;
9
+ * the parts the first paint needs (side, width, hidden, floating, autohide) also
10
+ * become classes and a token on the page.
11
+ *
12
+ * Layout side, width, sticky, floating, floatAnchor, movable, hidden
13
+ * Behaviour collapsible, collapsed, autohide, scrollSpy
14
+ * Content headings ('auto' | 'h2' | 'h3' | 'h2h3'), numbered, title, exclude
15
+ * Look hover, selected, accent, font, fontSize, fontWeight
16
+ */
17
+ import {FLOAT_ANCHORS, isValidColour} from './menus.js';
18
+ import {HIGHLIGHT_HOVER, HIGHLIGHT_ACTIVE} from '../../public/js/menu-decor.mjs';
19
+
20
+ export const CONTENTS_WIDTH_MIN = 160;
21
+ export const CONTENTS_WIDTH_MAX = 480;
22
+ const HEADINGS = new Set(['auto', 'h2', 'h3', 'h2h3']);
23
+ const BOOL_KEYS = ['sticky', 'floating', 'movable', 'hidden', 'collapsible', 'collapsed', 'autohide', 'scrollSpy', 'numbered'];
24
+ const CSS_SIZE = /^\d+(\.\d+)?(px|rem|em|%)$/;
25
+ const FONT = /^[A-Za-z0-9 ,'"-]{1,80}$/;
26
+
27
+ /** The defaults: what the With Sidebar layout has always done. */
28
+ export const CONTENTS_DEFAULTS = Object.freeze({side: 'left', sticky: true, scrollSpy: true, headings: 'auto'});
29
+
30
+ /**
31
+ * A contents block with only known keys and sane values. Booleans keep `false`
32
+ * (a page turning off what its layout turns on); unknown or bad values are dropped.
33
+ */
34
+ export function normaliseContents(input) {
35
+ if (!input || typeof input !== 'object' || Array.isArray(input)) return {};
36
+ const out = {};
37
+ if (input.side === 'left' || input.side === 'right') out.side = input.side;
38
+ const width = parseInt(input.width, 10);
39
+ if (Number.isFinite(width)) out.width = Math.min(CONTENTS_WIDTH_MAX, Math.max(CONTENTS_WIDTH_MIN, width));
40
+ for (const key of BOOL_KEYS) if (typeof input[key] === 'boolean') out[key] = input[key];
41
+ if (FLOAT_ANCHORS.has(input.floatAnchor)) out.floatAnchor = input.floatAnchor;
42
+ if (HEADINGS.has(input.headings)) out.headings = input.headings;
43
+ if (typeof input.title === 'string' && input.title.trim()) out.title = input.title.trim().slice(0, 80);
44
+ if (typeof input.exclude === 'string' && input.exclude.trim()) out.exclude = input.exclude.trim().slice(0, 500);
45
+ if (HIGHLIGHT_HOVER.includes(input.hover)) out.hover = input.hover;
46
+ if (HIGHLIGHT_ACTIVE.includes(input.selected)) out.selected = input.selected;
47
+ if (input.accent && isValidColour(input.accent)) out.accent = input.accent;
48
+ if (typeof input.font === 'string' && FONT.test(input.font)) out.font = input.font;
49
+ if (typeof input.fontSize === 'string' && CSS_SIZE.test(input.fontSize)) out.fontSize = input.fontSize;
50
+ if (/^[1-9]00$|^(normal|bold)$/.test(String(input.fontWeight || ''))) out.fontWeight = String(input.fontWeight);
51
+ return out;
52
+ }
53
+
54
+ /** The tag's attributes (kebab-case) and the keys they set. */
55
+ export const CONTENTS_ATTRS = {
56
+ 'side': 'side', 'width': 'width', 'sticky': 'sticky', 'floating': 'floating', 'float-anchor': 'floatAnchor',
57
+ 'movable': 'movable', 'hidden': 'hidden', 'collapsible': 'collapsible', 'collapsed': 'collapsed',
58
+ 'autohide': 'autohide', 'scroll-spy': 'scrollSpy', 'headings': 'headings', 'numbered': 'numbered',
59
+ 'title': 'title', 'exclude': 'exclude', 'hover': 'hover', 'selected': 'selected', 'accent': 'accent',
60
+ 'font': 'font', 'font-size': 'fontSize', 'font-weight': 'fontWeight'
61
+ };
62
+
63
+ /** The override a `[contents]` tag's attributes describe (normalised). */
64
+ export function contentsOverrideFromAttrs(attrs = {}) {
65
+ const raw = {};
66
+ for (const [name, value] of Object.entries(attrs)) {
67
+ const key = CONTENTS_ATTRS[name];
68
+ if (!key) continue;
69
+ let v = value === true ? 'true' : String(value).trim();
70
+ if (BOOL_KEYS.includes(key)) v = !(v === 'false' || v === '0' || v === 'no');
71
+ raw[key] = v;
72
+ }
73
+ return normaliseContents(raw);
74
+ }
75
+
76
+ /**
77
+ * The settings a page gets: defaults, then its layout's, then the older
78
+ * `sidebarSide` (layout, then page), then the page's `[contents]` tag.
79
+ */
80
+ export function resolveContents(preset = {}, page = {}, override = null) {
81
+ const layout = normaliseContents(preset.contents);
82
+ const merged = {
83
+ ...CONTENTS_DEFAULTS,
84
+ ...(preset.sidebarSide === 'right' && {side: 'right'}),
85
+ ...layout,
86
+ ...(page.sidebarSide === 'right' || page.sidebarSide === 'left' ? {side: page.sidebarSide} : {}),
87
+ ...normaliseContents(override || {})
88
+ };
89
+ // Autohide and floating take the list out of the column; collapse then means nothing.
90
+ if (merged.autohide || merged.floating) { delete merged.collapsible; delete merged.collapsed; }
91
+ if (merged.collapsed) merged.collapsible = true;
92
+ return merged;
93
+ }
94
+
95
+ /**
96
+ * The classes the page's <main> needs from the first paint, and the width token.
97
+ * @returns {{mainClass: string, style: string}}
98
+ */
99
+ export function contentsPaint(c) {
100
+ const cls = [
101
+ c.side === 'right' ? 'sidebar-right' : '',
102
+ c.hidden ? 'contents-hidden' : '',
103
+ c.floating ? `contents-floating contents-float-${(c.floatAnchor || 'TR').toLowerCase()}` : '',
104
+ c.autohide ? 'contents-autohide' : '',
105
+ c.sticky === false ? 'contents-static' : '',
106
+ c.collapsible ? 'contents-collapsible' : '',
107
+ c.collapsed ? 'contents-start-collapsed' : ''
108
+ ].filter(Boolean).join(' ');
109
+ const style = Number.isFinite(c.width) ? `--dm-contents-width:${c.width}px` : '';
110
+ return {mainClass: cls, style};
111
+ }
112
+
113
+ /** The override a page's [contents] tag left in its html (the first), or null. */
114
+ export function readContentsOverride(html) {
115
+ const m = /data-contents-override="([^"]*)"/.exec(html || '');
116
+ if (!m) return null;
117
+ try {
118
+ return JSON.parse(m[1].replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&'));
119
+ } catch {
120
+ return null;
121
+ }
122
+ }
@@ -8,6 +8,7 @@ import {fileURLToPath} from 'url';
8
8
  import {getConfig} from '../config.js';
9
9
  import {getInjectionSnippets} from './plugins.js';
10
10
  import {applyTransforms} from './hooks.js';
11
+ import {contentsPaint, readContentsOverride, resolveContents} from './pageContents.js';
11
12
  import {applyMenuOverride, hasThemeSwitcherItem, resolveLocation, resolveMenuDecorations, resolveOverlaysForPage} from './menus.js';
12
13
  import {buildMenuNav} from './menuRender.js';
13
14
  import {getProjectForPage} from './projects.js';
@@ -122,6 +123,17 @@ function buildAnalyticsTag() {
122
123
  * html (body and overlay menus) and the item lists of its navbar and footers
123
124
  * @returns {{headTag: string, bodyTag: string}} Empty strings when nothing needs it
124
125
  */
126
+ /** A value for a double-quoted HTML attribute. */
127
+ function escapeAttrValue(v) {
128
+ return String(v).replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
129
+ }
130
+
131
+ /** The site's reduced-motion default for visitors who have not chosen: 'system', 'off' or 'on'. */
132
+ function motionDefaultOf(site) {
133
+ const d = site?.footer?.motionDefault;
134
+ return d === 'off' || d === 'on' ? d : 'system';
135
+ }
136
+
125
137
  /** The override a page's [navbar] tag left in its html (the first one), or null. */
126
138
  function readNavbarOverride(html) {
127
139
  const m = /data-navbar-override="([^"]*)"/.exec(html || '');
@@ -303,6 +315,7 @@ export async function renderPage(page, opts = {}) {
303
315
  : (await readLegacyNavBackup()) || {brand: site.brand || {}, items: []};
304
316
 
305
317
  const preset = presets[page.layout] || presets['default'] || {};
318
+ const pageContents = resolveContents(preset, page, readContentsOverride(page.html));
306
319
 
307
320
  // Per-page `width:` frontmatter overrides the preset's width when valid.
308
321
  const pageWidth = VALID_LAYOUT_WIDTHS.has(page.width) ? page.width : null;
@@ -418,11 +431,17 @@ export async function renderPage(page, opts = {}) {
418
431
  layoutBodyClass,
419
432
  pageBodyStyle,
420
433
  navbarClass,
434
+ motionDefault: motionDefaultOf(site),
421
435
  railDocked: /\bdm-vnav\b/.test(verticalNavClass),
422
436
  showNavbar: preset.navbar !== false,
423
437
  showFooter: preset.footer !== false,
424
- showSidebar: preset.sidebar === true || page.sidebar === true,
425
- sidebarRight: (page.sidebarSide || preset.sidebarSide) === 'right',
438
+ // The page contents list: the layout's settings, the page's sidebarSide and its
439
+ // [contents] tag merged (pageContents.js); `hidden` drops the column altogether.
440
+ showSidebar: (preset.sidebar === true || page.sidebar === true) && !pageContents.hidden,
441
+ contentsClass: contentsPaint(pageContents).mainClass,
442
+ contentsStyle: contentsPaint(pageContents).style,
443
+ contentsJson: escapeAttrValue(JSON.stringify(pageContents)),
444
+ contentsCollapsible: pageContents.collapsible === true && (preset.sidebar === true || page.sidebar === true) && !pageContents.hidden,
426
445
  navJson: JSON.stringify(filterHiddenNavItems(navigation)).replace(/<\/script>/gi, '<\\/script>'),
427
446
  footerScript: footerJson ? `window.__CMS_FOOTER__ = ${footerJson};` : '',
428
447
  ctxMenusScript: ctxMenusScript,
@@ -1161,6 +1180,7 @@ export async function renderBlogPage(templatePath, data = {}, seoMeta = {}) {
1161
1180
  ? railBodyClass(navigation)
1162
1181
  : '', ...cssClasses(site.bodyClass)].filter(Boolean).join(' '),
1163
1182
  pageBodyStyle: '',
1183
+ motionDefault: motionDefaultOf(site),
1164
1184
  railDocked: navigation.orientation === 'vertical' && /\bdm-vnav\b/.test(railBodyClass(navigation)),
1165
1185
  showNavbar: true,
1166
1186
  showFooter: true,
@@ -25,7 +25,7 @@
25
25
  <link rel="stylesheet" href="/dist/domma/themes/domma-themes.css">
26
26
 
27
27
  <!-- Site CSS -->
28
- <link rel="stylesheet" href="/public/css/site.css?v=20261011-docked-menus">
28
+ <link rel="stylesheet" href="/public/css/site.css?v=20261011-page-contents">
29
29
  <link rel="stylesheet" href="/public/css/forms.css?v=20260927-submit-btn-css">
30
30
  <!-- Menu item hover / current-page effects. Loaded after site.css so a
31
31
  menu's own highlight wins over the blanket link styling there. -->
@@ -65,9 +65,19 @@
65
65
  <nav id="site-navbar" class="{{navbarClass}}"></nav>
66
66
  {{/if}}
67
67
 
68
- <main class="site-main {{#if showSidebar}}with-sidebar{{/if}} {{#if sidebarRight}}sidebar-right{{/if}}">
68
+ <main class="site-main {{#if showSidebar}}with-sidebar {{contentsClass}}{{/if}}"{{#if contentsStyle}} style="{{contentsStyle}}"{{/if}}>
69
69
  {{#if showSidebar}}
70
- <aside id="site-sidebar" class="site-sidebar"></aside>
70
+ <aside id="site-sidebar" class="site-sidebar" data-contents="{{contentsJson}}"></aside>
71
+ {{/if}}
72
+ {{#if contentsCollapsible}}
73
+ <script>
74
+ // The visitor's own choice (or the layout's starting state) before the first paint.
75
+ (function () {
76
+ var m = document.currentScript.parentNode, c = null;
77
+ try { c = JSON.parse(localStorage.getItem('domma:contents_collapsed')); } catch (e) {}
78
+ if (c === true || (c === null && m.classList.contains('contents-start-collapsed'))) m.classList.add('contents-collapsed');
79
+ })();
80
+ </script>
71
81
  {{/if}}
72
82
 
73
83
  <article class="site-content">
@@ -98,6 +108,13 @@
98
108
  _stored = JSON.parse(localStorage.getItem('domma:reduced_motion'));
99
109
  } catch (e) {
100
110
  }
111
+ // No choice of the visitor's yet: the site's default (Site Settings > Footer) -
112
+ // 'on' or 'off' decide it; 'system' (or nothing) leaves it to the device.
113
+ if (_stored === null || _stored === undefined) {
114
+ var _def = '{{motionDefault}}';
115
+ if (_def === 'on') _stored = true;
116
+ else if (_def === 'off') _stored = false;
117
+ }
101
118
  if (_stored === true) {
102
119
  document.documentElement.classList.add('dm-reduced-motion');
103
120
  }
@@ -156,7 +173,7 @@
156
173
  </script>
157
174
 
158
175
  <!-- Site initialisation -->
159
- <script src="/public/js/site.js?v=20261011-docked-menus" type="module"></script>
176
+ <script src="/public/js/site.js?v=20261011-page-contents" type="module"></script>
160
177
  <script src="/public/js/collection-context.js?v=20261010-utf8-payloads" type="module"></script>
161
178
  {{ctxMenusModule}}
162
179