diff --git a/assets/css/custom.css b/assets/css/custom.css index d011359f7..0863a4529 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -259,6 +259,12 @@ ul.list-primary li::marker { @apply rounded-sm shadow-sm bg-white; } +/* The container's overflow-x-clip leaves the y axis visible, so it cannot clip + the opaque corner cells to its own radius — they round themselves. */ +.compare-th { + @apply sticky top-[var(--nav-height,48px)] z-10 align-bottom border-b border-gray-200 first:rounded-tl-sm last:rounded-tr-sm; +} + .input-box { @apply rounded-sm border border-gray-300 focus:ring-0 focus:border-secondary px-3 py-2; } @@ -317,9 +323,9 @@ textarea:read-only { left: 50%; } -/* offsetting anchor targets to adjust for fixed navbar; the offset grows when the - nav banner strip (.nav-promo-strip) is rendered, detected via :has() below; - tooltips carry an id only for aria-describedby, not as scroll anchors */ +/* offsetting anchor targets to adjust for fixed navbar; the offset grows with the + nav banner strip, see the :has() rule below; tooltips carry an id only for + aria-describedby, not as scroll anchors */ [id]:not([role="tooltip"])::before { content: ''; display: block; @@ -328,6 +334,9 @@ textarea:read-only { visibility: hidden; } -body:has(.nav-promo-strip) { +/* The webinar strip stays in the DOM when Alpine hides it, so the marker class — + not its presence — decides whether the nav is one strip taller. */ +body:has(.nav-promo-strip:not(.nav-promo-strip-collapsed)) { --anchor-offset: 138px; + --nav-height: 72px; } diff --git a/assets/css/theme.css b/assets/css/theme.css index e16b0104d..5b3ba3a5c 100644 --- a/assets/css/theme.css +++ b/assets/css/theme.css @@ -114,6 +114,7 @@ --color-secondary: #008a7b; --color-tertiary: #005e71; --color-hub: #354960; + --color-hub-l2: #ebedef; --color-dark: #1e2b33; --font-*: initial; diff --git a/data/pricing_comparison.yaml b/data/pricing_comparison.yaml new file mode 100644 index 000000000..f98bd3abc --- /dev/null +++ b/data/pricing_comparison.yaml @@ -0,0 +1,90 @@ +# Shared across languages: a plan capability is a product fact, not a +# translation. Booleans render as a check or a dash, numbers render as they are, +# and every other cell is an i18n key. +- SectionKey: "pricing_compare_section_basics" + Rows: + - LabelKey: "pricing_compare_seats" + Cryptomator: 1 + Community: 5 + Standard: "pricing_compare_unlimited" + Enterprise: "pricing_compare_unlimited" + - LabelKey: "pricing_compare_unlimited_vaults" + Cryptomator: true + Community: true + Standard: true + Enterprise: true + - LabelKey: "pricing_compare_any_cloud" + TooltipKey: "pricing_individual_benefit_2_tooltip" + Cryptomator: true + Community: true + Standard: true + Enterprise: true + - LabelKey: "pricing_compare_desktop_access" + Cryptomator: true + Community: true + Standard: true + Enterprise: true +- SectionKey: "pricing_compare_section_collaboration" + Rows: + - LabelKey: "pricing_compare_user_management" + Cryptomator: false + Community: true + Standard: true + Enterprise: true + - LabelKey: "pricing_compare_key_management" + Cryptomator: false + Community: true + Standard: true + Enterprise: true + - LabelKey: "pricing_compare_mobile_access" + Cryptomator: "pricing_compare_one_time_purchase" + Community: "pricing_compare_one_time_purchase" + Standard: true + Enterprise: true +- SectionKey: "pricing_compare_section_hosting" + Rows: + - LabelKey: "pricing_compare_self_hosted" + Cryptomator: false + Community: true + Standard: true + Enterprise: true + - LabelKey: "pricing_compare_managed_hosting" + TooltipKey: "pricing_compare_managed_hosting_tooltip" + Cryptomator: false + Community: false + Standard: true + Enterprise: true +- SectionKey: "pricing_compare_section_security" + Rows: + - LabelKey: "pricing_compare_audit_logs" + Cryptomator: false + Community: false + Standard: true + Enterprise: true + - LabelKey: "pricing_compare_emergency_access" + Cryptomator: false + Community: false + Standard: false + Enterprise: "pricing_compare_coming_soon" +- SectionKey: "pricing_compare_section_support" + Rows: + - LabelKey: "pricing_compare_community_support" + Cryptomator: true + Community: true + Standard: true + Enterprise: true + - LabelKey: "pricing_compare_email_support" + Cryptomator: false + Community: false + Standard: true + Enterprise: "pricing_compare_priority" + - LabelKey: "pricing_compare_expert_training" + Cryptomator: false + Community: false + Standard: false + Enterprise: true + - LabelKey: "pricing_compare_account_manager" + Cryptomator: false + Community: false + Standard: false + Enterprise: true diff --git a/i18n/de.yaml b/i18n/de.yaml index 433a45c32..4e8b13553 100644 --- a/i18n/de.yaml +++ b/i18n/de.yaml @@ -1124,6 +1124,8 @@ translation: "Community" - id: pricing_hub_community_subtitle translation: "Kostenlos und Open Source für kleine Teams. Auf eigener Infrastruktur deployen mit voller Kontrolle über die Daten." +- id: pricing_hub_community_price_description + translation: "Self-Hosted" - id: pricing_hub_community_benefit_1 translation: "Inklusive 5 Sitze" - id: pricing_hub_community_benefit_2 @@ -1171,6 +1173,69 @@ - id: pricing_hub_enterprise_benefit_5 translation: "Vorrangiger E-Mail-Support" +- id: pricing_compare_title + translation: "Alle Funktionen im Vergleich" +- id: pricing_compare_subtitle + translation: "Jeder Plan enthält alles aus den vorherigen Plänen." +- id: more_information + translation: "Weitere Informationen" +- id: pricing_compare_features + translation: "Funktionen" +- id: pricing_compare_included + translation: "Enthalten" +- id: pricing_compare_not_included + translation: "Nicht enthalten" +- id: pricing_compare_managed_hosting_tooltip + translation: "Gewartet und aktualisiert von unserem Team in einem ISO/IEC 27001:2022-zertifizierten Rechenzentrum in der EU." +- id: pricing_compare_section_basics + translation: "Grundlagen" +- id: pricing_compare_section_collaboration + translation: "Teamarbeit" +- id: pricing_compare_section_hosting + translation: "Hosting" +- id: pricing_compare_section_security + translation: "Sicherheit & Compliance" +- id: pricing_compare_section_support + translation: "Support" +- id: pricing_compare_seats + translation: "Sitze" +- id: pricing_compare_unlimited_vaults + translation: "Unbegrenzte Tresore" +- id: pricing_compare_any_cloud + translation: "Kompatibel mit jedem Cloud-Speicher" +- id: pricing_compare_desktop_access + translation: "Voller Zugriff über Desktop-App" +- id: pricing_compare_user_management + translation: "Benutzer- und Gruppenverwaltung" +- id: pricing_compare_key_management + translation: "Zero-Knowledge-Schlüsselverwaltung" +- id: pricing_compare_mobile_access + translation: "Voller Zugriff über Mobile-Apps für alle Nutzer" +- id: pricing_compare_self_hosted + translation: "Self-Hosted-Deployment" +- id: pricing_compare_managed_hosting + translation: "Managed Hosting verfügbar" +- id: pricing_compare_audit_logs + translation: "Audit-Logs" +- id: pricing_compare_emergency_access + translation: "Notfallzugriff" +- id: pricing_compare_community_support + translation: "Community-Support" +- id: pricing_compare_email_support + translation: "E-Mail-Support" +- id: pricing_compare_expert_training + translation: "Beratung und Unterstützung durch Experten" +- id: pricing_compare_account_manager + translation: "Dedizierter Kundenbetreuer" +- id: pricing_compare_unlimited + translation: "Unbegrenzt" +- id: pricing_compare_one_time_purchase + translation: "Einmalkauf" +- id: pricing_compare_coming_soon + translation: "Bald verfügbar" +- id: pricing_compare_priority + translation: "Vorrangig" + - id: pricing_support_us_title translation: "Möchtest du unser Open-Source-Projekt unterstützen?" - id: pricing_support_us_description diff --git a/i18n/en.yaml b/i18n/en.yaml index f7f7b33a5..09224cecb 100644 --- a/i18n/en.yaml +++ b/i18n/en.yaml @@ -1125,6 +1125,8 @@ translation: "Community" - id: pricing_hub_community_subtitle translation: "Free and open source for small teams. Deploy on your own infrastructure with full control over your data." +- id: pricing_hub_community_price_description + translation: "self-hosted" - id: pricing_hub_community_benefit_1 translation: "Includes 5 seats" - id: pricing_hub_community_benefit_2 @@ -1172,6 +1174,69 @@ - id: pricing_hub_enterprise_benefit_5 translation: "Priority email support" +- id: pricing_compare_title + translation: "Compare All Features" +- id: pricing_compare_subtitle + translation: "Every plan includes everything from the plans before it." +- id: more_information + translation: "More information" +- id: pricing_compare_features + translation: "Features" +- id: pricing_compare_included + translation: "Included" +- id: pricing_compare_not_included + translation: "Not included" +- id: pricing_compare_managed_hosting_tooltip + translation: "Maintained and updated by our team in an ISO/IEC 27001:2022 certified data center in the EU." +- id: pricing_compare_section_basics + translation: "Basics" +- id: pricing_compare_section_collaboration + translation: "Team Collaboration" +- id: pricing_compare_section_hosting + translation: "Hosting" +- id: pricing_compare_section_security + translation: "Security & Compliance" +- id: pricing_compare_section_support + translation: "Support" +- id: pricing_compare_seats + translation: "Seats" +- id: pricing_compare_unlimited_vaults + translation: "Unlimited vaults" +- id: pricing_compare_any_cloud + translation: "Works with any cloud storage" +- id: pricing_compare_desktop_access + translation: "Full access via Desktop app" +- id: pricing_compare_user_management + translation: "User and group management" +- id: pricing_compare_key_management + translation: "Zero-knowledge key management" +- id: pricing_compare_mobile_access + translation: "Full access via Mobile apps for all users" +- id: pricing_compare_self_hosted + translation: "Self-hosted deployment" +- id: pricing_compare_managed_hosting + translation: "Managed hosting available" +- id: pricing_compare_audit_logs + translation: "Audit logs" +- id: pricing_compare_emergency_access + translation: "Emergency Access" +- id: pricing_compare_community_support + translation: "Community support" +- id: pricing_compare_email_support + translation: "Email support" +- id: pricing_compare_expert_training + translation: "Expert training and assistance" +- id: pricing_compare_account_manager + translation: "Dedicated account manager" +- id: pricing_compare_unlimited + translation: "Unlimited" +- id: pricing_compare_one_time_purchase + translation: "One-time purchase" +- id: pricing_compare_coming_soon + translation: "Coming Soon" +- id: pricing_compare_priority + translation: "Priority" + - id: pricing_support_us_title translation: "Want to support our open-source project?" - id: pricing_support_us_description diff --git a/layouts/partials/nav.html b/layouts/partials/nav.html index 5aff3c9d4..b425605a5 100644 --- a/layouts/partials/nav.html +++ b/layouts/partials/nav.html @@ -11,7 +11,8 @@ {{- else if .Scratch.Get "webinarBannerActive" -}} {{- $webinarPath := printf "%s/webinar/" .Site.LanguagePrefix -}} - + {{/* Feature Comparison Section */}} +
+
+

{{ i18n "pricing_compare_title" . }}

+

{{ i18n "pricing_compare_subtitle" . }}

+
+
+ + {{- $cryptomatorBrand := dict "brand" "Cryptomator" "logo" "/img/logo.svg" "logoAspect" "aspect-1110/942" "brandColor" "text-primary" }} + {{- $hubBrand := dict "brand" "Cryptomator Hub" "logo" "/img/zero-knowledge-bot.svg" "logoAspect" "aspect-988/877" "brandColor" "text-hub" }} + {{- $plans := slice + (merge $cryptomatorBrand (dict "dataKey" "Cryptomator" "titleKey" "pricing_individual_title" "highlight" false)) + (merge $hubBrand (dict "dataKey" "Community" "titleKey" "pricing_hub_community_title" "highlight" false)) + (merge $hubBrand (dict "dataKey" "Standard" "titleKey" "pricing_hub_standard_title" "highlight" true)) + (merge $hubBrand (dict "dataKey" "Enterprise" "titleKey" "pricing_hub_enterprise_title" "highlight" false)) + }} + + + + {{- range $plans }} + + {{- end }} + + + + {{- range $sectionIndex, $section := hugo.Data.pricing_comparison }} + + + {{- /* empty cells exist solely to carry the highlighted column's tint across section rows */}} + {{- range $plans }} + + {{- end }} + + {{- range $rowIndex, $row := .Rows }} + + + {{- range $plans }} + {{- $plan := . }} + {{- $cell := index $row $plan.dataKey }} + + {{- end }} + + {{- end }} + {{- end }} + + {{- /* The tfoot below is hand-written per plan, so it silently desyncs if $plans changes. */}} + {{- $footerPlans := "Cryptomator:false,Community:false,Standard:true,Enterprise:false" }} + {{- $actualParts := slice }} + {{- range $plans }}{{ $actualParts = $actualParts | append (printf "%v:%v" .dataKey .highlight) }}{{ end }} + {{- $actualPlans := delimit $actualParts "," }} + {{- if ne $actualPlans $footerPlans }}{{ errorf "pricing_comparison: $plans is %q but the tfoot is hand-written for %q — update the tfoot cells to match" $actualPlans $footerPlans }}{{ end }} + + + + + + + + + +
+ {{ i18n "pricing_compare_features" . }} + + + {{ .brand }} + {{ i18n .titleKey $ }} +
{{ i18n .SectionKey $ }}
+ {{ i18n .LabelKey $ }} + {{- with .TooltipKey }} + {{- $tooltipId := printf "tooltip-compare-%d-%d" $sectionIndex $rowIndex }} +
+ + +
+ {{- end }} +
+ {{- if eq $cell true }} + + {{ i18n "pricing_compare_included" $ }} + {{- else if eq $cell false }} + + {{ i18n "pricing_compare_not_included" $ }} + {{- else if eq (printf "%T" $cell) "string" }} + {{ i18n $cell $ }} + {{- else if $cell }} + {{ $cell }} + {{- else }} + {{- errorf "pricing_comparison: row %q is missing a value for plan %q" $row.LabelKey $plan.dataKey }} + {{- end }} +
+ {{ i18n "pricing_free" . }} + {{ i18n "pricing_individual_price_description" . }} + + {{ i18n "pricing_individual_cta" . }} + + + {{ i18n "pricing_free" . }} + {{ i18n "pricing_hub_community_price_description" . }} + + {{ i18n "pricing_hub_self_hosted_cta" . }} + + + + {{ i18n "pricing_hub_standard_price_description" . }} + + {{ i18n "pricing_hub_managed_cta" . }} + + + {{ i18n "pricing_hub_self_hosted_cta" . }} + + + {{ i18n "pricing_hub_enterprise_price" . }} + {{ i18n "pricing_hub_enterprise_price_description" . }} + + {{ i18n "pricing_hub_enterprise_cta" . }} + +
+
+
+ {{/* Support Us Section */}} + {{- $data := index hugo.Data $.Site.Language.Lang }}

{{ i18n "pricing_support_us_title" . }}

{{ i18n "pricing_support_us_description" . }}

@@ -366,7 +487,6 @@

{{ i18n "pricing_support_us_title" . }}

{{ i18n "faq_title" . }}

- {{- $data := index hugo.Data $.Site.Language.Lang }}