diff --git a/AGENTS.md b/AGENTS.md
index 8454ef2..312473f 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -47,7 +47,7 @@ Removing the fallback deleted the heuristics that existed only to decide it (`is
### `output.go` - Formatting, escaping, and colored printing
- `printDecryptedPayload()` / `escapeTerminalText()` / `escapeFormattedJSONControls()` - Recursively decode nested JWTs/JWEs and pretty-print JSON objects or arrays; raw plaintext escapes C0 controls except newline/tab, DEL, C1 controls, invalid UTF-8 bytes, and targeted bidi controls, while formatted JSON sanitizes C1, DEL, and the same targeted bidi controls
-- `formatTimestamps()` / `timestampStatus()` - Convert exact `iat`, `exp`, `nbf` Unix numeric values, including fractions, to RFC3339 strings (original value shown in parentheses); `exp` in the past is annotated `expired` and `nbf` in the future `not yet valid`. This is display-only and never affects verification or the exit code; `timeNow` is a package variable so the annotations are testable. The `--json` path skips this entirely and keeps raw numeric claims
+- `formatTimestamps()` / `timestampStatus()` / `humanizeDuration()` - Convert exact `iat`, `exp`, `nbf` Unix numeric values, including fractions, to RFC3339 strings (original value shown in parentheses); `exp` is annotated with the time remaining or elapsed (`expires in 14m` / `expired 2h ago`) and a future `nbf` with `not yet valid, in 5m` (an already-valid `nbf` gets no note). `humanizeDuration` renders the largest whole unit (s/m/h/d), truncating toward zero for deterministic output. This is display-only and never affects verification or the exit code; `timeNow` is a package variable so the annotations are testable. The `--json` path skips this entirely and keeps raw numeric claims
- `newFormatter()` - Creates a `go-prettyjson` formatter with the project color scheme
- `printSection()` / `printSignature()` - Formatted output using `fatih/color`
diff --git a/README.md b/README.md
index e4ec899..9e5788d 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ A CLI tool that decodes and pretty-prints JSON Web Tokens (JWTs) and JSON Web En
- `JWTD_KEY` environment variable for default key configuration
- Syntax-highlighted JSON output with a consistent color scheme
- Machine-readable output with `--json` for scripting and piping into tools like `jq`
-- Automatic conversion of `iat`, `exp`, and `nbf` timestamps to human-readable RFC3339 dates, with `expired` / `not yet valid` annotations
+- Automatic conversion of `iat`, `exp`, and `nbf` timestamps to human-readable RFC3339 dates, annotated with the time remaining or elapsed (`expires in 14m`, `expired 2h ago`, `not yet valid, in 5m`)
- Accepts tokens as arguments, from stdin pipes, or via an interactive prompt
- Colors auto-disable when output is not a TTY, or are controlled explicitly with `--color`
- Shell completions (bash, zsh, fish) shipped in the Homebrew formula and the `.deb`/`.rpm` packages
diff --git a/docs/superpowers/plans/2026-07-22-project-website.md b/docs/superpowers/plans/2026-07-22-project-website.md
new file mode 100644
index 0000000..1060357
--- /dev/null
+++ b/docs/superpowers/plans/2026-07-22-project-website.md
@@ -0,0 +1,1872 @@
+# jwtd Project Website Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Build and deploy a fast, accessible, progressively enhanced single-page website for jwtd at `https://jwtd.webcodr.io/`.
+
+**Architecture:** Serve a hand-built static document from `site/` with all essential content and anchor navigation in HTML, Tokyo Night presentation in one local stylesheet, and optional browser enhancements in one local script. A focused Go contract test locks down repository-owned website and GitHub Pages invariants, while Node's built-in test runner exercises the script's pure OS detection logic without adding npm or a frontend build step.
+
+**Tech Stack:** Semantic HTML5, CSS, browser JavaScript, Node.js 26.4.0 built-in test runner, Go 1.26 repository tests, GitHub Pages official actions.
+
+---
+
+## File Map
+
+- Create: `site/index.html` - complete single-page content, metadata, semantic landmarks, no-JavaScript navigation, installation methods, usage documentation, and local asset references.
+- Create: `site/styles.css` - responsive terminal-editorial layout, approved Tokyo Night tokens, CLI syntax colors, focus states, tab/mobile-menu enhancement states, and reduced-motion behavior.
+- Create: `site/script.js` - pure OS detection plus accessible installation tabs, copy feedback, and mobile navigation enhancements.
+- Create: `site/script.test.js` - dependency-free Node tests for OS detection, install-method mapping, fallback, and source-priority behavior.
+- Create: `site/favicon.svg` - local, Tokyo Night-colored favicon with no external resource.
+- Create: `site/CNAME` - exact custom domain declaration, `jwtd.webcodr.io`.
+- Create: `site_test.go` - focused repository contract for the custom domain, canonical URL, core sections, local assets, CSP, Tokyo Night/CLI colors, Pages workflow, and pinned Node CI support.
+- Create: `.github/workflows/pages.yml` - build-free Pages artifact upload and deployment with full-SHA official actions, least privilege, deployment environment, and concurrency.
+- Modify: `.github/workflows/test.yml` - install a full-SHA-pinned Node 26.4.0 only in the test job, then run syntax and built-in tests; leave mise, release jobs, and all existing Go and release-package checks unchanged.
+
+Do not modify application source, release configuration, `.github/workflows/release.yml`, release packaging, README content, or any existing release behavior. DNS remains an external prerequisite: `jwtd.webcodr.io` must be a CNAME for `webcodr.github.io`.
+
+### Task 1: Add Tested Progressive Enhancements
+
+**Files:**
+- Create: `site/script.test.js`
+- Create: `site/script.js`
+
+- [ ] **Step 1: Write the failing OS detection tests**
+
+Create the site directory after verifying its parent: `ls . && mkdir site`.
+
+Expected: the repository root listing succeeds and the empty `site/` directory is created.
+
+Create `site/script.test.js`:
+
+```javascript
+"use strict";
+
+const test = require("node:test");
+const assert = require("node:assert/strict");
+
+const {
+ detectOperatingSystem,
+ installMethodForOperatingSystem,
+} = require("./script.js");
+
+test("detectOperatingSystem classifies supported operating systems", () => {
+ const cases = [
+ ["macOS", "", "", "macos"],
+ ["", "MacIntel", "", "macos"],
+ ["", "", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)", "macos"],
+ ["Windows", "", "", "windows"],
+ ["", "Win32", "", "windows"],
+ ["", "", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "windows"],
+ ["Linux", "", "", "linux"],
+ ["", "Linux x86_64", "", "linux"],
+ ["", "", "Mozilla/5.0 (X11; Linux x86_64)", "linux"],
+ ];
+
+ for (const [userAgentDataPlatform, platform, userAgent, expected] of cases) {
+ assert.equal(
+ detectOperatingSystem(userAgentDataPlatform, platform, userAgent),
+ expected,
+ );
+ }
+});
+
+test("detectOperatingSystem returns unknown when no platform matches", () => {
+ assert.equal(detectOperatingSystem("", "", ""), "unknown");
+ assert.equal(detectOperatingSystem("Plan 9", "Unknown", "custom-client"), "unknown");
+});
+
+test("installMethodForOperatingSystem selects the approved default", () => {
+ assert.equal(installMethodForOperatingSystem("macos"), "homebrew");
+ assert.equal(installMethodForOperatingSystem("windows"), "scoop");
+ assert.equal(installMethodForOperatingSystem("linux"), "linux");
+ assert.equal(installMethodForOperatingSystem("unknown"), "homebrew");
+});
+
+test("detectOperatingSystem honors platform source priority", () => {
+ assert.equal(
+ detectOperatingSystem("Windows", "MacIntel", "Mozilla/5.0 (X11; Linux x86_64)"),
+ "windows",
+ );
+ assert.equal(
+ detectOperatingSystem("", "MacIntel", "Mozilla/5.0 (Windows NT 10.0)"),
+ "macos",
+ );
+});
+```
+
+- [ ] **Step 2: Run the tests and verify the module is missing**
+
+Run: `node --test site/script.test.js`
+
+Expected: FAIL with `Cannot find module './script.js'`.
+
+- [ ] **Step 3: Implement the pure detector and browser enhancements**
+
+Create `site/script.js`:
+
+```javascript
+"use strict";
+
+function classifyPlatform(value) {
+ const normalized = String(value || "").toLowerCase();
+
+ if (/mac|iphone|ipad|ipod/.test(normalized)) {
+ return "macos";
+ }
+ if (/win/.test(normalized)) {
+ return "windows";
+ }
+ if (/linux|x11/.test(normalized)) {
+ return "linux";
+ }
+ return "unknown";
+}
+
+function detectOperatingSystem(userAgentDataPlatform, platform, userAgent) {
+ for (const candidate of [userAgentDataPlatform, platform, userAgent]) {
+ const detected = classifyPlatform(candidate);
+ if (detected !== "unknown") {
+ return detected;
+ }
+ }
+ return "unknown";
+}
+
+function installMethodForOperatingSystem(operatingSystem) {
+ if (operatingSystem === "windows") {
+ return "scoop";
+ }
+ if (operatingSystem === "linux") {
+ return "linux";
+ }
+ return "homebrew";
+}
+
+if (typeof module !== "undefined" && module.exports) {
+ module.exports = { detectOperatingSystem, installMethodForOperatingSystem };
+}
+
+if (typeof document !== "undefined") {
+ document.documentElement.classList.add("js");
+
+ const initialize = () => {
+ const tabs = Array.from(document.querySelectorAll('[role="tab"]'));
+ const panels = Array.from(document.querySelectorAll('[role="tabpanel"]'));
+
+ const selectTab = (method, moveFocus = false) => {
+ for (const tab of tabs) {
+ const selected = tab.dataset.installMethod === method;
+ tab.setAttribute("aria-selected", String(selected));
+ tab.tabIndex = selected ? 0 : -1;
+ if (selected && moveFocus) {
+ tab.focus();
+ }
+ }
+
+ for (const panel of panels) {
+ panel.hidden = panel.dataset.installPanel !== method;
+ }
+ };
+
+ if (tabs.length > 0 && panels.length > 0) {
+ let operatingSystem = "unknown";
+ try {
+ operatingSystem = detectOperatingSystem(
+ navigator.userAgentData?.platform || "",
+ navigator.platform || "",
+ navigator.userAgent || "",
+ );
+ } catch {
+ operatingSystem = "unknown";
+ }
+
+ selectTab(installMethodForOperatingSystem(operatingSystem));
+
+ tabs.forEach((tab, index) => {
+ tab.addEventListener("click", (event) => {
+ event.preventDefault();
+ selectTab(tab.dataset.installMethod);
+ });
+
+ tab.addEventListener("keydown", (event) => {
+ let nextIndex;
+ if (event.key === "ArrowRight" || event.key === "ArrowDown") {
+ nextIndex = (index + 1) % tabs.length;
+ } else if (event.key === "ArrowLeft" || event.key === "ArrowUp") {
+ nextIndex = (index - 1 + tabs.length) % tabs.length;
+ } else if (event.key === "Home") {
+ nextIndex = 0;
+ } else if (event.key === "End") {
+ nextIndex = tabs.length - 1;
+ } else {
+ return;
+ }
+
+ event.preventDefault();
+ selectTab(tabs[nextIndex].dataset.installMethod, true);
+ });
+ });
+ }
+
+ const feedbackTimers = new WeakMap();
+ for (const button of document.querySelectorAll("[data-copy-target]")) {
+ button.addEventListener("click", async () => {
+ const command = document.getElementById(button.dataset.copyTarget);
+ const feedback = button.parentElement.querySelector("[data-copy-feedback]");
+ if (!command || !feedback) {
+ return;
+ }
+
+ const previousTimer = feedbackTimers.get(button);
+ if (previousTimer) {
+ window.clearTimeout(previousTimer);
+ }
+
+ try {
+ if (!navigator.clipboard?.writeText) {
+ throw new Error("Clipboard API unavailable");
+ }
+ await navigator.clipboard.writeText(command.textContent.trim());
+ feedback.textContent = "Copied.";
+ } catch {
+ feedback.textContent = "Select the command and copy it manually.";
+ }
+
+ feedbackTimers.set(
+ button,
+ window.setTimeout(() => {
+ feedback.textContent = "";
+ }, 3000),
+ );
+ });
+ }
+
+ const navToggle = document.querySelector("[data-nav-toggle]");
+ const navigation = document.getElementById("primary-navigation");
+ if (navToggle && navigation) {
+ const closeNavigation = (restoreFocus = false) => {
+ navToggle.setAttribute("aria-expanded", "false");
+ navigation.dataset.open = "false";
+ if (restoreFocus) {
+ navToggle.focus();
+ }
+ };
+
+ navToggle.addEventListener("click", () => {
+ const open = navToggle.getAttribute("aria-expanded") !== "true";
+ navToggle.setAttribute("aria-expanded", String(open));
+ navigation.dataset.open = String(open);
+ });
+
+ navigation.addEventListener("click", (event) => {
+ if (event.target.closest("a")) {
+ closeNavigation();
+ }
+ });
+
+ document.addEventListener("keydown", (event) => {
+ if (event.key === "Escape" && navToggle.getAttribute("aria-expanded") === "true") {
+ closeNavigation(true);
+ }
+ });
+ }
+ };
+
+ if (document.readyState === "loading") {
+ document.addEventListener("DOMContentLoaded", initialize, { once: true });
+ } else {
+ initialize();
+ }
+}
+```
+
+- [ ] **Step 4: Run JavaScript tests and syntax checking**
+
+Run: `node --test site/script.test.js && node --check site/script.js`
+
+Expected: four passing tests followed by a zero-exit syntax check with no output.
+
+- [ ] **Step 5: Commit the independently tested enhancement module**
+
+```bash
+git add site/script.js site/script.test.js
+git commit -m "feat: add website progressive enhancements"
+```
+
+### Task 2: Build the Static Page and Tokyo Night Presentation
+
+**Files:**
+- Create: `site_test.go`
+- Create: `site/index.html`
+- Create: `site/styles.css`
+- Create: `site/favicon.svg`
+- Create: `site/CNAME`
+
+- [ ] **Step 1: Write the failing static-site contract test**
+
+Create `site_test.go`:
+
+```go
+package main
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+func readWebsiteFile(t *testing.T, elements ...string) string {
+ t.Helper()
+ data, err := os.ReadFile(filepath.Join(elements...))
+ if err != nil {
+ t.Fatalf("reading website file %s: %v", filepath.Join(elements...), err)
+ }
+ return string(data)
+}
+
+func TestWebsiteContentContract(t *testing.T) {
+ if got, want := readWebsiteFile(t, "site", "CNAME"), "jwtd.webcodr.io\n"; got != want {
+ t.Fatalf("site/CNAME must be exactly %q, got %q", want, got)
+ }
+
+ index := readWebsiteFile(t, "site", "index.html")
+ for label, required := range map[string]string{
+ "canonical URL": ``,
+ "content security": `default-src 'none'`,
+ "skip link": `href="#main-content"`,
+ "header landmark": `