← back to Pattern Vault
feat(vault): /handoff — manual-action cockpit for Steve: signup links (Spoonflower/Etsy/Stripe/affiliates) + all 14 designs with copy-title/desc/tags buttons + 4096px master downloads + one-click listings CSV. Everything he needs to list, in one page
928c4a95b632416b0ea252498dd32b1bea1c4312 · 2026-07-02 10:13:57 -0700 · Steve
Files touched
A public/handoff.htmlM server.js
Diff
commit 928c4a95b632416b0ea252498dd32b1bea1c4312
Author: Steve <steve@designerwallcoverings.com>
Date: Thu Jul 2 10:13:57 2026 -0700
feat(vault): /handoff — manual-action cockpit for Steve: signup links (Spoonflower/Etsy/Stripe/affiliates) + all 14 designs with copy-title/desc/tags buttons + 4096px master downloads + one-click listings CSV. Everything he needs to list, in one page
---
public/handoff.html | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++
server.js | 1 +
2 files changed, 92 insertions(+)
diff --git a/public/handoff.html b/public/handoff.html
new file mode 100644
index 0000000..4023888
--- /dev/null
+++ b/public/handoff.html
@@ -0,0 +1,91 @@
+<!doctype html><html lang="en"><head>
+<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>Pattern Vault — Manual Handoff</title>
+<style>
+:root{--bg:#0d0b10;--card:#17141d;--ink:#efe9f7;--mut:#9a8fb0;--gold:#d4af6a;--line:#2a2436;--good:#37d67a}
+*{box-sizing:border-box;margin:0}body{background:var(--bg);color:var(--ink);font:14px/1.5 "Avenir Next",system-ui,sans-serif}
+header{padding:34px 6vw 12px}h1{font-size:26px;letter-spacing:.1em;text-transform:uppercase}h1 em{color:var(--gold);font-style:normal}
+.sub{color:var(--mut);margin-top:6px}
+h2{font-size:15px;letter-spacing:.08em;text-transform:uppercase;color:var(--gold);margin:26px 6vw 10px;padding-top:20px;border-top:1px solid var(--line)}
+.steps{padding:0 6vw;display:grid;gap:10px}
+.step{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:14px 16px;display:flex;gap:14px;align-items:flex-start}
+.num{flex:0 0 26px;height:26px;border-radius:50%;background:#221a33;color:var(--gold);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:13px}
+.step .t{font-weight:600}.step .d{color:var(--mut);font-size:13px;margin-top:2px}
+.step a.btn{display:inline-block;margin-top:8px;background:var(--gold);color:#1a1206;padding:7px 14px;border-radius:8px;font-weight:600;text-decoration:none;font-size:13px}
+.step a.btn.sec{background:transparent;color:var(--gold);border:1px solid var(--gold)}
+.tools{padding:6px 6vw 0;display:flex;gap:10px;flex-wrap:wrap}
+.tools button{background:#221a33;color:var(--ink);border:1px solid var(--line);border-radius:8px;padding:9px 14px;cursor:pointer;font-size:13px}
+.tools button:hover{border-color:var(--gold);color:var(--gold)}
+.grid{padding:10px 6vw 60px;display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px}
+.dcard{background:var(--card);border:1px solid var(--line);border-radius:12px;overflow:hidden}
+.dcard img{width:100%;aspect-ratio:1;object-fit:cover;display:block}
+.dpad{padding:12px 14px}.dpad .dt{font-weight:600}.dpad .ds{color:var(--mut);font-size:12px;margin:4px 0 10px;min-height:32px}
+.row{display:flex;gap:6px;flex-wrap:wrap}
+.row button,.row a{font-size:11.5px;padding:5px 9px;border-radius:7px;border:1px solid var(--line);background:transparent;color:var(--ink);cursor:pointer;text-decoration:none}
+.row button:hover,.row a:hover{border-color:var(--gold);color:var(--gold)}
+.copied{color:var(--good)!important;border-color:var(--good)!important}
+.pill{font-size:11px;color:var(--gold);border:1px solid var(--gold);border-radius:99px;padding:2px 8px;display:inline-block;margin-bottom:8px}
+</style></head><body>
+<header><h1>Pattern Vault — <em>Manual Handoff</em></h1>
+<div class="sub">Everything that needs YOU (accounts + uploads). The designs, print masters, and paste-ready copy are done — this page hands them to you in one place.</div></header>
+
+<h2>① Accounts & signups (one-time)</h2>
+<div class="steps">
+ <div class="step"><div class="num">1</div><div><div class="t">Spoonflower — recommended first (you already have a login)</div>
+ <div class="d">Log in with your existing <b>info@designerwallcoverings.com</b> account → Sell → Upload. The 14 tiles are print-clean 4096px masters; use the copy below. ~10–15% commission, passive after upload.</div>
+ <a class="btn" href="https://www.spoonflower.com/login" target="_blank" rel="noopener">Open Spoonflower login →</a>
+ <a class="btn sec" href="https://www.spoonflower.com/designs/new" target="_blank" rel="noopener">Upload a design →</a></div></div>
+ <div class="step"><div class="num">2</div><div><div class="t">Etsy — own the customer (~$0.20/listing)</div>
+ <div class="d">Create/confirm a seller account, then paste each listing (copy below).</div>
+ <a class="btn" href="https://www.etsy.com/sell" target="_blank" rel="noopener">Etsy — start selling →</a></div></div>
+ <div class="step"><div class="num">3</div><div><div class="t">Stripe (AbramsEgo account) — to sell licenses on the Vault itself</div>
+ <div class="d">Create the dedicated AbramsEgo Stripe account, then paste me the <b>sk_test_</b> key (I verify a 4242 checkout) before any live key.</div>
+ <a class="btn" href="https://dashboard.stripe.com/register" target="_blank" rel="noopener">Register Stripe →</a></div></div>
+</div>
+
+<h2>② Affiliate programs (paste your links back to me)</h2>
+<div class="steps">
+ <div class="step"><div class="num">4</div><div><div class="t">Join any — then send me the referral URLs</div>
+ <div class="row" style="margin-top:8px">
+ <a href="https://www.digitalocean.com/referral-program" target="_blank" rel="noopener">DigitalOcean</a>
+ <a href="https://www.hetzner.com/referral-program" target="_blank" rel="noopener">Hetzner</a>
+ <a href="https://betterstack.com/affiliate" target="_blank" rel="noopener">Better Stack</a>
+ <a href="https://www.vultr.com/company/referral-program/" target="_blank" rel="noopener">Vultr</a>
+ <a href="https://elevenlabs.io/affiliates" target="_blank" rel="noopener">ElevenLabs</a></div></div></div>
+</div>
+
+<h2>③ The 14 designs — masters + paste-ready copy</h2>
+<div class="tools">
+ <button onclick="dlCSV()">⬇ Download all listings (CSV)</button>
+ <button onclick="dlAllMasters()">⬇ Open all 14 print masters</button>
+ <span style="color:var(--mut);font-size:12px;align-self:center">Click any field to copy it to your clipboard.</span>
+</div>
+<div class="grid" id="grid">loading…</div>
+
+<script>
+let ITEMS=[];
+const master=d=>'/assets/masters/'+d.img.split('/').pop().replace('.webp','')+'_4x.png';
+const tagsOf=d=>{const b=(d.styleLine||'').split('—')[0].trim().toLowerCase();
+ return [...b.split(' ').filter(w=>w.length>3).slice(0,4),'seamless pattern','wallpaper','original design','repeat pattern','surface design','print ready','removable wallpaper','peel and stick'].slice(0,13).join(', ')};
+const descOf=d=>(d.styleLine||'')+' Seam-verified seamless repeat, print-clean. Digital single-use license included; commercial + exclusive available. Original — never a copy.';
+function copy(el,txt){navigator.clipboard.writeText(txt).then(()=>{const o=el.textContent;el.textContent='✓ copied';el.classList.add('copied');setTimeout(()=>{el.textContent=o;el.classList.remove('copied')},1200)})}
+function render(){document.getElementById('grid').innerHTML=ITEMS.map((d,i)=>`
+ <div class="dcard"><img loading="lazy" src="${d.img}" alt="${d.title}">
+ <div class="dpad"><span class="pill">✦ #${i+1}</span><div class="dt">${d.title}</div><div class="ds">${d.styleLine||''}</div>
+ <div class="row">
+ <button onclick="copy(this,${JSON.stringify(d.title).replace(/"/g,'"')})">Copy title</button>
+ <button onclick='copy(this,DESC[${i}])'>Copy description</button>
+ <button onclick='copy(this,TAGS[${i}])'>Copy tags</button>
+ <a href="${master(d)}" download target="_blank">⬇ Master (4096px)</a>
+ </div></div></div>`).join('')}
+let DESC=[],TAGS=[];
+function dlCSV(){let rows=[['title','description','tags','digital_price','commercial_price','master']];
+ ITEMS.forEach(d=>{const t=d.licenseTiers||[];const dg=(t.find(x=>x.tier==='digital_single_use')||{}).priceUsd||149;const cm=(t.find(x=>x.tier==='commercial_print')||{}).priceUsd||495;
+ rows.push([d.title,descOf(d),tagsOf(d),dg,cm,master(d)])});
+ const csv=rows.map(r=>r.map(c=>'"'+String(c).replace(/"/g,'""')+'"').join(',')).join('\n');
+ const a=document.createElement('a');a.href=URL.createObjectURL(new Blob([csv],{type:'text/csv'}));a.download='pattern-vault-listings.csv';a.click()}
+function dlAllMasters(){ITEMS.forEach((d,i)=>setTimeout(()=>window.open(master(d),'_blank'),i*250))}
+(async()=>{const r=await fetch('/api/catalog');const j=await r.json();
+ ITEMS=(j.items||[]).filter(x=>x.category==='in-house-original');
+ DESC=ITEMS.map(descOf);TAGS=ITEMS.map(tagsOf);render()})();
+</script></body></html>
diff --git a/server.js b/server.js
index 81587f8..4261009 100644
--- a/server.js
+++ b/server.js
@@ -84,6 +84,7 @@ app.get('/api/trends', (req, res) => {
});
app.get('/trends', (req, res) => res.sendFile(path.join(__dirname, 'public', 'trends.html')));
+app.get('/handoff', (req, res) => res.sendFile(path.join(__dirname, 'public', 'handoff.html')));
app.get('/api/healthz', (req, res) => res.json({ ok: true, service: 'pattern-vault' }));
app.listen(PORT, () => console.log(`pattern-vault on :${PORT} · public=${PUBLIC_VAULT} · stripe=${stripe ? 'test' : 'none'}`));
← 831c5ad feat: trend board loop closed — mark 6/7 hot lanes FILLED by
·
back to Pattern Vault
·
chore(handoff): finalize brand — Fernwick Studio shop name + b92f4dc →