feat: add system architecture diagram and Port-Navalo use case

- Architecture section: inline SVG pipeline (terrain → réseau → cloud → users) with
  per-layer detail cards and stack pills.
- Use case: stylised Golfe du Morbihan map showing the three pilot rocks and their
  respective roles, alongside narrative and next-step roadmap teaser.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-21 07:32:31 +00:00
parent 17f4171f55
commit f832ed0c45
2 changed files with 363 additions and 0 deletions

View File

@@ -0,0 +1,200 @@
---
const layers = [
{
title: 'Terrain',
subtitle: 'Rochers connectés',
points: [
'Hydrophone calibré + capteurs modulaires (T°, O₂, pH, turbidité…)',
'SBC Linux edge-compute — FFT, SPL, moyennes glissantes',
'Stockage local si perte réseau · store-and-forward',
'Énergie : solaire + batterie LiFePO₄ · 3 à 5 jours dautonomie',
],
},
{
title: 'Réseau',
subtitle: 'Transport sécurisé',
points: [
'MQTT over TLS 1.3 · QoS 1 · reconnexion auto',
'VPN WireGuard — une clé par rocher, révocable',
'SIM M2M multi-opérateur · fallback HTTPS',
'LoRa inter-rochers optionnel — résilience locale',
],
},
{
title: 'Cloud',
subtitle: 'Ingestion &amp; traitement',
points: [
'Broker MQTT → ingestion → TimescaleDB',
'Moteur d\'alertes par seuils configurables',
'Agrégation temporelle, indicateurs réglementaires',
'Monitoring interne : batterie, signal, uptime',
],
},
{
title: 'Utilisateurs',
subtitle: 'Interfaces',
points: [
'Dashboard web · carte live, graphes, jauges par indicateur',
'Alertes SMS, email, webhook',
'API REST &amp; WebSocket pour chercheurs et tiers',
'Export CSV, rapport PDF, format Quadrige',
],
},
];
---
<section id="architecture" class="section">
<div class="container-narrow">
<div class="section-title-block">
<span class="eyebrow">Architecture système</span>
<h2 class="h-section mt-4">Du capteur sous-marin au tableau de bord, en temps réel.</h2>
<p class="lead mt-6">
Une pile pensée pour la sobriété : on traite sur le rocher, on transmet l'essentiel, on
stocke ce qu'il faut. Sécurité bout-en-bout, intéropérabilité à tous les étages.
</p>
</div>
<!-- SVG schema -->
<div class="relative overflow-hidden rounded-2xl border border-white/10 bg-abyss-900/40 p-6 md:p-10">
<div class="pointer-events-none absolute inset-0 caustic-bg"></div>
<svg
viewBox="0 0 1000 420"
class="relative mx-auto w-full max-w-5xl"
role="img"
aria-label="Schéma de l'architecture système NowYouSea : terrain, réseau, cloud, utilisateurs"
>
<defs>
<linearGradient id="flow" x1="0" x2="1" y1="0" y2="0">
<stop offset="0" stop-color="#38bdf8" stop-opacity=".1"/>
<stop offset="0.5" stop-color="#38bdf8" stop-opacity=".95"/>
<stop offset="1" stop-color="#06b6d4" stop-opacity=".1"/>
</linearGradient>
<linearGradient id="box" x1="0" x2="0" y1="0" y2="1">
<stop offset="0" stop-color="#0f2742"/>
<stop offset="1" stop-color="#0a1a2f"/>
</linearGradient>
</defs>
<!-- flow line -->
<path d="M60 210 H 940" stroke="url(#flow)" stroke-width="2" fill="none"/>
<!-- Terrain (rochers) -->
<g transform="translate(40 130)">
<rect width="200" height="160" rx="14" fill="url(#box)" stroke="#215889"/>
<text x="100" y="26" text-anchor="middle" fill="#38bdf8" font-size="12" font-weight="600" letter-spacing="2">TERRAIN</text>
<!-- 4 rock icons -->
<g fill="#143558" stroke="#1a4570" stroke-width="1">
<path d="M18 82 q 10 -22 26 -12 q 10 -20 28 -4 q 12 -18 24 -2 q 8 -8 18 0 v 34 H 18 z"/>
<path d="M30 130 q 14 -20 32 -8 q 16 -18 34 -4 q 14 -14 30 0 v 24 H 30 z"/>
</g>
<g fill="#06b6d4">
<circle cx="48" cy="92" r="2.5"/>
<circle cx="82" cy="88" r="2.5"/>
<circle cx="118" cy="90" r="2.5"/>
<circle cx="152" cy="94" r="2.5"/>
</g>
<text x="100" y="144" text-anchor="middle" fill="#94a3b8" font-size="10">Hydrophone · CTD · Edge · 4G</text>
</g>
<!-- Arrow -->
<g stroke="#38bdf8" fill="none" stroke-width="1.5" stroke-linecap="round">
<path d="M250 210 H 310" />
<path d="M304 205 l 6 5 l -6 5" />
</g>
<!-- Réseau -->
<g transform="translate(320 130)">
<rect width="180" height="160" rx="14" fill="url(#box)" stroke="#215889"/>
<text x="90" y="26" text-anchor="middle" fill="#38bdf8" font-size="12" font-weight="600" letter-spacing="2">RÉSEAU</text>
<g transform="translate(55 50)">
<circle cx="35" cy="30" r="20" fill="#0a1a2f" stroke="#38bdf8" stroke-width="1.2"/>
<path d="M15 30 a 20 20 0 0 1 40 0" fill="none" stroke="#38bdf8" stroke-width="1" opacity=".5"/>
<path d="M20 30 a 15 15 0 0 1 30 0" fill="none" stroke="#38bdf8" stroke-width="1" opacity=".7"/>
<circle cx="35" cy="30" r="3" fill="#38bdf8"/>
</g>
<text x="90" y="124" text-anchor="middle" fill="#94a3b8" font-size="10">MQTT · TLS · VPN WireGuard</text>
<text x="90" y="138" text-anchor="middle" fill="#64748b" font-size="9">SIM M2M · 4G · LoRa</text>
</g>
<!-- Arrow -->
<g stroke="#38bdf8" fill="none" stroke-width="1.5" stroke-linecap="round">
<path d="M510 210 H 570" />
<path d="M564 205 l 6 5 l -6 5" />
</g>
<!-- Cloud -->
<g transform="translate(580 130)">
<rect width="200" height="160" rx="14" fill="url(#box)" stroke="#215889"/>
<text x="100" y="26" text-anchor="middle" fill="#38bdf8" font-size="12" font-weight="600" letter-spacing="2">CLOUD</text>
<g transform="translate(30 46)">
<rect width="60" height="22" rx="4" fill="#050d1a" stroke="#215889"/>
<text x="30" y="15" text-anchor="middle" fill="#e0f2fe" font-size="9">MQTT</text>
<rect x="80" width="60" height="22" rx="4" fill="#050d1a" stroke="#215889"/>
<text x="110" y="15" text-anchor="middle" fill="#e0f2fe" font-size="9">Ingest</text>
<rect y="36" width="60" height="22" rx="4" fill="#050d1a" stroke="#215889"/>
<text x="30" y="51" text-anchor="middle" fill="#e0f2fe" font-size="9">TS DB</text>
<rect x="80" y="36" width="60" height="22" rx="4" fill="#050d1a" stroke="#215889"/>
<text x="110" y="51" text-anchor="middle" fill="#e0f2fe" font-size="9">Alerts</text>
<rect y="72" width="140" height="22" rx="4" fill="#050d1a" stroke="#215889"/>
<text x="70" y="87" text-anchor="middle" fill="#e0f2fe" font-size="9">API REST · WebSocket</text>
</g>
</g>
<!-- Arrow -->
<g stroke="#38bdf8" fill="none" stroke-width="1.5" stroke-linecap="round">
<path d="M790 210 H 850" />
<path d="M844 205 l 6 5 l -6 5" />
</g>
<!-- Users -->
<g transform="translate(860 130)">
<rect width="110" height="160" rx="14" fill="url(#box)" stroke="#215889"/>
<text x="55" y="26" text-anchor="middle" fill="#38bdf8" font-size="12" font-weight="600" letter-spacing="2">USERS</text>
<g transform="translate(17 44)" fill="none" stroke="#e0f2fe" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
<rect width="76" height="46" rx="3"/>
<path d="M6 40 h 64"/>
<circle cx="20" cy="22" r="3" fill="#38bdf8" stroke="none"/>
<path d="M30 28 l 10 -10 l 10 6 l 16 -14" stroke="#38bdf8"/>
</g>
<text x="55" y="124" text-anchor="middle" fill="#94a3b8" font-size="10">Dashboard · Alertes</text>
<text x="55" y="138" text-anchor="middle" fill="#64748b" font-size="9">API · Rapports</text>
</g>
</svg>
</div>
<!-- Layer cards -->
<div class="mt-10 grid gap-5 md:grid-cols-2 xl:grid-cols-4">
{layers.map((layer, i) => (
<div class="card h-full">
<div class="flex items-center justify-between">
<span class="font-display text-xs font-medium uppercase tracking-widest text-foam-100/45">Couche {i + 1}</span>
<span class="inline-block h-2 w-2 rounded-full bg-tide-400 shadow-[0_0_12px_rgba(56,189,248,0.8)]"></span>
</div>
<h3 class="mt-3 font-display text-lg font-semibold text-foam-50">{layer.title}</h3>
<p class="text-sm text-tide-300" set:html={layer.subtitle}></p>
<ul class="mt-4 space-y-2 text-sm text-foam-100/75">
{layer.points.map((p) => (
<li class="flex items-start gap-2">
<span class="mt-1.5 h-1 w-1 shrink-0 rounded-full bg-lagoon-400"></span>
<span set:html={p}></span>
</li>
))}
</ul>
</div>
))}
</div>
<!-- Stack pills -->
<div class="mt-10 flex flex-wrap items-center justify-center gap-2 text-xs text-foam-100/70">
{['Linux embarqué', 'MQTT', 'TLS 1.3', 'WireGuard', 'TimescaleDB', 'Grafana', 'REST', 'WebSocket', 'OAuth2 / OIDC', 'LoRa', 'IA embarquée'].map((t) => (
<span class="rounded-full border border-white/10 bg-white/5 px-3 py-1">{t}</span>
))}
</div>
</div>
</section>

