# Story 2.2: Draft View UI (The Slide-Up)

Status: done

<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->

## Story

As a user,
I want to view the generated draft in a clean, reading-focused interface,
So that I can review it without the distraction of the chat.

## Acceptance Criteria

1. **Draft View Slide-Up Display**
   - Given the draft generation is complete
   - When the result is ready
   - Then a "Sheet" or modal slides up from the bottom
   - And it displays the post in "Medium-style" typography (Merriweather font)

2. **Comfortable Reading Experience**
   - Given the draft view is open
   - When the user scrolls
   - Then the reading experience is comfortable with appropriate whitespace
   - And the "Thumbs Up" and "Thumbs Down" actions are sticky or easily accessible

## Tasks / Subtasks

- [x] Implement Draft View Sheet Component
  - [x] Create `Sheet.tsx` slide-up component (custom implementation, no ShadCN)
  - [x] Configure slide-up animation from bottom (300ms ease-out)
  - [x] Set up responsive behavior (full-screen on mobile, centered card on desktop)
  - [x] Implement backdrop/dim overlay with tap-to-close

- [x] Implement Draft Content Display
  - [x] Create `DraftContent.tsx` component for Markdown rendering
  - [x] Apply Merriweather serif font for content (UI font stays Inter)
  - [x] Add generous whitespace and line-height for readability
  - [x] Style headings, paragraphs, and code blocks following Medium-style
  - [x] Add tag display section

- [x] Implement Action Bar (Thumbs Up/Down)
  - [x] Create `DraftActions.tsx` component with sticky footer
  - [x] Add Thumbs Up button (approve/copy)
  - [x] Add Thumbs Down button (regenerate feedback)
  - [x] Style buttons to be touch-friendly (44px min height)
  - [x] Add proper ARIA labels for accessibility

- [x] Integrate with ChatStore
  - [x] Connect `currentDraft` state to DraftViewSheet
  - [x] Auto-open sheet when `currentDraft` transitions from null to populated
  - [x] Handle sheet close action (clear currentDraft and showDraftView)
  - [x] Use atomic selectors for state access

- [x] Implement Copy to Clipboard (Thumbs Up)
  - [x] Implement `copyToClipboard()` utility inline in ChatStore
  - [x] Copy to clipboard with fallback for older browsers
  - [x] Mark draft as 'completed' in IndexedDB

- [x] Implement Regeneration Flow (Thumbs Down)
  - [x] Close DraftViewSheet on Thumbs Down tap
  - [x] System message to chat: "What should we change?" (Story 2.3 will add this)
  - [x] Set chat input focus for user feedback
  - [x] Preserve draft context for regeneration in Story 2.3

- [x] Implement Responsive Layout
  - [x] Mobile (< 768px): Full-screen sheet
  - [x] Desktop (>= 768px): Centered card layout (max-width ~600px)
  - [x] Ensure sheet doesn't stretch too wide on large screens
  - [x] Test keyboard navigation (Escape to close)

- [x] Test Draft View End-to-End
  - [x] Unit test: Sheet renders in closed state by default
  - [x] Unit test: Sheet opens when open prop is true
  - [x] Unit test: Markdown rendering handles code blocks, lists, links
  - [x] Unit test: DraftContent handles empty tags array
  - [x] Integration test: Sheet auto-opens after Ghostwriter completes
  - [x] Integration test: Thumbs Up copies to clipboard and marks completed
  - [x] Integration test: Thumbs Down returns to chat with feedback prompt
  - [x] Integration test: DraftViewSheet full flow with all components

## Dev Notes

### Architecture Compliance (CRITICAL)

**Logic Sandwich Pattern - DO NOT VIOLATE:**
- **UI Components** MUST NOT import `src/lib/db` or touch IndexedDB directly
- Draft status updates MUST go through `ChatService` -> `DraftService`
- Components use Zustand store via atomic selectors only
- Services return plain objects, not Dexie observables

**State Management - Atomic Selectors Required:**
```typescript
// BAD - Causes unnecessary re-renders
const { currentDraft, showDraftView } = useChatStore();

// GOOD - Atomic selectors
const currentDraft = useChatStore(s => s.currentDraft);
const showDraftView = useChatStore(s => s.showDraftView);
const closeDraftView = useChatStore(s => s.closeDraftView);
const approveDraft = useChatStore(s => s.approveDraft);
const rejectDraft = useChatStore(s => s.rejectDraft);
```

**Local-First Data Boundary:**
- Draft content already stored in IndexedDB (from Story 2.1)
- Thumbs Up action updates draft status to 'completed'
- Draft content remains in IndexedDB for history access (Epic 3)
- No draft content sent to server for any reason

### Architecture Implementation Details

**Story Purpose:**
This story implements the **"Magic Moment"** UI - the visual transformation from casual chat to polished artifact. The slide-up sheet creates a distinct mode shift that reinforces the value the Ghostwriter added. This is the climax of the user journey.

**State Management Extensions:**
```typescript
// Add to ChatStore (src/lib/store/chat-store.ts)
interface ChatStore {
  // Draft view state
  showDraftView: boolean;
  closeDraftView: () => void;
  approveDraft: (draftId: string) => Promise<void>;
  rejectDraft: (draftId: string, feedback?: string) => void;
}
```

**Component Architecture:**
```
DraftViewSheet (ShadCN Sheet wrapper)
├── DraftHeader (Title + Close button)
├── DraftContent (Markdown rendering with Merriweather font)
│   └── TagList (Tag chips)
└── DraftActions (Sticky footer with Thumbs Up/Down)
```

**Logic Flow:**
1. Ghostwriter completes generation (Story 2.1)
2. ChatStore sets `currentDraft` with generated draft
3. `showDraftView` becomes `true` (auto-trigger)
4. DraftViewSheet slides up from bottom
5. User reviews content in comfortable reading view
6. User taps Thumbs Up OR Thumbs Down:
   - **Thumbs Up**: Copy to clipboard, mark as 'completed', show success animation
   - **Thumbs Down**: Close sheet, return to chat, prompt for feedback
7. Sheet closes and flow continues

**Responsive Behavior:**
- **Mobile (< 768px)**: Full-screen sheet (covers chat completely)
- **Desktop (>= 768px)**: Centered card (max-width 600px) with visible backdrop
- This "Centered App" pattern keeps the mobile-first feel on desktop

**Files to Create:**
- `src/components/features/draft/DraftViewSheet.tsx` - Main sheet component
- `src/components/features/draft/DraftContent.tsx` - Markdown renderer with Merriweather
- `src/components/features/draft/DraftActions.tsx` - Thumbs Up/Down footer
- `src/lib/utils/clipboard.ts` - Copy to clipboard utility

**Files to Modify:**
- `src/lib/store/chat-store.ts` - Add draft view state and actions
- `src/services/draft-service.ts` - Add `updateDraftStatus()` method
- `src/services/chat-service.ts` - Add `approveDraft()` and `rejectDraft()` orchestration

### UX Design Specifications

**From UX Design Document:**

**The "Magic Moment" Visualization:**
- Clear visual shift from "Chat" (casual) to "Draft" (professional)
- Sheet slide-up animation is critical for emotional payoff
- Chat should remain visible underneath (context preserved)

**Typography - The "Split-Personality" UI:**
- **Chat UI (Input)**: Inter font (sans-serif) - casual, fast
- **Draft UI (Output)**: Merriweather font (serif) - published, authoritative
- This font change signals the transformation value

**Visual Design - "Morning Mist" Theme:**
```css
/* Draft Content Styling */
.draft-content {
  font-family: 'Merriweather', serif;
  line-height: 1.8;
  color: #334155; /* Deep Slate */
  padding: 24px;
}

.draft-title {
  font-size: 1.75rem;
  font-weight: 700;
  margin-bottom: 1.5rem;
  color: #1E293B;
}

.draft-body {
  font-size: 1.125rem;
  margin-bottom: 2rem;
}

/* Tags */
.tag-chip {
  background: #E2E8F0;
  color: #475569;
  padding: 4px 12px;
  border-radius: 9999px;
  font-size: 0.875rem;
  font-family: 'Inter', sans-serif;
}
```

