feat: bilingual website (DE/EN) with language switcher
CI / TypeScript — type-check (push) Successful in 8s
CI / Publish — build & push image (push) Successful in 8s
CI / Deploy — pull & restart on host (push) Successful in 2s

Add LangContext with localStorage persistence (default: DE).
DE/EN toggle in Nav (top-right, before CTA button).

All pages translated:
- Home: all 8 sections incl. NIS2 block
- Features: all 13 sections with full German bullet points
- Drivers: headings and descriptions
- Getting Started (coming soon)
- Roadmap: bilingual data arrays inline (Planned + Under consideration)
- NIS2: full Art. 21 mapping, evidence blocks, coming-soon list

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Christian Manivong
2026-06-28 17:00:22 +02:00
co-authored by Claude Sonnet 4.6
parent f194c379e4
commit d48c7143aa
11 changed files with 1011 additions and 618 deletions
+149 -211
View File
@@ -1,215 +1,190 @@
import { Link } from 'react-router-dom'
import { ShieldCheckIcon } from '@heroicons/react/24/outline'
import { useLang } from '../context/LangContext'
type Coverage = 'covered' | 'partial' | 'roadmap' | 'na'
type Requirement = { article: string; label: string; coverage: Coverage; netork: string }
type EvidenceBlock = { trigger: string; produces: string[] }
type ComingItem = { title: string; detail: string }
type Requirement = {
article: string
label: string
coverage: Coverage
netork: string
const REQUIREMENTS: Record<'en' | 'de', Requirement[]> = {
en: [
{ article: 'Art. 21 (2a)', label: 'Risk analysis & information system security policies', coverage: 'partial', netork: 'Config drift detection, SNMP health metrics, and security agent coverage across all devices provide a continuous risk baseline. A formal risk register is out of scope for netOrk.' },
{ article: 'Art. 21 (2b)', label: 'Incident handling', coverage: 'partial', netork: 'Wazuh alert history, CrowdSec decisions, and Graylog syslog per device surface incidents at the network layer. A structured incident record with NIS2 Art. 23 reporting timers is on the roadmap.' },
{ article: 'Art. 21 (2c)', label: 'Business continuity, backup management, disaster recovery', coverage: 'roadmap', netork: 'Git-backed configuration snapshots (on roadmap) provide config-level recovery. Backup monitoring for individual devices is not yet implemented.' },
{ article: 'Art. 21 (2d)', label: 'Supply chain security', coverage: 'partial', netork: 'Vendor, model, firmware, and OS version are tracked per device after every poll. EOL tracking against endoflife.date is on the roadmap to flag unsupported software.' },
{ article: 'Art. 21 (2e)', label: 'Vulnerability handling in acquisition, development & maintenance', coverage: 'covered', netork: 'Per-device update status and installed package list tracked on every poll. Wazuh CVE counts by severity (critical / high / medium) linked directly to each device record. CVE cross-reference against NVD/OSV (without Wazuh) is on the roadmap.' },
{ article: 'Art. 21 (2f)', label: 'Assessing effectiveness of cybersecurity measures', coverage: 'partial', netork: 'The audit log records all orchestration actions. A per-site compliance dashboard (on roadmap) will aggregate security agent coverage, drift status, and patch metrics into a single view.' },
{ article: 'Art. 21 (2g)', label: 'Basic cyber hygiene & cybersecurity training', coverage: 'na', netork: 'Out of scope for a network orchestration platform. Training and hygiene policies are handled at the organizational level.' },
{ article: 'Art. 21 (2h)', label: 'Access control, asset management, human resources security', coverage: 'covered', netork: 'Full device inventory maintained automatically via discovery and continuous polling. RBAC with four built-in roles (viewer / operator / engineer / administrator) and custom role combinations. Complete audit log of all orchestration actions.' },
{ article: 'Art. 21 (2i)', label: 'Multi-factor authentication', coverage: 'roadmap', netork: 'TOTP-based MFA for netOrk user accounts is on the roadmap. Current authentication is JWT-based (username + password).' },
{ article: 'Art. 21 (2j)', label: 'Physical and environmental security', coverage: 'na', netork: 'Out of scope. Physical security of the infrastructure hosting netOrk is an organizational and facility concern.' },
],
de: [
{ article: 'Art. 21 (2a)', label: 'Risikoanalyse und Sicherheitsrichtlinien für Informationssysteme', coverage: 'partial', netork: 'Konfigurationsdrift-Erkennung, SNMP-Gesundheitsmetriken und Security-Agent-Abdeckung über alle Geräte liefern eine kontinuierliche Risiko-Baseline. Ein formales Risikoregister liegt außerhalb des Scopes von netOrk.' },
{ article: 'Art. 21 (2b)', label: 'Bewältigung von Sicherheitsvorfällen', coverage: 'partial', netork: 'Wazuh-Alert-Historie, CrowdSec-Entscheidungen und Graylog-Syslog pro Gerät decken Vorfälle auf Netzwerkebene auf. Ein strukturierter Incident-Datensatz mit NIS2 Art. 23 Melde-Timern ist auf der Roadmap.' },
{ article: 'Art. 21 (2c)', label: 'Geschäftskontinuität, Backup-Management, Disaster Recovery', coverage: 'roadmap', netork: 'Git-basierte Konfigurationssnapshots (auf der Roadmap) ermöglichen Wiederherstellung auf Konfigurationsebene. Backup-Monitoring für einzelne Geräte ist noch nicht implementiert.' },
{ article: 'Art. 21 (2d)', label: 'Supply-Chain-Sicherheit', coverage: 'partial', netork: 'Hersteller, Modell, Firmware und OS-Version werden nach jedem Poll pro Gerät erfasst. EOL-Tracking über endoflife.date ist auf der Roadmap, um nicht unterstützte Software zu kennzeichnen.' },
{ article: 'Art. 21 (2e)', label: 'Schwachstellenbehandlung bei Erwerb, Entwicklung & Wartung', coverage: 'covered', netork: 'Update-Status und installierte Paketliste pro Gerät werden bei jedem Poll erfasst. Wazuh-CVE-Anzahl nach Schweregrad (kritisch / hoch / mittel) direkt mit jedem Gerätedatensatz verknüpft. CVE-Abgleich gegen NVD/OSV (ohne Wazuh) ist auf der Roadmap.' },
{ article: 'Art. 21 (2f)', label: 'Beurteilung der Wirksamkeit von Cybersicherheitsmaßnahmen', coverage: 'partial', netork: 'Das Audit-Log erfasst alle Orchestrierungsaktionen. Ein Compliance-Dashboard pro Standort (auf der Roadmap) wird Security-Agent-Abdeckung, Drift-Status und Patch-Metriken in einer Ansicht zusammenfassen.' },
{ article: 'Art. 21 (2g)', label: 'Grundlegende Cyberhygiene und Cybersicherheitsschulungen', coverage: 'na', netork: 'Außerhalb des Scopes einer Netzwerk-Orchestrierungsplattform. Schulungen und Hygiene-Richtlinien werden auf Organisationsebene gehandhabt.' },
{ article: 'Art. 21 (2h)', label: 'Zugangskontrolle, Asset-Management, Personalsicherheit', coverage: 'covered', netork: 'Vollständiges Geräteinventar automatisch über Discovery und kontinuierliches Polling gepflegt. RBAC mit vier integrierten Rollen (Betrachter / Operator / Ingenieur / Administrator) und benutzerdefinierten Rollenkombinationen. Vollständiges Audit-Log aller Orchestrierungsaktionen.' },
{ article: 'Art. 21 (2i)', label: 'Multi-Faktor-Authentifizierung', coverage: 'roadmap', netork: 'TOTP-basierte MFA für netOrk-Benutzerkonten ist auf der Roadmap. Die aktuelle Authentifizierung ist JWT-basiert (Benutzername + Passwort).' },
{ article: 'Art. 21 (2j)', label: 'Physische und umgebungsbezogene Sicherheit', coverage: 'na', netork: 'Außerhalb des Scopes. Die physische Sicherheit der Infrastruktur, die netOrk hostet, ist eine organisatorische und gebäudetechnische Angelegenheit.' },
],
}
const REQUIREMENTS: Requirement[] = [
{
article: 'Art. 21 (2a)',
label: 'Risk analysis & information system security policies',
coverage: 'partial',
netork:
'Config drift detection, SNMP health metrics, and security agent coverage across all devices provide a continuous risk baseline. A formal risk register is out of scope for netOrk.',
},
{
article: 'Art. 21 (2b)',
label: 'Incident handling',
coverage: 'partial',
netork:
'Wazuh alert history, CrowdSec decisions, and Graylog syslog per device surface incidents at the network layer. A structured incident record with NIS2 Art. 23 reporting timers is on the roadmap.',
},
{
article: 'Art. 21 (2c)',
label: 'Business continuity, backup management, disaster recovery',
coverage: 'roadmap',
netork:
'Git-backed configuration snapshots (on roadmap) provide config-level recovery. Backup monitoring for individual devices is not yet implemented.',
},
{
article: 'Art. 21 (2d)',
label: 'Supply chain security',
coverage: 'partial',
netork:
'Vendor, model, firmware, and OS version are tracked per device after every poll. EOL tracking against endoflife.date is on the roadmap to flag unsupported software.',
},
{
article: 'Art. 21 (2e)',
label: 'Vulnerability handling in acquisition, development & maintenance',
coverage: 'covered',
netork:
'Per-device update status and installed package list tracked on every poll. Wazuh CVE counts by severity (critical / high / medium) linked directly to each device record. CVE cross-reference against NVD/OSV (without Wazuh) is on the roadmap.',
},
{
article: 'Art. 21 (2f)',
label: 'Assessing effectiveness of cybersecurity measures',
coverage: 'partial',
netork:
'The audit log records all orchestration actions. A per-site compliance dashboard (on roadmap) will aggregate security agent coverage, drift status, and patch metrics into a single view.',
},
{
article: 'Art. 21 (2g)',
label: 'Basic cyber hygiene & cybersecurity training',
coverage: 'na',
netork:
'Out of scope for a network orchestration platform. Training and hygiene policies are handled at the organizational level.',
},
{
article: 'Art. 21 (2h)',
label: 'Access control, asset management, human resources security',
coverage: 'covered',
netork:
'Full device inventory maintained automatically via discovery and continuous polling. RBAC with four built-in roles (viewer / operator / engineer / administrator) and custom role combinations. Complete audit log of all orchestration actions.',
},
{
article: 'Art. 21 (2i)',
label: 'Multi-factor authentication',
coverage: 'roadmap',
netork:
'TOTP-based MFA for netOrk user accounts is on the roadmap. Current authentication is JWT-based (username + password).',
},
{
article: 'Art. 21 (2j)',
label: 'Physical and environmental security',
coverage: 'na',
netork:
'Out of scope. Physical security of the infrastructure hosting netOrk is an organizational and facility concern.',
},
]
const EVIDENCE: Record<'en' | 'de', EvidenceBlock[]> = {
en: [
{
trigger: 'Every device poll',
produces: [
'Installed package list with versions',
'Available update count',
'Interface status, ARP table, DHCP leases',
'VLAN membership vs. desired state (drift)',
'SNMP health metrics (CPU, memory, interface counters)',
'Wazuh agent status and CVE counts by severity',
'Graylog syslog forwarding status',
'CrowdSec decisions and ban counts',
],
},
{
trigger: 'Every user action',
produces: [
'Audit log entry: user, timestamp, resource, action',
'Before/after values for configuration changes',
],
},
{
trigger: 'On demand',
produces: [
'Topology graph — network segmentation view',
'Subnet browser — IP space coverage',
'VLAN matrix — which devices carry which VLANs',
'Audit log export to PDF / CSV (roadmap)',
],
},
],
de: [
{
trigger: 'Bei jedem Geräte-Poll',
produces: [
'Installierte Paketliste mit Versionen',
'Anzahl verfügbarer Updates',
'Schnittstellenstatus, ARP-Tabelle, DHCP-Leases',
'VLAN-Zugehörigkeit vs. Sollzustand (Drift)',
'SNMP-Gesundheitsmetriken (CPU, Speicher, Schnittstellenzähler)',
'Wazuh-Agent-Status und CVE-Anzahl nach Schweregrad',
'Graylog-Syslog-Weiterleitungsstatus',
'CrowdSec-Entscheidungen und Ban-Anzahl',
],
},
{
trigger: 'Bei jeder Benutzeraktion',
produces: [
'Audit-Log-Eintrag: Benutzer, Zeitstempel, Ressource, Aktion',
'Vorher/Nachher-Werte für Konfigurationsänderungen',
],
},
{
trigger: 'Auf Abruf',
produces: [
'Topologie-Graph — Netzwerksegmentierungs-Ansicht',
'Subnetz-Browser — IP-Raum-Abdeckung',
'VLAN-Matrix — welche Geräte welche VLANs führen',
'Audit-Log-Export als PDF / CSV (Roadmap)',
],
},
],
}
const EVIDENCE = [
{
trigger: 'Every device poll',
produces: [
'Installed package list with versions',
'Available update count',
'Interface status, ARP table, DHCP leases',
'VLAN membership vs. desired state (drift)',
'SNMP health metrics (CPU, memory, interface counters)',
'Wazuh agent status and CVE counts by severity',
'Graylog syslog forwarding status',
'CrowdSec decisions and ban counts',
],
},
{
trigger: 'Every user action',
produces: [
'Audit log entry: user, timestamp, resource, action',
'Before/after values for configuration changes',
],
},
{
trigger: 'On demand',
produces: [
'Topology graph — network segmentation view',
'Subnet browser — IP space coverage',
'VLAN matrix — which devices carry which VLANs',
'Audit log export to PDF / CSV (roadmap)',
],
},
]
const COMING: Record<'en' | 'de', ComingItem[]> = {
en: [
{ title: 'CVE tracking per device', detail: 'Automatic cross-reference of installed packages against NVD / OSV — no Wazuh agent required.' },
{ title: 'Configuration backup & versioning', detail: 'Git-backed config snapshots after every poll. Detect unauthorized changes, compare over time.' },
{ title: 'Compliance dashboard', detail: 'Per-site Art. 21 checklist: asset coverage, patch status, agent deployment, drift, syslog, audit activity.' },
{ title: 'Audit log export', detail: 'PDF and CSV export filtered by date range, device, user, or action — ready to hand to an auditor.' },
{ title: 'Incident workflow', detail: 'Structured incident records with NIS2 Art. 23 reporting timers (24 h / 72 h) and external webhook delivery.' },
{ title: 'EOL tracking', detail: 'Flag devices running end-of-life firmware or OS versions via the endoflife.date API.' },
{ title: 'MFA (TOTP)', detail: 'Time-based one-time passwords as a second factor for netOrk user accounts (Art. 21 (2i)).' },
],
de: [
{ title: 'CVE-Tracking pro Gerät', detail: 'Automatischer Abgleich installierter Pakete gegen NVD / OSV — kein Wazuh-Agent erforderlich.' },
{ title: 'Konfigurationsbackup & -versionierung', detail: 'Git-basierte Konfigurationssnapshots nach jedem Poll. Nicht autorisierte Änderungen erkennen, über die Zeit vergleichen.' },
{ title: 'Compliance-Dashboard', detail: 'Art. 21-Checkliste pro Standort: Asset-Abdeckung, Patch-Status, Agent-Deployment, Drift, Syslog, Audit-Aktivität.' },
{ title: 'Audit-Log-Export', detail: 'PDF- und CSV-Export gefiltert nach Datumsbereich, Gerät, Benutzer oder Aktion — bereit zur Übergabe an einen Prüfer.' },
{ title: 'Incident-Workflow', detail: 'Strukturierte Incident-Datensätze mit NIS2 Art. 23 Melde-Timern (24 h / 72 h) und externer Webhook-Zustellung.' },
{ title: 'EOL-Tracking', detail: 'Geräte mit End-of-Life-Firmware oder OS-Versionen über die endoflife.date-API kennzeichnen.' },
{ title: 'MFA (TOTP)', detail: 'Zeitbasierte Einmalpasswörter als zweiter Faktor für netOrk-Benutzerkonten (Art. 21 (2i)).' },
],
}
function CoverageTag({ coverage }: { coverage: Coverage }) {
if (coverage === 'covered')
return (
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-green-500/15 text-green-400 border border-green-500/20 whitespace-nowrap">
✓ Covered
</span>
)
if (coverage === 'partial')
return (
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-yellow-500/15 text-yellow-400 border border-yellow-500/20 whitespace-nowrap">
⚠ Partial
</span>
)
if (coverage === 'roadmap')
return (
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-sky-500/15 text-sky-400 border border-sky-500/20 whitespace-nowrap">
→ Roadmap
</span>
)
return (
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-slate-700/50 text-slate-500 border border-slate-700 whitespace-nowrap">
— N/A
</span>
)
function CoverageTag({ coverage, labels }: { coverage: Coverage; labels: Record<string, string> }) {
const base = 'inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium whitespace-nowrap border'
if (coverage === 'covered') return <span className={`${base} bg-green-500/15 text-green-400 border-green-500/20`}>{labels.covered}</span>
if (coverage === 'partial') return <span className={`${base} bg-yellow-500/15 text-yellow-400 border-yellow-500/20`}>{labels.partial}</span>
if (coverage === 'roadmap') return <span className={`${base} bg-sky-500/15 text-sky-400 border-sky-500/20`}>{labels.roadmap}</span>
return <span className={`${base} bg-slate-700/50 text-slate-500 border-slate-700`}>{labels.na}</span>
}
export default function Nis2() {
const { lang, t } = useLang()
const reqs = REQUIREMENTS[lang]
const evidence = EVIDENCE[lang]
const coming = COMING[lang]
const coverageLabels = t.nis2.coverageLabels
const legendLabels = t.nis2.legendLabels
return (
<div className="py-16 md:py-24">
<div className="max-w-4xl mx-auto px-6">
{/* Header */}
<div className="mb-16">
<div className="mb-4 flex h-10 w-10 items-center justify-center rounded-lg bg-sky-600/10">
<ShieldCheckIcon className="h-5 w-5 text-sky-400" />
</div>
<h1 className="text-4xl md:text-5xl font-bold text-slate-100 mb-4">
NIS2 &amp; netOrk
</h1>
<p className="text-base text-slate-400 leading-relaxed max-w-2xl">
NIS2 Art. 21 defines ten categories of technical and organizational
measures. Some of them are directly addressed by what netOrk does
every day. This page maps each requirement to netOrk's current
capabilities — honestly, including what's partial and what's not
applicable.
</p>
<h1 className="text-4xl md:text-5xl font-bold text-slate-100 mb-4">{t.nis2.heading}</h1>
<p className="text-base text-slate-400 leading-relaxed max-w-2xl">{t.nis2.sub}</p>
</div>
{/* Art. 21 mapping */}
<div className="mb-20">
<h2 className="text-xl font-semibold text-slate-200 mb-6 pb-2 border-b border-slate-800">
Art. 21 — requirement by requirement
{t.nis2.mappingHeading}
</h2>
<div className="space-y-0 rounded-xl border border-slate-800 overflow-hidden">
{REQUIREMENTS.map((r, i) => (
<div
key={r.article}
className={`p-5 ${i < REQUIREMENTS.length - 1 ? 'border-b border-slate-800' : ''}`}
>
{reqs.map((r, i) => (
<div key={r.article} className={`p-5 ${i < reqs.length - 1 ? 'border-b border-slate-800' : ''}`}>
<div className="flex flex-wrap items-start justify-between gap-3 mb-2">
<div className="flex items-center gap-3">
<span className="text-xs font-mono text-sky-500 shrink-0">{r.article}</span>
<span className="text-sm font-medium text-slate-200">{r.label}</span>
</div>
<CoverageTag coverage={r.coverage} />
<CoverageTag coverage={r.coverage} labels={coverageLabels} />
</div>
<p className="text-sm text-slate-500 leading-relaxed pl-0 md:pl-24">{r.netork}</p>
<p className="text-sm text-slate-500 leading-relaxed md:pl-24">{r.netork}</p>
</div>
))}
</div>
{/* Legend */}
<div className="mt-4 flex flex-wrap gap-4 text-xs text-slate-500">
<span className="flex items-center gap-1.5"><CoverageTag coverage="covered" /> netOrk covers this today</span>
<span className="flex items-center gap-1.5"><CoverageTag coverage="partial" /> partially covered — see description</span>
<span className="flex items-center gap-1.5"><CoverageTag coverage="roadmap" /> planned — see roadmap</span>
<span className="flex items-center gap-1.5"><CoverageTag coverage="na" /> outside scope of a network management tool</span>
{(['covered', 'partial', 'roadmap', 'na'] as Coverage[]).map((c) => (
<span key={c} className="flex items-center gap-1.5">
<CoverageTag coverage={c} labels={coverageLabels} />
{legendLabels[c]}
</span>
))}
</div>
</div>
{/* Evidence produced */}
<div className="mb-20">
<h2 className="text-xl font-semibold text-slate-200 mb-2 pb-2 border-b border-slate-800">
What netOrk produces as evidence
{t.nis2.evidenceHeading}
</h2>
<p className="text-sm text-slate-500 mb-6">
NIS2 audits require demonstrable outputs, not just claimed controls. Here's
what netOrk generates automatically.
</p>
<p className="text-sm text-slate-500 mb-6">{t.nis2.evidenceSub}</p>
<div className="space-y-6">
{EVIDENCE.map((e) => (
{evidence.map((e) => (
<div key={e.trigger} className="rounded-xl border border-slate-800 bg-slate-900 p-5">
<p className="text-xs font-semibold text-sky-500 uppercase tracking-widest mb-3">
{e.trigger}
</p>
<p className="text-xs font-semibold text-sky-500 uppercase tracking-widest mb-3">{e.trigger}</p>
<ul className="space-y-1.5">
{e.produces.map((item) => (
<li key={item} className="flex items-start gap-2 text-sm text-slate-400">
@@ -223,42 +198,12 @@ export default function Nis2() {
</div>
</div>
{/* Roadmap callout */}
<div className="mb-20">
<h2 className="text-xl font-semibold text-slate-200 mb-6 pb-2 border-b border-slate-800">
What's coming
{t.nis2.comingHeading}
</h2>
<div className="space-y-4">
{[
{
title: 'CVE tracking per device',
detail: 'Automatic cross-reference of installed packages against NVD / OSV — no Wazuh agent required.',
},
{
title: 'Configuration backup & versioning',
detail: 'Git-backed config snapshots after every poll. Detect unauthorized changes, compare over time.',
},
{
title: 'Compliance dashboard',
detail: 'Per-site Art. 21 checklist: asset coverage, patch status, agent deployment, drift, syslog, audit activity.',
},
{
title: 'Audit log export',
detail: 'PDF and CSV export filtered by date range, device, user, or action — ready to hand to an auditor.',
},
{
title: 'Incident workflow',
detail: 'Structured incident records with NIS2 Art. 23 reporting timers (24 h / 72 h) and external webhook delivery.',
},
{
title: 'EOL tracking',
detail: 'Flag devices running end-of-life firmware or OS versions via the endoflife.date API.',
},
{
title: 'MFA (TOTP)',
detail: 'Time-based one-time passwords as a second factor for netOrk user accounts (Art. 21 (2i)).',
},
].map((item) => (
{coming.map((item) => (
<div key={item.title} className="flex gap-4">
<div className="mt-2 shrink-0 h-1.5 w-1.5 rounded-full bg-sky-500" />
<div>
@@ -270,28 +215,21 @@ export default function Nis2() {
</div>
<div className="mt-8">
<Link to="/roadmap" className="text-sky-400 hover:text-sky-300 transition-colors text-sm font-medium">
Full roadmap →
{lang === 'de' ? 'Vollständige Roadmap →' : 'Full roadmap →'}
</Link>
</div>
</div>
{/* CTA */}
<div className="rounded-xl border border-slate-800 bg-slate-900 p-8 text-center">
<h2 className="text-xl font-semibold text-slate-100 mb-3">
Start with the foundation.
</h2>
<p className="text-sm text-slate-400 leading-relaxed mb-6 max-w-md mx-auto">
Asset inventory, continuous polling, drift detection, RBAC, and a full
audit log — deployed in one command.
</p>
<h2 className="text-xl font-semibold text-slate-100 mb-3">{t.nis2.cta.heading}</h2>
<p className="text-sm text-slate-400 leading-relaxed mb-6 max-w-md mx-auto">{t.nis2.cta.body}</p>
<Link
to="/docs/getting-started"
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-sky-600 hover:bg-sky-500 text-white text-sm font-medium transition-colors"
>
Get started →
{t.nis2.cta.button}
</Link>
</div>
</div>
</div>
)