Skip to content

feat(landing-purchase): new Landing/Purchase page + weownchat-design skill - #163

Closed
SinachPat wants to merge 2 commits into
WeOwnNetwork:mainfrom
SinachPat:feature/patrick-landing-purchase-page
Closed

feat(landing-purchase): new Landing/Purchase page + weownchat-design skill#163
SinachPat wants to merge 2 commits into
WeOwnNetwork:mainfrom
SinachPat:feature/patrick-landing-purchase-page

Conversation

@SinachPat

Copy link
Copy Markdown
Contributor

Summary

First build of the WeOwn Chat Landing/Purchase page (PRD roadmap item 2 — the top-of-funnel marketing site; the transactional purchase/checkout flow itself already lives separately at billing.weown.dev, live in test-mode). New landing-purchase/ project: Next.js 15 (App Router) + TypeScript + Tailwind v4.

Also adds .claude/skills/weownchat-design/ — a Claude Code skill capturing the real WeOwn brand tokens, so this page (and any future frontend work) is built from the actual product's design system instead of an independently-invented one. See "Design tokens" below — this went through one real correction mid-build and I'm surfacing that rather than hiding it.

What's in the page

  • Nav, hero (a live, looping recreation of the real chat UI — message bubbles, the "thinking" dots — not a static mockup), a "Live preview" section that embeds the real, unmodified dashboard HTML in an iframe with seeded fictional demo content, how-it-works, the two-tab (private/public) explainer, value pillars, pricing, FAQ, footer.
  • Pricing section shows no dollar figure — commercial terms are still draft/unsigned per the PRD; this is deliberate, not an oversight.
  • Scroll-reveal animations and hover micro-interactions copied from the real product's own CSS (exact easing curve, exact button lift/press transform), not invented values.

Design tokens — sourced from the real product