**Action Bar - Sticky Footer:**
- Thumbs Up (Approve): Primary action, brand color (Slate Blue #64748B)
- Thumbs Down (Reject): Secondary action, outline style
- Minimum 44px touch targets (WCAG AA)
- Position: sticky at bottom of sheet
- ARIA labels: "Approve and copy to clipboard", "Request changes"

**Responsive - "Centered App" Pattern:**
- Desktop view should NOT stretch to full width
- Centered card container with max-width ~600px
- Generous whitespace background (Morning Mist #F8FAFC)

**Animation - Slide-Up Transition:**
- Duration: 300ms (ease-out)
- Should feel like "unveiling" the result
- No bounce or overshoot (feels professional, not playful)

### Previous Story Intelligence (from Story 2.1)

**Patterns Established (must follow):**
- **Logic Sandwich Pattern:** UI -> Zustand -> Service -> Database (strictly enforced)
- **Atomic Selectors:** All state access uses `useChatStore(s => s.field)`
- **Draft Storage:** Drafts already stored in IndexedDB via `drafts` table
- **Ghostwriter Integration:** `currentDraft` state already in ChatStore
- **DraftingIndicator:** Already shows during generation (Story 2.1)

**Key Files from Story 2.1 (Reference):**
- `src/lib/db/draft-service.ts` - Draft CRUD operations (add status update method)
- `src/lib/db/index.ts` - Drafts table with status field ('draft' | 'completed' | 'regenerated')
- `src/lib/store/chat-store.ts` - Has `currentDraft`, add draft view state
- `src/components/features/chat/DraftingIndicator.tsx` - Pattern for loading states

**Learnings to Apply:**
- Story 2.1 established the Draft data structure (id, title, content, tags, status)
- Draft is already persisted to IndexedDB when Ghostwriter completes
- Use the same DraftRecord interface from Story 2.1
- Follow the same testing pattern: unit tests for components, integration tests for flow

**Draft Data Structure (from Story 2.1):**
```typescript
interface DraftRecord {
  id: string;
  sessionId: string;
  title: string;
  content: string;      // Markdown formatted
  tags: string[];
  createdAt: number;
  status: 'draft' | 'completed' | 'regenerated';
}
```

**Integration with Ghostwriter:**
- When Ghostwriter completes, `currentDraft` is already set in ChatStore
- This story should auto-open DraftViewSheet when `currentDraft` changes
- Use `useEffect` to watch for `currentDraft` and set `showDraftView = true`

### Testing Requirements

**Unit Tests:**
- `DraftViewSheet`: Renders draft content correctly
- `DraftViewSheet`: Renders in closed state by default
- `DraftViewSheet`: Opens when showDraftView is true
- `DraftContent`: Renders Markdown with proper styling
- `DraftContent`: Handles code blocks, lists, links correctly
- `DraftActions`: Thumbs Up button calls approveDraft callback
- `DraftActions`: Thumbs Down button calls rejectDraft callback
- `ClipboardUtils`: copyToClipboard() writes to clipboard correctly

**Integration Tests:**
- Auto-open: Sheet opens when Ghostwriter completes (currentDraft populated)
- Thumbs Up: Copies to clipboard, marks draft as completed, closes sheet
- Thumbs Down: Closes sheet, adds feedback prompt to chat
- Close button: Closes sheet without changing draft status
- Escape key: Closes sheet on desktop
- Backdrop click: Closes sheet on desktop

**Edge Cases:**
- Very long draft (>2000 words): Scrolling works, actions remain accessible
- Draft with code blocks: Markdown renders correctly with syntax highlighting
- Draft with no tags: Tag section doesn't break
- Draft with special characters: Emojis, unicode render correctly
- Empty draft: Shows placeholder or error state

**Accessibility Tests:**
- Keyboard navigation: Tab through actions, Enter to activate
- Screen reader: ARIA labels on all buttons
- Focus trap: Focus stays in sheet when open
- Focus management: Focus returns to chat trigger after close
- Color contrast: All text passes WCAG AA (4.5:1)

### Component Implementation Details

**DraftViewSheet Component:**
```typescript
// src/components/features/draft/DraftViewSheet.tsx
import { Sheet, SheetContent, SheetHeader } from '@/components/ui/sheet';
import { DraftContent } from './DraftContent';
import { DraftActions } from './DraftActions';

interface DraftViewSheetProps {
  draft: Draft | null;
  open: boolean;
  onClose: () => void;
  onApprove: (draftId: string) => void;
  onReject: (draftId: string) => void;
}

export function DraftViewSheet({
  draft,
  open,
  onClose,
  onApprove,
  onReject
}: DraftViewSheetProps) {
  if (!draft) return null;

  return (
    <Sheet open={open} onOpenChange={onClose}>
      <SheetContent
        side="bottom"
        className="h-[85vh] sm:h-auto sm:max-h-[85vh] sm:max-w-[600px] sm:mx-auto"
      >
        <SheetHeader>
          {/* Close button, Title */}
        </SheetHeader>
        <DraftContent draft={draft} />
        <DraftActions
          onApprove={() => onApprove(draft.id)}
          onReject={() => onReject(draft.id)}
        />
      </SheetContent>
    </Sheet>
  );
}
```

**DraftContent Component:**
```typescript
// src/components/features/draft/DraftContent.tsx
import ReactMarkdown from 'react-markdown';

interface DraftContentProps {
  draft: Draft;
}

export function DraftContent({ draft }: DraftContentProps) {
  return (
    <div className="draft-content font-merriweather prose prose-slate max-w-none">
      <h2 className="draft-title">{draft.title}</h2>
      <ReactMarkdown className="draft-body">
        {draft.content}
      </ReactMarkdown>
      {draft.tags && draft.tags.length > 0 && (
        <div className="flex flex-wrap gap-2 mt-4">
          {draft.tags.map(tag => (
            <span key={tag} className="tag-chip">#{tag}</span>
          ))}
        </div>
      )}
    </div>
  );
}
```

**DraftActions Component:**
```typescript
// src/components/features/draft/DraftActions.tsx
import { Button } from '@/components/ui/button';
import { ThumbsUp, ThumbsDown } from 'lucide-react';

interface DraftActionsProps {
  onApprove: () => void;
  onReject: () => void;
}

export function DraftActions({ onApprove, onReject }: DraftActionsProps) {
  return (
    <div className="sticky bottom-0 flex gap-3 p-4 bg-white border-t">
      <Button
        onClick={onReject}
        variant="outline"
        className="flex-1 min-h-[44px]"
        aria-label="Request changes to this draft"
      >
        <ThumbsDown className="mr-2 h-5 w-5" />
        Not Quite
      </Button>
      <Button
        onClick={onApprove}
        className="flex-1 min-h-[44px] bg-slate-700 hover:bg-slate-800"
        aria-label="Approve and copy to clipboard"
      >
        <ThumbsUp className="mr-2 h-5 w-5" />
        Copy
      </Button>
    </div>
  );
}
```

### Markdown Rendering Strategy

**Library Choice:**
- Use `react-markdown` for Markdown parsing and rendering
- Add `remark-gfm` for GitHub Flavored Markdown support (tables, strikethrough)
- Add `rehype-highlight` for syntax highlighting in code blocks

**Styling Approach:**
- Use Tailwind's `@tailwindcss/typography` plugin for prose styling
- Override prose styles to match "Morning Mist" theme
- Apply Merriweather font to prose elements only
- Keep UI elements (buttons, tags) in Inter font

**Configuration:**
```javascript
// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      fontFamily: {
        sans: ['Inter', 'sans-serif'],
        serif: ['Merriweather', 'serif'],
      },
    },
  },
  plugins: [
    require('@tailwindcss/typography'),
  ],
};
```

### Clipboard Copy Implementation

**Utility Function:**
```typescript
// src/lib/utils/clipboard.ts
export async function copyToClipboard(text: string): Promise<boolean> {
  try {
    await navigator.clipboard.writeText(text);
    return true;
  } catch (err) {
    // Fallback for older browsers
    const textArea = document.createElement('textarea');
    textArea.value = text;
    document.body.appendChild(textArea);
    textArea.select();
    try {
      document.execCommand('copy');
      document.body.removeChild(textArea);
      return true;
    } catch (fallbackErr) {
      document.body.removeChild(textArea);
      return false;
    }
  }
}
```

**Success Feedback:**
- Use ShadCN `useToast()` hook for success notification
- Toast message: "Copied to clipboard!"
- Optional: Add confetti animation using `canvas-confetti` library

### Service Layer Extensions

**DraftService Updates:**
```typescript
// src/services/draft-service.ts
export class DraftService {
  // ... existing methods from Story 2.1

  async updateDraftStatus(
    draftId: string,
    status: 'draft' | 'completed' | 'regenerated'
  ): Promise<void> {
    await db.drafts.update(draftId, { status });
  }
}
```

**ChatService Orchestration:**
```typescript
// src/services/chat-service.ts
export class ChatService {
  async approveDraft(draftId: string): Promise<void> {
    // Update draft status to completed
    await DraftService.updateDraftStatus(draftId, 'completed');

    // Copy to clipboard
    const draft = await DraftService.getDraftById(draftId);
    const markdown = this.formatDraftAsMarkdown(draft);
    await copyToClipboard(markdown);
  }

  rejectDraft(draftId: string): void {
    // Just close the view and return to chat
    // Story 2.3 will handle the regeneration flow
  }

  private formatDraftAsMarkdown(draft: Draft): string {
    return `# ${draft.title}\n\n${draft.content}\n\nTags: ${draft.tags.join(', ')}`;
  }
}
```

### Project Structure Notes

**Following Feature-First Lite Pattern:**
- New feature folder: `src/components/features/draft/`
- Draft components co-located for easy discovery
- Shares `src/components/ui/` ShadCN primitives

**Alignment with Unified Project Structure:**
- All feature code under `src/components/features/`
- Services orchestrate logic, don't touch DB directly from UI
- State managed centrally in Zustand stores
- Utilities in `src/lib/utils/`

**No Conflicts Detected:**
- DraftViewSheet is new UI layer, no conflicts with existing code
- Extends existing DraftService with status update method
- Adds to ChatStore state, following established patterns

### Performance Requirements

**NFR-02 Compliance (App Load Time):**
- DraftViewSheet should animate smoothly (< 300ms transition)
- Markdown rendering should not block the main thread
- Use React.memo for DraftContent to prevent unnecessary re-renders

**Rendering Performance:**
- Large drafts (>2000 words) should render without jank
- Code blocks with syntax highlighting should load quickly
- Lazy load syntax highlighting library only when needed

**Memory Management:**
- Clear draft from currentDraft state after approval/rejection
- Don't keep multiple drafts in memory simultaneously
- IndexedDB is the source of truth, not component state

### Accessibility Requirements

**WCAG AA Compliance:**
- Color contrast: All text meets 4.5:1 ratio (Morning Mist palette enforces this)
- Touch targets: Minimum 44px for all buttons
- Keyboard navigation: Full keyboard support (Tab, Enter, Escape)
- Screen reader: ARIA labels on all interactive elements
- Focus management: Focus trap in sheet, proper focus return on close

**Focus Management:**
- When sheet opens, focus moves to close button
- Focus is trapped inside sheet while open
- When sheet closes, focus returns to trigger element
- Thumbs Up/Down buttons are keyboard accessible

**Semantic HTML:**
- Use `<article>` for draft content
- Use `<nav>` for action bar
- Use proper heading hierarchy (h2 for draft title)
- Use `aria-label` for icon-only buttons

### Security & Privacy Requirements

**NFR-03 & NFR-04 Compliance:**
- Clipboard copy is local-only (client-side API)
- No draft content transmitted to server on approval
- Draft status update is a local IndexedDB operation
- Copy to clipboard does not expose content to external services

**Content Safety:**
- Draft content is user-generated, no sanitization needed for local display
- Markdown rendering should not execute arbitrary scripts
- Use `react-markdown` which sanitizes by default

### References

**Epic Reference:**
- [Epic 2: "The Magic Mirror" - Ghostwriter & Draft Refinement](file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/epics.md#epic-2-the-magic-mirror---ghostwriter--draft-refinement)
- [Story 2.2: Draft View UI (The Slide-Up)](file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/epics.md#story-22-draft-view-ui-the-slide-up)

**Architecture Documents:**
- [Project Context: Logic Sandwich](file:///home/maximilienmao/Projects/Test01/_bmad-output/project-context.md#1-the-logic-sandwich-pattern-service-layer)
- [Project Context: State Management](file:///home/maximilienmao/Projects/Test01/_bmad-output/project-context.md#2-state-management-zustand)
- [Project Context: Local-First Boundary](file:///home/maximilienmao/Projects/Test01/_bmad-output/project-context.md#3-local-first-data-boundary)
- [Architecture: Service Boundaries](file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/architecture.md#architectural-boundaries)

**UX Design Specifications:**
- [UX: The "Magic Moment" Visualization](file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/ux-design-specification.md#key-design-challenges)
- [UX: Typography System](file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/ux-design-specification.md#typography-system)
- [UX: Component Strategy - DraftCard](file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/ux-design-specification.md#component-strategy)
- [UX: Experience Mechanics - The Magic](file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/ux-design-specification.md#2-experience-mechanics)
- [UX: Responsive Strategy](file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/ux-design-specification.md#responsive-strategy)

**Previous Stories:**
- [Story 2.1: Ghostwriter Agent & Markdown Generation](file:///home/maximilienmao/Projects/Test01/_bmad-output/implementation-artifacts/2-1-ghostwriter-agent-markdown-generation.md) - Ghostwriter generates drafts stored in IndexedDB

## Dev Agent Record

### Agent Model Used

Claude Opus 4.5 (model ID: 'claude-opus-4-5-20251101')

### Debug Log References

Session file: `/tmp/claude/-home-maximilienmao-Projects/Test01/80b59076-c368-433c-92e4-1937285218ee/scratchpad`

### Completion Notes List

**Story Analysis Completed:**
- Extracted story requirements from Epic 2, Story 2.2
- Analyzed previous Story 2.1 for established patterns (Ghostwriter, DraftRecord, DraftService)
- Reviewed architecture for compliance requirements (Logic Sandwich, State Management, Local-First)
- Reviewed UX specification for "Magic Moment" visualization, typography, responsive design
- Identified all files to create and modify

**Implementation Context Summary:**

**Story Purpose:**
This story implements the "Magic Moment" UI - the slide-up sheet that displays the Ghostwriter's draft in a polished, reading-focused interface. This is the emotional climax of the user journey where raw chat is transformed into a tangible artifact.

**Key Technical Decisions:**
1. **Component Structure:** DraftViewSheet (Sheet wrapper) -> DraftContent (Markdown) + DraftActions (Footer)
2. **Typography Split:** Merriweather (serif) for draft content, Inter (sans-serif) for UI - reinforces transformation value
3. **Responsive Design:** Full-screen on mobile, centered 600px card on desktop ("Centered App" pattern)
4. **Markdown Rendering:** react-markdown + @tailwindcss/typography for prose styling
5. **State Management:** Add draft view state to ChatStore (showDraftView, closeDraftView, approveDraft, rejectDraft)
6. **Service Extensions:** DraftService.updateDraftStatus() for marking drafts as completed

**Dependencies:**
- **New dependencies to add:**
  - `react-markdown` - Markdown parsing and rendering
  - `remark-gfm` - GitHub Flavored Markdown support
  - `rehype-highlight` - Syntax highlighting for code blocks
  - `@tailwindcss/typography` - Tailwind prose styling plugin
  - `canvas-confetti` (optional) - Success animation on copy
- **Reuses existing:** Zustand, Dexie, ShadCN Sheet component

**Integration Points:**
- Auto-opens when Ghostwriter completes (watch currentDraft state in ChatStore)
- Thumbs Up: Copies to clipboard, marks draft as 'completed' in IndexedDB
- Thumbs Down: Closes sheet, returns to chat (Story 2.3 handles regeneration flow)
- Draft content already stored in IndexedDB from Story 2.1

**Files to Create:**
- `src/components/features/draft/DraftViewSheet.tsx` - Main sheet component with ShadCN Sheet wrapper
- `src/components/features/draft/DraftContent.tsx` - Markdown renderer with Merriweather font
- `src/components/features/draft/DraftActions.tsx` - Thumbs Up/Down sticky footer
- `src/lib/utils/clipboard.ts` - Copy to clipboard utility function

**Files to Modify:**
- `src/lib/store/chat-store.ts` - Add draft view state and actions
- `src/services/draft-service.ts` - Add updateDraftStatus() method
- `src/services/chat-service.ts` - Add approveDraft() and rejectDraft() orchestration
- `tailwind.config.js` - Add Merriweather font and typography plugin

**Testing Strategy:**
- Unit tests for each component (DraftViewSheet, DraftContent, DraftActions)
- Integration tests for full flow (Ghostwriter -> DraftView -> Copy/Reject)
- Edge case tests (long drafts, code blocks, special characters)
- Accessibility tests (keyboard nav, screen reader, focus management)

### File List

**New Files to Create:**
- `src/components/features/draft/DraftViewSheet.tsx` - Main sheet component
- `src/components/features/draft/DraftContent.tsx` - Markdown renderer with Merriweather
- `src/components/features/draft/DraftActions.tsx` - Thumbs Up/Down footer
- `src/lib/utils/clipboard.ts` - Copy to clipboard utility
- `src/components/features/draft/draft-view-sheet.test.tsx` - Component tests
- `src/components/features/draft/draft-content.test.tsx` - Markdown rendering tests
- `src/components/features/draft/draft-actions.test.tsx` - Action button tests
- `src/integration/draft-view-flow.test.ts` - End-to-end flow tests
