16 Commits

Author SHA1 Message Date
Christian Gick
9dd4a68d71 fix: quote LANG variable in shortcode JS (was unquoted ReferenceError)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 14:01:28 +03:00
Christian Gick
7f6d96cbe7 fix: switch map from MapLibre to Leaflet (minifier compat)
MapLibre inline style JSON was mangled by Hugo minifier.
Leaflet + OSM tiles is simpler and works reliably.
Updated map colors to Agiliton green palette.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 13:56:42 +03:00
Christian Gick
e732b9d96e fix: nav menu, language dropdown, sidebar TOC + recent articles
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 13:53:27 +03:00
Christian Gick
18dc7ae66f feat(IFK-11): migrate from Congo to HugoBlox with custom layouts
All checks were successful
Deploy Internet for Kids / Build & Push (push) Successful in 12s
Deploy Internet for Kids / Deploy (push) Successful in 5s
Deploy Internet for Kids / Health Check (push) Successful in 2s
Deploy Internet for Kids / Smoke Tests (push) Successful in 3s
Deploy Internet for Kids / IndexNow Ping (push) Successful in 9s
Deploy Internet for Kids / Promote to Latest (push) Successful in 2s
Deploy Internet for Kids / Rollback (push) Has been skipped
Deploy Internet for Kids / Audit (push) Successful in 2s
- Remove Congo theme submodule, add Hugo modules (blox-core)
- Custom baseof/single/home/list layouts (no Tailwind build needed)
- Newsreader serif font preserved throughout
- Agiliton lime/emerald brand colors
- All 7 shortcodes preserved (world-map, charts, stats, etc.)
- Multilingual EN/DE/FR with i18n files
- Self-hosted fonts, no external CSS dependencies
- Wide content layout, clean research-publication aesthetic

