diff --git a/DESIGN.md b/DESIGN.md
index 9283e75..0ff4c2f 100644
--- a/DESIGN.md
+++ b/DESIGN.md
@@ -120,7 +120,18 @@ ctao.org drifts looping nebula videos under Galaxy vignettes; our translation:
(Cherenkov .16 / Azure .12 / Indigo .10) drifting on a 36s alternate
cycle. Opt-in via ``: **home only**. Body text NEVER sits on
a moving gradient — cards, panels and bands are solid layers above it.
-4. One-shot hero `rise` stagger; unified hover system (below). Nothing else.
+4. One-shot hero `rise` stagger; unified hover system (below).
+5. **CSS scroll-driven effects** (2026 pass; `animation-timeline`, no JS —
+ "no scroll-triggered motion" below means no JS/observer motion): card
+ reveal on viewport entry (`view()`, entry 0–35%), header shadow fading in
+ over the first 80px of scroll, article reading-progress bar (2px Cherenkov
+ under the header — functional navigation state, same budget line as the
+ TOC scrollspy). All behind `@supports (animation-timeline: view())`,
+ compositor-only (the header shadow animates a pseudo-element's OPACITY,
+ never box-shadow in keyframes), auto-killed by the reduce block.
+6. Micro-interactions: nav underline-grow (2px white rule, `scaleX` 0→1 on
+ hover, held on `aria-current`) and 1px press-down on `.btn`/navlink
+ `:active`. Nothing else.
**One hover language** (mid-pass audit: hovers were inconsistent per element;
ctao.org uses a single quiet-fade token — we do the same): every interactive
diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png
new file mode 100644
index 0000000..46a0aa8
Binary files /dev/null and b/public/apple-touch-icon.png differ
diff --git a/public/brand/hero.jpg b/public/brand/hero.jpg
index a80bfaf..fdc915c 100644
Binary files a/public/brand/hero.jpg and b/public/brand/hero.jpg differ
diff --git a/public/diagrams/architektura-cms-z-baza.svg b/public/diagrams/architektura-cms-z-baza.svg
new file mode 100644
index 0000000..8b4db31
--- /dev/null
+++ b/public/diagrams/architektura-cms-z-baza.svg
@@ -0,0 +1,110 @@
+
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..64c8443
Binary files /dev/null and b/public/favicon.ico differ
diff --git a/public/robots.txt b/public/robots.txt
index b89f538..7bc6508 100644
--- a/public/robots.txt
+++ b/public/robots.txt
@@ -1,5 +1,4 @@
+# Demo instance — keep out of search indexes until this becomes the real
+# portal on its final domain (then: Allow: / and restore the Sitemap line).
User-agent: *
-Allow: /
-Disallow: /admin/
-
-Sitemap: https://portal.ctao.org/sitemap.xml
+Disallow: /
diff --git a/src/content.config.ts b/src/content.config.ts
index 2ee77ab..e6850f8 100644
--- a/src/content.config.ts
+++ b/src/content.config.ts
@@ -15,6 +15,9 @@ const news = defineCollection({
// Public-path string, e.g. "/uploads/foo.jpg" — Sveltia uploads media to
// public/uploads, which Astro's image() helper can't validate (src/ only).
cover: z.string().optional(),
+ // BCP-47 tag when an article is not in English (e.g. "pl") — carried to
+ // so screen readers pick the right voice.
+ lang: z.string().optional(),
draft: z.boolean().default(false),
}),
});
diff --git a/src/content/news/architektura-demo-git-cms.md b/src/content/news/architektura-demo-git-cms.md
index 2bb52fb..73c73b3 100644
--- a/src/content/news/architektura-demo-git-cms.md
+++ b/src/content/news/architektura-demo-git-cms.md
@@ -5,6 +5,7 @@ date: 2026-07-23
category: demo
author: Cyfronet
cover: /diagrams/cover-git-cms.svg
+lang: pl
draft: false
---
@@ -25,6 +26,13 @@ na stronie, bez żadnego ręcznego deployu. Każda zmiana to commit, więc

