diff --git a/frontend/src/pages/DonutPage.vue b/frontend/src/pages/DonutPage.vue
index 328d6df..0464ea9 100644
--- a/frontend/src/pages/DonutPage.vue
+++ b/frontend/src/pages/DonutPage.vue
@@ -58,7 +58,8 @@ const R_CEILING_OUTER = 225
const R_CEILING_INNER = 200
const R_FLOOR_OUTER = 100
const R_FLOOR_INNER = 78
-const R_CENTER = 56
+const R_SOCIAL_LABEL = 67 // étiquettes des dimensions du plancher social (intérieur)
+const R_CENTER = 50
/** Les 9 limites planétaires de Kate Raworth, placées autour de l'anneau extérieur. */
const PLANETARY_BOUNDARIES = [
@@ -73,6 +74,65 @@ const PLANETARY_BOUNDARIES = [
"Couche d'ozone",
]
+/** Les 12 dimensions du plancher social de Kate Raworth, placées entre le centre et l'anneau intérieur. */
+const SOCIAL_FOUNDATIONS = [
+ 'Alimentation',
+ 'Santé',
+ 'Éducation',
+ 'Revenu & travail',
+ 'Eau',
+ 'Énergie',
+ 'Liens sociaux',
+ 'Logement',
+ 'Égalité de genre',
+ 'Équité sociale',
+ 'Voix politique',
+ 'Paix & justice',
+]
+
+/**
+ * Génère pour chaque label un petit arc de cercle (utilisé par ).
+ * Le texte épouse alors la courbure de l'anneau au lieu d'être posé en ligne droite.
+ *
+ * Astuce de lisibilité : on inverse le sens du path (CW vs CCW) selon la moitié
+ * du cercle pour que les labels du bas restent lisibles à l'endroit. Sans ça,
+ * les labels du bas seraient écrits à l'envers ou inversés (lus de droite à gauche).
+ *
+ * - Moitié haute (angles proches de 0°) : path va de θ-δ à θ+δ, sweep=1 (CW visuellement)
+ * → direction RIGHT au sommet → texte "left of right" = UP = OUTSIDE
+ * - Moitié basse (angles proches de 180°) : path va de θ+δ à θ-δ, sweep=0 (CCW visuellement)
+ * → direction RIGHT au bas → texte "left of right" = UP = vers le CENTRE
+ * (= lisible à l'endroit pour un lecteur fixe en face de l'écran)
+ *
+ * @param labels Liste des libellés
+ * @param radius Rayon de l'arc de chaque label
+ * @param idPrefix Préfixe pour générer les `id` des paths (chaque path doit être unique en DOM)
+ * @param halfSpanDeg Demi-largeur angulaire du path (= halfSpan ≈ 90 % du segment alloué au label)
+ */
+function arcLabelPaths(labels: readonly string[], radius: number, idPrefix: string, halfSpanDeg: number) {
+ const n = labels.length
+ const stepDeg = 360 / n
+ return labels.map((label, i) => {
+ const centerDeg = i * stepDeg + stepDeg / 2
+ const startDeg = centerDeg - halfSpanDeg
+ const endDeg = centerDeg + halfSpanDeg
+
+ // Top half ⇨ chemin CW (sweep=1) ; bottom half ⇨ chemin CCW (sweep=0).
+ const normalized = ((centerDeg % 360) + 360) % 360
+ const isTopHalf = normalized <= 90 || normalized >= 270
+
+ const fromDeg = isTopHalf ? startDeg : endDeg
+ const toDeg = isTopHalf ? endDeg : startDeg
+ const sweep = isTopHalf ? 1 : 0
+
+ const [x1, y1] = pt(fromDeg, radius)
+ const [x2, y2] = pt(toDeg, radius)
+ const d = `M ${x1.toFixed(2)} ${y1.toFixed(2)} A ${radius} ${radius} 0 0 ${sweep} ${x2.toFixed(2)} ${y2.toFixed(2)}`
+
+ return { label, pathId: `${idPrefix}-${i}`, d, key: i }
+ })
+}
+
/** deg → rad avec offset -90° (0° en haut). */
function rad(deg: number): number {
return (deg - 90) * Math.PI / 180
@@ -139,27 +199,11 @@ const categoryLabels = computed(() => {
})
})
-/** Étiquettes des 9 limites planétaires, autour de l'anneau extérieur.
- *
- * Texte tangent à la circonférence + flip 180° dans la moitié inférieure
- * pour rester lisible (sinon le label serait à l'envers en bas du donut).
- *
- * - angle 0 (haut) : rotation 0 → texte horizontal lisible
- * - angle 90 (droite) : rotation 90 → texte vertical lisible de haut en bas
- * - angle 180 (bas) : rotation 180+180 = 0 → texte horizontal lisible
- * - angle 270 (gauche) : rotation 270 → texte vertical lisible de bas en haut
- */
-const planetaryLabels = computed(() => {
- const n = PLANETARY_BOUNDARIES.length
- const step = 360 / n
- return PLANETARY_BOUNDARIES.map((label, i) => {
- const angle = i * step + step / 2
- const [x, y] = pt(angle, R_PLANET_LABEL)
- let rotation = angle
- if (angle > 90 && angle < 270) rotation += 180
- return { label, x, y, rotation, key: i }
- })
-})
+/** Paths d'arc pour les 9 limites planétaires (anneau extérieur). */
+const planetaryArcs = computed(() => arcLabelPaths(PLANETARY_BOUNDARIES, R_PLANET_LABEL, 'pb', 18))
+
+/** Paths d'arc pour les 12 dimensions du plancher social (anneau intérieur). */
+const socialArcs = computed(() => arcLabelPaths(SOCIAL_FOUNDATIONS, R_SOCIAL_LABEL, 'sf', 14))
interface DotView {
project: ProjectListRow
@@ -274,22 +318,31 @@ function gotoProject(p: ProjectListRow) {
+
+
+
-
+
-
-
+
{{ p.label }}
-
-
+
+
@@ -311,7 +364,7 @@ function gotoProject(p: ProjectListRow) {
-
+
{{ l.cat.name }}
-
- PLAFOND ÉCOLOGIQUE
- PLANCHER SOCIAL
+
+
+
+
+ {{ s.label }}
+
+
+