Chapter 10 / 12 · Page templates

Page templates

The big-shape compositions that define MagicBlocks: pricing grid, sign-in, 404, the hero conversation preview, the dashboard shell, and the three empty-state tones. Anything here can be dropped whole onto a production page.

10.1 Pricing page

Three-tier card grid with the middle tier lifted into ink for emphasis. Pink ribbon for 'Most popular', pink accent on ink. Feature lists use pink checks — the single spot of chroma on the white tiers.

3-tier pricing

.pg-grid

Hero card translates up 6px with an ink surface and a pink corner ribbon. Features, price and CTA styling all flip cleanly between paper and ink.

Pricing

Priced per conversation,
not per seat.

Start free, scale when your pipeline does.

Starter

For founders testing the water.

$49/mo
  • 200 agent conversations / month
  • Email + chat channels
  • 1 AI agent persona
  • HubSpot integration
  • Community support
Start free
Most popular

Growth

When speed-to-lead pays for itself.

$249/mo
  • 2,000 agent conversations / month
  • All channels (chat, email, SMS, voice)
  • Up to 5 agent personas
  • HubSpot + Salesforce integrations
  • Priority email support
  • Tone and guardrail editor
Start 14-day trial

Scale

For teams with serious inbound volume.

Custom
  • Unlimited conversations
  • Unlimited personas + tone training
  • Dedicated success manager
  • SOC 2, SSO, audit logs
  • Custom integrations
  • 99.95% uptime SLA
Talk to sales
<section class="pg">
  <header class="pg-head">
    <p class="pg-eyebrow mono">Pricing</p>
    <h2 class="pg-title">Priced per <em>conversation</em>,<br>not per seat.</h2>
    <p class="pg-lede">Start free, scale when your pipeline does.</p>
    <div class="pg-toggle">
      <button class="is-active">Monthly</button>
      <button>Annual <span class="pg-save">-20%</span></button>
    </div>
  </header>
  <div class="pg-grid">
    <div class="pg-tier">
      <h3 class="pg-name">Starter</h3>
      <p class="pg-tag">For founders testing the water.</p>
      <div class="pg-price"><span class="pg-dollar">$</span><span class="pg-amt">49</span><span class="pg-per">/mo</span></div>
      <ul class="pg-feats">
        <li>200 agent conversations / month</li>
        <li>Email + chat channels</li>
        <li>1 AI agent persona</li>
        <li>HubSpot integration</li>
        <li>Community support</li>
      </ul>
      <a href="#" class="pg-cta">Start free</a>
    </div>

    <div class="pg-tier pg-tier-hero">
      <div class="pg-ribbon">Most popular</div>
      <h3 class="pg-name">Growth</h3>
      <p class="pg-tag">When speed-to-lead pays for itself.</p>
      <div class="pg-price"><span class="pg-dollar">$</span><span class="pg-amt">249</span><span class="pg-per">/mo</span></div>
      <ul class="pg-feats">
        <li>2,000 agent conversations / month</li>
        <li>All channels (chat, email, SMS, voice)</li>
        <li>Up to 5 agent personas</li>
        <li>HubSpot + Salesforce integrations</li>
        <li>Priority email support</li>
        <li>Tone and guardrail editor</li>
      </ul>
      <a href="#" class="pg-cta pg-cta-hero">Start 14-day trial</a>
    </div>

    <div class="pg-tier">
      <h3 class="pg-name">Scale</h3>
      <p class="pg-tag">For teams with serious inbound volume.</p>
      <div class="pg-price pg-price-custom">Custom</div>
      <ul class="pg-feats">
        <li>Unlimited conversations</li>
        <li>Unlimited personas + tone training</li>
        <li>Dedicated success manager</li>
        <li>SOC 2, SSO, audit logs</li>
        <li>Custom integrations</li>
        <li>99.95% uptime SLA</li>
      </ul>
      <a href="#" class="pg-cta">Talk to sales</a>
    </div>
  </div>
</section>
.pg { padding: var(--s-9) 0; }
.pg-head { text-align: center; max-width: 680px; margin: 0 auto var(--s-7); }
.pg-eyebrow { font-size: 11px; text-transform: uppercase; letter-spacing: 0.12em; color: var(--accent-text); font-weight: 600; margin: 0 0 var(--s-3); }
.pg-title { font: 600 clamp(32px, 4vw, 44px)/1.15 var(--f-display); letter-spacing: -0.02em; color: var(--fg); margin: 0 0 var(--s-3); }
.pg-title em { font-family: var(--f-italic); font-style: italic; font-weight: 400; color: var(--accent); font-variation-settings: "SOFT" 80; }
.pg-lede { font: 400 17px/1.55 var(--f-body); color: var(--fg-soft); margin: 0 0 var(--s-5); }
.pg-toggle {
  display: inline-flex; padding: 4px;
  background: var(--bg-paper); border: 1px solid var(--hair);
  border-radius: var(--r-pill);
}
.pg-toggle button {
  background: transparent; border: 0; cursor: pointer;
  padding: 8px 16px; border-radius: var(--r-pill);
  font: 500 13.5px/1 var(--f-body); color: var(--fg-soft);
}
.pg-toggle button.is-active { background: var(--ink); color: var(--paper); }
.pg-save { margin-left: 6px; font: 600 11px/1 var(--f-mono); color: var(--accent-text); }
.pg-toggle button.is-active .pg-save { color: var(--accent-text); }

.pg-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-4); align-items: stretch; }
@media (max-width: 880px) { .pg-grid { grid-template-columns: 1fr; } }

