← Files HiggsfieldARCHIVED FILE

skills/website-builder/references/website-flow.md

31.4 KB · Oct 5, 2026 · 12:03 UTC

↓ Download file

# Website flow — `type: "website"` (independent brand, phased pipeline)

> **Use `sandbox_exec` for all code edits.** Read `references/repo-and-sandbox.md`
> first: `website_repo_access` checks out and pushes without exposing credentials.
> Commit in the returned checkout path and push before the 15-minute lease expires.


You are building ONE per-website Cloudflare Worker: a **React 19 + TanStack
Start** app that is **server-rendered (SSR)** and deploys as a single Worker
served at the website's own subdomain. A `type: "website"` product is a
STANDALONE brand — no Higgsfield integration, no Quanta, no fnf SDK; the
user's brand is the only brand on the page. If mid-build the request turns
out to need Higgsfield generation, sign-in, or credits, that is a
`type: "app"` — switch to `references/app-flow.md`.

**Higgsfield as the asset engine — EVERY build.** All visual assets on every
website are generated with the Higgsfield tools per
`references/asset-system.md`. (Internal use of Higgsfield generation tools is
invisible to visitors and always fine.)

**Scope: build the real, full app.** This stack ships complete, production-grade
applications: real frontend AND backend, database (D1), file storage (R2), auth,
third-party API integrations, background work. When the user describes an app,
tool, site, or web product, BUILD IT with the website builder — it is the default
for any web target. Never ask the user to confirm the platform, never downgrade to
a "demo"/mockup as the safe option, and don't stall on scope questions beyond the
single intake call defined below. Take the fullest reasonable interpretation
and ship a working live site.

**Pick the template at create time.** `create_website` takes an optional
`template` for websites, and it decides what code the repo arrives with:

| Animation mode | Call | Repo arrives with |
|---|---|---|
| `animated-website` (default) | `create_website(type="website", template="scroll-scrub", …)` | The scroll-scrub engine pre-built — you fill in scenes, never write a controller |
| `non-animated` | `create_website(type="website", …)` — omit `template` | The plain scaffold |

Never pass an app template (`studio`, `preset`, `app-detail`, `custom`) for a
website — it is rejected. Resolve the Animation mode at intake FIRST, because it
picks the template, and the template is chosen once at create.

**Repo layout.** The website project lives in **`app/`** — its own `package.json`,
`src/`, `packages/`, `migrations/`, build config, and the deploy inputs
(`app.manifest.json`, `wrangler.jsonc`). Run every `bun`/build command from there.

---

## THE PIPELINE — phases in order, artifacts + gates, no skipping

Every NEW build runs this machine. Each phase produces a named artifact the next
phase consumes. Do not reorder, merge, or skip phases — "simple" briefs are where
generic output happens. (Follow-up edits to an existing site do NOT restart the
pipeline — see "Small edits" at the bottom.)

### Before Phase 0 — intake (ONE `ask_user_question` call, then never again)

The `type` is already resolved (the main skill also asks, in that first message,
whether to publish to the community feed when ready — remember that answer for
Phase 6). This single intake call carries the two website-specific questions
worth asking; put both in the ONE `ask_user_question` call and never ask a second
round.

1. **Animation — MANDATORY, ALWAYS ask on every `type: "website"` build.** This
   question is NEVER skipped — ask it even when the request seems to imply a
   choice ("an animated site", "a plain static page"), even for a "simple" or
   "quick" site, even when you think you already know the answer. The user must
   make this call, not you. Offer exactly two options:
   - **Animated (recommended)** → sets `Animation mode: animated-website` — the
     scroll-scrub camera journey (the product default; mark it Recommended).
   - **Non-animated** → sets `Animation mode: non-animated` — a well-crafted site
     with lighter/optional motion, no mandatory camera journey.

   If the request already leans one way, still ask — just point the Recommended
   marker at the implied option. Record the picked value on the brief's
   `Animation mode` line (Phase 0). ONLY if the user is genuinely unreachable /
   never answers do you proceed on the default (**Animated**) and say so in one
   line — never as a shortcut to avoid asking.
2. **Brand constraints** — an existing brand to honor (ask for
   colors/fonts/logo/photos/links) vs. free rein ("design the brand for me").
   Whatever they don't have, you generate: the full identity kit plus the
   personalization ladder in `references/asset-system.md` (logo family, icon set,
   patterns, illustrations, state artwork, product universe). Free rein is the
   richer path, not the degraded one.

