feat(marketing): section interactive 'Comment ça marche' (réacteur DictIA cyclant 6 features)

Ajoute une nouvelle section interactive sous les 6 fonctionnalités
(préservées intégralement) reproduisant le composant React
dictai-narrative.tsx en CSS pur + Alpine.js — sans Framer Motion ni
autre lib JS.

- Réacteur central holographique : 3 anneaux concentriques rotatifs
  (15 s / 22 s / 30 s) + 8 particules orbitales (cyan/blue/fuchsia)
  + wordmark DictIA glow pulsant
- Auto-cycle Alpine.js entre 6 features (1.6 s) avec pause au
  hover/focus et reprise au leave/blur
- Panneau feature active avec aria-live='polite' pour annonce
  lecteur d'écran (Transcription · Diarisation · 99+ langues ·
  Exports · Utilisateurs illimités · Partage & Classement)
- Card 'IA intégrée Mistral 7B LOCAL' avec 3 bullets souveraineté
- Spec list cliquable / hover déclenchant feature dans réacteur
- Layout responsive grid 2 cols desktop, stack mobile
- prefers-reduced-motion désactive rings + orbites + auto-cycle
- Position : APRÈS '6 fonctionnalités', AVANT 'Intégrations'
- Sub-nav reste à 4 ancres (sous-partie visuelle de Fonctionnalités)
- Tests : nouveau test_fonctionnalites_how_it_works_reactor_section
  valide structure, contenu canonique, a11y et Alpine bindings

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Allison
2026-04-29 09:09:40 -04:00
parent e06cba2123
commit 03f6e56f04
3 changed files with 295 additions and 0 deletions

View File

