@daowolf/system-design-quest 0.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ var ge={remember:10,understand:15,apply:20,analyse:30,evaluate:40,create:60},be=100,ye=.8,we=.8,mt=1.6,ve=2;function ke(t){let e=t>>>0;return()=>{e=e+1831565813>>>0;let s=e;return s=Math.imul(s^s>>>15,s|1),s^=s+Math.imul(s^s>>>7,s|61),((s^s>>>14)>>>0)/4294967296}}function b(t,e=Math.random){let s=t.slice();for(let n=s.length-1;n>0;n--){let o=Math.floor(e()*(n+1));[s[n],s[o]]=[s[o],s[n]]}return s}function _(t,[e,s]){let n=[];for(let o=Math.floor(Math.log10(e))-1;o<=Math.ceil(Math.log10(s))+1;o++)for(let a of[1,2,5]){let l=a*10**o;l>=e&&l<=s&&n.push(l)}return n.length||n.push(e),n[Math.floor(t()*n.length)]}function h(t){return t>=1e9?`${+(t/1e9).toPrecision(3)} billion`:t>=1e6?`${+(t/1e6).toPrecision(3)} million`:t>=1e4?Math.round(t).toLocaleString("en-GB"):`${+t.toPrecision(3)}`}var gt={peak_rps(t,e){let s=_(t,e.users),n=_(t,e.actions),o=s*n,a=o/1e5,l=a*10;return{question:`${h(s)} people each do ${h(n)} actions a day. Requests a second at the peak?`,answer:l,unit:"requests a second",working:`${h(s)} \xD7 ${h(n)} = ${h(o)} a day. \xF7 100,000 = ${h(a)} a second on average. \xD7 10 for the peak = ${h(l)}.`}},storage_year(t,e){let s=_(t,e.items),n=_(t,e.kb),o=s*n*365/1e6,a=o>=1e3;return{question:`${h(s)} new items a day, about ${h(n)} KB each. Storage after a year, in ${a?"TB":"GB"}?`,answer:a?o/1e3:o,unit:a?"TB":"GB",working:`${h(s)} \xD7 ${h(n)} KB = ${h(s*n)} KB a day. \xD7 365 \u2248 ${h(s*n*365)} KB a year = ${h(o)} GB${a?` = ${h(o/1e3)} TB`:""}. (KB \u2192 MB \u2192 GB \u2192 TB is \xD7 1,000 each step.)`}},shard_or_not(t,e){let s=_(t,e.users),n=_(t,e.writes),o=s*n/1e5*10;return{question:`${h(s)} people each make ${h(n)} writes a day. Writes a second at the peak? (Then: one database, or split?)`,answer:o,unit:"writes a second",working:`${h(s)} \xD7 ${h(n)} \xF7 100,000 \xD7 10 = ${h(o)} at the peak. ${o>1e4?"Past about 10,000 writes a second: consider splitting (sharding).":"Well under about 10,000 writes a second: one database copes, no splitting."}`}},bandwidth(t,e){let s=_(t,e.rps),n=_(t,e.kb),o=s*n/1e3;return{question:`${h(s)} responses a second, about ${h(n)} KB each. Outbound bandwidth in MB a second?`,answer:o,unit:"MB a second",working:`${h(s)} \xD7 ${h(n)} KB = ${h(s*n)} KB a second \xF7 1,000 = ${h(o)} MB a second.`}}};function N(t,e){let s=ke(e);if(!Object.hasOwn(gt,t.kind))throw new Error(`unknown generator ${t.kind}`);return gt[t.kind](s,t.params)}var xe={again:0,hard:.5,good:1,easy:1},H=t=>Math.max(0,Math.min(1,t));function bt(t,e){let s=0,n=0;for(let o of t)e.has(o)?s++:n++;return{right:s,wrong:n}}var yt={recall:(t,e)=>xe[e.grade]??0,multi(t,e){let s=new Set(t.correct),{right:n,wrong:o}=bt(new Set(e.picked||[]),s);return H((n-o)/s.size)},duel:(t,e)=>((e.choice===t.correct)+(e.reason===t.reason)+(e.cost===t.cost))/3,write:(t,e)=>H((e.ticked||[]).length/t.key_points.length),sprint:(t,e)=>H((e.ticked||[]).length/t.checks.length),estimate(t,e){let{answer:s}=N(t.generator,e.seed),n=Number(e.value);return n>0&&n<=s*mt&&n>=s/mt?1:0},fill(t,e){let s=Object.entries(t.slots),n=e.placed||{};return s.filter(([o,a])=>n[o]===a).length/s.length},sticker(t,e){let s=0,n=0,o=0;for(let a of Object.values(t.answers))o+=a.length;for(let[a,l]of Object.entries(e.placed||{})){let i=bt(l,new Set(t.answers[a]||[]));s+=i.right,n+=i.wrong}return H((s-n)/o)},flaw:(t,e)=>.5*(e.node===t.flaw)+.5*(e.fix===t.fix),order(t,e){let s=e.order||[];return t.items.filter((n,o)=>s[o]===n).length/t.items.length},lightning:(t,e)=>H((e.right||0)/t.pairs.length)};function vt(t,e={}){if(!Object.hasOwn(yt,t.type))throw new Error(`unknown type ${t.type}`);let s=yt[t.type](t,e);return{fraction:s,correct:s>=.999}}function z(t){return t>=5?3:t>=3?2:1}function $e(t,e,s,n){let o=n?be:ge[t.bloom]??10,a=e>=.5?o*e:0;return e>=.999&&s==="sure"?a*1.5:a}function Se(t,e,s){return e?s?t:t+1:0}function kt(t,e,{confidence:s=null,combo:n=0,secondChance:o=!1,isBoss:a=!1}={}){let l=e>=.999,i=Se(n,l,o),d=$e(t,e,s,a)*z(i)*(o?.5:1);return{points:Math.round(d),combo:i,correct:l,multiplier:z(i)}}var Ee=["name","company","role"],ot=60,rt=["Design from scratch","Review a design","API design","Not sure yet"],Oe=/^\d{4}-\d{2}-\d{2}$/,Le=/^[a-z0-9]+(-[a-z0-9]+)*$/,R=t=>typeof t=="string"&&Oe.test(t)&&!Number.isNaN(Date.parse(`${t}T00:00:00`)),nt=t=>!!t&&typeof t=="object";function Be(t){let e=Object.entries(t).filter(([n,o])=>Le.test(n)&&nt(o)),s=Object.fromEntries(e.map(([n,o])=>[n,{lifts:R(o.lifts)?o.lifts:null,lifted:o.lifted===!0}]));return e.length?s:null}function Ce(t,e){for(let s of Ee)typeof t[s]=="string"&&t[s].trim()&&(e[s]=t[s].trim().slice(0,ot))}function E(t){let e={};if(!nt(t))return e;Ce(t,e),rt.includes(t.interview_type)&&(e.interview_type=t.interview_type),R(t.interview_date)&&(e.interview_date=t.interview_date),t.intro_dismissed===!0&&(e.intro_dismissed=!0);let s=nt(t.holds)?Be(t.holds):null;return s&&(e.holds=s),e}function xt(t,e,s){let n=e&&e.holds&&e.holds[t]||{};return n.lifted?!1:R(n.lifts)?s<n.lifts:e&&R(e.interview_date)?s<=e.interview_date:!0}var wt=["Novice","Advanced beginner","Competent"];function Te(t,e){let s=[],n=new Date(t);for(;s.length<e;){let o=n.getDay();o!==0&&o!==6&&s.push(D(n)),n.setDate(n.getDate()-1)}return s}function D(t){let e=s=>String(s).padStart(2,"0");return`${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())}`}var _e=(t,e)=>t.fraction>=ye&&(t.challenge===e.boss||t.boss===!0&&t.chapter===e.chapter);function Re(t,e){let s={};for(let o of t)s[o.challenge]=o;let n=Object.values(s);return n.length>=e&&n.filter(o=>o.correct).length/n.length>=we}function je(t,e){let s={};for(let n of t)(s[n.day]||=[]).push(n);return Object.keys(s).sort().slice(1).filter(n=>Re(s[n],e)).length}function Me(t,e,s){return t?e>=2?wt[2]:wt[s?1:0]:null}function Pe(t,e){let s=new Set(t.challenges.map(i=>i.id)),n=e.filter(i=>s.has(i.challenge)),o=n.filter(i=>!i.second_chance),a=e.some(i=>_e(i,t)),l=a?1+Math.min(2,je(o,Math.min(6,s.size))):0;return{played:n.length>0,answered:new Set(o.map(i=>i.challenge)).size,total:s.size,bossBeaten:a,stars:l,rank:Me(n.length>0,l,a),accuracy:o.length?o.filter(i=>i.correct).length/o.length:null}}function Ae(t,e,s,n){let o=0;for(let a of e){let l=a.open===!0||S(a),i=l||o<ve+n||s.has(a.slug);t.chapters[a.slug].unlocked=i,i&&!s.has(a.slug)&&t.newUnlocks.push(a.slug),l||o++}}function Ne(t,e){let s=Object.fromEntries(e.map(n=>[n.chapter,n]));for(let n of e.filter(S))t.chapters[n.slug].ready=(n.builds_on||[]).every(o=>!s[o]||t.chapters[s[o].slug].bossBeaten)}function De(t,e,s){let n=t.filter(o=>o.confidence==="sure"&&o.correct).length;return[["first-boss","First boss beaten",s>=1],["combo-5","Five in a row",t.some(o=>(o.combo||0)>=5)],["sure-shot","Ten sure and right",n>=10],["comeback","Got it on the second chance",t.some(o=>o.second_chance&&o.correct)],["all-bosses","Every boss beaten",e.length>0&&s===e.length]].filter(([,,o])=>o).map(([o,a])=>({id:o,label:a}))}function Ie(t,e){if(!t||!R(t.interview_date))return null;let s=new Date(`${t.interview_date}T00:00:00`),n=new Date(`${D(e)}T00:00:00`);return Math.round((s-n)/864e5)}function qe(t){let e={};for(let s of t)!s.second_chance&&!s.correct&&(e[s.challenge]=(e[s.challenge]||0)+1);return Object.entries(e).sort((s,n)=>n[1]-s[1]).slice(0,5).map(([s,n])=>({id:s,misses:n}))}function $t(t,e,s=new Date,n=null){let o=e.filter(u=>u.kind==="answer"),a=new Set(e.filter(u=>u.kind==="unlock").map(u=>u.slug)),l=[...t.chapters].sort((u,f)=>u.chapter-f.chapter),i={xp:0,chapters:{},badges:[],weekdays:0,daysLeft:null,weakest:[],newUnlocks:[]};for(let u of o)i.xp+=u.points||0;let d=0;for(let u of l)i.chapters[u.slug]=Pe(u,o),i.chapters[u.slug].bossBeaten&&d++;Ae(i,l,a,d),Ne(i,l),i.badges=De(o,l,d);let p=new Set(o.map(u=>u.day));return i.weekdays=Te(s,5).filter(u=>p.has(u)).length,i.daysLeft=Ie(n,s),i.weakest=qe(o),i}var S=t=>t.kind==="quest";function St(t,e){let s=[...t.chapters].sort((o,a)=>o.chapter-a.chapter),n=o=>e.chapters[o.slug];return s.find(o=>S(o)&&n(o).ready&&!n(o).bossBeaten)||s.find(o=>!S(o)&&n(o).unlocked&&!n(o).bossBeaten)||s.find(o=>n(o).unlocked&&!n(o).bossBeaten)||s.filter(o=>n(o).unlocked).sort((o,a)=>(n(o).accuracy??0)-(n(a).accuracy??0))[0]}function Et(t){let e=t.challenges.filter(n=>n.id!==t.boss),s=t.challenges.find(n=>n.id===t.boss);return s?[...e,s]:e}var c={bundle:null,events:[],store:null,coach:!1,combo:0,run:null,timers:[],coachNext:0,assigned:[],profile:{},holds:[]},I=document,X=new URL(".",import.meta.url),w=t=>I.getElementById(t),Ot=Math.random().toString(36).slice(2,10);function Lt(t,e){I=t,X=e}function q(){c.timers.forEach(clearInterval),c.timers=[]}function C(){return $t(c.bundle,c.events,new Date,c.profile)}function J(t){return c.bundle.chapters.find(e=>e.slug===t)}function Q(t){return St(c.bundle,t)}var Bt=t=>t==null||t===!1;function Fe(t,e,s){e.startsWith("on")?t.addEventListener(e.slice(2),s):e==="class"?t.className=s:e==="style"?t.style.cssText=s:t.setAttribute(e,s===!0?"":s)}var Ye=t=>t instanceof Node?t:document.createTextNode(String(t));function r(t,e={},...s){let n=document.createElement(t);for(let[o,a]of Object.entries(e||{}))Bt(a)||Fe(n,o,a);for(let o of s.flat())Bt(o)||n.append(Ye(o));return n}var v=(t,e={})=>{let s=document.createElementNS("http://www.w3.org/2000/svg",t);for(let[n,o]of Object.entries(e))n==="style"?s.style.cssText=o:s.setAttribute(n,o);return s},We=()=>w("screen");function O(...t){q();let e=We();e.replaceChildren(...t),e.focus({preventScroll:!0});let s=I.host||document.body;s.getBoundingClientRect().top<0&&s.scrollIntoView({block:"start"})}function m(t){let e=r("div",{class:"toast"},t);w("toasts").append(e),setTimeout(()=>e.remove(),3800)}function Tt(){let t=r("div",{class:"confetti"});for(let e=0;e<40;e++){let s=r("span",{},["\u{1F389}","\u2B50","\u2728","\u{1F3C6}"][e%4]);s.style.left=`${Math.random()*100}%`,s.style.animationDelay=`${Math.random()*.8}s`,t.append(s)}w("toasts").after(t),setTimeout(()=>t.remove(),3500)}var Ct=(t,e)=>t.length>2*e.length&&t.startsWith(e)&&t.endsWith(e);function Ge(t){return Ct(t,"**")?r("b",{},t.slice(2,-2)):Ct(t,"*")?r("i",{},t.slice(1,-1)):t}function V(t){let e=r("div");return String(t).split(`
2
+ `).forEach((s,n)=>{n&&e.append(r("br"));for(let o of s.split(/(\*\*.+?\*\*|\*.+?\*)/))o&&e.append(Ge(o))}),e}var Z=class{async load(){return(await fetch("/api/events",{cache:"no-store"})).json()}async add(e){await fetch("/api/events",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)})}async loadProfile(){try{return(await fetch("/api/profile",{cache:"no-store"})).json()}catch{return{}}}async saveProfile(e){await fetch("/api/profile",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)})}async holds(){try{return(await fetch("/api/holds",{cache:"no-store"})).json()}catch{return[]}}},tt=class{key="sdq-events-v1";profileKey="sdq-profile-v1";async load(){try{return JSON.parse(localStorage.getItem(this.key)||"[]")}catch{return[]}}async add(e){try{let s=JSON.parse(localStorage.getItem(this.key)||"[]");s.push(e),localStorage.setItem(this.key,JSON.stringify(s))}catch{}}async loadProfile(){try{return JSON.parse(localStorage.getItem(this.profileKey)||"{}")}catch{return{}}}async saveProfile(e){try{localStorage.setItem(this.profileKey,JSON.stringify(e))}catch{}}async holds(){return[]}replaceAll(e,s){localStorage.setItem(this.key,JSON.stringify(e)),localStorage.setItem(this.profileKey,JSON.stringify(s))}clear(e){try{localStorage.removeItem(this.profileKey),e&&localStorage.removeItem(this.key)}catch{}}};function k(t,e={}){let s=new Date;return{v:1,kind:t,ts:s.toISOString(),day:D(s),session:Ot,...e}}async function x(t){c.events.push(t),await c.store.add(t)}function et(t,e){let s={...e};return s.text&&(s.text=String(s.text).slice(0,4e3)),t.type==="estimate"&&(s.expected=N(t.generator,e.seed).answer),s}var Ke={"first-boss":"\u{1F3C6}","combo-5":"\u{1F525}","sure-shot":"\u{1F3AF}",comeback:"\u{1F501}","all-bosses":"\u{1F451}"},at=t=>Ke[t]||"\u{1F3C5}";function He(t){let e=c.profile.company;if(t===0)return r("span",{class:"stat"},"\u{1F3AF} ",r("b",{},`${e?`${e} interview`:"Interview"} today`));let s=e||"your interview";return r("span",{class:"stat"},"\u23F3 ",r("b",{},t),` day${t===1?"":"s"} to ${s}`)}function Je(t){let e=z(t);return r("span",{class:"stat combo"},`\u{1F525} ${t} in a row${e>1?` \xB7 \xD7${e}`:""}`)}function L(){let t=C(),e=[r("span",{class:"stat"},"\u2B50 ",r("b",{},t.xp.toLocaleString("en-GB"))," XP")];t.daysLeft!=null&&t.daysLeft>=0&&e.push(He(t.daysLeft)),e.push(r("span",{class:"stat",title:"Days you played, of the last five weekdays. Weekends never count against you."},"\u{1F4C5} ",r("b",{},t.weekdays)," of the last 5 weekdays")),c.combo>=2&&e.push(Je(c.combo)),w("stats").replaceChildren(...e)}function Ue(t,e){for(let[s,n]of Object.entries(e.chapters)){let o=t.chapters[s];n.rank&&n.rank!==o.rank&&m(`\u{1F396}\uFE0F Rank up: ${n.rank}`),n.unlocked&&!o.unlocked&&m(`\u{1F513} Unlocked: ${J(s).title}`)}}function ze(t,e){for(let s of e.badges)t.badges.some(n=>n.id===s.id)||m(`${at(s.id)} Badge: ${s.label}`)}function _t(t,e,s){s&&Tt(),Ue(t,e),ze(t,e)}function Rt(t,e,s){let n=r("textarea",{placeholder:"Say it out loud, or type it here. Then reveal."}),o=r("button",{class:"btn"},"Show the answer"),a=r("div");o.onclick=()=>{o.remove(),n.disabled=!0,a.append(r("div",{class:"answerbox"},r("b",{},"Answer: "),t.answer),r("div",{class:"muted"},"How did you do?"),r("div",{class:"row"},[["again","\u274C Missed it"],["hard","\u{1F7E1} Partly"],["good","\u2705 Got it"],["easy","\u26A1 Easy"]].map(([l,i])=>r("button",{class:"btn ghost",onclick:d=>{a.querySelectorAll("button").forEach(p=>p.disabled=!0),d.target.classList.add("sel"),s({grade:l,text:n.value})}},i))))},e.append(n,r("p"),o,a)}var $=(t="Check")=>r("button",{class:"btn",disabled:!0},t);function F(t,{multi:e=!1,onChange:s}){let n=r("div",{class:"options"}),o=new Set,a=t.map(({text:l,value:i})=>{let d=r("button",{class:"opt",onclick:()=>{e?o.has(i)?o.delete(i):o.add(i):(o.clear(),o.add(i)),a.forEach(p=>p.classList.toggle("sel",o.has(p._value))),s([...o])}},l);return d._value=i,d});return n.append(...a),n._buttons=a,n}function Y(t,e,s){for(let n of t._buttons){n.disabled=!0;let o=e.includes(n._value),a=s.includes(n._value);n.classList.remove("sel"),o&&a?n.classList.add("right"):a?n.classList.add("wrong"):o&&n.classList.add("missed")}}function jt(t,e,s){let n=b(t.options.map((d,p)=>({text:d,value:p}))),o=$(),a=[],l=F(n,{multi:!0,onChange:d=>{a=d,o.disabled=a.length!==t.correct.length}}),i=r("input",{type:"text",placeholder:"Why, in one line? (optional, but it helps it stick)"});o.onclick=()=>s({picked:a,why:i.value},()=>Y(l,t.correct,a)),e.append(r("div",{class:"muted"},`Pick ${t.correct.length}.`),l,r("p"),i,r("p"),o)}function Mt(t,e,s){let n={},o=$(),a=[["choice","Your call",t.choices,t.correct],["reason","Because",t.reasons,t.reason],["cost","And the cost",t.costs,t.cost]],l=a.map(([i,d,p])=>{let u=F(b(p.map((f,g)=>({text:f,value:g}))),{onChange:([f])=>{n[i]=f,o.disabled=Object.keys(n).length<3}});return e.append(r("div",{class:"group"},r("div",{class:"label"},d),u)),u});o.onclick=()=>s({...n},()=>a.forEach(([i,,,d],p)=>Y(l[p],[d],[n[i]]))),e.append(o)}function Pt(t,e,s){let n=r("textarea",{placeholder:"Write your answer. Bullet points are fine."}),o=r("button",{class:"btn"},"Done: show the key points"),a=r("div");o.onclick=()=>{if(n.value.trim().length<10){n.focus();return}n.disabled=!0,o.remove();let l=t.key_points.map((i,d)=>r("label",{class:"opt",style:"display:flex;gap:10px"},r("input",{type:"checkbox",value:d}),i));a.append(r("div",{class:"group"},r("div",{class:"label"},"Tick the ones you covered"),r("div",{class:"options"},l)),t.model?r("div",{class:"answerbox"},r("b",{},"One good answer: "),t.model):null,r("button",{class:"btn",onclick:i=>{i.target.disabled=!0,l.forEach(p=>p.querySelector("input").disabled=!0);let d=l.map((p,u)=>p.querySelector("input").checked?u:null).filter(p=>p!=null);s({ticked:d,text:n.value})}},"Score it"))},e.append(n,r("p"),o,a)}function At(t,e,s){let n=Math.floor(Math.random()*2147483648),o=N(t.generator,n),a=r("input",{type:"number",min:"0",step:"any",placeholder:`Your estimate, in ${o.unit}`}),l=$();a.oninput=()=>l.disabled=!(Number(a.value)>0),a.onkeydown=i=>{i.key==="Enter"&&!l.disabled&&l.click()},l.onclick=()=>{a.disabled=!0,s({seed:n,value:Number(a.value)})},e.append(r("div",{class:"answerbox"},o.question),r("div",{class:"row"},a,r("span",{class:"muted"},o.unit)),r("p"),l)}function Nt(t,e){let s=N(t.generator,e.seed);return r("div",{class:"answerbox"},r("div",{},r("b",{},"Worked: "),s.working),r("div",{class:"muted"},`You said ${e.value}. Anything within \xD71.6 of the worked answer counts: interview maths is about the order of magnitude.`))}var Xe={ok:"\u2713 ",no:"\u2717 ",miss:"+ "};function Qe(t,e=16){let s=String(t).split(" "),n=[""];for(let o of s)(n.at(-1)+" "+o).trim().length>e&&n.at(-1)?n.push(o):n[n.length-1]=(n.at(-1)+" "+o).trim();return n.slice(0,3)}function Ve({slots:t=[],clickable:e=[],onNode:s=()=>{},onDrop:n=null}={}){return{slots:t,clickable:e,onNode:s,onDrop:n}}function Ze(){let t=v("marker",{id:`arrow-${Math.random().toString(36).slice(2)}`,viewBox:"0 0 10 10",refX:"9",refY:"5",markerWidth:"7",markerHeight:"7",orient:"auto-start-reverse"});return t.append(v("path",{d:"M0,0 L10,5 L0,10 z"})),t}function ts(t){let e={};for(let s of t)e[s.id]={x:s.x,y:s.y,w:s.w||120,h:s.h||48,label:s.label};return e}var Dt=t=>[t.x+t.w/2,t.y+t.h/2];function It(t,e,s){let n=s[0]-t[0],o=s[1]-t[1],a=Math.min(Math.abs(e.w/2/(n||1e-9)),Math.abs(e.h/2/(o||1e-9)));return[t[0]+n*a,t[1]+o*a]}function es(t,e){let s=Dt(t),n=Dt(e);return[It(s,t,n),It(n,e,s)]}function ss(t,e,s,n){let[o,a]=es(e[s[0]],e[s[1]]);if(t.append(v("line",{x1:o[0],y1:o[1],x2:a[0],y2:a[1],class:"edge","marker-end":`url(#${n})`})),!s[2])return;let l=v("text",{x:(o[0]+a[0])/2,y:(o[1]+a[1])/2-5,"text-anchor":"middle",class:"edge-label"});l.textContent=s[2],t.append(l)}function ns(t,e){t.addEventListener("click",e),t.addEventListener("keydown",s=>{(s.key==="Enter"||s.key===" ")&&(s.preventDefault(),e())})}function os(t,e){t.addEventListener("dragover",s=>s.preventDefault()),t.addEventListener("drop",s=>{s.preventDefault(),e(s.dataTransfer.getData("text/plain"))})}function rs(t,e,s){let n=s.slots.includes(t.id),o=s.clickable.includes(t.id),a=v("g",{class:`node${n?" slot":""}${o?" clickable":""}`,tabindex:o?"0":"-1"});a.append(v("rect",{x:e.x,y:e.y,width:e.w,height:e.h,rx:9}));let l=v("text",{x:e.x+e.w/2,"text-anchor":"middle"});return a.append(l),o&&ns(a,()=>s.onNode(t.id)),s.onDrop&&n&&os(a,i=>s.onDrop(t.id,i)),{g:a,text:l,isSlot:n}}function qt(t,e,s){let n=Qe(s);t.replaceChildren();let o=e.y+e.h/2-(n.length-1)*15/2+4;n.forEach((a,l)=>{let i=v("tspan",{x:e.x+e.w/2,y:o+l*15});i.textContent=a,t.append(i)})}function as(t,e,s){let n=v("text",{x:t.x+t.w/2,y:t.y+t.h+13+s*12,"text-anchor":"middle",class:`tag ${e.cls||"plain"}`});return n.textContent=`${Xe[e.cls]??"\u{1F3F7} "}${e.s}`,n}function is(t,e,s){t.g.classList.remove("picked","clickable"),e&&(t.g.classList.add(e.ok?"right":"wrong"),e.text&&s(e.text))}function cs({root:t,box:e,groups:s,tagLayer:n,slots:o}){let a=(i,d)=>qt(s[i].text,e[i],d),l=i=>{n.replaceChildren();for(let[d,p]of Object.entries(i))p.forEach((u,f)=>n.append(as(e[d],u,f)))};return{el:t,update(i){for(let d of o)a(d,i[d]||"?"),s[d].g.classList.toggle("filled",!!i[d])},pick(i){for(let[d,p]of Object.entries(s))p.g.classList.toggle("picked",d===i)},tags(i){l(Object.fromEntries(Object.entries(i).map(([d,p])=>[d,p.map(u=>({s:u}))])))},tagResult(i){l(i)},result(i){for(let d of Object.keys(s))is(s[d],i(d),p=>a(d,p))}}}function W(t,e){let s=Ve(e),n=t.width||640,o=t.height||300,a=v("svg",{viewBox:`0 0 ${n} ${o+24}`,class:"diagram",role:"img"}),l=v("defs"),i=Ze();l.append(i),a.append(l);let d=ts(t.nodes);for(let f of t.edges||[])ss(a,d,f,i.id);let p={};for(let f of t.nodes)p[f.id]=rs(f,d[f.id],s),qt(p[f.id].text,d[f.id],p[f.id].isSlot?"?":f.label),a.append(p[f.id].g);let u=v("g");return a.append(u),cs({root:a,box:d,groups:p,tagLayer:u,slots:s.slots})}function Ft(t,e,s){let n={},o=null,a=$(),l=Object.keys(t.slots),i=b(t.palette).map(u=>{let f=r("button",{class:"chip",draggable:"true",onclick:()=>{o=o===u?null:u,i.forEach(g=>g.classList.toggle("sel",g._label===o))}},u);return f._label=u,f.addEventListener("dragstart",g=>g.dataTransfer.setData("text/plain",u)),f}),d=()=>{p.update(n);let u=new Set(Object.values(n));i.forEach(f=>f.classList.toggle("used",u.has(f._label))),a.disabled=l.some(f=>!n[f])},p=W(t.diagram,{slots:l,clickable:l,onNode:u=>{o?(n[u]=o,o=null,i.forEach(f=>f.classList.remove("sel"))):delete n[u],d()},onDrop:(u,f)=>{n[u]=f,d()}});a.onclick=()=>s({placed:{...n}},()=>p.result(u=>({ok:n[u]===t.slots[u],text:n[u]===t.slots[u]?n[u]:`\u2717 ${n[u]} \u2192 ${t.slots[u]}`}))),e.append(r("div",{class:"muted"},"Pick a part, then click the empty box it belongs in (or drag it). Click a filled box to clear it."),p.el,r("div",{class:"chips"},i),a)}function ls(t,e){let s=null,n=t.map(o=>{let l=r("button",{class:"chip",onclick:()=>{s=s===o?null:o,n.forEach(i=>i.classList.toggle("sel",i._label===s)),e(s)}},`\u{1F3F7}\uFE0F ${o}`);return l._label=o,l});return n}function ds(t,e,s){let n=t[e]||=[],o=n.indexOf(s);o>=0?n.splice(o,1):n.push(s)}function us(t,e,s){let n={};for(let o of e){let a=t.answers[o]||[],l=s[o]||[];n[o]=[...l.map(i=>({s:i,cls:a.includes(i)?"ok":"no"})),...a.filter(i=>!l.includes(i)).map(i=>({s:i,cls:"miss"}))]}return n}function Yt(t,e,s){let n={},o=null,a=$(),l=t.diagram.nodes.map(p=>p.id),i=ls(t.stickers,p=>o=p),d=W(t.diagram,{clickable:l,onNode:p=>{if(!o)return m("Pick a sticker first");ds(n,p,o),d.tags(n),a.disabled=!Object.values(n).some(u=>u.length)}});a.onclick=()=>s({placed:JSON.parse(JSON.stringify(n))},()=>d.tagResult(us(t,l,n))),e.append(r("div",{class:"muted"},"Pick a sticker, then click every box that needs it. Click again to take it off. Orange tags after checking are the ones you missed."),d.el,r("div",{class:"chips"},i),a)}function Wt(t,e,s){let n=null,o=null,a=$(),l=()=>a.disabled=n==null||o==null,i=W(t.diagram,{clickable:t.diagram.nodes.map(p=>p.id),onNode:p=>{n=p,i.pick(p),l()}}),d=F(b(t.fixes.map((p,u)=>({text:p,value:u}))),{onChange:([p])=>{o=p,l()}});a.onclick=()=>s({node:n,fix:o},()=>{i.result(p=>p===t.flaw?{ok:!0}:p===n?{ok:!1}:null),Y(d,[t.fix],[o])}),e.append(r("div",{class:"muted"},"Click the weak box, then pick the fix."),i.el,r("div",{class:"group"},r("div",{class:"label"},"The fix"),d),a)}function Gt(t,e,s,n){let o=b(t.items);for(;o.length>2&&o.every((u,f)=>u===t.items[f]);)o=b(t.items);let a=r("ol",{class:"orderlist"}),l=r("button",{class:"btn"},"Check"),i=null,d=()=>a.replaceChildren(...o.map((u,f)=>{let g=r("li",{draggable:"true"},r("span",{class:"muted"},`${f+1}.`),r("span",{class:"txt"},u),r("button",{"aria-label":"Move up",onclick:()=>{f>0&&([o[f-1],o[f]]=[o[f],o[f-1]],d())}},"\u2191"),r("button",{"aria-label":"Move down",onclick:()=>{f<o.length-1&&([o[f+1],o[f]]=[o[f],o[f+1]],d())}},"\u2193"));return g.addEventListener("dragstart",()=>i=f),g.addEventListener("dragover",T=>T.preventDefault()),g.addEventListener("drop",T=>{if(T.preventDefault(),i==null)return;let[P]=o.splice(i,1);o.splice(f,0,P),i=null,d()}),g}));d();let p=()=>s({order:o.slice()},()=>{a.querySelectorAll("li").forEach((u,f)=>{u.classList.add(o[f]===t.items[f]?"right":"wrong"),u.querySelectorAll("button").forEach(g=>g.remove()),u.draggable=!1,o[f]!==t.items[f]&&u.append(r("span",{class:"muted"},`\u2192 ${t.items[f]}`))})});l.onclick=p,e.addEventListener("timeup",()=>{n.submitted||(m("\u23F1\uFE0F Time's up"),p())}),e.append(r("div",{class:"muted"},"Drag, or use the arrows."),a,r("p"),l)}function Kt(t,e,s,n){let o=t.seconds||90,a=[...new Set(t.pairs.map(i=>i[1]))],l=r("button",{class:"btn"},`Start: ${o} seconds`);e.append(r("div",{class:"muted"},`${t.pairs.length} quick ones. Tap the right answer; it moves on by itself.`),r("p"),l),l.onclick=()=>{let i=b(t.pairs),d=0,p=0,u=o,f=[],g=r("div",{class:"timer"},`\u23F1\uFE0F ${u}s`),T=r("div",{class:"lightning-q"}),P=r("div",{class:"options"}),pt=()=>{if(n.submitted)return;s({right:p,shown:d},null);let K=[...f,...i.slice(d)];e.append(K.length?r("div",{class:"answerbox"},r("b",{},`${p} of ${i.length}. To learn: `),r("ul",{},K.map(([A,st])=>r("li",{},`${A} \u2192 ${st}`)))):r("div",{class:"answerbox"},`Clean sweep: ${p} of ${i.length}.`))},ft=()=>{if(d>=i.length)return pt();let[K,A]=i[d];T.textContent=K;let st=b([A,...b(a.filter(U=>U!==A)).slice(0,3)]);P.replaceChildren(...st.map(U=>r("button",{class:"opt",onclick:()=>{let ht=U===A;ht?p++:f.push([K,A]),P.classList.add(ht?"flash-right":"flash-wrong"),setTimeout(()=>P.classList.remove("flash-right","flash-wrong"),350),d++,ft()}},U)))};e.replaceChildren(r("div",{class:"row spread"},g,r("span",{class:"muted"},"Go!")),T,P),c.timers.push(setInterval(()=>{u--,g.textContent=`\u23F1\uFE0F ${u}s`,g.classList.toggle("low",u<=10),u<=0&&(P.replaceChildren(),pt(),q())},1e3)),ft()}}function Jt(t,e,s){let n=t.minutes*60,o=!1,a=r("div",{class:"bigtimer"},Ht(n)),l=r("button",{class:"btn"},"Start the clock"),i=r("button",{class:"btn ghost"},"I'm done"),d=r("div",{class:"muted",style:"text-align:center"},"Talk it through out loud, as if they're listening.");l.onclick=()=>{o=!o,l.textContent=o?"Pause":"Resume",o&&!c.timers.length&&c.timers.push(setInterval(()=>{o&&(n=Math.max(0,n-1),a.textContent=Ht(n),n===Math.floor(t.minutes*15)&&m("\u23F1\uFE0F A quarter left: wrap up and say it back"),n===0&&(m("\u23F1\uFE0F Time"),o=!1,i.click()))},1e3))},i.onclick=()=>{o=!1,q(),l.remove(),i.remove();let p=t.checks.map((u,f)=>r("label",{class:"opt",style:"display:flex;gap:10px"},r("input",{type:"checkbox",value:f}),u));e.append(r("div",{class:"group"},r("div",{class:"label"},"Tick what you did"),r("div",{class:"options"},p)),r("button",{class:"btn",onclick:u=>{u.target.disabled=!0;let f=p.map((g,T)=>g.querySelector("input").checked?T:null).filter(g=>g!=null);s({ticked:f,seconds_used:t.minutes*60-n})}},"Score it"))},e.append(a,d,r("div",{class:"row",style:"justify-content:center"},l,i))}var Ht=t=>`${String(Math.floor(t/60)).padStart(2,"0")}:${String(t%60).padStart(2,"0")}`;var Ut={recall:"\u{1F0CF} Recall",multi:"\u2611\uFE0F Pick all that apply",duel:"\u2696\uFE0F Trade-off duel",write:"\u270D\uFE0F Write it",estimate:"\u{1F522} Estimate",fill:"\u{1F9E9} Fill the diagram",sticker:"\u{1F3F7}\uFE0F Sticker the diagram",flaw:"\u{1F50D} Spot the flaw",order:"\u2195\uFE0F Put it in order",lightning:"\u26A1 Lightning round",sprint:"\u23F1\uFE0F Sprint"},zt=new Set(["recall","write","sprint","lightning"]),Xt={recall:Rt,multi:jt,duel:Mt,write:Pt,estimate:At,fill:Ft,sticker:Yt,flaw:Wt,order:Gt,lightning:Kt,sprint:Jt},Qt={estimate:Nt,recall:()=>null};var Vt="\u{1F479} Boss beaten!";function ps(t,e,s){return t.correct?[s?Vt:"\u2705 Right","right"]:e<.5?["\u274C Not this time","wrong"]:s&&e>=.8?[Vt,"right"]:["\u{1F7E1} Partly","part"]}function fs(t){return t?t.startsWith("vault:")?r("div",{class:"source muted"},`\u{1F4D6} In your notes: ${t.slice(6)}`):r("div",{class:"source"},"\u{1F4D6} ",r("a",{href:t,target:"_blank",rel:"noopener"},"Source")):null}function Zt({field:t,wrapAttrs:e,label:s,send:n,sent:o}){let a=r("button",{class:"btn small",onclick:async()=>{await n(t.value),l.replaceChildren(r("div",{class:"muted"},o))}},s),l=r("div",e,t,a);return l}function hs(t,e,s){let n=r("textarea",{placeholder:"What do you want to ask? (optional)",style:"min-height:60px"});s.after(Zt({field:n,wrapAttrs:{style:"width:100%"},label:"Send to coach",send:o=>x(k("coach_request",{coach:!0,challenge:t.id,prompt:t.prompt,response:et(t,e),question:o.slice(0,1e3)})),sent:"\u{1F4AC} Sent. The coach answers in the panel."})),n.focus()}function ms(t,e){let s=r("input",{type:"text",placeholder:"What's wrong with it?"});e.after(Zt({field:s,wrapAttrs:{class:"row",style:"width:100%"},label:"Flag it",send:n=>x(k("flag",{coach:c.coach,challenge:t.id,note:n.slice(0,500)})),sent:"\u{1F6A9} Flagged. It'll be reviewed."})),s.focus()}function gs(t,e){let s=r("div",{class:"row",style:"margin-top:12px"}),n=r("button",{class:"btn",onclick:()=>ee()},"Next \u25B6");return s.append(n),c.coach&&s.append(r("button",{class:"btn ghost",onclick:()=>hs(t,e,s)},"\u{1F4AC} Ask coach")),s.append(r("button",{class:"btn ghost",onclick:()=>ms(t,s)},"\u{1F6A9} Flag")),setTimeout(()=>n.focus(),50),s}function te({c:t,isBoss:e},s,n,o){let[a,l]=ps(o,n,e),i=Qt[t.type];return r("div",{class:`feedback ${l}`},o.points?r("div",{class:"plus"},`+${o.points}`):null,r("div",{class:"verdict"},a,o.multiplier>1&&o.correct?r("span",{class:"combo"},` \u{1F525} \xD7${o.multiplier}`):""),!o.correct&&n<1?r("div",{class:"muted"},"It comes back at the end of the run for a second chance."):null,i?i(t,s):null,r("div",{style:"margin-top:8px"},V(t.explain)),fs(t.source),gs(t,s))}function bs({card:t,feedbackSlot:e}){t.querySelectorAll("button, input, textarea").forEach(s=>{e.contains(s)||(s.disabled=!0)})}function ys({run:t,c:e,second:s,isBoss:n,state:o},a,l,i){let d=k("answer",{chapter:t.chapter?t.chapter.chapter:null,challenge:e.id,type:e.type,bloom:e.bloom,boss:n,second_chance:s,confidence:o.confidence,fraction:Math.round(l*1e3)/1e3,correct:i.correct,points:i.points,combo:i.combo,response:et(e,a)});return e.type==="write"&&c.coach&&Object.assign(d,{coach:!0,prompt:e.prompt}),d}var ws=({run:t,second:e,isBoss:s},n)=>!n.correct&&!e&&!s&&t.phase==="main";function vs(t,e,s){let{run:n,c:o,second:a,isBoss:l}=t;n.answered++,n.points+=s.points,s.correct&&n.right++,n.bestCombo=Math.max(n.bestCombo,s.combo),n.results.push({id:o.id,second:a,correct:s.correct,fraction:e,points:s.points,boss:l}),ws(t,s)&&n.misses.push(o),l&&(n.bossResult=e)}async function se(t,e,s){let{c:n,state:o}=t;if(o.submitted)return;o.submitted=!0,bs(t);let{fraction:a}=vt(n,e),l=C(),i=kt(n,a,{confidence:o.confidence,combo:c.combo,secondChance:t.second,isBoss:t.isBoss});c.combo=i.combo,await x(ys(t,e,a,i)),vs(t,a,i),s&&s(a),t.feedbackSlot.replaceChildren(te(t,e,a,i)),_t(l,C(),t.isBoss&&a>=.8),L()}var ks=t=>t&&t.game==="system-design-quest"&&Array.isArray(t.events);async function xs(t){try{let e=JSON.parse(await t.files[0].text());if(!ks(e))throw new Error("not a save file");if(!confirm(`Load this save? It replaces what's in this browser (${e.events.length} events).`))return;let s=e.events.filter(n=>n&&typeof n=="object"&&typeof n.kind=="string");c.store.replaceAll(s,E(e.profile)),c.events=s,c.profile=E(e.profile),m("\u{1F4BE} Save loaded"),y()}catch(e){m(`Couldn't load that file: ${e.message}`)}}function $s(){let t=new Blob([JSON.stringify({game:"system-design-quest",v:1,saved:new Date().toISOString(),profile:c.profile,events:c.events})],{type:"application/json"}),e=r("a",{href:URL.createObjectURL(t),download:"system-design-quest-save.json"});document.body.append(e),e.click(),setTimeout(()=>{URL.revokeObjectURL(e.href),e.remove()},1e3)}function Ss(){confirm("Clear your profile? Your progress stays.")&&(c.store.clear(!1),c.profile={},j())}function Es(){confirm("Clear everything: your profile and all your progress? This can't be undone.")&&(c.store.clear(!0),c.profile={},c.events=[],y())}function ne(){let t=r("input",{type:"file",accept:"application/json,.json",hidden:!0});return t.addEventListener("change",()=>xs(t)),r("div",{class:"card"},r("h2",{},"\u{1F4BE} Your save"),r("p",{class:"muted"},"Your progress lives only in this browser, and some browsers clear it after a few weeks without a visit. Download a save to keep it or move it to another device."),r("div",{class:"row"},r("button",{class:"btn ghost",onclick:$s},"Download my save"),r("button",{class:"btn ghost",onclick:()=>t.click()},"Load a save"),t),r("div",{class:"row",style:"margin-top:12px"},r("button",{class:"btn ghost small",onclick:Ss},"Clear profile"),r("button",{class:"btn ghost small",onclick:Es},"Clear everything")))}async function Os(){confirm("Clear your profile? Your progress stays.")&&(c.profile={},await c.store.saveProfile({}),j())}function oe(){return r("div",{class:"row",style:"margin-top:12px"},r("button",{class:"btn ghost small",onclick:Os},"Clear profile"))}function it(t,e,s,n){let o=r("input",{type:"text",maxlength:ot,placeholder:n,value:t[e]||"",autocomplete:"off"});return o.dataset.key=e,r("label",{class:"field"},r("span",{},s),o)}async function Ls(t,e,s){let n={...c.profile};for(let o of t){let a=o.querySelector("input[data-key]");a&&(n[a.dataset.key]=a.value)}n.interview_type=e.value,n.interview_date=s.value,c.profile=E(n),await c.store.saveProfile(c.profile),m("\u{1F464} Saved"),y()}function Bs(t){let e=r("select",{},r("option",{value:""},"Not set"),rt.map(a=>r("option",{value:a,selected:t.interview_type===a},a))),s=r("input",{type:"date",value:t.interview_date||""}),n=[it(t,"name","Your name","What should the game call you?"),it(t,"company","Interviewing with","For example, Acme"),it(t,"role","Role","For example, Engineering manager"),r("label",{class:"field"},r("span",{},"Interview type"),e),r("label",{class:"field"},r("span",{},"Interview date"),s)],o=c.coach?"Saved on this computer, next to your answers. Claude Code reads it to coach you; only you change it, here.":"Stays in this browser. Nothing is sent anywhere.";return r("div",{class:"card"},r("h1",{},"\u{1F464} You"),r("p",{class:"muted"},"Every field is optional, and the game works the same with none. ",o),r("div",{class:"fields"},n),r("p",{class:"muted"},"With a date, the header counts down to it. Nothing locks or unlocks by date: chapters open as you beat bosses."),r("div",{class:"row"},r("button",{class:"btn",onclick:()=>Ls(n,e,s)},"Save"),r("button",{class:"btn ghost",onclick:y},"Back to the map")))}function Cs(t,e,s){return t?R(e.lifts)?`Lifts on ${e.lifts}`:R(s.interview_date)?`Lifts the day after your interview (${s.interview_date})`:"Held until you lift it":"Lifted"}async function re(t,e,s){c.profile=E({...c.profile,holds:{...c.profile.holds||{},[t]:{...e,...s}}}),await c.store.saveProfile(c.profile),m("\u{1F512} Saved. It takes effect the next time the quest is built."),j()}function Ts(t,e,s){let n=e.holds&&e.holds[t.id]||{lifts:null,lifted:!1},o=xt(t.id,e,s),a=r("input",{type:"date",value:n.lifts||"",disabled:!o});a.addEventListener("change",()=>re(t.id,n,{lifts:a.value||null}));let l=()=>{confirm(`Lift "${t.label}" now? Its topics join the game.`)&&re(t.id,n,{lifted:!0})};return r("div",{class:"hold row spread"},r("div",{},r("b",{},`\u{1F512} ${t.label}`),r("div",{class:"muted"},Cs(o,n,e))),o?r("div",{class:"row"},r("label",{class:"field inline"},r("span",{},"Lift on"),a),r("button",{class:"btn ghost small",onclick:l},"Lift now")):null)}function _s(t){let e=D(new Date);return r("div",{class:"card"},r("h2",{},"Hold-backs for mocks"),r("p",{class:"muted"},"Topics held back so a mock stays a fair test. You never see which; you decide when they join."),c.holds.map(s=>Ts(s,t,e)))}function j(){c.run=null,L();let t=c.profile,e=[Bs(t)];c.holds.length&&e.push(_s(t)),e.push(c.coach?oe():ne()),O(...e)}var Rs=["name","company","role","interview_type","interview_date"],ae=(t,e)=>`${t} ${e}${t===1?"":"s"}`;function ct(t){return S(t)?`Quest ${c.bundle.chapters.filter(S).sort((s,n)=>s.chapter-n.chapter).indexOf(t)+1}`:`Chapter ${t.chapter}`}function js(t){return t.length?r("div",{class:"badges"},t.map(e=>r("span",{class:"pill",title:e.label},`${at(e.id)} ${e.label}`))):null}function Ms(t){let e=c.bundle.chapters.length,s=Object.values(t.chapters).filter(a=>a.bossBeaten).length,n=c.profile,o=[n.role,n.interview_type,n.company].filter(Boolean).join(" \xB7 ");return[r("div",{class:"hero"},r("div",{},r("h1",{},n.name?`${n.name}'s quest map`:"Your quest map"),o?r("div",{class:"muted"},`\u{1F3AF} ${o}`):null,r("div",{class:"muted"},`${s} of ${e} bosses beaten. Points come from right answers only; a miss costs nothing and comes back later.`)),js(t.badges)),r("div",{class:"bar",title:`${s} of ${e} bosses`},r("i",{style:`width:${100*s/Math.max(1,e)}%`})),r("p")]}async function Ps(){c.profile=E({...c.profile,intro_dismissed:!0}),await c.store.saveProfile(c.profile),y()}function As(){let t=c.profile;return Rs.some(e=>t[e])||t.intro_dismissed?[]:[r("div",{class:"card hint row spread"},r("span",{},"\u{1F464} Interviewing soon? Add it for a countdown. Optional: the game works the same without it."),r("span",{class:"row"},r("button",{class:"btn small",onclick:j},"Add it"),r("button",{class:"btn ghost small",onclick:Ps},"Not now")))]}function Ns(){let t=c.bundle.held;return t?.challenges||t?.chapters?[r("div",{class:"card hint"},`\u{1F512} Held back for a mock: ${ae(t.challenges,"challenge")}`+(t.chapters?` and ${ae(t.chapters,"chapter")}`:"")+". They join when you lift the hold-back (\u{1F464} You).")]:[]}function Ds(t,e){return t?[r("div",{class:"card next-up"},r("div",{},r("div",{class:"muted"},"\u25B6\uFE0F One next action"),r("h2",{},`${t.emoji} ${ct(t)}: ${t.title}`),r("div",{class:"muted"},t.blurb)),r("button",{class:"btn",onclick:()=>M(t.slug)},e.chapters[t.slug].played?"Replay":"Play"))]:[]}function Is(t,e,s){if(!S(t))return r("div",{class:"muted"},`${ct(t)} \xB7 `,r("span",{class:"pill"},t.pillar));let n=(t.builds_on||[]).filter(o=>s[o]);return r("div",{class:"muted"},`${ct(t)} \xB7 `,e.ready&&!e.bossBeaten?r("span",{class:"pill ready"},"\u2705 Ready"):null,n.length?r("span",{class:"pill",title:n.map(o=>`${o}. ${s[o].title}`).join(`
3
+ `)},`Builds on ${n.join(", ")}`):null)}function qs(t,e){return r("div",{class:"row"},r("button",{class:"btn small",onclick:()=>M(t.slug)},e.played?"Replay":"Play"),!e.bossBeaten&&t.challenges.length>1?r("button",{class:"btn ghost small",title:"Know this already? Beat the boss cold and the next chapter opens.",onclick:()=>le(t.slug)},"\u{1F479} Try the boss now"):null)}function Fs(t){return r("div",{class:"row spread"},r("span",{class:"muted"},"Opens when you beat another boss"),r("button",{class:"btn ghost small",title:"Skip the order and play this chapter now. It stays open.",onclick:()=>ce(t.slug)},"Jump in anyway"))}function Ys(t,e,s){let n="\u2605".repeat(e.stars)+"\u2606".repeat(3-e.stars);return r("div",{class:`card chapter${e.unlocked?"":" locked"}`},r("div",{class:"row spread"},r("span",{class:"emoji"},e.unlocked?t.emoji:"\u{1F512}"),r("span",{class:"stars",title:"Stars: beat the boss, then replay on later days at 80% or better"},n)),Is(t,e,s),r("h3",{},t.title),r("div",{class:"muted"},t.blurb),r("div",{class:"row spread"},r("span",{class:"muted"},e.rank?`\u{1F396}\uFE0F ${e.rank}`:"Not played yet"),r("span",{class:"muted"},e.accuracy!=null?`${Math.round(e.accuracy*100)}% first try`:"")),r("div",{class:"bar"},r("i",{style:`width:${100*e.answered/Math.max(1,e.total)}%`})),e.unlocked?qs(t,e):Fs(t))}var ie=(t,e)=>r("h2",{class:"section"},t,r("span",{class:"muted small"},e));function Ws(t){let e=[...c.bundle.chapters].sort((d,p)=>d.chapter-p.chapter),s=Object.fromEntries(e.map(d=>[d.chapter,d])),n=d=>Ys(d,t.chapters[d.slug],s),o=e.filter(d=>!S(d)),a=e.filter(S),l=d=>d.filter(p=>t.chapters[p.slug].bossBeaten).length,i=[ie("\u{1F4DA} Training ",`${l(o)} of ${o.length} bosses beaten \xB7 chapters open in order, or jump in`),r("div",{class:"chapters"},o.map(n))];return a.length&&i.push(ie("\u{1F5FA}\uFE0F Quests ",`${l(a)} of ${a.length} beaten \xB7 design one product end to end; all open`),r("div",{class:"chapters"},a.map(n))),i}function y(){c.run=null,c.combo=0,L();let t=C();for(let e of t.newUnlocks)x(k("unlock",{slug:e}));O(...Ms(t),...As(),...Ns(),...Ds(Q(t),t),...Ws(t))}function Gs(t,e){return t.results.filter(s=>!s.second).filter(s=>!s.correct&&!s.boss).map(s=>e.challenges.find(n=>n.id===s.id)).filter(Boolean)}function Ks({ch:t,missed:e,bossFailed:s,otherUp:n}){return s?`Play ${t.title} and take the boss again at the end.`:e.length?`Replay the ${e.length} you missed while they're fresh${n?`, then try ${n.title}`:""}.`:n?`Next: ${n.title}.`:"Come back on another day and replay it: a later day at 80% earns the next star."}function Hs(t){return t==null?null:r("span",{class:"stat"},t>=.8?"\u{1F479} Boss beaten":`\u{1F479} Boss: ${Math.round(t*100)}%, needs 80%`)}function Js(t,e){return r("div",{class:"row"},r("span",{class:"stat"},`\u2705 ${t.right} of ${t.answered} right`),r("span",{class:"stat"},`\u{1F525} best run of ${t.bestCombo}`),Hs(t.bossResult),e?r("span",{class:"stat"},`${"\u2605".repeat(e.stars)}${"\u2606".repeat(3-e.stars)} ${e.rank||""}`):null)}function Us(t){return t.length?r("div",{},r("div",{class:"muted"},"Missed first time:"),r("ul",{},t.map(e=>r("li",{},e.prompt)))):null}function zs({ch:t,missed:e,bossFailed:s,otherUp:n}){return r("div",{class:"row",style:"margin-top:12px"},s?r("button",{class:"btn",onclick:()=>M(t.slug)},`Play ${t.title}`):null,e.length?r("button",{class:s?"btn ghost":"btn",onclick:()=>M(t.slug,e)},"Replay the misses"):null,n?r("button",{class:e.length?"btn ghost":"btn",onclick:()=>M(n.slug)},`Play ${n.title}`):null,r("button",{class:"btn ghost",onclick:y},"Map"))}function de(){let t=c.run,e=C(),s=t.chapter,n=s?e.chapters[s.slug]:null;x(k("session_end",{coach:c.coach,chapter:s?s.chapter:null,points:t.points,answered:t.answered,right:t.right,boss:t.bossResult}));let o=Q(e),a={ch:s,missed:Gs(t,s),bossFailed:t.bossResult!=null&&t.bossResult<.8,otherUp:o&&o.slug!==s?.slug?o:null};c.run=null,O(r("div",{class:"card"},r("div",{class:"muted"},s?`${s.emoji} ${s.title}`:"Coach's challenge"),r("div",{class:"big"},`+${t.points} XP`),Js(t,n),r("h3",{style:"margin-top:16px"},"\u25B6\uFE0F One next action"),r("p",{},Ks(a)),Us(a.missed),zs(a))),L()}var Xs=[["guess","\u{1F62C} Guess"],["fairly","\u{1F642} Fairly"],["sure","\u{1F60E} Sure (\xD71.5 if right)"]],Qs=new Set(["lightning","sprint"]);function lt(t,e,s,n){return{chapter:t,queue:e,boss:s,phase:n,i:0,misses:[],points:0,answered:0,right:0,bestCombo:0,results:[],bossResult:null}}function M(t,e=null){let s=J(t),n=e||Et(s);c.run=lt(s,n.filter(o=>o.id!==s.boss).map(o=>({c:o,second:!1})),e?null:n.find(o=>o.id===s.boss),"main"),c.combo=0,x(k("session_start",{chapter:s.chapter,slug:t})),G()}function ce(t){x(k("unlock",{slug:t,chosen:!0})),M(t)}function le(t){let e=J(t),s=e.challenges.find(n=>n.id===e.boss);c.run=lt(e,[{c:s,second:!1}],null,"boss"),c.combo=0,x(k("session_start",{chapter:e.chapter,slug:t,boss_only:!0})),G()}function ue(t,e){c.run=lt(e,[{c:t,second:!1}],null,"single"),G()}function Vs(){return c.run.queue[c.run.i]}var Zs=new Set(["main","second"]);function ee(){let t=c.run;if(t.i++,t.i<t.queue.length)return G();if(t.phase==="main"&&t.misses.length)return t.phase="second",t.queue=t.misses.map(e=>({c:e,second:!0})),t.misses=[],t.i=0,m("\u{1F501} Second chance: the ones you missed, for half points"),G();if(t.boss&&Zs.has(t.phase))return t.phase="boss",t.queue=[{c:t.boss,second:!1}],t.i=0,m("\u{1F479} Boss time"),G();de()}function tn(){let t=c.run;return r("div",{class:"dots",title:"This run"},t.queue.map((e,s)=>{let n=t.results.find(a=>a.id===e.c.id&&a.second===e.second),o=n?n.correct?"right":n.fraction>=.5?"part":"wrong":s===t.i?"now":"";return r("i",{class:o})}))}function en(t){let e=()=>{confirm("Leave this run? Your answers so far are kept.")&&y()};return r("div",{class:"runbar"},r("div",{},r("b",{},t.chapter?`${t.chapter.emoji} ${t.chapter.title}`:"Coach's challenge")," ",tn()),r("button",{class:"btn ghost small",onclick:e},"Map"))}function sn({run:t,c:e,second:s,isBoss:n}){return r("div",{class:"kind"},r("span",{class:"pill"},Ut[e.type]),r("span",{class:"pill",title:"Revised Bloom's level"},e.bloom),n?r("span",{class:"pill boss-tag"},"\u{1F479} Boss"):null,s?r("span",{class:"pill"},"\u{1F501} Second chance"):null,t.phase==="single"?r("span",{class:"pill"},"\u{1F3AF} Coach's challenge"):null)}function nn(t){let e=r("div",{class:"conf"},r("span",{class:"muted"},"How sure are you?"));for(let[s,n]of Xs){let o=r("button",{onclick:()=>{t.confidence=s,e.querySelectorAll("button").forEach(a=>a.classList.toggle("sel",a===o))}},n);e.append(o)}return e}function on(t,e,s,n){let o=t,a=r("span",{class:"timer"},`\u23F1\uFE0F ${o}s`);e.append(a),c.timers.push(setInterval(()=>{n.submitted||(o--,a.textContent=`\u23F1\uFE0F ${o}s`,a.classList.toggle("low",o<=10),o<=0&&s.dispatchEvent(new Event("timeup")))},1e3))}function G(){let t=c.run,{c:e,second:s}=Vs(),n={run:t,c:e,second:s,isBoss:t.phase==="boss",state:{confidence:null,submitted:!1}};L(),n.card=r("div",{class:"card challenge"}),n.feedbackSlot=r("div");let o=sn(n),a=r("div");n.card.append(o,r("div",{class:"prompt"},e.prompt)),zt.has(e.type)||n.card.append(nn(n.state)),n.card.append(a,n.feedbackSlot),Xt[e.type](e,a,(l,i)=>se(n,l,i),n.state),e.seconds&&!Qs.has(e.type)&&on(e.seconds,o,a,n.state),O(en(t),n.card)}function pe(){let t=w("coach");t.hidden=!1,I.querySelector(".layout").classList.add("with-coach"),t.replaceChildren(r("div",{class:"row spread"},r("h3",{style:"margin:0"},"\u{1F4AC} Coach"),r("span",{class:"pill"},"Claude Code")),r("div",{class:"muted",style:"font-size:13px"},'When Claude Code is running beside you, it sees your written answers and "Ask coach" requests and replies here. It never sets your points.'),r("div",{id:"assigned"}),r("div",{class:"msgs",id:"msgs"}))}function rn(t){let e=new Date(t.ts).toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit"});return r("div",{class:"msg"},r("div",{class:"t"},e),V(t.text))}function an(t){if(!t.length)return;let e=w("msgs");e.append(...t.map(rn)),e.scrollTop=e.scrollHeight,c.coachNext>0&&m("\u{1F4AC} The coach replied")}function cn(t){let e=t.chapter?c.bundle.chapters.find(s=>s.chapter===t.chapter):null;return r("div",{class:"assigned"},r("div",{},r("b",{},"\u{1F3AF} Coach's challenge"),t.why?r("div",{class:"muted"},t.why):null),r("div",{},t.challenge.prompt),r("button",{class:"btn small",onclick:()=>ue(t.challenge,e)},"Play it"))}function ln(t){let e=new Set(c.events.filter(n=>n.kind==="answer").map(n=>n.challenge)),s=t.filter(n=>!e.has(n.challenge.id));s.length!==c.assigned.length&&(s.length>c.assigned.length&&m("\u{1F3AF} The coach set you a challenge"),c.assigned=s,w("assigned").replaceChildren(...s.map(cn)))}async function dt(){try{let t=await(await fetch(`/api/coach?after=${c.coachNext}`,{cache:"no-store"})).json();an(t.messages),c.coachNext=t.next,ln(await(await fetch("/api/assigned",{cache:"no-store"})).json())}catch{}}async function fe(t,{base:e=X,lookForServer:s=!0}={}){Lt(t,e);let n=!1;if(s)try{let o=await fetch("/api/health",{cache:"no-store"});n=o.ok&&(await o.json()).ok}catch{}c.store=n?new Z:new tt,c.coach=n,c.bundle=await(await fetch(new URL("chapters.json",X),{cache:"no-cache"})).json(),c.events=await c.store.load(),c.profile=E(await c.store.loadProfile()),c.holds=await c.store.holds(),w("home").onclick=()=>{(!c.run||confirm("Back to the map? Your answers so far are kept."))&&y()},w("you").onclick=()=>{(!c.run||confirm("Leave this run? Your answers so far are kept."))&&j()},c.coach&&(pe(),await dt(),setInterval(dt,2500)),y()}function he(t){O(r("div",{class:"card"},r("h2",{},"Couldn't start"),r("p",{},String(t))))}var me=new URL(".",import.meta.url);function B(t,e={},...s){let n=document.createElement(t);for(let[o,a]of Object.entries(e))n.setAttribute(o,a);return n.append(...s),n}var ut=class extends HTMLElement{connectedCallback(){if(this.shadowRoot)return;let e=this.attachShadow({mode:"open"});e.append(B("link",{rel:"stylesheet",href:new URL("style.css",me).href}),B("header",{class:"top"},B("button",{class:"brand",id:"home",title:"Back to the map"},"\u2694\uFE0F ",B("span",{},"System Design Quest")),B("div",{class:"stats",id:"stats","aria-live":"polite"}),B("button",{class:"you",id:"you",title:"Your profile: optional"},"\u{1F464} You")),B("div",{class:"layout"},B("section",{id:"screen",tabindex:"-1","aria-label":"System Design Quest"}),B("aside",{id:"coach",class:"coach",hidden:""})),B("div",{id:"toasts",class:"toasts","aria-live":"polite"})),fe(e,{base:me,lookForServer:!this.hasAttribute("static")}).catch(he)}};customElements.get("system-design-quest")||customElements.define("system-design-quest",ut);
4
+ //# sourceMappingURL=element.js.map