// Shared UI components and SVG placeholders for Think Architecture
const { useState, useEffect, useRef } = React;
const PARTNERS = [
{ name: 'Ballay Architecte', src: 'assets/partners/ballay-architecte.png' },
{ name: 'MAF — Mutuelle des Architectes Français', src: 'assets/partners/ordre-architectes-2.png' },
{ name: 'Ordre des Architectes', src: 'assets/partners/ordre-architectes-logo.png' },
{ name: 'Groupe Qualiconsult', src: 'assets/partners/qualiconsult.png' },
{ name: 'GES Études Structures', src: 'assets/partners/ges.png' },
{ name: 'GT Structures', src: 'assets/partners/gt-structures.png' },
{ name: 'Edifis Études Structures', src: 'assets/partners/edifis.png' },
{ name: 'Evally Promotion', src: 'assets/partners/evally-promotion.png' },
{ name: 'Matthieu Husser Architectures', src: 'assets/partners/mha-husser.png' },
{ name: 'Avenir Investissement', src: 'assets/partners/avenir-investissement.png', dark: true },
{ name: 'Bois Conseil', src: 'assets/partners/bois-conseil.png' },
{ name: 'Bureau Veritas', src: 'assets/partners/bureau-veritas.png' },
{ name: 'Énergies et Fluides', src: 'assets/partners/energies-et-fluides.png' },
{ name: 'Globeco', src: 'assets/partners/globeco.png' },
{ name: 'Société Régionale de Coordination', src: 'assets/partners/src.png' },
{ name: '3D Ingénierie', src: 'assets/partners/3d-ingenierie.png' },
{ name: 'BAC', src: 'assets/partners/alpes-controles-bac.png' },
{ name: 'Sud-Est Prévention', src: 'assets/partners/sud-est-prevention.png' }];
// === ICONS ===
function ArrowNE({ size = 14 }) {
return (
);
}
function ArrowRight({ size = 14 }) {
return (
);
}
function ArrowLeft({ size = 14 }) {
return (
);
}
function PlayIcon({ size = 12 }) {
return (
);
}
// === Architectural SVG placeholder ===
// Generates a procedural architectural illustration based on a seed string.
// Each "image" is unique-looking but stays in our palette.
function ArchPlaceholder({ seed = 'a', label = '', tone = 'warm' }) {
// Deterministic pseudo-random from seed
const hash = Array.from(seed).reduce((a, c) => a + c.charCodeAt(0), 0);
const rand = (i) => {
const x = Math.sin(hash + i * 9301 + 49297) * 233280;
return x - Math.floor(x);
};
// Palette options
const palettes = {
warm: { sky: '#d4c5b8', ground: '#3c240f', mid: '#7a4a2b', accent: '#da734a', light: '#e4dcd5' },
cool: { sky: '#b8b8b8', ground: '#2a2a2a', mid: '#5a5a5a', accent: '#da734a', light: '#d8d8d8' },
forest: { sky: '#c4ccc0', ground: '#2a3528', mid: '#4a5840', accent: '#da734a', light: '#dde0d4' },
snow: { sky: '#dde2e6', ground: '#a0a8b0', mid: '#c4ccd2', accent: '#da734a', light: '#eef0f2' },
dusk: { sky: '#c4a890', ground: '#3a2418', mid: '#7a4a2b', accent: '#da734a', light: '#dccebd' }
};
const p = palettes[tone] || palettes.warm;
// Pick a composition type from seed
const compositionType = Math.floor(rand(1) * 5);
return (
);
}
// === Floor plan placeholder (used as decorative bg) ===
function FloorPlanBg({ opacity = 0.15 }) {
return (
);
}
// === Reveal-on-scroll wrapper ===
function Reveal({ children, delay = 0, as: As = 'div', ...rest }) {
const ref = useRef(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
const io = new IntersectionObserver((entries) => {
entries.forEach((e) => {
if (e.isIntersecting) {
setTimeout(() => e.target.classList.add('in'), delay);
io.unobserve(e.target);
}
});
}, { threshold: 0.12 });
io.observe(el);
return () => io.disconnect();
}, [delay]);
return
avec intention.