OOKNET                             [ /  search the index  ]  
──────────────────────────────────────────────────────────────────────────────────────
══════════════════════════════════════════════════════════════════════════════════════
OOKNET   [ /  search  ]  
────────────────────────────────────────────────
════════════════════════════════════════════════
 
HASH      65bc9b129161
DATE      2026-07-18
SUBJECT   web: real project descriptions, per-project docs nav, slug-safe ooknet-org
          docs dir
FILES     15 CHANGED
HASH      65bc9b129161
DATE      2026-07-18
SUBJECT   web: real project descriptions,
          per-project docs nav, slug-safe
          ooknet-org docs dir
FILES     15 CHANGED
 

diff --git a/ooknet-design/src/components/DocsNav/DocsNav.astro b/ooknet-design/sr
c/components/DocsNav/DocsNav.astro
index e6550e5..b4b44b7 100644
--- a/ooknet-design/src/components/DocsNav/DocsNav.astro
+++ b/ooknet-design/src/components/DocsNav/DocsNav.astro
@@ -15,13 +15,17 @@ import "./DocsNav.scss";
 interface Props {
   docs: DocEntry[];
   currentId?: string;
+  /** Card title, e.g. the project name. */
+  title?: string;
+  /** Url prefix for tree links. */
+  base?: string;
 }
-const { docs, currentId } = Astro.props as Props;
-const nodes = docsTree(docs, currentId);
+const { docs, currentId, title = "INDEX", base = "/docs/" } = Astro.props as Prop
s;
+const nodes = docsTree(docs, currentId, base);
 const rows = layoutTree(nodes);
-const inner = Math.max(12, ...rows.map((r) => len(r.prefix) + len(r.label)));
+const inner = Math.max(12, len(title) + 2, ...rows.map((r) => len(r.prefix) + len
(r.label)));
 
-const top = `${G.TL}${G.H} INDEX ${G.H.repeat(Math.max(0, inner - 6))}${G.TR}`;
+const top = `${G.TL}${G.H} ${title} ${G.H.repeat(Math.max(0, inner - 1 - len(titl
e)))}${G.TR}`;
 const blank = `${G.V}${" ".repeat(inner + 2)}${G.V}`;
 const bottom = `${G.BL}${G.H.repeat(inner + 2)}${G.BR}`;
 ---

diff --git a/ooknet-design/src/components/Do
csNav/DocsNav.astro b/ooknet-design/src/comp
onents/DocsNav/DocsNav.astro
index e6550e5..b4b44b7 100644
--- a/ooknet-design/src/components/DocsNav/D
ocsNav.astro
+++ b/ooknet-design/src/components/DocsNav/D
ocsNav.astro
@@ -15,13 +15,17 @@ import "./DocsNav.scss";
 interface Props {
   docs: DocEntry[];
   currentId?: string;
+  /** Card title, e.g. the project name. */
+  title?: string;
+  /** Url prefix for tree links. */
+  base?: string;
 }
-const { docs, currentId } = Astro.props as 
Props;
-const nodes = docsTree(docs, currentId);
+const { docs, currentId, title = "INDEX", b
ase = "/docs/" } = Astro.props as Props;
+const nodes = docsTree(docs, currentId, bas
e);
 const rows = layoutTree(nodes);
-const inner = Math.max(12, ...rows.map((r) 
=> len(r.prefix) + len(r.label)));
+const inner = Math.max(12, len(title) + 2, 
...rows.map((r) => len(r.prefix) + len(r.lab
el)));
 
-const top = `${G.TL}${G.H} INDEX ${G.H.repe
at(Math.max(0, inner - 6))}${G.TR}`;
+const top = `${G.TL}${G.H} ${title} ${G.H.r
epeat(Math.max(0, inner - 1 - len(title)))}$
{G.TR}`;
 const blank = `${G.V}${" ".repeat(inner + 2
)}${G.V}`;
 const bottom = `${G.BL}${G.H.repeat(inner +
 2)}${G.BR}`;
 ---
 

diff --git a/ooknet-design/src/content/docs/ooknet.org/authoring/wikilinks.md b/oo
knet-design/src/content/docs/ooknet-org/authoring/wikilinks.md
similarity index 97%
rename from ooknet-design/src/content/docs/ooknet.org/authoring/wikilinks.md
rename to ooknet-design/src/content/docs/ooknet-org/authoring/wikilinks.md
index 105255f..b62c3c8 100644
--- a/ooknet-design/src/content/docs/ooknet.org/authoring/wikilinks.md
+++ b/ooknet-design/src/content/docs/ooknet-org/authoring/wikilinks.md
@@ -12,7 +12,7 @@ order: 2
 | `[[Style Sheet\|see the sheet]]` | alias - target before the pipe |
 
 Live: [[Entropy Bounds in Sparse Graphs]] by title,
-[[ooknet.org/style-sheet]] by id,
+[[ooknet-org/style-sheet]] by id,
 [[Entropy Bounds in Sparse Graphs|aliased to this]] with an alias.
 
 # Resolution

diff --git a/ooknet-design/src/content/docs/
ooknet.org/authoring/wikilinks.md b/ooknet-d
esign/src/content/docs/ooknet-org/authoring/
wikilinks.md
similarity index 97%
rename from ooknet-design/src/content/docs/o
oknet.org/authoring/wikilinks.md
rename to ooknet-design/src/content/docs/ook
net-org/authoring/wikilinks.md
index 105255f..b62c3c8 100644
--- a/ooknet-design/src/content/docs/ooknet.
org/authoring/wikilinks.md
+++ b/ooknet-design/src/content/docs/ooknet-
org/authoring/wikilinks.md
@@ -12,7 +12,7 @@ order: 2
 | `[[Style Sheet\|see the sheet]]` | alias 
- target before the pipe |
 
 Live: [[Entropy Bounds in Sparse Graphs]] b
y title,
-[[ooknet.org/style-sheet]] by id,
+[[ooknet-org/style-sheet]] by id,
 [[Entropy Bounds in Sparse Graphs|aliased t
o this]] with an alias.
 
 # Resolution
 

diff --git a/ooknet-design/src/content/docs/ooknet.org/authoring/writing-content.m
d b/ooknet-design/src/content/docs/ooknet-org/authoring/writing-content.md
similarity index 100%
rename from ooknet-design/src/content/docs/ooknet.org/authoring/writing-content.md
rename to ooknet-design/src/content/docs/ooknet-org/authoring/writing-content.md

diff --git a/ooknet-design/src/content/docs/
ooknet.org/authoring/writing-content.md b/oo
knet-design/src/content/docs/ooknet-org/auth
oring/writing-content.md
similarity index 100%
rename from ooknet-design/src/content/docs/o
oknet.org/authoring/writing-content.md
rename to ooknet-design/src/content/docs/ook
net-org/authoring/writing-content.md
 

diff --git a/ooknet-design/src/content/docs/ooknet.org/design/components.md b/ookn
et-design/src/content/docs/ooknet-org/design/components.md
similarity index 100%
rename from ooknet-design/src/content/docs/ooknet.org/design/components.md
rename to ooknet-design/src/content/docs/ooknet-org/design/components.md

diff --git a/ooknet-design/src/content/docs/
ooknet.org/design/components.md b/ooknet-des
ign/src/content/docs/ooknet-org/design/compo
nents.md
similarity index 100%
rename from ooknet-design/src/content/docs/o
oknet.org/design/components.md
rename to ooknet-design/src/content/docs/ook
net-org/design/components.md
 

diff --git a/ooknet-design/src/content/docs/ooknet.org/design/frames.md b/ooknet-d
esign/src/content/docs/ooknet-org/design/frames.md
similarity index 100%
rename from ooknet-design/src/content/docs/ooknet.org/design/frames.md
rename to ooknet-design/src/content/docs/ooknet-org/design/frames.md

diff --git a/ooknet-design/src/content/docs/
ooknet.org/design/frames.md b/ooknet-design/
src/content/docs/ooknet-org/design/frames.md
similarity index 100%
rename from ooknet-design/src/content/docs/o
oknet.org/design/frames.md
rename to ooknet-design/src/content/docs/ook
net-org/design/frames.md
 

diff --git a/ooknet-design/src/content/docs/ooknet.org/design/pipeline.md b/ooknet
-design/src/content/docs/ooknet-org/design/pipeline.md
similarity index 100%
rename from ooknet-design/src/content/docs/ooknet.org/design/pipeline.md
rename to ooknet-design/src/content/docs/ooknet-org/design/pipeline.md

diff --git a/ooknet-design/src/content/docs/
ooknet.org/design/pipeline.md b/ooknet-desig
n/src/content/docs/ooknet-org/design/pipelin
e.md
similarity index 100%
rename from ooknet-design/src/content/docs/o
oknet.org/design/pipeline.md
rename to ooknet-design/src/content/docs/ook
net-org/design/pipeline.md
 

diff --git a/ooknet-design/src/content/docs/ooknet.org/overview.md b/ooknet-design
/src/content/docs/ooknet-org/overview.md
similarity index 85%
rename from ooknet-design/src/content/docs/ooknet.org/overview.md
rename to ooknet-design/src/content/docs/ooknet-org/overview.md
index dbe17da..ff3744b 100644
--- a/ooknet-design/src/content/docs/ooknet.org/overview.md
+++ b/ooknet-design/src/content/docs/ooknet-org/overview.md
@@ -10,5 +10,5 @@ strict monospace grid. Notes, a credited knowledge base, and pro
ject
 documentation, cross-linked by a build-time wiki graph.
 
 The design system behind it lives in these docs - start with