Reference: metaconscious.org, matteocourthoud.github.io

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 13:40:21 +03:00
Christian Gick
ec370d2d7e feat: increase font sizes throughout
All checks were successful
Deploy Internet for Kids / Build & Push (push) Successful in 9s
Deploy Internet for Kids / Deploy (push) Successful in 6s
Deploy Internet for Kids / Health Check (push) Successful in 2s
Deploy Internet for Kids / Smoke Tests (push) Successful in 3s
Deploy Internet for Kids / IndexNow Ping (push) Successful in 8s
Deploy Internet for Kids / Promote to Latest (push) Successful in 2s
Deploy Internet for Kids / Rollback (push) Has been skipped
Deploy Internet for Kids / Audit (push) Successful in 2s
H1: 2.75rem, body: 22px/36px, H2: 1.85rem, H3: 1.5rem,
blockquotes: 24px, tables: 18px, nav/TOC: larger.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 13:14:39 +03:00
Christian Gick
b517af5c2f feat: self-host Newsreader font, remove Google Fonts CDN
All checks were successful
Deploy Internet for Kids / Build & Push (push) Successful in 13s
Deploy Internet for Kids / Deploy (push) Successful in 6s
Deploy Internet for Kids / Health Check (push) Successful in 2s
Deploy Internet for Kids / Smoke Tests (push) Successful in 3s
Deploy Internet for Kids / IndexNow Ping (push) Successful in 7s
Deploy Internet for Kids / Promote to Latest (push) Successful in 2s
Deploy Internet for Kids / Rollback (push) Has been skipped
Deploy Internet for Kids / Audit (push) Successful in 2s
Local woff2 files for latin + latin-ext (DE/FR support).
No external font requests for privacy/GDPR compliance.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 13:07:32 +03:00
Christian Gick
852640a574 feat: Newsreader serif typography + Agiliton brand colors
All checks were successful
Deploy Internet for Kids / Build & Push (push) Successful in 13s
Deploy Internet for Kids / Deploy (push) Successful in 6s
Deploy Internet for Kids / Health Check (push) Successful in 2s
Deploy Internet for Kids / Smoke Tests (push) Successful in 3s
Deploy Internet for Kids / IndexNow Ping (push) Successful in 7s
Deploy Internet for Kids / Promote to Latest (push) Successful in 2s
Deploy Internet for Kids / Rollback (push) Has been skipped
Deploy Internet for Kids / Audit (push) Successful in 2s
- Add Newsreader font (darioamodei.com-inspired literary serif)
- Warm light background (#fafaf9) with Agiliton lime/emerald accents
- 20px body text, 32px line-height for research publication feel
- Sans-serif UI elements (nav, tags, TOC) for contrast
- 800px max content width for focused reading
- Status badges updated to green palette

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 13:01:03 +03:00
Christian Gick
09a11520e8 fix: use OSM raster tiles for world map (clicksports vector tiles 503)
All checks were successful
Deploy Internet for Kids / Build & Push (push) Successful in 13s
Deploy Internet for Kids / Deploy (push) Successful in 6s
Deploy Internet for Kids / Health Check (push) Successful in 2s
Deploy Internet for Kids / Smoke Tests (push) Successful in 3s
Deploy Internet for Kids / IndexNow Ping (push) Successful in 8s
Deploy Internet for Kids / Promote to Latest (push) Successful in 1s
Deploy Internet for Kids / Rollback (push) Has been skipped
Deploy Internet for Kids / Audit (push) Successful in 2s
maps.clicksports.de vector tile style.json returns 503 on render.
Fall back to OSM raster tiles until tile server is fixed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 12:39:45 +03:00
Christian Gick
df72e6aaba feat: switch world map to MapLibre GL + clicksports vector tiles
All checks were successful
Deploy Internet for Kids / Build & Push (push) Successful in 13s
Deploy Internet for Kids / Deploy (push) Successful in 6s
Deploy Internet for Kids / Health Check (push) Successful in 2s
Deploy Internet for Kids / Smoke Tests (push) Successful in 3s
Deploy Internet for Kids / IndexNow Ping (push) Successful in 7s
Deploy Internet for Kids / Promote to Latest (push) Successful in 2s
Deploy Internet for Kids / Rollback (push) Has been skipped
Deploy Internet for Kids / Audit (push) Successful in 2s
Replace Leaflet+OSM with MapLibre GL JS consuming vector tiles
from maps.clicksports.de (TileServer GL). Choropleth overlay via
TopoJSON remains the same.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 12:08:28 +03:00
Christian Gick
308034e40d feat(IFK-9): interactive world map, charts, stats banner, timeline
All checks were successful
Deploy Internet for Kids / Build & Push (push) Successful in 9s
Deploy Internet for Kids / Deploy (push) Successful in 6s
Deploy Internet for Kids / Health Check (push) Successful in 2s
Deploy Internet for Kids / Smoke Tests (push) Successful in 4s
Deploy Internet for Kids / IndexNow Ping (push) Successful in 8s
Deploy Internet for Kids / Promote to Latest (push) Successful in 1s
Deploy Internet for Kids / Rollback (push) Has been skipped
Deploy Internet for Kids / Audit (push) Successful in 2s
- Add centralized data/countries.json with all 17 countries (EN/DE/FR)
- Add Leaflet choropleth world map with TopoJSON boundaries
- Add stats banner shortcode (enforced/passed/progress/guidelines counts)
- Add Chart.js charts (status donut, age limits bar, legislation timeline)
- Add Mermaid timeline of legislation milestones 2020-2026
- Refactor child-safety-map table to use data file (DRY)
- Update all 3 language articles (EN/DE/FR)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 12:03:02 +03:00
Christian Gick
5e76d8745a feat: SEO improvements — related content, IndexNow, manifest, llms.txt
All checks were successful
Deploy Internet for Kids / Build & Push (push) Successful in 13s
Deploy Internet for Kids / Deploy (push) Successful in 6s
Deploy Internet for Kids / Health Check (push) Successful in 2s
Deploy Internet for Kids / Smoke Tests (push) Successful in 2s
Deploy Internet for Kids / IndexNow Ping (push) Successful in 7s
Deploy Internet for Kids / Promote to Latest (push) Successful in 2s
Deploy Internet for Kids / Rollback (push) Has been skipped
Deploy Internet for Kids / Audit (push) Successful in 3s
- Enable showRelatedContent + showSummary in article config
- Add IndexNow ping step to CI deploy pipeline
- Fix site.webmanifest branding (was Congo, now Internet for Kids)
- Update llms.txt to include French language section

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 11:41:07 +03:00
Christian Gick
2dfc9ed8bd fix: language switch losing prefix + SSL redirect scheme
All checks were successful
Deploy Internet for Kids / Build & Push (push) Successful in 9s
Deploy Internet for Kids / Deploy (push) Successful in 6s
Deploy Internet for Kids / Health Check (push) Successful in 2s
Deploy Internet for Kids / Smoke Tests (push) Successful in 3s
Deploy Internet for Kids / Promote to Latest (push) Successful in 2s
Deploy Internet for Kids / Rollback (push) Has been skipped
Deploy Internet for Kids / Audit (push) Successful in 2s
Menu links used url= which doesn't prepend language prefix.
Changed to pageRef= which resolves per-language correctly.

Also fixed root redirect using http:// instead of https://
by reading X-Forwarded-Proto from gateway proxy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 11:08:36 +03:00
Christian Gick
b6951ec29d feat: migrate domain from internetforkids.ong to internetforkids.org
All checks were successful
Deploy Internet for Kids / Build & Push (push) Successful in 12s
Deploy Internet for Kids / Deploy (push) Successful in 6s
Deploy Internet for Kids / Health Check (push) Successful in 1s
Deploy Internet for Kids / Smoke Tests (push) Successful in 3s
Deploy Internet for Kids / Promote to Latest (push) Successful in 2s
Deploy Internet for Kids / Rollback (push) Has been skipped
Deploy Internet for Kids / Audit (push) Successful in 2s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 09:10:50 +03:00
Christian Gick
99350eef8a fix: exclude BingSiteAuth.xml from language redirect
All checks were successful
Deploy Internet for Kids / Build & Push (push) Successful in 9s
Deploy Internet for Kids / Deploy (push) Successful in 7s
Deploy Internet for Kids / Health Check (push) Successful in 1s
Deploy Internet for Kids / Smoke Tests (push) Successful in 3s
Deploy Internet for Kids / Promote to Latest (push) Successful in 2s
Deploy Internet for Kids / Rollback (push) Has been skipped
Deploy Internet for Kids / Audit (push) Successful in 2s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 08:47:24 +03:00
Christian Gick
c7376db2cb feat: add Bing Webmaster Tools verification XML
All checks were successful
Deploy Internet for Kids / Build & Push (push) Successful in 14s
Deploy Internet for Kids / Deploy (push) Successful in 12s
Deploy Internet for Kids / Health Check (push) Successful in 7s
Deploy Internet for Kids / Smoke Tests (push) Successful in 9s
Deploy Internet for Kids / Promote to Latest (push) Successful in 4s
Deploy Internet for Kids / Rollback (push) Has been skipped
Deploy Internet for Kids / Audit (push) Successful in 2s
Auth code: 7B8CDCFD8FC8D5E226BD9E01CEE80814

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 08:45:55 +03:00
Christian Gick
ba41263e48 fix: rename VPN redirect path to /vpn-app
All checks were successful
Deploy Internet for Kids / Build & Push (push) Successful in 9s
Deploy Internet for Kids / Deploy (push) Successful in 6s
Deploy Internet for Kids / Health Check (push) Successful in 2s
Deploy Internet for Kids / Smoke Tests (push) Successful in 3s
Deploy Internet for Kids / Promote to Latest (push) Successful in 2s
Deploy Internet for Kids / Rollback (push) Has been skipped
Deploy Internet for Kids / Audit (push) Successful in 2s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 08:10:32 +03:00
42 changed files with 1733 additions and 174 deletions

View File

@@ -30,7 +30,7 @@ env:
REMOTE_USER: root
DEPLOY_PATH: /opt/apps/internetforkids
HEALTH_ENDPOINT: "http://localhost:3006/health"
PUBLIC_URL: "https://internetforkids.ong"
PUBLIC_URL: "https://internetforkids.org"
HEALTH_TIMEOUT: 60
jobs:
@@ -163,6 +163,24 @@ jobs:
fi
echo "All smoke tests passed"
indexnow:
name: IndexNow Ping
runs-on: ubuntu-latest
needs: [smoke-test]
steps:
- name: Notify search engines
run: |
KEY="40101d97ec848f6ea016fac347b1a5bc"
for url in \
"https://internetforkids.org/" \
"https://internetforkids.org/en/" \
"https://internetforkids.org/de/" \
"https://internetforkids.org/fr/" \
"https://internetforkids.org/sitemap.xml"; do
curl -sf "https://api.indexnow.org/indexnow?url=${url}&key=${KEY}" || true
done
echo "IndexNow pings sent"
promote:
name: Promote to Latest
runs-on: ubuntu-latest

6
.session/metadata.env Normal file
View File

@@ -0,0 +1,6 @@
SESSION_ISSUE_KEY=CF-2812
PROJECT_DIR=/Users/christian.gick/Development/Apps/internetforkids
WORKSPACE_PATH=/Users/christian.gick/.claude/workspaces/cf-2812
BASE_BRANCH=main
CREATED_AT=1775206558
CLAUDE_PID=74267

3
.session/notes.md Normal file
View File

@@ -0,0 +1,3 @@
# Session CF-2812
Started: 2026-04-03 11:55
Project: internetforkids

0
.session/plan.md Normal file
View File

202
assets/css/custom.css Normal file
View File

@@ -0,0 +1,202 @@
/* Internet for Kids — Newsreader typography (darioamodei.com-inspired)
Warm, literary, research-publication feel */
/* Local Newsreader font — latin */
@font-face {
font-family: 'Newsreader';
font-style: normal;
font-weight: 400 700;
font-display: swap;
src: url(/fonts/newsreader/newsreader-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext (DE umlauts, FR accents) */
@font-face {
font-family: 'Newsreader';
font-style: normal;
font-weight: 400 700;
font-display: swap;
src: url(/fonts/newsreader/newsreader-latin-ext.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* italic latin */
@font-face {
font-family: 'Newsreader';
font-style: italic;
font-weight: 400 500;
font-display: swap;
src: url(/fonts/newsreader/newsreader-italic-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* italic latin-ext */
@font-face {
font-family: 'Newsreader';
font-style: italic;
font-weight: 400 500;
font-display: swap;
src: url(/fonts/newsreader/newsreader-italic-latin-ext.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
:root {
--ifk-font: 'Newsreader', 'Times New Roman', serif;
--ifk-text: #1c1917;
--ifk-bg: #fafaf9;
--ifk-accent: #4d7c0f;
--ifk-accent-hover: #3f6212;
--ifk-secondary: #059669;
}
/* Global background */
body {
background-color: var(--ifk-bg) !important;
color: var(--ifk-text) !important;
}
/* Override Congo's dark bg classes */
.bg-neutral {
background-color: var(--ifk-bg) !important;
}
/* All text in Newsreader */
body, p, li, td, th, blockquote, figcaption {
font-family: var(--ifk-font) !important;
font-size: 22px;
line-height: 36px;
color: var(--ifk-text);
}
/* Headings */
h1, h2, h3, h4, h5, h6 {
font-family: var(--ifk-font) !important;
color: var(--ifk-text);
font-weight: 600;
}
h1, .article-title {
font-size: 2.75rem;
line-height: 1.15;
font-weight: 600;
margin-bottom: 1.5rem;
letter-spacing: -0.01em;
}
h2 {
font-size: 1.85rem;
line-height: 1.25;
margin-top: 3rem;
margin-bottom: 1.25rem;
}
h3 {
font-size: 1.5rem;
line-height: 1.3;
margin-top: 2.5rem;
}
/* Article content — constrain width for readability */
article .prose,
article .content,
.article-content {
max-width: 800px;
}
/* Paragraphs */
article p {
margin-bottom: 1.5rem;
}
/* Links — subtle, Amodei-style */
article a {
color: var(--ifk-accent);
text-decoration: underline;
text-decoration-thickness: 1px;
text-underline-offset: 2px;
}
article a:hover {
color: var(--ifk-accent-hover);
text-decoration-thickness: 2px;
}
/* Blockquotes — elegant left-border */
blockquote {
font-family: var(--ifk-font) !important;
font-style: italic;
font-size: 24px;
line-height: 38px;
border-left: 3px solid var(--ifk-secondary);
padding-left: 1.5rem;
margin: 2rem 0;
color: var(--ifk-text);
opacity: 0.85;
}
/* Navigation — keep sans-serif for UI elements */
nav, nav a, nav span,
header, header a,
footer, footer a, footer span,
.breadcrumbs, .taxonomy-list,
.article-meta, .post-meta,
time, .reading-time {
font-family: system-ui, -apple-system, 'Segoe UI', sans-serif !important;
font-size: 1rem;
}
/* Header title */
header a[href="/"] span,
header a[rel="me"] {
font-family: var(--ifk-font) !important;
font-size: 1.35rem;
font-weight: 500;
}
/* Tags/categories — small sans pills */
.taxonomy-term, [href*="/tags/"], [href*="/categories/"] {
font-family: system-ui, -apple-system, sans-serif !important;
font-size: 0.8rem !important;
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* Table of contents — smaller sans */
#TableOfContents, #TableOfContents a {
font-family: system-ui, -apple-system, sans-serif !important;
font-size: 0.95rem !important;
line-height: 1.7;
}
/* Homepage recent articles */
.summary {
font-family: var(--ifk-font) !important;
font-size: 20px;
line-height: 32px;
}
/* Data tables in articles — keep readable */
.csm-table, .csm-table td, .csm-table th {
font-family: var(--ifk-font) !important;
font-size: 18px;
line-height: 1.6;
}
/* Stats banner — override with sans for numbers */
.ifk-stat-num {
font-family: var(--ifk-font) !important;
}
.ifk-stat-label {
font-family: system-ui, -apple-system, sans-serif !important;
}
/* Chart labels keep sans */
.ifk-chart-card h4 {
font-family: system-ui, -apple-system, sans-serif !important;
}
/* Smooth dark mode override (if ever enabled) */
/* Status badges keep their blue palette */
.csm-enforced { background: var(--ifk-accent) !important; }
.csm-passed { background: var(--ifk-secondary) !important; }
.csm-progress { background: #86efac !important; color: #14532d !important; }
.csm-guidelines { background: #d1fae5 !important; color: #065f46 !important; }

324
assets/css/main.css Normal file
View File

@@ -0,0 +1,324 @@
/* Custom font imports */
@font-face {
font-family: 'Newsreader';
font-style: normal;
font-weight: 400 700;
font-display: swap;
src: url(/fonts/newsreader/newsreader-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Newsreader';
font-style: normal;
font-weight: 400 700;
font-display: swap;
src: url(/fonts/newsreader/newsreader-latin-ext.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'Newsreader';
font-style: italic;
font-weight: 400 500;
font-display: swap;
src: url(/fonts/newsreader/newsreader-italic-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Newsreader';
font-style: italic;
font-weight: 400 500;
font-display: swap;
src: url(/fonts/newsreader/newsreader-italic-latin-ext.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* CSS Variables */
:root {
--ifk-font: 'Newsreader', 'Times New Roman', serif;
--ifk-text: #1c1917;
--ifk-bg: #fafaf9;
--ifk-accent: #4d7c0f;
--ifk-accent-hover: #3f6212;
--ifk-secondary: #059669;
}
/* Utility classes for colors */
.text-ifk-accent { color: var(--ifk-accent); }
.text-ifk-text { color: var(--ifk-text); }
.bg-ifk-bg { background-color: var(--ifk-bg); }
.hover\:text-ifk-accent:hover { color: var(--ifk-accent); }
.hover\:text-ifk-accent-hover:hover { color: var(--ifk-accent-hover); }
/* Font family utility */
.font-newsreader { font-family: var(--ifk-font); }
/* Tailwind-equivalent utilities */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
.flex { display: flex; }
.flex-1 { flex: 1 1 0%; }
.flex-wrap { flex-wrap: wrap; }
.inline-block { display: inline-block; }
.grid { display: grid; }
.grid-cols-2 { grid-template-columns: repeat(2, 1fr); }
.justify-between { justify-content: space-between; }
.items-center { align-items: center; }
.gap-4 { gap: 1rem; }
.gap-8 { gap: 2rem; }
.mx-auto { margin-left: auto; margin-right: auto; }
.max-w-2xl { max-width: 42rem; }
.max-w-3xl { max-width: 48rem; }
.max-w-4xl { max-width: 56rem; }
.px-4 { padding-left: 1rem; padding-right: 1rem; }
.py-4 { padding-top: 1rem; padding-bottom: 1rem; }
.py-12 { padding-top: 3rem; padding-bottom: 3rem; }
.p-4 { padding: 1rem; }
.pt-8 { padding-top: 2rem; }
.mb-2 { margin-bottom: 0.5rem; }
.mb-3 { margin-bottom: 0.75rem; }
.mb-4 { margin-bottom: 1rem; }
.mb-8 { margin-bottom: 2rem; }
.mb-12 { margin-bottom: 3rem; }
.mr-2 { margin-right: 0.5rem; }
.mt-12 { margin-top: 3rem; }
.mt-20 { margin-top: 5rem; }
.sticky { position: sticky; }
.top-0 { top: 0; }
.z-40 { z-index: 40; }
.border { border: 1px solid #e5e7eb; }
.border-t { border-top: 1px solid #e5e7eb; }
.border-b { border-bottom: 1px solid #e5e7eb; }
.rounded { border-radius: 0.25rem; }
.bg-white { background-color: #fff; }
.bg-gray-50 { background-color: #f9fafb; }
.text-center { text-align: center; }
.text-left { text-align: left; }
.text-right { text-align: right; }
.text-xs { font-size: 0.75rem; line-height: 1rem; }
.text-sm { font-size: 0.875rem; line-height: 1.25rem; }
.text-lg { font-size: 1.125rem; line-height: 1.75rem; }
.text-xl { font-size: 1.25rem; line-height: 1.75rem; }
.text-4xl { font-size: 2.75rem; line-height: 1.15; }
.font-semibold { font-weight: 600; }
.uppercase { text-transform: uppercase; }
.tracking-wide { letter-spacing: 0.025em; }
.leading-tight { line-height: 1.25; }
.leading-relaxed { line-height: 1.625; }
.text-gray-600 { color: #4b5563; }
.hover\:bg-gray-50:hover { background-color: #f9fafb; }
@media (min-width: 768px) { .md\:grid-cols-3 { grid-template-columns: repeat(3, 1fr); } }
/* Global typography */
body {
background-color: var(--ifk-bg);
color: var(--ifk-text);
font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
}
article, .prose {
font-family: var(--ifk-font);
}
h1, h2, h3, h4, h5, h6 {
font-family: var(--ifk-font);
color: var(--ifk-text);
font-weight: 600;
}
h1, .article-title {
font-size: 2.75rem;
line-height: 1.15;
letter-spacing: -0.01em;
margin-bottom: 1.5rem;
}
h2 {
font-size: 1.85rem;
line-height: 1.25;
margin-top: 3rem;
margin-bottom: 1.25rem;
}
h3 {
font-size: 1.5rem;
line-height: 1.3;
margin-top: 2.5rem;
margin-bottom: 1rem;
}
/* Article content */
article p {
font-size: 1.375rem;
line-height: 1.6;
margin-bottom: 1.5rem;
font-family: var(--ifk-font);
}
/* Links */
article a {
color: var(--ifk-accent);
text-decoration: underline;
text-decoration-thickness: 1px;
text-underline-offset: 2px;
transition: all 0.2s ease;
}
article a:hover {
color: var(--ifk-accent-hover);
text-decoration-thickness: 2px;
}
/* Blockquotes */
blockquote {
font-family: var(--ifk-font);
font-style: italic;
font-size: 1.5rem;
line-height: 1.6;
border-left: 3px solid var(--ifk-secondary);
padding-left: 1.5rem;
margin: 2rem 0;
color: var(--ifk-text);
opacity: 0.85;
}
/* Code blocks */
code {
font-family: 'Courier New', monospace;
font-size: 0.9rem;
background: #f3f4f6;
padding: 0.1rem 0.4rem;
border-radius: 0.25rem;
}
pre {
background: #1f2937;
color: #f3f4f6;
padding: 1.5rem;
border-radius: 0.5rem;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.5;
margin: 1.5rem 0;
}
pre code {
background: none;
color: inherit;
padding: 0;
border-radius: 0;
}
/* Tables */
table {
width: 100%;
border-collapse: collapse;
margin: 1.5rem 0;
font-family: var(--ifk-font);
font-size: 1.1rem;
line-height: 1.6;
}
th, td {
padding: 0.75rem 1rem;
border: 1px solid #e5e7eb;
text-align: left;
}
th {
background: #f3f4f6;
font-weight: 600;
}
/* Lists */
ul, ol {
margin-left: 1.5rem;
margin-bottom: 1.5rem;
}
li {
margin-bottom: 0.5rem;
font-family: var(--ifk-font);
font-size: 1.375rem;
line-height: 1.6;
}
/* Navigation */
nav, header, footer {
font-family: system-ui, -apple-system, sans-serif;
}
nav a {
text-decoration: none;
transition: color 0.2s ease;
}
nav a:hover {
color: var(--ifk-accent);
}
/* Article metadata */
.article-meta, .post-meta, time, .reading-time {
font-family: system-ui, -apple-system, sans-serif;
font-size: 0.95rem;
color: #6b7280;
}
/* Tags */
.taxonomy-term, [href*="/tags/"], [href*="/categories/"] {
font-family: system-ui, -apple-system, sans-serif;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--ifk-accent);
text-decoration: none;
}
.taxonomy-term:hover, [href*="/tags/"]:hover, [href*="/categories/"]:hover {
color: var(--ifk-accent-hover);
text-decoration: underline;
}
/* Table of Contents */
#TableOfContents {
font-family: system-ui, -apple-system, sans-serif;
font-size: 0.95rem;
line-height: 1.7;
}
#TableOfContents a {
color: var(--ifk-accent);
text-decoration: none;
}
#TableOfContents a:hover {
text-decoration: underline;
}
/* Status badges (for shortcodes) */
.csm-enforced { background: var(--ifk-accent); color: white; }
.csm-passed { background: var(--ifk-secondary); color: white; }
.csm-progress { background: #86efac; color: #14532d; }
.csm-guidelines { background: #d1fae5; color: #065f46; }
/* Shortcode-specific overrides */
.ifk-stat-num { font-family: var(--ifk-font); }
.ifk-stat-label { font-family: system-ui, -apple-system, sans-serif; }
.ifk-chart-card h4 { font-family: system-ui, -apple-system, sans-serif; }
/* Responsive design */
@media (max-width: 768px) {
h1, .article-title {
font-size: 2rem;
}
h2 {
font-size: 1.5rem;
}
article p, li {
font-size: 1.1rem;
}
}

View File

@@ -28,23 +28,23 @@ contentDir = "content/de"
[[menus.main]]
identifier = "about"
name = "Über uns"
url = "/about/"
pageRef = "/about"
weight = 2
[[menus.main]]
identifier = "tags"
name = "Themen"
url = "/tags/"
pageRef = "/tags"
weight = 3
[[menus.footer]]
identifier = "impressum"
name = "Impressum"
url = "/impressum/"
pageRef = "/impressum"
weight = 1
[[menus.footer]]
identifier = "privacy"
name = "Datenschutzerklärung"
url = "/datenschutz/"
pageRef = "/datenschutz"
weight = 2

View File

@@ -28,23 +28,23 @@ contentDir = "content/en"
[[menus.main]]
identifier = "about"
name = "About"
url = "/about/"
pageRef = "/about"
weight = 2
[[menus.main]]
identifier = "tags"
name = "Topics"
url = "/tags/"
pageRef = "/tags"
weight = 3
[[menus.footer]]
identifier = "impressum"
name = "Imprint"
url = "/imprint/"
pageRef = "/imprint"
weight = 1
[[menus.footer]]
identifier = "privacy"
name = "Privacy Policy"
url = "/privacy/"
pageRef = "/privacy"
weight = 2

View File

@@ -28,23 +28,23 @@ contentDir = "content/fr"
[[menus.main]]
identifier = "about"
name = "À propos"
url = "/about/"
pageRef = "/about"
weight = 2
[[menus.main]]
identifier = "tags"
name = "Sujets"
url = "/tags/"
pageRef = "/tags"
weight = 3
[[menus.footer]]
identifier = "impressum"
name = "Mentions légales"
url = "/mentions-legales/"
pageRef = "/mentions-legales"
weight = 1
[[menus.footer]]
identifier = "privacy"
name = "Politique de confidentialité"
url = "/politique-de-confidentialite/"
pageRef = "/politique-de-confidentialite"
weight = 2

View File

@@ -1,34 +1,18 @@
colorScheme = "avocado"
colorScheme = "light"
defaultAppearance = "light"
autoSwitchAppearance = false
mainSections = [""]
# Blox Core config
minimal = false
[header]
layout = "basic"
showTitle = true
navbar = true
[footer]
showCopyright = true
showThemeAttribution = false
showScrollToTop = true
showAppearanceSwitcher = false
copyright = "© Internet for Kids"
privacyPolicy = "/privacy"
[homepage]
layout = "profile"
showRecent = true
recentLimit = 5
[article]
showDate = true
showReadingTime = true
showAuthor = true
showBreadcrumbs = true
showTableOfContents = true
showTaxonomies = true
[list]
showBreadcrumbs = true
showTaxonomies = true
[taxonomy]
showTermCount = true
[appearance]
color_theme = "ifk"
font_theme = "ifk_fonts"

View File

@@ -13,8 +13,16 @@ Regierungen auf der ganzen Welt ergreifen beispiellose Maßnahmen zum Schutz von
Hier erfahren Sie, was Sie über die wichtigsten Kinderschutzgesetze wissen müssen, die in diesem Jahr in Kraft treten.
{{< stats-banner >}}
{{< world-map >}}
{{< child-safety-map >}}
{{< law-charts >}}
{{< legislation-timeline >}}
## Australien: Das erste Land mit Social-Media-Verbot für unter 16-Jährige
Im Dezember 2025 wurde Australien das erste Land der Welt, das ein umfassendes Social-Media-Verbot für Nutzer unter 16 Jahren durchsetzt. Der *Online Safety Amendment (Social Media Minimum Age) Act 2024* verpflichtet Plattformen wie TikTok, Instagram, Snapchat, YouTube und X (ehemals Twitter), „angemessene Maßnahmen" zu ergreifen, um Minderjährige an der Erstellung oder Beibehaltung von Konten zu hindern.

View File

@@ -13,8 +13,16 @@ Governments around the world are taking unprecedented action to protect children
Here is what you need to know about the most important child protection laws taking effect this year.
{{< stats-banner >}}
{{< world-map >}}
{{< child-safety-map >}}
{{< law-charts >}}
{{< legislation-timeline >}}
## Australia: The First Country to Ban Social Media for Under-16s
In December 2025, Australia became the first country in the world to enforce a blanket social media ban for users under 16. The *Online Safety Amendment (Social Media Minimum Age) Act 2024* requires platforms like TikTok, Instagram, Snapchat, YouTube, and X (formerly Twitter) to take "reasonable steps" to prevent minors from creating or maintaining accounts.

View File

@@ -13,8 +13,16 @@ Les gouvernements du monde entier prennent des mesures sans précédent pour pro
Voici ce que vous devez savoir sur les lois de protection de l'enfance les plus importantes qui entrent en vigueur cette année.
{{< stats-banner >}}
{{< world-map >}}
{{< child-safety-map >}}
{{< law-charts >}}
{{< legislation-timeline >}}
## Australie : le premier pays à interdire les réseaux sociaux aux moins de 16 ans
En décembre 2025, l'Australie est devenue le premier pays au monde à appliquer une interdiction générale des réseaux sociaux pour les utilisateurs de moins de 16 ans. Le *Online Safety Amendment (Social Media Minimum Age) Act 2024* oblige les plateformes comme TikTok, Instagram, Snapchat, YouTube et X (anciennement Twitter) à prendre des « mesures raisonnables » pour empêcher les mineurs de créer ou de maintenir des comptes.

86
data/countries.json Normal file
View File

@@ -0,0 +1,86 @@
[
{"iso3": "AUS", "isoNum": "036", "flag": "🇦🇺", "status": "enforced", "year": 2024, "ageLimitSocial": 16,
"en": {"name": "Australia", "law": "Social Media Minimum Age Act 2024", "detail": "Social media ban for under-16s. Fines up to AUD 49.5M."},
"de": {"name": "Australien", "law": "Social Media Minimum Age Act 2024", "detail": "Social-Media-Verbot für unter 16-Jährige. Bußgelder bis 49,5 Mio. AUD."},
"fr": {"name": "Australie", "law": "Social Media Minimum Age Act 2024", "detail": "Interdiction des réseaux sociaux pour les moins de 16 ans. Amendes jusqu'à 49,5 M AUD."}},
{"iso3": "GBR", "isoNum": "826", "flag": "🇬🇧", "status": "enforced", "year": 2023, "ageLimitSocial": 13,
"en": {"name": "United Kingdom", "law": "Online Safety Act 2023", "detail": "Comprehensive online safety regime. Age verification required."},
"de": {"name": "Vereinigtes Königreich", "law": "Online Safety Act 2023", "detail": "Umfassendes Online-Sicherheitsregime. Altersverifikation erforderlich."},
"fr": {"name": "Royaume-Uni", "law": "Online Safety Act 2023", "detail": "Régime complet de sécurité en ligne. Vérification d'âge obligatoire."}},
{"iso3": "DEU", "isoNum": "276", "flag": "🇩🇪", "status": "enforced", "year": 2021, "ageLimitSocial": 13,
"en": {"name": "Germany", "law": "Jugendschutzgesetz (JuSchG)", "detail": "Youth Protection Act extended to online platforms. BzKJ monitoring."},
"de": {"name": "Deutschland", "law": "Jugendschutzgesetz (JuSchG)", "detail": "Auf Online-Plattformen ausgeweitet. BzKJ überwacht aktiv die Einhaltung."},
"fr": {"name": "Allemagne", "law": "Jugendschutzgesetz (JuSchG)", "detail": "Loi sur la protection de la jeunesse étendue aux plateformes en ligne. Surveillance BzKJ."}},
{"iso3": "CHN", "isoNum": "156", "flag": "🇨🇳", "status": "enforced", "year": 2021, "ageLimitSocial": 14,
"en": {"name": "China", "law": "Minor Protection Law", "detail": "1hr/day gaming for minors. Real-name verification mandatory."},
"de": {"name": "China", "law": "Minderjährigenschutzgesetz", "detail": "1 Std./Tag Gaming für Minderjährige. Echtnamen-Verifizierung verpflichtend."},
"fr": {"name": "Chine", "law": "Loi sur la protection des mineurs", "detail": "1h/jour de jeux vidéo pour les mineurs. Vérification d'identité obligatoire."}},
{"iso3": "KOR", "isoNum": "410", "flag": "🇰🇷", "status": "enforced", "year": 2020, "ageLimitSocial": 14,
"en": {"name": "South Korea", "law": "Youth Protection Act", "detail": "Long-standing youth protection framework. Real-name verification."},
"de": {"name": "Südkorea", "law": "Jugendschutzgesetz", "detail": "Langjähriger Jugendschutzrahmen. Echtnamen-Verifizierung für Online-Dienste."},
"fr": {"name": "Corée du Sud", "law": "Loi sur la protection de la jeunesse", "detail": "Cadre de protection de la jeunesse établi de longue date. Vérification d'identité."}},
{"iso3": "IRL", "isoNum": "372", "flag": "🇮🇪", "status": "enforced", "year": 2022, "ageLimitSocial": 13,
"en": {"name": "Ireland", "law": "Online Safety and Media Regulation Act", "detail": "Coimisiún na Meán regulates online safety with binding codes."},
"de": {"name": "Irland", "law": "Online Safety and Media Regulation Act", "detail": "Coimisiún na Meán reguliert Online-Sicherheit mit verbindlichen Kodizes."},
"fr": {"name": "Irlande", "law": "Online Safety and Media Regulation Act", "detail": "Coimisiún na Meán régule la sécurité en ligne avec des codes contraignants."}},
{"iso3": "NLD", "isoNum": "528", "flag": "🇳🇱", "status": "enforced", "year": 2024, "ageLimitSocial": 16,
"en": {"name": "Netherlands", "law": "DSA + Children's Code", "detail": "Dutch DPA actively enforcing children's data protection."},
"de": {"name": "Niederlande", "law": "DSA + Kinderkodex", "detail": "Datenschutzbehörde setzt Kinderdatenschutz aktiv durch."},
"fr": {"name": "Pays-Bas", "law": "DSA + Code de l'enfance", "detail": "Autorité de protection des données applique activement la protection des données des enfants."}},
{"iso3": "SWE", "isoNum": "752", "flag": "🇸🇪", "status": "enforced", "year": 2024, "ageLimitSocial": 13,
"en": {"name": "Sweden", "law": "DSA + National Youth Guidelines", "detail": "EU DSA framework plus national youth protection guidelines."},
"de": {"name": "Schweden", "law": "DSA + Nationale Jugendrichtlinien", "detail": "EU-DSA-Rahmen plus nationale Jugendschutzrichtlinien."},
"fr": {"name": "Suède", "law": "DSA + Directives nationales jeunesse", "detail": "Cadre DSA de l'UE plus directives nationales de protection de la jeunesse."}},
{"iso3": "FRA", "isoNum": "250", "flag": "🇫🇷", "status": "passed", "year": 2026, "ageLimitSocial": 15,
"en": {"name": "France", "law": "Loi SREN + Social Media Ban", "detail": "Senate votes to ban social media for under-15s (April 2026). Arcom blacklist."},
"de": {"name": "Frankreich", "law": "Loi SREN + Social-Media-Verbot", "detail": "Senat stimmt für Social-Media-Verbot unter 15 (April 2026). Arcom-Blacklist."},
"fr": {"name": "France", "law": "Loi SREN + Interdiction réseaux sociaux", "detail": "Le Sénat vote l'interdiction des réseaux sociaux pour les moins de 15 ans (avril 2026)."}},
{"iso3": "BRA", "isoNum": "076", "flag": "🇧🇷", "status": "passed", "year": 2025, "ageLimitSocial": 12,
"en": {"name": "Brazil", "law": "ECA Digital", "detail": "Loot box ban for minors. Age-appropriate design requirements."},
"de": {"name": "Brasilien", "law": "ECA Digital", "detail": "Lootbox-Verbot für Minderjährige. Altersgerechtes Design."},
"fr": {"name": "Brésil", "law": "ECA Digital", "detail": "Interdiction des loot boxes pour les mineurs. Exigences de design adapté à l'âge."}},
{"iso3": "IND", "isoNum": "356", "flag": "🇮🇳", "status": "passed", "year": 2023, "ageLimitSocial": 18,
"en": {"name": "India", "law": "DPDP Act 2023", "detail": "Verifiable parental consent for under-18s."},
"de": {"name": "Indien", "law": "DPDP Act 2023", "detail": "Verifizierbare elterliche Einwilligung für unter 18-Jährige."},
"fr": {"name": "Inde", "law": "DPDP Act 2023", "detail": "Consentement parental vérifiable pour les moins de 18 ans."}},
{"iso3": "ITA", "isoNum": "380", "flag": "🇮🇹", "status": "passed", "year": 2025, "ageLimitSocial": 14,
"en": {"name": "Italy", "law": "DSA + Parental Consent Law", "detail": "Digital age of consent set to 14."},
"de": {"name": "Italien", "law": "DSA + Einwilligungsgesetz", "detail": "Digitales Einwilligungsalter auf 14 festgelegt."},
"fr": {"name": "Italie", "law": "DSA + Loi sur le consentement parental", "detail": "Âge de consentement numérique fixé à 14 ans."}},
{"iso3": "ESP", "isoNum": "724", "flag": "🇪🇸", "status": "passed", "year": 2025, "ageLimitSocial": 14,
"en": {"name": "Spain", "law": "Child Protection in Digital Environments", "detail": "Comprehensive child digital protection law."},
"de": {"name": "Spanien", "law": "Organisches Kinderschutzgesetz", "detail": "Umfassendes digitales Kinderschutzgesetz."},
"fr": {"name": "Espagne", "law": "Protection de l'enfance dans les environnements numériques", "detail": "Loi complète de protection numérique de l'enfance."}},
{"iso3": "NOR", "isoNum": "578", "flag": "🇳🇴", "status": "passed", "year": 2025, "ageLimitSocial": 15,
"en": {"name": "Norway", "law": "Social Media Age Limit", "detail": "Proposed social media age limit of 15."},
"de": {"name": "Norwegen", "law": "Social-Media-Altersgrenze", "detail": "Geplante Altersgrenze von 15 Jahren."},
"fr": {"name": "Norvège", "law": "Limite d'âge réseaux sociaux", "detail": "Limite d'âge proposée à 15 ans."}},
{"iso3": "USA", "isoNum": "840", "flag": "🇺🇸", "status": "progress", "year": 2026, "ageLimitSocial": 13,
"en": {"name": "United States", "law": "KOSA + COPPA Update", "detail": "KOSA passed Senate 91-3. COPPA update by Apr 2026."},
"de": {"name": "Vereinigte Staaten", "law": "KOSA + COPPA-Update", "detail": "KOSA im Senat angenommen. COPPA-Update bis Apr. 2026."},
"fr": {"name": "États-Unis", "law": "KOSA + Mise à jour COPPA", "detail": "KOSA adopté au Sénat 91-3. Mise à jour COPPA prévue avr. 2026."}},
{"iso3": "CAN", "isoNum": "124", "flag": "🇨🇦", "status": "progress", "year": 2026, "ageLimitSocial": 13,
"en": {"name": "Canada", "law": "Online Harms Act (C-63)", "detail": "Proposed duty of care for platforms."},
"de": {"name": "Kanada", "law": "Online Harms Act (C-63)", "detail": "Sorgfaltspflicht für Plattformen in Prüfung."},
"fr": {"name": "Canada", "law": "Online Harms Act (C-63)", "detail": "Devoir de diligence proposé pour les plateformes."}},
{"iso3": "JPN", "isoNum": "392", "flag": "🇯🇵", "status": "guidelines", "year": 2024, "ageLimitSocial": 13,
"en": {"name": "Japan", "law": "Act on Regulation of Soliciting Children", "detail": "Platform self-regulation encouraged."},
"de": {"name": "Japan", "law": "Regulierung der Kontaktaufnahme", "detail": "Selbstregulierung der Plattformen empfohlen."},
"fr": {"name": "Japon", "law": "Loi sur la régulation de la sollicitation des enfants", "detail": "Autorégulation des plateformes encouragée."}}
]

5
go.mod Normal file
View File

@@ -0,0 +1,5 @@
module github.com/christian/internetforkids
go 1.26.1
require github.com/HugoBlox/hugo-blox-builder/modules/blox-core v0.4.1 // indirect

2
go.sum Normal file
View File

@@ -0,0 +1,2 @@
github.com/HugoBlox/hugo-blox-builder/modules/blox-core v0.4.1 h1:LAIEtt5FVZVJoKVmZ+rB/M5exoek52ksCEK12HWssIo=
github.com/HugoBlox/hugo-blox-builder/modules/blox-core v0.4.1/go.mod h1:So8+V2U+TNxlXmcpZfdDX0muLh3PdJ7z92h30sv3bZg=

View File

@@ -1,16 +1,19 @@
baseURL = "https://internetforkids.ong/"
baseURL = "https://internetforkids.org/"
title = "Internet for Kids"
theme = "congo"
defaultContentLanguage = "en"
defaultContentLanguageInSubdir = true
enableRobotsTXT = true
[module]
[[module.imports]]
path = "github.com/HugoBlox/hugo-blox-builder/modules/blox-core"
[markup]
[markup.goldmark]
[markup.goldmark.renderer]
unsafe = false
unsafe = true
[markup.highlight]
style = "monokai"

17
i18n/de.toml Normal file
View File

@@ -0,0 +1,17 @@
[recent_posts]
other = "Neueste Artikel"
[read_more]
other = "Weiterlesen"
[table_of_contents]
other = "Inhaltsverzeichnis"
[tags]
other = "Schlagwörter"
[previous]
other = "Vorheriger"
[next]
other = "Nächster"

17
i18n/en.toml Normal file
View File

@@ -0,0 +1,17 @@
[recent_posts]
other = "Recent Articles"
[read_more]
other = "Read more"
[table_of_contents]
other = "Table of Contents"
[tags]
other = "Tags"
[previous]
other = "Previous"
[next]
other = "Next"

17
i18n/fr.toml Normal file
View File

@@ -0,0 +1,17 @@
[recent_posts]
other = "Articles récents"
[read_more]
other = "Lire la suite"
[table_of_contents]
other = "Table des matières"
[tags]
other = "Mots-clés"
[previous]
other = "Précédent"
[next]
other = "Suivant"

View File

@@ -0,0 +1,58 @@
<!DOCTYPE html>
<html lang="{{ .Language.LanguageCode | default "en" }}" dir="{{ .Language.LanguageDirection | default "ltr" }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="{{ .Description | default .Summary | plainify | truncate 160 }}">
<meta name="author" content="{{ .Params.author | default "Internet for Kids" }}">
<title>{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} — {{ .Site.Title }}{{ end }}</title>
{{ partial "extend-head.html" . }}
{{/* Main stylesheet with Newsreader + custom colors */}}
<link rel="stylesheet" href="{{ "css/main.css" | relURL }}">
</head>
<body style="background-color:var(--ifk-bg);color:var(--ifk-text)">
<header style="border-bottom:1px solid #e5e7eb;background:white;position:sticky;top:0;z-index:40">
<nav style="max-width:64rem;margin:0 auto;padding:1rem;display:flex;justify-content:space-between;align-items:center">
<a href="{{ "/" | relLangURL }}" style="font-family:var(--ifk-font);font-size:1.35rem;font-weight:600;color:var(--ifk-text);text-decoration:none">
{{ .Site.Title }}
</a>
<div style="display:flex;align-items:center;gap:1.5rem;font-family:system-ui,-apple-system,sans-serif;font-size:0.95rem">
{{ range .Site.Menus.main }}
{{ if ne .Identifier "locale" }}
<a href="{{ .URL | relLangURL }}" style="color:#4b5563;text-decoration:none">{{ .Name }}</a>
{{ end }}
{{ end }}
{{ partial "language-switcher.html" . }}
</div>
</nav>
</header>
<main style="flex:1">
{{ block "main" . }}{{ end }}
</main>
<footer style="border-top:1px solid #e5e7eb;background:white;margin-top:5rem">
<div style="max-width:64rem;margin:0 auto;padding:3rem 1rem">
<div style="display:flex;gap:2rem;flex-wrap:wrap;margin-bottom:2rem">
{{ range .Site.Menus.footer }}
<a href="{{ .URL | relLangURL }}" style="color:#6b7280;text-decoration:none;font-family:system-ui,sans-serif;font-size:0.875rem">
{{ .Name }}
</a>
{{ end }}
</div>
<div style="border-top:1px solid #e5e7eb;padding-top:2rem;text-align:center;font-size:0.85rem;color:#6b7280;font-family:system-ui,sans-serif">
<p>
&copy; {{ now.Year }} {{ .Site.Title }}. A publication by <a href="https://www.agiliton.eu" style="color:var(--ifk-accent);text-decoration:none">Agiliton</a>.
</p>
{{ partial "extend-footer.html" . }}
</div>
</div>
</footer>
{{ partial "rybbit.html" . }}
{{ partial "structured-data.html" . }}
</body>
</html>

View File

@@ -0,0 +1,30 @@
{{ define "main" }}
<article class="prose max-w-3xl mx-auto py-12 px-4">
{{ .Content }}
<div class="mt-12">
<h2>{{ i18n "recent_posts" | default "Recent Articles" }}</h2>
<div class="grid gap-8">
{{ range first 5 .Site.RegularPages }}
<article class="border-b pb-8">
<h3 class="text-2xl font-newsreader mb-2">
<a href="{{ .Permalink }}" class="text-ifk-accent hover:text-ifk-accent-hover">
{{ .Title }}
</a>
</h3>
<div class="text-sm text-gray-600 mb-4">
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "January 2, 2006" }}</time>
{{ with .Params.author }}— {{ . }}{{ end }}
</div>
<p class="text-lg font-newsreader leading-relaxed">
{{ .Summary }}
</p>
<a href="{{ .Permalink }}" class="inline-block mt-4 text-ifk-accent hover:text-ifk-accent-hover font-medium">
{{ i18n "read_more" | default "Read more" }} →
</a>
</article>
{{ end }}
</div>
</div>
</article>
{{ end }}

View File

@@ -0,0 +1,35 @@
{{ define "main" }}
<div class="max-w-3xl mx-auto py-12 px-4">
<header class="mb-12">
<h1 class="text-4xl font-newsreader font-semibold mb-4">{{ .Title }}</h1>
{{ with .Description }}
<p class="text-lg text-gray-600">{{ . }}</p>
{{ end }}
</header>
<div class="space-y-8">
{{ range .Paginator.Pages }}
<article class="pb-8 border-b last:border-b-0">
<h2 class="text-2xl font-newsreader font-semibold mb-2">
<a href="{{ .Permalink }}" class="text-ifk-accent hover:text-ifk-accent-hover">
{{ .Title }}
</a>
</h2>
<div class="text-sm text-gray-600 mb-3">
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "January 2, 2006" }}</time>
{{ with .Params.author }}— {{ . }}{{ end }}
{{ if .ReadingTime }}— {{ .ReadingTime }} min read{{ end }}
</div>
<p class="text-lg font-newsreader leading-relaxed mb-3">
{{ .Summary }}
</p>
<a href="{{ .Permalink }}" class="inline-block text-ifk-accent hover:text-ifk-accent-hover font-medium">
{{ i18n "read_more" | default "Read more" }} →
</a>
</article>
{{ end }}
</div>
{{ template "_internal/pagination.html" . }}
</div>
{{ end }}

View File

@@ -0,0 +1,102 @@
{{ define "main" }}
<div style="max-width:64rem;margin:0 auto;padding:3rem 1rem;display:grid;grid-template-columns:1fr 240px;gap:3rem">
{{/* Main article column */}}
<article>
<header style="margin-bottom:2rem">
<h1 style="font-family:var(--ifk-font);font-size:2.75rem;font-weight:600;line-height:1.15;letter-spacing:-0.01em;margin-bottom:1rem">
{{ .Title }}
</h1>
<div style="color:#6b7280;font-family:system-ui,-apple-system,sans-serif;font-size:0.95rem;display:flex;gap:1rem;flex-wrap:wrap;align-items:center">
{{ if .Date }}
<time datetime="{{ .Date.Format "2006-01-02" }}">
{{ .Date.Format "January 2, 2006" }}
</time>
{{ end }}
{{ with .Params.author }}
<span>{{ . }}</span>
{{ end }}
{{ if .ReadingTime }}
<span>{{ .ReadingTime }} min read</span>
{{ end }}
</div>
{{ with .Params.tags }}
<div style="margin-top:0.75rem;display:flex;gap:0.5rem;flex-wrap:wrap">
{{ range . }}
<a href="{{ "/tags/" | relLangURL }}{{ . | urlize }}" style="font-family:system-ui,sans-serif;font-size:0.75rem;text-transform:uppercase;letter-spacing:0.05em;color:var(--ifk-accent);text-decoration:none;border:1px solid #e5e7eb;padding:0.2rem 0.6rem;border-radius:4px">
{{ . }}
</a>
{{ end }}
</div>
{{ end }}
</header>
<div class="article-body" style="font-family:var(--ifk-font);font-size:1.375rem;line-height:1.65">
{{ .Content }}
</div>
{{ if or .NextInSection .PrevInSection }}
<nav style="margin-top:3rem;padding-top:2rem;border-top:1px solid #e5e7eb;display:flex;justify-content:space-between;gap:1rem">
{{ with .PrevInSection }}
<a href="{{ .Permalink }}" style="flex:1;padding:1rem;border:1px solid #e5e7eb;border-radius:6px;text-decoration:none;color:var(--ifk-text)">
<div style="font-size:0.75rem;font-family:system-ui,sans-serif;text-transform:uppercase;color:#6b7280;margin-bottom:0.25rem">&larr; {{ i18n "previous" | default "Previous" }}</div>
<div style="font-family:var(--ifk-font);font-size:1.1rem">{{ .Title }}</div>
</a>
{{ end }}
{{ with .NextInSection }}
<a href="{{ .Permalink }}" style="flex:1;padding:1rem;border:1px solid #e5e7eb;border-radius:6px;text-decoration:none;color:var(--ifk-text);text-align:right">
<div style="font-size:0.75rem;font-family:system-ui,sans-serif;text-transform:uppercase;color:#6b7280;margin-bottom:0.25rem">{{ i18n "next" | default "Next" }} &rarr;</div>
<div style="font-family:var(--ifk-font);font-size:1.1rem">{{ .Title }}</div>
</a>
{{ end }}
</nav>
{{ end }}
</article>
{{/* Sidebar */}}
<aside style="font-family:system-ui,-apple-system,sans-serif">
{{/* Table of Contents */}}
{{ if .TableOfContents }}
<div style="position:sticky;top:5rem">
<h3 style="font-size:0.75rem;text-transform:uppercase;letter-spacing:0.08em;color:#6b7280;font-weight:600;margin-bottom:0.75rem">
{{ i18n "table_of_contents" | default "Contents" }}
</h3>
<div style="font-size:0.85rem;line-height:1.7;border-left:2px solid #e5e7eb;padding-left:0.75rem">
{{ .TableOfContents }}
</div>
{{/* Recent articles */}}
{{ $pages := where .Site.RegularPages "Section" .Section }}
{{ $recent := first 5 $pages }}
{{ if gt (len $recent) 1 }}
<h3 style="font-size:0.75rem;text-transform:uppercase;letter-spacing:0.08em;color:#6b7280;font-weight:600;margin-top:2rem;margin-bottom:0.75rem">
{{ i18n "recent_articles" | default "Recent" }}
</h3>
<div style="font-size:0.85rem;line-height:1.6">
{{ range $recent }}
{{ if ne .Permalink $.Permalink }}
<a href="{{ .Permalink }}" style="display:block;color:var(--ifk-text);text-decoration:none;padding:0.4rem 0;border-bottom:1px solid #f3f4f6">
{{ .Title }}
</a>
{{ end }}
{{ end }}
</div>
{{ end }}
</div>
{{ end }}
</aside>
</div>
<style>
@media (max-width: 768px) {
div[style*="grid-template-columns:1fr 240px"] {
display: block !important;
}
div[style*="grid-template-columns:1fr 240px"] > aside {
display: none;
}
}
</style>
{{ end }}

View File

@@ -1,9 +1,23 @@
{{ if .IsTranslated }}
<nav class="language-switcher" aria-label="Language">
{{ range .Translations }}
<a href="{{ .Permalink }}" lang="{{ .Language.Lang }}" hreflang="{{ .Language.Lang }}">
<div class="lang-switch" style="position:relative">
<button onclick="this.parentElement.classList.toggle('open')" style="background:none;border:1px solid #e5e7eb;border-radius:6px;padding:0.35rem 0.75rem;cursor:pointer;font-family:system-ui,-apple-system,sans-serif;font-size:0.875rem;color:#4b5563;display:flex;align-items:center;gap:0.4rem">
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" style="opacity:0.6"><path d="M4.545 6.714L4.11 8H3l1.862-5h1.284L8 8H6.833l-.435-1.286H4.545zm1.634-.736L5.5 3.956h-.049l-.679 2.022H6.18z"/><path d="M0 2a2 2 0 012-2h7a2 2 0 012 2v3h3a2 2 0 012 2v7a2 2 0 01-2 2H7a2 2 0 01-2-2v-3H2A2 2 0 010 9V2zm2-1A1 1 0 001 2v7a1 1 0 001 1h7a1 1 0 001-1V2A1 1 0 009 1H2zm7.138 9.995c.193.301.402.583.63.846-.748.575-1.673 1.001-2.768 1.292.178.217.451.635.555.867 1.125-.359 2.08-.844 2.886-1.494.777.665 1.739 1.165 2.93 1.472.133-.254.414-.673.629-.89-1.125-.253-2.057-.694-2.82-1.284.681-.747 1.222-1.651 1.621-2.757H14V8h-3v1.047h.765c-.318.844-.74 1.546-1.272 2.13a6.066 6.066 0 01-.415-.492 1.988 1.988 0 01-.94.31z"/></svg>
{{ .Language.LanguageName }}
</a>
{{ end }}
</nav>
<svg width="10" height="10" viewBox="0 0 10 10" fill="currentColor" style="opacity:0.5"><path d="M2 3.5L5 7l3-3.5H2z"/></svg>
</button>
<div class="lang-dropdown" style="display:none;position:absolute;right:0;top:calc(100% + 4px);background:white;border:1px solid #e5e7eb;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,0.1);overflow:hidden;min-width:140px;z-index:50">
<a href="{{ .Permalink }}" style="display:block;padding:0.5rem 1rem;font-size:0.875rem;font-family:system-ui,sans-serif;color:#4d7c0f;background:#f0fdf4;text-decoration:none;font-weight:500">
{{ .Language.LanguageName }} ✓
</a>
{{ range .Translations }}
<a href="{{ .Permalink }}" lang="{{ .Language.Lang }}" hreflang="{{ .Language.Lang }}" style="display:block;padding:0.5rem 1rem;font-size:0.875rem;font-family:system-ui,sans-serif;color:#374151;text-decoration:none">
{{ .Language.LanguageName }}
</a>
{{ end }}
</div>
</div>
<style>
.lang-switch.open .lang-dropdown { display:block !important; }
.lang-dropdown a:hover { background:#f9fafb; }
</style>
{{ end }}

View File

@@ -7,7 +7,7 @@
Agiliton VPN provides content filtering, device protection, and safe browsing for the whole family.
{{ end }}
</p>
<a href="https://go.agiliton.eu/vpn" style="display: inline-block; background: #fff; color: #1a365d; padding: 0.75rem 2rem; border-radius: 8px; text-decoration: none; font-weight: 600;">
<a href="https://go.agiliton.eu/vpn-app" style="display: inline-block; background: #fff; color: #1a365d; padding: 0.75rem 2rem; border-radius: 8px; text-decoration: none; font-weight: 600;">
{{ if eq .Language.Lang "de" }}Mehr erfahren{{ else }}Learn More{{ end }}
</a>
</aside>

View File

@@ -1,75 +1,31 @@
{{ $lang := .Page.Language.Lang }}
{{ $countries := hugo.Data.countries }}
{{ $statusClass := dict "enforced" "csm-enforced" "passed" "csm-passed" "progress" "csm-progress" "guidelines" "csm-guidelines" }}
{{ $statusLabel := dict
"enforced" (dict "en" "Enforced" "de" "In Kraft" "fr" "En vigueur")
"passed" (dict "en" "Passed" "de" "Verabschiedet" "fr" "Adopté")
"progress" (dict "en" "In Progress" "de" "In Bearbeitung" "fr" "En cours")
"guidelines" (dict "en" "Guidelines" "de" "Richtlinien" "fr" "Directives")
}}
<style>
.csm-container {
max-width: 100%;
margin: 2rem 0;
}
.csm-container h3 {
text-align: center;
margin-bottom: 0.5rem;
font-size: 1.25rem;
}
.csm-subtitle {
text-align: center;
color: #666;
font-size: 0.85rem;
margin-bottom: 1rem;
}
.csm-table {
width: 100%;
border-collapse: collapse;
font-size: 0.95rem;
}
.csm-table th {
background: #1e40af;
color: white;
padding: 0.75rem 1rem;
text-align: left;
font-weight: 600;
}
.csm-table td {
padding: 0.65rem 1rem;
border-bottom: 1px solid #e2e8f0;
vertical-align: top;
}
.csm-table tr:hover {
background: #f0fdf4;
}
.csm-status {
display: inline-block;
padding: 0.15rem 0.5rem;
border-radius: 4px;
font-size: 0.8rem;
font-weight: 600;
white-space: nowrap;
}
.csm-container { max-width: 100%; margin: 2rem 0; }
.csm-container h3 { text-align: center; margin-bottom: 0.5rem; font-size: 1.25rem; }
.csm-subtitle { text-align: center; color: #666; font-size: 0.85rem; margin-bottom: 1rem; }
.csm-table { width: 100%; border-collapse: collapse; font-size: 0.95rem; }
.csm-table th { background: #1e40af; color: white; padding: 0.75rem 1rem; text-align: left; font-weight: 600; }
.csm-table td { padding: 0.65rem 1rem; border-bottom: 1px solid #e2e8f0; vertical-align: top; }
.csm-table tr:hover { background: #f0fdf4; }
.csm-status { display: inline-block; padding: 0.15rem 0.5rem; border-radius: 4px; font-size: 0.8rem; font-weight: 600; white-space: nowrap; }
.csm-enforced { background: #1e40af; color: white; }
.csm-passed { background: #3b82f6; color: white; }
.csm-progress { background: #93c5fd; color: #1e3a5f; }
.csm-guidelines { background: #dbeafe; color: #1e3a5f; }
.csm-legend {
display: flex;
flex-wrap: wrap;
gap: 1rem;
justify-content: center;
margin-top: 1.5rem;
font-size: 0.8rem;
}
.csm-legend-item {
display: flex;
align-items: center;
gap: 0.35rem;
}
.csm-legend-swatch {
width: 14px;
height: 14px;
border-radius: 3px;
border: 1px solid rgba(0,0,0,0.1);
}
@media (max-width: 640px) {
.csm-table { font-size: 0.85rem; }
.csm-table th, .csm-table td { padding: 0.5rem; }
}
.csm-legend { display: flex; flex-wrap: wrap; gap: 1rem; justify-content: center; margin-top: 1.5rem; font-size: 0.8rem; }
.csm-legend-item { display: flex; align-items: center; gap: 0.35rem; }
.csm-legend-swatch { width: 14px; height: 14px; border-radius: 3px; border: 1px solid rgba(0,0,0,0.1); }
@media (max-width: 640px) { .csm-table { font-size: 0.85rem; } .csm-table th, .csm-table td { padding: 0.5rem; } }
</style>
<div class="csm-container">
@@ -86,60 +42,16 @@
</tr>
</thead>
<tbody>
{{ if eq $lang "de" }}
<tr><td>🇦🇺 Australien</td><td>Social Media Minimum Age Act 2024</td><td><span class="csm-status csm-enforced">In Kraft</span></td><td>Social-Media-Verbot für unter 16-Jährige. Bußgelder bis 49,5 Mio. AUD.</td></tr>
<tr><td>🇬🇧 Vereinigtes Königreich</td><td>Online Safety Act 2023</td><td><span class="csm-status csm-enforced">In Kraft</span></td><td>Umfassendes Online-Sicherheitsregime. Altersverifikation erforderlich.</td></tr>
<tr><td>🇩🇪 Deutschland</td><td>Jugendschutzgesetz (JuSchG)</td><td><span class="csm-status csm-enforced">In Kraft</span></td><td>Auf Online-Plattformen ausgeweitet. BzKJ überwacht aktiv die Einhaltung.</td></tr>
<tr><td>🇨🇳 China</td><td>Minderjährigenschutzgesetz</td><td><span class="csm-status csm-enforced">In Kraft</span></td><td>1 Std./Tag Gaming für Minderjährige. Echtnamen-Verifizierung verpflichtend.</td></tr>
<tr><td>🇰🇷 Südkorea</td><td>Jugendschutzgesetz</td><td><span class="csm-status csm-enforced">In Kraft</span></td><td>Langjähriger Jugendschutzrahmen. Echtnamen-Verifizierung für Online-Dienste.</td></tr>
<tr><td>🇮🇪 Irland</td><td>Online Safety and Media Regulation Act</td><td><span class="csm-status csm-enforced">In Kraft</span></td><td>Coimisiún na Meán reguliert Online-Sicherheit mit verbindlichen Kodizes.</td></tr>
<tr><td>🇳🇱 Niederlande</td><td>DSA + Kinderkodex</td><td><span class="csm-status csm-enforced">In Kraft</span></td><td>Datenschutzbehörde setzt Kinderdatenschutz aktiv durch.</td></tr>
<tr><td>🇸🇪 Schweden</td><td>DSA + Nationale Jugendrichtlinien</td><td><span class="csm-status csm-enforced">In Kraft</span></td><td>EU-DSA-Rahmen plus nationale Jugendschutzrichtlinien.</td></tr>
<tr><td>🇫🇷 Frankreich</td><td>Loi SREN + Social-Media-Verbot</td><td><span class="csm-status csm-passed">Verabschiedet</span></td><td>Senat stimmt für Social-Media-Verbot unter 15 (April 2026). Arcom-Blacklist. Vermittlung läuft.</td></tr>
<tr><td>🇧🇷 Brasilien</td><td>ECA Digital</td><td><span class="csm-status csm-passed">Verabschiedet</span></td><td>Lootbox-Verbot für Minderjährige. Altersgerechtes Design.</td></tr>
<tr><td>🇮🇳 Indien</td><td>DPDP Act 2023</td><td><span class="csm-status csm-passed">Verabschiedet</span></td><td>Verifizierbare elterliche Einwilligung für unter 18-Jährige.</td></tr>
<tr><td>🇮🇹 Italien</td><td>DSA + Einwilligungsgesetz</td><td><span class="csm-status csm-passed">Verabschiedet</span></td><td>Digitales Einwilligungsalter auf 14 festgelegt.</td></tr>
<tr><td>🇪🇸 Spanien</td><td>Organisches Kinderschutzgesetz</td><td><span class="csm-status csm-passed">Verabschiedet</span></td><td>Umfassendes digitales Kinderschutzgesetz.</td></tr>
<tr><td>🇳🇴 Norwegen</td><td>Social-Media-Altersgrenze</td><td><span class="csm-status csm-passed">Verabschiedet</span></td><td>Geplante Altersgrenze von 15 Jahren.</td></tr>
<tr><td>🇺🇸 Vereinigte Staaten</td><td>KOSA + COPPA-Update</td><td><span class="csm-status csm-progress">In Bearbeitung</span></td><td>KOSA im Senat angenommen. COPPA-Update bis Apr. 2026.</td></tr>
<tr><td>🇨🇦 Kanada</td><td>Online Harms Act (C-63)</td><td><span class="csm-status csm-progress">In Bearbeitung</span></td><td>Sorgfaltspflicht für Plattformen in Prüfung.</td></tr>
<tr><td>🇯🇵 Japan</td><td>Regulierung der Kontaktaufnahme</td><td><span class="csm-status csm-guidelines">Richtlinien</span></td><td>Selbstregulierung der Plattformen empfohlen.</td></tr>
{{ else if eq $lang "fr" }}
<tr><td>🇦🇺 Australie</td><td>Social Media Minimum Age Act 2024</td><td><span class="csm-status csm-enforced">En vigueur</span></td><td>Interdiction des réseaux sociaux pour les moins de 16 ans. Amendes jusqu'à 49,5 M AUD.</td></tr>
<tr><td>🇬🇧 Royaume-Uni</td><td>Online Safety Act 2023</td><td><span class="csm-status csm-enforced">En vigueur</span></td><td>Régime complet de sécurité en ligne. Vérification d'âge obligatoire.</td></tr>
<tr><td>🇩🇪 Allemagne</td><td>Jugendschutzgesetz (JuSchG)</td><td><span class="csm-status csm-enforced">En vigueur</span></td><td>Loi sur la protection de la jeunesse étendue aux plateformes en ligne. Surveillance BzKJ.</td></tr>
<tr><td>🇨🇳 Chine</td><td>Loi sur la protection des mineurs</td><td><span class="csm-status csm-enforced">En vigueur</span></td><td>1h/jour de jeux vidéo pour les mineurs. Vérification d'identité obligatoire.</td></tr>
<tr><td>🇰🇷 Corée du Sud</td><td>Loi sur la protection de la jeunesse</td><td><span class="csm-status csm-enforced">En vigueur</span></td><td>Cadre de protection de la jeunesse établi de longue date. Vérification d'identité.</td></tr>
<tr><td>🇮🇪 Irlande</td><td>Online Safety and Media Regulation Act</td><td><span class="csm-status csm-enforced">En vigueur</span></td><td>Coimisiún na Meán régule la sécurité en ligne avec des codes contraignants.</td></tr>
<tr><td>🇳🇱 Pays-Bas</td><td>DSA + Code de l'enfance</td><td><span class="csm-status csm-enforced">En vigueur</span></td><td>Autorité de protection des données applique activement la protection des données des enfants.</td></tr>
<tr><td>🇸🇪 Suède</td><td>DSA + Directives nationales jeunesse</td><td><span class="csm-status csm-enforced">En vigueur</span></td><td>Cadre DSA de l'UE plus directives nationales de protection de la jeunesse.</td></tr>
<tr><td>🇫🇷 France</td><td>Loi SREN + Interdiction réseaux sociaux</td><td><span class="csm-status csm-passed">Adopté</span></td><td>Le Sénat vote l'interdiction des réseaux sociaux pour les moins de 15 ans (avril 2026). Liste noire Arcom. Conciliation en cours.</td></tr>
<tr><td>🇧🇷 Brésil</td><td>ECA Digital</td><td><span class="csm-status csm-passed">Adopté</span></td><td>Interdiction des loot boxes pour les mineurs. Exigences de design adapté à l'âge.</td></tr>
<tr><td>🇮🇳 Inde</td><td>DPDP Act 2023</td><td><span class="csm-status csm-passed">Adopté</span></td><td>Consentement parental vérifiable pour les moins de 18 ans.</td></tr>
<tr><td>🇮🇹 Italie</td><td>DSA + Loi sur le consentement parental</td><td><span class="csm-status csm-passed">Adopté</span></td><td>Âge de consentement numérique fixé à 14 ans.</td></tr>
<tr><td>🇪🇸 Espagne</td><td>Protection de l'enfance dans les environnements numériques</td><td><span class="csm-status csm-passed">Adopté</span></td><td>Loi complète de protection numérique de l'enfance.</td></tr>
<tr><td>🇳🇴 Norvège</td><td>Limite d'âge réseaux sociaux</td><td><span class="csm-status csm-passed">Adopté</span></td><td>Limite d'âge proposée à 15 ans.</td></tr>
<tr><td>🇺🇸 États-Unis</td><td>KOSA + Mise à jour COPPA</td><td><span class="csm-status csm-progress">En cours</span></td><td>KOSA adopté au Sénat 91-3. Mise à jour COPPA prévue avr. 2026.</td></tr>
<tr><td>🇨🇦 Canada</td><td>Online Harms Act (C-63)</td><td><span class="csm-status csm-progress">En cours</span></td><td>Devoir de diligence proposé pour les plateformes.</td></tr>
<tr><td>🇯🇵 Japon</td><td>Loi sur la régulation de la sollicitation des enfants</td><td><span class="csm-status csm-guidelines">Directives</span></td><td>Autorégulation des plateformes encouragée.</td></tr>
{{ else }}
<tr><td>🇦🇺 Australia</td><td>Social Media Minimum Age Act 2024</td><td><span class="csm-status csm-enforced">Enforced</span></td><td>Social media ban for under-16s. Fines up to AUD 49.5M.</td></tr>
<tr><td>🇬🇧 United Kingdom</td><td>Online Safety Act 2023</td><td><span class="csm-status csm-enforced">Enforced</span></td><td>Comprehensive online safety regime. Age verification required.</td></tr>
<tr><td>🇩🇪 Germany</td><td>Jugendschutzgesetz (JuSchG)</td><td><span class="csm-status csm-enforced">Enforced</span></td><td>Youth Protection Act extended to online platforms. BzKJ monitoring.</td></tr>
<tr><td>🇨🇳 China</td><td>Minor Protection Law</td><td><span class="csm-status csm-enforced">Enforced</span></td><td>1hr/day gaming for minors. Real-name verification mandatory.</td></tr>
<tr><td>🇰🇷 South Korea</td><td>Youth Protection Act</td><td><span class="csm-status csm-enforced">Enforced</span></td><td>Long-standing youth protection framework. Real-name verification.</td></tr>
<tr><td>🇮🇪 Ireland</td><td>Online Safety and Media Regulation Act</td><td><span class="csm-status csm-enforced">Enforced</span></td><td>Coimisiún na Meán regulates online safety with binding codes.</td></tr>
<tr><td>🇳🇱 Netherlands</td><td>DSA + Children's Code</td><td><span class="csm-status csm-enforced">Enforced</span></td><td>Dutch DPA actively enforcing children's data protection.</td></tr>
<tr><td>🇸🇪 Sweden</td><td>DSA + National Youth Guidelines</td><td><span class="csm-status csm-enforced">Enforced</span></td><td>EU DSA framework plus national youth protection guidelines.</td></tr>
<tr><td>🇫🇷 France</td><td>Loi SREN + Social Media Ban</td><td><span class="csm-status csm-passed">Passed</span></td><td>Senate votes to ban social media for under-15s (April 2026). Arcom blacklist. Reconciliation pending.</td></tr>
<tr><td>🇧🇷 Brazil</td><td>ECA Digital</td><td><span class="csm-status csm-passed">Passed</span></td><td>Loot box ban for minors. Age-appropriate design requirements.</td></tr>
<tr><td>🇮🇳 India</td><td>DPDP Act 2023</td><td><span class="csm-status csm-passed">Passed</span></td><td>Verifiable parental consent for under-18s.</td></tr>
<tr><td>🇮🇹 Italy</td><td>DSA + Parental Consent Law</td><td><span class="csm-status csm-passed">Passed</span></td><td>Digital age of consent set to 14.</td></tr>
<tr><td>🇪🇸 Spain</td><td>Child Protection in Digital Environments</td><td><span class="csm-status csm-passed">Passed</span></td><td>Comprehensive child digital protection law.</td></tr>
<tr><td>🇳🇴 Norway</td><td>Social Media Age Limit</td><td><span class="csm-status csm-passed">Passed</span></td><td>Proposed social media age limit of 15.</td></tr>
<tr><td>🇺🇸 United States</td><td>KOSA + COPPA Update</td><td><span class="csm-status csm-progress">In Progress</span></td><td>KOSA passed Senate 91-3. COPPA update by Apr 2026.</td></tr>
<tr><td>🇨🇦 Canada</td><td>Online Harms Act (C-63)</td><td><span class="csm-status csm-progress">In Progress</span></td><td>Proposed duty of care for platforms.</td></tr>
<tr><td>🇯🇵 Japan</td><td>Act on Regulation of Soliciting Children</td><td><span class="csm-status csm-guidelines">Guidelines</span></td><td>Platform self-regulation encouraged.</td></tr>
{{ range $countries }}
{{ $loc := index . $lang | default (index . "en") }}
{{ $cls := index $statusClass .status }}
{{ $lbl := index (index $statusLabel .status) $lang | default (index (index $statusLabel .status) "en") }}
<tr>
<td>{{ .flag }} {{ $loc.name }}</td>
<td>{{ $loc.law }}</td>
<td><span class="csm-status {{ $cls }}">{{ $lbl }}</span></td>
<td>{{ $loc.detail }}</td>
</tr>
{{ end }}
</tbody>
</table>

View File

@@ -0,0 +1,124 @@
{{ $lang := .Page.Language.Lang }}
{{ $countries := hugo.Data.countries }}
<style>
.ifk-charts { margin: 2rem 0; }
.ifk-charts h3 { text-align: center; margin-bottom: 1.5rem; font-size: 1.25rem; }
.ifk-chart-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; }
.ifk-chart-card {
background: #fafbfc; border: 1px solid #e2e8f0; border-radius: 10px;
padding: 1.25rem; text-align: center;
}
.ifk-chart-card h4 { margin: 0 0 1rem; font-size: 0.95rem; color: #334155; }
.ifk-chart-card canvas { max-height: 280px; }
@media (max-width: 640px) {
.ifk-chart-grid { grid-template-columns: 1fr; }
}
</style>
<div class="ifk-charts">
<h3>{{ if eq $lang "de" }}Analyse der Kinderschutzgesetze{{ else if eq $lang "fr" }}Analyse des lois de protection{{ else }}Child Protection Law Analysis{{ end }}</h3>
<div class="ifk-chart-grid">
<div class="ifk-chart-card">
<h4>{{ if eq $lang "de" }}Gesetzgebungsstatus{{ else if eq $lang "fr" }}Statut législatif{{ else }}Legislative Status{{ end }}</h4>
<canvas id="ifk-chart-status"></canvas>
</div>
<div class="ifk-chart-card">
<h4>{{ if eq $lang "de" }}Mindestalter für soziale Medien{{ else if eq $lang "fr" }}Âge minimum réseaux sociaux{{ else }}Social Media Age Limits{{ end }}</h4>
<canvas id="ifk-chart-age"></canvas>
</div>
<div class="ifk-chart-card" style="grid-column: 1 / -1;">
<h4>{{ if eq $lang "de" }}Zeitachse der Gesetzgebung{{ else if eq $lang "fr" }}Chronologie législative{{ else }}Legislation Timeline{{ end }}</h4>
<canvas id="ifk-chart-timeline"></canvas>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
<script>
(function() {
var LANG = "{{ $lang }}";
var countries = {{ $countries | jsonify }};
var statusLabels = {
en: { enforced: 'Enforced', passed: 'Passed', progress: 'In Progress', guidelines: 'Guidelines' },
de: { enforced: 'In Kraft', passed: 'Verabschiedet', progress: 'In Bearbeitung', guidelines: 'Richtlinien' },
fr: { enforced: 'En vigueur', passed: 'Adopté', progress: 'En cours', guidelines: 'Directives' }
};
var labels = statusLabels[LANG] || statusLabels.en;
/* Pie: status distribution */
var counts = { enforced: 0, passed: 0, progress: 0, guidelines: 0 };
countries.forEach(function(c) { counts[c.status]++; });
new Chart(document.getElementById('ifk-chart-status'), {
type: 'doughnut',
data: {
labels: [labels.enforced, labels.passed, labels.progress, labels.guidelines],
datasets: [{
data: [counts.enforced, counts.passed, counts.progress, counts.guidelines],
backgroundColor: ['#1e40af', '#3b82f6', '#93c5fd', '#dbeafe'],
borderWidth: 2, borderColor: '#fff'
}]
},
options: {
responsive: true,
plugins: {
legend: { position: 'bottom', labels: { padding: 16, usePointStyle: true } }
}
}
});
/* Horizontal bar: age limits */
var sorted = countries.slice().sort(function(a, b) { return b.ageLimitSocial - a.ageLimitSocial; });
var colors = { enforced: '#1e40af', passed: '#3b82f6', progress: '#93c5fd', guidelines: '#dbeafe' };
new Chart(document.getElementById('ifk-chart-age'), {
type: 'bar',
data: {
labels: sorted.map(function(c) { return c.flag + ' ' + (c[LANG] || c.en).name; }),
datasets: [{
label: LANG === 'de' ? 'Mindestalter' : LANG === 'fr' ? 'Âge minimum' : 'Min. Age',
data: sorted.map(function(c) { return c.ageLimitSocial; }),
backgroundColor: sorted.map(function(c) { return colors[c.status]; }),
borderRadius: 4
}]
},
options: {
indexAxis: 'y', responsive: true,
scales: {
x: { min: 0, max: 20, title: { display: true, text: LANG === 'de' ? 'Jahre' : LANG === 'fr' ? 'Ans' : 'Years' } }
},
plugins: { legend: { display: false } }
}
});
/* Bar: timeline by year */
var yearCounts = {};
countries.forEach(function(c) {
if (!yearCounts[c.year]) yearCounts[c.year] = { enforced: 0, passed: 0, progress: 0, guidelines: 0 };
yearCounts[c.year][c.status]++;
});
var years = Object.keys(yearCounts).sort();
new Chart(document.getElementById('ifk-chart-timeline'), {
type: 'bar',
data: {
labels: years,
datasets: [
{ label: labels.enforced, data: years.map(function(y) { return yearCounts[y].enforced; }), backgroundColor: '#1e40af', borderRadius: 4 },
{ label: labels.passed, data: years.map(function(y) { return yearCounts[y].passed; }), backgroundColor: '#3b82f6', borderRadius: 4 },
{ label: labels.progress, data: years.map(function(y) { return yearCounts[y].progress; }), backgroundColor: '#93c5fd', borderRadius: 4 },
{ label: labels.guidelines, data: years.map(function(y) { return yearCounts[y].guidelines; }), backgroundColor: '#dbeafe', borderRadius: 4 }
]
},
options: {
responsive: true,
scales: {
x: { stacked: true }, y: { stacked: true, beginAtZero: true, ticks: { stepSize: 1 } }
},
plugins: { legend: { position: 'bottom', labels: { padding: 16, usePointStyle: true } } }
}
});
})();
</script>

View File

@@ -0,0 +1,82 @@
{{ $lang := .Page.Language.Lang }}
<style>
.ifk-timeline-wrap { margin: 2rem 0; }
.ifk-timeline-wrap h3 { text-align: center; margin-bottom: 1rem; font-size: 1.25rem; }
.ifk-mermaid-container {
background: #fafbfc; border: 1px solid #e2e8f0; border-radius: 10px;
padding: 1.5rem; overflow-x: auto;
}
</style>
<div class="ifk-timeline-wrap">
<h3>{{ if eq $lang "de" }}Meilensteine der Gesetzgebung{{ else if eq $lang "fr" }}Jalons législatifs{{ else }}Legislation Milestones{{ end }}</h3>
<div class="ifk-mermaid-container">
<pre class="mermaid">
timeline
{{ if eq $lang "de" -}}
title Kinderschutzgesetze — Meilensteine
2020 : 🇰🇷 Südkorea Jugendschutzgesetz
2021 : 🇩🇪 JuSchG-Novelle (Online)
: 🇨🇳 Minderjährigenschutzgesetz
2022 : 🇮🇪 Online Safety Act
2023 : 🇬🇧 Online Safety Act
: 🇮🇳 DPDP Act
2024 : 🇦🇺 Social Media Ban (unter 16)
: 🇳🇱 DSA + Kinderkodex
: 🇸🇪 DSA + Jugendrichtlinien
: 🇯🇵 Regulierung Kontaktaufnahme
2025 : 🇧🇷 ECA Digital
: 🇮🇹 Einwilligungsgesetz (14+)
: 🇪🇸 Digitaler Kinderschutz
: 🇳🇴 Social-Media-Altersgrenze (15)
2026 : 🇫🇷 Social-Media-Verbot (unter 15)
: 🇺🇸 KOSA + COPPA-Update
: 🇨🇦 Online Harms Act
{{- else if eq $lang "fr" -}}
title Lois de protection de l'enfance — Jalons
2020 : 🇰🇷 Loi protection jeunesse
2021 : 🇩🇪 JuSchG en ligne
: 🇨🇳 Loi protection mineurs
2022 : 🇮🇪 Online Safety Act
2023 : 🇬🇧 Online Safety Act
: 🇮🇳 DPDP Act
2024 : 🇦🇺 Interdiction réseaux sociaux (-16)
: 🇳🇱 DSA + Code enfance
: 🇸🇪 DSA + Directives jeunesse
: 🇯🇵 Régulation sollicitation
2025 : 🇧🇷 ECA Digital
: 🇮🇹 Consentement (14+)
: 🇪🇸 Protection enfance numérique
: 🇳🇴 Limite âge réseaux (15)
2026 : 🇫🇷 Interdiction réseaux (-15)
: 🇺🇸 KOSA + COPPA
: 🇨🇦 Online Harms Act
{{- else -}}
title Child Protection Laws — Milestones
2020 : 🇰🇷 South Korea Youth Protection Act
2021 : 🇩🇪 Germany JuSchG Online Extension
: 🇨🇳 China Minor Protection Law
2022 : 🇮🇪 Ireland Online Safety Act
2023 : 🇬🇧 UK Online Safety Act
: 🇮🇳 India DPDP Act
2024 : 🇦🇺 Australia Social Media Ban (under 16)
: 🇳🇱 Netherlands DSA + Children's Code
: 🇸🇪 Sweden DSA + Youth Guidelines
: 🇯🇵 Japan Solicitation Regulation
2025 : 🇧🇷 Brazil ECA Digital
: 🇮🇹 Italy Consent Law (14+)
: 🇪🇸 Spain Digital Child Protection
: 🇳🇴 Norway Social Media Age (15)
2026 : 🇫🇷 France Social Media Ban (under 15)
: 🇺🇸 USA KOSA + COPPA Update
: 🇨🇦 Canada Online Harms Act
{{- end }}
</pre>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
<script>
mermaid.initialize({ startOnLoad: true, theme: 'neutral', timeline: { useMaxWidth: true } });
</script>

View File

@@ -0,0 +1,49 @@
{{ $lang := .Page.Language.Lang }}
{{ $countries := hugo.Data.countries }}
{{ $enforced := 0 }}{{ $passed := 0 }}{{ $progress := 0 }}{{ $guidelines := 0 }}
{{ range $countries }}
{{ if eq .status "enforced" }}{{ $enforced = add $enforced 1 }}
{{ else if eq .status "passed" }}{{ $passed = add $passed 1 }}
{{ else if eq .status "progress" }}{{ $progress = add $progress 1 }}
{{ else if eq .status "guidelines" }}{{ $guidelines = add $guidelines 1 }}
{{ end }}
{{ end }}
<style>
.ifk-stats {
display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem;
margin: 2rem 0; text-align: center;
}
.ifk-stat {
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
border-radius: 10px; padding: 1.25rem 0.75rem;
border: 1px solid #e2e8f0; transition: transform 0.15s;
}
.ifk-stat:hover { transform: translateY(-2px); }
.ifk-stat-num { font-size: 2rem; font-weight: 800; line-height: 1.1; }
.ifk-stat-label { font-size: 0.75rem; color: #64748b; margin-top: 0.25rem; font-weight: 500; text-transform: uppercase; letter-spacing: 0.05em; }
@media (max-width: 480px) {
.ifk-stats { grid-template-columns: repeat(2, 1fr); }
.ifk-stat-num { font-size: 1.5rem; }
}
</style>
<div class="ifk-stats">
<div class="ifk-stat">
<div class="ifk-stat-num" style="color:#1e40af">{{ $enforced }}</div>
<div class="ifk-stat-label">{{ if eq $lang "de" }}In Kraft{{ else if eq $lang "fr" }}En vigueur{{ else }}Enforced{{ end }}</div>
</div>
<div class="ifk-stat">
<div class="ifk-stat-num" style="color:#3b82f6">{{ $passed }}</div>
<div class="ifk-stat-label">{{ if eq $lang "de" }}Verabschiedet{{ else if eq $lang "fr" }}Adoptées{{ else }}Passed{{ end }}</div>
</div>
<div class="ifk-stat">
<div class="ifk-stat-num" style="color:#6366f1">{{ $progress }}</div>
<div class="ifk-stat-label">{{ if eq $lang "de" }}In Bearbeitung{{ else if eq $lang "fr" }}En cours{{ else }}In Progress{{ end }}</div>
</div>
<div class="ifk-stat">
<div class="ifk-stat-num" style="color:#8b5cf6">{{ $guidelines }}</div>
<div class="ifk-stat-label">{{ if eq $lang "de" }}Richtlinien{{ else if eq $lang "fr" }}Directives{{ else }}Guidelines{{ end }}</div>
</div>
</div>

View File

@@ -7,7 +7,7 @@
Agiliton VPN provides content filtering, device protection, and safe browsing for the whole family.
{{ end }}
</p>
<a href="https://go.agiliton.eu/vpn" style="display: inline-block; background: #fff; color: #1a365d; padding: 0.75rem 2rem; border-radius: 8px; text-decoration: none; font-weight: 600;">
<a href="https://go.agiliton.eu/vpn-app" style="display: inline-block; background: #fff; color: #1a365d; padding: 0.75rem 2rem; border-radius: 8px; text-decoration: none; font-weight: 600;">
{{ if eq .Page.Language.Lang "de" }}Mehr erfahren{{ else }}Learn More{{ end }}
</a>
</aside>

View File

@@ -0,0 +1,91 @@
{{ $lang := .Page.Language.Lang }}
{{ $countries := hugo.Data.countries }}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.min.css" />
<style>
.ifk-map-wrap { max-width: 100%; margin: 2rem 0; }
.ifk-map-wrap h3 { text-align: center; margin-bottom: 0.5rem; font-size: 1.25rem; }
.ifk-map-subtitle { text-align: center; color: #666; font-size: 0.85rem; margin-bottom: 1rem; }
#ifk-world-map { width: 100%; height: 420px; border-radius: 8px; background: #f0f4f8; }
.ifk-map-legend {
display: flex; flex-wrap: wrap; gap: 1rem; justify-content: center;
margin-top: 1rem; font-size: 0.8rem;
}
.ifk-map-legend-item { display: flex; align-items: center; gap: 0.35rem; }
.ifk-map-legend-swatch { width: 14px; height: 14px; border-radius: 3px; border: 1px solid rgba(0,0,0,0.1); }
@media (max-width: 640px) { #ifk-world-map { height: 280px; } }
</style>
<div class="ifk-map-wrap">
<h3>{{ if eq $lang "de" }}Interaktive Weltkarte: Kinderschutzgesetze{{ else if eq $lang "fr" }}Carte interactive : lois de protection de l'enfance{{ else }}Interactive Map: Child Protection Laws{{ end }}</h3>
<p class="ifk-map-subtitle">{{ if eq $lang "de" }}Klicken Sie auf ein Land für Details{{ else if eq $lang "fr" }}Cliquez sur un pays pour plus de détails{{ else }}Click a country for details{{ end }}</p>
<div id="ifk-world-map"></div>
<div class="ifk-map-legend">
<div class="ifk-map-legend-item"><div class="ifk-map-legend-swatch" style="background:#4d7c0f"></div>{{ if eq $lang "de" }}In Kraft{{ else if eq $lang "fr" }}En vigueur{{ else }}Enforced{{ end }}</div>
<div class="ifk-map-legend-item"><div class="ifk-map-legend-swatch" style="background:#059669"></div>{{ if eq $lang "de" }}Verabschiedet{{ else if eq $lang "fr" }}Adopté{{ else }}Passed{{ end }}</div>
<div class="ifk-map-legend-item"><div class="ifk-map-legend-swatch" style="background:#86efac"></div>{{ if eq $lang "de" }}In Bearbeitung{{ else if eq $lang "fr" }}En cours{{ else }}In Progress{{ end }}</div>
<div class="ifk-map-legend-item"><div class="ifk-map-legend-swatch" style="background:#d1fae5"></div>{{ if eq $lang "de" }}Richtlinien{{ else if eq $lang "fr" }}Directives{{ else }}Guidelines{{ end }}</div>
<div class="ifk-map-legend-item"><div class="ifk-map-legend-swatch" style="background:#e2e8f0"></div>{{ if eq $lang "de" }}Keine Daten{{ else if eq $lang "fr" }}Pas de données{{ else }}No Data{{ end }}</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/topojson-client@3.1.0/dist/topojson-client.min.js"></script>
<script>
(function() {
var LANG = "{{ $lang }}";
var STATUS_COLORS = { enforced: '#4d7c0f', passed: '#059669', progress: '#86efac', guidelines: '#d1fae5' };
var STATUS_LABELS = {
en: { enforced: 'Enforced', passed: 'Passed', progress: 'In Progress', guidelines: 'Guidelines' },
de: { enforced: 'In Kraft', passed: 'Verabschiedet', progress: 'In Bearbeitung', guidelines: 'Richtlinien' },
fr: { enforced: 'En vigueur', passed: 'Adopté', progress: 'En cours', guidelines: 'Directives' }
};
var countries = {{ $countries | jsonify }};
var byIsoNum = {};
countries.forEach(function(c) { byIsoNum[c.isoNum] = c; });
var map = L.map('ifk-world-map', {
center: [25, 10], zoom: 2, minZoom: 2, maxZoom: 6,
scrollWheelZoom: false, zoomControl: true
});
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://openstreetmap.org/copyright">OpenStreetMap</a>',
maxZoom: 6
}).addTo(map);
fetch('https://cdn.jsdelivr.net/npm/world-atlas@2.0.2/countries-110m.json')
.then(function(r) { return r.json(); })
.then(function(topo) {
var geo = topojson.feature(topo, topo.objects.countries);
L.geoJSON(geo, {
style: function(feature) {
var id = String(feature.id).padStart(3, '0');
var c = byIsoNum[id];
return {
fillColor: c ? STATUS_COLORS[c.status] : '#e2e8f0',
weight: 0.8,
color: '#94a3b8',
fillOpacity: c ? 0.75 : 0.3
};
},
onEachFeature: function(feature, layer) {
var id = String(feature.id).padStart(3, '0');
var c = byIsoNum[id];
if (!c) return;
var loc = c[LANG] || c.en;
var labels = STATUS_LABELS[LANG] || STATUS_LABELS.en;
layer.bindPopup(
'<strong>' + c.flag + ' ' + loc.name + '</strong><br>' +
'<em>' + loc.law + '</em><br>' +
'<span style="color:' + STATUS_COLORS[c.status] + ';font-weight:600">' + labels[c.status] + '</span> (' + c.year + ')<br>' +
loc.detail
);
layer.on('mouseover', function(e) { e.target.setStyle({ weight: 2, color: '#1e293b' }); });
layer.on('mouseout', function(e) { e.target.setStyle({ weight: 0.8, color: '#94a3b8' }); });
}
}).addTo(map);
});
})();
</script>

View File

@@ -1,3 +1,8 @@
map $http_x_forwarded_proto $real_scheme {
default $scheme;
https https;
}
server {
listen 80;
server_name _;
@@ -35,8 +40,8 @@ server {
}
# Redirect unprefixed paths to /en/
location ~ "^/(?!en/|de/|fr/|css/|js/|img/|favicon|android|apple|site|llms|health|robots|index\.xml|[0-9a-f]{32}\.txt)" {
return 302 /en$request_uri;
location ~ "^/(?!en/|de/|fr/|css/|js/|img/|favicon|android|apple|site|llms|health|robots|index\.xml|BingSiteAuth|[0-9a-f]{32}\.txt)" {
return 302 $real_scheme://$host/en$request_uri;
}
# Clean URLs

4
static/BingSiteAuth.xml Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0"?>
<users>
<user>7B8CDCFD8FC8D5E226BD9E01CEE80814</user>
</users>

324
static/css/main.css Normal file
View File

@@ -0,0 +1,324 @@
/* Custom font imports */
@font-face {
font-family: 'Newsreader';
font-style: normal;
font-weight: 400 700;
font-display: swap;
src: url(/fonts/newsreader/newsreader-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Newsreader';
font-style: normal;
font-weight: 400 700;
font-display: swap;
src: url(/fonts/newsreader/newsreader-latin-ext.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'Newsreader';
font-style: italic;
font-weight: 400 500;
font-display: swap;
src: url(/fonts/newsreader/newsreader-italic-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Newsreader';
font-style: italic;
font-weight: 400 500;
font-display: swap;
src: url(/fonts/newsreader/newsreader-italic-latin-ext.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* CSS Variables */
:root {
--ifk-font: 'Newsreader', 'Times New Roman', serif;
--ifk-text: #1c1917;
--ifk-bg: #fafaf9;
--ifk-accent: #4d7c0f;
--ifk-accent-hover: #3f6212;
--ifk-secondary: #059669;
}
/* Utility classes for colors */
.text-ifk-accent { color: var(--ifk-accent); }
.text-ifk-text { color: var(--ifk-text); }
.bg-ifk-bg { background-color: var(--ifk-bg); }
.hover\:text-ifk-accent:hover { color: var(--ifk-accent); }
.hover\:text-ifk-accent-hover:hover { color: var(--ifk-accent-hover); }
/* Font family utility */
.font-newsreader { font-family: var(--ifk-font); }
/* Tailwind-equivalent utilities */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
.flex { display: flex; }
.flex-1 { flex: 1 1 0%; }
.flex-wrap { flex-wrap: wrap; }
.inline-block { display: inline-block; }
.grid { display: grid; }
.grid-cols-2 { grid-template-columns: repeat(2, 1fr); }
.justify-between { justify-content: space-between; }
.items-center { align-items: center; }
.gap-4 { gap: 1rem; }
.gap-8 { gap: 2rem; }
.mx-auto { margin-left: auto; margin-right: auto; }
.max-w-2xl { max-width: 42rem; }
.max-w-3xl { max-width: 48rem; }
.max-w-4xl { max-width: 56rem; }
.px-4 { padding-left: 1rem; padding-right: 1rem; }
.py-4 { padding-top: 1rem; padding-bottom: 1rem; }
.py-12 { padding-top: 3rem; padding-bottom: 3rem; }
.p-4 { padding: 1rem; }
.pt-8 { padding-top: 2rem; }
.mb-2 { margin-bottom: 0.5rem; }
.mb-3 { margin-bottom: 0.75rem; }
.mb-4 { margin-bottom: 1rem; }
.mb-8 { margin-bottom: 2rem; }
.mb-12 { margin-bottom: 3rem; }
.mr-2 { margin-right: 0.5rem; }
.mt-12 { margin-top: 3rem; }
.mt-20 { margin-top: 5rem; }
.sticky { position: sticky; }
.top-0 { top: 0; }
.z-40 { z-index: 40; }
.border { border: 1px solid #e5e7eb; }
.border-t { border-top: 1px solid #e5e7eb; }
.border-b { border-bottom: 1px solid #e5e7eb; }
.rounded { border-radius: 0.25rem; }
.bg-white { background-color: #fff; }
.bg-gray-50 { background-color: #f9fafb; }
.text-center { text-align: center; }
.text-left { text-align: left; }
.text-right { text-align: right; }
.text-xs { font-size: 0.75rem; line-height: 1rem; }
.text-sm { font-size: 0.875rem; line-height: 1.25rem; }
.text-lg { font-size: 1.125rem; line-height: 1.75rem; }
.text-xl { font-size: 1.25rem; line-height: 1.75rem; }
.text-4xl { font-size: 2.75rem; line-height: 1.15; }
.font-semibold { font-weight: 600; }
.uppercase { text-transform: uppercase; }
.tracking-wide { letter-spacing: 0.025em; }
.leading-tight { line-height: 1.25; }
.leading-relaxed { line-height: 1.625; }
.text-gray-600 { color: #4b5563; }
.hover\:bg-gray-50:hover { background-color: #f9fafb; }
@media (min-width: 768px) { .md\:grid-cols-3 { grid-template-columns: repeat(3, 1fr); } }
/* Global typography */
body {
background-color: var(--ifk-bg);
color: var(--ifk-text);
font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
}
article, .prose {
font-family: var(--ifk-font);
}
h1, h2, h3, h4, h5, h6 {
font-family: var(--ifk-font);
color: var(--ifk-text);
font-weight: 600;
}
h1, .article-title {
font-size: 2.75rem;
line-height: 1.15;
letter-spacing: -0.01em;
margin-bottom: 1.5rem;
}
h2 {
font-size: 1.85rem;
line-height: 1.25;
margin-top: 3rem;
margin-bottom: 1.25rem;
}
h3 {
font-size: 1.5rem;
line-height: 1.3;
margin-top: 2.5rem;
margin-bottom: 1rem;
}
/* Article content */
article p {
font-size: 1.375rem;
line-height: 1.6;
margin-bottom: 1.5rem;
font-family: var(--ifk-font);
}
/* Links */
article a {
color: var(--ifk-accent);
text-decoration: underline;
text-decoration-thickness: 1px;
text-underline-offset: 2px;
transition: all 0.2s ease;
}
article a:hover {
color: var(--ifk-accent-hover);
text-decoration-thickness: 2px;
}
/* Blockquotes */
blockquote {
font-family: var(--ifk-font);
font-style: italic;
font-size: 1.5rem;
line-height: 1.6;
border-left: 3px solid var(--ifk-secondary);
padding-left: 1.5rem;
margin: 2rem 0;
color: var(--ifk-text);
opacity: 0.85;
}
/* Code blocks */
code {
font-family: 'Courier New', monospace;
font-size: 0.9rem;
background: #f3f4f6;
padding: 0.1rem 0.4rem;
border-radius: 0.25rem;
}
pre {
background: #1f2937;
color: #f3f4f6;
padding: 1.5rem;
border-radius: 0.5rem;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.5;
margin: 1.5rem 0;
}
pre code {
background: none;
color: inherit;
padding: 0;
border-radius: 0;
}
/* Tables */
table {
width: 100%;
border-collapse: collapse;
margin: 1.5rem 0;
font-family: var(--ifk-font);
font-size: 1.1rem;
line-height: 1.6;
}
th, td {
padding: 0.75rem 1rem;
border: 1px solid #e5e7eb;
text-align: left;
}
th {
background: #f3f4f6;
font-weight: 600;
}
/* Lists */
ul, ol {
margin-left: 1.5rem;
margin-bottom: 1.5rem;
}
li {
margin-bottom: 0.5rem;
font-family: var(--ifk-font);
font-size: 1.375rem;
line-height: 1.6;
}
/* Navigation */
nav, header, footer {
font-family: system-ui, -apple-system, sans-serif;
}
nav a {
text-decoration: none;
transition: color 0.2s ease;
}
nav a:hover {
color: var(--ifk-accent);
}
/* Article metadata */
.article-meta, .post-meta, time, .reading-time {
font-family: system-ui, -apple-system, sans-serif;
font-size: 0.95rem;
color: #6b7280;
}
/* Tags */
.taxonomy-term, [href*="/tags/"], [href*="/categories/"] {
font-family: system-ui, -apple-system, sans-serif;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--ifk-accent);
text-decoration: none;
}
.taxonomy-term:hover, [href*="/tags/"]:hover, [href*="/categories/"]:hover {
color: var(--ifk-accent-hover);
text-decoration: underline;
}
/* Table of Contents */
#TableOfContents {
font-family: system-ui, -apple-system, sans-serif;
font-size: 0.95rem;
line-height: 1.7;
}
#TableOfContents a {
color: var(--ifk-accent);
text-decoration: none;
}
#TableOfContents a:hover {
text-decoration: underline;
}
/* Status badges (for shortcodes) */
.csm-enforced { background: var(--ifk-accent); color: white; }
.csm-passed { background: var(--ifk-secondary); color: white; }
.csm-progress { background: #86efac; color: #14532d; }
.csm-guidelines { background: #d1fae5; color: #065f46; }
/* Shortcode-specific overrides */
.ifk-stat-num { font-family: var(--ifk-font); }
.ifk-stat-label { font-family: system-ui, -apple-system, sans-serif; }
.ifk-chart-card h4 { font-family: system-ui, -apple-system, sans-serif; }
/* Responsive design */
@media (max-width: 768px) {
h1, .article-title {
font-size: 2rem;
}
h2 {
font-size: 1.5rem;
}
article p, li {
font-size: 1.1rem;
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -15,12 +15,13 @@ This blog covers:
## Languages
Content is available in English (/en/) and German (/de/).
Content is available in English (/en/), German (/de/), and French (/fr/).
## Sections
- /en/ — Articles on child protection and online safety (English)
- /de/ — Artikel zum Kinderschutz und Online-Sicherheit (Deutsch)
- /fr/ — Articles sur la protection de l'enfance et la sécurité en ligne (Français)
- /en/about/ — About this publication
- /en/tags/ — Browse by topic

21
static/site.webmanifest Normal file
View File

@@ -0,0 +1,21 @@
{
"name": "Internet for Kids",
"short_name": "IFK",
"description": "Expert insights on child protection laws, online safety, and digital family protection worldwide.",
"start_url": "/en/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#1a365d",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}

Submodule themes/congo deleted from b426bf91b0