@treeseed/core 0.8.8 → 0.8.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/components/SiteTitle.astro +2 -2
  2. package/dist/components/content/ContentStatusLegend.astro +4 -4
  3. package/dist/components/docs/BookFontControls.astro +9 -9
  4. package/dist/components/docs/DesktopSidebarToggle.astro +8 -8
  5. package/dist/components/docs/Footer.astro +7 -91
  6. package/dist/components/docs/Header.astro +9 -2
  7. package/dist/components/docs/PageTitle.astro +1 -1
  8. package/dist/components/docs/ThemeSelect.astro +3 -1
  9. package/dist/components/forms/ContactForm.astro +21 -21
  10. package/dist/components/forms/FooterSubscribeForm.astro +9 -9
  11. package/dist/components/site/BookList.astro +7 -7
  12. package/dist/components/site/CTASection.astro +4 -4
  13. package/dist/components/site/ChronicleList.astro +6 -6
  14. package/dist/components/site/Hero.astro +3 -3
  15. package/dist/components/site/PathCard.astro +5 -5
  16. package/dist/components/site/ProfileList.astro +5 -5
  17. package/dist/components/site/RouteNotFound.astro +6 -6
  18. package/dist/components/site/SectionIntro.astro +3 -3
  19. package/dist/components/site/StageBanner.astro +2 -2
  20. package/dist/components/site/TrustCallout.astro +3 -3
  21. package/dist/components/ui/data/ActionList.astro +51 -0
  22. package/dist/components/ui/data/Badge.astro +19 -0
  23. package/dist/components/ui/data/DataTable.astro +51 -0
  24. package/dist/components/ui/data/KeyValueList.astro +28 -0
  25. package/dist/components/ui/data/MetricCard.astro +25 -0
  26. package/dist/components/ui/data/MetricGrid.astro +27 -0
  27. package/dist/components/ui/data/StatusPill.astro +20 -0
  28. package/dist/components/ui/forms/Button.astro +52 -0
  29. package/dist/components/ui/forms/Field.astro +39 -0
  30. package/dist/components/ui/forms/FormActions.astro +12 -0
  31. package/dist/components/ui/forms/PasswordMeter.astro +80 -0
  32. package/dist/components/ui/forms/RadioGroup.astro +55 -0
  33. package/dist/components/ui/forms/Select.astro +44 -0
  34. package/dist/components/ui/forms/TextInput.astro +58 -0
  35. package/dist/components/ui/forms/Textarea.astro +45 -0
  36. package/dist/components/ui/layout/PageHeader.astro +45 -0
  37. package/dist/components/ui/shell/AppShell.astro +112 -0
  38. package/dist/components/ui/shell/BottomNav.astro +35 -0
  39. package/dist/components/ui/shell/ProjectHeader.astro +66 -0
  40. package/dist/components/ui/shell/PublicFooter.astro +39 -0
  41. package/dist/components/ui/shell/PublicShell.astro +179 -0
  42. package/dist/components/ui/shell/RailNav.astro +35 -0
  43. package/dist/components/ui/shell/TopBar.astro +52 -0
  44. package/dist/components/ui/surface/Card.astro +46 -0
  45. package/dist/components/ui/surface/EmptyState.astro +45 -0
  46. package/dist/components/ui/surface/Panel.astro +54 -0
  47. package/dist/components/ui/theme/ThemeMenu.astro +32 -0
  48. package/dist/components/ui/theme/ThemePreviewSwatch.astro +18 -0
  49. package/dist/components/ui/theme/ThemeScript.astro +111 -0
  50. package/dist/components/ui/theme/ThemeSelector.astro +202 -0
  51. package/dist/components/ui/types.js +0 -0
  52. package/dist/dev-watch.d.ts +6 -2
  53. package/dist/dev-watch.js +12 -3
  54. package/dist/dev.d.ts +10 -2
  55. package/dist/dev.js +352 -68
  56. package/dist/layouts/AuthoredEntryLayout.astro +27 -27
  57. package/dist/layouts/BookLayout.astro +10 -10
  58. package/dist/layouts/ContentLayout.astro +4 -4
  59. package/dist/layouts/MainLayout.astro +66 -193
  60. package/dist/layouts/NoteLayout.astro +6 -6
  61. package/dist/layouts/ProfileLayout.astro +17 -17
  62. package/dist/middleware/starlightRouteData.js +20 -14
  63. package/dist/pages/404.astro +8 -8
  64. package/dist/pages/[slug].astro +1 -1
  65. package/dist/pages/books/[slug].astro +5 -5
  66. package/dist/pages/contact.astro +4 -4
  67. package/dist/pages/docs-runtime/[...slug].astro +12 -12
  68. package/dist/pages/docs-runtime/index.astro +13 -13
  69. package/dist/pages/index.astro +28 -28
  70. package/dist/pages/ui/index.astro +216 -0
  71. package/dist/scripts/dev-platform.js +7 -1
  72. package/dist/site.js +53 -5
  73. package/dist/styles/app-shell.css +597 -0
  74. package/dist/styles/forms.css +258 -0
  75. package/dist/styles/global.css +125 -120
  76. package/dist/styles/prose.css +11 -11
  77. package/dist/styles/theme.css +177 -0
  78. package/dist/styles/tokens.css +62 -22
  79. package/dist/styles/ui.css +551 -0
  80. package/dist/utils/color-schemes/cedar.js +53 -0
  81. package/dist/utils/color-schemes/fern.js +53 -0
  82. package/dist/utils/color-schemes/index.js +13 -0
  83. package/dist/utils/color-schemes/lichen.js +53 -0
  84. package/dist/utils/color-schemes/shared.js +33 -0
  85. package/dist/utils/color-schemes/tidepool.js +53 -0
  86. package/dist/utils/content-status.js +5 -5
  87. package/dist/utils/site-config.js +2 -2
  88. package/dist/utils/starlight-nav.js +13 -7
  89. package/dist/utils/theme.js +133 -41
  90. package/package.json +36 -2
