# S02: Topics, Creator Stats & Card Polish

**Goal:** Topics page loads collapsed with smooth expand/collapse animation. Creator stats show colored topic pills. Cards limit tags to 4 with +N overflow. Empty subtopics show "Coming soon" badge.
**Demo:** After this: Topics page loads collapsed, expands with animation. Creator stats show colored topic pills. Cards limit tags to 4 with +N more. Empty subtopics show Coming soon.

## Tasks
- [x] **T01: Topics page loads with all categories collapsed; expand/collapse uses smooth CSS grid-template-rows animation instead of conditional render** — Change TopicsBrowse.tsx to start with all categories collapsed and add smooth CSS grid-template-rows animation for expand/collapse transitions.

## Steps

1. In `frontend/src/pages/TopicsBrowse.tsx`, change the `useEffect` that calls `setExpanded(new Set(data.map((c) => c.name)))` to `setExpanded(new Set())` so all categories start collapsed.

2. Refactor the subtopics rendering from conditional `{isExpanded && (<div className="topic-subtopics">...)}` to always-rendered with a CSS grid animation wrapper. Use the `display: grid; grid-template-rows: 0fr / 1fr` technique:
   - Wrap the subtopics in a `<div className="topic-subtopics-wrapper" data-expanded={isExpanded}>` that has `display: grid; grid-template-rows: 0fr; transition: grid-template-rows 300ms ease;`
   - When `data-expanded="true"`, set `grid-template-rows: 1fr`
   - The inner `<div className="topic-subtopics">` gets `overflow: hidden; min-height: 0;`
   - Always render both wrapper and inner div (remove the conditional render)

3. Add the CSS in `frontend/src/App.css` near the existing `.topic-subtopics` rule (~line 2318):
   - `.topic-subtopics-wrapper { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 300ms ease; }`
   - `.topic-subtopics-wrapper[data-expanded="true"] { grid-template-rows: 1fr; }`
   - `.topic-subtopics { overflow: hidden; min-height: 0; }` (modify existing rule)

4. Verify: `cd frontend && npx tsc --noEmit && npm run build`
  - Estimate: 30m
  - Files: frontend/src/pages/TopicsBrowse.tsx, frontend/src/App.css
  - Verify: cd frontend && npx tsc --noEmit && npm run build
- [x] **T02: Replaced run-on dot-separated topic stats on CreatorDetail with colored pill badges using catSlug-based badge classes** — Replace the run-on text stats on CreatorDetail page with colored pill badges using existing badge CSS classes.

## Steps

1. In `frontend/src/pages/CreatorDetail.tsx`, add import: `import { catSlug } from "../utils/catSlug";`

2. Find the stats section (~line 108-126) that renders `<span className="creator-detail__topic-stat">` with dot separators. Replace the entire `.map(([cat, count], i) => ...)` block with pills:
   ```tsx
   .map(([cat, count]) => (
     <span key={cat} className={`badge badge--cat-${catSlug(cat)}`}>
       {cat}: {count}
     </span>
   ))
   ```
   Remove the dot separator spans (`queue-card__separator`) between them.

3. Wrap the topic stat pills in a flex container: `<span className="creator-detail__topic-pills">` and add CSS in `frontend/src/App.css`:
   ```css
   .creator-detail__topic-pills {
     display: flex;
     flex-wrap: wrap;
     gap: 0.375rem;
     align-items: center;
   }
   ```

4. Remove the old `.creator-detail__topic-stat` class from App.css if it exists (or leave it — no harm).

5. Verify: `cd frontend && npx tsc --noEmit && npm run build`
  - Estimate: 20m
  - Files: frontend/src/pages/CreatorDetail.tsx, frontend/src/App.css
  - Verify: cd frontend && npx tsc --noEmit && npm run build
- [x] **T03: Created shared TagList component with max-4 overflow, applied across all 5 tag sites, and added empty subtopic Coming soon badge** — Create a shared TagList component for tag overflow (R027), apply it across all 5 tag-rendering sites, and add empty subtopic handling in TopicsBrowse (R028).

## Steps

1. Create `frontend/src/components/TagList.tsx`:
   ```tsx
   interface TagListProps {
     tags: string[];
     max?: number;
   }
   export default function TagList({ tags, max = 4 }: TagListProps) {
     const visible = tags.slice(0, max);
     const overflow = tags.length - max;
     return (
       <>
         {visible.map(tag => <span key={tag} className="pill">{tag}</span>)}
         {overflow > 0 && <span className="pill pill--overflow">+{overflow} more</span>}
       </>
     );
   }
   ```

2. Add `.pill--overflow` CSS in `frontend/src/App.css`:
   ```css
   .pill--overflow {
     background: var(--color-surface-2);
     color: var(--color-text-secondary);
     font-style: italic;
   }
   ```

3. Replace tag rendering in all 5 sites with `<TagList tags={...} />`:
   - `frontend/src/pages/Home.tsx` line ~201 (featured technique tags) — replace `featured.topic_tags.map(...)` with `<TagList tags={featured.topic_tags} />`
   - `frontend/src/pages/Home.tsx` line ~247 (recent technique card tags) — replace `t.topic_tags.map(...)` with `<TagList tags={t.topic_tags} />`
   - `frontend/src/pages/SearchResults.tsx` line ~145 — replace `item.topic_tags.map(...)` with `<TagList tags={item.topic_tags} />`
   - `frontend/src/pages/SubTopicPage.tsx` line ~149 — replace `t.topic_tags.map(...)` with `<TagList tags={t.topic_tags} />`
   - `frontend/src/pages/CreatorDetail.tsx` line ~157 — replace `t.topic_tags.map(...)` with `<TagList tags={t.topic_tags} />`
   Each site: add `import TagList from "../components/TagList";` and replace the `.map(tag => <span className="pill">{tag}</span>)` with `<TagList tags={...} />`.

4. In `frontend/src/pages/TopicsBrowse.tsx`, update the subtopic rendering inside the `.topic-subtopics` div. For subtopics with `st.technique_count === 0`:
   - Render as a `<span>` (not `<Link>`) with class `topic-subtopic topic-subtopic--empty` instead of a clickable link
   - Add a small "Coming soon" badge: `<span className="pill pill--coming-soon">Coming soon</span>`
   - Keep the subtopic name visible so users know the topic exists
   Use a conditional: `st.technique_count === 0 ? <span className="topic-subtopic topic-subtopic--empty">...</span> : <Link ...>...</Link>`

5. Add CSS for empty subtopic in `frontend/src/App.css`:
   ```css
   .topic-subtopic--empty {
     opacity: 0.5;
     cursor: default;
   }
   .topic-subtopic--empty:hover {
     background: transparent;
   }
   .pill--coming-soon {
     font-size: 0.65rem;
     background: var(--color-surface-2);
     color: var(--color-text-secondary);
     font-style: italic;
   }
   ```

6. Verify: `cd frontend && npx tsc --noEmit && npm run build`
  - Estimate: 40m
  - Files: frontend/src/components/TagList.tsx, frontend/src/pages/Home.tsx, frontend/src/pages/SearchResults.tsx, frontend/src/pages/SubTopicPage.tsx, frontend/src/pages/CreatorDetail.tsx, frontend/src/pages/TopicsBrowse.tsx, frontend/src/App.css
  - Verify: cd frontend && npx tsc --noEmit && npm run build