+Dla porównania — ta sama maszyna z klasycznym CMS-em. Układ diagramu jest
+celowo identyczny, żeby różnice było widać na pierwszy rzut oka: tu każdy
+element działa bez przerwy, a panel logowania i aplikacja są wystawione
+do internetu.
+
+
+
## Gdzie co jest
| Co | Gdzie |
@@ -79,6 +87,40 @@ researchu:
planów (przykład wyżej). Gdy treść i jej historia leżą w gicie, nie ma
funkcji, którą dostawca mógłby przenieść do płatnego planu.
+## Plusy i minusy obu podejść
+
+Portal ma być utrzymywany przez co najmniej 6 lat, więc bilans trzeba robić
+w dwóch horyzontach. Poniżej ocena podejść jako klas rozwiązań, nie
+konkretnych produktów.
+
+**Krótkoterminowo (uruchomienie i pierwsze miesiące)**
+
+| | Statyczne + git | CMS z bazą |
+|---|---|---|
+| Plusy | mało ruchomych części od pierwszego dnia; edytor graficzny gotowy; historia i rollback od razu, w gicie | bogatszy panel od ręki: role i uprawnienia, workflow redakcyjny, relacje między treściami, więcej gotowych integracji |
+| Minusy | uprawnienia proste (dziedziczone z gita — bez ról redakcyjnych); przeglądanie historii na razie w interfejsie gita, nie w edytorze | więcej komponentów do postawienia, spięcia i zabezpieczenia od pierwszego dnia (aplikacja, baza, backupy) |
+
+**W horyzoncie 6 lat**
+
+| | Statyczne + git | CMS z bazą |
+|---|---|---|
+| Plusy | brak publicznie działającej aplikacji = brak presji łatek bezpieczeństwa; koszt utrzymania bliski zera; treść w otwartym formacie przetrwa każdą wymianę narzędzi | jeśli z czasem będą potrzebne funkcje aplikacyjne (konta, personalizacja, treści silnie strukturalne), platforma już je ma |
+| Minusy | przy bardzo dużej skali (tysiące stron) buildy rosną i wymagają optymalizacji; zaawansowane funkcje redakcyjne zależą od tempa rozwoju narzędzi open-source | kilka dużych migracji wersji w 6 lat (zmiany łamiące); baza wymaga backupów i testów odtwarzania; stała powierzchnia ataku; ryzyko przenoszenia funkcji do płatnych planów |
+
+## Gdzie jeszcze może się przydać
+
+Ten sam wzorzec — edytor graficzny nad repozytorium Markdown — nie jest
+ograniczony do portalu. Naturalni kandydaci u nas:
+
+- **Projekt Mickiewicza** — treści redagowane przez osoby nietechniczne,
+ a publikowane jako strona statyczna.
+- **Dokumentacja Episodes Platform** — już dziś jest w Markdownie; podpięcie
+ edytora dałoby wygodną edycję w przeglądarce zamiast edytora plików
+ w Gitei, bez żadnych zmian w istniejącym repozytorium.
+
+Koszt wdrożenia w takich miejscach jest niewielki: edytor to jeden statyczny
+plik HTML plus konfiguracja wskazująca repozytorium i strukturę treści.
+
## Brak lock-inu
Każdy element jest wymienialny osobno, bo treść to czysty Markdown:
diff --git a/src/layouts/Base.astro b/src/layouts/Base.astro
index 6d8dae0..e2ebd61 100644
--- a/src/layouts/Base.astro
+++ b/src/layouts/Base.astro
@@ -2,12 +2,14 @@
import '../styles/global.css';
// `ambient` opts a page into the whole-page drifting brand wash (home only — see DESIGN.md)
// `type`/`image` feed the social meta: articles pass type="article" + their cover.
-const { title = 'CTAO Science Portal', description = 'CTAO Science Portal — demo', ambient = false, type = 'website', image } = Astro.props;
+const { title = 'CTAO Science Portal', description = 'CTAO Science Portal — demo', ambient = false, type = 'website', image, lang = 'en' } = Astro.props;
// Absolute URLs for canonical/OG/RSS (head pattern from the official Astro blog
// template). `site` is a placeholder domain until the portal has a real one.
const site = Astro.site ?? new URL('https://portal.ctao.org');
const canonical = new URL(Astro.url.pathname, site);
-const ogImage = new URL(image || '/brand/og-card.jpg', site);
+// SVG covers never reach og:image — link-preview crawlers (Slack/Teams/
+// LinkedIn) don't render SVG, so those articles fall back to the brand card.
+const ogImage = new URL(image && !image.endsWith('.svg') ? image : '/brand/og-card.jpg', site);
const path = Astro.url.pathname;
const isActive = (href) => (href === '/' ? path === '/' : path.startsWith(href));
// Navigation = services from the Science Portal spec (REQUIREMENTS.md §2/§5).
@@ -24,7 +26,7 @@ const links = [
];
---
-
+
@@ -42,6 +44,11 @@ const links = [
+
+
+ {/* The home hero photo is a CSS background, which browsers discover late —
+ preloading it moves the page's LCP element to the front of the queue. */}
+ {path === '/' && }
{/* Brand typography (BRAND D.3): Inter (body) + Space Grotesk (headlines),
self-hosted latin woff2 (variable) — @font-face lives in global.css. */}
{/* Both brand fonts ship inlined in the render-blocking stylesheet —
diff --git a/src/pages/index.astro b/src/pages/index.astro
index bde48cd..086e2b6 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -97,7 +97,7 @@ const planned = ['Scheduling', 'Science alerts'];