@@ -52,80 +52,80 @@ const {
52
52
 
53
53
  <MainLayout title={entry.title} description={entry.description} currentPath={currentPath}>
54
54
  <article class="max-w-4xl space-y-8">
55
- <div class="space-y-4 border-b border-[color:var(--site-border)] pb-8">
55
+ <div class="space-y-4 border-b border-[color:var(--ts-color-border)] pb-8">
56
56
  <div class="flex flex-wrap items-center gap-3">
57
57
  <StatusBadge status={entry.status} />
58
- <p class="text-sm font-medium text-[color:var(--site-text-soft)]">{entry.date.toISOString().slice(0, 10)}</p>
59
- {contributor && <p class="text-sm text-[color:var(--site-text-soft)]">{contributor.data.name}</p>}
60
- {metaLabel && metaValue && <p class="text-sm text-[color:var(--site-text-soft)]">{metaLabel}: {metaValue}</p>}
58
+ <p class="text-sm font-medium text-[color:var(--ts-color-text-subtle)]">{entry.date.toISOString().slice(0, 10)}</p>
59
+ {contributor && <p class="text-sm text-[color:var(--ts-color-text-subtle)]">{contributor.data.name}</p>}
60
+ {metaLabel && metaValue && <p class="text-sm text-[color:var(--ts-color-text-subtle)]">{metaLabel}: {metaValue}</p>}
61
61
  </div>
62
- <h1 class="max-w-4xl font-serif text-5xl font-bold tracking-tight text-[color:var(--site-text)] md:text-6xl">{entry.title}</h1>
63
- <p class="max-w-3xl text-xl leading-10 text-[color:var(--site-text-muted)]">{entry.summary}</p>
64
- {(introText ?? entry.motivation) && <p class="max-w-3xl text-base leading-8 text-[color:var(--site-text-muted)]">{introText ?? entry.motivation}</p>}
62
+ <h1 class="max-w-4xl font-serif text-5xl font-bold tracking-tight text-[color:var(--ts-color-text)] md:text-6xl">{entry.title}</h1>
63
+ <p class="max-w-3xl text-xl leading-10 text-[color:var(--ts-color-text-muted)]">{entry.summary}</p>
64
+ {(introText ?? entry.motivation) && <p class="max-w-3xl text-base leading-8 text-[color:var(--ts-color-text-muted)]">{introText ?? entry.motivation}</p>}
65
65
  {entry.tags.length > 0 && (
66
- <p class="text-sm uppercase tracking-[0.14em] text-[color:var(--site-accent-strong)]">{entry.tags.join(' / ')}</p>
66
+ <p class="text-sm uppercase tracking-[0.14em] text-[color:var(--ts-color-accent-strong)]">{entry.tags.join(' / ')}</p>
67
67
  )}
68
68
  </div>
69
69
  <div class="prose-karyon">
70
70
  <slot />
71
71
  </div>
72
- <div class="grid gap-6 border-t border-[color:var(--site-border)] pt-8 md:grid-cols-3">
72
+ <div class="grid gap-6 border-t border-[color:var(--ts-color-border)] pt-8 md:grid-cols-3">
73
73
  {relatedQuestions.length > 0 && (
74
74
  <div>
75
- <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--site-blue-strong)]">Related questions</p>
76
- <ul class="mt-3 space-y-2 text-[color:var(--site-text-muted)]">
75
+ <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)]">Related questions</p>
76
+ <ul class="mt-3 space-y-2 text-[color:var(--ts-color-text-muted)]">
77
77
  {relatedQuestions.map((question) => (
78
- <li><a href={`/questions/${question.id}/`} class="hover:text-[color:var(--site-text)]">{entryTitle(question)}</a></li>
78
+ <li><a href={`/questions/${question.id}/`} class="hover:text-[color:var(--ts-color-text)]">{entryTitle(question)}</a></li>
79
79
  ))}
80
80
  </ul>
81
81
  </div>
82
82
  )}
83
83
  {relatedObjectives.length > 0 && (
84
84
  <div>
85
- <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--site-blue-strong)]">Related objectives</p>
86
- <ul class="mt-3 space-y-2 text-[color:var(--site-text-muted)]">
85
+ <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)]">Related objectives</p>
86
+ <ul class="mt-3 space-y-2 text-[color:var(--ts-color-text-muted)]">
87
87
  {relatedObjectives.map((objective) => (
88
- <li><a href={`/objectives/${objective.id}/`} class="hover:text-[color:var(--site-text)]">{entryTitle(objective)}</a></li>
88
+ <li><a href={`/objectives/${objective.id}/`} class="hover:text-[color:var(--ts-color-text)]">{entryTitle(objective)}</a></li>
89
89
  ))}
90
90
  </ul>
91
91
  </div>
92
92
  )}
93
93
  {relatedNotes.length > 0 && (
94
94
  <div>
95
- <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--site-blue-strong)]">Related notes</p>
96
- <ul class="mt-3 space-y-2 text-[color:var(--site-text-muted)]">
95
+ <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)]">Related notes</p>
96
+ <ul class="mt-3 space-y-2 text-[color:var(--ts-color-text-muted)]">
97
97
  {relatedNotes.map((note) => (
98
- <li><a href={`/notes/${note.id}/`} class="hover:text-[color:var(--site-text)]">{entryTitle(note)}</a></li>
98
+ <li><a href={`/notes/${note.id}/`} class="hover:text-[color:var(--ts-color-text)]">{entryTitle(note)}</a></li>
99
99
  ))}
100
100
  </ul>
101
101
  </div>
102
102
  )}
103
103
  {relatedProposals.length > 0 && (
104
104
  <div>
105
- <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--site-blue-strong)]">Related proposals</p>
106
- <ul class="mt-3 space-y-2 text-[color:var(--site-text-muted)]">
105
+ <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)]">Related proposals</p>
106
+ <ul class="mt-3 space-y-2 text-[color:var(--ts-color-text-muted)]">
107
107
  {relatedProposals.map((proposal) => (
108
- <li><a href={`/proposals/${proposal.id}/`} class="hover:text-[color:var(--site-text)]">{entryTitle(proposal)}</a></li>
108
+ <li><a href={`/proposals/${proposal.id}/`} class="hover:text-[color:var(--ts-color-text)]">{entryTitle(proposal)}</a></li>
109
109
  ))}
110
110
  </ul>
111
111
  </div>
112
112
  )}
