feat: add new project and single page layouts

- Created a new layout for projects with sections for hero, projects, roadmap, selection criteria, and a call to action.
- Added a single page layout for individual project details, including a hero section and content area.
- Introduced a brand highlight partial to emphasize "TheAfriForge" in text.
- Implemented an icon partial for reusable SVG icons across the site.
- Developed a site header and footer with navigation and legal links, adapting for language variations.
- Enhanced the index layout with sections for hero, stats, pillars, projects, and members.
- Added a development script to load environment variables and run Hugo with optional arguments.
This commit is contained in:
Cheick Oumar 2026-05-17 17:11:54 +02:00
commit 3dbacc94d3
35 changed files with 3046 additions and 0 deletions

9
.dockerignore Normal file
View file

@ -0,0 +1,9 @@
.git
.gitignore
.env
.env.*
!.env.example
.hugo_build.lock
public
resources/_gen
node_modules

12
.env.example Normal file
View file

@ -0,0 +1,12 @@
# Membership form endpoint (provider URL)
HUGO_MEMBERSHIP_FORM_ACTION=https://your-form-provider-endpoint
# Formbricks SDK (recommended for this project)
HUGO_FORMBRICKS_API_HOST=https://form.theafriforge.org
HUGO_FORMBRICKS_ENV_ID=cmp9q6jb6000anz01waq5fprk
HUGO_FORMBRICKS_EVENT=membership_form_submitted
HUGO_FORMBRICKS_SDK_URL=https://form.theafriforge.org/js/formbricks.umd.cjs
# Optional success redirect path (language-aware in templates)
# Examples: /devenir-membre or /become-member
HUGO_MEMBERSHIP_FORM_SUCCESS_PATH=/become-member

20
.gitignore vendored Normal file
View file

@ -0,0 +1,20 @@
# Hugo build output and caches
public/
resources/
.hugo_build.lock
# Node dependencies and logs
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Environment files
.env
# OS and editor files
.DS_Store
Thumbs.db
.idea/
.vscode/

28
Dockerfile Normal file
View file

@ -0,0 +1,28 @@
FROM klakegg/hugo:0.128.0-ext-alpine AS builder
WORKDIR /src
COPY . .
# Build-time env vars used by Hugo templates.
ARG HUGO_MEMBERSHIP_FORM_ACTION=""
ARG HUGO_MEMBERSHIP_FORM_SUCCESS_PATH=""
ARG HUGO_FORMBRICKS_API_HOST=""
ARG HUGO_FORMBRICKS_ENV_ID=""
ARG HUGO_FORMBRICKS_EVENT=""
ARG HUGO_FORMBRICKS_SDK_URL=""
ENV HUGO_MEMBERSHIP_FORM_ACTION=$HUGO_MEMBERSHIP_FORM_ACTION
ENV HUGO_MEMBERSHIP_FORM_SUCCESS_PATH=$HUGO_MEMBERSHIP_FORM_SUCCESS_PATH
ENV HUGO_FORMBRICKS_API_HOST=$HUGO_FORMBRICKS_API_HOST
ENV HUGO_FORMBRICKS_ENV_ID=$HUGO_FORMBRICKS_ENV_ID
ENV HUGO_FORMBRICKS_EVENT=$HUGO_FORMBRICKS_EVENT
ENV HUGO_FORMBRICKS_SDK_URL=$HUGO_FORMBRICKS_SDK_URL
RUN hugo --config ./hugo.toml --environment production --minify
FROM nginx:1.27-alpine
COPY --from=builder /src/public /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

187
README.md Normal file
View file

@ -0,0 +1,187 @@
# TheAfriForge - Guide Local et Production
Ce projet est un site statique Hugo multilingue :
- Francais par defaut sur /
- Anglais sur /en/
## 1) Prerequis
Installe les outils suivants :
- Hugo Extended (recommande: version recente)
- Git
Verification rapide :
```bash
hugo version
git --version
```
## 2) Lancer le site en local
Depuis le dossier du projet :
```bash
cd /home/choudi/Bureau/Projets/Open-Ivoire/Clients/TheAfricaForge
hugo server --config ./hugo.toml -D
```
Ensuite ouvre :
- http://localhost:1313/
- http://localhost:1313/en/
Notes :
- L'option -D permet d'afficher aussi les contenus draft.
- Si tu ne veux pas les drafts, retire -D.
## 3) Erreur frequente: hugo server exit code 255
Si le serveur ne demarre pas et retourne 255, verifie :
1. Un processus Hugo deja lance
```bash
pkill -f "hugo server" || true
```
2. Presence du lock file dans le dossier projet
```bash
rm -f .hugo_build.lock
```
3. Relance ensuite le serveur
```bash
hugo server --config ./hugo.toml -D
```
## 4) Build de production
Genere les fichiers statiques optimises dans public/ :
```bash
hugo --config ./hugo.toml --environment production --minify
```
Le dossier de sortie a deployer est :
- public/
## 5) Tester la build de production en local
Option A (Python, simple) :
```bash
python3 -m http.server 8080 --directory public
```
Option B (Node) :
```bash
npx serve public
```
Puis ouvre :
- http://localhost:8080/ (Python)
- URL affichee par serve (Node)
## 6) Deploiement en production
### Option 1 - VPS + Nginx (recommande pour controle complet)
1. Build local :
```bash
hugo --config ./hugo.toml --environment production --minify
```
2. Copier public/ vers le serveur :
```bash
rsync -avz --delete public/ user@ton-serveur:/var/www/theafriforge/
```
3. Config Nginx minimale :
```nginx
server {
listen 80;
server_name theafriforge.org www.theafriforge.org;
root /var/www/theafriforge;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
```
4. Activer HTTPS via Certbot (recommande).
### Option 2 - Netlify (ultra rapide)
Build command :
```bash
hugo --config ./hugo.toml --environment production --minify
```
Publish directory :
- public
Important :
- Selectionne Hugo Extended dans les settings de build si necessaire.
### Option 3 - Cloudflare Pages
Build command :
```bash
hugo --config ./hugo.toml --environment production --minify
```
Build output directory :
- public
### Option 4 - Vercel (site statique)
Framework preset :
- Hugo
Build command :
```bash
hugo --config ./hugo.toml --environment production --minify
```
Output directory :
- public
## 7) Workflow recommande
1. Developpement local avec hugo server
2. Verification FR/EN sur / et /en/
3. Build production avec minify
4. Test rapide du dossier public/
5. Deploiement sur hebergeur cible
## 8) Commandes utiles
Build standard :
```bash
hugo --config ./hugo.toml --minify
```
Nettoyage cache Hugo (si comportement etrange) :
```bash
rm -rf resources/_gen
```
Forcer un autre port local :
```bash
hugo server --config ./hugo.toml -D --port 1314
```

71
content/en/_index.md Normal file
View file

@ -0,0 +1,71 @@
---
title: "Home"
hero:
kicker: "African digital infrastructure"
titleLine1: "Free and sovereign"
titleLine2: "infrastructure"
titleLine3: "for Africa."
subtitle: "TheAfriForge aligns public institutions, academia and industry to secure local control over critical digital systems."
ctaManifesto: "Read the Manifesto"
ctaProjects: "See projects"
whyJoin:
title: "Why join us"
intro: "Access opportunities around African digital sovereignty with committed partners and world-class infrastructure."
cards:
- title: "Sovereignty"
icon: "🔐"
text: "Full control over your data, infrastructure and critical systems. No external dependency."
- title: "Resilience"
icon: "💪"
text: "Decentralized, interoperable systems designed to withstand shocks. Robust African infrastructure."
- title: "Growth"
icon: "📈"
text: "Access to a growing African market. Ongoing business and innovation opportunities."
- title: "Community"
icon: "👥"
text: "A continental network of innovators, experts and public stakeholders committed to digital transformation."
pillars:
title: "Three founding pillars"
items:
- title: "Autonomy"
text: "Strengthen Africa's capacity to build, maintain and govern essential software components."
- title: "Infrastructure"
text: "Connect public and private platforms into resilient, interoperable and trusted services."
- title: "Neutrality"
text: "Promote open, transparent and public-interest governance across all shared systems."
projects:
title: "Strategic projects"
intro: "Initiatives in production, incubation and research for African digital sovereignty."
tag: "Open Source Programs"
items:
- name: "GitForge.africa"
status: "Production"
text: "Sovereign code forge hosting strategic African projects in production."
cta: "Access"
- name: "AfriRegistry"
status: "Incubation"
text: "Federated registry of open standards, services and software artifacts currently in incubation."
cta: "View"
- name: "AIForge"
status: "Research"
text: "Research initiative for an open and responsible AI platform tailored to African realities."
cta: "Learn more"
members:
title: "Join the movement"
subtitle: "Join African stakeholders committed to digital sovereignty. Together, we are building tomorrow's infrastructure."
categories:
- label: "Universities & Schools"
title: "Contribute to digital commons"
text: "Join technical groups, research labs and certification-driven training tracks."
cta: "Learn more"
link: "become-member"
- label: "Datacenters & Operators"
title: "Federate continental infrastructure"
text: "Co-design hosting, security and interconnection standards for critical services."
cta: "Learn more"
link: "become-member"
---

143
content/en/become-member.md Normal file
View file

@ -0,0 +1,143 @@
---
title: "Become a member"
description: "Join TheAfriForge to contribute to African digital sovereignty through concrete programmes and open governance."
layout: "join"
value:
title: "Why become a member"
intro: "Joining TheAfriForge means contributing to critical infrastructure, open source commons, and governance standards for the continent."
items:
- icon: "network"
title: "Continental impact"
text: "Contribute to strategic digital services reusable across countries and institutions."
- icon: "shield"
title: "Security and resilience"
text: "Help build a stronger and safer software supply chain."
- icon: "academic"
title: "Capability building"
text: "Access a collaborative ecosystem for training, documentation, and technical exchange."
membership:
title: "Member profiles"
intro: "We welcome technical, institutional, and operational contributions."
items:
- title: "Technical contributor"
text: "Engineering teams, security experts, and open source maintainers."
expectations:
- "Contribute to priority software components"
- "Join technical and security reviews"
- "Share quality and documentation practices"
- title: "Infrastructure partner"
text: "Datacenters, operators, hosting providers, and regional cloud actors."
expectations:
- "Co-design sovereign hosting standards"
- "Provide technical or federation capacity"
- "Contribute to availability and recovery objectives"
- title: "Institutional partner"
text: "Universities, public agencies, companies, and impact actors."
expectations:
- "Support platform adoption"
- "Contribute to governance orientation"
- "Fund or support strategic programmes"
process:
title: "Membership process"
steps:
- title: "Submission"
text: "You submit the form with your profile, objectives, and contribution proposal."
delay: "Immediate"
- title: "Qualification"
text: "Our team reviews your request and checks alignment with TheAfriForge priorities."
delay: "Within 48h"
- title: "Scoping call"
text: "A 30-minute call clarifies expectations, deliverables, and collaboration model."
delay: "Within 7 days"
- title: "Decision and onboarding"
text: "You receive the decision, participation charter, and onboarding plan."
delay: "Within 10 days"
form:
title: "Membership application"
intro: "This form is sent to the TheAfriForge membership team."
action: "https://formsubmit.co/membership@theafriforge.org"
subject: "New membership application - TheAfriForge"
labels:
organization: "Organization (optional)"
actorType: "Actor type"
firstName: "First name"
lastName: "Last name"
profession: "Profession / role"
email: "Professional email"
phone: "Phone number"
country: "Country"
contribution: "Proposed contribution"
placeholders:
organization: "Your organization name (if applicable)"
actorType: "Select your type"
firstName: "Example: Cheick Oumar"
lastName: "Example: Diaby"
profession: "Example: Cloud engineer / Student / CTO"
email: "name@organization.org"
phone: "+225..."
country: "Select your country"
contribution: "Describe what you can contribute"
countries:
- "Algeria"
- "Angola"
- "Benin"
- "Botswana"
- "Burkina Faso"
- "Burundi"
- "Cameroon"
- "Cape Verde"
- "Comoros"
- "Congo"
- "Cote d'Ivoire"
- "Djibouti"
- "Egypt"
- "Ethiopia"
- "Gabon"
- "Gambia"
- "Ghana"
- "Guinea"
- "Kenya"
- "Liberia"
- "Madagascar"
- "Mali"
- "Morocco"
- "Mauritius"
- "Mauritania"
- "Mozambique"
- "Namibia"
- "Niger"
- "Nigeria"
- "Uganda"
- "DR Congo"
- "Rwanda"
- "Senegal"
- "Sierra Leone"
- "Somalia"
- "Sudan"
- "Tanzania"
- "Chad"
- "Togo"
- "Tunisia"
- "Zambia"
- "Zimbabwe"
- "Other"
actorTypes:
- "Individual contributor"
- "University / School"
- "Private company"
- "Public agency / Institution"
- "Datacenter / Operator"
- "Open source community"
- "Other"
consentText: "I agree that my information may be processed to evaluate my application in line with the"
privacyLinkLabel: "privacy policy"
submit: "Submit my application"
successTitle: "Application sent!"
successText: "Thank you for your interest. We will get back to you shortly."
errorText: "An error occurred. Please try again or contact us directly."
sending: "Sending…"
---

91
content/en/governance.md Normal file
View file

@ -0,0 +1,91 @@
---
title: "Governance"
description: "Open, traceable, and multi-stakeholder governance that ensures technical decisions serve Africa's long-term collective interest."
layout: "governance"
hero:
metrics:
- value: "3"
label: "Permanent bodies"
- value: "100%"
label: "Documented decisions"
- value: "2/year"
label: "Governance reviews"
principles:
title: "Governance principles"
intro: "Our governance model balances fast execution, institutional accountability, and public transparency."
items:
- icon: "balance"
title: "Neutrality"
text: "No single actor can capture the foundation's strategic direction."
- icon: "lock"
title: "Accountability"
text: "Every decision is attributed, justified, and auditable."
- icon: "globe"
title: "Representation"
text: "Governance includes institutions, industry, academia, and technical communities."
- icon: "leaf"
title: "Durability"
text: "Choices prioritize service continuity and long-term viability."
bodies:
title: "Our governing bodies"
intro: "TheAfriForge relies on three complementary bodies to arbitrate, execute, and verify."
items:
- icon: "flag"
title: "Strategic council"
text: "Defines vision, validates annual priorities, and safeguards the public interest."
missions:
- "Approve the annual roadmap"
- "Arbitrate structural investments"
- "Assess sovereignty alignment"
- icon: "network"
title: "Technical committee"
text: "Leads architecture, security, and interoperability standards across platforms."
missions:
- "Validate technical standards"
- "Track service-level indicators"
- "Coordinate security reviews"
- icon: "chart"
title: "Audit and compliance unit"
text: "Controls traceability, process quality, and publication of evidence."
missions:
- "Publish quarterly indicators"
- "Verify decision compliance"
- "Trigger independent audits"
decisions:
title: "Decision cycle"
intro: "Every structural decision follows a shared cycle to ensure clarity, consultation, and execution discipline."
steps:
- title: "Qualification"
text: "The topic is formalized with goals, risks, options, and expected impact."
- title: "Consultation"
text: "Relevant stakeholders review the proposal and provide traceable feedback."
- title: "Arbitration"
text: "The competent body decides with written rationale and implementation conditions."
- title: "Publication"
text: "The decision is published with date, owners, tracking indicators, and review date."
- title: "Evaluation"
text: "An outcome review measures real impact and enables adjustment when needed."
transparency:
title: "Transparency commitments"
intro: "Trust is built through evidence that is accessible, regular, and comparable over time."
items:
- title: "Decision journal"
text: "Publication of a timestamped register for strategic and technical decisions."
- title: "Public indicators"
text: "Availability of performance, security, and reliability indicators."
- title: "Open reviews"
text: "Periodic review sessions with ecosystem members."
- title: "External audit"
text: "Regular independent assessment of governance and compliance practices."
call:
title: "Contribute to governance"
text: "Universities, institutions, companies, and technical communities can propose contributions to the charter and decision processes."
cta: "Join the governance framework"
link: "become-member"
---

