Chapter 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.

12.0 In action

A single conversation, the whole story. The flagship scenario is a mortgage refinance — chosen because the maths (rate, monthly saving, loan amount) makes the agent's intelligence concrete in a way SaaS pricing can't. 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 (mortgage refi)

.hero-scene

Lead started a refi quote two days ago; agent reaches out with the current rate, handles a points/loan-size question, quotes the monthly saving against the existing rate, and books the call — all under 30 seconds of human-feeling exchange. Annotations on the right reveal what MagicBlocks knew at each turn.

Intent signal Refi calc · 2× · draft app
Hi Mike 👋 Saw you started a refi quote on Saturday. Quick update — 30-yr fixed is at 6.45% today, just under your last view (6.51%). Worth 5 mins?
is that with points
No-points, owner-occupied, 740+ FICO. Loan around $420k like your draft?
closer to 450
At $450k / 6.45% you'd save ~$280/mo vs your current 7.10%. 15-min call with a loan officer to lock it?
today if possible
Two slots open today:
2:30 works
✓ Booked with Priya · today 2:30 PM. Calendar invite + your draft application sent.
<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">
        <span class="hs-trigger-dot"></span>
        <span class="hs-trigger-title">Intent signal</span>
        <span class="hs-trigger-sub">Refi calc · 2× · draft app</span>
      </div>
      <div class="hs-thread" data-thread>
        <div class="hs-msg out" data-step="m1">Hi Mike 👋 Saw you started a refi quote on Saturday. Quick update — 30-yr fixed is at <strong>6.45%</strong> today, just under your last view (6.51%). Worth 5 mins?</div>
        <div class="hs-msg in"  data-step="m2">is that with points</div>
        <div class="hs-msg out" data-step="m3">No-points, owner-occupied, 740+ FICO. Loan around $420k like your draft?</div>
        <div class="hs-msg in"  data-step="m4">closer to 450</div>
        <div class="hs-msg out" data-step="m5">At $450k / 6.45% you'd save <strong>~$280/mo</strong> vs your current 7.10%. 15-min call with a loan officer to lock it?</div>
        <div class="hs-msg in"  data-step="m6">today if possible</div>
        <div class="hs-msg out hs-msg-card" data-step="m7">
          <span class="hs-cal-head">Two slots open today:</span>
          <div class="hs-cal">
            <button type="button" class="hs-cal-slot">
              <span class="hs-cal-day">Today</span><span class="hs-cal-time">2:30 PM</span>
            </button>
            <button type="button" class="hs-cal-slot">
              <span class="hs-cal-day">Today</span><span class="hs-cal-time">4:00 PM</span>
            </button>
          </div>
        </div>
        <div class="hs-msg in"  data-step="m8">2:30 works</div>
        <div class="hs-msg out hs-msg-booked" data-step="m9">✓ Booked with Priya · today 2:30 PM. Calendar invite + your draft application sent.</div>
        <!-- Typing indicator — JS toggles [hidden] between bubbles -->
        <div class="hs-typing" data-typing hidden><span></span><span></span><span></span></div>
      </div>
    </div>
  </div>
  <aside class="hs-annotations">
    <div class="hs-ann" data-ann="a1"><span class="hs-ann-k">Last intent</span><span class="hs-ann-v">30-yr fixed · 2 visits</span></div>
    <div class="hs-ann" data-ann="a2"><span class="hs-ann-k">Loan stage</span><span class="hs-ann-v">Draft app · day 2</span></div>
    <div class="hs-ann" data-ann="a3"><span class="hs-ann-k">Existing rate</span><span class="hs-ann-v">7.10% · current loan</span></div>
    <div class="hs-ann" data-ann="a4"><span class="hs-ann-k">Preferred</span><span class="hs-ann-v">SMS · evenings</span></div>
  </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; }
}
import { HeroScene } from "@magicblocksai/ui";

<HeroScene
  triggerTitle="Intent signal"
  triggerSub="Refi calc · 2× · draft app"
  messages={[
    { step: "m1", from: "out", text: "Hi Mike — saw you started a refi quote on Saturday." },
    { step: "m2", from: "in",  text: "is that with points" },
    // …m3–m9 including a calendar slot card and a booked confirmation
  ]}
  annotations={[
    { id: "a1", kicker: "Last intent",   value: "30-yr fixed · 2 visits" },
    { id: "a2", kicker: "Loan stage",    value: "Draft app · day 2" },
    { id: "a3", kicker: "Existing rate", value: "7.10% · current loan" },
    { id: "a4", kicker: "Preferred",     value: "SMS · evenings" },
  ]}
/>

12.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">
      <circle class="marker-ring" cx="90" cy="65" r="9"/>
      <circle class="marker-dot"  cx="90" cy="65" r="4"/>
    </g>
    <g class="marker lost">
      <circle class="marker-ring" cx="540" cy="265" r="9"/>
      <circle class="marker-dot"  cx="540" cy="265" r="4"/>
    </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; }
import { DecayCurve } from "@magicblocksai/ui";

<DecayCurve
  ariaLabel="Lead intent decays within minutes of form submission"
  wonTitle="MagicBlocks · 5 sec"
  wonSub="CONVERSATION BEGINS"
  lostTitle="Your team · 10 min"
  lostSub="CONTACT RATE DOWN 80%"
/>

Dormant-lead mine — database reactivator

.dormant-mine

The other half of the story. Most of your CRM is dormant inquiries nobody had time to follow up on — real revenue sitting in boxes. The component plays a continuous 14s loop: each row of a mortgage CRM cycles cold → scanning → drafting → sent → replied, with a pink scan-sweep visualising the agent reaching the row. Pure CSS — no JS state. Adapted from the “Mining for Leads · V1 Database” design.

Dormant inquiries · Mortgage CRM
7 of 1,284 shown Mining
Revenue recovered
$482,400
7 leads re-engaged
MR
Marcus Rivera
30-yr fixed inquiry
$485k
47 days0 days
Cold · 47dScanning…Drafting…SentReplied
PS
Priya Shah
Refi quote, no follow-up
$312k
62 days0 days
Cold · 62dScanning…Drafting…SentReplied
JB
Jordan Bell
Pre-approval, ghosted
$640k
28 days0 days
Cold · 28dScanning…Drafting…SentReplied
SN
Sara Nakamura
FHA, partial app
$295k
84 days0 days
Cold · 84dScanning…Drafting…SentReplied
DA
Diego Alvarez
Investor, paused
$1.2M
51 days0 days
Cold · 51dScanning…Drafting…SentReplied
LO
Lena Okafor
Cash-out refi inquiry
$418k
39 days0 days
Cold · 39dScanning…Drafting…SentReplied
TW
Tom Wallace
First-time buyer
$365k
73 days0 days
Cold · 73dScanning…Drafting…SentReplied

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">
  <div class="dm-bar">
    <div class="dm-titles">
      <div class="dm-title">Dormant inquiries · Mortgage CRM</div>
      <div class="dm-meta">
        <span class="dm-token">7 of 1,284 shown</span>
        <span class="dm-token dm-token-live"><span class="dm-livedot"></span>Mining</span>
      </div>
    </div>
    <div class="dm-revchip">
      <div class="dm-revchip-label">Revenue recovered</div>
      <div class="dm-revchip-value">$482,400</div>
      <div class="dm-revchip-delta">▲ 7 leads re-engaged</div>
    </div>
  </div>
  <div class="dm-table">
    <div class="dm-cols"><div>Lead</div><div>Loan</div><div>Last touch</div><div>Status</div></div>
    <div class="dm-rows">
      <!-- 7 rows; each <div class="dm-row" style="--i:N"> has 4 cells +
           a <div class="dm-scan-sweep">. The pill stacks 5 status spans
           (lbl-cold/scan/draft/sent/ok) — only one is visible at a time
           per the row's animation phase. -->
      <div class="dm-row" style="--i:0">
        <div class="dm-name">
          <div class="dm-avatar">MR</div>
          <div>
            <div class="dm-name-text">Marcus Rivera</div>
            <div class="dm-stage-text">30-yr fixed inquiry</div>
          </div>
        </div>
        <div class="dm-amount">$485k</div>
        <div class="dm-last"><span class="dm-last-stack"><span class="last-cold">47 days</span><span class="last-fresh">0 days</span></span></div>
        <div class="dm-status">
          <span class="dm-pill">
            <span class="dm-pill-dot"></span>
            <span class="dm-pill-lbl">
              <span class="lbl-cold">Cold · 47d</span>
              <span class="lbl-scan">Scanning…</span>
              <span class="lbl-draft">Drafting…</span>
              <span class="lbl-sent">Sent</span>
              <span class="lbl-ok">Replied</span>
            </span>
          </span>
        </div>
        <div class="dm-scan-sweep"></div>
      </div>
      <div class="dm-row" style="--i:1">
        <div class="dm-name"><div class="dm-avatar">PS</div><div><div class="dm-name-text">Priya Shah</div><div class="dm-stage-text">Refi quote, no follow-up</div></div></div>
        <div class="dm-amount">$312k</div>
        <div class="dm-last"><span class="dm-last-stack"><span class="last-cold">62 days</span><span class="last-fresh">0 days</span></span></div>
        <div class="dm-status"><span class="dm-pill"><span class="dm-pill-dot"></span><span class="dm-pill-lbl"><span class="lbl-cold">Cold · 62d</span><span class="lbl-scan">Scanning…</span><span class="lbl-draft">Drafting…</span><span class="lbl-sent">Sent</span><span class="lbl-ok">Replied</span></span></span></div>
        <div class="dm-scan-sweep"></div>
      </div>
      <div class="dm-row" style="--i:2">
        <div class="dm-name"><div class="dm-avatar">JB</div><div><div class="dm-name-text">Jordan Bell</div><div class="dm-stage-text">Pre-approval, ghosted</div></div></div>
        <div class="dm-amount">$640k</div>
        <div class="dm-last"><span class="dm-last-stack"><span class="last-cold">28 days</span><span class="last-fresh">0 days</span></span></div>
        <div class="dm-status"><span class="dm-pill"><span class="dm-pill-dot"></span><span class="dm-pill-lbl"><span class="lbl-cold">Cold · 28d</span><span class="lbl-scan">Scanning…</span><span class="lbl-draft">Drafting…</span><span class="lbl-sent">Sent</span><span class="lbl-ok">Replied</span></span></span></div>
        <div class="dm-scan-sweep"></div>
      </div>
      <div class="dm-row" style="--i:3">
        <div class="dm-name"><div class="dm-avatar">SN</div><div><div class="dm-name-text">Sara Nakamura</div><div class="dm-stage-text">FHA, partial app</div></div></div>
        <div class="dm-amount">$295k</div>
        <div class="dm-last"><span class="dm-last-stack"><span class="last-cold">84 days</span><span class="last-fresh">0 days</span></span></div>
        <div class="dm-status"><span class="dm-pill"><span class="dm-pill-dot"></span><span class="dm-pill-lbl"><span class="lbl-cold">Cold · 84d</span><span class="lbl-scan">Scanning…</span><span class="lbl-draft">Drafting…</span><span class="lbl-sent">Sent</span><span class="lbl-ok">Replied</span></span></span></div>
        <div class="dm-scan-sweep"></div>
      </div>
      <div class="dm-row" style="--i:4">
        <div class="dm-name"><div class="dm-avatar">DA</div><div><div class="dm-name-text">Diego Alvarez</div><div class="dm-stage-text">Investor, paused</div></div></div>
        <div class="dm-amount">$1.2M</div>
        <div class="dm-last"><span class="dm-last-stack"><span class="last-cold">51 days</span><span class="last-fresh">0 days</span></span></div>
        <div class="dm-status"><span class="dm-pill"><span class="dm-pill-dot"></span><span class="dm-pill-lbl"><span class="lbl-cold">Cold · 51d</span><span class="lbl-scan">Scanning…</span><span class="lbl-draft">Drafting…</span><span class="lbl-sent">Sent</span><span class="lbl-ok">Replied</span></span></span></div>
        <div class="dm-scan-sweep"></div>
      </div>
      <div class="dm-row" style="--i:5">
        <div class="dm-name"><div class="dm-avatar">LO</div><div><div class="dm-name-text">Lena Okafor</div><div class="dm-stage-text">Cash-out refi inquiry</div></div></div>
        <div class="dm-amount">$418k</div>
        <div class="dm-last"><span class="dm-last-stack"><span class="last-cold">39 days</span><span class="last-fresh">0 days</span></span></div>
        <div class="dm-status"><span class="dm-pill"><span class="dm-pill-dot"></span><span class="dm-pill-lbl"><span class="lbl-cold">Cold · 39d</span><span class="lbl-scan">Scanning…</span><span class="lbl-draft">Drafting…</span><span class="lbl-sent">Sent</span><span class="lbl-ok">Replied</span></span></span></div>
        <div class="dm-scan-sweep"></div>
      </div>
      <div class="dm-row" style="--i:6">
        <div class="dm-name"><div class="dm-avatar">TW</div><div><div class="dm-name-text">Tom Wallace</div><div class="dm-stage-text">First-time buyer</div></div></div>
        <div class="dm-amount">$365k</div>
        <div class="dm-last"><span class="dm-last-stack"><span class="last-cold">73 days</span><span class="last-fresh">0 days</span></span></div>
        <div class="dm-status"><span class="dm-pill"><span class="dm-pill-dot"></span><span class="dm-pill-lbl"><span class="lbl-cold">Cold · 73d</span><span class="lbl-scan">Scanning…</span><span class="lbl-draft">Drafting…</span><span class="lbl-sent">Sent</span><span class="lbl-ok">Replied</span></span></span></div>
        <div class="dm-scan-sweep"></div>
      </div>
    </div>
  </div>
  <p class="dm-cap">Dormant leads are revenue sitting in boxes…</p>
</div>
/* All animations driven by ONE 14s loop; per-row --i (0..6) staggers by 1.4s.
   Same delay drives row bg, avatar, pill bg/dot, the 5 stacked labels,
   and the pink scan-sweep so they stay in lock-step. */
.dormant-mine .dm-row {
  animation: dm-row-cycle 14s linear infinite;
  animation-delay: calc(var(--i, 0) * -1.4s);
}
@keyframes dm-row-cycle {
  0%, 9%   { background: var(--paper); }                                   /* cold */
  12%, 18% { background: color-mix(in oklab, var(--accent) 6%, var(--paper)); }   /* scanning */
  21%, 31% { background: color-mix(in oklab, var(--accent) 7%, var(--paper)); }   /* drafting */
  34%, 44% { background: color-mix(in oklab, var(--info) 6%, var(--paper)); }     /* sent */
  47%, 92% { background: color-mix(in oklab, var(--green-500) 9%, var(--paper)); } /* replied */
  100%     { background: var(--paper); }
}
/* The 5 status labels share grid-area so they stack; only one is opaque
   at a time per the row's phase. */
.dormant-mine .dm-pill-lbl > span { grid-area: 1 / 1; opacity: 0;
  animation: 14s linear infinite;
  animation-delay: calc(var(--i, 0) * -1.4s); }
