Files
dictia-public/templates/marketing/fonctionnalites.html
Allison 8a7650f9fa feat(marketing): refonte stratégique 'Comment ça marche' — 4 catégories × 12 sous-modes
Audit complet des fonctionnalités réelles DictIA (manuel utilisateur + composants
production) puis restructuration de l'animation phone container en 4 catégories
logiques regroupant 12 sous-modes (6 historiques + 6 nouveaux).

CATÉGORIES (bottom tab bar 4 boutons + sub-mode dots indicator) :
- Capture (b2 cyan)         : Transcription, Recording live, Recherche IA
- Transformation IA (b1)    : Diarisation, 99+ langues, Résumé+actions, Chat IA
- Distribution (b3 fuchsia) : Exports, Intégrations Hub, Partage, Users
- Gouvernance (b1 blue)     : Audit trail, Conformité Loi 25 + 9 ordres pros

NOUVEAUX MODES IMPLÉMENTÉS :
- Mode 7 Recording live : minuteur 99s + waveform 24 bars random + 3 boutons
  sources (mic/système/combiné) selon manuel utilisation v1.0
- Mode 8 Recherche sémantique : query typed + 3 résultats highlight RAG
- Mode 9 Résumé + actions : décisions/actions stagger + extraction ICS
- Mode 10 Intégrations : hub central DictIA + 8 logos en orbite (Word, Outlook,
  Teams, Notion, Obsidian, Zapier, Make, n8n) + lignes connexion SVG
- Mode 11 Audit trail : 6 events horodatés (INFO/AUTH/PROC/READ/EXP/SHARE) +
  badge consentement tracé immutable (Loi 25 art. 8)
- Mode 12 Conformité Loi 25 : 6 badges (Loi 25/96/EFVP CAI/MCN/AGPL/0 Cloud
  Act US) + 9 ordres pros (Barreau, CNQ, CPA, ChAD, OACIQ, CMQ, OIIQ, OPQ, OEQ)

REFACTOR Alpine dictiaDashboard() :
- FEATURES étendu de 7 à 13 entrées (idx 0-12)
- CATEGORIES array avec submodes[], iconPath, color, subtitle
- activeCategory + handleCategorySelect(ci) en plus de handleManualSelect(i)
- Auto-cycle 1100ms entre sous-modes ; switch catégorie quand fin atteinte
- Right grid 3×6 → 2×2 categories cards (preview sub-modes dots)
- Bottom tab bar 6 modes → 4 catégories (icons larger 18px) + sub-mode dots
- Mobile pills par catégorie (au lieu de par mode)

Préservé : palette brand-b1/b2/b3 stricte, phone shell statique 280×580,
WCAG AA, prefers-reduced-motion, eyebrow text-brand-navy, IA Mistral card,
section integrations, architecture, conformité-resume.

Tests : 9/9 passent dans test_fonctionnalites_*. Assertions ajoutées pour les
4 catégories + 6 nouveaux modes + handleCategorySelect + 1100ms cycle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 12:57:44 -04:00