View file

@ -0,0 +1,81 @@
---
title: "Legal Notice"
description: "Legal and publishing information for TheAfriForge website."
---
## Website publisher
TheAfriForge website is published by the TheAfriForge initiative, an African foundation for open-source software and digital sovereignty.
- Project name: TheAfriForge
- Purpose: development and governance of open digital commons in Africa
- Editorial contact: contact@theafriforge.org
- Legal contact: legal@theafriforge.org
## Publishing management
Publishing management is provided by the TheAfriForge coordination team.
## Administrative status
TheAfriForge operates as a non-profit public-interest initiative.
Additional administrative details (legal form, registration identifiers, registered office) are provided upon legitimate request via legal@theafriforge.org.
## Hosting
The website is hosted on technical infrastructure operated on behalf of TheAfriForge.
- Technical contact: infra@theafriforge.org
## Service availability
TheAfriForge applies reasonable measures to maintain website availability 24/7.
Temporary interruptions may occur for maintenance, security updates, technical incidents, or external causes.
## Intellectual property
Unless otherwise stated, website content (texts, visual assets, trademarks, and logos) is protected under applicable laws.
Open-source projects referenced on this website remain distributed under their respective licenses.
Any full or partial reproduction, representation, or adaptation of proprietary content without prior written authorization is prohibited.
## Terms of use
Users must use the website lawfully and fairly, without harming service security, integrity, or availability.
The following are prohibited:
- mass extraction of content without authorization
- attempts to intrude or disrupt services
- fraudulent use of forms or communication channels
## Liability
TheAfriForge uses reasonable efforts to ensure the accuracy of published information. However, errors or omissions may occur.
Use of information published on this website is under the sole responsibility of the user.
Within the limits of applicable law, TheAfriForge shall not be liable for indirect damages resulting from access to or use of the website.
## External links
The website may include links to third-party resources. TheAfriForge is not responsible for external websites content.
## Personal data
Personal data processing is described in the website Privacy Policy.
For data-related requests: privacy@theafriforge.org
## Applicable law and dispute resolution
This legal notice is interpreted under the law applicable to the operating entity and relevant international legal frameworks.
In case of dispute, an amicable resolution process is encouraged before litigation.
## Update
Last update: 17 May 2026.

111
content/en/manifesto.md Normal file
View file

@ -0,0 +1,111 @@
---
title: "Manifesto"
description: "TheAfriForge is the African foundation for free software and digital sovereignty. This manifesto sets out our vision, our principles, and our collective commitment."
layout: "manifeste"
preamble:
quote: "Africa is not behind in talent. It is behind in control."
text: "Every day, African teams build critical services for our governments, banks, hospitals, universities, and businesses. Yet much of this software heritage is still hosted, tooled, and governed outside our jurisdictions. This dependency is not neutral: it exposes our ecosystems to risks of censorship, access disruption, value extraction, and operational fragility."
conviction: "A continent that does not control its digital infrastructure controls neither its economic future, nor its security, nor its capacity for innovation. TheAfriForge exists to change that."
conviction:
title: "Our conviction"
intro: "Digital sovereignty is not a slogan. It is a concrete capability we build brick by brick."
items:
- icon: "server"
text: "Host our code and data on reliable, close, and audited infrastructure"
- icon: "shield"
text: "Govern our collaboration tools according to our values and needs"
- icon: "globe"
text: "Produce open source commons that remain in the service of the public interest"
- icon: "bolt"
text: "Ensure service continuity, even in the event of geopolitical or technical shocks"
pillars:
title: "What TheAfriForge builds"
intro: "A non-profit foundation structured around three strategic pillars."
items:
- number: "01"
icon: "network"
title: "Federated and resilient infrastructure"
text: "We interconnect local capacities — data centers, universities, technical partners — to offer robust, performant, and distributed services, without depending on a single actor outside the continent."
- number: "02"
icon: "code"
title: "Open digital commons"
text: "We host and structure strategic open source projects that can be adopted by institutions, businesses, and communities, under free licences and transparent governance."
- number: "03"
icon: "balance"
title: "Neutral and transparent governance"
text: "We operate with clear, public, and auditable rules, so that technical and institutional decisions serve the long term, not particular interests."
principles:
title: "Our non-negotiable principles"
items:
- icon: "flag"
title: "Sovereignty first"
text: "Critical assets remain under African control, in compliance with our legal frameworks."
- icon: "unlock"
title: "Openness and interoperability"
text: "No proprietary lock-in. Open standards and portability are baseline requirements."
- icon: "lock"
title: "Security by design"
text: "Encryption in transit and at rest, rigorous access management, defense in depth, and auditability."
- icon: "zap"
title: "Local performance"
text: "Reduce latency, international transit costs, and single points of failure."
- icon: "balance"
title: "Institutional neutrality"
text: "The foundation must not be captured by any single actor, public or private."
- icon: "leaf"
title: "Social responsibility"
text: "Build useful, lean, and sustainable infrastructure adapted to the realities of the continent."
- icon: "academic"
title: "Skills transmission"
text: "Train, document, and equip the next generations of engineers and maintainers."
commitments:
title: "Our measurable commitments"
intro: "TheAfriForge commits to publishing verifiable public indicators."
quote: "Sovereignty is rarely proclaimed; it is measured constantly."
items:
- "Availability and resilience of critical services"
- "Recovery time objectives in the event of an incident"
- "Number of projects hosted and actively maintained"
- "Adoption level by African institutions and businesses"
- "Training programmes and community contributions"
vision:
title: "Our ecosystem vision"
intro: "TheAfriForge is not a single platform. It is a trust framework capable of hosting an evolving portfolio of strategic projects, with a shared objective."
objective: "Increase the continent's collective autonomy in the global digital economy."
items:
- icon: "git"
title: "Collaborative software forge"
text: "GitForge.africa — Git repositories, issue tracking, CI/CD hosted on the continent"
- icon: "package"
title: "Regional registries and mirrors"
text: "AfriRegistry — local distribution of NPM, Python, Docker packages"
- icon: "shield"
title: "Software supply chain security"
text: "AIForge — security tools, compliance and sovereign code analysis"
- icon: "chart"
title: "Compliance and observability"
text: "Governance, audit, and monitoring services for institutions"
governance:
title: "Governance and method"
quote: "Trust is not decreed. It is built through proof."
items:
- "A public, collectively revisable charter"
- "Multi-stakeholder governance: academia, industry, public bodies, technical community"
- "Decisions that are traced, documented, and accountable"
- "Regular technical and organisational audits"
- "A published roadmap reviewed periodically"
coalition:
title: "A call for coalition"
text: "We call on universities, research centers, businesses, data centers, operators, governments, impact investors, and African open source communities to join TheAfriForge."
statement: "Our ambition is not to copy existing platforms. Our ambition is to build an African strategic capability — open, reliable, and durable."
closing: "This manifesto is a starting point. The work is collective. The time to act is now."
cta: "Join TheAfriForge"
---

View file

@ -0,0 +1,126 @@
---
title: "Privacy Policy"
description: "Policy for personal data processing on TheAfriForge website."
---
## Principle
TheAfriForge is committed to protecting personal data of visitors, contributors, and partners using this website.
This policy explains what data may be processed, why, for how long, and what rights you have.
## Data controller
The data controller is the TheAfriForge initiative.
- General contact: contact@theafriforge.org
- Privacy contact: privacy@theafriforge.org
- Legal contact: legal@theafriforge.org
## Data we may process
Depending on your interactions, we may process:
- contact information voluntarily submitted (name, email, organization, message)
- minimal technical browsing data (server logs, IP address, timestamp, requested page)
- information required to secure and operate the website
TheAfriForge does not intentionally collect sensitive personal data unless required by law or explicitly justified.
## Purposes
Data is processed to:
- answer contact and partnership requests
- ensure website security, availability, and integrity
- support continuous improvement of TheAfriForge services
- comply with applicable legal and regulatory obligations
## Legal basis
Processing is based on:
- your consent when you voluntarily submit information
- TheAfriForge legitimate interest in securing and operating the website
- compliance with applicable legal obligations
Where processing is based on consent, you may withdraw consent at any time without retroactive effect.
## Retention
Data is kept for a period proportionate to each purpose, then deleted or anonymized.
Indicative retention rules:
- contact requests: up to 24 months after last meaningful interaction
- technical logs: up to 12 months unless security or legal constraints require more
- compliance traces: according to applicable legal obligations
## Recipients
Data is accessible only to authorized TheAfriForge teams and technical service providers strictly necessary to operate the website.
No commercial sale of personal data is performed.
## Processors and transfers
TheAfriForge may rely on technical processors (hosting, monitoring, anti-abuse, messaging).
If data is transferred outside your legal region, TheAfriForge implements appropriate safeguards (contractual clauses, additional technical measures, data minimization).
## Cookies
The website mainly uses technical cookies required for proper operation.
If audience measurement cookies are enabled, they are used proportionately and in line with applicable requirements.
You may configure your browser to block cookies. Blocking strictly necessary cookies may degrade website functionality.
## Your rights
You may request:
- access to your data
- correction of your data
- deletion of your data, where applicable
- restriction or objection to certain processing activities
- data portability, where applicable
- post-mortem data handling instructions, where provided by applicable law
To exercise your rights: privacy@theafriforge.org
We may request reasonable proof of identity to prevent fraudulent requests.
## Response timelines
TheAfriForge handles requests within a reasonable timeframe and in line with applicable legal requirements.
In complex situations, timelines may be extended with prior notice.
## Security
TheAfriForge implements reasonable technical and organizational measures to protect data against unauthorized access, alteration, or loss.
Measures include access control, technical logging, privilege limitation, security monitoring, and recovery procedures.
## Incident notification
In case of a personal data breach likely to create significant risk, TheAfriForge follows notification duties required by applicable law.
## Minors
The website is not intended to intentionally collect data from minors without appropriate legal safeguards.
If you believe a minor has provided data inappropriately, contact privacy@theafriforge.org.
## Contact
For any privacy-related question: privacy@theafriforge.org
For legal requests: legal@theafriforge.org
## Complaint
If you believe your rights are not respected, you may first contact TheAfriForge for amicable resolution, then refer to the competent data protection authority in your jurisdiction.
Last update: 17 May 2026.

83
content/en/projects.md Normal file
View file

@ -0,0 +1,83 @@
---
title: "Our Projects"
description: "A portfolio of strategic open source programmes designed to build practical, measurable, and durable African digital sovereignty."
layout: "projects"
hero:
metrics:
- value: "03"
label: "Active programmes"
- value: "12"
label: "Target technical partners"
- value: "24 months"
label: "Deployment horizon"
portfolio:
tag: "Portfolio"
title: "Initiatives across production, incubation, and research"
intro: "Each programme addresses a critical need: sovereign collaboration, reliable dependency distribution, and software supply chain security for Africa."
impactLabel: "Expected impact"
stackLabel: "Technical base"
items:
- name: "GitForge.africa"
status: "Production"
icon: "git"
text: "Sovereign code forge to host repositories, issue tracking, CI/CD pipelines, and code reviews on continental infrastructure."
impact: "Lower service disruption risk from external platform dependencies for critical projects."
stack: "Git, CI/CD, IAM, observability"
cta: "View roadmap"
link: "#"
- name: "AfriRegistry"
status: "Incubation"
icon: "package"
text: "Regional registry to distribute npm, PyPI, and container images with local mirrors and trust policies."
impact: "Reduced latency, higher availability, and stronger control over software artifacts."
stack: "Registry federation, cache, signatures, policy engine"
cta: "Track incubation"
link: "#"
- name: "AIForge Secure"
status: "Research"
icon: "shield"
text: "R&D programme for code analysis, compliance, and software supply chain hardening in sovereign environments."
impact: "Faster audits and stronger trust for public institutions and private operators."
stack: "SBOM, SAST, provenance, dashboards"
cta: "Explore research"
link: "#"
roadmap:
title: "Roadmap 2026-2027"
intro: "We execute in waves: core hardening, regional federation, then service industrialisation."
items:
- period: "Q3 2026"
title: "Core stack hardening"
text: "Strengthen architecture, backups, and incident recovery procedures."
- period: "Q4 2026"
title: "Multi-site federation"
text: "Connect regional nodes and align shared governance standards."
- period: "Q1 2027"
title: "Partner expansion"
text: "Scale access for institutions, universities, and businesses through a unified onboarding framework."
selection:
title: "How we select projects"
intro: "TheAfriForge supports initiatives that serve the public interest and strengthen African digital autonomy."
items:
- icon: "flag"
title: "Sovereignty value"
text: "The project must reduce a critical structural dependency."
- icon: "network"
title: "Ecosystem impact"
text: "The project must be reusable by multiple actors across the continent."
- icon: "lock"
title: "Security maturity"
text: "The project must integrate security and auditability by design."
- icon: "academic"
title: "Knowledge transfer"
text: "The project must enable local skills development and open documentation."
call:
title: "Do you lead a strategic project?"
text: "Share your initiative with TheAfriForge to assess support in hosting, governance, and technical acceleration."
cta: "Submit a project"
link: "become-member"
---

71
content/fr/_index.md Normal file
View file