.pg-tier {
  background: var(--bg-paper);
  border: 1px solid var(--hair);
  border-radius: var(--r-lg);
  padding: var(--s-6);
  display: flex; flex-direction: column;
  transition: transform var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease);
}
.pg-tier:hover { transform: translateY(-2px); box-shadow: var(--sh-2); }
.pg-tier-hero {
  background: var(--ink); color: var(--paper);
  border-color: var(--ink);
  transform: translateY(-6px);
  box-shadow: 0 24px 60px -20px color-mix(in oklab, var(--ink) 40%, transparent);
  position: relative;
  overflow: hidden;
}
.pg-tier-hero::before {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(400px 300px at 80% -20%, color-mix(in oklab, var(--accent) 35%, transparent), transparent 60%);
  pointer-events: none;
}
.pg-ribbon {
  position: absolute; top: 16px; right: -32px;
  transform: rotate(35deg);
  background: var(--accent); color: var(--paper);
  font: 600 10.5px/1 var(--f-mono);
  text-transform: uppercase; letter-spacing: 0.1em;
  padding: 5px 36px; z-index: 2;
}
.pg-name { font: 500 14px/1 var(--f-mono); text-transform: uppercase; letter-spacing: 0.08em; color: var(--fg-dim); margin: 0 0 var(--s-2); position: relative; z-index: 1; }
.pg-tier-hero .pg-name { color: color-mix(in oklab, var(--paper) 70%, transparent); }
.pg-tag { font: 400 14px/1.5 var(--f-body); color: var(--fg-soft); margin: 0 0 var(--s-5); position: relative; z-index: 1; }
.pg-tier-hero .pg-tag { color: color-mix(in oklab, var(--paper) 75%, transparent); }
.pg-price { display: flex; align-items: baseline; margin-bottom: var(--s-5); position: relative; z-index: 1; }
.pg-dollar { font: 500 20px/1 var(--f-display); color: var(--fg-soft); margin-right: 2px; }
.pg-tier-hero .pg-dollar { color: color-mix(in oklab, var(--paper) 70%, transparent); }
.pg-amt { font: 600 52px/1 var(--f-display); color: var(--fg); letter-spacing: -0.025em; font-variant-numeric: tabular-nums; }
.pg-tier-hero .pg-amt { color: var(--paper); }
.pg-per { font: 500 14px/1 var(--f-body); color: var(--fg-dim); margin-left: 6px; }
.pg-tier-hero .pg-per { color: color-mix(in oklab, var(--paper) 60%, transparent); }
.pg-price-custom { font: 600 36px/1 var(--f-display); color: var(--fg); letter-spacing: -0.02em; }
.pg-feats { list-style: none; padding: 0; margin: 0 0 var(--s-6); flex: 1; position: relative; z-index: 1; }
.pg-feats li {
  display: flex; align-items: flex-start; gap: 10px;
  font: 400 14px/1.55 var(--f-body); color: var(--fg);
  padding: 8px 0;
  border-bottom: 1px solid var(--hair-soft);
}
.pg-feats li:last-child { border-bottom: 0; }
.pg-feats li::before {
  content: "✓"; color: var(--accent-text); font-weight: 600;
  flex-shrink: 0; width: 16px; text-align: center;
}
.pg-tier-hero .pg-feats li { color: color-mix(in oklab, var(--paper) 92%, transparent); border-color: color-mix(in oklab, var(--paper) 12%, transparent); }
.pg-cta {
  display: block; text-align: center;
  background: var(--bg-paper); color: var(--fg);
  border: 1px solid var(--hair);
  padding: 12px 16px; border-radius: var(--r-md);
  font: 600 14px/1 var(--f-display); text-decoration: none;
  position: relative; z-index: 1;
  transition: background var(--dur-2) var(--ease);
}
.pg-cta:hover { background: var(--bg-warm); color: var(--fg); }
.pg-cta-hero { background: var(--accent); color: var(--paper); border-color: transparent; box-shadow: var(--sh-pink); }
.pg-cta-hero:hover { background: var(--accent); color: var(--paper); transform: translateY(-1px); }

10.2 Auth — sign in

Two-panel layout: form on the left, testimonial on the right. Google SSO, divider, email/password. Right panel uses the warm-3 canvas + pink bloom + Fraunces quote to humanise the moment.

Sign-in page

.au

On mobile the sides stack with the testimonial on top. Form focus uses the pink + soft halo. Forgot-password link sits inline with the password label.

MagicBlocks

Welcome back.

Sign in to your agents, conversations, and dashboards.

or

New to MagicBlocks? Create an account

<section class="au">
  <div class="au-card">
    <a class="au-brand" href="#"><span class="au-dot"></span> MagicBlocks</a>
    <h1 class="au-title">Welcome back.</h1>
    <p class="au-lede">Sign in to your agents, conversations, and dashboards.</p>

    <button class="au-google"><svg width="14" height="14" viewBox="0 0 24 24"><path fill="#EA4335" d="M12 11v3.05h8.5c-.35 1.9-1.5 3.5-3.2 4.6l5 3.9c2.9-2.7 4.6-6.6 4.6-11.25 0-.95-.09-1.85-.25-2.75H12z"/><path fill="#34A853" d="M12 22c3.3 0 6.05-1.1 8.1-3l-5-3.9c-1.1.75-2.55 1.2-3.1 1.2-2.35 0-4.3-1.6-5-3.75H1.7V15c2.05 4.1 6.3 7 10.3 7z" transform="translate(-1 -.25)"/><path fill="#FBBC05" d="M6 14.25c-.2-.55-.3-1.15-.3-1.75s.1-1.2.3-1.75V8.15H1.65C1.25 9.45 1 10.7 1 12s.25 2.55.65 3.85L6 14.25z" transform="translate(-1 -.25)"/><path fill="#EA4335" d="M12 5.45c1.8 0 3.4.65 4.65 1.8l3.45-3.45C18.05 1.8 15.3.75 12 .75 7.95.75 3.75 3.6 1.7 7.75L6 11c.7-2.15 2.65-3.75 5-3.75z" transform="translate(0 -.5)"/></svg> <span>Continue with Google</span></button>

    <div class="au-div"><span>or</span></div>

    <form class="au-form">
      <label class="au-field">
        <span>Email</span>
        <input type="email" placeholder="you@company.com" value="jay@magicblocks.ai">
      </label>
      <label class="au-field">
        <span>Password <a href="#" class="au-forgot">Forgot?</a></span>
        <input type="password" placeholder="••••••••" value="correct-horse-battery">
      </label>
      <button type="submit" class="au-submit">Sign in <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg></button>
    </form>

    <p class="au-foot">New to MagicBlocks? <a href="#">Create an account</a></p>
  </div>
  <aside class="au-side">
    <div class="au-quote-glyph">&ldquo;</div>
    <blockquote class="au-quote">
      Our inbound replies went from <em>seven minutes</em> to <em>seven seconds</em>. Thursday was the first day we closed more from inbound than outbound.
    </blockquote>
    <div class="au-quote-cite">
      <span class="av" style="background:#FFE2EC; color:var(--accent-text);">MR</span>
      <div>
        <div class="au-quote-name">Maya Ruiz</div>
        <div class="au-quote-role">VP Sales · Northpeak Mortgage</div>
      </div>
    </div>
  </aside>
</section>
.au {
  display: grid; grid-template-columns: 1fr 1fr; gap: 0;
  background: var(--bg-paper);
  border: 1px solid var(--hair);
  border-radius: var(--r-xl);
  overflow: hidden;
  min-height: 580px;
}
@media (max-width: 880px) { .au { grid-template-columns: 1fr; } .au-side { order: -1; } }
.au-card { padding: var(--s-8) var(--s-7); display: flex; flex-direction: column; }
.au-brand { display: inline-flex; align-items: center; gap: 10px; font: 700 16px/1 var(--f-display); color: var(--fg); text-decoration: none; letter-spacing: -0.01em; margin-bottom: var(--s-7); }
.au-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 4px var(--accent-soft); }
.au-title { font: 600 32px/1.15 var(--f-display); letter-spacing: -0.02em; color: var(--fg); margin: 0 0 8px; }
.au-lede { font: 400 15px/1.55 var(--f-body); color: var(--fg-soft); margin: 0 0 var(--s-6); }
.au-google {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%;
  background: var(--bg-paper); border: 1px solid var(--hair);
  padding: 12px 16px; border-radius: var(--r-md);
  font: 600 14px/1 var(--f-display); color: var(--fg); cursor: pointer;
  transition: background var(--dur-2) var(--ease);
}
.au-google:hover { background: var(--bg-warm); }
.au-div {
  position: relative; text-align: center;
  margin: var(--s-5) 0;
  font: 500 11.5px/1 var(--f-mono);
  text-transform: uppercase; letter-spacing: 0.1em; color: var(--fg-dim);
}
.au-div::before { content: ""; position: absolute; left: 0; right: 0; top: 50%; height: 1px; background: var(--hair); z-index: 0; }
.au-div span { position: relative; z-index: 1; background: var(--bg-paper); padding: 0 10px; }
.au-form { display: flex; flex-direction: column; gap: var(--s-4); }
.au-field { display: flex; flex-direction: column; gap: 6px; }
.au-field > span:first-child {
  display: flex; justify-content: space-between; align-items: baseline;
  font: 500 12.5px/1 var(--f-body); color: var(--fg-soft);
}
.au-forgot { font: 500 12px/1 var(--f-body); color: var(--accent-text); text-decoration: none; }
.au-forgot:hover { text-decoration: underline; }
.au-field input {
  font: 400 15px/1 var(--f-body);
  padding: 12px 14px;
  background: var(--bg-paper);
  border: 1px solid var(--hair);
  border-radius: var(--r-md);
  color: var(--fg);
  transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);
}
.au-field input:focus { outline: 0; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.au-submit {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  background: var(--accent); color: var(--paper); border: 0;
  padding: 13px 20px; border-radius: var(--r-md);
  font: 600 15px/1 var(--f-display); cursor: pointer;
  box-shadow: var(--sh-pink);
  margin-top: var(--s-3);
  transition: transform var(--dur-2) var(--ease);
}
.au-submit:hover { transform: translateY(-1px); }
.au-foot { margin-top: auto; padding-top: var(--s-5); font: 400 13.5px/1.5 var(--f-body); color: var(--fg-soft); }
.au-foot a { color: var(--accent-text); text-decoration: none; font-weight: 500; }
.au-foot a:hover { text-decoration: underline; }

.au-side {
  background: var(--warm-3);
  padding: var(--s-8) var(--s-7);
  display: flex; flex-direction: column; justify-content: center;
  position: relative; overflow: hidden;
  /* pinned warm — rescope type tokens so dark-mode copy reads */
  --fg:      var(--ink);
  --fg-soft: color-mix(in oklab, var(--ink) 68%, transparent);
  --fg-dim:  color-mix(in oklab, var(--ink) 48%, transparent);
  --hair:    rgba(25, 30, 50, 0.09);
  color: var(--fg);
}
.au-side::before {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(400px 320px at 100% 0%, color-mix(in oklab, var(--accent) 22%, transparent), transparent 60%);
  pointer-events: none;
}
.au-quote-glyph { position: relative; font: 400 86px/0.8 var(--f-italic); font-style: italic; color: var(--accent-text); font-variation-settings: "SOFT" 80; margin-bottom: var(--s-3); }
.au-quote { position: relative; font: 400 22px/1.4 var(--f-display); letter-spacing: -0.01em; color: var(--fg); margin: 0 0 var(--s-6); }
.au-quote em { font-family: var(--f-italic); font-style: italic; font-weight: 400; color: var(--accent); font-variation-settings: "SOFT" 80; }
.au-quote-cite { position: relative; display: flex; align-items: center; gap: var(--s-3); }
.au-quote-name { font: 600 14px/1.2 var(--f-display); color: var(--fg); }
.au-quote-role { font: 400 12.5px/1.3 var(--f-body); color: var(--fg-soft); margin-top: 2px; }

10.3 404 / not found

Warm cream canvas with double radial blooms, mono eyebrow with route, Fraunces-italic emotional phrase in the title, and two ways out — a homepage CTA plus a suggested-links grid.

Not-found page

.nf

The suggestion list is the real work — a 404 that only says 'sorry' is a dead end. Always offer 4 next-best pages.

404 · page not found

You took a turn at an imaginary corridor.

The page you're looking for doesn't exist — or it moved house. Let's get you back to somewhere real.

<section class="nf">
  <div class="nf-bloom"></div>
  <div class="nf-inner">
    <p class="nf-eyebrow mono">404 · page not found</p>
    <h1 class="nf-title">You took a turn at an <em>imaginary</em> corridor.</h1>
    <p class="nf-lede">The page you're looking for doesn't exist — or it moved house. Let's get you back to somewhere real.</p>
    <div class="nf-actions">
      <a href="#" class="nf-cta">Back to homepage <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg></a>
      <a href="#" class="nf-link">Browse the docs</a>
    </div>
    <div class="nf-sug">
      <p class="nf-sug-label mono">You might be looking for</p>
      <ul>
        <li><a href="#">Agent Composer</a></li>
        <li><a href="#">Tone training guide</a></li>
        <li><a href="#">Integrations — HubSpot</a></li>
        <li><a href="#">Changelog</a></li>
      </ul>
    </div>
  </div>
</section>
.nf {
  position: relative;
  padding: var(--s-11) var(--s-7);
  background: var(--warm-3);
  border-radius: var(--r-xl);
  overflow: hidden;
  isolation: isolate;
  min-height: 480px;
  /* pinned warm — rescope type tokens so dark-mode copy reads */
  --fg:      var(--ink);
  --fg-soft: color-mix(in oklab, var(--ink) 68%, transparent);
  --fg-dim:  color-mix(in oklab, var(--ink) 48%, transparent);
  --hair:    rgba(25, 30, 50, 0.09);
  color: var(--fg);
}
.nf-bloom {
  position: absolute; inset: 0;
  background:
    radial-gradient(500px 400px at 20% 100%, color-mix(in oklab, var(--accent) 20%, transparent), transparent 60%),
    radial-gradient(400px 300px at 90% 10%, color-mix(in oklab, #FFC6A5 55%, transparent), transparent 60%);
  z-index: -1;
}
.nf-inner { max-width: 640px; }
.nf-eyebrow { font-size: 11px; text-transform: uppercase; letter-spacing: 0.12em; color: var(--accent-text); font-weight: 600; margin: 0 0 var(--s-4); }
.nf-title {
  font: 600 clamp(36px, 5vw, 56px)/1.1 var(--f-display);
  letter-spacing: -0.025em; color: var(--fg);
  margin: 0 0 var(--s-4);
}
.nf-title em { font-family: var(--f-italic); font-style: italic; font-weight: 400; color: var(--accent); font-variation-settings: "SOFT" 80; }
.nf-lede { font: 400 18px/1.55 var(--f-body); color: var(--fg-soft); margin: 0 0 var(--s-6); max-width: 560px; }
.nf-actions { display: flex; gap: var(--s-4); align-items: center; flex-wrap: wrap; margin-bottom: var(--s-7); }
.nf-cta {
  display: inline-flex; align-items: center; gap: 10px;
  background: var(--accent); color: var(--paper);
  font: 600 14.5px/1 var(--f-display);
  padding: 13px 20px;
  border-radius: var(--r-pill);
  text-decoration: none;
  box-shadow: var(--sh-pink);
  transition: transform var(--dur-2) var(--ease);
}
.nf-cta:hover { transform: translateY(-2px); color: var(--paper); }
.nf-link { color: var(--fg); text-decoration: none; font: 500 14px/1 var(--f-body); border-bottom: 1px solid currentColor; padding: 4px 2px; }
.nf-link:hover { color: var(--accent-text); }
.nf-sug { border-top: 1px solid var(--hair); padding-top: var(--s-5); max-width: 480px; }
.nf-sug-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--fg-dim); font-weight: 600; margin: 0 0 var(--s-3); }
.nf-sug ul { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 10px var(--s-5); }
.nf-sug a { color: var(--fg); text-decoration: none; font: 500 14px/1.4 var(--f-body); }
.nf-sug a:hover { color: var(--accent-text); }

10.4 Conversation preview

The money shot. A chat surface showing the MagicBlocks agent in flow — inbound bubble in warm+hair, outbound in pink. Fraunces italic picks out the key stat inside the bubble. Typing dots animate the next-reply moment.

Agent conversation

.cv

Use on landing pages, dashboards, and case studies. Keep the transcript tight — a real agent sounds human, not verbose.

M
Mia · MagicBlocks Agent
SDR persona · warm & curious
Live
Today · 2:14 PM
AC

Hi — saw you help mortgage brokers with AI sales. Curious what kind of volume you see on the inbound side.

Hi Alicia! Great to meet you. Our brokers average 400–800 inbound leads a week — we usually see speed-to-lead drop from 7 minutes to under 30 seconds. What's your current volume like?

M
AC

We're at about 600/week. Honestly our problem isn't volume, it's the nights and weekends — leads go cold.

That's exactly the wedge. Our agents cover 24/7 and route the hot ones to a human within minutes. Want me to put 20 minutes on the calendar for Thursday — I'll show you it running on your own site?

M
AC
<section class="cv">
  <header class="cv-head">
    <div class="cv-persona">
      <span class="av" style="background:var(--accent-soft); color: var(--accent-text);">M</span>
      <div>
        <div class="cv-name">Mia · MagicBlocks Agent</div>
        <div class="cv-role">SDR persona · warm &amp; curious</div>
      </div>
    </div>
    <span class="chip chip-green"><span class="dot dot-green"></span> Live</span>
  </header>
  <div class="cv-thread">
    <div class="cv-meta mono">Today · 2:14 PM</div>

    <div class="cv-msg cv-in">
      <span class="av av-sm" style="background:var(--info-soft); color: var(--info-text);">AC</span>
      <div class="cv-bubble">
        <p>Hi — saw you help mortgage brokers with AI sales. Curious what kind of volume you see on the inbound side.</p>
      </div>
    </div>

    <div class="cv-msg cv-out">
      <div class="cv-bubble cv-bubble-pink">
        <p>Hi Alicia! Great to meet you. Our brokers average 400–800 inbound leads a week — we usually see speed-to-lead drop from <em>7 minutes to under 30 seconds</em>. What's your current volume like?</p>
      </div>
      <span class="av av-sm" style="background:var(--accent-soft); color: var(--accent-text);">M</span>
    </div>

    <div class="cv-msg cv-in">
      <span class="av av-sm" style="background:var(--info-soft); color: var(--info-text);">AC</span>
      <div class="cv-bubble">
        <p>We're at about 600/week. Honestly our problem isn't volume, it's the nights and weekends — leads go cold.</p>
      </div>
    </div>

    <div class="cv-msg cv-out">
      <div class="cv-bubble cv-bubble-pink">
        <p>That's exactly the wedge. Our agents cover 24/7 and route the hot ones to a human within minutes. Want me to put 20 minutes on the calendar for Thursday — I'll show you it running on your own site?</p>
      </div>
      <span class="av av-sm" style="background:var(--accent-soft); color: var(--accent-text);">M</span>
    </div>

    <div class="cv-typing">
      <span class="av av-sm" style="background:var(--info-soft); color: var(--info-text);">AC</span>
      <div class="cv-bubble cv-bubble-typing">
        <span></span><span></span><span></span>
      </div>
    </div>
  </div>
  <footer class="cv-foot">
    <div class="cv-meta-inline mono">
      <span class="dot dot-pink"></span> Agent handled this thread &middot; <a href="#">view full transcript →</a>
    </div>
  </footer>
</section>
.cv {
  background: var(--bg-paper);
  border: 1px solid var(--hair);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
  overflow: hidden;
  max-width: 720px;
  margin: 0 auto;
}
.cv-head { display: flex; align-items: center; justify-content: space-between; padding: var(--s-4) var(--s-5); border-bottom: 1px solid var(--hair); background: var(--bg-warm); }
.cv-persona { display: flex; align-items: center; gap: var(--s-3); }
.cv-name { font: 600 14.5px/1.2 var(--f-display); color: var(--fg); letter-spacing: -0.005em; }
.cv-role { font: 400 12.5px/1.3 var(--f-body); color: var(--fg-soft); margin-top: 2px; }

.cv-thread { padding: var(--s-5); display: flex; flex-direction: column; gap: var(--s-3); }
.cv-meta { text-align: center; font-size: 11px; color: var(--fg-dim); padding: 4px 0; letter-spacing: 0.05em; }

.cv-msg { display: flex; gap: 10px; align-items: flex-end; max-width: 88%; }
.cv-in { align-self: flex-start; }
.cv-out { align-self: flex-end; flex-direction: row-reverse; }

.cv-bubble {
  padding: 10px 14px;
  background: var(--bg-warm);
  border: 1px solid var(--hair);
  border-radius: 16px 16px 16px 4px;
  font: 400 14.5px/1.5 var(--f-body); color: var(--fg);
}
.cv-bubble p { margin: 0; }
.cv-bubble em { font-family: var(--f-italic); font-style: italic; font-weight: 500; color: var(--accent-text); font-variation-settings: "SOFT" 80; }

.cv-bubble-pink {
  background: var(--accent);
  color: var(--paper);
  border-color: transparent;
  border-radius: 16px 16px 4px 16px;
  box-shadow: var(--sh-pink);
}
.cv-bubble-pink em { color: var(--paper); text-decoration: underline; text-decoration-thickness: 1.5px; text-underline-offset: 3px; }

.cv-typing { display: flex; gap: 10px; align-items: center; align-self: flex-start; }
.cv-bubble-typing { display: flex; gap: 4px; padding: 14px 16px; }
.cv-bubble-typing span {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--fg-dim);
  animation: cv-blink 1.2s ease-in-out infinite;
}
.cv-bubble-typing span:nth-child(2) { animation-delay: 0.2s; }
.cv-bubble-typing span:nth-child(3) { animation-delay: 0.4s; }
@keyframes cv-blink { 0%, 60%, 100% { opacity: 0.3; } 30% { opacity: 1; } }