-[[ooknet.org/design/frames]] for how the grid works, or the
+[[ooknet-org/design/frames]] for how the grid works, or the
 [[Style Sheet]] to see every construct the pipeline renders.

diff --git a/ooknet-design/src/content/docs/
ooknet.org/overview.md b/ooknet-design/src/c
ontent/docs/ooknet-org/overview.md
similarity index 85%
rename from ooknet-design/src/content/docs/o
oknet.org/overview.md
rename to ooknet-design/src/content/docs/ook
net-org/overview.md
index dbe17da..ff3744b 100644
--- a/ooknet-design/src/content/docs/ooknet.
org/overview.md
+++ b/ooknet-design/src/content/docs/ooknet-
org/overview.md
@@ -10,5 +10,5 @@ strict monospace grid. Not
es, a credited knowledge base, and project
 documentation, cross-linked by a build-time
 wiki graph.
 
 The design system behind it lives in these 
docs - start with
-[[ooknet.org/design/frames]] for how the gr
id works, or the
+[[ooknet-org/design/frames]] for how the gr
id works, or the
 [[Style Sheet]] to see every construct the 
pipeline renders.
 

diff --git a/ooknet-design/src/content/docs/ooknet.org/style-sheet.md b/ooknet-des
ign/src/content/docs/ooknet-org/style-sheet.md
similarity index 98%
rename from ooknet-design/src/content/docs/ooknet.org/style-sheet.md
rename to ooknet-design/src/content/docs/ooknet-org/style-sheet.md
index 7db9052..9fd4c05 100644
--- a/ooknet-design/src/content/docs/ooknet.org/style-sheet.md
+++ b/ooknet-design/src/content/docs/ooknet-org/style-sheet.md
@@ -111,7 +111,7 @@ palette stays monochrome:
 
 A wikilink resolves by title [[Entropy Bounds in Sparse Graphs]],
 by file id [[graphs/entropy-bounds]], or doc path
-[[ooknet.org/design/frames]], and takes an alias:
+[[ooknet-org/design/frames]], and takes an alias:
 [[Entropy Bounds in Sparse Graphs|the entropy note]]. Unresolved
 targets render dead: [[A NOTE THAT DOES NOT EXIST]].
 

diff --git a/ooknet-design/src/content/docs/
ooknet.org/style-sheet.md b/ooknet-design/sr
c/content/docs/ooknet-org/style-sheet.md
similarity index 98%
rename from ooknet-design/src/content/docs/o
oknet.org/style-sheet.md
rename to ooknet-design/src/content/docs/ook
net-org/style-sheet.md
index 7db9052..9fd4c05 100644
--- a/ooknet-design/src/content/docs/ooknet.
org/style-sheet.md
+++ b/ooknet-design/src/content/docs/ooknet-
org/style-sheet.md
@@ -111,7 +111,7 @@ palette stays monochrome
:
 
 A wikilink resolves by title [[Entropy Boun
ds in Sparse Graphs]],
 by file id [[graphs/entropy-bounds]], or do
c path
-[[ooknet.org/design/frames]], and takes an 
alias:
+[[ooknet-org/design/frames]], and takes an 
alias:
 [[Entropy Bounds in Sparse Graphs|the entro
py note]]. Unresolved
 targets render dead: [[A NOTE THAT DOES NOT
 EXIST]].
 
 

diff --git a/ooknet-design/src/content/docs/ooknet/overview.md b/ooknet-design/src
/content/docs/ooknet/overview.md
index 29204f5..080d189 100644
--- a/ooknet-design/src/content/docs/ooknet/overview.md
+++ b/ooknet-design/src/content/docs/ooknet/overview.md
@@ -5,5 +5,5 @@ order: 0
 
 # ooknet
 
-Documentation for the ooknet homelab stack lands here. Nothing filed
-yet.
+Documentation for ooknet, my personal nixos infrastructure
+configuration. Nothing filed yet.

diff --git a/ooknet-design/src/content/docs/
ooknet/overview.md b/ooknet-design/src/conte
nt/docs/ooknet/overview.md
index 29204f5..080d189 100644
--- a/ooknet-design/src/content/docs/ooknet/
overview.md
+++ b/ooknet-design/src/content/docs/ooknet/
overview.md
@@ -5,5 +5,5 @@ order: 0
 
 # ooknet
 
-Documentation for the ooknet homelab stack 
lands here. Nothing filed
-yet.
+Documentation for ooknet, my personal nixos
 infrastructure
+configuration. Nothing filed yet.
 

diff --git a/ooknet-design/src/lib/docs.ts b/ooknet-design/src/lib/docs.ts
index 6b1fb9e..cf58664 100644
--- a/ooknet-design/src/lib/docs.ts
+++ b/ooknet-design/src/lib/docs.ts
@@ -2,6 +2,13 @@
 // "design/frames". astro:content stays in the pages, as with notes.ts.
 import type { TreeNode } from "../components/Tree/Tree";
 
