diff --git a/src/marketing/routes.py b/src/marketing/routes.py index af7d017..ff08c77 100644 --- a/src/marketing/routes.py +++ b/src/marketing/routes.py @@ -93,3 +93,17 @@ def fonctionnalites(): plus full integrations list and supported export formats. """ return render_template('marketing/fonctionnalites.html') + + +@marketing_bp.route('/conformite') +def conformite(): + """Standalone compliance page — Loi 25, LGGRI, AGPL, EFVP details.""" + return render_template('marketing/conformite.html') + + +@marketing_bp.route('/contact', methods=['GET']) +def contact(): + """Contact page — pre-launch: mailto-only form (no backend submit yet). + POST handler will be added in B-2.x once form-handling + Turnstile are wired. + """ + return render_template('marketing/contact.html') diff --git a/static/css/marketing.css b/static/css/marketing.css index 51921fa..36b7380 100644 --- a/static/css/marketing.css +++ b/static/css/marketing.css @@ -2303,6 +2303,9 @@ .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); } @@ -3049,6 +3052,13 @@ } } } + .hover\:border-brand-b1\/30 { + &:hover { + @media (hover: hover) { + border-color: color-mix(in oklab, #0062ff 30%, transparent); + } + } + } .hover\:border-yellow-500\/30 { &:hover { @media (hover: hover) { @@ -3336,6 +3346,13 @@ } } } + .hover\:bg-white { + &:hover { + @media (hover: hover) { + background-color: var(--color-white); + } + } + } .hover\:bg-white\/20 { &:hover { @media (hover: hover) { @@ -4154,6 +4171,11 @@ flex-direction: row; } } + .md\:items-start { + @media (width >= 48rem) { + align-items: flex-start; + } + } .md\:gap-4 { @media (width >= 48rem) { gap: calc(var(--spacing) * 4); diff --git a/templates/marketing/conformite.html b/templates/marketing/conformite.html new file mode 100644 index 0000000..9f0387a --- /dev/null +++ b/templates/marketing/conformite.html @@ -0,0 +1,147 @@ +{% extends 'marketing/base.html' %} + +{% block title %}Conformité DictIA — Loi 25 (LPRPSP), LGGRI, AGPL v3, audit trail{% endblock %} +{% block description %}DictIA mappe son architecture aux exigences Loi 25 (LPRPSP), au cadre IA du secteur public québécois (LGGRI), avec hébergement OVH Beauharnois et code source AGPL v3 vérifiable.{% endblock %} + +{% block content %} + +{# ===== HEADER ===== #} +
+ +
+

CONFORMITÉ — FORTERESSE QUÉBÉCOISE

+

+ Architecture conçue avec les exigences professionnelles québécoises. +

+

+ Détails techniques, EFVP type, modèles de déclaration CAI : disponibles sur demande à info@dictia.ca. +

+
+
+ +{# ===== 4 PILLARS (same as landing's Conformité section, content-identical for SEO single source of truth on /conformite landing page) ===== #} +
+
+
+

QUATRE PILIERS

+

+ Pourquoi la conformité est structurelle, pas optionnelle. +

+
+
+ {% for card in [ + { + 'icon': '🍁', + 'title': 'Hébergement OVH Beauharnois', + 'desc': 'Centre de données opéré par OVHcloud Canada en territoire québécois. Conformité documentée selon les services (ISO 27001, SOC 2 selon le périmètre). Détails sur demande.' + }, + { + 'icon': '⚖️', + 'title': 'Mappé Loi 25 (LPRPSP)', + 'desc': 'Audit trail art. 3.5, EFVP préparée art. 3.3, registre des consentements art. 14. Modèles de déclaration CAI fournis.' + }, + { + 'icon': '🏛️', + 'title': 'Compatible Cadre IA secteur public', + 'desc': 'DictIA est conçu pour s\'inscrire dans le cadre de gestion des systèmes d\'IA du secteur public québécois (LGGRI). Documentation détaillée sur demande.' + }, + { + 'icon': '🔓', + 'title': 'Code source AGPL v3 vérifiable', + 'desc': 'Architecture entièrement auditable sur Gitea public. Aucune boîte noire. Vos auditeurs peuvent examiner chaque ligne.' + } + ] %} +
+ +

{{ card.title | safe }}

+

{{ card.desc | safe }}

+
+ {% endfor %} +
+
+
+ +{# ===== LOI 25 DETAIL ===== #} +
+
+
+

LOI 25 (LPRPSP)

+

+ Trois articles centraux que DictIA adresse par construction. +

+

+ La Loi sur la protection des renseignements personnels dans le secteur privé (LPRPSP, communément appelée « Loi 25 ») impose une discipline stricte sur les données biométriques et confidentielles. Les voix capturées en réunion en font partie. Voici comment notre architecture y répond. +

+
+ +
+ {% for art in [ + { + 'num': 'Art. 3.3', + 'title': 'Évaluation des facteurs relatifs à la vie privée (EFVP)', + 'desc': 'Tout déploiement de DictIA dans un cabinet ou un organisme public déclenche une EFVP. Nous fournissons un modèle pré-rempli pour la voix professionnelle (catégories de données, finalités, mesures de sécurité, durée de conservation, transferts) — à compléter avec votre responsable de la protection des renseignements personnels (RPRP).' + }, + { + 'num': 'Art. 3.5', + 'title': 'Audit trail intégré', + 'desc': 'Chaque enregistrement, écoute, export, partage ou suppression est journalisé : utilisateur, IP, date/heure UTC, action. Le journal est consultable par votre RPRP et exportable pour audits CAI ou ordres professionnels. Aucun moyen de désactiver le journal côté client.' + }, + { + 'num': 'Art. 14', + 'title': 'Consentement explicite et tracé', + 'desc': 'Avant tout enregistrement, DictIA exige une confirmation que les participants ont consenti à l\'enregistrement et à la transcription IA. Le consentement est tracé dans le journal d\'audit. Vous pouvez configurer une demande de consentement automatique en début de session.' + } + ] %} +
+
+
+ {{ art.num | safe }} +
+
+

{{ art.title | safe }}

+

{{ art.desc | safe }}

+
+
+
+ {% endfor %} +
+
+
+ +{# ===== AGPL TRANSPARENCY ===== #} +
+
+

AGPL V3 — TRANSPARENCE

+

+ Code vérifiable ligne par ligne. +

+

+ DictIA est publié sous licence GNU AGPL v3. Conséquence pratique : tout fork hébergé doit publier ses modifications sous la même licence. Vos auditeurs internes ou un tiers de confiance peuvent inspecter chaque ligne — modèle ML, pipeline audio, gestion d'identité, journal d'audit, exports. Aucune boîte noire propriétaire. +

+
+ {% from 'macros/button.html' import button %} + {{ button('Code source sur Gitea', href='https://gitea.innova-ai.ca/Innova-AI/dictia-public', variant='primary', size='lg', icon='↗', target='_blank', rel='noopener') }} + {{ button('Comprendre AGPL v3', href='https://www.gnu.org/licenses/agpl-3.0.fr.html', variant='ghost', size='lg', target='_blank', rel='noopener') }} +
+
+
+ +{# ===== CTA ===== #} +
+
+

+ Une question de conformité ? +

+

+ Nous accompagnons votre RPRP, votre comptable d'ordre ou votre service juridique dans l'évaluation. Modèle d'EFVP, registre de consentements et exemple de déclaration CAI sur demande. +

+
+ {% from 'macros/button.html' import button %} + {{ button('Demander un dossier conformité', href='mailto:info@dictia.ca?subject=Demande%20dossier%20conformit%C3%A9', variant='primary', size='lg', icon='✉️') }} + {{ button('Voir les forfaits', href='/tarifs', variant='secondary', size='lg') }} +
+
+
+ +{% endblock %} diff --git a/templates/marketing/contact.html b/templates/marketing/contact.html new file mode 100644 index 0000000..319d1d0 --- /dev/null +++ b/templates/marketing/contact.html @@ -0,0 +1,107 @@ +{% extends 'marketing/base.html' %} + +{% block title %}Contact DictIA — info@dictia.ca, (581) 996-8471, Inverness QC{% endblock %} +{% block description %}Joignez DictIA Inc. à info@dictia.ca ou (581) 996-8471. Bureau au 77 ch. de la Seigneurie, Inverness QC. Réponse sous 2 jours ouvrables.{% endblock %} + +{% block content %} + +{# ===== HEADER ===== #} +
+
+

CONTACT

+

+ Parlons de votre projet. +

+

+ Réponse sous 2 jours ouvrables. Pour les urgences techniques des clients existants, voyez la console DictIA → Support. +

+
+
+ +{# ===== CONTACT METHODS ===== #} +
+
+

Trois manières de nous joindre

+
+ + {# Email card #} +
+ +

Courriel

+

+ Privilégiez le courriel pour : pré-inscription, devis, démonstration, dossier de conformité, partenariats. +

+ info@dictia.ca +
+ + {# Phone card #} +
+ +

Téléphone

+

+ Du lundi au vendredi, 9 h à 17 h (heure de l'Est). Laissez un message en dehors de ces heures. +

+ (581) 996-8471 +
+ + {# Mailing address card #} +
+ +

Bureau

+

+ Sur rendez-vous uniquement. Visites en personne pour démonstrations on-premise et déploiements DictIA 16 corporatifs. +

+
+ 77 ch. de la Seigneurie
+ Inverness QC G0S 1K0 +
+
+
+
+
+ +{# ===== USE-CASE SHORTCUTS ===== #} +
+
+
+

RACCOURCIS

+

+ Un sujet précis ? +

+

+ Pré-remplissez le sujet du courriel selon votre besoin : +

+
+ +
+ {% for shortcut in [ + {'label': 'Pré-inscription DictIA', 'subject': 'Pr%C3%A9-inscription%20DictIA', 'icon': '🎯'}, + {'label': 'Devis multi-sites', 'subject': 'Devis%20multi-sites', 'icon': '🏢'}, + {'label': 'Demande de démonstration', 'subject': 'Demande%20de%20d%C3%A9monstration', 'icon': '📺'}, + {'label': 'Dossier conformité Loi 25', 'subject': 'Dossier%20conformit%C3%A9%20Loi%2025', 'icon': '⚖️'}, + {'label': 'Partenariat / intégration', 'subject': 'Partenariat%20/%20int%C3%A9gration', 'icon': '🤝'}, + {'label': 'Question média / presse', 'subject': 'Question%20m%C3%A9dia', 'icon': '📰'} + ] %} + + + {{ shortcut.label | safe }} + + {% endfor %} +
+
+
+ +{# ===== PRE-LAUNCH NOTE ===== #} +
+
+

+ Formulaire en ligne : bientôt disponible +

+

+ Le formulaire de contact en ligne ouvrira au lancement (printemps 2026). D'ici là, le courriel reste le canal officiel. +

+
+
+ +{% endblock %} diff --git a/tests/test_marketing_secondary_pages.py b/tests/test_marketing_secondary_pages.py index e0aeec1..c0127c6 100644 --- a/tests/test_marketing_secondary_pages.py +++ b/tests/test_marketing_secondary_pages.py @@ -176,3 +176,143 @@ def test_secondary_pages_have_canonical_meta(): assert 'rel="canonical"' in body assert 'og:type' in body assert 'twitter:card' in body + + +# === /conformite === + +def test_conformite_route_returns_200(): + client = app.test_client() + response = client.get('/conformite') + assert response.status_code == 200 + + +def test_conformite_extends_base_with_h1(): + client = app.test_client() + body = client.get('/conformite').data.decode('utf-8') + assert '' in body + assert 'lang="fr-CA"' in body + assert 'page-title' in body + assert body.count('' in body + assert 'page-title' in body + assert body.count('