Skip to content

Commit 9ab0157

Browse files
committed
self host licences
1 parent a80f255 commit 9ab0157

23 files changed

Lines changed: 344 additions & 130 deletions

src/components/CheckIcon.astro

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
// A check mark for "included" lists. Pricing cards list only what a tier includes — nothing
3+
// greyed out, nothing struck through — so every row carries the same mark and the absence of
4+
// a row is the only "not included" there is.
5+
interface Props {
6+
class?: string;
7+
}
8+
const { class: className = "" } = Astro.props;
9+
---
10+
11+
<svg
12+
class:list={["h-4 w-4 shrink-0 mt-0.5 text-primary", className]}
13+
viewBox="0 0 20 20"
14+
fill="currentColor"
15+
aria-hidden="true"
16+
>
17+
<path
18+
fill-rule="evenodd"
19+
d="M16.704 5.29a1 1 0 0 1 0 1.42l-7.5 7.5a1 1 0 0 1-1.42 0l-3.5-3.5a1 1 0 1 1 1.42-1.42L8.5 12.09l6.79-6.8a1 1 0 0 1 1.414 0Z"
20+
clip-rule="evenodd"></path>
21+
</svg>

src/components/ComparisonCTA.astro

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ interface Props {
77
88
const {
99
title = "Ready to try Notifuse?",
10-
description = "Start sending beautiful emails with Notifuse Cloud. Or self-host.",
10+
description = "Start sending beautiful emails with Notifuse Cloud, or self-host it.",
1111
compact = false,
1212
} = Astro.props;
1313
---
@@ -27,12 +27,6 @@ const {
2727
>
2828
Try Live Demo
2929
</a>
30-
<a
31-
href="https://docs.notifuse.com/installation"
32-
class="border border-gray-200 text-gray-700 px-6 py-3 rounded-md text-base font-semibold hover:bg-white transition-colors text-center"
33-
>
34-
Self-Host
35-
</a>
3630
</div>
3731
) : (
3832
<div class="bg-gradient-to-br from-gray-50 to-orange-50 border border-gray-200 rounded-xl p-8 my-12">
@@ -52,12 +46,6 @@ const {
5246
>
5347
Try Live Demo
5448
</a>
55-
<a
56-
href="https://docs.notifuse.com/installation"
57-
class="border border-gray-200 bg-white text-gray-700 px-6 py-3 rounded-md text-base font-semibold hover:bg-gray-50 transition-colors"
58-
>
59-
Self-Host
60-
</a>
6149
</div>
6250
</div>
6351
</div>

src/components/Footer.astro

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ const t = isFr ? {
1414
tryEditor: "Essayer l'éditeur email",
1515
pricing: "Tarifs",
1616
pricingHref: "/fr/tarifs",
17+
licences: "Licences auto-hébergées",
18+
licencesHref: "/fr/tarifs/auto-heberge",
1719
compare: "Comparer les alternatives",
1820
compareHref: "/fr/compare",
1921
docs: "Documentation",
@@ -30,6 +32,8 @@ const t = isFr ? {
3032
tryEditor: "Try Email Editor Live",
3133
pricing: "Pricing",
3234
pricingHref: "/pricing",
35+
licences: "Self-hosted licences",
36+
licencesHref: "/pricing/self-hosted",
3337
compare: "Compare Alternatives",
3438
compareHref: "/compare",
3539
docs: "Documentation",
@@ -80,6 +84,13 @@ const t = isFr ? {
8084
>{t.pricing}</a
8185
>
8286
</li>
87+
<li>
88+
<a
89+
href={t.licencesHref}
90+
class="text-gray-600 hover:text-gray-900 transition-colors"
91+
>{t.licences}</a
92+
>
93+
</li>
8394
<li>
8495
<a
8596
href={t.compareHref}

src/components/LaunchBanner.astro

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
// The self-hosted licence launch offer. Short, one line, one code, one date.
3+
//
4+
// It disappears twice over: at build time once the date has passed, and in the browser for a
5+
// site built before the date and not rebuilt since — a stale promise of a discount is worse
6+
// than no banner. The date is the only thing to change when the offer is extended.
7+
interface Props {
8+
lang?: "en" | "fr";
9+
/** The link to the tiers is left out on the page that lists them. */
10+
showCta?: boolean;
11+
class?: string;
12+
}
13+
const { lang = "en", showCta = true, class: className = "" } = Astro.props;
14+
15+
export const LAUNCH_OFFER_UNTIL = "2026-10-05T23:59:59Z";
16+
const expired = Date.now() > Date.parse(LAUNCH_OFFER_UNTIL);
17+
18+
const copy = {
19+
en: {
20+
lead: "Self-hosted licences are live: 30% off with code",
21+
until: "until 5 October 2026.",
22+
cta: "See the tiers →",
23+
href: "/pricing/self-hosted",
24+
},
25+
fr: {
26+
lead: "Licences auto-hébergées : lancement à -30 % avec le code",
27+
until: "jusqu'au 5 octobre 2026.",
28+
cta: "Voir les paliers →",
29+
href: "/fr/tarifs/auto-heberge",
30+
},
31+
}[lang];
32+
---
33+
34+
{
35+
!expired && (
36+
<div
37+
data-launch-banner
38+
data-until={LAUNCH_OFFER_UNTIL}
39+
class:list={[
40+
"rounded-lg p-[1.5px] bg-gradient-to-r from-primary via-fuchsia-500 to-pink-500",
41+
className,
42+
]}
43+
>
44+
<div class="rounded-[6.5px] bg-white px-4 py-3 flex flex-wrap items-center justify-center gap-x-3 gap-y-1 text-sm text-gray-700 text-center">
45+
<span>
46+
{copy.lead}{" "}
47+
<code class="rounded bg-gray-100 px-1.5 py-0.5 font-mono text-[13px] font-semibold text-slate-900">LAUNCH30</code>, {copy.until}
48+
</span>
49+
{showCta && (
50+
<a href={copy.href} class="font-semibold text-primary hover:underline">
51+
{copy.cta}
52+
</a>
53+
)}
54+
</div>
55+
</div>
56+
)
57+
}
58+
59+
<script>
60+
// Hides the banner after the date on a site built before it and not rebuilt since.
61+
document.querySelectorAll<HTMLElement>("[data-launch-banner]").forEach((el) => {
62+
const until = el.dataset.until;
63+
if (until && Date.now() > Date.parse(until)) el.hidden = true;
64+
});
65+
</script>

src/data/licensing.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,8 @@ export const selfHostTiers = [
227227
workspaces: 5,
228228
capabilities: ["rbac", "ses_tenant", "template_i18n"],
229229
forWhom: {
230-
en: "One company, a few brands, a team that needs permission control.",
231-
fr: "Une entreprise, quelques marques, une équipe qui a besoin de gérer les permissions.",
230+
en: "For a company and its brands.",
231+
fr: "Pour une entreprise et ses marques.",
232232
},
233233
},
234234
{
@@ -239,8 +239,8 @@ export const selfHostTiers = [
239239
workspaces: 15,
240240
capabilities: ["rbac", "ses_tenant", "template_i18n"],
241241
forWhom: {
242-
en: "One workspace per client, each with its own isolated database.",
243-
fr: "Un workspace par client, chacun avec sa base isolée.",
242+
en: "For an agency and its clients.",
243+
fr: "Pour une agence et ses clients.",
244244
},
245245
},
246246
{
@@ -249,10 +249,10 @@ export const selfHostTiers = [
249249
name: "Enterprise",
250250
annualUsd: 1990,
251251
workspaces: 15,
252-
capabilities: ["rbac", "ses_tenant", "template_i18n", "sso"],
252+
capabilities: ["rbac", "ses_tenant", "template_i18n", "sso", "audit_logs"],
253253
forWhom: {
254-
en: "Everything above, plus single sign-on through your identity provider.",
255-
fr: "Tout ce qui précède, plus l'authentification unique via votre fournisseur d'identité.",
254+
en: "For SSO and compliance requirements.",
255+
fr: "Pour les exigences SSO et conformité.",
256256
},
257257
},
258258
] as const;
@@ -265,12 +265,28 @@ export const capabilityLabels = {
265265
ses_tenant: "Amazon SES tenant isolation",
266266
template_i18n: "Template translations",
267267
sso: "Single sign-on (OIDC)",
268+
audit_logs: "Audit logs",
268269
},
269270
fr: {
270271
workspaces: "Workspaces",
271272
rbac: "Permissions granulaires",
272273
ses_tenant: "Isolation de tenant Amazon SES",
273274
template_i18n: "Traductions de templates",
274275
sso: "Authentification unique (OIDC)",
276+
audit_logs: "Journaux d'audit",
275277
},
276278
} as const;
279+
280+
/**
281+
* Capabilities a tier will carry that no release ships yet.
282+
*
283+
* `audit_logs` is on the Enterprise card as a roadmap item, and the tag it renders under is
284+
* what keeps that honest. It is NOT in `licensedCapabilities` above and must not be: that
285+
* list is what the software refuses, and a capability that exists in no release cannot be one
286+
* you are refused. Enterprise keys already carry `audit_logs` (billing-api mints it from day
287+
* one, so nothing has to be re-issued when it ships), which is why it can be promised here
288+
* without promising a date.
289+
*/
290+
export const comingSoon: ReadonlySet<string> = new Set(["audit_logs"]);
291+
292+
export const comingSoonLabel = { en: "coming soon", fr: "bientôt" } as const;

src/pages/compare/notifuse-vs-brevo.astro

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ const proConsRows = [
6868
},
6969
{
7070
label: "Feature Access",
71-
notifuse: "All features included",
71+
notifuse: "All on Cloud; 5 need a key self-hosted",
7272
competitor: "Gated by tier ($18-$499+/mo)",
7373
},
7474
{
@@ -117,7 +117,7 @@ const proConsRows = [
117117
/>
118118
<meta
119119
property="og:description"
120-
content="Compare Notifuse (open-source, Cloud or self-hosted) and Brevo (SaaS, multi-channel). All features included vs tier-gated pricing up to $499/mo."
120+
content="Compare Notifuse (open-source, Cloud or self-hosted) and Brevo (SaaS, multi-channel). No per-contact pricing vs tier-gated pricing up to $499/mo."
121121
/>
122122
<meta property="og:image" content="https://notifuse.com/og-image.png" />
123123
<meta property="og:image:width" content="1200" />
@@ -132,7 +132,7 @@ const proConsRows = [
132132
/>
133133
<meta
134134
name="twitter:description"
135-
content="Compare Notifuse (open-source, self-hosted) vs Brevo (SaaS). All features included vs $499/mo for advanced features."
135+
content="Compare Notifuse (open-source, self-hosted) vs Brevo (SaaS). No per-contact pricing vs $499/mo for advanced features."
136136
/>
137137
<meta name="twitter:image" content="https://notifuse.com/og-image.png" />
138138
<meta name="twitter:site" content="@notifuse" />
@@ -166,7 +166,7 @@ const proConsRows = [
166166
<!-- Hero Section -->
167167
<ComparisonHero
168168
competitor="Brevo"
169-
description="Notifuse is open-source and self-hosted with all features included. Brevo (formerly Sendinblue) is a multi-channel SaaS that gates key features behind $18-$499+/mo tiers. Choose Notifuse for full data ownership and no per-contact pricing."
169+
description="Notifuse is open source and runs on your infrastructure or ours, and never charges per contact. Brevo (formerly Sendinblue) is a multi-channel SaaS that gates key features behind $18-$499+/mo tiers. Choose Notifuse for full data ownership and no per-contact pricing."
170170
/>
171171

172172
<!-- Main Comparison Layout -->
@@ -209,12 +209,12 @@ const proConsRows = [
209209
<div class="flex items-start mb-4">
210210
<span class="text-2xl font-bold text-primary mr-3">2.</span>
211211
<h4 class="font-semibold text-slate-900 mt-1">
212-
All Features Included vs Tier-Gated
212+
Included vs Tier-Gated
213213
</h4>
214214
</div>
215215
<p class="text-sm text-gray-700 mb-3">
216-
<strong class="text-primary">Notifuse:</strong> Every feature available
217-
from day one. No per-contact fees.
216+
<strong class="text-primary">Notifuse:</strong> Every feature on every
217+
Cloud plan. No per-contact fees, ever — Cloud or self-hosted.
218218
</p>
219219
<p class="text-sm text-gray-500">
220220
<strong>Brevo:</strong> Key features locked behind Standard ($18/mo),
@@ -334,7 +334,7 @@ const proConsRows = [
334334
<span class="font-semibold text-slate-900">Notifuse</span> is an open-source
335335
email platform (available as managed Cloud or self-hosted) with a visual MJML builder, advanced segmentation,
336336
automations, multi-tenancy, and flexible ESP connections. <strong
337-
>All features included</strong
337+
>Every feature on every Cloud plan</strong
338338
> from day one—no tier-based pricing, no per-contact fees.
339339
</p>
340340
</FeatureSection>
@@ -1757,7 +1757,7 @@ const proConsRows = [
17571757
</h4>
17581758
<p class="text-gray-600 text-center mb-6">
17591759
Import your contacts, recreate your templates in MJML, and connect
1760-
your preferred ESP. All features included from day one.
1760+
your preferred ESP. Every feature on every Cloud plan, from day one.
17611761
</p>
17621762
<div class="flex flex-col sm:flex-row gap-4 justify-center">
17631763
<a
@@ -1862,7 +1862,7 @@ const proConsRows = [
18621862
<div class="max-w-4xl mx-auto">
18631863
<ComparisonCTA
18641864
title="Ready to experience Notifuse?"
1865-
description="Open-source, self-hosted, all features included. No tier gates, no per-contact pricing. Join teams who switched from Brevo."
1865+
description="Open source, Cloud or self-hosted. No per-contact pricing, and no tier gates on Cloud. Join teams who switched from Brevo."
18661866
/>
18671867
</div>
18681868
</div>

0 commit comments

Comments
 (0)