.cv-foot { padding: var(--s-3) var(--s-5); border-top: 1px solid var(--hair); background: var(--bg-warm); }
.cv-meta-inline { font-size: 11.5px; color: var(--fg-dim); display: flex; align-items: center; gap: 8px; letter-spacing: 0.04em; }
.cv-meta-inline a { color: var(--accent-text); text-decoration: none; margin-left: auto; }
.cv-meta-inline a:hover { text-decoration: underline; }

10.5 Dashboard shell

A warm sidebar + paper main layout. Sidebar uses warm-3 to separate from content; the active nav item gets the pink-soft pill. Main surface has a greeting, three stat cards, and a live-conversations feed.

App dashboard

.dash

Sidebar is ALWAYS warm-3 in MagicBlocks — never dark. Pink-soft pill for active nav, pink CTA on the top right, pink accent for deltas.

Overview

Good morning, Jay.

Conversations today
127
+18 vs yesterday
Meetings booked
14
+4
Avg. response time
27s
holding steady

Live conversations

AC
Alicia Chen
SMS · 2m · awaiting reply
Qualified
MR
Marcus Reyes
Email · 14m · 3 turns
Negotiating
JP
Jordan Park
Voice · ended 1h ago · meeting booked
Won
<section class="dash">
  <aside class="dash-side">
    <div class="dash-brand">
      <span class="au-dot"></span> MagicBlocks
    </div>
    <nav class="dash-nav">
      <p class="dash-nav-label mono">Workspace</p>
      <a href="#" class="dash-nav-item is-active"><span>Overview</span></a>
      <a href="#" class="dash-nav-item"><span>Agents</span><span class="chip chip-pink" style="font-size:10px;padding:2px 6px;">3</span></a>
      <a href="#" class="dash-nav-item"><span>Conversations</span></a>
      <a href="#" class="dash-nav-item"><span>Contacts</span></a>
      <p class="dash-nav-label mono" style="margin-top: var(--s-5);">Settings</p>
      <a href="#" class="dash-nav-item"><span>Channels</span></a>
      <a href="#" class="dash-nav-item"><span>Integrations</span></a>
      <a href="#" class="dash-nav-item"><span>Team</span></a>
    </nav>
  </aside>
  <main class="dash-main">
    <header class="dash-main-head">
      <div>
        <p class="mono" style="font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--fg-dim); margin: 0 0 4px;">Overview</p>
        <h1 style="font: 600 24px/1.2 var(--f-display); letter-spacing: -0.01em; color: var(--fg); margin: 0;">Good morning, Jay.</h1>
      </div>
      <button class="dash-act">New agent <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg></button>
    </header>
    <div class="dash-stats">
      <div class="dash-stat"><div class="dash-stat-l mono">Conversations today</div><div class="dash-stat-n">127</div><div class="dash-stat-d">+18 vs yesterday</div></div>
      <div class="dash-stat"><div class="dash-stat-l mono">Meetings booked</div><div class="dash-stat-n">14</div><div class="dash-stat-d">+4</div></div>
      <div class="dash-stat"><div class="dash-stat-l mono">Avg. response time</div><div class="dash-stat-n">27s</div><div class="dash-stat-d">holding steady</div></div>
    </div>
    <div class="dash-feed">
      <p class="mono" style="font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--fg-dim); margin: 0 0 var(--s-3);">Live conversations</p>
      <div class="dash-row"><span class="av av-sm" style="background:var(--info-soft); color: var(--info-text);">AC</span><div><strong>Alicia Chen</strong><div style="font-size: 12px; color: var(--fg-dim); margin-top: 2px;">SMS · 2m · <span style="color:var(--accent);">awaiting reply</span></div></div><span class="chip chip-blue" style="margin-left:auto;">Qualified</span></div>
      <div class="dash-row"><span class="av av-sm" style="background:#FFE2EC; color:var(--accent-text);">MR</span><div><strong>Marcus Reyes</strong><div style="font-size: 12px; color: var(--fg-dim); margin-top: 2px;">Email · 14m · 3 turns</div></div><span class="chip chip-pink" style="margin-left:auto;">Negotiating</span></div>
      <div class="dash-row"><span class="av av-sm" style="background:#DDEFDD; color:#1E6B44;">JP</span><div><strong>Jordan Park</strong><div style="font-size: 12px; color: var(--fg-dim); margin-top: 2px;">Voice · ended 1h ago · meeting booked</div></div><span class="chip chip-green" style="margin-left:auto;">Won</span></div>
    </div>
  </main>