Colors (--bg: #0e1726, --accent: #00A3FF, ...), the system-ui font stack (no webfonts — the real product loads none), and the 8/10/16/999px radius scale are copied verbatim from:

  • anythingllm-docker/template/dashboard/public/index.html's :root
  • the Keycloak weown login theme (keycloak-docker/.../theme/weown/login/resources/css/weown.css)

Corrected mid-build: an earlier pass had invented its own light/warm/serif palette before checking whether a real brand already existed. It did. Everything was rebuilt against the real tokens once caught — see .claude/skills/weownchat-design/SKILL.md's own changelog (v1 → v2) for the full account. The skill exists specifically so this doesn't happen again on the next frontend surface.

The dashboard iframe, in detail

landing-purchase/public/_demo/dashboard-preview.html is an unmodified copy of the real dashboard template with one addition: a window.fetch mock feeding it realistic fictional content (a placeholder tenant, Harborview CPA — matching the existing weown-cpa-demo.pages.dev placeholder-brand convention already used elsewhere in this repo) instead of hitting a live backend. The real app's own code, hash router, and tab logic run completely unmodified — the Private/Public toggle on the landing page just sets iframe.contentWindow.location.hash.

Maintenance contract (stated in the file's own header comment): edit this file to match the real template if the dashboard's markup changes — never the reverse.

.gitignore

Narrowed from a blanket .claude/ exclusion to .claude/* + !.claude/skills/. Skills are authored, shareable content (not session/worktree/transcript noise, which is what that rule was originally written for) — this makes .claude/skills/ trackable while everything else under .claude/ stays ignored.

Verification

  • next build — clean: types check, lint passes, static generation succeeds (108 KB first-load JS, fully prerendered).
  • Manually checked in-browser at desktop, 1440px, and mobile (390px) widths.
  • Confirmed scroll-reveal fires correctly on a real scroll event.
  • Manual grep for secrets/PII/private-IPs across every newly-tracked file — clean. (gitleaks/pre-commit weren't available in this environment to run directly; CI's own secret-scan is the backstop.)

Explicitly out of scope / open follow-ups

Tracked in landing-purchase/CHANGELOG.md and README.md:

  • Hosting/deployment target not decided — Docker Compose + droplet (matching every sibling *-docker/ service) vs. a static host. Nothing here forecloses either path.
  • npm audit: 3 high-severity issues, both nested inside Next.js's own postcss/sharp. The only available fix is a Next 16 major-version bump — not taken unilaterally given the breaking-change risk; flagging for a deliberate look rather than silently ignoring or force-upgrading.
  • No legal/privacy pages, no analytics, no real Stripe/billing wiring beyond a link to billing.weown.dev.
  • Page copy is written against the (not-yet-merged-anywhere-public) Branding & Marketing guide's guardrails, but hasn't had a second pass from anyone else yet.

🤖 Generated with Claude Code

…skill

New Next.js 15 + TypeScript + Tailwind v4 project implementing PRD roadmap
item 2 (top-of-funnel marketing site). Full first build: nav, animated
live-chat hero, a "Live preview" section embedding the real dashboard HTML
in an iframe with seeded demo content, how-it-works, the two-tab
(private/public) explainer, value pillars, pricing (no dollar figure —
commercial terms are still draft/unsigned), FAQ, footer.

Design tokens are copied verbatim from the real product — the dashboard's
own :root (anythingllm-docker/template/dashboard/public/index.html) and the
Keycloak weown login theme — not invented independently. Codified as a
Claude Code skill (.claude/skills/weownchat-design/) so future frontend
work reuses this source of truth instead of drifting from it. An earlier
draft of both the skill and the page shipped an independently-invented
light/serif palette before this was caught and corrected mid-build.

.gitignore narrowed from a blanket `.claude/` exclusion to `.claude/*` +
`!.claude/skills/`, so skills are shareable while local session state
(worktrees, transcripts) stays ignored.

Verification: `next build` clean (types + lint + static generation, 108 KB
first-load JS); checked at desktop/1440px/mobile widths; manual secret/PII
grep over all newly-tracked files came back clean (gitleaks/pre-commit not
available in this environment).

Open follow-ups tracked in landing-purchase/CHANGELOG.md and README.md:
hosting/deployment target not yet decided; npm audit flags 3 high-severity
issues nested inside Next.js's own postcss/sharp (fix requires a Next 16
major bump, not taken unilaterally); no legal/analytics/real billing wiring
yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 6, 2026 18:11
@SinachPat
SinachPat requested a review from ncimino as a code owner August 6, 2026 18:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds a new Next.js-based Landing/Purchase site and codifies the existing WeOwn Chat visual design system as a shareable Claude skill, including a live dashboard preview embedded via iframe.

Changes:

  • Introduces landing-purchase/ (Next.js App Router + TS + Tailwind v4) with composable marketing sections.
  • Adds a demo “real dashboard” HTML asset in public/_demo/ with a fetch-mock so the iframe renders seeded fictional content.
  • Updates repo changelogs and .gitignore to track/share .claude/skills/ while ignoring other local Claude workspace artifacts.

Reviewed changes

Copilot reviewed 31 out of 33 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
landing-purchase/tsconfig.json Adds TypeScript configuration for the new Next.js app.
landing-purchase/public/_demo/dashboard-preview.html Adds embedded demo dashboard HTML used by the landing page’s live preview.
landing-purchase/postcss.config.mjs Configures PostCSS for Tailwind v4.
landing-purchase/package.json Adds Next/React/TS/Tailwind/ESLint dependencies and scripts for the new app.
landing-purchase/next.config.ts Adds Next.js configuration for the new app.
landing-purchase/components/icons.tsx Adds shared inline SVG icon components for landing page sections.
landing-purchase/components/hooks.ts Adds client hook for reduced-motion preference detection.
landing-purchase/components/ValuePillars.tsx Adds “value pillars” marketing section.
landing-purchase/components/TwoTabExplainer.tsx Adds private/public tab concept explainer section.
landing-purchase/components/TrustLabelStrip.tsx Adds “trust label” strip component for proof points.
landing-purchase/components/Reveal.tsx Adds scroll-into-view reveal animation wrapper using IntersectionObserver.
landing-purchase/components/ProductShowcase.tsx Adds iframe-based “live preview” showcase with tab switching.
landing-purchase/components/Pricing.tsx Adds pricing section (intentionally no dollar amounts).
landing-purchase/components/Nav.tsx Adds responsive navigation with mobile menu.
landing-purchase/components/HowItWorks.tsx Adds “how it works” steps section.
landing-purchase/components/Hero.tsx Adds hero section including animated chat preview.
landing-purchase/components/Footer.tsx Adds footer with nav columns and trust labels.
landing-purchase/components/FAQ.tsx Adds FAQ accordion section.
landing-purchase/components/Container.tsx Adds layout container primitive.
landing-purchase/components/Button.tsx Adds internal/external link button component.
landing-purchase/components/BrandMark.tsx Adds WeOwn “W” brand mark component.
landing-purchase/components/AnimatedChatMock.tsx Adds animated chat UI recreation for the hero.
landing-purchase/app/page.tsx Wires landing page sections into the root page.
landing-purchase/app/layout.tsx Adds root layout and metadata for the new app.
landing-purchase/app/globals.css Adds Tailwind v4 theme tokens and base styles/animations.
landing-purchase/README.md Documents the new landing/purchase app and how to run it locally.
landing-purchase/CHANGELOG.md Adds a per-project changelog for landing-purchase.
landing-purchase/.gitignore Adds ignores for Next/Node/TS artifacts within landing-purchase.
CHANGELOG.md Adds landing-purchase to the global changelog index + release entry.
.gitignore Adjusts Claude workspace ignores to include .claude/skills/ in repo.
.claude/skills/weownchat-design/references/tokens.md Adds canonical design token reference sourced from production UI.
.claude/skills/weownchat-design/SKILL.md Adds design-system rules for consistent frontend work across surfaces.
Suppressed comments (2)

landing-purchase/public/_demo/dashboard-preview.html:1

  • The demo fetch mock matches routes via path.indexOf(key) !== -1, which causes collisions for nested endpoints. For example, /api/threads/<slug>/chats will match the /api/threads key and return the thread list JSON shape (no history), breaking per-thread chat loading in the preview. Prefer matching a normalized /api/... path (e.g., extract substring starting at /api/) and use exact matches for known demo routes, plus add explicit handling for /api/threads/:slug/chats so switchThread() receives { history: [...] }.
    landing-purchase/public/_demo/dashboard-preview.html:1
  • #chatlog is the scroll container in the CSS (overflow-y:auto), but renderChat() scrolls #msgs (wrap.scrollTop = ...). This likely won’t actually scroll the visible container when messages overflow. Update the code to scroll the element that actually has overflow (e.g., document.getElementById('chatlog')), or scroll the last message into view.

Comment on lines +14 to +21
as: Tag = "div",
}: {
children: ReactNode;
delay?: number;
className?: string;
as?: "div" | "li";
}) {
const ref = useRef<HTMLDivElement>(null);
Comment on lines +41 to +45
const Comp = Tag as "div";

return (
<Comp
ref={ref}
Comment on lines +15 to +16
next-env.d.ts
*.tsbuildinfo
Comment on lines +8 to +12
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
setReduced(mq.matches);
const onChange = () => setReduced(mq.matches);
mq.addEventListener("change", onChange);
return () => mq.removeEventListener("change", onChange);
WeOwn AI is the venture studio; WeOwnChat is the product this page sells.
Updates the page title/metadata, hero copy, nav + footer wordmark (was
split across a <span> for accent-color styling, so it didn't match a
plain-text search for "WeOwn AI" and needed a separate check), and the
dashboard-preview iframe's title attribute. Also re-syncs
public/_demo/dashboard-preview.html with the same fix landing in the real
dashboard template (anythingllm-docker — separate PR), per that file's own
maintenance rule: it follows the real template, never the reverse.

Also includes the .vercel entry the Vercel CLI added to .gitignore on first
deploy link (project-link metadata, correctly excluded).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 7, 2026 15:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 31 out of 33 changed files in this pull request and generated no new comments.

Suppressed comments (3)

landing-purchase/components/Reveal.tsx:22

  • Reveal allows as: "div" | "li", but the ref is typed as HTMLDivElement, which becomes incorrect as soon as as="li" is used (type mismatch and potential incorrect assumptions in future edits). This should be typed to a generic HTMLElement (or fully generic over the intrinsic element). (NIST CSF §3.1 PR.IP — secure/robust implementation; MEDIUM)
  as?: "div" | "li";
}) {
  const ref = useRef<HTMLDivElement>(null);
  const [visible, setVisible] = useState(false);

landing-purchase/components/Reveal.tsx:45

  • const Comp = Tag as "div" forces the component type to div even when as="li" is passed, and ref={ref} then no longer matches the rendered element type. This defeats the point of the as prop and can hide real typing issues. Use const Comp = Tag; and cast the ref (or make the component generic) so the runtime element and types stay aligned. (NIST CSF §3.1 PR.IP — robust client code; MEDIUM)
  const Comp = Tag as "div";

  return (
    <Comp
      ref={ref}

landing-purchase/components/ProductShowcase.tsx:83

  • The embedded dashboard preview runs same-origin and executes scripts. Without a sandbox, the iframe content can freely interact with the parent page (e.g., navigate top-level, read/modify parent DOM) if it ever regresses or is modified later. Add a restrictive sandbox that still permits required functionality (scripts + same-origin + forms) to reduce blast radius. (NIST CSF §3.1 PR.PS — browser isolation/hardening; HIGH)
            <iframe
              ref={iframeRef}
              src="/_demo/dashboard-preview.html"
              title="WeOwnChat dashboard preview, with example content"
              className="h-[420px] w-full sm:h-[540px]"

@SinachPat

Copy link
Copy Markdown
Contributor Author

Closing this in favor of a fresh PR — the page has been rewritten wholesale on Astro (was Next.js) per direct direction to match the framework used elsewhere in the WeOwn ecosystem. All content/design/animation work carries over unchanged (WeOwnChat branding, real dashboard tokens, the live-preview iframe, etc.); the rewrite also hardened the scroll-reveal system after testing surfaced a real gap in it. Same page, different (and cleaner — 0 npm audit findings vs. 3) foundation, in a new PR rather than a confusing delete-everything-add-everything diff on this one.

@SinachPat SinachPat closed this Aug 8, 2026
@SinachPat
SinachPat deleted the feature/patrick-landing-purchase-page branch August 8, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants