PORTAL / LIBRARY / landing

[ LANDING & WEB ]

/landing

Build a brand-accurate, direct-response landing page for a ACME Agency client on the Paradox Landing System (Cloudflare Pages + shared Supabase + edge A/B + the admin console).

Download the skill file (.md)

Placeholders like ACME Agency, <id> and you@example.com mark values that are per-agency — your install fills them with YOUR clients and accounts. If a section references a helper script you don't have yet, it ships with that workflow's install.

/landing — brand-accurate direct-response landers on the Paradox Landing System

Build client landing pages in our own stack (NOT Lovable, NOT the /website SiteGround flow). Output is a direct-response landing page — one offer, one CTA, built to convert paid traffic — deployed to Cloudflare Pages, capturing leads through the shared Supabase + submit-lead pipeline, A/B-tested at the edge, and managed from the console (your-domain.example). System lives in shared/landing/ (read shared/landing/README.md).

This is the BUILD step. The resolver, submit-lead, deploy tooling, and console already exist — this skill makes the pages themselves good: on-brand and direct-response.


The two hard rules

  1. Brand FIRST — never invent colors. Read the client's real brand before designing.

(Incident 2026-06-07: a lander was built in generic teal when ACME Agency's brand is amber gold #D59C44 — because the brand doc wasn't read. Don't repeat.)

  1. Direct-response, not a brand website. One offer, one job, one CTA, no nav bar.

Lead with the offer. Message-match the ad. Specific > pretty.


Step 0 — Apply learned rules (read FIRST, every build)

Read .claude/skills/landing/LEARNED_RULES.md and satisfy every rule before you write any markup. Each rule is a mistake a human already had to fix on a past lander (mobile cramming, iOS blank boxes, hero copy that ran long, conflict markers shipped live, lead-forwarding dropped on redeploy). Applying them up front is the whole point of the self-improvement loop — the file grows as the team edits real landers (shared/self_improve/engine.mjs proposes new rules from audit_log edits; a human approves them in). If a rule ever conflicts with an explicit client brief, the brief wins.


Step 1 — Resolve the client + brand (do this before any design)

  1. ACME Agency/clients/clients.json → the client entry: drive_folder_id,

reference_assets (logo/doctor/photo Drive URLs), website, ghl_location_id, ghl_api_key_env, slack_channel, vertical.

  1. ACME Agency/clients/<Client>/brand-dna.md — the canonical brand doc: exact hex

colors, fonts, logo, photography direction, voice, and the real offer/USP. Read it fully. (Also check the client's Drive folder for a cached brand-dna.md.)

  1. Pull real assets into the project: copy the local logo.svg if present; download key

photos (curl -sL "https://drive.google.com/uc?export=download&id=<id>" -o ...), resize big ones with sharp. Use the client's OWN logo + photos, not stock.

If NO brand exists for the client → define it the /static-ad-generator Phase-1 way: scrape the live site with Firecrawl + read a Playwright screenshot with vision for the EXACT hex colors/fonts, inspect their Drive designs, write brand-dna.md locally + to Drive. Confirm the palette with the user before building. (/reference-ads is the higher-fidelity path when the client has strong existing designs.)

Step 2 — Lock the offer + copy (direct-response)

Identify the offer (from brand-dna.md "Product/Service Details" or the user's brief). If the user supplied copy → use it verbatim (HR/BS: keep š/ć/č/ž/đ + emojis, see ACME Agency/CLAUDE.md). If not → write it (or call the copywriter agent) to the DR blueprint below. Apply the Hormozi value equation to make the offer a no-brainer: Value = (Dream outcome × Perceived likelihood) ÷ (Time delay × Effort) — maximize the top (result + proof + guarantee), minimize the bottom (speed + done-for-you).

DR landing-page section order (the blueprint):

  1. Hook headline (above fold) — echoes the ad's promise; specific result + number/timeframe.
  2. Subheadline — who it's for + mechanism/timeframe.
  3. CTA + form above the fold — minimal fields (name + phone; email optional). Benefit-led button.
  4. Pain agitation — name the problem in the prospect's words.
  5. Mechanism — the named "why this works / why now" (one per page).
  6. Offer / value stack — components, each with its benefit; lead with the dream outcome.
  7. Proof — testimonials, numbers, authority (doctor/founder), ratings — near the CTA.
  8. Risk reversal — "first consult free / no obligation".
  9. Urgency — real scarcity only.
  10. FAQ — 4-6, each an objection answered.
  11. Repeat CTA band.

DR rules: one CTA repeated, no nav bar / no outbound links, lead with the offer, message-match the traffic source, specificity over fluff, emotion-first then proof, conversational HR/BS (no AI-tells, no em-dashes, read-aloud test).

Step 3 — Design within the brand

node .claude/skills/ui-ux-pro-max/scripts/search.mjs "landing page <vertical> <tone>" --domain landing

fonts, and logo to the client's brand-dna.md** (brand fidelity beats novelty here).

(posts to /api/submit-lead with variant_path + tracking), assets/img/, v1/index.html (control), v2/index.html (variant), root index.html (= control, fail-safe), functions/ (synced from shared/landing/functions/ by the deploy script).

(probe their live site for the actual @font-face files + computed font-family, download the .ttf/.woff2 into assets/fonts/, declare + preload them, point --serif/--sans at them) and use their EXACT palette. Never ship a free look-alike serif — the client catches it on sight. See LEARNED_RULES.md [brand].

/v1 (and /v2) and read the PNG with vision; fix until it's genuinely on-brand. Don't guess.

websites/<dir> — it audits every vN/` at 390px (no horizontal scroll/overflow, tap targets ≥44px, text ≥13px, section padding). It exits non-zero on any hard failure; fix until CLEAN. Every new variant must pass it too. This is the standing "pixel-perfect mobile" standard.

Step 4 — Deploy + register

Check agency-os_SANDBOX first — it decides whether you deploy directly or enqueue.

Sandboxed colleague session (agency-os_SANDBOX=1) — NEVER a proposal

You can't push or create CF/Supabase resources from a sandbox, so hand the deploy to the privileged runner (cron as faris). For a brand-new lander, enqueue a new-client request — it seeds Supabase + creates the CF Pages project + deploys + pushes, live in ~1 min:

node shared/landing/scripts/<id>.mjs \
  --slug <kebab> --name "<Display Name>" --project <cf-project> \
  --site-dir websites/<dir> --page-name "<A/B test name>" \
  --v1-name "Control" --v2-name "<variant hook>" [--split 50] [--ghl-webhook <url>]

It embeds every file under --site-dir, so the runner gets your exact source. Do NOT file an improvement proposal for a landing build — this path exists precisely to remove that wait. (Changing an EXISTING lander is /landing-edit, not this skill.)

Direct (Faris / non-sandbox) — seed then deploy

# new client: create the Supabase clients/pages/variants rows (generic, idempotent)
node shared/landing/scripts/seed_client.mjs \
  --slug <slug> --name "<Name>" --root-domain <cf-project>.pages.dev \
  --page-name "<A/B test name>" --v1-name "Control" --v2-name "<variant hook>"
# then deploy: functions + pages, CF env (Supabase + CLIENT_SLUG [+ GHL]), writes live_url, pushes source
node shared/landing/scripts/deploy_client.mjs --site websites/<dir> --project <cf-project> --client-slug <slug> [--ghl-webhook <url> | --ghl-key-env GHL_<CLIENT>_API_KEY]

GHL_WEBHOOK_URL binding, or paste it in the console's client view). API-key fallback exists. Leave it off during smoke tests so you never write to a client's live CRM uninvited.

in Cloudflare Pages + have them add the CNAME.

form submits, screenshot loads) all count as visits/leads. Before the client sees the dashboard or real ads go live, wipe them so the client starts at zero — only real traffic counts: node shared/landing/scripts/reset_stats.mjs <slug> (or the console's Reset button). A new client must NEVER carry our test data. (Incident 2026-06-09: ACME Agency showed 109 test visits in the console on first view.)

When to trigger

Guardrails