diff --git a/about/our-team.mdx b/about/our-team.mdx new file mode 100644 index 000000000..5015d0720 --- /dev/null +++ b/about/our-team.mdx @@ -0,0 +1,9 @@ +--- +id: our-team +title: Our Team +hide_table_of_contents: true +--- + +import TeamDirectory from '../src/components/TeamDirectory'; + + diff --git a/docusaurus.config.js b/docusaurus.config.js index b5054cb47..1d4017eeb 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -120,6 +120,11 @@ module.exports = { href:'/about/stack' }, + { + label: "Our Team", + href:'/about/our-team' + }, + ], }, diff --git a/sidebars.js b/sidebars.js index 9b2aa8da4..abf55b0d7 100644 --- a/sidebars.js +++ b/sidebars.js @@ -4,6 +4,7 @@ module.exports = { 'ekilab', 'ekimetrics', 'stack', + 'our-team', ], }, diff --git a/src/components/TeamDirectory/data.json b/src/components/TeamDirectory/data.json new file mode 100644 index 000000000..ad461b6f2 --- /dev/null +++ b/src/components/TeamDirectory/data.json @@ -0,0 +1,109 @@ +{ + "people": [ + { + "id": "duong_nguyen", + "name": "Duong Nguyen", + "role": "Senior AI Research Lead", + "email": "duong.nguyen@ekimetrics.com", + "tags": ["Time Series", "Forecasting", "Tabular Foundation Models"] + }, + { + "id": "nicolas_chesneau", + "name": "Nicolas Chesneau", + "role": "Director of Research", + "email": "nicolas.chesneau@ekimetrics.com", + "tags": ["Computer Vision", "NLP", "GenAI"] + }, + { + "id": "jean_lelong", + "name": "Jean Lelong", + "role": "Senior Ai Researcher", + "email": "jean.lelong@ekimetrics.com", + "tags": ["NLP", "GenAI"] + }, + { + "id": "yedidia_agnimo", + "name": "Yedidia Agnimo", + "role": "Doctoral Researcher", + "email": "yedidia.agnimo@ekimetrics.com", + "tags": ["NLP", "GenAI", "LLM Hallucinations"] + }, + { + "id": "yann_choho", + "name": "Yann Choho", + "role": "Doctoral Researcher", + "email": "yann.choho@ekimetrics.com", + "tags": ["NLP"] + }, + { + "id": "francois_guerillon", + "name": "François Guerillon", + "role": "Senior AI Researcher", + "email": "francois.guerillon@ekimetrics.com", + "tags": ["Deep Learning", "Model Fine-tuning", "Computer Vision", "Multimodality"] + }, + { + "id": "andrew_mcdonald", + "name": "Andrew McDonald", + "role": "Doctoral Researcher", + "email": "andrew.mcdonald@ekimetrics.com", + "tags": ["Tabular Foundation Models", "Forecasting"] + }, + { + "id": "sami_achour", + "name": "Sami Achour", + "role": "Senior AI Scientist", + "email": "sami.achour@ekimetrics.com", + "tags": ["Time Series", "Forecasting"] + }, + { + "id": "audrey_poinsot", + "name": "Audrey Poinsot", + "role": "Senior AI Researcher", + "email": "audrey.poinsot@ekimetrics.com", + "tags": ["Causal Inference"] + }, + { + "id": "milan_bhan", + "name": "Milan Bhan", + "role": "Senior AI Researcher", + "email": "milan.bhan@ekimetrics.com", + "tags": ["Interpretability", "NLP"] + }, + { + "id": "victoria_danon", + "name": "Victoria Danon", + "role": "Senior Data Scientist", + "email": "victoria.danon@ekimetrics.com", + "tags": ["NLP", "GenAI"] + }, + { + "id": "pierre_moreau", + "name": "Pierre Moreau", + "role": "Senior Data Scientist", + "email": "pierre.moreau@ekimetrics.com", + "tags": ["Interpretability"] + }, + { + "id": "benjamin_wong", + "name": "Benjamin Wong", + "role": "Senior Data Scientist", + "email": "benjamin.wong@ekimetrics.com", + "tags": [] + }, + { + "id": "annabelle_blangero", + "name": "Annabelle Blangero", + "role": "Senior AI Research Lead", + "email": "annabelle.blangero@ekimetrics.com", + "tags": ["NLP", "GenAI", "AI Governance"] + }, + { + "id": "toni_fortora", + "name": "Toni Fortora", + "role": "Senior Data Scientist", + "email": "toni.fortora@ekimetrics.com", + "tags": ["Causal Inference"] + } + ] +} diff --git a/src/components/TeamDirectory/index.js b/src/components/TeamDirectory/index.js new file mode 100644 index 000000000..d8fbb1f57 --- /dev/null +++ b/src/components/TeamDirectory/index.js @@ -0,0 +1,118 @@ +import React, { useState, useMemo } from 'react'; +import clsx from 'clsx'; +import styles from './styles.module.css'; +import data from './data.json'; + +const AVATAR_COLORS = [ + '#1a6b8a', '#2d7a4f', '#7a3d8a', '#8a5c1a', + '#1a4a8a', '#8a1a3d', '#3d8a1a', '#8a6b1a', +]; + +function avatarColor(name) { + let h = 0; + for (let i = 0; i < name.length; i++) h = (h * 31 + name.charCodeAt(i)) >>> 0; + return AVATAR_COLORS[h % AVATAR_COLORS.length]; +} + +function initials(name) { + return name + .split(' ') + .filter(Boolean) + .slice(0, 2) + .map((w) => w[0].toUpperCase()) + .join(''); +} + +function EmailIcon() { + return ( + + ); +} + +function collectTags(people) { + const set = new Set(); + for (const p of people) for (const t of p.tags) set.add(t); + return [...set].sort(); +} + +function PersonCard({ person, activeTags, onToggleTag }) { + return ( +
+
+ {initials(person.name)} +
+
+

{person.name}

+

{person.role}

+
+ {person.tags.map((tag) => ( + onToggleTag(tag)} + > + {tag} + + ))} +
+
+ + + +
+ ); +} + +export default function TeamDirectory() { + const [activeTags, setActiveTags] = useState(new Set()); + const allTags = useMemo(() => collectTags(data.people), []); + + const toggleTag = (tag) => { + setActiveTags((prev) => { + const next = new Set(prev); + if (next.has(tag)) next.delete(tag); + else next.add(tag); + return next; + }); + }; + + const visiblePeople = useMemo(() => { + if (activeTags.size === 0) return data.people; + return data.people.filter((p) => [...activeTags].every((t) => p.tags.includes(t))); + }, [activeTags]); + + return ( +
+
+

Our Team

+

Meet the people behind Eki.Lab

+
+ +
+ Filter: + {allTags.map((tag) => ( + toggleTag(tag)} + > + {tag} + + ))} +
+ +
+ {visiblePeople.length === 0 ? ( +
No team members match the selected filters.
+ ) : ( + visiblePeople.map((person) => ( + + )) + )} +
+
+ ); +} diff --git a/src/components/TeamDirectory/styles.module.css b/src/components/TeamDirectory/styles.module.css new file mode 100644 index 000000000..add753ed8 --- /dev/null +++ b/src/components/TeamDirectory/styles.module.css @@ -0,0 +1,191 @@ +/* ── Our Team Directory ─────────────────────────────────────────── */ + +.teamPage { + padding: 60px 0 80px; +} + +.teamHero { + text-align: center; + margin-bottom: 40px; +} + +.teamHero h1 { + font-size: 2rem; + font-weight: 700; + color: #fff; + margin-bottom: 8px; +} + +.teamHero p { + color: #8d949e; + font-size: 1rem; +} + +/* ── Filter bar ─────────────────────────────────────────────────── */ + +.tagFilterBar { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 32px; + align-items: center; +} + +.tagFilterBarLabel { + font-size: 0.8rem; + font-weight: 600; + color: #8d949e; + text-transform: uppercase; + letter-spacing: 0.05em; + margin-right: 4px; +} + +.filterTag { + display: inline-block; + background: #1e3347; + color: #c8d0da; + border: 1px solid #2e4a63; + border-radius: 4px; + padding: 4px 12px; + font-size: 0.78rem; + cursor: pointer; + transition: background 0.15s, color 0.15s, border-color 0.15s; + user-select: none; +} + +.filterTag:hover { + background: #2e4a63; + color: #fff; +} + +.filterTagActive { + background: #3578e5; + border-color: #3578e5; + color: #fff; +} + +/* ── Grid ───────────────────────────────────────────────────────── */ + +.teamGrid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 24px; +} + +@media (max-width: 600px) { + .teamGrid { + grid-template-columns: 1fr; + } +} + +/* ── Person card ────────────────────────────────────────────────── */ + +.personCard { + position: relative; + background: rgba(22, 41, 60, 0.7); + border: 1px solid #2e4a63; + border-radius: 10px; + padding: 24px; + display: flex; + flex-direction: row; + gap: 20px; + align-items: flex-start; + transition: transform 0.15s, box-shadow 0.15s; +} + +.personCard:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35); +} + +/* ── Avatar ─────────────────────────────────────────────────────── */ + +.personAvatar { + flex-shrink: 0; + width: 64px; + height: 64px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.3rem; + font-weight: 700; + color: #fff; + letter-spacing: 0.02em; +} + +/* ── Card body ──────────────────────────────────────────────────── */ + +.personBody { + flex: 1; + min-width: 0; +} + +.personName { + font-size: 1rem; + font-weight: 600; + color: #fff; + margin: 0 0 4px; + padding-right: 28px; /* leave room for email icon */ +} + +.personRole { + font-size: 0.85rem; + color: #8d949e; + margin: 0 0 14px; +} + +/* ── Tags ───────────────────────────────────────────────────────── */ + +.personTags { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.personTag { + display: inline-block; + background: #32475a; + color: #c8d0da; + border-radius: 4px; + padding: 2px 8px; + font-size: 0.75rem; + cursor: pointer; + transition: background 0.15s, color 0.15s; + user-select: none; +} + +.personTag:hover { + background: #3578e5; + color: #fff; +} + +.personTagActive { + background: #3578e5; + color: #fff; +} + +/* ── Email icon ─────────────────────────────────────────────────── */ + +.personEmail { + position: absolute; + top: 20px; + right: 20px; + color: #8d949e; + transition: color 0.15s; + line-height: 0; +} + +.personEmail:hover { + color: #3578e5; +} + +/* ── Empty state ────────────────────────────────────────────────── */ + +.teamEmpty { + grid-column: 1 / -1; + text-align: center; + color: #8d949e; + padding: 48px 0; + font-size: 0.95rem; +}