</section>
.dash {
  display: grid; grid-template-columns: 240px 1fr;
  background: var(--bg-paper); border: 1px solid var(--hair);
  border-radius: var(--r-xl); overflow: hidden; min-height: 560px;
}
@media (max-width: 720px) { .dash { grid-template-columns: 1fr; } .dash-side { display: none; } }
.dash-side {
  background: var(--warm-3); padding: var(--s-4) var(--s-4); border-right: 1px solid var(--hair);
  /* pinned warm — rescope type tokens */
  --fg: var(--ink); --fg-soft: color-mix(in oklab, var(--ink) 68%, transparent); --fg-dim: color-mix(in oklab, var(--ink) 48%, transparent); --hair: rgba(25, 30, 50, 0.09); color: var(--fg);
}
.dash-brand { display: inline-flex; align-items: center; gap: 10px; font: 700 15px/1 var(--f-display); color: var(--fg); letter-spacing: -0.005em; padding: 6px 10px; margin-bottom: var(--s-5); }
.dash-nav-label { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--fg-dim); font-weight: 600; margin: 0 10px var(--s-2); }
.dash-nav-item {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px;
  font: 500 13.5px/1 var(--f-body);
  color: var(--fg-soft);
  text-decoration: none;
  border-radius: var(--r-sm);
  margin-bottom: 2px;
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.dash-nav-item > span:first-child { flex: 1; }
.dash-nav-item:hover { background: var(--bg-paper); color: var(--fg); }
.dash-nav-item.is-active { background: var(--accent-soft); color: var(--accent-text); font-weight: 600; }

.dash-main { padding: var(--s-6); }
.dash-main-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--s-5); gap: var(--s-3); flex-wrap: wrap; }
.dash-act {
  display: inline-flex; align-items: center; gap: 8px;
  background: var(--accent); color: var(--paper); border: 0;
  padding: 10px 16px; border-radius: var(--r-md);
  font: 600 13.5px/1 var(--f-display); cursor: pointer;
  box-shadow: var(--sh-pink);
}
.dash-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-3); margin-bottom: var(--s-6); }
@media (max-width: 560px) { .dash-stats { grid-template-columns: 1fr; } }
.dash-stat { padding: var(--s-4); background: var(--bg-paper); border: 1px solid var(--hair); border-radius: var(--r-md); }
.dash-stat-l { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--fg-dim); font-weight: 600; margin-bottom: 6px; }
.dash-stat-n { font: 600 28px/1 var(--f-display); color: var(--fg); letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.dash-stat-d { font: 500 12px/1.3 var(--f-body); color: var(--accent-text); margin-top: 6px; }
.dash-feed > p { margin-bottom: var(--s-3); }
.dash-row { display: flex; align-items: center; gap: var(--s-3); padding: 12px var(--s-3); border-bottom: 1px solid var(--hair-soft); }
.dash-row:last-child { border-bottom: 0; }
.dash-row strong { font: 600 14px/1.2 var(--f-display); color: var(--fg); }

10.6 Empty-state pages

Three tones of empty: first-run (come-in-and-set-up), zero-results (try again), and error (something broke). Same structure, different icon tint and different action.

Three empty states

.empty

First-run uses warm icon + pink button. Zero-results uses sunken icon + ghost button. Error uses pink-soft icon + retry button.

No leads yet

Connect a source and your first conversation will appear here.

Something went sideways

We couldn't load this data. Try again, or ping us if it keeps happening.

<div class="emp-grid">
  <div class="empty">
    <div class="empty-ic"><svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12"/><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"/></svg></div>
    <h3 class="empty-title">No leads yet</h3>
    <p class="empty-lede">Connect a source and your first conversation will appear here.</p>
    <div class="empty-actions"><a href="#" class="empty-btn">Connect a source</a></div>
  </div>
  <div class="empty empty-search">
    <div class="empty-ic empty-ic-ghost"><svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg></div>
    <h3 class="empty-title">No matches</h3>
    <p class="empty-lede">Try a different keyword or clear your filters.</p>
    <div class="empty-actions"><button class="empty-ghost">Clear filters</button></div>
  </div>
  <div class="empty empty-error">
    <div class="empty-ic empty-ic-error"><svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><circle cx="12" cy="12" r="10"/><path d="M12 8v4M12 16h.01"/></svg></div>
    <h3 class="empty-title">Something went sideways</h3>
    <p class="empty-lede">We couldn't load this data. Try again, or ping us if it keeps happening.</p>
    <div class="empty-actions"><button class="empty-btn">Retry</button><a href="#" class="empty-link">Contact support →</a></div>
  </div>
</div>
/* Three-up empty-state grid — variants tint the icon well.
   Base .empty / .empty-ic / .empty-ghost primitives now live in
   _shared.css so every chapter gets them; only the chapter-specific
   icon-background tint variants stay here. */
.emp-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-4); }
@media (max-width: 880px) { .emp-grid { grid-template-columns: 1fr; } }
.emp-grid .empty { margin: 0; padding: var(--s-6) var(--s-4); }
.empty-ic-ghost { background: var(--bg-sunk) !important; }
.empty-ic-error { background: var(--accent-soft) !important; color: var(--accent-text) !important; }

