From 202e1a08d92c67a0363213db812ece6a6c880088 Mon Sep 17 00:00:00 2001 From: Allison Date: Mon, 27 Apr 2026 21:06:26 -0400 Subject: [PATCH] =?UTF-8?q?fix(marketing):=20A-2.8a=20=E2=80=94=20extract?= =?UTF-8?q?=20pricing=20partial=20+=20sync=20bento=20+=20OQLF=20+=20test?= =?UTF-8?q?=20calibration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Extract 3 pricing tiers to templates/marketing/_partials/_pricing_tiers.html Single source of truth — landing.html and tarifs.html now {% include %} it. Prevents price drift (LPC art. 219 risk). - Sync bento card #2 description across landing + fonctionnalites (was diverged: 'embeddings' vs 'embeddings vocaux'). Add maintenance reminder comments in both files. - Fix OQLF NBSP on '~2 semaines' matrix cells in /tarifs deep-dive table. - Fix mixed UTF-8/entity 'québécois' -> 'québécois' in tech specs (consistent with rest of file). - Calibrate H2 size on /tarifs FAQ to match landing (clamp 2.75rem cap). - Repair 2 pre-existing test bugs from earlier A-2.x commits: * 'violent la Loi 25' -> accept both NBSP and plain forms (commit 7c6c6fd added the NBSP after the test was written) * 'résilie' -> 'résilie' (Jinja outputs raw UTF-8, not entities) - Update src/marketing/routes.py module docstring to reflect 2/4 done. --- src/marketing/routes.py | 2 +- static/css/marketing.css | 3 -- .../marketing/_partials/_pricing_tiers.html | 30 +++++++++++++++++ templates/marketing/fonctionnalites.html | 4 ++- templates/marketing/landing.html | 33 +++---------------- templates/marketing/tarifs.html | 33 ++----------------- tests/test_marketing_landing_template.py | 5 +-- tests/test_marketing_secondary_pages.py | 2 +- 8 files changed, 45 insertions(+), 67 deletions(-) create mode 100644 templates/marketing/_partials/_pricing_tiers.html diff --git a/src/marketing/routes.py b/src/marketing/routes.py index f2b5f57..af7d017 100644 --- a/src/marketing/routes.py +++ b/src/marketing/routes.py @@ -2,7 +2,7 @@ Phase 2 (A-2.1+): renders templates/marketing/landing.html. Tasks A-2.2 through A-2.7 will progressively enrich the landing template. -Tasks A-2.8 will add /tarifs, /fonctionnalites, /conformite, /contact routes. +Task A-2.8a added /tarifs and /fonctionnalites; A-2.8b will add /conformite and /contact. """ from flask import render_template diff --git a/static/css/marketing.css b/static/css/marketing.css index add2a8b..51921fa 100644 --- a/static/css/marketing.css +++ b/static/css/marketing.css @@ -2303,9 +2303,6 @@ .text-\[clamp\(2\.25rem\,4vw\,3\.5rem\)\] { font-size: clamp(2.25rem, 4vw, 3.5rem); } - .text-\[clamp\(2rem\,3vw\,2\.5rem\)\] { - font-size: clamp(2rem, 3vw, 2.5rem); - } .text-\[clamp\(2rem\,3vw\,2\.75rem\)\] { font-size: clamp(2rem, 3vw, 2.75rem); } diff --git a/templates/marketing/_partials/_pricing_tiers.html b/templates/marketing/_partials/_pricing_tiers.html new file mode 100644 index 0000000..a11e00f --- /dev/null +++ b/templates/marketing/_partials/_pricing_tiers.html @@ -0,0 +1,30 @@ +{# Single source of truth for the 3 pricing tiers — used by landing.html#tarifs and /tarifs page. + When prices change, edit ONLY this file. #} +{% from 'macros/pricing_card.html' import pricing_card %} +
+ {{ pricing_card( + 'dictia-8', + 'DictIA 8', + '3 450 $', + '173 $', + 'PME · RH · Manufacturiers', + ['GPU 8 Go RTX', 'Volume illimité', 'WhisperX FR-CA', 'Diarisation 8 locuteurs', 'Support inclus'] + ) }} + {{ pricing_card( + 'dictia-16', + 'DictIA 16', + '5 750 $', + '201 $', + 'Cabinets juridiques · CPA · Finance', + ['GPU 16 Go RTX', 'Mistral 7B local', 'Q&R sur enregistrement', 'Tout DictIA 8', 'Support prioritaire'], + recommended=True + ) }} + {{ pricing_card( + 'dictia-cloud', + 'DictIA Cloud', + '0 $', + '369 $', + 'Organismes · Municipalités · Multi-sites', + ['Hébergé OVH Beauharnois (Québec)', 'Opérationnel sous 48 h', 'Aucun matériel à gérer', 'SLA visé 99,9 %', 'Conforme Loi 25'] + ) }} +
diff --git a/templates/marketing/fonctionnalites.html b/templates/marketing/fonctionnalites.html index 2421a10..c674b87 100644 --- a/templates/marketing/fonctionnalites.html +++ b/templates/marketing/fonctionnalites.html @@ -22,6 +22,8 @@

