diff --git a/static/css/marketing.css b/static/css/marketing.css index 2144237..21694c1 100644 --- a/static/css/marketing.css +++ b/static/css/marketing.css @@ -399,6 +399,9 @@ .-top-20 { top: calc(var(--spacing) * -20); } + .-top-32 { + top: calc(var(--spacing) * -32); + } .top-0 { top: calc(var(--spacing) * 0); } @@ -453,6 +456,9 @@ .-right-24 { right: calc(var(--spacing) * -24); } + .-right-32 { + right: calc(var(--spacing) * -32); + } .right-0 { right: calc(var(--spacing) * 0); } @@ -489,6 +495,9 @@ .-bottom-24 { bottom: calc(var(--spacing) * -24); } + .-bottom-32 { + bottom: calc(var(--spacing) * -32); + } .bottom-0 { bottom: calc(var(--spacing) * 0); } @@ -507,6 +516,9 @@ .-left-20 { left: calc(var(--spacing) * -20); } + .-left-32 { + left: calc(var(--spacing) * -32); + } .left-0 { left: calc(var(--spacing) * 0); } @@ -1411,6 +1423,9 @@ .gap-8 { gap: calc(var(--spacing) * 8); } + .gap-10 { + gap: calc(var(--spacing) * 10); + } .gap-\[1\.5px\] { gap: 1.5px; } @@ -2132,6 +2147,9 @@ .bg-brand-navy2 { background-color: #0b1525; } + .bg-brand-navy3\/60 { + background-color: color-mix(in oklab, #0f1e35 60%, transparent); + } .bg-emerald-600 { background-color: var(--color-emerald-600); } @@ -5256,6 +5274,11 @@ padding: calc(var(--spacing) * 10); } } + .md\:p-12 { + @media (width >= 48rem) { + padding: calc(var(--spacing) * 12); + } + } .md\:px-6 { @media (width >= 48rem) { padding-inline: calc(var(--spacing) * 6); @@ -5434,6 +5457,11 @@ grid-template-columns: 1fr 240px; } } + .lg\:grid-cols-\[minmax\(0\,1fr\)_minmax\(0\,420px\)\] { + @media (width >= 64rem) { + grid-template-columns: minmax(0,1fr) minmax(0,420px); + } + } .lg\:flex-row { @media (width >= 64rem) { flex-direction: row; @@ -5444,6 +5472,11 @@ gap: calc(var(--spacing) * 10); } } + .lg\:gap-12 { + @media (width >= 64rem) { + gap: calc(var(--spacing) * 12); + } + } .lg\:border-r { @media (width >= 64rem) { border-right-style: var(--tw-border-style); diff --git a/templates/marketing/_partials/_pricing_tiers.html b/templates/marketing/_partials/_pricing_tiers.html index 3aae245..4b82f29 100644 --- a/templates/marketing/_partials/_pricing_tiers.html +++ b/templates/marketing/_partials/_pricing_tiers.html @@ -1,11 +1,11 @@ {# Single source of truth for the v7.0 pricing — used by landing.html#tarifs and /tarifs page. When prices change, edit ONLY this file (and src/billing/plans.py for Stripe IDs). - v7.0 — 4 forfaits + 1 soumission : + v7.0 — 3 forfaits Cloud (en rangée) + 1 DictIA LOCAL (bloc dédié) + 1 soumission : - Cloud BASIC 189 $/mois (no setup) - Cloud ESSENTIEL 349 $/mois (no setup) - Cloud PRO 549 $/mois + 485 $ onboarding (recommended) - - DictIA LOCAL 5 998 $ An 1 puis 500 $/an dès An 2 (no monthly) + - DictIA LOCAL 5 998 $ An 1 puis 500 $/an dès An 2 (bloc large dédié, "Vous en êtes propriétaire") - Pro+ soumission personnalisée → /contact?pro-plus=1 Common to all forfaits : @@ -13,6 +13,7 @@ exports SRT/VTT/TXT/JSON/DOCX, Loi 25 conforme, OVH Beauharnois (Cloud) ou local. #} {% from 'macros/pricing_card.html' import pricing_card %} +{% from 'macros/button.html' import button %} {%- set _baseline_features_cloud = [ 'WhisperX Large-v3 · 99 %+ précision · 99+ langues', @@ -24,7 +25,8 @@ 'Aucune limite utilisateurs' ] -%} -
+{# === Ligne 1 — 3 forfaits Cloud (1/2/3 cols responsive) === #} +
{{ pricing_card( slug='cloud-basic', @@ -70,32 +72,116 @@ cta_label='Commander Pro' ) }} - {{ pricing_card( - slug='dictia-local', - name='DictIA LOCAL', - badge='Local · 100 % hors-ligne', - target='Confidentialité maximale · 100 % hors-ligne chez vous.', - setup=5998, - yearly_renewal=500, - capacity_audio='~1 100 h audio/mois', - capacity_storage='2 To SSD', - gpu='RTX 5070 Ti 16 Go (dédié)', - features=[ - 'WhisperX Large-v3 · 99 %+ précision · 99+ langues', - 'Diarisation pyannote (qui parle)', - 'Résumés IA + Points d’action (Mistral 7B local)', - 'Exports SRT, VTT, TXT, JSON, DOCX', - 'GPU local dédié · transcription locale', - 'Données jamais sortantes (chez vous)', - '500 $/an dès An 2 (MAJ + support)', - 'Aucune limite utilisateurs' - ], - cta_label='Configurer DictIA Local' - ) }} -
-{# Pro+ banner — soumission personnalisée pour grands volumes / SLA renforcé #} +{# === Bloc 2 — DictIA LOCAL (large, distinctif, pleine largeur) === #} +
+ {# Decorative orbs background — purely decorative, hidden from AT #} + + + +
+ + {# === LEFT — copy + checkmarks === #} +
+
+ + + Au Québec + + par InnovA AI +
+ +

DictIA LOCAL · Serveur souverain

+

+ Vous en êtes propriétaire. +

+

+ On vous vend, configure et installe votre serveur IA directement dans vos locaux. Vous êtes propriétaire du matériel. Vos données ne quittent jamais votre bureau. +

+ +
    + {% for bullet in [ + ('PC + GPU RTX vous appartient', 'pas de location, pas d’abonnement matériel'), + ('Traitement 100 % local', 'aucun transit réseau, fonctionne hors-ligne'), + ('Assemblé et configuré au Québec par InnovA AI', 'support local inclus'), + ('On vient l’installer chez vous', 'formation incluse, opérationnel le jour 1'), + ('Achat direct sans appel d’offres si < 34 700 $', 'DictIA LOCAL s’y qualifie') + ] %} +
  • + + {{ bullet[0] | safe }} — {{ bullet[1] | safe }} +
  • + {% endfor %} +
+ +
+ {{ button('Voir les serveurs disponibles', href='/contact?plan=dictia-local', variant='primary', size='md', icon='') }} + 5 998 $ An 1 · 500 $/an dès An 2 +
+
+ + {# === RIGHT — server visual mockup === #} +
+
+

GPU RTX — DictIA LOCAL

+
+ {# Server icon SVG (rack stylisé) — purely decorative #} + +

Serveur DictIA

+
    +
  • + + Interface web +
  • +
  • + + PC gaming haute performance +
  • +
  • + + GPU RTX 5070 Ti 16 Go dédié IA +
  • +
  • + + WhisperX + LLM Mistral 7B local +
  • +
  • + + DictIA pré-installé +
  • +
  • + + Votre propriété +
  • +
+
+
+
+
+
+ +{# === Pro+ banner — soumission personnalisée pour grands volumes / SLA renforcé === #}
@@ -107,7 +193,6 @@ > 660 h audio/mois · > 500 Go stockage · 7+ utilisateurs intensifs · multi-sites · SLA 99,9 % · SOC 2 Type I/II · PHIPA · PIPEDA Ontario · documentation gouv. (SEAO/MCN).

- {% from 'macros/button.html' import button %} {{ button('Demander une soumission', href='/contact?pro-plus=1', variant='primary', size='md') }}
diff --git a/templates/marketing/tarifs.html b/templates/marketing/tarifs.html index 059a2c8..5d76a81 100644 --- a/templates/marketing/tarifs.html +++ b/templates/marketing/tarifs.html @@ -1,7 +1,7 @@ {% extends 'marketing/base.html' %} -{% block title %}Tarifs DictIA — 4 forfaits transparents en CAD (Cloud Basic 189 $/mo · Essentiel 349 $ · Pro 549 $ · DictIA Local 5 998 $){% endblock %} -{% block description %}Tarifs DictIA en CAD : Cloud Basic (189 $/mo), Cloud Essentiel (349 $/mo), Cloud Pro (549 $/mo + 485 $ onboarding) et DictIA Local (5 998 $ An 1 puis 500 $/an). Aucune limite utilisateurs, taxes en sus.{% endblock %} +{% block title %}Tarifs DictIA — 3 Cloud + 1 serveur Local en CAD (Cloud Basic 189 $/mo · Essentiel 349 $ · Pro 549 $ · DictIA LOCAL 5 998 $){% endblock %} +{% block description %}Tarifs DictIA en CAD : Cloud Basic (189 $/mo), Cloud Essentiel (349 $/mo), Cloud Pro (549 $/mo + 485 $ onboarding) et DictIA LOCAL (5 998 $ An 1 puis 500 $/an, vous en êtes propriétaire). Aucune limite utilisateurs, taxes en sus.{% endblock %} {% block content %} @@ -10,18 +10,18 @@

TARIFS

- Quatre forfaits : choisissez votre infrastructure. + Trois forfaits Cloud + DictIA LOCAL : choisissez votre infrastructure.

- 3 Cloud souverains au Québec + 1 100 % local hors-ligne. Aucune limite utilisateurs, tarifs en CAD, taxes en sus (TPS 5 % + TVQ 9,975 %). + 3 Cloud souverains hébergés au Québec + 1 serveur 100 % local dont vous êtes propriétaire. Aucune limite utilisateurs, tarifs en CAD, taxes en sus (TPS 5 % + TVQ 9,975 %).

-{# ===== 4 PRICING TIERS + Pro+ ===== #} +{# ===== 3 Cloud + DictIA LOCAL block + Pro+ ===== #}
-

Quatre forfaits DictIA + Pro+ sur soumission

+

Trois forfaits Cloud DictIA + DictIA LOCAL + Pro+ sur soumission

{% include 'marketing/_partials/_pricing_tiers.html' %}
diff --git a/tests/test_marketing_landing_template.py b/tests/test_marketing_landing_template.py index eac0eb7..9ab05b0 100644 --- a/tests/test_marketing_landing_template.py +++ b/tests/test_marketing_landing_template.py @@ -360,28 +360,32 @@ def test_pricing_recommended_tier_is_cloud_pro(): def test_pricing_cta_labels_v7(): - """CTAs reflect v7.0 forfait choice (Démarrer en Cloud / Choisir Essentiel / Commander Pro / Configurer DictIA Local).""" + """CTAs reflect v7.0 forfait choice — 3 Cloud go to /checkout, DictIA LOCAL goes to /contact?plan=dictia-local.""" client = app.test_client() body = client.get('/').data.decode('utf-8') - for slug in ['cloud-basic', 'cloud-essentiel', 'cloud-pro', 'dictia-local']: + # 3 Cloud forfaits use /checkout/ + for slug in ['cloud-basic', 'cloud-essentiel', 'cloud-pro']: assert f'href="/checkout/{slug}"' in body, f"Missing checkout link for {slug}" # CTA labels match the macro callers in _pricing_tiers.html assert 'Démarrer en Cloud' in body or 'Démarrer en Cloud' in body assert 'Choisir Essentiel' in body assert 'Commander Pro' in body - assert 'Configurer DictIA Local' in body + # DictIA LOCAL now has a dedicated block with a contact CTA (no /checkout slug) + assert 'Voir les serveurs disponibles' in body, "Missing DictIA LOCAL block CTA label" + assert '/contact?plan=dictia-local' in body, "Missing DictIA LOCAL contact CTA href" def test_pricing_features_use_safe_filter_no_double_escape(): """Pricing card features piped through | safe — ' ' must render single-escaped, not double.""" client = app.test_client() body = client.get('/').data.decode('utf-8') - # Capacity chips use NBSP + # Capacity chips use NBSP (3 Cloud forfaits) assert '~165 h audio/mois' in body, "Missing Cloud BASIC capacity chip" assert '100 Go' in body, "Missing Cloud BASIC storage chip" assert '~660 h audio/mois' in body, "Missing Cloud PRO capacity chip" assert '500 Go' in body, "Missing Cloud PRO storage chip" - assert '2 To SSD' in body, "Missing DictIA LOCAL storage chip" + # DictIA LOCAL dedicated block — server visual mockup uses NBSP for GPU spec + assert 'RTX 5070 Ti 16 Go' in body, "Missing DictIA LOCAL GPU spec line in dedicated block" # WhisperX precision claim w/ NBSP assert 'WhisperX Large-v3' in body, "Missing WhisperX Large-v3 mention" # Loi 25 with NBSP @@ -462,8 +466,9 @@ def test_pricing_cta_url_no_double_slash(): """pricing_card uses cta_url.rstrip('/') so href never has '//' (regression guard).""" client = app.test_client() body = client.get('/').data.decode('utf-8') - # All 4 CTAs use the default cta_url='/checkout' (no trailing slash) — so /checkout/ - for slug in ['cloud-basic', 'cloud-essentiel', 'cloud-pro', 'dictia-local']: + # The 3 Cloud forfaits use the default cta_url='/checkout' (no trailing slash) — so /checkout/ + # DictIA LOCAL is presented in a dedicated block with /contact?plan=dictia-local (not via pricing_card macro) + for slug in ['cloud-basic', 'cloud-essentiel', 'cloud-pro']: assert f'href="/checkout/{slug}"' in body, f"Missing single-slash href for {slug}" assert f'href="/checkout//{slug}"' not in body, f"Double-slash regression for {slug}" diff --git a/tests/test_marketing_secondary_pages.py b/tests/test_marketing_secondary_pages.py index 5c11865..07f7eb0 100644 --- a/tests/test_marketing_secondary_pages.py +++ b/tests/test_marketing_secondary_pages.py @@ -34,7 +34,7 @@ def test_tarifs_has_h1_with_anchor(): def test_tarifs_renders_4_pricing_cards_v7(): - """Tarifs page renders the v7.0 4 forfaits + Pro+ chip.""" + """Tarifs page renders the v7.0 3 Cloud forfaits + DictIA LOCAL dedicated block + Pro+ chip.""" client = app.test_client() body = client.get('/tarifs').data.decode('utf-8') for tier in ['Cloud BASIC', 'Cloud ESSENTIEL', 'Cloud PRO', 'DictIA LOCAL']: @@ -45,11 +45,15 @@ def test_tarifs_renders_4_pricing_cards_v7(): assert '549 $' in body assert '485 $' in body # Cloud Pro onboarding assert '5 998 $' in body # DictIA Local An 1 - # Checkout slugs + # 3 Cloud forfaits use checkout slugs assert 'href="/checkout/cloud-basic"' in body assert 'href="/checkout/cloud-essentiel"' in body assert 'href="/checkout/cloud-pro"' in body - assert 'href="/checkout/dictia-local"' in body + # DictIA LOCAL has its own dedicated block with a contact CTA (no /checkout slug) + assert '/contact?plan=dictia-local' in body, "Missing DictIA LOCAL block contact CTA" + assert 'Vous en êtes' in body or 'Vous en êtes' in body, \ + "Missing DictIA LOCAL block headline 'Vous en êtes propriétaire'" + assert 'Serveur DictIA' in body, "Missing DictIA LOCAL block server visual mockup label" # Pro+ chip with /contact link assert 'Pro+' in body assert '/contact?pro-plus=1' in body