@ -0,0 +1,71 @@
---
title: "Accueil"
hero:
kicker: "Infrastructure numérique africaine"
titleLine1: "L'infrastructure libre"
titleLine2: "et souveraine"
titleLine3: "de l'Afrique."
subtitle: "TheAfriForge n'est pas une offre de plus. C'est un projet continental pour maîtriser nos technologies critiques, héberger nos communs numériques chez nous et décider, en Afrique, de notre destin numérique."
ctaManifesto: "Lire le Manifeste"
ctaProjects: "Voir les projets"
whyJoin:
title: "Pourquoi nous rejoindre"
intro: "Rejoindre TheAfriForge, c'est prendre part à une stratégie de souveraineté: bâtir des capacités locales durables et réduire notre exposition aux dépendances technologiques extérieures."
cards:
- title: "Souveraineté"
icon: "🔐"
text: "Gouvernance africaine des données, des plateformes et des services essentiels, pour faire de l'autonomie une réalité opérationnelle."
- title: "Résilience"
icon: "💪"
text: "Des architectures interopérables et distribuées, conçues pour encaisser les chocs géopolitiques, économiques et techniques."
- title: "Croissance"
icon: "📈"
text: "Création de valeur locale, emplois qualifiés et montée en compétence des écosystèmes africains sur des fondations maîtrisées."
- title: "Communauté"
icon: "👥"
text: "Une alliance continentale d'innovateurs, d'institutions et d'opérateurs mobilisés autour d'un cap commun de souveraineté."
pillars:
title: "Trois piliers fondateurs"
items:
- title: "Autonomie"
text: "Renforcer la capacité africaine à produire, maintenir et gouverner ses briques logicielles essentielles."
- title: "Infrastructure"
text: "Connecter les plateformes publiques et privées en services résilients, interopérables et de confiance."
- title: "Neutralité"
text: "Promouvoir une gouvernance ouverte, transparente et d'intérêt public pour tous les systèmes partagés."
projects:
title: "Projets stratégiques"
intro: "Des briques concrètes en production, incubation et recherche pour construire une indépendance technologique durable à l'échelle du continent."
tag: "Programmes Open Source"
items:
- name: "GitForge.africa"
status: "Production"
text: "Forge de code souveraine hébergeant les projets stratégiques africains en production."
cta: "Accéder"
- name: "AfriRegistry"
status: "Incubation"
text: "Registre fédéré des standards ouverts, services et artefacts logiciels du continent."
cta: "Voir"
- name: "AIForge"
status: "Recherche"
text: "Initiative de recherche pour une plateforme IA ouverte et responsable, adaptée aux réalités africaines."
cta: "En savoir +"
members:
title: "Rejoindre le mouvement"
subtitle: "Universités, entreprises, États et opérateurs: unissons nos capacités pour faire émerger une puissance numérique africaine souveraine et pérenne."
categories:
- label: "Universités & Écoles"
title: "Contribuer aux communs numériques"
text: "Participez aux groupes techniques, aux laboratoires de recherche et aux parcours de formation certifiants."
cta: "En savoir plus"
link: "devenir-membre"
- label: "Datacenters & Opérateurs"
title: "Fédérer l'infrastructure continentale"
text: "Co-concevez les standards d'hébergement, sécurité et interconnexion pour les services critiques."
cta: "En savoir plus"
link: "devenir-membre"
---

View file

@ -0,0 +1,143 @@
---
title: "Devenir membre"
description: "Rejoignez TheAfriForge pour contribuer a la souverainete numerique africaine a travers des projets concrets et une gouvernance ouverte."
layout: "join"
value:
title: "Pourquoi devenir membre"
intro: "Rejoindre TheAfriForge, c'est agir sur des infrastructures critiques, des communs open source et des standards de gouvernance pour le continent."
items:
- icon: "network"
title: "Impact continental"
text: "Contribuez a des services numeriques strategiques reutilisables par plusieurs pays et institutions."
- icon: "shield"
title: "Securite et resilience"
text: "Participez a la construction d'une chaine logicielle plus sure et plus robuste."
- icon: "academic"
title: "Montee en competences"
text: "Accedez a un ecosysteme de formation, de documentation et de collaboration technique."
membership:
title: "Profils de membres"
intro: "Nous accueillons des contributions techniques, institutionnelles et operationnelles."
items:
- title: "Contributeur technique"
text: "Equipes engineering, experts securite, mainteneurs open source."
expectations:
- "Contribuer aux briques logicielles prioritaires"
- "Participer aux revues techniques et securite"
- "Partager des pratiques de qualite et de documentation"
- title: "Partenaire infrastructure"
text: "Datacenters, operateurs, hebergeurs et fournisseurs cloud regionaux."
expectations:
- "Co-construire les standards d'hebergement souverain"
- "Fournir des capacites techniques ou de federation"
- "Contribuer aux objectifs de disponibilite et de reprise"
- title: "Partenaire institutionnel"
text: "Universites, administrations, entreprises, acteurs de l'impact."
expectations:
- "Soutenir l'adoption des plateformes"
- "Contribuer aux orientations de gouvernance"
- "Financer ou accompagner des programmes strategiques"
process:
title: "Processus d'adhesion"
steps:
- title: "Soumission"
text: "Vous completez le formulaire avec votre profil, vos objectifs et votre proposition de contribution."
delay: "Immediate"
- title: "Qualification"
text: "Notre equipe examine votre dossier et verifie l'alignement avec les priorites TheAfriForge."
delay: "Sous 48h"
- title: "Entretien de cadrage"
text: "Un appel de 30 minutes permet de clarifier les attentes, livrables et modalites de collaboration."
delay: "Sous 7 jours"
- title: "Decision et onboarding"
text: "Vous recevez la decision, la charte de participation et le plan d'integration."
delay: "Sous 10 jours"
form:
title: "Demande d'adhesion"
intro: "Ce formulaire est envoye a l'equipe membership TheAfriForge."
action: "https://formsubmit.co/membership@theafriforge.org"
subject: "Nouvelle demande d'adhesion - TheAfriForge"
labels:
organization: "Organisation (optionnel)"
actorType: "Type d'acteur"
firstName: "Prénom"
lastName: "Nom"
profession: "Profession / fonction"
email: "Email professionnel"
phone: "Numero de telephone"
country: "Pays"
contribution: "Contribution proposee"
placeholders:
organization: "Nom de votre organisation (si applicable)"
actorType: "Selectionnez votre type"
firstName: "Ex: Cheick Oumar"
lastName: "Ex: Diaby"
profession: "Ex: Ingenieure cloud / Etudiant / CTO"
email: "nom@organisation.org"
phone: "+225..."
country: "Selectionnez votre pays"
contribution: "Decrivez ce que vous apportez concretement"
countries:
- "Algerie"
- "Angola"
- "Benin"
- "Botswana"
- "Burkina Faso"
- "Burundi"
- "Cameroun"
- "Cap-Vert"
- "Comores"
- "Congo"
- "Cote d'Ivoire"
- "Djibouti"
- "Egypte"
- "Ethiopie"
- "Gabon"
- "Gambie"
- "Ghana"
- "Guinee"
- "Kenya"
- "Liberia"
- "Madagascar"
- "Mali"
- "Maroc"
- "Maurice"
- "Mauritanie"
- "Mozambique"
- "Namibie"
- "Niger"
- "Nigeria"
- "Ouganda"
- "RDC"
- "Rwanda"
- "Senegal"
- "Sierra Leone"
- "Somalie"
- "Soudan"
- "Tanzanie"
- "Tchad"
- "Togo"
- "Tunisie"
- "Zambie"
- "Zimbabwe"
- "Autre"
actorTypes:
- "Contributeur individuel"
- "Universite / Ecole"
- "Entreprise privee"
- "Administration / Institution publique"
- "Datacenter / Operateur"
- "Communaute open source"
- "Autre"
consentText: "J'accepte que mes informations soient traitees pour l'evaluation de ma demande conformement a la"
privacyLinkLabel: "politique de confidentialite"
submit: "Envoyer ma candidature"
successTitle: "Candidature envoyée !"
successText: "Merci pour votre intérêt. Nous reviendrons vers vous prochainement."
errorText: "Une erreur est survenue. Veuillez réessayer ou nous contacter directement."
sending: "Envoi en cours…"
---

91
content/fr/gouvernance.md Normal file
View file

@ -0,0 +1,91 @@
---
title: "Gouvernance"
description: "Une gouvernance ouverte, tracee et multi-acteurs pour garantir que les decisions techniques servent l'interet collectif africain sur le long terme."
layout: "governance"
hero:
metrics:
- value: "3"
label: "Instances permanentes"
- value: "100%"
label: "Decisions documentees"
- value: "2/an"
label: "Revues de gouvernance"
principles:
title: "Principes de gouvernance"
intro: "Notre gouvernance garantit l'equilibre entre execution rapide, responsabilite institutionnelle et transparence publique."
items:
- icon: "balance"
title: "Neutralite"
text: "Aucun acteur unique ne peut capturer la direction strategique de la fondation."
- icon: "lock"
title: "Responsabilite"
text: "Chaque decision est attribuee, justifiee et rendue auditable."
- icon: "globe"
title: "Representation"
text: "La gouvernance integre institutions, industrie, academie et communaute technique."
- icon: "leaf"
title: "Durabilite"
text: "Les choix privilegient la continuite de service et la viabilite sur le long terme."
bodies:
title: "Nos instances"
intro: "TheAfriForge s'appuie sur trois instances complementaires pour arbitrer, executer et controler."
items:
- icon: "flag"
title: "Conseil strategique"
text: "Definit la vision, valide les priorites annuelles et protege l'interet general."
missions:
- "Approuver la feuille de route annuelle"
- "Arbitrer les investissements structurants"
- "Evaluer l'alignement souverainete"
- icon: "network"
title: "Comite technique"
text: "Pilote l'architecture, la securite et les standards d'interoperabilite des plateformes."
missions:
- "Valider les standards techniques"
- "Suivre les niveaux de service"
- "Coordonner les revues de securite"
- icon: "chart"
title: "Cellule audit et conformite"
text: "Controle la tracabilite, la qualite des process et la publication des preuves."
missions:
- "Publier les indicateurs trimestriels"
- "Verifier la conformite des decisions"
- "Declencher des audits independants"
decisions:
title: "Cycle de decision"
intro: "Toute decision structurante suit un cycle commun afin de garantir clarte, consultation et execution."
steps:
- title: "Qualification"
text: "Le sujet est formalise avec objectifs, risques, options et impacts attendus."
- title: "Consultation"
text: "Les parties prenantes concernes examinent la proposition et produisent des retours traces."
- title: "Arbitrage"
text: "L'instance competente tranche avec motivation ecrite et conditions d'application."
- title: "Publication"
text: "La decision est publiee avec date, responsables, indicateurs de suivi et date de revue."
- title: "Evaluation"
text: "Un bilan mesure l'effet reel et permet un ajustement si necessaire."
transparency:
title: "Nos engagements de transparence"
intro: "La confiance se construit avec des preuves accessibles, regulieres et comparables dans le temps."
items:
- title: "Journal des decisions"
text: "Publication d'un registre horodate des decisions strategiques et techniques."
- title: "Indicateurs publics"
text: "Mise a disposition d'indicateurs de performance, securite et disponibilite."
- title: "Revues ouvertes"
text: "Organisation de points de revue periodiques avec les membres de l'ecosysteme."
- title: "Audit externe"
text: "Evaluation independante reguliere des pratiques de gouvernance et de conformite."
call:
title: "Contribuer a la gouvernance"
text: "Universites, institutions, entreprises et communautes techniques peuvent proposer des contributions a la charte et aux processus de decision."
cta: "Rejoindre le cadre de gouvernance"
link: "devenir-membre"
---

215
content/fr/manifeste.md Normal file
View file