113
113
  {relatedDecisions.length > 0 && (
114
114
  <div>
115
- <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--site-blue-strong)]">Related decisions</p>
116
- <ul class="mt-3 space-y-2 text-[color:var(--site-text-muted)]">
115
+ <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)]">Related decisions</p>
116
+ <ul class="mt-3 space-y-2 text-[color:var(--ts-color-text-muted)]">
117
117
  {relatedDecisions.map((decision) => (
118
- <li><a href={`/decisions/${decision.id}/`} class="hover:text-[color:var(--site-text)]">{entryTitle(decision)}</a></li>
118
+ <li><a href={`/decisions/${decision.id}/`} class="hover:text-[color:var(--ts-color-text)]">{entryTitle(decision)}</a></li>
119
119
  ))}
120
120
  </ul>
121
121
  </div>
122
122
  )}
123
123
  {relatedBooks.length > 0 && (
124
124
  <div>
125
- <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--site-blue-strong)]">Related books</p>
126
- <ul class="mt-3 space-y-2 text-[color:var(--site-text-muted)]">
125
+ <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)]">Related books</p>
126
+ <ul class="mt-3 space-y-2 text-[color:var(--ts-color-text-muted)]">
127
127
  {relatedBooks.map((book) => (
128
- <li><a href={`/books/${book.id}/`} class="hover:text-[color:var(--site-text)]">{entryTitle(book)}</a></li>
128
+ <li><a href={`/books/${book.id}/`} class="hover:text-[color:var(--ts-color-text)]">{entryTitle(book)}</a></li>
129
129
  ))}
130
130
  </ul>
131
131
  </div>
@@ -6,25 +6,25 @@ const { entry, currentPath } = Astro.props;
6
6
 
7
7
  <MainLayout title={entry.title} description={entry.description} currentPath={currentPath}>
8
8
  <article class="max-w-5xl space-y-8">
9
- <div class="space-y-4 border-b border-[color:var(--site-border)] pb-8">
10
- <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--site-blue-strong)]">{entry.sectionLabel}</p>
11
- <h1 class="max-w-4xl font-serif text-5xl font-bold tracking-tight text-[color:var(--site-text)] md:text-6xl">{entry.title}</h1>
12
- <p class="max-w-4xl text-xl leading-10 text-[color:var(--site-text-muted)]">{entry.summary}</p>
9
+ <div class="space-y-4 border-b border-[color:var(--ts-color-border)] pb-8">
10
+ <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)]">{entry.sectionLabel}</p>
11
+ <h1 class="max-w-4xl font-serif text-5xl font-bold tracking-tight text-[color:var(--ts-color-text)] md:text-6xl">{entry.title}</h1>
12
+ <p class="max-w-4xl text-xl leading-10 text-[color:var(--ts-color-text-muted)]">{entry.summary}</p>
13
13
  {entry.tags.length > 0 && (
14
- <p class="text-sm uppercase tracking-[0.14em] text-[color:var(--site-accent-strong)]">{entry.tags.join(' / ')}</p>
14
+ <p class="text-sm uppercase tracking-[0.14em] text-[color:var(--ts-color-accent-strong)]">{entry.tags.join(' / ')}</p>
15
15
  )}
16
16
  <div class="flex flex-wrap gap-3 pt-2">
17
- <a href={entry.landingPath} class="border border-[color:var(--site-border-strong)] px-4 py-2 text-sm font-semibold text-[color:var(--site-text)] transition hover:border-[color:var(--site-blue)] hover:bg-[color:var(--site-blue-soft)]">Open knowledge</a>
18
- <a href={entry.downloadHref} class="border border-[color:var(--site-accent)] bg-[color:var(--site-accent)] px-4 py-2 text-sm font-semibold text-[color:var(--site-text)] transition hover:border-[color:var(--site-blue)] hover:bg-[color:var(--site-blue-soft)]">Download book</a>
17
+ <a href={entry.landingPath} class="border border-[color:var(--ts-color-border-strong)] px-4 py-2 text-sm font-semibold text-[color:var(--ts-color-text)] transition hover:border-[color:var(--ts-color-info)] hover:bg-[color:var(--ts-color-info-soft)]">Open knowledge</a>
18
+ <a href={entry.downloadHref} class="border border-[color:var(--ts-color-accent)] bg-[color:var(--ts-color-accent)] px-4 py-2 text-sm font-semibold text-[color:var(--ts-color-text)] transition hover:border-[color:var(--ts-color-info)] hover:bg-[color:var(--ts-color-info-soft)]">Download book</a>
19
19
  </div>
20
20
  </div>
21
21
  <div class="grid gap-6 md:grid-cols-[1.25fr_0.75fr]">
22
22
  <div class="prose-karyon">
23
23
  <slot />
24
24
  </div>
25
- <div class="border border-[color:var(--site-border)] bg-[color:var(--site-surface)] p-5">
26
- <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--site-blue-strong)]">Book paths</p>
27
- <ul class="mt-3 space-y-2 text-[color:var(--site-text-muted)]">
25
+ <div class="border border-[color:var(--ts-color-border)] bg-[color:var(--ts-color-surface)] p-5">
26
+ <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)]">Book paths</p>
27
+ <ul class="mt-3 space-y-2 text-[color:var(--ts-color-text-muted)]">
28
28
  <li>Landing path: <code>{entry.landingPath}</code></li>
29
29
  <li>Base path: <code>{entry.basePath}</code></li>
30
30
  <li>Download file: <code>{entry.downloadFileName}</code></li>
@@ -9,13 +9,13 @@ const { entry, currentPath } = Astro.props;
9
9
  <MainLayout title={entry.title} description={entry.description} currentPath={currentPath}>
10
10
  <div class="space-y-8">
11
11
  <StageBanner />
12
- <section class="max-w-5xl space-y-5 border-b border-[color:var(--site-border)] pb-8">
12
+ <section class="max-w-5xl space-y-5 border-b border-[color:var(--ts-color-border)] pb-8">
13
13
  <div class="flex flex-wrap items-center gap-3">
14
14
  <StatusBadge status={entry.status} />
15
- <p class="text-sm font-medium text-[color:var(--site-text-soft)]">Updated {entry.updated.toISOString().slice(0, 10)}</p>
15
+ <p class="text-sm font-medium text-[color:var(--ts-color-text-subtle)]">Updated {entry.updated.toISOString().slice(0, 10)}</p>
16
16
  </div>
