diff --git a/site/src/components/Header.astro b/site/src/components/Header.astro index 43c55428..1cf36e47 100644 --- a/site/src/components/Header.astro +++ b/site/src/components/Header.astro @@ -1,6 +1,12 @@ --- import Wordmark from './Wordmark.astro'; +interface Props { + /** Render as transparent over the sky hero, with light text. Becomes solid once scrolled past the hero. */ + onSky?: boolean; +} +const { onSky = false } = Astro.props; + const path = Astro.url.pathname; const nav: { label: string; href: string; match?: string }[] = [ { label: 'Product', href: '/product/', match: '/product' }, @@ -16,7 +22,14 @@ function active(match?: string) { return path === match || path.startsWith(match + '/'); } --- -
+ diff --git a/site/src/pages/index.astro b/site/src/pages/index.astro index 6f58a244..ec7ad019 100644 --- a/site/src/pages/index.astro +++ b/site/src/pages/index.astro @@ -1,109 +1,179 @@ --- import Layout from '../layouts/Layout.astro'; -import Section from '../components/Section.astro'; -import SectionHead from '../components/SectionHead.astro'; -import Card from '../components/Card.astro'; -import Button from '../components/Button.astro'; import Icon from '../components/Icon.astro'; -import Eyebrow from '../components/Eyebrow.astro'; -const features = [ - { icon: 'flame', title: 'Built-in warmup', desc: 'Reputation-safe warmup pools that spin up before you start, and keep running after you do.' }, - { icon: 'send', title: 'Distributed sending', desc: 'Sends across many mailboxes and many machines so no single IP carries the heat.' }, - { icon: 'inbox', title: 'Unified inbox', desc: 'Every reply, bounce, OOO, and click in one console with assignment and search.' }, - { icon: 'spark', title: 'Smart sequences', desc: 'Step delays, variants, conditions, and reply detection — without the dark magic.' }, - { icon: 'shield', title: 'Encrypted at rest', desc: 'Envelope encryption with AWS KMS. Per-user data keys. Worker boundary preserved.' }, - { icon: 'chart', title: 'Deliverability analytics', desc: 'Placement, complaints, bounces, and warmup health by mailbox, pool, and worker.' }, -]; +const logos = ['Loop', 'Northwind', 'Spruce', 'Heliogen', 'Foundry', 'Quanto', 'Cinder', 'Vellum', 'Atlas', 'Mosaic', 'Trellis', 'Helia']; -const logos = ['Loop', 'Northwind', 'Spruce', 'Heliogen', 'Foundry', 'Quanto', 'Cinder', 'Vellum']; +const ramp = [10, 11, 12, 14, 16, 18, 21, 24, 27, 30, 33, 36, 38, 40, 40]; +const placement = [88, 90, 92, 93, 91, 94, 95, 96, 96, 97, 98, 98, 99]; const stats = [ - { value: '99.97%', label: 'Worker uptime, last 90 days' }, - { value: '< 1ms', label: 'Tracking edge dedupe latency' }, - { value: '0.04%', label: 'Mean platform complaint rate' }, - { value: '12.4k', label: 'Mailboxes warming right now' }, + { value: '99.97%', label: 'Worker uptime · 90 days' }, + { value: '0.04%', label: 'Median complaint rate' }, + { value: '12.4k', label: 'Mailboxes warming now' }, + { value: '4.2 ms', label: 'Edge dedupe latency' }, +]; + +const compareCold = [ + ['Per-mailbox cold cap', 'Warmbly defaults to 50/day', 'Most tools: 200/day if you ask'], + ['Warmup pool quality', 'Vetted, monitored, quarantined', 'Shared with trial mailboxes'], + ['Reputation reaction', 'Acts on the watch band (10%)', 'Acts on catastrophe (80%)'], + ['Worker concentration', 'Capped by sum of mailbox budgets', 'Flat per-worker ceiling'], ]; --- - -
-
-
-
- - - Warmup & sending, one platform - -

- Cold email that actually lands in the inbox. -

-

- Warmbly warms your mailboxes, distributes sending across workers and IPs, and watches reputation in real time — so the cold campaign you run on Monday is still landing on Friday. -

-
- - - No credit card. 14-day pro trial. -
+ +
+ + -
    -
  • SPF, DKIM & DMARC checks
  • -
  • Free + premium warmup pools
  • -
  • Dedicated workers on Business
  • -
  • Native Gmail & Outlook
  • -
+
+ + - -
-
-
+ +

+ Cold email that + + actually lands + — in the inbox. + +

+ +

+ Warmbly warms your mailboxes, distributes sending across workers and IPs, and watches reputation in real time — so the cold campaign you run on Monday is still landing on Friday. +

+ + + +

+ Free forever for one mailbox · 14-day Growth trial · no credit card +

+ + +
+ +
+ +
- - - + + +
- app.warmbly.com / accounts +
+ + app.warmbly.com / accounts +
+
-
-
- -
-
-

- Trusted by founders, sales teams and recruiters at 4,000+ companies + +

+
+

+ Trusted by 4,000+ founders, sales teams and agencies

-
- {logos.map((l) => ( - {l} - ))} +
+
+ {[...logos, ...logos].map((l) => ( + {l} + ))} +
- -
- -
- {features.map((f) => ( - -
- -
-

{f.title}

-

{f.desc}

-
- ))} + +
+
+ + One platform + +

+ Warmup, sending and inbox — engineered together. +

+

+ Most tools bolt warmup onto an SMTP relay. We built sending, warmup, analytics and inbox as one system so the signal one part collects is the signal the next part acts on. +

-
- -
-
-
- {stats.map((s) => ( -
-
{s.value}
-
{s.label}
+
+ +
+
+
+
+ +
+

Reputation-safe warmup

+

+ Start at 10/day, ramp +1/day to a 40/day ceiling. Conversational replies. Quarantine on warning, not catastrophe. +

- ))} + + healthy + +
+ +
+
+ {ramp.map((v) => ( +
+ ))} +
+
+ day 1 · 10/day + day 30 · 40/day ceiling +
+
-
-
- -
- -
- {[ - { n: '01', title: 'Connect mailboxes', body: 'Native OAuth for Google and Microsoft. SMTP + IMAP for everything else.', icon: 'mail' }, - { n: '02', title: 'Warm up safely', body: 'Pool placement, gradual ramp, automatic backoff if reputation slips.', icon: 'flame' }, - { n: '03', title: 'Run campaigns', body: 'Sequences, variables, A/B variants and reply detection across all senders.', icon: 'send' }, - { n: '04', title: 'Watch placement', body: 'Per-mailbox placement, complaint and bounce signals. Quarantine before providers do.', icon: 'chart' }, - ].map((s) => ( - -
- {s.n} - -
-

{s.title}

-

{s.body}

-
- ))} -
-
- - -
-
-
- Why we're different -

- We refuse to be the reason your domain ends up on a blocklist. -

-

- Most cold-email platforms reward you for sending more. We reward you for sending well. Mailbox caps default to 50/day. Workers are capped by the sum of their mailbox budgets — not an arbitrary global number. Shared warmup pools quarantine misbehaving senders before providers do. + +

+
+ +
+

Distributed sending

+

+ One worker per machine. Each with its own IP. We add workers before forcing each to do more.

-
+
{[ - ['50 / day', 'Default per-mailbox cold cap'], - ['10 min', 'Default minimum gap between sends'], - ['10 / day', 'Warmup starting volume'], - ['+1 / day', 'Default warmup ramp'], - ].map(([k, v]) => ( -
-
{k}
-
{v}
+ ['eu-worker-3', 68], + ['us-worker-7', 54], + ['eu-worker-1', 41], + ['us-worker-2', 27], + ].map(([name, pct]) => ( +
+
+ {name}{pct}% +
+
+
+
))}
-
- -
-
-

Pool health

-

Premium warmup

+ +
+
+ +
+

Unified inbox

+

+ Every reply across every mailbox in one console — classified, assignable. +

+
+ {[ + { who: 'Jenna · Northwind', snippet: 'Sure — Thursday 10am ET', tag: 'positive', tone: 'text-emerald-700' }, + { who: 'Tom · Spruce', snippet: 'Add me to your blocklist', tag: 'unsub', tone: 'text-muted-foreground' }, + { who: 'Out of office', snippet: 'I’m away until Monday', tag: 'OOO', tone: 'text-amber-700' }, + ].map((r) => ( +
+
+ {r.who} + {r.tag} +
+

{r.snippet}

+
+ ))} +
+
+ + +
+
+ +
+

Placement, not opens

+

+ We probe inbox placement through the pool, then act on it. +

+
+
+ + + + + + + + + + +
+ 96% + inbox
- - - healthy -
-
+
+
+ + +
+
+
+
+ +
+

Sequences with conditions

+

+ Up to 12 steps. A/B variants on subject and body. Business-hour delays by recipient time zone. Branch on reply, open, click or bounce. +

+
+
+
+ {[ + { n: 'Step 1', t: 'Intro · plain text', m: '2 variants', tone: 'text-emerald-700' }, + { n: 'Step 2', t: 'Wait 3 biz days', m: 'if no reply', tone: 'text-muted-foreground' }, + { n: 'Step 3', t: 'Follow-up · context line', m: '4 variants', tone: 'text-emerald-700' }, + { n: 'Step 4', t: 'Soft break · 7 biz days', m: 'if no open', tone: 'text-muted-foreground' }, + ].map((s) => ( +
+
+ {s.n} + {s.m} +
+
{s.t}
+
+ ))} +
+
+ + +
+
+
+
+ +
+

Acts on the watch band, not the catastrophe band.

+

+ Most platforms wait until a mailbox lands in spam 80% of the time before quarantining. We trip thresholds weeks earlier — placement ≥ 10% spam, complaint ≥ 0.03%, bounce ≥ 1%. +

+
+
{[ - ['Complaint rate', '0.02%', 'success'], - ['Bounce rate', '0.7%', 'success'], - ['Spam placement', '3.1%', 'warn'], - ].map(([k, v, color]) => ( -
-
{k}
-
{v}
+ { name: 'Healthy', dot: 'bg-emerald-500' }, + { name: 'Watch', dot: 'bg-amber-500' }, + { name: 'Quarantined', dot: 'bg-orange-500' }, + { name: 'Blocked', dot: 'bg-rose-500' }, + ].map((b) => ( +
+
{b.name}
))}
-
-

Last 14 days

-
- {[18,24,32,28,40,36,44,52,48,58,56,62,68,72].map((h) => ( -
- ))} -
-
- -
-
-
- - -
-
- -

- “We had eight mailboxes burning out a week with our old tool. Switched to Warmbly, rebuilt the pool placement, dropped to one mailbox a quarter. The reply rate doubled because half our mail used to land in spam.” -

-
-
M
-
-
Maya Okonkwo
-
Head of GTM, Northwind
+
+ +
+ + + + + + + + + {[0, 40, 80, 120, 160].map((y) => ( + + ))} + + {(() => { + const pts = placement.map((v, i) => [Math.round((i / (placement.length - 1)) * 600), Math.round(160 - ((v - 85) / 14) * 140)]); + const linePath = pts.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p[0]} ${p[1]}`).join(' '); + const areaPath = `${linePath} L 600 160 L 0 160 Z`; + return ( + <> + + + {pts.map((p) => ( + + ))} + + ); + })()} + +
13 weeks ago
+
today · 99% inbox
-
+
- -
-
-

- Send cold email like you actually want it to work. + +
+
+
+ {stats.map((s, i) => ( +
+
{s.value}
+
{s.label}
+
+ ))} +
+
+
+ + +
+
+
+ Warmup +

+ A warmup pool that quarantines before mailbox providers do. +

+

+ The free and premium pools never mix. Spam-score tracking, verification tokens and recent-partner avoidance keep traffic natural. Anything past the watch band gets pulled. +

+
    + {[ + 'Start 10/day, ramp +1/day, ceiling 40/day', + '≥ 3 invalid tokens / 24h → auto-block', + 'Spam score > 50 → recovery pool', + 'Re-entry requires fresh probation', + ].map((l) => ( +
  • + + {l} +
  • + ))} +
+ + See how warmup works + +
+ +
+
+
+
+
Pool — premium
+
Last 14 days · 124 mailboxes
+
+
+ healthy +
+
+ +
+ {[ + { k: 'Complaint rate', v: '0.02%', tone: 'text-emerald-700', band: '< 0.03 warn' }, + { k: 'Bounce rate', v: '0.7%', tone: 'text-emerald-700', band: '< 1.0 warn' }, + { k: 'Spam placement', v: '3.1%', tone: 'text-amber-700', band: '< 10 watch' }, + ].map((m) => ( +
+
{m.k}
+
{m.v}
+
{m.band}
+
+ ))} +
+ +
+ {[18,24,32,28,40,36,44,52,48,58,56,62,68,72].map((h) => ( +
+ ))} +
+ +
+ warmup volume / day + 14d trailing +
+
+
+
+
+ + +
+
+
+
+
+
+ +
+
+ Search 12 mailboxes +
+ {[ + { who: 'Jenna · Northwind', sub: 'Re: quick question on outbound', time: '2m', tag: 'positive', active: true }, + { who: 'Aman · Spruce', sub: 'Re: intro from Maya', time: '14m', tag: 'positive', active: false }, + { who: 'OOO', sub: 'I’m away until Monday', time: '1h', tag: 'OOO', active: false }, + { who: 'Karim · Foundry', sub: 'Re: not the right time', time: '2h', tag: 'negative', active: false }, + { who: 'Lina · Cinder', sub: 'Re: pricing tier?', time: '3h', tag: 'positive', active: false }, + { who: 'unsubscribe', sub: 'Please remove me', time: '4h', tag: 'unsub', active: false }, + ].map((r) => ( +
+
+ {r.who} + {r.time} +
+
{r.sub}
+ {r.tag} +
+ ))} +
+ + +
+
+
+ +
+ Unified inbox +

+ One inbox for every mailbox you send from. +

+

+ No more bouncing between Gmail tabs to handle replies from eight cold mailboxes. Reply detection survives quoted text. OOO and unsubscribe auto-suppress. Assign by territory, snooze with reminders, search across every connected mailbox. +

+
    + {[ + 'Positive / negative / OOO / unsub / bounce classification', + 'Cross-mailbox search and saved views', + 'Round-robin or domain-aware assignment', + 'Realtime push for replies, not for opens', + ].map((l) => ( +
  • + + {l} +
  • + ))} +
+ + Explore the inbox + +
+
+
+
+ + +
+
+ Why we’re different +

+ We refuse to be the reason your domain ends up on a blocklist. +

+

+ Most cold-email platforms reward you for sending more. We reward you for sending well. Defaults that keep you safe even if you never touch them. +

+
+ +
+
+
Setting
+
Warmbly
+
Other cold-email tools
+
+ {compareCold.map(([k, ours, theirs]) => ( +
+
{k}
+
{ours}
+
{theirs}
+
+ ))} +
+
+ + +
+
+
+
+ +
+ “We had eight mailboxes burning out a week with our old tool. Switched to Warmbly, rebuilt pool placement, dropped to one mailbox a quarter. Reply rate doubled because half our mail used to land in spam.” +
+
+
+
+
Maya Okonkwo
+
Head of GTM · Northwind
+
+
+
+ +
+ {[ + ['2.4 → 5.1%', 'Reply rate'], + ['1 / quarter', 'Mailbox burnouts'], + ['98.4%', 'Inbox placement'], + ['6 hrs / wk', 'Saved per BDR'], + ].map(([v, k]) => ( +
+
{v}
+
{k}
+
+ ))} +
+
+
+
+ + +
+
+ Pricing +

+ Per mailbox. Predictable. No deliverability paywall. +

+

+ Warmup, sending, the inbox, analytics and CRM are not separate line items. You pay for the mailboxes you connect. +

+
+ +
+ {[ + { name: 'Free', price: '$0', per: 'forever', cta: 'Start free', href: 'https://app.warmbly.com/register?plan=free' }, + { name: 'Starter', price: '$39', per: 'mailbox / month', cta: 'Start trial', href: 'https://app.warmbly.com/register?plan=starter', highlight: false }, + { name: 'Growth', price: '$79', per: 'mailbox / month', cta: 'Start trial', href: 'https://app.warmbly.com/register?plan=growth', highlight: true }, + ].map((p) => ( +
+

{p.name}

+
+ {p.price} + {p.per} +
+ + {p.cta} + +
+ ))} +
+ + +
+ + +
+ +
+

+ Send cold email like you actually want it to land.

-

- Start free. Connect a mailbox. Warm it up. Send your first sequence in an afternoon. +

+ Connect a mailbox in a minute. Warm it up. Send your first sequence in an afternoon.

diff --git a/site/src/styles/global.css b/site/src/styles/global.css index 05ef5ca9..4279ee7b 100644 --- a/site/src/styles/global.css +++ b/site/src/styles/global.css @@ -197,6 +197,154 @@ body { z-index: 0; } +/* ---------------------------------------------------------------- + Auth-style sky — deep blue with cloud blurs, sun glow. + Same palette as web/src/app/auth/layout.tsx but flattened to + pure CSS (no SVG turbulence filters) so it's light to load. + The hero owns its own sky; the bottom of the hero masks to white + so the white page picks up below. +---------------------------------------------------------------- */ + +.hero-sky { + position: absolute; + inset: 0; + pointer-events: none; + background: + radial-gradient(ellipse 140% 140% at 72% 25%, + #7dd3fc 0%, + #38bdf8 18%, + #0ea5e9 36%, + #0284c7 58%, + #075985 82% + ); +} + +/* Soft sun glow in the upper-right */ +.hero-sun { + position: absolute; + top: 0; right: 0; + width: 900px; height: 900px; + pointer-events: none; + background: radial-gradient(circle, + rgba(253, 230, 138, 0.30) 0%, + rgba(253, 186, 116, 0.10) 32%, + rgba(56, 189, 248, 0.04) 55%, + transparent 70% + ); + filter: blur(8px); + transform: translate(20%, -20%); +} + +/* Atmospheric upper-left scatter */ +.hero-haze { + position: absolute; + inset: 0; + pointer-events: none; + background: + radial-gradient(ellipse 60% 50% at 20% 40%, + rgba(186, 230, 253, 0.18) 0%, + transparent 60% + ), + radial-gradient(ellipse 70% 30% at 60% 80%, + rgba(125, 211, 252, 0.20) 0%, + transparent 70% + ); +} + +/* Bottom of hero fades into pure white — no white wash on top */ +.hero-fade { + position: absolute; + inset: 0; + pointer-events: none; + background: linear-gradient(to bottom, + transparent 0%, + transparent 62%, + rgba(255,255,255,0.5) 82%, + #ffffff 100% + ); +} + +/* Cloud — three soft, blurred white blobs that read as a cumulus. + No SVG filters; just border-radius + blur on layered divs. */ +.cloud { + position: absolute; + pointer-events: none; +} +.cloud-back, .cloud-mid, .cloud-front { + position: absolute; + border-radius: 50% 50% 40% 40%; + filter: blur(22px); +} +.cloud-back { background: rgba(255, 255, 255, 0.95); } +.cloud-mid { background: rgba(232, 240, 254, 0.85); filter: blur(28px); } +.cloud-front { background: rgba(186, 207, 230, 0.70); filter: blur(34px); } + +/* Sky variants for the page header (transparent over sky) */ +.header-on-sky { + background: transparent !important; + border-color: transparent !important; + backdrop-filter: none !important; +} +.header-on-sky a, +.header-on-sky button, +.header-on-sky span, +.header-on-sky svg { color: white; } +.header-on-sky .nav-link { + color: rgba(255, 255, 255, 0.78); +} +.header-on-sky .nav-link:hover { + color: white; + background: rgba(255, 255, 255, 0.10); +} +.header-on-sky .nav-link.is-active { + color: white; + background: rgba(255, 255, 255, 0.14); +} +.header-on-sky .cta-primary { + background: white; + color: #0c4a6e; +} +.header-on-sky .cta-primary:hover { background: rgba(255, 255, 255, 0.9); } +.header-on-sky .cta-ghost { + color: rgba(255, 255, 255, 0.85); +} +.header-on-sky .cta-ghost:hover { + color: white; + background: rgba(255, 255, 255, 0.10); +} + +/* Hairline + soft inner-edge for product mock cards */ +.glass-card { + background: color-mix(in oklab, var(--card) 92%, transparent); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + box-shadow: + 0 1px 0 0 color-mix(in oklab, white 70%, transparent) inset, + 0 30px 80px -40px color-mix(in oklab, oklch(0.20 0.06 250) 30%, transparent), + 0 8px 24px -16px color-mix(in oklab, oklch(0.20 0.06 250) 20%, transparent); +} +.dark .glass-card { + box-shadow: + 0 1px 0 0 color-mix(in oklab, white 8%, transparent) inset, + 0 30px 80px -40px black, + 0 8px 24px -16px black; +} + +/* Subtle marquee for logo strip */ +@keyframes marquee-x { + from { transform: translateX(0); } + to { transform: translateX(-50%); } +} +.marquee-track { + animation: marquee-x 38s linear infinite; +} + +/* Mask the edges of a horizontal marquee to fade in/out */ +.marquee-mask { + mask-image: linear-gradient(to right, transparent 0%, black 8%, black 92%, transparent 100%); + -webkit-mask-image: linear-gradient(to right, transparent 0%, black 8%, black 92%, transparent 100%); +} + /* Inline code style for the docs/learn pages. */ .kbd { font-family: var(--font-mono);