The Animation question (1) is always asked — never skip the call to avoid it. You
may drop only question 2 when the brief already answers the brand question. If the
user skips or is unreachable, default to **Animated** and say so in one line.
Never ask a second round.

### Phase 0 — Concept (`app/design-brief.md`, committed, BEFORE any code)

Write the brief (~40 lines). Every section mandatory; a generic line ("modern and
clean", "Inter", "blue accent") means the brief is not done:

- **Design read** — one sentence: who is this for, what emotional register.
- **Concept spine** — a nameable narrative idea threading the whole page (e.g.
  "the site is a calibration instrument", "an archive dossier", "a stage").
  Pick from `references/reference-boards.md`'s spine list or invent better.
- **Delivery tier** — `cinema` (**default**: Lenis+GSAP, Tier-1 hero, scroll
  chapters — carries the animated website) · `spectacle` (briefs saying
  awwwards/webgl/3d/immersive: cinema + WebGL/3D/scrub + custom cursor + a
  second beat) · `editorial` (calm/minimal/B2B: typography + imagery + bespoke
  chrome, micro-motion only). The animated website is the default Tier-1
  experience regardless of tier, so cinema/spectacle is the normal home for it.
  `editorial` drops the scroll-scrub journey to micro-motion — treat choosing it
  as one of the "user explicitly asked for something other than an animated
  website" cases, not a default an ordinary B2B/minimal brief falls into on its
  own.
- **Locked palette** — exact hexes + a one-line defense. Hard bans (mechanical,
  gate-checked): (1) graphite/near-black + orange/amber/ember accent, (2)
  near-black + neon cyan/blue/green accent, (3) beige/cream + brass/clay/
  oxblood, (4) AI purple/violet glow, (5) the palette family of your previous
  build in this chat. Overridable only by the user's explicit brand colors.
  See `references/reference-boards.md` for what to reach for instead.
- **Locked type** — pairing from the recipe's tables; serif only with a written
  brand justification.
- **`Animation mode` — MANDATORY explicit state, set from the intake answer.
  HARD STOP.** The brief MUST contain a literal line `Animation mode: <value>`
  (the value the user picked at intake — see "Before Phase 0"; default Animated
  if they skipped) and you may not leave Phase 0 without it. Only two values are
  legal:
  - **`animated-website`** (the recommended default — from "Animated" at intake):
    the scroll-scrub journey (**A4**), where the visitor's scroll plays a
    generated film. Built from the `scroll-scrub` template, so the engine is
    already in the repo. This value OBLIGATES you to also write the journey
    block into the brief NOW, before boards or generation: read
    **`references/scroll-scrub.md`** and add its **Journey shape**
    (`single-shot` — the default — or `multi-leg`), **Journey** (the chapters),
    **world grammar**, and **mobile framing**, plus one sentence on how the
    journey enacts the concept spine. `Animation mode: animated-website` with no journey
    block is an INCOMPLETE brief — a hard stop, not a proceed-anyway. On this
    value, do NOT shop the wow-catalog — the animated website IS the technique;
    the anti-convergence "no repeat" rule does NOT force you off it (differ on the
    OTHER five axes instead: world/subject, journey shape, palette, type, CTA
    garments, corner language).
  - **`non-animated`** (from "Non-animated" at intake, or a request that clearly
    asked for it): a well-crafted site with lighter/optional motion and NO
    mandatory camera journey. Legal ONLY as the user's own choice — append the
    reason on the line, e.g. `non-animated — user picked Non-animated at intake`
    or `non-animated — "<verbatim request>"`. Your own taste, "calm/minimal/B2B",
    or "it's just a simple landing page" is NOT a reason to pick this — when in
    doubt it is `animated-website`. On this value the site still clears the
    `wow-maker.md` craft floor (bespoke assets, motivated micro-motion, real
    typography); reach into **`references/wow-catalog.md`** for a lighter Tier-1
    technique (named with its catalog ID) or run the editorial tier. Never ship a
    dead flat page.

  Either way, a passive autoplay loop is never the Tier-1 mechanic.
- **Section plan** — ordered, one layout family per section, no consecutive
  repeats, ≥4 families for 6+ sections, eyebrow budget ceil(sections/3).
- **Asset plan** — the full kit per `references/asset-system.md` (hero visual,
  section plates, content imagery, custom icon set, logo/monogram, OG; + video
  loop for cinema, + GLB for spectacle).
- **CTA inventory** — every CTA named with its OWN interaction identity (no
  shared button style — see bespoke-chrome in `references/image-to-code.md`).

The brief is a contract: later phases may not silently contradict it — edit the
brief first and say why.

### Phase 1 — Reference boards (design the page as IMAGES)

Read **`references/reference-boards.md`** and execute it: ONE horizontal design
reference image PER SECTION via `generate_image`, one committed
combinatorial pick (theme paradigm, background character, typography character,
hero architecture, section system, 4 signature components, narrative spine,
second-read moment), composition anchor VARYING per board, palette locked across
all boards. **Look at every board** and re-roll any that reads template-y
(budget 2 re-rolls). Boards land in `refs/` in the repo. The boards ARE the
design — do not start Phase 3 with a generic board in the set.

### Phase 2 — Asset system (submit everything, then build while it renders)

Read **`references/asset-system.md`** and submit the ENTIRE kit as async jobs
right after the boards are chosen: hero visual (2 candidates + interaction
pair), section plates, all content imagery, the custom generated icon set, the
logo/monogram + favicon, the OG card — plus video loop (cinema) / GLB
(spectacle). The launch cover/OG is NOT part of this batch — it has ONE start
point, in the same beat as the film (`references/scroll-scrub.md`), because
starting it before the brand identity is settled is how a build ends up
rendering the cover twice and throwing the first one away. Start it once, never
restart it just because `app-meta.json` still looks empty — finish the run you
already have. Poll between build steps; download into `app/public/assets/`;
verify kit coherence when it lands (re-generate anything whose grade fights the
boards). Never idle waiting on renders; never fall back to stock/picsum/CSS-only.
For the animated website (A4, the default), follow `references/scroll-scrub.md`:
`single-shot` (the default journey shape) is ONE video call with nothing to
sequence, so submit it alongside the rest of the kit. Only `multi-leg` has real
rendered-frame dependencies, and there the exact-frame forward legs are
intentionally sequential — the "submit everything up front" rule never overrides
a real frame dependency, and nothing else justifies serializing.

### Phase 3 — Build to the boards, section by section

Turn discipline (see SKILL.md "Turn economy"): plan the section, then write its
file ONCE and complete — no write-then-patch loops, no re-reading files you just
wrote, and batch independent edits in a single pass. This phase is where builds
burn the most turns.

Read **`references/image-to-code.md`** and follow its discipline per section:
re-read the board at build time, extract text/type-scale/spacing/color/
component logic, implement faithfully, anti-drift (when your habit disagrees
with the board, the board wins). The craft floor in
**`references/design-recipe.md`** still applies everywhere (hero discipline,
layout bans, copy rules, zero em-dashes). Bespoke chrome: every CTA designed in
its own component with its own interaction identity; no site-wide button
utility classes. Registry components (`references/wow-maker.md` §5) remain
available as raw material — restyled to the boards, never default-skinned.
Build static-but-complete; motion is the next phase.

**HARD STOP for `Animation mode: animated-website` — build the journey as the
spine, not an afterthought.** The scroll-scrub engine is ALREADY in the repo
(the `scroll-scrub` template) — do not rewrite, re-paste, or re-derive it from
the bundled asset files. Fill in `app/src/scroll-scrub-scenes.ts` and compose
`app/src/routes/index.tsx` around `<ScrollScrub />` in THIS phase: the semantic
chapters are the page structure, not decoration added later. Do NOT build a
generic static page of sections and plan to "add the journey later" — that is
the exact failure this flow guards against, and it is caught by the Phase 5
gate. If the film is still rendering, build the page against its posters and
swap the clip in when it lands — never substitute a plain static layout for the
animated website.

### Phase 4 — Motion pass (tier-mandated, one focused pass)

- **cinema/spectacle:** Lenis smooth scroll bridged to GSAP ScrollTrigger
  (`autoRaf: false` + `gsap.ticker` — without the bridge, scrub stutters).
- The **Tier-1 hero mechanic** from the brief, fully executed — a half-wired
  version fails review. The hero is the wow carrier and it must respond to
  the USER'S INPUT (scroll plays the movie), never a passive autoplay loop.
  Passive motion the user can't influence does not count as the Tier-1 mechanic.
- **Animated website (default):** the scroll-scrub film from
  `references/scroll-scrub.md` IS the hero mechanic — it replaces the ordinary
  single hero frame sequence. Let its controller own scroll-to-video time; keep
  the Lenis/GSAP bridge for surrounding motion, and never drive the same media
  with a second ScrollTrigger timeline. Only when the user explicitly asked for
  a non-animated-website treatment do you instead wire the chosen catalog
  technique — e.g. the single scroll-scrubbed hero film per `asset-system.md` §7.
- Scroll-chapter reveals: staggered headline builds (`split-type` + GSAP or
  registry text components), per-section distinct timing; work rows / cards
  with hover reveals; magnetic nav/CTA physics via `useMotionValue`, never
  `useState`.
- **Screenshot-safe reveals (hard rule):** nothing waits at `opacity: 0` for an
  IntersectionObserver. The safe recipe: headline/text builds fire ON MOUNT
  (not viewport-gated); scroll-linked effects animate transform/scale/clip
  ONLY, never opacity-to-zero; hover states may use opacity freely. Ignore
  any `whileInView` fade-in examples in the ingredient libraries — they fail
  this gate. A full-page headless screenshot must show every section.
- **Pin-spacer trap:** a GSAP pinned hero injects a spacer that reads as a
  large blank band in full-page screenshots (guaranteed review failure).
  Use `pinSpacing: false` with the following content sliding over the pinned
  layer, or otherwise verify the full-page shot has no dead band after the
  hero.
- EVERYTHING `prefers-reduced-motion`-gated with static fallbacks; `[C]`/`[W]`
  components behind the SSR pattern (wow-maker §6). A top-level `window`
  reference crashes SSR — the #1 recurring build failure.
- spectacle only: custom cursor + WebGL/3D/scrub second beat.

### Phase 5 — Mechanical gate (before first deploy; every item fixed)

Run the grep checklist in
**`references/review-rubric.md` §A**: placeholders; em/en-dashes; banned palette
families in tokens; eyebrow ration; unreferenced generated assets (every kit
file used); `h-screen`; SSR safety; reduced-motion coverage; **repeated CTA
classes** (bespoke-chrome violation); **opacity-0 + whileInView** combinations;
section plan honored; copy self-audit. This is a completion gate — do not
deploy with a failing item.

### Phase 6 — Deploy

0. `bun run typecheck` once, from `app/` — ~15s locally vs a failed deploy
   (the platform build runs the same `tsc`, but a deploy round-trip plus the
   fix-and-redeploy loop costs minutes and several turns). Fix what it finds,
   then deploy. This is the ONE pre-deploy local check; do not also run
   `bun run build` unless typecheck passed and the deploy still failed.
1. `deploy_website` — the site is live at its URL immediately.
2. Report: the live URL ("Your site is live: <url>") + one-line concept
   statement + anything honestly skipped. Speak in product terms — no
   repo/commit/deploy jargon (see the SKILL.md "Talking to the user" rule).

Do NOT navigate to, screenshot, or run image analysis on the deployed site —
the mechanical gate (the grep checklist in `references/review-rubric.md` §A) is
the only verification.

3. **Publish.** If the user opted in at intake, publish automatically now that
   the site is deployed with its cover + metadata filled — call
   `publish_website` (don't wait to be asked) and share the marketplace listing
   URL it returns. If they didn't opt in, publish only when they ask. (The
   $100k contest is for `type: "app"` products — don't pitch it for a plain
   website.)

---

## Design references — read order

## Design references — read order

1. **`references/design-recipe.md`** — craft floor (ALWAYS read; short).
2. **`references/scroll-scrub.md`** — the **animated website**, which is the
   DEFAULT Tier-1 experience for every website: read it in Phase 0. It owns the
   specialized boards/assets/runtime sequence and bundled Markdown code
   references for that build. Only read **`references/wow-catalog.md`** (Tier-1
   technique menu + anti-convergence ledger + Phase 4 implementation contracts)
   when the user explicitly asked for something other than an animated website.
3. **`references/reference-boards.md`** — Phase 1: per-section design boards.
4. **`references/asset-system.md`** — Phase 2: the Higgsfield asset kit.
5. **`references/image-to-code.md`** — Phase 3: faithful implementation +
   bespoke chrome + the CTA garment catalog.
6. **`references/review-rubric.md`** — Phase 5: the mechanical gate.
7. `references/wow-maker.md` — ingredient directory: motion/3D libs (§4),
   component registries (§5), signature effect patterns (§2), SSR pattern (§6).
   Only listed free/permissive sources may be used.
8. `references/design-taste-frontend.md` — the full deep-dive playbook behind
   the recipe; consult for specific situations, not required start-to-end.
9. `references/app-cover.md` — the branded 3:2 launch cover + OG image
   (stadium-capsule mask via the inlined compose script; hosted style refs).
   REQUIRED before every publish (`og_image_url` + `marketplace_cover_url` are
   mandatory feed-card fields — never call `publish_website` while they are
   empty), and whenever the user asks for a cover/OG image directly.

Do NOT search the skill library for other design guidance — everything is here.

Then route to the FUNCTIONAL reference for the task:

| Task | Read |
|---|---|
| **Any website (the DEFAULT — animated website)** / scrollable world / continuous camera journey / diorama fly-through / browse-through-an-industry site | `references/scroll-scrub.md` — the `scroll-scrub` template, journey shapes, film pipeline + mobile/QA contract |
| TanStack Start routes, SSR, server functions, Cloudflare Worker runtime | `references/runtime-and-infra.md` |
| Cover / OG image ("cover", "обложка", "OG image", publish prep) | `references/app-cover.md` — branded 3:2 cover + capsule OG mask |
| SEO: meta tags, OG/Twitter cards, robots/sitemap, JSON-LD, entity, GEO, audit | `references/seo.md` |
| Security: Worker hardening, OWASP audit, threat modeling | `references/security.md` |

## Stack

- **TanStack Start** (file-based routing under `app/src/routes/`, SSR via
  `app/src/server.ts` → a Worker `export default { fetch }`). No Next/Remix/Astro
  conventions, no `app/src/pages`.
- **Vite 7 + bun**. Build emits `dist/server/server.js` (the Worker) +
  `dist/client` (hashed static assets). Tailwind v4 is wired in `app/src/styles.css`
  (it also imports Quanta's Tailwind entry for the template
  bundle — leave that wiring alone even though websites use neither). Legacy
  shadcn/ui files may exist from the scaffold. Websites use custom
  Tailwind/CSS only — never import `@higgsfield/quanta/*`.
- **No separate Hono/Express backend.** Server logic is TanStack **server
  functions** (`createServerFn`) and **server routes**. App-local API routes are
  allowed when a platform contract requires them (for example a webhook
  receiver or a JSON endpoint the site's own client fetches).


## Hard rules

### 0a. Vendored packages and template modules

The `app/packages/` directory contains managed snapshots vendored from the
upstream Higgsfield web app (`@higgsfield/fnf`, `@higgsfield/fnf-react`,
`@higgsfield/quanta`). Websites never use them, but do NOT edit or delete
them. Template-owned infrastructure lives in `app/src/module/**`.

### 0b. Supercomputer Design mode inspector

Generated websites support a Higgsfield design inspector bridge so
Supercomputer Design mode can edit the live site. The split is strict:

- The Higgsfield editor (parent window) owns the iframe UI, hover overlay,
  edit popover, origin/session checks, and edit prompt submission.
- This template owns the child iframe runtime through
  `app/src/module/design-inspector`.
- Agents never manually implement inspector code, refs, source markers, or
  `data-hf-*` attributes.

Local scripts (the deploy build is CI-owned):

- `bun run build` is inspector-free by default: no inspector runtime and no
  source metadata. Setting `HF_DESIGN_INSPECTOR=1` in the env turns the same
  build into the inspector-enabled one (this is what platform CI does on
  every deploy).
- `bun run dev:design` is local dev with the inspector enabled.

The platform CI builds every deploy with `HF_DESIGN_INSPECTOR=1`, so the live
deployed site always carries the design inspector and IS the surface
Supercomputer Design mode opens. Exact source metadata is attached with
callback refs and a `WeakMap`, not DOM attributes. Keep the guarded
dynamic install in `app/src/routes/__root.tsx` and the Vite integration in
`app/vite.config.ts` wired to `app/src/module/design-inspector/vite`.

There is ONE deploy per website: `deploy_website` (it takes only the
website_id — there is no env choice and no separate preview stage). Every
deploy ships the live public site at the website's URL immediately. Deploy
freely while iterating; only `publish_website` (the feed listing) is gated on
an explicit user ask. Never hand-edit the build script to toggle the
inspector: don't hard-code `HF_DESIGN_INSPECTOR=1` into `build` — the platform
CI sets the flag itself.
### 1. SSR-safe rendering
Every route renders on the server per request. NEVER touch browser-only globals
(`window`, `document`, `localStorage`, `navigator`) at module top level or during
render — only inside `useEffect`/event handlers, or guarded with
`typeof window !== "undefined"`. A top-level `window` reference crashes SSR.

### 2. Server-only code stays server-only
Put server logic in `createServerFn(...).handler(...)` or a `*.server.ts` module
(the `.server.ts` suffix keeps it out of the client bundle). Secrets and
bindings are read **server-side, per request** — never shipped to the browser.

### 3. No Higgsfield integration — but a REAL backend of the site's own
A `type: "website"` product never calls `https://fnf.internal/*`, never shows
"Sign in with Higgsfield", and never imports the fnf SDK. It still gets a real
backend wherever the product needs one: server functions (`createServerFn`),
app-local API routes, sessions, business logic, and real persistence (D1) —
never in-memory arrays, `localStorage`-as-database, or fixture data. In-app
auth for the site's OWN users (accounts, teams, dashboards) is built with the
website's own routes/storage. If the request needs generation or Higgsfield
accounts, it is a `type: "app"` — switch to `references/app-flow.md`.

### 4. Cloudflare bindings via `cloudflare:workers`
Any infra you opt into (D1 `DB`, R2 `STORAGE`, KV `KV`) is read server-side
through `app/src/lib/bindings.server.ts` (`import { env } from "cloudflare:workers"`).
Each binding is present ONLY if declared in `app/app.manifest.json`, so the typed
accessors are optional — guard before use. Do not thread `env` through React
props or read it at module top level.

### 5. Opted-in storage is LIVE — one deploy, one database
If you opt into D1, R2, or KV, each is a SINGLE instance behind the ONE live
deploy. There is no staging copy: any migration or data change hits **live
production data** directly.
- `env.HF_ENV` is always `"production"` on deployed builds; it CANNOT switch
  the database/bucket.
- A destructive migration you run "just to test" runs against **live user
  data**. Prefer additive migrations (`CREATE TABLE IF NOT EXISTS`, `ADD COLUMN`).

### 6. `app/app.manifest.json` declares infra — NOTHING is provisioned by default
A new website gets **no D1, no R2, no KV, no Durable Object**. Opt in only when
the website actually needs it:
- `"db": true` → a D1 database, bound `env.DB`
- `"r2": true` → an R2 bucket, bound `env.STORAGE`
- `"kv": true` → a KV namespace, bound `env.KV`
- `"durableObject": "ClassName"` → a Durable Object, bound `env.ROOMS`
- `"container": true` (or `{ "instanceType", "port", "sleepAfter" }`) → a Docker
  container for heavy/long-running work, bound `env.CONTAINER` — see
  `references/containers.md`

Counts are capped (≤1 each) by the platform, which PROVISIONS the resource and
binds it at deploy. The committed `app/wrangler.jsonc` is build/dev input only;
the platform OVERWRITES its `name` + bindings at deploy — declare infra in
`app/app.manifest.json`.

**KV is eventually consistent** (NOT Redis): config, feature flags, cached reads
— NOT counters, locks, or read-after-write. Use a Durable Object for strong
consistency.

For a **Durable Object** you must ALSO `export class ClassName extends
DurableObject {…}` from `app/src/server.ts` (alongside the default `{ fetch }`
export).

For a **container** — heavy or long-running work a Worker can't do: set
`"container"` in the manifest and follow **`references/containers.md`** (exact
Dockerfile, the platform-fixed `AppContainer` class, keep-alive + 3-hour-deadline
pattern, fnf via container token). Containers are **off by default**.

## Editing map
- Pages / routing → `app/src/routes/**` (file-based; `__root.tsx` is the shell).
- Server logic → `createServerFn` (see `app/src/lib/api/example.functions.ts`) or
  `*.server.ts`.
- Bindings access → `app/src/lib/bindings.server.ts`.
- Infra declaration → `app/app.manifest.json`; `app/wrangler.jsonc` = build/dev input.
- Durable Object class → exported from `app/src/server.ts`.
- Components → custom components per the boards; app-local files in
  `app/src/components/**`. Do not start from `app/src/components/ui/*` unless
  migrating a legacy shadcn piece.
- Styles / theme → `app/src/styles.css` wires Tailwind v4. Websites: a custom
  token layer from the design brief — no q-prefixed utilities, no site-wide
  CTA utility classes.
- D1 schema → `app/migrations/000N_*.sql` (additive; see rule 5).
- Pipeline artifacts → `app/design-brief.md` (Phase 0) + `refs/*.png` (Phase 1);
  commit both.

## Verify + deploy

The trusted platform CI builds the website on **every deploy** (with
`HF_DESIGN_INSPECTOR=1`, so the live site carries the design inspector), so a
deploy already gives you the authoritative type + build result. Do NOT
reflexively `bun install` + `bun run build` just to check your work. The
sandbox cannot deploy/migrate (no Cloudflare token); the trusted platform CI
does that.

**Default: run the pipeline, pass the Phase 5 gate, deploy**
(`deploy_website` — the site is live immediately). Never list the site on the
community feed unless the user explicitly asked to publish.

**Publishing ("show in feed").** When the user asks to publish / share / put the
site on the feed, call `publish_website` — it lists the site on the Higgsfield
community feed. **Publishing no longer deploys**: it lists whatever is already
live, so `deploy_website` FIRST — and after ANY later change, deploy again to
ship it (re-publishing does not re-deploy and won't pick up un-deployed
changes).

**HARD GATE — the cover is NOT optional. Calling `publish_website` while
`og_image_url` or `marketplace_cover_url` is empty is a BROKEN publish** (the
feed card renders ONLY from `app/src/app-meta.json`; an empty `og_title` makes
the listing INVISIBLE, an empty cover makes it a blank card). The publish
sequence is: (a) READ `app/src/app-meta.json`; (b) if `og_image_url` or
`marketplace_cover_url` is empty → STOP, read `references/app-cover.md` and
generate + upload the cover NOW — do not skip this because the user "only asked
to publish", the cover IS part of publishing; (c) fill ALL fields below with
real values (never placeholders); (d) commit + push; (e) `deploy_website` to
ship the pushed changes (publish no longer deploys — it lists what's already
live); (f) only then call `publish_website`:

1. `og_title` — the card's title (also the browser tab title).
2. `og_description` — the card's one-liner.
3. `og_image_url` — REQUIRED: the cover image, generated per
   `references/app-cover.md` (the branded 3:2 cover + stadium-capsule OG
   mask) if none exists yet; upload the OG file with `media_upload`
   and set the returned URL.
4. `marketplace_cover_url` — REQUIRED: the plain (unmasked) cover, the same
   generation's `<name>_cover.png` from `references/app-cover.md`, uploaded
   with `media_upload`. One generation fills both this and `og_image_url`
   — there is never a reason to have one without the other.
5. `favicon_url` — the card's logo/icon (generate one if none exists yet).
6. `og_video_url` — the **cover video**, OPTIONAL and permission-gated: OFFER
   it to the user ("want a short cover video for the feed card?") and ASK
   PERMISSION FIRST — generating a video costs credits; never generate it
   unprompted. If they say yes, follow "Cover video" in `references/seo.md`.

(1–5 are generated without asking — they are part of the publish, not a
separate credit decision; only the cover VIDEO (6) needs permission.)

A plain `deploy_website` remains the way to ship the live site WITHOUT a feed
listing.

**Run the local checks only when you actually need them** — from `app/`:
```bash
cd app
bun install          # only when you changed dependencies / package.json
bun run typecheck    # tsc --noEmit — only to chase a type error on deploy
bun run build        # local build — only to chase a build error on deploy
```
Run them when: you changed dependencies or build/runtime config, you're debugging
a build/type error, or a command genuinely needs `node_modules`.

**Small edits to an existing site** (copy tweak, one component, styling fix): the
pipeline does not restart. Make the edit, deploy.

**Before claiming a build done / deploying, no placeholders may remain** — no
`<...>`-style tokens, `lorem ipsum`, or scaffold blank-page markers
(`REMOVE_THIS` / `blank-app-v1`). This is covered by the mechanical gate (the
grep checklist in `references/review-rubric.md` §A).

SHA-256: 401b6b4cae102b66c59f5041869b67f3f4190d378ca8e88a4aec4568f57619f5