diff --git a/docs/superpowers/plans/2026-04-08-checklist-hyperlinks.md b/docs/superpowers/plans/2026-04-08-checklist-hyperlinks.md
deleted file mode 100644
index 8f9899b..0000000
--- a/docs/superpowers/plans/2026-04-08-checklist-hyperlinks.md
+++ /dev/null
@@ -1,994 +0,0 @@
-# Checklist Hyperlinks Implementation Plan
-
-> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
-
-**Goal:** Auto-detect URLs in checklist items and render them as clickable hyperlinks across all views (editor, done section, card preview, shared view), with a link popover in the editor matching Google Keep's UX.
-
-**Architecture:** A `linkifyText()` utility HTML-escapes text then wraps detected URLs in `` tags. Active checklist items switch from `` to `contenteditable` divs to render inline links. A `LinkPopover` component appears on link click in the editor. Read-only views (done section, NoteCard, shared view) use `{@html linkifyText()}` with direct click-to-open.
-
-**Tech Stack:** Svelte 5, TypeScript, Vitest, Playwright, Lucide icons
-
-**Spec:** `docs/superpowers/specs/2026-04-08-checklist-hyperlinks-design.md`
-
----
-
-### Task 1: `linkifyText` and `unlinkifyHtml` utilities
-
-**Files:**
-- Modify: `src/lib/utils/checklist.ts` (append new functions after line 153)
-- Test: `src/lib/utils/checklist.test.ts` (append new describe blocks)
-
-- [ ] **Step 1: Write failing tests for `linkifyText`**
-
-Add to `src/lib/utils/checklist.test.ts`:
-
-```typescript
-import { linkifyText, unlinkifyHtml } from '$lib/utils/checklist.js';
-
-describe('linkifyText', () => {
- it('converts https URLs to anchor tags', () => {
- expect(linkifyText('visit https://example.com today')).toBe(
- 'visit https://example.com today'
- );
- });
-
- it('converts http URLs to anchor tags', () => {
- expect(linkifyText('go to http://example.com')).toBe(
- 'go to http://example.com'
- );
- });
-
- it('converts bare www URLs with https href', () => {
- expect(linkifyText('check www.example.com')).toBe(
- 'check www.example.com'
- );
- });
-
- it('handles multiple URLs in one string', () => {
- const result = linkifyText('see https://a.com and https://b.com');
- expect(result).toContain('href="https://a.com"');
- expect(result).toContain('href="https://b.com"');
- });
-
- it('returns plain text unchanged when no URLs present', () => {
- expect(linkifyText('just some text')).toBe('just some text');
- });
-
- it('HTML-escapes user content to prevent XSS', () => {
- expect(linkifyText('')).toBe(
- '<script>alert("xss")</script>'
- );
- });
-
- it('HTML-escapes text around URLs', () => {
- expect(linkifyText('bold https://example.com')).toContain('<b>bold</b>');
- expect(linkifyText('bold https://example.com')).toContain('href="https://example.com"');
- });
-
- it('handles URL at start of string', () => {
- expect(linkifyText('https://example.com is great')).toContain('href="https://example.com"');
- });
-
- it('handles URL at end of string', () => {
- expect(linkifyText('visit https://example.com')).toContain('href="https://example.com"');
- });
-
- it('does not linkify partial matches without protocol or www', () => {
- expect(linkifyText('example.com is not linked')).toBe('example.com is not linked');
- });
-
- it('handles URLs with paths and query strings', () => {
- const url = 'https://example.com/path?q=1&b=2';
- const result = linkifyText(url);
- expect(result).toContain('href="https://example.com/path?q=1&b=2"');
- });
-
- it('strips trailing punctuation from URLs', () => {
- expect(linkifyText('see https://example.com.')).toBe(
- 'see https://example.com.'
- );
- });
-
- it('returns empty string for empty input', () => {
- expect(linkifyText('')).toBe('');
- });
-});
-```
-
-- [ ] **Step 2: Run tests to verify they fail**
-
-Run: `pnpm vitest run src/lib/utils/checklist.test.ts`
-Expected: FAIL — `linkifyText` is not exported from checklist.ts
-
-- [ ] **Step 3: Implement `linkifyText`**
-
-Add to `src/lib/utils/checklist.ts` after the `serializeChecklist` function:
-
-```typescript
-function escapeHtml(text: string): string {
- return text
- .replace(/&/g, '&')
- .replace(//g, '>')
- .replace(/"/g, '"');
-}
-
-const URL_REGEX = /(?:https?:\/\/|www\.)[^\s<]+[^\s<.,;:!?)'"}\]]/g;
-
-export function linkifyText(text: string): string {
- if (!text) return '';
-
- const matches: { index: number; match: string; href: string }[] = [];
- let m: RegExpExecArray | null;
- const regex = new RegExp(URL_REGEX.source, 'g');
-
- while ((m = regex.exec(text)) !== null) {
- const match = m[0];
- const href = match.startsWith('www.') ? `https://${match}` : match;
- matches.push({ index: m.index, match, href });
- }
-
- if (matches.length === 0) return escapeHtml(text);
-
- let result = '';
- let lastIndex = 0;
- for (const { index, match, href } of matches) {
- result += escapeHtml(text.slice(lastIndex, index));
- result += `${escapeHtml(match)}`;
- lastIndex = index + match.length;
- }
- result += escapeHtml(text.slice(lastIndex));
- return result;
-}
-```
-
-- [ ] **Step 4: Run `linkifyText` tests to verify they pass**
-
-Run: `pnpm vitest run src/lib/utils/checklist.test.ts`
-Expected: All `linkifyText` tests PASS
-
-- [ ] **Step 5: Write failing tests for `unlinkifyHtml`**
-
-Add to `src/lib/utils/checklist.test.ts`:
-
-```typescript
-describe('unlinkifyHtml', () => {
- it('strips anchor tags and returns text content', () => {
- expect(unlinkifyHtml('visit https://example.com today'))
- .toBe('visit https://example.com today');
- });
-
- it('returns plain text unchanged', () => {
- expect(unlinkifyHtml('just text')).toBe('just text');
- });
-
- it('handles multiple anchor tags', () => {
- expect(unlinkifyHtml('https://a.com and https://b.com'))
- .toBe('https://a.com and https://b.com');
- });
-
- it('returns empty string for empty input', () => {
- expect(unlinkifyHtml('')).toBe('');
- });
-
- it('handles div and br tags from contenteditable', () => {
- expect(unlinkifyHtml('line1
line2')).toBe('line1line2');
- });
-});
-```
-
-- [ ] **Step 6: Run tests to verify they fail**
-
-Run: `pnpm vitest run src/lib/utils/checklist.test.ts`
-Expected: FAIL — `unlinkifyHtml` is not exported
-
-- [ ] **Step 7: Implement `unlinkifyHtml`**
-
-Add to `src/lib/utils/checklist.ts` after `linkifyText`:
-
-```typescript
-export function unlinkifyHtml(html: string): string {
- if (!html) return '';
- return html.replace(/<[^>]*>/g, '');
-}
-```
-
-- [ ] **Step 8: Run all tests to verify they pass**
-
-Run: `pnpm vitest run src/lib/utils/checklist.test.ts`
-Expected: All tests PASS
-
-- [ ] **Step 9: Commit**
-
-```bash
-git add src/lib/utils/checklist.ts src/lib/utils/checklist.test.ts
-git commit -m "feat: add linkifyText and unlinkifyHtml utilities for checklist hyperlinks"
-```
-
----
-
-### Task 2: CSS styles for checklist links
-
-**Files:**
-- Modify: `src/app.css` (add after line 207, before the tooltip section)
-
-- [ ] **Step 1: Add checklist-link styles**
-
-Add to `src/app.css` after the `.prose ul.task-list li p { margin: 0; }` block (line 207) and before the `/* Custom tooltips */` comment (line 209):
-
-```css
-/* Checklist link styles */
-.checklist-link {
- color: var(--primary);
- text-decoration: underline;
- text-underline-offset: 2px;
-}
-
-.checklist-link:hover {
- color: var(--primary-hover);
-}
-```
-
-- [ ] **Step 2: Commit**
-
-```bash
-git add src/app.css
-git commit -m "style: add checklist-link CSS for hyperlink display"
-```
-
----
-
-### Task 3: LinkPopover component
-
-**Files:**
-- Create: `src/lib/components/LinkPopover.svelte`
-
-- [ ] **Step 1: Create the LinkPopover component**
-
-Create `src/lib/components/LinkPopover.svelte`:
-
-```svelte
-
-
-
-
-
-
e.stopPropagation()}
- data-testid="link-popover"
->
-
-
-```
-
-- [ ] **Step 2: Commit**
-
-```bash
-git add src/lib/components/LinkPopover.svelte
-git commit -m "feat: add LinkPopover component for checklist hyperlinks"
-```
-
----
-
-### Task 4: Convert active checklist items to `contenteditable`
-
-**Files:**
-- Modify: `src/lib/components/Checklist.svelte`
-
-This is the main change. Replace the `` with a `contenteditable` div and integrate the link popover.
-
-- [ ] **Step 1: Add imports and link popover state**
-
-In `src/lib/components/Checklist.svelte`, update the import line for checklist utilities (line 9) to include the new functions:
-
-```typescript
-import { type ChecklistItem, generateId, parseChecklist, serializeChecklist, toggleItemWithCascade, indentItem, outdentItem, linkifyText, unlinkifyHtml } from '$lib/utils/checklist.js';
-```
-
-Add the LinkPopover import after the existing component imports (after line 8):
-
-```typescript
-import LinkPopover from './LinkPopover.svelte';
-```
-
-Add link popover state after the `flipDurationMs` declaration (after line 21):
-
-```typescript
-let linkPopover = $state<{ url: string; anchor: DOMRect } | null>(null);
-```
-
-- [ ] **Step 2: Update `updateText` to accept contenteditable HTML**
-
-The existing `updateText` function (line 88) accepts a plain text string from ``. For contenteditable, the input event gives us innerHTML. Update `updateText` to extract plain text:
-
-Replace the `updateText` function (lines 88-94):
-
-```typescript
-function updateText(id: string, innerHTML: string) {
- const item = items.find((i) => i.id === id);
- if (item) {
- item.text = unlinkifyHtml(innerHTML);
- emitChange();
- }
-}
-```
-
-- [ ] **Step 3: Update `handleKeydown` for contenteditable behavior**
-
-The Backspace check (line 132) currently checks `items[index].text === ''`. This still works because `updateText` now extracts plain text on every input event. However, `handleKeydown` also references `data-testid="checklist-input"` when querying for arrow key navigation (lines 136, 140). These selectors need to work with `[contenteditable]` divs instead of `` elements.
-
-Replace the `handleKeydown` function (lines 127-156):
-
-```typescript
-function handleKeydown(e: KeyboardEvent, id: string) {
- const index = items.findIndex((i) => i.id === id);
- if (e.key === 'Enter') {
- e.preventDefault();
- addItem(index);
- } else if (e.key === 'Backspace' && items[index].text === '' && items.length > 1) {
- e.preventDefault();
- removeItem(id);
- setTimeout(() => {
- const inputs = document.querySelectorAll('[data-testid="checklist-input"]');
- inputs[Math.max(0, index - 1)]?.focus();
- }, 0);
- } else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
- const inputs = Array.from(document.querySelectorAll('[data-testid="checklist-input"]'));
- const current = inputs.findIndex((el) => el.dataset.itemId === id);
- const target = e.key === 'ArrowUp' ? current - 1 : current + 1;
- if (target >= 0 && target < inputs.length) {
- e.preventDefault();
- inputs[target].focus();
- }
- } else if (e.key === 'Tab' && !e.shiftKey) {
- e.preventDefault();
- items = indentItem(id, items, activeItems);
- emitChange();
- } else if (e.key === 'Tab' && e.shiftKey) {
- e.preventDefault();
- items = outdentItem(id, items);
- emitChange();
- }
-}
-```
-
-(The only changes: `HTMLInputElement` -> `HTMLElement` in the querySelectorAll calls, and `dataset.itemId` for the arrow key lookup.)
-
-- [ ] **Step 4: Add link click handler and paste handler**
-
-Add after the `handleKeydown` function (before the directional lock drag handle section):
-
-```typescript
-function handleLinkClick(e: MouseEvent) {
- const target = e.target as HTMLElement;
- const anchor = target.closest('a');
- if (anchor) {
- e.preventDefault();
- e.stopPropagation();
- linkPopover = { url: anchor.href, anchor: anchor.getBoundingClientRect() };
- }
-}
-
-function handlePaste(e: ClipboardEvent) {
- e.preventDefault();
- const text = e.clipboardData?.getData('text/plain') ?? '';
- const selection = window.getSelection();
- if (!selection?.rangeCount) return;
- const range = selection.getRangeAt(0);
- range.deleteContents();
- range.insertNode(document.createTextNode(text));
- range.collapse(false);
- selection.removeAllRanges();
- selection.addRange(range);
- (e.target as HTMLElement).dispatchEvent(new Event('input', { bubbles: true }));
-}
-```
-
-- [ ] **Step 5: Update `addItem` focus to work with contenteditable**
-
-Update the `addItem` function's focus logic (lines 108-110). Change `HTMLInputElement` to `HTMLElement`:
-
-```typescript
-setTimeout(() => {
- document.querySelector(`[data-item-id="${newId}"]`)?.focus();
-}, 0);
-```
-
-- [ ] **Step 6: Replace `` with `contenteditable` div in the template**
-
-Replace the `` block (lines 279-288) with:
-
-```svelte
-
- updateText(item.id, (e.target as HTMLElement).innerHTML)}
- onkeydown={(e) => handleKeydown(e, item.id)}
- onclick={handleLinkClick}
- onpaste={handlePaste}
- class="flex-1 min-w-0 bg-transparent text-sm outline-none break-words {item.checked ? 'text-[var(--text-muted)] line-through' : 'text-[var(--text)]'}"
- data-placeholder="List item"
- data-testid="checklist-input"
- data-item-id={item.id}
- role="textbox"
->{@html linkifyText(item.text)}
-```
-
-Note: The `{@html ...}` must be on the same line as the opening `>` to avoid extra whitespace in contenteditable.
-
-- [ ] **Step 7: Add empty-state placeholder CSS**
-
-Add to `src/app.css` after the `.checklist-link:hover` rule:
-
-```css
-[data-placeholder]:empty::before {
- content: attr(data-placeholder);
- color: var(--text-muted);
- pointer-events: none;
-}
-```
-
-- [ ] **Step 8: Add LinkPopover to the template**
-
-Add at the end of the component template, just before the closing `` (before line 355):
-
-```svelte
-{#if linkPopover}
- (linkPopover = null)} />
-{/if}
-```
-
-- [ ] **Step 9: Run type check**
-
-Run: `pnpm check`
-Expected: No type errors
-
-- [ ] **Step 10: Commit**
-
-```bash
-git add src/lib/components/Checklist.svelte src/lib/components/LinkPopover.svelte src/app.css
-git commit -m "feat: replace checklist text inputs with contenteditable for inline link rendering"
-```
-
----
-
-### Task 5: Linkify the done section in Checklist.svelte
-
-**Files:**
-- Modify: `src/lib/components/Checklist.svelte`
-
-- [ ] **Step 1: Linkify done item text**
-
-In `Checklist.svelte`, find the done item text span (line 339):
-
-```svelte
-{item.text}
-```
-
-Replace with:
-
-```svelte
-{@html linkifyText(item.text)}
-```
-
-- [ ] **Step 2: Linkify done parent label text**
-
-Find the parent label span (line 327):
-
-```svelte
-{group.parentLabel.text}
-```
-
-Replace with:
-
-```svelte
-{@html linkifyText(group.parentLabel.text)}
-```
-
-- [ ] **Step 3: Commit**
-
-```bash
-git add src/lib/components/Checklist.svelte
-git commit -m "feat: linkify URLs in checklist done section"
-```
-
----
-
-### Task 6: Linkify NoteCard checklist preview
-
-**Files:**
-- Modify: `src/lib/components/NoteCard.svelte`
-
-- [ ] **Step 1: Add import**
-
-Add `linkifyText` import to NoteCard.svelte. After the existing imports (e.g., after line 15):
-
-```typescript
-import { linkifyText } from '$lib/utils/checklist.js';
-```
-
-- [ ] **Step 2: Linkify checklist item text**
-
-Find the checklist item text span (line 145):
-
-```svelte
-{item.text}
-```
-
-Replace with:
-
-```svelte
-{@html linkifyText(item.text)}
-```
-
-- [ ] **Step 3: Prevent link clicks from opening the note editor**
-
-Links inside the NoteCard should open the URL instead of triggering `onEdit`. The card's `onclick` handler (line 73) currently opens the editor. Links need `e.stopPropagation()`.
-
-Add a click handler on the checklist `` (line 134) to intercept link clicks:
-
-Replace:
-```svelte
-
-```
-
-With:
-```svelte
-
- { if ((e.target as HTMLElement).closest('a')) e.stopPropagation(); }}>
-```
-
-- [ ] **Step 4: Commit**
-
-```bash
-git add src/lib/components/NoteCard.svelte
-git commit -m "feat: linkify URLs in NoteCard checklist preview"
-```
-
----
-
-### Task 7: Linkify shared note checklist view
-
-**Files:**
-- Modify: `src/routes/(share)/s/[token]/+page.svelte`
-
-- [ ] **Step 1: Add import**
-
-Add `linkifyText` import. After the existing imports (after line 4):
-
-```typescript
-import { linkifyText } from '$lib/utils/checklist.js';
-```
-
-- [ ] **Step 2: Linkify checklist item text**
-
-Find the checklist item text span (line 83):
-
-```svelte
-{item.text}
-```
-
-Replace with:
-
-```svelte
-{@html linkifyText(item.text)}
-```
-
-- [ ] **Step 3: Commit**
-
-```bash
-git add "src/routes/(share)/s/[token]/+page.svelte"
-git commit -m "feat: linkify URLs in shared note checklist view"
-```
-
----
-
-### Task 8: Update existing e2e tests for contenteditable
-
-**Files:**
-- Modify: `tests/e2e/checklist.spec.ts`
-
-The existing tests use `.fill()` and `.toHaveValue()` on checklist inputs, which work with `` but not `contenteditable`. These need to be updated.
-
-- [ ] **Step 1: Update the `createChecklistNote` helper**
-
-Replace the `createChecklistNote` function (lines 11-23):
-
-```typescript
-async function createChecklistNote(page: Page, title: string, items: string[]) {
- await page.getByTestId('new-note-btn').click();
- await page.getByTestId('note-title-input').fill(title);
- await toggleChecklistMode(page);
- for (let i = 0; i < items.length; i++) {
- await page.getByTestId('checklist-input').nth(i).focus();
- await page.keyboard.type(items[i]);
- if (i < items.length - 1) {
- await page.getByTestId('checklist-input').nth(i).press('Enter');
- }
- }
- await page.getByTestId('close-editor-btn').click();
- await expect(noteCard(page, title)).toBeVisible();
-}
-```
-
-Key change: `.fill()` replaced with `.focus()` + `page.keyboard.type()` (contenteditable doesn't support `.fill()`).
-
-- [ ] **Step 2: Update all `.fill()` calls on checklist inputs throughout the file**
-
-Every `page.getByTestId('checklist-input')...fill('Text')` must change to:
-
-```typescript
-await page.getByTestId('checklist-input')...focus();
-await page.keyboard.type('Text');
-```
-
-Affected tests and the specific changes:
-
-**"Enter key adds a new checklist item" (line 98):**
-```typescript
-// Before:
-await page.getByTestId('checklist-input').first().fill('First item');
-// After:
-await page.getByTestId('checklist-input').first().focus();
-await page.keyboard.type('First item');
-```
-
-**"Backspace on empty item removes it" (line 137):**
-```typescript
-// Before:
-await page.getByTestId('checklist-input').first().fill('First item');
-// After:
-await page.getByTestId('checklist-input').first().focus();
-await page.keyboard.type('First item');
-```
-
-**"Completed items are separated into a done section" (lines 153-155):**
-```typescript
-// Before:
-await page.getByTestId('checklist-input').first().fill('Done task');
-await page.getByTestId('checklist-input').first().press('Enter');
-await page.getByTestId('checklist-input').nth(1).fill('Pending task');
-// After:
-await page.getByTestId('checklist-input').first().focus();
-await page.keyboard.type('Done task');
-await page.getByTestId('checklist-input').first().press('Enter');
-await page.getByTestId('checklist-input').nth(1).focus();
-await page.keyboard.type('Pending task');
-```
-
-**"Arrow keys navigate between checklist items" (lines 174-178):**
-```typescript
-// Before:
-await page.getByTestId('checklist-input').first().fill('First');
-await page.getByTestId('checklist-input').first().press('Enter');
-await page.getByTestId('checklist-input').nth(1).fill('Second');
-await page.getByTestId('checklist-input').nth(1).press('Enter');
-await page.getByTestId('checklist-input').nth(2).fill('Third');
-// After:
-await page.getByTestId('checklist-input').first().focus();
-await page.keyboard.type('First');
-await page.getByTestId('checklist-input').first().press('Enter');
-await page.getByTestId('checklist-input').nth(1).focus();
-await page.keyboard.type('Second');
-await page.getByTestId('checklist-input').nth(1).press('Enter');
-await page.getByTestId('checklist-input').nth(2).focus();
-await page.keyboard.type('Third');
-```
-
-**"Tab indents / Shift+Tab outdents" (lines 197-199):**
-```typescript
-// Before:
-await page.getByTestId('checklist-input').first().fill('Parent');
-await page.getByTestId('checklist-input').first().press('Enter');
-await page.getByTestId('checklist-input').nth(1).fill('Child');
-// After:
-await page.getByTestId('checklist-input').first().focus();
-await page.keyboard.type('Parent');
-await page.getByTestId('checklist-input').first().press('Enter');
-await page.getByTestId('checklist-input').nth(1).focus();
-await page.keyboard.type('Child');
-```
-
-**"Checking a parent checks all its children" (lines 218-223):**
-```typescript
-// Before:
-await page.getByTestId('checklist-input').first().fill('Buy groceries');
-await page.getByTestId('checklist-input').first().press('Enter');
-await page.getByTestId('checklist-input').nth(1).fill('Milk');
-await page.getByTestId('checklist-input').nth(1).press('Tab');
-await page.getByTestId('checklist-input').nth(1).press('Enter');
-await page.getByTestId('checklist-input').nth(2).fill('Eggs');
-// After:
-await page.getByTestId('checklist-input').first().focus();
-await page.keyboard.type('Buy groceries');
-await page.getByTestId('checklist-input').first().press('Enter');
-await page.getByTestId('checklist-input').nth(1).focus();
-await page.keyboard.type('Milk');
-await page.getByTestId('checklist-input').nth(1).press('Tab');
-await page.getByTestId('checklist-input').nth(1).press('Enter');
-await page.getByTestId('checklist-input').nth(2).focus();
-await page.keyboard.type('Eggs');
-```
-
-**"Checking a child shows read-only parent label" (lines 237-240):**
-```typescript
-// Before:
-await page.getByTestId('checklist-input').first().fill('Groceries');
-await page.getByTestId('checklist-input').first().press('Enter');
-await page.getByTestId('checklist-input').nth(1).fill('Milk');
-// After:
-await page.getByTestId('checklist-input').first().focus();
-await page.keyboard.type('Groceries');
-await page.getByTestId('checklist-input').first().press('Enter');
-await page.getByTestId('checklist-input').nth(1).focus();
-await page.keyboard.type('Milk');
-```
-
-**"Unchecking a parent from done restores group" (lines 255-258):**
-```typescript
-// Before:
-await page.getByTestId('checklist-input').first().fill('Shopping');
-await page.getByTestId('checklist-input').first().press('Enter');
-await page.getByTestId('checklist-input').nth(1).fill('Milk');
-// After:
-await page.getByTestId('checklist-input').first().focus();
-await page.keyboard.type('Shopping');
-await page.getByTestId('checklist-input').first().press('Enter');
-await page.getByTestId('checklist-input').nth(1).focus();
-await page.keyboard.type('Milk');
-```
-
-**"Enter on a child creates a sibling" (lines 291-294):**
-```typescript
-// Before:
-await page.getByTestId('checklist-input').first().fill('Parent');
-await page.getByTestId('checklist-input').first().press('Enter');
-await page.getByTestId('checklist-input').nth(1).fill('Child 1');
-// After:
-await page.getByTestId('checklist-input').first().focus();
-await page.keyboard.type('Parent');
-await page.getByTestId('checklist-input').first().press('Enter');
-await page.getByTestId('checklist-input').nth(1).focus();
-await page.keyboard.type('Child 1');
-```
-
-- [ ] **Step 3: Update all `.toHaveValue()` assertions on checklist inputs**
-
-Replace `.toHaveValue('text')` with `.toHaveText('text')`:
-
-**"Checklist item persists" (line 49):**
-```typescript
-// Before:
-await expect(page.getByTestId('checklist-input').first()).toHaveValue('Buy milk');
-// After:
-await expect(page.getByTestId('checklist-input').first()).toHaveText('Buy milk');
-```
-
-**"Sequential check and uncheck" (line 91):**
-```typescript
-// Before:
-await expect(page.getByTestId('checklist-input').first()).toHaveValue('Buy milk');
-// After:
-await expect(page.getByTestId('checklist-input').first()).toHaveText('Buy milk');
-```
-
-**"Enter key focuses new item" (line 130):**
-```typescript
-// Before:
-await expect(page.getByTestId('checklist-input').nth(1)).toHaveValue('');
-// After:
-await expect(page.getByTestId('checklist-input').nth(1)).toHaveText('');
-```
-
-**"Completed items separated" (line 162):**
-```typescript
-// Before:
-await expect(page.getByTestId('checklist-input').first()).toHaveValue('Pending task');
-// After:
-await expect(page.getByTestId('checklist-input').first()).toHaveText('Pending task');
-```
-
-**"Delete checked items" (line 344):**
-```typescript
-// Before:
-await expect(page.getByTestId('checklist-input').first()).toHaveValue('Keep me');
-// After:
-await expect(page.getByTestId('checklist-input').first()).toHaveText('Keep me');
-```
-
-- [ ] **Step 4: Run all checklist e2e tests**
-
-Run: `pnpm test:e2e --grep "Checklist"`
-Expected: All existing tests PASS
-
-- [ ] **Step 5: Commit**
-
-```bash
-git add tests/e2e/checklist.spec.ts
-git commit -m "test: update e2e tests for contenteditable checklist inputs"
-```
-
----
-
-### Task 9: E2E tests for checklist hyperlinks
-
-**Files:**
-- Modify: `tests/e2e/checklist.spec.ts`
-
-- [ ] **Step 1: Write e2e tests for hyperlink behavior**
-
-Add to `tests/e2e/checklist.spec.ts` inside the existing `test.describe('Checklist', ...)` block, after the last test:
-
-```typescript
-test('Scenario: URL typed in a checklist item becomes a clickable link', async ({ authenticatedPage: page }) => {
- // Given a new checklist note
- await page.getByTestId('new-note-btn').click();
- await toggleChecklistMode(page);
-
- // When the user types a URL
- await page.getByTestId('checklist-input').first().focus();
- await page.keyboard.type('check https://example.com today');
-
- // Then the URL is rendered as a link
- const link = page.getByTestId('checklist-input').first().locator('a');
- await expect(link).toBeVisible();
- await expect(link).toHaveAttribute('href', 'https://example.com');
-});
-
-test('Scenario: Clicking a link in a checklist item shows the link popover', async ({ authenticatedPage: page }) => {
- // Given a checklist item with a URL
- await page.getByTestId('new-note-btn').click();
- await toggleChecklistMode(page);
- await page.getByTestId('checklist-input').first().focus();
- await page.keyboard.type('https://example.com');
-
- // When the user clicks the link
- await page.getByTestId('checklist-input').first().locator('a').click();
-
- // Then the link popover appears with an Open button
- await expect(page.getByTestId('link-popover')).toBeVisible();
- await expect(page.getByTestId('link-popover-open')).toBeVisible();
-});
-
-test('Scenario: Link popover dismisses on Escape', async ({ authenticatedPage: page }) => {
- // Given a link popover is open
- await page.getByTestId('new-note-btn').click();
- await toggleChecklistMode(page);
- await page.getByTestId('checklist-input').first().focus();
- await page.keyboard.type('https://example.com');
- await page.getByTestId('checklist-input').first().locator('a').click();
- await expect(page.getByTestId('link-popover')).toBeVisible();
-
- // When the user presses Escape
- await page.keyboard.press('Escape');
-
- // Then the popover is dismissed
- await expect(page.getByTestId('link-popover')).not.toBeVisible();
-});
-
-test('Scenario: Links in done section are clickable', async ({ authenticatedPage: page }) => {
- // Given a checklist item with a URL is checked
- await page.getByTestId('new-note-btn').click();
- await toggleChecklistMode(page);
- await page.getByTestId('checklist-input').first().focus();
- await page.keyboard.type('https://example.com');
- await page.getByTestId('checklist-checkbox').first().click();
-
- // Then the done section shows the URL as a link
- await expect(page.getByTestId('checklist-done-section').locator('a.checklist-link')).toBeVisible();
-});
-
-test('Scenario: Links in NoteCard preview are rendered', async ({ authenticatedPage: page }) => {
- // Given a checklist note with a URL
- await page.getByTestId('new-note-btn').click();
- await page.getByTestId('note-title-input').fill('Link Note');
- await toggleChecklistMode(page);
- await page.getByTestId('checklist-input').first().focus();
- await page.keyboard.type('visit https://example.com');
- await page.getByTestId('close-editor-btn').click();
-
- // Then the NoteCard preview shows the URL as a link
- const card = noteCard(page, 'Link Note');
- await expect(card.locator('a.checklist-link')).toBeVisible();
- await expect(card.locator('a.checklist-link')).toHaveAttribute('href', 'https://example.com');
-});
-```
-
-- [ ] **Step 2: Run e2e tests**
-
-Run: `pnpm test:e2e --grep "Checklist"`
-Expected: All checklist tests PASS (both existing and new)
-
-- [ ] **Step 3: Commit**
-
-```bash
-git add tests/e2e/checklist.spec.ts
-git commit -m "test: add e2e tests for checklist hyperlink support"
-```
-
----
-
-### Task 10: Full verification
-
-- [ ] **Step 1: Run unit tests**
-
-Run: `pnpm vitest run`
-Expected: All unit tests PASS
-
-- [ ] **Step 2: Run type check**
-
-Run: `pnpm check`
-Expected: No errors
-
-- [ ] **Step 3: Run full e2e test suite**
-
-Run: `pnpm test:e2e`
-Expected: All tests PASS
-
-- [ ] **Step 4: Run lint**
-
-Run: `pnpm lint`
-Expected: No lint errors
diff --git a/docs/superpowers/specs/2026-04-08-checklist-hyperlinks-design.md b/docs/superpowers/specs/2026-04-08-checklist-hyperlinks-design.md
deleted file mode 100644
index 55ffe02..0000000
--- a/docs/superpowers/specs/2026-04-08-checklist-hyperlinks-design.md
+++ /dev/null
@@ -1,130 +0,0 @@
-# Checklist Hyperlink Support
-
-**Issue:** bretzel-app/crumbs#59
-**Date:** 2026-04-08
-
-## Summary
-
-Add automatic URL detection and clickable hyperlinks to checklist items across all views — the interactive checklist editor, NoteCard previews, done section, and shared note view. Matches Google Keep's behavior where links are recognized immediately as you type.
-
-## URL Detection Utility
-
-New functions in `src/lib/utils/checklist.ts`:
-
-### `linkifyText(text: string): string`
-
-Takes plain text, HTML-escapes it, then wraps detected URLs in `` tags.
-
-- Detects `https://`, `http://`, and bare `www.` prefixes
-- Output links have `target="_blank"`, `rel="noopener"`, and `class="checklist-link"`
-- HTML-escapes the input **before** wrapping URLs to prevent XSS
-- Returns safe HTML string suitable for `{@html ...}`
-
-### `unlinkifyHtml(html: string): string`
-
-Extracts plain text from HTML content (strips all tags). Used to get raw text back from contenteditable divs for serialization to the checklist data model.
-
-## Active Items — `contenteditable` Replacing ``
-
-In `Checklist.svelte`, replace each `` (line 279-288) with a `` that renders item text with linkified HTML via `{@html linkifyText(item.text)}`.
-
-### Editing behavior
-
-- On `input` event: extract plain text via `unlinkifyHtml()`, update `item.text`, re-render with `linkifyText()`
-- Save and restore cursor position after re-linkification to avoid cursor jumping
-- On `paste`: intercept and insert `text/plain` only (strip any HTML from clipboard)
-- Prevent default on Enter (existing behavior — creates new item)
-- All existing keyboard shortcuts preserved: Enter, Backspace (delete empty), Tab (indent), Shift+Tab (outdent), ArrowUp/ArrowDown (navigate)
-
-### Link click behavior
-
-- Clicking an `
` tag inside contenteditable prevents default navigation
-- Instead, shows a `LinkPopover` component positioned near the clicked link
-- The popover has a single "Open" button that opens the URL in a new tab
-
-## Link Popover Component
-
-New component: `src/lib/components/LinkPopover.svelte`
-
-### Props
-
-- `url: string` — the href to open
-- `anchor: DOMRect` — bounding rect of the clicked link element
-- `onClose: () => void` — callback to dismiss
-
-### Appearance
-
-- Small floating element positioned below the anchor link (or above if near viewport bottom)
-- Contains a single "Open" button with the Lucide `external-link` icon
-- Styled with the retro parchment theme:
- - `bg-[var(--bg-surface)]`
- - `border border-[var(--border-subtle)]`
- - Hard-offset shadow: `var(--card-shadow)`
- - `rounded-sm`
-
-### Dismissal
-
-- Click outside the popover
-- Escape key
-- Scrolling the checklist
-
-## Read-Only Contexts
-
-### NoteCard Preview (`src/lib/components/NoteCard.svelte`)
-
-Line 145: change `{item.text}` to `{@html linkifyText(item.text)}`.
-
-Links open directly in a new tab on click (no popover — this is a read-only preview).
-
-### Shared Note View (`src/routes/(share)/s/[token]/+page.svelte`)
-
-Line 83: change `{item.text}` to `{@html linkifyText(item.text)}`.
-
-Links open directly in a new tab on click (no popover).
-
-### Done Section in `Checklist.svelte`
-
-Line 339: change `{item.text}` to `{@html linkifyText(item.text)}`.
-
-Links open directly in a new tab on click (no popover — done items aren't being edited).
-
-## Link Styling
-
-Add to `src/app.css`:
-
-```css
-.checklist-link {
- color: var(--primary);
- text-decoration: underline;
- text-underline-offset: 2px;
-}
-.checklist-link:hover {
- color: var(--primary-hover);
-}
-```
-
-## Files Changed
-
-| File | Change |
-|---|---|
-| `src/lib/utils/checklist.ts` | Add `linkifyText()` and `unlinkifyHtml()` |
-| `src/lib/components/Checklist.svelte` | `contenteditable` for active items, linkified done section, link popover integration |
-| `src/lib/components/LinkPopover.svelte` | New component — "Open" button popover |
-| `src/lib/components/NoteCard.svelte` | `{@html linkifyText(item.text)}` in checklist preview |
-| `src/routes/(share)/s/[token]/+page.svelte` | `{@html linkifyText(item.text)}` in shared checklist |
-| `src/app.css` | `.checklist-link` styles |
-
-## Testing
-
-### Unit tests
-
-- `linkifyText` — detects http/https/www URLs, escapes HTML entities, handles multiple URLs in one string, handles edge cases (URL at start/end, adjacent punctuation)
-- `unlinkifyHtml` — strips tags, preserves text content
-
-### E2E tests
-
-- Type a URL in a checklist item, verify it becomes a clickable link
-- Click a link, verify the popover appears with "Open" button
-- Verify link popover dismisses on click-outside and Escape
-- Verify links are clickable in NoteCard preview
-- Verify links are clickable in done section