diff --git a/_data/eoc_dashboard.yml b/_data/eoc_dashboard.yml
new file mode 100644
index 0000000..4817a00
--- /dev/null
+++ b/_data/eoc_dashboard.yml
@@ -0,0 +1,78 @@
+# Equal Opportunity Dashboard data.
+#
+# The `meta`, `members` and `mc` keys below are AUTO-GENERATED from
+# CA24136-WG-members.csv by scripts/extract-eoc-stats.rb — do not edit
+# them by hand; re-run the script to refresh. Aggregate counts only;
+# no personal data is stored here.
+
+meta:
+ generated: "2026-06-29"
+ source: "CA24136-WG-members.csv"
+
+members:
+ title: "Action Members"
+ subtitle: "Composition of the Action"
+ headline: "191"
+ headline_label: "members across 38 countries"
+ stats:
+ - label: "Women"
+ value: "24.6%"
+ fraction: [47, 191]
+ - label: "ITC"
+ value: "54.5%"
+ fraction: [104, 191]
+ - label: "Young Researchers"
+ value: "55.0%"
+ fraction: [105, 191]
+ note: "Men: 142 (74.3%) · Other / prefer not to say: 2 (1.0%)"
+
+mc:
+ title: "Management Committee"
+ subtitle: "Governance representation"
+ headline: "56"
+ headline_label: "MC members across 33 countries"
+ stats:
+ - label: "Women"
+ value: "30.4%"
+ fraction: [17, 56]
+ - label: "ITC"
+ value: "64.3%"
+ fraction: [36, 56]
+ - label: "Young Researchers"
+ value: "28.6%"
+ fraction: [16, 56]
+ note: "Men: 39 (69.6%)"
+
+# === MANUAL SECTION (edited by hand — preserved when this script re-runs) ===
+
+# Each entry below renders as its own summary box on the dashboard.
+# Add as many entries as you like; provide `fraction: [count, total]` on a stat
+# to draw a bar. These figures are gathered by hand (not available in the CSV).
+
+conference:
+ - title: "Inaugural Conference: NextGen Synergy"
+ subtitle: "Inclusiveness in scientific visibility"
+ headline: "90+"
+ headline_label: "participants across 21 countries"
+ stats:
+ - label: "ITC"
+ value: "17/21"
+ fraction: [17, 21]
+ - label: "Women keynote speakers"
+ value: "1/8"
+ fraction: [1, 8]
+ note: "Hosted in Prague · local chair: Tatiana Valentine Guy · ~50% women as session chairs"
+
+stsm:
+ - title: "STSM Participation"
+ subtitle: "Mobility and career development"
+ headline: "5"
+ headline_label: "STSM applications (1 call)"
+ stats:
+ - label: "ITC involved"
+ value: "4/6"
+ fraction: [4, 6]
+ - label: "Women applicants"
+ value: "2/5"
+ fraction: [2, 5]
+ note: "2 ITC ↔ ITC missions"
diff --git a/_includes/desktop-menu.html b/_includes/desktop-menu.html
index 4e3cae3..288c00b 100644
--- a/_includes/desktop-menu.html
+++ b/_includes/desktop-menu.html
@@ -27,6 +27,7 @@
Action Leaders
Core Group
Management Committee
+ Equal Opportunity
@@ -68,12 +69,13 @@
Dissemination
-
+
Organisation
diff --git a/_includes/eoc-box.html b/_includes/eoc-box.html
new file mode 100644
index 0000000..da01ed9
--- /dev/null
+++ b/_includes/eoc-box.html
@@ -0,0 +1,37 @@
+{% comment %}
+ Renders one Equal Opportunity summary box.
+ Parameter: box - a hash with title, subtitle, headline, headline_label,
+ stats (list of {label, value, fraction:[count,total]}), note.
+{% endcomment %}
+{% assign box = include.box %}
+
+
{{ box.title }}
+ {% if box.subtitle %}
{{ box.subtitle }}
{% endif %}
+ {% if box.headline %}
+
+ {{ box.headline }}
+ {% if box.headline_label %}{{ box.headline_label }}{% endif %}
+
+ {% endif %}
+ {% if box.stats %}
+
+ {% endif %}
+ {% if box.note %}
{{ box.note }}
{% endif %}
+
diff --git a/_sass/eoc.scss b/_sass/eoc.scss
new file mode 100644
index 0000000..771b07b
--- /dev/null
+++ b/_sass/eoc.scss
@@ -0,0 +1,85 @@
+@use "variables" as *;
+
+.eoc-box {
+ text-align: left;
+
+ &__subtitle {
+ margin: -0.25rem 0 0.75rem;
+ color: $altBlue;
+ font-size: 0.9rem;
+ }
+
+ &__headline {
+ margin: 0 0 1rem;
+ }
+
+ &__number {
+ font-size: 2.4rem;
+ font-weight: bold;
+ color: $mainBlue;
+ line-height: 1;
+ }
+
+ &__headline-label {
+ color: $mainBlue;
+ font-size: 0.9rem;
+ margin-left: 0.4rem;
+ }
+
+ &__stats {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ }
+
+ &__stat {
+ margin-bottom: 0.75rem;
+ }
+
+ &__stat-head {
+ display: flex;
+ justify-content: space-between;
+ align-items: baseline;
+ margin-bottom: 0.2rem;
+ }
+
+ &__stat-label {
+ color: $mainBlue;
+ font-size: 0.9rem;
+ }
+
+ &__stat-value {
+ color: $altBlue;
+ font-weight: bold;
+ font-size: 0.9rem;
+ }
+
+ &__bar {
+ display: block;
+ width: 100%;
+ height: 0.5rem;
+ background: $altGray;
+ border-radius: 999px;
+ overflow: hidden;
+ }
+
+ &__bar-fill {
+ display: block;
+ height: 100%;
+ background: $mainGreen;
+ border-radius: 999px;
+ }
+
+ &__note {
+ margin: 1rem 0 0;
+ color: $mainGray;
+ font-size: 0.8rem;
+ }
+}
+
+.eoc-source {
+ text-align: center;
+ color: $mainGray;
+ font-size: 0.85rem;
+ margin-top: 2rem;
+}
diff --git a/assets/css/main.scss b/assets/css/main.scss
index abb96f7..6b51964 100644
--- a/assets/css/main.scss
+++ b/assets/css/main.scss
@@ -11,4 +11,5 @@
@use "news" as *;
@use "responsive" as *;
@use "dissemination" as *;
-@use "jobs" as *;
\ No newline at end of file
+@use "jobs" as *;
+@use "eoc" as *;
\ No newline at end of file
diff --git a/extract-eoc-stats.sh b/extract-eoc-stats.sh
new file mode 100755
index 0000000..f8427b1
--- /dev/null
+++ b/extract-eoc-stats.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+# Extracts aggregate Equal Opportunity statistics from the WG members CSV into
+# _data/eoc_dashboard.yml, via the Docker container (so no local Ruby is needed).
+#
+# Usage: ./extract-eoc-stats.sh [path/to/members.csv]
+
+CSV="${1:-CA24136-WG-members.csv}"
+
+if [ ! -f "$CSV" ]; then
+ echo "Error: CSV file '$CSV' not found"
+ echo "Usage: ./extract-eoc-stats.sh [path/to/members.csv]"
+ exit 1
+fi
+
+docker compose run --rm intercoml-homepage ruby scripts/extract-eoc-stats.rb "$CSV"
diff --git a/pages/equal-opportunity.html b/pages/equal-opportunity.html
new file mode 100644
index 0000000..1ec51e9
--- /dev/null
+++ b/pages/equal-opportunity.html
@@ -0,0 +1,36 @@
+---
+title: Equal Opportunity
+permalink: /:basename/
+---
+
+{% assign d = site.data.eoc_dashboard %}
+
+
+
Equal Opportunity Dashboard
+
+
+ {{ site.actionAbbreviation }} is committed to inclusiveness and balanced participation across all
+ Action activities. This dashboard summarises the involvement of women, Young Researchers (YR) and
+ researchers from Inclusiveness Target Countries (ITC) in the Action.
+
+
+
Composition of the Action
+
+ {% if d.members %}{% include eoc-box.html box=d.members %}{% endif %}
+ {% if d.mc %}{% include eoc-box.html box=d.mc %}{% endif %}
+
+
+ {% if d.conference or d.stsm %}
+
Activities
+
+ {% for c in d.conference %}{% include eoc-box.html box=c %}{% endfor %}
+ {% for s in d.stsm %}{% include eoc-box.html box=s %}{% endfor %}
+
+ {% endif %}
+
+ {% if d.meta.generated %}
+
+ Figures generated from membership records on {{ d.meta.generated }}.
+
+ {% endif %}
+
diff --git a/scripts/extract-eoc-stats.rb b/scripts/extract-eoc-stats.rb
new file mode 100755
index 0000000..30ad4fd
--- /dev/null
+++ b/scripts/extract-eoc-stats.rb
@@ -0,0 +1,151 @@
+#!/usr/bin/env ruby
+#
+# Extracts aggregate Equal Opportunity statistics from the WG members CSV and
+# writes them to _data/eoc_dashboard.yml.
+#
+# Usage: ruby scripts/extract-eoc-stats.rb [path/to/members.csv]
+# (defaults to CA24136-WG-members.csv in the project root)
+#
+# Only the `meta`, `members` and `mc` keys at the top of the output file are
+# owned by this script. Everything below the MANUAL SECTION marker (the
+# hand-maintained conference/STSM entries) is preserved verbatim on re-run.
+#
+# Privacy: only aggregate counts/percentages are written — no names, emails or
+# any per-person data leave the CSV. The CSV itself is not committed (.gitignore).
+
+require 'csv'
+require 'date'
+
+CSV_PATH = ARGV[0] || 'CA24136-WG-members.csv'
+OUT_PATH = '_data/eoc_dashboard.yml'
+MARKER = '# === MANUAL SECTION (edited by hand — preserved when this script re-runs) ==='
+
+unless File.exist?(CSV_PATH)
+ STDERR.puts "Error: CSV file '#{CSV_PATH}' not found"
+ STDERR.puts "Usage: ruby scripts/extract-eoc-stats.rb [path/to/members.csv]"
+ exit 1
+end
+
+def parse_country(country_str)
+ # "Serbia (RS)" -> "Serbia"
+ country_str.to_s.sub(/\s*\([^)]*\)\s*$/, '').strip
+end
+
+def compute_box(rows, title, subtitle, members_word)
+ n = rows.size
+ women = rows.count { |r| r['Gender'].to_s.strip == 'Female' }
+ men = rows.count { |r| r['Gender'].to_s.strip == 'Male' }
+ other = n - women - men
+ itc = rows.count { |r| r['ITC'].to_s.strip == 'y' }
+ yr = rows.count { |r| r['Young Researcher'].to_s.strip == 'y' }
+ countries = rows.map { |r| parse_country(r['Country']) }.reject(&:empty?).uniq.size
+
+ pct = ->(x) { n.zero? ? 0.0 : (100.0 * x / n).round(1) }
+
+ note = "Men: #{men} (#{pct.call(men)}%)"
+ note += " · Other / prefer not to say: #{other} (#{pct.call(other)}%)" if other.positive?
+
+ {
+ title: title,
+ subtitle: subtitle,
+ headline: n.to_s,
+ headline_label: "#{members_word} across #{countries} countries",
+ stats: [
+ ['Women', "#{pct.call(women)}%", women, n],
+ ['ITC', "#{pct.call(itc)}%", itc, n],
+ ['Young Researchers', "#{pct.call(yr)}%", yr, n],
+ ],
+ note: note,
+ }
+end
+
+def box_to_yaml(key, box)
+ lines = ["#{key}:"]
+ lines << %Q( title: "#{box[:title]}")
+ lines << %Q( subtitle: "#{box[:subtitle]}")
+ lines << %Q( headline: "#{box[:headline]}")
+ lines << %Q( headline_label: "#{box[:headline_label]}")
+ lines << ' stats:'
+ box[:stats].each do |label, value, num, den|
+ lines << %Q( - label: "#{label}")
+ lines << %Q( value: "#{value}")
+ lines << " fraction: [#{num}, #{den}]"
+ end
+ lines << %Q( note: "#{box[:note]}")
+ lines.join("\n")
+end
+
+SEED_MANUAL = <<~'YAML'
+
+ # Each entry below renders as its own summary box on the dashboard.
+ # Add as many entries as you like; provide `fraction: [count, total]` on a stat
+ # to draw a bar. These figures are gathered by hand (not available in the CSV).
+
+ conference:
+ - title: "Inaugural Conference: NextGen Synergy"
+ subtitle: "Inclusiveness in scientific visibility"
+ headline: "90+"
+ headline_label: "participants across 21 countries"
+ stats:
+ - label: "ITC countries"
+ value: "17/21"
+ fraction: [17, 21]
+ - label: "Women keynote speakers"
+ value: "1/8"
+ fraction: [1, 8]
+ note: "Hosted in Prague · local chair: Tatiana Valentine Guy · ~5 women session chairs"
+
+ stsm:
+ - title: "STSM Participation"
+ subtitle: "Mobility and career development"
+ headline: "5"
+ headline_label: "STSM applications (1 call)"
+ stats:
+ - label: "ITC countries involved"
+ value: "4/6"
+ fraction: [4, 6]
+ - label: "Women applicants"
+ value: "2/5"
+ fraction: [2, 5]
+ note: "2 ITC ↔ ITC missions · broader participation to encourage"
+YAML
+
+rows = CSV.read(CSV_PATH, headers: true, col_sep: ';', encoding: 'bom|utf-8')
+
+members = rows.select { |r| r['Application Status'].to_s.strip.downcase == 'approved' }
+mc = rows.select { |r| r['MC Membership Status'].to_s.strip == 'Member' }
+
+members_box = compute_box(members, 'Action Members', 'Composition of the Action', 'members')
+mc_box = compute_box(mc, 'Management Committee', 'Governance representation', 'MC members')
+
+auto = []
+auto << '# Equal Opportunity Dashboard data.'
+auto << '#'
+auto << '# The `meta`, `members` and `mc` keys below are AUTO-GENERATED from'
+auto << '# CA24136-WG-members.csv by scripts/extract-eoc-stats.rb — do not edit'
+auto << '# them by hand; re-run the script to refresh. Aggregate counts only;'
+auto << '# no personal data is stored here.'
+auto << ''
+auto << 'meta:'
+auto << %Q( generated: "#{Date.today.iso8601}")
+auto << ' source: "CA24136-WG-members.csv"'
+auto << ''
+auto << box_to_yaml('members', members_box)
+auto << ''
+auto << box_to_yaml('mc', mc_box)
+auto << ''
+auto_text = auto.join("\n") + "\n"
+
+# Preserve the manual section if the file already exists.
+manual =
+ if File.exist?(OUT_PATH) && File.read(OUT_PATH).include?(MARKER)
+ File.read(OUT_PATH).split(MARKER, 2)[1]
+ else
+ "\n" + SEED_MANUAL
+ end
+
+File.write(OUT_PATH, auto_text + MARKER + manual)
+
+puts "Wrote #{OUT_PATH}"
+puts " Action Members: #{members_box[:headline]} (#{members_box[:headline_label]})"
+puts " Management Committee: #{mc_box[:headline]} (#{mc_box[:headline_label]})"