#jsd-editor-ui{font-family:"DM Sans",sans-serif}.jsd-edit-link{font-size:12px;opacity:.65}.jsd-auth{border:0;border-radius:16px;padding:0;background:var(--paper);color:var(--ink);box-shadow:0 30px 80px #0005}.jsd-auth::backdrop{background:#151713aa}.jsd-auth form{width:min(400px,calc(100vw - 40px));padding:30px}.jsd-auth h2{font-size:27px}.jsd-auth-actions{display:flex;gap:10px;justify-content:flex-end;margin-top:20px}.jsd-error{color:#8b2f2f;font-size:14px;min-height:1.5em}.jsd-bar{position:fixed;z-index:90;left:16px;right:16px;bottom:16px;display:flex;align-items:center;gap:10px;padding:12px 14px;border-radius:14px;background:#20221f;color:#fff;box-shadow:0 15px 45px #0004}.jsd-bar span{margin-right:auto;font-size:14px}.jsd-bar button{min-height:42px;border:1px solid #ffffff55;border-radius:99px;padding:8px 16px;background:transparent;color:#fff;font:600 13px inherit;cursor:pointer}.jsd-bar .jsd-save{background:#fff;co…1666 tokens truncated…a-action="logout">Log out</button><button data-action="cancel">Cancel</button><button data-action="reset">Reset page</button><button class="jsd-save" data-action="save">Save</button>';bar.addEventListener("click",e=>{const action=e.target.dataset.action;if(action==="save")save();if(action==="cancel")cancel();if(action==="reset")resetPage();if(action==="logout")logout()});document.body.append(bar)}
async function save(){const updates={};for(const [el,original]of state.original)if(sanitizeHTML(el.innerHTML)!==sanitizeHTML(original))updates[el.dataset.editKey]=sanitizeHTML(el.innerHTML);if(Object.keys(updates).length){const response=await fetch(`/.netlify/functions/content?page=${encodeURIComponent(PAGE)}`,{method:"POST",headers:headers(),body:JSON.stringify({page:PAGE,updates})});if(!response.ok){alert("The changes could not be saved. Sign in again and retry.");return}}await exit()}
async function cancel(){for(const[el,html]of state.original)el.innerHTML=html;await exit()}
async function exit(){state.editing=false;clearInterval(state.heartbeat);await lock("release");for(const el of textNodes()){el.contentEditable="false";el.classList.remove("jsd-editable")}for(const img of images())img.classList.remove("jsd-image-editable");document.querySelector(".jsd-bar")?.remove()}
async function resetPage(){if(!confirm("Reset every saved edit on this page?"))return;await fetch(`/.netlify/functions/content?page=${encodeURIComponent(PAGE)}`,{method:"DELETE",headers:headers()});location.reload()}
async function logout(){if(state.editing)await cancel();await fetch("/.netlify/functions/otp",{method:"DELETE",headers:headers()});state.token="";sessionStorage.removeItem(TOKEN_KEY);location.reload()}
async function replaceImage(e){if(!state.editing)return;e.preventDefault();const img=e.currentTarget,input=document.createElement("input");input.type="file";input.accept="image/jpeg,image/png,image/webp,image/gif";input.onchange=async()=>{const file=input.files[0];if(!file)return;const form=new FormData();form.append("page",PAGE);form.append("editKey",img.dataset.editKey);form.append("file",file);img.style.opacity=".45";const response=await fetch("/.netlify/functions/image",{method:"POST",headers:{"x-jsd-token":state.token},body:form});img.style.opacity="";if(!response.ok){alert("That image could not be uploaded.");return}img.src=(await response.json()).url};input.click()}
addEventListener("beforeunload",()=>{if(state.editing)navigator.sendBeacon("/.netlify/functions/lock",JSON.stringify({action:"release",sessionId:state.sessionId,token:state.token}))});
await loadOverrides().catch(()=>{});mountLink();