@@ -249,6 +249,221 @@
</div>
</section>
{# ===== COMMENT ÇA MARCHE — RÉACTEUR INTERACTIF =====
Sous-partie visuelle de la section Fonctionnalités (sub-nav reste à 4 ancres).
Reproduit dictai-narrative.tsx (Website-Sanity) en CSS pur + Alpine.js.
#}
<section class="bg-brand-bg py-20" aria-labelledby="how-it-works-title">
<style>
/* Anneaux concentriques rotatifs (réacteur DictIA) */
.reactor-ring { position: absolute; border-radius: 9999px; pointer-events: none; }
.ring-outer { width: 420px; height: 420px; border: 1px solid rgba(37,99,235,0.25); animation: ring-rotate-cw 30s linear infinite; }
.ring-mid { width: 300px; height: 300px; border: 1px solid rgba(6,182,212,0.30); animation: ring-rotate-ccw 22s linear infinite; }
.ring-inner { width: 180px; height: 180px; border: 1px solid rgba(192,38,211,0.35); animation: ring-rotate-cw 15s linear infinite; }
@keyframes ring-rotate-cw { from { transform: rotate(0); } to { transform: rotate(360deg); } }
@keyframes ring-rotate-ccw { from { transform: rotate(0); } to { transform: rotate(-360deg); } }
/* Particules orbitales — 8 dots qui tournent autour du wordmark */
.reactor-orbit-host { position: absolute; left: 50%; top: 50%; width: 1px; height: 1px; }
.orbit { position: absolute; left: 0; top: 0; border-radius: 9999px; transform-origin: 0 0; }
.orbit-1 { width: 8px; height: 8px; background: #2563eb; animation: orbit-spin 12s linear infinite; --r: 210px; }
.orbit-2 { width: 6px; height: 6px; background: #06b6d4; animation: orbit-spin 18s linear infinite reverse; --r: 150px; }
.orbit-3 { width: 6px; height: 6px; background: #c026d3; animation: orbit-spin 9s linear infinite; --r: 90px; }
.orbit-4 { width: 5px; height: 5px; background: #06b6d4; animation: orbit-spin 14s linear infinite reverse; --r: 210px; animation-delay: -3.5s; }
.orbit-5 { width: 7px; height: 7px; background: #c026d3; animation: orbit-spin 20s linear infinite; --r: 150px; animation-delay: -5s; }
.orbit-6 { width: 5px; height: 5px; background: #2563eb; animation: orbit-spin 11s linear infinite reverse; --r: 90px; animation-delay: -2s; }
.orbit-7 { width: 4px; height: 4px; background: #06b6d4; animation: orbit-spin 16s linear infinite; --r: 210px; animation-delay: -8s; }
.orbit-8 { width: 5px; height: 5px; background: #c026d3; animation: orbit-spin 13s linear infinite reverse; --r: 150px; animation-delay: -1s; }
@keyframes orbit-spin {
from { transform: rotate(0deg) translateX(var(--r)) rotate(0deg); }
to { transform: rotate(360deg) translateX(var(--r)) rotate(-360deg); }
}
/* Glow pulsant sous le wordmark central */
@keyframes reactor-glow-pulse {
0%, 100% { opacity: 0.55; transform: scale(1); }
50% { opacity: 0.85; transform: scale(1.08); }
}
.reactor-glow { animation: reactor-glow-pulse 3.2s ease-in-out infinite; }
/* Spec list item état actif */
.feature-list-item { transition: background-color 200ms ease-out, color 150ms ease-out, border-color 150ms ease-out; }
.feature-list-item.is-active { background: rgba(37,99,235,0.10); border-left-color: #2563eb; color: #060d1a; }
@media (prefers-reduced-motion: reduce) {
.ring-outer, .ring-mid, .ring-inner,
.orbit, .reactor-glow { animation: none; }
}
/* Mobile: tighter ring sizes pour rester dans le cadre */
@media (max-width: 640px) {
.ring-outer { width: 320px; height: 320px; }
.ring-mid { width: 220px; height: 220px; }
.ring-inner { width: 140px; height: 140px; }
.orbit-1, .orbit-4, .orbit-7 { --r: 160px; }
.orbit-2, .orbit-5, .orbit-8 { --r: 110px; }
.orbit-3, .orbit-6 { --r: 70px; }
}
</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">
<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">
Du fichier au résumé&nbsp;<span class="grad-text">en temps réel</span>
</h2>
<p class="text-base text-brand-navy/70">
Survolez une fonctionnalité pour voir la machine en action. Glissez pour calculer votre gain de productivité.
</p>
</div>
<div x-data='{
features: ["Transcription", "Diarisation", "99+ langues", "Exports", "Utilisateurs illimités", "Partage &amp; Classement"],
details: {
"Transcription": { tag: "WhisperX Large-v3", desc: "STT 95%+ FR-CA" },
"Diarisation": { tag: "pyannote · 8 locuteurs max", desc: "Identification automatique" },
"99+ langues": { tag: "Détection automatique", desc: "FR · EN · ES · ZH · ..." },
"Exports": { tag: "DOCX, SRT, JSON, PDF", desc: "7 formats standards" },
"Utilisateurs illimités": { tag: "Aucun frais par utilisateur", desc: "Volume illimité" },
"Partage &amp; Classement": { tag: "Permissions granulaires", desc: "Tags + dossiers" }
},
active: "Transcription",
isHovered: false,
timer: null,
init() {
if (window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
this.timer = setInterval(() => {
if (!this.isHovered) {
const idx = this.features.indexOf(this.active);
this.active = this.features[(idx + 1) % this.features.length];
}
}, 1600);
},
setActive(name) { this.isHovered = true; this.active = name; },
resumeCycle() { this.isHovered = false; }
}'
x-init="init()"
class="grid lg:grid-cols-[1fr_minmax(0,360px)] gap-8 items-stretch">
{# ── COLONNE GAUCHE : Réacteur central holographique ── #}
<div class="relative bg-brand-navy p-8 rounded overflow-hidden min-h-[480px] flex flex-col items-center justify-center">
{# Backdrop radial #}
<div class="absolute inset-0 pointer-events-none" aria-hidden="true"
style="background: radial-gradient(ellipse 80% 50% at 50% 50%, rgba(37,99,235,0.10) 0%, transparent 70%);"></div>
{# 3 anneaux concentriques rotatifs #}
<div class="absolute inset-0 flex items-center justify-center" aria-hidden="true">
<div class="reactor-ring ring-outer"></div>
<div class="reactor-ring ring-mid"></div>
<div class="reactor-ring ring-inner"></div>
{# 8 particules orbitales #}
<div class="reactor-orbit-host">
<span class="orbit orbit-1"></span>
<span class="orbit orbit-2"></span>
<span class="orbit orbit-3"></span>
<span class="orbit orbit-4"></span>
<span class="orbit orbit-5"></span>
<span class="orbit orbit-6"></span>
<span class="orbit orbit-7"></span>
<span class="orbit orbit-8"></span>
</div>
</div>
{# Auto badge — top right #}
<div class="absolute top-4 right-4 inline-flex items-center gap-1.5 text-xs text-white/80 z-10 font-mono">
<span class="w-2 h-2 rounded-full bg-brand-b2 animate-pulse" aria-hidden="true"></span>
Auto
</div>
{# Centre : DictIA wordmark + glow + feature panel #}
<div class="relative z-10 text-center">
<div class="relative inline-block mb-6">
<div class="absolute inset-0 reactor-glow pointer-events-none" aria-hidden="true"
style="background: radial-gradient(circle at 50% 50%, rgba(6,182,212,0.35) 0%, transparent 65%); filter: blur(24px); transform: scale(1.4);"></div>
<p class="relative font-black text-5xl sm:text-6xl grad-text leading-none tracking-tight">DictIA</p>
</div>
{# Panneau feature active — swap fluide via x-transition #}
<div class="bg-white/[0.06] border border-white/[0.10] rounded p-4 backdrop-blur-sm min-w-[260px] max-w-[320px] mx-auto"
role="status" aria-live="polite" aria-atomic="true">
<template x-for="feat in features" :key="feat">
<div x-show="active === feat"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 translate-y-1"
x-transition:enter-end="opacity-100 translate-y-0"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0">
<p class="text-[11px] uppercase tracking-[0.18em] text-white/60 mb-1 font-bold" x-text="feat"></p>
<p class="text-sm text-white font-semibold font-mono" x-text="details[feat].tag"></p>
<p class="text-xs text-white/65 mt-1" x-text="details[feat].desc"></p>
</div>
</template>
</div>
</div>
</div>
{# ── COLONNE DROITE : IA intégrée Mistral 7B + spec list cliquable ── #}
<div class="flex flex-col gap-4">
{# Card "IA intégrée Mistral 7B LOCAL" #}
<div class="bg-white border border-brand-border rounded p-5">
<div class="flex items-center gap-2 mb-2 flex-wrap">
<p class="eyebrow text-brand-navy/70">IA intégrée</p>
<span class="px-2 py-0.5 rounded-full bg-brand-b1/10 text-brand-b1 text-[10px] font-bold uppercase tracking-[0.14em] border border-brand-b1/20">Local</span>
</div>
<p class="text-2xl font-black text-brand-navy mb-1">Mistral 7B</p>
<p class="text-sm text-brand-navy/70 mb-4">Résumé · Points d'action · Q&amp;R</p>
<ul class="space-y-2 text-xs text-brand-navy/80" role="list">
<li class="flex items-start gap-2">
{{ icon_check | safe }}
<span>Données hébergées sur VOS serveurs · jamais partagées</span>
</li>
<li class="flex items-start gap-2">
{{ icon_check | safe }}
<span>Zéro connexion OpenAI · Google · Microsoft</span>
</li>
<li class="flex items-start gap-2">
{{ icon_check | safe }}
<span>Inférence hors-ligne · résultats en secondes</span>
</li>
</ul>
</div>
{# Spec list cliquable / hover — déclenche feature dans réacteur #}
<div class="bg-white border border-brand-border rounded p-3">
<p class="eyebrow text-brand-navy/70 px-2 mb-2 mt-1">Fonctions clés</p>
<ul role="list" class="flex flex-col">
<template x-for="feat in features" :key="feat">
<li>
<button type="button"
@mouseenter="setActive(feat)"
@mouseleave="resumeCycle()"
@focus="setActive(feat)"
@blur="resumeCycle()"
@click="setActive(feat)"
:class="active === feat ? 'is-active' : 'border-l-2 border-transparent text-brand-navy/70'"
class="feature-list-item w-full text-left px-3 py-2 text-sm font-semibold border-l-2 hover:text-brand-navy hover:bg-brand-bg focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2 rounded-none"
:aria-pressed="active === feat ? 'true' : 'false'">
<span x-text="feat"></span>
</button>
</li>
</template>
</ul>
</div>
</div>
</div>
</div>
</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">