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).
This commit is contained in:
Allison
2026-04-29 10:04:04 -04:00
parent 7aaedf2cdf
commit 5edaddd788
2 changed files with 721 additions and 216 deletions

View File

@@ -255,13 +255,68 @@
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" aria-labelledby="how-it-works-title">
<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; }
.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 ambient glow + brain glow + LOCAL pulse */
/* ============ 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; }
@@ -273,14 +328,26 @@
.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 (MISTRAL 7B · LOCAL header) */
/* 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); } }
@@ -291,59 +358,204 @@
.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 fade */
.dictia-mode-fade { animation: dictia-mode-fade 0.22s ease-out both; }
@keyframes dictia-mode-fade { from { opacity: 0; } to { opacity: 1; } }
/* 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 { animation: none; }
.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>
<div class="max-w-[1200px] mx-auto px-6">
<div class="text-center max-w-2xl mx-auto mb-12">
<p class="eyebrow grad-text mb-3 inline-flex items-center gap-2 justify-center">
{# 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-3">
<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">
<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">
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-[280px] flex-shrink-0">
<div class="flex flex-col items-center gap-3 lg:w-[290px] flex-shrink-0 relative z-10">
{# Phone (border-radius 44px, color tinting selon feature active) #}
<div class="w-full max-w-[260px] mx-auto flex flex-col overflow-hidden relative"
:style="`border-radius: 44px; border: 1.5px solid ${activeColor}33; background: rgba(8,12,24,0.8); box-shadow: 0 0 56px ${activeColor}1A, inset 0 0 28px ${activeColor}0A; min-height: 460px; transition: border-color 0.4s, box-shadow 0.4s;`">
{# 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}06; transition: background-color 0.4s;`"
:style="`border-radius: 44px; background-color: ${activeColor}08; transition: background-color 0.4s;`"
aria-hidden="true"></div>
{# TOP : Logo + Mic pulsing #}
{# 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: 96px; border-bottom: 1px solid rgba(255,255,255,0.06);">
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;">
<span class="dictia-mic-pulse" :style="`color: ${activeColor};`" aria-hidden="true">
<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"/>
@@ -352,15 +564,48 @@
</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: 220px;"
<div class="flex-1 relative z-10" style="min-height: 260px;"
role="status" aria-live="polite" aria-atomic="true">
{# Mode 1 : Transcription (upload bar + words appearing) #}
{# Mode 1 : Transcription (header REC + waveform + upload bar + words) #}
<template x-if="displayMode === 1">
<div class="dictia-mode-fade w-full h-full" x-data="trModeData()" x-init="init()">
<div x-show="phase === 'upload'" class="w-full h-full flex flex-col items-center justify-center gap-3 px-4">
<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);">
@@ -372,21 +617,21 @@
</svg>
<span style="font-size:5.5px;font-family:monospace;color:rgba(34,211,238,0.70);letter-spacing:0.06em;">MP3</span>
</div>
<span style="font-size:7px;font-family:monospace;color:rgba(255,255,255,0.42);">reunion-jan14.mp3</span>
</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>
<div class="w-full rounded-full overflow-hidden" style="height:3px;background-color:rgba(34,211,238,0.10);">
<div class="h-full rounded-full" :style="`width: ${progress}%; background-color: #22D3EE; transition: width 60ms linear;`"></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="w-full h-full p-3 overflow-hidden flex flex-col justify-start">
<p class="font-mono leading-relaxed break-words" style="font-size:9px;color:rgba(34,211,238,0.90);">
<span x-text="words.slice(0, n + 1).join(' ')"></span><span class="dictia-blink ml-px" style="color:#22D3EE;"></span>
<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>
@@ -394,77 +639,190 @@
{# Mode 2 : Diarisation (conversation Sophie/Marc/Julie) #}
<template x-if="displayMode === 2">
<div class="dictia-mode-fade w-full h-full flex flex-col justify-end gap-1.5 p-2 overflow-hidden" x-data="diaModeData()" x-init="init()">
<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-[8px] font-bold shrink-0 mt-0.5"
style="width:20px;height:20px;"
:style="`background-color: ${msg.c}28; border: 1px solid ${msg.c}55; color: ${msg.c};`"
<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 flex-1"
:style="`background-color: ${msg.c}10; border: 1px solid ${msg.c}22;`">
<p class="text-[8px] font-bold leading-none mb-0.5" :style="`color: ${msg.c};`" x-text="msg.s"></p>
<p class="text-[8px] font-mono leading-snug" style="color:rgba(255,255,255,0.75);" x-text="msg.t"></p>
<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 staggered 12ms) #}
{# 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 overflow-hidden" style="padding:6px 5px;" x-data="langModeData()">
<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"
:style="`font-size:6.5px;line-height:14px;background-color:${LANG_COLORS[i % LANG_COLORS.length]}12;border:1px solid ${LANG_COLORS[i % LANG_COLORS.length]}28;color:${LANG_COLORS[i % LANG_COLORS.length]};animation-delay:${i * 12}ms;`"
: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>
<p class="text-center font-mono font-bold mt-2 dictia-fade-y"
style="font-size:8px;color:#22D3EE;letter-spacing:0.1em;animation-delay:1.4s;">
</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
</p>
</span>
</div>
</div>
</template>
{# Mode 4 : Exports (7 file icons spring stagger 90ms) #}
{# Mode 4 : Exports (grid 4x2 + files détaillés + checkmark) #}
<template x-if="displayMode === 4">
<div class="dictia-mode-fade w-full h-full p-3 flex flex-wrap gap-2 items-center justify-center" x-data="expModeData()">
<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 * 90}ms;`">
<div class="w-10 h-12 rounded-md flex flex-col items-center justify-center gap-0.5 relative"
<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 7px 7px 0;border-color:transparent rgba(0,0,0,0.35) transparent transparent;"></div>
<span class="text-[10px] font-black leading-none" :style="`color:${f.fg};`" x-text="f.sym"></span>
<span class="text-[7px] font-mono font-bold leading-none" :style="`color:${f.fg}BB;`" x-text="f.ext"></span>
<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 (avatars 1→20 multiplication 200ms) #}
{# 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 p-2 flex flex-wrap gap-1.5 items-center justify-center content-center" x-data="usersModeData()" x-init="init()">
<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"
<div class="rounded-full flex items-center justify-center relative z-10"
:class="i === count ? 'dictia-spring' : ''"
style="width:24px;height:24px;background-color:#A78BFA22;border:1.5px solid #A78BFA66;">
<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:#A78BFA;" aria-hidden="true">
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/>
<circle cx="9" cy="7" r="4"/>
<line x1="20" y1="8" x2="20" y2="14"/>
<line x1="23" y1="11" x2="17" y2="11"/>
: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 (folders + tags + files staggered) #}
{# 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 gap-1.5" style="padding:8px 7px;">
<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">
@@ -487,30 +845,33 @@
x-text="tag"></span>
</template>
</div>
{# Separator #}
<div style="height:1px;background:rgba(255,255,255,0.05);margin:2px 0;"></div>
{# File rows #}
<div class="flex flex-col gap-1">
{# 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="flex items-center gap-1.5 rounded-lg dictia-fade-x"
:style="`background-color:${file.color}0C;border:1px solid ${file.color}22;padding:4px 6px;animation-delay:${650 + fi2 * 120}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:${file.color};`" aria-hidden="true">
<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:6px;font-family:monospace;color:rgba(255,255,255,0.70);" x-text="file.name"></p>
<p style="font-size:5.5px;" :style="`color:${file.color}99;`" x-text="file.folder"></p>
<p class="truncate" style="font-size:7px;font-family:monospace;color:rgba(255,255,255,0.78);" x-text="file.name"></p>
</div>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:6px;height:6px;flex-shrink:0;" :style="`color:${file.color}66;`" aria-hidden="true">
<circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/>
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/>
<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>
<p class="text-center font-mono mt-auto dictia-fade-y"
style="font-size:6px;color:rgba(52,211,153,0.55);letter-spacing:0.08em;animation-delay:1.2s;">
</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>
@@ -544,43 +905,77 @@
</template>
</div>
{# BOTTOM : 6 feature icons (skip IA index 0) + auto-cycle status #}
{# 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: 90px; border-top: 1px solid rgba(255,255,255,0.06);">
<div class="flex items-center gap-3.5">
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="outline-none p-0 cursor-pointer focus-visible:ring-2 focus-visible:ring-white/40 rounded"
style="background:transparent;border:none;"
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.28)'}; 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.3 : 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:15px;height:15px;" aria-hidden="true" x-html="featureIconPath(i)"></svg>
<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>
<p class="text-[9px]" style="color:rgba(255,255,255,0.28);"
x-text="isManual ? 'Cliquez pour changer · Auto reprend bientôt' : '● Auto'"></p>
</div>
</div>
{# Feature info card sous le phone (toujours visible, change avec auto-cycle) #}
<div style="min-height:54px;" class="w-full max-w-[260px]">
<div class="rounded-xl px-3 py-2.5"
:style="`background-color: ${activeColor}0E; border: 1px solid ${activeColor}33; box-shadow: 0 4px 20px ${activeColor}18;`">
<div class="flex items-center gap-1.5 mb-1 flex-wrap">
<span :style="`color: ${activeColor}; flex-shrink: 0;`" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:12px;height:12px;" x-html="featureIconPath(displayMode)"></svg>
{# 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>
<span class="text-[11px] font-bold text-white" x-text="FEATURES[displayMode].title"></span>
<template x-if="FEATURES[displayMode].badge">
<span class="text-[8px] px-1.5 py-0.5 rounded font-mono font-bold"
:style="`background-color: ${activeColor}1A; color: ${activeColor}; border: 1px solid ${activeColor}44;`"
x-text="FEATURES[displayMode].badge"></span>
</template>
</div>
<p class="text-[10px] leading-snug" style="color:rgba(255,255,255,0.52);" x-text="FEATURES[displayMode].subtitle"></p>
</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>
@@ -604,85 +999,124 @@
</div>
{# ─────────── ZONE RIGHT : IA Mistral premium card + grid 6 features ─────────── #}
<div class="flex flex-col gap-3 w-full lg:w-[300px] flex-shrink-0">
<div class="flex flex-col gap-3 w-full lg:w-[320px] flex-shrink-0 relative z-10">
<div class="mb-1">
<p class="text-[9px] font-medium uppercase tracking-[0.22em] mb-1" style="color:rgba(167,139,250,0.85);">Fonctions clés</p>
<p class="font-bold text-base leading-snug text-brand-navy">Fonctionnalité</p>
<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.28);background-color:rgba(8,12,24,0.85);box-shadow:0 0 28px rgba(167,139,250,0.12);">
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.18) 0%, transparent 70%);"
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 #}
<div class="relative px-3 pt-3 pb-2 flex items-start gap-2.5">
<div class="relative flex-shrink-0 mt-0.5">
<div class="absolute -inset-2 rounded-full pointer-events-none ia-brain-glow"
style="background:radial-gradient(circle, rgba(167,139,250,0.4) 0%, transparent 70%);"
{# 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>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:18px;height:18px;color:#A78BFA;position:relative;z-index:1;" aria-hidden="true">
<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-sm text-white">IA intégrée</span>
<span class="text-[8px] px-1.5 py-0.5 rounded-md font-mono font-bold"
style="background-color:rgba(167,139,250,0.20);color:#C4B5FD;border:1px solid rgba(167,139,250,0.42);letter-spacing:0.06em;">Mistral 7B</span>
<span class="text-[7px] px-1.5 py-0.5 rounded font-mono font-bold ia-local-badge"
style="background-color:rgba(52,211,153,0.12);color:#34D399;border:1px solid rgba(52,211,153,0.30);letter-spacing:0.10em;">LOCAL</span>
<span class="font-bold text-base text-white">IA intégrée</span>
</div>
<p class="text-[10px] mt-0.5" style="color:rgba(255,255,255,0.55);">Résumé · Points d'action · Q&amp;R</p>
<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 #}
<div class="relative px-3 py-2.5 flex flex-col gap-2">
<div class="flex items-start gap-2">
<span style="color:rgba(167,139,250,0.65);flex-shrink:0;margin-top:1px;" 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;">
{# 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-[10px] leading-snug" style="color:rgba(255,255,255,0.85);"><strong>Données hébergées sur VOS serveurs</strong> · jamais partagées</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">
<span style="color:rgba(167,139,250,0.65);flex-shrink:0;margin-top:1px;" 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;">
<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-[10px] leading-snug" style="color:rgba(255,255,255,0.65);">Zéro connexion OpenAI · Google · Microsoft</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">
<span style="color:rgba(167,139,250,0.65);flex-shrink:0;margin-top:1px;" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="currentColor" stroke="none" style="width:10px;height:10px;"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
<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-[10px] leading-snug" style="color:rgba(255,255,255,0.65);">Inférence hors-ligne · résultats en secondes</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 (cliquables, état actif = featureColor border + glow + scale 1.25) #}
<div class="grid grid-cols-3 gap-1.5">
{# 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-2.5 px-1 outline-none transition-all focus-visible:ring-2 focus-visible:ring-white/40"
:style="`border: 1px solid ${selectedFeature === i ? FEATURES[i].color + '55' : 'rgba(255,255,255,0.07)'}; background-color: ${selectedFeature === i ? FEATURES[i].color + '12' : 'rgba(8,12,24,0.85)'}; box-shadow: ${selectedFeature === i ? '0 0 12px ' + FEATURES[i].color + '22' : 'none'}; cursor: pointer;`"
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.45)'}; filter: ${selectedFeature === i ? 'drop-shadow(0 0 5px ' + FEATURES[i].color + 'AA)' : '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:14px;height:14px;" aria-hidden="true" x-html="featureIconPath(i)"></svg>
<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-[7.5px] font-medium text-center leading-tight"
:style="`color: ${selectedFeature === i ? 'rgba(255,255,255,0.92)' : 'rgba(255,255,255,0.55)'}; transition: color 0.2s;`"
<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>
@@ -743,6 +1177,10 @@
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] || '';
}
};
}
@@ -807,7 +1245,7 @@
};
}
/* Mode 3 — Langues : grille 100 codes (CSS-only stagger 12ms) */
/* 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'],
@@ -822,7 +1260,20 @@
'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);
}
};
}
@@ -841,10 +1292,11 @@
};
}
/* Mode 5 — Users : multiplication 1→20 (200ms each, loop after 700ms) */
/* 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;

View File

@@ -201,6 +201,59 @@ def test_fonctionnalites_how_it_works_reactor_section():
assert 'prefers-reduced-motion' in body, "Reduced-motion guard missing"
assert 'aria-pressed' in body, "Missing aria-pressed on feature buttons"
# ── HYPER PRO polish refactor 2026-04-29 ── ----------------------------
# Phone shell : bezel + notch + speaker + status bar
assert 'dictia-phone-shell' in body, "Missing phone shell class"
assert 'dictia-notch' in body, "Missing notch element"
assert 'dictia-statusbar' in body, "Missing mobile-style status bar"
assert '9:41' in body, "Missing 9:41 status bar time"
assert 'dictia-sound-ring' in body, "Missing sound ring animation"
assert 'dictia-phone-glow-ring' in body, "Missing phone external glow ring"
# Mode 1 — REC indicator + waveform
assert 'REC' in body, "Missing transcription REC indicator"
assert 'dictia-rec-dot' in body, "Missing REC pulsing dot"
assert 'dictia-wave-bar' in body, "Missing waveform animation"
# Mode 2 — typing indicator + timestamps + stacked avatars
assert 'dictia-typing-dot' in body, "Missing typing indicator dots"
assert 'Réunion · 3 participants' in body, "Missing diarisation header"
# Mode 3 — DÉTECTION AUTOMATIQUE header + ripple from center + counter
assert 'DÉTECTION AUTOMATIQUE' in body, "Missing langues detection header"
assert 'rippleDelay' in body, "Missing ripple delay function"
assert 'HIGHLIGHTS' in body, "Missing langues highlights array"
# Mode 4 — header EXPORTS + checkmark
assert 'EXPORTS DISPONIBLES' in body, "Missing exports header"
assert '7 FORMATS PRÊTS' in body, "Missing exports checkmark subtitle"
# Mode 5 — counter centered + USER_COLORS
assert 'UTILISATEURS' in body, "Missing users counter header"
assert 'USER_COLORS' in body, "Missing users color variations"
# Mode 6 — breadcrumb + toolbar
assert 'Mes&nbsp;dossiers' in body, "Missing share breadcrumb"
# BOTTOM zone — feature buttons + AUTO pill + countdown
assert 'featureShortLabel' in body, "Missing feature short labels function"
assert 'dictia-auto-pulse' in body, "Missing auto pill pulse"
assert 'dictia-countdown-fill' in body, "Missing manuel countdown bar"
# Right panel — IA card metrics + Brain in 40x40 circle
assert 'MISTRAL 7B' in body, "Missing Mistral badge label"
assert 'latence' in body, "Missing latency metric label"
# Section background + connecting line
assert 'dictia-bg-grid' in body, "Missing background grid pattern"
assert 'dictia-bg-orbs' in body, "Missing floating orbs"
assert 'dictia-connecting-line' in body, "Missing connecting line phone↔IA"
# Stats row in section header
assert 'modules' in body, "Missing stats row 'modules'"
assert 'cloud' in body, "Missing stats row 'cloud'"
assert 'Voir une démo' in body, "Missing demo CTA link"
def test_fonctionnalites_export_formats_section():
client = app.test_client()