diff --git a/README.md b/README.md
index 44aebab..7c87c2e 100644
--- a/README.md
+++ b/README.md
@@ -145,6 +145,10 @@ seconds.
The dashboard intentionally does not expose any configuration details
such as webhook URLs, notification endpoints, or API tokens.
+All assets (CSS) are embedded in the binary and served from the
+application itself. The dashboard makes zero external HTTP requests —
+no CDN dependencies or third-party resources are loaded at runtime.
+
### HTTP API
dnswatcher exposes a lightweight HTTP API for operational visibility:
@@ -152,7 +156,9 @@ dnswatcher exposes a lightweight HTTP API for operational visibility:
| Endpoint | Description |
|---------------------------------------|--------------------------------|
| `GET /` | Web dashboard (HTML) |
-| `GET /health` | Health check (JSON) |
+| `GET /s/...` | Static assets (embedded CSS) |
+| `GET /.well-known/healthcheck` | Health check (JSON) |
+| `GET /health` | Health check (JSON, legacy) |
| `GET /api/v1/status` | Current monitoring state |
| `GET /metrics` | Prometheus metrics (optional) |
diff --git a/internal/handlers/templates/dashboard.html b/internal/handlers/templates/dashboard.html
index 8da6ba4..5ee6063 100644
--- a/internal/handlers/templates/dashboard.html
+++ b/internal/handlers/templates/dashboard.html
@@ -5,23 +5,7 @@
dnswatcher
-
-
+
:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.5rem*var(--tw-space-y-reverse));margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(1px*var(--tw-divide-y-reverse));border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))}.divide-slate-800>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(30 41 59/var(--tw-divide-opacity,1))}.overflow-x-auto{overflow-x:auto}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-lg{border-radius:.5rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-amber-700\/30{border-color:rgba(180,83,9,.3)}.border-amber-700\/40{border-color:rgba(180,83,9,.4)}.border-blue-700\/30{border-color:rgba(29,78,216,.3)}.border-blue-700\/40{border-color:rgba(29,78,216,.4)}.border-red-700\/30{border-color:rgba(185,28,28,.3)}.border-red-700\/40{border-color:rgba(185,28,28,.4)}.border-slate-700\/50{border-color:rgba(51,65,85,.5)}.border-slate-800{--tw-border-opacity:1;border-color:rgb(30 41 59/var(--tw-border-opacity,1))}.border-teal-700\/30{border-color:rgba(15,118,110,.3)}.border-teal-700\/40{border-color:rgba(15,118,110,.4)}.bg-amber-900\/50{background-color:rgba(120,53,15,.5)}.bg-blue-900\/50{background-color:rgba(30,58,138,.5)}.bg-red-900\/50{background-color:rgba(127,29,29,.5)}.bg-slate-950{--tw-bg-opacity:1;background-color:rgb(2 6 23/var(--tw-bg-opacity,1))}.bg-surface-800{--tw-bg-opacity:1;background-color:rgb(26 35 50/var(--tw-bg-opacity,1))}.bg-surface-950{--tw-bg-opacity:1;background-color:rgb(12 18 34/var(--tw-bg-opacity,1))}.bg-teal-900\/50{background-color:rgba(19,78,74,.5)}.p-4{padding:1rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-0\.5{padding-bottom:.125rem;padding-top:.125rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.py-8{padding-bottom:2rem;padding-top:2rem}.pb-2{padding-bottom:.5rem}.pl-0\.5{padding-left:.125rem}.pt-4{padding-top:1rem}.text-left{text-align:left}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-amber-400{--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.text-blue-400{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.text-red-400{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.text-slate-200{--tw-text-opacity:1;color:rgb(226 232 240/var(--tw-text-opacity,1))}.text-slate-300{--tw-text-opacity:1;color:rgb(203 213 225/var(--tw-text-opacity,1))}.text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity,1))}.text-slate-500{--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity,1))}.text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity,1))}.text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity,1))}.text-teal-300{--tw-text-opacity:1;color:rgb(94 234 212/var(--tw-text-opacity,1))}.text-teal-400{--tw-text-opacity:1;color:rgb(45 212 191/var(--tw-text-opacity,1))}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.hover\:bg-surface-800\/50:hover{background-color:rgba(26,35,50,.5)}@media (min-width:640px){.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}
\ No newline at end of file
diff --git a/static/static.go b/static/static.go
new file mode 100644
index 0000000..28ee133
--- /dev/null
+++ b/static/static.go
@@ -0,0 +1,10 @@
+// Package static provides embedded static assets.
+package static
+
+import "embed"
+
+// Static contains the embedded static assets (CSS, JS) served
+// at the /s/ URL prefix.
+//
+//go:embed css
+var Static embed.FS