# App shell & Home scope — work-package 2: Responsive shell & mobile audit

> **Kickoff plan. Written by Fable (the senior seat), 2026-07-17. Amended
> same day after the operator ruled four of the five forks** (recorded in
> `docs/DECISIONS.md` § "2026-07-17 — appshell-scope-2 forks ruled").
> **Status: blocked on the scope-1 gate** — the WIP rule (one package in
> flight per scope) means execution cannot start until
> `appshell-scope-1-kickoff.md` passes its gate. The one open decision is the
> collapse pattern, which the round-7 mock reaction decides (per house style).
> Sources: scope board App-shell backlog (operator, 2026-07-17, from live
> review); evidence `docs/evidence/2026-07-17-posts-mobile-emulated.png`;
> `docs/DECISIONS.md` (the fork rulings);
> `plans/rethink/web-adopt-audit-2026-07-17.md` (CR-1);
> harness frontend playbooks **B3 responsive-layout** + **B2 design-tokens**
> (consumed via the gateway, operator ruling 2026-07-17 — never vendored here).
> Gate judges the package against THIS document.
> Intake was triaged: one item (Facebook comments moderation, 2026-07-17) — it
> is Channels-scope, not App-shell; it stays in Intake for the next Channels
> kickoff to absorb. Nothing in Intake belongs to this package.

The package's spine: **the app stops being desktop-only, and the shell lands
on the real design system while it happens.** At phone widths the sidebar
currently renders as a full-screen navy block (no collapse, no burger) and
content panes overflow horizontally. This package gives the shell a collapse
pattern **built on the `--cm-*` light-shell visual system** (operator-ruled
fold-in — one package, one deploy; converges with adopt-audit **CR-1: three
competing styling systems**), mocked first per house style, then makes **all
eight nav screens** work at phone widths — no audit-only tier; Inbox's
three-pane and the Flow editor are in scope with their own stage. The bar
comes from the harness B3 playbook: **reflow at 320px CSS width with no
horizontal document scroll** (WCAG 1.4.10), verified at 320/375/768/1440.

## Read before touching anything

`CLAUDE.md` · `HANDOFF.md` · `plans/rethink/scope-board.md` ·
`plans/rethink-execution.md` (you are the executor seat) ·
`design/tokens.md` (law) · the harness frontend playbooks via the
`harness-gateway` skill — **B3 responsive-layout** (the method + bar) and
**B2 design-tokens** (breakpoints are tokens) — before any UI work.

## Ground truth (verified at kickoff, 2026-07-17 — trust this over older docs)

- **The root cause is named, and it is not "no responsive CSS".**
  `apps/web/src/styles/global.css` already carries a
  `@media (max-width: 900px)` reflow block (§Responsive, lines ~656–685) that
  turns the sidebar into a wrapping top bar — **and it is broken against the
  current DOM**: it sets `flex-direction: row; flex-wrap: wrap` on
  `.so-sidebar-nav` and sizes `.so-sidebar-nav li`, but `AppShell.tsx:87`
  renders a `<ul>` (`styles.navGroupList`) between them. The single `ul` is
  the only flex child, so the entries stay a vertical full-width stack — the
  full-screen navy block in the evidence screenshot.
- **Why it drifted:** the shell's layout lives in inline `styles` objects
  (`apps/web/src/components/AppShell.tsx:148–326` — sidebar `width: 220`,
  `flexShrink: 0`, sticky, `minHeight: 100vh`). Inline styles cannot carry
  media queries, so every responsive rule is a CSS `!important` patch fighting
  the inline layer from a different file. Structure and its overrides live
  apart; they diverged. The fix is structural: the shell's layout moves into a
  stylesheet the media/container queries can own.
- **No collapse state exists.** `AppShell.tsx` has no burger, no drawer, no
  open/closed state of any kind. The ≤900px intent was a wrapping top bar;
  even working, eight entries wrap to 2–3 rows before content.
- The evidence screenshot (`docs/evidence/2026-07-17-posts-mobile-emulated.png`,
  390px emulated, Agent → Posts): sidebar full-screen above the header; below
  it the Agent tab strip clips mid-word ("Beh…") and the hero copy runs past
  the right edge — the *document* scrolls horizontally.
- **Viewport meta is correct** (`apps/web/index.html:5`,
  `width=device-width, initial-scale=1`) — this is a genuine layout problem,
  not a missing meta.
- **Two design systems coexist — and this package ends that for the shell.**
  The shell is still legacy navy/gold (`global.css` bare `--navy`/`--gold`
  names); the new `--cm-*` token sheet (`apps/web/src/styles/tokens.css`,
  from approved `design/tokens.md`) owns the rebuilt screens.
  `AppShell.tsx:199–200` records that the light-theme shell rebuild lands
  "when the shell retires onto the new tokens" — **operator-ruled 2026-07-17:
  that retirement IS this package.** The collapse work lands ON the light
  shell, never on the navy it would immediately obsolete. This converges
  with the adopt-audit's Critical finding CR-1
  (`plans/rethink/web-adopt-audit-2026-07-17.md`): three competing styling
  systems live simultaneously — the shell moving to `--cm-*` retires the
  biggest legacy consumer.
- **No breakpoint tokens exist.** `tokens.css` has `--cm-sidebar-w: 216px`
  and `--cm-content-max: 1360px` but no breakpoint values; the 900px/540px
  numbers in `global.css` are magic. B2's rule: breakpoint values are tokens
  — **the `design/tokens.md` amendment is APPROVED (operator, 2026-07-17);
  adding them is a Stage 1 deliverable, not a pending sign-off.**
- **Interacting rule:** `global.css:645–648` scales the root font-size UP at
  ≥1280px (17→23px). Any `rem`-based breakpoint must account for this; the
  ruled 768px is px-based, which avoids the interaction. Do not "fix" the
  scaling.
- **Good patterns already in the codebase — replicate, don't reinvent:**
  `agent-nav.css` (the Agent subnav scrolls horizontally instead of wrapping,
  by design); `.so-table-scroll` (`global.css:352`) wraps wide tables in their
  own `overflow-x: auto` container; `body.so-scroll-lock` exists for the
  modal and can serve the drawer.
- **The eight nav screens** (`AppShell.tsx:24–33`): Home `/`, Inbox `/inbox`,
  Agent `/agent`, Campaigns `/campaigns`, Contacts `/contacts`, Automation
  `/automation`, Analytics `/analytics`, Settings `/settings`.
- **Known hard cases — IN SCOPE (operator-ruled: all screens must-fix, no
  audit-only tier):** Inbox is a pinned three-pane row with a fixed
  `width: 330` conversation pane (`apps/web/src/routes/Inbox.tsx:364`;
  pinned logic `AppShell.tsx:50–55`) — structurally impossible at 390px; a
  real mobile Inbox is pane *navigation*, not pane shrinking. The Flow
  editor (`/flows/*/edit`, immersive canvas) has desktop-shaped chrome.
  Both get their own stage (Stage 3) so the heavier work is planned
  honestly, not squeezed into the audit sweep.
- `prefers-reduced-motion` is already honored globally (`global.css:442`) —
  the drawer's animation inherits this for free; don't duplicate it.
- Shell tests exist: `apps/web/src/components/AppShell.test.tsx` — extend, do
  not fork a second suite.

## Harness basis (B3 playbook — the defaults; deviate only with a reason)

- **Intrinsic first**: widths in `%`/`fr`/`min()`, never fixed px; the
  overflow-proof grid is `repeat(auto-fit, minmax(min(100%, 16rem), 1fr))`;
  fluid space via `clamp()` (e.g. page padding
  `padding-inline: clamp(1rem, 4vw, 2rem)`).
- **A viewport media query is sanctioned exactly here**: the shell collapse is
  a genuine top-level template switch — B3's one legitimate MQ use. Per-screen
  content adapts intrinsically or by container query, not by screen-specific
  viewport breakpoints.
- **`dvh`, not `vh`**, for anything full-height on mobile (the drawer): `vh`
  mis-measures under mobile browser UI bars.
- **Logical properties** (`padding-inline`, `margin-block`, `inset`) for new
  shell CSS.
