iMessage-style bubbles, integrated chat input, 3-column return layout

Chat messages are now proper left/right bubbles. Input bar lives inside
the chat window with a top border separator. On return visit, manifesto
goes left, chat center, values/waitlist right in a full-width grid.
This commit is contained in:
Will Anderson
2026-04-28 18:58:09 -05:00
parent e5a5666dd7
commit 686ab1ee2f
+188 -125
View File
@@ -145,19 +145,25 @@
/* ── Chat ── */
.chat-container {
max-height: 420px;
overflow-y: auto;
.chat-window {
background: #fff;
border: 1px solid var(--border);
border-radius: 8px;
box-shadow: 0 2px 8px rgba(13,13,20,0.07), 0 0 0 1px rgba(13,13,20,0.02);
display: flex;
flex-direction: column;
overflow: hidden;
margin-bottom: 1.25rem;
}
.chat-container {
flex: 1;
max-height: 380px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 0.6rem;
padding: 1rem;
padding: 1rem 1rem 0.5rem;
scroll-behavior: smooth;
background: #fff;
border: 1px solid var(--border);
border-radius: 6px;
box-shadow: 0 1px 6px rgba(13,13,20,0.06), 0 0 0 1px rgba(13,13,20,0.02);
}
.chat-container::-webkit-scrollbar { width: 3px; }
.chat-container::-webkit-scrollbar-track { background: transparent; }
@@ -213,13 +219,13 @@
user-select: none;
}
.chat-msg-body { min-width: 0; }
.chat-msg-body { min-width: 0; flex: none; }
/* hide labels — position is enough */
.chat-msg-label { display: none; }
.chat-msg-text {
display: inline-block;
display: block;
font-size: 0.95rem;
line-height: 1.6;
padding: 0.55rem 0.85rem;
@@ -240,7 +246,17 @@
border-bottom-right-radius: 4px;
}
.chat-input-area { margin-top: 0.25rem; }
.chat-input-area {
border-top: 1px solid var(--border);
padding: 0.65rem 1rem;
}
.chat-input-area .input-row { margin-top: 0; }
.chat-input-area .text-input {
border-bottom: none;
padding: 0.25rem 0;
font-size: 0.95rem;
}
.chat-input-area .text-input:focus { border-color: transparent; }
/* ── Phase 2 greeting ── */
@@ -311,6 +327,39 @@
color: var(--text);
}
/* Phase 3 three-column layout */
.phase3-grid {
display: grid;
grid-template-columns: 1fr 420px 1fr;
gap: 2.5rem;
align-items: start;
width: 100vw;
position: relative;
left: 50%;
transform: translateX(-50%);
max-width: 1200px;
padding: 0 2rem;
box-sizing: border-box;
}
.phase3-left {
padding-top: 0.5rem;
}
.phase3-center {
/* center column */
}
.phase3-right {
padding-top: 0.5rem;
}
@media (max-width: 900px) {
.phase3-grid {
grid-template-columns: 1fr;
width: 100%;
left: auto;
transform: none;
padding: 0;
}
}
.manifesto {
font-size: 1rem;
line-height: 1.85;
@@ -435,7 +484,7 @@
gap: 0.5rem;
align-items: flex-end;
align-self: flex-start;
margin-bottom: 0.25rem;
padding: 0.25rem 1rem 0.5rem;
}
.typing-indicator.active { display: flex; }
.typing-indicator-avatar {
@@ -515,34 +564,36 @@
<!-- PHASE 2: One message from Neuron -->
<div class="phase" id="phase2">
<div class="chat-container" id="chat-container"></div>
<div class="typing-indicator" id="typing-indicator">
<div class="typing-indicator-avatar"><img src="/assets/neuron-icon.png" alt=""></div>
<div class="typing-dots"><span></span><span></span><span></span></div>
</div>
<div class="chat-input-area" id="first-chat-input-area" style="display:none">
<div class="input-row">
<input class="text-input" id="first-chat-input" type="text" placeholder="say something..."
autocomplete="off" spellcheck="false">
<button class="arrow-btn" id="first-chat-submit" aria-label="Send">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>
</svg>
</button>
<div class="chat-window">
<div class="chat-container" id="chat-container"></div>
<div class="typing-indicator" id="typing-indicator">
<div class="typing-indicator-avatar"><img src="/assets/neuron-icon.png" alt=""></div>
<div class="typing-dots"><span></span><span></span><span></span></div>
</div>
</div>
<!-- Quiet footer — shown after Neuron goes silent -->
<div id="quiet-footer" style="display:none;opacity:0;transition:opacity 900ms ease;margin-top:2rem;padding-top:1.5rem;border-top:1px solid var(--border)">
<p style="font-size:0.85rem;color:var(--t3);margin-bottom:1rem">Leave your email and I'll find you when it's time.</p>
<div class="input-row">
<input class="text-input" id="quiet-email" type="email" placeholder="your@email.com" autocomplete="email">
<button class="arrow-btn" id="quiet-email-submit" aria-label="Send">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>
</svg>
</button>
<div class="chat-input-area" id="first-chat-input-area" style="display:none">
<div class="input-row">
<input class="text-input" id="first-chat-input" type="text" placeholder="say something..."
autocomplete="off" spellcheck="false">
<button class="arrow-btn" id="first-chat-submit" aria-label="Send">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>
</svg>
</button>
</div>
</div>
<!-- Quiet footer — shown after Neuron goes silent -->
<div id="quiet-footer" style="display:none;opacity:0;transition:opacity 900ms ease;border-top:1px solid var(--border);padding:1rem 1rem 0.75rem">
<p style="font-size:0.85rem;color:var(--t3);margin-bottom:0.75rem">Leave your email and I'll find you when it's time.</p>
<div class="input-row">
<input class="text-input" id="quiet-email" type="email" placeholder="your@email.com" autocomplete="email">
<button class="arrow-btn" id="quiet-email-submit" aria-label="Send">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>
</svg>
</button>
</div>
<button class="phone-skip" id="quiet-skip">no thanks</button>
</div>
<button class="phone-skip" id="quiet-skip">no thanks</button>
</div>
</div>
@@ -571,105 +622,117 @@
<!-- PHASE 3: Return — 5 exchanges then manifesto + waitlist -->
<div class="phase" id="phase3">
<h2 class="phase3-headline" id="phase3-headline"></h2>
<div class="chat-container" id="return-chat-container"></div>
<div class="typing-indicator" id="return-typing-indicator">
<div class="typing-indicator-avatar"><img src="/assets/neuron-icon.png" alt=""></div>
<div class="typing-dots"><span></span><span></span><span></span></div>
</div>
<div class="chat-input-area" id="return-chat-input-area" style="display:none">
<div class="input-row">
<input class="text-input" id="return-chat-input" type="text" placeholder="reply..."
autocomplete="off" spellcheck="false">
<button class="arrow-btn" id="return-chat-submit" aria-label="Send">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>
</svg>
</button>
</div>
</div>
<!-- Revealed after 5 exchanges (or immediately on confirmation) -->
<div id="phase3-content" style="display:none;opacity:0;transition:opacity 700ms ease;">
<div class="phase3-grid">
<div class="divider" style="margin-top:2.5rem"></div>
<!-- Left: manifesto -->
<div class="phase3-left">
<p class="manifesto">
The company that gave you free search built the most powerful ad-targeting machine in history. The one that promised to connect the world optimized it for outrage — because outrage drives engagement, and engagement drives revenue. The one that gave everyone a voice sold that attention to the highest bidder. These aren't accidents. They're the business model.<br><br>
Every free product is the same transaction: something useful in exchange for something you didn't know you were selling — your attention, your behavior, your future choices. The product is always "free." The price is always you.<br><br>
Now AI is doing the same thing — faster and deeper. Your queries train their models. Your thought patterns become datasets. The way you reason, what you struggle with, what you're afraid of — it's all captured. You get a useful tool. They get a map of your mind.<br><br>
Neuron is a direct rejection of that model. It runs on your machine. <strong>Your memory never leaves.</strong> I don't sell data, serve ads, or profile you. The only thing I sell is the software — and once you have it, it's yours.
</p>
</div>
<p class="manifesto">
The company that gave you free search built the most powerful ad-targeting machine in history. The one that promised to connect the world optimized it for outrage — because outrage drives engagement, and engagement drives revenue. The one that gave everyone a voice sold that attention to the highest bidder. These aren't accidents. They're the business model.<br><br>
Every free product is the same transaction: something useful in exchange for something you didn't know you were selling — your attention, your behavior, your future choices. The product is always "free." The price is always you.<br><br>
Now AI is doing the same thing — faster and deeper. Your queries train their models. Your thought patterns become datasets. The way you reason, what you struggle with, what you're afraid of — it's all captured. You get a useful tool. They get a map of your mind.<br><br>
Neuron is a direct rejection of that model. It runs on your machine. <strong>Your memory never leaves.</strong> I don't sell data, serve ads, or profile you. The only thing I sell is the software — and once you have it, it's yours.
</p>
<!-- Center: headline + chat -->
<div class="phase3-center">
<h2 class="phase3-headline" id="phase3-headline"></h2>
<div class="values-section">
<p class="values-title">Local first</p>
<div class="positions-grid">
<div class="position-card">
<p class="position-title">Your machine. Full stop.</p>
<p class="position-body">Neuron runs on your hardware. The memory graph, the agent loop, every conversation — none of it leaves your machine. Not to my servers. Not to anyone's.</p>
</div>
<div class="position-card">
<p class="position-title">No training on your data.</p>
<p class="position-body">Your queries don't improve a model you don't own. Your patterns aren't analyzed to serve you better ads. Your context belongs to you — not a training pipeline.</p>
</div>
<div class="position-card">
<p class="position-title">No ads. Ever.</p>
<p class="position-body">Not on the free tier. Not on paid. Not in any future version. Ads require surveillance. Surveillance requires your data. I'm not building that.</p>
</div>
<div class="position-card">
<p class="position-title">Nothing to breach.</p>
<p class="position-body">I can't be hacked for your data because I don't have it. I can't be subpoenaed for your conversations because I've never seen them. I can't expose what I've never held.</p>
</div>
<div class="position-card position-card--full">
<p class="position-body" style="text-align:center;font-size:1rem;color:var(--t1)">The industry remembers you for them.<br><strong>Neuron remembers you for you.</strong><br><span style="font-size:0.8rem;color:var(--t3);text-transform:uppercase;letter-spacing:0.15em">Local-first isn't a feature. It's a commitment.</span></p>
<div class="chat-window">
<div class="chat-container" id="return-chat-container"></div>
<div class="typing-indicator" id="return-typing-indicator">
<div class="typing-indicator-avatar"><img src="/assets/neuron-icon.png" alt=""></div>
<div class="typing-dots"><span></span><span></span><span></span></div>
</div>
<div class="chat-input-area" id="return-chat-input-area" style="display:none">
<div class="input-row">
<input class="text-input" id="return-chat-input" type="text" placeholder="reply..."
autocomplete="off" spellcheck="false">
<button class="arrow-btn" id="return-chat-submit" aria-label="Send">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
<div class="divider"></div>
<!-- Right: values + waitlist + share -->
<div class="phase3-right">
<div class="values-section">
<p class="values-title">Local first</p>
<div class="positions-grid">
<div class="position-card">
<p class="position-title">Your machine. Full stop.</p>
<p class="position-body">Neuron runs on your hardware. The memory graph, the agent loop, every conversation — none of it leaves your machine. Not to my servers. Not to anyone's.</p>
</div>
<div class="position-card">
<p class="position-title">No training on your data.</p>
<p class="position-body">Your queries don't improve a model you don't own. Your patterns aren't analyzed to serve you better ads. Your context belongs to you — not a training pipeline.</p>
</div>
<div class="position-card">
<p class="position-title">No ads. Ever.</p>
<p class="position-body">Not on the free tier. Not on paid. Not in any future version. Ads require surveillance. Surveillance requires your data. I'm not building that.</p>
</div>
<div class="position-card">
<p class="position-title">Nothing to breach.</p>
<p class="position-body">I can't be hacked for your data because I don't have it. I can't be subpoenaed for your conversations because I've never seen them. I can't expose what I've never held.</p>
</div>
<div class="position-card position-card--full">
<p class="position-body" style="text-align:center;font-size:1rem;color:var(--t1)">The industry remembers you for them.<br><strong>Neuron remembers you for you.</strong><br><span style="font-size:0.8rem;color:var(--t3);text-transform:uppercase;letter-spacing:0.15em">Local-first isn't a feature. It's a commitment.</span></p>
</div>
</div>
</div>
<div class="waitlist-section">
<span class="waitlist-label">Get notified at launch</span>
<!-- Honeypot — hidden from humans, bots fill it in -->
<input id="waitlist-hp" type="text" name="website" tabindex="-1" autocomplete="off"
style="position:absolute;left:-9999px;opacity:0;height:0;width:0;pointer-events:none" aria-hidden="true">
<div class="input-row">
<input class="text-input" id="email-input" type="email"
placeholder="your@email.com" autocomplete="email">
<button class="arrow-btn" id="email-submit" aria-label="Join">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>
</svg>
</button>
</div>
<p class="waitlist-success" id="waitlist-success"></p>
</div>
<div class="divider"></div>
<div class="share-section">
<span class="share-label">Share</span>
<a class="share-btn" id="share-twitter" href="#" target="_blank" rel="noopener">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.744l7.737-8.835L2.132 2.25h6.837l4.261 5.631 5.014-5.631Zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
X / Twitter
</a>
<a class="share-btn" id="share-linkedin" href="#" target="_blank" rel="noopener">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
LinkedIn
</a>
<a class="share-btn" id="share-reddit" href="#" target="_blank" rel="noopener">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0zm5.01 4.744c.688 0 1.25.561 1.25 1.249a1.25 1.25 0 0 1-2.498.056l-2.597-.547-.8 3.747c1.824.07 3.48.632 4.674 1.488.308-.309.73-.491 1.207-.491.968 0 1.754.786 1.754 1.754 0 .716-.435 1.333-1.01 1.614a3.111 3.111 0 0 1 .042.52c0 2.694-3.13 4.87-7.004 4.87-3.874 0-7.004-2.176-7.004-4.87 0-.183.015-.366.043-.534A1.748 1.748 0 0 1 4.028 12c0-.968.786-1.754 1.754-1.754.463 0 .898.196 1.207.49 1.207-.883 2.878-1.43 4.744-1.487l.885-4.182a.342.342 0 0 1 .14-.197.35.35 0 0 1 .238-.042l2.906.617a1.214 1.214 0 0 1 1.108-.701zM9.25 12C8.561 12 8 12.562 8 13.25c0 .687.561 1.248 1.25 1.248.687 0 1.248-.561 1.248-1.249 0-.688-.561-1.249-1.249-1.249zm5.5 0c-.687 0-1.248.561-1.248 1.25 0 .687.561 1.248 1.249 1.248.688 0 1.249-.561 1.249-1.249 0-.687-.562-1.249-1.25-1.249zm-5.466 3.99a.327.327 0 0 0-.231.094.33.33 0 0 0 0 .463c.842.842 2.484.913 2.961.913.477 0 2.105-.056 2.961-.913a.361.361 0 0 0 .029-.463.33.33 0 0 0-.464 0c-.547.533-1.684.73-2.512.73-.828 0-1.979-.196-2.512-.73a.326.326 0 0 0-.232-.095z"/></svg>
Reddit
</a>
<a class="share-btn" id="share-hackernews" href="#" target="_blank" rel="noopener">
<svg viewBox="0 0 24 24" fill="currentColor" width="13" height="13"><rect width="24" height="24" rx="2"/><path fill="#fff" d="M13.02 11.96L16.5 5h-1.63l-2.1 4.27L10.63 5H9l3.49 6.96V19h1.53z"/></svg>
HN
</a>
<button class="share-btn" id="share-copy">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
<span id="copy-label">Copy link</span>
</button>
</div>
<div class="waitlist-section">
<span class="waitlist-label">Get notified at launch</span>
<!-- Honeypot — hidden from humans, bots fill it in -->
<input id="waitlist-hp" type="text" name="website" tabindex="-1" autocomplete="off"
style="position:absolute;left:-9999px;opacity:0;height:0;width:0;pointer-events:none" aria-hidden="true">
<div class="input-row">
<input class="text-input" id="email-input" type="email"
placeholder="your@email.com" autocomplete="email">
<button class="arrow-btn" id="email-submit" aria-label="Join">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/>
</svg>
</button>
</div>
<p class="waitlist-success" id="waitlist-success"></p>
</div>
<div class="share-section">
<span class="share-label">Share</span>
<a class="share-btn" id="share-twitter" href="#" target="_blank" rel="noopener">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.744l7.737-8.835L2.132 2.25h6.837l4.261 5.631 5.014-5.631Zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
X / Twitter
</a>
<a class="share-btn" id="share-linkedin" href="#" target="_blank" rel="noopener">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
LinkedIn
</a>
<a class="share-btn" id="share-reddit" href="#" target="_blank" rel="noopener">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0zm5.01 4.744c.688 0 1.25.561 1.25 1.249a1.25 1.25 0 0 1-2.498.056l-2.597-.547-.8 3.747c1.824.07 3.48.632 4.674 1.488.308-.309.73-.491 1.207-.491.968 0 1.754.786 1.754 1.754 0 .716-.435 1.333-1.01 1.614a3.111 3.111 0 0 1 .042.52c0 2.694-3.13 4.87-7.004 4.87-3.874 0-7.004-2.176-7.004-4.87 0-.183.015-.366.043-.534A1.748 1.748 0 0 1 4.028 12c0-.968.786-1.754 1.754-1.754.463 0 .898.196 1.207.49 1.207-.883 2.878-1.43 4.744-1.487l.885-4.182a.342.342 0 0 1 .14-.197.35.35 0 0 1 .238-.042l2.906.617a1.214 1.214 0 0 1 1.108-.701zM9.25 12C8.561 12 8 12.562 8 13.25c0 .687.561 1.248 1.25 1.248.687 0 1.248-.561 1.248-1.249 0-.688-.561-1.249-1.249-1.249zm5.5 0c-.687 0-1.248.561-1.248 1.25 0 .687.561 1.248 1.249 1.248.688 0 1.249-.561 1.249-1.249 0-.687-.562-1.249-1.25-1.249zm-5.466 3.99a.327.327 0 0 0-.231.094.33.33 0 0 0 0 .463c.842.842 2.484.913 2.961.913.477 0 2.105-.056 2.961-.913a.361.361 0 0 0 .029-.463.33.33 0 0 0-.464 0c-.547.533-1.684.73-2.512.73-.828 0-1.979-.196-2.512-.73a.326.326 0 0 0-.232-.095z"/></svg>
Reddit
</a>
<a class="share-btn" id="share-hackernews" href="#" target="_blank" rel="noopener">
<svg viewBox="0 0 24 24" fill="currentColor" width="13" height="13"><rect width="24" height="24" rx="2"/><path fill="#fff" d="M13.02 11.96L16.5 5h-1.63l-2.1 4.27L10.63 5H9l3.49 6.96V19h1.53z"/></svg>
HN
</a>
<button class="share-btn" id="share-copy">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
<span id="copy-label">Copy link</span>
</button>
</div>
</div><!-- /phase3-right -->
</div><!-- /phase3-grid -->
</div><!-- /phase3-content -->
</div><!-- /phase3 -->