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

@@ -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">