- **The bar**: reflows to **320px** CSS width, no horizontal document scroll,
  no content loss (WCAG 1.4.10); text at 200% doesn't break (WCAG 1.4.4);
  `prefers-reduced-motion` respected.
- **Verification tooling**: `mcp__chrome-devtools__resize_page` to explicit
  widths (320/375/768/1440 — it defaults to desktop, always set width) +
  `take_screenshot`; overflow oracle via `evaluate_script`:
  `document.documentElement.scrollWidth <= document.documentElement.clientWidth`.
- **B2 seam**: the breakpoint *values* land as tokens in `tokens.css` and
  `design/tokens.md` (which is law; amendment operator-APPROVED 2026-07-17),
  consumed by the shell CSS. The value is **768px** — the industry-standard
  tablet boundary (Tailwind/Bootstrap `md`); the operator explicitly rejected
  a project-invented number.

## Standing rules

All of `CLAUDE.md` + rethink standing rules. **Zero LLM spend** in this
package. Tokens are law. TDD for all logic; static mocks are reaction
material, not code. Web changes are not live until the operator publishes
(`pnpm --filter @channels/web build` first; `scripts/publish-web.sh` only
rsyncs). Commit+push per verified step.

### Sequencing constraints

- **Blocked on the scope-1 gate** (WIP rule). The forks are ruled
  (2026-07-17), so Stage 0 (mocks, zero code) may be prepared anytime;
  Stages 1–3 start only after scope-1's verdict is recorded.
- This package inherits scope-1's ownership of `AppShell.tsx` / `App.tsx` /
  the shell CSS. The Channels scope's package 2 (filters everywhere) waits on
  the nav per the board — if it starts meanwhile, the same collision-avoidance
  note applies: nav/route changes are filed in this package's court.
- No migrations, no backend, no email, no gates flipped: this package is
  web-only.

---

## Stage 0 — Mock round FIRST (round-7, zero build)

1. Static tiles in `design/tiles/round-7/` (self-contained HTML or PNG, no
   app code). **Every tile renders the `--cm-*` light shell — never the navy
   it replaces** (operator-ruled fold-in): the reaction must cover the
   light-shell visuals AND the collapse pattern in one round. Tiles at
   **390px** and **768px**, plus one **desktop (1440px) light-shell tile** so
   the reskin's desktop face gets reacted to as well:
   - **Tile A — overlay drawer** (recommended): compact top bar (brand mark +
     burger); nav slides in as a `100dvh` overlay drawer; body scroll-locked
     while open; closes on navigate/Esc/backdrop.
   - **Tile B — compact wrapping top bar**: the original ≤900px intent done
     right — brand row + nav entries as wrapped pill rows, always visible.
   - **Tile C — the worst screen fixed**: Agent → Posts at 390px under the
     chosen shell, hero copy wrapping, tab strip scrolling — proves the
     content-fix pattern, not just the shell.
   - **Tile D — mobile Inbox pattern**: the three panes as stacked
     navigation at 390px (list → conversation, back affordance, context as a
     drawer/sheet) — Stage 3's shape gets reacted to before it is built.
2. The operator reacts on the tiles: collapse pattern (the one open fork),
   the light-shell desktop face, and the Inbox pane-navigation shape.
   Reactions are recorded in this file's stage log, like round-6 in scope-1.

**Verify**: the operator's reaction is the oracle. No code, no tests, no
spend.

## Stage 1 — Light shell + collapse (build per the reacted mock)

1. **Rebuild the shell on the `--cm-*` token system** (operator-ruled
   fold-in): the sidebar/header retire off the legacy navy/gold onto the
   light shell per the reacted round-7 tiles. The shell's layout moves out
   of inline styles into a shell stylesheet
   (`apps/web/src/styles/appshell.css` or equivalent) so media queries own
   it natively. Delete the broken `!important` reflow block from
   `global.css` §Responsive (≤900px shell rules) AND the legacy shell rules
   that only the navy sidebar consumed (`.so-nav-link*`, `.so-sidebar*` —
   verify no other consumer first) — replaced, not patched. This is
   adopt-audit CR-1 work: the shell stops being the legacy system's biggest
   tenant. Desktop keeps its layout *behavior* (sticky sidebar, sticky
   header, pinned/immersive handling for `/inbox` and `/flows`); its visuals
   change to exactly the reacted light-shell tile.
2. **Breakpoint tokens (APPROVED amendment — a deliverable here)**: add
   `--cm-bp-shell: 768px` (the industry-standard tablet boundary,
   Tailwind/Bootstrap `md` — operator-ruled, never a project-invented
   number) to `tokens.css` and record it in `design/tokens.md`. Note: custom
   properties cannot parameterize `@media` preludes — the token entry is the
   single documented source the CSS constants cite; no other magic numbers.
3. **Implement the ruled pattern** (Tile A default): burger button in the
   header (`aria-expanded`, `aria-controls`, visible focus ring per the
   global rule); drawer at `height: 100dvh`; `body.so-scroll-lock` while
   open; closes on route change, Esc, and backdrop click; role gating
   (`canSee`) untouched.
4. **TDD in `AppShell.test.tsx`** (mock `matchMedia`): burger renders at
   narrow, not at desktop; drawer opens/closes; navigation closes it; every
   role still sees exactly its entries.

**Verify (exact oracles):**
- `cd apps/web && npx vitest run --pool=forks --poolOptions.forks.maxForks=2`
- `pnpm --filter @channels/web typecheck`
- `pnpm --filter @channels/web lint`
- `pnpm --filter @channels/web build`
- Live sweep against the dev server (`pnpm --filter @channels/web dev`):
  `mcp__chrome-devtools__resize_page` to **320/375/768/1440** +
  `take_screenshot` each; at 1440 the shell matches the reacted light-shell
  tile (layout behavior unchanged: sticky sidebar/header, pinned routes); at
  <768 the collapse pattern renders; `evaluate_script` overflow check passes
  on `/`.
- `grep` proves no `--navy`/legacy shell class survives in the shell's
  markup/styles (`rg 'so-nav-link|so-sidebar' apps/web/src` → only
  intentional survivors, each named in the stage log).

## Stage 2 — Per-screen sweep and intrinsic fixes (all screens must-fix)

Operator-ruled: **no audit-only tier — every finding gets fixed in this
package.** This stage covers the seven flow-layout nav screens — Home, Agent
(all tabs), Campaigns, Contacts, Automation, Analytics, Settings; the two
structural cases (Inbox, Flow editor) are Stage 3's dedicated work.

1. **Sweep**: for each screen, at **320/375/768**, record in this file's
   stage log: overflow oracle result
   (`document.documentElement.scrollWidth <= clientWidth` via
   `evaluate_script`), clipped/unreachable controls, unusable interactions.
   Screenshots to `docs/evidence/` (dated, per screen).
2. **Fix everything found**, using the house patterns: wide tables into
   `.so-table-scroll`; tab strips scroll like `agent-nav.css`; card grids
   onto `repeat(auto-fit, minmax(min(100%, Xrem), 1fr))`; fixed px widths
   onto `min()`/`%`; page padding onto `clamp()`. Screen-specific viewport
   breakpoints are a smell — intrinsic first.
3. Re-take the original repro: Agent → Posts at 390px emulated — the evidence
   screenshot's failure must be dead on the published site.

**Verify (exact oracles):**
- `cd apps/web && npx vitest run --pool=forks --poolOptions.forks.maxForks=2`
- `pnpm --filter @channels/web typecheck && pnpm --filter @channels/web lint`
- `pnpm --filter @channels/web build`
- Scripted sweep: the seven Stage-2 routes × 320/375/768 → overflow oracle
  green, no exceptions + screenshot evidence.
- Web built; operator publishes; the repro re-check runs against the live
  site.

## Stage 3 — The structural two: mobile Inbox and the Flow editor

Ruled into scope 2026-07-17 ("ALL screens must-fix … package is bigger by
design"). These are layout *redesigns*, not container fixes — hence their own
stage, after the shell exists and the reacted Tile D shape is known.

1. **Inbox becomes pane navigation below `--cm-bp-shell`** (per Tile D):
   - `<768px`: single-pane stack — conversation list first; selecting a
     conversation navigates to the chat pane with a visible back affordance;
     the context/detail pane opens as a drawer/sheet (the
     `MessageDetailDrawer` pattern already exists — reuse it).
   - `≥768px`: the three-pane row is unchanged.
   - The fixed `width: 330` list pane (`Inbox.tsx:364`) becomes
     `min()`-bounded so the desktop pane also stops being a magic number.
   - Selection state survives the pane switch (whatever holds it today keeps
     holding it; do not invent new state machinery — D1 seam).
   - TDD in the existing Inbox tests: narrow renders list-only; selecting
     shows chat + back; back returns to the list; desktop still renders
     three panes.
2. **Flow editor usable at tablet/phone widths**: the immersive canvas
   already fills the viewport (`AppShell.tsx:50`); the work is its chrome —
   toolbars/panels (`AddNodePanel`, executions view) must not force
   horizontal document scroll and must remain reachable at 320/375/768; the
   canvas pans *inside itself*. Full touch-gesture editing UX (pinch zoom,
   drag ergonomics) is NOT redesigned here — if the sweep shows editing is
   fundamentally unusable without it, stop and consult rather than
   improvising a touch UX.

**Verify (exact oracles):**
- `cd apps/web && npx vitest run --pool=forks --poolOptions.forks.maxForks=2`
- `pnpm --filter @channels/web typecheck && pnpm --filter @channels/web lint`
- `pnpm --filter @channels/web build`
- Live interaction evidence at **320/375/768** via chrome-devtools: on
  `/inbox` — open a conversation from the list, back returns, detail drawer
  opens/closes, overflow oracle green at every step; on a flow edit route —
  overflow oracle green, toolbar controls reachable, canvas pan contained.
  Screenshots to `docs/evidence/`.
- At **1440**: Inbox three-pane and Flow editor render with today's layout
  behavior (screenshot comparison).

---

## Gate bar

1. Web suite green via the exact per-package command; typecheck + lint clean;
   build passes by exit code.
2. **No horizontal document scroll at 320/375/768 on any of the eight nav
   screens — no exceptions** (operator-ruled: all must-fix) — evidence
   screenshots in `docs/evidence/`, overflow-oracle results in the stage log.
3. The shell collapse matches the reacted round-7 tile: keyboard-operable
   (Tab to burger, Enter opens, Esc closes, focus visible), correct
   `aria-expanded`, closes on navigation; drawer uses `dvh`; reduced-motion
   respected.
4. **The shell is on the `--cm-*` light system**: desktop matches the
   reacted light-shell tile; layout *behavior* is unregressed (sticky
   sidebar + header, pinned Inbox/Flows handling); no legacy navy shell
   class/`--navy` reference survives in the shell except survivors named in
   the stage log (CR-1 progress, verified by grep).
5. **Mobile Inbox works as pane navigation** (list → chat → back, context
   drawer) at 320/375; the Flow editor shows no document overflow and
   reachable chrome at 320/375/768; both keep today's desktop layout at
   1440 — interaction evidence recorded.
6. The original repro (Posts, 390px emulated) re-taken on the live published
   site and clean.
7. `--cm-bp-shell: 768px` exists in `tokens.css` AND `design/tokens.md`
   (approved amendment executed); the broken ≤900px `!important` shell block
   is gone; no new magic numbers, no new `!important` layout patches.
8. Zero LLM spend; committed+pushed; HANDOFF + board current; anything
   consult-deferred (e.g. a ruled-out touch UX) written into the scope
   backlog on the board before the gate closes.

## Operator decisions at kickoff (the executor must not decide these)

1. **Collapse pattern** — react to round-7: Tile A overlay drawer behind a
   burger (rec — eight entries never fit a bar without eating 2–3 rows) vs
   Tile B compact wrapping top bar. **Intentionally left to the mock
   reaction (operator, 2026-07-17), per house style.** *(Open — the only
   open decision.)*
2. ~~Breakpoint value~~ **LOCKED 2026-07-17 (prompt line, DECISIONS.md):
   768px — a STANDARD value ("not a random number you decided";
   Tailwind/Bootstrap `md`), tokenized, never a raw px in CSS.**
3. ~~Must-fix vs audit-only~~ **LOCKED 2026-07-17: ALL screens must-fix,
   including Inbox's three-pane and the Flow editor — nothing ships
   audit-only; the package is bigger by design (Stage 3).**
4. ~~Shell visual system~~ **LOCKED 2026-07-17: fold the `--cm-*`
   light-shell rebuild IN — responsive work and the reskin land as one
   package, one deploy. Converges with adopt-audit CR-1 (three competing
   styling systems).**
5. ~~Token-sheet amendment~~ **LOCKED 2026-07-17: APPROVED — breakpoint
   tokens become law; executing the `design/tokens.md` edit is a Stage 1
   deliverable.**

## Stop-and-consult list

- A screen's overflow can't be fixed intrinsically and seems to need its own
  viewport breakpoint → consult before adding one.
- The Stage-2 sweep finds a *third* screen in the Inbox class — a mobile fix
  that means redesigning its information layout, not its containers →
  consult; never silently descope, never silently balloon further.
- Flow-editor editing turns out fundamentally unusable without a real touch
  UX → consult with evidence; do not improvise gesture handling (Stage 3.2).
- Desktop layout *behavior* regresses (sticky, pinned routes) → stop; the
  reskin changes the paint, never the mechanics.
- A legacy `.so-*` shell rule turns out to have consumers outside the shell →
  name them in the stage log and leave them standing; retiring non-shell
  legacy CSS is CR-1 follow-up work, not this package.
- Anything tempting a `zoom`, root-font hack, or `!important` against the
  inline layer → stop; the package exists to remove that pattern.

## Stage log

- **GATE VERDICT: PASS (2026-07-17, Fable) — one trailing observation.**
  Bar-by-bar; items marked (F) were re-verified by Fable's own oracle, not
  the executor's report:
  1. ✅ (F) web suite 520/520 green (77 files, capped forks, 388s, Fable's own
     run); typecheck exit 0 (F), lint 0 errors (7 pre-existing warnings),
     build ✓ (F, 9.92s).
  2. ✅ overflow oracle green on all 14 routes × 320/375/768 (stage log,
     evidence `docs/evidence/2026-07-17-sweep-*.png`); no exceptions per the
     operator's all-must-fix ruling.
  3. ✅ Tile A drawer built to the reacted tile: aria-expanded, Esc/focus
     return, scroll lock, dvh, closes on navigation (7 TDD AppShell tests).
  4. ✅ (F) light shell on `--cm-*`; grep oracle `so-nav-link|so-sidebar` →
     0 matches (Fable re-ran it); tokens.md §Breakpoints amended
     (`--cm-bp-shell: 768px`).
  5. ✅ mobile Inbox pane navigation + Flow editor reachable chrome at
     320/375/768, desktop unchanged at 1440 (Stage 3 log + interaction
     evidence).
  6. ⏳ **Trailing:** Posts-390 repro re-taken on the PUBLISHED site requires
     an authenticated session — Fable verified the live site serves the new
     bundle `index-9fRsFmK9.js` (from inside the page) and /login is
     overflow-clean at 390, but the authenticated Posts view awaits the
     operator's first phone glance. Local emulated repro is dead
     (`2026-07-17-repro-posts-390-fixed.png`). A live overflow report reopens
     bar 2.
  7. ✅ (F) publish done + hash-verified served = VPS-built.
  **Package closed; scope idles. Round-7 locks all executed (drawer, mark
  logo, 768 boundary, Tile C pattern, D bottom sheet, E desktop face).**

- **Stage 3 DONE (executor, 2026-07-17) — mobile Inbox pane navigation + Flow
  editor chrome.**
  **3.1 Inbox (per reacted Tile D, sheet not side-drawer):** below
  `--cm-bp-shell` the list is the screen; opening a conversation renders the
  chat with a `Back to conversations` affordance (clearing the `contactId`
  param — selection stays in the URL, NO new state machinery, D1 seam
  honored); lead context opens as a bottom sheet (`74dvh`, grab handle,
  `role=dialog` "Lead context", close button + Esc + scrim close,
  `aria-expanded`/`aria-controls` on the Details trigger; `ContactSidebar`
  gains a `fluid` prop to fill the sheet). The chat header wraps at narrow so
  the chip/toggle/Details row stops squeezing out the name. Desktop: the
  fixed `width: 330` list pane became `min(330px, 38vw)`; three-pane
  rendering otherwise byte-identical (verified live at 1440: list 330px,
  context rail, no back/no sheet, pinned shell `overflow: hidden` —
  `docs/evidence/2026-07-17-inbox-1440-threepane.png`). Pre-existing
  observation, NOT a regression: at 1440 the chat-header name truncates hard
  because the desktop header's right-side controls don't shrink — untouched
  by this package.
  **TDD:** 5 new tests in `Inbox.test.tsx` (list-only at narrow / chat+back /
  back returns / sheet open+close / desktop three-pane regression) — red
  first (4 failed pre-implementation after a ToastProvider harness fix),
  then 10/10.
  **3.2 Flow editor:** at 320 the toolbar CLIPPED Redo/Published/Save/More
  past the viewport (unreachable, measured x=321..568) and the
  absolute-centered Editor/Executions tabs sat on the flow name. Fix: below
  the boundary the top bar wraps (`topBarWrap`), tabs join the flow
  (`tabsInline`), name input `min(160px, 38vw)` + flex; desktop keeps the
  absolute-centered single row (verified at 768/1440, tabs computed
  `position: absolute`). Live at 320: all toolbar controls reachable
  (measured), AddNodePanel contained, canvas pans inside itself, overflow
  oracle green at 320/375/768. **Editing was NOT fundamentally unusable —
  no touch-UX consult needed; no gesture handling invented.**
  **Oracles:** web suite 77 files / **520 tests** green (exact per-package
  command); typecheck exit 0; lint 0 errors (same 7 pre-existing warnings);
  build ✓. Live interaction evidence:
  `docs/evidence/2026-07-17-inbox-{320-list,320-chat,320-sheet,375-sheet,1440-threepane}.png`,
  `docs/evidence/2026-07-17-flow-editor-{320,320-addnode,1440}.png`.
- **Stage 2 DONE (executor, 2026-07-17) — per-screen sweep, all findings fixed.**
  Sweep method: dev server + the Stage-1 stub API (fixtures per real client
  shapes; a 4-row turns fixture exercises the Activity table with content),
  chrome-devtools device emulation (Chrome's ~500px minimum window width
  forces `emulate` viewports instead of `resize_page` for 320/375 — deviation
  from the kickoff's tool note, same oracle). **Overflow oracle
  (`scrollWidth <= clientWidth`) green on every route at 320, 375 AND 768**
  (768 renders the desktop sidebar on all): `/` · `/agent/{activity,review,
  posts,triggers,behaviour,knowledge,playground,models,channels}` ·
  `/campaigns` · `/contacts` · `/automation` · `/analytics` · `/settings`.
  **Findings → fixes (house patterns only, no new viewport breakpoints):**
  (1) Activity at 320: turns table + trace column blew the grid track →
  shared `Table` component now renders inside its own `.cm-table-scroll`
  container (ui.css; the `.so-table-scroll` pattern for the cm system,
  app-wide) + `.cm-activity__split > div { min-width: 0 }`. (2) Playground
  composer: input's flex `min-width:auto` pushed the Test-triggers button 2px
  past 320 → `min-width: 0`. (3) Agent Channels canary add-row: fixed
  `minWidth: 220` input in a non-wrapping flex → `min(220px, 100%)` + wrap
  (`routes/agent/styles.ts`). (4) Campaigns (+ every PageHeader screen):
  hero actions row never wrapped → `flexWrap: "wrap"` in the shared
  `PageHeader`. (5) Home grids fixed in Stage 1 (`min(100%, Npx)`).
  **Original repro re-taken:** Agent → Posts at 390 emulated — no document
  overflow, subnav scrolls internally with the fade-cue pattern
  (`docs/evidence/2026-07-17-repro-posts-390-fixed.png`); the LIVE-site
  re-check happens after the operator publishes (gate bar 6).
  **No third structural screen found** — nothing beyond Inbox + Flow editor
  needed information-layout redesign; no stop rule triggered.
  **Oracles:** web suite 77 files / 515 tests green (exact per-package
  command); typecheck exit 0; lint 0 errors (same 7 pre-existing warnings);
  build ✓ 9.1s. Evidence: `docs/evidence/2026-07-17-sweep-375-*.png`
  (12 screens) + the repro shot.
- **Stage 1 DONE (executor, 2026-07-17) — light shell + Tile A collapse.**
  Built per the locked round-7 reactions: shell rebuilt on `--cm-*` in a new
  `apps/web/src/styles/appshell.css` (Tile E desktop face: white 216px sticky
  sidebar, accent-soft active nav, light sticky header; Tile A below the
  boundary: 52px top bar + burger + `100dvh` overlay drawer, real
  `logo-mark.png` asset — not the "SO" stand-in). Collapse is matchMedia-driven
  (`SHELL_COLLAPSE_MQ = (max-width: 767.98px)` in
  `apps/web/src/lib/use-media-query.ts`, citing the new `--cm-bp-shell: 768px`
  token — desktop AT 768) because the drawer is behavior (burger state, scroll
  lock, Esc, focus return), not paint; the two layouts render different markup.
  Breakpoint token landed in `tokens.css` AND `design/tokens.md` §Breakpoints
  (approved amendment executed). Deleted from `global.css`: the `.so-nav-link*`
  block, the broken ≤900px `!important` reflow block, the ≤540px `.so-header`
  block, and the dead `.so-kpi-row` rules (zero markup consumers — verified by
  grep). `body.so-scroll-lock` KEPT (Modal's shared utility; the drawer reuses
  it). `index.html` theme-color navy → `#FFFFFF` (shell paint, same fold-in).
  **Survivors named:** legacy `--navy`/`--gold` tokens + `.so-btn/.so-card/…`
  in `global.css` remain for *content screens* (Templates, Campaigns, etc.) —
  non-shell consumers, CR-1 follow-up per the stop-rule.
  **Oracles:** TDD red→green — 7 new drawer tests failed against the navy
  shell, then `npx vitest run --pool=forks --poolOptions.forks.maxForks=2` →
  **77 files / 515 tests passed** (13/13 in AppShell.test.tsx); typecheck
  exit 0; lint 0 errors (7 pre-existing warnings in analytics/quality api
  tests, not this package's files); build ✓ 9.1s; grep oracle
  `rg 'so-nav-link|so-sidebar' apps/web/src` → **no matches**. Live sweep
  (dev server + stub API — no local Postgres/API on this machine; stub serves
  `/api/auth/me` + a `/api/home` fixture, zero app-code changes): 1440
  matches Tile E (sticky sidebar+header computed `sticky`, sidebar 216px,
  overflow oracle green); **768 stays desktop** (no burger); 767 collapses;
  375 drawer open/close verified live (open → `100dvh` drawer, scroll lock,
  `aria-expanded=true`, focus → close button; Esc → closed + focus returns to
  burger; nav click → routes to /contacts + closed + lock released); 320
  overflow oracle green on `/` after fixing Home's three `minmax(Npx,1fr)`
  grids to the B3 `minmax(min(100%,Npx),1fr)` form (`home.css` — the 320px
  min track was forcing 336px scrollWidth; desktop rendering identical).
  Evidence: `docs/evidence/2026-07-17-shell-{1440-light,768-desktop,375-closed,375-drawer-open,320-home}.png`.
  **Not verified here:** 200% text zoom (WCAG 1.4.4) and reduced-motion were
  not exercised live (reduced-motion inherits the untouched global rule by
  construction); live-published re-checks are the gate's publish half.

- **Forks ruled + kickoff amended (2026-07-17):** operator ruled four of the
  five kickoff forks via the prompt line (recorded in `docs/DECISIONS.md`
  § "2026-07-17 — appshell-scope-2 forks ruled"): 768px standard breakpoint
  (tokenized, amendment approved), ALL screens must-fix (Stage 3 added for
  Inbox + Flow editor), `--cm-*` light-shell rebuild folded in (round-7
  mocks render the light shell; CR-1 convergence noted). Collapse pattern
  deliberately left to the round-7 reaction. Execution remains blocked on
  the scope-1 gate (WIP rule).
- *(Kickoff written 2026-07-17.)*
