# Handoff: ChicoryTV — Channel Builder (+ admin design system)

## Overview
The **Channel Builder** ("New Channel") is a ground-up replacement for ErsatzTV's four-editor
channel-creation chain (Channel → Schedule → Schedule Items → Playout). It is **one screen**: browse
your media library on the left, build the channel's lineup in the center, set friendly options +
a Channel Template on the right, and hit **Create Channel** — which (in the real app) builds all the
underlying entities atomically. It ships inside the **ChicoryTV admin**, a dark, dense, app-like
console and its React component library.

## About the design files
The files in this bundle are **design references created in HTML/React** — prototypes showing the
intended look and behavior, **not production code to ship directly**. The task is to **recreate these
designs in the target codebase's environment** using its established patterns. Per the redesign brief,
the intended target is a **React SPA over the ErsatzTV REST API** (replacing Blazor/MudBlazor); if that
app doesn't exist yet, scaffold it (Vite + React + TypeScript is a reasonable default) and implement
there. Nothing here depends on Blazor.

The prototypes are built on a real design system (React primitives + CSS-custom-property tokens +
three themes) that lives in the parent project — treat that as the component/token source of truth.

## Fidelity
**High-fidelity.** Final colors, typography, spacing, radii, motion, and interactions are all resolved.
Recreate pixel-for-pixel using the codebase's real component library (promoting these primitives into
it, or mapping to existing equivalents). All values below are exact.

---

## Screens / Views

### New Channel — Channel Builder (primary deliverable)
**Purpose:** create a working channel by adding library items to a lineup and setting a few friendly
options; technical config comes from a selectable Channel Template.

**Layout:** full-height column. A **builder toolbar** (fixed, 12px×20px padding, hairline bottom
border): accent icon tile + "New Channel" title + live summary (`N in lineup · total duration`) on the
left; **Cancel** (ghost) and **Create Channel** (primary, right) — Create is disabled until there's a
name + ≥1 lineup item, tooltip below. Below the toolbar, a 3-column grid, full height:
`grid-template-columns: minmax(0,1.2fr) minmax(0,1fr) 304px` (columns shrink, never overflow; the
304px rail is fixed). Each column scrolls independently.

**Column 1 — Library browser** (`overflow:hidden` section):
- Header: a **Segmented** source toggle **Library / Collections** (sliding highlight), a spacer, and a
  grid/compact **density** Segmented. Then a search input (`Search shows & movies…` /
  `Search collections…`). Then, in Library mode only, pill **filter chips** (All, Cartoons, Movies,
  TV Shows, Music Videos, Documentaries) — active chip uses accent-soft bg + accent text.
- Body (scrolls): **poster grid** `repeat(auto-fill, minmax(104px,1fr))`, gap 10 — each poster is a
  2:3 tinted, diagonally-striped placeholder (deterministic hue from title) with a bottom gradient
  scrim, title (12px semibold) + mono meta, and a type icon top-left (Tv/Film/Music/Radio, or
  Layers/Sparkles/FolderTree/Repeat for collection kinds). Compact mode is a 30×40 thumb + title +
  `type · meta` row. **Double-click or drag** to add; added cards show an accent ring + a **pop-in**
  check badge (top-right).

**Column 2 — Lineup** (the channel's content, `overflow:hidden` section, accepts drops):
- Header: "LINEUP" eyebrow, a **Sequential / Shuffled** status badge (icon, informational — the real
  control is the Shuffle toggle in the rail), a spacer, a **Clear** ghost button.
- Body (scrolls): ordered rows, gap 7. Each row: grip handle, order number (mono) when Sequential or a
  shuffle glyph when Shuffled, a 34×46 poster thumb, title (14px semibold) + `type · library`, mono
  duration, and a remove (×) button. **Drag-to-reorder** (HTML5 DnD; a `0 -2px 0 accent` top-border
  marks the drop target; dragged row drops to 0.35 opacity). Empty state = dashed dropzone. A trailing
  dashed "double-click or drag more titles to add" hint.

**Column 3 — Settings rail** (fixed 304px, `background: surface-card`, scrolls via an overlay
scrollbar — hidden native bar so no reflow, subtle fading thumb):
- **Channel name** input; **Number** input with an `AUTO` mono chip trailing.
- **Channel image**: a dashed dropzone (drop or click → file input; live `FileReader` preview). Below,
  two preview tiles — **Guide logo** (logo + name + "listing") and **On-screen bug** (logo bottom-right
  over a striped "video frame"). A "Set logo & bug separately" disclosure reveals two image buttons.
  Default: the one image drives both.
- **Shuffle** toggle (label + "Plays in a random order." / "Plays in sequence (chronological)."). Shows
  an "overrides template" tag when it differs from the selected template's default.
- **Always playing** toggle — the live-TV concept. Rendered in the **live color** (`--ctv-live`) with a
  tinted row + a "● LIVE" pill when on. Helper: "Like live TV — advances on schedule even when nobody is
  watching." Also shows the "overrides template" tag when diverging. *(Backend gap #68 — still needs
  confirmation.)*
- **Channel Template** picker: a button showing the current template (name + one-line desc + Custom
  badge). Opens a list of built-ins + custom, each with a radio, name, desc; a "Save current settings as
  template…" action. Selecting a template **applies its Shuffle + Always-playing defaults** and shows
  its config as chips (e.g. `1080p H.264`, `HLS Segmenter`, `Sequential`, `Always on`, `Pre + post filler`).
- **Advanced** disclosure: a **SlidersHorizontal** header + `N fields · <template>`. Two buttons —
  **Override settings** (one click → editable fields, active-orange in dual theme) and **View defaults**
  (read-only chips). Clicking the active one collapses. The field region animates via `max-height`.
  ~24 fields grouped Streaming / Filler / Playback / Behavior.

### Other admin screens (context; already in the design system)
- **Dashboard** — on-air cards, stat tiles, condensed health panel, activity feed, release notes.
- **Channels** — dense table, bulk-select, `(D)`/`(H)` markers, streaming-mode badges, inline actions.
- **Guide / EPG** — dark channels×time grid, "now" marker, live focal color, jump-to-now.
- **Schedule editor** — two-pane, drag-to-reorder items + tabbed inspector.
- **Playouts** — now/next monitor (live preview, up-next, timeline strip, upcoming list).
- **Libraries** — Local/Plex/Jellyfin/Emby source cards with scan status.
- App shell: 232px left sidebar (nav + health), 52px top bar (search, Connect menu). Primary "big"
  button is **top-right** on every screen.

---

## Interactions & behavior
- **Add to lineup:** double-click a library card OR drag it onto the lineup. Dedupes by id; added cards
  show a check.
- **Reorder lineup:** native drag-and-drop; drop-target top-border indicator; source dims to 0.35.
- **Shuffle vs Sequential:** rail toggle; lineup order display switches (numbers vs shuffle glyph).
- **Channel image:** drop/select → `FileReader.readAsDataURL` → live logo + bug preview; falls back to a
  generated initials chip (deterministic color from the name) when empty.
- **Template selection:** applies the template's shuffle + always-playing defaults; toggles then show
  "overrides template" if the user changes them.
- **Advanced:** one-click Override (editable) / View defaults (read-only); `max-height` collapse
  animation; on expand, the panel smooth-scrolls just enough to bring the Advanced header to the top.
- **Create Channel:** enabled only with a name + ≥1 item.
- **Themes:** `data-theme` on `<html>` = ``""`` (warm, default) / `cool` / `dual`. In **dual**, the focal
  warm hue drives primary actions, active nav, and selection via `--action-primary`.

### Motion (iOS/macOS-subtle; respect `prefers-reduced-motion`)
- Durations 90 / 140 / 220–340ms. Standard ease `cubic-bezier(0.2,0,0,1)`; entrance/settle
  `cubic-bezier(0.16,1,0.3,1)`.
- Segmented control: highlight **slides** (transform, ~300ms). Cards: **hover-lift** (`translateY(-2px)`
  + shadow). Buttons/rows: **press-scale** (`scale(0.96)`, ~150ms). Selection check: **pop-in**
  (`scale` + fade). Advanced: **max-height** accordion. Switches slide. Live status dot pings.

## State management
Per Channel Builder instance: `search`, `libraryFilter`, `source` (library|collections), `compact`,
`lineup[]` (ordered items), drag ids (`dragLib`, `dragRow`, `overRow`), `name`, `number`, `shuffle`,
`always`, `logoSrc` (dataURL|null), selected `template` id, `tplOpen`, and Advanced `mode`
(closed|view|override) + edited field values. Derived: `addedIds`, filtered pool, total duration,
`canCreate`. Real app: replace mock arrays with the **library browse/search API (#65)**, **Channel
Templates entity (#64)**, and the **composite create endpoint (#63)**.

## Design tokens (exact)
**Surfaces:** app `#0F1115` · sunken `#0B0D11` · card `#171A21` · raised `#1C2029` · selected `#222732`.
**Borders:** hairline `#262B36` · control `#333A48`.
**Text:** primary `#E6E9EF` · muted `#9AA4B2` · faint `#626B7A`.
**Accent:** warm chicory `#E08A3C` (default) · cool periwinkle `#5B7CFA` · soft = accent @14%.
**Semantic alias:** `--action-primary` = accent (warm/cool) or **`--ctv-live`** in dual.
**Status:** ok `#3FB984` · warn `#E0A83D` · error `#E5484D` · **live** `#E08A3C` (warm) / `#F0663F` (dual),
each with a ~14–16% soft tint.
**Radii:** chips 3px · buttons/inputs 5px · cards/rows/menus 7px · dialogs 10px · pills full.
**Type:** Geist (UI) + Geist Mono (numerics, tabular figures). Base 13px; scale 11·12·13·14·16·20·26·34;
weights 400/500/600. Eyebrows 11px/600 uppercase, 0.06em tracking.
**Spacing:** 4px grid. Fixed chrome: sidebar 232 · top bar 52 · rows 44 · controls 34 (sm 28).
**Elevation:** border + subtle shadow (sm `0 1px 2px/.40`, md `0 4px 12px/.45`, pop `0 8px 28px/.55`,
dialog `0 16px 40px/.55`); live elements get a small accent glow.

## Assets
- **Icons:** [Lucide](https://lucide.dev) (2px stroke), via CDN in the prototypes; use your icon lib's
  Lucide set. Glyphs used: Plus, Check, X, Search, Library, FolderTree, LayoutGrid, Rows3, GripVertical,
  Shuffle, ListOrdered, Radio, Film, Tv, Music, Layers, Sparkles, Repeat, Image, SlidersHorizontal,
  SquarePen, Eye, CircleCheck, Circle, Save, ChevronDown/Up/Right.
- **Brand mark:** `assets/chicory-mark.svg` (periwinkle chicory flower) — do not redraw; reference it.
- **Posters/artwork:** placeholders only (striped, tinted). Real artwork comes from the library API (#65).
- No other raster assets.

## Files
In the parent project (download it alongside this folder):
- `templates/chicorytv-admin/ChannelBuilder.jsx` — **the Channel Builder** (also copied into this folder).
- `templates/chicorytv-admin/{Shell,Dashboard,Channels,Epg,ScheduleEditor,Playouts,ScheduleLibrary}.jsx`,
  `data.js`, `icons.jsx`, `app.html`, `ds-base.js` — the admin shell + other screens.
- `components/` — the React component library (`.jsx` + `.d.ts` per component; namespace
  `window.ChicoryTVDesignSystem_eb3b61`).
- `tokens/` + `styles.css` — design tokens and the three themes (`tokens/themes.css`).
- `readme.md` — full design-system documentation.
- `docs/superpowers/specs/2026-07-01-chicorytv-ui-redesign-design.md` — the redesign brief; **§6** is the
  Channel Builder spec, **§8** records exactly what was built + backend gaps (#62/#63–#68).

To view the whole admin: open `templates/chicorytv-admin/app.html` and click **New Channel**.
