Chapter 11 / 12 · Narrative systems

Narrative systems

Every branded system diagram, timeline, and wireframe abstraction the marketing site needs — built in code, themed by tokens, animated on reveal. Decay curves, scoreboards, race timelines, the engine, handoff and profile cards, the integration hub, HAPPA arc, Guardian shield, journey maps, the closest-edge race, ecosystem rings, the Cost-of-Inaction calculator, a brand timeline, and a hero bloom canvas — plus four mini-atoms (compliance row, channel orbit, spark, stat badge) that slot into the big pieces.

11.0 In action

A single conversation, the whole story. Watch the agent move from intent signal to booked meeting — with the context it pulled from memory highlighted alongside. Plays on a continuous 15.5s loop (12.5s action + 3s rest on Booked) so the scene is always live; respects prefers-reduced-motion and renders full state immediately for reduced-motion users.

Flagship — AI agent conversation

.hero-scene

Trigger fires a personalised outbound; the agent handles a mid-conversation tangent, then converts the lead into a booked meeting. Annotations on the right show what MagicBlocks knew at each turn.

Intent signal Pro pricing · 3× · 48h
Hey Sarah 👋 Saw you came back to Pro pricing a few times. We just reduced annual $240 → $192. Want the 60-second rundown?
does it cover 50+ users
Yep — Pro goes up to 100 seats. Above that we have Business. How big's the team?
around 75
Then Pro's the fit — no need to jump. Want a demo?
this week?
Pick a slot:
Thu · 2pm
Fri · 10am
Thursday works
✓ Booked. Invite + tour link incoming 🎉
<div class="hero-scene reveal" data-hero-scene>
  <div class="hs-phone">
    <div class="hs-notch"></div>
    <div class="hs-screen">
      <div class="hs-trigger" data-step="trigger">…Intent signal…</div>
      <div class="hs-thread" data-thread>
        <div class="hs-msg out" data-step="m1">…outbound 1…</div>
        <div class="hs-msg in"  data-step="m2">…inbound 1…</div>
        <!-- …m3 … m9, plus an .hs-msg-booked final state -->
        <div class="hs-typing" data-typing hidden><span></span>…</div>
      </div>
    </div>
  </div>
  <aside class="hs-annotations">
    <div class="hs-ann" data-ann="a1">…</div>
    <!-- …a2 … a4 -->
  </aside>
</div>
/* Timed reveal via JS toggling .is-visible on each bubble + annotation.
   Phone shell is chapter-scoped (not .device.phone), purpose-built for
   hosting a live thread with pinned trigger and scrollable history. */
.hero-scene { display: grid; grid-template-columns: 340px 280px; gap: var(--s-7); }
.hs-phone   { aspect-ratio: 9/18; background: var(--ink); border-radius: 44px; padding: 14px; }
.hs-screen  { background: var(--warm-3); border-radius: 32px; overflow: hidden; }
.hs-msg.out { background: var(--accent); color: var(--paper); }
.hs-msg.in  { background: var(--bg-paper); border: 1px solid var(--hair); }
.hs-msg.is-visible { opacity: 1; transform: translateY(0); }
.hs-ann     { border-left: 2px solid var(--accent); padding: 10px 14px; }
@media (prefers-reduced-motion: reduce) {
  .hs-msg, .hs-ann, .hs-trigger { opacity: 1 !important; transform: none !important; }
  .hs-typing { display: none !important; }
}

11.1 Decay curve

A line graph of lead intent against time. Steep exponential drop, shaded dead zone after 5 minutes, two pinned markers — "Your team" (10 min, red) vs "MagicBlocks" (5 sec, pink). The single most-reused visual on the site.

Intent decay — lost vs fixed

.decay-curve

Curve draws on scroll-in; red marker pulses; pink marker pops at the tail. Respects reduced-motion.

0 sec 1 min 5 min 15 min 30 min 100% 50% 0% Hot Cooling Dead zone MagicBlocks · 5 sec CONVERSATION BEGINS Your team · 10 min CONTACT RATE DOWN 80%
<div class="decay-curve reveal svg-draw" data-state="lost" style="--draw-len: 1100;">
  <svg viewBox="0 0 900 340" role="img" aria-label="Lead intent decays within minutes">
    <!-- gridlines, axis labels, dead-zone rect -->
    <rect class="dead-zone" x="460" y="60" width="400" height="240"/>
    <!-- curve (draws on reveal via stroke-dasharray) -->
    <path class="curve-path draw"
          d="M 60 60 Q 130 72, 200 110 T 360 200 T 520 260 T 700 285 T 860 295"/>
    <!-- markers -->
    <g class="marker won"  transform="translate(90 65)">…</g>
    <g class="marker lost" transform="translate(540 265)">…</g>
  </svg>