17
- <h1 class="max-w-4xl font-serif text-5xl font-bold tracking-tight text-[color:var(--site-text)] md:text-7xl">{entry.title}</h1>
18
- <p class="max-w-4xl text-xl leading-10 text-[color:var(--site-text-muted)]">{entry.summary}</p>
17
+ <h1 class="max-w-4xl font-serif text-5xl font-bold tracking-tight text-[color:var(--ts-color-text)] md:text-7xl">{entry.title}</h1>
18
+ <p class="max-w-4xl text-xl leading-10 text-[color:var(--ts-color-text-muted)]">{entry.summary}</p>
19
19
  </section>
20
20
  <article class="prose-karyon max-w-4xl">
21
21
  <slot />
@@ -1,203 +1,76 @@
1
1
  ---
2
2
  import '../styles/global.css';
3
- import { SITE_NAV_GROUPS, isCurrentSitePath } from '../utils/routes';
4
- import { PROJECT_STAGE } from '../utils/content-status';
3
+ import PublicShell from '../components/ui/shell/PublicShell.astro';
4
+ import { SITE_NAV_GROUPS } from '../utils/routes';
5
5
  import { SITE } from '../utils/seo';
6
- import { SITE_FOOTER_MENU, SITE_THEME_CSS } from '../utils/site-config';
7
- import FooterSubscribeForm from '../components/forms/FooterSubscribeForm.astro';
8
- import DevWatchReload from '../components/DevWatchReload.astro';
6
+ import { SITE_THEME_CSS } from '../utils/site-config';
7
+ import { normalizeThemePreference } from '../utils/theme.js';
9
8
 
10
9
  const { title, description, currentPath = '/' } = Astro.props;
10
+ const appearance = normalizeThemePreference({
11
+ scheme: SITE.theme?.defaultScheme,
12
+ mode: SITE.theme?.defaultMode,
13
+ });
14
+ const navItems = SITE_NAV_GROUPS.flatMap((group) => group.items);
11
15
  ---
12
16
 
13
- <!doctype html>
14
- <html lang="en">
15
- <head>
16
- <meta charset="UTF-8" />
17
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
18
- <title>{title ? `${title} | ${SITE.name}` : SITE.name}</title>
19
- <meta name="description" content={description ?? SITE.description} />
17
+ <PublicShell
18
+ title={title ? `${title} | ${SITE.name}` : SITE.name}
19
+ description={description ?? SITE.description}
20
+ currentPath={currentPath}
21
+ appearance={appearance}
22
+ brand={{
23
+ name: SITE.name,
24
+ tag: SITE.statement,
25
+ href: '/',
26
+ logoSrc: SITE.logo.src,
27
+ logoAlt: SITE.logo.alt,
28
+ }}
29
+ navItems={navItems}
30
+ navGroups={SITE_NAV_GROUPS}
31
+ contentWidth="content"
32
+ >
33
+ <Fragment slot="head">
20
34
  <link rel="icon" href="/favicon.svg" type="image/svg+xml" />
21
35
  <link rel="shortcut icon" href="/favicon.svg" type="image/svg+xml" />
22
36
  {SITE_THEME_CSS && <style is:global>{SITE_THEME_CSS}</style>}
