# Publish flow, Phase 1 analysis (no building yet)

Hand-off from the campaign-details work. This walks the current publish flow stage by stage, says
where it diverges from the campaign-details system, what we reuse as-is, and proposes a section
architecture for the manual screen-selection screen (the one we have been calling "hyperlocal").
Stop and review before I build.

Status: analysis only. Nothing in `platform-pages/publish/` is built yet except this file.

---

## 0. What I read and looked at

Reference system (the gold standard everything aligns to):
`platform-pages/campaign-details/campaign-details.html` + `DOCUMENTATION.md` + `IMPLEMENTATION-GUIDE.md`,
`design-system/library.html` (PLAT-01..10), `design-system/tokens.css`, `blindspot.css`,
`design-system/VOICE.md`, plus the standing rules in auto-memory.

Two batches of screenshots:

- **Live (current production), `portal.seeblindspot.com`.** The sidebar product app: a megaphone
  campaign-type step (HyperLocal / AI Planner), a "Set Filters" region + screen-type gate, the
  screen-select grid over a Miami map, an hourly schedule with PPH and schedule groups, a media step
  with contextual rules, and a review/bill step.
- **Staging (work in progress), `portal-vx.seeblindspot.com`.** A six-step wizard (New Campaign,
  Getting Started, Location Search, Schedule, Upload Media, Review Campaign): the "Pick your boards"
  list + Manhattan map, a schedule grid + an advanced PPH table, media upload, and a review +
  Bill Summary.

Plus three screenshots that change the picture: a **rebuilt Publish dashboard already exists** in the
campaign-details house style (Anton "GOOD MORNING, BOGDAN" / "REACH REAL PEOPLE ON REAL STREETS",
mono kickers, `$0.23 / play`, "No sales calls. Pick a city and go live this week."). It lives on disk
at `WORK IN PROGRESS/dashboard/dashboard.html` with a `_reuse-kit.css` + `_reuse-kit.js` (the
campaign-details components already extracted for reuse) and a `mapScrim` modal stub. That dashboard is
the front door to this flow, so the screen-select page has to look like its child, not just like
campaign-details.

---

## 1. The current flow, stage by stage

Both generations are the same manual pick-screens-yourself flow, one newer than the other. The
screen-selection step is the focus; the rest is context for the boundaries.

1. **Start / campaign type.** Pick HyperLocal (manual) vs Blinky/AI Planner, name the campaign, set
   category, industry and start date. (Blinky path is out of scope per your note.)
2. **Getting started / scope.** Start date, end date, planned budget. Live splits scope into a
   separate "Set Filters" gate: region (Around me / United States / Europe / All the world) and screen
   type (Indoor / Outdoor / Mobile).
3. **Location search (the screen-selection step, our target).**
   - Heading "Pick your boards" with a live result count ("There are 117,587 billboards for this
     search" on staging, "There are 59 billboards for this search" on live after filtering).
   - Search by location + search by name; a venue-type category chip row (Transit, Retail, Outdoor,
     Health & Beauty, Point of Care, Education, Office Buildings, Leisure, Government, Financial,
     Residential); "Show all filters".
   - View toggle: **List / Split / Map** (Split = results column beside a live map; default).
   - Result cards: photo (or a "Blindspot, no photo available" placeholder), a media-type + resolution
     badge (video / static, e.g. 3840x4320), name, address, network/operator, venue + size chips,
     **average price per play in USD**, a save/bookmark, a preview (eye) icon, an availability /
     play-opportunity read, and a **Book** action.
   - Map: red price/cluster pins over the city; pins map to cards.
   - A **Basket** count in the top right.
4. **Schedule.** A stat bar (locations, screens, est run time, total plays, est watch time,
   impressions, budget all-in), a Global Schedule vs per-location model, an hourly day x hour grid,
   PPH (plays per hour) controls, "Mass update PPH", and "Schedule groups". The selected hours render
   as a filled block.
5. **Media.** Upload now or later; formats grouped by accepted duration + resolution; a media library
   picker; per-creative **contextual rules** (times of day, weather, day of week, API trigger).
6. **Review.** Campaign overview stat cards, a per-location preview table, a **Bill Summary**
   (subtotal / tax / total, USD), a discount code, downloads (media plan CSV/PDF), and **Publish**.

---

## 2. Where it diverges from the campaign-details system

Layout and chrome
- **Two different shells.** Staging uses a top wizard stepper with gray numbered circles; live uses the
  product sidebar. Neither matches campaign-details' nav. We already resolved this in the rebuilt
  dashboard: a `dash-side` sidebar + `dash-main` column + `cd-foot`. The select screen should live in
  that shell, not invent a third.
- **Generic UI kit.** Current screens read as a default Material-ish kit (system fonts, gray strokes,
  rounded inputs). The system is Anton (display), Archivo (body), IBM Plex Mono (labels/kickers),
  Instrument Serif (one accent clause), on warm paper with hairline tokens.

House-rule breaks (non-negotiable to fix)
- **Green is used.** Availability pills ("Available", "Availability varies") are green/teal, the
  schedule selection block is green, and the Continue button carries a green check. House rule is **no
  green anywhere**; a positive reads in `--red-deep`, statuses run through the state library tones
  (coral / ink / amber / red), never green.
- **Vocabulary.** "boards", "billboards", "inventory" everywhere. VOICE says **screens**. "Book
  Location" on a card conflates adding to a plan with spending money.
- **Metric.** Cards already say "avg price per play" in USD, which is close to our **average cost per
  play**. Keep the metric, relabel to "avg cost per play", and confirm there is no CPM or cost-per-
  install anywhere in the flow.

Voice
- Headings are fine but generic ("Pick your boards", "Set your campaign schedule"). The dashboard
  already set the tone for this area ("Reach real people on real streets", "Pick a city and go live
  this week"); the select screen should match that register: literal labels, buttons that say exactly
  what happens, sentence case in prose, mono uppercase for labels.
- The "Earn Cash & Credits by recommending Blindspot" promo strip is marketing, not product-quiet UI.
  Decision below on whether it belongs here at all.

Components
- The card, the filter chips, the view toggle, the map, the drawer, the stat bar, the empty states and
  the confirm dialogs are all bespoke today. Every one of them has a direct equivalent already built
  in the reuse-kit or campaign-details (see §3), so this is mostly composition, not net-new components.

---

## 3. What we reuse as-is (almost all of it)

From the already-built publish dashboard (`WORK IN PROGRESS/dashboard/`)
- The **shell**: `dash-side` sidebar (logo mark, `dash-nav` links, profile/help/logout foot),
  `dash-main` + `.wrap` container, `cd-foot` footer.
- The **header** pattern: `dash-head` with an Anton H1 + `serif-acc` accent, a sub line + "Review
  build, sample data" chip, a `dash-quick` action row, and the `hdrdrop` **gear menu** (Theme +
  Density segmented controls), all wired.
- The **section head** pattern `secHead(icon, eyebrow, h2, more)` and `cd-sec` sections.
- The **map card / `mapScrim`** stub (an "Open the map" modal already scaffolded).
- `_reuse-kit.css` + `_reuse-kit.js`: `pb / pb-primary / pb-onink / pb-sm` buttons, `seg` segmented
  control, `dwstats` KPI bar, the `dwai` Blinky card, the media carousel (the gutters + glow rule),
  `hdrdrop`, `toasts`, footer, `sk` skeleton + `cd-emptystate` + `cklab`, `cdot` condition dots,
  trigger pills, the live-pulse keyframes, the compact-density rules, the **complete `body.theme-dark`
  override block** (the dark-mode landmines already solved), the **reduced-motion** blocks, and the
  `.rv` reveal helper.

From campaign-details directly
- The **`.drawer`** chrome (slide-from-right, focus-trapped, Esc, returns focus) for the screen-detail
  drawer.
- **`.scrim` + `.modal`** for the booking confirm.
- **`BS_STATE` + `stateChip`** for availability and screen status (online / offline / maintenance and
  the campaign/creative tones), so the green pills become on-system chips.
- The **Leaflet map harness** + offline static-basemap fallback, and the **`onFocus / focusHub`**
  cross-highlight pub/sub (list row to map pin to card).
- **`.cd-table`** + the explorable-table toolbar pattern (sort / search / pivot / CSV) for the List
  view of screens.
- The **deep-link pattern** (`cdSyncURL` / `cdReadURL`, `history.replaceState`, URL + localStorage).
- The **lifecycle / empty-state instincts** (PLAT-10): a friendly "nothing picked yet" rather than a
  blank panel.
- Tokens + `blindspot.css` carried locally (self-contained per the platform-page convention).

Net: the screen-select page is mostly **composition over the dashboard shell + reuse-kit**, with two or
three genuinely new pieces (the screen card, the filter system, the plan/basket summary).

---

## 4. Naming

"Hyperlocal" is the internal name for the manual, pick-them-yourself path (the opposite of the Blinky
path). It is not a great user-facing word (VOICE bans "journey"-style jargon and prefers plain). My
recommendation:

- **User-facing screen title:** "Pick your screens" (keep the dashboard's voice; drop "boards").
- **The path/mode name** (vs Blinky): "Pick screens yourself" vs "Let Blinky plan".
- **Internal / folder / route:** keep `publish/select` (or `publish/screens`), and you can keep
  "hyperlocal" as the internal mode id in code if the backend already uses it.

Open for your call; everything below uses "Pick your screens" / the select screen.

---

## 5. Proposed section architecture, the screen-select page

Built self-contained under `platform-pages/publish/` (its own `blindspot.css` +
`design-system/tokens.css` + `_reuse-kit.*` + assets), opening standalone, light + dark, deep-linkable.

**A. Shell.** `dash-side` sidebar + `dash-main` + `.wrap` + `cd-foot`, identical to the dashboard, with
the Publish/Campaigns item active in the sidebar.

**B. Page header (`dash-head` variant).** Breadcrumb in the sidebar/crumb (Campaigns / draft name /
Pick your screens). Anton H1 "Pick your screens" with a `serif-acc` accent clause; a sub line that
states the live scope (city + dates carried from the start step) and a result count
("1,284 screens match"). The `hdrdrop` gear (Theme + Density) on the right. A slim, on-brand
**progress rail** (mono, coral active, in the `cd-jump` style) instead of the gray numbered stepper, so
the flow position is still legible. (Stepper vs no-stepper is an open question, §7.)

**C. Scope + filter bar (sticky under the header).**
- Search by location + search by name (two inputs, mono labels).
- The region + screen-type scope (Around me / US / Europe / World; Indoor / Outdoor / Mobile),
  folded in here as editable chips rather than a separate gate (recommendation, §7).
- A **venue-type chip row** (Transit, Retail, Outdoor, ...), horizontally scrollable with edge gutters
  and the carousel glow rule, coral when active.
- **All filters** opens a filter **panel** (reuse the drawer chrome): availability only, has photo,
  price range (USD), format (digital / static), size/resolution, orientation, operator/network,
  audience, daypart fit.
- An **active-filters chip line** with individual removes + "Clear all".

**D. Results region with a `seg` view toggle: List / Split / Map.**
- **Split (default):** results column on the left, a sticky Leaflet map on the right.
- **List:** the `cd-table` explorable table (sortable columns: screen, venue, format, size, avg cost
  per play, est plays, availability; search; CSV export), for buyers who want to scan many at once.
- **Map:** full-width map; clicking a pin opens the card/drawer.
- **The screen card (`scard`, new):** photo (or a branded "no photo yet" placeholder), a media-type +
  resolution chip, a save/bookmark, a preview (eye), name, address, operator/network, venue + size
  chips, **avg cost per play (USD)**, an **availability read via `stateChip`** (no green), and a
  primary **Add to plan** action that toggles to "Added, remove". Hover lifts with the soft red glow
  (carousel rule) and cross-highlights its map pin (`focusHub`).
- **Map behavior:** price pins in ink/coral (never green), clusters neutral, selected screens pinned
  coral; hovering a card lights its pin and vice versa; offline static-basemap fallback like
  campaign-details.

**E. Screen detail drawer (`.drawer`, deep-linked `?screen=`).** Photo/gallery, full specs (resolution,
physical size, media types + accepted durations, orientation), operator + network, address + a mini
locator map, **cost per play** + estimated plays/impressions/watch-time at this screen for the campaign
window, an availability + lead-time read, a small schedule-preview, and **Add to plan / Remove**.
Focus-trapped, Esc, returns focus to the card.

**F. Plan summary (the basket, persistent).** A right-side rail in Split (collapsible), or a sticky
bottom summary bar in List/Map: count of screens + distinct locations, running totals (est plays,
impressions, est watch time, **budget all-in in USD / avg cost per play**), a per-screen mini-list with
remove, and a primary **Continue to schedule**. Empty state uses the VOICE line: "No screens picked.
Open the map, or let Blinky draft a plan." A small **Blinky nudge** (`dwai`) offering to draft a plan
from the current filters.

**G. States.** `sk` skeletons on load; a no-results empty state ("No screens match that filter. Clear
it and start over."); an error + Retry; and the friendly nothing-picked-yet state in the plan rail
(PLAT-10 instinct). All in both themes.

**H. Cross-cutting.**
- **Deep-link** the search, filters, view mode, open screen drawer, and the plan, in the URL +
  localStorage (the `cdSyncURL/cdReadURL` pattern), so a plan is shareable and restorable.
- **Light + dark** via the reuse-kit's complete `theme-dark` block; watch the documented landmines
  (any `var(--ink)` filled background; any hover that flips to `#fff`/`var(--ink)`), especially on the
  card, chips, map controls and the plan rail.
- **Motion** behind `prefers-reduced-motion`: section/card reveal, pin drop on the map, a card "added
  to plan" pulse, drawer slide.
- **Confirm-before-side-effect.** Adding/removing a screen to the plan is reversible and needs no
  confirm. The money/commit boundary (the eventual **Publish**, on a later screen) is where the confirm
  + toast + change-log lives; on this screen the only such control is "Continue to schedule", which
  spends nothing. We keep the booking/publish confirm honest by not labeling "Add to plan" as "Book".

---

## 6. Scope boundary for the Phase 2 build

Build the **screen-select page** end to end (shell, header, scope + filter bar, the three views, the
card, the detail drawer, the plan summary, all states, deep-linking, light + dark, motion, QA), against
**deterministic simulated data** like campaign-details. Schedule, Media and Review are separate screens
for later tasks; this page hands off to them with a "Continue to schedule" button and a restorable plan.
The Blinky/AI path stays out of scope (your note), but we leave the `dwai` nudge as the seam to it.

---

## 7. Decisions

Locked with Bogdan, 2026-06-19:

1. **Name.** "Pick your screens" (screen title); "Pick screens yourself" vs "Let Blinky plan" (mode);
   route stays `publish/select`, internal id can stay "hyperlocal". Drop "boards/billboards".
2. **Stepper.** Slim coral progress rail (cd-jump style) + sidebar + breadcrumb. No gray numbered
   wizard.
3. **Scope gate.** Fold region + screen-type into the filter bar on the select screen (editable
   chips). No separate "Set filters" step.
4. **Plan/basket.** Collapsible right rail in Split; sticky bottom summary bar in List/Map. Running
   totals in USD always visible.

Still my defaults unless you say otherwise (called out for your veto, not blocking the build):

5. **Default view.** Split (list + map), with List and Map as alternates.
6. **Promo strip.** Drop the "Earn Cash & Credits" banner from the product flow (it is marketing, not
   product-quiet UI).
7. **Map.** Leaflet with offline static fallback, like campaign-details. Sample-data city: New York
   (consistent with the rest of the project), unless you prefer Miami.
8. **Card action wording.** "Add to plan" (reversible), not "Book". The spend confirm lives at Publish.

Next: on your go-ahead I build the page under `platform-pages/publish/`, QA it headless (jsdom 0 errors
+ Playwright light/dark/responsive screenshots), and package + rebuild the master archive the same way
as campaign-details.

*Blindspot platform, publish flow, Phase 1 analysis, 2026-06-19. Operated by TPS Engage, LLC.*