+// Collection ids are slugified (dots stripped), so directory names
+// stay slug-safe and pretty names live here.
+const PROJECT_LABELS: Record<string, string> = { "ooknet-org": "OOKNET.ORG" };
+
+export const projectLabel = (seg: string): string =>
+  PROJECT_LABELS[seg] ?? seg.toUpperCase();
+
 export interface DocEntry {
   id: string;
   title: string;

diff --git a/ooknet-design/src/lib/docs.ts b
/ooknet-design/src/lib/docs.ts
index 6b1fb9e..cf58664 100644
--- a/ooknet-design/src/lib/docs.ts
+++ b/ooknet-design/src/lib/docs.ts
@@ -2,6 +2,13 @@
 // "design/frames". astro:content stays in 
the pages, as with notes.ts.
 import type { TreeNode } from "../component
s/Tree/Tree";
 
+// Collection ids are slugified (dots strip
ped), so directory names
+// stay slug-safe and pretty names live her
e.
+const PROJECT_LABELS: Record<string, string
> = { "ooknet-org": "OOKNET.ORG" };
+
+export const projectLabel = (seg: string): 
string =>
+  PROJECT_LABELS[seg] ?? seg.toUpperCase();
+
 export interface DocEntry {
   id: string;
   title: string;
 

diff --git a/ooknet-design/src/lib/notes.test.ts b/ooknet-design/src/lib/notes.tes
t.ts
index 2c149b5..749c1b3 100644
--- a/ooknet-design/src/lib/notes.test.ts
+++ b/ooknet-design/src/lib/notes.test.ts
@@ -12,7 +12,7 @@ describe("published", () => {
 describe("topSegment", () => {
   it("takes the first directory of an id", () => {
     expect(topSegment("nix/flakes")).toBe("nix");
-    expect(topSegment("ooknet.org/design/frames")).toBe("ooknet.org");
+    expect(topSegment("ooknet-org/design/frames")).toBe("ooknet-org");
     expect(topSegment("orphan")).toBe("orphan");
   });
 });

diff --git a/ooknet-design/src/lib/notes.tes
t.ts b/ooknet-design/src/lib/notes.test.ts
index 2c149b5..749c1b3 100644
--- a/ooknet-design/src/lib/notes.test.ts
+++ b/ooknet-design/src/lib/notes.test.ts
@@ -12,7 +12,7 @@ describe("published", () =
> {
 describe("topSegment", () => {
   it("takes the first directory of an id", 
() => {
     expect(topSegment("nix/flakes")).toBe("
nix");
-    expect(topSegment("ooknet.org/design/fr
ames")).toBe("ooknet.org");
+    expect(topSegment("ooknet-org/design/fr
ames")).toBe("ooknet-org");
     expect(topSegment("orphan")).toBe("orph
an");
   });
 });
 

diff --git a/ooknet-design/src/lib/wikilinks.test.ts b/ooknet-design/src/lib/wikil
inks.test.ts
index cde52c5..a8b7d90 100644
--- a/ooknet-design/src/lib/wikilinks.test.ts
+++ b/ooknet-design/src/lib/wikilinks.test.ts
@@ -9,11 +9,11 @@ describe("buildWikiMap", () => {
     expect(resolveWikilink("graphs/entropy-bounds", map)).toBe("/notes/graphs/ent
ropy-bounds/");
     expect(resolveWikilink("Entropy Bounds in Sparse Graphs", map)).toBe("/notes/
graphs/entropy-bounds/");
     expect(resolveWikilink("windows/collect-data-tss", map)).toBe("/kb/windows/co
llect-data-tss/");
-    expect(resolveWikilink("Style Sheet", map)).toBe("/docs/ooknet.org/style-shee
t/");
+    expect(resolveWikilink("Style Sheet", map)).toBe("/docs/ooknet-org/style-shee
t/");
   });
 
   it("is case-insensitive and trims", () => {
-    expect(resolveWikilink("  WIKILINKS  ", map)).toBe("/docs/ooknet.org/authorin
g/wikilinks/");
+    expect(resolveWikilink("  WIKILINKS  ", map)).toBe("/docs/ooknet-org/authorin
g/wikilinks/");
   });
 
   it("excludes drafts", () => {
@@ -26,7 +26,7 @@ describe("buildWikiMap", () => {
 });
 
 describe("wikiNodes", () => {
-  const map = new Map([["style sheet", "/docs/ooknet.org/style-sheet/"]]);
+  const map = new Map([["style sheet", "/docs/ooknet-org/style-sheet/"]]);
 
   it("leaves plain text untouched", () => {
     expect(wikiNodes("no links here", map)).toBeNull();
@@ -35,7 +35,7 @@ describe("wikiNodes", () => {
   it("splits text around a resolved link", () => {
     const nodes = wikiNodes("see [[Style Sheet]] for glyphs", map)!;
     expect(nodes.map((n) => n.type)).toEqual(["text", "link", "text"]);
-    expect(nodes[1].url).toBe("/docs/ooknet.org/style-sheet/");
+    expect(nodes[1].url).toBe("/docs/ooknet-org/style-sheet/");
   });
 
   it("uses the alias as the label", () => {
@@ -69,7 +69,7 @@ describe("buildBacklinks", () => {
     // the ai note cites the kb entry
     expect(urls("/kb/windows/collect-data-tss/")).toContain("/notes/ai/using-ai-t
o-troubleshoot/");
     // the style-sheet doc cites the entropy note
-    expect(urls("/notes/graphs/entropy-bounds/")).toContain("/docs/ooknet.org/sty
le-sheet/");
+    expect(urls("/notes/graphs/entropy-bounds/")).toContain("/docs/ooknet-org/sty
le-sheet/");
   });
 
   it("labels sources with their collection", () => {

diff --git a/ooknet-design/src/lib/wikilinks
.test.ts b/ooknet-design/src/lib/wikilinks.t
est.ts
index cde52c5..a8b7d90 100644
--- a/ooknet-design/src/lib/wikilinks.test.t
s
+++ b/ooknet-design/src/lib/wikilinks.test.t
s
@@ -9,11 +9,11 @@ describe("buildWikiMap", (
) => {
     expect(resolveWikilink("graphs/entropy-
bounds", map)).toBe("/notes/graphs/entropy-b
ounds/");
     expect(resolveWikilink("Entropy Bounds 
in Sparse Graphs", map)).toBe("/notes/graphs
/entropy-bounds/");
     expect(resolveWikilink("windows/collect
-data-tss", map)).toBe("/kb/windows/collect-
data-tss/");
-    expect(resolveWikilink("Style Sheet", m
ap)).toBe("/docs/ooknet.org/style-sheet/");
+    expect(resolveWikilink("Style Sheet", m
ap)).toBe("/docs/ooknet-org/style-sheet/");
   });
 
   it("is case-insensitive and trims", () =>
 {
-    expect(resolveWikilink("  WIKILINKS  ",
 map)).toBe("/docs/ooknet.org/authoring/wiki
links/");
+    expect(resolveWikilink("  WIKILINKS  ",
 map)).toBe("/docs/ooknet-org/authoring/wiki
links/");
   });
 
   it("excludes drafts", () => {
@@ -26,7 +26,7 @@ describe("buildWikiMap", (
) => {
 });
 
 describe("wikiNodes", () => {
-  const map = new Map([["style sheet", "/do
cs/ooknet.org/style-sheet/"]]);
+  const map = new Map([["style sheet", "/do
cs/ooknet-org/style-sheet/"]]);
 
   it("leaves plain text untouched", () => {
     expect(wikiNodes("no links here", map))
.toBeNull();
@@ -35,7 +35,7 @@ describe("wikiNodes", () =
> {
   it("splits text around a resolved link", 
() => {
     const nodes = wikiNodes("see [[Style Sh
eet]] for glyphs", map)!;
     expect(nodes.map((n) => n.type)).toEqua
l(["text", "link", "text"]);
-    expect(nodes[1].url).toBe("/docs/ooknet
.org/style-sheet/");
+    expect(nodes[1].url).toBe("/docs/ooknet
-org/style-sheet/");
   });
 
   it("uses the alias as the label", () => {
@@ -69,7 +69,7 @@ describe("buildBacklinks",
 () => {
     // the ai note cites the kb entry
     expect(urls("/kb/windows/collect-data-t
ss/")).toContain("/notes/ai/using-ai-to-trou
bleshoot/");
     // the style-sheet doc cites the entrop
y note
-    expect(urls("/notes/graphs/entropy-boun
ds/")).toContain("/docs/ooknet.org/style-she
et/");
+    expect(urls("/notes/graphs/entropy-boun
ds/")).toContain("/docs/ooknet-org/style-she
et/");
   });
 
   it("labels sources with their collection"
, () => {
 

diff --git a/ooknet-design/src/pages/components.astro b/ooknet-design/src/pages/co
mponents.astro
index f01141e..bdb9bc9 100644
--- a/ooknet-design/src/pages/components.astro
+++ b/ooknet-design/src/pages/components.astro
@@ -71,7 +71,7 @@ const DEMO_HEADINGS = [
 ];
 
 const HISTORY: TimelineEntry[] = [
-  { date: "2026-07-16", title: "FILED /WEBSITE", desc: "style sheet - exercises e
very pipeline construct", href: "/docs/ooknet.org/style-sheet/" },
+  { date: "2026-07-16", title: "FILED /WEBSITE", desc: "style sheet - exercises e
very pipeline construct", href: "/docs/ooknet-org/style-sheet/" },
   { date: "2026-04-27", title: "FILED /AI", desc: "using ai to troubleshoot and r
esolve issues", href: "/notes/ai/using-ai-to-troubleshoot/" },
   { date: "2026-04-25", title: "FILED /KB", href: "/kb/windows/collect-data-tss/"
 },
   { date: "2026-04-19", title: "FILED /GRAPHS", desc: "entropy bounds in sparse g
raphs", href: "/notes/graphs/entropy-bounds/" },

diff --git a/ooknet-design/src/pages/compone
nts.astro b/ooknet-design/src/pages/componen
ts.astro
index f01141e..bdb9bc9 100644
--- a/ooknet-design/src/pages/components.ast
ro
+++ b/ooknet-design/src/pages/components.ast
ro
@@ -71,7 +71,7 @@ const DEMO_HEADINGS = [
 ];
 
 const HISTORY: TimelineEntry[] = [
-  { date: "2026-07-16", title: "FILED /WEBS
ITE", desc: "style sheet - exercises every p
ipeline construct", href: "/docs/ooknet.org/
style-sheet/" },
+  { date: "2026-07-16", title: "FILED /WEBS
ITE", desc: "style sheet - exercises every p
ipeline construct", href: "/docs/ooknet-org/
style-sheet/" },
   { date: "2026-04-27", title: "FILED /AI",
 desc: "using ai to troubleshoot and resolve
 issues", href: "/notes/ai/using-ai-to-troub
leshoot/" },
   { date: "2026-04-25", title: "FILED /KB",
 href: "/kb/windows/collect-data-tss/" },
   { date: "2026-04-19", title: "FILED /GRAP
HS", desc: "entropy bounds in sparse graphs"
, href: "/notes/graphs/entropy-bounds/" },
 

diff --git a/ooknet-design/src/pages/docs/[...slug].astro b/ooknet-design/src/page
s/docs/[...slug].astro
index 525dca1..3565b79 100644
--- a/ooknet-design/src/pages/docs/[...slug].astro
+++ b/ooknet-design/src/pages/docs/[...slug].astro
@@ -13,7 +13,7 @@ import DocsNav from "../../components/DocsNav/DocsNav.astro";
 import ArticleBacklinks from "../../components/ArticleBacklinks/ArticleBacklinks.
astro";
 import { box, center, len, rule, ruleD, row2, wrap } from "../../lib/ascii";
 import { published, topSegment } from "../../lib/notes";
-import { docsOrder, type DocEntry } from "../../lib/docs";
+import { docsOrder, projectLabel, type DocEntry } from "../../lib/docs";
 import { buildBacklinks, type Backlink } from "../../lib/wikilinks";
 import { describeBody } from "../../lib/meta";
 
@@ -34,16 +34,25 @@ export async function getStaticPaths() {
     return { href: `/docs/${id}/`, note };
   };
 
-  return ordered.map((d, i) => ({
-    params: { slug: d.id },
-    props: {
-      doc: byId.get(d.id)!,
-      prev: link(ordered[i - 1]?.id),
-      next: link(ordered[i + 1]?.id),
-      all: ordered,
-      refs: backlinks.get(`/docs/${d.id}/`) ?? [],
-    },
-  }));
+  // Docs are siloed by project: the index card and prev/next never
+  // cross into another project's pages.
+  return ordered.map((d) => {
+    const project = topSegment(d.id);
+    const projectDocs = ordered.filter((p) => topSegment(p.id) === project);
+    const i = projectDocs.findIndex((p) => p.id === d.id);
+    const strip = (id: string) => id.slice(project.length + 1) || id;
+    return {
+      params: { slug: d.id },
+      props: {
+        doc: byId.get(d.id)!,
+        prev: link(projectDocs[i - 1]?.id),
+        next: link(projectDocs[i + 1]?.id),
+        all: projectDocs.map((p) => ({ ...p, id: strip(p.id) })),
+        currentId: strip(d.id),
+        refs: backlinks.get(`/docs/${d.id}/`) ?? [],
+      },
+    };
+  });
 }
 
 interface Props {
@@ -51,9 +60,11 @@ interface Props {
   prev: PageLink | null;
   next: PageLink | null;
   all: DocEntry[];
+  currentId: string;
   refs: Backlink[];
 }
-const { doc, prev, next, all, refs } = Astro.props as Props;
+const { doc, prev, next, all, currentId, refs } = Astro.props as Props;
+const project = topSegment(doc.id);
 const { Content, headings } = await render(doc);
 const description =
   doc.data.description ?? (describeBody(doc.body ?? "") || `OOKNET documentation 
- ${doc.data.title}.`);
@@ -70,13 +81,13 @@ const buildTitleCard = (w: number) => {
 ---
 <ArticleLayout title={`DOCS - ${doc.data.title}`} description={description} type=
"article">
   <TopHeader />
-  <Frame build={(w) => [row2("OOKNET DOCUMENTATION", `PROJECT: ${topSegment(doc.i
d).toUpperCase()}`, w), rule(w)].join("\n")} />
+  <Frame build={(w) => [row2("OOKNET DOCUMENTATION", `PROJECT: ${projectLabel(pro
ject)}`, w), rule(w)].join("\n")} />
   <Pre>{" "}</Pre>
   <Frame build={buildTitleCard} />
   <Pre>{" "}</Pre>
   <Breadcrumbs filed={`/DOCS/${doc.id.toUpperCase()}`} />
   <Frame build={(w) => ruleD(w)} />
-  <DocsNav docs={all} currentId={doc.id} />
+  <DocsNav docs={all} currentId={currentId} title={projectLabel(project)} base={`
/docs/${project}/`} />
   {headings.length >= 3 && <TableOfContents headings={headings} />}
   <Prose><Content /></Prose>
   {refs.length > 0 && (

diff --git a/ooknet-design/src/pages/docs/[.
..slug].astro b/ooknet-design/src/pages/docs
/[...slug].astro
index 525dca1..3565b79 100644
--- a/ooknet-design/src/pages/docs/[...slug]
.astro
+++ b/ooknet-design/src/pages/docs/[...slug]
.astro
@@ -13,7 +13,7 @@ import DocsNav from "../..
/components/DocsNav/DocsNav.astro";
 import ArticleBacklinks from "../../compone
nts/ArticleBacklinks/ArticleBacklinks.astro"
;
 import { box, center, len, rule, ruleD, row
2, wrap } from "../../lib/ascii";
 import { published, topSegment } from "../.
./lib/notes";
-import { docsOrder, type DocEntry } from ".
./../lib/docs";
+import { docsOrder, projectLabel, type DocE
ntry } from "../../lib/docs";
 import { buildBacklinks, type Backlink } fr
om "../../lib/wikilinks";
 import { describeBody } from "../../lib/met
a";
 
@@ -34,16 +34,25 @@ export async function ge
tStaticPaths() {
     return { href: `/docs/${id}/`, note };
   };
 
-  return ordered.map((d, i) => ({
-    params: { slug: d.id },
-    props: {
-      doc: byId.get(d.id)!,
-      prev: link(ordered[i - 1]?.id),
-      next: link(ordered[i + 1]?.id),
-      all: ordered,
-      refs: backlinks.get(`/docs/${d.id}/`)
 ?? [],
-    },
-  }));
+  // Docs are siloed by project: the index 
card and prev/next never
+  // cross into another project's pages.
+  return ordered.map((d) => {
+    const project = topSegment(d.id);
+    const projectDocs = ordered.filter((p) 
=> topSegment(p.id) === project);
+    const i = projectDocs.findIndex((p) => 
p.id === d.id);
+    const strip = (id: string) => id.slice(
project.length + 1) || id;
+    return {
+      params: { slug: d.id },
+      props: {
+        doc: byId.get(d.id)!,
+        prev: link(projectDocs[i - 1]?.id),
+        next: link(projectDocs[i + 1]?.id),
+        all: projectDocs.map((p) => ({ ...p
, id: strip(p.id) })),
+        currentId: strip(d.id),
+        refs: backlinks.get(`/docs/${d.id}/
`) ?? [],
+      },
+    };
+  });
 }
 
 interface Props {
@@ -51,9 +60,11 @@ interface Props {
   prev: PageLink | null;
   next: PageLink | null;
   all: DocEntry[];
+  currentId: string;
   refs: Backlink[];
 }
-const { doc, prev, next, all, refs } = Astr
o.props as Props;
+const { doc, prev, next, all, currentId, re
fs } = Astro.props as Props;
+const project = topSegment(doc.id);
 const { Content, headings } = await render(
doc);
 const description =
   doc.data.description ?? (describeBody(doc
.body ?? "") || `OOKNET documentation - ${do
c.data.title}.`);
@@ -70,13 +81,13 @@ const buildTitleCard = (
w: number) => {
 ---
 <ArticleLayout title={`DOCS - ${doc.data.ti
tle}`} description={description} type="artic
le">
   <TopHeader />
-  <Frame build={(w) => [row2("OOKNET DOCUME
NTATION", `PROJECT: ${topSegment(doc.id).toU
pperCase()}`, w), rule(w)].join("\n")} />
+  <Frame build={(w) => [row2("OOKNET DOCUME
NTATION", `PROJECT: ${projectLabel(project)}
`, w), rule(w)].join("\n")} />
   <Pre>{" "}</Pre>
   <Frame build={buildTitleCard} />
   <Pre>{" "}</Pre>
   <Breadcrumbs filed={`/DOCS/${doc.id.toUpp
erCase()}`} />
   <Frame build={(w) => ruleD(w)} />
-  <DocsNav docs={all} currentId={doc.id} />
+  <DocsNav docs={all} currentId={currentId}
 title={projectLabel(project)} base={`/docs/
${project}/`} />
   {headings.length >= 3 && <TableOfContents
 headings={headings} />}
   <Prose><Content /></Prose>
   {refs.length > 0 && (
 

diff --git a/ooknet-design/src/pages/projects.astro b/ooknet-design/src/pages/proj
ects.astro
index f543a0f..cf6d570 100644
--- a/ooknet-design/src/pages/projects.astro
+++ b/ooknet-design/src/pages/projects.astro
@@ -11,24 +11,25 @@ import { row2, ruleD } from "../lib/ascii";
 const buildHeader = (w: number) =>
   [row2("PROJECT LEDGER", "DEPT. OF OOKS", w), ruleD(w)].join("\n");
 
-const PROJECTS = [
+const PROJECTS: { name: string; desc: string; git: string; docs: string; web?: st
ring }[] = [
   {
     name: "OOKNET",
-    desc: "the homelab: hosts, services, and the nix flake that runs them all",
+    desc: "my personal nixos infrastructure configuration",
     git: "https://github.com/ooks-io/ooknet",
     docs: "/docs/ooknet/overview/",
   },
   {
     name: "OOKNET.ORG",
-    desc: "this site - a text-mode knowledge base and the ascii design system beh
ind it",
+    desc: "personal website - a text-mode knowledge base and the ascii design sys
tem that renders it, everything on a strict monospace grid",
     git: "https://github.com/ooks-io/ooknet.org",
-    docs: "/docs/ooknet.org/overview/",
+    docs: "/docs/ooknet-org/overview/",
   },
   {
     name: "WOWSIM-STATS",
-    desc: "simulation-driven stat weights and rankings for classic wow",
+    desc: "a nix pipeline that orchestrates mist of pandaria simulations at scale
, and a statically generated challenge mode leaderboard",
     git: "https://github.com/ooks-io/wowsim-stats",
     docs: "/docs/wowsim-stats/overview/",
+    web: "https://wowsimstats.com",
   },
 ];
 ---
@@ -41,7 +42,7 @@ const PROJECTS = [
     <Fragment>
       <Panel title={p.name}>
         <p class="project-desc">{p.desc}</p>
-        <Pre><Button label="GIT" href={p.git} />{"  "}<Button label="MORE" href={
p.docs} /></Pre>
+        <Pre><Button label="GIT" href={p.git} />{"  "}<Button label="MORE" href={
p.docs} />{p.web && <Fragment>{"  "}<Button label="WEB" href={p.web} /></Fragment>
}</Pre>
       </Panel>
       <Pre>{" "}</Pre>
     </Fragment>

diff --git a/ooknet-design/src/pages/project
s.astro b/ooknet-design/src/pages/projects.a
stro
index f543a0f..cf6d570 100644
--- a/ooknet-design/src/pages/projects.astro
+++ b/ooknet-design/src/pages/projects.astro
@@ -11,24 +11,25 @@ import { row2, ruleD } f
rom "../lib/ascii";
 const buildHeader = (w: number) =>
   [row2("PROJECT LEDGER", "DEPT. OF OOKS", 
w), ruleD(w)].join("\n");
 
-const PROJECTS = [
+const PROJECTS: { name: string; desc: strin
g; git: string; docs: string; web?: string }
[] = [
   {
     name: "OOKNET",
-    desc: "the homelab: hosts, services, an
d the nix flake that runs them all",
+    desc: "my personal nixos infrastructure
 configuration",
     git: "https://github.com/ooks-io/ooknet
",
     docs: "/docs/ooknet/overview/",
   },
   {
     name: "OOKNET.ORG",
-    desc: "this site - a text-mode knowledg
e base and the ascii design system behind it
",
+    desc: "personal website - a text-mode k
nowledge base and the ascii design system th
at renders it, everything on a strict monosp
ace grid",
     git: "https://github.com/ooks-io/ooknet
.org",
-    docs: "/docs/ooknet.org/overview/",
+    docs: "/docs/ooknet-org/overview/",
   },
   {
     name: "WOWSIM-STATS",
-    desc: "simulation-driven stat weights a
nd rankings for classic wow",
+    desc: "a nix pipeline that orchestrates
 mist of pandaria simulations at scale, and 
a statically generated challenge mode leader
board",
     git: "https://github.com/ooks-io/wowsim
-stats",
     docs: "/docs/wowsim-stats/overview/",
+    web: "https://wowsimstats.com",
   },
 ];
 ---
@@ -41,7 +42,7 @@ const PROJECTS = [
     <Fragment>
       <Panel title={p.name}>
         <p class="project-desc">{p.desc}</p
>
-        <Pre><Button label="GIT" href={p.gi
t} />{"  "}<Button label="MORE" href={p.docs
} /></Pre>
+        <Pre><Button label="GIT" href={p.gi
t} />{"  "}<Button label="MORE" href={p.docs
} />{p.web && <Fragment>{"  "}<Button label=
"WEB" href={p.web} /></Fragment>}</Pre>
       </Panel>
       <Pre>{" "}</Pre>
     </Fragment>
 
[ BACK TO LOG ]
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET