feat(marketing): intègre pipeline 4 étapes + réseau hub d'intégrations depuis dictia.ca/solutions/dictai

- Pipeline (entre Solution & Bento) : Upload → GPU WhisperX → IA Mistral → Export
  Auto-advance Alpine 2400ms, sweep ring SVG CSS, dot glow, prefers-reduced-motion
- Hub (entre Bento & Pricing) : DictIA → 3 hubs → 9 outils
  SVG natif <animateMotion> sur bezier paths, zéro lib JS, fallback liste 3-col WCAG
- Texte 100% canonique extrait de Website-Sanity dictai-pipeline.tsx + dictai-hub.tsx
- OQLF NBSP : "1 heure d'audio → 2 minutes", "5 000+ apps", "100 % en local"
- WCAG : aria-labelledby sections, role=tab/list, focus-visible, prefers-reduced-motion
- +397 lignes, npm run build:css exécuté pour utilities cyan/purple/opacity

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Allison
2026-04-28 12:03:33 -04:00
parent aad37f8566
commit e49652d85d
2 changed files with 502 additions and 0 deletions

View File

@@ -56,6 +56,7 @@
--color-teal-400: oklch(77.7% 0.152 181.912);
--color-teal-500: oklch(70.4% 0.14 182.503);
--color-teal-600: oklch(60% 0.118 184.704);
--color-cyan-300: oklch(86.5% 0.127 207.078);
--color-blue-50: oklch(97% 0.014 254.604);
--color-blue-100: oklch(93.2% 0.032 255.585);
--color-blue-200: oklch(88.2% 0.059 254.128);
@@ -137,6 +138,7 @@
--radius-md: 0.375rem;
--radius-lg: 0.5rem;
--radius-xl: 0.75rem;
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--animate-spin: spin 1s linear infinite;
@@ -378,6 +380,9 @@
.-top-1 {
top: calc(var(--spacing) * -1);
}
.-top-1\.5 {
top: calc(var(--spacing) * -1.5);
}
.-top-3 {
top: calc(var(--spacing) * -3);
}
@@ -423,6 +428,9 @@
.top-full {
top: 100%;
}
.-right-1\.5 {
right: calc(var(--spacing) * -1.5);
}
.-right-6 {
right: calc(var(--spacing) * -6);
}
@@ -654,6 +662,9 @@
.-mb-px {
margin-bottom: -1px;
}
.mb-0\.5 {
margin-bottom: calc(var(--spacing) * 0.5);
}
.mb-1 {
margin-bottom: calc(var(--spacing) * 1);
}
@@ -810,9 +821,18 @@
.h-72 {
height: calc(var(--spacing) * 72);
}
.h-\[2px\] {
height: 2px;
}
.h-\[62px\] {
height: 62px;
}
.h-\[68px\] {
height: 68px;
}
.h-\[88px\] {
height: 88px;
}
.h-\[95vh\] {
height: 95vh;
}
@@ -831,6 +851,12 @@
.h-\[600px\] {
height: 600px;
}
.h-\[700px\] {
height: 700px;
}
.h-auto {
height: auto;
}
.h-full {
height: 100%;
}
@@ -882,6 +908,9 @@
.min-h-\[8rem\] {
min-height: 8rem;
}
.min-h-\[110px\] {
min-height: 110px;
}
.min-h-\[calc\(100vh-62px\)\] {
min-height: calc(100vh - 62px);
}
@@ -963,6 +992,12 @@
.w-80 {
width: calc(var(--spacing) * 80);
}
.w-\[68px\] {
width: 68px;
}
.w-\[88px\] {
width: 88px;
}
.w-\[400px\] {
width: 400px;
}
@@ -975,6 +1010,9 @@
.w-\[600px\] {
width: 600px;
}
.w-\[700px\] {
width: 700px;
}
.w-full {
width: 100%;
}
@@ -1097,6 +1135,14 @@
--tw-translate-y: calc(calc(1 / 2 * 100%) * -1);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.translate-y-0 {
--tw-translate-y: calc(var(--spacing) * 0);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.translate-y-2 {
--tw-translate-y: calc(var(--spacing) * 2);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.translate-y-full {
--tw-translate-y: 100%;
translate: var(--tw-translate-x) var(--tw-translate-y);
@@ -1113,6 +1159,15 @@
--tw-scale-z: 100%;
scale: var(--tw-scale-x) var(--tw-scale-y);
}
.scale-105 {
--tw-scale-x: 105%;
--tw-scale-y: 105%;
--tw-scale-z: 105%;
scale: var(--tw-scale-x) var(--tw-scale-y);
}
.-rotate-90 {
rotate: calc(90deg * -1);
}
.rotate-45 {
rotate: 45deg;
}
@@ -1190,6 +1245,9 @@
.grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.grid-cols-4 {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.grid-cols-7 {
grid-template-columns: repeat(7, minmax(0, 1fr));
}
@@ -1564,6 +1622,12 @@
.border-brand-b1 {
border-color: #0062ff;
}
.border-brand-b2\/40 {
border-color: color-mix(in oklab, #00bdd8 40%, transparent);
}
.border-brand-b3\/60 {
border-color: color-mix(in oklab, #00c896 60%, transparent);
}
.border-brand-border {
border-color: #e6ebf2;
}
@@ -1831,6 +1895,9 @@
.bg-blue-600 {
background-color: var(--color-blue-600);
}
.bg-brand-b1 {
background-color: #0062ff;
}
.bg-brand-b1\/10 {
background-color: color-mix(in oklab, #0062ff 10%, transparent);
}
@@ -1840,6 +1907,9 @@
.bg-brand-b3\/10 {
background-color: color-mix(in oklab, #00c896 10%, transparent);
}
.bg-brand-b3\/60 {
background-color: color-mix(in oklab, #00c896 60%, transparent);
}
.bg-brand-bg {
background-color: #f7f9fc;
}
@@ -2231,6 +2301,9 @@
.px-6 {
padding-inline: calc(var(--spacing) * 6);
}
.px-7 {
padding-inline: calc(var(--spacing) * 7);
}
.px-8 {
padding-inline: calc(var(--spacing) * 8);
}
@@ -2657,12 +2730,18 @@
.text-brand-b1 {
color: #0062ff;
}
.text-brand-b2 {
color: #00bdd8;
}
.text-brand-b3 {
color: #00c896;
}
.text-brand-navy {
color: #060d1a;
}
.text-brand-navy\/30 {
color: color-mix(in oklab, #060d1a 30%, transparent);
}
.text-brand-navy\/40 {
color: color-mix(in oklab, #060d1a 40%, transparent);
}
@@ -2681,6 +2760,9 @@
.text-brand-navy\/90 {
color: color-mix(in oklab, #060d1a 90%, transparent);
}
.text-cyan-300 {
color: var(--color-cyan-300);
}
.text-emerald-300 {
color: var(--color-emerald-300);
}
@@ -2729,6 +2811,9 @@
.text-purple-300 {
color: var(--color-purple-300);
}
.text-purple-400 {
color: var(--color-purple-400);
}
.text-purple-500 {
color: var(--color-purple-500);
}
@@ -2913,6 +2998,9 @@
.opacity-75 {
opacity: 75%;
}
.opacity-80 {
opacity: 80%;
}
.opacity-90 {
opacity: 90%;
}
@@ -2927,6 +3015,10 @@
--tw-shadow: 0 25px 50px -12px var(--tw-shadow-color, rgb(0 0 0 / 0.25));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.shadow-\[0_0_28px_rgba\(0\,98\,255\,0\.35\)\] {
--tw-shadow: 0 0 28px var(--tw-shadow-color, rgba(0,98,255,0.35));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.shadow-cta {
--tw-shadow: 0 4px 20px var(--tw-shadow-color, rgba(0, 98, 255, 0.28));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -2947,6 +3039,10 @@
--tw-shadow: 0 20px 25px -5px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 8px 10px -6px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.ring {
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.ring-1 {
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -3060,6 +3156,10 @@
--tw-duration: 500ms;
transition-duration: 500ms;
}
.ease-in {
--tw-ease: var(--ease-in);
transition-timing-function: var(--ease-in);
}
.ease-in-out {
--tw-ease: var(--ease-in-out);
transition-timing-function: var(--ease-in-out);
@@ -4110,6 +4210,11 @@
outline-offset: 2px;
}
}
.focus-visible\:outline-offset-4 {
&:focus-visible {
outline-offset: 4px;
}
}
.focus-visible\:outline-brand-b1 {
&:focus-visible {
outline-color: #0062ff;

View File

@@ -455,6 +455,212 @@
</div>
</section>
{# ===== PIPELINE — Comment ça marche : 4 étapes ===== #}
{# Source canonique : InnovA-AI/Website-Sanity/components/sections/dictai-pipeline.tsx
Animation traduite : Framer Motion auto-advance + sweep ring → Alpine.js setInterval + CSS keyframes #}
<style>
/* Sweep ring on the active pipeline node — restarts via :key (DOM swap) */
@keyframes pipeline-sweep {
from { stroke-dashoffset: 257.6; }
to { stroke-dashoffset: 0; }
}
.pipeline-sweep-ring {
stroke-dasharray: 257.6; /* 2π × 41 */
animation: pipeline-sweep 2400ms linear forwards;
}
/* Underline sweep on the description card — restart on Alpine swap */
@keyframes pipeline-underline {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
.pipeline-underline {
transform-origin: left;
animation: pipeline-underline 2400ms linear forwards;
}
/* Glowing dot trailing the active step on the track */
.pipeline-dot {
transition: left 450ms cubic-bezier(0.4, 0, 0.2, 1);
}
/* Filled track from start to active step */
.pipeline-track-fill {
transition: transform 450ms cubic-bezier(0.4, 0, 0.2, 1);
transform-origin: left;
}
/* Reduced motion : freeze auto-advance + remove all animations */
@media (prefers-reduced-motion: reduce) {
.pipeline-sweep-ring, .pipeline-underline, .pipeline-dot, .pipeline-track-fill {
animation: none !important;
transition: none !important;
}
}
/* Hide Alpine x-show elements until Alpine hydrates (avoids FOUC flash of all 4 cards) */
[x-cloak] { display: none !important; }
</style>
<section class="bg-white py-20 border-y border-brand-border" aria-labelledby="pipeline-title">
<div class="max-w-[1100px] mx-auto px-6">
<div class="text-center max-w-2xl mx-auto mb-14">
<p class="eyebrow grad-text mb-4">COMMENT&nbsp;ÇA MARCHE</p>
<h2 id="pipeline-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black mb-4 text-brand-navy">
Du fichier au résumé <span class="grad-text">en 4 étapes</span>.
</h2>
<p class="text-lg text-brand-navy/70">
Aucune installation côté utilisateur, aucune conversion préalable. DictIA orchestre l'ensemble du pipeline — du téléversement à l'export — en moins de deux minutes pour une heure d'audio.
</p>
</div>
{# Pipeline track + 4 nodes — Alpine state drives all visuals.
active = 0..3 (current step), prefers-reduced-motion stops the autoplay (handled in x-init). #}
<div
class="relative max-w-4xl mx-auto"
x-data="{
active: 0,
timer: null,
steps: [
{ positionPct: 12.5, fillPct: 0 },
{ positionPct: 37.5, fillPct: 33.33 },
{ positionPct: 62.5, fillPct: 66.66 },
{ positionPct: 87.5, fillPct: 100 }
],
startTimer() {
if (this.timer) clearInterval(this.timer);
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
this.timer = setInterval(() => { this.active = (this.active + 1) % 4; }, 2400);
},
select(i) { this.active = i; this.startTimer(); }
}"
x-init="startTimer()"
>
{# Background + filled track (top: 44px = node center 88px / 2) #}
<div class="absolute h-px bg-brand-border" style="top: 44px; left: 12.5%; right: 12.5%;" aria-hidden="true"></div>
<div
class="pipeline-track-fill absolute h-[2px]"
style="top: 43px; left: 12.5%; width: 75%; background: linear-gradient(90deg, #0062ff 0%, #00bdd8 100%);"
:style="`transform: scaleX(${steps[active].fillPct / 100})`"
aria-hidden="true"
></div>
{# Moving glowing dot — left transitions via CSS #}
<div
class="pipeline-dot absolute w-3.5 h-3.5 rounded-full bg-white pointer-events-none z-10"
style="top: 44px; transform: translate(-50%, -50%); box-shadow: 0 0 18px rgba(0,189,216,0.55), 0 0 36px rgba(0,98,255,0.35);"
:style="`left: ${steps[active].positionPct}%`"
aria-hidden="true"
></div>
{# Nodes grid — 4 cols #}
<div class="grid grid-cols-4 gap-2">
{# Each step rendered statically; active state computed via :class. #}
{% set steps = [
('Uploader', 'Audio ou vidéo', '<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="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>',
"Glissez n'importe quel format — MP3, WAV, MP4, MKV. Aucune conversion nécessaire. DictIA accepte tout sans préparation."),
('GPU transcrit', 'WhisperX Large-v3', '<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="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6"/><path d="M9 1v3M15 1v3M9 20v3M15 20v3M20 9h3M20 14h3M1 9h3M1 14h3"/></svg>',
"Votre GPU traite le fichier 60× plus vite qu'un humain. 1&nbsp;heure d'audio → 2&nbsp;minutes. Précision 95&nbsp;%+ en français canadien. 100&nbsp;% en local — zéro cloud étranger."),
('IA structure', 'Mistral 7B / Nemo 12B', '<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="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>',
"L'IA identifie automatiquement chaque locuteur, génère un résumé structuré, extrait les points d'action et répond à vos questions sur le contenu transcrit."),
('Exporter', 'Dans votre écosystème', '<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>',
"DOCX, SRT, VTT, JSON, Obsidian, Logseq. Connectez avec n8n, Zapier ou Make pour automatiser vos workflows — zéro copier-coller.")
] %}
{% for label, sublabel, icon, desc in steps %}
<button
type="button"
@click="select({{ loop.index0 }})"
class="group flex flex-col items-center gap-4 cursor-pointer select-none focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-4 rounded-none p-2"
:aria-current="active === {{ loop.index0 }} ? 'step' : 'false'"
aria-label="Étape {{ loop.index }} sur 4 : {{ label }} — {{ sublabel }}"
>
{# Circle node — visual state driven by :class (active / completed / pending) #}
<span
class="relative w-[88px] h-[88px] rounded-full border-2 flex items-center justify-center transition-all duration-300"
:class="{
'border-brand-b1 bg-brand-b1/10 scale-105 shadow-[0_0_28px_rgba(0,98,255,0.35)]': active === {{ loop.index0 }},
'border-brand-b3/60 bg-brand-b3/10': active > {{ loop.index0 }},
'border-brand-border bg-white': active < {{ loop.index0 }}
}"
aria-hidden="true"
>
<span
class="transition-opacity duration-300"
:class="active >= {{ loop.index0 }} ? 'opacity-100 text-brand-b1' : 'opacity-30 text-brand-navy/30'"
>{{ icon | safe }}</span>
{# Step badge — top-right corner with index #}
<span
class="absolute -top-1.5 -right-1.5 w-6 h-6 rounded-full flex items-center justify-center text-[11px] font-bold transition-colors duration-300"
:class="active >= {{ loop.index0 }} ? 'bg-brand-b1 text-white' : 'bg-brand-border text-brand-navy/40'"
>{{ loop.index }}</span>
{# Sweep progress ring — only visible on active step. Re-keyed via :key when active changes. #}
<template x-if="active === {{ loop.index0 }}">
<svg
:key="active"
class="absolute inset-0 w-full h-full -rotate-90 pointer-events-none"
viewBox="0 0 88 88"
aria-hidden="true"
>
<circle class="pipeline-sweep-ring" cx="44" cy="44" r="41" fill="none" stroke="#0062ff" stroke-width="2" stroke-linecap="round"></circle>
</svg>
</template>
</span>
{# Label + sublabel — opacity dimmed on pending steps #}
<span
class="text-center px-1 transition-opacity duration-300 block"
:class="active >= {{ loop.index0 }} ? 'opacity-100' : 'opacity-40'"
>
<span class="block font-bold text-sm leading-tight mb-0.5 text-brand-navy">{{ label }}</span>
<span class="block text-xs text-brand-navy/70 leading-tight">{{ sublabel }}</span>
</span>
</button>
{% endfor %}
</div>
{# Step description card — swaps with x-show + x-transition. Underline sweep restarts via :key on active. #}
<div class="mt-12 max-w-2xl mx-auto min-h-[110px] flex items-center justify-center">
{% for label, sublabel, icon, desc in steps %}
<div
x-show="active === {{ loop.index0 }}"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 translate-y-2"
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"
class="relative w-full bg-brand-bg border border-brand-border px-7 py-6 text-center rounded overflow-hidden"
x-cloak
>
<p class="text-sm text-brand-navy/80 leading-relaxed">{{ desc | safe }}</p>
<span
:key="active"
class="pipeline-underline absolute bottom-0 left-0 h-[2px] w-full"
style="background: linear-gradient(90deg, #0062ff, #00bdd8);"
aria-hidden="true"
></span>
</div>
{% endfor %}
</div>
{# Progress dots (mobile-friendly + click target redundancy) #}
<div class="flex justify-center gap-2 mt-8" role="tablist" aria-label="Sélectionner une étape">
{% for label, sublabel, icon, desc in steps %}
<button
type="button"
role="tab"
@click="select({{ loop.index0 }})"
class="h-1 rounded-full transition-all duration-300 focus-visible:outline-2 focus-visible:outline-brand-b1 focus-visible:outline-offset-2"
:class="{
'w-8 bg-brand-b1': active === {{ loop.index0 }},
'w-8 bg-brand-b3/60': active > {{ loop.index0 }},
'w-2 bg-brand-border': active < {{ loop.index0 }}
}"
:aria-selected="active === {{ loop.index0 }} ? 'true' : 'false'"
aria-label="Aller à l'étape {{ loop.index }} : {{ label }}"
></button>
{% endfor %}
</div>
</div>
</div>
</section>
{# ===== BENTO FEATURES ===== #}
<section class="bg-white py-20" aria-labelledby="bento-title">
<div class="max-w-[1060px] mx-auto px-6">
@@ -486,6 +692,197 @@
</div>
</section>
{# ===== HUB — Réseau d'intégrations ===== #}
{# Source canonique : InnovA-AI/Website-Sanity/components/sections/dictai-hub.tsx
Animation : SVG pur avec <animateMotion> sur bezier paths — aucune lib JS, aucune config.
Particules cyan circulent en continu de DictIA → Hubs → Outils. #}
<style>
/* DictIA central node — pulsing rings (decorative, native CSS) */
@keyframes hub-ring-pulse {
0% { transform: scale(1); opacity: 0.55; }
70% { transform: scale(2.6); opacity: 0; }
100% { transform: scale(2.6); opacity: 0; }
}
.hub-ring { animation: hub-ring-pulse 3s ease-out infinite; }
.hub-ring-2 { animation-delay: 1s; }
.hub-ring-3 { animation-delay: 2s; }
/* DictIA core breathing glow */
@keyframes hub-core-breathe {
0%, 100% { box-shadow: 0 0 26px rgba(0,189,216,0.55), 0 0 56px rgba(0,189,216,0.18); }
50% { box-shadow: 0 0 38px rgba(0,189,216,0.75), 0 0 72px rgba(0,189,216,0.28); }
}
.hub-core { animation: hub-core-breathe 3.5s ease-in-out infinite; }
/* Reduced motion : freeze everything */
@media (prefers-reduced-motion: reduce) {
.hub-ring, .hub-core { animation: none !important; }
/* Disable SVG animateMotion via display:none on the animateMotion elements (browser respects this). */
.hub-network-svg animateMotion { display: none !important; }
}
</style>
<section class="bg-brand-navy text-white py-20 overflow-hidden relative" aria-labelledby="hub-title">
{# Decorative cosmic orb — cyan, mid-section #}
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[700px] h-[700px] rounded-full pointer-events-none" aria-hidden="true"
style="background: radial-gradient(circle, rgba(0,189,216,0.10) 0%, transparent 60%); filter: blur(60px);"></div>
<div class="relative max-w-[1200px] mx-auto px-6">
<div class="text-center max-w-2xl mx-auto mb-12">
<p class="eyebrow grad-text mb-4">INTÉGRATIONS — RÉSEAU OUVERT</p>
<h2 id="hub-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black mb-4">
DictIA <span class="grad-text">se connecte à tout</span>.
</h2>
<p class="text-lg text-white/70">
Transcriptions et résumés acheminés automatiquement vers vos outils de travail. Trois familles d'intégrations couvrent l'essentiel des flux documentaires, communication et automatisation.
</p>
</div>
{# Network diagram — SVG with native <animateMotion> for particle flow.
Layout : DictIA center-top, 3 hubs (Documents | Communication | Automatisation), 9 tools below. #}
<div class="relative max-w-4xl mx-auto">
<svg
viewBox="0 0 900 460"
preserveAspectRatio="xMidYMid meet"
class="hub-network-svg w-full h-auto"
role="img"
aria-labelledby="hub-svg-title hub-svg-desc"
>
<title id="hub-svg-title">Diagramme du réseau d'intégrations DictIA</title>
<desc id="hub-svg-desc">DictIA au centre, connecté à trois familles : Documents (Word, Google Docs, Obsidian), Communication (Outlook, Teams, Notion) et Automatisation (Zapier, Make, n8n).</desc>
<defs>
{# Bezier path definitions — DictIA top → 3 hubs (mid) #}
<path id="hub-path-docs" d="M450,54 C450,127 168,127 168,200" />
<path id="hub-path-comm" d="M450,54 C450,127 450,127 450,200" />
<path id="hub-path-auto" d="M450,54 C450,127 732,127 732,200" />
{# Bezier paths — Hub → Tool #}
{# Documents hub (168, 200) → Word(62,390), Google(175,390), Obsidian(282,390) #}
<path id="hub-path-word" d="M168,216 C168,303 62,303 62,390" />
<path id="hub-path-google" d="M168,216 C168,303 175,303 175,390" />
<path id="hub-path-obsidian" d="M168,216 C168,303 282,303 282,390" />
{# Communication hub (450, 200) → Outlook(358,390), Teams(450,390), Notion(542,390) #}
<path id="hub-path-outlook" d="M450,216 C450,303 358,303 358,390" />
<path id="hub-path-teams" d="M450,216 C450,303 450,303 450,390" />
<path id="hub-path-notion" d="M450,216 C450,303 542,303 542,390" />
{# Automatisation hub (732, 200) → Zapier(618,390), Make(722,390), n8n(828,390) #}
<path id="hub-path-zapier" d="M732,216 C732,303 618,303 618,390" />
<path id="hub-path-make" d="M732,216 C732,303 722,303 722,390" />
<path id="hub-path-n8n" d="M732,216 C732,303 828,303 828,390" />
</defs>
{# Render bezier connections (visible strokes) — DictIA → Hubs (thick), Hub → Tools (thin) #}
{# DictIA → Hubs #}
<use href="#hub-path-docs" fill="none" stroke="#0062ff" stroke-width="1.5" opacity="0.55" />
<use href="#hub-path-comm" fill="none" stroke="#00bdd8" stroke-width="1.5" opacity="0.55" />
<use href="#hub-path-auto" fill="none" stroke="#8b5cf6" stroke-width="1.5" opacity="0.55" />
{# Hub → Tool (lighter) #}
<use href="#hub-path-word" fill="none" stroke="#0062ff" stroke-width="1" opacity="0.30" />
<use href="#hub-path-google" fill="none" stroke="#0062ff" stroke-width="1" opacity="0.30" />
<use href="#hub-path-obsidian" fill="none" stroke="#0062ff" stroke-width="1" opacity="0.30" />
<use href="#hub-path-outlook" fill="none" stroke="#00bdd8" stroke-width="1" opacity="0.30" />
<use href="#hub-path-teams" fill="none" stroke="#00bdd8" stroke-width="1" opacity="0.30" />
<use href="#hub-path-notion" fill="none" stroke="#00bdd8" stroke-width="1" opacity="0.30" />
<use href="#hub-path-zapier" fill="none" stroke="#8b5cf6" stroke-width="1" opacity="0.30" />
<use href="#hub-path-make" fill="none" stroke="#8b5cf6" stroke-width="1" opacity="0.30" />
<use href="#hub-path-n8n" fill="none" stroke="#8b5cf6" stroke-width="1" opacity="0.30" />
{# Animated particles — DictIA → Hubs (bigger, dual particles) #}
{% for hub in [
('docs', '#0062ff', '0s', '2.4s'),
('comm', '#00bdd8', '0.8s', '2.4s'),
('auto', '#8b5cf6', '1.6s', '2.4s')
] %}
<circle r="3.2" fill="{{ hub[1] }}" opacity="0.95">
<animateMotion dur="{{ hub[3] }}" repeatCount="indefinite" begin="{{ hub[2] }}">
<mpath href="#hub-path-{{ hub[0] }}" />
</animateMotion>
</circle>
<circle r="1.8" fill="{{ hub[1] }}" opacity="0.6">
<animateMotion dur="{{ hub[3] }}" repeatCount="indefinite" begin="{{ hub[2] }}s + 1.2s">
<mpath href="#hub-path-{{ hub[0] }}" />
</animateMotion>
</circle>
{% endfor %}
{# Animated particles — Hub → Tool (smaller, fast) #}
{% for tool in [
('word', '#0062ff', '0s'),
('google', '#0062ff', '0.4s'),
('obsidian', '#0062ff', '0.8s'),
('outlook', '#00bdd8', '0.2s'),
('teams', '#00bdd8', '0.6s'),
('notion', '#00bdd8', '1.0s'),
('zapier', '#8b5cf6', '0.1s'),
('make', '#8b5cf6', '0.5s'),
('n8n', '#8b5cf6', '0.9s')
] %}
<circle r="2.2" fill="{{ tool[1] }}" opacity="0.75">
<animateMotion dur="2s" repeatCount="indefinite" begin="{{ tool[2] }}">
<mpath href="#hub-path-{{ tool[0] }}" />
</animateMotion>
</circle>
{% endfor %}
</svg>
{# DictIA central node — absolute positioned over the SVG (z-10) #}
<div class="absolute pointer-events-none" style="left: 50%; top: 11.7%; transform: translate(-50%, -50%); z-index: 10;" aria-hidden="true">
<div class="relative w-[68px] h-[68px]">
{# Pulsing rings #}
<span class="hub-ring absolute inset-0 rounded-full border-2 border-brand-b2/40"></span>
<span class="hub-ring hub-ring-2 absolute inset-0 rounded-full border-2 border-brand-b2/40"></span>
<span class="hub-ring hub-ring-3 absolute inset-0 rounded-full border-2 border-brand-b2/40"></span>
{# Core orb #}
<span class="hub-core absolute inset-0 rounded-full grad-bg flex items-center justify-center">
<span class="font-black text-white text-[13px] tracking-tight">DictIA</span>
</span>
</div>
</div>
{# Hub labels overlay — positioned to match SVG hub coords (200/460 = 43.5% top, 168/900=18.7% / 450/900=50% / 732/900=81.3% left) #}
{% set hub_data = [
('Documents', 'Word · Google Docs · Obsidian', '18.7%', 'rgba(0,98,255,0.10)', 'rgba(0,98,255,0.30)', 'text-blue-300'),
('Communication', 'Outlook · Teams · Notion', '50%', 'rgba(0,189,216,0.10)', 'rgba(0,189,216,0.30)', 'text-cyan-300'),
('Automatisation', 'Zapier · Make · n8n', '81.3%', 'rgba(139,92,246,0.10)', 'rgba(139,92,246,0.30)', 'text-purple-300')
] %}
{% for name, sub, leftpos, bgcol, brdcol, txtcol in hub_data %}
<div
class="absolute pointer-events-none px-3 py-2 rounded-full backdrop-blur-sm whitespace-nowrap"
style="left: {{ leftpos }}; top: 43.5%; transform: translate(-50%, -50%); background: {{ bgcol }}; border: 1px solid {{ brdcol }};"
aria-hidden="true"
>
<p class="text-[11px] font-bold text-white leading-none">{{ name }}</p>
<p class="text-[9px] {{ txtcol }} leading-none mt-0.5 opacity-80">{{ sub }}</p>
</div>
{% endfor %}
</div>
{# Tool list — text fallback below the diagram (covers screen readers + reduces visual noise on mobile) #}
<div class="mt-12 grid sm:grid-cols-3 gap-6 max-w-3xl mx-auto" role="list" aria-label="Outils intégrés à DictIA">
{% for col_label, tools, accent in [
('Documents', [('Word', 'Export .docx instantané'), ('Google Docs', 'Sync Google Workspace'), ('Obsidian', 'Notes Markdown locales')], 'text-brand-b1'),
('Communication', [('Outlook', 'Envoi email automatique'), ('Teams', 'Canal Teams dédié'), ('Notion', 'Page Notion structurée')], 'text-brand-b2'),
('Automatisation', [('Zapier', '5&nbsp;000+ apps connectées'), ('Make', 'Scénarios visuels no-code'), ('n8n', 'Workflows open-source')], 'text-purple-400')
] %}
<div role="listitem" class="bg-white/[0.03] border border-white/[0.06] p-5 rounded">
<p class="eyebrow {{ accent }} mb-3">{{ col_label | upper }}</p>
<ul class="space-y-2">
{% for tool_name, tool_desc in tools %}
<li class="text-sm">
<span class="font-semibold text-white">{{ tool_name }}</span>
<span class="text-white/70"> — {{ tool_desc | safe }}</span>
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
</div>
<p class="text-center text-sm text-white/70 mt-8 max-w-2xl mx-auto">
Architecture ouverte. Tout export est aussi disponible en DOCX, SRT, VTT, JSON, TXT et MD — formats standards consommables par n'importe quel logiciel métier (gestion de dossiers, paie, CRM).
</p>
</div>
</section>
{# ===== PRICING ===== #}
<section class="bg-brand-bg py-20" id="tarifs" aria-labelledby="pricing-title">
<div class="max-w-[1200px] mx-auto px-6">