@keyframes dm-lbl-cold  { 0%, 9%  { opacity: 1; } 10%, 100% { opacity: 0; } }
@keyframes dm-lbl-scan  { 0%, 9%  { opacity: 0; } 12%, 18% { opacity: 1; } 19%, 100% { opacity: 0; } }
@keyframes dm-lbl-draft { 0%, 18% { opacity: 0; } 21%, 31% { opacity: 1; } 32%, 100% { opacity: 0; } }
@keyframes dm-lbl-sent  { 0%, 31% { opacity: 0; } 34%, 44% { opacity: 1; } 45%, 100% { opacity: 0; } }
@keyframes dm-lbl-ok    { 0%, 44% { opacity: 0; } 47%, 92% { opacity: 1; } 95%, 100% { opacity: 0; } }
/* Pink scan-sweep that runs L→R across each row during the scan phase. */
.dormant-mine .dm-scan-sweep {
  position: absolute; top: 0; bottom: 0; left: 0; width: 70px;
  background: linear-gradient(90deg, transparent,
    color-mix(in oklab, var(--accent) 32%, transparent) 50%, transparent);
  opacity: 0;
  animation: dm-sweep 14s linear infinite;
  animation-delay: calc(var(--i, 0) * -1.4s);
}
@keyframes dm-sweep {
  0%, 9%   { left: 0;    opacity: 0; }
  10%      { left: 0;    opacity: 1; }
  19%      { left: 100%; opacity: 1; }
  20%, 100%{ left: 100%; opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  /* End-state snapshot — every row replied. Same story without motion. */
  .dormant-mine .dm-row,
  .dormant-mine .dm-avatar,
  .dormant-mine .dm-pill,
  .dormant-mine .dm-pill-dot,
  .dormant-mine .dm-pill-lbl > span,
  .dormant-mine .dm-scan-sweep { animation: none; }
  .dormant-mine .dm-pill-lbl .lbl-ok { opacity: 1; }
}
import { DormantMine } from "@magicblocksai/ui";

<DormantMine
  title="Dormant inquiries · Mortgage CRM"
  totalLabel="7 of 1,284 shown"
  revChipLabel="Revenue recovered"
  revChipValue="$482,400"
  revChipDelta="7 leads re-engaged"
  rows={[
    { id: "mr", initials: "MR", name: "Marcus Rivera", stage: "30-yr fixed inquiry", amount: "$485k", lastTouch: "47 days" },
    // …6 more rows; rows cycle cold → scanning → drafting → sent → replied
  ]}
  caption="Dormant leads are revenue sitting in boxes."
/>

12.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>
  <div class="sb-row"><div class="label">Contact rate</div><div class="bad">15–20%</div><div class="good">2–4× higher</div></div>
  <div class="sb-row"><div class="label">Follow-up consistency</div><div class="bad">Drops off by day 3</div><div class="good">Every lead, every time</div></div>
  <div class="sb-row"><div class="label">Qualification</div><div class="bad">Manual, inconsistent</div><div class="good">Automatic, pre-handoff</div></div>
  <div class="sb-row"><div class="label">Task completion</div><div class="bad">59% (single-prompt)</div><div class="good">97.5% (multi-prompt)</div></div>
  <div class="sb-row"><div class="label">Hallucination rate</div><div class="bad">Unbounded</div><div class="good">Guardian-gated</div></div>
</div>
<div class="scoreboard-foot">Stress-tested across 400 simulated sessions · <strong>Z = 9.33</strong> · <em>p < 0.00001</em></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; }
import { Scoreboard } from "@magicblocksai/ui";

<Scoreboard
  tone="light"  // or "dark" for the war-room variant
  badHeading="Most teams"
  goodHeading="With MagicBlocks"
  rows={[
    { id: "rt", label: "Response time", bad: "8–12 minutes", good: "Under 5 seconds" },
    { id: "cr", label: "Contact rate",  bad: "15–20%",       good: "2–4× higher" },
    // …more rows
  ]}
  footer={<>Stress-tested across 400 sessions · <strong>Z = 9.33</strong></>}
/>

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

12.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>

  <div class="rt-tick rt-left">
    <span class="rt-stamp">9:55 AM</span>
    <div class="rt-body">Rep reads notification<em>Between calls</em></div>
  </div>
  <div class="rt-tick rt-right win">
    <span class="rt-stamp">9:47:08</span>
    <div class="rt-body"><strong>Personalised conversation begins</strong><em>Across SMS</em></div>
  </div>

  <div class="rt-tick rt-left">
    <span class="rt-stamp">10:12 AM</span>
    <div class="rt-body">Rep calls back — no answer<em>Leaves voicemail</em></div>
  </div>
  <div class="rt-tick rt-right win">
    <span class="rt-stamp">9:48:30</span>
    <div class="rt-body"><strong>Lead qualified</strong><em>HAPPA checkpoints cleared</em></div>
  </div>

  <div class="rt-tick rt-left">
    <span class="rt-stamp">2:30 PM</span>
    <div class="rt-body">Lead went with competitor<em>Deal lost</em></div>
  </div>
  <div class="rt-tick rt-right win final">
    <span class="rt-stamp">9:49:15</span>
    <div class="rt-body"><strong>Appointment booked</strong><em>Handed off with full context</em></div>
  </div>
</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: "✓"; }
import { RaceTimeline } from "@magicblocksai/ui";

<RaceTimeline
  leftHeading="Without MagicBlocks"
  rightHeading="With MagicBlocks"
  pairs={[
    {
      id: "submit",
      leftStamp:  "9:47 AM",  leftBody: "Lead submits form", leftMeta: "Enters CRM queue",
      rightStamp: "9:47:00",  rightBody: <strong>Lead submits</strong>, rightMeta: "Engine fires",
    },
    // …more pairs; the last pair carries `final: true`
  ]}
/>

12.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"><span class="ico">◼</span>Website forms</div>
    <div class="engine-source"><span class="ico">◼</span>Facebook / Instagram</div>
    <div class="engine-source"><span class="ico">◼</span>Google Ads</div>
    <div class="engine-source"><span class="ico">◼</span>Marketplaces</div>
    <div class="engine-source"><span class="ico">◼</span>CRM backfill</div>
  </div>
  <div class="engine-centre">
    <svg class="engine-orbit" viewBox="0 0 320 320">
      <g class="orbit-anim">
        <circle class="ring" cx="160" cy="160" r="140"/>
        <g class="channel"><circle class="bg" cx="160" cy="20"  r="20"/><text class="lbl" x="160" y="20">SMS</text></g>
        <g class="channel"><circle class="bg" cx="300" cy="160" r="20"/><text class="lbl" x="300" y="160">MAIL</text></g>
        <g class="channel"><circle class="bg" cx="160" cy="300" r="20"/><text class="lbl" x="160" y="300">CHAT</text></g>
        <g class="channel"><circle class="bg" cx="20"  cy="160" r="20"/><text class="lbl" x="20"  y="160">VOICE</text></g>
      </g>
    </svg>
    <div class="engine-core">
      <div class="label">The engine</div>
      <div class="name">MagicBlocks<br><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 <span class="ico">→</span></div>
    <div class="engine-output">Booked appointments <span class="ico">→</span></div>
    <div class="engine-output">Context-rich handoffs <span class="ico">→</span></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; } }
import { EngineBlock } from "@magicblocksai/ui";

<EngineBlock
  sources={["Website forms", "Facebook / Instagram", "Google Ads", "Marketplaces", "CRM backfill"]}
  outputs={[
    <>Qualified conversations <span className="ico"></span></>,
    <>Booked appointments <span className="ico"></span></>,
    <>Context-rich handoffs <span className="ico"></span></>,
  ]}
  coreLabel="The engine"
  coreName="MagicBlocks"
  coreSubline="conversion engine"
  pills={["Engage", "Qualify", "Follow up", "Reengage"]}
/>

12.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 viewBox="0 0 24 24" aria-hidden="true"><path d="M4 12h16m-6-6 6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
  <div class="panel">
    <div class="step">02 · Run</div>
    <h4>The engine takes over</h4>
    <p>Instant, personalised conversations on every channel. Qualifies, nurtures, follows up.</p>
    <div class="art"><span class="stat-badge"><span class="eyebrow">Avg.</span><span class="num">5<sup>s</sup></span><span class="cap">First response</span></span></div>
  </div>
  <div class="arrow"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 12h16m-6-6 6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
  <div class="panel">
    <div class="step">03 · Close</div>
    <h4>Your team closes deals</h4>
    <p>Pre-qualified, warm handoffs with full context. No more cold calls.</p>
    <div class="art"><span class="stat-badge"><span class="eyebrow">Task completion</span><span class="num">97.5<sup>%</sup></span><span class="cap">vs 59% single-prompt</span></span></div>
  </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); } }
import { Triptych } from "@magicblocksai/ui";

<Triptych
  panels={[
    { id: "connect", step: "01 · Connect", title: "Plug in your leads",
      body: "Forms, marketplaces, ad platforms, your CRM. Setup takes days." },
    { id: "run",     step: "02 · Run",     title: "The engine takes over",
      body: "Instant, personalised conversations on every channel." },
    { id: "close",   step: "03 · Close",   title: "Your team closes deals",
      body: "Pre-qualified, warm handoffs with full context." },
  ]}
/>

12.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
<!-- 1. Mortgage loan officer (default accent) -->
<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>
    <span class="ho-tag">Prefers SMS</span>
  </div>
  <div class="ho-facts">
    <div class="ho-fact"><div class="k">Income</div><div class="v">$145k</div></div>
    <div class="ho-fact"><div class="k">Timeline</div><div class="v"><em>4–8 weeks</em></div></div>
    <div class="ho-fact"><div class="k">Credit band</div><div class="v">740+</div></div>
    <div class="ho-fact"><div class="k">Intent score</div><div class="v">92</div></div>
  </div>
  <div class="ho-cta">
    <button class="primary">Call now</button>
    <button>Send rate</button>
    <button>Notes</button>
  </div>
</article>

<!-- 2. Insurance agent (green accent) -->
<article class="handoff-card reveal" data-role="insurance-agent">
  <span class="ho-pulse" aria-hidden="true"></span>
  <div class="ho-band">
    <span class="ho-av">PR</span>
    <div class="ho-who">
      <p class="name">Priya Ramanathan</p>
      <p class="role">Insurance · Agent</p>
    </div>
    <span class="ho-stamp">16:02</span>
  </div>
  <div class="ho-tags">
    <span class="ho-tag lead">Qualified</span>
    <span class="ho-tag">Auto + Home</span>
    <span class="ho-tag">Bundling quote</span>
    <span class="ho-tag">Prefers email</span>
  </div>
  <div class="ho-facts">
    <div class="ho-fact"><div class="k">Current carrier</div><div class="v">State Farm</div></div>
    <div class="ho-fact"><div class="k">Renewal date</div><div class="v">23 days</div></div>
    <div class="ho-fact"><div class="k">Premium</div><div class="v">$2,140/yr</div></div>
    <div class="ho-fact"><div class="k">Intent score</div><div class="v">81</div></div>
  </div>
  <div class="ho-cta">
    <button class="primary">Send quote</button>
    <button>Schedule</button>
  </div>
</article>

<!-- 3. Booking specialist (warm-orange accent) -->
<article class="handoff-card reveal" data-role="booking-specialist">
  <span class="ho-pulse" aria-hidden="true"></span>
  <div class="ho-band">
    <span class="ho-av">MK</span>
    <div class="ho-who">
      <p class="name">Marcus Kim</p>
      <p class="role">Tourism · Booking specialist</p>
    </div>
    <span class="ho-stamp">08:14</span>
  </div>
  <div class="ho-tags">
    <span class="ho-tag lead">Qualified</span>
    <span class="ho-tag">Family of 4</span>
    <span class="ho-tag">2 nights</span>
    <span class="ho-tag">Dec 18</span>
  </div>
  <div class="ho-facts">
    <div class="ho-fact"><div class="k">Party size</div><div class="v">4</div></div>
    <div class="ho-fact"><div class="k">Budget</div><div class="v">$1,800</div></div>
    <div class="ho-fact"><div class="k">Prior guest</div><div class="v"><em>yes</em></div></div>
    <div class="ho-fact"><div class="k">Intent score</div><div class="v">96</div></div>
  </div>
  <div class="ho-cta">
    <button class="primary">Confirm booking</button>
    <button>Upgrade</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; }
import { HandoffCard } from "@magicblocksai/ui";

// 1. Mortgage loan officer (default accent)
<HandoffCard
  role="mortgage-lo"
  initials="TS"
  name="Taylor Simmons"
  roleLabel="Mortgage · Loan officer"
  stamp="09:49"
  tags={["Qualified", "$450k", "30-yr fixed", "Prefers SMS"]}
  facts={[
    { id: "income",   k: "Income",       v: "$145k" },
    { id: "timeline", k: "Timeline",     v: <em>4–8 weeks</em> },
    { id: "credit",   k: "Credit band",  v: "740+" },
    { id: "score",    k: "Intent score", v: "92" },
  ]}
  actions={[
    { id: "call",  label: "Call now",  primary: true },
    { id: "rate",  label: "Send rate" },
    { id: "notes", label: "Notes" },
  ]}
/>

// 2. Insurance agent (green accent)
<HandoffCard
  role="insurance-agent"
  initials="PR"
  name="Priya Ramanathan"
  roleLabel="Insurance · Agent"
  stamp="16:02"
  tags={["Qualified", "Auto + Home", "Bundling quote", "Prefers email"]}
  facts={[
    { id: "carrier", k: "Current carrier", v: "State Farm" },
    { id: "renewal", k: "Renewal date",    v: "23 days" },
    { id: "premium", k: "Premium",         v: "$2,140/yr" },
    { id: "score",   k: "Intent score",    v: "81" },
  ]}
  actions={[
    { id: "quote",    label: "Send quote", primary: true },
    { id: "schedule", label: "Schedule" },
  ]}
/>

// 3. Booking specialist (warm-orange accent)
<HandoffCard
  role="booking-specialist"
  initials="MK"
  name="Marcus Kim"
  roleLabel="Tourism · Booking specialist"
  stamp="08:14"
  tags={["Qualified", "Family of 4", "2 nights", "Dec 18"]}
  facts={[
    { id: "party",    k: "Party size",   v: "4" },
    { id: "budget",   k: "Budget",       v: "$1,800" },
    { id: "prior",    k: "Prior guest",  v: <em>yes</em> },
    { id: "score",    k: "Intent score", v: "96" },
  ]}
  actions={[
    { id: "confirm", label: "Confirm booking", primary: true },
    { id: "upgrade", label: "Upgrade" },
  ]}
/>