</div>
.decay-curve .curve-path { fill: none; stroke: var(--ink); stroke-width: 2.4; stroke-linecap: round; }
.decay-curve .dead-zone  { fill: color-mix(in oklab, #D64545 16%, transparent); }
.decay-curve .marker.lost .marker-ring { stroke: #D64545; animation: dc-pulse-lost 1.8s ease-out infinite; }
.decay-curve .marker.won  .marker-ring { stroke: var(--accent); }
/* draw primitive (shared) */
.svg-draw .draw { stroke-dasharray: var(--draw-len, 1200); stroke-dashoffset: var(--draw-len, 1200); transition: stroke-dashoffset 1200ms ease-out; }
.svg-draw.is-visible .draw { stroke-dashoffset: 0; }

Dormant-lead mine

.dormant-mine

The other half of the story. Most of your CRM is dormant leads nobody had time to touch — real revenue sitting in boxes. Agents open every box with a hyper-personalised message. The grid animates left → right as the agent mines each lead.

~70%
of your CRM went cold. Nobody had the hours to personally re-engage every lead.
Agent mining at scale
Every one
gets a hyper-personalised message — name, context, credit band, last touch.

Dormant leads are revenue sitting in boxes. Agents open every box — in under a second, in the customer's language, with their full history loaded.

<div class="dormant-mine reveal">
  <div class="dm-stats">
    <div class="dm-stat dm-stat-dim">…70% went cold…</div>
    <div class="dm-sweep-label">Agent mining at scale</div>
    <div class="dm-stat dm-stat-hot">…every one gets a reply…</div>
  </div>
  <div class="dm-grid">
    <!-- 60 <span class="dm-dot" style="--i:N"> — --i is column index -->
  </div>
  <p class="dm-cap">Dormant leads are revenue in boxes…</p>
</div>
.dm-grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: 10px; }
.dm-dot {
  aspect-ratio: 1; max-width: 26px; border-radius: 50%;
  background: transparent;
  border: 1.5px solid color-mix(in oklab, var(--fg) 18%, transparent);
  animation: dm-wave 7s ease-in-out infinite;
  animation-delay: calc(var(--i, 0) * 0.24s);
}
@keyframes dm-wave {
  0%, 35%  { background: transparent; box-shadow: none; }
  50%, 92% { background: var(--accent);
             border-color: var(--accent);
             box-shadow: 0 0 0 3px var(--accent-soft); }
  100%     { background: transparent; }
}

11.2 Scoreboard

A two-column contrast table. Left column strikes through a losing value; right column pink-tinted with a one-shot glow on reveal. Used for every "most teams vs MagicBlocks" moment on the site.

Most teams vs MagicBlocks

.scoreboard

Values strike through on the losing side; the winning side gets a pink-tinted cell + one-shot glow on first view.

Most teams
With MagicBlocks
Response time
8–12 minutes
Under 5 seconds
Contact rate
15–20%
2–4× higher
Follow-up consistency
Drops off by day 3
Every lead, every time
Qualification
Manual, inconsistent
Automatic, pre-handoff
Task completion
59% (single-prompt)
97.5% (multi-prompt)
Hallucination rate
Unbounded
Guardian-gated
Stress-tested across 400 simulated sessions · Z = 9.33 · p < 0.00001
<div class="scoreboard reveal">
  <div class="sb-head">
    <div></div>
    <div class="bad">Most teams</div>
    <div class="good">With MagicBlocks</div>
  </div>
  <div class="sb-row">
    <div class="label">Response time</div>
    <div class="bad">8–12 minutes</div>
    <div class="good">Under 5 seconds</div>
  </div>
  <!-- more rows… -->
</div>
<div class="scoreboard-foot">Stress-tested · <strong>Z = 9.33</strong></div>
.scoreboard { display: grid; grid-template-columns: minmax(180px, 1fr) 1fr 1fr; border: 1px solid var(--hair); border-radius: var(--r-lg); overflow: hidden; }
.scoreboard .sb-head  { display: contents; }
.scoreboard .sb-row   { display: contents; }
.scoreboard .sb-row .bad  { text-decoration: line-through; background: color-mix(in oklab, var(--error)   6%, transparent); }
.scoreboard .sb-row .good { background: color-mix(in oklab, var(--success) 8%, transparent); font-weight: 500; }
.scoreboard.is-visible .sb-row .good::before { /* one-shot glow */ animation: sb-glow 1200ms ease-out 600ms forwards; }

Dark variant

.scoreboard.dark

War-room surface. Pairs well with `.hero-bloom-canvas[data-variant="war-room"]` sections.

Single-prompt AI
Multi-prompt AI
Architecture
One instruction
Specialised modules
Context
Fills & degrades
Managed per module
Hallucination
Grows with complexity
Guardian-gated
Recovery
Fails on edge cases
Recovers in 4.55 turns

11.3 Race timeline

Split timestamp sequence — left track loses, right track wins. Replaces every "race lane" visual in the doc with a cleaner time-based contrast.

The 9:47 moment

.race-timeline

Used on the homepage signature proof + every use-case page variant.

Without MagicBlocks
With MagicBlocks
9:47 AM
Lead submits formEnters CRM queue
9:47:00
Lead submitsEngine fires
9:55 AM
Rep reads notificationBetween calls
9:47:08
Personalised conversation beginsAcross SMS
10:12 AM
Rep calls back — no answerLeaves voicemail
9:48:30
Lead qualifiedHAPPA checkpoints cleared
2:30 PM
Lead went with competitorDeal lost
9:49:15
Appointment bookedHanded off with full context
<div class="race-timeline reveal">
  <div class="rt-head bad">Without MagicBlocks</div>
  <div class="rt-midline" aria-hidden="true"></div>
  <div class="rt-head good">With MagicBlocks</div>

  <div class="rt-tick rt-left">
    <span class="rt-stamp">9:47 AM</span>
    <div class="rt-body">Lead submits form<em>Enters CRM queue</em></div>
  </div>
  <div class="rt-tick rt-right win">
    <span class="rt-stamp">9:47:00</span>
    <div class="rt-body"><strong>Lead submits</strong><em>Engine fires</em></div>
  </div>
  <!-- more paired ticks… -->
</div>
.race-timeline { display: grid; grid-template-columns: 1fr auto 1fr; }
.race-timeline .rt-midline { grid-row: 2 / span 99; margin: 0 var(--s-6); width: 2px; background: linear-gradient(var(--hair), var(--accent)); }
.race-timeline .rt-left  { grid-column: 1; text-align: right; direction: rtl; }
.race-timeline .rt-left > *  { direction: ltr; }
.race-timeline .rt-right { grid-column: 3; }
.race-timeline .rt-tick.win .rt-stamp::after { content: "✓"; }

11.4 Engine block

A central ink-block engine with four channel icons orbiting it. Lead sources stack on the left, qualified outputs on the right. Replaces the "lead conversion engine" diagram (plus the "knowledge flow" variant).

Conversion engine

.engine-block

Orbit rotates at 40s / revolution. Sources left, outputs right. Mobile: orbit disappears and channels become a chip row under the engine.

Website forms
Facebook / Instagram
Google Ads
Marketplaces
CRM backfill
The engine
MagicBlocks
conversion engine
EngageQualifyFollow upReengage
Qualified conversations
Booked appointments
Context-rich handoffs
<div class="engine-block reveal">
  <div class="engine-sources">
    <div class="engine-source">Website forms</div>
    <div class="engine-source">Facebook / Instagram</div>
    <!-- … -->
  </div>
  <div class="engine-centre">
    <svg class="engine-orbit" viewBox="0 0 320 320">
      <circle class="ring" cx="160" cy="160" r="140"/>
      <g class="orbit-anim"><!-- 4 channels at N/E/S/W --></g>
    </svg>
    <div class="engine-core">
      <div class="label">The engine</div>
      <div class="name">MagicBlocks <em>conversion engine</em></div>
      <div class="pill-row"><span>Engage</span><span>Qualify</span><span>Follow up</span><span>Reengage</span></div>
    </div>
  </div>
  <div class="engine-outputs">
    <div class="engine-output">Qualified conversations →</div>
  </div>
</div>
.engine-block { display: grid; grid-template-columns: minmax(140px, 180px) 1fr minmax(140px, 180px); gap: var(--s-5); }
.engine-orbit .orbit-anim { transform-origin: center; animation: eb-orbit 40s linear infinite; }
@keyframes eb-orbit { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .engine-orbit .orbit-anim { animation: none; } }

11.5 Triptych

Three equal panels with arrow dividers. Used for "How It Works" across every page. Each panel has an eyebrow, title, body, and an art slot that can hold any other chapter-11 component.

3-step flow

.triptych

On mobile, panels stack and arrows rotate to ↓.

01 · Connect

Plug in your leads

Forms, marketplaces, ad platforms, your CRM. Setup takes days, not months.

02 · Run

The engine takes over

Instant, personalised conversations on every channel. Qualifies, nurtures, follows up.

Avg.5sFirst response
03 · Close

Your team closes deals

Pre-qualified, warm handoffs with full context. No more cold calls.

Task completion97.5%vs 59% single-prompt
<div class="triptych reveal">
  <div class="panel">
    <div class="step">01 · Connect</div>
    <h4>Plug in your leads</h4>
    <p>Forms, marketplaces, ad platforms, your CRM.</p>
    <div class="art"><!-- any chapter-11 atom --></div>
  </div>
  <div class="arrow"><svg>…arrow right…</svg></div>
  <div class="panel">…</div>
  <div class="arrow">…</div>
  <div class="panel">…</div>
</div>
.triptych { display: grid; grid-template-columns: 1fr auto 1fr auto 1fr; gap: var(--s-5); }
@media (max-width: 880px) { .triptych { grid-template-columns: 1fr; } .triptych .arrow { transform: rotate(90deg); } }

11.6 Handoff card

A credential-style card showing a qualified lead ready for human handoff. Five industry variants via `data-role` — each shifts accent colour, role label, and qualification tags.

Handoff card — five roles

.handoff-card[data-role]

Card lifts in on reveal; name gets a coloured underline sweep; a "new" pulse indicates fresh handoff.

TS

Taylor Simmons

Mortgage · Loan officer

09:49
Qualified $450k 30-yr fixed Prefers SMS
Income
$145k
Timeline
4–8 weeks
Credit band
740+
Intent score
92
PR

Priya Ramanathan

Insurance · Agent

16:02
Qualified Auto + Home Bundling quote Prefers email
Current carrier
State Farm
Renewal date
23 days
Premium
$2,140/yr
Intent score
81
MK

Marcus Kim

Tourism · Booking specialist

08:14
Qualified Family of 4 2 nights Dec 18
Party size
4
Budget
$1,800
Prior guest
yes
Intent score
96
<article class="handoff-card reveal" data-role="mortgage-lo">
  <span class="ho-pulse" aria-hidden="true"></span>
  <div class="ho-band">
    <span class="ho-av">TS</span>
    <div class="ho-who">
      <p class="name">Taylor Simmons</p>
      <p class="role">Mortgage · Loan officer</p>
    </div>
    <span class="ho-stamp">09:49</span>
  </div>
  <div class="ho-tags">
    <span class="ho-tag lead">Qualified</span>
    <span class="ho-tag">$450k</span>
    <span class="ho-tag">30-yr fixed</span>
  </div>
  <div class="ho-facts">
    <div class="ho-fact"><div class="k">Income</div><div class="v">$145k</div></div>
    <!-- more facts… -->
  </div>
  <div class="ho-cta">
    <button class="primary">Call now</button>
    <button>Send rate</button>
  </div>
</article>
/* role colours via a single custom property */
.handoff-card { --ho-accent: var(--accent); }
.handoff-card[data-role="insurance-agent"]    { --ho-accent: var(--green-700); }
.handoff-card[data-role="counsellor"]         { --ho-accent: var(--blue-700); }
.handoff-card[data-role="front-desk"]         { --ho-accent: var(--yellow-700); }
.handoff-card[data-role="booking-specialist"] { --ho-accent: #C77A3E; }
.handoff-card .ho-band { background: color-mix(in oklab, var(--ho-accent) 12%, var(--bg-paper)); }
.handoff-card.is-visible .ho-who .name::after { animation: ho-underline 700ms ease-out 300ms backwards; }

11.7 Profile card

A card that visibly accretes memory as the AI learns more about a lead. Shows five progressive "ticks" of relationship memory. Secondary "dormant then reactivated" state illustrates the Reengage use case.

Growing memory · dormant · reactivated

.profile-card

Auto-cycles when in view; respects reduced-motion by rendering the fully-grown state immediately.

MS

Morgan Shaw

First message at 09:47 · SMSDay 1
!Raised pricing concern · resolvedDay 1
Prefers SMS over emailDay 2
Mentioned "after holidays" timelineDay 3
Intent score raised to 87Day 4
Active relationship 5 / 5 signals
JK

Jae Kim

Requested rate quote · MarchDay 1
Went quiet after pre-approvalDay 4
Dormant · 47 days Queued for reactivation
JK

Jae Kim

Rate-drop trigger · sent SMSDay 48
Replied within 6 minDay 48
!Scheduled recap callDay 48
Reactivated Back in pipeline
<div class="profile-card reveal" data-step="0" data-max-step="5" data-interval="1100" data-auto-cycle>
  <header class="pc-head">
    <span class="pc-av">MS</span>
    <div><p class="pc-name">Morgan Shaw</p><p class="pc-email">morgan@sh.example</p></div>
  </header>
  <div class="pc-bars"><span class="pc-bar"></span>×5</div>
  <div class="pc-rows">
    <div class="pc-row"><span class="ico">✎</span><span>First message at 09:47</span><span class="meta">Day 1</span></div>
    <!-- 4 more rows -->
  </div>
  <div class="pc-status"><span>Active relationship</span><span>5 / 5 signals</span></div>
</div>
/* rows reveal based on [data-step] */
.profile-card .pc-row { opacity: 0; transform: translateY(4px); transition: opacity 300ms, transform 300ms; }
.profile-card[data-step="1"] .pc-row:nth-child(-n+1),
.profile-card[data-step="2"] .pc-row:nth-child(-n+2),
/* … */
.profile-card[data-step="5"] .pc-row { opacity: 1; transform: none; }

.profile-card.is-dormant     { filter: grayscale(0.6); opacity: 0.7; }
.profile-card.is-reactivated { box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 30%, transparent); }

11.8 Integration hub

Radial diagram — central MagicBlocks hub, integration chips arranged on a ring. Each chip is a branded pill rather than a real logo. Industry variants swap the chip set.

Hub-and-spoke

.integration-hub

Chips are `.chip`-styled pills with mono labels. Spokes are dashed hairlines converging on the core. Motion = chips fade clockwise on reveal.

MagicBlocks
Salesforce HubSpot Twilio Calendly Zapier Segment Stripe Slack
<div class="integration-hub reveal">
  <svg class="spokes" viewBox="0 0 520 520"><!-- 8 lines from centre to ring positions --></svg>
  <div class="hub-core">
    <span class="label">Engine</span>
    <span class="name">MagicBlocks</span>
  </div>
  <div class="nodes">
    <span class="node" style="top: 6%; left: 50%;">Salesforce</span>
    <!-- 7 more chips at ring positions -->
  </div>
</div>
.integration-hub { position: relative; aspect-ratio: 1; }
.integration-hub .hub-core { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: var(--ink); color: var(--paper); }
.integration-hub .node { position: absolute; transform: translate(-50%, -50%); padding: 6px 10px; background: var(--bg-paper); border: 1px solid var(--hair); border-radius: 999px; font: 500 11px/1 var(--f-mono); }
.integration-hub .spokes line { stroke: var(--hair); stroke-dasharray: 2 4; }

11.9 HAPPA arc

Five conversation stages — Hook · Align · Personalise · Pitch · Action — anchored along a soft S-curve. Branches off each node show adaptive moves (objection routed to handler, etc.).

5-stage conversation arc

.happa-arc
H Hook Open with relevance A Align Match their priorities P Personalise Use everything known P Pitch Right offer, right moment A Action Low-friction next step

11.10 Guardian shield

One shield, one promise. Six universal compliance primitives — consent, privacy, encryption, retention, opt-out, audit — that apply across every vertical we serve. Industry-specific extras (TCPA, RESPA, HIPAA, etc.) configure in the product; the shield on the brand page stays universal.

Compliance shield

.guardian-shield

Used on marketing surfaces — pricing, enterprise, trust/security pages — anywhere the question "are you safe to deploy?" lands. Draws in on reveal, shine sweeps on loop.

Consent Privacy Encryption Retention Opt-out Audit
Guardian · every lead, every vertical

11.11 Journey map

Horizontal branching flow with labelled connectors. One component, many variants — prequalification · follow-up · reactivation · insurance renewal · healthcare patient loop.

Prequalification flow

.journey-map
StartLead arrives
01Engage instantly
02Collect context
YesHandoff to sales
Not readyDrop into nurture
DormantQueue for reactivation

11.12 Closest edge wins

YOU sit at the centre. The edges scatter around you at the distances they actually are. Every cycle a pulse races from each edge towards you — the fastest arrival wins, gets ringed in green, and the latency badge appears next to it. The scene then transitions to the next region (US-WEST → US-EAST → EUROPE → ASIA → OCEANIA, looping). Tells the proximity story directly: the closest server always wins, in single-digit milliseconds, wherever the customer is.

Edge race · 5 scenarios

.edge-race

11.13 Ecosystem rings

Three concentric rings — engine at centre, today's products in the middle ring, tomorrow's vision on the outer ring. For the About page vision section.

Expanding ecosystem

.ecosystem-rings
The engine MagicBlocks
Engage Qualify Follow up Reengage Voice agents Inbox Analytics Marketplace

11.14 Revenue calculator

Two modes in one widget. Activation models converting more inbound leads (leads × conversion lift × deal value vs plan cost). Reactivation models re-engaging the aged database (dbSize × conversion uplift vs outreach + worked-lead pricing). Ink surface + radial pink glow in the output panel; hero number gets a green halo, ROI gets a pink halo.

Revenue calculator

.roi-calc

Plan-aware pricing (Core $1K / Scale $4K / Enterprise $15K) picks the cheapest tier automatically based on lead volume. Slider values are double-click-to-edit for precision. All maths runs client-side via inputmode="numeric" fields; no server round-trip.

Revenue calculator

How much revenue could MagicBlocks unlock?

Plug in your numbers. See the opportunity. This isn’t a marketing exercise — it’s the math your CFO would run.

Your numbers

$
3%
+1%

New rate: 4%

$

Your results

Current monthly revenue

Projected revenue at 4% conversion

Additional revenue per month

MagicBlocks cost

Return on investment

Payback period

Your database

1%

Typical for aged leads without re-engagement

2%

AI-driven reactivation typically achieves 3–8%

$

Your results

Current revenue at 1% conversion

Revenue with MagicBlocks at 2%

Additional revenue unlocked

MagicBlocks cost

Return on investment

Cost per reactivated deal

<div class="roi-calc" data-roi-calc>
  <h2 class="roi-calc-title">How much revenue could MagicBlocks <em>unlock</em>?</h2>

  <div class="roi-tabs" role="tablist">
    <button class="roi-tab-btn is-active" data-roi-tab="activation">Lead activation</button>
    <button class="roi-tab-btn"           data-roi-tab="reactivation">Lead reactivation</button>
  </div>

  <div class="roi-card">
    <div class="roi-panel is-active" data-roi-panel="activation">
      <div class="roi-inputs">…5 fields…</div>
      <div class="roi-outputs">
        <div class="roi-result is-hero"><p>Additional revenue/mo</p><p data-roi-out="activation.additional"></p></div>
        <div class="roi-result is-roi"><p>ROI</p><p data-roi-out="activation.roi"></p></div>
        <!-- cost, payback, current, projected -->
      </div>
    </div>
    <div class="roi-panel" data-roi-panel="reactivation">…</div>
  </div>
</div>
.roi-card { background: var(--ink); border-radius: var(--r-lg); box-shadow: 0 30px 60px -24px color-mix(in oklab, var(--ink) 60%, transparent); }
.roi-panel.is-active { display: grid; grid-template-columns: 1fr 1fr; }

/* Output column: darker + pink radial glow for pop */
.roi-outputs {
  background:
    radial-gradient(80% 65% at 92% -10%, color-mix(in oklab, var(--accent) 14%, transparent), transparent 70%),
    color-mix(in oklab, var(--ink) 88%, #000);
}

/* Hero revenue gets a green glow, ROI gets a pink glow */
.roi-result.is-hero .roi-result-value { color: var(--green-500); text-shadow: 0 0 32px color-mix(in oklab, var(--green-500) 38%, transparent); }
.roi-result.is-roi  .roi-result-value { color: var(--accent-text);    text-shadow: 0 0 28px color-mix(in oklab, var(--accent) 34%, transparent); }

/* Mode-prop documentation table — defaults per page/industry. */
.roi-mode-table {
  width: 100%; border-collapse: collapse;
  font: 400 13px/1.4 var(--f-body);
  background: var(--bg-paper);
  border: 1px solid var(--hair);
  border-radius: var(--r-md);
  overflow: hidden;
}
.roi-mode-table thead {
  background: var(--bg-sunk);
  font: 600 10.5px/1 var(--f-mono);
  text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--fg-soft);
}
.roi-mode-table th, .roi-mode-table td {
  padding: 10px 12px;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--hair);
}
.roi-mode-table tbody th {
  font: 500 12px/1.3 var(--f-mono);
  color: var(--accent-text);
  white-space: nowrap;
}
.roi-mode-table tbody td:nth-of-type(2) {
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  color: var(--fg-soft);
}
.roi-mode-table tbody td:last-child {
  font: 500 12.5px/1.3 var(--f-body);
  color: var(--fg);
}
.roi-mode-table tbody tr:last-child th,
.roi-mode-table tbody tr:last-child td { border-bottom: 0; }
.roi-mode-table tbody tr:hover { background: color-mix(in oklab, var(--accent-soft) 60%, transparent); }
@media (max-width: 720px) {
  .roi-mode-table thead { display: none; }
  .roi-mode-table tr { display: grid; grid-template-columns: 1fr; gap: 4px; padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--hair); }
  .roi-mode-table th, .roi-mode-table td { border: 0; padding: 0; }
  .roi-mode-table tbody th { color: var(--accent-text); }
}