23
- </head>
24
- <body>
25
- <div class="flex min-h-screen flex-col">
26
- <header class="sticky top-0 z-30 mb-12 bg-[color:rgba(244,237,224,0.92)]/95 backdrop-blur md:mb-16">
27
- <div class="border-y border-[color:var(--site-border-strong)] px-4 py-4 md:px-6">
28
- <div class="mx-auto flex max-w-[var(--site-max)] flex-col gap-4 sm:px-2 lg:flex-row lg:items-center lg:justify-between lg:px-4">
29
- <a href="/" class="flex items-center gap-3">
30
- <div class="flex h-11 w-11 items-center justify-center tracking-[0.2em]">
31
- <img src={SITE.logo.src} alt={SITE.logo.alt} class="h-11 w-11" />
32
- </div>
33
- <div>
34
- <p class="text-xl font-bold text-[color:var(--site-text)]">{SITE.name}</p>
35
- <p class="text-sm text-[color:var(--site-text-soft)]">{SITE.statement}</p>
36
- </div>
37
- </a>
38
- <div class="ml-auto flex w-full flex-wrap items-center justify-end gap-3 lg:w-auto lg:flex-nowrap">
39
- <nav class="flex w-full justify-end text-base text-[color:var(--site-text-muted)] lg:w-auto">
40
- <ul class="js-site-nav-disclosures flex flex-wrap justify-end gap-3">
41
- {
42
- SITE_NAV_GROUPS.map((group) => {
43
- return (
44
- <li class="relative">
45
- <details class="group js-site-nav-disclosure">
46
- <summary
47
- class:list={[
48
- 'flex cursor-pointer list-none items-center gap-2 border-b-2 border-transparent px-1 py-2 font-medium transition marker:content-none hover:border-[color:var(--site-blue)] hover:text-[color:var(--site-text)]',
49
- group.items.some((item) => isCurrentSitePath(currentPath, item.href)) &&
50
- 'border-[color:var(--site-accent)] text-[color:var(--site-text)]',
51
- ]}
52
- >
53
- {group.label}
54
- <span class="text-xs transition group-open:rotate-180">▾</span>
55
- </summary>
56
- <div class="absolute right-0 top-full z-40 mt-2 min-w-56 border border-[color:var(--site-border-strong)] bg-[color:rgba(244,237,224,0.98)] p-2 shadow-[0_12px_30px_rgba(52,37,19,0.12)] backdrop-blur">
57
- <ul class="flex flex-col gap-1">
58
- {group.items.map((item) => (
59
- <li>
60
- <a
61
- href={item.href}
62
- class:list={[
63
- 'block px-3 py-2 text-sm transition hover:bg-[color:var(--site-blue-soft)] hover:text-[color:var(--site-text)]',
64
- isCurrentSitePath(currentPath, item.href) &&
65
- 'bg-[color:var(--site-surface-strong)] text-[color:var(--site-text)]',
66
- ]}
67
- >
68
- {item.label}
69
- </a>
70
- </li>
71
- ))}
72
- </ul>
73
- </div>
74
- </details>
75
- </li>
76
- );
77
- })
78
- }
79
- </ul>
80
- </nav>
81
- <a
82
- href={SITE.githubRepository}
83
- target="_blank"
84
- rel="noreferrer"
85
- aria-label={`${SITE.name} GitHub repository`}
86
- class="inline-flex h-12 w-12 shrink-0 items-center justify-center border-2 border-transparent bg-transparent text-[color:var(--site-text)] transition hover:-translate-y-0.5 hover:text-[color:var(--site-blue)] focus-visible:border-[color:var(--site-blue)] focus-visible:outline-none"
87
- >
88
- <svg
89
- xmlns="http://www.w3.org/2000/svg"
90
- viewBox="0 0 16 16"
91
- aria-hidden="true"
92
- class="h-6 w-6 fill-current"
93
- >
94
- <path d="M8 0C3.58 0 0 3.67 0 8.2c0 3.63 2.29 6.7 5.47 7.78.4.08.55-.18.55-.39 0-.2-.01-.85-.01-1.54-2.01.38-2.53-.5-2.69-.95-.09-.24-.48-.95-.82-1.15-.28-.15-.68-.54-.01-.55.63-.01 1.08.59 1.23.84.72 1.24 1.87.89 2.33.68.07-.54.28-.89.5-1.09-1.78-.21-3.64-.92-3.64-4.08 0-.9.31-1.64.82-2.22-.08-.21-.36-1.06.08-2.2 0 0 .67-.22 2.2.85A7.34 7.34 0 0 1 8 4.83c.68 0 1.37.09 2.01.27 1.53-1.07 2.2-.85 2.2-.85.44 1.14.16 1.99.08 2.2.51.58.82 1.31.82 2.22 0 3.17-1.87 3.87-3.65 4.08.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.19 0 .21.15.48.55.39A8.22 8.22 0 0 0 16 8.2C16 3.67 12.42 0 8 0Z" />
95
- </svg>
96
- </a>
97
- <a
98
- href={SITE.discordLink}
99
- target="_blank"
100
- rel="noreferrer"
101
- aria-label={`${SITE.name} Discord`}
102
- class="inline-flex h-12 w-12 shrink-0 items-center justify-center border-2 border-transparent bg-transparent text-[color:var(--site-text)] transition hover:-translate-y-0.5 hover:text-[color:var(--site-blue)] focus-visible:border-[color:var(--site-blue)] focus-visible:outline-none"
103
- >
104
- <svg
105
- xmlns="http://www.w3.org/2000/svg"
106
- viewBox="0 0 24 24"
107
- aria-hidden="true"
108
- class="h-6 w-6 fill-current"
109
- >
110
- <path d="M20.317 4.369A19.791 19.791 0 0 0 15.885 3c-.191.328-.403.762-.554 1.104a18.27 18.27 0 0 0-5.314 0A11.64 11.64 0 0 0 9.463 3a19.736 19.736 0 0 0-4.433 1.369C2.227 8.617 1.468 12.759 1.848 16.845a19.9 19.9 0 0 0 5.437 2.755c.438-.6.825-1.236 1.157-1.902-.637-.241-1.246-.545-1.818-.9.152-.111.3-.229.444-.347 3.507 1.648 7.316 1.648 10.782 0 .145.118.293.236.444.347-.573.355-1.183.659-1.82.9.332.666.719 1.302 1.158 1.902a19.87 19.87 0 0 0 5.438-2.755c.446-4.737-.762-8.842-3.753-12.476ZM9.954 14.379c-1.053 0-1.918-.966-1.918-2.153 0-1.188.845-2.153 1.918-2.153 1.082 0 1.938.975 1.918 2.153 0 1.187-.846 2.153-1.918 2.153Zm4.092 0c-1.053 0-1.918-.966-1.918-2.153 0-1.188.845-2.153 1.918-2.153 1.082 0 1.938.975 1.918 2.153 0 1.187-.836 2.153-1.918 2.153Z" />
111
- </svg>
112
- </a>
113
- <a
114
- href="/contact/"
115
- aria-label={`Contact ${SITE.name}`}
116
- class="inline-flex h-12 w-12 shrink-0 items-center justify-center border-2 border-transparent bg-transparent text-[color:var(--site-text)] transition hover:-translate-y-0.5 hover:text-[color:var(--site-blue)] focus-visible:border-[color:var(--site-blue)] focus-visible:outline-none"
117
- >
118
- <svg
119
- xmlns="http://www.w3.org/2000/svg"
120
- viewBox="0 0 24 24"
121
- aria-hidden="true"
122
- class="h-6 w-6 fill-none stroke-current stroke-[1.8]"
123
- >
124
- <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5v10.5H3.75z" />
125
- <path stroke-linecap="round" stroke-linejoin="round" d="m4.5 7.5 7.5 6 7.5-6" />
126
- </svg>
127
- </a>
128
- </div>
129
- </div>
130
- </div>
131
- </header>
132
-
133
- <main class="mx-auto flex-1 max-w-[var(--site-max)] px-4 pb-12 sm:px-6 lg:px-8">
134
- <slot />
135
- </main>
136
-
137
- <footer class="mt-20 border-t-2 border-[color:var(--site-border-strong)] pt-8">
138
- <div class="mb-15 mx-auto max-w-[var(--site-max)] px-4 sm:px-6 lg:px-8">
139
- <div class="flex flex-wrap justify-center gap-8 pb-8">
140
- <div class="w-64">
141
- <p class="text-xl font-bold text-[color:var(--site-text)]">{SITE.name}</p>
142
- <p class="mt-3 text-base leading-8 text-[color:var(--site-text-muted)]">
143
- {SITE.summary}
144
- </p>
145
- </div>
146
- <div class="w-64">
147
- <p class="text-sm font-semibold uppercase tracking-[0.16em] text-[color:var(--site-accent-strong)]">Project stage</p>
148
- <p class="mt-3 text-base font-semibold text-[color:var(--site-text)]">{PROJECT_STAGE.label}</p>
149
- <p class="mt-2 text-base leading-8 text-[color:var(--site-text-muted)]">{PROJECT_STAGE.description}</p>
150
- </div>
151
- {SITE_FOOTER_MENU.map((group) => (
152
- <div class="w-64">
153
- <p class="text-sm font-semibold uppercase tracking-[0.16em] text-[color:var(--site-blue-strong)]">{group.label}</p>
154
- <div class="mt-3 flex flex-col gap-2 text-base text-[color:var(--site-text-muted)]">
155
- {group.items.map((item) => (
156
- <a href={item.href} class="hover:text-[color:var(--site-text)]">{item.label}</a>
157
- ))}
158
- </div>
159
- </div>
160
- ))}
161
- </div>
162
- <FooterSubscribeForm currentPath={currentPath} />
163
- </div>
164
- </footer>
165
- </div>
166
- <script>
167
- const navRoot = document.querySelector('.js-site-nav-disclosures');
168
- const navDetails = document.querySelectorAll<HTMLDetailsElement>('details.js-site-nav-disclosure');
169
-
170
- navDetails.forEach((details) => {
171
- details.addEventListener('toggle', () => {
172
- if (!details.open) return;
173
-
174
- navDetails.forEach((otherDetails) => {
175
- if (otherDetails !== details) {
176
- otherDetails.open = false;
177
- }
178
- });
179
- });
180
- });
181
-
182
- document.addEventListener('click', (event) => {
183
- if (!(navRoot instanceof HTMLElement)) return;
184
- if (event.target instanceof Node && navRoot.contains(event.target)) return;
185
-
186
- navDetails.forEach((details) => {
187
- details.open = false;
188
- });
189
- });
190
-
191
- navRoot?.addEventListener('click', (event) => {
192
- const target = event.target;
193
- if (!(target instanceof HTMLElement)) return;
194
- if (!target.closest('a')) return;
195
-
196
- navDetails.forEach((details) => {
197
- details.open = false;
198
- });
199
- });
200
- </script>
201
- <DevWatchReload />
202
- </body>
203
- </html>
37
+ </Fragment>
38
+ <Fragment slot="actions">
39
+ <a
40
+ href={SITE.githubRepository}
41
+ target="_blank"
42
+ rel="noreferrer"
43
+ aria-label={`${SITE.name} GitHub repository`}
44
+ title={`${SITE.name} GitHub repository`}
45
+ class="ts-public-shell__icon-link"
46
+ >
47
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" aria-hidden="true" focusable="false">
48
+ <path d="M8 0C3.58 0 0 3.67 0 8.2c0 3.63 2.29 6.7 5.47 7.78.4.08.55-.18.55-.39 0-.2-.01-.85-.01-1.54-2.01.38-2.53-.5-2.69-.95-.09-.24-.48-.95-.82-1.15-.28-.15-.68-.54-.01-.55.63-.01 1.08.59 1.23.84.72 1.24 1.87.89 2.33.68.07-.54.28-.89.5-1.09-1.78-.21-3.64-.92-3.64-4.08 0-.9.31-1.64.82-2.22-.08-.21-.36-1.06.08-2.2 0 0 .67-.22 2.2.85A7.34 7.34 0 0 1 8 4.83c.68 0 1.37.09 2.01.27 1.53-1.07 2.2-.85 2.2-.85.44 1.14.16 1.99.08 2.2.51.58.82 1.31.82 2.22 0 3.17-1.87 3.87-3.65 4.08.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.19 0 .21.15.48.55.39A8.22 8.22 0 0 0 16 8.2C16 3.67 12.42 0 8 0Z" />
49
+ </svg>
50
+ </a>
51
+ <a
52
+ href={SITE.discordLink}
53
+ target="_blank"
54
+ rel="noreferrer"
55
+ aria-label={`${SITE.name} Discord`}
56
+ title={`${SITE.name} Discord`}
57
+ class="ts-public-shell__icon-link"
58
+ >
59
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
60
+ <path d="M20.317 4.369A19.791 19.791 0 0 0 15.885 3c-.191.328-.403.762-.554 1.104a18.27 18.27 0 0 0-5.314 0A11.64 11.64 0 0 0 9.463 3a19.736 19.736 0 0 0-4.433 1.369C2.227 8.617 1.468 12.759 1.848 16.845a19.9 19.9 0 0 0 5.437 2.755c.438-.6.825-1.236 1.157-1.902-.637-.241-1.246-.545-1.818-.9.152-.111.3-.229.444-.347 3.507 1.648 7.316 1.648 10.782 0 .145.118.293.236.444.347-.573.355-1.183.659-1.82.9.332.666.719 1.302 1.158 1.902a19.87 19.87 0 0 0 5.438-2.755c.446-4.737-.762-8.842-3.753-12.476ZM9.954 14.379c-1.053 0-1.918-.966-1.918-2.153 0-1.188.845-2.153 1.918-2.153 1.082 0 1.938.975 1.918 2.153 0 1.187-.846 2.153-1.918 2.153Zm4.092 0c-1.053 0-1.918-.966-1.918-2.153 0-1.188.845-2.153 1.918-2.153 1.082 0 1.938.975 1.918 2.153 0 1.187-.836 2.153-1.918 2.153Z" />
61
+ </svg>
62
+ </a>
63
+ <a
64
+ href="/contact/"
65
+ aria-label={`Contact ${SITE.name}`}
66
+ title={`Contact ${SITE.name}`}
67
+ class="ts-public-shell__icon-link ts-public-shell__icon-link--stroke"
68
+ >
69
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
70
+ <path d="M3.75 6.75h16.5v10.5H3.75z" />
71
+ <path d="m4.5 7.5 7.5 6 7.5-6" />
72
+ </svg>
73
+ </a>
74
+ </Fragment>
75
+ <slot />
76
+ </PublicShell>
@@ -7,16 +7,16 @@ const { note } = Astro.props;
7
7
 
8
8
  <MainLayout title={note.title} description={note.description} currentPath="/notes/">
9
9
  <article class="max-w-4xl space-y-8">
10
- <div class="space-y-4 border-b border-[color:var(--site-border)] pb-8">
10
+ <div class="space-y-4 border-b border-[color:var(--ts-color-border)] pb-8">
11
11
  <div class="flex flex-wrap items-center gap-3">
12
12
  <StatusBadge status={note.status} />
13
- <p class="text-sm font-medium text-[color:var(--site-text-soft)]">{note.date.toISOString().slice(0, 10)}</p>
14
- <p class="text-sm text-[color:var(--site-text-soft)]">{note.author}</p>
13
+ <p class="text-sm font-medium text-[color:var(--ts-color-text-subtle)]">{note.date.toISOString().slice(0, 10)}</p>
14
+ <p class="text-sm text-[color:var(--ts-color-text-subtle)]">{note.author}</p>
15
15
  </div>
16
- <h1 class="max-w-4xl font-serif text-5xl font-bold tracking-tight text-[color:var(--site-text)] md:text-6xl">{note.title}</h1>
17
- <p class="max-w-3xl text-xl leading-10 text-[color:var(--site-text-muted)]">{note.summary}</p>
16
+ <h1 class="max-w-4xl font-serif text-5xl font-bold tracking-tight text-[color:var(--ts-color-text)] md:text-6xl">{note.title}</h1>
17
+ <p class="max-w-3xl text-xl leading-10 text-[color:var(--ts-color-text-muted)]">{note.summary}</p>
18
18
  {note.tags.length > 0 && (
19
- <p class="text-sm uppercase tracking-[0.14em] text-[color:var(--site-accent-strong)]">{note.tags.join(' / ')}</p>
19
+ <p class="text-sm uppercase tracking-[0.14em] text-[color:var(--ts-color-accent-strong)]">{note.tags.join(' / ')}</p>
20
20
  )}
21
21
  </div>
22
22
  <div class="prose-karyon">
@@ -33,15 +33,15 @@ const {
33
33
 
34
34
  <MainLayout title={entry.name} description={entry.description} currentPath={currentPath}>
35
35
  <article class="max-w-4xl space-y-8">
36
- <div class="space-y-4 border-b border-[color:var(--site-border)] pb-8">
36
+ <div class="space-y-4 border-b border-[color:var(--ts-color-border)] pb-8">
37
37
  <div class="flex flex-wrap items-center gap-3">
38
38
  {'status' in entry && entry.status && <StatusBadge status={entry.status} />}
39
- <p class="text-sm font-medium text-[color:var(--site-text-soft)]">{metaLabel}: {metaValue}</p>
39
+ <p class="text-sm font-medium text-[color:var(--ts-color-text-subtle)]">{metaLabel}: {metaValue}</p>
40
40
  </div>
41
- <h1 class="max-w-4xl font-serif text-5xl font-bold tracking-tight text-[color:var(--site-text)] md:text-6xl">{entry.name}</h1>
42
- <p class="max-w-3xl text-xl leading-10 text-[color:var(--site-text-muted)]">{entry.summary}</p>
41
+ <h1 class="max-w-4xl font-serif text-5xl font-bold tracking-tight text-[color:var(--ts-color-text)] md:text-6xl">{entry.name}</h1>
42
+ <p class="max-w-3xl text-xl leading-10 text-[color:var(--ts-color-text-muted)]">{entry.summary}</p>
43
43
  {entry.tags.length > 0 && (
44
- <p class="text-sm uppercase tracking-[0.14em] text-[color:var(--site-accent-strong)]">{entry.tags.join(' / ')}</p>
44
+ <p class="text-sm uppercase tracking-[0.14em] text-[color:var(--ts-color-accent-strong)]">{entry.tags.join(' / ')}</p>
45
45
  )}
46
46
  </div>
47
47
  <div class="grid gap-6 md:grid-cols-[1.3fr_0.7fr]">
@@ -50,31 +50,31 @@ const {
50
50
  </div>
51
51
  <div class="space-y-6">
52
52
  {entry.links.length > 0 && (
53
- <div class="border border-[color:var(--site-border)] bg-[color:var(--site-surface)] p-5">
54
- <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--site-blue-strong)]">Links</p>
55
- <ul class="mt-3 space-y-2 text-[color:var(--site-text-muted)]">
53
+ <div class="border border-[color:var(--ts-color-border)] bg-[color:var(--ts-color-surface)] p-5">
54
+ <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)]">Links</p>
55
+ <ul class="mt-3 space-y-2 text-[color:var(--ts-color-text-muted)]">
56
56
  {entry.links.map((link) => (
57
- <li><a href={link.href} class="hover:text-[color:var(--site-text)]">{link.label}</a></li>
57
+ <li><a href={link.href} class="hover:text-[color:var(--ts-color-text)]">{link.label}</a></li>
58
58
  ))}
59
59
  </ul>
60
60
  </div>
61
61
  )}
62
62
  {relatedQuestions.length > 0 && (
63
- <div class="border border-[color:var(--site-border)] bg-[color:var(--site-surface)] p-5">
64
- <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--site-blue-strong)]">Questions</p>
65
- <ul class="mt-3 space-y-2 text-[color:var(--site-text-muted)]">
63
+ <div class="border border-[color:var(--ts-color-border)] bg-[color:var(--ts-color-surface)] p-5">
64
+ <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)]">Questions</p>
65
+ <ul class="mt-3 space-y-2 text-[color:var(--ts-color-text-muted)]">
66
66
  {relatedQuestions.map((question) => (
67
- <li><a href={`/questions/${question.id}/`} class="hover:text-[color:var(--site-text)]">{entryTitle(question)}</a></li>
67
+ <li><a href={`/questions/${question.id}/`} class="hover:text-[color:var(--ts-color-text)]">{entryTitle(question)}</a></li>
68
68
  ))}
69
69
  </ul>
70
70
  </div>
71
71
  )}
72
72
  {relatedObjectives.length > 0 && (
73
- <div class="border border-[color:var(--site-border)] bg-[color:var(--site-surface)] p-5">
74
- <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--site-blue-strong)]">Objectives</p>
75
- <ul class="mt-3 space-y-2 text-[color:var(--site-text-muted)]">
73
+ <div class="border border-[color:var(--ts-color-border)] bg-[color:var(--ts-color-surface)] p-5">
74
+ <p class="text-sm font-semibold uppercase tracking-[0.14em] text-[color:var(--ts-color-info-text)]">Objectives</p>
75
+ <ul class="mt-3 space-y-2 text-[color:var(--ts-color-text-muted)]">
76
76
  {relatedObjectives.map((objective) => (
77
- <li><a href={`/objectives/${objective.id}/`} class="hover:text-[color:var(--site-text)]">{entryTitle(objective)}</a></li>
77
+ <li><a href={`/objectives/${objective.id}/`} class="hover:text-[color:var(--ts-color-text)]">{entryTitle(objective)}</a></li>
78
78
  ))}
