Version 2 of 3 · · razie via designer-86designer · Current version

Open the page

---
description: Pro project setup as a signed base d2 Page (P-1215): your team, agents now or later, then Members (no members, by invite, open) and the dashboard becomes home
tags: page, onboarding
---
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"><meta name="color-scheme" content="light dark"><title>Ready to prototype</title>
<style>
:root{--paper:#fbfaf7;--card:#fff;--ink:#1d1d1b;--soft:#6b6a66;--line:#e4e1da;--rose:#b23a48;--lv:#6c8cff;--display:'Manrope',system-ui,-apple-system,'Segoe UI',sans-serif;--body:system-ui,-apple-system,'Segoe UI',sans-serif}
@media (prefers-color-scheme:dark){:root{--paper:#141413;--card:#1d1d1b;--ink:#ecebe6;--soft:#9c9a93;--line:#33322f;--rose:#f08a92}}
*{box-sizing:border-box}html,body{background:var(--paper);color:var(--ink);margin:0}body{font:calc(15px*var(--fz,1))/1.5 var(--body)}
.sp-wrap{max-width:900px;margin:0 auto;padding:6px clamp(12px,3vw,28px) 56px}
h1{font:800 calc(clamp(26px,4vw,34px)*var(--fz,1))/1.1 var(--display);letter-spacing:-.02em;margin:12px 0}
h2{font:800 22px/1.2 var(--display);margin:28px 0 8px}a{color:var(--lv)}.small{font-size:13px;color:var(--soft)}
.emb{margin:16px 0}ul{padding-left:20px}li{margin:4px 0}
.sp-last{border-top:1px solid var(--line);margin-top:28px}
.sp-opts{display:grid;gap:10px;margin:12px 0 16px;max-width:560px}
.sp-opt{display:flex;gap:10px;align-items:flex-start;cursor:pointer;background:color-mix(in srgb,var(--lv) 9%,var(--card));border:1px solid color-mix(in srgb,var(--lv) 35%,var(--line));border-radius:12px;padding:12px 14px}
.sp-opt input{width:20px;height:20px;margin:2px 0 0;accent-color:var(--lv);flex:none}.sp-opt b{display:block}.sp-opt span{color:var(--soft);font-size:14px}
.sp-go{border:0;cursor:pointer;padding:14px 30px;border-radius:999px;background:var(--lv);color:#111;font:800 1.3em/1.2 var(--body);box-shadow:0 2px 8px rgba(0,0,0,.18)}.sp-go:disabled{opacity:.6;cursor:default}
.sp-say{display:block;margin-top:8px;color:var(--rose);font-size:13px;min-height:1em}
</style>
</head><body><div class="sp-wrap">
<h1>Ready to prototype</h1>
<p>You have more ideas than time. Here your designer turns them into a spec, a design, a UI design, a working prototype and its tests, in real time. Hand it all to your team, or make the prototype your product.</p>
<div class="emb"><style>

.w{--c:#6c8cff;--c2:#4fb3a9;--ink:#e8e6e1;--mute:#9aa0a6;--line:#2e343c;--card:#1b1f24;--ok:#4caf6a;background:#121417;border-radius:14px;padding:20px 18px;font:13px/1.35 system-ui,-apple-system,'Segoe UI',sans-serif;color:var(--ink);display:grid;grid-template-columns:auto 30px auto 30px minmax(0,1fr) minmax(150px,auto);grid-template-rows:auto auto;align-items:center;gap:12px 10px;overflow:hidden}
.w>.docs{grid-column:5;grid-row:1}.w>.pipe{grid-column:5;grid-row:2}.w>.tests{grid-column:6;grid-row:1/3}.w>.you{grid-row:1/3}.w>.l1{grid-row:1/3}.w>.team{grid-row:1/3}.w>.l2{grid-row:1/3}
@media (max-width:680px){.w{display:flex;flex-wrap:wrap;justify-content:center;row-gap:16px}.w .lk{width:28px;flex:none}.w .lk.l2{display:none}.w .docs,.w .pipe,.w .tests{flex:1 1 100%}}
.who{display:grid;justify-items:center;gap:6px;text-align:center}
.av{width:52px;height:52px;border-radius:50%;display:grid;place-items:center;font-weight:800;font-size:15px}
.you .av{border:2px solid var(--c);color:var(--c);background:#1b1f24}
.lbl{font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--mute)}
.bub{max-width:150px;min-height:34px;background:color-mix(in srgb,var(--c) 22%,#1b1f24);border-radius:12px 12px 12px 4px;padding:6px 9px;font-size:12px;text-align:left}
.bub i{display:inline-block;width:1px;height:12px;background:var(--ink);vertical-align:-2px;animation:blink .8s steps(1) infinite}
@keyframes blink{50%{opacity:0}}
/* the team: the designer agent, and a coder agent that joins (optional on Pro) */
.team{display:grid;gap:12px;justify-items:center}
.ag .av{position:relative;width:58px;height:58px;background:radial-gradient(circle at 35% 30%,color-mix(in srgb,var(--k) 70%,#fff) 0,var(--k) 45%,color-mix(in srgb,var(--k) 45%,#000) 100%);color:#0d1426;font-size:20px;transition:box-shadow .4s,opacity .4s}
.ag{--k:var(--c)}.ag.cd{--k:var(--c2)}.ag.cd .av{width:46px;height:46px;font-size:15px}
.ag .av::after{content:"";position:absolute;inset:-6px;border-radius:50%;border:2px dashed color-mix(in srgb,var(--k) 70%,transparent);opacity:.25}
.ag.busy .av{box-shadow:0 0 22px 4px color-mix(in srgb,var(--k) 45%,transparent)}.ag.busy .av::after{opacity:1;animation:spin 3s linear infinite}
.ag.cd{opacity:.3;transition:opacity .5s}.ag.cd.on{opacity:1}
@keyframes spin{to{transform:rotate(360deg)}}
.lk{position:relative;height:2px;background:var(--line);border-radius:2px}
.lk::after{content:"";position:absolute;top:-3px;left:0;width:8px;height:8px;border-radius:50%;background:var(--c);opacity:0}
.lk.go::after{animation:run .7s ease-in forwards}
@keyframes run{0%{left:0;opacity:1}100%{left:calc(100% - 8px);opacity:1}}
/* the docs write themselves; the UI design draws its boxes; the prototype builds */
.docs{display:flex;gap:8px;min-width:0}
.doc{flex:1 1 0;min-width:0;max-width:120px;background:#f4f1ea;color:#2a2a28;border-radius:4px 12px 4px 4px;padding:8px 9px 10px;box-shadow:0 3px 10px rgba(0,0,0,.35);opacity:.3;transform:translateY(6px);transition:opacity .4s,transform .4s;position:relative;min-height:92px}
.doc.on{opacity:1;transform:none}
.doc h4{margin:0 0 6px;font:800 10.5px system-ui;letter-spacing:.05em;text-transform:uppercase;color:#3550b5}
.doc .ln{height:5px;border-radius:3px;background:#c9c3b6;margin:5px 0;width:0;transition:width .35s ease-out}.doc .ln.h{background:#9fb0f0;height:6px}
.pen{position:absolute;font-size:11px;opacity:0;transition:top .3s,left .3s,opacity .2s}.doc.writing .pen{opacity:1}
.ui .wf{display:grid;grid-template-columns:1fr 2fr;gap:4px}.ui .wf span{border:1.5px dashed #9fb0f0;border-radius:3px;height:14px;opacity:0;transition:opacity .3s}.ui .wf span.tall{grid-row:span 2;height:auto}.ui .wf span.on{opacity:1}
.app{background:#1b1f24;color:var(--ink);border:1px solid var(--line);border-radius:8px;padding:0 0 8px;overflow:hidden}
.app .bar{display:flex;gap:3px;padding:5px 6px;background:#232830}.app .bar b{width:6px;height:6px;border-radius:50%;background:#4a515b}
.app h4{color:var(--c);margin:6px 6px 4px;font-size:9.5px;letter-spacing:.01em}
.tiles{display:grid;grid-template-columns:1fr 1fr;gap:5px;padding:0 8px}
.tiles span{height:17px;border-radius:5px;background:color-mix(in srgb,var(--c) 35%,#232830);transform:scale(.4);opacity:0;transition:transform .3s cubic-bezier(.3,1.6,.6,1),opacity .3s}.tiles span.on{transform:none;opacity:1}
/* the pipeline: tickets move right as they are worked */
.pipe{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;min-width:0}
.lane{background:#1b1f24;border:1px dashed var(--line);border-radius:8px;padding:5px 6px;min-height:46px;display:flex;flex-direction:column;gap:4px}
.lane h5{margin:0;font:700 9.5px system-ui;letter-spacing:.07em;text-transform:uppercase;color:var(--mute)}
.tk{font-size:10.5px;padding:2px 6px;border-radius:5px;background:#2b3139;border-left:3px solid var(--c);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;animation:in .35s}
.tk.cdr{border-left-color:var(--c2)}.lane.dn .tk{border-left-color:var(--ok);opacity:.8}
@keyframes in{from{transform:translateX(-10px);opacity:0}to{transform:none;opacity:1}}
/* tests: a checklist that goes green */
.tests{background:#1b1f24;border:1px solid var(--line);border-radius:10px;padding:9px 11px;opacity:.3;transition:opacity .4s}.tests.on{opacity:1}
.tests h4{margin:0 0 6px;font:800 11px system-ui;letter-spacing:.05em;text-transform:uppercase;color:var(--mute)}
.tests ul{list-style:none;margin:0;padding:0;display:grid;gap:5px}
.tests li{display:flex;gap:7px;align-items:center;color:var(--mute);transition:color .3s;white-space:nowrap}
.tests li .bx{width:15px;height:15px;flex:none;border-radius:4px;border:1.5px solid #4a515b;display:grid;place-items:center;font-size:11px;font-weight:900;color:transparent;transition:all .3s}
.tests li.ok{color:var(--ink)}.tests li.ok .bx{background:var(--ok);border-color:var(--ok);color:#0b1a0f;animation:pop .35s}
@keyframes pop{0%{transform:scale(.6)}70%{transform:scale(1.25)}100%{transform:scale(1)}}
.tests .sum{margin-top:7px;font-size:11px;color:var(--ok);opacity:0;transition:opacity .4s}.tests.done .sum{opacity:1}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
</style>
<div class="w" id="w">
  <div class="who you"><div class="bub"><span id="say"></span><i></i></div><div class="av">you</div></div>
  <div class="lk l1" id="l1"></div>
  <div class="team"><div class="who ag" id="ag"><div class="av">✦</div><div class="lbl">designer</div></div><div class="who ag cd" id="cd"><div class="av">⌘</div><div class="lbl">coder</div></div></div>
  <div class="lk l2" id="l2"></div>
  <div class="docs">
    <div class="doc" id="d1"><span class="pen">✎</span><h4>Spec</h4><div class="ln h"></div><div class="ln"></div><div class="ln"></div><div class="ln h"></div><div class="ln"></div></div>
    <div class="doc" id="d2"><span class="pen">✎</span><h4>Design</h4><div class="ln h"></div><div class="ln"></div><div class="ln"></div><div class="ln"></div><div class="ln h"></div></div>
    <div class="doc ui" id="d3"><h4>UI design</h4><div class="wf"><span class="tall"></span><span></span><span></span></div></div>
    <div class="doc app" id="d4"><div class="bar"><b></b><b></b><b></b></div><h4>Prototype</h4><div class="tiles"><span></span><span></span><span></span><span></span></div></div>
  </div>
  <div class="tests" id="ts"><h4>Tests</h4><ul>
    <li><span class="bx">✓</span>an order totals its lines</li>
    <li><span class="bx">✓</span>stock never goes negative</li>
    <li><span class="bx">✓</span>a refund reopens stock</li>
    <li><span class="bx">✓</span>the API lists orders</li>
  </ul><div class="sum">4 of 4 passing</div></div>
  <div class="pipe" id="pp"><div class="lane" id="pn"><h5>New</h5></div><div class="lane" id="pw"><h5>Working</h5></div><div class="lane dn" id="pd"><h5>Done</h5></div></div>
</div>
<script>
// you ask → your designer (and a coder, if you add one) → Spec, Design, UI design, Prototype → tickets move through the
// pipeline → the tests go green; then again
const $=id=>document.getElementById(id),W=[0.92,0.7,0.84,0.6,0.88],T=[],at=(ms,f)=>T.push(setTimeout(f,ms));
const say="Here are the requirements for our order system…";
const tk=(lane,txt,cdr)=>{const e=document.createElement('div');e.className='tk'+(cdr?' cdr':'');e.textContent=txt;$(lane).appendChild(e);return e};
const mv=(e,lane)=>{if(e&&e.parentNode){$(lane).appendChild(e);e.style.animation='none';void e.offsetWidth;e.style.animation=''}};
function write(d,t0){const ls=[...d.querySelectorAll('.ln')],pen=d.querySelector('.pen');at(t0,()=>d.classList.add('on','writing'));
  ls.forEach((l,k)=>at(t0+150+k*150,()=>{const w=W[k%W.length]*100;l.style.width=w+'%';pen.style.top=(l.offsetTop-6)+'px';pen.style.left='calc('+w+'% - 2px)'}));
  at(t0+150+ls.length*150+120,()=>d.classList.remove('writing'))}
function run(){T.splice(0).forEach(clearTimeout);const w=$('w');
  w.querySelectorAll('.on,.ok,.go,.busy,.done,.writing').forEach(e=>e.classList.remove('on','ok','go','busy','done','writing'));
  w.querySelectorAll('.ln').forEach(l=>l.style.width='0');w.querySelectorAll('.tk').forEach(e=>e.remove());$('say').textContent='';
  [...say].forEach((c,k)=>at(80+k*28,()=>$('say').textContent+=c));
  const t1=80+say.length*28+200;
  at(t1,()=>$('l1').classList.add('go'));at(t1+600,()=>$('ag').classList.add('busy'));at(t1+900,()=>$('cd').classList.add('on','busy'));
  let a,b,c;at(t1+900,()=>{a=tk('pn','Spec: orders');b=tk('pn','Design: stock');c=tk('pn','Build: refunds',1)});
  at(t1+1100,()=>$('l2').classList.add('go'));
  write($('d1'),t1+1400);at(t1+1500,()=>mv(a,'pw'));
  write($('d2'),t1+2300);at(t1+2400,()=>{mv(a,'pd');mv(b,'pw')});
  at(t1+3200,()=>$('d3').classList.add('on'));$('d3').querySelectorAll('.wf span').forEach((s,k)=>at(t1+3350+k*200,()=>s.classList.add('on')));
  at(t1+3500,()=>{mv(b,'pd');mv(c,'pw')});
  const ta=t1+4100;at(ta,()=>$('d4').classList.add('on'));$('d4').querySelectorAll('.tiles span').forEach((s,k)=>at(ta+200+k*160,()=>s.classList.add('on')));
  at(ta+900,()=>mv(c,'pd'));
  const tt=ta+1000;at(tt,()=>$('ts').classList.add('on'));
  $('ts').querySelectorAll('li').forEach((li,k)=>at(tt+300+k*400,()=>li.classList.add('ok')));
  at(tt+300+4*400,()=>{$('ts').classList.add('done');$('ag').classList.remove('busy');$('cd').classList.remove('busy')});
  at(tt+300+4*400+3200,run)}
if(matchMedia('(prefers-reduced-motion: reduce)').matches){$('say').textContent=say;document.querySelectorAll('.doc,.tests,.tiles span,.wf span,.ag.cd').forEach(e=>e.classList.add('on'));document.querySelectorAll('.ln').forEach((l,k)=>l.style.width=W[k%W.length]*100+'%');document.querySelectorAll('.tests li').forEach(l=>l.classList.add('ok'));$('ts').classList.add('done');tk('pd','Spec: orders');tk('pd','Design: stock');tk('pd','Build: refunds',1)}
else run();
</script></div>
<ul><li><b>You:</b> ideas, in plain words, typed or spoken. Use <a href="/thoughts" data-go>Thoughts</a> to collect them and send them in a batch.</li><li><b>Your designer:</b> turns them into tasks on the <a href="/pipeline" data-go>pipeline</a>, and you see each one land in the spec, the design, the prototype and its tests.</li><li><b>A coder</b> (optional): builds the tasks while the designer keeps designing.</li></ul>
<p><b>This works from a single prompt, but it's best with a team of agents</b> working in the background while you keep thinking.</p>
<h2>Your team</h2>
<div class="emb"><style>
.t{font:15px/1.45 system-ui,-apple-system,'Segoe UI',sans-serif;color:var(--ink,#1d1d1b);display:grid;gap:14px;justify-items:start}
.ck{display:flex;gap:10px;align-items:flex-start;cursor:pointer;background:color-mix(in srgb,#6c8cff 9%,var(--card,#fff));border:1px solid color-mix(in srgb,#6c8cff 35%,var(--line,#e4e1da));border-radius:12px;padding:12px 14px;max-width:560px}
.ck input{width:20px;height:20px;margin:2px 0 0;accent-color:#6c8cff;flex:none}.ck b{display:block}.ck span{color:var(--soft,#6b6a66);font-size:14px}
.go{display:inline-block;border:0;cursor:pointer;padding:14px 30px;border-radius:999px;background:#6c8cff;color:#111;font:800 1.3em/1.2 system-ui,-apple-system,'Segoe UI',sans-serif;box-shadow:0 2px 8px rgba(0,0,0,.18)}.go:hover{filter:brightness(1.07)}
.say{color:var(--rose,#b23a48);font-size:13px;min-height:1em}
</style>
<div class="t">
<label class="ck"><input type="checkbox" id="tm-cd"><div><b>A coder too</b><span>Don't worry, you can add one later. For a complete setup, add it now: it builds while the designer keeps designing.</span></div></label>
<button class="go" id="tm-go" type="button">Create agents now →</button><span class="say" id="tm-say"></span>
</div>
<script>
// the coder choice rides to Start your agents as ?coder=1 (that page names the agents to start from it)
document.getElementById('tm-go').onclick=()=>{const to='/page/StartAgents?level=pro'+(document.getElementById('tm-cd').checked?'&coder=1':'');
  Promise.resolve(window.d2&&d2.go?d2.go(to):Promise.reject(new Error('open it in d2'))).catch(e=>{document.getElementById('tm-say').textContent=String(e&&e.message||e).replace(/^E_[A-Z_]+: /,'')})};
</script></div>
<section class="sp-last" id="members">
<h2>Will other people work here?</h2>
<p class="small">Your project stays closed until you choose. You can change this any time in Settings.</p>
<div class="sp-opts" role="radiogroup" aria-label="Members">
<label class="sp-opt"><input type="radio" name="join" value="none" checked><div><b>No members</b><span>Just you and your AI.</span></div></label>
<label class="sp-opt"><input type="radio" name="join" value="invite"><div><b>By invite only</b><span>People join when you invite them.</span></div></label>
<label class="sp-opt"><input type="radio" name="join" value="open"><div><b>Open join</b><span>Anyone with an account can join.</span></div></label>
</div>
<p>Or <b>start with prompts</b>: go on in your AI's chat, and add agents any time. The <a href="/app/Dashboard" data-go>dashboard</a> becomes your home.</p>
<p><button class="sp-go" id="sp-go" type="button">Start with prompts →</button> <span class="sp-say" id="sp-say" role="status"></span></p>
</section>
</div>
<script>
(()=>{const D2=window.d2||{},$=s=>document.querySelector(s),LEVEL="pro",PAGE="Page:"+"SetupPro";
const api=(m,u,b)=>D2.api?Promise.resolve(D2.api(m,u,b)):Promise.reject(new Error("E_BRIDGE: open this page in d2"));
const go=u=>D2.go?D2.go(u):(location.href=u);
const msg=e=>String(e&&e.message||e).replace(/^E_[A-Z_]+: /,"");
document.addEventListener("click",e=>{const a=e.target.closest&&e.target.closest("a[data-go]");if(!a)return;e.preventDefault();go(a.getAttribute("href"))});
// this page is the project's home from the moment the owner lands on it, until the last button makes the dashboard home
// (Razie 2026-10-10, P-1215: a signed d2 page sets the project's properties as its viewer); refused quietly to a member
(async()=>{try{const t=await api("GET","/api/v2/topics/Settings:PROJECT?format=raw");const s=typeof t==="string"?t:(t&&t.text)||"";
  const m=/^home:\s*(.*)$/m.exec(s);const cur=m?m[1].trim():"";if(!s||/App:Dashboard$/.test(cur)||cur.endsWith(PAGE))return;
  const next=m?s.replace(/^home:.*$/m,"home: "+PAGE):s.replace(/^---\n/,"---\nhome: "+PAGE+"\n");
  await api("PUT","/api/v2/drafts/Settings:PROJECT?ver=none",next);await api("POST","/api/v2/drafts/Settings:PROJECT/publish")}catch(e){}})();
$("#sp-go").onclick=async()=>{const b=$("#sp-go"),say=$("#sp-say"),v=(document.querySelector("input[name=join]:checked")||{}).value||"none";
  b.disabled=true;say.textContent="";
  try{await api("PATCH","/api/v2/project",{join:v==="open"?"open":"invite"})}
  catch(e){if(v!=="none"){say.innerHTML="Couldn't set who can join ("+msg(e).replace(/[<&]/g,"")+"). Set it in <a href=\"/d2admin/members\">Settings › Members</a>, or press again to go on.";b.disabled=false;b.dataset.skip="1";if(!b.dataset.tried){b.dataset.tried="1";return}}}
  try{await api("POST","/api/v2/project/home",{to:"dashboard"});go("/")}catch(e){say.textContent=msg(e);b.disabled=false}};
})();
</script>
</body></html>