diff --git a/apps/backoffice/src/styles.css b/apps/backoffice/src/styles.css
index 3d99d51ae..46ef17e86 100644
--- a/apps/backoffice/src/styles.css
+++ b/apps/backoffice/src/styles.css
@@ -1,4 +1,7 @@
-@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
+/* Inter carries no Ge'ez glyphs, so Noto Sans Ethiopic is loaded alongside it.
+ Without it every Amharic string in the app renders in whatever the OS
+ happens to substitute — different on Windows, macOS and Android. */
+@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Noto+Sans+Ethiopic:wght@400;500;600;700&display=swap');
*, *::before, *::after { box-sizing: border-box; }
diff --git a/apps/e2e/visual/theme.spec.ts-snapshots/backoffice-gallery-dark-desktop-chromium-win32.png b/apps/e2e/visual/theme.spec.ts-snapshots/backoffice-gallery-dark-desktop-chromium-win32.png
index fe9da8df6..a3836a31b 100644
Binary files a/apps/e2e/visual/theme.spec.ts-snapshots/backoffice-gallery-dark-desktop-chromium-win32.png and b/apps/e2e/visual/theme.spec.ts-snapshots/backoffice-gallery-dark-desktop-chromium-win32.png differ
diff --git a/apps/e2e/visual/theme.spec.ts-snapshots/backoffice-gallery-dark-tablet-chromium-win32.png b/apps/e2e/visual/theme.spec.ts-snapshots/backoffice-gallery-dark-tablet-chromium-win32.png
index b2ac0a6c9..cc4d3a1cc 100644
Binary files a/apps/e2e/visual/theme.spec.ts-snapshots/backoffice-gallery-dark-tablet-chromium-win32.png and b/apps/e2e/visual/theme.spec.ts-snapshots/backoffice-gallery-dark-tablet-chromium-win32.png differ
diff --git a/apps/e2e/visual/theme.spec.ts-snapshots/backoffice-gallery-light-desktop-chromium-win32.png b/apps/e2e/visual/theme.spec.ts-snapshots/backoffice-gallery-light-desktop-chromium-win32.png
index 79df6ccab..ff08cf4f6 100644
Binary files a/apps/e2e/visual/theme.spec.ts-snapshots/backoffice-gallery-light-desktop-chromium-win32.png and b/apps/e2e/visual/theme.spec.ts-snapshots/backoffice-gallery-light-desktop-chromium-win32.png differ
diff --git a/apps/e2e/visual/theme.spec.ts-snapshots/backoffice-gallery-light-tablet-chromium-win32.png b/apps/e2e/visual/theme.spec.ts-snapshots/backoffice-gallery-light-tablet-chromium-win32.png
index c98ab52b8..8fcaa1290 100644
Binary files a/apps/e2e/visual/theme.spec.ts-snapshots/backoffice-gallery-light-tablet-chromium-win32.png and b/apps/e2e/visual/theme.spec.ts-snapshots/backoffice-gallery-light-tablet-chromium-win32.png differ
diff --git a/apps/e2e/visual/theme.spec.ts-snapshots/portal-gallery-dark-desktop-chromium-win32.png b/apps/e2e/visual/theme.spec.ts-snapshots/portal-gallery-dark-desktop-chromium-win32.png
index 406819423..fcb53cffa 100644
Binary files a/apps/e2e/visual/theme.spec.ts-snapshots/portal-gallery-dark-desktop-chromium-win32.png and b/apps/e2e/visual/theme.spec.ts-snapshots/portal-gallery-dark-desktop-chromium-win32.png differ
diff --git a/apps/e2e/visual/theme.spec.ts-snapshots/portal-gallery-dark-tablet-chromium-win32.png b/apps/e2e/visual/theme.spec.ts-snapshots/portal-gallery-dark-tablet-chromium-win32.png
index c4ccb4e98..96c9165fb 100644
Binary files a/apps/e2e/visual/theme.spec.ts-snapshots/portal-gallery-dark-tablet-chromium-win32.png and b/apps/e2e/visual/theme.spec.ts-snapshots/portal-gallery-dark-tablet-chromium-win32.png differ
diff --git a/apps/e2e/visual/theme.spec.ts-snapshots/portal-gallery-light-desktop-chromium-win32.png b/apps/e2e/visual/theme.spec.ts-snapshots/portal-gallery-light-desktop-chromium-win32.png
index 69b66859a..e50811108 100644
Binary files a/apps/e2e/visual/theme.spec.ts-snapshots/portal-gallery-light-desktop-chromium-win32.png and b/apps/e2e/visual/theme.spec.ts-snapshots/portal-gallery-light-desktop-chromium-win32.png differ
diff --git a/apps/e2e/visual/theme.spec.ts-snapshots/portal-gallery-light-tablet-chromium-win32.png b/apps/e2e/visual/theme.spec.ts-snapshots/portal-gallery-light-tablet-chromium-win32.png
index f6cf3fc82..465a9af2d 100644
Binary files a/apps/e2e/visual/theme.spec.ts-snapshots/portal-gallery-light-tablet-chromium-win32.png and b/apps/e2e/visual/theme.spec.ts-snapshots/portal-gallery-light-tablet-chromium-win32.png differ
diff --git a/apps/portal/src/app/theme/portal.css b/apps/portal/src/app/theme/portal.css
index 6b5fec975..93a2ac64a 100644
--- a/apps/portal/src/app/theme/portal.css
+++ b/apps/portal/src/app/theme/portal.css
@@ -1,6 +1,9 @@
/* Portal global styles — loaded after Mantine's CSS, no Tailwind preflight so
it never fights Mantine's base styles. */
-@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
+/* Inter carries no Ge'ez glyphs, so Noto Sans Ethiopic is loaded alongside it.
+ Without it every Amharic string in the app renders in whatever the OS
+ happens to substitute — different on Windows, macOS and Android. */
+@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Noto+Sans+Ethiopic:wght@400;500;600;700&display=swap');
:root {
--ema-surface-light: #f5f8fc;
diff --git a/libs/shared/src/lib/theme/base-theme.ts b/libs/shared/src/lib/theme/base-theme.ts
index 43dc11c80..73315594a 100644
--- a/libs/shared/src/lib/theme/base-theme.ts
+++ b/libs/shared/src/lib/theme/base-theme.ts
@@ -15,13 +15,18 @@ import { createTheme, rem } from '@mantine/core';
/**
* The type stack.
*
- * Ge'ez support is added in the font stage, not here: Inter carries no Ethiopic
- * glyphs today, so every Amharic string falls back to an arbitrary OS font.
- * Inserting "Noto Sans Ethiopic" changes metrics, which would make this
- * structural refactor look like a visual change.
+ * Noto Sans Ethiopic sits directly after Inter rather than being swapped in by
+ * a `[lang='am']` rule. Browsers fall back per *glyph*, not per element, so one
+ * stack renders Latin in Inter and Ge'ez in Noto automatically — including
+ * inside a single string. That matters here: a registry is full of mixed-script
+ * lines like an Amharic name beside a Latin IMO number, and a language-scoped
+ * swap would render one half of those in the wrong face.
+ *
+ * Inter carries no Ge'ez glyphs at all, so before this the Amharic half of a
+ * bilingual system rendered in whatever the OS happened to substitute.
*/
export const EMA_FONT_STACK =
- 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
+ 'Inter, "Noto Sans Ethiopic", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
export const baseTheme = createTheme({
fontFamily: EMA_FONT_STACK,
@@ -29,16 +34,16 @@ export const baseTheme = createTheme({
headings: {
fontFamily: EMA_FONT_STACK,
fontWeight: '700',
- // Exactly the portal's original values. Ethiopic's taller ascenders may
- // want a little more leading, but that is a typography change and belongs
- // with the font work — not smuggled into a structural refactor whose whole
- // claim is that it changes nothing for the portal.
+ // A little looser than the portal's original values. Ge'ez has taller
+ // ascenders and deeper descenders than Latin, so a heading set to Inter's
+ // natural leading clips its Amharic rendering — which only became visible
+ // once Ethiopic was actually being rendered rather than substituted.
sizes: {
- h1: { fontSize: rem(32), lineHeight: '1.25' },
- h2: { fontSize: rem(25), lineHeight: '1.3' },
- h3: { fontSize: rem(21), lineHeight: '1.35' },
- h4: { fontSize: rem(17), lineHeight: '1.4' },
- h5: { fontSize: rem(15), lineHeight: '1.45' },
+ h1: { fontSize: rem(32), lineHeight: '1.3' },
+ h2: { fontSize: rem(25), lineHeight: '1.35' },
+ h3: { fontSize: rem(21), lineHeight: '1.4' },
+ h4: { fontSize: rem(17), lineHeight: '1.45' },
+ h5: { fontSize: rem(15), lineHeight: '1.5' },
},
},
diff --git a/libs/ui/src/lib/dev/ThemeGallery.tsx b/libs/ui/src/lib/dev/ThemeGallery.tsx
index ba90fd555..77e7ec41e 100644
--- a/libs/ui/src/lib/dev/ThemeGallery.tsx
+++ b/libs/ui/src/lib/dev/ThemeGallery.tsx
@@ -117,6 +117,9 @@ export function ThemeGallery() {
{/* Mixed-script line: the case a [lang] font swap would break. */}