← back to Norma Sdcc Pitch
add: 'Why custom CRM?' section — head-to-head vs Salesforce/EveryAction/HubSpot across 8 axes + hybrid play + honest risks + 'the real question'
16362c021395b8043ed3785ac9901723a4be42e0 · 2026-05-20 08:33:54 -0700 · SteveStudio2
Files touched
M data/features.jsonM public/app.jsM public/index.htmlM public/styles.css
Diff
commit 16362c021395b8043ed3785ac9901723a4be42e0
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Wed May 20 08:33:54 2026 -0700
add: 'Why custom CRM?' section — head-to-head vs Salesforce/EveryAction/HubSpot across 8 axes + hybrid play + honest risks + 'the real question'
---
data/features.json | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
public/app.js | 31 +++++++++++++++++++++
public/index.html | 26 ++++++++++++++++++
public/styles.css | 28 +++++++++++++++++++
4 files changed, 166 insertions(+)
diff --git a/data/features.json b/data/features.json
index d285b2a..851e6b7 100644
--- a/data/features.json
+++ b/data/features.json
@@ -123,6 +123,87 @@
]
},
+ "why_custom_crm": {
+ "headline": "Why build a custom CRM instead of using Salesforce / EveryAction / HubSpot?",
+ "intro": "The most common objection. Real answer: off-the-shelf CRMs were built for sales teams, not borrower advocacy. SDCC's work doesn't fit their schema, and the AI era makes that mismatch worse.",
+ "the_short_answer": "Off-the-shelf CRMs model 'leads → opportunities → closed deals.' SDCC models 'borrower in crisis → story tagged → policy lever → press hit → coalition activation.' Same word ('CRM'), totally different shape. Forcing SDCC into a sales schema costs more, slows the team, and locks the data away.",
+ "comparison": [
+ {
+ "axis": "Cost (5 staff, year 1)",
+ "salesforce": "$2,400-$9,000 + Einstein add-on ($1,000+/mo) + implementation consultant ($30k)",
+ "everyaction": "$5k-$25k base + per-user + onboarding",
+ "hubspot": "$1,200-$14,400 + AI add-ons",
+ "norma": "$2,400-$9,600/year all-in (incl. AI inference + hosting)",
+ "winner": "norma"
+ },
+ {
+ "axis": "AI native",
+ "salesforce": "Einstein is bolted on. Doesn't know SDCC's voice. Extra cost.",
+ "everyaction": "Limited AI; mostly templated automations.",
+ "hubspot": "Breeze AI exists but is generic marketing-flavored.",
+ "norma": "AI is the substrate. Claude is the model. SDCC's voice library + KB are first-class.",
+ "winner": "norma"
+ },
+ {
+ "axis": "Data ownership",
+ "salesforce": "Your data lives in their schema. Export costs extra. Lock-in is the business model.",
+ "everyaction": "Better than Salesforce on export, still proprietary.",
+ "hubspot": "Lock-in by integration sprawl.",
+ "norma": "Postgres on SDCC's tenancy. Daily SQL dump exportable. Standard formats (CSV/JSON) on day 1.",
+ "winner": "norma"
+ },
+ {
+ "axis": "Borrower-story modeling",
+ "salesforce": "Force-fit into 'Contact' or 'Lead' object. No native debt-type/urgency/anonymizability fields.",
+ "everyaction": "Closer fit (designed for advocacy) but still record-based, not story-based.",
+ "hubspot": "Generic contact records. Definitely not modeled for this.",
+ "norma": "BorrowerStory is a first-class object. Structured by debt type, urgency, program, anonymizability, consent status.",
+ "winner": "norma"
+ },
+ {
+ "axis": "Speed of change",
+ "salesforce": "Workflow change = consultant + months + committee.",
+ "everyaction": "Faster than Salesforce, still gated by their roadmap.",
+ "hubspot": "Marketing automation strong; nonprofit-specific changes slow.",
+ "norma": "Workflow change = code commit. Hours, not months.",
+ "winner": "norma"
+ },
+ {
+ "axis": "Community + ecosystem",
+ "salesforce": "Huge consultant + admin community. Lots of tutorials. Industry standard.",
+ "everyaction": "Strong nonprofit community.",
+ "hubspot": "Massive community.",
+ "norma": "Tiny community (it's bespoke). Documentation lives in this app + Steve's brain.",
+ "winner": "off-the-shelf"
+ },
+ {
+ "axis": "Donations + bulk email",
+ "salesforce": "Strong with Pardot/Marketing Cloud, expensive.",
+ "everyaction": "BEST in class for nonprofit donations + bulk email.",
+ "hubspot": "Marketing-first, donation tooling weaker.",
+ "norma": "Not focus. Should integrate with EveryAction or ActBlue, not replace.",
+ "winner": "everyaction"
+ },
+ {
+ "axis": "Compliance + audit",
+ "salesforce": "Strong audit trail, expensive to access.",
+ "everyaction": "Adequate for nonprofit compliance.",
+ "hubspot": "Marketing-grade audit.",
+ "norma": "Append-only audit log. Every AI decision logged with model + prompt + actor + timestamp. Regulator-ready.",
+ "winner": "norma"
+ }
+ ],
+ "the_hybrid_play": "Don't replace EveryAction. SDCC keeps EveryAction for donations + bulk email (where it's actually best in class). Norma owns the advocacy-specific work no off-the-shelf tool handles — borrower stories, press response, coalition map, AI drafting, inbox triage. Two tools, clear boundaries, no overlap.",
+ "honest_risks": [
+ "Maintenance burden — SDCC needs a technical owner (Steve, initially). Off-the-shelf is more turnkey.",
+ "If the technical owner leaves, continuity risk. Mitigation: full Postgres + code + voice library export at any time, plus 30-day handoff clause.",
+ "No huge user community asking 'how do I do X'. Mitigation: Norma's interface mirrors Gmail patterns, so staff onboarding is faster than enterprise CRMs anyway.",
+ "Standard integrations (Mailchimp, ActBlue, Slack) need to be built per request — not pre-configured.",
+ "Bus factor — Norma's success depends on a small team's ongoing maintenance. The off-the-shelf path has 'somebody else's problem' built in."
+ ],
+ "the_real_question": "The real question isn't 'Salesforce vs Norma.' It's 'do we want our CRM to fit our work, or do we want to bend our work to fit a CRM?' Salesforce works for sales teams because its schema matches their work. SDCC's schema doesn't match any off-the-shelf product. Either bend the work or build the tool."
+ },
+
"wix_integration": {
"headline": "Wix ↔ Norma — concrete integration guide",
"intro": "Wix is fine. It's where SDCC's site lives. Here's exactly how to make it work with Norma's data + workflows.",
diff --git a/public/app.js b/public/app.js
index ecdca5f..6c71aba 100644
--- a/public/app.js
+++ b/public/app.js
@@ -52,6 +52,7 @@
renderPriorities();
renderObjections();
renderShiftTable();
+ renderCrmCompare();
renderWixOptions();
renderWixPrinciples();
renderAgents();
@@ -604,6 +605,36 @@ ${items.map((a) => `
`).join('');
}
+ /* ─────────────── CRM compare table ─────────────── */
+ function renderCrmCompare() {
+ const w = state.data.why_custom_crm;
+ if (!w) return;
+ const t = document.getElementById('crm-compare');
+ if (!t) return;
+ t.innerHTML = `
+ <thead>
+ <tr>
+ <th>Axis</th>
+ <th>Salesforce</th>
+ <th>EveryAction</th>
+ <th>HubSpot</th>
+ <th class="norma-col">Norma</th>
+ </tr>
+ </thead>
+ <tbody>
+ ${w.comparison.map((row) => `
+ <tr>
+ <td class="axis">${escapeHtml(row.axis)}</td>
+ <td class="${row.winner === 'salesforce' ? 'win' : ''}">${escapeHtml(row.salesforce)}</td>
+ <td class="${row.winner === 'everyaction' ? 'win' : ''}">${escapeHtml(row.everyaction)}</td>
+ <td class="${row.winner === 'hubspot' ? 'win' : ''}">${escapeHtml(row.hubspot)}</td>
+ <td class="norma-col ${row.winner === 'norma' ? 'win' : ''}">${escapeHtml(row.norma)}</td>
+ </tr>
+ `).join('')}
+ </tbody>
+ `;
+ }
+
/* ─────────────── wix options + principles ─────────────── */
function renderWixOptions() {
const w = state.data.wix_integration;
diff --git a/public/index.html b/public/index.html
index 608c28b..c51f770 100644
--- a/public/index.html
+++ b/public/index.html
@@ -20,6 +20,7 @@
<a href="#reluctant">Skeptic?</a>
<a href="#db-shift">DB Era</a>
<a href="#wix">Wix</a>
+ <a href="#why-custom">Why custom CRM</a>
<a href="#why">Why AI</a>
<a href="#loops">Loops</a>
<a href="#cron">Cron</a>
@@ -113,6 +114,31 @@
</div>
</section>
+<!-- ─────────── why custom CRM (vs Salesforce/EveryAction/HubSpot) ─────────── -->
+<section id="why-custom" class="lecture">
+ <div class="lecture-inner">
+ <span class="section-num" style="background:#047857;">$</span>
+ <h2 data-bind="why_custom_crm.headline"></h2>
+ <p class="sub" data-bind="why_custom_crm.intro"></p>
+ <div class="callout callout-strategy">
+ <h3>The short answer</h3>
+ <p data-bind="why_custom_crm.the_short_answer"></p>
+ </div>
+ <h3 class="subsection">Head-to-head — 8 axes</h3>
+ <div class="crm-compare-wrap"><table class="crm-compare" id="crm-compare"></table></div>
+ <div class="callout callout-info">
+ <h3>The hybrid play</h3>
+ <p data-bind="why_custom_crm.the_hybrid_play"></p>
+ </div>
+ <h3 class="subsection">Honest risks (don't hide these from SDCC)</h3>
+ <ul class="lecture-bullets compact" data-bind-list="why_custom_crm.honest_risks"></ul>
+ <div class="callout callout-honest">
+ <h3>The real question to ask SDCC</h3>
+ <p data-bind="why_custom_crm.the_real_question"></p>
+ </div>
+ </div>
+</section>
+
<!-- ─────────── wix integration ─────────── -->
<section id="wix" class="lecture alt">
<div class="lecture-inner">
diff --git a/public/styles.css b/public/styles.css
index 8f372d9..a0caffd 100644
--- a/public/styles.css
+++ b/public/styles.css
@@ -637,6 +637,34 @@ code { font-family: 'SF Mono', Menlo, Consolas, monospace; font-size: .85em; bac
}
.shift-table td.after-col { background: var(--good-soft); color: #064e3b; }
+/* ────── CRM compare table ────── */
+.crm-compare-wrap { overflow-x: auto; margin: 0 0 24px; }
+.crm-compare {
+ width: 100%; border-collapse: separate; border-spacing: 0;
+ background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
+ overflow: hidden; min-width: 720px;
+}
+.crm-compare th {
+ background: linear-gradient(180deg, #f8fafc, #fff); padding: 14px 16px;
+ text-align: left; font-size: 13px; color: var(--muted);
+ text-transform: uppercase; letter-spacing: 1px;
+ border-bottom: 2px solid var(--border);
+}
+.crm-compare th.norma-col { color: var(--sdcc-blue-deep); background: var(--info-soft); }
+.crm-compare td {
+ padding: 14px 16px; font-size: 13.5px; color: var(--ink-2); line-height: 1.55;
+ border-bottom: 1px solid var(--border-soft); vertical-align: top;
+}
+.crm-compare td.axis { font-weight: 700; color: var(--ink); background: var(--bg); }
+.crm-compare td.norma-col { background: var(--info-soft); }
+.crm-compare td.win { position: relative; background: var(--good-soft); color: #064e3b; font-weight: 500; }
+.crm-compare td.win::before {
+ content: "★"; position: absolute; top: 6px; right: 8px;
+ color: var(--good); font-size: 12px;
+}
+.crm-compare td.norma-col.win { background: linear-gradient(180deg, var(--good-soft), var(--info-soft)); }
+.crm-compare tr:last-child td { border-bottom: none; }
+
/* ────── wix options ────── */
.wix-options { display: grid; gap: 14px; margin-bottom: 28px; }
.wix-option-card {
← 2907114 add: top-5 priorities, reluctant-adopter, companies-are-data
·
back to Norma Sdcc Pitch
·
hero: lead with 'A CRM that fits SDCC's work. Not the other 502c282 →