@ -0,0 +1,215 @@
---
title: "Manifeste"
description: "TheAfriForge est la fondation africaine du logiciel libre et de la souveraineté numérique. Ce manifeste expose notre vision, nos principes et notre engagement collectif."
layout: "manifeste"
preamble:
quote: "L'Afrique n'est pas en retard de talent. Elle est en retard de contrôle."
text: "Chaque jour, des équipes africaines construisent des services critiques pour nos administrations, nos banques, nos hôpitaux, nos universités et nos entreprises. Pourtant, une grande partie de ce patrimoine logiciel est encore hébergée, outillée et gouvernée hors de nos juridictions. Cette dépendance n'est pas neutre : elle expose nos écosystèmes à des risques de censure, de rupture d'accès, de captation de valeur et de fragilité opérationnelle."
conviction: "Un continent qui ne maîtrise pas ses infrastructures numériques ne maîtrise ni son avenir économique, ni sa sécurité, ni sa capacité d'innovation. TheAfriForge existe pour changer cela."
conviction:
title: "Notre conviction"
intro: "La souveraineté numérique n'est pas un slogan. C'est une capacité concrète que nous construisons pierre par pierre."
items:
- icon: "server"
text: "Héberger nos codes et données sur des infrastructures fiables, proches et auditées"
- icon: "shield"
text: "Gouverner nos outils de collaboration selon nos valeurs et nos besoins"
- icon: "globe"
text: "Produire des communs open source qui restent au service de l'intérêt général"
- icon: "bolt"
text: "Assurer la continuité de service, même en cas de choc géopolitique ou technique"
pillars:
title: "Ce que TheAfriForge construit"
intro: "Une fondation à but non lucratif structurée autour de trois piliers stratégiques."
items:
- number: "01"
icon: "network"
title: "Infrastructure fédérée et résiliente"
text: "Nous interconnectons des capacités locales — datacenters, universités, partenaires techniques — pour offrir des services robustes, performants et distribués, sans dépendre d'un acteur unique hors du continent."
- number: "02"
icon: "code"
title: "Communs numériques ouverts"
text: "Nous hébergeons et structurons des projets open source stratégiques qui peuvent être adoptés par les institutions, les entreprises et les communautés, sous licence libre et gouvernance transparente."
- number: "03"
icon: "balance"
title: "Gouvernance neutre et transparente"
text: "Nous opérons avec des règles claires, publiques et auditables, afin que les décisions techniques et institutionnelles servent le long terme, pas des intérêts particuliers."
principles:
title: "Nos principes non négociables"
items:
- icon: "flag"
title: "Souveraineté d'abord"
text: "Les actifs critiques restent sous contrôle africain, dans le respect de nos cadres juridiques."
- icon: "unlock"
title: "Ouverture et interopérabilité"
text: "Aucun verrou propriétaire. Les standards ouverts et la portabilité sont des exigences de base."
- icon: "lock"
title: "Sécurité par conception"
text: "Chiffrement en transit et au repos, gestion rigoureuse des accès, défense en profondeur et auditabilité."
- icon: "zap"
title: "Performance locale"
text: "Réduire la latence, les coûts de transit international et les points uniques de défaillance."
- icon: "balance"
title: "Neutralité institutionnelle"
text: "La fondation ne doit pas être capturée par un acteur unique, public ou privé."
- icon: "leaf"
title: "Responsabilité sociale"
text: "Construire une infrastructure utile, sobre et durable, adaptée aux réalités du continent."
- icon: "academic"
title: "Transmission des compétences"
text: "Former, documenter et outiller les nouvelles générations d'ingénieurs et de mainteneurs."
commitments:
title: "Nos engagements mesurables"
intro: "TheAfriForge s'engage à publier des indicateurs publics vérifiables."
quote: "La souveraineté se proclame peu ; elle se mesure beaucoup."
items:
- "Disponibilité et résilience des services critiques"
- "Délais de reprise en cas d'incident"
- "Nombre de projets hébergés et maintenus activement"
- "Niveau d'adoption par les institutions et entreprises africaines"
- "Programmes de formation et contribution des communautés"
vision:
title: "Notre vision écosystémique"
intro: "TheAfriForge n'est pas une plateforme unique. C'est un cadre de confiance capable d'accueillir un portefeuille évolutif de projets stratégiques, avec un objectif commun."
objective: "Augmenter l'autonomie collective du continent dans l'économie numérique mondiale."
items:
- icon: "git"
title: "Forge logicielle collaborative"
text: "GitForge.africa — dépôts Git, tickets, CI/CD hébergés sur le continent"
- icon: "package"
title: "Registres et miroirs régionaux"
text: "AfriRegistry — distribution locale des paquets NPM, Python, Docker"
- icon: "shield"
title: "Sécurisation de la chaîne logicielle"
text: "AIForge — outils de sécurité, conformité et analyse de code souverain"
- icon: "chart"
title: "Conformité et observabilité"
text: "Services de gouvernance, d'audit et de monitoring pour les institutions"
governance:
title: "Gouvernance et méthode"
quote: "La confiance ne se décrète pas. Elle se construit par la preuve."
items:
- "Une charte publique et révisable collectivement"
- "Une gouvernance multi-acteurs : académie, industrie, collectivités, communauté technique"
- "Des décisions tracées, documentées et opposables"
- "Des audits techniques et organisationnels réguliers"
- "Une feuille de route publiée et revue périodiquement"
coalition:
title: "Appel à coalition"
text: "Nous appelons les universités, centres de recherche, entreprises, datacenters, opérateurs, administrations, investisseurs d'impact et communautés open source africaines à rejoindre TheAfriForge."
statement: "Notre ambition n'est pas de copier des plateformes existantes. Notre ambition est de bâtir une capacité stratégique africaine, ouverte, fiable et durable."
closing: "Ce manifeste est un point de départ. Le chantier est collectif. Le moment d'agir est maintenant."
cta: "Rejoindre TheAfriForge"
---
## Préambule
L'Afrique n'est pas en retard de talent. Elle est en retard de contrôle.
Chaque jour, des équipes africaines construisent des services critiques pour nos administrations, nos banques, nos hôpitaux, nos universités et nos entreprises. Pourtant, une grande partie de ce patrimoine logiciel est encore hébergée, outillée et gouvernée hors de nos juridictions.
Cette dépendance n'est pas neutre. Elle expose nos écosystèmes à des risques de censure, de rupture d'accès, de captation de valeur, d'extraterritorialité juridique et de fragilité opérationnelle.
> **TheAfriForge existe pour changer cela.** Nous affirmons qu'un continent qui ne maîtrise pas ses infrastructures numériques ne maîtrise ni son avenir économique, ni sa sécurité, ni sa capacité d'innovation.
## Notre conviction
La souveraineté numérique n'est pas un slogan. C'est une capacité concrète :
- héberger nos codes et données sur des infrastructures fiables, proches et auditées
- gouverner nos outils de collaboration selon nos valeurs et nos besoins
- produire des communs open source qui restent au service de l'intérêt général
- assurer la continuité de service, même en cas de choc géopolitique ou technique
## Ce que TheAfriForge construit
TheAfriForge est une fondation à but non lucratif qui fédérera un écosystème africain de production logicielle autour de trois piliers.
### Infrastructure fédérée et résiliente
Nous interconnectons des capacités locales — datacenters, universités, partenaires techniques — pour offrir des services robustes, performants et distribués, sans dépendre d'un acteur unique hors du continent.
### Communs numériques ouverts
Nous hébergeons et structurons des projets open source stratégiques qui peuvent être adoptés par les institutions, les entreprises et les communautés, sous licence libre et gouvernance transparente.
### Gouvernance neutre et transparente
Nous opérons avec des règles claires, publiques et auditables, afin que les décisions techniques et institutionnelles servent le long terme, pas des intérêts particuliers.
## Nos principes non négociables
### Souveraineté d'abord
Les actifs critiques doivent rester sous contrôle africain, dans le respect de nos cadres juridiques.
### Ouverture et interopérabilité
Aucun verrou propriétaire. Les standards ouverts et la portabilité sont des exigences de base.
### Sécurité par conception
Chiffrement en transit et au repos, gestion rigoureuse des accès, défense en profondeur et auditabilité systématique.
### Performance locale
Réduire la latence, les coûts de transit international et les points uniques de défaillance.
### Neutralité institutionnelle
La fondation ne doit pas être capturée par un acteur unique, public ou privé.
### Responsabilité sociale et environnementale
Construire une infrastructure utile, sobre et durable, adaptée aux réalités du continent.
### Transmission des compétences
Former, documenter et outiller les nouvelles générations d'ingénieurs et de mainteneurs.
## Nos engagements mesurables
TheAfriForge s'engage à publier des objectifs et des indicateurs publics, notamment :
- disponibilité des services critiques
- délais de reprise en cas d'incident
- nombre de projets hébergés et maintenus activement
- niveau d'adoption par les institutions et entreprises africaines
- programmes de formation et contribution des communautés
> La souveraineté se proclame peu ; elle se mesure beaucoup.
## Notre vision écosystémique
TheAfriForge n'est pas une plateforme unique. C'est un cadre de confiance capable d'accueillir un portefeuille évolutif de projets stratégiques :
- forge logicielle collaborative
- registres et miroirs régionaux de dépendances
- outils de sécurisation de la chaîne logicielle
- services de conformité, de gouvernance et d'observabilité
Chaque projet doit contribuer à un objectif commun : **augmenter l'autonomie collective du continent dans l'économie numérique mondiale.**
## Gouvernance et méthode
Notre méthode repose sur :
- une charte publique
- une gouvernance multi-acteurs (académie, industrie, collectivités, communauté technique)
- des décisions tracées
- des audits techniques et organisationnels réguliers
- une feuille de route publiée et revue périodiquement
La confiance ne se décrète pas. Elle se construit par la preuve.
## Appel à coalition
Nous appelons les universités, centres de recherche, entreprises, datacenters, opérateurs, administrations, investisseurs d'impact et communautés open source africaines à rejoindre TheAfriForge.
Notre ambition n'est pas de copier des plateformes existantes. Notre ambition est de **bâtir une capacité stratégique africaine, ouverte, fiable et durable.**
---
*Ce manifeste est un point de départ. Le chantier est collectif. Le moment d'agir est maintenant.*

View file

@ -0,0 +1,81 @@
---
title: "Mentions legales"
description: "Informations legales et de publication du site TheAfriForge."
---
## Editeur du site
Le site TheAfriForge est edite par l'initiative TheAfriForge, fondation africaine du logiciel libre et de la souverainete numerique.
- Nom du projet : TheAfriForge
- Objet : developpement et gouvernance de communs numeriques open source en Afrique
- Contact editorial : contact@theafriforge.org
- Contact legal : legal@theafriforge.org
## Direction de la publication
La direction de la publication est assuree par l'equipe de coordination TheAfriForge.
## Statut et informations administratives
TheAfriForge opere comme une initiative a but non lucratif orientee interet general.
Les informations administratives complementaires (forme juridique, identifiants, siege social) sont communiquees sur demande legitime via legal@theafriforge.org.
## Hebergement
Le site est heberge sur une infrastructure technique administree pour le compte de TheAfriForge.
- Contact technique : infra@theafriforge.org
## Accessibilite du service
TheAfriForge met en oeuvre des moyens raisonnables pour assurer l'accessibilite du site 24h/24 et 7j/7.
Des interruptions peuvent intervenir pour maintenance, mise a jour de securite, incidents techniques ou causes externes.
## Propriete intellectuelle
Les contenus du site (textes, elements graphiques, marques et logos), sauf mention contraire, sont proteges par les lois applicables.
Les contenus open source references sur le site demeurent distribues selon leurs licences respectives.
Toute reproduction, representation ou adaptation totale ou partielle des contenus proprietaires sans autorisation ecrite prealable est interdite.
## Conditions d'usage
L'utilisateur s'engage a utiliser le site de maniere loyale, legale et sans porter atteinte a la securite, l'integrite ou la disponibilite des services.
Sont notamment interdits :
- l'extraction massive de contenus sans autorisation
- les tentatives d'intrusion ou de perturbation des services
- l'usage frauduleux des formulaires ou canaux de contact
## Responsabilite
TheAfriForge met en oeuvre des moyens raisonnables pour garantir l'exactitude des informations publiees. Toutefois, des erreurs ou omissions peuvent subsister.
L'utilisation des informations presentes sur ce site se fait sous la seule responsabilite de l'utilisateur.
TheAfriForge ne peut etre tenu responsable des dommages indirects lies a l'acces ou a l'usage du site, dans les limites prevues par les lois applicables.
## Liens externes
Le site peut contenir des liens vers des ressources tierces. TheAfriForge n'est pas responsable du contenu de ces sites externes.
## Protection des donnees personnelles
Le traitement des donnees personnelles est detaille dans la Politique de confidentialite du site.
Pour toute demande relative a vos donnees : privacy@theafriforge.org
## Droit applicable et juridiction
Les presentes mentions sont interpretees conformement au droit applicable au lieu d'etablissement de l'entite operatrice et, le cas echeant, aux regles internationales applicables.
En cas de litige, une resolution amiable est privilegiee avant toute procedure contentieuse.
## Mise a jour
Derniere mise a jour : 17 mai 2026.

View file

@ -0,0 +1,126 @@
---
title: "Politique de confidentialite"
description: "Politique de traitement des donnees personnelles sur TheAfriForge."
---
## Principe
TheAfriForge s'engage a proteger les donnees personnelles des visiteurs, contributeurs et partenaires du site.
Cette politique explique quelles donnees sont traitees, pourquoi, pendant combien de temps, et quels sont vos droits.
## Responsable de traitement
Le responsable de traitement est l'initiative TheAfriForge.
- Contact general : contact@theafriforge.org
- Contact confidentialite : privacy@theafriforge.org
- Contact legal : legal@theafriforge.org
## Donnees traitees
Selon vos interactions, nous pouvons traiter les categories suivantes :
- informations de contact transmises volontairement (nom, email, organisation, message)
- donnees techniques minimales de navigation (logs serveur, adresse IP, date et heure, page demandee)
- informations necessaires a la securisation et au fonctionnement du site
TheAfriForge ne collecte pas volontairement de donnees sensibles, sauf obligation legale expresse ou demande explicite justifiee.
## Finalites
Ces donnees sont traitees pour :
- repondre aux demandes de contact et de partenariat
- assurer la securite, la disponibilite et l'integrite du site
- piloter l'amelioration continue des services de TheAfriForge
- respecter les obligations legales, reglementaires et de conformite applicables
## Base de traitement
Les traitements reposent sur :
- votre consentement lorsque vous soumettez volontairement des informations
- l'interet legitime de TheAfriForge pour la securite et le bon fonctionnement du site
- le respect des obligations legales applicables
Lorsque le traitement repose sur votre consentement, vous pouvez le retirer a tout moment sans effet retroactif.
## Duree de conservation
Les donnees sont conservees pour une duree proportionnee a leur finalite, puis supprimees ou anonymisees.
Regles indicatives :
- demandes de contact : jusqu'a 24 mois apres le dernier echange utile
- journaux techniques : jusqu'a 12 mois, sauf exigence de securite ou d'enquete
- traces de conformite : duree imposee par les obligations legales applicables
## Destinataires
Les donnees sont accessibles uniquement aux equipes habilitees de TheAfriForge et a ses prestataires techniques strictement necessaires a l'operation du site.
Aucune cession commerciale de donnees personnelles n'est realisee.
## Sous-traitants et transferts
TheAfriForge peut recourir a des sous-traitants techniques (hebergement, supervision, anti-abus, messagerie).
En cas de transfert hors de votre zone juridique, TheAfriForge met en oeuvre des garanties appropriees (clauses contractuelles, mesures techniques complementaires, minimisation des donnees).
## Cookies
Le site utilise principalement des cookies techniques necessaires au fonctionnement.
Si des cookies de mesure d'audience sont actives, ils sont utilises de maniere proportionnee et dans le respect des exigences applicables.
Vous pouvez configurer votre navigateur pour bloquer certains cookies. Le refus des cookies strictement necessaires peut toutefois degrader le fonctionnement du site.
## Vos droits
Vous pouvez demander :
- l'acces a vos donnees
- la rectification de vos donnees
- l'effacement de vos donnees, lorsque applicable
- la limitation ou l'opposition a certains traitements
- la portabilite de vos donnees, lorsque applicable
- la definition de directives sur le sort de vos donnees apres deces, lorsque le droit applicable le prevoit
Pour exercer vos droits : privacy@theafriforge.org
Nous pouvons demander un justificatif raisonnable d'identite pour prevenir les demandes frauduleuses.
## Delais de reponse
TheAfriForge traite les demandes dans un delai raisonnable et conforme aux exigences legales applicables.
En cas de complexite particuliere, le delai peut etre prolonge avec information prealable.
## Securite
TheAfriForge met en place des mesures techniques et organisationnelles raisonnables pour proteger les donnees contre l'acces non autorise, l'alteration ou la perte.
Ces mesures incluent notamment le controle d'acces, la journalisation technique, la limitation des privileges, la surveillance de securite et les procedures de reprise.
## Notification d'incident
En cas de violation de donnees susceptible d'engendrer un risque significatif, TheAfriForge applique les obligations de notification prevues par les textes applicables.
## Mineurs
Le site n'est pas destine a collecter intentionnellement des donnees de mineurs sans encadrement legal approprie.
Si vous pensez qu'un mineur nous a transmis des donnees de maniere inappropriee, contactez privacy@theafriforge.org.
## Contact
Pour toute question relative a la confidentialite : privacy@theafriforge.org
Pour toute demande legale : legal@theafriforge.org
## Reclamation
Si vous estimez que vos droits ne sont pas respectes, vous pouvez contacter TheAfriForge pour resolution amiable, puis saisir l'autorite competente de protection des donnees selon votre pays.
Derniere mise a jour : 17 mai 2026.

83
content/fr/projets.md Normal file
View file

@ -0,0 +1,83 @@
---
title: "Nos Projets"
description: "Un portefeuille de projets open source stratégiques pour construire une souveraineté numérique africaine concrète, mesurable et durable."
layout: "projects"
hero:
metrics:
- value: "03"
label: "Programmes actifs"
- value: "12"
label: "Partenaires techniques ciblés"
- value: "24 mois"
label: "Horizon de déploiement"
portfolio:
tag: "Portefeuille"
title: "Initiatives en production, incubation et recherche"
intro: "Chaque projet répond a un besoin critique: collaboration souveraine, distribution fiable des dependances, et securisation de la chaine logicielle africaine."
impactLabel: "Impact attendu"
stackLabel: "Socle technique"
items:
- name: "GitForge.africa"
status: "Production"
icon: "git"
text: "Forge de code souveraine pour heberger depots, tickets, pipelines CI/CD et revue de code sur infrastructure continentale."
impact: "Reduction des risques d'arret de service sur des plateformes externes pour les projets critiques."
stack: "Git, CI/CD, IAM, observabilite"
cta: "Voir la feuille de route"
link: "#"
- name: "AfriRegistry"
status: "Incubation"
icon: "package"
text: "Registre regional pour distribuer npm, PyPI et images conteneurs avec miroirs locaux et politiques de confiance."
impact: "Baisse de latence, meilleure disponibilite et meilleure maitrise des artefacts logiciels."
stack: "Registry federation, cache, signatures, policy engine"
cta: "Suivre l'incubation"
link: "#"
- name: "AIForge Secure"
status: "Recherche"
icon: "shield"
text: "Programme R&D pour l'analyse de code, la conformite et la securisation de la chaine logicielle dans des environnements souverains."
impact: "Acceleration des audits et renforcement de la confiance pour les institutions publiques et privees."
stack: "SBOM, SAST, provenance, tableaux de bord"
cta: "Explorer la recherche"
link: "#"
roadmap:
title: "Roadmap 2026-2027"
intro: "Nous operons par vagues: stabilisation des fondamentaux, extension regionale, puis industrialisation des services."
items:
- period: "T3 2026"
title: "Stabilisation des briques coeur"
text: "Durcir l'architecture, les sauvegardes et les procedures de reprise sur incident."
- period: "T4 2026"
title: "Federation multi-sites"
text: "Connecter plusieurs noeuds regionaux et harmoniser les standards de gouvernance."
- period: "T1 2027"
title: "Ouverture partenaires"
text: "Elargir l'acces aux institutions, universites et entreprises via un cadre d'onboarding unifie."
selection:
title: "Comment nous selectionnons un projet"
intro: "TheAfriForge soutient les initiatives qui servent l'interet general et renforcent l'autonomie numerique africaine."
items:
- icon: "flag"
title: "Valeur souveraine"
text: "Le projet doit reduire une dependance structurelle critique."
- icon: "network"
title: "Impact ecosystemique"
text: "Le projet doit etre reutilisable par plusieurs acteurs du continent."
- icon: "lock"
title: "Maturite securite"
text: "Le projet doit integrer la securite et l'auditabilite des la conception."
- icon: "academic"
title: "Transmission"
text: "Le projet doit favoriser la montee en competence locale et la documentation ouverte."
call:
title: "Vous portez un projet strategique ?"
text: "Partagez votre initiative avec TheAfriForge pour evaluer un accompagnement en hebergement, gouvernance et acceleration technique."
cta: "Soumettre un projet"
link: "devenir-membre"
---

