diff --git a/TODO.md b/TODO.md
index 76ca93f..f6cfd4a 100644
--- a/TODO.md
+++ b/TODO.md
@@ -539,9 +539,11 @@ Goal: rename the tables and column to the canonical M2M pattern (`tags`, `thesis
site blurb for `index.php`, synopsis excerpt for `tfe.php`, page content intro for
`apropos.php`/`licence.php`. Necessary for search indexing and link preview cards.
-- [ ] **No Open Graph tags** - `tfe.php` is the ideal candidate for `og:title`, `og:description`
- (synopsis), `og:image` (banner or cover path through `/media.php`), `og:type=article`.
- Without them, sharing a thesis link on social media or messaging apps shows a blank preview.
+- [x] **No Open Graph tags** - `tfe.php` now emits `og:type=article`, `og:title`, `og:description`,
+ `og:url`, `og:image` (banner → first image file → none), `og:image:alt`, `og:site_name`,
+ `article:author`, `article:published_time`, plus `twitter:card`/`twitter:title`/`twitter:description`/
+ `twitter:image`. All other public pages (`index`, `search`, `apropos`, `licence`) emit basic
+ `og:type=website` tags. OG rendering is centralised in `templates/public/head.php` via `$ogTags` array.
### H - Minor / low-hanging fruit
diff --git a/public/apropos.php b/public/apropos.php
index fca62a0..5c05f7b 100644
--- a/public/apropos.php
+++ b/public/apropos.php
@@ -26,6 +26,13 @@ $pd->setSafeMode(true);
$aboutHtml = $pd->text($rawContent);
$pageTitle = 'À Propos – Posterg';
$metaDescription = 'À propos de Posterg, le répertoire des mémoires de fin d\'études de l\'erg – École de Recherches Graphiques de Bruxelles.';
+$ogTags = [
+ 'type' => 'website',
+ 'title' => $pageTitle,
+ 'description' => $metaDescription,
+ 'url' => 'https://posterg.erg.be/apropos.php',
+ 'site_name' => 'Posterg – ERG',
+];
$extraCss = ['assets/apropos.css'];
?>
diff --git a/public/index.php b/public/index.php
index 1453dd4..3c32a4b 100644
--- a/public/index.php
+++ b/public/index.php
@@ -52,6 +52,13 @@ try {
$currentNav = '';
$pageTitle = 'Posterg – Mémoires de l\'ERG';
$metaDescription = 'Posterg répertorie et valorise les mémoires de fin d\'études (TFE) de l\'erg – École de Recherches Graphiques de Bruxelles.';
+$ogTags = [
+ 'type' => 'website',
+ 'title' => $pageTitle,
+ 'description' => $metaDescription,
+ 'url' => 'https://posterg.erg.be/',
+ 'site_name' => 'Posterg – ERG',
+];
$extraCss = ['assets/main.css'];
?>
diff --git a/public/licence.php b/public/licence.php
index 7d32175..4d35ef7 100644
--- a/public/licence.php
+++ b/public/licence.php
@@ -22,6 +22,13 @@ $html = $pd->text($content);
$pageTitle = $licencePageTitle . ' – Posterg';
$metaDescription = 'Informations sur les licences d\'utilisation des mémoires publiés sur Posterg, le répertoire des TFE de l\'erg.';
+$ogTags = [
+ 'type' => 'website',
+ 'title' => $pageTitle,
+ 'description' => $metaDescription,
+ 'url' => 'https://posterg.erg.be/licence.php',
+ 'site_name' => 'Posterg – ERG',
+];
$extraCss = ['assets/apropos.css'];
?>
diff --git a/public/search.php b/public/search.php
index 33e9c11..8428913 100644
--- a/public/search.php
+++ b/public/search.php
@@ -63,6 +63,13 @@ $currentNav = 'repertoire';
$searchBarValue = $_GET['query'] ?? '';
$pageTitle = 'Répertoire – Posterg';
$metaDescription = 'Parcourez le répertoire des mémoires de fin d\'études (TFE) de l\'erg – École de Recherches Graphiques de Bruxelles. Recherche par année, orientation, atelier et mots-clés.';
+$ogTags = [
+ 'type' => 'website',
+ 'title' => $pageTitle,
+ 'description' => $metaDescription,
+ 'url' => 'https://posterg.erg.be/search.php',
+ 'site_name' => 'Posterg – ERG',
+];
$extraCss = ['assets/search.css'];
?>
diff --git a/public/tfe.php b/public/tfe.php
index 8416c94..7bbf407 100644
--- a/public/tfe.php
+++ b/public/tfe.php
@@ -25,6 +25,37 @@ $metaDescription = mb_strlen($_synopsisRaw) > 160
? mb_substr($_synopsisRaw, 0, 157) . '…'
: (empty($_synopsisRaw) ? 'Mémoire de fin d\'études – Posterg, répertoire des TFE de l\'erg.' : $_synopsisRaw);
unset($_tfeAuthor, $_synopsisRaw);
+
+// --- Open Graph / Twitter Card tags ------------------------------------------
+$_ogBaseUrl = 'https://posterg.erg.be';
+// Resolve OG image: banner → first image file → none
+$_ogImage = '';
+if (!empty($data['banner_path'])) {
+ $_ogImage = $_ogBaseUrl . '/media.php?path=' . rawurlencode($data['banner_path']);
+} elseif (!empty($data['files'])) {
+ foreach ($data['files'] as $_f) {
+ $_ext = strtolower(pathinfo($_f['file_path'], PATHINFO_EXTENSION));
+ if (in_array($_ext, ['jpg', 'jpeg', 'png', 'gif', 'webp'])) {
+ $_ogImage = $_ogBaseUrl . '/media.php?path=' . rawurlencode($_f['file_path']);
+ break;
+ }
+ }
+ unset($_f, $_ext);
+}
+$ogTags = [
+ 'type' => 'article',
+ 'title' => $data['title'] . (!empty($data['authors']) ? ' – ' . $data['authors'] : ''),
+ 'description' => $metaDescription,
+ 'url' => $_ogBaseUrl . '/tfe.php?id=' . $thesisId,
+ 'image' => $_ogImage,
+ 'image_alt' => $data['title'] . (!empty($data['authors']) ? ' par ' . $data['authors'] : ''),
+ 'site_name' => 'Posterg – ERG',
+ 'article_author' => $data['authors'] ?? '',
+ 'article_published_time' => !empty($data['year']) ? $data['year'] . '-01-01' : '',
+];
+unset($_ogBaseUrl, $_ogImage);
+// --- End Open Graph ----------------------------------------------------------
+
$extraCss = ['assets/tfe.css'];
?>
diff --git a/templates/public/head.php b/templates/public/head.php
index 0920faf..c36df88 100644
--- a/templates/public/head.php
+++ b/templates/public/head.php
@@ -7,6 +7,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+