Six fonctionnalités principales

+ {# NOTE: bento card content is duplicated between landing.html and fonctionnalites.html. + When editing, sync both files. Future refactor: extract to _partials/_bento_features.html. #} {% from 'macros/bento.html' import bento_card %}
{{ bento_card('01', 'Transcription WhisperX', 'Large-v3 fine-tuné FR-CA. Précision 95 %+ sur réunions, dictées, audiences (méthodologie disponible sur demande).', '🎙️') }} @@ -108,7 +110,7 @@
{% for spec in [ - {'title': 'Modèle ASR', 'desc': 'WhisperX Large-v3 (1,55 G paramètres) fine-tuné sur audio professionnel québécois. Format ONNX optimisé GPU.'}, + {'title': 'Modèle ASR', 'desc': 'WhisperX Large-v3 (1,55 G paramètres) fine-tuné sur audio professionnel québécois. Format ONNX optimisé GPU.'}, {'title': 'Diarisation', 'desc': 'pyannote 3.x — clustering hiérarchique sur embeddings ECAPA-TDNN. 1 à 8 locuteurs détectés automatiquement.'}, {'title': 'LLM (résumés / Q&R)', 'desc': 'Mistral 7B Instruct quantifié 4-bit. Inférence locale sur le même GPU. Aucune sortie réseau.'}, {'title': 'Stack web', 'desc': 'Flask 2.3 (backend) + Vue 3 / Alpine.js (front). Chiffrement AES-256 au repos. AGPL v3.'}, diff --git a/templates/marketing/landing.html b/templates/marketing/landing.html index d83ff7e..153e11c 100644 --- a/templates/marketing/landing.html +++ b/templates/marketing/landing.html @@ -195,10 +195,12 @@
+ {# NOTE: bento card content is duplicated between landing.html and fonctionnalites.html. + When editing, sync both files. Future refactor: extract to _partials/_bento_features.html. #} {% from 'macros/bento.html' import bento_card %}
{{ bento_card('01', 'Transcription WhisperX', 'Large-v3 fine-tuné FR-CA. Précision 95 %+ sur réunions, dictées, audiences (méthodologie disponible sur demande).', '🎙️') }} - {{ bento_card('02', 'Diarisation 8 locuteurs', 'pyannote sépare automatiquement les intervenants. Identification par embeddings.', '👥') }} + {{ bento_card('02', 'Diarisation 8 locuteurs', 'pyannote sépare automatiquement les intervenants. Identification par embeddings vocaux.', '👥') }} {{ bento_card('03', 'Résumés Mistral 7B', 'IA locale génère résumés, points d\'action et procès-verbaux. Aucune connexion cloud.', '📝') }} {{ bento_card('04', 'Q&R sur enregistrement', 'Posez des questions à vos réunions. RAG local sur embeddings sentence-transformers.', '💬') }} {{ bento_card('05', 'Exports multiples', 'DOCX, PDF, SRT, VTT, TXT, JSON, MD. Formats avocat, notaire, CPA.', '📄') }} @@ -220,34 +222,7 @@

- {% from 'macros/pricing_card.html' import pricing_card %} -
- {{ pricing_card( - 'dictia-8', - 'DictIA 8', - '3 450 $', - '173 $', - 'PME · RH · Manufacturiers', - ['GPU 8 Go RTX', 'Volume illimité', 'WhisperX FR-CA', 'Diarisation 8 locuteurs', 'Support inclus'] - ) }} - {{ pricing_card( - 'dictia-16', - 'DictIA 16', - '5 750 $', - '201 $', - 'Cabinets juridiques · CPA · Finance', - ['GPU 16 Go RTX', 'Mistral 7B local', 'Q&R sur enregistrement', 'Tout DictIA 8', 'Support prioritaire'], - recommended=True - ) }} - {{ pricing_card( - 'dictia-cloud', - 'DictIA Cloud', - '0 $', - '369 $', - 'Organismes · Municipalités · Multi-sites', - ['Hébergé OVH Beauharnois (Québec)', 'Opérationnel sous 48 h', 'Aucun matériel à gérer', 'SLA visé 99,9 %', 'Conforme Loi 25'] - ) }} -
+ {% include 'marketing/_partials/_pricing_tiers.html' %} {# ROI CALCULATOR — Alpine.js, hypotheses transparentes pour LPC art. 219 hygiene #}
diff --git a/templates/marketing/tarifs.html b/templates/marketing/tarifs.html index e40b2bb..23f7264 100644 --- a/templates/marketing/tarifs.html +++ b/templates/marketing/tarifs.html @@ -22,34 +22,7 @@

Trois forfaits DictIA

- {% from 'macros/pricing_card.html' import pricing_card %} -
- {{ pricing_card( - 'dictia-8', - 'DictIA 8', - '3 450 $', - '173 $', - 'PME · RH · Manufacturiers', - ['GPU 8 Go RTX', 'Volume illimité', 'WhisperX FR-CA', 'Diarisation 8 locuteurs', 'Support inclus'] - ) }} - {{ pricing_card( - 'dictia-16', - 'DictIA 16', - '5 750 $', - '201 $', - 'Cabinets juridiques · CPA · Finance', - ['GPU 16 Go RTX', 'Mistral 7B local', 'Q&R sur enregistrement', 'Tout DictIA 8', 'Support prioritaire'], - recommended=True - ) }} - {{ pricing_card( - 'dictia-cloud', - 'DictIA Cloud', - '0 $', - '369 $', - 'Organismes · Municipalités · Multi-sites', - ['Hébergé OVH Beauharnois (Québec)', 'Opérationnel sous 48 h', 'Aucun matériel à gérer', 'SLA visé 99,9 %', 'Conforme Loi 25'] - ) }} -
+ {% include 'marketing/_partials/_pricing_tiers.html' %}
@@ -83,7 +56,7 @@ {'name': 'Diarisation', 'd8': '8 locuteurs', 'd16': '8 locuteurs', 'cloud': '8 locuteurs'}, {'name': 'Résumés Mistral 7B local', 'd8': '—', 'd16': '✓', 'cloud': '✓ (mutualisé)'}, {'name': 'Q&R sur enregistrement', 'd8': '—', 'd16': '✓', 'cloud': '✓'}, - {'name': 'Délai de mise en service', 'd8': '~2 semaines', 'd16': '~2 semaines', 'cloud': '48 h'} + {'name': 'Délai de mise en service', 'd8': '~2 semaines', 'd16': '~2 semaines', 'cloud': '48 h'} ] %} {{ row.name | safe }} @@ -107,7 +80,7 @@

QUESTIONS DE TARIFICATION

-

Vos questions sur les tarifs.

+

Vos questions sur les tarifs.

diff --git a/tests/test_marketing_landing_template.py b/tests/test_marketing_landing_template.py index af55b5e..223d69a 100644 --- a/tests/test_marketing_landing_template.py +++ b/tests/test_marketing_landing_template.py @@ -199,7 +199,8 @@ def test_pas_probleme_section_present(): client = app.test_client() body = client.get('/').data.decode('utf-8') assert 'PROBL' in body and 'TRANSCRIPTION CLOUD' in body, "Missing Problème eyebrow" - assert 'violent la Loi 25' in body, "Missing legal-risk H2 anchor phrase" + assert 'violent la Loi 25' in body or 'violent la Loi 25' in body, \ + "Missing legal-risk H2 anchor phrase" assert 'Cloud Act' in body, "Missing Cloud Act card" assert 'biom' in body and 'Loi 25' in body, "Missing Loi 25 biometric card" assert 'Sanctions disciplinaires' in body, "Missing sanctions disciplinaires card" @@ -661,7 +662,7 @@ def test_faq_section_with_7_questions(): assert f'id="faq-panel-{i}"' in body, f"Missing FAQ panel {i}" # Question topic anchors topics = ['Loi', 'Cloud et DictIA on-premise', 'fran', 'audiences', 'formats', - 'résilie', 'AGPL'] + 'résilie', 'AGPL'] for topic in topics: assert topic in body, f"FAQ missing topic anchor: {topic}" diff --git a/tests/test_marketing_secondary_pages.py b/tests/test_marketing_secondary_pages.py index 5f43824..e0aeec1 100644 --- a/tests/test_marketing_secondary_pages.py +++ b/tests/test_marketing_secondary_pages.py @@ -141,7 +141,7 @@ def test_fonctionnalites_tech_specs_6_items(): client = app.test_client() body = client.get('/fonctionnalites').data.decode('utf-8') assert 'specs-title' in body - for spec_keyword in ['Modèle ASR', 'pyannote', 'Mistral 7B', 'Flask', 'WAV, MP3', 'québécois']: + for spec_keyword in ['Modèle ASR', 'pyannote', 'Mistral 7B', 'Flask', 'WAV, MP3', 'québécois']: assert spec_keyword in body, f"Missing tech spec keyword: {spec_keyword}"