74
hugo.toml Normal file
View file

@ -0,0 +1,74 @@
baseURL = "https://theafriforge.org/"
title = "TheAfriForge"
defaultContentLanguage = "fr"
defaultContentLanguageInSubdir = true
enableRobotsTXT = true
disableKinds = ["taxonomy", "term"]
disableAliases = true
[params]
organization = "TheAfriForge"
gitforgeUrl = "https://gitforge.africa"
repositoryUrl = "https://github.com/theafriforge"
# Membership form configuration (fallback values).
# Priority used in templates:
# 1) HUGO_MEMBERSHIP_FORM_ACTION / HUGO_MEMBERSHIP_FORM_SUCCESS_PATH
# 2) params.membershipFormAction / params.membershipFormSuccessPath (below)
# 3) page front matter defaults
membershipFormAction = ""
membershipFormSuccessPath = ""
[languages]
[languages.fr]
languageName = "Français"
languageCode = "fr-FR"
weight = 1
contentDir = "content/fr"
title = "TheAfriForge"
[[languages.fr.menu.main]]
name = "Accueil"
pageRef = "/"
weight = 10
[[languages.fr.menu.main]]
name = "Manifeste"
pageRef = "/manifeste"
weight = 20
[[languages.fr.menu.main]]
name = "Nos Projets"
pageRef = "/projets"
weight = 30
[[languages.fr.menu.main]]
name = "Gouvernance"
pageRef = "/gouvernance"
weight = 40
[languages.en]
languageName = "English"
languageCode = "en-US"
weight = 2
contentDir = "content/en"
title = "TheAfriForge"
[[languages.en.menu.main]]
name = "Home"
pageRef = "/"
weight = 10
[[languages.en.menu.main]]
name = "Manifesto"
pageRef = "/manifesto"
weight = 20
[[languages.en.menu.main]]
name = "Our Projects"
pageRef = "/projects"
weight = 30
[[languages.en.menu.main]]
name = "Governance"
pageRef = "/governance"
weight = 40

37
i18n/en.toml Normal file
View file

@ -0,0 +1,37 @@
[siteDescription]
other = "African foundation for open-source software and digital sovereignty."
[gitforgeCta]
other = "Access GitForge"
[headerCtaMembers]
other = "Become a Member"
[themeModeLight]
other = "Day"
[themeModeDark]
other = "Night"
[themeToggleAria]
other = "Toggle theme"
[themeSetLightAria]
other = "Enable day mode"
[themeSetDarkAria]
other = "Enable night mode"
[footerTagline]
other = "Foundation for open-source software and digital sovereignty in Africa."
[footerLegal]
other = "Legal Notice"
[footerPrivacy]
other = "Privacy Policy"
[footerGovernance]
other = "Governance Charter"

37
i18n/fr.toml Normal file
View file

@ -0,0 +1,37 @@
[siteDescription]
other = "Fondation africaine du logiciel libre et de la souveraineté numerique."
[gitforgeCta]
other = "Acceder a GitForge"
[headerCtaMembers]
other = "Devenir Membre"
[themeModeLight]
other = "Jour"
[themeModeDark]
other = "Nuit"
[themeToggleAria]
other = "Basculer le theme"
[themeSetLightAria]
other = "Activer le mode jour"
[themeSetDarkAria]
other = "Activer le mode nuit"
[footerTagline]
other = "Fondation du logiciel libre et de la souverainete numerique en Afrique."
[footerLegal]
other = "Mentions legales"
[footerPrivacy]
other = "Politique de confidentialite"
[footerGovernance]
other = "Charte de gouvernance"

View file

@ -0,0 +1,16 @@
{{ if eq .Level 2 }}
<div class="mt-16 mb-6">
<div class="flex items-start gap-4">
<div class="mt-1.5 h-6 w-1.5 flex-shrink-0 rounded-full bg-green-500"></div>
<h2 id="{{ .Anchor }}" class="text-2xl font-black text-slate-900 leading-snug">{{ .Text | safeHTML }}</h2>
</div>
<div class="mt-4 ml-5 h-px bg-slate-100"></div>
</div>
{{ else if eq .Level 3 }}
<h3 id="{{ .Anchor }}" class="mt-8 mb-3 flex items-center gap-2 text-base font-bold uppercase tracking-widest text-green-700">
<span class="inline-block h-1.5 w-1.5 rounded-full bg-green-500"></span>
{{ .Text | safeHTML }}
</h3>
{{ else }}
<h{{ .Level }} id="{{ .Anchor }}">{{ .Text | safeHTML }}</h{{ .Level }}>
{{ end }}

View file

@ -0,0 +1,37 @@
<!doctype html>
<html lang="{{ .Site.Language.Lang }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} | {{ .Site.Title }}{{ end }}</title>
<meta name="description" content="{{ i18n "siteDescription" }}">
<script>
tailwind = {
config: {
theme: {
extend: {
fontFamily: {
sans: ["Space Grotesk", "ui-sans-serif", "system-ui", "sans-serif"]
}
}
}
}
}
</script>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
<body class="min-h-screen bg-white text-slate-900 antialiased">
{{ partial "site-header.html" . }}
<main>
{{ block "main" . }}{{ end }}
</main>
{{ partial "site-footer.html" . }}
</body>
</html>

View file

@ -0,0 +1,128 @@
{{ define "main" }}
{{ $p := .Params }}
<!-- HERO -->
<section class="relative overflow-hidden bg-green-950 px-6 py-28 lg:px-8">
<div class="absolute inset-0 opacity-10" style="background-image:radial-gradient(circle at 1px 1px,white 1px,transparent 0);background-size:34px 34px"></div>
<div class="relative mx-auto max-w-6xl">
<span class="inline-flex items-center gap-2 rounded-full border border-green-700 bg-green-900 px-4 py-1.5 text-xs font-bold uppercase tracking-widest text-green-400 mb-7">
<span class="h-1.5 w-1.5 rounded-full bg-green-400"></span>
TheAfriForge
</span>
<h1 class="max-w-4xl text-5xl md:text-7xl font-black leading-tight text-white">{{ .Title }}</h1>
{{ with $p.description }}
<p class="mt-6 max-w-3xl text-lg leading-relaxed text-green-200">{{ . }}</p>
{{ end }}
<div class="mt-10 grid max-w-3xl gap-4 sm:grid-cols-3">
{{ range $p.hero.metrics }}
<div class="rounded-xl border border-green-900 bg-green-900/55 px-4 py-4">
<p class="text-2xl font-black text-green-300">{{ .value }}</p>
<p class="text-xs uppercase tracking-wider text-green-500">{{ .label }}</p>
</div>
{{ end }}
</div>
</div>
</section>
<!-- PRINCIPLES -->
<section class="bg-white px-6 py-20 lg:px-8">
<div class="mx-auto max-w-6xl">
<h2 class="text-3xl md:text-4xl font-black text-slate-900">{{ $p.principles.title }}</h2>
<p class="mt-4 max-w-3xl text-slate-600">{{ $p.principles.intro }}</p>
<div class="mt-10 grid gap-6 md:grid-cols-2 lg:grid-cols-4">
{{ range $p.principles.items }}
<article class="rounded-2xl border border-slate-200 bg-slate-50 p-6">
<span class="inline-flex h-10 w-10 items-center justify-center rounded-xl bg-green-900 text-green-300">
{{ partial "icon.html" .icon }}
</span>
<h3 class="mt-4 text-lg font-black text-slate-900">{{ .title }}</h3>
<p class="mt-2 text-sm leading-relaxed text-slate-600">{{ .text }}</p>
</article>
{{ end }}
</div>
</div>
</section>
<!-- BODIES -->
<section class="bg-slate-100 px-6 py-20 lg:px-8">
<div class="mx-auto max-w-6xl">
<h2 class="text-3xl md:text-4xl font-black text-slate-900">{{ $p.bodies.title }}</h2>
<p class="mt-4 max-w-3xl text-slate-600">{{ $p.bodies.intro }}</p>
<div class="mt-10 grid gap-6 md:grid-cols-3">
{{ range $p.bodies.items }}
<article class="rounded-2xl border border-slate-200 bg-white p-7 shadow-sm">
<div class="flex items-center gap-3">
<span class="inline-flex h-10 w-10 items-center justify-center rounded-xl bg-slate-900 text-green-300">
{{ partial "icon.html" .icon }}
</span>
<h3 class="text-xl font-black text-slate-900">{{ .title }}</h3>
</div>
<p class="mt-4 text-slate-600">{{ .text }}</p>
<ul class="mt-5 space-y-2 text-sm text-slate-600">
{{ range .missions }}
<li class="flex items-start gap-2">
<span class="mt-1 inline-flex h-1.5 w-1.5 rounded-full bg-green-600"></span>
<span>{{ . }}</span>
</li>
{{ end }}
</ul>
</article>
{{ end }}
</div>
</div>
</section>
<!-- DECISION LOOP -->
<section class="bg-white px-6 py-20 lg:px-8">
<div class="mx-auto max-w-6xl">
<h2 class="text-3xl md:text-4xl font-black text-slate-900">{{ $p.decisions.title }}</h2>
<p class="mt-4 max-w-3xl text-slate-600">{{ $p.decisions.intro }}</p>
<div class="mt-10 space-y-4">
{{ range $index, $item := $p.decisions.steps }}
<div class="rounded-2xl border border-slate-200 bg-slate-50 p-6">
<div class="flex items-start gap-4">
<span class="inline-flex h-9 min-w-9 items-center justify-center rounded-full bg-green-900 px-3 text-sm font-black text-green-300">{{ add $index 1 }}</span>
<div>
<h3 class="text-lg font-black text-slate-900">{{ .title }}</h3>
<p class="mt-2 text-slate-600">{{ .text }}</p>
</div>
</div>
</div>
{{ end }}
</div>
</div>
</section>
<!-- TRANSPARENCY -->
<section class="bg-green-950 px-6 py-20 lg:px-8">
<div class="mx-auto max-w-6xl">
<h2 class="text-3xl md:text-4xl font-black text-white">{{ $p.transparency.title }}</h2>
<p class="mt-4 max-w-3xl text-green-200">{{ $p.transparency.intro }}</p>
<div class="mt-10 grid gap-5 md:grid-cols-2">
{{ range $p.transparency.items }}
<div class="rounded-2xl border border-green-900 bg-green-900/45 p-6">
<h3 class="text-lg font-black text-white">{{ .title }}</h3>
<p class="mt-2 text-green-200">{{ .text }}</p>
</div>
{{ end }}
</div>
</div>
</section>
<!-- CTA -->
<section class="bg-white px-6 py-20 lg:px-8">
<div class="mx-auto max-w-4xl text-center">
<h2 class="text-4xl font-black text-slate-900">{{ $p.call.title }}</h2>
<p class="mt-5 text-lg leading-relaxed text-slate-600">{{ $p.call.text }}</p>
<a href="{{ $p.call.link | relLangURL }}" class="mt-10 inline-flex items-center justify-center rounded-xl bg-green-600 px-8 py-4 text-sm font-black uppercase tracking-wide text-white transition hover:bg-green-500">
{{ $p.call.cta }}
</a>
</div>
</section>
{{ end }}

312
layouts/_default/join.html Normal file
View file