2314 lines
148 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
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 %}Fonctionnalités DictIA — WhisperX FR-CA, diarisation 8 locuteurs, Mistral 7B local, Q&R, exports DOCX/PDF/SRT{% endblock %}
{% block description %}Toutes les fonctionnalités de DictIA : transcription WhisperX Large-v3 fine-tunée FR-CA, diarisation pyannote 8 locuteurs, résumés Mistral 7B local, chat Q&R RAG, synchronisation audio-texte, exports multi-formats, intégrations Word, Outlook, Teams, Notion, Obsidian, Zapier.{% endblock %}
{% block head_extra %}
{# Page-scoped CSS — animations subtiles sans dépendre d'un rebuild Tailwind ; respecte prefers-reduced-motion via base.css. #}
<style>
/* Animated underline H2 — visible classe ajoutée via IntersectionObserver */
.ani-underline { position: relative; display: inline-block; }
.ani-underline::after {
content: '';
position: absolute;
left: 50%;
bottom: -10px;
width: 0;
height: 3px;
background: linear-gradient(118deg, #2563eb, #06b6d4 52%, #06b6d4);
transform: translateX(-50%);
transition: width 600ms ease-out;
}
.ani-underline.is-visible::after { width: 56px; }
/* Stagger fade-in cards — class ajoutée par IntersectionObserver */
.ani-fade { opacity: 0; transform: translateY(16px); transition: opacity 600ms ease-out, transform 600ms ease-out; transition-delay: var(--delay, 0ms); }
.ani-fade.is-visible { opacity: 1; transform: translateY(0); }
/* Hover lift cards */
.ani-lift { transition: transform 200ms ease-out, box-shadow 200ms ease-out; }
.ani-lift:hover { transform: translateY(-2px); box-shadow: 0 8px 32px rgba(37,99,235, 0.18); }
/* Sticky sub-nav active link */
.subnav-link[aria-current="true"] { color: #2563eb; }
.subnav-link[aria-current="true"]::after {
content: '';
display: block;
height: 2px;
margin-top: 4px;
background: linear-gradient(118deg, #2563eb, #06b6d4 52%, #06b6d4);
}
/* Cosmic float orbs (n'utilise pas tc-float-y pour éviter rebuild) */
@keyframes orb-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-12px); } }
.orb-float-a { animation: orb-float 8s ease-in-out infinite; }
.orb-float-b { animation: orb-float 11s ease-in-out infinite reverse; }
/* Cloud PRO pulse glow (recommended tier) */
@keyframes card-pulse-glow {
0%, 100% { box-shadow: 0 4px 20px rgba(37,99,235, 0.28); }
50% { box-shadow: 0 12px 40px rgba(37,99,235, 0.5); }
}
.card-pulse-glow { animation: card-pulse-glow 3s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
.ani-fade { opacity: 1; transform: none; transition: none; }
.ani-underline::after { transition: none; width: 56px; }
.ani-lift { transition: none; }
.ani-lift:hover { transform: none; }
.orb-float-a, .orb-float-b, .card-pulse-glow { animation: none; }
}
</style>
{% endblock %}
{% block content %}
{%- 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_sync = '<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="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10"/><path d="M20.49 15a9 9 0 0 1-14.85 3.36L1 14"/></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_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 mt-0.5 flex-shrink-0 text-brand-b3" aria-hidden="true"><path d="M5 13l4 4L19 7"/></svg>' -%}
{%- set icon_link = '<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="M5 12h14M13 5l7 7-7 7"/></svg>' -%}
{# ===== HERO ===== #}
<section class="relative overflow-hidden bg-brand-navy text-white py-24 md:py-28" aria-labelledby="page-title">
{# Cosmic orbs background — float animation subtile #}
<div class="absolute inset-0 pointer-events-none" aria-hidden="true">
<div class="orb-float-a absolute top-1/4 left-1/4 w-[600px] h-[600px] rounded-full"
style="background: radial-gradient(circle, rgba(37,99,235,0.16) 0%, transparent 60%); filter: blur(40px);"></div>
<div class="orb-float-b 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>
<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>
<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>
<div class="relative max-w-[1200px] mx-auto px-6 text-center">
<p class="eyebrow grad-text mb-5 animate-tc-fade-in-up" style="animation-delay: 0ms; animation-fill-mode: backwards;">
FONCTIONNALITÉS · WHISPERX · MISTRAL 7B
</p>
<h1 id="page-title" class="text-[clamp(2.25rem,4vw,3.75rem)] font-black leading-[1.05] mb-5 max-w-3xl mx-auto animate-tc-fade-in-up" style="animation-delay: 75ms; animation-fill-mode: backwards;">
Tout ce qu'il vous faut pour transcrire <span class="grad-text">en restant chez soi</span>.
</h1>
<p class="text-lg text-white/70 max-w-2xl mx-auto mb-12 animate-tc-fade-in-up" style="animation-delay: 150ms; animation-fill-mode: backwards;">
Pile technique 100&nbsp;% québécoise. Inférence sur GPU local ou OVH Beauharnois. Aucun transit cloud américain. Conçu avec 9&nbsp;ordres professionnels.
</p>
{# Stats hero — counter via Alpine x-data + IntersectionObserver pour déclenchement #}
<div class="grid grid-cols-2 md:grid-cols-4 gap-8 max-w-4xl mx-auto" role="list">
{% for stat in [
{'target': 95, 'suffix': '&nbsp;%+', 'label': "précision FR-CA", 'sub': 'WhisperX Large-v3'},
{'target': 30, 'suffix': '×', 'label': "vitesse temps réel", 'sub': '1 h audio → ~5 min'},
{'target': 8, 'suffix': '', 'label': "locuteurs simultanés", 'sub': 'pyannote-audio'},
{'target': 0, 'suffix': '', 'label': "transit cloud", 'sub': '100&nbsp;% local'}
] %}
<div class="text-center" role="listitem"
x-data="{ value: 0, target: {{ stat.target }} }"
x-init="
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (reduced) { value = target; return; }
const io = new IntersectionObserver((entries) => {
if (!entries[0].isIntersecting) return;
const start = performance.now();
const dur = 1200;
const tick = (now) => {
const t = Math.min(1, (now - start) / dur);
value = Math.round(target * (1 - Math.pow(1 - t, 3)));
if (t < 1) requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
io.disconnect();
}, { threshold: 0.5 });
io.observe($el);
">
<div class="text-5xl font-black grad-text mb-2 tabular-nums">
<span x-text="value"></span><span>{{ stat.suffix | safe }}</span>
</div>
<div class="text-sm font-semibold text-white mb-1">{{ stat.label }}</div>
<div class="text-xs text-white/60">{{ stat.sub | safe }}</div>
</div>
{% endfor %}
</div>
</div>
</section>
{# ===== STICKY SUB-NAV ===== #}
<nav class="sticky top-[62px] z-40 bg-white/95 backdrop-blur-sm border-b border-brand-border" aria-label="Navigation des sections de la page"
x-data="{ active: 'fonctionnalites' }"
x-init="
const sections = ['fonctionnalites', 'integrations', 'architecture', 'conformite-resume'];
const io = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting && e.intersectionRatio > 0.15) active = e.target.id;
});
}, { rootMargin: '-120px 0px -55% 0px', threshold: [0, 0.15, 0.3] });
sections.forEach(id => { const el = document.getElementById(id); if (el) io.observe(el); });
">
<div class="max-w-[1200px] mx-auto px-6">
<ul class="flex flex-wrap gap-x-6 gap-y-1 py-3 text-sm font-semibold text-brand-navy/70" role="list">
<li><a href="#fonctionnalites" class="subnav-link inline-block py-1 hover:text-brand-b1 transition-colors focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
:aria-current="active === 'fonctionnalites' ? 'true' : 'false'">Fonctionnalités</a></li>
<li><a href="#integrations" class="subnav-link inline-block py-1 hover:text-brand-b1 transition-colors focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
:aria-current="active === 'integrations' ? 'true' : 'false'">Intégrations</a></li>
<li><a href="#architecture" class="subnav-link inline-block py-1 hover:text-brand-b1 transition-colors focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
:aria-current="active === 'architecture' ? 'true' : 'false'">Architecture</a></li>
<li><a href="#conformite-resume" class="subnav-link inline-block py-1 hover:text-brand-b1 transition-colors focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
:aria-current="active === 'conformite-resume' ? 'true' : 'false'">Conformité</a></li>
</ul>
</div>
</nav>
{# ===== 6 FONCTIONNALITÉS PRINCIPALES =====
NOTE: bento card content is duplicated between landing.html and fonctionnalites.html.
When editing, sync both files. Future refactor: extract to _partials/_bento_features.html.
#}
<section id="fonctionnalites" class="bg-white py-20 scroll-mt-32" aria-labelledby="features-title">
<div class="max-w-[1200px] mx-auto px-6">
<div class="text-center max-w-3xl mx-auto mb-14">
<p class="eyebrow grad-text mb-4">SIX FONCTIONNALITÉS</p>
<h2 id="features-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black text-brand-navy">
<span class="ani-underline" data-ani-underline>Conçu pour les pros qui parlent et écrivent toute la journée.</span>
</h2>
<p class="text-lg text-brand-navy/70 mt-6">
Chaque module fonctionne hors-ligne sur votre matériel ou notre VPS Québec OVH. Aucun appel à OpenAI, Microsoft Copilot ou Google.
</p>
</div>
{% from 'macros/bento.html' import bento_card %}
{%- set features = [
{
'n': '01',
'title': 'Transcription IA locale FR-CA',
'desc': 'WhisperX Large-v3 entraîné sur des millions d\'heures de québécois juridique, médical et gouvernemental. 95&nbsp;%+ dès la première utilisation, sans aucun ajustement manuel.',
'icon': icon_microphone,
'chips': ['WhisperX Large-v3', 'GPU RTX 8 ou 16&nbsp;Go', '1 h audio → ~5 min', '< 5&nbsp;% taux d\'erreur', '100&nbsp;% local · hors-ligne']
},
{
'n': '02',
'title': 'Identification des locuteurs (diarisation)',
'desc': 'Jusqu\'à 8 interlocuteurs distingués automatiquement. Chaque prise de parole est étiquetée et horodatée pour une lecture nette des comités, audiences et entrevues.',
'icon': icon_users,
'chips': ['pyannote-audio 3.x', '8 personnes max', 'Étiquettes auto', 'Horodatage word-level']
},
{
'n': '03',
'title': 'Résumés &amp; points d\'action',
'desc': 'Résumé exécutif, décisions clés et liste d\'actions générés en français. Format avocat, notaire, CPA ou médecin selon votre profil. Récupérez ~2 h/jour de rédaction.',
'icon': icon_document,
'chips': ['Mistral Nemo 12B (Cloud) · Mistral 7B (Local)', 'Templates pro', 'Décisions + actions', '2&nbsp;h/jour récupérées']
},
{
'n': '04',
'title': 'Chat IA sur enregistrement (Q&amp;R)',
'desc': 'Posez vos questions directement sur l\'audio : « Quel montant a été mentionné&nbsp;?&nbsp;» ou « Quels engagements ont été pris&nbsp;? » réponses instantanées en langage naturel.',
'icon': icon_chat,
'chips': ['Mistral 7B local', 'RAG local', 'sentence-transformers', 'Recherche contextuelle']
},
{
'n': '05',
'title': 'Synchronisation audio-texte',
'desc': 'Cliquez sur n\'importe quel mot pour sauter à ce moment dans l\'audio. Suivi automatique en lecture avec mot actif surligné révision et référencement instantanés.',
'icon': icon_sync,
'chips': ['Timestamps word-level', 'Lecture guidée', 'Mot actif surligné', 'Navigation par mot clé']
},
{
'n': '06',
'title': 'Recherche sémantique &amp; exports',
'desc': 'Recherchez par sens et non seulement par mot-clé. Exportez vers DOCX, PDF, SRT, VTT, TXT, JSON, MD compatible Obsidian, Notion, Logseq via Markdown.',
'icon': icon_export,
'chips': ['DOCX, PDF, SRT, VTT', 'TXT, JSON, MD', 'Obsidian · Notion · Logseq', 'Format avocat / notaire / CPA']
}
] -%}
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-[1.5px] bg-brand-border rounded overflow-hidden">
{% for f in features %}
<article class="ani-fade ani-lift relative bg-brand-navy2 p-6 overflow-hidden border border-white/[0.045]"
style="--delay: {{ loop.index0 * 60 }}ms;"
data-ani-fade>
<div class="absolute top-2 right-4 text-[80px] font-black grad-text opacity-20 leading-none" aria-hidden="true">{{ f.n }}</div>
<div class="relative">
<div class="text-brand-b1 mb-4" aria-hidden="true">{{ f.icon | safe }}</div>
<h3 class="text-lg font-bold mb-2 text-white">{{ f.title | safe }}</h3>
<p class="text-sm text-white/70 leading-relaxed mb-4">{{ f.desc | safe }}</p>
<ul class="flex flex-wrap gap-1.5" role="list">
{% for chip in f.chips %}
<li class="text-[11px] font-mono font-medium text-white/80 bg-white/[0.06] border border-white/[0.08] px-2 py-1 rounded-full">{{ chip | safe }}</li>
{% endfor %}
</ul>
</div>
</article>
{% endfor %}
</div>
{# Microcopy LPC art. 219 — méthodologie 95% #}
<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 underline">info@dictia.ca</a>.
</p>
</div>
</section>
{# ===== COMMENT ÇA MARCHE — DASHBOARD HOLOGRAPHIQUE (4 CATÉGORIES × 12 SOUS-MODES) =====
Reproduction fidèle de DashboardHolographique (Website-Sanity/dictai-narrative.tsx),
refonte stratégique 2026-04-29 : 4 catégories (Capture · IA · Distribution · Gouvernance)
regroupant 12 sous-modes (6 historiques + 6 nouveaux : Recording, Recherche, Résumé,
Intégrations, Audit, Loi 25, AGPL).
Phone container central + 12 sous-modes + IA Mistral 7B premium card + grid 4 catégories.
Auto-cycle : sous-mode toutes les 1100ms ; passage à catégorie suivante quand fin atteinte.
Click manuel → 4500ms isManual. Catégorie 4 (Gouvernance) inclut audit trail / Loi 25 / AGPL.
#}
<section class="bg-brand-bg py-20 relative overflow-hidden" aria-labelledby="how-it-works-title">
<style>
/* ============ DECORATIVE BACKGROUND ============ */
.dictia-section-grid {
background-image:
linear-gradient(to right, rgba(15,23,42,0.05) 1px, transparent 1px),
linear-gradient(to bottom, rgba(15,23,42,0.05) 1px, transparent 1px);
background-size: 56px 56px;
mask-image: radial-gradient(ellipse 80% 60% at 50% 50%, black 0%, transparent 75%);
-webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 50%, black 0%, transparent 75%);
}
.dictia-orb-float-1 { animation: dictia-orb-1 14s ease-in-out infinite; will-change: transform; }
.dictia-orb-float-2 { animation: dictia-orb-2 18s ease-in-out infinite; will-change: transform; }
@keyframes dictia-orb-1 { 0%, 100% { transform: translate(0, 0); } 50% { transform: translate(30px, -20px); } }
@keyframes dictia-orb-2 { 0%, 100% { transform: translate(0, 0); } 50% { transform: translate(-25px, 25px); } }
/* ============ PHONE BEZEL & DEVICE FRAME ============ */
.dictia-phone-shell {
border-radius: 44px;
position: relative;
will-change: box-shadow, border-color;
}
/* Inner screen border : effet "écran encastré" subtle entre bezel et écran */
.dictia-phone-shell::before {
content: '';
position: absolute;
inset: 2px;
border-radius: 42px;
border: 1px solid rgba(255,255,255,0.05);
pointer-events: none;
background: linear-gradient(180deg, rgba(255,255,255,0.06) 0%, rgba(255,255,255,0) 28%);
z-index: 0;
}
/* Inner screen seam : ligne 1px entre bezel + écran (look device authentique) */
.dictia-phone-screen-seam {
position: absolute;
inset: 6px;
border-radius: 38px;
pointer-events: none;
box-shadow: inset 0 0 0 1px rgba(255,255,255,0.04), inset 0 1px 0 0 rgba(255,255,255,0.06);
z-index: 1;
}
.dictia-phone-glow-ring { animation: dictia-glow-ring 3.6s ease-in-out infinite; will-change: opacity; }
@keyframes dictia-glow-ring { 0%, 100% { opacity: 0.35; } 50% { opacity: 0.7; } }
/* ============ STATUS BAR + NOTCH + SPEAKER ============ */
.dictia-statusbar { font-variant-numeric: tabular-nums; }
/* ============ MIC SOUND WAVES (3 ripples) ============ */
.dictia-sound-ring {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
border-radius: 50%;
border: 1.5px solid currentColor;
opacity: 0;
will-change: transform, opacity;
animation: dictia-sound-ring-anim 2.4s ease-out infinite;
}
.dictia-sound-ring.r1 { animation-delay: 0s; }
.dictia-sound-ring.r2 { animation-delay: 0.8s; }
.dictia-sound-ring.r3 { animation-delay: 1.6s; }
@keyframes dictia-sound-ring-anim {
0% { transform: translate(-50%, -50%) scale(0.4); opacity: 0.55; }
100% { transform: translate(-50%, -50%) scale(2.4); opacity: 0; }
}
/* Mic pulsing en haut du phone */
.dictia-mic-pulse { animation: dmic-pulse 1.2s ease-in-out infinite; display: inline-block; will-change: transform; }
@keyframes dmic-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.18); } }
/* ============ IA CARD ============ */
.ia-ambient-glow { animation: ia-glow 3s ease-in-out infinite; }
@keyframes ia-glow { 0%, 100% { opacity: 0.4; } 50% { opacity: 0.75; } }
.ia-brain-glow { animation: ia-brain 2.2s ease-in-out infinite; }
@keyframes ia-brain { 0%, 100% { opacity: 0.2; } 50% { opacity: 0.55; } }
.ia-local-badge { animation: ia-local 2.5s ease-in-out infinite; }
@keyframes ia-local { 0%, 100% { opacity: 0.75; } 50% { opacity: 1; } }
/* Curseur clignotant (transcription, IA chat) */
.dictia-blink { animation: dictia-cursor-blink 0.6s steps(2, end) infinite; }
@keyframes dictia-cursor-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
/* Dot pulse */
.dictia-dot-pulse { animation: dictia-dot-pulse-anim 1.8s ease-in-out infinite; }
@keyframes dictia-dot-pulse-anim { 0%, 100% { opacity: 0.4; transform: scale(1); } 50% { opacity: 1; transform: scale(1.35); } }
/* REC dot (transcription) */
.dictia-rec-dot { animation: dictia-rec-pulse 1.4s ease-in-out infinite; }
@keyframes dictia-rec-pulse { 0%, 100% { opacity: 0.5; box-shadow: 0 0 0 0 rgba(239,68,68,0.6); } 50% { opacity: 1; box-shadow: 0 0 0 4px rgba(239,68,68,0); } }
/* Waveform bars */
.dictia-wave-bar { animation: dictia-wave 0.9s ease-in-out infinite; will-change: transform; transform-origin: bottom; }
@keyframes dictia-wave { 0%, 100% { transform: scaleY(0.3); } 50% { transform: scaleY(1); } }
/* Slide-up + fade-in (diarisation messages) */
.dictia-msg-in { animation: dictia-fade-up 0.3s ease-out both; }
@keyframes dictia-fade-up { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
/* Typing indicator (3 dots) */
.dictia-typing-dot { animation: dictia-typing 1.2s ease-in-out infinite; }
@keyframes dictia-typing { 0%, 60%, 100% { opacity: 0.25; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-2px); } }
/* Spring pop (langues, exports, users, share) */
.dictia-spring { animation: dictia-spring-pop 0.28s cubic-bezier(0.34, 1.56, 0.64, 1) both; }
@keyframes dictia-spring-pop { 0% { opacity: 0; transform: scale(0.5); } 80% { transform: scale(1.08); } 100% { opacity: 1; transform: scale(1); } }
.dictia-spring-y { animation: dictia-spring-y 0.32s cubic-bezier(0.34, 1.56, 0.64, 1) both; }
@keyframes dictia-spring-y { 0% { opacity: 0; transform: translateY(14px) scale(0.65); } 80% { transform: translateY(-2px) scale(1.05); } 100% { opacity: 1; transform: translateY(0) scale(1); } }
.dictia-fade-x { animation: dictia-fade-x 0.32s ease-out both; }
@keyframes dictia-fade-x { from { opacity: 0; transform: translateX(10px); } to { opacity: 1; transform: translateX(0); } }
.dictia-fade-y { animation: dictia-fade-y 0.3s ease-out both; }
@keyframes dictia-fade-y { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
/* Mode wrapper crossfade + scale */
.dictia-mode-fade {
animation: dictia-mode-crossfade 0.32s cubic-bezier(0.4, 0, 0.2, 1) both;
will-change: opacity, transform;
}
@keyframes dictia-mode-crossfade {
from { opacity: 0; transform: scale(0.96); }
to { opacity: 1; transform: scale(1); }
}
/* Lang highlight pulse (random langs glow) */
.dictia-lang-highlight { animation: dictia-lang-glow 2.4s ease-in-out infinite; }
@keyframes dictia-lang-glow {
0%, 100% { box-shadow: 0 0 0 0 currentColor; filter: brightness(1); }
50% { box-shadow: 0 0 8px 1px currentColor; filter: brightness(1.4); }
}
/* Connecting line (phone -> IA panel) */
.dictia-connecting-line { animation: dictia-flow 3s linear infinite; }
@keyframes dictia-flow { from { stroke-dashoffset: 24; } to { stroke-dashoffset: 0; } }
/* Auto pill */
.dictia-auto-pulse { animation: dictia-auto-pulse-anim 1.6s ease-in-out infinite; }
@keyframes dictia-auto-pulse-anim { 0%, 100% { transform: scale(1); opacity: 0.7; } 50% { transform: scale(1.4); opacity: 1; } }
/* Manuel countdown bar */
.dictia-countdown-fill { animation: dictia-countdown 4.5s linear forwards; }
@keyframes dictia-countdown { from { width: 100%; } to { width: 0%; } }
/* Feature info card hover */
.dictia-feature-card { transition: transform 0.25s ease, box-shadow 0.25s ease; }
.dictia-feature-card:hover { transform: translateY(-1px); }
/* Bottom feature button (tab indicator) */
.dictia-feat-btn { transition: transform 0.18s ease, background-color 0.2s ease, opacity 0.2s ease; will-change: transform; }
.dictia-feat-btn:hover { transform: scale(1.08); opacity: 1 !important; }
/* Hide scrollbar mobile pills */
.dictia-hide-scrollbar::-webkit-scrollbar { display: none; }
/* ============ INTERNAL SCROLL (modes content) — scrollbar invisible ============ */
.dictia-mode-scroll {
overflow-y: auto;
scrollbar-width: none;
-ms-overflow-style: none;
}
.dictia-mode-scroll::-webkit-scrollbar { display: none; }
/* Fade gradient bottom (suggest scroll continuation) */
.dictia-fade-bottom {
mask-image: linear-gradient(to bottom, black 0%, black 85%, transparent 100%);
-webkit-mask-image: linear-gradient(to bottom, black 0%, black 85%, transparent 100%);
}
/* ============ MOBILE OPTIMIZATIONS ============ */
@media (max-width: 767px) {
.dictia-statusbar,
.dictia-speaker-grille,
.dictia-camera-dot,
.dictia-notch,
.dictia-sound-ring,
.dictia-connecting-line-wrap,
.dictia-bg-orbs,
.dictia-bg-grid,
.dictia-wave-bar { display: none !important; }
/* Phone reste à largeur fixe — pas de stretch, scale léger si écran très étroit */
.dictia-phone-wrap { width: 280px !important; max-width: 100% !important; }
}
@media (max-width: 320px) {
.dictia-phone-wrap { transform: scale(0.92); transform-origin: top center; }
}
@media (prefers-reduced-motion: reduce) {
.dictia-mic-pulse, .ia-ambient-glow, .ia-brain-glow, .ia-local-badge,
.dictia-blink, .dictia-dot-pulse, .dictia-msg-in, .dictia-spring,
.dictia-spring-y, .dictia-fade-x, .dictia-fade-y, .dictia-mode-fade,
.dictia-sound-ring, .dictia-orb-float-1, .dictia-orb-float-2,
.dictia-phone-glow-ring, .dictia-rec-dot, .dictia-wave-bar,
.dictia-typing-dot, .dictia-lang-highlight, .dictia-connecting-line,
.dictia-auto-pulse, .dictia-countdown-fill { animation: none !important; }
}
</style>
{# Decorative background : grid + floating orbs (desktop only) — brand-b1/b3 only #}
<div class="dictia-bg-grid absolute inset-0 dictia-section-grid pointer-events-none" aria-hidden="true"></div>
<div class="dictia-bg-orbs absolute inset-0 pointer-events-none overflow-hidden" aria-hidden="true">
<div class="dictia-orb-float-1 absolute" style="top:12%;left:6%;width:240px;height:240px;background:radial-gradient(circle, rgba(37,99,235,0.10) 0%, transparent 70%);filter:blur(80px);"></div>
<div class="dictia-orb-float-2 absolute" style="bottom:8%;right:8%;width:280px;height:280px;background:radial-gradient(circle, rgba(192,38,211,0.10) 0%, transparent 70%);filter:blur(90px);"></div>
</div>
<div class="max-w-[1200px] mx-auto px-6 relative">
<div class="text-center max-w-2xl mx-auto mb-10">
{# Eyebrow : typo NOIRE sur fond clair, dot pulse brand-b3 pour accent #}
<p class="eyebrow text-brand-navy mb-4 inline-flex items-center gap-2 justify-center px-3 py-1 rounded-full font-mono uppercase tracking-widest text-xs font-bold"
style="background:linear-gradient(135deg, rgba(6,182,212,0.10), rgba(192,38,211,0.10)); border:1px solid rgba(192,38,211,0.20);">
<span class="dictia-dot-pulse inline-block w-1.5 h-1.5 rounded-full bg-brand-b3" aria-hidden="true"></span>
COMMENT ÇA MARCHE
</p>
<h2 id="how-it-works-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black text-brand-navy mb-4 leading-tight">
Du fichier au résumé&nbsp;<span class="grad-text">en temps réel</span>
</h2>
<p class="text-base text-brand-navy/70 mb-5">
Survolez une fonctionnalité pour voir la machine en action. Glissez pour calculer votre gain de productivité.
<a href="/contact" class="grad-text font-semibold hover:underline whitespace-nowrap ml-1">Voir une démo&nbsp;</a>
</p>
{# Stats row #}
<div class="flex flex-wrap items-center justify-center gap-x-8 gap-y-3 text-brand-navy">
<div class="flex items-center gap-2">
<span class="text-2xl font-black grad-text">4</span>
<span class="text-xs font-medium text-brand-navy/60 uppercase tracking-wider">catégories · 6 modules</span>
</div>
<div class="w-px h-6 bg-brand-navy/15 hidden sm:block" aria-hidden="true"></div>
<div class="flex items-center gap-2">
<span class="text-2xl font-black grad-text">99+</span>
<span class="text-xs font-medium text-brand-navy/60 uppercase tracking-wider">langues</span>
</div>
<div class="w-px h-6 bg-brand-navy/15 hidden sm:block" aria-hidden="true"></div>
<div class="flex items-center gap-2">
<span class="text-2xl font-black grad-text">0</span>
<span class="text-xs font-medium text-brand-navy/60 uppercase tracking-wider">cloud</span>
</div>
</div>
</div>
{# ── DASHBOARD HOLOGRAPHIQUE — 2 colonnes (phone center + IA right) ── #}
<div x-data="dictiaDashboard()" x-init="init()"
class="w-full flex flex-col lg:flex-row lg:justify-center lg:items-start gap-6 lg:gap-10 relative">
{# Connecting line SVG (phone -> IA panel, desktop only) #}
<div class="dictia-connecting-line-wrap absolute pointer-events-none hidden lg:block"
style="top:200px;left:50%;transform:translateX(-50%);width:160px;height:2px;z-index:1;"
aria-hidden="true">
<svg width="160" height="2" viewBox="0 0 160 2" preserveAspectRatio="none">
<defs>
<linearGradient id="dictia-connecting-grad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#06b6d4" stop-opacity="0.6"/>
<stop offset="100%" stop-color="#c026d3" stop-opacity="0.6"/>
</linearGradient>
</defs>
<line x1="0" y1="1" x2="160" y2="1" stroke="url(#dictia-connecting-grad)"
stroke-width="1.5" stroke-dasharray="4 4" class="dictia-connecting-line"/>
</svg>
</div>
{# ─────────── ZONE CENTER : Phone container ─────────── #}
<div class="flex flex-col items-center gap-3 lg:w-[280px] flex-shrink-0 relative z-10">
{# Phone shell (bezel, glow ring, drop shadow) — LARGEUR/HAUTEUR FIXES pour stabilité du cadre #}
<div class="relative mx-auto dictia-phone-wrap" style="width: 280px; max-width: 100%;">
{# External glow ring (pulse) — STATIC brand-b1 halo, ne change PAS selon mode pour stabilité visuelle du contour #}
<div class="absolute -inset-2 pointer-events-none dictia-phone-glow-ring"
style="border-radius: 52px; background: radial-gradient(ellipse 80% 90% at 50% 50%, rgba(37,99,235,0.18) 0%, transparent 60%);"
aria-hidden="true"></div>
{# Phone shell — bezel/border/shadow STATIQUES (couleur ne change jamais entre modes — comme un vrai téléphone) #}
<div class="dictia-phone-shell flex flex-col overflow-hidden relative"
style="width: 280px; height: 580px; border: 1.5px solid rgba(255,255,255,0.10); background: rgba(8,12,24,0.88); box-shadow: 0 30px 80px -20px rgba(0,0,0,0.6), 0 12px 32px -8px rgba(37,99,235,0.20), inset 0 0 32px rgba(255,255,255,0.04);">
{# Inner screen seam — effet "écran encastré dans le bezel" #}
<div class="dictia-phone-screen-seam" aria-hidden="true"></div>
{# Ambient overlay — STATIC subtle (pas de tint par mode) #}
<div class="absolute inset-0 pointer-events-none"
style="border-radius: 44px; background-color: rgba(255,255,255,0.015);"
aria-hidden="true"></div>
{# Notch / Dynamic Island avec mini speaker grille (3 dots) #}
<div class="dictia-notch absolute left-1/2 -translate-x-1/2 z-30 flex items-center justify-center gap-1"
style="top:8px;width:78px;height:18px;border-radius:12px;background:rgba(0,0,0,0.85);box-shadow:inset 0 1px 2px rgba(0,0,0,0.5);"
aria-hidden="true">
{# Speaker grille (3 dots) #}
<span class="block rounded-full" style="width:3px;height:3px;background:rgba(255,255,255,0.10);"></span>
<span class="block rounded-full" style="width:3px;height:3px;background:rgba(255,255,255,0.12);"></span>
<span class="block rounded-full" style="width:3px;height:3px;background:rgba(255,255,255,0.10);"></span>
{# Camera dot #}
<span class="block rounded-full ml-1" style="width:5px;height:5px;background:rgba(255,255,255,0.18);box-shadow:inset 0 0 1px rgba(0,0,0,0.5);"></span>
</div>
{# Status bar mobile-style (9:41 + signals) — vraies icônes SVG device-like #}
<div class="dictia-statusbar relative z-20 flex items-center justify-between px-6 font-mono text-[10px] font-semibold"
style="height:34px;padding-top:12px;color:rgba(255,255,255,0.65);"
aria-hidden="true">
<span class="font-bold">9:41</span>
<div class="flex items-center gap-1.5">
{# Signal — 4 bars croissantes, look device authentique #}
<svg viewBox="0 0 16 10" width="13" height="9" fill="currentColor">
<rect x="0" y="7" width="2.5" height="3" rx="0.5"/>
<rect x="3.5" y="5" width="2.5" height="5" rx="0.5"/>
<rect x="7" y="2.5" width="2.5" height="7.5" rx="0.5"/>
<rect x="10.5" y="0" width="2.5" height="10" rx="0.5"/>
</svg>
{# Wifi — 3 arcs concentriques + dot, look iOS authentique #}
<svg viewBox="0 0 16 12" width="14" height="10" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round">
<path d="M1 4.5 Q8 -1.5 15 4.5"/>
<path d="M3.2 7 Q8 3.2 12.8 7"/>
<path d="M5.4 9.5 Q8 7.5 10.6 9.5"/>
<circle cx="8" cy="11" r="0.9" fill="currentColor"/>
</svg>
{# Battery 80% — fill réel à 80% pour authenticité #}
<svg viewBox="0 0 22 10" width="18" height="9" fill="none">
<rect x="0.5" y="0.5" width="18" height="9" rx="2" stroke="currentColor" stroke-opacity="0.6" stroke-width="0.8" fill="none"/>
<rect x="19.5" y="3.5" width="1.5" height="3" rx="0.5" fill="currentColor" fill-opacity="0.6"/>
<rect x="2" y="2" width="14" height="6" rx="1" fill="currentColor"/>
</svg>
</div>
</div>
{# TOP : Logo + Mic pulsing (with sound waves) — hauteur fixe 96px #}
<div class="flex flex-col items-center justify-center gap-2 relative z-10 flex-shrink-0"
style="height: 96px; padding-top: 12px; border-bottom: 1px solid rgba(255,255,255,0.06);">
<img src="/static/images/dictia-logo-nom.png" alt="DictIA"
style="width: 92px; height: 28px; object-fit: contain; opacity: 0.85;">
<div class="relative" style="width:36px;height:36px;">
{# 3 sound rings emanating #}
<span class="dictia-sound-ring r1" :style="`color: ${activeColor}; width:24px; height:24px;`" aria-hidden="true"></span>
<span class="dictia-sound-ring r2" :style="`color: ${activeColor}; width:24px; height:24px;`" aria-hidden="true"></span>
<span class="dictia-sound-ring r3" :style="`color: ${activeColor}; width:24px; height:24px;`" aria-hidden="true"></span>
<span class="dictia-mic-pulse absolute" style="top:50%;left:50%;transform:translate(-50%,-50%);" :style="`color: ${activeColor};`" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:22px;height:22px;">
<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="23"/>
<line x1="8" y1="23" x2="16" y2="23"/>
</svg>
</span>
</div>
</div>
{# MIDDLE : 6 modes — hauteur fixe 374px, overflow hidden strict #}
<div class="dictia-phone-middle relative z-10 flex-shrink-0" style="height: 374px; overflow: hidden;"
role="status" aria-live="polite" aria-atomic="true">
{# Mode 1 : Transcription (header REC + waveform + upload bar + words) #}
<template x-if="displayMode === 1">
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden" x-data="trModeData()" x-init="init()">
{# Header bar (file + REC) — compact mic icon + filename + REC dot #}
<div class="flex items-center justify-between px-3 py-2 flex-shrink-0"
style="background:rgba(6,182,212,0.06);border-bottom:1px solid rgba(6,182,212,0.12);">
<div class="flex items-center gap-1.5 min-w-0">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-2.5 h-2.5 text-brand-b2 shrink-0" 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="23"/>
</svg>
<span class="font-mono text-[10px] text-white/60 truncate">reunion-jan14.mp3</span>
</div>
<div class="flex items-center gap-1 shrink-0">
<span class="dictia-rec-dot block rounded-full" style="width:5px;height:5px;background:#ef4444;"></span>
<span class="font-mono text-[9px] font-bold tracking-widest" style="color:rgba(239,68,68,0.90);">REC</span>
</div>
</div>
{# Waveform animée (16 bars régulières — pattern symétrique) #}
<div class="flex items-end justify-center gap-0.5 px-3 py-2 flex-shrink-0" style="height:28px;" aria-hidden="true">
<div class="dictia-wave-bar bg-brand-b2" style="width:2px;height:8px;animation-delay:0s;"></div>
<div class="dictia-wave-bar bg-brand-b2" style="width:2px;height:12px;animation-delay:0.08s;"></div>
<div class="dictia-wave-bar bg-brand-b2" style="width:2px;height:18px;animation-delay:0.16s;"></div>
<div class="dictia-wave-bar bg-brand-b2" style="width:2px;height:14px;animation-delay:0.24s;"></div>
<div class="dictia-wave-bar bg-brand-b2" style="width:2px;height:20px;animation-delay:0.32s;"></div>
<div class="dictia-wave-bar bg-brand-b2" style="width:2px;height:16px;animation-delay:0.4s;"></div>
<div class="dictia-wave-bar bg-brand-b2" style="width:2px;height:22px;animation-delay:0.48s;"></div>
<div class="dictia-wave-bar bg-brand-b2" style="width:2px;height:18px;animation-delay:0.56s;"></div>
<div class="dictia-wave-bar bg-brand-b2" style="width:2px;height:22px;animation-delay:0.48s;"></div>
<div class="dictia-wave-bar bg-brand-b2" style="width:2px;height:16px;animation-delay:0.4s;"></div>
<div class="dictia-wave-bar bg-brand-b2" style="width:2px;height:20px;animation-delay:0.32s;"></div>
<div class="dictia-wave-bar bg-brand-b2" style="width:2px;height:14px;animation-delay:0.24s;"></div>
<div class="dictia-wave-bar bg-brand-b2" style="width:2px;height:18px;animation-delay:0.16s;"></div>
<div class="dictia-wave-bar bg-brand-b2" style="width:2px;height:12px;animation-delay:0.08s;"></div>
<div class="dictia-wave-bar bg-brand-b2" style="width:2px;height:8px;animation-delay:0s;"></div>
<div class="dictia-wave-bar bg-brand-b2" style="width:2px;height:6px;animation-delay:0.04s;"></div>
</div>
<div x-show="phase === 'upload'" class="flex-1 flex flex-col items-center justify-center gap-3 px-4 overflow-hidden">
<div class="flex flex-col items-center gap-2">
{# File card MP3 — design device pro avec corner fold #}
<div class="flex flex-col items-center justify-center relative"
style="width:44px;height:54px;background-color:rgba(6,182,212,0.12);border:1.5px solid rgba(6,182,212,0.45);border-radius:6px;">
<div class="absolute top-0 right-0" style="width:0;height:0;border-style:solid;border-width:0 8px 8px 0;border-color:transparent rgba(0,0,0,0.45) transparent transparent;"></div>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-brand-b2" style="width:14px;height:14px;" 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="23"/>
</svg>
<span class="font-mono text-[9px] font-bold tracking-wider mt-1" style="color:rgba(6,182,212,0.80);">MP3</span>
</div>
</div>
<div class="w-full flex flex-col gap-1.5">
<div class="flex justify-between items-center">
<span class="font-mono text-[10px]" style="color:rgba(6,182,212,0.75);" x-text="progress < 100 ? 'Envoi en cours…' : 'Prêt ✓'"></span>
<span class="font-mono text-[10px] font-semibold" style="color:rgba(6,182,212,0.65);"><span x-text="progress"></span>%</span>
</div>
{# Track gris fin + fill brand-b2 glow #}
<div class="w-full rounded-full overflow-hidden relative" style="height:4px;background-color:rgba(255,255,255,0.06);box-shadow:inset 0 0 2px rgba(0,0,0,0.4);">
<div class="h-full rounded-full bg-brand-b2" :style="`width: ${progress}%; box-shadow: 0 0 8px rgba(6,182,212,0.7); transition: width 60ms linear;`"></div>
</div>
</div>
</div>
{# Transcript zone — scroll interne avec fade bottom #}
<div x-show="phase === 'transcribing'" class="flex-1 p-3 dictia-mode-scroll dictia-fade-bottom flex flex-col justify-start" x-ref="transcriptBox">
<p class="font-mono leading-relaxed break-words text-[10px] rounded"
style="color:rgba(6,182,212,0.92);background:rgba(6,182,212,0.04);padding:6px 8px;border-left:2px solid rgba(6,182,212,0.4);">
<span x-text="words.slice(0, n + 1).join(' ')"></span><span class="dictia-blink ml-px inline-block align-middle bg-brand-b2" style="width:3px;height:10px;">&nbsp;</span>
</p>
</div>
</div>
</template>
{# Mode 2 : Diarisation (conversation Sophie/Marc/Julie) #}
<template x-if="displayMode === 2">
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden" x-data="diaModeData()" x-init="init()">
{# Conversation header — 3 avatars empilés 24×24 avec bordure white/15 #}
<div class="flex items-center justify-between px-3 py-2 flex-shrink-0"
style="background:rgba(37,99,235,0.06);border-bottom:1px solid rgba(37,99,235,0.12);">
<div class="flex items-center gap-2">
<div class="flex">
<div class="rounded-full flex items-center justify-center text-[10px] font-bold text-white"
style="width:18px;height:18px;background:linear-gradient(135deg,#06b6d4,#2563eb);border:1.5px solid rgba(8,12,24,0.9);">S</div>
<div class="rounded-full flex items-center justify-center text-[10px] font-bold text-white"
style="width:18px;height:18px;background:linear-gradient(135deg,#2563eb,#06b6d4);border:1.5px solid rgba(8,12,24,0.9);margin-left:-6px;">M</div>
<div class="rounded-full flex items-center justify-center text-[10px] font-bold text-white"
style="width:18px;height:18px;background:linear-gradient(135deg,#c026d3,#2563eb);border:1.5px solid rgba(8,12,24,0.9);margin-left:-6px;">J</div>
</div>
<span class="font-mono text-[10px] text-white/65">Réunion · 3 participants</span>
</div>
<span class="dictia-rec-dot block rounded-full" style="width:5px;height:5px;background:#10b981;"></span>
</div>
<div class="flex-1 flex flex-col justify-end gap-1.5 p-2 dictia-mode-scroll dictia-fade-bottom">
<template x-for="(msg, i) in visible" :key="`${cycle}-${startIdx + i}`">
<div class="dictia-msg-in flex items-start gap-1.5">
<div class="rounded-full flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5"
style="width:24px;height:24px;"
:style="`background:linear-gradient(135deg, ${msg.c}33, ${msg.c}22); border: 1.5px solid ${msg.c}66; color: ${msg.c};`"
x-text="msg.s.charAt(0)"></div>
<div class="rounded px-2 py-1.5 flex-1 relative"
style="max-width:80%;"
:style="`background-color: ${msg.c}10; border: 1px solid ${msg.c}28; box-shadow: 0 1px 4px rgba(0,0,0,0.15);`">
<div class="flex items-baseline justify-between gap-2 mb-0.5">
<p class="text-[10px] font-bold leading-none" :style="`color: ${msg.c};`" x-text="msg.s"></p>
<span class="text-[9px] font-mono" style="color:rgba(255,255,255,0.30);" x-text="`09:0${i+1}`"></span>
</div>
<p class="text-[10px] font-mono leading-snug" style="color:rgba(255,255,255,0.80);" x-text="msg.t"></p>
</div>
</div>
</template>
{# Typing indicator (3 dots pulse en boucle) #}
<div class="flex items-center gap-1.5 px-1" x-show="shownCount < CONVO.length" aria-hidden="true">
<div class="rounded-full" style="width:24px;height:24px;background:rgba(255,255,255,0.05);border:1px dashed rgba(255,255,255,0.15);"></div>
<div class="flex items-center gap-1 rounded px-2 py-1.5"
style="background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.08);">
<span class="dictia-typing-dot block rounded-full" style="width:4px;height:4px;background:rgba(255,255,255,0.5);animation-delay:0s;"></span>
<span class="dictia-typing-dot block rounded-full" style="width:4px;height:4px;background:rgba(255,255,255,0.5);animation-delay:0.2s;"></span>
<span class="dictia-typing-dot block rounded-full" style="width:4px;height:4px;background:rgba(255,255,255,0.5);animation-delay:0.4s;"></span>
</div>
</div>
</div>
</div>
</template>
{# Mode 3 : 99+ langues (grille 100 codes — ripple depuis centre, highlight aléatoire) #}
<template x-if="displayMode === 3">
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden" x-data="langModeData()">
{# Header DÉTECTION AUTOMATIQUE #}
<div class="flex items-center justify-center gap-2 py-2 flex-shrink-0"
style="background:rgba(6,182,212,0.06);border-bottom:1px solid rgba(6,182,212,0.12);">
<span class="dictia-dot-pulse block rounded-full bg-brand-b2" style="width:5px;height:5px;"></span>
<span class="font-mono text-[10px] font-bold tracking-widest" style="color:rgba(6,182,212,0.90);">DÉTECTION AUTOMATIQUE</span>
</div>
{# Scroll interne pour les 100 codes — fade bottom suggère continuation #}
<div class="flex-1 dictia-mode-scroll dictia-fade-bottom" style="padding:8px 6px;">
<div class="grid content-start" style="grid-template-columns: repeat(7, 1fr); gap: 4px;">
<template x-for="(lang, i) in LANGS" :key="lang">
<span class="font-mono font-bold text-center rounded dictia-spring text-[10px]"
:class="HIGHLIGHTS.includes(i) ? 'dictia-lang-highlight' : ''"
:style="`line-height:18px;background-color:${LANG_COLORS[i % LANG_COLORS.length]}14;border:1px solid ${LANG_COLORS[i % LANG_COLORS.length]}30;color:${LANG_COLORS[i % LANG_COLORS.length]};animation-delay:${rippleDelay(i)}ms;`"
x-text="lang"></span>
</template>
</div>
</div>
{# Counter live en bas #}
<div class="flex items-center justify-between px-3 py-2 flex-shrink-0"
style="background:rgba(6,182,212,0.04);border-top:1px solid rgba(6,182,212,0.10);">
<span class="font-mono dictia-fade-y text-[9px] tracking-wide" style="color:rgba(6,182,212,0.75);animation-delay:1.4s;">
FR · EN · ES · DE · ZH · JA · AR
</span>
<span class="font-mono font-bold dictia-fade-y text-[10px] tracking-wider text-brand-b2" style="animation-delay:1.4s;">
99+ langues détectées
</span>
</div>
</div>
</template>
{# Mode 4 : Exports (grid 4x2 + files détaillés + checkmark) #}
<template x-if="displayMode === 4">
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden" x-data="expModeData()">
{# Header subtle #}
<div class="flex items-center justify-between px-3 py-2 flex-shrink-0"
style="background:rgba(37,99,235,0.06);border-bottom:1px solid rgba(37,99,235,0.12);">
<span class="font-mono text-[10px] font-bold tracking-widest" style="color:rgba(37,99,235,0.90);">EXPORTS DISPONIBLES</span>
<span class="font-mono text-[9px] text-brand-b1/60">7 formats</span>
</div>
{# Grid 4×2 — staggered drop animation #}
<div class="grid grid-cols-4 gap-2 p-3 flex-1 content-center justify-items-center overflow-hidden">
<template x-for="(f, i) in FILE_TYPES" :key="f.ext">
<div class="dictia-spring-y" :style="`animation-delay:${i * 90}ms;`">
<div class="flex flex-col items-center justify-end gap-0.5 relative"
style="width:42px;height:50px;border-radius:4px;"
:style="`background-color:${f.bg}CC;border:1.5px solid ${f.bg};box-shadow:0 4px 12px ${f.bg}40;`">
<div class="absolute top-0 right-0" style="width:0;height:0;border-style:solid;border-width:0 9px 9px 0;border-color:transparent rgba(0,0,0,0.45) transparent transparent;"></div>
{# Mini "page" lines #}
<div class="absolute top-2 left-1.5 right-3 flex flex-col gap-0.5" aria-hidden="true">
<div :style="`height:1px;background:${f.fg}55;width:80%;`"></div>
<div :style="`height:1px;background:${f.fg}40;width:65%;`"></div>
<div :style="`height:1px;background:${f.fg}40;width:75%;`"></div>
</div>
<span class="text-[11px] font-black leading-none" :style="`color:${f.fg};margin-bottom:6px;`" x-text="f.sym"></span>
<span class="text-[9px] font-mono font-bold leading-none mb-1.5" :style="`color:${f.fg}BB;`" x-text="f.ext"></span>
</div>
</div>
</template>
</div>
{# Subtitle dynamique avec checkmark — vert sémantique conservé pour status "ready" #}
<div class="flex items-center justify-center gap-2 px-3 py-2 dictia-fade-y flex-shrink-0"
style="background:rgba(16,185,129,0.06);border-top:1px solid rgba(16,185,129,0.14);animation-delay:0.9s;">
<span class="inline-flex items-center justify-center rounded-full" style="width:13px;height:13px;background:#10b981;">
<svg viewBox="0 0 24 24" fill="none" stroke="rgba(8,12,24,0.9)" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round" style="width:8px;height:8px;" aria-hidden="true">
<polyline points="20 6 9 17 4 12"/>
</svg>
</span>
<span class="font-mono text-[10px] font-bold tracking-widest" style="color:#10b981;">7 FORMATS PRÊTS</span>
</div>
</div>
</template>
{# Mode 5 : Users (counter centré + avatars colors variés + connecting lines) #}
<template x-if="displayMode === 5">
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden" x-data="usersModeData()" x-init="init()">
{# Header counter — gros chiffre Inter font-black #}
<div class="flex items-center justify-center gap-2 py-2 flex-shrink-0"
style="background:rgba(192,38,211,0.06);border-bottom:1px solid rgba(192,38,211,0.12);">
<span class="font-mono text-[10px] font-bold tracking-widest" style="color:rgba(192,38,211,0.85);">UTILISATEURS</span>
<span class="font-black text-base text-brand-b3 leading-none" x-text="count.toString().padStart(2, '0')"></span>
<span class="font-mono text-[10px]" style="color:rgba(192,38,211,0.50);">→ ∞</span>
</div>
{# Avatar grid with connecting lines SVG #}
<div class="flex-1 relative p-3 flex flex-wrap gap-2 items-center justify-center content-center overflow-hidden">
{# SVG connecting lines (brand b1/b2/b3 only) — opacité subtle #}
<svg class="absolute inset-0 pointer-events-none" width="100%" height="100%" aria-hidden="true" style="opacity:0.18;">
<line x1="20%" y1="30%" x2="50%" y2="50%" stroke="#c026d3" stroke-width="0.5" stroke-dasharray="2 2"/>
<line x1="80%" y1="30%" x2="50%" y2="50%" stroke="#06b6d4" stroke-width="0.5" stroke-dasharray="2 2"/>
<line x1="20%" y1="70%" x2="50%" y2="50%" stroke="#c026d3" stroke-width="0.5" stroke-dasharray="2 2"/>
<line x1="80%" y1="70%" x2="50%" y2="50%" stroke="#2563eb" stroke-width="0.5" stroke-dasharray="2 2"/>
</svg>
<template x-for="i in count" :key="`${cycle}-${i}`">
<div class="rounded-full flex items-center justify-center relative z-10"
:class="i === count ? 'dictia-spring' : ''"
:style="`width:24px;height:24px;background-color:${USER_COLORS[(i-1) % USER_COLORS.length]}22;border:1.5px solid ${USER_COLORS[(i-1) % USER_COLORS.length]}77;`">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:11px;height:11px;" :style="`color:${USER_COLORS[(i-1) % USER_COLORS.length]};`" aria-hidden="true">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
<circle cx="12" cy="7" r="4"/>
</svg>
</div>
</template>
</div>
</div>
</template>
{# Mode 6 : Share (breadcrumb + toolbar + rows structurées) #}
<template x-if="displayMode === 6">
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden">
{# Breadcrumb compact 12px max + toolbar 4 icons #}
<div class="flex items-center gap-1.5 px-3 py-2 flex-shrink-0"
style="background:rgba(6,182,212,0.06);border-bottom:1px solid rgba(6,182,212,0.12);">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-brand-b2 shrink-0" style="width:9px;height:9px;" 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>
<span class="font-mono text-[10px] text-white/55">Mes&nbsp;dossiers</span>
<span class="text-[9px] text-white/30"></span>
<span class="font-mono text-[10px] font-bold text-brand-b2">Réunions</span>
{# Toolbar mini 4 icons #}
<div class="flex items-center gap-1.5 ml-auto" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-white/40" style="width:10px;height:10px;">
<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
</svg>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-white/40" style="width:10px;height:10px;">
<polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/>
</svg>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-white/40" style="width:10px;height:10px;">
<path d="M3 6h18M3 12h18M3 18h18"/>
</svg>
<svg viewBox="0 0 24 24" fill="currentColor" class="text-white/40" style="width:10px;height:10px;">
<circle cx="5" cy="12" r="1.6"/><circle cx="12" cy="12" r="1.6"/><circle cx="19" cy="12" r="1.6"/>
</svg>
</div>
</div>
<div class="flex-1 flex flex-col gap-2 dictia-mode-scroll dictia-fade-bottom" style="padding:8px;">
{# Folders #}
<div class="flex gap-1 flex-wrap">
<template x-for="(f, fi) in [{name:'Réunions',color:'#06b6d4',count:12},{name:'Entretiens',color:'#2563eb',count:7},{name:'Formations',color:'#c026d3',count:24}]" :key="f.name">
<div class="flex items-center gap-1 rounded dictia-fade-y"
:style="`background-color:${f.color}18;border:1px solid ${f.color}35;padding:3px 6px;animation-delay:${fi * 100}ms;`">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:9px;height:9px;" :style="`color:${f.color};`" 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>
<span class="font-mono text-[9px] font-semibold" :style="`color:${f.color};`" x-text="f.name"></span>
<span class="font-mono text-[9px]" :style="`color:${f.color}88;`" x-text="f.count"></span>
</div>
</template>
</div>
{# Tags pills #}
<div class="flex flex-wrap gap-1">
<template x-for="(tag, ti) in ['#RH','#Direction','#Urgent','#Archivé','#Suivi','#Confidentiel','#2024']" :key="tag">
<span class="rounded-full dictia-spring font-mono text-[9px] text-brand-b2"
style="padding:2px 6px;background-color:rgba(6,182,212,0.10);border:1px solid rgba(6,182,212,0.25);"
:style="`animation-delay:${350 + ti * 70}ms;`"
x-text="tag"></span>
</template>
</div>
{# File rows structurées — hover highlight #}
<div class="flex flex-col gap-1">
<template x-for="(file, fi2) in [{name:'CR-Réunion-Jan14',folder:'Réunions',color:'#06b6d4'},{name:'Entretien-Sophie',folder:'Entretiens',color:'#2563eb'},{name:'Formation-RGPD',folder:'Formations',color:'#c026d3'}]" :key="file.name">
<div class="dictia-share-row group flex items-center gap-2 rounded dictia-fade-x cursor-pointer"
:style="`background-color:${file.color}0C;border:1px solid ${file.color}22;padding:5px 8px;animation-delay:${650 + fi2 * 120}ms;transition:background-color 0.2s;`">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0" style="width:10px;height:10px;" :style="`color:${file.color};`" 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"/>
</svg>
<div class="flex-1 min-w-0">
<p class="truncate font-mono text-[10px] text-white/80" x-text="file.name"></p>
</div>
<span class="rounded font-mono shrink-0 text-[9px]"
style="padding:2px 5px;"
:style="`background:${file.color}18;color:${file.color};border:1px solid ${file.color}33;`"
x-text="file.folder"></span>
<span class="shrink-0" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="currentColor" style="width:10px;height:10px;" :style="`color:${file.color}77;`">
<circle cx="5" cy="12" r="1.6"/><circle cx="12" cy="12" r="1.6"/><circle cx="19" cy="12" r="1.6"/>
</svg>
</span>
</div>
</template>
</div>
</div>
<p class="text-center font-mono dictia-fade-y px-3 py-2 flex-shrink-0 text-[9px] tracking-wide"
style="color:rgba(6,182,212,0.70);background:rgba(6,182,212,0.04);border-top:1px solid rgba(6,182,212,0.10);animation-delay:1.2s;">
partage sécurisé · recherche plein texte
</p>
</div>
</template>
{# Mode 0 : IA chat (Mistral 7B local Q&R typing) — visible si user clique sur IA dans grid #}
<template x-if="displayMode === 0">
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden" x-data="iaModeData()" x-init="init()">
<div class="flex items-center justify-center gap-2 py-2 flex-shrink-0"
style="border-bottom: 1px solid rgba(192,38,211,0.14); background-color: rgba(192,38,211,0.05);">
<span class="rounded-full flex-shrink-0 dictia-dot-pulse block" style="width:6px;height:6px;background-color:#10b981;"></span>
<span class="font-mono text-[10px] font-bold tracking-widest" style="color:rgba(192,38,211,0.90);">MISTRAL 7B · LOCAL</span>
</div>
<div class="flex-1 flex flex-col justify-end gap-2 p-2 dictia-mode-scroll dictia-fade-bottom">
<template x-for="(msg, mi) in msgs" :key="mi">
<div class="dictia-fade-y" :class="msg.role === 'user' ? 'flex justify-end' : 'flex justify-start'">
<div class="rounded px-2 py-1.5" style="max-width:88%;"
:style="msg.role === 'user' ? 'background-color:rgba(255,255,255,0.07);border:1px solid rgba(255,255,255,0.12);' : 'background-color:rgba(192,38,211,0.12);border:1px solid rgba(192,38,211,0.30);'">
<p class="font-mono leading-relaxed whitespace-pre-line text-[10px]"
:style="msg.role === 'user' ? 'color:rgba(255,255,255,0.70);' : 'color:rgba(192,38,211,0.95);'">
<span x-text="msg.text"></span><span x-show="msg.role === 'bot' && mi === msgs.length - 1" class="dictia-blink ml-px text-brand-b3"></span>
</p>
</div>
</div>
</template>
</div>
<div class="flex items-center justify-center gap-1.5 py-2 flex-shrink-0" style="border-top:1px solid rgba(192,38,211,0.10);background:rgba(192,38,211,0.03);">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="shrink-0" style="width:9px;height:9px;color:rgba(16,185,129,0.85);" aria-hidden="true">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
</svg>
<span class="font-mono text-[9px] tracking-wide" style="color:rgba(16,185,129,0.85);">0 donnée envoyée au cloud</span>
</div>
</div>
</template>
{# Mode 7 : Recording live (waveform live + minuteur + 3 boutons mic/syst/combiné) — Catégorie Capture #}
<template x-if="displayMode === 7">
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden" x-data="recModeData()" x-init="init()">
<div class="flex items-center justify-between px-3 py-2 flex-shrink-0"
style="background:rgba(6,182,212,0.06);border-bottom:1px solid rgba(6,182,212,0.12);">
<span class="font-mono text-[10px] font-bold tracking-widest" style="color:rgba(6,182,212,0.90);">ENREGISTREMENT LIVE</span>
<div class="flex items-center gap-1">
<span class="dictia-rec-dot block rounded-full" style="width:5px;height:5px;background:#ef4444;"></span>
<span class="font-mono text-[9px] font-bold tracking-widest" style="color:rgba(239,68,68,0.90);">REC</span>
</div>
</div>
{# Minuteur central + waveform live #}
<div class="flex flex-col items-center justify-center gap-2 flex-1 px-3">
<p class="font-mono font-black text-2xl text-white tabular-nums leading-none" x-text="time"></p>
<p class="font-mono text-[9px] uppercase tracking-widest" style="color:rgba(6,182,212,0.65);">Microphone + Système</p>
{# Waveform 24 barres dynamiques (pattern live aléatoire CSS-only) #}
<div class="flex items-center justify-center gap-0.5 mt-1" style="height:36px;" aria-hidden="true">
<template x-for="(h, bi) in BARS" :key="bi">
<div class="dictia-wave-bar bg-brand-b2"
:style="`width:2px;height:${h}px;animation-delay:${bi * 30}ms;`"></div>
</template>
</div>
{# Estimation taille fichier #}
<p class="font-mono text-[9px] mt-2" style="color:rgba(6,182,212,0.55);">~<span x-text="size"></span> Mo · auto-save 5 s</p>
</div>
{# 3 boutons sources (rouge mic / bleu système / violet combiné) — selon manuel #}
<div class="flex items-center justify-center gap-2 px-3 py-2 flex-shrink-0"
style="background:rgba(8,12,24,0.5);border-top:1px solid rgba(255,255,255,0.06);">
<span class="rounded-full flex items-center justify-center" style="width:18px;height:18px;background:rgba(239,68,68,0.30);border:1px solid rgba(239,68,68,0.55);">
<svg viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:9px;height:9px;" 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"/>
</svg>
</span>
<span class="rounded-full flex items-center justify-center" style="width:18px;height:18px;background:rgba(37,99,235,0.30);border:1px solid rgba(37,99,235,0.55);">
<svg viewBox="0 0 24 24" fill="none" stroke="#2563eb" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:9px;height:9px;" aria-hidden="true">
<rect x="3" y="4" width="18" height="14" rx="2"/><line x1="9" y1="22" x2="15" y2="22"/>
</svg>
</span>
<span class="rounded-full flex items-center justify-center dictia-spring" style="width:22px;height:22px;background:rgba(192,38,211,0.30);border:1.5px solid rgba(192,38,211,0.65);">
<svg viewBox="0 0 24 24" fill="none" stroke="#c026d3" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:11px;height:11px;" aria-hidden="true">
<path d="M12 2v8M5 7l7 7 7-7"/>
</svg>
</span>
</div>
</div>
</template>
{# Mode 8 : Recherche sémantique (search bar + résultats highlight) — Catégorie Capture #}
<template x-if="displayMode === 8">
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden" x-data="searchModeData()" x-init="init()">
<div class="flex items-center justify-center gap-2 py-2 flex-shrink-0"
style="background:rgba(6,182,212,0.06);border-bottom:1px solid rgba(6,182,212,0.12);">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:11px;height:11px;color:rgba(6,182,212,0.90);" aria-hidden="true">
<circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
</svg>
<span class="font-mono text-[10px] font-bold tracking-widest" style="color:rgba(6,182,212,0.90);">RECHERCHE IA SÉMANTIQUE</span>
</div>
{# Search bar simulée (typing query) #}
<div class="px-3 py-2 flex-shrink-0">
<div class="rounded flex items-center gap-2 px-2 py-1.5"
style="background:rgba(255,255,255,0.06);border:1px solid rgba(6,182,212,0.30);">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:9px;height:9px;color:rgba(6,182,212,0.70);" aria-hidden="true">
<circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
</svg>
<p class="font-mono text-[10px] flex-1 truncate" style="color:rgba(255,255,255,0.85);">
<span x-text="query"></span><span class="dictia-blink ml-px inline-block align-middle bg-brand-b2" style="width:2px;height:9px;">&nbsp;</span>
</p>
</div>
</div>
{# Résultats : 3 transcriptions matchées avec phrase highlight #}
<div class="flex-1 dictia-mode-scroll dictia-fade-bottom flex flex-col gap-1.5 px-3 pb-2">
<template x-for="(r, ri) in results" :key="ri">
<div class="rounded dictia-fade-y"
:style="`background-color:${r.c}10;border:1px solid ${r.c}30;padding:6px 8px;animation-delay:${ri * 280 + 600}ms;`">
<div class="flex items-center gap-1.5 mb-1">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" :style="`color:${r.c};width:9px;height:9px;`" 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"/>
</svg>
<span class="font-mono text-[9px] font-bold" :style="`color:${r.c};`" x-text="r.title"></span>
<span class="font-mono text-[9px] ml-auto" style="color:rgba(255,255,255,0.40);" x-text="r.score + ' %'"></span>
</div>
<p class="font-mono text-[9px] leading-snug" style="color:rgba(255,255,255,0.75);">
<span :style="`background:${r.c}28;color:${r.c};padding:0 2px;border-radius:2px;`" x-text="r.match"></span>
</p>
</div>
</template>
</div>
</div>
</template>
{# Mode 9 : Résumé + extraction d'événements (panel structuré décisions/actions/ICS) — Catégorie IA #}
<template x-if="displayMode === 9">
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden" x-data="summaryModeData()" x-init="init()">
<div class="flex items-center justify-between px-3 py-2 flex-shrink-0"
style="background:rgba(37,99,235,0.06);border-bottom:1px solid rgba(37,99,235,0.12);">
<span class="font-mono text-[10px] font-bold tracking-widest" style="color:rgba(37,99,235,0.90);">RÉSUMÉ EXÉCUTIF</span>
<span class="font-mono text-[9px] text-brand-b1/60">Mistral Nemo 12B</span>
</div>
<div class="flex-1 dictia-mode-scroll dictia-fade-bottom flex flex-col gap-2 px-3 py-2">
{# Décisions #}
<div class="dictia-fade-y" style="animation-delay:200ms;">
<p class="font-mono text-[9px] font-bold uppercase tracking-wider mb-1" style="color:rgba(37,99,235,0.85);">Décisions</p>
<template x-for="(d, di) in decisions.slice(0, dShown)" :key="di">
<div class="flex items-start gap-1.5 mb-0.5 dictia-fade-x">
<span class="text-brand-b1 leading-none mt-0.5" aria-hidden="true"></span>
<p class="text-[10px] font-mono leading-snug" style="color:rgba(255,255,255,0.85);" x-text="d"></p>
</div>
</template>
</div>
{# Actions à suivre #}
<div class="dictia-fade-y" style="animation-delay:900ms;" x-show="dShown >= decisions.length">
<p class="font-mono text-[9px] font-bold uppercase tracking-wider mb-1" style="color:rgba(192,38,211,0.85);">Actions à suivre</p>
<template x-for="(a, ai) in actions.slice(0, aShown)" :key="ai">
<div class="flex items-start gap-1.5 mb-0.5 dictia-fade-x">
<span class="rounded flex items-center justify-center mt-0.5 flex-shrink-0" style="width:9px;height:9px;background:rgba(192,38,211,0.30);border:1px solid rgba(192,38,211,0.55);" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="#c026d3" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" style="width:5px;height:5px;"><polyline points="20 6 9 17 4 12"/></svg>
</span>
<p class="text-[10px] font-mono leading-snug" style="color:rgba(255,255,255,0.85);" x-text="a"></p>
</div>
</template>
</div>
{# Événement ICS détecté (extraction calendrier) #}
<div class="dictia-fade-y rounded mt-1 flex-shrink-0" x-show="aShown >= actions.length"
style="background:rgba(6,182,212,0.10);border:1px solid rgba(6,182,212,0.30);padding:5px 8px;animation-delay:1800ms;">
<div class="flex items-center gap-1.5 mb-0.5">
<svg viewBox="0 0 24 24" fill="none" stroke="#06b6d4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:9px;height:9px;" aria-hidden="true">
<rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/>
</svg>
<span class="font-mono text-[9px] font-bold tracking-widest" style="color:rgba(6,182,212,0.95);">ÉVÉNEMENT DÉTECTÉ → ICS</span>
</div>
<p class="font-mono text-[9px]" style="color:rgba(255,255,255,0.70);">Comité de suivi · 21 janv. 14h00</p>
</div>
</div>
</div>
</template>
{# Mode 10 : Intégrations animées (logos hub Word, Outlook, Teams, Notion, Obsidian, Zapier...) — Catégorie Distribution #}
<template x-if="displayMode === 10">
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden" x-data="integModeData()" x-init="init()">
<div class="flex items-center justify-center gap-2 py-2 flex-shrink-0"
style="background:rgba(192,38,211,0.06);border-bottom:1px solid rgba(192,38,211,0.12);">
<span class="dictia-dot-pulse block rounded-full" style="width:5px;height:5px;background:#c026d3;"></span>
<span class="font-mono text-[10px] font-bold tracking-widest" style="color:rgba(192,38,211,0.90);">INTÉGRATIONS HUB</span>
</div>
{# Hub central + 8 logos en orbite (grille pour mobile, ring pour desktop) #}
<div class="flex-1 relative flex items-center justify-center px-3 py-2 overflow-hidden">
{# Cercle central DictIA #}
<div class="rounded-full flex items-center justify-center relative z-10"
style="width:42px;height:42px;background:linear-gradient(135deg,rgba(6,182,212,0.25),rgba(192,38,211,0.25));border:1.5px solid rgba(192,38,211,0.50);box-shadow:0 0 20px rgba(192,38,211,0.30);">
<span class="font-black text-[10px] grad-text">Dict</span>
</div>
{# 8 logos animés en cercle autour du hub (positions absolutes) #}
<template x-for="(l, li) in LOGOS" :key="l.name">
<div class="absolute dictia-spring rounded flex items-center justify-center"
:style="`width:28px;height:28px;left:${l.x}%;top:${l.y}%;transform:translate(-50%,-50%);background:${l.c}18;border:1px solid ${l.c}45;animation-delay:${li * 90 + 200}ms;`">
<span class="font-mono font-black text-[9px]" :style="`color:${l.c};`" x-text="l.short"></span>
</div>
</template>
{# Lignes de connexion SVG hub→logos #}
<svg class="absolute inset-0 pointer-events-none" width="100%" height="100%" aria-hidden="true" style="opacity:0.20;">
<line x1="50%" y1="50%" x2="20%" y2="20%" stroke="#c026d3" stroke-width="0.5" stroke-dasharray="2 2"/>
<line x1="50%" y1="50%" x2="80%" y2="20%" stroke="#06b6d4" stroke-width="0.5" stroke-dasharray="2 2"/>
<line x1="50%" y1="50%" x2="20%" y2="80%" stroke="#2563eb" stroke-width="0.5" stroke-dasharray="2 2"/>
<line x1="50%" y1="50%" x2="80%" y2="80%" stroke="#c026d3" stroke-width="0.5" stroke-dasharray="2 2"/>
</svg>
</div>
<div class="flex items-center justify-center gap-3 px-3 py-2 flex-shrink-0"
style="background:rgba(192,38,211,0.04);border-top:1px solid rgba(192,38,211,0.10);">
<span class="font-mono text-[9px] tracking-wide" style="color:rgba(192,38,211,0.75);">REST · Webhook · OAuth2</span>
</div>
</div>
</template>
{# Mode 11 : Audit trail (event log horodaté qui a fait quoi) — Catégorie Gouvernance #}
<template x-if="displayMode === 11">
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden" x-data="auditModeData()" x-init="init()">
<div class="flex items-center justify-between px-3 py-2 flex-shrink-0"
style="background:rgba(37,99,235,0.06);border-bottom:1px solid rgba(37,99,235,0.12);">
<span class="font-mono text-[10px] font-bold tracking-widest" style="color:rgba(37,99,235,0.90);">AUDIT TRAIL</span>
<span class="font-mono text-[9px]" style="color:rgba(37,99,235,0.55);">Loi 25 art. 8</span>
</div>
<div class="flex-1 dictia-mode-scroll dictia-fade-bottom flex flex-col gap-1 px-2 py-2">
<template x-for="(e, ei) in events.slice(0, shown)" :key="ei">
<div class="rounded flex items-start gap-1.5 dictia-fade-x"
:style="`background-color:${e.c}0C;border-left:2px solid ${e.c};padding:3px 6px;animation-delay:${ei * 100}ms;`">
<span class="font-mono text-[9px] font-bold mt-px" :style="`color:${e.c};`" x-text="e.t"></span>
<span class="rounded font-mono text-[8px] font-bold tracking-widest mt-px"
style="padding:0 4px;"
:style="`background:${e.c}22;color:${e.c};`" x-text="e.lvl"></span>
<p class="font-mono text-[9px] leading-snug flex-1" style="color:rgba(255,255,255,0.78);" x-text="e.msg"></p>
</div>
</template>
</div>
<div class="flex items-center justify-center gap-2 px-3 py-2 flex-shrink-0"
style="background:rgba(16,185,129,0.06);border-top:1px solid rgba(16,185,129,0.14);">
<span class="dictia-dot-pulse block rounded-full" style="width:5px;height:5px;background:#10b981;"></span>
<span class="font-mono text-[9px] font-bold tracking-widest" style="color:rgba(16,185,129,0.90);">CONSENTEMENT TRACÉ · IMMUTABLE</span>
</div>
</div>
</template>
{# Mode 12 : Conformité Loi 25 + 9 ordres pros (badges défilants + EFVP CAI) — Catégorie Gouvernance #}
<template x-if="displayMode === 12">
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden" x-data="loi25ModeData()" x-init="init()">
<div class="flex items-center justify-center gap-2 py-2 flex-shrink-0"
style="background:rgba(192,38,211,0.06);border-bottom:1px solid rgba(192,38,211,0.12);">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:11px;height:11px;color:rgba(192,38,211,0.90);" 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 text-[10px] font-bold tracking-widest" style="color:rgba(192,38,211,0.90);">CONFORMITÉ QUÉBEC</span>
</div>
<div class="flex-1 dictia-mode-scroll dictia-fade-bottom flex flex-col gap-1.5 px-3 py-2">
{# Badges Loi 25 / Loi 96 / EFVP CAI / Cadre IA MCN #}
<div class="flex flex-wrap gap-1.5">
<template x-for="(b, bi) in BADGES" :key="b.t">
<span class="rounded font-mono dictia-spring inline-flex items-center gap-1"
:style="`background:${b.c}18;border:1px solid ${b.c}45;color:${b.c};padding:2px 6px;animation-delay:${bi * 110}ms;font-size:9px;font-weight:bold;`">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" style="width:7px;height:7px;" aria-hidden="true">
<polyline points="20 6 9 17 4 12"/>
</svg>
<span x-text="b.t"></span>
</span>
</template>
</div>
{# 9 ordres pros mappés #}
<p class="font-mono text-[9px] font-bold uppercase tracking-wider mt-1" style="color:rgba(192,38,211,0.75);">9 ordres professionnels</p>
<div class="grid grid-cols-3 gap-1">
<template x-for="(o, oi) in ORDERS" :key="o">
<span class="rounded font-mono dictia-fade-y text-center text-[9px]"
style="padding:2px;background:rgba(192,38,211,0.10);border:1px solid rgba(192,38,211,0.25);color:rgba(255,255,255,0.85);"
:style="`animation-delay:${oi * 60 + 800}ms;`"
x-text="o"></span>
</template>
</div>
</div>
<div class="flex items-center justify-center gap-2 px-3 py-2 flex-shrink-0"
style="background:rgba(16,185,129,0.06);border-top:1px solid rgba(16,185,129,0.14);">
<span class="font-mono text-[9px] font-bold tracking-widest" style="color:rgba(16,185,129,0.90);">EFVP CAI · CADRE IA MCN</span>
</div>
</div>
</template>
</div>
{# BOTTOM : 4 catégories tabs (Capture / IA / Distribution / Gouvernance) + sub-mode dots + Auto pill — hauteur fixe 90px #}
<div class="relative flex flex-col items-center justify-center gap-1 z-10 flex-shrink-0"
style="height: 90px; border-top: 1px solid rgba(255,255,255,0.06); background: linear-gradient(180deg, transparent, rgba(0,0,0,0.32));">
{# Sub-mode dots indicator (shows position within active category) #}
<div class="flex items-center gap-1" style="height:6px;" aria-hidden="true">
<template x-for="(sm, di) in CATEGORIES[activeCategory].submodes" :key="di">
<span class="block rounded-full"
:style="`width:${sm === selectedFeature ? '10px' : '4px'};height:4px;background:${sm === selectedFeature ? CATEGORIES[activeCategory].color : 'rgba(255,255,255,0.20)'};transition:width 0.2s, background 0.2s;`"></span>
</template>
</div>
{# 4 catégories tabs #}
<div class="flex items-end gap-1">
<template x-for="(cat, ci) in CATEGORIES" :key="ci">
<button type="button" @click="handleCategorySelect(ci)"
class="dictia-feat-btn dictia-cat-btn outline-none cursor-pointer focus-visible:ring-2 focus-visible:ring-white/40 flex flex-col items-center justify-end relative"
style="width:54px;height:42px;border:none;padding:4px 0 6px;background:transparent;"
:aria-pressed="activeCategory === ci ? 'true' : 'false'"
:aria-label="`Catégorie : ${cat.title}`">
<span :style="`color: ${activeCategory === ci ? cat.color : 'rgba(255,255,255,0.30)'}; transition: color 0.2s, transform 0.15s, filter 0.2s; filter: ${activeCategory === ci ? 'drop-shadow(0 0 6px ' + cat.color + 'CC)' : 'none'}; transform: scale(${activeCategory === ci ? 1.15 : 1}); display: inline-block;`">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:18px;height:18px;" aria-hidden="true" x-html="cat.iconPath"></svg>
</span>
<span class="text-[9px] font-mono font-bold mt-0.5 leading-none uppercase tracking-wider"
:style="`color: ${activeCategory === ci ? cat.color : 'rgba(255,255,255,0.40)'};`"
x-text="cat.short"></span>
{# Tab indicator bottom border 2px #}
<span x-show="activeCategory === ci" class="absolute" aria-hidden="true"
:style="`bottom: 0; left: 6px; right: 6px; height: 2px; background: ${cat.color}; border-radius: 1px; box-shadow: 0 0 8px ${cat.color};`"></span>
</button>
</template>
</div>
{# Auto pill / Manuel countdown — style premium pill #}
<div class="flex items-center justify-center gap-1.5" style="min-height:14px;">
<template x-if="!isManual">
<span class="inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full"
style="background:rgba(16,185,129,0.12);border:1px solid rgba(16,185,129,0.30);">
<span class="dictia-auto-pulse block rounded-full" style="width:5px;height:5px;background:#10b981;"></span>
<span class="font-mono text-[9px] font-bold tracking-widest uppercase" style="color:#10b981;">Auto</span>
</span>
</template>
<template x-if="isManual">
<span class="inline-flex flex-col items-center gap-0.5">
<span class="font-mono text-[9px] text-white/60">Auto reprend bientôt</span>
<span class="rounded-full overflow-hidden" style="width:56px;height:2px;background:rgba(255,255,255,0.12);">
<span class="block dictia-countdown-fill h-full" :key="selectedFeature" style="background:linear-gradient(90deg,#06b6d4,#c026d3);"></span>
</span>
</span>
</template>
</div>
</div>
</div>{# /dictia-phone-shell #}
</div>{# /phone wrapper (glow ring) #}
{# Feature info card sous le phone — fond bg-brand-navy SOLIDE (extension visuelle du phone, contraste WCAG AA garanti sur section claire) + accent border-left tab indicator #}
<div style="min-height:64px;width:280px;max-width:100%;" class="mt-3 mx-auto">
<div class="dictia-feature-card rounded-xl px-4 py-3 relative bg-brand-navy border border-white/10 overflow-hidden"
:style="`box-shadow: 0 6px 24px rgba(0,0,0,0.20);`">
{# Border-left 3px accent — style tab indicator #}
<span class="absolute left-0 top-0 bottom-0" aria-hidden="true"
:style="`width:3px;background:${activeColor};box-shadow:0 0 8px ${activeColor}80;`"></span>
{# Badge top-right #}
<template x-if="FEATURES[displayMode].badge">
<span class="absolute text-[10px] px-1.5 py-0.5 rounded font-mono font-bold tracking-wider"
style="top:8px;right:8px;"
:style="`background-color: ${activeColor}22; color: ${activeColor}; border: 1px solid ${activeColor}55;`"
x-text="FEATURES[displayMode].badge"></span>
</template>
<div class="flex items-start gap-2.5">
{# Icon container 32×32 #}
<span class="rounded flex items-center justify-center flex-shrink-0"
style="width:32px;height:32px;"
:style="`background-color: ${activeColor}26; border: 1px solid ${activeColor}40;`"
aria-hidden="true">
<span :style="`color: ${activeColor};`">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px;" x-html="featureIconPath(displayMode)"></svg>
</span>
</span>
<div class="flex-1 min-w-0 pr-12">
<p class="text-sm font-bold text-white leading-tight mb-0.5" x-text="FEATURES[displayMode].title"></p>
<p class="text-[11px] leading-snug text-white/65" x-text="FEATURES[displayMode].subtitle"></p>
</div>
</div>
</div>
</div>
{# Mobile : pills catégories horizontales scrollables #}
<div class="lg:hidden w-full overflow-x-auto dictia-hide-scrollbar" style="scrollbar-width:none;">
<div class="flex gap-2 px-1 pb-1" style="width:max-content;">
<template x-for="(cat, ci) in CATEGORIES" :key="ci">
<button type="button" @click="handleCategorySelect(ci)"
class="flex items-center gap-1.5 px-2.5 py-1.5 rounded-full shrink-0 transition-all focus-visible:ring-2 focus-visible:ring-brand-b1"
:style="`border: 1px solid ${activeCategory === ci ? cat.color + '70' : 'rgba(0,0,0,0.10)'}; background-color: ${activeCategory === ci ? cat.color + '18' : 'rgba(0,0,0,0.04)'}; outline: none;`">
<span :style="`color: ${activeCategory === ci ? cat.color : 'rgba(0,0,0,0.40)'}; transition: color 0.2s;`">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:12px;height:12px;" aria-hidden="true" x-html="cat.iconPath"></svg>
</span>
<span class="text-[11px] font-bold whitespace-nowrap"
:style="`color: ${activeCategory === ci ? cat.color : 'rgba(0,0,0,0.50)'};`"
x-text="cat.title"></span>
</button>
</template>
</div>
</div>
</div>
{# ─────────── ZONE RIGHT : IA Mistral premium card + grid 6 features ─────────── #}
<div class="flex flex-col gap-3 w-full lg:w-[320px] flex-shrink-0 relative z-10">
{# Eyebrow + title — alignés sur le système typo #}
<div class="mb-1 flex items-center gap-2">
<span class="block w-1 h-6 rounded-full" style="background:linear-gradient(180deg,#06b6d4,#c026d3);" aria-hidden="true"></span>
<div>
<p class="text-xs font-mono font-bold uppercase tracking-widest text-brand-b3">4 catégories · 12 sous-modes</p>
<p class="font-bold text-lg leading-snug text-brand-navy">Le moteur IA local</p>
</div>
</div>
{# IA Mistral 7B premium card — couleurs brand-b3 (fuchsia officiel) #}
<div class="relative rounded-xl overflow-hidden border-brand-b3/30 border-[1.5px]"
style="background-color:rgba(8,12,24,0.92);box-shadow:0 0 28px rgba(192,38,211,0.18), 0 12px 40px -10px rgba(0,0,0,0.5);">
{# Ambient fuchsia glow #}
<div class="absolute inset-0 pointer-events-none ia-ambient-glow"
style="border-radius:inherit;background:radial-gradient(ellipse 120% 60% at 15% 50%, rgba(192,38,211,0.22) 0%, transparent 70%);"
aria-hidden="true"></div>
{# Header — Brain in 40x40 circle (premium feel) #}
<div class="relative p-5 flex items-start gap-3">
<div class="relative flex-shrink-0">
<div class="absolute -inset-3 rounded-full pointer-events-none ia-brain-glow"
style="background:radial-gradient(circle, rgba(192,38,211,0.55) 0%, transparent 70%);"
aria-hidden="true"></div>
<div class="rounded-full flex items-center justify-center relative"
style="width:40px;height:40px;background:linear-gradient(135deg, rgba(192,38,211,0.30), rgba(192,38,211,0.12));border:1.5px solid rgba(192,38,211,0.50);box-shadow:0 4px 12px rgba(192,38,211,0.20);">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-brand-b3 relative" style="width:20px;height:20px;z-index:1;" 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>
</div>
</div>
<div class="flex-1 min-w-0">
<span class="font-bold text-lg text-white block">IA intégrée</span>
<div class="flex items-center gap-1.5 flex-wrap mt-1.5">
<span class="text-[10px] px-2 py-0.5 rounded font-mono font-bold tracking-wider text-brand-b3"
style="background-color:rgba(192,38,211,0.22);border:1px solid rgba(192,38,211,0.45);">MISTRAL 7B</span>
<span class="text-[10px] px-2 py-0.5 rounded font-mono font-bold ia-local-badge inline-flex items-center gap-1 tracking-widest"
style="background-color:rgba(16,185,129,0.14);color:#10b981;border:1px solid rgba(16,185,129,0.40);">
<span class="dictia-dot-pulse block rounded-full" style="width:4px;height:4px;background:#10b981;"></span>
LOCAL
</span>
</div>
<p class="text-xs mt-2 text-white/65">Résumé · Points d'action · Q&amp;R</p>
</div>
</div>
{# Divider #}
<div style="height:1px;background:linear-gradient(90deg, transparent, rgba(192,38,211,0.30), transparent);" aria-hidden="true"></div>
{# Performance metrics 3 cells — chiffres en grad-text font-bold #}
<div class="relative grid grid-cols-3 gap-0">
<div class="text-center py-3 px-1" style="border-right:1px solid rgba(192,38,211,0.10);">
<p class="font-mono font-black text-lg leading-none grad-text">0&thinsp;ms</p>
<p class="text-[10px] mt-1 uppercase tracking-wider font-semibold" style="color:rgba(192,38,211,0.70);">latence</p>
</div>
<div class="text-center py-3 px-1" style="border-right:1px solid rgba(192,38,211,0.10);">
<p class="font-mono font-black text-lg leading-none" style="color:#10b981;">100&thinsp;%</p>
<p class="text-[10px] mt-1 uppercase tracking-wider font-semibold" style="color:rgba(192,38,211,0.70);">privé</p>
</div>
<div class="text-center py-3 px-1">
<p class="font-mono font-black text-lg leading-none grad-text">24/7</p>
<p class="text-[10px] mt-1 uppercase tracking-wider font-semibold" style="color:rgba(192,38,211,0.70);">dispo</p>
</div>
</div>
{# Divider #}
<div style="height:1px;background:linear-gradient(90deg, transparent, rgba(192,38,211,0.22), transparent);" aria-hidden="true"></div>
{# Sovereignty bullets — chacun avec icon dans cercle 20×20 brand-b3/[0.15] #}
<div class="relative p-5 flex flex-col gap-3">
<div class="flex items-start gap-2.5">
<span class="rounded-full flex items-center justify-center flex-shrink-0 mt-0.5 bg-brand-b3/[0.15] border border-brand-b3/30"
style="width:20px;height:20px;"
aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-brand-b3" style="width:11px;height:11px;">
<rect x="2" y="2" width="20" height="8" rx="2"/><rect x="2" y="14" width="20" height="8" rx="2"/>
<line x1="6" y1="6" x2="6.01" y2="6"/><line x1="6" y1="18" x2="6.01" y2="18"/>
</svg>
</span>
<span class="text-[11px] leading-snug text-white/90"><strong class="font-bold">Données hébergées sur VOS serveurs</strong> · jamais partagées</span>
</div>
<div class="flex items-start gap-2.5">
<span class="rounded-full flex items-center justify-center flex-shrink-0 mt-0.5 bg-brand-b3/[0.15] border border-brand-b3/30"
style="width:20px;height:20px;"
aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-brand-b3" style="width:11px;height:11px;">
<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>
<span class="text-[11px] leading-snug text-white/75">Zéro connexion OpenAI · Google · Microsoft</span>
</div>
<div class="flex items-start gap-2.5">
<span class="rounded-full flex items-center justify-center flex-shrink-0 mt-0.5 bg-brand-b3/[0.15] border border-brand-b3/30"
style="width:20px;height:20px;"
aria-hidden="true">
<svg viewBox="0 0 24 24" fill="currentColor" stroke="none" class="text-brand-b3" style="width:11px;height:11px;"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
</span>
<span class="text-[11px] leading-snug text-white/75">Inférence hors-ligne · résultats en secondes</span>
</div>
</div>
</div>
{# Category grid 2×2 — 4 catégories avec sous-modes affichés dessous (architecture lisible) #}
<div class="grid grid-cols-2 gap-2">
<template x-for="(cat, ci) in CATEGORIES" :key="ci">
<button type="button" @click="handleCategorySelect(ci)"
class="flex flex-col items-start gap-2 rounded-xl py-3 px-3 outline-none transition-all focus-visible:ring-2 focus-visible:ring-white/40 text-left"
:style="`border: 1px solid ${activeCategory === ci ? cat.color + '70' : 'rgba(255,255,255,0.10)'}; background-color: ${activeCategory === ci ? 'rgba(8,12,24,0.95)' : 'rgba(8,12,24,0.85)'}; box-shadow: ${activeCategory === ci ? '0 0 16px ' + cat.color + '40, inset 0 0 16px ' + cat.color + '20' : 'none'}; cursor: pointer;`"
:aria-pressed="activeCategory === ci ? 'true' : 'false'"
:aria-label="`Catégorie : ${cat.title}`">
<div class="flex items-center gap-2 w-full">
<span :style="`color: ${activeCategory === ci ? cat.color : 'rgba(255,255,255,0.55)'}; filter: ${activeCategory === ci ? 'drop-shadow(0 0 6px ' + cat.color + 'BB)' : 'none'}; transition: all 0.2s; display:inline-block;`">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:18px;height:18px;" aria-hidden="true" x-html="cat.iconPath"></svg>
</span>
<span class="text-[11px] font-bold leading-tight flex-1"
:style="`color: ${activeCategory === ci ? 'rgba(255,255,255,0.98)' : 'rgba(255,255,255,0.85)'};`"
x-text="cat.title"></span>
</div>
<p class="text-[10px] font-mono leading-snug w-full"
:style="`color: ${activeCategory === ci ? cat.color + 'EE' : 'rgba(255,255,255,0.55)'};`"
x-text="cat.subtitle"></p>
{# Mini sous-modes dots dans la card #}
<div class="flex items-center gap-0.5 mt-auto" aria-hidden="true">
<template x-for="(sm, si) in cat.submodes" :key="si">
<span class="block rounded-full"
:style="`width:${activeCategory === ci && sm === selectedFeature ? '8px' : '3px'};height:3px;background:${activeCategory === ci ? (sm === selectedFeature ? cat.color : cat.color + '55') : 'rgba(255,255,255,0.15)'};transition:all 0.2s;`"></span>
</template>
</div>
</button>
</template>
</div>
</div>
</div>
</div>
{# Alpine logic — dictiaDashboard + 4 catégories × 12 sous-modes (refonte stratégique 2026-04-29) #}
<script>
function dictiaDashboard() {
return {
/* FEATURES indexes (0-12) — même structure que v1 (0=IA, 1-6=existants), nouveaux 7-12 ajoutés */
FEATURES: [
{ idx: 0, title: 'IA intégrée', subtitle: "Résumé, actions, Q&R", color: '#c026d3', badge: 'Mistral 7B' }, /* brand-b3 */
{ idx: 1, title: 'Transcription', subtitle: 'Parole → texte en temps réel', color: '#06b6d4', badge: 'Whisper AI' }, /* brand-b2 */
{ idx: 2, title: 'Diarisation', subtitle: 'Identification des locuteurs', color: '#2563eb', badge: null }, /* brand-b1 */
{ idx: 3, title: '99+ langues', subtitle: 'Détection automatique', color: '#06b6d4', badge: null }, /* brand-b2 */
{ idx: 4, title: 'Exports', subtitle: 'DOCX, SRT, JSON, PDF', color: '#2563eb', badge: null }, /* brand-b1 */
{ idx: 5, title: 'Utilisateurs illimités', subtitle: 'Toute votre équipe', color: '#c026d3', badge: 'Illimité' }, /* brand-b3 */
{ idx: 6, title: 'Partage & Classement', subtitle: 'Dossiers, tags, recherche', color: '#06b6d4', badge: null }, /* brand-b2 */
/* === Nouveaux modes 2026-04-29 === */
{ idx: 7, title: 'Enregistrement live', subtitle: 'Mic + système + minuteur', color: '#06b6d4', badge: 'PWA' }, /* brand-b2 */
{ idx: 8, title: 'Recherche IA', subtitle: 'Sémantique sur toute la bibliothèque', color: '#06b6d4', badge: 'RAG' }, /* brand-b2 */
{ idx: 9, title: 'Résumé + actions', subtitle: 'Décisions, ICS, événements', color: '#2563eb', badge: 'Nemo 12B' }, /* brand-b1 */
{ idx: 10, title: 'Intégrations', subtitle: 'Word, Notion, Teams, Zapier...', color: '#c026d3', badge: '8 hubs' }, /* brand-b3 */
{ idx: 11, title: 'Audit trail', subtitle: 'Traçabilité Loi 25 immutable', color: '#2563eb', badge: 'Loi 25' }, /* brand-b1 */
{ idx: 12, title: 'Conformité', subtitle: '9 ordres pros · EFVP CAI', color: '#c026d3', badge: 'MCN' } /* brand-b3 */
],
/* CATEGORIES : 4 regroupements logiques. Chaque catégorie a ses sous-modes (ids des FEATURES). */
CATEGORIES: [
{
title: 'Capture',
short: 'Capture',
subtitle: 'Audio in : upload, recording, recherche',
color: '#06b6d4', /* brand-b2 cyan */
submodes: [1, 7, 8], /* Transcription (upload), Recording live, Recherche sémantique */
iconPath: '<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="23"/>'
},
{
title: 'Transformation IA',
short: 'IA',
subtitle: 'Diarisation, langues, résumés, Q&R',
color: '#2563eb', /* brand-b1 blue */
submodes: [2, 3, 9, 0], /* Diarisation, Langues, Résumé+actions, Chat IA */
iconPath: '<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"/>'
},
{
title: 'Distribution',
short: 'Distrib',
subtitle: 'Exports, intégrations, partage',
color: '#c026d3', /* brand-b3 fuchsia */
submodes: [4, 10, 6, 5], /* Exports, Intégrations, Partage, Users */
iconPath: '<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"/>'
},
{
title: 'Gouvernance',
short: 'Gouv',
subtitle: 'Audit, Loi 25, AGPL, ordres pros',
color: '#2563eb', /* brand-b1 blue (sobre, institutionnel) */
submodes: [11, 12], /* Audit trail, Conformité Loi 25 */
iconPath: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="M9 12l2 2 4-4"/>'
}
],
selectedFeature: 1,
activeCategory: 0,
isManual: false,
autoCycleTimer: null,
manualResetTimer: null,
get displayMode() { return this.selectedFeature; },
get activeColor() { return this.FEATURES[this.displayMode].color; },
init() {
if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
this.startAutoCycle();
},
/* Auto-cycle logic : avance dans les sous-modes de la catégorie active toutes les 1100ms.
Quand on atteint le dernier sous-mode → switch catégorie suivante (boucle 0→1→2→3→0). */
startAutoCycle() {
if (this.autoCycleTimer) clearInterval(this.autoCycleTimer);
this.autoCycleTimer = setInterval(() => {
const cat = this.CATEGORIES[this.activeCategory];
const currentIdx = cat.submodes.indexOf(this.selectedFeature);
if (currentIdx < cat.submodes.length - 1) {
/* Avance dans la catégorie courante */
this.selectedFeature = cat.submodes[currentIdx + 1];
} else {
/* Fin de catégorie → passe à la suivante */
this.activeCategory = (this.activeCategory + 1) % this.CATEGORIES.length;
this.selectedFeature = this.CATEGORIES[this.activeCategory].submodes[0];
}
}, 1100);
},
/* Click manuel sur un sous-mode (compatibilité 6 boutons originaux) */
handleManualSelect(i) {
this.selectedFeature = i;
/* Trouve la catégorie qui contient ce sous-mode */
for (let ci = 0; ci < this.CATEGORIES.length; ci++) {
if (this.CATEGORIES[ci].submodes.includes(i)) { this.activeCategory = ci; break; }
}
this.isManual = true;
if (this.autoCycleTimer) { clearInterval(this.autoCycleTimer); this.autoCycleTimer = null; }
if (this.manualResetTimer) clearTimeout(this.manualResetTimer);
this.manualResetTimer = setTimeout(() => {
this.isManual = false;
this.startAutoCycle();
}, 4500);
},
/* Click manuel sur une catégorie : sélectionne son premier sous-mode */
handleCategorySelect(ci) {
this.activeCategory = ci;
this.selectedFeature = this.CATEGORIES[ci].submodes[0];
this.isManual = true;
if (this.autoCycleTimer) { clearInterval(this.autoCycleTimer); this.autoCycleTimer = null; }
if (this.manualResetTimer) clearTimeout(this.manualResetTimer);
this.manualResetTimer = setTimeout(() => {
this.isManual = false;
this.startAutoCycle();
}, 4500);
},
featureIconPath(i) {
const paths = {
0: '<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"/>',
1: '<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="23"/><line x1="8" y1="23" x2="16" y2="23"/>',
2: '<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"/>',
3: '<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"/>',
4: '<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="12" y1="18" x2="12" y2="12"/><polyline points="9 15 12 18 15 15"/>',
5: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><line x1="20" y1="8" x2="20" y2="14"/><line x1="23" y1="11" x2="17" y2="11"/>',
6: '<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"/>',
7: '<circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/>',
8: '<circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>',
9: '<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"/>',
10: '<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/>',
11: '<path d="M12 2v6"/><path d="M12 22v-6"/><path d="M4.93 4.93l4.24 4.24"/><path d="M14.83 14.83l4.24 4.24"/><circle cx="12" cy="12" r="4"/>',
12: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="M9 12l2 2 4-4"/>'
};
return paths[i] || '';
},
featureShortLabel(i) {
const labels = { 1: 'Trans', 2: 'Diari', 3: 'Lang', 4: 'Exp', 5: 'Users', 6: 'Part',
7: 'Rec', 8: 'Search', 9: 'Résu', 10: 'Hub', 11: 'Audit', 12: 'Conf' };
return labels[i] || '';
}
};
}
/* Mode 1 — Transcription : upload bar (60ms × 20 steps) puis words (155ms each) */
function trModeData() {
return {
phase: 'upload', progress: 0, n: 0,
words: "La réunion du lundi 12 janvier débutera à 9h. Ordre du jour : budget Q4 et les objectifs du trimestre. Points d'action à définir en fin de séance.".split(' '),
_timers: [],
init() {
this._cleanup();
this.phase = 'upload'; this.progress = 0; this.n = 0;
const iv1 = setInterval(() => {
this.progress = Math.min(this.progress + 5, 100);
if (this.progress >= 100) {
clearInterval(iv1);
const t1 = setTimeout(() => {
this.phase = 'transcribing'; this.n = 0;
const iv2 = setInterval(() => {
if (this.n >= this.words.length - 1) {
clearInterval(iv2);
const t2 = setTimeout(() => this.init(), 900);
this._timers.push(t2);
} else this.n++;
}, 155);
this._timers.push(iv2);
}, 450);
this._timers.push(t1);
}
}, 60);
this._timers.push(iv1);
},
_cleanup() { this._timers.forEach(t => { clearInterval(t); clearTimeout(t); }); this._timers = []; }
};
}
/* Mode 2 — Diarisation : 6 messages staggered 1600ms */
function diaModeData() {
return {
CONVO: [
{ s: "Sophie", c: "#06b6d4", t: "La réunion commence à 9h." }, /* brand-b2 cyan */
{ s: "Marc", c: "#2563eb", t: "J'ai les chiffres du Q4 ici." }, /* brand-b1 blue */
{ s: "Julie", c: "#c026d3", t: "Je propose reporter la démo." }, /* brand-b3 fuchsia */
{ s: "Sophie", c: "#06b6d4", t: "Accord — on vote là-dessus ?" },
{ s: "Marc", c: "#2563eb", t: "Approuvé à l'unanimité." },
{ s: "Julie", c: "#c026d3", t: "Action : CR envoyé ce soir." }
],
shownCount: 1, cycle: 0, _iv: null,
get startIdx() { return Math.max(0, this.shownCount - 4); },
get visible() { return this.CONVO.slice(this.startIdx, this.shownCount); },
init() {
if (this._iv) clearInterval(this._iv);
this.shownCount = 1;
this._iv = setInterval(() => {
if (this.shownCount >= this.CONVO.length) {
clearInterval(this._iv);
setTimeout(() => { this.cycle++; this.init(); }, 800);
} else this.shownCount++;
}, 1600);
}
};
}
/* Mode 3 — Langues : grille 100 codes (ripple depuis centre + highlight aléatoire) */
function langModeData() {
return {
/* Cycle EXCLUSIVEMENT entre brand-b1 / brand-b2 / brand-b3 (palette canonique stricte) */
LANG_COLORS: ['#2563eb','#06b6d4','#c026d3','#06b6d4','#2563eb','#c026d3','#2563eb','#06b6d4'],
LANGS: [
'FR','EN','ES','DE','PT','IT','NL','PL','ZH','JA',
'KO','AR','RU','HI','TR','VI','TH','SV','DA','NO',
'FI','CS','RO','HU','EL','HE','UK','BG','FA','ID',
'MS','NB','BN','UR','SW','TL','SK','HR','SR','SL',
'LT','LV','ET','IS','MT','GL','CA','EU','CY','GA',
'AF','MK','BE','LB','MR','TA','TE','KN','ML','PA',
'GU','SI','MY','KM','LO','KA','AZ','KK','UZ','TK',
'MN','NE','PS','AM','SQ','HY','MG','YO','SO','ZU',
'NY','XH','IG','HA','SN','ST','CEB','HT','JW','KY',
'TG','BS','OC','BR','FO','LG','YI','TT','SD','WA'
],
/* HIGHLIGHTS : 5 indexes aléatoires qui pulsent (regenerated chaque cycle) */
HIGHLIGHTS: (() => {
const set = new Set();
while (set.size < 5) set.add(Math.floor(Math.random() * 100));
return Array.from(set);
})(),
/* rippleDelay : delay calculé selon distance au centre (50, ~ idx 49) */
rippleDelay(i) {
const cols = 7, row = Math.floor(i / cols), col = i % cols;
const centerRow = 7, centerCol = 3;
const dist = Math.sqrt(Math.pow(row - centerRow, 2) + Math.pow(col - centerCol, 2));
return Math.round(dist * 28);
}
};
}
/* Mode 4 — Exports : 7 file types staggered 90ms (CSS-only) — palette brand stricte */
function expModeData() {
return {
FILE_TYPES: [
{ ext: 'DOCX', bg: '#2563eb', fg: '#ffffff', sym: 'W' }, /* brand-b1 blue */
{ ext: 'PDF', bg: '#dc2626', fg: '#ffffff', sym: 'PDF' }, /* red sémantique conservé pour fichier PDF */
{ ext: 'SRT', bg: '#c026d3', fg: '#ffffff', sym: 'CC' }, /* brand-b3 fuchsia */
{ ext: 'VTT', bg: '#c026d3', fg: '#ffffff', sym: 'CC' }, /* brand-b3 fuchsia */
{ ext: 'TXT', bg: '#374151', fg: '#d1d5db', sym: '≡' }, /* gray-700 neutre (sémantique TXT raw) */
{ ext: 'JSON', bg: '#06b6d4', fg: '#ffffff', sym: '{}' }, /* brand-b2 aqua */
{ ext: 'MD', bg: '#2563eb', fg: '#ffffff', sym: '#' } /* brand-b1 blue */
]
};
}
/* Mode 5 — Users : multiplication 1→20 (200ms each, loop after 700ms) avec couleurs variées */
function usersModeData() {
return {
MAX: 20, count: 1, cycle: 0, _iv: null,
/* EXCLUSIVEMENT brand-b1 / brand-b2 / brand-b3 (palette canonique stricte) */
USER_COLORS: ['#c026d3', '#06b6d4', '#2563eb'],
init() {
if (this._iv) clearInterval(this._iv);
this.count = 1;
this._iv = setInterval(() => {
if (this.count >= this.MAX) {
clearInterval(this._iv);
setTimeout(() => { this.cycle++; this.init(); }, 700);
} else this.count++;
}, 200);
}
};
}
/* Mode 0 — IA chat : Q&R typed char-by-char (28ms/char) */
function iaModeData() {
return {
msgs: [], cycle: 0, _timers: [],
CHAT: [
{ role: 'user', text: "Quels sont les points d'action ?" },
{ role: 'bot', text: "→ Envoyer CR avant 17h\n→ Contacter client vendredi\n→ Réviser budget avec Luc" },
{ role: 'user', text: "Qui a ouvert la réunion ?" },
{ role: 'bot', text: "Sophie a ouvert la séance à 9h00." }
],
init() {
this._cleanup();
this.msgs = [];
const t0 = setTimeout(() => { this.msgs = [{ role: 'user', text: this.CHAT[0].text }]; }, 300);
this._timers.push(t0);
const t1 = setTimeout(() => {
const bot1 = this.CHAT[1].text; let i = 0;
const iv = setInterval(() => {
i++;
this.msgs = [
{ role: 'user', text: this.CHAT[0].text },
{ role: 'bot', text: bot1.slice(0, i) }
];
if (i >= bot1.length) {
clearInterval(iv);
const t2 = setTimeout(() => {
this.msgs = [
{ role: 'user', text: this.CHAT[0].text },
{ role: 'bot', text: this.CHAT[1].text },
{ role: 'user', text: this.CHAT[2].text }
];
const t3 = setTimeout(() => {
const bot2 = this.CHAT[3].text; let j = 0;
const iv2 = setInterval(() => {
j++;
this.msgs = [
{ role: 'user', text: this.CHAT[0].text },
{ role: 'bot', text: this.CHAT[1].text },
{ role: 'user', text: this.CHAT[2].text },
{ role: 'bot', text: bot2.slice(0, j) }
];
if (j >= bot2.length) {
clearInterval(iv2);
const t4 = setTimeout(() => { this.cycle++; this.init(); }, 1200);
this._timers.push(t4);
}
}, 40);
this._timers.push(iv2);
}, 500);
this._timers.push(t3);
}, 700);
this._timers.push(t2);
}
}, 28);
this._timers.push(iv);
}, 900);
this._timers.push(t1);
},
_cleanup() { this._timers.forEach(t => { clearInterval(t); clearTimeout(t); }); this._timers = []; }
};
}
/* === MODES AJOUTÉS 2026-04-29 === */
/* Mode 7 — Recording live : minuteur incrémentiel + waveform random pattern */
function recModeData() {
return {
sec: 0, BARS: [], _iv: null, _ivBars: null,
get time() {
const m = String(Math.floor(this.sec / 60)).padStart(2, '0');
const s = String(this.sec % 60).padStart(2, '0');
return `${m}:${s}`;
},
get size() { return Math.round(this.sec * 0.16 * 10) / 10; },
init() {
if (this._iv) clearInterval(this._iv);
if (this._ivBars) clearInterval(this._ivBars);
this.sec = 0;
/* 24 bars hauteur initiale random 6-26px */
this.BARS = Array.from({ length: 24 }, () => Math.floor(Math.random() * 20) + 6);
this._iv = setInterval(() => {
this.sec++;
if (this.sec >= 99) { clearInterval(this._iv); setTimeout(() => this.init(), 800); }
}, 1000);
/* Update bars heights chaque 250ms pour effet "live" */
this._ivBars = setInterval(() => {
this.BARS = this.BARS.map(() => Math.floor(Math.random() * 20) + 6);
}, 250);
}
};
}
/* Mode 8 — Recherche sémantique : query typed + résultats matched */
function searchModeData() {
return {
QUERY: 'préoccupations budgétaires',
query: '', results: [], _timers: [],
RESULTS_DATA: [
{ title: 'CR-Réunion-Jan14', match: 'contraintes budgétaires Q4', score: 94, c: '#06b6d4' },
{ title: 'Comité-Direction', match: 'compressions à anticiper', score: 87, c: '#2563eb' },
{ title: 'Entretien-Sophie', match: 'inquiétudes financières', score: 82, c: '#c026d3' }
],
init() {
this._cleanup();
this.query = ''; this.results = [];
let i = 0;
const ivType = setInterval(() => {
i++; this.query = this.QUERY.slice(0, i);
if (i >= this.QUERY.length) {
clearInterval(ivType);
const t1 = setTimeout(() => { this.results = this.RESULTS_DATA; }, 500);
this._timers.push(t1);
const t2 = setTimeout(() => this.init(), 5500);
this._timers.push(t2);
}
}, 60);
this._timers.push(ivType);
},
_cleanup() { this._timers.forEach(t => { clearInterval(t); clearTimeout(t); }); this._timers = []; }
};
}
/* Mode 9 — Résumé + extraction d'événements : décisions/actions stagger */
function summaryModeData() {
return {
decisions: [
'Approuvé budget Q4 à l\'unanimité',
'Reporter démo à février',
'Validation finale 25 janv.'
],
actions: [
'Marc : envoyer CR avant 17h',
'Sophie : contacter client vendredi',
'Julie : réviser budget avec Luc'
],
dShown: 0, aShown: 0, _timers: [],
init() {
this._cleanup();
this.dShown = 0; this.aShown = 0;
let di = 0;
const ivD = setInterval(() => {
di++; this.dShown = di;
if (di >= this.decisions.length) {
clearInterval(ivD);
let ai = 0;
const t1 = setTimeout(() => {
const ivA = setInterval(() => {
ai++; this.aShown = ai;
if (ai >= this.actions.length) {
clearInterval(ivA);
const t2 = setTimeout(() => this.init(), 3500);
this._timers.push(t2);
}
}, 600);
this._timers.push(ivA);
}, 700);
this._timers.push(t1);
}
}, 600);
this._timers.push(ivD);
},
_cleanup() { this._timers.forEach(t => { clearInterval(t); clearTimeout(t); }); this._timers = []; }
};
}
/* Mode 10 — Intégrations Hub : 8 logos en orbite */
function integModeData() {
return {
LOGOS: [
{ name: 'Word', short: 'W', c: '#2563eb', x: 20, y: 30 },
{ name: 'Outlook', short: 'O', c: '#06b6d4', x: 80, y: 30 },
{ name: 'Teams', short: 'T', c: '#c026d3', x: 15, y: 55 },
{ name: 'Notion', short: 'N', c: '#2563eb', x: 85, y: 55 },
{ name: 'Obsidian', short: 'Ob', c: '#c026d3', x: 25, y: 80 },
{ name: 'Zapier', short: 'Z', c: '#06b6d4', x: 75, y: 80 },
{ name: 'Make', short: 'Mk', c: '#2563eb', x: 50, y: 18 },
{ name: 'n8n', short: 'n8', c: '#06b6d4', x: 50, y: 88 }
],
init() {}
};
}
/* Mode 11 — Audit trail : event log staggered */
function auditModeData() {
return {
events: [
{ t: '09:01', lvl: 'INFO', c: '#06b6d4', msg: 'Sophie a uploadé reunion-jan14.mp3' },
{ t: '09:02', lvl: 'AUTH', c: '#2563eb', msg: 'Consentement explicite enregistré' },
{ t: '09:04', lvl: 'PROC', c: '#c026d3', msg: 'WhisperX terminé · diarisation 3 loc.' },
{ t: '09:08', lvl: 'READ', c: '#06b6d4', msg: 'Marc a écouté le segment 02:14-04:30' },
{ t: '09:12', lvl: 'EXP', c: '#2563eb', msg: 'Export DOCX par Julie · IP 198.x.x.42' },
{ t: '09:15', lvl: 'SHARE', c: '#c026d3', msg: 'Partage interne accordé : Luc (édition)' }
],
shown: 0, _iv: null,
init() {
if (this._iv) clearInterval(this._iv);
this.shown = 0;
this._iv = setInterval(() => {
if (this.shown >= this.events.length) {
clearInterval(this._iv);
setTimeout(() => this.init(), 2400);
} else this.shown++;
}, 700);
}
};
}
/* Mode 12 — Conformité Loi 25 + 9 ordres pros */
function loi25ModeData() {
return {
BADGES: [
{ t: 'Loi 25', c: '#06b6d4' },
{ t: 'Loi 96', c: '#2563eb' },
{ t: 'EFVP CAI', c: '#c026d3' },
{ t: 'Cadre IA MCN', c: '#06b6d4' },
{ t: 'AGPL v3', c: '#2563eb' },
{ t: '0 Cloud Act US', c: '#c026d3' }
],
ORDERS: ['Barreau', 'CNQ', 'CPA', 'ChAD', 'OACIQ', 'CMQ', 'OIIQ', 'OPQ', 'OEQ'],
init() {}
};
}
</script>
</section>
{# ===== INTÉGRATIONS ===== #}
<section id="integrations" class="bg-brand-bg py-20 scroll-mt-32" aria-labelledby="integrations-title">
<div class="max-w-[1200px] mx-auto px-6">
<div class="text-center max-w-3xl mx-auto mb-14">
<p class="eyebrow grad-text mb-4">INTÉGRATIONS</p>
<h2 id="integrations-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black text-brand-navy">
<span class="ani-underline" data-ani-underline>Branchez DictIA à votre stack existant.</span>
</h2>
<p class="text-lg text-brand-navy/70 mt-6">
Webhooks REST, plugin Word natif et connecteurs Zapier / Make / n8n auto-hébergés. API documentée — vous gardez le contrôle des flux.
</p>
</div>
{%- set icon_chat_box = '<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" 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_book = '<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" aria-hidden="true"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20V3H6.5A2.5 2.5 0 0 0 4 5.5z"/><path d="M4 19.5V21h16v-4"/></svg>' -%}
{%- set icon_gear = '<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" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.9-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1-1.5 1.7 1.7 0 0 0-1.9.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.9 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1 1.7 1.7 0 0 0-.3-1.9l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.9.3h.1a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.9-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.9v.1a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z"/></svg>' -%}
{%- set integ_groups = [
{
'title': 'Communication',
'desc': 'Importez vos enregistrements depuis vos outils de réunion et de pratique.',
'icon': icon_chat_box,
'tools': [
{'name': 'Microsoft Teams', 'detail': 'Export depuis enregistrements'},
{'name': 'Microsoft Outlook','detail': 'Pièces jointes audio'},
{'name': 'Clio Manage', 'detail': 'Pour avocats'},
{'name': 'PCLaw', 'detail': 'Pour avocats'}
]
},
{
'title': 'Knowledge / notes',
'desc': 'Synchronisez transcriptions et résumés vers vos bases de connaissances.',
'icon': icon_book,
'tools': [
{'name': 'Obsidian', 'detail': 'via Markdown'},
{'name': 'Notion', 'detail': 'via Markdown'},
{'name': 'Logseq', 'detail': 'via Markdown'},
{'name': 'Word', 'detail': 'via DOCX'}
]
},
{
'title': 'Automatisation',
'desc': 'Déclenchez vos workflows à chaque transcription terminée.',
'icon': icon_gear,
'tools': [
{'name': 'Zapier', 'detail': 'No-code'},
{'name': 'Make (Integromat)','detail': 'Scénarios visuels'},
{'name': 'n8n', 'detail': 'Open source self-host'}
]
}
] -%}
<div class="grid md:grid-cols-3 gap-6">
{% for group in integ_groups %}
<div class="ani-fade bg-white p-6 rounded border border-brand-border" style="--delay: {{ loop.index0 * 80 }}ms;" data-ani-fade>
<div class="flex items-center gap-2 mb-2 text-brand-b1" aria-hidden="true">
{{ group.icon | safe }}
<span class="text-xs uppercase tracking-wider font-bold text-brand-navy">{{ group.title }}</span>
</div>
<p class="text-sm text-brand-navy/70 mb-5">{{ group.desc }}</p>
<ul class="grid grid-cols-2 gap-2" role="list">
{% for t in group.tools %}
<li class="ani-lift bg-brand-bg border border-brand-border p-3 rounded">
<p class="text-sm font-semibold text-brand-navy font-mono leading-tight">{{ t.name }}</p>
<p class="text-[11px] text-brand-navy/60 mt-0.5">{{ t.detail }}</p>
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
</div>
<p class="text-xs text-brand-navy/70 text-center mt-8 max-w-2xl mx-auto">
Microsoft, Notion, Obsidian, Logseq, Clio, PCLaw, Zapier, Make et n8n sont des marques de leurs propriétaires respectifs. DictIA n'est pas affilié à ces produits.
</p>
</div>
</section>
{# ===== ARCHITECTURE & INFRASTRUCTURE ===== #}
<section id="architecture" class="relative bg-brand-navy text-white py-20 scroll-mt-32 overflow-hidden" aria-labelledby="architecture-title">
<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-3xl mx-auto mb-14">
<p class="eyebrow grad-text mb-4">ARCHITECTURE &amp; INFRASTRUCTURE</p>
<h2 id="architecture-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black">
<span class="ani-underline" data-ani-underline>Quatre formules selon votre organisation.</span>
</h2>
<p class="text-lg text-white/70 mt-6">
Cloud BASIC, ESSENTIEL et PRO tournent sur GPU NVIDIA L4 à OVH Beauharnois (Québec). DictIA LOCAL fonctionne 100&nbsp;% hors-ligne chez vous. Toutes les formules incluent aucune limite utilisateurs.
</p>
</div>
{%- set tiers = [
{
'name': 'Cloud BASIC',
'tagline': 'Solopreneur · petite équipe',
'gpu': 'NVIDIA L4 partagé',
'users': 'Aucune limite',
'setup': '—',
'monthly': '189&nbsp;$',
'host': 'OVH Beauharnois (QC)',
'llm': 'Mistral Nemo 12B',
'recommended': False,
'features': ['Transcription WhisperX Large-v3', 'Diarisation pyannote', '~165&nbsp;h audio/mois · 100&nbsp;Go', 'Exports DOCX, PDF, SRT, VTT, TXT, JSON, MD', 'Self-service · 0&nbsp;$ d&rsquo;installation']
},
{
'name': 'Cloud ESSENTIEL',
'tagline': 'Cabinet en croissance',
'gpu': 'L4 partagé étendu',
'users': 'Aucune limite',
'setup': '—',
'monthly': '349&nbsp;$',
'host': 'OVH Beauharnois (QC)',
'llm': 'Mistral Nemo 12B',
'recommended': False,
'features': ['Tout Cloud BASIC', '~330&nbsp;h audio/mois · 200&nbsp;Go', 'Onboarding assisté', 'Templates métier (avocat · notaire · CPA · médecin)']
},
{
'name': 'Cloud PRO',
'tagline': 'Usage intensif multi-postes',
'gpu': 'NVIDIA L4 dédié priorité',
'users': 'Aucune limite',
'setup': '485&nbsp;$',
'monthly': '549&nbsp;$',
'host': 'OVH Beauharnois (QC)',
'llm': 'Mistral Nemo 12B',
'recommended': True,
'features': ['Tout Cloud ESSENTIEL', '~660&nbsp;h audio/mois · 500&nbsp;Go', 'GPU dédié priorité (latence garantie)', 'Onboarding assisté inclus (485&nbsp;$ unique)', 'Multi-sites et télétravail']
},
{
'name': 'DictIA LOCAL',
'tagline': '100&nbsp;% hors-ligne · chez vous',
'gpu': 'RTX 5070&nbsp;Ti 16&nbsp;Go',
'users': 'Aucune limite',
'setup': '5&nbsp;998&nbsp;$',
'monthly': '500&nbsp;$/an dès An&nbsp;2',
'host': 'Chez le client',
'llm': 'Mistral 7B local',
'recommended': False,
'features': ['Tout Cloud PRO en mode local', '~1&nbsp;100&nbsp;h audio/mois · 2&nbsp;To SSD', 'GPU local dédié', 'Données jamais sortantes', 'Admissible achat direct gouv. (≤ 34&nbsp;700&nbsp;$)']
}
] -%}
<div class="grid md:grid-cols-2 lg:grid-cols-4 gap-6 items-stretch">
{% for tier in tiers %}
<article class="ani-fade {% if tier.recommended %}card-pulse-glow grad-bg p-[1.5px] rounded{% endif %} relative h-full"
style="--delay: {{ loop.index0 * 100 }}ms;"
data-ani-fade>
{% if tier.recommended %}
<span class="absolute -top-3 left-1/2 -translate-x-1/2 grad-bg text-white text-xs font-bold px-3 py-1 rounded-full inline-flex items-center gap-1.5 z-10">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-3 h-3" aria-hidden="true"><path d="M12 2l2.9 6.9L22 10l-5.5 4.8L18 22l-6-3.6L6 22l1.5-7.2L2 10l7.1-1.1z"/></svg>
RECOMMANDÉ
</span>
{% endif %}
<div class="bg-brand-navy2 p-6 rounded border border-white/[0.08] h-full flex flex-col">
<header class="mb-4 pb-4 border-b border-white/[0.08]">
<h3 class="text-xl font-black mb-1 text-white">{{ tier.name | safe }}</h3>
<p class="text-xs uppercase tracking-wider text-white/60">{{ tier.tagline | safe }}</p>
</header>
<dl class="grid grid-cols-2 gap-x-4 gap-y-2 mb-5 text-xs">
<dt class="text-white/60">GPU</dt><dd class="font-mono text-white">{{ tier.gpu | safe }}</dd>
<dt class="text-white/60">Utilisateurs</dt><dd class="text-white">{{ tier.users | safe }}</dd>
<dt class="text-white/60">Hébergement</dt><dd class="text-white">{{ tier.host | safe }}</dd>
<dt class="text-white/60">LLM résumés</dt><dd class="text-white">{{ tier.llm | safe }}</dd>
</dl>
<div class="mb-5 pb-5 border-b border-white/[0.08]">
{% if tier.setup != '—' %}
<div class="text-3xl font-black grad-text leading-none">{{ tier.setup | safe }}</div>
<div class="text-xs text-white/60 mt-1">{% if tier.name == 'DictIA LOCAL' %}An&nbsp;1 · puis {{ tier.monthly | safe }}{% else %}setup unique + {{ tier.monthly | safe }}&nbsp;/ mois{% endif %}</div>
{% else %}
<div class="text-3xl font-black grad-text leading-none">{{ tier.monthly | safe }}</div>
<div class="text-xs text-white/60 mt-1">par mois · sans frais d&rsquo;installation</div>
{% endif %}
</div>
<ul class="space-y-2 mb-6 flex-grow text-sm" role="list">
{% for feat in tier.features %}
<li class="flex items-start gap-2 text-white/80">
{{ icon_check | safe }}
<span>{{ feat | safe }}</span>
</li>
{% endfor %}
</ul>
{% from 'macros/button.html' import button %}
{{ button('Voir le détail tarifs', href='/tarifs', variant='primary' if tier.recommended else 'ghost', size='md') }}
</div>
</article>
{% endfor %}
</div>
{# Bloc inclus dans le forfait DictIA LOCAL #}
<div class="mt-12 max-w-4xl mx-auto bg-white/[0.05] backdrop-blur-sm p-6 rounded border border-white/[0.08]">
<p class="eyebrow grad-text mb-3">INCLUS DANS LE FORFAIT DICTIA LOCAL (5&nbsp;998&nbsp;$ AN&nbsp;1)</p>
<ul class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-3 text-sm" role="list">
{% for inc in [
'PC + GPU RTX 5070&nbsp;Ti + 2&nbsp;To SSD',
'Configuration complète',
'Installation sur site',
'Formation équipe (23&nbsp;h)',
'1<sup>re</sup> année logiciel + support'
] %}
<li class="flex items-start gap-2 text-white/80">
<span class="text-brand-b3 mt-0.5" aria-hidden="true">{{ icon_check | safe }}</span>
<span>{{ inc | safe }}</span>
</li>
{% endfor %}
</ul>
</div>
<p class="text-xs text-white/60 text-center mt-6 max-w-2xl mx-auto">
DictIA LOCAL est admissible à l'achat direct gouvernemental sans appel d'offres (seuil 34&nbsp;700&nbsp;$ — Règlement sur les contrats d'approvisionnement, art.&nbsp;15). Pour &gt;&nbsp;660&nbsp;h audio/mois ou SLA&nbsp;99,9&nbsp;%, demandez une <a href="/contact?pro-plus=1" class="grad-text underline">soumission Pro+</a>.
</p>
</div>
</section>
{# ===== EXPORT FORMATS DEEP-DIVE (test compat) ===== #}
<section class="bg-white py-16" aria-labelledby="exports-title">
<div class="max-w-[1200px] mx-auto px-6">
<div class="text-center max-w-2xl mx-auto mb-10">
<p class="eyebrow grad-text mb-3">FORMATS D'EXPORT</p>
<h2 id="exports-title" class="text-[clamp(1.75rem,2.5vw,2.25rem)] font-black text-brand-navy">
7 formats, prêts pour vos workflows.
</h2>
</div>
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-7 gap-3">
{% for fmt in [
{'ext': 'DOCX', 'use': 'Word avec timestamps cliquables'},
{'ext': 'PDF', 'use': 'Procès-verbaux signés'},
{'ext': 'SRT', 'use': 'Sous-titres vidéo'},
{'ext': 'VTT', 'use': 'Web/streaming standard'},
{'ext': 'TXT', 'use': 'Texte brut universel'},
{'ext': 'JSON', 'use': 'Pipeline développeur'},
{'ext': 'MD', 'use': 'Notion, Obsidian, GitHub'}
] %}
<div class="ani-lift bg-brand-bg p-4 rounded border border-brand-border text-center">
<p class="text-base font-black font-mono text-brand-navy">{{ fmt.ext }}</p>
<p class="text-xs text-brand-navy/70 mt-1">{{ fmt.use | safe }}</p>
</div>
{% endfor %}
</div>
</div>
</section>
{# ===== TECH SPECS (préservé pour rétro-compat tests) ===== #}
<section class="bg-brand-bg py-16" aria-labelledby="specs-title">
<div class="max-w-[1060px] mx-auto px-6">
<div class="text-center max-w-2xl mx-auto mb-10">
<p class="eyebrow grad-text mb-3">SOUS LE CAPOT</p>
<h2 id="specs-title" class="text-[clamp(1.75rem,2.5vw,2.25rem)] font-black text-brand-navy">
Spécifications techniques détaillées.
</h2>
</div>
<div class="grid md:grid-cols-2 gap-4">
{% for spec in [
{'title': 'Modèle ASR', 'desc': 'WhisperX Large-v3 (1,55&nbsp;G paramètres) fine-tuné sur audio professionnel québécois. Format ONNX optimisé GPU.'},
{'title': 'Diarisation', 'desc': 'pyannote 3.x — clustering hiérarchique sur embeddings ECAPA-TDNN. 1 à 8 locuteurs détectés automatiquement.'},
{'title': 'LLM (résumés / Q&amp;R)', 'desc': 'Mistral 7B Instruct quantifié 4-bit. Inférence locale sur le même GPU. Aucune sortie réseau.'},
{'title': 'Stack web', 'desc': 'Flask 2.3 (backend) + Vue 3 / Alpine.js (front). Chiffrement AES-256 au repos. AGPL&nbsp;v3.'},
{'title': 'Audio supportés (WAV, MP3, M4A, FLAC, OGG, WebM)', 'desc': 'Jusqu\'à 8&nbsp;h par fichier. Conversion ffmpeg automatique. Compatible enregistrements Zoom et Teams.'},
{'title': 'Langues', 'desc': 'Optimisé français québécois. Aussi&nbsp;: français de France, anglais (canadien et US), espagnol, allemand, mandarin, russe.'}
] %}
<article class="ani-fade ani-lift bg-white p-5 rounded border border-brand-border" style="--delay: {{ loop.index0 * 50 }}ms;" data-ani-fade>
<h3 class="text-base font-bold mb-2 text-brand-navy">{{ spec.title | safe }}</h3>
<p class="text-sm text-brand-navy/70 leading-relaxed">{{ spec.desc | safe }}</p>
</article>
{% endfor %}
</div>
</div>
</section>
{# ===== CONFORMITÉ — RÉSUMÉ ===== #}
<section id="conformite-resume" class="bg-white py-20 scroll-mt-32" aria-labelledby="conformite-resume-title">
<div class="max-w-[1060px] mx-auto px-6">
<div class="text-center max-w-3xl mx-auto mb-10">
<p class="eyebrow grad-text mb-4">CONFORMITÉ</p>
<h2 id="conformite-resume-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black text-brand-navy">
<span class="ani-underline" data-ani-underline>Conforme par construction.</span>
</h2>
<p class="text-lg text-brand-navy/70 mt-6">
Architecture pensée d'abord pour la Loi&nbsp;25 et les ordres professionnels québécois — pas une certification ajoutée après coup.
</p>
</div>
<ul class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-10" role="list">
{% for chip in [
{'title': 'Loi&nbsp;25', 'desc': 'La voix est biométrique — le traitement local élimine le risque.'},
{'title': 'Loi&nbsp;96', 'desc': 'Interface, documentation et support 100&nbsp;% français.'},
{'title': '9 ordres pros', 'desc': 'Mappé Barreau, CNQ, CPA, ChAD, OACIQ, CMQ, OIIQ, OPQ, OEQ.'},
{'title': '0 Cloud Act US', 'desc': 'Aucun fournisseur soumis à la juridiction américaine.'}
] %}
<li class="ani-fade ani-lift bg-brand-bg p-5 rounded border border-brand-border" style="--delay: {{ loop.index0 * 60 }}ms;" data-ani-fade>
<span class="inline-flex items-center gap-1.5 bg-brand-navy text-white text-xs font-black uppercase tracking-wider px-2.5 py-1 rounded-full mb-3">
<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"><path d="M5 13l4 4L19 7"/></svg>
{{ chip.title | safe }}
</span>
<p class="text-sm text-brand-navy/80 leading-relaxed">{{ chip.desc | safe }}</p>
</li>
{% endfor %}
</ul>
<div class="text-center">
<a href="/conformite" class="inline-flex items-center gap-2 text-base font-semibold grad-text hover:underline focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2">
Voir le détail conformité
{{ icon_link | safe }}
</a>
</div>
</div>
</section>
{# ===== INTEGRATIONS GRID (compat tests + plugin partner list) ===== #}
<section class="bg-brand-bg py-12" aria-labelledby="integrations-grid-title">
<div class="max-w-[1200px] mx-auto px-6">
<h2 id="integrations-grid-title" class="sr-only">Liste détaillée des intégrations partenaires</h2>
<div class="grid grid-cols-2 md:grid-cols-4 gap-3" role="list">
{% for integ in [
{'name': 'Microsoft Word', 'desc': 'Plugin natif (.docx)'},
{'name': 'Microsoft Outlook','desc': 'Pièces jointes audio'},
{'name': 'Microsoft Teams', 'desc': 'Enregistrements de réunions'},
{'name': 'Notion', 'desc': 'Pages markdown auto'},
{'name': 'Obsidian', 'desc': 'Notes timestamped'},
{'name': 'Zapier', 'desc': 'Workflows no-code'},
{'name': 'Make (Integromat)','desc': 'Scénarios visuels'},
{'name': 'n8n', 'desc': 'Open source self-host'}
] %}
<div class="bg-white p-4 rounded border border-brand-border text-center" role="listitem">
<p class="text-sm font-bold text-brand-navy font-mono">{{ integ.name | safe }}</p>
<p class="text-xs text-brand-navy/70 mt-1">{{ integ.desc | safe }}</p>
</div>
{% endfor %}
</div>
</div>
</section>
{# ===== CTA FINAL ===== #}
<section class="bg-white py-20 border-t border-brand-border" aria-labelledby="features-cta-title">
<div class="max-w-[820px] mx-auto px-6 text-center">
<p class="eyebrow grad-text mb-4">PRÊT À TRANSCRIRE EN LOCAL</p>
<h2 id="features-cta-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black mb-5 text-brand-navy">
Prêt à transcrire <span class="grad-text">en local</span>&nbsp;?
</h2>
<p class="text-lg text-brand-navy/70 mb-10">
Lancement printemps&nbsp;2026 — pré-inscription ouverte. Conçu avec 9&nbsp;ordres professionnels québécois.
</p>
<div class="flex flex-col sm:flex-row gap-3 justify-center">
{% from 'macros/button.html' import button %}
{{ button('Voir les tarifs', href='/tarifs', 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" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4" aria-hidden="true"><path d="M14 5l7 7m0 0l-7 7m7-7H3"/></svg>') }}
{{ button('Demander une démo', href='/contact', variant='secondary', size='lg') }}
</div>
</div>
</section>
{# ===== JS — IntersectionObserver pour ani-fade + ani-underline ===== #}
<script>
(function () {
if (typeof window === 'undefined' || typeof IntersectionObserver === 'undefined') return;
var reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var fadeEls = document.querySelectorAll('[data-ani-fade]');
var underlineEls = document.querySelectorAll('[data-ani-underline]');
if (reduced) {
fadeEls.forEach(function (el) { el.classList.add('is-visible'); });
underlineEls.forEach(function (el) { el.classList.add('is-visible'); });
return;
}
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (!e.isIntersecting) return;
e.target.classList.add('is-visible');
io.unobserve(e.target);
});
}, { threshold: 0.15, rootMargin: '0px 0px -80px 0px' });
fadeEls.forEach(function (el) { io.observe(el); });
underlineEls.forEach(function (el) { io.observe(el); });
})();
</script>
{% endblock %}