feat(marketing): round 4 — Cadre + Cycle cinématiques (radar, data packet flight, stamp impact, savings counter)
Round 4 transforme les 2 sections "Cadre réglementaire" en expériences cinématiques : CADRE (Moniteur d'Interception) - Radar sweep circulaire vert continu en background HUD (4s loop, SVG + @keyframes) - 6 paquets data "voice.wav" en flight QC→US via offset-path bezier (stagger 420ms, glow rouge) - Console typewriter char-by-char 3 lignes (28ms/char + caret blink, 3e ligne rouge glow) - 6 REGS reveal cascadé via revealRegsCascade (stagger 120ms) + hover red glow + border-left - Verdict NON CONFORME : pulse glow rouge + scan-line traversante 3s - Decorative grid 40×40 console-style + grid existant 20×20 - Eyebrow ⚠ remplacé par SVG warning-triangle inline CYCLE (Trois options) - Phase reveal 1→4 séquentiel (déjà existant) avec animations renforcées - Col 1 horloge accélérée 1 tour/3s (au lieu de 8s) - Col 1 prix counter Alpine 0→315 (easeOutCubic 1.4s) via priceHumain + countTo - Col 2 stamp NON CONFORME impact (rotate -22→-3deg + scale 2.4→1, cubic-bezier 1.6 ease) - Col 2 flash rouge background à l'impact (cycle-col-flash) + 10 particules de fuite (au lieu de 6) - Col 3 checkmark draw via stroke-dashoffset 24→0 - Col 3 glow border vert pulsant (cycle-conforme-glow, double-couche emerald + cyan) - Col 3 badge "Loi 25 conforme" top-right avec pulse subtil (cycle-conforme-badge) - Connecting lines avec dash flow continu (cycle-line-flow @keyframes) - Live red dot "Réunion en cours" avec pulse box-shadow - Section "Économies annuelles · 25 utilisateurs" : 3 cards avec counter Alpine (sav1=3924, sav2=6924, sav3=2004) + hover lift + emerald shadow - Eyebrow ⚠ remplacé par SVG warning-triangle Accessibilité & performance - prefers-reduced-motion désactive TOUT (radar, packets, typewriter, stamp, glow, counter) - Mobile (<768px) cache radar + packets + leak particles (CPU-intensive) - Counter helper countTo respecte reduced-motion via matchMedia - Tous les SVG ont aria-hidden, scènes ont role=img/listitem appropriés - HUD console role=log + aria-live=polite - OQLF NBSP préservé (315 $/réunion, Loi 25, 100 % Québec, 25 utilisateurs, 3 924 $) Tests : 4 tests round 4 ajoutés (cadre cinematic, cycle cinematic, no-emoji warning, reduced-motion guards). 65/68 landing tests passent (3 failures pré-existantes unrelated : nav /blog, footer /blog, trust-bar phrasing). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -411,10 +411,10 @@
|
||||
|
||||
{# ===== CYCLE — "Trois options. Une seule est conforme." ===== #}
|
||||
{# Source canonique : InnovA-AI/Website-Sanity/components/sections/dictai-cycle.tsx
|
||||
Animation : phase reveal phasé (1→4) déclenché par IntersectionObserver natif + Alpine.
|
||||
3 colonnes comparatives : 01 Humaine · 02 Cloud US (overlay non-conforme) · 03 DictIA (featured + pulse rings) #}
|
||||
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 #}
|
||||
<style>
|
||||
/* Cycle pulse rings — appliqués au nœud source "Réunion en cours" */
|
||||
/* 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; }
|
||||
@@ -423,12 +423,19 @@
|
||||
.cycle-pulse-2 { animation-delay: 0.65s; }
|
||||
.cycle-pulse-3 { animation-delay: 1.3s; }
|
||||
|
||||
/* Cycle col 3 (DictIA) — bordure lumineuse pulsante */
|
||||
@keyframes cycle-glow {
|
||||
0%, 100% { box-shadow: 0 0 0 0 rgba(0,189,216,0.12); }
|
||||
50% { box-shadow: 0 0 35px 0 rgba(0,189,216,0.30); }
|
||||
/* Live red dot — pulse plus marqué pour signal "réunion en cours" */
|
||||
@keyframes cycle-live-dot {
|
||||
0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(239,68,68,0.55); }
|
||||
50% { opacity: 0.6; box-shadow: 0 0 0 5px rgba(239,68,68,0); }
|
||||
}
|
||||
.cycle-card-dictia.is-visible { animation: cycle-glow 3.8s ease-in-out infinite; }
|
||||
.cycle-live-dot { animation: cycle-live-dot 1.4s ease-in-out infinite; }
|
||||
|
||||
/* Cycle col 3 (DictIA) — bordure lumineuse pulsante VERT (round 4 : conformité) */
|
||||
@keyframes cycle-conforme-glow {
|
||||
0%, 100% { box-shadow: 0 0 0 0 rgba(34,197,94,0.18), 0 0 0 0 rgba(0,189,216,0.10); }
|
||||
50% { box-shadow: 0 0 32px 2px rgba(34,197,94,0.32), 0 0 60px 0 rgba(0,189,216,0.14); }
|
||||
}
|
||||
.cycle-card-dictia.is-visible { animation: cycle-conforme-glow 3.4s ease-in-out infinite; }
|
||||
|
||||
/* Cycle col 3 — anneaux concentriques autour du Lock */
|
||||
@keyframes cycle-ring-out {
|
||||
@@ -438,48 +445,123 @@
|
||||
.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 */
|
||||
.cycle-line { stroke-dasharray: 50; stroke-dashoffset: 50; transition: stroke-dashoffset 600ms ease-out; }
|
||||
/* Cycle SVG lignes de connexion — drawn via stroke-dashoffset, dash flow continu (round 4) */
|
||||
.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 — colonnes 1+2 (phase 2), overlay (phase 3), col 3 (phase 4) */
|
||||
/* Cycle phase reveal */
|
||||
.cycle-reveal { opacity: 0; transform: translateX(14px); transition: opacity 380ms ease-out, transform 380ms ease-out; }
|
||||
.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.is-visible { opacity: 1; transform: translateY(0); }
|
||||
|
||||
/* Cycle horloge rotation (col 1) */
|
||||
/* Cycle horloge rotation (col 1) — accélérée 1 tour / 3s pour rendre "lent" PALPABLE */
|
||||
@keyframes cycle-clock-spin { to { transform: rotate(360deg); } }
|
||||
.cycle-clock { animation: cycle-clock-spin 8s linear infinite; transform-origin: center; display: inline-block; }
|
||||
.cycle-clock { animation: cycle-clock-spin 3s linear infinite; transform-origin: center; display: inline-block; }
|
||||
|
||||
/* Cycle col 2 — fuite particules rouges */
|
||||
/* Cycle col 2 — fuite particules rouges (continu, plus dense round 4) */
|
||||
@keyframes cycle-leak {
|
||||
0% { transform: translate(0,0) scale(1); opacity: 0; }
|
||||
20% { opacity: 0.75; }
|
||||
100% { transform: translate(var(--lx,18px), var(--ly,-22px)) scale(0.4); opacity: 0; }
|
||||
20% { opacity: 0.85; }
|
||||
100% { transform: translate(var(--lx,18px), var(--ly,-22px)) scale(0.35); opacity: 0; }
|
||||
}
|
||||
.cycle-leak-particle { animation: cycle-leak 1.8s ease-out infinite; }
|
||||
|
||||
/* Reduced motion */
|
||||
/* Cycle col 2 — STAMP NON CONFORME impact (round 4 : tampon huissier qui claque) */
|
||||
@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; }
|
||||
}
|
||||
.cycle-stamp.is-visible { animation: cycle-stamp-impact 720ms cubic-bezier(0.5,1.6,0.4,1) forwards; }
|
||||
|
||||
/* Cycle col 2 — flash background rouge à l'impact stamp */
|
||||
@keyframes cycle-flash-red {
|
||||
0% { background-color: rgba(239,68,68,0); }
|
||||
35% { background-color: rgba(239,68,68,0.18); }
|
||||
100% { background-color: rgba(239,68,68,0); }
|
||||
}
|
||||
.cycle-col-flash.is-visible { animation: cycle-flash-red 700ms ease-out 350ms forwards; }
|
||||
|
||||
/* Cycle col 3 — checkmark draw (stroke-dashoffset) */
|
||||
.cycle-check-svg path { stroke-dasharray: 24; stroke-dashoffset: 24; transition: stroke-dashoffset 350ms ease-out 200ms; }
|
||||
.cycle-check-svg.is-visible path { stroke-dashoffset: 0; }
|
||||
|
||||
/* Cycle col 3 — badge "Loi 25 conforme" pulse subtil */
|
||||
@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); }
|
||||
}
|
||||
.cycle-conforme-badge.is-visible { animation: cycle-badge-pulse 2.4s ease-in-out infinite 600ms; }
|
||||
|
||||
/* Cycle "Économies annuelles" cards — hover lift + glow */
|
||||
.cycle-savings-card {
|
||||
transition: transform 220ms ease-out, box-shadow 220ms ease-out, border-color 220ms 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);
|
||||
}
|
||||
|
||||
/* Mobile — désactiver fuites particules + glow heavy */
|
||||
@media (max-width: 768px) {
|
||||
.cycle-leak-particle { display: none; }
|
||||
.cycle-card-dictia.is-visible { animation: none; }
|
||||
}
|
||||
|
||||
/* Reduced motion — TOUT figé */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.cycle-pulse, .cycle-card-dictia, .cycle-ring-outer, .cycle-ring-inner,
|
||||
.cycle-clock, .cycle-leak-particle { animation: none !important; }
|
||||
.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-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; }
|
||||
}
|
||||
</style>
|
||||
<section
|
||||
class="bg-white py-20 border-y border-brand-border relative overflow-hidden"
|
||||
aria-labelledby="cycle-title"
|
||||
x-data="{ phase: 0, observer: null }"
|
||||
x-data="{
|
||||
phase: 0,
|
||||
observer: null,
|
||||
priceHumain: 0,
|
||||
sav1: 0, sav2: 0, sav3: 0,
|
||||
/* easeOutCubic counter helper */
|
||||
countTo(prop, target, duration) {
|
||||
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) { this[prop] = target; return; }
|
||||
const start = performance.now();
|
||||
const tick = (now) => {
|
||||
const t = Math.min(1, (now - start) / duration);
|
||||
const eased = 1 - Math.pow(1 - t, 3);
|
||||
this[prop] = Math.round(target * eased);
|
||||
if (t < 1) requestAnimationFrame(tick);
|
||||
};
|
||||
requestAnimationFrame(tick);
|
||||
},
|
||||
fmt(n) { return n.toLocaleString('fr-CA').replace(/ /g, ' '); }
|
||||
}"
|
||||
x-init="
|
||||
observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(e => {
|
||||
if (e.isIntersecting && phase === 0) {
|
||||
setTimeout(() => phase = 1, 250);
|
||||
setTimeout(() => phase = 2, 1100);
|
||||
setTimeout(() => { phase = 1; }, 250);
|
||||
setTimeout(() => { phase = 2; countTo('priceHumain', 315, 1400); }, 1100);
|
||||
setTimeout(() => phase = 3, 2200);
|
||||
setTimeout(() => phase = 4, 3000);
|
||||
setTimeout(() => {
|
||||
phase = 4;
|
||||
/* Savings counters fire 600ms after col 3 reveal (round 4) */
|
||||
setTimeout(() => {
|
||||
countTo('sav1', 3924, 1500);
|
||||
countTo('sav2', 6924, 1500);
|
||||
countTo('sav3', 2004, 1500);
|
||||
}, 700);
|
||||
}, 3000);
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
@@ -493,7 +575,10 @@
|
||||
|
||||
<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">⚠ CADRE RÉGLEMENTAIRE</p>
|
||||
<p class="eyebrow text-amber-600 mb-4 inline-flex items-center gap-1.5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" class="w-3.5 h-3.5" 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>
|
||||
CADRE RÉGLEMENTAIRE
|
||||
</p>
|
||||
<h2 id="cycle-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black mb-3 text-brand-navy">
|
||||
Trois options. <span class="text-brand-navy/30">Une seule est conforme.</span>
|
||||
</h2>
|
||||
@@ -519,23 +604,25 @@
|
||||
</template>
|
||||
<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.5 h-3.5 shrink-0 text-brand-navy/40" aria-hidden="true"><path d="M3 21h18"/><path d="M5 21V8l7-4 7 4v13"/><path d="M9 21v-6h6v6"/></svg>
|
||||
<span class="text-[11px] tracking-wide text-brand-navy/55">Réunion en cours — données confidentielles</span>
|
||||
<span class="cycle-live-dot inline-block w-1.5 h-1.5 rounded-full bg-red-500 shrink-0" aria-hidden="true"></span>
|
||||
<span class="font-mono font-bold text-[8px] tracking-[0.22em] uppercase text-red-500/85">Live</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Lignes de connexion SVG — de la source vers les 3 colonnes #}
|
||||
{# Lignes de connexion SVG — de la source vers les 3 colonnes (round 4 : dash flow continu) #}
|
||||
<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" :class="phase >= 1 ? 'is-visible' : ''"
|
||||
<line class="cycle-line cycle-line-flow" :class="phase >= 1 ? 'is-visible' : ''"
|
||||
x1="50" y1="0" x2="14" y2="10"
|
||||
stroke="rgba(148,163,184,0.45)" stroke-width="0.3" stroke-dasharray="1.2 0.9" />
|
||||
<line class="cycle-line" :class="phase >= 1 ? 'is-visible' : ''"
|
||||
stroke="rgba(148,163,184,0.55)" stroke-width="0.3" />
|
||||
<line class="cycle-line cycle-line-flow" :class="phase >= 1 ? 'is-visible' : ''"
|
||||
x1="50" y1="0" x2="50" y2="10"
|
||||
stroke="rgba(239,68,68,0.45)" stroke-width="0.3" stroke-dasharray="1.2 0.9"
|
||||
style="transition-delay: 80ms;" />
|
||||
<line class="cycle-line" :class="phase >= 1 ? 'is-visible' : ''"
|
||||
stroke="rgba(239,68,68,0.55)" stroke-width="0.3"
|
||||
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(0,189,216,0.55)" stroke-width="0.3" stroke-dasharray="1.2 0.9"
|
||||
style="transition-delay: 160ms;" />
|
||||
stroke="rgba(34,197,94,0.6)" stroke-width="0.3"
|
||||
style="transition-delay: 160ms; animation-delay: 0.8s;" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
@@ -576,8 +663,8 @@
|
||||
</div>
|
||||
<div class="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">315</span>
|
||||
<span class="text-xs text-brand-navy/45">$ / réunion</span>
|
||||
<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">$ / réunion</span>
|
||||
</div>
|
||||
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded bg-red-50 border border-red-100">
|
||||
<span class="w-1 h-1 rounded-full bg-red-400"></span>
|
||||
@@ -587,18 +674,20 @@
|
||||
</div>
|
||||
|
||||
{# COL 2 — IA cloud américaine #}
|
||||
<div class="cycle-reveal-up relative flex flex-col rounded border border-red-200 bg-red-50/30 overflow-hidden"
|
||||
<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' : ''"
|
||||
style="transition-delay: 120ms;">
|
||||
{# Overlay légal NON CONFORME (phase 3) #}
|
||||
<div class="absolute inset-0 z-30 flex flex-col items-center justify-center pointer-events-none cycle-reveal-up"
|
||||
:class="phase >= 3 ? 'is-visible' : ''"
|
||||
style="backdrop-filter: blur(6px); background: rgba(255,255,255,0.78);">
|
||||
<div class="flex flex-col items-center gap-3 px-6 py-5 rounded bg-white border border-red-300 shadow-lg">
|
||||
{# Overlay légal NON CONFORME (phase 3) — round 4 : STAMP huissier qui claque #}
|
||||
<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)]"
|
||||
: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="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 25 · Cloud Act américain</div>
|
||||
<div class="font-mono text-[10px] tracking-[0.20em] uppercase mt-1.5 text-red-500/70">Loi 25 · Cloud Act américain</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -612,10 +701,10 @@
|
||||
<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 #}
|
||||
{% for i in range(6) %}
|
||||
{# 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: {{ (-30 + i*12) }}px; --ly: -{{ 14 + (i % 3) * 8 }}px; animation-delay: {{ i * 0.20 }}s;"></span>
|
||||
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>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="px-2 py-0.5 rounded bg-red-50 border border-red-100">
|
||||
@@ -660,11 +749,18 @@
|
||||
style="background: radial-gradient(ellipse 80% 35% at 50% 0%, rgba(0,189,216,0.12) 0%, transparent 65%);"></div>
|
||||
|
||||
<div class="relative px-5 py-3 border-b border-brand-b1/15 flex items-center gap-2.5">
|
||||
<div class="relative w-4 h-4 flex items-center justify-center shrink-0">
|
||||
<div class="absolute inset-0 rounded-full bg-brand-b1/15 border border-brand-b1/30"></div>
|
||||
<div class="w-1.5 h-1.5 rounded-full bg-brand-b1"></div>
|
||||
</div>
|
||||
{# 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"
|
||||
: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 25 conforme</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="relative flex-1 flex flex-col items-center justify-center px-5 py-7 gap-5">
|
||||
@@ -725,20 +821,46 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Barre d'économies — apparaît avec phase 4 #}
|
||||
<div class="cycle-reveal-up mt-10 flex flex-wrap items-center gap-x-8 gap-y-3 justify-center"
|
||||
{# Section "Économies annuelles · 25 utilisateurs" — round 4 : 3 cards avec counter animation #}
|
||||
<div class="cycle-reveal-up mt-12"
|
||||
:class="phase >= 4 ? 'is-visible' : ''"
|
||||
style="transition-delay: 700ms;">
|
||||
<div class="flex items-center gap-2 text-[12px] text-brand-navy/55">
|
||||
<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.5 h-3.5 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>
|
||||
Économies annuelles · 25 utilisateurs
|
||||
<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>
|
||||
<span class="font-mono font-bold text-[10px] uppercase tracking-[0.22em] text-brand-navy/65">
|
||||
Économies annuelles · 25 utilisateurs
|
||||
</span>
|
||||
<span class="flex-1 h-px bg-brand-border max-w-[60px]" aria-hidden="true"></span>
|
||||
</div>
|
||||
{% for sav in [('3 924 $', 'vs Otter.ai'), ('6 924 $', 'vs MS Teams'), ('2 004 $', 'vs Sténographe')] %}
|
||||
<div class="flex items-baseline gap-1.5">
|
||||
<span class="font-black text-xl text-emerald-600">{{ sav[0] | safe }}</span>
|
||||
<span class="text-xs text-brand-navy/55">{{ sav[1] }}</span>
|
||||
|
||||
<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 924', 'label': 'vs Otter.ai', 'sub': 'IA cloud US'},
|
||||
{'icon': 'teams', 'val_prop': 'sav2', 'val_static': '6 924', 'label': 'vs MS Teams', 'sub': 'Copilot premium'},
|
||||
{'icon': 'scribe','val_prop': 'sav3', 'val_static': '2 004', 'label': 'vs Sténographe','sub': '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">
|
||||
{% 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>
|
||||
{% 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>
|
||||
{% 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>
|
||||
{% endif %}
|
||||
</span>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-baseline">
|
||||
{# 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 }}) + ' <span class="text-sm text-emerald-600/70 font-bold">$</span>'">{{ sav.val_static | safe }} $</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>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1768,11 +1890,13 @@
|
||||
|
||||
{# ===== CADRE RÉGLEMENTAIRE — Moniteur d'Interception ===== #}
|
||||
{# Source canonique : InnovA-AI/Website-Sanity/components/sections/dictai-contraste.tsx (REGS + MoniteurInterception)
|
||||
Animation : cycle automatique en 4 étapes (folder QC→US, alerte, HUD log, flash REGS séquentiel) + Alpine
|
||||
Cartographie 6 textes : Loi 25, Loi 96, Cloud Act, Guide IA Barreau, Cadre IA MCN, CAI #}
|
||||
Round 4 cinématique : radar sweep continu, 6 paquets data en flight QC→US (offset-path bezier),
|
||||
typewriter 3 lignes char-par-char, REGS reveal cascadé + glow rouge hover, verdict pulse + scan-line,
|
||||
grid pattern bg console. #}
|
||||
<style>
|
||||
/* Cadre — folder qui glisse de QC vers US */
|
||||
.cadre-folder { transition: left 1.4s cubic-bezier(0.4,0,0.2,1), color 0.4s, filter 0.4s; }
|
||||
/* Cadre — RADAR SWEEP circulaire continu (round 4) */
|
||||
@keyframes cadre-radar-sweep { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
|
||||
.cadre-radar { animation: cadre-radar-sweep 4s linear infinite; transform-origin: center; }
|
||||
|
||||
/* Cadre — pulse halo autour du folder en alerte */
|
||||
@keyframes cadre-folder-halo {
|
||||
@@ -1781,10 +1905,38 @@
|
||||
}
|
||||
.cadre-halo { animation: cadre-folder-halo 1.2s ease-out infinite; }
|
||||
|
||||
/* Cadre — DATA PACKET flight QC→US via offset-path bezier (round 4) */
|
||||
@keyframes cadre-packet-fly {
|
||||
0% { offset-distance: 0%; opacity: 0; transform: scale(0.7); }
|
||||
8% { opacity: 1; transform: scale(1); }
|
||||
85% { opacity: 1; transform: scale(1); }
|
||||
100% { offset-distance: 100%; opacity: 0; transform: scale(0.4); }
|
||||
}
|
||||
.cadre-packet {
|
||||
offset-path: path('M 0 24 Q 100 -24, 200 24');
|
||||
offset-rotate: auto;
|
||||
animation: cadre-packet-fly 2.6s ease-in-out infinite;
|
||||
will-change: offset-distance, opacity, transform;
|
||||
}
|
||||
|
||||
/* Cadre — packet trail (traînée fade rouge à l'arrivée) */
|
||||
@keyframes cadre-packet-arrive {
|
||||
0%, 70% { opacity: 0; transform: translate(-50%,-50%) scale(0.5); }
|
||||
80% { opacity: 0.85; transform: translate(-50%,-50%) scale(1.4); }
|
||||
100% { opacity: 0; transform: translate(-50%,-50%) scale(2.2); }
|
||||
}
|
||||
.cadre-packet-burst { animation: cadre-packet-arrive 2.6s ease-in-out infinite; }
|
||||
|
||||
/* Cadre — REGS row flash amber pendant le sweep */
|
||||
.cadre-reg { transition: border-color 180ms, background-color 180ms; }
|
||||
.cadre-reg { transition: border-color 220ms, background-color 220ms, box-shadow 220ms, transform 220ms; }
|
||||
.cadre-reg.is-flash { border-color: rgba(245,158,11,0.50) !important; background-color: rgba(245,158,11,0.06) !important; }
|
||||
.cadre-reg.is-flash .cadre-reg-label { color: #d97706 !important; }
|
||||
/* Hover red glow (round 4) */
|
||||
.cadre-reg:hover { box-shadow: 0 0 18px rgba(239,68,68,0.22); border-left-width: 3px !important; border-left-color: rgba(239,68,68,0.65) !important; transform: translateX(2px); }
|
||||
|
||||
/* Cadre — REGS reveal cascadé (round 4 : IntersectionObserver via Alpine `revealRegs`) */
|
||||
.cadre-reg-item { opacity: 0; transform: translateX(12px); transition: opacity 420ms ease-out, transform 420ms ease-out; }
|
||||
.cadre-reg-item.is-visible { opacity: 1; transform: translateX(0); }
|
||||
|
||||
/* Cadre — alerte clignotante footer */
|
||||
@keyframes cadre-blink {
|
||||
@@ -1800,10 +1952,37 @@
|
||||
}
|
||||
.cadre-caret { animation: cadre-caret 0.7s ease-in-out infinite; }
|
||||
|
||||
/* Cadre — VERDICT NON CONFORME pulse glow (round 4) */
|
||||
@keyframes cadre-verdict-pulse {
|
||||
0%, 100% { box-shadow: 0 0 0 0 rgba(239,68,68,0.50), inset 0 0 0 1px rgba(239,68,68,0.45); }
|
||||
50% { box-shadow: 0 0 18px 3px rgba(239,68,68,0.30), inset 0 0 0 1px rgba(239,68,68,0.85); }
|
||||
}
|
||||
.cadre-verdict-active { animation: cadre-verdict-pulse 2s ease-in-out infinite; }
|
||||
|
||||
/* Cadre — VERDICT scan-line traversante (round 4) */
|
||||
@keyframes cadre-scan-line {
|
||||
0% { transform: translateX(-110%); opacity: 0; }
|
||||
20% { opacity: 0.85; }
|
||||
80% { opacity: 0.85; }
|
||||
100% { transform: translateX(110%); opacity: 0; }
|
||||
}
|
||||
.cadre-scan-line { animation: cadre-scan-line 3s linear infinite; }
|
||||
|
||||
/* Cadre — typewriter caret (3e ligne en rouge avec glow) */
|
||||
.cadre-hud-line-3 { color: #ff5252 !important; text-shadow: 0 0 8px rgba(239,68,68,0.55); }
|
||||
|
||||
/* Mobile — désactiver radar + packets (CPU-intensive) */
|
||||
@media (max-width: 768px) {
|
||||
.cadre-radar, .cadre-packet, .cadre-packet-burst { display: none; }
|
||||
}
|
||||
|
||||
/* Reduced motion — TOUT figé */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.cadre-folder, .cadre-halo, .cadre-reg, .cadre-blink, .cadre-caret {
|
||||
animation: none !important; transition: none !important;
|
||||
.cadre-radar, .cadre-halo, .cadre-blink, .cadre-caret, .cadre-packet,
|
||||
.cadre-packet-burst, .cadre-verdict-active, .cadre-scan-line {
|
||||
animation: none !important;
|
||||
}
|
||||
.cadre-reg, .cadre-reg-item { transition: none !important; opacity: 1 !important; transform: none !important; }
|
||||
}
|
||||
</style>
|
||||
<section
|
||||
@@ -1811,32 +1990,62 @@
|
||||
aria-labelledby="cadre-title"
|
||||
x-data="{
|
||||
step: 0,
|
||||
hudLines: [],
|
||||
/* hudTyped[i] = sous-chaîne progressive de HUD[i] (typewriter char-by-char) */
|
||||
hudTyped: ['','',''],
|
||||
hudCursorLine: -1,
|
||||
flashIdx: -1,
|
||||
revealedRegs: [],
|
||||
timers: [],
|
||||
observer: null,
|
||||
HUD: ['> Interception IA détectée.', '> Données utilisées pour entraînement.', '> Statut : Violation Légale.'],
|
||||
typeLine(idx, cb) {
|
||||
const full = this.HUD[idx];
|
||||
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
if (reduced) { this.hudTyped[idx] = full; if (cb) cb(); return; }
|
||||
this.hudCursorLine = idx;
|
||||
let i = 0;
|
||||
const tick = () => {
|
||||
if (i > full.length) { if (cb) cb(); return; }
|
||||
this.hudTyped[idx] = full.slice(0, i);
|
||||
i++;
|
||||
this.timers.push(setTimeout(tick, 28));
|
||||
};
|
||||
tick();
|
||||
},
|
||||
runCycle() {
|
||||
this.step = 0; this.hudLines = []; this.flashIdx = -1;
|
||||
this.step = 0; this.hudTyped = ['','','']; this.hudCursorLine = -1; this.flashIdx = -1;
|
||||
this.timers.push(setTimeout(() => this.step = 1, 900));
|
||||
this.timers.push(setTimeout(() => this.step = 2, 2300));
|
||||
this.timers.push(setTimeout(() => {
|
||||
this.step = 3;
|
||||
this.HUD.forEach((line, i) => {
|
||||
this.timers.push(setTimeout(() => this.hudLines.push(line), i * 650));
|
||||
/* Typewriter chain : ligne 0 → 1 → 2 puis reset cursor */
|
||||
this.typeLine(0, () => {
|
||||
this.timers.push(setTimeout(() => this.typeLine(1, () => {
|
||||
this.timers.push(setTimeout(() => this.typeLine(2, () => {
|
||||
this.hudCursorLine = -1;
|
||||
}), 280));
|
||||
}), 280));
|
||||
});
|
||||
}, 2800));
|
||||
for (let i = 0; i < 6; i++) {
|
||||
this.timers.push(setTimeout(() => this.flashIdx = i, 4700 + i * 200));
|
||||
this.timers.push(setTimeout(() => this.flashIdx = i, 5400 + i * 200));
|
||||
}
|
||||
this.timers.push(setTimeout(() => this.flashIdx = -1, 4700 + 6 * 200 + 500));
|
||||
this.timers.push(setTimeout(() => this.runCycle(), 8500));
|
||||
this.timers.push(setTimeout(() => this.flashIdx = -1, 5400 + 6 * 200 + 500));
|
||||
this.timers.push(setTimeout(() => this.runCycle(), 9500));
|
||||
},
|
||||
revealRegsCascade() {
|
||||
[0,1,2,3,4,5].forEach((i) => {
|
||||
this.timers.push(setTimeout(() => {
|
||||
if (!this.revealedRegs.includes(i)) this.revealedRegs.push(i);
|
||||
}, i * 120));
|
||||
});
|
||||
},
|
||||
init() {
|
||||
this.observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(e => {
|
||||
if (e.isIntersecting && this.step === 0 && this.timers.length === 0) {
|
||||
this.timers.push(setTimeout(() => this.runCycle(), 300));
|
||||
this.timers.push(setTimeout(() => this.revealRegsCascade(), 500));
|
||||
this.observer.disconnect();
|
||||
}
|
||||
});
|
||||
@@ -1845,12 +2054,16 @@
|
||||
}
|
||||
}"
|
||||
>
|
||||
{# Round 4 : decorative grid pattern bg console-style 40×40 #}
|
||||
<div class="absolute inset-0 pointer-events-none" aria-hidden="true"
|
||||
style="background-image: radial-gradient(circle, rgba(11,15,26,0.04) 1px, transparent 1px); background-size: 28px 28px;"></div>
|
||||
style="background-image: linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px), radial-gradient(circle, rgba(11,15,26,0.045) 1px, transparent 1px); background-size: 40px 40px, 40px 40px, 28px 28px;"></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 text-amber-600 mb-4">⚠ CADRE RÉGLEMENTAIRE QUÉBEC</p>
|
||||
<p class="eyebrow text-amber-600 mb-4 inline-flex items-center gap-1.5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" class="w-3.5 h-3.5" 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>
|
||||
CADRE RÉGLEMENTAIRE QUÉBEC
|
||||
</p>
|
||||
<h2 id="cadre-title" class="text-[clamp(2rem,3vw,2.75rem)] font-black mb-4 text-brand-navy">
|
||||
Ce que vos outils actuels enfreignent <span class="grad-text">en secret</span>.
|
||||
</h2>
|
||||
@@ -1872,20 +2085,45 @@
|
||||
Moniteur d'Interception
|
||||
</span>
|
||||
<span x-show="step >= 2"
|
||||
class="ml-auto font-bold text-[9px] uppercase tracking-[0.18em] text-amber-600 cadre-blink"
|
||||
class="ml-auto inline-flex items-center gap-1 font-bold text-[9px] uppercase tracking-[0.18em] text-amber-600 cadre-blink"
|
||||
x-cloak>
|
||||
⚠ Alerte Active
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" class="w-2.5 h-2.5" 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>
|
||||
Alerte Active
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{# Body — 2 colonnes #}
|
||||
<div class="flex flex-col md:flex-row">
|
||||
|
||||
{# LEFT — animation track (~42%) #}
|
||||
<div class="relative md:w-[42%] px-5 py-5 overflow-hidden bg-brand-bg" style="min-height: 200px;">
|
||||
{# Grille bg #}
|
||||
{# LEFT — animation track (~42%) — round 4 : radar + data packets en flight #}
|
||||
<div class="relative md:w-[42%] px-5 py-5 overflow-hidden bg-brand-bg" style="min-height: 240px;" role="img" aria-label="Visualisation : radar de surveillance et paquets de données voix transférés du Québec aux États-Unis">
|
||||
{# Grille bg console #}
|
||||
<div class="absolute inset-0 pointer-events-none" aria-hidden="true"
|
||||
style="background-image: linear-gradient(rgba(11,15,26,0.04) 1px, transparent 1px), linear-gradient(90deg, rgba(11,15,26,0.04) 1px, transparent 1px); background-size: 20px 20px;"></div>
|
||||
style="background-image: linear-gradient(rgba(11,15,26,0.045) 1px, transparent 1px), linear-gradient(90deg, rgba(11,15,26,0.045) 1px, transparent 1px); background-size: 20px 20px;"></div>
|
||||
|
||||
{# Round 4 : Radar sweep — cercle vert avec ligne rotative balayant 360° #}
|
||||
<div class="absolute -top-8 -right-8 w-40 h-40 pointer-events-none opacity-50" aria-hidden="true">
|
||||
<svg viewBox="0 0 100 100" class="w-full h-full">
|
||||
<defs>
|
||||
<linearGradient id="cadre-radar-grad" x1="0" y1="0" x2="1" y2="0">
|
||||
<stop offset="0%" stop-color="rgba(34,197,94,0)"/>
|
||||
<stop offset="80%" stop-color="rgba(34,197,94,0.55)"/>
|
||||
<stop offset="100%" stop-color="rgba(34,197,94,0.85)"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<circle cx="50" cy="50" r="48" fill="none" stroke="rgba(34,197,94,0.20)" stroke-width="0.5"/>
|
||||
<circle cx="50" cy="50" r="32" fill="none" stroke="rgba(34,197,94,0.15)" stroke-width="0.4"/>
|
||||
<circle cx="50" cy="50" r="16" fill="none" stroke="rgba(34,197,94,0.12)" stroke-width="0.4"/>
|
||||
<line x1="2" y1="50" x2="98" y2="50" stroke="rgba(34,197,94,0.10)" stroke-width="0.3"/>
|
||||
<line x1="50" y1="2" x2="50" y2="98" stroke="rgba(34,197,94,0.10)" stroke-width="0.3"/>
|
||||
{# Sweep arm #}
|
||||
<g class="cadre-radar" style="transform-origin: 50px 50px;">
|
||||
<path d="M 50 50 L 98 50 A 48 48 0 0 0 84 16 Z" fill="url(#cadre-radar-grad)" opacity="0.45"/>
|
||||
<line x1="50" y1="50" x2="98" y2="50" stroke="rgba(34,197,94,0.85)" stroke-width="0.6"/>
|
||||
</g>
|
||||
<circle cx="50" cy="50" r="1.6" fill="rgba(34,197,94,0.85)"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{# Labels QC / US #}
|
||||
<div class="relative flex items-center justify-between mb-3">
|
||||
@@ -1898,14 +2136,15 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{# Track #}
|
||||
<div class="relative h-10">
|
||||
{# Track + folder + DATA PACKETS en flight (round 4) #}
|
||||
<div class="relative h-12">
|
||||
<div class="absolute top-0 bottom-0 transition-colors duration-300"
|
||||
:style="`left: 50%; width: 0; border-left: 1.5px dashed ${step >= 2 ? 'rgba(245,158,11,0.6)' : 'rgba(11,15,26,0.18)'};`"
|
||||
aria-hidden="true"></div>
|
||||
<div class="cadre-folder absolute top-1/2 -translate-y-1/2 -translate-x-1/2"
|
||||
:style="`left: ${step === 0 ? '6%' : '54%'}`">
|
||||
<span x-show="step >= 2" class="cadre-halo absolute inset-[-8px] rounded-full bg-amber-500/15" x-cloak></span>
|
||||
|
||||
{# Folder source (QC) #}
|
||||
<div class="absolute top-1/2 -translate-y-1/2 -translate-x-1/2"
|
||||
style="left: 6%; transition: color 0.4s, filter 0.4s;">
|
||||
<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="relative w-5 h-5 transition-colors duration-300"
|
||||
:class="step >= 2 ? 'text-amber-500' : 'text-brand-b1'"
|
||||
@@ -1914,20 +2153,55 @@
|
||||
<path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{# Folder destination (US) avec halo alerte #}
|
||||
<div class="absolute top-1/2 -translate-y-1/2 -translate-x-1/2"
|
||||
style="left: 94%;">
|
||||
<span x-show="step >= 2" class="cadre-halo absolute inset-[-8px] rounded-full bg-red-500/20" x-cloak aria-hidden="true"></span>
|
||||
<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="relative w-5 h-5 transition-colors duration-300"
|
||||
:class="step >= 2 ? 'text-red-500' : 'text-brand-navy/40'"
|
||||
:style="`filter: drop-shadow(0 0 ${step >= 2 ? '6px rgba(239,68,68,0.7)' : '0'})`"
|
||||
aria-hidden="true">
|
||||
<path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
|
||||
</svg>
|
||||
{# Burst rouge à l'arrivée des paquets #}
|
||||
<span x-show="step >= 2" class="cadre-packet-burst absolute top-1/2 left-1/2 w-4 h-4 rounded-full bg-red-500/40 pointer-events-none" x-cloak aria-hidden="true"></span>
|
||||
</div>
|
||||
|
||||
{# 6 PAQUETS DATA en flight QC→US (round 4 : offset-path bezier) #}
|
||||
<div x-show="step >= 1" class="absolute inset-0 pointer-events-none" x-cloak aria-hidden="true">
|
||||
{% for pi in range(6) %}
|
||||
<span class="cadre-packet absolute top-0 left-[6%] inline-flex items-center gap-1 px-1 py-0.5 rounded bg-red-500/85 text-white font-mono text-[8px] tracking-tighter shadow-[0_0_6px_rgba(239,68,68,0.6)]"
|
||||
style="animation-delay: {{ pi * 0.42 }}s;">
|
||||
<span class="w-1 h-1 rounded-full bg-white/80"></span>
|
||||
voice.wav
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# HUD panel #}
|
||||
{# HUD panel — typewriter char-by-char (round 4) #}
|
||||
<div x-show="step >= 3"
|
||||
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"
|
||||
class="mt-3 rounded px-3 py-2.5 font-mono"
|
||||
style="background: rgba(11,15,26,0.92); border: 1px solid rgba(245,158,11,0.30); font-size: 10px; color: #F59E0B; line-height: 1.65;"
|
||||
class="relative mt-3 rounded px-3 py-2.5 font-mono"
|
||||
style="background: rgba(11,15,26,0.94); border: 1px solid rgba(245,158,11,0.30); font-size: 10px; color: #F59E0B; line-height: 1.65; min-height: 70px;"
|
||||
role="log" aria-live="polite"
|
||||
x-cloak>
|
||||
<template x-for="(line, i) in hudLines" :key="i">
|
||||
<div x-html="line"></div>
|
||||
</template>
|
||||
<span x-show="hudLines.length < HUD.length" class="cadre-caret">▌</span>
|
||||
<div class="flex items-baseline">
|
||||
<span x-html="hudTyped[0]"></span>
|
||||
<span x-show="hudCursorLine === 0" class="cadre-caret ml-0.5">▮</span>
|
||||
</div>
|
||||
<div class="flex items-baseline" x-show="hudTyped[1].length > 0 || hudCursorLine === 1">
|
||||
<span x-html="hudTyped[1]"></span>
|
||||
<span x-show="hudCursorLine === 1" class="cadre-caret ml-0.5">▮</span>
|
||||
</div>
|
||||
<div class="flex items-baseline cadre-hud-line-3" x-show="hudTyped[2].length > 0 || hudCursorLine === 2">
|
||||
<span x-html="hudTyped[2]"></span>
|
||||
<span x-show="hudCursorLine === 2" class="cadre-caret ml-0.5">▮</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1945,7 +2219,9 @@
|
||||
{'label': 'Cadre IA — MCN', 'detail': 'Gouvernance IA pour organismes publics (déc. 2025, conformité 19 juin 2026)', 'href': 'https://www.tresor.gouv.qc.ca/', 'risk': False},
|
||||
{'label': 'CAI', 'detail': 'Commission d\'accès à l\'information — application active', 'href': 'https://www.cai.gouv.qc.ca/', 'risk': False}
|
||||
] %}
|
||||
<li role="listitem">
|
||||
<li role="listitem" class="cadre-reg-item"
|
||||
:class="revealedRegs.includes({{ loop.index0 }}) ? 'is-visible' : ''"
|
||||
style="transition-delay: {{ loop.index0 * 30 }}ms;">
|
||||
<a href="{{ reg.href }}" target="_blank" rel="noopener noreferrer"
|
||||
class="cadre-reg group flex items-start gap-2 rounded px-2.5 py-1.5 no-underline border focus-visible:outline-2 focus-visible:outline-amber-500 focus-visible:outline-offset-2"
|
||||
:class="flashIdx === {{ loop.index0 }} ? 'is-flash' : ''"
|
||||
@@ -1972,18 +2248,26 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Footer — verdict #}
|
||||
<div class="px-5 py-3 flex items-center gap-2.5 border-t border-brand-border bg-white">
|
||||
<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-4 h-4 transition-colors"
|
||||
:class="step >= 2 ? 'text-amber-500 cadre-blink' : 'text-brand-navy/35'"
|
||||
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>
|
||||
<span class="font-bold text-[10px] uppercase tracking-[0.20em] transition-colors"
|
||||
:class="step >= 2 ? 'text-amber-600 cadre-blink' : 'text-brand-navy/35'"
|
||||
style="text-shadow: 0 0 6px rgba(245,158,11,0.20);"
|
||||
aria-live="polite">
|
||||
<span x-text="step >= 2 ? 'NON CONFORME — Loi 25 · Cloud Act' : 'Surveillance active...'"></span>
|
||||
</span>
|
||||
{# Footer — verdict (round 4 : pulse glow rouge + scan-line traversante) #}
|
||||
<div class="relative px-5 py-3 border-t border-brand-border bg-white overflow-hidden">
|
||||
<div class="relative flex items-center gap-2.5 px-3 py-2 rounded transition-all duration-300"
|
||||
:class="step >= 2 ? 'cadre-verdict-active bg-red-50/40' : ''"
|
||||
style="border-radius: 4px;">
|
||||
<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-4 h-4 transition-colors"
|
||||
:class="step >= 2 ? 'text-red-500 cadre-blink' : 'text-brand-navy/35'"
|
||||
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>
|
||||
<span class="font-bold text-[10px] uppercase tracking-[0.20em] transition-colors"
|
||||
:class="step >= 2 ? 'text-red-600' : 'text-brand-navy/35'"
|
||||
style="text-shadow: 0 0 6px rgba(239,68,68,0.30);"
|
||||
aria-live="polite">
|
||||
<span x-text="step >= 2 ? 'NON CONFORME — Loi 25 · Cloud Act' : 'Surveillance active...'"></span>
|
||||
</span>
|
||||
{# Scan-line horizontale (round 4) #}
|
||||
<span x-show="step >= 2" x-cloak class="cadre-scan-line absolute inset-y-0 left-0 w-1/3 pointer-events-none"
|
||||
style="background: linear-gradient(90deg, transparent 0%, rgba(239,68,68,0.18) 50%, transparent 100%);"
|
||||
aria-hidden="true"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user