Before you run this skill
This skill is brand-neutral. It reads its brand, palette and endpoints from
brand.config.json at the repo root.
On first use, do this before anything else:
- Run
python3 brandkit.py. It prints the config source and any placeholder
that is still unset.
- If it says
configured: False, copy brand.config.example.json to
brand.config.json.
- Ask the operator for each value under
missing, then write them in. Do not
guess a brand name, a domain, or a colour.
- Anything the skill writes out should be passed through
brandkit.fill(text), which swaps every {{TOKEN}} for its configured value
and remaps the default palette to the operator's.
Text below uses {{TOKEN}} where a value is operator-specific. Treat an
unresolved {{TOKEN}} in your output as a bug, not as literal copy.
Publishing: which CMS
Never write CMS calls by hand in this skill. Use the shared adapter layer, which
covers Strapi, WordPress, Contentful, Sanity, Ghost, Webflow, Payload, Directus,
and plain files.
from cms import get_adapter, Page, CMSError
cms = get_adapter() # reads cms.kind from brand.config.json
page = Page(slug=slug, title=title, description=meta_desc,
html=body_html, blocks=components, jsonld=graph)
entry_id = cms.upsert(page) # ALWAYS a draft, whatever the CMS
admin, public = cms.locate(page, entry_id)
Rules:
upsert() creates a draft. There is no way to publish through it, by design.
cms.publish(page) goes live. Call it only after the operator says so, for
this page, in this session. Approval never carries forward.
- If
cms.kind is none, get_adapter() raises. Report that and offer files,
which writes the page to disk instead, rather than guessing a CMS.
python3 -c "import cms; print(cms.describe())" tells the operator what is
wired and whether that adapter has been verified live.
- Only
strapi and files are verified against real instances. For the rest,
build the page, run the audit, then tell the operator to expect a possible
field-name mismatch on the first push.
{{BRAND_NAME}} Product Page Builder (SERP + LLM aware)
Builds a {{BRAND_NAME}} product landing page (typically /<keyword>-generator/, /<keyword>-builder/, /<keyword>-creator/, or feature-page) end-to-end with mandatory Phase 0 SERP + LLM ranking-factor recon before a single line of copy is written. Every ranking factor from the top-10 competitor pages gets identified, then either matched or exceeded. Every top LLM-cited domain gets checked so we know what pattern we're competing against.
The skill produces a pages entry that mirrors the /ai-form-generator/ design exactly: 6 macro components + 6 raw-html micro components + 1 raw-html tail (13 slots total). Same rendering pipeline, same visual system, zero drift.
Reference implementation: consent-form-generator (page id 898, shipped 2026-08-03). When in doubt about a macro shape or a raw-html section, fetch that page's components array and copy the pattern.
Rule 0 — Inputs (ask one at a time, echo back, confirm)
- Primary keyword — the exact search phrase (US). Example:
consent form generator, photo release form maker.
- Slug — kebab-case URL path, no leading slash. Example:
consent-form-generator.
- Page-type verb — one of
generator, builder, creator, maker. Sets the tone across the 13 components.
- Secondary keywords — 4-6 phrases. Includes at least one AI variant (
ai <primary>), one free variant (free <primary>), and one output-format variant (<primary> pdf or similar) when applicable.
- Regulated-industry framing — one of:
explicit-not-hipaa-with-routing — name {{BRAND_NAME}} as not HIPAA-certified and route to Jotform Gold / Cognito Enterprise in the Clinicians / Healthcare persona (recommended for any consent / clinical / medical topic)
compact-caveat — one-sentence non-HIPAA note, no competitor routing
not-applicable — the topic is not health/consent-adjacent (event, contact, feedback, quiz, etc.) — skip HIPAA copy entirely
- Companion how-to blog (optional) — the source tracker often has an adjacent blog row (
how to create a <keyword>). Offer to ship both landing + blog as a pair. Default: no.
After all six, echo the block back and wait for explicit confirmation before Phase 0.
Rule 1 — Phase 0 SERP + LLM recon (mandatory, BEFORE any content build)
Do NOT write copy or build components until this phase is complete and the ranking-factor synthesis is presented to the user.
1a — Google SERP top-10 (DataForSEO Live SERP)
Direct HTTP call — DataForSEO MCP tools may not be loaded. Load credentials from .env:
import base64, ssl, json, urllib.request
ctx = ssl.create_default_context(); ctx.check_hostname=False; ctx.verify_mode=ssl.CERT_NONE
auth = base64.b64encode(f"{DFS_LOGIN}:{DFS_PASS}".encode()).decode()
payload = json.dumps([{
"keyword": PRIMARY_KEYWORD,
"location_name": "United States", "language_code": "en",
"device": "desktop", "depth": 10,
}]).encode()
req = urllib.request.Request(
"https://api.dataforseo.com/v3/serp/google/organic/live/advanced",
data=payload, method="POST",
headers={"Authorization": f"Basic {auth}", "Content-Type": "application/json"}
)
Extract from response:
- Top 10 organic — URL, domain, title, rank
- SERP features — AI Overview present? PAA questions? Video pack? Related searches? Discussions & Forums block?
- Related searches — often reveal the highest-intent long-tail variants (
<primary> free, <primary> pdf, <primary> for research, etc.)
1b — Firecrawl the top 5-6 commercial competitors
Skip academic/IRB .edu results unless they represent a real audience {{BRAND_NAME}} wants. Extract per competitor:
- Word count, H1/H2/H3 tree
- Tools mentioned (which entities they name)
- Compare / benchmark: which pages ARE listicles vs product pages vs template galleries?
- Any structural pattern the top 3 share that {{BRAND_NAME}}'s default set doesn't have
1c — LLM citation probe (OpenAI gpt-4o-search-preview × 4 queries)
payload = json.dumps({
"model": "gpt-4o-search-preview",
"messages": [{"role": "user", "content": QUERY}],
}).encode()
Run 4 query variants covering informational / free / format-specific / use-case intents. Parse choices[0].message.annotations[].url_citation for cited domains. Record:
- Does {{BRAND_NAME}} get cited (URL or in-answer name)?
- Which competitor domains get cited most?
- What criteria does the LLM emphasize (unlimited free, AI generation, PDF export, e-signature legality, HIPAA scope, etc.)?
1d — Ranking-factor synthesis (present to user, wait for approval)
Produce a compact report before touching components:
- Top 10 organic domain list with type (listicle / product page / template gallery / academic / brand)
- SERP features present + PAA questions verbatim
- Related-search intent list ranked by inferred volume
- Competitor structural pattern (word count, H2/H3 counts, distinctive sections)
- LLM citation status for {{BRAND_NAME}} on this keyword cluster (typically 0/4 on new keywords)
- Ranking factors the user's page will take from top-10 (design patterns + entity coverage + intent match)
- Ranking factors the user's page will add on top ({{BRAND_NAME}} differentiators: unlimited free, AI form gen from PDF, native e-signature, PDF export, GDPR + DPA §4.4, MCP for AI agents, etc.)
- Proposed component-by-component plan (13 slots — see Rule 2)
- Meta title + description proposals
- Regulated-industry framing decision (from Rule 0.5) applied to specific components
Wait for explicit approval before Phase 1.
Rule 2 — Component structure (15 slots, LOCKED — updated 2026-08-09 for legal-form-management-software)
Every product page ships with exactly 15 components in this order. Slot type is fixed. Content varies by keyword.
| # |
Slot |
Component type |
Purpose |
| 0 |
Custom animated hero |
micro-components.raw-html |
Two-column hero: text left (badge / H1 with violet accent / description / CTAs / trust bullets), right = SINGLE panel with two states — State 1 prompt input with 2-line typing animation + Generate button, State 2 generated form preview with 6 fields drawing in. Both states occupy the same absolute-positioned space, crossfade on a 14s loop. Whole State-2 form card is a clickable link to signup. See "Hero pattern" below. |
| 1 |
3-ways |
micro-components.raw-html |
3 cards: Template · Prompt AI · Upload PDF |
| 2 |
Trust seals |
macro-components.trust-seals |
showTrustBadges: False (org logo band only, no 4 rating cards) |
| 3 |
Quality control |
micro-components.raw-html |
5-item bullet list of what makes the output reliable |
| 4 |
How it works |
macro-components.simple-steps-create |
3 numbered steps with title + description each |
| 5 |
See in action (video/GIF grid) |
micro-components.raw-html |
2×2 grid of 4 short GIFs captured from {{APP_HOST}} showing the product in motion. Frame-accurate trim to skip page-load blank frames. <img> tags for GIFs (not <video>) so no autoplay-blank issue. See "GIF grid pattern" below. |
| 6 |
Who uses this |
micro-components.raw-html |
5-8 personas grid (apply HIPAA framing per Rule 0.5) |
| 7 |
API + MCP |
micro-components.raw-html |
2 cards: MCP for AI agents + REST API v2 |
| 8 |
Privacy |
micro-components.raw-html |
DPA §4.4 + GDPR/UK GDPR/CCPA + encryption |
| 9 |
Feature grid (with topic icons) |
micro-components.raw-html |
6 feature cards. Each card has a topic-specific 40×40 icon tile (violet-25 bg + violet-100 border + violet-600 Lucide icon) above the title. NOT the standout-feature-detail macro — that macro's schema doesn't accept icons. See "Icon feature grid pattern" below. |
| 10 |
Testimonial wall |
macro-components.testimonial-wall |
G2 / Trustpilot / Product Hunt / Capterra live reviews (showG2/Trustpilot/ProductHunt/Capterra: True) |
| 11 |
Mid-page CTA |
macro-components.cta |
Existing CTA record ctaValue: 67 (default: "Get access to advanced AI, unlimited forms & more.") |
| 12 |
FAQs |
macro-components.faqs |
9 Q&A pairs, PAA-matched, structured faqList (not raw-html) |
| 13 |
Steps videos (optional) |
micro-components.raw-html |
3-column grid of 3 GIFs — one per simple-step above. Optional; skip if the page doesn't have a natural 3-step flow. |
| 14 |
More {{BRAND_NAME}} tools |
micro-components.raw-html |
4 sibling tools cross-sell grid |
Non-negotiable:
- Slots 2, 4, 10, 11, 12 use macro components — do not replace with raw-html
- All other slots use
micro-components.raw-html with markup field (never body — writing to body results in silently empty components; this bug shipped on the initial cognito-forms-alternative update on 2026-08-03 and was caught in prod)
- Every raw-html component uses
useContainer: false so it renders edge-to-edge like AFG's macros. The site's default container wrapper (useContainer: true) constrains sections to a narrow column and breaks the full-width feel of the AFG-hybrid look. Each raw-html component MUST manage its own inner max-width via a .__container or .__inner element (max-width: 1180px; margin: 0 auto; padding: 0 24px). Verified fix applied on legal-form-management-software (page 903) + form-management-software (page 904) on 2026-08-09.
- Class prefix on raw-html: unique 3-char kebab code per page (e.g.
lfm for legal-form-management-software, fmg for form-management-software)
Hero pattern (slot 0) — reference: legal-form-management-software (page 903)
Full raw-html hero with the two-state animation. Non-negotiables:
- 2-column grid on desktop (
grid-template-columns: 1fr 1fr), stacks to 1-column below 900px
- Top padding 120px on desktop (was 72px — this gap is required to clear the sticky nav)
- Left column: violet-100 badge pill, H1 with
<span class="__accent"> violet on the second half, description paragraph, 2 CTAs (Start free primary + See pricing ghost), trust bullets row
- Right column: single
.__panel with two absolutely-positioned .__state children. State 1 is the prompt view (icon + label header, prompt box with 2-line typing, generate button below). State 2 is the generated form preview.
- Animation loop = 14s. Timing: 0-27% typing, 27-30% button pulse, 30-36% spinner + State 1 fadeout, 36-40% State 2 fadein, 40-92% form fields draw in one by one, 92-100% fade back to State 1
- Typing: two
.__line elements each with white-space: nowrap. Each .__line-fill has its own max-width keyframe animation. Line 1 reveals 0-8.5%, then ~1.5s natural pause, then Line 2 reveals 12-21%. Timing function cubic-bezier(0.22, 1, 0.36, 1) — strong ease-out feels like natural typing that decelerates at word boundaries. Two cursors, one per line, with softer blink (opacity dips to 0.15 not 0)
- Clickable: State-2 form is wrapped in a single
<a href="{{SIGNUP_URL}}"> so the entire card acts as a signup link during the "form visible" phase. Use pointer-events: none in the keyframes for the invisible state so users can't click through
- Legal-page canonical markup: see
output/alt-pages/legal-form-management-software/ OR pull page 903's slot-0 markup fresh (GET /api/pages/903?populate[components][populate]=*&publicationState=preview)
Icon feature grid pattern (slot 9)
Not a macro. Full raw-html. Non-negotiables:
6 feature cards, 3-col desktop / 2-col tablet / 1-col mobile
Each card: 40×40 icon tile at top-left (border-radius 8px, background #f7f3ff violet-25, border 1px solid #e4d7ff violet-100, icon color #6941c6 violet-600)
Icons: Lucide-style, 20×20 viewBox, stroke-width 1.8, currentColor — match the 3-ways and who-uses icon style already used elsewhere on the page. Pick icons that DIRECTLY represent the feature meaning, not decorative filler. Sample mapping used on legal-form-management-software:
| Card |
Lucide-style icon |
| e-signature / signed forms |
Pen writing a signature curve |
| PDF export / document download |
Document with down-arrow |
| Conditional logic / branching |
3 connected circles (branch pattern) |
| Custom domain / branding |
Globe with meridian |
| Team seats / roles |
2 users |
| Integrations |
Puzzle pieces / grid connectors |
| AI form generator |
Sparkles |
| Native payments |
Credit card |
| Offline / PWA |
Download cloud |
| Multi-language |
Globe with language char |
| Privacy / GDPR |
Shield check |
| REST API / MCP |
Code brackets < > |
Card body: title (H3, 18px, weight 600) + description (15px, line-height 1.55, color #475467). No CTA link inside cards.
Hover: border → #9777e0 (violet), shadow lift, translateY(-2px)
GIF grid pattern (slot 5)
Not a macro. Full raw-html. Non-negotiables:
- 2×2 grid on desktop, 1-col below 760px
- 4 clips ONLY. More clips dilute impact.
- Format:
<img> tags pointing at animated GIF files uploaded to your CMS media library — NOT <video> tags. GIFs render instantly with no blank first frame, no autoplay-block issues, no seek-required behavior. Autoplay is guaranteed on every browser.
- File size target: 300-500 KB per GIF, ~1.5MB total for the section
- Capture pipeline (Playwright + ffmpeg):
- Playwright headed, log in with
BRAND_APP_EMAIL + BRAND_APP_PASSWORD from .env, save storage_state.json
- Pre-extract a real form ID from the /forms dashboard (use JS to find
data-form-id, else fall back to creating a Start-From-Scratch form)
- Record each interaction as a WebM (Playwright
record_video_dir context option, viewport 1280×720)
- Convert WebM → GIF with ffmpeg using frame-accurate trim via the
trim=start=X:end=Y,setpts=PTS-STARTPTS filter — NOT -ss before -i which does keyframe-based seek and often produces blank first frames
- GIF settings:
fps=12, scale=800:-1:flags=lanczos, 2-pass palette generation with stats_mode=diff + paletteuse=dither=bayer:bayer_scale=5
- Upload each GIF via
POST /api/upload multipart with Content-Type: image/gif
- Card design: same shape as other card grids — white bg, 1px grey border, 16px border-radius, subtle shadow, violet border on hover. Each card has a 16:10 aspect-ratio
.__frame at top containing the GIF, then title + body below.
- Playwright locator strategy: coordinate clicks work better than text-based locators for Vue/Element-UI dialogs. Inspect the DOM first via a JS eval that dumps
{tag, classes, rect} for candidate elements, then use page.mouse.click(x, y) at rect centers for Vue-tile clicks. Use CSS selectors like button.template-card or button.fmst-btn for real buttons.
- Fallback: if a specific interaction won't fire reliably from Playwright, ship the clip anyway showing the static screen — real product UI beats a fake mock. Note honestly in the report which clips are interactive vs static.
Reference implementation: legal-form-management-software (page 903) shipped 2026-08-09 with 4 GIFs (media ids 4780-4783) covering "Six ways to create a form", "Form builder with live design controls", "Ready-made template library", "Form-to-PDF export".
Rule 3 — Design system (byte-for-byte from ai-form-generator)
The canonical source is /ai-form-generator/ (reference entry). When building a new product page, fetch AFG's 13 components and:
- Macros: mirror the field structure exactly. Only vary
title text parts, description, itemList items, suggestions, footerItems, faqList. Leave showG2/Trustpilot/ProductHunt/Capterra: True and ctaValue: 67 and showTrustBadges as-is.
- Raw-html: copy from
HTML component/consent-form-generator/ (reference build), swap the 3-char prefix (csg → new prefix), rewrite the copy for the new keyword. Do NOT copy from HTML component/contact-form-generator/ — that library is compact and misses some AFG design details. Consent-form-generator is the canonical raw-html reference going forward.
Class prefix substitution: pick a unique 3-char kebab code, do a global csg- → <new-prefix>- replace across all 6 raw-html files.
The 8 canonical topic SVG icons + Lucide shape rules from ~/.claude/skills/_shared/alt_page_design_system.py also apply here — if the who-uses block has icons, they get topic-swapped by apply_alt_page_rules() automatically.
Rule 4 — Voice + accuracy rules (mandatory)
Same as every {{BRAND_NAME}} content skill:
- No em-dashes anywhere in body copy. Preflight blocks publish if any leak in. Applies to macro content too, not just raw-html.
- No hardcoded dates. Every "As of X" resolves to
today() at build time.
- No placeholders. Strip
TODO, FIXME, INSERT_, REPLACE_, PLACEHOLDER before commit.
- No external URLs in FAQ answers. Internal
/features/... links are fine.
- No repeat internal links in body: each internal URL linked at most once outside the more-tools grid.
- Every {{BRAND_NAME}} claim traces to
memory/brand-facts.md. Never invent features. {{BRAND_NAME}} is NOT HIPAA-certified, NOT SOC 2, NOT ISO 27001, and NOT "on the HIPAA roadmap" — banned phrasing.
- User-count is 56,000+ teams unless a newer confirmed number lives in the current conversation.
- Every competitor claim traces to
competitor-research.md. Correct brand casing (Jotform / Google Forms / Typeform / Microsoft Forms / Zoho Forms / Fillout / forms.app / KoboToolbox).
- HIPAA framing (from Rule 0.5):
explicit-not-hipaa-with-routing → the Clinicians / Healthcare persona card names Jotform Gold and Cognito Forms Enterprise as HIPAA-eligible alternatives, positions {{BRAND_NAME}} for non-PHI use only (marketing opt-in, patient education, event RSVPs).
compact-caveat → one-sentence note only ("{{BRAND_NAME}} is not HIPAA-certified. Use for non-PHI intake."), no competitor routing.
not-applicable → no HIPAA mention on the page.
Rule 5 — Google Doc draft (approval gate, mandatory before CMS push)
After the 13 components are built locally, produce a Google Doc for content approval before any CMS write.
Doc structure is LOCKED by the shared standard at ~/.claude/skills/_shared/doc_content_standard.md.
- Metadata table first (Title, Description, Meta title, Meta description, Primary keyword, Primary keyword volume (US), Secondary keywords, Slug, Recommended URL, CMS link). No other rows.
- Body: verbatim copy from the 13 components, in reading order, with explicit
[H1] / [H2] / [H3] heading markers and [Image: ...] placement markers wherever an on-page image will render.
- No commentary, no rationale, no
**Why:** lines, no [REUSE: ...] or [NEW TEMPLATE] annotations, no cover URL or media ids, no schema JSON, no preflight-status footer.
- CTAs render as plain lines:
Button: <label> + Link: <url>.
- Use
~/.claude/skills/gdoc/build_gdoc.py with GDOC_AUTH_DIR={{GOOGLE_AUTH_DIR}} ({{GOOGLE_ACCOUNT}} OAuth, not {{GOOGLE_ACCOUNT}} — {{BRAND_NAME}} project rule).
- Doc lands in the "{{BRAND_NAME}} SEO Audits" Drive folder.
- When updating an existing page, regenerate in place via
--doc-id <existing_id>.
- Share URL with the user. Wait for explicit content approval before touching the CMS.
Content approval unlocks Rule 6.
Rule 6 — CMS push (POST /api/pages, draft state)
After content approval:
Preflight all 6 raw-html components via run_preflight() from ~/.claude/skills/_shared/preflight.py. This runs strip_trailing_periods, fix_button_hover_color, fix_markdown_leakage. Any change gets persisted to disk before push. Mandatory.
Audit-scan all 6 raw-html components via python3 ~/.claude/skills/_shared/audit_scan.py <file>. Exit 1 = do not push, fix and re-run. Catches {{BRAND_NAME}} HIPAA overclaim, "on roadmap" phrasing, em-dashes, hardcoded dates.
Build the payload in this exact shape:
payload = {
"data": {
"slug": SLUG,
"publishedAt": None, # HARD RULE: never publish on POST
"meta": {
"title": META_TITLE, # 30-60 chars, ends with "| {{BRAND_NAME}}"
"description": META_DESCRIPTION, # 120-160 chars
"type": "website",
"url": LIVE_URL,
"keywords": [{"text": kw} for kw in KEYWORDS],
"link": [{"hid": "canonical", "rel": "canonical", "href": LIVE_URL}],
"jsonld": JSONLD_GRAPH, # see Rule 7
},
"components": [
{"__component": "micro-components.raw-html", "markup": CUSTOM_HERO, "useContainer": False}, # slot 0 — custom animated hero
{"__component": "micro-components.raw-html", "markup": ..., "useContainer": True}, # slot 1 3-ways
{"__component": "macro-components.trust-seals", "showTrustBadges": False}, # slot 2 (org logos only)
{"__component": "micro-components.raw-html", "markup": ..., "useContainer": True}, # slot 3 quality
SIMPLE_STEPS, # slot 4 macro (simple-steps-create)
{"__component": "micro-components.raw-html", "markup": GIF_GRID, "useContainer": True}, # slot 5 — See in action GIF grid
{"__component": "micro-components.raw-html", "markup": ..., "useContainer": True}, # slot 6 who-uses
{"__component": "micro-components.raw-html", "markup": ..., "useContainer": True}, # slot 7 api-mcp
{"__component": "micro-components.raw-html", "markup": ..., "useContainer": True}, # slot 8 privacy
{"__component": "micro-components.raw-html", "markup": ICON_FEATURE_GRID, "useContainer": True}, # slot 9 — Icon feature grid (NOT standout-feature macro)
TESTIMONIAL_WALL, # slot 10 macro
{"__component": "macro-components.cta", "ctaValue": 67}, # slot 11 macro
FAQS, # slot 12 macro (faqs)
{"__component": "micro-components.raw-html", "markup": ..., "useContainer": True}, # slot 13 — steps videos (optional)
{"__component": "micro-components.raw-html", "markup": ..., "useContainer": True}, # slot 14 — more {{BRAND_NAME}} tools
],
}
}
POST to {{CMS_BASE_URL}}/api/pages. Use the CMS token from your environment.
Verify via GET /api/pages/<id>?populate[components][populate]=*&populate[meta][populate]=*&publicationState=preview. Confirm 14-15 components with correct types, publishedAt: null, canonical link set, JSON-LD @graph has 5 entities.
Report CMS admin URL to the user. Wait for explicit publish approval.
Rule 7 — 5-entity JSON-LD @graph (mandatory)
Every product page's meta.jsonld['@graph'] must include:
- Organization — {{BRAND_NAME}} (referenced by
@id: {{SITE_URL}}/#organization)
- WebPage — url, name, description,
datePublished = today, dateModified = today, inLanguage: en-US, isPartOf, breadcrumb
- BreadcrumbList — Home → Page name
- FAQPage — 9 verbatim Q&A pairs from the
faqs macro's faqList. Structured as {"@type": "Question", "name": q, "acceptedAnswer": {"@type": "Answer", "text": a}}.
- SoftwareApplication — {{BRAND_NAME}} product entity: name, url,
operatingSystem: "Web, iOS, Android", applicationCategory: "BusinessApplication", offers array (Free at $0, Personal at $13), aggregateRating (4.7/5, 11 reviews on G2).
Dates resolve to today at build time. Never a hardcoded string.
Rule 8 — SEO preflight (blocks publish)
Any FAIL blocks the publish step. Auto-run these checks before flipping publishedAt:
- Meta title 30-60 chars, ends with
| {{BRAND_NAME}}
- Meta description 120-160 chars
- Slug matches URL path
- Exactly 1
<h1> across all components (from the ai-hero macro)
- No em-dashes anywhere in components or macro strings
- No banned placeholders (
TODO, FIXME, INSERT_, REPLACE_, PLACEHOLDER)
- Canonical link set:
meta.link[0].rel == 'canonical'
meta.jsonld['@graph'] has exactly 5 entities (Rule 7)
- FAQPage entity has ≥ 5 Q&A pairs, target 9
publishedAt: null (still a draft)
- Component count = 13 in the exact order from Rule 2
- Every raw-html component uses
markup field (not body) + useContainer: true
run_preflight() + audit_scan.py exit 0
Rule 9 — Publish (only on explicit approval)
Per feedback_never_publish_without_approval.md: never flip publishedAt without explicit user approval on THIS specific page. Prior approvals do not carry forward.
When approved:
- Set
publishedAt to datetime.now(timezone.utc).replace(microsecond=0).isoformat() + 'Z'.
- PUT to
/api/pages/<id>.
- Poll
{{SITE_URL}}/<slug>/ for 200. Nuxt static rebuild takes ~5-15 minutes; 404 in that window is expected.
- Log to Cluster Master Tracker
Published Content Tracker tab if the source row exists.
Rule 10 — Reference implementation
Canonical reference for the 15-slot pattern (updated 2026-08-09): legal-form-management-software (reference entry). Custom animated hero, GIF grid section, icon feature grid.
Prior reference for the base 13-slot AFG-hybrid pattern: consent-form-generator (reference entry).
When in doubt about:
- Macro field shapes → GET
/api/pages/1?populate[components][populate]=*&publicationState=preview (ai-form-generator)
- Raw-html section design → read
HTML component/consent-form-generator/*.html
- Component ordering → GET
/api/pages/898?populate[components][populate]=*&publicationState=preview (consent-form-generator)
- Doc format → open the consent-form-generator doc (in "{{BRAND_NAME}} SEO Audits" Drive folder)
Do NOT reference:
contact-form-generator — this predates the AFG-hybrid architecture; its 10 raw-html structure is deprecated for new builds
ai-form-generator local HTML component/ai-form-generator/*.html files as a raw-html template — those are the OLD (pre-macro) raw-html versions. AFG's LIVE page uses the macros defined in the CMS, not those local files.
Rule 11 — Things to never do
- Never publish without explicit approval on THIS page.
- Never invent {{BRAND_NAME}} features. HIPAA / SOC 2 / ISO 27001 are all NO. Never claim "on the roadmap" for any of these.
- Never use em-dashes.
- Never leave trailing periods on headings, badges, or CTAs.
- Never hardcode dates in generated components — always
today() at build time.
- Never skip the Phase 0 SERP+LLM recon. It's the whole point of this skill vs the older
/landing-page.
- Never skip the Google Doc approval gate.
- Never write to the
body field on micro-components.raw-html — use markup. Writing to body results in silently empty components (Strapi only: it discards unknown fields). This bug shipped on the initial cognito-forms-alternative update 2026-08-03 and was caught in prod.
- Never reuse a class prefix across pages. Each page gets a unique 3-char kebab code.
- Never let the
type field on macro-components.ai-hero be anything except Form, Score Quiz, or Outcome Quiz (the CMS enum). "Consent form" or similar strings will 400 the POST.
No self-referential media captions (HARD RULE, added 2026-08-09)
Never add authenticity remarks under or near screenshots, GIFs, or videos: "Real {{BRAND_NAME}} UI, captured in-app", "actual product screenshot", "real UI, not a mockup", "captured from the live app", or any similar meta-commentary about the asset being real. These read as AI-generated filler and were ordered removed site-wide. The asset speaks for itself. If a media element genuinely needs a caption, the caption states only WHAT the user is looking at (e.g. "Form builder, Design tab"), never that it is real, actual, or captured. This applies to every page type: product, landing, alternative, blog, listicle, review, template.
Inspiration pages are design-only, never fact sources (GLOBAL HARD RULE, added 2026-08-10)
Root cause: the Aug 2026 factual audit found 4 alternative pages shipped with the inspiration page's facts (Wufoo's pricing tiers, ownership, founding year) left in place for different competitors. This must never recur.
- An internal inspiration/reference page may be reused ONLY for: page structure, section structure, layout, design patterns, visual treatment, formatting, component patterns, UX approach.
- NEVER carry over content, claims, facts, statistics, pricing, plan names, limits, ownership, founding dates, quotes, or any information from the inspiration page into a new page.
- Research comes FIRST on every new page: (1) research the subject independently, (2) identify current correct information, (3) verify every factual claim against first-party/authoritative sources (official pricing page, docs, company newsroom), (4) write original content from that research.
- Never assume something is correct because it appears on an existing {{BRAND_NAME}} page. Never fill gaps with guesses. If a claim cannot be verified, flag it explicitly instead of presenting it as fact.
- Cloned template blocks that carry competitor-specific facts (pricing tables, "why teams leave" cards, honest-comparison cards, free-plan tables) must be rebuilt from fresh research for every new subject, not find-and-replaced.
1---2name: product-page3description: Build a {{BRAND_NAME}} product / generator / builder / creator landing page end-to-end, starting with a Phase 0 SERP + LLM ranking-factor recon before any copy is written. Ships as a CMS pages entry that mirrors the ai-form-generator design exactly (6 macro components + 6 raw-html components + 1 raw-html tail — 13 slots total). Trigger with `/product-page` or when the user asks to "build a product page", "build the <keyword> generator page", "build a {{BRAND_NAME}} tool page" and wants SERP + LLM rankings baked into the design.4---5<!-- SETUP:BEGIN -->6## Before you run this skill78This skill is brand-neutral. It reads its brand, palette and endpoints from9`brand.config.json` at the repo root.1011**On first use, do this before anything else:**12131. Run `python3 brandkit.py`. It prints the config source and any placeholder14 that is still unset.152. If it says `configured: False`, copy `brand.config.example.json` to16 `brand.config.json`.173. Ask the operator for each value under `missing`, then write them in. Do not18 guess a brand name, a domain, or a colour.194. Anything the skill writes out should be passed through20 `brandkit.fill(text)`, which swaps every `{{TOKEN}}` for its configured value21 and remaps the default palette to the operator's.2223Text below uses `{{TOKEN}}` where a value is operator-specific. Treat an24unresolved `{{TOKEN}}` in your output as a bug, not as literal copy.2526<!-- SETUP:END -->2728<!-- CMS:BEGIN -->29## Publishing: which CMS3031Never write CMS calls by hand in this skill. Use the shared adapter layer, which32covers Strapi, WordPress, Contentful, Sanity, Ghost, Webflow, Payload, Directus,33and plain files.3435```python36from cms import get_adapter, Page, CMSError3738cms = get_adapter() # reads cms.kind from brand.config.json39page = Page(slug=slug, title=title, description=meta_desc,40 html=body_html, blocks=components, jsonld=graph)4142entry_id = cms.upsert(page) # ALWAYS a draft, whatever the CMS43admin, public = cms.locate(page, entry_id)44```4546Rules:4748- `upsert()` creates a draft. There is no way to publish through it, by design.49- `cms.publish(page)` goes live. Call it **only** after the operator says so, for50 this page, in this session. Approval never carries forward.51- If `cms.kind` is `none`, `get_adapter()` raises. Report that and offer `files`,52 which writes the page to disk instead, rather than guessing a CMS.53- `python3 -c "import cms; print(cms.describe())"` tells the operator what is54 wired and whether that adapter has been verified live.55- Only `strapi` and `files` are verified against real instances. For the rest,56 build the page, run the audit, then tell the operator to expect a possible57 field-name mismatch on the first push.5859<!-- CMS:END -->6061# {{BRAND_NAME}} Product Page Builder (SERP + LLM aware)6263Builds a {{BRAND_NAME}} product landing page (typically `/<keyword>-generator/`, `/<keyword>-builder/`, `/<keyword>-creator/`, or feature-page) end-to-end **with mandatory Phase 0 SERP + LLM ranking-factor recon before a single line of copy is written.** Every ranking factor from the top-10 competitor pages gets identified, then either matched or exceeded. Every top LLM-cited domain gets checked so we know what pattern we're competing against.6465The skill produces a `pages` entry that **mirrors the /ai-form-generator/ design exactly**: 6 macro components + 6 raw-html micro components + 1 raw-html tail (13 slots total). Same rendering pipeline, same visual system, zero drift.6667**Reference implementation:** `consent-form-generator` (page id 898, shipped 2026-08-03). When in doubt about a macro shape or a raw-html section, fetch that page's `components` array and copy the pattern.6869---7071## Rule 0 — Inputs (ask one at a time, echo back, confirm)72731. **Primary keyword** — the exact search phrase (US). Example: `consent form generator`, `photo release form maker`.742. **Slug** — kebab-case URL path, no leading slash. Example: `consent-form-generator`.753. **Page-type verb** — one of `generator`, `builder`, `creator`, `maker`. Sets the tone across the 13 components.764. **Secondary keywords** — 4-6 phrases. Includes at least one AI variant (`ai <primary>`), one free variant (`free <primary>`), and one output-format variant (`<primary> pdf` or similar) when applicable.775. **Regulated-industry framing** — one of:78 - `explicit-not-hipaa-with-routing` — name {{BRAND_NAME}} as not HIPAA-certified and route to Jotform Gold / Cognito Enterprise in the Clinicians / Healthcare persona (recommended for any consent / clinical / medical topic)79 - `compact-caveat` — one-sentence non-HIPAA note, no competitor routing80 - `not-applicable` — the topic is not health/consent-adjacent (event, contact, feedback, quiz, etc.) — skip HIPAA copy entirely816. **Companion how-to blog (optional)** — the source tracker often has an adjacent blog row (`how to create a <keyword>`). Offer to ship both landing + blog as a pair. Default: no.8283After all six, echo the block back and wait for explicit confirmation before Phase 0.8485---8687## Rule 1 — Phase 0 SERP + LLM recon (mandatory, BEFORE any content build)8889Do NOT write copy or build components until this phase is complete and the ranking-factor synthesis is presented to the user.9091### 1a — Google SERP top-10 (DataForSEO Live SERP)9293Direct HTTP call — DataForSEO MCP tools may not be loaded. Load credentials from `.env`:9495```python96import base64, ssl, json, urllib.request97ctx = ssl.create_default_context(); ctx.check_hostname=False; ctx.verify_mode=ssl.CERT_NONE98auth = base64.b64encode(f"{DFS_LOGIN}:{DFS_PASS}".encode()).decode()99payload = json.dumps([{100 "keyword": PRIMARY_KEYWORD,101 "location_name": "United States", "language_code": "en",102 "device": "desktop", "depth": 10,103}]).encode()104req = urllib.request.Request(105 "https://api.dataforseo.com/v3/serp/google/organic/live/advanced",106 data=payload, method="POST",107 headers={"Authorization": f"Basic {auth}", "Content-Type": "application/json"}108)109```110111Extract from response:112- **Top 10 organic** — URL, domain, title, rank113- **SERP features** — AI Overview present? PAA questions? Video pack? Related searches? Discussions & Forums block?114- **Related searches** — often reveal the highest-intent long-tail variants (`<primary> free`, `<primary> pdf`, `<primary> for research`, etc.)115116### 1b — Firecrawl the top 5-6 commercial competitors117118Skip academic/IRB `.edu` results unless they represent a real audience {{BRAND_NAME}} wants. Extract per competitor:119- Word count, H1/H2/H3 tree120- Tools mentioned (which entities they name)121- Compare / benchmark: which pages ARE listicles vs product pages vs template galleries?122- Any structural pattern the top 3 share that {{BRAND_NAME}}'s default set doesn't have123124### 1c — LLM citation probe (OpenAI gpt-4o-search-preview × 4 queries)125126```python127payload = json.dumps({128 "model": "gpt-4o-search-preview",129 "messages": [{"role": "user", "content": QUERY}],130}).encode()131```132133Run 4 query variants covering informational / free / format-specific / use-case intents. Parse `choices[0].message.annotations[].url_citation` for cited domains. Record:134- Does {{BRAND_NAME}} get cited (URL or in-answer name)?135- Which competitor domains get cited most?136- What criteria does the LLM emphasize (unlimited free, AI generation, PDF export, e-signature legality, HIPAA scope, etc.)?137138### 1d — Ranking-factor synthesis (present to user, wait for approval)139140Produce a compact report before touching components:141142- Top 10 organic domain list with type (listicle / product page / template gallery / academic / brand)143- SERP features present + PAA questions verbatim144- Related-search intent list ranked by inferred volume145- Competitor structural pattern (word count, H2/H3 counts, distinctive sections)146- LLM citation status for {{BRAND_NAME}} on this keyword cluster (typically 0/4 on new keywords)147- Ranking factors the user's page will **take** from top-10 (design patterns + entity coverage + intent match)148- Ranking factors the user's page will **add on top** ({{BRAND_NAME}} differentiators: unlimited free, AI form gen from PDF, native e-signature, PDF export, GDPR + DPA §4.4, MCP for AI agents, etc.)149- Proposed component-by-component plan (13 slots — see Rule 2)150- Meta title + description proposals151- Regulated-industry framing decision (from Rule 0.5) applied to specific components152153Wait for explicit approval before Phase 1.154155---156157## Rule 2 — Component structure (15 slots, LOCKED — updated 2026-08-09 for legal-form-management-software)158159Every product page ships with **exactly 15 components in this order**. Slot type is fixed. Content varies by keyword.160161| # | Slot | Component type | Purpose |162|---|---|---|---|163| 0 | **Custom animated hero** | `micro-components.raw-html` | Two-column hero: text left (badge / H1 with violet accent / description / CTAs / trust bullets), right = SINGLE panel with two states — State 1 prompt input with 2-line typing animation + Generate button, State 2 generated form preview with 6 fields drawing in. Both states occupy the same absolute-positioned space, crossfade on a 14s loop. Whole State-2 form card is a clickable link to signup. See "Hero pattern" below. |164| 1 | 3-ways | `micro-components.raw-html` | 3 cards: Template · Prompt AI · Upload PDF |165| 2 | Trust seals | `macro-components.trust-seals` | `showTrustBadges: False` (org logo band only, no 4 rating cards) |166| 3 | Quality control | `micro-components.raw-html` | 5-item bullet list of what makes the output reliable |167| 4 | How it works | `macro-components.simple-steps-create` | 3 numbered steps with title + description each |168| 5 | **See in action (video/GIF grid)** | `micro-components.raw-html` | 2×2 grid of 4 short GIFs captured from `{{APP_HOST}}` showing the product in motion. Frame-accurate trim to skip page-load blank frames. `<img>` tags for GIFs (not `<video>`) so no autoplay-blank issue. See "GIF grid pattern" below. |169| 6 | Who uses this | `micro-components.raw-html` | 5-8 personas grid (apply HIPAA framing per Rule 0.5) |170| 7 | API + MCP | `micro-components.raw-html` | 2 cards: MCP for AI agents + REST API v2 |171| 8 | Privacy | `micro-components.raw-html` | DPA §4.4 + GDPR/UK GDPR/CCPA + encryption |172| 9 | **Feature grid (with topic icons)** | `micro-components.raw-html` | 6 feature cards. Each card has a topic-specific 40×40 icon tile (violet-25 bg + violet-100 border + violet-600 Lucide icon) above the title. **NOT the `standout-feature-detail` macro** — that macro's schema doesn't accept icons. See "Icon feature grid pattern" below. |173| 10 | Testimonial wall | `macro-components.testimonial-wall` | G2 / Trustpilot / Product Hunt / Capterra live reviews (`showG2/Trustpilot/ProductHunt/Capterra: True`) |174| 11 | Mid-page CTA | `macro-components.cta` | Existing CTA record `ctaValue: 67` (default: "Get access to advanced AI, unlimited forms & more.") |175| 12 | FAQs | `macro-components.faqs` | 9 Q&A pairs, PAA-matched, structured `faqList` (not raw-html) |176| 13 | Steps videos (optional) | `micro-components.raw-html` | 3-column grid of 3 GIFs — one per simple-step above. Optional; skip if the page doesn't have a natural 3-step flow. |177| 14 | More {{BRAND_NAME}} tools | `micro-components.raw-html` | 4 sibling tools cross-sell grid |178179**Non-negotiable:**180- Slots 2, 4, 10, 11, 12 use macro components — do not replace with raw-html181- All other slots use `micro-components.raw-html` with **`markup` field** (never `body` — writing to `body` results in silently empty components; this bug shipped on the initial cognito-forms-alternative update on 2026-08-03 and was caught in prod)182- **Every raw-html component uses `useContainer: false`** so it renders edge-to-edge like AFG's macros. The site's default container wrapper (`useContainer: true`) constrains sections to a narrow column and breaks the full-width feel of the AFG-hybrid look. Each raw-html component MUST manage its own inner max-width via a `.__container` or `.__inner` element (`max-width: 1180px; margin: 0 auto; padding: 0 24px`). Verified fix applied on legal-form-management-software (page 903) + form-management-software (page 904) on 2026-08-09.183- Class prefix on raw-html: unique 3-char kebab code per page (e.g. `lfm` for legal-form-management-software, `fmg` for form-management-software)184185### Hero pattern (slot 0) — reference: legal-form-management-software (page 903)186187Full raw-html hero with the two-state animation. Non-negotiables:188- 2-column grid on desktop (`grid-template-columns: 1fr 1fr`), stacks to 1-column below 900px189- Top padding **120px on desktop** (was 72px — this gap is required to clear the sticky nav)190- Left column: violet-100 badge pill, H1 with `<span class="__accent">` violet on the second half, description paragraph, 2 CTAs (Start free primary + See pricing ghost), trust bullets row191- Right column: single `.__panel` with two absolutely-positioned `.__state` children. State 1 is the prompt view (icon + label header, prompt box with 2-line typing, generate button below). State 2 is the generated form preview.192- **Animation loop = 14s.** Timing: 0-27% typing, 27-30% button pulse, 30-36% spinner + State 1 fadeout, 36-40% State 2 fadein, 40-92% form fields draw in one by one, 92-100% fade back to State 1193- **Typing**: two `.__line` elements each with `white-space: nowrap`. Each `.__line-fill` has its own `max-width` keyframe animation. Line 1 reveals 0-8.5%, then ~1.5s natural pause, then Line 2 reveals 12-21%. Timing function `cubic-bezier(0.22, 1, 0.36, 1)` — strong ease-out feels like natural typing that decelerates at word boundaries. Two cursors, one per line, with softer blink (opacity dips to 0.15 not 0)194- **Clickable**: State-2 form is wrapped in a single `<a href="{{SIGNUP_URL}}">` so the entire card acts as a signup link during the "form visible" phase. Use `pointer-events: none` in the keyframes for the invisible state so users can't click through195- Legal-page canonical markup: see `output/alt-pages/legal-form-management-software/` OR pull page 903's slot-0 markup fresh (`GET /api/pages/903?populate[components][populate]=*&publicationState=preview`)196197### Icon feature grid pattern (slot 9)198199Not a macro. Full raw-html. Non-negotiables:200- 6 feature cards, 3-col desktop / 2-col tablet / 1-col mobile201- Each card: **40×40 icon tile at top-left** (border-radius 8px, background `#f7f3ff` violet-25, border `1px solid #e4d7ff` violet-100, icon color `#6941c6` violet-600)202- Icons: **Lucide-style, 20×20 viewBox, stroke-width 1.8, currentColor** — match the `3-ways` and `who-uses` icon style already used elsewhere on the page. Pick icons that DIRECTLY represent the feature meaning, not decorative filler. Sample mapping used on legal-form-management-software:203204 | Card | Lucide-style icon |205 |---|---|206 | e-signature / signed forms | Pen writing a signature curve |207 | PDF export / document download | Document with down-arrow |208 | Conditional logic / branching | 3 connected circles (branch pattern) |209 | Custom domain / branding | Globe with meridian |210 | Team seats / roles | 2 users |211 | Integrations | Puzzle pieces / grid connectors |212 | AI form generator | Sparkles |213 | Native payments | Credit card |214 | Offline / PWA | Download cloud |215 | Multi-language | Globe with language char |216 | Privacy / GDPR | Shield check |217 | REST API / MCP | Code brackets `< >` |218219- Card body: title (H3, 18px, weight 600) + description (15px, line-height 1.55, color `#475467`). No CTA link inside cards.220- Hover: border → `#9777e0` (violet), shadow lift, `translateY(-2px)`221222### GIF grid pattern (slot 5)223224Not a macro. Full raw-html. Non-negotiables:225- 2×2 grid on desktop, 1-col below 760px226- 4 clips ONLY. More clips dilute impact.227- **Format: `<img>` tags pointing at animated GIF files uploaded to your CMS media library** — NOT `<video>` tags. GIFs render instantly with no blank first frame, no autoplay-block issues, no seek-required behavior. Autoplay is guaranteed on every browser.228- **File size target**: 300-500 KB per GIF, ~1.5MB total for the section229- **Capture pipeline** (Playwright + ffmpeg):230 1. Playwright headed, log in with `BRAND_APP_EMAIL` + `BRAND_APP_PASSWORD` from `.env`, save `storage_state.json`231 2. Pre-extract a real form ID from the /forms dashboard (use JS to find `data-form-id`, else fall back to creating a Start-From-Scratch form)232 3. Record each interaction as a WebM (Playwright `record_video_dir` context option, viewport 1280×720)233 4. Convert WebM → GIF with ffmpeg using **frame-accurate trim** via the `trim=start=X:end=Y,setpts=PTS-STARTPTS` filter — NOT `-ss` before `-i` which does keyframe-based seek and often produces blank first frames234 5. GIF settings: `fps=12, scale=800:-1:flags=lanczos`, 2-pass palette generation with `stats_mode=diff` + `paletteuse=dither=bayer:bayer_scale=5`235 6. Upload each GIF via `POST /api/upload` multipart with `Content-Type: image/gif`236- **Card design**: same shape as other card grids — white bg, 1px grey border, 16px border-radius, subtle shadow, violet border on hover. Each card has a 16:10 aspect-ratio `.__frame` at top containing the GIF, then title + body below.237- **Playwright locator strategy**: coordinate clicks work better than text-based locators for Vue/Element-UI dialogs. Inspect the DOM first via a JS eval that dumps `{tag, classes, rect}` for candidate elements, then use `page.mouse.click(x, y)` at rect centers for Vue-tile clicks. Use CSS selectors like `button.template-card` or `button.fmst-btn` for real buttons.238- **Fallback**: if a specific interaction won't fire reliably from Playwright, ship the clip anyway showing the static screen — real product UI beats a fake mock. Note honestly in the report which clips are interactive vs static.239240Reference implementation: legal-form-management-software (page 903) shipped 2026-08-09 with 4 GIFs (media ids 4780-4783) covering "Six ways to create a form", "Form builder with live design controls", "Ready-made template library", "Form-to-PDF export".241242---243244## Rule 3 — Design system (byte-for-byte from ai-form-generator)245246The canonical source is `/ai-form-generator/` (reference entry). When building a new product page, fetch AFG's 13 components and:2472481. **Macros**: mirror the field structure exactly. Only vary `title` text parts, `description`, `itemList` items, `suggestions`, `footerItems`, `faqList`. Leave `showG2/Trustpilot/ProductHunt/Capterra: True` and `ctaValue: 67` and `showTrustBadges` as-is.2492. **Raw-html**: copy from `HTML component/consent-form-generator/` (reference build), swap the 3-char prefix (`csg` → new prefix), rewrite the copy for the new keyword. **Do NOT copy from `HTML component/contact-form-generator/`** — that library is compact and misses some AFG design details. Consent-form-generator is the canonical raw-html reference going forward.250251Class prefix substitution: pick a unique 3-char kebab code, do a global `csg-` → `<new-prefix>-` replace across all 6 raw-html files.252253The 8 canonical topic SVG icons + Lucide shape rules from `~/.claude/skills/_shared/alt_page_design_system.py` also apply here — if the `who-uses` block has icons, they get topic-swapped by `apply_alt_page_rules()` automatically.254255---256257## Rule 4 — Voice + accuracy rules (mandatory)258259Same as every {{BRAND_NAME}} content skill:260261- **No em-dashes anywhere** in body copy. Preflight blocks publish if any leak in. Applies to macro content too, not just raw-html.262- **No hardcoded dates.** Every "As of X" resolves to `today()` at build time.263- **No placeholders.** Strip `TODO`, `FIXME`, `INSERT_`, `REPLACE_`, `PLACEHOLDER` before commit.264- **No external URLs in FAQ answers.** Internal `/features/...` links are fine.265- **No repeat internal links** in body: each internal URL linked at most once outside the more-tools grid.266- **Every {{BRAND_NAME}} claim** traces to `memory/brand-facts.md`. Never invent features. {{BRAND_NAME}} is NOT HIPAA-certified, NOT SOC 2, NOT ISO 27001, and NOT "on the HIPAA roadmap" — banned phrasing.267- **User-count is 56,000+ teams** unless a newer confirmed number lives in the current conversation.268- **Every competitor claim** traces to `competitor-research.md`. Correct brand casing (Jotform / Google Forms / Typeform / Microsoft Forms / Zoho Forms / Fillout / forms.app / KoboToolbox).269- **HIPAA framing (from Rule 0.5):**270 - `explicit-not-hipaa-with-routing` → the Clinicians / Healthcare persona card names Jotform Gold and Cognito Forms Enterprise as HIPAA-eligible alternatives, positions {{BRAND_NAME}} for non-PHI use only (marketing opt-in, patient education, event RSVPs).271 - `compact-caveat` → one-sentence note only ("{{BRAND_NAME}} is not HIPAA-certified. Use for non-PHI intake."), no competitor routing.272 - `not-applicable` → no HIPAA mention on the page.273274---275276## Rule 5 — Google Doc draft (approval gate, mandatory before CMS push)277278After the 13 components are built locally, produce a Google Doc for content approval before any CMS write.279280**Doc structure is LOCKED by the shared standard at [`~/.claude/skills/_shared/doc_content_standard.md`](../_shared/doc_content_standard.md).**2812821. Metadata table first (Title, Description, Meta title, Meta description, Primary keyword, Primary keyword volume (US), Secondary keywords, Slug, Recommended URL, CMS link). No other rows.2832. Body: verbatim copy from the 13 components, in reading order, with explicit `[H1] / [H2] / [H3]` heading markers and `[Image: ...]` placement markers wherever an on-page image will render.2843. **No commentary, no rationale, no `**Why:**` lines, no `[REUSE: ...]` or `[NEW TEMPLATE]` annotations, no cover URL or media ids, no schema JSON, no preflight-status footer.**2854. CTAs render as plain lines: `Button: <label>` + `Link: <url>`.2865. Use `~/.claude/skills/gdoc/build_gdoc.py` with `GDOC_AUTH_DIR={{GOOGLE_AUTH_DIR}}` ({{GOOGLE_ACCOUNT}} OAuth, not {{GOOGLE_ACCOUNT}} — {{BRAND_NAME}} project rule).2876. Doc lands in the "{{BRAND_NAME}} SEO Audits" Drive folder.2887. When updating an existing page, regenerate in place via `--doc-id <existing_id>`.2898. Share URL with the user. Wait for explicit content approval before touching the CMS.290291Content approval unlocks Rule 6.292293---294295## Rule 6 — CMS push (POST /api/pages, draft state)296297After content approval:2982991. **Preflight all 6 raw-html components** via `run_preflight()` from `~/.claude/skills/_shared/preflight.py`. This runs `strip_trailing_periods`, `fix_button_hover_color`, `fix_markdown_leakage`. Any change gets persisted to disk before push. Mandatory.3003012. **Audit-scan all 6 raw-html components** via `python3 ~/.claude/skills/_shared/audit_scan.py <file>`. Exit 1 = do not push, fix and re-run. Catches {{BRAND_NAME}} HIPAA overclaim, "on roadmap" phrasing, em-dashes, hardcoded dates.3023033. **Build the payload** in this exact shape:304305```python306payload = {307 "data": {308 "slug": SLUG,309 "publishedAt": None, # HARD RULE: never publish on POST310 "meta": {311 "title": META_TITLE, # 30-60 chars, ends with "| {{BRAND_NAME}}"312 "description": META_DESCRIPTION, # 120-160 chars313 "type": "website",314 "url": LIVE_URL,315 "keywords": [{"text": kw} for kw in KEYWORDS],316 "link": [{"hid": "canonical", "rel": "canonical", "href": LIVE_URL}],317 "jsonld": JSONLD_GRAPH, # see Rule 7318 },319 "components": [320 {"__component": "micro-components.raw-html", "markup": CUSTOM_HERO, "useContainer": False}, # slot 0 — custom animated hero321 {"__component": "micro-components.raw-html", "markup": ..., "useContainer": True}, # slot 1 3-ways322 {"__component": "macro-components.trust-seals", "showTrustBadges": False}, # slot 2 (org logos only)323 {"__component": "micro-components.raw-html", "markup": ..., "useContainer": True}, # slot 3 quality324 SIMPLE_STEPS, # slot 4 macro (simple-steps-create)325 {"__component": "micro-components.raw-html", "markup": GIF_GRID, "useContainer": True}, # slot 5 — See in action GIF grid326 {"__component": "micro-components.raw-html", "markup": ..., "useContainer": True}, # slot 6 who-uses327 {"__component": "micro-components.raw-html", "markup": ..., "useContainer": True}, # slot 7 api-mcp328 {"__component": "micro-components.raw-html", "markup": ..., "useContainer": True}, # slot 8 privacy329 {"__component": "micro-components.raw-html", "markup": ICON_FEATURE_GRID, "useContainer": True}, # slot 9 — Icon feature grid (NOT standout-feature macro)330 TESTIMONIAL_WALL, # slot 10 macro331 {"__component": "macro-components.cta", "ctaValue": 67}, # slot 11 macro332 FAQS, # slot 12 macro (faqs)333 {"__component": "micro-components.raw-html", "markup": ..., "useContainer": True}, # slot 13 — steps videos (optional)334 {"__component": "micro-components.raw-html", "markup": ..., "useContainer": True}, # slot 14 — more {{BRAND_NAME}} tools335 ],336 }337}338```3393404. **POST** to `{{CMS_BASE_URL}}/api/pages`. Use the CMS token from your environment.3413425. **Verify** via `GET /api/pages/<id>?populate[components][populate]=*&populate[meta][populate]=*&publicationState=preview`. Confirm 14-15 components with correct types, `publishedAt: null`, canonical link set, JSON-LD @graph has 5 entities.3433446. Report CMS admin URL to the user. Wait for explicit publish approval.345346---347348## Rule 7 — 5-entity JSON-LD @graph (mandatory)349350Every product page's `meta.jsonld['@graph']` must include:3513521. **Organization** — {{BRAND_NAME}} (referenced by `@id: {{SITE_URL}}/#organization`)3532. **WebPage** — url, name, description, `datePublished` = today, `dateModified` = today, inLanguage: `en-US`, isPartOf, breadcrumb3543. **BreadcrumbList** — Home → Page name3554. **FAQPage** — 9 verbatim Q&A pairs from the `faqs` macro's `faqList`. Structured as `{"@type": "Question", "name": q, "acceptedAnswer": {"@type": "Answer", "text": a}}`.3565. **SoftwareApplication** — {{BRAND_NAME}} product entity: name, url, `operatingSystem: "Web, iOS, Android"`, `applicationCategory: "BusinessApplication"`, `offers` array (Free at $0, Personal at $13), `aggregateRating` (4.7/5, 11 reviews on G2).357358Dates resolve to today at build time. Never a hardcoded string.359360---361362## Rule 8 — SEO preflight (blocks publish)363364Any FAIL blocks the publish step. Auto-run these checks before flipping `publishedAt`:365366- Meta title 30-60 chars, ends with `| {{BRAND_NAME}}`367- Meta description 120-160 chars368- Slug matches URL path369- Exactly 1 `<h1>` across all components (from the ai-hero macro)370- No em-dashes anywhere in components or macro strings371- No banned placeholders (`TODO`, `FIXME`, `INSERT_`, `REPLACE_`, `PLACEHOLDER`)372- Canonical link set: `meta.link[0].rel == 'canonical'`373- `meta.jsonld['@graph']` has exactly 5 entities (Rule 7)374- FAQPage entity has ≥ 5 Q&A pairs, target 9375- `publishedAt: null` (still a draft)376- Component count = 13 in the exact order from Rule 2377- Every raw-html component uses `markup` field (not `body`) + `useContainer: true`378- `run_preflight()` + `audit_scan.py` exit 0379380---381382## Rule 9 — Publish (only on explicit approval)383384Per `feedback_never_publish_without_approval.md`: never flip `publishedAt` without explicit user approval on THIS specific page. Prior approvals do not carry forward.385386When approved:3873881. Set `publishedAt` to `datetime.now(timezone.utc).replace(microsecond=0).isoformat() + 'Z'`.3892. PUT to `/api/pages/<id>`.3903. Poll `{{SITE_URL}}/<slug>/` for 200. Nuxt static rebuild takes ~5-15 minutes; 404 in that window is expected.3914. Log to Cluster Master Tracker `Published Content Tracker` tab if the source row exists.392393---394395## Rule 10 — Reference implementation396397**Canonical reference for the 15-slot pattern (updated 2026-08-09):** `legal-form-management-software` (reference entry). Custom animated hero, GIF grid section, icon feature grid.398399**Prior reference for the base 13-slot AFG-hybrid pattern:** `consent-form-generator` (reference entry).400401When in doubt about:402- Macro field shapes → GET `/api/pages/1?populate[components][populate]=*&publicationState=preview` (ai-form-generator)403- Raw-html section design → read `HTML component/consent-form-generator/*.html`404- Component ordering → GET `/api/pages/898?populate[components][populate]=*&publicationState=preview` (consent-form-generator)405- Doc format → open the consent-form-generator doc (in "{{BRAND_NAME}} SEO Audits" Drive folder)406407**Do NOT reference:**408- `contact-form-generator` — this predates the AFG-hybrid architecture; its 10 raw-html structure is deprecated for new builds409- `ai-form-generator` local `HTML component/ai-form-generator/*.html` files as a raw-html template — those are the OLD (pre-macro) raw-html versions. AFG's LIVE page uses the macros defined in the CMS, not those local files.410411---412413## Rule 11 — Things to never do414415- Never publish without explicit approval on THIS page.416- Never invent {{BRAND_NAME}} features. HIPAA / SOC 2 / ISO 27001 are all NO. Never claim "on the roadmap" for any of these.417- Never use em-dashes.418- Never leave trailing periods on headings, badges, or CTAs.419- Never hardcode dates in generated components — always `today()` at build time.420- Never skip the Phase 0 SERP+LLM recon. It's the whole point of this skill vs the older `/landing-page`.421- Never skip the Google Doc approval gate.422- Never write to the `body` field on `micro-components.raw-html` — use `markup`. Writing to `body` results in silently empty components (**Strapi only:** it discards unknown fields). This bug shipped on the initial cognito-forms-alternative update 2026-08-03 and was caught in prod.423- Never reuse a class prefix across pages. Each page gets a unique 3-char kebab code.424- Never let the `type` field on `macro-components.ai-hero` be anything except `Form`, `Score Quiz`, or `Outcome Quiz` (the CMS enum). "Consent form" or similar strings will 400 the POST.425426427## No self-referential media captions (HARD RULE, added 2026-08-09)428429Never add authenticity remarks under or near screenshots, GIFs, or videos: "Real {{BRAND_NAME}} UI, captured in-app", "actual product screenshot", "real UI, not a mockup", "captured from the live app", or any similar meta-commentary about the asset being real. These read as AI-generated filler and were ordered removed site-wide. The asset speaks for itself. If a media element genuinely needs a caption, the caption states only WHAT the user is looking at (e.g. "Form builder, Design tab"), never that it is real, actual, or captured. This applies to every page type: product, landing, alternative, blog, listicle, review, template.430431432## Inspiration pages are design-only, never fact sources (GLOBAL HARD RULE, added 2026-08-10)433434Root cause: the Aug 2026 factual audit found 4 alternative pages shipped with the inspiration page's facts (Wufoo's pricing tiers, ownership, founding year) left in place for different competitors. This must never recur.435436- An internal inspiration/reference page may be reused ONLY for: page structure, section structure, layout, design patterns, visual treatment, formatting, component patterns, UX approach.437- NEVER carry over content, claims, facts, statistics, pricing, plan names, limits, ownership, founding dates, quotes, or any information from the inspiration page into a new page.438- Research comes FIRST on every new page: (1) research the subject independently, (2) identify current correct information, (3) verify every factual claim against first-party/authoritative sources (official pricing page, docs, company newsroom), (4) write original content from that research.439- Never assume something is correct because it appears on an existing {{BRAND_NAME}} page. Never fill gaps with guesses. If a claim cannot be verified, flag it explicitly instead of presenting it as fact.440- Cloned template blocks that carry competitor-specific facts (pricing tables, "why teams leave" cards, honest-comparison cards, free-plan tables) must be rebuilt from fresh research for every new subject, not find-and-replaced.