wip: rescue source from photon
14 files (6 modified + 8 new) recovered from
photon /home/electron/electron-rare.github.io
during decom forensics.
New:
- src/components/BmuViewer.tsx (3D viewer)
- public/assets/models3d/{bmu_v2,bmu-assembly,
i2c_repeater,mosfet_switch}.glb
- src/pages/{conception,consulting,contact,
index-classic}.astro
- infra/Dockerfile.prebuild
- CLAUDE.md
Modified:
- public/sitemap.xml
- src/layouts/BaseLayout.astro
- src/pages/{formation,index,preview}.astro
- src/pages/sitemap.xml.ts (+5/-)
Snapshot mtime 2026-04-02 (photon last edits).
Recovery 2026-04-28 02:30 CEST.
This commit is contained in:
@@ -0,0 +1,73 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
Corporate site for **L'électron rare** (electronics consulting studio) — an Astro 5 SSR site with a React Three Fiber 3D product viewer as its centerpiece. Production URL: `https://www.lelectronrare.fr/`
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev # Astro dev server (default port 4321)
|
||||||
|
npm run build # Production build (SSR, outputs dist/)
|
||||||
|
npm run build:external # Build with PUBLIC_SITE_URL for external domain
|
||||||
|
npm run preview # Preview production build locally
|
||||||
|
npm run typecheck # astro check (TypeScript validation)
|
||||||
|
npm run storybook # Component dev on port 6006
|
||||||
|
npm run lab:dev # Sub-app (apps/lab-interactif) dev server
|
||||||
|
npm run tracking:check # Validate GA4 tracking events
|
||||||
|
npm run image:budget # Check image sizes against budget
|
||||||
|
```
|
||||||
|
|
||||||
|
**Docker build & deploy (from `/home/electron/electron-rare.github.io/infra/`):**
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.site.yml build electron-rare-site
|
||||||
|
docker compose -f docker-compose.site.yml up -d electron-rare-site
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
**Runtime:** Astro SSR with `@astrojs/node` adapter (standalone mode). The built site runs as `node dist/server/entry.mjs` on port 4321, behind Traefik.
|
||||||
|
|
||||||
|
**Styling:** Tailwind CSS 4 integrated via `@tailwindcss/vite` plugin — no separate tailwind.config. Uses `cva` + `clsx` + `tailwind-merge` for component variants.
|
||||||
|
|
||||||
|
**Path alias:** `@/*` maps to `src/*`.
|
||||||
|
|
||||||
|
### Site URL & Base Path
|
||||||
|
|
||||||
|
The site supports multiple deployment targets (GitHub Pages subpath, custom domain). `PUBLIC_SITE_URL` env var drives both `astro.config.mjs` and `src/lib/site.ts`. All internal links must use `withSiteBase()` from `src/lib/site.ts` to work across deployments.
|
||||||
|
|
||||||
|
### 3D Viewer (`src/components/BmuViewer.tsx`)
|
||||||
|
|
||||||
|
The main differentiating feature — a scroll-driven 3D PCB viewer on the `/preview/` page:
|
||||||
|
- Loads 5 GLTF models (BMU, I2C Repeater, MOSFET switches) from `public/assets/models3d/`
|
||||||
|
- 16+ named scroll sections, each defining camera position, rotation, focus card, and explode amount
|
||||||
|
- Scroll progress (0–1) interpolates between section states for smooth transitions
|
||||||
|
- Uses `@react-three/drei` Html component for interactive PCB label overlays
|
||||||
|
- Post-processing: Bloom + Vignette via `@react-three/postprocessing`
|
||||||
|
- VideoTexture meshes for PCB bottom views
|
||||||
|
- Brand color: `#5bd1d8` (cyan)
|
||||||
|
|
||||||
|
### Pages
|
||||||
|
|
||||||
|
- `index.astro` — Landing page with section components (Hero, About, CaseStudies, GraphicSprints, Faq, Contact)
|
||||||
|
- `preview.astro` — 3D scroll-driven viewer (1000vh scroll track, fixed overlays, dot navigation)
|
||||||
|
- `api/submit-lead.ts` — Server endpoint posting to Frappe CRM
|
||||||
|
- `formation.astro`, `portfolio.astro`, `ops.astro` — Content pages
|
||||||
|
|
||||||
|
### CRM Integration
|
||||||
|
|
||||||
|
Contact form submits to Frappe CRM via `src/pages/api/submit-lead.ts`. Requires `FRAPPE_URL`, `FRAPPE_API_KEY`, `FRAPPE_API_SECRET` env vars (set in `infra/.env`).
|
||||||
|
|
||||||
|
## CI/CD
|
||||||
|
|
||||||
|
Push to `main` triggers GitHub Actions (`.github/workflows/deploy-cloudflare.yml`):
|
||||||
|
1. `npm ci` + `npm run build` with `PUBLIC_SITE_URL=https://www.lelectronrare.fr/`
|
||||||
|
2. SCP `dist/` to Tower server
|
||||||
|
3. Docker container restart (node:22-alpine running `dist/server/entry.mjs`)
|
||||||
|
4. Health check on `http://127.0.0.1:4321/`
|
||||||
|
|
||||||
|
## GLB Pipeline
|
||||||
|
|
||||||
|
KiCad `.kicad_pcb` → STEP export → FreeCAD Docker tessellation → GLB with per-component materials and KiCad raytraced textures. Models stored in `public/assets/models3d/`.
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
FROM node:22-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm ci --omit=dev
|
||||||
|
COPY dist ./dist
|
||||||
|
ENV HOST=0.0.0.0
|
||||||
|
ENV PORT=4321
|
||||||
|
EXPOSE 4321
|
||||||
|
CMD ["node", "dist/server/entry.mjs"]
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+12
-6
@@ -1,20 +1,26 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
<url>
|
<url>
|
||||||
<loc>https://electron-rare.github.io/</loc>
|
<loc>https://www.lelectronrare.fr/</loc>
|
||||||
<lastmod>2026-03-02</lastmod>
|
<lastmod>2026-04-01</lastmod>
|
||||||
<changefreq>weekly</changefreq>
|
<changefreq>weekly</changefreq>
|
||||||
<priority>1.0</priority>
|
<priority>1.0</priority>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://electron-rare.github.io/formation/</loc>
|
<loc>https://www.lelectronrare.fr/formation/</loc>
|
||||||
<lastmod>2026-03-14</lastmod>
|
<lastmod>2026-04-01</lastmod>
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>0.9</priority>
|
<priority>0.9</priority>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://electron-rare.github.io/mentions-legales/</loc>
|
<loc>https://www.lelectronrare.fr/preview/</loc>
|
||||||
<lastmod>2026-03-14</lastmod>
|
<lastmod>2026-04-01</lastmod>
|
||||||
|
<changefreq>weekly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://www.lelectronrare.fr/mentions-legales/</loc>
|
||||||
|
<lastmod>2026-04-01</lastmod>
|
||||||
<changefreq>yearly</changefreq>
|
<changefreq>yearly</changefreq>
|
||||||
<priority>0.3</priority>
|
<priority>0.3</priority>
|
||||||
</url>
|
</url>
|
||||||
|
|||||||
@@ -0,0 +1,586 @@
|
|||||||
|
import { useRef, Suspense, useMemo, useEffect, useState } from 'react';
|
||||||
|
import { Canvas, useFrame, useThree } from '@react-three/fiber';
|
||||||
|
import { useGLTF, Environment, ContactShadows, Html } from '@react-three/drei';
|
||||||
|
import { EffectComposer, Bloom, Vignette } from '@react-three/postprocessing';
|
||||||
|
import * as THREE from 'three';
|
||||||
|
|
||||||
|
const MODELS_BASE = '/assets/models3d';
|
||||||
|
const S = 0.1;
|
||||||
|
|
||||||
|
function fc2three(fx: number, fy: number, fz: number): [number, number, number] {
|
||||||
|
return [fx * S, fz * S, -fy * S];
|
||||||
|
}
|
||||||
|
|
||||||
|
// explodeDelay: 0-1 stagger (0=moves first, 1=moves last)
|
||||||
|
// explodeRotTarget: rotation when fully exploded (null = keep original)
|
||||||
|
const CARDS_DEF = [
|
||||||
|
{ file: `${MODELS_BASE}/bmu_v2.glb`, name: 'BMU v2',
|
||||||
|
position: fc2three(0, 0, 0), rotation: [-Math.PI / 2, 0, 0] as [number, number, number],
|
||||||
|
explodeDir: [0, 0.8, 0] as [number, number, number], explodeDist: 1.5,
|
||||||
|
explodeDelay: 0, explodeRotTarget: null as [number, number, number] | null },
|
||||||
|
{ file: `${MODELS_BASE}/i2c_repeater.glb`, name: 'I2C Repeater',
|
||||||
|
position: fc2three(-52, 33, 4), rotation: [-Math.PI / 2, 0, 0] as [number, number, number],
|
||||||
|
explodeDir: [-0.8, 0.2, -0.6] as [number, number, number], explodeDist: 2.0,
|
||||||
|
explodeDelay: 0.15, explodeRotTarget: null as [number, number, number] | null },
|
||||||
|
{ file: `${MODELS_BASE}/mosfet_switch.glb`, name: 'Mosfet #1',
|
||||||
|
position: fc2three(23.2, 20, -11.2), rotation: [Math.PI / 2, 0, Math.PI] as [number, number, number],
|
||||||
|
explodeDir: [0.5, -0.8, 0.4] as [number, number, number], explodeDist: 4.0,
|
||||||
|
explodeDelay: 0.25,
|
||||||
|
explodeRotTarget: [0, 0, Math.PI] as [number, number, number] },
|
||||||
|
{ file: `${MODELS_BASE}/mosfet_switch.glb`, name: 'Mosfet #2',
|
||||||
|
position: fc2three(-15.56, 20, -11.16), rotation: [Math.PI / 2, 0, Math.PI] as [number, number, number],
|
||||||
|
explodeDir: [-0.2, -0.8, 0.5] as [number, number, number], explodeDist: 4.0,
|
||||||
|
explodeDelay: 0.35,
|
||||||
|
explodeRotTarget: [0, 0, Math.PI] as [number, number, number] },
|
||||||
|
{ file: `${MODELS_BASE}/mosfet_switch.glb`, name: 'Mosfet #3',
|
||||||
|
position: fc2three(-54.72, 20, -11.12), rotation: [Math.PI / 2, 0, Math.PI] as [number, number, number],
|
||||||
|
explodeDir: [-0.6, -0.8, 0.3] as [number, number, number], explodeDist: 4.0,
|
||||||
|
explodeDelay: 0.45,
|
||||||
|
explodeRotTarget: [0, 0, Math.PI] as [number, number, number] },
|
||||||
|
{ file: `${MODELS_BASE}/mosfet_switch.glb`, name: 'Mosfet #4',
|
||||||
|
position: fc2three(62.12, 20, -11.16), rotation: [Math.PI / 2, 0, Math.PI] as [number, number, number],
|
||||||
|
explodeDir: [0.7, -0.8, 0.2] as [number, number, number], explodeDist: 4.0,
|
||||||
|
explodeDelay: 0.55,
|
||||||
|
explodeRotTarget: [0, 0, Math.PI] as [number, number, number] },
|
||||||
|
];
|
||||||
|
|
||||||
|
CARDS_DEF.forEach((c) => useGLTF.preload(c.file));
|
||||||
|
|
||||||
|
/* ── Sections ── */
|
||||||
|
// Camera positions are relative: offset from card center when focused
|
||||||
|
// 'focus' = which card index camera should look at (null = assembly center)
|
||||||
|
interface Section {
|
||||||
|
id: string; start: number; end: number;
|
||||||
|
camOffset: [number, number, number]; // camera position offset from target
|
||||||
|
camOffsetEnd?: [number, number, number];
|
||||||
|
targetShift?: [number, number, number]; // shift lookAt from card center (start)
|
||||||
|
targetShiftEnd?: [number, number, number]; // shift lookAt (end) — for dolly movement
|
||||||
|
focus: number | null; // card index to focus on, null = center
|
||||||
|
focusEnd?: number | null;
|
||||||
|
explode: number; explodeEnd?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SECTIONS: Section[] = [
|
||||||
|
// 1. HERO — vue large, focus sur l'assemblage (card 0 = BMU)
|
||||||
|
{ id: 'hero', start: 0.00, end: 0.10,
|
||||||
|
camOffset: [2.0, 1.5, 2.0], camOffsetEnd: [1.5, 1.2, 1.5], focus: 0, explode: 0 },
|
||||||
|
// 1→2 TRANSITION — zoom progressif vers la zone I2C (haut-gauche)
|
||||||
|
{ id: 'hero-to-exp', start: 0.10, end: 0.16,
|
||||||
|
camOffset: [1.5, 1.2, 1.5], camOffsetEnd: [0.6, 0.7, 0.7],
|
||||||
|
targetShift: [0, 0, 0], targetShiftEnd: [-0.7, 0.35, 0],
|
||||||
|
focus: 0, explode: 0 },
|
||||||
|
// 2a. Électronique — vise la zone I2C sur la BMU
|
||||||
|
{ id: 'exp-elec', start: 0.16, end: 0.20,
|
||||||
|
camOffset: [0.4, 0.6, 0.5], camOffsetEnd: [0.2, 0.5, 0.4],
|
||||||
|
targetShift: [-0.7, 0.35, 0],
|
||||||
|
focus: 0, explode: 0 },
|
||||||
|
// 2b. BAS — tuile card-local [0.85, 0, 0.45] → Rx(-π/2) → [0.85, 0.45, 0]
|
||||||
|
{ id: 'exp-auto', start: 0.20, end: 0.24,
|
||||||
|
camOffset: [-0.3, 0.35, -0.2], camOffsetEnd: [-0.15, 0.3, -0.15],
|
||||||
|
targetShift: [0.85, 0.45, 0],
|
||||||
|
focus: 0, explode: 0 },
|
||||||
|
// 2c. HAUT — tuile card-local [-0.5, 0, -0.45] → Rx(-π/2) → [-0.5, -0.45, 0]
|
||||||
|
{ id: 'exp-energie', start: 0.24, end: 0.28,
|
||||||
|
camOffset: [0.25, 0.35, 0.3], camOffsetEnd: [0.12, 0.3, 0.2],
|
||||||
|
targetShift: [-0.5, -0.45, 0],
|
||||||
|
focus: 0, explode: 0 },
|
||||||
|
// 2d. BAS — tuile card-local [-0.85, 0, 0.45] → Rx(-π/2) → [-0.85, 0.45, 0]
|
||||||
|
{ id: 'exp-dispositifs', start: 0.28, end: 0.31,
|
||||||
|
camOffset: [0.3, 0.35, -0.2], camOffsetEnd: [0.15, 0.3, -0.1],
|
||||||
|
targetShift: [-0.85, 0.45, 0],
|
||||||
|
focus: 0, explode: 0 },
|
||||||
|
// 2e. HAUT — tuile card-local [0.3, 0, -0.45] → Rx(-π/2) → [0.3, -0.45, 0]
|
||||||
|
{ id: 'exp-consulting', start: 0.31, end: 0.34,
|
||||||
|
camOffset: [-0.2, 0.35, 0.3], camOffsetEnd: [0.1, 0.3, 0.2],
|
||||||
|
targetShift: [0.3, -0.45, 0],
|
||||||
|
focus: 0, explode: 0 },
|
||||||
|
// 3a. RECUL — rapide
|
||||||
|
{ id: 'eclate-recul', start: 0.34, end: 0.36,
|
||||||
|
camOffset: [0.0, 1.0, 0.8], camOffsetEnd: [1.0, 3.0, 4.0], focus: 0, focusEnd: null,
|
||||||
|
explode: 0 },
|
||||||
|
// 3b. SÉPARATION — rapide
|
||||||
|
{ id: 'eclate-sep', start: 0.36, end: 0.39,
|
||||||
|
camOffset: [1.0, 3.0, 4.0], camOffsetEnd: [1.6, 3.6, 4.4], focus: null,
|
||||||
|
explode: 0, explodeEnd: 0.7 },
|
||||||
|
// 3c. MISE À 90° — rapide
|
||||||
|
{ id: 'eclate-plat', start: 0.39, end: 0.41,
|
||||||
|
camOffset: [1.6, 3.6, 4.4], camOffsetEnd: [2.0, 3.6, 5.0], focus: null,
|
||||||
|
explode: 0.7, explodeEnd: 1 },
|
||||||
|
// 4. CAS — recul caméra, vue des 4 mosfets, tuiles + médias visibles
|
||||||
|
{ id: 'cas', start: 0.41, end: 0.65,
|
||||||
|
camOffset: [0.5, -0.5, -3.0], camOffsetEnd: [-0.5, -0.3, -2.5], focus: null, explode: 1 },
|
||||||
|
// 7a. MEDIA — vidéo 1, caméra de l'autre côté (Z négatif)
|
||||||
|
{ id: 'media', start: 0.65, end: 0.69,
|
||||||
|
camOffset: [0.2, 0.0, -1.0], camOffsetEnd: [0.0, 0.0, -0.8], focus: 2, explode: 1 },
|
||||||
|
// 7b. MEDIA — vidéo 2
|
||||||
|
{ id: 'media', start: 0.69, end: 0.73,
|
||||||
|
camOffset: [0.2, 0.0, -1.0], camOffsetEnd: [-0.1, 0.0, -0.8], focus: 3, explode: 1 },
|
||||||
|
// 7c. MEDIA — vidéo 3
|
||||||
|
{ id: 'media', start: 0.73, end: 0.77,
|
||||||
|
camOffset: [-0.1, 0.0, -1.0], camOffsetEnd: [-0.3, 0.0, -0.8], focus: 4, explode: 1 },
|
||||||
|
// 7d. MEDIA — carrousel photos
|
||||||
|
{ id: 'media', start: 0.77, end: 0.81,
|
||||||
|
camOffset: [-0.2, 0.0, -1.0], camOffsetEnd: [0.1, 0.0, -0.8], focus: 5, explode: 1 },
|
||||||
|
// 8. FORMATS
|
||||||
|
{ id: 'formats', start: 0.81, end: 0.91,
|
||||||
|
camOffset: [2.4, 3.0, 4.0], camOffsetEnd: [1.6, 2.4, 3.0], focus: null, explode: 1, explodeEnd: 0 },
|
||||||
|
// 9. CONTACT — zoom serré sur la tuile contact au centre de la BMU
|
||||||
|
{ id: 'contact', start: 0.91, end: 1.00,
|
||||||
|
camOffset: [0.15, 0.3, 0.2], camOffsetEnd: [0.0, 0.25, 0.15], focus: 0, explode: 0 },
|
||||||
|
];
|
||||||
|
|
||||||
|
/* ── Tuiles plaquées sur les faces PCB ── */
|
||||||
|
interface PcbLabel {
|
||||||
|
section: string; cardIndex: number;
|
||||||
|
localOffset: [number, number, number];
|
||||||
|
title: string; sub?: string; color?: string;
|
||||||
|
href?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PCB_LABELS: PcbLabel[] = [
|
||||||
|
// Expertise — alternance haut/bas du PCB BMU
|
||||||
|
// 1. HAUT — côté I2C (card 1)
|
||||||
|
{ section: 'exp-elec', cardIndex: 1, localOffset: [0.0, 0.005, 0.0],
|
||||||
|
title: 'Électronique spécifique', sub: 'Cartes · Interfaces · Capteurs · Alimentation' },
|
||||||
|
// 2. BAS — bas-droit BMU
|
||||||
|
{ section: 'exp-auto', cardIndex: 0, localOffset: [0.85, 0.005, 0.45],
|
||||||
|
title: 'Instrumentation & Automatisme', sub: 'Bancs · Automates · Variateurs · Protocoles' },
|
||||||
|
// 3. HAUT — haut BMU, à droite de I2C
|
||||||
|
{ section: 'exp-energie', cardIndex: 0, localOffset: [-0.5, 0.005, -0.45],
|
||||||
|
title: 'Énergie & Stockage', sub: 'Batterie · Conversion · Supervision · Télémétrie' },
|
||||||
|
// 4. BAS — bas-gauche BMU
|
||||||
|
{ section: 'exp-dispositifs', cardIndex: 0, localOffset: [-0.85, 0.005, 0.45],
|
||||||
|
title: 'Dispositifs pour le réel', sub: 'Audio · LED · Scène · Robustesse terrain' },
|
||||||
|
// 5. HAUT — centre-haut BMU
|
||||||
|
{ section: 'exp-consulting', cardIndex: 0, localOffset: [0.3, 0.005, -0.45],
|
||||||
|
title: 'Consulting & Formation', sub: 'Audit · Transfert · Pédagogie · Projet' },
|
||||||
|
// 6-9. CAS — les 4 tuiles visibles en même temps, face bottom des mosfets
|
||||||
|
{ section: 'cas', cardIndex: 2, localOffset: [0.0, 0.0, -0.08],
|
||||||
|
title: 'Industries créatives', sub: 'Audio embarqué · Batterie LiFePO4 · KXKM' },
|
||||||
|
{ section: 'cas', cardIndex: 3, localOffset: [0.0, 0.0, -0.08],
|
||||||
|
title: 'Industrie', sub: 'Production V3.2 · KXKM ESP32 → STM32F030' },
|
||||||
|
{ section: 'cas', cardIndex: 4, localOffset: [0.0, 0.0, -0.08],
|
||||||
|
title: 'Formation', sub: 'PCB · KiCad · µC' },
|
||||||
|
{ section: 'cas', cardIndex: 5, localOffset: [0.0, 0.0, -0.08],
|
||||||
|
title: 'Service', sub: 'Mise en service · Maintenance · Support terrain' },
|
||||||
|
// 9. Conception — Mosfet #1 (card 2)
|
||||||
|
{ section: 'formats', cardIndex: 2, localOffset: [0.0, 0.005, 0.0],
|
||||||
|
title: 'Conception', sub: 'Schéma · PCB · Firmware · Validation',
|
||||||
|
href: '/conception/' },
|
||||||
|
// 10. Formation — Mosfet #2 (card 3)
|
||||||
|
{ section: 'formats', cardIndex: 3, localOffset: [0.0, 0.005, 0.0],
|
||||||
|
title: 'Formation', sub: 'PCB · KiCad · µC · Soudure',
|
||||||
|
href: '/formation/' },
|
||||||
|
// 11. Consulting — Mosfet #3 (card 4)
|
||||||
|
{ section: 'formats', cardIndex: 4, localOffset: [0.0, 0.005, 0.0],
|
||||||
|
title: 'Consulting', sub: 'Audit · Diagnostic · Transfert',
|
||||||
|
href: '/consulting/' },
|
||||||
|
// 12. Contact — sur BMU (card 0)
|
||||||
|
{ section: 'contact', cardIndex: 0, localOffset: [0.0, 0.005, 0.0],
|
||||||
|
title: 'Parlons de votre projet', sub: 'Premier échange sans engagement',
|
||||||
|
href: '/contact/' },
|
||||||
|
];
|
||||||
|
|
||||||
|
/* ── Tuile plaquée sur la face du PCB ── */
|
||||||
|
function PcbTile({ offset, title, sub, visible, section, href }: {
|
||||||
|
offset: [number, number, number];
|
||||||
|
title: string; sub?: string; color?: string; visible: boolean; section?: string; href?: string;
|
||||||
|
}) {
|
||||||
|
const isContact = section === 'contact';
|
||||||
|
const isClickable = !!href || isContact;
|
||||||
|
const linkHref = isContact ? undefined : href;
|
||||||
|
|
||||||
|
const isBig = isContact;
|
||||||
|
|
||||||
|
const content = (
|
||||||
|
<div style={{
|
||||||
|
minWidth: isBig ? '340px' : '260px',
|
||||||
|
padding: isBig ? '24px 36px' : '14px 24px',
|
||||||
|
textAlign: 'center',
|
||||||
|
background: isClickable ? 'rgba(0,0,0,0.7)' : 'rgba(0,0,0,0.55)',
|
||||||
|
border: isClickable ? '2px solid #5bd1d8' : '1.5px solid rgba(255,255,255,0.4)',
|
||||||
|
borderRadius: isClickable ? '12px' : '8px',
|
||||||
|
backdropFilter: 'blur(4px)',
|
||||||
|
cursor: isClickable ? 'pointer' : 'default',
|
||||||
|
}}>
|
||||||
|
<div style={{
|
||||||
|
fontSize: isBig ? '24px' : '18px',
|
||||||
|
fontWeight: 800,
|
||||||
|
color: isClickable ? '#5bd1d8' : '#fff',
|
||||||
|
letterSpacing: '0.05em', textTransform: 'uppercase',
|
||||||
|
fontFamily: "'Courier New', monospace",
|
||||||
|
}}>{title}</div>
|
||||||
|
{sub && <div style={{
|
||||||
|
fontSize: isBig ? '14px' : '12px',
|
||||||
|
color: 'rgba(255,255,255,0.55)',
|
||||||
|
marginTop: isBig ? '10px' : '6px',
|
||||||
|
fontFamily: "'Courier New', monospace", letterSpacing: '0.03em',
|
||||||
|
}}>{sub}</div>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Html position={offset} center distanceFactor={isBig ? 0.4 : 0.8}
|
||||||
|
style={{
|
||||||
|
opacity: visible ? 1 : 0,
|
||||||
|
transition: 'opacity 0.5s',
|
||||||
|
pointerEvents: isClickable && visible ? 'auto' : 'none',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
}}>
|
||||||
|
{isContact ? (
|
||||||
|
<div onClick={() => window.dispatchEvent(new CustomEvent('open-contact-drawer', { detail: { source: 'pcb_contact' } }))}
|
||||||
|
style={{ textDecoration: 'none', display: 'block' }}>{content}</div>
|
||||||
|
) : linkHref ? (
|
||||||
|
<a href={linkHref} style={{ textDecoration: 'none', display: 'block' }}>{content}</a>
|
||||||
|
) : content}
|
||||||
|
</Html>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Card ── */
|
||||||
|
function CardModel({ file }: { file: string }) {
|
||||||
|
const { scene } = useGLTF(file);
|
||||||
|
const cloned = useMemo(() => {
|
||||||
|
const c = scene.clone(true);
|
||||||
|
c.traverse((child) => { if (child instanceof THREE.Mesh && child.material) child.material.envMapIntensity = 1.5; });
|
||||||
|
return c;
|
||||||
|
}, [scene]);
|
||||||
|
return <primitive object={cloned} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Video plane on PCB bottom (real 3D mesh with VideoTexture) ── */
|
||||||
|
function PcbVideoMesh({ offset, src, visible }: {
|
||||||
|
offset: [number, number, number]; src: string; visible: boolean;
|
||||||
|
}) {
|
||||||
|
const meshRef = useRef<THREE.Mesh>(null);
|
||||||
|
const videoRef = useRef<HTMLVideoElement | null>(null);
|
||||||
|
const texRef = useRef<THREE.VideoTexture | null>(null);
|
||||||
|
|
||||||
|
// Mosfet card local space: ~0.38 x 0.86 (XZ), Y = thickness
|
||||||
|
// Plane size: cover most of the bottom face
|
||||||
|
const planeW = 1.9, planeH = 4.275;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const video = document.createElement('video');
|
||||||
|
video.src = src;
|
||||||
|
video.crossOrigin = 'anonymous';
|
||||||
|
video.loop = true;
|
||||||
|
video.muted = true;
|
||||||
|
video.playsInline = true;
|
||||||
|
video.preload = 'metadata';
|
||||||
|
videoRef.current = video;
|
||||||
|
|
||||||
|
const tex = new THREE.VideoTexture(video);
|
||||||
|
tex.minFilter = THREE.LinearFilter;
|
||||||
|
tex.magFilter = THREE.LinearFilter;
|
||||||
|
tex.colorSpace = THREE.SRGBColorSpace;
|
||||||
|
texRef.current = tex;
|
||||||
|
|
||||||
|
return () => { video.pause(); video.src = ''; tex.dispose(); };
|
||||||
|
}, [src]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!videoRef.current) return;
|
||||||
|
if (visible) { videoRef.current.play().catch(() => {}); }
|
||||||
|
else { videoRef.current.pause(); }
|
||||||
|
}, [visible]);
|
||||||
|
|
||||||
|
useFrame(() => {
|
||||||
|
if (meshRef.current && texRef.current) {
|
||||||
|
(meshRef.current.material as THREE.MeshStandardMaterial).opacity = visible ? 1 : 0;
|
||||||
|
texRef.current.needsUpdate = visible;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<mesh ref={meshRef} position={offset} rotation={[0, Math.PI, 0]} scale={[-1, -1, 1]}>
|
||||||
|
<planeGeometry args={[planeW, planeH]} />
|
||||||
|
<meshStandardMaterial
|
||||||
|
map={texRef.current}
|
||||||
|
transparent
|
||||||
|
opacity={0}
|
||||||
|
emissive="#ffffff"
|
||||||
|
emissiveIntensity={0.3}
|
||||||
|
emissiveMap={texRef.current}
|
||||||
|
side={THREE.DoubleSide}
|
||||||
|
/>
|
||||||
|
</mesh>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Photo carousel plane on PCB bottom (real 3D mesh with swapping texture) ── */
|
||||||
|
const CAROUSEL_PHOTOS = [
|
||||||
|
'/assets/photos/pcb-teensy-led-kxkm.webp',
|
||||||
|
'/assets/photos/pcb-verso-soudures.webp',
|
||||||
|
'/assets/photos/soudure-pcb-composants.webp',
|
||||||
|
'/assets/photos/bench-bms-batteries-xt60.webp',
|
||||||
|
'/assets/photos/automate-siemens-s7.webp',
|
||||||
|
'/assets/photos/armoire-automate-schneider.webp',
|
||||||
|
'/assets/photos/oscilloscope-philips-vintage.webp',
|
||||||
|
'/assets/photos/workspace-dev-tektronix.webp',
|
||||||
|
'/assets/photos/platine-automate-cablage.webp',
|
||||||
|
'/assets/photos/bench-ampli-oscilloscope.webp',
|
||||||
|
'/assets/photos/pcb-produit-embarque.webp',
|
||||||
|
'/assets/photos/portrait-bench.webp',
|
||||||
|
];
|
||||||
|
|
||||||
|
function PcbCarouselMesh({ offset, visible }: {
|
||||||
|
offset: [number, number, number]; visible: boolean;
|
||||||
|
}) {
|
||||||
|
const meshRef = useRef<THREE.Mesh>(null);
|
||||||
|
const textures = useRef<THREE.Texture[]>([]);
|
||||||
|
const [idx, setIdx] = useState(0);
|
||||||
|
const autoTimer = useRef(0);
|
||||||
|
const planeW = 1.9, planeH = 4.275;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loader = new THREE.TextureLoader();
|
||||||
|
textures.current = CAROUSEL_PHOTOS.map(url => {
|
||||||
|
const t = loader.load(url);
|
||||||
|
t.colorSpace = THREE.SRGBColorSpace;
|
||||||
|
t.minFilter = THREE.LinearFilter;
|
||||||
|
return t;
|
||||||
|
});
|
||||||
|
return () => textures.current.forEach(t => t.dispose());
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Auto-advance every 3s when visible
|
||||||
|
useFrame((_, delta) => {
|
||||||
|
if (!meshRef.current) return;
|
||||||
|
const mat = meshRef.current.material as THREE.MeshStandardMaterial;
|
||||||
|
mat.opacity = visible ? 1 : 0;
|
||||||
|
if (textures.current[idx]) {
|
||||||
|
mat.map = textures.current[idx];
|
||||||
|
mat.emissiveMap = textures.current[idx];
|
||||||
|
mat.needsUpdate = true;
|
||||||
|
}
|
||||||
|
if (visible) {
|
||||||
|
autoTimer.current += delta;
|
||||||
|
if (autoTimer.current > 3) {
|
||||||
|
autoTimer.current = 0;
|
||||||
|
setIdx(i => (i + 1) % CAROUSEL_PHOTOS.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<mesh ref={meshRef} position={offset} rotation={[0, Math.PI, 0]} scale={[-1, -1, 1]}>
|
||||||
|
<planeGeometry args={[planeW, planeH]} />
|
||||||
|
<meshStandardMaterial
|
||||||
|
transparent
|
||||||
|
opacity={0}
|
||||||
|
emissive="#ffffff"
|
||||||
|
emissiveIntensity={0.3}
|
||||||
|
side={THREE.DoubleSide}
|
||||||
|
/>
|
||||||
|
</mesh>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Media definitions for mosfet bottoms ── */
|
||||||
|
const MOSFET_VIDEOS = [
|
||||||
|
{ cardIndex: 2, src: '/assets/videos/video-test-prototype.mp4' },
|
||||||
|
{ cardIndex: 3, src: '/assets/videos/video-bench-electronique.mp4' },
|
||||||
|
{ cardIndex: 4, src: '/assets/videos/video-atelier-terrain.mp4' },
|
||||||
|
];
|
||||||
|
const MOSFET_CAROUSEL_CARD = 5; // mosfet #4
|
||||||
|
|
||||||
|
/* ── Assembly ── */
|
||||||
|
function Assembly({ scrollRef, onSection }: {
|
||||||
|
scrollRef: React.MutableRefObject<number>; onSection: (id: string) => void;
|
||||||
|
}) {
|
||||||
|
const assemblyGroup = useRef<THREE.Group>(null);
|
||||||
|
const cardsRef = useRef<THREE.Group[]>([]);
|
||||||
|
const fitted = useRef(false);
|
||||||
|
const { camera } = useThree();
|
||||||
|
const smoothScroll = useRef(0);
|
||||||
|
const origPositions = useRef<THREE.Vector3[]>([]);
|
||||||
|
const assemblyScale = useRef(1);
|
||||||
|
const [currentSection, setCurrentSection] = useState('hero');
|
||||||
|
|
||||||
|
useFrame((_, delta) => {
|
||||||
|
if (!assemblyGroup.current) return;
|
||||||
|
|
||||||
|
// Auto-fit on first valid frame
|
||||||
|
if (!fitted.current && cardsRef.current.filter(Boolean).length === CARDS_DEF.length) {
|
||||||
|
fitted.current = true;
|
||||||
|
const box = new THREE.Box3().setFromObject(assemblyGroup.current);
|
||||||
|
const center = box.getCenter(new THREE.Vector3());
|
||||||
|
const maxDim = Math.max(...box.getSize(new THREE.Vector3()).toArray());
|
||||||
|
const scale = 2.2 / maxDim;
|
||||||
|
assemblyScale.current = scale;
|
||||||
|
assemblyGroup.current.scale.setScalar(scale);
|
||||||
|
assemblyGroup.current.position.copy(center.multiplyScalar(-scale));
|
||||||
|
assemblyGroup.current.updateMatrixWorld(true);
|
||||||
|
origPositions.current = cardsRef.current.map(g => g.position.clone());
|
||||||
|
}
|
||||||
|
if (!fitted.current) return;
|
||||||
|
|
||||||
|
// Smooth scroll
|
||||||
|
smoothScroll.current += (scrollRef.current - smoothScroll.current) * Math.min(1, delta * 2);
|
||||||
|
const t = Math.max(0, Math.min(1, smoothScroll.current));
|
||||||
|
|
||||||
|
// Find current section
|
||||||
|
let section = SECTIONS[SECTIONS.length - 1];
|
||||||
|
for (const s of SECTIONS) { if (t >= s.start && t <= s.end) { section = s; break; } }
|
||||||
|
|
||||||
|
const range = section.end - section.start || 1;
|
||||||
|
let f = Math.max(0, Math.min(1, (t - section.start) / range));
|
||||||
|
f = f * f * (3 - 2 * f); // smoothstep
|
||||||
|
|
||||||
|
// Explode
|
||||||
|
const explodeEnd = section.explodeEnd ?? section.explode;
|
||||||
|
const explode = section.explode + (explodeEnd - section.explode) * f;
|
||||||
|
|
||||||
|
// Apply explode to cards with stagger delay and rotation
|
||||||
|
for (let i = 0; i < cardsRef.current.length; i++) {
|
||||||
|
if (!cardsRef.current[i] || !origPositions.current[i]) continue;
|
||||||
|
const g = cardsRef.current[i];
|
||||||
|
const orig = origPositions.current[i];
|
||||||
|
const def = CARDS_DEF[i];
|
||||||
|
|
||||||
|
// Staggered explode: each card starts moving at its delay point
|
||||||
|
const delay = def.explodeDelay;
|
||||||
|
const cardExplode = Math.max(0, Math.min(1, (explode - delay) / (1 - delay)));
|
||||||
|
|
||||||
|
// Position
|
||||||
|
g.position.set(
|
||||||
|
orig.x + def.explodeDir[0] * def.explodeDist * cardExplode,
|
||||||
|
orig.y + def.explodeDir[1] * def.explodeDist * cardExplode,
|
||||||
|
orig.z + def.explodeDir[2] * def.explodeDist * cardExplode,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Rotation: interpolate toward flat when explodeRotTarget is set
|
||||||
|
if (def.explodeRotTarget) {
|
||||||
|
const origRot = def.rotation;
|
||||||
|
// Mosfets flip to flat during the last phase (cardExplode > 0.5)
|
||||||
|
const rotT = Math.max(0, Math.min(1, (cardExplode - 0.5) * 2));
|
||||||
|
const smoothRot = rotT * rotT * (3 - 2 * rotT);
|
||||||
|
g.rotation.set(
|
||||||
|
origRot[0] + (def.explodeRotTarget[0] - origRot[0]) * smoothRot,
|
||||||
|
origRot[1] + (def.explodeRotTarget[1] - origRot[1]) * smoothRot,
|
||||||
|
origRot[2] + (def.explodeRotTarget[2] - origRot[2]) * smoothRot,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute camera: focus on card or center
|
||||||
|
const focusStart = section.focus;
|
||||||
|
const focusEnd = section.focusEnd !== undefined ? section.focusEnd : focusStart;
|
||||||
|
|
||||||
|
// Get world position of focused card
|
||||||
|
function getCardWorldPos(cardIdx: number | null): THREE.Vector3 {
|
||||||
|
if (cardIdx !== null && cardsRef.current[cardIdx]) {
|
||||||
|
const wp = new THREE.Vector3();
|
||||||
|
cardsRef.current[cardIdx].getWorldPosition(wp);
|
||||||
|
return wp;
|
||||||
|
}
|
||||||
|
return new THREE.Vector3(0, 0, 0); // assembly center
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetStart = getCardWorldPos(focusStart);
|
||||||
|
const targetEnd = getCardWorldPos(focusEnd);
|
||||||
|
const target = new THREE.Vector3().lerpVectors(targetStart, targetEnd, f);
|
||||||
|
|
||||||
|
// Apply targetShift — interpolate between start and end for dolly effect
|
||||||
|
if (section.targetShift) {
|
||||||
|
const shiftStart = new THREE.Vector3(...section.targetShift);
|
||||||
|
const shiftEnd = new THREE.Vector3(...(section.targetShiftEnd || section.targetShift));
|
||||||
|
const shift = new THREE.Vector3().lerpVectors(shiftStart, shiftEnd, f);
|
||||||
|
if (assemblyGroup.current) {
|
||||||
|
shift.applyMatrix4(assemblyGroup.current.matrixWorld).sub(
|
||||||
|
new THREE.Vector3().setFromMatrixPosition(assemblyGroup.current.matrixWorld)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
target.add(shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
const offStart = new THREE.Vector3(...section.camOffset);
|
||||||
|
const offEnd = new THREE.Vector3(...(section.camOffsetEnd || section.camOffset));
|
||||||
|
const offset = new THREE.Vector3().lerpVectors(offStart, offEnd, f);
|
||||||
|
|
||||||
|
const camPos = target.clone().add(offset);
|
||||||
|
|
||||||
|
camera.position.lerp(camPos, Math.min(1, delta * 2));
|
||||||
|
camera.lookAt(target);
|
||||||
|
|
||||||
|
// Update section
|
||||||
|
if (section.id !== currentSection) {
|
||||||
|
setCurrentSection(section.id);
|
||||||
|
onSection(section.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<group ref={assemblyGroup} rotation={[-0.25, 0, 0]}>
|
||||||
|
{CARDS_DEF.map((card, i) => (
|
||||||
|
<group key={i}
|
||||||
|
ref={(el) => { if (el) cardsRef.current[i] = el; }}
|
||||||
|
position={card.position} rotation={card.rotation}>
|
||||||
|
<CardModel file={card.file} />
|
||||||
|
{/* Tuiles 3D plaquées sur les faces */}
|
||||||
|
{PCB_LABELS.filter(l => l.cardIndex === i).map((label, li) => (
|
||||||
|
<PcbTile key={`t${li}`} offset={label.localOffset}
|
||||||
|
title={label.title} sub={label.sub} color={label.color}
|
||||||
|
visible={currentSection === label.section || (label.section === 'cas' && currentSection === 'media')} section={label.section}
|
||||||
|
href={label.href} />
|
||||||
|
))}
|
||||||
|
{/* Videos on mosfet bottoms — real 3D mesh planes */}
|
||||||
|
{MOSFET_VIDEOS.filter(v => v.cardIndex === i).map((v, vi) => (
|
||||||
|
<PcbVideoMesh key={`v${vi}`} offset={[0, 0, -0.1]} src={v.src}
|
||||||
|
visible={currentSection === 'media' || currentSection === 'cas'} />
|
||||||
|
))}
|
||||||
|
{/* Carousel on mosfet #4 bottom — real 3D mesh plane */}
|
||||||
|
{i === MOSFET_CAROUSEL_CARD && (
|
||||||
|
<PcbCarouselMesh offset={[0, 0, -0.1]} visible={currentSection === 'media' || currentSection === 'cas'} />
|
||||||
|
)}
|
||||||
|
</group>
|
||||||
|
))}
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Pulsed Bloom — machine breathing ── */
|
||||||
|
function PulsedBloom() {
|
||||||
|
const bloomRef = useRef<any>(null);
|
||||||
|
useFrame(({ clock }) => {
|
||||||
|
if (!bloomRef.current) return;
|
||||||
|
const t = clock.getElapsedTime();
|
||||||
|
bloomRef.current.intensity = 0.25 + Math.sin(t * 0.8) * 0.1;
|
||||||
|
});
|
||||||
|
return <Bloom ref={bloomRef} intensity={0.25} luminanceThreshold={0.8} luminanceSmoothing={0.9} mipmapBlur />;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Export ── */
|
||||||
|
export function BmuViewer() {
|
||||||
|
const scrollRef = useRef(0);
|
||||||
|
const [, setSectionId] = useState('hero');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function onScroll() {
|
||||||
|
const max = document.documentElement.scrollHeight - window.innerHeight;
|
||||||
|
scrollRef.current = max > 0 ? window.scrollY / max : 0;
|
||||||
|
}
|
||||||
|
window.addEventListener('scroll', onScroll, { passive: true });
|
||||||
|
onScroll();
|
||||||
|
return () => window.removeEventListener('scroll', onScroll);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ position: 'fixed', inset: 0, width: '100vw', height: '100vh', zIndex: 0, pointerEvents: 'none' }}>
|
||||||
|
<Canvas gl={{ antialias: true, alpha: true, powerPreference: 'high-performance' }}
|
||||||
|
dpr={[1, 1.5]} camera={{ fov: 35, near: 0.01, far: 50 }}
|
||||||
|
style={{ background: 'transparent', pointerEvents: 'none' }}>
|
||||||
|
<ambientLight intensity={0.5} />
|
||||||
|
<directionalLight position={[5, 8, 5]} intensity={2.2} color="#ffffff" />
|
||||||
|
<directionalLight position={[-3, 4, -2]} intensity={0.9} color="#5bd1d8" />
|
||||||
|
<directionalLight position={[0, -4, 2]} intensity={0.5} color="#f1c27a" />
|
||||||
|
<Suspense fallback={null}>
|
||||||
|
<Assembly scrollRef={scrollRef} onSection={setSectionId} />
|
||||||
|
<ContactShadows position={[0, -1.5, 0]} opacity={0.15} scale={10} blur={2.5} far={5} color="#000000" />
|
||||||
|
<Environment preset="city" />
|
||||||
|
</Suspense>
|
||||||
|
</Canvas>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -321,7 +321,7 @@ const structuredDataJsonLd = JSON.stringify({
|
|||||||
var uiAllowed = { a: 1, b: 1 };
|
var uiAllowed = { a: 1, b: 1 };
|
||||||
var uiSprint = uiRaw.toLowerCase().trim() in uiAllowed ? uiRaw.toLowerCase().trim() : 'b';
|
var uiSprint = uiRaw.toLowerCase().trim() in uiAllowed ? uiRaw.toLowerCase().trim() : 'b';
|
||||||
|
|
||||||
root.setAttribute('data-da-variant', 'v12');
|
root.setAttribute('data-da-variant', 'classic');
|
||||||
root.setAttribute('data-ui-sprint', uiSprint);
|
root.setAttribute('data-ui-sprint', uiSprint);
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
@@ -344,7 +344,7 @@ const structuredDataJsonLd = JSON.stringify({
|
|||||||
|
|
||||||
<script is:inline>
|
<script is:inline>
|
||||||
(function () {
|
(function () {
|
||||||
var daVariant = document.documentElement.getAttribute('data-da-variant') || 'v12';
|
var daVariant = document.documentElement.getAttribute('data-da-variant') || 'classic';
|
||||||
var pagePath = window.location.pathname || '/';
|
var pagePath = window.location.pathname || '/';
|
||||||
var pageTitle = document.title || '';
|
var pageTitle = document.title || '';
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
---
|
||||||
|
---
|
||||||
|
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Conception électronique | L'électron rare</title>
|
||||||
|
<meta name="description" content="Conception de systèmes électroniques spécifiques : schéma, PCB, firmware, prototypage et validation. Du diagnostic à la mission complète." />
|
||||||
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||||
|
<style>
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
html { background: #06080b; color: #fff; }
|
||||||
|
body { background: #06080b; min-height: 100vh; font-family: 'Courier New', monospace; }
|
||||||
|
.site-hdr { position: fixed; top: 0; left: 0; right: 0; z-index: 10; display: flex; align-items: center; justify-content: space-between; padding: 16px clamp(20px, 3vw, 40px); background: rgba(6,8,11,0.85); backdrop-filter: blur(12px); border-bottom: 1px solid rgba(255,255,255,0.05); }
|
||||||
|
.hdr-left { display: flex; align-items: center; gap: 10px; }
|
||||||
|
.hdr-left a { text-decoration: none; display: flex; align-items: center; gap: 10px; }
|
||||||
|
.hdr-logo { width: 36px; height: auto; filter: drop-shadow(0 0 12px rgba(91,209,216,0.3)); }
|
||||||
|
.hdr-name { font-size: 16px; font-weight: 800; color: rgba(255,255,255,0.8); letter-spacing: 0.04em; }
|
||||||
|
.hdr-nav { display: flex; align-items: center; gap: 8px; }
|
||||||
|
.hdr-link { padding: 8px 16px; color: rgba(255,255,255,0.6); font-size: 12px; font-weight: 600; text-decoration: none; letter-spacing: 0.04em; transition: color 0.2s; }
|
||||||
|
.hdr-link:hover { color: #5bd1d8; }
|
||||||
|
.hdr-link.active { color: #5bd1d8; }
|
||||||
|
.hdr-cta { padding: 8px 20px; border-radius: 6px; background: rgba(91,209,216,0.15); border: 1px solid rgba(91,209,216,0.3); color: #5bd1d8; font-size: 12px; font-weight: 700; text-decoration: none; letter-spacing: 0.05em; }
|
||||||
|
.burger { display: none; background: none; border: none; color: #fff; font-size: 24px; cursor: pointer; padding: 8px; }
|
||||||
|
.mobile-menu { display: none; position: fixed; inset: 0; z-index: 100; background: rgba(6,8,11,0.95); backdrop-filter: blur(16px); flex-direction: column; align-items: center; justify-content: center; gap: 24px; }
|
||||||
|
.mobile-menu.open { display: flex; }
|
||||||
|
.mobile-menu a { font-size: 20px; font-weight: 700; color: rgba(255,255,255,0.8); text-decoration: none; letter-spacing: 0.05em; padding: 12px 24px; }
|
||||||
|
.mobile-menu a:hover { color: #5bd1d8; }
|
||||||
|
.mobile-menu .close-btn { position: absolute; top: 20px; right: 20px; background: none; border: none; color: #fff; font-size: 28px; cursor: pointer; }
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.hdr-nav { display: none; }
|
||||||
|
.burger { display: block; }
|
||||||
|
}
|
||||||
|
.content { max-width: 800px; margin: 0 auto; padding: 120px 24px 80px; }
|
||||||
|
h1 { font-size: clamp(28px, 4vw, 48px); font-weight: 800; color: #5bd1d8; margin-bottom: 24px; letter-spacing: -0.02em; }
|
||||||
|
h2 { font-size: 18px; font-weight: 700; color: #fff; margin: 40px 0 16px; text-transform: uppercase; letter-spacing: 0.06em; }
|
||||||
|
p { font-size: 14px; color: rgba(255,255,255,0.6); line-height: 1.8; margin-bottom: 16px; }
|
||||||
|
.card { background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08); border-radius: 12px; padding: 24px; margin-bottom: 16px; }
|
||||||
|
.card-t { font-size: 16px; font-weight: 700; color: #fff; margin-bottom: 8px; }
|
||||||
|
.card-pct { font-size: 28px; font-weight: 800; margin-bottom: 4px; }
|
||||||
|
.card-time { font-size: 11px; color: rgba(255,255,255,0.35); text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 12px; }
|
||||||
|
.card-desc { font-size: 13px; color: rgba(255,255,255,0.5); line-height: 1.6; margin-bottom: 12px; }
|
||||||
|
.card-del { font-size: 12px; color: rgba(255,255,255,0.4); }
|
||||||
|
.cta-box { margin-top: 48px; text-align: center; }
|
||||||
|
.cta-btn { display: inline-block; padding: 14px 32px; border-radius: 8px; background: #5bd1d8; color: #06080b; font-weight: 700; font-size: 14px; text-decoration: none; letter-spacing: 0.04em; }
|
||||||
|
.cta-btn:hover { background: #7de0e6; }
|
||||||
|
.footer { text-align: center; padding: 40px 24px; font-size: 10px; color: rgba(255,255,255,0.15); }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="site-hdr">
|
||||||
|
<div class="hdr-left"><a href="/"><img src="/assets/brand/logo-mark.svg" alt="" class="hdr-logo" /><span class="hdr-name">L'électron rare</span></a></div>
|
||||||
|
<nav class="hdr-nav">
|
||||||
|
<a href="/conception/" class="hdr-link active">Conception</a>
|
||||||
|
<a href="/formation/" class="hdr-link">Formation</a>
|
||||||
|
<a href="/consulting/" class="hdr-link">Consulting</a>
|
||||||
|
<a href="/contact/" class="hdr-cta">Contact</a>
|
||||||
|
</nav>
|
||||||
|
<button class="burger" id="burger-btn" aria-label="Menu">☰</button>
|
||||||
|
</header>
|
||||||
|
<div class="mobile-menu" id="mobile-menu">
|
||||||
|
<button class="close-btn" id="close-menu">✕</button>
|
||||||
|
<a href="/">Accueil</a>
|
||||||
|
<a href="/conception/">Conception</a>
|
||||||
|
<a href="/formation/">Formation</a>
|
||||||
|
<a href="/consulting/">Consulting</a>
|
||||||
|
<a href="/contact/">Contact</a>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
document.getElementById('burger-btn')?.addEventListener('click',()=>document.getElementById('mobile-menu')?.classList.add('open'));
|
||||||
|
document.getElementById('close-menu')?.addEventListener('click',()=>document.getElementById('mobile-menu')?.classList.remove('open'));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main class="content">
|
||||||
|
<h1>Conception électronique</h1>
|
||||||
|
<p>L'Électron Rare conçoit, met au point et fiabilise des systèmes électroniques spécifiques, des automatismes et des ensembles liés à l'énergie, au stockage et à l'optimisation terrain.</p>
|
||||||
|
|
||||||
|
<h2>Formats de mission</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<p class="card-pct" style="color:#5bd1d8">28%</p>
|
||||||
|
<p class="card-t">Diagnostic</p>
|
||||||
|
<p class="card-time">1 à 2 semaines</p>
|
||||||
|
<p class="card-desc">Analyser votre besoin, identifier les sous-systèmes critiques et livrer une carte de faisabilité.</p>
|
||||||
|
<p class="card-del">Livrables : analyse de faisabilité · risques identifiés · plan d'action</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<p class="card-pct" style="color:#a78bfa">55%</p>
|
||||||
|
<p class="card-t">Prototype</p>
|
||||||
|
<p class="card-time">4 à 6 semaines</p>
|
||||||
|
<p class="card-desc">Concevoir et tester un prototype fonctionnel : schéma, PCB, firmware, mesures et validation.</p>
|
||||||
|
<p class="card-del">Livrables : prototype testé · mesures documentées · go/no-go argumenté</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<p class="card-pct" style="color:#f59e0b">100%</p>
|
||||||
|
<p class="card-t">Mission complète</p>
|
||||||
|
<p class="card-time">2 à 6 mois</p>
|
||||||
|
<p class="card-desc">Développement complet jusqu'au produit industrialisable : itérations, tests terrain, documentation et transfert.</p>
|
||||||
|
<p class="card-del">Livrables : livrables par lot · dossier de production · transfert clé en main</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Cas concrets</h2>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<p class="card-t" style="color:#5bd1d8">Battery Parallelator — KXKM</p>
|
||||||
|
<p class="card-desc">Système de gestion parallèle de 4 batteries LiFePO4 pour le spectacle vivant. Conception complète KiCad : carte BMU, 4 cartes Mosfet Switch, carte I2C Repeater. Firmware ESP32, supervision I2C, protection thermique.</p>
|
||||||
|
<p class="card-del" style="color:#5bd1d8;">→ 8h autonomie · OTA · 0 panne sur 2 saisons</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<p class="card-t" style="color:#a78bfa">HypnoLED — Contrôle LED embarqué</p>
|
||||||
|
<p class="card-desc">Carte de contrôle LED haute densité pour installations scéniques. Architecture multi-cartes, protocole DMX/Art-Net, topologie bus, alimentation distribuée. PCB 4 couches KiCad.</p>
|
||||||
|
<p class="card-del" style="color:#a78bfa;">→ 512 canaux DMX · Latence <1ms · Déployé en tournée</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<p class="card-t" style="color:#34d399">Télémétrie ESP32 — Monitoring énergie</p>
|
||||||
|
<p class="card-desc">Système de supervision terrain avec ESP32 : mesure courant/tension, transmission MQTT, dashboard temps réel. Capteurs INA226, connectivité WiFi, boîtier IP65.</p>
|
||||||
|
<p class="card-del" style="color:#34d399;">→ Autonomie 6 mois sur batterie · Dashboard Grafana</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Domaines d'intervention</h2>
|
||||||
|
<p>Électronique spécifique · Instrumentation et automatisme · Énergie et stockage · Dispositifs pour le réel · Montages multi-techniques avec partenaires.</p>
|
||||||
|
|
||||||
|
<div class="cta-box">
|
||||||
|
<a href="/contact/?from=conception" class="cta-btn">Discuter de votre projet</a>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div class="footer">© 2026 Clément Saillant — L'électron rare · <a href="https://blog.saillant.cc" target="_blank" rel="noopener" style="color:rgba(255,255,255,0.3);">Blog</a> · <a href="https://fr.linkedin.com/in/electron-rare" target="_blank" rel="noopener" style="color:rgba(255,255,255,0.3);">LinkedIn</a> · <a href="https://github.com/electron-rare/" target="_blank" rel="noopener" style="color:rgba(255,255,255,0.3);">GitHub</a></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
---
|
||||||
|
---
|
||||||
|
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Consulting technique | L'électron rare</title>
|
||||||
|
<meta name="description" content="Consulting en systèmes électroniques : audit, diagnostic, transfert de compétences, accompagnement technique terrain." />
|
||||||
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||||
|
<style>
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
html { background: #06080b; color: #fff; }
|
||||||
|
body { background: #06080b; min-height: 100vh; font-family: 'Courier New', monospace; }
|
||||||
|
.site-hdr { position: fixed; top: 0; left: 0; right: 0; z-index: 10; display: flex; align-items: center; justify-content: space-between; padding: 16px clamp(20px, 3vw, 40px); background: rgba(6,8,11,0.85); backdrop-filter: blur(12px); border-bottom: 1px solid rgba(255,255,255,0.05); }
|
||||||
|
.hdr-left { display: flex; align-items: center; gap: 10px; }
|
||||||
|
.hdr-left a { text-decoration: none; display: flex; align-items: center; gap: 10px; }
|
||||||
|
.hdr-logo { width: 36px; height: auto; filter: drop-shadow(0 0 12px rgba(91,209,216,0.3)); }
|
||||||
|
.hdr-name { font-size: 16px; font-weight: 800; color: rgba(255,255,255,0.8); letter-spacing: 0.04em; }
|
||||||
|
.hdr-nav { display: flex; align-items: center; gap: 8px; }
|
||||||
|
.hdr-link { padding: 8px 16px; color: rgba(255,255,255,0.6); font-size: 12px; font-weight: 600; text-decoration: none; letter-spacing: 0.04em; transition: color 0.2s; }
|
||||||
|
.hdr-link:hover { color: #5bd1d8; }
|
||||||
|
.hdr-link.active { color: #5bd1d8; }
|
||||||
|
.hdr-cta { padding: 8px 20px; border-radius: 6px; background: rgba(91,209,216,0.15); border: 1px solid rgba(91,209,216,0.3); color: #5bd1d8; font-size: 12px; font-weight: 700; text-decoration: none; letter-spacing: 0.05em; }
|
||||||
|
.burger { display: none; background: none; border: none; color: #fff; font-size: 24px; cursor: pointer; padding: 8px; }
|
||||||
|
.mobile-menu { display: none; position: fixed; inset: 0; z-index: 100; background: rgba(6,8,11,0.95); backdrop-filter: blur(16px); flex-direction: column; align-items: center; justify-content: center; gap: 24px; }
|
||||||
|
.mobile-menu.open { display: flex; }
|
||||||
|
.mobile-menu a { font-size: 20px; font-weight: 700; color: rgba(255,255,255,0.8); text-decoration: none; letter-spacing: 0.05em; padding: 12px 24px; }
|
||||||
|
.mobile-menu a:hover { color: #5bd1d8; }
|
||||||
|
.mobile-menu .close-btn { position: absolute; top: 20px; right: 20px; background: none; border: none; color: #fff; font-size: 28px; cursor: pointer; }
|
||||||
|
@media (max-width: 768px) { .hdr-nav { display: none; } .burger { display: block; } }
|
||||||
|
.content { max-width: 800px; margin: 0 auto; padding: 120px 24px 80px; }
|
||||||
|
h1 { font-size: clamp(28px, 4vw, 48px); font-weight: 800; color: #a78bfa; margin-bottom: 24px; letter-spacing: -0.02em; }
|
||||||
|
h2 { font-size: 18px; font-weight: 700; color: #fff; margin: 40px 0 16px; text-transform: uppercase; letter-spacing: 0.06em; }
|
||||||
|
p { font-size: 14px; color: rgba(255,255,255,0.6); line-height: 1.8; margin-bottom: 16px; }
|
||||||
|
.card { background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08); border-radius: 12px; padding: 24px; margin-bottom: 16px; }
|
||||||
|
.card-t { font-size: 16px; font-weight: 700; color: #fff; margin-bottom: 8px; }
|
||||||
|
.card-desc { font-size: 13px; color: rgba(255,255,255,0.5); line-height: 1.6; }
|
||||||
|
.chip { display: inline-block; font-size: 11px; padding: 4px 12px; border-radius: 20px; border: 1px solid; margin: 4px 4px 4px 0; letter-spacing: 0.04em; }
|
||||||
|
.families { margin: 20px 0 32px; }
|
||||||
|
.cta-box { margin-top: 48px; text-align: center; }
|
||||||
|
.cta-btn { display: inline-block; padding: 14px 32px; border-radius: 8px; background: #a78bfa; color: #06080b; font-weight: 700; font-size: 14px; text-decoration: none; letter-spacing: 0.04em; }
|
||||||
|
.cta-btn:hover { background: #c4b5fd; }
|
||||||
|
.footer { text-align: center; padding: 40px 24px; font-size: 10px; color: rgba(255,255,255,0.15); }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="site-hdr">
|
||||||
|
<div class="hdr-left"><a href="/"><img src="/assets/brand/logo-mark.svg" alt="" class="hdr-logo" /><span class="hdr-name">L'électron rare</span></a></div>
|
||||||
|
<nav class="hdr-nav">
|
||||||
|
<a href="/conception/" class="hdr-link">Conception</a>
|
||||||
|
<a href="/formation/" class="hdr-link">Formation</a>
|
||||||
|
<a href="/consulting/" class="hdr-link active">Consulting</a>
|
||||||
|
<a href="/contact/" class="hdr-cta">Contact</a>
|
||||||
|
</nav>
|
||||||
|
<button class="burger" id="burger-btn" aria-label="Menu">☰</button>
|
||||||
|
</header>
|
||||||
|
<div class="mobile-menu" id="mobile-menu">
|
||||||
|
<button class="close-btn" id="close-menu">✕</button>
|
||||||
|
<a href="/">Accueil</a><a href="/conception/">Conception</a><a href="/formation/">Formation</a><a href="/consulting/">Consulting</a><a href="/contact/">Contact</a>
|
||||||
|
</div>
|
||||||
|
<script>document.getElementById('burger-btn')?.addEventListener('click',()=>document.getElementById('mobile-menu')?.classList.add('open'));document.getElementById('close-menu')?.addEventListener('click',()=>document.getElementById('mobile-menu')?.classList.remove('open'));</script>
|
||||||
|
|
||||||
|
<main class="content">
|
||||||
|
<h1>Consulting technique</h1>
|
||||||
|
<p>Je suis Clément Saillant, fondateur de L'électron rare. Mon rôle n'est pas de vendre "un peu d'embarqué" en plus, mais de rendre possible le bon système pour le besoin réel : électronique, automatisme, énergie, stockage, optimisation, diagnostic, prototype, reprise d'existant ou fiabilisation.</p>
|
||||||
|
|
||||||
|
<h2>Familles d'intervention</h2>
|
||||||
|
<div class="families">
|
||||||
|
<span class="chip" style="color:#5bd1d8;border-color:#5bd1d833">Électronique spécifique</span>
|
||||||
|
<span class="chip" style="color:#a78bfa;border-color:#a78bfa33">Instrumentation & Automatisme</span>
|
||||||
|
<span class="chip" style="color:#f472b6;border-color:#f472b633">Dispositifs pour le réel</span>
|
||||||
|
<span class="chip" style="color:#34d399;border-color:#34d39933">Énergie & Stockage</span>
|
||||||
|
<span class="chip" style="color:#fbbf24;border-color:#fbbf2433">Multi-techniques & Partenaires</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<p class="card-t">Audit & Diagnostic</p>
|
||||||
|
<p class="card-desc">Analyse de l'existant, identification des points critiques, recommandations chiffrées. Diagnostic terrain sur site industriel, automate, variateur, instrumentation.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<p class="card-t">Accompagnement technique</p>
|
||||||
|
<p class="card-desc">Pilotage de sous-traitance, revue de conception, validation de prototypes, suivi de production. Mobilisation de partenaires spécialisés quand le projet devient multi-technique.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<p class="card-t">Transfert & Documentation</p>
|
||||||
|
<p class="card-desc">Rédaction de dossiers techniques, procédures de maintenance, documentation de production. Transfert de compétences vers l'équipe interne.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Cas concret</h2>
|
||||||
|
<div class="card">
|
||||||
|
<p class="card-t">Mise en production et correction V3.2 KXKM</p>
|
||||||
|
<p class="card-desc">Mise en production du hardware ESP32, correction des bugs V3.2, passage STM32F030. Tests terrain, validation embarquée, documentation technique.</p>
|
||||||
|
<p class="card-desc" style="color:#a78bfa;margin-top:8px;">→ Production V3.2 livrée · Migration STM32F030 validée</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>À propos</h2>
|
||||||
|
<div class="card">
|
||||||
|
<p class="card-t">Clément Saillant — 10 ans d'expérience</p>
|
||||||
|
<p class="card-desc">Cyborg en systèmes électroniques, j'interviens depuis 10 ans sur des projets allant du spectacle vivant à l'industrie automatisée, en passant par l'énergie et la formation. Contributeur open-source (KiCad, KXKM), ambassadeur IA appliquée à l'électronique.</p>
|
||||||
|
<p class="card-desc">Secteurs : industries créatives, ferroviaire, automatisme industriel, éducation, énergie/stockage.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cta-box">
|
||||||
|
<a href="/contact/?from=consulting" class="cta-btn">Discuter de votre besoin</a>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
© 2026 Clément Saillant — L'électron rare ·
|
||||||
|
<a href="https://blog.saillant.cc" target="_blank" rel="noopener" style="color:rgba(255,255,255,0.3);">Blog</a> ·
|
||||||
|
<a href="https://fr.linkedin.com/in/electron-rare" target="_blank" rel="noopener" style="color:rgba(255,255,255,0.3);">LinkedIn</a> ·
|
||||||
|
<a href="https://github.com/electron-rare/" target="_blank" rel="noopener" style="color:rgba(255,255,255,0.3);">GitHub</a>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
---
|
||||||
|
---
|
||||||
|
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Contact | L'électron rare</title>
|
||||||
|
<meta name="description" content="Parlons de votre projet électronique. Premier échange sans engagement." />
|
||||||
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||||
|
<style>
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
html { background: #06080b; color: #fff; }
|
||||||
|
body { background: #06080b; min-height: 100vh; font-family: 'Courier New', monospace; }
|
||||||
|
.site-hdr { position: fixed; top: 0; left: 0; right: 0; z-index: 10; display: flex; align-items: center; justify-content: space-between; padding: 16px clamp(20px, 3vw, 40px); background: rgba(6,8,11,0.85); backdrop-filter: blur(12px); border-bottom: 1px solid rgba(255,255,255,0.05); }
|
||||||
|
.hdr-left { display: flex; align-items: center; gap: 10px; }
|
||||||
|
.hdr-left a { text-decoration: none; display: flex; align-items: center; gap: 10px; }
|
||||||
|
.hdr-logo { width: 36px; height: auto; filter: drop-shadow(0 0 12px rgba(91,209,216,0.3)); }
|
||||||
|
.hdr-name { font-size: 16px; font-weight: 800; color: rgba(255,255,255,0.8); letter-spacing: 0.04em; }
|
||||||
|
.hdr-nav { display: flex; align-items: center; gap: 8px; }
|
||||||
|
.hdr-link { padding: 8px 16px; color: rgba(255,255,255,0.6); font-size: 12px; font-weight: 600; text-decoration: none; letter-spacing: 0.04em; transition: color 0.2s; }
|
||||||
|
.hdr-link:hover { color: #5bd1d8; }
|
||||||
|
.hdr-cta { padding: 8px 20px; border-radius: 6px; background: rgba(91,209,216,0.15); border: 1px solid rgba(91,209,216,0.3); color: #5bd1d8; font-size: 12px; font-weight: 700; text-decoration: none; letter-spacing: 0.05em; }
|
||||||
|
.hdr-cta.active { background: rgba(91,209,216,0.25); }
|
||||||
|
.burger { display: none; background: none; border: none; color: #fff; font-size: 24px; cursor: pointer; padding: 8px; }
|
||||||
|
.mobile-menu { display: none; position: fixed; inset: 0; z-index: 100; background: rgba(6,8,11,0.95); backdrop-filter: blur(16px); flex-direction: column; align-items: center; justify-content: center; gap: 24px; }
|
||||||
|
.mobile-menu.open { display: flex; }
|
||||||
|
.mobile-menu a { font-size: 20px; font-weight: 700; color: rgba(255,255,255,0.8); text-decoration: none; letter-spacing: 0.05em; padding: 12px 24px; }
|
||||||
|
.mobile-menu .close-btn { position: absolute; top: 20px; right: 20px; background: none; border: none; color: #fff; font-size: 28px; cursor: pointer; }
|
||||||
|
@media (max-width: 768px) { .hdr-nav { display: none; } .burger { display: block; } }
|
||||||
|
.content { max-width: 600px; margin: 0 auto; padding: 120px 24px 80px; }
|
||||||
|
h1 { font-size: clamp(28px, 4vw, 48px); font-weight: 800; color: #5bd1d8; margin-bottom: 12px; letter-spacing: -0.02em; }
|
||||||
|
.sub { font-size: 14px; color: rgba(255,255,255,0.4); margin-bottom: 40px; }
|
||||||
|
label { display: block; font-size: 11px; color: rgba(255,255,255,0.5); text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 6px; margin-top: 20px; }
|
||||||
|
input, select, textarea { width: 100%; padding: 12px 16px; background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.12); border-radius: 8px; color: #fff; font-family: 'Courier New', monospace; font-size: 14px; outline: none; transition: border-color 0.2s; }
|
||||||
|
input:focus, select:focus, textarea:focus { border-color: #5bd1d8; }
|
||||||
|
select { appearance: none; cursor: pointer; }
|
||||||
|
select option { background: #06080b; color: #fff; }
|
||||||
|
textarea { resize: vertical; min-height: 160px; }
|
||||||
|
.consent { display: flex; align-items: flex-start; gap: 10px; margin-top: 20px; font-size: 11px; color: rgba(255,255,255,0.4); line-height: 1.5; }
|
||||||
|
.consent input { width: auto; }
|
||||||
|
.submit { display: block; width: 100%; margin-top: 28px; padding: 14px; border: none; border-radius: 8px; background: #5bd1d8; color: #06080b; font-family: 'Courier New', monospace; font-size: 14px; font-weight: 700; cursor: pointer; letter-spacing: 0.04em; transition: background 0.2s; }
|
||||||
|
.submit:hover { background: #7de0e6; }
|
||||||
|
.submit:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||||
|
.success { display: none; text-align: center; padding: 40px 0; }
|
||||||
|
.success h2 { font-size: 24px; color: #5bd1d8; margin-bottom: 12px; }
|
||||||
|
.success p { font-size: 14px; color: rgba(255,255,255,0.5); }
|
||||||
|
.error { display: none; color: #f66; font-size: 12px; margin-top: 12px; }
|
||||||
|
.alt { margin-top: 40px; text-align: center; font-size: 12px; color: rgba(255,255,255,0.3); }
|
||||||
|
.alt a { color: #5bd1d8; text-decoration: none; }
|
||||||
|
.footer { text-align: center; padding: 40px 24px; font-size: 10px; color: rgba(255,255,255,0.15); }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="site-hdr">
|
||||||
|
<div class="hdr-left"><a href="/"><img src="/assets/brand/logo-mark.svg" alt="" class="hdr-logo" /><span class="hdr-name">L'électron rare</span></a></div>
|
||||||
|
<nav class="hdr-nav">
|
||||||
|
<a href="/conception/" class="hdr-link">Conception</a>
|
||||||
|
<a href="/formation/" class="hdr-link">Formation</a>
|
||||||
|
<a href="/consulting/" class="hdr-link">Consulting</a>
|
||||||
|
<a href="/contact/" class="hdr-cta active">Contact</a>
|
||||||
|
</nav>
|
||||||
|
<button class="burger" id="burger-btn" aria-label="Menu">☰</button>
|
||||||
|
</header>
|
||||||
|
<div class="mobile-menu" id="mobile-menu">
|
||||||
|
<button class="close-btn" id="close-menu">✕</button>
|
||||||
|
<a href="/">Accueil</a><a href="/conception/">Conception</a><a href="/formation/">Formation</a><a href="/consulting/">Consulting</a><a href="/contact/">Contact</a>
|
||||||
|
</div>
|
||||||
|
<script>document.getElementById('burger-btn')?.addEventListener('click',()=>document.getElementById('mobile-menu')?.classList.add('open'));document.getElementById('close-menu')?.addEventListener('click',()=>document.getElementById('mobile-menu')?.classList.remove('open'));</script>
|
||||||
|
|
||||||
|
<main class="content">
|
||||||
|
<h1>Parlons de votre projet</h1>
|
||||||
|
<p class="sub">Décrivez votre besoin en quelques lignes — premier échange sans engagement.</p>
|
||||||
|
|
||||||
|
<form id="contact-form">
|
||||||
|
<label for="name">Nom *</label>
|
||||||
|
<input type="text" id="name" name="name" required placeholder="Votre nom" />
|
||||||
|
|
||||||
|
<label for="email">Email *</label>
|
||||||
|
<input type="email" id="email" name="email" required placeholder="votre@email.com" />
|
||||||
|
|
||||||
|
<label for="phone">Téléphone</label>
|
||||||
|
<input type="tel" id="phone" name="phone" placeholder="06 00 00 00 00" />
|
||||||
|
|
||||||
|
<label for="org">Organisation</label>
|
||||||
|
<input type="text" id="org" name="organization" placeholder="Entreprise / École / Association" />
|
||||||
|
|
||||||
|
<label for="need">Type de besoin *</label>
|
||||||
|
<select id="need" name="needType" required>
|
||||||
|
<option value="">Choisir...</option>
|
||||||
|
<option value="conception">Projet technique / Conception</option>
|
||||||
|
<option value="formation">Formation</option>
|
||||||
|
<option value="consulting">Consulting / Audit</option>
|
||||||
|
<option value="mixte">Projet mixte</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Préremplir selon la page d'origine (?from=conception|formation|consulting)
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const from = params.get('from');
|
||||||
|
if (from) {
|
||||||
|
const sel = document.getElementById('need');
|
||||||
|
if (sel) sel.value = from;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<label for="message">Message *</label>
|
||||||
|
<textarea id="message" name="message" required placeholder="Bonjour Clément,
|
||||||
|
|
||||||
|
J'ai un projet / besoin autour d'un système électronique spécifique.
|
||||||
|
|
||||||
|
Contexte :
|
||||||
|
Ce que j'attends :
|
||||||
|
Contraintes (délai, budget) :"></textarea>
|
||||||
|
|
||||||
|
<div class="consent">
|
||||||
|
<input type="checkbox" id="consent" required />
|
||||||
|
<label for="consent" style="margin:0;text-transform:none;font-size:11px;">J'accepte que mes données soient utilisées pour répondre à ma demande. Aucune donnée n'est transmise à des tiers.</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="submit" id="submit-btn">Envoyer le message</button>
|
||||||
|
<p class="error" id="error-msg"></p>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="success" id="success-msg">
|
||||||
|
<h2>Merci !</h2>
|
||||||
|
<p>Je reviens vers vous sous 48h.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="alt">
|
||||||
|
<p>Ou directement : <a href="mailto:contact@lelectronrare.fr">contact@lelectronrare.fr</a></p>
|
||||||
|
<p style="margin-top:8px;"><a href="https://fr.linkedin.com/in/electron-rare" target="_blank" rel="noopener">LinkedIn</a> · <a href="https://github.com/electron-rare/" target="_blank" rel="noopener">GitHub</a></p>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div class="footer">© 2026 Clément Saillant — L'électron rare · <a href="https://blog.saillant.cc" target="_blank" rel="noopener" style="color:rgba(255,255,255,0.3);">Blog</a> · <a href="https://fr.linkedin.com/in/electron-rare" target="_blank" rel="noopener" style="color:rgba(255,255,255,0.3);">LinkedIn</a> · <a href="https://github.com/electron-rare/" target="_blank" rel="noopener" style="color:rgba(255,255,255,0.3);">GitHub</a></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const form = document.getElementById('contact-form');
|
||||||
|
const btn = document.getElementById('submit-btn');
|
||||||
|
const errEl = document.getElementById('error-msg');
|
||||||
|
const successEl = document.getElementById('success-msg');
|
||||||
|
|
||||||
|
form?.addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.textContent = 'Envoi...';
|
||||||
|
errEl.style.display = 'none';
|
||||||
|
|
||||||
|
const data = Object.fromEntries(new FormData(form));
|
||||||
|
try {
|
||||||
|
const res = await fetch('https://api.lelectronrare.fr/api/submit-lead', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
});
|
||||||
|
if (!res.ok) throw new Error('Erreur serveur');
|
||||||
|
form.style.display = 'none';
|
||||||
|
successEl.style.display = 'block';
|
||||||
|
if (typeof gtag === 'function') gtag('event', 'contact_form_submit', { need_type: data.needType });
|
||||||
|
} catch (err) {
|
||||||
|
errEl.textContent = 'Erreur lors de l\'envoi. Essayez par email : contact@lelectronrare.fr';
|
||||||
|
errEl.style.display = 'block';
|
||||||
|
btn.disabled = false;
|
||||||
|
btn.textContent = 'Envoyer le message';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+164
-363
@@ -1,370 +1,171 @@
|
|||||||
---
|
---
|
||||||
import BaseLayout from '@/layouts/BaseLayout.astro';
|
|
||||||
import '@/styles/global.css';
|
|
||||||
import '@/styles/home-workbench.css';
|
|
||||||
import SiteHeader from '@/components/SiteHeader.astro';
|
|
||||||
import { withSiteBase } from '@/lib/site';
|
|
||||||
import { TRACK_EVENTS, trackAttrs } from '@/lib/tracking';
|
|
||||||
|
|
||||||
const title = "Formation électronique appliquée | L'électron rare";
|
|
||||||
const description = "Formations sur mesure : électronique, automatisme, énergie, stockage, PCB, instrumentation, IoT et optimisation. 1 jour, 3 jours ou programme dédié. Écoles, entreprises.";
|
|
||||||
const homeHref = withSiteBase('/');
|
|
||||||
const homeAboutHref = withSiteBase('/#a-propos');
|
|
||||||
const homeMissionsHref = withSiteBase('/#graphic-sprints-title');
|
|
||||||
const formationHref = withSiteBase('/formation/');
|
|
||||||
const contactHref = withSiteBase('/#contact');
|
|
||||||
const mentionsHref = withSiteBase('/mentions-legales/');
|
|
||||||
const headerItems = [
|
|
||||||
{ href: homeAboutHref, label: 'Approche' },
|
|
||||||
{ href: homeMissionsHref, label: 'Missions' },
|
|
||||||
{ href: formationHref, label: 'Formation', current: true },
|
|
||||||
{ href: contactHref, label: 'Contact', desktopOnly: true }
|
|
||||||
];
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title={title} description={description} ogDescription="Formations sur mesure en électronique, automatisme, énergie, stockage, PCB, instrumentation et IoT. 1 jour, 3 jours ou programme dédié. Écoles, entreprises, centres de formation.">
|
<html lang="fr">
|
||||||
<SiteHeader brandHref={homeHref} items={headerItems} quickHref={contactHref} />
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
<main id="main-content" class="site-shell studio-structure pb-12 pt-3">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Formation électronique appliquée | L'électron rare</title>
|
||||||
<section class="circuit-board rounded-2xl p-5 md:p-6 mt-4" data-reveal>
|
<meta name="description" content="Formations sur mesure : électronique, automatisme, énergie, PCB, instrumentation, IoT. 1 jour, 3 jours ou programme dédié. Écoles, entreprises." />
|
||||||
<div class="circuit-title-row">
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||||
<span class="circuit-node" aria-hidden="true"></span>
|
<style>
|
||||||
<h1 class="m-0 text-3xl md:text-4xl">Formation électronique appliquée</h1>
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
<span class="circuit-pinline" aria-hidden="true"></span>
|
html { background: #06080b; color: #fff; }
|
||||||
</div>
|
body { background: #06080b; min-height: 100vh; font-family: 'Courier New', monospace; }
|
||||||
|
.site-hdr { position: fixed; top: 0; left: 0; right: 0; z-index: 10; display: flex; align-items: center; justify-content: space-between; padding: 16px clamp(20px, 3vw, 40px); background: rgba(6,8,11,0.85); backdrop-filter: blur(12px); border-bottom: 1px solid rgba(255,255,255,0.05); }
|
||||||
<p class="section-lead mb-0 mt-3">
|
.hdr-left { display: flex; align-items: center; gap: 10px; }
|
||||||
Des formations pratiques, ancrées dans le terrain, pour monter en compétences sur l'électronique, l'automatisme, l'énergie, le stockage, la conception PCB, l'instrumentation, l'IoT et l'optimisation d'usage.
|
.hdr-left a { text-decoration: none; display: flex; align-items: center; gap: 10px; }
|
||||||
</p>
|
.hdr-logo { width: 36px; height: auto; filter: drop-shadow(0 0 12px rgba(91,209,216,0.3)); }
|
||||||
<p class="mb-0 mt-3 studio-muted">
|
.hdr-name { font-size: 16px; font-weight: 800; color: rgba(255,255,255,0.8); letter-spacing: 0.04em; }
|
||||||
Chaque formation est construite sur mesure à partir de votre contexte réel : vos projets, vos équipes, vos contraintes. Pas de slides génériques — du bench, du schéma et du livrable.
|
.hdr-nav { display: flex; align-items: center; gap: 8px; }
|
||||||
</p>
|
.hdr-link { padding: 8px 16px; color: rgba(255,255,255,0.6); font-size: 12px; font-weight: 600; text-decoration: none; letter-spacing: 0.04em; transition: color 0.2s; }
|
||||||
</section>
|
.hdr-link:hover { color: #5bd1d8; }
|
||||||
|
.hdr-link.active { color: #f59e0b; }
|
||||||
<section class="circuit-board rounded-2xl p-5 md:p-6 mt-5" data-reveal>
|
.hdr-cta { padding: 8px 20px; border-radius: 6px; background: rgba(91,209,216,0.15); border: 1px solid rgba(91,209,216,0.3); color: #5bd1d8; font-size: 12px; font-weight: 700; text-decoration: none; letter-spacing: 0.05em; }
|
||||||
<div class="circuit-title-row">
|
.burger { display: none; background: none; border: none; color: #fff; font-size: 24px; cursor: pointer; padding: 8px; }
|
||||||
<span class="circuit-node circuit-node--magenta" aria-hidden="true"></span>
|
.mobile-menu { display: none; position: fixed; inset: 0; z-index: 100; background: rgba(6,8,11,0.95); backdrop-filter: blur(16px); flex-direction: column; align-items: center; justify-content: center; gap: 24px; }
|
||||||
<h2 class="m-0 text-2xl md:text-3xl">Publics cibles</h2>
|
.mobile-menu.open { display: flex; }
|
||||||
<span class="circuit-pinline" aria-hidden="true"></span>
|
.mobile-menu a { font-size: 20px; font-weight: 700; color: rgba(255,255,255,0.8); text-decoration: none; letter-spacing: 0.05em; padding: 12px 24px; }
|
||||||
</div>
|
.mobile-menu a:hover { color: #5bd1d8; }
|
||||||
|
.mobile-menu .close-btn { position: absolute; top: 20px; right: 20px; background: none; border: none; color: #fff; font-size: 28px; cursor: pointer; }
|
||||||
<div class="mt-4 rounded-xl border border-[color:var(--line)] p-3">
|
@media (max-width: 768px) { .hdr-nav { display: none; } .burger { display: block; } }
|
||||||
<ul class="m-0 grid grid-cols-1 gap-2 p-0">
|
.content { max-width: 800px; margin: 0 auto; padding: 120px 24px 80px; }
|
||||||
<li class="list-none studio-chip studio-chip--cyan">
|
h1 { font-size: clamp(28px, 4vw, 48px); font-weight: 800; color: #f59e0b; margin-bottom: 16px; letter-spacing: -0.02em; }
|
||||||
<strong>Entreprises industrielles</strong> — équipes R&D, bureaux d'études, techniciens de maintenance qui veulent concevoir, diagnostiquer ou fiabiliser des systèmes électroniques dans leurs produits ou process.
|
h2 { font-size: 18px; font-weight: 700; color: #fff; margin: 48px 0 16px; text-transform: uppercase; letter-spacing: 0.06em; border-bottom: 1px solid rgba(255,255,255,0.08); padding-bottom: 8px; }
|
||||||
</li>
|
h3 { font-size: 14px; font-weight: 700; color: #f59e0b; margin: 28px 0 8px; text-transform: uppercase; letter-spacing: 0.08em; }
|
||||||
<li class="list-none studio-chip studio-chip--vio">
|
p { font-size: 14px; color: rgba(255,255,255,0.6); line-height: 1.8; margin-bottom: 12px; }
|
||||||
<strong>Écoles et universités</strong> — BTS, IUT, grandes écoles : TP encadrés, projets tutorés, modules spécialisés en systèmes électroniques, PCB et prototypage.
|
.lead { font-size: 15px; color: rgba(255,255,255,0.5); margin-bottom: 32px; }
|
||||||
</li>
|
.card { background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08); border-radius: 12px; padding: 20px; margin-bottom: 12px; }
|
||||||
<li class="list-none studio-chip studio-chip--pink">
|
.card-t { font-size: 15px; font-weight: 700; color: #fff; margin-bottom: 6px; }
|
||||||
<strong>Centres de formation</strong> — organismes souhaitant intégrer des modules électronique, instrumentation, énergie, stockage, IoT ou automatisme à leur catalogue.
|
.card-desc { font-size: 12px; color: rgba(255,255,255,0.45); line-height: 1.6; margin-bottom: 8px; }
|
||||||
</li>
|
.card-meta { font-size: 11px; color: rgba(255,255,255,0.3); letter-spacing: 0.04em; }
|
||||||
<li class="list-none studio-chip studio-chip--emerald">
|
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 12px; }
|
||||||
<strong>Studios créatifs et compagnies</strong> — équipes techniques du spectacle vivant, de l'événementiel ou des arts numériques qui veulent gagner en autonomie.
|
.subjects { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
||||||
</li>
|
@media (max-width: 700px) { .subjects, .grid { grid-template-columns: 1fr; } }
|
||||||
</ul>
|
.subject { background: rgba(255,255,255,0.02); border: 1px solid rgba(255,255,255,0.06); border-radius: 10px; padding: 16px; }
|
||||||
</div>
|
.subject-t { font-size: 13px; font-weight: 700; color: #fff; margin-bottom: 6px; }
|
||||||
</section>
|
.subject-d { font-size: 11px; color: rgba(255,255,255,0.4); line-height: 1.5; }
|
||||||
|
.audience { display: flex; flex-wrap: wrap; gap: 8px; margin: 16px 0 32px; }
|
||||||
<section class="mt-5" data-reveal>
|
.audience-chip { font-size: 11px; padding: 6px 14px; border-radius: 20px; border: 1px solid rgba(255,255,255,0.12); color: rgba(255,255,255,0.55); }
|
||||||
<div class="graphic-sprints-panel graphic-sprints-panel--routing">
|
.format-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
|
||||||
<div class="graphic-sprints-head">
|
@media (max-width: 700px) { .format-grid { grid-template-columns: 1fr; } }
|
||||||
<p class="graphic-sprints-kicker">Formats</p>
|
.format { text-align: center; padding: 24px 16px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08); border-radius: 12px; }
|
||||||
<h2 class="graphic-sprints-title">Trois formats, un principe : le concret d'abord</h2>
|
.format-dur { font-size: 24px; font-weight: 800; color: #f59e0b; margin-bottom: 4px; }
|
||||||
<p class="graphic-sprints-copy">
|
.format-name { font-size: 13px; font-weight: 700; color: #fff; margin-bottom: 8px; text-transform: uppercase; letter-spacing: 0.06em; }
|
||||||
Chaque format inclut un support de cours, des exercices pratiques sur matériel réel et un livrable que les participants repartent avec.
|
.format-desc { font-size: 11px; color: rgba(255,255,255,0.4); line-height: 1.5; }
|
||||||
</p>
|
.cta-box { margin-top: 48px; text-align: center; }
|
||||||
</div>
|
.cta-btn { display: inline-block; padding: 14px 32px; border-radius: 8px; background: #f59e0b; color: #06080b; font-weight: 700; font-size: 14px; text-decoration: none; letter-spacing: 0.04em; }
|
||||||
|
.cta-btn:hover { background: #fbbf24; }
|
||||||
<div class="graphic-sprints-grid">
|
.info { margin-top: 40px; font-size: 11px; color: rgba(255,255,255,0.25); line-height: 1.8; }
|
||||||
<article class="graphic-sprint-card" style="--sprint-progress-target: 33%">
|
.footer { text-align: center; padding: 40px 24px; font-size: 10px; color: rgba(255,255,255,0.15); }
|
||||||
<header class="graphic-sprint-card-head">
|
</style>
|
||||||
<p class="graphic-sprint-status graphic-sprint-status--trace">Initiation</p>
|
</head>
|
||||||
<p class="graphic-sprint-window">1 jour (7h)</p>
|
<body>
|
||||||
|
<header class="site-hdr">
|
||||||
|
<div class="hdr-left"><a href="/"><img src="/assets/brand/logo-mark.svg" alt="" class="hdr-logo" /><span class="hdr-name">L'électron rare</span></a></div>
|
||||||
|
<nav class="hdr-nav">
|
||||||
|
<a href="/conception/" class="hdr-link">Conception</a>
|
||||||
|
<a href="/formation/" class="hdr-link active">Formation</a>
|
||||||
|
<a href="/consulting/" class="hdr-link">Consulting</a>
|
||||||
|
<a href="/contact/" class="hdr-cta">Contact</a>
|
||||||
|
</nav>
|
||||||
|
<button class="burger" id="burger-btn" aria-label="Menu">☰</button>
|
||||||
</header>
|
</header>
|
||||||
<h3 class="graphic-sprint-title">Découverte</h3>
|
<div class="mobile-menu" id="mobile-menu">
|
||||||
<p class="graphic-sprint-objective">
|
<button class="close-btn" id="close-menu">✕</button>
|
||||||
Comprendre les fondamentaux d'un domaine technique. Idéal pour une première prise en main ou une mise à niveau rapide.
|
<a href="/">Accueil</a><a href="/conception/">Conception</a><a href="/formation/">Formation</a><a href="/consulting/">Consulting</a><a href="/contact/">Contact</a>
|
||||||
|
</div>
|
||||||
|
<script>document.getElementById('burger-btn')?.addEventListener('click',()=>document.getElementById('mobile-menu')?.classList.add('open'));document.getElementById('close-menu')?.addEventListener('click',()=>document.getElementById('mobile-menu')?.classList.remove('open'));</script>
|
||||||
|
|
||||||
|
<main class="content">
|
||||||
|
<h1>Formation électronique appliquée</h1>
|
||||||
|
<p class="lead">Des formations pratiques, ancrées dans le terrain. Chaque programme est construit sur mesure à partir de votre contexte réel : vos projets, vos équipes, vos contraintes.</p>
|
||||||
|
<div style="display:flex;align-items:center;gap:12px;padding:14px 18px;background:rgba(245,158,11,0.08);border:1px solid rgba(245,158,11,0.2);border-radius:10px;margin-bottom:32px;">
|
||||||
|
<span style="font-size:24px;">🏅</span>
|
||||||
|
<div>
|
||||||
|
<p style="font-size:13px;color:#f59e0b;font-weight:700;margin:0;">Formations finançables OPCO / Afdas</p>
|
||||||
|
<p style="font-size:11px;color:rgba(255,255,255,0.4);margin:4px 0 0;">Intervention possible via un organisme partenaire certifié Qualiopi.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="audience">
|
||||||
|
<span class="audience-chip">Entreprises industrielles</span>
|
||||||
|
<span class="audience-chip">Écoles et universités</span>
|
||||||
|
<span class="audience-chip">Centres de formation</span>
|
||||||
|
<span class="audience-chip">Studios créatifs</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Formats</h2>
|
||||||
|
<div class="format-grid">
|
||||||
|
<div class="format">
|
||||||
|
<p class="format-dur">1 jour</p>
|
||||||
|
<p class="format-name">Initiation</p>
|
||||||
|
<p class="format-desc">Fondamentaux, démos live, TP sur matériel réel, support PDF</p>
|
||||||
|
</div>
|
||||||
|
<div class="format">
|
||||||
|
<p class="format-dur">3 jours</p>
|
||||||
|
<p class="format-name">Approfondissement</p>
|
||||||
|
<p class="format-desc">Projet fil rouge, prototype fonctionnel, documentation technique</p>
|
||||||
|
</div>
|
||||||
|
<div class="format">
|
||||||
|
<p class="format-dur">Sur mesure</p>
|
||||||
|
<p class="format-name">Programme dédié</p>
|
||||||
|
<p class="format-desc">Autour de votre projet, vos équipements, suivi post-formation</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Sujets</h2>
|
||||||
|
<div class="subjects">
|
||||||
|
<div class="subject"><p class="subject-t">Systèmes électroniques et embarqués</p><p class="subject-d">Architecture matérielle, µC (STM32, ESP32, Teensy), alimentation, debug bench, IPC-A-610, CEM.</p></div>
|
||||||
|
<div class="subject"><p class="subject-t">Conception PCB</p><p class="subject-d">KiCad, routage, stackup, DRC/ERC, Gerber, BOM, pick & place.</p></div>
|
||||||
|
<div class="subject"><p class="subject-t">IoT et protocoles</p><p class="subject-d">Capteurs, MQTT, WiFi, BLE, LoRa, TinyML, du capteur au dashboard.</p></div>
|
||||||
|
<div class="subject"><p class="subject-t">Énergie et stockage</p><p class="subject-d">Batterie, BMS, télémétrie, supervision, optimisation d'exploitation.</p></div>
|
||||||
|
<div class="subject"><p class="subject-t">Automatisme industriel</p><p class="subject-d">Siemens S7, Schneider, câblage armoires, variateurs, IEC 60204-1.</p></div>
|
||||||
|
<div class="subject"><p class="subject-t">Contrôle LED et scénographie</p><p class="subject-d">DMX, Art-Net, DANTE, pilotage LED, intégration spectacle.</p></div>
|
||||||
|
<div class="subject"><p class="subject-t">Firmware et programmation</p><p class="subject-d">C/C++ embarqué, Python, debug série, interruptions, RTOS.</p></div>
|
||||||
|
<div class="subject"><p class="subject-t">Workflows agentiques et IA</p><p class="subject-d">Agents IA, génération de code, revue automatisée, documentation assistée.</p></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Catalogue</h2>
|
||||||
|
|
||||||
|
<h3>Initiations — Tous publics</h3>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="card"><p class="card-t">Mes Premiers Circuits</p><p class="card-desc">Breadboard, soudure, badge lumineux. Zéro prérequis.</p><p class="card-meta">1 jour · 420€ · 6-8 pers.</p></div>
|
||||||
|
<div class="card"><p class="card-t">Arduino Pour Tous</p><p class="card-desc">Capteurs, LED, moteurs. Kit Arduino à emporter.</p><p class="card-meta">1 jour · 450€ · 6-8 pers.</p></div>
|
||||||
|
<div class="card"><p class="card-t">Lumière et Son</p><p class="card-desc">LED NeoPixel, DMX512, audio interactif. Kit inclus.</p><p class="card-meta">1 jour · 480€ · 6-8 pers.</p></div>
|
||||||
|
<div class="card"><p class="card-t">KiCad Découverte</p><p class="card-desc">Du breadboard au PCB. Commandez votre carte chez JLCPCB.</p><p class="card-meta">1 jour · 420€ · 6-8 pers.</p></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Créatif & Artistes</h3>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="card"><p class="card-t">Créer son Contrôleur MIDI</p><p class="card-desc">Teensy, potentiomètres, capteurs → instrument MIDI USB.</p><p class="card-meta">1 jour · 450€ · Variante 3h : 220€</p></div>
|
||||||
|
<div class="card"><p class="card-t">Programmation Embarquée pour Artistes</p><p class="card-desc">ESP32, capteurs, DMX, Art-Net, OSC → installations interactives.</p><p class="card-meta">1 jour · 480€ · Variante 3h : 240€</p></div>
|
||||||
|
<div class="card"><p class="card-t">IA pour la Création Artistique</p><p class="card-desc">Stable Diffusion, ComfyUI, Ollama. Image, son, vidéo, performance live.</p><p class="card-meta">1 jour · 500€ · Variante 3h : 250€</p></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Pro — Avancé</h3>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="card"><p class="card-t">KiCad — Conception PCB</p><p class="card-desc">KiCad 9/10, routage avancé, impédance, DFM, fabrication JLCPCB.</p><p class="card-meta">2 jours · 1 550€ · 6 pers. max</p></div>
|
||||||
|
<div class="card"><p class="card-t">Dispositifs Culturels</p><p class="card-desc">LED, DMX512, Art-Net, autonomie batteries, intégration terrain spectacle.</p><p class="card-meta">1 jour · 775€ · Finançable Afdas/OPCO</p></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Enfants & Jeunes</h3>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="card"><p class="card-t">Premiers Circuits (8-10 ans)</p><p class="card-desc">micro:bit, programmation par blocs, LED, capteurs.</p><p class="card-meta">3 demi-journées · 50€/demi-journée</p></div>
|
||||||
|
<div class="card"><p class="card-t">Maker Junior (10-13 ans)</p><p class="card-desc">Arduino, première soudure, capteurs, projet personnel.</p><p class="card-meta">5 demi-journées · 50€/demi-journée</p></div>
|
||||||
|
<div class="card"><p class="card-t">Electro Lab (13-16 ans)</p><p class="card-desc">ESP32, KiCad, conception et fabrication de vrai PCB.</p><p class="card-meta">8 sessions · 299€ hors frais de fabrication</p></div>
|
||||||
|
<div class="card"><p class="card-t">Atelier Parent-Enfant</p><p class="card-desc">3h de découverte en duo. Montage, soudure, LED.</p><p class="card-meta">3h · 59€/binôme · À partir de 8 ans</p></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cta-box">
|
||||||
|
<a href="/contact/?from=formation" class="cta-btn">Demander un programme</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="info">
|
||||||
|
Intervention en présentiel (France entière) ou en distanciel · Matériel de TP fourni · Convention de formation sur demande · Financement OPCO possible via organisme Qualiopi · Assuré RC Pro · NDA sur demande
|
||||||
</p>
|
</p>
|
||||||
<div class="graphic-sprint-progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="33">
|
|
||||||
<span class="graphic-sprint-progress-fill"></span>
|
|
||||||
</div>
|
|
||||||
<ul class="graphic-sprint-deliverables">
|
|
||||||
<li>Théorie appliquée + démos live</li>
|
|
||||||
<li>TP sur matériel réel</li>
|
|
||||||
<li>Support de cours PDF</li>
|
|
||||||
</ul>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<article class="graphic-sprint-card" style="--sprint-progress-target: 66%">
|
|
||||||
<header class="graphic-sprint-card-head">
|
|
||||||
<p class="graphic-sprint-status graphic-sprint-status--bench">Approfondissement</p>
|
|
||||||
<p class="graphic-sprint-window">3 jours (21h)</p>
|
|
||||||
</header>
|
|
||||||
<h3 class="graphic-sprint-title">Pratique</h3>
|
|
||||||
<p class="graphic-sprint-objective">
|
|
||||||
Maîtriser un sujet de bout en bout avec un projet fil rouge. Les participants repartent avec un prototype fonctionnel.
|
|
||||||
</p>
|
|
||||||
<div class="graphic-sprint-progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="66">
|
|
||||||
<span class="graphic-sprint-progress-fill"></span>
|
|
||||||
</div>
|
|
||||||
<ul class="graphic-sprint-deliverables">
|
|
||||||
<li>Projet fil rouge réalisé</li>
|
|
||||||
<li>Prototype fonctionnel</li>
|
|
||||||
<li>Documentation technique complète</li>
|
|
||||||
</ul>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<article class="graphic-sprint-card" style="--sprint-progress-target: 100%">
|
|
||||||
<header class="graphic-sprint-card-head">
|
|
||||||
<p class="graphic-sprint-status graphic-sprint-status--run">Sur mesure</p>
|
|
||||||
<p class="graphic-sprint-window">Durée adaptée</p>
|
|
||||||
</header>
|
|
||||||
<h3 class="graphic-sprint-title">Programme dédié</h3>
|
|
||||||
<p class="graphic-sprint-objective">
|
|
||||||
Formation construite intégralement autour de votre projet, vos équipements et vos objectifs. Accompagnement post-formation inclus.
|
|
||||||
</p>
|
|
||||||
<div class="graphic-sprint-progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="100">
|
|
||||||
<span class="graphic-sprint-progress-fill"></span>
|
|
||||||
</div>
|
|
||||||
<ul class="graphic-sprint-deliverables">
|
|
||||||
<li>Programme sur mesure</li>
|
|
||||||
<li>Matériel adapté à votre contexte</li>
|
|
||||||
<li>Suivi post-formation</li>
|
|
||||||
</ul>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="circuit-board rounded-2xl p-5 md:p-6 mt-5" data-reveal>
|
|
||||||
<div class="circuit-title-row">
|
|
||||||
<span class="circuit-node" aria-hidden="true"></span>
|
|
||||||
<h2 class="m-0 text-2xl md:text-3xl">Sujets de formation</h2>
|
|
||||||
<span class="circuit-pinline" aria-hidden="true"></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-4 grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h3 class="m-0 text-lg font-semibold">Systèmes électroniques et embarqués</h3>
|
|
||||||
<p class="mt-2 mb-0 studio-muted">
|
|
||||||
Architecture matérielle, microcontrôleurs (STM32, ESP32, Teensy), alimentation, lecture de datasheet, debug sur bench. Conformité IPC-A-610 et CEM.
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h3 class="m-0 text-lg font-semibold">Conception PCB</h3>
|
|
||||||
<p class="mt-2 mb-0 studio-muted">
|
|
||||||
De la schématique au routage avec KiCad. Règles de design, stackup, vérification DRC/ERC, préparation des fichiers de fabrication (Gerber, BOM, pick & place).
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h3 class="m-0 text-lg font-semibold">IoT et protocoles</h3>
|
|
||||||
<p class="mt-2 mb-0 studio-muted">
|
|
||||||
Capteurs, télémétrie, protocoles terrain (MQTT, HTTP, VE.Direct), connectivité (WiFi, BLE, LoRa), TinyML et IA embarquée. Du capteur au dashboard.
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h3 class="m-0 text-lg font-semibold">Énergie, stockage et optimisation</h3>
|
|
||||||
<p class="mt-2 mb-0 studio-muted">
|
|
||||||
Batterie, BMS, télémétrie, supervision, pilotage de charges, logique d'optimisation et amélioration d'exploitation. De la mesure au système mieux réglé.
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h3 class="m-0 text-lg font-semibold">Automatisme industriel</h3>
|
|
||||||
<p class="mt-2 mb-0 studio-muted">
|
|
||||||
Automates Siemens (S7) et Schneider, câblage d'armoires, variateurs, mise en service. Conformité IEC 60204-1, IEC 61439, NFC 13-200. Lecture de schémas électriques industriels.
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h3 class="m-0 text-lg font-semibold">Contrôle LED et scénographie</h3>
|
|
||||||
<p class="mt-2 mb-0 studio-muted">
|
|
||||||
Protocoles DMX, Art-Net, DANTE. Pilotage LED, topologies multi-cartes, intégration dans des dispositifs scéniques et événementiels.
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h3 class="m-0 text-lg font-semibold">Firmware et programmation</h3>
|
|
||||||
<p class="mt-2 mb-0 studio-muted">
|
|
||||||
C/C++ embarqué, Python pour l'instrumentation, debug série, gestion mémoire, interruptions, RTOS. Du code qui tourne sur le terrain.
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h3 class="m-0 text-lg font-semibold">Workflows agentiques et IA appliquée</h3>
|
|
||||||
<p class="mt-2 mb-0 studio-muted">
|
|
||||||
Intégrer l'IA et les agents autonomes dans vos flux de travail : génération de code embarqué, revue automatisée, documentation technique, analyse de datasheets et tests assistés par IA. Productivité et qualité augmentées.
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="circuit-board rounded-2xl p-5 md:p-6 mt-5" data-reveal>
|
|
||||||
<div class="circuit-title-row">
|
|
||||||
<span class="circuit-node circuit-node--magenta" aria-hidden="true"></span>
|
|
||||||
<h2 class="m-0 text-2xl md:text-3xl">Catalogue des formations</h2>
|
|
||||||
<span class="circuit-pinline" aria-hidden="true"></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p class="section-lead mb-0 mt-3">14 modules prêts à l'emploi. Chaque formation est disponible en inter-entreprises (Lyon) ou intra/sur site (toute la France).</p>
|
|
||||||
|
|
||||||
<h3 class="mt-4 mb-2 text-lg font-semibold" style="color:var(--electric)">Initiations — Tous publics</h3>
|
|
||||||
<div class="mt-2 grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h4 class="m-0 text-base font-semibold">Mes Premiers Circuits</h4>
|
|
||||||
<p class="mt-1 mb-1 studio-muted" style="font-size:0.85rem">Breadboard, soudure, badge lumineux. Zéro prérequis.</p>
|
|
||||||
<p class="m-0" style="font-size:0.8rem"><strong>1 jour · 420€</strong> · 6-8 pers.</p>
|
|
||||||
</article>
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h4 class="m-0 text-base font-semibold">Arduino Pour Tous</h4>
|
|
||||||
<p class="mt-1 mb-1 studio-muted" style="font-size:0.85rem">Capteurs, LED, moteurs. Kit Arduino à emporter.</p>
|
|
||||||
<p class="m-0" style="font-size:0.8rem"><strong>1 jour · 450€</strong> · 6-8 pers.</p>
|
|
||||||
</article>
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h4 class="m-0 text-base font-semibold">Lumière et Son</h4>
|
|
||||||
<p class="mt-1 mb-1 studio-muted" style="font-size:0.85rem">LED NeoPixel, DMX512, audio interactif. Kit LED+son inclus.</p>
|
|
||||||
<p class="m-0" style="font-size:0.8rem"><strong>1 jour · 480€</strong> · 6-8 pers.</p>
|
|
||||||
</article>
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h4 class="m-0 text-base font-semibold">KiCad Découverte</h4>
|
|
||||||
<p class="mt-1 mb-1 studio-muted" style="font-size:0.85rem">Du breadboard au PCB. Commandez votre carte chez JLCPCB.</p>
|
|
||||||
<p class="m-0" style="font-size:0.8rem"><strong>1 jour · 420€</strong> · 6-8 pers.</p>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3 class="mt-4 mb-2 text-lg font-semibold" style="color:var(--trace-magenta)">Créatif & Artistes</h3>
|
|
||||||
<div class="mt-2 grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h4 class="m-0 text-base font-semibold">Créer son Contrôleur MIDI</h4>
|
|
||||||
<p class="mt-1 mb-1 studio-muted" style="font-size:0.85rem">Teensy, potentiomètres, capteurs → instrument MIDI USB personnalisé.</p>
|
|
||||||
<p class="m-0" style="font-size:0.8rem"><strong>1 jour · 450€</strong> · Variante 3h : 220€</p>
|
|
||||||
</article>
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h4 class="m-0 text-base font-semibold">Programmation Embarquée pour Artistes</h4>
|
|
||||||
<p class="mt-1 mb-1 studio-muted" style="font-size:0.85rem">ESP32, capteurs, DMX, Art-Net, OSC → installations interactives.</p>
|
|
||||||
<p class="m-0" style="font-size:0.8rem"><strong>1 jour · 480€</strong> · Variante 3h : 240€</p>
|
|
||||||
</article>
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h4 class="m-0 text-base font-semibold">IA pour la Création Artistique</h4>
|
|
||||||
<p class="mt-1 mb-1 studio-muted" style="font-size:0.85rem">Stable Diffusion, ComfyUI, Ollama. Image, son, vidéo, performance live.</p>
|
|
||||||
<p class="m-0" style="font-size:0.8rem"><strong>1 jour · 500€</strong> · Variante 3h : 250€</p>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3 class="mt-4 mb-2 text-lg font-semibold" style="color:var(--accent)">Pro — Avancé</h3>
|
|
||||||
<div class="mt-2 grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h4 class="m-0 text-base font-semibold">KiCad — Conception PCB</h4>
|
|
||||||
<p class="mt-1 mb-1 studio-muted" style="font-size:0.85rem">KiCad 9/10, routage avancé, impedance, DFM, fabrication JLCPCB.</p>
|
|
||||||
<p class="m-0" style="font-size:0.8rem"><strong>2 jours · 1 550€</strong> · 6 pers. max</p>
|
|
||||||
</article>
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h4 class="m-0 text-base font-semibold">Dispositifs Culturels</h4>
|
|
||||||
<p class="mt-1 mb-1 studio-muted" style="font-size:0.85rem">LED, DMX512, Art-Net, autonomie batteries, intégration terrain spectacle.</p>
|
|
||||||
<p class="m-0" style="font-size:0.8rem"><strong>1 jour · 775€</strong> · Finançable Afdas/OPCO</p>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3 class="mt-4 mb-2 text-lg font-semibold" style="color:var(--trace-green)">Enfants & Jeunes</h3>
|
|
||||||
<div class="mt-2 grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h4 class="m-0 text-base font-semibold">Premiers Circuits (8-10 ans)</h4>
|
|
||||||
<p class="mt-1 mb-1 studio-muted" style="font-size:0.85rem">micro:bit, programmation par blocs, LED, capteurs.</p>
|
|
||||||
<p class="m-0" style="font-size:0.8rem"><strong>3 demi-journées · 89€</strong></p>
|
|
||||||
</article>
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h4 class="m-0 text-base font-semibold">Maker Junior (10-13 ans)</h4>
|
|
||||||
<p class="mt-1 mb-1 studio-muted" style="font-size:0.85rem">Arduino, première soudure, capteurs, projet personnel.</p>
|
|
||||||
<p class="m-0" style="font-size:0.8rem"><strong>5 demi-journées · 149€</strong></p>
|
|
||||||
</article>
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h4 class="m-0 text-base font-semibold">Electro Lab (13-16 ans)</h4>
|
|
||||||
<p class="mt-1 mb-1 studio-muted" style="font-size:0.85rem">ESP32, KiCad, conception et fabrication de vrai PCB.</p>
|
|
||||||
<p class="m-0" style="font-size:0.8rem"><strong>8 sessions · 299€</strong></p>
|
|
||||||
</article>
|
|
||||||
<article class="rounded-xl border border-[color:var(--line)] p-4">
|
|
||||||
<h4 class="m-0 text-base font-semibold">Atelier Parent-Enfant</h4>
|
|
||||||
<p class="mt-1 mb-1 studio-muted" style="font-size:0.85rem">3h de découverte en duo. Montage, soudure, LED.</p>
|
|
||||||
<p class="m-0" style="font-size:0.8rem"><strong>3h · 59€/binôme</strong> · À partir de 8 ans</p>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="circuit-board rounded-2xl p-5 md:p-6 mt-5" data-reveal>
|
|
||||||
<div class="circuit-title-row">
|
|
||||||
<span class="circuit-node circuit-node--magenta" aria-hidden="true"></span>
|
|
||||||
<h2 class="m-0 text-2xl md:text-3xl">Demander un programme</h2>
|
|
||||||
<span class="circuit-pinline" aria-hidden="true"></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p class="section-lead mb-0 mt-3">
|
|
||||||
Décrivez votre besoin en quelques lignes. Je vous propose un programme adapté sous 48h.
|
|
||||||
</p>
|
|
||||||
<p class="mb-0 mt-3 studio-muted">
|
|
||||||
Précisez le public, le niveau, le sujet visé et le format souhaité. Même une idée vague suffit pour démarrer.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="mt-4 flex flex-wrap gap-3">
|
|
||||||
<a
|
|
||||||
href="mailto:contact@lelectronrare.fr?subject=Demande%20de%20formation&body=Bonjour%20Cl%C3%A9ment%2C%0A%0AJe%20suis%20int%C3%A9ress%C3%A9%20par%20une%20formation.%0A%0AContexte%20(entreprise%2C%20%C3%A9cole%2C%20centre%20de%20formation)%20%3A%0ASujet%20vis%C3%A9%20%3A%0APublic%20concern%C3%A9%20%3A%0AFormat%20souhait%C3%A9%20(dur%C3%A9e%2C%20pr%C3%A9sentiel%2Fdistanciel)%20%3A"
|
|
||||||
class="figma-lab-pill figma-lab-pill--primary"
|
|
||||||
{...trackAttrs(TRACK_EVENTS.outboundEmailContact, 'mailto:contact@lelectronrare.fr')}
|
|
||||||
>
|
|
||||||
Envoyer par mail
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://rdv.saillant.cc/clement/formation"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
class="figma-lab-pill figma-lab-pill--secondary"
|
|
||||||
>
|
|
||||||
Réserver un échange 30 min
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://fr.linkedin.com/in/electron-rare"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
class="figma-lab-pill figma-lab-pill--secondary"
|
|
||||||
{...trackAttrs(TRACK_EVENTS.outboundLinkedinContact, 'linkedin.com')}
|
|
||||||
>
|
|
||||||
Contacter sur LinkedIn
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul aria-label="Repères" class="mb-0 mt-4 pl-5 studio-muted">
|
|
||||||
<li>Intervention en présentiel (France entière) ou en distanciel</li>
|
|
||||||
<li>Matériel de TP fourni ou adapté à votre parc existant</li>
|
|
||||||
<li>Convention de formation et programme détaillé sur demande</li>
|
|
||||||
<li>Intervention possible via un organisme certifié Qualiopi pour le financement OPCO</li>
|
|
||||||
<li>Assuré RC Pro · NDA sur demande</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer class="site-footer border-t border-[var(--line)] py-6 text-sm text-[var(--text-muted)]">
|
<div class="footer">© 2026 Clément Saillant — L'électron rare · <a href="https://blog.saillant.cc" target="_blank" rel="noopener" style="color:rgba(255,255,255,0.3);">Blog</a> · <a href="https://fr.linkedin.com/in/electron-rare" target="_blank" rel="noopener" style="color:rgba(255,255,255,0.3);">LinkedIn</a> · <a href="https://github.com/electron-rare/" target="_blank" rel="noopener" style="color:rgba(255,255,255,0.3);">GitHub</a></div>
|
||||||
<div class="site-shell footer-grid footer-grid--enhanced">
|
</body>
|
||||||
<div class="footer-col footer-col--brand">
|
</html>
|
||||||
<p class="m-0 footer-title">L'électron rare</p>
|
|
||||||
<div class="footer-brand-meta">
|
|
||||||
<p class="mb-0 footer-copy">
|
|
||||||
© 2026 Clément Saillant - systèmes électroniques spécifiques, conseil, formation — industrie, culture et projets multi-techniques.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<nav aria-label="Ressources" class="footer-links footer-links--single-rail">
|
|
||||||
<a href={homeHref} class="footer-link">Accueil</a>
|
|
||||||
<a href={formationHref} class="footer-link">Formation</a>
|
|
||||||
<a href="https://fr.linkedin.com/in/electron-rare" target="_blank" rel="noopener noreferrer me" class="footer-link">LinkedIn</a>
|
|
||||||
<a href="mailto:contact@lelectronrare.fr" class="footer-link">contact@lelectronrare.fr</a>
|
|
||||||
<a href="https://github.com/electron-rare/" target="_blank" rel="noopener noreferrer me" class="footer-link">GitHub</a>
|
|
||||||
<a href={mentionsHref} class="footer-link">Mentions légales</a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</BaseLayout>
|
|
||||||
|
|||||||
@@ -0,0 +1,508 @@
|
|||||||
|
---
|
||||||
|
import BaseLayout from '@/layouts/BaseLayout.astro';
|
||||||
|
import '@/styles/global.css';
|
||||||
|
import '@/styles/home-workbench.css';
|
||||||
|
import SiteHeader from '@/components/SiteHeader.astro';
|
||||||
|
import { Hero } from '@/components/sections/Hero';
|
||||||
|
import { About } from '@/components/sections/About';
|
||||||
|
import { CaseStudies } from '@/components/sections/CaseStudies';
|
||||||
|
import { GraphicSprints } from '@/components/sections/GraphicSprints';
|
||||||
|
import Contact from '@/components/sections/Contact.astro';
|
||||||
|
import Faq from '@/components/sections/Faq.astro';
|
||||||
|
import { withSiteBase } from '@/lib/site';
|
||||||
|
import { TRACK_EVENTS, trackAttrs } from '@/lib/tracking';
|
||||||
|
|
||||||
|
const LAUNCH_DATE = new Date('2026-05-01T00:00:00+02:00');
|
||||||
|
const isLaunched = Date.now() >= LAUNCH_DATE.getTime();
|
||||||
|
|
||||||
|
const formationHref = withSiteBase('/formation/');
|
||||||
|
const mentionsHref = withSiteBase('/mentions-legales/');
|
||||||
|
const headerItems = [
|
||||||
|
{ href: '#a-propos', label: 'Approche' },
|
||||||
|
{ href: '#graphic-sprints-title', label: 'Missions' },
|
||||||
|
{ href: formationHref, label: 'Formation' },
|
||||||
|
{ href: '#faq', label: 'FAQ' },
|
||||||
|
{ href: '#contact', label: 'Contact', desktopOnly: true }
|
||||||
|
];
|
||||||
|
|
||||||
|
const title = isLaunched
|
||||||
|
? "Systèmes électroniques spécifiques | L'électron rare"
|
||||||
|
: "L'Electron Rare — Bientôt";
|
||||||
|
const description = isLaunched
|
||||||
|
? "Conception, mise au point et fiabilisation de systèmes électroniques spécifiques."
|
||||||
|
: "L'Electron Rare arrive. Systèmes électroniques spécifiques, formations, prototypage.";
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout title={title} description={description}>
|
||||||
|
{isLaunched ? (
|
||||||
|
<Fragment>
|
||||||
|
<SiteHeader brandHref="#top" items={headerItems} quickHref="#contact" />
|
||||||
|
<main id="main-content" class="site-shell studio-structure pb-12 pt-3">
|
||||||
|
<section class="structure-grid structure-grid--hero" data-reveal>
|
||||||
|
<div class="structure-cell structure-cell--hero">
|
||||||
|
<Hero />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="structure-grid structure-grid--systems" data-reveal>
|
||||||
|
<div class="structure-cell structure-cell--about">
|
||||||
|
<About />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="structure-grid structure-grid--cases" data-reveal>
|
||||||
|
<div class="structure-cell structure-cell--cases">
|
||||||
|
<CaseStudies />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="structure-grid structure-grid--sprints" data-reveal>
|
||||||
|
<div class="structure-cell structure-cell--sprints">
|
||||||
|
<GraphicSprints />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="structure-grid structure-grid--faq" data-reveal>
|
||||||
|
<div class="structure-cell structure-cell--faq">
|
||||||
|
<Faq />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="structure-grid structure-grid--conversion" data-reveal>
|
||||||
|
<div class="structure-cell structure-cell--contact">
|
||||||
|
<Contact />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
<footer class="site-footer border-t border-[var(--line)] py-6 text-sm text-[var(--text-muted)]">
|
||||||
|
<div class="site-shell footer-grid footer-grid--enhanced">
|
||||||
|
<div class="footer-col footer-col--brand">
|
||||||
|
<p class="m-0 footer-title">L'électron rare</p>
|
||||||
|
<p class="mb-0 footer-copy">© 2026 Clément Saillant</p>
|
||||||
|
</div>
|
||||||
|
<nav aria-label="Ressources" class="footer-links footer-links--single-rail">
|
||||||
|
<a href="#a-propos" class="footer-link">Approche</a>
|
||||||
|
<a href="#graphic-sprints-title" class="footer-link">Missions</a>
|
||||||
|
<a href={formationHref} class="footer-link">Formation</a>
|
||||||
|
<a href="#faq" class="footer-link">FAQ</a>
|
||||||
|
<a href="#contact" class="footer-link">Contact</a>
|
||||||
|
<a href={mentionsHref} class="footer-link">Mentions légales</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</Fragment>
|
||||||
|
) : (
|
||||||
|
<!-- COUNTDOWN — random WebGL or CSS -->
|
||||||
|
<main class="countdown-page">
|
||||||
|
<!-- BG A: WebGL shader (PCB traces) -->
|
||||||
|
<div class="countdown-bg" id="bg-webgl">
|
||||||
|
<div class="webgl-bg" id="webgl-countdown"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- BG B: Pure CSS circuit board -->
|
||||||
|
<div class="countdown-bg" id="bg-css">
|
||||||
|
<div class="css-circuit">
|
||||||
|
<div class="circuit-grid">
|
||||||
|
<!-- Horizontal traces -->
|
||||||
|
<div class="trace trace-h trace-h1"><span class="pulse"></span></div>
|
||||||
|
<div class="trace trace-h trace-h2"><span class="pulse"></span></div>
|
||||||
|
<div class="trace trace-h trace-h3"><span class="pulse"></span></div>
|
||||||
|
<div class="trace trace-h trace-h4"><span class="pulse"></span></div>
|
||||||
|
<div class="trace trace-h trace-h5"><span class="pulse"></span></div>
|
||||||
|
<div class="trace trace-h trace-h6"><span class="pulse"></span></div>
|
||||||
|
<!-- Vertical traces -->
|
||||||
|
<div class="trace trace-v trace-v1"><span class="pulse"></span></div>
|
||||||
|
<div class="trace trace-v trace-v2"><span class="pulse"></span></div>
|
||||||
|
<div class="trace trace-v trace-v3"><span class="pulse"></span></div>
|
||||||
|
<div class="trace trace-v trace-v4"><span class="pulse"></span></div>
|
||||||
|
<div class="trace trace-v trace-v5"><span class="pulse"></span></div>
|
||||||
|
<div class="trace trace-v trace-v6"><span class="pulse"></span></div>
|
||||||
|
<!-- Nodes (intersections) -->
|
||||||
|
<div class="node node-1"></div>
|
||||||
|
<div class="node node-2"></div>
|
||||||
|
<div class="node node-3"></div>
|
||||||
|
<div class="node node-4"></div>
|
||||||
|
<div class="node node-5"></div>
|
||||||
|
<div class="node node-6"></div>
|
||||||
|
<div class="node node-7"></div>
|
||||||
|
<div class="node node-8"></div>
|
||||||
|
<div class="node node-9"></div>
|
||||||
|
<div class="node node-10"></div>
|
||||||
|
<div class="node node-11"></div>
|
||||||
|
<div class="node node-12"></div>
|
||||||
|
<!-- IC chips -->
|
||||||
|
<div class="ic ic-1">
|
||||||
|
<span class="ic-label">MCU</span>
|
||||||
|
<div class="ic-pins ic-pins-l"></div>
|
||||||
|
<div class="ic-pins ic-pins-r"></div>
|
||||||
|
</div>
|
||||||
|
<div class="ic ic-2">
|
||||||
|
<span class="ic-label">FPGA</span>
|
||||||
|
<div class="ic-pins ic-pins-l"></div>
|
||||||
|
<div class="ic-pins ic-pins-r"></div>
|
||||||
|
</div>
|
||||||
|
<div class="ic ic-3">
|
||||||
|
<span class="ic-label">PWR</span>
|
||||||
|
<div class="ic-pins ic-pins-l"></div>
|
||||||
|
<div class="ic-pins ic-pins-r"></div>
|
||||||
|
</div>
|
||||||
|
<!-- SMD components -->
|
||||||
|
<div class="smd smd-1"></div>
|
||||||
|
<div class="smd smd-2"></div>
|
||||||
|
<div class="smd smd-3"></div>
|
||||||
|
<div class="smd smd-4"></div>
|
||||||
|
<div class="smd smd-5"></div>
|
||||||
|
<div class="smd smd-6"></div>
|
||||||
|
<div class="smd smd-7"></div>
|
||||||
|
<div class="smd smd-8"></div>
|
||||||
|
<!-- Vias -->
|
||||||
|
<div class="via via-1"></div>
|
||||||
|
<div class="via via-2"></div>
|
||||||
|
<div class="via via-3"></div>
|
||||||
|
<div class="via via-4"></div>
|
||||||
|
<div class="via via-5"></div>
|
||||||
|
<div class="via via-6"></div>
|
||||||
|
</div>
|
||||||
|
<!-- Scanline sweep -->
|
||||||
|
<div class="scanline"></div>
|
||||||
|
<!-- Vignette -->
|
||||||
|
<div class="css-vignette"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="overlay">
|
||||||
|
<div class="top-bar">
|
||||||
|
<div class="logo-sprite" id="logo-sprite" aria-label="L'Electron Rare"></div>
|
||||||
|
<span class="brand-mark">L'ELECTRON RARE</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="center-spacer">
|
||||||
|
<div class="countdown-hero-text" id="hero-text">
|
||||||
|
<h1 class="hero-title">
|
||||||
|
<span class="hero-line hero-line-1">Systèmes</span>
|
||||||
|
<span class="hero-line hero-line-2">électroniques</span>
|
||||||
|
<span class="hero-line hero-line-3">spécifiques</span>
|
||||||
|
</h1>
|
||||||
|
<div class="hero-divider"></div>
|
||||||
|
<p class="hero-sub">Conception · Mise au point · Fiabilisation</p>
|
||||||
|
</div>
|
||||||
|
<div class="tagline-area" id="tagline-bubble" aria-live="polite">
|
||||||
|
<span class="tagline-cursor">></span>
|
||||||
|
<span class="tagline-text" id="tagline"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bottom-bar">
|
||||||
|
<span class="bottom-left">electronique · automatisme · energie</span>
|
||||||
|
<span class="bottom-right">mai 2026</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cartouche">
|
||||||
|
<p class="cartouche-title">L'électron rare</p>
|
||||||
|
<p class="cartouche-copy">© 2026 Clément Saillant — systèmes électroniques spécifiques, conseil, formation — industrie, culture et projets multi-techniques.</p>
|
||||||
|
<p class="cartouche-contact">Contact : <a href="https://fr.linkedin.com/in/electron-rare" target="_blank" rel="noopener">LinkedIn</a> · <a href="mailto:contact@lelectronrare.fr">contact@lelectronrare.fr</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
)}
|
||||||
|
</BaseLayout>
|
||||||
|
|
||||||
|
{!isLaunched && (
|
||||||
|
<script>
|
||||||
|
// ── Random mode pick ──
|
||||||
|
const useWebGL = Math.random() < 0.5;
|
||||||
|
const bgWebGL = document.getElementById('bg-webgl');
|
||||||
|
const bgCSS = document.getElementById('bg-css');
|
||||||
|
if (useWebGL) { bgCSS?.remove(); }
|
||||||
|
else { bgWebGL?.remove(); }
|
||||||
|
document.body.dataset.mode = useWebGL ? 'webgl' : 'css';
|
||||||
|
|
||||||
|
// ── Hero text reveal ──
|
||||||
|
const heroText = document.getElementById('hero-text');
|
||||||
|
if (heroText) {
|
||||||
|
setTimeout(() => heroText.classList.add('revealed'), 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Tagline rotation ──
|
||||||
|
const messages = [
|
||||||
|
"Charge des condensateurs en cours...",
|
||||||
|
"Calibration de l'oscilloscope...",
|
||||||
|
"Soudure des derniers composants...",
|
||||||
|
"Vérification de la BOM...",
|
||||||
|
"Flash du firmware en cours...",
|
||||||
|
"Mesure de continuité... bip.",
|
||||||
|
"Routage des dernières pistes...",
|
||||||
|
"Test de la chaîne d'alimentation...",
|
||||||
|
"Programmation du bootloader...",
|
||||||
|
"Revue de schéma en cours...",
|
||||||
|
"Placement des composants CMS...",
|
||||||
|
"Passage au four de refusion...",
|
||||||
|
"Debug JTAG en cours...",
|
||||||
|
"Vérification du plan de masse...",
|
||||||
|
"Analyse du spectre RF..."
|
||||||
|
];
|
||||||
|
let msgIndex = Math.floor(Math.random() * messages.length);
|
||||||
|
|
||||||
|
function rotateMessage() {
|
||||||
|
const bubble = document.getElementById('tagline-bubble');
|
||||||
|
const el = document.getElementById('tagline');
|
||||||
|
if (!bubble || !el) return;
|
||||||
|
bubble.style.opacity = '0';
|
||||||
|
bubble.style.transform = 'translateY(4px)';
|
||||||
|
setTimeout(() => {
|
||||||
|
el.textContent = messages[msgIndex];
|
||||||
|
bubble.style.opacity = '1';
|
||||||
|
bubble.style.transform = 'translateY(0)';
|
||||||
|
msgIndex = (msgIndex + 1) % messages.length;
|
||||||
|
}, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sprite = document.getElementById('logo-sprite');
|
||||||
|
if (sprite) sprite.style.backgroundImage = 'url(/assets/brand/logo-mark.png)';
|
||||||
|
|
||||||
|
rotateMessage();
|
||||||
|
setInterval(rotateMessage, 8000);
|
||||||
|
|
||||||
|
// ── WebGL shader (only if picked) ──
|
||||||
|
if (useWebGL) {
|
||||||
|
(function () {
|
||||||
|
var c = document.getElementById('webgl-countdown');
|
||||||
|
if (!c) return;
|
||||||
|
var cvs = document.createElement('canvas');
|
||||||
|
cvs.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;display:block;';
|
||||||
|
c.appendChild(cvs);
|
||||||
|
var gl = cvs.getContext('webgl') || cvs.getContext('experimental-webgl');
|
||||||
|
if (!gl) { cvs.remove(); return; }
|
||||||
|
|
||||||
|
function mk(type, src) {
|
||||||
|
var s = gl.createShader(type);
|
||||||
|
gl.shaderSource(s, src);
|
||||||
|
gl.compileShader(s);
|
||||||
|
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
|
||||||
|
console.warn('WebGL shader error:', gl.getShaderInfoLog(s));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
var vs = mk(gl.VERTEX_SHADER, 'attribute vec2 p;void main(){gl_Position=vec4(p,0.,1.);}');
|
||||||
|
var fs = mk(gl.FRAGMENT_SHADER, [
|
||||||
|
'precision mediump float;',
|
||||||
|
'uniform float T;uniform vec2 R;',
|
||||||
|
'float h(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5);}',
|
||||||
|
'float h1(float n){return fract(sin(n)*43758.5);}',
|
||||||
|
'void main(){',
|
||||||
|
' vec2 uv=gl_FragCoord.xy/R;',
|
||||||
|
' float t=T*.22;',
|
||||||
|
' vec3 col=vec3(0.),cy=vec3(.357,.82,.847),wh=vec3(.7,.97,1.);',
|
||||||
|
' float S=18.;',
|
||||||
|
' vec2 g=fract(uv*S),cell=floor(uv*S);',
|
||||||
|
' float lw=.03;',
|
||||||
|
' float hH=h(cell),hV=h(cell.yx+7.3);',
|
||||||
|
' float tH=smoothstep(lw,0.,abs(g.y-.5))*step(.38,hH);',
|
||||||
|
' float tV=smoothstep(lw,0.,abs(g.x-.5))*step(.38,hV);',
|
||||||
|
' float tr=max(tH,tV);',
|
||||||
|
' col+=cy*tr*.2;',
|
||||||
|
' float nd=smoothstep(.11,.01,length(g-.5));',
|
||||||
|
' col+=cy*nd*step(.38,hH)*step(.38,hV)*.8;',
|
||||||
|
' for(int i=0;i<8;i++){',
|
||||||
|
' float fi=float(i),sp=.3+h1(fi*7.31)*.5,ph=h1(fi*3.17+9.);',
|
||||||
|
' float rw=floor(h1(fi*5.71+1.)*S);',
|
||||||
|
' float rm=step(0.,.499-abs(uv.y*S-rw-.5));',
|
||||||
|
' if(rm>.0){',
|
||||||
|
' float px=fract(t*sp+ph);',
|
||||||
|
' float pi=smoothstep(.06,.0,abs(uv.x-px));',
|
||||||
|
' float pl=smoothstep(lw*.5,.0,abs(g.y-.5));',
|
||||||
|
' col+=wh*pi*pl*step(.38,h(vec2(floor(uv.x*S),rw)))*rm*2.5;',
|
||||||
|
' }',
|
||||||
|
' float cl=floor(h1(fi*4.13+11.)*S);',
|
||||||
|
' float cm=step(0.,.499-abs(uv.x*S-cl-.5));',
|
||||||
|
' if(cm>.0){',
|
||||||
|
' float py=fract(t*sp*.75+ph+.5);',
|
||||||
|
' float pi2=smoothstep(.06,.0,abs(uv.y-py));',
|
||||||
|
' float pl2=smoothstep(lw*.5,.0,abs(g.x-.5));',
|
||||||
|
' col+=wh*pi2*pl2*step(.38,h(vec2(cl,floor(uv.y*S))+7.3))*cm*2.5;',
|
||||||
|
' }',
|
||||||
|
' }',
|
||||||
|
' col+=cy*tr*(sin(t*6.28)*.5+.5)*.05;',
|
||||||
|
' col=1.-exp(-col*1.8);',
|
||||||
|
' float v=length((uv-.5)*vec2(1.4,1.));',
|
||||||
|
' col*=1.-smoothstep(.35,1.1,v);',
|
||||||
|
' col*=.94+.06*sin(gl_FragCoord.y*3.14159);',
|
||||||
|
' gl_FragColor=vec4(col,1.);',
|
||||||
|
'}'
|
||||||
|
].join('\n'));
|
||||||
|
|
||||||
|
if (!vs || !fs) return;
|
||||||
|
var prog = gl.createProgram();
|
||||||
|
gl.attachShader(prog, vs);
|
||||||
|
gl.attachShader(prog, fs);
|
||||||
|
gl.linkProgram(prog);
|
||||||
|
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
|
||||||
|
console.warn('WebGL link error:', gl.getProgramInfoLog(prog));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gl.useProgram(prog);
|
||||||
|
|
||||||
|
var buf = gl.createBuffer();
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
|
||||||
|
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1,1,-1,-1,1,1,1]), gl.STATIC_DRAW);
|
||||||
|
var ap = gl.getAttribLocation(prog, 'p');
|
||||||
|
gl.enableVertexAttribArray(ap);
|
||||||
|
gl.vertexAttribPointer(ap, 2, gl.FLOAT, false, 0, 0);
|
||||||
|
|
||||||
|
var uT = gl.getUniformLocation(prog, 'T');
|
||||||
|
var uR = gl.getUniformLocation(prog, 'R');
|
||||||
|
|
||||||
|
function resize() {
|
||||||
|
cvs.width = Math.round(cvs.offsetWidth * (window.devicePixelRatio || 1));
|
||||||
|
cvs.height = Math.round(cvs.offsetHeight * (window.devicePixelRatio || 1));
|
||||||
|
gl.viewport(0, 0, cvs.width, cvs.height);
|
||||||
|
}
|
||||||
|
resize();
|
||||||
|
window.addEventListener('resize', resize, { passive: true });
|
||||||
|
|
||||||
|
var t0 = performance.now();
|
||||||
|
function frame() {
|
||||||
|
gl.uniform1f(uT, (performance.now() - t0) * .001);
|
||||||
|
gl.uniform2f(uR, cvs.width, cvs.height);
|
||||||
|
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||||
|
requestAnimationFrame(frame);
|
||||||
|
}
|
||||||
|
frame();
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── CSS mode: animate random node blinks ──
|
||||||
|
if (!useWebGL) {
|
||||||
|
const nodes = document.querySelectorAll('.node');
|
||||||
|
function blinkNodes() {
|
||||||
|
nodes.forEach(n => {
|
||||||
|
if (Math.random() < 0.3) {
|
||||||
|
n.classList.add('node-active');
|
||||||
|
setTimeout(() => n.classList.remove('node-active'), 600 + Math.random() * 1200);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
blinkNodes();
|
||||||
|
setInterval(blinkNodes, 2000);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* ── Base countdown ── */
|
||||||
|
.countdown-page { position:relative; min-height:100vh; overflow:hidden; background:#000; color:#fff; font-family:'Manrope',-apple-system,sans-serif; }
|
||||||
|
.countdown-bg { position:fixed; inset:0; z-index:0; width:100vw; height:100vh; }
|
||||||
|
.webgl-bg { position:absolute; inset:0; width:100%; height:100%; }
|
||||||
|
.overlay { position:relative; z-index:10; min-height:100vh; display:flex; flex-direction:column; justify-content:space-between; padding:clamp(20px,3vw,40px) clamp(24px,5vw,60px); pointer-events:none; }
|
||||||
|
.overlay > * { pointer-events:none; }
|
||||||
|
.overlay a { pointer-events:auto; }
|
||||||
|
|
||||||
|
/* ── Top bar ── */
|
||||||
|
.top-bar { display:flex; align-items:center; gap:16px; }
|
||||||
|
.logo-sprite { width:48px; height:48px; background-size:contain; background-repeat:no-repeat; filter:brightness(1.2) drop-shadow(0 0 12px rgba(91,209,216,0.4)); }
|
||||||
|
.brand-mark { font-size:11px; font-weight:600; letter-spacing:0.25em; color:rgba(255,255,255,0.5); text-transform:uppercase; }
|
||||||
|
|
||||||
|
/* ── Hero text ── */
|
||||||
|
.center-spacer { flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:24px; }
|
||||||
|
.countdown-hero-text { text-align:center; opacity:0; transform:translateY(20px); transition:opacity 1.2s ease, transform 1.2s ease; }
|
||||||
|
.countdown-hero-text.revealed { opacity:1; transform:translateY(0); }
|
||||||
|
.hero-title { margin:0; line-height:1.1; }
|
||||||
|
.hero-line { display:block; font-weight:800; letter-spacing:-0.03em; color:rgba(255,255,255,0.85); }
|
||||||
|
.hero-line-1 { font-size:clamp(32px,5vw,64px); opacity:0; animation:fadeUp 0.8s ease 0.4s forwards; }
|
||||||
|
.hero-line-2 { font-size:clamp(36px,6vw,72px); color:#5bd1d8; opacity:0; animation:fadeUp 0.8s ease 0.7s forwards; }
|
||||||
|
.hero-line-3 { font-size:clamp(32px,5vw,64px); opacity:0; animation:fadeUp 0.8s ease 1.0s forwards; }
|
||||||
|
.hero-divider { width:60px; height:2px; margin:16px auto; background:linear-gradient(90deg,transparent,#5bd1d8,transparent); opacity:0; animation:fadeUp 0.6s ease 1.3s forwards; }
|
||||||
|
.hero-sub { font-size:clamp(12px,1.4vw,16px); color:rgba(255,255,255,0.35); letter-spacing:0.15em; text-transform:uppercase; margin:0; opacity:0; animation:fadeUp 0.6s ease 1.5s forwards; }
|
||||||
|
@keyframes fadeUp { to { opacity:1; transform:translateY(0); } from { opacity:0; transform:translateY(12px); } }
|
||||||
|
|
||||||
|
/* ── Tagline ── */
|
||||||
|
.tagline-area { display:flex; align-items:center; gap:8px; padding:10px 20px; background:rgba(255,255,255,0.03); border:1px solid rgba(255,255,255,0.06); border-radius:8px; transition:opacity 0.4s ease, transform 0.4s ease; pointer-events:auto; }
|
||||||
|
.tagline-cursor { font-family:monospace; font-size:14px; color:#5bd1d8; animation:blink 1s step-end infinite; }
|
||||||
|
@keyframes blink { 50% { opacity:0; } }
|
||||||
|
.tagline-text { font-family:monospace; font-size:clamp(11px,1.3vw,14px); color:rgba(255,255,255,0.4); }
|
||||||
|
|
||||||
|
/* ── Bottom bar ── */
|
||||||
|
.bottom-bar { display:flex; justify-content:space-between; align-items:flex-end; }
|
||||||
|
.bottom-left, .bottom-right { font-size:clamp(10px,1.1vw,12px); color:rgba(255,255,255,0.15); letter-spacing:0.08em; text-transform:uppercase; }
|
||||||
|
|
||||||
|
/* ── Cartouche ── */
|
||||||
|
.cartouche { position:fixed; bottom:clamp(16px,2vw,24px); right:clamp(16px,2vw,24px); max-width:320px; padding:14px 18px; background:rgba(0,0,0,0.5); backdrop-filter:blur(12px); border:1px solid rgba(255,255,255,0.06); border-radius:12px; pointer-events:auto; }
|
||||||
|
.cartouche-title { font-size:13px; font-weight:700; color:rgba(255,255,255,0.6); margin:0 0 6px; }
|
||||||
|
.cartouche-copy { font-size:9px; color:rgba(255,255,255,0.25); margin:0 0 4px; line-height:1.4; }
|
||||||
|
.cartouche-contact { font-size:9px; color:rgba(255,255,255,0.25); margin:0; }
|
||||||
|
.cartouche-contact a { color:#5bd1d8; text-decoration:none; }
|
||||||
|
|
||||||
|
/* ── CSS Circuit Board Background ── */
|
||||||
|
.css-circuit { position:absolute; inset:0; background:#020a0d; overflow:hidden; }
|
||||||
|
.circuit-grid { position:absolute; inset:5%; }
|
||||||
|
|
||||||
|
/* Traces */
|
||||||
|
.trace { position:absolute; background:rgba(91,209,216,0.08); }
|
||||||
|
.trace-h { height:1px; left:0; right:0; }
|
||||||
|
.trace-v { width:1px; top:0; bottom:0; }
|
||||||
|
.trace-h1 { top:8%; } .trace-h2 { top:22%; } .trace-h3 { top:38%; }
|
||||||
|
.trace-h4 { top:55%; } .trace-h5 { top:72%; } .trace-h6 { top:88%; }
|
||||||
|
.trace-v1 { left:10%; } .trace-v2 { left:25%; } .trace-v3 { left:42%; }
|
||||||
|
.trace-v4 { left:58%; } .trace-v5 { left:75%; } .trace-v6 { left:90%; }
|
||||||
|
|
||||||
|
/* Pulse on traces */
|
||||||
|
.trace .pulse { position:absolute; border-radius:50%; }
|
||||||
|
.trace-h .pulse { width:40px; height:3px; top:-1px; background:linear-gradient(90deg,transparent,#5bd1d8,transparent); animation:pulseH 4s linear infinite; }
|
||||||
|
.trace-v .pulse { width:3px; height:40px; left:-1px; background:linear-gradient(180deg,transparent,#5bd1d8,transparent); animation:pulseV 5s linear infinite; }
|
||||||
|
.trace-h1 .pulse { animation-delay:0s; } .trace-h2 .pulse { animation-delay:0.7s; } .trace-h3 .pulse { animation-delay:1.4s; }
|
||||||
|
.trace-h4 .pulse { animation-delay:2.1s; } .trace-h5 .pulse { animation-delay:0.3s; } .trace-h6 .pulse { animation-delay:1.8s; }
|
||||||
|
.trace-v1 .pulse { animation-delay:0.5s; } .trace-v2 .pulse { animation-delay:1.2s; } .trace-v3 .pulse { animation-delay:2.5s; }
|
||||||
|
.trace-v4 .pulse { animation-delay:0.9s; } .trace-v5 .pulse { animation-delay:1.7s; } .trace-v6 .pulse { animation-delay:3.1s; }
|
||||||
|
@keyframes pulseH { 0% { left:-40px; } 100% { left:100%; } }
|
||||||
|
@keyframes pulseV { 0% { top:-40px; } 100% { top:100%; } }
|
||||||
|
|
||||||
|
/* Nodes */
|
||||||
|
.node { position:absolute; width:6px; height:6px; border-radius:50%; background:rgba(91,209,216,0.15); border:1px solid rgba(91,209,216,0.2); transition:all 0.6s ease; }
|
||||||
|
.node-active { background:#5bd1d8; box-shadow:0 0 12px rgba(91,209,216,0.6), 0 0 24px rgba(91,209,216,0.3); }
|
||||||
|
.node-1 { top:8%; left:10%; } .node-2 { top:8%; left:42%; } .node-3 { top:8%; left:75%; }
|
||||||
|
.node-4 { top:22%; left:25%; } .node-5 { top:38%; left:58%; } .node-6 { top:38%; left:90%; }
|
||||||
|
.node-7 { top:55%; left:10%; } .node-8 { top:55%; left:42%; } .node-9 { top:72%; left:25%; }
|
||||||
|
.node-10 { top:72%; left:75%; } .node-11 { top:88%; left:42%; } .node-12 { top:88%; left:58%; }
|
||||||
|
|
||||||
|
/* IC chips */
|
||||||
|
.ic { position:absolute; background:rgba(91,209,216,0.04); border:1px solid rgba(91,209,216,0.12); border-radius:2px; display:flex; align-items:center; justify-content:center; }
|
||||||
|
.ic-label { font-family:monospace; font-size:8px; color:rgba(91,209,216,0.4); letter-spacing:0.1em; }
|
||||||
|
.ic-pins { position:absolute; }
|
||||||
|
.ic-pins-l { left:-4px; top:20%; bottom:20%; width:4px; border-right:1px solid rgba(91,209,216,0.15); background:repeating-linear-gradient(180deg,transparent,transparent 3px,rgba(91,209,216,0.1) 3px,rgba(91,209,216,0.1) 5px); }
|
||||||
|
.ic-pins-r { right:-4px; top:20%; bottom:20%; width:4px; border-left:1px solid rgba(91,209,216,0.15); background:repeating-linear-gradient(180deg,transparent,transparent 3px,rgba(91,209,216,0.1) 3px,rgba(91,209,216,0.1) 5px); }
|
||||||
|
.ic-1 { width:60px; height:36px; top:18%; left:55%; }
|
||||||
|
.ic-2 { width:50px; height:30px; top:50%; left:20%; }
|
||||||
|
.ic-3 { width:44px; height:24px; top:78%; left:68%; }
|
||||||
|
|
||||||
|
/* SMD resistors/caps */
|
||||||
|
.smd { position:absolute; width:12px; height:5px; background:rgba(241,194,122,0.08); border:1px solid rgba(241,194,122,0.15); border-radius:1px; }
|
||||||
|
.smd-1 { top:12%; left:32%; } .smd-2 { top:15%; left:65%; transform:rotate(90deg); }
|
||||||
|
.smd-3 { top:30%; left:15%; } .smd-4 { top:45%; left:80%; transform:rotate(90deg); }
|
||||||
|
.smd-5 { top:60%; left:48%; } .smd-6 { top:65%; left:35%; transform:rotate(90deg); }
|
||||||
|
.smd-7 { top:82%; left:52%; } .smd-8 { top:35%; left:72%; }
|
||||||
|
|
||||||
|
/* Vias */
|
||||||
|
.via { position:absolute; width:8px; height:8px; border-radius:50%; border:1px solid rgba(91,209,216,0.2); background:radial-gradient(circle,rgba(91,209,216,0.15) 30%,transparent 70%); }
|
||||||
|
.via::after { content:''; position:absolute; inset:2px; border-radius:50%; background:rgba(91,209,216,0.1); }
|
||||||
|
.via-1 { top:14%; left:48%; } .via-2 { top:28%; left:82%; } .via-3 { top:42%; left:18%; }
|
||||||
|
.via-4 { top:62%; left:62%; } .via-5 { top:76%; left:38%; } .via-6 { top:85%; left:85%; }
|
||||||
|
|
||||||
|
/* Scanline sweep */
|
||||||
|
.scanline { position:absolute; inset:0; background:linear-gradient(180deg,transparent 0%,transparent 48%,rgba(91,209,216,0.03) 50%,transparent 52%,transparent 100%); background-size:100% 200%; animation:scanline 8s linear infinite; pointer-events:none; }
|
||||||
|
@keyframes scanline { 0% { background-position:0 0; } 100% { background-position:0 100%; } }
|
||||||
|
|
||||||
|
/* CSS vignette */
|
||||||
|
.css-vignette { position:absolute; inset:0; background:radial-gradient(ellipse at center,transparent 30%,rgba(0,0,0,0.7) 100%); pointer-events:none; }
|
||||||
|
|
||||||
|
/* ── Mobile ── */
|
||||||
|
@media (max-width:640px) {
|
||||||
|
.bottom-bar { flex-direction:column; align-items:center; gap:4px; }
|
||||||
|
.cartouche { position:relative; bottom:auto; right:auto; max-width:100%; margin-top:12px; text-align:center; }
|
||||||
|
.hero-line-1, .hero-line-3 { font-size:28px; }
|
||||||
|
.hero-line-2 { font-size:32px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Reduced motion ── */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.trace .pulse, .scanline { animation:none !important; }
|
||||||
|
.hero-line-1, .hero-line-2, .hero-line-3, .hero-divider, .hero-sub { animation:none !important; opacity:1 !important; }
|
||||||
|
.countdown-hero-text { opacity:1 !important; transform:none !important; transition:none !important; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+358
-165
@@ -1,188 +1,381 @@
|
|||||||
---
|
---
|
||||||
import BaseLayout from '@/layouts/BaseLayout.astro';
|
import { BmuViewer } from '@/components/BmuViewer';
|
||||||
import '@/styles/global.css';
|
|
||||||
import '@/styles/home-workbench.css';
|
|
||||||
import SiteHeader from '@/components/SiteHeader.astro';
|
|
||||||
import { Hero } from '@/components/sections/Hero';
|
|
||||||
import { About } from '@/components/sections/About';
|
|
||||||
import { CaseStudies } from '@/components/sections/CaseStudies';
|
|
||||||
import { GraphicSprints } from '@/components/sections/GraphicSprints';
|
|
||||||
import Contact from '@/components/sections/Contact.astro';
|
|
||||||
import Faq from '@/components/sections/Faq.astro';
|
|
||||||
import { withSiteBase } from '@/lib/site';
|
|
||||||
import { TRACK_EVENTS, trackAttrs } from '@/lib/tracking';
|
|
||||||
|
|
||||||
const LAUNCH_DATE = new Date('2026-05-01T00:00:00+02:00');
|
|
||||||
const isLaunched = Date.now() >= LAUNCH_DATE.getTime();
|
|
||||||
|
|
||||||
const formationHref = withSiteBase('/formation/');
|
|
||||||
const mentionsHref = withSiteBase('/mentions-legales/');
|
|
||||||
const headerItems = [
|
|
||||||
{ href: '#a-propos', label: 'Approche' },
|
|
||||||
{ href: '#graphic-sprints-title', label: 'Missions' },
|
|
||||||
{ href: formationHref, label: 'Formation' },
|
|
||||||
{ href: '#faq', label: 'FAQ' },
|
|
||||||
{ href: '#contact', label: 'Contact', desktopOnly: true }
|
|
||||||
];
|
|
||||||
|
|
||||||
const title = isLaunched
|
|
||||||
? "Systèmes électroniques spécifiques | L'électron rare"
|
|
||||||
: "L'Electron Rare — Bientôt";
|
|
||||||
const description = isLaunched
|
|
||||||
? "Conception, mise au point et fiabilisation de systèmes électroniques spécifiques."
|
|
||||||
: "L'Electron Rare arrive. Systèmes électroniques spécifiques, formations, prototypage.";
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title={title} description={description}>
|
<html lang="fr">
|
||||||
{isLaunched ? (
|
<head>
|
||||||
<Fragment>
|
<meta charset="UTF-8" />
|
||||||
<SiteHeader brandHref="#top" items={headerItems} quickHref="#contact" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<main id="main-content" class="site-shell studio-structure pb-12 pt-3">
|
<title>L'électron rare — Systèmes électroniques spécifiques</title>
|
||||||
<section class="structure-grid structure-grid--hero" data-reveal>
|
<meta name="description" content="Conception, mise au point et fiabilisation de systèmes électroniques spécifiques, automatismes et ensembles liés à l'énergie. Consulting et formation PCB, KiCad, µC." />
|
||||||
<div class="structure-cell structure-cell--hero">
|
<meta property="og:title" content="L'électron rare — Systèmes électroniques spécifiques" />
|
||||||
<Hero />
|
<meta property="og:description" content="Conception, mise au point et fiabilisation de systèmes électroniques spécifiques. Consulting et formation." />
|
||||||
|
<meta property="og:image" content="https://www.lelectronrare.fr/assets/og-cover.jpg" />
|
||||||
|
<meta property="og:url" content="https://www.lelectronrare.fr/" />
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:site_name" content="L'électron rare" />
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:title" content="L'électron rare — Systèmes électroniques spécifiques" />
|
||||||
|
<meta name="twitter:description" content="Conception, mise au point et fiabilisation de systèmes électroniques spécifiques." />
|
||||||
|
<meta name="twitter:image" content="https://www.lelectronrare.fr/assets/og-cover.jpg" />
|
||||||
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||||
|
<link rel="preload" href="/assets/models3d/bmu_v2.glb" as="fetch" crossorigin />
|
||||||
|
<link rel="preload" href="/assets/models3d/i2c_repeater.glb" as="fetch" crossorigin />
|
||||||
|
<link rel="preload" href="/assets/models3d/mosfet_switch.glb" as="fetch" crossorigin />
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "ProfessionalService",
|
||||||
|
"name": "L'électron rare",
|
||||||
|
"description": "Conception, mise au point et fiabilisation de systèmes électroniques spécifiques, automatismes et ensembles liés à l'énergie.",
|
||||||
|
"url": "https://www.lelectronrare.fr",
|
||||||
|
"email": "contact@lelectronrare.fr",
|
||||||
|
"founder": { "@type": "Person", "name": "Clément Saillant" },
|
||||||
|
"areaServed": "France",
|
||||||
|
"serviceType": ["Conception électronique", "Formation PCB et embarqué", "Consulting technique", "Automatisme industriel"],
|
||||||
|
"sameAs": ["https://fr.linkedin.com/in/electron-rare", "https://github.com/electron-rare/"]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
html { background: #06080b; color: #fff; overflow-x: hidden; }
|
||||||
|
body { background: #06080b; min-height: 100vh; font-family: 'Courier New', monospace; }
|
||||||
|
.scroll-track { height: 1000vh; position: relative; z-index: 1; }
|
||||||
|
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0; }
|
||||||
|
|
||||||
|
/* Loader */
|
||||||
|
.loader { position: fixed; inset: 0; z-index: 9999; background: #06080b; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px; transition: opacity 0.8s ease, visibility 0.8s ease; }
|
||||||
|
.loader.done { opacity: 0; visibility: hidden; pointer-events: none; }
|
||||||
|
.loader-logo { width: clamp(60px, 8vw, 100px); height: auto; filter: drop-shadow(0 0 20px rgba(91,209,216,0.3)); }
|
||||||
|
.loader-name { font-size: clamp(20px, 2.5vw, 32px); font-weight: 800; color: rgba(255,255,255,0.8); letter-spacing: 0.06em; }
|
||||||
|
.loader-bar { width: clamp(180px, 30vw, 280px); height: 2px; background: rgba(255,255,255,0.06); border-radius: 1px; overflow: hidden; }
|
||||||
|
.loader-fill { height: 100%; width: 0%; background: linear-gradient(90deg, #5bd1d8, #a78bfa); transition: width 0.3s ease; }
|
||||||
|
.loader-status { font-size: 10px; color: rgba(255,255,255,0.25); letter-spacing: 0.12em; text-transform: uppercase; }
|
||||||
|
.loader-tagline { font-size: 12px; color: rgba(255,255,255,0.3); letter-spacing: 0.08em; margin-bottom: 20px; }
|
||||||
|
.loader-actions { margin-top: 24px; }
|
||||||
|
.loader-skip { display: inline-block; padding: 10px 24px; border: 1px solid rgba(91,209,216,0.3); border-radius: 8px; color: rgba(91,209,216,0.7); font-size: 12px; text-decoration: none; letter-spacing: 0.04em; transition: all 0.2s; }
|
||||||
|
.loader-skip:hover { border-color: #5bd1d8; color: #5bd1d8; background: rgba(91,209,216,0.08); }
|
||||||
|
.loader-a11y { margin-top: 20px; font-size: 10px; color: rgba(255,255,255,0.15); }
|
||||||
|
.loader-a11y a { color: rgba(255,255,255,0.25); text-decoration: none; }
|
||||||
|
.loader-a11y a:hover { color: #5bd1d8; }
|
||||||
|
|
||||||
|
.ov { position: fixed; z-index: 3; pointer-events: none; opacity: 0; transform: translateY(6px); transition: opacity 0.5s, transform 0.5s; }
|
||||||
|
.ov.on { opacity: 1; transform: translateY(0); }
|
||||||
|
|
||||||
|
/* Hero */
|
||||||
|
.ov-hero { inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; }
|
||||||
|
.hero-bottom { display: flex; flex-direction: column; align-items: center; padding-bottom: clamp(60px, 10vh, 120px); }
|
||||||
|
.hero-t { text-align: center; margin: 0; line-height: 1.1; }
|
||||||
|
.hl1,.hl3 { display: block; font-size: clamp(28px, 4.5vw, 56px); font-weight: 800; color: rgba(255,255,255,0.85); letter-spacing: -0.02em; }
|
||||||
|
.hl2 { display: block; font-size: clamp(32px, 5.5vw, 64px); font-weight: 800; color: #5bd1d8; letter-spacing: -0.02em; }
|
||||||
|
.hero-s { font-size: clamp(11px, 1.2vw, 15px); color: rgba(255,255,255,0.3); letter-spacing: 0.15em; text-transform: uppercase; margin: 16px 0 0; }
|
||||||
|
.hero-s2 { font-size: clamp(10px, 1vw, 13px); color: rgba(255,255,255,0.2); letter-spacing: 0.12em; text-transform: uppercase; margin: 8px 0 0; }
|
||||||
|
|
||||||
|
/* Header */
|
||||||
|
.site-hdr { position: fixed; top: 0; left: 0; right: 0; z-index: 10; display: flex; align-items: center; justify-content: space-between; padding: 16px clamp(20px, 3vw, 40px); pointer-events: none; }
|
||||||
|
.hdr-left { display: flex; align-items: center; gap: 10px; pointer-events: auto; }
|
||||||
|
.hdr-logo { width: 36px; height: auto; filter: drop-shadow(0 0 12px rgba(91,209,216,0.3)); }
|
||||||
|
.hdr-name { font-size: 16px; font-weight: 800; color: rgba(255,255,255,0.8); letter-spacing: 0.04em; }
|
||||||
|
.hdr-nav { display: flex; align-items: center; gap: 8px; pointer-events: auto; }
|
||||||
|
.hdr-link { padding: 8px 16px; color: rgba(255,255,255,0.6); font-size: 12px; font-weight: 600; text-decoration: none; letter-spacing: 0.04em; transition: color 0.2s; }
|
||||||
|
.hdr-link:hover { color: #5bd1d8; }
|
||||||
|
.hdr-social { font-weight: 800; font-size: 11px; text-transform: uppercase; padding: 8px 10px; opacity: 0.5; }
|
||||||
|
.hdr-social:hover { opacity: 1; }
|
||||||
|
.hdr-cta { padding: 8px 20px; border-radius: 6px; background: rgba(91,209,216,0.15); border: 1px solid rgba(91,209,216,0.3); color: #5bd1d8; font-size: 12px; font-weight: 700; text-decoration: none; letter-spacing: 0.05em; transition: background 0.2s; }
|
||||||
|
.hdr-cta:hover { background: rgba(91,209,216,0.25); }
|
||||||
|
.burger { display: none; background: none; border: none; color: #fff; font-size: 24px; cursor: pointer; padding: 8px; pointer-events: auto; }
|
||||||
|
.mobile-menu { display: none; position: fixed; inset: 0; z-index: 100; background: rgba(6,8,11,0.95); backdrop-filter: blur(16px); flex-direction: column; align-items: center; justify-content: center; gap: 24px; }
|
||||||
|
.mobile-menu.open { display: flex; }
|
||||||
|
.mobile-menu a { font-size: 20px; font-weight: 700; color: rgba(255,255,255,0.8); text-decoration: none; letter-spacing: 0.05em; padding: 12px 24px; }
|
||||||
|
.mobile-menu .close-btn { position: absolute; top: 20px; right: 20px; background: none; border: none; color: #fff; font-size: 28px; cursor: pointer; }
|
||||||
|
@media (max-width: 768px) { .hdr-nav { display: none; } .burger { display: block; } }
|
||||||
|
|
||||||
|
/* Formats */
|
||||||
|
.ov-formats { bottom: clamp(30px, 5vh, 60px); left: 50%; transform: translateX(-50%); display: flex; gap: 12px; max-width: 820px; width: 92vw; }
|
||||||
|
.ov-formats.on { transform: translateX(-50%); }
|
||||||
|
.fmt { flex: 1; padding: 14px 16px; border-radius: 8px; background: rgba(6,8,11,0.7); backdrop-filter: blur(12px); border: 1px solid rgba(255,255,255,0.05); text-align: center; }
|
||||||
|
.fmt-t { font-size: 13px; font-weight: 700; color: #fff; letter-spacing: 0.06em; text-transform: uppercase; margin-bottom: 4px; }
|
||||||
|
.fmt-d { font-size: 10px; color: rgba(255,255,255,0.35); }
|
||||||
|
|
||||||
|
/* Progress */
|
||||||
|
.prog { position: fixed; top: 0; left: 0; height: 2px; z-index: 5; background: linear-gradient(90deg, #5bd1d8, #a78bfa, #f59e0b); width: 0%; }
|
||||||
|
.inf { position: fixed; bottom: 10px; right: 14px; z-index: 4; font-size: 8px; color: rgba(255,255,255,0.1); letter-spacing: 0.06em; }
|
||||||
|
|
||||||
|
/* Dots */
|
||||||
|
.dots { position: fixed; right: 20px; top: 50%; transform: translateY(-50%); z-index: 4; display: flex; flex-direction: column; gap: 14px; }
|
||||||
|
.dot { width: 12px; height: 12px; border-radius: 50%; background: rgba(255,255,255,0.15); border: 1px solid rgba(255,255,255,0.1); transition: background 0.3s, transform 0.3s, border-color 0.3s; cursor: pointer; pointer-events: auto; position: relative; }
|
||||||
|
.dot.on { background: #5bd1d8; border-color: #5bd1d8; transform: scale(1.3); box-shadow: 0 0 10px rgba(91,209,216,0.4); }
|
||||||
|
.dot:hover { background: rgba(91,209,216,0.5); border-color: rgba(91,209,216,0.5); }
|
||||||
|
.dot[data-l]:not([data-l=""])::after { content: attr(data-l); position: absolute; right: 20px; top: 50%; transform: translateY(-50%); font-size: 9px; color: rgba(255,255,255,0.5); letter-spacing: 0.05em; white-space: nowrap; opacity: 0; transition: opacity 0.2s; pointer-events: none; }
|
||||||
|
.dot[data-l]:not([data-l=""]):hover::after { opacity: 1; }
|
||||||
|
|
||||||
|
/* Trust */
|
||||||
|
.trust { position: fixed; bottom: 12px; left: clamp(16px, 3vw, 40px); z-index: 3; pointer-events: none; }
|
||||||
|
.trust-k { font-size: 9px; color: rgba(255,255,255,0.2); letter-spacing: 0.12em; text-transform: uppercase; margin-bottom: 4px; }
|
||||||
|
.trust-names { font-size: 11px; color: rgba(255,255,255,0.3); letter-spacing: 0.04em; }
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.ov-formats { flex-direction: column; gap: 6px; }
|
||||||
|
.trust { display: none; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body data-da-variant="preview">
|
||||||
|
<!-- Loader -->
|
||||||
|
<div class="loader" id="loader">
|
||||||
|
<img src="/assets/brand/logo-mark.svg" alt="L'électron rare" class="loader-logo" />
|
||||||
|
<p class="loader-name">L'électron rare</p>
|
||||||
|
<p class="loader-tagline">Systèmes électroniques spécifiques</p>
|
||||||
|
<div class="loader-bar"><div class="loader-fill" id="loader-fill"></div></div>
|
||||||
|
<p class="loader-status" id="loader-status">Chargement des modèles 3D...</p>
|
||||||
|
<div class="loader-actions">
|
||||||
|
<a href="/conception/" class="loader-skip">Accéder au site sans 3D →</a>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
<p class="loader-a11y">Version accessible : <a href="/conception/">Conception</a> · <a href="/formation/">Formation</a> · <a href="/consulting/">Consulting</a> · <a href="/contact/">Contact</a></p>
|
||||||
<section class="structure-grid structure-grid--systems" data-reveal>
|
|
||||||
<div class="structure-cell structure-cell--about">
|
|
||||||
<About />
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
<section class="structure-grid structure-grid--cases" data-reveal>
|
<BmuViewer client:load />
|
||||||
<div class="structure-cell structure-cell--cases">
|
<div class="prog" id="prog"></div>
|
||||||
<CaseStudies />
|
|
||||||
|
<div class="dots" id="dots">
|
||||||
|
<div class="dot on" data-s="hero" data-p="0.03" data-l="Hero"></div>
|
||||||
|
<div class="dot" data-s="hero-to-exp" data-p="0.09" data-l=""></div>
|
||||||
|
<div class="dot" data-s="exp-elec" data-p="0.14" data-l="Élec"></div>
|
||||||
|
<div class="dot" data-s="exp-auto" data-p="0.18" data-l="Auto"></div>
|
||||||
|
<div class="dot" data-s="exp-energie" data-p="0.22" data-l="Énergie"></div>
|
||||||
|
<div class="dot" data-s="exp-dispositifs" data-p="0.255" data-l="Dispositifs"></div>
|
||||||
|
<div class="dot" data-s="exp-consulting" data-p="0.285" data-l="Consulting"></div>
|
||||||
|
<div class="dot" data-s="eclate-recul" data-p="0.31" data-l=""></div>
|
||||||
|
<div class="dot" data-s="eclate-sep" data-p="0.34" data-l=""></div>
|
||||||
|
<div class="dot" data-s="eclate-plat" data-p="0.36" data-l=""></div>
|
||||||
|
<div class="dot" data-s="cas" data-p="0.53" data-l="Cas concrets"></div>
|
||||||
|
<div class="dot" data-s="media" data-p="0.70" data-l="Médias"></div>
|
||||||
|
<div class="dot" data-s="formats" data-p="0.85" data-l="Services"></div>
|
||||||
|
<div class="dot" data-s="contact" data-p="0.95" data-l="Contact"></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
<section class="structure-grid structure-grid--sprints" data-reveal>
|
<!-- HEADER -->
|
||||||
<div class="structure-cell structure-cell--sprints">
|
<header class="site-hdr">
|
||||||
<GraphicSprints />
|
<div class="hdr-left">
|
||||||
|
<img src="/assets/brand/logo-mark.svg" alt="" class="hdr-logo" />
|
||||||
|
<span class="hdr-name">L'électron rare</span>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
<nav class="hdr-nav">
|
||||||
<section class="structure-grid structure-grid--faq" data-reveal>
|
<a href="/conception/" class="hdr-link">Conception</a>
|
||||||
<div class="structure-cell structure-cell--faq">
|
<a href="/formation/" class="hdr-link">Formation</a>
|
||||||
<Faq />
|
<a href="/consulting/" class="hdr-link">Consulting</a>
|
||||||
</div>
|
<a href="https://fr.linkedin.com/in/electron-rare" target="_blank" rel="noopener" class="hdr-link hdr-social" title="LinkedIn">in</a>
|
||||||
</section>
|
<a href="https://github.com/electron-rare/" target="_blank" rel="noopener" class="hdr-link hdr-social" title="GitHub">gh</a>
|
||||||
<section class="structure-grid structure-grid--conversion" data-reveal>
|
<a href="/contact/" class="hdr-cta">Contact</a>
|
||||||
<div class="structure-cell structure-cell--contact">
|
|
||||||
<Contact />
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
<footer class="site-footer border-t border-[var(--line)] py-6 text-sm text-[var(--text-muted)]">
|
|
||||||
<div class="site-shell footer-grid footer-grid--enhanced">
|
|
||||||
<div class="footer-col footer-col--brand">
|
|
||||||
<p class="m-0 footer-title">L'électron rare</p>
|
|
||||||
<p class="mb-0 footer-copy">© 2026 Clément Saillant</p>
|
|
||||||
</div>
|
|
||||||
<nav aria-label="Ressources" class="footer-links footer-links--single-rail">
|
|
||||||
<a href="#a-propos" class="footer-link">Approche</a>
|
|
||||||
<a href="#graphic-sprints-title" class="footer-link">Missions</a>
|
|
||||||
<a href={formationHref} class="footer-link">Formation</a>
|
|
||||||
<a href="#faq" class="footer-link">FAQ</a>
|
|
||||||
<a href="#contact" class="footer-link">Contact</a>
|
|
||||||
<a href={mentionsHref} class="footer-link">Mentions légales</a>
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
<button class="burger" id="burger-btn" aria-label="Menu">☰</button>
|
||||||
</footer>
|
</header>
|
||||||
</Fragment>
|
<div class="mobile-menu" id="mobile-menu">
|
||||||
) : (
|
<button class="close-btn" id="close-menu">✕</button>
|
||||||
<!-- COUNTDOWN — WebGL placeholder -->
|
<a href="/">Accueil</a><a href="/conception/">Conception</a><a href="/formation/">Formation</a><a href="/consulting/">Consulting</a><a href="/contact/">Contact</a>
|
||||||
<main class="countdown-page">
|
|
||||||
<!-- WebGL goes here -->
|
|
||||||
<div class="webgl-bg" id="webgl-countdown"></div>
|
|
||||||
|
|
||||||
<div class="overlay">
|
|
||||||
<div class="top-bar">
|
|
||||||
<div class="logo-sprite" id="logo-sprite" aria-label="L'Electron Rare"></div>
|
|
||||||
<span class="brand-mark">L'ELECTRON RARE</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="center-spacer">
|
<!-- HERO -->
|
||||||
<div class="tagline-area" id="tagline-bubble" aria-live="polite">
|
<div class="ov ov-hero on" data-sec="hero">
|
||||||
<span class="tagline-cursor">></span>
|
<div class="hero-bottom">
|
||||||
<span class="tagline-text" id="tagline"></span>
|
<h1 class="hero-t">
|
||||||
|
<span class="hl1">Systèmes</span>
|
||||||
|
<span class="hl2">électroniques</span>
|
||||||
|
<span class="hl3">spécifiques</span>
|
||||||
|
</h1>
|
||||||
|
<p class="hero-s">électronique · automatisme · énergie</p>
|
||||||
|
<p class="hero-s2">Conception · Mise au point · Fiabilisation · Consulting · Formation</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bottom-bar">
|
<!-- FORMATS -->
|
||||||
<span class="bottom-left">electronique · automatisme · energie</span>
|
<div class="ov ov-formats" data-sec="formats">
|
||||||
<span class="bottom-right">mai 2026</span>
|
<div class="fmt">
|
||||||
|
<p class="fmt-t" style="color:#5bd1d8">Conception</p>
|
||||||
|
<p class="fmt-d">Schéma · PCB · Firmware · Validation</p>
|
||||||
|
</div>
|
||||||
|
<div class="fmt">
|
||||||
|
<p class="fmt-t" style="color:#a78bfa">Formation</p>
|
||||||
|
<p class="fmt-d">PCB · KiCad · µC · Soudure</p>
|
||||||
|
</div>
|
||||||
|
<div class="fmt">
|
||||||
|
<p class="fmt-t" style="color:#f59e0b">Consulting</p>
|
||||||
|
<p class="fmt-d">Audit · Diagnostic · Transfert</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cartouche">
|
<!-- CONFIANCE -->
|
||||||
<p class="cartouche-title">L'électron rare</p>
|
<div class="trust" id="trust">
|
||||||
<p class="cartouche-copy">© 2026 Clément Saillant — systèmes électroniques spécifiques, conseil, formation — industrie, culture et projets multi-techniques.</p>
|
<p class="trust-k">Ils m'ont fait confiance</p>
|
||||||
<p class="cartouche-contact">Contact : <a href="https://fr.linkedin.com/in/electron-rare" target="_blank" rel="noopener">LinkedIn</a> · <a href="mailto:contact@lelectronrare.fr">contact@lelectronrare.fr</a></p>
|
<p class="trust-names">KXKM · Spectacle vivant · Secteur ferroviaire · Industrie automatisée · Écoles d'art numérique</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
)}
|
|
||||||
</BaseLayout>
|
|
||||||
|
|
||||||
{!isLaunched && (
|
<div class="scroll-track"></div>
|
||||||
|
|
||||||
|
<!-- SEO: texte indexable caché -->
|
||||||
|
<div class="sr-only" aria-hidden="false">
|
||||||
|
<h1>L'électron rare — Systèmes électroniques spécifiques</h1>
|
||||||
|
<p>Conception, mise au point et fiabilisation de systèmes électroniques spécifiques, automatismes et ensembles liés à l'énergie, au stockage et à l'optimisation terrain.</p>
|
||||||
|
<h2>Domaines d'expertise</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Électronique spécifique : cartes, interfaces, capteurs, alimentation</li>
|
||||||
|
<li>Instrumentation et automatisme : bancs, automates, variateurs, protocoles terrain</li>
|
||||||
|
<li>Énergie et stockage : batterie, conversion, supervision, télémétrie</li>
|
||||||
|
<li>Dispositifs pour le réel : audio, LED, scène, robustesse terrain</li>
|
||||||
|
<li>Consulting et formation : audit, transfert, pédagogie, projet</li>
|
||||||
|
</ul>
|
||||||
|
<h2>Cas concrets</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Industries créatives : système audio embarqué sur batterie pour le spectacle (KXKM)</li>
|
||||||
|
<li>Industrie : diagnostic et remise en service d'une ligne automatisée</li>
|
||||||
|
<li>Formation : formation PCB et embarqué pour une école d'art numérique</li>
|
||||||
|
</ul>
|
||||||
|
<h2>Services</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Conception : schéma, PCB, firmware, validation</li>
|
||||||
|
<li>Formation : PCB, KiCad, microcontrôleurs, soudure</li>
|
||||||
|
<li>Consulting : audit, diagnostic, transfert</li>
|
||||||
|
</ul>
|
||||||
|
<h2>Ils m'ont fait confiance</h2>
|
||||||
|
<p>KXKM (spectacle vivant), secteur ferroviaire, industrie automatisée, écoles d'art numérique.</p>
|
||||||
|
<h2>À propos</h2>
|
||||||
|
<p>Clément Saillant — 10 ans d'expérience en systèmes électroniques. Contributeur open-source KiCad et KXKM. Ambassadeur IA appliquée à l'électronique.</p>
|
||||||
|
<p>Contact : <a href="/contact/">Parlons de votre projet</a> — contact@lelectronrare.fr · <a href="https://blog.saillant.cc">Blog technique</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<div style="position:fixed;inset:0;z-index:9999;background:#06080b;display:flex;flex-direction:column;align-items:center;justify-content:center;color:#fff;font-family:monospace;text-align:center;padding:40px;">
|
||||||
|
<img src="/assets/photos/bmu-assembly.jpg" alt="BMU v2 Assembly" style="width:clamp(200px,50vw,500px);border-radius:12px;margin-bottom:20px;" />
|
||||||
|
<h1 style="font-size:24px;">L'électron rare</h1>
|
||||||
|
<p style="color:rgba(255,255,255,0.5);margin:12px 0;">Systèmes électroniques spécifiques</p>
|
||||||
|
<a href="https://www.lelectronrare.fr/" style="color:#5bd1d8;margin-top:16px;">Accéder au site →</a>
|
||||||
|
</div>
|
||||||
|
</noscript>
|
||||||
|
|
||||||
|
<div class="inf">L'électron rare · Clément Saillant</div>
|
||||||
|
|
||||||
|
<!-- GTM for A/B tracking -->
|
||||||
<script>
|
<script>
|
||||||
const messages = [
|
window.dataLayer = window.dataLayer || [];
|
||||||
"Charge des condensateurs en cours...",
|
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!=='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-5SLM67QF');
|
||||||
"Calibration de l'oscilloscope...",
|
</script>
|
||||||
"Soudure des derniers composants...",
|
|
||||||
"Vérification de la BOM...",
|
|
||||||
"Flash du firmware en cours...",
|
|
||||||
"Mesure de continuité... bip.",
|
|
||||||
"Routage des dernières pistes...",
|
|
||||||
"Test de la chaîne d'alimentation...",
|
|
||||||
"Programmation du bootloader...",
|
|
||||||
"Revue de schéma en cours...",
|
|
||||||
"Placement des composants CMS...",
|
|
||||||
"Passage au four de refusion...",
|
|
||||||
"Debug JTAG en cours...",
|
|
||||||
"Vérification du plan de masse...",
|
|
||||||
"Analyse du spectre RF..."
|
|
||||||
];
|
|
||||||
let msgIndex = Math.floor(Math.random() * messages.length);
|
|
||||||
|
|
||||||
function rotateMessage() {
|
<script>
|
||||||
const bubble = document.getElementById('tagline-bubble');
|
const prog = document.getElementById('prog');
|
||||||
const el = document.getElementById('tagline');
|
const dots = [...document.querySelectorAll('.dot')];
|
||||||
if (!bubble || !el) return;
|
const overlays = [...document.querySelectorAll('.ov')];
|
||||||
bubble.style.opacity = '0';
|
const RANGES = [
|
||||||
bubble.style.transform = 'translateY(4px)';
|
['hero',0,0.10],['hero-to-exp',0.10,0.16],
|
||||||
setTimeout(() => {
|
['exp-elec',0.16,0.20],['exp-auto',0.20,0.24],['exp-energie',0.24,0.28],
|
||||||
el.textContent = messages[msgIndex];
|
['exp-dispositifs',0.28,0.31],['exp-consulting',0.31,0.34],
|
||||||
bubble.style.opacity = '1';
|
['eclate-recul',0.34,0.36],['eclate-sep',0.36,0.39],['eclate-plat',0.39,0.41],
|
||||||
bubble.style.transform = 'translateY(0)';
|
['cas',0.41,0.65],
|
||||||
msgIndex = (msgIndex + 1) % messages.length;
|
['media',0.65,0.81],['formats',0.81,0.91],['contact',0.91,1.0],
|
||||||
}, 400);
|
];
|
||||||
|
function update() {
|
||||||
|
const max = document.documentElement.scrollHeight - window.innerHeight;
|
||||||
|
const t = max > 0 ? window.scrollY / max : 0;
|
||||||
|
if (prog) prog.style.width = (t*100)+'%';
|
||||||
|
let active = 'hero';
|
||||||
|
for (const [id,s,e] of RANGES) { if (t>=s&&t<=e){active=id;break;} }
|
||||||
|
overlays.forEach(el => el.classList.toggle('on', el.getAttribute('data-sec')===active));
|
||||||
|
dots.forEach(d => d.classList.toggle('on', d.getAttribute('data-s')===active));
|
||||||
|
}
|
||||||
|
window.addEventListener('scroll', update, {passive:true});
|
||||||
|
update();
|
||||||
|
|
||||||
|
// Dots cliquables
|
||||||
|
dots.forEach(d => {
|
||||||
|
d.addEventListener('click', () => {
|
||||||
|
const p = parseFloat(d.getAttribute('data-p') || '0');
|
||||||
|
const max = document.documentElement.scrollHeight - window.innerHeight;
|
||||||
|
window.scrollTo({ top: Math.round(max * p), behavior: 'smooth' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// GA4 tracking
|
||||||
|
document.querySelectorAll('a[href]').forEach(a => {
|
||||||
|
a.addEventListener('click', () => {
|
||||||
|
const label = a.textContent?.trim() || a.getAttribute('href') || '';
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
window.dataLayer.push({ event: 'click', event_category: 'navigation', event_label: label, da_variant: 'preview', page_path: '/preview/' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Burger menu
|
||||||
|
document.getElementById('burger-btn')?.addEventListener('click',()=>document.getElementById('mobile-menu')?.classList.add('open'));
|
||||||
|
document.getElementById('close-menu')?.addEventListener('click',()=>document.getElementById('mobile-menu')?.classList.remove('open'));
|
||||||
|
|
||||||
|
// Preloader — télécharge les assets en arrière-plan avec progression réelle
|
||||||
|
const loader = document.getElementById('loader');
|
||||||
|
const loaderFill = document.getElementById('loader-fill');
|
||||||
|
const loaderStatus = document.getElementById('loader-status');
|
||||||
|
|
||||||
|
const ASSETS = [
|
||||||
|
{ url: '/assets/models3d/bmu_v2.glb', label: 'Carte BMU principale', size: 5500000 },
|
||||||
|
{ url: '/assets/models3d/i2c_repeater.glb', label: 'Carte I2C Repeater', size: 1800000 },
|
||||||
|
{ url: '/assets/models3d/mosfet_switch.glb', label: 'Cartes Mosfet Switch', size: 1500000 },
|
||||||
|
];
|
||||||
|
const TOTAL_SIZE = ASSETS.reduce((s, a) => s + a.size, 0);
|
||||||
|
|
||||||
|
let loadedBytes = 0;
|
||||||
|
let allDone = false;
|
||||||
|
|
||||||
|
function updateProgress(bytes, label) {
|
||||||
|
loadedBytes += bytes;
|
||||||
|
const pct = Math.min(85, Math.round(loadedBytes / TOTAL_SIZE * 85));
|
||||||
|
if (loaderFill) loaderFill.style.width = pct + '%';
|
||||||
|
if (loaderStatus) loaderStatus.textContent = label;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sprite = document.getElementById('logo-sprite');
|
async function preloadAsset(asset) {
|
||||||
if (sprite) sprite.style.backgroundImage = 'url(/assets/brand/logo-mark.png)';
|
try {
|
||||||
|
const res = await fetch(asset.url);
|
||||||
|
const reader = res.body?.getReader();
|
||||||
|
if (!reader) { updateProgress(asset.size, asset.label); return; }
|
||||||
|
let received = 0;
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) break;
|
||||||
|
received += value.length;
|
||||||
|
updateProgress(value.length, asset.label);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
updateProgress(asset.size, asset.label + ' (cache)');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rotateMessage();
|
function hideLoader() {
|
||||||
setInterval(rotateMessage, 8000);
|
if (allDone) return;
|
||||||
|
allDone = true;
|
||||||
|
if (loaderFill) loaderFill.style.width = '100%';
|
||||||
|
if (loaderStatus) loaderStatus.textContent = 'Prêt';
|
||||||
|
setTimeout(() => { if (loader) loader.classList.add('done'); }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 1 : preload les GLB en streaming
|
||||||
|
(async () => {
|
||||||
|
if (loaderStatus) loaderStatus.textContent = 'Téléchargement des modèles 3D...';
|
||||||
|
for (const asset of ASSETS) {
|
||||||
|
await preloadAsset(asset);
|
||||||
|
}
|
||||||
|
if (loaderStatus) loaderStatus.textContent = 'Initialisation WebGL...';
|
||||||
|
if (loaderFill) loaderFill.style.width = '90%';
|
||||||
|
|
||||||
|
// Phase 2 : attendre le canvas Three.js
|
||||||
|
const checkCanvas = setInterval(() => {
|
||||||
|
const cvs = document.querySelector('canvas');
|
||||||
|
if (cvs && cvs.width > 0) {
|
||||||
|
clearInterval(checkCanvas);
|
||||||
|
if (loaderFill) loaderFill.style.width = '95%';
|
||||||
|
if (loaderStatus) loaderStatus.textContent = 'Compilation shaders...';
|
||||||
|
setTimeout(hideLoader, 600);
|
||||||
|
}
|
||||||
|
}, 150);
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Fallback 15s max
|
||||||
|
setTimeout(hideLoader, 15000);
|
||||||
</script>
|
</script>
|
||||||
)}
|
</body>
|
||||||
|
</html>
|
||||||
<style>
|
|
||||||
.countdown-page { position:relative; min-height:100vh; overflow:hidden; background:#000; color:#fff; font-family:'Manrope',-apple-system,sans-serif; }
|
|
||||||
.webgl-bg { position:fixed; inset:0; z-index:0; width:100vw; height:100vh; pointer-events:auto; }
|
|
||||||
.overlay { position:relative; z-index:10; min-height:100vh; display:flex; flex-direction:column; justify-content:space-between; padding:clamp(20px,3vw,40px) clamp(24px,5vw,60px); pointer-events:none; }
|
|
||||||
.overlay > * { pointer-events:none; }
|
|
||||||
.overlay a { pointer-events:auto; }
|
|
||||||
.top-bar { display:flex; align-items:center; gap:16px; }
|
|
||||||
.logo-sprite { width:48px; height:48px; background-size:contain; background-repeat:no-repeat; filter:brightness(1.2) drop-shadow(0 0 12px rgba(91,209,216,0.4)); }
|
|
||||||
.brand-mark { font-size:11px; font-weight:600; letter-spacing:0.25em; color:rgba(255,255,255,0.5); text-transform:uppercase; }
|
|
||||||
.center-spacer { flex:1; display:flex; align-items:center; justify-content:center; }
|
|
||||||
.tagline-area { display:flex; align-items:center; gap:8px; padding:10px 20px; background:rgba(255,255,255,0.03); border:1px solid rgba(255,255,255,0.06); border-radius:8px; transition:opacity 0.4s ease, transform 0.4s ease; pointer-events:auto; }
|
|
||||||
.tagline-cursor { font-family:monospace; font-size:14px; color:#5bd1d8; animation:blink 1s step-end infinite; }
|
|
||||||
@keyframes blink { 50% { opacity:0; } }
|
|
||||||
.tagline-text { font-family:monospace; font-size:clamp(11px,1.3vw,14px); color:rgba(255,255,255,0.4); }
|
|
||||||
.bottom-bar { display:flex; justify-content:space-between; align-items:flex-end; }
|
|
||||||
.bottom-left, .bottom-right { font-size:clamp(10px,1.1vw,12px); color:rgba(255,255,255,0.15); letter-spacing:0.08em; text-transform:uppercase; }
|
|
||||||
.cartouche { position:fixed; bottom:clamp(16px,2vw,24px); right:clamp(16px,2vw,24px); max-width:320px; padding:14px 18px; background:rgba(0,0,0,0.5); backdrop-filter:blur(12px); border:1px solid rgba(255,255,255,0.06); border-radius:12px; pointer-events:auto; }
|
|
||||||
.cartouche-title { font-size:13px; font-weight:700; color:rgba(255,255,255,0.6); margin:0 0 6px; }
|
|
||||||
.cartouche-copy { font-size:9px; color:rgba(255,255,255,0.25); margin:0 0 4px; line-height:1.4; }
|
|
||||||
.cartouche-contact { font-size:9px; color:rgba(255,255,255,0.25); margin:0; }
|
|
||||||
.cartouche-contact a { color:#5bd1d8; text-decoration:none; }
|
|
||||||
@media (max-width:640px) { .bottom-bar{flex-direction:column;align-items:center;gap:4px;} .cartouche{position:relative;bottom:auto;right:auto;max-width:100%;margin-top:12px;text-align:center;} }
|
|
||||||
</style>
|
|
||||||
|
|||||||
+285
-158
@@ -1,177 +1,304 @@
|
|||||||
---
|
---
|
||||||
import BaseLayout from '@/layouts/BaseLayout.astro';
|
import { BmuViewer } from '@/components/BmuViewer';
|
||||||
import '@/styles/global.css';
|
|
||||||
import '@/styles/home-workbench.css';
|
|
||||||
import SiteHeader from '@/components/SiteHeader.astro';
|
|
||||||
import { About } from '@/components/sections/About';
|
|
||||||
import { CaseStudies } from '@/components/sections/CaseStudies';
|
|
||||||
import { GraphicSprints } from '@/components/sections/GraphicSprints';
|
|
||||||
import Contact from '@/components/sections/Contact.astro';
|
|
||||||
import Faq from '@/components/sections/Faq.astro';
|
|
||||||
import { withSiteBase } from '@/lib/site';
|
|
||||||
import { TRACK_EVENTS, trackAttrs } from '@/lib/tracking';
|
|
||||||
|
|
||||||
const formationHref = withSiteBase('/formation/');
|
|
||||||
const mentionsHref = withSiteBase('/mentions-legales/');
|
|
||||||
const headerItems = [
|
|
||||||
{ href: '#a-propos', label: 'Approche' },
|
|
||||||
{ href: '#graphic-sprints-title', label: 'Missions' },
|
|
||||||
{ href: formationHref, label: 'Formation' },
|
|
||||||
{ href: '#faq', label: 'FAQ' },
|
|
||||||
{ href: '#contact', label: 'Contact', desktopOnly: true }
|
|
||||||
];
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout>
|
<html lang="fr">
|
||||||
<!-- Loading screen — BMU photo while WebGL loads -->
|
<head>
|
||||||
<div class="loading-screen" id="loading-screen">
|
<meta charset="UTF-8" />
|
||||||
<div class="loading-content">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<img src="/assets/photos/bmu-assembly.jpg" alt="BMU v2 Assembly" class="loading-bmu-img" />
|
<title>L'électron rare — Systèmes électroniques spécifiques</title>
|
||||||
<h1 class="loading-title">L'<span class="loading-electric">é</span>lectron rare</h1>
|
<meta name="description" content="Conception, mise au point et fiabilisation de systèmes électroniques spécifiques, automatismes et ensembles liés à l'énergie. Consulting et formation PCB, KiCad, µC." />
|
||||||
<div class="loading-bar">
|
<meta property="og:title" content="L'électron rare — Systèmes électroniques spécifiques" />
|
||||||
<div class="loading-bar-fill" id="loading-bar-fill"></div>
|
<meta property="og:description" content="Conception, mise au point et fiabilisation de systèmes électroniques spécifiques. Consulting et formation." />
|
||||||
</div>
|
<meta property="og:image" content="https://www.lelectronrare.fr/assets/og-cover.jpg" />
|
||||||
<p class="loading-status" id="loading-status">Chargement...</p>
|
<meta property="og:url" content="https://www.lelectronrare.fr/preview/" />
|
||||||
<a href="/static/" class="loading-static-btn">Version sans 3D →</a>
|
<meta property="og:type" content="website" />
|
||||||
<p class="loading-tagline">électronique · automatisme · énergie</p>
|
<meta property="og:site_name" content="L'électron rare" />
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:title" content="L'électron rare — Systèmes électroniques spécifiques" />
|
||||||
|
<meta name="twitter:description" content="Conception, mise au point et fiabilisation de systèmes électroniques spécifiques." />
|
||||||
|
<meta name="twitter:image" content="https://www.lelectronrare.fr/assets/og-cover.jpg" />
|
||||||
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||||
|
<script type="application/ld+json">
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "ProfessionalService",
|
||||||
|
"name": "L'électron rare",
|
||||||
|
"description": "Conception, mise au point et fiabilisation de systèmes électroniques spécifiques, automatismes et ensembles liés à l'énergie.",
|
||||||
|
"url": "https://www.lelectronrare.fr",
|
||||||
|
"email": "contact@lelectronrare.fr",
|
||||||
|
"founder": { "@type": "Person", "name": "Clément Saillant" },
|
||||||
|
"areaServed": "France",
|
||||||
|
"serviceType": ["Conception électronique", "Formation PCB et embarqué", "Consulting technique", "Automatisme industriel"],
|
||||||
|
"sameAs": ["https://fr.linkedin.com/in/electron-rare", "https://github.com/electron-rare/"]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
html { background: #06080b; color: #fff; overflow-x: hidden; }
|
||||||
|
body { background: #06080b; min-height: 100vh; font-family: 'Courier New', monospace; }
|
||||||
|
.scroll-track { height: 1000vh; position: relative; z-index: 1; }
|
||||||
|
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0; }
|
||||||
|
|
||||||
|
/* Loader */
|
||||||
|
.loader { position: fixed; inset: 0; z-index: 9999; background: #06080b; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px; transition: opacity 0.8s ease, visibility 0.8s ease; }
|
||||||
|
.loader.done { opacity: 0; visibility: hidden; pointer-events: none; }
|
||||||
|
.loader-logo { width: clamp(60px, 8vw, 100px); height: auto; filter: drop-shadow(0 0 20px rgba(91,209,216,0.3)); }
|
||||||
|
.loader-name { font-size: clamp(20px, 2.5vw, 32px); font-weight: 800; color: rgba(255,255,255,0.8); letter-spacing: 0.06em; }
|
||||||
|
.loader-bar { width: clamp(180px, 30vw, 280px); height: 2px; background: rgba(255,255,255,0.06); border-radius: 1px; overflow: hidden; }
|
||||||
|
.loader-fill { height: 100%; width: 0%; background: linear-gradient(90deg, #5bd1d8, #a78bfa); transition: width 0.3s ease; }
|
||||||
|
.loader-status { font-size: 10px; color: rgba(255,255,255,0.25); letter-spacing: 0.12em; text-transform: uppercase; }
|
||||||
|
.loader-tagline { font-size: 12px; color: rgba(255,255,255,0.3); letter-spacing: 0.08em; margin-bottom: 20px; }
|
||||||
|
.loader-actions { margin-top: 24px; }
|
||||||
|
.loader-skip { display: inline-block; padding: 10px 24px; border: 1px solid rgba(91,209,216,0.3); border-radius: 8px; color: rgba(91,209,216,0.7); font-size: 12px; text-decoration: none; letter-spacing: 0.04em; transition: all 0.2s; }
|
||||||
|
.loader-skip:hover { border-color: #5bd1d8; color: #5bd1d8; background: rgba(91,209,216,0.08); }
|
||||||
|
.loader-a11y { margin-top: 20px; font-size: 10px; color: rgba(255,255,255,0.15); }
|
||||||
|
.loader-a11y a { color: rgba(255,255,255,0.25); text-decoration: none; }
|
||||||
|
.loader-a11y a:hover { color: #5bd1d8; }
|
||||||
|
|
||||||
|
.ov { position: fixed; z-index: 3; pointer-events: none; opacity: 0; transform: translateY(6px); transition: opacity 0.5s, transform 0.5s; }
|
||||||
|
.ov.on { opacity: 1; transform: translateY(0); }
|
||||||
|
|
||||||
|
/* Hero */
|
||||||
|
.ov-hero { inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; }
|
||||||
|
.hero-bottom { display: flex; flex-direction: column; align-items: center; padding-bottom: clamp(60px, 10vh, 120px); }
|
||||||
|
.hero-t { text-align: center; margin: 0; line-height: 1.1; }
|
||||||
|
.hl1,.hl3 { display: block; font-size: clamp(28px, 4.5vw, 56px); font-weight: 800; color: rgba(255,255,255,0.85); letter-spacing: -0.02em; }
|
||||||
|
.hl2 { display: block; font-size: clamp(32px, 5.5vw, 64px); font-weight: 800; color: #5bd1d8; letter-spacing: -0.02em; }
|
||||||
|
.hero-s { font-size: clamp(11px, 1.2vw, 15px); color: rgba(255,255,255,0.3); letter-spacing: 0.15em; text-transform: uppercase; margin: 16px 0 0; }
|
||||||
|
.hero-s2 { font-size: clamp(10px, 1vw, 13px); color: rgba(255,255,255,0.2); letter-spacing: 0.12em; text-transform: uppercase; margin: 8px 0 0; }
|
||||||
|
|
||||||
|
/* Header */
|
||||||
|
.site-hdr { position: fixed; top: 0; left: 0; right: 0; z-index: 10; display: flex; align-items: center; justify-content: space-between; padding: 16px clamp(20px, 3vw, 40px); pointer-events: none; }
|
||||||
|
.hdr-left { display: flex; align-items: center; gap: 10px; pointer-events: auto; }
|
||||||
|
.hdr-logo { width: 36px; height: auto; filter: drop-shadow(0 0 12px rgba(91,209,216,0.3)); }
|
||||||
|
.hdr-name { font-size: 16px; font-weight: 800; color: rgba(255,255,255,0.8); letter-spacing: 0.04em; }
|
||||||
|
.hdr-nav { display: flex; align-items: center; gap: 8px; pointer-events: auto; }
|
||||||
|
.hdr-link { padding: 8px 16px; color: rgba(255,255,255,0.6); font-size: 12px; font-weight: 600; text-decoration: none; letter-spacing: 0.04em; transition: color 0.2s; }
|
||||||
|
.hdr-link:hover { color: #5bd1d8; }
|
||||||
|
.hdr-social { font-weight: 800; font-size: 11px; text-transform: uppercase; padding: 8px 10px; opacity: 0.5; }
|
||||||
|
.hdr-social:hover { opacity: 1; }
|
||||||
|
.hdr-cta { padding: 8px 20px; border-radius: 6px; background: rgba(91,209,216,0.15); border: 1px solid rgba(91,209,216,0.3); color: #5bd1d8; font-size: 12px; font-weight: 700; text-decoration: none; letter-spacing: 0.05em; transition: background 0.2s; }
|
||||||
|
.hdr-cta:hover { background: rgba(91,209,216,0.25); }
|
||||||
|
|
||||||
|
/* Formats */
|
||||||
|
.ov-formats { bottom: clamp(30px, 5vh, 60px); left: 50%; transform: translateX(-50%); display: flex; gap: 12px; max-width: 820px; width: 92vw; }
|
||||||
|
.ov-formats.on { transform: translateX(-50%); }
|
||||||
|
.fmt { flex: 1; padding: 14px 16px; border-radius: 8px; background: rgba(6,8,11,0.7); backdrop-filter: blur(12px); border: 1px solid rgba(255,255,255,0.05); text-align: center; }
|
||||||
|
.fmt-t { font-size: 13px; font-weight: 700; color: #fff; letter-spacing: 0.06em; text-transform: uppercase; margin-bottom: 4px; }
|
||||||
|
.fmt-d { font-size: 10px; color: rgba(255,255,255,0.35); }
|
||||||
|
|
||||||
|
/* Progress */
|
||||||
|
.prog { position: fixed; top: 0; left: 0; height: 2px; z-index: 5; background: linear-gradient(90deg, #5bd1d8, #a78bfa, #f59e0b); width: 0%; }
|
||||||
|
.inf { position: fixed; bottom: 10px; right: 14px; z-index: 4; font-size: 8px; color: rgba(255,255,255,0.1); letter-spacing: 0.06em; }
|
||||||
|
|
||||||
|
/* Dots */
|
||||||
|
.dots { position: fixed; right: 20px; top: 50%; transform: translateY(-50%); z-index: 4; display: flex; flex-direction: column; gap: 14px; }
|
||||||
|
.dot { width: 12px; height: 12px; border-radius: 50%; background: rgba(255,255,255,0.15); border: 1px solid rgba(255,255,255,0.1); transition: background 0.3s, transform 0.3s, border-color 0.3s; cursor: pointer; pointer-events: auto; position: relative; }
|
||||||
|
.dot.on { background: #5bd1d8; border-color: #5bd1d8; transform: scale(1.3); box-shadow: 0 0 10px rgba(91,209,216,0.4); }
|
||||||
|
.dot:hover { background: rgba(91,209,216,0.5); border-color: rgba(91,209,216,0.5); }
|
||||||
|
.dot[data-l]:not([data-l=""])::after { content: attr(data-l); position: absolute; right: 20px; top: 50%; transform: translateY(-50%); font-size: 9px; color: rgba(255,255,255,0.5); letter-spacing: 0.05em; white-space: nowrap; opacity: 0; transition: opacity 0.2s; pointer-events: none; }
|
||||||
|
.dot[data-l]:not([data-l=""]):hover::after { opacity: 1; }
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.ov-formats { flex-direction: column; gap: 6px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body data-da-variant="preview">
|
||||||
|
<!-- Loader -->
|
||||||
|
<div class="loader" id="loader">
|
||||||
|
<img src="/assets/brand/logo-mark.svg" alt="L'électron rare" class="loader-logo" />
|
||||||
|
<p class="loader-name">L'électron rare</p>
|
||||||
|
<p class="loader-tagline">Systèmes électroniques spécifiques</p>
|
||||||
|
<div class="loader-bar"><div class="loader-fill" id="loader-fill"></div></div>
|
||||||
|
<p class="loader-status" id="loader-status">Chargement des modèles 3D...</p>
|
||||||
|
<div class="loader-actions">
|
||||||
|
<a href="/conception/" class="loader-skip">Accéder au site sans 3D →</a>
|
||||||
</div>
|
</div>
|
||||||
|
<p class="loader-a11y">Version accessible : <a href="/conception/">Conception</a> · <a href="/formation/">Formation</a> · <a href="/consulting/">Consulting</a> · <a href="/contact/">Contact</a></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- WebGL background placeholder — NEW WEBGL GOES HERE -->
|
<BmuViewer client:load />
|
||||||
<div id="webgl-container"></div>
|
<div class="prog" id="prog"></div>
|
||||||
|
|
||||||
<SiteHeader brandHref="#top" items={headerItems} quickHref="#contact" />
|
<div class="dots" id="dots">
|
||||||
|
<div class="dot on" data-s="hero" data-p="0.03" data-l="Hero"></div>
|
||||||
<main id="main-content" class="dark-site">
|
<div class="dot" data-s="hero-to-exp" data-p="0.09" data-l=""></div>
|
||||||
<div class="pcb-spacer pcb-spacer--hero" id="top" aria-hidden="true"></div>
|
<div class="dot" data-s="exp-elec" data-p="0.14" data-l="Élec"></div>
|
||||||
|
<div class="dot" data-s="exp-auto" data-p="0.18" data-l="Auto"></div>
|
||||||
<section class="section-dark" id="a-propos">
|
<div class="dot" data-s="exp-energie" data-p="0.22" data-l="Énergie"></div>
|
||||||
<div class="section-content">
|
<div class="dot" data-s="exp-dispositifs" data-p="0.255" data-l="Dispositifs"></div>
|
||||||
<About />
|
<div class="dot" data-s="exp-consulting" data-p="0.285" data-l="Consulting"></div>
|
||||||
|
<div class="dot" data-s="eclate-recul" data-p="0.31" data-l=""></div>
|
||||||
|
<div class="dot" data-s="eclate-sep" data-p="0.34" data-l=""></div>
|
||||||
|
<div class="dot" data-s="eclate-plat" data-p="0.36" data-l=""></div>
|
||||||
|
<div class="dot" data-s="cas-kxkm" data-p="0.42" data-l="KXKM"></div>
|
||||||
|
<div class="dot" data-s="cas-industrie" data-p="0.52" data-l="Industrie"></div>
|
||||||
|
<div class="dot" data-s="cas-formation" data-p="0.60" data-l="Formation"></div>
|
||||||
|
<div class="dot" data-s="media" data-p="0.70" data-l="Médias"></div>
|
||||||
|
<div class="dot" data-s="formats" data-p="0.85" data-l="Services"></div>
|
||||||
|
<div class="dot" data-s="contact" data-p="0.95" data-l="Contact"></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
|
|
||||||
<div class="pcb-spacer" aria-hidden="true"></div>
|
<!-- HEADER -->
|
||||||
|
<header class="site-hdr">
|
||||||
<section class="section-dark">
|
<div class="hdr-left">
|
||||||
<div class="section-content">
|
<img src="/assets/brand/logo-mark.svg" alt="" class="hdr-logo" />
|
||||||
<CaseStudies />
|
<span class="hdr-name">L'électron rare</span>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
<nav class="hdr-nav">
|
||||||
|
<a href="https://www.lelectronrare.fr/#graphic-sprints-title" class="hdr-link">Conception</a>
|
||||||
<div class="pcb-spacer" aria-hidden="true"></div>
|
<a href="https://www.lelectronrare.fr/formation/" class="hdr-link">Formation</a>
|
||||||
|
<a href="https://www.lelectronrare.fr/#a-propos" class="hdr-link">Consulting</a>
|
||||||
<section class="photo-strip section-dark" aria-label="Photos de terrain">
|
<a href="https://fr.linkedin.com/in/electron-rare" target="_blank" rel="noopener" class="hdr-link hdr-social" title="LinkedIn">in</a>
|
||||||
<div class="photo-strip-track">
|
<a href="https://github.com/electron-rare/" target="_blank" rel="noopener" class="hdr-link hdr-social" title="GitHub">gh</a>
|
||||||
<img src={withSiteBase('/assets/photos/pcb-teensy-led-kxkm.webp')} alt="PCB custom Teensy LED" loading="lazy" decoding="async" width="280" height="190" class="photo-strip-img" />
|
<a href="https://www.lelectronrare.fr/" class="hdr-link" style="opacity:0.4">Site →</a>
|
||||||
<img src={withSiteBase('/assets/photos/platine-automate-cablage.webp')} alt="Câblage automate industriel" loading="lazy" decoding="async" width="280" height="190" class="photo-strip-img" />
|
<a href="https://www.lelectronrare.fr/#contact" class="hdr-cta">Contact</a>
|
||||||
<img src={withSiteBase('/assets/photos/bench-bms-batteries-xt60.webp')} alt="Banc de test BMS batteries" loading="lazy" decoding="async" width="280" height="190" class="photo-strip-img" />
|
|
||||||
<img src={withSiteBase('/assets/photos/automate-siemens-s7.webp')} alt="Automate Siemens S7" loading="lazy" decoding="async" width="280" height="190" class="photo-strip-img" />
|
|
||||||
<img src={withSiteBase('/assets/photos/soudure-pcb-composants.webp')} alt="Soudure PCB" loading="lazy" decoding="async" width="280" height="190" class="photo-strip-img" />
|
|
||||||
<img src={withSiteBase('/assets/photos/oscilloscope-philips-vintage.webp')} alt="Oscilloscope" loading="lazy" decoding="async" width="280" height="190" class="photo-strip-img" />
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div class="pcb-spacer" aria-hidden="true"></div>
|
|
||||||
|
|
||||||
<section class="section-dark" id="graphic-sprints-title">
|
|
||||||
<div class="section-content">
|
|
||||||
<GraphicSprints />
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div class="pcb-spacer" aria-hidden="true"></div>
|
|
||||||
|
|
||||||
<section class="section-dark" id="faq">
|
|
||||||
<div class="section-content">
|
|
||||||
<Faq />
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div class="pcb-spacer" aria-hidden="true"></div>
|
|
||||||
|
|
||||||
<section class="section-dark" id="contact">
|
|
||||||
<div class="section-content">
|
|
||||||
<Contact />
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer class="dark-footer">
|
|
||||||
<div class="site-shell footer-grid footer-grid--enhanced">
|
|
||||||
<div class="footer-col footer-col--brand">
|
|
||||||
<p class="m-0 footer-title">L'électron rare</p>
|
|
||||||
<div class="footer-brand-meta">
|
|
||||||
<p class="mb-0 footer-copy">© 2026 Clément Saillant — systèmes électroniques spécifiques, conseil, formation.</p>
|
|
||||||
<p class="mb-0 footer-copy">Contact : <a href="mailto:contact@lelectronrare.fr">contact@lelectronrare.fr</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<nav aria-label="Ressources" class="footer-links footer-links--single-rail">
|
|
||||||
<a href="#a-propos" class="footer-link">Approche</a>
|
|
||||||
<a href="#graphic-sprints-title" class="footer-link">Missions</a>
|
|
||||||
<a href={formationHref} class="footer-link">Formation</a>
|
|
||||||
<a href="#faq" class="footer-link">FAQ</a>
|
|
||||||
<a href="#contact" class="footer-link">Contact</a>
|
|
||||||
<a href="https://fr.linkedin.com/in/electron-rare" target="_blank" rel="noopener" class="footer-link">LinkedIn</a>
|
|
||||||
<a href="https://github.com/electron-rare/" target="_blank" rel="noopener" class="footer-link">GitHub</a>
|
|
||||||
<a href={mentionsHref} class="footer-link">Mentions légales</a>
|
|
||||||
</nav>
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- HERO -->
|
||||||
|
<div class="ov ov-hero on" data-sec="hero">
|
||||||
|
<div class="hero-bottom">
|
||||||
|
<h1 class="hero-t">
|
||||||
|
<span class="hl1">Systèmes</span>
|
||||||
|
<span class="hl2">électroniques</span>
|
||||||
|
<span class="hl3">spécifiques</span>
|
||||||
|
</h1>
|
||||||
|
<p class="hero-s">électronique · automatisme · énergie</p>
|
||||||
|
<p class="hero-s2">Conception · Mise au point · Fiabilisation · Consulting · Formation</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
|
||||||
</BaseLayout>
|
|
||||||
|
|
||||||
|
<!-- FORMATS -->
|
||||||
|
<div class="ov ov-formats" data-sec="formats">
|
||||||
|
<div class="fmt">
|
||||||
|
<p class="fmt-t" style="color:#5bd1d8">Conception</p>
|
||||||
|
<p class="fmt-d">Schéma · PCB · Firmware · Validation</p>
|
||||||
|
</div>
|
||||||
|
<div class="fmt">
|
||||||
|
<p class="fmt-t" style="color:#a78bfa">Formation</p>
|
||||||
|
<p class="fmt-d">PCB · KiCad · µC · Soudure</p>
|
||||||
|
</div>
|
||||||
|
<div class="fmt">
|
||||||
|
<p class="fmt-t" style="color:#f59e0b">Consulting</p>
|
||||||
|
<p class="fmt-d">Audit · Diagnostic · Transfert</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="scroll-track"></div>
|
||||||
|
|
||||||
|
<!-- SEO: texte indexable caché -->
|
||||||
|
<div class="sr-only" aria-hidden="false">
|
||||||
|
<h1>L'électron rare — Systèmes électroniques spécifiques</h1>
|
||||||
|
<p>Conception, mise au point et fiabilisation de systèmes électroniques spécifiques, automatismes et ensembles liés à l'énergie, au stockage et à l'optimisation terrain.</p>
|
||||||
|
<h2>Domaines d'expertise</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Électronique spécifique : cartes, interfaces, capteurs, alimentation</li>
|
||||||
|
<li>Instrumentation et automatisme : bancs, automates, variateurs, protocoles terrain</li>
|
||||||
|
<li>Énergie et stockage : batterie, conversion, supervision, télémétrie</li>
|
||||||
|
<li>Dispositifs pour le réel : audio, LED, scène, robustesse terrain</li>
|
||||||
|
<li>Consulting et formation : audit, transfert, pédagogie, projet</li>
|
||||||
|
</ul>
|
||||||
|
<h2>Cas concrets</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Industries créatives : système audio embarqué sur batterie pour le spectacle (KXKM)</li>
|
||||||
|
<li>Industrie : diagnostic et remise en service d'une ligne automatisée</li>
|
||||||
|
<li>Formation : formation PCB et embarqué pour une école d'art numérique</li>
|
||||||
|
</ul>
|
||||||
|
<h2>Services</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Conception : schéma, PCB, firmware, validation</li>
|
||||||
|
<li>Formation : PCB, KiCad, microcontrôleurs, soudure</li>
|
||||||
|
<li>Consulting : audit, diagnostic, transfert</li>
|
||||||
|
</ul>
|
||||||
|
<p>Contact : <a href="https://www.lelectronrare.fr/#contact">Parlons de votre projet</a> — contact@lelectronrare.fr</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<noscript>
|
||||||
|
<div style="position:fixed;inset:0;z-index:9999;background:#06080b;display:flex;flex-direction:column;align-items:center;justify-content:center;color:#fff;font-family:monospace;text-align:center;padding:40px;">
|
||||||
|
<img src="/assets/photos/bmu-assembly.jpg" alt="BMU v2 Assembly" style="width:clamp(200px,50vw,500px);border-radius:12px;margin-bottom:20px;" />
|
||||||
|
<h1 style="font-size:24px;">L'électron rare</h1>
|
||||||
|
<p style="color:rgba(255,255,255,0.5);margin:12px 0;">Systèmes électroniques spécifiques</p>
|
||||||
|
<a href="https://www.lelectronrare.fr/" style="color:#5bd1d8;margin-top:16px;">Accéder au site →</a>
|
||||||
|
</div>
|
||||||
|
</noscript>
|
||||||
|
|
||||||
|
<div class="inf">L'électron rare · Clément Saillant</div>
|
||||||
|
|
||||||
|
<!-- GTM for A/B tracking -->
|
||||||
<script>
|
<script>
|
||||||
// Loading screen — auto-hide after 10s or when ready
|
window.dataLayer = window.dataLayer || [];
|
||||||
const loadScreen = document.getElementById('loading-screen');
|
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!=='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-5SLM67QF');
|
||||||
const loadBar = document.getElementById('loading-bar-fill');
|
|
||||||
const loadStatus = document.getElementById('loading-status');
|
|
||||||
|
|
||||||
const msgs = ["Chargement...", "Initialisation...", "Préparation du rendu..."];
|
|
||||||
let idx = 0;
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
idx = (idx + 1) % msgs.length;
|
|
||||||
if (loadStatus) loadStatus.textContent = msgs[idx];
|
|
||||||
if (loadBar) loadBar.style.width = Math.min(90, parseFloat(loadBar.style.width || '0') + 20) + '%';
|
|
||||||
}, 1500);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
if (loadBar) loadBar.style.width = '100%';
|
|
||||||
if (loadStatus) loadStatus.textContent = 'Prêt.';
|
|
||||||
clearInterval(interval);
|
|
||||||
setTimeout(() => loadScreen?.classList.add('loading-done'), 500);
|
|
||||||
}, 10000);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<script>
|
||||||
/* Loading screen */
|
const prog = document.getElementById('prog');
|
||||||
.loading-screen { position:fixed; inset:0; z-index:9999; background:#000; display:flex; align-items:center; justify-content:center; transition:opacity 1s ease, visibility 1s ease; }
|
const dots = [...document.querySelectorAll('.dot')];
|
||||||
.loading-screen.loading-done { opacity:0; visibility:hidden; pointer-events:none; }
|
const overlays = [...document.querySelectorAll('.ov')];
|
||||||
.loading-content { display:flex; flex-direction:column; align-items:center; text-align:center; gap:20px; padding:24px; }
|
const RANGES = [
|
||||||
.loading-bmu-img { width:clamp(200px,50vw,400px); height:auto; border-radius:16px; border:1px solid rgba(91,209,216,0.15); box-shadow:0 0 40px rgba(91,209,216,0.08); }
|
['hero',0,0.10],['hero-to-exp',0.10,0.16],
|
||||||
.loading-title { font-size:clamp(28px,5vw,48px); font-weight:800; color:#fff; margin:0; }
|
['exp-elec',0.16,0.20],['exp-auto',0.20,0.24],['exp-energie',0.24,0.28],
|
||||||
.loading-electric { color:#5bd1d8; }
|
['exp-dispositifs',0.28,0.31],['exp-consulting',0.31,0.34],
|
||||||
.loading-bar { width:clamp(200px,40vw,300px); height:2px; background:rgba(255,255,255,0.06); border-radius:1px; overflow:hidden; }
|
['eclate-recul',0.34,0.36],['eclate-sep',0.36,0.39],['eclate-plat',0.39,0.41],
|
||||||
.loading-bar-fill { height:100%; width:0%; background:linear-gradient(90deg,#5bd1d8,#f1c27a); transition:width 0.3s ease; }
|
['cas',0.41,0.65],
|
||||||
.loading-status { font-family:monospace; font-size:12px; color:rgba(255,255,255,0.3); margin:0; }
|
['media',0.65,0.81],['formats',0.81,0.91],['contact',0.91,1.0],
|
||||||
.loading-static-btn { font-size:11px; color:rgba(91,209,216,0.6); text-decoration:none; padding:8px 16px; border:1px solid rgba(91,209,216,0.15); border-radius:8px; }
|
];
|
||||||
.loading-static-btn:hover { color:#5bd1d8; border-color:rgba(91,209,216,0.4); }
|
function update() {
|
||||||
.loading-tagline { font-size:11px; color:rgba(255,255,255,0.12); letter-spacing:0.1em; text-transform:uppercase; margin:0; }
|
const max = document.documentElement.scrollHeight - window.innerHeight;
|
||||||
|
const t = max > 0 ? window.scrollY / max : 0;
|
||||||
|
if (prog) prog.style.width = (t*100)+'%';
|
||||||
|
let active = 'hero';
|
||||||
|
for (const [id,s,e] of RANGES) { if (t>=s&&t<=e){active=id;break;} }
|
||||||
|
overlays.forEach(el => el.classList.toggle('on', el.getAttribute('data-sec')===active));
|
||||||
|
dots.forEach(d => d.classList.toggle('on', d.getAttribute('data-s')===active));
|
||||||
|
}
|
||||||
|
window.addEventListener('scroll', update, {passive:true});
|
||||||
|
update();
|
||||||
|
|
||||||
/* Dark site base */
|
// Dots cliquables
|
||||||
.dark-site { position:relative; z-index:1; background:transparent; color:#e0e0e0; }
|
dots.forEach(d => {
|
||||||
.section-dark { padding:clamp(40px,8vw,100px) 0; }
|
d.addEventListener('click', () => {
|
||||||
.section-content { max-width:1200px; margin:0 auto; padding:0 clamp(20px,4vw,48px); }
|
const p = parseFloat(d.getAttribute('data-p') || '0');
|
||||||
.pcb-spacer { height:60vh; }
|
const max = document.documentElement.scrollHeight - window.innerHeight;
|
||||||
.pcb-spacer--hero { height:100vh; }
|
window.scrollTo({ top: Math.round(max * p), behavior: 'smooth' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
/* Dark overrides */
|
// GA4 tracking
|
||||||
.section-dark :global(h1), .section-dark :global(h2), .section-dark :global(h3) { color:#fff !important; }
|
document.querySelectorAll('a[href]').forEach(a => {
|
||||||
.section-dark :global(p), .section-dark :global(li) { color:rgba(255,255,255,0.7) !important; }
|
a.addEventListener('click', () => {
|
||||||
.section-dark :global(a) { color:#5bd1d8 !important; }
|
const label = a.textContent?.trim() || a.getAttribute('href') || '';
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
window.dataLayer.push({ event: 'click', event_category: 'navigation', event_label: label, da_variant: 'preview', page_path: '/preview/' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
/* Dark footer */
|
// Loader
|
||||||
.dark-footer { position:relative; z-index:2; border-top:1px solid rgba(255,255,255,0.06); padding:24px 0; background:rgba(0,0,0,0.4); }
|
const loader = document.getElementById('loader');
|
||||||
.dark-footer :global(.footer-title) { color:#fff; }
|
const loaderFill = document.getElementById('loader-fill');
|
||||||
.dark-footer :global(.footer-copy) { color:rgba(255,255,255,0.4); }
|
const loaderStatus = document.getElementById('loader-status');
|
||||||
.dark-footer :global(.footer-link) { color:rgba(255,255,255,0.5) !important; }
|
const msgs = ['Chargement des modèles 3D...', 'Compilation shaders...', 'Préparation du rendu...'];
|
||||||
</style>
|
let msgIdx = 0;
|
||||||
|
const msgInterval = setInterval(() => {
|
||||||
|
msgIdx = (msgIdx + 1) % msgs.length;
|
||||||
|
if (loaderStatus) loaderStatus.textContent = msgs[msgIdx];
|
||||||
|
if (loaderFill) loaderFill.style.width = Math.min(90, parseFloat(loaderFill.style.width || '0') + 20) + '%';
|
||||||
|
}, 1200);
|
||||||
|
|
||||||
|
function hideLoader() {
|
||||||
|
if (loaderFill) loaderFill.style.width = '100%';
|
||||||
|
if (loaderStatus) loaderStatus.textContent = 'Prêt';
|
||||||
|
clearInterval(msgInterval);
|
||||||
|
setTimeout(() => { if (loader) loader.classList.add('done'); }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkReady = setInterval(() => {
|
||||||
|
const cvs = document.querySelector('canvas');
|
||||||
|
if (cvs && cvs.width > 0) { clearInterval(checkReady); setTimeout(hideLoader, 800); }
|
||||||
|
}, 200);
|
||||||
|
setTimeout(hideLoader, 10000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import type { APIRoute } from 'astro';
|
import type { APIRoute } from 'astro';
|
||||||
import { CANONICAL_URL, PUBLIC_SITE_ROOT_URL, SITE_IS_SUBPATH_BUILD } from '../lib/site';
|
import { CANONICAL_URL, PUBLIC_SITE_ROOT_URL, SITE_IS_SUBPATH_BUILD } from '../lib/site';
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
export const GET: APIRoute = () => {
|
export const GET: APIRoute = () => {
|
||||||
const sitemapRootUrl = SITE_IS_SUBPATH_BUILD ? PUBLIC_SITE_ROOT_URL : CANONICAL_URL;
|
const sitemapRootUrl = process.env.PUBLIC_SITE_URL || CANONICAL_URL;
|
||||||
const pages = [
|
const pages = [
|
||||||
{ path: '', priority: '1.0', changefreq: 'weekly' },
|
{ path: '', priority: '1.0', changefreq: 'weekly' },
|
||||||
{ path: 'formation/', priority: '0.9', changefreq: 'monthly' },
|
{ path: 'formation/', priority: '0.9', changefreq: 'monthly' },
|
||||||
|
{ path: 'preview/', priority: '0.8', changefreq: 'weekly' },
|
||||||
{ path: 'mentions-legales/', priority: '0.3', changefreq: 'yearly' }
|
{ path: 'mentions-legales/', priority: '0.3', changefreq: 'yearly' }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user