12.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
<!-- 1. Growing memory · auto-cycles 0 → 5 -->
<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 · SMS</span><span class="meta">Day 1</span></div>
    <div class="pc-row"><span class="ico">!</span><span>Raised pricing concern · resolved</span><span class="meta">Day 1</span></div>
    <div class="pc-row"><span class="ico">☎</span><span>Prefers SMS over email</span><span class="meta">Day 2</span></div>
    <div class="pc-row"><span class="ico">◷</span><span>Mentioned "after holidays" timeline</span><span class="meta">Day 3</span></div>
    <div class="pc-row"><span class="ico">✓</span><span>Intent score raised to 87</span><span class="meta">Day 4</span></div>
  </div>
  <div class="pc-status"><span>Active relationship</span><span>5 / 5 signals</span></div>
</div>

<!-- 2. Dormant · 3/5 bars filled, desaturated -->
<div class="profile-card reveal is-dormant">
  <header class="pc-head">
    <span class="pc-av">JK</span>
    <div><p class="pc-name">Jae Kim</p><p class="pc-email">jae@k.example</p></div>
  </header>
  <div class="pc-bars">
    <span class="pc-bar" style="background: var(--accent);"></span>
    <span class="pc-bar" style="background: var(--accent);"></span>
    <span class="pc-bar" style="background: var(--accent);"></span>
    <span class="pc-bar"></span>
    <span class="pc-bar"></span>
  </div>
  <div class="pc-rows">
    <div class="pc-row" style="opacity: 1; transform: none;"><span class="ico">✎</span><span>Requested rate quote · March</span><span class="meta">Day 1</span></div>
    <div class="pc-row" style="opacity: 1; transform: none;"><span class="ico">◷</span><span>Went quiet after pre-approval</span><span class="meta">Day 4</span></div>
  </div>
  <div class="pc-status"><span>Dormant · 47 days</span><span>Queued for reactivation</span></div>
</div>

<!-- 3. Reactivated · all 5 bars + accent ring -->
<div class="profile-card reveal is-reactivated">
  <header class="pc-head">
    <span class="pc-av">JK</span>
    <div><p class="pc-name">Jae Kim</p><p class="pc-email">jae@k.example</p></div>
  </header>
  <div class="pc-bars">
    <span class="pc-bar" style="background: var(--accent);"></span>
    <span class="pc-bar" style="background: var(--accent);"></span>
    <span class="pc-bar" style="background: var(--accent);"></span>
    <span class="pc-bar" style="background: var(--accent);"></span>
    <span class="pc-bar" style="background: var(--accent);"></span>
  </div>
  <div class="pc-rows">
    <div class="pc-row" style="opacity: 1; transform: none;"><span class="ico">⚡</span><span>Rate-drop trigger · sent SMS</span><span class="meta">Day 48</span></div>
    <div class="pc-row" style="opacity: 1; transform: none;"><span class="ico">✓</span><span>Replied within 6 min</span><span class="meta">Day 48</span></div>
    <div class="pc-row" style="opacity: 1; transform: none;"><span class="ico">!</span><span>Scheduled recap call</span><span class="meta">Day 48</span></div>
  </div>
  <div class="pc-status"><span>Reactivated</span><span>Back in pipeline</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); }
import { ProfileCard } from "@magicblocksai/ui";

// 1. Growing memory · auto-cycles 0 → 5 ticks
<ProfileCard
  state="growing"
  initials="MS"
  name="Morgan Shaw"
  email="morgan@sh.example"
  autoCycle
  rows={[
    { id: "r1", icon: "✎", text: "First message at 09:47 · SMS",        meta: "Day 1" },
    { id: "r2", icon: "!", text: "Raised pricing concern · resolved",     meta: "Day 1" },
    { id: "r3", icon: "☎", text: "Prefers SMS over email",                meta: "Day 2" },
    { id: "r4", icon: "◷", text: "Mentioned \"after holidays\" timeline",   meta: "Day 3" },
    { id: "r5", icon: "✓", text: "Intent score raised to 87",             meta: "Day 4" },
  ]}
  statusLeft="Active relationship"
  statusRight="5 / 5 signals"
/>

// 2. Dormant · 3/5 bars filled, desaturated
<ProfileCard
  state="dormant"
  initials="JK"
  name="Jae Kim"
  email="jae@k.example"
  filledBars={3}
  rows={[
    { id: "r1", icon: "✎", text: "Requested rate quote · March",  meta: "Day 1" },
    { id: "r2", icon: "◷", text: "Went quiet after pre-approval", meta: "Day 4" },
  ]}
  statusLeft="Dormant · 47 days"
  statusRight="Queued for reactivation"
/>

// 3. Reactivated · all 5 bars + accent ring
<ProfileCard
  state="reactivated"
  initials="JK"
  name="Jae Kim"
  email="jae@k.example"
  filledBars={5}
  rows={[
    { id: "r1", icon: "⚡", text: "Rate-drop trigger · sent SMS", meta: "Day 48" },
    { id: "r2", icon: "✓", text: "Replied within 6 min",         meta: "Day 48" },
    { id: "r3", icon: "!", text: "Scheduled recap call",         meta: "Day 48" },
  ]}
  statusLeft="Reactivated"
  statusRight="Back in pipeline"
/>

12.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" aria-hidden="true">
    <line x1="260" y1="260" x2="260" y2="30"/>
    <line x1="260" y1="260" x2="420" y2="95"/>
    <line x1="260" y1="260" x2="500" y2="260"/>
    <line x1="260" y1="260" x2="420" y2="425"/>
    <line x1="260" y1="260" x2="260" y2="490"/>
    <line x1="260" y1="260" x2="100" y2="425"/>
    <line x1="260" y1="260" x2="20"  y2="260"/>
    <line x1="260" y1="260" x2="100" y2="95"/>
  </svg>
  <div class="hub-core">
    <img src="/02-icon/svg/magicblocks-icon-mono-white.svg" alt="MagicBlocks" aria-label="MagicBlocks engine"/>
  </div>
  <div class="nodes">
    <span class="node" style="top: 6%;  left: 50%;">Salesforce</span>
    <span class="node" style="top: 18%; left: 81%;">HubSpot</span>
    <span class="node" style="top: 50%; left: 96%;">Twilio</span>
    <span class="node" style="top: 82%; left: 81%;">Calendly</span>
    <span class="node" style="top: 94%; left: 50%;">Zapier</span>
    <span class="node" style="top: 82%; left: 19%;">Segment</span>
    <span class="node" style="top: 50%; left: 4%;">Stripe</span>
    <span class="node" style="top: 18%; left: 19%;">Slack</span>
  </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; }
import { IntegrationHub } from "@magicblocksai/ui";

<IntegrationHub
  core={<img src="/02-icon/svg/magicblocks-icon-mono-white.svg" alt="MagicBlocks" />}
  nodes={[
    { id: "salesforce", label: "Salesforce", top: "6%",  left: "50%" },
    { id: "hubspot",    label: "HubSpot",    top: "18%", left: "81%" },
    // …8 chips total at ring positions
  ]}
/>

12.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
<div class="happa-arc reveal svg-draw" style="--draw-len: 1400;">
  <svg viewBox="0 0 1200 320" role="img" aria-label="HAPPA arc">
    <path class="arc-path draw" d="M 120 170 C …"/>
    <g transform="translate(120 170)">
      <circle class="node-ring" r="26"/>
      <text class="node-letter">H</text>
      <text class="node-label" y="62">Hook</text>
      <text class="node-desc"  y="82">Open with relevance</text>
    </g>
    <!-- A · P · P · A nodes at x = 360, 600, 840, 1080 -->
  </svg>
</div>
.happa-arc {
  position: relative;
  width: 100%; max-width: 1080px;
  /* v1.25.0 (Website Round W5-R018): centre by default. See `.scoreboard`. */
  margin-inline: auto;
  padding: 20px 0 60px;
}

.happa-arc svg { width: 100%; height: auto; display: block; }

.happa-arc .arc-path {
  fill: none; stroke: var(--fg);           /* dark-mode safe — flips to warm cream on dark */
  stroke-width: 2;
  stroke-linecap: round; stroke-dasharray: 6 8;
}

.happa-arc .node-ring { fill: var(--bg-paper); stroke: var(--accent); stroke-width: 2; }

.happa-arc .node-letter {
  font: 700 22px/1 var(--f-display); fill: var(--accent); text-anchor: middle; dominant-baseline: central;
}

.happa-arc .node-label {
  font: 600 16px/1 var(--f-display); fill: var(--fg); text-anchor: middle; letter-spacing: -0.005em;
}

.happa-arc .node-desc {
  font: 400 14px/1.3 var(--f-body); fill: var(--fg-soft); text-anchor: middle;
}

.happa-arc .tracer-halo { fill: color-mix(in oklab, var(--accent) 30%, transparent); }

.happa-arc .tracer-dot  { fill: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .happa-arc .tracer { display: none; }
}

@media (max-width: 720px) {
  /* Demo stage margins contract so every component has more horizontal room */
  .ns-stage.pad { padding: var(--s-5) var(--s-4); }
  /* Decay curve: shrink callout text & leader reach so they still fit at narrower widths */
  .decay-curve .callout-title,
  .decay-curve .callout-title.won { font-size: 12px; }
  .decay-curve .callout-sub,
  .decay-curve .callout-sub.lost  { font-size: 9.5px; letter-spacing: 0.08em; }
  /* Happa arc gets cramped — shrink node descriptions */
  .happa-arc .node-desc { font-size: 10px; }
  /* Integration hub: chips can overlap at small scale — shrink text */
  .integration-hub .node { font-size: 9.5px; padding: 4px 7px; }
  /* Race timeline body copy compacts */
  .race-timeline .rt-body { font-size: 12.5px; }
  .race-timeline .rt-tick .rt-body em { font-size: 11px; }
}

/* …additional rules trimmed for brevity — see _shared.css */
import { HappaArc } from "@magicblocksai/ui";

<HappaArc ariaLabel="HAPPA conversation arc: Hook, Align, Personalise, Pitch, Action" />

// Or with custom stage copy:
<HappaArc
  stages={[
    { letter: "H", label: "Hook",        description: "Acknowledge the rate" },
    { letter: "A", label: "Align",       description: "Confirm timeline" },
    { letter: "P", label: "Personalise", description: "Use draft app" },
    { letter: "P", label: "Pitch",       description: "Concrete monthly saving" },
    { letter: "A", label: "Action",      description: "Two booking slots" },
  ]}
/>

12.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
<div class="guardian-shield reveal svg-draw" style="--draw-len: 900;">
  <svg viewBox="0 0 200 240" aria-label="Guardian shield">
    <path class="sh-body draw" d="M 20 28 …Z"/>
    <path class="sh-shine"     d="M 20 28 …Z"/>
  </svg>
  <div class="sh-items">
    <span class="sh-item">Consent</span>
    <!-- Privacy · Encryption · Retention · Opt-out · Audit -->
  </div>
  <div class="sh-caption">Guardian · every lead, every vertical</div>
</div>
.guardian-shield {
  position: relative;
  width: 100%; max-width: 320px;
  margin: 0 auto;
}

.guardian-shield svg { width: 100%; height: auto; display: block; }

.guardian-shield .sh-body {
  fill: color-mix(in oklab, var(--ink) 94%, transparent);
  stroke: var(--accent); stroke-width: 2.4;
}

.guardian-shield .sh-shine {
  fill: url(#shShine); pointer-events: none;
  transform-origin: center; transform-box: view-box;
  animation: gs-shine 5.5s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .guardian-shield .sh-shine { animation: none; }
}

.guardian-shield .sh-items {
  position: absolute;
  /* Single-column stack fills the shield's tall shape naturally — the
     old 2×3 grid left dead space above and below the centred items.
     Bigger inset rings the stack with breathing room on all sides. */
  inset: 20% 22% 22% 22%;
  display: flex; flex-direction: column;
  justify-content: center;
  gap: 14px;
}

.guardian-shield .sh-item {
  display: flex; align-items: center; gap: 10px;
  min-width: 0;
  font: 600 12.5px/1.15 var(--f-mono);
  color: color-mix(in oklab, var(--paper) 94%, transparent);
  letter-spacing: 0.08em; text-transform: uppercase;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.guardian-shield .sh-item::before {
  content: ""; width: 14px; height: 14px; flex: 0 0 14px;
  border-radius: 50%;
  background: var(--accent);
  display: inline-grid; place-items: center;
  /* a tiny white ✓ baked into the bullet disc */
  background-image:
    linear-gradient(transparent, transparent),
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'><path d='M2 5l2 2 4-4' fill='none' stroke='white' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-size: cover;
}

.guardian-shield .sh-caption {
  text-align: center; margin-top: 12px;
  font: 500 11.5px/1 var(--f-mono); color: var(--fg-soft);
  letter-spacing: 0.14em; text-transform: uppercase;
}

.guardian-shield .sh-caption strong { color: var(--accent-text); font-weight: 600; }

/* …additional rules trimmed for brevity — see _shared.css */
import { GuardianShield } from "@magicblocksai/ui";

<GuardianShield caption={<>Guardian <strong>·</strong> every lead, every vertical</>} />

// Or with custom items:
<GuardianShield items={["TCPA", "GDPR", "HIPAA", "SOC 2"]} />

12.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
<div class="journey-map reveal">
  <div class="jm-node start"><span class="jm-k">Start</span><span class="jm-t">Lead arrives</span></div>
  <span class="jm-connector"><svg width="40" height="16" aria-hidden="true"><path class="draw" d="M 2 8 H 36 m-6-4 6 4-6 4" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
  <div class="jm-node" style="--jm-delay: 0s"><span class="jm-k">01</span><span class="jm-t">Engage instantly</span></div>
  <span class="jm-connector"><svg width="40" height="16"><path class="draw" d="M 2 8 H 36 m-6-4 6 4-6 4" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
  <div class="jm-node" style="--jm-delay: 2s"><span class="jm-k">02</span><span class="jm-t">Collect context</span></div>
  <span class="jm-connector"><svg width="40" height="16"><path class="draw" d="M 2 8 H 36 m-6-4 6 4-6 4" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
  <div class="jm-branch">
    <div class="jm-node end"     style="--jm-delay: 4s"><span class="jm-k">Yes</span><span class="jm-t">Handoff to sales</span></div>
    <div class="jm-node nurture" style="--jm-delay: 4.3s"><span class="jm-k">Not ready</span><span class="jm-t">Drop into nurture</span></div>
    <div class="jm-node nurture" style="--jm-delay: 4.6s"><span class="jm-k">Dormant</span><span class="jm-t">Queue for reactivation</span></div>
  </div>
</div>
.journey-map {
  display: flex; flex-wrap: wrap; align-items: flex-start;
  gap: 10px;
  max-width: 1080px;
}

.journey-map .jm-node {
  position: relative;
  background: var(--bg-paper);
  border: 1px solid var(--hair);
  border-radius: var(--r-md);
  padding: 10px 14px;
  min-width: 140px;
  display: flex; flex-direction: column; gap: 4px;
}

.journey-map .jm-node .jm-k { font: 500 10px/1 var(--f-mono); letter-spacing: 0.12em; text-transform: uppercase; color: var(--fg-faint); }

.journey-map .jm-node .jm-t { font: 500 13px/1.25 var(--f-body); color: var(--fg); }

.journey-map .jm-node.start {
  background: var(--accent); color: var(--paper); border-color: transparent;
  position: relative;
  box-shadow: 0 0 0 0 color-mix(in oklab, var(--accent) 50%, transparent);
  animation: jm-pulse 2.8s ease-out infinite;
}

.journey-map .jm-node.start .jm-k { color: color-mix(in oklab, var(--paper) 90%, transparent); }

.journey-map .jm-node.start .jm-t { color: var(--paper); }

@media (prefers-reduced-motion: reduce) {
  .journey-map .jm-node.start { animation: none; }
}

.journey-map .jm-node.end     { background: var(--warm-3); border-color: var(--warm-5); }

.journey-map .jm-node.nurture { border-style: dashed; }

.journey-map .jm-node:not(.start) { position: relative; }

.journey-map .jm-node:not(.start)::after {
  content: "";
  position: absolute; inset: -2px;
  border: 2px solid var(--accent);
  border-radius: inherit;
  opacity: 0;
  pointer-events: none;
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 18%, transparent),
              0 8px 18px color-mix(in oklab, var(--accent) 22%, transparent);
  animation: jm-stage-active 10s ease-in-out infinite;
  animation-delay: var(--jm-delay, 0s);
}

@media (prefers-reduced-motion: reduce) {
  .journey-map .jm-node:not(.start)::after { animation: none; opacity: 0; }
}

.journey-map .jm-connector {
  display: inline-flex; align-items: center;
  min-height: 40px;
}

.journey-map .jm-connector svg { display: block; }

.journey-map .jm-label {
  font: 500 10px/1 var(--f-mono); color: var(--fg-faint);
  letter-spacing: 0.08em; text-transform: uppercase;
  align-self: center;
  padding: 0 4px;
}

.journey-map .jm-branch {
  display: grid; gap: 6px; padding: 4px 0;
}

/* …additional rules trimmed for brevity — see _shared.css */
import { JourneyMap } from "@magicblocksai/ui";

<JourneyMap
  items={[
    { kind: "node", node: { id: "start", k: "Start", t: "Lead arrives", variant: "start" } },
    { kind: "node", node: { id: "01",    k: "01",    t: "Engage instantly" } },
    { kind: "node", node: { id: "02",    k: "02",    t: "Collect context" } },
    { kind: "branch", nodes: [
      { id: "yes",     k: "Yes",       t: "Handoff to sales",       variant: "end" },
      { id: "nurture", k: "Not ready", t: "Drop into nurture",       variant: "nurture" },
      { id: "dormant", k: "Dormant",   t: "Queue for reactivation",  variant: "nurture" },
    ] },
  ]}
/>

12.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
<div class="edge-race reveal" data-edge-race role="img">
  <div class="er-region" data-er-region>US WEST</div>

  <div class="er-ring r1"></div>
  <div class="er-ring r2"></div>
  <div class="er-ring r3"></div>

  <svg class="er-lines" viewBox="0 0 100 100" preserveAspectRatio="none" data-er-lines aria-hidden="true"></svg>

  <div class="er-halo"></div>
  <div class="er-halo" style="animation-delay: 1.5s;"></div>
  <div class="er-you"></div>
  <div class="er-you-label">
    <span class="kicker">You in</span>
    <span class="name" data-er-username>Oceanside, CA</span>
  </div>

  <!-- JS appends server pins (.er-server) at angle/distance per region -->
  <div data-er-servers></div>
  <div class="er-badge" data-er-badge><span class="num">8</span> ms</div>

  <!-- JS appends pager dots (.er-pager-dot), one per region -->
  <div class="er-pager" data-er-pager></div>
</div>
.edge-race {
  position: relative;
  width: 100%;
  max-width: 540px;
  aspect-ratio: 1 / 1;
  /* Vertical breathing room — the pager dots sit at bottom: -28px and the
     region pill at top: -8px, so account for both plus a comfortable gap. */
  margin: var(--s-6) auto var(--s-7);
}

.edge-race .er-ring {
  position: absolute; left: 50%; top: 50%;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  border: 1px dashed color-mix(in oklab, var(--accent) 22%, transparent);
}

.edge-race .er-ring.r1 { width: 50%;  height: 50%; }

.edge-race .er-ring.r2 { width: 80%;  height: 80%; }

.edge-race .er-ring.r3 {
  width: 100%; height: 100%;
  border-style: solid;
  border-color: color-mix(in oklab, var(--accent) 12%, transparent);
}

.edge-race .er-region {
  position: absolute;
  top: -8px; left: -8px;
  font: 600 10.5px/1 var(--f-mono);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-text);
  background: var(--bg-paper);
  border: 1px solid color-mix(in oklab, var(--accent) 30%, var(--hair));
  padding: 5px 11px;
  border-radius: 4px;
  white-space: nowrap;
  z-index: 7;
  transition: opacity 0.4s;
}

.edge-race .er-you {
  position: absolute;
  left: 50%; top: 50%;
  width: 22px; height: 22px;
  margin: -11px 0 0 -11px;
  border-radius: 50%;
  background: var(--ink);
  box-shadow:
    0 0 0 4px var(--bg-paper),
    0 0 0 5.5px color-mix(in oklab, var(--ink) 18%, transparent),
    0 4px 14px color-mix(in oklab, var(--ink) 25%, transparent);
  z-index: 5;
}

.edge-race .er-you::before {
  content: '';
  position: absolute; inset: 6px;
  border-radius: 50%;
  background: var(--bg-paper);
}

.edge-race .er-you-label {
  position: absolute;
  left: 50%; top: calc(50% + 22px);
  transform: translateX(-50%);
  text-align: center;
  z-index: 5;
  transition: opacity 0.4s;
}

.edge-race .er-you-label .kicker {
  font: 600 9.5px/1 var(--f-mono);
  letter-spacing: 0.2em;
  color: var(--fg-soft);
  text-transform: uppercase;
}

.edge-race .er-you-label .name {
  display: block; margin-top: 6px;
  font: 600 14px/1.2 var(--f-display);
  color: var(--fg);
  letter-spacing: -0.01em;
}

/* …additional rules trimmed for brevity — see _shared.css */
import { EdgeRace } from "@magicblocksai/ui";

<EdgeRace
  regions={[
    {
      id: "us-west", name: "US WEST", userCity: "Oceanside, CA",
      servers: [
        { id: "lax", city: "Los Angeles",    distanceKm: 130, angleDeg:  10, latencyMs:  8 },
        { id: "sfo", city: "San Francisco", distanceKm: 740, angleDeg: 350, latencyMs: 24 },
        // …more cities
      ],
    },
    // …4 more regions: us-east · europe · asia · oceania
  ]}
/>

12.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
<div class="ecosystem-rings reveal">
  <svg viewBox="0 0 540 540">
    <circle class="er-ring outer" cx="270" cy="270" r="228"/>
    <circle class="er-ring"       cx="270" cy="270" r="150"/>
    <circle class="er-core"       cx="270" cy="270" r="70"/>
  </svg>
  <div class="er-core-label">
    <span class="kicker">The engine</span>
    <span class="name">MagicBlocks</span>
  </div>
  <span class="er-label" style="top: 22%; left: 50%;">Engage</span>
  <!-- Qualify · Follow up · Reengage on the middle ring; outer-ring labels for vision -->
</div>
.ecosystem-rings {
  position: relative;
  width: 100%; max-width: 560px; aspect-ratio: 1;
  margin: 0 auto;
}

.ecosystem-rings svg { width: 100%; height: 100%; display: block; }

.ecosystem-rings .er-ring {
  fill: none;
  /* v1.28.0 (Website Round W6-R023): bumped stroke width 1 → 1.6 and
     pinned the stroke colour to a 22%-ink tint instead of the
     default `--hair` (which can resolve to a near-invisible value
     against cream / paper marketing surfaces). The dashed connector
     reads as "the engine connects everything" — at 1px / hair tint
     it was barely visible at 1× zoom on typical laptop screens. */
  stroke: color-mix(in oklab, var(--ink) 22%, transparent);
  stroke-width: 1.6;
  stroke-dasharray: 2 5;
  transform-origin: center; transform-box: fill-box;
  animation: er-spin 120s linear infinite;
}

.ecosystem-rings .er-ring.outer {
  /* v1.28.0 (W6-R023): bumped accent tint 32% → 55% so the outer
     ring reads as the brand-pink anchor of the visual centre. */
  stroke: color-mix(in oklab, var(--accent) 55%, transparent);
  animation: er-spin-rev 80s linear infinite;
}

.ecosystem-rings .er-core { fill: var(--ink); }

@media (prefers-reduced-motion: reduce) {
  .ecosystem-rings .er-ring { animation: none; }
}

.ecosystem-rings .er-core-label {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  color: var(--paper);
  pointer-events: none;
  z-index: 3;
}

.ecosystem-rings .er-core-label .kicker {
  display: block;
  font: 500 9.5px/1 var(--f-mono); letter-spacing: 0.14em; text-transform: uppercase;
  color: color-mix(in oklab, var(--paper) 62%, transparent);
  margin-bottom: 4px;
}

.ecosystem-rings .er-core-label .name {
  display: block;
  font: 700 15px/1.15 var(--f-display); letter-spacing: -0.01em;
}

.ecosystem-rings .er-label {
  position: absolute;
  transform: translate(-50%, -50%);
  font: 600 10.5px/1 var(--f-mono); color: var(--fg);
  letter-spacing: 0.1em; text-transform: uppercase;
  white-space: nowrap;
  padding: 5px 10px;
  background: var(--bg-paper);
  border: 1px solid var(--hair);
  border-radius: 999px;
  z-index: 2;
}

.ecosystem-rings .er-label.outer {
  color: var(--accent-text);
  border-color: color-mix(in oklab, var(--accent) 30%, transparent);
  background: color-mix(in oklab, var(--accent) 7%, var(--bg-paper));
}

/* …additional rules trimmed for brevity — see _shared.css */
import { EcosystemRings } from "@magicblocksai/ui";

<EcosystemRings coreLabel="The engine" coreName="MagicBlocks" />

12.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>
  <p class="roi-calc-eyebrow">Revenue calculator</p>
  <h2 class="roi-calc-title" data-roi-title>How much revenue could MagicBlocks <em>unlock</em>?</h2>
  <p class="roi-calc-subtitle" data-roi-subtitle>Plug in your numbers. See the opportunity. This isn't a marketing exercise — it's the math your CFO would run.</p>

  <div class="roi-tabs" role="tablist">
    <button class="roi-tab-btn is-active" type="button" role="tab" aria-selected="true"  data-roi-tab="activation"><span class="ic">⚡</span>Lead activation<span class="sub">Convert more inbound</span></button>
    <button class="roi-tab-btn"           type="button" role="tab" aria-selected="false" data-roi-tab="reactivation"><span class="ic">↺</span>Lead reactivation<span class="sub">Re-engage aged leads</span></button>
  </div>

  <div class="roi-card">
    <!-- ACTIVATION PANEL -->
    <div class="roi-panel is-active" data-roi-panel="activation">
      <div class="roi-inputs">
        <p class="roi-inputs-label">Your numbers</p>
        <div class="roi-field">
          <label for="roi-a-leads">Monthly lead volume</label>
          <div class="input-wrap"><input type="text" id="roi-a-leads" value="250" inputmode="numeric" data-roi-input="activation"></div>
        </div>
        <div class="roi-field">
          <label for="roi-a-cpl">Average cost per lead</label>
          <div class="input-wrap"><span class="roi-prefix">$</span><input type="text" id="roi-a-cpl" class="has-prefix" value="50" inputmode="numeric" data-roi-input="activation"></div>
        </div>
        <div class="roi-field">
          <label for="roi-a-conv">Current conversion rate</label>
          <div class="roi-slider-row">
            <input type="range" id="roi-a-conv" min="0.5" max="15" step="0.5" value="3" data-roi-input="activation">
            <span class="roi-slider-val" data-roi-val="roi-a-conv" data-roi-suffix="%">3%</span>
          </div>
        </div>
        <div class="roi-field">
          <label for="roi-a-lift">Expected conversion rate lift</label>
          <div class="roi-slider-row">
            <input type="range" id="roi-a-lift" min="0.5" max="10" step="0.5" value="1" data-roi-input="activation">
            <span class="roi-slider-val" data-roi-val="roi-a-lift" data-roi-prefix="+" data-roi-suffix="%">+1%</span>
          </div>
          <p class="roi-field-hint" data-roi-lift-hint>New rate: <strong>4%</strong></p>
        </div>
        <div class="roi-field">
          <label for="roi-a-deal">Average deal value</label>
          <div class="input-wrap"><span class="roi-prefix">$</span><input type="text" id="roi-a-deal" class="has-prefix" value="3,000" inputmode="numeric" data-roi-input="activation"></div>
        </div>
        <div class="roi-plan-note" data-roi-plan-note="activation"></div>
      </div>
      <div class="roi-outputs">
        <p class="roi-outputs-label">Your results</p>
        <div class="roi-result"><p class="roi-result-label">Current monthly revenue</p><p class="roi-result-value" data-roi-out="activation.current">—</p></div>
        <div class="roi-result"><p class="roi-result-label" data-roi-out-label="activation.projected">Projected revenue at 4% conversion</p><p class="roi-result-value" data-roi-out="activation.projected">—</p></div>
        <div class="roi-result is-hero"><p class="roi-result-label">Additional revenue per month</p><p class="roi-result-value" data-roi-out="activation.additional">—</p></div>
        <div class="roi-result is-cost"><p class="roi-result-label">MagicBlocks cost</p><p class="roi-result-value" data-roi-out="activation.cost">—</p></div>
        <div class="roi-result is-roi"><p class="roi-result-label">Return on investment</p><p class="roi-result-value" data-roi-out="activation.roi">—</p></div>
        <div class="roi-result is-payback"><p class="roi-result-label">Payback period</p><p class="roi-result-value" data-roi-out="activation.payback">—</p></div>
      </div>
    </div>

    <!-- REACTIVATION PANEL -->
    <div class="roi-panel" data-roi-panel="reactivation">
      <div class="roi-inputs">
        <p class="roi-inputs-label">Your database</p>
        <div class="roi-field">
          <label for="roi-r-dbsize">Aged leads in your database</label>
          <div class="input-wrap"><input type="text" id="roi-r-dbsize" value="5,000" inputmode="numeric" data-roi-input="reactivation"></div>
        </div>
        <div class="roi-field">
          <label for="roi-r-conv-current">Current conversion rate</label>
          <div class="roi-slider-row">
            <input type="range" id="roi-r-conv-current" min="0.5" max="10" step="0.5" value="1" data-roi-input="reactivation">
            <span class="roi-slider-val" data-roi-val="roi-r-conv-current" data-roi-suffix="%">1%</span>
          </div>
        </div>
        <div class="roi-field">
          <label for="roi-r-conv-new">Conversion rate with MagicBlocks</label>
          <div class="roi-slider-row">
            <input type="range" id="roi-r-conv-new" min="0.5" max="15" step="0.5" value="2" data-roi-input="reactivation">
            <span class="roi-slider-val" data-roi-val="roi-r-conv-new" data-roi-suffix="%">2%</span>
          </div>
        </div>
        <div class="roi-field">
          <label for="roi-r-deal">Average deal value</label>
          <div class="input-wrap"><span class="roi-prefix">$</span><input type="text" id="roi-r-deal" class="has-prefix" value="3,000" inputmode="numeric" data-roi-input="reactivation"></div>
        </div>
        <div class="roi-plan-note" data-roi-plan-note="reactivation"></div>
      </div>
      <div class="roi-outputs">
        <p class="roi-outputs-label">Your results</p>
        <div class="roi-result"><p class="roi-result-label" data-roi-out-label="reactivation.current">Current revenue at 1% conversion</p><p class="roi-result-value" data-roi-out="reactivation.current">—</p></div>
        <div class="roi-result"><p class="roi-result-label" data-roi-out-label="reactivation.projected">Revenue with MagicBlocks at 2%</p><p class="roi-result-value" data-roi-out="reactivation.projected">—</p></div>
        <div class="roi-result is-hero"><p class="roi-result-label">Additional revenue unlocked</p><p class="roi-result-value" data-roi-out="reactivation.additional">—</p></div>
        <div class="roi-result is-cost"><p class="roi-result-label">MagicBlocks cost</p><p class="roi-result-value" data-roi-out="reactivation.cost">—</p></div>
        <div class="roi-result is-roi"><p class="roi-result-label">Return on investment</p><p class="roi-result-value" data-roi-out="reactivation.roi">—</p></div>
        <div class="roi-result is-payback"><p class="roi-result-label">Cost per reactivated deal</p><p class="roi-result-value" data-roi-out="reactivation.cpa">—</p></div>
      </div>
    </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; } }
