diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 00dc024..c2c8440 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -68,6 +68,9 @@ jobs:
# Authenticates the build-time GitHub API fetch on the home page
# (unauthenticated requests are rate-limited on shared runners).
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ # GoatCounter site code (the "xyz" in xyz.goatcounter.com), set as
+ # a repository variable. Unset means the site ships no analytics.
+ NEXT_PUBLIC_GOATCOUNTER_CODE: ${{ vars.GOATCOUNTER_CODE }}
- name: Upload artifact
uses: actions/upload-pages-artifact@v5
diff --git a/README.md b/README.md
index 04c0859..9c876da 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,14 @@ pnpm check-links # after a build: fail on broken internal links in ./out
pnpm format # format with Prettier
```
+## Analytics
+
+Page views and resume PDF downloads are counted with
+[GoatCounter](https://www.goatcounter.com/) (no cookies). It is off unless the
+`GOATCOUNTER_CODE` repository variable is set (Settings → Secrets and
+variables → Actions → Variables) to the site code, the `xyz` in
+`xyz.goatcounter.com`. The next deploy picks it up.
+
## Credits
The design is based on the [Spotlight](https://spotlight.tailwindui.com/)
diff --git a/app/components/Analytics.tsx b/app/components/Analytics.tsx
new file mode 100644
index 0000000..b995328
--- /dev/null
+++ b/app/components/Analytics.tsx
@@ -0,0 +1,21 @@
+import Script from 'next/script';
+
+/* GoatCounter: cookieless page-view counts, plus click events for elements
+ with a data-goatcounter-click attribute (e.g. the resume PDF links).
+ The site code comes from the GOATCOUNTER_CODE repository variable at
+ deploy time; without it, nothing is loaded. */
+const code = process.env.NEXT_PUBLIC_GOATCOUNTER_CODE;
+
+export function Analytics() {
+ if (!code) {
+ return null;
+ }
+
+ return (
+
+ );
+}
diff --git a/app/components/Container.tsx b/app/components/Container.tsx
index d43ea41..1ef59f1 100644
--- a/app/components/Container.tsx
+++ b/app/components/Container.tsx
@@ -6,8 +6,10 @@ export function ContainerOuter({
...props
}: React.ComponentPropsWithoutRef<'div'>) {
return (
-