79
79
  </ul>
80
80
  </div>
@@ -9,6 +9,9 @@ const copyEntry = (entry) => entry.type === "link" ? copyLink(entry) : {
9
9
  };
10
10
  const findTopLevelGroup = (sidebar, label) => sidebar.find((entry) => entry.type === "group" && entry.label === label);
11
11
  const flattenLinks = (entries) => entries.flatMap((entry) => entry.type === "link" ? [copyLink(entry)] : flattenLinks(entry.entries));
12
+ const findTopLevelGroupForPath = (sidebar, currentPath) => sidebar.find(
13
+ (entry) => entry.type === "group" && flattenLinks(entry.entries).some((link) => normalizeHref(link.href) === normalizeHref(currentPath))
14
+ );
12
15
  const buildPagination = (entries, currentHref) => {
13
16
  const flatLinks = flattenLinks(entries);
14
17
  const currentIndex = flatLinks.findIndex((link) => normalizeHref(link.href) === normalizeHref(currentHref));
@@ -25,19 +28,6 @@ const setRouteSidebar = (route, currentPath, sidebar, paginationSource) => {
25
28
  route.hasSidebar = sidebar.length > 0;
26
29
  route.pagination = paginationSource ? buildPagination(paginationSource, currentPath) : { prev: void 0, next: void 0 };
27
30
  };
28
- const defaultRuntime = {
29
- BOOKS: [],
30
- BOOKS_LINK: {
31
- label: "Books",
32
- link: TREESEED_LINKS.home
33
- },
34
- TREESEED_LIBRARY_DOWNLOAD: {
35
- downloadFileName: "treeseed-knowledge.md",
36
- downloadHref: "/books/treeseed-knowledge.md",
37
- downloadTitle: "TreeSeed Knowledge Library"
38
- },
39
- TREESEED_LINKS
40
- };
41
31
  function runtimeTenantModelRendered(modelName) {
42
32
  const featureValue = RUNTIME_TENANT.features?.[modelName];
43
33
  const siteValue = RUNTIME_TENANT.site?.[modelName];
@@ -50,7 +40,23 @@ const onRequest = defineRouteMiddleware(async (context) => {
50
40
  setRouteSidebar(route, currentPath, [], null);
51
41
  return;
52
42
  }
53
- const runtime = await loadHostedBookRuntime(context.locals) ?? defaultRuntime;
43
+ let runtime = null;
44
+ try {
45
+ runtime = await loadHostedBookRuntime(context.locals);
46
+ } catch {
47
+ runtime = null;
48
+ }
49
+ if (!runtime) {
50
+ const bookGroup = findTopLevelGroupForPath(route.sidebar, currentPath);
51
+ if (bookGroup) {
52
+ setRouteSidebar(route, currentPath, [copyEntry(bookGroup)], bookGroup.entries);
53
+ return;
54
+ }
55
+ if (currentPath === normalizeHref(TREESEED_LINKS.home)) {
56
+ setRouteSidebar(route, currentPath, [], null);
57
+ }
58
+ return;
59
+ }
54
60
  route.sidebar = buildStarlightSidebarEntriesFromRuntime(runtime, currentPath);
55
61
  const activeBook = runtime.BOOKS.find(
56
62
  (book) => currentPath.startsWith(normalizeHref(book.basePath))
@@ -8,20 +8,20 @@ import MainLayout from '../layouts/MainLayout.astro';
8
8
  currentPath="/404/"
9
9
  >
10
10
  <section class="mx-auto max-w-3xl space-y-6 py-20">
11
- <p class="text-sm font-semibold uppercase tracking-[0.16em] text-[color:var(--site-accent-strong)]">404</p>
12
- <h1 class="font-serif text-5xl text-[color:var(--site-text)]">Page not found</h1>
13
- <p class="text-lg leading-9 text-[color:var(--site-text-muted)]">
14
- The page you requested is not available in this Treeseed. Try the homepage, the knowledge
11
+ <p class="text-sm font-semibold uppercase tracking-[0.16em] text-[color:var(--ts-color-accent-strong)]">404</p>
12
+ <h1 class="font-serif text-5xl text-[color:var(--ts-color-text)]">Page not found</h1>
13
+ <p class="text-lg leading-9 text-[color:var(--ts-color-text-muted)]">
14
+ The page you requested is not available in this Treeseed. Try the homepage, the books
15
15
  library, or the current project status page instead.
16
16
  </p>
17
17
  <div class="flex flex-wrap gap-4">
18
- <a href="/" class="border border-[color:var(--site-accent)] bg-[color:var(--site-accent)] px-5 py-3 text-base font-semibold text-[color:var(--site-text)] transition hover:border-[color:var(--site-blue)] hover:bg-[color:var(--site-blue-soft)]">
18
+ <a href="/" class="border border-[color:var(--ts-color-accent)] bg-[color:var(--ts-color-accent)] px-5 py-3 text-base font-semibold text-[color:var(--ts-color-text)] transition hover:border-[color:var(--ts-color-info)] hover:bg-[color:var(--ts-color-info-soft)]">
19
19
  Go home
20
20
  </a>
21
- <a href="/knowledge/" class="border border-[color:var(--site-border-strong)] px-5 py-3 text-base font-semibold text-[color:var(--site-text)] transition hover:border-[color:var(--site-blue)] hover:bg-[color:var(--site-blue-soft)]">
22
- Open knowledge
21
+ <a href="/books/" class="border border-[color:var(--ts-color-border-strong)] px-5 py-3 text-base font-semibold text-[color:var(--ts-color-text)] transition hover:border-[color:var(--ts-color-info)] hover:bg-[color:var(--ts-color-info-soft)]">
22
+ Open books
23
23
  </a>
24
- <a href="/status/" class="border border-[color:var(--site-border-strong)] px-5 py-3 text-base font-semibold text-[color:var(--site-text)] transition hover:border-[color:var(--site-blue)] hover:bg-[color:var(--site-blue-soft)]">
24
+ <a href="/status/" class="border border-[color:var(--ts-color-border-strong)] px-5 py-3 text-base font-semibold text-[color:var(--ts-color-text)] transition hover:border-[color:var(--ts-color-info)] hover:bg-[color:var(--ts-color-info-soft)]">
25
25
  View status
26
26
  </a>
27
27
  </div>