← back to Contact Mailer Ideas
viewer: score-based chip coloring (green=top/31+, neutral=strong/28-30, dim=weak/<28) + Tier filter (opportunities/top/weak) + pass-3 artifacts rendered as expandable sections in dialog
4dff97e1f940c1aae2aa5a7ed8aeb99273bc4b60 · 2026-05-11 14:32:57 -0700 · Steve Abrams
Files touched
A batches/2026-05-12.jsonM viewer/server.js
Diff
commit 4dff97e1f940c1aae2aa5a7ed8aeb99273bc4b60
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon May 11 14:32:57 2026 -0700
viewer: score-based chip coloring (green=top/31+, neutral=strong/28-30, dim=weak/<28) + Tier filter (opportunities/top/weak) + pass-3 artifacts rendered as expandable sections in dialog
---
batches/2026-05-12.json | 450 ++++++++++++++++++++++++++++++++++++++++++++++++
viewer/server.js | 90 ++++++++--
2 files changed, 529 insertions(+), 11 deletions(-)
diff --git a/batches/2026-05-12.json b/batches/2026-05-12.json
new file mode 100644
index 0000000..ff295fb
--- /dev/null
+++ b/batches/2026-05-12.json
@@ -0,0 +1,450 @@
+{
+ "batch": "contact-mailer-20260512",
+ "batch_date": "2026-05-12",
+ "vertical": "contact-mailer / email-marketing (Constant Contact-adjacent)",
+ "authority": "ideate-only",
+ "generated_at": "2026-05-11T13:10:08-07:00",
+ "generator": "claude-cli daily",
+ "ideas": [
+ {
+ "idx": 1,
+ "name": "ConsentLedger",
+ "angle": "Cryptographically signed opt-in receipts that travel with every contact between platforms.",
+ "target": "GDPR/CCPA-paranoid SaaS marketers migrating ESPs or fearing audits",
+ "diff": "Portable, tamper-evident consent provenance — not stored in any one ESP, survives platform churn and DPA disputes.",
+ "headline": "Consent You Can Actually Prove",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Show 10 privacy/legal counsels a sample signed receipt — measure willingness to require it in vendor RFPs"
+ },
+ {
+ "step": "MVP",
+ "what": "Hosted opt-in widget + signed JSON receipts + Zapier sink, $99/mo per 10k contacts"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "IAPP (privacy pro) Slack + r/gdpr + sponsor one Future of Privacy Forum newsletter"
+ }
+ ]
+ },
+ {
+ "idx": 2,
+ "name": "InboxSundial",
+ "angle": "Time-shifted newsletter that auto-delivers when the subscriber's circadian inbox-open window opens — not when you hit send.",
+ "target": "Solo newsletter operators with global readerships (Beehiiv/Substack escapees)",
+ "diff": "Per-subscriber chronotype detection from historical open-time vectors, not crude timezone bucketing.",
+ "headline": "Send Once. Arrive Right On Time.",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Cohort A/B test on 5 friendly newsletters: chronotype-shifted vs send-now opens"
+ },
+ {
+ "step": "MVP",
+ "what": "BYO-ESP middleware (sits in front of Postmark/Resend), $29/mo solo plan"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "Indie Hackers + Beehiiv Discord + DM 50 newsletter operators with personalized chronotype heatmaps"
+ }
+ ]
+ },
+ {
+ "idx": 3,
+ "name": "PressKitMailer",
+ "angle": "Newsletter platform exclusively for journalists pitching sources — every send is also a public pitch archive.",
+ "target": "Freelance investigative reporters, podcast producers, book authors with source networks",
+ "diff": "Built-in pitch tracker, source-confidentiality controls, and reply-anonymization shield — replaces 200 BCC emails.",
+ "headline": "Pitch The Room Without Burning It",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Interview 15 freelancers at MuckRack or in NICAR Slack about current pitch-list workflow"
+ },
+ {
+ "step": "MVP",
+ "what": "Pitch-blast tool with reply threading + source roster, $24/mo per reporter"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "NICAR conference table + sponsor Study Hall newsletter + cold to top 100 Substack journalists"
+ }
+ ]
+ },
+ {
+ "idx": 4,
+ "name": "TradeDeckly",
+ "angle": "Email campaigns rendered as paginated digital tearsheets sized to physical sample binders for trade showrooms.",
+ "target": "B2B trade industries with showrooms — wallcoverings, lighting, tile, kitchen/bath, textiles",
+ "diff": "Output is print-ready PDF + email + showroom QR card from a single send. Designed for designer-rep workflows.",
+ "headline": "One Send. Inbox, PDF, Showroom.",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Show mockups to 10 ASID interior designers and 5 DW-style showrooms — measure 'would pay' on tearsheet output"
+ },
+ {
+ "step": "MVP",
+ "what": "Template editor + PDF/email/QR triple-render pipeline, $149/mo per showroom"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "NYDC/PDC showroom walk-ins, IDS sponsorship, post in r/InteriorDesign trade subs"
+ }
+ ]
+ },
+ {
+ "idx": 5,
+ "name": "SilentSync",
+ "angle": "Zero-tracking newsletter platform — no opens, no clicks, no pixels, just signed delivery receipts.",
+ "target": "Privacy-first creators, security newsletters, journalists protecting reader identity",
+ "diff": "Marketing-tracking abolitionism as a product: subscribers see a 'no surveillance' badge that creators can show off.",
+ "headline": "Send. No Pixel. No Pixel. No Pixel.",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Land 20 paid pre-orders from privacy-newsletter readers via Hacker News post"
+ },
+ {
+ "step": "MVP",
+ "what": "SMTP + signed-receipt newsletter w/ no analytics, flat $19/mo solo, $99 team"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "HN Show, EFF community, sponsor Bruce Schneier and Tutanota newsletters"
+ }
+ ]
+ },
+ {
+ "idx": 6,
+ "name": "CongregationMail",
+ "angle": "Email + SMS + bulletin printer for churches, synagogues, mosques — sermons, prayer requests, and weekly bulletins from one queue.",
+ "target": "Small/mid US congregations (100–2k members), volunteer admins, no marketing dept",
+ "diff": "Liturgical-calendar aware scheduling, prayer-request privacy controls, paper-bulletin export — designed for non-marketer volunteers.",
+ "headline": "One Tool For The Whole Congregation",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Visit 10 local congregations, observe Friday/Sunday bulletin prep, time the current workflow"
+ },
+ {
+ "step": "MVP",
+ "what": "Web app + USPS bulletin print-on-demand integration, $39/mo per congregation"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "Denominational admin Facebook groups, Christianity Today ads, partner with one diocese for pilot"
+ }
+ ]
+ },
+ {
+ "idx": 7,
+ "name": "EstateMailer",
+ "angle": "Posthumous-trigger newsletter — drafts that send on death-verification, anniversary dates, or estate-trustee approval.",
+ "target": "End-of-life planners, estate attorneys, individuals doing legacy letters",
+ "diff": "Integrates with US Social Security death registry feeds and notarized trustee approvals — not a 'dead-man switch' toy.",
+ "headline": "The Last Send You'll Schedule",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Interview 20 estate attorneys about current 'letter to be opened upon death' workflow"
+ },
+ {
+ "step": "MVP",
+ "what": "Notarized-trustee dashboard + scheduled posthumous send, $199 one-time + $9/yr storage"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "Partner with Trust & Will + Cake.app; sponsor estate-planning podcasts"
+ }
+ ]
+ },
+ {
+ "idx": 8,
+ "name": "ReplyForest",
+ "angle": "Threaded newsletter — every send is a public branching conversation tree, not a one-way blast.",
+ "target": "Community-builders, intellectual newsletters, philosophy/economics writers",
+ "diff": "Replies become public threads under the original send, indexed and searchable — turns subscribers into a forum without leaving email.",
+ "headline": "Your Newsletter Becomes A Conversation",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Mock up thread UI, show to 30 readers of intellectual newsletters — measure 'would subscribe again' delta"
+ },
+ {
+ "step": "MVP",
+ "what": "Threaded mailer + public archive site, $29/mo creator plan with revenue-share on premium tier"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "Cold to top 50 Substack intellectuals; sponsor The Browser; post to r/SlateStarCodex"
+ }
+ ]
+ },
+ {
+ "idx": 9,
+ "name": "DripDeck",
+ "angle": "Cold-email cadences that adapt to LinkedIn job-change signals in real time, pausing or escalating per signal.",
+ "target": "B2B AEs and founders running founder-led sales",
+ "diff": "Signal-reactive sequences (not just time-based) — auto-pauses when recipient changes jobs, escalates when they get promoted.",
+ "headline": "Sequences That Read The Room",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Run 5 friendly AE pilots, measure reply-rate vs their existing Lemlist baseline"
+ },
+ {
+ "step": "MVP",
+ "what": "Cadence builder + LinkedIn Sales Nav webhook ingestion, $89/seat/mo"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "Pavilion + RevGenius + cold-outbound Twitter; lifetime deal on AppSumo for first 200"
+ }
+ ]
+ },
+ {
+ "idx": 10,
+ "name": "DocketDispatch",
+ "angle": "Law-firm newsletter that auto-personalizes per recipient's matter docket — every client sees only their relevant case updates.",
+ "target": "Mid-market law firms (10–200 attorneys) doing client communications",
+ "diff": "Reads from Clio/MyCase/PracticePanther matter data — newsletter ≠ marketing blast, it's a per-client matter digest.",
+ "headline": "Your Newsletter, Their Case File",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Demo to 10 Clio-using firms; measure 'pay $500/mo' commitment"
+ },
+ {
+ "step": "MVP",
+ "what": "Clio API integration + per-client matter digest, $499/mo per firm"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "Clio Cloud Conference booth, ABA TechShow, partner with Lawyerist podcast"
+ }
+ ]
+ },
+ {
+ "idx": 11,
+ "name": "FridgeMag",
+ "angle": "Newsletter platform that also prints a fridge-magnet recap once a quarter — the parts subscribers want to keep.",
+ "target": "Local-newsletter operators (city/neighborhood newsletters, school PTAs)",
+ "diff": "Hybrid digital + physical: AI extracts the 'evergreen' bits each quarter, prints a magnet, mails it. Premium-tier moat.",
+ "headline": "The Bits Worth Keeping On The Fridge",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Pre-sell quarterly magnet at $49/yr from 3 friendly local newsletters' lists"
+ },
+ {
+ "step": "MVP",
+ "what": "Web app + Stickermule print fulfillment + quarterly auto-curate, $19/mo creator + $39/yr reader add-on"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "6AM City partner pitch + DM 100 city-newsletter operators; PTA Facebook groups"
+ }
+ ]
+ },
+ {
+ "idx": 12,
+ "name": "ListMortgage",
+ "angle": "Get an instant cash advance against the future revenue of your email list.",
+ "target": "Indie newsletter creators with $5k–$50k/mo recurring sponsor revenue",
+ "diff": "Pipe-style revenue-based financing scoped to newsletter sponsor pipelines + Beehiiv/ConvertKit revenue data.",
+ "headline": "Your List Is Already Worth Cash",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Underwrite 5 newsletters manually, place 1 friendly $10k advance, track repayment for 6 weeks"
+ },
+ {
+ "step": "MVP",
+ "what": "OAuth-into-ESP + sponsor-rev underwriting + advance, 5–8% factor rate"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "Beehiiv Ambassadors + Newsletter Operator Slack + Twitter creator-finance threads"
+ }
+ ]
+ },
+ {
+ "idx": 13,
+ "name": "QuietHours",
+ "angle": "Behavioral-economics opt-in flow — subscribers pre-commit to specific weekly inbox windows; you can never send outside them.",
+ "target": "Anti-burnout newsletter creators, mental-wellness brands",
+ "diff": "Subscriber-defined send-windows enforced server-side. Brand promise: 'we literally can't spam you.'",
+ "headline": "Subscribers Set The Schedule",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Test opt-in flow on 5 wellness newsletter landing pages; measure conversion delta vs vanilla opt-in"
+ },
+ {
+ "step": "MVP",
+ "what": "Drop-in opt-in widget + scheduling enforcer middleware, $39/mo creator"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "Cold to Calm/Headspace-adjacent newsletter creators; sponsor Ness Labs"
+ }
+ ]
+ },
+ {
+ "idx": 14,
+ "name": "DraftRoom",
+ "angle": "Multiplayer Google-Docs-style newsletter composer with built-in editorial review queue and inline reader-rep comments.",
+ "target": "2–10 person newsletter teams (think Defector, Platformer-clone shops, nonprofit comms teams)",
+ "diff": "Editorial workflow native: drafts, suggestions, editor approval, fact-check sign-off — not just a publish button.",
+ "headline": "Edit Together. Send As One.",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Interview 15 newsletter editorial teams; identify their current Google-Docs-to-ESP pain"
+ },
+ {
+ "step": "MVP",
+ "what": "Live-collab editor + queue + 1-click send to existing ESP, $79/mo team of 5"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "Nieman Lab partnerships, Online News Association, cold to top 50 multi-author Substacks"
+ }
+ ]
+ },
+ {
+ "idx": 15,
+ "name": "QRCarbon",
+ "angle": "Direct-mail postcards with smart QR codes whose landing pages and tracking become a parallel email-style funnel.",
+ "target": "DTC brands hitting iOS-tracking walls who want a physical reacquisition channel",
+ "diff": "Each postcard QR = unique tracked landing page with attribution back to the recipient — postcard CRM, not just print fulfillment.",
+ "headline": "Postcards Your CRM Actually Sees",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Run 1k-piece test for 3 friendly DTC brands; measure response-rate vs email-only baseline"
+ },
+ {
+ "step": "MVP",
+ "what": "Shopify app + Lob fulfillment + unique-QR tracker, $0.85/piece + $99/mo"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "Shopify App Store + DTC Twitter + sponsor 2pm.fm newsletter"
+ }
+ ]
+ },
+ {
+ "idx": 16,
+ "name": "OpenSourceList",
+ "angle": "Self-hosted, open-source newsletter platform with one-click migration FROM Mailchimp/Beehiiv — the 'I want out' product.",
+ "target": "Devs, security newsletters, EU operators wanting EU-only data residency",
+ "diff": "MIT-licensed, $0 self-host or $39/mo managed; explicit export-everything-anytime promise.",
+ "headline": "The Newsletter Platform You Own",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Post the GitHub repo + roadmap; measure stars + waitlist signups in 30 days"
+ },
+ {
+ "step": "MVP",
+ "what": "Single-binary self-host + managed hosted tier, $39/mo or $0 self-host"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "HN launch, Awesome-Self-Hosted PR, sponsor Console.dev + Changelog podcast"
+ }
+ ]
+ },
+ {
+ "idx": 17,
+ "name": "GuildBlast",
+ "angle": "Newsletter network where vetted creators in the same vertical cross-promote in each others' sends with revenue split.",
+ "target": "Mid-size niche newsletters (5k–50k subs) plateaued on organic growth",
+ "diff": "Trust-graph powered — only invited creators in the same vertical, automatic placement + revenue tracking. Not SparkLoop-broad.",
+ "headline": "Grow Your List With Your Peers",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Curate 1 vertical (e.g. fintech) with 10 invited creators; track cross-list growth over 6 weeks"
+ },
+ {
+ "step": "MVP",
+ "what": "Marketplace + auto-insertion + rev-split escrow, 15% rake"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "Hand-curate first 3 verticals (fintech, design, parenting); referrals expand it"
+ }
+ ]
+ },
+ {
+ "idx": 18,
+ "name": "SponsorVault",
+ "angle": "Newsletter sponsorship marketplace with held-in-escrow performance guarantees — sponsors pay only on verified open/click metrics.",
+ "target": "Performance-marketing buyers and mid-size newsletter operators",
+ "diff": "Verified delivery metrics via DKIM-signed receipts + 3rd-party verification; escrow until SLA hit. Solves trust gap.",
+ "headline": "Newsletter Ads That Actually Settle",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Run 10 sponsor-newsletter matches manually with escrow; measure repeat-buyer rate"
+ },
+ {
+ "step": "MVP",
+ "what": "Marketplace + escrow + verified-metrics layer, 10% take rate"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "Cold to perf-marketing agencies + newsletter operators with sponsor inbound; sponsor Why We Buy"
+ }
+ ]
+ },
+ {
+ "idx": 19,
+ "name": "VoteSlate",
+ "angle": "Nonpartisan candidate / ballot-measure email platform for local civic groups, with built-in FEC + state-level compliance.",
+ "target": "Local civic orgs, candidate campaigns under $1M budget, ballot-measure committees",
+ "diff": "Compliance-as-product: handles disclaimer rules, donor disclosure thresholds, blackout windows automatically — Mailchimp won't.",
+ "headline": "Campaign Email Without The Compliance Lawyer",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Interview 15 local-race campaign managers, audit their current ESP + compliance workflow"
+ },
+ {
+ "step": "MVP",
+ "what": "Web app + auto-disclaimer + state compliance pack (start with CA, NY, TX), $199/campaign month"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "NDTC + Higher Ground Labs partnerships; cold to county-level civic groups"
+ }
+ ]
+ },
+ {
+ "idx": 20,
+ "name": "SignalSeed",
+ "angle": "Newsletter platform where every send doubles as a public RSS-style feed AND a Matrix/IRC broadcast — multi-protocol output.",
+ "target": "Open-protocol enthusiasts, security/crypto newsletters, dev-tool newsletters",
+ "diff": "One compose box → email + RSS + Matrix + ActivityPub + Bluesky thread; ESP isn't the protocol monopoly.",
+ "headline": "Write Once. Broadcast Everywhere.",
+ "action_items": [
+ {
+ "step": "VALIDATE",
+ "what": "Land 10 paying customers from one HN Show post offering Matrix + RSS + email mailer combo"
+ },
+ {
+ "step": "MVP",
+ "what": "Multi-protocol render-and-send service with ActivityPub + Matrix + email, $29/mo solo"
+ },
+ {
+ "step": "DISTRIBUTION",
+ "what": "HN, lobste.rs, Fediverse hashtags, sponsor Console.dev + Lobsters Show"
+ }
+ ]
+ }
+ ]
+}
diff --git a/viewer/server.js b/viewer/server.js
index c5336f8..37986ec 100644
--- a/viewer/server.js
+++ b/viewer/server.js
@@ -195,6 +195,16 @@ header a:hover{color:#fff;border-color:#fff}
.wrap{padding:18px 48px 32px;display:flex;flex-wrap:wrap;gap:10px;align-content:flex-start}
.chip{background:#13131a;border:1px solid #1f1f27;border-radius:10px;padding:10px 14px;cursor:pointer;display:inline-flex;align-items:center;gap:10px;transition:all .12s ease;position:relative;overflow:hidden;min-width:calc(var(--chip-min,160px))}
.chip:hover{border-color:#3a3a47;transform:translateY(-1px);background:#181820}
+.chip.tier-top{background:linear-gradient(135deg,rgba(34,197,94,.13),rgba(34,197,94,.04));border-color:rgba(34,197,94,.45);box-shadow:0 0 0 1px rgba(34,197,94,.08)}
+.chip.tier-top:hover{border-color:rgba(34,197,94,.7);background:linear-gradient(135deg,rgba(34,197,94,.18),rgba(34,197,94,.06))}
+.chip.tier-top .chip-name{color:#bbf7d0}
+.chip.tier-top .chip-score{background:rgba(34,197,94,.22);color:#86efac}
+.chip.tier-top::after{content:"●";position:absolute;top:6px;right:8px;color:#22c55e;font-size:8px;text-shadow:0 0 6px rgba(34,197,94,.6)}
+.chip.tier-strong{border-color:rgba(56,189,248,.35)}
+.chip.tier-strong .chip-name{color:#bae6fd}
+.chip.tier-weak{opacity:.55;border-color:#1c1c22}
+.chip.tier-weak .chip-name{color:#9a9aa2}
+.chip.tier-weak .chip-score{background:rgba(239,68,68,.12);color:#fca5a5}
.chip .chip-swatch{width:10px;height:10px;border-radius:50%;flex-shrink:0;box-shadow:0 0 0 2px rgba(255,255,255,.04)}
.chip .chip-name{font-weight:600;font-size:13.5px;color:#fff;letter-spacing:-0.005em}
.chip .chip-idx{font-size:10.5px;color:#6a6a72;letter-spacing:.1em;font-variant-numeric:tabular-nums}
@@ -294,6 +304,14 @@ footer{padding:20px 48px 40px;color:#5a5a62;font-size:12px;text-align:center}
<button data-mode="chips" id="modeChips">Chips</button>
<button data-mode="cards" id="modeCards">Cards</button>
</div>
+ <label>Tier
+ <select id="tier">
+ <option value="all">All ideas</option>
+ <option value="opps">Opportunities only (30+)</option>
+ <option value="top">🟢 Top tier (31+)</option>
+ <option value="weak">Weak (under 28)</option>
+ </select>
+ </label>
<div class="density">Density
<input type="range" id="density" min="120" max="600" step="20" value="160" />
<span id="densityLbl">160px</span>
@@ -316,6 +334,9 @@ const sortSel = document.getElementById('sort');
const density = document.getElementById('density');
const densityLbl = document.getElementById('densityLbl');
let viewMode = LS.getItem('cmi.mode') || 'chips';
+const tierSel = document.getElementById('tier');
+tierSel.value = LS.getItem('cmi.tier') || 'all';
+tierSel.addEventListener('change', () => { LS.setItem('cmi.tier', tierSel.value); render(); });
q.value = LS.getItem('cmi.q') || '';
sortSel.value = LS.getItem('cmi.sort') || 'idx';
const dVal = LS.getItem('cmi.density') || (viewMode === 'chips' ? '160' : '360');
@@ -407,9 +428,20 @@ function render(){
return hay.includes(query);
});
}
- // Sort
- const totalScore = i => { const s = i.ticks[0] && i.ticks[0].scores; return s ? (s.novelty||0)+(s.fit||0)+(s.feas||0)+(s.impact||0) : -1; };
- const axisScore = (i,axis) => { const s = i.ticks[0] && i.ticks[0].scores; return s ? (s[axis]||0) : -1; };
+ // Resolve latest-known score from any tick that has a scores block (pass-1 or pass-2 or pass-4 rescore)
+ const latestScored = i => {
+ const sorted = (i.ticks||[]).slice().sort((a,b) => (b.tick||0) - (a.tick||0));
+ for (const t of sorted) if (t.scores) return t;
+ return null;
+ };
+ const totalScore = i => { const t = latestScored(i); if (!t) return -1; const s = t.scores; return (s.novelty||0)+(s.fit||0)+(s.feas||0)+(s.impact||0); };
+ const axisScore = (i,axis) => { const t = latestScored(i); return t ? (t.scores[axis]||0) : -1; };
+ // Tier filter
+ switch (tierSel.value){
+ case 'opps': ideas = ideas.filter(i => totalScore(i) >= 30); break;
+ case 'top': ideas = ideas.filter(i => totalScore(i) >= 31); break;
+ case 'weak': ideas = ideas.filter(i => { const s = totalScore(i); return s > 0 && s < 28; }); break;
+ }
switch (sortSel.value){
case 'name': ideas.sort((a,b)=>a.name.localeCompare(b.name)); break;
case 'score-desc': ideas.sort((a,b)=>totalScore(b)-totalScore(a) || a.idx-b.idx); break;
@@ -438,18 +470,30 @@ function lastVerdictForIdea(i){
}
function renderChip(i, query){
const c = colorFor(i.name);
- const last = i.ticks[0];
- const scores = last && last.scores;
+ const scoredTick = (function(){
+ for (const t of (i.ticks||[]).slice().sort((a,b)=>(b.tick||0)-(a.tick||0))) if (t.scores) return t;
+ return null;
+ })();
+ const scores = scoredTick && scoredTick.scores;
const total = scores ? (scores.novelty||0)+(scores.fit||0)+(scores.feas||0)+(scores.impact||0) : null;
const verdict = lastVerdictForIdea(i);
const hasNote = state.notes && state.notes[i.idx];
+ // Use display name from latest tick if renamed (e.g., FormForward → CompliantSend)
+ const displayName = (scoredTick && scoredTick.name && scoredTick.name !== i.name) ? scoredTick.name : i.name;
+ // Tier coloring
+ let tier = '';
+ if (total != null){
+ if (total >= 31) tier = 'tier-top';
+ else if (total >= 28) tier = 'tier-strong';
+ else tier = 'tier-weak';
+ }
const chip = document.createElement('div');
- chip.className = 'chip';
+ chip.className = 'chip ' + tier;
chip.dataset.idx = i.idx;
chip.innerHTML = \`
<span class="chip-swatch" style="background:linear-gradient(135deg,\${c.c},\${c.a})"></span>
<span class="chip-idx">#\${String(i.idx).padStart(2,'0')}</span>
- <span class="chip-name">\${highlight(i.name, query)}</span>
+ <span class="chip-name">\${highlight(displayName, query)}</span>
\${verdict ? '<span class="chip-mark ' + verdict + '">' + verdict + '</span>' : ''}
\${total != null ? '<span class="chip-score">' + total + '/40</span>' : ''}
\${hasNote ? '<span class="chip-note">●</span>' : ''}
@@ -484,15 +528,27 @@ function renderCard(i, query){
function openDialog(idx){
const i = state.byIdx[idx];
if (!i) return;
- const last = i.ticks[0];
- const headline = (last && last.new_headline) || (last && last.replacement && last.replacement.headline) || i.headline || '—';
+ const sortedTicks = (i.ticks||[]).slice().sort((a,b)=>(b.tick||0)-(a.tick||0));
+ const scoredTick = sortedTicks.find(t => t.scores) || null;
+ const lastHeadlineTick = sortedTicks.find(t => t.new_headline) || null;
+ const headline = (lastHeadlineTick && lastHeadlineTick.new_headline) || (sortedTicks[0] && sortedTicks[0].replacement && sortedTicks[0].replacement.headline) || i.headline || '—';
+ const displayName = (scoredTick && scoredTick.name && scoredTick.name !== i.name) ? scoredTick.name : i.name;
const c = colorFor(i.name);
- const scores = last && last.scores ? last.scores : null;
+ const scores = scoredTick ? scoredTick.scores : null;
+ const total = scores ? (scores.novelty||0)+(scores.fit||0)+(scores.feas||0)+(scores.impact||0) : null;
+ let tierBadge = '';
+ if (total != null){
+ if (total >= 31) tierBadge = '<span style="background:rgba(34,197,94,.18);color:#86efac;padding:3px 10px;border-radius:4px;font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;margin-left:10px">Top opportunity · '+total+'/40</span>';
+ else if (total >= 28) tierBadge = '<span style="background:rgba(56,189,248,.15);color:#7dd3fc;padding:3px 10px;border-radius:4px;font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;margin-left:10px">Strong · '+total+'/40</span>';
+ else tierBadge = '<span style="background:rgba(239,68,68,.12);color:#fca5a5;padding:3px 10px;border-radius:4px;font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;margin-left:10px">Weak · '+total+'/40</span>';
+ }
+ // Find pass-3+ artifacts
+ const artifacts = sortedTicks.filter(t => t.artifact && t.artifact_type).reverse();
const inner = document.getElementById('dlgInner');
inner.innerHTML = \`
<div style="height:6px;border-radius:3px;background:linear-gradient(90deg,\${c.c},\${c.a});margin-bottom:18px"></div>
<div class="idx">IDEA #\${String(i.idx).padStart(2,'0')}</div>
- <h2 style="font-size:24px;font-weight:700;margin:4px 0 16px">\${escapeHtml(i.name)}</h2>
+ <h2 style="font-size:24px;font-weight:700;margin:4px 0 16px;display:flex;align-items:center;flex-wrap:wrap">\${escapeHtml(displayName)}\${tierBadge}</h2>
<div class="headline">\${escapeHtml(headline === '—' ? i.angle : headline)}</div>
<div class="sub">\${escapeHtml(i.diff || '')}</div>
<div class="diff"><b style="color:#cfcfd5">target</b> · \${escapeHtml(i.target || '')}</div>
@@ -500,12 +556,24 @@ function openDialog(idx){
\${renderRowActions(i)}
\${renderNote(i)}
\${i.action_items && i.action_items.length ? renderActions(i.action_items, '') : ''}
+ \${artifacts.length ? renderArtifacts(artifacts) : ''}
\${i.ticks.length ? renderTicks(i.ticks, '') : '<div class="tick" style="color:#5a5a62">no refinement ticks yet</div>'}
<a class="figma-link" href="https://www.figma.com/design/1hHZQ0xggOqzVuPIB0rD3r?node-id=\${(i.node_id||'').replace(':','-')}" target="_blank">View frame in Figma ↗</a>
\`;
document.getElementById('ideaDialog').showModal();
bindCardEvents();
}
+function renderArtifacts(arts){
+ const labels = { pitch:'🎤 Investor pitch', coldlines:'📧 Cold-email openers', competitors:'🥊 Competitor matrix', spike:'🧱 MVP architecture', pricing:'💰 Pricing' };
+ return '<div style="margin-top:20px;border-top:1px solid #1f1f27;padding-top:18px">' +
+ '<h3 style="font-size:11px;letter-spacing:.15em;color:#7a7a82;margin-bottom:14px;text-transform:uppercase">Opportunity assets</h3>' +
+ arts.map(a => \`
+ <details style="background:#0d0d12;border:1px solid #1c1c22;border-radius:8px;padding:12px 14px;margin-bottom:10px" \${arts.indexOf(a) === 0 ? 'open' : ''}>
+ <summary style="cursor:pointer;font-size:13px;font-weight:600;color:#cfcfd5;outline:none">\${labels[a.artifact_type] || a.artifact_type} <span style="color:#5a5a62;font-weight:400;font-size:11px;margin-left:8px">tick \${a.tick}</span></summary>
+ <pre style="white-space:pre-wrap;font-family:'SF Mono',Menlo,monospace;font-size:12.5px;line-height:1.55;color:#bcbcc4;margin-top:12px">\${escapeHtml(a.artifact)}</pre>
+ </details>
+ \`).join('') + '</div>';
+}
window.openIdeaDialog = openDialog;
function renderRowActions(i){
return \`<div class="row-actions">
← 946b12a viewer: preserve scrollY across re-renders, signature-gated
·
back to Contact Mailer Ideas
·
pulse-pattern batch: 20 PULSE-flavored end-user advocacy/eng 1f2646c →