@ -0,0 +1,312 @@
{{ define "main" }}
{{ $p := .Params }}
{{ $privacyPath := cond (eq .Site.Language.Lang "fr") "politique-confidentialite" "privacy-policy" }}
{{ $formAction := or (getenv "HUGO_MEMBERSHIP_FORM_ACTION") .Site.Params.membershipFormAction $p.form.action }}
{{ $defaultSuccessPath := cond (eq .Site.Language.Lang "fr") "devenir-membre" "become-member" }}
{{ $successPath := or (getenv "HUGO_MEMBERSHIP_FORM_SUCCESS_PATH") .Site.Params.membershipFormSuccessPath $defaultSuccessPath }}
{{ $formbricksHost := getenv "HUGO_FORMBRICKS_API_HOST" }}
{{ $formbricksEnv := getenv "HUGO_FORMBRICKS_ENV_ID" }}
{{ $formbricksEvent := or (getenv "HUGO_FORMBRICKS_EVENT") "membership_form_submitted" }}
{{ $formbricksSdkURL := or (getenv "HUGO_FORMBRICKS_SDK_URL") "https://form.theafriforge.com/js/formbricks.umd.cjs" }}
<section class="relative overflow-hidden bg-green-950 px-6 py-28 lg:px-8">
<div class="absolute inset-0 opacity-10" style="background-image:radial-gradient(circle at 1px 1px,white 1px,transparent 0);background-size:34px 34px"></div>
<div class="relative mx-auto max-w-6xl">
<span class="inline-flex items-center gap-2 rounded-full border border-green-700 bg-green-900 px-4 py-1.5 text-xs font-bold uppercase tracking-widest text-green-400 mb-7">
<span class="h-1.5 w-1.5 rounded-full bg-green-400"></span>
TheAfriForge
</span>
<h1 class="max-w-4xl text-5xl md:text-7xl font-black leading-tight text-white">{{ .Title }}</h1>
{{ with $p.description }}
<p class="mt-6 max-w-3xl text-lg leading-relaxed text-green-200">{{ . }}</p>
{{ end }}
</div>
</section>
<section class="bg-white px-6 py-20 lg:px-8">
<div class="mx-auto max-w-6xl">
<h2 class="text-3xl md:text-4xl font-black text-slate-900">{{ $p.value.title }}</h2>
<p class="mt-4 max-w-3xl text-slate-600">{{ $p.value.intro }}</p>
<div class="mt-10 grid gap-6 md:grid-cols-3">
{{ range $p.value.items }}
<article class="rounded-2xl border border-slate-200 bg-slate-50 p-6">
<span class="inline-flex h-10 w-10 items-center justify-center rounded-xl bg-green-900 text-green-300">
{{ partial "icon.html" .icon }}
</span>
<h3 class="mt-4 text-lg font-black text-slate-900">{{ .title }}</h3>
<p class="mt-2 text-sm text-slate-600 leading-relaxed">{{ .text }}</p>
</article>
{{ end }}
</div>
</div>
</section>
<section class="bg-slate-100 px-6 py-20 lg:px-8">
<div class="mx-auto max-w-6xl">
<h2 class="text-3xl md:text-4xl font-black text-slate-900">{{ $p.membership.title }}</h2>
<p class="mt-4 max-w-3xl text-slate-600">{{ $p.membership.intro }}</p>
<div class="mt-10 grid gap-6 md:grid-cols-3">
{{ range $p.membership.items }}
<article class="rounded-2xl border border-slate-200 bg-white p-7 shadow-sm">
<h3 class="text-xl font-black text-slate-900">{{ .title }}</h3>
<p class="mt-3 text-slate-600">{{ .text }}</p>
<ul class="mt-5 space-y-2 text-sm text-slate-600">
{{ range .expectations }}
<li class="flex items-start gap-2">
<span class="mt-1 inline-flex h-1.5 w-1.5 rounded-full bg-green-600"></span>
<span>{{ . }}</span>
</li>
{{ end }}
</ul>
</article>
{{ end }}
</div>
</div>
</section>
<section class="bg-white px-6 py-20 lg:px-8">
<div class="mx-auto max-w-6xl">
<h2 class="text-3xl md:text-4xl font-black text-slate-900">{{ $p.process.title }}</h2>
<div class="mt-10 space-y-4">
{{ range $index, $item := $p.process.steps }}
<div class="rounded-2xl border border-slate-200 bg-slate-50 p-6">
<div class="flex items-start gap-4">
<span class="inline-flex h-9 min-w-9 items-center justify-center rounded-full bg-green-900 px-3 text-sm font-black text-green-300">{{ add $index 1 }}</span>
<div>
<h3 class="text-lg font-black text-slate-900">{{ .title }}</h3>
<p class="mt-2 text-slate-600">{{ .text }}</p>
{{ with .delay }}
<p class="mt-2 text-xs font-bold uppercase tracking-wider text-green-700">{{ . }}</p>
{{ end }}
</div>
</div>
</div>
{{ end }}
</div>
</div>
</section>
<section class="bg-green-950 px-6 py-20 lg:px-8">
<div class="mx-auto max-w-5xl">
<h2 class="text-3xl md:text-4xl font-black text-white">{{ $p.form.title }}</h2>
<p class="mt-4 max-w-3xl text-green-200">{{ $p.form.intro }}</p>
<div id="join-success" class="mt-10 hidden rounded-2xl border border-green-500 bg-green-900/60 p-10 text-center">
<svg class="mx-auto mb-5 h-14 w-14 text-green-400" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0z"/></svg>
<h3 class="text-2xl font-black text-white">{{ $p.form.successTitle }}</h3>
<p class="mt-3 text-green-200">{{ $p.form.successText }}</p>
</div>
<p id="join-error" class="mt-4 hidden rounded-xl border border-red-700 bg-red-900/40 px-5 py-3 text-sm text-red-300">{{ $p.form.errorText }}</p>
<form id="join-form" data-action="{{ $formAction }}" data-sending="{{ $p.form.sending }}" data-submit="{{ $p.form.submit }}" data-formbricks-host="{{ $formbricksHost }}" data-formbricks-env="{{ $formbricksEnv }}" data-formbricks-event="{{ $formbricksEvent }}" data-formbricks-sdk="{{ $formbricksSdkURL }}" novalidate class="mt-10 grid gap-5 rounded-2xl border border-green-900 bg-green-900/40 p-8 md:grid-cols-2">
<input type="text" name="_honey" class="hidden" tabindex="-1" autocomplete="off">
<label class="block">
<span class="mb-2 block text-sm font-bold text-green-100">{{ $p.form.labels.organization }}</span>
<input name="organization" class="w-full rounded-lg border border-green-800 bg-green-950/60 px-4 py-3 text-sm text-white placeholder:text-green-400 focus:border-green-500 focus:outline-none" placeholder="{{ $p.form.placeholders.organization }}">
</label>
<label class="block">
<span class="mb-2 block text-sm font-bold text-green-100">{{ $p.form.labels.actorType }}</span>
<select name="actor_type" required class="w-full rounded-lg border border-green-800 bg-green-950/60 px-4 py-3 text-sm text-white focus:border-green-500 focus:outline-none">
<option value="" disabled selected>{{ $p.form.placeholders.actorType }}</option>
{{ range $p.form.actorTypes }}
<option value="{{ . }}">{{ . }}</option>
{{ end }}
</select>
</label>
<label class="block">
<span class="mb-2 block text-sm font-bold text-green-100">{{ $p.form.labels.firstName }}</span>
<input name="first_name" required class="w-full rounded-lg border border-green-800 bg-green-950/60 px-4 py-3 text-sm text-white placeholder:text-green-400 focus:border-green-500 focus:outline-none" placeholder="{{ $p.form.placeholders.firstName }}">
</label>
<label class="block">
<span class="mb-2 block text-sm font-bold text-green-100">{{ $p.form.labels.lastName }}</span>
<input name="last_name" required class="w-full rounded-lg border border-green-800 bg-green-950/60 px-4 py-3 text-sm text-white placeholder:text-green-400 focus:border-green-500 focus:outline-none" placeholder="{{ $p.form.placeholders.lastName }}">
</label>
<label class="block">
<span class="mb-2 block text-sm font-bold text-green-100">{{ $p.form.labels.profession }}</span>
<input name="profession" required class="w-full rounded-lg border border-green-800 bg-green-950/60 px-4 py-3 text-sm text-white placeholder:text-green-400 focus:border-green-500 focus:outline-none" placeholder="{{ $p.form.placeholders.profession }}">
</label>
<label class="block md:col-span-2">
<span class="mb-2 block text-sm font-bold text-green-100">{{ $p.form.labels.country }}</span>
<select name="country" required class="w-full rounded-lg border border-green-800 bg-green-950/60 px-4 py-3 text-sm text-white focus:border-green-500 focus:outline-none">
<option value="" disabled selected>{{ $p.form.placeholders.country }}</option>
{{ range $p.form.countries }}
<option value="{{ . }}">{{ . }}</option>
{{ end }}
</select>
</label>
<label class="block">
<span class="mb-2 block text-sm font-bold text-green-100">{{ $p.form.labels.email }}</span>
<input type="email" name="email" required class="w-full rounded-lg border border-green-800 bg-green-950/60 px-4 py-3 text-sm text-white placeholder:text-green-400 focus:border-green-500 focus:outline-none" placeholder="{{ $p.form.placeholders.email }}">
</label>
<label class="block">
<span class="mb-2 block text-sm font-bold text-green-100">{{ $p.form.labels.phone }}</span>
<input name="phone" required class="w-full rounded-lg border border-green-800 bg-green-950/60 px-4 py-3 text-sm text-white placeholder:text-green-400 focus:border-green-500 focus:outline-none" placeholder="{{ $p.form.placeholders.phone }}">
</label>
<label class="block md:col-span-2">
<span class="mb-2 block text-sm font-bold text-green-100">{{ $p.form.labels.contribution }}</span>
<textarea name="contribution" rows="4" required class="w-full rounded-lg border border-green-800 bg-green-950/60 px-4 py-3 text-sm text-white placeholder:text-green-400 focus:border-green-500 focus:outline-none" placeholder="{{ $p.form.placeholders.contribution }}"></textarea>
</label>
<label class="flex items-start gap-3 md:col-span-2">
<input type="checkbox" name="consent" required class="mt-1 h-4 w-4 rounded border-green-700 bg-green-950 text-green-500">
<span class="text-sm text-green-200">{{ $p.form.consentText }} <a href="{{ $privacyPath | relLangURL }}" class="font-bold text-green-300 hover:text-green-200">{{ $p.form.privacyLinkLabel }}</a>.</span>
</label>
<div class="md:col-span-2">
<button id="join-submit" type="submit" class="inline-flex items-center justify-center rounded-xl bg-green-500 px-8 py-4 text-sm font-black uppercase tracking-wide text-white transition hover:bg-green-400 disabled:opacity-60 disabled:cursor-not-allowed">
{{ $p.form.submit }}
</button>
</div>
</form>
</div>
</section>
<script>
(function () {
var form = document.getElementById('join-form');
var btn = document.getElementById('join-submit');
var successEl = document.getElementById('join-success');
var errorEl = document.getElementById('join-error');
if (!form) return;
function showSuccess() {
form.classList.add('hidden');
successEl.classList.remove('hidden');
successEl.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
function showError() {
btn.disabled = false;
btn.textContent = form.dataset.submit;
errorEl.classList.remove('hidden');
}
function toFields(raw) {
var out = {};
raw.forEach(function (val, key) {
if (key !== '_honey') out[key] = val;
});
return out;
}
function initFormbricks(host, envId, sdkUrl) {
return new Promise(function (resolve, reject) {
if (!host || !envId) {
resolve(false);
return;
}
function boot() {
if (!window.formbricks || typeof window.formbricks.init !== 'function') {
reject(new Error('formbricks not ready'));
return;
}
window.formbricks.init({ environmentId: envId, apiHost: host });
resolve(true);
}
if (window.formbricks && typeof window.formbricks.init === 'function') {
boot();
return;
}
var src = (sdkUrl || '').trim();
if (!src && host) {
src = host.replace(/\/$/, '') + '/js/formbricks.umd.cjs';
}
if (!src) {
reject(new Error('sdk url missing'));
return;
}
var existing = document.querySelector('script[data-formbricks-sdk="1"]');
if (existing) {
existing.addEventListener('load', boot, { once: true });
existing.addEventListener('error', function () { reject(new Error('sdk load failed')); }, { once: true });
return;
}
var script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.src = src;
script.dataset.formbricksSdk = '1';
script.addEventListener('load', boot, { once: true });
script.addEventListener('error', function () { reject(new Error('sdk load failed')); }, { once: true });
document.head.appendChild(script);
});
}
form.addEventListener('submit', function (e) {
e.preventDefault();
if (form.querySelector('[name="_honey"]').value) return;
var action = form.dataset.action;
if (!action) { errorEl.classList.remove('hidden'); return; }
btn.disabled = true;
btn.textContent = form.dataset.sending;
errorEl.classList.add('hidden');
var raw = new FormData(form);
var fields = toFields(raw);
var fbHost = (form.dataset.formbricksHost || '').trim();
var fbEnv = (form.dataset.formbricksEnv || '').trim();
var fbEvent = (form.dataset.formbricksEvent || 'membership_form_submitted').trim();
var fbSdk = (form.dataset.formbricksSdk || '').trim();
var isDebug = window.location.search.indexOf('formbricksDebug=true') !== -1;
if (isDebug) {
console.info('[membership] Formbricks config', {
apiHost: fbHost,
environmentId: fbEnv,
event: fbEvent,
sdkUrl: fbSdk
});
}
console.log('[membership] Submit with env=' + fbEnv + ' event=' + fbEvent + ' host=' + fbHost);
if (fbHost && fbEnv) {
initFormbricks(fbHost, fbEnv, fbSdk)
.then(function (ready) {
if (!ready || !window.formbricks || typeof window.formbricks.track !== 'function') {
throw new Error('formbricks track unavailable');
}
window.formbricks.track(fbEvent, fields);
console.log('[membership] Event tracked: ' + fbEvent);
showSuccess();
})
.catch(function (err) {
console.warn('[membership] Formbricks error (non-blocking):', err);
showSuccess();
});
return;
}
var payload = JSON.stringify({ data: fields });
fetch(action, {
method: 'POST',
body: payload,
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }
})
.then(function (res) {
if (!res.ok) throw new Error('server error');
showSuccess();
})
.catch(showError);
});
})();
</script>
{{ end }}

View file

@ -0,0 +1,191 @@
{{ define "main" }}
{{ $p := .Params }}
{{ $joinPath := cond (eq .Site.Language.Lang "fr") "devenir-membre" "become-member" }}
<!-- ─── HERO ─────────────────────────────────────────────────── -->
<section class="relative overflow-hidden bg-green-950 px-6 py-28 lg:px-8">
<div class="absolute inset-0 opacity-5" style="background-image:radial-gradient(circle at 1px 1px,white 1px,transparent 0);background-size:40px 40px"></div>
<div class="relative mx-auto max-w-5xl">
<span class="inline-flex items-center gap-2 rounded-full border border-green-700 bg-green-900 px-4 py-1.5 text-xs font-bold uppercase tracking-widest text-green-400 mb-8">
<span class="h-1.5 w-1.5 rounded-full bg-green-400"></span>
TheAfriForge
</span>
<h1 class="text-5xl md:text-7xl font-black leading-none text-white mb-6">{{ .Title }}</h1>
{{ with .Description }}
<p class="text-lg text-green-200 max-w-2xl leading-relaxed">{{ . }}</p>
{{ end }}
</div>
</section>
<!-- ─── PRÉAMBULE ─────────────────────────────────────────────── -->
<section class="bg-white px-6 py-20 lg:px-8">
<div class="mx-auto max-w-5xl">
<blockquote class="relative mb-12 rounded-2xl bg-green-950 px-10 py-10">
<svg class="absolute top-6 left-6 h-8 w-8 text-green-700" fill="currentColor" viewBox="0 0 24 24"><path d="M14.017 21v-7.391c0-5.704 3.731-9.57 8.983-10.609l.995 2.151c-2.432.917-3.995 3.638-3.995 5.849h4v10h-9.983zm-14.017 0v-7.391c0-5.704 3.748-9.57 9-10.609l.996 2.151c-2.433.917-3.996 3.638-3.996 5.849h3.983v10h-9.983z"/></svg>
<p class="text-2xl md:text-3xl font-black leading-snug text-white pl-6">{{ $p.preamble.quote }}</p>
</blockquote>
<div class="grid md:grid-cols-2 gap-8 text-slate-600 text-lg leading-relaxed">
<p>{{ $p.preamble.text }}</p>
<div class="rounded-xl border-l-4 border-green-500 bg-green-50 p-6">
<p class="font-semibold text-green-900">{{ partial "brand-highlight.html" $p.preamble.conviction }}</p>
</div>
</div>
</div>
</section>
<!-- ─── CONVICTION ───────────────────────────────────────────── -->
<section class="bg-slate-50 px-6 py-20 lg:px-8">
<div class="mx-auto max-w-5xl">
<div class="mb-12">
<h2 class="text-3xl font-black text-slate-900 mb-3">{{ $p.conviction.title }}</h2>
<p class="text-slate-500 max-w-xl">{{ $p.conviction.intro }}</p>
</div>
<div class="grid sm:grid-cols-2 gap-4">
{{ range $p.conviction.items }}
<div class="flex items-start gap-4 rounded-xl bg-white border border-slate-100 p-6 shadow-sm hover:shadow-md transition">
<div class="flex-shrink-0 flex h-11 w-11 items-center justify-center rounded-lg bg-green-100 text-green-700">
{{ partial "icon.html" .icon }}
</div>
<p class="text-slate-700 font-medium leading-relaxed">{{ .text }}</p>
</div>
{{ end }}
</div>
</div>
</section>
<!-- ─── PILIERS ───────────────────────────────────────────────── -->
<section class="bg-white px-6 py-20 lg:px-8">
<div class="mx-auto max-w-5xl">
<div class="mb-12">
<h2 class="text-3xl font-black text-slate-900 mb-3">{{ $p.pillars.title }}</h2>
<p class="text-slate-500 max-w-xl">{{ $p.pillars.intro }}</p>
</div>
<div class="grid md:grid-cols-3 gap-6">
{{ range $p.pillars.items }}
<div class="group relative rounded-2xl border border-slate-200 bg-white p-8 shadow-sm hover:border-green-400 hover:shadow-lg transition">
<div class="mb-6 flex items-center justify-between">
<div class="flex h-12 w-12 items-center justify-center rounded-xl bg-green-950 text-green-400">
{{ partial "icon.html" .icon }}
</div>
<span class="text-4xl font-black text-slate-100 group-hover:text-green-100 transition">{{ .number }}</span>
</div>
<h3 class="text-xl font-black text-slate-900 mb-3">{{ .title }}</h3>
<p class="text-slate-600 leading-relaxed text-sm">{{ .text }}</p>
</div>
{{ end }}
</div>
</div>
</section>
<!-- ─── PRINCIPES ─────────────────────────────────────────────── -->
<section class="bg-green-950 px-6 py-20 lg:px-8">
<div class="mx-auto max-w-5xl">
<div class="mb-12">
<h2 class="text-3xl font-black text-white mb-3">{{ $p.principles.title }}</h2>
</div>
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
{{ range $p.principles.items }}
<div class="rounded-xl border border-green-800 bg-green-900 p-6 hover:border-green-600 hover:bg-green-800 transition">
<div class="mb-4 flex h-10 w-10 items-center justify-center rounded-lg bg-green-700 text-green-200">
{{ partial "icon.html" .icon }}
</div>
<h3 class="text-base font-black text-white mb-2">{{ .title }}</h3>
<p class="text-green-300 text-sm leading-relaxed">{{ .text }}</p>
</div>
{{ end }}
</div>
</div>
</section>
<!-- ─── ENGAGEMENTS ───────────────────────────────────────────── -->
<section class="bg-white px-6 py-20 lg:px-8">
<div class="mx-auto max-w-5xl">
<div class="grid md:grid-cols-2 gap-12 items-start">
<div>
<h2 class="text-3xl font-black text-slate-900 mb-3">{{ $p.commitments.title }}</h2>
<p class="text-slate-500 mb-8">{{ $p.commitments.intro }}</p>
<ul class="space-y-4">
{{ range $p.commitments.items }}
<li class="flex items-start gap-3">
<span class="mt-1 flex-shrink-0 flex h-5 w-5 items-center justify-center rounded-full bg-green-500">
<svg class="h-3 w-3 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M5 13l4 4L19 7"></path></svg>
</span>
<span class="text-slate-700">{{ . }}</span>
</li>
{{ end }}
</ul>
</div>
<div class="sticky top-24">
<div class="rounded-2xl bg-green-950 p-10 text-center">
<svg class="mx-auto mb-4 h-10 w-10 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path></svg>
<p class="text-xl font-black text-white leading-snug">{{ $p.commitments.quote }}</p>
</div>
</div>
</div>
</div>
</section>
<!-- ─── VISION ────────────────────────────────────────────────── -->
<section class="bg-slate-50 px-6 py-20 lg:px-8">
<div class="mx-auto max-w-5xl">
<div class="mb-4">
<h2 class="text-3xl font-black text-slate-900 mb-3">{{ $p.vision.title }}</h2>
<p class="text-slate-500 max-w-2xl mb-2">{{ partial "brand-highlight.html" $p.vision.intro }}</p>
<p class="font-bold text-green-800">→ {{ $p.vision.objective }}</p>
</div>
<div class="mt-10 grid sm:grid-cols-2 gap-4">
{{ range $p.vision.items }}
<div class="flex gap-5 rounded-xl bg-white border border-slate-100 p-6 shadow-sm hover:shadow-md transition">
<div class="flex-shrink-0 flex h-12 w-12 items-center justify-center rounded-xl bg-slate-900 text-green-400">
{{ partial "icon.html" .icon }}
</div>
<div>
<h3 class="font-black text-slate-900 mb-1">{{ .title }}</h3>
<p class="text-slate-500 text-sm leading-relaxed">{{ .text }}</p>
</div>
</div>
{{ end }}
</div>
</div>
</section>
<!-- ─── GOUVERNANCE ───────────────────────────────────────────── -->
<section class="bg-white px-6 py-20 lg:px-8">
<div class="mx-auto max-w-5xl">
<div class="grid md:grid-cols-2 gap-12 items-center">
<div>
<h2 class="text-3xl font-black text-slate-900 mb-8">{{ $p.governance.title }}</h2>
<ul class="space-y-4">
{{ range $p.governance.items }}
<li class="flex items-start gap-3">
<span class="mt-0.5 flex-shrink-0 h-5 w-5 text-green-600">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>
</span>
<span class="text-slate-700">{{ . }}</span>
</li>
{{ end }}
</ul>
</div>
<div class="rounded-2xl border-l-4 border-green-500 bg-green-50 p-8">
<p class="text-xl font-black text-green-900 leading-snug italic">« {{ $p.governance.quote }} »</p>
</div>
</div>
</div>
</section>
<!-- ─── COALITION ─────────────────────────────────────────────── -->
<section class="bg-green-950 px-6 py-24 lg:px-8">
<div class="mx-auto max-w-3xl text-center">
<h2 class="text-4xl md:text-5xl font-black text-white mb-6">{{ $p.coalition.title }}</h2>
<p class="text-green-200 text-lg leading-relaxed mb-6">{{ $p.coalition.text }}</p>
<p class="text-white font-bold text-xl mb-10">{{ partial "brand-highlight.html" $p.coalition.statement }}</p>
<a href="{{ $joinPath | relLangURL }}" class="inline-flex items-center gap-2 rounded-xl bg-green-500 px-8 py-4 text-base font-black text-white hover:bg-green-400 transition">
{{ $p.coalition.cta }}
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3"></path></svg>
</a>
<p class="mt-10 text-sm text-green-500 italic">{{ $p.coalition.closing }}</p>
</div>
</section>
{{ end }}

View file

@ -0,0 +1,122 @@
{{ define "main" }}
{{ $p := .Params }}
<!-- HERO -->
<section class="relative overflow-hidden bg-green-950 px-6 py-28 lg:px-8">
<div class="absolute inset-0 opacity-20" style="background:radial-gradient(circle at 20% 20%,#166534 0%,transparent 40%),radial-gradient(circle at 80% 0%,#15803d 0%,transparent 35%)"></div>
<div class="relative mx-auto max-w-6xl">
<span class="inline-flex items-center gap-2 rounded-full border border-green-800 bg-green-900/40 px-4 py-1.5 text-xs font-bold uppercase tracking-widest text-green-300 mb-7">
<span class="h-1.5 w-1.5 rounded-full bg-green-400"></span>
TheAfriForge
</span>
<h1 class="max-w-4xl text-5xl md:text-7xl font-black leading-tight text-white">{{ .Title }}</h1>
{{ with $p.description }}
<p class="mt-6 max-w-3xl text-lg leading-relaxed text-green-200">{{ . }}</p>
{{ end }}
<div class="mt-10 grid gap-4 sm:grid-cols-3 max-w-3xl">
{{ range $p.hero.metrics }}
<div class="rounded-xl border border-green-900 bg-green-900/60 px-4 py-4">
<p class="text-2xl font-black text-green-300">{{ .value }}</p>
<p class="text-xs uppercase tracking-wider text-green-500">{{ .label }}</p>
</div>
{{ end }}
</div>
</div>
</section>
<!-- PROJECTS -->
<section class="bg-white px-6 py-20 lg:px-8">
<div class="mx-auto max-w-6xl">
<div class="mb-12 flex flex-wrap items-end justify-between gap-4">
<div>
<p class="text-xs font-bold uppercase tracking-[0.2em] text-green-700">{{ $p.portfolio.tag }}</p>
<h2 class="mt-3 text-3xl md:text-4xl font-black text-slate-900">{{ $p.portfolio.title }}</h2>
</div>
<p class="max-w-2xl text-slate-600">{{ $p.portfolio.intro }}</p>
</div>
<div class="grid gap-6 md:grid-cols-3">
{{ range $p.portfolio.items }}
{{ $status := lower .status }}
{{ $statusClass := "bg-slate-100 text-slate-700" }}
{{ if eq $status "production" }}
{{ $statusClass = "bg-green-100 text-green-800" }}
{{ else if eq $status "incubation" }}
{{ $statusClass = "bg-amber-100 text-amber-800" }}
{{ else if eq $status "research" }}
{{ $statusClass = "bg-sky-100 text-sky-800" }}
{{ else if eq $status "recherche" }}
{{ $statusClass = "bg-sky-100 text-sky-800" }}
{{ end }}
<article class="group rounded-2xl border border-slate-200 bg-slate-50 p-7 shadow-sm transition hover:-translate-y-1 hover:shadow-lg">
<div class="mb-5 flex items-center justify-between gap-3">
<span class="inline-flex items-center rounded-full px-3 py-1 text-xs font-bold uppercase tracking-wide {{ $statusClass }}">{{ .status }}</span>
<span class="inline-flex h-10 w-10 items-center justify-center rounded-xl bg-slate-900 text-green-300">
{{ partial "icon.html" .icon }}
</span>
</div>
<h3 class="text-2xl font-black text-slate-900">{{ .name }}</h3>
<p class="mt-4 text-slate-600 leading-relaxed">{{ .text }}</p>
<p class="mt-5 text-sm text-slate-500"><span class="font-bold text-slate-700">{{ $p.portfolio.impactLabel }}:</span> {{ .impact }}</p>
<p class="mt-2 text-sm text-slate-500"><span class="font-bold text-slate-700">{{ $p.portfolio.stackLabel }}:</span> {{ .stack }}</p>
<a href="{{ .link }}" class="mt-7 inline-flex items-center gap-2 text-sm font-bold text-green-800 transition group-hover:text-green-600">
{{ .cta }}
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3"></path></svg>
</a>
</article>
{{ end }}
</div>
</div>
</section>
<!-- ROADMAP -->
<section class="bg-slate-100 px-6 py-20 lg:px-8">
<div class="mx-auto max-w-6xl">
<h2 class="text-3xl md:text-4xl font-black text-slate-900">{{ $p.roadmap.title }}</h2>
<p class="mt-4 max-w-3xl text-slate-600">{{ $p.roadmap.intro }}</p>
<div class="mt-10 grid gap-5 md:grid-cols-3">
{{ range $p.roadmap.items }}
<div class="rounded-2xl border border-slate-200 bg-white p-6">
<p class="text-xs font-black uppercase tracking-[0.2em] text-green-700">{{ .period }}</p>
<h3 class="mt-3 text-xl font-black text-slate-900">{{ .title }}</h3>
<p class="mt-3 text-slate-600">{{ .text }}</p>
</div>
{{ end }}
</div>
</div>
</section>
<!-- SELECTION CRITERIA -->
<section class="bg-white px-6 py-20 lg:px-8">
<div class="mx-auto max-w-6xl">
<h2 class="text-3xl md:text-4xl font-black text-slate-900">{{ $p.selection.title }}</h2>
<p class="mt-4 max-w-3xl text-slate-600">{{ $p.selection.intro }}</p>
<div class="mt-10 grid gap-6 md:grid-cols-2 lg:grid-cols-4">
{{ range $p.selection.items }}
<div class="rounded-2xl border border-slate-200 bg-slate-50 p-6">
<span class="inline-flex h-10 w-10 items-center justify-center rounded-xl bg-green-900 text-green-300">
{{ partial "icon.html" .icon }}
</span>
<h3 class="mt-4 text-lg font-black text-slate-900">{{ .title }}</h3>
<p class="mt-2 text-sm leading-relaxed text-slate-600">{{ .text }}</p>
</div>
{{ end }}
</div>
</div>
</section>
<!-- CTA -->
<section class="bg-green-950 px-6 py-20 lg:px-8">
<div class="mx-auto max-w-4xl text-center">
<h2 class="text-4xl font-black text-white">{{ $p.call.title }}</h2>
<p class="mt-5 text-lg leading-relaxed text-green-200">{{ $p.call.text }}</p>
<a href="{{ $p.call.link | relLangURL }}" class="mt-10 inline-flex items-center justify-center rounded-xl bg-green-500 px-8 py-4 text-sm font-black uppercase tracking-wide text-white transition hover:bg-green-400">
{{ $p.call.cta }}
</a>
</div>
</section>
{{ end }}

View file

@ -0,0 +1,31 @@
{{ define "main" }}
<!-- ─── HERO ───────────────────────────────────── -->
<section class="bg-green-950 px-6 py-24 lg:px-8">
<div class="mx-auto max-w-4xl">
<p class="text-xs font-bold uppercase tracking-widest text-green-400 mb-5">TheAfriForge — {{ .Site.Language.LanguageName }}</p>
<h1 class="text-5xl md:text-6xl font-black leading-tight text-white mb-6">{{ .Title }}</h1>
{{ with .Description }}
<p class="text-lg text-green-200 max-w-2xl leading-relaxed">{{ . }}</p>
{{ end }}
</div>
</section>
<!-- ─── CONTENT ─────────────────────────────────── -->
<section class="bg-white px-6 py-16 lg:px-8">
<div class="mx-auto max-w-4xl">
<div class="prose prose-slate prose-lg max-w-none
prose-p:text-slate-600 prose-p:leading-relaxed
prose-li:text-slate-600 prose-li:leading-relaxed
prose-strong:text-slate-900 prose-strong:font-bold
prose-a:text-green-700 prose-a:font-medium
prose-blockquote:not-italic prose-blockquote:border-l-4 prose-blockquote:border-green-500
prose-blockquote:bg-green-50 prose-blockquote:rounded-r-xl prose-blockquote:px-6 prose-blockquote:py-4
prose-blockquote:text-green-900 prose-blockquote:font-medium
prose-hr:border-slate-200">
{{ partial "brand-highlight.html" (printf "%s" .Content) }}
</div>
</div>
</section>
{{ end }}

118
layouts/index.html Normal file
View file

