Files
dictia-public/templates/marketing/fonctionnalites.html
Allison 5edaddd788 polish(marketing): refonte HYPER PRO 'Comment ça marche' — bezel iPhone, status bar, sound waves, modes enrichis (REC, waveform, typing, ripple, breadcrumb), IA card 40px brain + metrics, connecting line
10 améliorations cinématiques sur la section interactive :

PHONE FRAME RÉALISTE
- Bezel double border + glow ring externe pulsant
- Notch (Dynamic Island stylisé) + speaker grille + camera dot
- Status bar mobile (9:41, signal/wifi/batterie SVG)
- Drop shadow dramatique + inner shadow encastrée

MIC + SOUND WAVES
- 3 ripples concentriques émanant du mic (sound-ring keyframes)

MODE 1 TRANSCRIPTION
- Header REC indicator pulsant (rouge) + nom fichier
- Waveform animée (12 bars hauteurs randomisées)
- Barre progress double avec gradient + glow

MODE 2 DIARISATION
- Header conversation : 3 avatars empilés overlapping
- Bubbles avec timestamps (09:0X) + box-shadow
- Typing indicator (3 dots) entre messages

MODE 3 LANGUES
- Header DÉTECTION AUTOMATIQUE avec dot pulsant
- Ripple wave depuis le centre (rippleDelay calculé par distance)
- 5 langues highlight aléatoire glow brièvement
- Counter live FR · EN · ES · ... + 99+ détectées

MODE 4 EXPORTS
- Grid 4×2 organisé (au lieu de wrap aléatoire)
- Files détaillés avec mini-pages (3 lignes texte)
- Subtitle '7 FORMATS PRÊTS' + checkmark vert

MODE 5 USERS
- Counter centré 01 → 20 (font-mono black)
- 5 USER_COLORS variations (purple/cyan/green/blue/amber)
- Connecting lines SVG pointillés vers centre

MODE 6 SHARE
- Breadcrumb 'Mes dossiers › Réunions'
- Toolbar mini (search, filter, sort)
- Rows structurées avec 3-dots action

BOTTOM ZONE
- 6 boutons agrandis (30+px) avec labels + tab indicator border 2px
- AUTO pill (badge dot pulse) / Manuel countdown bar 4.5s

CARD INFO
- Icon container 32×32 + badge top-right + hover lift

RIGHT PANEL IA
- Brain dans cercle 40×40 gradient + glow
- Metrics row : 0ms latence · 100% privé · 24/7
- Sovereignty bullets : icon dans rounded box

GLOBAL
- Section background : grid pattern + 2 floating orbs blur
- Connecting line SVG cyan→violet phone↔IA panel
- Header section : eyebrow gradient pill + stats row (6/99+/0) + CTA démo
- Crossfade transitions modes (scale 0.96 → 1)

ACCESSIBILITÉ
- prefers-reduced-motion désactive toutes animations
- @media (max-width: 767px) désactive éléments décoratifs CPU-intensive
- aria-pressed, aria-live polite, focus-visible préservés
- 30 nouveaux keyframes CSS scopés

Tests : 1 existant + 1 enrichi (53 lignes, 22 nouvelles assertions)
Tests pass : 30/30 sur how-it-works (les 2 fails conformite sont pré-existants).
2026-04-29 10:04:04 -04:00

