function slugify(text){return text.replace(/\s+/g,'-').replace(/[^\w-]/g,'');} document.querySelectorAll('#content h2, #content h4, #content h5').forEach(function(h){if(!h.id)h.id=slugify(h.textContent);});var toc=document.getElementById('toc');if(toc){document.querySelectorAll('#content h2').forEach(function(h){var li=document.createElement('li');li.innerHTML=''+h.textContent+'';toc.appendChild(li);});} function getVisited(){try{return JSON.parse(localStorage.getItem('rstat_visited')||'{}');}catch(e){return{};}} function saveVisited(v){try{localStorage.setItem('rstat_visited',JSON.stringify(v));}catch(e){}} function getCollapsed(){try{return JSON.parse(localStorage.getItem('rstat_subsec_collapsed')||'{}');}catch(e){return{};}} function saveCollapsed(c){try{localStorage.setItem('rstat_subsec_collapsed',JSON.stringify(c));}catch(e){}} function getStarted(){try{return JSON.parse(localStorage.getItem('rstat_started')||'{}');}catch(e){return{};}} function saveStarted(s){try{localStorage.setItem('rstat_started',JSON.stringify(s));}catch(e){}} (function(){var page=window.location.pathname.split('/').pop()||'index.html';if(!page)return;var visited=getVisited();var started=getStarted();if(!visited[page]){started[page]=true;saveStarted(started);} try{var priorRaw=localStorage.getItem('rstat_last_visited');if(priorRaw){var prior=JSON.parse(priorRaw);if(prior&&prior.href&&prior.href!==page){localStorage.setItem('rstat_continue',priorRaw);}} var title=document.title.replace(/\s*[\|—\-].*$/,'').trim()||page;localStorage.setItem('rstat_last_visited',JSON.stringify({href:page,title:title,ts:Date.now()}));}catch(e){} if(visited[page])return;function markVisited(){var v=getVisited();if(v[page])return;v[page]=true;saveVisited(v);var s=getStarted();if(s[page]){delete s[page];saveStarted(s);}} function checkDepth(){var doc=document.documentElement;var scrolled=window.scrollY||window.pageYOffset||0;var viewport=window.innerHeight||doc.clientHeight;var total=doc.scrollHeight-viewport;if(total<=0||scrolled/total>=0.8){markVisited();window.removeEventListener('scroll',onScroll);}} var ticking=false;function onScroll(){if(ticking)return;ticking=true;requestAnimationFrame(function(){ticking=false;checkDepth();});} window.addEventListener('scroll',onScroll,{passive:true});setTimeout(checkDepth,100);})();(function(){var tabs=document.querySelectorAll('.sidebar-tab');var panels=document.querySelectorAll('.sidebar-panel');if(!tabs.length||!panels.length)return;function activate(name){tabs.forEach(function(t){t.classList.toggle('active',t.getAttribute('data-tab')===name);});panels.forEach(function(p){p.classList.toggle('active',p.getAttribute('data-panel')===name);});} var onToolPage=window.location.pathname.indexOf('/tools/')===0;if(!onToolPage){try{var pinned=localStorage.getItem('rstat_sidebar_tab');if(pinned==='tools'||pinned==='posts')activate(pinned);}catch(e){}} tabs.forEach(function(tab){tab.addEventListener('click',function(){var name=tab.getAttribute('data-tab');activate(name);try{localStorage.setItem('rstat_sidebar_tab',name);}catch(e){}});});})();(function(){var sidebarEl=document.getElementById('sidebar-nav');if(!sidebarEl)return;var visited=getVisited();var started=getStarted();var collapsed=getCollapsed();var currentPage=window.location.pathname.split('/').pop()||'index.html';function keyOf(href){return href.replace(/^\//,'');} sidebarEl.querySelectorAll('.sidebar-section-items a').forEach(function(a){var href=a.getAttribute('href');if(!href)return;var key=keyOf(href);var dot=a.querySelector('.progress-dot');if(!dot)return;if(visited[key])dot.classList.add('visited');else if(started[key])dot.classList.add('started');});sidebarEl.querySelectorAll('.sidebar-section').forEach(function(section){var meta=section.querySelector('[data-section-meta]');if(!meta)return;var links=section.querySelectorAll('.sidebar-section-items > li:not(.sidebar-divider) > a');var total=links.length;if(!total)return;var done=0;links.forEach(function(a){var href=a.getAttribute('href');if(!href)return;var key=keyOf(href);if(visited[key]||started[key]||key===currentPage)done++;});meta.textContent=done+' / '+total;});function applyChip(href,title){if(!href||href===currentPage)return;var chip=sidebarEl.querySelector('[data-continue-chip]');if(chip){var chipLink=chip.querySelector('[data-continue-link]');if(chipLink){chipLink.href=href;chipLink.textContent=title||href;chip.classList.add('has-link');}} var bottomBlock=document.querySelector('[data-continue-block]');if(bottomBlock){var bottomLink=bottomBlock.querySelector('[data-continue-link]');if(bottomLink){bottomLink.href=href;bottomLink.textContent=title||href;bottomBlock.classList.add('has-link');}}} (function(){var raw;try{raw=localStorage.getItem('rstat_continue');}catch(e){return;} if(!raw)return;var lv;try{lv=JSON.parse(raw);}catch(e){return;} if(!lv||!lv.href)return;applyChip(lv.href,lv.title);})();document.addEventListener('auth-hydrated',function(ev){var token=ev.detail&&ev.detail.token;if(!token)return;var lookup={};sidebarEl.querySelectorAll('.sidebar-section-items a').forEach(function(a){var href=a.getAttribute('href');if(!href)return;var slug=href.replace(/^\//,'').replace(/\.html?$/,'');lookup[slug]={href:href,title:(a.textContent||'').trim()};});function chooseAndApply(slug){if(!slug)return false;var entry=lookup[slug];var href=entry?entry.href:('/'+slug+'.html');var title=entry?entry.title:slug.replace(/-/g,' ');if(href===currentPage)return false;applyChip(href,title);return true;} function fetchKind(kind){return fetch('/api/me/reading?kind='+kind+'&limit=1',{headers:{'Authorization':'Bearer '+token,'Accept':'application/json'},}).then(function(r){return r.ok?r.json():null;}).catch(function(){return null;});} fetchKind('in_progress').then(function(j){if(j&&j.items&&j.items.length&&chooseAndApply(j.items[0].slug))return;fetchKind('all').then(function(j2){if(j2&&j2.items&&j2.items.length)chooseAndApply(j2.items[0].slug);});});});sidebarEl.querySelectorAll('.sidebar-subsection-toggle').forEach(function(divider){var key=divider.getAttribute('data-subkey');if(collapsed[key]===true){divider.setAttribute('data-collapsed','true');var chev=divider.querySelector('.subsec-chevron');if(chev)chev.innerHTML='▶';sidebarEl.querySelectorAll('li[data-subkey="'+key+'"]:not(.sidebar-subsection-toggle)').forEach(function(li){li.style.display='none';});}});sidebarEl.querySelectorAll('.sidebar-section-header').forEach(function(header){header.addEventListener('click',function(){this.closest('.sidebar-section').classList.toggle('expanded');});});sidebarEl.querySelectorAll('.sidebar-subsection-toggle').forEach(function(divider){divider.addEventListener('click',function(){var key=divider.getAttribute('data-subkey');var nowCollapsed=divider.getAttribute('data-collapsed')!=='true';divider.setAttribute('data-collapsed',nowCollapsed);var chev=divider.querySelector('.subsec-chevron');if(chev)chev.innerHTML=nowCollapsed?'▶':'▼';var c=getCollapsed();c[key]=nowCollapsed;saveCollapsed(c);sidebarEl.querySelectorAll('li[data-subkey="'+key+'"]:not(.sidebar-subsection-toggle)').forEach(function(li){li.style.display=nowCollapsed?'none':'';});});});})();(function(){if(!('IntersectionObserver'in window))return;var headings=document.querySelectorAll('#content h2');var tocLinks=document.querySelectorAll('#toc a');if(!headings.length||!tocLinks.length)return;var linkById={};tocLinks.forEach(function(a){var href=a.getAttribute('href')||'';if(href.charAt(0)==='#')linkById[href.slice(1)]=a;});var visibleIds={};var activeId=null;function updateActive(){var chosen=null;for(var i=0;ia{background:#eaf4ee;color:#14532d;font-weight:600}','.bn-cur .bn-n{color:#1f7a55}','.bn-soon{color:#a7acb4}','.bn-soon .bn-tag{margin-left:auto;font-size:9.5px;font-weight:700;color:#8a8f98;border:1px solid #e5e7ea;border-radius:999px;padding:0 6px}','.bn-pro{border-top:1px solid #eceadf;margin-top:14px;padding-top:11px}','.bn-pro-h{font-family:"Inter Tight",Inter,sans-serif;font-weight:600;font-size:13px;color:#16181d;margin-bottom:3px}','.bn-pro p{font-size:11.5px;color:#6b7280;line-height:1.45;margin:0 0 6px}','.bn-pro a{font-size:12px;font-weight:600;color:#14532d;text-decoration:none}','.bn-pro a:hover{text-decoration:underline}','.bn-return{border:1px solid #d9e8de;background:#f2f8f4;border-radius:10px;padding:9px 11px;margin:0 0 12px}','.bn-return-hint{font-size:10.5px;color:#6b7280;margin-bottom:4px}','.bn-return-btn{background:#1f7a55;color:#fff;border:0;border-radius:8px;padding:6px 10px;font-size:12px;font-weight:600;cursor:pointer;text-align:left;width:100%}','.bn-return-btn:hover{background:#17603f}'].join('\n');var st=document.createElement('style');st.setAttribute('data-bn-css','');st.textContent=css;document.head.appendChild(st);function esc(t){return String(t).replace(/&/g,'&').replace(/← All tutorials');html.push('
'+esc(book.title)+'
');html.push('Book contents →');book.parts.forEach(function(p,pi){html.push('
'+(pi+1)+'. '+esc(p.title)+'
');});if(book.pro){html.push('
Prefer it guided?

'+esc(book.pro.blurb)+'

'+esc(book.pro.label)+' →
');} var wrap=document.createElement('div');wrap.className='bn-wrap';var main=document.createElement('div');main.className='bn-panel';main.id='bn-main';while(sidebarEl.firstChild)main.appendChild(sidebarEl.firstChild);var ret=document.createElement('div');ret.className='bn-return';ret.innerHTML='
You are reading a chapter of
'+'';main.insertBefore(ret,main.firstChild);var bookPanel=document.createElement('div');bookPanel.className='bn-panel';bookPanel.id='bn-book';bookPanel.innerHTML=html.join('');wrap.appendChild(bookPanel);wrap.appendChild(main);sidebarEl.appendChild(wrap);function show(which){if(which==='main'){bookPanel.classList.add('bn-off','bn-off-l');main.classList.remove('bn-off','bn-off-r','bn-off-l');}else{main.classList.add('bn-off','bn-off-r');bookPanel.classList.remove('bn-off','bn-off-l','bn-off-r');} try{sessionStorage.setItem('bn-state:'+book.key,which);}catch(e){} wrap.style.minHeight=((which==='main'?main:bookPanel).offsetHeight||0)+'px';} wrap.addEventListener('click',function(e){if(e.target.closest('[data-bn-main]'))show('main');else if(e.target.closest('[data-bn-book]'))show('book');});var initial='book';try{initial=sessionStorage.getItem('bn-state:'+book.key)||'book';}catch(e){} show(initial);}).catch(function(){});})();