# Funnel walkthrough and private draft previews

Use **Preview** in the editor to walk the current draft, including unsaved changes.
The template preview dialog uses the same walkthrough before a draft is created.
The clean **Open full page** template link remains a design-only view for reviewing
and capturing the landing page.

## Try the journey

- Page chips jump to any step. **Restart** returns to the first step and clears
  the preview's form fields.
- A next-step button opens the next ordered page.
- A form CTA scrolls to the page's visible lead form and focuses its name field.
- Preview forms validate name, email, and the contact-consent checkbox. A valid
  simulated submission opens the next ordered page. No contact is created and no
  submission, email, message, tracking event, or form value is persisted or sent.
- An external CTA shows its exact configured destination. **Open destination**
  deliberately opens that HTTPS link in a separate tab. The walkthrough does not
  visit it automatically. Public page buttons keep their existing behavior.
- Missing forms, next steps, or URL destinations produce actionable feedback in
  the preview. An unfinished last-page form remains on the page with an error.

Preview navigation does not mutate the document, save the draft, change its
revision, or publish it. **Edit** returns to editing the currently viewed page.
Simulated form values exist only in DOM form controls and are discarded when the
page is replaced, restarted, closed, or expired.

## Open an unsaved draft in its own tab

The toolbar's external-arrow button, **Open private preview tab**, opens a validated
snapshot of the current draft and page. It does not require Save. The new view has
its own desktop/phone controls, page navigation, and Restart action. Further edits
in the editor do not change this snapshot; open another preview to review them.

A private snapshot expires 30 minutes after creation and survives reload in its
own tab until then. Its URL identifies the local preview but contains no document
or form values. Copying that URL to an unrelated tab, browser, or device does not
make the draft available. This is a local review tool, not a shareable hosted draft.
The editor's Tailscale URL can still be used to review the application and create
independent local demo drafts on another device.

The launcher places a temporary snapshot in session storage, opens the same-origin
tab, detaches its opener immediately, and removes the parent copy. The child keeps
its independent session copy until expiration or closure. This follows the
[documented session-storage creation/copy behavior](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage).
If the browser blocks storage or a new tab, the editor explains the problem and
keeps its existing in-editor Preview available. No storage/provider permission
flow is required by this feature.

`/funnel-preview/TOKEN` serves only a generic client shell. The server does not
receive or retrieve a draft document, even when a valid token is supplied. The
client validates the envelope, document schema, size, selected page, and expiration
before rendering. Expired/corrupt entries are removed. The route has noindex,
nofollow metadata and headers, is excluded in robots rules, and is never added to
published sitemaps. No Convex or provider API is called by a preview.

## Validation

- `tests/funnel-preview.test.ts`: independent snapshot creation, schema validation,
  bounded lifetime, invalid pages, expiry, malformed envelopes, and unsafe URLs.
- `tests/integration/funnel-preview.mjs`: template walkthrough, required consent,
  simulated advance, Restart/reset, unsaved snapshot, opener detachment, parent
  cleanup, reload, unrelated-tab isolation, expiration cleanup, blocked popup and
  storage fallbacks, next/form/external actions, unfinished forms, phone fit, no
  POST requests, and unchanged local storage.
- `tests/integration/funnel-seo.mjs`: production HTTP checks include private-preview
  metadata/headers, generic SSR content, invalid-token 404s, and robots exclusion,
  alongside the published funnel SEO checks.

Run the browser harness against the loopback dev server with
`node --import tsx tests/integration/funnel-preview.mjs`. Supply `PUPPETEER_MODULE`
when using a separate local browser QA installation. The production HTTP fixture
runs after `pnpm build` and makes no cloud requests. Captures and logs stay under
ignored `artifacts/funnels/`.