10.7 Settings page

The canonical settings shell — left nav with grouped sections, right pane with section title + lede + setting rows. Each row has a label, a description, and a control (input, switch, button, select). Save is bottom-right and only appears when dirty.

Settings shell

.settings

Two-column grid: 200px nav column, fluid content. Rows are dividing, not carded. The sticky save bar is hidden until a value changes.

Profile

This is how the rest of the team sees you.

Display name
What appears on messages and activity logs.
Email digest
Summary of every new lead, delivered at 8am.
Time zone
Used for scheduled sends and digest timing.
<div class="settings">
  <aside class="settings-nav">
    <div class="settings-group-label">Account</div>
    <a class="settings-nav-link is-active">Profile</a>
  </aside>
  <div class="settings-pane">
    <h2 class="settings-title">Profile</h2>
    <div class="settings-row">
      <div class="settings-row-label">Display name</div>
      <input class="input" />
    </div>
  </div>
</div>
.settings { display: grid; grid-template-columns: 200px 1fr;
  gap: var(--s-6); max-width: 880px; margin: 0 auto; }
@media (max-width: 720px) { .settings { grid-template-columns: 1fr; } }
.settings-nav { display: flex; flex-direction: column; gap: var(--s-4); }
.settings-group-label { font: 500 11px/1 var(--f-mono); text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--fg-dim); margin-bottom: var(--s-2); }
.settings-nav-link { padding: 8px 12px; border-radius: var(--r-xs);
  font: 500 14px/1 var(--f-body); color: var(--fg-soft); text-decoration: none;
  cursor: pointer; }
.settings-nav-link:hover { background: var(--bg-sunk); color: var(--fg); }
.settings-nav-link.is-active { background: var(--accent-soft); color: var(--accent-text); }
.settings-head { margin-bottom: var(--s-5); padding-bottom: var(--s-4);
  border-bottom: 1px solid var(--hair); }
.settings-title { font: 700 24px/1.2 var(--f-display); color: var(--fg); margin: 0; }
.settings-lede  { font: 400 14px/1.55 var(--f-body); color: var(--fg-soft); margin: var(--s-1) 0 0; }
.settings-row { display: grid; grid-template-columns: 1fr auto;
  gap: var(--s-5); align-items: center;
  padding: var(--s-4) 0; border-bottom: 1px solid var(--hair); }
.settings-row-label { font: 600 14px/1.3 var(--f-body); color: var(--fg); }
.settings-row-desc  { font: 400 13px/1.5 var(--f-body); color: var(--fg-soft);
  margin-top: 2px; }

10.8 Onboarding

First-run flow — a three-step welcome with a progress stepper at the top, a single focused panel in the middle, and "Back / Continue" at the bottom. Warm page surface; panel is paper. One decision per step.

Onboarding step

.ob

Step 2 of 3: "Connect your inbox". Top stepper shows progress; panel is a single focused action; primary CTA right, ghost back left.

  1. Create account
  2. 2 Connect your inbox
  3. 3 Meet your agent
Step 2 of 3

Connect your inbox.

Your agent replies from your real address — so every lead knows a human is nearby.

<section class="ob">
  <ol class="ob-steps">
    <li class="is-done">Create account</li>
    <li class="is-active">Connect your inbox</li>
    <li>Meet your agent</li>
  </ol>
  <div class="ob-panel">
    <h2 class="ob-title">Connect your <em>inbox</em>.</h2>
    <button class="ob-choice">Continue with Google</button>
  </div>
</section>
.ob { max-width: 560px; margin: 0 auto; }
.ob-steps { list-style: none; display: flex; gap: var(--s-3);
  padding: 0; margin: 0 0 var(--s-5); }
.ob-steps li { flex: 1; display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) 0; font: 500 12px/1 var(--f-mono);
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--fg-dim);
  border-top: 2px solid var(--hair); }
.ob-steps .is-active { color: var(--accent-text); border-top-color: var(--accent); }
.ob-steps .is-done   { color: var(--fg-soft); border-top-color: var(--accent); }
.ob-dot { width: 20px; height: 20px; border-radius: 50%;
  background: var(--bg-sunk); color: var(--fg-soft);
  display: inline-flex; align-items: center; justify-content: center;
  font: 600 11px/1 var(--f-mono); }
.ob-steps .is-active .ob-dot { background: var(--accent); color: var(--paper); }
.ob-steps .is-done .ob-dot   { background: var(--accent-soft); color: var(--accent-text); }
.ob-panel { padding: var(--s-6); background: var(--bg-paper);
  border: 1px solid var(--hair); border-radius: var(--r-lg); }
.ob-eyebrow { font-size: 11px; color: var(--fg-dim);
  text-transform: uppercase; letter-spacing: 0.08em; }
.ob-title { font: 700 28px/1.15 var(--f-display); color: var(--fg);
  margin: var(--s-2) 0 var(--s-3); text-wrap: balance; }
.ob-title em { font-family: var(--f-serif); font-style: italic;
  font-variation-settings: "SOFT" 80; color: var(--accent); font-weight: 400; }
.ob-lede { font: 400 15px/1.6 var(--f-body); color: var(--fg-soft);
  margin: 0 0 var(--s-4); }
.ob-choices { display: flex; flex-direction: column; gap: var(--s-2); }
.ob-choice { display: flex; align-items: center; gap: var(--s-3);
  padding: 12px 16px; background: var(--bg-paper);
  border: 1px solid var(--hair); border-radius: var(--r-sm);
  font: 500 14px/1 var(--f-body); color: var(--fg); cursor: pointer;
  text-align: left; }
.ob-choice:hover { border-color: var(--accent); color: var(--accent-text); }
.ob-choice-icon { width: 24px; height: 24px; border-radius: 50%;
  background: var(--bg-sunk); display: inline-flex;
  align-items: center; justify-content: center;
  font: 600 12px/1 var(--f-mono); color: var(--fg-soft); }
.ob-foot { display: flex; justify-content: space-between;
  margin-top: var(--s-4); }

10.9 Anatomy — pricing hero tier

The five parts of the 'Most popular' tier. The ink surface + pink ribbon combination is load-bearing — if you drop either, the hero tier stops reading as the recommended choice.

Five required parts

Most popular

Growth

When speed-to-lead pays for itself.

$249/mo
  • 2,000 conversations
  • All channels
  • HubSpot + Salesforce
Start trial
1
2
3
4
5
  1. 1
    Pink 'Most popular' ribbon
    Mono uppercase, rotated 35°, only on the hero tier. Signals best default without shouting.
  2. 2
    Mono tier name
    Uppercase, tracked, desaturated. Subordinates to the price below.
  3. 3
    Price: display 600
    Large tabular number with small $ prefix and mono /mo suffix. Pink /mo in the ink variant.
  4. 4
    Pink check feature list
    Only the check glyph is pink — the text stays readable. Hair-soft divider between rows.
  5. 5
    Pink pill CTA
    Inherits sh-pink. Lifts on hover. Ghost outline on the other tiers.