View File

@@ -0,0 +1,163 @@
---
const rocks = [
{ id: 1, label: "Chenal d'entrée", role: "Bruit de transit · profil acoustique des navires · détection cétacés entrée/sortie du Golfe", color: '#38bdf8' },
{ id: 2, label: "Bassin portuaire", role: "Manœuvres, qualité de l'eau du port, détection rejets et hydrocarbures", color: '#22d3ee' },
{ id: 3, label: "Zone de référence", role: "Bruit de fond naturel — indispensable pour quantifier la pression anthropique", color: '#a78bfa' },
];
---
<section id="cas-dusage" class="section">
<div class="container-narrow">
<div class="section-title-block">
<span class="eyebrow">Cas d'usage</span>
<h2 class="h-section mt-4">Port-Navalo · entrée du Golfe du Morbihan.</h2>
<p class="lead mt-6">
Un des sites les plus denses de Bretagne sud : passage unique, courants puissants, trafic
mixte permanent, zone Natura 2000 et présence avérée de cétacés. Un site où mesurer,
c'est déjà agir.
</p>
</div>
<div class="grid gap-8 lg:grid-cols-2">
<!-- Map SVG -->
<div class="card overflow-hidden p-0">
<div class="relative aspect-[4/3] w-full">
<svg viewBox="0 0 520 400" class="absolute inset-0 h-full w-full" role="img" aria-label="Carte stylisée du Golfe du Morbihan avec les 3 rochers à Port-Navalo">
<defs>
<linearGradient id="sea" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#0a1a2f"/>
<stop offset="1" stop-color="#050d1a"/>
</linearGradient>
<linearGradient id="land" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#143558"/>
<stop offset="1" stop-color="#0f2742"/>
</linearGradient>
</defs>
<rect width="520" height="400" fill="url(#sea)"/>
<!-- Wave texture hints -->
<g stroke="#1a4570" stroke-width="0.6" fill="none" opacity=".55">
<path d="M0 70 q 40 -8 80 0 t 80 0 t 80 0 t 80 0 t 80 0 t 80 0 t 80 0"/>
<path d="M0 110 q 40 -8 80 0 t 80 0 t 80 0 t 80 0 t 80 0 t 80 0 t 80 0"/>
<path d="M0 330 q 40 -8 80 0 t 80 0 t 80 0 t 80 0 t 80 0 t 80 0 t 80 0"/>
</g>
<!-- Ocean (south) -->
<text x="60" y="370" fill="#94a3b8" font-size="10" letter-spacing="3">OCÉAN ATLANTIQUE</text>
<!-- Landmasses around Golfe du Morbihan (stylised) -->
<!-- North coast -->
<path d="M-10 -10 L 520 -10 L 520 120 Q 440 140 380 115 Q 320 150 260 120 Q 210 155 160 125 Q 110 145 70 120 Q 30 135 -10 110 Z"
fill="url(#land)" stroke="#215889" stroke-width="1"/>
<!-- Rhuys peninsula (south of Port-Navalo) -->
<path d="M-10 300 Q 80 285 170 295 Q 230 305 290 290 Q 330 282 360 275 Q 390 268 395 250
Q 370 252 350 240 Q 315 232 290 245 Q 250 255 200 248 Q 130 242 80 250 Q 20 258 -10 245 Z"
fill="url(#land)" stroke="#215889" stroke-width="1"/>
<!-- Ile-aux-Moines island -->
<ellipse cx="230" cy="170" rx="28" ry="14" fill="url(#land)" stroke="#215889"/>
<text x="230" y="172" text-anchor="middle" fill="#94a3b8" font-size="8">Île-aux-Moines</text>
<!-- Île d'Arz -->
<ellipse cx="300" cy="175" rx="20" ry="10" fill="url(#land)" stroke="#215889"/>
<text x="300" y="177" text-anchor="middle" fill="#94a3b8" font-size="8">Île d'Arz</text>
<!-- Port-Navalo label -->
<text x="310" y="263" fill="#e0f2fe" font-size="11" font-weight="600">Port-Navalo</text>
<!-- Currents arrows through chenal -->
<g stroke="#38bdf8" fill="none" stroke-width="1.3" opacity=".8">
<path d="M320 240 q -10 20 -12 30"/>
<path d="M317 268 l -4 -3 m 4 3 l 0 -4"/>
<path d="M345 240 q -8 22 -9 32"/>
<path d="M335 270 l -4 -3 m 4 3 l 0 -4"/>
</g>
<!-- Rocher 1 — chenal -->
<g transform="translate(330 255)">
<circle r="14" fill="#050d1a" stroke="#38bdf8" stroke-width="1.5"/>
<circle r="5" fill="#38bdf8"/>
<circle r="22" fill="none" stroke="#38bdf8" opacity="0.35"/>
<text x="22" y="4" fill="#e0f2fe" font-size="11" font-weight="600">1</text>
</g>
<!-- Rocher 2 — bassin -->
<g transform="translate(370 240)">
<circle r="12" fill="#050d1a" stroke="#22d3ee" stroke-width="1.5"/>
<circle r="4" fill="#22d3ee"/>
<text x="20" y="4" fill="#e0f2fe" font-size="11" font-weight="600">2</text>
</g>
<!-- Rocher 3 — reference -->
<g transform="translate(300 305)">
<circle r="12" fill="#050d1a" stroke="#a78bfa" stroke-width="1.5"/>
<circle r="4" fill="#a78bfa"/>
<text x="-26" y="4" fill="#e0f2fe" font-size="11" font-weight="600">3</text>
</g>
<!-- Compass -->
<g transform="translate(465 350)" fill="#38bdf8" opacity=".8">
<circle r="14" fill="none" stroke="currentColor" stroke-width="0.8"/>
<path d="M0 -12 L 3 0 L 0 4 L -3 0 Z" fill="currentColor"/>
<text y="-16" text-anchor="middle" font-size="8">N</text>
</g>
</svg>
</div>
<div class="border-t border-white/5 bg-abyss-900/70 p-4 text-xs text-foam-100/60 md:text-center">
Visualisation stylisée · position indicative des trois stations
</div>
</div>
<!-- Narrative -->
<div>
<div class="flex flex-wrap gap-4 text-sm">
<div class="rounded-lg border border-white/10 bg-white/5 px-4 py-3">
<div class="text-[11px] uppercase tracking-widest text-foam-100/50">Courants</div>
<div class="font-display text-lg font-semibold text-tide-400">6 9 nœuds</div>
</div>
<div class="rounded-lg border border-white/10 bg-white/5 px-4 py-3">
<div class="text-[11px] uppercase tracking-widest text-foam-100/50">Zonage</div>
<div class="font-display text-lg font-semibold text-tide-400">Natura 2000</div>
</div>
<div class="rounded-lg border border-white/10 bg-white/5 px-4 py-3">
<div class="text-[11px] uppercase tracking-widest text-foam-100/50">Faune</div>
<div class="font-display text-lg font-semibold text-tide-400">Grands dauphins</div>
</div>
</div>
<h3 class="mt-8 font-display text-xl font-semibold text-foam-50">
Un déploiement pilote à trois stations
</h3>
<p class="mt-3 text-sm leading-relaxed text-foam-100/75">
L'idée : mesurer chaque navire qui entre et sort du Golfe, comparer les niveaux sonores
à une zone de référence hors chenal, et croiser ces données avec la qualité de l'eau
du bassin portuaire. Un protocole directement inspiré du programme ECHO (port de
Vancouver), transposé à l'échelle d'un port français.
</p>
<ol class="mt-6 space-y-4">
{rocks.map((r) => (
<li class="flex items-start gap-4">
<span
class="mt-1 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full border text-sm font-semibold"
style={`border-color: ${r.color}; color: ${r.color}; box-shadow: 0 0 12px ${r.color}30`}
>
{r.id}
</span>
<div>
<div class="font-semibold text-foam-50">{r.label}</div>
<div class="mt-1 text-sm text-foam-100/70">{r.role}</div>
</div>
</li>
))}
</ol>
<div class="mt-8 rounded-xl border border-tide-400/20 bg-tide-500/5 p-5 text-sm leading-relaxed text-foam-100/80">
<strong class="text-tide-300">Et après ?</strong> Une fois le pilote validé, la logique
se réplique : Golfe du Morbihan (15+ ports), façade Nord Atlantique Manche Ouest,
puis extension à d'autres façades. Chaque rocher ajouté augmente la valeur du réseau.
</div>
</div>
</div>
</div>
</section>