Reference
Gated Access CTA
Free preview, gated code. Visitors get their one best-fit offer, or the whole ladder when we know nothing about them.
Gated Access CTA
Show references for free and gate the code behind an upgrade. The gate never shows a generic sales page. It renders only the
offer cards from /invest, picked per visitor from the blueprint.
The component
Live, exactly as a visitor sees it. Links open in a new tab; the pay switch works.
In context — Preview | Code
Click Code. Every link inside the widget opens in a new tab, and the Paid Upfront / Split Pay switch is live.
Try it as a known visitor: agency · brand · freelancer.
Who sees what
| Visitor | Code tab shows |
|---|---|
| Studio owner (signed in) | The real source for that variant |
| Nothing known | The whole ladder: Accelerator · Automator · Operator |
| Persona known | That persona's entry offer only |
| Meets a higher offer's qualifiers | The highest offer they fully qualify for |
| Already owns an offer | The next offer up (Advisor only after a sprint) |
| Disqualified | Free option — [PLACEHOLDER] until decided |
Decided server-side by apps/portal/lib/offers/best-fit.ts, which reads the Different Hunger blueprint
(personas · persona→offer links · offer qualifiers · disqualifiers) on every request. Change the blueprint and every gate moves with it.
Prices, features and buttons come from the same buildPlans as /invest, so they never drift.
Use it
1 · Put a Preview | Code pair on a docs page
<PreviewCode component="reels" variant="thirds-card-mcm">
{/* anything free: video, image, live preview */}
</PreviewCode>?persona= on the docs URL is passed through to the gate.
2 · Gate a new component family
- Generate a server-only module of its variants:
{ [variant]: { name, brand, status, files: [{ path, code }] } }— copyapps/portal/scripts/sync-reel-code.mjs. - Register it in
apps/portal/lib/gated-code/registry.ts:
export const CODE_REGISTRY = {
reels: REEL_CODE,
// yourComponent: YOUR_CODE,
};- The gate is live at
/os/code/<component>/<variant>. No new route.
3 · Show the offer panel anywhere else
import { GatedAccessCTA } from "@/app/os/code/upgrade-panel";
import { loadOfferEconomics } from "@/lib/offers/blueprint-economics";
import { bestFit } from "@/lib/offers/best-fit";
const [econ, fit] = await Promise.all([loadOfferEconomics(), bestFit({ persona })]);
<GatedAccessCTA econ={econ} offerKey={fit.mode === "one" ? fit.offer : null} />Embed treatment — locked
Every widget framed inside another surface follows this, no exceptions:
| Rule | How |
|---|---|
| Content only — no header, footer or title | route listed in apps/portal/lib/non-conversion.ts |
| Fits the frame side by side, no swipe | app/os/code/zoom-fit.tsx scales the desktop layout down; swipe row only under 560px |
| Frame hugs the content, zero inner scroll | app/os/code/frame-height.tsx posts content height; the host frame listens |
| Every link opens in a new tab | frame-height.tsx intercepts clicks when framed |
| Controls actually work | e.g. the pay switch holds state |
Files
| File | Role |
|---|---|
apps/portal/app/os/code/[component]/[variant]/page.tsx | The gate: owner → code, everyone else → offers |
apps/portal/app/os/code/upgrade-panel.tsx | GatedAccessCTA — the offer cards |
apps/portal/lib/offers/best-fit.ts | One offer per visitor, from the blueprint |
apps/portal/lib/gated-code/registry.ts | Which component families are gated |
apps/portal/app/os/code/frame-height.tsx · zoom-fit.tsx | Locked embed treatment |
apps/docs/packages/template/app/components/docs/mdx/preview-code.tsx | Docs Preview |