# Preserve an existing Vitris website

Use `--mode preserve` to create a custom copy of a legacy Vitris client-facing site, retaining its HTML, CSS, assets, copy, routes, and supported interactions. The default remains `--mode redesign`. Preserve mode does not apply a theme or run the creative workflow.

```bash
vitris site create --dir ../acme --slug acme-preserve \
  --name "Acme Construction" \
  --from https://old-site.example/ --mode preserve
cd ../acme
npm install
npx playwright install chromium
npm run go
npm run preview
npm run deploy
```

The legacy `vitris-create-site` entry point accepts the same mode. With a local harness checkout, supply `--harness file:../vitris-harness` when creating a sibling site. `vitris run --site DIR`, `vitris verify --site DIR`, and `vitris deploy --site DIR --environment preview` are also supported.

Capture runs once. It follows same-origin links and legacy Vitris navigation data, including hidden navigation entries. The original renderer executes only during capture; its scripts, analytics, and services are removed from the copy. Assets are copied locally, including CSS imports, fonts, backgrounds, gallery images, and linked downloads. URL queries distinguish asset variants. A small local runtime supplies supported menu, gallery, mobile footer spacing, and form interactions.

Authored output lives in `preserved/`, which builds directly without the usual site wrapper or design CSS. References and capture provenance live under `onboarding/`. The existing managed agent kit is retained; preservation dispatch does not modify or execute its creative graph. A later redesign should be scaffolded separately so the preserved site remains available.

Verification compares every captured route at 1440px and 390px against hashed source screenshots. Dimensions must match; the pixel difference limit is 0.5%, with a 24-channel-value noise tolerance. These are measured visual checks, not a promise of exact equivalence across every browser and state. Verification also checks local links, anchors, images, menus, gallery opening/closing, and form feedback, with external resource requests blocked. Failed checks stop deployment. Inspect `agent/reviews/preserve.json` and screenshots in `artifacts/preserve-verification/`; repair `preserved/` and rerun. Never edit reference captures to make a check pass.

Contact forms use the shared `/api/leads` service only after explicit server registration and public `launch.json.forms` bindings. `backendOrigin` alone does not enable submissions or notifications. The old `number` field maps to the compatible phone column while retaining its authored HTML name. Default previews validate locally and display a notice. The shared runtime adds a security challenge on submission and accessible feedback while preserving layouts and fields. Verification separates resting-page fidelity from mocked challenge/submission states and sends no customer inquiries. See [CONTACT-FORMS.md](CONTACT-FORMS.md) for sandbox setup, provisioning, server readiness, delivery semantics and the separately authorized rollout. Legacy analytics and employee portal services are not migrated. External navigation links, including employee-login links, remain links to their existing destinations.

Deployment requires `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN` with Pages Edit permission. The harness inspects or creates the Pages project, verifies that the preview branch differs from its production branch, stages hashed verified bytes, uploads them, and repeats browser checks against the returned `pages.dev` URL. A newly created project uses `production` as its reserved production branch. No customer domains or DNS records are changed. Preview responses carry `noindex`; previews are public and do not expire automatically. Local builds and direct uploads are the default. The inactive deployment/github-actions.yml.example can be installed explicitly for manual, preview-only hosted automation.

Initial availability checks retry transient hosting failures. If Cloudflare has accepted the upload but its preview is not reachable yet, keep the returned URL and recheck it with `vitris verify --site DIR --remote https://HASH.PROJECT.pages.dev`. This reruns the hosted browser checks without uploading another deployment. Local and remote reports are separate.

The adapters target legacy Vitris v1 pages using navbar, header, photo gallery, cards, info, contact form, footer and static HTML components, and legacy Vitris React SSR pages using call-to-action, contact-form, HTML and branded-footer blocks. React navigation is discovered from its JSON state, including nested and hidden entries. The existing React mobile navbar and contact-form markup use the local preview runtime. Public route segments ending in a period use confined hashed files and explicit Cloudflare 200 rewrites so Windows storage does not change the public URL. Unsupported components, embedded applications, query-dependent pages, asset failures, and visual differences require an adapter or a repair; they are not silently accepted. Arbitrary external websites, authenticated content, checkout, booking, and backend migration are not supported. Pages absent from both navigation and discovered links cannot be inferred from a public URL.

For an inventory audit, the source API `capturePreservedSite(root, { diagnostic: true })` can retain independent static capture work when a component or embed is unsupported. It records `onboarding/preserve.json.blockers`; verification and release preparation fail while any blocker remains, even when resting-page pixels match. The ordinary CLI capture still rejects unsupported content. Diagnostic mode does not authorize publication or imply functional parity. Keep the original capture, rendered HTML and hashed references; repair authored source and use new evidence for any later adapter review. Run the targeted preservation regressions with `node --experimental-strip-types --test src/preserve.test.ts src/preserve-batch.test.ts`.

Existing public Google Maps `/maps/embed` and YouTube `/embed/VIDEO_ID` frames can be retained as read-only provider dependencies. These embedded applications are not downloaded or replaced. Their provider content, availability and privacy behavior remain external and must be listed in the preview exceptions. The top-level site's scripts, images, fonts and connections stay local; CSP `frame-src` adds only the exact required provider origins. Verification admits provider resources only within a descendant of an approved embed, while unrelated top-level requests and legacy Vitris service calls remain failures. Booking, checkout and embedded form applications remain unsupported. Historical diagnostic blockers for a now-supported static React HTML block or an approved read-only embed are resolved by the current adapter without editing the immutable capture manifest; other blockers still stop release.

Diagnostic capture also records unavailable navigation targets and missing assets while continuing through other public pages. A missing authored asset is explicitly an empty placeholder with a release blocker; its original failed response is retained as evidence. This never counts as successful asset preservation. Every served asset and download is checked byte-for-byte against the authored build locally and remotely, and retained original asset responses are checked against their capture hashes. Hosted checks also require noindex and the preservation CSP.

Static v1 blog feeds and iframe/map wrappers are supported only when their original rendered DOM has no active search, pagination or unknown callbacks. Accordions use the existing collapse markup and are exercised during verification. React galleries are the original static `portfolio-grid` image layout; the legacy Gallery renderer has no lightbox interaction. Navbar dropdowns retain the legacy click behavior. Windows case-distinct routes use separate hashed files when their ordinary folders would collide. Dotted page slugs are routes; recognized document/media suffixes identify downloadable files.

Capture blocks legacy Matomo and Google Analytics requests, while leaving the original visual renderer and provider frames active. Original rendered HTML, screenshots and source asset bytes remain immutable. The browser verifier checks horizontal overflow against the original reference and reports existing source overflow without redesigning it. A narrowly identified YouTube compute-pressure permission denial is reported as a provider warning; other browser errors and failed resources remain failures. Run `npm run test:preserve` for the adapter and preview-release regressions.

Button hover colors are measured on the original without clicking or submitting it. Authored `data-preserve-hover` metadata replays those observed color/background/border states through the local runtime and restores the original resting style. Hover evidence is stored beside new captures; older captures can be repaired using separate immutable interaction evidence without replacing their screenshots. Verification exercises these hover states at both viewport widths. The pointer is parked outside the viewport before resting screenshots. Application scripts such as accessibility widgets, financing calculators and property searches are explicit release blockers until adapted; removing a script cannot silently count as functional preservation. Opaque read-only Maps query values retain their original request data and still must pass the visual/browser gates.

After explicit operator authorization, a temporary React preview can set `launch.json.previewDisabledTransactions: true`. This supports the known captured calendar and single-page custom intake form markup only after authored controls are adapted and the verifier proves their original block/field structure. Booking dates and month controls display a disabled notice; they do not fetch current availability or book appointments. Custom forms retain fields and local file selection, but never upload, route to the shared contact service, or claim delivery. Checkout and Add To Cart controls display a disabled notice rather than creating orders or payments. These are documented functional differences, not full transaction parity. Without this explicit preview mode, transaction capture blockers still fail release. Static React blog and collection blocks require proof that their original markup contains no active search, pagination, form or other unsupported controls.

Adapted product carousels retain the original slide markup, transforms and selection state. Captured product variant states retain the original price/quantity controls and use the same disabled purchase notice. Verification exercises selections, images, keyboard navigation and disabled transaction feedback. Deferred React images use local bytes and the original renderer's 200px observer margin; references remain unchanged. Assets found during later inspection can be registered in immutable `onboarding/preserve-supplement-TIMESTAMP/assets.json` files, listed by `onboarding/preserve-supplements.json`. Supplements must match the original source URL/capture timestamp and cannot replace any original asset record. Their raw bytes and hashes join local and hosted asset checks.

Authorized disabled previews also support the known v1 service-list markup. Additional service cards are captured through read-only pagination, stored with immutable interaction evidence, and paged locally. Booking opens the original modal shell with a disabled notice. Unknown controls still block publication. Single-page custom forms retain the exact original fields, including local attachment selection, without uploads or delivery.

Original defects remain explicit exceptions. The verifier can distinguish an anchor already missing in both the original link and the original target from a newly broken authored anchor. Proven `/null` image placeholders and the unavailable legacy 404 illustration retain their original broken-image rendering; they are never described as preserved photographs or successful source assets. Malformed telephone and relative Google Maps links can be repaired only to their original intended external destinations. All visual and browser gates still apply.

Newly discovered routes can join the immutable supplement index without replacing any captured route or reference. `capturePreservedSite` accepts an explicit `onlyPaths` list for supplemental capture. Diagnostic `includeSourceErrors` requires that list and retains original HTTP/soft-404 pages with hashed raw response evidence. The preview serves this static error content with HTTP 200, an explicitly reported HTTP-status difference; absent business content is not invented. A supplemental error page must pass the same desktop/mobile comparisons and interaction checks before the corresponding old diagnostic blocker can be resolved. Supplements retain their own blockers and warnings.

The existing MassageBook `/therapists/SLUG/widget/reviews` endpoint can remain an external read-only reviews dependency. Booking widget paths, query variants and unrelated origins are excluded. This exception does not enable embedded booking or customer submissions; provider availability, scripts and privacy behavior remain external limitations.

The browser verifier distinguishes only the known cancelled Google `/measurement/conversion` telemetry request originating inside the approved MassageBook reviews frame from a functional resource failure. It reports that cancellation as an external-provider privacy warning. Other endpoints, top-level tracking, connection failures, review assets and visual differences remain failures. Dynamically paged service cards rebind measured hover states and are exercised after pagination, including disabled booking feedback.

Earlier screenshot-only captures remain verifiable without recapturing or altering their baselines. Missing original DOM evidence grants no source-defect or component exception: authored links, resources, visual comparisons and browser gates still apply. Supplemental source-error exceptions require the original rendered DOM and hashed raw response evidence.
