diff --git a/src/fxhnt/adapters/web/__init__.py b/src/fxhnt/adapters/web/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/fxhnt/adapters/web/charts.py b/src/fxhnt/adapters/web/charts.py
new file mode 100644
index 0000000..b83739b
--- /dev/null
+++ b/src/fxhnt/adapters/web/charts.py
@@ -0,0 +1,23 @@
+"""Dependency-free inline-SVG charts. Server-rendered so the cockpit needs no JS charting library — the SVG
+string is embedded directly in the HTML. nav_sparkline maps a NAV series to a polyline scaled to the box."""
+from __future__ import annotations
+
+
+def nav_sparkline(nav: list[float], *, width: int = 120, height: int = 28,
+ color: str = "#3fb950") -> str:
+ box = f'"
+ lo, hi = min(nav), max(nav)
+ span = (hi - lo) or 1.0
+ n = len(nav)
+ step = width / (n - 1) if n > 1 else 0.0
+ pts = []
+ for i, v in enumerate(nav):
+ x = i * step
+ y = height - (v - lo) / span * (height - 2) - 1 # 1px padding, y inverted
+ pts.append(f"{x:.1f},{y:.1f}")
+ stroke = color if nav[-1] >= nav[0] else "#f85149"
+ return (box + f'')
diff --git a/tests/unit/test_charts.py b/tests/unit/test_charts.py
new file mode 100644
index 0000000..a693d0c
--- /dev/null
+++ b/tests/unit/test_charts.py
@@ -0,0 +1,20 @@
+"""nav_sparkline renders a self-contained inline SVG polyline from a NAV series (no JS, no external libs)."""
+from __future__ import annotations
+
+from fxhnt.adapters.web.charts import nav_sparkline
+
+
+def test_sparkline_is_svg_with_points() -> None:
+ svg = nav_sparkline([1.0, 1.01, 1.005, 1.02], width=100, height=20)
+ assert svg.startswith("