NP@naman-parashar-s-team / Startup idea validatorPublic
Startup idea validator
A JS snippet on QuikRun, callable at quik.run/r/idea-001.
JS· node:20· 3 runs· 0 forks· updated 13h ago
Startup idea validator.js
// idea-001 — Startup Idea Validator (pure SSR prototype, no client JS, no fetch)const BRAND = {bg: "#0b0b0c",surface: "#111214",border: "#23252a",text: "#eaeaea",muted: "#6b6f76",accent: "#E6FF55",};const DIMENSIONS = [{ key: "market", label: "Market Pull", blurb: "How badly does the world already want this?" },{ key: "moat", label: "Defensibility", blurb: "What stops a weekend clone from eating you?" },{ key: "clarity", label: "Pitch Clarity", blurb: "Can a stranger repeat it back in one breath?" },{ key: "timing", label: "Timing", blurb: "Why now and not in 2019 or 2031?" },{ key: "wedge", label: "Wedge", blurb: "The tiny painful thing you win first." },{ key: "gtm", label: "Go-To-Market", blurb: "A channel that scales without a sales army." },];const VERDICTS = [{ min: 88, tag: "FUNDABLE", line: "This one has legs. Ship the waitlist tonight." },{ min: 74, tag: "PROMISING", line: "Sharp idea. Cut one feature and it sings." },{ min: 58, tag: "NEEDS A WEDGE", line: "Good bones, fuzzy entry point. Narrow it." },{ min: 40, tag: "TOO EARLY", line: "Interesting, but the market is still asleep." },{ min: 0, tag: "BACK TO THE WHITEBOARD", line: "Charming. Also currently a hobby, not a company." },];const SAMPLE_IDEAS = ["AI that turns your gym selfies into a personalized meal plan","A marketplace for renting houseplants by the season","Carbon-negative concrete for backyard patios","Slack but every message self-destructs after you read it","On-demand notary that shows up on a scooter","A dating app that only matches people during power outages",];// deterministic hash so the same idea always scores the samefunction hashString(str) {let h = 2166136261;for (let i = 0; i < str.length; i++) {h ^= str.charCodeAt(i);h = Math.imul(h, 16777619);}return h >>> 0;}function seeded(seed) {let s = seed >>> 0 || 1;return function () {s ^= s << 13; s >>>= 0;s ^= s >> 17;s ^= s << 5; s >>>= 0;return s / 4294967296;};}function esc(v) {return String(v ?? "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");}function clampScore(n) {return Math.max(28, Math.min(97, Math.round(n)));}function scoreIdea(idea) {const seed = hashString(idea.toLowerCase().trim() || "empty");const rand = seeded(seed);const words = idea.trim().split(/\s+/).filter(Boolean);const hasAI = /\b(ai|ml|agent|gpt|llm|model)\b/i.test(idea);const hasMarket = /\b(market|marketplace|platform|network|community)\b/i.test(idea);const tooLong = words.length > 14;const tooShort = words.length < 3;const dims = DIMENSIONS.map((d) => {let base = 42 + rand() * 52;if (d.key === "market" && hasMarket) base += 10;if (d.key === "moat" && hasAI) base -= 8;if (d.key === "clarity" && tooLong) base -= 14;if (d.key === "clarity" && tooShort) base -= 10;if (d.key === "timing" && hasAI) base += 9;return { ...d, score: clampScore(base) };});const overall = Math.round(dims.reduce((a, d) => a + d.score, 0) / dims.length);const verdict = VERDICTS.find((v) => overall >= v.min) || VERDICTS[VERDICTS.length - 1];const waitlist = 300 + (seed % 8700);const investors = 3 + (seed % 14);const runwayWeeks = 6 + (seed % 20);return { dims, overall, verdict, waitlist, investors, runwayWeeks };}function barColor(score) {if (score >= 75) return "#E6FF55";if (score >= 55) return "#c5df6a";return "#7d8270";}function ring(overall) {const r = 54;const c = 2 * Math.PI * r;const filled = c * (overall / 100);return { r, c, filled };}function page(idea, data) {const { dims, overall, verdict, waitlist, investors, runwayWeeks } = data;const ringData = ring(overall);const safeIdea = esc(idea);const rows = dims.map((d, i) => {const delay = 0.2 + i * 0.08;const color = barColor(d.score);const idx = String(i + 1).padStart(2, "0");return `<div class="row"><div class="row-head"><span class="row-label"><em class="row-idx">${idx}</em>${esc(d.label)}</span><span class="row-score" style="color:${color}">${d.score}</span></div><div class="track"><div class="fill" style="--w:${d.score}%;--c:${color};--d:${delay}s"></div></div><div class="row-blurb">${esc(d.blurb)}</div></div>`;}).join("");const chips = SAMPLE_IDEAS.map((s) => `<a class="chip" href="/?idea=${encodeURIComponent(s)}">${esc(s)}</a>`).join("");return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><meta name="color-scheme" content="dark" /><title>Idea Validator · ${safeIdea}</title><style>:root{--bg:${BRAND.bg}; --surface:${BRAND.surface}; --border:${BRAND.border};--text:${BRAND.text}; --muted:${BRAND.muted}; --accent:${BRAND.accent};--mono:ui-monospace,"SF Mono",Menlo,Consolas,monospace;}*{box-sizing:border-box}html{-webkit-text-size-adjust:100%}body{margin:0;background:radial-gradient(1100px 620px at 82% -12%, rgba(230,255,85,.10), transparent 58%),radial-gradient(820px 560px at -8% 108%, rgba(230,255,85,.05), transparent 56%),var(--bg);color:var(--text);font-family:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;line-height:1.5;min-height:100vh;}.wrap{max-width:940px;margin:0 auto;padding:clamp(40px,7vw,72px) 22px 88px}/* ---- hero ---- */.kicker{font-family:var(--mono);font-size:11px;letter-spacing:.24em;text-transform:uppercase;color:var(--accent);display:inline-flex;align-items:center;gap:9px;border:1px solid rgba(230,255,85,.24);background:rgba(230,255,85,.05);padding:6px 12px;border-radius:999px;}.kicker .dot{width:6px;height:6px;border-radius:50%;background:var(--accent);box-shadow:0 0 10px 1px rgba(230,255,85,.75)}h1{font-size:clamp(32px,6vw,52px);line-height:1.02;margin:20px 0 12px;font-weight:700;letter-spacing:-.03em}h1 .hl{color:var(--accent);position:relative}.sub{color:var(--muted);font-size:clamp(14px,1.6vw,16px);max-width:580px;margin:0}/* ---- form ---- */form{margin:30px 0 0}.field{display:flex;gap:10px;background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:8px 8px 8px 18px;align-items:center;box-shadow:0 1px 0 rgba(255,255,255,.03) inset, 0 20px 50px -34px rgba(0,0,0,.9);transition:border-color .18s ease, box-shadow .18s ease;}.field:focus-within{border-color:rgba(230,255,85,.55);box-shadow:0 0 0 3px rgba(230,255,85,.12), 0 20px 50px -34px rgba(0,0,0,.9)}.field .glyph{color:var(--muted);flex:0 0 auto;display:flex}.field input{flex:1;background:transparent;border:0;outline:0;color:var(--text);font-size:15px;font-family:inherit;min-width:0;padding:6px 0;}.field input::placeholder{color:#4c5057}.field button{border:0;border-radius:11px;padding:12px 20px;cursor:pointer;background:var(--accent);color:#0b0b0c;font-weight:650;font-size:14px;font-family:inherit;white-space:nowrap;letter-spacing:.01em;transition:transform .08s ease, box-shadow .18s ease;box-shadow:0 0 0 0 rgba(230,255,85,0);}.field button:hover{box-shadow:0 8px 24px -10px rgba(230,255,85,.55)}.field button:active{transform:translateY(1px)}.chips{display:flex;flex-wrap:wrap;gap:8px;margin-top:16px}.chips-label{font-family:var(--mono);font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:#4c5057;align-self:center;margin-right:2px}.chip{font-size:12px;color:var(--muted);text-decoration:none;border:1px solid var(--border);background:rgba(255,255,255,.018);padding:7px 12px;border-radius:999px;transition:all .15s ease;}.chip:hover{color:var(--text);border-color:rgba(230,255,85,.4);background:rgba(230,255,85,.06)}/* ---- result card ---- */.card{margin-top:38px;background:linear-gradient(180deg, rgba(255,255,255,.02), transparent 30%),var(--surface);border:1px solid var(--border);border-radius:24px;padding:clamp(20px,3.4vw,30px);box-shadow:0 30px 80px -40px rgba(0,0,0,.95);position:relative;overflow:hidden;}.card::before{content:"";position:absolute;inset:0;pointer-events:none;background:radial-gradient(420px 220px at 88% 0%, rgba(230,255,85,.06), transparent 70%)}.verdict{display:flex;gap:clamp(20px,4vw,32px);align-items:center;flex-wrap:wrap;padding-bottom:26px;border-bottom:1px solid var(--border);position:relative;}.gauge{position:relative;width:140px;height:140px;flex:0 0 auto}.gauge svg{transform:rotate(-90deg)}.gauge .num{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center}.gauge .num b{font-size:42px;font-weight:700;font-family:var(--mono);font-variant-numeric:tabular-nums;letter-spacing:-.03em;line-height:1;color:var(--text)}.gauge .num small{font-size:10px;color:var(--muted);letter-spacing:.2em;text-transform:uppercase;margin-top:7px}.verdict-text{flex:1;min-width:230px}.tag{display:inline-flex;align-items:center;gap:7px;font-family:var(--mono);font-size:11px;letter-spacing:.18em;font-weight:600;color:var(--accent);border:1px solid rgba(230,255,85,.42);background:rgba(230,255,85,.08);padding:6px 11px;border-radius:8px}.tag .spark{width:5px;height:5px;border-radius:50%;background:var(--accent);box-shadow:0 0 8px 1px rgba(230,255,85,.7)}.verdict-text p{margin:15px 0 0;font-size:clamp(18px,2.4vw,21px);line-height:1.32;letter-spacing:-.015em;color:var(--text)}.idea-echo{margin-top:12px;color:var(--muted);font-size:13px;font-style:italic;padding-left:12px;border-left:2px solid var(--border);line-height:1.45}/* ---- dimension rows ---- */.rows{margin-top:28px;display:grid;gap:20px}.row-head{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:9px}.row-label{font-size:14px;font-weight:560;display:flex;align-items:baseline;gap:10px}.row-idx{font-family:var(--mono);font-style:normal;font-size:11px;color:#4c5057;letter-spacing:.05em}.row-score{font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:15px;font-weight:600}.track{height:9px;border-radius:999px;background:#17181b;overflow:hidden;border:1px solid rgba(255,255,255,.035)}.fill{height:100%;width:0;border-radius:999px;background:var(--c);box-shadow:0 0 14px -2px var(--c);animation:grow 1.1s cubic-bezier(.16,.84,.3,1) forwards;animation-delay:var(--d)}@keyframes grow{to{width:var(--w)}}.row-blurb{margin-top:8px;font-size:12.5px;color:var(--muted);line-height:1.45}/* ---- stats ---- */.stats{margin-top:30px;display:grid;gap:12px;grid-template-columns:repeat(3,1fr)}.stat{background:rgba(255,255,255,.018);border:1px solid var(--border);border-radius:15px;padding:16px 17px}.stat b{display:block;font-size:24px;font-weight:700;font-family:var(--mono);font-variant-numeric:tabular-nums;letter-spacing:-.02em;line-height:1}.stat span{font-size:10px;color:var(--muted);letter-spacing:.14em;text-transform:uppercase;margin-top:9px;display:block}.stat.live b{color:var(--accent)}.live-dot{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--accent);margin-right:8px;vertical-align:middle;animation:pulse 1.6s ease-in-out infinite;box-shadow:0 0 8px 1px rgba(230,255,85,.7)}@keyframes pulse{0%,100%{opacity:1}50%{opacity:.32}}footer{margin-top:44px;color:var(--muted);font-size:12px;text-align:center;font-family:var(--mono);letter-spacing:.05em}footer .q{color:var(--accent);font-weight:700}footer .sep{opacity:.4;margin:0 8px}@media (prefers-reduced-motion: reduce){.fill{animation:none;width:var(--w)}.live-dot{animation:none}circle[stroke-dashoffset]{animation:none !important}}@media(max-width:560px){.stats{grid-template-columns:1fr}.field{flex-direction:column;align-items:stretch;padding:14px}.field .glyph{display:none}.field input{padding:2px 0}.field button{width:100%;padding:14px}.verdict{justify-content:center;text-align:center;flex-direction:column}.idea-echo{text-align:left}}</style></head><body><div class="wrap"><span class="kicker"><span class="dot"></span> Prototype · Idea Validator</span><h1>Prove the idea<br/>before you open an <span class="hl">IDE</span>.</h1><p class="sub">Paste a startup idea. Get a brutally quick read across six dimensions,an investor-flavored verdict, and pretend momentum you have not earned yet.</p><form method="get" action="/"><div class="field"><span class="glyph" aria-hidden="true"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a5 5 0 0 0-3 9c.6.5 1 1.2 1 2v1h4v-1c0-.8.4-1.5 1-2a5 5 0 0 0-3-9Z"/><path d="M10 19h4M10.5 22h3"/></svg></span><input name="idea" value="${safeIdea}" placeholder="e.g. AI that turns whiteboard photos into working prototypes" autofocus autocomplete="off" /><button type="submit">Validate</button></div></form><div class="chips"><span class="chips-label">try</span>${chips}</div><div class="card"><div class="verdict"><div class="gauge"><svg width="140" height="140" viewBox="0 0 140 140"><circle cx="70" cy="70" r="${ringData.r}" fill="none" stroke="#1c1d21" stroke-width="11" /><circle cx="70" cy="70" r="${ringData.r}" fill="none"stroke="${BRAND.accent}" stroke-width="11" stroke-linecap="round"stroke-dasharray="${ringData.c.toFixed(1)}"stroke-dashoffset="${ringData.c.toFixed(1)}"style="filter:drop-shadow(0 0 7px rgba(230,255,85,.55));animation:ringfill 1.4s cubic-bezier(.16,.84,.3,1) .25s forwards" /></svg><div class="num"><b>${overall}</b><small>Score</small></div></div><div class="verdict-text"><span class="tag"><span class="spark"></span>${esc(verdict.tag)}</span><p>${esc(verdict.line)}</p><div class="idea-echo">“${safeIdea}”</div></div></div><div class="rows">${rows}</div><div class="stats"><div class="stat live"><b><span class="live-dot"></span>${waitlist.toLocaleString("en-US")}</b><span>Waitlist (imaginary)</span></div><div class="stat"><b>${investors}</b><span>Warm intros</span></div><div class="stat"><b>${runwayWeeks}w</b><span>Vibe runway</span></div></div></div><footer>Rendered on the edge by <span class="q">q.</span><span class="sep">·</span>QuikRun<span class="sep">·</span>no real VCs were harmed</footer></div><style>@keyframes ringfill{to{stroke-dashoffset:${(ringData.c - ringData.filled).toFixed(1)}}}</style></body></html>`;}export default async function handler(req, env) {const raw = (req.query && req.query.idea) || "";const idea =String(raw).trim() ||SAMPLE_IDEAS[Math.floor(Date.now() / 3600000) % SAMPLE_IDEAS.length];const data = scoreIdea(idea);const html = page(idea, data);return new Response(html, {status: 200,headers: { "content-type": "text/html; charset=utf-8" },});}
Fork this and make it yours
Describe a change in plain English and QuikRun deploys it to your own URL.