Files
dictia-public/templates/marketing/landing.html
Allison 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>
2026-04-28 21:06:12 -04:00

2818 lines
176 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends 'marketing/base.html' %}
{% block title %}Transcription IA conforme Loi 25 — DictIA pour avocats, CPA, secteur public{% endblock %}
{% block description %}DictIA transcrit vos réunions confidentielles 100% au Québec. Conforme Loi 25, Barreau, CPA, ChAD. Conçu avec 9 ordres professionnels — lancement printemps 2026.{% endblock %}
{% block content %}
{# ===== HERO — round 3 : reproduction fidèle dictia.ca/solutions/dictai ===== #}
{# Source : InnovA-AI/Website-Sanity/components/sections/dictai-page-content.tsx (lignes 260-518)
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 (mousemove window → translate, inertie via CSS transition)
4. Shockwave on click (CSS pseudo-element, scale 0→4 + opacity 1→0)
5. Hero title fade-in + word-staggered animation #}
<style>
/* ── 3-step flow : auto-cycle highlight ── */
.hero-flow-card {
transition: transform 350ms ease-out, opacity 350ms ease-out, box-shadow 350ms ease-out, background-color 350ms ease-out;
}
.hero-flow-card.is-active {
transform: scale(1.05);
box-shadow: 0 0 22px rgba(6, 182, 212, 0.45), 0 0 44px rgba(37,99,235, 0.18);
}
.hero-flow-card.is-inactive { opacity: 0.45; }
/* Animated arrow draw between cards */
@keyframes hero-arrow-draw {
0% { stroke-dashoffset: 24; opacity: 0.3; }
50% { stroke-dashoffset: 0; opacity: 1; }
100% { stroke-dashoffset: -24; opacity: 0.3; }
}
.hero-flow-arrow path { stroke-dasharray: 24; animation: hero-arrow-draw 1.8s ease-in-out infinite; }
/* ── Magnetic CTA — Alpine writes :style="--mx,--my", we apply it ── */
.hero-magnetic { transition: transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1); will-change: transform; }
.hero-magnetic:hover { transform: translate(var(--mx, 0px), var(--my, 0px)); }
/* Pulse halo around primary CTA */
@keyframes hero-cta-halo {
0%, 100% { transform: scale(1); opacity: 0.35; }
50% { transform: scale(1.18); opacity: 0.10; }
}
.hero-cta-halo { animation: hero-cta-halo 2.4s ease-in-out infinite; }
/* ── Parallax orb — Alpine writes :style="--ox,--oy" ── */
.hero-parallax-orb {
transform: translate3d(var(--ox, 0px), var(--oy, 0px), 0);
transition: transform 600ms cubic-bezier(0.2, 0.8, 0.2, 1);
will-change: transform;
}
/* Slow rotating mesh ring overlay */
@keyframes hero-orb-rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hero-orb-ring-a { animation: hero-orb-rotate 28s linear infinite; }
.hero-orb-ring-b { animation: hero-orb-rotate 40s linear infinite reverse; }
/* ── Shockwave click effect ── */
@keyframes hero-shockwave {
0% { transform: translate(-50%, -50%) scale(0); opacity: 0.55; }
100% { transform: translate(-50%, -50%) scale(4.5); opacity: 0; }
}
.hero-shockwave {
position: fixed;
width: 30vmax;
height: 30vmax;
border-radius: 9999px;
pointer-events: none;
z-index: 9999;
background: radial-gradient(circle, rgba(6,182,212,0.18) 0%, rgba(37,99,235,0.10) 50%, transparent 70%);
animation: hero-shockwave 700ms cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
}
/* ── Word-staggered title reveal ── */
@keyframes hero-word-rise {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.hero-h1-word {
display: inline-block;
opacity: 0;
animation: hero-word-rise 0.55s ease-out forwards;
}
/* ── Reduced-motion : freeze all hero animations ── */
@media (prefers-reduced-motion: reduce) {
.hero-flow-card, .hero-flow-arrow path, .hero-magnetic,
.hero-cta-halo, .hero-parallax-orb, .hero-orb-ring-a, .hero-orb-ring-b,
.hero-shockwave, .hero-h1-word {
animation: none !important;
transition: none !important;
}
.hero-flow-card.is-active { transform: none !important; box-shadow: none !important; }
.hero-flow-card.is-inactive { opacity: 1 !important; }
.hero-h1-word { opacity: 1 !important; transform: none !important; }
.hero-parallax-orb { transform: none !important; }
}
</style>
<section
class="relative overflow-hidden bg-brand-navy text-white py-24 md:py-32 min-h-[85vh] flex items-center"
aria-labelledby="hero-title"
x-data="{
flowIdx: 0,
flowTimer: null,
mx: 0, my: 0,
ox: 0, oy: 0,
shockwaves: [],
swId: 0,
startFlow() {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
if (this.flowTimer) clearInterval(this.flowTimer);
this.flowTimer = setInterval(() => { this.flowIdx = (this.flowIdx + 1) % 3 }, 1800);
},
onSectionMove($event) {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
const r = $event.currentTarget.getBoundingClientRect();
const cx = r.left + r.width / 2;
const cy = r.top + r.height / 2;
this.ox = ($event.clientX - cx) * -0.025;
this.oy = ($event.clientY - cy) * -0.018;
},
onSectionLeave() { this.ox = 0; this.oy = 0; this.mx = 0; this.my = 0; },
onCtaMove($event) {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
const r = $event.currentTarget.getBoundingClientRect();
const dx = $event.clientX - (r.left + r.width / 2);
const dy = $event.clientY - (r.top + r.height / 2);
const dist = Math.sqrt(dx*dx + dy*dy);
const max = 110;
if (dist < max) {
const f = (1 - dist / max) * 0.18;
this.mx = Math.max(-8, Math.min(8, dx * f));
this.my = Math.max(-8, Math.min(8, dy * f));
} else { this.mx = 0; this.my = 0; }
},
onCtaLeave() { this.mx = 0; this.my = 0; },
onCtaClick($event) {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
const id = ++this.swId;
this.shockwaves.push({ id, x: $event.clientX, y: $event.clientY });
setTimeout(() => { this.shockwaves = this.shockwaves.filter(s => s.id !== id); }, 700);
}
}"
x-init="startFlow()"
@mousemove="onSectionMove($event)"
@mouseleave="onSectionLeave()"
>
{# Shockwave overlays — fixed-position ripples on CTA click #}
<template x-for="sw in shockwaves" :key="sw.id">
<span class="hero-shockwave" :style="`left:${sw.x}px; top:${sw.y}px;`" aria-hidden="true"></span>
</template>
{# Cosmic orbs background — 3 radial gradients (mauve 16%, aqua 7%, aqua 11%) + subtle grid + horizontal accent line #}
<div class="absolute inset-0 pointer-events-none" aria-hidden="true">
{# Orb 1 — primary mauve, top-left #}
<div class="absolute top-1/4 left-1/4 w-[600px] h-[600px] rounded-full opacity-100"
style="background: radial-gradient(circle, rgba(37,99,235,0.16) 0%, transparent 60%); filter: blur(40px);"></div>
{# Orb 2 — aqua, mid-right #}
<div class="absolute top-1/2 right-1/4 w-[500px] h-[500px] rounded-full"
style="background: radial-gradient(circle, rgba(6,182,212,0.07) 0%, transparent 60%); filter: blur(40px);"></div>
{# Orb 3 — aqua accent, bottom-left #}
<div class="absolute bottom-1/4 left-1/3 w-[400px] h-[400px] rounded-full"
style="background: radial-gradient(circle, rgba(6,182,212,0.11) 0%, transparent 60%); filter: blur(40px);"></div>
{# Subtle grid overlay (FlexiHub signature) #}
<div class="absolute inset-0"
style="background-image: linear-gradient(rgba(255,255,255,0.02) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.02) 1px, transparent 1px); background-size: 40px 40px;"></div>
{# Horizontal accent line — gradient mauve to aqua to transparent #}
<div class="absolute top-1/3 left-0 right-0 h-px"
style="background: linear-gradient(90deg, transparent, rgba(37,99,235,0.3), rgba(6,182,212,0.2), transparent);"></div>
</div>
{# 3D abstract orb — reacts to mouse via Alpine ox/oy → CSS variables #}
<div
class="hero-parallax-orb absolute right-[6%] top-[12%] pointer-events-none hidden lg:block"
:style="`--ox:${ox}px; --oy:${oy}px;`"
aria-hidden="true"
>
<div class="relative w-72 h-72">
{# Outer ring #}
<div class="absolute inset-0 rounded-full border border-brand-b3/15" style="transform: scale(1.35);"></div>
{# Ambient blob #}
<div class="absolute inset-0 rounded-full"
style="background: radial-gradient(ellipse at 40% 40%, rgba(96,165,250,0.14) 0%, rgba(6,182,212,0.07) 50%, transparent 75%); filter: blur(20px);"></div>
{# Rotating mesh ring A #}
<div class="hero-orb-ring-a absolute inset-0 rounded-full border border-dashed border-white/[0.07]" style="transform: scale(1.15);"></div>
{# Rotating mesh ring B #}
<div class="hero-orb-ring-b absolute inset-0 rounded-full border border-white/[0.05]" style="transform: scale(0.9);"></div>
{# Inner glow core #}
<div class="absolute inset-[30%] rounded-full"
style="background: radial-gradient(circle, rgba(96,165,250,0.25) 0%, rgba(6,182,212,0.08) 60%, transparent 100%);"></div>
</div>
</div>
<div class="relative max-w-[1200px] mx-auto px-6 w-full">
{# Single-column hero — texte centré (lg : aligné gauche). Le visuel canonique est le 3-step flow inline (pas de mockup app). #}
<div class="max-w-4xl mx-auto lg:mx-0">
{# Eyebrow / breadcrumb back-link "Toutes les solutions" — link to anchor #solutions sur landing #}
<a href="#tarifs"
class="inline-flex items-center gap-2 text-white/40 hover:text-white text-xs font-sans uppercase tracking-[0.18em] transition-colors mb-8 animate-tc-fade-in-up"
style="animation-delay: 0ms; animation-fill-mode: backwards;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-3.5 h-3.5" aria-hidden="true"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
Toutes les solutions
</a>
{# Sous-eyebrow — pillars #}
<div class="flex flex-wrap items-center gap-x-3 gap-y-1 mb-5 animate-tc-fade-in-up" style="animation-delay: 75ms; animation-fill-mode: backwards;">
<span class="eyebrow grad-text">
TRANSCRIPTION IA · CONFORME LOI&nbsp;25 · QUÉBEC
</span>
<span class="hidden sm:block h-px bg-white/10 w-[80px]" aria-hidden="true"></span>
</div>
<p class="text-sm text-white/60 mb-8 animate-tc-fade-in-up" style="animation-delay: 75ms; animation-fill-mode: backwards;">
Audio → Texte · Résumés IA · Conforme Loi&nbsp;25 &amp; ordres professionnels
</p>
{# Brand wordmark large — Dict + IA accent #}
<h1 id="hero-title" class="font-black leading-[0.92] mb-6 text-white" style="font-size: clamp(3rem, 7vw, 6rem); letter-spacing: -0.025em;">
<span class="hero-h1-word" style="animation-delay: 80ms;">Dict</span><span class="hero-h1-word grad-text" style="animation-delay: 160ms;">IA</span>
</h1>
{# 3-step flow inline — REMPLACE le mockup app actuel. Auto-cycle 1→2→3 toutes les 1.8s. #}
<div
class="flex flex-wrap items-center gap-2 mb-8 animate-tc-fade-in-up"
style="animation-delay: 200ms; animation-fill-mode: backwards;"
role="group"
aria-label="Flux DictIA en 3 étapes"
>
{% set flow_steps = [
('Importez un fichier', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-3.5 h-3.5" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>'),
('Texte en 2&nbsp;min', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-3.5 h-3.5" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="13" y2="17"/></svg>'),
('Résumé + actions', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-3.5 h-3.5" aria-hidden="true"><path d="M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5z"/><path d="M5 19l1.5-1.5"/><path d="M19 19l-1.5-1.5"/></svg>')
] %}
{% for label, icon in flow_steps %}
<span
class="hero-flow-card inline-flex items-center gap-1.5 border border-white/10 bg-white/[0.04] rounded-full px-3.5 py-1.5 text-[12px] font-medium text-white/70"
:class="flowIdx === {{ loop.index0 }} ? 'is-active grad-bg !text-white !border-transparent' : 'is-inactive'"
aria-label="Étape {{ loop.index }} sur 3 : {{ label | striptags }}"
>
<span aria-hidden="true">{{ icon | safe }}</span>
<span>{{ label | safe }}</span>
</span>
{% if not loop.last %}
<svg class="hero-flow-arrow w-5 h-5 text-white/40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M5 12h14M13 6l6 6-6 6"/>
</svg>
{% endif %}
{% endfor %}
</div>
{# H2 (canonique : phrase clé en cyan/grad) #}
<p class="text-xl md:text-2xl lg:text-3xl font-black mb-6 leading-snug grad-text animate-tc-fade-in-up" style="animation-delay: 280ms; animation-fill-mode: backwards;">
Transcription IA locale en 2&nbsp;minutes — Audio → Texte · Résumés IA · Conforme Loi&nbsp;25 &amp; ordres professionnels
</p>
{# Sub canonique #}
<p class="text-base md:text-lg text-white/65 max-w-3xl leading-relaxed mb-10 animate-tc-fade-in-up" style="animation-delay: 360ms; animation-fill-mode: backwards;">
DictIA transforme vos réunions en texte, résumés et points d'action en 2&nbsp;minutes — hébergé au Québec sur OVH&nbsp;Beauharnois, zéro Cloud Act. Contrairement à Teams Copilot ou Otter.ai, vos données ne quittent jamais le Québec. Conforme au Cadre&nbsp;IA MCN et aux 5&nbsp;ordres à directives IA formelles (Barreau, ChAD, CMQ, OIIQ, OACIQ).
</p>
{# Stats grid — 4 colonnes #}
<div class="grid grid-cols-2 sm:grid-cols-4 gap-3 mb-10 max-w-3xl animate-tc-fade-in-up" style="animation-delay: 440ms; animation-fill-mode: backwards;">
{% for stat in [
('~2&nbsp;min', 'Pour 1&nbsp;h d\'audio'),
('5&nbsp;ordres','Directives IA formelles'),
('95&nbsp;%+', 'Précision FR-CA'),
('0&nbsp;$', 'Frais par utilisateur')
] %}
<div class="bg-white/[0.04] border border-white/10 rounded px-4 py-3 text-center">
<div class="font-display font-black grad-text text-xl leading-none mb-1">{{ stat[0] | safe }}</div>
<div class="text-[10.5px] text-white/55 uppercase tracking-wider leading-tight">{{ stat[1] | safe }}</div>
</div>
{% endfor %}
</div>
{# Dual CTA — primary "Réserver une démo" magnétique + ghost "Voir les forfaits" #}
<div class="flex flex-col sm:flex-row gap-3 animate-tc-fade-in-up" style="animation-delay: 520ms; animation-fill-mode: backwards;">
{# Magnetic CTA wrapper — Alpine writes mx/my via mousemove #}
<span class="relative inline-flex">
{# Pulse halo behind the primary CTA #}
<span class="hero-cta-halo absolute -inset-1 rounded pointer-events-none"
style="background: radial-gradient(ellipse, rgba(96,165,250,0.50) 0%, transparent 70%);"
aria-hidden="true"></span>
<a
href="/contact"
@mousemove="onCtaMove($event)"
@mouseleave="onCtaLeave()"
@click="onCtaClick($event)"
class="hero-magnetic relative inline-flex items-center justify-center gap-2 bg-gradient-to-r from-brand-b3 to-brand-b1 hover:from-brand-b1 hover:to-brand-b3 text-brand-navy font-bold px-8 h-12 text-sm rounded transition-colors w-full sm:w-auto focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2"
:style="`--mx:${mx}px; --my:${my}px;`"
>
<span>Réserver une démo</span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
</a>
</span>
<a
href="/tarifs"
class="inline-flex items-center justify-center gap-2 border border-white/15 hover:border-white/30 text-white font-semibold px-8 h-12 text-sm rounded transition-colors w-full sm:w-auto focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2"
>
<span>Voir les forfaits</span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
</a>
</div>
{# Social proof microcopy — defensible: 9 ordres pros + waitlist + launch date (préservé pour SEO/legal) #}
<p class="mt-8 text-sm text-white/70 flex flex-wrap items-center gap-x-2 gap-y-1 animate-tc-fade-in-up" style="animation-delay: 600ms; animation-fill-mode: backwards;">
<span class="inline-flex items-center gap-1.5">
<svg width="14" height="14" viewBox="0 0 20 20" fill="currentColor" class="text-brand-b3" aria-hidden="true">
<path d="M10 2L3 5v5.5c0 4.04 2.84 7.85 7 8.5 4.16-.65 7-4.46 7-8.5V5l-7-3z"/>
</svg>
<span>Conçu avec 9 ordres professionnels québécois</span>
</span>
<span class="text-white/30">·</span>
<span>Pré-inscription ouverte</span>
<span class="text-white/30">·</span>
<span>Lancement printemps 2026</span>
</p>
</div>
</div>
</section>
{# ===== TRUST BAR — 9 ordres pros ===== #}
<section class="bg-white py-16 border-y border-brand-border" aria-labelledby="trust-bar-title">
<div class="max-w-[1200px] mx-auto px-6">
<div class="text-center max-w-xl mx-auto mb-10">
<p id="trust-bar-title" class="eyebrow text-brand-navy/70 mb-3">
Mappé aux 9&nbsp;ordres professionnels québécois
</p>
<p class="text-sm text-brand-navy/70">
L'architecture DictIA est conçue pour répondre aux exigences de confidentialité et d'audit des principaux ordres professionnels du Québec.
</p>
</div>
{# 9 ordres — uniform card grid, official names, profession labels (no repeated acronyms) #}
<ul class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 mb-14" role="list">
{% for ordre in [
{'acronym': 'Barreau', 'name': 'Barreau du Québec', 'profession': 'Avocats'},
{'acronym': 'CNQ', 'name': 'Chambre des notaires du Québec', 'profession': 'Notaires'},
{'acronym': 'CPA', 'name': 'Ordre des CPA du Québec', 'profession': 'Comptables'},
{'acronym': 'ChAD', 'name': "Chambre de l'assurance de dommages", 'profession': 'Assurance'},
{'acronym': 'OACIQ', 'name': "Organisme d'autoréglementation du courtage immobilier du Québec", 'profession': 'Courtage immobilier'},
{'acronym': 'CMQ', 'name': 'Collège des médecins du Québec', 'profession': 'Médecins'},
{'acronym': 'OIIQ', 'name': 'Ordre des infirmières et infirmiers du Québec', 'profession': 'Infirmières'},
{'acronym': 'OPQ', 'name': 'Ordre des pharmaciens du Québec', 'profession': 'Pharmaciens'},
{'acronym': 'OEQ', 'name': 'Ordre des ergothérapeutes du Québec', 'profession': 'Ergothérapeutes'}
] %}
<li class="flex items-center gap-4 p-4 bg-white border border-brand-border rounded hover:border-brand-b1 transition-colors">
<span class="flex-shrink-0 w-14 h-14 flex items-center justify-center bg-brand-bg border border-brand-border" aria-hidden="true">
<span class="grad-text font-black text-sm tracking-tight">{{ ordre.acronym }}</span>
</span>
<span class="min-w-0 flex-1">
<span class="block text-sm font-semibold text-brand-navy leading-snug">{{ ordre.name | safe }}</span>
<span class="block text-xs uppercase tracking-wider text-brand-navy/60 mt-1">{{ ordre.profession }}</span>
</span>
</li>
{% endfor %}
</ul>
{# 4 KPI metrics — defensible numbers with footnote attribution #}
<div class="grid grid-cols-2 md:grid-cols-4 gap-8">
{% for kpi in [
('~5 min', 'par heure d\'audio', 'Traitement local 30× temps réel sur GPU RTX'),
('95&nbsp;%+', 'précision FR-CA', 'WhisperX Large-v3 — test interne 2026-Q1'),
('0&nbsp;$', 'frais par utilisateur', 'Modèle par serveur, volume illimité'),
('100&nbsp;%', 'local au Québec', 'OVH Beauharnois ou vos serveurs')
] %}
<div class="text-center">
<div class="text-4xl font-black grad-text mb-2">{{ kpi[0] | safe }}</div>
<div class="text-sm font-semibold text-brand-navy mb-1">{{ kpi[1] }}</div>
<div class="text-xs text-brand-navy/60">{{ kpi[2] }}</div>
</div>
{% endfor %}
</div>
{# Footnote — discloses methodology for the 95% claim (LPC art. 219 hygiene) #}
<p class="text-xs text-brand-navy/70 text-center mt-8 max-w-2xl mx-auto">
Précision mesurée sur un échantillon interne d'audio professionnel québécois (juridique, médical, municipal) — méthodologie disponible sur demande à <a href="mailto:info@dictia.ca" class="hover:text-brand-navy">info@dictia.ca</a>.
</p>
</div>
</section>
{# ===== PROBLÈME (P de PAS) ===== #}
<section class="bg-brand-bg py-20" aria-labelledby="probleme-title">
<div class="max-w-[1200px] mx-auto px-6">
<div class="text-center max-w-2xl mx-auto mb-12">
<p class="eyebrow grad-text mb-4">LE PROBLÈME — TRANSCRIPTION CLOUD</p>
<h2 id="probleme-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black mb-4">
Vos réunions confidentielles dans Teams Copilot ou Otter.ai
<span class="grad-text">violent la Loi&nbsp;25</span>.
</h2>
<p class="text-lg text-brand-navy/70">
Le transfert de données vocales hors-Québec sans consentement explicite expose les professionnels réglementés à des sanctions disciplinaires de leurs ordres et à des amendes CAI jusqu'à 25&nbsp;M$ ou 4&nbsp;% du chiffre d'affaires mondial.
</p>
</div>
{# 3 problem cards on white surface — Cloud Act, Loi 25, Sanctions #}
<div class="grid md:grid-cols-3 gap-6 mt-12">
{% for card in [
('Cloud Act', 'Loi américaine 2018', 'Microsoft, Google et OpenAI sont soumis au Cloud Act. Vos données peuvent être saisies par les autorités américaines sans votre consentement ni notification — y compris les enregistrements de vos réunions client.',
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" class="w-6 h-6" aria-hidden="true"><path d="M12 3v18"/><path d="M5 7h14"/><path d="M5 7l-2 6a4 4 0 0 0 8 0L9 7"/><path d="M19 7l2 6a4 4 0 0 1-8 0l2-6"/><path d="M8 21h8"/></svg>'),
('Loi 25 — biométrie', 'Sanctions CAI jusqu\'à 25&nbsp;M$', 'La voix est une donnée biométrique au sens des articles 44-45 de la LCCJTI et un renseignement sensible au sens de la Loi 25 (art.&nbsp;12 LSP). Tout traitement nécessite un consentement explicite, une déclaration préalable à la CAI et un transfert vers un territoire offrant une protection équivalente — ce que les États-Unis n\'offrent pas.',
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" class="w-6 h-6" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>'),
('Sanctions disciplinaires', '~250&nbsp;000 pros réglementés QC (CIQ)', 'Les ordres professionnels québécois — au premier rang desquels le Barreau, la Chambre des notaires et CPA Québec — exigent une obligation stricte de confidentialité. Le transfert de données client hors-juridiction sans consentement explicite peut être qualifié de manquement, jusqu\'à la radiation pour les fautes graves.',
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" class="w-6 h-6" aria-hidden="true"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>')
] %}
<article class="bg-white p-6 rounded border border-brand-border">
<div class="grad-text mb-4" aria-hidden="true">{{ card[3] | safe }}</div>
<h3 class="text-lg font-bold mb-1 text-brand-navy">{{ card[0] }}</h3>
<p class="text-xs uppercase tracking-wider text-brand-navy/70 mb-3 font-semibold">{{ card[1] | safe }}</p>
<p class="text-sm text-brand-navy/70 leading-relaxed">{{ card[2] | safe }}</p>
</article>
{% endfor %}
</div>
</div>
</section>
{# ===== CYCLE — "Trois options. Une seule est conforme." ===== #}
{# Source canonique : InnovA-AI/Website-Sanity/components/sections/dictai-cycle.tsx
Round 5 cinématique PRO : palette brand uniformisée (blue/cyan/fuchsia, zéro vert)
+ connecting beam progressive 0→33→66→100 + spotlight active column + USA map silhouette col 2
+ Quebec outline col 3 + halo multi-couches blue+cyan + screen shake + 12 paquets data flying
+ grad-text counters + chips contextuels + phase delays cinématiques 400/1100/2000/3100ms #}
<style>
/* === ROUND 5 — Cycle background ambiance (vignette + grid + orbes) === */
.cycle-section-bg {
background:
radial-gradient(ellipse 60% 40% at 12% 0%, rgba(37,99,235,0.045) 0%, transparent 70%),
radial-gradient(ellipse 60% 40% at 88% 100%, rgba(6,182,212,0.045) 0%, transparent 70%);
}
.cycle-grid-bg {
background-image:
linear-gradient(rgba(11,15,26,0.025) 1px, transparent 1px),
linear-gradient(90deg, rgba(11,15,26,0.025) 1px, transparent 1px);
background-size: 40px 40px;
}
@keyframes cycle-orb-float {
0%, 100% { transform: translate(0,0) scale(1); }
50% { transform: translate(8px,-12px) scale(1.04); }
}
.cycle-orb { animation: cycle-orb-float 8s ease-in-out infinite; }
.cycle-orb-2 { animation-delay: 2.5s; animation-duration: 10s; }
/* === ROUND 5 — Connecting horizontal beam progressive 0→33→66→100 === */
.cycle-beam {
height: 2px;
background: linear-gradient(90deg,
rgba(148,163,184,0.55) 0%,
rgba(148,163,184,0.55) 33%,
rgba(239,68,68,0.55) 33%,
rgba(239,68,68,0.55) 66%,
rgba(6,182,212,0.85) 66%,
rgba(37,99,235,0.85) 100%);
width: var(--beam, 0%);
transition: width 800ms cubic-bezier(0.34, 1.56, 0.64, 1);
box-shadow: 0 0 8px rgba(37,99,235,0.25);
}
/* Loading dot qui suit la beam */
@keyframes cycle-beam-dot {
0% { left: 4%; background: rgba(148,163,184,1); }
33% { left: 33%; background: rgba(148,163,184,1); }
50% { left: 50%; background: rgba(239,68,68,1); }
66% { left: 66%; background: rgba(239,68,68,1); }
100% { left: 96%; background: rgba(6,182,212,1); }
}
.cycle-beam-dot {
width: 6px; height: 6px; border-radius: 9999px;
top: 50%; transform: translate(-50%,-50%);
box-shadow: 0 0 10px currentColor;
animation: cycle-beam-dot 4s ease-in-out 0.4s 1 forwards;
}
/* === ROUND 5 — Spotlight active column === */
.cycle-col-card {
transition: opacity 600ms cubic-bezier(0.34, 1.56, 0.64, 1),
transform 600ms cubic-bezier(0.34, 1.56, 0.64, 1);
opacity: 0.65;
}
.cycle-col-card.is-active { opacity: 1; transform: scale(1.015); }
.cycle-col-card.is-revealed { opacity: 1; }
/* === Cycle pulse rings — nœud source "Réunion en cours" === */
@keyframes cycle-pulse-ring {
0% { transform: scale(1); opacity: 0.5; }
100% { transform: scale(2.1); opacity: 0; }
}
.cycle-pulse { animation: cycle-pulse-ring 2.4s ease-out infinite; }
.cycle-pulse-2 { animation-delay: 0.65s; }
.cycle-pulse-3 { animation-delay: 1.3s; }
/* Live red dot — pulse plus marqué pour signal "réunion en cours" */
@keyframes cycle-live-dot {
0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(239,68,68,0.55); }
50% { opacity: 0.6; box-shadow: 0 0 0 5px rgba(239,68,68,0); }
}
.cycle-live-dot { animation: cycle-live-dot 1.4s ease-in-out infinite; }
/* === ROUND 5 — Cycle col 3 (DictIA) — halo multi-couches blue+cyan (PALETTE BRAND) === */
@keyframes cycle-conforme-glow {
0%, 100% { box-shadow: 0 0 0 0 rgba(37,99,235,0.20), 0 0 0 0 rgba(6,182,212,0.12), 0 0 0 0 rgba(192,38,211,0.06); }
50% { box-shadow: 0 0 36px 3px rgba(37,99,235,0.45), 0 0 64px 6px rgba(6,182,212,0.20), 0 0 96px 12px rgba(192,38,211,0.10); }
}
.cycle-card-dictia.is-visible { animation: cycle-conforme-glow 3.4s ease-in-out infinite; will-change: box-shadow; }
.cycle-card-dictia { transition: transform 400ms ease-out; will-change: transform; }
.cycle-card-dictia.is-visible:hover { transform: translateY(-3px) scale(1.03); }
/* Cycle col 3 — anneaux concentriques autour du Shield (multi-couches drawn) */
@keyframes cycle-ring-out {
0%, 100% { transform: scale(1); opacity: 0.55; }
50% { transform: scale(1.08); opacity: 0.20; }
}
.cycle-ring-outer.is-visible { animation: cycle-ring-out 3.8s ease-in-out infinite; }
.cycle-ring-inner.is-visible { animation: cycle-ring-out 2.9s ease-in-out infinite 0.5s; }
/* Ring SVG drawn line cycling (round 5) */
@keyframes cycle-ring-draw {
0% { stroke-dashoffset: 220; }
100% { stroke-dashoffset: 0; }
}
.cycle-ring-draw-svg circle {
stroke-dasharray: 220;
stroke-dashoffset: 220;
transform-origin: center;
transform: rotate(-90deg);
}
.cycle-ring-draw-svg.is-visible circle { animation: cycle-ring-draw 4s linear infinite; }
/* Cycle SVG lignes de connexion — drawn via stroke-dashoffset, dash flow continu */
.cycle-line { stroke-dasharray: 1.2 0.9; stroke-dashoffset: 50; transition: stroke-dashoffset 600ms ease-out; }
.cycle-line.is-visible { stroke-dashoffset: 0; }
@keyframes cycle-dash-flow { to { stroke-dashoffset: -42; } }
.cycle-line.is-visible.cycle-line-flow { animation: cycle-dash-flow 12s linear infinite; }
/* Cycle phase reveal */
.cycle-reveal { opacity: 0; transform: translateX(14px); transition: opacity 380ms cubic-bezier(0.34, 1.56, 0.64, 1), transform 380ms cubic-bezier(0.34, 1.56, 0.64, 1); }
.cycle-reveal.is-visible { opacity: 1; transform: translateX(0); }
.cycle-reveal-up { opacity: 0; transform: translateY(10px); transition: opacity 500ms cubic-bezier(0.34, 1.56, 0.64, 1), transform 500ms cubic-bezier(0.34, 1.56, 0.64, 1); }
.cycle-reveal-up.is-visible { opacity: 1; transform: translateY(0); }
/* === ROUND 5 — Col 1 — Stack papiers stagger reveal === */
@keyframes cycle-paper-in {
0% { opacity: 0; transform: translateY(8px) rotate(var(--rot, 0deg)); }
100% { opacity: 1; transform: translateY(0) rotate(var(--rot, 0deg)); }
}
.cycle-paper-stack.is-active .cycle-paper {
animation: cycle-paper-in 400ms ease-out forwards;
opacity: 0;
}
/* Cycle horloge rotation (col 1) — accélérée 1 tour / 2.5s + motion blur subtle */
@keyframes cycle-clock-spin { to { transform: rotate(360deg); } }
.cycle-clock { animation: cycle-clock-spin 2.5s linear infinite; transform-origin: center; display: inline-block; will-change: transform; }
/* === ROUND 5 — Col 2 — Data leak particles AMÉLIORÉES (12 paquets vers USA top-right) === */
@keyframes cycle-leak {
0% { transform: translate(0,0) rotate(var(--rot, 0deg)) scale(0.85); opacity: 0; }
18% { opacity: 0.9; }
100% { transform: translate(var(--lx, 60px), var(--ly, -80px)) rotate(var(--rot, 0deg)) scale(0.35); opacity: 0; }
}
.cycle-leak-particle { animation: cycle-leak 2.2s ease-out infinite; will-change: transform, opacity; }
/* Cycle col 2 — server rack LED pulse */
@keyframes cycle-led-pulse {
0%, 100% { opacity: 0.4; }
50% { opacity: 1; box-shadow: 0 0 6px currentColor; }
}
.cycle-led { animation: cycle-led-pulse 1.2s ease-in-out infinite; }
.cycle-led-2 { animation-delay: 0.3s; }
.cycle-led-3 { animation-delay: 0.6s; }
/* Warning chevrons slide */
@keyframes cycle-chev-slide {
0%, 100% { transform: translateX(0); opacity: 0.4; }
50% { transform: translateX(-6px); opacity: 1; }
}
.cycle-chev { animation: cycle-chev-slide 1.4s ease-in-out infinite; }
.cycle-chev-2 { animation-delay: 0.2s; }
.cycle-chev-3 { animation-delay: 0.4s; }
/* Cycle col 2 — STAMP NON CONFORME impact (overshoot + screen shake + flash red) */
@keyframes cycle-stamp-impact {
0% { transform: translate(-50%,-50%) rotate(-25deg) scale(3); opacity: 0; filter: blur(3px); }
55% { transform: translate(-50%,-50%) rotate(-9deg) scale(0.90); opacity: 1; filter: blur(0); }
70% { transform: translate(-50%,-50%) rotate(-6deg) scale(1.08); }
100% { transform: translate(-50%,-50%) rotate(-8deg) scale(1); opacity: 1; }
}
.cycle-stamp.is-visible { animation: cycle-stamp-impact 700ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards; will-change: transform, opacity; }
/* Screen shake on stamp impact */
@keyframes cycle-shake {
0%, 100% { transform: translate(0,0); }
20% { transform: translate(-3px, 2px); }
40% { transform: translate(4px, -2px); }
60% { transform: translate(-2px, 3px); }
80% { transform: translate(3px, -1px); }
}
.cycle-col-shake.is-visible { animation: cycle-shake 320ms ease-out 480ms 1; }
/* Cycle col 2 — flash background rouge à l'impact stamp (amplifié) */
@keyframes cycle-flash-red {
0% { background-color: rgba(239,68,68,0); }
30% { background-color: rgba(239,68,68,0.22); }
100% { background-color: rgba(239,68,68,0); }
}
.cycle-col-flash.is-visible { animation: cycle-flash-red 700ms ease-out 480ms forwards; }
/* Cycle col 2 — sound icon pulse */
@keyframes cycle-alert-pulse {
0%, 100% { opacity: 0.7; transform: scale(1); }
50% { opacity: 1; transform: scale(1.2); }
}
.cycle-alert-icon { animation: cycle-alert-pulse 0.9s ease-in-out infinite; }
/* === ROUND 5 — Col 3 — checkmark draw CYAN (au lieu de vert) === */
.cycle-check-svg path { stroke-dasharray: 24; stroke-dashoffset: 24; transition: stroke-dashoffset 400ms ease-out 220ms; stroke: #06b6d4; stroke-width: 3.5; }
.cycle-check-svg.is-visible path { stroke-dashoffset: 0; }
/* === ROUND 5 — Col 3 — badge "Loi 25 conforme" pulse BLUE+CYAN (palette brand) === */
@keyframes cycle-badge-pulse {
0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(37,99,235,0.45); }
50% { transform: scale(1.04); box-shadow: 0 0 0 7px rgba(37,99,235,0); }
}
.cycle-conforme-badge.is-visible { animation: cycle-badge-pulse 2.4s ease-in-out infinite 600ms; }
/* === ROUND 5 — Cycle "Économies annuelles" cards — hover lift + glow BRAND BLUE === */
.cycle-savings-card {
transition: transform 240ms cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 240ms ease-out, border-color 240ms ease-out;
}
.cycle-savings-card:hover {
transform: translateY(-3px);
box-shadow: 0 12px 28px -8px rgba(37,99,235,0.30), 0 6px 14px -4px rgba(11,15,26,0.08);
border-color: rgba(37,99,235,0.45);
}
/* Save chip subtle pulse */
@keyframes cycle-save-chip {
0%, 100% { opacity: 0.85; }
50% { opacity: 1; }
}
.cycle-save-chip { animation: cycle-save-chip 2.5s ease-in-out infinite; }
/* === ROUND 5 — Phase 4 zoom-out subtle (suggère "all options revealed") === */
.cycle-grid-zoom { transition: transform 700ms ease-out; transform: scale(1.02); }
.cycle-grid-zoom.is-revealed { transform: scale(1); }
/* Mobile — désactiver fuites particules + glow heavy + screen shake */
@media (max-width: 768px) {
.cycle-leak-particle, .cycle-orb, .cycle-orb-2 { display: none; }
.cycle-card-dictia.is-visible { animation: none; }
.cycle-col-shake.is-visible, .cycle-col-flash.is-visible { animation: none; }
.cycle-ring-draw-svg.is-visible circle { animation: none; }
.cycle-col-card { opacity: 1 !important; transform: none !important; }
}
/* Reduced motion — TOUT figé (mode statique) */
@media (prefers-reduced-motion: reduce) {
.cycle-pulse, .cycle-card-dictia, .cycle-ring-outer, .cycle-ring-inner,
.cycle-clock, .cycle-leak-particle, .cycle-live-dot, .cycle-conforme-badge,
.cycle-line.cycle-line-flow, .cycle-stamp, .cycle-col-flash, .cycle-col-shake,
.cycle-led, .cycle-chev, .cycle-alert-icon, .cycle-orb, .cycle-orb-2,
.cycle-save-chip, .cycle-paper-stack.is-active .cycle-paper,
.cycle-ring-draw-svg.is-visible circle, .cycle-beam-dot { animation: none !important; }
.cycle-paper-stack .cycle-paper { opacity: 1 !important; }
.cycle-reveal, .cycle-reveal-up { opacity: 1 !important; transform: none !important; }
.cycle-line { stroke-dashoffset: 0 !important; }
.cycle-check-svg path { stroke-dashoffset: 0 !important; }
.cycle-savings-card { transition: none !important; }
.cycle-col-card { opacity: 1 !important; transform: none !important; transition: none !important; }
.cycle-beam { width: 100% !important; transition: none !important; }
.cycle-grid-zoom { transform: none !important; transition: none !important; }
}
</style>
<section
class="cycle-section-bg bg-white py-20 border-y border-brand-border relative overflow-hidden"
aria-labelledby="cycle-title"
x-data="{
phase: 0,
observer: null,
priceHumain: 0,
sav1: 0, sav2: 0, sav3: 0,
/* easeOutCubic counter helper */
countTo(prop, target, duration) {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) { this[prop] = target; return; }
const start = performance.now();
const tick = (now) => {
const t = Math.min(1, (now - start) / duration);
const eased = 1 - Math.pow(1 - t, 3);
this[prop] = Math.round(target * eased);
if (t < 1) requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
},
fmt(n) { return n.toLocaleString('fr-CA').replace(//g, ' '); },
/* Round 5 — beam progressive % par phase */
get beamPct() {
if (this.phase >= 4) return 100;
if (this.phase >= 3) return 66;
if (this.phase >= 2) return 33;
return 0;
}
}"
x-init="
observer = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting && phase === 0) {
/* Round 5 — phase delays cinématiques 400/1100/2000/3100ms */
setTimeout(() => { phase = 1; }, 400);
setTimeout(() => { phase = 2; countTo('priceHumain', 315, 1400); }, 1100);
setTimeout(() => phase = 3, 2000);
setTimeout(() => {
phase = 4;
/* Savings counters fire 700ms after col 3 reveal (round 5) */
setTimeout(() => {
countTo('sav1', 3924, 1500);
countTo('sav2', 6924, 1500);
countTo('sav3', 2004, 1500);
}, 700);
}, 3100);
observer.disconnect();
}
});
}, { rootMargin: '-60px' });
observer.observe($el);
"
>
{# Round 5 — Subtle grid pattern bg + dot grid overlay #}
<div class="cycle-grid-bg absolute inset-0 pointer-events-none" aria-hidden="true"></div>
<div class="absolute inset-0 pointer-events-none opacity-50" aria-hidden="true"
style="background-image: radial-gradient(circle, rgba(11,15,26,0.04) 1px, transparent 1px); background-size: 28px 28px;"></div>
{# Round 5 — Decorative orbs ambiance #}
<div class="cycle-orb absolute -top-20 -left-20 w-[360px] h-[360px] rounded-full pointer-events-none" aria-hidden="true"
style="background: radial-gradient(circle, rgba(37,99,235,0.10) 0%, transparent 65%); filter: blur(40px);"></div>
<div class="cycle-orb cycle-orb-2 absolute -bottom-24 -right-24 w-[420px] h-[420px] rounded-full pointer-events-none" aria-hidden="true"
style="background: radial-gradient(circle, rgba(6,182,212,0.08) 0%, transparent 65%); filter: blur(50px);"></div>
<div class="relative max-w-[1200px] mx-auto px-6">
<div class="max-w-2xl mb-10">
<p class="eyebrow text-amber-600 mb-4 inline-flex items-center gap-1.5">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" class="w-3.5 h-3.5" aria-hidden="true"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
CADRE RÉGLEMENTAIRE
</p>
<h2 id="cycle-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black mb-3 text-brand-navy">
Trois options. <span class="text-brand-navy/30">Une seule est conforme.</span>
</h2>
<p class="text-base text-brand-navy/70">
La Loi&nbsp;25, le Cloud Act américain et le Guide IA du Barreau du Québec délimitent vos options&nbsp;
<span class="font-semibold text-brand-b1">pas votre volonté.</span>
</p>
</div>
<div class="max-w-5xl mx-auto">
{# Nœud source : "Réunion en cours" #}
<div class="flex justify-center mb-2">
<div class="relative inline-flex items-center gap-2.5 px-5 py-3 bg-brand-bg border border-brand-border rounded">
<template x-if="phase >= 1">
<span class="absolute inset-0 cycle-pulse pointer-events-none border border-brand-border rounded"></span>
</template>
<template x-if="phase >= 1">
<span class="absolute inset-0 cycle-pulse cycle-pulse-2 pointer-events-none border border-brand-border rounded"></span>
</template>
<template x-if="phase >= 1">
<span class="absolute inset-0 cycle-pulse cycle-pulse-3 pointer-events-none border border-brand-border rounded"></span>
</template>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-3.5 h-3.5 shrink-0 text-brand-navy/40" aria-hidden="true"><path d="M3 21h18"/><path d="M5 21V8l7-4 7 4v13"/><path d="M9 21v-6h6v6"/></svg>
<span class="text-[11px] tracking-wide text-brand-navy/55">Réunion en cours — données confidentielles</span>
<span class="cycle-live-dot inline-block w-1.5 h-1.5 rounded-full bg-red-500 shrink-0" aria-hidden="true"></span>
<span class="font-mono font-bold text-[8px] tracking-[0.22em] uppercase text-red-500/85">Live</span>
</div>
</div>
{# Lignes de connexion SVG — de la source vers les 3 colonnes #}
<div class="relative h-10">
<svg class="w-full h-full" viewBox="0 0 100 10" preserveAspectRatio="none" aria-hidden="true">
<line class="cycle-line cycle-line-flow" :class="phase >= 1 ? 'is-visible' : ''"
x1="50" y1="0" x2="14" y2="10"
stroke="rgba(148,163,184,0.55)" stroke-width="0.3" />
<line class="cycle-line cycle-line-flow" :class="phase >= 1 ? 'is-visible' : ''"
x1="50" y1="0" x2="50" y2="10"
stroke="rgba(239,68,68,0.55)" stroke-width="0.3"
style="transition-delay: 80ms; animation-delay: 0.4s;" />
<line class="cycle-line cycle-line-flow" :class="phase >= 1 ? 'is-visible' : ''"
x1="50" y1="0" x2="86" y2="10"
stroke="rgba(6,182,212,0.7)" stroke-width="0.3"
style="transition-delay: 160ms; animation-delay: 0.8s;" />
</svg>
</div>
{# Round 5 — Connecting horizontal beam progressive 0->33->66->100 #}
<div class="relative h-3 mb-4" aria-hidden="true">
<div class="absolute inset-x-0 top-1/2 -translate-y-1/2 h-[2px] bg-brand-border/60"></div>
<div class="cycle-beam absolute top-1/2 left-0 -translate-y-1/2 rounded-full"
:style="`--beam: ${beamPct}%`"></div>
<template x-if="phase >= 1">
<div class="cycle-beam-dot absolute"></div>
</template>
</div>
{# Grille 3 colonnes — équivalence du grid-cols-[2fr_2fr_3fr] desktop, avec spotlight active #}
<div class="cycle-grid-zoom grid grid-cols-1 md:grid-cols-[2fr_2fr_3fr] gap-3 items-stretch"
:class="phase >= 4 ? 'is-revealed' : ''">
{# COL 1 — Retranscription humaine (sténographe) #}
<div class="cycle-col-card cycle-reveal-up flex flex-col rounded border border-brand-border bg-brand-bg overflow-hidden relative"
:class="{ 'is-visible': phase >= 2, 'is-revealed': phase >= 4, 'is-active': phase === 2 }">
{# Subtle "cahier" lines bg #}
<div class="absolute inset-0 pointer-events-none opacity-[0.04]" aria-hidden="true"
style="background-image: linear-gradient(rgba(11,15,26,1) 1px, transparent 1px); background-size: 100% 18px;"></div>
<div class="relative px-5 py-3 border-b border-brand-border">
<div class="font-mono text-[10px] tracking-[0.28em] uppercase text-brand-navy/40 mb-0.5">01</div>
<div class="font-bold text-sm text-brand-navy/55">Retranscription humaine</div>
</div>
<div class="relative flex-1 flex flex-col items-center justify-center px-5 py-7 gap-6">
{# Stack de papiers (round 5 : stagger reveal stagger 180ms) #}
<div class="cycle-paper-stack relative w-[100px] h-[90px]"
:class="phase >= 2 ? 'is-active' : ''"
aria-hidden="true">
{% for offset in [0,1,2,3,4] %}
<div class="cycle-paper absolute left-0 right-0 h-[17px] rounded-sm flex items-center px-2 gap-1.5 bg-white border border-brand-border shadow-sm"
style="bottom: {{ offset * 15 }}px; --rot: {{ '1deg' if offset is even else '-1deg' }}; transform: rotate({{ '1deg' if offset is even else '-1deg' }}); animation-delay: {{ offset * 180 }}ms;">
<div class="w-2.5 h-1.5 rounded-sm bg-brand-navy/15 shrink-0"></div>
<div class="flex-1 h-[1.5px] rounded-full bg-brand-navy/10"></div>
</div>
{% endfor %}
</div>
<div class="flex items-center gap-4 w-full">
<div class="flex flex-col items-center gap-1.5 flex-1">
<span class="cycle-clock">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5 text-brand-navy/45" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>
</span>
<span class="font-mono text-[10px] text-brand-navy/45">5&nbsp;jours</span>
</div>
<div class="w-px h-8 bg-brand-border"></div>
<div class="flex flex-col items-center gap-1.5 flex-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5 text-brand-navy/45" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>
<span class="font-mono text-[10px] text-brand-navy/45">~85&nbsp;$/h</span>
</div>
</div>
</div>
<div class="relative px-5 pb-5 pt-4 border-t border-brand-border">
<div class="flex items-baseline gap-1.5 mb-2">
<span class="font-black text-3xl leading-none text-brand-navy/65 tabular-nums" x-text="priceHumain">315</span>
<span class="text-xs text-brand-navy/45">$&nbsp;/&nbsp;réunion</span>
</div>
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded bg-red-50 border border-red-100">
<span class="w-1 h-1 rounded-full bg-red-400"></span>
<span class="font-mono text-[9px] tracking-[0.16em] uppercase text-red-500/80">Lent · Coûteux</span>
</span>
</div>
</div>
{# COL 2 — IA cloud américaine (DRAMATIC : USA map + 12 paquets + screen shake) #}
<div class="cycle-col-card cycle-reveal-up cycle-col-flash cycle-col-shake relative flex flex-col rounded border border-red-200 bg-red-50/30 overflow-hidden"
:class="{ 'is-visible': phase >= 3, 'is-revealed': phase >= 4, 'is-active': phase === 3 }"
style="transition-delay: 120ms;">
{# Round 5 — USA map silhouette subtle background #}
<svg class="absolute pointer-events-none" aria-hidden="true"
style="top: 22%; right: 8%; width: 110px; height: 70px; opacity: 0.55;"
viewBox="0 0 110 70" preserveAspectRatio="xMidYMid meet">
<path d="M5,25 L8,18 L18,15 L32,12 L48,10 L65,11 L82,14 L92,18 L102,22 L105,28 L100,35 L95,42 L88,48 L78,52 L68,55 L58,57 L48,58 L38,57 L28,55 L18,52 L10,45 L6,38 Z"
fill="rgba(239,68,68,0.05)" stroke="rgba(239,68,68,0.20)" stroke-width="0.8"/>
<circle cx="78" cy="32" r="2.2" fill="rgba(239,68,68,0.55)"/>
<circle cx="78" cy="32" r="4.5" fill="none" stroke="rgba(239,68,68,0.35)" stroke-width="0.6"/>
</svg>
{# Overlay légal NON CONFORME (phase 3) — STAMP huissier amplifié round 5 #}
<div class="absolute inset-0 z-30 flex flex-col items-center justify-center pointer-events-none"
:class="phase >= 3 ? 'opacity-100' : 'opacity-0'"
style="transition: opacity 220ms ease-out; backdrop-filter: blur(6px); background: rgba(255,255,255,0.78);">
<div class="cycle-stamp absolute top-1/2 left-1/2 flex flex-col items-center gap-3 px-7 py-6 rounded bg-white border-[3px] border-red-500 shadow-[0_12px_40px_-6px_rgba(239,68,68,0.65)]"
:class="phase >= 3 ? 'is-visible' : ''"
style="transform: translate(-50%,-50%) scale(0); opacity: 0;">
<div class="flex items-center gap-2.5">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" class="w-8 h-8 text-red-500" aria-hidden="true"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="cycle-alert-icon w-5 h-5 text-red-500" aria-hidden="true"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><line x1="23" y1="9" x2="17" y2="15"/><line x1="17" y1="9" x2="23" y2="15"/></svg>
</div>
<div class="text-center">
<div class="font-mono font-black tracking-[0.16em] text-2xl uppercase text-red-600 leading-none">NON CONFORME</div>
<div class="font-mono text-[10px] tracking-[0.20em] uppercase mt-2 text-red-500/70">Loi&nbsp;25 · Cloud Act américain</div>
</div>
</div>
</div>
<div class="relative px-5 py-3 border-b border-red-100">
<div class="font-mono text-[10px] tracking-[0.28em] uppercase text-red-500/60 mb-0.5">02</div>
<div class="font-bold text-sm text-red-500/75">IA cloud américaine</div>
</div>
<div class="relative flex-1 flex flex-col items-center justify-center px-5 py-6 gap-5">
{# Server rack icon (round 5) — 3 stacked rectangles avec leds rouges #}
<div class="relative flex flex-col items-center gap-2">
<div class="relative w-16 h-14 rounded-sm flex flex-col items-center justify-center gap-[3px] bg-red-100/60 border border-red-200 px-2 py-1.5" aria-hidden="true">
{% for rack_i in [0,1,2] %}
<div class="w-full h-2.5 rounded-[2px] bg-red-50/80 border border-red-200/70 flex items-center px-1 gap-1">
<span class="cycle-led{% if rack_i > 0 %} cycle-led-{{ rack_i + 1 }}{% endif %} w-1 h-1 rounded-full bg-red-500"></span>
<span class="cycle-led cycle-led-{{ ((rack_i + 1) % 3) + 1 }} w-1 h-1 rounded-full bg-red-500"></span>
<span class="flex-1 h-[1px] rounded-full bg-red-300/60"></span>
<span class="cycle-led cycle-led-{{ ((rack_i + 2) % 3) + 1 }} w-1 h-1 rounded-full bg-red-500"></span>
</div>
{% endfor %}
{# Round 5 — 12 paquets data .wav/.aac qui s'envolent vers TOP-RIGHT (USA direction) #}
{% for i in range(12) %}
<span class="cycle-leak-particle absolute pointer-events-none flex items-center justify-center font-mono font-bold uppercase text-white text-[6px] rounded-sm"
style="left: {{ 25 + (i % 4) * 12 }}%; top: {{ 30 + (i % 3) * 18 }}%;
width: 18px; height: 9px;
background: rgba(239,68,68,0.65); border: 1px solid rgba(239,68,68,0.85);
box-shadow: 0 0 4px rgba(239,68,68,0.55);
--lx: {{ 50 + i*6 }}px; --ly: -{{ 70 + (i % 4) * 15 }}px;
--rot: {{ -8 + (i % 5) * 4 }}deg;
animation-delay: {{ i * 0.18 }}s;">{{ '.wav' if i is even else '.aac' }}</span>
{% endfor %}
</div>
<div class="px-2 py-0.5 rounded bg-red-50 border border-red-100 inline-flex items-center gap-1">
<span class="cycle-chev font-mono font-bold text-[8px] text-red-500/70">&lt;</span>
<span class="cycle-chev cycle-chev-2 font-mono font-bold text-[8px] text-red-500/70">&lt;</span>
<span class="cycle-chev cycle-chev-3 font-mono font-bold text-[8px] text-red-500/70">&lt;</span>
<span class="font-mono text-[9px] tracking-[0.22em] uppercase text-red-500/65">USA</span>
</div>
</div>
<div class="flex flex-col gap-2.5 w-full">
{% for risk in [
('cloud', 'Données transférées aux États-Unis'),
('alert', "Entraînement de l'IA publique"),
('users', '500&ndash;750&nbsp;$/mois · facturation par utilisateur')
] %}
<div class="flex items-start gap-2.5">
{% if risk[0] == 'cloud' %}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 mt-0.5 shrink-0 text-red-500/60" aria-hidden="true"><path d="M2 12s3-7 10-7c2.5 0 4.7.9 6.4 2.4M22 12s-3 7-10 7c-2.5 0-4.7-.9-6.4-2.4"/><line x1="2" y1="2" x2="22" y2="22"/></svg>
{% elif risk[0] == 'alert' %}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 mt-0.5 shrink-0 text-red-500/60" aria-hidden="true"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
{% else %}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 mt-0.5 shrink-0 text-red-500/60" aria-hidden="true"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/></svg>
{% endif %}
<span class="text-[12px] leading-snug text-brand-navy/55">{{ risk[1] | safe }}</span>
</div>
{% endfor %}
</div>
</div>
<div class="relative px-5 pb-5 pt-4 border-t border-red-100">
<div class="flex items-center gap-2">
<div class="w-1.5 h-1.5 rounded-full bg-red-400"></div>
<span class="font-mono text-[9px] tracking-[0.20em] uppercase text-red-500/70">Violation légale possible</span>
</div>
</div>
</div>
{# COL 3 — DictIA (featured, 3fr) — PALETTE BRAND BLUE+CYAN+FUCHSIA #}
<div class="cycle-col-card cycle-card-dictia cycle-reveal-up relative flex flex-col rounded border border-brand-b1/40 bg-gradient-to-br from-brand-b1/[0.07] via-brand-b2/[0.04] to-brand-b3/[0.04] overflow-hidden"
:class="{ 'is-visible': phase >= 4, 'is-revealed': phase >= 4, 'is-active': phase >= 4 }"
style="transition-delay: 240ms;">
{# Halo ambiant blue->cyan->fuchsia #}
<div class="absolute inset-0 pointer-events-none" aria-hidden="true"
style="background: radial-gradient(ellipse 80% 35% at 50% 0%, rgba(6,182,212,0.18) 0%, transparent 65%), radial-gradient(ellipse 60% 50% at 50% 100%, rgba(37,99,235,0.10) 0%, transparent 70%);"></div>
{# Round 5 — Quebec province outline subtle background #}
<svg class="absolute pointer-events-none" aria-hidden="true"
style="top: 18%; right: 6%; width: 95px; height: 95px; opacity: 0.6;"
viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet">
<path d="M22,10 L35,8 L48,9 L62,11 L74,15 L82,22 L86,32 L88,45 L85,58 L80,68 L72,76 L62,82 L52,85 L42,84 L32,80 L24,72 L18,62 L15,50 L14,38 L17,26 Z"
fill="rgba(37,99,235,0.06)" stroke="rgba(37,99,235,0.28)" stroke-width="1"/>
<circle cx="48" cy="62" r="2.2" fill="rgba(6,182,212,0.85)"/>
<circle cx="48" cy="62" r="5" fill="none" stroke="rgba(6,182,212,0.45)" stroke-width="0.8"/>
<text x="40" y="71" font-family="Inter, sans-serif" font-size="7" fill="rgba(192,38,211,0.7)" font-weight="bold">QC</text>
</svg>
<div class="relative px-5 py-3 border-b border-brand-b1/20 flex items-center gap-2.5">
{# Round 5 — mini logo DictIA (subtle) connecte le brand visuellement #}
<img src="{{ url_for('static', filename='images/dictia-logo.png') }}"
alt="" aria-hidden="true"
class="w-7 h-7 rounded shrink-0 opacity-95"
width="28" height="28" loading="lazy">
{# Numéro 03 → checkmark CYAN (round 5 : palette brand) #}
<span class="relative w-5 h-5 rounded-full flex items-center justify-center shrink-0 bg-brand-b2/20 border border-brand-b2/50" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" class="cycle-check-svg w-3 h-3"
:class="phase >= 4 ? 'is-visible' : ''"><path d="M5 13l4 4L19 7"/></svg>
</span>
<span class="text-[11px] uppercase tracking-[0.22em] font-semibold text-brand-b1/85">Solution</span>
{# Badge top-right : Loi 25 conforme — round 5 : gradient brand blue->fuchsia #}
<span class="cycle-conforme-badge ml-auto inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 border border-brand-b2/50"
:class="phase >= 4 ? 'is-visible' : ''" aria-label="Loi 25 conforme"
style="background: linear-gradient(135deg, rgba(37,99,235,0.22), rgba(192,38,211,0.18));">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-2.5 h-2.5 text-white" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="M9 12l2 2 4-4"/></svg>
<span class="font-mono font-bold text-[8px] tracking-[0.16em] uppercase text-white">Loi&nbsp;25 conforme</span>
</span>
</div>
<div class="relative flex-1 flex flex-col items-center justify-center px-5 py-7 gap-5">
{# Shield + microphone (round 5) + halo multi-couches blue+cyan + drawn ring SVG #}
<div class="relative flex items-center justify-center" aria-hidden="true">
{# Couche externe drawn ring SVG (fuchsia accent) #}
<svg class="cycle-ring-draw-svg absolute" :class="phase >= 4 ? 'is-visible' : ''"
width="108" height="108" viewBox="0 0 80 80">
<circle cx="40" cy="40" r="35" fill="none" stroke="rgba(192,38,211,0.32)" stroke-width="1" stroke-linecap="round"/>
</svg>
<span class="cycle-ring-outer absolute rounded-full border border-brand-b1/25"
:class="phase >= 4 ? 'is-visible' : ''"
style="width: 96px; height: 96px;"></span>
<span class="cycle-ring-inner absolute rounded-full border border-brand-b2/40"
:class="phase >= 4 ? 'is-visible' : ''"
style="width: 68px; height: 68px;"></span>
<div class="relative w-[56px] h-[56px] rounded flex items-center justify-center bg-gradient-to-br from-brand-b1/20 via-brand-b2/15 to-brand-b3/10 border border-brand-b2/45"
style="box-shadow: 0 0 24px rgba(37,99,235,0.35), 0 0 48px rgba(6,182,212,0.18);">
{# Shield extérieur #}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7 text-brand-b1">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
</svg>
{# Microphone à l'intérieur #}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="w-3.5 h-3.5 text-brand-b2 absolute"
style="top: 50%; left: 50%; transform: translate(-50%, -45%);">
<rect x="9" y="6" width="6" height="9" rx="3"/>
<path d="M12 17v3"/>
<path d="M9 20h6"/>
</svg>
</div>
</div>
{# Flux sécurisé (palette brand b1+b2) #}
<div class="cycle-reveal flex items-center gap-2 px-3.5 py-1.5 rounded bg-brand-b1/10 border border-brand-b2/25"
:class="phase >= 4 ? 'is-visible' : ''"
style="transition-delay: 350ms;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-3 h-3 shrink-0 text-brand-b1/65" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/></svg>
<span class="text-[11px] text-brand-b1/80 font-medium">Transcription sécurisée</span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-3 h-3 text-brand-b2" aria-hidden="true"><path d="M5 12h14M13 5l7 7-7 7"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-3 h-3 shrink-0 text-brand-b1/65" aria-hidden="true"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg>
</div>
{# Badges conformité (palette brand b1+b2 cyan checkmark) #}
<div class="flex flex-col gap-2 w-full">
{% for badge_label in ['Loi 25 conforme', '100&nbsp;% hébergé au Québec', 'Données jamais partagées'] %}
<div class="cycle-reveal flex items-center gap-2.5 px-3.5 py-2.5 rounded bg-brand-b1/[0.08] border border-brand-b2/25"
:class="phase >= 4 ? 'is-visible' : ''"
style="transition-delay: {{ 180 + loop.index0 * 170 }}ms;">
<span class="w-4 h-4 rounded-full flex items-center justify-center shrink-0 bg-brand-b2/20 border border-brand-b2/45">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="w-2.5 h-2.5 text-brand-b2" aria-hidden="true"><path d="M5 13l4 4L19 7"/></svg>
</span>
<span class="text-[12px] font-medium text-brand-navy/80">{{ badge_label | safe }}</span>
</div>
{% endfor %}
</div>
</div>
<div class="relative px-5 pb-5 pt-4 border-t border-brand-b2/20">
<div class="flex items-center gap-2 mb-2.5">
<span class="flex-1 h-px bg-gradient-to-r from-transparent via-brand-b1/30 to-brand-b2/30"></span>
<span class="font-mono font-bold text-[10px] uppercase tracking-[0.20em] grad-text">UTILISATEURS ILLIMITÉS</span>
<span class="flex-1 h-px bg-gradient-to-l from-transparent via-brand-b3/30 to-brand-b2/30"></span>
</div>
<p class="text-center text-[10px] mb-3.5 text-brand-b1/65">Zéro frais caché · Du jamais vu</p>
<div class="flex items-baseline gap-2">
{# Round 5 — big number en grad-text (blue->cyan->fuchsia) — Cloud BASIC v7.0 #}
<span class="font-black leading-none text-5xl grad-text">189</span>
<span class="text-sm text-brand-navy/55 mb-0.5 font-semibold">$ / mois</span>
</div>
<div class="flex items-center gap-1.5 mt-2">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-3 h-3 text-brand-b2" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
<p class="text-[10px] text-brand-b1/70 font-medium">Conforme Loi&nbsp;25 · 100&nbsp;% Québec</p>
</div>
</div>
</div>
</div>
{# Section "Économies annuelles · 25 utilisateurs" — round 5 : 3 cards palette brand + chips contextuels #}
<div class="cycle-reveal-up mt-12"
:class="phase >= 4 ? 'is-visible' : ''"
style="transition-delay: 700ms;">
<div class="flex items-center justify-center gap-2.5 mb-5">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 text-brand-b1" aria-hidden="true"><polyline points="23 18 13.5 8.5 8.5 13.5 1 6"/><polyline points="17 18 23 18 23 12"/></svg>
<span class="font-mono font-bold text-[10px] uppercase tracking-[0.22em] text-brand-navy/65">
Économies annuelles · 25&nbsp;utilisateurs
</span>
<span class="flex-1 h-px bg-brand-border max-w-[60px]" aria-hidden="true"></span>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-3" role="list" aria-label="Trois comparaisons d'économies annuelles">
{% for sav in [
{'icon': 'otter', 'val_prop': 'sav1', 'val_static': '3&nbsp;924', 'label': 'vs Otter.ai', 'sub': 'IA cloud US', 'chip': 'IA cloud US'},
{'icon': 'teams', 'val_prop': 'sav2', 'val_static': '6&nbsp;924', 'label': 'vs MS Teams', 'sub': 'Copilot premium', 'chip': 'Copilot premium'},
{'icon': 'scribe','val_prop': 'sav3', 'val_static': '2&nbsp;004', 'label': 'vs Sténographe','sub': 'Service humain', 'chip': 'Service humain'}
] %}
<div class="cycle-savings-card flex items-center gap-3 px-4 py-4 rounded border border-brand-border bg-white relative overflow-hidden" role="listitem">
{# Round 5 — subtle blue tint bg layer #}
<div class="absolute inset-0 pointer-events-none opacity-60" aria-hidden="true"
style="background: linear-gradient(135deg, rgba(37,99,235,0.04) 0%, transparent 60%);"></div>
<span class="relative shrink-0 w-10 h-10 rounded flex items-center justify-center bg-brand-b1/10 border border-brand-b2/30 text-brand-b1" aria-hidden="true">
{% if sav.icon == 'otter' %}
{# Round 5 — silhouette de loutre stylisée (tête + oreilles + moustache) #}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5">
<path d="M5 8 C5 5, 7 3, 8 3 C8.5 4, 9 5, 8.5 6"/>
<path d="M19 8 C19 5, 17 3, 16 3 C15.5 4, 15 5, 15.5 6"/>
<ellipse cx="12" cy="13" rx="6" ry="6.5"/>
<circle cx="9.5" cy="11.5" r="0.8" fill="currentColor"/>
<circle cx="14.5" cy="11.5" r="0.8" fill="currentColor"/>
<path d="M11 15 Q12 16 13 15"/>
<path d="M9 16 L7.5 16.5 M15 16 L16.5 16.5"/>
</svg>
{% elif sav.icon == 'teams' %}
{# Round 5 — carré avec "T" stylisé Teams #}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5">
<rect x="3" y="3" width="18" height="18" rx="2"/>
<path d="M8 8h8"/>
<path d="M12 8v9"/>
</svg>
{% else %}
{# Round 5 — silhouette humain (sténographe) avec doc #}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5">
<circle cx="9" cy="6" r="3"/>
<path d="M3 21v-1.5C3 17 5.7 15 9 15s6 2 6 4.5V21"/>
<rect x="15.5" y="11" width="6" height="8" rx="0.8"/>
<line x1="17" y1="13.5" x2="20" y2="13.5"/>
<line x1="17" y1="15.5" x2="20" y2="15.5"/>
</svg>
{% endif %}
</span>
<div class="relative flex-1 min-w-0">
{# Round 5 — save chip "+économies" au-dessus du big number #}
<span class="cycle-save-chip inline-flex items-center gap-1 rounded-full px-1.5 py-px mb-1 bg-brand-b1/10 border border-brand-b1/25">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-2 h-2 text-brand-b1" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
<span class="font-mono font-bold text-[7px] tracking-[0.16em] uppercase text-brand-b1">+économies</span>
</span>
<div class="flex items-baseline">
{# Round 5 — big number en grad-text (blue->cyan->fuchsia) #}
{# OQLF NBSP entre nombre et $ — préservé en placeholder statique pour SEO/no-JS, JS écrase via x-html #}
<span class="font-black text-2xl leading-none grad-text tabular-nums"
x-html="fmt({{ sav.val_prop }}) + '&nbsp;<span class=&quot;text-sm font-bold&quot;>$</span>'">{{ sav.val_static | safe }}&nbsp;$</span>
</div>
<div class="text-[11px] font-semibold text-brand-navy/85 mt-1">{{ sav.label }}</div>
<div class="inline-flex items-center gap-1 mt-0.5">
<span class="w-1 h-1 rounded-full bg-brand-b2"></span>
<span class="font-mono text-[9px] tracking-[0.10em] text-brand-navy/50">{{ sav.chip }}</span>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</section>
{# ===== SOLUTION (S de PAS) ===== #}
<section class="relative bg-brand-navy text-white py-20 overflow-hidden" aria-labelledby="solution-title">
{# Single subtle orb in solution bg — less busy than hero #}
<div class="absolute top-1/2 right-1/4 w-[500px] h-[500px] rounded-full pointer-events-none" aria-hidden="true"
style="background: radial-gradient(circle, rgba(6,182,212,0.08) 0%, transparent 60%); filter: blur(60px);"></div>
<div class="relative max-w-[1200px] mx-auto px-6">
<div class="text-center max-w-2xl mx-auto mb-12">
<p class="eyebrow grad-text mb-4">LA SOLUTION — DICTIA</p>
<h2 id="solution-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black mb-4">
Conforme <span class="grad-text">par design</span>, pas par défaut.
</h2>
<p class="text-lg text-white/70">
DictIA reproduit la précision des outils cloud — WhisperX Large-v3 + Mistral 7B — mais avec une architecture où vos données ne quittent jamais vos murs ou nos serveurs OVH Beauharnois.
</p>
</div>
{# 3 solution pillars — dark cards with grad-bg icon corners #}
<div class="grid md:grid-cols-3 gap-6">
{% for pillar in [
('100&nbsp;% local', 'Vos données ne sortent jamais de vos murs. Inférence GPU sur place ou VPS Québec OVH Beauharnois — vous gardez le contrôle, l\'audit, et les clés.'),
('Conforme Loi&nbsp;25', 'Audit trail intégré (qui a écouté quoi, quand). Déclaration CAI prête. Consentement explicite tracé pour chaque enregistrement. Code source AGPL v3 — transparence vérifiable.'),
('Précision FR-CA', 'WhisperX Large-v3 fine-tuné français québécois. Diarisation pyannote 8 locuteurs. Résumés Mistral 7B local — aucune connexion OpenAI/Google/Microsoft.')
] %}
<article class="bg-white/[0.05] backdrop-blur-sm p-6 rounded border border-white/[0.08]">
<h3 class="text-lg font-bold mb-2 text-white">{{ pillar[0] | safe }}</h3>
<p class="text-sm text-white/70 leading-relaxed">{{ pillar[1] | safe }}</p>
</article>
{% endfor %}
</div>
</div>
</section>
{# ===== WAVE — Onde de transformation chaos→ordre ===== #}
{# Source canonique : InnovA-AI/Website-Sanity/components/sections/dictai-wave.tsx
Animation : slider mouse-X interactif morphe les barres rouge→cyan, particules pain→solution.
Mobile : bouton toggle (activé/désactivé) — pas de mousemove. #}
<style>
/* Wave bars — chaque barre est composée d'un wrapper + 2 spans (rouge et cyan) qu'on fait fader */
.wave-bar { transition: opacity 60ms linear, transform 60ms linear; }
/* Wave pain labels — flottent légèrement */
@keyframes wave-pain-float {
0%, 100% { transform: translate(-50%, calc(-50% - 2px)); opacity: 0.7; }
50% { transform: translate(-50%, calc(-50% + 3px)); opacity: 0.45; }
}
.wave-pain { animation: wave-pain-float 3.4s ease-in-out infinite; }
.wave-pain:nth-child(2) { animation-delay: 0.4s; }
.wave-pain:nth-child(3) { animation-delay: 0.8s; }
.wave-pain:nth-child(4) { animation-delay: 1.2s; }
/* Wave fall particles — chute des $ et h */
@keyframes wave-fall {
0% { transform: translate(-50%, 0) scale(1); opacity: 0.85; }
50% { transform: translate(-50%, 22px) scale(0.7); opacity: 0.30; }
100% { transform: translate(-50%, 42px) scale(0.3); opacity: 0; }
}
.wave-fall { animation: wave-fall 2.4s ease-out infinite; }
/* Wave flow particles — flux cyan gauche→droite (état ordre) */
@keyframes wave-flow {
0% { left: 12%; opacity: 0; }
20% { opacity: 0.85; }
100% { left: 84%; opacity: 0; }
}
.wave-flow { animation: wave-flow 1.7s linear infinite; }
/* Wave hint — chevron CTA hover droite */
@keyframes wave-hint-pulse {
0%, 100% { transform: translateX(0); opacity: 0.55; }
50% { transform: translateX(7px); opacity: 1; }
}
.wave-hint { animation: wave-hint-pulse 1.5s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
.wave-pain, .wave-fall, .wave-flow, .wave-hint { animation: none !important; }
}
</style>
<section class="bg-brand-navy text-white py-20 border-t border-white/[0.06] overflow-hidden" aria-labelledby="wave-title"
x-data="{
p: 0,
isMobile: false,
activated: false,
init() {
this.isMobile = window.innerWidth < 768;
window.addEventListener('resize', () => { this.isMobile = window.innerWidth < 768; });
},
onMove(e) {
if (this.isMobile) return;
const r = e.currentTarget.getBoundingClientRect();
this.p = Math.max(0, Math.min(1, (e.clientX - r.left) / r.width));
},
onLeave() { if (!this.isMobile) this.p = 0; },
toggle() { this.activated = !this.activated; this.p = this.activated ? 1 : 0; },
clamp(v) { return Math.max(0, Math.min(1, v)); },
get chaosOp() { return this.clamp(1 - (this.p - 0.12) / 0.42); },
get orderOp() { return this.clamp((this.p - 0.42) / 0.42); }
}">
<div class="max-w-[1200px] mx-auto px-6">
<div class="flex items-center gap-3 mb-5">
<span class="eyebrow text-brand-b3">Le problème</span>
<span class="flex-1 h-px bg-white/10 max-w-[60px]"></span>
</div>
<h2 id="wave-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black leading-[1.05] mb-3">
La transcription manuelle <span class="grad-text">coûte cher</span>.
</h2>
<p class="text-base text-white/55 mb-8 max-w-lg hidden md:block">
Déplacez votre curseur vers la droite pour transcrire l'avenir.
</p>
<p class="text-base text-white/55 mb-8 max-w-lg md:hidden">
Touchez pour basculer entre&nbsp;: aujourd'hui (chaos) et avec DictIA (ordre).
</p>
{# Scène interactive — aspect-ratio 2/1 desktop, 4/3 mobile #}
<div
class="relative w-full max-w-5xl mx-auto overflow-hidden rounded border border-white/[0.06]"
style="aspect-ratio: 2 / 1; min-height: 280px; cursor: ew-resize;"
:style="isMobile ? 'cursor: pointer; aspect-ratio: 4/3' : ''"
@mousemove="onMove($event)"
@mouseleave="onLeave()"
@click="if (isMobile) toggle()"
role="img"
aria-label="Visualisation interactive : transcription manuelle (chaos rouge) vs DictIA (ordre cyan)"
>
{# Fond dégradé radial dynamique #}
<div class="absolute inset-0"
:style="`background: radial-gradient(ellipse 75% 75% at 50% 50%, rgba(${Math.round(45 + (11-45)*p)},${Math.round(10 + (15-10)*p)},${Math.round(12 + (40-12)*p)},0.55) 0%, rgba(11,15,26,1) 100%);`"
aria-hidden="true"></div>
{# Grille subtile #}
<div class="absolute inset-0 opacity-[0.02]"
style="background-image: linear-gradient(rgba(255,255,255,0.1) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.1) 1px, transparent 1px); background-size: 40px 40px;"
aria-hidden="true"></div>
{# SVG : 30 barres d'onde — calculées par macro Jinja (chaos vs order par lerp p) #}
<svg class="absolute inset-0 w-full h-full pointer-events-none z-10" viewBox="0 0 100 50" preserveAspectRatio="none" aria-hidden="true">
<defs>
<filter id="dw-glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="0.9" result="blur"/>
<feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
</defs>
{# Fixed bar geometry — nous générons 30 barres avec hauteur min/max statique;
l'opacité chaos vs order est gérée par 2 groupes superposés contrôlés par x-attr style. #}
{% set NUM_BARS = 30 %}
{% set chaos_heights = [10.5, 13.2, 15.8, 17.4, 18.1, 17.6, 15.9, 13.6, 11.2, 9.5, 8.7, 9.4, 11.7, 14.5, 17.2, 18.8, 18.6, 16.9, 14.3, 11.5, 9.2, 8.4, 9.8, 12.6, 15.5, 17.5, 17.8, 16.2, 13.4, 10.1] %}
{% set order_heights = [0.85, 0.95, 1.05, 1.15, 1.10, 1.00, 0.92, 0.88, 1.00, 1.12, 1.18, 1.14, 1.05, 0.95, 0.88, 0.92, 1.05, 1.15, 1.18, 1.10, 1.00, 0.92, 0.88, 0.95, 1.10, 1.18, 1.15, 1.05, 0.95, 0.90] %}
{% for i in range(NUM_BARS) %}
{% set spacing = 84 / NUM_BARS %}
{% set bw = spacing * 0.52 %}
{% set cx = 8 + i * spacing + spacing / 2 %}
{# Chaos bar — visible quand p ≈ 0 #}
<g class="wave-bar" :style="`opacity: ${1 - p}`">
<rect x="{{ cx - bw / 2 }}" y="{{ 25 - chaos_heights[i] / 2 }}" width="{{ bw }}" height="{{ chaos_heights[i] }}" rx="{{ bw / 2 }}" fill="rgba(255,75,75,0.78)"/>
</g>
{# Order bar — visible quand p ≈ 1 #}
<g class="wave-bar" :style="`opacity: ${p}`">
<rect x="{{ cx - bw / 2 }}" y="{{ 25 - order_heights[i] / 2 }}" width="{{ bw }}" height="{{ order_heights[i] }}" rx="{{ bw / 2 }}" fill="rgba(96,165,250,0.6)"/>
</g>
{% endfor %}
{# Bouclier holographique (état ordre) #}
<path d="M 50 8 L 68 14 L 68 27 Q 68 38 50 43 Q 32 38 32 27 L 32 14 Z"
fill="none" stroke="rgba(96,165,250,0.55)" stroke-width="0.55" stroke-dasharray="2.5 2"
:style="`opacity: ${orderOp * 0.4}`"/>
</svg>
{# Icône Microphone (gauche) #}
<div class="absolute z-20 left-[3%] top-1/2 -translate-y-1/2 flex flex-col items-center gap-1.5"
:style="`opacity: ${Math.max(0.25, 1 - p * 0.55)}`" aria-hidden="true">
<div class="w-10 h-10 md:w-12 md:h-12 rounded flex items-center justify-center backdrop-blur-md border"
:style="`background: rgba(${Math.round(255 + (167-255)*p)},${Math.round(60 + (139-60)*p)},${Math.round(60 + (250-60)*p)},0.12); border-color: rgba(${Math.round(255 + (167-255)*p)},${Math.round(80 + (139-80)*p)},${Math.round(80 + (250-80)*p)},0.28);`">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 md:w-5 md:h-5"
:style="`color: ${p < 0.5 ? '#FF6B6B' : '#60a5fa'}`"><rect x="9" y="2" width="6" height="12" rx="3"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="22"/></svg>
</div>
<span class="text-[8px] font-medium" :style="`color: ${p < 0.5 ? 'rgba(255,107,107,0.65)' : 'rgba(96,165,250,0.65)'}`">Audio</span>
</div>
{# Icône Texte transcrit (droite) #}
<div class="absolute z-20 right-[3%] top-1/2 -translate-y-1/2 flex flex-col items-center gap-1.5"
:style="`opacity: ${orderOp}`" aria-hidden="true">
<div class="w-10 h-10 md:w-12 md:h-12 rounded flex items-center justify-center backdrop-blur-md border border-brand-b1/30"
style="background: linear-gradient(135deg, rgba(96,165,250,0.15), rgba(37,99,235,0.07)); box-shadow: 0 0 18px rgba(96,165,250,0.18);">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 md:w-5 md:h-5 text-brand-b1"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="9" y1="13" x2="15" y2="13"/></svg>
</div>
<span class="text-[8px] font-medium text-brand-b1/70">Texte</span>
</div>
{# Particules tombantes chaos ($, h) #}
<div class="absolute inset-0 z-10 pointer-events-none" :style="`opacity: ${chaosOp}`" aria-hidden="true">
{% for fp in [
{'x': 22, 'd': 0, 'type': 'money'},
{'x': 36, 'd': 0.9, 'type': 'time'},
{'x': 50, 'd': 1.5, 'type': 'money'},
{'x': 63, 'd': 0.4, 'type': 'time'},
{'x': 77, 'd': 1.2, 'type': 'money'},
{'x': 30, 'd': 1.9, 'type': 'time'},
{'x': 56, 'd': 0.7, 'type': 'money'},
{'x': 43, 'd': 2.1, 'type': 'time'}
] %}
<span class="wave-fall absolute font-bold text-[9px] md:text-[10px]"
style="left: {{ fp.x }}%; top: 30%; color: {{ '#FF6B6B' if fp.type == 'money' else '#FFD93D' }}; text-shadow: 0 0 8px rgba({{ '255,80,80' if fp.type == 'money' else '255,210,60' }},0.6); animation-delay: {{ fp.d }}s;">{{ '-$' if fp.type == 'money' else '-1h' }}</span>
{% endfor %}
</div>
{# Flux cyan (ordre) #}
<div class="absolute inset-0 z-10 pointer-events-none" :style="`opacity: ${orderOp}`" aria-hidden="true">
{% for fy in [18, 21, 24, 27, 30] %}
<span class="wave-flow absolute w-1 h-1 rounded-full bg-brand-b1"
style="top: {{ fy }}%; left: 12%; box-shadow: 0 0 5px rgba(96,165,250,0.7); animation-delay: {{ loop.index0 * 0.32 }}s;"></span>
{% endfor %}
</div>
{# Étiquettes douleur (chaos) #}
<div class="absolute inset-0 pointer-events-none" :style="`opacity: ${chaosOp}`" aria-hidden="true">
{% for pain in [
{'text': '4 à 6h pour transcrire 1h', 'x': 20, 'y': 12},
{'text': '600 à 1&nbsp;800&nbsp;$ / enregistrement', 'x': 64, 'y': 10},
{'text': 'Délais de 48h à 5 jours', 'x': 74, 'y': 38},
{'text': 'Risques de confidentialité', 'x': 18, 'y': 40}
] %}
<div class="wave-pain absolute text-[8px] md:text-[10px] font-medium px-2 py-0.5 rounded whitespace-nowrap"
style="left: {{ pain.x }}%; top: {{ pain.y }}%; transform: translate(-50%, -50%); background: rgba(255,50,50,0.1); border: 1px solid rgba(255,80,80,0.18); color: rgba(255,150,130,0.85);">
{{ pain.text | safe }}
</div>
{% endfor %}
</div>
{# Cartes solution (ordre) #}
<div class="absolute inset-0 pointer-events-none" :style="`opacity: ${orderOp}`" aria-hidden="true">
{% for sol in [
{'text': '~2 min pour 1h d\'audio', 'x': 20, 'y': 12},
{'text': 'Dès 189&nbsp;$/mois — illimité', 'x': 64, 'y': 10},
{'text': '90&nbsp;%+ d\'économies', 'x': 74, 'y': 38},
{'text': '100&nbsp;% Confidentiel — Hébergé au Qc', 'x': 18, 'y': 40}
] %}
<div class="absolute flex items-center gap-1.5 px-2 py-1 rounded backdrop-blur-sm"
style="left: {{ sol.x }}%; top: {{ sol.y }}%; transform: translate(-50%, -50%); background: rgba(96,165,250,0.10); border: 1px solid rgba(96,165,250,0.20);">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="w-2.5 h-2.5 text-brand-b1 shrink-0" aria-hidden="true"><path d="M5 13l4 4L19 7"/></svg>
<span class="text-[8px] md:text-[10px] text-white/80 whitespace-nowrap">{{ sol.text | safe }}</span>
</div>
{% endfor %}
</div>
{# Séparateur central + hint "→" #}
<div class="absolute left-1/2 top-0 bottom-0 w-px z-10 hidden md:block pointer-events-none"
:style="`opacity: ${Math.max(0, 1 - p * 2.8)}; background: linear-gradient(to bottom, transparent 10%, rgba(255,255,255,0.06) 30%, rgba(255,255,255,0.06) 70%, transparent 90%);`"
aria-hidden="true">
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 whitespace-nowrap"
:style="`opacity: ${Math.max(0, 1 - p * 3.2)}`">
<span class="wave-hint inline-block text-[9px] text-white/40 tracking-wider uppercase px-3 py-1 rounded-full border border-white/[0.08] bg-brand-navy/80 backdrop-blur-sm">
Survolez vers la droite →
</span>
</div>
</div>
{# Toggle mobile #}
<button type="button"
x-show="isMobile"
@click.stop="toggle()"
class="absolute bottom-3 left-1/2 -translate-x-1/2 z-30 inline-flex items-center gap-2 px-5 py-2.5 rounded text-xs font-bold transition-all border focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
:class="activated ? 'border-brand-b1/30 text-brand-b1' : 'border-white/15 text-white/70'"
:style="activated ? 'background: linear-gradient(135deg, rgba(96,165,250,0.2), rgba(37,99,235,0.1));' : 'background: linear-gradient(135deg, rgba(255,255,255,0.08), rgba(255,255,255,0.03));'"
x-cloak>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-3.5 h-3.5"><rect x="9" y="2" width="6" height="12" rx="3"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/></svg>
<span x-text="activated ? 'Voir sans DictIA' : 'Activer DictIA'"></span>
</button>
</div>
</div>
</section>
{# ===== PIPELINE — Comment ça marche : 4 étapes ===== #}
{# Source canonique : InnovA-AI/Website-Sanity/components/sections/dictai-pipeline.tsx
Animation traduite : Framer Motion auto-advance + sweep ring → Alpine.js setInterval + CSS keyframes #}
<style>
/* Sweep ring on the active pipeline node — restarts via :key (DOM swap) */
@keyframes pipeline-sweep {
from { stroke-dashoffset: 257.6; }
to { stroke-dashoffset: 0; }
}
.pipeline-sweep-ring {
stroke-dasharray: 257.6; /* 2π × 41 */
animation: pipeline-sweep 2400ms linear forwards;
}
/* Underline sweep on the description card — restart on Alpine swap */
@keyframes pipeline-underline {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
.pipeline-underline {
transform-origin: left;
animation: pipeline-underline 2400ms linear forwards;
}
/* Glowing dot trailing the active step on the track */
.pipeline-dot {
transition: left 450ms cubic-bezier(0.4, 0, 0.2, 1);
}
/* Filled track from start to active step */
.pipeline-track-fill {
transition: transform 450ms cubic-bezier(0.4, 0, 0.2, 1);
transform-origin: left;
}
/* Reduced motion : freeze auto-advance + remove all animations */
@media (prefers-reduced-motion: reduce) {
.pipeline-sweep-ring, .pipeline-underline, .pipeline-dot, .pipeline-track-fill {
animation: none !important;
transition: none !important;
}
}
/* Hide Alpine x-show elements until Alpine hydrates (avoids FOUC flash of all 4 cards) */
[x-cloak] { display: none !important; }
</style>
<section class="bg-white py-20 border-y border-brand-border" aria-labelledby="pipeline-title">
<div class="max-w-[1100px] mx-auto px-6">
<div class="text-center max-w-2xl mx-auto mb-14">
<p class="eyebrow grad-text mb-4">COMMENT&nbsp;ÇA MARCHE</p>
<h2 id="pipeline-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black mb-4 text-brand-navy">
Du fichier au résumé <span class="grad-text">— en temps réel</span>.
</h2>
<p class="text-lg text-brand-navy/70">
Aucune installation côté utilisateur, aucune conversion préalable. DictIA orchestre l'ensemble du pipeline — du téléversement à l'export — en moins de deux minutes pour une heure d'audio.
</p>
<p class="text-sm text-brand-navy/55 mt-3 italic">
Survolez une fonctionnalité pour voir la machine en action. Glissez pour calculer votre gain de productivité.
</p>
</div>
{# Pipeline track + 4 nodes — Alpine state drives all visuals.
active = 0..3 (current step), prefers-reduced-motion stops the autoplay (handled in x-init). #}
<div
class="relative max-w-4xl mx-auto"
x-data="{
active: 0,
timer: null,
steps: [
{ positionPct: 12.5, fillPct: 0 },
{ positionPct: 37.5, fillPct: 33.33 },
{ positionPct: 62.5, fillPct: 66.66 },
{ positionPct: 87.5, fillPct: 100 }
],
startTimer() {
if (this.timer) clearInterval(this.timer);
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
this.timer = setInterval(() => { this.active = (this.active + 1) % 4; }, 2400);
},
select(i) { this.active = i; this.startTimer(); }
}"
x-init="startTimer()"
>
{# Background + filled track (top: 44px = node center 88px / 2) #}
<div class="absolute h-px bg-brand-border" style="top: 44px; left: 12.5%; right: 12.5%;" aria-hidden="true"></div>
<div
class="pipeline-track-fill absolute h-[2px]"
style="top: 43px; left: 12.5%; width: 75%; background: linear-gradient(90deg, #2563eb 0%, #06b6d4 100%);"
:style="`transform: scaleX(${steps[active].fillPct / 100})`"
aria-hidden="true"
></div>
{# Moving glowing dot — left transitions via CSS #}
<div
class="pipeline-dot absolute w-3.5 h-3.5 rounded-full bg-white pointer-events-none z-10"
style="top: 44px; transform: translate(-50%, -50%); box-shadow: 0 0 18px rgba(6,182,212,0.55), 0 0 36px rgba(37,99,235,0.35);"
:style="`left: ${steps[active].positionPct}%`"
aria-hidden="true"
></div>
{# Nodes grid — 4 cols #}
<div class="grid grid-cols-4 gap-2">
{# Each step rendered statically; active state computed via :class. #}
{% set steps = [
('Uploader', 'Audio ou vidéo', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>',
"Glissez n'importe quel format — MP3, WAV, MP4, MKV. Aucune conversion nécessaire. DictIA accepte tout sans préparation."),
('GPU transcrit', 'WhisperX Large-v3', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><rect x="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6"/><path d="M9 1v3M15 1v3M9 20v3M15 20v3M20 9h3M20 14h3M1 9h3M1 14h3"/></svg>',
"Votre GPU traite le fichier 60× plus vite qu'un humain. 1&nbsp;heure d'audio → 2&nbsp;minutes. Précision 95&nbsp;%+ en français canadien. 100&nbsp;% en local — zéro cloud étranger."),
('IA structure', 'Mistral 7B / Nemo 12B', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24 2.5 2.5 0 0 1 1.98-3A2.5 2.5 0 0 1 9.5 2z"/><path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24 2.5 2.5 0 0 0-1.98-3A2.5 2.5 0 0 0 14.5 2z"/></svg>',
"L'IA identifie automatiquement chaque locuteur, génère un résumé structuré, extrait les points d'action et répond à vos questions sur le contenu transcrit."),
('Exporter', 'Dans votre écosystème', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>',
"DOCX, SRT, VTT, JSON, Obsidian, Logseq. Connectez avec n8n, Zapier ou Make pour automatiser vos workflows — zéro copier-coller.")
] %}
{% for label, sublabel, icon, desc in steps %}
<button
type="button"
@click="select({{ loop.index0 }})"
class="group flex flex-col items-center gap-4 cursor-pointer select-none focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-4 rounded-none p-2"
:aria-current="active === {{ loop.index0 }} ? 'step' : 'false'"
aria-label="Étape {{ loop.index }} sur 4 : {{ label }} — {{ sublabel }}"
>
{# Circle node — visual state driven by :class (active / completed / pending) #}
<span
class="relative w-[88px] h-[88px] rounded-full border-2 flex items-center justify-center transition-all duration-300"
:class="{
'border-brand-b1 bg-brand-b1/10 scale-105 shadow-[0_0_28px_rgba(37,99,235,0.35)]': active === {{ loop.index0 }},
'border-brand-b3/60 bg-brand-b3/10': active > {{ loop.index0 }},
'border-brand-border bg-white': active < {{ loop.index0 }}
}"
aria-hidden="true"
>
<span
class="transition-opacity duration-300"
:class="active >= {{ loop.index0 }} ? 'opacity-100 text-brand-b1' : 'opacity-30 text-brand-navy/30'"
>{{ icon | safe }}</span>
{# Step badge — top-right corner with index #}
<span
class="absolute -top-1.5 -right-1.5 w-6 h-6 rounded-full flex items-center justify-center text-[11px] font-bold transition-colors duration-300"
:class="active >= {{ loop.index0 }} ? 'bg-brand-b1 text-white' : 'bg-brand-border text-brand-navy/40'"
>{{ loop.index }}</span>
{# Sweep progress ring — only visible on active step. Re-keyed via :key when active changes. #}
<template x-if="active === {{ loop.index0 }}">
<svg
:key="active"
class="absolute inset-0 w-full h-full -rotate-90 pointer-events-none"
viewBox="0 0 88 88"
aria-hidden="true"
>
<circle class="pipeline-sweep-ring" cx="44" cy="44" r="41" fill="none" stroke="#2563eb" stroke-width="2" stroke-linecap="round"></circle>
</svg>
</template>
</span>
{# Label + sublabel — opacity dimmed on pending steps #}
<span
class="text-center px-1 transition-opacity duration-300 block"
:class="active >= {{ loop.index0 }} ? 'opacity-100' : 'opacity-40'"
>
<span class="block font-bold text-sm leading-tight mb-0.5 text-brand-navy">{{ label }}</span>
<span class="block text-xs text-brand-navy/70 leading-tight">{{ sublabel }}</span>
</span>
</button>
{% endfor %}
</div>
{# Step description card — swaps with x-show + x-transition. Underline sweep restarts via :key on active. #}
<div class="mt-12 max-w-2xl mx-auto min-h-[110px] flex items-center justify-center">
{% for label, sublabel, icon, desc in steps %}
<div
x-show="active === {{ loop.index0 }}"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 translate-y-2"
x-transition:enter-end="opacity-100 translate-y-0"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
class="relative w-full bg-brand-bg border border-brand-border px-7 py-6 text-center rounded overflow-hidden"
x-cloak
>
<p class="text-sm text-brand-navy/80 leading-relaxed">{{ desc | safe }}</p>
<span
:key="active"
class="pipeline-underline absolute bottom-0 left-0 h-[2px] w-full"
style="background: linear-gradient(90deg, #2563eb, #06b6d4);"
aria-hidden="true"
></span>
</div>
{% endfor %}
</div>
{# Progress dots (mobile-friendly + click target redundancy) #}
<div class="flex justify-center gap-2 mt-8" role="tablist" aria-label="Sélectionner une étape">
{% for label, sublabel, icon, desc in steps %}
<button
type="button"
role="tab"
@click="select({{ loop.index0 }})"
class="h-1 rounded-full transition-all duration-300 focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
:class="{
'w-8 bg-brand-b1': active === {{ loop.index0 }},
'w-8 bg-brand-b3/60': active > {{ loop.index0 }},
'w-2 bg-brand-border': active < {{ loop.index0 }}
}"
:aria-selected="active === {{ loop.index0 }} ? 'true' : 'false'"
aria-label="Aller à l'étape {{ loop.index }} : {{ label }}"
></button>
{% endfor %}
</div>
</div>
</div>
</section>
{# ===== LANGUES + IA LOCALE ===== #}
{# Section compacte : grille 99+ langues détectées (gauche) + carte IA Mistral 7B LOCAL (droite). #}
<section class="bg-brand-bg py-16 border-y border-brand-border" aria-labelledby="langues-title">
<div class="max-w-[1100px] mx-auto px-6">
<div class="grid lg:grid-cols-2 gap-8">
{# Colonne gauche — 99+ langues #}
<div class="bg-white rounded border border-brand-border p-6">
<div class="flex items-center gap-3 mb-4">
<span class="grad-bg w-9 h-9 rounded flex items-center justify-center text-white" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
</span>
<div>
<h2 id="langues-title" class="text-lg font-bold text-brand-navy leading-tight">99+ langues détectées</h2>
<p class="text-xs text-brand-navy/55 mt-0.5">WhisperX Large-v3 · multilingue par défaut</p>
</div>
</div>
{# Grille codes langue — 12 puces (FR mis en avant via grad-bg) #}
<ul class="grid grid-cols-6 sm:grid-cols-8 gap-1.5 mb-4" role="list" aria-label="Quelques langues supportées">
{% for code in ['FR','EN','ES','DE','PT','IT','NL','PL','ZH','JA','KO','AR','RU','HI','TR','VI','TH','SV','DA','NO','FI'] %}
<li class="text-center">
<span class="block text-[10px] font-mono font-bold py-1 rounded
{{ 'grad-bg text-white' if code == 'FR' else 'bg-brand-bg border border-brand-border text-brand-navy/70' }}">
{{ code }}
</span>
</li>
{% endfor %}
</ul>
<p class="text-xs text-brand-navy/55 flex items-center gap-1.5">
<span class="inline-block w-1.5 h-1.5 rounded-full bg-brand-b3"></span>
<span><strong class="text-brand-navy">Auto</strong> · Détection automatique de la langue à l'upload</span>
</p>
</div>
{# Colonne droite — IA intégrée Mistral 7B LOCAL #}
<div class="bg-brand-navy text-white rounded p-6 relative overflow-hidden">
{# Subtle orb décoratif #}
<div class="absolute -top-12 -right-12 w-48 h-48 rounded-full pointer-events-none" aria-hidden="true"
style="background: radial-gradient(circle, rgba(6,182,212,0.15) 0%, transparent 60%); filter: blur(30px);"></div>
<div class="relative">
<div class="flex items-center gap-3 mb-4">
<span class="bg-white/10 border border-white/15 w-9 h-9 rounded flex items-center justify-center text-brand-b3" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5"><path d="M12 2a3 3 0 0 0-3 3v1a3 3 0 0 0-3 3v1a3 3 0 0 0 0 6v1a3 3 0 0 0 3 3 3 3 0 0 0 6 0 3 3 0 0 0 3-3v-1a3 3 0 0 0 0-6V9a3 3 0 0 0-3-3V5a3 3 0 0 0-3-3z"/></svg>
</span>
<div>
<h3 class="text-lg font-bold leading-tight">IA intégrée — Mistral 7B <span class="grad-text">(LOCAL)</span></h3>
<p class="text-xs text-white/55 mt-0.5">Inférence sur votre GPU · zéro cloud étranger</p>
</div>
</div>
<ul class="space-y-2 text-sm" role="list">
<li class="flex items-start gap-2">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 mt-0.5 text-brand-b3 shrink-0" aria-hidden="true"><path d="M5 13l4 4L19 7"/></svg>
<span class="text-white/80"><strong class="text-white">Résumé · Points d'action · Q&amp;R</strong></span>
</li>
<li class="flex items-start gap-2">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 mt-0.5 text-brand-b3 shrink-0" aria-hidden="true"><path d="M5 13l4 4L19 7"/></svg>
<span class="text-white/80">Données hébergées sur <strong class="text-white">vos serveurs</strong> · jamais partagées</span>
</li>
<li class="flex items-start gap-2">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 mt-0.5 text-brand-b3 shrink-0" aria-hidden="true"><path d="M5 13l4 4L19 7"/></svg>
<span class="text-white/80">Zéro connexion <strong class="text-white">OpenAI · Google · Microsoft</strong></span>
</li>
<li class="flex items-start gap-2">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 mt-0.5 text-brand-b3 shrink-0" aria-hidden="true"><path d="M5 13l4 4L19 7"/></svg>
<span class="text-white/80">Inférence <strong class="text-white">hors-ligne</strong> · résultats en secondes</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</section>
{# ===== BENTO FEATURES ===== #}
<section class="bg-white py-20" aria-labelledby="bento-title">
<div class="max-w-[1060px] mx-auto px-6">
<div class="text-center max-w-2xl mx-auto mb-12">
<p class="eyebrow grad-text mb-4">FONCTIONNALITÉS</p>
<h2 id="bento-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black text-brand-navy">
Tout ce dont vous avez besoin, <span class="grad-text">rien que vous n'ayez besoin</span>.
</h2>
</div>
{# 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.
Icon SVGs (heroicons-style outline) are inlined directly because the macro renders `icon | safe`. #}
{% from 'macros/bento.html' import bento_card %}
{%- set icon_microphone = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><rect x="9" y="2" width="6" height="12" rx="3"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="22"/></svg>' -%}
{%- set icon_users = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>' -%}
{%- set icon_document = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="9" y1="13" x2="15" y2="13"/><line x1="9" y1="17" x2="15" y2="17"/></svg>' -%}
{%- set icon_chat = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>' -%}
{%- set icon_export = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>' -%}
{%- set icon_plug = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><path d="M9 2v6"/><path d="M15 2v6"/><path d="M5 8h14v4a5 5 0 0 1-5 5h-4a5 5 0 0 1-5-5z"/><path d="M12 17v5"/></svg>' -%}
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-[1.5px] bg-brand-border rounded overflow-hidden">
{{ bento_card('01', 'Transcription WhisperX', 'Large-v3 fine-tuné FR-CA. Précision 95&nbsp;%+ sur réunions, dictées, audiences (méthodologie disponible sur demande).', icon_microphone) }}
{{ bento_card('02', 'Diarisation 8 locuteurs', 'pyannote sépare automatiquement les intervenants. Identification par embeddings vocaux.', icon_users) }}
{{ 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.', icon_document) }}
{{ bento_card('04', 'Q&amp;R sur enregistrement', 'Posez des questions à vos réunions. RAG local sur embeddings sentence-transformers.', icon_chat) }}
{{ bento_card('05', 'Exports multiples', 'DOCX, PDF, SRT, VTT, TXT, JSON, MD. Formats avocat, notaire, CPA.', icon_export) }}
{{ bento_card('06', 'Intégrations', 'Word, Outlook, Teams, Notion, Obsidian, Zapier, Make, n8n.', icon_plug) }}
</div>
</div>
</section>
{# ===== HUB — Réseau d'intégrations ===== #}
{# Source canonique : InnovA-AI/Website-Sanity/components/sections/dictai-hub.tsx
Animation : SVG pur avec <animateMotion> sur bezier paths — aucune lib JS, aucune config.
Particules cyan circulent en continu de DictIA → Hubs → Outils. #}
<style>
/* DictIA central node — pulsing rings (decorative, native CSS) */
@keyframes hub-ring-pulse {
0% { transform: scale(1); opacity: 0.55; }
70% { transform: scale(2.6); opacity: 0; }
100% { transform: scale(2.6); opacity: 0; }
}
.hub-ring { animation: hub-ring-pulse 3s ease-out infinite; }
.hub-ring-2 { animation-delay: 1s; }
.hub-ring-3 { animation-delay: 2s; }
/* DictIA core breathing glow */
@keyframes hub-core-breathe {
0%, 100% { box-shadow: 0 0 26px rgba(6,182,212,0.55), 0 0 56px rgba(6,182,212,0.18); }
50% { box-shadow: 0 0 38px rgba(6,182,212,0.75), 0 0 72px rgba(6,182,212,0.28); }
}
.hub-core { animation: hub-core-breathe 3.5s ease-in-out infinite; }
/* Reduced motion : freeze everything */
@media (prefers-reduced-motion: reduce) {
.hub-ring, .hub-core { animation: none !important; }
/* Disable SVG animateMotion via display:none on the animateMotion elements (browser respects this). */
.hub-network-svg animateMotion { display: none !important; }
}
</style>
<section class="bg-brand-navy text-white py-20 overflow-hidden relative" aria-labelledby="hub-title">
{# Decorative cosmic orb — aqua, mid-section #}
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[700px] h-[700px] rounded-full pointer-events-none" aria-hidden="true"
style="background: radial-gradient(circle, rgba(6,182,212,0.10) 0%, transparent 60%); filter: blur(60px);"></div>
<div class="relative max-w-[1200px] mx-auto px-6">
<div class="text-center max-w-2xl mx-auto mb-12">
<p class="eyebrow grad-text mb-4">INTÉGRATIONS — RÉSEAU OUVERT</p>
<h2 id="hub-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black mb-4">
DictIA <span class="grad-text">se connecte à tout</span>.
</h2>
<p class="text-lg text-white/70">
Transcriptions et résumés acheminés automatiquement vers vos outils de travail. Trois familles d'intégrations couvrent l'essentiel des flux documentaires, communication et automatisation.
</p>
</div>
{# Network diagram — SVG with native <animateMotion> for particle flow.
Layout : DictIA center-top, 3 hubs (Documents | Communication | Automatisation), 9 tools below. #}
<div class="relative max-w-4xl mx-auto">
<svg
viewBox="0 0 900 460"
preserveAspectRatio="xMidYMid meet"
class="hub-network-svg w-full h-auto"
role="img"
aria-labelledby="hub-svg-title hub-svg-desc"
>
<title id="hub-svg-title">Diagramme du réseau d'intégrations DictIA</title>
<desc id="hub-svg-desc">DictIA au centre, connecté à trois familles : Documents (Word, Google Docs, Obsidian), Communication (Outlook, Teams, Notion) et Automatisation (Zapier, Make, n8n).</desc>
<defs>
{# Bezier path definitions — DictIA top → 3 hubs (mid) #}
<path id="hub-path-docs" d="M450,54 C450,127 168,127 168,200" />
<path id="hub-path-comm" d="M450,54 C450,127 450,127 450,200" />
<path id="hub-path-auto" d="M450,54 C450,127 732,127 732,200" />
{# Bezier paths — Hub → Tool #}
{# Documents hub (168, 200) → Word(62,390), Google(175,390), Obsidian(282,390) #}
<path id="hub-path-word" d="M168,216 C168,303 62,303 62,390" />
<path id="hub-path-google" d="M168,216 C168,303 175,303 175,390" />
<path id="hub-path-obsidian" d="M168,216 C168,303 282,303 282,390" />
{# Communication hub (450, 200) → Outlook(358,390), Teams(450,390), Notion(542,390) #}
<path id="hub-path-outlook" d="M450,216 C450,303 358,303 358,390" />
<path id="hub-path-teams" d="M450,216 C450,303 450,303 450,390" />
<path id="hub-path-notion" d="M450,216 C450,303 542,303 542,390" />
{# Automatisation hub (732, 200) → Zapier(618,390), Make(722,390), n8n(828,390) #}
<path id="hub-path-zapier" d="M732,216 C732,303 618,303 618,390" />
<path id="hub-path-make" d="M732,216 C732,303 722,303 722,390" />
<path id="hub-path-n8n" d="M732,216 C732,303 828,303 828,390" />
</defs>
{# Render bezier connections (visible strokes) — DictIA → Hubs (thick), Hub → Tools (thin) #}
{# DictIA → Hubs #}
<use href="#hub-path-docs" fill="none" stroke="#2563eb" stroke-width="1.5" opacity="0.55" />
<use href="#hub-path-comm" fill="none" stroke="#06b6d4" stroke-width="1.5" opacity="0.55" />
<use href="#hub-path-auto" fill="none" stroke="#8b5cf6" stroke-width="1.5" opacity="0.55" />
{# Hub → Tool (lighter) #}
<use href="#hub-path-word" fill="none" stroke="#2563eb" stroke-width="1" opacity="0.30" />
<use href="#hub-path-google" fill="none" stroke="#2563eb" stroke-width="1" opacity="0.30" />
<use href="#hub-path-obsidian" fill="none" stroke="#2563eb" stroke-width="1" opacity="0.30" />
<use href="#hub-path-outlook" fill="none" stroke="#06b6d4" stroke-width="1" opacity="0.30" />
<use href="#hub-path-teams" fill="none" stroke="#06b6d4" stroke-width="1" opacity="0.30" />
<use href="#hub-path-notion" fill="none" stroke="#06b6d4" stroke-width="1" opacity="0.30" />
<use href="#hub-path-zapier" fill="none" stroke="#8b5cf6" stroke-width="1" opacity="0.30" />
<use href="#hub-path-make" fill="none" stroke="#8b5cf6" stroke-width="1" opacity="0.30" />
<use href="#hub-path-n8n" fill="none" stroke="#8b5cf6" stroke-width="1" opacity="0.30" />
{# Animated particles — DictIA → Hubs (bigger, dual particles) #}
{% for hub in [
('docs', '#2563eb', '0s', '2.4s'),
('comm', '#06b6d4', '0.8s', '2.4s'),
('auto', '#8b5cf6', '1.6s', '2.4s')
] %}
<circle r="3.2" fill="{{ hub[1] }}" opacity="0.95">
<animateMotion dur="{{ hub[3] }}" repeatCount="indefinite" begin="{{ hub[2] }}">
<mpath href="#hub-path-{{ hub[0] }}" />
</animateMotion>
</circle>
<circle r="1.8" fill="{{ hub[1] }}" opacity="0.6">
<animateMotion dur="{{ hub[3] }}" repeatCount="indefinite" begin="{{ hub[2] }}s + 1.2s">
<mpath href="#hub-path-{{ hub[0] }}" />
</animateMotion>
</circle>
{% endfor %}
{# Animated particles — Hub → Tool (smaller, fast) #}
{% for tool in [
('word', '#2563eb', '0s'),
('google', '#2563eb', '0.4s'),
('obsidian', '#2563eb', '0.8s'),
('outlook', '#06b6d4', '0.2s'),
('teams', '#06b6d4', '0.6s'),
('notion', '#06b6d4', '1.0s'),
('zapier', '#8b5cf6', '0.1s'),
('make', '#8b5cf6', '0.5s'),
('n8n', '#8b5cf6', '0.9s')
] %}
<circle r="2.2" fill="{{ tool[1] }}" opacity="0.75">
<animateMotion dur="2s" repeatCount="indefinite" begin="{{ tool[2] }}">
<mpath href="#hub-path-{{ tool[0] }}" />
</animateMotion>
</circle>
{% endfor %}
</svg>
{# DictIA central node — absolute positioned over the SVG (z-10) #}
<div class="absolute pointer-events-none" style="left: 50%; top: 11.7%; transform: translate(-50%, -50%); z-index: 10;" aria-hidden="true">
<div class="relative w-[68px] h-[68px]">
{# Pulsing rings #}
<span class="hub-ring absolute inset-0 rounded-full border-2 border-brand-b2/40"></span>
<span class="hub-ring hub-ring-2 absolute inset-0 rounded-full border-2 border-brand-b2/40"></span>
<span class="hub-ring hub-ring-3 absolute inset-0 rounded-full border-2 border-brand-b2/40"></span>
{# Core orb #}
<span class="hub-core absolute inset-0 rounded-full grad-bg flex items-center justify-center">
<span class="font-black text-white text-[13px] tracking-tight">DictIA</span>
</span>
</div>
</div>
{# Hub labels overlay — positioned to match SVG hub coords (200/460 = 43.5% top, 168/900=18.7% / 450/900=50% / 732/900=81.3% left) #}
{% set hub_data = [
('Documents', 'Word · Google Docs · Obsidian', '18.7%', 'rgba(37,99,235,0.10)', 'rgba(37,99,235,0.30)', 'text-violet-300'),
('Communication', 'Outlook · Teams · Notion', '50%', 'rgba(6,182,212,0.10)', 'rgba(6,182,212,0.30)', 'text-cyan-300'),
('Automatisation', 'Zapier · Make · n8n', '81.3%', 'rgba(139,92,246,0.10)', 'rgba(139,92,246,0.30)', 'text-purple-300')
] %}
{% for name, sub, leftpos, bgcol, brdcol, txtcol in hub_data %}
<div
class="absolute pointer-events-none px-3 py-2 rounded-full backdrop-blur-sm whitespace-nowrap"
style="left: {{ leftpos }}; top: 43.5%; transform: translate(-50%, -50%); background: {{ bgcol }}; border: 1px solid {{ brdcol }};"
aria-hidden="true"
>
<p class="text-[11px] font-bold text-white leading-none">{{ name }}</p>
<p class="text-[9px] {{ txtcol }} leading-none mt-0.5 opacity-80">{{ sub }}</p>
</div>
{% endfor %}
</div>
{# Tool list — text fallback below the diagram (covers screen readers + reduces visual noise on mobile) #}
<div class="mt-12 grid sm:grid-cols-3 gap-6 max-w-3xl mx-auto" role="list" aria-label="Outils intégrés à DictIA">
{% for col_label, tools, accent in [
('Documents', [('Word', 'Export .docx instantané'), ('Google Docs', 'Sync Google Workspace'), ('Obsidian', 'Notes Markdown locales')], 'text-brand-b1'),
('Communication', [('Outlook', 'Envoi email automatique'), ('Teams', 'Canal Teams dédié'), ('Notion', 'Page Notion structurée')], 'text-brand-b2'),
('Automatisation', [('Zapier', '5&nbsp;000+ apps connectées'), ('Make', 'Scénarios visuels no-code'), ('n8n', 'Workflows open-source')], 'text-purple-400')
] %}
<div role="listitem" class="bg-white/[0.03] border border-white/[0.06] p-5 rounded">
<p class="eyebrow {{ accent }} mb-3">{{ col_label | upper }}</p>
<ul class="space-y-2">
{% for tool_name, tool_desc in tools %}
<li class="text-sm">
<span class="font-semibold text-white">{{ tool_name }}</span>
<span class="text-white/70"> — {{ tool_desc | safe }}</span>
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
</div>
<p class="text-center text-sm text-white/70 mt-8 max-w-2xl mx-auto">
Architecture ouverte. Tout export est aussi disponible en DOCX, SRT, VTT, JSON, TXT et MD — formats standards consommables par n'importe quel logiciel métier (gestion de dossiers, paie, CRM).
</p>
</div>
</section>
{# ===== PRICING ===== #}
<section class="bg-brand-bg py-20" id="tarifs" aria-labelledby="pricing-title">
<div class="max-w-[1200px] mx-auto px-6">
<div class="text-center max-w-2xl mx-auto mb-12">
<p class="eyebrow grad-text mb-4">TARIFS</p>
<h2 id="pricing-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black mb-4 text-brand-navy">
Choisissez votre formule.
</h2>
<p class="text-lg text-brand-navy/70">
Tous les forfaits incluent WhisperX Large-v3, volume illimité et zéro frais par utilisateur. Prix en CAD, taxes en sus (TPS&nbsp;5&nbsp;% + TVQ&nbsp;9,975&nbsp;%).
</p>
</div>
{% include 'marketing/_partials/_pricing_tiers.html' %}
<p class="text-center text-xs text-brand-navy/60 mt-6 max-w-2xl mx-auto">
Tous les prix en CAD, taxes en sus (TPS&nbsp;5&nbsp;% + TVQ&nbsp;9,975&nbsp;%).
</p>
{# ROI CALCULATOR — Alpine.js, hypotheses transparentes pour LPC art. 219 hygiene #}
<div x-data="roiCalculator()" class="mt-16 max-w-3xl mx-auto bg-white p-8 rounded border border-brand-border" aria-labelledby="roi-title">
<p class="eyebrow text-center grad-text mb-2">CALCULATEUR ROI</p>
<h3 id="roi-title" class="text-2xl font-black text-center mb-6 text-brand-navy">Combien DictIA peut vous faire économiser&nbsp;?</h3>
<div class="grid sm:grid-cols-3 gap-4 mb-6">
<label class="flex flex-col">
<span class="text-xs font-semibold mb-1 text-brand-navy">Utilisateurs</span>
<input type="range" x-model.number="users" min="1" max="25" step="1" class="accent-brand-b1" aria-label="Nombre d'utilisateurs">
<span class="text-sm text-brand-navy/70" x-text="users + ' utilisateur' + (users > 1 ? 's' : '')"></span>
</label>
<label class="flex flex-col">
<span class="text-xs font-semibold mb-1 text-brand-navy">Heures audio / jour</span>
<input type="range" x-model.number="hours" min="0.5" max="4" step="0.5" class="accent-brand-b1" aria-label="Heures d'audio par jour">
<span class="text-sm text-brand-navy/70" x-text="hours + ' h/jour'"></span>
</label>
<label class="flex flex-col">
<span class="text-xs font-semibold mb-1 text-brand-navy">Tarif horaire ($)</span>
<input type="range" x-model.number="rate" min="50" max="500" step="25" class="accent-brand-b1" aria-label="Tarif horaire en dollars">
<span class="text-sm text-brand-navy/70" x-text="rate + ' $/h'"></span>
</label>
</div>
<div class="text-center pt-6 border-t border-brand-border">
<p class="text-sm text-brand-navy/70 mb-2">Économies estimées par an</p>
<p class="text-5xl font-black grad-text" aria-live="polite" aria-atomic="true"
x-text="savings.toLocaleString('fr-CA') + ' $'"></p>
<p class="text-sm text-brand-navy/70 mt-2"
x-text="payback === null ? 'Payable dès la première année' : (payback < 1 ? 'Payback : moins d\'un mois' : 'Payback : ' + Math.round(payback) + ' mois')"></p>
</div>
<p class="text-xs text-brand-navy/70 mt-6 text-center">
Hypothèses&nbsp;: 80&nbsp;% du temps de transcription manuelle économisé, 220 jours ouvrables/an, comparé à Cloud&nbsp;ESSENTIEL (349&nbsp;$/mois × 12 = 4&nbsp;188&nbsp;$/an). Estimation à titre indicatif.
</p>
</div>
</div>
</section>
{# ===== COMPARATIF ===== #}
<section class="bg-white py-20" aria-labelledby="comparatif-title">
<div class="max-w-[1200px] mx-auto px-6">
<div class="text-center max-w-3xl mx-auto mb-12">
<p class="eyebrow grad-text mb-4">COMPARATIF</p>
<h2 id="comparatif-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black mb-4 text-brand-navy">
DictIA face aux solutions cloud américaines.
</h2>
<p class="text-lg text-brand-navy/70">
Comparaison factuelle au 2026-04-27. Sources&nbsp;: politiques de confidentialité publiques + grilles tarifaires officielles. Détails sur demande&nbsp;: <a href="mailto:info@dictia.ca" class="grad-text font-semibold hover:underline">info@dictia.ca</a>.
</p>
</div>
<div class="overflow-x-auto rounded border border-brand-border">
<table class="w-full min-w-[720px] text-sm">
<caption class="sr-only">Comparaison DictIA, Microsoft Teams Premium, Otter.ai Business, Whisper local sur 6 critères</caption>
<thead class="bg-brand-bg">
<tr>
<th scope="col" class="text-left p-4 font-bold text-brand-navy">Critère</th>
<th scope="col" class="p-4 font-bold text-brand-navy">DictIA</th>
<th scope="col" class="p-4 font-bold text-brand-navy/70">MS Teams Premium</th>
<th scope="col" class="p-4 font-bold text-brand-navy/70">Otter.ai Business</th>
<th scope="col" class="p-4 font-bold text-brand-navy/70">Whisper local (DIY)</th>
</tr>
</thead>
{# Status SVGs — check (green), x (red), warning (amber). `aria-label` preserves the meaning for AT users. #}
{%- set svg_check = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 inline-block flex-shrink-0 text-brand-b3" aria-label="Conforme" role="img"><path d="M5 13l4 4L19 7"/></svg>' -%}
{%- set svg_x = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 inline-block flex-shrink-0 text-red-500" aria-label="Non conforme" role="img"><path d="M6 18L18 6M6 6l12 12"/></svg>' -%}
{%- set svg_warn = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 inline-block flex-shrink-0 text-amber-500" aria-label="Partiel" role="img"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>' -%}
<tbody class="divide-y divide-brand-border">
{% for row in [
{
'critere': 'Conforme Loi&nbsp;25 sans transfert hors-Québec',
'dictia': {'status': 'check', 'text': 'Hébergement OVH Beauharnois'},
'teams': {'status': 'x', 'text': 'Soumis Cloud Act (US)'},
'otter': {'status': 'x', 'text': 'Hébergement US'},
'whisper':{'status': 'check', 'text': 'Aucun transfert (local)'}
},
{
'critere': 'Souveraineté hors Cloud Act US',
'dictia': {'status': 'check', 'text': 'Aucune exposition'},
'teams': {'status': 'x', 'text': 'Microsoft = entité US'},
'otter': {'status': 'x', 'text': 'Otter.ai Inc. = US'},
'whisper':{'status': 'check', 'text': 'Local'}
},
{
'critere': 'WhisperX Large-v3 fine-tuné FR-CA',
'dictia': {'status': 'check', 'text': 'FR-CA optimisé'},
'teams': {'status': 'warn', 'text': 'FR générique (FR-FR)'},
'otter': {'status': 'x', 'text': 'Anglais privilégié'},
'whisper':{'status': 'warn', 'text': 'FR générique de base'}
},
{
'critere': 'Diarisation jusqu\'à 8 locuteurs (pyannote)',
'dictia': {'status': 'check', 'text': 'Inclus par défaut'},
'teams': {'status': 'warn', 'text': 'Limité ~6 (Premium)'},
'otter': {'status': 'warn', 'text': 'Variable selon le forfait'},
'whisper':{'status': 'x', 'text': 'Non incluse'}
},
{
'critere': 'Coût mensuel par utilisateur',
'dictia': {'status': None, 'text': '0&nbsp;$ (forfait fixe)'},
'teams': {'status': None, 'text': '~14&nbsp;$ CAD (Premium)'},
'otter': {'status': None, 'text': '~20&nbsp;$ US (Business)'},
'whisper':{'status': None, 'text': '0&nbsp;$ (mais GPU&nbsp;+ DevOps requis)'}
},
{
'critere': 'Audit trail intégré (Loi&nbsp;25 art.&nbsp;3.5)',
'dictia': {'status': 'check', 'text': 'Inclus par défaut'},
'teams': {'status': 'warn', 'text': 'Via M365 Audit séparé'},
'otter': {'status': 'warn', 'text': 'Logs basiques seulement'},
'whisper':{'status': 'x', 'text': 'À développer soi-même'}
}
] %}
{%- set status_svg = {'check': svg_check, 'x': svg_x, 'warn': svg_warn} -%}
<tr class="hover:bg-brand-bg/50 transition-colors">
<th scope="row" class="text-left p-4 font-semibold text-brand-navy/80">{{ row.critere | safe }}</th>
<td class="p-4 text-center font-semibold text-brand-navy">
<span class="inline-flex items-center gap-1.5 px-2 py-1 rounded-none bg-brand-b3/10 text-brand-navy">
{%- if row.dictia.status -%}{{ status_svg[row.dictia.status] | safe }}{%- endif -%}
<span>{{ row.dictia.text | safe }}</span>
</span>
</td>
<td class="p-4 text-center text-brand-navy/70">
<span class="inline-flex items-center gap-1.5">
{%- if row.teams.status -%}{{ status_svg[row.teams.status] | safe }}{%- endif -%}
<span>{{ row.teams.text | safe }}</span>
</span>
</td>
<td class="p-4 text-center text-brand-navy/70">
<span class="inline-flex items-center gap-1.5">
{%- if row.otter.status -%}{{ status_svg[row.otter.status] | safe }}{%- endif -%}
<span>{{ row.otter.text | safe }}</span>
</span>
</td>
<td class="p-4 text-center text-brand-navy/70">
<span class="inline-flex items-center gap-1.5">
{%- if row.whisper.status -%}{{ status_svg[row.whisper.status] | safe }}{%- endif -%}
<span>{{ row.whisper.text | safe }}</span>
</span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<p class="text-xs text-brand-navy/70 mt-6 text-center max-w-3xl mx-auto">
Comparatif établi à partir de sources publiques (politiques de confidentialité, grilles tarifaires officielles, documentation produit) au 2026-04-27. Microsoft Teams Premium et Otter.ai sont des marques déposées de leurs propriétaires respectifs. DictIA n'est pas affilié à ces produits.
</p>
</div>
</section>
{# ===== CONFORMITÉ FORTERESSE ===== #}
<section class="bg-brand-navy text-white py-20 overflow-hidden relative" aria-labelledby="conformite-title">
{# Subtle decorative orb — aqua, like the Solution section #}
<div class="absolute top-1/3 left-1/4 w-[500px] h-[500px] rounded-full pointer-events-none" aria-hidden="true"
style="background: radial-gradient(circle, rgba(6,182,212,0.07) 0%, transparent 60%); filter: blur(60px);"></div>
<div class="relative max-w-[1200px] mx-auto px-6">
<div class="text-center max-w-2xl mx-auto mb-12">
<p class="eyebrow grad-text mb-4">CONFORMITÉ — FORTERESSE QUÉBÉCOISE</p>
<h2 id="conformite-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black mb-4">
Architecture <span class="grad-text">conçue avec</span> les exigences professionnelles québécoises.
</h2>
<p class="text-lg text-white/70">
DictIA mappe son architecture aux cadres réglementaires applicables au secteur public et aux ordres professionnels du Québec. DictIA a été conçu pour les secteurs réglementés du Québec — Loi&nbsp;25, Cloud Act, Barreau, ChAD, AMF. Détails techniques (EFVP, audit trail, déclaration CAI) disponibles sur demande&nbsp;: <a href="mailto:info@dictia.ca" class="grad-text font-semibold hover:underline">info@dictia.ca</a>.
</p>
{# Chips claims — 3 marqueurs canoniques (~192 000 pros · 5 ordres · 0 donnée hors-Québec) #}
<div class="flex flex-wrap items-center justify-center gap-2 mt-6" role="list" aria-label="Chiffres clés conformité">
{% for chip in [
('~192&nbsp;000 professionnels Tier&nbsp;1', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-3.5 h-3.5" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>'),
('5&nbsp;ordres · directives IA formelles', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-3.5 h-3.5" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>'),
('0&nbsp;donnée transmise hors Québec', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-3.5 h-3.5" aria-hidden="true"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>')
] %}
<span class="inline-flex items-center gap-1.5 bg-white/[0.06] border border-white/15 rounded-full px-3 py-1 text-xs text-white/85" role="listitem">
<span class="text-brand-b3" aria-hidden="true">{{ chip[1] | safe }}</span>
<span>{{ chip[0] | safe }}</span>
</span>
{% endfor %}
</div>
</div>
{# 4 conformity pillars — dark cards with grad-bg icon corners (matches Solution pillars style).
Icons (heroicons-style outline): map-pin (Québec), scale (Loi 25), building-library (LGGRI), code-bracket (AGPL). #}
{%- set svg_pin = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>' -%}
{%- set svg_scale = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><path d="M12 3v18"/><path d="M5 7h14"/><path d="M5 7l-2 6a4 4 0 0 0 8 0L9 7"/><path d="M19 7l2 6a4 4 0 0 1-8 0l2-6"/><path d="M8 21h8"/></svg>' -%}
{%- set svg_building = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><path d="M3 21h18"/><path d="M5 21V8l7-4 7 4v13"/><path d="M9 21v-6h6v6"/><path d="M9 11h.01"/><path d="M15 11h.01"/></svg>' -%}
{%- set svg_code = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/><line x1="14" y1="4" x2="10" y2="20"/></svg>' -%}
<div class="grid md:grid-cols-2 lg:grid-cols-4 gap-6">
{% for card in [
{
'icon': svg_pin,
'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&nbsp;27001, SOC&nbsp;2 selon le périmètre). Détails sur demande.'
},
{
'icon': svg_scale,
'title': 'Mappé Loi&nbsp;25 (LPRPSP)',
'desc': 'Audit trail art.&nbsp;3.5, EFVP préparée art.&nbsp;3.3, registre des consentements art.&nbsp;14. Modèles de déclaration CAI fournis.'
},
{
'icon': svg_building,
'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': svg_code,
'title': 'Code source AGPL&nbsp;v3 vérifiable',
'desc': 'Architecture entièrement auditable sur <a href="https://gitea.innova-ai.ca/Innova-AI/dictia-public" target="_blank" rel="noopener" class="underline hover:text-white">Gitea public</a>. Aucune boîte noire. Vos auditeurs peuvent examiner chaque ligne.'
}
] %}
<article class="bg-white/[0.05] backdrop-blur-sm p-6 rounded border border-white/[0.08]">
<div class="text-brand-b1 mb-4" aria-hidden="true">{{ card.icon | safe }}</div>
<h3 class="text-lg font-bold mb-2 text-white">{{ card.title | safe }}</h3>
<p class="text-sm text-white/80 leading-relaxed">{{ card.desc | safe }}</p>
</article>
{% endfor %}
</div>
<div class="text-center mt-10">
<p class="text-sm text-white/70">
Lancement prévu&nbsp;: <strong class="text-white">printemps 2026</strong>. Pré-inscription ouverte.
</p>
</div>
</div>
</section>
{# ===== CADRE RÉGLEMENTAIRE — Moniteur d'Interception ===== #}
{# Source canonique : InnovA-AI/Website-Sanity/components/sections/dictai-contraste.tsx (REGS + MoniteurInterception)
Round 4 cinématique : radar sweep continu, 6 paquets data en flight QC→US (offset-path bezier),
typewriter 3 lignes char-par-char, REGS reveal cascadé + glow rouge hover, verdict pulse + scan-line,
grid pattern bg console. #}
<style>
/* Cadre — RADAR SWEEP circulaire continu (round 4) */
@keyframes cadre-radar-sweep { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
.cadre-radar { animation: cadre-radar-sweep 4s linear infinite; transform-origin: center; }
/* Cadre — pulse halo autour du folder en alerte */
@keyframes cadre-folder-halo {
0%, 100% { transform: scale(1); opacity: 0.5; }
50% { transform: scale(1.8); opacity: 0; }
}
.cadre-halo { animation: cadre-folder-halo 1.2s ease-out infinite; }
/* Cadre — DATA PACKET flight QC→US via offset-path bezier (round 4) */
@keyframes cadre-packet-fly {
0% { offset-distance: 0%; opacity: 0; transform: scale(0.7); }
8% { opacity: 1; transform: scale(1); }
85% { opacity: 1; transform: scale(1); }
100% { offset-distance: 100%; opacity: 0; transform: scale(0.4); }
}
.cadre-packet {
offset-path: path('M 0 24 Q 100 -24, 200 24');
offset-rotate: auto;
animation: cadre-packet-fly 2.6s ease-in-out infinite;
will-change: offset-distance, opacity, transform;
}
/* Cadre — packet trail (traînée fade rouge à l'arrivée) */
@keyframes cadre-packet-arrive {
0%, 70% { opacity: 0; transform: translate(-50%,-50%) scale(0.5); }
80% { opacity: 0.85; transform: translate(-50%,-50%) scale(1.4); }
100% { opacity: 0; transform: translate(-50%,-50%) scale(2.2); }
}
.cadre-packet-burst { animation: cadre-packet-arrive 2.6s ease-in-out infinite; }
/* Cadre — REGS row flash amber pendant le sweep */
.cadre-reg { transition: border-color 220ms, background-color 220ms, box-shadow 220ms, transform 220ms; }
.cadre-reg.is-flash { border-color: rgba(245,158,11,0.50) !important; background-color: rgba(245,158,11,0.06) !important; }
.cadre-reg.is-flash .cadre-reg-label { color: #d97706 !important; }
/* Hover red glow (round 4) */
.cadre-reg:hover { box-shadow: 0 0 18px rgba(239,68,68,0.22); border-left-width: 3px !important; border-left-color: rgba(239,68,68,0.65) !important; transform: translateX(2px); }
/* Cadre — REGS reveal cascadé (round 4 : IntersectionObserver via Alpine `revealRegs`) */
.cadre-reg-item { opacity: 0; transform: translateX(12px); transition: opacity 420ms ease-out, transform 420ms ease-out; }
.cadre-reg-item.is-visible { opacity: 1; transform: translateX(0); }
/* Cadre — alerte clignotante footer */
@keyframes cadre-blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
.cadre-blink { animation: cadre-blink 0.85s ease-in-out infinite; }
/* Cadre — caret HUD blink */
@keyframes cadre-caret {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
.cadre-caret { animation: cadre-caret 0.7s ease-in-out infinite; }
/* Cadre — VERDICT NON CONFORME pulse glow (round 4) */
@keyframes cadre-verdict-pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(239,68,68,0.50), inset 0 0 0 1px rgba(239,68,68,0.45); }
50% { box-shadow: 0 0 18px 3px rgba(239,68,68,0.30), inset 0 0 0 1px rgba(239,68,68,0.85); }
}
.cadre-verdict-active { animation: cadre-verdict-pulse 2s ease-in-out infinite; }
/* Cadre — VERDICT scan-line traversante (round 4) */
@keyframes cadre-scan-line {
0% { transform: translateX(-110%); opacity: 0; }
20% { opacity: 0.85; }
80% { opacity: 0.85; }
100% { transform: translateX(110%); opacity: 0; }
}
.cadre-scan-line { animation: cadre-scan-line 3s linear infinite; }
/* Cadre — typewriter caret (3e ligne en rouge avec glow) */
.cadre-hud-line-3 { color: #ff5252 !important; text-shadow: 0 0 8px rgba(239,68,68,0.55); }
/* Mobile — désactiver radar + packets (CPU-intensive) */
@media (max-width: 768px) {
.cadre-radar, .cadre-packet, .cadre-packet-burst { display: none; }
}
/* Reduced motion — TOUT figé */
@media (prefers-reduced-motion: reduce) {
.cadre-radar, .cadre-halo, .cadre-blink, .cadre-caret, .cadre-packet,
.cadre-packet-burst, .cadre-verdict-active, .cadre-scan-line {
animation: none !important;
}
.cadre-reg, .cadre-reg-item { transition: none !important; opacity: 1 !important; transform: none !important; }
}
</style>
<section
class="bg-white py-20 border-b border-brand-border relative overflow-hidden"
aria-labelledby="cadre-title"
x-data="{
step: 0,
/* hudTyped[i] = sous-chaîne progressive de HUD[i] (typewriter char-by-char) */
hudTyped: ['','',''],
hudCursorLine: -1,
flashIdx: -1,
revealedRegs: [],
timers: [],
observer: null,
HUD: ['&gt; Interception IA détectée.', '&gt; Données utilisées pour entraînement.', '&gt; Statut : Violation Légale.'],
typeLine(idx, cb) {
const full = this.HUD[idx];
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (reduced) { this.hudTyped[idx] = full; if (cb) cb(); return; }
this.hudCursorLine = idx;
let i = 0;
const tick = () => {
if (i > full.length) { if (cb) cb(); return; }
this.hudTyped[idx] = full.slice(0, i);
i++;
this.timers.push(setTimeout(tick, 28));
};
tick();
},
runCycle() {
this.step = 0; this.hudTyped = ['','','']; this.hudCursorLine = -1; this.flashIdx = -1;
this.timers.push(setTimeout(() => this.step = 1, 900));
this.timers.push(setTimeout(() => this.step = 2, 2300));
this.timers.push(setTimeout(() => {
this.step = 3;
/* Typewriter chain : ligne 0 → 1 → 2 puis reset cursor */
this.typeLine(0, () => {
this.timers.push(setTimeout(() => this.typeLine(1, () => {
this.timers.push(setTimeout(() => this.typeLine(2, () => {
this.hudCursorLine = -1;
}), 280));
}), 280));
});
}, 2800));
for (let i = 0; i < 6; i++) {
this.timers.push(setTimeout(() => this.flashIdx = i, 5400 + i * 200));
}
this.timers.push(setTimeout(() => this.flashIdx = -1, 5400 + 6 * 200 + 500));
this.timers.push(setTimeout(() => this.runCycle(), 9500));
},
revealRegsCascade() {
[0,1,2,3,4,5].forEach((i) => {
this.timers.push(setTimeout(() => {
if (!this.revealedRegs.includes(i)) this.revealedRegs.push(i);
}, i * 120));
});
},
init() {
this.observer = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting && this.step === 0 && this.timers.length === 0) {
this.timers.push(setTimeout(() => this.runCycle(), 300));
this.timers.push(setTimeout(() => this.revealRegsCascade(), 500));
this.observer.disconnect();
}
});
}, { rootMargin: '-100px' });
this.observer.observe($el);
}
}"
>
{# Round 4 : decorative grid pattern bg console-style 40×40 #}
<div class="absolute inset-0 pointer-events-none" aria-hidden="true"
style="background-image: linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px), radial-gradient(circle, rgba(11,15,26,0.045) 1px, transparent 1px); background-size: 40px 40px, 40px 40px, 28px 28px;"></div>
<div class="relative max-w-[1200px] mx-auto px-6">
<div class="text-center max-w-2xl mx-auto mb-12">
<p class="eyebrow text-amber-600 mb-4 inline-flex items-center gap-1.5">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" class="w-3.5 h-3.5" aria-hidden="true"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
CADRE RÉGLEMENTAIRE QUÉBEC
</p>
<h2 id="cadre-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black mb-4 text-brand-navy">
Ce que vos outils actuels enfreignent <span class="grad-text">en secret</span>.
</h2>
<p class="text-base text-brand-navy/70">
Six textes officiels encadrent l'usage de l'IA et la circulation des données vocales au Québec.
Visualisez en temps réel comment une transcription cloud quitte la province et déclenche les violations.
</p>
</div>
{# Carte principale — header + body 2 colonnes (animation à gauche, REGS à droite) #}
<div class="max-w-5xl mx-auto rounded border border-brand-border bg-brand-bg overflow-hidden">
{# Header — Moniteur d'Interception #}
<div class="flex items-center gap-2.5 px-5 pt-4 pb-3 border-b border-brand-border bg-white">
<span class="w-1.5 h-1.5 rounded-full shrink-0 transition-all duration-300"
:class="step >= 2 ? 'bg-amber-500 shadow-[0_0_6px_#F59E0B]' : 'bg-brand-navy/30'"
aria-hidden="true"></span>
<span class="font-medium text-[10px] uppercase tracking-[0.22em] text-brand-navy/55">
Moniteur d'Interception
</span>
<span x-show="step >= 2"
class="ml-auto inline-flex items-center gap-1 font-bold text-[9px] uppercase tracking-[0.18em] text-amber-600 cadre-blink"
x-cloak>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" class="w-2.5 h-2.5" aria-hidden="true"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
Alerte Active
</span>
</div>
{# Body — 2 colonnes #}
<div class="flex flex-col md:flex-row">
{# LEFT — animation track (~42%) — round 4 : radar + data packets en flight #}
<div class="relative md:w-[42%] px-5 py-5 overflow-hidden bg-brand-bg" style="min-height: 240px;" role="img" aria-label="Visualisation : radar de surveillance et paquets de données voix transférés du Québec aux États-Unis">
{# Grille bg console #}
<div class="absolute inset-0 pointer-events-none" aria-hidden="true"
style="background-image: linear-gradient(rgba(11,15,26,0.045) 1px, transparent 1px), linear-gradient(90deg, rgba(11,15,26,0.045) 1px, transparent 1px); background-size: 20px 20px;"></div>
{# Round 4 : Radar sweep — cercle vert avec ligne rotative balayant 360° #}
<div class="absolute -top-8 -right-8 w-40 h-40 pointer-events-none opacity-50" aria-hidden="true">
<svg viewBox="0 0 100 100" class="w-full h-full">
<defs>
<linearGradient id="cadre-radar-grad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="rgba(34,197,94,0)"/>
<stop offset="80%" stop-color="rgba(34,197,94,0.55)"/>
<stop offset="100%" stop-color="rgba(34,197,94,0.85)"/>
</linearGradient>
</defs>
<circle cx="50" cy="50" r="48" fill="none" stroke="rgba(34,197,94,0.20)" stroke-width="0.5"/>
<circle cx="50" cy="50" r="32" fill="none" stroke="rgba(34,197,94,0.15)" stroke-width="0.4"/>
<circle cx="50" cy="50" r="16" fill="none" stroke="rgba(34,197,94,0.12)" stroke-width="0.4"/>
<line x1="2" y1="50" x2="98" y2="50" stroke="rgba(34,197,94,0.10)" stroke-width="0.3"/>
<line x1="50" y1="2" x2="50" y2="98" stroke="rgba(34,197,94,0.10)" stroke-width="0.3"/>
{# Sweep arm #}
<g class="cadre-radar" style="transform-origin: 50px 50px;">
<path d="M 50 50 L 98 50 A 48 48 0 0 0 84 16 Z" fill="url(#cadre-radar-grad)" opacity="0.45"/>
<line x1="50" y1="50" x2="98" y2="50" stroke="rgba(34,197,94,0.85)" stroke-width="0.6"/>
</g>
<circle cx="50" cy="50" r="1.6" fill="rgba(34,197,94,0.85)"/>
</svg>
</div>
{# Labels QC / US #}
<div class="relative flex items-center justify-between mb-3">
<span class="font-bold text-[9px] uppercase tracking-[0.16em] transition-colors duration-300"
:class="step >= 2 ? 'text-brand-navy/30' : 'text-brand-b3'">
QC
</span>
<span class="font-bold text-[9px] uppercase tracking-[0.16em] text-brand-navy/30">
US
</span>
</div>
{# Track + folder + DATA PACKETS en flight (round 4) #}
<div class="relative h-12">
<div class="absolute top-0 bottom-0 transition-colors duration-300"
:style="`left: 50%; width: 0; border-left: 1.5px dashed ${step >= 2 ? 'rgba(245,158,11,0.6)' : 'rgba(11,15,26,0.18)'};`"
aria-hidden="true"></div>
{# Folder source (QC) #}
<div class="absolute top-1/2 -translate-y-1/2 -translate-x-1/2"
style="left: 6%; transition: color 0.4s, filter 0.4s;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"
class="relative w-5 h-5 transition-colors duration-300"
:class="step >= 2 ? 'text-amber-500' : 'text-brand-b1'"
:style="`filter: drop-shadow(0 0 ${step >= 2 ? '6px rgba(245,158,11,0.7)' : '4px rgba(37,99,235,0.5)'})`"
aria-hidden="true">
<path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
</svg>
</div>
{# Folder destination (US) avec halo alerte #}
<div class="absolute top-1/2 -translate-y-1/2 -translate-x-1/2"
style="left: 94%;">
<span x-show="step >= 2" class="cadre-halo absolute inset-[-8px] rounded-full bg-red-500/20" x-cloak aria-hidden="true"></span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"
class="relative w-5 h-5 transition-colors duration-300"
:class="step >= 2 ? 'text-red-500' : 'text-brand-navy/40'"
:style="`filter: drop-shadow(0 0 ${step >= 2 ? '6px rgba(239,68,68,0.7)' : '0'})`"
aria-hidden="true">
<path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
</svg>
{# Burst rouge à l'arrivée des paquets #}
<span x-show="step >= 2" class="cadre-packet-burst absolute top-1/2 left-1/2 w-4 h-4 rounded-full bg-red-500/40 pointer-events-none" x-cloak aria-hidden="true"></span>
</div>
{# 6 PAQUETS DATA en flight QC→US (round 4 : offset-path bezier) #}
<div x-show="step >= 1" class="absolute inset-0 pointer-events-none" x-cloak aria-hidden="true">
{% for pi in range(6) %}
<span class="cadre-packet absolute top-0 left-[6%] inline-flex items-center gap-1 px-1 py-0.5 rounded bg-red-500/85 text-white font-mono text-[8px] tracking-tighter shadow-[0_0_6px_rgba(239,68,68,0.6)]"
style="animation-delay: {{ pi * 0.42 }}s;">
<span class="w-1 h-1 rounded-full bg-white/80"></span>
voice.wav
</span>
{% endfor %}
</div>
</div>
{# HUD panel — typewriter char-by-char (round 4) #}
<div x-show="step >= 3"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 translate-y-2"
x-transition:enter-end="opacity-100 translate-y-0"
class="relative mt-3 rounded px-3 py-2.5 font-mono"
style="background: rgba(11,15,26,0.94); border: 1px solid rgba(245,158,11,0.30); font-size: 10px; color: #F59E0B; line-height: 1.65; min-height: 70px;"
role="log" aria-live="polite"
x-cloak>
<div class="flex items-baseline">
<span x-html="hudTyped[0]"></span>
<span x-show="hudCursorLine === 0" class="cadre-caret ml-0.5"></span>
</div>
<div class="flex items-baseline" x-show="hudTyped[1].length > 0 || hudCursorLine === 1">
<span x-html="hudTyped[1]"></span>
<span x-show="hudCursorLine === 1" class="cadre-caret ml-0.5"></span>
</div>
<div class="flex items-baseline cadre-hud-line-3" x-show="hudTyped[2].length > 0 || hudCursorLine === 2">
<span x-html="hudTyped[2]"></span>
<span x-show="hudCursorLine === 2" class="cadre-caret ml-0.5"></span>
</div>
</div>
</div>
{# RIGHT — REGS list (~58%) #}
<div class="md:w-[58%] px-5 py-5 border-t md:border-t-0 md:border-l border-brand-border bg-white">
<p class="font-semibold text-[10px] uppercase tracking-[0.18em] mb-3 text-brand-navy/45">
Ce que vos outils actuels enfreignent en secret&nbsp;:
</p>
<ul class="flex flex-col gap-1.5" role="list">
{% for reg in [
{'label': 'Loi 25 (P-39.1)', 'detail': 'Renseignements personnels sensibles — pénalité jusqu\'à 25 M$', 'href': 'https://www.legisquebec.gouv.qc.ca/fr/document/lc/P-39.1', 'risk': True},
{'label': 'Loi 96 (C-11)', 'detail': 'Francisation — PME 25+ employés depuis juin 2025', 'href': 'https://www.legisquebec.gouv.qc.ca/fr/document/lc/C-11', 'risk': True},
{'label': 'US Cloud Act', 'detail': 'Accès aux données hébergées hors QC par les autorités américaines', 'href': 'https://www.congress.gov/bill/115th-congress/senate-bill/2383', 'risk': True},
{'label': 'Guide IA — Barreau QC', 'detail': 'Recommande systèmes fermés déployés en interne (publié 2024)', 'href': 'https://www.barreau.qc.ca/', 'risk': False},
{'label': 'Cadre IA — MCN', 'detail': 'Gouvernance IA pour organismes publics (déc.&nbsp;2025, conformité 19 juin 2026)', 'href': 'https://www.tresor.gouv.qc.ca/', 'risk': False},
{'label': 'CAI', 'detail': 'Commission d\'accès à l\'information — application active', 'href': 'https://www.cai.gouv.qc.ca/', 'risk': False}
] %}
<li role="listitem" class="cadre-reg-item"
:class="revealedRegs.includes({{ loop.index0 }}) ? 'is-visible' : ''"
style="transition-delay: {{ loop.index0 * 30 }}ms;">
<a href="{{ reg.href }}" target="_blank" rel="noopener noreferrer"
class="cadre-reg group flex items-start gap-2 rounded px-2.5 py-1.5 no-underline border focus-visible:outline-2 focus-visible:outline-amber-500 focus-visible:outline-offset-2"
:class="flashIdx === {{ loop.index0 }} ? 'is-flash' : ''"
style="border-color: {{ 'rgba(239,68,68,0.20)' if reg.risk else 'rgba(37,99,235,0.15)' }};">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="w-3.5 h-3.5 mt-0.5 shrink-0"
style="color: {{ 'rgba(239,68,68,0.65)' if reg.risk else 'rgba(37,99,235,0.55)' }};"
aria-hidden="true"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
<div class="min-w-0 flex-1">
<div class="flex items-start gap-1.5 flex-wrap">
<span class="cadre-reg-label font-semibold text-[11px] leading-tight shrink-0 transition-colors"
style="color: {{ '#dc2626' if reg.risk else '#2563eb' }};">{{ reg.label }}</span>
<span class="text-[10px] leading-tight text-brand-navy/55">{{ reg.detail | safe }}</span>
</div>
</div>
<span class="text-[9px] shrink-0 mt-0.5 opacity-0 group-hover:opacity-60 transition-opacity text-brand-navy/70">Lire ↗</span>
</a>
</li>
{% endfor %}
</ul>
<p class="mt-3 text-right text-[9px] text-brand-navy/30">
Les textes officiels font foi. Les informations ci-dessus sont fournies à titre indicatif.
</p>
</div>
</div>
{# Footer — verdict (round 4 : pulse glow rouge + scan-line traversante) #}
<div class="relative px-5 py-3 border-t border-brand-border bg-white overflow-hidden">
<div class="relative flex items-center gap-2.5 px-3 py-2 rounded transition-all duration-300"
:class="step >= 2 ? 'cadre-verdict-active bg-red-50/40' : ''"
style="border-radius: 4px;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"
class="w-4 h-4 transition-colors"
:class="step >= 2 ? 'text-red-500 cadre-blink' : 'text-brand-navy/35'"
aria-hidden="true"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
<span class="font-bold text-[10px] uppercase tracking-[0.20em] transition-colors"
:class="step >= 2 ? 'text-red-600' : 'text-brand-navy/35'"
style="text-shadow: 0 0 6px rgba(239,68,68,0.30);"
aria-live="polite">
<span x-text="step >= 2 ? 'NON CONFORME — Loi 25 · Cloud Act' : 'Surveillance active...'"></span>
</span>
{# Scan-line horizontale (round 4) #}
<span x-show="step >= 2" x-cloak class="cadre-scan-line absolute inset-y-0 left-0 w-1/3 pointer-events-none"
style="background: linear-gradient(90deg, transparent 0%, rgba(239,68,68,0.18) 50%, transparent 100%);"
aria-hidden="true"></span>
</div>
</div>
</div>
<p class="text-center text-xs text-brand-navy/55 mt-6 max-w-2xl mx-auto">
DictIA est conçu pour les secteurs réglementés du Québec — Loi&nbsp;25, Cloud Act, Barreau, ChAD, AMF. Hébergement OVH&nbsp;Beauharnois,
code source AGPL&nbsp;v3 vérifiable, audit trail intégré.
</p>
</div>
</section>
{# ===== CÉGEPS · CONFORMITÉ AU 19 JUIN 2026 (spotlight) ===== #}
{# Source canonique : texte fourni par l'utilisateur — Cadre IA MCN, Énoncé de principes mis à jour
+ Indication d'application IAG (publié 19 déc. 2025, conformité 19 juin 2026, art. 21 LGGRI). #}
<style>
/* Pulse glow autour du badge "Conformité imminente" */
@keyframes cegep-pulse-badge {
0%, 100% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.55); }
50% { box-shadow: 0 0 0 6px rgba(245, 158, 11, 0); }
}
.cegep-badge-pulse { animation: cegep-pulse-badge 2.2s ease-in-out infinite; }
/* Bullets reveal staggered (intersection observer via Alpine x-intersect non requis, on utilise CSS animation-delay) */
@keyframes cegep-bullet-rise {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.cegep-bullet { opacity: 0; animation: cegep-bullet-rise 0.5s ease-out forwards; }
@media (prefers-reduced-motion: reduce) {
.cegep-badge-pulse, .cegep-bullet { animation: none !important; }
.cegep-bullet { opacity: 1 !important; transform: none !important; }
}
</style>
<section class="bg-brand-bg py-20 border-y border-brand-border" aria-labelledby="cegeps-title">
<div class="max-w-[1100px] mx-auto px-6">
{# Bandeau d'eyebrow + badge pulse #}
<div class="flex flex-wrap items-center justify-center gap-3 mb-5">
<span class="cegep-badge-pulse inline-flex items-center gap-1.5 bg-amber-50 border border-amber-300 text-amber-800 rounded-full px-3 py-1 text-[11px] font-bold uppercase tracking-wider">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-3 h-3" aria-hidden="true"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
Conformité imminente
</span>
<span class="text-xs uppercase tracking-[0.18em] text-brand-navy/55 font-semibold">
Adopté 19&nbsp;déc.&nbsp;2025 · Décret officiel
</span>
</div>
<div class="text-center max-w-3xl mx-auto mb-12">
<h2 id="cegeps-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black mb-5 text-brand-navy leading-tight">
Conformité au <span class="grad-text">19&nbsp;juin&nbsp;2026</span> — vous dirigez un cégep, un CISSS ou un ministère&nbsp;?
</h2>
<p class="text-lg text-brand-navy/80 font-semibold mb-3">
Vous avez jusqu'au 19&nbsp;juin&nbsp;2026.
</p>
<p class="text-base text-brand-navy/70 leading-relaxed">
Depuis le 19&nbsp;décembre&nbsp;2025, tous les organismes publics québécois doivent appliquer un cadre IA strict. Aucun outil cloud non approuvé ne peut recevoir de renseignements confidentiels — fini ChatGPT ou Teams Copilot dans les CA, les séances cliniques ou les comités universitaires.
</p>
</div>
{# Carte spotlight — Cadre IA MCN détaillé #}
<div class="bg-white border border-brand-border rounded p-6 md:p-8 max-w-4xl mx-auto">
<div class="flex items-start gap-3 mb-5">
<span class="grad-bg w-10 h-10 rounded flex items-center justify-center text-white shrink-0" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5"><path d="M3 21h18"/><path d="M5 21V8l7-4 7 4v13"/><path d="M9 21v-6h6v6"/></svg>
</span>
<div class="min-w-0">
<h3 class="text-lg md:text-xl font-bold text-brand-navy leading-tight">
Cadre IA MCN — Énoncé de principes mis à jour + Indication d'application IAG
</h3>
<p class="text-xs text-brand-navy/55 mt-1">Publié le 19&nbsp;décembre&nbsp;2025 sous l'art.&nbsp;21 LGGRI</p>
</div>
</div>
<p class="text-sm text-brand-navy/75 leading-relaxed mb-5">
Interdit aux organismes publics (art.&nbsp;2 LGGRI) d'entrer des renseignements confidentiels dans un système IA non approuvé — ministères, organismes budgétaires, Santé&nbsp;Québec, CISSS/CIUSSS, centres de services scolaires, cégeps, universités. Régime allégé pour entreprises du gouvernement (art.&nbsp;4 — Hydro-Québec, SAQ, Loto-Québec, CDPQ).
</p>
<ul class="space-y-2.5 mb-6" role="list">
{% for bullet in [
('Énoncé de principes — 12 principes éthiques applicables à tout système IA dans l\'administration publique', 0),
('Indication d\'application IAG — gouvernance, gestion des risques, mesures de contrôle, protection des données, formation du personnel', 80),
('Délai conformité&nbsp;: <strong>19&nbsp;juin&nbsp;2026</strong> (6&nbsp;mois post publication 19&nbsp;déc.&nbsp;2025)', 160),
('Municipalités, MRC et Assemblée nationale <em>non</em> visées par l\'Énoncé — mais Loi sur l\'accès (A-2.1) reste applicable aux séances publiques', 240),
('<strong>Loi&nbsp;25</strong> — voix = donnée biométrique (LCCJTI art.&nbsp;44-45), déclaration CAI obligatoire si banque biométrique', 320),
('<strong>Loi&nbsp;96 (C-11)</strong> — documents générés en français pour organisations 25+ employés', 400),
('Hébergement au Québec — aucune société US dans la chaîne (Cloud Act inapplicable)', 480)
] %}
<li class="cegep-bullet flex items-start gap-2.5 text-sm text-brand-navy/80 leading-relaxed" role="listitem"
style="animation-delay: {{ bullet[1] }}ms;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 mt-1 text-brand-b3 shrink-0" aria-hidden="true"><path d="M5 13l4 4L19 7"/></svg>
<span>{{ bullet[0] | safe }}</span>
</li>
{% endfor %}
</ul>
{# Liste organismes visés (chips) #}
<div class="border-t border-brand-border pt-5">
<p class="text-[11px] uppercase tracking-[0.16em] font-bold text-brand-navy/50 mb-3">Organismes visés par le Cadre&nbsp;IA MCN</p>
<div class="flex flex-wrap gap-1.5" role="list">
{% for org in ['Ministères', 'Santé Québec', 'CISSS/CIUSSS', 'Universités', 'Cégeps', 'Hydro-Québec', 'SAQ', 'SAAQ', 'CDPQ'] %}
<span class="inline-flex items-center bg-brand-bg border border-brand-border rounded-full px-2.5 py-0.5 text-[11px] text-brand-navy/75" role="listitem">{{ org }}</span>
{% endfor %}
</div>
</div>
</div>
</div>
</section>
{# ===== PARTENAIRE DE CONFIANCE — CyberPerformance ===== #}
<section class="bg-white py-12" aria-labelledby="partner-title">
<div class="max-w-[1100px] mx-auto px-6">
<div class="flex items-center gap-3 mb-4">
<span id="partner-title" class="text-[10px] font-bold uppercase tracking-[0.20em] text-brand-navy/45">
Partenaire de confiance
</span>
<span class="flex-1 h-px max-w-[60px] bg-brand-border"></span>
</div>
<a
href="https://cyberperformance.ca"
target="_blank"
rel="noopener noreferrer"
class="group flex flex-wrap sm:flex-nowrap items-center gap-5 px-5 py-4 rounded border border-brand-border hover:border-brand-b1 transition-colors no-underline focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
aria-label="CyberPerformance — Marketing numérique, Lévis, QC. Site externe (s'ouvre dans un nouvel onglet)"
>
{# Icône SVG handshake générique (pas d'emoji, pas de logo bitmap) #}
<span class="shrink-0 w-12 h-12 grad-bg rounded flex items-center justify-center text-white" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-6 h-6"><path d="M11 17l-2-2 2-2"/><path d="M13 7l2 2-2 2"/><path d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0z"/></svg>
</span>
<span class="hidden sm:block w-px h-8 bg-brand-border shrink-0"></span>
<span class="flex-1 min-w-0">
<span class="block text-sm font-bold text-brand-navy leading-tight">CyberPerformance</span>
<span class="block text-xs text-brand-navy/60 mt-0.5">Marketing numérique · Lévis, QC</span>
</span>
<span class="shrink-0 inline-flex items-center gap-1.5 ml-auto text-xs text-brand-navy/55 group-hover:text-brand-b1 transition-colors">
<span>cyberperformance.ca</span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-3.5 h-3.5" aria-hidden="true"><path d="M7 17L17 7M7 7h10v10"/></svg>
<span class="sr-only">(s'ouvre dans un nouvel onglet)</span>
</span>
</a>
</div>
</section>
{# ===== TÉMOIGNAGES (placeholder pré-lancement) ===== #}
<section class="bg-brand-bg py-20" aria-labelledby="testimonials-title">
<div class="max-w-[1200px] mx-auto px-6">
<div class="text-center max-w-2xl mx-auto mb-12">
<p class="eyebrow grad-text mb-4">TÉMOIGNAGES</p>
<h2 id="testimonials-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black mb-4 text-brand-navy">
Premiers cabinets pilotes interviewés au printemps&nbsp;2026.
</h2>
<p class="text-lg text-brand-navy/70">
Trois pilotes confidentiels en cours&nbsp;: un cabinet juridique, un cabinet CPA, une municipalité. Témoignages publiés ici dès l'aboutissement des entrevues, accompagnés d'une métrique vérifiable.
</p>
</div>
<div class="grid md:grid-cols-3 gap-6">
{% for t in testimonials %}
<article class="bg-white p-6 rounded border border-brand-border flex flex-col items-center text-center"
aria-label="Témoignage {{ t.placeholder_label }} à venir">
<div class="w-16 h-16 rounded-full grad-bg flex items-center justify-center mb-4 shadow-cta text-white" aria-hidden="true">
{%- if t.persona == 'avocat' -%}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7"><path d="M12 3v18"/><path d="M5 7h14"/><path d="M5 7l-2 6a4 4 0 0 0 8 0L9 7"/><path d="M19 7l2 6a4 4 0 0 1-8 0l2-6"/><path d="M8 21h8"/></svg>
{%- elif t.persona == 'cpa' -%}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/><line x1="3" y1="20" x2="21" y2="20"/></svg>
{%- elif t.persona == 'municipal' -%}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7"><path d="M3 21h18"/><path d="M5 21V8l7-4 7 4v13"/><path d="M9 21v-6h6v6"/><path d="M9 11h.01"/><path d="M15 11h.01"/></svg>
{%- endif -%}
</div>
<p class="text-sm font-bold text-brand-navy mb-1">{{ t.placeholder_label | safe }}</p>
<p class="text-xs text-brand-navy/70 mb-4">Témoignage à venir &middot; {{ t.expected | safe }}</p>
<p class="text-sm text-brand-navy/70 italic">
« Citation et métrique publiées après validation par le pilote. »
</p>
</article>
{% endfor %}
</div>
</div>
</section>
{# ===== FAQ ===== #}
<section class="bg-white py-20" aria-labelledby="faq-title">
<div class="max-w-[820px] mx-auto px-6">
<div class="text-center mb-12">
<p class="eyebrow grad-text mb-4">FAQ</p>
<h2 id="faq-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black mb-4 text-brand-navy">
Vos questions les plus fréquentes.
</h2>
<p class="text-lg text-brand-navy/70">
Une question qui n'est pas couverte ici&nbsp;? Écrivez-nous&nbsp;:
<a href="mailto:info@dictia.ca" class="grad-text font-semibold hover:underline">info@dictia.ca</a>.
</p>
</div>
<div class="divide-y divide-brand-border border-y border-brand-border" role="list">
{% for item in faq %}
<div x-data="{ open: false }" class="py-2" role="listitem">
<h3>
<button type="button"
class="w-full flex items-center justify-between gap-4 py-4 text-left hover:bg-brand-bg/50 transition-colors rounded-none px-2 focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
@click="open = !open"
:aria-expanded="open.toString()"
aria-controls="faq-panel-{{ loop.index }}">
<span class="font-semibold text-brand-navy text-base">{{ item.q | safe }}</span>
<span class="grad-text text-2xl flex-shrink-0 transition-transform"
:class="open ? 'rotate-45' : ''" aria-hidden="true">+</span>
</button>
</h3>
<div id="faq-panel-{{ loop.index }}"
x-show="open"
x-transition.opacity.duration.200ms>
<p class="px-2 pb-4 text-sm text-brand-navy/80 leading-relaxed">{{ item.a | safe }}</p>
</div>
</div>
{% endfor %}
</div>
</div>
{# Schema.org FAQPage JSON-LD for SEO/GEO — inline so it travels with this page only #}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{%- for item in faq -%}
{
"@type": "Question",
"name": {{ (item.q | striptags | replace('&nbsp;', ' '))|tojson }},
"acceptedAnswer": {
"@type": "Answer",
"text": {{ (item.a | striptags | replace('&nbsp;', ' '))|tojson }}
}
}{{ "," if not loop.last }}
{%- endfor -%}
]
}
</script>
</section>
{# ===== CTA FINAL ===== #}
<section class="relative bg-brand-navy text-white py-24 overflow-hidden" aria-labelledby="cta-title">
{# Two warm cosmic orbs to mirror the Hero — visual closure of the page #}
<div class="absolute inset-0 pointer-events-none" aria-hidden="true">
<div class="absolute top-1/4 left-1/3 w-[500px] h-[500px] rounded-full"
style="background: radial-gradient(circle, rgba(37,99,235,0.14) 0%, transparent 60%); filter: blur(50px);"></div>
<div class="absolute bottom-1/4 right-1/3 w-[450px] h-[450px] rounded-full"
style="background: radial-gradient(circle, rgba(6,182,212,0.10) 0%, transparent 60%); filter: blur(50px);"></div>
</div>
<div class="relative max-w-[820px] mx-auto px-6 text-center">
<p class="eyebrow grad-text mb-4">PRÊT&nbsp;?</p>
<h2 id="cta-title" class="text-[clamp(2.25rem,4vw,3.5rem)] font-black mb-6">
Prêt à <span class="grad-text">protéger vos données</span>&nbsp;?
</h2>
<p class="text-lg text-white/80 mb-3">
Réservez une démonstration gratuite. Nous analyserons vos besoins et vous recommanderons le forfait adapté à votre réalité.
</p>
<p class="text-base text-white/65 mb-8">
Lancement printemps&nbsp;2026. Les premiers utilisateurs bénéficient d'une remise de bienvenue et d'un accompagnement direct par notre équipe technique. Aucun engagement.
</p>
<div class="flex flex-col sm:flex-row gap-4 justify-center">
{% from 'macros/button.html' import button %}
{{ button('Réserver ma démo gratuite', href='/contact', variant='primary', size='lg', icon='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4" aria-hidden="true"><path d="M5 12h14M13 6l6 6-6 6"/></svg>') }}
{{ button('Pré-inscription par courriel', href='mailto:info@dictia.ca?subject=Pré-inscription%20DictIA', variant='ghost', size='lg', icon='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4" aria-hidden="true"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>') }}
{{ button('Voir les forfaits', href='#tarifs', variant='ghost', size='lg') }}
</div>
<p class="text-xs text-white/70 mt-8">
Pré-inscription par courriel jusqu'à l'ouverture de la plateforme. Inscription en ligne disponible dès le lancement.
</p>
</div>
</section>
{% endblock %}
{% block scripts %}
<script src="/static/js/roi_calculator.js" defer></script>
{% endblock %}