@media (max-width: 720px) { .roi-panel.is-active { grid-template-columns: 1fr; } }

Mode prop

.roi-calc[data-mode]

Same widget. Same maths. The data-mode prop only swaps the default input values and the output framing label per destination page — it does not change the calculation. Use the table below as the source of truth for what each mode pre-populates. The website pre-fills inputs on render; users can still edit any field.

data-mode Pages Defaults (leads · CPL · conv · deal) Output label
neutral Homepage §14, Pricing §7 1,200 · $45 · 4.5% · $2,400 Lost revenue per month
engage Engage New Leads §10 1,200 · $45 · 4.5% · $2,400 Revenue leaking per month
prequalify Prequalify Leads §10 8 reps · 20 calls · 35% qualified · $8,400 Rep hours/week wasted on bad-fit
followup Follow Up Leads §11 1,200 · 2.3 touches · 18% completion · $2,400 Deals lost to silence per month
reengage Reengage Aged Leads §12 25,000 dormant · $45 · 8% reactivation · $2,400 Sunk acquisition cost + Revenue recoverable
industry:mortgage Mortgage industry hub, Beeline case 1,200 · $45 · 4.5% · $2,400 Revenue leaking per month
industry:insurance Insurance industry hub 800 · $35 · 6% · $1,800 Commission leaking per month
industry:auto Auto industry hub 2,000 · $25 · 8% · $1,200 Gross leaking per month
industry:home-services Home Services industry hub 1,500 · $40 · 5% · $3,500 Revenue leaking per month
industry:solar Solar industry hub 600 · $80 · 3% · $9,500 Commission leaking per month
industry:fintech Fintech industry hub 2,500 · $30 · 5% · $800 Revenue leaking per month
industry:tourism Tourism industry hub, Waterbom case 5,000 · $12 · 6% · $480 Booking revenue leaking per month
<!-- pick the mode for the destination page; rest of markup is identical -->
<div class="roi-calc" data-roi-calc data-mode="industry:solar">
  <p class="roi-calc-eyebrow">Revenue calculator</p>
  <h2 class="roi-calc-title" data-roi-title>…</h2>
  <!-- inputs + outputs identical to the default — only defaults differ -->
</div>
// Mode → defaults map. Calculation logic in the existing JS is
// unchanged; this only seeds the inputs on render.
window.MB_ROI_MODES = {
  "neutral":            { leads: 1200, cpl: 45, conv: 4.5, deal: 2400, outputLabel: "Lost revenue per month" },
  "engage":             { leads: 1200, cpl: 45, conv: 4.5, deal: 2400, outputLabel: "Revenue leaking per month" },
  "prequalify":         { reps: 8, callsPerRep: 20, qualifiedRate: 35, deal: 8400, outputLabel: "Rep hours/week wasted on bad-fit" },
  "followup":           { leads: 1200, touches: 2.3, completion: 18, deal: 2400, outputLabel: "Deals lost to silence per month" },
  "reengage":           { dormant: 25000, originalCpl: 45, reactivation: 8, deal: 2400, outputLabel: "Sunk acquisition cost + Revenue recoverable" },
  "industry:mortgage":      { leads: 1200, cpl: 45, conv: 4.5, deal: 2400,  outputLabel: "Revenue leaking per month" },
  "industry:insurance":     { leads:  800, cpl: 35, conv: 6,   deal: 1800,  outputLabel: "Commission leaking per month" },
  "industry:auto":          { leads: 2000, cpl: 25, conv: 8,   deal: 1200,  outputLabel: "Gross leaking per month" },
  "industry:home-services": { leads: 1500, cpl: 40, conv: 5,   deal: 3500,  outputLabel: "Revenue leaking per month" },
  "industry:solar":         { leads:  600, cpl: 80, conv: 3,   deal: 9500,  outputLabel: "Commission leaking per month" },
  "industry:fintech":       { leads: 2500, cpl: 30, conv: 5,   deal:  800,  outputLabel: "Revenue leaking per month" },
  "industry:tourism":       { leads: 5000, cpl: 12, conv: 6,   deal:  480,  outputLabel: "Booking revenue leaking per month" }
};

// Wiring (existing init): on mount, look up the mode and seed the inputs.
// All four canonical formulas (neutral / engage / followup / industry:*),
// the prequalify hours formula, and the reengage recovery formula are
// already implemented in the calculator's existing IIFE — modes only
// pick which input set + output label to show.

11.15 Brand timeline

Horizontal milestone ticks. Used for the About page origin story ($200M → AI mortgage team → MagicBlocks) and for industry-specific multi-year timelines (insurance renewal cadence, etc.).

Origin timeline

.brand-timeline

2016 – 2022

$200M+ in leads

Six years running lead-driven businesses across mortgage, solar, insurance and home services.

2023

World’s first AI mortgage team

Built the first live AI sales team in mortgage — agents that engage, qualify and book inside regulated conversation.

2026

MagicBlocks

The conversion engine, productised. Same methodology, every vertical, deployed in days.

11.16 Hero bloom canvas

A large ambient SVG backdrop for hero sections. Three soft radial gradients, slow drift, optional drifting motes. Variants: warm · war-room · industry (driven by --industry-accent).

Three variants

.hero-bloom-canvas
Warm variant

Every lead. Every time.

War-room variant

The engine under the hood.

Industry variant · Insurance

Complex qualification. Handled instantly.

<div class="hero-bloom-canvas" data-variant="warm">
  <div class="hbc-content">
    <div class="kicker">Warm variant</div>
    <h4>Every lead. <em>Every time.</em></h4>
  </div>
  <div class="motes"><span class="mote"></span></div>
</div>
<!-- industry variant with per-page colour wash -->
<div class="hero-bloom-canvas" data-variant="industry" style="--industry-accent: #47DDB2;"></div>
.hero-bloom-canvas::before {
  background:
    radial-gradient(60% 60% at 85% 12%, color-mix(in oklab, var(--accent) 28%, transparent), transparent 70%),
    radial-gradient(40% 50% at 12% 80%, color-mix(in oklab, var(--blue-500) 18%, transparent), transparent 70%);
  animation: hb-drift 40s ease-in-out infinite alternate;
}
.hero-bloom-canvas[data-variant="industry"] { background: color-mix(in oklab, var(--industry-accent) 6%, var(--warm-3)); }

11.17 Hero live demo

The homepage above-the-fold flagship: a laptop frame running a looping mortgage-scenario conversation. Response-timer overlay proves the speed claim as you watch; the status badge cycles Inbound → Engaging → Qualified → Booked. Rate figures use live April-2026 benchmarks (6.12% vs 6.83% a year ago — a real ~70bps delta). Industry-swappable via data-scenario.

Live demo — laptop

.hero-live-demo

Starts on load and loops continuously every 20s. Timer ticks in 100ms increments until the agent's first reply (~4.2s) then freezes and pinkifies. Status chip's final state pulses. Play / Pause / Restart controls are available for viewers who want to drive it directly.

Response time
0.0s
Hey, what rates are you guys seeing right now?
30-year fixed is at 6.12% this morning — down from 6.83% a year ago. If you locked in 12 months ago, you're probably ~70bps high. Want me to pull what a refi would save you?
Sure
I just need two things — current balance and rate. Or I can pull from the CRM: are you Mike Chen at oakwood_dr@gmail.com?
yeah that's me
Got it — $582K at 6.83%. At today's rate you'd save ~$271/mo (~$3,250/yr). Want a 15-min rate review?
sounds good
Two slots: Thu 10:30 or Thu 2:45. ✓ Booked either way — pick one.
Scenario · Mortgage
<div class="hero-live-demo" data-hero-live-demo data-scenario="mortgage">
  <div class="hld-frame">
    <div class="hld-screen">
      <div class="hld-tabs">…browser chrome…</div>
      <div class="hld-app">
        <div class="hld-timer"  data-hld-timer>
          <div class="hld-timer-label">Response time</div>
          <div class="hld-timer-value" data-hld-timer-value>0.0s</div>
        </div>
        <div class="hld-status" data-hld-status>
          <span class="hld-status-dot"></span>
          <span data-hld-status-label></span>
        </div>
        <div class="hld-thread" data-hld-thread>
          <div class="hld-msg in"  data-hld-step="m1">…Lead asks rate…</div>
          <div class="hld-msg out" data-hld-step="m2">…Agent quotes 6.12%, offers refi calc…</div>
          <!-- m3–m8, plus -->
          <div class="hld-typing" data-hld-typing hidden>…</div>
        </div>
      </div>
    </div>
  </div>
  <div class="hld-controls">
    <button data-hld-action="restart">Restart</button>
    <button data-hld-action="pause">Pause</button>
    <button data-hld-action="play">Play</button>
  </div>
</div>
/* Laptop frame, continuous-loop. Status badge's state-colour comes
   from semantic tokens (inbound=info, engaging=warning, qualified=
   green-500, booked=accent). Timer freezes + pinkifies on first reply. */
