diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 9659707..67d827b 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -8,10 +8,10 @@ {"id":"docs-1ho.2","title":"Migrate color palette to OKLCH indigo-tinted neutrals","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nReplace the hex color values in the `:root` CSS custom properties with OKLCH equivalents that match the scaffold's indigo-tinted neutral palette (hue ~260). This aligns the docs site with the scaffold's visual identity.\n\n### Replace these custom properties in `:root`:\n\n**Neutrals:**\n```css\n/* OLD → NEW */\n--color-bg: #ffffff; → --color-bg: oklch(0.995 0.001 260);\n--color-bg-subtle: #f6f8fa; → --color-bg-subtle: oklch(0.96 0.005 260);\n--color-border: #ebeef1; → --color-border: oklch(0.91 0.005 260);\n--color-border-strong: #d8dee4; → --color-border-strong: oklch(0.86 0.008 260);\n--color-text-primary: #414552; → --color-text-primary: oklch(0.40 0.015 260);\n--color-text-heading: #1a1b25; → --color-text-heading: oklch(0.20 0.01 260);\n--color-text-title: #1a1b25; → --color-text-title: oklch(0.16 0.005 260);\n--color-text-secondary: #687385;→ --color-text-secondary: oklch(0.55 0.01 260);\n--color-text-sidebar: #414552; → --color-text-sidebar: oklch(0.40 0.015 260);\n```\n\n**Accent / Links — keep the blue but express in OKLCH:**\n```css\n--color-link: #0570de; → --color-link: oklch(0.50 0.18 260);\n--color-link-hover: #0055bc; → --color-link-hover: oklch(0.43 0.19 260);\n--color-accent: #0570de; → --color-accent: oklch(0.50 0.18 260);\n```\n\n**Interaction colors (misc section):**\n```css\n--hover-bg: #f6f8fa; → --hover-bg: oklch(0.96 0.005 260);\n--active-bg: #f0f4ff; → --active-bg: oklch(0.95 0.015 260);\n--focus-ring: 0 0 0 4px rgba(5, 112, 222, 0.36); → --focus-ring: 0 0 0 4px oklch(0.50 0.18 260 / 0.36);\n```\n\n**Callout colors — keep the hues but express in OKLCH:**\n```css\n--color-info: #0570de; → --color-info: oklch(0.50 0.18 260);\n--color-info-bg: #f0f7ff; → --color-info-bg: oklch(0.96 0.02 260);\n--color-warning: #c84801; → --color-warning: oklch(0.55 0.17 55);\n--color-warning-bg: #fef9f0; → --color-warning-bg: oklch(0.97 0.02 80);\n--color-danger: #df1b41; → --color-danger: oklch(0.52 0.22 25);\n--color-danger-bg: #fef0f4; → --color-danger-bg: oklch(0.97 0.02 15);\n--color-success: #228403; → --color-success: oklch(0.52 0.17 145);\n--color-success-bg: #f0fef0; → --color-success-bg: oklch(0.97 0.03 145);\n```\n\n### Also update these hardcoded colors in other selectors:\n\n1. `.layout-header` background: change `#f6f8fa` → `oklch(0.985 0.002 260)` (scaffold's background tone for the header — near-white, matching the page body but with a faint warmth)\n\n2. `.breadcrumbs-separator` color: change `#a3acba` → `oklch(0.70 0.01 260)`\n\n3. `.layout-content blockquote` border-left color: change `#c0c8d2` → `oklch(0.80 0.01 260)`\n\n### DO NOT change these hardcoded colors (they are code-block-specific and should stay):\n- `#011627` (Night Owl background)\n- `#0d2137` (code block header)\n- `#1e3a5f` (code block border)\n- `#8899aa`, `#556677`, `#aabbcc` (code block UI elements)\n- Any `rgba(255, 255, 255, ...)` scrollbar colors inside code blocks\n\n## Don't\n- Do NOT change font families, font sizes, font weights, or spacing\n- Do NOT change the code block color scheme (Night Owl)\n- Do NOT touch Layout.js or any other files\n- Do NOT add dark mode (separate concern)\n- Do NOT change the LANGUAGE_META colors in CodeBlock.js\n- Do NOT add new CSS rules — only modify existing property values","acceptance_criteria":"1. All `:root` color custom properties use oklch() syntax (no hex values remain for tokens listed above)\n2. The page background is near-white with a faint blue tint (visually similar to before but not pure white)\n3. Links are still blue and visually similar to before\n4. Callout boxes still show 4 distinct colors (blue info, orange warning, red danger, green success)\n5. Code blocks are unchanged (Night Owl dark blue background preserved)\n6. Header background is updated to oklch value\n7. Breadcrumb separator and blockquote border use oklch values\n8. `npm run build` in documentation/documentation/ succeeds without errors\n9. The site loads correctly in Chrome 111+ and Safari 15.4+ (oklch browser support baseline)","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":25,"created_at":"2026-02-20T18:20:31.538843+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:27:47.682078+08:00","closed_at":"2026-02-20T18:27:47.682078+08:00","close_reason":"6f5dd21 Migrate color palette to OKLCH indigo-tinted neutrals","labels":["scope:small"],"dependencies":[{"issue_id":"docs-1ho.2","depends_on_id":"docs-1ho","type":"parent-child","created_at":"2026-02-20T18:20:31.54053+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-1ho.2","depends_on_id":"docs-1ho.1","type":"blocks","created_at":"2026-02-20T18:20:31.542591+08:00","created_by":"einstein.climateai.org"}]} {"id":"docs-1ho.3","title":"Update border radii and spacing to match scaffold","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nUpdate the border radius custom properties and a few spacing values to match the scaffold's more rounded, modern aesthetic.\n\n### 1. Update radius tokens in `:root`\n```css\n/* OLD → NEW */\n--radius-sm: 4px; → --radius-sm: 6px;\n--radius-md: 6px; → --radius-md: 8px;\n--radius-lg: 8px; → --radius-lg: 10px;\n```\n\nAlso add a new token after `--radius-lg`:\n```css\n--radius-xl: 14px;\n```\n\n### 2. Update the code block border-radius\nIn `.codeblock`, change:\n```css\nborder-radius: 8px; → border-radius: var(--radius-lg);\n```\n\nIn the fallback rule `.layout-content pre:not(.codeblock-pre)`, change:\n```css\nborder-radius: 8px; → border-radius: var(--radius-lg);\n```\n\n### 3. Update card-link border-radius\nIn `.card-link`, change:\n```css\nborder-radius: var(--radius-lg);\n```\nThis already uses the token — it will automatically pick up the new 10px value. No change needed.\n\n### 4. Update pagination-link border-radius\nIn `.pagination-link`, it already uses `var(--radius-lg)` — will pick up automatically.\n\n## Don't\n- Do NOT change any font, color, or typography properties\n- Do NOT change spacing tokens (--space-*) — they are fine as-is\n- Do NOT change the code block internal padding or header styling\n- Do NOT touch any files other than globals.css","acceptance_criteria":"1. --radius-sm is 6px, --radius-md is 8px, --radius-lg is 10px, --radius-xl is 14px in :root\n2. Code blocks have 10px border radius (inspect .codeblock element)\n3. Card links have 10px border radius\n4. Sidebar links have 8px border radius (--radius-md)\n5. Inline code still has rounded corners (uses --radius-sm = 6px now)\n6. `npm run build` succeeds without errors","status":"closed","priority":2,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":15,"created_at":"2026-02-20T18:20:48.238131+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:25:12.922188+08:00","closed_at":"2026-02-20T18:25:12.922188+08:00","close_reason":"8d7ef9e Update border radii and spacing to match scaffold","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-1ho.3","depends_on_id":"docs-1ho","type":"parent-child","created_at":"2026-02-20T18:20:48.239264+08:00","created_by":"einstein.climateai.org"}]} {"id":"docs-1ho.4","title":"Refine heading typography to match scaffold display style","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nUpdate heading styles to match the scaffold's Syne display typography conventions. The scaffold uses tighter letter-spacing and bolder heading hierarchy with Syne. These changes assume docs-1ho.1 (font loading) is already applied.\n\n### 1. Update `.layout-content h1`\nAdd `letter-spacing: -0.02em;` (tracking-tight in scaffold convention).\nKeep all other properties (font-size: 32px, font-weight: 700, etc.) unchanged.\n\n### 2. Update `.layout-content h2`\nAdd `letter-spacing: -0.01em;`.\nKeep all other properties unchanged.\n\n### 3. Update `.sidebar-section-header`\nChange from:\n```css\n.sidebar-section-header {\n font-size: 14px;\n font-weight: 600;\n text-transform: uppercase;\n color: var(--color-text-heading);\n padding: var(--space-1) var(--space-5);\n margin-bottom: var(--space-3);\n}\n```\nTo:\n```css\n.sidebar-section-header {\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--color-text-secondary);\n padding: var(--space-1) var(--space-5);\n margin-bottom: var(--space-3);\n}\n```\n\nThis matches the scaffold's convention: sidebar section headers are smaller (11px), bolder (700), wider-tracked (0.08em), and use secondary color to feel like category labels rather than content headings.\n\n### 4. Update `.toc-title`\nChange `font-weight: 700` → `font-weight: 600` and add `font-family: var(--font-display);`\n\nThis gives the TOC \"On this page\" label the Syne display treatment.\n\n## Don't\n- Do NOT change font-size on h1, h2, h3, h4 (only add letter-spacing)\n- Do NOT change any colors (that is the colors task)\n- Do NOT change body text or paragraph styling\n- Do NOT touch any files other than globals.css","acceptance_criteria":"1. H1 has letter-spacing: -0.02em (inspect computed style)\n2. H2 has letter-spacing: -0.01em\n3. Sidebar section headers are 11px, font-weight 700, letter-spacing 0.08em, color is --color-text-secondary\n4. TOC title uses Syne (font-family: var(--font-display))\n5. No heading font sizes have changed from their current values\n6. `npm run build` succeeds without errors","status":"closed","priority":2,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":15,"created_at":"2026-02-20T18:21:05.00645+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:29:09.519173+08:00","closed_at":"2026-02-20T18:29:09.519173+08:00","close_reason":"6f5dd21 Refine heading typography to match scaffold display style","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-1ho.4","depends_on_id":"docs-1ho","type":"parent-child","created_at":"2026-02-20T18:21:05.008011+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-1ho.4","depends_on_id":"docs-1ho.1","type":"blocks","created_at":"2026-02-20T18:21:05.009956+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-1ho.5","title":"Add antialiased rendering and body font-weight","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nThe scaffold uses `antialiased` font rendering and Outfit at weight 400 for body text. The docs site already has `-webkit-font-smoothing: antialiased` and `-moz-osx-font-smoothing: grayscale` on body — but Outfit renders slightly differently than system fonts at the default weight. Add `font-weight: 400;` explicitly to the `body` rule to ensure consistent rendering with Outfit.\n\n### 1. In the `body { ... }` rule, add:\n```css\nfont-weight: 400;\n```\nPlace it after `font-size: 16px;`.\n\n### 2. Update the existing `body` line-height from `1.65` to `1.6`\nThe scaffold uses tighter line-height with Outfit. Change:\n```css\nline-height: 1.65; → line-height: 1.6;\n```\n\n### 3. Update list item line-height to match\nIn `.layout-content article li`, change:\n```css\nline-height: 1.65; → line-height: 1.6;\n```\n\n## Don't\n- Do NOT change any font-family properties\n- Do NOT change any colors\n- Do NOT change heading styles\n- Do NOT touch any files other than globals.css","acceptance_criteria":"1. Body element has font-weight: 400 in computed styles\n2. Body element has line-height: 1.6\n3. List items have line-height: 1.6\n4. Text renders with antialiased smoothing (already present, verify not removed)\n5. `npm run build` succeeds without errors","status":"closed","priority":2,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":10,"created_at":"2026-02-20T18:21:19.242335+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:27:48.11089+08:00","closed_at":"2026-02-20T18:27:48.11089+08:00","close_reason":"6f5dd21 Add antialiased rendering and body font-weight","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-1ho.5","depends_on_id":"docs-1ho","type":"parent-child","created_at":"2026-02-20T18:21:19.243605+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-1ho.5","depends_on_id":"docs-1ho.1","type":"blocks","created_at":"2026-02-20T18:21:19.245486+08:00","created_by":"einstein.climateai.org"}]} +{"id":"docs-1ho.5","title":"Add antialiased rendering and body font-weight","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nThe scaffold uses `antialiased` font rendering and Outfit at weight 400 for body text. The docs site already has `-webkit-font-smoothing: antialiased` and `-moz-osx-font-smoothing: grayscale` on body — but Outfit renders slightly differently than system fonts at the default weight. Add `font-weight: 400;` explicitly to the `body` rule to ensure consistent rendering with Outfit.\n\n### 1. In the `body { ... }` rule, add:\n```css\nfont-weight: 400;\n```\nPlace it after `font-size: 16px;`.\n\n### 2. Update the existing `body` line-height from `1.65` to `1.6`\nThe scaffold uses tighter line-height with Outfit. Change:\n```css\nline-height: 1.65; → line-height: 1.6;\n```\n\n### 3. Update list item line-height to match\nIn `.layout-content article li`, change:\n```css\nline-height: 1.65; → line-height: 1.6;\n```\n\n## Don't\n- Do NOT change any font-family properties\n- Do NOT change any colors\n- Do NOT change heading styles\n- Do NOT touch any files other than globals.css","acceptance_criteria":"1. Body element has font-weight: 400 in computed styles\n2. Body element has line-height: 1.6\n3. List items have line-height: 1.6\n4. Text renders with antialiased smoothing (already present, verify not removed)\n5. `npm run build` succeeds without errors","status":"closed","priority":2,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":10,"created_at":"2026-02-20T18:21:19.242335+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:27:48.11089+08:00","closed_at":"2026-02-20T18:27:48.11089+08:00","close_reason":"6f5dd21 Add antialiased rendering and body font-weight","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-1ho.5","depends_on_id":"docs-1ho.1","type":"blocks","created_at":"2026-02-20T18:21:19.245486+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-1ho.5","depends_on_id":"docs-1ho","type":"parent-child","created_at":"2026-02-20T18:21:19.243605+08:00","created_by":"einstein.climateai.org"}]} {"id":"docs-2wk","title":"Epic: Typography \u0026 Content Readability","description":"Fix typography hierarchy and content readability: H3 at 16px collapses into body text, content max-width 920px is too wide (~95 chars/line), body text too light, asymmetric padding, insufficient H2 whitespace, understyled blockquotes. Polish: paragraph margin, inline code padding, scroll-margin-top, code block margins. Covers UX findings #5, 6, 16, 17, 18, 19, 31, 32, 33, 34.","status":"in_progress","priority":1,"issue_type":"epic","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","created_at":"2026-02-20T19:53:44.087105+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:57:32.264972+08:00","labels":["scope:medium"]} {"id":"docs-2wk.1","title":"Heading scale + body color + content width + padding","description":"## Files\n- styles/globals.css (modify)\n\n## What to do\nAll changes are CSS-only in globals.css.\n\n### H3 size increase (finding #5)\nChange `.layout-content h3` font-size from `16px` to `18px`. This is the same as body text size currently, making sections indistinguishable. Also set `letter-spacing: -0.005em` on h3 for visual distinction.\n\nMobile h3 (`@media max-width: 768px`): change from `15px` to `17px`.\n\n### Content max-width (finding #6)\nChange `--content-max-width: 920px` to `--content-max-width: 768px` in `:root`. This brings line length from ~95 chars to ~72 chars (optimal).\n\n### Body text color (finding #16)\nChange `--color-text-primary: oklch(0.40 0.015 260)` to `--color-text-primary: oklch(0.30 0.015 260)` in `:root` (light mode only). This darkens body text for better readability.\n\nAlso update `--color-text-sidebar` from `oklch(0.40 0.015 260)` to `oklch(0.35 0.015 260)` to keep sidebar slightly lighter than body but still readable.\n\n### Content right padding (finding #17)\nThe current `.layout-content` has `padding: var(--space-6) 0 var(--space-8) 72px` — 72px left, 0px right. Add right padding:\nChange to: `padding: var(--space-6) var(--space-8) var(--space-8) 72px`\n\nThis adds 32px on the right to balance the 72px left margin.\n\n### H2 margin increase (finding #18)\nChange `.layout-content h2` margin-top from `var(--space-12)` (48px) to `56px`.\n\n## Dont\n- Do NOT change dark mode text colors — only light mode\n- Do NOT change H1 or H2 font sizes\n- Do NOT change font families\n- Do NOT touch the tablet/mobile responsive padding overrides — those are fine as-is","acceptance_criteria":"1. H3 headings render at 18px (was 16px), visually distinct from 16px body text\n2. Content area max-width is 768px (was 920px)\n3. Body text color is noticeably darker (oklch 0.30 vs 0.40) in light mode\n4. Dark mode body text color is unchanged\n5. Content area has visible right padding (~32px) instead of flush right edge\n6. H2 sections have more breathing room above (56px gap instead of 48px)\n7. Build passes: npm run build -- --webpack","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":30,"created_at":"2026-02-20T19:54:01.085494+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:06:32.804273+08:00","closed_at":"2026-02-20T20:06:32.804273+08:00","close_reason":"1094f5d typography fixes","labels":["scope:small"],"dependencies":[{"issue_id":"docs-2wk.1","depends_on_id":"docs-2wk","type":"parent-child","created_at":"2026-02-20T19:54:01.08663+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-2wk.2","title":"Blockquotes + inline code + scroll-margin + code margin + paragraph margin","description":"## Files\n- styles/globals.css (modify)\n\n## What to do\nAll changes are CSS-only in globals.css.\n\n### Blockquotes (finding #19)\nCurrent blockquote styles are understyled: 1px border, cramped padding, smaller font.\nReplace the `.layout-content blockquote` rule at ~line 1086:\n\n```css\n.layout-content blockquote {\n border-left: 3px solid var(--color-border-strong);\n padding: var(--space-4) var(--space-5);\n margin: var(--space-6) 0;\n color: var(--color-text-secondary);\n font-size: 16px;\n line-height: 1.6;\n background: var(--color-bg-subtle);\n border-radius: 0 var(--radius-md) var(--radius-md) 0;\n}\n```\n\nUpdate dark mode blockquote:\n```css\nhtml.dark .layout-content blockquote {\n border-left-color: oklch(0.40 0.01 260);\n background: oklch(0.16 0.005 260);\n}\n```\n\n### Paragraph margin (finding #31)\nChange `.layout-content p` margin-bottom from `var(--space-4)` (16px) to `var(--space-5)` (20px).\n\n### Inline code padding (finding #32)\nChange `.layout-content p code, .layout-content li code, .layout-content td code, .layout-content th code` padding from `1px 4px` to `2px 6px`.\n\n### Scroll-margin-top on headings (finding #33)\nAdd scroll-margin-top to all content headings so anchor links dont hide behind the sticky header:\n\n```css\n.layout-content h1,\n.layout-content h2,\n.layout-content h3,\n.layout-content h4 {\n scroll-margin-top: calc(var(--header-height) + var(--space-6));\n}\n```\n\nNote: `html` already has `scroll-padding-top` set, but individual headings need `scroll-margin-top` for anchor (#hash) navigation to work correctly.\n\n### Code block vertical margin (finding #34)\nChange `.codeblock` margin-bottom from `var(--space-4)` (16px) to `var(--space-6)` (24px).\nAlso change `.layout-content pre:not(.codeblock-pre)` margin-bottom from `var(--space-4)` to `var(--space-6)`.\n\n## Dont\n- Do NOT change code block colors or font sizes\n- Do NOT modify the .codeblock-pre styles\n- Do NOT touch the dark mode inline code styles (they were already set)","acceptance_criteria":"1. Blockquotes have 3px left border, 16px padding, subtle background, body-sized font (16px), and rounded right corners\n2. Dark mode blockquotes have adapted border and background colors\n3. Paragraphs have 20px bottom margin (was 16px)\n4. Inline code has 2px 6px padding (was 1px 4px), visibly more spacious\n5. Clicking an anchor link (#heading) scrolls the heading below the sticky header, not behind it\n6. Code blocks have 24px bottom margin (was 16px)\n7. Build passes: npm run build -- --webpack","status":"in_progress","priority":2,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":30,"created_at":"2026-02-20T19:54:17.337357+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:07:22.80838+08:00","labels":["scope:small"],"dependencies":[{"issue_id":"docs-2wk.2","depends_on_id":"docs-2wk","type":"parent-child","created_at":"2026-02-20T19:54:17.338566+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-2wk.2","depends_on_id":"docs-2wk.1","type":"blocks","created_at":"2026-02-20T19:56:45.710114+08:00","created_by":"einstein.climateai.org"}]} +{"id":"docs-2wk.2","title":"Blockquotes + inline code + scroll-margin + code margin + paragraph margin","description":"## Files\n- styles/globals.css (modify)\n\n## What to do\nAll changes are CSS-only in globals.css.\n\n### Blockquotes (finding #19)\nCurrent blockquote styles are understyled: 1px border, cramped padding, smaller font.\nReplace the `.layout-content blockquote` rule at ~line 1086:\n\n```css\n.layout-content blockquote {\n border-left: 3px solid var(--color-border-strong);\n padding: var(--space-4) var(--space-5);\n margin: var(--space-6) 0;\n color: var(--color-text-secondary);\n font-size: 16px;\n line-height: 1.6;\n background: var(--color-bg-subtle);\n border-radius: 0 var(--radius-md) var(--radius-md) 0;\n}\n```\n\nUpdate dark mode blockquote:\n```css\nhtml.dark .layout-content blockquote {\n border-left-color: oklch(0.40 0.01 260);\n background: oklch(0.16 0.005 260);\n}\n```\n\n### Paragraph margin (finding #31)\nChange `.layout-content p` margin-bottom from `var(--space-4)` (16px) to `var(--space-5)` (20px).\n\n### Inline code padding (finding #32)\nChange `.layout-content p code, .layout-content li code, .layout-content td code, .layout-content th code` padding from `1px 4px` to `2px 6px`.\n\n### Scroll-margin-top on headings (finding #33)\nAdd scroll-margin-top to all content headings so anchor links dont hide behind the sticky header:\n\n```css\n.layout-content h1,\n.layout-content h2,\n.layout-content h3,\n.layout-content h4 {\n scroll-margin-top: calc(var(--header-height) + var(--space-6));\n}\n```\n\nNote: `html` already has `scroll-padding-top` set, but individual headings need `scroll-margin-top` for anchor (#hash) navigation to work correctly.\n\n### Code block vertical margin (finding #34)\nChange `.codeblock` margin-bottom from `var(--space-4)` (16px) to `var(--space-6)` (24px).\nAlso change `.layout-content pre:not(.codeblock-pre)` margin-bottom from `var(--space-4)` to `var(--space-6)`.\n\n## Dont\n- Do NOT change code block colors or font sizes\n- Do NOT modify the .codeblock-pre styles\n- Do NOT touch the dark mode inline code styles (they were already set)","acceptance_criteria":"1. Blockquotes have 3px left border, 16px padding, subtle background, body-sized font (16px), and rounded right corners\n2. Dark mode blockquotes have adapted border and background colors\n3. Paragraphs have 20px bottom margin (was 16px)\n4. Inline code has 2px 6px padding (was 1px 4px), visibly more spacious\n5. Clicking an anchor link (#heading) scrolls the heading below the sticky header, not behind it\n6. Code blocks have 24px bottom margin (was 16px)\n7. Build passes: npm run build -- --webpack","status":"closed","priority":2,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":30,"created_at":"2026-02-20T19:54:17.337357+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:10:45.369244+08:00","closed_at":"2026-02-20T20:10:45.369244+08:00","close_reason":"7686f09 blockquotes, inline code, scroll-margin, code margin, paragraph margin","labels":["scope:small"],"dependencies":[{"issue_id":"docs-2wk.2","depends_on_id":"docs-2wk","type":"parent-child","created_at":"2026-02-20T19:54:17.338566+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-2wk.2","depends_on_id":"docs-2wk.1","type":"blocks","created_at":"2026-02-20T19:56:45.710114+08:00","created_by":"einstein.climateai.org"}]} {"id":"docs-390","title":"Epic: Replace speculative blockchain/tokenization language with freeze-then-fund model across all docs","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T12:19:57.096114+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T19:52:57.976277+13:00","closed_at":"2026-02-16T19:52:57.976277+13:00","close_reason":"Closed"} {"id":"docs-390.1","title":"Rewrite architecture/overview.md: replace blockchain ownership layer with freeze-then-fund model","description":"## Files\n- documentation/pages/architecture/overview.md (modify)\n\n## What to do\n\nUpdate all references to blockchain-based tokenization/ownership to reflect that:\n1. The tokenization layer has NOT been developed yet — it is TBD\n2. The theory and architecture are correct — freeze records, anchor on-chain, then fund\n3. The docs should present this as the intended design, not as something that exists today\n\nThe freeze-then-fund model:\n1. A hypercert starts as mutable ATProto records (activity claim + evidence + evaluations etc.)\n2. Before a hypercert can be funded, its ATProto records must be **frozen** — a snapshot of the current state is taken and anchored on-chain. This creates an immutable reference point.\n3. The reason: a funder must know exactly what they are funding. If the cert contents can still change after funding, the funder might end up paying for something different than what they signed up for.\n4. Once frozen and anchored on-chain, the hypercert can be listed for funding.\n5. Evaluations and evidence can still accumulate AFTER freezing — they are separate records that reference the frozen claim. But the core claim itself is immutable once frozen.\n\n### IMPORTANT FRAMING GUIDANCE\n- Do NOT strip out the blockchain/tokenization concept — the theory is right and the architecture is sound\n- DO make clear that the tokenization layer hasn't been built yet — it's TBD\n- Frame it as: \"the intended design is X\" or \"the planned approach is X\" — not as if it's live\n- Use phrases like \"the tokenization layer is not yet implemented\", \"this is the planned design\", \"the on-chain mechanisms are being designed\"\n- Keep the two-layer architecture (data layer + ownership/funding layer) — it's the right design\n- When describing how tokenization/funding WILL work, use future tense or \"planned\" language\n- The freeze-then-fund concept is the KEY insight to convey: you must freeze the cert before allowing funding\n\n### Specific changes needed:\n\n**Line 8:** Change \"combines AT Protocol for data portability with blockchain for ownership guarantees\" → something like \"combines AT Protocol for data portability with planned on-chain anchoring for ownership and funding guarantees.\" Make clear this is the design, not yet implemented.\n\n**Line 18 (Ownership Layer description):** Rewrite. The ownership layer is planned but not yet built. The intended design: frozen hypercert snapshots will be anchored on-chain so they can be funded. A hypercert cannot be funded while its contents are still changing — freezing ensures funders know exactly what they are paying for. Make clear this layer is TBD.\n\n**Lines 20-31 (stack diagram):** Update the bottom layer label. Add \"(planned)\" or similar to indicate it's not yet built.\n\n**Lines 63-81 (Ownership Layer Deep Dive):** Rewrite this section to describe the PLANNED design:\n- \"Anchoring\": Describe the planned approach — freezing the ATProto record state and anchoring the snapshot on-chain.\n- \"Tokenization\": Explain the planned approach — the specific on-chain mechanism (token standard, chain choice) is being designed. The concept: freeze the cert, anchor it, then enable funding.\n- \"Funding Mechanisms\": Keep the general concepts (direct purchase, retroactive funding, etc.) but frame as planned/intended, not implemented.\n- \"Multi-Chain Support\": Frame as intended design, not yet determined.\n\n**Lines 83-112 (How the Layers Connect):** Rewrite to describe the planned design:\n- The bridge will be: ATProto records are frozen, the snapshot CID is anchored on-chain, and funding operates against that frozen state.\n- Keep the cross-layer example but frame as \"how this will work\" not \"how this works.\"\n\n**Lines 114-136 (Key Design Decisions):**\n- \"Why Not Fully On-Chain?\" — Keep as-is, still valid.\n- \"Why Not Fully Off-Chain?\" — Rewrite to emphasize: without on-chain anchoring, there's no way to guarantee that what a funder pays for won't change. Freezing and anchoring provides the immutability guarantee.\n- \"Why This Separation?\" — Update to mention freeze-then-fund as the key planned mechanism.\n\n**Lines 138-148 (What This Enables):**\n- Frame enabled use cases as what the design WILL enable once the tokenization layer is built.\n\n### Key terminology:\n- Keep \"tokenization\" as a concept but mark as \"planned\" / \"not yet implemented\" / \"TBD\"\n- Use \"freeze\" / \"frozen\" / \"freezing\" to describe the new concept\n- Use future tense or \"planned\" language for anything on-chain\n- Keep \"funding\" language — the concept is real\n\n### Tone:\n- The architecture is sound, the theory is right\n- The tokenization layer just hasn't been built yet\n- Be clear about what exists (ATProto data layer) vs what's planned (on-chain ownership/funding layer)\n\n## Test\ngrep -q 'not yet\\|TBD\\|being designed\\|planned\\|will be' documentation/pages/architecture/overview.md \u0026\u0026 \\\ngrep -q 'freez' documentation/pages/architecture/overview.md \u0026\u0026 \\\ngrep -q 'funder.*know\\|know.*fund\\|change.*after\\|cannot.*funded.*changing\\|can.t.*funded.*chang\\|exactly what' documentation/pages/architecture/overview.md \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Remove the blockchain/tokenization concept entirely — the theory is right, keep it as the planned design\n- Present tokenization as if it's already implemented/live\n- Invent specific smart contract details or token standards that don't exist\n- Change the Data Layer section (lines 33-61) — that's accurate as-is\n- Change the \"Why ATProto Over IPFS\" section (126-130) — that's accurate","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T12:20:42.512677+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T12:32:03.352974+13:00","closed_at":"2026-02-16T12:32:03.352974+13:00","close_reason":"9055496 Rewrite architecture/overview.md to reflect freeze-then-fund model and planned tokenization layer","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-390.1","depends_on_id":"docs-390","type":"parent-child","created_at":"2026-02-16T12:20:42.514231+13:00","created_by":"sharfy-test.climateai.org"}]} {"id":"docs-390.2","title":"Rewrite architecture/data-flow-and-lifecycle.md: replace Stage 5 blockchain funding with freeze-then-fund","description":"## Files\n- documentation/pages/architecture/data-flow-and-lifecycle.md (modify)\n\n## What to do\n\nUpdate blockchain-centric language in the lifecycle doc. Key framing:\n1. The tokenization layer has NOT been developed yet — it is TBD\n2. The theory and architecture are correct — freeze records, anchor on-chain, then fund\n3. The docs should present this as the intended design, not as something that exists today\n\nThe freeze-then-fund model: before a hypercert can be funded, its ATProto records must be frozen (snapshotted and anchored on-chain). This protects funders — they need to know exactly what they're funding, and the cert contents must not change after funding.\n\n### IMPORTANT FRAMING GUIDANCE\n- Do NOT strip out the blockchain/tokenization concept — the theory is right and the architecture is sound\n- DO make clear that the tokenization layer hasn't been built yet — it's TBD\n- Frame it as: \"the intended design is X\" or \"the planned approach is X\" — not as if it's live\n- Use phrases like \"the tokenization layer is not yet implemented\", \"this is the planned design\", \"the on-chain mechanisms are being designed\"\n- When describing how tokenization/funding WILL work, use future tense or \"planned\" language\n- The freeze-then-fund concept is the KEY insight to convey\n\n### Specific changes:\n\n**Line 16 (Enrichment):** Change \"Rights records define what token holders receive\" → \"Rights records define what funders or stakeholders receive.\" (The concept of rights is correct, just don't assume tokens exist yet.)\n\n**Line 22 (Funding stage summary):** Rewrite. Current text presents tokenization as live. New text should describe the planned design: Before funding, the hypercert's ATProto records will be frozen — a cryptographic snapshot taken and anchored on-chain. This ensures funders know exactly what they are paying for. The cert's core content cannot change after freezing. The specific on-chain funding mechanisms are being designed.\n\n**Line 24 (Accumulation):** Update to reflect planned design. Evaluations and evidence continue accumulating around the frozen claim.\n\n**Line 29 (diagram):** Change \"Blockchain\" under Funding to \"On-chain (planned)\"\n\n**Line 69 (Rights records):** Change \"define what token holders receive\" → \"define what funders or stakeholders receive\"\n\n**Lines 146-181 (Stage 5: Funding \u0026 Ownership):** Rewrite this section to describe the PLANNED design:\n\n- **Anchoring:** Describe the planned approach — when a hypercert is ready for funding, its current ATProto state will be frozen. The snapshot CID is anchored on-chain. The reason: a funder must know exactly what they are funding.\n\n- **Tokenization → rename to \"Freezing and Immutability\":** Explain the planned concept. Once frozen, the core activity claim cannot be modified. Evidence and evaluations can still accumulate. The specific on-chain representation (token standard, contract design) is being designed. Note: the tokenization layer is not yet implemented, but the theory is sound.\n\n- **Funding Mechanisms:** Keep the general concepts but frame as planned. Various funding models are intended, including direct funding, retroactive funding, and impact certificates. The specific mechanisms are being designed.\n\n- **Multi-Chain Support:** Frame as intended design. The protocol plans to be chain-agnostic but specifics are TBD.\n\n- **Diagram:** Update to reflect planned state. Replace \"Token Contract\" / \"Token ID\" with planned equivalents like \"Frozen Snapshot\" / \"CID: bafyrei...\"\n\n**Lines 199-205 (Stage 6 - Ownership Transfers / Long-Term Value):**\n- Reframe around the planned design. The frozen on-chain anchor will persist independently. Remove language that presents token transfers as live. Frame as intended future behavior.\n\n**Line 212 (diagram):** Change \"Blockchain\" labels to \"On-chain (planned)\"\n\n### Key terminology:\n- Keep \"tokenization\" as a concept but mark as \"planned\" / \"not yet implemented\" / \"TBD\"\n- Use \"freeze\" / \"frozen\" / \"freezing\" to describe the new concept\n- \"funders\" instead of \"token holders\" where referring to people who fund\n- Use future tense or \"planned\" language for anything on-chain\n- Keep \"on-chain\" — blockchain IS the plan\n\n## Test\ngrep -q 'not yet\\|TBD\\|being designed\\|planned\\|will be' documentation/pages/architecture/data-flow-and-lifecycle.md \u0026\u0026 \\\ngrep -q 'freez' documentation/pages/architecture/data-flow-and-lifecycle.md \u0026\u0026 \\\ngrep -q 'cannot.*change\\|must not.*change\\|exactly what.*fund\\|know.*what.*pay\\|frozen' documentation/pages/architecture/data-flow-and-lifecycle.md \u0026\u0026 \\\n! grep -q 'token holder' documentation/pages/architecture/data-flow-and-lifecycle.md \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Remove the blockchain/tokenization concept entirely — the theory is right, keep it as the planned design\n- Present tokenization as if it's already implemented/live\n- Change Stages 1-4 (Creation, Enrichment, Evaluation, Discovery) unless they contain token holder language\n- Invent smart contract details\n- Change the Cross-PDS References section (lines 215-234) — that's accurate\n- Change the \"What This Flow Enables\" section (lines 236-246) unless it has token language","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T12:21:20.091221+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T12:31:55.410818+13:00","closed_at":"2026-02-16T12:31:55.410818+13:00","close_reason":"5d8ba48 Replace Stage 5 blockchain funding with freeze-then-fund model","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-390.2","depends_on_id":"docs-390","type":"parent-child","created_at":"2026-02-16T12:21:20.092158+13:00","created_by":"sharfy-test.climateai.org"}]} @@ -38,9 +38,9 @@ {"id":"docs-cur","title":"Epic: Add dark mode and search bar to navbar","description":"Add two navbar features inspired by atproto.com: (1) Dark mode toggle with sun/moon icons, class-based theming via .dark on \u003chtml\u003e, localStorage persistence, and dark CSS custom properties for all color tokens. (2) Search pill in the navbar — rounded pill button with search icon and ⌘K hint, opening a modal that filters pages by title from navigation.js. Success: users can toggle dark/light mode and search docs pages from the navbar.","status":"closed","priority":1,"issue_type":"epic","owner":"einstein.climateai.org","created_at":"2026-02-20T19:11:58.629965+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:35:05.254804+08:00","closed_at":"2026-02-20T19:35:05.254804+08:00","close_reason":"024f0c5 Dark mode + search bar complete (PRs #25-27)","labels":["scope:medium"]} {"id":"docs-cur.1","title":"Add dark mode CSS variables and component overrides","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\n\nAdd an `html.dark` CSS block after the existing `:root` block (after line 137) that overrides ALL design tokens for dark mode. Use the scaffold's dark palette (indigo hue 260) as the reference.\n\n### Dark token values (inside `html.dark { }`):\n```css\nhtml.dark {\n --color-bg: oklch(0.13 0.005 260);\n --color-bg-subtle: oklch(0.17 0.005 260);\n --color-border: oklch(0.25 0.005 260);\n --color-border-strong: oklch(0.30 0.01 260);\n --color-text-primary: oklch(0.85 0.005 260);\n --color-text-heading: oklch(0.95 0.005 260);\n --color-text-title: oklch(0.95 0.005 260);\n --color-text-secondary: oklch(0.65 0.01 260);\n --color-text-sidebar: oklch(0.85 0.005 260);\n --color-link: oklch(0.65 0.15 260);\n --color-link-hover: oklch(0.72 0.15 260);\n --color-accent: oklch(0.65 0.15 260);\n --color-info: oklch(0.65 0.15 260);\n --color-info-bg: oklch(0.20 0.02 260);\n --color-warning: oklch(0.70 0.15 55);\n --color-warning-bg: oklch(0.20 0.02 55);\n --color-danger: oklch(0.65 0.20 25);\n --color-danger-bg: oklch(0.20 0.02 25);\n --color-success: oklch(0.65 0.15 145);\n --color-success-bg: oklch(0.20 0.02 145);\n --hover-bg: oklch(0.20 0.008 260);\n --active-bg: oklch(0.22 0.01 260);\n --focus-ring: 0 0 0 4px oklch(0.65 0.15 260 / 0.36);\n}\n```\n\n### Component-specific dark overrides (add after the html.dark block):\n\n1. **Header glass panel** — `.layout-header` override:\n ```css\n html.dark .layout-header {\n background: oklch(0.13 0.005 260 / 0.7);\n border-bottom-color: oklch(0.95 0 0 / 0.1);\n }\n ```\n\n2. **Logo inversion** — make the black SVG logo white:\n ```css\n html.dark .layout-logo-img {\n filter: invert(1);\n }\n ```\n\n3. **Logo badge** — border and text adjustments:\n ```css\n html.dark .layout-logo-badge {\n border-color: oklch(0.95 0 0 / 0.15);\n color: var(--color-text-secondary);\n }\n ```\n\n4. **Sidebar** — border and active state overrides:\n ```css\n html.dark .sidebar {\n border-right-color: oklch(0.95 0 0 / 0.1);\n }\n html.dark .sidebar-section {\n border-top-color: oklch(0.95 0 0 / 0.1);\n }\n html.dark .sidebar-collapse-btn {\n background: var(--color-bg);\n }\n ```\n\n5. **Scrollbar dark mode**:\n ```css\n html.dark .sidebar-content:hover::-webkit-scrollbar-thumb,\n html.dark .toc:hover::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.15);\n }\n html.dark .sidebar-content::-webkit-scrollbar-thumb:hover,\n html.dark .toc::-webkit-scrollbar-thumb:hover {\n background: rgba(255, 255, 255, 0.25);\n }\n html.dark .sidebar-content:hover,\n html.dark .toc:hover {\n scrollbar-color: rgba(255, 255, 255, 0.15) transparent;\n }\n ```\n\n6. **Inline code dark mode**:\n ```css\n html.dark .layout-content p code,\n html.dark .layout-content li code,\n html.dark .layout-content td code,\n html.dark .layout-content th code {\n background: oklch(0.20 0.008 260);\n border-color: oklch(0.30 0.005 260);\n }\n ```\n\n7. **Dot pattern and hero banner**:\n ```css\n html.dark .dot-pattern {\n fill: oklch(0.22 0.005 260);\n }\n ```\n\n8. **Card link icon box dark mode**:\n ```css\n html.dark .card-link-icon-box {\n box-shadow: inset 0 0 0 1px oklch(0.95 0 0 / 0.15);\n }\n html.dark .card-link:hover .card-link-icon-box {\n box-shadow: inset 0 0 0 1px oklch(0.95 0 0 / 0.3);\n }\n ```\n\n9. **Tables**:\n ```css\n html.dark .layout-content th {\n border-bottom-color: oklch(0.30 0.01 260);\n }\n html.dark .layout-content td {\n border-bottom-color: oklch(0.25 0.005 260);\n }\n ```\n\n10. **Blockquotes**:\n ```css\n html.dark .layout-content blockquote {\n border-left-color: oklch(0.35 0.01 260);\n }\n ```\n\n11. **Pagination**:\n ```css\n html.dark .pagination {\n border-top-color: oklch(0.25 0.005 260);\n }\n html.dark .pagination-link {\n border-color: oklch(0.25 0.005 260);\n }\n html.dark .pagination-link:hover {\n border-color: var(--color-link);\n box-shadow: 0 2px 8px oklch(0.65 0.15 260 / 0.15);\n }\n ```\n\n12. **Breadcrumbs**:\n ```css\n html.dark .breadcrumbs-separator {\n color: oklch(0.45 0.01 260);\n }\n ```\n\n13. **Content links** (underline decoration color):\n ```css\n html.dark .layout-content a {\n text-decoration-color: oklch(0.65 0.15 260 / 0.4);\n }\n ```\n\n14. **HR**:\n ```css\n html.dark .layout-content hr {\n border-top-color: oklch(0.25 0.005 260);\n }\n ```\n\n15. **Sidebar overlay (mobile)**:\n ```css\n html.dark .sidebar-overlay {\n background: rgba(0, 0, 0, 0.6);\n }\n ```\n\n16. **Codeblock header** (already dark, but adjust border for consistency):\n ```css\n html.dark .codeblock {\n border-color: oklch(0.25 0.005 260);\n }\n html.dark .codeblock-header {\n border-bottom-color: oklch(0.25 0.005 260);\n }\n ```\n\n### Placement\nInsert ALL dark mode rules as a single contiguous section right after the `:root { }` block (after line 137, before the @media query on line 139). Add a comment header: `/* ===== Dark Mode ===== */`\n\n## Don't\n- Do NOT add any JavaScript or components — this is CSS only\n- Do NOT modify any existing light mode rules — only ADD new dark mode rules\n- Do NOT change the code block background (Night Owl theme is already dark)\n- Do NOT use @media (prefers-color-scheme) — we use class-based toggling via html.dark\n- Do NOT use Tailwind — this project uses plain CSS","acceptance_criteria":"1. A new 'html.dark { }' block exists in globals.css with all listed token overrides\n2. All 16 component-specific dark override blocks are present\n3. No existing light-mode CSS rules are modified\n4. The dark mode section has a '/* ===== Dark Mode ===== */' comment header\n5. When html element has class 'dark', the page background is dark (oklch 0.13), text is light, header is glass-dark, logo is white (inverted), sidebar borders are subtle white\n6. npm run build -- --webpack succeeds without errors","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":45,"created_at":"2026-02-20T19:19:35.606514+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:28:58.450998+08:00","closed_at":"2026-02-20T19:28:58.450998+08:00","close_reason":"a0feb77 Add dark mode CSS variables and component overrides","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-cur.1","depends_on_id":"docs-cur","type":"parent-child","created_at":"2026-02-20T19:19:35.607472+08:00","created_by":"einstein.climateai.org"}]} {"id":"docs-cur.2","title":"Add ThemeToggle component and FOUC prevention script","description":"## Files\n- documentation/components/ThemeToggle.js (create)\n- documentation/components/Layout.js (modify)\n\n## What to do\n\n### 1. Create ThemeToggle.js component\n\nCreate `documentation/components/ThemeToggle.js` that renders a button toggling dark mode. Copy the ATProto pattern: sun icon visible in light mode, moon icon visible in dark mode.\n\n```jsx\nimport { useState, useEffect } from 'react';\n\nexport function ThemeToggle() {\n const [isDark, setIsDark] = useState(false);\n\n useEffect(() =\u003e {\n // Sync state with actual DOM class on mount\n setIsDark(document.documentElement.classList.contains('dark'));\n }, []);\n\n const toggle = () =\u003e {\n const next = !isDark;\n setIsDark(next);\n document.documentElement.classList.toggle('dark', next);\n localStorage.setItem('theme', next ? 'dark' : 'light');\n };\n\n return (\n \u003cbutton\n className=\"theme-toggle\"\n onClick={toggle}\n aria-label=\"Toggle dark mode\"\n type=\"button\"\n \u003e\n {/* Sun icon — visible in light mode */}\n \u003csvg className=\"theme-toggle-sun\" viewBox=\"0 0 20 20\" fill=\"none\" width=\"20\" height=\"20\"\u003e\n \u003cpath d=\"M12.5 10a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0Z\" stroke=\"currentColor\" strokeWidth=\"1.5\" /\u003e\n \u003cpath d=\"M10 5.5v-1M13.182 6.818l.707-.707M14.5 10h1M13.182 13.182l.707.707M10 15.5v-1M6.11 13.889l.708-.707M4.5 10h1M6.11 6.111l.708.707\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeWidth=\"1.5\" /\u003e\n \u003c/svg\u003e\n {/* Moon icon — visible in dark mode */}\n \u003csvg className=\"theme-toggle-moon\" viewBox=\"0 0 20 20\" fill=\"none\" width=\"20\" height=\"20\"\u003e\n \u003cpath d=\"M15.224 11.724a5.5 5.5 0 0 1-6.949-6.949 5.5 5.5 0 1 0 6.949 6.949Z\" stroke=\"currentColor\" strokeWidth=\"1.5\" /\u003e\n \u003c/svg\u003e\n \u003c/button\u003e\n );\n}\n```\n\n### 2. Add FOUC prevention script to Layout.js\n\nIn `Layout.js`, add this inline script tag INSIDE the `\u003cHead\u003e` component, right before the closing `\u003c/Head\u003e` tag. This runs before paint to prevent flash of wrong theme:\n\n```jsx\n\u003cscript\n dangerouslySetInnerHTML={{\n __html: `(function(){try{var d=document.documentElement;var t=localStorage.getItem('theme');if(t==='dark'){d.classList.add('dark')}else if(t==='light'){d.classList.remove('dark')}else if(window.matchMedia('(prefers-color-scheme:dark)').matches){d.classList.add('dark')}}catch(e){}})()`\n }}\n/\u003e\n```\n\n### 3. Add ThemeToggle to Layout.js header\n\nImport ThemeToggle at top of Layout.js:\n```jsx\nimport { ThemeToggle } from './ThemeToggle';\n```\n\nAdd a spacer div and the ThemeToggle button to the header, after the logo link. The header inner should become:\n```jsx\n\u003cdiv className=\"layout-header-inner\"\u003e\n {/* hamburger button (unchanged) */}\n {/* logo link (unchanged) */}\n \u003cdiv style={{ flex: 1 }} /\u003e\n \u003cThemeToggle /\u003e\n\u003c/div\u003e\n```\n\nThe `\u003cdiv style={{ flex: 1 }} /\u003e` pushes the toggle to the far right. Keep the hamburger and logo exactly as they are.\n\n### 4. Add CSS for ThemeToggle to globals.css\n\nAdd these styles in the `/* ===== Header ===== */` section (after `.hamburger-btn:hover`, around line 253):\n\n```css\n/* Theme toggle */\n.theme-toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--color-text-secondary);\n border-radius: var(--radius-md);\n transition: background var(--transition-fast), color var(--transition-fast);\n}\n\n.theme-toggle:hover {\n background: var(--hover-bg);\n color: var(--color-text-primary);\n}\n\n.theme-toggle-sun {\n display: block;\n}\n\n.theme-toggle-moon {\n display: none;\n}\n\nhtml.dark .theme-toggle-sun {\n display: none;\n}\n\nhtml.dark .theme-toggle-moon {\n display: block;\n}\n```\n\n## Don't\n- Do NOT use React Context or any state management library — just DOM classList and localStorage\n- Do NOT use @media (prefers-color-scheme) in CSS — only in the JS init script as a fallback\n- Do NOT modify any existing Layout.js elements (hamburger, logo, sidebar, main content) — only ADD the spacer div and ThemeToggle\n- Do NOT import ThemeToggle in _app.js — it's used directly in Layout.js, not as a Markdoc component\n- Do NOT use Tailwind","acceptance_criteria":"1. ThemeToggle.js exists at documentation/components/ThemeToggle.js\n2. Clicking the toggle adds/removes 'dark' class on document.documentElement\n3. Theme choice persists in localStorage under key 'theme' (values: 'dark' or 'light')\n4. On page load, the inline script in \u003cHead\u003e applies the correct class BEFORE React hydrates (no FOUC)\n5. Sun icon shows in light mode, moon icon shows in dark mode (controlled via CSS display: none/block)\n6. Toggle button is positioned at the far right of the header bar\n7. If localStorage has no 'theme' key, system preference (prefers-color-scheme: dark) is respected as fallback\n8. npm run build -- --webpack succeeds without errors","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":30,"created_at":"2026-02-20T19:20:15.172151+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:29:52.241082+08:00","closed_at":"2026-02-20T19:29:52.241082+08:00","close_reason":"41c4f66 Add ThemeToggle component and FOUC prevention script","labels":["scope:small"],"dependencies":[{"issue_id":"docs-cur.2","depends_on_id":"docs-cur","type":"parent-child","created_at":"2026-02-20T19:20:15.172988+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-cur.3","title":"Add search pill button and SearchDialog component","description":"## Files\n- documentation/components/SearchDialog.js (create)\n- documentation/components/Layout.js (modify)\n- documentation/styles/globals.css (modify)\n\n## What to do\n\n### 1. Create SearchDialog.js\n\nCreate `documentation/components/SearchDialog.js` — a modal dialog that lets users search docs pages by title. It uses the navigation data from `lib/navigation.js`.\n\n```jsx\nimport { useState, useEffect, useRef, useCallback } from 'react';\nimport { useRouter } from 'next/router';\nimport { flattenNavigation } from '../lib/navigation';\n\nexport function SearchDialog({ isOpen, onClose }) {\n const [query, setQuery] = useState('');\n const inputRef = useRef(null);\n const router = useRouter();\n const allPages = flattenNavigation();\n\n // Filter pages by title match (case-insensitive substring)\n const results = query.trim().length \u003e 0\n ? allPages.filter(p =\u003e p.title.toLowerCase().includes(query.toLowerCase()))\n : allPages;\n\n // Focus input when dialog opens\n useEffect(() =\u003e {\n if (isOpen \u0026\u0026 inputRef.current) {\n inputRef.current.focus();\n setQuery('');\n }\n }, [isOpen]);\n\n // Close on Escape\n useEffect(() =\u003e {\n if (!isOpen) return;\n const handler = (e) =\u003e {\n if (e.key === 'Escape') onClose();\n };\n document.addEventListener('keydown', handler);\n return () =\u003e document.removeEventListener('keydown', handler);\n }, [isOpen, onClose]);\n\n const navigate = useCallback((path) =\u003e {\n router.push(path);\n onClose();\n }, [router, onClose]);\n\n if (!isOpen) return null;\n\n return (\n \u003cdiv className=\"search-overlay\" onClick={onClose}\u003e\n \u003cdiv className=\"search-dialog\" onClick={(e) =\u003e e.stopPropagation()}\u003e\n \u003cdiv className=\"search-input-wrapper\"\u003e\n \u003csvg className=\"search-input-icon\" viewBox=\"0 0 20 20\" fill=\"none\" width=\"20\" height=\"20\"\u003e\n \u003cpath d=\"M12.01 12a4.25 4.25 0 1 0-6.02-6 4.25 4.25 0 0 0 6.02 6Zm0 0 3.24 3.25\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"1.5\" /\u003e\n \u003c/svg\u003e\n \u003cinput\n ref={inputRef}\n className=\"search-input\"\n type=\"text\"\n placeholder=\"Search documentation...\"\n value={query}\n onChange={(e) =\u003e setQuery(e.target.value)}\n onKeyDown={(e) =\u003e {\n if (e.key === 'Enter' \u0026\u0026 results.length \u003e 0) {\n navigate(results[0].path);\n }\n }}\n /\u003e\n \u003ckbd className=\"search-input-kbd\"\u003eESC\u003c/kbd\u003e\n \u003c/div\u003e\n \u003cdiv className=\"search-results\"\u003e\n {results.length === 0 ? (\n \u003cdiv className=\"search-no-results\"\u003eNo pages found\u003c/div\u003e\n ) : (\n \u003cul className=\"search-results-list\"\u003e\n {results.map((page) =\u003e (\n \u003cli key={page.path}\u003e\n \u003cbutton\n className=\"search-result-item\"\n onClick={() =\u003e navigate(page.path)}\n type=\"button\"\n \u003e\n \u003cspan className=\"search-result-title\"\u003e{page.title}\u003c/span\u003e\n \u003cspan className=\"search-result-path\"\u003e{page.path}\u003c/span\u003e\n \u003c/button\u003e\n \u003c/li\u003e\n ))}\n \u003c/ul\u003e\n )}\n \u003c/div\u003e\n \u003c/div\u003e\n \u003c/div\u003e\n );\n}\n```\n\n### 2. Modify Layout.js — add search button and dialog\n\nAdd these imports at top:\n```jsx\nimport { SearchDialog } from './SearchDialog';\n```\n\nAdd search state inside the Layout component (after the existing useState calls):\n```jsx\nconst [searchOpen, setSearchOpen] = useState(false);\n```\n\nAdd keyboard shortcut effect (after the existing useEffect):\n```jsx\nuseEffect(() =\u003e {\n const handler = (e) =\u003e {\n if ((e.metaKey || e.ctrlKey) \u0026\u0026 e.key === 'k') {\n e.preventDefault();\n setSearchOpen(prev =\u003e !prev);\n }\n };\n document.addEventListener('keydown', handler);\n return () =\u003e document.removeEventListener('keydown', handler);\n}, []);\n```\n\nIn the header, between the logo link and the spacer div, add the search button. The header inner should be:\n```jsx\n\u003cdiv className=\"layout-header-inner\"\u003e\n {/* hamburger button (unchanged) */}\n {/* logo link (unchanged) */}\n \u003cbutton\n className=\"search-pill\"\n onClick={() =\u003e setSearchOpen(true)}\n type=\"button\"\n \u003e\n \u003csvg className=\"search-pill-icon\" viewBox=\"0 0 20 20\" fill=\"none\" width=\"16\" height=\"16\"\u003e\n \u003cpath d=\"M12.01 12a4.25 4.25 0 1 0-6.02-6 4.25 4.25 0 0 0 6.02 6Zm0 0 3.24 3.25\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"1.5\" /\u003e\n \u003c/svg\u003e\n \u003cspan className=\"search-pill-text\"\u003eFind something...\u003c/span\u003e\n \u003ckbd className=\"search-pill-kbd\"\u003e\u003cspan\u003e⌘\u003c/span\u003eK\u003c/kbd\u003e\n \u003c/button\u003e\n \u003cdiv style={{ flex: 1 }} /\u003e\n {/* ThemeToggle (added by docs-cur.2 — if not present yet, ignore) */}\n\u003c/div\u003e\n```\n\nAdd the SearchDialog component at the END of the JSX, right before the closing `\u003c/\u003e` fragment:\n```jsx\n\u003cSearchDialog isOpen={searchOpen} onClose={() =\u003e setSearchOpen(false)} /\u003e\n```\n\n### 3. Add CSS for search components to globals.css\n\nAdd these styles at the end of the `/* ===== Header ===== */` section (after theme-toggle styles if present, otherwise after .hamburger-btn:hover):\n\n```css\n/* Search pill button */\n.search-pill {\n display: none;\n align-items: center;\n gap: 8px;\n height: 32px;\n padding: 0 12px 0 8px;\n background: var(--color-bg);\n border: 1px solid var(--color-border);\n border-radius: 9999px;\n cursor: pointer;\n font-size: 14px;\n color: var(--color-text-secondary);\n transition: border-color var(--transition-fast);\n white-space: nowrap;\n}\n\n.search-pill:hover {\n border-color: var(--color-border-strong);\n}\n\n.search-pill-icon {\n flex-shrink: 0;\n color: var(--color-text-secondary);\n}\n\n.search-pill-text {\n font-size: 13px;\n color: var(--color-text-secondary);\n}\n\n.search-pill-kbd {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n margin-left: 8px;\n font-size: 11px;\n font-family: var(--font-sans);\n color: oklch(0.55 0.01 260);\n pointer-events: none;\n}\n\n/* Show pill only on large screens */\n@media (min-width: 769px) {\n .search-pill {\n display: flex;\n }\n}\n```\n\nAdd a new section before the responsive section for search dialog styles:\n```css\n/* ===== Component: SearchDialog ===== */\n.search-overlay {\n position: fixed;\n inset: 0;\n z-index: 200;\n background: oklch(0 0 0 / 0.4);\n display: flex;\n align-items: flex-start;\n justify-content: center;\n padding-top: 15vh;\n}\n\nhtml.dark .search-overlay {\n background: oklch(0 0 0 / 0.6);\n}\n\n.search-dialog {\n width: 90%;\n max-width: 560px;\n max-height: 60vh;\n background: var(--color-bg);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-xl);\n box-shadow: 0 16px 70px oklch(0 0 0 / 0.2);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n}\n\n.search-input-wrapper {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 16px;\n border-bottom: 1px solid var(--color-border);\n}\n\n.search-input-icon {\n flex-shrink: 0;\n color: var(--color-text-secondary);\n}\n\n.search-input {\n flex: 1;\n border: none;\n background: none;\n outline: none;\n font-size: 16px;\n font-family: var(--font-sans);\n color: var(--color-text-primary);\n}\n\n.search-input::placeholder {\n color: var(--color-text-secondary);\n}\n\n.search-input-kbd {\n font-size: 11px;\n font-family: var(--font-mono);\n color: var(--color-text-secondary);\n background: var(--color-bg-subtle);\n border: 1px solid var(--color-border);\n border-radius: 4px;\n padding: 2px 6px;\n line-height: 1;\n}\n\n.search-results {\n overflow-y: auto;\n max-height: calc(60vh - 52px);\n padding: 8px;\n}\n\n.search-results-list {\n list-style: none;\n padding: 0;\n margin: 0;\n}\n\n.search-result-item {\n display: flex;\n flex-direction: column;\n width: 100%;\n padding: 8px 12px;\n background: none;\n border: none;\n border-radius: var(--radius-md);\n cursor: pointer;\n text-align: left;\n font-family: var(--font-sans);\n transition: background var(--transition-fast);\n}\n\n.search-result-item:hover {\n background: var(--hover-bg);\n}\n\n.search-result-title {\n font-size: 14px;\n font-weight: 500;\n color: var(--color-text-heading);\n}\n\n.search-result-path {\n font-size: 12px;\n color: var(--color-text-secondary);\n font-family: var(--font-mono);\n margin-top: 2px;\n}\n\n.search-no-results {\n padding: 24px;\n text-align: center;\n font-size: 14px;\n color: var(--color-text-secondary);\n}\n```\n\n## Don't\n- Do NOT install any search library (Algolia, FlexSearch, etc.) — use simple substring matching on page titles from navigation.js\n- Do NOT use Tailwind\n- Do NOT modify Sidebar.js, _app.js, or any markdoc files\n- Do NOT add the search pill inside the mobile hamburger menu — it's only in the header\n- Do NOT create any API routes — this is fully client-side","acceptance_criteria":"1. SearchDialog.js exists at documentation/components/SearchDialog.js\n2. A pill-shaped 'Find something...' button appears in the header on screens \u003e= 769px\n3. Clicking the pill or pressing ⌘K (Mac) / Ctrl+K (Windows) opens a centered modal dialog\n4. Typing in the dialog filters pages from navigation.js by case-insensitive title substring\n5. Clicking a result navigates to that page and closes the dialog\n6. Pressing Enter navigates to the first result\n7. Pressing Escape or clicking the overlay closes the dialog\n8. The search dialog is dark-mode-aware (uses CSS variables)\n9. npm run build -- --webpack succeeds without errors","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":45,"created_at":"2026-02-20T19:21:12.221001+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:32:28.903279+08:00","closed_at":"2026-02-20T19:32:28.903279+08:00","close_reason":"1bb28fe Add search pill button and SearchDialog component","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-cur.3","depends_on_id":"docs-cur","type":"parent-child","created_at":"2026-02-20T19:21:12.221976+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-cur.3","depends_on_id":"docs-cur.2","type":"blocks","created_at":"2026-02-20T19:21:12.223132+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-cur.4","title":"Integration: verify dark mode and search build and render correctly","description":"## Files\n- All files from docs-cur.1, docs-cur.2, docs-cur.3 (verify only)\n\n## What to do\n\nRun the full build and verify all dark mode + search features work together:\n\n1. `npm run build -- --webpack` must succeed with zero errors\n2. Verify dark mode tokens are applied correctly by checking CSS\n3. Verify ThemeToggle exists and is imported in Layout.js\n4. Verify SearchDialog exists and is imported in Layout.js\n5. Verify search pill button markup exists in Layout.js header\n6. Verify no duplicate spacer divs or broken header layout\n\nIf any of these checks fail, fix the issue in the relevant file(s). Common issues:\n- Merge conflict artifacts in Layout.js (both docs-cur.2 and docs-cur.3 modify it)\n- Missing import statements\n- Duplicate CSS rules in globals.css\n- FOUC script missing from Head\n\n## Don't\n- Do NOT add new features — this is verification and conflict resolution only\n- Do NOT redesign any component's appearance\n- Do NOT use Tailwind","acceptance_criteria":"1. npm run build -- --webpack succeeds with zero errors\n2. Layout.js imports and renders both ThemeToggle and SearchDialog\n3. Layout.js has the FOUC prevention script in Head\n4. globals.css has the dark mode section with html.dark tokens\n5. globals.css has search pill and search dialog CSS\n6. No merge conflict markers (\u003c\u003c\u003c\u003c\u003c\u003c\u003c etc.) in any file\n7. Header contains: hamburger, logo, search pill, spacer, theme toggle (in that order)","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":15,"created_at":"2026-02-20T19:21:29.21361+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:34:15.1468+08:00","closed_at":"2026-02-20T19:34:15.1468+08:00","close_reason":"3d39aeb integration verified: build passes, dark mode + search render correctly, no conflicts","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-cur.4","depends_on_id":"docs-cur","type":"parent-child","created_at":"2026-02-20T19:21:29.214831+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-cur.4","depends_on_id":"docs-cur.1","type":"blocks","created_at":"2026-02-20T19:21:29.216033+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-cur.4","depends_on_id":"docs-cur.2","type":"blocks","created_at":"2026-02-20T19:21:29.216899+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-cur.4","depends_on_id":"docs-cur.3","type":"blocks","created_at":"2026-02-20T19:21:29.21772+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-d4r","title":"Epic 8: Build Configuration and Vercel Deployment","description":"## Summary\nVerify the Next.js build pipeline works end-to-end, fix any build errors, and configure Vercel deployment for the documentation site.\n\n## Context\nThis project is a Next.js + Markdoc documentation site in the `documentation/` directory inside the repository at `/Users/sharfy/Code/hypercerts-atproto-documentation/documentation/`. All commands should be run from the `documentation/` directory.\n\nBy the time this epic runs, Epics 1-7 have:\n- Scaffolded the Next.js + Markdoc project (Epic 1)\n- Created custom Markdoc tags and React components (Epic 2)\n- Migrated all 17 content files to `pages/` (Epic 3)\n- Built the navigation and layout system (Epic 4)\n- Applied Stripe-inspired styling (Epic 5)\n- Moved images to `public/images/` (Epic 6)\n- Fixed all broken links (Epic 7)\n\n## Tasks\n\n### 1. Verify Local Development Server\n```bash\ncd documentation\nnpm run dev\n```\n- Open http://localhost:3000 in a browser\n- Navigate through ALL pages via the sidebar and verify:\n - Pages load without console errors\n - Images display correctly\n - Tables render with proper styling\n - Callout boxes display with colored left borders\n - Column layouts work (side by side on desktop)\n - Sidebar navigation highlights the current page\n - Right-side table of contents shows headings\n - Prev/next pagination links work\n- Fix any runtime errors or warnings\n\n### 2. Verify Production Build\n```bash\ncd documentation\nnpm run build\n```\n- Ensure the build completes with ZERO errors\n- Common issues to watch for:\n - Missing component imports\n - Invalid Markdoc tag syntax in .md files\n - Broken internal links that cause 404s during static generation\n - CSS module import errors\n- Fix any build-time errors before proceeding\n\n### 3. Test Production Server Locally\n```bash\ncd documentation\nnpm run start\n```\n- Verify the production build serves correctly at http://localhost:3000\n- Spot-check 3-5 pages for rendering issues\n- Verify images load (they should be in `public/images/`)\n\n### 4. Configure Static Export (if desired)\nFor pure static hosting, add `output: 'export'` to `next.config.js`:\n```js\nconst withMarkdoc = require('@markdoc/next.js');\nmodule.exports = withMarkdoc({ mode: 'static' })({\n output: 'export',\n pageExtensions: ['md', 'mdoc', 'js', 'jsx', 'ts', 'tsx']\n});\n```\nThen `npm run build` will output static files to the `out/` directory.\n\n### 5. Verify .gitignore\nEnsure `documentation/.gitignore` contains:\n```\nnode_modules/\n.next/\nout/\n```\n\n### 6. Prepare for Vercel Deployment\nCreate or verify that the project is ready for Vercel:\n- The `documentation/` directory should be self-contained with its own `package.json`\n- `npm run build` must succeed\n- The Vercel project settings should be:\n - **Root Directory:** `documentation`\n - **Framework Preset:** Next.js (auto-detected)\n - **Build Command:** `npm run build` (or auto-detected)\n - **Output Directory:** `.next` (or `out` if using static export)\n\nNote: Actually connecting the GitHub repo to Vercel and deploying is a manual step that requires Vercel account access. This epic should ensure the project is deployment-ready.\n\n## Acceptance Criteria\n- `npm run dev` starts without errors and all 17 pages render correctly\n- `npm run build` completes with zero errors\n- `npm run start` serves the production build correctly\n- `.gitignore` excludes `node_modules/`, `.next/`, `out/`\n- All pages, images, tables, callouts, columns, sidebar navigation, and pagination work correctly\n- The project is ready to be deployed to Vercel (self-contained `documentation/` directory with valid `package.json` and build scripts)\n","status":"closed","priority":2,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-13T13:45:29.358726+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T13:19:10.831281+13:00","closed_at":"2026-02-14T13:19:10.831287+13:00","dependencies":[{"issue_id":"docs-d4r","depends_on_id":"docs-c34","type":"blocks","created_at":"2026-02-13T13:45:29.360892+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-d4r","depends_on_id":"docs-vbw","type":"blocks","created_at":"2026-02-13T13:45:29.363352+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-d4r","depends_on_id":"docs-qsc","type":"blocks","created_at":"2026-02-13T13:45:29.364147+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-d4r","depends_on_id":"docs-7dv","type":"blocks","created_at":"2026-02-13T13:45:29.364846+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-d4r","depends_on_id":"docs-yne","type":"blocks","created_at":"2026-02-13T13:45:29.365508+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-d4r","depends_on_id":"docs-cij","type":"blocks","created_at":"2026-02-13T13:45:29.366168+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-d4r","depends_on_id":"docs-7b4","type":"blocks","created_at":"2026-02-13T13:45:29.366821+13:00","created_by":"Sharfy Adamantine"}]} +{"id":"docs-cur.3","title":"Add search pill button and SearchDialog component","description":"## Files\n- documentation/components/SearchDialog.js (create)\n- documentation/components/Layout.js (modify)\n- documentation/styles/globals.css (modify)\n\n## What to do\n\n### 1. Create SearchDialog.js\n\nCreate `documentation/components/SearchDialog.js` — a modal dialog that lets users search docs pages by title. It uses the navigation data from `lib/navigation.js`.\n\n```jsx\nimport { useState, useEffect, useRef, useCallback } from 'react';\nimport { useRouter } from 'next/router';\nimport { flattenNavigation } from '../lib/navigation';\n\nexport function SearchDialog({ isOpen, onClose }) {\n const [query, setQuery] = useState('');\n const inputRef = useRef(null);\n const router = useRouter();\n const allPages = flattenNavigation();\n\n // Filter pages by title match (case-insensitive substring)\n const results = query.trim().length \u003e 0\n ? allPages.filter(p =\u003e p.title.toLowerCase().includes(query.toLowerCase()))\n : allPages;\n\n // Focus input when dialog opens\n useEffect(() =\u003e {\n if (isOpen \u0026\u0026 inputRef.current) {\n inputRef.current.focus();\n setQuery('');\n }\n }, [isOpen]);\n\n // Close on Escape\n useEffect(() =\u003e {\n if (!isOpen) return;\n const handler = (e) =\u003e {\n if (e.key === 'Escape') onClose();\n };\n document.addEventListener('keydown', handler);\n return () =\u003e document.removeEventListener('keydown', handler);\n }, [isOpen, onClose]);\n\n const navigate = useCallback((path) =\u003e {\n router.push(path);\n onClose();\n }, [router, onClose]);\n\n if (!isOpen) return null;\n\n return (\n \u003cdiv className=\"search-overlay\" onClick={onClose}\u003e\n \u003cdiv className=\"search-dialog\" onClick={(e) =\u003e e.stopPropagation()}\u003e\n \u003cdiv className=\"search-input-wrapper\"\u003e\n \u003csvg className=\"search-input-icon\" viewBox=\"0 0 20 20\" fill=\"none\" width=\"20\" height=\"20\"\u003e\n \u003cpath d=\"M12.01 12a4.25 4.25 0 1 0-6.02-6 4.25 4.25 0 0 0 6.02 6Zm0 0 3.24 3.25\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"1.5\" /\u003e\n \u003c/svg\u003e\n \u003cinput\n ref={inputRef}\n className=\"search-input\"\n type=\"text\"\n placeholder=\"Search documentation...\"\n value={query}\n onChange={(e) =\u003e setQuery(e.target.value)}\n onKeyDown={(e) =\u003e {\n if (e.key === 'Enter' \u0026\u0026 results.length \u003e 0) {\n navigate(results[0].path);\n }\n }}\n /\u003e\n \u003ckbd className=\"search-input-kbd\"\u003eESC\u003c/kbd\u003e\n \u003c/div\u003e\n \u003cdiv className=\"search-results\"\u003e\n {results.length === 0 ? (\n \u003cdiv className=\"search-no-results\"\u003eNo pages found\u003c/div\u003e\n ) : (\n \u003cul className=\"search-results-list\"\u003e\n {results.map((page) =\u003e (\n \u003cli key={page.path}\u003e\n \u003cbutton\n className=\"search-result-item\"\n onClick={() =\u003e navigate(page.path)}\n type=\"button\"\n \u003e\n \u003cspan className=\"search-result-title\"\u003e{page.title}\u003c/span\u003e\n \u003cspan className=\"search-result-path\"\u003e{page.path}\u003c/span\u003e\n \u003c/button\u003e\n \u003c/li\u003e\n ))}\n \u003c/ul\u003e\n )}\n \u003c/div\u003e\n \u003c/div\u003e\n \u003c/div\u003e\n );\n}\n```\n\n### 2. Modify Layout.js — add search button and dialog\n\nAdd these imports at top:\n```jsx\nimport { SearchDialog } from './SearchDialog';\n```\n\nAdd search state inside the Layout component (after the existing useState calls):\n```jsx\nconst [searchOpen, setSearchOpen] = useState(false);\n```\n\nAdd keyboard shortcut effect (after the existing useEffect):\n```jsx\nuseEffect(() =\u003e {\n const handler = (e) =\u003e {\n if ((e.metaKey || e.ctrlKey) \u0026\u0026 e.key === 'k') {\n e.preventDefault();\n setSearchOpen(prev =\u003e !prev);\n }\n };\n document.addEventListener('keydown', handler);\n return () =\u003e document.removeEventListener('keydown', handler);\n}, []);\n```\n\nIn the header, between the logo link and the spacer div, add the search button. The header inner should be:\n```jsx\n\u003cdiv className=\"layout-header-inner\"\u003e\n {/* hamburger button (unchanged) */}\n {/* logo link (unchanged) */}\n \u003cbutton\n className=\"search-pill\"\n onClick={() =\u003e setSearchOpen(true)}\n type=\"button\"\n \u003e\n \u003csvg className=\"search-pill-icon\" viewBox=\"0 0 20 20\" fill=\"none\" width=\"16\" height=\"16\"\u003e\n \u003cpath d=\"M12.01 12a4.25 4.25 0 1 0-6.02-6 4.25 4.25 0 0 0 6.02 6Zm0 0 3.24 3.25\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"1.5\" /\u003e\n \u003c/svg\u003e\n \u003cspan className=\"search-pill-text\"\u003eFind something...\u003c/span\u003e\n \u003ckbd className=\"search-pill-kbd\"\u003e\u003cspan\u003e⌘\u003c/span\u003eK\u003c/kbd\u003e\n \u003c/button\u003e\n \u003cdiv style={{ flex: 1 }} /\u003e\n {/* ThemeToggle (added by docs-cur.2 — if not present yet, ignore) */}\n\u003c/div\u003e\n```\n\nAdd the SearchDialog component at the END of the JSX, right before the closing `\u003c/\u003e` fragment:\n```jsx\n\u003cSearchDialog isOpen={searchOpen} onClose={() =\u003e setSearchOpen(false)} /\u003e\n```\n\n### 3. Add CSS for search components to globals.css\n\nAdd these styles at the end of the `/* ===== Header ===== */` section (after theme-toggle styles if present, otherwise after .hamburger-btn:hover):\n\n```css\n/* Search pill button */\n.search-pill {\n display: none;\n align-items: center;\n gap: 8px;\n height: 32px;\n padding: 0 12px 0 8px;\n background: var(--color-bg);\n border: 1px solid var(--color-border);\n border-radius: 9999px;\n cursor: pointer;\n font-size: 14px;\n color: var(--color-text-secondary);\n transition: border-color var(--transition-fast);\n white-space: nowrap;\n}\n\n.search-pill:hover {\n border-color: var(--color-border-strong);\n}\n\n.search-pill-icon {\n flex-shrink: 0;\n color: var(--color-text-secondary);\n}\n\n.search-pill-text {\n font-size: 13px;\n color: var(--color-text-secondary);\n}\n\n.search-pill-kbd {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n margin-left: 8px;\n font-size: 11px;\n font-family: var(--font-sans);\n color: oklch(0.55 0.01 260);\n pointer-events: none;\n}\n\n/* Show pill only on large screens */\n@media (min-width: 769px) {\n .search-pill {\n display: flex;\n }\n}\n```\n\nAdd a new section before the responsive section for search dialog styles:\n```css\n/* ===== Component: SearchDialog ===== */\n.search-overlay {\n position: fixed;\n inset: 0;\n z-index: 200;\n background: oklch(0 0 0 / 0.4);\n display: flex;\n align-items: flex-start;\n justify-content: center;\n padding-top: 15vh;\n}\n\nhtml.dark .search-overlay {\n background: oklch(0 0 0 / 0.6);\n}\n\n.search-dialog {\n width: 90%;\n max-width: 560px;\n max-height: 60vh;\n background: var(--color-bg);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-xl);\n box-shadow: 0 16px 70px oklch(0 0 0 / 0.2);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n}\n\n.search-input-wrapper {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 16px;\n border-bottom: 1px solid var(--color-border);\n}\n\n.search-input-icon {\n flex-shrink: 0;\n color: var(--color-text-secondary);\n}\n\n.search-input {\n flex: 1;\n border: none;\n background: none;\n outline: none;\n font-size: 16px;\n font-family: var(--font-sans);\n color: var(--color-text-primary);\n}\n\n.search-input::placeholder {\n color: var(--color-text-secondary);\n}\n\n.search-input-kbd {\n font-size: 11px;\n font-family: var(--font-mono);\n color: var(--color-text-secondary);\n background: var(--color-bg-subtle);\n border: 1px solid var(--color-border);\n border-radius: 4px;\n padding: 2px 6px;\n line-height: 1;\n}\n\n.search-results {\n overflow-y: auto;\n max-height: calc(60vh - 52px);\n padding: 8px;\n}\n\n.search-results-list {\n list-style: none;\n padding: 0;\n margin: 0;\n}\n\n.search-result-item {\n display: flex;\n flex-direction: column;\n width: 100%;\n padding: 8px 12px;\n background: none;\n border: none;\n border-radius: var(--radius-md);\n cursor: pointer;\n text-align: left;\n font-family: var(--font-sans);\n transition: background var(--transition-fast);\n}\n\n.search-result-item:hover {\n background: var(--hover-bg);\n}\n\n.search-result-title {\n font-size: 14px;\n font-weight: 500;\n color: var(--color-text-heading);\n}\n\n.search-result-path {\n font-size: 12px;\n color: var(--color-text-secondary);\n font-family: var(--font-mono);\n margin-top: 2px;\n}\n\n.search-no-results {\n padding: 24px;\n text-align: center;\n font-size: 14px;\n color: var(--color-text-secondary);\n}\n```\n\n## Don't\n- Do NOT install any search library (Algolia, FlexSearch, etc.) — use simple substring matching on page titles from navigation.js\n- Do NOT use Tailwind\n- Do NOT modify Sidebar.js, _app.js, or any markdoc files\n- Do NOT add the search pill inside the mobile hamburger menu — it's only in the header\n- Do NOT create any API routes — this is fully client-side","acceptance_criteria":"1. SearchDialog.js exists at documentation/components/SearchDialog.js\n2. A pill-shaped 'Find something...' button appears in the header on screens \u003e= 769px\n3. Clicking the pill or pressing ⌘K (Mac) / Ctrl+K (Windows) opens a centered modal dialog\n4. Typing in the dialog filters pages from navigation.js by case-insensitive title substring\n5. Clicking a result navigates to that page and closes the dialog\n6. Pressing Enter navigates to the first result\n7. Pressing Escape or clicking the overlay closes the dialog\n8. The search dialog is dark-mode-aware (uses CSS variables)\n9. npm run build -- --webpack succeeds without errors","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":45,"created_at":"2026-02-20T19:21:12.221001+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:32:28.903279+08:00","closed_at":"2026-02-20T19:32:28.903279+08:00","close_reason":"1bb28fe Add search pill button and SearchDialog component","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-cur.3","depends_on_id":"docs-cur.2","type":"blocks","created_at":"2026-02-20T19:21:12.223132+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-cur.3","depends_on_id":"docs-cur","type":"parent-child","created_at":"2026-02-20T19:21:12.221976+08:00","created_by":"einstein.climateai.org"}]} +{"id":"docs-cur.4","title":"Integration: verify dark mode and search build and render correctly","description":"## Files\n- All files from docs-cur.1, docs-cur.2, docs-cur.3 (verify only)\n\n## What to do\n\nRun the full build and verify all dark mode + search features work together:\n\n1. `npm run build -- --webpack` must succeed with zero errors\n2. Verify dark mode tokens are applied correctly by checking CSS\n3. Verify ThemeToggle exists and is imported in Layout.js\n4. Verify SearchDialog exists and is imported in Layout.js\n5. Verify search pill button markup exists in Layout.js header\n6. Verify no duplicate spacer divs or broken header layout\n\nIf any of these checks fail, fix the issue in the relevant file(s). Common issues:\n- Merge conflict artifacts in Layout.js (both docs-cur.2 and docs-cur.3 modify it)\n- Missing import statements\n- Duplicate CSS rules in globals.css\n- FOUC script missing from Head\n\n## Don't\n- Do NOT add new features — this is verification and conflict resolution only\n- Do NOT redesign any component's appearance\n- Do NOT use Tailwind","acceptance_criteria":"1. npm run build -- --webpack succeeds with zero errors\n2. Layout.js imports and renders both ThemeToggle and SearchDialog\n3. Layout.js has the FOUC prevention script in Head\n4. globals.css has the dark mode section with html.dark tokens\n5. globals.css has search pill and search dialog CSS\n6. No merge conflict markers (\u003c\u003c\u003c\u003c\u003c\u003c\u003c etc.) in any file\n7. Header contains: hamburger, logo, search pill, spacer, theme toggle (in that order)","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":15,"created_at":"2026-02-20T19:21:29.21361+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:34:15.1468+08:00","closed_at":"2026-02-20T19:34:15.1468+08:00","close_reason":"3d39aeb integration verified: build passes, dark mode + search render correctly, no conflicts","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-cur.4","depends_on_id":"docs-cur.1","type":"blocks","created_at":"2026-02-20T19:21:29.216033+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-cur.4","depends_on_id":"docs-cur.2","type":"blocks","created_at":"2026-02-20T19:21:29.216899+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-cur.4","depends_on_id":"docs-cur.3","type":"blocks","created_at":"2026-02-20T19:21:29.21772+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-cur.4","depends_on_id":"docs-cur","type":"parent-child","created_at":"2026-02-20T19:21:29.214831+08:00","created_by":"einstein.climateai.org"}]} +{"id":"docs-d4r","title":"Epic 8: Build Configuration and Vercel Deployment","description":"## Summary\nVerify the Next.js build pipeline works end-to-end, fix any build errors, and configure Vercel deployment for the documentation site.\n\n## Context\nThis project is a Next.js + Markdoc documentation site in the `documentation/` directory inside the repository at `/Users/sharfy/Code/hypercerts-atproto-documentation/documentation/`. All commands should be run from the `documentation/` directory.\n\nBy the time this epic runs, Epics 1-7 have:\n- Scaffolded the Next.js + Markdoc project (Epic 1)\n- Created custom Markdoc tags and React components (Epic 2)\n- Migrated all 17 content files to `pages/` (Epic 3)\n- Built the navigation and layout system (Epic 4)\n- Applied Stripe-inspired styling (Epic 5)\n- Moved images to `public/images/` (Epic 6)\n- Fixed all broken links (Epic 7)\n\n## Tasks\n\n### 1. Verify Local Development Server\n```bash\ncd documentation\nnpm run dev\n```\n- Open http://localhost:3000 in a browser\n- Navigate through ALL pages via the sidebar and verify:\n - Pages load without console errors\n - Images display correctly\n - Tables render with proper styling\n - Callout boxes display with colored left borders\n - Column layouts work (side by side on desktop)\n - Sidebar navigation highlights the current page\n - Right-side table of contents shows headings\n - Prev/next pagination links work\n- Fix any runtime errors or warnings\n\n### 2. Verify Production Build\n```bash\ncd documentation\nnpm run build\n```\n- Ensure the build completes with ZERO errors\n- Common issues to watch for:\n - Missing component imports\n - Invalid Markdoc tag syntax in .md files\n - Broken internal links that cause 404s during static generation\n - CSS module import errors\n- Fix any build-time errors before proceeding\n\n### 3. Test Production Server Locally\n```bash\ncd documentation\nnpm run start\n```\n- Verify the production build serves correctly at http://localhost:3000\n- Spot-check 3-5 pages for rendering issues\n- Verify images load (they should be in `public/images/`)\n\n### 4. Configure Static Export (if desired)\nFor pure static hosting, add `output: 'export'` to `next.config.js`:\n```js\nconst withMarkdoc = require('@markdoc/next.js');\nmodule.exports = withMarkdoc({ mode: 'static' })({\n output: 'export',\n pageExtensions: ['md', 'mdoc', 'js', 'jsx', 'ts', 'tsx']\n});\n```\nThen `npm run build` will output static files to the `out/` directory.\n\n### 5. Verify .gitignore\nEnsure `documentation/.gitignore` contains:\n```\nnode_modules/\n.next/\nout/\n```\n\n### 6. Prepare for Vercel Deployment\nCreate or verify that the project is ready for Vercel:\n- The `documentation/` directory should be self-contained with its own `package.json`\n- `npm run build` must succeed\n- The Vercel project settings should be:\n - **Root Directory:** `documentation`\n - **Framework Preset:** Next.js (auto-detected)\n - **Build Command:** `npm run build` (or auto-detected)\n - **Output Directory:** `.next` (or `out` if using static export)\n\nNote: Actually connecting the GitHub repo to Vercel and deploying is a manual step that requires Vercel account access. This epic should ensure the project is deployment-ready.\n\n## Acceptance Criteria\n- `npm run dev` starts without errors and all 17 pages render correctly\n- `npm run build` completes with zero errors\n- `npm run start` serves the production build correctly\n- `.gitignore` excludes `node_modules/`, `.next/`, `out/`\n- All pages, images, tables, callouts, columns, sidebar navigation, and pagination work correctly\n- The project is ready to be deployed to Vercel (self-contained `documentation/` directory with valid `package.json` and build scripts)\n","status":"closed","priority":2,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-13T13:45:29.358726+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T13:19:10.831281+13:00","closed_at":"2026-02-14T13:19:10.831287+13:00","dependencies":[{"issue_id":"docs-d4r","depends_on_id":"docs-7b4","type":"blocks","created_at":"2026-02-13T13:45:29.366821+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-d4r","depends_on_id":"docs-c34","type":"blocks","created_at":"2026-02-13T13:45:29.360892+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-d4r","depends_on_id":"docs-vbw","type":"blocks","created_at":"2026-02-13T13:45:29.363352+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-d4r","depends_on_id":"docs-qsc","type":"blocks","created_at":"2026-02-13T13:45:29.364147+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-d4r","depends_on_id":"docs-7dv","type":"blocks","created_at":"2026-02-13T13:45:29.364846+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-d4r","depends_on_id":"docs-yne","type":"blocks","created_at":"2026-02-13T13:45:29.365508+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-d4r","depends_on_id":"docs-cij","type":"blocks","created_at":"2026-02-13T13:45:29.366168+13:00","created_by":"Sharfy Adamantine"}]} {"id":"docs-dfq","title":"Epic 10: Stripe Design Language Refinements","description":"## Goal\n\nRefine the Hypercerts documentation site to match Stripe's documentation design language — not just CSS tokens but the full design language: layout patterns, content presentation, interaction design, and structural elements.\n\n## Context\n\nAll 9 migration epics are complete. The site builds and exports correctly. The current design has the right tokens (colors, fonts, spacing) but lacks the polish and interaction patterns that make Stripe docs feel premium:\n\n1. No custom scrollbar styling — browser defaults look chunky\n2. No heading anchor links — can't easily copy section links\n3. Content area is centered with `margin: 0 auto` — looks asymmetric with sidebar + TOC\n4. `\u003cstrong\u003e` / `\u003cb\u003e` needs darker color and weight 700\n5. Links use `text-decoration: underline` on hover — Stripe uses bottom-border approach\n6. First H2 has too much top margin after H1\n7. Header is too plain — just 'Hypercerts Protocol' text\n8. No smooth transitions on hover states\n9. Landing page lacks card-style navigation links\n10. No breadcrumb navigation\n\n## Acceptance Criteria\n\n- `npm run build --prefix documentation` exits 0\n- All 11 design refinements implemented\n- No regressions to existing content rendering\n- Site remains fully static-exportable\n\n## Technical Notes\n\n- Project uses Next.js 16 + @markdoc/next.js with `--webpack` flag\n- CSS is in `documentation/styles/globals.css` (654 lines)\n- Components in `documentation/components/`\n- Markdoc nodes in `documentation/markdoc/nodes/` (currently empty)\n- `documentation/pages/_app.js` registers Markdoc components","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T14:23:44.58559+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T20:10:10.597195+13:00","closed_at":"2026-02-14T20:10:10.597195+13:00","close_reason":"Closed"} {"id":"docs-dfq.1","title":"Custom scrollbar styling for sidebar, TOC, and code blocks","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nAdd custom thin scrollbar styling throughout the site, matching Stripe docs pattern:\n\n1. Add a global scrollbar utility section in globals.css after the Reset section (around line 9).\n\n2. **Sidebar scrollbar** (`.sidebar`): \n - Width: 6px\n - Track: transparent\n - Thumb: `rgba(0,0,0,0.15)` with border-radius 3px\n - Thumb on hover: `rgba(0,0,0,0.25)`\n - Auto-hide behavior: set thumb to transparent by default, show on `.sidebar:hover` (use `::-webkit-scrollbar-thumb` with transition workaround)\n - Firefox: `scrollbar-width: thin; scrollbar-color: transparent transparent;` by default, `scrollbar-color: rgba(0,0,0,0.15) transparent;` on hover\n\n3. **TOC scrollbar** (`.toc`):\n - Same styling as sidebar\n\n4. **Code block scrollbar** (`.layout-content pre`):\n - Horizontal scrollbar only (already has `overflow-x: auto`)\n - Height: 6px\n - Track: transparent \n - Thumb: `rgba(0,0,0,0.12)` with border-radius 3px\n - Always visible when content overflows (no auto-hide needed)\n\n5. Use both `-webkit-scrollbar` (Chrome/Safari/Edge) and `scrollbar-width`/`scrollbar-color` (Firefox) properties.\n\n## CSS to add (insert after line 8, before Design Tokens):\n\n```css\n/* ===== Scrollbar Styling ===== */\n/* Webkit (Chrome, Safari, Edge) */\n.sidebar::-webkit-scrollbar,\n.toc::-webkit-scrollbar {\n width: 6px;\n}\n\n.sidebar::-webkit-scrollbar-track,\n.toc::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.sidebar::-webkit-scrollbar-thumb,\n.toc::-webkit-scrollbar-thumb {\n background: transparent;\n border-radius: 3px;\n}\n\n.sidebar:hover::-webkit-scrollbar-thumb,\n.toc:hover::-webkit-scrollbar-thumb {\n background: rgba(0, 0, 0, 0.15);\n}\n\n.sidebar::-webkit-scrollbar-thumb:hover,\n.toc::-webkit-scrollbar-thumb:hover {\n background: rgba(0, 0, 0, 0.25);\n}\n\n/* Code block horizontal scrollbar */\n.layout-content pre::-webkit-scrollbar {\n height: 6px;\n}\n\n.layout-content pre::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.layout-content pre::-webkit-scrollbar-thumb {\n background: rgba(0, 0, 0, 0.12);\n border-radius: 3px;\n}\n\n.layout-content pre::-webkit-scrollbar-thumb:hover {\n background: rgba(0, 0, 0, 0.2);\n}\n\n/* Firefox */\n.sidebar,\n.toc {\n scrollbar-width: thin;\n scrollbar-color: transparent transparent;\n}\n\n.sidebar:hover,\n.toc:hover {\n scrollbar-color: rgba(0, 0, 0, 0.15) transparent;\n}\n\n.layout-content pre {\n scrollbar-width: thin;\n scrollbar-color: rgba(0, 0, 0, 0.12) transparent;\n}\n```\n\n## Edge cases\n- Do NOT remove or modify existing `overflow-y: auto` on `.sidebar` or `overflow-x: auto` on `.layout-content pre`\n- Do NOT add scrollbar styles to the main body/html element\n- The Firefox `scrollbar-color` properties on `.sidebar` and `.toc` must be added to the EXISTING rule blocks (merge with existing selectors), OR placed after them so they cascade correctly. Since `.sidebar` already has styles at line 162, add the Firefox properties there OR use the new scrollbar section with higher specificity.\n\n## Test\n```bash\ncd documentation \u0026\u0026 npm run build\n```\nMust exit 0. Verify visually that scrollbar styles are present in the built CSS.\n\n## Dont\n- Do not modify any JavaScript files\n- Do not change any existing CSS properties — only ADD new scrollbar-related properties\n- Do not use JavaScript-based scrollbar libraries","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T14:24:20.195548+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T20:03:50.061126+13:00","closed_at":"2026-02-14T20:03:50.061126+13:00","close_reason":"Added custom scrollbar styling for sidebar, TOC, and code blocks. Build successful.","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-dfq.1","depends_on_id":"docs-dfq","type":"parent-child","created_at":"2026-02-14T14:24:20.196559+13:00","created_by":"Sharfy Adamantine"}]} {"id":"docs-dfq.2","title":"Heading anchor links on hover with Markdoc node override","description":"## Files\n- documentation/markdoc/nodes/heading.markdoc.js (create)\n- documentation/components/Heading.js (create)\n- documentation/pages/_app.js (modify)\n- documentation/styles/globals.css (modify)\n\n## What to do\nAdd clickable anchor links (#) that appear on hover next to H2 and H3 headings, matching Stripe docs pattern. This requires a Markdoc heading node override.\n\n### 1. Create `documentation/markdoc/nodes/heading.markdoc.js`\n\nThis file tells @markdoc/next.js to use a custom component for heading nodes:\n\n```js\nimport { nodes } from \"@markdoc/markdoc\";\n\nconst heading = {\n ...nodes.heading,\n render: \"Heading\",\n};\n\nexport default heading;\n```\n\n### 2. Create `documentation/components/Heading.js`\n\nThis React component renders headings with auto-generated IDs and hover anchor links:\n\n```jsx\nimport React from \"react\";\n\nfunction generateId(children) {\n const text = React.Children.toArray(children)\n .filter((child) =\u003e typeof child === \"string\")\n .join(\"\");\n return text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/(^-|-$)/g, \"\");\n}\n\nexport function Heading({ level = 2, children, id }) {\n const Tag = `h${level}`;\n const headingId = id || generateId(children);\n\n return (\n \u003cTag id={headingId} className=\"heading-anchor-target\"\u003e\n {children}\n {(level === 2 || level === 3) \u0026\u0026 (\n \u003ca\n href={`#${headingId}`}\n className=\"heading-anchor\"\n aria-label={`Link to this section`}\n onClick={(e) =\u003e {\n e.preventDefault();\n const el = document.getElementById(headingId);\n if (el) {\n el.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\n history.pushState(null, \"\", `#${headingId}`);\n }\n }}\n \u003e\n #\n \u003c/a\u003e\n )}\n \u003c/Tag\u003e\n );\n}\n```\n\n### 3. Modify `documentation/pages/_app.js`\n\nImport the Heading component and register it in the components object:\n\nAdd this import at the top:\n```js\nimport { Heading } from \"../components/Heading\";\n```\n\nAdd `Heading` to the components object:\n```js\nconst components = {\n Callout,\n Columns,\n Column,\n Figure,\n Heading,\n};\n```\n\n### 4. Add CSS to `documentation/styles/globals.css`\n\nAdd these styles in the Typography section (after the existing heading styles, around line 416):\n\n```css\n/* Heading anchor links */\n.heading-anchor-target {\n position: relative;\n}\n\n.heading-anchor {\n position: absolute;\n left: -1.2em;\n top: 50%;\n transform: translateY(-50%);\n color: var(--color-text-secondary);\n text-decoration: none;\n font-weight: 400;\n opacity: 0;\n transition: opacity 0.15s ease;\n font-size: 0.85em;\n}\n\n.heading-anchor:hover {\n color: var(--color-link);\n text-decoration: none;\n}\n\n.heading-anchor-target:hover .heading-anchor {\n opacity: 1;\n}\n```\n\n## Edge cases\n- The `generateId` function must handle children that are React elements (not just strings). Use `React.Children.toArray` and filter for strings.\n- H1 headings should NOT get anchor links (only H2 and H3).\n- H4, H5, H6 should render normally without anchor links.\n- The heading ID generation must match the existing ID generation in `TableOfContents.js` (same algorithm: lowercase, replace non-alphanumeric with hyphens, trim hyphens). The TOC component reads IDs from the DOM, so as long as the Heading component sets `id` on the element, it will work.\n- The `id` prop may be passed by Markdoc if the markdown has `{% heading id=\"custom-id\" %}` — respect it if provided.\n\n## Test\n```bash\ncd documentation \u0026\u0026 npm run build\n```\nMust exit 0. Additionally verify:\n```bash\ngrep -r \"heading-anchor\" documentation/styles/globals.css \u0026\u0026 echo \"CSS OK\"\ngrep -r \"Heading\" documentation/components/Heading.js \u0026\u0026 echo \"Component OK\"\ngrep -r \"heading\" documentation/markdoc/nodes/heading.markdoc.js \u0026\u0026 echo \"Node OK\"\ngrep \"Heading\" documentation/pages/_app.js \u0026\u0026 echo \"Registration OK\"\n```\nAll four greps must produce output.\n\n## Dont\n- Do not modify TableOfContents.js — it reads IDs from the DOM and will continue to work\n- Do not add anchor links to H1 headings\n- Do not use any external libraries\n- Do not change the heading ID generation algorithm (must remain compatible with TOC scroll spy)","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T14:24:42.488252+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T20:03:48.382557+13:00","closed_at":"2026-02-14T20:03:48.382557+13:00","close_reason":"219b041 Add heading anchor links on hover - implemented Markdoc heading node override with auto-generated IDs and hover anchor links for H2/H3","labels":["scope:small"],"dependencies":[{"issue_id":"docs-dfq.2","depends_on_id":"docs-dfq","type":"parent-child","created_at":"2026-02-14T14:24:42.502369+13:00","created_by":"Sharfy Adamantine"}]} @@ -49,10 +49,10 @@ {"id":"docs-dfq.5","title":"Enhanced header with breadcrumbs and visual refinement","description":"## Files\n- documentation/components/Layout.js (modify)\n- documentation/components/Breadcrumbs.js (create)\n- documentation/styles/globals.css (modify)\n\n## What to do\nEnhance the header to be more visually refined like Stripe docs. Add breadcrumb navigation below the header, and improve the header visual treatment.\n\n### 1. Create `documentation/components/Breadcrumbs.js`\n\nA breadcrumb component that shows the current page location based on the navigation tree:\n\n```jsx\nimport Link from \"next/link\";\nimport { useRouter } from \"next/router\";\nimport { navigation } from \"../lib/navigation\";\n\nfunction findBreadcrumbs(nav, targetPath, trail = []) {\n for (const item of nav) {\n if (item.section) {\n const result = findBreadcrumbs(item.children || [], targetPath, [\n ...trail,\n { title: item.section },\n ]);\n if (result) return result;\n } else {\n if (item.path === targetPath) {\n return [...trail, { title: item.title, path: item.path }];\n }\n if (item.children) {\n const result = findBreadcrumbs(item.children, targetPath, [\n ...trail,\n { title: item.title, path: item.path },\n ]);\n if (result) return result;\n }\n }\n }\n return null;\n}\n\nexport function Breadcrumbs() {\n const router = useRouter();\n const currentPath = router.asPath.split(\"#\")[0].split(\"?\")[0];\n\n // Dont show breadcrumbs on home page\n if (currentPath === \"/\") return null;\n\n const crumbs = findBreadcrumbs(navigation, currentPath) || [];\n\n if (crumbs.length \u003c= 1) return null;\n\n return (\n \u003cnav className=\"breadcrumbs\" aria-label=\"Breadcrumb\"\u003e\n \u003col className=\"breadcrumbs-list\"\u003e\n \u003cli className=\"breadcrumbs-item\"\u003e\n \u003cLink href=\"/\" className=\"breadcrumbs-link\"\u003e\n Docs\n \u003c/Link\u003e\n \u003c/li\u003e\n {crumbs.slice(0, -1).map((crumb, i) =\u003e (\n \u003cli key={i} className=\"breadcrumbs-item\"\u003e\n \u003cspan className=\"breadcrumbs-separator\"\u003e/\u003c/span\u003e\n {crumb.path ? (\n \u003cLink href={crumb.path} className=\"breadcrumbs-link\"\u003e\n {crumb.title}\n \u003c/Link\u003e\n ) : (\n \u003cspan className=\"breadcrumbs-text\"\u003e{crumb.title}\u003c/span\u003e\n )}\n \u003c/li\u003e\n ))}\n \u003cli className=\"breadcrumbs-item\"\u003e\n \u003cspan className=\"breadcrumbs-separator\"\u003e/\u003c/span\u003e\n \u003cspan className=\"breadcrumbs-current\"\u003e{crumbs[crumbs.length - 1].title}\u003c/span\u003e\n \u003c/li\u003e\n \u003c/ol\u003e\n \u003c/nav\u003e\n );\n}\n```\n\n### 2. Modify `documentation/components/Layout.js`\n\nAdd the Breadcrumbs component import and render it inside the `\u003cmain\u003e` element, before the `\u003carticle\u003e`:\n\nAdd import at top:\n```js\nimport { Breadcrumbs } from \"./Breadcrumbs\";\n```\n\nInside the JSX, change the `\u003cmain\u003e` section from:\n```jsx\n\u003cmain className=\"layout-content\"\u003e\n \u003carticle\u003e{children}\u003c/article\u003e\n```\n\nTo:\n```jsx\n\u003cmain className=\"layout-content\"\u003e\n \u003cBreadcrumbs /\u003e\n \u003carticle\u003e{children}\u003c/article\u003e\n```\n\n### 3. Add CSS to `documentation/styles/globals.css`\n\nAdd a new section after the Header styles (after line ~153) for breadcrumbs:\n\n```css\n/* ===== Breadcrumbs ===== */\n.breadcrumbs {\n margin-bottom: var(--space-4);\n}\n\n.breadcrumbs-list {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n list-style: none;\n padding: 0;\n margin: 0;\n font-size: 13px;\n line-height: 1.4;\n}\n\n.breadcrumbs-item {\n display: flex;\n align-items: center;\n}\n\n.breadcrumbs-separator {\n margin: 0 6px;\n color: var(--color-text-secondary);\n font-size: 12px;\n}\n\n.breadcrumbs-link {\n color: var(--color-text-secondary);\n text-decoration: none;\n transition: color 0.15s ease;\n}\n\n.breadcrumbs-link:hover {\n color: var(--color-link);\n text-decoration: none;\n}\n\n.breadcrumbs-text {\n color: var(--color-text-secondary);\n}\n\n.breadcrumbs-current {\n color: var(--color-text-primary);\n font-weight: 500;\n}\n```\n\nAlso enhance the header visual treatment. Add a subtle backdrop blur to the header. Modify the existing `.layout-header` rule (line 110-117):\n\nChange FROM:\n```css\n.layout-header {\n position: sticky;\n top: 0;\n z-index: 100;\n height: var(--header-height);\n background: var(--color-bg);\n border-bottom: 1px solid var(--color-border);\n}\n```\n\nChange TO:\n```css\n.layout-header {\n position: sticky;\n top: 0;\n z-index: 100;\n height: var(--header-height);\n background: rgba(255, 255, 255, 0.95);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n border-bottom: 1px solid var(--color-border);\n}\n```\n\n## Test\n```bash\ncd documentation \u0026\u0026 npm run build\n```\nMust exit 0. Additionally:\n```bash\ntest -f documentation/components/Breadcrumbs.js \u0026\u0026 echo \"Breadcrumbs component OK\"\ngrep \"Breadcrumbs\" documentation/components/Layout.js \u0026\u0026 echo \"Layout import OK\"\ngrep \"breadcrumbs\" documentation/styles/globals.css \u0026\u0026 echo \"CSS OK\"\ngrep \"backdrop-filter\" documentation/styles/globals.css \u0026\u0026 echo \"Header blur OK\"\n```\nAll four checks must pass.\n\n## Dont\n- Do not modify the header height or padding\n- Do not add a search bar (out of scope for this task)\n- Do not modify navigation.js\n- Do not modify Sidebar.js or TableOfContents.js\n- Do not change the logo text or link","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T14:25:33.805204+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T20:03:55.98582+13:00","closed_at":"2026-02-14T20:03:55.98582+13:00","close_reason":"77ec8dc Enhanced header with breadcrumbs and visual refinement","labels":["scope:small"],"dependencies":[{"issue_id":"docs-dfq.5","depends_on_id":"docs-dfq","type":"parent-child","created_at":"2026-02-14T14:25:33.806485+13:00","created_by":"Sharfy Adamantine"}]} {"id":"docs-dfq.6","title":"Transition and hover state polish","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nAdd smooth transitions and refined hover states throughout the site, matching Stripe docs interaction patterns. All changes are CSS-only.\n\n### 1. Global transition timing\nAdd a CSS custom property for the standard transition in the `:root` design tokens section (around line 69, before the closing `}`):\n\n```css\n --transition-fast: 150ms ease;\n --transition-normal: 200ms cubic-bezier(0, 0.09, 0.4, 1);\n```\n\n### 2. Sidebar link transitions\nUpdate the existing `.sidebar-link` rule (line ~214-225). Add/modify the transition property:\n\nChange FROM:\n```css\n transition: background 0.1s ease;\n```\n\nChange TO:\n```css\n transition: background var(--transition-fast), color var(--transition-fast);\n```\n\n### 3. Sidebar active indicator\nAdd a left border indicator for the active sidebar link. Modify `.sidebar-link-active` (line ~233-237):\n\nChange FROM:\n```css\n.sidebar-link-active {\n font-weight: 600;\n color: var(--color-text-title);\n background: var(--hover-bg);\n}\n```\n\nChange TO:\n```css\n.sidebar-link-active {\n font-weight: 600;\n color: var(--color-link);\n background: var(--active-bg);\n border-left: 2px solid var(--color-link);\n padding-left: calc(var(--space-3) - 2px);\n}\n```\n\nAlso update `a.sidebar-link-active:hover` (line ~239-242):\n\nChange FROM:\n```css\na.sidebar-link-active:hover {\n background: var(--hover-bg);\n color: var(--color-text-title);\n}\n```\n\nChange TO:\n```css\na.sidebar-link-active:hover {\n background: var(--active-bg);\n color: var(--color-link);\n}\n```\n\n### 4. Pagination hover enhancement\nUpdate the `.pagination-link` transition (line ~354):\n\nChange FROM:\n```css\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n```\n\nChange TO:\n```css\n transition: border-color var(--transition-normal), box-shadow var(--transition-normal), transform var(--transition-normal);\n```\n\nAdd a subtle lift on hover. Update `.pagination-link:hover` (line ~357-361):\n\nChange FROM:\n```css\n.pagination-link:hover {\n border-color: var(--color-link);\n box-shadow: 0 1px 4px rgba(5, 112, 222, 0.08);\n text-decoration: none;\n}\n```\n\nChange TO:\n```css\n.pagination-link:hover {\n border-color: var(--color-link);\n box-shadow: 0 2px 8px rgba(5, 112, 222, 0.12);\n text-decoration: none;\n transform: translateY(-1px);\n}\n```\n\n### 5. TOC link transitions\nUpdate the `.toc-link` transition (line ~315):\n\nChange FROM:\n```css\n transition: color 0.15s ease, border-color 0.15s ease;\n```\n\nChange TO:\n```css\n transition: color var(--transition-fast), border-color var(--transition-fast);\n```\n\n### 6. Callout subtle left border animation\nAdd a transition to callout on hover:\n\n```css\n.callout {\n transition: box-shadow var(--transition-normal);\n}\n\n.callout:hover {\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);\n}\n```\n\nAdd these rules after the existing `.callout` rule (line ~533-537). Do NOT modify the existing `.callout` rule — add the transition property to a NEW `.callout` rule block that will merge via cascade, OR add the `transition` property directly into the existing `.callout` block.\n\n## Test\n```bash\ncd documentation \u0026\u0026 npm run build\n```\nMust exit 0. Additionally:\n```bash\ngrep \"transition-fast\" documentation/styles/globals.css \u0026\u0026 echo \"Tokens OK\"\ngrep \"transition-normal\" documentation/styles/globals.css \u0026\u0026 echo \"Tokens OK\"\ngrep \"translateY\" documentation/styles/globals.css \u0026\u0026 echo \"Pagination lift OK\"\ngrep \"border-left.*solid.*color-link\" documentation/styles/globals.css \u0026\u0026 echo \"Active indicator OK\"\n```\nAll four checks must pass.\n\n## Dont\n- Do not add page transition animations (route changes) — that requires JavaScript\n- Do not modify any JavaScript files\n- Do not change colors or font sizes\n- Do not add animation keyframes — only use CSS transitions\n- Do not modify the mobile responsive section","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T14:25:54.998064+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T20:03:35.256599+13:00","closed_at":"2026-02-14T20:03:35.256599+13:00","close_reason":"Implemented all transition and hover state polish: added transition timing variables, updated sidebar/TOC/pagination transitions, added active indicator with border-left, and callout hover effects","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-dfq.6","depends_on_id":"docs-dfq","type":"parent-child","created_at":"2026-02-14T14:25:54.998941+13:00","created_by":"Sharfy Adamantine"}]} {"id":"docs-dfq.7","title":"Landing page card-style navigation links","description":"## Files\n- documentation/pages/index.md (modify)\n- documentation/components/CardLink.js (create)\n- documentation/markdoc/tags/card-link.markdoc.js (create)\n- documentation/pages/_app.js (modify)\n- documentation/styles/globals.css (modify)\n\n## What to do\nAdd card-style navigation links to the landing page, similar to Stripe docs landing pages that have categorized link cards with titles, descriptions, and hover effects.\n\n### 1. Create `documentation/components/CardLink.js`\n\nA card component for navigation links:\n\n```jsx\nimport Link from \"next/link\";\n\nexport function CardLink({ title, href, children }) {\n return (\n \u003cLink href={href} className=\"card-link\"\u003e\n \u003cspan className=\"card-link-title\"\u003e{title}\u003c/span\u003e\n {children \u0026\u0026 \u003cspan className=\"card-link-desc\"\u003e{children}\u003c/span\u003e}\n \u003cspan className=\"card-link-arrow\"\u003e→\u003c/span\u003e\n \u003c/Link\u003e\n );\n}\n```\n\n### 2. Create `documentation/markdoc/tags/card-link.markdoc.js`\n\nRegister the CardLink as a Markdoc tag:\n\n```js\nexport default {\n render: \"CardLink\",\n attributes: {\n title: { type: String, required: true },\n href: { type: String, required: true },\n },\n};\n```\n\n### 3. Modify `documentation/pages/_app.js`\n\nAdd import and register:\n\n```js\nimport { CardLink } from \"../components/CardLink\";\n```\n\nAdd to components object:\n```js\nconst components = {\n Callout,\n Columns,\n Column,\n Figure,\n Heading, // (if heading task is done, otherwise omit)\n CardLink,\n};\n```\n\n**IMPORTANT**: The components object may already contain `Heading` if task docs-dfq.2 was completed first. If `Heading` is already there, keep it. If not, do NOT add it — only add `CardLink`.\n\n### 4. Add CSS to `documentation/styles/globals.css`\n\nAdd a new section after the Figure component styles (around line ~597):\n\n```css\n/* ===== Component: CardLink ===== */\n.card-link {\n display: block;\n padding: var(--space-4) var(--space-5);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-lg);\n text-decoration: none;\n transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;\n position: relative;\n margin-bottom: var(--space-3);\n}\n\n.card-link:hover {\n border-color: var(--color-link);\n box-shadow: 0 2px 8px rgba(5, 112, 222, 0.1);\n text-decoration: none;\n transform: translateY(-1px);\n}\n\n.card-link-title {\n display: block;\n font-size: 15px;\n font-weight: 600;\n color: var(--color-link);\n margin-bottom: var(--space-1);\n}\n\n.card-link-desc {\n display: block;\n font-size: 14px;\n color: var(--color-text-secondary);\n line-height: 1.5;\n}\n\n.card-link-arrow {\n position: absolute;\n right: var(--space-4);\n top: 50%;\n transform: translateY(-50%);\n color: var(--color-link);\n font-size: 16px;\n opacity: 0;\n transition: opacity 0.15s ease, transform 0.15s ease;\n}\n\n.card-link:hover .card-link-arrow {\n opacity: 1;\n transform: translateY(-50%) translateX(2px);\n}\n```\n\n### 5. Modify `documentation/pages/index.md`\n\nAdd a \"Quick Start\" section with card links after the existing content. Append to the end of the file:\n\n```markdown\n\n## Quick Start\n\n{% card-link title=\"Why We're Building Hypercerts\" href=\"/getting-started/why-were-building-hypercerts\" %}\nUnderstand the motivation behind the hypercerts protocol\n{% /card-link %}\n\n{% card-link title=\"Introduction to Impact Claims\" href=\"/getting-started/introduction-to-impact-claims\" %}\nLearn about impact claims and how they work\n{% /card-link %}\n\n{% card-link title=\"The Hypercerts Infrastructure\" href=\"/getting-started/the-hypercerts-infrastructure\" %}\nExplore the technical architecture\n{% /card-link %}\n\n{% card-link title=\"Introduction to Lexicons\" href=\"/lexicons/introduction-to-lexicons\" %}\nUnderstand the data schemas that power hypercerts\n{% /card-link %}\n```\n\n## Test\n```bash\ncd documentation \u0026\u0026 npm run build\n```\nMust exit 0. Additionally:\n```bash\ntest -f documentation/components/CardLink.js \u0026\u0026 echo \"Component OK\"\ntest -f documentation/markdoc/tags/card-link.markdoc.js \u0026\u0026 echo \"Tag OK\"\ngrep \"CardLink\" documentation/pages/_app.js \u0026\u0026 echo \"Registration OK\"\ngrep \"card-link\" documentation/styles/globals.css \u0026\u0026 echo \"CSS OK\"\ngrep \"card-link\" documentation/pages/index.md \u0026\u0026 echo \"Content OK\"\n```\nAll five checks must pass.\n\n## Dont\n- Do not modify existing content on the landing page — only append new content\n- Do not remove the existing columns/figures layout\n- Do not use any external icon libraries\n- Do not modify navigation.js\n- Do not add more than 4 card links","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T14:26:17.319286+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T20:07:08.03882+13:00","closed_at":"2026-02-14T20:07:08.03882+13:00","close_reason":"9969a9b Landing page card-style navigation links","labels":["scope:small"],"dependencies":[{"issue_id":"docs-dfq.7","depends_on_id":"docs-dfq","type":"parent-child","created_at":"2026-02-14T14:26:17.320306+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-dfq.8","title":"Integration: verify all refinements build and render correctly","description":"## Files\n- (no files to create/modify — verification only)\n\n## What to do\nAfter all other tasks in Epic 10 are complete, verify the full integration:\n\n1. Run `cd documentation \u0026\u0026 npm run build` — must exit 0 with no errors\n2. Verify all 17 pages are in the static export\n3. Spot-check that no CSS conflicts exist between the various refinements\n4. Verify the landing page renders with card links\n5. Verify breadcrumbs appear on non-home pages\n\n## Test\n```bash\ncd documentation \u0026\u0026 npm run build 2\u003e\u00261 | grep -c \"●\" | xargs test 17 -eq\n```\nMust show exactly 17 SSG pages.\n\n```bash\ncd documentation \u0026\u0026 npm run build\n```\nMust exit 0.\n\n## Dont\n- Do not modify any files\n- This is a verification-only task","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T14:26:25.677169+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T20:10:10.53275+13:00","closed_at":"2026-02-14T20:10:10.53275+13:00","close_reason":"Closed","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq","type":"parent-child","created_at":"2026-02-14T14:26:25.678222+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.1","type":"blocks","created_at":"2026-02-14T14:26:31.650675+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.2","type":"blocks","created_at":"2026-02-14T14:26:31.749901+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.3","type":"blocks","created_at":"2026-02-14T14:26:31.849858+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.4","type":"blocks","created_at":"2026-02-14T14:26:31.950473+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.5","type":"blocks","created_at":"2026-02-14T14:26:32.047374+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.6","type":"blocks","created_at":"2026-02-14T14:26:32.141096+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.7","type":"blocks","created_at":"2026-02-14T14:26:32.236529+13:00","created_by":"Sharfy Adamantine"}]} +{"id":"docs-dfq.8","title":"Integration: verify all refinements build and render correctly","description":"## Files\n- (no files to create/modify — verification only)\n\n## What to do\nAfter all other tasks in Epic 10 are complete, verify the full integration:\n\n1. Run `cd documentation \u0026\u0026 npm run build` — must exit 0 with no errors\n2. Verify all 17 pages are in the static export\n3. Spot-check that no CSS conflicts exist between the various refinements\n4. Verify the landing page renders with card links\n5. Verify breadcrumbs appear on non-home pages\n\n## Test\n```bash\ncd documentation \u0026\u0026 npm run build 2\u003e\u00261 | grep -c \"●\" | xargs test 17 -eq\n```\nMust show exactly 17 SSG pages.\n\n```bash\ncd documentation \u0026\u0026 npm run build\n```\nMust exit 0.\n\n## Dont\n- Do not modify any files\n- This is a verification-only task","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T14:26:25.677169+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T20:10:10.53275+13:00","closed_at":"2026-02-14T20:10:10.53275+13:00","close_reason":"Closed","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.1","type":"blocks","created_at":"2026-02-14T14:26:31.650675+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.3","type":"blocks","created_at":"2026-02-14T14:26:31.849858+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq","type":"parent-child","created_at":"2026-02-14T14:26:25.678222+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.2","type":"blocks","created_at":"2026-02-14T14:26:31.749901+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.4","type":"blocks","created_at":"2026-02-14T14:26:31.950473+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.5","type":"blocks","created_at":"2026-02-14T14:26:32.047374+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.6","type":"blocks","created_at":"2026-02-14T14:26:32.141096+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.7","type":"blocks","created_at":"2026-02-14T14:26:32.236529+13:00","created_by":"Sharfy Adamantine"}]} {"id":"docs-j3q","title":"Epic: Header \u0026 Accessibility","description":"Fix critical header UX issues: mobile search is invisible (\u003c769px), no top-level nav links, no skip-to-content link (WCAG 2.4.1), no visual group dividers, missing focus-visible styles, search pill lacks affordance. Polish: reduce height 64→56px, add GitHub icon. Covers UX findings #1, 2, 3, 8, 9, 10, 26, 27.","status":"in_progress","priority":1,"issue_type":"epic","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","created_at":"2026-02-20T19:52:34.66104+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:57:32.208545+08:00","labels":["scope:medium"]} {"id":"docs-j3q.1","title":"Mobile search icon + skip-to-content link","description":"## Files\n- components/Layout.js (modify)\n- styles/globals.css (modify)\n\n## What to do\n\n### Mobile search icon (finding #1)\nThe search pill is hidden below 769px with `display:none` and no fallback. Add a **search icon button** visible only on mobile (\u003c769px) that opens the SearchDialog.\n\nIn `Layout.js`, add a button AFTER the logo group and BEFORE `\u003cdiv style={{ flex: 1 }} /\u003e`:\n```jsx\n\u003cbutton\n className=\"search-icon-btn\"\n onClick={() =\u003e setSearchOpen(true)}\n type=\"button\"\n aria-label=\"Search\"\n\u003e\n \u003csvg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\"\u003e\n \u003cpath d=\"M12.01 12a4.25 4.25 0 1 0-6.02-6 4.25 4.25 0 0 0 6.02 6Zm0 0 3.24 3.25\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"1.5\" /\u003e\n \u003c/svg\u003e\n\u003c/button\u003e\n```\n\nCSS for `.search-icon-btn`:\n- `display: none` by default\n- Inside `@media (max-width: 768px)`: `display: flex`, same styling as `.hamburger-btn` (align-items center, justify-content center, background none, border none, cursor pointer, padding var(--space-1), color var(--color-text-primary), border-radius var(--radius-sm), min-width 44px, min-height 44px)\n- `:hover` → `background: var(--hover-bg)`\n- Place the mobile search icon button in the flex spacer area (after logo, before ThemeToggle)\n\n### Skip-to-content link (finding #3)\nAdd a visually-hidden skip link as the **very first child** inside `\u003cheader\u003e`, before `.layout-header-inner`:\n```jsx\n\u003ca href=\"#main-content\" className=\"skip-to-content\"\u003eSkip to content\u003c/a\u003e\n```\n\nAdd `id=\"main-content\"` to `\u003cmain className=\"layout-content\"\u003e`.\n\nCSS for `.skip-to-content`:\n- Position absolute, left -9999px (visually hidden)\n- On `:focus`: position static, display block, padding 8px 16px, background var(--color-accent), color white, text-decoration none, font-weight 600, font-size 14px, z-index 999, text-align center\n- Sits at the very top of the page when focused\n\n## Dont\n- Do NOT remove or modify the existing search pill behavior\n- Do NOT change the ⌘K keyboard shortcut\n- Do NOT remove the hamburger button","acceptance_criteria":"1. On mobile viewport (\u003c769px), a magnifying glass icon button is visible in the header\n2. Clicking the mobile search icon opens the SearchDialog modal\n3. The search pill remains visible on desktop (≥769px) and the mobile icon is hidden\n4. A \"Skip to content\" link is the first focusable element on the page\n5. Pressing Tab on page load focuses the skip link, pressing Enter scrolls to main content\n6. The skip link is visually hidden until focused\n7. Build passes: npm run build -- --webpack","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":45,"created_at":"2026-02-20T19:52:55.462643+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:06:32.660671+08:00","closed_at":"2026-02-20T20:06:32.660671+08:00","close_reason":"c617130 mobile search icon + skip-to-content link","labels":["scope:small"],"dependencies":[{"issue_id":"docs-j3q.1","depends_on_id":"docs-j3q","type":"parent-child","created_at":"2026-02-20T19:52:55.463614+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-j3q.2","title":"Top-level nav links + group dividers + search pill affordance","description":"## Files\n- components/Layout.js (modify)\n- styles/globals.css (modify)\n\n## What to do\n\n### Top-level nav links (finding #2)\nAdd navigation links between the logo and the search pill. In `Layout.js`, after the logo `\u003cLink\u003e` and before the search pill `\u003cbutton\u003e`, add a `\u003cnav\u003e` with class `header-nav`:\n\n```jsx\n\u003cnav className=\"header-nav\" aria-label=\"Main navigation\"\u003e\n \u003cLink href=\"/getting-started/quickstart\" className=\"header-nav-link\"\u003eDocs\u003c/Link\u003e\n \u003cLink href=\"/tools/scaffold\" className=\"header-nav-link\"\u003eTools\u003c/Link\u003e\n \u003ca href=\"https://github.com/gainforest/hypercerts\" className=\"header-nav-link\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eGitHub\u003c/a\u003e\n\u003c/nav\u003e\n```\n\nCSS for `.header-nav`:\n- `display: none` by default (hidden on mobile)\n- `@media (min-width: 769px)`: `display: flex; align-items: center; gap: var(--space-2);`\n- `.header-nav-link`: font-size 14px, font-weight 500, color var(--color-text-secondary), text-decoration none, padding 6px 12px, border-radius var(--radius-sm), transition background+color var(--transition-fast)\n- `.header-nav-link:hover`: background var(--hover-bg), color var(--color-text-primary), text-decoration none\n\n### Group dividers (finding #8)\nAdd thin vertical dividers between logo group, nav links, and search+toggle group. Use `\u003cspan className=\"header-divider\" aria-hidden=\"true\" /\u003e` elements in the header-inner, placed:\n1. After the logo `\u003cLink\u003e` (before nav)\n2. After the `\u003cnav\u003e` (before search pill)\n\nCSS for `.header-divider`:\n- `display: none` by default\n- `@media (min-width: 769px)`: display block, width 1px, height 20px, background var(--color-border), margin 0 var(--space-2)\n- Dark mode: `html.dark .header-divider { background: oklch(0.30 0.005 260); }`\n\n### Search pill affordance (finding #10)\nThe search pill blends into the glass header. Add a subtle shadow:\n- `.search-pill`: add `box-shadow: 0 1px 3px oklch(0 0 0 / 0.06);`\n- `.search-pill:hover`: `box-shadow: 0 1px 4px oklch(0 0 0 / 0.10);`\n- Dark mode: `html.dark .search-pill { box-shadow: 0 1px 3px oklch(0 0 0 / 0.2); }`\n\n## Dont\n- Do NOT add more than 3 nav links — keep it minimal\n- Do NOT hide the Docs badge on the logo\n- Do NOT change the flex:1 spacer behavior — nav links go between logo and spacer","acceptance_criteria":"1. On desktop (≥769px), \"Docs\", \"Tools\", \"GitHub\" links are visible between logo and search pill\n2. Nav links are hidden on mobile (\u003c769px)\n3. Thin vertical dividers (1px, 20px tall) separate logo | nav | search groups\n4. Dividers are hidden on mobile\n5. Search pill has a subtle box-shadow distinguishing it from the glass header\n6. Dark mode: dividers and search pill shadow adapt correctly\n7. GitHub link opens in new tab with rel=\"noopener noreferrer\"\n8. Build passes: npm run build -- --webpack","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":40,"created_at":"2026-02-20T19:53:13.289646+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:08:48.511696+08:00","closed_at":"2026-02-20T20:08:48.511696+08:00","close_reason":"03b3c0c Add top-level nav links, group dividers, and search pill shadow","labels":["scope:small"],"dependencies":[{"issue_id":"docs-j3q.2","depends_on_id":"docs-j3q","type":"parent-child","created_at":"2026-02-20T19:53:13.290593+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-j3q.2","depends_on_id":"docs-j3q.1","type":"blocks","created_at":"2026-02-20T19:56:40.289382+08:00","created_by":"einstein.climateai.org"}]} +{"id":"docs-j3q.2","title":"Top-level nav links + group dividers + search pill affordance","description":"## Files\n- components/Layout.js (modify)\n- styles/globals.css (modify)\n\n## What to do\n\n### Top-level nav links (finding #2)\nAdd navigation links between the logo and the search pill. In `Layout.js`, after the logo `\u003cLink\u003e` and before the search pill `\u003cbutton\u003e`, add a `\u003cnav\u003e` with class `header-nav`:\n\n```jsx\n\u003cnav className=\"header-nav\" aria-label=\"Main navigation\"\u003e\n \u003cLink href=\"/getting-started/quickstart\" className=\"header-nav-link\"\u003eDocs\u003c/Link\u003e\n \u003cLink href=\"/tools/scaffold\" className=\"header-nav-link\"\u003eTools\u003c/Link\u003e\n \u003ca href=\"https://github.com/gainforest/hypercerts\" className=\"header-nav-link\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eGitHub\u003c/a\u003e\n\u003c/nav\u003e\n```\n\nCSS for `.header-nav`:\n- `display: none` by default (hidden on mobile)\n- `@media (min-width: 769px)`: `display: flex; align-items: center; gap: var(--space-2);`\n- `.header-nav-link`: font-size 14px, font-weight 500, color var(--color-text-secondary), text-decoration none, padding 6px 12px, border-radius var(--radius-sm), transition background+color var(--transition-fast)\n- `.header-nav-link:hover`: background var(--hover-bg), color var(--color-text-primary), text-decoration none\n\n### Group dividers (finding #8)\nAdd thin vertical dividers between logo group, nav links, and search+toggle group. Use `\u003cspan className=\"header-divider\" aria-hidden=\"true\" /\u003e` elements in the header-inner, placed:\n1. After the logo `\u003cLink\u003e` (before nav)\n2. After the `\u003cnav\u003e` (before search pill)\n\nCSS for `.header-divider`:\n- `display: none` by default\n- `@media (min-width: 769px)`: display block, width 1px, height 20px, background var(--color-border), margin 0 var(--space-2)\n- Dark mode: `html.dark .header-divider { background: oklch(0.30 0.005 260); }`\n\n### Search pill affordance (finding #10)\nThe search pill blends into the glass header. Add a subtle shadow:\n- `.search-pill`: add `box-shadow: 0 1px 3px oklch(0 0 0 / 0.06);`\n- `.search-pill:hover`: `box-shadow: 0 1px 4px oklch(0 0 0 / 0.10);`\n- Dark mode: `html.dark .search-pill { box-shadow: 0 1px 3px oklch(0 0 0 / 0.2); }`\n\n## Dont\n- Do NOT add more than 3 nav links — keep it minimal\n- Do NOT hide the Docs badge on the logo\n- Do NOT change the flex:1 spacer behavior — nav links go between logo and spacer","acceptance_criteria":"1. On desktop (≥769px), \"Docs\", \"Tools\", \"GitHub\" links are visible between logo and search pill\n2. Nav links are hidden on mobile (\u003c769px)\n3. Thin vertical dividers (1px, 20px tall) separate logo | nav | search groups\n4. Dividers are hidden on mobile\n5. Search pill has a subtle box-shadow distinguishing it from the glass header\n6. Dark mode: dividers and search pill shadow adapt correctly\n7. GitHub link opens in new tab with rel=\"noopener noreferrer\"\n8. Build passes: npm run build -- --webpack","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":40,"created_at":"2026-02-20T19:53:13.289646+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:08:48.511696+08:00","closed_at":"2026-02-20T20:08:48.511696+08:00","close_reason":"03b3c0c Add top-level nav links, group dividers, and search pill shadow","labels":["scope:small"],"dependencies":[{"issue_id":"docs-j3q.2","depends_on_id":"docs-j3q.1","type":"blocks","created_at":"2026-02-20T19:56:40.289382+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-j3q.2","depends_on_id":"docs-j3q","type":"parent-child","created_at":"2026-02-20T19:53:13.290593+08:00","created_by":"einstein.climateai.org"}]} {"id":"docs-j3q.3","title":"Focus-visible styles + header height + GitHub icon","description":"## Files\n- components/Layout.js (modify)\n- styles/globals.css (modify)\n\n## What to do\n\n### Focus-visible styles (finding #9)\nAdd `:focus-visible` styles to all interactive header elements that currently lack them:\n\n```css\n.hamburger-btn:focus-visible,\n.search-pill:focus-visible,\n.search-icon-btn:focus-visible,\n.theme-toggle:focus-visible {\n outline: none;\n box-shadow: var(--focus-ring);\n}\n```\n\nThe `--focus-ring` token already exists: `0 0 0 4px oklch(0.50 0.18 260 / 0.36)`. Just apply it.\n\nAlso add focus-visible to sidebar collapse button:\n```css\n.sidebar-collapse-btn:focus-visible,\n.sidebar-expand-btn:focus-visible {\n outline: none;\n box-shadow: var(--focus-ring);\n}\n```\n\n### Header height reduction (finding #26)\nChange `--header-height: 64px` to `--header-height: 56px` in `:root`.\n\nNote: the mobile breakpoint already sets `--header-height: 56px`, so this just makes desktop match. Remove the mobile override for header-height inside `@media (max-width: 768px)` since its now redundant.\n\n### GitHub icon in header (finding #27)\nAdd a GitHub icon link AFTER the ThemeToggle and BEFORE the closing `\u003c/div\u003e` of `.layout-header-inner`. Use a simple SVG icon button:\n\n```jsx\n\u003ca\n href=\"https://github.com/gainforest/hypercerts\"\n className=\"header-icon-link\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label=\"GitHub repository\"\n\u003e\n \u003csvg width=\"20\" height=\"20\" viewBox=\"0 0 16 16\" fill=\"currentColor\"\u003e\n \u003cpath d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z\" /\u003e\n \u003c/svg\u003e\n\u003c/a\u003e\n```\n\nCSS for `.header-icon-link`:\n- display flex, align-items center, justify-content center\n- width 36px, height 36px, border-radius var(--radius-md)\n- color var(--color-text-secondary), background none, text-decoration none\n- transition background+color var(--transition-fast)\n- `:hover` → background var(--hover-bg), color var(--color-text-primary)\n- `:focus-visible` → outline none, box-shadow var(--focus-ring)\n\nNOTE: If docs-j3q.2 already added a GitHub text link in the nav, this adds the icon version in the right group (near theme toggle). Both can coexist — the nav link is for desktop only, the icon is always visible. OR remove the text \"GitHub\" from the nav links added in docs-j3q.2, since this icon replaces it. Use your judgment — if both exist, remove the text one from nav to avoid duplication.\n\n## Dont\n- Do NOT change the logo size\n- Do NOT remove the Docs badge\n- Do NOT change any color tokens","acceptance_criteria":"1. Pressing Tab through header shows visible focus rings on hamburger, search pill, search icon, theme toggle, GitHub icon, sidebar buttons\n2. Focus ring uses the existing --focus-ring token (indigo 4px outline)\n3. Header height is 56px on all viewports (was 64px on desktop, already 56px on mobile)\n4. GitHub icon (octocat) is visible next to the theme toggle\n5. GitHub icon links to https://github.com/gainforest/hypercerts in new tab\n6. No duplicate GitHub links (if nav has text \"GitHub\" AND icon exists, remove the text one)\n7. Build passes: npm run build -- --webpack","status":"open","priority":2,"issue_type":"task","owner":"einstein.climateai.org","estimated_minutes":40,"created_at":"2026-02-20T19:53:35.844215+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:53:35.844215+08:00","labels":["scope:small"],"dependencies":[{"issue_id":"docs-j3q.3","depends_on_id":"docs-j3q","type":"parent-child","created_at":"2026-02-20T19:53:35.845103+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-j3q.3","depends_on_id":"docs-j3q.2","type":"blocks","created_at":"2026-02-20T19:56:40.356242+08:00","created_by":"einstein.climateai.org"}]} {"id":"docs-jss","title":"Epic: Add project-level README.md and DEVELOPMENT.md","description":"The documentation project lacks the standard project-level README.md and DEVELOPMENT.md files that our other repos (like hypercerts-scaffold-atproto) have. This epic adds both files following the same structure and style: comprehensive README with quick start, prerequisites, project structure, architecture; and DEVELOPMENT.md with local dev workflow, content authoring guide, Markdoc conventions, contributing, and troubleshooting. Success: a new contributor can clone the repo, run the docs site locally, and add a new page — all by reading README + DEVELOPMENT only.","status":"closed","priority":2,"issue_type":"epic","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","created_at":"2026-02-20T18:12:19.646036+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:14:51.247295+08:00","closed_at":"2026-02-20T18:14:51.247299+08:00","labels":["scope:medium"]} {"id":"docs-jss.1","title":"Create README.md at project root","description":"## Files\n- README.md (create)\n\n## What to do\nCreate a comprehensive README.md at the repo root following the structure and style of hypercerts-scaffold-atproto/README.md. The file must include these sections in order:\n\n1. **Title \u0026 description** — \"Hypercerts Documentation\" heading. One paragraph: this is the official documentation site for the Hypercerts protocol, built with Next.js and Markdoc, deployed on Vercel.\n\n2. **Prerequisites** — Node.js 20+, npm (not pnpm — the project uses npm per vercel.json and package-lock.json).\n\n3. **Quick Start** — Clone, cd documentation/documentation, npm install, npm run dev. Note the nested directory structure (the actual Next.js app lives in documentation/documentation/).\n\n4. **Project Structure** — ASCII tree showing:\n```\n├── documentation/ # Next.js documentation site\n│ ├── components/ # React components (Layout, Sidebar, CodeBlock, etc.)\n│ ├── lib/ # Navigation config and helpers\n│ ├── markdoc/ # Markdoc tag and node definitions\n│ │ ├── nodes/ # Custom node renderers (fence, heading)\n│ │ └── tags/ # Custom tags (callout, card-link, columns, figure)\n│ ├── pages/ # Documentation content (.md files)\n│ │ ├── architecture/\n│ │ ├── core-concepts/\n│ │ ├── ecosystem/\n│ │ ├── getting-started/\n│ │ ├── lexicons/\n│ │ ├── reference/\n│ │ └── tools/\n│ ├── public/ # Static assets\n│ └── styles/ # CSS\n├── heartbeads-agents/ # (reserved)\n├── AGENTS.md # Agent workflow instructions\n└── .beads/ # Issue tracking\n```\n\n5. **Content Sections** — Table listing the 7 doc sections (Get Started, Core Concepts, Tools, Architecture, Reference/Lexicons, Ecosystem \u0026 Vision) with one-line descriptions, matching the style of the scaffold README tables.\n\n6. **Deployment** — State that the site is deployed on Vercel. The build command is `next build --webpack`, install command is `npm install`. Static export via `output: \"export\"` in next.config.js.\n\n7. **Learn More** — Links to: Hypercerts website (https://hypercerts.org), ATProto docs (https://atproto.com/docs), Markdoc docs (https://markdoc.dev), Next.js docs (https://nextjs.org/docs), DEVELOPMENT.md for contributor guide.\n\n## Don't\n- Do NOT mention pnpm — this project uses npm\n- Do NOT include any authentication, OAuth, or SDK content (this is a docs site, not an application)\n- Do NOT copy scaffold-specific sections (environment variables, Redis, JWK keys, etc.)\n- Do NOT add a License section (there is none currently)\n- Keep it under 150 lines","acceptance_criteria":"1. README.md exists at /Users/david/Projects/gainforest/documentation/README.md\n2. Contains all 7 sections listed in spec: title, prerequisites, quick start, project structure, content sections, deployment, learn more\n3. Quick Start instructions work: cd documentation/documentation \u0026\u0026 npm install \u0026\u0026 npm run dev starts the dev server\n4. References npm (not pnpm) throughout\n5. Project structure ASCII tree accurately reflects actual directory layout\n6. No scaffold-specific content (no Redis, OAuth, JWK, SDK references)\n7. File is under 150 lines\n8. All links in Learn More section are valid URLs","status":"closed","priority":2,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":30,"created_at":"2026-02-20T18:12:47.807873+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:14:51.095793+08:00","closed_at":"2026-02-20T18:14:51.095815+08:00","labels":["scope:small"],"dependencies":[{"issue_id":"docs-jss.1","depends_on_id":"docs-jss","type":"parent-child","created_at":"2026-02-20T18:12:47.809207+08:00","created_by":"einstein.climateai.org"}]} @@ -63,14 +63,14 @@ {"id":"docs-nuw","title":"Epic: Architecture \u0026 Infrastructure Deep Dives","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:47:44.339166+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T01:08:17.134655+13:00","closed_at":"2026-02-15T01:08:17.134655+13:00","close_reason":"All children complete: nuw.1 (architecture overview), nuw.2 (data flow \u0026 lifecycle)"} {"id":"docs-nuw.1","title":"Write 'Architecture Overview' page with system diagram","description":"## Context\n\nThis project is a documentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. The site lives in `documentation/`. Pages are Markdoc `.md` files in `documentation/pages/`. Navigation is defined in `documentation/lib/navigation.js`. Available Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`, `{% card-link %}`.\n\nStripe's equivalent: \"How Stripe works\" — a high-level architecture page showing all the pieces and how they connect. The hypercerts docs currently describe architecture scattered across the \"Why\" page but have no dedicated architecture overview.\n\n## Files\n- documentation/pages/architecture/overview.md (create)\n- documentation/lib/navigation.js (modify — add new \"Architecture\" section)\n\n## What to do\n\n### 1. Create the page\n\nCreate `documentation/pages/architecture/overview.md` (150–220 lines of Markdoc):\n\n**Frontmatter:**\n```\n---\ntitle: Architecture Overview\ndescription: How the Hypercerts Protocol stack fits together.\n---\n```\n\n**Sections:**\n\n1. **The Hypercerts Stack** — High-level overview (3-4 paragraphs). The protocol has three layers:\n - **Application Layer** — Platforms, dashboards, funding tools that users interact with\n - **Data Layer (AT Protocol)** — Where claims, evidence, evaluations live. Includes PDS/SDS, lexicons, indexers/relays.\n - **Ownership Layer (Blockchain)** — Where tokenized ownership, funding flows, and immutability guarantees live.\n \n Describe this as a text-based diagram using a Markdoc code block (no images needed):\n ```\n ┌─────────────────────────────────────────┐\n │ Application Layer │\n │ Funding platforms, dashboards, tools │\n ├─────────────────────────────────────────┤\n │ Data Layer (AT Protocol) │\n │ PDS/SDS → Relay → App View/Indexer │\n ├─────────────────────────────────────────┤\n │ Ownership Layer (Blockchain) │\n │ Tokenization, funding, rights │\n └─────────────────────────────────────────┘\n ```\n\n2. **Data Layer Deep Dive** — How ATProto components work together:\n - PDS (Personal Data Server): stores user's records\n - Relay (formerly BGS): aggregates data across PDS instances\n - App View / Indexer: reads from relay, builds queryable views\n - Lexicons: shared schemas that define record structure\n - Explain the flow: user writes to PDS → relay picks it up → indexer makes it searchable\n\n3. **Ownership Layer Deep Dive** — How blockchain components work:\n - Anchoring: linking ATProto records to on-chain state\n - Tokenization: representing hypercerts as transferable tokens\n - Funding mechanisms: how tokens enable various funding models\n - Multi-chain support: different chains for different communities\n\n4. **How the Layers Connect** — The bridge between ATProto and on-chain:\n - A hypercert's content lives on ATProto (the claim, evidence, evaluations)\n - Its ownership and funding state lives on-chain\n - References between layers (ATProto URI ↔ on-chain token ID)\n - Why this separation matters (data portability + financial guarantees)\n\n5. **Key Design Decisions** — Why the architecture is the way it is:\n - Why not fully on-chain? (cost, flexibility, data portability)\n - Why not fully off-chain? (ownership guarantees, funding mechanisms)\n - Why ATProto over IPFS/Ceramic/other? (identity, federation, existing ecosystem)\n\n### 2. Add to navigation\n\nIn `documentation/lib/navigation.js`, add a new top-level section AFTER the \"Lexicons\" section and BEFORE \"Deep Dive: The Work Scope\":\n\n```javascript\n{\n section: 'Architecture',\n children: [\n { title: 'Architecture Overview', path: '/architecture/overview' },\n ],\n},\n```\n\n## Writing style\n- Technical but accessible — a developer should understand this without prior ATProto knowledge\n- Use `##` for main sections, `####` for subsections\n- Use text-based diagrams in fenced code blocks (no images)\n- Use `{% callout %}` for important design rationale\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5\n```\nMust exit 0 (static export succeeds).\n\n## Dont\n- Do NOT add image files — use text-based diagrams only.\n- Do NOT modify any existing pages other than navigation.js.\n- Do NOT use HTML tags — use Markdoc syntax only.\n- Do NOT create more than one page — just the overview.","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:49:44.483676+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T01:01:28.941433+13:00","closed_at":"2026-02-15T01:01:28.941433+13:00","close_reason":"Created Architecture Overview page with system diagram, added to navigation after Tools section","dependencies":[{"issue_id":"docs-nuw.1","depends_on_id":"docs-nuw","type":"parent-child","created_at":"2026-02-14T20:49:44.485585+13:00","created_by":"Sharfy Adamantine"}],"comments":[{"id":14,"issue_id":"docs-nuw.1","author":"Sharfy Adamantine","text":"NAV UPDATE: Navigation reorganized. Add the 'Architecture' section AFTER 'Core Concepts' and BEFORE 'Lexicons'. The new nav structure is: Get Started → Core Concepts → Architecture (new) → Lexicons.","created_at":"2026-02-14T11:24:41Z"},{"id":15,"issue_id":"docs-nuw.1","author":"Sharfy Adamantine","text":"DESIGN LANGUAGE: Follow Stripe docs (docs.stripe.com) patterns — one-sentence opener, short paragraphs (1-3 sentences), no preamble, no link dumps. Use text-based diagrams in code blocks. Study existing pages: documentation/pages/getting-started/why-atproto.md. BUILD COMMAND FIX: Use 'cd documentation \u0026\u0026 npx next build --webpack 2\u003e\u00261 | tail -5' (must include --webpack flag). NAV PLACEMENT: Add Architecture section AFTER 'Tools' and BEFORE 'Tutorials'. Current nav order: Get Started → Core Concepts → Lexicons → Tools → Tutorials.","created_at":"2026-02-14T11:58:10Z"}]} {"id":"docs-nuw.2","title":"Write 'Data Flow \u0026 Lifecycle' page","description":"## Context\n\nThis project is a documentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. The site lives in `documentation/`. Pages are Markdoc `.md` files in `documentation/pages/`. Navigation is defined in `documentation/lib/navigation.js`. Available Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`, `{% card-link %}`.\n\nStripe's equivalent: \"Payment lifecycle\" — shows how a payment moves through states from creation to completion. This page does the same for a hypercert.\n\n## Files\n- documentation/pages/architecture/data-flow-and-lifecycle.md (create)\n- documentation/lib/navigation.js (modify — add entry under Architecture section)\n\n## What to do\n\n### 1. Create the page\n\nCreate `documentation/pages/architecture/data-flow-and-lifecycle.md` (120–180 lines of Markdoc):\n\n**Frontmatter:**\n```\n---\ntitle: Data Flow \u0026 Lifecycle\ndescription: How a hypercert moves from creation through evaluation to funding.\n---\n```\n\n**Sections:**\n\n1. **The Lifecycle of a Hypercert** — Overview of the stages:\n - **Creation** — A contributor creates an activity claim on their PDS\n - **Enrichment** — Evidence, measurements, and contributions are attached\n - **Evaluation** — Third parties create evaluation records referencing the claim\n - **Discovery** — Indexers aggregate claims; platforms surface them to funders\n - **Funding** — Ownership is tokenized on-chain; funders acquire shares\n - **Accumulation** — More evaluations and evidence accrue over time\n\n2. **Stage 1: Creation** — What happens when a hypercert is created:\n - The contributor writes an `org.hypercerts.claim.activity` record to their PDS\n - The record gets a unique AT URI (e.g., `at://did:plc:abc123/org.hypercerts.claim.activity/tid`)\n - The PDS signs the record and includes it in the user's repository\n - The relay picks up the new record\n\n3. **Stage 2: Enrichment** — Attaching supporting data:\n - Contribution records (`org.hypercerts.claim.contribution`) are created and linked via strong references\n - Evidence records (`org.hypercerts.claim.evidence`) are attached\n - Measurement records (`org.hypercerts.claim.measurement`) provide quantitative data\n - Rights records (`org.hypercerts.claim.rights`) define what rights holders have\n - Location records (`app.certified.location`) anchor work geographically\n - These can live on the SAME PDS or DIFFERENT PDS instances\n\n4. **Stage 3: Evaluation** — Third-party assessment:\n - An evaluator creates an `org.hypercerts.claim.evaluation` record on THEIR OWN PDS\n - The evaluation references the original activity claim via a strong reference (`subject` field)\n - Multiple evaluators can independently evaluate the same claim\n - Evaluations accumulate over time — they are never \"final\"\n\n5. **Stage 4: Discovery \u0026 Indexing** — How data becomes findable:\n - Relays aggregate records from many PDS instances\n - Indexers (App Views) read from relays and build searchable databases\n - Platforms query indexers to surface hypercerts to users\n - Different indexers can build different views of the same data\n\n6. **Stage 5: Funding \u0026 Ownership** — The on-chain layer:\n - A hypercert can be anchored on-chain, creating a token\n - The token represents ownership shares of the contribution\n - Funders acquire shares through various mechanisms (direct purchase, retroactive funding, etc.)\n - On-chain state references the ATProto URI of the original claim\n\n7. **Cross-PDS References** — How records on different servers reference each other:\n - Strong references include both URI and CID (content hash)\n - This ensures references are tamper-evident\n - A record on PDS-A can reference a record on PDS-B\n\n### 2. Add to navigation\n\nIn `documentation/lib/navigation.js`, add this entry under the Architecture section (which should already exist from the Architecture Overview task):\n\n```javascript\n{ title: 'Data Flow \u0026 Lifecycle', path: '/architecture/data-flow-and-lifecycle' },\n```\n\nIf the Architecture section does not yet exist, create it after the Lexicons section.\n\n## Writing style\n- Narrative, walkthrough style — follow a single hypercert through its lifecycle\n- Use `##` for main sections, `####` for subsections\n- Use text-based flow diagrams in fenced code blocks where helpful\n- Reference specific lexicon IDs (e.g., `org.hypercerts.claim.activity`) and link to their documentation pages\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5\n```\nMust exit 0 (static export succeeds).\n\n## Dont\n- Do NOT add image files.\n- Do NOT modify any existing pages other than navigation.js.\n- Do NOT use HTML tags — use Markdoc syntax only.\n- Do NOT duplicate content from the Architecture Overview page — reference it instead.","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:50:08.992973+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T01:04:48.551227+13:00","closed_at":"2026-02-15T01:04:48.551227+13:00","close_reason":"Completed: Created Data Flow \u0026 Lifecycle page with 6-stage narrative walkthrough, added navigation entry, build passes","dependencies":[{"issue_id":"docs-nuw.2","depends_on_id":"docs-nuw","type":"parent-child","created_at":"2026-02-14T20:50:08.994666+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-nuw.2","depends_on_id":"docs-nuw.1","type":"blocks","created_at":"2026-02-14T20:54:30.68945+13:00","created_by":"Sharfy Adamantine"}],"comments":[{"id":16,"issue_id":"docs-nuw.2","author":"Sharfy Adamantine","text":"DESIGN LANGUAGE: Follow Stripe docs (docs.stripe.com) patterns — one-sentence opener, short paragraphs, no preamble, no link dumps. BUILD COMMAND FIX: Use 'cd documentation \u0026\u0026 npx next build --webpack 2\u003e\u00261 | tail -5' (must include --webpack flag).","created_at":"2026-02-14T11:58:17Z"}]} -{"id":"docs-qsc","title":"Epic 3: Content Migration - Convert All 17 Markdown Files","description":"## Summary\nConvert all 17 GitBook Markdown files to Markdoc-compatible format and move them into the Next.js `pages/` directory. This involves replacing GitBook-proprietary syntax with Markdoc tags, cleaning up HTML artifacts, fixing frontmatter, and restructuring file paths for Next.js routing.\n\n## Context\nThis project is a Next.js + Markdoc documentation site in the `documentation/` directory at the repo root (`/Users/sharfy/Code/hypercerts-atproto-documentation/documentation/`).\n\nThe current GitBook content files are at the `documentation/` root level (e.g., `documentation/README.md`, `documentation/getting-started/why-were-building-hypercerts.md`). They need to be moved into `documentation/pages/` for Next.js file-based routing.\n\nBy the time this epic runs:\n- Epic 1 (Scaffold) has created the `pages/`, `components/`, `markdoc/`, etc. directories\n- Epic 2 (Custom Tags) has created Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`\n- A basic `pages/_app.js` and `pages/index.md` (test page) exist\n\n### Key routing rules:\n- `pages/index.md` -\u003e renders at `/`\n- `pages/getting-started/why-were-building-hypercerts.md` -\u003e renders at `/getting-started/why-were-building-hypercerts`\n- `README.md` files must be renamed to `index.md` (Next.js convention for directory index pages)\n- Internal links should NOT include `.md` extensions\n\n## Content Files to Migrate (17 total)\n\n### File mapping (source -\u003e destination):\n```\ndocumentation/README.md -\u003e documentation/pages/index.md\ndocumentation/deep-dive-the-work-scope.md -\u003e documentation/pages/deep-dive-the-work-scope.md\ndocumentation/getting-started/why-were-building-hypercerts.md -\u003e documentation/pages/getting-started/why-were-building-hypercerts.md\ndocumentation/getting-started/introduction-to-impact-claims.md -\u003e documentation/pages/getting-started/introduction-to-impact-claims.md\ndocumentation/getting-started/the-impact-and-work-space.md -\u003e documentation/pages/getting-started/the-impact-and-work-space.md\ndocumentation/getting-started/the-hypercerts-infrastructure.md -\u003e documentation/pages/getting-started/the-hypercerts-infrastructure.md\ndocumentation/getting-started/installing-the-sdk.md -\u003e documentation/pages/getting-started/installing-the-sdk.md\ndocumentation/lexicons/introduction-to-lexicons.md -\u003e documentation/pages/lexicons/introduction-to-lexicons.md\ndocumentation/lexicons/general-lexicons/README.md -\u003e documentation/pages/lexicons/general-lexicons/index.md\ndocumentation/lexicons/general-lexicons/shared-defs.md -\u003e documentation/pages/lexicons/general-lexicons/shared-defs.md\ndocumentation/lexicons/general-lexicons/location.md -\u003e documentation/pages/lexicons/general-lexicons/location.md\ndocumentation/lexicons/hypercerts-lexicons/README.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/index.md\ndocumentation/lexicons/hypercerts-lexicons/activity-claim.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/activity-claim.md\ndocumentation/lexicons/hypercerts-lexicons/contribution.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/contribution.md\ndocumentation/lexicons/hypercerts-lexicons/evaluation.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/evaluation.md\ndocumentation/lexicons/hypercerts-lexicons/measurement.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/measurement.md\ndocumentation/lexicons/hypercerts-lexicons/attachment.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/attachment.md\ndocumentation/lexicons/hypercerts-lexicons/rights.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/rights.md\ndocumentation/lexicons/hypercerts-lexicons/collection.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/collection.md\n```\n\n## Global Transformations (apply to ALL files)\n\n1. **Remove all `\u0026#x20;` HTML entities** (trailing space artifacts from GitBook) -- found in 6 files, ~9 instances\n2. **Remove all zero-width space characters** (`\\u200b`) from table cells -- found in activity-claim.md, rights.md\n3. **Remove all GitBook anchor tags from headings**: `\u003ca href=\"#...\" id=\"...\"\u003e\u003c/a\u003e` -- found in why-were-building-hypercerts.md (15 instances), activity-claim.md (3 instances)\n4. **Remove trailing `\u003cbr\u003e` tags** at end of files -- found in 6 files\n5. **Convert GitBook layout frontmatter** to simple `title`/`description` frontmatter. The GitBook frontmatter looks like:\n```yaml\nlayout:\n width: default\n title:\n visible: true\n ...\nmetaLinks:\n alternates:\n - https://app.gitbook.com/s/...\n```\nReplace with:\n```yaml\ntitle: Page Title Here\ndescription: Optional description\n```\n6. **Rename README.md -\u003e index.md** (3 files: root, general-lexicons, hypercerts-lexicons)\n7. **Update all internal links** to use Next.js-compatible paths (remove `.md` extensions, use absolute paths from root)\n8. **Add frontmatter** to every file that lacks it (at minimum: `title`)\n\n## File-Specific Transformations\n\n### README.md -\u003e pages/index.md\n- Replace the entire GitBook layout frontmatter block with: `title: Welcome to the Hypercerts Protocol`\n- Convert `{% columns %}`/`{% column %}`/`{% endcolumn %}`/`{% endcolumns %}` to Markdoc syntax: `{% columns %}`/`{% column %}`/`{% /column %}`/`{% /columns %}`\n- Convert 2 `\u003cfigure\u003e\u003cimg src=\".gitbook/assets/...\"\u003e` tags to `{% figure src=\"/images/...\" /%}`:\n - `.gitbook/assets/hypercerts_for_projects.png` -\u003e `/images/hypercerts_for_projects.png`\n - `.gitbook/assets/hypercert erd.png` -\u003e `/images/hypercert-erd.png`\n- Remove or fix broken link `/broken/pages/gNQzZ9R1b3NKrJoQ0Iqa` (read surrounding text to determine intent; if undeterminable, remove the link and keep the text)\n- Remove trailing `\u003cbr\u003e`\n\n### deep-dive-the-work-scope.md -\u003e pages/deep-dive-the-work-scope.md\n- Add frontmatter: `title: \"Deep Dive: The Work Scope\"`\n- Convert `{% hint style=\"info\" %}...{% endhint %}` to `{% callout type=\"info\" %}...{% /callout %}`\n\n### getting-started/why-were-building-hypercerts.md\n- Keep existing `description` frontmatter, add `title: \"Why We're Building Hypercerts\"`\n- Remove all 15 anchor `\u003ca href=\"#...\" id=\"...\"\u003e\u003c/a\u003e` tags from headings\n- Remove trailing `\u003cbr\u003e`\n\n### getting-started/introduction-to-impact-claims.md\n- Replace `icon: bolt` frontmatter with `title: Introduction to Impact Claims`\n- Remove `\u0026#x20;` entities (3 instances)\n- Convert `\u003cfigure\u003e\u003cimg src=\"../.gitbook/assets/hypercert erd.png\"\u003e` to `{% figure src=\"/images/hypercert-erd.png\" alt=\"Hypercert ERD\" /%}`\n\n### getting-started/the-impact-and-work-space.md\n- Add frontmatter: `title: The Impact and Work Space`\n- Remove `\u0026#x20;` entity\n- Fix broken self-referencing anchor link `the-impact-and-work-space.md#the-impact-space` -- the heading `#the-impact-space` does not exist. Check what heading was intended and fix.\n\n### getting-started/the-hypercerts-infrastructure.md\n- Add frontmatter: `title: The Hypercerts Infrastructure`\n- Content is just \"TBD\" -- leave as-is\n\n### getting-started/installing-the-sdk.md\n- Add frontmatter: `title: Installing the SDK`\n- Remove `\u003cbr\u003e`. Content is empty -- leave as-is\n\n### lexicons/introduction-to-lexicons.md\n- Add frontmatter: `title: Introduction to Lexicons`\n- Remove `\u0026#x20;`\n- Fix 2 broken links: `/broken/pages/3UwgsMpD20ErXurhzmTh` and `/broken/pages/FdVczwFhTBj7n5nszh0P` -- read surrounding text to determine intent. If undeterminable, remove the links and keep the text.\n\n### lexicons/general-lexicons/README.md -\u003e pages/lexicons/general-lexicons/index.md\n- Add frontmatter: `title: General Lexicons`\n\n### lexicons/general-lexicons/shared-defs.md\n- Add frontmatter: `title: Shared Definitions`\n- Remove `\u0026#x20;`\n\n### lexicons/general-lexicons/location.md\n- Add frontmatter: `title: Location`\n- Remove `\u0026#x20;` (3 instances)\n- Fix inline HTML in table cell: `\u003cp\u003e\u003ccode\u003eunion\u003c/code\u003e\u003cbr\u003e...\u003c/p\u003e` -\u003e clean Markdown equivalent\n- Fix broken anchor link `location.md#locationtype` -\u003e `#location-type` (the heading is \"### Location Type\" which generates anchor `#location-type`)\n\n### lexicons/hypercerts-lexicons/README.md -\u003e pages/lexicons/hypercerts-lexicons/index.md\n- Add frontmatter: `title: Hypercerts Lexicons`\n\n### lexicons/hypercerts-lexicons/activity-claim.md\n- Add frontmatter: `title: Activity Claim`\n- Remove 3 anchor `\u003ca\u003e` tags from headings\n- Fix malformed table: the table is missing a proper header row. The first data row currently acts as the header. Add a real header row with columns: Property, Type, Required, Description, Comments\n- Remove zero-width space characters (`\\u200b`) from table cells\n- Replace broken GitBook editor URL `https://app.gitbook.com/o/uxU8o4s6mrp88yBisLn5/s/8dkOAon1uQbyqL8RomaJ/hypercert-claim-lexicons/the-impact-and-work-space` with relative link `/getting-started/the-impact-and-work-space`\n- Remove trailing `\u003cbr\u003e`\n\n### lexicons/hypercerts-lexicons/contribution.md\n- Add frontmatter: `title: Contribution`\n- Remove trailing `\u003cbr\u003e`\n\n### lexicons/hypercerts-lexicons/evaluation.md\n- Add frontmatter: `title: Evaluation`\n- Remove trailing `\u003cbr\u003e`\n\n### lexicons/hypercerts-lexicons/measurement.md\n- Add frontmatter: `title: Measurement`\n- Remove `\u0026#x20;`\n\n### lexicons/hypercerts-lexicons/attachment.md\n- Add frontmatter: `title: Evidence`\n- No special syntax changes needed\n\n### lexicons/hypercerts-lexicons/rights.md\n- Add frontmatter: `title: Rights`\n- Fix malformed table: add proper header row (same issue as activity-claim.md)\n- Remove zero-width space characters from table cells\n\n### lexicons/hypercerts-lexicons/collection.md\n- Add frontmatter: `title: Collection`\n- No special syntax changes needed\n\n## Acceptance Criteria\n- All 17 files are in `documentation/pages/` directory with correct paths\n- README.md files renamed to index.md (3 files)\n- No GitBook-specific syntax remains (`{% hint %}`, `{% endcolumns %}`, `{% endcolumn %}`, `{% endhint %}`, etc.)\n- No `\u0026#x20;` entities, no zero-width spaces, no orphan `\u003ca\u003e` anchor tags, no trailing `\u003cbr\u003e`\n- All files have proper YAML frontmatter with at minimum a `title` field\n- All broken links either resolved to correct targets or removed with text preserved\n- Malformed tables in activity-claim.md and rights.md have proper header rows\n- All image paths updated from `.gitbook/assets/` to `/images/`\n- All internal links use paths without `.md` extensions\n- All files render correctly via `npm run dev`\n","status":"closed","priority":0,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-13T13:42:41.246087+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T13:13:55.289826+13:00","closed_at":"2026-02-14T13:13:55.289831+13:00","dependencies":[{"issue_id":"docs-qsc","depends_on_id":"docs-c34","type":"blocks","created_at":"2026-02-13T13:42:41.247806+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-qsc","depends_on_id":"docs-vbw","type":"blocks","created_at":"2026-02-13T13:42:41.24883+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-r1z","title":"Epic 9: Cleanup and Verification","description":"## Summary\nFinal cleanup pass to remove all GitBook artifacts, verify link integrity, perform visual QA against the Stripe-inspired design language, and ensure the project is clean and production-ready.\n\n## Context\nThis project is a Next.js + Markdoc documentation site in the `documentation/` directory inside the repository at `/Users/sharfy/Code/hypercerts-atproto-documentation/documentation/`.\n\nBy the time this epic runs, Epics 1-8 have completed:\n- Next.js + Markdoc project scaffolded (Epic 1)\n- Custom Markdoc tags created: callout, columns, column, figure (Epic 2)\n- All 17 content files migrated to `pages/` with cleaned syntax (Epic 3)\n- Three-column layout with sidebar, content, and right TOC built (Epic 4)\n- Stripe-inspired CSS styling applied (Epic 5)\n- Images moved to `public/images/` and `.gitbook/` deleted (Epic 6)\n- All broken links fixed (Epic 7)\n- Build verified and deployment configured (Epic 8)\n\nThis epic is the final quality gate before the migration is considered complete.\n\n## Tasks\n\n### 1. Remove GitBook Artifacts\n\nDelete all files and directories that are GitBook-specific and no longer needed:\n\n```bash\n# Verify .gitbook/ is already deleted (should be gone from Epic 6)\nls -la documentation/.gitbook 2\u003e\u00261 # Should say \"No such file or directory\"\n\n# Delete SUMMARY.md (replaced by programmatic navigation in Epic 4)\nrm -f documentation/SUMMARY.md\n\n# Delete the original GitBook content files that were copied to pages/\n# These are the OLD files at the documentation/ root level (NOT the ones in pages/)\nrm -f documentation/README.md\nrm -f documentation/deep-dive-the-work-scope.md\nrm -rf documentation/getting-started/\nrm -rf documentation/lexicons/\n```\n\n**Important:** Only delete the old root-level files. Do NOT delete anything inside `documentation/pages/`, `documentation/components/`, `documentation/markdoc/`, etc.\n\nAlso check for and remove any remaining GitBook-specific frontmatter in content files. GitBook frontmatter looks like:\n```yaml\nlayout:\n width: default\n title:\n visible: true\nmetaLinks:\n alternates:\n - https://app.gitbook.com/s/...\n```\nIf any of this remains in any file under `pages/`, strip it and leave only `title` and `description` fields.\n\n### 2. Run Artifact Grep Checks\n\nSearch all content files for leftover GitBook artifacts. Every one of these commands should return ZERO results:\n\n```bash\n# GitBook broken page links\ngrep -r '/broken/pages/' documentation/pages/\n\n# GitBook editor URLs\ngrep -r 'app.gitbook.com' documentation/pages/\n\n# GitBook asset paths\ngrep -r '.gitbook/' documentation/pages/\ngrep -r '.gitbook/' documentation/components/\n\n# HTML entity artifacts\ngrep -r '\u0026#x20;' documentation/pages/\n\n# Zero-width spaces (use hex search)\ngrep -rP '\\x{200b}' documentation/pages/ 2\u003e/dev/null || grep -r $'\\u200b' documentation/pages/\n\n# GitBook proprietary tag syntax (should all be converted to Markdoc)\ngrep -r '{% hint' documentation/pages/\ngrep -r '{% endhint' documentation/pages/\ngrep -r '{% endcolumn' documentation/pages/\ngrep -r '{% endcolumns' documentation/pages/\n\n# Orphan HTML tags\ngrep -r '\u003cbr\u003e$' documentation/pages/\ngrep -r '\u003ca href=\"#' documentation/pages/\n```\n\nFix any remaining issues found.\n\n### 3. Verify All Internal Links\n\nStart the dev server and manually navigate to every page via the sidebar:\n\n```bash\ncd documentation \u0026\u0026 npm run dev\n```\n\nPages to check (17 total):\n1. `/` (Welcome / landing page)\n2. `/getting-started/why-were-building-hypercerts`\n3. `/getting-started/introduction-to-impact-claims`\n4. `/getting-started/the-impact-and-work-space`\n5. `/getting-started/the-hypercerts-infrastructure`\n6. `/getting-started/installing-the-sdk`\n7. `/lexicons/introduction-to-lexicons`\n8. `/lexicons/general-lexicons`\n9. `/lexicons/general-lexicons/shared-defs`\n10. `/lexicons/general-lexicons/location`\n11. `/lexicons/hypercerts-lexicons`\n12. `/lexicons/hypercerts-lexicons/activity-claim`\n13. `/lexicons/hypercerts-lexicons/contribution`\n14. `/lexicons/hypercerts-lexicons/evaluation`\n15. `/lexicons/hypercerts-lexicons/measurement`\n16. `/lexicons/hypercerts-lexicons/attachment`\n17. `/lexicons/hypercerts-lexicons/rights`\n18. `/lexicons/hypercerts-lexicons/collection`\n19. `/deep-dive-the-work-scope`\n\nFor each page verify:\n- Page loads without errors (check browser console)\n- No 404s for any internal links on the page\n- Anchor links (e.g., `#location-type`) scroll to the correct heading\n\n### 4. Visual QA Against Stripe Design Language\n\nFor each page, verify the design matches the Stripe-inspired patterns:\n\n**Layout:**\n- [ ] Three-column layout on desktop: left sidebar (~240px), content (~720px max), right TOC (~200px)\n- [ ] Sidebar has subtle 1px right border (#ebeef1), not a heavy shadow\n- [ ] Content area is well-centered with comfortable padding\n- [ ] Header is minimal with site title and 1px bottom border\n\n**Typography:**\n- [ ] Body text is 16px, dark gray (#414552), NOT pure black\n- [ ] Headings use proper hierarchy (H1 largest, H2 with large top margin creating section breaks)\n- [ ] System font stack is used (no custom font loading delays)\n- [ ] Text is anti-aliased (`-webkit-font-smoothing: antialiased`)\n- [ ] Line height is comfortable (~1.65 for body)\n\n**Colors:**\n- [ ] Page background is pure white\n- [ ] Links are blue (#0570de), darken on hover\n- [ ] The page is overwhelmingly neutral/grayscale -- color appears only for links, callouts, and interactive elements\n- [ ] No decorative elements, no gradients, no heavy shadows\n\n**Components:**\n- [ ] Tables are clean: no outer borders, header has bottom border only, no zebra striping\n- [ ] Code blocks have light gray background (#f6f8fa), rounded corners, monospace font\n- [ ] Inline code has subtle gray background pill\n- [ ] Callout boxes (on deep-dive page) have colored left border and tinted background\n- [ ] Column layouts (on landing page) display side-by-side on desktop, stack on mobile\n- [ ] Images render correctly with proper sizing\n\n**Navigation:**\n- [ ] Sidebar section headers are small, semibold, secondary gray\n- [ ] Active page is highlighted in sidebar (bold text + subtle background)\n- [ ] Right TOC shows H2 headings and highlights current section on scroll\n- [ ] Prev/next pagination works at bottom of content area\n- [ ] On mobile (\u003c 768px): sidebar collapses, content goes full width\n\n### 5. Verify Heading Anchor IDs\n\nMarkdoc auto-generates heading IDs from heading text. Verify that the generation is consistent:\n- \"### Location Type\" should generate `#location-type` (lowercase, hyphenated)\n- Any in-page anchor links should match the generated IDs\n- Click each anchor link on pages that have them to confirm they work\n\n### 6. Final File Inventory\n\nVerify the project structure is clean:\n```\ndocumentation/\n├── components/\n│ ├── Callout.js\n│ ├── Column.js\n│ ├── Columns.js\n│ ├── Figure.js\n│ ├── Layout.js\n│ ├── Sidebar.js\n│ └── TableOfContents.js\n├── lib/\n│ └── navigation.js\n├── markdoc/\n│ ├── tags/\n│ │ ├── callout.markdoc.js\n│ │ ├── column.markdoc.js\n│ │ ├── columns.markdoc.js\n│ │ ├── figure.markdoc.js\n│ │ └── index.js\n│ └── nodes/ (may be empty or have heading overrides)\n├── pages/\n│ ├── _app.js\n│ ├── index.md\n│ ├── deep-dive-the-work-scope.md\n│ ├── getting-started/\n│ │ ├── why-were-building-hypercerts.md\n│ │ ├── introduction-to-impact-claims.md\n│ │ ├── the-impact-and-work-space.md\n│ │ ├── the-hypercerts-infrastructure.md\n│ │ └── installing-the-sdk.md\n│ └── lexicons/\n│ ├── introduction-to-lexicons.md\n│ ├── general-lexicons/\n│ │ ├── index.md\n│ │ ├── shared-defs.md\n│ │ └── location.md\n│ └── hypercerts-lexicons/\n│ ├── index.md\n│ ├── activity-claim.md\n│ ├── contribution.md\n│ ├── evaluation.md\n│ ├── measurement.md\n│ ├── attachment.md\n│ ├── rights.md\n│ └── collection.md\n├── public/\n│ └── images/\n│ ├── hypercerts_for_projects.png\n│ └── hypercert-erd.png\n├── styles/\n│ └── globals.css\n├── next.config.js\n├── package.json\n├── package-lock.json\n└── .gitignore\n```\n\nConfirm:\n- No `.gitbook/` directory\n- No `SUMMARY.md`\n- No root-level `.md` content files (only inside `pages/`)\n- No orphaned files outside the standard Next.js structure\n\n### 7. Final Build Check\n```bash\ncd documentation\nnpm run build\n```\nMust succeed with zero errors after all cleanup.\n\n## Acceptance Criteria\n- Zero grep hits for `/broken/pages/`, `app.gitbook.com`, `.gitbook/`, `\u0026#x20;`, zero-width spaces, `{% hint`, `{% endhint`, `{% endcolumn`, `{% endcolumns` in content files\n- `SUMMARY.md` is deleted\n- `.gitbook/` directory is deleted\n- Old root-level content files (README.md, getting-started/, lexicons/, deep-dive-the-work-scope.md) are deleted\n- All 17+ pages render correctly in the browser with no console errors\n- All internal links resolve correctly (no 404s)\n- All heading anchor links work\n- The visual design matches the Stripe-inspired patterns (white bg, gray text, blue links, clean tables, colored callouts, three-column layout)\n- The project directory structure is clean with no orphaned files\n- `npm run build` succeeds with zero errors\n","status":"closed","priority":2,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-13T13:46:01.998664+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T13:19:59.577445+13:00","closed_at":"2026-02-14T13:19:59.577453+13:00","dependencies":[{"issue_id":"docs-r1z","depends_on_id":"docs-c34","type":"blocks","created_at":"2026-02-13T13:46:02.000863+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-vbw","type":"blocks","created_at":"2026-02-13T13:46:02.002015+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-qsc","type":"blocks","created_at":"2026-02-13T13:46:02.002729+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-7dv","type":"blocks","created_at":"2026-02-13T13:46:02.003372+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-yne","type":"blocks","created_at":"2026-02-13T13:46:02.004004+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-cij","type":"blocks","created_at":"2026-02-13T13:46:02.004627+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-7b4","type":"blocks","created_at":"2026-02-13T13:46:02.00526+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-d4r","type":"blocks","created_at":"2026-02-13T13:46:02.005893+13:00","created_by":"Sharfy Adamantine"}]} +{"id":"docs-qsc","title":"Epic 3: Content Migration - Convert All 17 Markdown Files","description":"## Summary\nConvert all 17 GitBook Markdown files to Markdoc-compatible format and move them into the Next.js `pages/` directory. This involves replacing GitBook-proprietary syntax with Markdoc tags, cleaning up HTML artifacts, fixing frontmatter, and restructuring file paths for Next.js routing.\n\n## Context\nThis project is a Next.js + Markdoc documentation site in the `documentation/` directory at the repo root (`/Users/sharfy/Code/hypercerts-atproto-documentation/documentation/`).\n\nThe current GitBook content files are at the `documentation/` root level (e.g., `documentation/README.md`, `documentation/getting-started/why-were-building-hypercerts.md`). They need to be moved into `documentation/pages/` for Next.js file-based routing.\n\nBy the time this epic runs:\n- Epic 1 (Scaffold) has created the `pages/`, `components/`, `markdoc/`, etc. directories\n- Epic 2 (Custom Tags) has created Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`\n- A basic `pages/_app.js` and `pages/index.md` (test page) exist\n\n### Key routing rules:\n- `pages/index.md` -\u003e renders at `/`\n- `pages/getting-started/why-were-building-hypercerts.md` -\u003e renders at `/getting-started/why-were-building-hypercerts`\n- `README.md` files must be renamed to `index.md` (Next.js convention for directory index pages)\n- Internal links should NOT include `.md` extensions\n\n## Content Files to Migrate (17 total)\n\n### File mapping (source -\u003e destination):\n```\ndocumentation/README.md -\u003e documentation/pages/index.md\ndocumentation/deep-dive-the-work-scope.md -\u003e documentation/pages/deep-dive-the-work-scope.md\ndocumentation/getting-started/why-were-building-hypercerts.md -\u003e documentation/pages/getting-started/why-were-building-hypercerts.md\ndocumentation/getting-started/introduction-to-impact-claims.md -\u003e documentation/pages/getting-started/introduction-to-impact-claims.md\ndocumentation/getting-started/the-impact-and-work-space.md -\u003e documentation/pages/getting-started/the-impact-and-work-space.md\ndocumentation/getting-started/the-hypercerts-infrastructure.md -\u003e documentation/pages/getting-started/the-hypercerts-infrastructure.md\ndocumentation/getting-started/installing-the-sdk.md -\u003e documentation/pages/getting-started/installing-the-sdk.md\ndocumentation/lexicons/introduction-to-lexicons.md -\u003e documentation/pages/lexicons/introduction-to-lexicons.md\ndocumentation/lexicons/general-lexicons/README.md -\u003e documentation/pages/lexicons/general-lexicons/index.md\ndocumentation/lexicons/general-lexicons/shared-defs.md -\u003e documentation/pages/lexicons/general-lexicons/shared-defs.md\ndocumentation/lexicons/general-lexicons/location.md -\u003e documentation/pages/lexicons/general-lexicons/location.md\ndocumentation/lexicons/hypercerts-lexicons/README.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/index.md\ndocumentation/lexicons/hypercerts-lexicons/activity-claim.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/activity-claim.md\ndocumentation/lexicons/hypercerts-lexicons/contribution.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/contribution.md\ndocumentation/lexicons/hypercerts-lexicons/evaluation.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/evaluation.md\ndocumentation/lexicons/hypercerts-lexicons/measurement.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/measurement.md\ndocumentation/lexicons/hypercerts-lexicons/attachment.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/attachment.md\ndocumentation/lexicons/hypercerts-lexicons/rights.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/rights.md\ndocumentation/lexicons/hypercerts-lexicons/collection.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/collection.md\n```\n\n## Global Transformations (apply to ALL files)\n\n1. **Remove all `\u0026#x20;` HTML entities** (trailing space artifacts from GitBook) -- found in 6 files, ~9 instances\n2. **Remove all zero-width space characters** (`\\u200b`) from table cells -- found in activity-claim.md, rights.md\n3. **Remove all GitBook anchor tags from headings**: `\u003ca href=\"#...\" id=\"...\"\u003e\u003c/a\u003e` -- found in why-were-building-hypercerts.md (15 instances), activity-claim.md (3 instances)\n4. **Remove trailing `\u003cbr\u003e` tags** at end of files -- found in 6 files\n5. **Convert GitBook layout frontmatter** to simple `title`/`description` frontmatter. The GitBook frontmatter looks like:\n```yaml\nlayout:\n width: default\n title:\n visible: true\n ...\nmetaLinks:\n alternates:\n - https://app.gitbook.com/s/...\n```\nReplace with:\n```yaml\ntitle: Page Title Here\ndescription: Optional description\n```\n6. **Rename README.md -\u003e index.md** (3 files: root, general-lexicons, hypercerts-lexicons)\n7. **Update all internal links** to use Next.js-compatible paths (remove `.md` extensions, use absolute paths from root)\n8. **Add frontmatter** to every file that lacks it (at minimum: `title`)\n\n## File-Specific Transformations\n\n### README.md -\u003e pages/index.md\n- Replace the entire GitBook layout frontmatter block with: `title: Welcome to the Hypercerts Protocol`\n- Convert `{% columns %}`/`{% column %}`/`{% endcolumn %}`/`{% endcolumns %}` to Markdoc syntax: `{% columns %}`/`{% column %}`/`{% /column %}`/`{% /columns %}`\n- Convert 2 `\u003cfigure\u003e\u003cimg src=\".gitbook/assets/...\"\u003e` tags to `{% figure src=\"/images/...\" /%}`:\n - `.gitbook/assets/hypercerts_for_projects.png` -\u003e `/images/hypercerts_for_projects.png`\n - `.gitbook/assets/hypercert erd.png` -\u003e `/images/hypercert-erd.png`\n- Remove or fix broken link `/broken/pages/gNQzZ9R1b3NKrJoQ0Iqa` (read surrounding text to determine intent; if undeterminable, remove the link and keep the text)\n- Remove trailing `\u003cbr\u003e`\n\n### deep-dive-the-work-scope.md -\u003e pages/deep-dive-the-work-scope.md\n- Add frontmatter: `title: \"Deep Dive: The Work Scope\"`\n- Convert `{% hint style=\"info\" %}...{% endhint %}` to `{% callout type=\"info\" %}...{% /callout %}`\n\n### getting-started/why-were-building-hypercerts.md\n- Keep existing `description` frontmatter, add `title: \"Why We're Building Hypercerts\"`\n- Remove all 15 anchor `\u003ca href=\"#...\" id=\"...\"\u003e\u003c/a\u003e` tags from headings\n- Remove trailing `\u003cbr\u003e`\n\n### getting-started/introduction-to-impact-claims.md\n- Replace `icon: bolt` frontmatter with `title: Introduction to Impact Claims`\n- Remove `\u0026#x20;` entities (3 instances)\n- Convert `\u003cfigure\u003e\u003cimg src=\"../.gitbook/assets/hypercert erd.png\"\u003e` to `{% figure src=\"/images/hypercert-erd.png\" alt=\"Hypercert ERD\" /%}`\n\n### getting-started/the-impact-and-work-space.md\n- Add frontmatter: `title: The Impact and Work Space`\n- Remove `\u0026#x20;` entity\n- Fix broken self-referencing anchor link `the-impact-and-work-space.md#the-impact-space` -- the heading `#the-impact-space` does not exist. Check what heading was intended and fix.\n\n### getting-started/the-hypercerts-infrastructure.md\n- Add frontmatter: `title: The Hypercerts Infrastructure`\n- Content is just \"TBD\" -- leave as-is\n\n### getting-started/installing-the-sdk.md\n- Add frontmatter: `title: Installing the SDK`\n- Remove `\u003cbr\u003e`. Content is empty -- leave as-is\n\n### lexicons/introduction-to-lexicons.md\n- Add frontmatter: `title: Introduction to Lexicons`\n- Remove `\u0026#x20;`\n- Fix 2 broken links: `/broken/pages/3UwgsMpD20ErXurhzmTh` and `/broken/pages/FdVczwFhTBj7n5nszh0P` -- read surrounding text to determine intent. If undeterminable, remove the links and keep the text.\n\n### lexicons/general-lexicons/README.md -\u003e pages/lexicons/general-lexicons/index.md\n- Add frontmatter: `title: General Lexicons`\n\n### lexicons/general-lexicons/shared-defs.md\n- Add frontmatter: `title: Shared Definitions`\n- Remove `\u0026#x20;`\n\n### lexicons/general-lexicons/location.md\n- Add frontmatter: `title: Location`\n- Remove `\u0026#x20;` (3 instances)\n- Fix inline HTML in table cell: `\u003cp\u003e\u003ccode\u003eunion\u003c/code\u003e\u003cbr\u003e...\u003c/p\u003e` -\u003e clean Markdown equivalent\n- Fix broken anchor link `location.md#locationtype` -\u003e `#location-type` (the heading is \"### Location Type\" which generates anchor `#location-type`)\n\n### lexicons/hypercerts-lexicons/README.md -\u003e pages/lexicons/hypercerts-lexicons/index.md\n- Add frontmatter: `title: Hypercerts Lexicons`\n\n### lexicons/hypercerts-lexicons/activity-claim.md\n- Add frontmatter: `title: Activity Claim`\n- Remove 3 anchor `\u003ca\u003e` tags from headings\n- Fix malformed table: the table is missing a proper header row. The first data row currently acts as the header. Add a real header row with columns: Property, Type, Required, Description, Comments\n- Remove zero-width space characters (`\\u200b`) from table cells\n- Replace broken GitBook editor URL `https://app.gitbook.com/o/uxU8o4s6mrp88yBisLn5/s/8dkOAon1uQbyqL8RomaJ/hypercert-claim-lexicons/the-impact-and-work-space` with relative link `/getting-started/the-impact-and-work-space`\n- Remove trailing `\u003cbr\u003e`\n\n### lexicons/hypercerts-lexicons/contribution.md\n- Add frontmatter: `title: Contribution`\n- Remove trailing `\u003cbr\u003e`\n\n### lexicons/hypercerts-lexicons/evaluation.md\n- Add frontmatter: `title: Evaluation`\n- Remove trailing `\u003cbr\u003e`\n\n### lexicons/hypercerts-lexicons/measurement.md\n- Add frontmatter: `title: Measurement`\n- Remove `\u0026#x20;`\n\n### lexicons/hypercerts-lexicons/attachment.md\n- Add frontmatter: `title: Evidence`\n- No special syntax changes needed\n\n### lexicons/hypercerts-lexicons/rights.md\n- Add frontmatter: `title: Rights`\n- Fix malformed table: add proper header row (same issue as activity-claim.md)\n- Remove zero-width space characters from table cells\n\n### lexicons/hypercerts-lexicons/collection.md\n- Add frontmatter: `title: Collection`\n- No special syntax changes needed\n\n## Acceptance Criteria\n- All 17 files are in `documentation/pages/` directory with correct paths\n- README.md files renamed to index.md (3 files)\n- No GitBook-specific syntax remains (`{% hint %}`, `{% endcolumns %}`, `{% endcolumn %}`, `{% endhint %}`, etc.)\n- No `\u0026#x20;` entities, no zero-width spaces, no orphan `\u003ca\u003e` anchor tags, no trailing `\u003cbr\u003e`\n- All files have proper YAML frontmatter with at minimum a `title` field\n- All broken links either resolved to correct targets or removed with text preserved\n- Malformed tables in activity-claim.md and rights.md have proper header rows\n- All image paths updated from `.gitbook/assets/` to `/images/`\n- All internal links use paths without `.md` extensions\n- All files render correctly via `npm run dev`\n","status":"closed","priority":0,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-13T13:42:41.246087+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T13:13:55.289826+13:00","closed_at":"2026-02-14T13:13:55.289831+13:00","dependencies":[{"issue_id":"docs-qsc","depends_on_id":"docs-vbw","type":"blocks","created_at":"2026-02-13T13:42:41.24883+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-qsc","depends_on_id":"docs-c34","type":"blocks","created_at":"2026-02-13T13:42:41.247806+13:00","created_by":"Sharfy Adamantine"}]} +{"id":"docs-r1z","title":"Epic 9: Cleanup and Verification","description":"## Summary\nFinal cleanup pass to remove all GitBook artifacts, verify link integrity, perform visual QA against the Stripe-inspired design language, and ensure the project is clean and production-ready.\n\n## Context\nThis project is a Next.js + Markdoc documentation site in the `documentation/` directory inside the repository at `/Users/sharfy/Code/hypercerts-atproto-documentation/documentation/`.\n\nBy the time this epic runs, Epics 1-8 have completed:\n- Next.js + Markdoc project scaffolded (Epic 1)\n- Custom Markdoc tags created: callout, columns, column, figure (Epic 2)\n- All 17 content files migrated to `pages/` with cleaned syntax (Epic 3)\n- Three-column layout with sidebar, content, and right TOC built (Epic 4)\n- Stripe-inspired CSS styling applied (Epic 5)\n- Images moved to `public/images/` and `.gitbook/` deleted (Epic 6)\n- All broken links fixed (Epic 7)\n- Build verified and deployment configured (Epic 8)\n\nThis epic is the final quality gate before the migration is considered complete.\n\n## Tasks\n\n### 1. Remove GitBook Artifacts\n\nDelete all files and directories that are GitBook-specific and no longer needed:\n\n```bash\n# Verify .gitbook/ is already deleted (should be gone from Epic 6)\nls -la documentation/.gitbook 2\u003e\u00261 # Should say \"No such file or directory\"\n\n# Delete SUMMARY.md (replaced by programmatic navigation in Epic 4)\nrm -f documentation/SUMMARY.md\n\n# Delete the original GitBook content files that were copied to pages/\n# These are the OLD files at the documentation/ root level (NOT the ones in pages/)\nrm -f documentation/README.md\nrm -f documentation/deep-dive-the-work-scope.md\nrm -rf documentation/getting-started/\nrm -rf documentation/lexicons/\n```\n\n**Important:** Only delete the old root-level files. Do NOT delete anything inside `documentation/pages/`, `documentation/components/`, `documentation/markdoc/`, etc.\n\nAlso check for and remove any remaining GitBook-specific frontmatter in content files. GitBook frontmatter looks like:\n```yaml\nlayout:\n width: default\n title:\n visible: true\nmetaLinks:\n alternates:\n - https://app.gitbook.com/s/...\n```\nIf any of this remains in any file under `pages/`, strip it and leave only `title` and `description` fields.\n\n### 2. Run Artifact Grep Checks\n\nSearch all content files for leftover GitBook artifacts. Every one of these commands should return ZERO results:\n\n```bash\n# GitBook broken page links\ngrep -r '/broken/pages/' documentation/pages/\n\n# GitBook editor URLs\ngrep -r 'app.gitbook.com' documentation/pages/\n\n# GitBook asset paths\ngrep -r '.gitbook/' documentation/pages/\ngrep -r '.gitbook/' documentation/components/\n\n# HTML entity artifacts\ngrep -r '\u0026#x20;' documentation/pages/\n\n# Zero-width spaces (use hex search)\ngrep -rP '\\x{200b}' documentation/pages/ 2\u003e/dev/null || grep -r $'\\u200b' documentation/pages/\n\n# GitBook proprietary tag syntax (should all be converted to Markdoc)\ngrep -r '{% hint' documentation/pages/\ngrep -r '{% endhint' documentation/pages/\ngrep -r '{% endcolumn' documentation/pages/\ngrep -r '{% endcolumns' documentation/pages/\n\n# Orphan HTML tags\ngrep -r '\u003cbr\u003e$' documentation/pages/\ngrep -r '\u003ca href=\"#' documentation/pages/\n```\n\nFix any remaining issues found.\n\n### 3. Verify All Internal Links\n\nStart the dev server and manually navigate to every page via the sidebar:\n\n```bash\ncd documentation \u0026\u0026 npm run dev\n```\n\nPages to check (17 total):\n1. `/` (Welcome / landing page)\n2. `/getting-started/why-were-building-hypercerts`\n3. `/getting-started/introduction-to-impact-claims`\n4. `/getting-started/the-impact-and-work-space`\n5. `/getting-started/the-hypercerts-infrastructure`\n6. `/getting-started/installing-the-sdk`\n7. `/lexicons/introduction-to-lexicons`\n8. `/lexicons/general-lexicons`\n9. `/lexicons/general-lexicons/shared-defs`\n10. `/lexicons/general-lexicons/location`\n11. `/lexicons/hypercerts-lexicons`\n12. `/lexicons/hypercerts-lexicons/activity-claim`\n13. `/lexicons/hypercerts-lexicons/contribution`\n14. `/lexicons/hypercerts-lexicons/evaluation`\n15. `/lexicons/hypercerts-lexicons/measurement`\n16. `/lexicons/hypercerts-lexicons/attachment`\n17. `/lexicons/hypercerts-lexicons/rights`\n18. `/lexicons/hypercerts-lexicons/collection`\n19. `/deep-dive-the-work-scope`\n\nFor each page verify:\n- Page loads without errors (check browser console)\n- No 404s for any internal links on the page\n- Anchor links (e.g., `#location-type`) scroll to the correct heading\n\n### 4. Visual QA Against Stripe Design Language\n\nFor each page, verify the design matches the Stripe-inspired patterns:\n\n**Layout:**\n- [ ] Three-column layout on desktop: left sidebar (~240px), content (~720px max), right TOC (~200px)\n- [ ] Sidebar has subtle 1px right border (#ebeef1), not a heavy shadow\n- [ ] Content area is well-centered with comfortable padding\n- [ ] Header is minimal with site title and 1px bottom border\n\n**Typography:**\n- [ ] Body text is 16px, dark gray (#414552), NOT pure black\n- [ ] Headings use proper hierarchy (H1 largest, H2 with large top margin creating section breaks)\n- [ ] System font stack is used (no custom font loading delays)\n- [ ] Text is anti-aliased (`-webkit-font-smoothing: antialiased`)\n- [ ] Line height is comfortable (~1.65 for body)\n\n**Colors:**\n- [ ] Page background is pure white\n- [ ] Links are blue (#0570de), darken on hover\n- [ ] The page is overwhelmingly neutral/grayscale -- color appears only for links, callouts, and interactive elements\n- [ ] No decorative elements, no gradients, no heavy shadows\n\n**Components:**\n- [ ] Tables are clean: no outer borders, header has bottom border only, no zebra striping\n- [ ] Code blocks have light gray background (#f6f8fa), rounded corners, monospace font\n- [ ] Inline code has subtle gray background pill\n- [ ] Callout boxes (on deep-dive page) have colored left border and tinted background\n- [ ] Column layouts (on landing page) display side-by-side on desktop, stack on mobile\n- [ ] Images render correctly with proper sizing\n\n**Navigation:**\n- [ ] Sidebar section headers are small, semibold, secondary gray\n- [ ] Active page is highlighted in sidebar (bold text + subtle background)\n- [ ] Right TOC shows H2 headings and highlights current section on scroll\n- [ ] Prev/next pagination works at bottom of content area\n- [ ] On mobile (\u003c 768px): sidebar collapses, content goes full width\n\n### 5. Verify Heading Anchor IDs\n\nMarkdoc auto-generates heading IDs from heading text. Verify that the generation is consistent:\n- \"### Location Type\" should generate `#location-type` (lowercase, hyphenated)\n- Any in-page anchor links should match the generated IDs\n- Click each anchor link on pages that have them to confirm they work\n\n### 6. Final File Inventory\n\nVerify the project structure is clean:\n```\ndocumentation/\n├── components/\n│ ├── Callout.js\n│ ├── Column.js\n│ ├── Columns.js\n│ ├── Figure.js\n│ ├── Layout.js\n│ ├── Sidebar.js\n│ └── TableOfContents.js\n├── lib/\n│ └── navigation.js\n├── markdoc/\n│ ├── tags/\n│ │ ├── callout.markdoc.js\n│ │ ├── column.markdoc.js\n│ │ ├── columns.markdoc.js\n│ │ ├── figure.markdoc.js\n│ │ └── index.js\n│ └── nodes/ (may be empty or have heading overrides)\n├── pages/\n│ ├── _app.js\n│ ├── index.md\n│ ├── deep-dive-the-work-scope.md\n│ ├── getting-started/\n│ │ ├── why-were-building-hypercerts.md\n│ │ ├── introduction-to-impact-claims.md\n│ │ ├── the-impact-and-work-space.md\n│ │ ├── the-hypercerts-infrastructure.md\n│ │ └── installing-the-sdk.md\n│ └── lexicons/\n│ ├── introduction-to-lexicons.md\n│ ├── general-lexicons/\n│ │ ├── index.md\n│ │ ├── shared-defs.md\n│ │ └── location.md\n│ └── hypercerts-lexicons/\n│ ├── index.md\n│ ├── activity-claim.md\n│ ├── contribution.md\n│ ├── evaluation.md\n│ ├── measurement.md\n│ ├── attachment.md\n│ ├── rights.md\n│ └── collection.md\n├── public/\n│ └── images/\n│ ├── hypercerts_for_projects.png\n│ └── hypercert-erd.png\n├── styles/\n│ └── globals.css\n├── next.config.js\n├── package.json\n├── package-lock.json\n└── .gitignore\n```\n\nConfirm:\n- No `.gitbook/` directory\n- No `SUMMARY.md`\n- No root-level `.md` content files (only inside `pages/`)\n- No orphaned files outside the standard Next.js structure\n\n### 7. Final Build Check\n```bash\ncd documentation\nnpm run build\n```\nMust succeed with zero errors after all cleanup.\n\n## Acceptance Criteria\n- Zero grep hits for `/broken/pages/`, `app.gitbook.com`, `.gitbook/`, `\u0026#x20;`, zero-width spaces, `{% hint`, `{% endhint`, `{% endcolumn`, `{% endcolumns` in content files\n- `SUMMARY.md` is deleted\n- `.gitbook/` directory is deleted\n- Old root-level content files (README.md, getting-started/, lexicons/, deep-dive-the-work-scope.md) are deleted\n- All 17+ pages render correctly in the browser with no console errors\n- All internal links resolve correctly (no 404s)\n- All heading anchor links work\n- The visual design matches the Stripe-inspired patterns (white bg, gray text, blue links, clean tables, colored callouts, three-column layout)\n- The project directory structure is clean with no orphaned files\n- `npm run build` succeeds with zero errors\n","status":"closed","priority":2,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-13T13:46:01.998664+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T13:19:59.577445+13:00","closed_at":"2026-02-14T13:19:59.577453+13:00","dependencies":[{"issue_id":"docs-r1z","depends_on_id":"docs-7dv","type":"blocks","created_at":"2026-02-13T13:46:02.003372+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-cij","type":"blocks","created_at":"2026-02-13T13:46:02.004627+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-d4r","type":"blocks","created_at":"2026-02-13T13:46:02.005893+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-c34","type":"blocks","created_at":"2026-02-13T13:46:02.000863+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-7b4","type":"blocks","created_at":"2026-02-13T13:46:02.00526+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-yne","type":"blocks","created_at":"2026-02-13T13:46:02.004004+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-vbw","type":"blocks","created_at":"2026-02-13T13:46:02.002015+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-qsc","type":"blocks","created_at":"2026-02-13T13:46:02.002729+13:00","created_by":"Sharfy Adamantine"}]} {"id":"docs-rsa","title":"Epic: Rewrite scaffold starter app documentation","description":"Rewrite the scaffold docs page (documentation/pages/tools/scaffold.md) to be genuinely useful for developers. Current docs are too shallow — missing architecture, OAuth flow, tech stack, and have placeholder screenshots. New structure: Tech Stack → Screenshots → Env Vars → Local Setup → Architecture (OAuth, server-side data boundary, Constellation backlinks) → Comprehensive Project Structure → Feature Walkthrough. Remove code snippets (too shallow, will break with SDK changes) and repo context/DID resolution docs (being removed from codebase). Success: a developer can clone the scaffold, understand the architecture, and start building without reading source code.","status":"closed","priority":1,"issue_type":"epic","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","created_at":"2026-02-17T12:00:47.006934865+06:00","created_by":"karma.gainforest.id","updated_at":"2026-02-17T12:15:39.14809733+06:00","closed_at":"2026-02-17T12:15:39.14809733+06:00","close_reason":"7267eca All 6 child tasks completed — scaffold docs rewritten","labels":["scope:medium"]} {"id":"docs-rsa.1","title":"Add tech stack table to scaffold docs","description":"## Files\n- documentation/pages/tools/scaffold.md (modify)\n\n## What to do\nAdd a \"Tech Stack\" section immediately after the intro paragraph (after the live/source links, before everything else). Format as a table with two columns: Category and Technology.\n\nCategories and values:\n| Category | Technology |\n|----------|-----------|\n| Framework | Next.js 16 (App Router), React 19, TypeScript |\n| Styling | Tailwind CSS 4, shadcn/ui (Radix primitives) |\n| State Management | TanStack React Query v5 |\n| Auth / Protocol | AT Protocol OAuth, `@atproto/oauth-client-node` |\n| Hypercerts SDK | `@hypercerts-org/sdk-core` (pre-release) |\n| Infrastructure | Redis (session + OAuth state storage) |\n| Fonts | Geist, Geist Mono, Syne, Outfit (via next/font) |\n| Icons | Lucide React |\n\n## Dont\n- Add version numbers that will go stale quickly (except where meaningful like \"Next.js 16\" or \"React 19\")\n- Add dev dependencies or build tools\n- Change any other section of the doc","acceptance_criteria":"1. Tech Stack section exists immediately after the intro paragraph\n2. Table has Category and Technology columns\n3. All 8 categories listed above are present\n4. No other sections of the doc are modified","status":"closed","priority":2,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":30,"created_at":"2026-02-17T12:01:01.278350786+06:00","created_by":"karma.gainforest.id","updated_at":"2026-02-17T12:06:33.685300718+06:00","closed_at":"2026-02-17T12:06:33.685300718+06:00","close_reason":"43cfd75 Add tech stack table to scaffold docs","labels":["scope:small"],"dependencies":[{"issue_id":"docs-rsa.1","depends_on_id":"docs-rsa","type":"parent-child","created_at":"2026-02-17T12:01:01.280099149+06:00","created_by":"karma.gainforest.id"}]} {"id":"docs-rsa.2","title":"Reorder sections: move env vars and local setup above architecture","description":"## Files\n- documentation/pages/tools/scaffold.md (modify)\n\n## What to do\nRestructure the document so sections appear in this order:\n\n1. H1 title + intro paragraph (with live/source links) — keep as-is\n2. ## Tech Stack — (added by docs-rsa.1)\n3. ## What the app does — keep existing content with figure tags\n4. ## Environment Variables — move up from current position, keep table as-is\n5. ## Run it locally — move up, keep content as-is, sits right after env vars\n6. ## Architecture — (will be added by later tasks, leave placeholder: `## Architecture\\n\\n*Coming soon.*`)\n7. ## Project Structure — keep existing but will be rewritten by later task\n8. Remove the \"Key patterns\" section entirely (the getRepoContext, creating a hypercert, and listing hypercerts code snippets)\n\nThe \"What the app does\" section with screenshots stays between Tech Stack and Env Vars because a developer wants to see what they are building before setting it up.\n\n## Dont\n- Rewrite any section content (just move them)\n- Remove the figure tags in \"What the app does\"\n- Remove the callout notes (127.0.0.1 note, pre-release SDK note)\n- Remove the \"Screenshots needed\" callout (will be removed when screenshots are added)","acceptance_criteria":"1. Sections appear in order: Title/intro → Tech Stack → What the app does → Environment Variables → Run it locally → Architecture (placeholder) → Project Structure\n2. Key patterns section (getRepoContext, create, list code snippets) is completely removed\n3. All existing content in kept sections is unchanged (just moved)\n4. The 127.0.0.1 callout note remains with the Run it locally section\n5. The pre-release SDK callout is removed (it was in Key patterns)","status":"closed","priority":2,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":30,"created_at":"2026-02-17T12:01:16.627412001+06:00","created_by":"karma.gainforest.id","updated_at":"2026-02-17T12:08:01.546927714+06:00","closed_at":"2026-02-17T12:08:01.546927714+06:00","close_reason":"88b2d2e Reorder sections: env vars and local setup above architecture","labels":["scope:small"],"dependencies":[{"issue_id":"docs-rsa.2","depends_on_id":"docs-rsa","type":"parent-child","created_at":"2026-02-17T12:01:16.628684422+06:00","created_by":"karma.gainforest.id"},{"issue_id":"docs-rsa.2","depends_on_id":"docs-rsa.1","type":"blocks","created_at":"2026-02-17T12:01:16.630931017+06:00","created_by":"karma.gainforest.id"}]} {"id":"docs-rsa.3","title":"Write OAuth flow architecture section","description":"## Files\n- documentation/pages/tools/scaffold.md (modify)\n\n## What to do\nReplace the Architecture placeholder with a full \"## Architecture\" section. The first subsection is \"### OAuth Flow\". Write a clear, end-to-end explanation of how ATProto OAuth works in the scaffold. This is the most important section of the entire doc — developers struggle most with auth.\n\nCover these steps in order:\n\n1. **Client metadata discovery** — The ATProto authorization server fetches `\u003cbase-url\u003e/client-metadata.json` (served by `app/client-metadata.json/route.ts`). This returns RFC 7591 client metadata: client_id, redirect_uris, scopes (`atproto transition:generic`), DPoP-bound access tokens. Mention that for loopback dev the client_id format differs from production.\n\n2. **JWKS endpoint** — `\u003cbase-url\u003e/jwks.json` (served by `app/jwks.json/route.ts`) exposes the public key. The auth server uses this to verify client assertion JWTs. The private key comes from `ATPROTO_JWK_PRIVATE` env var (ES256).\n\n3. **Login initiation** — User enters handle → client calls `POST /api/auth/login` → server calls `sdk.authorize(handle)` → returns an authorization URL → browser redirects to the users PDS for consent.\n\n4. **OAuth callback** — PDS redirects back to `/api/auth/callback` with auth code → server calls `sdk.callback(params)` to exchange code for session → session (tokens, refresh tokens) stored in Redis via `RedisSessionStore` (key: `session:\u003cdid\u003e`, no TTL) → `user-did` httpOnly cookie set → user redirected to app.\n\n5. **Session restore on subsequent requests** — Every server-side request reads the `user-did` cookie → calls `sdk.restoreSession(did)` which reads from Redis → tokens auto-refreshed if expired. This is wrapped in Reacts `cache()` for per-request deduplication (multiple calls in one request only hit Redis once).\n\n6. **Temporary OAuth state** — During the auth flow, temporary state is stored in Redis via `RedisStateStore` (key: `oauth-state:\u003cstate\u003e`, 10-minute TTL). This prevents CSRF and is cleaned up automatically.\n\n7. **Logout** — `GET /api/auth/logout` → `session.signOut()` revokes tokens → Redis session deleted → `user-did` cookie deleted.\n\n8. **Loopback compliance** — ATProto OAuth requires `127.0.0.1` not `localhost` per RFC 8252. The app has a proxy that auto-redirects. The `config.ts` auto-detects loopback vs production and adjusts client_id format accordingly.\n\nWrite in prose with subheadings, not as a numbered list. Use a callout for the 127.0.0.1/RFC 8252 note. Do NOT include code snippets — describe the flow conceptually with file paths as references.\n\n## Dont\n- Include TypeScript code blocks — this is architecture docs, not a code tutorial\n- Document the repo-context or DID resolution chain (being removed)\n- Change any section outside of Architecture","acceptance_criteria":"1. Architecture section exists with an OAuth Flow subsection\n2. All 8 steps listed above are covered in prose\n3. File paths are mentioned as references (e.g. app/api/auth/callback/route.ts)\n4. No TypeScript code blocks\n5. RFC 8252 / 127.0.0.1 note is in a callout\n6. Redis role in session + state storage is explained\n7. The cache() deduplication pattern is mentioned","status":"closed","priority":1,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":60,"created_at":"2026-02-17T12:01:42.60171065+06:00","created_by":"karma.gainforest.id","updated_at":"2026-02-17T12:09:53.172089509+06:00","closed_at":"2026-02-17T12:09:53.172089509+06:00","close_reason":"c6a57c7 docs: write OAuth flow architecture section","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-rsa.3","depends_on_id":"docs-rsa","type":"parent-child","created_at":"2026-02-17T12:01:42.602975023+06:00","created_by":"karma.gainforest.id"},{"issue_id":"docs-rsa.3","depends_on_id":"docs-rsa.2","type":"blocks","created_at":"2026-02-17T12:01:42.605286027+06:00","created_by":"karma.gainforest.id"}]} -{"id":"docs-rsa.4","title":"Write server-side data boundary architecture section","description":"## Files\n- documentation/pages/tools/scaffold.md (modify)\n\n## What to do\nAdd a \"### Server-Side Data Boundary\" subsection inside the Architecture section, after the OAuth Flow subsection.\n\nThe core insight to communicate: **ALL data fetching in this app happens server-side.** The ATProto session (tokens, refresh logic) lives on the server in Redis and is only accessible via server-side code. Client components never talk to the PDS directly.\n\nCover these points:\n\n1. **Why server-only** — The ATProto OAuth session is stored in Redis and accessed via an httpOnly cookie. There is no browser-side session. This means any operation that needs authentication (reading/writing hypercerts, profiles, etc.) must go through the server.\n\n2. **Two server-side patterns** — The app uses two ways to run server-side code:\n - **API Routes** (`app/api/`) — Traditional REST-style endpoints. Used for operations that need FormData (file uploads for evidence, images) or complex request/response handling. Examples: `POST /api/certs/create`, `POST /api/certs/add-attachment`, `POST /api/profile/update`.\n - **Server Actions** (`lib/create-actions.ts`, marked with `\"use server\"`) — Called directly from client components without an HTTP round-trip. Used for simpler operations like fetching profile info, adding evaluations/measurements. Examples: `getActiveProfileInfo()`, `addEvaluation()`, `addMeasurement()`.\n\n3. **Client-side data layer** — Client components use TanStack React Query hooks (in `queries/`) to call these server-side endpoints. The pattern is three tiers:\n - **API client** (`lib/api/client.ts`) — Base fetch wrappers with consistent error handling\n - **Domain functions** (`lib/api/hypercerts.ts`, `lib/api/profile.ts`, etc.) — Construct requests and call the API client\n - **Query hooks** (`queries/hypercerts/`, `queries/profile/`, etc.) — TanStack Query wrappers that manage caching, loading states, error toasts, and cache invalidation on mutations\n\n4. **Server components fetch directly** — Pages like `/hypercerts` and `/hypercerts/[uri]` are server components that call the SDK directly on the server (no API route needed). They pass the fetched data as props to client components for interactivity.\n\nWrite in prose. No code blocks. Reference file paths.\n\n## Dont\n- Include code snippets\n- Document specific API request/response shapes\n- Mention repo-context or DID resolution (being removed)\n- Change any section outside of Architecture","acceptance_criteria":"1. Server-Side Data Boundary subsection exists after OAuth Flow\n2. Explains WHY data fetching is server-only (session in Redis, httpOnly cookie)\n3. Describes both API Routes and Server Actions as the two server-side patterns\n4. Describes the 3-tier client data layer (API client → domain functions → query hooks)\n5. Mentions that server components fetch directly without API routes\n6. No code blocks\n7. File paths referenced for each layer","status":"closed","priority":2,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":45,"created_at":"2026-02-17T12:02:02.796441287+06:00","created_by":"karma.gainforest.id","updated_at":"2026-02-17T12:11:56.446804532+06:00","closed_at":"2026-02-17T12:11:56.446804532+06:00","close_reason":"9b5cfd7 docs: add server-side data boundary architecture section","labels":["scope:small"],"dependencies":[{"issue_id":"docs-rsa.4","depends_on_id":"docs-rsa","type":"parent-child","created_at":"2026-02-17T12:02:02.798609989+06:00","created_by":"karma.gainforest.id"},{"issue_id":"docs-rsa.4","depends_on_id":"docs-rsa.3","type":"blocks","created_at":"2026-02-17T12:02:02.801339312+06:00","created_by":"karma.gainforest.id"}]} -{"id":"docs-rsa.5","title":"Write Constellation backlinks architecture section","description":"## Files\n- documentation/pages/tools/scaffold.md (modify)\n\n## What to do\nAdd a \"### Constellation Backlinks\" subsection inside the Architecture section, after the Server-Side Data Boundary subsection.\n\nExplain how the scaffold discovers related records for a hypercert. In ATProto, records reference other records via strong references (AT-URIs), but there is no built-in reverse lookup. The scaffold uses Constellation (an external backlinks service at `https://constellation.microcosm.blue`) to solve this.\n\nCover:\n\n1. **The problem** — When viewing a hypercert detail page, the app needs to find all evidence, evaluations, and measurements that reference that hypercert. ATProto does not provide reverse lookups natively.\n\n2. **How Constellation works** — Constellation indexes ATProto records and provides a backlinks API. You query it with a subject URI (the hypercert AT-URI) and a source collection path, and it returns all records that reference that subject. The scaffold queries three source paths:\n - Evidence: `org.hypercerts.claim.attachment:subjects[com.atproto.repo.strongRef].uri`\n - Evaluations: `org.hypercerts.claim.evaluation:subject.uri`\n - Measurements: `org.hypercerts.claim.measurement:subject.uri`\n\n3. **Where this happens in the code** — The client-side API layer (`lib/api/external/constellation.ts`) calls Constellation. TanStack Query hooks (`queries/hypercerts/use-evidence-query.ts`, `use-evaluations-query.ts`, `use-measurements-query.ts`) use a two-step pattern: first fetch backlinks from Constellation to get record URIs, then fetch each record individually via server actions to get the full data.\n\nWrite in prose. Keep it concise — this is a shorter subsection.\n\n## Dont\n- Include code snippets\n- Explain the Constellation API in detail (link to it if needed)\n- Change any section outside of Architecture","acceptance_criteria":"1. Constellation Backlinks subsection exists after Server-Side Data Boundary\n2. Explains the reverse-lookup problem in ATProto\n3. Lists the three source paths queried (evidence, evaluations, measurements)\n4. Mentions the two-step pattern (backlinks → individual record fetch)\n5. References the relevant file paths\n6. No code blocks","status":"closed","priority":2,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":30,"created_at":"2026-02-17T12:02:18.640700877+06:00","created_by":"karma.gainforest.id","updated_at":"2026-02-17T12:13:18.724095484+06:00","closed_at":"2026-02-17T12:13:18.724095484+06:00","close_reason":"a17a0d1 Add Constellation backlinks architecture section","labels":["scope:small"],"dependencies":[{"issue_id":"docs-rsa.5","depends_on_id":"docs-rsa","type":"parent-child","created_at":"2026-02-17T12:02:18.641962933+06:00","created_by":"karma.gainforest.id"},{"issue_id":"docs-rsa.5","depends_on_id":"docs-rsa.4","type":"blocks","created_at":"2026-02-17T12:02:18.6442514+06:00","created_by":"karma.gainforest.id"}]} +{"id":"docs-rsa.4","title":"Write server-side data boundary architecture section","description":"## Files\n- documentation/pages/tools/scaffold.md (modify)\n\n## What to do\nAdd a \"### Server-Side Data Boundary\" subsection inside the Architecture section, after the OAuth Flow subsection.\n\nThe core insight to communicate: **ALL data fetching in this app happens server-side.** The ATProto session (tokens, refresh logic) lives on the server in Redis and is only accessible via server-side code. Client components never talk to the PDS directly.\n\nCover these points:\n\n1. **Why server-only** — The ATProto OAuth session is stored in Redis and accessed via an httpOnly cookie. There is no browser-side session. This means any operation that needs authentication (reading/writing hypercerts, profiles, etc.) must go through the server.\n\n2. **Two server-side patterns** — The app uses two ways to run server-side code:\n - **API Routes** (`app/api/`) — Traditional REST-style endpoints. Used for operations that need FormData (file uploads for evidence, images) or complex request/response handling. Examples: `POST /api/certs/create`, `POST /api/certs/add-attachment`, `POST /api/profile/update`.\n - **Server Actions** (`lib/create-actions.ts`, marked with `\"use server\"`) — Called directly from client components without an HTTP round-trip. Used for simpler operations like fetching profile info, adding evaluations/measurements. Examples: `getActiveProfileInfo()`, `addEvaluation()`, `addMeasurement()`.\n\n3. **Client-side data layer** — Client components use TanStack React Query hooks (in `queries/`) to call these server-side endpoints. The pattern is three tiers:\n - **API client** (`lib/api/client.ts`) — Base fetch wrappers with consistent error handling\n - **Domain functions** (`lib/api/hypercerts.ts`, `lib/api/profile.ts`, etc.) — Construct requests and call the API client\n - **Query hooks** (`queries/hypercerts/`, `queries/profile/`, etc.) — TanStack Query wrappers that manage caching, loading states, error toasts, and cache invalidation on mutations\n\n4. **Server components fetch directly** — Pages like `/hypercerts` and `/hypercerts/[uri]` are server components that call the SDK directly on the server (no API route needed). They pass the fetched data as props to client components for interactivity.\n\nWrite in prose. No code blocks. Reference file paths.\n\n## Dont\n- Include code snippets\n- Document specific API request/response shapes\n- Mention repo-context or DID resolution (being removed)\n- Change any section outside of Architecture","acceptance_criteria":"1. Server-Side Data Boundary subsection exists after OAuth Flow\n2. Explains WHY data fetching is server-only (session in Redis, httpOnly cookie)\n3. Describes both API Routes and Server Actions as the two server-side patterns\n4. Describes the 3-tier client data layer (API client → domain functions → query hooks)\n5. Mentions that server components fetch directly without API routes\n6. No code blocks\n7. File paths referenced for each layer","status":"closed","priority":2,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":45,"created_at":"2026-02-17T12:02:02.796441287+06:00","created_by":"karma.gainforest.id","updated_at":"2026-02-17T12:11:56.446804532+06:00","closed_at":"2026-02-17T12:11:56.446804532+06:00","close_reason":"9b5cfd7 docs: add server-side data boundary architecture section","labels":["scope:small"],"dependencies":[{"issue_id":"docs-rsa.4","depends_on_id":"docs-rsa.3","type":"blocks","created_at":"2026-02-17T12:02:02.801339312+06:00","created_by":"karma.gainforest.id"},{"issue_id":"docs-rsa.4","depends_on_id":"docs-rsa","type":"parent-child","created_at":"2026-02-17T12:02:02.798609989+06:00","created_by":"karma.gainforest.id"}]} +{"id":"docs-rsa.5","title":"Write Constellation backlinks architecture section","description":"## Files\n- documentation/pages/tools/scaffold.md (modify)\n\n## What to do\nAdd a \"### Constellation Backlinks\" subsection inside the Architecture section, after the Server-Side Data Boundary subsection.\n\nExplain how the scaffold discovers related records for a hypercert. In ATProto, records reference other records via strong references (AT-URIs), but there is no built-in reverse lookup. The scaffold uses Constellation (an external backlinks service at `https://constellation.microcosm.blue`) to solve this.\n\nCover:\n\n1. **The problem** — When viewing a hypercert detail page, the app needs to find all evidence, evaluations, and measurements that reference that hypercert. ATProto does not provide reverse lookups natively.\n\n2. **How Constellation works** — Constellation indexes ATProto records and provides a backlinks API. You query it with a subject URI (the hypercert AT-URI) and a source collection path, and it returns all records that reference that subject. The scaffold queries three source paths:\n - Evidence: `org.hypercerts.claim.attachment:subjects[com.atproto.repo.strongRef].uri`\n - Evaluations: `org.hypercerts.claim.evaluation:subject.uri`\n - Measurements: `org.hypercerts.claim.measurement:subject.uri`\n\n3. **Where this happens in the code** — The client-side API layer (`lib/api/external/constellation.ts`) calls Constellation. TanStack Query hooks (`queries/hypercerts/use-evidence-query.ts`, `use-evaluations-query.ts`, `use-measurements-query.ts`) use a two-step pattern: first fetch backlinks from Constellation to get record URIs, then fetch each record individually via server actions to get the full data.\n\nWrite in prose. Keep it concise — this is a shorter subsection.\n\n## Dont\n- Include code snippets\n- Explain the Constellation API in detail (link to it if needed)\n- Change any section outside of Architecture","acceptance_criteria":"1. Constellation Backlinks subsection exists after Server-Side Data Boundary\n2. Explains the reverse-lookup problem in ATProto\n3. Lists the three source paths queried (evidence, evaluations, measurements)\n4. Mentions the two-step pattern (backlinks → individual record fetch)\n5. References the relevant file paths\n6. No code blocks","status":"closed","priority":2,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":30,"created_at":"2026-02-17T12:02:18.640700877+06:00","created_by":"karma.gainforest.id","updated_at":"2026-02-17T12:13:18.724095484+06:00","closed_at":"2026-02-17T12:13:18.724095484+06:00","close_reason":"a17a0d1 Add Constellation backlinks architecture section","labels":["scope:small"],"dependencies":[{"issue_id":"docs-rsa.5","depends_on_id":"docs-rsa.4","type":"blocks","created_at":"2026-02-17T12:02:18.6442514+06:00","created_by":"karma.gainforest.id"},{"issue_id":"docs-rsa.5","depends_on_id":"docs-rsa","type":"parent-child","created_at":"2026-02-17T12:02:18.641962933+06:00","created_by":"karma.gainforest.id"}]} {"id":"docs-rsa.6","title":"Rewrite project structure section with comprehensive annotated tree","description":"## Files\n- documentation/pages/tools/scaffold.md (modify)\n\n## What to do\nReplace the existing shallow \"Project structure\" section with a comprehensive, annotated directory tree. Group by concern and explain what each directory/file does.\n\nUse a code block for the tree, with inline comments. Structure:\n\n```\nhypercerts-scaffold/\n├── app/\n│ ├── layout.tsx # Root layout (server component, wraps providers)\n│ ├── page.tsx # Landing page (server component)\n│ ├── client-metadata.json/\n│ │ └── route.ts # OAuth client metadata endpoint (RFC 7591)\n│ ├── jwks.json/\n│ │ └── route.ts # JWKS public key endpoint for OAuth\n│ ├── api/\n│ │ ├── auth/\n│ │ │ ├── login/route.ts # POST — initiate OAuth login\n│ │ │ ├── callback/route.ts # GET — OAuth callback, sets session\n│ │ │ └── logout/route.ts # GET — revoke session, clear cookie\n│ │ ├── certs/\n│ │ │ ├── create/route.ts # POST — create hypercert (FormData)\n│ │ │ ├── add-location/route.ts # POST — attach location to hypercert\n│ │ │ └── add-attachment/route.ts # POST — attach evidence/files\n│ │ └── profile/\n│ │ ├── update/route.ts # POST — update Certified profile\n│ │ └── bsky/update/route.ts # POST — update Bluesky profile\n│ ├── hypercerts/\n│ │ ├── page.tsx # List all hypercerts (server component)\n│ │ ├── create/page.tsx # Multi-step creation wizard (client component)\n│ │ └── [hypercertUri]/page.tsx # Hypercert detail view (server component)\n│ ├── profile/page.tsx # Certified profile editor\n│ └── bsky-profile/page.tsx # Bluesky profile editor\n│\n├── lib/\n│ ├── config.ts # Centralized config, env validation, URL detection\n│ ├── hypercerts-sdk.ts # SDK singleton initialization\n│ ├── atproto-session.ts # Session restore helpers (server-only, cached)\n│ ├── redis.ts # Redis client singleton (server-only)\n│ ├── redis-state-store.ts # Redis-backed OAuth state + session stores\n│ ├── create-actions.ts # Server Actions (\"use server\")\n│ ├── blob-utils.ts # Blob/image URL resolution (server-only)\n│ ├── utils.ts # Shared utilities (cn, validators)\n│ ├── types.ts # Core TypeScript types\n│ └── api/ # Client-side API layer\n│ ├── client.ts # Base fetch wrappers (JSON, FormData)\n│ ├── auth.ts # Auth API functions\n│ ├── hypercerts.ts # Hypercert API functions\n│ ├── profile.ts # Profile API functions\n│ ├── query-keys.ts # Centralized TanStack Query key factory\n│ └── external/\n│ ├── bluesky.ts # Bluesky public API (search, profiles)\n│ └── constellation.ts # Constellation backlinks API\n│\n├── providers/\n│ ├── AllProviders.tsx # QueryClientProvider (client component)\n│ └── SignedInProvider.tsx # Auth gate + Navbar (server component)\n│\n├── queries/ # TanStack Query hooks (all client-side)\n│ ├── auth/ # Login/logout mutations\n│ ├── hypercerts/ # Create, attach, list queries/mutations\n│ ├── profile/ # Profile update mutations\n│ └── external/ # Bluesky search, Constellation queries\n│\n├── components/\n│ ├── ui/ # shadcn/ui primitives (button, dialog, etc.)\n│ ├── navbar.tsx # Top navigation\n│ ├── login-dialog.tsx # Login form\n│ ├── hypercerts-create-form.tsx # Create wizard wrapper\n│ ├── evidence-form.tsx # Evidence step\n│ ├── locations-form.tsx # Location step\n│ ├── measurement-form.tsx # Measurement step\n│ ├── evaluation-form.tsx # Evaluation step\n│ ├── hypercert-detail-view.tsx # Detail page client component\n│ ├── profile-form.tsx # Certified profile form\n│ └── bsky-profile-form.tsx # Bluesky profile form\n│\n├── lexicons/ # Auto-generated ATProto lexicon types\n│ └── types/org/hypercerts/claim/ # Hypercert record type definitions\n│\n├── scripts/\n│ └── generate-jwk.mjs # JWK key pair generator (ES256)\n│\n└── vendor/ # Packed SDK tarballs (pre-release)\n```\n\nAfter the tree, add a brief paragraph explaining the key architectural boundaries:\n- `app/` contains pages (server components by default) and API routes\n- `lib/` is split: top-level files are server-only, `lib/api/` is the client-side fetch layer\n- `providers/` has one server component (SignedInProvider) and one client component (AllProviders)\n- `queries/` is entirely client-side TanStack Query hooks\n- `components/` is entirely client-side React components\n- `lexicons/` is auto-generated — do not edit manually\n\n## Dont\n- List every single file in components/ui/ (just say \"shadcn/ui primitives\")\n- List loading.tsx or layout.tsx files unless they do something meaningful\n- Include the tests/ directory (minimal, not useful for docs)\n- Change any section outside of Project Structure","acceptance_criteria":"1. Project structure section contains a comprehensive annotated tree in a code block\n2. All directories listed above are present with inline comments\n3. A summary paragraph after the tree explains the server/client boundaries\n4. components/ui/ is summarized, not listed file-by-file\n5. No other sections are modified","status":"closed","priority":2,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":45,"created_at":"2026-02-17T12:02:50.906045436+06:00","created_by":"karma.gainforest.id","updated_at":"2026-02-17T12:14:39.474919695+06:00","closed_at":"2026-02-17T12:14:39.474919695+06:00","close_reason":"bf30776 Rewrite project structure section with comprehensive annotated tree","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-rsa.6","depends_on_id":"docs-rsa","type":"parent-child","created_at":"2026-02-17T12:02:50.907462063+06:00","created_by":"karma.gainforest.id"},{"issue_id":"docs-rsa.6","depends_on_id":"docs-rsa.5","type":"blocks","created_at":"2026-02-17T12:02:50.909892906+06:00","created_by":"karma.gainforest.id"}]} {"id":"docs-scg","title":"Epic: Bug Fixes \u0026 Design Polish","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T21:06:01.764873+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T21:13:13.114836+13:00","closed_at":"2026-02-14T21:13:13.114836+13:00","close_reason":"All 9 tasks complete. All acceptance tests pass. Build succeeds."} {"id":"docs-scg.1","title":"Fix SSR hydration mismatch in Layout.js — guard localStorage with typeof window check","description":"## Files\n- documentation/components/Layout.js (modify)\n\n## What to do\nLine 21 calls `localStorage.getItem(\"sidebar-collapsed\")` inside a `useEffect` without guarding against SSR. While `useEffect` only runs client-side in React, Next.js static export can still trigger hydration warnings if the initial render differs from server render.\n\nFix: Wrap the `localStorage` access in a `typeof window !== \"undefined\"` check inside the existing `useEffect` on lines 20-25. Also wrap the `localStorage.setItem` call on line 30 in the same guard.\n\nBefore (line 20-25):\n```js\nuseEffect(() =\u003e {\n const stored = localStorage.getItem(\"sidebar-collapsed\");\n if (stored === \"true\") {\n setSidebarCollapsed(true);\n }\n}, []);\n```\n\nAfter:\n```js\nuseEffect(() =\u003e {\n if (typeof window === \"undefined\") return;\n const stored = localStorage.getItem(\"sidebar-collapsed\");\n if (stored === \"true\") {\n setSidebarCollapsed(true);\n }\n}, []);\n```\n\nAlso update `toggleCollapsed` (line 27-31):\n```js\nconst toggleCollapsed = () =\u003e {\n const next = !sidebarCollapsed;\n setSidebarCollapsed(next);\n if (typeof window !== \"undefined\") {\n localStorage.setItem(\"sidebar-collapsed\", String(next));\n }\n};\n```\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require(\\\"fs\\\");\nconst src = fs.readFileSync(\\\"components/Layout.js\\\", \\\"utf8\\\");\nconst hasGuard = src.includes(\\\"typeof window\\\");\nif (!hasGuard) { console.error(\\\"FAIL: missing typeof window guard\\\"); process.exit(1); }\nconsole.log(\\\"PASS: localStorage guarded with typeof window check\\\");\n\"\n```\n\n## Dont\n- Do not change any other component files\n- Do not change the sidebar collapse behavior or UX\n- Do not add try/catch around localStorage (the guard is sufficient)\n- Do not remove the useEffect or change its dependency array","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T21:06:16.432116+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T21:10:35.991575+13:00","closed_at":"2026-02-14T21:10:35.991575+13:00","close_reason":"e4a6983 guard localStorage with typeof window check in Layout.js","dependencies":[{"issue_id":"docs-scg.1","depends_on_id":"docs-scg","type":"parent-child","created_at":"2026-02-14T21:06:16.433467+13:00","created_by":"Sharfy Adamantine"}]} @@ -96,7 +96,7 @@ {"id":"docs-vuj.1","title":"Enhance glossary with developer key concepts from infrastructure page","description":"## Files\n- documentation/pages/reference/glossary.md (modify)\n\n## What to do\n\nThe 'Key Concepts for Developers' section in the-hypercerts-infrastructure.md has richer, developer-oriented definitions for DID, PDS, Lexicon, XRPC, and Repository. Merge this content into the existing glossary page.\n\n### Specific changes:\n\n1. **DID entry** — Replace the current 1-line definition with the 2-paragraph version from infrastructure page. Keep the format: `did:plc:abc123xyz` example. Add the note about persistent identity across platforms.\n\n2. **PDS entry** — Replace with the 2-paragraph version. Include the note about XRPC, data portability, and that apps are views over your data.\n\n3. **Lexicon entry** — Replace with the version that includes the concrete example (`org.hypercerts.claim.activity` with field names). Add cross-link to [Introduction to Lexicons](/lexicons/introduction-to-lexicons).\n\n4. **XRPC entry** — Replace with the version that mentions `com.atproto.repo.createRecord` and `com.atproto.repo.getRecord` examples. Add note about SDK wrapping XRPC calls.\n\n5. **Repository** — Add new entry (does not exist in current glossary). Include the Merkle Search Tree description, versioning via commits, CID content hashing, and tamper-evidence.\n\n### Format rules:\n- Keep alphabetical ordering of all entries\n- Each entry: `#### Term Name` followed by 1-2 paragraphs\n- Keep existing entries that are NOT being replaced (Activity Claim, CID, Collection, Contribution, Evaluation, Evidence, Hypercert, Indexer, Measurement, Relay, Rights, SDS, Strong Reference, Work Scope)\n- Do NOT add any new terms beyond the 5 listed above\n- Cross-link to other docs pages where the infrastructure page did (e.g., Lexicon entry links to Introduction to Lexicons)\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require('fs');\nconst content = fs.readFileSync('pages/reference/glossary.md', 'utf8');\nconst required = ['#### Repository', 'Merkle Search Tree', 'org.hypercerts.claim.activity', 'com.atproto.repo.createRecord', 'did:plc:', 'Introduction to Lexicons'];\nconst missing = required.filter(r =\u003e !content.includes(r));\nif (missing.length) { console.error('Missing:', missing); process.exit(1); }\n// Check alphabetical order of #### headings\nconst headings = [...content.matchAll(/^#### (.+)$/gm)].map(m =\u003e m[1]);\nconst sorted = [...headings].sort((a, b) =\u003e a.localeCompare(b));\nif (JSON.stringify(headings) !== JSON.stringify(sorted)) { console.error('Not alphabetical:', headings); process.exit(1); }\nconsole.log('PASS: glossary enhanced correctly');\n\"\n```\n\n## Don't\n- Remove any existing glossary entries\n- Change the page title or frontmatter\n- Add entries not listed above (no new terms)\n- Change the H4 heading format for entries","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-16T11:31:33.853221+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-16T12:01:19.279813+13:00","closed_at":"2026-02-16T12:01:19.279813+13:00","close_reason":"91a76a2 Enhanced glossary with richer developer-oriented definitions from infrastructure page","labels":["scope:small"],"dependencies":[{"issue_id":"docs-vuj.1","depends_on_id":"docs-vuj","type":"parent-child","created_at":"2026-02-16T11:31:33.855+13:00","created_by":"Sharfy Adamantine"}]} {"id":"docs-vuj.2","title":"Split infrastructure page into 4 sub-pages under infrastructure/ directory","description":"## Files\n- documentation/pages/getting-started/the-hypercerts-infrastructure.md (modify — becomes short overview)\n- documentation/pages/getting-started/infrastructure/indexers-and-discovery.md (create)\n- documentation/pages/getting-started/infrastructure/blockchain-integration.md (create)\n- documentation/pages/getting-started/infrastructure/portability-and-scaling.md (create)\n\n## What to do\n\nThe current infrastructure page is ~193 lines covering 9 sections. Split it into a short overview page + 3 sub-pages. The overview keeps the core architecture explanation; sub-pages get the deep-dive topics.\n\n### 1. Rewrite `the-hypercerts-infrastructure.md` to be a SHORT overview (~60 lines max)\n\nKeep these sections from the original:\n- **The Two-Layer Architecture** (lines 11-29) — keep as-is\n- **How Hypercerts Data Flows** (lines 63-87) — keep as-is\n- **Data Integrity and Trust** (lines 89-103) — keep as-is\n\nREMOVE these sections entirely:\n- **Key Concepts for Developers** (lines 31-61) — being moved to glossary by another task. Replace with a single line: `For definitions of DID, PDS, Lexicon, XRPC, and Repository, see the [Glossary](/reference/glossary).`\n- **Developer Resources** (lines 189-192) — fold into a callout at the bottom\n\nAdd a \"Keep reading\" section at the bottom with links to the 3 sub-pages:\n```\n## Keep Reading\n\n- [Indexers \u0026 Discovery](/getting-started/infrastructure/indexers-and-discovery) — how data is aggregated and queried across the network\n- [Blockchain Integration](/getting-started/infrastructure/blockchain-integration) — minting patterns and on-chain ownership\n- [Portability \u0026 Scaling](/getting-started/infrastructure/portability-and-scaling) — PDS migration, performance, and privacy\n```\n\n### 2. Create `infrastructure/indexers-and-discovery.md`\n\nFrontmatter:\n```yaml\n---\ntitle: Indexers \u0026 Discovery\n---\n```\n\nContent: Move the **Indexers and Discovery** section (lines 105-121) from the original page. Keep all 3 subsections (Why indexers?, What indexers do, Running your own indexer). Keep the code-style API endpoint examples.\n\n### 3. Create `infrastructure/blockchain-integration.md`\n\nFrontmatter:\n```yaml\n---\ntitle: Blockchain Integration\n---\n```\n\nContent: Move the **Blockchain Integration Patterns** section (lines 123-139) from the original page. Keep all 4 patterns (Mint-on-create, Lazy minting, Batch anchoring, Hybrid ownership). Keep the cross-link to the Rights lexicon.\n\n### 4. Create `infrastructure/portability-and-scaling.md`\n\nFrontmatter:\n```yaml\n---\ntitle: Portability \u0026 Scaling\n---\n```\n\nContent: Combine these 3 sections from the original page:\n- **Migration and Portability** (lines 141-158)\n- **Performance and Scalability** (lines 160-172)\n- **Privacy and Access Control** (lines 174-186)\n\nKeep all subsections and content. Use H2 headings for the 3 major sections.\n\n### Format rules:\n- Each new page starts with `# {% $markdoc.frontmatter.title %}` after frontmatter\n- Use H2 (##) for major sections, H4 (####) for subsections (matching existing style)\n- Preserve all cross-links to other pages\n- No content should be lost — every paragraph from the original must appear in exactly one of the 4 files\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require('fs');\nconst path = require('path');\n\n// Check all files exist\nconst files = [\n 'pages/getting-started/the-hypercerts-infrastructure.md',\n 'pages/getting-started/infrastructure/indexers-and-discovery.md',\n 'pages/getting-started/infrastructure/blockchain-integration.md',\n 'pages/getting-started/infrastructure/portability-and-scaling.md',\n];\nfor (const f of files) {\n if (!fs.existsSync(f)) { console.error('Missing:', f); process.exit(1); }\n}\n\n// Check overview is short\nconst overview = fs.readFileSync(files[0], 'utf8');\nconst overviewLines = overview.split('\\n').length;\nif (overviewLines \u003e 80) { console.error('Overview too long:', overviewLines, 'lines (max 80)'); process.exit(1); }\n\n// Check Key Concepts removed from overview\nif (overview.includes('## Key Concepts for Developers')) { console.error('Key Concepts section still in overview'); process.exit(1); }\n\n// Check glossary link added\nif (!overview.includes('[Glossary](/reference/glossary)')) { console.error('Missing glossary link in overview'); process.exit(1); }\n\n// Check sub-pages have required content\nconst indexers = fs.readFileSync(files[1], 'utf8');\nif (!indexers.includes('firehose')) { console.error('Indexers page missing firehose content'); process.exit(1); }\n\nconst blockchain = fs.readFileSync(files[2], 'utf8');\nif (!blockchain.includes('Lazy minting')) { console.error('Blockchain page missing Lazy minting'); process.exit(1); }\nif (!blockchain.includes('Batch anchoring')) { console.error('Blockchain page missing Batch anchoring'); process.exit(1); }\n\nconst portability = fs.readFileSync(files[3], 'utf8');\nif (!portability.includes('Switching PDSs')) { console.error('Portability page missing PDS migration'); process.exit(1); }\nif (!portability.includes('Public by default')) { console.error('Portability page missing privacy section'); process.exit(1); }\n\n// Check Keep Reading links in overview\nif (!overview.includes('/getting-started/infrastructure/indexers-and-discovery')) { console.error('Missing indexers link in overview'); process.exit(1); }\nif (!overview.includes('/getting-started/infrastructure/blockchain-integration')) { console.error('Missing blockchain link in overview'); process.exit(1); }\nif (!overview.includes('/getting-started/infrastructure/portability-and-scaling')) { console.error('Missing portability link in overview'); process.exit(1); }\n\nconsole.log('PASS: infrastructure split correctly');\n\"\n```\n\n## Don't\n- Change any content wording (only move content between files)\n- Add new content that wasn't in the original page\n- Remove the frontmatter from the overview page\n- Use H3 (###) headings — use H2 and H4 to match existing site style\n- Create an index.md in the infrastructure/ directory","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-16T11:32:03.659396+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-16T12:01:56.630712+13:00","closed_at":"2026-02-16T12:01:56.630712+13:00","close_reason":"2a1d330 Split infrastructure page into overview + 3 sub-pages","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-vuj.2","depends_on_id":"docs-vuj","type":"parent-child","created_at":"2026-02-16T11:32:03.660787+13:00","created_by":"Sharfy Adamantine"}]} {"id":"docs-vuj.3","title":"Update navigation.js — make Infrastructure a collapsible sidebar group with sub-pages","description":"## Files\n- documentation/lib/navigation.js (modify)\n\n## What to do\n\nIn the 'Understand' section of the navigation array, replace the flat 'The Hypercerts Infrastructure' entry with a collapsible group that has children — exactly like the existing 'Lexicons' entry in the 'Reference' section.\n\n### Current entry (line 20):\n```js\n{ title: 'The Hypercerts Infrastructure', path: '/getting-started/the-hypercerts-infrastructure' },\n```\n\n### Replace with:\n```js\n{\n title: 'The Hypercerts Infrastructure',\n path: '/getting-started/the-hypercerts-infrastructure',\n children: [\n { title: 'Indexers \u0026 Discovery', path: '/getting-started/infrastructure/indexers-and-discovery' },\n { title: 'Blockchain Integration', path: '/getting-started/infrastructure/blockchain-integration' },\n { title: 'Portability \u0026 Scaling', path: '/getting-started/infrastructure/portability-and-scaling' },\n ],\n},\n```\n\nThis makes \"The Hypercerts Infrastructure\" clickable (goes to the overview page) AND expandable (shows 3 sub-pages). The Sidebar component already supports this pattern — see the Lexicons entry.\n\n### No other changes to navigation.js\n\nThe rest of the navigation array must remain exactly as-is. Do not reorder, rename, or remove any other entries.\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst { navigation } = require('./lib/navigation.js');\n\n// Find the Understand section\nconst understand = navigation.find(n =\u003e n.section === 'Understand');\nif (!understand) { console.error('No Understand section'); process.exit(1); }\n\n// Find the Infrastructure entry\nconst infra = understand.children.find(c =\u003e c.title === 'The Hypercerts Infrastructure');\nif (!infra) { console.error('No Infrastructure entry'); process.exit(1); }\n\n// Check it has the right path\nif (infra.path !== '/getting-started/the-hypercerts-infrastructure') {\n console.error('Wrong path:', infra.path); process.exit(1);\n}\n\n// Check it has children\nif (!infra.children || infra.children.length !== 3) {\n console.error('Expected 3 children, got:', infra.children?.length); process.exit(1);\n}\n\n// Check child paths\nconst expectedPaths = [\n '/getting-started/infrastructure/indexers-and-discovery',\n '/getting-started/infrastructure/blockchain-integration',\n '/getting-started/infrastructure/portability-and-scaling',\n];\nconst actualPaths = infra.children.map(c =\u003e c.path);\nfor (const ep of expectedPaths) {\n if (!actualPaths.includes(ep)) { console.error('Missing child path:', ep); process.exit(1); }\n}\n\n// Check total Understand children count unchanged (still 8 top-level items)\nif (understand.children.length !== 8) {\n console.error('Understand section should have 8 children, got:', understand.children.length); process.exit(1);\n}\n\n// Check other sections untouched\nconst sections = navigation.filter(n =\u003e n.section).map(n =\u003e n.section);\nconst expected = ['Get Started', 'Understand', 'Guides', 'Tools', 'Reference'];\nif (JSON.stringify(sections) !== JSON.stringify(expected)) {\n console.error('Sections changed:', sections); process.exit(1);\n}\n\nconsole.log('PASS: navigation updated correctly');\n\"\n```\n\n## Don't\n- Change any other navigation entries\n- Reorder sections or items\n- Remove the path from the Infrastructure entry (it must remain clickable)\n- Add any new exports or functions","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-16T11:32:21.026059+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-16T12:04:41.918664+13:00","closed_at":"2026-02-16T12:04:41.918664+13:00","close_reason":"4f61df1 Make Infrastructure a collapsible sidebar group with 3 sub-pages","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-vuj.3","depends_on_id":"docs-vuj","type":"parent-child","created_at":"2026-02-16T11:32:21.027769+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-vuj.3","depends_on_id":"docs-vuj.2","type":"blocks","created_at":"2026-02-16T11:32:40.404821+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-vuj.4","title":"Integration: verify build succeeds and no broken internal links after infrastructure split","description":"## Files\n- (no files to modify — verification only)\n\n## What to do\n\nRun the Next.js build and verify:\n1. Build completes without errors\n2. All 3 new infrastructure sub-pages are generated in the output\n3. The glossary page builds\n4. No broken internal links (grep for links to old anchors that no longer exist)\n\n### Steps:\n\n1. Run `npm run build` (or `npx next build`) in the documentation/ directory\n2. Verify the build exits 0\n3. Check that these output files exist in `out/` or `.next/`:\n - getting-started/infrastructure/indexers-and-discovery\n - getting-started/infrastructure/blockchain-integration\n - getting-started/infrastructure/portability-and-scaling\n4. Grep all .md files for any links pointing to anchors that were removed from the infrastructure page:\n - `#key-concepts-for-developers` should not appear in any file\n - `#developer-resources` should not appear in any file\n5. If any broken links are found, fix them (update to point to the glossary or the correct sub-page)\n\n## Test\n```bash\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5 \u0026\u0026 echo \"BUILD_OK\"\n```\n\nThe output must contain \"BUILD_OK\" (meaning the build completed and the tail command ran).\n\nAdditionally:\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require('fs');\nconst glob = require('path');\n\n// Recursive file finder\nfunction findMd(dir) {\n let results = [];\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const full = dir + '/' + entry.name;\n if (entry.isDirectory() \u0026\u0026 entry.name !== 'node_modules' \u0026\u0026 entry.name !== '.next') {\n results = results.concat(findMd(full));\n } else if (entry.name.endsWith('.md')) {\n results.push(full);\n }\n }\n return results;\n}\n\nconst files = findMd('pages');\nlet broken = false;\nfor (const f of files) {\n const content = fs.readFileSync(f, 'utf8');\n if (content.includes('#key-concepts-for-developers')) {\n console.error('Broken anchor in', f, ': #key-concepts-for-developers');\n broken = true;\n }\n if (content.includes('#developer-resources')) {\n console.error('Broken anchor in', f, ': #developer-resources');\n broken = true;\n }\n}\nif (broken) process.exit(1);\nconsole.log('PASS: no broken anchors found');\n\"\n```\n\n## Don't\n- Modify any files unless broken links are found (this is primarily a verification task)\n- Skip the build step\n- Ignore build warnings that are actually errors","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-16T11:32:36.383122+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-16T12:06:47.257702+13:00","closed_at":"2026-02-16T12:06:47.257702+13:00","close_reason":"Verification complete: build succeeds, all infrastructure sub-pages generated, no broken anchor links","labels":["scope:small"],"dependencies":[{"issue_id":"docs-vuj.4","depends_on_id":"docs-vuj","type":"parent-child","created_at":"2026-02-16T11:32:36.384409+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-vuj.4","depends_on_id":"docs-vuj.1","type":"blocks","created_at":"2026-02-16T11:32:41.374978+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-vuj.4","depends_on_id":"docs-vuj.2","type":"blocks","created_at":"2026-02-16T11:32:41.519206+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-vuj.4","depends_on_id":"docs-vuj.3","type":"blocks","created_at":"2026-02-16T11:32:41.62861+13:00","created_by":"Sharfy Adamantine"}]} +{"id":"docs-vuj.4","title":"Integration: verify build succeeds and no broken internal links after infrastructure split","description":"## Files\n- (no files to modify — verification only)\n\n## What to do\n\nRun the Next.js build and verify:\n1. Build completes without errors\n2. All 3 new infrastructure sub-pages are generated in the output\n3. The glossary page builds\n4. No broken internal links (grep for links to old anchors that no longer exist)\n\n### Steps:\n\n1. Run `npm run build` (or `npx next build`) in the documentation/ directory\n2. Verify the build exits 0\n3. Check that these output files exist in `out/` or `.next/`:\n - getting-started/infrastructure/indexers-and-discovery\n - getting-started/infrastructure/blockchain-integration\n - getting-started/infrastructure/portability-and-scaling\n4. Grep all .md files for any links pointing to anchors that were removed from the infrastructure page:\n - `#key-concepts-for-developers` should not appear in any file\n - `#developer-resources` should not appear in any file\n5. If any broken links are found, fix them (update to point to the glossary or the correct sub-page)\n\n## Test\n```bash\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5 \u0026\u0026 echo \"BUILD_OK\"\n```\n\nThe output must contain \"BUILD_OK\" (meaning the build completed and the tail command ran).\n\nAdditionally:\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require('fs');\nconst glob = require('path');\n\n// Recursive file finder\nfunction findMd(dir) {\n let results = [];\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const full = dir + '/' + entry.name;\n if (entry.isDirectory() \u0026\u0026 entry.name !== 'node_modules' \u0026\u0026 entry.name !== '.next') {\n results = results.concat(findMd(full));\n } else if (entry.name.endsWith('.md')) {\n results.push(full);\n }\n }\n return results;\n}\n\nconst files = findMd('pages');\nlet broken = false;\nfor (const f of files) {\n const content = fs.readFileSync(f, 'utf8');\n if (content.includes('#key-concepts-for-developers')) {\n console.error('Broken anchor in', f, ': #key-concepts-for-developers');\n broken = true;\n }\n if (content.includes('#developer-resources')) {\n console.error('Broken anchor in', f, ': #developer-resources');\n broken = true;\n }\n}\nif (broken) process.exit(1);\nconsole.log('PASS: no broken anchors found');\n\"\n```\n\n## Don't\n- Modify any files unless broken links are found (this is primarily a verification task)\n- Skip the build step\n- Ignore build warnings that are actually errors","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-16T11:32:36.383122+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-16T12:06:47.257702+13:00","closed_at":"2026-02-16T12:06:47.257702+13:00","close_reason":"Verification complete: build succeeds, all infrastructure sub-pages generated, no broken anchor links","labels":["scope:small"],"dependencies":[{"issue_id":"docs-vuj.4","depends_on_id":"docs-vuj.1","type":"blocks","created_at":"2026-02-16T11:32:41.374978+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-vuj.4","depends_on_id":"docs-vuj.2","type":"blocks","created_at":"2026-02-16T11:32:41.519206+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-vuj.4","depends_on_id":"docs-vuj.3","type":"blocks","created_at":"2026-02-16T11:32:41.62861+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-vuj.4","depends_on_id":"docs-vuj","type":"parent-child","created_at":"2026-02-16T11:32:36.384409+13:00","created_by":"Sharfy Adamantine"}]} {"id":"docs-xdq","title":"Epic: Tools \u0026 Ecosystem — Document CLI, Scaffold, and Hyperboard","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-15T00:42:31.269164+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T00:54:18.054321+13:00","closed_at":"2026-02-15T00:54:18.054321+13:00","close_reason":"All 4 tasks completed: nav section added, CLI page (145 lines), Scaffold page (139 lines), Hyperboard page (51 lines). Build passes."} {"id":"docs-xdq.1","title":"Add 'Tools' section to navigation with CLI, Scaffold, and Hyperboard entries","description":"## Files\n- documentation/lib/navigation.js (modify)\n- documentation/pages/tools/hypercerts-cli.md (create)\n- documentation/pages/tools/scaffold.md (create)\n- documentation/pages/tools/hyperboard.md (create)\n\n## What to do\nAdd a new top-level section called \"Tools\" to the navigation array in `documentation/lib/navigation.js`, positioned between the \"Lexicons\" and \"Tutorials\" sections.\n\nThe section should contain three children:\n```js\n{\n section: \"Tools\",\n children: [\n { title: \"Hypercerts CLI\", path: \"/tools/hypercerts-cli\" },\n { title: \"Scaffold Starter App\", path: \"/tools/scaffold\" },\n { title: \"Hyperboard\", path: \"/tools/hyperboard\" },\n ],\n},\n```\n\nAlso create three stub pages so the build does not break. Each stub must have Markdoc frontmatter with a title and description, plus a one-line placeholder:\n\n**documentation/pages/tools/hypercerts-cli.md:**\n```\n---\ntitle: Hypercerts CLI\ndescription: A Go command-line tool for managing hypercerts on ATProto.\n---\nComing soon.\n```\n\n**documentation/pages/tools/scaffold.md:**\n```\n---\ntitle: Scaffold Starter App\ndescription: A Next.js starter app for building on ATProto with the Hypercerts SDK.\n---\nComing soon.\n```\n\n**documentation/pages/tools/hyperboard.md:**\n```\n---\ntitle: Hyperboard\ndescription: A visual board that showcases the contributors to a hypercert.\n---\nComing soon.\n```\n\n## Test\n```bash\ncd documentation \u0026\u0026 npx next build --webpack 2\u003e\u00261 | tail -5\n```\nMust exit 0 and show \"✓ Generating static pages\".\n\n## Dont\n- Do not modify any other navigation sections\n- Do not add content beyond the stub placeholder — other tasks handle that\n- Do not rename or reorder existing sections","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-15T00:42:43.269047+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T00:49:36.320288+13:00","closed_at":"2026-02-15T00:49:36.320288+13:00","close_reason":"a5807b2 Added Tools section to navigation between Lexicons and Tutorials with three stub pages","dependencies":[{"issue_id":"docs-xdq.1","depends_on_id":"docs-xdq","type":"parent-child","created_at":"2026-02-15T00:42:43.270576+13:00","created_by":"Sharfy Adamantine"}]} {"id":"docs-xdq.2","title":"Write 'Hypercerts CLI' tools page","description":"## Files\n- documentation/pages/tools/hypercerts-cli.md (modify — replace stub)\n\n## Design language — Stripe docs (docs.stripe.com)\nStudy the real Stripe documentation at docs.stripe.com for tone and structure. Key patterns:\n\n1. **One-sentence opener**: The first line tells you what the tool is and what it does. Example from Stripe CLI install page: \"The Stripe CLI lets you build, test, and manage your integration from the command line.\"\n2. **Capability bullet list**: Right after the opener, a short bullet list of what you can do with it. Example: \"You can use the Stripe CLI to: Create, retrieve, update, or delete any of your Stripe resources...\"\n3. **Code first, explain second**: Show the command, then explain what it does — not the other way around.\n4. **Numbered steps** for sequential processes (install, then login, then use).\n5. **Tables for reference data**: Flags, options, record types go in tables.\n6. **Short paragraphs**: 1-3 sentences max per paragraph. Scannable.\n7. **No preamble**: Never start with \"In this page you will learn...\" or \"This guide covers...\"\n8. **No link dumps**: No \"See also\", \"Next steps\", or \"Related pages\" sections at the end.\n9. **Inline prerequisites**: Mention requirements where they are needed, not in a separate section.\n\nAlso study the existing pages in this project for consistency:\n- `documentation/pages/getting-started/quickstart.md`\n- `documentation/pages/tutorials/creating-your-first-hypercert.md`\n\nAvailable Markdoc tags: `{% callout type=\"note\" %}...{% /callout %}`, `{% columns %}`, `{% column %}`.\n\n## What to do\nReplace the stub with a complete documentation page for the Hypercerts CLI (`hc`). Target ~120-160 lines of Markdoc.\n\n### Intro (follow Stripe pattern)\nStart with a one-sentence description, then a capability bullet list:\n\nThe Hypercerts CLI (`hc`) is a command-line tool for managing hypercerts on ATProto. You can use it to:\n\n- Create, read, update, and delete all Hypercerts record types (activities, measurements, evaluations, evidence, and more)\n- Authenticate with any ATProto PDS\n- Run interactively with a terminal UI or non-interactively with flags for CI/CD\n- Resolve identities and inspect any record on the network\n\nThen one sentence: Built in Go on [bluesky-social/indigo](https://github.com/bluesky-social/indigo) with interactive forms powered by [Charm](https://charm.sh) libraries. Source: [github.com/GainForest/hypercerts-cli](https://github.com/GainForest/hypercerts-cli).\n\n### Page structure\n\n1. **Install** section with three numbered options (Stripe uses numbered steps):\n\n 1. Quick install:\n ```bash\n curl -sSL https://raw.githubusercontent.com/GainForest/hypercerts-cli/main/install.sh | bash\n ```\n\n 2. Go install (requires Go 1.25+):\n ```bash\n go install github.com/GainForest/hypercerts-cli/cmd/hc@v0.1.1\n ```\n\n 3. Build from source:\n ```bash\n git clone https://github.com/GainForest/hypercerts-cli\n cd hypercerts-cli\n make build\n ```\n\n2. **Authenticate** section:\n ```bash\n hc account login -u yourhandle.certified.app -p your-app-password\n hc account status\n hc account logout\n ```\n Then a short paragraph: For CI/CD, set `HYPER_USERNAME`, `HYPER_PASSWORD`, and optionally `ATP_PDS_HOST` as environment variables.\n\n3. **Core commands** section — show the full CRUD pattern using activities as the primary example:\n ```bash\n # Create interactively (launches TUI form)\n hc activity create\n\n # Create with flags\n hc activity create \\\n --title \"Rainforest Carbon Study\" \\\n --description \"12-month carbon sequestration measurement\" \\\n --start-date 2025-01-01 \\\n --end-date 2025-12-31\n\n # List\n hc activity ls\n hc activity ls --json\n\n # Get details\n hc activity get \u003crkey\u003e\n\n # Edit\n hc activity edit \u003crkey\u003e --title \"Updated Title\"\n\n # Delete (cascades to linked measurements and attachments)\n hc activity delete \u003crkey\u003e -f\n ```\n\n4. **All record types** — a reference table:\n\n | Command | Record Type | Alias |\n |---------|------------|-------|\n | `hc activity` | `org.hypercerts.claim.activity` | — |\n | `hc measurement` | `org.hypercerts.claim.measurement` | `hc meas` |\n | `hc location` | `app.certified.location` | `hc loc` |\n | `hc attachment` | `org.hypercerts.claim.attachment` | `hc attach` |\n | `hc rights` | `org.hypercerts.claim.rights` | — |\n | `hc evaluation` | `org.hypercerts.claim.evaluation` | `hc eval` |\n | `hc collection` | `org.hypercerts.claim.collection` | `hc coll` |\n | `hc contributor` | `org.hypercerts.claim.contributorInformation` | `hc contrib` |\n | `hc funding` | `org.hypercerts.funding.receipt` | `hc fund` |\n | `hc workscope` | `org.hypercerts.helper.workScopeTag` | `hc ws` |\n\n One sentence: Every type supports `create`, `ls`, `get`, `edit`, `delete` with the same flag patterns shown above.\n\n5. **Generic operations** section:\n ```bash\n hc get at://did:plc:xxx/org.hypercerts.claim.activity/rkey\n hc ls handle.example.com\n hc ls handle.example.com --collection org.hypercerts.claim.activity\n hc resolve handle.example.com\n ```\n\n6. **Interactive UI** — one short paragraph: When you run commands without flags, the CLI launches interactive forms with keyboard navigation, live preview cards during activity creation, multi-select for bulk deletes, and select-or-create patterns when linking records.\n\n7. `{% callout type=\"note\" %}` for cascading deletes: \"Deleting an activity also removes all linked measurements and attachments. Use the `-f` flag to skip confirmation.\"\n\n## Test\n```bash\ncd documentation \u0026\u0026 npx next build --webpack 2\u003e\u00261 | tail -5\n```\nMust exit 0. Page must be \u003e100 lines and \u003c200 lines:\n```bash\nwc -l documentation/pages/tools/hypercerts-cli.md\n```\n\n## Dont\n- Do not copy the entire CLI README verbatim — distill it for a documentation audience\n- Do not document every flag for every command — show the pattern with activities, then reference the table\n- Do not use \"See also\", \"Next steps\", or \"Related pages\" sections\n- Do not create a standalone \"Prerequisites\" section\n- Do not start with \"In this page you will learn...\" or similar preamble\n- Do not modify navigation.js (done in docs-xdq.1)\n- Do not use {% figure %} tags (no images available)","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-15T00:43:17.589711+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T00:51:21.072623+13:00","closed_at":"2026-02-15T00:51:21.072623+13:00","close_reason":"Completed Hypercerts CLI documentation page (145 lines, build passes)","dependencies":[{"issue_id":"docs-xdq.2","depends_on_id":"docs-xdq","type":"parent-child","created_at":"2026-02-15T00:43:17.591208+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-xdq.2","depends_on_id":"docs-xdq.1","type":"blocks","created_at":"2026-02-15T00:44:02.188143+13:00","created_by":"Sharfy Adamantine"}]} @@ -105,17 +105,17 @@ {"id":"docs-y5x","title":"Epic: Adopt ATProto docs dotted banner, icon cards, and typography","description":"Adopt three visual elements from atproto.com/docs: (1) SVG dot-pattern banner behind the index page hero, (2) card-style navigation links with icons in bordered square containers in a 2-col grid, (3) larger card titles (24px) and monospace hero heading. The current index page uses plain markdown tables for links. Success: index page has a dotted hero banner and icon-based navigation cards matching ATProto's visual language.","status":"closed","priority":1,"issue_type":"epic","owner":"einstein.climateai.org","created_at":"2026-02-20T18:47:58.243696+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:35:05.304924+08:00","closed_at":"2026-02-20T19:35:05.304924+08:00","close_reason":"e2b1d9d Dotted banner and icon cards complete (PRs #19-23)","labels":["scope:medium"]} {"id":"docs-y5x.1","title":"Add DotPattern component and hero-banner CSS","description":"## Files\n- documentation/components/DotPattern.js (create)\n- documentation/styles/globals.css (modify)\n\n## What to do\n\n### 1. Create DotPattern.js component\nCreate a new React component that renders an SVG dot pattern, matching atproto.com/docs exactly:\n\n```jsx\nexport function DotPattern({ className }) {\n return (\n \u003csvg\n aria-hidden=\"true\"\n className={`dot-pattern ${className || \"\"}`}\n width=\"100%\"\n height=\"100%\"\n \u003e\n \u003cdefs\u003e\n \u003cpattern\n id=\"dot-grid\"\n width=\"10\"\n height=\"10\"\n patternUnits=\"userSpaceOnUse\"\n x=\"0\"\n y=\"0\"\n \u003e\n \u003crect width=\"2\" height=\"2\" fill=\"current\" /\u003e\n \u003c/pattern\u003e\n \u003c/defs\u003e\n \u003crect width=\"100%\" height=\"100%\" strokeWidth=\"0\" fill=\"url(#dot-grid)\" /\u003e\n \u003c/svg\u003e\n );\n}\n```\n\n### 2. Add CSS for the dot pattern and hero banner in globals.css\nAdd these rules AFTER the existing `/* ===== Component: Figure ===== */` section (before the `/* ===== Responsive ===== */` section):\n\n```css\n/* ===== Component: DotPattern ===== */\n.dot-pattern {\n position: absolute;\n inset: 0;\n fill: oklch(0.91 0.005 260);\n}\n\n/* ===== Component: Hero Banner ===== */\n.hero-banner {\n position: relative;\n margin: calc(-1 * var(--space-6)) 0 var(--space-8) calc(-72px);\n padding: var(--space-12) 72px var(--space-10) 72px;\n}\n\n.hero-banner-content {\n position: relative;\n z-index: 1;\n}\n\n.hero-title {\n font-family: var(--font-mono);\n font-size: 40px;\n font-weight: 500;\n color: var(--color-text-title);\n line-height: 1.15;\n margin: 0 0 var(--space-4) 0;\n letter-spacing: -0.02em;\n}\n\n.hero-subtitle {\n font-size: 18px;\n line-height: 1.6;\n color: var(--color-text-secondary);\n margin: 0;\n max-width: 600px;\n}\n```\n\nAlso add responsive overrides inside the existing mobile media query `@media (max-width: 768px)`:\n```css\n.hero-banner {\n margin: calc(-1 * var(--space-6)) calc(-1 * var(--space-4)) var(--space-6) calc(-1 * var(--space-4));\n padding: var(--space-8) var(--space-4) var(--space-6) var(--space-4);\n}\n\n.hero-title {\n font-size: 28px;\n}\n```\n\nAnd inside `@media (min-width: 769px) and (max-width: 1024px)`:\n```css\n.hero-banner {\n margin: calc(-1 * var(--space-6)) calc(-1 * var(--space-6)) var(--space-6) calc(-1 * var(--space-6));\n padding: var(--space-8) var(--space-6) var(--space-6) var(--space-6);\n}\n\n.hero-title {\n font-size: 32px;\n}\n```\n\n## Don't\n- Do NOT modify index.md yet (that is a separate task)\n- Do NOT register the component in _app.js yet (that is a separate task)\n- Do NOT change any existing CSS rules\n- Do NOT use Tailwind classes — this project uses plain CSS","acceptance_criteria":"1. DotPattern.js exists at documentation/components/DotPattern.js\n2. Component renders an SVG with a \u003cpattern\u003e element using 2x2px dots on 10px grid\n3. globals.css contains .dot-pattern, .hero-banner, .hero-banner-content, .hero-title, .hero-subtitle classes\n4. Hero title uses var(--font-mono) font family\n5. Dot pattern fill uses oklch color matching the design system\n6. Responsive rules exist for mobile and tablet\n7. `npm run build` succeeds","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":20,"created_at":"2026-02-20T18:48:26.142186+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:52:09.196549+08:00","closed_at":"2026-02-20T18:52:09.196549+08:00","close_reason":"87f8610 Add DotPattern component and hero-banner CSS","labels":["scope:small"],"dependencies":[{"issue_id":"docs-y5x.1","depends_on_id":"docs-y5x","type":"parent-child","created_at":"2026-02-20T18:48:26.143511+08:00","created_by":"einstein.climateai.org"}]} {"id":"docs-y5x.2","title":"Add icon-card grid CSS and update CardLink component","description":"## Files\n- documentation/components/CardLink.js (modify)\n- documentation/markdoc/tags/card-link.markdoc.js (modify)\n- documentation/styles/globals.css (modify)\n\n## What to do\n\n### 1. Update CardLink.js to support icon prop\nReplace the entire CardLink component with:\n\n```jsx\nimport Link from \"next/link\";\n\nexport function CardLink({ title, href, icon, children }) {\n return (\n \u003cLink href={href} className=\"card-link\"\u003e\n {icon \u0026\u0026 (\n \u003cspan className=\"card-link-icon-box\"\u003e\n \u003cspan className=\"card-link-icon\" dangerouslySetInnerHTML={{ __html: icon }} /\u003e\n \u003c/span\u003e\n )}\n \u003cspan className=\"card-link-text\"\u003e\n \u003cspan className=\"card-link-title\"\u003e{title}\u003c/span\u003e\n {children \u0026\u0026 \u003cspan className=\"card-link-desc\"\u003e{children}\u003c/span\u003e}\n \u003c/span\u003e\n \u003c/Link\u003e\n );\n}\n```\n\n### 2. Update card-link.markdoc.js to accept icon attribute\n```js\nexport default {\n render: \"CardLink\",\n attributes: {\n title: { type: String, required: true },\n href: { type: String, required: true },\n icon: { type: String },\n },\n};\n```\n\n### 3. Replace existing card-link CSS in globals.css\nFind the `/* ===== Component: CardLink ===== */` section and replace ALL card-link rules with:\n\n```css\n/* ===== Component: CardLink ===== */\n.card-link {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: var(--space-5);\n padding: var(--space-4) var(--space-5);\n border: none;\n border-radius: var(--radius-lg);\n text-decoration: none;\n transition: background var(--transition-normal);\n position: relative;\n}\n\n.card-link:hover {\n background: var(--hover-bg);\n text-decoration: none;\n}\n\n.card-link-icon-box {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 56px;\n height: 56px;\n min-width: 56px;\n padding: var(--space-3);\n border-radius: var(--radius-sm);\n box-shadow: inset 0 0 0 1px oklch(0.20 0.01 260 / 0.15);\n transition: box-shadow var(--transition-fast);\n}\n\n.card-link:hover .card-link-icon-box {\n box-shadow: inset 0 0 0 1px oklch(0.20 0.01 260 / 0.3);\n}\n\n.card-link-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--color-text-primary);\n}\n\n.card-link-icon svg {\n width: 32px;\n height: 32px;\n fill: none;\n stroke: currentColor;\n stroke-width: 1;\n}\n\n.card-link-text {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 0;\n}\n\n.card-link-title {\n display: block;\n font-family: var(--font-display);\n font-size: 20px;\n font-weight: 500;\n color: var(--color-text-heading);\n line-height: 1.3;\n}\n\n.card-link-desc {\n display: block;\n font-size: 14px;\n color: var(--color-text-secondary);\n line-height: 1.5;\n margin-top: 2px;\n}\n\n.card-link-arrow {\n display: none;\n}\n```\n\n### 4. Add card-grid CSS\nAdd AFTER the card-link rules:\n\n```css\n/* ===== Component: Card Grid ===== */\n.card-grid {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: var(--space-2);\n margin: var(--space-4) 0;\n}\n```\n\nAnd add mobile responsive rule inside `@media (max-width: 768px)`:\n```css\n.card-grid {\n grid-template-columns: 1fr;\n}\n\n.card-link-icon-box {\n width: 48px;\n height: 48px;\n min-width: 48px;\n}\n\n.card-link-icon svg {\n width: 24px;\n height: 24px;\n}\n\n.card-link-title {\n font-size: 16px;\n}\n```\n\n## Don't\n- Do NOT modify index.md (separate task)\n- Do NOT modify _app.js\n- Do NOT remove the .card-link-arrow class definition (just set display: none)\n- Do NOT use Tailwind\n- Do NOT change any other component files","acceptance_criteria":"1. CardLink.js accepts an optional `icon` prop containing SVG HTML string\n2. CardLink renders an icon box (56x56px) with a ring/inset-shadow border when icon is provided\n3. card-link.markdoc.js has icon attribute defined as type String\n4. globals.css has .card-grid class with 2-column grid layout\n5. Card title is 20px in Syne (--font-display), weight 500\n6. Icon SVGs inside .card-link-icon are 32px with stroke rendering\n7. Mobile responsive: grid collapses to 1 column, icon box shrinks to 48px\n8. `npm run build` succeeds","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":25,"created_at":"2026-02-20T18:48:51.496801+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:52:29.014512+08:00","closed_at":"2026-02-20T18:52:29.014512+08:00","close_reason":"69b0987 Add icon-card grid CSS and update CardLink component","labels":["scope:small"],"dependencies":[{"issue_id":"docs-y5x.2","depends_on_id":"docs-y5x","type":"parent-child","created_at":"2026-02-20T18:48:51.497896+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-y5x.3","title":"Register DotPattern and CardGrid, rewrite index.md with hero banner and icon cards","description":"## Files\n- documentation/pages/_app.js (modify)\n- documentation/components/CardGrid.js (create)\n- documentation/markdoc/tags/card-grid.markdoc.js (create)\n- documentation/markdoc/tags/hero-banner.markdoc.js (create)\n- documentation/components/HeroBanner.js (create)\n- documentation/pages/index.md (modify)\n\n## What to do\n\n### 1. Create HeroBanner.js component\n```jsx\nimport { DotPattern } from \"./DotPattern\";\n\nexport function HeroBanner({ title, children }) {\n return (\n \u003cdiv className=\"hero-banner\"\u003e\n \u003cDotPattern /\u003e\n \u003cdiv className=\"hero-banner-content\"\u003e\n {title \u0026\u0026 \u003ch1 className=\"hero-title\"\u003e{title}\u003c/h1\u003e}\n {children \u0026\u0026 \u003cp className=\"hero-subtitle\"\u003e{children}\u003c/p\u003e}\n \u003c/div\u003e\n \u003c/div\u003e\n );\n}\n```\n\n### 2. Create hero-banner.markdoc.js\n```js\nexport default {\n render: \"HeroBanner\",\n attributes: {\n title: { type: String, required: true },\n },\n};\n```\n\n### 3. Create CardGrid.js component\n```jsx\nexport function CardGrid({ children }) {\n return \u003cdiv className=\"card-grid\"\u003e{children}\u003c/div\u003e;\n}\n```\n\n### 4. Create card-grid.markdoc.js\n```js\nexport default {\n render: \"CardGrid\",\n children: [\"card-link\"],\n};\n```\n\n### 5. Register new components in _app.js\nAdd imports for DotPattern, HeroBanner, and CardGrid. Add them to the `components` object:\n```js\nimport { DotPattern } from \"../components/DotPattern\";\nimport { HeroBanner } from \"../components/HeroBanner\";\nimport { CardGrid } from \"../components/CardGrid\";\n```\n\nAdd to components object:\n```js\nDotPattern,\nHeroBanner,\nCardGrid,\n```\n\n### 6. Rewrite index.md\nReplace the entire content of index.md with the hero banner and icon card grids. Use inline SVG strings for icons. Here is the exact content:\n\n```markdown\n---\ntitle: Hypercerts Documentation\n---\n\n{% hero-banner title=\"Hypercerts Documentation\" %}\nStructured digital records of contributions — who did what, when, where, and with what supporting documentation. Build applications that create, evaluate, and fund impactful work.\n{% /hero-banner %}\n\n---\n\n## Get started\n\n{% card-grid %}\n{% card-link title=\"Quickstart\" href=\"/getting-started/quickstart\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z\\\"/\u003e\u003c/svg\u003e\" %}\nInstall the SDK and create your first hypercert in under 5 minutes\n{% /card-link %}\n{% card-link title=\"Creating Your First Hypercert\" href=\"/getting-started/creating-your-first-hypercert\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m3.75 9v6m3-3H9m1.5-12H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z\\\"/\u003e\u003c/svg\u003e\" %}\nBuild a complete hypercert with contributions, attachments, and measurements\n{% /card-link %}\n{% card-link title=\"Working with Evaluations\" href=\"/getting-started/working-with-evaluations\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M9 12.75L11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.745 0 01-1.043 3.296 3.745 3.745 0 01-3.296 1.043A3.745 3.745 0 0112 21c-1.268 0-2.39-.63-3.068-1.593a3.746 3.746 0 01-3.296-1.043 3.745 3.745 0 01-1.043-3.296A3.745 3.745 0 013 12c0-1.268.63-2.39 1.593-3.068a3.745 3.745 0 011.043-3.296 3.746 3.746 0 013.296-1.043A3.746 3.746 0 0112 3c1.268 0 2.39.63 3.068 1.593a3.746 3.746 0 013.296 1.043 3.745 3.745 0 011.043 3.296A3.745 3.745 0 0121 12z\\\"/\u003e\u003c/svg\u003e\" %}\nCreate evaluations of other people's work\n{% /card-link %}\n{% card-link title=\"Common Use Cases\" href=\"/getting-started/common-use-cases\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25\\\"/\u003e\u003c/svg\u003e\" %}\nWorked examples for open-source, climate, research, and community projects\n{% /card-link %}\n{% /card-grid %}\n\n## Core concepts\n\n{% card-grid %}\n{% card-link title=\"What are Hypercerts?\" href=\"/core-concepts/what-is-hypercerts\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z\\\"/\u003e\u003c/svg\u003e\" %}\nThe record structure, how people use them, and why they're built on ATProto\n{% /card-link %}\n{% card-link title=\"Core Data Model\" href=\"/core-concepts/hypercerts-core-data-model\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125\\\"/\u003e\u003c/svg\u003e\" %}\nRecord types, dimensions, and how they connect\n{% /card-link %}\n{% card-link title=\"Certified Identity\" href=\"/core-concepts/certified-identity\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z\\\"/\u003e\u003c/svg\u003e\" %}\nHow identity works — DIDs, signing, portability, and wallet linkage\n{% /card-link %}\n{% card-link title=\"Why ATProto?\" href=\"/core-concepts/why-atproto\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M14.25 9.75L16.5 12l-2.25 2.25m-4.5 0L7.5 12l2.25-2.25M6 20.25h12A2.25 2.25 0 0020.25 18V6A2.25 2.25 0 0018 3.75H6A2.25 2.25 0 003.75 6v12A2.25 2.25 0 006 20.25z\\\"/\u003e\u003c/svg\u003e\" %}\nWhy the protocol is built on AT Protocol\n{% /card-link %}\n{% /card-grid %}\n\n## Tools\n\n{% card-grid %}\n{% card-link title=\"Scaffold Starter App\" href=\"/tools/scaffold\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M6.75 7.5l3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0021 18V6a2.25 2.25 0 00-2.25-2.25H5.25A2.25 2.25 0 003 6v12a2.25 2.25 0 002.25 2.25z\\\"/\u003e\u003c/svg\u003e\" %}\nNext.js reference app with OAuth, creation wizard, and browsing\n{% /card-link %}\n{% card-link title=\"Hypercerts CLI\" href=\"/tools/hypercerts-cli\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M6.75 7.5l3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0021 18V6a2.25 2.25 0 00-2.25-2.25H5.25A2.25 2.25 0 003 6v12a2.25 2.25 0 002.25 2.25z\\\"/\u003e\u003c/svg\u003e\" %}\nCreate and manage hypercerts from the command line\n{% /card-link %}\n{% card-link title=\"Hyperindex\" href=\"/tools/hyperindex\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z\\\"/\u003e\u003c/svg\u003e\" %}\nGraphQL API for querying hypercert records across the network\n{% /card-link %}\n{% card-link title=\"Hyperboard\" href=\"/tools/hyperboard\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M3.75 6A2.25 2.25 0 016 3.75h2.25A2.25 2.25 0 0110.5 6v2.25a2.25 2.25 0 01-2.25 2.25H6a2.25 2.25 0 01-2.25-2.25V6zM3.75 15.75A2.25 2.25 0 016 13.5h2.25a2.25 2.25 0 012.25 2.25V18a2.25 2.25 0 01-2.25 2.25H6A2.25 2.25 0 013.75 18v-2.25zM13.5 6a2.25 2.25 0 012.25-2.25H18A2.25 2.25 0 0120.25 6v2.25A2.25 2.25 0 0118 10.5h-2.25a2.25 2.25 0 01-2.25-2.25V6zM13.5 15.75a2.25 2.25 0 012.25-2.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-2.25A2.25 2.25 0 0113.5 18v-2.25z\\\"/\u003e\u003c/svg\u003e\" %}\nVisual contributor boards for attribution and funding transparency\n{% /card-link %}\n{% /card-grid %}\n\n## Architecture\n\n{% card-grid %}\n{% card-link title=\"Architecture Overview\" href=\"/architecture/overview\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M2.25 7.125C2.25 6.504 2.754 6 3.375 6h6c.621 0 1.125.504 1.125 1.125v3.75c0 .621-.504 1.125-1.125 1.125h-6a1.125 1.125 0 01-1.125-1.125v-3.75zM14.25 8.625c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v8.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 01-1.125-1.125v-8.25zM3.75 16.125c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v2.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 01-1.125-1.125v-2.25z\\\"/\u003e\u003c/svg\u003e\" %}\nHow the protocol stack fits together\n{% /card-link %}\n{% card-link title=\"Data Flow \u0026 Lifecycle\" href=\"/architecture/data-flow-and-lifecycle\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M7.5 21L3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5\\\"/\u003e\u003c/svg\u003e\" %}\nHow a hypercert moves from creation through evaluation to funding\n{% /card-link %}\n{% card-link title=\"Indexers \u0026 Discovery\" href=\"/architecture/indexers-and-discovery\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-.778.099-1.533.284-2.253\\\"/\u003e\u003c/svg\u003e\" %}\nHow indexers make hypercerts findable across the network\n{% /card-link %}\n{% card-link title=\"Portability \u0026 Scaling\" href=\"/architecture/portability-and-scaling\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M3 8.689c0-.864.933-1.405 1.683-.977l7.108 4.062a1.125 1.125 0 010 1.953l-7.108 4.062A1.125 1.125 0 013 16.811V8.69zM12.75 8.689c0-.864.933-1.405 1.683-.977l7.108 4.062a1.125 1.125 0 010 1.953l-7.108 4.062a1.125 1.125 0 01-1.683-.977V8.69z\\\"/\u003e\u003c/svg\u003e\" %}\nMigration, performance, and privacy\n{% /card-link %}\n{% /card-grid %}\n\n## Reference\n\n{% card-grid %}\n{% card-link title=\"Lexicons\" href=\"/lexicons/introduction-to-lexicons\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M14.25 6.087c0-.355.186-.676.401-.959.221-.29.349-.634.349-1.003 0-1.036-1.007-1.875-2.25-1.875s-2.25.84-2.25 1.875c0 .369.128.713.349 1.003.215.283.401.604.401.959v0a.64.64 0 01-.657.643 48.491 48.491 0 01-4.163-.3c-1.108-.128-2.105-.65-2.813-1.536A5.99 5.99 0 003 6.375C3 9.101 4.232 12.126 6 14.25c1.768 2.124 3.879 3.768 6 3.768s4.232-1.644 6-3.768c1.768-2.124 3-5.149 3-7.875a5.99 5.99 0 00-.567-2.556c-.708.886-1.705 1.408-2.813 1.536a48.394 48.394 0 01-4.163.3.64.64 0 01-.657-.643v0z\\\"/\u003e\u003c/svg\u003e\" %}\nSchema definitions for every record type\n{% /card-link %}\n{% card-link title=\"Glossary\" href=\"/reference/glossary\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25\\\"/\u003e\u003c/svg\u003e\" %}\nKey terms used across the documentation\n{% /card-link %}\n{% card-link title=\"FAQ\" href=\"/reference/faq\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M8.625 9.75a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H8.25m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H12m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0h-.375m-13.5 3.01c0 1.6 1.123 2.994 2.707 3.227 1.087.16 2.185.283 3.293.369V21l4.184-4.183a1.14 1.14 0 01.778-.332 48.294 48.294 0 005.83-.498c1.585-.233 2.708-1.626 2.708-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z\\\"/\u003e\u003c/svg\u003e\" %}\nCommon questions about building with Hypercerts\n{% /card-link %}\n{% card-link title=\"Roadmap\" href=\"/roadmap\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M3 3v1.5M3 21v-6m0 0l2.77-.693a9 9 0 016.208.682l.108.054a9 9 0 006.086.71l3.114-.732a48.524 48.524 0 01-.005-10.499l-3.11.732a9 9 0 01-6.085-.711l-.108-.054a9 9 0 00-6.208-.682L3 4.5M3 15V4.5\\\"/\u003e\u003c/svg\u003e\" %}\nDevelopment priorities and phased delivery plan\n{% /card-link %}\n{% /card-grid %}\n\n## Ecosystem \u0026 Vision\n\n{% card-grid %}\n{% card-link title=\"Why We Need Hypercerts\" href=\"/ecosystem/why-we-need-hypercerts\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M12 18v-5.25m0 0a6.01 6.01 0 001.5-.189m-1.5.189a6.01 6.01 0 01-1.5-.189m3.75 7.478a12.06 12.06 0 01-4.5 0m3.75 2.383a14.406 14.406 0 01-3 0M14.25 18v-.192c0-.983.658-1.823 1.508-2.316a7.5 7.5 0 10-7.517 0c.85.493 1.509 1.333 1.509 2.316V18\\\"/\u003e\u003c/svg\u003e\" %}\nThe problem hypercerts solve and why they matter\n{% /card-link %}\n{% /card-grid %}\n```\n\n### Important notes on the SVG icons\n- All SVG icons use Heroicons outline style (24x24 viewBox, stroke-based)\n- No fill attribute — they inherit from the .card-link-icon CSS (fill: none, stroke: currentColor)\n- Each icon is thematically appropriate for its section\n\n## Don't\n- Do NOT modify globals.css (handled by other tasks)\n- Do NOT modify Layout.js or Sidebar.js\n- Do NOT change the navigation.js file\n- Do NOT remove the \"Building on Hypercerts\" or \"Testing \u0026 Deployment\" pages from the card grid — include them in the \"Get started\" section. Wait, the spec above only has 4 cards per section. That's intentional — the index page should show the TOP 4 items per section as cards for visual balance. The remaining pages are still accessible via the sidebar. However, include ALL items from each section as shown in the markdown above.\n- Do NOT add any items that are not in the current index.md","acceptance_criteria":"1. _app.js imports and registers DotPattern, HeroBanner, and CardGrid components\n2. HeroBanner.js renders a div.hero-banner containing DotPattern + title + subtitle\n3. CardGrid.js renders a div.card-grid wrapping children\n4. index.md uses {% hero-banner %} tag with dotted background\n5. index.md uses {% card-grid %} and {% card-link %} tags with icon attributes\n6. Every card-link has an icon prop with valid SVG markup\n7. All links from the original index.md are preserved (no pages dropped)\n8. The page renders with a dotted banner at the top and icon cards below\n9. `npm run build` succeeds with no errors","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":45,"created_at":"2026-02-20T18:50:21.102653+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:56:54.783424+08:00","closed_at":"2026-02-20T18:56:54.783424+08:00","close_reason":"e72f0e0 Register DotPattern, HeroBanner, CardGrid; rewrite index.md with hero banner and icon cards","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-y5x.3","depends_on_id":"docs-y5x","type":"parent-child","created_at":"2026-02-20T18:50:21.103916+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-y5x.3","depends_on_id":"docs-y5x.1","type":"blocks","created_at":"2026-02-20T18:50:21.105154+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-y5x.3","depends_on_id":"docs-y5x.2","type":"blocks","created_at":"2026-02-20T18:50:21.106109+08:00","created_by":"einstein.climateai.org"}]} +{"id":"docs-y5x.3","title":"Register DotPattern and CardGrid, rewrite index.md with hero banner and icon cards","description":"## Files\n- documentation/pages/_app.js (modify)\n- documentation/components/CardGrid.js (create)\n- documentation/markdoc/tags/card-grid.markdoc.js (create)\n- documentation/markdoc/tags/hero-banner.markdoc.js (create)\n- documentation/components/HeroBanner.js (create)\n- documentation/pages/index.md (modify)\n\n## What to do\n\n### 1. Create HeroBanner.js component\n```jsx\nimport { DotPattern } from \"./DotPattern\";\n\nexport function HeroBanner({ title, children }) {\n return (\n \u003cdiv className=\"hero-banner\"\u003e\n \u003cDotPattern /\u003e\n \u003cdiv className=\"hero-banner-content\"\u003e\n {title \u0026\u0026 \u003ch1 className=\"hero-title\"\u003e{title}\u003c/h1\u003e}\n {children \u0026\u0026 \u003cp className=\"hero-subtitle\"\u003e{children}\u003c/p\u003e}\n \u003c/div\u003e\n \u003c/div\u003e\n );\n}\n```\n\n### 2. Create hero-banner.markdoc.js\n```js\nexport default {\n render: \"HeroBanner\",\n attributes: {\n title: { type: String, required: true },\n },\n};\n```\n\n### 3. Create CardGrid.js component\n```jsx\nexport function CardGrid({ children }) {\n return \u003cdiv className=\"card-grid\"\u003e{children}\u003c/div\u003e;\n}\n```\n\n### 4. Create card-grid.markdoc.js\n```js\nexport default {\n render: \"CardGrid\",\n children: [\"card-link\"],\n};\n```\n\n### 5. Register new components in _app.js\nAdd imports for DotPattern, HeroBanner, and CardGrid. Add them to the `components` object:\n```js\nimport { DotPattern } from \"../components/DotPattern\";\nimport { HeroBanner } from \"../components/HeroBanner\";\nimport { CardGrid } from \"../components/CardGrid\";\n```\n\nAdd to components object:\n```js\nDotPattern,\nHeroBanner,\nCardGrid,\n```\n\n### 6. Rewrite index.md\nReplace the entire content of index.md with the hero banner and icon card grids. Use inline SVG strings for icons. Here is the exact content:\n\n```markdown\n---\ntitle: Hypercerts Documentation\n---\n\n{% hero-banner title=\"Hypercerts Documentation\" %}\nStructured digital records of contributions — who did what, when, where, and with what supporting documentation. Build applications that create, evaluate, and fund impactful work.\n{% /hero-banner %}\n\n---\n\n## Get started\n\n{% card-grid %}\n{% card-link title=\"Quickstart\" href=\"/getting-started/quickstart\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z\\\"/\u003e\u003c/svg\u003e\" %}\nInstall the SDK and create your first hypercert in under 5 minutes\n{% /card-link %}\n{% card-link title=\"Creating Your First Hypercert\" href=\"/getting-started/creating-your-first-hypercert\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m3.75 9v6m3-3H9m1.5-12H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z\\\"/\u003e\u003c/svg\u003e\" %}\nBuild a complete hypercert with contributions, attachments, and measurements\n{% /card-link %}\n{% card-link title=\"Working with Evaluations\" href=\"/getting-started/working-with-evaluations\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M9 12.75L11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.745 0 01-1.043 3.296 3.745 3.745 0 01-3.296 1.043A3.745 3.745 0 0112 21c-1.268 0-2.39-.63-3.068-1.593a3.746 3.746 0 01-3.296-1.043 3.745 3.745 0 01-1.043-3.296A3.745 3.745 0 013 12c0-1.268.63-2.39 1.593-3.068a3.745 3.745 0 011.043-3.296 3.746 3.746 0 013.296-1.043A3.746 3.746 0 0112 3c1.268 0 2.39.63 3.068 1.593a3.746 3.746 0 013.296 1.043 3.745 3.745 0 011.043 3.296A3.745 3.745 0 0121 12z\\\"/\u003e\u003c/svg\u003e\" %}\nCreate evaluations of other people's work\n{% /card-link %}\n{% card-link title=\"Common Use Cases\" href=\"/getting-started/common-use-cases\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25\\\"/\u003e\u003c/svg\u003e\" %}\nWorked examples for open-source, climate, research, and community projects\n{% /card-link %}\n{% /card-grid %}\n\n## Core concepts\n\n{% card-grid %}\n{% card-link title=\"What are Hypercerts?\" href=\"/core-concepts/what-is-hypercerts\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z\\\"/\u003e\u003c/svg\u003e\" %}\nThe record structure, how people use them, and why they're built on ATProto\n{% /card-link %}\n{% card-link title=\"Core Data Model\" href=\"/core-concepts/hypercerts-core-data-model\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125\\\"/\u003e\u003c/svg\u003e\" %}\nRecord types, dimensions, and how they connect\n{% /card-link %}\n{% card-link title=\"Certified Identity\" href=\"/core-concepts/certified-identity\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z\\\"/\u003e\u003c/svg\u003e\" %}\nHow identity works — DIDs, signing, portability, and wallet linkage\n{% /card-link %}\n{% card-link title=\"Why ATProto?\" href=\"/core-concepts/why-atproto\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M14.25 9.75L16.5 12l-2.25 2.25m-4.5 0L7.5 12l2.25-2.25M6 20.25h12A2.25 2.25 0 0020.25 18V6A2.25 2.25 0 0018 3.75H6A2.25 2.25 0 003.75 6v12A2.25 2.25 0 006 20.25z\\\"/\u003e\u003c/svg\u003e\" %}\nWhy the protocol is built on AT Protocol\n{% /card-link %}\n{% /card-grid %}\n\n## Tools\n\n{% card-grid %}\n{% card-link title=\"Scaffold Starter App\" href=\"/tools/scaffold\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M6.75 7.5l3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0021 18V6a2.25 2.25 0 00-2.25-2.25H5.25A2.25 2.25 0 003 6v12a2.25 2.25 0 002.25 2.25z\\\"/\u003e\u003c/svg\u003e\" %}\nNext.js reference app with OAuth, creation wizard, and browsing\n{% /card-link %}\n{% card-link title=\"Hypercerts CLI\" href=\"/tools/hypercerts-cli\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M6.75 7.5l3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0021 18V6a2.25 2.25 0 00-2.25-2.25H5.25A2.25 2.25 0 003 6v12a2.25 2.25 0 002.25 2.25z\\\"/\u003e\u003c/svg\u003e\" %}\nCreate and manage hypercerts from the command line\n{% /card-link %}\n{% card-link title=\"Hyperindex\" href=\"/tools/hyperindex\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z\\\"/\u003e\u003c/svg\u003e\" %}\nGraphQL API for querying hypercert records across the network\n{% /card-link %}\n{% card-link title=\"Hyperboard\" href=\"/tools/hyperboard\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M3.75 6A2.25 2.25 0 016 3.75h2.25A2.25 2.25 0 0110.5 6v2.25a2.25 2.25 0 01-2.25 2.25H6a2.25 2.25 0 01-2.25-2.25V6zM3.75 15.75A2.25 2.25 0 016 13.5h2.25a2.25 2.25 0 012.25 2.25V18a2.25 2.25 0 01-2.25 2.25H6A2.25 2.25 0 013.75 18v-2.25zM13.5 6a2.25 2.25 0 012.25-2.25H18A2.25 2.25 0 0120.25 6v2.25A2.25 2.25 0 0118 10.5h-2.25a2.25 2.25 0 01-2.25-2.25V6zM13.5 15.75a2.25 2.25 0 012.25-2.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-2.25A2.25 2.25 0 0113.5 18v-2.25z\\\"/\u003e\u003c/svg\u003e\" %}\nVisual contributor boards for attribution and funding transparency\n{% /card-link %}\n{% /card-grid %}\n\n## Architecture\n\n{% card-grid %}\n{% card-link title=\"Architecture Overview\" href=\"/architecture/overview\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M2.25 7.125C2.25 6.504 2.754 6 3.375 6h6c.621 0 1.125.504 1.125 1.125v3.75c0 .621-.504 1.125-1.125 1.125h-6a1.125 1.125 0 01-1.125-1.125v-3.75zM14.25 8.625c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v8.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 01-1.125-1.125v-8.25zM3.75 16.125c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v2.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 01-1.125-1.125v-2.25z\\\"/\u003e\u003c/svg\u003e\" %}\nHow the protocol stack fits together\n{% /card-link %}\n{% card-link title=\"Data Flow \u0026 Lifecycle\" href=\"/architecture/data-flow-and-lifecycle\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M7.5 21L3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5\\\"/\u003e\u003c/svg\u003e\" %}\nHow a hypercert moves from creation through evaluation to funding\n{% /card-link %}\n{% card-link title=\"Indexers \u0026 Discovery\" href=\"/architecture/indexers-and-discovery\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-.778.099-1.533.284-2.253\\\"/\u003e\u003c/svg\u003e\" %}\nHow indexers make hypercerts findable across the network\n{% /card-link %}\n{% card-link title=\"Portability \u0026 Scaling\" href=\"/architecture/portability-and-scaling\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M3 8.689c0-.864.933-1.405 1.683-.977l7.108 4.062a1.125 1.125 0 010 1.953l-7.108 4.062A1.125 1.125 0 013 16.811V8.69zM12.75 8.689c0-.864.933-1.405 1.683-.977l7.108 4.062a1.125 1.125 0 010 1.953l-7.108 4.062a1.125 1.125 0 01-1.683-.977V8.69z\\\"/\u003e\u003c/svg\u003e\" %}\nMigration, performance, and privacy\n{% /card-link %}\n{% /card-grid %}\n\n## Reference\n\n{% card-grid %}\n{% card-link title=\"Lexicons\" href=\"/lexicons/introduction-to-lexicons\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M14.25 6.087c0-.355.186-.676.401-.959.221-.29.349-.634.349-1.003 0-1.036-1.007-1.875-2.25-1.875s-2.25.84-2.25 1.875c0 .369.128.713.349 1.003.215.283.401.604.401.959v0a.64.64 0 01-.657.643 48.491 48.491 0 01-4.163-.3c-1.108-.128-2.105-.65-2.813-1.536A5.99 5.99 0 003 6.375C3 9.101 4.232 12.126 6 14.25c1.768 2.124 3.879 3.768 6 3.768s4.232-1.644 6-3.768c1.768-2.124 3-5.149 3-7.875a5.99 5.99 0 00-.567-2.556c-.708.886-1.705 1.408-2.813 1.536a48.394 48.394 0 01-4.163.3.64.64 0 01-.657-.643v0z\\\"/\u003e\u003c/svg\u003e\" %}\nSchema definitions for every record type\n{% /card-link %}\n{% card-link title=\"Glossary\" href=\"/reference/glossary\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25\\\"/\u003e\u003c/svg\u003e\" %}\nKey terms used across the documentation\n{% /card-link %}\n{% card-link title=\"FAQ\" href=\"/reference/faq\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M8.625 9.75a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H8.25m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H12m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0h-.375m-13.5 3.01c0 1.6 1.123 2.994 2.707 3.227 1.087.16 2.185.283 3.293.369V21l4.184-4.183a1.14 1.14 0 01.778-.332 48.294 48.294 0 005.83-.498c1.585-.233 2.708-1.626 2.708-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z\\\"/\u003e\u003c/svg\u003e\" %}\nCommon questions about building with Hypercerts\n{% /card-link %}\n{% card-link title=\"Roadmap\" href=\"/roadmap\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M3 3v1.5M3 21v-6m0 0l2.77-.693a9 9 0 016.208.682l.108.054a9 9 0 006.086.71l3.114-.732a48.524 48.524 0 01-.005-10.499l-3.11.732a9 9 0 01-6.085-.711l-.108-.054a9 9 0 00-6.208-.682L3 4.5M3 15V4.5\\\"/\u003e\u003c/svg\u003e\" %}\nDevelopment priorities and phased delivery plan\n{% /card-link %}\n{% /card-grid %}\n\n## Ecosystem \u0026 Vision\n\n{% card-grid %}\n{% card-link title=\"Why We Need Hypercerts\" href=\"/ecosystem/why-we-need-hypercerts\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M12 18v-5.25m0 0a6.01 6.01 0 001.5-.189m-1.5.189a6.01 6.01 0 01-1.5-.189m3.75 7.478a12.06 12.06 0 01-4.5 0m3.75 2.383a14.406 14.406 0 01-3 0M14.25 18v-.192c0-.983.658-1.823 1.508-2.316a7.5 7.5 0 10-7.517 0c.85.493 1.509 1.333 1.509 2.316V18\\\"/\u003e\u003c/svg\u003e\" %}\nThe problem hypercerts solve and why they matter\n{% /card-link %}\n{% /card-grid %}\n```\n\n### Important notes on the SVG icons\n- All SVG icons use Heroicons outline style (24x24 viewBox, stroke-based)\n- No fill attribute — they inherit from the .card-link-icon CSS (fill: none, stroke: currentColor)\n- Each icon is thematically appropriate for its section\n\n## Don't\n- Do NOT modify globals.css (handled by other tasks)\n- Do NOT modify Layout.js or Sidebar.js\n- Do NOT change the navigation.js file\n- Do NOT remove the \"Building on Hypercerts\" or \"Testing \u0026 Deployment\" pages from the card grid — include them in the \"Get started\" section. Wait, the spec above only has 4 cards per section. That's intentional — the index page should show the TOP 4 items per section as cards for visual balance. The remaining pages are still accessible via the sidebar. However, include ALL items from each section as shown in the markdown above.\n- Do NOT add any items that are not in the current index.md","acceptance_criteria":"1. _app.js imports and registers DotPattern, HeroBanner, and CardGrid components\n2. HeroBanner.js renders a div.hero-banner containing DotPattern + title + subtitle\n3. CardGrid.js renders a div.card-grid wrapping children\n4. index.md uses {% hero-banner %} tag with dotted background\n5. index.md uses {% card-grid %} and {% card-link %} tags with icon attributes\n6. Every card-link has an icon prop with valid SVG markup\n7. All links from the original index.md are preserved (no pages dropped)\n8. The page renders with a dotted banner at the top and icon cards below\n9. `npm run build` succeeds with no errors","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":45,"created_at":"2026-02-20T18:50:21.102653+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:56:54.783424+08:00","closed_at":"2026-02-20T18:56:54.783424+08:00","close_reason":"e72f0e0 Register DotPattern, HeroBanner, CardGrid; rewrite index.md with hero banner and icon cards","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-y5x.3","depends_on_id":"docs-y5x.2","type":"blocks","created_at":"2026-02-20T18:50:21.106109+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-y5x.3","depends_on_id":"docs-y5x","type":"parent-child","created_at":"2026-02-20T18:50:21.103916+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-y5x.3","depends_on_id":"docs-y5x.1","type":"blocks","created_at":"2026-02-20T18:50:21.105154+08:00","created_by":"einstein.climateai.org"}]} {"id":"docs-y5x.4","title":"Fix hydration mismatch: HeroBanner nesting and tags barrel file","description":"## Files\n- documentation/markdoc/tags/index.js (create — THIS FILE IS MISSING and is the root cause of tags not rendering)\n- documentation/components/HeroBanner.js (modify)\n\n## What to do\n\n### 1. Create markdoc/tags/index.js barrel file\nThis file is MISSING. Without it, @markdoc/next.js cannot discover any custom tags. The nodes directory already has one (markdoc/nodes/index.js) which is why headings work but tags don't.\n\nCreate `documentation/markdoc/tags/index.js` with:\n\n```js\nimport callout from \"./callout.markdoc\";\nimport columns from \"./columns.markdoc\";\nimport column from \"./column.markdoc\";\nimport figure from \"./figure.markdoc\";\nimport cardLink from \"./card-link.markdoc\";\nimport cardGrid from \"./card-grid.markdoc\";\nimport heroBanner from \"./hero-banner.markdoc\";\n\nexport default {\n callout,\n columns,\n column,\n figure,\n \"card-link\": cardLink,\n \"card-grid\": cardGrid,\n \"hero-banner\": heroBanner,\n};\n```\n\nUses default export because `defaultObject()` in @markdoc/next.js runtime checks for `obj.default` first. Kebab-case tag names must be string keys (can't use hyphens in JS identifiers).\n\n### 2. Fix HeroBanner.js hydration error\nThe hydration mismatch happens because Markdoc wraps the children text in a `\u003cp\u003e` tag, and HeroBanner also wraps children in `\u003cp className=\"hero-subtitle\"\u003e`, creating invalid `\u003cp\u003e\u003cp\u003e...\u003c/p\u003e\u003c/p\u003e` nesting.\n\nReplace HeroBanner.js with:\n\n```jsx\nimport { DotPattern } from \"./DotPattern\";\n\nexport function HeroBanner({ title, children }) {\n return (\n \u003cdiv className=\"hero-banner\"\u003e\n \u003cDotPattern /\u003e\n \u003cdiv className=\"hero-banner-content\"\u003e\n {title \u0026\u0026 \u003ch1 className=\"hero-title\"\u003e{title}\u003c/h1\u003e}\n {children \u0026\u0026 \u003cdiv className=\"hero-subtitle\"\u003e{children}\u003c/div\u003e}\n \u003c/div\u003e\n \u003c/div\u003e\n );\n}\n```\n\nChange: `\u003cp className=\"hero-subtitle\"\u003e` → `\u003cdiv className=\"hero-subtitle\"\u003e`. A `\u003cdiv\u003e` can contain `\u003cp\u003e` children without invalid nesting.\n\n### 3. Verify with clean build\nRun these commands:\n```bash\ncd documentation/documentation\nrm -rf .next out\nnpm run build\n```\n\nThen check the output:\n```bash\ngrep -c \"hero-banner\" out/index.html\ngrep -c \"card-link\" out/index.html\ngrep -c \"dot-pattern\" out/index.html\n```\n\nAll counts should be \u003e 0. If any are 0, the tags barrel file is not working.\n\n### 4. Also verify no hydration errors in dev\n```bash\nnpm run dev\n```\nOpen http://localhost:3000 in the terminal with curl and check there are no `\u003cp\u003e` inside `\u003cp\u003e` patterns:\n```bash\ncurl -s http://localhost:3000 | grep -c \"\u003cp\u003e\u003cp\u003e\"\n```\nShould return 0.\n\n## Don't\n- Do NOT modify any other files\n- Do NOT change the index.md content\n- Do NOT change the card-link, card-grid, or DotPattern components\n- Do NOT modify _app.js\n- Do NOT modify globals.css","acceptance_criteria":"1. documentation/markdoc/tags/index.js exists and exports all 7 tags\n2. `npm run build` succeeds (clean build with rm -rf .next out first)\n3. out/index.html contains \"hero-banner\" class (grep returns \u003e 0 matches)\n4. out/index.html contains \"card-link\" class (grep returns \u003e 0 matches)\n5. out/index.html contains \"dot-pattern\" class (grep returns \u003e 0 matches)\n6. out/index.html does NOT contain \u003cp\u003e\u003cp\u003e nesting\n7. HeroBanner uses \u003cdiv\u003e not \u003cp\u003e for hero-subtitle wrapper\n8. No hydration mismatch errors when running npm run dev","status":"closed","priority":0,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":20,"created_at":"2026-02-20T19:04:56.233516+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:06:41.041634+08:00","closed_at":"2026-02-20T19:06:41.041634+08:00","close_reason":"8982de3 Fix hydration mismatch: add tags barrel file and fix HeroBanner nesting","labels":["scope:small"],"dependencies":[{"issue_id":"docs-y5x.4","depends_on_id":"docs-y5x","type":"parent-child","created_at":"2026-02-20T19:04:56.234214+08:00","created_by":"einstein.climateai.org"}]} {"id":"docs-y5x.5","title":"Replace card-link CSS with icon-card layout and add card-grid","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nThe CardLink component renders icon-box, icon, and text wrapper elements, but the CSS for these classes is missing. The old card-link CSS (block display, border, arrow) does not match the new component structure. Replace it entirely.\n\n### 1. Find and replace the entire card-link CSS section\nFind the section starting with `/* ===== Component: CardLink ===== */` (around line 995) through `.card-link:hover .card-link-arrow` (around line 1043). Replace ALL of it with:\n\n```css\n/* ===== Component: CardLink ===== */\n.card-link {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: var(--space-5);\n padding: var(--space-3) var(--space-4);\n border: none;\n border-radius: var(--radius-lg);\n text-decoration: none;\n transition: background var(--transition-normal);\n position: relative;\n}\n\n.card-link:hover {\n background: var(--hover-bg);\n text-decoration: none;\n}\n\n.card-link-icon-box {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n min-width: 48px;\n padding: 10px;\n border-radius: 2px;\n box-shadow: inset 0 0 0 1px oklch(0.20 0.01 260 / 0.15);\n transition: box-shadow var(--transition-fast);\n}\n\n.card-link:hover .card-link-icon-box {\n box-shadow: inset 0 0 0 1px oklch(0.20 0.01 260 / 0.3);\n}\n\n.card-link-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--color-text-primary);\n}\n\n.card-link-icon svg {\n width: 28px;\n height: 28px;\n fill: none;\n stroke: currentColor;\n stroke-width: 1;\n}\n\n.card-link-text {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 0;\n}\n\n.card-link-title {\n display: block;\n font-family: var(--font-display);\n font-size: 16px;\n font-weight: 500;\n color: var(--color-text-heading);\n line-height: 1.3;\n}\n\n.card-link-desc {\n display: block;\n font-size: 14px;\n color: var(--color-text-secondary);\n line-height: 1.5;\n margin-top: 2px;\n}\n```\n\n### 2. Add card-grid CSS immediately after the card-link section\n\n```css\n/* ===== Component: Card Grid ===== */\n.card-grid {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: var(--space-1);\n margin: var(--space-3) 0;\n}\n```\n\n### 3. Add mobile responsive rules\nInside the existing `@media (max-width: 768px)` block, add:\n\n```css\n.card-grid {\n grid-template-columns: 1fr;\n}\n\n.card-link-icon-box {\n width: 40px;\n height: 40px;\n min-width: 40px;\n}\n\n.card-link-icon svg {\n width: 22px;\n height: 22px;\n}\n\n.card-link-title {\n font-size: 15px;\n}\n```\n\n## Don't\n- Do NOT modify any component files (CardLink.js, HeroBanner.js, etc.)\n- Do NOT modify index.md\n- Do NOT modify _app.js\n- Do NOT change the DotPattern, hero-banner, or hero-title CSS\n- Do NOT add back the old .card-link-arrow styles — the arrow element no longer exists in the component","acceptance_criteria":"1. globals.css contains .card-link-icon-box class with width/height 48px\n2. globals.css contains .card-link-icon svg rule with width/height 28px, fill: none, stroke: currentColor\n3. globals.css contains .card-link-text with flex-direction: column\n4. globals.css contains .card-grid with grid-template-columns: repeat(2, 1fr)\n5. .card-link uses display: flex (not display: block)\n6. No .card-link-arrow rules remain\n7. Mobile responsive rules exist for card-grid (1 column) and card-link-icon-box (40px)\n8. rm -rf .next out \u0026\u0026 npm run build succeeds\n9. SVG icons render at 28px (not full page width)","status":"closed","priority":0,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":15,"created_at":"2026-02-20T19:08:19.964869+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:09:55.213289+08:00","closed_at":"2026-02-20T19:09:55.213289+08:00","close_reason":"b4049e0 Replace card-link CSS with icon-card layout and add card-grid","labels":["scope:small"],"dependencies":[{"issue_id":"docs-y5x.5","depends_on_id":"docs-y5x","type":"parent-child","created_at":"2026-02-20T19:08:19.96556+08:00","created_by":"einstein.climateai.org"}]} {"id":"docs-yhx","title":"Epic: Sidebar Polish","description":"Improve sidebar UX: active link has no background highlight (color-only, fails for color-blind users), parent-active too bold at 700, no left-border tree connectors for nested items, section header 11px may fail contrast, scrollbar invisible until hover, no mobile drawer close button. Covers UX findings #4, 11, 12, 13, 14, 15, 28, 29, 30. User wants one big PR for all sidebar work.","status":"closed","priority":1,"issue_type":"epic","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","created_at":"2026-02-20T19:54:24.88055+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:06:38.187258+08:00","closed_at":"2026-02-20T20:06:38.187258+08:00","close_reason":"c617130 all sidebar tasks complete","labels":["scope:medium"]} {"id":"docs-yhx.1","title":"Sidebar: active state + parent weight + section header + scrollbar + spacing + mobile close","description":"## Files\n- styles/globals.css (modify)\n- components/Sidebar.js (modify)\n\n## What to do\nThis is a single big task covering all CSS-focused sidebar fixes. One branch, one PR.\n\n### Active link background highlight (finding #4)\nThe active sidebar link only changes color — color-blind users (~8%) cant distinguish it. Add a background:\n\n```css\n.sidebar-link-active {\n color: var(--color-link);\n font-weight: 500;\n background: var(--active-bg);\n}\n\na.sidebar-link-active:hover {\n background: var(--active-bg);\n color: var(--color-link);\n}\n```\n\nDark mode already has `--active-bg: oklch(0.22 0.01 260)` which works.\n\n### Parent-active weight reduction (finding #12)\nChange `.sidebar-link-parent-active` from `font-weight: 700` to `font-weight: 600`. This makes it less attention-grabbing than the active child.\n\n### Section header size + contrast (finding #14)\nChange `.sidebar-section-header` font-size from `11px` to `12px`. This improves contrast ratio at small sizes.\n\n### Tree connectors for nested items (finding #13)\nAdd a left border on nested nav children lists to create visual tree connectors:\n\n```css\n.sidebar-nav-children {\n list-style: none;\n margin-left: 20px;\n padding-left: 12px;\n border-left: 1px solid var(--color-border);\n}\n```\n\nDark mode: `html.dark .sidebar-nav-children { border-left-color: oklch(0.25 0.005 260); }`\n\n### Scrollbar visibility (finding #15)\nShow a subtle scrollbar by default (not just on hover). Change the initial scrollbar thumb from `transparent` to a light value:\n\nFor webkit:\n```css\n.sidebar-content::-webkit-scrollbar-thumb {\n background: rgba(0, 0, 0, 0.08);\n border-radius: 3px;\n}\n```\n\nFor Firefox, change `scrollbar-color: transparent transparent` to:\n```css\n.sidebar-content {\n scrollbar-color: rgba(0, 0, 0, 0.08) transparent;\n}\n```\n\nKeep the hover state that makes it darker. Dark mode: use `rgba(255, 255, 255, 0.08)` as default.\n\n### Section bottom padding (finding #30)\nAdd `padding-bottom: var(--space-2)` to `.sidebar-section` for symmetric spacing around section dividers.\n\n### Mobile drawer close button (finding #28)\nIn `Sidebar.js`, when `isOpen` is true (mobile drawer mode), show an X close button at the top-right of the sidebar. Add this inside the `\u003cnav\u003e` element, before the sidebar-content div:\n\n```jsx\n{isOpen \u0026\u0026 (\n \u003cbutton\n className=\"sidebar-close-btn\"\n onClick={onClose}\n aria-label=\"Close navigation\"\n \u003e\n \u003csvg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\"\u003e\n \u003cpath d=\"M5 5l10 10M15 5L5 15\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" /\u003e\n \u003c/svg\u003e\n \u003c/button\u003e\n)}\n```\n\nCSS for `.sidebar-close-btn`:\n- display none by default\n- `@media (max-width: 768px)`: display flex, position absolute, top var(--space-3), right var(--space-3), z-index 5, width 36px, height 36px, align-items center, justify-content center, background none, border none, cursor pointer, color var(--color-text-secondary), border-radius var(--radius-sm)\n- `:hover`: background var(--hover-bg), color var(--color-text-primary)\n\nWhen mobile drawer is open, hide the sidebar-collapse-btn (it is irrelevant on mobile). Add:\n```css\n@media (max-width: 768px) {\n .sidebar-collapse-btn {\n display: none;\n }\n}\n```\n\n### Skip finding #11 (nav icons) and #29 (collapsed icon rail)\nThese are complex structural changes that require modifying navigation.js data structure. Defer to a follow-up task.\n\n## Dont\n- Do NOT remove the sidebar collapse button on desktop\n- Do NOT change the sidebar width\n- Do NOT modify the navigation data structure in lib/navigation.js\n- Do NOT add icons to nav items (deferred)\n- Do NOT change the expand/collapse chevron behavior","acceptance_criteria":"1. Active sidebar link has a visible background highlight (not just color change)\n2. Parent-active links use font-weight 600 (was 700), visually lighter than active child\n3. Section headers render at 12px (was 11px)\n4. Nested nav items (e.g., Lexicons \u003e General \u003e Shared Defs) have a left border tree connector\n5. Scrollbar thumb is faintly visible by default without hovering\n6. Mobile drawer shows an X close button in the top-right corner\n7. Sidebar collapse button is hidden on mobile (irrelevant in drawer mode)\n8. Sections have symmetric top/bottom padding around dividers\n9. Dark mode: tree connectors, scrollbar, active background all adapt correctly\n10. Build passes: npm run build -- --webpack","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":50,"created_at":"2026-02-20T19:54:52.786632+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:06:32.708722+08:00","closed_at":"2026-02-20T20:06:32.708722+08:00","close_reason":"c617130 sidebar polish","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-yhx.1","depends_on_id":"docs-yhx","type":"parent-child","created_at":"2026-02-20T19:54:52.787687+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-yne","title":"Epic 5: Styling and Visual Design","description":"## Summary\nCreate the complete CSS styling for the documentation site following a design language inspired by Stripe's documentation (docs.stripe.com). The goal is a clean, professional, content-focused documentation site that prioritizes readability and quiet confidence.\n\n## Context\nThis project is a Next.js + Markdoc documentation site in the `documentation/` directory. By the time this epic runs:\n- Epic 1 has scaffolded the project with a minimal `styles/globals.css`\n- Epic 2 has created React components: Callout, Columns, Column, Figure\n- Epic 4 has created Layout, Sidebar, TableOfContents components\n\nAll these components need polished CSS. The styles go in `styles/globals.css` (or can be split into CSS modules if preferred).\n\n## Design Principles\n\nThe Stripe documentation design language follows these core principles:\n1. **Content is the interface** -- every visual decision makes content more scannable, never decorates\n2. **Mostly monochrome, with surgical color** -- the page is 95% grayscale; color appears only for links, callout indicators, and status badges\n3. **Generous but purposeful whitespace** -- everything breathes, nothing floats\n4. **Border, not shadow, for structure** -- 1px light gray borders define regions, not heavy shadows\n5. **Developer-first** -- code blocks are first-class citizens, typography is optimized for technical content\n\n## Typography\n\n### Font stacks\n```css\n--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';\n--font-mono: 'Source Code Pro', Menlo, Monaco, Consolas, monospace;\n```\nUse the system font stack. It loads instantly, feels native on every OS, and is what Stripe uses.\n\n### Font weights\n- Regular: 400 (body text, descriptions)\n- Semibold: 600 (section headers, emphasis, sidebar labels)\n- Bold: 700 (page titles, H1)\n\n### Type scale\n| Element | Size | Weight | Color | Line Height |\n|---------|------|--------|-------|-------------|\n| H1 | 32px | 700 | #21252c (near-black) | 1.25 |\n| H2 | 24px | 700 | #353a44 (dark charcoal) | 1.3 |\n| H3 | 20px | 600 | #353a44 | 1.4 |\n| H4 | 16px | 600 | #353a44 | 1.5 |\n| Body | 16px | 400 | #414552 (dark gray) | 1.65 |\n| Small/Caption | 13px | 400 | #687385 (medium gray) | 1.5 |\n| Code (inline) | 14px | 400 | #414552 | inherit |\n| Code (block) | 14px | 400 | #414552 | 1.55 |\n\n### Anti-aliasing\n```css\nbody {\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n```\n\n## Color Palette\n\n### Neutrals (the workhorse -- 95% of the page)\n| Token | Value | Usage |\n|-------|-------|-------|\n| --color-bg | #ffffff | Page background, content area |\n| --color-bg-subtle | #f6f8fa | Code block backgrounds, sidebar hover, cards |\n| --color-border | #ebeef1 | Section dividers, sidebar border, card borders |\n| --color-border-strong | #d8dee4 | Table header bottom border, input borders |\n| --color-text-secondary | #687385 | Descriptions, metadata, TOC items, captions |\n| --color-text-primary | #414552 | Body text |\n| --color-text-heading | #353a44 | Headings (H2-H4) |\n| --color-text-title | #21252c | H1 page titles |\n\n### Accent colors (used sparingly -- only for interactive elements and semantic indicators)\n| Token | Value | Usage |\n|-------|-------|-------|\n| --color-link | #0570de | Hyperlinks, interactive text |\n| --color-link-hover | #0055bc | Link hover state (slightly darker) |\n| --color-info | #0570de | Info callout border |\n| --color-info-bg | #f0f7ff | Info callout background |\n| --color-warning | #c84801 | Warning callout border |\n| --color-warning-bg | #fef9f0 | Warning callout background |\n| --color-danger | #df1b41 | Danger callout border |\n| --color-danger-bg | #fef0f4 | Danger callout background |\n| --color-success | #228403 | Success callout border |\n| --color-success-bg | #f0fef0 | Success callout background |\n\n### Focus ring\n```css\n--focus-ring: 0 0 0 4px rgba(5, 112, 222, 0.36);\n```\n\n## Spacing System\n\nUse an 8px base grid. All spacing should be multiples of 8 (with 4px for tight spaces):\n```css\n--space-1: 4px;\n--space-2: 8px;\n--space-3: 12px;\n--space-4: 16px;\n--space-5: 20px;\n--space-6: 24px;\n--space-8: 32px;\n--space-10: 40px;\n--space-12: 48px;\n--space-16: 64px;\n```\n\n### Application:\n- Between paragraphs: 16px\n- Between heading and its first paragraph: 8-12px (tight coupling)\n- Before a new H2 section: 48px (creates visible section breaks)\n- Sidebar item vertical spacing: 4-6px between items, 20-24px between section groups\n- Content area horizontal padding: 32px on each side\n- Content area top padding: 32px below header\n\n## Component Styles\n\n### Layout\n```\nHeader: height 56px, white background, bottom border 1px solid #ebeef1\nSidebar: width 240px, fixed/sticky, right border 1px solid #ebeef1, padding 16px\nContent: max-width 720px, centered, padding 32px\nRight TOC: width 200px, sticky top, padding-left 24px\n```\n\n### Sidebar\n- Section headers: 12px, font-weight 600, color #687385, text-transform uppercase, letter-spacing 0.05em, margin-bottom 8px\n- Nav items: 14px, color #414552, padding 6px 12px, border-radius 6px\n- Active nav item: font-weight 600, background-color #f6f8fa, color #21252c\n- Hover nav item: background-color #f6f8fa\n- Nested items: padding-left +16px\n\n### Table of Contents (right side)\n- Title \"On this page\": 12px, font-weight 600, color #687385, uppercase\n- Items: 13px, color #687385, padding 4px 0\n- Active item: color #0570de (link blue)\n- Left border indicator: 2px solid #0570de on active item (optional)\n\n### Callout boxes (from Epic 2 components)\n```css\n.callout {\n border-left: 4px solid var(--callout-color);\n background: var(--callout-bg);\n border-radius: 0 6px 6px 0;\n padding: 16px;\n margin: 16px 0;\n}\n.callout-title {\n font-weight: 600;\n margin-bottom: 4px;\n}\n```\n\n### Columns layout (from Epic 2 components)\n```css\n.columns {\n display: flex;\n gap: 24px;\n margin: 16px 0;\n}\n.column {\n flex: 1;\n min-width: 0;\n}\n@media (max-width: 768px) {\n .columns { flex-direction: column; }\n}\n```\n\n### Figure (from Epic 2 components)\n```css\n.figure {\n text-align: center;\n margin: 24px 0;\n}\n.figure img {\n max-width: 100%;\n height: auto;\n border-radius: 6px;\n}\n.figure-caption {\n font-size: 13px;\n color: #687385;\n margin-top: 8px;\n}\n```\n\n### Tables\nStripe uses clean, borderless tables with only a header bottom border:\n```css\ntable {\n width: 100%;\n border-collapse: collapse;\n margin: 16px 0;\n font-size: 14px;\n}\nth {\n text-align: left;\n font-weight: 600;\n color: #353a44;\n padding: 8px 12px;\n border-bottom: 1px solid #d8dee4;\n}\ntd {\n padding: 8px 12px;\n color: #414552;\n border-bottom: 1px solid #ebeef1;\n}\ntr:last-child td {\n border-bottom: none;\n}\n```\nNo zebra striping. No outer borders. Clean and minimal.\n\n### Code blocks\n```css\npre {\n background: #f6f8fa;\n border-radius: 6px;\n padding: 16px;\n overflow-x: auto;\n font-family: var(--font-mono);\n font-size: 14px;\n line-height: 1.55;\n margin: 16px 0;\n color: #414552;\n}\n```\nOptionally install `prism-react-renderer` for syntax highlighting. The current docs only have 2 JSON code blocks, but the SDK docs will grow. If adding syntax highlighting, use a light theme that matches the neutral palette.\n\n### Inline code\n```css\ncode {\n background: #f6f8fa;\n border-radius: 4px;\n padding: 2px 6px;\n font-family: var(--font-mono);\n font-size: 0.875em; /* slightly smaller than surrounding text */\n}\n/* Don't style code inside pre blocks */\npre code {\n background: none;\n border-radius: 0;\n padding: 0;\n font-size: inherit;\n}\n```\n\n### Links\n```css\na {\n color: #0570de;\n text-decoration: none;\n}\na:hover {\n color: #0055bc;\n text-decoration: underline;\n}\n```\n\n### Headings\n```css\nh1 { font-size: 32px; font-weight: 700; color: #21252c; margin-top: 0; margin-bottom: 16px; line-height: 1.25; }\nh2 { font-size: 24px; font-weight: 700; color: #353a44; margin-top: 48px; margin-bottom: 12px; line-height: 1.3; }\nh3 { font-size: 20px; font-weight: 600; color: #353a44; margin-top: 32px; margin-bottom: 8px; line-height: 1.4; }\nh4 { font-size: 16px; font-weight: 600; color: #353a44; margin-top: 24px; margin-bottom: 8px; line-height: 1.5; }\n```\n\nNote: `h2` has 48px top margin to create strong visual section breaks (the largest gaps on the page). This is a key Stripe pattern.\n\n### Pagination (prev/next)\n```css\n.pagination {\n display: flex;\n justify-content: space-between;\n margin-top: 48px;\n padding-top: 24px;\n border-top: 1px solid #ebeef1;\n}\n.pagination a {\n color: #0570de;\n font-weight: 500;\n}\n```\n\n### Lists\n```css\nul, ol {\n padding-left: 24px;\n margin: 12px 0;\n}\nli {\n margin: 6px 0;\n line-height: 1.65;\n}\nli \u003e p { margin: 0; }\n```\n\n### Blockquotes\n```css\nblockquote {\n border-left: 4px solid #ebeef1;\n padding: 0 16px;\n margin: 16px 0;\n color: #687385;\n}\n```\n\n### Horizontal rules\n```css\nhr {\n border: none;\n border-top: 1px solid #ebeef1;\n margin: 32px 0;\n}\n```\n\n## Responsive Breakpoints\n```css\n/* Mobile: \u003c 768px - sidebar and TOC hidden, content full width */\n/* Tablet: 768px - 1040px - sidebar visible, TOC hidden */\n/* Desktop: \u003e 1040px - full three-column layout */\n```\n\nOn mobile:\n- Sidebar hidden behind hamburger menu\n- Right TOC hidden entirely\n- Content goes full-width with 16px horizontal padding\n- Tables get `overflow-x: auto` wrapper\n\n## Shadow System\nShadows are used very sparingly -- almost never on the docs site:\n- Most elements: no shadow (flat)\n- Card hover: `0px 2px 5px rgba(48, 49, 61, 0.08)` (barely perceptible)\n- Dropdown/overlay: `0px 5px 15px rgba(0, 0, 0, 0.12), 0px 15px 35px rgba(48, 49, 61, 0.08)`\n\n## Border Radius\n- Inline code: 4px\n- Code blocks, callouts, cards: 6px\n- Buttons, tags, pills: 6px or rounded (9999em for pill shape)\n\n## Acceptance Criteria\n- All CSS is in `styles/globals.css` (or organized into CSS modules)\n- CSS custom properties are defined for colors, fonts, and spacing\n- System font stack is used (no custom font downloads)\n- Anti-aliased text rendering is enabled\n- All headings (H1-H4) follow the type scale with proper size, weight, color, and spacing\n- Body text is 16px, dark gray (#414552), line-height 1.65\n- Links are blue (#0570de), darken on hover, no underline by default\n- Tables are clean: no outer borders, header has bottom border only, proper padding\n- Code blocks have light gray background, rounded corners, monospace font\n- Inline code has subtle gray background pill\n- Callout boxes have colored left border and tinted background per type\n- Columns stack on mobile, flex row on desktop\n- Layout uses three columns on desktop (sidebar 240px, content ~720px, TOC ~200px)\n- Sidebar has 1px right border, section headers in uppercase small text\n- Active nav item has bold text and subtle background\n- Right TOC has scroll spy highlighting in blue\n- Responsive: sidebar/TOC collapse below 768px/1040px breakpoints\n- Pagination has top border separator and blue link text\n- The overall feel is clean, white, professional, content-focused -- no decorative elements, no gradients, no heavy shadows\n- The page looks like it could belong to the same family as docs.stripe.com\n","status":"closed","priority":2,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-13T13:43:24.486687+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T13:18:01.287139+13:00","closed_at":"2026-02-14T13:18:01.287141+13:00","dependencies":[{"issue_id":"docs-yne","depends_on_id":"docs-c34","type":"blocks","created_at":"2026-02-13T13:43:24.48892+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-yne","depends_on_id":"docs-vbw","type":"blocks","created_at":"2026-02-13T13:43:24.490264+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-yne","depends_on_id":"docs-7dv","type":"blocks","created_at":"2026-02-13T13:43:24.491023+13:00","created_by":"Sharfy Adamantine"}]} +{"id":"docs-yne","title":"Epic 5: Styling and Visual Design","description":"## Summary\nCreate the complete CSS styling for the documentation site following a design language inspired by Stripe's documentation (docs.stripe.com). The goal is a clean, professional, content-focused documentation site that prioritizes readability and quiet confidence.\n\n## Context\nThis project is a Next.js + Markdoc documentation site in the `documentation/` directory. By the time this epic runs:\n- Epic 1 has scaffolded the project with a minimal `styles/globals.css`\n- Epic 2 has created React components: Callout, Columns, Column, Figure\n- Epic 4 has created Layout, Sidebar, TableOfContents components\n\nAll these components need polished CSS. The styles go in `styles/globals.css` (or can be split into CSS modules if preferred).\n\n## Design Principles\n\nThe Stripe documentation design language follows these core principles:\n1. **Content is the interface** -- every visual decision makes content more scannable, never decorates\n2. **Mostly monochrome, with surgical color** -- the page is 95% grayscale; color appears only for links, callout indicators, and status badges\n3. **Generous but purposeful whitespace** -- everything breathes, nothing floats\n4. **Border, not shadow, for structure** -- 1px light gray borders define regions, not heavy shadows\n5. **Developer-first** -- code blocks are first-class citizens, typography is optimized for technical content\n\n## Typography\n\n### Font stacks\n```css\n--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';\n--font-mono: 'Source Code Pro', Menlo, Monaco, Consolas, monospace;\n```\nUse the system font stack. It loads instantly, feels native on every OS, and is what Stripe uses.\n\n### Font weights\n- Regular: 400 (body text, descriptions)\n- Semibold: 600 (section headers, emphasis, sidebar labels)\n- Bold: 700 (page titles, H1)\n\n### Type scale\n| Element | Size | Weight | Color | Line Height |\n|---------|------|--------|-------|-------------|\n| H1 | 32px | 700 | #21252c (near-black) | 1.25 |\n| H2 | 24px | 700 | #353a44 (dark charcoal) | 1.3 |\n| H3 | 20px | 600 | #353a44 | 1.4 |\n| H4 | 16px | 600 | #353a44 | 1.5 |\n| Body | 16px | 400 | #414552 (dark gray) | 1.65 |\n| Small/Caption | 13px | 400 | #687385 (medium gray) | 1.5 |\n| Code (inline) | 14px | 400 | #414552 | inherit |\n| Code (block) | 14px | 400 | #414552 | 1.55 |\n\n### Anti-aliasing\n```css\nbody {\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n```\n\n## Color Palette\n\n### Neutrals (the workhorse -- 95% of the page)\n| Token | Value | Usage |\n|-------|-------|-------|\n| --color-bg | #ffffff | Page background, content area |\n| --color-bg-subtle | #f6f8fa | Code block backgrounds, sidebar hover, cards |\n| --color-border | #ebeef1 | Section dividers, sidebar border, card borders |\n| --color-border-strong | #d8dee4 | Table header bottom border, input borders |\n| --color-text-secondary | #687385 | Descriptions, metadata, TOC items, captions |\n| --color-text-primary | #414552 | Body text |\n| --color-text-heading | #353a44 | Headings (H2-H4) |\n| --color-text-title | #21252c | H1 page titles |\n\n### Accent colors (used sparingly -- only for interactive elements and semantic indicators)\n| Token | Value | Usage |\n|-------|-------|-------|\n| --color-link | #0570de | Hyperlinks, interactive text |\n| --color-link-hover | #0055bc | Link hover state (slightly darker) |\n| --color-info | #0570de | Info callout border |\n| --color-info-bg | #f0f7ff | Info callout background |\n| --color-warning | #c84801 | Warning callout border |\n| --color-warning-bg | #fef9f0 | Warning callout background |\n| --color-danger | #df1b41 | Danger callout border |\n| --color-danger-bg | #fef0f4 | Danger callout background |\n| --color-success | #228403 | Success callout border |\n| --color-success-bg | #f0fef0 | Success callout background |\n\n### Focus ring\n```css\n--focus-ring: 0 0 0 4px rgba(5, 112, 222, 0.36);\n```\n\n## Spacing System\n\nUse an 8px base grid. All spacing should be multiples of 8 (with 4px for tight spaces):\n```css\n--space-1: 4px;\n--space-2: 8px;\n--space-3: 12px;\n--space-4: 16px;\n--space-5: 20px;\n--space-6: 24px;\n--space-8: 32px;\n--space-10: 40px;\n--space-12: 48px;\n--space-16: 64px;\n```\n\n### Application:\n- Between paragraphs: 16px\n- Between heading and its first paragraph: 8-12px (tight coupling)\n- Before a new H2 section: 48px (creates visible section breaks)\n- Sidebar item vertical spacing: 4-6px between items, 20-24px between section groups\n- Content area horizontal padding: 32px on each side\n- Content area top padding: 32px below header\n\n## Component Styles\n\n### Layout\n```\nHeader: height 56px, white background, bottom border 1px solid #ebeef1\nSidebar: width 240px, fixed/sticky, right border 1px solid #ebeef1, padding 16px\nContent: max-width 720px, centered, padding 32px\nRight TOC: width 200px, sticky top, padding-left 24px\n```\n\n### Sidebar\n- Section headers: 12px, font-weight 600, color #687385, text-transform uppercase, letter-spacing 0.05em, margin-bottom 8px\n- Nav items: 14px, color #414552, padding 6px 12px, border-radius 6px\n- Active nav item: font-weight 600, background-color #f6f8fa, color #21252c\n- Hover nav item: background-color #f6f8fa\n- Nested items: padding-left +16px\n\n### Table of Contents (right side)\n- Title \"On this page\": 12px, font-weight 600, color #687385, uppercase\n- Items: 13px, color #687385, padding 4px 0\n- Active item: color #0570de (link blue)\n- Left border indicator: 2px solid #0570de on active item (optional)\n\n### Callout boxes (from Epic 2 components)\n```css\n.callout {\n border-left: 4px solid var(--callout-color);\n background: var(--callout-bg);\n border-radius: 0 6px 6px 0;\n padding: 16px;\n margin: 16px 0;\n}\n.callout-title {\n font-weight: 600;\n margin-bottom: 4px;\n}\n```\n\n### Columns layout (from Epic 2 components)\n```css\n.columns {\n display: flex;\n gap: 24px;\n margin: 16px 0;\n}\n.column {\n flex: 1;\n min-width: 0;\n}\n@media (max-width: 768px) {\n .columns { flex-direction: column; }\n}\n```\n\n### Figure (from Epic 2 components)\n```css\n.figure {\n text-align: center;\n margin: 24px 0;\n}\n.figure img {\n max-width: 100%;\n height: auto;\n border-radius: 6px;\n}\n.figure-caption {\n font-size: 13px;\n color: #687385;\n margin-top: 8px;\n}\n```\n\n### Tables\nStripe uses clean, borderless tables with only a header bottom border:\n```css\ntable {\n width: 100%;\n border-collapse: collapse;\n margin: 16px 0;\n font-size: 14px;\n}\nth {\n text-align: left;\n font-weight: 600;\n color: #353a44;\n padding: 8px 12px;\n border-bottom: 1px solid #d8dee4;\n}\ntd {\n padding: 8px 12px;\n color: #414552;\n border-bottom: 1px solid #ebeef1;\n}\ntr:last-child td {\n border-bottom: none;\n}\n```\nNo zebra striping. No outer borders. Clean and minimal.\n\n### Code blocks\n```css\npre {\n background: #f6f8fa;\n border-radius: 6px;\n padding: 16px;\n overflow-x: auto;\n font-family: var(--font-mono);\n font-size: 14px;\n line-height: 1.55;\n margin: 16px 0;\n color: #414552;\n}\n```\nOptionally install `prism-react-renderer` for syntax highlighting. The current docs only have 2 JSON code blocks, but the SDK docs will grow. If adding syntax highlighting, use a light theme that matches the neutral palette.\n\n### Inline code\n```css\ncode {\n background: #f6f8fa;\n border-radius: 4px;\n padding: 2px 6px;\n font-family: var(--font-mono);\n font-size: 0.875em; /* slightly smaller than surrounding text */\n}\n/* Don't style code inside pre blocks */\npre code {\n background: none;\n border-radius: 0;\n padding: 0;\n font-size: inherit;\n}\n```\n\n### Links\n```css\na {\n color: #0570de;\n text-decoration: none;\n}\na:hover {\n color: #0055bc;\n text-decoration: underline;\n}\n```\n\n### Headings\n```css\nh1 { font-size: 32px; font-weight: 700; color: #21252c; margin-top: 0; margin-bottom: 16px; line-height: 1.25; }\nh2 { font-size: 24px; font-weight: 700; color: #353a44; margin-top: 48px; margin-bottom: 12px; line-height: 1.3; }\nh3 { font-size: 20px; font-weight: 600; color: #353a44; margin-top: 32px; margin-bottom: 8px; line-height: 1.4; }\nh4 { font-size: 16px; font-weight: 600; color: #353a44; margin-top: 24px; margin-bottom: 8px; line-height: 1.5; }\n```\n\nNote: `h2` has 48px top margin to create strong visual section breaks (the largest gaps on the page). This is a key Stripe pattern.\n\n### Pagination (prev/next)\n```css\n.pagination {\n display: flex;\n justify-content: space-between;\n margin-top: 48px;\n padding-top: 24px;\n border-top: 1px solid #ebeef1;\n}\n.pagination a {\n color: #0570de;\n font-weight: 500;\n}\n```\n\n### Lists\n```css\nul, ol {\n padding-left: 24px;\n margin: 12px 0;\n}\nli {\n margin: 6px 0;\n line-height: 1.65;\n}\nli \u003e p { margin: 0; }\n```\n\n### Blockquotes\n```css\nblockquote {\n border-left: 4px solid #ebeef1;\n padding: 0 16px;\n margin: 16px 0;\n color: #687385;\n}\n```\n\n### Horizontal rules\n```css\nhr {\n border: none;\n border-top: 1px solid #ebeef1;\n margin: 32px 0;\n}\n```\n\n## Responsive Breakpoints\n```css\n/* Mobile: \u003c 768px - sidebar and TOC hidden, content full width */\n/* Tablet: 768px - 1040px - sidebar visible, TOC hidden */\n/* Desktop: \u003e 1040px - full three-column layout */\n```\n\nOn mobile:\n- Sidebar hidden behind hamburger menu\n- Right TOC hidden entirely\n- Content goes full-width with 16px horizontal padding\n- Tables get `overflow-x: auto` wrapper\n\n## Shadow System\nShadows are used very sparingly -- almost never on the docs site:\n- Most elements: no shadow (flat)\n- Card hover: `0px 2px 5px rgba(48, 49, 61, 0.08)` (barely perceptible)\n- Dropdown/overlay: `0px 5px 15px rgba(0, 0, 0, 0.12), 0px 15px 35px rgba(48, 49, 61, 0.08)`\n\n## Border Radius\n- Inline code: 4px\n- Code blocks, callouts, cards: 6px\n- Buttons, tags, pills: 6px or rounded (9999em for pill shape)\n\n## Acceptance Criteria\n- All CSS is in `styles/globals.css` (or organized into CSS modules)\n- CSS custom properties are defined for colors, fonts, and spacing\n- System font stack is used (no custom font downloads)\n- Anti-aliased text rendering is enabled\n- All headings (H1-H4) follow the type scale with proper size, weight, color, and spacing\n- Body text is 16px, dark gray (#414552), line-height 1.65\n- Links are blue (#0570de), darken on hover, no underline by default\n- Tables are clean: no outer borders, header has bottom border only, proper padding\n- Code blocks have light gray background, rounded corners, monospace font\n- Inline code has subtle gray background pill\n- Callout boxes have colored left border and tinted background per type\n- Columns stack on mobile, flex row on desktop\n- Layout uses three columns on desktop (sidebar 240px, content ~720px, TOC ~200px)\n- Sidebar has 1px right border, section headers in uppercase small text\n- Active nav item has bold text and subtle background\n- Right TOC has scroll spy highlighting in blue\n- Responsive: sidebar/TOC collapse below 768px/1040px breakpoints\n- Pagination has top border separator and blue link text\n- The overall feel is clean, white, professional, content-focused -- no decorative elements, no gradients, no heavy shadows\n- The page looks like it could belong to the same family as docs.stripe.com\n","status":"closed","priority":2,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-13T13:43:24.486687+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T13:18:01.287139+13:00","closed_at":"2026-02-14T13:18:01.287141+13:00","dependencies":[{"issue_id":"docs-yne","depends_on_id":"docs-7dv","type":"blocks","created_at":"2026-02-13T13:43:24.491023+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-yne","depends_on_id":"docs-c34","type":"blocks","created_at":"2026-02-13T13:43:24.48892+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-yne","depends_on_id":"docs-vbw","type":"blocks","created_at":"2026-02-13T13:43:24.490264+13:00","created_by":"Sharfy Adamantine"}]} {"id":"docs-yzy","title":"Epic: Separate planned funding/tokenization content into dedicated page","description":"Move all planned/TBD content about freeze-then-fund, tokenization, on-chain mechanisms, multi-chain support, and funding patterns out of the main documentation pages and into a single dedicated page. Main docs should describe what exists today (ATProto data layer) and briefly mention that on-chain funding is planned, linking to the dedicated page for details.","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T17:11:36.619138+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T19:52:57.591064+13:00","closed_at":"2026-02-16T19:52:57.591064+13:00","close_reason":"Closed"} {"id":"docs-yzy.1","title":"Create architecture/planned-funding-and-tokenization.md — the dedicated page for all planned on-chain content","description":"## Files\n- documentation/pages/architecture/planned-funding-and-tokenization.md (create)\n\n## What to do\n\nCreate a new page that consolidates ALL planned/TBD content about the on-chain funding and tokenization layer. This page is the single source of truth for what's planned but not yet built.\n\nThe page should have this structure:\n\n### Frontmatter\n```\n---\ntitle: \"Planned: Funding \u0026 Tokenization\"\ndescription: How hypercerts will be frozen, anchored on-chain, and funded. This layer is not yet implemented.\n---\n```\n\n### Page content (write this in full):\n\n**Opening paragraph:** The on-chain funding and tokenization layer is not yet implemented. This page describes the planned design. The theory and architecture are sound — the implementation is in progress. For what exists today (the ATProto data layer), see [Architecture Overview](/architecture/overview).\n\n**Section: The Freeze-Then-Fund Model**\nThe core concept: before a hypercert can be funded, its ATProto records must be frozen. Freezing means taking a cryptographic snapshot (CID) of the activity claim and all its associated records at a point in time. This snapshot is then anchored on-chain.\n\nWhy freezing is necessary: a funder must know exactly what they are funding. If the cert's contents could change after funding, the funder might end up paying for a different cert than what they committed to. A hypercert cannot be funded if its contents are still changing.\n\nWhat freezing preserves: the core activity claim — who did what, when, where. The frozen state is what funders commit to.\n\nWhat continues after freezing: evaluations and evidence can still accumulate. These are separate records that reference the frozen claim. The claim's reputation can evolve while its core content remains fixed.\n\n**Section: On-Chain Anchoring**\nWhen a hypercert is frozen, its snapshot CID will be anchored on-chain via a smart contract. The contract stores the AT-URI and the frozen snapshot CID, creating a verifiable link between the data layer and the ownership layer.\n\nThis creates an immutable reference point. Anyone can verify that the on-chain record matches the ATProto data by comparing CIDs.\n\n**Section: Tokenization**\nOnce frozen and anchored, the hypercert can be represented as a transferable token on-chain. A token could represent full ownership or fractional shares. Token holders would have rights defined in the hypercert's org.hypercerts.claim.rights record.\n\nThe specific token standard (ERC-1155, ERC-721, or a custom standard) is being designed. The protocol intends to support multiple token standards for different use cases — from non-transferable recognition to fully tradable certificates.\n\n**Section: Funding Mechanisms**\nOnce frozen and anchored, various funding models can operate on the ownership layer:\n- Direct funding — funders acquire shares directly from the contributor\n- Retroactive funding — rewarding past work based on demonstrated outcomes\n- Impact certificates — creating markets for outcomes\n- Quadratic funding — amplifying small donations through matching pools\n- Milestone-based payouts — releasing funds as work progresses\n\nSmart contracts will enforce rules and distribute payments. The specific implementations are being designed.\n\n**Section: Funding Readiness Patterns**\nDifferent applications may use different patterns for when to freeze:\n\nPattern 1: Freeze-on-create — the claim is frozen immediately upon creation. Simple but means the claim can't be enriched before funding.\n\nPattern 2: Freeze-when-ready — the claim exists on ATProto, accumulates evidence and evaluations, and is frozen only when a funder expresses interest or the contributor decides it's ready. This is the expected default pattern.\n\nPattern 3: Batch freezing — multiple claims are frozen and anchored together periodically. Cost-efficient for high-volume use cases.\n\nPattern 4: Partial freezing — some aspects of the claim are frozen (e.g., the core activity and rights) while others remain mutable (e.g., ongoing measurements). The frozen portion is what funders commit to.\n\n**Section: Multi-Chain Support**\nThe protocol is designed to be chain-agnostic. Different communities may use different chains. The ATProto data layer remains the same regardless of which chain anchors the frozen snapshot. The specific multi-chain architecture is being designed.\n\n**Section: Cross-Layer Example**\nWalk through the full planned flow:\n1. Alice creates an activity claim on her PDS → gets AT-URI\n2. Bob evaluates Alice's claim from his PDS\n3. Alice decides the claim is ready for funding → freezes it\n4. An application anchors the frozen snapshot on-chain → gets token ID\n5. Carol funds the frozen cert on-chain\n6. New evaluations continue accumulating on ATProto, referencing the frozen claim\n7. Carol can verify her funded cert matches the frozen snapshot by comparing CIDs\n\nInclude the ASCII diagram:\n```\nATProto (exists today) On-chain (planned)\n────────────────────── ──────────────────\nActivity Claim Frozen Snapshot\nat://did:alice/... CID: bafyrei...\n ↓ ↓\nEvidence, Evaluations Ownership Record\nMeasurements, Rights Funder: 0xCarol...\n Metadata: { uri, cid }\n```\n\n**Section: What This Will Enable**\n- Retroactive funding of past contributions\n- Composable funding mechanisms across platforms\n- Portable proof of funding (funders can prove what they funded)\n- Independent evolution of data and ownership layers\n\n**Closing:** For the current architecture (what exists today), see [Architecture Overview](/architecture/overview). For the data lifecycle, see [Data Flow \u0026 Lifecycle](/architecture/data-flow-and-lifecycle).\n\n## Test\ntest -f documentation/pages/architecture/planned-funding-and-tokenization.md \u0026\u0026 \\\ngrep -q 'not yet implemented' documentation/pages/architecture/planned-funding-and-tokenization.md \u0026\u0026 \\\ngrep -q 'Freeze-Then-Fund' documentation/pages/architecture/planned-funding-and-tokenization.md \u0026\u0026 \\\ngrep -q 'cannot.*funded.*chang\\|can.t.*funded.*chang\\|exactly what' documentation/pages/architecture/planned-funding-and-tokenization.md \u0026\u0026 \\\ngrep -q 'Funding Readiness Patterns\\|Funding.*Pattern' documentation/pages/architecture/planned-funding-and-tokenization.md \u0026\u0026 \\\ngrep -q 'Multi-Chain' documentation/pages/architecture/planned-funding-and-tokenization.md \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Present any of this as implemented/live\n- Invent specific contract addresses or deployed details\n- Make it too short — this is THE comprehensive reference for all planned on-chain work\n- Forget the cross-layer example with the ASCII diagram","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T17:12:25.547762+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T17:16:00.16776+13:00","closed_at":"2026-02-16T17:16:00.16776+13:00","close_reason":"d937d10 Created comprehensive planned funding and tokenization architecture page","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-yzy.1","depends_on_id":"docs-yzy","type":"parent-child","created_at":"2026-02-16T17:12:25.549901+13:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"docs-yzy.2","title":"Update navigation and rewrite blockchain-integration.md to redirect to planned page","description":"## Files\n- documentation/lib/navigation.js (modify)\n- documentation/pages/getting-started/infrastructure/blockchain-integration.md (modify)\n- documentation/pages/getting-started/infrastructure/portability-and-scaling.md (modify)\n- documentation/pages/getting-started/the-hypercerts-infrastructure.md (modify — just the link on line 78)\n\n## What to do\n\n### 1. Update navigation.js\n\nChange line 25 from:\n```js\n{ title: 'Blockchain Integration', path: '/getting-started/infrastructure/blockchain-integration' },\n```\nto:\n```js\n{ title: 'Funding \u0026 Tokenization (Planned)', path: '/architecture/planned-funding-and-tokenization' },\n```\n\nAlso add the new page to the Understand section, after \"Data Flow \u0026 Lifecycle\" (line 30). Add:\n```js\n{ title: 'Planned: Funding \u0026 Tokenization', path: '/architecture/planned-funding-and-tokenization' },\n```\n\nWait — actually, since it's already linked as a child of \"The Hypercerts Infrastructure\", just update that child entry. Don't add a duplicate. So the only change is line 25.\n\n### 2. Rewrite blockchain-integration.md\n\nThis file currently has the old mint-on-create / lazy minting / batch anchoring / hybrid ownership patterns. Replace the entire content with a redirect notice:\n\n```markdown\n---\ntitle: Funding \u0026 Tokenization\n---\n\n# Funding \u0026 Tokenization\n\nThis content has moved to [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization).\n```\n\n### 3. Update portability-and-scaling.md\n\nLines 36-38 have \"Blockchain scalability\" section that says \"On-chain operations (minting, transfers, sales) are expensive...\" — this is planned content. Replace with:\n\n```markdown\n#### On-chain scalability (planned)\n\nThe on-chain funding and tokenization layer is not yet implemented. When built, on-chain operations will be expensive, so hypercerts will minimize on-chain activity by keeping rich data on ATProto. Only frozen snapshots and funding flows will touch the blockchain. For details on the planned on-chain design, see [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization).\n```\n\nAlso lines 50-52 have \"Access control via smart contracts\" with token-gated data. Replace with:\n\n```markdown\n#### Access control via smart contracts (planned)\n\nIn the planned design, on-chain tokens could have access control logic — for example, granting read access to private ATProto records only to token holders. This is a potential future feature. See [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for details.\n```\n\n### 4. Update the-hypercerts-infrastructure.md line 78\n\nChange:\n```\n- [Blockchain Integration](/getting-started/infrastructure/blockchain-integration) — minting patterns and on-chain ownership\n```\nto:\n```\n- [Funding \u0026 Tokenization (Planned)](/architecture/planned-funding-and-tokenization) — freeze-then-fund model and on-chain ownership design\n```\n\n## Test\ngrep -q 'planned-funding-and-tokenization' documentation/lib/navigation.js \u0026\u0026 \\\ngrep -q 'moved to' documentation/pages/getting-started/infrastructure/blockchain-integration.md \u0026\u0026 \\\ngrep -q 'planned-funding-and-tokenization' documentation/pages/getting-started/the-hypercerts-infrastructure.md \u0026\u0026 \\\ngrep -q 'not yet implemented' documentation/pages/getting-started/infrastructure/portability-and-scaling.md \u0026\u0026 \\\n! grep -q 'mints an NFT\\|Mint-on-create\\|Lazy minting' documentation/pages/getting-started/infrastructure/blockchain-integration.md \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Add duplicate nav entries\n- Delete blockchain-integration.md (keep it as a redirect so old links work)\n- Change anything else in navigation.js\n- Change anything else in the-hypercerts-infrastructure.md besides line 78\n- Change anything else in portability-and-scaling.md besides the blockchain scalability and token-gated sections","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T17:12:50.318417+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T17:18:08.762614+13:00","closed_at":"2026-02-16T17:18:08.762614+13:00","close_reason":"e8dd74d Update navigation and redirect blockchain-integration to planned funding page","labels":["scope:small"],"dependencies":[{"issue_id":"docs-yzy.2","depends_on_id":"docs-yzy","type":"parent-child","created_at":"2026-02-16T17:12:50.31972+13:00","created_by":"sharfy-test.climateai.org"},{"issue_id":"docs-yzy.2","depends_on_id":"docs-yzy.1","type":"blocks","created_at":"2026-02-16T17:14:24.044876+13:00","created_by":"sharfy-test.climateai.org"}]} +{"id":"docs-yzy.2","title":"Update navigation and rewrite blockchain-integration.md to redirect to planned page","description":"## Files\n- documentation/lib/navigation.js (modify)\n- documentation/pages/getting-started/infrastructure/blockchain-integration.md (modify)\n- documentation/pages/getting-started/infrastructure/portability-and-scaling.md (modify)\n- documentation/pages/getting-started/the-hypercerts-infrastructure.md (modify — just the link on line 78)\n\n## What to do\n\n### 1. Update navigation.js\n\nChange line 25 from:\n```js\n{ title: 'Blockchain Integration', path: '/getting-started/infrastructure/blockchain-integration' },\n```\nto:\n```js\n{ title: 'Funding \u0026 Tokenization (Planned)', path: '/architecture/planned-funding-and-tokenization' },\n```\n\nAlso add the new page to the Understand section, after \"Data Flow \u0026 Lifecycle\" (line 30). Add:\n```js\n{ title: 'Planned: Funding \u0026 Tokenization', path: '/architecture/planned-funding-and-tokenization' },\n```\n\nWait — actually, since it's already linked as a child of \"The Hypercerts Infrastructure\", just update that child entry. Don't add a duplicate. So the only change is line 25.\n\n### 2. Rewrite blockchain-integration.md\n\nThis file currently has the old mint-on-create / lazy minting / batch anchoring / hybrid ownership patterns. Replace the entire content with a redirect notice:\n\n```markdown\n---\ntitle: Funding \u0026 Tokenization\n---\n\n# Funding \u0026 Tokenization\n\nThis content has moved to [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization).\n```\n\n### 3. Update portability-and-scaling.md\n\nLines 36-38 have \"Blockchain scalability\" section that says \"On-chain operations (minting, transfers, sales) are expensive...\" — this is planned content. Replace with:\n\n```markdown\n#### On-chain scalability (planned)\n\nThe on-chain funding and tokenization layer is not yet implemented. When built, on-chain operations will be expensive, so hypercerts will minimize on-chain activity by keeping rich data on ATProto. Only frozen snapshots and funding flows will touch the blockchain. For details on the planned on-chain design, see [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization).\n```\n\nAlso lines 50-52 have \"Access control via smart contracts\" with token-gated data. Replace with:\n\n```markdown\n#### Access control via smart contracts (planned)\n\nIn the planned design, on-chain tokens could have access control logic — for example, granting read access to private ATProto records only to token holders. This is a potential future feature. See [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for details.\n```\n\n### 4. Update the-hypercerts-infrastructure.md line 78\n\nChange:\n```\n- [Blockchain Integration](/getting-started/infrastructure/blockchain-integration) — minting patterns and on-chain ownership\n```\nto:\n```\n- [Funding \u0026 Tokenization (Planned)](/architecture/planned-funding-and-tokenization) — freeze-then-fund model and on-chain ownership design\n```\n\n## Test\ngrep -q 'planned-funding-and-tokenization' documentation/lib/navigation.js \u0026\u0026 \\\ngrep -q 'moved to' documentation/pages/getting-started/infrastructure/blockchain-integration.md \u0026\u0026 \\\ngrep -q 'planned-funding-and-tokenization' documentation/pages/getting-started/the-hypercerts-infrastructure.md \u0026\u0026 \\\ngrep -q 'not yet implemented' documentation/pages/getting-started/infrastructure/portability-and-scaling.md \u0026\u0026 \\\n! grep -q 'mints an NFT\\|Mint-on-create\\|Lazy minting' documentation/pages/getting-started/infrastructure/blockchain-integration.md \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Add duplicate nav entries\n- Delete blockchain-integration.md (keep it as a redirect so old links work)\n- Change anything else in navigation.js\n- Change anything else in the-hypercerts-infrastructure.md besides line 78\n- Change anything else in portability-and-scaling.md besides the blockchain scalability and token-gated sections","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T17:12:50.318417+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T17:18:08.762614+13:00","closed_at":"2026-02-16T17:18:08.762614+13:00","close_reason":"e8dd74d Update navigation and redirect blockchain-integration to planned funding page","labels":["scope:small"],"dependencies":[{"issue_id":"docs-yzy.2","depends_on_id":"docs-yzy.1","type":"blocks","created_at":"2026-02-16T17:14:24.044876+13:00","created_by":"sharfy-test.climateai.org"},{"issue_id":"docs-yzy.2","depends_on_id":"docs-yzy","type":"parent-child","created_at":"2026-02-16T17:12:50.31972+13:00","created_by":"sharfy-test.climateai.org"}]} {"id":"docs-yzy.3","title":"Strip planned content from architecture/overview.md and architecture/data-flow-and-lifecycle.md — link to planned page","description":"## Files\n- documentation/pages/architecture/overview.md (modify)\n- documentation/pages/architecture/data-flow-and-lifecycle.md (modify)\n\n## What to do\n\nRemove detailed planned/TBD content from these two architecture pages. Replace with brief mentions and links to /architecture/planned-funding-and-tokenization. The main docs should describe what EXISTS TODAY (ATProto data layer) and only briefly note that on-chain funding is planned.\n\n### architecture/overview.md\n\n**Line 8:** Change to: \"The Hypercerts Protocol uses AT Protocol for data portability. On-chain anchoring for ownership and funding is [planned](/architecture/planned-funding-and-tokenization).\"\n\n**Line 18 (Ownership Layer):** Condense to 1-2 sentences max: \"The **Ownership Layer** is planned but not yet implemented. The intended design uses a freeze-then-fund model where hypercerts are frozen and anchored on-chain before funding — ensuring funders know exactly what they are paying for. See [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for details.\"\n\n**Lines 48-70 (Ownership Layer Deep Dive):** DELETE this entire section (Anchoring, Tokenization, Funding Mechanisms, Multi-Chain Support). Replace with a single short paragraph:\n\n\"## Ownership Layer (Planned)\n\nThe ownership layer is not yet implemented. The planned design freezes ATProto records and anchors them on-chain before funding, ensuring funders know exactly what they are paying for. For the full planned design — including anchoring, tokenization, funding mechanisms, and multi-chain support — see [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization).\"\n\n**Lines 72-95 (How the Layers Connect):** Keep the first paragraph about content living on ATProto (line 76). Remove lines 78-80 (detailed planned flow) and the cross-layer example (lines 86-95). Replace with:\n\n\"A hypercert's **ownership and funding state** will live on-chain once the tokenization layer is built. The planned bridge is a freeze-then-fund mechanism. See [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for the full cross-layer design.\"\n\nKeep the callout (lines 82-84) but simplify: \"The separation matters. ATProto provides data portability — users can switch servers, applications can read across the network, and records outlive any single platform. On-chain anchoring will provide ownership and funding guarantees. Neither layer can provide both properties alone.\"\n\n**Lines 105-107 (Why Not Fully Off-Chain?):** Condense. Keep the core point (mutable records are fine for collaboration but funding requires immutability) but remove the detailed freeze-then-fund explanation. Add link.\n\n**Lines 115-119 (Why This Separation):** Simplify. Remove \"through the planned freeze-then-fund mechanism\" and \"Once the tokenization layer is built\". Just say: \"Each layer does what it does best. ATProto handles identity, data portability, and schemas. On-chain anchoring will handle ownership, funding, and immutability. This separation reduces costs, increases flexibility, and maintains portability.\"\n\n**Lines 121-131 (What This Enables):** Remove \"(Planned)\" labels and the detailed planned descriptions. Keep the 4 bullet points but make them concise. For the two that work today, keep as-is. For the two that are planned, just say \"planned\" in one phrase and link to the planned page.\n\n**Add to Next Steps:** Add a link: \"For the planned on-chain funding and tokenization design, see [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization).\"\n\n### architecture/data-flow-and-lifecycle.md\n\n**Line 22 (Funding summary):** Condense to: \"**Funding** connects ownership to the claim. The on-chain funding layer is [planned but not yet implemented](/architecture/planned-funding-and-tokenization). The intended design freezes ATProto records before funding to ensure funders know exactly what they are paying for.\"\n\n**Line 24 (Accumulation):** Simplify to: \"**Accumulation** continues indefinitely. More evaluations arrive. Additional evidence gets attached. The data layer continues evolving.\"\n\n**Line 29 (diagram):** Change \"On-chain (planned)\" to just \"On-chain*\" and add a footnote: \"*On-chain layer is planned. See [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization).\"\n\n**Lines 146-187 (Stage 5: Funding \u0026 Ownership):** DELETE the detailed content. Replace with a short section:\n\n\"## Stage 5: Funding \u0026 Ownership (Planned)\n\nThe on-chain funding layer is not yet implemented. The planned design: before a hypercert can be funded, its ATProto records are frozen and the snapshot is anchored on-chain. This ensures funders know exactly what they are paying for — the cert's contents cannot change after freezing.\n\nFor the full planned design — including anchoring, tokenization, funding mechanisms, funding readiness patterns, and multi-chain support — see [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization).\"\n\n**Lines 205-218 (Ownership Transfers, Long-Term Value in Stage 6):** Remove the \"Ownership Transfers\" subsection entirely (it's all planned content). Keep \"Long-Term Value\" but simplify: \"The separation of data and ownership enables long-term value accumulation. A hypercert's reputation can grow as evaluations accumulate. The data remains portable and accessible regardless of future ownership changes.\"\n\nRemove the timeline diagram (lines 213-218) or simplify it to only show what exists today:\n```\nTime →\n─────────────────────────────────────────────────────────\nCreation Evaluation 1 Discovery Evaluation 2 Evidence Evaluation 3\n ↓ ↓ ↓ ↓ ↓ ↓\n PDS PDS-Eva1 Indexer PDS-Eva2 PDS PDS-Eva3\n```\n\n## Test\n! grep -q 'Multi-Chain Support' documentation/pages/architecture/overview.md \u0026\u0026 \\\ngrep -c 'planned-funding-and-tokenization' documentation/pages/architecture/overview.md | grep -qv '^0$' \u0026\u0026 \\\ngrep -c 'planned-funding-and-tokenization' documentation/pages/architecture/data-flow-and-lifecycle.md | grep -qv '^0$' \u0026\u0026 \\\n! grep -q 'Anchoring (Planned)' documentation/pages/architecture/overview.md \u0026\u0026 \\\n! grep -q 'Tokenization (Planned)' documentation/pages/architecture/overview.md \u0026\u0026 \\\n! grep -q 'Funding Mechanisms (Planned)' documentation/pages/architecture/overview.md \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Remove the Data Layer sections — those describe what exists today\n- Remove the Key Design Decisions section entirely — just simplify it\n- Remove the \"What This Enables\" section — just simplify it\n- Add new planned content — we're REMOVING it and linking to the dedicated page\n- Change Stages 1-4 in data-flow-and-lifecycle.md\n- Change the Cross-PDS References or \"What This Flow Enables\" sections in data-flow-and-lifecycle.md","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T17:13:45.724521+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T17:19:22.582072+13:00","closed_at":"2026-02-16T17:19:22.582072+13:00","close_reason":"4ed89bd Strip planned content from architecture docs and link to planned page","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-yzy.3","depends_on_id":"docs-yzy","type":"parent-child","created_at":"2026-02-16T17:13:45.725402+13:00","created_by":"sharfy-test.climateai.org"},{"issue_id":"docs-yzy.3","depends_on_id":"docs-yzy.1","type":"blocks","created_at":"2026-02-16T17:14:24.15891+13:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"docs-yzy.4","title":"Strip planned content from 5 getting-started and reference pages — link to planned page","description":"## Files\n- documentation/pages/getting-started/the-hypercerts-infrastructure.md (modify)\n- documentation/pages/getting-started/why-atproto.md (modify)\n- documentation/pages/getting-started/why-were-building-hypercerts.md (modify)\n- documentation/pages/getting-started/introduction-to-impact-claims.md (modify)\n- documentation/pages/reference/faq.md (modify)\n\n## What to do\n\nRemove detailed planned/TBD content from these 5 pages. Replace with brief mentions and links to /architecture/planned-funding-and-tokenization.\n\n### the-hypercerts-infrastructure.md\n\n**Line 7:** Change to: \"Hypercerts runs on AT Protocol for data. On-chain anchoring for ownership and funding is [planned](/architecture/planned-funding-and-tokenization).\"\n\n**Lines 19-23 (The funding layer section):** Condense to 2-3 sentences max:\n\n\"#### The funding layer: on-chain anchoring (planned)\n\nThe on-chain funding layer is not yet implemented. The planned design uses a freeze-then-fund model: before a hypercert can be funded, its ATProto records are frozen and anchored on-chain, ensuring funders know exactly what they are paying for. See [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for the full design.\"\n\n**Lines 25-29 (How the layers connect):** Condense. Keep the AT-URI example. Remove the detailed planned flow. Just say: \"A claim can exist on ATProto without ever being frozen for funding. The two layers are loosely coupled and complement each other.\"\n\n**Lines 53-57 (Step 4):** Condense to:\n\n\"#### 4. The hypercert is frozen and funded (planned)\n\nIn the planned design, Carol's funding app will freeze Alice's hypercert and anchor the snapshot on-chain. Carol knows exactly what she is paying for because the frozen claim cannot change. The tokenization layer is not yet implemented — see [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for details.\"\n\n### why-atproto.md\n\n**Lines 49-51 (ATProto + Blockchain section):** Condense to:\n\n\"ATProto handles the data layer — claims, evidence, evaluations, trust signals. On-chain anchoring is planned to handle the funding layer. The intended design: hypercerts are frozen and anchored on-chain before funding, ensuring funders know exactly what they're paying for. The tokenization layer is not yet implemented — see [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for the full design.\"\n\n### why-were-building-hypercerts.md\n\n**Line 182:** Keep as-is: \"That's where onchain anchoring comes in — and eventually, tokenization.\"\n\n**Lines 184-192 (The Ownership \u0026 Funding Layer):** Condense to:\n\n\"#### The Funding Layer: On-chain Anchoring (Planned)\n\nATProto provides the data layer. But funding requires a stronger guarantee: funders need to know that what they're paying for won't change after the fact. The planned approach is freeze-then-fund — before a hypercert can be funded, its ATProto records are frozen and anchored on-chain. The tokenization layer is not yet implemented. See [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for the full design.\"\n\n### introduction-to-impact-claims.md\n\n**Line 21:** Condense to: \"Hypercerts can exist as a standalone activity claim, or they can be [frozen and funded](/architecture/planned-funding-and-tokenization) on-chain (planned).\"\n\n### faq.md\n\n**Line 18 (How is this different):** Condense to: \"The new protocol is built on AT Protocol instead of purely on-chain. This gives data portability, richer schemas, and lower costs. On-chain anchoring for funding is [planned](/architecture/planned-funding-and-tokenization) but not yet implemented.\"\n\n**Lines 20-22 (Do I need a blockchain wallet?):** Condense to: \"Not to create or evaluate hypercerts — you only need an ATProto account (DID). A blockchain wallet will be needed for on-chain funding once the [tokenization layer](/architecture/planned-funding-and-tokenization) is built.\"\n\n**Lines 37-38 (How do I fund?):** Condense to: \"The on-chain funding layer is not yet implemented. The planned design freezes ATProto records before funding to ensure funders know exactly what they are paying for. See [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for details.\"\n\n**Lines 44-46 (What chains?):** Condense to: \"The protocol intends to be chain-agnostic. The on-chain layer is not yet implemented — see [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization).\"\n\n## Test\ngrep -c 'planned-funding-and-tokenization' documentation/pages/getting-started/the-hypercerts-infrastructure.md | grep -qv '^0$' \u0026\u0026 \\\ngrep -c 'planned-funding-and-tokenization' documentation/pages/getting-started/why-atproto.md | grep -qv '^0$' \u0026\u0026 \\\ngrep -c 'planned-funding-and-tokenization' documentation/pages/getting-started/why-were-building-hypercerts.md | grep -qv '^0$' \u0026\u0026 \\\ngrep -c 'planned-funding-and-tokenization' documentation/pages/getting-started/introduction-to-impact-claims.md | grep -qv '^0$' \u0026\u0026 \\\ngrep -c 'planned-funding-and-tokenization' documentation/pages/reference/faq.md | grep -qv '^0$' \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Remove the data layer sections — those describe what exists today\n- Remove the data flow walkthrough (Steps 1-3 in infrastructure) — those are accurate\n- Remove the Data Integrity and Trust section in infrastructure — accurate\n- Change anything in why-were-building-hypercerts.md before line 172\n- Add new planned content — we're condensing and linking\n- Change the \"Keep Reading\" links in infrastructure (except the blockchain-integration link which is handled by another task)","status":"closed","priority":2,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T17:14:18.331984+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-17T05:24:43.922182+13:00","closed_at":"2026-02-17T05:24:43.922182+13:00","close_reason":"Superseded by PR #4 merge — all planned-funding-and-tokenization references updated to funding-and-tokenization","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-yzy.4","depends_on_id":"docs-yzy","type":"parent-child","created_at":"2026-02-16T17:14:18.333338+13:00","created_by":"sharfy-test.climateai.org"},{"issue_id":"docs-yzy.4","depends_on_id":"docs-yzy.1","type":"blocks","created_at":"2026-02-16T17:14:24.250458+13:00","created_by":"sharfy-test.climateai.org"}]} +{"id":"docs-yzy.4","title":"Strip planned content from 5 getting-started and reference pages — link to planned page","description":"## Files\n- documentation/pages/getting-started/the-hypercerts-infrastructure.md (modify)\n- documentation/pages/getting-started/why-atproto.md (modify)\n- documentation/pages/getting-started/why-were-building-hypercerts.md (modify)\n- documentation/pages/getting-started/introduction-to-impact-claims.md (modify)\n- documentation/pages/reference/faq.md (modify)\n\n## What to do\n\nRemove detailed planned/TBD content from these 5 pages. Replace with brief mentions and links to /architecture/planned-funding-and-tokenization.\n\n### the-hypercerts-infrastructure.md\n\n**Line 7:** Change to: \"Hypercerts runs on AT Protocol for data. On-chain anchoring for ownership and funding is [planned](/architecture/planned-funding-and-tokenization).\"\n\n**Lines 19-23 (The funding layer section):** Condense to 2-3 sentences max:\n\n\"#### The funding layer: on-chain anchoring (planned)\n\nThe on-chain funding layer is not yet implemented. The planned design uses a freeze-then-fund model: before a hypercert can be funded, its ATProto records are frozen and anchored on-chain, ensuring funders know exactly what they are paying for. See [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for the full design.\"\n\n**Lines 25-29 (How the layers connect):** Condense. Keep the AT-URI example. Remove the detailed planned flow. Just say: \"A claim can exist on ATProto without ever being frozen for funding. The two layers are loosely coupled and complement each other.\"\n\n**Lines 53-57 (Step 4):** Condense to:\n\n\"#### 4. The hypercert is frozen and funded (planned)\n\nIn the planned design, Carol's funding app will freeze Alice's hypercert and anchor the snapshot on-chain. Carol knows exactly what she is paying for because the frozen claim cannot change. The tokenization layer is not yet implemented — see [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for details.\"\n\n### why-atproto.md\n\n**Lines 49-51 (ATProto + Blockchain section):** Condense to:\n\n\"ATProto handles the data layer — claims, evidence, evaluations, trust signals. On-chain anchoring is planned to handle the funding layer. The intended design: hypercerts are frozen and anchored on-chain before funding, ensuring funders know exactly what they're paying for. The tokenization layer is not yet implemented — see [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for the full design.\"\n\n### why-were-building-hypercerts.md\n\n**Line 182:** Keep as-is: \"That's where onchain anchoring comes in — and eventually, tokenization.\"\n\n**Lines 184-192 (The Ownership \u0026 Funding Layer):** Condense to:\n\n\"#### The Funding Layer: On-chain Anchoring (Planned)\n\nATProto provides the data layer. But funding requires a stronger guarantee: funders need to know that what they're paying for won't change after the fact. The planned approach is freeze-then-fund — before a hypercert can be funded, its ATProto records are frozen and anchored on-chain. The tokenization layer is not yet implemented. See [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for the full design.\"\n\n### introduction-to-impact-claims.md\n\n**Line 21:** Condense to: \"Hypercerts can exist as a standalone activity claim, or they can be [frozen and funded](/architecture/planned-funding-and-tokenization) on-chain (planned).\"\n\n### faq.md\n\n**Line 18 (How is this different):** Condense to: \"The new protocol is built on AT Protocol instead of purely on-chain. This gives data portability, richer schemas, and lower costs. On-chain anchoring for funding is [planned](/architecture/planned-funding-and-tokenization) but not yet implemented.\"\n\n**Lines 20-22 (Do I need a blockchain wallet?):** Condense to: \"Not to create or evaluate hypercerts — you only need an ATProto account (DID). A blockchain wallet will be needed for on-chain funding once the [tokenization layer](/architecture/planned-funding-and-tokenization) is built.\"\n\n**Lines 37-38 (How do I fund?):** Condense to: \"The on-chain funding layer is not yet implemented. The planned design freezes ATProto records before funding to ensure funders know exactly what they are paying for. See [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for details.\"\n\n**Lines 44-46 (What chains?):** Condense to: \"The protocol intends to be chain-agnostic. The on-chain layer is not yet implemented — see [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization).\"\n\n## Test\ngrep -c 'planned-funding-and-tokenization' documentation/pages/getting-started/the-hypercerts-infrastructure.md | grep -qv '^0$' \u0026\u0026 \\\ngrep -c 'planned-funding-and-tokenization' documentation/pages/getting-started/why-atproto.md | grep -qv '^0$' \u0026\u0026 \\\ngrep -c 'planned-funding-and-tokenization' documentation/pages/getting-started/why-were-building-hypercerts.md | grep -qv '^0$' \u0026\u0026 \\\ngrep -c 'planned-funding-and-tokenization' documentation/pages/getting-started/introduction-to-impact-claims.md | grep -qv '^0$' \u0026\u0026 \\\ngrep -c 'planned-funding-and-tokenization' documentation/pages/reference/faq.md | grep -qv '^0$' \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Remove the data layer sections — those describe what exists today\n- Remove the data flow walkthrough (Steps 1-3 in infrastructure) — those are accurate\n- Remove the Data Integrity and Trust section in infrastructure — accurate\n- Change anything in why-were-building-hypercerts.md before line 172\n- Add new planned content — we're condensing and linking\n- Change the \"Keep Reading\" links in infrastructure (except the blockchain-integration link which is handled by another task)","status":"closed","priority":2,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T17:14:18.331984+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-17T05:24:43.922182+13:00","closed_at":"2026-02-17T05:24:43.922182+13:00","close_reason":"Superseded by PR #4 merge — all planned-funding-and-tokenization references updated to funding-and-tokenization","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-yzy.4","depends_on_id":"docs-yzy.1","type":"blocks","created_at":"2026-02-16T17:14:24.250458+13:00","created_by":"sharfy-test.climateai.org"},{"issue_id":"docs-yzy.4","depends_on_id":"docs-yzy","type":"parent-child","created_at":"2026-02-16T17:14:18.333338+13:00","created_by":"sharfy-test.climateai.org"}]} {"id":"docs-z1q","title":"Epic: Search Improvements","description":"Fix search UX: no keyboard navigation of results (arrow keys dont work), no fuzzy matching (typo = 0 results), empty state shows all pages instead of popular links, no result grouping by section. Covers UX findings #21, 22, 36, 37.","status":"closed","priority":2,"issue_type":"epic","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","created_at":"2026-02-20T19:54:59.33049+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:06:38.262731+08:00","closed_at":"2026-02-20T20:06:38.262731+08:00","close_reason":"1094f5d all search tasks complete","labels":["scope:medium"]} {"id":"docs-z1q.1","title":"Search keyboard nav + fuzzy matching + result grouping + empty state","description":"## Files\n- components/SearchDialog.js (modify)\n- styles/globals.css (modify)\n- lib/navigation.js (modify — add section info to flattenNavigation)\n\n## What to do\n\n### Keyboard navigation of results (finding #21)\nAdd arrow key support to the search dialog. Track a `selectedIndex` state (default -1 = none selected):\n\n1. Add `const [selectedIndex, setSelectedIndex] = useState(-1)` state\n2. Reset selectedIndex to 0 when results change (in a useEffect on results.length or query)\n3. In the `onKeyDown` handler on the input, add:\n - ArrowDown: `e.preventDefault(); setSelectedIndex(i =\u003e Math.min(i + 1, results.length - 1))`\n - ArrowUp: `e.preventDefault(); setSelectedIndex(i =\u003e Math.max(i - 1, 0))`\n - Enter: navigate to `results[selectedIndex]` (or results[0] if selectedIndex is -1)\n4. Add class `search-result-item-selected` to the item at `selectedIndex`\n5. Use a ref to scroll the selected item into view: `itemRefs[selectedIndex]?.scrollIntoView({ block: \"nearest\" })`\n\nCSS for `.search-result-item-selected`:\n```css\n.search-result-item-selected {\n background: var(--hover-bg);\n}\n```\n\n### Fuzzy matching (finding #22)\nReplace the exact substring match with a simple fuzzy algorithm. Do NOT add external dependencies.\n\nImplement a `fuzzyMatch(query, text)` function:\n- Convert both to lowercase\n- Check if all characters of query appear in text in order (not necessarily contiguous)\n- Return true/false\n\n```js\nfunction fuzzyMatch(query, text) {\n const q = query.toLowerCase();\n const t = text.toLowerCase();\n let qi = 0;\n for (let ti = 0; ti \u003c t.length \u0026\u0026 qi \u003c q.length; ti++) {\n if (t[ti] === q[qi]) qi++;\n }\n return qi === q.length;\n}\n```\n\nUse fuzzyMatch for filtering, then sort by: exact substring match first, then fuzzy-only matches.\n\n### Result grouping by section (finding #37)\nModify `flattenNavigation` in `lib/navigation.js` to also return the section name for each page. Change the return type from `{ title, path }` to `{ title, path, section }`.\n\nIn `flattenNavigation`, track the current section:\n```js\nexport function flattenNavigation(nav = navigation, currentSection = \"\") {\n const result = [];\n for (const item of nav) {\n const section = item.section || currentSection;\n if (item.path) {\n result.push({ title: item.title, path: item.path, section });\n }\n if (item.children) {\n result.push(...flattenNavigation(item.children, section));\n }\n }\n return result;\n}\n```\n\nIn SearchDialog, group results by section. Render section headers between groups:\n```jsx\n{Object.entries(groupedResults).map(([section, items]) =\u003e (\n \u003cli key={section}\u003e\n \u003cdiv className=\"search-result-section\"\u003e{section || \"General\"}\u003c/div\u003e\n \u003cul className=\"search-results-list\"\u003e\n {items.map(page =\u003e (/* existing result item */))}\n \u003c/ul\u003e\n \u003c/li\u003e\n))}\n```\n\nCSS for `.search-result-section`: font-size 11px, font-weight 700, text-transform uppercase, letter-spacing 0.06em, color var(--color-text-secondary), padding 8px 12px 4px, margin-top 4px.\n\n### Empty state (finding #36)\nWhen query is empty, instead of showing ALL pages, show a curated set of 5-6 popular pages grouped as \"Quick Links\":\n- Quickstart\n- What are Hypercerts?\n- Core Data Model\n- Scaffold Starter App\n- Architecture Overview\n- Glossary\n\nFilter these from allPages by path. Show them under a \"Quick Links\" section header.\n\nWhen query has no matches, show \"No results for [query]\" with the quick links below it as fallback.\n\n## Dont\n- Do NOT add any npm dependencies (no fuse.js, no external fuzzy library)\n- Do NOT change the Escape key behavior\n- Do NOT change the ⌘K shortcut\n- Do NOT change the search dialog visual layout (width, position, overlay)","acceptance_criteria":"1. Arrow Down/Up keys move a visual highlight through search results\n2. Enter navigates to the currently highlighted result\n3. Typing a partial/misspelled query still returns relevant results (e.g., \"quikstart\" matches \"Quickstart\")\n4. Results are grouped under section headers (e.g., \"Get Started\", \"Core Concepts\", \"Tools\")\n5. Empty state shows 5-6 Quick Links instead of dumping all 40 pages\n6. No external dependencies were added\n7. selectedIndex resets when query changes\n8. Build passes: npm run build -- --webpack","status":"closed","priority":2,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":50,"created_at":"2026-02-20T19:55:26.436073+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:06:32.852484+08:00","closed_at":"2026-02-20T20:06:32.852484+08:00","close_reason":"1094f5d search keyboard nav + fuzzy + grouping","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-z1q.1","depends_on_id":"docs-z1q","type":"parent-child","created_at":"2026-02-20T19:55:26.437153+08:00","created_by":"einstein.climateai.org"}]} {"id":"docs-z6h","title":"Epic: Mobile Responsiveness Fixes","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T23:33:01.220879+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T23:41:13.491601+13:00","closed_at":"2026-02-16T23:41:13.491601+13:00","close_reason":"b2ca35e All 5 mobile responsiveness tasks complete"} diff --git a/styles/globals.css b/styles/globals.css index 12ee40c..c7e27ba 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -251,7 +251,8 @@ html.dark .layout-content td { } html.dark .layout-content blockquote { - border-left-color: oklch(0.35 0.01 260); + border-left-color: oklch(0.40 0.01 260); + background: oklch(0.16 0.005 260); } html.dark .pagination { @@ -989,6 +990,14 @@ a.sidebar-link-active:hover { margin-bottom: var(--space-2); } +/* Scroll margin so anchor links clear the sticky header */ +.layout-content h1, +.layout-content h2, +.layout-content h3, +.layout-content h4 { + scroll-margin-top: calc(var(--header-height) + var(--space-6)); +} + /* Bold text */ .layout-content strong, .layout-content b { @@ -1028,7 +1037,7 @@ a.sidebar-link-active:hover { opacity: 1; } .layout-content p { - margin-bottom: var(--space-4); + margin-bottom: var(--space-5); } .layout-content article > p:last-child { @@ -1068,7 +1077,7 @@ a.sidebar-link-active:hover { /* Code blocks */ .codeblock { - margin-bottom: var(--space-4); + margin-bottom: var(--space-6); border-radius: var(--radius-lg); overflow: hidden; border: 1px solid #1e3a5f; @@ -1148,7 +1157,7 @@ a.sidebar-link-active:hover { padding: var(--space-4); border-radius: var(--radius-lg); overflow-x: auto; - margin-bottom: var(--space-4); + margin-bottom: var(--space-6); font-family: var(--font-mono); font-size: 13px; line-height: 20px; @@ -1166,7 +1175,7 @@ a.sidebar-link-active:hover { .layout-content td code, .layout-content th code { background: var(--color-bg-subtle); - padding: 1px 4px; + padding: 2px 6px; border-radius: var(--radius-sm); border: 1px solid var(--color-border); } @@ -1211,12 +1220,14 @@ a.sidebar-link-active:hover { /* Blockquotes */ .layout-content blockquote { - border-left: 1px solid oklch(0.80 0.01 260); - padding: 5px 0 5px 10px; - margin: var(--space-4) 0; + border-left: 3px solid var(--color-border-strong); + padding: var(--space-4) var(--space-5); + margin: var(--space-6) 0; color: var(--color-text-secondary); - font-size: 14px; - line-height: 20px; + font-size: 16px; + line-height: 1.6; + background: var(--color-bg-subtle); + border-radius: 0 var(--radius-md) var(--radius-md) 0; } /* Horizontal rules */