@ -0,0 +1,118 @@
{{ define "main" }}
{{ $manifestoPath := cond (eq .Site.Language.Lang "fr") "manifeste" "manifesto" }}
{{ $projectsPath := cond (eq .Site.Language.Lang "fr") "projets" "projects" }}
{{ $joinPath := cond (eq .Site.Language.Lang "fr") "devenir-membre" "become-member" }}
<!-- ─── HERO ───────────────────────────────────── -->
<section class="px-6 py-28 lg:px-8 lg:py-36 bg-green-950">
<div class="mx-auto max-w-4xl">
<p class="text-sm font-semibold uppercase tracking-widest text-green-400 mb-6">{{ .Params.hero.kicker }}</p>
<h1 class="text-5xl md:text-6xl lg:text-7xl font-black leading-tight text-white mb-8">
{{ .Params.hero.titleLine1 }}<br>{{ .Params.hero.titleLine2 }}<br><span class="text-green-400">{{ .Params.hero.titleLine3 }}</span>
</h1>
<p class="text-xl text-green-200 max-w-xl mb-10 leading-relaxed">
{{ partial "brand-highlight.html" .Params.hero.subtitle }}
</p>
<div class="flex flex-wrap gap-4">
<a href="{{ $manifestoPath | relLangURL }}" class="inline-flex items-center gap-2 rounded-lg bg-green-500 px-7 py-3.5 text-sm font-bold text-white hover:bg-green-400 transition">
{{ .Params.hero.ctaManifesto }}
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3"></path></svg>
</a>
<a href="{{ $projectsPath | relLangURL }}" class="inline-flex items-center gap-2 rounded-lg border-2 border-green-700 px-7 py-3.5 text-sm font-bold text-green-200 hover:border-green-500 hover:text-white transition">
{{ .Params.hero.ctaProjects }}
</a>
</div>
</div>
</section>
<!-- ─── DIVIDER ───────────────────────────────── -->
<div class="border-t border-slate-100"></div>
<!-- ─── STATS ─────────────────────────────────── -->
<section class="px-6 py-16 lg:px-8 bg-slate-100">
<div class="mx-auto max-w-5xl">
<h2 class="text-3xl lg:text-4xl font-black text-slate-900 mb-3 text-center">{{ .Params.whyJoin.title }}</h2>
<p class="text-center text-slate-600 mb-12 max-w-2xl mx-auto">{{ partial "brand-highlight.html" .Params.whyJoin.intro }}</p>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
{{ range .Params.whyJoin.cards }}
<div class="rounded-xl border border-slate-100 bg-white p-8 shadow-md hover:shadow-lg transition">
<div class="text-5xl mb-4">{{ .icon }}</div>
<h3 class="text-xl font-bold text-slate-900 mb-2">{{ .title }}</h3>
<p class="text-slate-600">{{ partial "brand-highlight.html" .text }}</p>
</div>
{{ end }}
</div>
</div>
</section>
<!-- ─── DIVIDER ───────────────────────────────── -->
<div class="border-t border-slate-100"></div>
<!-- ─── PILIERS ───────────────────────────────── -->
<section class="px-6 py-24 lg:px-8 bg-white">
<div class="mx-auto max-w-5xl">
<h2 class="text-3xl md:text-4xl font-black text-slate-900 mb-12 text-center">{{ .Params.pillars.title }}</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
{{ range $index, $item := .Params.pillars.items }}
{{ $bgColorClass := cond (eq $index 0) "bg-green-50" (cond (eq $index 1) "bg-green-100" "bg-green-200") }}
{{ $borderColorClass := cond (eq $index 0) "border-green-600" (cond (eq $index 1) "border-green-700" "border-green-800") }}
{{ $textColorClass := cond (eq $index 0) "text-green-600" (cond (eq $index 1) "text-green-700" "text-green-800") }}
<div class="rounded-lg p-8 {{ $bgColorClass }} border-l-4 {{ $borderColorClass }} shadow-sm hover:shadow-md transition">
<div class="text-4xl font-black {{ $textColorClass }} mb-4">{{ add $index 1 }}</div>
<h3 class="text-xl font-bold text-slate-900 mb-3">{{ $item.title }}</h3>
<p class="text-slate-600 leading-relaxed">{{ $item.text }}</p>
</div>
{{ end }}
</div>
</div>
</section>
<!-- ─── PROJETS ───────────────────────────────── -->
<section id="projects" class="px-6 py-24 lg:px-8 bg-green-900">
<div class="mx-auto max-w-6xl">
<h2 class="text-3xl md:text-4xl font-black text-white mb-4 text-center">{{ .Params.projects.title }}</h2>
<p class="text-green-100 mb-14 max-w-3xl mx-auto text-center">{{ partial "brand-highlight.html" .Params.projects.intro }}</p>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
{{ range .Params.projects.items }}
{{ $statusColor := cond (eq .status "Production") "bg-green-200 text-green-900" (cond (eq .status "Incubation") "bg-amber-200 text-amber-900" "bg-slate-200 text-slate-800") }}
<div class="rounded-2xl border border-green-700 bg-green-800 p-8 min-h-[320px] flex flex-col justify-between shadow-md hover:shadow-lg hover:bg-green-700 transition">
<div>
<span class="inline-flex rounded-full {{ $statusColor }} px-3 py-1 text-xs font-black uppercase tracking-wide mb-5">{{ .status }}</span>
<h3 class="text-2xl font-black text-white mb-4">{{ .name }}</h3>
<p class="text-green-100 leading-relaxed">{{ partial "brand-highlight.html" .text }}</p>
</div>
<a href="#" class="mt-8 inline-flex items-center justify-center rounded-lg bg-white px-5 py-3 text-sm font-black text-green-900 hover:bg-green-100 transition">
{{ .cta }} →
</a>
</div>
{{ end }}
</div>
</div>
</section>
<!-- ─── MEMBRES ───────────────────────────────── -->
<section id="membres" class="px-6 py-24 lg:px-8 bg-slate-50">
<div class="mx-auto max-w-4xl">
<h2 class="text-3xl md:text-4xl font-black text-slate-900 mb-4">{{ .Params.members.title }}</h2>
<p class="text-slate-600 mb-14 max-w-xl">{{ partial "brand-highlight.html" .Params.members.subtitle }}</p>
<div class="grid md:grid-cols-2 gap-8">
{{ range .Params.members.categories }}
<div class="rounded-xl bg-white border border-slate-200 p-8 shadow-sm hover:shadow-md transition">
<p class="text-xs font-bold uppercase tracking-widest text-green-700 mb-3">{{ .label }}</p>
<h3 class="text-2xl font-bold text-slate-900 mb-4">{{ .title }}</h3>
<p class="text-slate-600 mb-6">{{ partial "brand-highlight.html" .text }}</p>
<a href="{{ (or .link $joinPath) | relLangURL }}" class="inline-flex items-center gap-2 font-bold text-green-800 hover:text-green-900 transition">
{{ .cta }}
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3"></path></svg>
</a>
</div>
{{ end }}
</div>
</div>
</section>
{{ end }}

View file

@ -0,0 +1,3 @@
{{- $text := . -}}
{{- $highlight := "<span class=\"rounded bg-green-200 px-1 font-semibold text-green-900\">TheAfriForge</span>" -}}
{{- replace $text "TheAfriForge" $highlight | safeHTML -}}

View file

@ -0,0 +1,34 @@
{{ $name := . }}
{{ if eq $name "server" }}
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2"></path></svg>
{{ else if eq $name "shield" }}
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"></path></svg>
{{ else if eq $name "globe" }}
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
{{ else if eq $name "bolt" }}
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path></svg>
{{ else if eq $name "network" }}
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"></path></svg>
{{ else if eq $name "code" }}
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"></path></svg>
{{ else if eq $name "balance" }}
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 6l3 1m0 0l-3 9a5.002 5.002 0 006.001 0M6 7l3 9M6 7l6-2m6 2l3-1m-3 1l-3 9a5.002 5.002 0 006.001 0M18 7l3 9m-3-9l-6-2m0-2v2m0 16V5m0 16H9m3 0h3"></path></svg>
{{ else if eq $name "flag" }}
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 21v-4m0 0V5a2 2 0 012-2h6.5l1 1H21l-3 6 3 6h-8.5l-1-1H5a2 2 0 00-2 2zm9-13.5V9"></path></svg>
{{ else if eq $name "unlock" }}
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 11V7a4 4 0 118 0m-4 8v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2z"></path></svg>
{{ else if eq $name "lock" }}
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path></svg>
{{ else if eq $name "zap" }}
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path></svg>
{{ else if eq $name "leaf" }}
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z"></path></svg>
{{ else if eq $name "academic" }}
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 14l9-5-9-5-9 5 9 5zm0 0l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14zm-4 6v-7.5l4-2.222"></path></svg>
{{ else if eq $name "git" }}
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg>
{{ else if eq $name "package" }}
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"></path></svg>
{{ else if eq $name "chart" }}
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path></svg>
{{ end }}

View file

@ -0,0 +1,16 @@
<footer class="border-t border-green-900 bg-green-950">
{{ $legalPath := cond (eq .Site.Language.Lang "fr") "mentions-legales" "legal-notice" }}
{{ $privacyPath := cond (eq .Site.Language.Lang "fr") "politique-confidentialite" "privacy-policy" }}
{{ $governancePath := cond (eq .Site.Language.Lang "fr") "gouvernance" "governance" }}
<div class="mx-auto grid max-w-7xl gap-8 px-6 py-10 text-sm text-green-200 md:grid-cols-2 lg:px-8">
<div>
<p class="text-base font-bold text-white">TheAfriForge</p>
<p class="mt-2 max-w-xl">{{ i18n "footerTagline" }}</p>
</div>
<div class="grid gap-2 md:justify-items-end">
<a href="{{ $legalPath | relLangURL }}" class="transition hover:text-white">{{ i18n "footerLegal" }}</a>
<a href="{{ $privacyPath | relLangURL }}" class="transition hover:text-white">{{ i18n "footerPrivacy" }}</a>
<a href="{{ $governancePath | relLangURL }}" class="transition hover:text-white">{{ i18n "footerGovernance" }}</a>
</div>
</div>
</footer>

View file

@ -0,0 +1,106 @@
<header class="sticky top-0 z-50 border-b border-slate-200 bg-white">
{{ $joinPath := cond (eq .Site.Language.Lang "fr") "devenir-membre" "become-member" }}
{{ $menuLabel := cond (eq .Site.Language.Lang "fr") "Menu" "Menu" }}
<div class="mx-auto max-w-6xl px-6 py-4 lg:px-8">
<div class="flex items-center justify-between">
<!-- Logo -->
<a href="{{ "/" | relLangURL }}" class="flex items-center gap-2 font-bold text-lg text-slate-900">
<span class="inline-flex h-8 w-8 items-center justify-center rounded-md bg-green-800 text-white text-sm font-black">AF</span>
<span>TheAfriForge</span>
</a>
<!-- Navigation -->
<nav class="hidden md:flex items-center gap-8 text-sm font-medium">
{{ range .Site.Menus.main }}
<a href="{{ .URL }}" class="text-slate-600 hover:text-slate-900 transition">{{ .Name }}</a>
{{ end }}
</nav>
<!-- Right: Language + CTA + Mobile menu -->
<div class="flex items-center gap-3">
<!-- Language -->
<div class="relative hidden md:block group">
<button class="flex items-center gap-1 rounded-lg border border-slate-200 px-3 py-2 text-sm font-medium text-slate-600 hover:border-slate-300 hover:text-slate-900 transition">
{{ .Site.Language.Lang | upper }}
<svg class="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path></svg>
</button>
<div class="absolute right-0 top-full mt-1 w-40 rounded-lg border border-slate-200 bg-white shadow-lg invisible group-hover:visible opacity-0 group-hover:opacity-100 transition-all">
{{ range .Site.Languages }}
{{ $isCurrent := eq $.Site.Language.Lang .Lang }}
{{ $targetSite := index (where $.Sites "Language.Lang" .Lang) 0 }}
{{ $langURL := "/" }}
{{ if and $targetSite $.Page (not $.IsHome) }}
{{ $translation := index (where $.Page.AllTranslations "Language.Lang" .Lang) 0 }}
{{ if $translation }}
{{ $langURL = $translation.RelPermalink }}
{{ else }}
{{ $langURL = $targetSite.Home.RelPermalink }}
{{ end }}
{{ else if $targetSite }}
{{ $langURL = $targetSite.Home.RelPermalink }}
{{ end }}
{{ if $isCurrent }}
<div class="px-4 py-2 text-sm font-semibold text-green-800 bg-green-50">✓ {{ .LanguageName }}</div>
{{ else }}
<a href="{{ $langURL }}" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-50 transition">{{ .LanguageName }}</a>
{{ end }}
{{ end }}
</div>
</div>
<!-- CTA -->
<a href='{{ $joinPath | relLangURL }}' class="hidden sm:inline-flex rounded-lg bg-green-800 px-5 py-2 text-sm font-bold text-white hover:bg-green-900 transition">
{{ i18n "headerCtaMembers" }}
</a>
<details class="relative md:hidden">
<summary class="flex list-none cursor-pointer items-center gap-1 rounded-lg border border-slate-200 px-3 py-2 text-sm font-medium text-slate-700 hover:border-slate-300 hover:text-slate-900 transition">
{{ .Site.Language.Lang | upper }}
<svg class="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path></svg>
</summary>
<div class="absolute right-0 top-full mt-1 w-44 rounded-lg border border-slate-200 bg-white shadow-lg">
{{ range .Site.Languages }}
{{ $isCurrent := eq $.Site.Language.Lang .Lang }}
{{ $targetSite := index (where $.Sites "Language.Lang" .Lang) 0 }}
{{ $langURL := "/" }}
{{ if and $targetSite $.Page (not $.IsHome) }}
{{ $translation := index (where $.Page.AllTranslations "Language.Lang" .Lang) 0 }}
{{ if $translation }}
{{ $langURL = $translation.RelPermalink }}
{{ else }}
{{ $langURL = $targetSite.Home.RelPermalink }}
{{ end }}
{{ else if $targetSite }}
{{ $langURL = $targetSite.Home.RelPermalink }}
{{ end }}
{{ if $isCurrent }}
<div class="px-4 py-2 text-sm font-semibold text-green-800 bg-green-50">✓ {{ .LanguageName }}</div>
{{ else }}
<a href="{{ $langURL }}" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-50 transition">{{ .LanguageName }}</a>
{{ end }}
{{ end }}
</div>
</details>
<!-- Mobile menu -->
<details class="relative md:hidden">
<summary class="flex list-none cursor-pointer items-center gap-2 rounded-lg border border-slate-200 px-3 py-2 text-sm font-medium text-slate-700 hover:border-slate-300 hover:text-slate-900">
<span>{{ $menuLabel }}</span>
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path></svg>
</summary>
<div class="absolute right-0 mt-2 w-[84vw] max-w-sm rounded-2xl border border-slate-200 bg-white p-4 shadow-xl">
<nav class="grid gap-2 text-sm font-medium">
{{ range .Site.Menus.main }}
<a href="{{ .URL }}" class="rounded-lg px-3 py-2 text-slate-700 hover:bg-slate-100 hover:text-slate-900 transition">{{ .Name }}</a>
{{ end }}
</nav>
<a href='{{ $joinPath | relLangURL }}' class="mt-4 inline-flex w-full items-center justify-center rounded-lg bg-green-800 px-5 py-2.5 text-sm font-bold text-white hover:bg-green-900 transition">
{{ i18n "headerCtaMembers" }}
</a>
</div>
</details>
</div>
</div>
</div>
</header>

12
scripts/dev.sh Normal file
View file

@ -0,0 +1,12 @@
#!/bin/sh
# Load .env variables and run hugo with optional args
# Usage: ./scripts/dev.sh [hugo args...]
# Example: ./scripts/dev.sh serve --port 1313
if [ -f .env ]; then
set -a
. ./.env
set +a
fi
hugo --config ./hugo.toml "$@"