Files
dictia-public/templates/marketing/fonctionnalites.html
Allison 224e51cc81 feat(marketing): refonte narrative 'Comment ça marche' — processus 4 étapes step-by-step (capture → IA → distribution → gouvernance)
Au lieu d'un cycle générique 1→12, l'animation suit maintenant un FLOW PROCESSUS
canonique qui raconte l'histoire produit du début à la fin :

  ÉTAPE 1 · CAPTURE          → Recording (7) + Transcription (1)
  ÉTAPE 2 · TRANSFORMATION IA → Diarisation (2) + Langues (3) + Résumé (9) + Chat IA (0)
  ÉTAPE 3 · DISTRIBUTION     → Exports (4) + Partage (6) + Users (5) + Intégrations (10)
  ÉTAPE 4 · GOUVERNANCE      → Recherche IA (8) + Audit (11) + Conformité (12)

PROCESS_ORDER = [7, 1, 2, 3, 9, 0, 4, 6, 5, 10, 8, 11, 12] — 13 sub-modes,
mode 0 IA inclus dans le cycle (au lieu d'être skippé), 1100ms × 13 ≈ 14.3s
tour complet.

Composants ajoutés / refondus :
- PROCESS BREADCRUMB visible au-dessus du phone (4 pilules · flèches · past/active/future)
- PROGRESS BAR sous breadcrumb (gradient brand-b1 → b2 → b3, role=progressbar)
- BOTTOM TAB BAR : 4 boutons étapes (au lieu de 6 features individuelles)
- RIGHT PANEL : 4 mini-sections par étape (12 boutons regroupés selon flow)
- FEATURE INFO CARD : préfixe 'Étape X/4 · TITRE' pour contexte processus
- CONNECTING LINE : flèche directionnelle + dot animé qui se déplace selon processIdx
- Mobile pills : suivent PROCESS_ORDER (13 sub-modes au lieu de 1-12)

Alpine refactor :
- selectedFeature devient un getter computed sur PROCESS_ORDER[processIdx]
- nouveau STEPS array avec subModes mapping + activeStep / progressPercent getters
- goToStep(id) helper jump au 1er sub-mode d'une étape
- handleManualSelect(i) résout l'index dans PROCESS_ORDER

Préservé : palette brand stricte (b1/b2/b3/navy), V3 radii, Inter+JetBrains Mono,
phone shell statique, 13 templates de modes, IA Mistral premium card, eyebrow
'COMMENT ÇA MARCHE' brand-navy, WCAG (aria-current, aria-label, role=navigation,
role=progressbar, role=tablist), prefers-reduced-motion guards.

Tests : 9/9 fonctionnalites passent (test how_it_works_reactor étendu de +37 assertions
narratives : PROCESS_ORDER, STEPS, processIdx, activeStep, goToStep, breadcrumb,
progressbar, step-bottom, step-section, contexte processus dans card).

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

2390 lines
154 KiB
HTML
Raw Permalink 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; }
/* Process breadcrumb step buttons (top of section, light bg) */
.dictia-step-btn { transition: transform 0.2s ease, background-color 0.25s ease, border-color 0.25s ease; will-change: transform; }
.dictia-step-btn:hover { transform: translateY(-1px); }
.dictia-step-btn.is-past { opacity: 0.55; }
.dictia-step-btn.is-future { opacity: 0.40; }
.dictia-step-btn.is-active { transform: scale(1.03); }
/* Step navigator bottom (dark bg, inside phone) */
.dictia-step-bottom:hover { transform: translateY(-1px); }
/* Step section right panel (mini groups by stage) */
.dictia-step-section { transition: border-color 0.3s ease, background-color 0.3s ease, box-shadow 0.3s ease; }
.dictia-step-section.is-active { box-shadow: 0 0 24px rgba(0,0,0,0.25), inset 0 0 16px rgba(255,255,255,0.02); }
/* 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; }
.dictia-step-btn, .dictia-step-bottom, .dictia-step-section { transition: 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">12</span>
<span class="text-xs font-medium text-brand-navy/60 uppercase tracking-wider">fonctions · 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 — wrapper outer column (process breadcrumb + progress + 2-col layout) ── #}
<div x-data="dictiaDashboard()" x-init="init()" class="w-full relative">
{# ─────────────── PROCESS BREADCRUMB — 4 étapes du flow utilisateur (refonte narrative 2026-04-29 v3) ─────────────── #}
<nav class="dictia-process-breadcrumb mb-3 mx-auto max-w-3xl"
role="navigation" aria-label="Étapes du processus DictIA">
<ol class="flex items-stretch justify-center gap-1.5 sm:gap-2 px-2">
<template x-for="(step, sIdx) in STEPS" :key="step.id">
<li class="flex items-center flex-1 min-w-0">
<button type="button" @click="goToStep(step.id)"
class="dictia-step-btn group relative flex flex-col items-start justify-center w-full px-2 sm:px-3 py-2 rounded transition-all outline-none focus-visible:ring-2 focus-visible:ring-brand-b1 text-left overflow-hidden"
:class="{
'is-active': activeStep === step.id,
'is-past': activeStep > step.id,
'is-future': activeStep < step.id
}"
:style="`border:1px solid ${activeStep === step.id ? step.color + '70' : 'rgba(0,0,0,0.10)'}; background-color:${activeStep === step.id ? step.color + '14' : 'rgba(255,255,255,0.70)'};`"
:aria-current="activeStep === step.id ? 'step' : false"
:aria-label="`Étape ${step.id} : ${step.title}`">
<div class="flex items-center gap-1.5 w-full min-w-0">
<span class="font-mono font-black text-[10px] tracking-widest flex-shrink-0"
:style="`color:${activeStep === step.id ? step.color : 'rgba(0,0,0,0.35)'};`"
x-text="`0${step.id}`"></span>
<span class="font-bold text-[11px] sm:text-xs leading-tight truncate"
:style="`color:${activeStep === step.id ? 'rgb(2,6,23)' : 'rgba(0,0,0,0.55)'};`"
x-text="step.title"></span>
</div>
<span class="font-mono text-[9px] uppercase tracking-wider mt-0.5 hidden sm:block"
:style="`color:${activeStep === step.id ? step.color : 'rgba(0,0,0,0.40)'};`"
x-text="step.subtitle"></span>
{# Active indicator bottom border 2px #}
<span x-show="activeStep === step.id" class="absolute left-0 right-0 bottom-0" aria-hidden="true"
:style="`height:2px;background:${step.color};box-shadow:0 0 8px ${step.color}80;`"></span>
</button>
{# Arrow between steps (skip last) #}
<template x-if="sIdx < STEPS.length - 1">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"
class="hidden sm:block flex-shrink-0 mx-0.5 text-brand-navy/30" style="width:14px;height:14px;" aria-hidden="true">
<polyline points="9 18 15 12 9 6"/>
</svg>
</template>
</li>
</template>
</ol>
{# PROGRESS BAR — gradient brand-b1 → b2 → b3, width selon processIdx (13 sub-modes) #}
<div class="mx-auto mt-2 max-w-3xl px-2">
<div class="flex items-center justify-between mb-1">
<span class="font-mono text-[9px] uppercase tracking-widest text-brand-navy/50">
Progression du flow
</span>
<span class="font-mono text-[9px] font-bold text-brand-navy/65">
<span x-text="processIdx + 1"></span> / <span x-text="PROCESS_ORDER.length"></span>
</span>
</div>
<div class="w-full rounded-full overflow-hidden" style="height:2px;background:rgba(2,6,23,0.08);"
role="progressbar" :aria-valuenow="processIdx + 1" aria-valuemin="1" :aria-valuemax="PROCESS_ORDER.length">
<div class="h-full rounded-full transition-all duration-700 ease-out"
style="background:linear-gradient(90deg,#2563eb 0%,#06b6d4 50%,#c026d3 100%);box-shadow:0 0 8px rgba(192,38,211,0.35);"
:style="`width:${progressPercent}%;`"></div>
</div>
</div>
</nav>
{# ── 2 colonnes (phone center + IA right) ── #}
<div 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) — directional arrow with travelling dot #}
<div class="dictia-connecting-line-wrap absolute pointer-events-none hidden lg:block"
style="top:200px;left:50%;transform:translateX(-50%);width:160px;height:12px;z-index:1;"
aria-hidden="true">
<svg width="160" height="12" viewBox="0 0 160 12" preserveAspectRatio="none" overflow="visible">
<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="6" x2="148" y2="6" stroke="url(#dictia-connecting-grad)"
stroke-width="1.5" stroke-dasharray="4 4" class="dictia-connecting-line"/>
{# Arrow head pointing right (phone → right panel direction) #}
<polyline points="148,2 156,6 148,10" fill="none" stroke="#c026d3" stroke-opacity="0.7"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
{# Travelling dot — animated position based on processIdx #}
<circle r="2.5" cy="6" fill="#c026d3"
:cx="6 + (140 * processIdx / (PROCESS_ORDER.length - 1))"
style="filter:drop-shadow(0 0 4px #c026d3); transition:cx 0.7s ease-out;"/>
</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 : Step navigator — 4 boutons étapes (au lieu de 6 features individuelles) + Auto pill — refonte narrative 2026-04-29 v3 #}
<div class="relative flex flex-col items-center justify-center gap-1.5 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));">
{# 4 step buttons (mapped to STEPS) — bottom step navigator #}
<div class="flex items-end gap-1 justify-center" role="tablist" aria-label="Étapes du processus">
<template x-for="step in STEPS" :key="step.id">
<button type="button" @click="goToStep(step.id)"
class="dictia-feat-btn dictia-step-bottom outline-none cursor-pointer focus-visible:ring-2 focus-visible:ring-white/40 flex flex-col items-center justify-end relative"
style="min-width:48px;height:42px;border:none;padding:4px 6px 6px;background:transparent;"
:aria-pressed="activeStep === step.id ? 'true' : 'false'"
:aria-current="activeStep === step.id ? 'step' : false"
:aria-label="`Étape ${step.id} : ${step.title}`">
<span class="font-mono font-black text-[10px] tracking-widest leading-none"
:style="`color: ${activeStep === step.id ? step.color : 'rgba(255,255,255,0.40)'}; transition: color 0.2s; filter: ${activeStep === step.id ? 'drop-shadow(0 0 6px ' + step.color + 'CC)' : 'none'};`"
x-text="`0${step.id}`"></span>
<span class="text-[9px] font-mono font-bold mt-1 leading-none uppercase tracking-wider"
:style="`color: ${activeStep === step.id ? step.color : 'rgba(255,255,255,0.55)'};`"
x-text="step.short"></span>
{# Tab indicator bottom border 2px #}
<span x-show="activeStep === step.id" class="absolute" aria-hidden="true"
:style="`bottom: 0; left: 4px; right: 4px; height: 2px; background: ${step.color}; border-radius: 1px; box-shadow: 0 0 8px ${step.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 · 14&thinsp;s</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">Manuel · 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 — affiche contexte processus (étape + sub-mode) — bg-brand-navy SOLIDE + accent border-left #}
<div style="min-height:80px;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>
{# Process context header : ÉTAPE X/4 · TITRE — small breadcrumb pill #}
<div class="flex items-center justify-between gap-2 mb-2">
<span class="font-mono text-[9px] font-bold uppercase tracking-widest"
:style="`color:${STEPS[activeStep - 1].color};`">
Étape <span x-text="activeStep"></span>/4 · <span x-text="STEPS[activeStep - 1].title"></span>
</span>
{# Badge top-right (sub-mode) #}
<template x-if="FEATURES[displayMode].badge">
<span class="text-[9px] px-1.5 py-0.5 rounded font-mono font-bold tracking-wider"
:style="`background-color: ${activeColor}22; color: ${activeColor}; border: 1px solid ${activeColor}55;`"
x-text="FEATURES[displayMode].badge"></span>
</template>
</div>
<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">
<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 features horizontales scrollables — suivent PROCESS_ORDER (13 sub-modes) #}
<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="i in PROCESS_ORDER" :key="i">
<button type="button" @click="handleManualSelect(i)"
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 ${selectedFeature === i ? FEATURES[i].color + '70' : 'rgba(0,0,0,0.10)'}; background-color: ${selectedFeature === i ? FEATURES[i].color + '18' : 'rgba(0,0,0,0.04)'}; outline: none;`"
:aria-pressed="selectedFeature === i ? 'true' : 'false'"
:aria-label="`Fonction : ${FEATURES[i].title}`">
<span :style="`color: ${selectedFeature === i ? FEATURES[i].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="featureIconPath(i)"></svg>
</span>
<span class="text-[11px] font-bold whitespace-nowrap"
:style="`color: ${selectedFeature === i ? FEATURES[i].color : 'rgba(0,0,0,0.50)'};`"
x-text="FEATURES[i].title"></span>
</button>
</template>
</div>
</div>
</div>
{# ─────────── ZONE RIGHT : IA Mistral premium card + grid 12 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 justify-between gap-2">
<div class="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">Fonctions clés</p>
<p class="font-bold text-lg leading-snug text-brand-navy">Le moteur IA local</p>
</div>
</div>
<span class="text-[10px] font-mono font-bold px-2 py-0.5 rounded-full text-brand-b3 whitespace-nowrap"
style="background-color:rgba(192,38,211,0.10);border:1px solid rgba(192,38,211,0.25);">12 fonctions · 4 étapes</span>
</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>
{# Feature grid REFONTE NARRATIVE — 4 mini-sections par étape (12 boutons regroupés selon flow) #}
<div class="flex flex-col gap-3">
<template x-for="step in STEPS" :key="step.id">
<div class="dictia-step-section rounded-xl p-2.5"
:class="activeStep === step.id ? 'is-active' : ''"
:style="`border:1px solid ${activeStep === step.id ? step.color + '60' : 'rgba(255,255,255,0.08)'}; background-color:${activeStep === step.id ? 'rgba(8,12,24,0.92)' : 'rgba(8,12,24,0.65)'}; transition:all 0.3s ease;`"
:aria-current="activeStep === step.id ? 'step' : false">
{# Mini header : badge numéro + titre #}
<div class="flex items-center gap-2 mb-2 px-1">
<span class="rounded-full flex items-center justify-center flex-shrink-0 font-mono font-black text-[10px]"
style="width:18px;height:18px;"
:style="`background:linear-gradient(135deg,${step.color}30,${step.color}14);border:1px solid ${step.color}60;color:${step.color};`"
aria-hidden="true"
x-text="step.id"></span>
<span class="font-mono text-[10px] font-bold uppercase tracking-widest"
:style="`color:${activeStep === step.id ? step.color : 'rgba(255,255,255,0.55)'};`">
Étape <span x-text="step.id"></span> · <span x-text="step.title"></span>
</span>
</div>
{# Mini buttons — flex wrap compact #}
<div class="flex flex-wrap gap-1.5">
<template x-for="i in step.subModes" :key="i">
<button type="button" @click="handleManualSelect(i)"
class="flex items-center gap-1.5 rounded py-1.5 px-2 outline-none transition-all focus-visible:ring-2 focus-visible:ring-white/40 cursor-pointer"
:style="`border:1px solid ${selectedFeature === i ? FEATURES[i].color + '70' : 'rgba(255,255,255,0.10)'}; background-color:${selectedFeature === i ? FEATURES[i].color + '20' : 'rgba(8,12,24,0.55)'}; box-shadow:${selectedFeature === i ? '0 0 12px ' + FEATURES[i].color + '40, inset 0 0 8px ' + FEATURES[i].color + '15' : 'none'}; min-height:30px;`"
:aria-pressed="selectedFeature === i ? 'true' : 'false'"
:aria-label="`Fonction : ${FEATURES[i].title}`">
<span :style="`color:${selectedFeature === i ? FEATURES[i].color : 'rgba(255,255,255,0.55)'}; filter:${selectedFeature === i ? 'drop-shadow(0 0 4px ' + FEATURES[i].color + 'BB)' : 'none'}; transition:all 0.2s; transform:scale(${selectedFeature === i ? 1.1 : 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:13px;height:13px;" aria-hidden="true" x-html="featureIconPath(i)"></svg>
</span>
<span class="text-[10px] font-semibold leading-none whitespace-nowrap"
:style="`color:${selectedFeature === i ? 'rgba(255,255,255,0.98)' : 'rgba(255,255,255,0.80)'};`"
x-text="featureGridLabel(i)"></span>
</button>
</template>
</div>
</div>
</template>
</div>
</div>
</div>{# /2-col flex layout #}
</div>{# /dictiaDashboard wrapper outer column #}
</div>
{# Alpine logic — dictiaDashboard : 12 fonctions visibles (1-12) + IA accessible via Brain card (refonte 2026-04-29) #}
<script>
function dictiaDashboard() {
return {
/* FEATURES indexes (0-12) — IA mode 0 accessible via Brain card; 1-6 = originaux, 7-12 = nouveaux */
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 */
],
/* PROCESS NARRATIF — ordre canonique du flow utilisateur (refonte 2026-04-29 v3).
Cycle : Capture → Transformation IA → Distribution → Recherche & Gouvernance.
13 sub-modes (mode 0 IA inclus) — auto-cycle ≈ 14.3s tour complet. */
PROCESS_ORDER: [7, 1, 2, 3, 9, 0, 4, 6, 5, 10, 8, 11, 12],
STEPS: [
{ id: 1, title: 'Capture', short: 'Capture', subtitle: 'Audio in', subModes: [7, 1], color: '#06b6d4' }, /* brand-b2 */
{ id: 2, title: 'Transformation IA', short: 'Transform.', subtitle: 'Le moteur', subModes: [2, 3, 9, 0], color: '#c026d3' }, /* brand-b3 */
{ id: 3, title: 'Distribution', short: 'Distrib.', subtitle: 'Partage', subModes: [4, 6, 5, 10], color: '#2563eb' }, /* brand-b1 */
{ id: 4, title: 'Recherche & Gouvernance', short: 'Gouv.', subtitle: 'Long terme',subModes: [8, 11, 12], color: '#06b6d4' } /* brand-b2 */
],
processIdx: 0,
isManual: false,
autoCycleTimer: null,
manualResetTimer: null,
get selectedFeature() { return this.PROCESS_ORDER[this.processIdx]; },
get displayMode() { return this.selectedFeature; },
get activeColor() { return this.FEATURES[this.displayMode].color; },
get activeStep() {
const sf = this.selectedFeature;
const s = this.STEPS.find(st => st.subModes.includes(sf));
return s ? s.id : 1;
},
get progressPercent() {
return Math.round(((this.processIdx + 1) / this.PROCESS_ORDER.length) * 100);
},
init() {
if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
this.startAutoCycle();
},
/* Auto-cycle logic : suit PROCESS_ORDER (13 sub-modes), 1100ms each.
Cycle complet : 13 modes × 1100ms ≈ 14.3s puis recommence à processIdx=0. */
startAutoCycle() {
if (this.autoCycleTimer) clearInterval(this.autoCycleTimer);
this.autoCycleTimer = setInterval(() => {
this.processIdx = (this.processIdx + 1) % this.PROCESS_ORDER.length;
}, 1100);
},
/* Click manuel sur un bouton feature : trouve son index dans PROCESS_ORDER + freeze auto 4500ms */
handleManualSelect(i) {
const idx = this.PROCESS_ORDER.indexOf(i);
if (idx >= 0) this.processIdx = idx;
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 sur une étape du breadcrumb / bottom step nav : jump au 1er sub-mode de l'étape */
goToStep(stepId) {
const step = this.STEPS.find(s => s.id === stepId);
if (!step || step.subModes.length === 0) return;
this.handleManualSelect(step.subModes[0]);
},
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] || '';
},
/* Labels compacts pour la grid 3×4 du right panel (raccourcis pour ne pas tronquer) */
featureGridLabel(i) {
const labels = {
1: 'Transcription', 2: 'Diarisation', 3: '99+ langues', 4: 'Exports',
5: 'Utilisateurs', 6: 'Partage', 7: 'Recording', 8: 'Recherche IA',
9: 'Résumés', 10: 'Intégrations', 11: 'Audit trail', 12: 'Conformité'
};
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 %}