
const APP=document.getElementById('app');
const KEY='the_being_task3_state_v1';
let BOOKS={}, THREAD=null;
let arrivalNotice='';
const defaultState={started:false,threadIndex:0,tab:'read',mode:'thread',bookId:null,bookPage:0,returnIndex:0,thoughtOpen:true,seenThreshold:false,noThreshold:false,seenHandoff:false,seenExplore:false,seenMap:false,complete:false,pulse:'',pulseSaved:false,books:{}};
function loadState(){try{return Object.assign({},defaultState,JSON.parse(localStorage.getItem(KEY)||'{}'))}catch(e){return {...defaultState}}}
let S=loadState();
function save(){localStorage.setItem(KEY,JSON.stringify(S))}
function esc(s=''){return String(s).replace(/[&<>"']/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}[m]))}
function paras(s){return s.split(/\n\n+/).map(x=>`<p>${esc(x)}</p>`).join('')}
function b(id){return BOOKS[id]}
function pageObj(id,p){return b(id).pages[p-1]}
function touch(id){S.books[id]=Object.assign({touched:false,owned:false,read:false},S.books[id]||{});S.books[id].touched=true}
function stateFor(id){return Object.assign({touched:false,owned:false,read:false},S.books[id]||{})}
function current(){return THREAD.sequence[Math.min(S.threadIndex,THREAD.sequence.length-1)]}
function currentSegmentIndex(){let result=0;THREAD.segments.forEach((seg,i)=>{if(S.threadIndex>=seg.start_index)result=i});return result}
function reachedBookIds(){const set=new Set();if(!S.started)return set;for(let i=0;i<=Math.min(S.threadIndex,THREAD.sequence.length-1);i++)set.add(THREAD.sequence[i].book_id);return set}
function firstLoadNotice(){if(S.complete)return 'That Thread ended.';if(S.started&&S.threadIndex>0){const n=current();return `You left this thought inside ${b(n.book_id).title}. Your place is still there.`}return ''}
Promise.all([fetch('content/books.json').then(r=>r.json()),fetch('content/thread.json').then(r=>r.json())]).then(([bd,td])=>{bd.books.forEach(x=>BOOKS[x.id]=x);THREAD=td;arrivalNotice=firstLoadNotice();render();if(!S.noThreshold&&!S.seenThreshold)openThreshold();});
function brand(){return `<div class="topline"></div><div class="brandbar"><div class="brand"><div class="seal"></div><div><div class="brandname">THE BEING</div><div class="tagline">you wonder. it weaves.</div></div></div><div class="header-actions"><button class="tinybtn" onclick="resetDemo()">Reset</button></div></div>`}
function thoughtBar(){return `<div class="thoughtbar" onclick="S.thoughtOpen=!S.thoughtOpen;save();render()"><div class="eyebrow">Your Thought</div><div class="thought ${S.thoughtOpen?'':'collapsed'}">${esc(THREAD.thought)}</div><div class="hint">tap to ${S.thoughtOpen?'collapse':'expand'}</div></div>`}
function tabs(){return `<div class="tabbar"><button class="tabbtn ${S.tab==='read'?'active':''}" onclick="setTab('read')">Read</button><button class="tabbtn ${S.tab==='map'?'active':''}" onclick="setTab('map')">Map</button><button class="tabbtn ${S.tab==='library'?'active':''}" onclick="setTab('library')">Library</button></div>`}
function setTab(tab){const firstMap=tab==='map'&&!S.seenMap;S.tab=tab;if(firstMap)S.seenMap=true;save();render();if(firstMap)openInfo('This map remembers the route your thought has taken. Future Books stay unnamed until the Thread reaches them.','The map')}
function miniJourney(){const segNow=currentSegmentIndex();return `<div class="journey-mini">${THREAD.segments.map((seg,i)=>{const future=i>segNow;const cur=i===segNow&&!S.complete;const name=future?'Unrevealed':b(seg.book_id).title;return `<div class="mini-node"><div class="mini-dot ${future?'':cur?'current':'done'}"></div><div class="mini-label ${future?'future':''}">${esc(name)}${future?'':` · ${seg.pages.length>1?'pages '+seg.pages[0]+'–'+seg.pages[seg.pages.length-1]:'page '+seg.pages[0]}`}</div></div>`}).join('')}</div>`}
function leftSide(){return `<aside class="side panel"><div class="side-section"><div class="side-title">Your Thought</div><div class="side-copy">${esc(THREAD.thought)}</div></div><div class="side-section"><div class="side-title">The Journey</div>${miniJourney()}</div><div class="side-section"><div class="side-title">How it works</div><div class="side-copy">Follow the exact pages chosen for this thought. Step into any surfaced Book if it catches you. Your Thread position waits.</div></div></aside>`}
function rightSide(){if(!S.started)return `<aside class="side panel"><div class="side-section"><div class="side-title">The Library</div><div class="side-copy">Books surface as the Thread reaches them. The shelves continue beyond this Journey.</div></div></aside>`;const n=current(),bk=b(n.book_id);return `<aside class="side panel"><div class="side-section"><div class="side-title">Your Journey Map</div>${miniJourney()}</div><div class="side-section"><div class="side-title">Current Book</div><div class="bookcover" style="background-image:url('${bk.cover}')"></div><div style="margin-top:10px" class="booktitle">${esc(bk.title)}</div><div class="side-copy">This Book continues beyond the pages used by your Thread.</div><button class="action gold" style="width:100%;margin-top:10px" onclick="requestExplore('${bk.id}')">Explore Book</button></div></aside>`}
function startView(){return `<section class="startview"><div class="startcard"><div class="seal"></div><div class="eyebrow">A thought is waiting</div><h1>You don't need to make it sound clever.</h1><div class="startthought">${esc(THREAD.thought)}</div><div class="startnote">The Books already existed. This Journey is the route found through the pages that touch your question from different sides.</div><button class="action primary beginbtn" onclick="begin()">Begin Journey</button></div></section>`}
function begin(){S.started=true;S.mode='thread';S.threadIndex=0;S.complete=false;S.tab='read';touch(current().book_id);save();render()}
function reader(){if(!S.started)return startView();if(S.complete)return pulseView();if(S.mode==='book')return bookReader();return threadReader()}
function compClass(c){return c?.contrast_treatment||'parchment_split'}
function pageMarkup(bk,pg,modeLabel,subLabel){const c=pg.composition||{};return `<div class="reader"><div class="reader-head"><div class="bookline">${esc(bk.title)} · page ${pg.page} of 8</div><div class="modebadge">${modeLabel}</div></div><article class="page ${compClass(c)} dropcap"><div class="prose">${paras(pg.text)}</div><div class="art" style="background-image:url('${pg.art}');background-position:${c.desktop_crop||'50% 50%'}"></div></article>${subLabel}</div>`}
function threadReader(){const n=current(),bk=b(n.book_id),pg=pageObj(n.book_id,n.page);touch(bk.id);save();const prevDisabled=S.threadIndex===0;const nextDiff=S.threadIndex<THREAD.sequence.length-1&&THREAD.sequence[S.threadIndex+1].book_id!==n.book_id;const nextLabel=S.threadIndex===THREAD.sequence.length-1?'Complete Thread →':nextDiff?'Continue →':'Next →';const notice=arrivalNotice?`<div class="presence"><strong>Still here.</strong> ${esc(arrivalNotice)}</div>`:'';const controls=`<div class="reader-foot"><button class="navbtn" ${prevDisabled?'disabled':''} onclick="threadPrev()">← Previous</button><div class="status">Following Thread · ${S.threadIndex+1}/${THREAD.sequence.length}</div><button class="navbtn" onclick="threadNext()">${nextLabel}</button></div>`;return `${notice}${pageMarkup(bk,pg,'Following Thread',controls)}<div class="page-actions"><button class="action gold" onclick="requestExplore('${bk.id}')">Explore Book</button><button class="action" onclick="setTab('map')">See Journey Map</button></div>`}
function threadPrev(){arrivalNotice='';if(S.threadIndex>0)S.threadIndex--;touch(current().book_id);save();render()}
function doThreadNext(){arrivalNotice='';if(S.threadIndex>=THREAD.sequence.length-1){S.complete=true;S.tab='read';save();render();return}S.threadIndex++;touch(current().book_id);save();render()}
function threadNext(){const n=current();if(S.threadIndex<THREAD.sequence.length-1){const nx=THREAD.sequence[S.threadIndex+1];if(nx.book_id!==n.book_id&&!S.seenHandoff){S.seenHandoff=true;save();openChoice('This Book keeps going, but your Thread leaves it here.','The Thread is moving to another Book because this thought continues there.',()=>doThreadNext(),'Continue');return}}doThreadNext()}
function requestExplore(id){if(!S.seenExplore){S.seenExplore=true;save();openChoice(`You're stepping out of the Thread to read ${b(id).title} from the beginning.`,`Your place in the Thread will wait exactly where you left it.`,()=>openBook(id),'Open Book');return}openBook(id)}
function openBook(id){arrivalNotice='';S.mode='book';S.bookId=id;S.bookPage=0;S.returnIndex=S.threadIndex;touch(id);S.tab='read';save();render()}
function bookReader(){const bk=b(S.bookId);if(S.bookPage===0){return coverReader(bk)}const pg=pageObj(bk.id,S.bookPage);const controls=`<div class="reader-foot"><button class="navbtn" onclick="bookPrev()">← Previous</button><div class="status">Exploring Book · ${S.bookPage}/8</div><button class="navbtn" onclick="bookNext()">${S.bookPage===8?'Return to Thread →':'Next →'}</button></div>`;return `${pageMarkup(bk,pg,'Exploring Book',controls)}<div class="page-actions"><button class="action primary" onclick="returnThread()">Return to Thread</button><button class="action gold" onclick="toggleOwned('${bk.id}')">${stateFor(bk.id).owned?'Owned':'Keep Book'}</button></div>`}
function coverReader(bk){return `<div class="reader"><div class="reader-head"><div class="bookline">${esc(bk.title)}</div><div class="modebadge">Exploring Book · cover</div></div><div class="page art_overlay" style="min-height:650px"><div class="art" style="position:absolute;inset:0;background-image:url('${bk.cover}')"></div><div class="prose" style="position:relative;z-index:2;margin-top:60%;padding:28px"><div class="eyebrow">A complete Book</div><h1 style="font-weight:normal;font-size:2rem;margin:.3em 0">${esc(bk.title)}</h1><div style="color:#cabba5;max-width:34rem;line-height:1.5">A complete eight-page story. Your Thread only used the pages it needed.</div></div></div><div class="reader-foot"><button class="navbtn" onclick="returnThread()">← Return to Thread</button><div class="status">Exploring Book</div><button class="navbtn" onclick="bookNext()">Page 1 →</button></div></div><div class="page-actions"><button class="action gold" onclick="toggleOwned('${bk.id}')">${stateFor(bk.id).owned?'Owned':'Keep Book'}</button></div>`}
function bookPrev(){if(S.bookPage>0)S.bookPage--;save();render()}
function bookNext(){if(S.bookPage<8){S.bookPage++;if(S.bookPage===8){S.books[S.bookId]=Object.assign({touched:true,owned:false,read:true},S.books[S.bookId]||{}, {read:true})}save();render();return}returnThread()}
function returnThread(){const title=b(S.bookId).title;S.mode='thread';S.threadIndex=S.returnIndex;S.bookId=null;S.bookPage=0;arrivalNotice=`You went farther into ${title} than the Thread asked you to. Your place is still waiting.`;save();render()}
function toggleOwned(id){S.books[id]=Object.assign({touched:true,owned:false,read:false},S.books[id]||{});S.books[id].owned=!S.books[id].owned;save();render()}
function mapView(){const segNow=currentSegmentIndex();let body=THREAD.segments.map((seg,i)=>{const future=!S.started||(i>segNow&&!S.complete);const cur=S.started&&i===segNow&&!S.complete;const done=S.started&&!future&&!cur;const title=future?'Unrevealed':b(seg.book_id).title;const pages=future?'Not yet surfaced':(seg.pages.length>1?`pages ${seg.pages[0]}–${seg.pages[seg.pages.length-1]}`:`page ${seg.pages[0]}`);return `<div class="route-node"><div class="route-dot ${future?'future':cur?'current':'done'}"></div><div class="route-info"><div class="route-book ${future?'route-future':''}">${esc(title)}</div><div class="route-pages">${esc(pages)}${seg.return&&!future?' · returns here':''}</div></div></div>`}).join('');return `<section class="mapview"><div class="eyebrow">Journey Map</div><div class="viewtitle">The route found for your thought</div><div class="viewsub">Reached Books are named. What comes later stays hidden until the Thread gets there.</div><div class="mapcanvas"><div class="route">${body}</div></div><div class="legend"><span class="now">you are here</span><span class="passed">passed</span><span>not yet surfaced</span></div></section>`}
function libraryView(){const reached=reachedBookIds();const cards=[...reached].map(id=>{const bk=b(id),st=stateFor(id);const label=st.read?'READ':st.owned?'OWNED':'TOUCHED';return `<article class="bookcard"><div class="bookcover" style="background-image:url('${bk.cover}')"></div><div class="bookmeta"><div class="booktitle">${esc(bk.title)}</div><div class="bookstate">${label}</div><div class="bookactions"><button class="tinybtn" onclick="requestExplore('${id}')">Open</button><button class="tinybtn" onclick="toggleOwned('${id}')">${st.owned?'Kept':'Keep'}</button></div></div></article>`}).join('');return `<section class="libraryview"><div class="eyebrow">Library</div><div class="viewtitle">Books that have surfaced</div><div class="viewsub">The library is larger than this Journey. Books appear here when your Thread reaches them.</div><div class="library-grid">${cards}<div class="hiddenbook">More remains unsurfaced</div><div class="hiddenbook">The shelves continue</div></div></section>`}
function pulseView(){return `<section class="pulseview"><div class="pulsebox">${arrivalNotice?`<div class="presence"><strong>Still here.</strong> ${esc(arrivalNotice)}</div>`:''}<div class="eyebrow">Thread complete</div><div class="viewtitle">What stayed with you?</div><div class="viewsub">One Pulse can remain with this Journey. It is not a score and it changes nothing in the library.</div><textarea oninput="S.pulse=this.value;S.pulseSaved=false;save()" placeholder="Write what stayed...">${esc(S.pulse)}</textarea><div style="display:flex;gap:8px;margin-top:10px"><button class="action primary" onclick="savePulse()">Save Pulse</button><button class="action" onclick="restartThread()">Walk it again</button></div>${S.pulseSaved?'<div class="saved">Saved locally in this browser.</div>':''}</div></section>`}
function savePulse(){S.pulseSaved=true;save();render()}
function restartThread(){S.started=true;S.complete=false;S.mode='thread';S.threadIndex=0;S.tab='read';arrivalNotice='';save();render()}
function centerView(){if(S.tab==='map')return mapView();if(S.tab==='library')return libraryView();return reader()}
function render(){if(!THREAD)return;let sideL=leftSide(),sideR=rightSide();APP.innerHTML=`<div class="app"><div class="desktop-shell">${brand()}${thoughtBar()}${tabs()}<main class="desktop-grid">${sideL}<section class="center">${centerView()}</section>${sideR}</main></div></div><div id="overlay" class="overlay"><div id="modal" class="modal"></div></div>`}
function openThreshold(){S.seenThreshold=true;save();const html=`<div class="eyebrow">Before you begin</div><h2>You came with a thought.</h2><p>These Books weren't written for your question.</p><p>A path was found through the parts that touch it. Follow the Thread to stay with your thought. If a Book catches you, you can step inside it from the beginning. Your place in the Thread will wait.</p><div class="modal-actions"><button class="textbtn primary" onclick="closeOverlay();begin()">Begin</button><button class="textbtn" onclick="S.noThreshold=true;save();closeOverlay()">Don't show this again</button></div>`;showModal(html)}
function openInfo(body,title='A note'){showModal(`<div class="eyebrow">${esc(title)}</div><h2>${esc(title)}</h2><p>${esc(body)}</p><div class="modal-actions"><button class="textbtn primary" onclick="closeOverlay()">Continue</button></div>`)}
function openChoice(title,body,fn,label){window.__choice=fn;showModal(`<div class="eyebrow">Following Thread</div><h2>${esc(title)}</h2><p>${esc(body)}</p><div class="modal-actions"><button class="textbtn primary" onclick="closeOverlay();window.__choice&&window.__choice()">${esc(label)}</button><button class="textbtn" onclick="closeOverlay()">Stay here</button></div>`)}
function showModal(html){document.getElementById('modal').innerHTML=html;document.getElementById('overlay').classList.add('open')}
function closeOverlay(){document.getElementById('overlay')?.classList.remove('open')}
function resetDemo(){localStorage.removeItem(KEY);location.reload()}
Object.assign(window,{setTab,begin,threadPrev,threadNext,requestExplore,bookPrev,bookNext,returnThread,toggleOwned,savePulse,restartThread,closeOverlay,resetDemo});