import { RevenueCalculator } from "@magicblocksai/ui";

// Default — homepage / pricing
<RevenueCalculator />

// With mode preset — pre-fills industry defaults
<RevenueCalculator mode="industry:mortgage" />
<RevenueCalculator mode="industry:solar"    initialTab="reactivation" />

// All maths runs client-side; plan-aware pricing picks Core/Scale/Enterprise.

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>How much revenue could MagicBlocks <em>unlock</em>?</h2>
  <p class="roi-calc-subtitle" data-roi-subtitle>Plug in your numbers. See the opportunity. This isn't a marketing exercise — it's the math your CFO would run.</p>
  <div class="roi-tabs" role="tablist">
    <button class="roi-tab-btn is-active" type="button" role="tab" aria-selected="true"  data-roi-tab="activation"><span class="ic">⚡</span>Lead activation<span class="sub">Convert more inbound</span></button>
    <button class="roi-tab-btn"           type="button" role="tab" aria-selected="false" data-roi-tab="reactivation"><span class="ic">↺</span>Lead reactivation<span class="sub">Re-engage aged leads</span></button>
  </div>
  <div class="roi-card">
    <!-- Activation + reactivation panels — same .roi-inputs / .roi-outputs structure as the default;
         only the seeded values from MB_ROI_MODES["industry:solar"] differ. -->
  </div>
</div>
.roi-calc {
  width: 100%;
  max-width: min(960px, 100%);
  margin: 0 auto;
}

.roi-calc :where(p, h2, h3) { margin: 0; }

.roi-calc-eyebrow {
  font: 600 11px/1 var(--f-mono);
  text-transform: uppercase; letter-spacing: 0.16em;
  color: var(--fg-dim);
  text-align: center;
  margin-bottom: var(--s-2);
}

.roi-calc-title {
  font: 700 clamp(22px, 3.2vw, 32px)/1.2 var(--f-display);
  letter-spacing: -0.02em;
  color: var(--fg);
  text-align: center;
  margin: 0 0 var(--s-2);
  text-wrap: balance;
}

.roi-calc-title em {
  font-family: var(--f-italic);
  font-style: italic; font-weight: 500;
  color: var(--accent-text);
  font-variation-settings: "SOFT" 80;
}

.roi-calc-subtitle {
  font: 400 15px/1.55 var(--f-body);
  color: var(--fg-soft);
  text-align: center;
  max-width: 560px;
  margin: 0 auto var(--s-6);
}
// 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.
import { RevenueCalculator } from "@magicblocksai/ui";

// `mode` seeds different defaults per destination page:
//   "neutral" · "engage" · "prequalify" · "followup" · "reengage"
//   "industry:mortgage" · "industry:insurance" · "industry:auto"
//   "industry:home-services" · "industry:solar" · "industry:fintech"
//   "industry:tourism"
export default function Demo() {
  return <RevenueCalculator mode="industry:solar" />;
}

12.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.

<div class="brand-timeline reveal" style="--bt-n: 3;">
  <div class="bt-ticks">
    <div class="bt-tick">
      <p class="bt-date">2016 – 2022</p>
      <span class="bt-dot"></span>
      <p class="bt-title">$200M+ in leads</p>
      <p class="bt-sub">Six years running lead-driven businesses…</p>
    </div>
    <!-- two more milestones -->
  </div>
</div>
.brand-timeline {
  position: relative;
  max-width: 900px;
  padding: 20px 0 20px;
}

.brand-timeline .bt-ticks {
  display: grid;
  grid-template-columns: repeat(var(--bt-n, 3), 1fr);
  gap: var(--s-3);
  position: relative;
}

.brand-timeline .bt-ticks::before {
  content: "";
  position: absolute;
  left: 8%; right: 8%;
  top: 42px;                       /* aligns with the centre of each dot */
  height: 2px;
  background: linear-gradient(90deg, var(--hair), var(--accent), var(--hair));
  border-radius: 2px;
  z-index: 0;
}

.brand-timeline .bt-tick {
  text-align: center;
  display: flex; flex-direction: column; align-items: center;
  position: relative;
  padding: 0 var(--s-3);
  z-index: 1;
}

.brand-timeline .bt-date {
  font: 500 11px/1 var(--f-mono); color: var(--fg-faint);
  letter-spacing: 0.12em; text-transform: uppercase;
  margin: 0 0 10px;
  height: 16px;                    /* lock date row height → dot alignment stays consistent */
  display: flex; align-items: center;
}

.brand-timeline .bt-dot {
  width: 14px; height: 14px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px var(--bg-paper);
  flex: 0 0 auto;
  margin-bottom: 18px;
  position: relative;
}

.brand-timeline .bt-tick:last-child .bt-dot::after {
  content: ""; position: absolute; inset: -3px; border-radius: 50%;
  border: 2px solid var(--accent);
  opacity: 0;
  animation: bt-ping 2.4s ease-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .brand-timeline .bt-tick:last-child .bt-dot::after { animation: none; opacity: 0; }
}

.brand-timeline .bt-title {
  font: 600 16px/1.25 var(--f-display); color: var(--fg); margin: 0;
  max-width: 24ch;
}

.brand-timeline .bt-title em { font-family: var(--f-serif); font-style: italic; font-weight: 400; color: var(--accent); }

.brand-timeline .bt-sub {
  font: 400 12.5px/1.45 var(--f-body); color: var(--fg-soft);
  margin: 6px 0 0; max-width: 30ch;
}

/* …additional rules trimmed for brevity — see _shared.css */
import { BrandTimeline } from "@magicblocksai/ui";

<BrandTimeline
  entries={[
    { id: "2016-22", date: "2016 – 2022", title: "$200M+ in leads",
      sub: "Six years running lead-driven businesses across mortgage, solar, insurance and home services." },
    { id: "2023",    date: "2023",        title: <>World's first AI <em>mortgage team</em></>,
      sub: "Built the first live AI sales team in mortgage." },
    { id: "2026",    date: "2026",        title: "MagicBlocks",
      sub: "The conversion engine, productised." },
  ]}
/>

12.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.

<!-- 1. Warm variant (default · pink/blue glows + drifting motes) -->
<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>

<!-- 2. War-room variant (dark surface, paper-on-ink content) -->
<div class="hero-bloom-canvas" data-variant="war-room">
  <div class="hbc-content">
    <div class="kicker">War-room variant</div>
    <h4>The engine <em>under the hood.</em></h4>
  </div>
</div>

<!-- 3. Industry variant — per-page colour wash via --industry-accent -->
<div class="hero-bloom-canvas" data-variant="industry" style="--industry-accent: #47DDB2;">
  <div class="hbc-content">
    <div class="kicker">Industry variant · Insurance</div>
    <h4>Complex qualification. <em>Handled instantly.</em></h4>
  </div>
</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)); }
import { HeroBloomCanvas } from "@magicblocksai/ui";

// Warm variant
<HeroBloomCanvas
  variant="warm"
  kicker="Warm variant"
  heading={<>Every lead. <em>Every time.</em></>}
  motes
/>

// War-room variant
<HeroBloomCanvas variant="war-room" kicker="War-room" heading={<>The engine <em>under the hood.</em></>} />

// Industry variant — pass --industry-accent via style
<HeroBloomCanvas
  variant="industry"
  kicker="Industry · Insurance"
  heading={<>Complex qualification. <em>Handled instantly.</em></>}
  style={{ "--industry-accent": "#47DDB2" } as React.CSSProperties}
/>

12.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" aria-hidden="true">
        <div class="hld-dots"><span></span><span></span><span></span></div>
        <div class="hld-url">magicblocks.com / agent / live</div>
      </div>
      <div class="hld-app">
        <div class="hld-timer"  data-hld-timer aria-live="off">
          <div>
            <div class="hld-timer-label">Response time</div>
            <div class="hld-timer-value" data-hld-timer-value>0.0s</div>
          </div>
        </div>
        <div class="hld-status" data-hld-status aria-live="polite">
          <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">Hey, what rates are you guys seeing right now?</div>
          <div class="hld-msg out" data-hld-step="m2">30-year fixed is at <em>6.12%</em> this morning — down from <em>6.83%</em> 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?</div>
          <div class="hld-msg in"  data-hld-step="m3">Sure</div>
          <div class="hld-msg out" data-hld-step="m4">I just need two things — current balance and rate. Or I can pull from the CRM: are you <strong>Mike Chen</strong> at oakwood_dr@gmail.com?</div>
          <div class="hld-msg in"  data-hld-step="m5">yeah that's me</div>
          <div class="hld-msg out" data-hld-step="m6">Got it — <em>$582K</em> at 6.83%. At today's rate you'd save ~<strong>$271/mo</strong> (~$3,250/yr). Want a 15-min rate review?</div>
          <div class="hld-msg in"  data-hld-step="m7">sounds good</div>
          <div class="hld-msg out" data-hld-step="m8">Two slots: <em>Thu 10:30</em> or <em>Thu 2:45</em>. ✓ Booked either way — pick one.</div>

          <div class="hld-typing" data-hld-typing hidden>
            <span></span><span></span><span></span>
          </div>
        </div>
      </div>
    </div>
  </div>
  <div class="hld-controls">
    <div>
      <button type="button" class="hld-btn" data-hld-action="restart">↻ Restart</button>
      <button type="button" class="hld-btn" data-hld-action="pause">⏸ Pause</button>
      <button type="button" class="hld-btn" data-hld-action="play">▶ Play</button>
    </div>
    <div class="hld-scenario">Scenario · Mortgage</div>
  </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; }
}
import { HeroLiveDemo } from "@magicblocksai/ui";

<HeroLiveDemo
  scenario="mortgage"
  url="magicblocks.com / agent / live"
  messages={[
    { step: "m1", from: "in",  text: "Hey, what rates are you guys seeing right now?" },
    { step: "m2", from: "out", text: <>30-year fixed is at <em>6.12%</em> this morning…</> },
    // …m3–m8
  ]}
  timerValue="0.0s"
  statusLabel="Inbound"
  statusState="inbound"
/>

// With mode prop — overlay-emphasis variant for a destination page:
<HeroLiveDemo mode="speed-forward"          scenario="mortgage"     messages={…} />
<HeroLiveDemo mode="qualification-forward"  scenario="insurance"    messages={…} />

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
<!-- One mode per destination page. Each mode foregrounds a different overlay
     (timer · qualification score · cadence track · reactivation row).
     The shared chrome (frame · tabs · status · thread) is identical. -->

<!-- 01 · speed-forward — Engage / Mortgage / Auto / Home Services / SMS -->
<div class="hero-live-demo" data-hero-live-demo
     data-mode="speed-forward"
     data-scenario="mortgage"
     data-script="engage-mortgage-v1">
  <div class="hld-frame"><div class="hld-screen">
    <div class="hld-tabs" aria-hidden="true">
      <div class="hld-dots"><span></span><span></span><span></span></div>
      <div class="hld-url">magicblocks.com / speed</div>
    </div>
    <div class="hld-app">
      <div class="hld-timer is-frozen">
        <div class="hld-timer-label">First reply</div>
        <div class="hld-timer-value">4.2s</div>
      </div>
      <div class="hld-status" data-state="booked"><span class="hld-status-dot"></span>Booked</div>
      <div class="hld-thread" data-hld-thread>
        <div class="hld-msg in">A/C just died. House is 92°. Two kids. Need someone today.</div>
        <div class="hld-msg out">North Dallas + 12-year system + click-then-silence usually points to a failed capacitor. <em>12:30–2:30 PM</em> with Aaron Ramirez — diagnostic $89, applied to repair. ✓</div>
      </div>
    </div>
  </div></div>
</div>

<!-- 02 · qualification-forward — Prequalify / Insurance / Solar -->
<div class="hero-live-demo" data-hero-live-demo
     data-mode="qualification-forward"
     data-scenario="insurance"
     data-script="qualify-insurance-v1">
  <div class="hld-frame"><div class="hld-screen">
    <div class="hld-tabs" aria-hidden="true">
      <div class="hld-dots"><span></span><span></span><span></span></div>
      <div class="hld-url">magicblocks.com / qualify</div>
    </div>
    <div class="hld-app">
      <div class="hld-qual-score" style="--qs-pct: 86%;">
        <div class="qs-label">Qualification score</div>
        <div class="qs-row"><div class="qs-num">86</div><div class="qs-bar"><span></span></div></div>
      </div>
      <div class="hld-thread" data-hld-thread>
        <div class="hld-msg in">Auto + home, current with Allstate, renewal in 23 days.</div>
        <div class="hld-msg out">Quick three: any tickets in the last 36 mo? Garaged or street? Any teen drivers on the policy?</div>
      </div>
    </div>
  </div></div>
</div>

<!-- 03 · cadence-forward — Follow Up / Tourism -->
<div class="hero-live-demo" data-hero-live-demo
     data-mode="cadence-forward"
     data-scenario="mortgage"
     data-script="follow-up-mortgage-v1">
  <div class="hld-frame"><div class="hld-screen">
    <div class="hld-tabs" aria-hidden="true">
      <div class="hld-dots"><span></span><span></span><span></span></div>
      <div class="hld-url">magicblocks.com / cadence</div>
    </div>
    <div class="hld-app">
      <div class="hld-thread" data-hld-thread>
        <div class="hld-msg out">Hey Mike — circling back on the rate review. You mentioned closing in 45d; we're now <em>22 days out</em>. Slot tomorrow 2pm?</div>
        <div class="hld-msg in">Yeah let's do tomorrow.</div>
      </div>
      <div class="hld-cadence" style="--cd-progress: 65%;">
        <span class="cd-label">Touch 5/7</span>
        <div class="cd-track">
          <span class="cd-touch is-fired"   style="left: 5%;"></span>
          <span class="cd-touch is-fired"   style="left: 20%;"></span>
          <span class="cd-touch is-fired"   style="left: 35%;"></span>
          <span class="cd-touch is-fired"   style="left: 50%;"></span>
          <span class="cd-touch is-current" style="left: 65%;"></span>
          <span class="cd-touch"            style="left: 80%;"></span>
          <span class="cd-touch"            style="left: 95%;"></span>
        </div>
        <span class="cd-label">Day 22 / 30</span>
      </div>
    </div>
  </div></div>
</div>

<!-- 04 · reactivation-forward — Reengage Aged -->
<div class="hero-live-demo" data-hero-live-demo
     data-mode="reactivation-forward"
     data-scenario="mortgage"
     data-script="reengage-mortgage-v1">
  <div class="hld-frame"><div class="hld-screen">
    <div class="hld-tabs" aria-hidden="true">
      <div class="hld-dots"><span></span><span></span><span></span></div>
      <div class="hld-url">magicblocks.com / reactivate</div>
    </div>
    <div class="hld-app">
      <div class="hld-reactivation">
        <div class="rx-row is-dormant"><span class="rx-dot"></span>Last contact · 147 days ago</div>
        <div class="rx-row is-active"><span class="rx-dot"></span>Re-engaged today</div>
      </div>
      <div class="hld-thread" data-hld-thread>
        <div class="hld-msg out">Hey Sarah — saw rates dropped 60bps since we last spoke back in November. On your old quote, that's about <em>$340/mo</em> back. Want a fresh look?</div>
        <div class="hld-msg in">Wow yeah send me what you've got.</div>
      </div>
    </div>
  </div></div>
</div>
.hero-live-demo {
  width: 100%;
  max-width: min(900px, 100%);
  margin: 0 auto;
  position: relative;
}

.hero-live-demo[data-mode="speed-forward"] .hld-timer {
  padding: 12px 18px;
  font-size: 13px;
}

.hero-live-demo[data-mode="speed-forward"] .hld-timer-value {
  font-size: 22px; font-weight: 700;
}

.hero-live-demo[data-mode="speed-forward"] .hld-timer.is-frozen {
  box-shadow: 0 8px 28px -8px color-mix(in oklab, var(--accent) 70%, transparent),
              0 0 0 4px color-mix(in oklab, var(--accent) 18%, transparent);
}

.hero-live-demo[data-mode="speed-forward"] .hld-status { opacity: 0.78; transform: scale(0.92); transform-origin: bottom left; }

.hero-live-demo[data-mode="speed-forward"] .hld-status.is-visible { opacity: 0.78; transform: scale(0.92); }

.hero-live-demo[data-mode="qualification-forward"] .hld-status { display: none; }

.hero-live-demo[data-mode="cadence-forward"] .hld-status { display: none; }

.hero-live-demo[data-mode="reactivation-forward"] .hld-status { display: none; }
// 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
import { HeroLiveDemo } from "@magicblocksai/ui";

// `mode` controls which overlay set is foregrounded:
//   "speed-forward" · "qualification-forward" · "cadence-forward" · "reactivation-forward"
// `scenario` swaps the industry copy + status badge.

// 01 · speed-forward — Engage / Mortgage / Auto / Home Services / SMS
<HeroLiveDemo
  mode="speed-forward"
  scenario="mortgage"
  timerValue="4.2s"
  timerFrozen
  statusLabel="Booked"
  statusState="booked"
  messages={[
    { step: "m1", from: "in",  text: "A/C just died. House is 92°. Two kids. Need someone today." },
    { step: "m2", from: "out", text: "North Dallas + 12-year system points to a failed capacitor. 12:30–2:30 PM with Aaron Ramirez. ✓" },
  ]}
/>

// 02 · qualification-forward — Prequalify / Insurance / Solar
<HeroLiveDemo
  mode="qualification-forward"
  scenario="insurance"
  qualScore={86}
  messages={[
    { step: "m1", from: "in",  text: "Commercial property, 6 locations AZ + NV, $8M revenue, prior kitchen fire 2022." },
    { step: "m2", from: "out", text: "Strong profile — fits our commercial broker bench. Marcus Ruiz Thu 10:30?" },
  ]}
/>

// 03 · cadence-forward — Follow Up / Tourism
<HeroLiveDemo
  mode="cadence-forward"
  scenario="mortgage"
  cadence={{ touch: "5/7", progress: "65%", day: "22 / 30" }}
  messages={[
    { step: "m1", from: "out", text: "Hey Mike — circling back on the rate review. Slot tomorrow 2pm?" },
    { step: "m2", from: "in",  text: "Yeah let's do tomorrow." },
  ]}
/>

// 04 · reactivation-forward — Reengage Aged
<HeroLiveDemo
  mode="reactivation-forward"
  scenario="mortgage"
  reactivation={{ dormantDays: 147, reEngagedToday: true }}
  messages={[
    { step: "m1", from: "out", text: "Hey Sarah — rates dropped 60bps since November. About $340/mo back." },
    { step: "m2", from: "in",  text: "Wow yeah send me what you've got." },
  ]}
/>

12.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">

  <!-- 1. Engage new leads (pink) -->
  <a class="leak-card" data-leak="1" href="/use-cases/engage-new-leads">
    <span class="lk-icon" aria-hidden="true">
      <svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></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-five-minute reply. Five-minute responders are <strong>100×</strong> more likely to make contact and <strong>21×</strong> more likely to qualify.
      <cite>Hatch 2024 · MIT / HBR</cite>
    </p>
    <span class="lk-spark" aria-hidden="true" style="--lk-len: 180;">
      <svg viewBox="0 0 180 36" preserveAspectRatio="none"><path d="M 2 4 C 24 4 32 28 52 32 L 178 32"/></svg>
    </span>
    <span class="lk-cta">Fix Leak #1 <span class="arrow">→</span></span>
  </a>

  <!-- 2. Prequalify leads (yellow) -->
  <a class="leak-card" data-leak="2" href="/use-cases/prequalify">
    <span class="lk-icon" aria-hidden="true">
      <svg viewBox="0 0 24 24"><path d="M3 5h18L14 14v6l-4-2v-4L3 5z"/></svg>
    </span>
    <span class="lk-stat" aria-label="Key statistic: 67 percent">67%</span>
    <h3 class="lk-title">Prequalify leads</h3>
    <p class="lk-body">
      <strong>67%</strong> of reps burn hours on leads that were never going to close. Reps spend just <strong>34%</strong> of their time actually selling — the rest is admin plus chasing bad-fit prospects.
      <cite>BANT research · S2W Media · Salesforce</cite>
    </p>
    <span class="lk-spark" aria-hidden="true" style="--lk-len: 200;">
      <svg viewBox="0 0 180 36" preserveAspectRatio="none"><path d="M 2 20 L 22 8 L 38 24 L 56 12 L 72 26 L 90 14 L 108 28 L 126 20 L 178 32"/></svg>
    </span>
    <span class="lk-cta">Fix Leak #2 <span class="arrow">→</span></span>
  </a>

  <!-- 3. Follow up (green) -->
  <a class="leak-card" data-leak="3" href="/use-cases/follow-up">
    <span class="lk-icon" aria-hidden="true">
      <svg viewBox="0 0 24 24"><path d="M4 18c4 0 4-6 8-6s4 6 8 6"/><circle cx="4" cy="18" r="1.6"/><circle cx="20" cy="18" r="1.6"/><circle cx="12" cy="12" r="1.6"/></svg>
    </span>
    <span class="lk-stat" aria-label="Key statistic: 44 percent">44%</span>
    <h3 class="lk-title">Follow up</h3>
    <p class="lk-body">
      <strong>44%</strong> of salespeople give up after one attempt. Only <strong>8%</strong> follow up more than five times — yet <strong>80%</strong> of sales require exactly that.
      <cite>Marketing Donut · widely cited 2024–2026</cite>
    </p>
    <span class="lk-spark" aria-hidden="true" style="--lk-len: 190;">
      <svg viewBox="0 0 180 36" preserveAspectRatio="none">
        <path d="M 4 28 L 178 28" opacity="0.18"/>
        <path d="M 14 18 L 14 36" stroke-width="2.6"/>
        <path d="M 46 24 L 46 32" opacity="0.32"/>
        <path d="M 78 24 L 78 32" opacity="0.32"/>
        <path d="M 110 24 L 110 32" opacity="0.32"/>
        <path d="M 142 24 L 142 32" opacity="0.32"/>
      </svg>
    </span>
    <span class="lk-cta">Fix Leak #3 <span class="arrow">→</span></span>
  </a>

  <!-- 4. Reengage aged leads (blue) -->
  <a class="leak-card" data-leak="4" href="/use-cases/reengage">
    <span class="lk-icon" aria-hidden="true">
      <svg viewBox="0 0 24 24"><path d="M3 7h18v4H3zM5 11v9h14v-9M10 16h4"/></svg>
    </span>
    <span class="lk-stat" aria-label="Key statistic: 84 percent">84%</span>
    <h3 class="lk-title">Reengage aged leads</h3>
    <p class="lk-body">
      <strong>84%</strong> of CRM leads go untouched after 30 days. Reactivating dormant contacts costs <strong>5–10×</strong> less than chasing new ones — and converts <strong>3–4×</strong> higher.
      <cite>VisQuanta · BoldTrail · Mixed Media Ventures</cite>
    </p>
    <span class="lk-spark" aria-hidden="true" style="--lk-len: 180;">
      <svg viewBox="0 0 180 36" preserveAspectRatio="none">
        <path d="M 4 30 L 84 30" opacity="0.28"/>
        <path d="M 14 26 L 14 34" opacity="0.42"/>
        <path d="M 30 26 L 30 34" opacity="0.42"/>
        <path d="M 46 26 L 46 34" opacity="0.42"/>
        <path d="M 62 26 L 62 34" opacity="0.42"/>
        <path d="M 100 30 L 118 14 L 140 22 L 158 10 L 178 8" stroke-width="2.2"/>
      </svg>
    </span>
    <span class="lk-cta">Fix Leak #4 <span class="arrow">→</span></span>
  </a>

</div>
/* One base class, four variants. --lk-accent is the vivid tone (rail,
   icon, sparkline); --lk-accent-text is its AA-safe partner for the CTA
   text. data-leak selects both. */
.leak-card { --lk-accent: var(--accent); --lk-accent-soft: var(--accent-soft); --lk-accent-text: var(--accent-text-strong); }
.leak-card[data-leak="1"] { --lk-accent: var(--pink-500);   }
.leak-card[data-leak="2"] { --lk-accent: var(--yellow-500); --lk-accent-text: var(--warning-text); }
.leak-card[data-leak="3"] { --lk-accent: var(--green-500);  --lk-accent-text: var(--success-text); }
.leak-card[data-leak="4"] { --lk-accent: var(--blue-500);   --lk-accent-text: var(--info-text); }

.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 .lk-cta  { color: var(--lk-accent-text); }  /* AA-safe; not the vivid --lk-accent */
.leak-card:hover .lk-spark path { animation: lk-spark-draw 1.4s var(--ease) forwards; }
import { LeakCard } from "@magicblocksai/ui";

// All four leaks rendered from a single source array.
const leaks = [
  {
    leak: 1,
    href: "/use-cases/engage-new-leads",
    icon: <svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg>,
    stat: "88%",
    statAriaLabel: "Key statistic: 88 percent",
    title: "Engage new leads",
    body: <><strong>88%</strong> of inbound leads don't get a sub-five-minute reply. <cite>Hatch 2024 · MIT / HBR</cite></>,
    sparkline: <svg viewBox="0 0 180 36" preserveAspectRatio="none"><path d="M 2 4 C 24 4 32 28 52 32 L 178 32"/></svg>,
    cta: "Fix Leak #1",
  },
  {
    leak: 2,
    href: "/use-cases/prequalify",
    icon: <svg viewBox="0 0 24 24"><path d="M3 5h18L14 14v6l-4-2v-4L3 5z"/></svg>,
    stat: "67%",
    statAriaLabel: "Key statistic: 67 percent",
    title: "Prequalify leads",
    body: <><strong>67%</strong> of reps burn hours on leads that were never going to close. <cite>BANT research · S2W Media · Salesforce</cite></>,
    sparkline: <svg viewBox="0 0 180 36" preserveAspectRatio="none"><path d="M 2 20 L 22 8 L 38 24 L 56 12 L 72 26 L 90 14 L 108 28 L 126 20 L 178 32"/></svg>,
    cta: "Fix Leak #2",
  },
  {
    leak: 3,
    href: "/use-cases/follow-up",
    icon: <svg viewBox="0 0 24 24"><path d="M4 18c4 0 4-6 8-6s4 6 8 6"/><circle cx="4" cy="18" r="1.6"/><circle cx="20" cy="18" r="1.6"/><circle cx="12" cy="12" r="1.6"/></svg>,
    stat: "44%",
    statAriaLabel: "Key statistic: 44 percent",
    title: "Follow up",
    body: <><strong>44%</strong> of salespeople give up after one attempt — yet <strong>80%</strong> of sales require five+ touches. <cite>Marketing Donut</cite></>,
    sparkline: <svg viewBox="0 0 180 36" preserveAspectRatio="none"><path d="M 4 28 L 178 28" opacity="0.18"/><path d="M 14 18 L 14 36" strokeWidth="2.6"/></svg>,
    cta: "Fix Leak #3",
  },
  {
    leak: 4,
    href: "/use-cases/reengage",
    icon: <svg viewBox="0 0 24 24"><path d="M3 7h18v4H3zM5 11v9h14v-9M10 16h4"/></svg>,
    stat: "84%",
    statAriaLabel: "Key statistic: 84 percent",
    title: "Reengage aged leads",
    body: <><strong>84%</strong> of CRM leads go untouched after 30 days. Reactivating costs <strong>5–10×</strong> less. <cite>VisQuanta · BoldTrail</cite></>,
    sparkline: <svg viewBox="0 0 180 36" preserveAspectRatio="none"><path d="M 4 30 L 84 30" opacity="0.28"/><path d="M 100 30 L 118 14 L 140 22 L 158 10 L 178 8" strokeWidth="2.2"/></svg>,
    cta: "Fix Leak #4",
  },
];

