refactor(marketing): Cycle cinematic PRO — palette brand uniformisée + USA map + Quebec outline + halo brand

Round 5 cinématique de la section "Trois options. Une seule est conforme." :
- Palette brand uniformisée (blue/cyan/fuchsia) — col 3 passe de emerald/green à brand-b1+brand-b2 + accents fuchsia
- Connecting horizontal beam progressive 0→33→66→100% entre les 3 colonnes (gris→rouge→cyan)
- Spotlight active column (opacity 0.65 inactif, 1 actif + scale 1.02)
- Col 1 : stack papiers stagger reveal 180ms + horloge 2.5s + counter \$315
- Col 2 : USA map silhouette subtle bg + server rack 3-leds + 12 paquets data .wav/.aac flying vers top-right + screen shake + flash red + chevrons + sound icon pulse
- Col 3 : Quebec province outline subtle + mini logo DictIA + halo multi-couches blue+cyan+fuchsia + drawn ring SVG (fuchsia accent) + shield-with-microphone + checkmark cyan + badge Loi 25 conforme gradient brand + big 173 \$ en grad-text
- Section Économies : counters en grad-text + save chips "+économies" + chips contextuels + icônes distinctives (loutre/Teams T/sténographe humain)
- Phase delays cinématiques 400/1100/2000/3100ms + cubic-bezier overshoot
- Vignette ambiante + grid pattern 40×40 + orbes décoratifs blur

WCAG : aria-labels préservés + prefers-reduced-motion désactive radar/particles/screen-shake/halo/beam.
Mobile : leak particles + orbes + halo heavy + screen shake désactivés via @media (max-width: 768px).
Performance : will-change GPU hints sur stamp/halo/leak.

Tests cycle round 4 (3) toujours passants. 65/68 tests pass (3 failures pré-existantes : /blog nav).
HTTP 200 vérifié sur http://127.0.0.1:8899/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Allison
2026-04-28 17:07:59 -04:00
parent 575db5e342
commit e8c7e5cd43
2 changed files with 541 additions and 218 deletions

View File

@@ -411,10 +411,69 @@
{# ===== CYCLE — "Trois options. Une seule est conforme." ===== #}
{# Source canonique : InnovA-AI/Website-Sanity/components/sections/dictai-cycle.tsx
Round 4 cinématique : phase reveal séquentiel + horloge accélérée + prix counter + stamp impact NON CONFORME
+ checkmark draw + glow vert + connecting line dash flow + section "Économies annuelles" avec 3 counters animés #}
Round 5 cinématique PRO : palette brand uniformisée (blue/cyan/fuchsia, zéro vert)
+ connecting beam progressive 0→33→66→100 + spotlight active column + USA map silhouette col 2
+ Quebec outline col 3 + halo multi-couches blue+cyan + screen shake + 12 paquets data flying
+ grad-text counters + chips contextuels + phase delays cinématiques 400/1100/2000/3100ms #}
<style>
/* Cycle pulse rings — nœud source "Réunion en cours" */
/* === ROUND 5 — Cycle background ambiance (vignette + grid + orbes) === */
.cycle-section-bg {
background:
radial-gradient(ellipse 60% 40% at 12% 0%, rgba(37,99,235,0.045) 0%, transparent 70%),
radial-gradient(ellipse 60% 40% at 88% 100%, rgba(6,182,212,0.045) 0%, transparent 70%);
}
.cycle-grid-bg {
background-image:
linear-gradient(rgba(11,15,26,0.025) 1px, transparent 1px),
linear-gradient(90deg, rgba(11,15,26,0.025) 1px, transparent 1px);
background-size: 40px 40px;
}
@keyframes cycle-orb-float {
0%, 100% { transform: translate(0,0) scale(1); }
50% { transform: translate(8px,-12px) scale(1.04); }
}
.cycle-orb { animation: cycle-orb-float 8s ease-in-out infinite; }
.cycle-orb-2 { animation-delay: 2.5s; animation-duration: 10s; }
/* === ROUND 5 — Connecting horizontal beam progressive 0→33→66→100 === */
.cycle-beam {
height: 2px;
background: linear-gradient(90deg,
rgba(148,163,184,0.55) 0%,
rgba(148,163,184,0.55) 33%,
rgba(239,68,68,0.55) 33%,
rgba(239,68,68,0.55) 66%,
rgba(6,182,212,0.85) 66%,
rgba(37,99,235,0.85) 100%);
width: var(--beam, 0%);
transition: width 800ms cubic-bezier(0.34, 1.56, 0.64, 1);
box-shadow: 0 0 8px rgba(37,99,235,0.25);
}
/* Loading dot qui suit la beam */
@keyframes cycle-beam-dot {
0% { left: 4%; background: rgba(148,163,184,1); }
33% { left: 33%; background: rgba(148,163,184,1); }
50% { left: 50%; background: rgba(239,68,68,1); }
66% { left: 66%; background: rgba(239,68,68,1); }
100% { left: 96%; background: rgba(6,182,212,1); }
}
.cycle-beam-dot {
width: 6px; height: 6px; border-radius: 9999px;
top: 50%; transform: translate(-50%,-50%);
box-shadow: 0 0 10px currentColor;
animation: cycle-beam-dot 4s ease-in-out 0.4s 1 forwards;
}
/* === ROUND 5 — Spotlight active column === */
.cycle-col-card {
transition: opacity 600ms cubic-bezier(0.34, 1.56, 0.64, 1),
transform 600ms cubic-bezier(0.34, 1.56, 0.64, 1);
opacity: 0.65;
}
.cycle-col-card.is-active { opacity: 1; transform: scale(1.015); }
.cycle-col-card.is-revealed { opacity: 1; }
/* === Cycle pulse rings — nœud source "Réunion en cours" === */
@keyframes cycle-pulse-ring {
0% { transform: scale(1); opacity: 0.5; }
100% { transform: scale(2.1); opacity: 0; }
@@ -430,102 +489,182 @@
}
.cycle-live-dot { animation: cycle-live-dot 1.4s ease-in-out infinite; }
/* Cycle col 3 (DictIA) — bordure lumineuse pulsante VERT (round 4 : conformité) */
/* === ROUND 5 — Cycle col 3 (DictIA) — halo multi-couches blue+cyan (PALETTE BRAND) === */
@keyframes cycle-conforme-glow {
0%, 100% { box-shadow: 0 0 0 0 rgba(34,197,94,0.18), 0 0 0 0 rgba(6,182,212,0.10); }
50% { box-shadow: 0 0 32px 2px rgba(34,197,94,0.32), 0 0 60px 0 rgba(6,182,212,0.14); }
0%, 100% { box-shadow: 0 0 0 0 rgba(37,99,235,0.20), 0 0 0 0 rgba(6,182,212,0.12), 0 0 0 0 rgba(192,38,211,0.06); }
50% { box-shadow: 0 0 36px 3px rgba(37,99,235,0.45), 0 0 64px 6px rgba(6,182,212,0.20), 0 0 96px 12px rgba(192,38,211,0.10); }
}
.cycle-card-dictia.is-visible { animation: cycle-conforme-glow 3.4s ease-in-out infinite; }
.cycle-card-dictia.is-visible { animation: cycle-conforme-glow 3.4s ease-in-out infinite; will-change: box-shadow; }
.cycle-card-dictia { transition: transform 400ms ease-out; will-change: transform; }
.cycle-card-dictia.is-visible:hover { transform: translateY(-3px) scale(1.03); }
/* Cycle col 3 — anneaux concentriques autour du Lock */
/* Cycle col 3 — anneaux concentriques autour du Shield (multi-couches drawn) */
@keyframes cycle-ring-out {
0%, 100% { transform: scale(1); opacity: 0.45; }
50% { transform: scale(1.07); opacity: 0.18; }
0%, 100% { transform: scale(1); opacity: 0.55; }
50% { transform: scale(1.08); opacity: 0.20; }
}
.cycle-ring-outer.is-visible { animation: cycle-ring-out 3.8s ease-in-out infinite; }
.cycle-ring-inner.is-visible { animation: cycle-ring-out 2.9s ease-in-out infinite 0.5s; }
/* Cycle SVG lignes de connexion — drawn via stroke-dashoffset, dash flow continu (round 4) */
/* Ring SVG drawn line cycling (round 5) */
@keyframes cycle-ring-draw {
0% { stroke-dashoffset: 220; }
100% { stroke-dashoffset: 0; }
}
.cycle-ring-draw-svg circle {
stroke-dasharray: 220;
stroke-dashoffset: 220;
transform-origin: center;
transform: rotate(-90deg);
}
.cycle-ring-draw-svg.is-visible circle { animation: cycle-ring-draw 4s linear infinite; }
/* Cycle SVG lignes de connexion — drawn via stroke-dashoffset, dash flow continu */
.cycle-line { stroke-dasharray: 1.2 0.9; stroke-dashoffset: 50; transition: stroke-dashoffset 600ms ease-out; }
.cycle-line.is-visible { stroke-dashoffset: 0; }
@keyframes cycle-dash-flow { to { stroke-dashoffset: -42; } }
.cycle-line.is-visible.cycle-line-flow { animation: cycle-dash-flow 12s linear infinite; }
/* Cycle phase reveal */
.cycle-reveal { opacity: 0; transform: translateX(14px); transition: opacity 380ms ease-out, transform 380ms ease-out; }
.cycle-reveal { opacity: 0; transform: translateX(14px); transition: opacity 380ms cubic-bezier(0.34, 1.56, 0.64, 1), transform 380ms cubic-bezier(0.34, 1.56, 0.64, 1); }
.cycle-reveal.is-visible { opacity: 1; transform: translateX(0); }
.cycle-reveal-up { opacity: 0; transform: translateY(10px); transition: opacity 500ms ease-out, transform 500ms ease-out; }
.cycle-reveal-up { opacity: 0; transform: translateY(10px); transition: opacity 500ms cubic-bezier(0.34, 1.56, 0.64, 1), transform 500ms cubic-bezier(0.34, 1.56, 0.64, 1); }
.cycle-reveal-up.is-visible { opacity: 1; transform: translateY(0); }
/* Cycle horloge rotation (col 1)accélérée 1 tour / 3s pour rendre "lent" PALPABLE */
/* === ROUND 5 — Col 1 — Stack papiers stagger reveal === */
@keyframes cycle-paper-in {
0% { opacity: 0; transform: translateY(8px) rotate(var(--rot, 0deg)); }
100% { opacity: 1; transform: translateY(0) rotate(var(--rot, 0deg)); }
}
.cycle-paper-stack.is-active .cycle-paper {
animation: cycle-paper-in 400ms ease-out forwards;
opacity: 0;
}
/* Cycle horloge rotation (col 1) — accélérée 1 tour / 2.5s + motion blur subtle */
@keyframes cycle-clock-spin { to { transform: rotate(360deg); } }
.cycle-clock { animation: cycle-clock-spin 3s linear infinite; transform-origin: center; display: inline-block; }
.cycle-clock { animation: cycle-clock-spin 2.5s linear infinite; transform-origin: center; display: inline-block; will-change: transform; }
/* Cycle col 2 — fuite particules rouges (continu, plus dense round 4) */
/* === ROUND 5 — Col 2 — Data leak particles AMÉLIORÉES (12 paquets vers USA top-right) === */
@keyframes cycle-leak {
0% { transform: translate(0,0) scale(1); opacity: 0; }
20% { opacity: 0.85; }
100% { transform: translate(var(--lx,18px), var(--ly,-22px)) scale(0.35); opacity: 0; }
0% { transform: translate(0,0) rotate(var(--rot, 0deg)) scale(0.85); opacity: 0; }
18% { opacity: 0.9; }
100% { transform: translate(var(--lx, 60px), var(--ly, -80px)) rotate(var(--rot, 0deg)) scale(0.35); opacity: 0; }
}
.cycle-leak-particle { animation: cycle-leak 1.8s ease-out infinite; }
.cycle-leak-particle { animation: cycle-leak 2.2s ease-out infinite; will-change: transform, opacity; }
/* Cycle col 2 — STAMP NON CONFORME impact (round 4 : tampon huissier qui claque) */
/* Cycle col 2 — server rack LED pulse */
@keyframes cycle-led-pulse {
0%, 100% { opacity: 0.4; }
50% { opacity: 1; box-shadow: 0 0 6px currentColor; }
}
.cycle-led { animation: cycle-led-pulse 1.2s ease-in-out infinite; }
.cycle-led-2 { animation-delay: 0.3s; }
.cycle-led-3 { animation-delay: 0.6s; }
/* Warning chevrons slide */
@keyframes cycle-chev-slide {
0%, 100% { transform: translateX(0); opacity: 0.4; }
50% { transform: translateX(-6px); opacity: 1; }
}
.cycle-chev { animation: cycle-chev-slide 1.4s ease-in-out infinite; }
.cycle-chev-2 { animation-delay: 0.2s; }
.cycle-chev-3 { animation-delay: 0.4s; }
/* Cycle col 2 — STAMP NON CONFORME impact (overshoot + screen shake + flash red) */
@keyframes cycle-stamp-impact {
0% { transform: translate(-50%,-50%) rotate(-22deg) scale(2.4); opacity: 0; filter: blur(2px); }
55% { transform: translate(-50%,-50%) rotate(-7deg) scale(0.92); opacity: 1; filter: blur(0); }
70% { transform: translate(-50%,-50%) rotate(-3deg) scale(1.06); }
100% { transform: translate(-50%,-50%) rotate(-3deg) scale(1); opacity: 1; }
0% { transform: translate(-50%,-50%) rotate(-25deg) scale(3); opacity: 0; filter: blur(3px); }
55% { transform: translate(-50%,-50%) rotate(-9deg) scale(0.90); opacity: 1; filter: blur(0); }
70% { transform: translate(-50%,-50%) rotate(-6deg) scale(1.08); }
100% { transform: translate(-50%,-50%) rotate(-8deg) scale(1); opacity: 1; }
}
.cycle-stamp.is-visible { animation: cycle-stamp-impact 720ms cubic-bezier(0.5,1.6,0.4,1) forwards; }
.cycle-stamp.is-visible { animation: cycle-stamp-impact 700ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards; will-change: transform, opacity; }
/* Cycle col 2 — flash background rouge à l'impact stamp */
/* Screen shake on stamp impact */
@keyframes cycle-shake {
0%, 100% { transform: translate(0,0); }
20% { transform: translate(-3px, 2px); }
40% { transform: translate(4px, -2px); }
60% { transform: translate(-2px, 3px); }
80% { transform: translate(3px, -1px); }
}
.cycle-col-shake.is-visible { animation: cycle-shake 320ms ease-out 480ms 1; }
/* Cycle col 2 — flash background rouge à l'impact stamp (amplifié) */
@keyframes cycle-flash-red {
0% { background-color: rgba(239,68,68,0); }
35% { background-color: rgba(239,68,68,0.18); }
30% { background-color: rgba(239,68,68,0.22); }
100% { background-color: rgba(239,68,68,0); }
}
.cycle-col-flash.is-visible { animation: cycle-flash-red 700ms ease-out 350ms forwards; }
.cycle-col-flash.is-visible { animation: cycle-flash-red 700ms ease-out 480ms forwards; }
/* Cycle col 3checkmark draw (stroke-dashoffset) */
.cycle-check-svg path { stroke-dasharray: 24; stroke-dashoffset: 24; transition: stroke-dashoffset 350ms ease-out 200ms; }
/* Cycle col 2sound icon pulse */
@keyframes cycle-alert-pulse {
0%, 100% { opacity: 0.7; transform: scale(1); }
50% { opacity: 1; transform: scale(1.2); }
}
.cycle-alert-icon { animation: cycle-alert-pulse 0.9s ease-in-out infinite; }
/* === ROUND 5 — Col 3 — checkmark draw CYAN (au lieu de vert) === */
.cycle-check-svg path { stroke-dasharray: 24; stroke-dashoffset: 24; transition: stroke-dashoffset 400ms ease-out 220ms; stroke: #06b6d4; stroke-width: 3.5; }
.cycle-check-svg.is-visible path { stroke-dashoffset: 0; }
/* Cycle col 3 — badge "Loi 25 conforme" pulse subtil */
/* === ROUND 5 — Col 3 — badge "Loi 25 conforme" pulse BLUE+CYAN (palette brand) === */
@keyframes cycle-badge-pulse {
0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(34,197,94,0.4); }
50% { transform: scale(1.04); box-shadow: 0 0 0 6px rgba(34,197,94,0); }
0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(37,99,235,0.45); }
50% { transform: scale(1.04); box-shadow: 0 0 0 7px rgba(37,99,235,0); }
}
.cycle-conforme-badge.is-visible { animation: cycle-badge-pulse 2.4s ease-in-out infinite 600ms; }
/* Cycle "Économies annuelles" cards — hover lift + glow */
/* === ROUND 5 — Cycle "Économies annuelles" cards — hover lift + glow BRAND BLUE === */
.cycle-savings-card {
transition: transform 220ms ease-out, box-shadow 220ms ease-out, border-color 220ms ease-out;
transition: transform 240ms cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 240ms ease-out, border-color 240ms ease-out;
}
.cycle-savings-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px -8px rgba(34,197,94,0.35), 0 4px 12px -4px rgba(11,15,26,0.08);
border-color: rgba(34,197,94,0.45);
transform: translateY(-3px);
box-shadow: 0 12px 28px -8px rgba(37,99,235,0.30), 0 6px 14px -4px rgba(11,15,26,0.08);
border-color: rgba(37,99,235,0.45);
}
/* Save chip subtle pulse */
@keyframes cycle-save-chip {
0%, 100% { opacity: 0.85; }
50% { opacity: 1; }
}
.cycle-save-chip { animation: cycle-save-chip 2.5s ease-in-out infinite; }
/* Mobile — désactiver fuites particules + glow heavy */
/* === ROUND 5 — Phase 4 zoom-out subtle (suggère "all options revealed") === */
.cycle-grid-zoom { transition: transform 700ms ease-out; transform: scale(1.02); }
.cycle-grid-zoom.is-revealed { transform: scale(1); }
/* Mobile — désactiver fuites particules + glow heavy + screen shake */
@media (max-width: 768px) {
.cycle-leak-particle { display: none; }
.cycle-leak-particle, .cycle-orb, .cycle-orb-2 { display: none; }
.cycle-card-dictia.is-visible { animation: none; }
.cycle-col-shake.is-visible, .cycle-col-flash.is-visible { animation: none; }
.cycle-ring-draw-svg.is-visible circle { animation: none; }
.cycle-col-card { opacity: 1 !important; transform: none !important; }
}
/* Reduced motion — TOUT figé */
/* Reduced motion — TOUT figé (mode statique) */
@media (prefers-reduced-motion: reduce) {
.cycle-pulse, .cycle-card-dictia, .cycle-ring-outer, .cycle-ring-inner,
.cycle-clock, .cycle-leak-particle, .cycle-live-dot, .cycle-conforme-badge,
.cycle-line.cycle-line-flow, .cycle-stamp, .cycle-col-flash { animation: none !important; }
.cycle-line.cycle-line-flow, .cycle-stamp, .cycle-col-flash, .cycle-col-shake,
.cycle-led, .cycle-chev, .cycle-alert-icon, .cycle-orb, .cycle-orb-2,
.cycle-save-chip, .cycle-paper-stack.is-active .cycle-paper,
.cycle-ring-draw-svg.is-visible circle, .cycle-beam-dot { animation: none !important; }
.cycle-paper-stack .cycle-paper { opacity: 1 !important; }
.cycle-reveal, .cycle-reveal-up { opacity: 1 !important; transform: none !important; }
.cycle-line { stroke-dashoffset: 0 !important; }
.cycle-check-svg path { stroke-dashoffset: 0 !important; }
.cycle-savings-card { transition: none !important; }
.cycle-col-card { opacity: 1 !important; transform: none !important; transition: none !important; }
.cycle-beam { width: 100% !important; transition: none !important; }
.cycle-grid-zoom { transform: none !important; transition: none !important; }
}
</style>
<section
class="bg-white py-20 border-y border-brand-border relative overflow-hidden"
class="cycle-section-bg bg-white py-20 border-y border-brand-border relative overflow-hidden"
aria-labelledby="cycle-title"
x-data="{
phase: 0,
@@ -544,24 +683,32 @@
};
requestAnimationFrame(tick);
},
fmt(n) { return n.toLocaleString('fr-CA').replace(//g, ' '); }
fmt(n) { return n.toLocaleString('fr-CA').replace(//g, ' '); },
/* Round 5 — beam progressive % par phase */
get beamPct() {
if (this.phase >= 4) return 100;
if (this.phase >= 3) return 66;
if (this.phase >= 2) return 33;
return 0;
}
}"
x-init="
observer = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting && phase === 0) {
setTimeout(() => { phase = 1; }, 250);
/* Round 5 — phase delays cinématiques 400/1100/2000/3100ms */
setTimeout(() => { phase = 1; }, 400);
setTimeout(() => { phase = 2; countTo('priceHumain', 315, 1400); }, 1100);
setTimeout(() => phase = 3, 2200);
setTimeout(() => phase = 3, 2000);
setTimeout(() => {
phase = 4;
/* Savings counters fire 600ms after col 3 reveal (round 4) */
/* Savings counters fire 700ms after col 3 reveal (round 5) */
setTimeout(() => {
countTo('sav1', 3924, 1500);
countTo('sav2', 6924, 1500);
countTo('sav3', 2004, 1500);
}, 700);
}, 3000);
}, 3100);
observer.disconnect();
}
});
@@ -569,10 +716,17 @@
observer.observe($el);
"
>
{# Subtle dot-grid bg #}
<div class="absolute inset-0 pointer-events-none" aria-hidden="true"
{# Round 5 — Subtle grid pattern bg + dot grid overlay #}
<div class="cycle-grid-bg absolute inset-0 pointer-events-none" aria-hidden="true"></div>
<div class="absolute inset-0 pointer-events-none opacity-50" aria-hidden="true"
style="background-image: radial-gradient(circle, rgba(11,15,26,0.04) 1px, transparent 1px); background-size: 28px 28px;"></div>
{# Round 5 — Decorative orbs ambiance #}
<div class="cycle-orb absolute -top-20 -left-20 w-[360px] h-[360px] rounded-full pointer-events-none" aria-hidden="true"
style="background: radial-gradient(circle, rgba(37,99,235,0.10) 0%, transparent 65%); filter: blur(40px);"></div>
<div class="cycle-orb cycle-orb-2 absolute -bottom-24 -right-24 w-[420px] h-[420px] rounded-full pointer-events-none" aria-hidden="true"
style="background: radial-gradient(circle, rgba(6,182,212,0.08) 0%, transparent 65%); filter: blur(50px);"></div>
<div class="relative max-w-[1200px] mx-auto px-6">
<div class="max-w-2xl mb-10">
<p class="eyebrow text-amber-600 mb-4 inline-flex items-center gap-1.5">
@@ -609,7 +763,7 @@
</div>
</div>
{# Lignes de connexion SVG — de la source vers les 3 colonnes (round 4 : dash flow continu) #}
{# Lignes de connexion SVG — de la source vers les 3 colonnes #}
<div class="relative h-10">
<svg class="w-full h-full" viewBox="0 0 100 10" preserveAspectRatio="none" aria-hidden="true">
<line class="cycle-line cycle-line-flow" :class="phase >= 1 ? 'is-visible' : ''"
@@ -621,27 +775,43 @@
style="transition-delay: 80ms; animation-delay: 0.4s;" />
<line class="cycle-line cycle-line-flow" :class="phase >= 1 ? 'is-visible' : ''"
x1="50" y1="0" x2="86" y2="10"
stroke="rgba(34,197,94,0.6)" stroke-width="0.3"
stroke="rgba(6,182,212,0.7)" stroke-width="0.3"
style="transition-delay: 160ms; animation-delay: 0.8s;" />
</svg>
</div>
{# Grille 3 colonnes — équivalence du grid-cols-[2fr_2fr_3fr] desktop #}
<div class="grid grid-cols-1 md:grid-cols-[2fr_2fr_3fr] gap-3 items-stretch">
{# Round 5 — Connecting horizontal beam progressive 0->33->66->100 #}
<div class="relative h-3 mb-4" aria-hidden="true">
<div class="absolute inset-x-0 top-1/2 -translate-y-1/2 h-[2px] bg-brand-border/60"></div>
<div class="cycle-beam absolute top-1/2 left-0 -translate-y-1/2 rounded-full"
:style="`--beam: ${beamPct}%`"></div>
<template x-if="phase >= 1">
<div class="cycle-beam-dot absolute"></div>
</template>
</div>
{# COL 1 — Retranscription humaine #}
<div class="cycle-reveal-up flex flex-col rounded border border-brand-border bg-brand-bg overflow-hidden"
:class="phase >= 2 ? 'is-visible' : ''">
<div class="px-5 py-3 border-b border-brand-border">
{# Grille 3 colonnes — équivalence du grid-cols-[2fr_2fr_3fr] desktop, avec spotlight active #}
<div class="cycle-grid-zoom grid grid-cols-1 md:grid-cols-[2fr_2fr_3fr] gap-3 items-stretch"
:class="phase >= 4 ? 'is-revealed' : ''">
{# COL 1 — Retranscription humaine (sténographe) #}
<div class="cycle-col-card cycle-reveal-up flex flex-col rounded border border-brand-border bg-brand-bg overflow-hidden relative"
:class="{ 'is-visible': phase >= 2, 'is-revealed': phase >= 4, 'is-active': phase === 2 }">
{# Subtle "cahier" lines bg #}
<div class="absolute inset-0 pointer-events-none opacity-[0.04]" aria-hidden="true"
style="background-image: linear-gradient(rgba(11,15,26,1) 1px, transparent 1px); background-size: 100% 18px;"></div>
<div class="relative px-5 py-3 border-b border-brand-border">
<div class="font-mono text-[10px] tracking-[0.28em] uppercase text-brand-navy/40 mb-0.5">01</div>
<div class="font-bold text-sm text-brand-navy/55">Retranscription humaine</div>
</div>
<div class="flex-1 flex flex-col items-center justify-center px-5 py-7 gap-6">
{# Stack de papiers #}
<div class="relative w-[100px] h-[90px]" aria-hidden="true">
<div class="relative flex-1 flex flex-col items-center justify-center px-5 py-7 gap-6">
{# Stack de papiers (round 5 : stagger reveal stagger 180ms) #}
<div class="cycle-paper-stack relative w-[100px] h-[90px]"
:class="phase >= 2 ? 'is-active' : ''"
aria-hidden="true">
{% for offset in [0,1,2,3,4] %}
<div class="absolute left-0 right-0 h-[17px] rounded-sm flex items-center px-2 gap-1.5 bg-white border border-brand-border"
style="bottom: {{ offset * 15 }}px; transform: rotate({{ '1deg' if offset is even else '-1deg' }});">
<div class="cycle-paper absolute left-0 right-0 h-[17px] rounded-sm flex items-center px-2 gap-1.5 bg-white border border-brand-border shadow-sm"
style="bottom: {{ offset * 15 }}px; --rot: {{ '1deg' if offset is even else '-1deg' }}; transform: rotate({{ '1deg' if offset is even else '-1deg' }}); animation-delay: {{ offset * 180 }}ms;">
<div class="w-2.5 h-1.5 rounded-sm bg-brand-navy/15 shrink-0"></div>
<div class="flex-1 h-[1.5px] rounded-full bg-brand-navy/10"></div>
</div>
@@ -661,7 +831,7 @@
</div>
</div>
</div>
<div class="px-5 pb-5 pt-4 border-t border-brand-border">
<div class="relative px-5 pb-5 pt-4 border-t border-brand-border">
<div class="flex items-baseline gap-1.5 mb-2">
<span class="font-black text-3xl leading-none text-brand-navy/65 tabular-nums" x-text="priceHumain">315</span>
<span class="text-xs text-brand-navy/45">$&nbsp;/&nbsp;réunion</span>
@@ -673,41 +843,72 @@
</div>
</div>
{# COL 2 — IA cloud américaine #}
<div class="cycle-reveal-up cycle-col-flash relative flex flex-col rounded border border-red-200 bg-red-50/30 overflow-hidden"
:class="phase >= 2 ? 'is-visible' : ''"
{# COL 2 — IA cloud américaine (DRAMATIC : USA map + 12 paquets + screen shake) #}
<div class="cycle-col-card cycle-reveal-up cycle-col-flash cycle-col-shake relative flex flex-col rounded border border-red-200 bg-red-50/30 overflow-hidden"
:class="{ 'is-visible': phase >= 3, 'is-revealed': phase >= 4, 'is-active': phase === 3 }"
style="transition-delay: 120ms;">
{# Overlay légal NON CONFORME (phase 3) — round 4 : STAMP huissier qui claque #}
{# Round 5 — USA map silhouette subtle background #}
<svg class="absolute pointer-events-none" aria-hidden="true"
style="top: 22%; right: 8%; width: 110px; height: 70px; opacity: 0.55;"
viewBox="0 0 110 70" preserveAspectRatio="xMidYMid meet">
<path d="M5,25 L8,18 L18,15 L32,12 L48,10 L65,11 L82,14 L92,18 L102,22 L105,28 L100,35 L95,42 L88,48 L78,52 L68,55 L58,57 L48,58 L38,57 L28,55 L18,52 L10,45 L6,38 Z"
fill="rgba(239,68,68,0.05)" stroke="rgba(239,68,68,0.20)" stroke-width="0.8"/>
<circle cx="78" cy="32" r="2.2" fill="rgba(239,68,68,0.55)"/>
<circle cx="78" cy="32" r="4.5" fill="none" stroke="rgba(239,68,68,0.35)" stroke-width="0.6"/>
</svg>
{# Overlay légal NON CONFORME (phase 3) — STAMP huissier amplifié round 5 #}
<div class="absolute inset-0 z-30 flex flex-col items-center justify-center pointer-events-none"
:class="phase >= 3 ? 'opacity-100' : 'opacity-0'"
style="transition: opacity 220ms ease-out; backdrop-filter: blur(6px); background: rgba(255,255,255,0.78);">
<div class="cycle-stamp absolute top-1/2 left-1/2 flex flex-col items-center gap-3 px-6 py-5 rounded bg-white border-[3px] border-red-500 shadow-[0_8px_30px_-6px_rgba(239,68,68,0.55)]"
<div class="cycle-stamp absolute top-1/2 left-1/2 flex flex-col items-center gap-3 px-7 py-6 rounded bg-white border-[3px] border-red-500 shadow-[0_12px_40px_-6px_rgba(239,68,68,0.65)]"
:class="phase >= 3 ? 'is-visible' : ''"
style="transform: translate(-50%,-50%) scale(0); opacity: 0;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7 text-red-500" aria-hidden="true"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
<div class="flex items-center gap-2.5">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" class="w-8 h-8 text-red-500" aria-hidden="true"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
<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="cycle-alert-icon w-5 h-5 text-red-500" aria-hidden="true"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><line x1="23" y1="9" x2="17" y2="15"/><line x1="17" y1="9" x2="23" y2="15"/></svg>
</div>
<div class="text-center">
<div class="font-mono font-bold tracking-[0.14em] text-sm uppercase text-red-600">NON CONFORME</div>
<div class="font-mono text-[10px] tracking-[0.20em] uppercase mt-1.5 text-red-500/70">Loi&nbsp;25 · Cloud Act américain</div>
<div class="font-mono font-black tracking-[0.16em] text-2xl uppercase text-red-600 leading-none">NON CONFORME</div>
<div class="font-mono text-[10px] tracking-[0.20em] uppercase mt-2 text-red-500/70">Loi&nbsp;25 · Cloud Act américain</div>
</div>
</div>
</div>
<div class="px-5 py-3 border-b border-red-100">
<div class="relative px-5 py-3 border-b border-red-100">
<div class="font-mono text-[10px] tracking-[0.28em] uppercase text-red-500/60 mb-0.5">02</div>
<div class="font-bold text-sm text-red-500/75">IA cloud américaine</div>
</div>
<div class="relative flex-1 flex flex-col items-center justify-center px-5 py-6 gap-5">
{# Server rack icon (round 5) — 3 stacked rectangles avec leds rouges #}
<div class="relative flex flex-col items-center gap-2">
<div class="relative w-14 h-14 rounded flex items-center justify-center bg-red-100/60 border border-red-200" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" class="w-6 h-6 text-red-500/70"><path d="M2 12s3-7 10-7c2.5 0 4.7.9 6.4 2.4M22 12s-3 7-10 7c-2.5 0-4.7-.9-6.4-2.4"/><line x1="2" y1="2" x2="22" y2="22"/></svg>
{# Particules de fuite (round 4 : 10 particules, plus dense) #}
{% for i in range(10) %}
<span class="cycle-leak-particle absolute w-[3px] h-[3px] rounded-full bg-red-400 pointer-events-none"
style="left: 50%; top: 50%; --lx: {{ (-32 + i*8) }}px; --ly: -{{ 12 + (i % 4) * 7 }}px; animation-delay: {{ i * 0.16 }}s; box-shadow: 0 0 4px rgba(239,68,68,0.55);"></span>
<div class="relative w-16 h-14 rounded-sm flex flex-col items-center justify-center gap-[3px] bg-red-100/60 border border-red-200 px-2 py-1.5" aria-hidden="true">
{% for rack_i in [0,1,2] %}
<div class="w-full h-2.5 rounded-[2px] bg-red-50/80 border border-red-200/70 flex items-center px-1 gap-1">
<span class="cycle-led{% if rack_i > 0 %} cycle-led-{{ rack_i + 1 }}{% endif %} w-1 h-1 rounded-full bg-red-500"></span>
<span class="cycle-led cycle-led-{{ ((rack_i + 1) % 3) + 1 }} w-1 h-1 rounded-full bg-red-500"></span>
<span class="flex-1 h-[1px] rounded-full bg-red-300/60"></span>
<span class="cycle-led cycle-led-{{ ((rack_i + 2) % 3) + 1 }} w-1 h-1 rounded-full bg-red-500"></span>
</div>
{% endfor %}
{# Round 5 — 12 paquets data .wav/.aac qui s'envolent vers TOP-RIGHT (USA direction) #}
{% for i in range(12) %}
<span class="cycle-leak-particle absolute pointer-events-none flex items-center justify-center font-mono font-bold uppercase text-white text-[6px] rounded-sm"
style="left: {{ 25 + (i % 4) * 12 }}%; top: {{ 30 + (i % 3) * 18 }}%;
width: 18px; height: 9px;
background: rgba(239,68,68,0.65); border: 1px solid rgba(239,68,68,0.85);
box-shadow: 0 0 4px rgba(239,68,68,0.55);
--lx: {{ 50 + i*6 }}px; --ly: -{{ 70 + (i % 4) * 15 }}px;
--rot: {{ -8 + (i % 5) * 4 }}deg;
animation-delay: {{ i * 0.18 }}s;">{{ '.wav' if i is even else '.aac' }}</span>
{% endfor %}
</div>
<div class="px-2 py-0.5 rounded bg-red-50 border border-red-100">
<div class="px-2 py-0.5 rounded bg-red-50 border border-red-100 inline-flex items-center gap-1">
<span class="cycle-chev font-mono font-bold text-[8px] text-red-500/70">&lt;</span>
<span class="cycle-chev cycle-chev-2 font-mono font-bold text-[8px] text-red-500/70">&lt;</span>
<span class="cycle-chev cycle-chev-3 font-mono font-bold text-[8px] text-red-500/70">&lt;</span>
<span class="font-mono text-[9px] tracking-[0.22em] uppercase text-red-500/65">USA</span>
</div>
</div>
@@ -732,7 +933,7 @@
</div>
</div>
<div class="px-5 pb-5 pt-4 border-t border-red-100">
<div class="relative px-5 pb-5 pt-4 border-t border-red-100">
<div class="flex items-center gap-2">
<div class="w-1.5 h-1.5 rounded-full bg-red-400"></div>
<span class="font-mono text-[9px] tracking-[0.20em] uppercase text-red-500/70">Violation légale possible</span>
@@ -740,93 +941,127 @@
</div>
</div>
{# COL 3 — DictIA (featured, 3fr) #}
<div class="cycle-card-dictia cycle-reveal-up relative flex flex-col rounded border border-brand-b1/35 bg-gradient-to-br from-brand-b1/[0.06] to-brand-b3/[0.04] overflow-hidden"
:class="phase >= 4 ? 'is-visible' : ''"
{# COL 3 — DictIA (featured, 3fr) — PALETTE BRAND BLUE+CYAN+FUCHSIA #}
<div class="cycle-col-card cycle-card-dictia cycle-reveal-up relative flex flex-col rounded border border-brand-b1/40 bg-gradient-to-br from-brand-b1/[0.07] via-brand-b2/[0.04] to-brand-b3/[0.04] overflow-hidden"
:class="{ 'is-visible': phase >= 4, 'is-revealed': phase >= 4, 'is-active': phase >= 4 }"
style="transition-delay: 240ms;">
{# Halo ambiant #}
{# Halo ambiant blue->cyan->fuchsia #}
<div class="absolute inset-0 pointer-events-none" aria-hidden="true"
style="background: radial-gradient(ellipse 80% 35% at 50% 0%, rgba(6,182,212,0.12) 0%, transparent 65%);"></div>
style="background: radial-gradient(ellipse 80% 35% at 50% 0%, rgba(6,182,212,0.18) 0%, transparent 65%), radial-gradient(ellipse 60% 50% at 50% 100%, rgba(37,99,235,0.10) 0%, transparent 70%);"></div>
<div class="relative px-5 py-3 border-b border-brand-b1/15 flex items-center gap-2.5">
{# Numéro 03 → checkmark vert (round 4) #}
<span class="relative w-5 h-5 rounded-full flex items-center justify-center shrink-0 bg-emerald-500/15 border border-emerald-500/45" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="cycle-check-svg w-3 h-3 text-emerald-600"
{# Round 5 — Quebec province outline subtle background #}
<svg class="absolute pointer-events-none" aria-hidden="true"
style="top: 18%; right: 6%; width: 95px; height: 95px; opacity: 0.6;"
viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet">
<path d="M22,10 L35,8 L48,9 L62,11 L74,15 L82,22 L86,32 L88,45 L85,58 L80,68 L72,76 L62,82 L52,85 L42,84 L32,80 L24,72 L18,62 L15,50 L14,38 L17,26 Z"
fill="rgba(37,99,235,0.06)" stroke="rgba(37,99,235,0.28)" stroke-width="1"/>
<circle cx="48" cy="62" r="2.2" fill="rgba(6,182,212,0.85)"/>
<circle cx="48" cy="62" r="5" fill="none" stroke="rgba(6,182,212,0.45)" stroke-width="0.8"/>
<text x="40" y="71" font-family="Inter, sans-serif" font-size="7" fill="rgba(192,38,211,0.7)" font-weight="bold">QC</text>
</svg>
<div class="relative px-5 py-3 border-b border-brand-b1/20 flex items-center gap-2.5">
{# Round 5 — mini logo DictIA (subtle) connecte le brand visuellement #}
<img src="{{ url_for('static', filename='images/dictia-logo.png') }}"
alt="" aria-hidden="true"
class="w-7 h-7 rounded shrink-0 opacity-95"
width="28" height="28" loading="lazy">
{# Numéro 03 → checkmark CYAN (round 5 : palette brand) #}
<span class="relative w-5 h-5 rounded-full flex items-center justify-center shrink-0 bg-brand-b2/20 border border-brand-b2/50" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" class="cycle-check-svg w-3 h-3"
:class="phase >= 4 ? 'is-visible' : ''"><path d="M5 13l4 4L19 7"/></svg>
</span>
<span class="text-[11px] uppercase tracking-[0.22em] font-semibold text-brand-b1/80">Solution</span>
{# Badge top-right : Loi 25 conforme (round 4) #}
<span class="cycle-conforme-badge ml-auto inline-flex items-center gap-1 rounded-full px-2 py-0.5 bg-emerald-500/12 border border-emerald-500/40"
:class="phase >= 4 ? 'is-visible' : ''" aria-label="Loi 25 conforme">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-2.5 h-2.5 text-emerald-600" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
<span class="font-mono font-bold text-[8px] tracking-[0.16em] uppercase text-emerald-700">Loi&nbsp;25 conforme</span>
<span class="text-[11px] uppercase tracking-[0.22em] font-semibold text-brand-b1/85">Solution</span>
{# Badge top-right : Loi 25 conforme round 5 : gradient brand blue->fuchsia #}
<span class="cycle-conforme-badge ml-auto inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 border border-brand-b2/50"
:class="phase >= 4 ? 'is-visible' : ''" aria-label="Loi 25 conforme"
style="background: linear-gradient(135deg, rgba(37,99,235,0.22), rgba(192,38,211,0.18));">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-2.5 h-2.5 text-white" aria-hidden="true"><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 class="font-mono font-bold text-[8px] tracking-[0.16em] uppercase text-white">Loi&nbsp;25 conforme</span>
</span>
</div>
<div class="relative flex-1 flex flex-col items-center justify-center px-5 py-7 gap-5">
{# Lock + anneaux concentriques #}
{# Shield + microphone (round 5) + halo multi-couches blue+cyan + drawn ring SVG #}
<div class="relative flex items-center justify-center" aria-hidden="true">
<span class="cycle-ring-outer absolute rounded-full border border-brand-b1/15"
{# Couche externe drawn ring SVG (fuchsia accent) #}
<svg class="cycle-ring-draw-svg absolute" :class="phase >= 4 ? 'is-visible' : ''"
width="108" height="108" viewBox="0 0 80 80">
<circle cx="40" cy="40" r="35" fill="none" stroke="rgba(192,38,211,0.32)" stroke-width="1" stroke-linecap="round"/>
</svg>
<span class="cycle-ring-outer absolute rounded-full border border-brand-b1/25"
:class="phase >= 4 ? 'is-visible' : ''"
style="width: 96px; height: 96px;"></span>
<span class="cycle-ring-inner absolute rounded-full border border-brand-b1/25"
<span class="cycle-ring-inner absolute rounded-full border border-brand-b2/40"
:class="phase >= 4 ? 'is-visible' : ''"
style="width: 68px; height: 68px;"></span>
<div class="relative w-[52px] h-[52px] rounded flex items-center justify-center bg-gradient-to-br from-brand-b1/15 to-brand-b3/10 border border-brand-b1/35">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" class="w-6 h-6 text-brand-b1"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
<div class="relative w-[56px] h-[56px] rounded flex items-center justify-center bg-gradient-to-br from-brand-b1/20 via-brand-b2/15 to-brand-b3/10 border border-brand-b2/45"
style="box-shadow: 0 0 24px rgba(37,99,235,0.35), 0 0 48px rgba(6,182,212,0.18);">
{# Shield extérieur #}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" class="w-7 h-7 text-brand-b1">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
</svg>
{# Microphone à l'intérieur #}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="w-3.5 h-3.5 text-brand-b2 absolute"
style="top: 50%; left: 50%; transform: translate(-50%, -45%);">
<rect x="9" y="6" width="6" height="9" rx="3"/>
<path d="M12 17v3"/>
<path d="M9 20h6"/>
</svg>
</div>
</div>
{# Flux sécurisé #}
<div class="cycle-reveal flex items-center gap-2 px-3.5 py-1.5 rounded bg-brand-b1/5 border border-brand-b1/15"
{# Flux sécurisé (palette brand b1+b2) #}
<div class="cycle-reveal flex items-center gap-2 px-3.5 py-1.5 rounded bg-brand-b1/10 border border-brand-b2/25"
:class="phase >= 4 ? 'is-visible' : ''"
style="transition-delay: 350ms;">
<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-3 h-3 shrink-0 text-brand-b1/55" 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"/></svg>
<span class="text-[11px] text-brand-b1/70">Transcription sécurisée</span>
<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-3 h-3 text-brand-b1/45" aria-hidden="true"><path d="M5 12h14M13 5l7 7-7 7"/></svg>
<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-3 h-3 shrink-0 text-brand-b1/55" aria-hidden="true"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg>
<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-3 h-3 shrink-0 text-brand-b1/65" 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"/></svg>
<span class="text-[11px] text-brand-b1/80 font-medium">Transcription sécurisée</span>
<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-3 h-3 text-brand-b2" aria-hidden="true"><path d="M5 12h14M13 5l7 7-7 7"/></svg>
<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-3 h-3 shrink-0 text-brand-b1/65" aria-hidden="true"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/></svg>
</div>
{# Badges conformité #}
{# Badges conformité (palette brand b1+b2 cyan checkmark) #}
<div class="flex flex-col gap-2 w-full">
{% for badge_label in ['Loi 25 conforme', '100&nbsp;% hébergé au Québec', 'Données jamais partagées'] %}
<div class="cycle-reveal flex items-center gap-2.5 px-3.5 py-2.5 rounded bg-brand-b1/[0.06] border border-brand-b1/20"
<div class="cycle-reveal flex items-center gap-2.5 px-3.5 py-2.5 rounded bg-brand-b1/[0.08] border border-brand-b2/25"
:class="phase >= 4 ? 'is-visible' : ''"
style="transition-delay: {{ 180 + loop.index0 * 170 }}ms;">
<span class="w-4 h-4 rounded-full flex items-center justify-center shrink-0 bg-brand-b1/15 border border-brand-b1/30">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="w-2.5 h-2.5 text-brand-b1" aria-hidden="true"><path d="M5 13l4 4L19 7"/></svg>
<span class="w-4 h-4 rounded-full flex items-center justify-center shrink-0 bg-brand-b2/20 border border-brand-b2/45">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" class="w-2.5 h-2.5 text-brand-b2" aria-hidden="true"><path d="M5 13l4 4L19 7"/></svg>
</span>
<span class="text-[12px] font-medium text-brand-navy/75">{{ badge_label | safe }}</span>
<span class="text-[12px] font-medium text-brand-navy/80">{{ badge_label | safe }}</span>
</div>
{% endfor %}
</div>
</div>
<div class="relative px-5 pb-5 pt-4 border-t border-brand-b1/15">
<div class="relative px-5 pb-5 pt-4 border-t border-brand-b2/20">
<div class="flex items-center gap-2 mb-2.5">
<span class="flex-1 h-px bg-brand-b1/15"></span>
<span class="font-mono font-bold text-[10px] uppercase tracking-[0.20em] text-brand-b1">UTILISATEURS ILLIMITÉS</span>
<span class="flex-1 h-px bg-brand-b1/15"></span>
<span class="flex-1 h-px bg-gradient-to-r from-transparent via-brand-b1/30 to-brand-b2/30"></span>
<span class="font-mono font-bold text-[10px] uppercase tracking-[0.20em] grad-text">UTILISATEURS ILLIMITÉS</span>
<span class="flex-1 h-px bg-gradient-to-l from-transparent via-brand-b3/30 to-brand-b2/30"></span>
</div>
<p class="text-center text-[10px] mb-3.5 text-brand-b1/55">Zéro frais caché · Du jamais vu</p>
<p class="text-center text-[10px] mb-3.5 text-brand-b1/65">Zéro frais caché · Du jamais vu</p>
<div class="flex items-baseline gap-2">
<span class="font-black leading-none text-5xl text-brand-b1">173</span>
<span class="text-sm text-brand-navy/45 mb-0.5">$ / mois</span>
{# Round 5 — big number en grad-text (blue->cyan->fuchsia) #}
<span class="font-black leading-none text-5xl grad-text">173</span>
<span class="text-sm text-brand-navy/55 mb-0.5 font-semibold">$ / mois</span>
</div>
<div class="flex items-center gap-1.5 mt-2">
<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-3 h-3 text-brand-b1/65" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
<p class="text-[10px] text-brand-b1/60">Conforme Loi&nbsp;25 · 100&nbsp;% Québec</p>
<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-3 h-3 text-brand-b2" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
<p class="text-[10px] text-brand-b1/70 font-medium">Conforme Loi&nbsp;25 · 100&nbsp;% Québec</p>
</div>
</div>
</div>
</div>
{# Section "Économies annuelles · 25 utilisateurs" — round 4 : 3 cards avec counter animation #}
{# Section "Économies annuelles · 25 utilisateurs" — round 5 : 3 cards palette brand + chips contextuels #}
<div class="cycle-reveal-up mt-12"
:class="phase >= 4 ? 'is-visible' : ''"
style="transition-delay: 700ms;">
<div class="flex items-center justify-center gap-2.5 mb-5">
<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 text-emerald-500" aria-hidden="true"><polyline points="23 18 13.5 8.5 8.5 13.5 1 6"/><polyline points="17 18 23 18 23 12"/></svg>
<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 text-brand-b1" aria-hidden="true"><polyline points="23 18 13.5 8.5 8.5 13.5 1 6"/><polyline points="17 18 23 18 23 12"/></svg>
<span class="font-mono font-bold text-[10px] uppercase tracking-[0.22em] text-brand-navy/65">
Économies annuelles · 25&nbsp;utilisateurs
</span>
@@ -835,28 +1070,61 @@
<div class="grid grid-cols-1 md:grid-cols-3 gap-3" role="list" aria-label="Trois comparaisons d'économies annuelles">
{% for sav in [
{'icon': 'otter', 'val_prop': 'sav1', 'val_static': '3&nbsp;924', 'label': 'vs Otter.ai', 'sub': 'IA cloud US'},
{'icon': 'teams', 'val_prop': 'sav2', 'val_static': '6&nbsp;924', 'label': 'vs MS Teams', 'sub': 'Copilot premium'},
{'icon': 'scribe','val_prop': 'sav3', 'val_static': '2&nbsp;004', 'label': 'vs Sténographe','sub': 'Service humain'}
{'icon': 'otter', 'val_prop': 'sav1', 'val_static': '3&nbsp;924', 'label': 'vs Otter.ai', 'sub': 'IA cloud US', 'chip': 'IA cloud US'},
{'icon': 'teams', 'val_prop': 'sav2', 'val_static': '6&nbsp;924', 'label': 'vs MS Teams', 'sub': 'Copilot premium', 'chip': 'Copilot premium'},
{'icon': 'scribe','val_prop': 'sav3', 'val_static': '2&nbsp;004', 'label': 'vs Sténographe','sub': 'Service humain', 'chip': 'Service humain'}
] %}
<div class="cycle-savings-card flex items-center gap-3 px-4 py-4 rounded border border-brand-border bg-white" role="listitem">
<span class="shrink-0 w-10 h-10 rounded flex items-center justify-center bg-emerald-50 border border-emerald-100 text-emerald-600" aria-hidden="true">
<div class="cycle-savings-card flex items-center gap-3 px-4 py-4 rounded border border-brand-border bg-white relative overflow-hidden" role="listitem">
{# Round 5 — subtle blue tint bg layer #}
<div class="absolute inset-0 pointer-events-none opacity-60" aria-hidden="true"
style="background: linear-gradient(135deg, rgba(37,99,235,0.04) 0%, transparent 60%);"></div>
<span class="relative shrink-0 w-10 h-10 rounded flex items-center justify-center bg-brand-b1/10 border border-brand-b2/30 text-brand-b1" aria-hidden="true">
{% if sav.icon == 'otter' %}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5"><path d="M2 12s3-7 10-7c2.5 0 4.7.9 6.4 2.4M22 12s-3 7-10 7c-2.5 0-4.7-.9-6.4-2.4"/><circle cx="12" cy="12" r="3"/></svg>
{# Round 5 — silhouette de loutre stylisée (tête + oreilles + moustache) #}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5">
<path d="M5 8 C5 5, 7 3, 8 3 C8.5 4, 9 5, 8.5 6"/>
<path d="M19 8 C19 5, 17 3, 16 3 C15.5 4, 15 5, 15.5 6"/>
<ellipse cx="12" cy="13" rx="6" ry="6.5"/>
<circle cx="9.5" cy="11.5" r="0.8" fill="currentColor"/>
<circle cx="14.5" cy="11.5" r="0.8" fill="currentColor"/>
<path d="M11 15 Q12 16 13 15"/>
<path d="M9 16 L7.5 16.5 M15 16 L16.5 16.5"/>
</svg>
{% elif sav.icon == 'teams' %}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
{# Round 5 — carré avec "T" stylisé Teams #}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5">
<rect x="3" y="3" width="18" height="18" rx="2"/>
<path d="M8 8h8"/>
<path d="M12 8v9"/>
</svg>
{% else %}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
{# Round 5 — silhouette humain (sténographe) avec doc #}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5">
<circle cx="9" cy="6" r="3"/>
<path d="M3 21v-1.5C3 17 5.7 15 9 15s6 2 6 4.5V21"/>
<rect x="15.5" y="11" width="6" height="8" rx="0.8"/>
<line x1="17" y1="13.5" x2="20" y2="13.5"/>
<line x1="17" y1="15.5" x2="20" y2="15.5"/>
</svg>
{% endif %}
</span>
<div class="flex-1 min-w-0">
<div class="relative flex-1 min-w-0">
{# Round 5 — save chip "+économies" au-dessus du big number #}
<span class="cycle-save-chip inline-flex items-center gap-1 rounded-full px-1.5 py-px mb-1 bg-brand-b1/10 border border-brand-b1/25">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="w-2 h-2 text-brand-b1" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
<span class="font-mono font-bold text-[7px] tracking-[0.16em] uppercase text-brand-b1">+économies</span>
</span>
<div class="flex items-baseline">
{# Round 5 — big number en grad-text (blue->cyan->fuchsia) #}
{# OQLF NBSP entre nombre et $ — préservé en placeholder statique pour SEO/no-JS, JS écrase via x-html #}
<span class="font-black text-2xl leading-none text-emerald-600 tabular-nums"
x-html="fmt({{ sav.val_prop }}) + '&nbsp;<span class=&quot;text-sm text-emerald-600/70 font-bold&quot;>$</span>'">{{ sav.val_static | safe }}&nbsp;$</span>
<span class="font-black text-2xl leading-none grad-text tabular-nums"
x-html="fmt({{ sav.val_prop }}) + '&nbsp;<span class=&quot;text-sm font-bold&quot;>$</span>'">{{ sav.val_static | safe }}&nbsp;$</span>
</div>
<div class="text-[11px] font-semibold text-brand-navy/85 mt-1">{{ sav.label }}</div>
<div class="text-[10px] text-brand-navy/45">{{ sav.sub }}</div>
<div class="inline-flex items-center gap-1 mt-0.5">
<span class="w-1 h-1 rounded-full bg-brand-b2"></span>
<span class="font-mono text-[9px] tracking-[0.10em] text-brand-navy/50">{{ sav.chip }}</span>
</div>
</div>
</div>
{% endfor %}