1754 lines
112 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends 'marketing/base.html' %}
{% block title %}Fonctionnalités DictIA — WhisperX FR-CA, diarisation 8 locuteurs, Mistral 7B local, Q&R, exports DOCX/PDF/SRT{% endblock %}
{% block description %}Toutes les fonctionnalités de DictIA : transcription WhisperX Large-v3 fine-tunée FR-CA, diarisation pyannote 8 locuteurs, résumés Mistral 7B local, chat Q&R RAG, synchronisation audio-texte, exports multi-formats, intégrations Word, Outlook, Teams, Notion, Obsidian, Zapier.{% endblock %}
{% block head_extra %}
{# Page-scoped CSS — animations subtiles sans dépendre d'un rebuild Tailwind ; respecte prefers-reduced-motion via base.css. #}
<style>
/* Animated underline H2 — visible classe ajoutée via IntersectionObserver */
.ani-underline { position: relative; display: inline-block; }
.ani-underline::after {
content: '';
position: absolute;
left: 50%;
bottom: -10px;
width: 0;
height: 3px;
background: linear-gradient(118deg, #2563eb, #06b6d4 52%, #06b6d4);
transform: translateX(-50%);
transition: width 600ms ease-out;
}
.ani-underline.is-visible::after { width: 56px; }
/* Stagger fade-in cards — class ajoutée par IntersectionObserver */
.ani-fade { opacity: 0; transform: translateY(16px); transition: opacity 600ms ease-out, transform 600ms ease-out; transition-delay: var(--delay, 0ms); }
.ani-fade.is-visible { opacity: 1; transform: translateY(0); }
/* Hover lift cards */
.ani-lift { transition: transform 200ms ease-out, box-shadow 200ms ease-out; }
.ani-lift:hover { transform: translateY(-2px); box-shadow: 0 8px 32px rgba(37,99,235, 0.18); }
/* Sticky sub-nav active link */
.subnav-link[aria-current="true"] { color: #2563eb; }
.subnav-link[aria-current="true"]::after {
content: '';
display: block;
height: 2px;
margin-top: 4px;
background: linear-gradient(118deg, #2563eb, #06b6d4 52%, #06b6d4);
}
/* Cosmic float orbs (n'utilise pas tc-float-y pour éviter rebuild) */
@keyframes orb-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-12px); } }
.orb-float-a { animation: orb-float 8s ease-in-out infinite; }
.orb-float-b { animation: orb-float 11s ease-in-out infinite reverse; }
/* Cloud PRO pulse glow (recommended tier) */
@keyframes card-pulse-glow {
0%, 100% { box-shadow: 0 4px 20px rgba(37,99,235, 0.28); }
50% { box-shadow: 0 12px 40px rgba(37,99,235, 0.5); }
}
.card-pulse-glow { animation: card-pulse-glow 3s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
.ani-fade { opacity: 1; transform: none; transition: none; }
.ani-underline::after { transition: none; width: 56px; }
.ani-lift { transition: none; }
.ani-lift:hover { transform: none; }
.orb-float-a, .orb-float-b, .card-pulse-glow { animation: none; }
}
</style>
{% endblock %}
{% block content %}
{%- set icon_microphone = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><rect x="9" y="2" width="6" height="12" rx="3"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="22"/></svg>' -%}
{%- set icon_users = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>' -%}
{%- set icon_document = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="9" y1="13" x2="15" y2="13"/><line x1="9" y1="17" x2="15" y2="17"/></svg>' -%}
{%- set icon_chat = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>' -%}
{%- set icon_sync = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10"/><path d="M20.49 15a9 9 0 0 1-14.85 3.36L1 14"/></svg>' -%}
{%- set icon_export = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>' -%}
{%- set icon_check = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 mt-0.5 flex-shrink-0 text-brand-b3" aria-hidden="true"><path d="M5 13l4 4L19 7"/></svg>' -%}
{%- set icon_link = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4" aria-hidden="true"><path d="M5 12h14M13 5l7 7-7 7"/></svg>' -%}
{# ===== HERO ===== #}
<section class="relative overflow-hidden bg-brand-navy text-white py-24 md:py-28" aria-labelledby="page-title">
{# Cosmic orbs background — float animation subtile #}
<div class="absolute inset-0 pointer-events-none" aria-hidden="true">
<div class="orb-float-a absolute top-1/4 left-1/4 w-[600px] h-[600px] rounded-full"
style="background: radial-gradient(circle, rgba(37,99,235,0.16) 0%, transparent 60%); filter: blur(40px);"></div>
<div class="orb-float-b absolute top-1/2 right-1/4 w-[500px] h-[500px] rounded-full"
style="background: radial-gradient(circle, rgba(6,182,212,0.07) 0%, transparent 60%); filter: blur(40px);"></div>
<div class="absolute inset-0"
style="background-image: linear-gradient(rgba(255,255,255,0.02) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.02) 1px, transparent 1px); background-size: 40px 40px;"></div>
<div class="absolute top-1/3 left-0 right-0 h-px"
style="background: linear-gradient(90deg, transparent, rgba(37,99,235,0.3), rgba(6,182,212,0.2), transparent);"></div>
</div>
<div class="relative max-w-[1200px] mx-auto px-6 text-center">
<p class="eyebrow grad-text mb-5 animate-tc-fade-in-up" style="animation-delay: 0ms; animation-fill-mode: backwards;">
FONCTIONNALITÉS · WHISPERX · MISTRAL 7B
</p>
<h1 id="page-title" class="text-[clamp(2.25rem,4vw,3.75rem)] font-black leading-[1.05] mb-5 max-w-3xl mx-auto animate-tc-fade-in-up" style="animation-delay: 75ms; animation-fill-mode: backwards;">
Tout ce qu'il vous faut pour transcrire <span class="grad-text">en restant chez soi</span>.
</h1>
<p class="text-lg text-white/70 max-w-2xl mx-auto mb-12 animate-tc-fade-in-up" style="animation-delay: 150ms; animation-fill-mode: backwards;">
Pile technique 100&nbsp;% québécoise. Inférence sur GPU local ou OVH Beauharnois. Aucun transit cloud américain. Conçu avec 9&nbsp;ordres professionnels.
</p>
{# Stats hero — counter via Alpine x-data + IntersectionObserver pour déclenchement #}
<div class="grid grid-cols-2 md:grid-cols-4 gap-8 max-w-4xl mx-auto" role="list">
{% for stat in [
{'target': 95, 'suffix': '&nbsp;%+', 'label': "précision FR-CA", 'sub': 'WhisperX Large-v3'},
{'target': 30, 'suffix': '×', 'label': "vitesse temps réel", 'sub': '1 h audio → ~5 min'},
{'target': 8, 'suffix': '', 'label': "locuteurs simultanés", 'sub': 'pyannote-audio'},
{'target': 0, 'suffix': '', 'label': "transit cloud", 'sub': '100&nbsp;% local'}
] %}
<div class="text-center" role="listitem"
x-data="{ value: 0, target: {{ stat.target }} }"
x-init="
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (reduced) { value = target; return; }
const io = new IntersectionObserver((entries) => {
if (!entries[0].isIntersecting) return;
const start = performance.now();
const dur = 1200;
const tick = (now) => {
const t = Math.min(1, (now - start) / dur);
value = Math.round(target * (1 - Math.pow(1 - t, 3)));
if (t < 1) requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
io.disconnect();
}, { threshold: 0.5 });
io.observe($el);
">
<div class="text-5xl font-black grad-text mb-2 tabular-nums">
<span x-text="value"></span><span>{{ stat.suffix | safe }}</span>
</div>
<div class="text-sm font-semibold text-white mb-1">{{ stat.label }}</div>
<div class="text-xs text-white/60">{{ stat.sub | safe }}</div>
</div>
{% endfor %}
</div>
</div>
</section>
{# ===== STICKY SUB-NAV ===== #}
<nav class="sticky top-[62px] z-40 bg-white/95 backdrop-blur-sm border-b border-brand-border" aria-label="Navigation des sections de la page"
x-data="{ active: 'fonctionnalites' }"
x-init="
const sections = ['fonctionnalites', 'integrations', 'architecture', 'conformite-resume'];
const io = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting && e.intersectionRatio > 0.15) active = e.target.id;
});
}, { rootMargin: '-120px 0px -55% 0px', threshold: [0, 0.15, 0.3] });
sections.forEach(id => { const el = document.getElementById(id); if (el) io.observe(el); });
">
<div class="max-w-[1200px] mx-auto px-6">
<ul class="flex flex-wrap gap-x-6 gap-y-1 py-3 text-sm font-semibold text-brand-navy/70" role="list">
<li><a href="#fonctionnalites" class="subnav-link inline-block py-1 hover:text-brand-b1 transition-colors focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
:aria-current="active === 'fonctionnalites' ? 'true' : 'false'">Fonctionnalités</a></li>
<li><a href="#integrations" class="subnav-link inline-block py-1 hover:text-brand-b1 transition-colors focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
:aria-current="active === 'integrations' ? 'true' : 'false'">Intégrations</a></li>
<li><a href="#architecture" class="subnav-link inline-block py-1 hover:text-brand-b1 transition-colors focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
:aria-current="active === 'architecture' ? 'true' : 'false'">Architecture</a></li>
<li><a href="#conformite-resume" class="subnav-link inline-block py-1 hover:text-brand-b1 transition-colors focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
:aria-current="active === 'conformite-resume' ? 'true' : 'false'">Conformité</a></li>
</ul>
</div>
</nav>
{# ===== 6 FONCTIONNALITÉS PRINCIPALES =====
NOTE: bento card content is duplicated between landing.html and fonctionnalites.html.
When editing, sync both files. Future refactor: extract to _partials/_bento_features.html.
#}
<section id="fonctionnalites" class="bg-white py-20 scroll-mt-32" aria-labelledby="features-title">
<div class="max-w-[1200px] mx-auto px-6">
<div class="text-center max-w-3xl mx-auto mb-14">
<p class="eyebrow grad-text mb-4">SIX FONCTIONNALITÉS</p>
<h2 id="features-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black text-brand-navy">
<span class="ani-underline" data-ani-underline>Conçu pour les pros qui parlent et écrivent toute la journée.</span>
</h2>
<p class="text-lg text-brand-navy/70 mt-6">
Chaque module fonctionne hors-ligne sur votre matériel ou notre VPS Québec OVH. Aucun appel à OpenAI, Microsoft Copilot ou Google.
</p>
</div>
{% from 'macros/bento.html' import bento_card %}
{%- set features = [
{
'n': '01',
'title': 'Transcription IA locale FR-CA',
'desc': 'WhisperX Large-v3 entraîné sur des millions d\'heures de québécois juridique, médical et gouvernemental. 95&nbsp;%+ dès la première utilisation, sans aucun ajustement manuel.',
'icon': icon_microphone,
'chips': ['WhisperX Large-v3', 'GPU RTX 8 ou 16&nbsp;Go', '1 h audio → ~5 min', '< 5&nbsp;% taux d\'erreur', '100&nbsp;% local · hors-ligne']
},
{
'n': '02',
'title': 'Identification des locuteurs (diarisation)',
'desc': 'Jusqu\'à 8 interlocuteurs distingués automatiquement. Chaque prise de parole est étiquetée et horodatée pour une lecture nette des comités, audiences et entrevues.',
'icon': icon_users,
'chips': ['pyannote-audio 3.x', '8 personnes max', 'Étiquettes auto', 'Horodatage word-level']
},
{
'n': '03',
'title': 'Résumés &amp; points d\'action',
'desc': 'Résumé exécutif, décisions clés et liste d\'actions générés en français. Format avocat, notaire, CPA ou médecin selon votre profil. Récupérez ~2 h/jour de rédaction.',
'icon': icon_document,
'chips': ['Mistral Nemo 12B (Cloud) · Mistral 7B (Local)', 'Templates pro', 'Décisions + actions', '2&nbsp;h/jour récupérées']
},
{
'n': '04',
'title': 'Chat IA sur enregistrement (Q&amp;R)',
'desc': 'Posez vos questions directement sur l\'audio : « Quel montant a été mentionné&nbsp;?&nbsp;» ou « Quels engagements ont été pris&nbsp;? » réponses instantanées en langage naturel.',
'icon': icon_chat,
'chips': ['Mistral 7B local', 'RAG local', 'sentence-transformers', 'Recherche contextuelle']
},
{
'n': '05',
'title': 'Synchronisation audio-texte',
'desc': 'Cliquez sur n\'importe quel mot pour sauter à ce moment dans l\'audio. Suivi automatique en lecture avec mot actif surligné révision et référencement instantanés.',
'icon': icon_sync,
'chips': ['Timestamps word-level', 'Lecture guidée', 'Mot actif surligné', 'Navigation par mot clé']
},
{
'n': '06',
'title': 'Recherche sémantique &amp; exports',
'desc': 'Recherchez par sens et non seulement par mot-clé. Exportez vers DOCX, PDF, SRT, VTT, TXT, JSON, MD compatible Obsidian, Notion, Logseq via Markdown.',
'icon': icon_export,
'chips': ['DOCX, PDF, SRT, VTT', 'TXT, JSON, MD', 'Obsidian · Notion · Logseq', 'Format avocat / notaire / CPA']
}
] -%}
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-[1.5px] bg-brand-border rounded overflow-hidden">
{% for f in features %}
<article class="ani-fade ani-lift relative bg-brand-navy2 p-6 overflow-hidden border border-white/[0.045]"
style="--delay: {{ loop.index0 * 60 }}ms;"
data-ani-fade>
<div class="absolute top-2 right-4 text-[80px] font-black grad-text opacity-20 leading-none" aria-hidden="true">{{ f.n }}</div>
<div class="relative">
<div class="text-brand-b1 mb-4" aria-hidden="true">{{ f.icon | safe }}</div>
<h3 class="text-lg font-bold mb-2 text-white">{{ f.title | safe }}</h3>
<p class="text-sm text-white/70 leading-relaxed mb-4">{{ f.desc | safe }}</p>
<ul class="flex flex-wrap gap-1.5" role="list">
{% for chip in f.chips %}
<li class="text-[11px] font-mono font-medium text-white/80 bg-white/[0.06] border border-white/[0.08] px-2 py-1 rounded-full">{{ chip | safe }}</li>
{% endfor %}
</ul>
</div>
</article>
{% endfor %}
</div>
{# Microcopy LPC art. 219 — méthodologie 95% #}
<p class="text-xs text-brand-navy/70 text-center mt-8 max-w-2xl mx-auto">
Précision mesurée sur un échantillon interne d'audio professionnel québécois (juridique, médical, municipal) — méthodologie disponible sur demande à <a href="mailto:info@dictia.ca" class="hover:text-brand-navy underline">info@dictia.ca</a>.
</p>
</div>
</section>
{# ===== COMMENT ÇA MARCHE — DASHBOARD HOLOGRAPHIQUE =====
Reproduction fidèle de DashboardHolographique (Website-Sanity/dictai-narrative.tsx).
Phone container central + 6 modes uniques (Transcription, Diarisation, Langues,
Exports, Users, Share) + IA Mistral 7B premium card + grid 6 features.
Auto-cycle 900ms (1→6→1, skip IA index 0). Click manuel → 4500ms isManual.
#}
<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.04) 1px, transparent 1px),
linear-gradient(to bottom, rgba(15,23,42,0.04) 1px, transparent 1px);
background-size: 60px 60px;
mask-image: radial-gradient(ellipse 80% 60% at 50% 50%, black 0%, transparent 70%);
-webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 50%, black 0%, transparent 70%);
}
.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;
}
.dictia-phone-shell::before {
content: '';
position: absolute;
inset: 2px;
border-radius: 42px;
border: 1px solid rgba(255,255,255,0.04);
pointer-events: none;
background: linear-gradient(180deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0) 30%);
z-index: 0;
}
.dictia-phone-glow-ring { animation: dictia-glow-ring 3.6s ease-in-out infinite; will-change: opacity; }
@keyframes dictia-glow-ring { 0%, 100% { opacity: 0.35; } 50% { opacity: 0.7; } }
/* ============ STATUS BAR + NOTCH + SPEAKER ============ */
.dictia-statusbar { font-variant-numeric: tabular-nums; }
/* ============ MIC SOUND WAVES (3 ripples) ============ */
.dictia-sound-ring {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
border-radius: 50%;
border: 1.5px solid currentColor;
opacity: 0;
will-change: transform, opacity;
animation: dictia-sound-ring-anim 2.4s ease-out infinite;
}
.dictia-sound-ring.r1 { animation-delay: 0s; }
.dictia-sound-ring.r2 { animation-delay: 0.8s; }
.dictia-sound-ring.r3 { animation-delay: 1.6s; }
@keyframes dictia-sound-ring-anim {
0% { transform: translate(-50%, -50%) scale(0.4); opacity: 0.55; }
100% { transform: translate(-50%, -50%) scale(2.4); opacity: 0; }
}
/* Mic pulsing en haut du phone */
.dictia-mic-pulse { animation: dmic-pulse 1.2s ease-in-out infinite; display: inline-block; will-change: transform; }
@keyframes dmic-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.18); } }
/* ============ IA CARD ============ */
.ia-ambient-glow { animation: ia-glow 3s ease-in-out infinite; }
@keyframes ia-glow { 0%, 100% { opacity: 0.4; } 50% { opacity: 0.75; } }
.ia-brain-glow { animation: ia-brain 2.2s ease-in-out infinite; }
@keyframes ia-brain { 0%, 100% { opacity: 0.2; } 50% { opacity: 0.55; } }
.ia-local-badge { animation: ia-local 2.5s ease-in-out infinite; }
@keyframes ia-local { 0%, 100% { opacity: 0.75; } 50% { opacity: 1; } }
/* Curseur clignotant (transcription, IA chat) */
.dictia-blink { animation: dictia-cursor-blink 0.6s steps(2, end) infinite; }
@keyframes dictia-cursor-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
/* Dot pulse */
.dictia-dot-pulse { animation: dictia-dot-pulse-anim 1.8s ease-in-out infinite; }
@keyframes dictia-dot-pulse-anim { 0%, 100% { opacity: 0.4; transform: scale(1); } 50% { opacity: 1; transform: scale(1.35); } }
/* REC dot (transcription) */
.dictia-rec-dot { animation: dictia-rec-pulse 1.4s ease-in-out infinite; }
@keyframes dictia-rec-pulse { 0%, 100% { opacity: 0.5; box-shadow: 0 0 0 0 rgba(239,68,68,0.6); } 50% { opacity: 1; box-shadow: 0 0 0 4px rgba(239,68,68,0); } }
/* Waveform bars */
.dictia-wave-bar { animation: dictia-wave 0.9s ease-in-out infinite; will-change: transform; transform-origin: bottom; }
@keyframes dictia-wave { 0%, 100% { transform: scaleY(0.3); } 50% { transform: scaleY(1); } }
/* Slide-up + fade-in (diarisation messages) */
.dictia-msg-in { animation: dictia-fade-up 0.3s ease-out both; }
@keyframes dictia-fade-up { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
/* Typing indicator (3 dots) */
.dictia-typing-dot { animation: dictia-typing 1.2s ease-in-out infinite; }
@keyframes dictia-typing { 0%, 60%, 100% { opacity: 0.25; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-2px); } }
/* Spring pop (langues, exports, users, share) */
.dictia-spring { animation: dictia-spring-pop 0.28s cubic-bezier(0.34, 1.56, 0.64, 1) both; }
@keyframes dictia-spring-pop { 0% { opacity: 0; transform: scale(0.5); } 80% { transform: scale(1.08); } 100% { opacity: 1; transform: scale(1); } }
.dictia-spring-y { animation: dictia-spring-y 0.32s cubic-bezier(0.34, 1.56, 0.64, 1) both; }
@keyframes dictia-spring-y { 0% { opacity: 0; transform: translateY(14px) scale(0.65); } 80% { transform: translateY(-2px) scale(1.05); } 100% { opacity: 1; transform: translateY(0) scale(1); } }
.dictia-fade-x { animation: dictia-fade-x 0.32s ease-out both; }
@keyframes dictia-fade-x { from { opacity: 0; transform: translateX(10px); } to { opacity: 1; transform: translateX(0); } }
.dictia-fade-y { animation: dictia-fade-y 0.3s ease-out both; }
@keyframes dictia-fade-y { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
/* Mode wrapper crossfade + scale */
.dictia-mode-fade {
animation: dictia-mode-crossfade 0.32s cubic-bezier(0.4, 0, 0.2, 1) both;
will-change: opacity, transform;
}
@keyframes dictia-mode-crossfade {
from { opacity: 0; transform: scale(0.96); }
to { opacity: 1; transform: scale(1); }
}
/* Lang highlight pulse (random langs glow) */
.dictia-lang-highlight { animation: dictia-lang-glow 2.4s ease-in-out infinite; }
@keyframes dictia-lang-glow {
0%, 100% { box-shadow: 0 0 0 0 currentColor; filter: brightness(1); }
50% { box-shadow: 0 0 8px 1px currentColor; filter: brightness(1.4); }
}
/* Connecting line (phone -> IA panel) */
.dictia-connecting-line { animation: dictia-flow 3s linear infinite; }
@keyframes dictia-flow { from { stroke-dashoffset: 24; } to { stroke-dashoffset: 0; } }
/* Auto pill */
.dictia-auto-pulse { animation: dictia-auto-pulse-anim 1.6s ease-in-out infinite; }
@keyframes dictia-auto-pulse-anim { 0%, 100% { transform: scale(1); opacity: 0.7; } 50% { transform: scale(1.4); opacity: 1; } }
/* Manuel countdown bar */
.dictia-countdown-fill { animation: dictia-countdown 4.5s linear forwards; }
@keyframes dictia-countdown { from { width: 100%; } to { width: 0%; } }
/* Feature info card hover */
.dictia-feature-card { transition: transform 0.25s ease, box-shadow 0.25s ease; }
.dictia-feature-card:hover { transform: translateY(-1px); }
/* Bottom feature button (tab indicator) */
.dictia-feat-btn { transition: transform 0.18s ease, background-color 0.2s ease, opacity 0.2s ease; will-change: transform; }
.dictia-feat-btn:hover { transform: scale(1.08); opacity: 1 !important; }
/* Hide scrollbar mobile pills */
.dictia-hide-scrollbar::-webkit-scrollbar { display: none; }
/* ============ 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; }
}
@media (prefers-reduced-motion: reduce) {
.dictia-mic-pulse, .ia-ambient-glow, .ia-brain-glow, .ia-local-badge,
.dictia-blink, .dictia-dot-pulse, .dictia-msg-in, .dictia-spring,
.dictia-spring-y, .dictia-fade-x, .dictia-fade-y, .dictia-mode-fade,
.dictia-sound-ring, .dictia-orb-float-1, .dictia-orb-float-2,
.dictia-phone-glow-ring, .dictia-rec-dot, .dictia-wave-bar,
.dictia-typing-dot, .dictia-lang-highlight, .dictia-connecting-line,
.dictia-auto-pulse, .dictia-countdown-fill { animation: none !important; }
}
</style>
{# Decorative background : grid + floating orbs (desktop only) #}
<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:15%;left:8%;width:200px;height:200px;background:radial-gradient(circle, rgba(34,211,238,0.15) 0%, transparent 70%);filter:blur(60px);"></div>
<div class="dictia-orb-float-2 absolute" style="bottom:10%;right:10%;width:240px;height:240px;background:radial-gradient(circle, rgba(167,139,250,0.18) 0%, transparent 70%);filter:blur(70px);"></div>
</div>
<div class="max-w-[1200px] mx-auto px-6 relative">
<div class="text-center max-w-2xl mx-auto mb-10">
<p class="eyebrow grad-text mb-4 inline-flex items-center gap-2 justify-center px-3 py-1 rounded-full"
style="background:linear-gradient(135deg, rgba(34,211,238,0.10), rgba(167,139,250,0.10)); border:1px solid rgba(167,139,250,0.18);">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4" 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>
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">6</span>
<span class="text-xs font-medium text-brand-navy/60 uppercase tracking-wider">modules</span>
</div>
<div class="w-px h-6 bg-brand-navy/15 hidden sm:block" aria-hidden="true"></div>
<div class="flex items-center gap-2">
<span class="text-2xl font-black grad-text">99+</span>
<span class="text-xs font-medium text-brand-navy/60 uppercase tracking-wider">langues</span>
</div>
<div class="w-px h-6 bg-brand-navy/15 hidden sm:block" aria-hidden="true"></div>
<div class="flex items-center gap-2">
<span class="text-2xl font-black grad-text">0</span>
<span class="text-xs font-medium text-brand-navy/60 uppercase tracking-wider">cloud</span>
</div>
</div>
</div>
{# ── DASHBOARD HOLOGRAPHIQUE — 2 colonnes (phone center + IA right) ── #}
<div x-data="dictiaDashboard()" x-init="init()"
class="w-full flex flex-col lg:flex-row lg:justify-center lg:items-start gap-6 lg:gap-10 relative">
{# Connecting line SVG (phone -> IA panel, desktop only) #}
<div class="dictia-connecting-line-wrap absolute pointer-events-none hidden lg:block"
style="top:200px;left:50%;transform:translateX(-50%);width:160px;height:2px;z-index:1;"
aria-hidden="true">
<svg width="160" height="2" viewBox="0 0 160 2" preserveAspectRatio="none">
<defs>
<linearGradient id="dictia-connecting-grad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#22D3EE" stop-opacity="0.6"/>
<stop offset="100%" stop-color="#A78BFA" stop-opacity="0.6"/>
</linearGradient>
</defs>
<line x1="0" y1="1" x2="160" y2="1" stroke="url(#dictia-connecting-grad)"
stroke-width="1.5" stroke-dasharray="4 4" class="dictia-connecting-line"/>
</svg>
</div>
{# ─────────── ZONE CENTER : Phone container ─────────── #}
<div class="flex flex-col items-center gap-3 lg:w-[290px] flex-shrink-0 relative z-10">
{# Phone shell (bezel, glow ring, drop shadow) #}
<div class="relative w-full max-w-[290px] mx-auto">
{# External glow ring (pulse) #}
<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%, ${activeColor}28 0%, transparent 60%); transition: background 0.4s;`"
aria-hidden="true"></div>
<div class="dictia-phone-shell w-full flex flex-col overflow-hidden relative"
:style="`border: 1.5px solid ${activeColor}40; background: rgba(8,12,24,0.88); box-shadow: 0 30px 80px -20px rgba(0,0,0,0.6), 0 12px 32px -8px ${activeColor}30, inset 0 0 32px ${activeColor}10; min-height: 540px; transition: border-color 0.4s, box-shadow 0.4s;`">
{# Ambient color tint overlay #}
<div class="absolute inset-0 pointer-events-none"
:style="`border-radius: 44px; background-color: ${activeColor}08; transition: background-color 0.4s;`"
aria-hidden="true"></div>
{# Notch / Dynamic Island (desktop only via mobile media) #}
<div class="dictia-notch absolute left-1/2 -translate-x-1/2 z-30"
style="top:8px;width:62px;height:6px;border-radius:6px;background:rgba(0,0,0,0.7);"
aria-hidden="true"></div>
{# Status bar mobile-style (9:41 + signals) #}
<div class="dictia-statusbar relative z-20 flex items-center justify-between px-5 pt-1"
style="height:20px;font-family:ui-monospace,monospace;color:rgba(255,255,255,0.55);font-size:9px;font-weight:600;"
aria-hidden="true">
<span>9:41</span>
<div class="flex items-center gap-1">
{# Signal #}
<svg viewBox="0 0 14 10" width="11" height="8" fill="currentColor">
<rect x="0" y="7" width="2" height="3" rx="0.5"/>
<rect x="3" y="5" width="2" height="5" rx="0.5"/>
<rect x="6" y="3" width="2" height="7" rx="0.5"/>
<rect x="9" y="0" width="2" height="10" rx="0.5"/>
</svg>
{# Wifi #}
<svg viewBox="0 0 14 10" width="11" height="8" fill="none" stroke="currentColor" stroke-width="1.4">
<path d="M1 4 Q7 -1 13 4"/>
<path d="M3 6 Q7 3 11 6"/>
<circle cx="7" cy="9" r="0.8" fill="currentColor"/>
</svg>
{# Battery #}
<svg viewBox="0 0 18 8" width="14" height="7" fill="none" stroke="currentColor" stroke-width="0.8">
<rect x="0" y="0" width="14" height="8" rx="1.5"/>
<rect x="14.5" y="3" width="1" height="2" rx="0.4" fill="currentColor"/>
<rect x="1.5" y="1.5" width="9" height="5" rx="0.5" fill="currentColor"/>
</svg>
</div>
</div>
{# Speaker grille + camera (top of screen, below notch) #}
<div class="dictia-speaker-grille absolute z-10 flex items-center justify-center gap-2"
style="top:32px;left:50%;transform:translateX(-50%);width:80px;height:8px;"
aria-hidden="true">
<div style="width:24px;height:2px;background:rgba(255,255,255,0.10);border-radius:2px;"></div>
<div class="dictia-camera-dot" style="width:4px;height:4px;border-radius:50%;background:rgba(255,255,255,0.18);box-shadow:inset 0 0 1px rgba(0,0,0,0.5);"></div>
</div>
{# TOP : Logo + Mic pulsing (with sound waves) #}
<div class="flex flex-col items-center justify-center gap-2 relative z-10"
style="height: 100px; padding-top: 24px; border-bottom: 1px solid rgba(255,255,255,0.06);">
<img src="/static/images/dictia-logo-nom.png" alt="DictIA"
style="width: 80px; height: 24px; object-fit: contain; opacity: 0.75;">
<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 (1=Transcription, 2=Diarisation, 3=Langues, 4=Exports, 5=Users, 6=Share, 0=IA chat) #}
<div class="flex-1 relative z-10" style="min-height: 260px;"
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" x-data="trModeData()" x-init="init()">
{# Header bar (file + REC) #}
<div class="flex items-center justify-between px-2.5 py-1.5"
style="background:rgba(34,211,238,0.06);border-bottom:1px solid rgba(34,211,238,0.12);">
<div class="flex items-center gap-1.5">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:8px;height:8px;color:rgba(34,211,238,0.80);" 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 style="font-size:6.5px;font-family:monospace;color:rgba(255,255,255,0.55);">reunion-jan14.mp3</span>
</div>
<div class="flex items-center gap-1">
<span class="dictia-rec-dot" style="width:5px;height:5px;border-radius:50%;background:#EF4444;"></span>
<span style="font-size:6px;font-family:monospace;color:rgba(239,68,68,0.85);letter-spacing:0.08em;font-weight:700;">REC</span>
</div>
</div>
{# Waveform animée (8 bars) #}
<div class="flex items-end justify-center gap-0.5 px-3 py-2" style="height:24px;" aria-hidden="true">
<div class="dictia-wave-bar" style="width:2px;height:14px;background:#22D3EE;animation-delay:0s;"></div>
<div class="dictia-wave-bar" style="width:2px;height:18px;background:#22D3EE;animation-delay:0.1s;"></div>
<div class="dictia-wave-bar" style="width:2px;height:10px;background:#22D3EE;animation-delay:0.2s;"></div>
<div class="dictia-wave-bar" style="width:2px;height:20px;background:#22D3EE;animation-delay:0.05s;"></div>
<div class="dictia-wave-bar" style="width:2px;height:12px;background:#22D3EE;animation-delay:0.25s;"></div>
<div class="dictia-wave-bar" style="width:2px;height:16px;background:#22D3EE;animation-delay:0.15s;"></div>
<div class="dictia-wave-bar" style="width:2px;height:8px;background:#22D3EE;animation-delay:0.3s;"></div>
<div class="dictia-wave-bar" style="width:2px;height:14px;background:#22D3EE;animation-delay:0.08s;"></div>
<div class="dictia-wave-bar" style="width:2px;height:18px;background:#22D3EE;animation-delay:0.22s;"></div>
<div class="dictia-wave-bar" style="width:2px;height:10px;background:#22D3EE;animation-delay:0.18s;"></div>
<div class="dictia-wave-bar" style="width:2px;height:14px;background:#22D3EE;animation-delay:0s;"></div>
<div class="dictia-wave-bar" style="width:2px;height:6px;background:#22D3EE;animation-delay:0.12s;"></div>
</div>
<div x-show="phase === 'upload'" class="flex-1 flex flex-col items-center justify-center gap-3 px-4">
<div class="flex flex-col items-center gap-1.5">
<div class="w-10 h-12 rounded-md flex flex-col items-center justify-center relative"
style="background-color: rgba(34,211,238,0.12); border: 1.5px solid rgba(34,211,238,0.40);">
<div class="absolute top-0 right-0" style="width:0;height:0;border-style:solid;border-width:0 7px 7px 0;border-color:transparent rgba(0,0,0,0.40) transparent transparent;"></div>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:13px;height:13px;color:#22D3EE;" 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 style="font-size:5.5px;font-family:monospace;color:rgba(34,211,238,0.70);letter-spacing:0.06em;">MP3</span>
</div>
</div>
<div class="w-full flex flex-col gap-1">
<div class="flex justify-between">
<span style="font-size:6.5px;font-family:monospace;color:rgba(34,211,238,0.65);" x-text="progress < 100 ? 'Envoi en cours…' : 'Prêt ✓'"></span>
<span style="font-size:6.5px;font-family:monospace;color:rgba(34,211,238,0.50);"><span x-text="progress"></span>%</span>
</div>
{# Double progress bar (track + active with glow) #}
<div class="w-full rounded-full overflow-hidden relative" style="height:4px;background-color:rgba(34,211,238,0.10);box-shadow:inset 0 0 2px rgba(0,0,0,0.4);">
<div class="h-full rounded-full" :style="`width: ${progress}%; background: linear-gradient(90deg, #22D3EE, #06B6D4); box-shadow: 0 0 6px rgba(34,211,238,0.6); transition: width 60ms linear;`"></div>
</div>
</div>
</div>
<div x-show="phase === 'transcribing'" class="flex-1 p-3 overflow-y-auto flex flex-col justify-start">
<p class="font-mono leading-relaxed break-words" style="font-size:9px;color:rgba(34,211,238,0.92);background:rgba(34,211,238,0.04);padding:4px 6px;border-radius:6px;border-left:2px solid rgba(34,211,238,0.4);">
<span x-text="words.slice(0, n + 1).join(' ')"></span><span class="dictia-blink ml-px" style="color:#22D3EE;display:inline-block;width:3px;background:#22D3EE;height:9px;vertical-align:middle;">&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" x-data="diaModeData()" x-init="init()">
{# Conversation header with stacked avatars #}
<div class="flex items-center justify-between px-2.5 py-1.5"
style="background:rgba(107,159,255,0.06);border-bottom:1px solid rgba(107,159,255,0.12);">
<div class="flex items-center gap-1.5">
<div class="flex" style="margin-right:2px;">
<div class="rounded-full flex items-center justify-center text-[7px] font-bold"
style="width:14px;height:14px;background:linear-gradient(135deg,#22D3EE,#0891B2);border:1.5px solid rgba(8,12,24,0.9);color:#fff;">S</div>
<div class="rounded-full flex items-center justify-center text-[7px] font-bold"
style="width:14px;height:14px;background:linear-gradient(135deg,#6B9FFF,#3B82F6);border:1.5px solid rgba(8,12,24,0.9);color:#fff;margin-left:-5px;">M</div>
<div class="rounded-full flex items-center justify-center text-[7px] font-bold"
style="width:14px;height:14px;background:linear-gradient(135deg,#F59E0B,#D97706);border:1.5px solid rgba(8,12,24,0.9);color:#fff;margin-left:-5px;">J</div>
</div>
<span style="font-size:6.5px;font-family:monospace;color:rgba(255,255,255,0.65);">Réunion · 3 participants</span>
</div>
<span class="dictia-rec-dot" style="width:5px;height:5px;border-radius:50%;background:#34D399;"></span>
</div>
<div class="flex-1 flex flex-col justify-end gap-1.5 p-2 overflow-hidden">
<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-lg px-2 py-1.5 flex-1 relative"
: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-[8.5px] font-bold leading-none" :style="`color: ${msg.c};`" x-text="msg.s"></p>
<span class="text-[6.5px] font-mono" style="color:rgba(255,255,255,0.30);" x-text="`09:0${i+1}`"></span>
</div>
<p class="text-[8.5px] font-mono leading-snug" style="color:rgba(255,255,255,0.78);" x-text="msg.t"></p>
</div>
</div>
</template>
{# Typing indicator #}
<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-lg 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" style="width:4px;height:4px;border-radius:50%;background:rgba(255,255,255,0.5);animation-delay:0s;"></span>
<span class="dictia-typing-dot" style="width:4px;height:4px;border-radius:50%;background:rgba(255,255,255,0.5);animation-delay:0.2s;"></span>
<span class="dictia-typing-dot" style="width:4px;height:4px;border-radius:50%;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" x-data="langModeData()">
{# Header DÉTECTION AUTOMATIQUE #}
<div class="flex items-center justify-center gap-1.5 py-1.5"
style="background:rgba(34,211,238,0.06);border-bottom:1px solid rgba(34,211,238,0.12);">
<span class="dictia-dot-pulse" style="width:5px;height:5px;border-radius:50%;background:#22D3EE;"></span>
<span style="font-size:7px;font-family:monospace;color:rgba(34,211,238,0.85);letter-spacing:0.16em;font-weight:700;">DÉTECTION AUTOMATIQUE</span>
</div>
<div class="flex-1 overflow-hidden" style="padding:6px 5px;">
<div class="grid content-start" style="grid-template-columns: repeat(7, 1fr); gap: 3px;">
<template x-for="(lang, i) in LANGS" :key="lang">
<span class="font-mono font-bold text-center rounded dictia-spring"
:class="HIGHLIGHTS.includes(i) ? 'dictia-lang-highlight' : ''"
:style="`font-size:6.5px;line-height:16px;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-2.5 py-1.5 mt-auto"
style="background:rgba(34,211,238,0.04);border-top:1px solid rgba(34,211,238,0.10);">
<span class="font-mono dictia-fade-y" style="font-size:6.5px;color:rgba(34,211,238,0.75);letter-spacing:0.06em;animation-delay:1.4s;">
FR · EN · ES · DE · ZH · JA · AR · ...
</span>
<span class="font-mono font-bold dictia-fade-y" style="font-size:7.5px;color:#22D3EE;letter-spacing:0.1em;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" x-data="expModeData()">
{# Header subtle #}
<div class="flex items-center justify-between px-2.5 py-1.5"
style="background:rgba(107,159,255,0.06);border-bottom:1px solid rgba(107,159,255,0.12);">
<span style="font-size:7px;font-family:monospace;color:rgba(107,159,255,0.85);letter-spacing:0.14em;font-weight:700;">EXPORTS DISPONIBLES</span>
</div>
{# Grid 4×2 #}
<div class="grid grid-cols-4 gap-2 p-3 flex-1 content-center justify-items-center">
<template x-for="(f, i) in FILE_TYPES" :key="f.ext">
<div class="dictia-spring-y" :style="`animation-delay:${i * 110}ms;`">
<div class="rounded-md flex flex-col items-center justify-end gap-0.5 relative"
style="width:38px;height:46px;"
: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 8px 8px 0;border-color:transparent rgba(0,0,0,0.40) 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-[10px] font-black leading-none mb-0.5" :style="`color:${f.fg};margin-bottom:8px;`" x-text="f.sym"></span>
<span class="text-[7px] font-mono font-bold leading-none mb-1" :style="`color:${f.fg}BB;`" x-text="f.ext"></span>
</div>
</div>
</template>
</div>
{# Subtitle dynamique avec checkmark #}
<div class="flex items-center justify-center gap-1.5 px-2.5 py-1.5 mt-auto dictia-fade-y"
style="background:rgba(52,211,153,0.06);border-top:1px solid rgba(52,211,153,0.14);animation-delay:0.9s;">
<span style="width:11px;height:11px;border-radius:50%;background:#34D399;display:inline-flex;align-items:center;justify-content:center;">
<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:7px;height:7px;" aria-hidden="true">
<polyline points="20 6 9 17 4 12"/>
</svg>
</span>
<span style="font-size:7px;font-family:monospace;color:#34D399;letter-spacing:0.08em;font-weight:700;">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" x-data="usersModeData()" x-init="init()">
{# Header counter #}
<div class="flex items-center justify-center gap-1.5 py-1.5"
style="background:rgba(167,139,250,0.06);border-bottom:1px solid rgba(167,139,250,0.12);">
<span style="font-size:7px;font-family:monospace;color:rgba(167,139,250,0.75);letter-spacing:0.14em;font-weight:700;">UTILISATEURS</span>
<span class="font-mono font-black" style="font-size:11px;color:#A78BFA;letter-spacing:0.05em;" x-text="count.toString().padStart(2, '0')"></span>
<span style="font-size:7px;font-family:monospace;color:rgba(167,139,250,0.45);">/ ∞</span>
</div>
{# Avatar grid with connecting lines SVG #}
<div class="flex-1 relative p-2 flex flex-wrap gap-1.5 items-center justify-center content-center">
{# SVG connecting lines (desktop only via data attr) #}
<svg class="absolute inset-0 pointer-events-none" width="100%" height="100%" aria-hidden="true" style="opacity:0.4;">
<line x1="20%" y1="30%" x2="50%" y2="50%" stroke="#A78BFA" stroke-width="0.5" stroke-dasharray="2 2"/>
<line x1="80%" y1="30%" x2="50%" y2="50%" stroke="#22D3EE" stroke-width="0.5" stroke-dasharray="2 2"/>
<line x1="20%" y1="70%" x2="50%" y2="50%" stroke="#34D399" stroke-width="0.5" stroke-dasharray="2 2"/>
<line x1="80%" y1="70%" x2="50%" y2="50%" stroke="#6B9FFF" 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:10px;height:10px;" :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">
{# Breadcrumb #}
<div class="flex items-center gap-1 px-2.5 py-1.5"
style="background:rgba(52,211,153,0.06);border-bottom:1px solid rgba(52,211,153,0.12);">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:7px;height:7px;color:#34D399;" 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 style="font-size:6.5px;font-family:monospace;color:rgba(255,255,255,0.55);">Mes&nbsp;dossiers</span>
<span style="font-size:6px;color:rgba(255,255,255,0.30);"></span>
<span style="font-size:6.5px;font-family:monospace;color:#34D399;font-weight:700;">Réunions</span>
{# Toolbar mini #}
<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" style="width:8px;height:8px;color:rgba(255,255,255,0.40);">
<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" style="width:8px;height:8px;color:rgba(255,255,255,0.40);">
<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" style="width:8px;height:8px;color:rgba(255,255,255,0.40);">
<path d="M3 6h18M3 12h18M3 18h18"/>
</svg>
</div>
</div>
<div class="flex-1 flex flex-col gap-1.5" style="padding:6px 7px;">
{# Folders #}
<div class="flex gap-1 flex-wrap">
<template x-for="(f, fi) in [{name:'Réunions',color:'#22D3EE',count:12},{name:'Entretiens',color:'#6B9FFF',count:7},{name:'Formations',color:'#34D399',count:24}]" :key="f.name">
<div class="flex items-center gap-1 rounded-md 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:7px;height:7px;" :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 style="font-size:6.5px;font-family:monospace;" :style="`color:${f.color};`" x-text="f.name"></span>
<span style="font-size:6px;font-family:monospace;" :style="`color:${f.color}88;`" x-text="f.count"></span>
</div>
</template>
</div>
{# Tags #}
<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 dictia-spring"
style="font-size:5.5px;padding:2px 4px;background-color:rgba(52,211,153,0.10);border:1px solid rgba(52,211,153,0.25);color:#34D399;font-family:monospace;"
:style="`animation-delay:${350 + ti * 70}ms;`"
x-text="tag"></span>
</template>
</div>
{# File rows structurées #}
<div class="flex flex-col gap-1 mt-0.5">
<template x-for="(file, fi2) in [{name:'CR-Réunion-Jan14',folder:'Réunions',color:'#22D3EE'},{name:'Entretien-Sophie',folder:'Entretiens',color:'#6B9FFF'},{name:'Formation-RGPD',folder:'Formations',color:'#34D399'}]" :key="file.name">
<div class="dictia-share-row group flex items-center gap-1.5 rounded-lg dictia-fade-x cursor-pointer"
:style="`background-color:${file.color}0C;border:1px solid ${file.color}22;padding:4px 6px;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" style="width:8px;height:8px;flex-shrink:0;" :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" style="font-size:7px;font-family:monospace;color:rgba(255,255,255,0.78);" x-text="file.name"></p>
</div>
<span class="rounded font-mono shrink-0"
style="font-size:5.5px;padding:1px 4px;"
:style="`background:${file.color}18;color:${file.color};border:1px solid ${file.color}33;`"
x-text="file.folder"></span>
<span class="dictia-share-action shrink-0" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="currentColor" style="width:8px;height:8px;" :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 mt-auto dictia-fade-y px-2 py-1"
style="font-size:6.5px;color:rgba(52,211,153,0.65);letter-spacing:0.08em;background:rgba(52,211,153,0.04);border-top:1px solid rgba(52,211,153,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-1.5 py-1.5"
style="border-bottom: 1px solid rgba(167,139,250,0.14); background-color: rgba(167,139,250,0.05);">
<div class="rounded-full flex-shrink-0 dictia-dot-pulse" style="width:6px;height:6px;background-color:#34D399;"></div>
<span style="font-size:6px;font-family:monospace;color:rgba(52,211,153,0.85);letter-spacing:0.14em;">MISTRAL 7B · LOCAL</span>
</div>
<div class="flex-1 flex flex-col justify-end gap-1.5 p-2 overflow-hidden">
<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-lg px-2 py-1.5" style="max-width:92%;"
:style="msg.role === 'user' ? 'background-color:rgba(255,255,255,0.07);border:1px solid rgba(255,255,255,0.12);' : 'background-color:rgba(167,139,250,0.12);border:1px solid rgba(167,139,250,0.30);'">
<p class="font-mono leading-relaxed whitespace-pre-line" style="font-size:7.5px;"
:style="msg.role === 'user' ? 'color:rgba(255,255,255,0.65);' : 'color:#C4B5FD;'">
<span x-text="msg.text"></span><span x-show="msg.role === 'bot' && mi === msgs.length - 1" class="dictia-blink ml-px" style="color:#A78BFA;"></span>
</p>
</div>
</div>
</template>
</div>
<div class="flex items-center justify-center gap-1 py-1.5" style="border-top:1px solid rgba(167,139,250,0.08);">
<span style="font-size:5.5px;font-family:monospace;color:rgba(239,68,68,0.55);letter-spacing:0.06em;">0 donnée envoyée au cloud</span>
</div>
</div>
</template>
</div>
{# BOTTOM : 6 feature icons agrandis (28×28) + label + tab indicator + Auto pill #}
<div class="relative flex flex-col items-center justify-center gap-2 z-10"
style="height: 110px; border-top: 1px solid rgba(255,255,255,0.06); background: linear-gradient(180deg, transparent, rgba(0,0,0,0.30));">
<div class="flex items-end gap-1.5">
<template x-for="i in [1,2,3,4,5,6]" :key="i">
<button type="button" @click="handleManualSelect(i)"
class="dictia-feat-btn outline-none cursor-pointer focus-visible:ring-2 focus-visible:ring-white/40 rounded-md flex flex-col items-center justify-end relative"
:style="`background-color: ${selectedFeature === i ? FEATURES[i].color + '14' : 'transparent'}; border:none; padding: 4px 5px 6px; opacity: ${selectedFeature === i ? 1 : 0.55}; min-width: 30px;`"
:aria-pressed="selectedFeature === i ? 'true' : 'false'"
:aria-label="`Voir : ${FEATURES[i].title}`">
<span :style="`color: ${selectedFeature === i ? FEATURES[i].color : 'rgba(255,255,255,0.42)'}; transition: color 0.2s, transform 0.15s, filter 0.2s; filter: ${selectedFeature === i ? 'drop-shadow(0 0 6px ' + FEATURES[i].color + 'CC)' : 'none'}; transform: scale(${selectedFeature === i ? 1.18 : 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:16px;height:16px;" aria-hidden="true" x-html="featureIconPath(i)"></svg>
</span>
<span class="text-[7px] font-medium mt-0.5 leading-none uppercase tracking-wide"
:style="`color: ${selectedFeature === i ? FEATURES[i].color : 'rgba(255,255,255,0.30)'};`"
x-text="featureShortLabel(i)"></span>
{# Tab indicator bottom border #}
<span x-show="selectedFeature === i" class="absolute" aria-hidden="true"
:style="`bottom: 0; left: 4px; right: 4px; height: 2px; background: ${FEATURES[i].color}; border-radius: 1px; box-shadow: 0 0 6px ${FEATURES[i].color};`"></span>
</button>
</template>
</div>
{# Auto pill / Manuel countdown #}
<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 px-2 py-0.5 rounded-full"
style="background:rgba(52,211,153,0.10);border:1px solid rgba(52,211,153,0.25);">
<span class="dictia-auto-pulse" style="width:5px;height:5px;border-radius:50%;background:#34D399;"></span>
<span style="font-size:7px;font-family:monospace;color:#34D399;letter-spacing:0.12em;font-weight:700;">AUTO</span>
</span>
</template>
<template x-if="isManual">
<span class="inline-flex flex-col items-center gap-0.5">
<span style="font-size:7.5px;font-family:monospace;color:rgba(255,255,255,0.55);" x-text="'Auto reprend bientôt'"></span>
<span class="rounded-full overflow-hidden" style="width:50px;height:2px;background:rgba(255,255,255,0.10);">
<span class="block dictia-countdown-fill h-full" :key="selectedFeature" style="background:linear-gradient(90deg,#22D3EE,#A78BFA);"></span>
</span>
</span>
</template>
</div>
</div>
</div>{# /dictia-phone-shell #}
</div>{# /phone wrapper (glow ring) #}
{# Feature info card sous le phone — premium look avec icon container + hover lift #}
<div style="min-height:64px;" class="w-full max-w-[290px] mt-3">
<div class="dictia-feature-card rounded-xl px-4 py-3 relative"
:style="`background-color: ${activeColor}10; border: 1px solid ${activeColor}38; box-shadow: 0 6px 24px ${activeColor}1F;`">
{# Badge top-right #}
<template x-if="FEATURES[displayMode].badge">
<span class="absolute text-[7.5px] px-1.5 py-0.5 rounded font-mono font-bold"
style="top:8px;right:8px;letter-spacing:0.08em;"
:style="`background-color: ${activeColor}22; color: ${activeColor}; border: 1px solid ${activeColor}55;`"
x-text="FEATURES[displayMode].badge"></span>
</template>
<div class="flex items-start gap-2.5">
{# Icon container 32×32 #}
<span class="rounded-lg flex items-center justify-center flex-shrink-0"
style="width:32px;height:32px;"
:style="`background-color: ${activeColor}18; border: 1px solid ${activeColor}33;`"
aria-hidden="true">
<span :style="`color: ${activeColor};`">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px;" x-html="featureIconPath(displayMode)"></svg>
</span>
</span>
<div class="flex-1 min-w-0 pr-12">
<p class="text-sm font-bold text-white leading-tight mb-0.5" x-text="FEATURES[displayMode].title"></p>
<p class="text-[11px] leading-snug" style="color:rgba(255,255,255,0.62);" x-text="FEATURES[displayMode].subtitle"></p>
</div>
</div>
</div>
</div>
{# Mobile : pills horizontales scrollables #}
<div class="lg:hidden w-full overflow-x-auto dictia-hide-scrollbar" style="scrollbar-width:none;">
<div class="flex gap-2 px-1 pb-1" style="width:max-content;">
<template x-for="i in [1,2,3,4,5,6]" :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;`">
<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:11px;height:11px;" aria-hidden="true" x-html="featureIconPath(i)"></svg>
</span>
<span class="text-[11px] font-medium 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 6 features ─────────── #}
<div class="flex flex-col gap-3 w-full lg:w-[320px] flex-shrink-0 relative z-10">
<div class="mb-1 flex items-center gap-2">
<span class="block w-1 h-5 rounded-full" style="background:linear-gradient(180deg,#22D3EE,#A78BFA);" aria-hidden="true"></span>
<div>
<p class="text-[9px] font-medium uppercase tracking-[0.22em]" style="color:rgba(167,139,250,0.95);">Fonctions clés</p>
<p class="font-bold text-base leading-snug text-brand-navy">Le moteur IA local</p>
</div>
</div>
{# IA Mistral 7B premium card #}
<div class="relative rounded-xl overflow-hidden"
style="border:1.5px solid rgba(167,139,250,0.32);background-color:rgba(8,12,24,0.92);box-shadow:0 0 32px rgba(167,139,250,0.18), 0 12px 40px -10px rgba(0,0,0,0.5);">
{# Ambient purple 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(167,139,250,0.22) 0%, transparent 70%);"
aria-hidden="true"></div>
{# Header — Brain in 40x40 circle #}
<div class="relative px-4 pt-4 pb-3 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(167,139,250,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(167,139,250,0.30), rgba(167,139,250,0.12));border:1.5px solid rgba(167,139,250,0.50);box-shadow:0 4px 12px rgba(167,139,250,0.20);">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:20px;height:20px;color:#C4B5FD;position:relative;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">
<div class="flex items-center gap-1.5 flex-wrap">
<span class="font-bold text-base text-white">IA intégrée</span>
</div>
<div class="flex items-center gap-1.5 flex-wrap mt-1">
<span class="text-[9px] px-2 py-0.5 rounded-md font-mono font-bold"
style="background-color:rgba(167,139,250,0.22);color:#C4B5FD;border:1px solid rgba(167,139,250,0.45);letter-spacing:0.08em;">MISTRAL 7B</span>
<span class="text-[8px] px-2 py-0.5 rounded font-mono font-bold ia-local-badge inline-flex items-center gap-1"
style="background-color:rgba(52,211,153,0.14);color:#34D399;border:1px solid rgba(52,211,153,0.40);letter-spacing:0.12em;">
<span class="dictia-dot-pulse" style="width:4px;height:4px;border-radius:50%;background:#34D399;"></span>
LOCAL
</span>
</div>
<p class="text-[11px] mt-1.5" style="color:rgba(255,255,255,0.62);">Résumé · Points d'action · Q&amp;R</p>
</div>
</div>
{# Divider #}
<div style="height:1px;background:linear-gradient(90deg, transparent, rgba(167,139,250,0.30), transparent);" aria-hidden="true"></div>
{# Performance metrics 3 cells #}
<div class="relative grid grid-cols-3 gap-0">
<div class="text-center py-2.5 px-1" style="border-right:1px solid rgba(167,139,250,0.10);">
<p class="font-mono font-black text-base text-white leading-none">0&thinsp;ms</p>
<p class="text-[8px] mt-1 uppercase tracking-wider" style="color:rgba(167,139,250,0.65);">latence</p>
</div>
<div class="text-center py-2.5 px-1" style="border-right:1px solid rgba(167,139,250,0.10);">
<p class="font-mono font-black text-base leading-none" style="color:#34D399;">100&thinsp;%</p>
<p class="text-[8px] mt-1 uppercase tracking-wider" style="color:rgba(167,139,250,0.65);">privé</p>
</div>
<div class="text-center py-2.5 px-1">
<p class="font-mono font-black text-base leading-none" style="color:#C4B5FD;">24/7</p>
<p class="text-[8px] mt-1 uppercase tracking-wider" style="color:rgba(167,139,250,0.65);">dispo</p>
</div>
</div>
{# Divider #}
<div style="height:1px;background:linear-gradient(90deg, transparent, rgba(167,139,250,0.22), transparent);" aria-hidden="true"></div>
{# Sovereignty bullets — chacun avec icon dans cercle #}
<div class="relative px-4 py-3 flex flex-col gap-2.5">
<div class="flex items-start gap-2.5">
<span class="rounded-md flex items-center justify-center flex-shrink-0 mt-0.5"
style="width:18px;height:18px;background:rgba(167,139,250,0.14);border:1px solid rgba(167,139,250,0.30);"
aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:10px;height:10px;color:#C4B5FD;">
<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" style="color:rgba(255,255,255,0.88);"><strong>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-md flex items-center justify-center flex-shrink-0 mt-0.5"
style="width:18px;height:18px;background:rgba(167,139,250,0.14);border:1px solid rgba(167,139,250,0.30);"
aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:10px;height:10px;color:#C4B5FD;">
<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" style="color:rgba(255,255,255,0.72);">Zéro connexion OpenAI · Google · Microsoft</span>
</div>
<div class="flex items-start gap-2.5">
<span class="rounded-md flex items-center justify-center flex-shrink-0 mt-0.5"
style="width:18px;height:18px;background:rgba(167,139,250,0.14);border:1px solid rgba(167,139,250,0.30);"
aria-hidden="true">
<svg viewBox="0 0 24 24" fill="currentColor" stroke="none" style="width:10px;height:10px;color:#C4B5FD;"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
</span>
<span class="text-[11px] leading-snug" style="color:rgba(255,255,255,0.72);">Inférence hors-ligne · résultats en secondes</span>
</div>
</div>
</div>
{# Feature grid 3 cols × 6 buttons #}
<div class="grid grid-cols-3 gap-2">
<template x-for="i in [1,2,3,4,5,6]" :key="i">
<button type="button" @click="handleManualSelect(i)"
class="flex flex-col items-center gap-1 rounded-xl py-3 px-1 outline-none transition-all focus-visible:ring-2 focus-visible:ring-white/40"
:style="`border: 1px solid ${selectedFeature === i ? FEATURES[i].color + '60' : 'rgba(255,255,255,0.08)'}; background-color: ${selectedFeature === i ? FEATURES[i].color + '14' : 'rgba(8,12,24,0.85)'}; box-shadow: ${selectedFeature === i ? '0 0 16px ' + FEATURES[i].color + '30, inset 0 0 12px ' + FEATURES[i].color + '10' : 'none'}; cursor: pointer;`"
:aria-pressed="selectedFeature === i ? 'true' : 'false'"
:aria-label="`Sélectionner : ${FEATURES[i].title}`">
<span :style="`color: ${selectedFeature === i ? FEATURES[i].color : 'rgba(255,255,255,0.50)'}; filter: ${selectedFeature === i ? 'drop-shadow(0 0 6px ' + FEATURES[i].color + 'BB)' : 'none'}; transform: scale(${selectedFeature === i ? 1.25 : 1}) translateY(${selectedFeature === i ? -1 : 0}px); transition: all 0.2s; display:inline-block;`">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px;" aria-hidden="true" x-html="featureIconPath(i)"></svg>
</span>
<span class="text-[8px] font-medium text-center leading-tight"
:style="`color: ${selectedFeature === i ? 'rgba(255,255,255,0.95)' : 'rgba(255,255,255,0.60)'}; transition: color 0.2s;`"
x-text="FEATURES[i].title"></span>
</button>
</template>
</div>
</div>
</div>
</div>
{# Alpine logic — dictiaDashboard + sub-data functions pour les 6 modes #}
<script>
function dictiaDashboard() {
return {
FEATURES: [
{ idx: 0, title: 'IA intégrée', subtitle: "Résumé, actions, Q&R", color: '#A78BFA', badge: 'Mistral 7B' },
{ idx: 1, title: 'Transcription', subtitle: 'Parole → texte en temps réel', color: '#22D3EE', badge: 'Whisper AI' },
{ idx: 2, title: 'Diarisation', subtitle: 'Identification des locuteurs', color: '#6B9FFF', badge: null },
{ idx: 3, title: '99+ langues', subtitle: 'Détection automatique', color: '#22D3EE', badge: null },
{ idx: 4, title: 'Exports', subtitle: 'DOCX, SRT, JSON, PDF', color: '#6B9FFF', badge: null },
{ idx: 5, title: 'Utilisateurs illimités', subtitle: 'Toute votre équipe', color: '#A78BFA', badge: 'Illimité' },
{ idx: 6, title: 'Partage & Classement', subtitle: 'Dossiers, tags, recherche', color: '#34D399', badge: null }
],
selectedFeature: 1,
isManual: false,
autoCycleTimer: null,
manualResetTimer: null,
get displayMode() { return this.selectedFeature; },
get activeColor() { return this.FEATURES[this.displayMode].color; },
init() {
if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
this.startAutoCycle();
},
startAutoCycle() {
if (this.autoCycleTimer) clearInterval(this.autoCycleTimer);
this.autoCycleTimer = setInterval(() => {
this.selectedFeature = this.selectedFeature < this.FEATURES.length - 1 ? this.selectedFeature + 1 : 1;
}, 900);
},
handleManualSelect(i) {
this.selectedFeature = i;
this.isManual = true;
if (this.autoCycleTimer) { clearInterval(this.autoCycleTimer); this.autoCycleTimer = null; }
if (this.manualResetTimer) clearTimeout(this.manualResetTimer);
this.manualResetTimer = setTimeout(() => {
this.isManual = false;
this.startAutoCycle();
}, 4500);
},
featureIconPath(i) {
const paths = {
0: '<path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24 2.5 2.5 0 0 1 1.98-3A2.5 2.5 0 0 1 9.5 2Z"/><path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24 2.5 2.5 0 0 0-1.98-3A2.5 2.5 0 0 0 14.5 2Z"/>',
1: '<rect x="9" y="2" width="6" height="12" rx="3"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/>',
2: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
3: '<circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>',
4: '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="12" y1="18" x2="12" y2="12"/><polyline points="9 15 12 18 15 15"/>',
5: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><line x1="20" y1="8" x2="20" y2="14"/><line x1="23" y1="11" x2="17" y2="11"/>',
6: '<path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>'
};
return paths[i] || '';
},
featureShortLabel(i) {
const labels = { 1: 'Trans', 2: 'Diari', 3: 'Lang', 4: 'Exp', 5: 'Users', 6: 'Part' };
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: "#22D3EE", t: "La réunion commence à 9h." },
{ s: "Marc", c: "#6B9FFF", t: "J'ai les chiffres du Q4 ici." },
{ s: "Julie", c: "#F59E0B", t: "Je propose reporter la démo." },
{ s: "Sophie", c: "#22D3EE", t: "Accord — on vote là-dessus ?" },
{ s: "Marc", c: "#6B9FFF", t: "Approuvé à l'unanimité." },
{ s: "Julie", c: "#F59E0B", 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 {
LANG_COLORS: ['#22D3EE','#6B9FFF','#A78BFA','#34D399','#F59E0B','#22D3EE','#6B9FFF','#A78BFA'],
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) */
function expModeData() {
return {
FILE_TYPES: [
{ ext: 'DOCX', bg: '#1E6FD9', fg: '#fff', sym: 'W' },
{ ext: 'PDF', bg: '#D93E1E', fg: '#fff', sym: 'PDF' },
{ ext: 'SRT', bg: '#7C3AED', fg: '#fff', sym: 'CC' },
{ ext: 'VTT', bg: '#5B21B6', fg: '#DDD6FE', sym: 'CC' },
{ ext: 'TXT', bg: '#374151', fg: '#9CA3AF', sym: '≡' },
{ ext: 'JSON', bg: '#065F46', fg: '#34D399', sym: '{}' },
{ ext: 'MD', bg: '#1C3A5E', fg: '#6B9FFF', sym: '#' }
]
};
}
/* 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,
USER_COLORS: ['#A78BFA', '#22D3EE', '#34D399', '#6B9FFF', '#F59E0B'],
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 = []; }
};
}
</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 %}