<div className="leak-grid">
  {leaks.map((l) => <LeakCard key={l.leak} {...l} />)}
</div>

12.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; }
}
import { ChatCompare } from "@magicblocksai/ui";

<ChatCompare
  variant="generic-chatbot"
  scenario="mortgage"
  heading={<>Same lead. Same question. Watch <em>the difference</em>.</>}
  generic={{
    name: "Generic chatbot",
    status: "—",
    messages: [
      { from: "in",  text: "Hey, what rates are you offering on a 30-year fixed?" },
      { from: "out", text: "Thanks for your interest! A loan specialist will reach out." },
      { from: "out", text: "[link dump]", deadEnd: true },
    ],
  }}
  mb={{
    name: "MagicBlocks",
    status: "Booked",
    messages: [
      { from: "in",  text: "Hey, what rates are you offering on a 30-year fixed?" },
      { from: "out", text: <>30-year fixed at <em>6.75–7.0%</em> this week. 15-min call tomorrow at 10:30?</> },
      { from: "in",  text: "10:30 works." },
      { from: "out", text: "Done. Sarah Chen will call. ✓" },
    ],
  }}
/>

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. ✓
<!-- The shared shell — same .cc-cols / .cc-col / .cc-thread structure for every variant.
     data-cc-variant swaps the left-column label + dialog template; the right column
     always carries the MagicBlocks multi-turn close. -->

<!-- 01 · generic-chatbot (homepage / vs-chatbots) — left dead-ends with a link dump -->
<div class="chat-comparison" data-chat-comparison
     data-cc-variant="generic-chatbot"
     data-scenario="mortgage">
  <div class="cc-cols">
    <div class="cc-col generic" data-cc-side="generic">
      <div class="cc-col-head">
        <div class="cc-col-name"><span class="dot"></span> Generic chatbot</div>
        <div class="cc-col-status">—</div>
      </div>
      <div class="cc-thread">…dead-end script: "Thanks for your interest" + link dump…</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 class="cc-col-status">Booked</div></div>
      <div class="cc-thread">…MB script — quotes rate, books slot…</div>
    </div>
  </div>
</div>

<!-- 02 · closebot-style (/compare/closebot) — left echoes back, ends with logged inquiry -->
<div class="chat-comparison" data-chat-comparison
     data-cc-variant="closebot-style"
     data-scenario="mortgage">
  <div class="cc-cols">
    <div class="cc-col generic" data-cc-side="generic">
      <div class="cc-col-head">
        <div class="cc-col-name"><span class="dot"></span> CLOSEBOT-style workflow AI</div>
        <div class="cc-col-status">Logged</div>
      </div>
      <div class="cc-thread">…echo-back loop: confirms details, ends with "I've logged your inquiry"…</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 class="cc-col-status">Booked</div></div>
      <div class="cc-thread">…MB script — qualifies + books in one turn…</div>
    </div>
  </div>
</div>

<!-- 03 · chatbase-style (/compare/chatbase) — left answers FAQ accurately, never converts -->
<div class="chat-comparison" data-chat-comparison
     data-cc-variant="chatbase-style"
     data-scenario="mortgage">
  <div class="cc-cols">
    <div class="cc-col generic" data-cc-side="generic">
      <div class="cc-col-head">
        <div class="cc-col-name"><span class="dot"></span> CHATBASE-style FAQ AI</div>
        <div class="cc-col-status">Answered</div>
      </div>
      <div class="cc-thread">…FAQ + handoff: rates page link, then "loan officers Mon–Fri"…</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 class="cc-col-status">Booked</div></div>
      <div class="cc-thread">…MB script — proposes review, books Sarah Chen for 10:30…</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" -->
.chat-comparison {
  width: 100%;
  max-width: min(980px, 100%);
  margin: 0 auto;
  display: flex; flex-direction: column; gap: var(--s-5);
}
// 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."
};
import { ChatCompare } from "@magicblocksai/ui";

// `variant` swaps the left-column dialog template:
//   "generic-chatbot" · "closebot-style" · "chatbase-style"
// `scenario` swaps the industry copy across both columns.
// The right (MagicBlocks) column always carries the multi-turn close.

// 01 · generic-chatbot — homepage / vs-chatbots — left dead-ends with a link dump
<ChatCompare
  variant="generic-chatbot"
  scenario="mortgage"
  generic={{
    name: "Generic chatbot",
    status: "—",
    messages: [
      { from: "in",  text: "Hey, what rates are you offering on a 30-year fixed right now?" },
      { from: "out", text: "Thanks for your interest! A loan specialist will reach out during business hours." },
      { from: "out", text: "[link dump]", deadEnd: true },
    ],
  }}
  mb={{
    name: "MagicBlocks",
    status: "Booked",
    messages: [
      { from: "in",  text: "Hey, what rates are you offering on a 30-year fixed right now?" },
      { from: "out", text: <>30-year fixed at <em>6.75–7.0%</em>. 15-min review tomorrow at 10:30?</> },
      { from: "in",  text: "10:30 works." },
      { from: "out", text: "Done. Sarah Chen will call. ✓" },
    ],
  }}
/>

// 02 · closebot-style — /compare/closebot — left echoes back, ends with logged inquiry
<ChatCompare
  variant="closebot-style"
  scenario="mortgage"
  generic={{
    name: "CLOSEBOT-style workflow AI",
    status: "Logged",
    messages: [
      { from: "in",  text: "Hey, what rates are you offering on a 30-year fixed right now?" },
      { from: "out", text: "Happy to help! What's your estimated credit score and loan amount?" },
      { from: "in",  text: "Mid-700s, $420K." },
      { from: "out", text: "Confirming: 30-year fixed for $420K, mid-700s credit. Is that correct?" },
      { from: "in",  text: "Yes." },
      { from: "out", text: "Got it — I've logged your inquiry.", deadEnd: true },
    ],
  }}
  mb={{
    name: "MagicBlocks",
    status: "Booked",
    messages: [
      { from: "in",  text: "Hey, what rates are you offering on a 30-year fixed right now?" },
      { from: "out", text: <>30-year fixed at <em>6.75–7.0%</em> for strong credit. Refi or purchase, rough loan amount, credit range?</> },
      { from: "in",  text: "Refi, $420K, mid-700s." },
      { from: "out", text: <>Strong profile — Sarah Chen <em>tomorrow 10:30</em>?</> },
      { from: "in",  text: "10:30." },
      { from: "out", text: "Done. ✓" },
    ],
  }}
/>

// 03 · chatbase-style — /compare/chatbase — left answers FAQ, never converts
<ChatCompare
  variant="chatbase-style"
  scenario="mortgage"
  generic={{
    name: "CHATBASE-style FAQ AI",
    status: "Answered",
    messages: [
      { from: "in",  text: "Hey, what rates are you offering on a 30-year fixed right now?" },
      { from: "out", text: "Great question! Current 30-year fixed rates vary by credit, loan amount, and other factors. See our rates page: [link]" },
      { from: "in",  text: "Can I talk to someone?" },
      { from: "out", text: "Loan officers are available Mon–Fri, 9 AM–5 PM at [number].", deadEnd: true },
    ],
  }}
  mb={{
    name: "MagicBlocks",
    status: "Booked",
    messages: [
      { from: "in",  text: "Hey, what rates are you offering on a 30-year fixed right now?" },
      { from: "out", text: <>30-year fixed at <em>6.75–7.0%</em> for strong credit. Want a 15-min rate review with one of our LOs?</> },
      { from: "in",  text: "Yes." },
      { from: "out", text: "Tomorrow 10:30 or 2:45?" },
      { from: "in",  text: "10:30." },
      { from: "out", text: "Done. Sarah Chen will call. ✓" },
    ],
  }}
/>

12.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
<span class="stat-badge">
  <span class="eyebrow">Avg. response</span>
  <span class="num">5<sup>s</sup></span>
  <span class="cap">Every lead, every time</span>
</span>

<!-- with the [data-count-to] count-up -->
<span class="stat-badge">
  <span class="eyebrow">Task completion</span>
  <span class="num" data-count-to="97.5" data-decimals="1" data-suffix="%">97.5%</span>
  <span class="cap">vs 59% single-prompt</span>
</span>
.stat-badge {
  display: inline-flex; flex-direction: column; gap: 6px;
  padding: 14px 18px;
  background: var(--bg-paper);
  border: 1px solid var(--hair);
  border-radius: var(--r-md);
  max-width: 200px;
}

.stat-badge .eyebrow {
  font: 500 10.5px/1 var(--f-mono); color: var(--accent-text);
  letter-spacing: 0.14em; text-transform: uppercase;
}

.stat-badge .num {
  font: 700 40px/1 var(--f-display); color: var(--fg);
  letter-spacing: -0.025em; font-variant-numeric: tabular-nums;
}

.stat-badge .num em { font-family: var(--f-serif); font-style: italic; font-weight: 400; color: var(--accent); }

.stat-badge .num sup { font-size: 0.55em; font-weight: 500; vertical-align: top; color: var(--fg-soft); margin-left: 2px; }

.stat-badge .cap {
  font: 500 11.5px/1.3 var(--f-body); color: var(--fg-soft);
}
import { StatBadge } from "@magicblocksai/ui";

<StatBadge
  eyebrow="Avg. response"
  num={<>5<sup>s</sup></>}
  caption="Every lead, every time"
/>

// With count-up animation:
<StatBadge
  eyebrow="Task completion"
  num="97.5%"
  countTo={97.5}
  decimals={1}
  suffix="%"
  caption="vs 59% single-prompt"
/>

Cost compare

.cost-compare

Tells the pricing argument as a three-beat story: a clear headline pins the meaning, two bars show the dramatic gap (acquisition cost vs MagicBlocks per lead), and a punchline callout pulls out the percentage so the conclusion lands without squinting. Lives on the Pricing hero.

Per lead in your funnel
You already paid to acquire the lead. Working it costs a fraction more.
Acquisition
Ads · SDRs · list buys · content
$30 – $80
MagicBlocks
Every channel, every attempt, qualified
$2.50 – $4
~7% of what you've already spent — and the lead gets worked, not left to rot in a CRM.
<div class="cost-compare" aria-label="Acquisition cost vs MagicBlocks per lead">
  <div class="cc-eyebrow">Per lead in your funnel</div>
  <div class="cc-headline">
    You already paid to acquire the lead.
    <em>Working it costs a fraction more.</em>
  </div>
  <div class="cc-bars">
    <div class="cc-bar" style="--cc-pct: 100%;">
      <div class="cc-bar-meta">
        <div class="cc-bar-label">Acquisition</div>
        <div class="cc-bar-sub">Ads · SDRs · list buys · content</div>
      </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-meta">
        <div class="cc-bar-label">MagicBlocks</div>
        <div class="cc-bar-sub">Every channel, every attempt</div>
      </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-callout">
    <span class="cc-callout-pct">~7%</span>
    <span class="cc-callout-cap">of what you've already spent — and the lead gets <strong>worked</strong>.</span>
  </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);
}
// CostCompare is a chapter-only narrative shape — render with the kit's CSS classes.

<div className="cost-compare" aria-label="Acquisition cost vs MagicBlocks per lead">
  <div className="cc-eyebrow">Per lead in your funnel</div>
  <div className="cc-headline">
    You already paid to acquire the lead. <em>Working it costs a fraction more.</em>
  </div>
  <div className="cc-bars">
    <div className="cc-bar" style={{ "--cc-pct": "100%" } as React.CSSProperties}>
      <div className="cc-bar-meta">
        <div className="cc-bar-label">Acquisition</div>
        <div className="cc-bar-sub">Ads · SDRs · list buys · content</div>
      </div>
      <div className="cc-bar-track"><div className="cc-bar-fill" /></div>
      <div className="cc-bar-amount">$30 – $80</div>
    </div>
    <div className="cc-bar is-engine" style={{ "--cc-pct": "6%" } as React.CSSProperties}>
      <div className="cc-bar-meta">
        <div className="cc-bar-label">MagicBlocks</div>
        <div className="cc-bar-sub">Every channel, every attempt, qualified</div>
      </div>
      <div className="cc-bar-track"><div className="cc-bar-fill" /></div>
      <div className="cc-bar-amount">$2.50 – $4</div>
    </div>
  </div>
  <div className="cc-callout">
    <span className="cc-callout-pct">~7%</span>
    <span className="cc-callout-cap">
      of what you've already spent — and the lead gets <strong>worked</strong>, not left to rot in a CRM.
    </span>
  </div>
</div>

Stress scoreboard

.stress-scoreboard

Oversized stat-pair (97.5% vs 59%) dominating the visual, paired with two animated dot-rows that tick on left-to-right in a continuous 8s loop — each dot is one lead arriving. The single-prompt row's right tail fades out as completions drop; MagicBlocks stays solid. Ink surface for max contrast. Honours prefers-reduced-motion. Built to live on Built for Production §4.

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.
100 leads through each system · live trace
MagicBlocks · ~24/25 complete Single-prompt · ~15/25 complete, tail drops
<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">100 leads through each system · live trace</div>
    <div class="ss-rows" data-stress-rows aria-hidden="true">
      <div class="ss-row is-fail" data-row-label="Single-prompt"></div>
      <div class="ss-row is-pass" data-row-label="MagicBlocks"></div>
    </div>
  </div>
</div>

<script>
  // 25 dots per row; CSS animation does the wave + loop.
  document.querySelectorAll("[data-stress-rows] .ss-row").forEach(row => {
    const total = 25;
    const isFail = row.classList.contains("is-fail");
    const solid = isFail ? 15 : 24;
    for (let i = 0; i < total; i++) {
      const s = document.createElement("span");
      s.style.setProperty("--i", i);
      // Per-dot state drives the green / red colour. Failed dots get
      // a fading tail opacity for visual weight (0.6 → 0.1 across them).
      s.dataset.state = i < solid ? "pass" : "fail";
      s.style.setProperty("--final-op",
        i < solid ? "0.95"
                  : Math.max(0.1, 0.6 - ((i - solid) / (total - solid)) * 0.5).toFixed(2));
      row.appendChild(s);
    }
  });
</script>
.stress-scoreboard .ss-row {
  display: grid; grid-template-columns: repeat(25, 1fr); gap: 8px;
}
.stress-scoreboard .ss-row span {
  aspect-ratio: 1; border-radius: 50%;
  background: var(--green-500);                /* default; per-dot data-state overrides */
  opacity: 0; transform: scale(0.3);
  /* 8s loop: pop in (0-3%), hold (to 72%), fade (to 90%), pause (to 100%).
     animation-delay staggers each dot left-to-right. */
  animation: ss-pop 8s linear infinite;
  animation-delay: calc(var(--i, 0) * 0.05s);
}
.stress-scoreboard .ss-row span[data-state="pass"] { background: var(--green-500); }
.stress-scoreboard .ss-row span[data-state="fail"] { background: var(--error); }
@keyframes ss-pop {
  0%   { opacity: 0;                     transform: scale(0.3); }
  3%   { opacity: var(--final-op, 0.95); transform: scale(1); }
  72%  { opacity: var(--final-op, 0.95); transform: scale(1); }
  90%  { opacity: 0;                     transform: scale(0.85); }
  100% { opacity: 0;                     transform: scale(0.3); }
}
@media (prefers-reduced-motion: reduce) {
  .stress-scoreboard .ss-row span { animation: none; opacity: var(--final-op, 0.95); transform: none; }
}
/* 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". */
import { useEffect, useRef } from "react";

// StressScoreboard is a chapter-only narrative shape — render with the kit's CSS classes.
// Two rows of 25 dots; CSS animation handles the wave. Each dot gets --i (delay) and
// --final-op (held opacity) so the failure tail fades out while MagicBlocks stays solid.
function StressScoreboard() {
  const rowsRef = useRef<HTMLDivElement>(null);
  useEffect(() => {
    rowsRef.current?.querySelectorAll(".ss-row").forEach((row) => {
      if (row.childElementCount > 0) return;
      const total = 25;
      const isFail = row.classList.contains("is-fail");
      const solid = isFail ? 15 : 24;
      for (let i = 0; i < total; i++) {
        const s = document.createElement("span");
        s.style.setProperty("--i", String(i));
        s.dataset.state = i < solid ? "pass" : "fail";
        s.style.setProperty(
          "--final-op",
          i < solid
            ? "0.95"
            : Math.max(0.1, 0.6 - ((i - solid) / (total - solid)) * 0.5).toFixed(2),
        );
        row.appendChild(s);
      }
    });
  }, []);

  return (
    <div className="stress-scoreboard" aria-label="Production-scale completion rate">
      <div className="ss-pair">
        <div className="ss-stat is-pass">
          <div className="ss-stat-eyebrow">MagicBlocks</div>
          <div className="ss-stat-num">97.5<span style={{ fontSize: "0.4em", verticalAlign: "top" }}>%</span></div>
          <div className="ss-stat-cap">complete every lead, end-to-end, at production scale.</div>
        </div>
        <div className="ss-vs">vs</div>
        <div className="ss-stat is-fail">
          <div className="ss-stat-eyebrow">Single-prompt AI</div>
          <div className="ss-stat-num">59<span style={{ fontSize: "0.4em", verticalAlign: "top" }}>%</span></div>
          <div className="ss-stat-cap">drops the rest somewhere between turn 3 and turn 30.</div>
        </div>
      </div>
      <div>
        <div className="ss-cap" style={{ marginBottom: "var(--s-4)" }}>
          100 leads through each system · live trace
        </div>
        <div className="ss-rows" data-stress-rows aria-hidden="true" ref={rowsRef}>
          <div className="ss-row is-fail" data-row-label="Single-prompt" />
          <div className="ss-row is-pass" data-row-label="MagicBlocks" />
        </div>
      </div>
      <div className="ss-dot-legend">
        <span><i className="is-pass" />MagicBlocks · ~24/25 complete</span>
        <span><i className="is-fail" />Single-prompt · ~15/25 complete, tail drops</span>
      </div>
    </div>
  );
}

12.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.

12.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.

12.23 Scroll cue

Animated “scroll for more” indicator harvested from the research-report landing page. Mono uppercase label above a vertical hairline that pulses on a 2.4s loop. Position with the consumer's wrapper — the canonical placement is position: absolute; bottom: var(--s-5); left: 50%; transform: translateX(-50%) inside a position: relative hero. Honours prefers-reduced-motion; the line stays static, no pulse.

Pulsing hairline cue

.scroll-cue
Scroll for the data
<div class="scroll-cue">
  <span>Scroll for the data</span>
  <span class="scroll-cue-line" aria-hidden="true"></span>
</div>
.scroll-cue {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  font-family: var(--f-mono);
  font-size: 11px;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--fg-dim);
}

.scroll-cue-line {
  width: 1px;
  height: 38px;
  background: currentColor;
  opacity: 0.4;
  transform-origin: top;
  animation: scroll-cue-pulse 2.4s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .scroll-cue-line { animation: none; opacity: 0.5; }
}
import { ScrollCue } from "@magicblocksai/ui";

<ScrollCue>Scroll for the data</ScrollCue>

12.24 Dialogue contrast

Two-column conversation comparison for “our agent vs theirs”, “old script vs new”, before/after behaviour stories. Each column has a header (who · tag · status), a stack of message bubbles alternating from="us" and from="them", and an optional outcome banner pinned at the bottom. Tones drive the top stripe and the is-us bubble fill: win is accent pink, loss is warm grey, neutral has no stripe. Collapses to a single column below 720px.

Reliability Gap — OnePrompt vs MagicBlocks

.dialogue-contrast

The flagship comparison from the research-report landing page: same prompt, two agents. OnePrompt forgets it never got the phone number and moves on; MagicBlocks refuses to progress past Contact until the field is collected.

OnePrompt · run #400 Failed
AI What is your phone number?
C Why do you need that?
AI [explains privacy policy for 3 long paragraphs]
C Okay. Are rates going up next week?
Outcome The agent forgot it never got the phone number and moved on.
MagicBlocks · run #400 Completed
AI What is your phone number?
C Why do you need that?
AI I still need your phone number to continue your application.
Outcome The agent refused to progress past Contact until phone_number was collected.
<div class="dialogue-contrast">
  <article class="dialogue-column" data-tone="loss">
    <header class="dialogue-head">
      <span class="dialogue-who">OnePrompt</span>
      <span class="dialogue-tag">· run #400</span>
      <span class="dialogue-status"><span class="badge badge-danger">Failed</span></span>
    </header>
    <div class="dialogue-msgs">
      <div class="dialogue-msg is-us">
        <span class="dialogue-avatar">AI</span>
        <span class="dialogue-bubble">What is your phone number?</span>
      </div>
      <!-- … more <.dialogue-msg> rows -->
    </div>
    <div class="dialogue-outcome" data-tone="bad">
      <span class="dialogue-outcome-label">Outcome</span>
      <span>The agent <strong>forgot it never got the phone number</strong> and moved on.</span>
    </div>
  </article>
  <article class="dialogue-column" data-tone="win">… mirror structure …</article>
</div>
.dialogue-contrast {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-5);
  margin-top: var(--s-5);
}

@media (max-width: 720px) {
  .dialogue-contrast { grid-template-columns: 1fr; }
}
import {
  DialogueContrast,
  DialogueColumn,
  DialogueMessage,
  DialogueEllip,
  Badge,
} from "@magicblocksai/ui";

<DialogueContrast>
  <DialogueColumn
    tone="loss"
    who="OnePrompt"
    tag="· run #400"
    status={<Badge tone="danger">Failed</Badge>}
    outcome={{
      tone: "bad",
      text: <>The agent <strong>forgot it never got the phone number</strong> and moved on.</>,
    }}
  >
    <DialogueMessage from="us" avatar="AI">What is your phone number?</DialogueMessage>
    <DialogueMessage from="them" avatar="C">Why do you need that?</DialogueMessage>
    <DialogueMessage from="us" avatar="AI">
      <DialogueEllip>[explains privacy policy for 3 long paragraphs]</DialogueEllip>
    </DialogueMessage>
    <DialogueMessage from="them" avatar="C">Okay. Are rates going up next week?</DialogueMessage>
  </DialogueColumn>

  <DialogueColumn
    tone="win"
    who="MagicBlocks"
    tag="· run #400"
    status={<Badge tone="success">Completed</Badge>}
    outcome={{
      tone: "good",
      text: <>The agent <strong>refused to progress past Contact</strong> until <code>phone_number</code> was collected.</>,
    }}
  >
    <DialogueMessage from="us" avatar="AI">What is your phone number?</DialogueMessage>
    <DialogueMessage from="them" avatar="C">Why do you need that?</DialogueMessage>
    <DialogueMessage from="us" avatar="AI">I still need your phone number to continue your application.</DialogueMessage>
  </DialogueColumn>
</DialogueContrast>

12.25 Dialogue column

One side of a .dialogue-contrast. Useful on its own when the comparison is implicit — e.g. a single-column run log under a screenshot. The data-tone attribute drives the top-stripe colour and the is-us bubble fill: omit it for the neutral (no-stripe, dark-bubble) treatment.

Neutral standalone column

.dialogue-column
Charlie · transcript
AI Are you still looking to refinance the property on Maple?
C Yes — if rates are under 6 still.
<article class="dialogue-column">
  <header class="dialogue-head">
    <span class="dialogue-who">Charlie</span>
    <span class="dialogue-tag">· transcript</span>
  </header>
  <div class="dialogue-msgs">
    <div class="dialogue-msg is-us">
      <span class="dialogue-avatar">AI</span>
      <span class="dialogue-bubble">Are you still looking to refinance the property on Maple?</span>
    </div>
    <div class="dialogue-msg is-them">
      <span class="dialogue-avatar">C</span>
      <span class="dialogue-bubble">Yes — if rates are under 6 still.</span>
    </div>
  </div>
</article>
.dialogue-column {
  background: var(--bg-paper);
  border: 1px solid var(--hair);
  border-radius: var(--r-lg);
  padding: var(--s-5);
  box-shadow: var(--sh-1);
  display: flex;
  flex-direction: column;
  position: relative;
  overflow: hidden;
}

.dialogue-column[data-tone="win"]  { border-top: 3px solid var(--accent); }

.dialogue-column[data-tone="loss"] { border-top: 3px solid var(--warm-7); }

.dialogue-column[data-tone="win"]  .dialogue-msg.is-us .dialogue-bubble { background: var(--accent); }
import { DialogueColumn, DialogueMessage } from "@magicblocksai/ui";

<DialogueColumn who="Charlie" tag="· transcript">
  <DialogueMessage from="us" avatar="AI">
    Are you still looking to refinance the property on Maple?
  </DialogueMessage>
  <DialogueMessage from="them" avatar="C">
    Yes — if rates are under 6 still.
  </DialogueMessage>
</DialogueColumn>

12.26 Dialogue message

One row inside a column. from="us" renders right-aligned with the column-tone bubble fill; from="them" renders left-aligned with the warm bubble fill. The .dialogue-avatar is a 28px round with the speaker's initial; the .dialogue-bubble takes the body content.

Pair: us & them

.dialogue-msg
AI What is your phone number?
C Why do you need that?
<div class="dialogue-msg is-us">
  <span class="dialogue-avatar">AI</span>
  <span class="dialogue-bubble">What is your phone number?</span>
</div>
<div class="dialogue-msg is-them">
  <span class="dialogue-avatar">C</span>
  <span class="dialogue-bubble">Why do you need that?</span>
</div>
.dialogue-msg {
  display: flex;
  gap: 10px;
  max-width: 92%;
}

.dialogue-msg.is-them { align-self: flex-start; }

.dialogue-msg.is-us   { align-self: flex-end; flex-direction: row-reverse; }

.dialogue-msg.is-us .dialogue-avatar { background: var(--ink); color: var(--paper); }

.dialogue-msg.is-them .dialogue-bubble { border-radius: 14px 14px 14px 4px; }

.dialogue-msg.is-us .dialogue-bubble {
  background: var(--ink);
  color: var(--paper);
  border-radius: 14px 14px 4px 14px;
}

.dialogue-column[data-tone="win"]  .dialogue-msg.is-us .dialogue-bubble { background: var(--accent); }
import { DialogueMessage } from "@magicblocksai/ui";

<DialogueMessage from="us" avatar="AI">
  What is your phone number?
</DialogueMessage>
<DialogueMessage from="them" avatar="C">
  Why do you need that?
</DialogueMessage>

12.27 Dialogue ellipsis

Soft italic ellipsised span for stage-direction text inside a .dialogue-bubble — signals “the agent went on for a while” without quoting it verbatim. Used for “[explains privacy policy at length]”, “[tries to reroute to a sales rep]”, anything where the length is the point.

Stage-direction inside a bubble

.dialogue-bubble-ellip
AI [explains privacy policy for 3 long paragraphs]
<div class="dialogue-msg is-us">
  <span class="dialogue-avatar">AI</span>
  <span class="dialogue-bubble">
    <span class="dialogue-bubble-ellip">[explains privacy policy for 3 long paragraphs]</span>
  </span>
</div>
.dialogue-bubble {
  padding: 10px 14px;
  border-radius: 14px;
  font-size: 14px;
  line-height: 1.5;
  background: var(--bg-warm);
  color: var(--fg);
}

.dialogue-msg.is-them .dialogue-bubble { border-radius: 14px 14px 14px 4px; }

.dialogue-msg.is-us .dialogue-bubble {
  background: var(--ink);
  color: var(--paper);
  border-radius: 14px 14px 4px 14px;
}

.dialogue-column[data-tone="win"]  .dialogue-msg.is-us .dialogue-bubble { background: var(--accent); }

.dialogue-bubble-ellip {
  color: color-mix(in oklab, currentColor 60%, transparent);
  font-style: italic;
  font-size: 12.5px;
}
import { DialogueMessage, DialogueEllip } from "@magicblocksai/ui";

<DialogueMessage from="us" avatar="AI">
  <DialogueEllip>[explains privacy policy for 3 long paragraphs]</DialogueEllip>
</DialogueMessage>

12.28 Dot matrix

Proportion visualisation — a grid of dots where a subset is highlighted to communicate “out of N, M did X”. The canonical default is a 1,000-dot 40×25 grid (one dot per lead in a 1k-lead sample). Resize via total / cols for other ratios. Distribution modes: scatter (default) evenly spaces the highlights so the proportion reads visually; head / tail cluster them; random uses a deterministic PRNG so SSR + client agree on the layout.

Two systems, side by side

.dot-matrix

The flagship comparison from the Reliability Gap landing page: ~25 leads lost to agent errors on MagicBlocks vs ~410 on OnePrompt, at the same 1,000-lead sample.

MagicBlocks

~25 lost to agent errors.

OnePrompt

~410 lost to agent errors.

<div class="dot-matrix" role="img"
     aria-label="25 of 1,000 leads lost"
     style="--dm-cols: 40;">
  <!-- 1,000 <span class="dot-matrix-cell"> cells; scatter the
       25 highlighted ones (.is-highlight) evenly through the grid -->
</div>
.dot-matrix {
  display: grid;
  grid-template-columns: repeat(var(--dm-cols, 40), 1fr);
  gap: var(--dm-gap, 3px);
  margin-top: var(--s-4);
}

.dot-matrix-cell {
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: 2px;
  background: var(--dm-base, var(--bg-warm));
  opacity: 0.45;
}

.dot-matrix-cell.is-highlight {
  background: var(--dm-highlight, var(--accent));
  opacity: 1;
}
import { DotMatrix } from "@magicblocksai/ui";

<DotMatrix
  total={1000}
  highlight={25}
  highlightColor="var(--accent)"
  aria-label="25 of 1,000 leads lost to agent errors"
/>
<DotMatrix
  total={1000}
  highlight={410}
  highlightColor="var(--accent)"
  aria-label="410 of 1,000 leads lost to agent errors"
/>