0
TOOLS#2c1a16cd
PROMPT SET - An AI field guide for engineers & project managers
@Owner·deposited 1d ago·updated 1d ago·19 views
TOOLS#2c1a16cd
PROMPT SET - An AI field guide for engineers & project managers
OW
@Owner
19Views
0Comments
0Forks
0Saves
SHARE · REMIX
PROMPT SET - An AI field guide for engineers & project managers — a HTML Tools widget by @Owner.
CONTROLS
#engineering#prompt#game
No comments yet. Be the first!
✦ Remix with AI
SDK in this widgetNo Vibes SDK features detected yet
Generated prompt
You are helping me modify a vibe-coded widget from itjustvibes.com.
[VIBE CODE: "PROMPT SET - An AI field guide for engineers & project managers" by @Owner]
Source: https://itjustvibes.com/Owner/prompt-set-an-ai-field-guide-for-engineers-project-managers
Type: HTML
--- SOURCE CODE ---
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<title>PROMPT SET - An AI field guide for engineers & project managers</title>
<style>
:root{
--ink:#0b1322; --sheet:#11203a; --sheet-2:#0d1a30; --raise:#16263f;
--hair:rgba(122,162,224,0.18); --hair-2:rgba(122,162,224,0.10);
--blueprint:#4aa3ff; --cyan:#74d0ff; --trace:#f0b53f; --violet:#b08cff;
--redline:#ff5a5f; --verified:#34c98a;
--mist:#dde6f7; --graphite:#8895b1; --faint:#5d6b88;
--sans:system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
--mono:ui-monospace,"SF Mono","JetBrains Mono","Roboto Mono",Menlo,Consolas,monospace;
}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent;}
html,body{margin:0;min-height:100%;background:var(--ink);color:var(--mist);font-family:var(--sans);}
body{overflow-x:hidden;}
button{font-family:inherit;cursor:pointer;}
::selection{background:rgba(74,163,255,.3);}
/* blueprint backdrop */
#bg{position:fixed;inset:0;z-index:0;pointer-events:none;
background:
linear-gradient(var(--hair-2) 1px,transparent 1px),
linear-gradient(90deg,var(--hair-2) 1px,transparent 1px),
radial-gradient(120% 80% at 50% 0%,#13243f 0%,#0b1322 60%);
background-size:32px 32px,32px 32px,100% 100%;}
#bg::after{content:"";position:absolute;inset:0;
background:
linear-gradient(rgba(122,162,224,.05) 1px,transparent 1px),
linear-gradient(90deg,rgba(122,162,224,.05) 1px,transparent 1px);
background-size:160px 160px,160px 160px;}
#app{position:relative;z-index:1;max-width:980px;margin:0 auto;padding:0 16px 120px;}
/* ---------- title block (top bar) ---------- */
.titleblock{position:sticky;top:0;z-index:20;margin:0 -16px 18px;padding:10px 16px;
background:linear-gradient(180deg,rgba(11,19,34,.96),rgba(11,19,34,.82));
backdrop-filter:blur(10px);border-bottom:1px solid var(--hair);
display:flex;align-items:center;gap:14px;flex-wrap:wrap;}
.tb-mark{font-family:var(--mono);font-weight:700;letter-spacing:.22em;font-size:13px;color:var(--cyan);white-space:nowrap;display:flex;align-items:center;gap:8px;}
.tb-mark .gl{width:14px;height:14px;border:2px solid var(--cyan);border-radius:2px;transform:rotate(45deg);box-shadow:0 0 12px rgba(116,208,255,.5);}
.tb-sheet{font-family:var(--mono);font-size:11px;color:var(--graphite);letter-spacing:.1em;border-left:1px solid var(--hair);padding-left:12px;text-transform:uppercase;line-height:1.3;min-width:0;}
.tb-sheet b{color:var(--mist);display:block;font-size:11.5px;letter-spacing:.08em;}
.tb-meters{display:flex;gap:14px;margin-left:auto;}
.meter{font-family:var(--mono);font-size:10px;letter-spacing:.08em;color:var(--graphite);text-transform:uppercase;min-width:72px;}
.meter .lab{display:flex;justify-content:space-between;margin-bottom:3px;}
.meter .val{color:var(--mist);font-weight:700;}
.meter .bar{height:5px;border-radius:3px;background:var(--sheet-2);overflow:hidden;border:1px solid var(--hair);}
.meter .fill{height:100%;width:60%;transition:width .6s cubic-bezier(.2,.8,.2,1);}
.m-quality .fill{background:linear-gradient(90deg,#2f7fd6,var(--blueprint));}
.m-hours .fill{background:linear-gradient(90deg,#caa23a,var(--trace));}
.m-trust .fill{background:linear-gradient(90deg,#28a874,var(--verified));}
.meter.low .fill{background:linear-gradient(90deg,#b23,var(--redline))!important;}
.meter.low .val{color:var(--redline);}
.tb-actions{display:flex;gap:7px;}
.ib{font-family:var(--mono);font-size:10.5px;letter-spacing:.06em;color:var(--graphite);background:var(--sheet-2);
border:1px solid var(--hair);border-radius:7px;padding:8px 10px;transition:.16s;text-transform:uppercase;white-space:nowrap;}
.ib:hover{color:var(--cyan);border-color:var(--blueprint);}
@media (max-width:720px){
.tb-meters{order:3;width:100%;margin-left:0;justify-content:space-between;gap:8px;}
.meter{min-width:0;flex:1;}
.tb-sheet{border-left:none;padding-left:0;}
}
/* ---------- sheet / card ---------- */
.stage{animation:rise .4s cubic-bezier(.2,.9,.25,1);}
@keyframes rise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
.sheet{position:relative;background:linear-gradient(160deg,var(--sheet),var(--sheet-2));
border:1px solid var(--hair);border-radius:4px;padding:24px;margin-bottom:16px;
background-image:linear-gradient(160deg,var(--sheet),var(--sheet-2)),linear-gradient(var(--hair-2) 1px,transparent 1px),linear-gradient(90deg,var(--hair-2) 1px,transparent 1px);
background-size:100% 100%,28px 28px,28px 28px;box-shadow:0 24px 60px rgba(0,0,0,.4);}
.sheet::before{content:"";position:absolute;top:0;right:0;border-width:0 18px 18px 0;border-style:solid;border-color:transparent var(--ink) transparent transparent;}
.sheet::after{content:"";position:absolute;top:2px;right:2px;width:22px;height:22px;border-top:1px solid var(--hair);}
.eyebrow{font-family:var(--mono);font-size:11px;letter-spacing:.28em;text-transform:uppercase;color:var(--trace);display:flex;align-items:center;gap:10px;margin-bottom:10px;}
.eyebrow .code{color:var(--cyan);border:1px solid var(--hair);border-radius:5px;padding:3px 7px;letter-spacing:.14em;}
h1.title{font-size:clamp(30px,7vw,52px);line-height:.98;margin:0 0 8px;font-weight:800;letter-spacing:-.01em;}
h2.title{font-size:clamp(24px,5.5vw,36px);line-height:1.02;margin:0 0 6px;font-weight:800;letter-spacing:-.01em;}
.lead{color:var(--graphite);font-size:16px;line-height:1.5;margin:0 0 18px;max-width:60ch;}
.lead b{color:var(--mist);font-weight:600;}
.bullets{list-style:none;margin:0 0 18px;padding:0;display:flex;flex-direction:column;gap:12px;}
.bullets li{display:flex;gap:12px;align-items:flex-start;font-size:15.5px;line-height:1.45;}
.bullets li .dim{flex:none;font-family:var(--mono);font-size:12px;color:var(--blueprint);margin-top:2px;width:18px;text-align:right;}
.bullets li b{color:var(--cyan);font-weight:700;}
.bullets li .warn{color:var(--trace);}
.tipbox{display:flex;gap:12px;align-items:flex-start;background:rgba(240,181,63,.08);border:1px solid rgba(240,181,63,.28);
border-radius:6px;padding:13px 15px;margin-bottom:20px;font-size:14.5px;line-height:1.45;color:var(--mist);}
.tipbox .tg{flex:none;font-family:var(--mono);font-size:10px;letter-spacing:.16em;color:var(--trace);border:1px solid rgba(240,181,63,.35);border-radius:4px;padding:3px 6px;text-transform:uppercase;}
.goalbox{display:flex;gap:12px;align-items:flex-start;background:rgba(74,163,255,.08);border:1px solid rgba(74,163,255,.26);
border-radius:6px;padding:13px 15px;margin-bottom:20px;font-size:14.5px;line-height:1.45;}
.goalbox .tg{flex:none;font-family:var(--mono);font-size:10px;letter-spacing:.16em;color:var(--cyan);border:1px solid rgba(74,163,255,.35);border-radius:4px;padding:3px 6px;text-transform:uppercase;}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;border:none;font-weight:700;font-size:15px;
letter-spacing:.02em;border-radius:7px;padding:14px 22px;transition:.16s;}
.btn-primary{background:linear-gradient(120deg,var(--blueprint),#74b6ff);color:#04101f;box-shadow:0 10px 26px rgba(74,163,255,.28);}
.btn-primary:hover{transform:translateY(-1px);box-shadow:0 14px 32px rgba(74,163,255,.42);}
.btn-amber{background:linear-gradient(120deg,var(--trace),#ffd071);color:#1e1404;box-shadow:0 10px 26px rgba(240,181,63,.26);}
.btn-amber:hover{transform:translateY(-1px);}
.btn-ghost{background:var(--sheet-2);color:var(--mist);border:1px solid var(--hair);}
.btn-ghost:hover{border-color:var(--blueprint);color:var(--cyan);}
.btn-block{width:100%;}
.btnrow{display:flex;gap:10px;flex-wrap:wrap;margin-top:6px;}
/* ---------- build interaction ---------- */
.build-grid{display:grid;grid-template-columns:1fr 300px;gap:16px;align-items:start;}
@media (max-width:760px){ .build-grid{grid-template-columns:1fr;} }
.pool-head{font-family:var(--mono);font-size:11px;letter-spacing:.1em;color:var(--graphite);text-transform:uppercase;margin:2px 0 10px;display:flex;justify-content:space-between;align-items:center;}
.pool{display:flex;flex-direction:column;gap:9px;}
.chip{position:relative;text-align:left;background:var(--raise);border:1px solid var(--hair);border-radius:6px;
padding:12px 13px 12px 40px;color:var(--mist);font-size:14.5px;line-height:1.35;transition:.14s;width:100%;}
.chip:hover{border-color:var(--graphite);}
.chip .cat{position:absolute;left:11px;top:12px;font-family:var(--mono);font-size:9px;letter-spacing:.04em;color:var(--faint);writing-mode:vertical-rl;transform:rotate(180deg);text-transform:uppercase;}
.chip .box{position:absolute;left:13px;bottom:12px;width:16px;height:16px;border:1.5px solid var(--faint);border-radius:3px;display:none;}
.chip.sel{border-color:var(--blueprint);background:linear-gradient(180deg,rgba(74,163,255,.12),var(--raise));box-shadow:0 0 0 1px rgba(74,163,255,.25) inset;}
.chip.sel .cat{color:var(--cyan);}
.chip.locked{opacity:.4;pointer-events:none;}
.chip .num{position:absolute;right:11px;top:10px;font-family:var(--mono);font-size:10px;color:var(--cyan);background:rgba(74,163,255,.12);border-radius:4px;padding:2px 6px;display:none;}
.chip.sel .num{display:block;}
.ordbtns{position:absolute;right:10px;bottom:9px;display:none;gap:5px;}
.chip.sel.ordmode .ordbtns{display:flex;}
.ordbtns button{font-family:var(--mono);font-size:11px;width:24px;height:22px;border-radius:4px;background:var(--sheet-2);border:1px solid var(--hair);color:var(--cyan);padding:0;}
.build-side{position:sticky;top:120px;display:flex;flex-direction:column;gap:14px;}
@media (max-width:760px){ .build-side{position:static;} }
/* context window visualization (signature element) */
.ctxwin{background:var(--sheet-2);border:1px solid var(--hair);border-radius:6px;padding:13px;}
.ctxwin h4{font-family:var(--mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--cyan);margin:0 0 4px;display:flex;justify-content:space-between;align-items:center;}
.ctxwin .sub{font-family:var(--mono);font-size:10px;color:var(--faint);margin-bottom:10px;letter-spacing:.04em;}
.cap{height:6px;border-radius:3px;background:#0a1426;border:1px solid var(--hair);overflow:hidden;margin-bottom:3px;}
.cap .fill{height:100%;width:0;background:linear-gradient(90deg,var(--blueprint),var(--cyan));transition:width .4s ease;}
.cap.over .fill{background:linear-gradient(90deg,var(--redline),#ff8a8d);}
.cap-lab{font-family:var(--mono);font-size:9.5px;color:var(--graphite);text-align:right;margin-bottom:11px;letter-spacing:.04em;}
.cap-lab.over{color:var(--redline);}
.ctx-stack{position:relative;display:flex;flex-direction:column-reverse;gap:4px;min-height:120px;border-radius:4px;overflow:hidden;}
.ctx-empty{color:var(--faint);font-family:var(--mono);font-size:11px;text-align:center;padding:34px 8px;line-height:1.6;border:1px dashed var(--hair);border-radius:4px;}
.ctx-layer{position:relative;border-radius:3px;padding:6px 9px;font-size:11px;line-height:1.25;border-left:3px solid var(--blueprint);
background:rgba(74,163,255,.10);color:var(--mist);animation:layin .3s ease;overflow:hidden;}
@keyframes layin{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.ctx-layer .lt{font-family:var(--mono);font-size:8.5px;letter-spacing:.08em;text-transform:uppercase;opacity:.8;display:block;}
.ctx-layer.role{border-color:var(--blueprint);background:rgba(74,163,255,.12);}
.ctx-layer.task{border-color:var(--cyan);background:rgba(116,208,255,.12);}
.ctx-layer.context{border-color:var(--verified);background:rgba(52,201,138,.12);}
.ctx-layer.format{border-color:var(--trace);background:rgba(240,181,63,.12);}
.ctx-layer.constraint{border-color:var(--violet);background:rgba(176,140,255,.12);}
.ctx-layer.noise{border-color:var(--redline);color:var(--redline);
background-image:repeating-linear-gradient(45deg,rgba(255,90,95,.16),rgba(255,90,95,.16) 6px,transparent 6px,transparent 12px);}
.ctx-layer.dropped{opacity:.28;filter:grayscale(.6);}
.attn{position:absolute;left:0;right:0;height:40px;pointer-events:none;
background:linear-gradient(180deg,transparent,rgba(116,208,255,.35),transparent);opacity:0;}
.attn.run{animation:sweep 1s ease;}
@keyframes sweep{0%{top:-40px;opacity:.9}100%{top:100%;opacity:0}}
.pin{position:absolute;right:6px;top:5px;font-family:var(--mono);font-size:8px;color:var(--verified);border:1px solid rgba(52,201,138,.4);border-radius:3px;padding:1px 4px;letter-spacing:.06em;}
/* preview */
.preview{background:var(--sheet-2);border:1px solid var(--hair);border-radius:6px;padding:13px;}
.preview h4{font-family:var(--mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--graphite);margin:0 0 9px;}
.pv-body{font-size:13px;line-height:1.5;color:var(--mist);min-height:48px;transition:.2s;}
.pv-body.bad{color:var(--graphite);}
.pv-meta{display:flex;gap:6px;margin-top:11px;}
.dial{flex:1;font-family:var(--mono);font-size:9px;letter-spacing:.04em;color:var(--graphite);text-transform:uppercase;text-align:center;}
.dial .v{display:block;font-size:14px;font-weight:700;margin-top:2px;}
/* decision interaction */
.scene-meta{font-family:var(--mono);font-size:11px;letter-spacing:.1em;color:var(--graphite);text-transform:uppercase;margin-bottom:8px;}
.situation{font-size:18px;line-height:1.45;font-weight:600;margin:0 0 6px;}
.sit-detail{color:var(--graphite);font-size:14.5px;line-height:1.5;margin:0 0 18px;}
.options{display:flex;flex-direction:column;gap:10px;}
.opt{position:relative;text-align:left;background:var(--raise);border:1px solid var(--hair);border-radius:7px;padding:15px 16px;color:var(--mist);font-size:15px;line-height:1.4;transition:.14s;}
.opt:hover{border-color:var(--blueprint);transform:translateX(3px);}
.opt:disabled{opacity:.4;pointer-events:none;}
.opt .ok{font-family:var(--mono);font-size:11px;color:var(--graphite);}
.outcome{margin-top:16px;border-radius:7px;padding:15px 16px;font-size:14.5px;line-height:1.5;animation:rise .3s ease;border:1px solid var(--hair);background:var(--sheet-2);}
.outcome.good{border-color:rgba(52,201,138,.4);background:rgba(52,201,138,.08);}
.outcome.bad{border-color:rgba(255,90,95,.4);background:rgba(255,90,95,.08);}
.outcome.risky{border-color:rgba(240,181,63,.4);background:rgba(240,181,63,.08);}
.outcome .vh{font-family:var(--mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;display:block;margin-bottom:5px;}
.outcome.good .vh{color:var(--verified);} .outcome.bad .vh{color:var(--redline);} .outcome.risky .vh{color:var(--trace);}
.deltas{display:flex;gap:12px;margin-top:10px;font-family:var(--mono);font-size:11px;flex-wrap:wrap;}
.deltas span.up{color:var(--verified);} .deltas span.dn{color:var(--redline);}
/* debrief */
.vrow{display:flex;gap:11px;align-items:flex-start;padding:10px 0;border-bottom:1px solid var(--hair-2);font-size:14px;line-height:1.4;}
.vrow:last-child{border-bottom:none;}
.vrow .ic{flex:none;width:20px;height:20px;border-radius:50%;display:grid;place-items:center;font-size:11px;font-weight:700;margin-top:1px;}
.vrow.good .ic{background:rgba(52,201,138,.18);color:var(--verified);}
.vrow.bad .ic{background:rgba(255,90,95,.18);color:var(--redline);}
.vrow.meh .ic{background:rgba(240,181,63,.18);color:var(--trace);}
.vrow .tx b{color:var(--mist);} .vrow .tx span{color:var(--graphite);}
/* modal */
.scrim{position:fixed;inset:0;z-index:50;display:none;place-items:center;padding:16px;background:rgba(6,11,20,.8);backdrop-filter:blur(8px);overflow:auto;}
.scrim.show{display:grid;}
.modal{position:relative;width:min(94vw,720px);max-height:88vh;overflow:auto;background:linear-gradient(160deg,var(--sheet),var(--sheet-2));border:1px solid var(--hair);border-radius:8px;padding:24px;animation:rise .35s ease;box-shadow:0 30px 80px rgba(0,0,0,.6);}
.modal-close{position:absolute;top:12px;right:12px;font-family:var(--mono);font-size:12px;background:var(--sheet-2);border:1px solid var(--hair);border-radius:6px;color:var(--graphite);padding:7px 11px;}
.modal-close:hover{color:var(--redline);border-color:var(--redline);}
.modal h3{font-size:22px;margin:0 0 4px;letter-spacing:-.01em;}
.modal .msub{color:var(--graphite);font-size:14px;margin:0 0 18px;}
/* cross-AI table */
.xtable{width:100%;border-collapse:collapse;font-size:13px;margin-bottom:8px;}
.xtable th,.xtable td{text-align:left;padding:10px 9px;border-bottom:1px solid var(--hair);vertical-align:top;}
.xtable th{font-family:var(--mono);font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:var(--graphite);}
.xtable th.c{color:var(--cyan);} .xtable td .feat{font-weight:600;color:var(--mist);display:block;}
.xtable td .det{color:var(--faint);font-size:11.5px;}
.xtable td.concept{font-weight:600;color:var(--trace);width:26%;}
@media (max-width:640px){ .xtable,.xtable tbody,.xtable tr,.xtable td,.xtable th{display:block;width:100%;} .xtable thead{display:none;} .xtable tr{border:1px solid var(--hair);border-radius:6px;margin-bottom:10px;padding:6px;} .xtable td{border-bottom:1px solid var(--hair-2);} .xtable td.concept{color:var(--trace);font-size:15px;} .xtable td::before{content:attr(data-h);font-family:var(--mono);font-size:9px;letter-spacing:.1em;text-transform:uppercase;color:var(--cyan);display:block;margin-bottom:3px;} .xtable td.concept::before{content:"";} }
.tiplist{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:10px;}
.tiplist li{display:flex;gap:10px;font-size:14px;line-height:1.45;}
.tiplist li .b{flex:none;color:var(--trace);font-family:var(--mono);}
.tiplist li b{color:var(--cyan);}
/* playbook + kit */
.pb-item{border:1px solid var(--hair);border-radius:6px;margin-bottom:10px;overflow:hidden;}
.pb-head{display:flex;justify-content:space-between;align-items:center;padding:12px 14px;background:var(--sheet-2);}
.pb-head .nm{font-weight:700;font-size:14.5px;}
.pb-head .tg{font-family:var(--mono);font-size:9.5px;letter-spacing:.1em;color:var(--verified);text-transform:uppercase;}
.pb-body{padding:0 14px;max-height:0;overflow:hidden;transition:max-height .3s ease,padding .3s ease;}
.pb-item.open .pb-body{max-height:520px;padding:12px 14px;}
pre.kit{white-space:pre-wrap;font-family:var(--mono);font-size:11.5px;line-height:1.5;color:var(--mist);background:#08111f;border:1px solid var(--hair);border-radius:6px;padding:14px;margin:0;overflow:auto;}
.pb-empty{color:var(--faint);font-family:var(--mono);font-size:12px;text-align:center;padding:24px;}
/* results */
.rating{font-family:var(--mono);font-size:12px;letter-spacing:.18em;color:var(--cyan);text-transform:uppercase;margin-bottom:6px;}
.recap{list-style:none;padding:0;margin:18px 0;display:grid;grid-template-columns:1fr 1fr;gap:10px;}
@media(max-width:560px){ .recap{grid-template-columns:1fr;} }
.recap li{background:var(--sheet-2);border:1px solid var(--hair);border-radius:6px;padding:12px;display:flex;gap:10px;}
.recap li .n{font-family:var(--mono);font-size:11px;color:var(--trace);flex:none;}
.recap li b{display:block;color:var(--mist);font-size:14px;margin-bottom:2px;}
.recap li span{color:var(--graphite);font-size:12.5px;line-height:1.35;}
/* setback */
.setback .modal{border-color:rgba(255,90,95,.5);}
.cloud{font-family:var(--mono);color:var(--redline);font-size:11px;letter-spacing:.2em;text-transform:uppercase;border:1.5px dashed var(--redline);border-radius:40px;padding:6px 16px;display:inline-block;margin-bottom:14px;}
:focus-visible{outline:2px solid var(--cyan);outline-offset:2px;}
@media (prefers-reduced-motion:reduce){ *{animation:none!important;transition:none!important;} }
</style>
</head>
<body>
<div id="bg"></div>
<div id="app">
<div class="titleblock">
<div class="tb-mark"><span class="gl"></span> PROMPT SET</div>
<div class="tb-sheet"><b id="tbName">FIELD GUIDE</b><span id="tbPhase">press start</span></div>
<div class="tb-meters" id="meters">
<div class="meter m-quality"><div class="lab"><span>Quality</span><span class="val" id="vQuality">60</span></div><div class="bar"><div class="fill" id="fQuality"></div></div></div>
<div class="meter m-hours"><div class="lab"><span>Hours</span><span class="val" id="vHours">100</span></div><div class="bar"><div class="fill" id="fHours"></div></div></div>
<div class="meter m-trust"><div class="lab"><span>Trust</span><span class="val" id="vTrust">65</span></div><div class="bar"><div class="fill" id="fTrust"></div></div></div>
</div>
<div class="tb-actions">
<button class="ib" id="btnPlaybook">Playbook</button>
<button class="ib" id="btnPlatforms">Platforms</button>
<button class="ib" id="btnMenu">Menu</button>
</div>
</div>
<div class="stage" id="stage"></div>
</div>
<div class="scrim" id="scrim"><div class="modal" id="modal"><button class="modal-close" id="modalClose">Close</button><div id="modalContent"></div></div></div>
<script>
(function(){
"use strict";
var $=function(id){return document.getElementById(id);};
var clamp=function(v,a,b){return v<a?a:(v>b?b:v);};
var el=function(tag,cls,html){var e=document.createElement(tag);if(cls)e.className=cls;if(html!=null)e.innerHTML=html;return e;};
var REDUCED=(window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches)||false;
var stage=$("stage"), scrim=$("scrim"), modalContent=$("modalContent");
var S={ module:0, quality:60, hours:100, trust:65, score:0,
workspaceReady:false, agentCalibrated:null, rev:0, playbook:[], snapshot:null };
function snap(){ S.snapshot={quality:S.quality,hours:S.hours,trust:S.trust,score:S.score}; }
function restore(){ if(S.snapshot){ S.quality=S.snapshot.quality; S.hours=S.snapshot.hours; S.trust=S.snapshot.trust; S.score=S.snapshot.score; S.rev++; renderMeters(); } }
function renderMeters(){
$("vQuality").textContent=Math.round(S.quality); $("fQuality").style.width=clamp(S.quality,0,100)+"%";
$("vHours").textContent=Math.round(S.hours); $("fHours").style.width=clamp(S.hours,0,100)+"%";
$("vTrust").textContent=Math.round(S.trust); $("fTrust").style.width=clamp(S.trust,0,100)+"%";
document.querySelector(".m-hours").classList.toggle("low",S.hours<25);
document.querySelector(".m-trust").classList.toggle("low",S.trust<25);
document.querySelector(".m-quality").classList.toggle("low",S.quality<25);
}
function applyDelta(d){
if(d.quality) S.quality=clamp(S.quality+d.quality,0,100);
if(d.trust) S.trust=clamp(S.trust+d.trust,0,100);
if(d.hours) S.hours=S.hours+d.hours;
if(d.score) S.score+=d.score;
renderMeters();
}
function failed(){ return S.trust<=0 || S.hours<0; }
function scrollTop(){ window.scrollTo({top:0,behavior:REDUCED?"auto":"smooth"}); }
function setSheet(name,phase){ $("tbName").textContent=name; $("tbPhase").textContent=phase; }
function openModal(html){ modalContent.innerHTML=html; scrim.classList.add("show"); scrim.classList.remove("setback"); }
function closeModal(){ scrim.classList.remove("show"); }
$("modalClose").onclick=closeModal;
scrim.addEventListener("click",function(e){ if(e.target===scrim && !scrim.classList.contains("setback")) closeModal(); });
/* ============================ CURRICULUM ============================ */
var MODULES=[
/* ---- 00 PRIMER ---- */
{ code:'00', phase:'PRE-DESIGN / ORIENTATION', title:'Know your instrument', type:'primer',
intro:{ lead:'Before you brief anyone, you learn what they are. This tool is not a search engine and not a calculator. Three things to internalize first.',
bullets:[
['It learned <b>patterns</b>, not facts. Trained on a huge pile of text — codes, reports, emails, books.'],
['It works by <b>predicting the next word</b>, over and over. A fast, well-read mimic.'],
['It has <span class="warn">no memory between chats</span>. It only knows what you put in front of it.']
], tip:'Treat it like a brilliant junior who has read everything and remembers nothing. Your job is the briefing.' },
beats:[
{ k:'01 / TRAINING', t:'It learned from patterns', d:'Nobody typed in the answers. It read millions of documents and learned how language fits together — how a spec reads, how an RFI is worded, how a calc is explained. It is statistics about language, not a database of truth.' },
{ k:'02 / PREDICTION', t:'It predicts the next word', d:'Everything it writes is the most-likely next chunk of text, one after another. That is the whole trick. Try it:',
demo:{ q:'The structural engineer reviewed and stamped the ____', options:[ {t:'drawings',best:true},{t:'sandwich',best:false},{t:'thunderstorm',best:false} ],
reveal:'It picks what is statistically likely from everything it has read — not what is verified true. That gap is why <b>grounding</b> and <b>verification</b> matter later.' } },
{ k:'03 / ALIGNMENT', t:'Humans tuned it to help', d:'After training, people rated thousands of answers to make it helpful, honest, and safe, and to follow instructions. That is why a clear instruction changes the output so much — you are steering a model that was shaped to be steerable.' },
{ k:'04 / STATELESS', t:'It starts blank every time', d:'Each new chat knows nothing about your project, your firm, or yesterday. Everything in this guide is one skill: deliberately controlling what goes into that blank window — and how to keep it for next time.' }
],
unlock:{ id:'mental', name:'The mental model', tag:'mindset', body:
'MENTAL MODEL FOR WORKING WITH AI\n\n- It is a fast, well-read junior who remembers nothing between chats.\n- It predicts likely text. Likely is not the same as true.\n- Clear briefing in -> useful work out. Vague in -> vague out.\n- You control the context. Nothing else is loaded unless you load it.\n- Always verify anything that leaves your office.' } },
/* ---- 01 THE BRIEF ---- */
{ code:'01', phase:'PRE-DESIGN / THE BRIEF', title:'The Brief', type:'build', cap:8, pass:70,
intro:{ lead:'An RFI just landed: a 24-inch supply duct clashes with a W18 structural beam at grid C-4, 9-foot ceiling. You want a sharp response, fast.',
bullets:[
['A strong prompt has four bones: <b>Role</b>, <b>Task</b>, <b>Context</b>, <b>Format</b>.'],
['Be specific. Name the discipline, the facts, the output you want.'],
['<span class="warn">More words is not more signal.</span> Filler and vague flattery make it worse.']
], tip:'Pattern to remember: Role · Task · Context · Format. If a piece is not one of those, ask why it is in the prompt.' },
brief:'Goal — get a usable draft response to RFI-118 (duct vs beam clash).',
ask:'Assemble the prompt. Your context window is finite (cap shown). Not everything that sounds helpful earns its place.',
components:[
{id:'role1',cat:'role',text:'Act as a senior MEP & structural coordinator.',q:18,cost:1,essential:true,verdict:'good',note:'Sets expertise and the lens to judge from. Biggest single lift.'},
{id:'task',cat:'task',text:'Draft a response to RFI-118 recommending a resolution.',q:16,cost:1,essential:true,verdict:'good',note:'The actual ask, stated plainly. Non-negotiable.'},
{id:'facts',cat:'context',text:'The clash facts: 24in supply duct vs W18 beam, grid C-4, 9ft ceiling.',q:16,cost:2,essential:true,verdict:'good',note:'The specifics it cannot guess. This is what grounds the answer.'},
{id:'spec',cat:'context',text:'Relevant spec: 23 31 00 ductwork + the structural framing notes.',q:10,cost:2,verdict:'good',note:'Ties the answer to your project rules, not generic ones.'},
{id:'fmt',cat:'format',text:'Output: summary, 2-3 options, recommended action, who must sign off.',q:14,cost:1,verdict:'good',note:'Turns a wall of text into a deliverable you can send.'},
{id:'flag',cat:'constraint',text:'Flag if structural re-analysis is needed; cite the affected drawing.',q:10,cost:1,verdict:'good',note:'Keeps it inside discipline lines instead of overstepping.'},
{id:'ex',cat:'format',text:'Here is how we wrote RFI-090 (one short sample for format).',q:8,cost:2,verdict:'good',note:'A single good example teaches the format faster than describing it.'},
{id:'len',cat:'constraint',text:'Keep it under 200 words, plain language for the owner.',q:6,cost:1,verdict:'meh',note:'Reasonable right-sizing, but lower priority than the bones.'},
{id:'imp',cat:'noise',text:'Make it sound impressive and use lots of detail.',q:-10,cost:1,trap:true,verdict:'bad',note:'Vague filler. Invites fluff, not substance. Cut it.'},
{id:'cre',cat:'noise',text:'Be creative and think outside the box.',q:-8,cost:1,trap:true,verdict:'bad',note:'Wrong job. You want correct and defensible, not creative.'},
{id:'dump',cat:'noise',text:'Paste the entire 380-page project specification.',q:-12,cost:6,trap:true,verdict:'bad',note:'Looks thorough; actually buries the 2 sections that matter in noise.'},
{id:'role2',cat:'role',text:'Act as a helpful expert assistant.',q:-4,cost:1,trap:true,dup:'role1',verdict:'bad',note:'Generic and redundant. The specific coordinator role already wins.'}
],
unlock:{ id:'rfi', name:'RFI response prompt', tag:'template', body:
'RFI RESPONSE PROMPT (reusable)\n\nRole: You are a senior [discipline] coordinator on a design-bid-build project.\nTask: Draft a response to [RFI #] that recommends a resolution.\nContext: [the specific clash / question + the grid/location + relevant spec section + drawing ref].\nFormat: 1) one-line summary 2) 2-3 options with pros/cons 3) recommended action 4) who must review/sign off.\nConstraints: cite the affected drawing; flag if another discipline must re-analyze; under 200 words, plain language.\n\nTip: paste only the 1-2 relevant spec sections, never the whole book.' } },
/* ---- 02 THE CONTEXT WINDOW ---- */
{ code:'02', phase:'DESIGN / CONTEXT', title:'The Context Window', type:'build', cap:6, pass:70,
intro:{ lead:'Now a coordination question: does the revised mechanical layout violate required working clearance over the electrical room? Watch the window fill as you load it.',
bullets:[
['The context window is the model\u2019s <b>entire</b> working memory — and it is finite.'],
['Right info in → reliable answer. <span class="warn">Junk in → the signal drowns</span> (and old context gets dropped).'],
['Bigger is not better. The skill is loading the few things the question actually needs.']
], tip:'When the window overflows, the model quietly loses track of earlier context. This is real, and it is why dumping everything backfires.' },
brief:'Goal — a defensible clearance check over the electrical room.',
ask:'Load only what the check needs. Overfilling the window (cap) drops context and the answer gets worse, not better.',
components:[
{id:'code',cat:'context',text:'The 2 governing code clauses: working clearance + headroom.',q:18,cost:1,essential:true,verdict:'good',note:'The actual rule to check against. Without it, it guesses.'},
{id:'plan',cat:'context',text:'The revised mechanical plan over the electrical room.',q:18,cost:2,essential:true,verdict:'good',note:'The thing you are checking. Essential.'},
{id:'sched',cat:'context',text:'Electrical equipment schedule (defines clearances needed).',q:12,cost:1,verdict:'good',note:'Tells it which clearances apply to which gear.'},
{id:'rcp',cat:'context',text:'Reflected ceiling plan at that area.',q:10,cost:1,verdict:'good',note:'Vertical clearance and headroom live here.'},
{id:'show',cat:'constraint',text:'Ask it to check clause by clause and flag any failure.',q:10,cost:1,verdict:'good',note:'Forces a verifiable, auditable answer instead of a vibe.'},
{id:'book',cat:'noise',text:'The full 600-page electrical code book.',q:-14,cost:6,trap:true,verdict:'bad',note:'Overflow. Drowns the 2 clauses that matter. Classic mistake.'},
{id:'old',cat:'noise',text:'Last year\u2019s hospital project, for reference.',q:-12,cost:4,trap:true,verdict:'bad',note:'Different project, different rules. Pure noise and confusion.'},
{id:'thread',cat:'noise',text:'The entire 3-week email thread on the redesign.',q:-10,cost:4,trap:true,verdict:'bad',note:'Mostly chatter. Eats the window, adds almost nothing.'},
{id:'photos',cat:'noise',text:'47 site progress photos.',q:-8,cost:5,trap:true,verdict:'bad',note:'Heavy and irrelevant to a clearance check.'},
{id:'narr',cat:'context',text:'Architect\u2019s written narrative of the redesign.',q:4,cost:2,verdict:'meh',note:'Nice-to-have. Spend the window on rules + plan first.'}
],
unlock:{ id:'check', name:'Code / clearance check', tag:'template', body:
'CODE / CLEARANCE CHECK PROMPT (reusable)\n\nRole: You are a [discipline] reviewer checking compliance.\nTask: Check whether [element/layout] satisfies [specific requirement].\nContext (load only these): the exact code clause(s); the relevant plan/section; the schedule that defines the requirement.\nFormat: a table - clause | requirement | as-designed | pass/fail | note.\nConstraint: work clause by clause, show the numbers, and flag anything you cannot verify from what I gave you.\n\nGolden rule: give the 2 sections that matter, never the whole code book.' } },
/* ---- 03 THE WORKSPACE ---- */
{ code:'03', phase:'DESIGN / STANDARDS', title:'The Workspace', type:'build', cap:7, pass:65, sets:'workspaceReady', showPlatforms:true,
intro:{ lead:'You are tired of re-explaining the project every chat. Set up a reusable workspace for the Riverside Civic Center — a Project (Claude / ChatGPT) or a Gem (Gemini) that remembers your context.',
bullets:[
['A workspace holds <b>persistent</b> instructions + reference files that apply to every chat in it.'],
['Put the <b>constants</b> here (project facts, your formats, key codes). Keep per-chat asks short.'],
['<span class="warn">Curate, do not dump.</span> A bloated or unsafe workspace hurts you.']
], tip:'This is the single highest-leverage setup for a busy professional. Do it once per project; save yourself the re-explaining forever.' },
brief:'Goal — a clean, reusable project workspace.',
ask:'Decide what belongs in the persistent workspace. A good setup here makes every later task faster.',
components:[
{id:'rules',cat:'constraint',text:'Standing rules: ask clarifying questions first; cite sources; flag cross-discipline impacts.',q:16,cost:1,essential:true,verdict:'good',note:'Your house rules, auto-applied to every chat. Huge.'},
{id:'pfacts',cat:'context',text:'Project facts: name, scope, delivery = design-bid-build, codes (IBC 2021, ASHRAE 90.1).',q:16,cost:1,essential:true,verdict:'good',note:'The constants every conversation needs. Set once.'},
{id:'refs',cat:'context',text:'Reference set: spec index, code excerpts, drawing list.',q:14,cost:2,verdict:'good',note:'Grounding the AI can pull from in any chat here.'},
{id:'fmts',cat:'format',text:'Preferred outputs: your firm\u2019s RFI / submittal / minutes formats.',q:12,cost:1,verdict:'good',note:'Consistency with zero re-asking.'},
{id:'team',cat:'context',text:'Team + disciplines: who owns mechanical, structural, electrical, civil.',q:8,cost:1,verdict:'good',note:'Lets it route work and flag the right reviewer.'},
{id:'srv',cat:'noise',text:'Dump the entire company server into the project.',q:-14,cost:6,trap:true,verdict:'bad',note:'Bloat + security risk. Curate the few documents that matter.'},
{id:'chat',cat:'noise',text:'Today\u2019s lunch order and office chit-chat.',q:-8,cost:2,trap:true,verdict:'bad',note:'Volatile junk. Persistent space is for constants only.'},
{id:'secret',cat:'noise',text:'Client\u2019s confidential fee breakdown and shared passwords.',q:-12,cost:1,trap:true,verdict:'bad',note:'Never store secrets in a persistent or shared workspace.'},
{id:'yes',cat:'noise',text:'Instruction: always agree with me and stay positive.',q:-8,cost:1,trap:true,verdict:'bad',note:'A yes-man misses errors. You want it to catch problems.'}
],
unlock:{ id:'brief', name:'Project brief + custom instructions', tag:'template', body:
'PROJECT WORKSPACE STARTER (paste into Project / Gem instructions)\n\nPROJECT: [name] | Delivery: design-bid-build | Role on file: [PM / discipline lead]\nGOVERNING CODES: [IBC / ASHRAE / NEC / local].\nTEAM + DISCIPLINES: [who owns mech / struct / elec / civil].\n\nHOW TO WORK WITH ME (standing rules):\n- Ask clarifying questions before drafting anything substantial.\n- Cite the drawing / spec section you relied on.\n- Flag any cross-discipline impact or needed sign-off.\n- Use my formats (RFI / submittal / minutes) when relevant.\n- Never invent a code requirement; say so if it is not in the files.\n\nATTACHED REFERENCES: spec index, code excerpts, drawing list.' } },
/* ---- 04 THE TEMPLATE (build a skill, ordered) ---- */
{ code:'04', phase:'PRODUCTION / REUSE', title:'The Template', type:'build', ordered:true, cap:6, pass:70,
intro:{ lead:'You write OAC meeting minutes every single week. Build a reusable skill once, so you never start from a blank page again.',
bullets:[
['A <b>skill</b> is a saved set of instructions for a routine task. Claude calls it a Skill; in ChatGPT/Gemini it lives in a Custom GPT / Gem.'],
['Order matters: structure → produce → <b>verify</b> → format.'],
['<span class="warn">Never skip the check, and never hard-code things that change</span> (dates, names).']
], tip:'The highest-value step engineers forget: a built-in verification pass. Put it after the draft, before the output.' },
brief:'Goal — a one-click weekly minutes skill that holds up every week.',
ask:'Pick the steps AND order them (use the arrows on selected steps). Include the right checks; leave out what breaks reuse.',
components:[
{id:'in',cat:'task',role:'intake',text:'Take my raw notes or transcript as the input.',q:10,cost:1,essential:true,verdict:'good',note:'The variable input. A reusable skill never assumes the content.'},
{id:'pl',cat:'constraint',role:'plan',text:'Sort content into agenda categories: safety, schedule, RFIs, submittals, budget.',q:12,cost:1,essential:true,verdict:'good',note:'Structure first. Gives every set of minutes the same shape.'},
{id:'pr',cat:'format',role:'produce',text:'Draft action items as: task — owner — due date.',q:14,cost:1,essential:true,verdict:'good',note:'The part people actually read. Owner + date is the whole point.'},
{id:'vf',cat:'constraint',role:'verify',text:'Carry forward last week\u2019s open items; check each is resolved or re-listed.',q:14,cost:1,essential:true,verdict:'good',note:'The verification step. Catches the items that quietly slip.'},
{id:'fm',cat:'format',role:'format',text:'Output in our minutes template; flag anything unclear for me to confirm.',q:10,cost:1,verdict:'good',note:'Clean output + an honest flag beats confident guessing.'},
{id:'send',cat:'noise',role:'format',text:'Auto-send the minutes to the full distribution list.',q:-12,cost:1,trap:true,verdict:'bad',note:'Never auto-send unverified work. You review, then you send.'},
{id:'hard',cat:'noise',role:'intake',text:'Hard-code today\u2019s date and the attendee list.',q:-8,cost:1,trap:true,verdict:'bad',note:'Breaks reuse. Keep dates and names as variables.'},
{id:'pre',cat:'noise',role:'plan',text:'Write a long background preamble about the project each time.',q:-6,cost:1,trap:true,verdict:'bad',note:'Padding. Minutes are scannable, not prose.'}
],
unlock:{ id:'min', name:'Weekly minutes skill', tag:'skill', body:
'WEEKLY MINUTES SKILL (save as a Skill / Custom GPT / Gem)\n\nWhen I paste raw OAC meeting notes:\n1. Sort items into: Safety, Schedule, RFIs, Submittals, Budget, Other.\n2. Pull every decision and every action item. Format actions as: task - owner - due date.\n3. Compare against the prior week: mark each open item Resolved or carry it forward as Still Open.\n4. Output in this template: Header (project, date, attendees) / Decisions / Action Items table / Open Items.\n5. Flag anything ambiguous in a short Questions list for me to confirm. Do not guess attendees or dates.\n\nNever auto-send. Always return a draft for my review.' } },
/* ---- 05 THE AGENT (decision) ---- */
{ code:'05', phase:'BID / REVIEW', title:'The Agent', type:'decision', wsCredit:6,
intro:{ lead:'Fourteen submittals just came in. You are going to delegate the first pass to an AI agent — a model that runs multiple steps and uses your files. How you set it up decides whether it saves you or burns you.',
bullets:[
['An <b>agent</b> does multi-step work: read, check, draft, route — not just answer one question.'],
['It is only as good as its <b>scope</b>, its <b>grounding</b>, and your <b>verification</b>.'],
['<span class="warn">Over-trust ships errors. Over-control wastes the leverage.</span> Calibrate.']
], tip:'Best practice: let the agent do the volume, but keep a human checkpoint on anything life-safety or high-consequence.' },
scenes:[
{ meta:'Scene 1 / 3 - Scoping the work',
situation:'How do you scope the agent\u2019s job?',
detail:'Fourteen submittals, multiple disciplines, due tomorrow.',
options:[
{text:'One vague ask: review these and tell me if they are ok.',ok:'A',delta:{quality:-8,trust:-6,hours:-10,score:-4},verdict:'bad',outcome:'You get mushy, unusable verdicts with no reasoning. You redo most of it by hand.'},
{text:'Break it into steps: log, check against spec, draft comments, route by discipline - and I approve the routing.',ok:'B',delta:{quality:16,trust:8,hours:-4,score:14},verdict:'good',outcome:'A clean, checkable pipeline with a human gate on routing. This is the move.'},
{text:'Tell it to just approve everything to save time.',ok:'C',delta:{quality:-15,trust:-20,hours:5,score:-10},verdict:'bad',outcome:'Two non-compliant submittals get approved. The owner notices. Bad day.'}
] },
{ meta:'Scene 2 / 3 - Grounding the check',
situation:'What do you give it to check against?',
detail:'It needs something to compare submittals to.',
options:[
{text:'Nothing - let it use general knowledge.',ok:'A',delta:{quality:-12,trust:-8,score:-4},verdict:'bad',outcome:'With no spec, it invents plausible-sounding requirements. Hallucinated compliance.'},
{text:'The relevant spec sections plus the submittal register.',ok:'B',delta:{quality:14,score:12},verdict:'good',outcome:'Grounded against the real requirements. Checks you can actually defend.'},
{text:'The full spec book and every past project, to be safe.',ok:'C',delta:{quality:-6,hours:-6,score:-2},verdict:'risky',outcome:'Slow, noisy, over-stuffed - and it still misses the point. More is not safer.'}
] },
{ meta:'Scene 3 / 3 - The judgment call',
situation:'It flags a fire-rated wall assembly mismatch on submittal #9. Your move?',
detail:'This one is life-safety.',
options:[
{text:'Trust it and forward the comments as-is.',ok:'A',delta:{trust:-12,score:-2},verdict:'risky',outcome:'It was only half-right. You forwarded an error on a fire-rated assembly. Trust dings.'},
{text:'Spot-check the 3 life-safety-critical items; accept the routine rest.',ok:'B',delta:{quality:16,trust:10,hours:-3,score:16},verdict:'good',set:'spot',outcome:'Calibrated. You caught the real fire-rating issue and still kept the speed on the other 11.'},
{text:'Manually re-review all 14 yourself.',ok:'C',delta:{hours:-18,trust:2,score:2},verdict:'meh',outcome:'Safe - but you just threw away the leverage you built. Calibrate your trust.'}
] }
],
unlock:{ id:'agent', name:'Submittal review agent', tag:'agent', body:
'SUBMITTAL REVIEW AGENT (workflow spec)\n\nGoal: first-pass review of incoming submittals.\nInputs: the submittal files + the relevant spec sections + the submittal register.\nSteps:\n1. Log each submittal: number, item, spec section, date.\n2. Check each against its spec section; note conforms / does not conform / needs info.\n3. Draft review comments per item in our standard language.\n4. Route each to the responsible discipline (mech / struct / elec / civil).\nHuman checkpoints (do not skip):\n- I approve all discipline routing.\n- I personally verify anything life-safety (fire rating, egress, structural).\nRule: never mark Approved without a spec citation. Flag low-confidence items for me.' } },
/* ---- 06 THE HANDOFF (capture) ---- */
{ code:'06', phase:'CLOSEOUT / HANDOFF', title:'The Handoff', type:'capture', cap:5, pass:65, kitAfter:true,
intro:{ lead:'The project is wrapping. Lock in what worked so the next one starts at speed instead of from zero. This is how you turn one good project into a repeatable system.',
bullets:[
['Save the <b>distilled</b> assets: refined prompts, the project brief, your checklist.'],
['Use <b>memory / project knowledge</b> for what should carry forward; let one-offs go.'],
['<span class="warn">An over-stuffed kit gets dropped.</span> Lean and reusable beats huge.']
], tip:'The repeatability trick: end your last working chat with - summarize everything we established and give me a reusable brief for next time. Save that.' },
brief:'Goal — a lean, portable kit that makes the next project faster.',
ask:'Pin only what is worth carrying forward. More is not better here either.',
components:[
{id:'ktmpl',cat:'format',text:'Your refined prompt templates (RFI, minutes, code check).',q:16,cost:1,essential:true,verdict:'good',note:'The reusable engine. This is the whole point - keep it.'},
{id:'kbrief',cat:'context',text:'The project brief + custom instructions.',q:14,cost:1,essential:true,verdict:'good',note:'Drop-in starting context for the next workspace.'},
{id:'kcheck',cat:'constraint',text:'Your verification checklist.',q:12,cost:1,verdict:'good',note:'So future-you never skips the safety step.'},
{id:'ksum',cat:'context',text:'A short note: what worked, what to change next time.',q:10,cost:1,verdict:'good',note:'You are versioning your own playbook. Smart.'},
{id:'klog',cat:'noise',text:'Every raw chat log from the entire project.',q:-12,cost:4,trap:true,verdict:'bad',note:'Volume, not value. Distill the wins; do not hoard the transcripts.'},
{id:'kone',cat:'noise',text:'One-off answers to questions you will never ask again.',q:-8,cost:2,trap:true,verdict:'bad',note:'Ephemeral. Let it go.'},
{id:'ksec',cat:'noise',text:'The client\u2019s confidential cost data.',q:-12,cost:1,trap:true,verdict:'bad',note:'Never persist secrets, especially anywhere shared.'},
{id:'kbig',cat:'noise',text:'A single 60-page everything-document.',q:-8,cost:5,trap:true,verdict:'bad',note:'Too big to load reliably - it just gets dropped. Keep it lean.'}
],
unlock:{ id:'preserve', name:'Context-preservation tricks', tag:'tips', body:
'KEEPING CONTEXT FOR NEXT TIME\n\n- End big chats with: summarize what we established + give me a reusable brief to paste next time.\n- Keep one living project-brief doc. Attach it instead of re-typing.\n- Constants go in project / custom instructions. Per-chat asks stay short.\n- Name and version your best prompts. Reuse like standard details.\n- Turn anything you do weekly into a saved skill / Custom GPT / Gem.\n- Save your verification checklist with the prompts, not separately.' } },
/* ---- 07 CAPSTONE (decision, final) ---- */
{ code:'07', phase:'FIELD / CAPSTONE', title:'The Coordination', type:'decision', final:true, wsCredit:0,
intro:{ lead:'5pm Friday. The owner wants a coordination report and a fee-impact narrative for three clashes across mechanical, structural, and civil — by end of day. You have limited hours and everything you have learned.',
bullets:[
['Use your <b>workspace</b>, a <b>specific</b> prompt, and a <b>verification</b> pass.'],
['Every choice spends real hours and real trust.'],
['<span class="warn">This is the whole loop, under pressure.</span>']
], tip:'Speed comes from setup you already did. Quality comes from the check you do before you hit send.' },
scenes:[
{ meta:'Scene 1 / 3 - Where you start',
situation:'It is 5pm. Where do you begin?',
detail:'Three clashes, three disciplines, fee impact, owner waiting.',
options:[
{text:'Fresh chat - explain the whole project from scratch.',ok:'A',delta:{hours:-15,quality:-4,score:-4},verdict:'bad',outcome:'You burn the better part of an hour re-explaining context you could have saved.'},
{text:'Open your project workspace - context, codes, and formats already loaded.',ok:'B',req:'workspace',delta:{hours:8,quality:12,trust:5,score:14},verdict:'good',outcome:'Context, codes, and your formats are already there. You are drafting within minutes.',altDelta:{hours:-12,quality:-2,score:-4},altVerdict:'bad',altOutcome:'You reach for your workspace - and there is not one. (You skipped that setup.) Back to explaining from scratch.'},
{text:'One giant prompt asking for all three clashes and the fee math at once.',ok:'C',delta:{quality:-8,score:-2},verdict:'risky',outcome:'It conflates the three clashes and tangles the fee math. You spend time untangling it.'}
] },
{ meta:'Scene 2 / 3 - The fee narrative prompt',
situation:'For the fee-impact narrative, your prompt should...',
detail:'The owner reads this one closely.',
options:[
{text:'Be specific: each clash, the rework scope, hours by discipline, owner-friendly summary.',ok:'A',delta:{quality:16,score:12},verdict:'good',outcome:'Crisp, structured, and grounded. Exactly what an owner can act on.'},
{text:'Just say: write a fee-impact narrative.',ok:'B',delta:{quality:-10,score:-4},verdict:'bad',outcome:'Generic mush with no real numbers. You would be embarrassed to send it.'},
{text:'Include last project\u2019s numbers to save time.',ok:'C',delta:{quality:-8,trust:-6,score:-4},verdict:'bad',outcome:'Wrong figures in front of the owner. That is a credibility problem, not a shortcut.'}
] },
{ meta:'Scene 3 / 3 - Before it ships',
situation:'The draft is done. The owner is waiting. Final move?',
detail:'It looks good. Does it go?',
options:[
{text:'Send it - the deadline is now.',ok:'A',delta:{trust:-15,score:-4},verdict:'bad',outcome:'A transposed clearance dimension goes out to the owner. Confident, fluent, and wrong.'},
{text:'Run a 5-minute verification pass on the numbers and clause citations, then send.',ok:'B',delta:{quality:16,trust:12,score:18},verdict:'good',outcome:'The check catches one bad figure. You ship it clean, on time. This is the habit that protects you.'},
{text:'Pull an all-nighter re-checking every line by hand.',ok:'C',delta:{hours:-20,score:2},verdict:'meh',outcome:'Done - but you did not need to. You built tools you did not trust. Calibrate.'}
] },
],
unlock:{ id:'loop', name:'The full loop', tag:'recap', body:
'THE LOOP (run it every time)\n\n1. WORKSPACE - open the project that already holds your context.\n2. CONTEXT - load only what this task needs.\n3. PROMPT - role / task / context / format, specific.\n4. DELEGATE - let it do the volume; keep human checkpoints.\n5. VERIFY - check numbers + citations before anything ships.\n6. PRESERVE - save what worked for next time.' } }
];
/* ============================ REFERENCE DATA ============================ */
var PATTERNS={ id:'patterns', name:'Standard prompt patterns', tag:'reference', body:
'STANDARD PROMPT PATTERNS\n\n- Role / Task / Context / Format - the four bones of any prompt.\n- Give one example of the output you want.\n- Ask me clarifying questions before you start.\n- Show your work / check it clause by clause.\n- Chain it: outline -> draft -> critique -> finalize.\n- Constrain it: length, audience, what to cite, what to avoid.' };
var CHEATSHEET=[
{ c:'Personal rules (set once)', cg:['Custom Instructions','Global; applied to every chat'], cl:['Instructions for Claude + Styles','Account rules + per-chat tone'], gm:['Personalization','Saved info carried across chats'] },
{ c:'Project workspace', cg:['Projects','Chats + files + per-project instructions'], cl:['Projects','Persistent space, ~200K context, unlimited files'], gm:['Gems + Drive','Custom persona + live Workspace files, ~1M context'] },
{ c:'Build your own assistant', cg:['Custom GPTs','Instructions + knowledge + Actions; GPT Store'], cl:['Skills (+ Projects)','Auto-loads by task; no length limit'], gm:['Gems','Quick custom persona (~4K chars)'] },
{ c:'Reusable skill, portable', cg:['Paste into a GPT','Skill text in the GPT instructions'], cl:['Skills','SKILL.md open standard, reused across tools'], gm:['Paste into a Gem','Same skill text in the Gem'] },
{ c:'Memory across chats', cg:['Memory','Remembers facts you share'], cl:['Memory','Recall across past chats'], gm:['Personal intelligence','Carries stable preferences'] },
{ c:'Ground it in your docs', cg:['Upload + Browsing','Files in Library; web when needed'], cl:['Upload + Web search','Files + live search'], gm:['Drive / Docs + Search','Pulls straight from Workspace'] },
{ c:'Visual working doc', cg:['Canvas','Edit a living draft beside the chat'], cl:['Artifacts','Edit a living draft beside the chat'], gm:['Canvas','Edit a living draft beside the chat'] }
];
var TIPS=[
['End any big chat with: <b>summarize what we established and give me a reusable brief I can paste next time.</b>'],
['Keep a living <b>project-brief</b> doc. Attach it instead of re-typing your context.'],
['Put constants in the <b>project / custom instructions</b>; keep each chat\u2019s ask short.'],
['<b>Name and version</b> your best prompts. Reuse them like standard details across sheets.'],
['Never paste whole code books. Give the <b>two sections</b> that actually matter.'],
['Add a <b>verification step</b> before anything leaves the office. Every time.']
];
/* ============================ HELPERS ============================ */
function pad(n){ return n<10?("0"+n):(""+n); }
function catLabel(c){ var m={role:"ROLE",task:"TASK",context:"CONTEXT",format:"FORMAT",constraint:"CONSTRAINT",noise:"NOISE / FILLER"}; return m[c]||c.toUpperCase(); }
function chipCat(c){ var m={role:"Role",task:"Task",context:"Context",format:"Format",constraint:"Rule",noise:"?"}; return m[c]||c; }
function vlabel(v){ return v==="good"?"Good call":v==="bad"?"Costly":v==="risky"?"Risky":"Acceptable"; }
function compById(mod,id){ for(var i=0;i<mod.components.length;i++){ if(mod.components[i].id===id) return mod.components[i]; } return null; }
function escapeHTML(s){ return String(s).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); }
function stripTags(s){ return String(s).replace(/<[^>]+>/g,"").replace(/—/g,"-").replace(/→/g,"->").replace(/·/g,"-").replace(/&/g,"&").replace(/\u2019/g,"'"); }
function shorten(t){ t=String(t).replace(/<[^>]+>/g,"").replace(/&/g,"&").replace(/—/g,"-"); return t.length>54?(t.slice(0,52)+"..."):t; }
function savePlaybook(u){ if(!u) return; for(var i=0;i<S.playbook.length;i++){ if(S.playbook[i].id===u.id) return; } S.playbook.push(u); }
function ratingFor(v){
if(v>=150) return ["AI-READY LEAD","You run the whole loop by reflex - scoped prompts, grounded context, a reusable workspace, and a verification habit. You are the person the rest of the office will be asking."];
if(v>=95) return ["SOLID PRACTITIONER","You have the core moves and you mostly dodge the traps. Tighten your reuse and never skip the verification pass, and you are all the way there."];
if(v>=45) return ["GETTING THERE","Good instincts, a few costly detours. Re-run the modules where Trust or Hours took the hit - the patterns will lock in fast."];
return ["FOUNDATIONS SET","You have seen every core idea once, which is the hardest step. Run it again and watch the consequences change as your choices sharpen."];
}
/* category -> bar height by token cost */
function layerHeight(cost){ return (12+cost*9)+"px"; }
/* THE SIGNATURE: paint the live context window from the selected inputs */
function paintContext(selObjs, cap){
var stack=$("ctxStack"); if(!stack) return;
var total=0,i; for(i=0;i<selObjs.length;i++) total+=selObjs[i].cost;
var over = total>cap;
var dropped={};
if(over){
var order=selObjs.slice().sort(function(a,b){ return b.q-a.q; }); /* most valuable kept first */
var acc=0;
for(i=0;i<order.length;i++){ acc+=order[i].cost; if(acc>cap) dropped[order[i].id]=true; }
}
stack.innerHTML="";
if(selObjs.length===0){
var empty=el("div","","Empty window. Load inputs and watch the model's working memory fill.");
empty.style.cssText="font-family:var(--mono);font-size:11px;color:var(--faint);text-align:center;padding:34px 8px;";
stack.appendChild(empty);
} else {
for(i=0;i<selObjs.length;i++){
var c=selObjs[i];
var lay=el("div","ctx-layer "+c.cat+(dropped[c.id]?" dropped":""));
lay.style.minHeight=layerHeight(c.cost);
var lab=catLabel(c.cat)+(dropped[c.id]?" - DROPPED (overflow)":"");
lay.innerHTML="<span class='lt'>"+lab+"</span>"+shorten(c.text);
if(c.cat!=="noise" && !dropped[c.id] && (c.essential||c.q>=14)) lay.innerHTML+="<span class='pin'>kept</span>";
stack.appendChild(lay);
}
if(!REDUCED){ var sweep=el("div","attn run"); stack.appendChild(sweep); }
}
var badge=$("capBadge"); if(badge) badge.textContent=total+" / "+cap;
var fill=$("capFill"); if(fill) fill.style.width=Math.min(100,Math.round(total/cap*100))+"%";
var wrap=$("capWrap"); if(wrap) wrap.classList.toggle("over",over);
var l2=$("capLab2"); if(l2){ l2.classList.toggle("over",over); l2.textContent = selObjs.length===0?"empty":(over?("over capacity - the model is dropping context"):(total+" of "+cap+" used")); }
}
/* score a build/capture selection - pure, no side effects */
function scoreBuild(mod, selIds){
var selObjs=[],i; for(i=0;i<selIds.length;i++){ var o=compById(mod,selIds[i]); if(o) selObjs.push(o); }
var base=50,sumQ=0,total=0;
for(i=0;i<selObjs.length;i++){ sumQ+=selObjs[i].q; total+=selObjs[i].cost; }
var missed=[];
for(i=0;i<mod.components.length;i++){ var c=mod.components[i]; if(c.essential && selIds.indexOf(c.id)<0) missed.push(c); }
var missPen=missed.length*14;
var overflow= total>mod.cap?(total-mod.cap):0;
var overPen=overflow*8;
var dupPen=0;
for(i=0;i<selObjs.length;i++){ if(selObjs[i].dup && selIds.indexOf(selObjs[i].dup)>=0) dupPen+=6; }
var ordInv=0;
if(mod.ordered){
var rank={intake:0,plan:1,produce:2,verify:3,format:4};
var seq=[]; for(i=0;i<selObjs.length;i++){ if(selObjs[i].role!=null && rank[selObjs[i].role]!=null) seq.push(rank[selObjs[i].role]); }
var a,b; for(a=0;a<seq.length;a++){ for(b=a+1;b<seq.length;b++){ if(seq[a]>seq[b]) ordInv++; } }
}
var ordPen=ordInv*6;
var result=clamp(Math.round(base+sumQ-missPen-overPen-dupPen-ordPen),0,100);
var passed = result>=mod.pass && missed.length===0;
return { result:result, passed:passed, missed:missed, overflow:overflow, ordInv:ordInv, selObjs:selObjs };
}
/* ============================ SHARED UI ============================ */
function vrow(v,title,note){
var ic = v==="good"?"✓":(v==="bad"?"✗":"~");
return "<div class='vrow "+v+"'><div class='ic'>"+ic+"</div><div class='tx'><b>"+title+"</b><br><span>"+note+"</span></div></div>";
}
function deltaHTML(d){
var parts=[]; function add(label,v){ if(v){ var up=v>0; parts.push("<span class='"+(up?"up":"dn")+"'>"+label+" "+(up?"+":"")+v+"</span>"); } }
add("Quality",d.quality); add("Hours",d.hours); add("Trust",d.trust); add("Score",d.score);
return parts.length?("<div class='deltas'>"+parts.join("")+"</div>"):"";
}
function startLabel(mod){ return mod.type==="primer"?"Start":(mod.type==="decision"?"Make the calls":(mod.type==="capture"?"Build the kit":"Build the prompt")); }
function recapLine(code){
var m={"00":"How a model is trained, predicts, and forgets.","01":"Role - Task - Context - Format.","02":"A finite window: signal in, junk out.","03":"A reusable project workspace.","04":"A weekly skill with a built-in check.","05":"Delegating to an agent - scoped and verified.","06":"A portable kit for the next project.","07":"The whole loop, under a deadline."};
return m[code]||"";
}
/* ============================ ENGINE ============================ */
function loadModule(idx){
if(idx>=MODULES.length){ showResults(); return; }
S.module=idx; snap();
var mod=MODULES[idx];
setSheet(mod.code+" - "+mod.title, mod.phase);
if(mod.wsCredit && S.workspaceReady){ S.hours+=mod.wsCredit; renderMeters(); }
renderIntro(mod, idx);
}
function renderIntro(mod, idx){
scrollTop();
var it=mod.intro, html="<div class='sheet'>";
html+="<div class='eyebrow'><span class='code'>"+mod.code+"</span> "+mod.phase+"</div>";
html+="<h2 class='title'>"+mod.title+"</h2>";
html+="<p class='lead'>"+it.lead+"</p>";
html+="<ul class='bullets'>";
for(var i=0;i<it.bullets.length;i++){ html+="<li><span class='dim'>"+pad(i+1)+"</span><div>"+it.bullets[i][0]+"</div></li>"; }
html+="</ul>";
if(it.tip) html+="<div class='tipbox'><span class='tg'>Tip</span><div>"+it.tip+"</div></div>";
if(mod.wsCredit && S.workspaceReady) html+="<div class='goalbox' style='margin-top:14px'><span class='tg' style='color:var(--verified);border-color:rgba(52,201,138,.4)'>Bonus</span><div>Your project workspace is already set up, so you start this one with hours in the bank.</div></div>";
html+="<div style='margin-top:22px;display:flex;gap:10px;flex-wrap:wrap'>";
html+="<button class='btn btn-primary' id='introStart'>"+startLabel(mod)+"</button>";
if(mod.showPlatforms) html+="<button class='btn btn-ghost' id='introPlat'>How each AI does this</button>";
html+="</div></div>";
stage.innerHTML=html;
$("introStart").onclick=function(){ route(mod, idx); };
if(mod.showPlatforms) $("introPlat").onclick=openPlatforms;
}
function route(mod, idx){
if(mod.type==="primer") renderPrimer(mod, idx);
else if(mod.type==="decision") renderDecision(mod, idx);
else renderBuild(mod, idx);
}
/* ---- PRIMER: how the model is made + how it works ---- */
function renderPrimer(mod, idx){
scrollTop();
var html="<div class='sheet'>";
html+="<div class='eyebrow'><span class='code'>"+mod.code+"</span> "+mod.phase+"</div>";
html+="<h2 class='title'>"+mod.title+"</h2>";
for(var i=0;i<mod.beats.length;i++){
var b=mod.beats[i];
html+="<div class='goalbox' style='flex-direction:column;align-items:stretch;margin-bottom:12px'>";
html+="<div style='display:flex;gap:12px;align-items:baseline;margin-bottom:6px'><span class='tg'>"+b.k+"</span><b style='font-size:16px;color:var(--mist)'>"+b.t+"</b></div>";
html+="<div style='color:var(--graphite);font-size:14.5px;line-height:1.55'>"+b.d+"</div>";
if(b.demo){
html+="<div style='margin-top:12px;padding:13px;border:1px dashed var(--hair);border-radius:6px'>";
html+="<div style='font-family:var(--mono);font-size:13.5px;color:var(--cyan);margin-bottom:11px'>"+b.demo.q+"</div>";
html+="<div class='demo-opts' style='display:flex;gap:8px;flex-wrap:wrap'>";
for(var o=0;o<b.demo.options.length;o++){ html+="<button class='btn btn-ghost demo-o' data-best='"+(b.demo.options[o].best?"1":"0")+"' style='font-size:14px;padding:8px 15px'>"+b.demo.options[o].t+"</button>"; }
html+="</div><div class='demo-rev' style='display:none;margin-top:13px;color:var(--graphite);font-size:14px;line-height:1.55'>"+b.demo.reveal+"</div></div>";
}
html+="</div>";
}
html+="<div class='goalbox'><span class='tg' style='color:var(--verified);border-color:rgba(52,201,138,.4)'>Saved</span><div>Added to your Playbook: <b>"+mod.unlock.name+"</b>.</div></div>";
html+="<div style='margin-top:20px'><button class='btn btn-primary' id='pmNext'>I have got it - continue</button></div>";
html+="</div>";
stage.innerHTML=html;
Array.prototype.forEach.call(stage.querySelectorAll(".demo-o"),function(btn){
btn.onclick=function(){
var dash=btn.parentNode.parentNode;
Array.prototype.forEach.call(dash.querySelectorAll(".demo-o"),function(b2){
b2.disabled=true;
if(b2.getAttribute("data-best")==="1"){ b2.style.borderColor="var(--verified)"; b2.style.color="var(--verified)"; }
else if(b2===btn){ b2.style.borderColor="var(--redline)"; b2.style.color="var(--redline)"; }
});
dash.querySelector(".demo-rev").style.display="block";
};
});
savePlaybook(mod.unlock);
$("pmNext").onclick=function(){ applyDelta({quality:4,score:5}); loadModule(idx+1); };
}
/* ---- BUILD / CAPTURE: assemble a prompt, skill, or workspace ---- */
function chipHTML(c, ordered){
var s="<div class='chip' data-id='"+c.id+"' role='button' tabindex='0'>";
s+="<span class='cat'>"+chipCat(c.cat)+"</span>";
s+="<span class='num'></span>";
s+="<div style='margin:2px 36px 2px 26px'>"+c.text+"</div>";
if(ordered) s+="<span class='ordbtns'><button type='button' class='oup' aria-label='move up'>↑</button><button type='button' class='odn' aria-label='move down'>↓</button></span>";
s+="</div>";
return s;
}
function previewLine(mod,pr,total){
if(total>mod.cap) return "Window overflowing - the model is dropping earlier context to fit. The answer gets <b style='color:var(--redline)'>less</b> reliable, not more.";
if(pr.result>=85) return "Sharp, specific, grounded. <b style='color:var(--verified)'>A draft you could actually send.</b>";
if(pr.result>=70) return "Solid and usable. A little tightening and it ships.";
if(pr.result>=45) return "Workable but soft - a key piece is missing or some filler is creeping in.";
if(pr.result>0) return "Vague and unfocused. <b style='color:var(--redline)'>You will be redoing this by hand.</b>";
return "Nothing loaded yet.";
}
function renderBuild(mod, idx){
scrollTop();
var ordered=!!mod.ordered;
var sel=[];
var html="<div class='sheet'>";
html+="<div class='eyebrow'><span class='code'>"+mod.code+"</span> "+mod.phase+"</div>";
html+="<h2 class='title'>"+mod.title+"</h2>";
html+="<p class='lead'>"+mod.brief+"</p>";
html+="<div class='goalbox'><span class='tg'>Your move</span><div>"+mod.ask+"</div></div>";
html+="<div class='build-grid'>";
html+="<div><div class='pool-head'><span>Available inputs"+(ordered?" - select, then order":"")+"</span><span id='selCount'>0 selected</span></div>";
html+="<div class='pool' id='pool'></div>";
html+="<button class='btn btn-primary btn-block' id='btnSend' style='margin-top:14px'>"+(ordered?"Lock in the skill":(mod.type==="capture"?"Save the kit":"Send to the AI"))+"</button>";
html+="</div>";
html+="<div class='build-side'>";
html+="<div class='ctxwin'><h4>Context window<span id='capBadge' style='color:var(--graphite);font-size:10px'>0 / "+mod.cap+"</span></h4>";
html+="<div class='sub'>Everything the model can see for this task</div>";
html+="<div class='cap' id='capWrap'><div class='fill' id='capFill'></div></div>";
html+="<div class='cap-lab' id='capLab2'>empty</div>";
html+="<div class='ctx-stack' id='ctxStack'></div></div>";
html+="<div class='preview'><h4>Projected result</h4><div id='previewText' style='font-size:13.5px;line-height:1.5;color:var(--graphite)'>Nothing loaded yet.</div>";
html+="<div class='dials' style='display:flex;gap:8px;margin-top:13px'>";
html+="<div class='dial'>Quality<span class='v' id='dQ'>--</span></div>";
html+="<div class='dial'>Load<span class='v' id='dL'>0%</span></div>";
html+="<div class='dial'>Signal<span class='v' id='dS'>--</span></div></div></div>";
html+="</div></div></div>";
stage.innerHTML=html;
var pool=$("pool");
var ph=""; for(var i=0;i<mod.components.length;i++){ ph+=chipHTML(mod.components[i],ordered); }
pool.innerHTML=ph;
function toggle(id){ var k=sel.indexOf(id); if(k>=0) sel.splice(k,1); else sel.push(id); repaint(); }
function reorder(id,dir){ var k=sel.indexOf(id); if(k<0) return; var n=k+dir; if(n<0||n>=sel.length) return; var t=sel[k]; sel[k]=sel[n]; sel[n]=t; repaint(); }
function repaint(){
var selObjs=[],i; for(i=0;i<sel.length;i++){ var o=compById(mod,sel[i]); if(o) selObjs.push(o); }
paintContext(selObjs, mod.cap);
Array.prototype.forEach.call(pool.querySelectorAll(".chip"),function(ch){
var id=ch.getAttribute("data-id"); var pos=sel.indexOf(id);
ch.classList.toggle("sel",pos>=0);
if(ordered) ch.classList.toggle("ordmode",pos>=0);
var num=ch.querySelector(".num"); if(num) num.textContent=pos>=0?(pos+1):"";
});
$("selCount").textContent=sel.length+" selected";
var pr=scoreBuild(mod, sel);
var total=0,pos=0,neg=0; for(i=0;i<selObjs.length;i++){ total+=selObjs[i].cost; if(selObjs[i].q>0)pos+=selObjs[i].q; else neg+=Math.abs(selObjs[i].q); }
var dQ=$("dQ"),dL=$("dL"),dS=$("dS"),pv=$("previewText");
if(dQ) dQ.textContent= sel.length?pr.result:"--";
if(dL){ var lp=Math.round(total/mod.cap*100); dL.textContent=lp+"%"; dL.style.color= total>mod.cap?"var(--redline)":"var(--cyan)"; }
if(dS){ var sig=(pos+neg)>0?Math.round(pos/(pos+neg)*100):0; dS.textContent= sel.length?(sig+"%"):"--"; dS.style.color= sig>=70?"var(--verified)":(sig>=45?"var(--trace)":"var(--redline)"); }
if(pv) pv.innerHTML=previewLine(mod,pr,total);
}
pool.addEventListener("click",function(e){
var tgt=e.target; if(!tgt||!tgt.closest) return;
var chip=tgt.closest(".chip"); if(!chip) return;
var id=chip.getAttribute("data-id");
if(tgt.closest(".oup")){ e.stopPropagation(); reorder(id,-1); return; }
if(tgt.closest(".odn")){ e.stopPropagation(); reorder(id,1); return; }
toggle(id);
});
pool.addEventListener("keydown",function(e){
if(e.key==="Enter"||e.key===" "){ var tgt=e.target; if(tgt&&tgt.classList&&tgt.classList.contains("chip")){ e.preventDefault(); toggle(tgt.getAttribute("data-id")); } }
});
$("btnSend").onclick=function(){
if(sel.length===0){ var pv=$("previewText"); if(pv) pv.innerHTML="Load at least the essentials before you send."; return; }
var res=scoreBuild(mod, sel);
S.quality=clamp(Math.round(S.quality+(res.result-S.quality)*0.5),0,100);
var hrs=4+(res.passed?2:8)+res.overflow*2; S.hours-=hrs;
if(res.passed){ S.trust=clamp(S.trust+6,0,100); S.score+=Math.round(res.result*0.2); }
else { S.trust=clamp(S.trust-8,0,100); }
renderMeters();
if(failed()){ setback(idx); return; }
if(res.passed){ if(mod.sets) S[mod.sets]=true; savePlaybook(mod.unlock); }
buildDebrief(mod, idx, res);
};
paintContext([], mod.cap);
repaint();
}
function buildDebrief(mod, idx, res){
scrollTop();
var passed=res.passed;
var html="<div class='sheet'>";
html+="<div class='eyebrow'><span class='code'>"+mod.code+"</span> "+(passed?"Approved":"Rework needed")+"</div>";
html+="<h2 class='title'>"+(passed?"That will fly":"This will not fly yet")+"</h2>";
var lead = passed
? ("Result "+res.result+" / 100. Clean enough to send - and you walk away with a reusable pattern.")
: ("Result "+res.result+" / 100, under the "+mod.pass+" you need. Here is exactly why - then take another pass.");
html+="<p class='lead'>"+lead+"</p>";
if(res.overflow>0) html+="<div class='goalbox' style='border-color:rgba(255,90,95,.4);background:rgba(255,90,95,.08)'><span class='tg' style='color:var(--redline);border-color:rgba(255,90,95,.4)'>Overflow</span><div>You loaded "+res.overflow+" over capacity, so the model quietly dropped your lowest-priority context. That is why piling on more made it worse.</div></div>";
if(mod.ordered && res.ordInv>0) html+="<div class='goalbox'><span class='tg'>Order</span><div>Your steps are out of sequence ("+res.ordInv+" swap"+(res.ordInv>1?"s":"")+" off). A good skill runs structure, then produce, then <b>verify</b>, then format.</div></div>";
html+="<div style='margin-top:16px'>";
for(var i=0;i<res.selObjs.length;i++){ var c=res.selObjs[i]; html+=vrow(c.verdict,c.text,c.note); }
for(var m=0;m<res.missed.length;m++){ html+=vrow("bad","Missing: "+res.missed[m].text,"Essential. "+res.missed[m].note); }
html+="</div>";
if(passed) html+="<div class='goalbox' style='margin-top:16px'><span class='tg' style='color:var(--verified);border-color:rgba(52,201,138,.4)'>Saved</span><div>Added to your Playbook: <b>"+mod.unlock.name+"</b>. Open Playbook any time to copy it.</div></div>";
html+="<div style='margin-top:22px'>";
html+= passed?"<button class='btn btn-primary' id='dbNext'>Continue</button>":"<button class='btn btn-amber' id='dbRetry'>Rework it</button>";
html+="</div></div>";
stage.innerHTML=html;
if(passed){ $("dbNext").onclick=function(){ if(mod.kitAfter){ showKit(idx); } else { loadModule(idx+1); } }; }
else { $("dbRetry").onclick=function(){ renderBuild(mod, idx); }; }
}
function setback(idx){
openModal("<div style='text-align:center'><div class='cloud'>Revision Cloud</div><h2 class='title' style='font-size:25px'>Back to rework</h2><p class='lead' style='margin:10px auto 18px'>That pushed your hours or the owner's trust past the breaking point - in the real world, that is a redesign. Reset and run this one again, smarter.</p><button class='btn btn-amber' id='sbBack'>Reset & retry</button></div>");
scrim.classList.add("setback");
$("sbBack").onclick=function(){ closeModal(); restore(); loadModule(idx); };
}
/* ---- DECISION: scoped scenes, choices with consequences ---- */
function renderDecision(mod, idx){
var i=0;
function scene(){
scrollTop();
var sc=mod.scenes[i];
var html="<div class='sheet'>";
html+="<div class='eyebrow'><span class='code'>"+mod.code+"</span> "+mod.phase+"</div>";
html+="<div class='scene-meta'>"+sc.meta+"</div>";
html+="<p class='situation'>"+sc.situation+"</p>";
if(sc.detail) html+="<p class='lead' style='margin-bottom:16px'>"+sc.detail+"</p>";
html+="<div class='options' id='opts'>";
for(var o=0;o<sc.options.length;o++){ html+="<button class='opt' data-i='"+o+"'>"+sc.options[o].text+"</button>"; }
html+="</div><div id='oc'></div></div>";
stage.innerHTML=html;
Array.prototype.forEach.call(stage.querySelectorAll(".opt"),function(btn){ btn.onclick=function(){ choose(parseInt(btn.getAttribute("data-i"),10)); }; });
}
function choose(o){
var sc=mod.scenes[i], op=sc.options[o];
var delta=op.delta, verdict=op.verdict, outcome=op.outcome;
if(op.req==="workspace" && !S.workspaceReady && op.altDelta){ delta=op.altDelta; verdict=op.altVerdict||verdict; outcome=op.altOutcome||outcome; }
applyDelta(delta);
if(op.set) S.agentCalibrated=op.set;
Array.prototype.forEach.call(stage.querySelectorAll(".opt"),function(b){ b.disabled=true; if(b.getAttribute("data-i")===String(o)) b.style.borderColor="var(--blueprint)"; });
var oc=$("oc");
var last=(i>=mod.scenes.length-1);
oc.innerHTML="<div class='outcome "+verdict+"'><span class='vh'>"+vlabel(verdict)+"</span>"+outcome+deltaHTML(delta)+"</div><div style='margin-top:20px'><button class='btn btn-primary' id='ocNext'>"+(last?"See debrief":"Next")+"</button></div>";
if(failed()){ var bn=$("ocNext"); bn.textContent="Rework"; bn.className="btn btn-amber"; bn.onclick=function(){ setback(idx); }; return; }
$("ocNext").onclick=function(){ i++; if(i<mod.scenes.length){ scene(); } else { decisionDebrief(mod, idx); } };
}
scene();
}
function decisionDebrief(mod, idx){
scrollTop();
savePlaybook(mod.unlock);
var html="<div class='sheet'>";
html+="<div class='eyebrow'><span class='code'>"+mod.code+"</span> Debrief</div>";
html+="<h2 class='title'>"+mod.title+" - done</h2>";
html+="<p class='lead'>How you scope it, what you ground it on, and whether you verify - that is the whole game. Right now: Quality "+Math.round(S.quality)+", Trust "+Math.round(S.trust)+", Hours left "+Math.round(S.hours)+".</p>";
html+="<div class='goalbox'><span class='tg' style='color:var(--verified);border-color:rgba(52,201,138,.4)'>Saved</span><div>Added to your Playbook: <b>"+mod.unlock.name+"</b>.</div></div>";
html+="<div style='margin-top:22px'><button class='btn btn-primary' id='ddNext'>"+(mod.final?"See your results":"Continue")+"</button></div></div>";
stage.innerHTML=html;
$("ddNext").onclick=function(){ if(mod.final){ showResults(); } else { loadModule(idx+1); } };
}
/* ---- STARTER KIT ---- */
function kitText(){
var L=[];
L.push("AEC AI STARTER KIT");
L.push("A reusable system for running AI on design-bid-build work.");
L.push("Paste any block into a ChatGPT Project, a Claude Project / Skill, or a Gemini Gem.");
L.push("");
L.push("====================================");
L.push(PATTERNS.body);
L.push("");
for(var i=0;i<S.playbook.length;i++){
if(S.playbook[i].id==="mental") continue;
L.push("====================================");
L.push(S.playbook[i].body);
L.push("");
}
L.push("====================================");
L.push("PRESERVE CONTEXT (every project)");
for(var t=0;t<TIPS.length;t++){ L.push("- "+stripTags(TIPS[t][0])); }
return L.join("\n");
}
function copyText(txt, btn, label){
function ok(){ if(btn){ btn.textContent="Copied"; setTimeout(function(){ btn.textContent=label; },1400); } }
function fb(){ var ta=document.createElement("textarea"); ta.value=txt; ta.style.position="fixed"; ta.style.opacity="0"; document.body.appendChild(ta); ta.focus(); ta.select(); try{ document.execCommand("copy"); ok(); }catch(e){} document.body.removeChild(ta); }
try{ if(navigator.clipboard && navigator.clipboard.writeText){ navigator.clipboard.writeText(txt).then(ok,fb); } else { fb(); } }
catch(e){ fb(); }
}
function showKit(idx){
scrollTop();
var txt=kitText();
var html="<div class='sheet'>";
html+="<div class='eyebrow'><span class='code'>KIT</span> Closeout</div>";
html+="<h2 class='title'>Your AEC AI Starter Kit</h2>";
html+="<p class='lead'>Everything you assembled, in one place - prompt patterns, your reusable templates, the weekly minutes skill, the submittal-review agent, and the habits that keep context alive between projects. Copy it, drop it into a Project or Gem, and your next job starts here instead of from a blank page.</p>";
html+="<pre class='kit' id='kitPre'>"+escapeHTML(txt)+"</pre>";
html+="<div style='margin-top:16px;display:flex;gap:10px;flex-wrap:wrap'><button class='btn btn-amber' id='kitCopy'>Copy the kit</button><button class='btn btn-primary' id='kitNext'>Continue</button></div></div>";
stage.innerHTML=html;
$("kitCopy").onclick=function(){ copyText(txt,$("kitCopy"),"Copy the kit"); };
$("kitNext").onclick=function(){ loadModule(idx+1); };
}
/* ---- RESULTS ---- */
function showResults(){
scrollTop();
var composite=Math.round(S.score + (S.quality-50)*0.4 + (S.trust-50)*0.4);
var r=ratingFor(composite);
var html="<div class='sheet'>";
html+="<div class='eyebrow'><span class='code'>DONE</span> Field guide complete</div>";
html+="<div class='rating'>"+r[0]+"</div>";
html+="<h2 class='title'>You can run AI like a tool, not a toy.</h2>";
html+="<p class='lead'>"+r[1]+"</p>";
html+="<div style='display:flex;gap:14px;flex-wrap:wrap;font-family:var(--mono);font-size:12px;margin:8px 0 6px'>";
html+="<span style='color:var(--cyan)'>Quality "+Math.round(S.quality)+"</span><span style='color:var(--trace)'>Hours left "+Math.round(S.hours)+"</span><span style='color:var(--verified)'>Trust "+Math.round(S.trust)+"</span><span style='color:var(--graphite)'>Score "+S.score+"</span>";
if(S.rev>0) html+="<span style='color:var(--redline)'>Reworks "+S.rev+"</span>";
html+="</div>";
html+="<ul class='recap'>";
for(var i=0;i<MODULES.length;i++){ var m=MODULES[i]; html+="<li><span class='n'>"+m.code+"</span><div><b>"+m.title+"</b><span>"+recapLine(m.code)+"</span></div></li>"; }
html+="</ul>";
html+="<div style='display:flex;gap:10px;flex-wrap:wrap'>";
html+="<button class='btn btn-amber' id='resKit'>Copy starter kit</button>";
html+="<button class='btn btn-ghost' id='resPlat'>Platform cheat sheet</button>";
html+="<button class='btn btn-primary' id='resReplay'>Play again</button>";
html+="</div></div>";
stage.innerHTML=html;
$("resKit").onclick=function(){ copyText(kitText(),$("resKit"),"Copy starter kit"); };
$("resPlat").onclick=openPlatforms;
$("resReplay").onclick=function(){ resetGame(); };
}
function resetGame(){ S={module:0,quality:60,hours:100,trust:65,score:0,workspaceReady:false,agentCalibrated:null,rev:0,playbook:[],snapshot:null}; renderMeters(); renderTitle(); }
/* ---- PLAYBOOK MODAL ---- */
function pbItem(u, open){
return "<div class='pb-item"+(open?" open":"")+"'><div class='pb-head'><span class='nm'>"+u.name+"</span><span class='tg'>"+u.tag+"</span></div><div class='pb-body'><pre class='kit'>"+escapeHTML(u.body)+"</pre></div></div>";
}
function openPlaybook(){
var html="<div class='eyebrow' style='margin-bottom:14px'><span class='code'>REF</span> Your Playbook</div>";
html+="<p class='lead' style='font-size:14px;margin-bottom:14px'>Standard patterns are always here. Everything else you unlock by playing - tap any card to expand it.</p>";
html+=pbItem(PATTERNS, true);
if(S.playbook.length===0){ html+="<div class='pb-empty'>Play through the modules to unlock reusable prompts, skills, and an agent spec - they collect here as you go.</div>"; }
for(var i=0;i<S.playbook.length;i++){ if(S.playbook[i].id==="mental"){ html+=pbItem(S.playbook[i],false); continue; } html+=pbItem(S.playbook[i],false); }
html+="<div style='margin-top:16px'><button class='btn btn-amber btn-block' id='pbCopyAll'>Copy everything as a starter kit</button></div>";
openModal(html);
Array.prototype.forEach.call(modalContent.querySelectorAll(".pb-item"),function(it){ it.querySelector(".pb-head").onclick=function(){ it.classList.toggle("open"); }; });
$("pbCopyAll").onclick=function(){ copyText(kitText(),$("pbCopyAll"),"Copy everything as a starter kit"); };
}
/* ---- PLATFORMS MODAL: cross-AI cheat sheet + preserve-context tips ---- */
function openPlatforms(){
var html="<div class='eyebrow' style='margin-bottom:6px'><span class='code'>X-AI</span> Same skills, three tools</div>";
html+="<p class='lead' style='font-size:14px;margin-bottom:14px'>Everything in this guide works in ChatGPT, Claude, and Gemini. Only the button names differ.</p>";
html+="<table class='xtable'><thead><tr><th>What you want</th><th class='c'>ChatGPT</th><th class='c'>Claude</th><th class='c'>Gemini</th></tr></thead><tbody>";
for(var i=0;i<CHEATSHEET.length;i++){ var r=CHEATSHEET[i];
html+="<tr><td class='concept'>"+r.c+"</td>";
html+="<td data-h='ChatGPT'><span class='feat'>"+r.cg[0]+"</span><span class='det'>"+r.cg[1]+"</span></td>";
html+="<td data-h='Claude'><span class='feat'>"+r.cl[0]+"</span><span class='det'>"+r.cl[1]+"</span></td>";
html+="<td data-h='Gemini'><span class='feat'>"+r.gm[0]+"</span><span class='det'>"+r.gm[1]+"</span></td></tr>";
}
html+="</tbody></table>";
html+="<div class='eyebrow' style='margin:20px 0 10px'><span class='code'>TIPS</span> Preserve context for next time</div>";
html+="<ul class='tiplist'>";
for(var t=0;t<TIPS.length;t++){ html+="<li><span class='b'>›</span><div>"+TIPS[t][0]+"</div></li>"; }
html+="</ul>";
openModal(html);
}
/* ---- TITLE SCREEN ---- */
function renderTitle(){
scrollTop();
setSheet("FIELD GUIDE","press start");
var html="<div class='sheet'>";
html+="<div class='eyebrow'><span class='code'>FIELD GUIDE</span> for engineers & project managers</div>";
html+="<h1 class='title'>PROMPT SET</h1>";
html+="<p class='lead'>A hands-on way to get genuinely good at AI for design-bid-build work - prompts, context, workspaces, skills, and agents - using the tools you already have: ChatGPT, Claude, and Gemini. No coding. Every choice spends real Quality, Hours, and Trust, and the consequences carry forward.</p>";
html+="<div class='goalbox'><span class='tg'>How</span><div>Eight short modules follow one project from pre-design to the field. Read the brief, make your calls, watch what each one costs. What you unlock collects in your <b>Playbook</b> as a copy-ready starter kit.</div></div>";
html+="<div style='margin:20px 0 12px;display:flex;gap:10px;flex-wrap:wrap'><button class='btn btn-primary' id='tStart'>Start training</button><button class='btn btn-ghost' id='tPlat'>Platform cheat sheet</button></div>";
html+="<div class='pool-head' style='margin-top:18px'><span>Jump to a module</span></div>";
html+="<div style='display:flex;gap:8px;flex-wrap:wrap'>";
for(var i=0;i<MODULES.length;i++){ var m=MODULES[i]; html+="<button class='btn btn-ghost tjump' data-i='"+i+"' style='font-size:13px;padding:9px 12px'>"+m.code+" · "+m.title+"</button>"; }
html+="</div>";
html+="<p style='color:var(--faint);font-family:var(--mono);font-size:11px;margin-top:18px;line-height:1.6'>First time through, go in order - each module builds on the last. Your meters persist across the whole run.</p>";
html+="</div>";
stage.innerHTML=html;
$("tStart").onclick=function(){ loadModule(0); };
$("tPlat").onclick=openPlatforms;
Array.prototype.forEach.call(stage.querySelectorAll(".tjump"),function(b){ b.onclick=function(){ loadModule(parseInt(b.getAttribute("data-i"),10)); }; });
}
/* ---- TOP BAR ---- */
$("btnPlaybook").onclick=openPlaybook;
$("btnPlatforms").onclick=openPlatforms;
$("btnMenu").onclick=function(){
openModal("<div style='text-align:center'><h2 class='title' style='font-size:24px'>Back to start?</h2><p class='lead' style='margin:10px auto 18px'>This returns you to the title screen. Your Playbook is kept; your meters reset when you begin a new run.</p><div style='display:flex;gap:10px;justify-content:center;flex-wrap:wrap'><button class='btn btn-ghost' id='mNo'>Stay here</button><button class='btn btn-amber' id='mYes'>Back to start</button></div></div>");
$("mNo").onclick=closeModal;
$("mYes").onclick=function(){ closeModal(); renderTitle(); };
};
/* ---- BOOT ---- */
renderMeters();
renderTitle();
})();
</script>
</body>
</html>
```
[REQUESTED CHANGES]
(no specific request — apply your best judgment)
--- HOW TO RESPOND (READ FIRST) ---
Before writing any code, follow this exact process:
1. **ANALYZE** the widget source code provided above and identify:
a. Which Vibes SDK features it already uses (vibes.save, vibes.load, vibes.shared.join, etc.)
b. Which SDK features would genuinely benefit THIS specific widget — tailored to what it does, not a dump of everything available.
2. **PRESENT A NUMBERED LIST** covering:
- SDK features currently active in this widget
- New SDK features that would concretely improve this widget (be specific: why this widget, what it enables)
3. **WAIT** — do not write any code yet. Reply with your analysis and numbered list, then stop and ask the user which numbered items they want.
4. **IMPLEMENT ONLY** the items the user confirms, plus any explicit change they requested. Do not add unrequested features.
**IMPORTANT — Shared state room names:**
If you add vibes.shared.join(), do NOT use a hardcoded string literal as the room name (e.g. vibes.shared.join("lobby")) unless the user explicitly wants ALL viewers to share one single global state. A hardcoded room name means every person who visits this widget reads and writes the same shared state — it is a global room. For per-user or per-session isolation, derive the room name from a variable (e.g. a user ID, session token, or random value). When in doubt, use vibes.save/vibes.load for per-user persistence instead.
--- VIBES SDK CONTEXT ---
## Vibes SDK Reference
You are building an HTML widget for It Just Vibes (itjustvibes.com). The Vibes SDK is auto-injected — do NOT add a script tag. Just use `window.vibes` (or just `vibes`).
### Setup
Wrap your startup code in `vibes.onReady`:
```js
vibes.onReady(async () => {
const saved = await vibes.load("myKey");
// your widget logic here
});
```
### State (Per-User Persistence)
Every user gets their own isolated state per widget. All methods return Promises.
| Method | Description |
|--------|-------------|
| `await vibes.save(key, value)` | Save JSON-serializable data |
| `await vibes.load(key)` | Load saved data (returns `null` if not found) |
| `await vibes.delete(key)` | Delete a saved key |
| `await vibes.listKeys()` | Get array of all saved key names |
**Key rules:**
- Keys are strings, max 64 characters, alphanumeric + dashes/underscores
- Values must be JSON-serializable (objects, arrays, strings, numbers, booleans)
- Max 100KB per value, 500KB per widget per user, 5MB per user total
- Max 100 keys per widget per user
### Fetch Proxy
Use direct `fetch()` for APIs with permissive CORS headers (`Access-Control-Allow-Origin: *`). Use `vibes.fetch` for APIs without CORS headers (the proxy handles cross-origin requests):
```js
const resp = await vibes.fetch("https://api.example.com/data", {
method: "GET", // GET, POST, PUT, DELETE
headers: {}, // optional headers
body: null, // optional body (string)
timeout: null // optional timeout in ms
});
const data = await resp.json(); // or resp.text()
console.log(resp.status, resp.ok);
```
### Multiplayer (Shared State)
Real-time shared state across all users viewing the same widget.
```js
// Join a room (call once at startup)
await vibes.shared.join("lobby", { persistent: true });
// Set shared state (broadcasts to all users)
await vibes.shared.set("score", { player1: 10, player2: 7 });
// Read shared state (synchronous, returns last known value)
const score = vibes.shared.get("score");
// Listen for changes to a specific key
vibes.shared.onChange("score", (newValue) => {
console.log("Score updated:", newValue);
});
// Listen for any shared state change
vibes.shared.onAny((key, value) => {
console.log(key, "changed to", value);
});
// Get number of connected users
const count = await vibes.shared.getUserCount();
// Leave room
vibes.shared.leave();
// Clear all shared state for this room
await vibes.shared.clear();
```
**Shared state options:**
- `{ persistent: true }` — state survives page reloads (stored server-side)
- Default room name is "__default__" if omitted
### Rules
1. **Do NOT use localStorage, sessionStorage, or window.storage** — they are blocked or undefined in the sandbox. Use `vibes.save`/`vibes.load` instead.
2. **Do NOT add a script tag to import the SDK** — it is auto-injected.
3. **Wrap startup code in `vibes.onReady()`** — the SDK may not be ready immediately.
4. **Await all SDK calls** — every method (except `vibes.shared.get`) returns a Promise.
5. **Do NOT import external JS libraries via script tags** — they will be blocked. Include library code inline or use a CDN link in a `<link>` tag for CSS only.
6. **Keep total code under 80KB** — that is the default widget size limit (your account limit may be higher).
### Rate Limits
| Operation | Limit |
|-----------|-------|
| Writes (save + delete combined) | 30/min |
| Reads (load) | 60/min |
| List keys | 30/min |
| Fetch proxy | Rate limited per widget |
### Error Handling
All SDK methods can reject. Wrap in try/catch:
```js
try {
await vibes.save("key", value);
} catch (err) {
console.error("Save failed:", err.message);
}
```
Fetch proxy errors include `err.code`: `"RATE_LIMITED"`, `"BLOCKED"`, `"FETCH_ERROR"`.
### Data Export
Export rows of data as CSV or Excel directly from your widget.
```js
// Register dataset for the platform's "Export data" button
// AND optionally trigger an immediate download
vibes.exportData(rows, { filename: 'results.csv' })
// rows: Array of arrays (each inner array is one row; first row = headers)
// options.filename: sets the download filename and format (csv or xlsx)
// options.directDownload: false to only register without downloading (default: true)
```
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `filename` | string | `'data.csv'` | Download filename; extension determines format (`.csv` or `.xlsx`) |
| `directDownload` | boolean | `true` | Trigger immediate browser download in addition to registering the dataset |
**Notes:**
- First call registers the dataset so the widget chrome shows an "Export data" button
- Use `.csv` extension for CSV, `.xlsx` for Excel
- CSV injection safety is automatic (formula-starting cells prefixed with `'`)
- Data stays in the browser — no server round-trip
--- FORK & RESUBMIT INSTRUCTION ---
Return the complete, self-contained HTML file with all changes applied.
It should be ready to paste into itjustvibes.com/submit to create a new fork.
Include this comment at the top of the output:
/* Forked from: https://itjustvibes.com/Owner/prompt-set-an-ai-field-guide-for-engineers-project-managers */