{"schemaVersion":1,"releaseId":"main_e7f83696-8ecb-438d-a8e5-0f94cc30a1c6_release","files":{"page.html":"<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">{{meta}}</head>\n<body>{{preview}}{{appearance}}\n<header><a href=\"/\">Tragik Minded</a><nav>{{navigation}}</nav></header>\n<main><section class=\"cms-page-heading\"><h1>{{title}}</h1>{{summary}}</section>{{cover}}{{content}}</main>\n<footer>{{footer}}</footer></body></html>\n","runtime/comic-font.mjs":"// Self-hosted Bangers (SIL Open Font License, public/static/fonts/Bangers-OFL.txt).\nlet loading;\nexport function loadComicFont(){return loading??=Promise.resolve().then(async()=>{if(typeof FontFace==='undefined')return;const face=new FontFace('Tragik Bangers',\"url('/media/static/fonts/tragik-bangers-latin.woff2')\");await face.load();document.fonts.add(face);});}\n","runtime/comic-layout.mjs":"// Authored artwork coordinates; CMS media may replace each panel independently.\nexport const COMIC_ATLAS_ID = 'f47bd0638dfd9a3131fb29ccfa2a71a5e2cb22bbe321f0dc763dc66e14ab275c';\nexport const isComicAtlas = source => String(source ?? '').includes(COMIC_ATLAS_ID);\nexport const COMIC_ART = Object.freeze({\n blog: [20,53,422,113,1004,87,1004,324,20,377],\n play: [20,392,520,363,445,694,20,672],\n artifact: [538,362,1004,335,1004,727,462,696],\n shop: [20,687,1004,740,1004,899,20,920],\n 'tool-bench': [20,937,1004,916,1004,1080,20,1122],\n studio: [20,1136,1004,1090,1004,1267,20,1306],\n contact: [20,1322,1004,1277,1004,1496,20,1496],\n});\nexport function comicArtFrame(id) {\n const points=COMIC_ART[id],xs=points.filter((_,i)=>i%2===0),ys=points.filter((_,i)=>i%2===1);\n const x=Math.min(...xs),y=Math.min(...ys);\n return {x,y,width:Math.max(...xs)-x,height:Math.max(...ys)-y};\n}\nexport function illustratedComicLayout(width) {\n const sx=width/1024,sy=sx*(width<620?1.2:1),panels={};\n for(const [id,points]of Object.entries(COMIC_ART)){\n  const f=comicArtFrame(id);panels[id]={x:f.x*sx,y:f.y*sy,width:f.width*sx,height:f.height*sy,points:points.map((v,i)=>(v-(i%2?f.y:f.x))*(i%2?sy:sx))};\n }\n const footerTop=1502*sy,footerHeight=Math.max(32,34*sy);\n return {illustrated:true,width,height:footerTop+footerHeight,logo:{x:20*sx,y:0,width:984*sx,height:114*sy},footer:{x:20*sx,y:footerTop+8,width:984*sx,height:footerHeight-8},panels};\n}\nexport const COMIC_LETTERING = Object.freeze({\n blog:{x:.055,y:.15,width:.48,height:.74,size:130,arrow:[.48,.82,.16],title:'READ\\nTHE BLOG'},\n play:{x:.12,y:.19,width:.49,height:.67,size:110,arrow:[.35,.86,.12],title:'ENTER\\nPLAY'},\n artifact:{x:.13,y:.19,width:.51,height:.77,size:110,title:'ACTIVATE\\n3D'},\n shop:{x:.055,y:.24,width:.61,height:.72,size:121,arrow:[.70,.74,.15]},\n 'tool-bench':{x:.05,y:.25,width:.69,height:.72,size:118},\n studio:{x:.05,y:.32,width:.60,height:.66,size:112},\n contact:{x:.05,y:.30,width:.41,height:.68,size:120,arrow:[.48,.72,.16]},\n});\n","runtime/consent.mjs":"const STORAGE_KEY='tragik.media-consent.v1';\nconst CONTROLLER=Symbol.for('tragik.media-consent.controller.v1');\nconst MAX_AGE=180*24*60*60*1000;\n\nexport function normalizeConsentPolicy(value){\n  if(!value||value.schemaVersion!==1||!Number.isSafeInteger(value.version)||value.version<0||!['ask','disabled'].includes(value.youtube)||typeof value.heading!=='string'||!value.heading.trim()||value.heading.length>80||typeof value.notice!=='string'||!value.notice.trim()||value.notice.length>600)return null;\n  let policyUrl='';\n  if(typeof value.policyUrl==='string'&&value.policyUrl.length<=1000){try{if(value.policyUrl.startsWith('/')&&!value.policyUrl.startsWith('//')&&!value.policyUrl.includes('\\\\'))policyUrl=value.policyUrl;else{const url=new URL(value.policyUrl);if(url.protocol==='https:'&&!url.username&&!url.password)policyUrl=url.href;}}catch{}}\n  return {schemaVersion:1,version:value.version,youtube:value.youtube,heading:value.heading.trim(),notice:value.notice.trim(),policyUrl};\n}\nexport function consentPreference(raw,policy,now=Date.now()){\n  if(!policy||policy.youtube!=='ask')return null;\n  try{const value=typeof raw==='string'?JSON.parse(raw):raw;if(value?.schemaVersion!==1||value.version!==policy.version||typeof value.youtube!=='boolean'||!Number.isSafeInteger(value.at)||value.at>now+300000||now-value.at>MAX_AGE)return null;return {schemaVersion:1,version:value.version,at:value.at,youtube:value.youtube};}catch{return null;}\n}\nexport function privacyFromDocument(document=globalThis.document){\n  try{return JSON.parse(document?.getElementById('cms-privacy-policy')?.textContent??'null');}catch{return null;}\n}\nexport function getConsentController(){return globalThis[CONTROLLER]??null;}\nexport function installConsent(value=privacyFromDocument()){\n  if(typeof document==='undefined'||!document.body)return null;\n  const previous=getConsentController();if(previous){previous.updatePolicy(value);return previous;}\n  let policy=normalizeConsentPolicy(value),memory=null,lastFocus=null,observerQueued=false,destroyed=false;\n  const listeners=new Set(),fallbacks=new Map();\n  const stored=()=>{try{return localStorage.getItem(STORAGE_KEY);}catch{return null;}};\n  memory=consentPreference(stored(),policy);\n  const status=()=>({available:!!policy,version:policy?.version??null,youtube:policy?.youtube==='ask'&&consentPreference(memory,policy)?.youtube===true,disabled:policy?.youtube==='disabled',choice:consentPreference(memory,policy)?.youtube??null});\n  const button=document.createElement('button');button.type='button';button.className='tragik-consent-entry';button.textContent='Privacy preferences';button.dataset.consentUi='true';\n  const dialog=document.createElement('dialog');dialog.className='tragik-consent-dialog';dialog.dataset.consentUi='true';dialog.setAttribute('aria-labelledby','tragik-consent-title');\n  const title=document.createElement('h2');title.id='tragik-consent-title';\n  const notice=document.createElement('p'),stateText=document.createElement('p');stateText.className='tragik-consent-state';stateText.setAttribute('role','status');\n  const explanation=document.createElement('p');explanation.className='tragik-consent-essential';explanation.textContent='This choice controls embedded YouTube players. Essential sign-in and hosting services continue to operate. Your choice is remembered on this website for up to 180 days.';\n  const policyLink=document.createElement('a');policyLink.textContent='Read the privacy policy';policyLink.target='_blank';policyLink.rel='noopener noreferrer';\n  const actions=document.createElement('div');actions.className='tragik-consent-actions';\n  const allow=document.createElement('button');allow.type='button';allow.textContent='Allow YouTube';\n  const reject=document.createElement('button');reject.type='button';reject.textContent='Keep YouTube blocked';\n  const close=document.createElement('button');close.type='button';close.textContent='Close';close.className='tragik-consent-close';\n  actions.append(reject,allow);dialog.append(title,notice,stateText,explanation,policyLink,actions,close);document.body.append(button,dialog);\n  function redraw(){const current=status();title.textContent=policy?.heading??'Privacy preferences';notice.textContent=policy?.notice??'Optional media is blocked because the website privacy settings are unavailable.';allow.disabled=!current.available||current.disabled;stateText.textContent=current.disabled?'YouTube embeds are disabled by this website.':!current.available?'YouTube is blocked.':current.youtube?'YouTube is allowed. You can withdraw permission here.':'YouTube is blocked until you allow it.';reject.textContent=current.youtube?'Withdraw YouTube permission':'Keep YouTube blocked';policyLink.hidden=!policy?.policyUrl;if(policy?.policyUrl)policyLink.href=policy.policyUrl;}\n  function closeDialog(){dialog.close();const target=lastFocus?.isConnected?lastFocus:button;target.focus({preventScroll:true});}\n  function openPreferences(){redraw();lastFocus=document.activeElement;if(!dialog.open)dialog.showModal();reject.focus();}\n  function notify(){redraw();for(const listener of listeners)listener(status());syncFallbacks();window.dispatchEvent(new CustomEvent('tragik:consent-change',{detail:status()}));}\n  function choose(youtube){\n    if(youtube&&(!policy||policy.youtube!=='ask'))return;\n    memory=policy?{schemaVersion:1,version:policy.version,at:Date.now(),youtube}:null;\n    try{if(memory)localStorage.setItem(STORAGE_KEY,JSON.stringify(memory));else localStorage.removeItem(STORAGE_KEY);}catch{}\n    notify();closeDialog();\n  }\n  allow.addEventListener('click',()=>choose(true));reject.addEventListener('click',()=>choose(false));button.addEventListener('click',openPreferences);close.addEventListener('click',closeDialog);dialog.addEventListener('cancel',event=>{event.preventDefault();closeDialog();});\n  function syncFallbacks(){\n    if(destroyed)return;\n    const allowed=status().youtube;\n    for(const [host,entry]of fallbacks){\n      if(!host.isConnected){entry.frame?.remove();host.removeEventListener('toggle',entry.toggle);fallbacks.delete(host);continue;}\n      const world=host.closest('[data-world-engine]'),semanticVisible=!world||['failed','stopped'].includes(world.dataset.worldState);\n      const shouldLoad=allowed&&semanticVisible&&host.open===true;\n      if(shouldLoad&&!entry.frame){entry.frame=entry.template.cloneNode(false);entry.frame.src=entry.src;host.appendChild(entry.frame);}\n      else if(!shouldLoad&&entry.frame){entry.frame.remove();entry.frame=null;}\n      const message=status().disabled?'YouTube embeds are disabled by this website.':!status().available?'YouTube is blocked while privacy settings are unavailable.':allowed?'YouTube is allowed for this website.':'YouTube is blocked until you allow it.';\n      if(entry.message.textContent!==message)entry.message.textContent=message;\n      entry.button.hidden=allowed||status().disabled;entry.button.disabled=!status().available;\n    }\n  }\n  function scanFallbacks(){\n    observerQueued=false;if(destroyed)return;\n    for(const host of document.querySelectorAll('details.cms-youtube-player')){\n      if(fallbacks.has(host))continue;\n      const template=host.querySelector('iframe[data-cms-provider=\"youtube\"][data-src]');if(!template)continue;\n      let src;try{const url=new URL(template.dataset.src);if(url.protocol!=='https:'||url.hostname!=='www.youtube-nocookie.com'||!/^\\/embed\\/[a-zA-Z0-9_-]{11}$/.test(url.pathname))continue;src=url.href;}catch{continue;}\n      template.removeAttribute('src');template.remove();\n      const message=document.createElement('p');message.className='tragik-consent-placeholder';\n      const request=document.createElement('button');request.type='button';request.className='tragik-consent-request';request.textContent='Review YouTube preferences';request.addEventListener('click',openPreferences);\n      const toggle=()=>syncFallbacks();host.addEventListener('toggle',toggle);host.append(message,request);fallbacks.set(host,{template,src,frame:null,message,button:request,toggle});\n    }\n    syncFallbacks();\n  }\n  const observer=new MutationObserver(()=>{if(!observerQueued){observerQueued=true;queueMicrotask(scanFallbacks);}});observer.observe(document.body,{childList:true,subtree:true});\n  const storageChanged=event=>{if(event.key===STORAGE_KEY||event.key===null){memory=consentPreference(stored(),policy);notify();}};\n  const visible=()=>{if(!document.hidden){memory=consentPreference(stored(),policy)??consentPreference(memory,policy);notify();}};\n  window.addEventListener('storage',storageChanged);document.addEventListener('visibilitychange',visible);window.addEventListener('tragik:scene-status',syncFallbacks);\n  const controller={status,openPreferences,subscribe(listener){listeners.add(listener);listener(status());return()=>listeners.delete(listener);},updatePolicy(next){policy=normalizeConsentPolicy(next);memory=consentPreference(stored(),policy)??consentPreference(memory,policy);notify();},destroy(){destroyed=true;observer.disconnect();window.removeEventListener('storage',storageChanged);document.removeEventListener('visibilitychange',visible);window.removeEventListener('tragik:scene-status',syncFallbacks);for(const [host,entry]of fallbacks){entry.frame?.remove();host.removeEventListener('toggle',entry.toggle);}listeners.clear();button.remove();dialog.remove();delete globalThis[CONTROLLER];}};\n  globalThis[CONTROLLER]=controller;redraw();scanFallbacks();return controller;\n}\n","runtime/engine.mjs":"import {createPageTransition} from './page-transition.mjs';\nimport {createSkyRenderer} from './sky-renderer.mjs';\nimport {loadComicFont} from './comic-font.mjs';\nimport * as PIXI from \"/cms-runtime/vendor-v1/pixi.js\";\nimport * as THREE from \"/cms-runtime/vendor-v1/three.js\";\nimport {createSceneDocumentReader} from \"./scene-document.mjs\";\nimport {createNativeScene} from \"./native-scene.mjs\";\nimport {createNativeMedia} from \"./native-media.mjs\";\nimport {createNativeInput,writeSemanticField} from \"./native-input.mjs\";\nimport {logicalViewport,sceneAnchorOffset} from \"./scene-contract.mjs\";\nimport {runtimeSettings} from \"./runtime-settings.mjs\";\n\n/** Native scene layout and hit testing; semantic markup is content only. */\nexport async function createWorldEngine({root,source,canvas,settings={}}) {\n  await loadComicFont().catch(()=>{});\n  const configuration=runtimeSettings(settings);\n  const measureViewport=()=>{const value=logicalViewport(window.innerWidth,window.innerHeight,window.devicePixelRatio);value.resolution=Math.min(value.resolution,configuration.resolutionLimit);return value;};\n  let disposed=false,contextLost=false,frame=0,needsLayout=true,needsRender=true,documentData,layout,lastDraw=0;\n  let viewport=measureViewport(),pendingAnchor=window.location.hash;\n  let scrollY=window.scrollY,focused=null,active=false,mediaAnimation=false,sceneAnimation=false;\n  const cleanup=[];\n  const reducedMotion=window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n  const overlay=document.createElement(\"div\");\n  overlay.className=\"world-overlays\"; root.appendChild(overlay);\n  let three;\n  try {three=new THREE.WebGLRenderer({canvas,antialias:true,alpha:false,stencil:true});}\n  catch(error){overlay.remove();throw error;}\n  three.autoClear=false;three.setClearColor(0x080a0d,1);\n  const pixi=new PIXI.WebGLRenderer();\n  try { await pixi.init({canvas,context:three.getContext(),width:viewport.width,height:viewport.height,resolution:viewport.resolution,autoDensity:true,clearBeforeRender:false,preference:\"webgl\"}); }\n  catch(error){three.dispose();overlay.remove();throw error;}\n  if(pixi.events?.features)pixi.events.features.globalMove=true;\n  canvas.style.touchAction=\"pan-y pinch-zoom\";\n  const stage=new PIXI.Container();stage.eventMode=\"static\";\n  const reader=createSceneDocumentReader(source);\n  const scene=new THREE.Scene(),camera=new THREE.PerspectiveCamera(45,1,.1,1000);\n\n  function status(isActive,state=isActive?\"active\":\"failed\") {\n    active=isActive;root.dataset.worldState=state;\n    overlay.style.display=isActive?\"block\":\"none\";\n    root.toggleAttribute(\"data-world-ready\",isActive);\n    root.dataset.worldRenderer=isActive?\"native-pixi\":\"fallback\";\n    if(isActive)document.documentElement.dataset.sceneNative=\"true\";\n    else delete document.documentElement.dataset.sceneNative;\n    window.dispatchEvent(new CustomEvent(\"tragik:scene-status\",{detail:{active:isActive,state}}));\n  }\n  function invalidate(options={}) {\n    if(disposed)return;\n    if(options.layout)needsLayout=true;\n    needsRender=true;\n    if(!frame&&!contextLost&&document.visibilityState!==\"hidden\")frame=requestAnimationFrame(draw);\n  }\n  function projectRect(rect) {\n    return {x:rect.x*viewport.scale,y:rect.y*viewport.scale-scrollY,width:rect.width*viewport.scale,height:rect.height*viewport.scale,scale:viewport.scale};\n  }\n  function sourceFor(id){return documentData?.actions.get(id)??documentData?.sources.get(id);}\n  function visible(rect){\n    if(!rect)return;\n    const top=rect.y*viewport.scale,bottom=(rect.y+rect.height)*viewport.scale;\n    if(top<window.scrollY+18)window.scrollTo({top:Math.max(0,top-18),behavior:\"instant\"});\n    else if(bottom>window.scrollY+viewport.height-18)window.scrollTo({top:bottom-viewport.height+18,behavior:\"instant\"});\n  }\n  function focus(id,{edit=false}={}) {\n    focused=id;\n    const player=layout?.media.find(item=>item.node.id===id&&[\"audio\",\"video\",\"youtube\"].includes(item.node.mediaType));\n    if(player){visible(player.rect);invalidate();requestAnimationFrame(()=>{if(!disposed&&focused===id)media.focus(id);});return;}\n    const rect=native.focus(id);visible(rect);\n    const element=sourceFor(id);\n    if(element&&document.activeElement!==element)element.focus?.({preventScroll:true});\n    if(edit){const field=layout?.fields.find(item=>(item.node.actionId??item.id)===id);if(field)input.open(field.node,field.rect);}\n    invalidate();\n  }\n  function focusNext(id,direction=1){\n    const controls=(layout?.controls??[]).filter(item=>!item.node.disabled&&item.actionId);\n    const players=(layout?.media??[]).filter(item=>[\"audio\",\"video\",\"youtube\"].includes(item.node.mediaType));\n    const ids=[...new Set([...controls,...players].sort((a,b)=>a.rect.y-b.rect.y||a.rect.x-b.rect.x).map(item=>item.actionId??item.node.id))];\n    if(!ids.length)return;\n    const index=ids.indexOf(id),next=ids[index<0?(direction>0?0:ids.length-1):(index+direction+ids.length)%ids.length];\n    focus(next,{edit:true});\n  }\n  function action(id,event){\n    const element=sourceFor(id);if(!element||element.disabled||transition.active)return;\n    input.close();focus(id);\n    if(element.tagName===\"A\"){\n      const destination=new URL(element.getAttribute(\"href\")||\"#\",window.location.href);\n      const original=event?.nativeEvent??event?.originalEvent;\n      if(original?.metaKey||original?.ctrlKey||original?.shiftKey){window.open(destination.href,\"_blank\",\"noopener,noreferrer\");return;}\n    }\n    element.click();invalidate({layout:true});\n  }\n  function scrollAnchor(hash, behavior=\"instant\") {\n    const y=sceneAnchorOffset(hash,layout?.anchors);\n    if(!Number.isFinite(y))return false;\n    window.scrollTo({top:y*viewport.scale,behavior});return true;\n  }\n  function field(node,detail,rect){\n    if(detail?.kind===\"focus\"){focus(node.actionId??node.id);native.setScrollY(window.scrollY);}\n    else if(detail?.kind===\"select\"||detail?.kind===\"change\"){\n      focus(node.actionId??node.id);\n      writeSemanticField(sourceFor(node.actionId??node.id),detail.value);invalidate({layout:true});\n    }else{focus(node.actionId??node.id);visible(rect);input.open(node,rect);}\n  }\n  let transition;\n  const sky=createSkyRenderer({THREE,renderer:three,invalidate,active:()=>transition?.showSky??documentData?.kind===\"home\"});\n  void sky.load();\n  const native=createNativeScene({PIXI,stage,invalidate,onAction:action,onField:field,onIntent:href=>window.dispatchEvent(new CustomEvent('tragik:navigation-intent',{detail:{href}}))});\n  const input=createNativeInput({overlay,resolve:sourceFor,projectRect,invalidate,onTab:focusNext});\n  const media=createNativeMedia({THREE,scene,camera,renderer:three,overlay,invalidate,projectRect,onTab:focusNext});\n  transition=createPageTransition({PIXI,native,stage,root,invalidate});\n  function paintScene(now){\n    three.resetState();three.setViewport(0,0,viewport.width,viewport.height);three.setScissorTest(false);three.clear(true,true,true);\n    if(transition.showSky)sky.render(now,viewport.width,viewport.height,viewport.resolution);\n    pixi.resetState();pixi.render({container:stage,clear:false});\n    three.resetState();mediaAnimation=media.render(now);\n  }\n  function listen(target,type,listener,options){target.addEventListener(type,listener,options);cleanup.push(()=>target.removeEventListener(type,listener,options));}\n  function resize(){\n    if(transition?.active)transition.cancel();\n    viewport=measureViewport();\n    three.setPixelRatio(viewport.resolution);three.setSize(viewport.width,viewport.height,false);\n    pixi.resize(viewport.width,viewport.height,viewport.resolution);\n    stage.scale.set(viewport.scale);stage.position.set(0,-window.scrollY);\n    invalidate({layout:true});\n  }\n  function publishDiagnostics(){\n    root.dataset.worldSettings=JSON.stringify(configuration);\n    root.dataset.worldPainted=String(layout?.paintedElements??0);\n    root.dataset.worldNativeCount=\"0\";root.dataset.worldLayout=\"pixi\";\n    root.dataset.worldControls=JSON.stringify((layout?.controls??[]).map(item=>({id:item.id,actionId:item.actionId,kind:item.kind,rect:projectRect(item.rect),interactive:item.view?.eventMode===\"static\"})));\n    root.dataset.worldMedia=JSON.stringify(media.snapshot());\n    root.dataset.worldFallbacks=\"[]\";\n    root.dataset.worldSky=JSON.stringify(sky.diagnostics);\n    if(document.getElementById('tragik-studio-preview')){\n      root.dataset.worldStudioRegions=JSON.stringify((layout?.studioRegions??[]).map(item=>({field:item.field,rect:projectRect(item.rect)})));\n      root.dataset.worldStudioImages=JSON.stringify(native.textureState);\n    }\n  }\n  function draw(now){\n    frame=0;if(disposed||contextLost||document.visibilityState===\"hidden\")return;\n    if(!needsRender&&!mediaAnimation&&!sceneAnimation&&!transition.active)return;\n    if(!needsLayout&&!needsRender&&!transition.moving&&now-lastDraw<1000/configuration.animationFps){frame=requestAnimationFrame(draw);return;}\n    try{\n      if(needsLayout){\n        documentData=reader.read();documentData.sky=sky.settings;\n        if(sky.settings.preview&&documentData.home)documentData.home.notices.push({text:sky.settings.unavailable?\"Sky preview expired or unavailable · open a fresh preview from the CMS\":\"Private sky preview · settings are not published\"});\n        layout=transition.build(documentData,viewport,{scrollY});\n\n        if(!transition.active)native.focus(focused);needsLayout=false;\n        if(!transition.active&&pendingAnchor&&scrollAnchor(pendingAnchor))pendingAnchor=\"\";\n        root.dataset.worldCaptureSequence=String((Number(root.dataset.worldCaptureSequence)||0)+1);\n      }\n      const pageAnimation=transition.update(now,{reducedMotion:reducedMotion.matches});\n      if(!transition.active&&native.layout)layout=native.layout;\n      scrollY=transition.displayScrollY;stage.position.set(0,-scrollY);stage.scale.set(viewport.scale);\n      root.style.height=`${Math.max(viewport.height,transition.displayHeight*viewport.scale)}px`;\n      native.setScrollY(transition.preparingScrollY);\n      sceneAnimation=native.animate(now,reducedMotion.matches);\n      media.sync(transition.media(),viewport,scrollY);input.sync(transition.active?[]:layout?.fields??[]);\n      paintScene(now);\n\n      publishDiagnostics();if(!active)status(true);\n      needsRender=false;lastDraw=now;\n      if(pageAnimation||sceneAnimation||(mediaAnimation&&!reducedMotion.matches))frame=requestAnimationFrame(draw);\n    }catch(error){\n      needsLayout=false;needsRender=false;mediaAnimation=false;sceneAnimation=false;status(false);\n      console.error(\"The native scene stopped; the semantic page remains available.\",error);\n    }\n  }\n  const observer=new MutationObserver(records=>{\n    if(records.some(record=>record.type!==\"attributes\"||!record.attributeName?.startsWith(\"data-world-runtime-\")))invalidate({layout:true});\n  });\n  observer.observe(source,{subtree:true,attributes:true,childList:true,characterData:true});\n  listen(window,'tragik:navigation-start',event=>{\n    if(!active||contextLost||!layout)return;\n    input.close();\n    const element=event.detail?.element;\n    const control=(layout.controls??[]).find(item=>sourceFor(item.actionId)===element);\n    transition.start({...event.detail,controlId:control?.id});\n  });\n  listen(window,'tragik:navigation-ready',event=>{\n    focused=null;input.close();native.closeDropdown();\n    pendingAnchor='';transition.commit(event.detail);invalidate({layout:true});\n  });\n  listen(window,\"resize\",resize);\n  for(const type of [\"popstate\",\"hashchange\"])listen(window,type,()=>{pendingAnchor=window.location.hash;invalidate({layout:true});});\n  // Keyboard/semantic and Pixi clicks share the same fragment behavior.\n  listen(source,\"click\",event=>{\n    const element=event.target.closest?.(\"a[href]\");\n    if(!element||event.defaultPrevented||event.metaKey||event.ctrlKey||event.shiftKey||event.altKey||element.hasAttribute(\"download\")||element.target&&element.target!==\"_self\")return;\n    const destination=new URL(element.getAttribute(\"href\"),window.location.href);\n    if(destination.origin!==location.origin||destination.pathname!==location.pathname||destination.search!==location.search||!destination.hash||!Number.isFinite(sceneAnchorOffset(destination.hash,layout?.anchors)))return;\n    event.preventDefault();\n    if(destination.href!==location.href)history.pushState(history.state,\"\",destination.href);\n    scrollAnchor(destination.hash,reducedMotion.matches?\"instant\":\"smooth\");\n  },true);\n  if(window.visualViewport)for(const type of [\"resize\",\"scroll\"])listen(window.visualViewport,type,()=>{input.sync(layout?.fields??[]);invalidate();});\n  listen(window,\"scroll\",()=>{scrollY=window.scrollY;native.setScrollY(transition.preparingScrollY);invalidate();},{passive:true});\n  listen(document,\"visibilitychange\",()=>invalidate());\n  listen(reducedMotion,\"change\",()=>invalidate());\n  listen(source,\"input\",()=>invalidate({layout:true}),true);\n  listen(source,\"change\",()=>invalidate({layout:true}),true);\n  listen(source,\"toggle\",()=>invalidate({layout:true}),true);\n  listen(source,\"focusin\",event=>{\n    const entry=[...(documentData?.actions??[])].find(([,element])=>element===event.target);\n    if(entry){focused=entry[0];visible(native.focus(focused));invalidate();}\n  });\n  listen(source,\"keydown\",event=>{\n    if(transition.active){event.preventDefault();return;}\n    if(native.handleKey?.(focused,event.key,event)){event.preventDefault();return;}\n    if(event.key===\"Tab\"){event.preventDefault();input.close();focusNext(focused,event.shiftKey?-1:1);}\n    else {\n      const control=layout?.controls.find(item=>item.actionId===focused);\n      if(control?.kind===\"range\"&&[\"ArrowLeft\",\"ArrowRight\",\"ArrowDown\",\"ArrowUp\",\"Home\",\"End\"].includes(event.key)){\n        event.preventDefault();control.view.sceneAdjust(event.key===\"Home\"?-10000:event.key===\"End\"?10000:[\"ArrowLeft\",\"ArrowDown\"].includes(event.key)?-1:1);\n      }else if(control&&[\"select\",\"checkbox\",\"radio\"].includes(control.kind)&&[\"Enter\",\" \"].includes(event.key)){\n        event.preventDefault();control.view.sceneActivate({nativeEvent:event});invalidate();\n      }else if([\"INPUT\",\"TEXTAREA\"].includes(event.target.tagName)&&![\"range\",\"checkbox\",\"radio\"].includes(event.target.type)&&event.key!==\"Escape\"){\n      const item=layout?.fields.find(item=>sourceFor(item.node.actionId??item.id)===event.target);\n      if(item&&!input.activeId)input.open(item.node,item.rect);\n      }\n    }\n  });\n  canvas.tabIndex=-1;\n  listen(canvas,\"keydown\",event=>{\n    if(transition.active){event.preventDefault();return;}\n    if(event.key===\"Tab\"){event.preventDefault();focusNext(focused,event.shiftKey?-1:1);}\n    else if(event.key===\"Escape\"){native.closeDropdown();input.close();invalidate();}\n    else if([\"PageDown\",\"PageUp\",\"ArrowDown\",\"ArrowUp\",\" \"].includes(event.key)){\n      event.preventDefault();window.scrollBy({top:(event.key===\"ArrowUp\"?-60:event.key===\"ArrowDown\"?60:event.key===\"PageUp\"?-viewport.height*.85:viewport.height*.85),behavior:\"instant\"});\n    }\n  });\n  for(const type of [\"pointerdown\",\"pointermove\",\"pointerup\",\"pointercancel\",\"wheel\"]){\n    listen(canvas,type,event=>{\n      if(type===\"pointerdown\"){\n        input.close();\n        native.pointerDown({x:event.clientX/viewport.scale,y:(event.clientY+scrollY)/viewport.scale});\n      }\n      if(transition.moving){event.preventDefault();return;}\n      if(media.pointer(type,event)){event.preventDefault();event.stopImmediatePropagation();if(type===\"pointerdown\")canvas.setPointerCapture?.(event.pointerId);invalidate();}\n    },{capture:true,passive:false});\n  }\n  listen(canvas,\"touchstart\",event=>{if(media.claimTouch(event))event.preventDefault();},{capture:true,passive:false});\n  listen(window,\"tragik:scene-media-command\",event=>{const detail=event.detail;if(detail?.id&&detail?.command){media.command(detail.id,detail.command,detail.args);invalidate();}});\n  listen(window,\"tragik:studio-selection\",event=>{if(document.getElementById('tragik-studio-preview')){native.studioSelect(event.detail?.field??null);invalidate();}});\n  listen(canvas,\"webglcontextlost\",event=>{\n    transition.cancel();event.preventDefault();contextLost=true;if(frame)cancelAnimationFrame(frame);frame=0;input.close();status(false);\n  });\n  listen(canvas,\"webglcontextrestored\",()=>{contextLost=false;needsLayout=true;resize();});\n  const studioConfig=document.getElementById('tragik-studio-preview');\n  if(studioConfig)native.studioSelect(studioConfig.dataset.selectedRegion??null);\n  status(false,\"starting\");resize();\n  return {destroy(){\n    disposed=true;if(frame)cancelAnimationFrame(frame);observer.disconnect();cleanup.forEach(remove=>remove());\n    transition.destroy();input.destroy();media.destroy();sky.destroy();native.destroy();reader.destroy();status(false,\"stopped\");\n    overlay.remove();stage.destroy({children:true});three.dispose();pixi.destroy({removeView:false});root.style.removeProperty(\"height\");\n  }};\n}\n","runtime/native-input.mjs":"/** Temporary browser text entry; all resting fields and controls are Pixi. */\nexport function writeSemanticField(element, value) {\n  if (!element || element.disabled || element.readOnly) return;\n  const view = element.ownerDocument.defaultView;\n  if (element.tagName === \"SELECT\") {\n    element.value = String(value);\n    element.dispatchEvent(new view.Event(\"change\", {bubbles:true}));\n    return;\n  }\n  const checkbox = [\"checkbox\", \"radio\"].includes(element.type);\n  if (checkbox) {\n    if (element.checked !== Boolean(value)) element.click();\n    return;\n  }\n  const prototype = element.tagName === \"TEXTAREA\" ? view.HTMLTextAreaElement.prototype : view.HTMLInputElement.prototype;\n  Object.getOwnPropertyDescriptor(prototype, \"value\")?.set?.call(element, String(value));\n  element.dispatchEvent(new view.Event(\"input\", {bubbles:true}));\n  element.dispatchEvent(new view.Event(\"change\", {bubbles:true}));\n}\n\nexport function createNativeInput({overlay, resolve, projectRect, invalidate, onTab}) {\n  let active = null;\n  function place() {\n    if (!active) return;\n    const rect = projectRect(active.rect);\n    const visible = globalThis.visualViewport;\n    const top = visible?.offsetTop ?? 0, height = visible?.height ?? globalThis.innerHeight;\n    const editorHeight = Math.min(rect.height, Math.max(48, height - 24));\n    const editorY = Math.max(top + 8, Math.min(rect.y, top + height - editorHeight - 12));\n    Object.assign(active.editor.style, {left:`${rect.x}px`,top:`${editorY}px`,width:`${rect.width}px`,height:`${editorHeight}px`,fontSize:`${Math.max(16, 18 * rect.scale)}px`});\n  }\n  function close() {\n    if (!active) return;\n    const previous = active; active = null;\n    previous.editor.remove();\n    invalidate();\n  }\n  return {\n    open(node, rect) {\n      const element = resolve(node.actionId ?? node.id);\n      if (!element || element.disabled || [\"select\", \"range\", \"checkbox\", \"radio\"].includes(node.fieldType)) return;\n      if (active?.id === node.id) { active.rect = rect; place(); active.editor.focus({preventScroll:true}); return; }\n      close();\n      const editor = document.createElement(element.tagName === \"TEXTAREA\" ? \"textarea\" : \"input\");\n      if (editor.tagName === \"INPUT\") editor.type = [\"text\",\"email\",\"url\",\"tel\",\"number\",\"search\",\"password\"].includes(element.type) ? element.type : \"text\";\n      editor.value = element.value ?? \"\";\n      editor.readOnly = Boolean(element.readOnly);\n      editor.placeholder = element.placeholder ?? \"\";\n      if (element.maxLength >= 0) editor.maxLength = element.maxLength;\n      editor.setAttribute(\"aria-label\", node.label ?? node.metadata?.label ?? node.text ?? \"Edit field\");\n      editor.autocomplete = element.autocomplete || \"off\";\n      editor.className = \"scene-text-entry\";\n      Object.assign(editor.style, {position:\"fixed\",pointerEvents:\"auto\",boxSizing:\"border-box\",margin:\"0\",padding:\"10px\",border:\"3px solid #005acb\",borderRadius:\"0\",background:\"#fffdf5\",color:\"#080a0d\",fontFamily:\"Arial, sans-serif\",lineHeight:\"1.4\",zIndex:\"10\"});\n      active = {id:node.id,actionId:node.actionId,editor,element,rect};\n      editor.addEventListener(\"input\", () => { writeSemanticField(element, editor.value); invalidate({layout:true}); });\n      editor.addEventListener(\"keydown\", event => {\n        if (event.key === \"Tab\") { event.preventDefault(); const id = active?.actionId; close(); onTab?.(id,event.shiftKey?-1:1); }\n        else if (event.key === \"Escape\") { event.preventDefault(); const original = active?.element; close(); original?.focus({preventScroll:true}); }\n      });\n      editor.addEventListener(\"blur\", close);\n      overlay.appendChild(editor); place(); editor.focus({preventScroll:true});\n      try { editor.setSelectionRange(editor.value.length,editor.value.length); } catch { /* Number fields have no text-selection API. */ }\n      invalidate();\n    },\n    sync(fields) {\n      if (!active) return;\n      const field = fields.find(item=>item.id===active.id);\n      if (!field) return close();\n      active.rect = field.rect;\n      if (active.editor !== document.activeElement) active.editor.value = active.element.value ?? \"\";\n      place();\n    },\n    close,\n    get activeId() { return active?.actionId ?? null; },\n    destroy:close,\n  };\n}\n","runtime/native-media.mjs":"import { GLTFLoader } from \"/cms-runtime/vendor-v1/gltf.js\";\nimport {sceneAssetUrl} from \"./scene-contract.mjs\";\nimport {getConsentController} from \"./consent.mjs\";\n\nconst SPRITE_ROOT = \"/labs/veiled-figure-2p5d/assets\";\nconst MEDIA_COMMANDS = new Set([\"reset\", \"fullscreen\", \"rotate\", \"hotspot\", \"version\", \"yaw\", \"sdf\", \"normal\", \"light\", \"grid\", \"auto-orbit\"]);\nconst clamp = (value, min, max) => Math.max(min, Math.min(max, value));\nconst numeric = (value, fallback = 0) => Number.isFinite(Number(value)) ? Number(value) : fallback;\n\nexport function projectMediaRect(rect, viewport, scrollY = 0) {\n  const scale = numeric(viewport?.scale, 1) || 1;\n  return { x: rect.x * scale, y: rect.y * scale - scrollY, width: rect.width * scale, height: rect.height * scale };\n}\n\nexport function normalizedModelFrame(extent, center = { x: 0, y: 0, z: 0 }) {\n  const scale = 2.8 / Math.max(numeric(extent?.x), numeric(extent?.y), numeric(extent?.z), .001);\n  return { scale, offset: { x: -numeric(center.x) * scale, y: -numeric(center.y) * scale, z: -numeric(center.z) * scale } };\n}\n\nexport function scanFrame(yaw) { return Math.round(((numeric(yaw) % 360) + 360) % 360 / 22.5) % 16; }\n\n/** Same-origin media host; no remote HTML is ever mounted into the page. */\nexport function safeMediaSource(src, kind, base = globalThis.location?.href ?? \"https://tragikminded.com/\") {\n  try {\n    const selected=[\"model\",\"audio\",\"video\"].includes(kind)?sceneAssetUrl(src,base):src;\n    if(typeof selected!==\"string\"||!selected||typeof src!==\"string\"||!src)return null;\n    const url = new URL(selected, base), origin = new URL(base).origin;\n    if (url.username || url.password) return null;\n    if (kind === \"youtube\") return url.protocol === \"https:\" && url.hostname === \"www.youtube-nocookie.com\" && !url.port && /^\\/embed\\/[a-zA-Z0-9_-]{11}$/.test(url.pathname) ? `${url.origin}${url.pathname}` : null;\n    if(kind === \"experience\")return url.origin===origin&&url.searchParams.get(\"cms_original\")===\"1\"&&!/^\\/(?:api|__cms|_vinext)(?:\\/|$)/.test(url.pathname)?url.href:null;\n    if (kind === \"legacy-fps\") return url.origin === origin && /^\\/legacy\\/fps(?:\\.html)?\\/?$/.test(url.pathname) ? url.href : null;\n    if (kind === \"sprite-scan\") return `${origin}${SPRITE_ROOT}`;\n    if ([\"audio\",\"video\"].includes(kind)) return url.origin === origin || url.origin === \"http://127.0.0.1:4191\" && [\"localhost\",\"127.0.0.1\"].includes(new URL(base).hostname) ? url.href : null;\n    return [\"http:\", \"https:\"].includes(url.protocol) && (url.origin === origin || url.protocol === \"https:\") ? url.href : null;\n  } catch { return null; }\n}\n\nfunction disposeObject(object) {\n  const textures = new Set(), materials = new Set(), geometries = new Set();\n  object.traverse((child) => {\n    if (child.geometry) geometries.add(child.geometry);\n    for (const material of Array.isArray(child.material) ? child.material : child.material ? [child.material] : []) {\n      materials.add(material);\n      for (const value of Object.values(material)) if (value?.isTexture) textures.add(value);\n      for (const uniform of Object.values(material.uniforms ?? {})) if (uniform?.value?.isTexture) textures.add(uniform.value);\n    }\n  });\n  textures.forEach(texture => texture.dispose()); materials.forEach(material => material.dispose()); geometries.forEach(geometry => geometry.dispose());\n}\n\n/** Rects come from native Pixi layout; no DOM measurements or CSS-derived values. */\nexport function createNativeMedia({ THREE, renderer, overlay, invalidate, projectRect = projectMediaRect, onTab }) {\n  const entries = new Map(), pointers = new Map();\n  let viewport = { width: 1, height: 1, scale: 1 }, scrollY = 0, destroyed = false, lastTime = 0, fullscreenId = null;\n  function fullscreenChanged() {\n    if (!document.fullscreenElement) {\n      const previous = [...entries.values()].find(e=>e.id===fullscreenId&&e.interactive!==false); fullscreenId = null;\n      if (previous) emit(previous, { message: \"\" });\n    }\n    for (const entry of entries.values()) updateRect(entry);\n    invalidate();\n  }\n  document.addEventListener(\"fullscreenchange\", fullscreenChanged);\n  function emit(entry, detail) {\n    if (destroyed||entry.interactive===false) return;\n    window.dispatchEvent(new CustomEvent(\"tragik:scene-media-state\", { detail: { id: entry.id, ...detail } }));\n  }\n  function makeView(entry) {\n    entry.scene = new THREE.Scene();\n    entry.scene.background = entry.node.metadata?.decorative ? null : new THREE.Color(0x0b1017);\n    entry.camera = new THREE.PerspectiveCamera(36, 1, .01, 150);\n    entry.yaw = 32 * Math.PI / 180; entry.pitch = 74 * Math.PI / 180;\n    entry.distance = 5.5; entry.target = new THREE.Vector3();\n    entry.rotating = entry.node.metadata?.rotate === true || entry.node.metadata?.rotate === \"true\";\n  }\n  function loadModel(entry) {\n    makeView(entry);\n    entry.scene.add(new THREE.HemisphereLight(0xffffff, 0x5c6681, 2.6));\n    const key = new THREE.DirectionalLight(0xffedcf, 3.2); key.position.set(3, 5, 4); entry.scene.add(key);\n    const rim = new THREE.DirectionalLight(0x81dfff, 2.2); rim.position.set(-4, 2, -3); entry.scene.add(rim);\n    emit(entry, { status: \"loading\" });\n    new GLTFLoader().load(entry.src, asset => {\n      if (destroyed || entries.get(entry.key??entry.id) !== entry) { disposeObject(asset.scene); return; }\n      const bounds = new THREE.Box3().setFromObject(asset.scene);\n      const center = bounds.getCenter(new THREE.Vector3());\n      const extent = bounds.getSize(new THREE.Vector3());\n      entry.normalization = normalizedModelFrame(extent, center);\n      asset.scene.scale.setScalar(entry.normalization.scale);\n      asset.scene.position.set(...[\"x\", \"y\", \"z\"].map(axis => entry.normalization.offset[axis]));\n      entry.scene.add(asset.scene); entry.object = asset.scene; entry.ready = true;\n      emit(entry, { status: \"ready\", rotating: entry.rotating }); invalidate();\n    }, undefined, () => {\n      entry.error = true; emit(entry, { status: \"error\", message: \"The model could not be loaded. Try loading it again.\" }); invalidate();\n    });\n  }\n  function loadSprite(entry) {\n    makeView(entry); entry.scene.background = new THREE.Color(0x030506); entry.scene.fog = new THREE.FogExp2(0x030506, .045);\n    entry.camera.fov = 34; entry.camera.updateProjectionMatrix(); entry.distance = 7.1; entry.autoOrbit = true; entry.version = \"b\";\n    entry.grid = new THREE.GridHelper(18, 36, 0x17434c, 0x151d21); entry.grid.position.y = -1.48; entry.scene.add(entry.grid);\n    entry.platform = new THREE.Mesh(new THREE.CylinderGeometry(1.75, 1.9, .08, 64), new THREE.MeshBasicMaterial({ color: 0x070b0d, transparent: true, opacity: .76 }));\n    entry.platform.position.y = -1.51; entry.scene.add(entry.platform);\n    entry.textures = new Map();\n    entry.uniforms = {\n      uColor: { value: null }, uSdf: { value: null }, uNormal: { value: null },\n      uFrameOffset: { value: new THREE.Vector2() }, uFrameScale: { value: new THREE.Vector2(1 / 16, 1) },\n      uSdfStrength: { value: .55 }, uNormalStrength: { value: .34 }, uLightDir: { value: new THREE.Vector3(.35, .2, .92).normalize() },\n    };\n    const material = new THREE.ShaderMaterial({\n      transparent: true, depthWrite: false, side: THREE.DoubleSide, uniforms: entry.uniforms,\n      vertexShader: \"varying vec2 vUv; void main(){vUv=uv;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);}\",\n      fragmentShader: `uniform sampler2D uColor;uniform sampler2D uSdf;uniform sampler2D uNormal;\n        uniform vec2 uFrameOffset;uniform vec2 uFrameScale;uniform float uSdfStrength;uniform float uNormalStrength;\n        uniform vec3 uLightDir;varying vec2 vUv;void main(){vec2 atlasUv=uFrameOffset+vUv*uFrameScale;\n        vec4 color=texture2D(uColor,atlasUv);if(color.a<.01)discard;float sdf=texture2D(uSdf,atlasUv).r;\n        vec3 normal=normalize(texture2D(uNormal,atlasUv).rgb*2.0-1.0);float diffuse=max(dot(normal,normalize(uLightDir)),0.0);\n        float bodyLight=mix(1.0,.56+diffuse*.72,uNormalStrength);float boundary=1.0-smoothstep(0.0,.065,abs(sdf-.5));\n        vec3 rim=mix(vec3(.67,.08,.09),vec3(.05,.73,.86),smoothstep(-.35,.35,normal.x));\n        color.rgb=color.rgb*bodyLight+rim*boundary*uSdfStrength;gl_FragColor=vec4(color.rgb,color.a);}`,\n    });\n    entry.character = new THREE.Mesh(new THREE.PlaneGeometry(2.35, 3.525), material);\n    entry.character.position.y = .27; entry.scene.add(entry.character);\n    setSpriteVersion(entry, \"b\");\n  }\n  function setSpriteVersion(entry, version) {\n    if (![\"a\", \"b\", \"c\"].includes(version)) return false;\n    entry.version = version; entry.ready = false; emit(entry, { status: \"loading\" });\n    const loader = new THREE.TextureLoader(), generation = (entry.generation ?? 0) + 1;\n    entry.generation = generation;\n    let remaining = 3;\n    for (const [uniform, filename] of [[\"uColor\", \"turntable-rgba.png\"], [\"uSdf\", \"turntable-sdf.png\"], [\"uNormal\", \"turntable-normal.png\"]]) {\n      const url = `${SPRITE_ROOT}/version-${version}/${filename}`;\n      const done = () => {\n        if (destroyed || entries.get(entry.key??entry.id) !== entry || generation !== entry.generation) return;\n        if (--remaining === 0) { entry.ready = true; emit(entry, { status: \"ready\" }); invalidate(); }\n      };\n      if (entry.textures.has(url)) { entry.uniforms[uniform].value = entry.textures.get(url); done(); continue; }\n      const texture = loader.load(url, done, undefined, () => { emit(entry, { status: \"error\", message: \"A scan atlas could not be loaded.\" }); invalidate(); });\n      texture.flipY = true; texture.minFilter = THREE.LinearFilter; texture.magFilter = THREE.LinearFilter; texture.generateMipmaps = false;\n      if (uniform === \"uColor\") texture.colorSpace = THREE.SRGBColorSpace;\n      entry.textures.set(url, texture); entry.uniforms[uniform].value = texture;\n    }\n    invalidate(); return true;\n  }\n  function mountLegacy(entry) {\n    if (!overlay) { emit(entry, { status: \"error\", message: \"The laboratory rendering host is unavailable.\" }); return; }\n    const frame = document.createElement(\"iframe\"); entry.frame = frame;\n    frame.title = entry.node.alt || \"Tragik Minded FPS laboratory\";\n    frame.src = entry.src; frame.allow = \"autoplay; fullscreen; gamepad\";\n    frame.dataset.sceneMediaHost = entry.id;\n    Object.assign(frame.style, { position: \"fixed\", border: \"0\", pointerEvents: \"auto\", display: \"block\", background: \"#030506\" });\n    frame.addEventListener(\"load\", () => { entry.ready = true; emit(entry, { status: \"ready\" }); invalidate(); });\n    frame.addEventListener(\"error\", () => emit(entry, { status: \"error\", message: \"The laboratory could not be loaded.\" }));\n    overlay.appendChild(frame);\n  }\n  function mountPlayer(entry) {\n    if (!overlay) { emit(entry, { status: \"error\", message: \"The media player host is unavailable.\" }); return; }\n    const host = document.createElement(\"div\");entry.frame = host;host.dataset.sceneMediaHost=entry.id;\n    host.dataset.sceneMediaKind=entry.kind;\n    Object.assign(host.style,{position:\"absolute\",pointerEvents:\"auto\",overflow:\"hidden\",background:\"#0b1017\",color:\"#fffdf5\",borderRadius:\"2px\"});\n    if(entry.kind===\"youtube\"){\n      const button=document.createElement(\"button\");entry.focusTarget=button;button.type=\"button\";\n      Object.assign(button.style,{width:\"100%\",height:\"100%\",padding:\"20px\",border:\"1px solid #6c7b8e\",background:\"#17212d\",color:\"#fffdf5\",font:\"600 18px system-ui\",cursor:\"pointer\"});\n      const consent=getConsentController();let requested=false;\n      function load(){\n        if(entry.player||!consent?.status().youtube||destroyed)return;\n        const frame=document.createElement(\"iframe\");entry.player=frame;entry.focusTarget=frame;\n        frame.title=entry.node.alt||\"YouTube video\";frame.src=entry.src+\"?autoplay=0&playsinline=1&rel=0\";\n        frame.allow=\"encrypted-media; fullscreen; picture-in-picture\";frame.allowFullscreen=true;frame.referrerPolicy=\"strict-origin-when-cross-origin\";\n        // Fractional blending prevents Chromium's cross-origin-frame\n        // occlusion optimization from discarding adjacent WebGL pixels.\n        // It is visually imperceptible and avoids a continuous GPU redraw.\n        Object.assign(frame.style,{position:\"absolute\",inset:\"0\",width:\"100%\",height:\"100%\",pointerEvents:\"auto\",border:\"0\",display:\"block\",background:\"#0b1017\",opacity:\"0.999\"});\n        frame.dataset.sceneMediaHost=entry.id;frame.dataset.sceneMediaKind=entry.kind;\n        frame.addEventListener(\"load\",()=>{entry.ready=true;emit(entry,{status:\"ready\"});invalidate();});\n        host.replaceChildren(frame);updateRect(entry);frame.focus({preventScroll:true});\n      }\n      function changed(state){\n        if(!state.youtube&&entry.player){entry.player.remove();entry.player=null;entry.ready=false;entry.focusTarget=button;host.replaceChildren(button);requested=false;invalidate();}\n        button.disabled=!state.available||state.disabled;\n        button.textContent=state.disabled?\"YouTube is disabled on this website\":!state.available?\"YouTube privacy settings are unavailable\":state.youtube?\"Load YouTube player\":\"Review YouTube privacy preferences\";\n        button.setAttribute(\"aria-label\",`${button.textContent}: ${entry.node.alt||\"Video\"}`);\n        if(state.youtube&&requested)load();\n      }\n      button.addEventListener(\"click\",()=>{\n        requested=true;if(consent?.status().youtube)load();else consent?.openPreferences();\n      });\n      host.appendChild(button);\n      if(consent)entry.consentUnsubscribe=consent.subscribe(changed);else changed({available:false,youtube:false,disabled:false});\n    }else{\n      const player=document.createElement(entry.kind);entry.player=player;entry.focusTarget=player;\n      player.controls=true;player.preload=\"none\";player.autoplay=false;player.src=entry.src;\n      player.setAttribute(\"aria-label\",entry.node.alt||`${entry.kind} player`);player.tabIndex=0;\n      if(entry.kind===\"video\"){player.playsInline=true;player.muted=true;}\n      Object.assign(player.style,{width:\"100%\",height:\"100%\",display:\"block\",objectFit:\"contain\",background:\"#0b1017\"});\n      player.addEventListener(\"loadedmetadata\",()=>{entry.ready=true;emit(entry,{status:\"ready\"});invalidate();});\n      player.addEventListener(\"error\",()=>{\n        if(entries.get(entry.key??entry.id)!==entry||destroyed||entry.error)return;\n        entry.error=true;const notice=document.createElement(\"p\"),link=document.createElement(\"a\");notice.setAttribute(\"role\",\"alert\");notice.textContent=\"This media cannot be played here. \";link.href=entry.src;link.textContent=\"Download file\";link.style.color=\"#8deff0\";notice.appendChild(link);\n        Object.assign(notice.style,{position:\"absolute\",inset:\"8px\",padding:\"8px\",margin:\"0\",background:\"#0b1017\",font:\"14px/1.5 system-ui\"});host.appendChild(notice);\n        emit(entry,{status:\"error\",message:\"This media could not be played. Try its download link or a supported browser.\"});invalidate();\n      });\n      host.appendChild(player);\n    }\n    host.addEventListener(\"keydown\",event=>{\n      // Browser video/audio controls retain their native keyboard behavior.\n      // Escape provides a predictable return to the next Pixi page control.\n      if(event.key===\"Escape\"&&onTab){event.preventDefault();onTab(entry.node.id,event.shiftKey?-1:1);}\n      else if(event.key===\"Tab\"&&event.target?.tagName===\"BUTTON\"&&onTab){event.preventDefault();onTab(entry.node.id,event.shiftKey?-1:1);}\n    });\n    overlay.appendChild(host);\n  }\n  function updateRect(entry) {\n    entry.screen = fullscreenId === entry.id && document.fullscreenElement && !entry.frame ? { x: 0, y: 0, width: viewport.width, height: viewport.height } : projectRect(entry.rect, viewport, scrollY);\n    entry.clipScreen=entry.clip?projectRect(entry.clip,viewport,scrollY):null;\n    if (!entry.frame) return;\n    entry.frame.inert=entry.interactive===false;\n    const clip=entry.clipScreen;entry.frame.style.clipPath=clip?`inset(${Math.max(0,clip.y-entry.screen.y)}px ${Math.max(0,entry.screen.x+entry.screen.width-clip.x-clip.width)}px ${Math.max(0,entry.screen.y+entry.screen.height-clip.y-clip.height)}px ${Math.max(0,clip.x-entry.screen.x)}px)`:'none';\n    const rect = entry.screen;\n    const inView=rect.y+rect.height>0&&rect.y<viewport.height;\n    if(entry.frame.tagName===\"IFRAME\"){entry.frame.width=String(Math.round(rect.width));entry.frame.height=String(Math.round(rect.height));}\n    Object.assign(entry.frame.style, { left: `${rect.x}px`, top: `${rect.y}px`, width: `${rect.width}px`, height: `${rect.height}px`, display:inView?\"block\":\"none\", visibility:inView?\"visible\":\"hidden\" });\n  }\n  function remove(entry) {\n    if (fullscreenId === entry.id) fullscreenId = null;\n    if([\"audio\",\"video\"].includes(entry.kind)&&entry.player){entry.player.pause();entry.player.removeAttribute(\"src\");entry.player.load();}\n    entry.consentUnsubscribe?.();entry.frame?.remove(); if (entry.scene) disposeObject(entry.scene);\n    entry.textures?.forEach(texture => texture.dispose());\n    entries.delete(entry.key??entry.id);\n    for (const [id, pointer] of pointers) if (pointer.entry === entry) pointers.delete(id);\n  }\n  function sync(mediaNodes, nextViewport, nextScrollY = 0) {\n    viewport = nextViewport; scrollY = nextScrollY;\n    const live = new Set();\n    for (const media of mediaNodes ?? []) {\n      const node = media.node ?? media;\n      const id = node.metadata?.id ?? media.id ?? node.id, key=media.renderKey??id, kind = node.mediaType ?? node.metadata?.kind ?? \"model\";\n      const src = safeMediaSource(node.src, kind);\n      if (!src || !media.rect || ![\"model\", \"legacy-fps\", \"experience\", \"sprite-scan\", \"video\", \"audio\", \"youtube\"].includes(kind)) continue;\n      live.add(key);\n      let entry = entries.get(key);\n      if (entry && (entry.src !== src || entry.kind !== kind)) { remove(entry); entry = null; }\n      if (!entry) {\n        entry = { id, key, kind, src, node, rect: media.rect, ready: false, interactive:media.interactive!==false }; entries.set(key, entry);\n        if (kind === \"model\") loadModel(entry); else if (kind === \"sprite-scan\") loadSprite(entry); else if([\"legacy-fps\",\"experience\"].includes(kind))mountLegacy(entry);else mountPlayer(entry);\n      }\n      const wasInteractive=entry.interactive;entry.interactive=media.interactive!==false;entry.clip=media.clip;\n      entry.node = node; entry.rect = media.rect; updateRect(entry);\n      if(!wasInteractive&&entry.interactive&&entry.ready)emit(entry,{status:'ready',rotating:entry.rotating});\n    }\n    for (const entry of entries.values()) if (!live.has(entry.key??entry.id)) remove(entry);\n  }\n  function command(id, operation, args) {\n    const entry = [...entries.values()].reverse().find(e=>e.id===id&&e.interactive!==false); if (!entry || !MEDIA_COMMANDS.has(operation)) return false;\n    const value = args && typeof args === \"object\" ? args.value : args;\n    if (operation === \"fullscreen\") {\n      const host = entry.frame ?? renderer.domElement.closest?.(\"[data-world-engine]\") ?? renderer.domElement.parentElement ?? renderer.domElement;\n      if (!host.requestFullscreen) { emit(entry, { message: \"Fullscreen is unavailable on this device.\" }); return false; }\n      fullscreenId = entry.id;\n      const request = host.requestFullscreen();\n      request?.then?.(() => { updateRect(entry); emit(entry, { message: \"Press Escape or use your device’s fullscreen gesture to leave full screen.\" }); invalidate(); });\n      request?.catch?.(() => { fullscreenId = null; updateRect(entry); emit(entry, { message: \"Fullscreen is unavailable on this device.\" }); invalidate(); }); return true;\n    }\n    if (operation === \"reset\") { entry.yaw = 32 * Math.PI / 180; entry.pitch = 74 * Math.PI / 180; entry.distance = entry.kind === \"sprite-scan\" ? 7.1 : 5.5; entry.target?.set(0, 0, 0); }\n    else if (operation === \"rotate\") { entry.rotating = typeof value === \"boolean\" ? value : !entry.rotating; emit(entry, { rotating: entry.rotating }); }\n    else if (operation === \"hotspot\") {\n      const key = typeof args === \"string\" ? args : args?.slot;\n      const hotspot = (entry.node.metadata?.hotspots ?? []).find(point => point.slot === key || point.label === key);\n      if (!hotspot || !entry.normalization) return false;\n      const coordinates = String(hotspot.position).split(/\\s+/).map(parseFloat);\n      if (coordinates.length !== 3 || !coordinates.every(Number.isFinite)) return false;\n      entry.target.set(...coordinates.map((coordinate, index) => coordinate * entry.normalization.scale + entry.normalization.offset[[\"x\", \"y\", \"z\"][index]]));\n      emit(entry, { activeHotspot: hotspot.label });\n    } else if (entry.kind === \"sprite-scan\") {\n      if (operation === \"version\") return setSpriteVersion(entry, String(value));\n      if (operation === \"yaw\") { entry.yaw = numeric(value) * Math.PI / 180; entry.autoOrbit = false; }\n      if (operation === \"sdf\") entry.uniforms.uSdfStrength.value = clamp(numeric(value), 0, 1.5);\n      if (operation === \"normal\") entry.uniforms.uNormalStrength.value = clamp(numeric(value), 0, 1);\n      if (operation === \"light\") { const angle = numeric(value) * Math.PI / 180; entry.uniforms.uLightDir.value.set(Math.sin(angle), .25, Math.cos(angle)).normalize(); }\n      if (operation === \"grid\") { entry.grid.visible = Boolean(value); entry.platform.visible = Boolean(value); }\n      if (operation === \"auto-orbit\") entry.autoOrbit = Boolean(value);\n    } else return false;\n    invalidate(); return true;\n  }\n  function findAt(event) {\n    return [...entries.values()].reverse().find(entry => entry.interactive!==false&&[\"model\",\"sprite-scan\"].includes(entry.kind) && !entry.node.metadata?.decorative && entry.screen && event.clientX >= entry.screen.x && event.clientY >= entry.screen.y && event.clientX <= entry.screen.x + entry.screen.width && event.clientY <= entry.screen.y + entry.screen.height);\n  }\n  function pointer(type, event) {\n    let tracked = pointers.get(event.pointerId ?? 0), entry = tracked?.entry ?? findAt(event);\n    if (!entry) return false;\n    if (type === \"wheel\") { entry.distance = clamp(entry.distance * Math.exp(numeric(event.deltaY) * .001), 2, 12); invalidate(); return true; }\n    if (type === \"pointerdown\") { pointers.set(event.pointerId ?? 0, { entry, x: event.clientX, y: event.clientY }); return true; }\n    if (type === \"pointermove\" && tracked) {\n      const peers = [...pointers.values()].filter(pointer => pointer.entry === entry && pointer !== tracked);\n      if (peers.length) {\n        const peer = peers[0], before = Math.hypot(tracked.x - peer.x, tracked.y - peer.y), after = Math.hypot(event.clientX - peer.x, event.clientY - peer.y);\n        if (before > 0 && after > 0) entry.distance = clamp(entry.distance * before / after, 2, 12);\n      } else {\n        entry.yaw -= (event.clientX - tracked.x) * .008;\n        if (entry.kind === \"model\") entry.pitch = clamp(entry.pitch - (event.clientY - tracked.y) * .008, .25, Math.PI - .25);\n      }\n      tracked.x = event.clientX; tracked.y = event.clientY; invalidate(); return true;\n    }\n    if ((type === \"pointerup\" || type === \"pointercancel\") && tracked) { pointers.delete(event.pointerId ?? 0); invalidate(); return true; }\n    return false;\n  }\n  function render(now) {\n    if (destroyed) return false;\n    const delta = Math.min(Math.max((now - (lastTime || now)) / 1000, 0), .05); lastTime = now;\n    const oldViewport = renderer.getViewport(new THREE.Vector4()), oldScissor = renderer.getScissor(new THREE.Vector4());\n    const oldTest = renderer.getScissorTest(), oldClear = renderer.getClearColor(new THREE.Color()), oldAlpha = renderer.getClearAlpha();\n    let animated = false;\n    try {\n      for (const entry of entries.values()) {\n        const rect = entry.screen;\n        if ((fullscreenId && document.fullscreenElement && entry.id !== fullscreenId) || !entry.scene || !rect || rect.width <= 0 || rect.height <= 0 || rect.y >= viewport.height || rect.y + rect.height <= 0) continue;\n        const dragging = [...pointers.values()].some(pointer => pointer.entry === entry);\n        const rotating = entry.kind === \"sprite-scan\" ? entry.autoOrbit : entry.rotating;\n        if (rotating && !dragging) { entry.yaw += delta * (entry.kind === \"sprite-scan\" ? 17 : 18) * Math.PI / 180; animated = true; }\n        entry.camera.aspect = rect.width / rect.height; entry.camera.updateProjectionMatrix();\n        if (entry.kind === \"sprite-scan\") {\n          entry.camera.position.set(Math.sin(entry.yaw) * entry.distance, 1.25, Math.cos(entry.yaw) * entry.distance); entry.camera.lookAt(0, .05, 0);\n          entry.character.lookAt(entry.camera.position.x, entry.character.position.y, entry.camera.position.z);\n          entry.uniforms.uFrameOffset.value.set(scanFrame(entry.yaw * 180 / Math.PI) / 16, 0);\n        } else {\n          const sinPitch = Math.sin(entry.pitch);\n          entry.camera.position.set(entry.target.x + Math.sin(entry.yaw) * sinPitch * entry.distance, entry.target.y + Math.cos(entry.pitch) * entry.distance, entry.target.z + Math.cos(entry.yaw) * sinPitch * entry.distance);\n          entry.camera.lookAt(entry.target);\n        }\n        renderer.resetState(); renderer.setViewport(rect.x, viewport.height - rect.y - rect.height, rect.width, rect.height);\n        const clip=entry.clipScreen,x0=Math.max(0,rect.x,clip?.x??0),y0=Math.max(0,rect.y,clip?.y??0),x1=Math.min(viewport.width,rect.x+rect.width,clip?clip.x+clip.width:Infinity),y1=Math.min(viewport.height,rect.y+rect.height,clip?clip.y+clip.height:Infinity);\n        if(x1<=x0||y1<=y0)continue;\n        renderer.setScissor(x0,viewport.height-y1,x1-x0,y1-y0);\n        renderer.setScissorTest(true); renderer.clear(!entry.node.metadata?.decorative, true, true); renderer.render(entry.scene, entry.camera);\n      }\n    } finally {\n      renderer.setViewport(oldViewport); renderer.setScissor(oldScissor); renderer.setScissorTest(oldTest); renderer.setClearColor(oldClear, oldAlpha);\n    }\n    return animated;\n  }\n  return { sync, command, pointer, render, focus(id){const entry=[...entries.values()].find(entry=>entry.interactive!==false&&(entry.id===id||entry.node.id===id));if(!entry?.focusTarget)return false;entry.focusTarget.focus({preventScroll:true});return true;}, claimTouch: event => [...(event.changedTouches ?? event.touches ?? [])].some(touch => Boolean(findAt(touch))), snapshot: () => [...entries.values()].map(entry => ({ id: entry.id, kind: entry.kind, ready: entry.ready, rotating: entry.kind === \"sprite-scan\" ? entry.autoOrbit : Boolean(entry.rotating), yaw: entry.yaw, pitch: entry.pitch, distance: entry.distance, target: entry.target?.toArray(), version: entry.version, grid: entry.grid?.visible, parameters: entry.uniforms ? { sdf: entry.uniforms.uSdfStrength.value, normal: entry.uniforms.uNormalStrength.value, light: entry.uniforms.uLightDir.value.toArray() } : undefined, rect: entry.screen ? { ...entry.screen } : null })), destroy() { destroyed = true; document.removeEventListener(\"fullscreenchange\", fullscreenChanged); for (const entry of entries.values()) remove(entry); pointers.clear(); } };\n}\n","runtime/native-paint.mjs":"import { SCENE_PALETTE as P, sceneAssetUrl } from './scene-contract.mjs';\n\nconst SCHEMATIC = '/media/artwork/quantum-schematic-paper-v2.webp';\nconst COVER = '/media/artwork/field-notes-hero-generated-v1.webp';\nconst PINK = 0xef527d;\nconst WHITE = 0xffffff;\nconst paper = (width = 1050, fill = P.paper) => ({ fill, image: SCHEMATIC, imageMode: 'repeat', imageWidth: width });\nconst dots = (color = P.ink, alpha = .14) => ({ pattern: 'dots', patternColor: color, patternAlpha: alpha });\nconst panel = (fill, extra = {}) => ({ fill, surface: true, ...extra });\nconst TEMPLATE = Object.freeze({\n  'studio-page': { ...paper(1200, WHITE), canvas: true },\n  'field-note-page': { ...paper(), canvas: true },\n  'editions-index-page': { ...paper(), canvas: true },\n  'release-page': { fill: WHITE, canvas: true },\n  'contact-page': { fill: 0xf6f1e6, ...dots(P.ink, .055), canvas: true },\n  'blog-page': { fill: 0xf6f1e6, ...dots(P.ink, .055), canvas: true },\n  'artifact-page': { fill: P.paper, ...dots(P.ink, .05), canvas: true },\n  'legacy-stage--cover': { ...paper(1050, 0xf6f1e6), canvas: true },\n  'not-found-page': { ...paper(), imageAlpha: .16, canvas: true },\n  'mesh-preview-page': { fill: 0x050708, ink: 0xeef2f3, pattern: 'grid', patternColor: 0x344e59, patternAlpha: .32, canvas: true },\n  'sprite-preview-route': { fill: 0x050708, ink: 0xeef2f3, pattern: 'grid', patternColor: 0x344e59, patternAlpha: .25, canvas: true },\n  storefront: { fill: P.paper, canvas: true },\n  'storefront-announcement': panel(0x333333, { ink: WHITE, padding: 15 }),\n  'storefront-header': panel(0x303030, { ink: WHITE, padding: 20 }),\n  'storefront-hero': panel(P.ink, { ink: WHITE, pattern: 'rings', patternColor: 0x777777, patternAlpha: .25 }),\n  'collection-grid': panel(WHITE, { ...paper(1100, WHITE), padding: 15 }),\n  'collection-panel': panel(P.paper, { ...dots(), padding: 24 }),\n  'drop-feature': panel(P.yellow, { pattern: 'rings', patternColor: PINK, patternAlpha: .8 }),\n  'archive-panel': panel(P.ink, { ink: WHITE }),\n  'drop-feature__visual': panel(P.paper),\n  'release-hero': panel(P.ink, { ink: WHITE, padding: 0, columns: 2 }),\n  'release-hero__copy': panel(P.ink, { ink: WHITE, ...dots(PINK, .24) }),\n  'release-hero__visual': panel(P.red, { ...paper(1000, P.red) }),\n  'release-record': panel(P.paper, { ...paper(1000) }),\n  'release-final': panel(P.yellow),\n  'editions-index-hero': panel(P.yellow, { pattern: 'rings', patternColor: PINK, patternAlpha: .85, columns: 2 }),\n  'edition-card': panel(WHITE, { columns: 2 }),\n  'edition-loop': panel(P.ink, { ink: WHITE }),\n  'contact-intro': panel(P.cyan, { ...dots(P.ink, .13) }),\n  'contact-intro__stamp': panel(P.yellow, { shadow: PINK, padding: 16 }),\n  'contact-form-panel': panel(WHITE, { ...paper(1050, WHITE) }),\n  'contact-brief-builder': panel(0xf6f1e6, { shadow: P.yellow, padding: 18 }),\n  'contact-form-preview__status': panel(P.orange, { padding: 13 }),\n  'contact-selected-brief': panel(P.cyan),\n  'contact-brief-output': panel(P.cyan),\n  'contact-privacy-note': panel(P.yellow),\n  'studio-hero': panel(P.ink, { ink: WHITE, padding: 0, columns: 2 }),\n  'studio-hero__copy': panel(P.ink, { ink: WHITE, ...dots(WHITE, .15) }),\n  'studio-hero__schematic': panel(P.yellow, { ...paper(880, P.yellow), padding: 28 }),\n  'studio-capability': panel(P.paper, { ...dots(P.ink, .14) }),\n  'studio-offers': panel(WHITE, { ...paper(1000, WHITE), columns: 2 }),\n  'studio-offers__intro': panel(P.ink, { ink: WHITE, shadow: PINK }),\n  'studio-contact': panel(P.yellow, { pattern: 'rings', patternColor: P.cyan, patternAlpha: .85 }),\n  'project-router__result': panel(WHITE, { shadow: P.cyan }),\n  'project-router__action': panel(P.yellow),\n  'blog-cover-art': panel(P.ink, { ink: WHITE, image: COVER, imageMode: 'cover', shade: .56, minHeight: 470, padding: 45 }),\n  'blog-masthead__bar': panel(P.ink, { ink: WHITE, padding: 18 }),\n  'blog-cover-art__stamp': panel(P.yellow, { shadow: PINK, padding: 16 }),\n  'blog-nav': panel(P.red, { padding: 14 }),\n  'blog-post-card': panel(P.paper, { ...dots(P.ink, .13) }),\n  'blog-archive': panel(P.ink, { ink: WHITE }),\n  'blog-theme-note': panel(P.yellow),\n  'field-note-hero': panel(P.ink, { ink: WHITE, padding: 0, columns: 2 }),\n  'field-note-hero__copy': panel(P.ink, { ink: WHITE, ...dots(P.cyan, .18) }),\n  'field-note-chapter': panel(P.paper, { columns: 2 }),\n  'field-note-chapter--object': panel(P.yellow),\n  'field-note-chapter--print': panel(P.cyan),\n  'field-note-chapter--loop': panel(WHITE),\n  'field-note-callout': panel(P.yellow, { shadow: PINK }),\n  'field-note-endcap': panel(P.ink, { ink: WHITE, pattern: 'rings', patternColor: PINK, patternAlpha: .8 }),\n  'field-note-recovery-hero': panel(P.ink, { ink: WHITE, ...paper(900, P.ink), imageAlpha: .08, columns: 2 }),\n  'field-note-recovery-endcap': panel(P.ink, { ink: WHITE, ...paper(900, P.ink), imageAlpha: .12 }),\n  'recovery-stack-visual': panel(WHITE, { ...paper(820, WHITE), imageAlpha: .35 }),\n  'recovery-stack-visual__panel': panel(P.paper, { shadow: P.ink }),\n  'recovery-stack-visual__panel--comic': panel(P.yellow),\n  'recovery-stack-visual__panel--lab': panel(P.cyan),\n  'recovery-stack-visual__panel--release': panel(PINK),\n  'artifact-title-panel': panel(0x080808, { ink: WHITE, pattern: 'rings', patternColor: PINK, patternAlpha: .55 }),\n  'artifact-viewer-panel': panel(P.yellow, { ...dots(P.ink, .1) }),\n  'artifact-viewer-caption': panel(WHITE, { padding: 14, shadow: P.cyan }),\n  'artifact-story-card': panel(P.paper, { ...dots(P.ink, .12) }),\n  'artifact-spec-sheet': panel(P.paper),\n  'artifact-spec-heading': panel(P.ink, { ink: WHITE }),\n  'artifact-commerce-panel': panel(P.yellow, { pattern: 'rings', patternColor: P.cyan, patternAlpha: .8 }),\n  'artifact-activate': panel(0xf3eee0, { pattern: 'rings', patternColor: PINK, patternAlpha: .55 }),\n  'artifact-hotspot-readout': panel(P.ink, { ink: WHITE, padding: 13 }),\n  'legacy-launch__copy': panel(P.blue, { ...dots(P.ink, .18) }),\n  'legacy-launch__controls': panel(WHITE),\n  'mesh-preview-copy': panel(0x070a0c, { ink: 0xeef2f3 }),\n  'mesh-preview-panel': panel(0x10191f, { ink: WHITE, pattern: 'grid', patternColor: 0x344e59, patternAlpha: .3 }),\n  'mesh-preview-maps': panel(0x080b0d, { ink: 0xeef2f3 }),\n  'not-found-panel': panel(P.yellow, { shadow: PINK }),\n  'comic-trail': panel(P.ink, { ink: WHITE }),\n  'comic-trail__previous': panel(P.cyan),\n  'comic-trail__next': panel(P.yellow),\n});\nconst parseColor = value => typeof value === 'number' ? value : P[value] ?? (/^#[\\da-f]{6}$/iu.test(value ?? '') ? Number.parseInt(value.slice(1), 16) : undefined);\nconst list = value => Array.isArray(value) ? value : String(value ?? '').split(/\\s+/u).filter(Boolean);\n\n/** Historic art direction encoded as authored scene tokens, without browser styles. */\nexport function nativePaintFor(node, appearance) {\n  const own = list(node?.metadata?.classes), parents = list(node?.metadata?.parentClasses), ancestors = list(node?.metadata?.ancestorClasses);\n  const has = name => own.includes(name), parent = name => parents.includes(name), ancestor = name => ancestors.includes(name) || parent(name);\n  let paint = {};\n  for (const token of own) if (TEMPLATE[token]) paint = { ...paint, ...TEMPLATE[token] };\n  if (own.some(token => /(?:-topbar|__topbar|-footer|__footer)$/u.test(token)) && !has('blog-footer')) paint = { ...paint, ...panel(P.ink, { ink: WHITE, padding: 18 }) };\n  if (has('blog-footer')) paint = panel(WHITE, { padding: 18 });\n  if (has('release-hero__visual') && ancestor('signal-release-page')) paint.fill = P.cyan;\n  if (has('release-hero__copy') && ancestor('signal-release-page')) paint.patternColor = P.cyan;\n  if (has('blog-cover-art') && appearance && Object.hasOwn(appearance, 'coverImage')) {\n    if (appearance.coverImage) paint.image = appearance.coverImage;\n    else delete paint.image;\n  }\n  if (node?.metadata?.tag === 'figure' && parent('field-note-hero')) paint = panel(P.cyan);\n  if (node?.metadata?.tag === 'figure' && parent('edition-card')) paint = panel(ancestor('edition-card--redline') ? P.red : P.cyan);\n  if (has('edition-card__copy')) paint = panel(ancestor('edition-card--signal') ? P.yellow : WHITE);\n  if (node?.metadata?.tag === 'article' && parent('release-sequence')) paint = panel([P.red, P.cyan, P.yellow][(node.metadata.siblingIndex ?? 0) % 3]);\n  if (parent('legacy-launch__controls')) paint = panel([P.cyan, P.orange, P.red, P.yellow][(node.metadata.siblingIndex ?? 0) % 4], { shadow: P.ink });\n  if (node?.metadata?.tag === 'span' && parent('studio-hero__schematic')) paint = panel((node.metadata.siblingIndex ?? 0) % 4 === 2 ? P.cyan : WHITE, { shadow: PINK, padding: 14 });\n  if (node?.metadata?.tag === 'div' && parent('studio-process')) paint = panel(P.ink, { ink: WHITE });\n  if (node?.metadata?.tag === 'li' && ancestor('studio-process')) paint = panel(WHITE, { ...paper(900, WHITE) });\n  const tone = parseColor(node?.tone);\n  if (tone !== undefined && !paint.canvas) { paint.fill = tone; paint.ink = luminance(tone) < 110 ? WHITE : P.ink; }\n  if (paint.fill !== undefined && paint.ink === undefined) paint.ink = luminance(paint.fill) < 110 ? WHITE : P.ink;\n  const design=node?.metadata?.design;\n  if(design){\n    if(design.backgroundImage){paint.image=design.backgroundImage;paint.imageMode='cover';paint.imageAlpha=design.backgroundOpacity??1;paint.surface=true;}\n    if(design.background){paint.fill=parseColor(design.background);paint.surface=true;}\n    if(design.foreground)paint.ink=parseColor(design.foreground);\n    for(const key of ['padding','gap','minHeight','columns','maxWidth','fontSize'])if(Number.isFinite(design[key]))paint[key]=design[key];\n    if(design.flow==='stack'){paint.columns=undefined;paint.stack=true;}else if(design.flow==='grid')paint.columns??=2;\n  }\n  return Object.keys(paint).length ? paint : null;\n}\n\nfunction luminance(value) { return (value >> 16) * .299 + ((value >> 8) & 255) * .587 + (value & 255) * .114; }\nfunction descendants(node) { return node ? [node, ...(node.children ?? []).flatMap(descendants)] : []; }\n\n/** CMS overrides the canvas independently of layout; template retains historic artwork. */\nexport function nativePagePaint(document) {\n  const rootPaint = descendants(document.root).map(node => nativePaintFor(node)).find(paint => paint?.canvas);\n  const paint = { fill: P.paper, ink: P.ink, ...rootPaint };\n  const appearance = document.appearance;\n  const design=descendants(document.root).find(node=>node.metadata?.designRoot&&node.metadata?.design)?.metadata.design;\n  const finish=()=>{if(design?.backgroundImage){paint.image=design.backgroundImage;paint.imageMode='cover';paint.imageAlpha=design.backgroundOpacity??1;}if(design?.background)paint.fill=parseColor(design.background);if(design?.foreground)paint.ink=parseColor(design.foreground);return paint;};\n  if (!appearance) return finish();\n  const fill = parseColor(appearance.backgroundColor);\n  if (fill !== undefined) { paint.fill = fill; paint.ink = luminance(fill) < 110 ? WHITE : P.ink; }\n  if (Object.hasOwn(appearance, 'backgroundImage')) {\n    if (appearance.backgroundImage) {\n      paint.image = appearance.backgroundImage; paint.imageMode = 'repeat'; paint.imageWidth = 1100;\n    } else delete paint.image;\n  }\n  if (Object.hasOwn(appearance, 'backgroundOpacity')) paint.imageAlpha = Math.max(0, Math.min(1, Number(appearance.backgroundOpacity ?? 1)));\n  const pattern = appearance.backgroundPattern;\n  if (pattern && pattern !== 'template') {\n    paint.pattern = pattern === 'none' ? null : pattern;\n    paint.patternColor = luminance(paint.fill) < 110 ? P.cyan : P.ink;\n    paint.patternAlpha = .12;\n  }\n  return finish();\n}\n\n/** Paints only inside native scene coordinates; textures never determine layout. */\nexport function createNativePainter({ PIXI, textureFor, requestTexture }) {\n  const graphic = target => { const view = new PIXI.Graphics(); target.addChild(view); return view; };\n  return function paintBackground(bounds, paint, target) {\n    const view = new PIXI.Container(); target.addChild(view);\n    view.label = 'native-background'; view.eventMode = 'none';\n    const { x, y, width, height } = bounds;\n    if (paint.shadow !== undefined) graphic(view).rect(x + 7, y + 7, width, height).fill(paint.shadow);\n    graphic(view).rect(x, y, width, height).fill(paint.fill ?? P.paper);\n    if (paint.image) {\n      const src = sceneAssetUrl(paint.image), texture = textureFor(src);\n      if (!texture) requestTexture(src,bounds);\n      else if (PIXI.Sprite) {\n        const holder = new PIXI.Container(); view.addChild(holder);\n        const tw = texture.width || texture.orig?.width || 1, th = texture.height || texture.orig?.height || 1;\n        if (paint.imageMode === 'repeat' && PIXI.TilingSprite) {\n          const tiled = new PIXI.TilingSprite({ texture, width, height });\n          tiled.x = x; tiled.y = y;\n          const scale = (paint.imageWidth || 1050) / tw;\n          tiled.tileScale?.set(scale, scale); tiled.alpha = paint.imageAlpha ?? 1; holder.addChild(tiled);\n        } else {\n          const scale = paint.imageMode === 'repeat' ? (paint.imageWidth || 1050) / tw : Math.max(width / tw, height / th);\n          const sw = tw * scale, sh = th * scale;\n          const repeat = paint.imageMode === 'repeat', columns = repeat ? Math.ceil(width / sw) : 1, rows = repeat ? Math.ceil(height / sh) : 1;\n          for (let row = 0; row < rows; row++) for (let column = 0; column < columns; column++) {\n            const sprite = new PIXI.Sprite(texture); sprite.width = sw; sprite.height = sh; sprite.alpha = paint.imageAlpha ?? 1;\n            sprite.x = x + (repeat ? column * sw : (width - sw) / 2); sprite.y = y + (repeat ? row * sh : (height - sh) / 2); holder.addChild(sprite);\n          }\n        }\n        const mask = graphic(view).rect(x, y, width, height).fill(WHITE); holder.mask = mask;\n      }\n    }\n    if (paint.shade) graphic(view).rect(x, y, width, height).fill({ color: 0, alpha: paint.shade });\n    const alpha = paint.patternAlpha ?? .14, color = paint.patternColor ?? P.ink;\n    if (paint.pattern === 'dots') {\n      const step = Math.max(15, Math.sqrt(width * height / 3500));\n      const dots = graphic(view);\n      for (let dx = x + step / 2; dx < x + width; dx += step) for (let dy = y + step / 2; dy < y + height; dy += step) dots.circle(dx, dy, Math.min(2.2, step * .08)).fill({ color, alpha });\n    } else if (paint.pattern === 'grid') {\n      const grid = graphic(view), step = 44;\n      for (let dx = x; dx <= x + width; dx += step) grid.moveTo(dx, y).lineTo(dx, y + height).stroke({ color, alpha, width: 1 });\n      for (let dy = y; dy <= y + height; dy += step) grid.moveTo(x, dy).lineTo(x + width, dy).stroke({ color, alpha, width: 1 });\n    } else if (paint.pattern === 'rings') {\n      const radius = Math.max(1, Math.min(width * .23, height * .23)), line = Math.min(Math.max(4, radius * .23), radius);\n      const cx = x + width - radius - line / 2 - 8, cy = y + radius + line / 2 + 8;\n      graphic(view).circle(cx, cy, radius).stroke({ color, alpha, width: line });\n      graphic(view).circle(x + width - radius * .4 - 8, y + height - radius * .4 - 8, radius * .4).fill({ color: color === PINK ? P.cyan : PINK, alpha });\n    }\n    if (paint.border) graphic(view).rect(x, y, width, height).stroke({ color: paint.borderColor ?? P.ink, width: paint.border, alignment: 1 });\n    return view;\n  };\n}\n","runtime/native-scene.mjs":"import {drawNativeShop} from './native-shop.mjs';\nimport { SCENE_PALETTE as P, cleanScenePolygon, logicalViewport, plainSceneText, sceneAssetUrl } from './scene-contract.mjs';\nimport { illustratedComicLayout, comicArtFrame, isComicAtlas, COMIC_LETTERING } from './comic-layout.mjs';\nimport { createNativePainter, nativePaintFor, nativePagePaint } from './native-paint.mjs';\n\nconst BODY = 'Arial, Helvetica, sans-serif';\nconst DISPLAY = 'Impact, Haettenschweiler, Arial Narrow, sans-serif';\nconst COMIC = 'CMS Comic, Arial, sans-serif';\nconst clamp = (n, a, b) => Math.min(b, Math.max(a, n));\nconst rect = (x, y, width, height) => ({ x, y, width, height });\nconst color = (value, fallback = P.paper) => typeof value === 'number' ? value : P[value] ?? (/^#[a-f\\d]{3,8}$/i.test(value ?? '') ? parseInt(value.slice(1).length === 3 ? [...value.slice(1)].map(v => v + v).join('') : value.slice(1, 7), 16) : fallback);\nconst isDark = value => { const c = color(value); return ((c >> 16) * .299 + ((c >> 8) & 255) * .587 + (c & 255) * .114) < 110; };\nconst visibleChildren = node => (node.children ?? []).filter(child => child && (child.type !== 'text' || plainSceneText(child).trim()));\n\n// Original WWW comic silhouettes in normalized paper coordinates.\nconst ARCHIVED_COMIC_PANELS = {\n  blog: [0,.01,.32,.01,.28,.33,.30,.42,.26,.47,.28,.52,.26,.64,0,.64],\n  'tool-bench': [.34,.01,.66,.01,.62,.32,.30,.32],\n  play: [.68,.01,1,.01,.96,.32,.64,.32],\n  shop: [.30,.34,.96,.34,1,.64,.28,.64,.30,.50,.28,.46,.32,.43],\n  studio: [0,.66,.32,.66,.32,.98,0,.98],\n  artifact: [.34,.66,.66,.66,.66,.98,.34,.98],\n  contact: [.68,.66,1,.66,1,.98,.68,.98],\n};\nexport function homePanelPolygon(id, bounds) {\n  if (bounds.points) return bounds.points;\n  const w=bounds.width,h=bounds.height;\n  if(id==='blog')return [0,0,w,0,w*.88,h*.40,w*.96,h*.56,w*.84,h*.66,w*.92,h*.78,w*.86,h,0,h];\n  if(id==='shop')return [w*.055,0,w*.95,0,w,h,0,h,w*.035,h*.56,0,h*.40,w*.07,h*.29];\n  if(id==='tool-bench'||id==='play')return [w*.075,0,w,0,w*.925,h,0,h];\n  return [0,0,w,0,w,h,0,h];\n}\n// Find a safe horizontal span for the complete label, including concave edges.\nexport function homePanelLabelSpan(points, y, height, width, inset) {\n  let left=inset,right=width-inset;\n  for(const scan of [y+.5,y+height/2,y+height-.5]){\n    const crossings=[];\n    for(let i=0;i<points.length;i+=2){const j=(i+2)%points.length,x1=points[i],y1=points[i+1],x2=points[j],y2=points[j+1];if((y1<=scan&&y2>scan)||(y2<=scan&&y1>scan))crossings.push(x1+(scan-y1)*(x2-x1)/(y2-y1));}\n    if(crossings.length>=2){left=Math.max(left,Math.min(...crossings)+inset);right=Math.min(right,Math.max(...crossings)-inset);}\n  }\n  return {x:left,width:Math.max(40,right-left)};\n}\n\n/** Explicit scene rectangles; the host applies one uniform viewport transform. */\nexport function homePanelLayout(viewport) {\n  const size = viewport.designWidth ? viewport : logicalViewport(viewport.width, viewport.height);\n  const w = size.designWidth;\n  if (w >= 1000 || (w >= 620 && size.designHeight < 550)) {\n    const h = Math.max(330, size.designHeight || 800), dense = h < 650;\n    const contentTop = dense ? 62 : 108, contentBottom = h - (dense ? 43 : 82), gap = dense ? 9 : 14;\n    const total = contentBottom - contentTop, panels={};\n    for(const [id,shape]of Object.entries(ARCHIVED_COMIC_PANELS)){\n      const xs=shape.filter((_,i)=>i%2===0).map(value=>value*w),ys=shape.filter((_,i)=>i%2===1).map(value=>contentTop+value*total),x=Math.min(...xs),y=Math.min(...ys);\n      panels[id]={...rect(x,y,Math.max(...xs)-x,Math.max(...ys)-y),points:shape.map((value,i)=>i%2?contentTop+value*total-y:value*w-x)};\n    }\n    return {width:w,height:h,logo:rect(22,dense?12:28,dense?220:290,dense?34:55),footer:rect(20,h-(dense?31:59),w-40,dense?24:36),panels};\n  }\n  return illustratedComicLayout(w);\n}\n\n/**\n * Pixi owns every visible rectangle, text run, hit area and pointer interaction.\n * The supplied document is content, never a source of layout/style measurements.\n */\nexport function createNativeScene({ PIXI, stage, invalidate = () => {}, onAction = () => {}, onField = () => {}, onMedia = () => {}, onIntent = () => {} }) {\n  let layer, motion, popup, studio, frameHost, frameId=0, frameSerial=0;\n  function createFrame(){\n    frameHost=new PIXI.Container();frameHost.label='comic-live-frame';frameHost.eventMode='passive';frameId=++frameSerial;\n    layer=new PIXI.Container();motion=new PIXI.Container();popup=new PIXI.Container();studio=new PIXI.Container();studio.eventMode='none';\n    frameHost.addChild(layer,motion,popup,studio);stage.addChild(frameHost);\n  }\n  createFrame();\n  let parts=[],shopLayout=null;\n  const criticalSources=new Set();\n  const textures = new Map(), pending = new Map(), failures = new Set();\n  const paintBackground = createNativePainter({ PIXI, textureFor: src => textures.get(src), requestTexture: (src,bounds) => { if (safeSource(src)) loadTexture(src,bounds); } });\n  const measurements = new Map();\n  let current, viewport, destroyed = false, generation = 0, focused = null, openSelect = null, activeRangeId = null, dropdownIndex = -1, dropdownBounds = null, dropdownField = null;\n  let controls = [], fields = [], media = [], anchors = new Map(), studioRegions=[];\n  let studioField=null;\n  let lastDocument, lastOptions;\n  let homeViews = [], transitionPhase = null, transitionStarted = 0, transitionScene = null;\n\n  function clear(container) {\n    for (const child of container.removeChildren()) child.destroy({ children: true, context: true });\n  }\n  function position(view, x, y) {\n    if (view.position?.set) view.position.set(x, y); else { view.x = x; view.y = y; }\n    return view;\n  }\n  function graphic(target = layer) { const view = new PIXI.Graphics(); target.addChild(view); return view; }\n  function box(bounds, fill = P.paper, stroke = P.ink, line = 3, target = layer) {\n    const view = graphic(target);\n    view.rect(bounds.x, bounds.y, bounds.width, bounds.height).fill(fill);\n    if (line) view.stroke({ color: stroke, width: line, alignment: 1 });\n    return view;\n  }\n  function font(options = {}) {\n    return { fontFamily: BODY, fontSize: viewport.designWidth === 390 ? 17 : 19, fontWeight: '400', fontStyle: 'normal', fontVariant: 'normal', fill: P.ink, lineHeight: viewport.designWidth === 390 ? 25 : 29, breakWords: true, ...options };\n  }\n  function measure(text, style) {\n    const key = `${style.fontFamily}|${style.fontSize}|${style.fontWeight}|${style.fontStyle}|${style.letterSpacing}|${text}`;\n    if (measurements.has(key)) return measurements.get(key);\n    const view = new PIXI.Text({ text: String(text), style: { ...style, wordWrap: false } });\n    const width = view.width;\n    view.destroy();\n    measurements.set(key, width);\n    return width;\n  }\n  function label(text, x, y, width, options = {}, target = layer) {\n    const style = font({ wordWrap: true, wordWrapWidth: Math.max(1, width), ...options });\n    const view = new PIXI.Text({ text: String(text ?? ''), style });\n    const alignedX = style.align === 'center' ? x + Math.max(0, (width - view.width) / 2) : style.align === 'right' ? x + Math.max(0, width - view.width) : x;\n    position(view, alignedX, y); target.addChild(view);\n    return { view, height: Math.max(style.lineHeight ?? style.fontSize * 1.4, view.height) };\n  }\n  function boundedLabel(text, x, y, width, maxHeight, options = {}, target = layer) {\n    let content = String(text ?? ''), size = Number(options.fontSize) || 18;\n    const lineRatio = (Number(options.lineHeight) || size * 1.2) / size;\n    let style = font({ ...options, wordWrap: true, wordWrapWidth: width });\n    const probe = new PIXI.Text({ text: content, style });\n    while (probe.height > maxHeight && size > (options.minFontSize ?? 12)) {\n      size -= 1; style = { ...style, fontSize: size, lineHeight: size * lineRatio }; probe.style = style;\n    }\n    if (probe.height > maxHeight) {\n      while (content.length > 1 && probe.height > maxHeight) { content = content.slice(0, -1).trimEnd(); probe.text = `${content}…`; }\n      content = probe.text;\n    }\n    probe.destroy();\n    return label(content, x, y, width, style, target);\n  }\n  function keyFor(node) { return node.actionId ?? node.id; }\n\n  function interactive(node, bounds, options = {}) {\n    const view = new PIXI.Container();\n    position(view, bounds.x, bounds.y);\n    view.eventMode = node.disabled ? 'none' : 'static';\n    view.cursor = node.disabled ? 'default' : 'pointer';\n    view.hitArea = options.points && PIXI.Polygon ? new PIXI.Polygon(options.points) : new PIXI.Rectangle(0, 0, bounds.width, bounds.height);\n    view.label = node.id ?? node.actionId ?? 'scene-control';\n    view.alpha = node.disabled ? .48 : 1;\n    const states = graphic(view);\n    let hovered = false, pressed = false;\n    const repaint = () => {\n      if(view.destroyed||states.destroyed)return;\n      states.clear();\n      if (hovered || pressed) (options.points?states.poly(options.points):states.rect(0,0,bounds.width,bounds.height)).fill({color:options.dark?P.paper:P.ink,alpha:pressed?.15:.07});\n      if(focused===keyFor(node))(options.points?states.poly(options.points):states.rect(3,3,Math.max(0,bounds.width-6),Math.max(0,bounds.height-6))).stroke({width:3,color:options.dark?P.yellow:0x005acb});\n      invalidate();\n    };\n    const active = handler => event => { if (!node.disabled && !destroyed && !view.destroyed) handler(event); };\n    view.on('pointerover', active(() => { hovered = true; if(node.href??node.metadata?.href)onIntent(node.href??node.metadata.href); repaint(); }));\n    view.on('pointerout', active(() => { hovered = false; pressed = false; repaint(); }));\n    view.on('pointerdown', active(() => { pressed = true; repaint(); }));\n    view.on('pointerup', active(() => { pressed = false; repaint(); }));\n    view.on('pointerupoutside', active(() => { pressed = false; repaint(); }));\n    const activate = active(event => { pressed = false; repaint(); (options.activate ?? (e => onAction(node.actionId, e, node)))(event); });\n    view.on('pointertap', activate);\n    view.sceneActivate = activate;\n    view.sceneFocus = repaint;\n    (options.target ?? layer).addChild(view);\n    // Paint state last, including after labels and any custom background.\n    view.sceneState = states;\n    repaint();\n    return view;\n  }\n  function putStateLast(view) { view.addChild(view.sceneState); }\n\n  function drawRich(node, x, y, width, options = {}) {\n    if(node.metadata?.design?.fontSize)options={...options,fontSize:node.metadata.design.fontSize,lineHeight:node.metadata.design.fontSize*1.35};\n    const style = font(options), height = style.lineHeight ?? style.fontSize * 1.4;\n    const runs = node.runs?.length ? node.runs : [{ text: plainSceneText(node) }];\n    let cx = x, cy = y, lineHasText = false;\n    const fragments = [];\n    for (const run of runs) {\n      const runStyle = { ...style, fontWeight: run.strong ? '700' : style.fontWeight, fontStyle: run.emphasis ? 'italic' : style.fontStyle, fontFamily: run.code ? 'monospace' : style.fontFamily, fill: run.actionId ? isDark(style.fill) ? 0x1746a2 : P.cyan : style.fill };\n      const tokens = String(run.text ?? '').split(/(\\n|[^\\S\\n]+|[^\\s]+)/).filter(Boolean);\n      for (let token of tokens) {\n        if (token === '\\n') { cx = x; cy += height; lineHasText = false; continue; }\n        if (!lineHasText && /^\\s+$/.test(token)) continue;\n        let tokenWidth = measure(token, runStyle);\n        if (cx > x && cx + tokenWidth > x + width) { cx = x; cy += height; lineHasText = false; if (/^\\s+$/.test(token)) continue; }\n        // Split unbroken URLs/code by native font metrics instead of overflowing.\n        while (tokenWidth > width && token.length > 1) {\n          let count = Math.max(1, Math.floor(token.length * width / tokenWidth));\n          while (count > 1 && measure(token.slice(0, count), runStyle) > width) count--;\n          const part = token.slice(0, count); fragments.push({ text: part, x: cx, y: cy, width: measure(part, runStyle), run, style: runStyle });\n          token = token.slice(count); cx = x; cy += height; tokenWidth = measure(token, runStyle); lineHasText = false;\n        }\n        if (token) { fragments.push({ text: token, x: cx, y: cy, width: tokenWidth, run, style: runStyle }); cx += tokenWidth; lineHasText = true; }\n      }\n    }\n    const alignment=node.metadata?.design?.alignment;\n    if(alignment==='center'||alignment==='right')for(const line of new Set(fragments.map(p=>p.y))){const parts=fragments.filter(p=>p.y===line),used=Math.max(...parts.map(p=>p.x+p.width))-x,shift=Math.max(0,width-used)*(alignment==='center'?.5:1);for(const part of parts)part.x+=shift;}\n    // Coalesce same-action fragments per line to give inline links useful hit areas.\n    const merged = [];\n    for (const part of fragments) {\n      const previous = merged.at(-1);\n      if (previous && previous.y === part.y && previous.run === part.run && Math.abs(previous.x + previous.width - part.x) < .5) { previous.text += part.text; previous.width += part.width; }\n      else merged.push({ ...part });\n    }\n    for (const part of merged) {\n      if (part.run.actionId) {\n        const linkNode = { id: `${node.id ?? 'rich'}:${merged.indexOf(part)}`, type: 'control', actionId: part.run.actionId, text: part.text };\n        const bounds = rect(part.x, part.y, Math.max(part.width, 10), height);\n        const view = interactive(linkNode, bounds);\n        label(part.text, 0, 0, Math.max(part.width + 1, 1), { ...part.style, wordWrap: false }, view);\n        graphic(view).moveTo(0, height - 3).lineTo(part.width, height - 3).stroke({ color: part.style.fill, width: 1 });\n        putStateLast(view);\n        controls.push({ id: linkNode.id, actionId: linkNode.actionId, rect: bounds, kind: 'inline-link', node: linkNode, view });\n      } else label(part.text, part.x, part.y, Math.max(part.width + 1, 1), { ...part.style, wordWrap: false });\n    }\n    return Math.max(height, cy - y + height);\n  }\n\n  function safeSource(source) {\n    if (!source || !/^(?:https?:\\/\\/|\\/|data:image\\/(?:png|jpe?g|webp|gif|avif);)/i.test(source)) return null;\n    return sceneAssetUrl(source);\n  }\n  function loadTexture(src,bounds) {\n    const top=(lastOptions?.scrollY??0)/(viewport?.scale??1),height=viewport?.designHeight??Infinity;\n    if(!bounds||bounds.y+bounds.height>=top&&bounds.y<=top+height)criticalSources.add(src);\n    if (textures.has(src) || pending.has(src) || failures.has(src) || destroyed) return;\n    // Pixi's image resource sets crossOrigin; inaccessible origins stay an explicit\n    // native error panel. The host may supply its approved same-origin CMS proxy.\n    // Rendition identities deliberately have no file extension. Pixi needs the\n    // explicit raster parser for this fixed image endpoint, including previews.\n    const asset = /\\/__cms\\/(?:media\\/[a-f0-9]{64}|feed-image\\/(?:shopify|blogger))(?:\\?|$)/u.test(src) ? { src, parser: 'texture' } : src;\n    const task = Promise.resolve().then(() => PIXI.Assets.load(asset)).then(texture => {\n      if (destroyed) return;\n      if (!texture) throw new Error('Image could not be decoded.');\n      textures.set(src, texture);\n      pending.delete(src);\n      invalidate({ layout: true });\n    }).catch(() => { if (!destroyed) { failures.add(src); pending.delete(src); invalidate({ layout: true }); } });\n    pending.set(src, task);\n  }\n  function image(node, x, y, width, height, { fit = 'contain', target = layer, background = P.paper } = {}) {\n    const src = safeSource(node.src);\n    if(background!==null)box(rect(x, y, width, height), background, P.ink, 0, target);\n    const texture = src && textures.get(src);\n    if (texture && PIXI.Sprite) {\n      const sprite = new PIXI.Sprite(texture), tw = texture.width || texture.orig?.width || width, th = texture.height || texture.orig?.height || height;\n      const scale = fit === 'cover' ? Math.max(width / tw, height / th) : Math.min(width / tw, height / th);\n      sprite.width = tw * scale; sprite.height = th * scale;\n      position(sprite, x + (width - sprite.width) / 2, y + (height - sprite.height) / 2);\n      if (fit === 'cover') { const mask = box(rect(x, y, width, height), 0xffffff, 0, 0, target); sprite.mask = mask; }\n      target.addChild(sprite);\n    } else {\n      if (src && !failures.has(src)) loadTexture(src,rect(x,y,width,height));\n      const text = node.alt || 'Image';\n      label(`${src && !failures.has(src) ? 'Loading image' : 'Image unavailable'}\\n${text}`, x + 14, y + Math.max(8, height / 2 - 30), Math.max(1, width - 28), { fontSize: 14, lineHeight: 20, align: 'center', fill: isDark(background) ? P.paper : P.ink }, target);\n    }\n  }\n\n  function drawControl(node, x, y, width, context = {}) {\n    const tiny = context.compact;\n    const children = visibleChildren(node);\n    const text = plainSceneText(node) || children.map(plainSceneText).join(' ');\n    const paint = nativePaintFor(node, lastDocument?.appearance);\n    const fill = color(paint?.fill ?? node.tone, node.selected ? P.green : context.fill ?? P.yellow), dark = isDark(fill), ink = paint?.ink ?? (dark ? P.paper : P.ink);\n    const padding = paint?.padding??(tiny ? 12 : 16);\n    if (!tiny && children.some(child => ['heading', 'image', 'stack', 'grid'].includes(child.type))) {\n      const start = layer.children.length;\n      const contentHeight = drawFlow(children, x + padding, y + padding, width - padding * 2, { ...context, ink, gap: 13 });\n      const contentViews = layer.children.slice(start);\n      const height = Math.max(48, contentHeight + padding * 2), bounds = rect(x, y, width, height);\n      const view = interactive(node, bounds, { dark });\n      paintBackground(rect(0, 0, width, height), { ...paint, fill, border: 3 }, view);\n      for (const child of contentViews) { const px = child.x ?? 0, py = child.y ?? 0; view.addChild(child); position(child, px - x, py - y); }\n      putStateLast(view);\n      layer.addChildAt(view, start);\n      controls.push({ id: node.id, actionId: node.actionId, rect: bounds, kind: 'card-link', node, view });\n      return height;\n    }\n    const style = font({ fontSize: paint?.fontSize??(tiny ? 15 : 18), lineHeight: paint?.fontSize?paint.fontSize*1.4:tiny ? 21 : 25, fontWeight: '700', fill: ink, wordWrap: true, wordWrapWidth: width - padding * 2 });\n    const measured = new PIXI.Text({ text, style });\n    const contentHeight = measured.height; measured.destroy();\n    const height = Math.max(48, contentHeight + padding * 2);\n    const bounds = rect(x, y, width, height), view = interactive(node, bounds, { dark });\n    paintBackground(rect(0, 0, width, height), { ...paint, fill, border: 3 }, view);\n    label(text, padding, padding, width - padding * 2, style, view);\n    putStateLast(view);\n    controls.push({ id: node.id, actionId: node.actionId, rect: bounds, kind: node.metadata?.href ? 'link' : 'button', node, view });\n    return height;\n  }\n\n  function closeDropdown() {\n    openSelect = null; dropdownIndex = -1; dropdownBounds = null; dropdownField = null;\n    for (let i = controls.length - 1; i >= 0; i--) if (controls[i].kind === 'select-option') {controls[i].view?.destroy({children:true,context:true});controls.splice(i,1);}\n    invalidate();\n  }\n  function dropdown(node, bounds) {\n    if (openSelect === node.id) { closeDropdown(); return; }\n    closeDropdown(); openSelect = node.id;\n    const options = node.options ?? [];\n    const measuredOptions = options.map(option => {\n      const style = font({ fontSize: 16, lineHeight: 23, fontWeight: String(option.value) === String(node.value) ? '700' : '400', wordWrap: true, wordWrapWidth: bounds.width - 28 });\n      const probe = new PIXI.Text({ text: option.label, style });\n      const height = Math.max(48, probe.height + 24); probe.destroy(); return { style, height };\n    });\n    const totalHeight = measuredOptions.reduce((sum, option) => sum + option.height, 0);\n    const visibleTop = Number(lastOptions?.scrollY ?? 0) / (viewport.scale || 1), visibleBottom = visibleTop + viewport.designHeight;\n    let cursorY = bounds.y + bounds.height + 4;\n    if (cursorY + totalHeight > visibleBottom - 8) cursorY = bounds.y - totalHeight - 4;\n    cursorY = clamp(cursorY, visibleTop + 8, Math.max(visibleTop + 8, visibleBottom - totalHeight - 8));\n    dropdownBounds = rect(bounds.x, cursorY, bounds.width, totalHeight); dropdownField = bounds;\n    for (const [index, option] of options.entries()) {\n      const optionNode = { ...node, id: `${node.id}:option:${index}`, type: 'control', actionId: `${node.id}:option:${index}`, text: option.label, disabled: option.disabled };\n      const optionStyle = measuredOptions[index].style, h = measuredOptions[index].height;\n      const optionBounds = rect(bounds.x, cursorY, bounds.width, h);\n      const view = interactive(optionNode, optionBounds, { target: popup, activate: event => { closeDropdown(); onField(node, { value: option.value, kind: 'select', event }, bounds); } });\n      box(rect(0, 0, bounds.width, h), String(option.value) === String(node.value) ? P.yellow : P.paper, P.ink, 2, view);\n      label(option.label, 14, 12, bounds.width - 28, optionStyle, view); putStateLast(view);\n      controls.push({ id: optionNode.id, actionId: optionNode.actionId, rect: optionBounds, kind: 'select-option', node: optionNode, view });\n      cursorY += h;\n    }\n    dropdownIndex = Math.max(0, options.findIndex(option => String(option.value) === String(node.value)));\n    if (options[dropdownIndex]?.disabled) dropdownIndex = options.findIndex(option => !option.disabled);\n    if (dropdownIndex >= 0) focus(`${node.id}:option:${dropdownIndex}`);\n    invalidate();\n  }\n  function drawField(node, x, y, width) {\n    if (['checkbox', 'radio'].includes(node.fieldType)) {\n      const bounds = rect(x, y, width, 50), checked = Boolean(node.selected ?? node.metadata?.checked);\n      const view = interactive(node, bounds, { activate: event => onField(node, { kind: 'change', value: node.fieldType === 'radio' ? true : !checked, checked: node.fieldType === 'radio' ? true : !checked, event }, bounds) });\n      box(rect(0, 0, width, 50), P.paper, P.ink, 2, view);\n      box(rect(12, 11, 28, 28), checked ? P.ink : 0xffffff, P.ink, 2, view);\n      if (checked) label('✓', 16, 10, 25, { fill: P.paper, fontSize: 23, lineHeight: 30, wordWrap: false }, view);\n      label(checked ? 'On' : 'Off', 54, 13, width - 67, { fontSize: 17, lineHeight: 24, fontWeight: '700' }, view);\n      putStateLast(view);\n      fields.push({ id: node.id, rect: bounds, node, view });\n      controls.push({ id: node.id, actionId: node.actionId ?? node.id, rect: bounds, kind: node.fieldType, node, view });\n      return bounds.height;\n    }\n    if (node.fieldType === 'range') {\n      const bounds = rect(x, y, width, 56), minimum = Number(node.metadata?.min ?? 0), maximum = Number(node.metadata?.max ?? 100), step = Number(node.metadata?.step) || 1;\n      const value = clamp(Number(node.value) || minimum, minimum, maximum), trackWidth = Math.max(20, width - 100);\n      const view = interactive(node, bounds, { activate: event => { if (event?.getLocalPosition || event?.global) update(event); } });\n      box(rect(0, 0, width, 56), P.paper, P.ink, 2, view);\n      const track = graphic(view);\n      const knob = graphic(view);\n      const valueLabel = label(String(value), width - 68, 16, 58, { fontSize: 15, lineHeight: 24, fontWeight: '700', wordWrap: false }, view).view;\n      function paint(next) {\n        const fraction = (next - minimum) / Math.max(.000001, maximum - minimum), knobX = 20 + trackWidth * fraction;\n        track.clear().rect(20, 25, trackWidth, 6).fill(0xc7c6bc).rect(20, 25, trackWidth * fraction, 6).fill(P.ink);\n        knob.clear().circle(knobX, 28, 11).fill(P.yellow).stroke({ color: P.ink, width: 3 });\n        valueLabel.text = String(next);\n        invalidate();\n      }\n      function update(event) {\n        if (node.disabled || node.readOnly) return;\n        const local = event?.getLocalPosition?.(view) ?? (event?.global && view.toLocal?.(event.global));\n        if (!local) return;\n        const raw = minimum + clamp((local.x - 20) / trackWidth, 0, 1) * (maximum - minimum);\n        const next = Number(clamp(Math.round((raw - minimum) / step) * step + minimum, minimum, maximum).toFixed(8));\n        paint(next);\n        onField(node, { kind: 'change', value: String(next), event }, bounds);\n      }\n      view.on('pointerdown', event => { if (!node.disabled && !node.readOnly) { activeRangeId = node.id; update(event); } });\n      view.on('globalpointermove', event => { if (activeRangeId === node.id) update(event); });\n      const end = event => { if (activeRangeId === node.id) { update(event); activeRangeId = null; } };\n      view.on('pointerup', end); view.on('pointerupoutside', end);\n      view.sceneAdjust = direction => {\n        if (!node.disabled && !node.readOnly) onField(node, { kind: 'change', value: String(clamp(value + direction * step, minimum, maximum)) }, bounds);\n      };\n      paint(value); putStateLast(view);\n      fields.push({ id: node.id, rect: bounds, node, view });\n      controls.push({ id: node.id, actionId: node.actionId ?? node.id, rect: bounds, kind: 'range', node, view });\n      return bounds.height;\n    }\n    const multiline = node.fieldType === 'textarea';\n    const height = multiline ? Math.max(116, (Number(node.rows) || 4) * 23 + 28) : 54;\n    const bounds = rect(x, y, width, height), select = node.fieldType === 'select';\n    const view = interactive(node, bounds, { activate: event => {\n      if (select) { onField(node, { kind: 'focus', event }, bounds); dropdown(node, bounds); }\n      else onField(node, { kind: 'edit', event }, bounds);\n    } });\n    box(rect(0, 0, width, height), node.readOnly ? 0xf1eedf : 0xffffff, P.ink, 2, view);\n    let value = String(node.value ?? '');\n    if (select) value = node.options?.find(option => String(option.value) === value)?.label ?? value;\n    const fill = value ? P.ink : 0x687078;\n    const content = value || node.placeholder || (select ? 'Choose an option' : 'Enter text');\n    const textView = label(content, 14, 13, width - (select ? 51 : 28), { fontSize: 16, lineHeight: 23, fill, wordWrap: multiline, wordWrapWidth: width - 28 }, view).view;\n    const mask = box(rect(12, 10, width - (select ? 49 : 24), height - 20), 0xffffff, 0, 0, view);\n    textView.mask = mask;\n    if (select) label('⌄', width - 36, 9, 25, { fontSize: 25, lineHeight: 32, fontWeight: '700', wordWrap: false }, view);\n    putStateLast(view);\n    fields.push({ id: node.id, rect: bounds, node, view });\n    controls.push({ id: node.id, actionId: node.actionId ?? node.id, rect: bounds, kind: select ? 'select' : 'field', node, view });\n    return height;\n  }\n\n  function drawMedia(node, x, y, width, context = {}) {\n    const aspect = Number(node.aspect) || (node.mediaType === 'model' ? 1.25 : 1.6);\n    const height = node.mediaType==='experience'&&node.metadata?.height?clamp(node.metadata.height,200,1400):node.mediaType === 'audio' ? 72 : clamp(width / aspect, node.mediaType === 'youtube' ? Math.max(240,208/viewport.scale) : viewport.designWidth === 390 ? 240 : 310, 650);\n    const bounds = rect(x, y, width, height);\n    const view = interactive(node, bounds, { dark: true, activate: event => onMedia(node, event, bounds) });\n    box(rect(0, 0, width, height), 0x12171e, P.ink, 3, view);\n    putStateLast(view);\n    const interior = rect(x + 4, y + 4, width - 8, height - 8);\n    media.push({ id: node.id, rect: interior, node, view });\n    const captionHeight = label(node.alt || (node.mediaType === 'model' ? 'Interactive 3D artifact' : 'Interactive scene'), x + 4, y + height + 9, width - 8, { fill: isDark(context.ink ?? P.ink) ? 0x424b57 : P.muted, fontSize: 13, lineHeight: 19 }).height;\n    return height + captionHeight + 9;\n  }\n\n  function drawFlow(children, x, y, width, context = {}) {\n    let cursor = y;\n    const gap = context.gap ?? (viewport.designWidth === 390 ? 17 : 22);\n    for (const child of children) {\n      const h = drawNode(child, x, cursor, width, context);\n      if (h > 0) cursor += h + gap;\n    }\n    return Math.max(0, cursor - y - (cursor > y ? gap : 0));\n  }\n  function drawColumns(children, x, y, width, context = {}) {\n    const compact = viewport.designWidth === 390;\n    const columns = compact && !context.explicitColumns ? 1 : Math.max(1, Math.min(context.columns || (viewport.designWidth >= 1000 ? 3 : 2), children.length));\n    const gap = context.gap??(compact ? 18 : 24), cell = (width - gap * (columns - 1)) / columns;\n    let cursor = y;\n    for (let index = 0; index < children.length; index += columns) {\n      const row = children.slice(index, index + columns);\n      const heights = row.map((child, column) => drawNode(child, x + column * (cell + gap), cursor, cell, context));\n      cursor += Math.max(...heights, 0) + gap;\n    }\n    return Math.max(0, cursor - y - (cursor > y ? gap : 0));\n  }\n  function drawToolbar(children, x, y, width, context = {}) {\n    let cx = x, cy = y, lineHeight = 0;\n    const gap = 10;\n    for (const child of children) {\n      const text = plainSceneText(child) || visibleChildren(child).map(plainSceneText).join(' ');\n      const desired = child.type === 'control' ? measure(text, font({ fontSize: 15, fontWeight: '700' })) + 32 : width;\n      const w = Math.min(width, Math.max(84, desired));\n      if (cx > x && cx + w > x + width) { cy += lineHeight + gap; cx = x; lineHeight = 0; }\n      const h = drawNode(child, cx, cy, w, { ...context, compact: true });\n      cx += w + gap; lineHeight = Math.max(lineHeight, h);\n    }\n    return cy - y + lineHeight;\n  }\n\n  function drawNode(node,x,y,width,context={}){\n    const design=node?.metadata?.design;\n    if(design?.maxWidth&&width>design.maxWidth){const remaining=width-design.maxWidth;x+=design.alignment==='right'?remaining:design.alignment==='center'?remaining/2:0;width=design.maxWidth;}\n    const start=layer.children.length;\n    const height=Math.max(drawNodeContent(node,x,y,width,context),design?.minHeight??0);\n    const track=height>0&&(['heading','control','image'].includes(node?.type)||['header','nav'].includes(node?.role));\n    if(track){\n      const views=layer.children.slice(start),view=new PIXI.Container();view.label='comic-part:'+node.id;\n      for(const child of views){const px=child.x??0,py=child.y??0;view.addChild(child);position(child,px-x,py-y);}position(view,x,y);layer.addChildAt(view,start);\n      const key=['header','nav'].includes(node.role)?node.role+':'+(node.metadata?.layoutId??node.metadata?.classes?.join(' ')??'')+':'+plainSceneText(node):\n        node.type==='image'?'image:'+node.src:node.type==='control'?'control:'+(node.href??node.metadata?.href??'')+':'+plainSceneText(node):'text:'+plainSceneText(node);\n      parts.push({key,node,view,rect:rect(x,y,width,height)});\n    }\n    if(height>0&&node?.metadata?.layoutId)studioRegions.push({field:'design.regions.'+node.metadata.layoutId,rect:rect(x,y,width,height)});\n    if(height>0&&(node?.metadata?.classes??[]).some(name=>name==='blog-cover-art'||name==='cms-page-cover'))studioRegions.push({field:'cover',rect:rect(x,y,width,height)});\n    return height;\n  }\n\n  function drawNodeContent(node, x, y, width, context = {}) {\n    if (!node || node.metadata?.hidden) return 0;\n    if (node.metadata?.anchor) anchors.set(node.metadata.anchor, y);\n    const paint = nativePaintFor(node, lastDocument?.appearance);\n    const bodyStyle = { fill: paint?.ink ?? context.ink ?? P.ink, ...(paint?.fontSize??context.fontSize?{fontSize:paint?.fontSize??context.fontSize}:{}) };\n    const children = visibleChildren(node);\n    switch (node.type) {\n      case 'heading': {\n        const level = Number(node.level) || 2, compact = viewport.designWidth === 390;\n        const size = node.metadata?.design?.fontSize??context.fontSize??(level === 1 ? (compact ? 52 : 88) : level === 2 ? (compact ? 36 : 52) : (compact ? 27 : 34));\n        return drawRich(node, x, y, width, { ...bodyStyle, fontFamily: DISPLAY, fontSize: size, lineHeight: size * 1.08, fontWeight: '700' });\n      }\n      case 'text': {\n        if (!plainSceneText(node).trim() && !node.runs?.length) return 0;\n        const padding = paint?.surface ? paint.padding ?? 18 : 0, start = layer.children.length;\n        const height = drawRich(node, x + padding, y + padding, Math.max(1, width - padding * 2), { ...bodyStyle, ...(node.role === 'eyebrow' ? { fontSize: 13, lineHeight: 19, fontWeight: '700', letterSpacing: 1.1 } : {}) }) + padding * 2;\n        if (paint?.surface) layer.addChildAt(paintBackground(rect(x, y, width, height), { ...paint, border: 2 }, layer), start);\n        return height;\n      }\n      case 'control': return drawControl(node, x, y, width, context);\n      case 'field': return drawField(node, x, y, width);\n      case 'media': return drawMedia(node, x, y, width, context);\n      case 'image': {\n        const aspect = clamp(Number(node.aspect) || 1.5, .45, 4);\n        const height = node.metadata?.design?.minHeight??Math.min(width / aspect, viewport.designWidth === 390 ? 540 : 760);\n        image(node, x, y, width, height); return height;\n      }\n      case 'divider': box(rect(x, y + 6, width, 3), P.ink, 0, 0); return 16;\n      case 'code': {\n        const style = { fontFamily: 'monospace', fontSize: 14, lineHeight: 22, fill: P.paper };\n        const start = layer.children.length;\n        const height = drawRich(node, x + 16, y + 16, width - 32, style) + 32;\n        const background = box(rect(x, y, width, height), 0x141920, P.ink, 3);\n        layer.addChildAt(background, start); return height;\n      }\n      case 'list': {\n        let cursor = y;\n        children.forEach((child, index) => {\n          label(node.metadata?.ordered ? `${index + (Number(node.metadata?.start) || 1)}.` : '•', x, cursor, 25, bodyStyle);\n          const height = drawNode(child, x + 29, cursor, width - 29, context);\n          cursor += height + 12;\n        });\n        return Math.max(0, cursor - y - 12);\n      }\n      case 'table': {\n        const rows = node.rows ?? children.map(child => child.children ?? []);\n        const count = Math.max(1, ...rows.map(row => Array.isArray(row) ? row.length : 1));\n        const cellWidth = width / count;\n        let cursor = y;\n        const caption = children.find(child => child.type === 'text');\n        if (caption) cursor += drawRich(caption, x, cursor, width, { fontWeight: '700' }) + 13;\n        for (let i = 0; i < rows.length; i++) {\n          const cells = Array.isArray(rows[i]) ? rows[i] : [rows[i]], start = layer.children.length;\n          const heights = cells.map((cell, column) => {\n            const content = typeof cell === 'string' ? { type: 'text', text: cell } : cell;\n            return content.children?.length ? drawNode(content, x + column * cellWidth + 10, cursor + 12, cellWidth - 20, context) : drawRich(content, x + column * cellWidth + 10, cursor + 12, cellWidth - 20, { fontSize: viewport.designWidth === 390 ? 14 : 17, lineHeight: 23, fontWeight: i === 0 ? '700' : '400' });\n          });\n          const height = Math.max(48, ...heights.map(h => h + 24));\n          const background = box(rect(x, cursor, width, height), i === 0 ? P.yellow : i % 2 ? P.paper : 0xf0ecdf, P.ink, 1);\n          layer.addChildAt(background, start);\n          cursor += height;\n        }\n        return cursor - y;\n      }\n      default: {\n        if (!children.length) {\n          if (plainSceneText(node).trim()) return drawRich(node, x, y, width, bodyStyle);\n          if (!paint?.surface) return paint?.minHeight??0;\n          const height = paint.minHeight ?? 48;\n          paintBackground(rect(x, y, width, height), paint, layer); return height;\n        }\n        const role = node.role ?? '';\n        const panel = !paint?.canvas && (paint?.surface || ['card', 'hero', 'callout', 'form', 'footer', 'quote'].includes(role));\n        const padding = paint?.padding!==undefined ? Math.min(paint.padding,160) : panel ? Math.min(paint?.padding ?? (role === 'hero' ? 38 : 24), viewport.designWidth === 390 ? 20 : 60) : 0;\n        const contentWidth = Math.max(1, width - padding * 2), start = layer.children.length;\n        const fill = color(paint?.fill ?? node.tone, role === 'hero' ? P.yellow : role === 'footer' ? P.ink : P.paper), ink = paint?.ink ?? (isDark(fill) ? P.paper : P.ink);\n        const childContext = { ...context, ink: panel ? ink : context.ink, fill: undefined, ...(paint?.fontSize?{fontSize:paint.fontSize}:{}), ...(paint?.gap!==undefined?{gap:paint.gap}:{}) };\n        let height;\n        if (['nav', 'actions', 'header', 'toolbar'].includes(role)) height = drawToolbar(children, x + padding, y + padding, contentWidth, childContext);\n        else if (!paint?.stack && (paint?.columns || node.type === 'grid' || role === 'grid' || role === 'columns' || (role === 'hero' && children.length === 2 && viewport.designWidth !== 390))) height = drawColumns(children, x + padding, y + padding, contentWidth, { ...childContext, explicitColumns:Boolean(node.metadata?.design?.columns)&&!node.metadata?.classes?.includes('cms-feed-grid'), columns: paint?.columns ?? (role === 'hero' || role === 'columns' ? 2 : undefined) });\n        else height = drawFlow(children, x + padding, y + padding, contentWidth, childContext);\n        height = Math.max(height + padding * 2, paint?.minHeight ?? 0);\n        if (panel) layer.addChildAt(paintBackground(rect(x, y, width, height), { ...paint, fill, border: role === 'hero' ? 4 : 3 }, layer), start);\n        return height;\n      }\n    }\n  }\n\n  function atlasArtwork(src, frame, width, height, target) {\n    const url=safeSource(src),texture=url&&textures.get(url);\n    if(!texture){if(url&&!failures.has(url))loadTexture(url);return false;}\n    const sprite=new PIXI.Sprite(texture),sx=width/frame.width,sy=height/frame.height;\n    sprite.width=1024*sx;sprite.height=1536*sy;position(sprite,-frame.x*sx,-frame.y*sy);target.addChild(sprite);\n    return true;\n  }\n  function comicIndicator(symbol,x,y,width,ink,target) {\n    const g=graphic(target),h=width*.24;\n    if(symbol==='▶')g.poly([x,y-h,x+width*.6,y,x,y+h]).fill(ink);\n    else if(symbol==='↻')g.arc(x+width*.35,y,width*.23,-Math.PI*.8,Math.PI*.72).stroke({color:ink,width:Math.max(2,width*.07)}).poly([x+width*.1,y+width*.14,x+width*.31,y+width*.19,x+width*.22,y-width*.02]).fill(ink);\n    else g.poly([x,y+h*.12,x+width*.72,y-h*.2,x+width*.67,y-h,x+width,y-h*.06,x+width*.65,y+h,x+width*.71,y+h*.2,x,y+h*.4]).fill(ink);\n  }\n  function buildHome(document,size) {\n    const layout=homePanelLayout(size),compact=size.designWidth===390;\n    if(!document.sky?.enabled)box(rect(0,0,layout.width,layout.height),P.ink,0,0);\n    if(isComicAtlas(document.home.logo?.src)){\n      const header=new PIXI.Container();position(header,layout.logo.x,layout.logo.y);layer.addChild(header);\n      const frame=layout.illustrated?{x:20,y:0,width:984,height:114}:{x:275,y:0,width:520,height:92};\n      const headerPoints=[0,0,layout.logo.width,0,layout.logo.width,layout.logo.height*.69,layout.logo.width*.41,layout.logo.height*.98,0,layout.logo.height*.36];\n      const mask=layout.illustrated?graphic(header).poly(headerPoints).fill(0xffffff):box(rect(0,0,layout.logo.width,layout.logo.height),P.ink,0,0,header);\n      const art=new PIXI.Container();header.addChild(art);art.mask=mask;\n      atlasArtwork(document.home.logo.src,frame,layout.logo.width,layout.logo.height,art);\n    }else if(document.home.logo?.src){\n      const x=layout.logo.x,y=layout.logo.y,w=layout.logo.width,h=layout.logo.height;\n      const branded=String(document.home.logo.src).includes('b37d7cf27de3756f45ff6d7596735b6c4aa8f3510d4ebfe6d6f60919f5f59de9');\n      if(layout.illustrated&&!document.sky?.enabled)graphic().poly([x,y,x+w,y,x+w,y+h*.69,x+w*.41,y+h*.98,x,y+h*.36]).fill(P.ink);\n      if(branded){\n        const source=safeSource(document.home.logo.src),texture=textures.get(source);\n        if(!texture)loadTexture(source);else{\n          const holder=new PIXI.Container();layer.addChild(holder);\n          const frame={x:34,y:146,width:2111,height:420},scale=Math.min(w*.74/frame.width,h*.80/frame.height),sw=frame.width*scale,sh=frame.height*scale;\n          const sprite=new PIXI.Sprite(texture);sprite.width=2172*scale;sprite.height=724*scale;position(sprite,x+(w-sw)/2-frame.x*scale,y+(h-sh)*.30-frame.y*scale);holder.addChild(sprite);\n          const mask=box(rect(x,y,w,h),0xffffff,0,0);holder.mask=mask;\n        }\n        if(layout.illustrated){const lines=graphic();for(const [a,b,c,d]of [[.03,.17,.22,.53],[.05,.66,.23,.70],[.77,.31,.96,.07],[.80,.60,.98,.44]])lines.moveTo(x+w*a,y+h*b).lineTo(x+w*c,y+h*d).stroke({color:0xffffff,width:.8});}\n      }else if(layout.illustrated)image(document.home.logo,x+w*.10,y+h*.03,w*.80,h*.70,{background:null});else image(document.home.logo,x,y,w,h,{background:P.ink});\n    }\n    else label('TRAGIK MINDED',layout.logo.x,layout.logo.y,layout.logo.width,{fontFamily:'Tragik Bangers',fontSize:compact?30:39,lineHeight:compact?40:50,fill:P.paper});\n    if(document.home.notices?.length){\n      const y=layout.logo.y+layout.logo.height+7,start=layer.children.length;\n      const height=drawFlow(document.home.notices.map((notice,index)=>({id:`home-notice-${index}`,type:'text',text:notice.text})),16,y+8,size.designWidth-32,{ink:P.ink,gap:8})+16;\n      const notice=box(rect(8,y,size.designWidth-16,height),P.yellow,P.ink,2);layer.addChildAt(notice,start);\n      const firstTop=Math.min(...Object.values(layout.panels).map(panel=>panel.y)),shift=Math.max(0,y+height+12-firstTop);\n      if(shift){for(const panel of Object.values(layout.panels))panel.y+=shift;layout.footer.y+=shift;layout.height+=shift;}\n    }\n    const design=document.root?.metadata?.design??{},paperTop=layout.illustrated?0:Math.min(...Object.values(layout.panels).map(panel=>panel.y))-6,paperBottom=Math.max(...Object.values(layout.panels).map(panel=>panel.y+panel.height))+4;\n    const paper=paintBackground(rect(0,paperTop,layout.width,paperBottom-paperTop),{fill:color(design.background,0xffffff),pattern:'dots',patternColor:P.ink,patternAlpha:.055,...(design.backgroundImage?{image:design.backgroundImage,imageMode:'cover',imageAlpha:design.backgroundOpacity??1}:{})},layer);\n    if(layout.illustrated){\n      if(document.sky?.enabled){const h=layout.logo.height,w=layout.width,mask=graphic().poly([0,h*.36,w*.02,h*.36,w*.412,h*.98,w*.98,h*.69,w,h*.69,w,paperBottom,0,paperBottom]).fill(0xffffff);paper.mask=mask;}\n      layer.addChildAt(paper,document.sky?.enabled?0:1);\n    } // paper below the angled masthead, sky remains visible above it\n    const panelColors={blog:P.red,'tool-bench':P.orange,play:P.green,shop:P.yellow,studio:P.cyan,artifact:P.blue,contact:0xffffff};\n    for(const panel of document.home.panels??[]){\n      let bounds=layout.panels[panel.id];if(!bounds)continue;\n      const custom=panel.layout;if(custom?.hidden)continue;\n      if(custom&&['x','y','width','height'].some(key=>Number.isFinite(custom[key]))){const original=bounds;bounds={...bounds,x:(custom.x??bounds.x/size.designWidth*100)*size.designWidth/100,y:(custom.y??bounds.y/size.designHeight*100)*size.designHeight/100,width:(custom.width??bounds.width/size.designWidth*100)*size.designWidth/100,height:(custom.height??bounds.height/size.designHeight*100)*size.designHeight/100};if(original.points)bounds.points=original.points.map((value,i)=>value*(i%2?bounds.height/original.height:bounds.width/original.width));}\n      const points=homePanelPolygon(panel.id,bounds),fill=color(custom?.background??panel.color,panelColors[panel.id]??P.paper),ink=color(custom?.foreground,P.ink);\n      studioRegions.push({field:'design.regions.panel_'+panel.id,rect:bounds});\n      const view=interactive({...panel,type:'control'},bounds,{points});\n      const panelFill=graphic(view).poly(points).fill(fill);\n      const artwork=new PIXI.Container();view.addChild(artwork);\n      const mask=graphic(view).poly(points).fill(0xffffff);artwork.mask=mask;\n      if(panel.image){\n        if(isComicAtlas(panel.image)){const cleanFrames={blog:[36,119,950,201],play:[50,402,382,268],artifact:[552,374,440,320],shop:[25,747,970,145],'tool-bench':[25,945,970,130],studio:[25,1145,970,118],contact:[25,1327,970,166]};const c=cleanFrames[panel.id],frame=layout.illustrated?comicArtFrame(panel.id):{x:c[0],y:c[1],width:c[2],height:c[3]};atlasArtwork(panel.image,frame,bounds.width,bounds.height,artwork);}\n        else image({src:panel.image,alt:panel.alt},0,0,bounds.width,bounds.height,{fit:'cover',target:artwork,background:fill});\n      }else if(panel.id==='contact')paintBackground(rect(0,0,bounds.width,bounds.height),{fill,pattern:'dots',patternColor:P.ink,patternAlpha:.055},artwork);\n      const panelBorder=graphic(view).poly(points).stroke({color:P.ink,width:layout.illustrated?.8:1.5});\n      const spec=COMIC_LETTERING[panel.id],frame=comicArtFrame(panel.id),sx=bounds.width/frame.width,sy=bounds.height/frame.height;\n      const mobile=layout.illustrated,small=bounds.height<150;\n      let textX,textY,textWidth,textHeight,titleSize,eyebrowSize,arrow;\n      if(mobile){\n        textX=bounds.width*spec.x;textY=bounds.height*spec.y;textWidth=bounds.width*spec.width;textHeight=bounds.height*spec.height;\n        titleSize=spec.size*sx;eyebrowSize=Math.max(12,32*sx);arrow=spec.arrow;\n      }else{\n        const inset=small?14:24,y=panel.id==='blog'?bounds.height*.40:bounds.height*.30;\n        const span=homePanelLabelSpan(points,y,Math.min(bounds.height*.58,130),bounds.width,inset);\n        textX=span.x;textY=y;textWidth=span.width;textHeight=bounds.height-y-inset;\n        titleSize=Math.min(panel.id==='shop'?64:56,bounds.height*.24);eyebrowSize=small?12:16;\n      }\n      const lettering=new PIXI.Container();position(lettering,textX,textY);view.addChild(lettering);lettering.rotation=mobile?-.055:-.025;\n      const eyebrowHeight=eyebrowSize*1.35;\n      boundedLabel(String(panel.eyebrow??'').toUpperCase(),0,0,textWidth,eyebrowHeight,{fontFamily:'Tragik Bangers',fontSize:eyebrowSize,minFontSize:10,lineHeight:eyebrowHeight,fill:panel.id==='artifact'&&mobile?P.paper:ink},lettering);\n      const title=mobile&&['blog','play','artifact'].includes(panel.id)&&String(panel.title).toUpperCase()===spec.title.replace(/\\n/g,' ')?spec.title:String(panel.title??'').toUpperCase();\n      const titleHeight=Math.max(24,textHeight-eyebrowHeight-3);\n      const lines=title.split('\\n'),letterSize=Math.min(Math.max(21,titleSize),titleHeight/(lines.length*.96));\n      for(let i=0;i<lines.length;i++){\n        const titleLabel=label(lines[i],0,eyebrowHeight+3+i*letterSize*.93,textWidth,{fontFamily:'Tragik Bangers',fontSize:letterSize,lineHeight:letterSize*.93,fill:ink,wordWrap:false},lettering);\n        if(titleLabel.view.width>textWidth)titleLabel.view.scale.x=textWidth/titleLabel.view.width;\n      }\n      if(mobile&&arrow)comicIndicator(panel.symbol||'→',bounds.width*arrow[0],bounds.height*arrow[1],bounds.width*arrow[2],ink,view);\n      else if(!mobile)comicIndicator(panel.symbol||'→',bounds.width-Math.max(55,bounds.width*.17),bounds.height*.78,Math.max(32,bounds.width*.12),ink,view);\n      view.scenePanel={points:[...points],width:bounds.width,height:bounds.height,morph(next,p){\n        const shape=cleanScenePolygon(points.map((value,i)=>{const n=value/(i%2?bounds.height:bounds.width);return (n+(+(n>=.5)-n)*p)*(i%2?next.height:next.width);}));\n        position(view,next.x,next.y);panelFill.clear().poly(shape).fill(fill);mask.clear().poly(shape).fill(0xffffff);panelBorder.clear().poly(shape).stroke({color:P.ink,width:1});\n        const scale=Math.max(next.width/bounds.width,next.height/bounds.height);artwork.scale.set(scale);position(artwork,(next.width-bounds.width*scale)/2,(next.height-bounds.height*scale)/2);\n        position(lettering,textX+(24-textX)*p,textY+(40-textY)*p-p*p*next.height*.42);lettering.scale.set(1+p*.16);\n      }};\n      putStateLast(view);\n      controls.push({id:panel.id,actionId:panel.actionId,rect:bounds,kind:'home-panel',node:panel,view});homeViews.push({id:panel.id,view,rect:bounds});anchors.set(panel.id,bounds.y);\n    }\n    // The illustrated cover is the activation link; its destination retains the live Three viewer.\n    // A custom, unillustrated artifact can still show the live cover specimen.\n    const artifact=document.home.panels?.find(panel=>panel.id==='artifact');\n    if(!artifact?.image&&!artifact?.layout?.hidden&&layout.panels.artifact&&document.phase==='comic'){\n      const panel=layout.panels.artifact,available=panel.height*.25;\n      if(available>20)media.push({id:'home-specimen',rect:rect(panel.x+panel.width*.64,panel.y+8,panel.width*.28,available),node:{id:'home-specimen',type:'media',mediaType:'model',src:'/media/models/signal-relic-001.glb',alt:'Signal Relic 001',metadata:{rotate:true,decorative:true}}});\n    }\n    const footerStyle=document.home.footerLayout??{},footerArt=footerStyle.backgroundImage;\n    if(footerArt){\n      const url=safeSource(footerArt),texture=url&&textures.get(url),fh=layout.illustrated?18:22,fw=fh*2111/420,cx=(layout.width-fw)/2,y=layout.footer.y;\n      if(!texture){if(url)loadTexture(url);}else{\n        const sprite=new PIXI.Sprite(texture),branded=String(footerArt).includes('b37d7cf27de3756f45ff6d7596735b6c4aa8f3510d4ebfe6d6f60919f5f59de9');\n        const frame=branded?{x:34,y:146,width:2111,height:420}:{x:0,y:0,width:texture.width,height:texture.height},scale=Math.min(fw/frame.width,fh/frame.height);\n        sprite.width=texture.width*scale;sprite.height=texture.height*scale;position(sprite,cx+(fw-frame.width*scale)/2-frame.x*scale,y-frame.y*scale);layer.addChild(sprite);\n      }\n      label(document.home.footer||'Copyright © 2026. All Rights Reserved.',layout.footer.x,y+fh+4,layout.footer.width,{fontFamily:BODY,fontSize:footerStyle.fontSize??10,lineHeight:14,letterSpacing:.4,fill:color(footerStyle.foreground,P.paper),align:'center'});\n      layout.height=Math.max(layout.height,y+fh+23);\n    }else label(document.home.footer||'Copyright © 2026 Tragik Minded. All Rights Reserved.',layout.footer.x,layout.footer.y,layout.footer.width,{fontFamily:BODY,fontSize:compact?10.5:layout.illustrated?13:15,lineHeight:compact?15:21,letterSpacing:layout.illustrated?2:1,fill:P.paper,align:'center'});\n\n    return layout.height;\n  }\n\n  function build(document, size, options = {}) {\n    if (destroyed) throw new Error('Native scene has been destroyed');\n    generation++;parts=[];shopLayout=null;criticalSources.clear();\n    const previousSelect = openSelect, previousOption = dropdownIndex;\n    lastDocument = document; lastOptions = options;\n    viewport = size.designWidth ? size : logicalViewport(size.width, size.height, size.resolution);\n    clear(layer); clear(motion); clear(popup); openSelect = null; homeViews = [];\n    controls = []; fields = []; media = []; anchors = new Map();studioRegions=[];\n    let height;\n    if(document.shop){shopLayout=drawNativeShop(document,viewport,{PIXI,layer,popup,controls,fields,anchors,parts,box,label,boundedLabel,image,interactive,drawField,drawNode,paintBackground,scrollY:options.scrollY??0});height=shopLayout.height;}\n    else if (document.kind === 'home' && document.home && document.phase !== 'shop') height = buildHome(document, viewport);\n    else {\n      const pageWidth = viewport.designWidth, compact = pageWidth === 390, gutter = compact ? 17 : pageWidth === 768 ? 28 : 66;\n      const rootDesign=(document.root?.children??[]).find(node=>node.metadata?.tag==='main')?.metadata?.design;\n      const width = Math.min(pageWidth - gutter * 2,rootDesign?.maxWidth??Infinity),left=(pageWidth-width)/2;\n      const paint = nativePagePaint(document), start = layer.children.length;\n      height = drawNode(document.root ?? { type: 'stack', children: [] }, left, compact ? 24 : 38, width, { ink: paint.ink }) + (compact ? 56 : 90);\n      height = Math.max(height, viewport.designHeight || 0);\n      layer.addChildAt(paintBackground(rect(0, 0, pageWidth, height), paint, layer), start);\n      studioRegions.push({field:'background',rect:rect(0,0,pageWidth,height)});\n    }\n    const phase = document.kind === 'home' ? document.phase : null;\n    if (phase !== transitionPhase) { transitionPhase = phase; transitionStarted = globalThis.performance?.now() ?? Date.now(); }\n    transitionScene = null;\n    if (phase === 'snap') {\n      const shop = homeViews.find(view => view.id === 'shop');\n      const cover = graphic(motion);\n      const text = label('ENTER THE SHOP', 0, 0, viewport.designWidth - 48, { fontFamily: DISPLAY, fontSize: viewport.designWidth === 390 ? 44 : 78, lineHeight: viewport.designWidth === 390 ? 50 : 88, fontWeight: '700', fill: P.ink, align: 'center' }, motion).view;\n      transitionScene = { cover, text, shop: shop?.rect ?? rect(0, 0, viewport.designWidth, height), height };\n    }\n    current = { height, width: viewport.designWidth, controls, fields, media, anchors, studioRegions, parts, paintedElements: layer.children.length,\n      nativeElements: [], nativeFallbacks: [], focus, destroy, generation };\n    const retainedSelect = previousSelect && fields.find(field => field.id === previousSelect && !field.node.disabled);\n    if (retainedSelect) {\n      dropdown(retainedSelect.node, retainedSelect.rect);\n      if (retainedSelect.node.options?.[previousOption] && !retainedSelect.node.options[previousOption].disabled) {\n        dropdownIndex = previousOption; focus(`${retainedSelect.id}:option:${dropdownIndex}`);\n      }\n    }\n    studioSelect(studioField);return current;\n  }\n  function frame(){return {id:frameId,host:frameHost,layout:current,document:lastDocument,panels:homeViews,parts,shopLayout,generation};}\n  function restoreFrame(frame){\n    frameHost.destroy({children:true,context:true});frameHost=frame.host;frameId=frame.id;\n    [layer,motion,popup,studio]=frameHost.children;current=frame.layout;lastDocument=frame.document;homeViews=frame.panels;parts=frame.parts;shopLayout=frame.shopLayout;\n    controls=current.controls;fields=current.fields;media=current.media;anchors=current.anchors;studioRegions=current.studioRegions;\n    frameHost.visible=true;frameHost.eventMode='passive';\n  }\n  function retainFrame(){\n    closeDropdown();focus(null);const previous=frame();previous.host.eventMode='none';\n    createFrame();frameHost.visible=false;frameHost.eventMode='none';current=null;controls=[];fields=[];media=[];homeViews=[];parts=[];\n    return previous;\n  }\n  function studioSelect(field){\n    studioField=field;clear(studio);\n    const region=studioRegions.find(item=>item.field===field);if(!region)return;\n    const bounds=region.rect,ink=0x2dcced;\n    const marker=graphic(studio);marker.rect(bounds.x,bounds.y,bounds.width,bounds.height).stroke({color:ink,width:2});\n    for(const [x,y] of [[bounds.x,bounds.y],[bounds.x+bounds.width,bounds.y],[bounds.x,bounds.y+bounds.height],[bounds.x+bounds.width,bounds.y+bounds.height]])marker.rect(x-4,y-4,8,8).fill(ink).stroke({color:0x172124,width:1});\n    invalidate();\n  }\n  function focus(actionId) {\n    focused = actionId;\n    for (const control of controls) control.view?.sceneFocus?.();\n    return controls.find(control => control.actionId === actionId || control.id === actionId)?.rect;\n  }\n  function handleKey(actionId, key, event) {\n    const item = fields.find(field => (field.node.actionId ?? field.id) === actionId);\n    if (!item || item.node.fieldType !== 'select' || item.node.disabled) return false;\n    if (key === 'Tab') { closeDropdown(); return false; }\n    if (!['ArrowDown', 'ArrowUp', 'Enter', ' ', 'Escape', 'Home', 'End'].includes(key)) return false;\n    const { node, rect: bounds } = item;\n    if (key === 'Escape') { closeDropdown(); focus(actionId); return true; }\n    if (openSelect !== node.id) { dropdown(node, bounds); return true; }\n    if (key === 'Enter' || key === ' ') {\n      const option = controls.find(control => control.id === `${node.id}:option:${dropdownIndex}`);\n      option?.view.sceneActivate({ nativeEvent: event }); focus(actionId); return true;\n    }\n    const choices = (node.options ?? []).map((option, index) => ({ ...option, index })).filter(option => !option.disabled);\n    if (!choices.length) return true;\n    const selected = choices.findIndex(option => option.index === dropdownIndex);\n    const index = key === 'Home' ? 0 : key === 'End' ? choices.length - 1 : (selected + (key === 'ArrowUp' ? -1 : 1) + choices.length) % choices.length;\n    dropdownIndex = choices[index].index;\n    focus(`${node.id}:option:${dropdownIndex}`);\n    return true;\n  }\n  function pointerDown(point) {\n    if (!openSelect || !point) return false;\n    const contains = bounds => bounds && point.x >= bounds.x && point.y >= bounds.y && point.x <= bounds.x + bounds.width && point.y <= bounds.y + bounds.height;\n    if (contains(dropdownBounds) || contains(dropdownField)) return false;\n    closeDropdown(); return true;\n  }\n  function animate(now = globalThis.performance?.now() ?? Date.now(), reducedMotion = false) {\n    if (!['lock', 'snap'].includes(transitionPhase)) return false;\n    const duration = transitionPhase === 'lock' ? 120 : 1450;\n    const progress = reducedMotion ? 1 : clamp((now - transitionStarted) / duration, 0, 1);\n    const eased = 1 - (1 - progress) ** 3;\n    for (const entry of homeViews) {\n      const direction = entry.rect.x + entry.rect.width / 2 < viewport.designWidth / 2 ? -1 : 1;\n      const distance = transitionPhase === 'lock' ? 9 * eased : viewport.designWidth * .38 * eased;\n      position(entry.view, entry.rect.x + (entry.id === 'shop' ? 0 : direction * distance), entry.rect.y);\n      entry.view.alpha = transitionPhase === 'lock' ? .65 : (1 - eased) * .65;\n    }\n    if (transitionScene) {\n      const { shop, height, cover, text } = transitionScene;\n      const x = shop.x * (1 - eased), y = shop.y * (1 - eased);\n      const width = shop.width + (viewport.designWidth - shop.width) * eased, h = shop.height + (height - shop.height) * eased;\n      cover.clear().rect(x, y, width, h).fill(P.yellow).stroke({ color: P.ink, width: 5 });\n      position(text, x + (width - text.width) / 2, y + (h - text.height) / 2); text.alpha = clamp((eased - .15) / .5, 0, 1);\n    }\n    return !reducedMotion && progress < 1;\n  }\n  function destroy() {\n    destroyed = true;\n    clear(layer); clear(motion); clear(popup);clear(studio); layer.destroy(); motion.destroy(); popup.destroy();studio.destroy();frameHost.destroy();\n    textures.clear(); pending.clear(); failures.clear(); measurements.clear();\n    lastDocument = null; lastOptions = null; current = null;\n  }\n  return { build, focus, destroy, closeDropdown, handleKey, animate, pointerDown,studioSelect,retainFrame,restoreFrame,\n    get frame(){return frame();},\n    get hasPendingVisibleTextures(){return [...criticalSources].some(src=>pending.has(src));},\n    setScrollY(value) { lastOptions = { ...lastOptions, scrollY: Number(value) || 0 };shopLayout?.setScrollY(value); },\n    get layout() { return current; },\n    refresh() { return lastDocument ? build(lastDocument, viewport, lastOptions) : null; },\n    get hasPendingTextures() { return pending.size > 0; },\n    get textureState() { return {pending:pending.size,failed:failures.size,loaded:textures.size}; },\n  };\n}\n","runtime/native-shop.mjs":"const ink=0x111318,white=0xffffff,yellow=0xfffb8a,coral=0xff9296,cyan=0x88e3e7;\nconst themes={\n 'comic-catalog':{bg:0xfffdf4,panel:0xfffdf4,ink,line:2,accent:yellow,sub:cyan,display:'Tragik Bangers, Impact, sans-serif'},\n 'night-gallery':{bg:0x090e15,panel:0x161e29,ink:0xf0f2f4,line:1,accent:0xfff66d,sub:0x26313e,display:'Arial, sans-serif'},\n 'comic-spread':{bg:0xffffff,panel:coral,ink,line:3,accent:yellow,sub:cyan,display:'Tragik Bangers, Impact, sans-serif'},\n};\n/** Product content is exclusively the live Shopify DTO in the semantic app. */\nexport function drawNativeShop(document,size,api){\n const data=document.shop,t=themes[data.template]??themes['comic-spread'],compact=size.designWidth<620,tablet=size.designWidth<1080,w=size.designWidth,vh=size.designHeight,g=compact?12:24,headerHeight=compact?78:88;\n const {PIXI,layer,popup,controls,fields,anchors,parts,box,label,boundedLabel,image,interactive,drawField,drawNode,paintBackground}=api;\n const rect=(x,y,width,height)=>({x,y,width,height});\n const text=(s,x,y,width,fontSize=16,extra={},target=layer)=>{const {display,maxHeight,...style}=extra;return boundedLabel(String(s??''),x,y,Math.max(1,width),maxHeight??fontSize*2.7,{fontFamily:display?t.display:'Arial, sans-serif',fontSize,lineHeight:fontSize*1.25,fill:t.ink,...style},target);};\n function surface(x,y,width,height,fill=t.panel,line=t.line,target=layer){return box(rect(x,y,width,height),fill,t.ink,line,target);}\n function button(key,title,x,y,width,height=42,{fill=null,color=t.ink,display=false,target=layer,selected=false}={}){\n  const node=data.nodes[key];if(!node)return;\n  const view=interactive(node,rect(x,y,width,height),{target,dark:color!==ink});\n  if(fill!==null)box(rect(0,0,width,height),fill,t.ink,t.line,view);\n  if(selected){const mark=new PIXI.Graphics();mark.rect(2,height-4,width-4,3).fill(t.accent);view.addChild(mark);}\n  text(title,9,Math.max(6,(height-22)/2),width-18,display?22:14,{fill:color,display,fontWeight:'700',maxHeight:height-8},view);\n  if(view.sceneState){view.addChild(view.sceneState);}\n  const entry={id:'shop:'+key,actionId:node.actionId,kind:'shop-control',node,view,rect:rect(x,y,width,height)};controls.push(entry);return entry;\n }\n function photo(source,x,y,width,height,background=t.panel,target=layer){\n  if(source?.src||source?.image)image({src:source.src??source.image,alt:source.alt??source.title??''},x,y,width,height,{fit:'contain',background,target});\n  else{surface(x,y,width,height,background,0,target);text('Image not provided',x+12,y+height/2-12,width-24,14,{fill:t.ink,align:'center'});}\n }\n const nodeField=(key,x,y,width)=>{const node=data.nodes[key];if(node){drawField(node,x,y,width);const field=fields.at(-1);return field?.rect.height??88;}return 0;};\n const header=new PIXI.Container();header.label='shop-header';layer.addChild(header);\n box(rect(0,0,w,headerHeight),0x05080d,0,0,header);\n button('return','← Main comic',g,compact?40:24,compact?114:150,32,{color:white,target:header});\n const logoWidth=compact?188:Math.min(340,w*.34);photo({src:data.logo,alt:'Tragik Minded'},(w-logoWidth)/2,compact?3:12,logoWidth,compact?34:58,0x05080d,header);\n button('shop','Shop',compact?w/2-28:w-240,compact?40:24,68,32,{color:yellow,target:header,selected:true});\n button('bag',data.bagOpen?'Close bag ×':`Bag (${data.cart.count})`,w-g-(compact?110:142),compact?40:24,compact?110:142,32,{color:white,target:header});\n const headerControls=controls.slice();\n parts.push({key:'shop:header',node:{type:'stack',role:'header'},view:header,rect:rect(0,0,w,headerHeight)});\n let y=headerHeight+g,height=vh,sticky=null,stickyControls=[],bagHeight=0;\n if(data.zoom&&data.product){\n   const h=Math.max(300,vh-headerHeight-100);photo(data.product.images[data.imageIndex],g,y,w-2*g,h,t.bg);button('zoom','Close zoom ×',w-160,y+8,140,42,{fill:t.accent,color:ink});button('image-prev','←',g,y+h/2,48);button('image-next','→',w-g-48,y+h/2,48);text(`${data.imageIndex+1} / ${data.product.images.length}`,g,y+h+12,w-2*g,16,{align:'center'});height=y+h+65;\n }else{\n  if(data.template==='comic-catalog'){\n   surface(g,y,w-2*g,compact?76:100,t.accent);text('THE SHOP',g+18,y+8,w-2*g-36,compact?44:64,{display:true,fill:ink,maxHeight:80});y+=compact?90:116;\n  }\n  text('Search products',g,y,w*.56,12,{fontWeight:'700',maxHeight:18});text('Sort',g+(compact?w*.62:Math.min(500,w*.56))+10,y,w*.25,12,{fontWeight:'700',maxHeight:18});const filtersY=y+22;\n  const searchWidth=compact?w*.62:Math.min(500,w*.56);nodeField('query',g,filtersY,searchWidth-g);nodeField('sort',g+searchWidth+10,filtersY,w-g*2-searchWidth-10);y+=compact?124:130;\n  if(data.notice){surface(g,y,w-2*g,46,t.accent,1);text(data.notice,g+12,y+11,w-2*g-24,14,{fill:ink,maxHeight:30});y+=60;}\n  if(data.status==='loading'||data.status==='error'||!data.items.length){\n   const title=data.status==='loading'?'Loading products…':data.status==='error'?'Products are unavailable':data.query?'No products match your search.':'No products are currently published.';\n   surface(g,y,w-2*g,170,t.panel);text(title,g+22,y+24,w-2*g-44,compact?26:36,{display:true,maxHeight:90});if(data.status==='error')button('retry','Try again →',g+22,y+112,180,42,{fill:t.accent,color:ink});else if(data.query)button('clear-search','Clear search',g+22,y+112,180);y+=190;\n  }\n  function buy(x,top,width){\n   const p=data.product;if(!p)return top;\n   let by=top+18;const dark=data.template==='night-gallery',fill=data.template==='comic-spread'?t.accent:t.panel,buyInk=dark?t.ink:ink;\n   const initial=layer.children.length;\n   text(p.title,x+18,by,width-36,compact?27:31,{display:!dark,fill:buyInk,fontWeight:'700',maxHeight:100});by+=compact?80:92;\n   text(p.priceLabel,x+18,by,width-36,compact?28:36,{display:!dark,fill:buyInk,fontWeight:'700',maxHeight:52});by+=60;\n   anchors.set('shop-options',by-20);\n   for(const option of p.options){\n    text(option.name,x+18,by,width-36,13,{fill:buyInk,fontWeight:'700',maxHeight:20});by+=27;\n    let bx=x+18;const cell=Math.min(option.values.length===1?160:76,Math.max(48,(width-36-6*(option.values.length-1))/Math.min(5,option.values.length)));\n    for(const value of option.values){if(bx+cell>x+width-15){bx=x+18;by+=48;}const selected=data.selectedOptions[option.name]===value;button('option:'+option.name+':'+value,value,bx,by,cell,40,{fill:selected?dark?t.accent:ink:dark?0x242e3a:white,color:selected?dark?ink:white:buyInk,selected});bx+=cell+6;}\n    by+=57;\n   }\n   text('Quantity',x+18,by,width-36,13,{fill:buyInk,fontWeight:'700',maxHeight:20});by+=26;\n   button('quantity-minus','−',x+18,by,48,40,{fill:dark?0x242e3a:white,color:buyInk});text(data.quantity,x+75,by+8,38,16,{fill:buyInk,align:'center',maxHeight:24});button('quantity-plus','+',x+122,by,48,40,{fill:dark?0x242e3a:white,color:buyInk});by+=59;\n   if(!compact){button('add',p.canBuy?'ADD TO BAG →':p.available?'ADD TO BAG →':data.nodes.add?.disabled?'Unavailable':'Choose options →',x+18,by,width-36,56,{fill:data.template==='comic-spread'?coral:t.accent,color:ink,display:!dark});by+=74;}\n   button('details',data.details?'Product details −':'Product details +',x+18,by,width-36,42,{color:buyInk});by+=56;\n   const views=layer.children.slice(initial),background=surface(x,top,width,by-top,fill);layer.addChildAt(background,initial);\n   return by;\n  }\n  function gallery(x,top,width){\n   const p=data.product;if(!p)return top;\n   const h=compact?Math.min(450,width*1.15):Math.min(570,width*.84);surface(x,top,width,h,data.template==='comic-spread'?coral:data.template==='night-gallery'?0x30343a:white);\n   photo(p.images[data.imageIndex],x+12,top+12,width-24,h-24,data.template==='comic-spread'?coral:data.template==='night-gallery'?0x30343a:white);\n   button('image-prev','←',x+12,top+h/2-20,44,40,{fill:t.panel,color:t.ink});button('image-next','→',x+width-56,top+h/2-20,44,40,{fill:t.panel,color:t.ink});button('zoom','Zoom',x+width-90,top+h-50,72,36,{fill:t.panel});\n   const count=p.images.length,thumb=Math.min(compact?62:82,(width-8*Math.max(0,count-1))/Math.max(1,Math.min(count,6)));let tx=x,ty=top+h+10;\n   for(let i=0;i<count;i++){if(tx+thumb>x+width+1){tx=x;ty+=thumb+8;}const node=data.nodes['image:'+i];if(node){const view=interactive(node,rect(tx,ty,thumb,thumb));box(rect(0,0,thumb,thumb),t.panel,i===data.imageIndex?t.accent:t.ink,i===data.imageIndex?3:1,view);image({src:p.images[i].src,alt:p.images[i].alt},4,4,thumb-8,thumb-8,{fit:'contain',target:view,background:t.panel});controls.push({id:'shop:image:'+i,actionId:node.actionId,kind:'shop-control',node,view,rect:rect(tx,ty,thumb,thumb)});}tx+=thumb+8;}\n   return ty+(count?thumb:0)+10;\n  }\n  function rail(x,top,width,horizontal=false){\n   let py=top,px=x;const tileWidth=horizontal?Math.min(145,(w-2*g-10)/Math.max(1,Math.min(data.items.length,2))):width;\n   for(const item of data.items){const tileHeight=horizontal?140:Math.min(260,width*1.4),node=data.nodes['product:'+item.id];if(!node)continue;const view=interactive(node,rect(px,py,tileWidth,tileHeight));box(rect(0,0,tileWidth,tileHeight),data.template==='comic-spread'?t.sub:t.panel,t.ink,t.line,view);image({src:item.image,alt:item.alt},8,8,tileWidth-16,tileHeight-52,{fit:'contain',target:view,background:data.template==='comic-spread'?t.sub:t.panel});text(item.title,9,tileHeight-42,tileWidth-18,12,{fontWeight:'700',maxHeight:34},view);if(item.id===data.selectedProductId)box(rect(2,tileHeight-5,tileWidth-4,3),t.accent,0,0,view);controls.push({id:'shop:product:'+item.id,actionId:node.actionId,kind:'shop-control',node,view,rect:rect(px,py,tileWidth,tileHeight)});if(horizontal){px+=tileWidth+10;if(px+tileWidth>w-g){px=x;py+=tileHeight+10;}}else py+=tileHeight+12;}\n   return horizontal?py+140:py;\n  }\n  if(!data.bagOpen&&data.product){\n   if(compact){text(data.product.title,g,y,w-2*g,26,{display:data.template!=='night-gallery',fontWeight:'700',maxHeight:64});y+=76;if(data.items.length>1)y=rail(g,y,w-2*g,true)+14;y=gallery(g,y,w-2*g)+12;y=buy(g,y,w-2*g)+16;}\n   else if(data.template==='comic-spread'){\n    const railWidth=Math.max(145,w*.15),buyWidth=Math.min(350,w*.29),centerWidth=w-2*g-railWidth-buyWidth-28;\n    const bottomRail=rail(g,y,railWidth),bottomGallery=gallery(g+railWidth+14,y,centerWidth),bottomBuy=buy(w-g-buyWidth,y,buyWidth);y=Math.max(bottomRail,bottomGallery,bottomBuy)+24;\n   }else if(data.template==='comic-catalog'){\n    const buyWidth=Math.min(380,w*.34),catalogWidth=w-g*2-buyWidth-20,cardW=data.items.length===1?catalogWidth:Math.max(220,(catalogWidth-14)/2);let cy=y;\n    for(let i=0;i<data.items.length;i++){const item=data.items[i],cx=g+(i%2)*(cardW+14),row=Math.floor(i/2),ch=380,top=cy+row*(ch+16),node=data.nodes['product:'+item.id];if(!node)continue;const view=interactive(node,rect(cx,top,cardW,ch));box(rect(0,0,cardW,ch),white,ink,2,view);image({src:item.image,alt:item.alt},12,12,cardW-24,270,{fit:'contain',target:view,background:white});text(item.title,14,292,cardW-28,22,{display:true,fill:ink,maxHeight:55},view);const price=item.price?new Intl.NumberFormat('en-US',{style:'currency',currency:item.price.currencyCode}).format(Number(item.price.amount)):'';text(price,14,349,cardW-28,16,{fill:ink,maxHeight:24},view);controls.push({id:'shop:product:'+item.id,actionId:node.actionId,kind:'shop-control',node,view,rect:rect(cx,top,cardW,ch)});}\n    y=Math.max(cy+Math.ceil(data.items.length/2)*396,buy(w-g-buyWidth,cy,buyWidth))+16;\n    y=gallery(g,y,w-2*g)+16;\n   }else{const buyWidth=Math.min(380,w*.30),galleryWidth=w-2*g-buyWidth-24;y=Math.max(gallery(g,y,galleryWidth),buy(w-g-buyWidth,y,buyWidth))+24;if(data.items.length>1)y=rail(g,y,w-2*g,true)+20;}\n   if(data.details&&data.detailsNode){surface(g,y,w-2*g,44,t.sub);text('Product details',g+14,y+11,w-2*g-28,18,{fontWeight:'700'});y+=58;y+=drawNode(data.detailsNode,g+16,y,w-2*g-32,{ink:t.ink});y+=24;}\n  }\n  if(data.next){button('more','Load more products →',g,y,w-2*g,46,{fill:t.accent,color:ink});y+=62;}\n  height=Math.max(vh,y+70);\n  const integrated=data.template==='comic-spread';\n  if(integrated||data.bagOpen){\n   const bx=g,bw=w-2*g,by=data.bagOpen?headerHeight+g:y;\n   if(data.bagOpen)height=vh;\n   const bagStart=layer.children.length;let cy=by+20;text(`YOUR BAG (${data.cart.count})`,bx+16,cy,bw-32,integrated?30:23,{display:integrated,fontWeight:'700',maxHeight:42});cy+=52;\n   if(!data.cart.lines.length){text('Your bag is empty.',bx+16,cy,bw-32,16);cy+=50;}\n   for(const row of data.cart.lines){photo({src:row.image,alt:row.title},bx+14,cy,68,88,t.panel);text(row.title,bx+94,cy,bw-110,14,{fontWeight:'700',maxHeight:42});text(row.variant,bx+94,cy+44,bw-110,12,{maxHeight:30});cy+=96;button('bag-minus:'+row.id,'−',bx+16,cy,40,34,{fill:t.panel});text(row.quantity,bx+64,cy+7,32,14,{maxHeight:22});button('bag-plus:'+row.id,'+',bx+104,cy,40,34,{fill:t.panel});button('bag-remove:'+row.id,'Remove',bx+154,cy,bw-170,34);cy+=46;text(row.lineTotal,bx+16,cy,bw-32,17,{fontWeight:'700',maxHeight:26});if(!row.available)text('Unavailable',bx+16,cy+26,bw-32,13);cy+=50;}\n   text('Subtotal',bx+16,cy,bw*.45,16,{maxHeight:25});text(data.cart.subtotal,bx+bw*.46,cy,bw*.49-16,22,{fontWeight:'700',align:'right',maxHeight:30});cy+=46;text('Shipping and tax confirmed at checkout.',bx+16,cy,bw-32,12,{maxHeight:34});cy+=48;\n   button('checkout',data.busy?'Checking availability…':'CHECKOUT →',bx+16,cy,bw-32,54,{fill:integrated?coral:t.accent,color:ink,display:integrated});cy+=72;\n   bagHeight=cy-by;const background=surface(bx,by,bw,bagHeight,integrated?cyan:t.panel,t.line);layer.addChildAt(background,bagStart);\n   height=Math.max(height,cy+60);if(integrated)y=cy+20;\n  }\n  if(compact&&data.product&&!data.bagOpen){\n   sticky=new PIXI.Container();sticky.label='shop-purchase-bar';popup.addChild(sticky);box(rect(0,0,w,82),t.accent,ink,2,sticky);text(data.product.priceLabel,12,13,110,18,{fill:ink,fontWeight:'700',maxHeight:46},sticky);\n   const before=controls.length;button('add',data.product.canBuy?'ADD TO BAG →':data.nodes.add?.disabled?'Unavailable':'Choose options →',126,12,w-212,56,{fill:data.template==='comic-spread'?coral:t.accent,color:ink,display:true,target:sticky});button('bag',`Bag (${data.cart.count})`,w-80,18,72,46,{color:ink,target:sticky});stickyControls=controls.slice(before).map(entry=>({entry,base:entry.rect.y}));height+=82;\n  }\n }\n // Background belongs to the scene, so all three templates share the live app.\n if(data.template==='night-gallery'&&document.sky?.enabled){const tint=new PIXI.Graphics();tint.rect(0,0,w,height).fill({color:t.bg,alpha:.86});layer.addChildAt(tint,0);}else{const background=paintBackground(rect(0,0,w,height),{fill:t.bg,...(data.template!=='night-gallery'?{pattern:'dots',patternColor:ink,patternAlpha:.055}:{})},layer);layer.addChildAt(background,0);}\n button('contact','Contact',g,height-46,120,32,{color:t.ink});\n layer.addChild(header);\n function scroll(value){const top=value/size.scale;header.y=top;for(const entry of headerControls)entry.rect.y=(entry.view.y??entry.rect.y)+top;if(sticky){sticky.y=top+vh-82;for(const item of stickyControls)item.entry.rect.y=item.base+sticky.y;}}\n scroll(api.scrollY??0);\n return {height,setScrollY:scroll};\n}\n","runtime/page-transition.mjs":"import {cleanScenePolygon} from './scene-contract.mjs';\nconst clamp=(v,a=0,b=1)=>Math.min(b,Math.max(a,v));\nconst ease=t=>t*t*t*(t*(t*6-15)+10);\nconst mix=(a,b,t)=>a+(b-a)*t;\nconst bounds=points=>{const xs=points.filter((_,i)=>!(i%2)),ys=points.filter((_,i)=>i%2);return {x:Math.min(...xs),y:Math.min(...ys),width:Math.max(...xs)-Math.min(...xs),height:Math.max(...ys)-Math.min(...ys)};};\nfunction cutBelow(points,cut){\n  const out=[];\n  for(let i=0;i<points.length;i+=2){const j=(i+2)%points.length,a={x:points[i],y:points[i+1]},b={x:points[j],y:points[j+1]},inside=a.y>=cut,next=b.y>=cut;\n    if(inside)out.push(a.x,a.y);if(inside!==next){const t=(cut-a.y)/(b.y-a.y);out.push(mix(a.x,b.x,t),cut);}}\n  return cleanScenePolygon(out);\n}\nconst samePath=(href,route)=>{try{const p=new URL(href,location.href).pathname;return p!=='/'&&(p===route||route.startsWith(p+'/'));}catch{return false;}};\n\n/** Two live display trees, one renderer. No image capture or page-wide fade. */\nexport function createPageTransition({PIXI,native,stage,root,invalidate}){\n  let outgoing=null,incoming=null,viewport=null,prepared=false,committed=false,started=0,moving=0,progress=0,fromScroll=0,targetScroll=0,href='',direction=1,kind='travel',selected=null,mask=null,matches=[],deferred=null,queued=null;\n  const announcement=document.createElement('span');announcement.className='sr-only';announcement.setAttribute('role','status');root.appendChild(announcement);\n  function diagnostics(phase){root.dataset.worldTransition=phase;root.dataset.worldMotion=JSON.stringify({mode:'live-scene',kind,progress,retainedFrames:outgoing?2:1,captures:0,outgoingGeneration:outgoing?.generation??null,incomingGeneration:incoming?.generation??native.frame.generation});}\n  function release(frame){if(frame?.host&&!frame.host.destroyed)frame.host.destroy({children:true,context:true});}\n  function clearMask(){if(outgoing?.host)outgoing.host.mask=null;if(incoming?.host)incoming.host.mask=null;mask?.destroy();mask=null;}\n  function restoreParts(){for(const pair of matches){if(!pair.next.view.destroyed)pair.next.view.visible=true;}matches=[];}\n  function finish(){\n    if(!outgoing)return;\n    clearMask();restoreParts();\n    const target=incoming??native.frame;\n    target.host.visible=true;target.host.position.set(0,0);target.host.eventMode='passive';\n    for(const panel of target.panels??[])if(panel.view.sceneMotionBase){const p=panel.view.sceneMotionBase;panel.view.position.set(p.x,p.y);delete panel.view.sceneMotionBase;}\n    release(outgoing);outgoing=incoming=null;prepared=committed=false;moving=started=progress=0;selected=null;\n    if(target.layout&&viewport){root.style.height=`${Math.max(viewport.height,target.layout.height*viewport.scale)}px`;window.scrollTo({top:targetScroll,behavior:'instant'});}\n    root.removeAttribute('aria-busy');announcement.textContent=target.document?.title??'';diagnostics('idle');\n    const pending=queued,next=deferred;queued=deferred=null;\n    if(pending){start(pending.detail);if(pending.commit){commit(pending.commit);if(next)build(next.document,next.size,next.options);}}\n    else if(next)native.build(next.document,next.size,next.options);\n    invalidate();\n  }\n  function start(detail={},now=performance.now()){\n    if(!outgoing&&!native.frame.layout)return;\n    if(moving){queued={detail};invalidate();return;}\n    if(!outgoing){outgoing=native.retainFrame();fromScroll=window.scrollY;}\n    href=detail.href??location.href;direction=detail.direction<0?-1:1;started=now;committed=prepared=false;moving=progress=0;incoming=null;deferred=null;\n    selected=outgoing.panels?.find(p=>p.id===detail.controlId)??null;\n    root.setAttribute('aria-busy','true');announcement.textContent='Opening next panel';diagnostics('loading');invalidate();\n  }\n  function commit(detail={}){if(!outgoing)return;if(queued){queued.commit=detail;invalidate();return;}committed=true;targetScroll=Number.isFinite(detail.scrollY)?detail.scrollY:0;invalidate({layout:true});}\n  function build(document,size,options={}){\n    viewport=size;\n    const current=native.frame;\n    if(!outgoing&&current.document&&current.document.kind!==document.kind&&[current.document.kind,document.kind].every(k=>['home','shop'].includes(k))){start({controlId:'shop',direction:document.kind==='home'?-1:1});commit({scrollY:0});}\n    if(!outgoing)return native.build(document,size,options);\n    if(!committed)return outgoing.layout;\n    if(moving){deferred={document,size,options};return incoming.layout;}\n    const layout=native.build(document,size,{...options,scrollY:targetScroll});incoming=native.frame;\n    const hash=new URL(href,location.href).hash;\n    if(hash&&direction>0){let anchor;try{anchor=decodeURIComponent(hash.slice(1));}catch{}const y=layout.anchors.get(anchor);if(Number.isFinite(y))targetScroll=y*size.scale;}\n    incoming.host.position.set(0,(fromScroll-targetScroll)/size.scale);incoming.host.visible=false;incoming.host.eventMode='none';prepared=true;diagnostics('preparing');return layout;\n  }\n  function matchParts(){\n    const candidates=[],used=new Set();\n    for(const old of outgoing.parts??[]){const next=(incoming.parts??[]).find(p=>!used.has(p.view)&&p.key===old.key&&Math.abs(p.rect.width-old.rect.width)<3&&Math.abs(p.rect.height-old.rect.height)<3);if(next){used.add(next.view);candidates.push({old,next,base:{x:old.view.x,y:old.view.y}});}}\n    const contains=(parent,child)=>{for(let p=child.parent;p;p=p.parent)if(p===parent)return true;return false;};\n    matches=candidates.filter(pair=>!candidates.some(other=>other!==pair&&contains(other.old.view,pair.old.view)));\n    for(const pair of matches)pair.next.view.visible=false;\n  }\n  function beginMotion(now){\n    moving=now;incoming.host.visible=true;\n    if(outgoing.document.kind==='home'&&selected?.view.scenePanel)kind='enter-panel';\n    else if(incoming.document.kind==='home'){\n      selected=outgoing.document.kind==='shop'?incoming.panels.find(p=>p.id==='shop'):[...(incoming.panels??[])].sort((a,b)=>(b.view.scenePanel?.width??0)-(a.view.scenePanel?.width??0)).find(p=>samePath(incoming.document.home.panels.find(n=>n.id===p.id)?.href,outgoing.document.route));\n      if(!selected){const route=outgoing.document.route,id=/shop|editions/.test(route)?'shop':/contact/.test(route)?'contact':/studio/.test(route)?'studio':/artifacts|mesh/.test(route)?'artifact':/fps/.test(route)?'play':'blog';selected=incoming.panels.find(p=>p.id===id);}\n      kind=selected?'leave-panel':'travel';\n    }else kind='travel';\n    if(kind!=='travel'){mask=new PIXI.Graphics();mask.eventMode='none';stage.addChild(mask);if(kind==='enter-panel')incoming.host.mask=mask;else{stage.addChild(outgoing.host);outgoing.host.mask=mask;}}\n    if(kind==='travel')matchParts();\n    diagnostics('moving');\n  }\n  function panelPolygon(panel,rect,t){const shape=panel.view.scenePanel?.points??[0,0,panel.rect.width,0,panel.rect.width,panel.rect.height,0,panel.rect.height];return cleanScenePolygon(shape.map((v,i)=>{const n=v/(i%2?panel.rect.height:panel.rect.width);return (mix(n,+(n>=.5),t))*(i%2?rect.height:rect.width)+(i%2?rect.y:rect.x);}));}\n  function update(now,{reducedMotion=false}={}){\n    if(!outgoing){diagnostics('idle');return false;}\n    if(!committed){if(now-started>18000){cancel();return false;}return true;}\n    if(!prepared)return true;\n    if(!moving&&((native.hasPendingVisibleTextures||incoming?.document?.shop?.status==='loading')&&now-started<10000))return true;\n    if(reducedMotion){finish();return false;}\n    if(!moving)beginMotion(now);\n    const t=clamp((now-moving)/540),p=ease(t),w=viewport.designWidth,h=viewport.height/viewport.scale,offset=fromScroll/viewport.scale;\n    progress=t;\n    if(kind==='enter-panel'){\n      const original=selected.rect,rect={x:mix(original.x,0,p),y:mix(original.y,offset,p),width:mix(original.width,w,p),height:mix(original.height,h,p)};\n      selected.view.scenePanel.morph(rect,p);\n      for(const panel of outgoing.panels)if(panel!==selected){panel.view.position.set(panel.rect.x+(panel.rect.x+panel.rect.width/2<w/2?-1:1)*w*.75*p,panel.rect.y+(panel.rect.y<original.y?-1:1)*h*.18*p);}\n      incoming.host.y=(fromScroll-targetScroll)/viewport.scale+(1-p)*h*.12;\n      const polygon=panelPolygon(selected,rect,p),reveal=ease(clamp((t-.18)/.82)),cut=rect.y+rect.height*(1-reveal),clip=cutBelow(polygon,cut);\n      const windowBounds=clip.length>=6?bounds(clip):{x:0,y:0,width:0,height:0};incoming.host.visible=windowBounds.width*viewport.scale>=1&&windowBounds.height*viewport.scale>=1;\n      mask.clear();if(incoming.host.visible)mask.poly(clip).fill(0xffffff);incoming.clip=windowBounds;\n    }else if(kind==='leave-panel'){\n      const target={...selected.rect,y:selected.rect.y+(fromScroll-targetScroll)/viewport.scale};\n      const rect={x:mix(0,target.x,p),y:mix(offset,target.y,p),width:mix(w,target.width,p),height:mix(h,target.height,p)};\n      let polygon=panelPolygon(selected,rect,1-p);const closing=clamp((t-.76)/.24);\n      if(closing){const center=rect.y+rect.height/2;polygon=polygon.map((v,i)=>i%2?mix(v,center,ease(closing)):v);}\n      const clip=bounds(polygon);if(clip.height*viewport.scale<1){finish();return false;}\n      polygon=cleanScenePolygon(polygon);if(polygon.length<6){finish();return false;}mask.clear().poly(polygon).fill(0xffffff);outgoing.clip=clip;\n      for(const panel of incoming.panels){panel.view.sceneMotionBase={x:panel.rect.x,y:panel.rect.y};if(panel!==selected)panel.view.position.set(panel.rect.x+(panel.rect.x+panel.rect.width/2<w/2?-1:1)*w*.5*(1-p),panel.rect.y);}\n    }else{\n      outgoing.host.x=-direction*w*p;incoming.host.x=direction*w*(1-p);\n      for(const pair of matches){pair.old.view.x=pair.base.x+mix(0,pair.next.rect.x-pair.old.rect.x,p)-outgoing.host.x;pair.old.view.y=pair.base.y+(pair.next.rect.y-targetScroll/viewport.scale-(pair.old.rect.y-fromScroll/viewport.scale))*p;}\n    }\n    diagnostics('moving');if(t>=1){finish();return !!outgoing;}return true;\n  }\n  function media(){\n    const frames=outgoing?[outgoing,...(incoming?.host.visible?[incoming]:[])]:[native.frame];\n    return frames.flatMap(frame=>(frame.layout?.media??[]).map(item=>({...item,renderKey:frame.id+':'+(item.node.metadata?.id??item.id),interactive:!outgoing,rect:{...item.rect,x:item.rect.x+frame.host.x,y:item.rect.y+frame.host.y},clip:frame.clip})));\n  }\n  function cancel(){queued=null;if(!outgoing)return;if(incoming){finish();return;}clearMask();restoreParts();native.restoreFrame(outgoing);outgoing=null;prepared=committed=false;started=moving=progress=0;root.removeAttribute('aria-busy');diagnostics('idle');invalidate({layout:true});}\n  return {start,commit,build,update,media,cancel,get active(){return !!outgoing;},get moving(){return !!moving;},get preparingScrollY(){return outgoing?targetScroll:window.scrollY;},get displayScrollY(){return outgoing?fromScroll:window.scrollY;},get displayHeight(){return (outgoing?.layout??native.layout)?.height??0;},get showSky(){return [outgoing?.document??native.frame.document,incoming?.document].some(d=>d?.kind==='home'||d?.shop?.template==='night-gallery');},get deferLayout(){return !!moving;},destroy(){clearMask();restoreParts();release(outgoing);outgoing=null;announcement.remove();}};\n}\n","runtime/runtime-settings.mjs":"/** Runtime controls remain bounded even if an older saved manifest is supplied. */\nexport function runtimeSettings(input={}) {\n  const bounded=(value,fallback,min,max)=>typeof value==='number'&&Number.isFinite(value)?Math.max(min,Math.min(max,value)):fallback;\n  return {resolutionLimit:bounded(input.resolutionLimit,2,1,2),animationFps:bounded(input.animationFps,30,15,60)};\n}\n","runtime/scene-contract.mjs":"/**\n * Native page-scene contract. Semantic markup supplies content and application\n * actions only. Never derive visible geometry/style from DOM layout or CSS.\n *\n * Document: {route,title,kind,phase,root,home?,sources:Map,actions:Map}\n * Node: {id,type,role?,tone?,children?,text?,runs?,level?,src?,alt?,aspect?,\n *        actionId?,disabled?,selected?,fieldType?,value?,placeholder?,readOnly?,\n *        options?:[{value,label,disabled?}],rows?,mediaType?,metadata?}\n * Types: stack,grid,text,heading,image,control,field,media,divider,code,list,table.\n * Runs: {text,actionId?,strong?,emphasis?,code?}. Group role/tone are explicit\n * semantic design tokens; they are not computed CSS or DOM dimensions.\n *\n * Layout: {height,controls:[{id,actionId,rect,kind,node,view?}],\n *          fields:[{id,rect,node}],media:[{id,rect,node}],anchors:Map,\n *          focus(actionId),destroy(),...}. All rects are in logical scene pixels.\n */\nexport const SCENE_TYPES = Object.freeze([\n  \"stack\", \"grid\", \"text\", \"heading\", \"image\", \"control\", \"field\", \"media\",\n  \"divider\", \"code\", \"list\", \"table\",\n]);\n\nexport const SCENE_PALETTE = Object.freeze({\n  ink: 0x080a0d, paper: 0xfffdf5, muted: 0xc9d0da,\n  red: 0xff8f8f, orange: 0xffd197, yellow: 0xffff91,\n  green: 0x9df794, cyan: 0x8deff0, blue: 0x7599ed,\n  violet: 0xb791e8,\n});\n\nexport function logicalViewport(width, height, pixelRatio = 1) {\n  const w = Math.max(1, Number(width) || 1);\n  const h = Math.max(1, Number(height) || 1);\n  const designWidth = w >= 1080 ? 1280 : w >= 620 || (w >= 480 && w / h >= 1.25) ? 768 : 390;\n  const scale = w / designWidth;\n  return {width:w,height:h,designWidth,designHeight:h/scale,scale,\n    resolution:Math.max(1,Math.min(2,Number(pixelRatio)||1))};\n}\n\nexport function plainSceneText(node) {\n  return String(node?.text ?? node?.runs?.map(run=>run.text).join(\"\") ?? \"\");\n}\n\n/** Keep CMS renditions origin-clean for GPU textures, including scoped previews. */\nexport function sceneAssetUrl(value, base = globalThis.location?.href ?? \"https://tragikminded.com/\") {\n  try {\n    const url = new URL(String(value), base);\n    const match = url.pathname.match(/^\\/cmsDelivery\\/delivery\\/media\\/([a-f0-9]{64})$/);\n    if (url.origin === \"https://us-central1-neoknown-server.cloudfunctions.net\" && match) {\n      const preview = url.searchParams.get(\"preview\");\n      const format=url.searchParams.get(\"format\");\n      if(format&&![\"webp\",\"glb\",\"mp4\",\"webm\",\"mp3\",\"m4a\",\"ogg\"].includes(format))return \"\";\n      const query=new URLSearchParams();if(preview)query.set(\"preview\",preview);if(format)query.set(\"format\",format);\n      return `/__cms/media/${match[1]}${query.size?`?${query}`:\"\"}`;\n    }\n    return url.href;\n  } catch { return \"\"; }\n}\n\n/** Fragment IDs are URL-encoded input, including links received from outside. */\nexport function sceneAnchorOffset(hash, anchors) {\n  try { return anchors?.get(decodeURIComponent(String(hash).replace(/^#/, \"\"))); }\n  catch { return undefined; }\n}\n\n/** Stencil paths must have real area and no zero-length/collinear edges. */\nexport function cleanScenePolygon(points,epsilon=.02){\n  const vertices=[];\n  for(let i=0;i<points.length;i+=2){const p={x:points[i],y:points[i+1]},last=vertices.at(-1);if(!Number.isFinite(p.x)||!Number.isFinite(p.y))return [];if(!last||Math.hypot(p.x-last.x,p.y-last.y)>epsilon)vertices.push(p);}\n  if(vertices.length>1&&Math.hypot(vertices[0].x-vertices.at(-1).x,vertices[0].y-vertices.at(-1).y)<=epsilon)vertices.pop();\n  let changed=true;\n  while(changed&&vertices.length>3){changed=false;for(let i=0;i<vertices.length;i++){const a=vertices[(i+vertices.length-1)%vertices.length],b=vertices[i],c=vertices[(i+1)%vertices.length],cross=(b.x-a.x)*(c.y-b.y)-(b.y-a.y)*(c.x-b.x),length=Math.hypot(b.x-a.x,b.y-a.y)+Math.hypot(c.x-b.x,c.y-b.y);if(Math.abs(cross)<=epsilon*length){vertices.splice(i,1);changed=true;break;}}}\n  const area=vertices.reduce((sum,p,i)=>{const q=vertices[(i+1)%vertices.length];return sum+p.x*q.y-q.x*p.y;},0);\n  return vertices.length>=3&&Math.abs(area)>epsilon*epsilon?vertices.flatMap(p=>[p.x,p.y]):[];\n}\n","runtime/scene-document.mjs":"/**\n * Reads the React/CMS document as semantic data. This module deliberately has no\n * dependency on the browser's layout engine: Pixi owns every visible rectangle,\n * line break, hit target and responsive decision.\n */\nimport { nativePaintFor } from './native-paint.mjs';\n\nconst OMIT = new Set(['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEMPLATE', 'SVG', 'LINK', 'META', 'SOURCE', 'TRACK']);\nconst INLINE = new Set(['A', 'ABBR', 'B', 'BDI', 'BDO', 'BR', 'CITE', 'CODE', 'DEL', 'EM', 'I', 'KBD', 'MARK', 'Q', 'S', 'SAMP', 'SMALL', 'SPAN', 'STRONG', 'SUB', 'SUP', 'TIME', 'U', 'VAR', 'WBR']);\nconst COPY = new Set(['P', 'DT', 'DD', 'FIGCAPTION', 'BLOCKQUOTE', 'ADDRESS', 'LEGEND']);\nconst MEDIA = new Set(['IFRAME', 'VIDEO', 'AUDIO', 'MODEL-VIEWER', 'CANVAS', 'OBJECT', 'EMBED']);\nconst GRID_CLASSES = new Set(['collection-grid', 'blog-posts', 'blog-grid', 'studio-capabilities', 'artifact-story-grid', 'edition-grid', 'shop-standards', 'project-router__choices', 'field-note-evidence', 'edition-spec-grid', 'recovery-stack-visual', 'legacy-launch__controls']);\nconst COLUMN_CLASSES = new Set(['contact-shell', 'artifact-hero', 'mesh-preview-hero', 'studio-hero', 'field-note-hero', 'edition-hero', 'drop-feature', 'field-note-chapter', 'legacy-launch__grid']);\nconst ACTION_CLASSES = new Set(['storefront-actions', 'drop-feature__actions', 'contact-brief-actions', 'project-router__action', 'artifact-commerce-actions', 'artifact-viewer-tools', 'mesh-preview-tools', 'field-note-actions', 'edition-actions', 'edition-hero__actions', 'comic-trail__links']);\nconst CARD_CLASSES = new Set(['studio-capability', 'artifact-story-card', 'blog-post', 'edition-card', 'project-router__result', 'contact-form-panel', 'archive-panel', 'artifact-commerce-panel', 'artifact-spec-sheet', 'shop-access-note', 'recovery-stack-visual__panel']);\nconst normalize = value => String(value ?? '').replace(/\\s+/gu, ' ').trim();\nconst attribute = (element, name) => element?.getAttribute?.(name) ?? null;\nconst has = (element, name) => element?.hasAttribute?.(name) ?? false;\nconst tag = element => String(element?.tagName ?? '').toUpperCase();\nconst nodes = element => Array.from(element?.childNodes ?? []);\nconst elements = element => nodes(element).filter(child => child.nodeType === 1);\nconst classes = element => new Set(String(attribute(element, 'class') ?? '').split(/\\s+/u).filter(Boolean));\nconst hasClass = (element, name) => classes(element).has(name);\nfunction descendants(element) { return [element, ...elements(element).flatMap(descendants)]; }\nfunction numeric(value, fallback) { const number = Number(value); return Number.isFinite(number) && number > 0 ? number : fallback; }\nfunction isHidden(element) {\n  return OMIT.has(tag(element)) || has(element, 'hidden') || has(element, 'inert') ||\n    attribute(element, 'aria-hidden') === 'true' || attribute(element, 'data-cms-hidden') === 'true' || has(element, 'data-world-fallback') ||\n    hasClass(element, 'sr-only') || hasClass(element, 'visually-hidden') ||\n    (tag(element) === 'INPUT' && attribute(element, 'type') === 'hidden');\n}\nfunction explicitRole(element) {\n  if (attribute(element, 'data-scene-role')) return attribute(element, 'data-scene-role');\n  const tokens = classes(element);\n  if ([...tokens].some(token => COLUMN_CLASSES.has(token))) return 'columns';\n  if ([...tokens].some(token => GRID_CLASSES.has(token))) return 'grid';\n  if ([...tokens].some(token => ACTION_CLASSES.has(token) || token.endsWith('__actions'))) return 'actions';\n  if ([...tokens].some(token => CARD_CLASSES.has(token))) return 'card';\n  if ([...tokens].some(token => token.endsWith('-hero'))) return 'hero';\n  return ({MAIN:'main', HEADER:'header', NAV:'nav', FOOTER:'footer', ARTICLE:'content', SECTION:'section', ASIDE:'aside', FORM:'form', FIGURE:'figure', LABEL:'label', LI:'list-item', DL:'definitions'})[tag(element)] ?? 'content';\n}\nfunction explicitTone(element) {\n  const explicit = attribute(element, 'data-world-color') ?? attribute(element, 'data-scene-tone');\n  if (explicit) return explicit;\n  for (const token of classes(element)) {\n    const match = token.match(/--(red|orange|yellow|green|cyan|blue|violet|white|paper|ink)$/u);\n    if (match) return match[1] === 'white' ? 'paper' : match[1];\n  }\n  if (hasClass(element, 'contact-intro')) return 'cyan';\n  if (hasClass(element, 'shop-access-note')) return 'yellow';\n  return undefined;\n}\n\nexport function createSceneDocumentReader(source, { pathname } = {}) {\n  if (!source) throw new TypeError('A semantic source is required.');\n  let sequence = 0;\n  let identities = new WeakMap();\n  let destroyed = false;\n  let sources = new Map(), actions = new Map(), lookup = new Map(), elementList = [];\n  function identity(element) {\n    if (!identities.has(element)) identities.set(element, `scene-${++sequence}`);\n    const id = identities.get(element);\n    sources.set(id, element.nodeType === 1 ? element : element.parentElement ?? element.parentNode ?? source);\n    return id;\n  }\n  function action(element) {\n    const id = `action-${identity(element)}`;\n    actions.set(id, element);\n    return id;\n  }\n  function semanticText(element, { includeHidden = false } = {}) {\n    if (element?.nodeType === 3) return String(element.data ?? element.textContent ?? '');\n    if (!includeHidden && isHidden(element)) return '';\n    if (tag(element) === 'BR') return '\\n';\n    if (['INPUT', 'SELECT', 'TEXTAREA'].includes(tag(element))) return '';\n    return nodes(element).map(child => semanticText(child, {includeHidden})).join('');\n  }\n  function labelFor(element) {\n    const aria = attribute(element, 'aria-label');\n    if (aria) return normalize(aria);\n    const labelled = String(attribute(element, 'aria-labelledby') ?? '').split(/\\s+/u).map(id => lookup.get(id)).filter(Boolean);\n    if (labelled.length) return normalize(labelled.map(label => semanticText(label, {includeHidden:true})).join(' '));\n    const id = attribute(element, 'id');\n    const external = id ? elementList.find(label => tag(label) === 'LABEL' && attribute(label, 'for') === id) : null;\n    if (external) return normalize(semanticText(external));\n    let parent = element.parentElement ?? element.parentNode;\n    while (parent && parent !== source) {\n      if (tag(parent) === 'LABEL') return normalize(nodes(parent).filter(child => !['INPUT','SELECT','TEXTAREA'].includes(tag(child))).map(child => semanticText(child)).join(' '));\n      parent = parent.parentElement ?? parent.parentNode;\n    }\n    return normalize(attribute(element, 'title') ?? '');\n  }\n  function metadata(element, extra = {}) {\n    const parent = element.parentElement ?? element.parentNode;\n    const ancestorClasses = [];\n    let ancestor = parent;\n    while (ancestor) {\n      ancestorClasses.push(...classes(ancestor));\n      if (ancestor === source) break;\n      ancestor = ancestor.parentElement ?? ancestor.parentNode;\n    }\n    const result = {tag:tag(element).toLowerCase(), classes:[...classes(element)], parentClasses:[...classes(parent)],\n      ancestorClasses:[...new Set(ancestorClasses)], siblingIndex:parent ? elements(parent).indexOf(element) : 0, ...extra};\n    const names = {'data-cms-node':'layoutId',id:'anchor', 'aria-label':'label', 'aria-live':'live', role:'ariaRole', href:'href', target:'target', rel:'rel', title:'title', 'data-scene-media-target':'mediaTarget', 'data-scene-command':'command', 'data-scene-args':'args'};\n    for (const [name, key] of Object.entries(names)) if (attribute(element, name) !== null) result[key] = attribute(element, name);\n    result.designRoot=attribute(element,'data-cms-layout-root')==='true';\n    try { result.design=JSON.parse(attribute(element, \"data-cms-layout\") ?? \"null\"); } catch {}\n    return result;\n  }\n  function basic(element, type, extra = {}) {\n    return {id:identity(element), type, role:explicitRole(element), tone:explicitTone(element), metadata:metadata(element), ...extra};\n  }\n  function inlineRuns(element, inherited = {}) {\n    if (element.nodeType === 3) return [{text:String(element.data ?? element.textContent ?? '').replace(/[\\t\\r\\n ]+/gu, ' '), ...inherited}];\n    if (isHidden(element)) return [];\n    if (tag(element) === 'BR') return [{text:'\\n', ...inherited}];\n    const flags = {...inherited};\n    if (tag(element) === 'STRONG' || tag(element) === 'B') flags.strong = true;\n    if (tag(element) === 'EM' || tag(element) === 'I' || tag(element) === 'CITE') flags.emphasis = true;\n    if (['CODE','KBD','SAMP','VAR'].includes(tag(element))) flags.code = true;\n    if (tag(element) === 'A' && has(element, 'href')) flags.actionId = action(element);\n    return nodes(element).flatMap(child => inlineRuns(child, flags));\n  }\n  function copy(element, type = 'text', extra = {}) {\n    const runs = inlineRuns(element);\n    if (runs.length) {\n      runs[0].text = runs[0].text.trimStart();\n      runs.at(-1).text = runs.at(-1).text.trimEnd();\n    }\n    return basic(element, type, {runs, text:runs.map(run => run.text).join(''), ...extra});\n  }\n  function parseChildren(element, omit = null) {\n    const result = [];\n    let pending = [];\n    function flush() {\n      if (!pending.length) return;\n      const text = pending.map(child => String(child.data ?? child.textContent ?? '')).join('').replace(/\\s+/gu, ' ').trim();\n      if (text) result.push({id:identity(pending[0]), type:'text', text, runs:[{text}], metadata:{tag:'text'}});\n      pending = [];\n    }\n    for (const child of nodes(element).sort((a,b)=>Number(attribute(a,\"data-cms-order\")??0)-Number(attribute(b,\"data-cms-order\")??0))) {\n      if (omit?.has(child)) continue;\n      if (child.nodeType === 3) { pending.push(child); continue; }\n      if (child.nodeType !== 1) continue;\n      flush();\n      const parsed = parse(child);\n      if (parsed) result.push(parsed);\n    }\n    flush();\n    return result;\n  }\n  function parseMedia(element) {\n    const mediaType = attribute(element, 'data-scene-media') ?? ({'MODEL-VIEWER':'model', IFRAME:'iframe', VIDEO:'video', AUDIO:'audio', CANVAS:'canvas', OBJECT:'object', EMBED:'embed'})[tag(element)];\n    let hotspots = [];\n    try { const value = JSON.parse(attribute(element, 'data-scene-hotspots') ?? '[]'); if (Array.isArray(value)) hotspots = value; } catch { /* Invalid optional metadata cannot discard the page. */ }\n    const src = attribute(element, 'data-scene-src') ?? attribute(element, 'src') ?? attribute(element, 'data') ?? '';\n    const alt = attribute(element, 'data-scene-alt') ?? attribute(element, 'alt') ?? attribute(element, 'title') ?? labelFor(element);\n    return basic(element, 'media', {\n      mediaType, src, alt, aspect:numeric(attribute(element, 'data-scene-aspect'), mediaType === 'model' ? 1 : 16/9),\n      metadata:metadata(element, {\n        id:attribute(element, 'data-scene-id') ?? attribute(element, 'id') ?? identity(element),\n        hotspots, rotate:attribute(element, 'data-scene-rotate') !== 'false',\n        height:Number(attribute(element,'data-scene-height')??0),poster:attribute(element, 'data-scene-poster') ?? attribute(element, 'poster') ?? '',\n        allow:attribute(element, 'allow') ?? '', controls:has(element, 'controls'),\n        autoplay:has(element, 'autoplay'), muted:has(element, 'muted'), loop:has(element, 'loop'),\n      }),\n    });\n  }\n  function parseField(element) {\n    const type = tag(element) === 'SELECT' ? 'select' : tag(element) === 'TEXTAREA' ? 'textarea' : attribute(element, 'type') ?? 'text';\n    const options = tag(element) === 'SELECT' ? descendants(element).filter(child => tag(child) === 'OPTION').map(option => ({\n      value:String(option.value ?? attribute(option, 'value') ?? normalize(semanticText(option))),\n      label:normalize(attribute(option, 'label') ?? semanticText(option)),\n      disabled:Boolean(option.disabled || has(option, 'disabled') || has(option.parentElement ?? option.parentNode, 'disabled')),\n    })) : undefined;\n    const value = element.value ?? attribute(element, 'value') ?? (tag(element) === 'TEXTAREA' ? semanticText(element) : '');\n    const described = String(attribute(element, 'aria-describedby') ?? '').split(/\\s+/u).map(id => lookup.get(id)).filter(Boolean);\n    return basic(element, 'field', {\n      fieldType:type, actionId:action(element), value:String(value), placeholder:attribute(element, 'placeholder') ?? '',\n      readOnly:Boolean(element.readOnly || has(element, 'readonly')), disabled:Boolean(element.disabled || has(element, 'disabled')),\n      selected:typeof element.checked === 'boolean' ? element.checked : has(element, 'checked'), options, rows:numeric(element.rows ?? attribute(element, 'rows'), type === 'textarea' ? 4 : 1),\n      metadata:metadata(element, {label:labelFor(element), name:attribute(element, 'name') ?? '', required:Boolean(element.required || has(element, 'required')),\n        maxLength:numeric(attribute(element, 'maxlength'), undefined), min:attribute(element, 'min'), max:attribute(element, 'max'), step:attribute(element, 'step'),\n        checked:typeof element.checked === 'boolean' ? element.checked : has(element, 'checked'), multiple:Boolean(element.multiple || has(element, 'multiple')),\n        description:normalize(described.map(description => semanticText(description, {includeHidden:true})).join(' ')),\n      }),\n    });\n  }\n  function parse(element) {\n    if (isHidden(element)) return null;\n    const name = tag(element);\n    if (has(element, 'data-scene-media') || MEDIA.has(name)) return parseMedia(element);\n    if (['INPUT','SELECT','TEXTAREA'].includes(name)) return parseField(element);\n    if (name === 'IMG') {\n      const width = numeric(attribute(element, 'width'), numeric(element.naturalWidth, 1));\n      const height = numeric(attribute(element, 'height'), numeric(element.naturalHeight, 1));\n      return basic(element, 'image', {src:attribute(element, 'src') ?? element.currentSrc ?? '', alt:attribute(element, 'alt') ?? '', aspect:width / height});\n    }\n    if (name === 'BUTTON' || (name === 'A' && has(element, 'href')) || ['button','tab','switch','menuitem'].includes(attribute(element, 'role'))) {\n      const children = parseChildren(element);\n      const text = children.map(child => child.text ?? child.runs?.map(run => run.text).join('') ?? child.alt ?? '').filter(Boolean).join(' ');\n      return basic(element, 'control', {actionId:action(element), text:text || labelFor(element), children,\n        disabled:Boolean(element.disabled || has(element, 'disabled') || attribute(element, 'aria-disabled') === 'true'),\n        selected:attribute(element, 'aria-pressed') === 'true' || attribute(element, 'aria-selected') === 'true' || attribute(element, 'aria-checked') === 'true',\n        metadata:metadata(element, {label:labelFor(element), buttonType:attribute(element, 'type') ?? 'button', symbol:elements(element).filter(child => tag(child) === 'I' && attribute(child, 'aria-hidden') === 'true').map(child => normalize(semanticText(child, {includeHidden:true}))).join(' ')}),\n      });\n    }\n    if (/^H[1-6]$/u.test(name)) return copy(element, 'heading', {level:Number(name[1])});\n    if (name === 'PRE') return basic(element, 'code', {text:String(element.textContent ?? semanticText(element)), metadata:metadata(element, {language:attribute(element, 'data-language') ?? ''})});\n    if (name === 'HR') return basic(element, 'divider');\n    if (name === 'DETAILS') {\n      const summary = elements(element).find(child => tag(child) === 'SUMMARY');\n      const expanded = Boolean(element.open || has(element, 'open'));\n      const heading = summary ? basic(summary, 'control', {actionId:action(summary), text:normalize(semanticText(summary)), selected:expanded, children:[copy(summary)], metadata:metadata(summary, {disclosure:true, expanded})}) : null;\n      return basic(element, 'stack', {role:'disclosure', children:[...(heading ? [heading] : []), ...(expanded ? parseChildren(element, new Set([summary])) : [])], metadata:metadata(element, {expanded})});\n    }\n    if (name === 'TABLE') {\n      const rows = descendants(element).filter(child => tag(child) === 'TR').map(row => elements(row).filter(cell => tag(cell) === 'TD' || tag(cell) === 'TH').map(cell => basic(cell, 'stack', {role:'table-cell', children:parseChildren(cell)})));\n      const caption = elements(element).find(child => tag(child) === 'CAPTION');\n      return basic(element, 'table', {rows, children:[...(caption ? [copy(caption)] : []), ...rows.map((cells, index) => ({id:`${identity(element)}-row-${index}`, type:'stack', role:'table-row', children:cells}))]});\n    }\n    if (name === 'UL' || name === 'OL') {\n      return basic(element, 'list', {children:elements(element).map(parse).filter(Boolean), metadata:metadata(element, {ordered:name === 'OL', start:numeric(attribute(element, 'start'), 1)})});\n    }\n    if (COPY.has(name) || INLINE.has(name)) {\n      // Authored rich content may contain nested media/controls. Keep their\n      // semantic nodes rather than reducing those objects to empty text runs.\n      if (descendants(element).slice(1).some(child => MEDIA.has(tag(child)) || has(child, 'data-scene-media') || ['IMG','INPUT','SELECT','TEXTAREA','BUTTON'].includes(tag(child)))) return basic(element, 'stack', {children:parseChildren(element)});\n      return copy(element);\n    }\n    const role = explicitRole(element), children = parseChildren(element);\n    const node = basic(element, role === 'grid' || role === 'columns' ? 'grid' : 'stack', {role, children});\n    if (!children.length && !attribute(element, 'id') && !nativePaintFor(node)) return null;\n    return node;\n  }\n  function readHome(main, phase) {\n    const all = descendants(main);\n    const panelElements = all.filter(element => has(element, 'data-cms-panel') && !isHidden(element));\n    const logoElement = all.find(element => has(element, 'data-world-logo') || hasClass(element, 'comic-header-wordmark'));\n    const textureElement = all.find(element => hasClass(element, 'comic-halftone'));\n    const footerElement = all.find(element => hasClass(element, 'cms-comic-footer'));\n    const panels = panelElements.map(element => {\n      const children = elements(element);\n      const image = descendants(element).find(child => tag(child) === 'IMG');\n      const title = children.find(child => tag(child) === 'STRONG');\n      const eyebrow = children.find(child => tag(child) === 'SPAN');\n      const symbol = children.find(child => tag(child) === 'I');\n      return {id:attribute(element, 'data-cms-panel'), actionId:action(element), disabled:phase !== 'comic' || Boolean(element.disabled),\n        eyebrow:normalize(semanticText(eyebrow)), title:normalize(semanticText(title)) || labelFor(element),\n        symbol:normalize(semanticText(symbol, {includeHidden:true})), color:attribute(element, 'data-world-color') ?? 'paper',\n        image:image ? attribute(image, 'src') ?? '' : '', alt:image ? attribute(image, 'alt') ?? '' : '', href:attribute(element, 'href') ?? '', layout:metadata(element).design,\n      };\n    });\n    const logo = logoElement ? {src:attribute(logoElement, 'src') ?? '', alt:attribute(logoElement, 'alt') ?? ''} : null;\n    if (logoElement) identity(logoElement);\n    const footer = normalize(semanticText(footerElement));\n    const notices = elements(source).filter(element => element !== main && !isHidden(element) && (attribute(element, 'role') === 'status' || hasClass(element, 'cms-preview-banner'))).map(element => ({text:normalize(semanticText(element)),role:'status',tone:'yellow'}));\n    const controls = panelElements.map(parse).filter(Boolean);\n    if (phase !== 'comic') for (const control of controls) control.disabled = true;\n    const texture = textureElement ? {src:attribute(textureElement, 'src') ?? '', alt:attribute(textureElement, 'alt') ?? ''} : null;\n    return {home:{panels,logo,texture,footer,notices}, root:basic(main, 'stack', {role:'home', children:[...controls, ...(footerElement ? [parse(footerElement)].filter(Boolean) : [])]}), phase};\n  }\n  return {\n    read() {\n      if (destroyed) throw new Error('The semantic document reader has been destroyed.');\n      sources = new Map(); actions = new Map();\n      const all = descendants(source);\n      elementList = all;\n      lookup = new Map(all.filter(element => attribute(element, 'id')).map(element => [attribute(element, 'id'), element]));\n      const shopElement=all.find(element=>has(element,'data-world-shop')&&!isHidden(element));\n      if(shopElement){\n        let shop;try{shop=JSON.parse(attribute(shopElement,'data-world-shop'));}catch{}\n        if(shop){const shopNodes={};for(const el of descendants(shopElement))if(has(el,'data-shop-control'))shopNodes[attribute(el,'data-shop-control')]=['INPUT','SELECT','TEXTAREA'].includes(tag(el))?parseField(el):parse(el);\n          const details=descendants(shopElement).find(el=>has(el,'data-shop-details'));\n          return {route:pathname??source.ownerDocument?.defaultView?.location?.pathname??'/',title:normalize(source.ownerDocument?.title)||'Shop',kind:'shop',phase:'shop',shop:{...shop,nodes:shopNodes,detailsNode:details?parse(details):null},root:basic(shopElement,'stack',{role:'shop',children:[]}),sources,actions};\n        }\n      }\n      const main = all.find(element => tag(element) === 'MAIN' && !isHidden(element)) ?? source;\n      const comic = hasClass(main, 'comic-shop-experience');\n      const phase = comic ? attribute(main, 'data-world-phase') ?? [...classes(main)].find(token => token.startsWith('phase-'))?.slice(6) ?? 'comic' : 'page';\n      const heading = all.find(element => tag(element) === 'H1');\n      const route = pathname ?? source.ownerDocument?.defaultView?.location?.pathname ?? '/';\n      const title = normalize(source.ownerDocument?.title) || normalize(semanticText(heading, {includeHidden:true})) || 'Tragik Minded';\n      const common = {route,title,kind:comic ? phase === 'shop' ? 'shop' : 'home' : 'page',phase};\n      const appearanceElement = all.find(element => has(element, 'data-scene-appearance') &&\n        (comic ? phase === 'shop' && attribute(element, 'data-scene-appearance') === 'shop-preview' : attribute(element, 'data-scene-route') === route)) ??\n        (!comic ? all.find(element => has(element, 'data-scene-appearance')) : null);\n      if (appearanceElement) common.appearance = {\n        document:attribute(appearanceElement, 'data-scene-appearance'),\n        backgroundColor:attribute(appearanceElement, 'data-scene-background-color') ?? '',\n        backgroundImage:attribute(appearanceElement, 'data-scene-background-image') ?? '',\n        backgroundOpacity:Number(attribute(appearanceElement, 'data-scene-background-opacity') ?? 1),\n        backgroundPattern:attribute(appearanceElement, 'data-scene-background-pattern') ?? 'template',\n        ...(has(appearanceElement, 'data-scene-cover-image') ? {coverImage:attribute(appearanceElement, 'data-scene-cover-image') ?? ''} : {}),\n      };\n      const content = comic && phase !== 'shop' ? readHome(main, phase) : {root:basic(source, 'stack', {role:'page', children:parseChildren(source)})};\n      return {...common,...content,sources,actions};\n    },\n    destroy() { destroyed = true; sources.clear(); actions.clear(); lookup.clear(); elementList = []; identities = new WeakMap(); },\n  };\n}\n","runtime/sky-catalog.mjs":"// Original Khnum planetarium catalog, 3186 stars and 781 constellation segments.\n// Source: Assets/Khnum/Runtime/Environment/Astronomy/Resources/planetarium_data.bytes\n// SHA-256: a9aae6b9ed9bca48317500b8d192f33cd72dce137964f73183a61fd3cbeb26fd\nexport const skyCatalogBase64=\"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AACAQDOzj0P2KKjBAACAQDMzMkIzMyHCAACAQDOzb0NI4dDBAACAQDTzQUOuRxlCAACAQACA30LXowRBAACAQJp5oUNxPbLAAACAQABAdUNSuMzBAACAQJqZbUI9CiBCAACAQMzMOEI9ClZCAACAQJpZekPNzPxBAACAQJrZEkPXI4LCAACAQGZmqkOPwvFBAACAQGbmzEKkcErCAACAQACAO0P2KITBAACAQADApUMK16O+AACAQDMzbkL2KFjBAACAQGfmqEK4HqlBAACAQGdmEkP2KL5BAACAQADAh0MpXPPBAACAQMzMR0MpXLnBAACAQM0sj0OPwl1BAACAQM1shUMfhSDCAACAQJqZAULD9QtCAACAQABApEOkcBXCAACAQABAZkP2qI9CAACAQGfmfEMzMxjCAACAQGZmJ0MAADJCAACAQDMzX0L2KD9CAACgQGbGgEMUrnvBAACgQACA00LXo77BAACgQDQzvkLhevDBAACgQM2MNkPD9bTBAACgQGfmlkKuRy9CAACgQDPzWUNxPRlCAACgQM2MP0NSOIjCAACgQACgmEPhemzBAACgQDNTkkMUrt9BAACgQGbmyUI9CslBAACgQM2MG0MAACZCAACgQDMTkEPsUYdCAACgQM0siUM9ChPCAACgQAAALkN7FHzCAACgQM3MBUNmZr5AAACgQJm5mkP2KD3CAACgQDNzIkMfhYHBAACgQDOzfkPD9V/CAACgQACAr0J7FA/CAACgQJnZYENmZijCAACgQABggUNSuMZBAACgQABADENcjwlCAACgQJnZBkP2KEBCAACgQABggUNxPRNCAACgQM3MDkO4HmTCAACgQACggEOFa4NCAACgQABAD0NSuE5CAACgQDOzjEOF69fBAACgQGfmxkLNzCzCAACgQM1MmUKF6yRCAACgQDSzmELD9bLBAACgQJq5hUP2KBTCAACgQGamXEOk8IHCAACgQGZmfkN7FBZBAACgQGbmkEJSuN5AAACgQDNzsUOPQptCAACgQGYmn0MK1/FBAACgQGZmZkOamSLCAACgQJkZoUO4Ho1CAACgQM1sl0OF61G/AACgQGamdEN7FJbAAACgQDOz4EIzMy3CAACgQMwMYkNxPcrBAACgQJlZjkOPwgJCAACgQDSziUOamTnAAACgQGZmY0LhepTCAACgQDPTq0O4Hn3BAACgQGamU0OkcNXBAACgQGdGgkMAAMjBAACgQGdmHkFI4fZBAACgQAAAzEKPwnfCAACgQDNzGUN7FIzCAACgQM1MZ0NI4WtCAACgQJmZnEIUroHBAACgQJoZiUIzM1zCAACgQGfmN0O4HmRCAACgQDPzHUOPwnbCAACgQACAu0J7FLRBAACgQGamgkMfhWHCAACgQGZmOUIpXBtCAACgQAAAhEFI4TrCAACgQJnZhkPsURzBAACgQGdmj0MpXN3BAACgQJmZKENI4XZBAACgQAAAgUPD9SzCAACgQGZmfkJI4XnCAACgQJqZ6kJI4cbBAACgQAAA/0JI4XJCAACgQDMz5UEUrn5CAACgQMxMokKamRnAAACgQACAykJmZk5BAACgQGamkUM9CkdAAACgQDOzS0OamRm/AACgQDOzZkOPwjLCAACgQDOzA0OkcM1AAACgQDSzp0JI4R5BAACgQDTzQUOamVlAAACgQDMzGkPsUXXCAACgQJpZpkPNzGhCAACgQGZmhkKF631BAACgQGbGgEMUrnvBAACgQDNzT0OPwibCAACgQGdmsEGuRy3CAACgQAAAZENmZlDCAACgQJqZcEIK10dBAACgQM0sqkOuRy1BAACgQJqZ4UHXo+xBAACgQACgm0NmZoTCAACgQGdGhUOPwt1BAACgQAAAcEOamRnCAACgQDSzCUNI4WvCAACgQDMzGkMpXLtBAACgQABAj0P2KJzAAACgQACgm0MpXHdCAACgQM3MiEFI4SLBAACgQDMzGkPXoytCAACgQM3MREGuR2dCAACgQACA7kKF61PCAACgQGfmZEOuRwVCAACgQJlZPkOambm/AACgQDNzT0NI4SnCAACgQGamekMUrhtCAACgQDMTq0OuR03CAACgQM3MF0N7FIZBAACgQJo5q0PNzMRBAACgQGemKUMpXARCAACgQGfm0kKkcN/BAACgQDNzYUNcjyFCAACgQJqZz0E9Cn/BAACgQABgikNI4TfCAACgQJo5q0NmZoRCAACgQGbmzEI9CgLCAACgQJnZlUOF65tBAACgQDMzjkNI4ajBAACgQJlZPkOambm/AACgQJo5jUOkcAVCAACgQABAFUOuR1rCAACgQJlZEUNxPR5BAACgQDMzX0LsURzBAACgQGZmhkKkcJlBAACgQDQz3EIK169BAACgQGZGpkNmZsZAAACgQGZm+EI9ChNBAACgQJk5hENmZnbBAACgQDNzbUMfhVvAAACgQABALUNI4f7BAACgQGdmrUK4Hm3BAACgQAAAgUIzMwfCAACgQMzMVkNxPTjCAACgQJmZmUC4Hg3BAACgQM4MfUN7FBjCAACgQM3MCEI9Ck7CAACgQDMzI0IpXE9AAACgQMzMKUPhemzBAACgQMwMl0MpXITCAACgQGZmZkM9ChHCAACgQDPzWUPD9fJBAACgQGfmB0PXozxCAACgQACgiUPDdZFCAACgQDMz6EIzM8NBAACgQABAmEPXo0jBAACgQDMzrEKambPBAACgQAAA20LsUYRBAACgQJqZw0EfhUJCAACgQDOznkJI4drAAACgQGZmOUOamXHCAACgQGdmg0NSuHLCAACgQM0MAkMUrlPCAACgQGdmzkIK1wdCAACgQAAAqEFI4QLBAACgQM1MakO4HuHBAACgQDMzhUOkcIHCAACgQJqZQ0OaGY/CAACgQDSzGEOamUXBAACgQJqZTELhehBBAACgQMzMKUJ7FNpBAACgQJqZMUNcj+I/AACgQDMzZUJmZsBBAACgQGZmtkGamXVBAACgQACA6EJI4RfCAACgQGamfUOkcCnCAACgQJq5rEPsUSlCAACgQGdmMEPDdYXCAACgQJm5mkOamWlBAACgQM2MbENSuHZBAACgQDTTh0MpXEjCAACgQJmZqEMK16O8AACgQGbmgUJ7FHpBAACgQGbmZ0NI4ehBAACgQJqZakNxPe7BAACgQDPzDkNxPXxCAACgQMwMU0OPwoBCAACgQDPTnEPNzGnCAACgQACAkUIfhbNAAACgQDOzrUMpXKnBAACgQDPzAkOPwgTCAACgQGaGokOuR4XBAACgQM1slENxPZRBAACgQDMzl0LsUSRCAACgQM2MMEO4Hj9CAACgQABAEkM9CnrCAACgQM3MFEGamVdCAACgQMzMR0J7FK7BAACgQDOzhkMAAOpBAACgQM2srkOF61FAAACgQJmZ50GkcE7CAACgQADAbUMpXI9AAACgQAAAk0L2KBxAAACgQJkZ7ELsUSLCAACgQAAAYUJI4cBBAACgQGZmlUO4Hs1AAACgQDOzs0K4HllCAACgQDMzskJSuGLBAACgQJoZnkNSuC9CAACgQGfmh0PD9RhBAACgQMwshkPhemNCAACgQJmZq0Ncj/LAAACgQAAAT0IUrhtBAACgQM2MXUOF6/E/AACgQAAAVUIpXBfBAACgQM6MdUMzM5lBAACgQDMz30GuRyXBAACgQMwMCENmZjzCAACgQM2sokOux5rCAACgQACAhUOkcC1AAACgQM3MpkLD9XnCAACgQM3Mr0I9CqfBAACgQGfm/EKuR4TCAACgQGZmwkLD9eDAAACgQGfmqEO4HklCAACgQGfmpUPNzMpBAACgQABAkkOF605CAACgQACAqUJmZibAAACgQGZmKkKamV9CAACgQDNzfEP2KGzCAACgQJoZj0OF663BAACgQAAAYULsUSpCAACgQM3MoEOuR7PBAACgQDQzAkOamTrCAACgQDPzmkNcj35BAACgQGdmg0LsUYxBAACgQJlZXEMUrltBAACgQJpZI0NmZmvCAACgQGbm3kJmZt5BAACgQADAE0P2KGxCAACgQM3sm0MAABjBAACgQACA1kIAAI3CAACgQJpZI0NI4QhCAACgQJqZPUKkcDNCAACgQJm5l0PD9TpCAACgQDOzaUPXoyhBAACgQACA5UJmZtbBAACgQM2skEPhelVCAACgQDSziULhevTBAACgQGbmHEPD9WrCAACgQGama0NmZtJBAACgQJk5sUMK1zlCAACgQABgn0P2KBhCAACgQDNzhEM9CjhCAACgQM3Md0OkcP0/AACgQJmZ80HXozBAAACgQM3MLEP2qIpCAACgQGemC0MzMxNCAACgQDRzHENSuIbBAACgQAAAXkN7FJ7CAACgQM2MUUNmZijCAACgQGZmhkIpXH9BAACgQGZmYEL2KAFCAACgQM1MH0OF60DCAACgQDSzp0OF67G/AACgQABAHkPD9RRBAACgQJn5h0N7FOZBAACgQJoZPEOPQpDCAACgQM3MBUNcj3LCAACgQJm5iEPheqjBAACgQACAlEM9ioxCAACgQMzsiUP2KK5BAACgQGdmPEN7lItCAACgQJkZv0KPwgXCAACgQM1si0MK1wPBAACgQM3MfUL2KCnCAACgQGZmYEK4noHCAACgQADAGUPheijCAACgQJkZb0Ncj3pBAACgQDOzrUKuR0zCAACgQDNzPUP2KELCAACgQJqZpUJI4Q3CAACgQGfmc0OPwn7CAACgQAAAZEEAABpCAACgQM2MhkMAABVCAACgQDOzUUMzMzPCAACgQABAb0MUrunBAACgQJqZYULD9cJBAACgQM2MA0P2KDjCAACgQGcGlUOuR4E/AACgQM3M4kHsUZpBAACgQDMzFEN7FNBBAACgQM3MXEO4HrXAAACgQGbGrUMAADXCAACgQAAAZEPD9ULCAACgQJmZGUAAADfCAACgQDPzOEMfhSu/AACgQGZmeEPNzJ3CAACgQAAAz0KkcMHBAACgQJmZCkPhehRAAACgQGdmMEJmZg7BAACgQM2MlUPsUQxCAACgQM3MSkPXox3CAACgQJrZaUPXo2zBAACgQGbmEEOF65G/AACgQABAKkPD9VnCAACgQDPzdENxPTlCAACgQGfm/EJxPXrAAACgQM0MO0OF60jCAACgQDMzi0LNzGTBAACgQAEAc0IUrr9AAACgQJpZYkMzMzzCAACgQAAAf0OkcPdBAACgQDNzn0MAAKhAAACgQAAA50JSOJHCAACgQJoZJ0OF62vCAACgQAAALkI9ClNCAACgQGZmtkGuR0TCAACgQDQzkUPNzI7BAACgQM1MikJmZlbAAACgQDMTh0MfhTtAAACgQGemcUMpXKXBAACgQJkZA0MzM5FBAACgQDMz00BSuC7CAACgQGbm50KamefBAACgQAAgnUMUriRCAACgQM3MiEEAAF3CAACgQDOz5kLNzBjBAACgQAAASUMUrltCAACgQAAAQEKF6+fBAACgQDMz90EK15BCAACgQM1MxkJ7FJrBAACgQACAkUPheiLCAACgQJnZCUOuR3nCAACgQDPzl0MK1z5CAACgQJlZkUMK1zHCAACgQJrZqkNcj7xBAACgQDOzs0KuRyvCAACgQGZmeEIK1z5CAACgQJlZr0PNzKDBAACgQDMTlkPs0ZHCAACgQJnZNkPhelHCAACgQAAAhEJ7FAjCAACgQM2sqEMAAC7CAACgQM3Mr0KamRxCAACgQM0MAkNxPQ3CAACgQGdm2kKF64fCAACgQADAbUMfhQbCAACgQGbGoUMpXDZCAACgQM3MbkL2KA9CAACgQM2srkPD9WjCAACgQAAA8EHXo6jBAACgQACAu0LXo8jAAACgQAAAK0PheihBAACgQAEAc0MUrpvBAACgQGYmHENcD5TCAACgQJoZ1EJcj6RBAACgQM1MSUPD9VtCAACgQJqZz0GPwkpCAACgQACAXEP2KBfCAACgQJqZB0PNzITCAACgQACA2UL2KNbBAACgQDRzcENxPWpCAACgQM2smUPD9fJBAACgQGZmjEMp3I7CAACgQM3MjkIAAFDAAACgQDPzdEPXo0jCAACgQJq5jkMUrrfAAACgQJoZNkMK18XBAACgQM1sjkO4HnFBAACgQJqZBEMUrt3BAACgQGamQUNSuGTCAACgQDOzA0N7FOZBAACgQM3Ml0KPwnFCAACgQGZmCUJmZgdCAACgQM3ss0MfhdtAAACgQAAgmkPNzDRBAACgQGdmMEPD9dBAAACgQM1sqUOPwvW9AACgQP//e0JI4drAAACgQAAAPUKkcEZCAACgQJo5q0OkcFnBAACgQMxMT0PNzHxBAACgQDNzW0NSuEXCAACgQJoZV0NcjxfCAACgQABAKkPD9cBAAACgQGYmK0OkcI3BAACgQGemOEMAAIDCAACgQDMzlEL2KFhBAACgQJpZOEOF64fCAACgQDPzXEMUrgzCAACgQJkZA0OamSrCAACgQAAA5EIzM9dBAACgQM1MWENmZk9CAACgQJnZfkPXo1TCAACgQM2sqEMUrmlCAACgQM5M+UIK15nCAACgQJpZZUMzM2vCAACgQABAY0O4HjXCAACgQGdGoENmZp5BAACgQGZmNkLD9bzBAACgQJmZDULsUYnCAACgQMyMD0OF62zCAACgQAAAz0LXo0DBAACgQJqZH0LD9ag+AACgQM0MVkMAAMDAAACgQAAAJUNmZpLBAACgQGbmjUPNzC9CAACgQABAnkMK14nBAACgQJqZPUH2KMJBAACgQDMzwUHXoyVCAACgQGcmbUO4HpFBAACgQJmZqELXoxRBAACgQJmZikOuR0TCAACgQJqZ6kLhejnCAACgQGbGj0MpXB3CAACgQJrZHkNSOJ3CAACgQM3M00J7FHrBAACgQDNzAUPD9SvCAACgQDOzj0OamRfCAACgQDOTrEMAAFPCAACgQGcmB0O4HidCAACgQM3MIEKkcB/CAACgQDOzMENSuHTCAACgQGYmakPXo4TCAACgQGbmE0OamW3BAACgQGemDkNI4SHCAACgQAAAf0LXo0FCAACgQADAqEMAAC/CAACgQDRznEMpXNfBAACgQGZmlUPheifCAACgQDMztUJmZhpBAACgQJqZH0I9iojCAACgQGfGqkOPwqLCAACgQACAbkMK14XBAACgQAAAokKF6/nAAACgQJpZNUMUrgtBAACgQGZmJEKF60RCAACgQGZmwkIUrqFBAACgQGfGm0P2KMrBAACgQGaGsUP2KLRAAACgQDMzYkKamb9BAACgQDNzAUNmZrZAAACgQGcGp0MAABdCAACgQJqZUkI9Ck9BAACgQABAaUNI4fpBAACgQGZmb0M9CtdBAACgQM0Mo0MzM81BAACgQMxsgkOamRRCAACgQAAA2EJI4fq+AACgQGaGsUPsUTFCAACgQGamNUNSOIHCAACgQJoZtkJ7FLpBAACgQDMzZULNzBDCAACgQM3MgkOkcMHBAACgQAAABEFSuHtCAACgQJoZp0PD9fjAAACgQDOz4EJxPf5BAACgQM0MVkMpXDhCAACgQGYmVUPsUSTBAACgQGcmeUM9Cg3CAACgQJoZqkPsUdjBAACgQDOzjENmZqRBAACgQADgpkP2KGRCAACgQJrZqkNSuEJBAACgQMxMT0PNzAnCAACgQACA7kI9CrfBAACgQM1MpUJmZr5AAACgQJr5q0P2KALCAACgQGbmHEMK1xJCAACgQMyMeEOPwilCAACgQM2sh0MAACBAAACgQM2Me0N7FKRCAACgQM3MUEKPwm9CAACgQJqZhELsUbJBAACgQM2smUPD9XtCAACgQGemcUMzMxPCAACgQM3MoENxvYLCAACgQAHAdkMUrpPBAACgQDOz7EJmZkDCAACgQGdmnkCPwoHCAACgQM3MYkIAALrBAACgQGZmKkKuRxlCAACgQGeGjUOPwnjCAACgQACgiUMUro5CAACgQM2sn0Ncjx1CAACgQM3sjEMAAJjAAACgQDOzm0OPwvVBAACgQDPTjUOamRNCAACgQM1MrkOamcHAAACgQDNzrkOkcBHBAACgQJpZo0NxPUVCAACgQGdmTkP2KATCAACgQABAckNSuDNCAACgQDNzUkO4HsU/AACgQJmZ50I9CudBAACgQJn5okO4HmtCAACgQMzMekM9CpvCAACgQM2shEN7FE7BAACgQAAAE0LNzD7CAACgQGZmJEKPwl3BAACgQGemOEO4np7CAACgQM3Md0PNzArCAACgQAAAikEfhaxCAACgQJqZdkJmZklCAACgQACAmkIAAAzBAACgQGdm+0KPwixCAACgQDMzo0PhenRCAACgQDSziUJcjyJBAACgQJoZgELD9U3CAACgQM1MikI9CiVCAACgQGbmWENmZpdCAACgQJpZQUNSuCDCAACgQDMzi0HD9TxCAACgQAAARkM9Ct9BAACgQDNzpUOPQoFCAACgQJmZVUI9Cq3BAACgQAAAk0IUroRCAACgQMzMR0KuRyzCAACgQJo5h0PNzKxBAACgQM2MBkOPwj1BAACgQDQThEMfhRrCAACgQDMz00FcjxJBAACgQGYGsEP2KMxAAACgQAAAJUKPwiFBAACgQJqZf0JxPQ5BAACgQP//e0HhevxAAACgQGdmPEOkcCVCAACgQDMzi0L2KEhBAACgQMxskUMzMzPCAACgQGemZUMfhT/CAACgQAAAjUIUrrdBAACgQJrZm0PD9YBBAACgQM3MREPXo0fCAACgQGaml0OuR2JCAACgQJmZWELNzMw+AACgQADAHENcj/jBAACgQM1MEEP2qKJCAACgQDMzhULherZBAACgQDRTrUN7FC7CAACgQGZGpkNSuARCAACgQDPzgkPD9e7BAACgQGdGoEPXo4bBAACgQDPza0N7lJtCAACgQDTTpUOF613BAACgQM3Md0NI4YTBAACgQDMzMkPXowfCAACgQMxMsUN7FC1CAACgQJr5qENmZgHCAACgQGbmn0JI4VLBAACgQGbmH0NmZl7CAACgQDPzO0OamYHBAACgQDQza0FI4erBAACgQDNzMUP2KH/CAACgQM3MAkOamVlAAACgQJoZV0Ncj2HCAACgQJqZhEKkcI9BAACgQDMzFEIpXAdBAACgQAAAtEKPwjdCAACgQM1MLkOF61G/AACgQJmZZELhehbCAACgQM0smEP2KEjBAACgQAAAYUKPwsNBAACgQDTzI0MAAMZBAACgQADAWEMKV6fCAACgQM7McUPD9abBAACgQDQzAkM9Cm/CAACgQMzMmkN7FI6/AACgQGYmZ0MfhRVCAACgQM2MA0PNzFjBAACgQJoZjEIpXJ3BAACgQAAggkOamU3BAACgQADANENxPX3CAACgQM3Mu0IAAOxBAACgQDPzDkOPwrdBAACgQGbmT0PD9QPCAACgQJmZWEKPwkBCAACgQGbmXkPsUS7CAACgQDMzpkJSuJRBAACgQJkZ4EJI4Q5BAACgQACAKUPsUfxBAACgQMwMiEMUrn7CAACgQJmZWEMfhTXCAACgQJp5iUNxPRBCAACgQJpZQUOPwkPCAACgQDQz+kLD9ZrCAACgQGfmw0IpXLvBAACgQACArELheoPCAACgQJmZGUHheupBAACgQM0Mo0PNzIpBAACgQDNzUkNmZjbCAACgQM3MgkPheoRAAACgQM0MC0P2KGbCAACgQGdmEkFI4QZCAACgQJrZjEOkcJFBAACgQGYmEEOkcEXCAACgQDNzakOuRyrCAACgQACgjEOkcBZCAACgQJmZjUGamVxCAACgQM2sqEMK1z5CAACgQGeGkEMfhRhCAACgQAAgo0O4HgTCAACgQMzsiUPD9XXCAACgQACAZUMzM/HBAACgQGZmxUKF61PCAACgQDQzkUJmZg5BAACgQADAOkP2KOJBAACgQGdmP0IK151BAACgQM2Mg0MULq1CAACgQGfm80JxPYnCAACgQGcmBENI4bpAAACgQACA0EIUrmlCAACgQM3MskK4Hg3CAACgQACAU0NSuCTCAACgQDQTk0O4HqW/AACgQGZm/kAK13vCAACgQJqZUkIAAEBCAACgQJmZnEI9Ck/BAACgQMzMdELXo7BBAACgQDQz9EJSuD7AAACgQDMzBUKamQ1BAACgQM3MLEEK12XCAACgQDMz0EJmZojBAACgQGdmkkJmZq7AAACgQM1M20KamcfBAACgQAAAJUOF6yjCAACgQGZm5j7sUcDAAACgQGdmvEJcjwzCAACgQAAgiEPheqZBAACgQAAAlkO4Hg3CAACgQAAgl0OFa5tCAACgQACgsEOuRxfCAACgQJq5r0O4HqXBAACgQM2MRUOuR7HAAACgQMzMZUJcD4NCAACgQACAfUNSuCJBAACgQDMz8UK4HhVAAACgQM3M4kEzMznCAACgQJqZk0MK14tBAACgQGZmp0IK1xdBAACgQADAYUOPwgVAAACgQGaGk0N7FJBBAACgQDPzF0MAACBBAACgQAAAwEJI4ZJAAACgQAAA6kIfhc/BAACgQGbGpEPD9VvCAACgQMxMsUJxPaJBAACgQAAgjkPDdYbCAACgQP9/9EIAAJrBAACgQAAAZEEpXLtBAACgQADAn0OamQtCAACgQDNTg0NI4dBBAACgQGbmzELhelbCAACgQDOzV0M9Ch7CAACgQDOz10IAAPJBAACgQADArkNI4RLBAACgQJl5rUOux5ZCAACgQDPTrkO4HgLCAACgQAAggkNI4ajBAACgQJnZhkMfhfFBAACgQGfmt0LsUWxBAACgQGbm9kJmZiHCAACgQGbm2EJcjzLCAACgQJo5h0MUrqc/AACgQMzsp0OF61BCAACgQGamr0MzM7tBAACgQGfmxkKF65HBAACgQJqZJUOkcKFBAACgQJqZXkMUrt/BAACgQDMz2ULNzNLBAACgQAAAmUOPwgBCAACgQAAAnEPD9aDAAACgQM1MlkMUrt3BAACgQGZmm0O4HnFBAACgQACAlEIAAFdCAACgQAAAf0LhevTAAACgQM2MbEMzM+tAAACgQDMzYkKamUHBAACgQJk5kEOamRxCAACgQJkZkkMpXMVBAACgQJoZWkMK10nCAACgQGZmQkFI4fJAAACgQJoZgEIzM23CAACgQGbmvUI9CmxCAACgQDKz9ULheh7CAACgQGZmjEKkcCfCAACgQGZmZkCkcJfBAACgQAAAB0MAACXCAACgQM3MykEUrq9AAACgQJrZHkNxPWbCAACgQDMzKULXowHCAACgQM5M+ULXoxLCAACgQADAAUOPwlVAAACgQGdmIUN7FKHCAACgQAAAnEKF6z3BAACgQDMzKUOamWnAAACgQJoZeEOPwqvBAACgQGbGkkP2KOxAAACgQJoZ40JmZrLBAACgQAAAukIpXGNBAACgQDNzkENSuJJCAACgQAAAakLNzDzAAACgQAHAdkMzMz7CAACgQDOzWkOF6+1BAACgQAAAJUJcj5TBAACgQDPzNUPXo0rCAACgQJmZnEI9CjdAAACgQM3MFELNzIZCAACgQGZmSEJmZuhBAACgQJkZ70LD9UTCAACgQJpZCEMzM5HCAACgQACAxEJcj+pAAACgQADApUP2KB7CAACgQGZmlUJI4do/AACgQJkZwkLsUQLCAACgQADAMUMUrjTCAACgQGYmDUOkcNFBAACgQDOzD0MzM1BCAACgQM3MF0PD9QxCAACgQDPzF0OkcL2+AACgQDMzhUKF63lBAACgQABACUNmZk5CAACgQJnZJ0PXo7bBAACgQDQTk0NI4UhCAACgQM1M2EI9CjvCAACgQAAAzEJxPRpAAACgQDPTsUPXo2jBAACgQAAAdkPsUaDBAACgQJqZcELXo3jCAACgQDRTrUOF673BAACgQAAANEG4HkFCAACgQJrZZkOuR23CAACgQDPzrEPhenRAAACgQM3MiEL2KO7BAACgQAAAtEP2KIPCAACgQGdmnkN7FMjBAACgQABAYEMzM4vAAACgQGbmyUIUrlNBAACgQJmZ80Hs0Y1CAACgQGdm7EKkcBvCAACgQADAsUMK1+M/AACgQDQzXEMpXINBAACgQGbmrkIUrmDCAACgQDOznkOF6zXBAACgQMwMpkPD9QPCAACgQACAqUIK14NAAACgQJoZqkO4HjFCAACgQJrZTkMUrotBAACgQDPzl0Phet5BAACgQJmZq0MfhQPCAACgQJkZoUPheq7BAACgQABAG0P2KGDCAACgQJr5n0PNzFXCAACgQDOzA0N7FGPCAACgQAAAMUOPwqFBAACgQJm5pkMK1x5CAACgQM3MjkFSuPBBAACgQJl5m0MUrk/CAACgQGbmEEPsUXXCAACgQJlZDkPNzA/CAACgQDOzm0PsUWZCAACgQM3MlED2KBNCAACgQJlZa0MpXJ1BAACgQGcmokOPwpvBAACgQDNTs0MAAGZCAACgQJqZrkJSuBxCAACgQDNzqEOk8IHCAACgQABglkNxvYdCAACgQMzMVkOF61XBAACgQABAoUO4Hr1BAACgQDMTrkPXo0VCAACgQAAAKEIAAOpBAACgQMwMYkOamddBAACgQDQzkUMpXH/BAACgQAAASUN7FIHCAACgQM1siEPNzDfCAACgQDOzSEPD9XPCAACgQDPTqEPheixCAACgQM1MVUP2KE9CAACgQJmZ5EJI4QvCAACgQAAAtELhekTAAACgQM1shUPXo97BAACgQGZm/kBI4XvCAACgQM3MgkNI4aLAAACgQDMzR0HsUSRCAACgQM3sm0PD9RFCAACgQDPzDkM9Cpe/AACgQMyMD0OamRFCAACgQACglUMpXNnBAACgQAAAZEPsUZ7BAACgQGZmxUIUrrfBAACgQJrZZkOkcBPCAACgQAAAn0LhegVCAACgQM0ssEP2KExBAACgQGdmrUNcjxZBAACgQJoZmEKF6w3CAACgQM0MFENxPVhCAACgQAAA0kLD9ZlCAACgQACgp0P2KDpCAACgQAAAakMK1zPCAACgQJoZdUMpXMHBAACgQDMTqEOF60VCAACgQAAAUkIfhWtCAACgQGZmZj9SuIrBAACgQJn5h0MUruPBAACgQADAokO4no5CAACgQAAA4UL2KEBBAACgQM3sg0M9CjrCAACgQADAKEPNzLhBAACgQM3MbkKamXXCAACgQJmZK0JxPQxCAACgQDOzsEJI4dxBAACgQDRTnkOamT5CAACgQGamCEPNzBlCAACgQJmZWENI4TTCAACgQMyMD0P2qItCAACgQMyMj0MAACLCAACgQM1MdkPhemBBAACgQMwslUPXo8BBAACgQDPTjUMzM7VBAACgQDMzWUJ7FCHCAACgQDRzdkOambvBAACgQGbmE0MzMzrCAACgQDSzp0JI4ZrAAACgQGYmc0OkcN/BAACgQJk5hEOF6wHBAACgQGeGnEPNzNhBAACgQAAARkMUrm/CAACgQM0Mr0OF671BAACgQGbmT0OkcIFCAACgQADAhEPNTJBCAACgQDPzIEOuR3LCAACgQJqZXkJxPRXCAACgQJqZJUEpXDjCAACgQDNzbUNcj9BBAACgQDPzGkO4HlzCAACgQGYmKEOuR3HCAACgQGamskM9CuHBAACgQDQTk0M9CsfBAACgQJpZbkPXo8rBAACgQM2MjEOkcB5CAACgQGZmZkL2qI5CAACgQM0MAkP2qIBCAACgQACAo0JmZkZAAACgQABAJENcjxTCAACgQJpZF0PD9VDBAACgQABAXUMUrodBAACgQM0MkUOFa4NCAACgQAAAgUIzM0lCAACgQJlZDkOuRzHAAACgQM1MZ0OF6xrCAACgQMyMaUPD9SDBAACgQM3MrD9SuLbAAACgQAAA8EIzM5PBAACgQDMzbkPNzClCAACgQGZmhkBSuBpCAACgQGdmP0KkcB5CAACgQM3M4kH2KExAAACgQACA30IK10RCAACgQGZGmkMAAA1CAACgQDMzi0NxPSnCAACgQGfmpUKamenAAACgQACAl0JSuKxBAACgQJqZYUFSuGxCAACgQDOzLUM9ClnCAACgQGamQUOamWzCAACgQM0MjkNmZoZAAACgQM2MP0PD9WHCAACgQDMzJkNI4epAAACgQGamJkMUrnnCAACgQGbmdkOF6wXBAACgQDPzCENI4c7BAACgQDMTh0P2KGzAAACgQGfmCkPXoxXCAACgQAAAcEL2KMDBAACgQGZmYEMp3INCAACgQGZmM0JSuKpBAACgQGfmikO4HnnCAACgQDPzAkMpXOfAAACgQGbm50KuR+PBAACgQGbmPUPD9R/CAACgQDQzTUNSuFpCAACgQGdm7EKkcEbCAACgQADAbUMAAM7BAACgQABgjUNcj21CAACgQDOzs0O4HslBAACgQGbmcEN7FGfCAACgQJlZa0PXozLCAACgQGemqUMzM05CAACgQMxMk0JmZiJBAACgQJoZtkK4HqFBAACgQGfmh0MUrgtBAACgQGbmakNcjxJCAACgQM1smkNSuGpBAACgQM3MkUN7FK4+AACgQAAAakJI4cTBAACgQDOzTkNSuE3CAACgQDNzfEPheizBAACgQM2sgUP2KBVCAACgQGYGrUMzM0hCAACgQM1M20KF6xLCAACgQDSz40LD9eLBAACgQM3MiEKkcG1BAACgQAAAsUIpXHzCAACgQM1M4UIUrvfBAACgQGemDkNI4SHCAACgQGcmNEMfhdNAAACgQDMzI0IUrt1BAACgQP+/cEOF60TCAACgQGbmHENcj2bCAACgQGZmmEJmZnZBAACgQJpZCEN7FI3CAACgQADAPUMAAADBAACgQGdm2kI9Ct/BAACgQGbmz0K4HqHBAACgQMzsiUNI4Q7BAACgQGZmyEJmZh5BAACgQGbmakPXownCAACgQJqZh0PXo+zBAACgQACAI0OPwixCAACgQGamPkNxPUPCAACgQABAiUPsUdjBAACgQM3MAkOPwqtBAACgQGcmlkMAAN5BAACgQM3MjkFxPahBAACgQAAAf0KF6yFCAACgQAAAh0MAAIZBAACgQM2MjEMUrh5CAACgQGemnUM9CgHCAACgQDSzCUM9Cn5CAACgQM2sikO4HmnBAACgQAAA9kIzM41BAACgQJqZ20E9CivBAACgQACAi0JSuH5BAACgQGZmUUI9CkZCAACgQAAAt0I9Cm/BAACgQJqZk0HXo8RBAACgQM3MMkLXox5CAACgQJoZdUNmZp3CAACgQGdmvEGF62xCAACgQGZm+EFmZurBAACgQJkZBkNI4VLCAACgQJqZEEPNzJRAAACgQGZm5kHNTIfCAACgQDPzSkPsUcjAAACgQJl5qkPXo5bBAACgQJk5okMzM/vAAACgQADAH0MK1/9BAACgQDNzk0MzM/FBAACgQAAAFkPXowBBAACgQM2MkkMKV4tCAACgQMyMS0N7FERCAACgQJqZ8EKF67G/AACgQABgpUO4HmPCAACgQJrZsEPheirCAACgQM1M6kLsUTzCAACgQADAH0NSuGzCAACgQDMzhUIpXGtBAACgQADAokOPwkxCAACgQGdmP0OF63PCAACgQDMz5UJI4crBAACgQACgsEOuR6fBAACgQJp5lUNmZtLBAACgQABAjEPNzBDBAACgQGbGnkN7FCJBAACgQDQzoEKF66nBAACgQGYmLkPNzBzBAACgQDPTq0Mzs6hCAACgQGbmXkPNzJhBAACgQACAfUOkcCnCAACgQM0ML0PD9QrCAACgQAAApUIfhYu/AACgQJrZDEPXo+bBAACgQABAlUMpXAdBAACgQADAn0MUrgDCAACgQJqZn0JmZiBCAACgQDSzmEJI4WXCAACgQABAMEPNzJLBAACgQGZmUUOPwn7CAACgQGdmkkIAAGRBAACgQAAANEJcjw5BAACgQGZmDEOPws/BAACgQACAREMK10HCAACgQACApkKPwgBCAACgQGZmUUKPwnvCAACgQJqZgUNmZua+AACgQDRzHEMzMwdCAACgQGemcUMUrjTCAACgQP+/cEP2KDhCAACgQABAOUPsUY5BAACgQACAr0JxPRVCAACgQJpZR0OuRyJCAACgQABAHkNSuCFCAACgQMwMNUMUrnzCAACgQGbmDUNSuLLBAACgQJoZuUJI4VvCAACgQDPTokPD9ZbBAACgQM3MoEJcj8K+AACgQGemHUPh+o/CAACgQJmZq0GPQohCAACgQDKz9UJI4U7BAACgQDNzJUNSuB7AAACgQABAckPD9RFCAACgQJmZnEMUrg/BAACgQM3MEUK4HklCAACgQDKz9ULD9SvCAACgQMzMR0LNTINCAACgQJqZUkJcj6LAAACgQADAbUMK16dBAACgQJlZAkOuRz3CAACgQJk5sUPD9TXCAACgQGdmnkF7FNpBAACgQM3MF0IK13PBAACgQM2MVEOPwlXCAACgQGZm/kB7FFpCAACgQJqZH0JcjyvCAACgQJp5kkPNzAlCAACgQMxMz0JI4VJBAACgQACAnUN7FD5CAACgQJqZB0OPQodCAACgQGemR0PhepLBAACgQADgjkMpXCjCAACgQAAAh0Ncj77BAACgQDNzpUNxPQrAAACgQJpZUEOkcP/BAACgQM0MVkPhek1CAACgQDMz2UJ7FEHCAACgQM1smkPsUXLCAACgQDPTrkNSOIhCAACgQACAa0OkcJ3BAACgQGama0MK1wrCAACgQGbmSUMfhX/BAACgQAAAMUO4noXCAACgQJk5okPsUXhCAACgQGdGgkM9iofCAACgQJkZsEJ7FO4/AACgQGbmT0OPwglCAACgQGZmKkLXI5bCAACgQGdmrUOPwstBAACgQP9/9EIzM3XCAACgQGeGkEO4HqtBAACgQM3M7kLXo/LBAACgQDOzQkOuR4tBAACgQMzMKUOPwhhCAACgQAAAK0IAAKjBAACgQDOzV0MK12nCAACgQJlZEUNSuLzBAACgQJmZ+UAzM0PCAACgQAAAOkNxPU5CAACgQACAIEOk8IDCAACgQM1MvULXo4pCAACgQM0Mi0MzM2RCAACgQJqZMUGPwinBAACgQGaGhEMUrkXCAACgQM2MmEP2KEzBAACgQGZmOUIK12JCAACgQGemLEMAAEDAAACgQDNzlkOPwhfCAACgQDPzC0MfhT/BAACgQABAmEO4HhhCAACgQGamQUPXoxjBAACgQDOzy0LXowBBAACgQM3sqkPNTKZCAACgQABAV0OPwjTCAACgQJqZ9kKamQ/CAACgQM3MhUMpXCDCAACgQAAAlkLXo0jBAACgQDMzYkJmZn1CAACgQDMzU0GF65G/AACgQDOzjENxvYHCAACgQDNzqENmZpZAAACgQGZm5j7XI5rCAACgQM0Mo0OF6+VBAACgQGdmAEKkcBdCAACgQDOzV0MAAN7BAACgQDQz9EI9Ck5CAACgQDOzOUPNzM5BAACgQM1Mh0IfhYFBAACgQGZmp0IAAMDAAACgQJmZmUPheo7BAACgQJpZR0M9Cq9AAACgQGdmg0MUrr/BAACgQDSzp0PXo+JBAACgQDMzpkMUrpBCAACgQACAd0PD9cjBAACgQABADEOamXnCAACgQGdm+0LD9UHCAACgQADAbUPXow5CAACgQADgl0NSuMxBAACgQGamhUOamf3BAACgQGZmZkAUrqFBAACgQADARkOux4fCAACgQDPzpkNmZiXCAACgQGdmkkLNzBJCAACgQGdm7EHNzLxBAACgQAAAh0MK14tAAACgQJoZDEPD9RjBAACgQAAAKEE9CkpCAACgQP9/dENI4eTBAACgQAAgoENxPSPCAACgQJqZlkKkcOXAAACgQM3MgkIAADpCAACgQABAqkPheupBAACgQGaml0OuRztCAACgQJqZYUN7FMhBAACgQM3MREPD9dxBAACgQMzMx0LD9SlCAACgQGYmqEPXo7A/AACgQACAKUPsUfxBAACgQM3MXENxPdRBAACgQACAVkM9Cg5CAACgQGcmhEMfhYlCAACgQDOzPEM9CrVBAACgQAAAhEIfhYtBAACgQJqZQ0K4Hg3BAACgQABAVEOaGZtCAACgQACAqUJSuObAAACgQMysnENcjzFCAACgQJkZIUMK13/CAACgQM2sgUNmZgRCAACgQDPzGkOPwptBAACgQDOzD0Pheh5CAACgQGYmikPsUaTBAACgQM1M8EKuR37CAACgQGYmK0OPwi3BAACgQGbmnEJIYYbCAACgQMzMmkOamalBAACgQGYmnEOkcDhCAACgQMzMmkKamXlBAACgQDOznkJcjwvCAACgQJkZs0Kamc9BAACgQAAA6kFcjz3CAACgQDPTjUOamY5CAACgQDSzp0MpXENBAACgQJmZOkPNzE3CAACgQADArkN7FERCAACgQDMzU0HhenRCAACgQJmZnEKF6xlCAACgQJo5ikO4HoNCAACgQDOzzkKPwqHBAACgQDPTqENI4SrBAACgQADAsUNcj47BAACgQDMzR0IUrldAAACgQACAx0JmZmLBAACgQJqZcENmZrZBAACgQM6MdUMK14M/AACgQM2MG0OamSbCAACgQM1MTEP2KBFCAACgQM1Mf0M9CofAAACgQMxMfEOF6zdCAACgQM3M4kLsUWjBAACgQJqZJUJxPYfCAACgQDMzd0MfhSdCAACgQACA1kLXox7CAACgQAAgjkMzM7vAAACgQJlZjkOkcBTCAACgQGdm2kJcj7rBAACgQM0MC0N7FFhCAACgQM3MXEFI4WtCAACgQGYmZEMAADLCAACgQDQzr0HXozVCAACgQGfmQ0O4HkbCAACgQMzM+kIzMwTCAACgQM2MVENSuMhBAACgQM0MWUNSuA7AAACgQJoZgEPhegjCAACgQGamAkOkcFTCAACgQGfGjEOuR9VBAACgQDMzBUKF6zBCAACgQGemHUOF619CAACgQDOzqkMAAFbCAACgQM3MfUL2KBRBAACgQDQzekMULoFCAACgQGcmJUMUrmdAAACgQJrZAEM9CmjCAACgQGZmqkPXoyXCAACgQMwMcUOF6zXBAACgQM2MCUOPQoZCAACgQJnZlUOamdHBAACgQJoZeEMK1zdBAACgQAAAtEHhesRAAACgQDPzVkPheoJBAACgQDOzqkJxPYRBAACgQMzM5UJxPRnCAACgQABAMEP2KARBAACgQDRTrUMUrm1CAACgQABAe0O4HmNC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AACgQGbmjUMpXLXBAACgQGdmoUJcj2JAAACgQJqZ9kLXo3zBAACgQACAzUK4HidCAACgQGbm3kLhehRBAACgQACAzULhegnCAACgQACAkUPD9exBAACgQDMzOENxPQRCAACgQGdGlEOF6wZCAACgQACACENI4aJAAACgQGaGlkNxPQDCAACgQACA00IUrofAAACgQJmZmT/2KCjBAACgQDOzKkOF6y1CAACgQAAAbUKkcHRCAACgQM0MrEOF60ZCAACgQAAAXkIfhf/BAACgQDMzl0GPwonCAACgQMwMpkP2KAjCAACgQACATUNxPVrCAACgQAAAVUNxPRpAAACgQACAi0KF60HBAACgQDMzUEJxPURCAACgQMzM9EHD9VlCAACgQDSzCUPhejPCAACgQDPzDkPNzDRBAACgQMxMokI9CotBAACgQJnZb0PD9SbCAACgQDRTj0NxPRTCAACgQJk5kEMK12ZCAACgQM1MmUJmZk5CAACgQM1MEENI4dpAAACgQM3Mu0IfhVvBAACgQDOzs0OamYDCAACgQDMz0ELXo2DBAACgQGfmt0IAABXCAACgQJkZA0MK11rCAACgQM2MZkNSuD/CAACgQJrZkkP2KJ5BAACgQM1M20IK10VCAACgQDLzcUNmZohBAACgQDPzL0PD9XnCAACgQGemjkOPwjtCAACgQM3MaEMzs5pCAACgQGfmh0MK1y1CAACgQJrZKkPXoxDCAACgQMwMcUOF6zXBAACgQDQzdELXo2xCAACgQJnZhkMAAPLBAACgQDOzuUIULoPCAACgQAAAvUJ7FHZCAACgQGbGlUMK13fBAACgQAAA4UIpXN9BAACgQM0MgkPhepBBAACgQGamlEO4HplBAACgQMyMD0M9Ck3CAACgQDRzOkN7FBxCAACgQMzMOENcj1zCAACgQACAO0NmZopCAACgQDMzI0CuRzhCAACgQM3MIEP2KIpCAACgQGbmLkPNzILCAACgQJqZ4UC4Ho9BAACgQDOzAEOPwkfCAACgQDMzx0FcjzFCAACgQABAD0PD9ajBAACgQDMzl0L2KNLBAACgQM3MnUKPwgJCAACgQDMzZUPXo49CAACgQDTzI0M9CgZCAACgQMxMokJcjxVCAACgQGamhUOF65lCAACgQGZmhkLNzFBBAACgQGamhUO4HktCAACgQABAHkMAADzCAACgQDMzX0MzMxfCAACgQACAo0MzM/fBAACgQDSziUPhekRCAACgQACgjEMK1wNAAACgQGamkUN7FMTBAACgQJkZUUOF69tBAACgQDMzjkOuR6XBAACgQDRzf0NcjwDCAACgQDIz/UEzM7VBAACgQDNTkkNcjzLAAACgQM3MlEPD9VNCAACgQJm5iEPNzIBCAACgQDPTnEPheuBBAACgQGfmfEOuR8VBAACgQJrZPEM9CpNBAACgQDOz/kJI4TLCAACgQDQzkUKPwoHBAACgQADAVUMpXGTCAACgQACAtUJxPdLBAACgQJlZTUMzMwvBAACgQDNTjEP2KF5CAACgQJlZkUMUrlnCAACgQDPTgUOPwi1BAACgQGdm2kIpXBLCAACgQJoZ6ULD9WjBAACgQGZm1EF7FFbCAACgQDMzREKPwktCAACgQJmZt0KamXjCAACgQGZms0LD9RjBAACgQJpZbkMpXKHBAACgQJmZyUJ7FDJCAACgQDMzNUM9CpnCAACgQM3spEMpXJJCAACgQJqZh0GPwi9CAACgQJm5qUMpXIfAAACgQM3MaEPsUSNCAACgQAAARkOamYHBAACgQMzM5UIpXI1BAACgQDMzZUMK13PCAACgQJoZvELsUURBAACgQM1MSUOux5XCAACgQGcmokPXoyFCAACgQDPTn0PNzC9CAACgQJmZGT+Pwu3BAACgQJlZSkMUrkzCAACgQMzMZUNcj+I/AACgQMxM80J7FDXCAACgQDOz3UJmZshBAACgQGZm5kI9Cq5CAACgQAAAjUJcjxTCAACgQDMzR0JmZkhCAACgQDMzx0JI4UDCAACgQDMzZUK4HsFBAACgQDOzrUNI4QpBAACgQGbmn0IK1wdCAACgQJnZJENSuCFCAACgQM0Mr0P2KKxAAACgQGZmmELsUUbCAACgQACAskO4nodCAACgQJkZ7EIK1wrCAACgQM1spkPXo21CAACgQGdmoULDdZ5CAACgQGcGs0PD9ZhBAACgQJrZgENxPTLCAACgQMzMmkPD9cBBAACgQM3MnUIfhdfBAACgQGYmn0OPQozCAACgQGcmlkNmZkhCAACgQACAF0Phej3CAACgQJqZk0N7FDZCAACgQAAAukKamdHAAACgQAAAXkJI4XxCAACgQACAuEIUronCAACgQGcmJUMK1xxCAACgQDPTk0PD9YDBAACgQM5M+UJSOIPCAACgQJkZwkLsUZjAAACgQM3MoEL2KMbBAACgQM2MV0NSOKDCAACgQJqZQEKamZm/AACgQDRznEOkcE7CAACgQJoZ40KPwrvBAACgQJmZ50IUrjTCAACgQJo5nENxPTBCAACgQDOz4ELD9eBBAACgQJr5ikM9CjfCAACgQDNzgUNSuNTBAACgQM2spUO4HnlCAACgQDOzHkNmZt5AAACgQJmZ2EKuR4FBAACgQJq5o0MpXPFBAACgQADgmkOkcCFBAACgQMxMokIK12O/AACgQDOz6UI9ChrCAACgQM0MEUOuR2XBAACgQJkZ0UKkcAjCAACgQDMzFEOamQHBAACgQDPzLEOPwm3CAACgQDPzDkMfhRtBAACgQM1Mh0LNzDPCAACgQDOzgEO4HiNCAACgQJp5g0OPQohCAACgQAAAeUMUrlNCAACgQGdmj0O4HjFBAACgQGdmD0NI4aHCAACgQJoZjELsUVRCAACgQM3MREEfhYdBAACgQMyMHkMAAL7BAACgQMwspEPNzFjBAACgQM3MkUJSuJZBAACgQDPTjUOamXnBAACgQM3M/UIpXFTCAACgQACAHUMK1yO/AACgQAAAEEEUrlhCAACgQJoZuUIAgJXCAACgQAAAZEJxPTJBAACgQACAqUOuR5NCAACgQGYmH0NxPW7CAACgQABAYEO4HnvCAACgQJrZKkNxPZbBAACgQDNzmUMUrpHBAACgQM3M7kKkcDDCAACgQACAi0LNzHxBAACgQM3MIEMfhblBAACgQM3Mo0KF6wlCAACgQDTTlkNI4bxBAACgQGamQUMUrmTCAACgQDOzy0IAABDBAACgQGbmsUOuRyVBAACgQGcmE0N7FDhCAACgQJp5p0NI4aDCAACgQDOThUPD9cxBAACgQADAbUOPwkXAAACgQADApUM9CjRCAACgQJkZ3UIfhaNBAACgQGZmQkEK15XCAACgQDQz60Fcj45BAACgQDOzEkMfhWRCAACgQDMz30J7FEzCAACgQM3MxEL2KJy/AACgQJmZDUMfhVXCAACgQM1MlkJIYaJCAACgQM2slkOF659BAACgQGfmh0PhevRBAACgQAAAUkLNzF1CAACgQJlZr0PD9URBAACgQJqZBEO4HjrCAACgQJrZKkNm5oHCAACgQGbm9kKuRxHCAACgQGYmVUN7FNrBAACgQDNzE0OF6zbCAACgQDSzckMpXOvBAACgQJkZpEOkcM9BAACgQGfGiUPXoxrCAACgQGZm70LherrBAACgQM0MOENmZm5BAACgQGaGokN7FC1CAACgQAAAMUKkcP9BAACgQM3MSkKF66VBAACgQM1McENcj45BAACgQDSze0NSOIbCAACgQAAgo0OuRxHBAACgQACAgkPXozDCAACgQDMTnEPD9S/CAACgQDMzR0N7FPzBAACgQAAAWENxPbpAAACgQAAAhEIpXBdBAACgQDQzekJmZqFCAACgQAAgiENmZqBBAACgQM2MXUMfhcvBAACgQGbmrkLNzIXCAACgQGcmMUMAANbBAACgQDPTkEMfhYs/AACgQGYGnkPNzJ7BAACgQDNzokMpXA9AAACgQACArEPNzGNCAACgQGcGs0M9CqTCAACgQAAAikM9Ch5CAACgQJoZJ0PNzHfCAACgQJmZSUMAAB/CAACgQJl5qkOuRydCAACgQMzMqUMzM2NCAACgQDNzgUPNzNTBAACgQGbGrUMUrrPBAACgQM3MskE9Cq3BAACgQAAAz0Kux4lCAACgQM1M20LD9RLCAACgQJl5jEOuRxnCAACgQADglENSuCZBAACgQM2MpEMfhX5CAACgQM3MbkMzM4nCAACgQACAmkJSuI7AAACgQM1Mn0P2KCBBAACgQGcGp0MfhSbCAACgQGdmaUKF6wrCAACgQDPzrEMAAAvCAACgQGfmikMzM5PBAACgQDMz5UEAACrCAACgQJpZI0MpXFpCAACgQDPzLEN7FG7CAACgQDNTkkOPwt9BAACgQACgiUPD9eZBAACgQGdmD0M9CkTCAACgQJpZi0MUri/BAACgQGYGm0MzM3LCAACgQGZGmkN7FDLCAACgQJmZN0LD9W7CAACgQM0MC0N7FDHCAACgQGbm50JSuCPCAACgQACAIEOkcINCAACgQDSzp0PNzKzBAACgQM3M60Jcj+I/AACgQM1MmUK4HpXAAACgQGZm10JSuEPCAACgQJkZ3UI9ChNCAACgQM3skkPD9SjBAACgQDOzP0PXo4RBAACgQAAAPUN7FCTCAACgQJqZTEIULoFCAACgQJoZnkM9CprCAACgQGZmZkDD9fjAAACgQABALUNSuPjBAACgQJpZYkOuRyTCAACgQDSzp0LheqzAAACgQAAAnEE9CmdA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AACgQJr5nEMfhVtBAACgQDOzbENmZnpCAACgQGamYkN7FILBAACgQJlZHUMpXC/AAACgQGfmYUOamT5CAACgQGfmmUKamX3CAACgQGbmn0NxvYFCAACgQDOz70IpXHLCAACgQM6MdUMzMwdCAACgQACAREMzMw9CAACgQDOzUUOPwsfBAACgQAAAjUPXoyHCAACgQMzM9ELhejDCAACgQDRTnkMpXAHCAACgQACAL0MAAALCAACgQM0MAkM9CiHCAACgQGbmrkMpXFfBAACgQJoZiUIUrgPBAACgQGdmj0MAAAJCAACgQM3M30KamatBAACgQM1MyUIzM41BAACgQJoZOUOPwrHBAACgQM3MDkJcj77BAACgQACAAkPXozXCAACgQDMz00KPwsFBAACgQGdmgEKkcFZCAACgQJqZsUJcj29CAACgQJoZ2kJcj25CAACgQDOzwkJmZmPCAACgQACAu0KuR5lBAACgQAAA8EAAAO5BAACgQABgikPNzEw+AACgQDMzR0NSuBZBAACgQJpZqUMUrqXBAACgQJoZ1EIpXCnCAACgQJqZk0Fcj/JAAACgQAAAwELhejjBAACgQM1MK0NxPRDCAACgQAAAyUJSuBLBAACgQDMzd0IAAOhBAACgQM3MIEIzM1rCAACgQGZms0LNzAw/AACgQJqZh0HD9SXCAACgQACAwUIUrmlCAACgQABAS0NcjyLBAACgQDRTnkPD9RpCAACgQJqZbUPNzAxAAACgQMxsgkNI4T3CAACgQDOz70IzMx3CAACgQGZmfkL2KCBBAACgQM3MMkKF6zHAAACgQM0soUP2KDpCAACgQJqZNEMzMyxCAACgQGfmw0LXozhBAACgQGYmKEMKV4DCAACgQDQzzUH2KKJBAACgQABABkO4HnVBAACgQJmZK0PhenTCAACgQDPzVkN7FHXCAACgQDOzy0L2KENCAACgQJo5jUMzMwJCAACgQGfm0kI9CrfAAACgQGdmsEPD9RxCAACgQGcmlkPXo4FCAACgQGfmikJ7FB7AAACgQM0MR0MfhZ/BAACgQGdmP0OkcPVAAACgQAAAmUJI4WtCAACgQM3MoEIzMydCAACgQDOz6UKkcDrCAACgQJkZ4EJ7FN5AAACgQP//e0MAAKhAAACgQDOzXUMfhVHCAACgQJlZkUN7FNJBAACgQMzM+kFxPZhCAACgQAAAokGF6+VBAACgQDTzBUOkcN9BAACgQGfmfEOF6yTCAACgQDPzCENSuCpBAACgQGama0MUrhXCAACgQJmZOkJI4cLAAACgQDNzB0OPwlDCAACgQDMzykJcj/jBAACgQM2MIUMfhYDCAACgQM3MQUNI4YJCAACgQJo5lkMfhcdBAACgQMzM9EKPwj/CAACgQAAAXkNSuNDBAACgQACgj0NxPcJAAACgQDOzFUNcjw/CAACgQJnZBkMUrgFCAACgQJpZI0O4HqHBAACgQAAANEF7FLDBAACgQAAA3kIUriJCAACgQGamfUPNzMTAAACgQABgh0NI4QLBAACgQJqZLkN7FAJBAACgQM0MskNxPZLBAACgQACglUOamWvCAACgQADAokN7FLrBAACgQM3sqkNI4ZzBAACgQGZmYELhepS/AACgQM3MgkJcj33CAACgQAAARkI9CnrCAACgQM1M20IK1xzCAACgQJoZiUIfhQ/BAACgQDMzC0MzM8vAAACgQGZmY0LD9b7BAACgQDNzc0OkcD3BAACgQGbmdkMzM/PAAACgQJmZCkOF6yzCAACgQDOzoUL2KGZCAACgQACAYkNxPVpCAACgQDMTikOkcDDCAACgQGdmy0LsUS5CAACgQADgmkMzM2/BAACgQGfm4ULhevQ/AACgQDNzAUMAANLBAACgQACApkMpXGNCAACgQJmZSUHD9UvCAACgQJqZNEJmZlFCAACgQGemd0MK16NBAACgQAAAWEKPwovBAACgQJlZnUOuR4lAAACgQGZmZkLXo8xBAACgQDSzp0IfhZvAAACgQJo5jUNcj6LBAACgQJnZRUPhei3CAACgQDPzrEOFa4ZCAACgQJnZiUPXo45BAACgQJkZ8kIUrgLCAACgQAAAzEHsUQHCAACgQM1Mh0PXo6ZBAACgQM0sqkOF6yBCAACgQDMzKUJxPXvCAACgQJk5hEMAAFrCAACgQGdmj0NmZhBCAACgQDOzjEMULpRCAACgQGYmn0MUrh3CAACgQAAAQEAfhQzCAACgQGfmxkIpXAHCAACgQDOzoUJI4V7BAACgQDMzxELNzBbCAACgQM3MF0KuRxFAAACgQJoZ6ULsUSxBAACgQJpZGkO4HgHBAACgQDNzakNmZiFCAACgQDMzCEKkcAVCAACgQABAGEPXo4PCAACgQDSzxUL2KOBBAACgQDQzL0PNzKpBAACgQDMzKUIUrotBAACgQGamMkMUrs3BAACgQDMzOEIzM/PAAACgQJoZVEMK10VCAACgQJlZaEOF6zrCAACgQACAjkMK19FBAACgQGYmakP2KCBBAACgQJmZCkOuR17CAACgQAAALkNcjz7CAACgQM2MFUM9CkdBAACgQDQzgkKuR4JCAACgQDNzpUPhenxCAACgQM3MHUO4HqVCAACgQAAArkJmZs5AAACgQM0MUENxPVPCAACgQACAC0MzM0zCAACgQDMzI0PD9WTCAACgQDQzgkKamXFBAACgQGdmdUNSOIpCAACgQAAASUIzM6lBAACgQADASUMpXEvBAACgQDPTsUMzM3fBAACgQJqZBEIUrqlBAACgQJoZmEPheiFCAACgQDRzf0OuRwZCAACgQADgo0OkcKXCAACgQAAAtEE9CjxCAACgQJlZjkOF61lBAACgQGamPkNxPXzCAACgQDOzy0JSuBfCAACgQGamX0O4HsXBAACgQJrZAENI4WjCAACgQM1MGUOF60zCAACgQM3MF0JSOJ7CAACgQDTzFEOuR0dCAACgQJoZdUMKV53CAACgQM3M4kKuR31BAACgQJqZpUP2KHtCAACgQGbmvUJxPfrAAACgQJqZhEKambFBAACgQGampkNSOKxCAACgQDOzMEMfhV5CAACgQMzM+kFcj5pCAACgQDMzHUMKV4DCAACgQDMzBUKPwvXBAACgQDMzjkKF627CAACgQDJzeUN7FGBCAACgQDPTk0OuRz1BAACgQGZmmEPD9ZjBAACgQDPzOEOF60NCAACgQM7McUJmZsa/AACgQAAArkJmZl5BAACgQMwMd0O4HoDCAACgQJqZEEPD9SBCAACgQGcmNEOuR53BAACgQGfmCkPD9WJCAACgQADAkEOamTlBAACgQADggkMpXDfCAACgQGdmMEOPwjbCAACgQJpZC0Oux5XCAACgQAAAw0LD9UjCAACgQGdmbELNzEpCAACgQGfmt0IpXJnBAACgQADAAUOuR7XBAACgQJkZ+0LNzJLCAACgQM3MxEF7FJJCAACgQJmZZEOuR+lBAACgQM2MlUNSuDZBAACgQJrZA0MzM/O/AACgQGdmy0JI4WbBAACgQGdGskOkcJXBAACgQGfmmUIpXKNBAACgQM3MzUJ7FK5BAACgQGZGi0N7FATCAACgQDMzO0OuR89BAACgQGbmE0MzM8NBAACgQDPTjUNSOK/CAACgQP+/f0PD9VTCAACgQGdGo0OambdBAACgQADgiEMK16XBAACgQGbmz0IfhbfBAACgQJqZpUPD9Wi/AACgQAAAq0IUrgrCAACgQJp5oUNSuCTCAACgQAAAkEI9CmNCAACgQADAVUOamSFBAACgQM3MfUI9CvdAAACgQMxMsUJcj1LCAACgQM3M7kFxPYFCAACgQM1M20JSuNTBAACgQDMzDkLNzMw+AACgQDMzC0K4HuVBAACgQAAAQEAfhY/BAACgQDMz00FmZsjBAACgQDMzFEIpXO1BAACgQDMzI0CkcKTCAACgQADgoEMpXENCAACgQDMzhUIfhftBAACgQGbmn0K4HkXBAACgQJkZ70J7FA5AAACgQJmZjULhei1CAACgQACAl0K4no7CAACgQM2MGEO4Hk3BAACgQMzsmENI4apAAACgQJqZsUOuRwDCAACgQGfmqEIfheXBAACgQJkZs0MzMy9BAACgQJlZnUOuR4lAAACgQJoZmEMpXMVBAACgQMxMBEP2KFzAAACgQDMz6EJxPc5BAACgQJqZJUEK1x1CAACgQDPzoEPhehRCAACgQDNzCkNI4SxCAACgQM1MjUKF60nCAACgQDMzHUL2KFLCAACgQGZmiUOamcNBAACgQJl5rUNcjzlCAACgQJqZk0PsUYLBAACgQGZGnUPXo7JBAACgQMxsgkO4HuHBAACgQAAAlkPNzArCAACgQGZmVEJxPThCAACgQADAokOPwrVAAACgQDQzEUOF62fCAACgQAAA2EIAACLCAACgQJoZy0LNzGzBAACgQM3MIELhethBAACgQMyMnkMfhanBAACgQGfm80LXo6xBAACgQDMzLEMpXB1CAACgQJmZRkOkcJ/BAACgQDNzN0NI4TbCAACgQAAAMUMAAARBAACgQM3MBUKuR0xCAACgQJoZrUJmZjrCAACgQGfmKEPD9VRBAACgQACABUN7FD7CAACgQDNzn0NSuHLBAACgQDTzd0OuRyfCAACgQDMzC0IfhT1CAACgQDMzC0OamR3CAACgQDOzj0MK14jCAACgQGbm50JSuElCAACgQDTzlEPheh/CAACgQJrZOUPXow3CAACgQM1sl0NmZhDCAACgQDMTgUOkcClBAACgQJoZp0KuR3FA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AACgQM1spkOuR0tCAACgQJkZYENxPVPCAACgQJqZn0IAAAdCAACgQACAi0IK1/tAAACgQGemWUN7FEbCAACgQGZmKkGk8ILCAACgQADglEP2KCzBAACgQGaGmUMK16JCAACgQDQzgkK4HqXBAACgQMxMQEPs0aZCAACgQDOzOUNSuGbCAACgQGeGkENI4TXCAACgQACAbkNxPcTBAACgQDQz3EIpXFDCAACgQGZGi0NmZlFCAACgQDMzo0A9CgNBAACgQAAAHEKPwgpCAACgQACATUP2KGvCAACgQM3MxEDsUXdCAACgQGdmg0I9Cs1BAACgQM2sikNSuP6/AACgQDSTi0OF6xFBAACgQGemnUPXozhCAACgQGbm7UI9Cp9BAACgQGdGgkNxPYzCAACgQGZm8kLD9Z5CAACgQDOzvEI9Ch9BAACgQACAjkO4HkpCAACgQMzMekIK19NBAACgQM2MiUOuR33BAACgQGdmy0J7FE3CAACgQAAAdkOPwhzCAACgQGamlEMAAJHCAACgQM0MgkNI4QFCAACgQM1MW0MfhTjCAACgQDMzjkJmZjJBAACgQGZmckN7FF7AAACgQJrZkkMUrutBAACgQDPzoENI4dxBAACgQM3MfUMUrqdBAACgQAAAK0PherQ/AACgQGZGmkPhelBBAACgQABAp0MfhVbCAACgQJqZzEJxPXLBAACgQGbGrUO4Hh1BAACgQM2MbENmZua/AACgQP//e0GamTnCAACgQDPzDkNmZgxCAACgQGdmLUMpXCLCAACgQAAAlkL2KCTBAACgQADAW0OamTFCAACgQM3Ma0Lhej9CAACgQMzMKUIfhd/BAACgQM1Mh0IfhVtBAACgQDPzaEOux5LCAACgQADgjkPNzGzAAACgQACgjEOPwh7CAACgQM1Mn0P2KCBBAACgQJmZWEMK15lBAACgQACAo0IUro9BAACgQJoZj0OamVVCAACgQDMzZUJxvY1CAACgQJoZj0PD9YDAAACgQM0MDkPD9RBBAACgQM0MSkPNzG9CAACgQDMzDkNmZjZCAACgQM2slkMAAHhCAACgQDMzqUKF6/NBAACgQDNzKEMpXI+9AACgQADAn0OF64/BAACgQM1MxkLNzBlCAACgQGdm3UIpXP/BAACgQACABUPXo/RBAACgQM6MdUPNzAZCAACgQP//e0FI4ZrAAACgQGYmdkOame3BAACgQM3MgkLD9XJCAACgQJpZekPNzPxBAACgQDSTi0NI4QVCAACgQDNzk0OuRytCAACgQM1M1ULXozTBAACgQDMzd0MfhSs/AACgQGYmn0PD9dzBAACgQAAAwEFmZnbBAACgQDPza0NSuHrBAACgQM3MzUKF643CAACgQJrZD0NxPQ9CAACgQGZmNkBmZt7BAACgQDOz5kK4HhrCAACgQDNzPUO4HtnBAACgQGfGiUNxPbpBAACgQJl5gEOPwg9CAACgQJkZg0PD9ag+AACgQMzMVkMAAFBBAACgQGZm4EJxPRvCAACgQADglEOPwmzCAACgQGZmiUPsUehAAACgQM1McENxPQVCAACgQABAm0MpXElCAACgQJqZIkIzM0vCAACgQGYmnENcj4nCAACgQAAANEFI4VxCAACgQDOz3ULNzADCAACgQACAbkMK17/BAACgQJrZTkNI4Y7BAACgQJmZGUCF69/BAACgQM3MxEKF6xDCAACgQGYmdkOuRxbCAACgQAAAQEEzs5VCAACgQABAP0OPwjVCAACgQAAA9kG4HgU+AACgQDPzi0OPwizCAACgQDPzoEO4noVCAACgQDPzCEMzM+1BAACgQMyMrUPsUQhAAACgQJqZ4UDXox/CAACgQDMzWUPNzNzAAACgQDMzGkMUrltBAACgQACAhULXo6FCAACgQACAykKPwudBAACgQDMzqUHD9SXCAACgQJmZmUEAACDAAACgQM3M4kGF6yJCAACgQJrZMEPNzGbCAACgQGbm/0LXo5zBAACgQJm5mkN7FDZBAACgQM3M4kIpXBHCAACgQDMzDkKuR2G/AACgQGdmPEMAAAVCAACgQDOTskMK13hCAACgQMzMmkKuRx1BAACgQAEAc0PNzAjBAACgQABgq0PXoxzCAACgQDMz8UHsUQjBAACgQDOzb0PXo8bBAACgQDMzJkMzMw/CAACgQGeGjUOkcKtBAACgQJrZP0N7lIVCAACgQDSTi0OkcNVAAACgQDOzA0PXozfCAACgQJpZpkPD9TjBAACgQGZmhkMfBZBCAACgQM1srEOPwnU/AACgQJoZHkNxPWJBAACgQDMTpUOamePBAACgQGfmw0JcjwLBAACgQM3MI0IzMzFCAACgQABAJ0PXo+DBAACgQJkZBkPsUavCAACgQAAApUKuR3xCAACgQDOzgEPsUSjBAACgQGemZUMAAHLCAACgQADAakPhelHCAACgQAAA8EFcj6bBAACgQM2MEkNcj5nCAACgQJo5q0OamV9CAACgQDMzDkLheizBAACgQACghkNcj4LAAACgQJqZn0KkcErCAACgQGbmrkMfhUJCAACgQM0srUN7FPbAAACgQMzMqUIfhQLCAACgQM1MnEJm5pNCAACgQJoZRUO4noLCAACgQJmZZEIpXLtBAACgQMzMdEMUrirCAACgQGZmSELDdZtCAACgQGfmmUNI4URCAACgQM3Mu0LNzEhBAACgQAAAakOamTHCAACgQDQzekKkcN3AAACgQDTzBUN7FDpBAACgQM2MM0OuR2HCAACgQJqZMUJI4b7BAACgQABgikPhekDCAACgQGZmZkGF6+dBAACgQJoZ2kKkcHnBAACgQABABkPXowNCAACgQDOz10KamZm+AACgQGemcUO4nodCAACgQDOzIUNmZorBAACgQM1MEEMUrlbCAACgQGaml0OuR4G/AACgQMxMz0LXo5C/AACgQGdm7EKkcBHCAACgQJoZjEIzM0NBAACgQDSzNkPNzLzBAACgQAAAeUK4HoPBAACgQJkZEkP2KN7BAACgQDTzbkPNzBdCAACgQDQzdEIzMwNBAACgQACAI0PsUQjAAACgQDMz0EIK157CAACgQAAAHEFmZkVCAACgQAAAH0IUrq9BAACgQDQzdENxPVfCAACgQM0MO0P2KBzCAACgQM3MpkP2KN7BAACgQGfm4UJSuIhBAACgQAAAYULsUcJBAACgQJnZY0NmZl3CAACgQM3M0EI9CsXBAACgQGZmOUJ7FMpBAACgQACgnkOF67HCAACgQACApkI9ChrCAACgQADAB0OamcNBAACgQM3MFEGamQ1CAACgQJoZy0LXI59CAACgQACADkN7FE7CAACgQM3MXEFcD4vCAACgQGamkUMUrr/BAACgQADAqEPXo51CAACgQM3MQUNxParCAACgQGbmAUOPwlXCAACgQM1MAUNmZnvCAACgQGambkPheqJBAACgQAAAf0KPwnjCAACgQDPziEMK1zDCAACgQM2MpEMAABXCAACgQJk5k0PNzJTAAACgQADAYUNcjwLCAACgQGfm/EKamSjCAACgQM3MQUMUrivCAACgQDQzr0JSuJ7CAACgQJqZgUIzM0hCAACgQJoZ10KF69vBAACgQM3MXEE9Cr1BAACgQACAfUNxPSfCAACgQDMTjUNSuC7CAACgQJqZpUJcj1JAAACgQMxMc0PXo6BAAACgQDPzDkP2KJLCAACgQDQzoEIpXCRCAACgQM0MkUMK12O/AACgQDPzdEMK1x5CAACgQDPzPkNcj+LBAACgQM1Mt0JI4bjBAACgQJrZTkOuR0nCAACgQACglUPheiFCAACgQJoZrULsUUdCAACgQAAApULsUTzCAACgQDQzdEJI4cBBAACgQJlZAkOF653CAACgQJmZ2EJSuE1CAACgQGbmZ0N7FBPCAACgQGamU0PD9RTBAACgQGcmeUOkcCvCAACgQMzMi0KPwmXBAACgQDNzLkMAAD/CAACgQJmZZ0MpXAlCAACgQGbmTEMUrlNCAACgQDPTjUNmZiZCAACgQADAkENxPbhBAACgQADADUMzM4/CAACgQABAIUOk8KDCAACgQJk5okNcj5pBAACgQAAgi0OF623BAACgQGcmcENxPYTBAACgQDMzU0F7FMDBAACgQJoZSEMAAFPCAACgQJlZO0NI4cBBAACgQJk5k0PNzGTBAACgQADAnEOPwgVCAACgQGZmCUPXowzBAACgQDOzpEIzM8lBAACgQJqZUkOkcNvBAACgQDNzBEOuRyHCAACgQDOzAEPNTIJCAACgQGZmVEJ7FEBCAACgQAAAh0IUrqZCAACgQJqZXkN7FBVCAACgQGamiEMpXCXCAACgQM1Mh0I9CntBAACgQAAgi0NmZvRBAACgQGYGm0NSuAXCAACgQAAANEJI4TxCAACgQM3MO0Ncj8RBAACgQM1MH0OF62rCAACgQGZmhkNmZtBBAACgQJo5qEM9io1CAACgQDMz1kJI4X5BAACgQJkZkkI9Cn5CAACgQJkZoUMfBYvCAACgQGemOEPsUZZCAACgQMxM7UJxPT5CAACgQMzsiUN7FJbCAACgQAAAJUO4HjZCAACgQJqZk0JI4ZXCAACgQJmZqEMfhRzCAACgQDOzrUIK141BAACgQAAA6kI9CkPBAACgQDSze0MfhR3CAACgQAAA7UIfhQ/CAACgQDOznkLsUVjBAACgQGfGm0PNzBzCAACgQM3MvkLsUTzBAACgQMysnENSuDRCAACgQGZmKkJSuA7CAACgQGbmXkP2KG1CAACgQGama0OkcFFCAACgQGZGiEMAAHjCAACgQDSzckOPwpdCAACgQACAoEN7FE7BAACgQJpZCEO4HkJCAACgQM3MaEIfhavAAACgQJqZfEMAAOhAAACgQGdmPEMzMxfBAACgQJqZLkMzM1PBAACgQAFAfkM9CgXCAACgQGamoEMK12PAAACgQABAYENcjzbBAACgQJqZEEKPwilBAACgQACAAkNmZkDCAACgQGbmrkIUrmfBAACgQACAskPXozDAAACgQADAPUPheqhBAACgQJoZ6ULXo9jAAACgQM0MGkPheupBAACgQGamAkNmZlTCAACgQDMzi0NxPcDBAACgQABAp0MfBZvCAACgQJn5k0MfhXfBAACgQMzMOEH2KLTBAACgQJk5sUMzM5NBAACgQM3MiEMUrq3BAACgQAAARkK4HjBCAACgQACADkNSuNTBAACgQGdmbELsUQxCAACgQGYGj0NmZvjBAACgQJn5h0OamRFCAACgQAAAOkIK155CAACgQGfmc0OF6wXBAACgQM2MIUM9CmNBAACgQGZm1EGuR0vCAACgQAAAukHhehPCAACgQDMzC0MK1wvBAACgQDPzO0MUrm3CAACgQAAgnUMzMxvBAACgQACAuEKkcLPBAACgQDOzj0KkcIfBAACgQDMzpkLheohBAACgQABAaUPsUZi/AACgQJrZHkNxPR7CAACgQAAA9kG4HgVCAACgQDMzlEKkcKXAAACgQGfmt0KamSjCAACgQM2MTkNI4UbBAACgQACA00JSuGzCAACgQDRzjUNI4VNCAACgQDMzmkL2KMJBAACgQJoZaUMpXJ3BAACgQM3spEOamRnCAACgQJmZSUK4Hr3BAACgQDOzLUNcj0TCAACgQGYGsEPD9a7CAACgQM3MrEGF65lBAACgQDMzrEP2KKZBAACgQJqZH0KF6z3BAACgQDMzo0BxPYvCAACgQM2MIUO4HpdBAACgQM1MTEPheo5CAACgQADgoEMzMyrCAACgQDQz3EJxPVHCAACgQJqZTEKPwsVBAACgQMyMeEPh+o3CAACgQGfGmEOamcNBAACgQJn5k0P2KM5BAACgQAAAFkNxPWNCAACgQJpZiEPhelRAAACgQGZmckMzMwbCAACgQJqZgUK4HqlBAACgQACAnUK4HqVAAACgQJmZeUFmZvzBAACgQAAAeUMAAIPCAACgQP//e0FmZv5BAACgQDOzTkP2KBpCAACgQAAArkHD9VDBAACgQM0MIEMzM4LCAACgQM1sjkOkcE3BAACgQACAl0PhetZBAACgQJmZDUEAAEDCAACgQGaGlkPD9ehAAACgQM3MhUNcj+pBAACgQACA/UIULozCAACgQDMzO0EfhT9BAACgQAAAeUJI4ZxBAACgQDOTjkPXo15CAACgQJoZZkPD9exBAACgQGdGlEOuR//BAACgQJkZhkK4nqdCAACgQGfmfEP2KJw/AACgQM3MIEPXo9JBAACgQGYmkEPhekTBAACgQM3MiEHNzLRAAACgQGeGjUNmZjrCAACgQGZmNkLNzI/CAACgQAAAvUKPwp/BAACgQJnZlUMK1/dBAACgQM3MCEKkcM3AAACgQM3MlEIzM4lBAACgQDMzREKux5rCAACgQM0MgkPD9ThCAACgQGbmokOamSRCAACgQADAqEOPwo1AAACgQM1srEMUruvBAACgQDNzEEOPwizCAACgQABANkOF6yTCAACgQGbmn0PhempCAACgQGZGnUOkcC1BAACgQACAWUOuRzXCAACgQJpZl0MzM9dBAACgQADAJUPNzDTBAACgQGZmeEL2KBhCAACgQACAHUMAgKhCAACgQJmZK0KuR3FBAACgQJn5h0MzM4nBAACgQABgnEPXoxfCAACgQAAAZEIpXPHBAACgQADgo0OPQovCAACgQGbm9kLNzEjCAACgQM3Mo0KF631BAACgQJqZk0I9Cl1CAACgQAAAHENI4QZCAACgQACAGkOF6+fBAACgQM2skEPsUYXCAACgQABAS0OPwnXBAACgQDMzskI9CvvBAACgQJoZOUPD9dRBAACgQJpZpkOPQoxCAACgQABgqEOPQoJCAACgQGZm8kKkcFTCAACgQM1MxkJSuKbAAACgQDQzzUHD9VbCAACgQM1MYUMAADDAAACgQABAkkOF69fBAACgQM3MJkNI4fo/AACgQDOzqkMzMzrCAACgQM0MZUMzM7PBAACgQP//+0JI4VRCAACgQACA/ULhekjBAACgQACA8UJxPSXCAACgQJoZnkKkcDVBAACgQMzsiUPD9RDCAACgQADAYUMK1wxCAACgQADggkPXo6BBAACgQM1MdkNcjxVCAACgQGZmYEN7FC6+AACgQJqZgUPXowLCAACgQM3Mr0NI4VLCAACgQJoZm0NxPQFCAACgQM1so0OPwnJCAACgQACAhUIAALhBAACgQJmZmT4pXEnCAACgQABAaUPNzIbBAACgQGcGpEPsUeZBAACgQDNzVUMpXE9BAACgQM3MXEJSuKbAAACgQJr5kEOF60VBAACgQJqZpUJI4abBAACgQDPzdEOuR/fBAACgQADArkOuRyPCAACgQGcmFkOFa6TCAACgQDRTj0M9CuVBAACgQMzsp0N7FMbBAACgQDPTq0P2KILBAACgQDPThEPNzBPCAACgQGYGnkNxPbrAAACgQDMzwUL2KGFCAACgQGbmgUNcjzrCAACgQGZGiEMK1/XBAACgQM1MVUMUrlbCAACgQAAAeUOamVNCAACgQMyMfkNcj0rCAACgQGZmhkLXo7A/AACgQDTzMkNmZnpBAACgQGcmcENcjwbBAACgQDMzjkIpXJXBAACgQJpZUEPXow7CAACgQJm5pkOPwjVCAACgQAAAHEJcj/rAAACgQGZm4EGamVxCAACgQJmZqELsUYhBAACgQGfmVUOamZHBAACgQGdmoUMK13FCAACgQJoZrUPXo4nCAACgQJmZjUIp3I3CAACgQMxMbUPhenTAAACgQDOzlUPsUYZBAACgQJqZrkMK11RCAACgQABAZkNxPbrAAACgQGcGlUOF68dBAACgQAAAbUNI4YLCAACgQJrZg0MK1zPBAACgQAAARkLsUXrCAACgQACgpEOuR0FBAACgQGemLEOuR5NBAACgQM1snUOPwm1CAACgQJoZuUJmZiHCAACgQDMzI0BmZjJBAACgQGdmoUK4HiRCAACgQM1MCkPXo23CAACgQABAb0Ncj2ZBAACgQGZmwkFxPUJBAACgQDQzr0LD9WRBAACgQGbm3kIULp7CAACgQGdm9ULNzFzBAACgQAAAzELNzF5CAACgQJlZL0NmZizCAACgQABAOUOkcGdCAACgQADAhEMzM39BAACgQJqZtELXowfCAACgQGdmvELD9RbCAACgQJr5nEO4HklBAACgQDMTgUPhentCAACgQADAH0MUrlZCAACgQJqZQEK4HtVAAACgQDQzvkJcjwjCAACgQDNzrkOux41CAACgQDMzSkJI4dxBAACgQAAAn0L2KCtCAACgQACAwUIpXI++AACgQDNzQEPh+o/CAACgQM3MwUJxPTpAAACgQM3MgkGPwqtBAACgQJqZW0IK10NAAACgQABgpUPXo9DAAACgQGdmXULsURZCAACgQJkZTkOkcIHBAACgQDQzgkNI4R9CAACgQAAA50IpXGNBAACgQDQzgkIUrktCAACgQDRzdkOF651CAACgQJoZV0NxPSzCAACgQGYmW0MAADnCAACgQMxMsUIK1x/CAACgQAAAK0MfhZDCAACgQJkZ4EKuR+nBAACgQAGAekMpXJ/BAACgQAAA1UJI4QlCAACgQGemrEPNzObBAACgQGcmNEM9CifBAACgQDMzR0EUrt1BAACgQDRTnkNcjyXCAACgQJo5pUMAAGhCAACgQGbm+UIfhZdCAACgQM3MrD9mZlZBAACgQGZGnUNI4TFCAACgQJrZsEPD9SBCAACgQGZmBkHheuzBAACgQJpZskPNzGVCAACgQJrZe0MpXGnCAACgQGeGnEPsUbDAAACgQJoZ1EKkcC7CAACgQACAXENcjzpBAACgQM3MxEGF62dCAACgQJqZE0JmZv5BAACgQGdmkkNxPUlCAACgQGbmBEOPwuXAAACgQJqZh0GF66VBAACgQAAAWEI9CjPBAACgQAAgjkNcjyJAAACgQJqZLkMfhS5CAACgQM3MjkFmZoBCAACgQADAE0MAAHvCAACgQDPziEPXoyhCAACgQDPzEUNcj1fCAACgQM0MMkPD9WPCAACgQGZmDEOPwijCAACgQJr5jUM9CoPBAACgQACA/UJmZkpBAACgQJlZr0MfhXBCAACgQAAAZEGkcHFCAACgQDOzD0O4Hr3AAACgQDNTlUNcj8JBAACgQGbmrkMK16PAAACgQDRTgEPXo7BBAACgQADAOkPsUQPCAACgQGYmrkMUrmRCAACgQDOTr0MfhQFCAACgQDQzTUN7lKvCAACgQJmZqEPh+obCAACgQDNzrkMAAGDAAACgQDPThEPhesRBAACgQGZmM0KkcB3AAACgQMzMuEKuR2nBAACgQGdmv0LD9YvCAACgQJkZpEKF6xTCAACgQAAAYUKamZm+AACgQM0MAkN7FFTCAACgQM1MB0OkcCfCAACgQGYmCkOkcC7CAACgQDOzZkPsUR5CAACgQJoZjEIK18PBAACgQM1MWENmZojCAACgQMyMj0PsUZrBAACgQADgr0O4nq5CAACgQJqZ6kLXo2HCAACgQAFAfkMzM7nBAACgQGZmckNSuGfCAACgQDQzzUF7FIhCAACgQM3MxEJcj2JBAACgQDOzoUNmZjPCAACgQM3MkUNxPa7BAACgQGYmEEOuR/lBAACgQGdmHkPD9QtCAACgQJkZBkMzM4FCAACgQAAAnEPsUcBAAACgQDPza0N7FAJCAACgQM3MCEHhelHCAACgQDMz0z9mZoBCAACgQGZmBkIUrofCAACgQAAADUKamUhCAACgQABgpUOamTJCAACgQJqZB0LsUchBAACgQGbmTEMAACxBAACgQJqZkEIfhftBAACgQJlZHUNI4fTBAACgQJpZo0OF6zXBAACgQGdmg0N7FC5AAACgQAAAEEAUrpFBAACgQM3M7kG4HijCAACgQJoZRUMUrg/BAACgQGZGiENI4RFCAACgQGZmUUJSuErBAACgQAAAQEGPwq3BAACgQM3MjkE9CoJCAACgQDMzi0GuR51BAACgQM3Ma0Ok8KjCAACgQACgs0MK115CAACgQM2MV0P2KAvCAACgQGbmIkOuR21CAACgQJpZekM9CkPCAACgQACAPkOF69lAAACgQGdmbENmZq5AAACgQM0skkOamWlBAACgQGamJkOPwo3CAACgQACA2ULsUdjBAACgQAAAwEB7FFBCAACgQACAkUIK11XCAACgQDOzEkM9CjPCAACgQGbmjUMK19NAAACgQGdmdUIzM93BAACgQAAgr0MpXPNBAACgQJqZw0LsUU/CAACgQGdmPEOuR03BAACgQMzM+kLXo6DBAACgQDMzWUMpXEdCAACgQM3MMkM9CgdBAACgQDRzK0MUroNBAACgQDPziENxPXzCAACgQMwshkOamQvCAACgQGYmikN7FPbBAACgQGfGmEP2KDdCAACgQADAHEMfhVvCAACgQJm5pkOkcMnBAACgQM3MUEJmZkdCAACgQAAACkIzM59BAACgQGbmgULXo2BBAACgQDNTmEMAABRCAACgQGZm5j5I4XRCAACgQAAAh0JxPXpBAACgQDMz5UIzMwxCAACgQAAAhEGkcB3BAACgQADAjUNI4RNCAACgQGdmMEL2KJBBAACgQDIz/UK4Ht9BAACgQAAgoENI4b5BAACgQAAAxkFxParBAACgQDNzkEOuR3FBAACgQACAo0MzMz3CAACgQDOz5kIfhUBCAACgQAAgpkMUrgRCAACgQJlZHUOuR+3BAACgQDNzNEP2KBBCAACgQGZm0UKuR8vB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\";\n","runtime/sky-math.mjs":"// Port of Khnum PlanetariumEngine / StarfieldRenderFeature coordinate conventions.\nconst D=Math.PI/180,wrap=n=>((n%360)+360)%360;\nexport const julianDate=instant=>Math.floor(Number(instant)/1000)/86400+2440587.5;\nexport const siderealDegrees=(jd,longitude)=>wrap(280.46061837+360.98564736629*(jd-2451545)+longitude);\nexport function raDec(ra,dec,radius=100){const a=ra*D,d=dec*D;return [Math.cos(d)*Math.sin(a)*radius,Math.sin(d)*radius,Math.cos(d)*Math.cos(a)*radius];}\nexport function observerDirection(direction,jd,latitude,longitude){const a=-siderealDegrees(jd,longitude)*D,b=(latitude-90)*D,[x,y,z]=direction,xx=Math.cos(a)*x+Math.sin(a)*z,zz=-Math.sin(a)*x+Math.cos(a)*z;return [xx,Math.cos(b)*y-Math.sin(b)*zz,Math.sin(b)*y+Math.cos(b)*zz];}\nexport function celestialBodies(jd){const d=jd-2451545,e=(23.439-.00000036*d)*D,g=(357.529+.98560028*d)*D,L=(280.459+.98564736*d+1.915*Math.sin(g)+.020*Math.sin(2*g))*D;\n const sun=[wrap(Math.atan2(Math.cos(e)*Math.sin(L),Math.cos(L))/D),Math.asin(Math.sin(e)*Math.sin(L))/D];\n const lm=(218.316+13.176396*d)*D,m=(134.963+13.064993*d)*D,f=(93.272+13.229350*d)*D,l=lm+6.289*Math.sin(m)*D,b=5.128*Math.sin(f)*D;\n const moon=[wrap(Math.atan2(Math.sin(l)*Math.cos(e)-Math.tan(b)*Math.sin(e),Math.cos(l))/D),Math.asin(Math.sin(b)*Math.cos(e)+Math.cos(b)*Math.sin(e)*Math.sin(l))/D];return [sun,moon];\n}\nexport function decodeSkyCatalog(base64){\n const bytes=Uint8Array.from(atob(base64),c=>c.charCodeAt(0)),view=new DataView(bytes.buffer);if(bytes.length<4||bytes.length>64*1024*1024)throw Error('Invalid sky catalog.');const count=view.getInt32(0,true),offset=4+count*12;if(count<0||offset>bytes.length)throw Error('Invalid star section.');const lineCount=offset===bytes.length?0:view.getInt32(offset,true);if(lineCount<0||offset+(offset===bytes.length?0:4)+lineCount*16!==bytes.length)throw Error('Invalid constellation section.');\n const positions=new Float32Array(count*3),colors=new Float32Array(count*3),magnitudes=new Float32Array(count),lines=new Float32Array(lineCount*6);\n function scalar(offset){const n=view.getFloat32(offset,true);if(!Number.isFinite(n))throw Error('Invalid sky coordinate.');return n;}\n for(let i=0;i<count;i++){const p=4+i*12,ra=scalar(p),dec=scalar(p+4),mag=scalar(p+8),t=Math.max(0,Math.min(1,(mag+1.5)/8));positions.set(raDec(ra,dec),i*3);magnitudes[i]=(Math.floor(Math.max(0,Math.min(255,mag*4+128)))-128)/4;colors.set([.8+.2*t,.9+.1*t,1-.2*t].map(v=>Math.floor(v*255)/255),i*3);}\n for(let i=0;i<lineCount;i++){const p=offset+4+i*16;lines.set([...raDec(scalar(p),scalar(p+4)),...raDec(scalar(p+8),scalar(p+12))],i*6);}return {positions,colors,magnitudes,lines,count,lineCount};\n}\n","runtime/sky-renderer.mjs":"import {skyCatalogBase64} from './sky-catalog.mjs';\nimport {decodeSkyCatalog,julianDate,siderealDegrees,celestialBodies,raDec} from './sky-math.mjs';\nconst gradientFragment=`precision highp float;varying vec3 direction;uniform vec3 zenith,horizon,bandColor;uniform float bandStrength,bandEnabled;\nfloat hash3(vec3 p){p=fract(p*.3183099+.1);p*=17.;return fract(p.x*p.y*p.z*(p.x+p.y+p.z));}\nfloat noise3(vec3 x){vec3 p=floor(x),f=fract(x);f=f*f*(3.-2.*f);return mix(mix(mix(hash3(p),hash3(p+vec3(1,0,0)),f.x),mix(hash3(p+vec3(0,1,0)),hash3(p+vec3(1,1,0)),f.x),f.y),mix(mix(hash3(p+vec3(0,0,1)),hash3(p+vec3(1,0,1)),f.x),mix(hash3(p+vec3(0,1,1)),hash3(p+vec3(1,1,1)),f.x),f.y),f.z);}\nvoid main(){vec3 d=normalize(direction);vec3 c=mix(horizon,zenith,d.y*.5+.5);if(bandEnabled>.5){float band=1.-smoothstep(0.,.6,abs(d.y+d.x*.3));float n=.5*noise3(d*4.)+.25*noise3(d*4.*2.02);c+=bandColor*band*n*bandStrength;}gl_FragColor=vec4(c,1.);}`;\nconst starVertex=`attribute float magnitude;attribute vec3 tint;varying vec3 appearance;uniform float starSize,pixelHeight,fovScale;void main(){vec4 p=modelViewMatrix*vec4(position,1.);float scale=max(.2,(6.-max(-2.,magnitude))*.5);gl_PointSize=clamp(starSize*scale*pixelHeight/(fovScale*max(.1,-p.z)),.65,80.);gl_Position=projectionMatrix*p;appearance=tint*max(.5,(6.-magnitude)/3.)*3.;}`;\nconst starFragment=`precision highp float;varying vec3 appearance;void main(){float d=length(gl_PointCoord-.5)*2.;float a=1.-smoothstep(.1,1.,d);gl_FragColor=vec4(appearance*a,a);}`;\nconst bodyVertex=`attribute float body;varying float bodyId;uniform float starSize,pixelHeight,fovScale;void main(){vec4 p=modelViewMatrix*vec4(position,1.);gl_Position=projectionMatrix*p;gl_PointSize=clamp(starSize*4.*pixelHeight/(fovScale*max(.1,-p.z)),2.,100.);bodyId=body;}`;\nconst bodyFragment=`precision highp float;varying float bodyId;uniform float elapsed;float h(vec2 p){return fract(sin(dot(p,vec2(12.9898,78.233)))*43758.5453);}float n(vec2 p){vec2 i=floor(p),f=fract(p);f=f*f*(3.-2.*f);return mix(mix(h(i),h(i+vec2(1,0)),f.x),mix(h(i+vec2(0,1)),h(i+vec2(1,1)),f.x),f.y);}void main(){vec2 uv=gl_PointCoord,xy=(uv-.5)*2.;float d=length(xy);if(d>1.)discard;vec3 normal=normalize(vec3(xy,sqrt(1.-d*d)));float diffuse=max(.2,dot(normal,normalize(vec3(1.))));vec3 c;if(bodyId<.5){c=vec3(1.,.9,.2)*(.8+.4*n(uv*20.+elapsed));diffuse=1.;}else c=vec3(.8)*(.6+.4*n(uv*15.));gl_FragColor=vec4(c*diffuse,1.);}`;\nexport function createSkyRenderer({THREE,renderer,invalidate=()=>{},active=()=>true}){\n let config={enabled:false},disposed=false,scene,camera,group,stars,lines,bodies,gradient,catalog,preview=false,unavailable=false;\n const resources=[],vector=new THREE.Vector3(),rotationX=new THREE.Quaternion(),rotationY=new THREE.Quaternion(),axisX=new THREE.Vector3(1,0,0),axisY=new THREE.Vector3(0,1,0);\n function rgb(value){const n=parseInt(value.slice(1),16);return new THREE.Vector3((n>>16)/255,((n>>8)&255)/255,(n&255)/255);}\n function own(value){resources.push(value);return value;}\n function geometry(positions){const g=own(new THREE.BufferGeometry());g.setAttribute('position',new THREE.BufferAttribute(positions,3));return g;}\n function init(){if(scene)return;catalog=decodeSkyCatalog(skyCatalogBase64);scene=new THREE.Scene();camera=new THREE.PerspectiveCamera(70,1,.1,500);group=new THREE.Group();scene.add(group);\n  const backgroundMaterial=own(new THREE.ShaderMaterial({vertexShader:'varying vec3 direction;void main(){direction=position;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.);}',fragmentShader:gradientFragment,side:THREE.BackSide,depthWrite:false,depthTest:false,uniforms:{zenith:{value:rgb('#000000')},horizon:{value:rgb('#0d0d0d')},bandColor:{value:rgb('#0d1321')},bandStrength:{value:.6},bandEnabled:{value:1}}}));gradient=new THREE.Mesh(own(new THREE.SphereGeometry(200,24,16)),backgroundMaterial);gradient.renderOrder=-4;gradient.frustumCulled=false;scene.add(gradient);\n  const starGeometry=geometry(catalog.positions);starGeometry.setAttribute('magnitude',new THREE.BufferAttribute(catalog.magnitudes,1));starGeometry.setAttribute('tint',new THREE.BufferAttribute(catalog.colors,3));\n  const uniforms={starSize:{value:.54},pixelHeight:{value:900},fovScale:{value:1}};\n  stars=new THREE.Points(starGeometry,own(new THREE.ShaderMaterial({vertexShader:starVertex,fragmentShader:starFragment,uniforms,transparent:true,blending:THREE.AdditiveBlending,depthTest:false,depthWrite:false})));stars.renderOrder=-3;stars.frustumCulled=false;group.add(stars);\n  lines=new THREE.LineSegments(geometry(catalog.lines),own(new THREE.ShaderMaterial({vertexShader:'void main(){gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.);}',fragmentShader:'uniform vec3 tint;uniform float opacity;void main(){gl_FragColor=vec4(tint,opacity);}',uniforms:{tint:{value:rgb('#3366cc')},opacity:{value:.12156863}},transparent:true,blending:THREE.AdditiveBlending,depthTest:false,depthWrite:false})));lines.renderOrder=-2;lines.frustumCulled=false;group.add(lines);\n  const bodyGeometry=geometry(new Float32Array(6));bodyGeometry.setAttribute('body',new THREE.BufferAttribute(new Float32Array([0,1]),1));bodies=new THREE.Points(bodyGeometry,own(new THREE.ShaderMaterial({vertexShader:bodyVertex,fragmentShader:bodyFragment,uniforms:{starSize:{value:.54},pixelHeight:{value:900},fovScale:{value:1},elapsed:{value:0}},transparent:true,depthTest:false,depthWrite:false})));bodies.renderOrder=-1;bodies.frustumCulled=false;group.add(bodies);\n }\n function set(value){if(disposed)return;config=value&&value.schemaVersion===1?value:{enabled:false};preview=value?.preview===true;invalidate({layout:true});}\n function render(now,width,height,pixelRatio=1){if(!config.enabled||disposed)return false;init();const time=config.clock==='fixed'?Date.parse(config.instant):Date.now(),jd=julianDate(time),r=Math.PI/180;\n  camera.fov=config.fov;camera.aspect=width/height;camera.updateProjectionMatrix();const a=config.azimuth*r,e=config.elevation*r;camera.lookAt(vector.set(Math.cos(e)*Math.sin(a),Math.sin(e),Math.cos(e)*Math.cos(a)));camera.updateMatrixWorld();\n  group.quaternion.copy(rotationX.setFromAxisAngle(axisX,(config.latitude-90)*r).multiply(rotationY.setFromAxisAngle(axisY,-siderealDegrees(jd,config.longitude)*r)));\n  const u=gradient.material.uniforms;u.zenith.value.copy(rgb(config.zenith));u.horizon.value.copy(rgb(config.horizon));u.bandColor.value.copy(rgb(config.milkyWayColor));u.bandStrength.value=config.milkyWayIntensity;u.bandEnabled.value=config.milkyWay?1:0;\n  stars.visible=config.stars;lines.visible=config.constellations;bodies.visible=config.bodies;\n  for(const object of [stars,bodies]){const uniforms=object.material.uniforms;uniforms.starSize.value=config.starSize;uniforms.pixelHeight.value=height*pixelRatio;uniforms.fovScale.value=2*Math.tan(camera.fov*r/2);}\n  lines.material.uniforms.tint.value.copy(rgb(config.lineColor));lines.material.uniforms.opacity.value=config.lineOpacity;\n  if(config.bodies){const positions=bodies.geometry.attributes.position;celestialBodies(jd).forEach(([ra,dec],i)=>positions.array.set(raDec(ra,dec,90),i*3));positions.needsUpdate=true;bodies.material.uniforms.elapsed.value=now/1000;}\n  renderer.render(scene,camera);return true;\n }\n const timer=setInterval(()=>{if(config.enabled&&config.clock==='realtime'&&active())invalidate();},1000);\n async function load(){const key=new URL(globalThis.location?.href??'https://tragikminded.com/').searchParams.get('cms_sky_preview'),root='https://us-central1-neoknown-server.cloudfunctions.net/';if(key&&!/^[a-zA-Z0-9_-]{43}$/.test(key))return;\n  try{const response=await fetch(key?root+'cmsAdmin/sky-preview/main/'+key:root+'cmsDelivery/delivery/main/sky',{credentials:'omit',cache:'no-store',redirect:'error',signal:AbortSignal.timeout(10000)});if(!response.ok)throw Error('Sky settings unavailable.');set(await response.json());unavailable=false;}catch{if(!disposed){preview=!!key;unavailable=true;config={enabled:false,preview:!!key,unavailable:true};invalidate({layout:true});}}\n }\n return {load,set,render,get settings(){return config;},get diagnostics(){return {enabled:!!config.enabled,preview,status:unavailable?'unavailable':'ready',version:config.version??0,stars:catalog?.count??0,constellationSegments:catalog?.lineCount??0,bodies:!!config.bodies,clock:config.clock??null,instant:config.clock==='fixed'?config.instant:null,latitude:config.latitude??null,longitude:config.longitude??null};},destroy(){disposed=true;clearInterval(timer);for(const resource of resources)resource.dispose();scene?.clear();}};\n}\n","settings.json":"{\n  \"resolutionLimit\": 2,\n  \"animationFps\": 30\n}\n","styles.css":"/* Browser host only. All visible page geometry and controls live in Pixi. */\n:root {color-scheme:dark;background:#080a0d;font-family:Arial,sans-serif}\n* {box-sizing:border-box}\nhtml,body {margin:0;min-height:100%;width:100%;overflow-x:hidden}\nbody {overflow-y:auto;background:#080a0d;color:#fffdf5}\n.world-engine {min-height:100dvh;isolation:isolate}\n.world-canvas-host {position:fixed;inset:0;z-index:1;pointer-events:none}\n.world-canvas {display:block;width:100%;height:100%;pointer-events:auto;touch-action:pan-y pinch-zoom;outline:none}\n.world-source {position:fixed;left:-100000px;top:0;width:1280px;height:1px;overflow:hidden;opacity:0;pointer-events:none}\n.world-overlays {position:fixed;inset:0;z-index:2;pointer-events:none}\n.world-engine[data-world-state=\"failed\"] .world-source,\n.world-engine[data-world-state=\"stopped\"] .world-source {position:static;width:auto;height:auto;overflow:visible;opacity:1;pointer-events:auto;max-width:70rem;padding:2rem;margin:auto;line-height:1.6}\n.world-engine[data-world-state=\"failed\"] .world-canvas-host {display:none}\n.world-engine[data-world-state=\"failed\"] .world-source label,\n.world-engine[data-world-state=\"stopped\"] .world-source label {display:block;margin-block:1rem}\n.world-engine[data-world-state=\"failed\"] .world-source input,\n.world-engine[data-world-state=\"failed\"] .world-source select,\n.world-engine[data-world-state=\"failed\"] .world-source textarea {display:block;max-width:100%;padding:.5rem}\n.world-source img {max-width:100%}\n.sr-only {position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n@media print {\n  :root {color-scheme:light;background:white;color:black}\n  .world-engine {height:auto!important}\n  .world-canvas-host,.world-overlays {display:none}\n  .world-source {position:static;width:auto;height:auto;overflow:visible;opacity:1;pointer-events:auto}\n}\n/* Optional media preferences remain accessible outside the Pixi canvas. */\n.tragik-consent-entry{position:fixed;left:12px;bottom:12px;z-index:10020;padding:8px 12px;border:1px solid #8395a5;border-radius:20px;background:#111c27;color:#f4f7fb;font:12px/1.4 Arial,sans-serif;cursor:pointer;box-shadow:0 2px 12px #0005;pointer-events:auto}.tragik-consent-entry:focus-visible,.tragik-consent-dialog button:focus-visible{outline:3px solid #8ee8d2;outline-offset:3px}.tragik-consent-dialog{position:fixed;inset:0;margin:auto;width:min(520px,calc(100vw - 32px));max-height:calc(100dvh - 32px);overflow:auto;padding:26px;border:1px solid #718596;border-radius:16px;background:#111c27;color:#f4f7fb;font:15px/1.6 Arial,sans-serif;box-shadow:0 16px 80px #0008;z-index:10030}.tragik-consent-dialog::backdrop{background:#02070dc9}.tragik-consent-dialog h2{font:700 22px/1.3 Arial,sans-serif;margin:0 0 16px;color:#fff}.tragik-consent-dialog p{margin:12px 0;color:#d5dfe7}.tragik-consent-dialog .tragik-consent-state{padding:12px;border:1px solid #536c78;border-radius:7px;background:#1b2c38;color:#fff}.tragik-consent-dialog .tragik-consent-essential{font-size:12px;color:#afc0cc}.tragik-consent-dialog a{color:#98e5dc;text-decoration:underline}.tragik-consent-actions{display:flex;gap:10px;margin-top:22px;flex-wrap:wrap}.tragik-consent-dialog button{font:600 13px/1.4 Arial,sans-serif;padding:11px 15px;background:#e1ede9;color:#10201c;border:1px solid #abc8bd;border-radius:7px;cursor:pointer;min-height:42px}.tragik-consent-actions button{flex:1 1 180px}.tragik-consent-dialog button:disabled{opacity:.45;cursor:not-allowed}.tragik-consent-dialog .tragik-consent-close{display:block;margin:14px 0 0 auto;background:transparent;color:#d5dfe7;border-color:#536c78}.tragik-consent-request{font:600 14px/1.4 Arial,sans-serif;background:#172b36;color:#e6f2f5;border:1px solid #648996;border-radius:6px;padding:10px 14px;cursor:pointer}.tragik-consent-placeholder{font:14px/1.6 Arial,sans-serif}.tragik-consent-request:disabled{opacity:.5;cursor:not-allowed}@media(max-width:520px){.tragik-consent-dialog{padding:20px}.tragik-consent-entry{font-size:11px;padding:7px 10px}}@media print{.tragik-consent-entry,.tragik-consent-dialog{display:none!important}}\n\n@media(max-width:619px){body:has([data-world-shop]) .tragik-consent-entry{bottom:calc(21.026vw + 12px)}}\n"},"imports":{"runtime/comic-font.mjs":[],"runtime/comic-layout.mjs":[],"runtime/consent.mjs":[],"runtime/engine.mjs":[{"start":35,"end":58,"specifier":"./page-transition.mjs"},{"start":92,"end":112,"specifier":"./sky-renderer.mjs"},{"start":142,"end":160,"specifier":"./comic-font.mjs"},{"start":184,"end":216,"specifier":"/cms-runtime/vendor-v1/pixi.js"},{"start":241,"end":274,"specifier":"/cms-runtime/vendor-v1/three.js"},{"start":316,"end":338,"specifier":"./scene-document.mjs"},{"start":372,"end":392,"specifier":"./native-scene.mjs"},{"start":426,"end":446,"specifier":"./native-media.mjs"},{"start":499,"end":519,"specifier":"./native-input.mjs"},{"start":569,"end":591,"specifier":"./scene-contract.mjs"},{"start":623,"end":647,"specifier":"./runtime-settings.mjs"}],"runtime/native-input.mjs":[],"runtime/native-media.mjs":[{"start":27,"end":59,"specifier":"/cms-runtime/vendor-v1/gltf.js"},{"start":89,"end":111,"specifier":"./scene-contract.mjs"},{"start":148,"end":163,"specifier":"./consent.mjs"}],"runtime/native-paint.mjs":[{"start":50,"end":72,"specifier":"./scene-contract.mjs"}],"runtime/native-scene.mjs":[{"start":29,"end":48,"specifier":"./native-shop.mjs"},{"start":152,"end":174,"specifier":"./scene-contract.mjs"},{"start":261,"end":281,"specifier":"./comic-layout.mjs"},{"start":352,"end":372,"specifier":"./native-paint.mjs"}],"runtime/native-shop.mjs":[],"runtime/page-transition.mjs":[{"start":32,"end":54,"specifier":"./scene-contract.mjs"}],"runtime/runtime-settings.mjs":[],"runtime/scene-contract.mjs":[],"runtime/scene-document.mjs":[{"start":253,"end":273,"specifier":"./native-paint.mjs"}],"runtime/sky-catalog.mjs":[],"runtime/sky-math.mjs":[],"runtime/sky-renderer.mjs":[{"start":31,"end":50,"specifier":"./sky-catalog.mjs"},{"start":132,"end":148,"specifier":"./sky-math.mjs"}]}}