From 561ed7b8740f9ccfc660ca2b0fe08cb1f6ee74cc Mon Sep 17 00:00:00 2001 From: Matthew Meszaros Date: Tue, 26 May 2026 13:59:39 +0000 Subject: [PATCH] site(header): cooler mega-menu + trim to 6 meaningful products MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Removed /product, /security, /workers (Footer, 404, privacy, dpa, trust references updated). - Mega-menu now 620px wide with a sky-tinted header strip showing 'The platform · 6 systems', a 2-col grid of products where each item has a 40x40 icon tile that goes from quiet surface ring to a sky gradient on hover (with a sky drop-shadow), current page is shown with the gradient tile active + a sky dot next to the name. Footer band kept (GitHub link + pricing CTA). --- site/src/components/Footer.astro | 2 - site/src/components/Header.astro | 125 ++++++--------- site/src/pages/404.astro | 2 +- site/src/pages/dpa.astro | 2 +- site/src/pages/privacy.astro | 2 +- site/src/pages/product.astro | 254 ------------------------------- site/src/pages/security.astro | 200 ------------------------ site/src/pages/trust.astro | 4 +- site/src/pages/workers.astro | 142 ----------------- 9 files changed, 51 insertions(+), 682 deletions(-) delete mode 100644 site/src/pages/product.astro delete mode 100644 site/src/pages/security.astro delete mode 100644 site/src/pages/workers.astro diff --git a/site/src/components/Footer.astro b/site/src/components/Footer.astro index 19515cd6..fb5f3971 100644 --- a/site/src/components/Footer.astro +++ b/site/src/components/Footer.astro @@ -5,7 +5,6 @@ const groups: { title: string; links: { label: string; href: string }[] }[] = [ { title: 'Product', links: [ - { label: 'Overview', href: '/product/' }, { label: 'Warmup', href: '/warmup/' }, { label: 'Sending engine', href: '/sending/' }, { label: 'Unified inbox', href: '/inbox/' }, @@ -47,7 +46,6 @@ const groups: { title: string; links: { label: string; href: string }[] }[] = [ { label: 'About', href: '/about/' }, { label: 'Contact', href: '/contact/' }, { label: 'Brand', href: '/brand/' }, - { label: 'Security', href: '/security/' }, { label: 'Trust', href: '/trust/' }, ], }, diff --git a/site/src/components/Header.astro b/site/src/components/Header.astro index 6a21ce77..b086dbe8 100644 --- a/site/src/components/Header.astro +++ b/site/src/components/Header.astro @@ -10,30 +10,20 @@ const { onSky = false } = Astro.props; const path = Astro.url.pathname; -// Product mega-menu items grouped into two columns -const productGroups = [ - { - label: 'Send & deliver', - items: [ - { icon: 'flame', href: '/warmup/', title: 'Warmup', desc: 'Reputation-safe pools and gradual ramps.' }, - { icon: 'send', href: '/sending/', title: 'Sending engine', desc: 'Distributed across mailboxes, workers, IPs.' }, - { icon: 'cpu', href: '/workers/', title: 'Workers', desc: 'One worker per machine, never NAT-shared.' }, - { icon: 'shield', href: '/security/', title: 'Security', desc: 'Envelope encryption with AWS KMS.' }, - ], - }, - { - label: 'Engage & analyse', - items: [ - { icon: 'inbox', href: '/inbox/', title: 'Unified inbox', desc: 'Every reply across every mailbox, classified.' }, - { icon: 'workflow', href: '/campaigns/', title: 'Campaigns', desc: 'Sequences with branching and A/B variants.' }, - { icon: 'contact', href: '/crm/', title: 'CRM', desc: 'Pipelines, deals and tasks next to sending.' }, - { icon: 'chart', href: '/analytics/', title: 'Analytics', desc: 'Placement, complaints, bounces, by mailbox.' }, - ], - }, +// Product mega-menu items +const products = [ + { icon: 'flame', href: '/warmup/', title: 'Warmup', desc: 'Reputation-safe pools, gradual ramps.' }, + { icon: 'send', href: '/sending/', title: 'Sending engine', desc: 'Distributed across mailboxes and IPs.' }, + { icon: 'inbox', href: '/inbox/', title: 'Unified inbox', desc: 'Every reply, classified on arrival.' }, + { icon: 'workflow', href: '/campaigns/', title: 'Campaigns', desc: 'Sequences with branching and variants.' }, + { icon: 'chart', href: '/analytics/', title: 'Analytics', desc: 'Placement, complaints, bounces per mailbox.' }, + { icon: 'contact', href: '/crm/', title: 'CRM', desc: 'Pipelines, deals and tasks next to sending.' }, + { icon: 'cpu', href: '/workers/', title: 'Workers', desc: 'One worker per machine, isolated IPs.' }, + { icon: 'shield', href: '/security/', title: 'Security', desc: 'KMS envelope encryption, audit log.' }, ]; const nav: { label: string; href: string; match?: string; mega?: boolean }[] = [ - { label: 'Product', href: '/product/', match: '/product', mega: true }, + { label: 'Product', href: '/warmup/', match: '/warmup|/sending|/inbox|/campaigns|/analytics|/crm', mega: true }, { label: 'Deliverability',href: '/deliverability/',match: '/deliverability' }, { label: 'Pricing', href: '/pricing/', match: '/pricing' }, { label: 'Learn', href: '/learn/', match: '/learn' }, @@ -41,7 +31,9 @@ const nav: { label: string; href: string; match?: string; mega?: boolean }[] = [ function active(match?: string) { if (!match) return false; - return path === match || path.startsWith(match + '/'); + // Support pipe-delimited multi-match for the Product mega item + const parts = match.split('|'); + return parts.some((m) => path === m || path.startsWith(m + '/')); } ---
-
-
- - -
- - -
-
- -
-
-
Product overview
-
Nine systems, one platform.
-
-
-
- See all -
-
- -
-
Resources
- +
+
+ +
+
+
+
The platform
+
{products.length} systems
- -
- {productGroups.map((g) => ( -
-
- {g.label} - -
-
- {g.items.map((it) => ( - -
- -
-
-
{it.title}
-
{it.desc}
-
- -
- ))} -
-
- ))} + +
+ {products.map((it) => { + const isCurrent = path === it.href || path.startsWith(it.href); + return ( + +
+ +
+
+
+ {it.title} + {isCurrent && } +
+
{it.desc}
+
+ +
+ ); + })}
- -
- + + diff --git a/site/src/pages/404.astro b/site/src/pages/404.astro index 4fab65bd..289a6013 100644 --- a/site/src/pages/404.astro +++ b/site/src/pages/404.astro @@ -36,7 +36,7 @@ import Icon from '../components/Icon.astro';
{[ - ['Product', '/product/'], + ['Warmup', '/warmup/'], ['Pricing', '/pricing/'], ['Changelog', '/changelog/'], ['Contact', '/contact/'], diff --git a/site/src/pages/dpa.astro b/site/src/pages/dpa.astro index fe1663ac..f28601ac 100644 --- a/site/src/pages/dpa.astro +++ b/site/src/pages/dpa.astro @@ -41,7 +41,7 @@ import Legal from '../layouts/Legal.astro';

6. Security

- Warmbly implements appropriate technical and organisational measures, including envelope encryption with AWS KMS, AES-256-GCM at rest, TLS in transit, role-based access control, audit logging, and the security controls described at /security. + Warmbly implements appropriate technical and organisational measures, including envelope encryption with AWS KMS, AES-256-GCM at rest, TLS in transit, role-based access control, audit logging, and the security controls described at /security.

7. Sub-processors

diff --git a/site/src/pages/privacy.astro b/site/src/pages/privacy.astro index e73cf045..9d993599 100644 --- a/site/src/pages/privacy.astro +++ b/site/src/pages/privacy.astro @@ -87,7 +87,7 @@ import Legal from '../layouts/Legal.astro';

8. Security

- We protect customer data with envelope encryption (AES-256-GCM with KMS-derived data keys), TLS in transit, strict access controls, audit logging, and ongoing security review. Details at /security. + We protect customer data with envelope encryption (AES-256-GCM with KMS-derived data keys), TLS in transit, strict access controls, audit logging, and ongoing security review. Details at /security.

9. Children

diff --git a/site/src/pages/product.astro b/site/src/pages/product.astro deleted file mode 100644 index 1a757e72..00000000 --- a/site/src/pages/product.astro +++ /dev/null @@ -1,254 +0,0 @@ ---- -import Layout from '../layouts/Layout.astro'; -import Cloud from '../components/Cloud.astro'; -import Icon from '../components/Icon.astro'; -import CTA from '../components/CTA.astro'; - -const pillars = [ - { - n: '01', - icon: 'flame', - title: 'Warmup', - href: '/warmup/', - body: 'Reputation-safe pools, gradual ramps, automatic quarantine. Free and premium pools never mix.', - bullets: [ - 'Start at 10 / day · ramp +1 / day · ceiling 40', - 'Spam-score tracking + invalid token detection', - 'Auto-block at 3 invalid tokens / 24h or score > 50', - 'Recovery pool for cooldown senders', - ], - }, - { - n: '02', - icon: 'send', - title: 'Sending engine', - href: '/sending/', - body: 'Workers distribute campaign mail across machines. Per-mailbox caps, per-worker concentration limits.', - bullets: [ - 'Default 50 / day cold cap per mailbox', - 'Minimum 10-minute gap between sends', - 'Worker volume = sum of mailbox budgets', - 'Native Gmail, Outlook, SMTP / IMAP', - ], - }, - { - n: '03', - icon: 'inbox', - title: 'Unified inbox', - href: '/inbox/', - body: 'Every reply, OOO, bounce and click across every mailbox in one console. Assign, snooze, label, search.', - bullets: [ - 'Reply detection that survives quoted text', - 'OOO classification with auto-suppress', - 'Thread-level assignment to teammates', - 'Search across all connected mailboxes', - ], - }, - { - n: '04', - icon: 'workflow', - title: 'Sequences', - href: '/campaigns/', - body: 'Multi-step sequences with conditions, A/B variants and time-zone aware sending.', - bullets: [ - 'Step delays in business hours by recipient TZ', - 'A/B variants on subject and body', - 'If-replied / if-opened / if-clicked branching', - 'Plain-text first. HTML is opt-in.', - ], - }, - { - n: '05', - icon: 'chart', - title: 'Analytics', - href: '/analytics/', - body: 'Placement, complaint rate, bounce rate, warmup health by mailbox, pool and worker. Not just opens.', - bullets: [ - 'Inbox vs spam placement per mailbox', - "Complaint rate vs Google's 0.1% / 0.3% bands", - "Bounce rate vs SES's 5% / 10% bands", - 'Health: healthy → watch → quarantined', - ], - }, - { - n: '06', - icon: 'contact', - title: 'Contacts & CRM', - href: '/crm/', - body: 'Light CRM with pipelines, deals and tasks. Imports, dedup, categories, suppression-aware.', - bullets: [ - 'CSV / XLSX / JSON import with column mapping', - 'Dedup by email, domain or custom key', - 'Categories instead of tag soup', - 'Suppression list shared across campaigns', - ], - }, - { - n: '07', - icon: 'cpu', - title: 'Workers', - href: '/workers/', - body: 'Distributed execution plane. Shared free, shared premium, or dedicated to your org.', - bullets: [ - 'Kafka-driven command / result topics', - 'Per-worker IP identity, no shared NAT', - 'Live rebalance and migration', - 'Lightweight: no Postgres on workers', - ], - }, - { - n: '08', - icon: 'shield', - title: 'Security', - href: '/security/', - body: 'Envelope encryption with AWS KMS, per-user data keys, cached decryption, audit log.', - bullets: [ - 'AES-256-GCM via KMS-generated DEKs', - 'Encrypted DEK stored, plaintext cached briefly', - 'Workers never touch Postgres', - 'Admin actions audited', - ], - }, - { - n: '09', - icon: 'webhook', - title: 'API & webhooks', - href: '/developers/', - body: 'Programmatic control over mailboxes, campaigns, contacts. Signed webhooks for every event.', - bullets: [ - 'REST with idempotency keys', - 'HMAC-signed webhooks', - 'Per-key scopes and rotation', - 'Rate limits surfaced in headers', - ], - }, -]; - -const flow = [ - { label: 'Connect mailbox', sub: 'OAuth · SMTP · IMAP' }, - { label: 'Warm reputation', sub: 'Premium pool · gradual ramp' }, - { label: 'Send sequences', sub: 'Distributed across workers' }, - { label: 'Triage replies', sub: 'Unified inbox · classification' }, - { label: 'Score health', sub: 'Placement · complaints · quarantine' }, -]; - -const principles = [ - ['Mailbox first, not worker first', "A worker's safe send volume is the sum of its mailbox budgets. We don't reward forcing one inbox to do the work of ten."], - ['Quarantine before providers do', 'Shared pool thresholds are stricter than Google or Microsoft enforcement, so reputation never gets the chance to slip publicly.'], - ['Plain-text by default', "Cold mail is a person talking to a person. HTML is opt-in. We don't add tracking pixels or branded footers without permission."], - ['No black boxes', 'Every score, suppression and quarantine has a reason you can read. No "the algorithm decided."'], - ['Reputation is shared infrastructure', 'A bad sender in a shared pool taxes everyone. We act on warning bands, not catastrophic bands.'], - ['Workers are replaceable', 'Worker state is disposable. We migrate a mailbox between workers without you noticing.'], -]; ---- - - -
-
- -
-
- -
- -
-
Product
-

- Nine systems.
One platform. -

-

- Each piece feeds the next. Warmup gathers the signal sending throttles on. Inbox classifies the replies analytics scores on. Nothing exists in a vacuum. -

- - -
- {flow.map((f, i) => ( -
-
-
- {String(i + 1).padStart(2, '0')} -
- {i < flow.length - 1 && ( - - )} -
-
{f.label}
-
{f.sub}
-
- ))} -
-
-
- - -
-
-
The platform
-

- Built so each part makes the next one safer. -

- - -
-
- - -
-
-
-
-
Design principles
-

- Opinionated where it matters. Quiet everywhere else. -

-
-
- {principles.map(([title, body], i) => ( -
-
{String(i + 1).padStart(2, '0')}
-
-
{title}
-

{body}

-
-
- ))} -
-
-
-
- - -
diff --git a/site/src/pages/security.astro b/site/src/pages/security.astro deleted file mode 100644 index 1656c233..00000000 --- a/site/src/pages/security.astro +++ /dev/null @@ -1,200 +0,0 @@ ---- -import Layout from '../layouts/Layout.astro'; -import Cloud from '../components/Cloud.astro'; -import Icon from '../components/Icon.astro'; -import CTA from '../components/CTA.astro'; - -const layers = [ - ['Edge', 'Cloudflare · WAF · DDoS · Turnstile'], - ['Auth', 'OAuth · CAPTCHA · session pinning'], - ['App', 'Per-user rate limits · idempotency · webhook HMAC'], - ['Encryption', 'AWS KMS · per-user DEKs · AES-256-GCM · brief Redis cache'], - ['Storage', 'Postgres (control) · DynamoDB (DEKs) · S3 (payloads)'], - ['Execution', 'Worker plane · no SQL · per-machine IP · Kafka topics'], -]; - -const compliance = { - today: [ - 'GDPR-aligned data handling', - 'DPA signature on request · 48h SLA', - 'Subprocessor list published + versioned', - 'Per-org audit log of admin actions', - 'Configurable data residency (US / EU) on Business', - 'TLS 1.2+ for all in-transit traffic', - 'Per-user data encryption keys backed by AWS KMS', - ], - progress: [ - 'SOC 2 Type II audit', - 'ISO 27001 gap assessment', - 'HIPAA covered-entity BAA pilot', - 'Customer-managed KMS keys (BYOK)', - 'Tenant-scoped IP allowlists for the API', - 'Hardware-key SSO enforcement', - ], -}; - -const controls = [ - ['CAPTCHA at auth', 'Cloudflare Turnstile on login, registration, password reset and confirmation flows.'], - ['API rate limiting', 'Per-user category-aware rate limiting backed by Redis and plan limits.'], - ['Realtime throttling', 'WebSocket joins, messages and events rate-limited as an anti-abuse boundary.'], - ['Event dedupe', 'Tracking events deduped at edge and again in the consumer. Idempotency keys everywhere.'], - ['Suppression', 'Bounce, complaint and unsubscribe signals suppress recipients across all campaigns.'], - ['Mailbox-sync limits', '100 new emails / 5min burst, 500 / hour ceiling. Exceeded mailboxes are rate-limited.'], - ['Admin enforcement', 'Manual ban / unban / rate-limit override with full audit history.'], - ['Webhook signatures', 'HMAC-signed webhooks with rotation primitives in the dashboard.'], -]; ---- - - -
-
- -
- -
-
-
Security
-

- Encrypted at rest.
Boundary-respecting
by design. -

-

- Mailbox credentials, refresh tokens and message payloads are envelope-encrypted with per-user data keys. Workers never open a Postgres connection. -

-
- - -
- - - - - - - - - - - ROOT KEY - AWS KMS - - - - PER-USER DEK - AES-256-GCM - - - - ENCRYPTED · STORED - DynamoDB - - - - PAYLOAD - Sealed at rest - - - - SHORT TTL · CACHED - Redis - - - - - - - - - - - - -
-
-
- - -
-
-
Layered architecture
-

Six layers, each with its own boundary.

- -
- {layers.map(([l, b], i) => ( -
-
{String(i + 1).padStart(2, '0')}
-
{l}
-
{b}
-
- ))} -
-
-
- - -
-
-
Anti-abuse
-

Layered controls, not one brittle gate.

- -
- {controls.map(([k, v]) => ( -
-
{k}
-

{v}

-
- ))} -
-
-
- - -
-
-
Compliance
-

Where we are, honestly.

- -
-
-
- -
Today
-
-
    - {compliance.today.map((l) => ( -
  • - - {l} -
  • - ))} -
-
-
-
- -
In progress
-
-
    - {compliance.progress.map((l) => ( -
  • - - {l} -
  • - ))} -
-
-
-
-
- - -
diff --git a/site/src/pages/trust.astro b/site/src/pages/trust.astro index b34a7e3b..179b8402 100644 --- a/site/src/pages/trust.astro +++ b/site/src/pages/trust.astro @@ -113,8 +113,8 @@ const subs = [

Per-user data encryption keys. Encrypted DEK in DynamoDB, plaintext briefly cached. Workers never touch Postgres.

- - Read the full security posture + + Request the security pack
diff --git a/site/src/pages/workers.astro b/site/src/pages/workers.astro deleted file mode 100644 index 93461dcb..00000000 --- a/site/src/pages/workers.astro +++ /dev/null @@ -1,142 +0,0 @@ ---- -import Layout from '../layouts/Layout.astro'; -import Cloud from '../components/Cloud.astro'; -import Icon from '../components/Icon.astro'; -import CTA from '../components/CTA.astro'; - -const tiers = [ - { - tier: 'Shared free', - plan: 'Free trial', - body: 'Pooled across free senders. Lower per-mailbox caps and conservative ramps.', - bullets: ['Free warmup pool', 'Strict per-mailbox caps', 'Shared IPs'], - }, - { - tier: 'Shared premium', - plan: 'Starter · Grow', - body: 'Pooled across paid senders. Stricter quarantine policy than free.', - bullets: ['Premium warmup pool', 'Default 50 / day cap', 'Shared paid IPs'], - featured: true, - }, - { - tier: 'Dedicated', - plan: 'Business · Enterprise', - body: 'A worker process and IP allocated to your organisation. Still mailbox-first.', - bullets: ['Dedicated worker process', 'Dedicated IP(s) · warmed', 'Custom data residency'], - }, -]; - -const props = [ - { icon: 'cpu', title: 'Per-machine identity', body: 'One worker process per machine. Each has its own IP. No shared NAT.' }, - { icon: 'database', title: 'Disposable state', body: 'Workers store nothing locally that cannot be rebuilt from a Kafka replay.' }, - { icon: 'workflow', title: 'Kafka-driven', body: 'Commands and results flow through worker-specific topics for traceability.' }, - { icon: 'shield', title: 'No SQL on workers', body: 'Workers do not open Postgres. They talk to Kafka, KMS, DynamoDB, S3, Redis.' }, - { icon: 'send', title: 'Migrable mailboxes', body: 'A mailbox can be reassigned between workers transparently.' }, - { icon: 'layers', title: 'Concentration caps', body: 'A shared worker should not be a concentration point. Add more before each does more.' }, -]; ---- - - -
-
- -
- -
-
-
Workers
-

- Many workers.
Many IPs.
By design. -

-

- Cold mail concentrated through one big sender is asking for trouble. Warmbly fans out: one worker per machine, each with its own network identity. -

-
- - -
-
- {Array.from({ length: 20 }).map((_, i) => { - const isYours = [6, 7, 11, 12].includes(i); - const isPremium = [0, 1, 2, 3, 4, 5, 8, 9, 10, 13, 14, 15, 16, 17, 18, 19].includes(i); - return ( -
-
w-{String(i + 1).padStart(2, '0')}
-
- {Array.from({ length: 6 }).map((_, j) => ( - - ))} -
-
- ); - })} -
-
- Your dedicated - Shared premium -
-
-
-
- - -
-
-
Three tiers
-

- Three ways your mail moves. -

- -
- {tiers.map((t) => ( -
-
{t.plan}
-
{t.tier}
-

{t.body}

-
    - {t.bullets.map((b) => ( -
  • - - {b} -
  • - ))} -
-
- ))} -
-
-
- - -
-
-
Why workers, not one big sender
-

- We treat workers as cattle, not pets. -

- -
- {props.map((s) => ( -
-
- -
-
{s.title}
-

{s.body}

-
- ))} -
-
-
- - -