.hero-live-demo  { max-width: min(900px, 100%); margin: 0 auto; position: relative; }
.hld-frame       { padding: 14px 14px 0; background: color-mix(in oklab, var(--ink) 95%, #999); border-radius: 18px 18px 4px 4px; }
.hld-screen      { aspect-ratio: 16/10; overflow: hidden; position: relative; }
.hld-timer       { position: absolute; top: var(--s-4); right: var(--s-4); background: rgba(25,30,50,.82); color: var(--paper); }
.hld-timer.is-frozen { background: var(--accent); }
.hld-status      { position: absolute; bottom: var(--s-4); left: var(--s-4); }
.hld-status[data-state="qualified"] .hld-status-dot { background: var(--green-500); animation: hld-pulse 1s ease-out 1; }
.hld-status[data-state="booked"]    .hld-status-dot { background: var(--accent);    animation: hld-pulse 1.4s ease-out 1; }
@media (prefers-reduced-motion: reduce) {
  .hld-msg, .hld-timer, .hld-status { opacity: 1 !important; }
  .hld-typing { display: none !important; }
}

Mode variants

.hero-live-demo[data-mode]

Same conversation engine, four overlay-emphasis modes for the four leak families. The data-mode prop chooses which overlay set is foregrounded; the conversation is supplied via JSON (see data contract below). Each mode pairs with its destination pages: speed-forward → Engage / Mortgage / Auto / Home Services / SMS · qualification-forward → Prequalify / Insurance / Solar · cadence-forward → Follow Up / Tourism · reactivation-forward → Reengage Aged.

First reply
4.2s
Booked
A/C just died. House is 92°. Two kids. Need someone today.
North Dallas + 12-year system + click-then-silence usually points to a failed capacitor. 12:30–2:30 PM with Aaron Ramirez — diagnostic $89, applied to repair. ✓
Mode · speed-forward
Qualification score
86
Commercial property insurance, 6 locations AZ + NV, $8M revenue, prior kitchen fire 2022.
Strong profile — $8M F&B multi-state + clean since 2022 fits our commercial broker bench. Marcus Ruiz Thu 10:30?
Mode · qualification-forward
Hey Mike — circling back on the rate review. You mentioned closing in 45d; we're now 22 days out. Slot tomorrow 2pm?
Yeah let's do tomorrow.
Touch 5/7
Day 22 / 30
Mode · cadence-forward
Last contact · 147 days ago
Re-engaged today
Hey Sarah — saw rates dropped 60bps since we last spoke back in November. On your old quote, that's about $340/mo back. Want a fresh look?
Wow yeah send me what you've got.
Mode · reactivation-forward
<!-- pick the mode that matches the destination page -->
<div class="hero-live-demo" data-hero-live-demo
     data-mode="speed-forward"            <!-- speed | qualification | cadence | reactivation -->
     data-scenario="mortgage"             <!-- mortgage | insurance | solar | home-services | auto | fintech | tourism -->
     data-script="engage-mortgage-v1">   <!-- key into window.MB_HLD_SCRIPTS -->
  <div class="hld-frame">
    <div class="hld-screen">
      <div class="hld-tabs">…</div>
      <div class="hld-app">
        <!-- mode-specific overlay (one of) -->
        <div class="hld-timer"        data-hld-timer>…</div>
        <div class="hld-qual-score">…</div>
        <div class="hld-cadence">…</div>
        <div class="hld-reactivation">…</div>
        <!-- shared -->
        <div class="hld-status" data-hld-status>…</div>
        <div class="hld-thread" data-hld-thread>…messages injected by JS…</div>
      </div>
    </div>
  </div>
</div>
// JSON data contract — register scripts before the page hydrates.
// The component reads window.MB_HLD_SCRIPTS[scriptKey] on load.
window.MB_HLD_SCRIPTS = {
  "engage-mortgage-v1": {
    "industry": "mortgage",
    "page": "engage",
    "leadContext": "Mortgage refi inquiry, credit mid-700s.",
    "messages": [
      { "role": "lead",   "timestamp": "9:47:04 AM", "text": "Hey — saw your rate quote page…", "delay": 200,  "typingDuration": 800 },
      { "role": "engine", "timestamp": "9:47:08 AM", "text": "Hey! Welcome…",                  "delay": 100,  "typingDuration": 1200 }
    ],
    "qualificationCheckpoints": ["balance", "value", "timeline"],
    "finalStatus": "Booked",
    "disclosureCaption": "Illustrative conversation. Rates and availability vary…"
  }
};

// Token rules
//  • [range] placeholders resolve at render from per-industry rate tables
//  • Timestamps render in the same wall-clock format across messages
//  • One green dot 🟢 at the end of the engine's last message only
//  • Disclosure caption renders in small type beneath the frame

11.18 Leak cards

Four paired tiles naming each leak in the funnel. Every stat is sourced research, not invented: Engage → Hatch 2024, Prequalify → BANT consensus, Follow Up → Marketing Donut, Reengage → CRM reactivation research. Each card is a single click target; the sparkline re-draws on hover. 2×2 grid desktop, stacked mobile.

Four Leaks

.leak-card[data-leak]

Recombines existing atoms (.stat-badge-like pill, .spark-style mini-chart, card surface) with leak-specific accent via the --lk-accent CSS custom property — one base class, four variants, zero rule duplication.

<div class="leak-grid">

  <a class="leak-card" data-leak="1" href="/use-cases/engage-new-leads">
    <span class="lk-icon" aria-hidden="true"><svg>…clock…</svg></span>
    <span class="lk-stat" aria-label="Key statistic: 88 percent">88%</span>
    <h3 class="lk-title">Engage new leads</h3>
    <p class="lk-body">
      <strong>88%</strong> of inbound leads don't get a sub-5-min reply…
      <cite>Hatch 2024 · MIT / HBR</cite>
    </p>
    <span class="lk-spark" aria-hidden="true"><svg>…drop curve…</svg></span>
    <span class="lk-cta">Fix Leak #1 <span class="arrow">→</span></span>
  </a>

  <!-- data-leak="2" (yellow) · "3" (green) · "4" (blue) — same structure -->

</div>
/* One base class, four variants via --lk-accent.
   data-leak selects the accent; everything else is shared. */
.leak-card { --lk-accent: var(--accent); --lk-accent-soft: var(--accent-soft); }
.leak-card[data-leak="1"] { --lk-accent: var(--pink-500);   }
.leak-card[data-leak="2"] { --lk-accent: var(--yellow-500); }
.leak-card[data-leak="3"] { --lk-accent: var(--green-500);  }
.leak-card[data-leak="4"] { --lk-accent: var(--blue-500);   }

.leak-card {
  display: grid;
  grid-template-areas: "icon stat" "title title" "body body" "spark spark" "cta cta";
  padding: var(--s-6); background: var(--bg-paper);
  border: 1px solid var(--hair); border-top: 3px solid var(--lk-accent);
  border-radius: var(--r-lg); box-shadow: var(--sh-1);
  transition: transform var(--dur-2), box-shadow var(--dur-2), border-color var(--dur-2);
}
.leak-card:hover { transform: translateY(-4px); box-shadow: var(--sh-3); }
.leak-card .lk-stat { background: var(--lk-accent-soft); color: var(--lk-accent); }
.leak-card:hover .lk-spark path { animation: lk-spark-draw 1.4s var(--ease) forwards; }

11.19 Chat comparison

Same inbound lead message. Same question. Watched live, side by side: a generic off-the-shelf chatbot on the left (desaturated, dead-ends at "thanks for your interest"), MagicBlocks on the right (pulls real rates, adapts, closes the booking). The asymmetry IS the message. Industry-swappable via data-scenario; Mortgage uses live April-2026 numbers.

Generic chatbot vs MagicBlocks

.chat-comparison

Both threads start empty, receive the same lead message, diverge at the first reply. Left stalls. Right qualifies, proposes two slots, books. Loops continuously on page load (3s rest between cycles). Status chip on the right pulses when the final state lands. Industry chip swap resets both columns and replays with the new script.

Same lead. Same question. Watch the difference.

Generic chatbot
MagicBlocks
Inbound
Scenario · Mortgage
<div class="chat-comparison" data-chat-comparison data-scenario="mortgage">
  <div class="cc-head">
    <h4>Same lead. Same question. Watch <em>the difference</em>.</h4>
    <div class="cc-toggle" role="radiogroup">
      <button data-cc-scenario="mortgage" class="is-active">Mortgage</button>
      <!-- insurance / solar / home-services / auto / fintech -->
    </div>
  </div>

  <div class="cc-cols">
    <div class="cc-col generic" data-cc-side="generic">
      <div class="cc-col-head">…Generic chatbot…</div>
      <div class="cc-thread" data-cc-thread><!-- JS-populated --></div>
    </div>
    <div class="cc-col mb" data-cc-side="mb">
      <div class="cc-col-head">…MagicBlocks…</div>
      <div class="cc-thread" data-cc-thread><!-- JS-populated --></div>
    </div>
  </div>

  <div class="cc-controls">
    <button data-cc-action="restart">Restart</button>
    <button data-cc-action="pause">Pause</button>
    <button data-cc-action="play">Play</button>
  </div>
</div>
/* Left column is desaturated via filter + warm-tinted surface; the
   asymmetry with the right column carries the entire message. */
.cc-cols   { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-5); }
.cc-col    { background: var(--bg-paper); border: 1px solid var(--hair); border-radius: var(--r-lg); }
.cc-col.generic { filter: saturate(0.55); background: color-mix(in oklab, var(--bg-paper) 92%, var(--warm-3)); }
/* MagicBlocks (right) column reads as the calm green alternative,
   not pink — pink-on-pink against a competitor felt aggressive/red. */
.cc-col.mb .cc-msg.out { background: var(--green-500); color: var(--paper); }

.cc-msg.is-visible { opacity: 1; transform: translateY(0); }
.cc-msg.dead-end { opacity: 0.55; font-style: italic; align-self: center; border: 1px dashed var(--hair); }

@media (max-width: 720px) { .cc-cols { grid-template-columns: 1fr; } }
@media (prefers-reduced-motion: reduce) {
  .cc-msg { opacity: 1 !important; transform: none !important; }
  .cc-typing { display: none !important; }
}

Competitor variants

.chat-comparison[data-cc-variant]

The left-column label + script swap by destination page. generic-chatbot (default) hard-stops with a link dump. closebot-style echoes back what the lead said and ends in a logged inquiry. chatbase-style answers the FAQ accurately but never converts. The right column always carries the MagicBlocks multi-turn close. Conversation scripts are page-specific (locked in the commission doc, Appendix B); the code-tabs here show the labelling contract.

Variant · generic-chatbot
Generic chatbot
Hey, what rates are you offering on a 30-year fixed right now?
Thanks for your interest! A loan specialist will reach out during business hours.
[link dump]
MagicBlocks
Booked
Hey, what rates are you offering on a 30-year fixed right now?
30-year fixed has been sitting at 6.75–7.0% this week for strong credit profiles. 15-min rate review tomorrow at 10:30?
10:30 works.
Done. Sarah Chen will call. ✓
Variant · closebot-style
CLOSEBOT-style workflow AI
Logged
Hey, what rates are you offering on a 30-year fixed right now?
Happy to help! What's your estimated credit score and loan amount?
Mid-700s, $420K.
Confirming: 30-year fixed for $420K, mid-700s credit. Is that correct?
Yes.
Got it — I've logged your inquiry.
MagicBlocks
Booked
Hey, what rates are you offering on a 30-year fixed right now?
30-year fixed at 6.75–7.0% for strong credit. Refi or purchase, rough loan amount, credit range?
Refi, $420K, mid-700s.
Strong profile — Sarah Chen tomorrow 10:30? She handles sub-45-day refi closes.
10:30.
Done. ✓
Variant · chatbase-style
CHATBASE-style FAQ AI
Answered
Hey, what rates are you offering on a 30-year fixed right now?
Great question! Current 30-year fixed rates vary by credit, loan amount, and other factors. See our rates page: [link]
Can I talk to someone?
Loan officers are available Mon–Fri, 9 AM–5 PM at [number].
MagicBlocks
Booked
Hey, what rates are you offering on a 30-year fixed right now?
30-year fixed at 6.75–7.0% for strong credit. Want me to put you on a 15-min rate review with one of our LOs this week?
Yes.
Tomorrow 10:30 or 2:45?
10:30.
Done. Sarah Chen will call. ✓
<!-- pick the variant per destination page -->
<div class="chat-comparison" data-chat-comparison
     data-cc-variant="generic-chatbot"   <!-- generic-chatbot | closebot-style | chatbase-style -->
     data-scenario="mortgage">           <!-- mortgage | insurance | solar | home-services | auto | fintech -->

  <div class="cc-cols">
    <div class="cc-col generic" data-cc-side="generic">
      <div class="cc-col-head">
        <!-- label swaps per data-cc-variant -->
        <div class="cc-col-name"><span class="dot"></span> Generic chatbot</div>
      </div>
      <div class="cc-thread">…JS-populated from MB_CC_SCRIPTS[variant][scenario].left…</div>
    </div>
    <div class="cc-col mb" data-cc-side="mb">
      <div class="cc-col-head"><div class="cc-col-name"><span class="dot"></span> MagicBlocks</div></div>
      <div class="cc-thread">…JS-populated from MB_CC_SCRIPTS[variant][scenario].right…</div>
    </div>
  </div>
</div>

<!-- destination-page mapping -->
<!-- /                  → data-cc-variant="generic-chatbot"  data-scenario="mortgage" -->
<!-- /compare/chatbots  → data-cc-variant="generic-chatbot"  data-scenario="mortgage" -->
<!-- /compare/closebot  → data-cc-variant="closebot-style"   data-scenario="mortgage" -->
<!-- /compare/chatbase  → data-cc-variant="chatbase-style"   data-scenario="mortgage" -->
// JSON data contract — register scripts once, the component picks
// the right pair based on data-cc-variant + data-scenario.
window.MB_CC_SCRIPTS = {
  "generic-chatbot": {
    "mortgage":      { "left": [/* dead-end script */], "right": [/* MB script */] },
    "insurance":     { "left": [...], "right": [...] }
    /* solar | home-services | auto | fintech */
  },
  "closebot-style":  { "mortgage": { "left": [/* echo-back loop */], "right": [...] } },
  "chatbase-style":  { "mortgage": { "left": [/* FAQ + handoff */],  "right": [...] } }
};

// Disclosure captions (rendered beneath each comparison)
window.MB_CC_DISCLOSURES = {
  "generic-chatbot": "Illustrative comparison. Rates shown are market ranges, not offers. Actual rates are determined by licensed loan officers based on full application.",
  "closebot-style":  "Illustrative comparison. Actual Closebot output varies. Both systems are real AI sales agent platforms; the comparison highlights architectural differences.",
  "chatbase-style":  "Illustrative comparison. Actual Chatbase output varies. Both systems are real conversational AI products in different categories."
};

11.20 Mini-atoms

Four tiny pieces that slot into the bigger components: the compliance trust row, a spinning channel orbit, an ignition spark, and an editorial stat badge.

Compliance row

.compliance-row
🔒 SOC 2 Type II 🛡 ISO 27001 TCPA Compliant 3,000+ edge servers 24/7/365

Channel orbit

.channel-orbit

Rotates 40s per revolution. Pairs with the engine block; can stand alone as decoration.

Spark

.spark

An 8-ray ignition glyph. Use inline where copy says "engine fires" or "moment arrives."

Stat badge

.stat-badge

Editorial big-number callout. Swap integer → Fraunces-italic suffix for a brand-signature beat.

Avg. response5sEvery lead, every time Contact liftvs 8–12 min response Task completion97.5%vs 59% single-prompt Reactivation70%of CRM leads never properly worked

Cost compare

.cost-compare

Two horizontal bars: cost to acquire a lead (tall, muted) vs cost to work that lead (short, brand-pink). The visual gap is the argument. Lives on the Pricing hero.

Cost per lead, per side of the funnel
Acquire
$30 – $80
Work
$2.50 – $4
5–10% of what you already spent
<div class="cost-compare" aria-label="Cost to acquire vs cost to work">
  <div class="cc-eyebrow">Cost per lead, per side of the funnel</div>
  <div class="cc-bars">
    <div class="cc-bar" style="--cc-pct: 100%;">
      <div class="cc-bar-label">Acquire</div>
      <div class="cc-bar-track"><div class="cc-bar-fill"></div></div>
      <div class="cc-bar-amount">$30 – $80</div>
    </div>
    <div class="cc-bar is-engine" style="--cc-pct: 6%;">
      <div class="cc-bar-label">Work</div>
      <div class="cc-bar-track"><div class="cc-bar-fill"></div></div>
      <div class="cc-bar-amount">$2.50 – $4</div>
    </div>
  </div>
  <div class="cc-gap-note"><strong>5–10%</strong> of what you already spent</div>
</div>
.cost-compare .cc-bar-fill { background: var(--warm-7); width: var(--cc-pct, 100%); }
.cost-compare .cc-bar.is-engine .cc-bar-fill {
  background: var(--accent);
  box-shadow: 0 0 0 1px color-mix(in oklab, var(--accent) 30%, transparent),
              0 4px 14px -6px color-mix(in oklab, var(--accent) 60%, transparent);
}

Stress scoreboard

.stress-scoreboard

Oversized stat-pair (97.5% vs 59%) dominating the visual, with a 1,000-lead-dot illustration showing failure-rate at scale. Ink surface for max contrast. Built to live on Built for Production §4. Each pink dot is one lead the single-prompt system dropped.

MagicBlocks
97.5%
complete every lead, end-to-end, at production scale.
vs
Single-prompt AI
59%
drops the rest somewhere between turn 3 and turn 30.
1,000 leads · single-prompt baseline
~590 complete (single-prompt baseline) ~410 dropped — the visible failure surface
<div class="stress-scoreboard" aria-label="Production-scale completion">
  <div class="ss-pair">
    <div class="ss-stat is-pass">
      <div class="ss-stat-eyebrow">MagicBlocks</div>
      <div class="ss-stat-num">97.5%</div>
      <div class="ss-stat-cap">complete every lead, end-to-end…</div>
    </div>
    <div class="ss-vs">vs</div>
    <div class="ss-stat is-fail">
      <div class="ss-stat-eyebrow">Single-prompt AI</div>
      <div class="ss-stat-num">59%</div>
      <div class="ss-stat-cap">drops the rest…</div>
    </div>
  </div>
  <div>
    <div class="ss-cap">1,000 leads · single-prompt baseline</div>
    <div class="ss-dots" data-stress-dots aria-hidden="true"></div>
  </div>
</div>

<script>
  // populate 1000 dots; ~410 fail
  document.querySelectorAll("[data-stress-dots]").forEach(el => {
    const total = 1000, failTarget = 410;
    const fails = new Set();
    while (fails.size < failTarget) fails.add(Math.floor(Math.random() * total));
    for (let i = 0; i < total; i++) {
      const s = document.createElement("span");
      if (fails.has(i)) s.className = "is-fail";
      el.appendChild(s);
    }
  });
</script>
.stress-scoreboard .ss-dots { display: grid; grid-template-columns: repeat(50, 1fr); gap: 2px; }
.stress-scoreboard .ss-dots span      { aspect-ratio: 1; border-radius: 50%; background: var(--green-500); opacity: 0.85; }
/* Failure dots use --error (red), not --accent (pink). Pass dots stay
   green. The brand pink would conflict with the bright pass-state and
   read as "neutral/decorative" rather than "this stress test failed". */
.stress-scoreboard .ss-dots span.is-fail { background: var(--error); opacity: 1; }

11.22 Inline illustrations

Five composite illustrations that live inline on specific page sections rather than as canonical components. They're hand-composed from brand-kit primitives (typography, tokens, SVG, conversation UI) — no shared API. Use them straight from this page, customise per destination.

Architecture compare

.architecture-compare

Single monolithic prompt vs modular multi-prompt stack. Used on How It Works §4 + Built for Production §3 + §9.

Single-prompt AI
Prompt + LLM handles everything · breaks at scale
MagicBlocks · modular
Intent router
Memory + context
Qualifier
Channel adapter
Compliance gate
Handoff & record

Knowledge flow

.knowledge-flow

Inward-converging diagram: knowledge sources → engine → conversational outputs. Used on How It Works §6.

CRM Rate sheets Compliance Brand voice Past convos
Chat SMS Voice Email

Demo vs production split

.demo-vs-prod-split

Side-by-side conversation pair: a polished demo turn (left) vs an unscripted production exchange (right). Used on Built for Production hero.

Demo · scripted
Hey, what rates are you seeing?
30-year fixed at 6.12%. Want me to pull what a refi would save you?
Sure
Booked Thursday 10:30. ✓
Production · unscripted, day 47
do u guys do reno loans for like a kitchen remodel
Welcome — yes, kitchen reno is a common renovation-loan use. Single-family home? Owner-occupied? Rough budget you're working with?
yeah single family own it. probably 35-45k
Got it. For a $35–45k kitchen reno on an owned single-family, two product paths fit cleanly: a HELOC or a personal-loan-for-home-improvement. Differ on rate vs flexibility. 15-min call with Sarah Chen tomorrow at 11 to walk through both?

Guardrails admin

.guardrails-admin

Admin UI mockup for the guardrails configuration screen — composed from Chapter 04 (Forms & Inputs) + Chapter 10 (Dashboard shell). Used on Built for Production §6.

Guardrails · Mortgage agent
Active · 247 conversations today

Voice waveform demo

.voice-waveform-demo

Animated waveform paired with transcript bubbles. Used on Channel · Voice hero.

Live · 2:14 +1 (415) 555-0142
LeadYeah hey I’m calling about the 2022 RAV4, is it still available?
EngineYes — silver, 38k miles, on the lot tonight. Looking to test drive Saturday?
LeadTen thirty Saturday works.

11.21 Anatomy & motion

How the narrative-systems chapter hangs together. Three shared primitives do most of the work; individual components only add what's unique to them.

Three shared primitives

  1. 1
    .reveal
    Adds fade-up on scroll-in. Stagger via inline --i: Ntransition-delay: calc(var(--i) * 60ms). Every chapter-11 component opts in with a single class.
  2. 2
    .svg-draw
    Complements .reveal. Any <path class="draw"> (or every path) gets a stroke-dasharraystroke-dashoffset transition. Tune per component with --draw-len.
  3. 3
    [data-count-to]
    Any element with this attribute counts up on reveal. Honours data-decimals and data-prefix / data-suffix. Used by stat badges and the ROI calculator.
  4. 4
    prefers-reduced-motion
    All three primitives short-circuit to the final state. No "frame-1" flashes. No spinning orbits. The _shared.js reveal loader also bails out, marking every element visible immediately.
  5. 5
    Tokens, not colours
    Every component references --accent, --ink, --warm-3, --hair, --industry-accent. The dark variants flip cleanly; industry colour-washes happen at the page level.