{"id":10,"date":"2025-09-30T21:50:40","date_gmt":"2025-09-30T21:50:40","guid":{"rendered":"https:\/\/fuck-em.com\/?page_id=10"},"modified":"2026-10-01T12:56:42","modified_gmt":"2026-10-01T19:56:42","slug":"10-2","status":"publish","type":"page","link":"https:\/\/fuck-em.com\/?page_id=10","title":{"rendered":"Upload a flight"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"10\" class=\"elementor elementor-10\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-be37612 e-flex e-con-boxed e-con e-parent\" data-id=\"be37612\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-541618f elementor-widget elementor-widget-shortcode\" data-id=\"541618f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"shortcode.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-shortcode\">        <div id=\"kfs_edf7e5ec-9d9c-487d-b149-5f3af3b39734\" data-ajax=\"https:\/\/fuck-em.com\/wp-admin\/admin-ajax.php\" data-nonce=\"30481cc2c4\">\n            <style>\n.kfs-box{font:14px\/1.45 var(--kfs-font-body,system-ui),-apple-system,\"Segoe UI\",Roboto,sans-serif;color:var(--kfs-text,#E8E4DC);border:1px solid var(--kfs-border,#2C3542);border-radius:10px;background:var(--kfs-panel,#161B22);padding:16px 18px;max-width:900px;box-sizing:border-box}\n.kfs-box *{box-sizing:border-box}\n.kfs-box-head{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:baseline;gap:4px 16px;margin-bottom:12px}\n.kfs-box-title{font-weight:700;font-size:16px;word-break:break-all;font-family:var(--kfs-font-display,inherit)}\n.kfs-box-sub{color:var(--kfs-muted,#8E97A3)}\n.kfs-hero{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 22px;padding:12px 14px;background:var(--kfs-soft,#1D242E);border-radius:8px;margin-bottom:16px}\n.kfs-summary{margin:0 0 14px;padding:12px 14px;border-left:3px solid var(--kfs-accent,#F0752B);background:var(--kfs-soft,#1D242E);border-radius:0 8px 8px 0}\n.kfs-summary p{margin:0;font-size:15px;line-height:1.55;color:var(--kfs-text,#E8E4DC)}\n.kfs-summary-meta{margin-top:6px;font-size:11.5px;color:var(--kfs-muted,#8E97A3)}\n.kfs-summary-meta a{color:var(--kfs-accent-dark,#F59A5B)}\n.kfs-summary.busy{opacity:.6}\n.kfs-sum-notes{margin-top:8px}\n.kfs-sum-notes textarea{width:100%;box-sizing:border-box;font:inherit;font-size:13.5px;padding:8px 10px;border:1px solid var(--kfs-border,#2C3542);border-radius:6px;background:var(--kfs-panel,#161B22);color:var(--kfs-text,#E8E4DC);resize:vertical}\n.kfs-sum-notes div{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:6px}\n.kfs-sum-notes button{font:inherit;font-size:12.5px;padding:4px 10px;border:1px solid var(--kfs-accent,#F0752B);background:var(--kfs-accent,#F0752B);color:#fff;border-radius:6px;cursor:pointer}\n.kfs-sum-notes button.kfs-sum-notes-cancel{background:transparent;color:var(--kfs-muted,#8E97A3);border-color:var(--kfs-border,#2C3542)}\n.kfs-sum-notes small{color:var(--kfs-muted,#8E97A3);font-size:11.5px}\n.kfs-hero-num{font-size:34px;font-weight:700;letter-spacing:-.5px;line-height:1.1;font-family:var(--kfs-font-display,inherit)}\n.kfs-hero-num small{font-size:14px;font-weight:600;color:var(--kfs-muted,#8E97A3);margin-left:6px;letter-spacing:0}\n.kfs-hero-side{color:var(--kfs-text-2,#C5C9D1);line-height:1.5}\n.kfs-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:16px 26px}\n.kfs-box h4{margin:0 0 6px;font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.07em;color:var(--kfs-muted,#8E97A3)}\n.kfs-box dl{margin:0;display:grid;grid-template-columns:auto 1fr;gap:5px 14px}\n.kfs-box dt{color:var(--kfs-text-2,#C5C9D1);white-space:nowrap}\n.kfs-box dd{margin:0;font-weight:600;text-align:right;min-width:0;overflow-wrap:anywhere}\n.kfs-box dd small{font-weight:400;color:var(--kfs-muted,#8E97A3);margin-left:4px;white-space:nowrap}\n.kfs-box dd a{font-weight:600}\n.kfs-note{margin-top:12px;padding:8px 10px;border-left:3px solid var(--kfs-gold,#F2B134);background:var(--kfs-note-bg,#2A2312);color:var(--kfs-note-text,#F5D48A);font-size:13px;border-radius:0 6px 6px 0}\n.kfs-links{margin-top:14px;padding-top:10px;border-top:1px solid var(--kfs-border,#2C3542);display:flex;flex-wrap:wrap;gap:6px 16px}\n.kfs-links .kfs-del{color:var(--kfs-danger,#F87171);margin-left:auto}\n.kfs-meta{margin-top:8px;color:var(--kfs-muted,#8E97A3);font-size:12px}\n.kfs-map-wrap{margin-top:14px}\n.kfs-map{height:380px;border:1px solid var(--kfs-border,#2C3542);border-radius:8px;overflow:hidden;background:var(--kfs-soft,#1D242E)}\n.kfs-map-side{margin-top:10px;font-size:13px;color:var(--kfs-text-2,#C5C9D1)}\n.kfs-legend{display:flex;flex-wrap:wrap;gap:4px 14px;align-items:center;color:var(--kfs-text-2,#C5C9D1)}\n.kfs-legend .dot{display:inline-block;width:10px;height:10px;border-radius:50%;margin-right:5px;vertical-align:-1px}\n.kfs-legend .kfs-colorby{margin-left:auto;display:inline-flex;align-items:center;gap:6px}\n.kfs-legend .kfs-colorby select{font:inherit;font-size:12.5px;padding:2px 6px;border:1px solid var(--kfs-border,#2C3542);border-radius:6px;background:var(--kfs-soft,#1D242E);color:var(--kfs-text,#E8E4DC)}\n.kfs-legend .kfs-cl{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--kfs-muted,#8E97A3);flex-basis:100%}\n.kfs-cl-bar{display:inline-block;width:140px;height:10px;border-radius:5px;vertical-align:middle}\n.kfs-legend .dot.g{background:#10b981;border:2px solid #065f46}.kfs-legend .dot.o{background:#fb923c;border:2px solid #9a3412}.kfs-legend .dot.r{background:#ef4444;border:2px solid var(--kfs-danger,#F87171)}\n.kfs-adjust{margin-top:10px;padding:10px 12px;border:1px solid var(--kfs-border,#2C3542);border-radius:8px;background:var(--kfs-soft,#1D242E)}\n.kfs-adjust h4{margin:0 0 4px}\n.kfs-adjust p{margin:0 0 8px;color:var(--kfs-text-2,#C5C9D1)}\n.kfs-adjust label{display:inline-flex;align-items:center;gap:6px}\n.kfs-adjust input[type=number]{width:70px;padding:3px 6px;border:1px solid var(--kfs-border,#2C3542);border-radius:5px}\n.kfs-adjust input[type=text]{width:min(100%,320px);padding:3px 8px;border:1px solid var(--kfs-border,#2C3542);border-radius:5px}\n.kfs-share input.kfs-share-url{width:100%;margin-bottom:6px;font-size:12.5px;background:var(--kfs-panel,#161B22);color:var(--kfs-text,#E8E4DC)}\n.kfs-share button{margin-left:0;margin-right:6px}\n.kfs-fl-tools{font-size:12.5px;margin-top:6px}\n.kfs-fl-form label{display:block;margin:4px 0}\n.kfs-fl-form select,.kfs-fl-form input[type=number]{font:inherit;font-size:13px;padding:3px 6px;border:1px solid var(--kfs-border,#2C3542);border-radius:5px;background:var(--kfs-panel,#161B22);color:var(--kfs-text,#E8E4DC);margin-left:6px;max-width:100%}\n.kfs-fl-form input[type=number]{width:90px}\n.kfs-box .kfs-grid section.kfs-flightlog{grid-column:1 \/ -1}\n.kfs-flightlog dl{max-width:420px}\n.kfs-bl-row{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;align-items:center;margin:8px 0;padding-bottom:10px;border-bottom:1px solid var(--kfs-border,#2C3542)}\n.kfs-bl-row:last-of-type{border-bottom:0}\n.kfs-bl-row label{display:flex;flex-direction:column;gap:3px;font-size:11.5px;color:var(--kfs-muted,#8E97A3);min-width:0}\n.kfs-bl-row input{font:inherit;font-size:13px;padding:6px 8px;border:1px solid var(--kfs-border,#2C3542);border-radius:5px;background:var(--kfs-panel,#161B22);color:var(--kfs-text,#E8E4DC);min-width:0;width:100%;box-sizing:border-box}\n.kfs-bl-row .btns{grid-column:1 \/ -1;display:flex;gap:6px;flex-wrap:wrap}\n.kfs-bl-row button{margin:0}\n.kfs-bl-new{border-top:1px dashed var(--kfs-border,#2C3542);padding-top:10px;margin-top:4px}\n@media (max-width:420px){ .kfs-bl-row{grid-template-columns:minmax(0,1fr)} }\n.kfs-balloons{max-width:760px}\n.kfs-bl-intro{color:var(--kfs-text-2,#C5C9D1);font-size:13.5px;margin:0 0 12px}\n.kfs-bl-card{border:1px solid var(--kfs-border,#2C3542);border-radius:10px;padding:12px 14px;background:var(--kfs-soft,#1D242E);margin:12px 0}\n.kfs-bl-card.kfs-bl-row{border-bottom:1px solid var(--kfs-border,#2C3542)}\n.kfs-bl-card-head{grid-column:1 \/ -1;display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 10px}\n.kfs-bl-card-head span{color:var(--kfs-text-2,#C5C9D1)}\n.kfs-bl-card-head small{flex-basis:100%;color:var(--kfs-muted,#8E97A3)}\n.kfs-bl-card.kfs-bl-new{border-style:dashed;background:transparent}\n.kfs-adjust button.kfs-site-forget{background:var(--kfs-panel,#161B22);color:var(--kfs-danger,#F87171);border-color:var(--kfs-danger,#F87171)}\n.kfs-adjust button,.leaflet-popup-content button.kfs-pick{margin-left:6px;padding:4px 10px;border:1px solid var(--kfs-accent,#F0752B);background:var(--kfs-accent,#F0752B);color:#fff;border-radius:6px;cursor:pointer;font-size:12.5px}\n.kfs-adjust button.kfs-reset,.kfs-adjust button.kfs-clear-trim,.kfs-adjust button.kfs-alt-reset{background:var(--kfs-panel,#161B22);color:var(--kfs-danger,#F87171);border-color:var(--kfs-danger,#F87171)}\n.kfs-adjust + .kfs-adjust{margin-top:8px}\n.leaflet-popup-content button.kfs-trim-start,.leaflet-popup-content button.kfs-trim-end{margin:4px 0 0;display:block;background:var(--kfs-panel,#161B22);color:var(--kfs-accent-dark,#F59A5B)}\n.leaflet-popup-content button.kfs-pick{margin:6px 0 0;display:block}\n.leaflet-popup-content{font:13px\/1.4 system-ui,sans-serif}\n.kfs-map-error{color:var(--kfs-danger,#F87171)}.kfs-map-loading{color:var(--kfs-muted,#8E97A3)}\n.kfs-adjusted{color:var(--kfs-note-text,#F5D48A);font-weight:400;font-size:11px;margin-left:5px;padding:1px 5px;border:1px solid var(--kfs-gold,#F2B134);background:var(--kfs-note-bg,#2A2312);border-radius:4px;vertical-align:1px}\n<\/style><style>\n.kfs-photos{margin-top:14px;padding-top:10px;border-top:1px solid var(--kfs-border,#2C3542)}\n.kfs-photos h4{margin:0;display:inline;font-size:11.5px;font-weight:700;text-transform:uppercase;letter-spacing:.07em;color:var(--kfs-muted,#8E97A3)}\n.kfs-photos summary{cursor:pointer;list-style:none;display:flex;align-items:center;gap:8px;user-select:none}\n.kfs-photos summary::-webkit-details-marker{display:none}\n.kfs-photos summary::before{content:\"\u25b8\";font-size:12px;color:var(--kfs-muted,#8E97A3);transition:transform .15s}\n.kfs-photos details[open] summary::before{transform:rotate(90deg)}\n.kfs-photos details[open] summary{margin-bottom:8px}\n.kfs-photo-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px}\n.kfs-photo{position:relative;border-radius:8px;overflow:hidden;background:var(--kfs-soft,#1D242E);border:1px solid var(--kfs-border,#2C3542)}\n.kfs-photo a{display:block;aspect-ratio:1;background:var(--kfs-border,#2C3542)}\n.kfs-photo img{width:100%;height:100%;object-fit:cover;display:block}\n.kfs-photo .kfs-photo-pending{display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;height:100%;font-size:26px;color:var(--kfs-muted,#8E97A3)}\n.kfs-photo .kfs-photo-pending small{font-size:10.5px;margin-top:4px}\n.kfs-photo .cap{font-size:11.5px;color:var(--kfs-text-2,#C5C9D1);padding:4px 6px;display:flex;justify-content:space-between;gap:6px}\n.kfs-photo .cap b{font-weight:600;color:var(--kfs-text,#E8E4DC)}\n.kfs-photo .cap a.kfs-photo-time{color:inherit;text-decoration:underline dotted;text-underline-offset:2px}\n.kfs-photo .cap a.kfs-photo-time:hover{color:var(--kfs-accent,#F0752B)}\n.kfs-photo .cap input[type=datetime-local]{font:inherit;font-size:11px;padding:1px 3px;border:1px solid var(--kfs-border,#2C3542);border-radius:4px;background:var(--kfs-soft,#1D242E);color:var(--kfs-text,#E8E4DC);max-width:100%}\n.kfs-photo .cap.editing{display:block}\n.kfs-photo .cap button{font:inherit;font-size:11px;margin-top:3px;margin-right:4px;padding:1px 7px;border:1px solid var(--kfs-accent,#F0752B);background:var(--kfs-accent,#F0752B);color:#fff;border-radius:4px;cursor:pointer}\n.kfs-photo .cap button.kfs-time-cancel{background:transparent;color:var(--kfs-muted,#8E97A3);border-color:var(--kfs-border,#2C3542)}\n.kfs-photo .tools{display:flex;gap:4px;padding:0 6px 6px;font-size:11px}\n.kfs-photo .tools select{font-size:11px;max-width:100%}\n.kfs-photo .kfs-photo-del{color:var(--kfs-danger,#F87171);background:none;border:0;cursor:pointer;padding:0 4px}\n.kfs-photo-add{display:inline-block;margin-top:10px;padding:6px 12px;border:1px dashed var(--kfs-muted,#8E97A3);border-radius:8px;cursor:pointer;color:var(--kfs-text-2,#C5C9D1);background:var(--kfs-soft,#1D242E)}\n.kfs-photo-add:hover{border-color:var(--kfs-accent,#F0752B);color:var(--kfs-accent,#F0752B)}\n.kfs-photos small{display:block;margin-top:6px;color:var(--kfs-muted,#8E97A3);font-size:11.5px}\n.kfs-photos .busy{color:var(--kfs-muted,#8E97A3);font-style:italic;margin-top:6px}\n.leaflet-marker-icon.kfs-cam{background:#fff;border:2px solid #7A4E2D;border-radius:50%;width:26px!important;height:26px!important;line-height:22px;text-align:center;font-size:14px;box-shadow:0 1px 4px rgba(0,0,0,.4)}\n.leaflet-popup-content img.kfs-pop{max-width:220px;max-height:160px;display:block;border-radius:6px;margin-bottom:4px}\n<\/style><style>\n.kfs-gp{display:inline-flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:10px}\n.kfs-gp-btn{padding:6px 12px;border:1px solid var(--kfs-border,#2C3542);border-radius:8px;background:var(--kfs-panel,#161B22);cursor:pointer;color:var(--kfs-text-2,#C5C9D1);font:inherit}\n.kfs-gp-btn:hover{border-color:var(--kfs-accent,#F0752B);color:var(--kfs-accent,#F0752B)}\n.kfs-gp-btn[disabled]{opacity:.6;cursor:default}\n.kfs-gp-status{font-size:12.5px;color:var(--kfs-muted,#8E97A3)}\n<\/style>        <script>\n        (function(){\n            if(window.KFS_GP_INIT) return; window.KFS_GP_INIT=true;\n            const CID=\"616417276170-tvpd86150itorijdifdk7s08uk23772o.apps.googleusercontent.com\", SCOPE=\"https:\\\/\\\/www.googleapis.com\\\/auth\\\/photospicker.mediaitems.readonly\";\n            let gis=null;\n            function loadGIS(){ if(gis) return gis; gis=new Promise((res,rej)=>{ if(window.google&&google.accounts&&google.accounts.oauth2) return res(); const s=document.createElement('script'); s.src='https:\/\/accounts.google.com\/gsi\/client'; s.async=true; s.onload=()=>res(); s.onerror=()=>rej(new Error('Could not load Google sign-in.')); document.head.appendChild(s); }); return gis; }\n            function token(){ return new Promise((res,rej)=>{ const tc=google.accounts.oauth2.initTokenClient({client_id:CID,scope:SCOPE,callback:(r)=>{ if(r.error) rej(new Error(r.error_description||r.error)); else res(r.access_token); }, error_callback:(e)=>rej(new Error(e.message||e.type||'Google sign-in was cancelled.'))}); tc.requestAccessToken({prompt:''}); }); }\n            async function gapi(tok,path,opt){ opt=opt||{}; const r=await fetch('https:\/\/photospicker.googleapis.com\/v1\/'+path,Object.assign({},opt,{headers:Object.assign({Authorization:'Bearer '+tok,'Content-Type':'application\/json'},opt.headers||{})})); if(!r.ok){ let m=''; try{ m=(await r.json()).error.message; }catch(_){} throw new Error('Google Photos said: '+(m||('HTTP '+r.status))); } return r.status===204 ? null : r.json(); }\n            async function pick(status){\n                await loadGIS(); status('Signing in to Google\u2026'); const tok=await token();\n                status('Opening the Google Photos picker\u2026'); const ses=await gapi(tok,'sessions',{method:'POST',body:'{}'});\n                \/\/ The token round-trip usually outlives the click's user activation, so the tab may\n                \/\/ be blocked; the link is always offered so a second click of the user's own opens it.\n                const w=window.open(ses.pickerUri,'_blank'); const esc=s=>String(s).replace(\/[&<>\"']\/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]));\n                status((w?'Google Photos opened in a new tab (or ':'Pop-up blocked: ')+'<a href=\"'+esc(ses.pickerUri)+'\" target=\"_blank\" rel=\"noopener\">open the Google Photos picker<\/a>'+(w?')':'')+'. Choose photos there, press Done, and come back. Waiting\u2026', true);\n                const every=Math.max(3000, parseFloat(((ses.pollingConfig||{}).pollInterval||'5s'))*1000); let s=ses; const t0=Date.now();\n                while(!s.mediaItemsSet){ await new Promise(r=>setTimeout(r,every)); s=await gapi(tok,'sessions\/'+ses.id); if(Date.now()-t0>20*60*1000) throw new Error('Gave up waiting for the picker after 20 minutes.'); }\n                const items=[]; let page=null;\n                do{ const j=await gapi(tok,'mediaItems?sessionId='+encodeURIComponent(ses.id)+'&pageSize=100'+(page?'&pageToken='+encodeURIComponent(page):'')); (j.mediaItems||[]).forEach(i=>items.push(i)); page=j.nextPageToken||null; }while(page);\n                try{ await gapi(tok,'sessions\/'+ses.id,{method:'DELETE'}); }catch(_){}\n                return {tok,items};\n            }\n            async function download(tok,it){\n                if(it.type==='VIDEO' || !it.mediaFile || !it.mediaFile.baseUrl) return null;\n                \/\/ '=d' is the original bytes (keeps EXIF). Phones shoot HEIC, which the site cannot\n                \/\/ store, so anything that is not JPEG\/PNG\/WebP is fetched as Google's JPEG rendition\n                \/\/ instead (EXIF lost, but the creation time travels separately in taken_at).\n                const mf=it.mediaFile, mime=(mf.mimeType||'').toLowerCase(); let name=mf.filename||('google-photo-'+it.id.slice(0,12)+'.jpg');\n                const native=\/^image\\\/(jpeg|png|webp)$\/.test(mime) && \/\\.(jpe?g|png|webp)$\/i.test(name);\n                const r=await fetch(mf.baseUrl+(native?'=d':'=w4096-h4096'),{headers:{Authorization:'Bearer '+tok}}); if(!r.ok) throw new Error('Could not download '+name+' from Google ('+r.status+').');\n                const blob=await r.blob(); let type=native?mime:(blob.type||'image\/jpeg');\n                if(!native){ name=name.replace(\/\\.[^.]+$\/,'')+(type==='image\/png'?'.png':type==='image\/webp'?'.webp':'.jpg'); if(!\/^image\\\/(png|webp)$\/.test(type)) type='image\/jpeg'; }\n                return new File([blob],name,{type});\n            }\n            async function push(ajax,nonce,flightId,files,metas){ const fd=new FormData(); fd.append('action','kfs_photo_upload'); fd.append('nonce',nonce); if(flightId) fd.append('flight_id',flightId); files.forEach((f,i)=>{ fd.append('files[]',f,f.name); fd.append('taken_at[]',metas[i].taken||''); fd.append('google_id[]',metas[i].id||''); }); const r=await fetch(ajax,{method:'POST',body:fd,credentials:'same-origin'}); return r.json(); }\n            document.addEventListener('click', async e=>{\n                let b=e.target.closest('.kfs-gp-btn'); if(!b) return; e.preventDefault();\n                const ajax=b.dataset.ajax, nonce=b.dataset.nonce, flightId=b.dataset.flight&&b.dataset.flight!=='0'?b.dataset.flight:'';\n                let st=b.parentElement.querySelector('.kfs-gp-status'); const status=(t,html)=>{ if(!st) return; if(html) st.innerHTML=t; else st.textContent=t; };\n                b.disabled=true;\n                try{\n                    const {tok,items}=await pick(status); const photos=items.filter(i=>i.type!=='VIDEO'); if(!photos.length){ status('Nothing was picked.'); return; }\n                    let results=[];\n                    \/\/ batches of up to 4 files and ~56 MB: the server takes 64 MB per request\n                    const MAXB=56*1024*1024; let files=[],metas=[],size=0,copied=0;\n                    const flush=async()=>{ if(!files.length) return;\n                        const j=await push(ajax,nonce,flightId,files,metas); if(!j.success) throw new Error(j.data||'The site rejected the upload.');\n                        results=results.concat(j.data.items||[]);\n                        if(j.data.html && flightId){ const box=b.closest('.kfs-box'); if(box){ const t=document.createElement('div'); t.innerHTML=j.data.html; const nb=t.firstElementChild; box.replaceWith(nb); const d=nb.querySelector('.kfs-photos details'); if(d) d.open=true; document.querySelectorAll('.kfs-stat-link[data-kfs-id=\"'+nb.dataset.kfsId+'\"]').forEach(a=>a.setAttribute('data-kfs-html',j.data.html)); b=nb.querySelector('.kfs-gp-btn')||b; st=b.parentElement.querySelector('.kfs-gp-status'); b.disabled=true; } }\n                        files=[]; metas=[]; size=0; };\n                    for(const it of photos){\n                        status('Copying '+(copied+1)+' of '+photos.length+' from Google Photos\u2026');\n                        const f=await download(tok,it); copied++; if(!f) continue;\n                        if(files.length && (size+f.size>MAXB || files.length>=4)) await flush();\n                        files.push(f); metas.push({taken: it.createTime||((it.mediaFile||{}).mediaFileMetadata||{}).creationTime||'', id: it.id}); size+=f.size;\n                    }\n                    await flush();\n                    const matched=results.filter(r=>r.flight_id).length, dup=results.filter(r=>r.duplicate).length, failed=results.filter(r=>r.error&&!r.duplicate).length;\n                    const why=results.filter(r=>r.error&&!r.duplicate).map(r=>(r.name||'photo')+': '+r.error).join(' \u00b7 ');\n                    status('Done: '+(results.length-failed-dup)+' copied, '+matched+' matched to flights'+(dup?', '+dup+' already on the site':'')+(failed?', '+failed+' failed ('+why+')':'')+'.');\n                    if(!flightId) setTimeout(()=>location.reload(),1800);\n                }catch(err){ status(err.message||'Google Photos import failed.'); }\n                finally{ if(b) b.disabled=false; }\n            });\n        })();\n        <\/script>\n                <script>\n        (function(){\n            if(window.KFS_PHOTOS_INIT) return; window.KFS_PHOTOS_INIT=true;\n            \/\/ Files go up in batches that fit the server's per-request limit; a single oversize file\n            \/\/ travels alone and lets the server reject it with a clear message.\n            window.KFS_photoBatches=function(files){\n                const MAXB=56*1024*1024, MAXN=15; const out=[]; let cur=[],size=0;\n                for(const f of files){ if(cur.length && (size+f.size>MAXB || cur.length>=MAXN)){ out.push(cur); cur=[]; size=0; } cur.push(f); size+=f.size; }\n                if(cur.length) out.push(cur); return out;\n            };\n            const swap=(box,html)=>{ if(!html) return; const wasOpen=!!(box.querySelector('.kfs-photos details')||{}).open; const t=document.createElement('div'); t.innerHTML=html; const nb=t.firstElementChild; box.replaceWith(nb); const d=nb.querySelector('.kfs-photos details'); if(d && wasOpen) d.open=true; document.querySelectorAll('.kfs-stat-link[data-kfs-id=\"'+nb.dataset.kfsId+'\"]').forEach(a=>a.setAttribute('data-kfs-html',html)); };\n            async function post(box,fd){ fd.append('nonce',box.querySelector('.kfs-photos').dataset.kfsPhotosNonce);\n                let r; try{ r=await fetch(box.dataset.kfsAjax,{method:'POST',body:fd,credentials:'same-origin'}); }\n                catch(e){ await new Promise(x=>setTimeout(x,3000)); r=await fetch(box.dataset.kfsAjax,{method:'POST',body:fd,credentials:'same-origin'}); }   \/\/ one retry after a dropped connection return r.json(); }\n            document.addEventListener('change', async e=>{\n                const inp=e.target.closest('.kfs-photo-add input'); if(inp){ const box=inp.closest('.kfs-box'); if(!inp.files.length) return;\n                    const sec=box.querySelector('.kfs-photos'); const b=document.createElement('div'); b.className='busy'; sec.appendChild(b);\n                    const files=[...inp.files].slice(0,30), batches=window.KFS_photoBatches(files);\n                    let done=0, errs=[], html=null;\n                    try{\n                        for(const batch of batches){\n                            b.textContent='Uploading '+(done+batch.length)+' of '+files.length+' photo'+(files.length===1?'':'s')+'\u2026';\n                            const fd=new FormData(); fd.append('action','kfs_photo_upload'); fd.append('flight_id',inp.dataset.flight); if(box.dataset.kfsShare) fd.append('share',box.dataset.kfsShare); batch.forEach(f=>fd.append('files[]',f,f.name));\n                            const j=await post(box,fd); if(!j.success){ b.textContent=String(j.data||'Upload failed.'); return; }\n                            errs=errs.concat((j.data.items||[]).filter(i=>i.error)); html=j.data.html||html; done+=batch.length;\n                        }\n                        if(errs.length) alert(errs.map(i=>i.name+': '+i.error).join('\\n')); if(html) swap(box,html); else b.remove();\n                    }catch(err){ b.textContent='Upload failed: '+(err.message||'network'); } return; }\n                const mv=e.target.closest('.kfs-photo-move'); if(mv){ const box=mv.closest('.kfs-box'); const pid=mv.closest('.kfs-photo').dataset.photoId; if(mv.value==='') return;\n                    const fd=new FormData(); fd.append('action','kfs_photo_update'); fd.append('op','move'); fd.append('photo_id',pid); fd.append('flight_id',mv.value); fd.append('refresh_flight',box.dataset.kfsId);\n                    try{ const j=await post(box,fd); if(!j.success){ alert(j.data||'Could not move.'); return; } swap(box,j.data.html); }catch(err){ alert(err.message); } }\n            });\n            document.addEventListener('click', async e=>{\n                const tl=e.target.closest('.kfs-photo-time'); if(tl){ e.preventDefault(); const cap=tl.closest('.cap'); if(cap.querySelector('input')) return;\n                    cap.dataset.orig=cap.innerHTML; cap.classList.add('editing'); cap.innerHTML='';\n                    const inp=document.createElement('input'); inp.type='datetime-local'; inp.step='1'; inp.value=tl.dataset.iso||''; const sv=document.createElement('button'); sv.type='button'; sv.className='kfs-time-save'; sv.textContent='Save'; const cc=document.createElement('button'); cc.type='button'; cc.className='kfs-time-cancel'; cc.textContent='Cancel';\n                    cap.append(inp,document.createElement('br'),sv,cc); inp.focus(); return; }\n                const tc=e.target.closest('.kfs-time-cancel'); if(tc){ const cap=tc.closest('.cap'); cap.innerHTML=cap.dataset.orig||''; cap.classList.remove('editing'); return; }\n                const tsv=e.target.closest('.kfs-time-save'); if(tsv){ const box=tsv.closest('.kfs-box'), ph=tsv.closest('.kfs-photo'); const v=ph.querySelector('input[type=datetime-local]').value; tsv.disabled=true;\n                    const fd=new FormData(); fd.append('action','kfs_photo_update'); fd.append('op','time'); fd.append('photo_id',ph.dataset.photoId); fd.append('ts',v); fd.append('refresh_flight',box.dataset.kfsId);\n                    try{ const j=await post(box,fd); if(!j.success){ tsv.disabled=false; alert(j.data||'Could not save the time.'); return; } swap(box,j.data.html); }catch(err){ tsv.disabled=false; alert(err.message); } return; }\n                const del=e.target.closest('.kfs-photo-del'); if(!del) return; const box=del.closest('.kfs-box'); const pid=del.closest('.kfs-photo').dataset.photoId;\n                if(!confirm('Delete this photo from the site?')) return;\n                const fd=new FormData(); fd.append('action','kfs_photo_update'); fd.append('op','delete'); fd.append('photo_id',pid); fd.append('refresh_flight',box.dataset.kfsId);\n                try{ const j=await post(box,fd); if(!j.success){ alert(j.data||'Could not delete.'); return; } swap(box,j.data.html); }catch(err){ alert(err.message); }\n            });\n        })();\n        <\/script>\n        <script>\n(function(){\n  if(window.KFS_TrackColor) return;\n  const MODES=[\n    {id:'none',label:'Plain yellow'},\n    {id:'alt', label:'Altitude (ft MSL)',          unit:'ft',     get:p=>p[2]},\n    {id:'agl', label:'Height above launch (ft)',   unit:'ft',     get:(p,c)=>p[2]==null?null:p[2]-c.base},\n    {id:'spd', label:'Speed (mph)',                unit:'mph',    get:p=>p[4]==null?null:p[4]*1.15078},\n    {id:'roc', label:'Climb \/ descent (ft\/min)',   unit:'ft\/min', get:p=>p[5]==null?null:p[5], diverging:true},\n    {id:'topt',label:'Envelope temperature (\u00b0F)',  unit:'\u00b0F',     get:(p,c)=>p[6]==null?null:c.toF(p[6])},\n    {id:'ambt',label:'Ambient temperature (\u00b0F)',   unit:'\u00b0F',     get:(p,c)=>p[7]==null?null:c.toF(p[7])},\n  ];\n  function ctxFor(d,pts){\n    const toF=(d.temp_unit==='F')?v=>v:v=>v*1.8+32; let base=null;\n    if(d.launch&&d.launch.ts){ let b=null,bd=Infinity; for(const p of pts){ if(p[3]==null||p[2]==null) continue; const dd=Math.abs(p[3]-d.launch.ts); if(dd<bd){bd=dd;b=p;} } if(b) base=b[2]; }\n    if(base==null){ for(const p of pts){ if(p[2]!=null){ base=p[2]; break; } } }\n    return {toF, base: base||0};\n  }\n  const available=(d,pts)=>{ const c=ctxFor(d,pts); return MODES.filter(m=>m.id==='none'||pts.some(p=>m.get(p,c)!=null)); };\n  const ramp=t=>{ t=Math.max(0,Math.min(1,t)); return `hsl(${Math.round(240-240*t)},90%,50%)`; };                \/\/ blue \u2192 cyan \u2192 green \u2192 yellow \u2192 red\n  const divRamp=t=>{ t=Math.max(-1,Math.min(1,t)); const k=Math.abs(t); return t<0 ? `rgb(${Math.round(225-170*k)},${Math.round(230-110*k)},${Math.round(235+15*k)})` : `rgb(${Math.round(225+25*k)},${Math.round(230-180*k)},${Math.round(235-205*k)})`; }; \/\/ blue (descending) \u2192 pale \u2192 red (climbing)\n  const quant=(vals,q)=>{ const s=vals.slice().sort((a,b)=>a-b); return s[Math.min(s.length-1,Math.max(0,Math.floor(q*(s.length-1))))]; };\n  const fmt=v=>Math.abs(v)>=100?Math.round(v).toLocaleString():(Math.round(v*10)\/10).toLocaleString();\n  function apply(map,pts,modeId,d,state){\n    if(state.layer){ map.removeLayer(state.layer); state.layer=null; }\n    const m=MODES.find(x=>x.id===modeId)||MODES[0];\n    if(state.baseLine) state.baseLine.setStyle({opacity: m.id==='none'?1:0});\n    if(m.id==='none'){ if(state.legendEl) state.legendEl.innerHTML=''; return; }\n    const c=ctxFor(d,pts); const vals=[]; for(const p of pts){ const v=m.get(p,c); if(v!=null&&isFinite(v)) vals.push(v); }\n    if(vals.length<2){ if(state.legendEl) state.legendEl.textContent='No data in this log for that channel.'; return; }\n    let lo=quant(vals,.02), hi=quant(vals,.98), color;                       \/\/ clip the extremes so one spike does not flatten the scale\n    if(m.diverging){ const a=Math.max(Math.abs(lo),Math.abs(hi))||1; lo=-a; hi=a; color=v=>divRamp(v\/a); }\n    else { if(hi-lo<1e-9) hi=lo+1; color=v=>ramp((v-lo)\/(hi-lo)); }\n    if(!map.getPane('kfsColor')){ map.createPane('kfsColor'); map.getPane('kfsColor').style.zIndex=395; }   \/\/ under the markers (400), over the tiles\n    const g=L.layerGroup(); let last=null,lastV=null;\n    for(const p of pts){ if(p[0]==null) continue; const v=m.get(p,c);\n      if(last){ const vv=(v!=null?v:lastV); if(vv!=null) L.polyline([[last[0],last[1]],[p[0],p[1]]],{color:color(vv),weight:4,opacity:1,interactive:false,pane:'kfsColor'}).addTo(g); }\n      last=p; if(v!=null) lastV=v; }\n    g.addTo(map); state.layer=g;\n    if(state.legendEl){ const stops=[]; for(let i=0;i<=10;i++){ const t=i\/10; stops.push(m.diverging?divRamp(t*2-1):ramp(t)); }\n      state.legendEl.innerHTML=`<span>${fmt(lo)}<\/span><span class=\"kfs-cl-bar\" style=\"background:linear-gradient(90deg,${stops.join(',')})\"><\/span><span>${fmt(hi)} ${m.unit}<\/span>`; }\n  }\n  function control(d,pts,map,state,selectEl,legendEl){\n    const av=available(d,pts); selectEl.innerHTML=av.map(m=>`<option value=\"${m.id}\">${m.label}<\/option>`).join('');\n    let pref='none'; try{ pref=localStorage.getItem('kfs_track_color')||'none'; }catch(_){}\n    if(!av.some(m=>m.id===pref)) pref='none'; selectEl.value=pref; state.legendEl=legendEl;\n    apply(map,pts,pref,d,state);\n    selectEl.addEventListener('change',()=>{ try{ localStorage.setItem('kfs_track_color',selectEl.value); }catch(_){} apply(map,pts,selectEl.value,d,state); });\n  }\n  window.KFS_TrackColor={control,apply,available,MODES};\n})();\n<\/script>        <script>\n        (function(){\n            if(window.KFS_MAP_INIT) return; window.KFS_MAP_INIT=true;\n            const LCSS='https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/leaflet\/1.9.4\/leaflet.min.css';\n            const LJS ='https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/leaflet\/1.9.4\/leaflet.min.js';\n            let leaflet=null;\n            function loadLeaflet(){\n                if(leaflet) return leaflet;\n                leaflet=new Promise((res,rej)=>{\n                    if(window.L){ res(window.L); return; }\n                    const l=document.createElement('link'); l.rel='stylesheet'; l.href=LCSS;\n                    const s=document.createElement('script'); s.src=LJS;\n                    let n=0; const ok=()=>{ if(++n===2) res(window.L); };\n                    l.onload=ok; l.onerror=ok; s.onload=ok; s.onerror=()=>rej(new Error('Could not load the map library.'));\n                    document.head.appendChild(l); document.head.appendChild(s);\n                });\n                return leaflet;\n            }\n            const fmtT =(ts)=> ts ? new Date(Number(ts)*1000).toLocaleTimeString([], {hour:'numeric',minute:'2-digit',second:'2-digit'}) : '\u2014';\n            const fmtKt=(k)=> (k==null) ? '\u2014' : (Number(k).toFixed(1)+' kt \u00b7 '+(Number(k)*1.15078).toFixed(1)+' mph');\n            async function post(box,action,fields){\n                const fd=new FormData(); fd.append('action',action); fd.append('nonce',box.dataset.kfsNonce); fd.append('id',box.dataset.kfsId);\n                for(const k in fields) fd.append(k,fields[k]);\n                const r=await fetch(box.dataset.kfsAjax,{method:'POST',body:fd,credentials:'same-origin'});\n                return r.json();\n            }\n            function replaceBox(box,html){\n                const tmp=document.createElement('div'); tmp.innerHTML=html; const nb=tmp.firstElementChild;\n                box.replaceWith(nb);\n                \/\/ keep the listing's stored copy current, so reopening the row shows the adjusted box\n                document.querySelectorAll('.kfs-stat-link[data-kfs-id=\"'+nb.dataset.kfsId+'\"]').forEach(a=>a.setAttribute('data-kfs-html',html));\n                return nb;\n            }\n            const esc=(t)=>String(t).replace(\/[&<>\"]\/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c]));\n            function nearest(pts,ts){ let b=null,bd=Infinity; for(const p of pts){ if(p[3]==null) continue; const d=Math.abs(p[3]-ts); if(d<bd){bd=d;b=p;} } return b; }\n            async function openMap(box){\n                const wrap=box.querySelector('.kfs-map-wrap'), btn=box.querySelector('.kfs-map-btn');\n                if(!wrap) return;\n                if(!wrap.hidden && box._kfsMap){ wrap.hidden=true; btn.textContent='Show map'; return; }\n                wrap.hidden=false; btn.textContent='Hide map';\n                if(box._kfsMap){ setTimeout(()=>box._kfsMap.invalidateSize(),30); return; }\n                const side=wrap.querySelector('.kfs-map-side'); side.innerHTML='<div class=\"kfs-map-loading\">Loading map\u2026<\/div>';\n                try{\n                    const L=await loadLeaflet();\n                    const r=await post(box,'kfs_track',{}); if(!r.success) throw new Error(r.data||'Could not load the track.');\n                    const d=r.data, pts=d.points||[]; if(!pts.length) throw new Error('This log has no timed track to draw.');\n                    const el=wrap.querySelector('.kfs-map'); const map=L.map(el,{scrollWheelZoom:false}); map.createPane('kfsTrack'); map.getPane('kfsTrack').style.zIndex=390; box._kfsMap=map;\n                    const layers={\n                        'Satellite': L.tileLayer('https:\/\/server.arcgisonline.com\/ArcGIS\/rest\/services\/World_Imagery\/MapServer\/tile\/{z}\/{y}\/{x}',{maxZoom:19,attribution:'Imagery &copy; Esri, Maxar, Earthstar Geographics'}),\n                        'Terrain':   L.tileLayer('https:\/\/{s}.tile.opentopomap.org\/{z}\/{x}\/{y}.png',{maxZoom:17,attribution:'&copy; OpenStreetMap contributors, SRTM | &copy; <a href=\"https:\/\/opentopomap.org\">OpenTopoMap<\/a> (CC-BY-SA)'}),\n                        'Streets':   L.tileLayer('https:\/\/tile.openstreetmap.org\/{z}\/{x}\/{y}.png',{maxZoom:19,attribution:'&copy; <a href=\"https:\/\/www.openstreetmap.org\/copyright\">OpenStreetMap<\/a>'})\n                    };\n                    let pref='Satellite'; try{ pref=localStorage.getItem('kfs_map_layer')||pref; }catch(_){}\n                    (layers[pref]||layers['Satellite']).addTo(map);\n                    L.control.layers(layers,null,{position:'topright'}).addTo(map);\n                    map.on('baselayerchange',ev=>{ try{ localStorage.setItem('kfs_map_layer',ev.name); }catch(_){} });\n                    const tr=d.trim||{}; const ts0=tr.start_ts?Number(tr.start_ts):null, ts1=tr.end_ts?Number(tr.end_ts):null;\n                    const inWin=p=> p[3]==null || ((ts0==null||p[3]>=ts0) && (ts1==null||p[3]<=ts1));\n                    \/\/ (named preSeg\/postSeg: a local called `post` would shadow the AJAX helper above)\n                    const kept=pts.filter(inWin), preSeg=pts.filter(p=>ts0!=null&&p[3]!=null&&p[3]<ts0), postSeg=pts.filter(p=>ts1!=null&&p[3]!=null&&p[3]>ts1);\n                    \/\/ trimmed-away portions stay visible, in grey, so the pilot can see what was cut\n                    [preSeg,postSeg].forEach(seg=>{ if(seg.length>1) L.polyline(seg.map(p=>[p[0],p[1]]),{color:'#9ca3af',weight:3,opacity:.8,dashArray:'6 6'}).addTo(map); });\n                    const ll=(kept.length>1?kept:pts).map(p=>[p[0],p[1]]);\n                    L.polyline(ll,{color:'#111827',weight:6,opacity:.55,pane:'kfsTrack'}).addTo(map);              \/\/ dark halo so the track reads on imagery\n                    const line=L.polyline(ll,{color:'#facc15',weight:3,opacity:1,pane:'kfsTrack'}).addTo(map);\n                    \/\/ clicks on the grey parts should work too\n                    const allLine=L.polyline(pts.map(p=>[p[0],p[1]]),{color:'#000',weight:12,opacity:0}).addTo(map);\n                    map.fitBounds(line.getBounds(),{padding:[24,24]});\n                    if(d.launch && d.launch.lat!=null) L.circleMarker([d.launch.lat,d.launch.lon],{radius:7,color:'#065f46',fillColor:'#10b981',fillOpacity:1}).addTo(map).bindPopup('<b>Launch<\/b><br>'+fmtT(d.launch.ts));\n                    const ov=d.override||{}; const finalTs=ov.landing_ts?Number(ov.landing_ts):(d.final_ts?Number(d.final_ts):null);\n                    let finalMarked=false;\n                    (d.landings||[]).forEach((l,i)=>{\n                        const isFinal=finalTs!=null && Math.abs(Number(l.ts)-finalTs)<=2; if(isFinal) finalMarked=true;\n                        L.circleMarker([l.lat,l.lon],{radius:isFinal?9:7,color:isFinal?'#7f1d1d':'#9a3412',fillColor:isFinal?'#ef4444':'#fb923c',fillOpacity:1}).addTo(map)\n                         .bindPopup(`<b>Landing ${i+1}${isFinal?' \u2014 final':''}<\/b><br>${fmtT(l.ts)}<br>${fmtKt(l.kt)}`+(d.can_edit&&!isFinal?`<button type=\"button\" class=\"kfs-pick\" data-ts=\"${l.ts}\">Use as final landing<\/button>`:''));\n                    });\n                    if(finalTs!=null && !finalMarked){ const p=nearest(pts,finalTs); if(p) L.circleMarker([p[0],p[1]],{radius:9,color:'#7f1d1d',fillColor:'#ef4444',fillOpacity:1}).addTo(map).bindPopup('<b>Final landing (adjusted)<\/b><br>'+fmtT(p[3])); }\n                    (d.photos||[]).forEach(ph=>{ if(ph.lat==null) return;\n                        L.marker([ph.lat,ph.lon],{icon:L.divIcon({className:'kfs-cam',html:'\ud83d\udcf7',iconSize:[26,26],iconAnchor:[13,13]}),title:ph.time||''}).addTo(map)\n                         .bindPopup(`<a href=\"${esc(ph.url)}\" target=\"_blank\" rel=\"noopener\"><img decoding=\"async\" class=\"kfs-pop\" src=\"${esc(ph.thumb||ph.url)}\" alt=\"\"><\/a><b>${ph.rel?'T'+esc(ph.rel):''}<\/b> ${esc(ph.time||'')}${ph.via==='gps'?' \u00b7 camera GPS':' \u00b7 on the track'}`); });\n                    const onTrack=e=>{\n                        let best=null,bd=Infinity; const cl=Math.cos(e.latlng.lat*Math.PI\/180);\n                        for(const p of pts){ const dx=p[0]-e.latlng.lat, dy=(p[1]-e.latlng.lng)*cl; const dd=dx*dx+dy*dy; if(dd<bd){bd=dd;best=p;} }\n                        if(!best) return;\n                        const edit=d.can_edit?`<button type=\"button\" class=\"kfs-pick\" data-ts=\"${best[3]}\">Set final landing here<\/button><button type=\"button\" class=\"kfs-trim-start\" data-ts=\"${best[3]}\">Trim start here<\/button><button type=\"button\" class=\"kfs-trim-end\" data-ts=\"${best[3]}\">Trim end here<\/button>`:'';\n                        const tF=v=>((d.temp_unit==='F')?Number(v):Number(v)*1.8+32).toFixed(1)+' \u00b0F';\n                        L.popup().setLatLng([best[0],best[1]]).setContent(`<b>${fmtT(best[3])}<\/b><br>${best[2]!=null?Number(best[2]).toLocaleString()+' ft MSL':''}<br>${fmtKt(best[4])}${best[5]!=null?'<br>'+(best[5]>0?'+':'')+Math.round(best[5]).toLocaleString()+' ft\/min':''}${best[6]!=null?'<br>envelope '+tF(best[6]):''}${best[7]!=null?'<br>ambient '+tF(best[7]):''}`+edit).openOn(map);\n                    };\n                    line.on('click',onTrack); allLine.on('click',onTrack);\n                    let html='<div class=\"kfs-legend\"><span><span class=\"dot g\"><\/span>Launch<\/span><span><span class=\"dot o\"><\/span>Detected landing<\/span><span><span class=\"dot r\"><\/span>Final landing<\/span>'\n                        +'<label class=\"kfs-colorby\">Colour track by <select class=\"kfs-colorby-sel\"><\/select><\/label><span class=\"kfs-cl\"><\/span><\/div>';\n                    if(d.can_edit){\n                        const hasOv = !!(ov.landing_ts || (ov.landings!=null && ov.landings!==''));\n                        html+='<div class=\"kfs-adjust\"><h4>Adjust landing<\/h4><p>Click a landing marker, or anywhere on the track, and choose <em>Set final landing<\/em>. Landing speed, time and place are recomputed from that point.<\/p>'\n                            +`<label>Number of landings <input type=\"number\" min=\"0\" step=\"1\" class=\"kfs-cnt\" value=\"${(ov.landings!=null&&ov.landings!=='')?ov.landings:(d.landing_count??'')}\"><\/label><button type=\"button\" class=\"kfs-save-cnt\">Save count<\/button>`\n                            +(hasOv?'<button type=\"button\" class=\"kfs-reset\">Reset to detected<\/button>':'')+'<\/div>';\n                        \/\/ trim\n                        const trimTxt = (ts0||ts1) ? `Using ${ts0?fmtT(ts0):'the start'} to ${ts1?fmtT(ts1):'the end'} of the log; the grey parts are ignored.` : 'Using the whole log.';\n                        html+='<div class=\"kfs-adjust\"><h4>Trim<\/h4><p>'+trimTxt+' Click the track and choose <em>Trim start here<\/em> or <em>Trim end here<\/em> \u2014 for logs that began on the drive out or kept running on the way home. Every statistic is recomputed for the window.<\/p>'\n                            +((ts0||ts1)?'<button type=\"button\" class=\"kfs-clear-trim\">Clear trim<\/button>':'')+'<\/div>';\n                        \/\/ altitude\n                        const A=d.alt||{}; const fix=A.fix||null;\n                        let altState = fix ? (fix.mode==='terrain'?'Forced to terrain elevation at launch.':fix.mode==='offset'?`Manual correction of ${fix.ft>0?'+':''}${fix.ft} ft.`:'Terrain correction switched off.')\n                                         : (A.calibrated ? `Calibrated to terrain at launch (${A.offset_m>0?'\u2212':'+'}${Math.abs(Math.round(A.offset_m||0))} m).` : (A.offset_m!=null ? `Not calibrated: the log starts ${Math.round(Math.abs(A.offset_m))} m ${A.offset_m>0?'above':'below'} the ground at its first point, which is past the automatic limit.` : 'No terrain elevation available.'));\n                        html+='<div class=\"kfs-adjust\"><h4>Altitude<\/h4><p>'+altState+` Max ${A.max_alt_ft!=null?Math.round(A.max_alt_ft).toLocaleString():'\u2014'} ft MSL \/ ${A.max_agl_ft!=null?Math.round(A.max_agl_ft).toLocaleString():'\u2014'} ft above ground.<\/p>`\n                            +(A.ground_elev_m!=null?'<button type=\"button\" class=\"kfs-alt-terrain\" title=\"Treat the first point of the log as being on the ground and correct every altitude to the terrain elevation there, even past the automatic limit\">Calibrate to terrain at launch<\/button>':'')\n                            +'<label>Manual correction <input type=\"number\" step=\"1\" class=\"kfs-alt-ft\" value=\"'+(fix&&fix.mode==='offset'?fix.ft:'')+'\" placeholder=\"\u00b1ft\"><\/label><button type=\"button\" class=\"kfs-alt-apply\">Apply<\/button>'\n                            +(fix?'<button type=\"button\" class=\"kfs-alt-reset\">Reset altitude<\/button>':'')+'<\/div>';\n                        \/\/ sites\n                        const Pl=d.places||{};\n                        const siteRow=(kind,label,cur,site)=>`<p><b>${label}:<\/b> ${cur?esc(cur):'\u2014'}${site?' <span class=\"kfs-adjusted\">remembered site<\/span>':''}<\/p><label>Name this ${kind} site <input type=\"text\" class=\"kfs-site-name\" data-kind=\"${kind}\" maxlength=\"120\" placeholder=\"e.g. 7987 Missimer Road, or Missimer Field\"><\/label><button type=\"button\" class=\"kfs-site-save\" data-kind=\"${kind}\">Save<\/button>${site?`<button type=\"button\" class=\"kfs-site-forget\" data-kind=\"${kind}\">Forget<\/button>`:''}`;\n                        html+='<div class=\"kfs-adjust\"><h4>Sites<\/h4><p>A named site applies to every flight that launches or lands within 150 m of it, on this and future uploads. Addresses the map finds on its own are remembered the same way.<\/p>'+siteRow('launch','Launch',Pl.launch,Pl.launch_site)+siteRow('landing','Landing',Pl.landing,Pl.landing_site)+'<\/div>';\n                    } else if(d.unit==='unknown'){ html+='<p>Speeds are unavailable for this log.<\/p>'; }\n                    side.innerHTML=html;\n                    { const sel=side.querySelector('.kfs-colorby-sel'); if(sel && window.KFS_TrackColor) KFS_TrackColor.control(d,(kept.length>1?kept:pts),map,{baseLine:line},sel,side.querySelector('.kfs-cl')); }\n                    setTimeout(()=>map.invalidateSize(),60);\n                }catch(err){ side.innerHTML='<div class=\"kfs-map-error\">'+(err.message||'The map could not be shown.')+'<\/div>'; }\n            }\n            async function applyOverride(box,fields){\n                box.querySelectorAll('button').forEach(b=>b.disabled=true);\n                try{ const r=await post(box,'kfs_set_override',fields); if(!r.success) throw new Error(r.data||'Could not save.'); const nb=replaceBox(box,r.data.html); openMap(nb); }\n                catch(err){ alert(err.message||'Could not save.'); box.querySelectorAll('button').forEach(b=>b.disabled=false); }\n            }\n            document.addEventListener('click', async (e)=>{\n                const mb=e.target.closest('.kfs-map-btn'); if(mb){ e.preventDefault(); openMap(mb.closest('.kfs-box')); return; }\n                const fle=e.target.closest('.kfs-fl-edit'); if(fle){ e.preventDefault(); const s=fle.closest('.kfs-flightlog'); s.querySelector('.kfs-fl-form').hidden=false; s.querySelector('.kfs-bl-panel').hidden=true; return; }\n                const flc=e.target.closest('.kfs-fl-cancel'); if(flc){ flc.closest('.kfs-fl-form').hidden=true; return; }\n                const blm=e.target.closest('.kfs-bl-manage'); if(blm){ e.preventDefault(); const s=blm.closest('.kfs-flightlog'); const p=s.querySelector('.kfs-bl-panel'); p.hidden=!p.hidden; s.querySelector('.kfs-fl-form').hidden=true; return; }\n                const fls=e.target.closest('.kfs-fl-save'); if(fls){ const box=fls.closest('.kfs-box'), f=fls.closest('.kfs-fl-form'); fls.disabled=true;\n                    try{ const j=await post(box,'kfs_flightlog',{balloon_id:f.querySelector('.kfs-fl-balloon').value, passengers:f.querySelector('.kfs-fl-pax').value, fuel_used_gal:f.querySelector('.kfs-fl-fuel').value}); if(j.success) replaceBox(box,j.data.html); else { fls.disabled=false; alert(j.data||'Could not save.'); } }catch(err){ fls.disabled=false; alert('Could not reach the site.'); } return; }\n                const bls=e.target.closest('.kfs-bl-save'); if(bls){ const box=bls.closest('.kfs-box'), r=bls.closest('.kfs-bl-row'); bls.disabled=true; if(box.classList.contains('kfs-balloons')){\n                        const fields={op:'save', balloon_id:r.dataset.id||'', name:r.querySelector('.kfs-bl-name').value, model:r.querySelector('.kfs-bl-model').value, n_number:r.querySelector('.kfs-bl-n').value, size:r.querySelector('.kfs-bl-size').value, fuel_gal:r.querySelector('.kfs-bl-fuel').value};\n                        try{ const j=await post(box,'kfs_balloons',fields); if(j.success){ const t=document.createElement('div'); t.innerHTML=j.data.html; box.replaceWith(t.firstElementChild); } else { bls.disabled=false; alert(j.data||'Could not save the balloon.'); } }catch(err){ bls.disabled=false; alert('Could not reach the site.'); } return; }\n                    const fields={op:'save', balloon_id:r.dataset.id||'', name:r.querySelector('.kfs-bl-name').value, model:r.querySelector('.kfs-bl-model').value, n_number:r.querySelector('.kfs-bl-n').value, size:r.querySelector('.kfs-bl-size').value, fuel_gal:r.querySelector('.kfs-bl-fuel').value}; if(r.classList.contains('kfs-bl-new')) fields.use_for_flight='1';\n                    try{ const j=await post(box,'kfs_balloons',fields); if(j.success){ const nb=replaceBox(box,j.data.html); const p=nb.querySelector('.kfs-bl-panel'); if(p && !r.classList.contains('kfs-bl-new')) p.hidden=false; } else { bls.disabled=false; alert(j.data||'Could not save the balloon.'); } }catch(err){ bls.disabled=false; alert('Could not reach the site.'); } return; }\n                const bld=e.target.closest('.kfs-bl-del'); if(bld){ const box=bld.closest('.kfs-box'), r=bld.closest('.kfs-bl-row'); if(!confirm('Remove this balloon from your list? Flights that used it keep their record.')) return; if(box.classList.contains('kfs-balloons')){\n                        try{ const j=await post(box,'kfs_balloons',{op:'delete', balloon_id:r.dataset.id}); if(j.success){ const t=document.createElement('div'); t.innerHTML=j.data.html; box.replaceWith(t.firstElementChild); } else alert(j.data||'Could not delete.'); }catch(err){ alert('Could not reach the site.'); } return; }\n                    try{ const j=await post(box,'kfs_balloons',{op:'delete', balloon_id:r.dataset.id}); if(j.success){ const nb=replaceBox(box,j.data.html); const p=nb.querySelector('.kfs-bl-panel'); if(p) p.hidden=false; } else alert(j.data||'Could not delete.'); }catch(err){ alert('Could not reach the site.'); } return; }                const shb=e.target.closest('.kfs-share-btn'); if(shb){ e.preventDefault(); const box=shb.closest('.kfs-box'), pan=box.querySelector('.kfs-share'); if(!pan) return;\n                    if(!pan.hidden){ pan.hidden=true; return; } pan.hidden=false; const inp=pan.querySelector('.kfs-share-url'); inp.value='\u2026';\n                    try{ const j=await post(box,'kfs_share',{op:'get'}); inp.value=j.success?(j.data.url||''):''; if(!j.success) alert(j.data||'Could not create the link.'); }catch(err){ inp.value=''; alert('Could not reach the site.'); } return; }\n                const shc=e.target.closest('.kfs-share-copy'); if(shc){ e.preventDefault(); const inp=shc.closest('.kfs-share').querySelector('.kfs-share-url'); inp.select();\n                    try{ await navigator.clipboard.writeText(inp.value); shc.textContent='Copied'; setTimeout(()=>{ shc.textContent='Copy'; },1500); }catch(_){ document.execCommand && document.execCommand('copy'); } return; }\n                const shr=e.target.closest('.kfs-share-rotate, .kfs-share-off'); if(shr){ e.preventDefault(); const box=shr.closest('.kfs-box'), off=shr.classList.contains('kfs-share-off');\n                    if(!confirm(off?'Turn the share link off? Anyone holding it loses access.':'Make a new link? The old one stops working.')) return;\n                    try{ const j=await post(box,'kfs_share',{op:off?'off':'rotate'}); const inp=box.querySelector('.kfs-share-url'); if(j.success) inp.value=j.data.url||'(sharing is off)'; else alert(j.data||'Failed.'); }catch(err){ alert('Could not reach the site.'); } return; }\n                const nb=e.target.closest('.kfs-sum-notes-btn'); if(nb){ e.preventDefault(); const pan=nb.closest('.kfs-summary').querySelector('.kfs-sum-notes'); if(pan){ pan.hidden=!pan.hidden; if(!pan.hidden) pan.querySelector('textarea').focus(); } return; }\n                const nc=e.target.closest('.kfs-sum-notes-cancel'); if(nc){ nc.closest('.kfs-sum-notes').hidden=true; return; }\n                const rg=e.target.closest('.kfs-sum-regen, .kfs-sum-save'); if(rg){ e.preventDefault(); const box=rg.closest('.kfs-box'), sum=rg.closest('.kfs-summary'); if(!box||sum.classList.contains('busy')) return;\n                    const fields={}; if(rg.classList.contains('kfs-sum-save')){ const ta=sum.querySelector('.kfs-sum-notes textarea'); fields.notes=ta?ta.value:''; }\n                    sum.classList.add('busy'); const label=rg.textContent; rg.textContent='Writing\u2026';\n                    try{ const j=await post(box,'kfs_summary',fields); if(j.success){ const t=document.createElement('div'); t.innerHTML=j.data.html; sum.replaceWith(t.firstElementChild); } else { rg.textContent=label; sum.classList.remove('busy'); alert(j.data||'Could not write the description.'); } }\n                    catch(err){ rg.textContent=label; sum.classList.remove('busy'); alert('Could not reach the site.'); }\n                    return; }\n                const pick=e.target.closest('.kfs-pick'); if(pick){ const box=pick.closest('.kfs-box'); if(box) applyOverride(box,{landing_ts:pick.dataset.ts}); return; }\n                const sc=e.target.closest('.kfs-save-cnt'); if(sc){ const box=sc.closest('.kfs-box'); applyOverride(box,{landings:box.querySelector('.kfs-cnt').value}); return; }\n                const tsb=e.target.closest('.kfs-trim-start'); if(tsb){ const box=tsb.closest('.kfs-box'); if(box) applyOverride(box,{trim_start_ts:tsb.dataset.ts}); return; }\n                const teb=e.target.closest('.kfs-trim-end');   if(teb){ const box=teb.closest('.kfs-box'); if(box) applyOverride(box,{trim_end_ts:teb.dataset.ts}); return; }\n                const ct=e.target.closest('.kfs-clear-trim');  if(ct){ const box=ct.closest('.kfs-box'); applyOverride(box,{clear_trim:1}); return; }\n                const at=e.target.closest('.kfs-alt-terrain'); if(at){ const box=at.closest('.kfs-box'); applyOverride(box,{alt_fix_mode:'terrain'}); return; }\n                const aa=e.target.closest('.kfs-alt-apply');   if(aa){ const box=aa.closest('.kfs-box'); const v=box.querySelector('.kfs-alt-ft').value.trim(); if(v==='' || isNaN(Number(v))){ alert('Enter a correction in feet, e.g. -240 or 150.'); return; } applyOverride(box,{alt_fix_mode:'offset',alt_fix_ft:v}); return; }\n                const ar=e.target.closest('.kfs-alt-reset');   if(ar){ const box=ar.closest('.kfs-box'); applyOverride(box,{clear_alt_fix:1}); return; }\n                const ss=e.target.closest('.kfs-site-save');   if(ss){ const box=ss.closest('.kfs-box'); const inp=box.querySelector('.kfs-site-name[data-kind=\"'+ss.dataset.kind+'\"]'); const v=inp.value.trim(); if(!v){ alert('Enter a name for the site.'); return; } applyOverride(box,{site_kind:ss.dataset.kind,site_name:v}); return; }\n                const sf=e.target.closest('.kfs-site-forget'); if(sf){ const box=sf.closest('.kfs-box'); if(!confirm('Forget the remembered name for this site? Flights near it go back to what the map finds.')) return; applyOverride(box,{site_kind:sf.dataset.kind,forget_site:1}); return; }\n                const rs=e.target.closest('.kfs-reset'); if(rs){ const box=rs.closest('.kfs-box'); if(!confirm('Reset landing details to the detected values?')) return;\n                    try{ const r=await post(box,'kfs_clear_override',{}); if(!r.success) throw new Error(r.data||'Could not reset.'); openMap(replaceBox(box,r.data.html)); }catch(err){ alert(err.message); } return; }\n            });\n        })();\n        <\/script>\n                    <style>\n                #kfs_edf7e5ec-9d9c-487d-b149-5f3af3b39734 .drop{border:2px dashed var(--kfs-border,#2C3542);border-radius:10px;padding:28px 24px;text-align:center;cursor:pointer;background:var(--kfs-soft,#1D242E);transition:all .15s;font-size:15px}\n                #kfs_edf7e5ec-9d9c-487d-b149-5f3af3b39734 .drop.is-over{border-color:var(--kfs-accent,#F0752B);background:var(--kfs-soft,#1D242E)}\n                #kfs_edf7e5ec-9d9c-487d-b149-5f3af3b39734 .drop small{display:block;margin-top:4px;color:var(--kfs-muted,#8E97A3)}\n                #kfs_edf7e5ec-9d9c-487d-b149-5f3af3b39734 details{margin-top:10px;color:var(--kfs-text-2,#C5C9D1);font-size:13px}\n                #kfs_edf7e5ec-9d9c-487d-b149-5f3af3b39734 summary{cursor:pointer}\n                #kfs_edf7e5ec-9d9c-487d-b149-5f3af3b39734 .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:10px;margin-top:8px}\n                #kfs_edf7e5ec-9d9c-487d-b149-5f3af3b39734 .grid label{display:flex;flex-direction:column;gap:3px}\n                #kfs_edf7e5ec-9d9c-487d-b149-5f3af3b39734 .hint{margin:6px 0 0;color:var(--kfs-muted,#8E97A3)}\n                #kfs_edf7e5ec-9d9c-487d-b149-5f3af3b39734 .queue{list-style:none;padding:0;margin:14px 0 0}\n                #kfs_edf7e5ec-9d9c-487d-b149-5f3af3b39734 .item{border:1px solid var(--kfs-border,#2C3542);border-radius:10px;padding:12px;margin:10px 0;background:var(--kfs-panel,#161B22)}\n                #kfs_edf7e5ec-9d9c-487d-b149-5f3af3b39734 .name{font-weight:600}\n                #kfs_edf7e5ec-9d9c-487d-b149-5f3af3b39734 .status{opacity:.85;margin-top:2px}\n                #kfs_edf7e5ec-9d9c-487d-b149-5f3af3b39734 .bad{color:var(--kfs-danger,#F87171)} #kfs_edf7e5ec-9d9c-487d-b149-5f3af3b39734 .good{color:var(--kfs-good,#4ADE80)}\n                #kfs_edf7e5ec-9d9c-487d-b149-5f3af3b39734 .results{margin-top:10px}\n                #kfs_edf7e5ec-9d9c-487d-b149-5f3af3b39734 .results .kfs-box{max-width:none}\n            <\/style>\n            <div class=\"drop\" tabindex=\"0\"><strong>Drop .kml files here<\/strong>, or click to choose<small>Up to 20 MB each. Stats appear below as each file is processed.<\/small><\/div>\n            <input type=\"file\" class=\"inp\" accept=\".kml\" multiple hidden>\n            <details><summary>Advanced options<\/summary>\n                <p class=\"hint\">The defaults suit these loggers. Change them only if launch or ground detection looks wrong.<\/p>\n                <div class=\"grid\">\n                    <label>Temperature unit recorded by the logger\n                        <select class=\"opt-temp\">\n                            <option value=\"C\"  selected='selected'>Celsius<\/option>\n                            <option value=\"F\" >Fahrenheit<\/option>\n                        <\/select>\n                    <\/label>\n                    <label>Launch: counts as moving above (m\/s)<input type=\"number\" step=\"0.1\" class=\"opt-move\" value=\"0.5\"><\/label>\n                    <label>Ground: counts as stopped below (m\/s)<input type=\"number\" step=\"0.1\" class=\"opt-landspd\" value=\"1.5\"><\/label>\n                    <label>Ground: within this height of lowest point (m)<input type=\"number\" step=\"0.1\" class=\"opt-ground\" value=\"10\"><\/label>\n                    <label>Distance decimals<input type=\"number\" min=\"0\" step=\"1\" class=\"opt-prec\" value=\"1\"><\/label>\n                    <label>Speed decimals<input type=\"number\" min=\"0\" step=\"1\" class=\"opt-sd\" value=\"1\"><\/label>\n                <\/div>\n            <\/details>\n            <ul class=\"queue\"><\/ul>\n        <\/div>\n        <script>\n        (function(){\n            const root=document.getElementById('kfs_edf7e5ec-9d9c-487d-b149-5f3af3b39734');\n            const drop=root.querySelector('.drop'), inp=root.querySelector('.inp'), list=root.querySelector('.queue');\n            const ajax=root.dataset.ajax, maxMB=20;\n            const $=(s)=>root.querySelector(s);\n            const human=(b)=>{const u=['B','KB','MB','GB'];let i=0,n=b;while(n>=1024&&i<u.length-1){n\/=1024;i++;}return n.toFixed(n>=10?0:1)+' '+u[i];};\n            function add(file){const li=document.createElement('li'); li.className='item'; li.innerHTML='<div class=\"name\"><\/div><div class=\"status\">Queued\u2026<\/div><div class=\"results\"><\/div>'; li.querySelector('.name').textContent=file.name+' ('+human(file.size)+')'; list.appendChild(li); return li;}\n            function ok(li,html){const el=li.querySelector('.status'); el.innerHTML=html; el.className='status good';}\n            function bad(li,html){const el=li.querySelector('.status'); el.innerHTML=html; el.className='status bad';}\n            \/\/ The server renders the same detail box the listings use, so the two never drift.\n            function render(li,item){\n                if(!item) return;\n                if(item.html){ li.querySelector('.results').innerHTML=item.html; return; }\n                const s=item.stats||{};\n                li.querySelector('.results').textContent=`Saved as attachment #${item.id}. Max altitude ${s.max_alt_ft==null?'\u2014':Math.round(s.max_alt_ft).toLocaleString()} ft, ${s.landing_count??'\u2014'} landing(s).`;\n            }\n            async function uploadOne(file){\n                const li=add(file), ext=file.name.split('.').pop().toLowerCase();\n                if(ext!=='kml'){bad(li,'Only .kml files are accepted.'); return;}\n                if(file.size>maxMB*1024*1024){bad(li,'File is larger than '+maxMB+' MB.'); return;}\n                const opts={ temp_unit:$('.opt-temp')?.value||'C', move_thresh:$('.opt-move')?.value||'0.5', landing_speed_thresh:$('.opt-landspd')?.value||'1.5', landing_alt_window:$('.opt-ground')?.value||'10', precision:$('.opt-prec')?.value||'1', speed_decimals:$('.opt-sd')?.value||'1' };\n                const fd=new FormData(); fd.append('action','kfs_multi_upload'); fd.append('nonce','30481cc2c4');\n                fd.append('temp_unit',opts.temp_unit); fd.append('move_thresh',opts.move_thresh); fd.append('landing_speed_thresh',opts.landing_speed_thresh); fd.append('landing_alt_window',opts.landing_alt_window);\n                fd.append('precision',opts.precision); fd.append('speed_decimals',opts.speed_decimals); fd.append('files[]',file,file.name);\n                li.querySelector('.status').textContent='Uploading & analysing\u2026';\n                try{\n                    const xhr=new XMLHttpRequest(); const done=new Promise((res,rej)=>{xhr.onreadystatechange=function(){if(xhr.readyState===4){if(xhr.status>=200&&xhr.status<300)res(xhr.responseText);else rej(new Error('HTTP '+xhr.status));}}});\n                    xhr.open('POST',ajax,true); xhr.send(fd); const text=await done; const data=JSON.parse(text);\n                    if(!data.success){bad(li,data.data?String(data.data):'Upload failed.');return;}\n                    const item=(data.data.items&&data.data.items[0])?data.data.items[0]:null;\n                    if(!item){bad(li,'No result returned.');return;} if(item.error){bad(li,item.error);return;}\n                    ok(li,'Saved.'); render(li,item);\n                }catch(err){bad(li,'Error: '+(err?.message||'Unknown'));}\n            }\n            function handle(files){[...files].reduce((p,f)=>p.then(()=>uploadOne(f)), Promise.resolve());}\n            ['dragenter','dragover'].forEach(ev=>drop.addEventListener(ev,e=>{e.preventDefault();e.stopPropagation();drop.classList.add('is-over');}));\n            ['dragleave','drop'].forEach(ev=>drop.addEventListener(ev,e=>{e.preventDefault();e.stopPropagation();drop.classList.remove('is-over');}));\n            drop.addEventListener('drop',e=>{const dt=e.dataTransfer; if(dt?.files?.length) handle(dt.files);});\n            drop.addEventListener('click',()=>inp.click()); inp.addEventListener('change',()=>{if(inp.files?.length) handle(inp.files); inp.value='';});\n            drop.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();inp.click();}});\n        })();\n        <\/script>\n        <\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-10","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/fuck-em.com\/index.php?rest_route=\/wp\/v2\/pages\/10","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fuck-em.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/fuck-em.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/fuck-em.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fuck-em.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=10"}],"version-history":[{"count":14,"href":"https:\/\/fuck-em.com\/index.php?rest_route=\/wp\/v2\/pages\/10\/revisions"}],"predecessor-version":[{"id":589,"href":"https:\/\/fuck-em.com\/index.php?rest_route=\/wp\/v2\/pages\/10\/revisions\/589"}],"wp:attachment":[{"href":"https:\/\/fuck-em.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=10"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}