feat/marketing-redesign-2026
77 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
224e51cc81 |
feat(marketing): refonte narrative 'Comment ça marche' — processus 4 étapes step-by-step (capture → IA → distribution → gouvernance)
Au lieu d'un cycle générique 1→12, l'animation suit maintenant un FLOW PROCESSUS canonique qui raconte l'histoire produit du début à la fin : ÉTAPE 1 · CAPTURE → Recording (7) + Transcription (1) ÉTAPE 2 · TRANSFORMATION IA → Diarisation (2) + Langues (3) + Résumé (9) + Chat IA (0) ÉTAPE 3 · DISTRIBUTION → Exports (4) + Partage (6) + Users (5) + Intégrations (10) ÉTAPE 4 · GOUVERNANCE → Recherche IA (8) + Audit (11) + Conformité (12) PROCESS_ORDER = [7, 1, 2, 3, 9, 0, 4, 6, 5, 10, 8, 11, 12] — 13 sub-modes, mode 0 IA inclus dans le cycle (au lieu d'être skippé), 1100ms × 13 ≈ 14.3s tour complet. Composants ajoutés / refondus : - PROCESS BREADCRUMB visible au-dessus du phone (4 pilules · flèches · past/active/future) - PROGRESS BAR sous breadcrumb (gradient brand-b1 → b2 → b3, role=progressbar) - BOTTOM TAB BAR : 4 boutons étapes (au lieu de 6 features individuelles) - RIGHT PANEL : 4 mini-sections par étape (12 boutons regroupés selon flow) - FEATURE INFO CARD : préfixe 'Étape X/4 · TITRE' pour contexte processus - CONNECTING LINE : flèche directionnelle + dot animé qui se déplace selon processIdx - Mobile pills : suivent PROCESS_ORDER (13 sub-modes au lieu de 1-12) Alpine refactor : - selectedFeature devient un getter computed sur PROCESS_ORDER[processIdx] - nouveau STEPS array avec subModes mapping + activeStep / progressPercent getters - goToStep(id) helper jump au 1er sub-mode d'une étape - handleManualSelect(i) résout l'index dans PROCESS_ORDER Préservé : palette brand stricte (b1/b2/b3/navy), V3 radii, Inter+JetBrains Mono, phone shell statique, 13 templates de modes, IA Mistral premium card, eyebrow 'COMMENT ÇA MARCHE' brand-navy, WCAG (aria-current, aria-label, role=navigation, role=progressbar, role=tablist), prefers-reduced-motion guards. Tests : 9/9 fonctionnalites passent (test how_it_works_reactor étendu de +37 assertions narratives : PROCESS_ORDER, STEPS, processIdx, activeStep, goToStep, breadcrumb, progressbar, step-bottom, step-section, contexte processus dans card). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
a14bcb9a1a |
fix(marketing): restaurer visibilité 12 fonctions dans 'Comment ça marche'
L'abstraction en 4 catégories (commit
|
||
|
|
8a7650f9fa |
feat(marketing): refonte stratégique 'Comment ça marche' — 4 catégories × 12 sous-modes
Audit complet des fonctionnalités réelles DictIA (manuel utilisateur + composants production) puis restructuration de l'animation phone container en 4 catégories logiques regroupant 12 sous-modes (6 historiques + 6 nouveaux). CATÉGORIES (bottom tab bar 4 boutons + sub-mode dots indicator) : - Capture (b2 cyan) : Transcription, Recording live, Recherche IA - Transformation IA (b1) : Diarisation, 99+ langues, Résumé+actions, Chat IA - Distribution (b3 fuchsia) : Exports, Intégrations Hub, Partage, Users - Gouvernance (b1 blue) : Audit trail, Conformité Loi 25 + 9 ordres pros NOUVEAUX MODES IMPLÉMENTÉS : - Mode 7 Recording live : minuteur 99s + waveform 24 bars random + 3 boutons sources (mic/système/combiné) selon manuel utilisation v1.0 - Mode 8 Recherche sémantique : query typed + 3 résultats highlight RAG - Mode 9 Résumé + actions : décisions/actions stagger + extraction ICS - Mode 10 Intégrations : hub central DictIA + 8 logos en orbite (Word, Outlook, Teams, Notion, Obsidian, Zapier, Make, n8n) + lignes connexion SVG - Mode 11 Audit trail : 6 events horodatés (INFO/AUTH/PROC/READ/EXP/SHARE) + badge consentement tracé immutable (Loi 25 art. 8) - Mode 12 Conformité Loi 25 : 6 badges (Loi 25/96/EFVP CAI/MCN/AGPL/0 Cloud Act US) + 9 ordres pros (Barreau, CNQ, CPA, ChAD, OACIQ, CMQ, OIIQ, OPQ, OEQ) REFACTOR Alpine dictiaDashboard() : - FEATURES étendu de 7 à 13 entrées (idx 0-12) - CATEGORIES array avec submodes[], iconPath, color, subtitle - activeCategory + handleCategorySelect(ci) en plus de handleManualSelect(i) - Auto-cycle 1100ms entre sous-modes ; switch catégorie quand fin atteinte - Right grid 3×6 → 2×2 categories cards (preview sub-modes dots) - Bottom tab bar 6 modes → 4 catégories (icons larger 18px) + sub-mode dots - Mobile pills par catégorie (au lieu de par mode) Préservé : palette brand-b1/b2/b3 stricte, phone shell statique 280×580, WCAG AA, prefers-reduced-motion, eyebrow text-brand-navy, IA Mistral card, section integrations, architecture, conformité-resume. Tests : 9/9 passent dans test_fonctionnalites_*. Assertions ajoutées pour les 4 catégories + 6 nouveaux modes + handleCategorySelect + 1100ms cycle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
323f0c81c4 |
fix(marketing): contraste/lisibilité 'Comment ça marche' — feature card + grid buttons WCAG AA
Audit complet : 2 incohérences fond/texte critiques sur fond CLAIR `bg-brand-bg`
(#f7f9fc) où des conteneurs `bg-white` à faible opacité rendaient le texte
quasi-invisible.
Fixes :
1. Bottom feature info card (sous le phone) — INVISIBLE
`bg-white/[0.06] border-white/[0.10]` sur section claire = blanc 6%/10%
sur near-white = card et bordure invisibles. Texte intérieur `text-white`
et `text-white/65` aussi invisibles.
→ `bg-brand-navy` SOLIDE + `border-white/10` (extension visuelle naturelle
du phone shell). Texte blanc maintenant lisible avec contraste WCAG AA.
2. Right panel feature grid 6 boutons — État ACTIF invisible
Quand actif, fond `${color}14` (8% opacité du color sur section claire)
= très light tint. Label `rgba(255,255,255,0.95)` sur fond clair tinté
= quasi-invisible. Idem pour inactif text à 0.65 — bordure subtile.
→ Fond TOUJOURS dark `rgba(8,12,24,0.85-0.95)` (actif/inactif), avec
différenciation via border + glow + scale + drop-shadow du color brand.
Label porté à 0.98/0.70 pour AA garanti.
Test adapté : assertion `bg-white/[0.06]` remplacée par
`dictia-feature-card rounded-xl px-4 py-3 relative bg-brand-navy`.
29/29 tests pertinents passent ; 5 échecs pré-existants sur /conformite et
/landing sans rapport avec /fonctionnalites.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
7d3348c3fd |
polish(marketing): refonte HYPER PRO 'Comment ça marche' — purge non-brand hex + uniformization typo/spacing
Refactor mécanique strict : - Purge 100% des hex non-brand (#0891b2, #a21caf, #e879f9, #1d4ed8, #9333ea, #f5d0fe, #67e8f9, #1e40af, #93c5fd, #9CA3AF, #0e7490, #EF4444 capital) → mapping vers brand-b1/b2/b3 - Standardisation tailles : 0 inline font-size, text-[9px/10px/11px] uniquement (purge 5.5/6/6.5/7/7.5/8/8.5 arbitraires) - 0 font-family inline (utilise font-sans/font-mono Tailwind) Polish device : - Inner screen seam (effet "écran encastré dans bezel") - Notch : intègre speaker grille 3 dots + camera dot dans la dynamic island - Status bar : vraie batterie 80% fill, vrai WiFi 3 arcs concentriques + dot, signal 4 bars croissantes - Logo DictIA 92×28 plus grand (opacité 85%) Polish modes : - Mode 1 : header compact mic+filename+REC, waveform 16 bars symétriques, file card MP3 redesign avec corner fold - Mode 2 : avatars empilés 18×18 avec bordure white/15, bubbles max-width 80%, timestamps text-[9px] - Mode 3 : grille langues text-[10px] line-height 18px, padding 8px, palette stricte b1/b2/b3 - Mode 4 : grid 4×2 cards 42×50, drop staggered 90ms, palette stricte b1/b2/b3 + dc2626 PDF + 374151 TXT - Mode 5 : header counter Inter font-black text-base, connecting lines opacité 0.18 - Mode 6 : breadcrumb compact, toolbar 4 icons, hover row highlight, palette b1/b2/b3 - Mode 0 : chat bubbles uniformisés text-[10px], footer shield emerald (sécurité) Polish right panel IA : - Brain 40×40 cercle gradient brand-b3 (déjà OK) - Badges Mistral 7B (b3 bg) + LOCAL (emerald bg) - 3 metrics : 0ms grad-text · 100% emerald · 24/7 grad-text font-black text-lg - Sovereignty bullets : icon dans cercle 20×20 rounded-full bg-brand-b3/[0.15] - Padding p-5 généreux Polish feature info card sous phone : - Background uniforme bg-white/[0.06] + border-white/[0.10] - Border-left 3px accent activeColor (style tab indicator) - Icon container 32×32 rounded-md - Badge top-right text-[10px] tracking-wider Polish bottom tab bar : - Buttons 34×42, gap-1 serré - Active : bottom border 2px + scale icon 1.15 + drop-shadow color - Labels text-[9px] uppercase tracking-wider - AUTO pill : px-2.5 py-0.5 rounded-full bg-emerald/12 Tests : - +6 assertions polish (forbidden hex purge, screen seam, white/0.06, brand-b3/[0.15], grad-text) - 9/9 fonctionnalites tests pass - 29/29 marketing tests pass (2 conformite failures pré-existantes baseline) Build : npm run build:css → static/css/marketing.css régénéré pour les nouvelles classes arbitraires Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
d6ff71640a |
fix(marketing): phone shell border/shadow/glow STATIC (ne morphe plus entre modes)
Le perception "le contour change" venait de 4 propriétés tintées par activeColor
sur le phone-shell : border (${activeColor}40), box-shadow (${activeColor}30),
ambient overlay (${activeColor}08), external glow ring (${activeColor}28).
À chaque switch de mode, ces 4 valeurs s'animaient en couleur → halo qui morphe
visuellement même si width/height restent fixes.
Fix : verrouille border/shadow/glow ring/ambient à des valeurs STATIQUES
(white/[0.10] border, brand-b1 0.20 shadow halo, white/[0.015] ambient,
brand-b1 0.18 glow ring) — comme un vrai téléphone, le bezel ne change
JAMAIS de couleur. Seul le contenu interne (modes, feature info card,
boutons feature) reste tinté par activeColor.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
199b315bc2 |
fix(marketing): phone container width stable + palette brand canonique (b1/b2/b3) sur Comment ça marche
- Phone shell hauteur/largeur FIXES (280×580px) — bezel ne reflow plus selon le mode actif. Zone TOP 96px / MIDDLE 374px overflow:hidden / BOTTOM 90px. Chaque mode a maintenant w-full h-full overflow-hidden. - Modes 1/2/3/6/0 : scroll interne invisible (.dictia-mode-scroll + fade gradient bottom .dictia-fade-bottom) pour contenu long. - Suppression mécanique des hex non-brand : #A78BFA, #22D3EE, #6B9FFF, #34D399, #F59E0B, #1E6FD9, #7C3AED, #5B21B6, #065F46, #1C3A5E, #D93E1E, #C4B5FD, #DDD6FE, etc. — remplacés par brand-b1 (#2563eb), brand-b2 (#06b6d4), brand-b3 (#c026d3) et leurs déclinaisons (#0891b2, #1d4ed8, #a21caf, #1e40af, #0e7490, #9333ea, #e879f9). - FEATURES / CONVO / LANG_COLORS / USER_COLORS / FILE_TYPES réalignés sur palette officielle. Sophie=b2, Marc=b1, Julie=b3. - Status sémantiques conservés : #EF4444 (REC dot), #10b981 (online status / 7 FORMATS PRÊTS / LOCAL badge), #dc2626 (PDF file icon). - font-family:monospace inline → font-mono Tailwind (JetBrains Mono). - Mobile : phone reste à 280px fixe, scale 0.92 en dessous de 320px. - Eyebrow + connecting line gradient SVG en couleurs brand. Tests : 9/9 fonctionnalites passent. 2 échecs préexistants sur /conformite (SOC 2 hedge / AGPL section) sans rapport avec ce fix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
5edaddd788 |
polish(marketing): refonte HYPER PRO 'Comment ça marche' — bezel iPhone, status bar, sound waves, modes enrichis (REC, waveform, typing, ripple, breadcrumb), IA card 40px brain + metrics, connecting line
10 améliorations cinématiques sur la section interactive : PHONE FRAME RÉALISTE - Bezel double border + glow ring externe pulsant - Notch (Dynamic Island stylisé) + speaker grille + camera dot - Status bar mobile (9:41, signal/wifi/batterie SVG) - Drop shadow dramatique + inner shadow encastrée MIC + SOUND WAVES - 3 ripples concentriques émanant du mic (sound-ring keyframes) MODE 1 TRANSCRIPTION - Header REC indicator pulsant (rouge) + nom fichier - Waveform animée (12 bars hauteurs randomisées) - Barre progress double avec gradient + glow MODE 2 DIARISATION - Header conversation : 3 avatars empilés overlapping - Bubbles avec timestamps (09:0X) + box-shadow - Typing indicator (3 dots) entre messages MODE 3 LANGUES - Header DÉTECTION AUTOMATIQUE avec dot pulsant - Ripple wave depuis le centre (rippleDelay calculé par distance) - 5 langues highlight aléatoire glow brièvement - Counter live FR · EN · ES · ... + 99+ détectées MODE 4 EXPORTS - Grid 4×2 organisé (au lieu de wrap aléatoire) - Files détaillés avec mini-pages (3 lignes texte) - Subtitle '7 FORMATS PRÊTS' + checkmark vert MODE 5 USERS - Counter centré 01 → 20 (font-mono black) - 5 USER_COLORS variations (purple/cyan/green/blue/amber) - Connecting lines SVG pointillés vers centre MODE 6 SHARE - Breadcrumb 'Mes dossiers › Réunions' - Toolbar mini (search, filter, sort) - Rows structurées avec 3-dots action BOTTOM ZONE - 6 boutons agrandis (30+px) avec labels + tab indicator border 2px - AUTO pill (badge dot pulse) / Manuel countdown bar 4.5s CARD INFO - Icon container 32×32 + badge top-right + hover lift RIGHT PANEL IA - Brain dans cercle 40×40 gradient + glow - Metrics row : 0ms latence · 100% privé · 24/7 - Sovereignty bullets : icon dans rounded box GLOBAL - Section background : grid pattern + 2 floating orbs blur - Connecting line SVG cyan→violet phone↔IA panel - Header section : eyebrow gradient pill + stats row (6/99+/0) + CTA démo - Crossfade transitions modes (scale 0.96 → 1) ACCESSIBILITÉ - prefers-reduced-motion désactive toutes animations - @media (max-width: 767px) désactive éléments décoratifs CPU-intensive - aria-pressed, aria-live polite, focus-visible préservés - 30 nouveaux keyframes CSS scopés Tests : 1 existant + 1 enrichi (53 lignes, 22 nouvelles assertions) Tests pass : 30/30 sur how-it-works (les 2 fails conformite sont pré-existants). |
||
|
|
7aaedf2cdf |
refactor(marketing): reproduit fidèlement DashboardHolographique de dictai-narrative.tsx (6 modes uniques + auto-cycle 900ms)
Remplace la section "Comment ça marche" (réacteur orbital générique du commit
|
||
|
|
03f6e56f04 |
feat(marketing): section interactive 'Comment ça marche' (réacteur DictIA cyclant 6 features)
Ajoute une nouvelle section interactive sous les 6 fonctionnalités (préservées intégralement) reproduisant le composant React dictai-narrative.tsx en CSS pur + Alpine.js — sans Framer Motion ni autre lib JS. - Réacteur central holographique : 3 anneaux concentriques rotatifs (15 s / 22 s / 30 s) + 8 particules orbitales (cyan/blue/fuchsia) + wordmark DictIA glow pulsant - Auto-cycle Alpine.js entre 6 features (1.6 s) avec pause au hover/focus et reprise au leave/blur - Panneau feature active avec aria-live='polite' pour annonce lecteur d'écran (Transcription · Diarisation · 99+ langues · Exports · Utilisateurs illimités · Partage & Classement) - Card 'IA intégrée Mistral 7B LOCAL' avec 3 bullets souveraineté - Spec list cliquable / hover déclenchant feature dans réacteur - Layout responsive grid 2 cols desktop, stack mobile - prefers-reduced-motion désactive rings + orbites + auto-cycle - Position : APRÈS '6 fonctionnalités', AVANT 'Intégrations' - Sub-nav reste à 4 ancres (sous-partie visuelle de Fonctionnalités) - Tests : nouveau test_fonctionnalites_how_it_works_reactor_section valide structure, contenu canonique, a11y et Alpine bindings Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
e06cba2123 |
refactor(pricing): 3 Cloud en rangée + DictIA LOCAL bloc dédié 'Vous en êtes propriétaire'
Restructure _pricing_tiers.html : les 3 forfaits Cloud (Basic 189$/Essentiel 349$/Pro 549$ recommandé) sont maintenant en grid responsive 1/2/3 cols, et DictIA LOCAL est sorti de la grille principale pour devenir un bloc large dédié 'propriété' avec : - badge 'Au Québec · par InnovA AI' (SVG map-pin, sans emoji 🇨🇦) - H3 'Vous en êtes propriétaire.' avec grad-text - 5 bullets checkmark (PC+GPU RTX, 100 % local, assemblé QC, installation incluse, achat direct < 34 700 $) - CTA 'Voir les serveurs disponibles' → /contact?plan=dictia-local - mockup serveur à droite (SVG rack + 6 specs : Interface web, PC gaming, RTX 5070 Ti 16 Go, WhisperX+Mistral, DictIA pré-installé, Votre propriété) - pricing tagline visible '5 998 $ An 1 · 500 $/an dès An 2' - decorative orbs background (b1 + b3) pour distinguer du grid Cloud Aussi mis à jour /tarifs (H1 'Trois forfaits Cloud + DictIA LOCAL' au lieu de 'Quatre forfaits') et tests pour refléter le nouveau slug /contact?plan=dictia-local (au lieu du /checkout/dictia-local d'avant). Conserve V3 radii (rounded-none/rounded/rounded-full), palette brand (b1/b2/b3/navy), OQLF NBSP, ARIA WCAG, zéro emoji. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
1c4cafaf69 |
refactor(pricing): refonte v7.0 — 3 Cloud (Basic 189$/Essentiel 349$/Pro 549$) + DictIA Local (5998$ An1) + Pro+ soumission
Remplace l'ancien pricing (DictIA 8 / 16 / Cloud) par la nouvelle structure canonique v7.0 : 4 forfaits + 1 sentinel quote-only. Changements clés : - pricing_card.html : signature étendue (badge, recommended, capacity_audio, capacity_storage, gpu, yearly_renewal, cta_label) + format prix server-side avec NBSP OQLF (5998 -> 5 998 $) - _pricing_tiers.html : 4 cards (Cloud Basic 189$, Cloud Essentiel 349$, Cloud Pro 549$+485$ RECOMMANDÉ, DictIA Local 5998$ An1) + chip Pro+ soumission -> /contact?pro-plus=1 - plans.py : refonte complète avec yearly_renewal_env (DictIA Local An 2+ = 500$/an) + is_quote_only sentinel (Pro+ -> redirect /contact, jamais Stripe) - routes.py : Pro+ intercepté avant le flow Stripe Checkout - env.stripe.example : nouveau naming STRIPE_CLOUD_BASIC|ESSENTIEL|PRO_* + STRIPE_DICTIA_LOCAL_SETUP/RENEWAL_YEARLY - tarifs.html : header "Quatre forfaits", matrice comparative 4 colonnes, FAQ enrichie (7 questions incluant DictIA Local + onboarding Pro + Pro+) - fonctionnalites.html : section Architecture refondue (4 cards v7.0) - landing.html : ROI footnote + cycle "189$" + wave "189$/mois" actualisés - roi_calculator.js : recalibrage sur Cloud ESSENTIEL 349$ × 12 = 4188$/an - routes.py marketing : FAQ "DictIA 8 et 16" -> "DictIA LOCAL" - contact.html : "déploiements DictIA 16" -> "Cloud PRO" + "DictIA LOCAL" Tests : - test_marketing_landing_template.py : assertions prix v7.0 (189/349/549/5998), 4 slugs (cloud-basic, cloud-essentiel, cloud-pro, dictia-local), Pro+ chip, capacity chips, RECOMMANDÉ sur Cloud PRO - test_marketing_secondary_pages.py : 4 cards + Pro+ chip + matrice 4 col + FAQ 7 questions - test_stripe_checkout.py : env vars v7.0, slugs cloud-basic/cloud-pro/ dictia-local + nouveau test pro-plus -> /contact + tests setup pour Cloud PRO et DictIA Local - test_stripe_webhook.py : plan_slug metadata cloud-basic Status : 28/28 Stripe checkout + 17/17 webhook + 93/98 marketing pass (les 5 marketing failures sont pré-existantes, non liées au pricing : test_landing_has_main_nav et test_footer_links_complete = /blog manquant ; test_trust_bar_has_eyebrow_factual_phrasing + 2 tests conformite = casing eyebrow + entité é — vérifié par git stash baseline). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
e8c7e5cd43 |
refactor(marketing): Cycle cinematic PRO — palette brand uniformisée + USA map + Quebec outline + halo brand
Round 5 cinématique de la section "Trois options. Une seule est conforme." : - Palette brand uniformisée (blue/cyan/fuchsia) — col 3 passe de emerald/green à brand-b1+brand-b2 + accents fuchsia - Connecting horizontal beam progressive 0→33→66→100% entre les 3 colonnes (gris→rouge→cyan) - Spotlight active column (opacity 0.65 inactif, 1 actif + scale 1.02) - Col 1 : stack papiers stagger reveal 180ms + horloge 2.5s + counter \$315 - Col 2 : USA map silhouette subtle bg + server rack 3-leds + 12 paquets data .wav/.aac flying vers top-right + screen shake + flash red + chevrons + sound icon pulse - Col 3 : Quebec province outline subtle + mini logo DictIA + halo multi-couches blue+cyan+fuchsia + drawn ring SVG (fuchsia accent) + shield-with-microphone + checkmark cyan + badge Loi 25 conforme gradient brand + big 173 \$ en grad-text - Section Économies : counters en grad-text + save chips "+économies" + chips contextuels + icônes distinctives (loutre/Teams T/sténographe humain) - Phase delays cinématiques 400/1100/2000/3100ms + cubic-bezier overshoot - Vignette ambiante + grid pattern 40×40 + orbes décoratifs blur WCAG : aria-labels préservés + prefers-reduced-motion désactive radar/particles/screen-shake/halo/beam. Mobile : leak particles + orbes + halo heavy + screen shake désactivés via @media (max-width: 768px). Performance : will-change GPU hints sur stamp/halo/leak. Tests cycle round 4 (3) toujours passants. 65/68 tests pass (3 failures pré-existantes : /blog nav). HTTP 200 vérifié sur http://127.0.0.1:8899/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
575db5e342 |
feat(brand): logo officiel DictIA + palette blue/cyan/fuchsia (matche le logo)
Logos officiels installés : - static/images/dictia-logo.png (28 KB optimisé 256×256) - static/images/dictia-logo-128.png (10 KB retina) - static/images/dictia-logo-fullres.png (originaux conservés OG/social) - static/images/dictia-logo.svg + dictia-logo-nom.svg (cleaned C2PA metadata) - Header marketing/base.html : <img> 40×40 + wordmark "DictIA" + tagline "Transcription" - Footer marketing/_footer.html : <img> 36×36 + wordmark - Favicon mis à jour vers logo PNG Note : SVG sources sont des PNG base64 wrappés (pas de vrais paths) — PNG utilisé en production (8× plus léger), SVG conservé pour fallback. Palette canonique alignée sur le logo : - brand-b1 : #7c3aed (mauve) → #2563eb (blue-600 vibrant — primary) - brand-b2 : #a855f7 (mauve clair) → #06b6d4 (cyan-500 — aqua mid) - brand-b3 : #06b6d4 (aqua) → #c026d3 (fuchsia-600 — magenta accent) - Gradient signature : linear-gradient(118deg, #2563eb, #06b6d4 52%, #c026d3) - Box shadow CTA : rgba(37,99,235,0.28/0.42) - 72 remplacements hex/rgba dans 5 templates marketing/legal + email service Tests : 81 passed / 3 failed (3 échecs pré-existants /blog + trust-bar phrasing, non liés à ce changement). 0 régression. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
34d40162b3 |
refactor(brand): décale palette bleu→mauve dégradé (b1=#7c3aed, b2=#a855f7, b3=#06b6d4 aqua) pour différencier DictIA
Décale la palette canonique DictIA du bleu/cyan/vert vers mauve/violet/aqua
afin de distinguer visuellement le produit DictIA des autres outils InnovA AI
(qui restent sur palette bleue) tout en gardant la même structure de gradient,
mêmes tokens Tailwind, et même intensité visuelle.
Mapping appliqué:
- Hex semantic: #0062ff → #7c3aed (mauve), #00bdd8 → #a855f7 (lighter mauve)
- Hex aqua décoratif: #00c896 → #06b6d4 (cyan-500, aqua préservé)
- Hex secondaire: #6B9FFF / rgba(107,159,255,*) → #a78bfa (violet-400)
- Hex blue-700 #1d4ed8 (cadre reg label) → #7c3aed (mauve)
- Box shadows / rgba opacités: rgba(0,98,255,*) → rgba(124,58,237,*)
- Décoratif (orbes cosmiques, glows): mauve+aqua mix pour préserver l'effet
"cosmic dégradé" — orbe primaire en mauve, orbes secondaires en aqua
- Hub network DictIA: Documents=mauve, Communication=aqua, Automatisation
reste #8b5cf6 (déjà violet, marqueur visuel distinct via positionnement)
- Couleurs sémantiques fonctionnelles (red/green pour erreurs/succès, amber
pour alertes) inchangées
Fichiers modifiés:
- static/css/tailwind.config.js (brand.b1/b2/b3 + brand-grad + boxShadow.cta
+ boxShadow.cta-hover + keyframes.tc-pulse-glow)
- static/css/marketing.css (rebuild Tailwind: 169356 → 163036 bytes)
- templates/legal/{_layout,index}.html
- templates/marketing/{landing,fonctionnalites,conformite,tarifs}.html
- tests/test_marketing_landing_template.py (test_hero_has_cosmic_orbs_background
mis à jour avec assertions mauve/aqua au lieu de blue/cyan/green)
Hors scope (non touchés):
- Couleurs Tailwind utility (red/green/amber/emerald) sémantiques
- --brand-navy* (backgrounds dark restent neutres)
- Templates legacy (account.html, admin.html, components/, modals/)
- #8b5cf6 (Automatisation hub), #f59e0b (alertes), #ef4444 (erreurs)
Tests: 111 passed, 5 failed (toutes 5 pré-existantes, non liées aux couleurs:
/blog link manquant, MAPP eyebrow, SOC 2 phrasing, Gitea URL).
HTTP 200 vérifié sur /, /fonctionnalites, /tarifs, /conformite, /legal/,
/legal/conditions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
680df39089 |
feat(marketing): round 4 — Cadre + Cycle cinématiques (radar, data packet flight, stamp impact, savings counter)
Round 4 transforme les 2 sections "Cadre réglementaire" en expériences cinématiques : CADRE (Moniteur d'Interception) - Radar sweep circulaire vert continu en background HUD (4s loop, SVG + @keyframes) - 6 paquets data "voice.wav" en flight QC→US via offset-path bezier (stagger 420ms, glow rouge) - Console typewriter char-by-char 3 lignes (28ms/char + caret blink, 3e ligne rouge glow) - 6 REGS reveal cascadé via revealRegsCascade (stagger 120ms) + hover red glow + border-left - Verdict NON CONFORME : pulse glow rouge + scan-line traversante 3s - Decorative grid 40×40 console-style + grid existant 20×20 - Eyebrow ⚠ remplacé par SVG warning-triangle inline CYCLE (Trois options) - Phase reveal 1→4 séquentiel (déjà existant) avec animations renforcées - Col 1 horloge accélérée 1 tour/3s (au lieu de 8s) - Col 1 prix counter Alpine 0→315 (easeOutCubic 1.4s) via priceHumain + countTo - Col 2 stamp NON CONFORME impact (rotate -22→-3deg + scale 2.4→1, cubic-bezier 1.6 ease) - Col 2 flash rouge background à l'impact (cycle-col-flash) + 10 particules de fuite (au lieu de 6) - Col 3 checkmark draw via stroke-dashoffset 24→0 - Col 3 glow border vert pulsant (cycle-conforme-glow, double-couche emerald + cyan) - Col 3 badge "Loi 25 conforme" top-right avec pulse subtil (cycle-conforme-badge) - Connecting lines avec dash flow continu (cycle-line-flow @keyframes) - Live red dot "Réunion en cours" avec pulse box-shadow - Section "Économies annuelles · 25 utilisateurs" : 3 cards avec counter Alpine (sav1=3924, sav2=6924, sav3=2004) + hover lift + emerald shadow - Eyebrow ⚠ remplacé par SVG warning-triangle Accessibilité & performance - prefers-reduced-motion désactive TOUT (radar, packets, typewriter, stamp, glow, counter) - Mobile (<768px) cache radar + packets + leak particles (CPU-intensive) - Counter helper countTo respecte reduced-motion via matchMedia - Tous les SVG ont aria-hidden, scènes ont role=img/listitem appropriés - HUD console role=log + aria-live=polite - OQLF NBSP préservé (315 $/réunion, Loi 25, 100 % Québec, 25 utilisateurs, 3 924 $) Tests : 4 tests round 4 ajoutés (cadre cinematic, cycle cinematic, no-emoji warning, reduced-motion guards). 65/68 landing tests passent (3 failures pré-existantes unrelated : nav /blog, footer /blog, trust-bar phrasing). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
529bd2263b |
feat(marketing): round 3 — hero remplacé par 3-step flow canonique + 99+ langues + Cégeps spotlight + CyberPerformance + FAQ enrichie
Hero (templates/marketing/landing.html) : - Reproduction fidèle de dictia.ca/solutions/dictai (source : Website-Sanity/components/sections/dictai-page-content.tsx lignes 260-518) - REMPLACE le mockup app DictIA par le 3-step flow inline canonique (Importez → Texte 2 min → Résumé + actions) - Wordmark large « DictIA » (style production) + H2 cyan « Transcription IA locale en 2 minutes — conforme Barreau, CPA Québec et ChAD » - Sub canonique référençant OVH Beauharnois, Cadre IA MCN, 5 ordres à directives IA formelles - Stats grid (4 col) : ~2 min · 5 ordres · 95 %+ · 0 $ (NBSP OQLF) - Eyebrow back-link « Toutes les solutions » - 5 animations Framer Motion → CSS pure + Alpine.js : 1. 3-step flow auto-cycle 1→2→3 (setInterval 1.8 s, désactivé reduced-motion) 2. Magnetic CTA primary (mousemove → translate max 8 px) 3. Mouse parallax orb 3D (mousemove window → CSS transition) 4. Shockwave on click (CSS pseudo-element scale 0→4 + opacity) 5. Word-staggered title reveal (Dict + IA via animation-delay) Sections enrichies / ajoutées : - Pipeline : sous-titre « Du fichier au résumé — en temps réel » + hint canonique - NEW « 99+ langues détectées » + carte « IA Mistral 7B (LOCAL) » 4 bullets - Pricing : sous-titres canoniques par forfait + note « Tous les prix en CAD, taxes en sus (TPS 5 % + TVQ 9,975 %) » - Conformité : 3 chips claims (~192 000 pros · 5 ordres · 0 donnée hors-Québec) + phrase secteurs réglementés - NEW Cégeps spotlight « Conformité au 19 juin 2026 » avec Cadre IA MCN détaillé (7 bullets, 9 chips organismes, badge pulse glow) - NEW Partenaire CyberPerformance (card horizontale + lien externe) - FAQ : enrichie de 7 → 10 questions canoniques sourcées de dictai-page-content.tsx (Teams Copilot, Otter.ai, Barreau, Clio Manage, etc.) - CTA final : « Prêt à protéger vos données ? » + bouton « Réserver ma démo gratuite » (préserve mailto pré-inscription) Tests : - Ajout tests/conftest.py (stub fcntl POSIX + env vars test) pour permettre exécution sur Windows - Mise à jour 8 assertions liées au nouveau hero, FAQ 10 Q, CTA renforcé, NBSP OQLF dans eyebrow - 61 passed / 3 pré-existant échecs baseline (/blog dans nav + footer + trust-bar phrasing) Contraintes respectées : zéro JS externe, aucun emoji (SVG inline aria-hidden), V3 radii (rounded/rounded-full), brand tokens, OQLF NBSP partout, WCAG (aria-labels, focus-visible, prefers-reduced-motion désactive toutes les animations hero). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
69baa1be2f |
feat(marketing): round 2 — intègre 3 sections de dictia.ca/solutions/dictai (cycle/wave/cadre)
- Cycle "Trois options. Une seule est conforme." (entre PAS Problème & Solution) 3 colonnes comparatives (humaine 315$/h / cloud US illégal / DictIA 173$/mo) Phases reveal 1→4 via IntersectionObserver + setTimeout chain Anneaux pulsants source node + horloge rotation + particules fuites cloud Overlay légal NON CONFORME sur col 2 - Wave "Onde de transformation" (entre Solution & Pipeline) Slider mouse-X interactif : 30 barres SVG morphent rouge → cyan Particules tombantes -$/-h (CSS keyframes staggered) Étiquettes douleur PAINS / SOLUTIONS flottantes Mobile : toggle button, pas de mouse interaction - Cadre réglementaire "Moniteur d'Interception" (entre Conformité & Témoignages) Mappe 6 textes officiels : Loi 25, Loi 96, Cloud Act US, Guide IA Barreau, Cadre IA MCN, CAI Liens vers sources autoritaires (legisquebec, congress.gov, barreau, tresor, cai) HUD console typing reveal + caret blink + folder QC→US transition aria-live="polite" sur verdict, role="list" sur REGS Texte 100% canonique extrait de Website-Sanity dictai-cycle/wave/contraste.tsx. Toutes animations CSS pure + Alpine.js + IntersectionObserver natif (zéro lib JS externe). prefers-reduced-motion désactive tout. +802 lignes landing.html, +119 lignes tests (6 nouveaux test_round2_*), npm run build:css exécuté. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
e49652d85d |
feat(marketing): intègre pipeline 4 étapes + réseau hub d'intégrations depuis dictia.ca/solutions/dictai
- Pipeline (entre Solution & Bento) : Upload → GPU WhisperX → IA Mistral → Export Auto-advance Alpine 2400ms, sweep ring SVG CSS, dot glow, prefers-reduced-motion - Hub (entre Bento & Pricing) : DictIA → 3 hubs → 9 outils SVG natif <animateMotion> sur bezier paths, zéro lib JS, fallback liste 3-col WCAG - Texte 100% canonique extrait de Website-Sanity dictai-pipeline.tsx + dictai-hub.tsx - OQLF NBSP : "1 heure d'audio → 2 minutes", "5 000+ apps", "100 % en local" - WCAG : aria-labelledby sections, role=tab/list, focus-visible, prefers-reduced-motion - +397 lignes, npm run build:css exécuté pour utilities cyan/purple/opacity Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
aad37f8566 |
feat(marketing): hero 2-col avec mockup app DictIA animé interactif
Refonte de la section hero: passage d'un layout centré single-col
à un grid 2 colonnes (texte gauche + mockup app à droite) sur lg+,
avec préservation du centrage actuel sur mobile/tablette.
- Mockup ~560×500px reproduit l'interface DictIA réelle:
- Window chrome (3 dots traffic light + tab "DictIA — Enquêter")
- Sidebar: 6 enregistrements groupés (Semaine dernière 2 + Mois
dernier 4) avec chips colorés (En cours, Barreau Confidentiel,
CPA Corporatif, Urgent Client) + bouton + recording rounded-none
- Center: header card avec 4 metas (avatars/calendar/clock/file),
audio player avec progress bar animée 50%-75% (15s loop),
transcript 5 lignes speaker (Allison/SPEAKER_02) où la ligne
active cycle toutes les 2.8s via Alpine x-data idx
- Right: tabs Résumé/Notes/Discuter (Résumé actif), résumé
exemple + 4 points clés
- Tilt subtil rotate-1 → straighten + scale au hover (lg only)
- 2 glow orbs flottants décoratifs derrière (bg-brand-b1/15 +
bg-brand-b3/10) avec tc-float-y reverse
- role="img" + aria-label descriptif sur le mockup complet
- prefers-reduced-motion désactive toutes animations + freeze
progress bar à 60% + retire transform tilt
- Tous les éléments interactifs ont tabindex="-1" + aria-hidden
car purement décoratifs (pas de duplication d'app réelle)
- Aucun emoji (SVG inline stroke="currentColor" partout)
- Système border-radius respecté: rounded-none (boutons/inputs/
tuiles), rounded (4px wrapper card), rounded-full (chips/avatar)
Tests: 6/6 hero tests pass (eyebrow, h1+grad-text, dual CTA,
cosmic orbs, social proof, animations staggered). Les 3 fails
restants (test_landing_has_main_nav, test_footer_links_complete,
test_trust_bar_has_eyebrow_factual_phrasing) sont préexistants
et sans rapport avec la refonte hero.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
3e56736fa7 |
feat(marketing): refonte fonctionnalites avec contenu canonique + animations modernes subtiles
7 sections (hero stats counter, sticky sub-nav, 6 fonctionnalités bento avec chips specs, 3 sous-groupes intégrations, tableau architecture 3 tiers, conformité résumée + lien /conformite, CTA final). Contenu canonique extrait du site prod (WhisperX Large-v3, pyannote-audio, Mistral 7B, RAG sentence-transformers, 8 locuteurs, 30× temps réel, 95%+ FR-CA, prix 3 450/5 750/369 $). Animations: counter rAF easeOutCubic via Alpine + IntersectionObserver, fade-in stagger via data-ani-fade, animated underline H2, hover lift cards, sticky sub-nav avec active highlight, cosmic orbs flottantes, pulse glow sur card recommandée. Toutes les animations respectent prefers-reduced-motion via media query inline. Conserve les sections exports/specs/integrations grid pour rétro-compat tests. 13 assertions pytest fonctionnalites passent (les 2 failures conformite sont pré-existantes sur Windows — mojibake console, non liées à cette refonte). |
||
|
|
48d65c2ab9 |
refactor(ui): épurer les 3 sections — pas de backdrop tiles, icônes brand-b1 + watermark grad-text
- Solution pillars (3 cards) : retirer le bloc icône — ne garder que h3 + p - Bento macro : supprimer la tuile grad-bg, rendre l'icône directement en text-brand-b1, watermark passe à grad-text opacity-20 (famille bleu marque, plus visible que white/[0.04]) - Conformité forteresse (4 cards) : supprimer la tuile grad-bg, rendre l'icône en text-brand-b1 - Bumper toutes les icônes bento (landing + fonctionnalites + default macro) et conformité de w-5 h-5 → w-7 h-7 maintenant qu'elles n'ont plus de backdrop - Mettre à jour test_bento_uses_flexihub_styling pour refléter la nouvelle structure (grad-text opacity-20 + text-brand-b1 mb-4 au lieu de white/[0.04] + grad-bg rounded-none) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
8d50d8ee01 |
refactor(ui): éliminer tous les emojis (SVG inline + texte propre, look pro/moderne)
Pass de modernisation visuelle : remplacement de TOUS les emojis Unicode dans les templates marketing/legal/billing/auth par des SVG inline (style heroicons) ou par du texte propre, pour un look SaaS pro à la Linear/Vercel/Stripe. Mapping principal : - ✓ / ✗ / ⚠ → SVG check / x / triangle (text-brand-b3 / red / amber) - → / ← / ↗ → SVG arrow-right / arrow-left / arrow-up-right - 🍁 / 🏛️ / ⚖️ / 🔓 → SVG map-pin / building / scale / code-brackets - 🎙️ / 👥 / 📝 / 💬 / 📄 / 🔌 → 6 SVG bento icons (microphone, users, doc, chat, export, plug) - ✉️ / ☎️ / 📬 → SVG envelope / phone / map-pin - ↺ → SVG refresh-counter-clockwise - ★ → SVG star (RECOMMANDÉ badge) - 🎯/🏢/📺/🤝/📰 → SVG target / office / play / handshake / news (raccourcis contact) - ⚖️/📊/🏛️ (testimonials) → SVG scale / bar-chart / building - ✦ (default bento icon) → SVG sparkle inline Tous les SVG utilisent stroke="currentColor" pour héritage Tailwind text-*. Les SVG informationnels du tableau comparatif portent un aria-label sémantique (Conforme/Non conforme/Partiel) ; les SVG décoratifs portent aria-hidden. Tests : - 18/18 legal pages passent (test_legal_pages.py) - test_comparatif_check_marks_consistently_mean_good ajusté pour asserter sur les aria-label SVG plutôt que les caractères ✓/✗ - 4 échecs pré-existants non liés (manque /blog dans nav, SOC 2 hedge dans conformite.html, gitea.innova-ai.ca url) — confirmés présents avant ce commit Fichiers modifiés (14) : - templates/macros/{bento,pricing_card}.html (sources de vérité) - templates/marketing/{base,_footer,landing,fonctionnalites,tarifs,conformite,contact}.html - templates/legal/{_layout,index}.html - templates/billing/{cancel,success}.html - tests/test_marketing_landing_template.py (assert sur aria-label) Audit final : 0 emoji restant dans les fichiers in-scope ; 0 emoji dans le HTML rendu de toutes les pages marketing/legal vérifiées. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f83fdfcd68 |
refactor(ui): V3 fully square buttons + inputs (rounded-none, brutalist/Swiss aesthetic)
V3 finalizes the radii pass to a fully brutalist/Swiss visual language: - Buttons (CTAs, submit, secondary, ghost, OAuth provider tiles): rounded-none (0px) - Form inputs (text/email/password/select/textarea/code-entry): rounded-none (0px) - Checkboxes: rounded-none (0px) — was rounded-sm - Small icon tiles (w-10 h-10 / w-12 h-12 grad-bg squares): rounded-none (0px) - Inline code blocks (totp recovery <pre>, secret <code>): rounded-none (0px) - Cards (pricing, bento, content panels, modals, prev/next nav): rounded (4px) — was rounded-lg - Alert / flash boxes: rounded (4px) — was rounded-lg - Pills, badges, status chips, ordres pros avatars, decorative cosmic orbs: rounded-full preserved - Legal _layout.html inline <style> blockquote/pre/code/draft-callout: border-radius 0 — was 4px Updated tests/test_marketing_landing_template.py assertions: - bento icon assertion: "grad-bg rounded " -> "grad-bg rounded-none " - pricing recommended frame: "rounded-lg" -> "rounded" (with strict trailing-char match to avoid rounded-none false positive) Verification: 18/18 legal tests pass, 58/58 marketing landing tests pass, 5/5 root redirect tests pass. Two pre-existing failures in test_marketing_secondary_pages (SOC 2 hedge text + gitea.innova-ai.ca URL) are unrelated to this radii pass. |
||
|
|
0b91294c45 |
refactor(ui): sharpen buttons + inputs to rounded (4px) for modern minimal SaaS look
V2 sharper radii system aligned with Stripe Dashboard / Linear / Vercel
aesthetic — the v1 rounded-lg (8px) on buttons still felt too soft.
New scale:
- Buttons (CTA, submit, ghost, secondary): rounded-lg → rounded (4px)
- Form inputs (text/email/password/select/textarea): rounded-md → rounded (4px)
- Checkboxes: rounded-sm (2px) added explicitly to consent + remember-me
- Cards (pricing, bento, content panels): rounded-xl → rounded-lg (8px)
- Small icon tiles (w-10 h-10 / w-12 h-12 grad-bg squares): rounded-md → rounded
- Pills, badges, avatars, status orbs: rounded-full (KEPT)
- Inline code in legal CSS: rounded (4px) (KEPT)
- Legal blockquote/pre/draft-callout border-radius: 8px → 4px
Files modified (24):
- templates/macros/{button,bento,pricing_card}.html
- templates/marketing/{landing,tarifs,fonctionnalites,conformite,contact}.html
- templates/auth/{check_email,forgot_password,magic_link_request,
oauth_finish_signup,passkey_setup,reset_password,totp_setup,totp_verify,
verify_success}.html
- templates/billing/{success,cancel}.html
- templates/legal/{_layout,index}.html
- templates/{register,login}.html
- tests/test_marketing_landing_template.py (assertions updated to match v2)
Verification:
- 18/18 legal page tests pass (tests/_run_legal_pages_windows.py)
- 58/58 marketing landing tests pass
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
48ff4e70e6 |
refactor(ui): modernize button + input border radii (rounded-lg/md/xl, sharper SaaS aesthetic)
Aligns DictIA marketing/auth/legal/billing templates with modern SaaS visual conventions (Linear, Vercel, Stripe Dashboard, Notion). Old radii (12-18px) felt dated; new system uses 6-12px for tighter, more contemporary corners. Border radius system: - Buttons (CTA, submit, secondary): rounded-[0.75rem] (12px) -> rounded-lg (8px) - Form inputs (text/email/password/select/textarea/checkbox): rounded-[0.5rem] -> rounded-md (6px) - Cards (pricing, bento, modals, content panels): rounded-[18px]/[14px]/[12px] -> rounded-xl (12px) - Pricing card outer gradient frame: rounded-[20px] -> rounded-xl (matches inner) - Pills / badges / status chips: KEEP rounded-full - Avatars / circular icon containers: KEEP rounded-full - Code blocks: KEEP rounded (4px) Decision tree applied for ambiguous cases: - Button-like clickable CTA -> rounded-lg - Form input -> rounded-md - Card / panel / modal -> rounded-xl - Badge / pill / chip / avatar -> rounded-full (preserved) In-scope templates modified (23): - macros/button.html (central macro, cascades to all callers) - macros/pricing_card.html, macros/bento.html - marketing/landing.html, tarifs.html, fonctionnalites.html, conformite.html, contact.html - auth/check_email.html, forgot_password.html, magic_link_request.html, oauth_finish_signup.html, passkey_setup.html, reset_password.html, totp_setup.html, totp_verify.html, verify_success.html - billing/cancel.html, billing/success.html - legal/_layout.html, legal/index.html - register.html, login.html Out of scope (untouched): index.html, account.html, admin.html, share.html, inquire.html, group-admin.html, components/**, includes/**, modals/** (all legacy Speakr Vue surfaces). Tests: test_marketing_landing_template.py — 2 assertions updated to match new bento icon (rounded-md) and pricing card frame (rounded-xl). All 18 legal tests + 58 marketing landing tests + 9 signup_loi25 tests still pass. Decorative rounded-full preserved on hero cosmic orbs and ordres-pros avatar circles. Diff: 94 insertions / 94 deletions (1:1 mechanical replacement, no class drift). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
924d127ab4 |
feat(legal): polished UX for 5 legal pages + AGPL external link (sticky TOC, prev/next, breadcrumb)
Refonte visuelle et accessibilité (WCAG 2.2 AA) de la section /legal/ sans toucher au contenu juridique signé ( |
||
|
|
dc4ac9754b |
fix(legal): conform site to signed master legal documents (PDC, CGU, EFVP, DPA)
Audit conducted 2026-04-27 against signed PDFs in DOCS_DictIA/. All 6 legal
markdown files + 3 marketing templates aligned on the contractual ground truth
(documents signed by Allison Rioux + Jean-David Lévesque-Rioux 9 mars 2026).
CRITICAL DISCREPANCIES FIXED (D1-D9 — Loi 25 / contractual)
D1. Entity identity: removed false "filiale d'InnovA AI S.E.N.C." claim.
Canonical (PDC §1.1, CGU §1, RPRP doc): DictIA Inc. is a standalone SPA
constituted 22 mars 2026 (LSAQ), 50/50 owned by Allison Rioux + Jean-David
Lévesque-Rioux. NOT a subsidiary of InnovA AI.
D2. NEQ: replaced placeholder with canonical NEQ 1181949562 (DictIA Inc.).
D3. Sub-processors list: PDC §6.2 declares 5 sub-processors. Site listed only
OVH, Stripe, Resend (the latter two not in canonical). Now aligned:
OVH Beauharnois QC + GCP Toronto ON (RAM-only, 5min) + Cloudflare US (CDN)
+ HubSpot US (CRM) + Stripe US (paiements). Resend removed.
D4. GCP Toronto disclosure: NEW. PDC §6.2, §11.2, EFVP_GCP all declare GPU
processing on GCP Toronto Ontario as a transfer hors-Québec under art. 17
LSP. Site previously claimed "100 % au Québec" without GCP disclosure.
Now declared in confidentialite.md §6, §7 + conditions.md §2.4, §9 +
conformite.html pillar.
D5. Biometrics: NEW dedicated section. PDC §12, CGU §6, EFVP_BIOVOCAL all
require disclosure of voice biometrics (pyannote.audio embeddings) per
LCCJTI art. 44-45 + CAI declaration K1. Site had ZERO mention. Now
documented in confidentialite.md §12 + conditions.md §8.
D6. Wrong article number: landing.html cited "art. 60.1 LPRPSP" for biometric
sanctions — that article does NOT exist. Replaced with canonical citation:
"art. 44-45 LCCJTI + art. 12 LSP".
D7. Speakr fork attribution: CGU §13.1.1 explicitly requires the AGPL §13
disclosure URL to be gitea.dictia.ca (not gitea.innova-ai.ca). Mentions.md
+ conformite.html + footer normalized.
D8. Conservation periods: aligned to canonical CGU §8.1.2 + PDC §7.2.
Audio: 30 jours par défaut (extensible 12 mois opt-in) — was "indéfinie".
Biométrie inter-sessions: max 12 mois — était absent.
Facturation: 7 ans — était "6 ans".
Sauvegardes: 30 jours OVH QC.
D9. RPRP contact: confirmed canonical rprp@dictia.ca (per PDC §1.2 + RPRP
designation §1.3) — was already correct on site, kept as-is.
MEDIUM (M1-M3)
M1. Cookies categories: aligned to PDC §5.1 (5 categories: essentiels +
Cloudflare + perf + fonctionnels + HubSpot). Removed "Plausible Analytics
auto-hébergé" claim (not in any signed doc).
M2. DPA status: noted as "signed" for OVH + HubSpot (signed PDFs verified),
"in vigueur" for Stripe.
M3. Footer mentions légales link: added (was missing).
MINOR (N1-N2)
N1. Stripe entity: "Stripe Inc., San Francisco CA" (canonical PDC §2.6),
not "Stripe Payments Canada Ltd." (which doesn't appear in any signed doc).
N2. Engagement de non-entraînement IA: added to conditions.md §10 (canonical
CGU §10).
NOT MODIFIED (per scope boundaries)
- src/api/auth.py, src/billing/*.py, src/models/*.py — code not touched.
- templates/marketing/{tarifs,fonctionnalites}.html — frontend A-2.x final.
- landing.html — only minimal art. 60.1 → art. 44-45 fix (factual law error).
PENDING ALLISON REVIEW
- landing.html line 167-174 marketing claim "Vos données ne sortent jamais
de vos murs ou nos serveurs OVH Beauharnois" is technically inaccurate for
DictIA Cloud users (audio briefly transits to GCP Toronto for GPU processing,
RAM-only, 5min, zero persistence — encadré par EFVP signée). Decision
required: rephrase OR add asterisk pointing to /conformite for Cloud
architecture caveat.
- CAI form (CAI_FO_Declaration_Biometrie_DictIA_COMPLET_signé.pdf) declares
90 jours retention for inter-sessions vectors, while PDC + CGU + EFVP
all say 12 mois. Site uses 12 mois (latest, contractual). Allison should
verify CAI form needs amendment before submission.
TESTS
9/9 test_legal_pages.py passing (added biometrics + decisions automatisees
to required_topics; corrected "transfert hors-québec" → "transferts hors
québec" to match canonical PDC §11 OQLF wording).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
e1e31b51fd |
fix(legal): B-2.9 Loi 25 accuracy — audio retention truth + OQLF + LPRPSP cite
L1 (Loi 25 art. 11 + 23): audio retention row in confidentialite.md now matches the actual code default (ENABLE_AUTO_DELETION=false, GLOBAL_RETENTION_DAYS=0). Previous wording falsely claimed audio was auto-deleted at end of transcription; truth is conservation indéfinie until manual deletion or admin-configured retention policy. L3 (OQLF): replaced English "DRAFT v1.0 — pending legal review by Allison Rioux" with French "BROUILLON v1.0 — en attente de revue juridique par Allison Rioux" in DRAFT callout of all 6 legal pages (conditions, confidentialite, cookies, remboursement, accessibilite, mentions). Required for OQLF compliance on a Quebec-public site. L5 (LPRPSP cite): replaced shorthand "article 32 de la Loi 25" with the precise citation "article 32 de la Loi sur la protection des renseignements personnels dans le secteur privé (LPRPSP, RLRQ c. P-39.1, telle que modifiée par la Loi 25)" — the exact form CAI uses in its own correspondence. All 9 legal page tests still pass (test_legal_pages_have_loi25_draft_callout matches on either 'draft' or 'allison rioux'; both still present after L3). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
55569366f4 |
feat(legal): B-2.9 6 pages légales (CGU, Loi 25, cookies, remboursement, accessibilité, mentions)
- src/legal/__init__.py: define canonical LEGAL_VERSION='2026-04-27' constant (single source of truth — auth.py now imports it as SIGNUP_LEGAL_VERSION). - src/legal/routes.py: add /legal/<page> + /legal/ index routes; markdown rendered from src/legal/content/*.md with toc, tables, fenced_code, attr_list extensions. - src/legal/content/: 6 French (Québec) markdown documents — DictIA Inc. / InnovA AI S.E.N.C. branding, Loi 25-compliant 12-section privacy policy, WCAG 2.2 AA accessibility statement, AGPL-3.0 attribution. All marked DRAFT v1.0 pending legal review by Allison Rioux. - templates/legal/_layout.html + index.html: extends marketing/base.html; inline .legal-content typographic styles (no CSS rebuild required). - .gitignore: allow-rule for src/legal/content/*.md so markdown is tracked despite the global *.md ignore. - tests/test_legal_pages.py: 9 tests covering 200 responses, DictIA branding, rprp@dictia.ca presence, 12 mandatory Loi 25 sections, public indexability (no X-Robots-Tag noindex), shared layout, marketing/base.html extension, DRAFT callout, and LEGAL_VERSION/SIGNUP_LEGAL_VERSION equivalence. - tests/_run_legal_pages_windows.py: manual driver (Windows fcntl stub). - static/css/marketing.css: regenerated by `npm run build:css` to include new utility classes referenced from templates/legal/*.html. Tests: 9/9 pass. No off-limits files modified beyond the 2-line auth.py constant move spec'd in B-2.9. No schema changes; markdown==3.5.1 already pinned in requirements.txt (B-1.1). Pages publicly indexable by design (Loi 25 transparency). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
64738bfd1f |
feat(billing): B-2.8 Stripe webhook handler (subscription lifecycle + idempotency)
Endpoint: POST /checkout/webhooks/stripe (CSRF-exempt; signature-verified) Handles 5 Stripe events: - checkout.session.completed -> create Subscription, activate user - customer.subscription.updated -> sync status + current_period_end - customer.subscription.deleted -> mark canceled - invoice.payment_succeeded -> recover from past_due if applicable - invoice.payment_failed -> mark past_due Idempotency via WebhookEvent table (Stripe ID dedup) and Subscription unique constraint on stripe_subscription_id (defends against duplicate deliveries with distinct event IDs). User resolution prefers stripe_customer_id (server-set, anti-tamper) over event metadata.dictia_user_id over customer_email (per B-2.7 review note). New tables created via db.create_all(): - subscription (FK user.id ondelete=SET NULL for Loi 25 art. 28.1) - webhook_event (idempotency ledger) CSRF exemption wired via src/billing/exempt_webhook_csrf(csrf) called from src/app.py after billing_bp registration. Tests: 17/17 pass via tests/_run_stripe_webhook_windows.py. Existing 25 B-2.7 + 21 TOTP + 22 WebAuthn + 21 OAuth + 16 email tests unaffected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f1a5ad565f |
feat(billing): B-2.7 Stripe Checkout 3 plans CAD/TVQ + Apple/Google Pay
Adds the customer-facing checkout flow under /checkout/<plan>:
- src/billing/plans.py — Plan dataclass + 3 plans (DictIA 8 / 16 / Cloud),
monthly + yearly Price IDs resolved from STRIPE_DICTIA_*_{SETUP,MONTHLY,YEARLY} env.
- src/billing/stripe_client.py — lazy stripe.api_key init, get_or_create_customer
(persists user.stripe_customer_id), create_checkout_session with mode=subscription,
currency=cad, automatic_tax=true (TPS 5% + TVQ 9.975%), billing_address_collection,
metadata on both Session and Subscription for the B-2.8 webhook.
- src/billing/routes.py — GET /checkout/<plan>?period=monthly|yearly returns 303
redirect to Stripe-hosted Checkout. Friendly French flash + redirect to /tarifs
on unknown plan, missing STRIPE_SECRET_KEY, missing Price IDs, or Stripe API error.
GET /checkout/success and /checkout/cancel render brand-tokenized templates that
extend marketing/base.html.
- templates/billing/{success,cancel}.html — explicit "activé sous quelques minutes"
note (webhook is async), aucun montant prélevé reassurance on cancel.
- config/env.stripe.example — env vars + Stripe Dashboard setup checklist
(CAD activation, Stripe Tax registrations, Apple/Google Pay enable, webhook).
- tests/test_stripe_checkout.py — 25 tests covering plans, stripe_client, routes,
and the _PUBLIC_INDEXABLE_ENDPOINTS integration. Stripe SDK mocked via
unittest.mock.patch (no network). Windows manual driver included.
Webhook (B-2.8) will be the source of truth for user.subscription_status.
This task only mutates user.stripe_customer_id (identity, not state).
Existing pricing CTAs in templates/marketing/_partials/_pricing_tiers.html
already link to /checkout/<slug> (verified) — no marketing template touched.
Tests: 25/25 new + 89/89 prior pass on Windows manual driver.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
b8fa321edd |
feat(auth): B-2.6 WebAuthn / Passkey support (FIDO2 + biometric 2FA)
Adds phishing-resistant 2nd factor via FIDO2 hardware keys (YubiKey etc.) and device biometrics (Touch ID, Windows Hello, etc.). Reuses the existing B-2.5 TOTP gate so a passkey is a 3rd valid option on /2fa/verify, alongside TOTP code and recovery code. Post-login enrolment lives at /2fa/passkey/setup. Wraps python-webauthn==2.5.2 in a thin service layer (src/auth/webauthn.py) that persists credentials in the existing User.webauthn_credentials JSON column (added in B-2.1 — no schema change). Each credential dict carries id, public_key, sign_count, transports, name, and created_at. sign_count is updated after every successful authentication for WebAuthn anti-cloning (§6.1.1). Backend: 6 new auth routes (passkey_setup, register/begin, register/finish, delete, auth/begin, auth/finish). The 4 JSON endpoints are CSRF-exempt at Flask-WTF level because CSRFProtect cannot read tokens from a JSON body without app-wide config; the X-CSRFToken header is still sent as defence-in-depth. The form-POST delete route DOES enforce CSRF. The @csrf_exempt decorator was previously a no-op label; init_auth_extensions now walks module-level functions and applies real csrf.exempt() to any flagged with _csrf_exempt=True. Login gate now fires when the user has TOTP enabled OR at least one passkey, and totp_verify_login passes has_passkeys + has_totp flags so the template can show only the relevant sections. Frontend: templates/auth/totp_verify.html updated IN PLACE with a passkey button section (above TOTP) and an "ou" divider. New templates/auth/passkey_setup.html for managing/enrolling passkeys. New static/js/webauthn-client.js (no external deps, ES2020) wraps navigator.credentials and exchanges base64url payloads with the backend. Tailwind CSS rebuilt. Tests: 22 new tests in tests/test_webauthn_passkey.py covering the service layer (b64url helpers, RP config, list/has, begin/finish for both registration and authentication, delete) and the route flow (CSRF-exempt JSON endpoints, login gate redirection, sign_count anti-cloning persistence). Mocks python-webauthn's verify_* functions so tests run without a real authenticator. Windows manual driver follows the existing no-conftest pattern. Self-review: 22/22 new tests pass; 21/21 prior TOTP, 16/16 email, 21/21 OAuth tests still pass (no regression). Env: config/env.oauth.example documents WEBAUTHN_RP_ID, WEBAUTHN_RP_NAME, WEBAUTHN_ORIGIN with full deployment notes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
aa269c5bc0 |
feat(auth): B-2.5 TOTP MFA + recovery codes (Fernet-encrypted secret)
Adds TOTP-based two-factor authentication (RFC 6238) with 10 single-use recovery codes. Secret is encrypted at rest with a Fernet key derived deterministically from app SECRET_KEY (SHA-256 -> urlsafe-base64); the raw base32 secret never lives in the database. Recovery codes are bcrypt-hashed and consumed atomically (single-use, removed from the JSON list on match). Routes: - GET /2fa/setup: generate fresh secret + QR + 10 recovery codes; cache pending state in session, render auth/totp_setup.html with inline QR data URL and the 10 codes shown ONCE. - POST /2fa/setup: verify the user-submitted 6-digit code against the pending secret; on success persist encrypted secret + hashes and flip totp_enabled=True. On invalid code re-render same QR (don't rotate), preserving the user's authenticator scan. - GET /2fa/verify: second factor during login; reads pending_totp_user_id from session and renders auth/totp_verify.html (TOTP code input + collapsed recovery code form, with X codes restants notice). - POST /2fa/verify: accepts EITHER a 6-digit TOTP code OR a recovery code; on success finalises login_user (preserving remember-me intent + next URL captured at the password step), audits success/failure. - POST /2fa/disable: requires password re-auth; nullifies the 3 TOTP fields. Login gate (src/api/auth.py /login): after password+email-verification checks but BEFORE login_user, if user.totp_enabled set session['pending_totp_user_id'] / pending_totp_remember / pending_totp_next and 302 -> /2fa/verify. OAuth/SSO/magic-link paths are intentionally NOT gated in B-2.5 (deferred — IdP handles its own MFA). Schema: - New JSON column User.totp_recovery_codes (nullable) added via add_column_if_not_exists in src/init_db.py (no Alembic, follows existing pattern). - Re-uses B-2.1 columns totp_secret_encrypted (VARCHAR 255) and totp_enabled (BOOLEAN); both already migrated. Compatibility audit overrides honoured: - Service layer at src/auth/totp.py (NOT a new src/auth_extended/ pkg). - Templates at templates/auth/totp_setup.html and templates/auth/totp_verify.html extending marketing/base.html with brand tokens + WCAG patterns (focus-visible, role=alert, aria-required, autocomplete=one-time-code, inputmode=numeric). - account.html integration deferred to a polish task — admins access /2fa/setup directly for now. Tests (21, all green via Windows manual driver): - Service layer: encrypt/decrypt round-trip, key-mismatch rejection, secret validity, code verification (current/wrong/non-digit), recovery codes (10 pairs, 1:1 bcrypt mapping, single-use consumption, unknown rejection), set/disable user TOTP fields. - Routes: login redirect-to-/2fa/verify when totp_enabled, direct login when disabled, /2fa/verify with correct/wrong TOTP, recovery code consume, redirect-to-login when no pending session, /2fa/setup GET creates pending, POST with valid code enables MFA, POST with invalid code keeps pending + returns 400, /2fa/disable wrong/correct password. Regression check: prior 21 OAuth+magic-link, 16 email-service, and 9 signup-Loi-25 tests all still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
3a41bb482d |
fix(auth): B-2.4 security review fixes — OAuth linking + magic link replay
Follow-up to commit
|
||
|
|
0513e67838 |
feat(auth): B-2.4 OAuth Microsoft/Google + magic link (Loi 25 deferred consent)
Adds Microsoft 365 + Google OAuth providers (separate from the existing generic OIDC SSO at src/auth/sso.py) and a passwordless magic-link login flow. New OAuth signups capture Loi 25 art. 14 consents (4 granular checkboxes) BEFORE creating the User row via /auth/oauth/finish-signup. Per compatibility-audit.md C2: - No src/auth_extended/ directory — extends src/auth/ in place - No new User columns — reuses sso_provider/sso_subject + email_verified - Magic-link tokens via itsdangerous URLSafeTimedSerializer (15-min, no DB) - All routes added to existing auth_bp; templates extend marketing/base.html - Anti-enumeration on /auth/magic-link (generic flash for unknown OR unverified emails) and /auth/magic-link/<token> (same flash for invalid/expired/unverified-user) Files added: - src/auth/oauth_providers.py — Microsoft + Google OAuth registration, is_oauth_provider_enabled(), find_user_by_oauth(), create_oauth_user_with_consent() - src/auth/magic_link.py — generate/consume magic-link tokens - templates/auth/magic_link_request.html, templates/auth/oauth_finish_signup.html - tests/test_oauth_magic_link.py + tests/_run_oauth_magic_link_windows.py (16 tests) - config/env.oauth.example Files modified: - src/api/auth.py — 5 new routes (oauth_provider_login/callback, oauth_finish_signup, magic_link_request/consume); login flashes translated FR; oauth_*_enabled flags passed to login template - src/app.py — wires init_oauth_providers(app) after blueprint registration - src/services/email.py — adds send_magic_link_email() (FR + DictIA brand) - templates/login.html — refondu IN PLACE (was 178 lines legacy Vue/TW3) to extend marketing/base.html with OAuth buttons, password form, magic-link CTA, signup link - templates/auth/check_email.html — adds action='magic_link' branch - static/css/tailwind.config.js — adds templates/login.html to content - static/css/marketing.css — rebuilt Tests: 16/16 PASS via Windows manual driver. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
dd270bca9e |
fix(auth): B-2.3 security review fixes — XSS escape + token replay
Targeted fixes for issues raised by code review on commit
|
||
|
|
37639a7d09 |
feat(auth): B-2.3 emails FR + DictIA branding (SMTP Resend)
Rebrand src/services/email.py IN PLACE: French + DictIA + brand gradient (#0062ff/#00bdd8/#00c896) — replaces legacy "Speakr" / #2563eb. Greetings now use user.name with fallback to user.username. Subjects: "Vérifiez votre courriel — DictIA" + "Réinitialiser votre mot de passe — DictIA". SMTP_FROM_NAME defaults to DictIA. Footer points to info@dictia.ca with the Loi 25 tagline. Refonte 4 auth templates IN PLACE pour étendre marketing/base.html : check_email, forgot_password, reset_password, verify_success. Tokens DictIA (brand-navy, brand-bg, grad-bg, shadow-cta), French copy, WCAG patterns (label for, focus-visible:outline-2, role=alert, aria-required, text-brand-navy/70 minimum, NBSP français pour Loi 25 / 24 heures / 1 heure / 8 caractères). Translate inline French flash messages in src/api/auth.py for /verify-email, /resend-verification, /forgot-password, /reset-password. Anti-enumeration fix: forgot_password no longer flashes the cooldown remaining (would leak account existence) — silently skips resend, generic flash unchanged. Cooldown logic in src/services/email.py UNCHANGED (60s — verified by test). config/env.email.example: defaults to Resend SMTP at the top + adds Resend to the provider examples list (preserves Gmail/SendGrid/Mailgun/SES/M365). Tests: tests/test_email_service_dictia.py — 12 tests covering DictIA branding, French copy, display-name fallback, anti-enumeration parity (forgot_password returns identical message for known/unknown emails), 60s cooldown, SMTP-not- configured returns False (no exception), check_email.html extends marketing/base (no var(--text-primary) leaks). Includes Windows manual driver (_run_email_service_dictia_windows.py) since pytest cannot collect on Windows native (fcntl POSIX-only). NO new dependency added (no resend SDK — SMTP via existing _send_email). NO new route added or removed. NO src/auth_extended/ created. NO change to itsdangerous-based token logic. templates/auth/**/*.html already in tailwind.config.js content array (B-2.2). Verified locally on Windows manual driver: 12/12 PASS B-2.3, 9/9 PASS regression on B-2.2 signup, 9/9 PASS regression on B-2.1 ConsentLog. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
3b324ad0b9 |
fix(auth): B-2.2 review fixes — Tailwind path + WCAG + race + flash + tests
C-1: Add templates/register.html (and templates/auth/**) to tailwind.config.js
content array so utility classes used by the signup template don't get purged
on next build. Rebuilt static/css/marketing.css; verified text-brand-navy/90
and min-h-[calc(100vh-62px)] are now compiled.
I-1: Replace flash() calls for missing required consents with WTForms
field-level errors (form.consent_cgu.errors.append / form.consent_confidentialite
.errors.append). Errors render inline next to each consent checkbox via
{% if form.consent_cgu.errors %}<p role="alert">…</p>{% endif %}. Prevents
session-backed flash messages from leaking across unrelated navigations.
I-2: Wrap user creation + flush in IntegrityError retry loop (max 5 attempts);
import IntegrityError from sqlalchemy.exc. Absorbs the inherent race between
_generate_unique_username's lookup and the subsequent flush under concurrent
signups. Added docstring note to _generate_unique_username explaining the
wrapper.
I-3: Move db.create_all() inside the try/finally in
test_signup_route_csrf_enforced so WTF_CSRF_ENABLED is restored even if
table creation fails.
I-4: Pin test_signup_rejects_duplicate_email assertion to status_code == 200
(WTForms validate_email raises ValidationError → form fails validation →
fall-through to default 200 render_template).
I-5: Add id="password-help" to the password help paragraph and
aria-describedby="password-help" to the password input so screen readers
announce the password requirements when the field is focused.
I-6: Bump flash banner text colors from -700/-800 to -900 variants
(text-amber-900, text-blue-900, text-red-900, text-green-900) for safer
WCAG 2.2 AA contrast against the -50 backgrounds. Same bump applied to the
new consent and password inline error renders.
|
||
|
|
d2fc1f03ed |
feat(auth): B-2.2 signup Loi 25-compliant (4 consent checkboxes)
Refondre /register en /signup avec consentement granulaire (LPRPSP art. 14):
- SignupLoi25Form (Flask-WTF) remplace RegistrationForm
- 4 BooleanField séparés: cgu, confidentialite (obligatoires) + marketing,
analytics (optionnels). Chaque consentement crée 1 row ConsentLog avec
ip_address (CF-Connecting-IP > remote_addr), user_agent (tronqué 500),
version='2026-04-27' (B-2.9 substituera LEGAL_VERSION canonique).
- Marketing/analytics non cochés -> ConsentLog row avec granted=False
(refus explicite tracé pour audit Loi 25).
- /register reste 302 -> /signup (backward compat).
- Username auto-généré unique depuis email local-part (max 20, alphanum,
suffixe numérique sur collision).
- name = "{first_name} {last_name}".strip() persisté dans User.name
(pas de colonnes first_name/last_name au modèle).
- send_verification_email() existant réutilisé (smtplib via env SMTP_*).
Template register.html refondu IN PLACE pour étendre marketing/base.html:
- 4 checkboxes dans <fieldset>+<legend>, AUCUNE pré-cochée
- WCAG 2.2 AA: focus-visible outlines, aria-required, label for=, role=alert
- OQLF: NBSP via | safe pour "Loi 25"
Tests: 9 cas couvrent GET 200, refus CGU, refus RPRP, happy path 4 rows,
capture IP+UA, duplicate email, username collision, /register redirect,
CSRF enforcement. Pattern test_consent_log.py (no conftest, env setup
avant imports, app_context, db.create_all/drop_all).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
8792ffb8a4 |
fix(auth): B-2.1 — FK erasure policy, totp_secret_encrypted, validates, docs
- ConsentLog.user_id: nullable=True + ondelete='SET NULL' for Loi 25 art. 28.1
right-to-erasure (audit row survives user deletion, user_id nulled out).
Matches existing pattern in auth_log.py / access_log.py.
- Add ConsentLog.@validates('consent_type') to reject typos at ORM level
(silent typos in audit data are very hard to detect later).
- Rename User.totp_secret -> totp_secret_encrypted (size 64->255 for Fernet
envelope). Self-documenting contract: never assign plaintext to this column.
- init_db.py: drop NOT NULL from totp_enabled migration string for consistency
with every other Boolean column in the file (model-side nullable=False is
sufficient).
- Docs: User class docstring updated to reflect MFA/billing/ordre context;
webauthn_credentials shape documented; version column policy documented.
- Tests: cleaner IntegrityError catch; add survives_user_deletion test
(right-to-erasure); add rejects_invalid_consent_type test (validator).
|
||
|
|
48d2abfa74 |
feat(auth): B-2.1 ConsentLog model (Loi 25) + User MFA/OAuth/Stripe fields
- New src/models/consent.py — ConsentLog with user_id FK, consent_type
('cgu' | 'confidentialite' | 'marketing' | 'analytics'), version, granted
bool, granted_at/revoked_at timestamps, ip_address (45 chars for IPv6),
user_agent (500 chars). User.consent_logs backref. Audit trail per
LPRPSP art. 14 (consent tracé) + art. 3.5 (journal).
- src/models/user.py: add 7 new columns (totp_secret, totp_enabled DEFAULT 0,
webauthn_credentials JSON, ordre_pro, cabinet, stripe_customer_id,
subscription_status). Do NOT duplicate existing sso_provider/sso_subject/
email_verified/etc. (per compatibility-audit C4).
- src/init_db.py: 7 add_column_if_not_exists() calls for the new User
columns + 2 create_index_if_not_exists() for stripe_customer_id and
subscription_status. NO Alembic — init_db.py pattern matches
compatibility-audit C3.
- src/models/__init__.py: register ConsentLog import.
- tests/test_consent_log.py: 7 tests — grant flow, 4 consent types, revoke
preserves audit trail, User backref, NOT NULL on ip/UA, User.B-2.1 fields
round-trip, defaults safe.
|
||
|
|
d45c9c9349 |
fix(marketing): A-2.8b — Loi 25 badge contrast (WCAG AA) + stale docstring
- Loi 25 article number badges (Art. 3.3, 3.5, 14): change from `grad-bg text-white text-xs font-black` to `bg-brand-navy text-white text-xs font-black`. White-on-grad-bg failed AA on the cyan/green portion of the gradient (~2:1 contrast); solid navy gives ~16:1. - Update routes.py module docstring to past tense (A-2.8b is now done). - Add regression assertion ensuring badges use solid navy, not grad-bg. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
3646a5e64d |
feat(marketing): A-2.8b /conformite + /contact standalone pages
- /conformite page: extends base.html, page H1 with cosmic orb header, 4 pillar cards on white (mirrors landing's Conformite section content with same hedges 'Mappe' 'concue avec' 'Compatible'), 3 Loi 25 article detail cards (art. 3.3 EFVP, art. 3.5 Audit trail, art. 14 Consentement) with grad-bg article-number badges, AGPL v3 transparency CTA section with external links to Gitea + gnu.org (rel=noopener), generic CTA section - /contact page: extends base.html, 3 method cards (email, phone tel:link, postal address with <address>), 6 pre-filled mailto subject shortcuts with focus-visible WCAG 2.2 AA, pre-launch disclaimer that online form ships at launch (B-2.x). NO <form> tag - mailto only - POST returns 405 until B-2.x adds the form handler. - routes.py: add /conformite and /contact routes; preserves existing landing/tarifs/fonctionnalites views and TESTIMONIALS/FAQ data. - tests: append 13 new tests to test_marketing_secondary_pages.py covering routes 200, single H1, 4 pillars + Loi 25 articles + AGPL externals on /conformite, 3 contact methods + 6 shortcuts + 405 on POST + pre-launch note + OQLF typography on /contact. - Apply established WCAG 2.2 AA, FlexiHub, OQLF, LPC art. 219 disciplines. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
202e1a08d9 |
fix(marketing): A-2.8a — extract pricing partial + sync bento + OQLF + test calibration
- 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
|
||
|
|
d471626183 |
feat(marketing): A-2.8a /tarifs + /fonctionnalites standalone pages
- /tarifs page: extends base.html, reuses pricing_card + button macros, shows the 3 forfaits with NBSP-formatted CAD prices, an 8-row deep-dive comparison matrix (DictIA 8 vs DictIA 16 vs DictIA Cloud), 5 tarification FAQ items (frais cachés, migration, GPU, taxes TPS/TVQ, plans annuels) with Alpine accordion + focus-visible WCAG 2.2 AA, CTA section - /fonctionnalites page: extends base.html, reuses bento_card macro, re-renders the 6 features with same content as landing's bento section for consistency, adds dedicated 7-format export grid + 8-integration grid (with trademark disclaimer) + 6 tech specs section (Whisper/pyannote /Mistral/stack/audio/langues), CTA section - routes.py: add /tarifs and /fonctionnalites routes (passes FAQ to /tarifs for the tarification accordion; preserves existing landing(), TESTIMONIALS, FAQ data structures unchanged) - tests/test_marketing_secondary_pages.py: NEW test file (16 tests covering routes 200, base.html inheritance, H1 anchors, 3 pricing cards, comparison matrix, tarifs FAQ accordion, OQLF typography, 6 bento + 7 exports + 8 integrations + 6 tech specs sections, canonical meta) - All sections respect WCAG 2.2 AA, FlexiHub design discipline, LPC art. 219 hygiene (sourcing dates, trademark disclaimer, hedged claims, NBSP) |
||
|
|
2b3eeb98e0 |
fix(marketing): A-2.7b WCAG 2.2 AA polish + JSON-LD test hardening
- Drop role="region" from FAQ panels (had no accessible name — axe-core violation; disclosure pattern with button + aria-controls + aria-expanded is sufficient per WAI-APG accordion guidance) - Add focus-visible:outline-2 outline-brand-b1 outline-offset-2 to FAQ buttons (WCAG 2.2 AA 2.4.7 Focus Not Obscured + 2.4.11 Focus Appearance — Safari default focus indicator is unreliable) - Sweep pre-existing text-white/50 on Hero social proof microcopy → /70 (branch-wide WCAG floor; recurring landmine flagged at A-2.7a review) - Strengthen test_faq_jsonld_schema_present to json.loads() the extracted block and validate the FAQPage schema shape (regression guard for future content edits with unescaped backslashes/quotes) |
||
|
|
824ea638de |
feat(marketing): A-2.7b témoignages placeholder + FAQ accordion + CTA + JSON-LD
- Pre-launch testimonials section: 3 placeholder cards (avocat, CPA, municipal)
with persona icons + 'Témoignage à venir' label — NO fabricated quotes
(LPC art. 219). Expected publication mai-juin 2026 from T-4.1 interviews.
- FAQ accordion: 7 verifiable Q&A using Alpine.js core (x-data + x-show +
built-in x-transition; NO x-collapse plugin). Each item has @click toggle,
:aria-expanded, aria-controls, role="region" panel, focusable button.
- Schema.org FAQPage JSON-LD inline at end of FAQ section — striptags +
replace(' ', ' ') to normalize entities for Google FAQ rich result.
- CTA final: 'Réservez votre pré-inscription' (mailto + #tarifs anchor),
cosmic orbs to mirror Hero (page closure), ghost variant secondary button.
- Inline TESTIMONIALS and FAQ Python lists in src/marketing/routes.py
(no PyYAML dep — YAGNI; T-4.1 can introduce it when real data warrants).
- 8 new tests covering testimonials placeholders, forbidden fake names,
7 FAQ panels, Alpine bindings, JSON-LD schema, CTA wording, route data.
|
||
|
|
31fada46d4 |
fix(marketing): A-2.7a — comparatif consistency + SOC 2 hedge + cross-platform emoji
- Reword comparatif row 1 to make Teams ✗ for non-Loi-25-compliant US transfer (was ⚠ — too soft; territoriality is binary) - Reword row 2 to positive: 'Souveraineté hors Cloud Act US' (was inverted — ✓ used for bad outcomes, breaking visual scan convention) - Reword row 4 criterion to match deliverable: 'Diarisation jusqu'à 8 locuteurs' (was '8+', mismatched DictIA's '✓ Jusqu'à 8' cell) - Reword row 5 to 'Coût mensuel par utilisateur' (was 'utilisateur/mois' — awkward French + missing NBSP per OQLF) - Hedge SOC 2 Type II claim about OVH Beauharnois — third-party certification scope-dependent; reword to 'conformité documentée selon les services (ISO 27001, SOC 2 selon le périmètre)' to avoid LPC art. 219 risk - Replace 🇨🇦 regional indicator pair with 🍁 maple leaf — renders reliably on Windows + Firefox Linux without color-emoji fonts - Update existing test for renamed criteria keywords - Add regression test for ✓-means-good convention + SOC 2 hedge guard Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
0d69fcd034 |
feat(marketing): A-2.7a footer 4-col + comparatif table + conformité 4 pillars
- Replace placeholder _footer.html with full 4-column footer (Brand, Produit, Légal, Compte) — added aria-labelledby landmark, sr-only headings, address block, tel: link, external-link new-tab announcements, NBSP per OQLF - Add Comparatif section: DictIA vs MS Teams vs Otter.ai vs Whisper local on 6 criteria. Sourced (politiques publiques + grilles officielles 2026-04-27), trademark disclaimer, responsive table with sr-only caption + scope attrs - Add Conformité forteresse section: 4 pillars (OVH Beauharnois, mapped Loi 25 LPRPSP refs, compatible Cadre IA LGGRI, AGPL v3 verifiable Gitea link) - Soft hedges throughout: 'mappé', 'conçu avec', 'compatible' (no 'certifié', 'endossé', 'reconnu' — LPC art. 219 / Competition Act s. 52 hygiene) - 11 new tests covering footer landmarks, comparatif sourcing, conformité hedges, WCAG /70 contrast, and forbidden competitive claim regressions Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |