The shell — rail, guest, and every failure stays inside it.
← Design HQ · HTML on GitHub · PRD — Platform core (shell chrome)
The chrome every page sits inside — 72px header, variant-B left rail, collapsed rail,
mobile drawer, and flat footer (as-built from PlatformLayout.tsx,
UnifiedSidebar.tsx, Footer.tsx) — plus the three surfaces that
replace the shell when it fails to render: the boot watchdog, the ErrorBoundary, and the
stale-chunk reload beat (frames 10–12).
Reload, never "Reload Page"no escape link — the rail is the escape
Frames 10–12 — the error never takes the shell
down (Peyton-locked 2026-08-10). There are three ways to fail — the boot watchdog (10)
when React never mounted, the ErrorBoundary (11 / 11c) when React mounted and something
threw, the reload beat (12) which is not an error at all — and in every one of them the
header, rail, and footer stay on screen with the message sitting in the content column. Two bugs
drove this: the layers had drifted into different visual and verbal registers, and
ErrorBoundary claimed min-h-screen even when nested inside
PlatformLayout, blanking a shell that was mounted the whole time.
The blast-radius ladder (frames 10–14). Every error sits at the smallest scope that actually failed, and the treatment shrinks with it: ① the whole app can't run → static shell (10, 11c) · ② one page crashed → real shell, message in the content column (11, 11b, 12) · ③ one region's data failed → inline block inside that region, page untouched (14) · ④ one action failed → toast or inline message under the control · ⑤ the route doesn't exist → 404 in the shell (13). Rungs ③ and ④ are deliberately NOT the crash surface — a failed list wearing a 20px headline would misreport how much broke. The scale difference is the message.
Two shells, one appearance. Where React is
healthy (11, 11b, 12) the real PlatformLayout renders. Where it is not — the
watchdog (10) and the root boundary (11c), which can be catching PlatformLayout
itself — the chrome is a hand-drawn static copy: markup only, nav rows as plain
<a href> (a full page load needs no JavaScript), and no balance / name /
sign-out, because neither case can know the auth state and neither may guess. Identical
appearance, deliberately different implementation. Rendering the real layout in those two cases
would risk re-crashing on the thing that just crashed.
The widths drawn here are samples of a fluid
continuum, not the only states that exist — content (spacing, type, gaps, card/column widths)
scales continuously between them, each clamp reading a named container. Structure (rail,
row↔stack, grid columns) steps only at sm 640 · md 768 ·
lg 1024; radius, hairlines, and pills never scale. Kit: design-canon.md U8.
Reading order in this document: § Desktop
journey first — every desktop frame, full flow, journey order — then § Mobile
journey — 375 below it — every mobile frame, same order, laid out side by side as a
wrapping grid (never a tall single-file column of phone screens). Two sections, never
interleaved — a review-document convention only (this repo's design-canon.md §
Mockup review reading order). Mobile 375px is still designed and scored FIRST in the /design
ritual (kit canon U5/U8) — the two rules never conflate.
Review Feedback here. Save this file, open it locally, then jump: form · sent · guest · 375. V3: Thread under the title, Seal when sent, Mark only if you are not signed in. Header lantern stays the one gold. Do not open feedback.html.
UnifiedSidebar.tsx)
/billing/usage exists, today reached only via in-page tab) · Sign Out.About · Privacy · Terms, the last two deep-linking into /legal). One home per link. FAQ stays in Support (a help resource in-rail; the footer FAQ serves the site-info context).rgba(255,255,255,.06) (surface-elevated) replaces the solid-white pill background across the whole row.· at white/30) separate; the C5 log-out glyph stays. Display name falls back to the account email only when no name is set. MEMBER badge retired — the signed-in cluster itself says "member"; Preview is the header's only badge.SidebarEyebrow)
--surface-el, hairline border, 12px label — the page title, so nobody has to guess what an icon means. Same trigger fires on keyboard focus.PlatformStatusBadge
preview stage is interactive — other stages render a plain muted label, no click target.tabindex="0"), never click — cursor stays default.preview_banner_viewed on open; CTA deep-links to WhatsApp (pre-filled "help build" message) + preview_banner_cta_clicked.n of 5 + words), and the chip carries its own progress line — a chip-wide .12 track 7px under the caption, lit white to the playbook required-% — steps 1–3 draw a 6% "just started" sliver (no Brain queries yet), step 4 the real %, step 5 full. v4.1 (Peyton, 2026-08-16, production): the v4 full-header hairline echo read as a bar across the whole 1440 chrome — "just the progress line, not the full bar." The header's own bottom hairline stays dark; the line belongs to the chip. The line under each header is the on-page wording.lg (1024px) and up only. Below that — tablet included — the chip renders as the 375 progress band (see the mobile frames), not a center caption. The old sm/640 switch left tablets a center chip that read as detached.Pre-built assistants. Connect what you need. Pay only when they run.
STEP 1 kicker so the line is findable in the header. The words stay the guide. No fill, no ring, no pill. Motion is opacity/scale only; it stills under reduced-motion.--fg-30). Hover reveals link-ness with a hairline underline on the words — never a background fill. Not a 3-step list, not a catalog banner, not a floating pill (2026-07-20). Preview stays the only amber badge.#assistants-shelf. Away from Assistants it reads STEP 1 “Pick an assistant →” and goes to /assistants.You opened On-Brand Support. The wizard still needs a finish.
2 of 5 “Activate it →” reopens that assistant. On this page: “Finish setup to go live →” is the same door — ?continueActivation= reopens the wizard. A caption with no door is not the chip.active. Chip advances to step 3 immediately.Your active assistants
STEP 3 “See it on Pulse →” goes to /pulse. On Pulse: “Your first run lands here →” opens the Activity tab (/pulse?tab=activity) — the place runs land.pulse.html) — the chip does not replace that page.First run landed. The chip stays until the playbook is ready.
% of required./brain#brain-landing-band. Away copy: “Make them sound like you · 38% →”. On Brain: “Every assistant reads this page” (no second %).Playbook is ready. Drop what’s new — or re-read the site.
/brain?teach=1.bg-black/80 backdrop closes on tap.Footer.tsx)
Pre-built assistants. Pay only when they run.
--surface-el tone step (the "you are here" tone), single line mark · n of 5 · words · →, whole band is the tap. Band ≠ pill: full-bleed, zero radius, borderless, nothing pressable-looking.lg (1024) — tablets use the band, not the desktop caption (Peyton 2026-08-16).Your active assistants
Chip stays. The number is the playbook %.
Playbook is ready. Open Teach once, then the chip is gone.
BrandBlock / HeaderActions / UnifiedSidebar)
useWallet + name + sign-out render null while isGuest.WaitlistModal) — the pill stays the surface's ONE lit object.AuthModal) — same position and row shape (RS3). About MystFlo + Legal removed here too — the footer owns them for guests as well.WalletBalancePill (now quiet text, no border/fill)TopUpModal — on desktop AND in the mobile header's right slot.MobileActionBar
isGuest is false.This is on our side, not yours. We've been notified — reload this page, or pick another from the menu.
index.html inline <script> (#2908), shell restored 2026-08-10
prd-pl-error-messages-self-recovery.md — it shipped in #2908 undocumented. Needs one: #root still empty 6s after load → static shell + message + one capped auto-report.PlatformLayout. The inline script paints a STATIC copy: wordmark, rail, footer. Every nav row is a plain <a href> — a full page load, which works with zero JavaScript. So the menu is not decorative; it genuinely navigates.innerHTML template string, still zero imports, still inside the existing try/catch, still nothing that can throw. The mirror test covers the chrome values too.Reload not "Reload Page"; no "Send report" button (it already auto-reports silently, capped 1/session — a second control claiming to do what already happened is the dishonesty #2908 removed).This one's on us — your data is safe and we've been notified. Reload this page, or pick another from the menu.
App.tsx:119, inside PlatformLayout (Peyton-locked 2026-08-10)
[DEPLOY-WINDOW] / [QA] Slack prefixes.App.tsx:119 exists to make that true but no criterion holds it. Needs one.Go to Explore since #1114. Correct the PRD line in the same PR.min-h-screen + centred flex unconditionally, so nested inside the content slot it paints a full-viewport block and reads as if it destroyed the shell — the header and rail were mounted the whole time, just shoved out of view. Nested renders .crash-inline (fills and centres in the content column, no viewport claim); only the root case keeps min-h-screen.WithLayout. This frame, not 11b, is the common case.Reload label, one-sentence body — same three parity changes as the other layers.This one's on us — your data is safe and we've been notified. Reload this page, or pick another from the menu.
[ref:xxx] to anchor a chat to, so its honest fallback stays the support email. The copy still admits the chat needs you to hit send.This one's on us — your data is safe and we've been notified. Reload this page, or pick another from the menu.
App.tsx:128, wraps the whole router
PlatformLayout itself, in a provider (Auth / QueryClient), or on a no-layout route (/auth/callback, /data-deletion-status, /s/:token).PlatformLayout: one of the things it catches is PlatformLayout (or a provider it depends on) throwing, and re-rendering the thing that just threw is an infinite crash loop. It renders the same hand-drawn static shell as frame 10 — markup only, no React layout component, no auth hooks, no useWallet. Identical chrome, different implementation, on purpose.ErrorBoundary.tsx + @/lib/staleChunk.ts
WithLayout page — the header and rail are mounted, so blanking them for a ~1s beat was the same min-h-screen bug in miniature, and the more jarring one because nothing was actually wrong. The full identity cluster stays live here (unlike frames 10 / 11c, React is healthy — only one chunk is stale).This page doesn't exist — or it moved. Pick a destination from the menu.
src/pages/NotFound.tsx, route path="*"
min-h-screen centred with no shell, routed OUTSIDE WithLayout — the exact pattern retired in frames 10–12. It moves inside WithLayout and drops the viewport claim, same as the nested boundary.--hairline tokens goes.We couldn't load your recent runs. Nothing's wrong with your assistants — this is just the list.
src/components/DataLoadError.tsx (currently ZERO consumers — dead code)
<DataLoadError> state with a "Try again" retry instead of silently rendering an empty state. This criterion does not currently ship — the component exists and nothing imports it.RefreshCw in a bg-muted circle, the same retired pattern as the crash screen's ⚠. The glyph moves inline into the button, where it labels an action instead of decorating a state.This is on our side, not yours. We've been notified — reload this page, or pick another from the menu.
lg 1024 breakpoint (frame 2's behaviour — the crash surface inherits it, it does not invent its own).max-width:420px yielding to width:100% below ~420px.This is on our side, not yours. We've been notified — reload this page, or pick another from the menu.
index.html)
This one's on us — your data is safe and we've been notified. Reload this page, or pick another from the menu.
App.tsx:119)
w-full sm:w-auto); everything above it stays centred.