# The frontend pull request — exact contents, not yet raised

> ⚠ **Scope moved 2026-08-04 (operator): the widget and how it looks belong to
> the FRONTEND. We own the endpoint and `docs/web-chat-api.md`.** That makes
> **hunk 1 (the rewrite) the only required part of this PR** — it is what makes
> the session cookie first-party, and no client works without it. **Hunk 2 is now
> optional**: take it only if the frontend chooses to use the widget we already
> serve instead of building their own. Our `widget.js` stays either way, as the
> reference implementation of the call sequence.
>
> The ONE change needed on `aatifaneez/silver-oak-properties-main-website` to put
> the chat on the site. Two hunks, no new files, no build step, no dependency.
> **Not raised yet** — it needs the operator's answer on who merges there, and it
> must be authored as `sop-dubai` (see Authorship).
>
> Local clone: `dev/frontend-sop`. **It was on branch `perf/lcp-fetchpriority`
> when this was written — another session's work. Do not switch it.** Branch
> from a fresh fetch of `main` in a worktree of that clone instead.

## Why it is only two hunks

- `assets/js/site-common.js` is loaded by **every** page, generated property
  pages included (verified 2026-08-03 by grepping every `*.html`). One edit
  there reaches the whole site, now and for every page generated later.
- The site already proxies through its own origin, so `/chat/*` needs a
  `rewrites` entry and **no CSP change at all**: `connect-src 'self'` and
  `script-src 'self'` already cover it. Nothing in `vercel.json`'s CSP moves.
- The existing `redirects` block sends `/api/:path*.js` to `404.html`. Our path
  is `/chat/*`, so the two do not interact — checked, not assumed.

## Hunk 1 — `vercel.json`

Add a top-level `rewrites` array (the file already has `headers` and
`redirects`; `rewrites` sits beside them):

```json
  "rewrites": [
    {
      "source": "/chat/:path*",
      "destination": "https://channels.silveroakglobal.com/api/web-chat/:path*"
    }
  ],
```

That single line is what makes the session cookie first-party. A widget calling
`channels.silveroakglobal.com` directly would be third-party, and Safari and
Firefox block third-party cookies by default.

## Hunk 2 — `assets/js/site-common.js`

Append, inside the file (it is already one IIFE; this can go at the end of it or
as a second IIFE — either is fine):

```js
  /* Silver Oak website chat. Served from our own origin via the /chat rewrite,
     so `script-src 'self'` covers it and the widget can be updated without
     another change to this repo. */
  (function () {
    var s = document.createElement('script');
    s.src = '/chat/widget.js';
    s.async = true;
    document.head.appendChild(s);
  })();
```

After this merges, **every later change to the chat ships from our side** — the
widget file, the copy, the styling, the behaviour. This repo should not need
another chat-related pull request.

## Authorship — operator instruction, 2026-08-03

That Vercel project is **not on the company account**. Commits there are
authored as **`sop-dubai`**, never `ammarsilveroak`. Set it per-repo, never
globally:

```bash
git -C <clone> config user.name  "sop-dubai"
git -C <clone> config user.email "<the sop-dubai address>"
git -C <clone> log -1 --format='%an <%ae>'   # show this before pushing
```

The email is the operator's to supply; do not guess it.

## Before raising it

1. `web:chat` must be `live` (or `shadow` for a silent trial) — with the gate
   `off`, `/chat/*` answers 503 and the widget shows its offline line, which is
   graceful but pointless.
2. The `web` connection must exist with `allowedOrigins` containing the real
   origin the visitor's browser sends. **Before the domain cutover that is
   `https://production-omega-rose.vercel.app`; after it, `https://silveroakglobal.com`
   (and `https://www.silveroakglobal.com` if the www host serves pages).** Both
   can be listed at once — the allowlist takes many.
3. `privacy.html` should say a chat conversation is stored and for how long.
   That is a content change on the same site and belongs in the same PR.

## The oracle for this PR

With the gate live, open a real property page on the deployed preview, send a
message, and confirm: the reply appears, the row lands in our database with
`channel='web'`, and `document.cookie` in the browser console does **not** show
`so_chat` (it is HttpOnly — if you can see it, the rewrite is not in play and
the cookie is being set by the wrong origin).
