Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@

## Aesthetic Direction

- **Direction:** Warm technical editorial in marketing; cinematic black creative operating system across Studio, Canvas, and comparison-led Pricing.
- **Direction:** Warm technical editorial in marketing; cinematic black creative operating system across Studio and Canvas.
- **Decoration:** Intentional. Typography, product media, fine grid lines, dark glass controls, and restrained highlights create character without ornamental UI noise.
- **Layout:** Hybrid. Marketing uses editorial scale and asymmetry; local project surfaces use a strict, predictable grid.
- **Mood:** Capable, cinematic, and crafted. Creation surfaces should feel like focused production tools, while the marketing site stays warmer and more editorial.
- **Reference language:** A focused editorial pricing language, not a separately selectable theme.

## Typography

Expand All @@ -31,15 +30,14 @@
- **Brand soft:** `#FFF0E5` — selected surfaces and subtle callouts.
- **Marketing canvas:** `#F6F0E7` — warm cream.
- **Studio / Canvas / Assets canvas:** `#08090A` — near-black production workspace.
- **Pricing canvas:** `#08090A` with graphite `#141517` plan cards, warm-white type, and clear comparison geometry.
- **Dark surface:** `#111214`; raised surface `#17181B`; control surface `#202126`.
- **Dark ink:** `#F6F6F4`; secondary `rgba(246,246,244,.58)`; faint `rgba(246,246,244,.34)`.
- **Surface:** `#FFFFFF`; raised surface `#FCFCFA`.
- **Ink:** `#171814`; secondary `#5F625B`; faint `#8E9188`.
- **Borders:** `#DDDED8`; strong `#C7C9C0`.
- **Highlight:** `#C7F36B` — sparse proof or status accent, never a primary CTA.
- **Success:** `#208A55`; warning `#B66A09`; error `#C23B32`; info `#2563A9`.
- **Dark product surfaces:** Near-black canvas with charcoal cards. Orange stays saturated because it is the only brand action color; cold blue is limited to Canvas graph semantics. Pricing keeps a clear commercial hierarchy while using the same dark product vocabulary as Studio, Canvas, and Assets.
- **Dark product surfaces:** Near-black canvas with charcoal cards. Orange stays saturated because it is the only brand action color; cold blue is limited to Canvas graph semantics.

## Spacing and Shape

Expand All @@ -66,7 +64,7 @@ Button, IconButton, Input, Textarea, Select, Checkbox, Switch, Tabs, Tooltip, Di

### Marketing

MarketingHeader, HeroStatement, HeroDemoFrame, ProofStrip, FeatureStory, MediaShowcase, WorkflowSteps, CodePreview, PricingSection, FAQ, CTA, and MarketingFooter.
MarketingHeader, HeroStatement, HeroDemoFrame, ProofStrip, FeatureStory, MediaShowcase, WorkflowSteps, CodePreview, FAQ, CTA, and MarketingFooter.

### Product

Expand Down Expand Up @@ -120,9 +118,7 @@ Homepage emphasis is approximately 70% what can be built with BeatAPI, 20% what
| 2026-08-12 | Shared AppShell with Studio, Canvas, and Assets modes | Preserves every project view without duplicating workspace chrome. |
| 2026-08-12 | Orange primary, cream marketing, neutral app | Connects the brand across marketing and product while preserving workspace clarity. |
| 2026-08-12 | Figtree + Geist Mono | Keeps creative brand expression and developer readability in one family of interfaces. |
| 2026-08-15 | Unified black Studio, Canvas, and Pricing | Pricing retains a clear comparison structure while matching the cinematic product environment. |
| 2026-08-15 | Studio has no sidebar | The media wall and composer are the product; permanent navigation would dilute focus and reduce working width. |
| 2026-08-15 | Independent BeatDesign pricing route | Pricing is linkable, uses consistent card geometry, and preserves BeatAPI price-comparison behavior. |
| 2026-08-18 | Studio/Canvas composers unified on the beat token system | Both composers share one vocabulary (`@/components/app/composer-styles.ts`): the homepage hero card recipe, `--beat-*` tokens, and one orange Generate action. `--beat-studio-*` became aliases; `--beatcanvas-*` surface/ink tokens map onto the beat neutral scale. |
| 2026-08-18 | Canvas graph semantics moved from purple to cold blue (`--beat-graph` #7fb0f2) | The previous purple graph accent is retired. Cold blue selection/edges/minimap echoes the Start Here thumbnails the product opens with, and keeps "selected" visually distinct from the orange action color. |
| 2026-08-18 | Start Here empty state is the protected quality bar | The four cold-blue thumbnails + "Create Here" stay untouched; dark product surfaces are tuned around them. |
17 changes: 0 additions & 17 deletions messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"product.shell.create": "Create",
"product.shell.studio": "Studio",
"product.shell.canvas": "Canvas",
"product.shell.pricing": "Pricing",
"product.shell.projects": "Projects",
"product.shell.openNavigation": "Open navigation",
"product.shell.closeNavigation": "Close navigation",
Expand Down Expand Up @@ -51,22 +50,6 @@
"product.history.startFirst": "Start your first project",
"product.history.emptyDescription": "One project keeps your Studio generations and Canvas workflow together.",
"product.history.newProject": "New project",
"product.pricing.titleLead": "Pay less for the same ",
"product.pricing.titleAccent": "models.",
"product.pricing.descriptionLead": "Pay only for what you generate.",
"product.pricing.descriptionRest": "BeatDesign is powered by BeatAPI. One balance, every workflow.",
"product.pricing.statCheaperHint": "cheaper than competitor",
"product.pricing.statSavingsHint": "average savings",
"product.pricing.filterAll": "All models",
"product.pricing.filterVideo": "AI Video",
"product.pricing.filterImage": "AI Image",
"product.pricing.search": "Search model or spec",
"product.pricing.empty": "No matching prices.",
"product.pricing.colSpec": "Spec",
"product.pricing.colCompetitor": "API Competitor (R/F)",
"product.pricing.colHiggsfield": "Competitor H",
"product.pricing.colDiscount": "Discount",
"product.pricing.ctaPrimary": "Start creating",
"common.pages.back_to_home": "Back to home",
"common.pages.last_updated": "Last updated",
"common.pages.not_found": "Page not found",
Expand Down
17 changes: 0 additions & 17 deletions messages/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"product.shell.create": "创作",
"product.shell.studio": "Studio",
"product.shell.canvas": "Canvas",
"product.shell.pricing": "价格",
"product.shell.projects": "项目",
"product.shell.openNavigation": "打开导航",
"product.shell.closeNavigation": "关闭导航",
Expand Down Expand Up @@ -51,22 +50,6 @@
"product.history.startFirst": "开始你的第一个项目",
"product.history.emptyDescription": "一个项目同时保存 Studio 生成结果和 Canvas 工作流。",
"product.history.newProject": "新建项目",
"product.pricing.titleLead": "同样的模型,更低的",
"product.pricing.titleAccent": "价格。",
"product.pricing.descriptionLead": "只为生成的内容付费。",
"product.pricing.descriptionRest": "BeatDesign 由 BeatAPI 驱动。一个余额,贯穿每个工作流。",
"product.pricing.statCheaperHint": "对比竞品最高优惠",
"product.pricing.statSavingsHint": "平均节省",
"product.pricing.filterAll": "所有模型",
"product.pricing.filterVideo": "AI 视频",
"product.pricing.filterImage": "AI 图片",
"product.pricing.search": "搜索模型或规格",
"product.pricing.empty": "没有符合条件的价格。",
"product.pricing.colSpec": "规格",
"product.pricing.colCompetitor": "API 竞品 (R/F)",
"product.pricing.colHiggsfield": "竞品 H",
"product.pricing.colDiscount": "优惠",
"product.pricing.ctaPrimary": "开始创作",
"common.pages.back_to_home": "返回首页",
"common.pages.last_updated": "最近更新",
"common.pages.not_found": "页面不存在",
Expand Down
5 changes: 3 additions & 2 deletions src/components/marketing/beatapi-product-shell.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ const source = readFileSync(
'utf8'
);

test('product shell keeps workspace navigation with pricing and hides SaaS auth', () => {
test('product shell keeps workspace navigation and hides SaaS auth', () => {
assert.match(source, /label: copy\.home, href: '\/'/);
assert.match(source, /label: copy\.create, href: '\/studio'/);
assert.match(source, /href: '\/pricing'/);
assert.match(source, /href: '\/projects'/);
assert.doesNotMatch(source, /href: '\/pricing'/);
assert.doesNotMatch(source, /ProductSurface.*pricing/);
assert.doesNotMatch(source, /href="\/sign-in"/);
assert.doesNotMatch(source, /useSession/);
});
13 changes: 5 additions & 8 deletions src/components/marketing/beatapi-product-shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { appConfig } from '@/config';
import { m } from '@/paraglide/messages.js';
import { setLocale } from '@/paraglide/runtime.js';

type ProductSurface = 'home' | 'projects' | 'pricing';
type ProductSurface = 'home' | 'projects';

type ProductLocale = 'en' | 'zh';

Expand All @@ -25,7 +25,6 @@ function getShellCopy(locale: string) {
return {
home: m['product.shell.home']({}, { locale: messageLocale }),
create: m['product.shell.create']({}, { locale: messageLocale }),
pricing: m['product.shell.pricing']({}, { locale: messageLocale }),
projects: m['product.shell.projects']({}, { locale: messageLocale }),
openNavigation: m['product.shell.openNavigation'](
{},
Expand All @@ -52,15 +51,13 @@ function getNavItems(copy: ShellCopy) {
return [
{ label: copy.home, href: '/' },
{ label: copy.create, href: '/studio' },
{ label: copy.pricing, href: '/pricing' },
{ label: copy.projects, href: '/projects' },
] as const;
}

const activeNavHref: Record<ProductSurface, string> = {
home: '/',
projects: '/projects',
pricing: '/pricing',
};

export function Brand({ href = '/' }: { href?: string }) {
Expand Down Expand Up @@ -89,7 +86,7 @@ function CapsuleNav({
}) {
const activeHref = activeNavHref[active];
return (
<nav className="absolute left-1/2 hidden -translate-x-1/2 items-center gap-8 text-sm font-medium lg:flex">
<nav className="absolute left-1/2 hidden -translate-x-1/2 items-center gap-5 text-sm font-medium md:flex lg:gap-8">
{items.map((item) => {
const isActive = item.href === activeHref;
return (
Expand Down Expand Up @@ -201,7 +198,7 @@ export function BeatApiProductShell({
<CapsuleNav active={active} items={navItems} />

<div className="ml-auto flex items-center gap-2">
<div className="hidden lg:block">
<div className="hidden md:block">
<LocaleMenu locale={currentLocale} copy={copy} />
</div>

Expand All @@ -212,15 +209,15 @@ export function BeatApiProductShell({
}
aria-expanded={menuOpen}
onClick={() => setMenuOpen((open) => !open)}
className="grid size-10 place-items-center rounded-full border border-white/10 bg-white/[0.035] text-white transition hover:border-white/20 lg:hidden"
className="grid size-10 place-items-center rounded-full border border-white/10 bg-white/[0.035] text-white transition hover:border-white/20 md:hidden"
>
{menuOpen ? <X className="size-4" /> : <Menu className="size-4" />}
</button>
</div>
</div>

{menuOpen ? (
<div className="absolute inset-x-4 top-[calc(100%+8px)] rounded-[18px] border border-white/10 bg-[#111113]/95 p-2 shadow-2xl backdrop-blur-xl lg:hidden">
<div className="absolute inset-x-4 top-[calc(100%+8px)] rounded-[18px] border border-white/10 bg-[#111113]/95 p-2 shadow-2xl backdrop-blur-xl md:hidden">
{navItems.map((item) => (
<Link
key={item.href}
Expand Down
43 changes: 0 additions & 43 deletions src/components/pricing/beatapi-pricing-page.test.ts

This file was deleted.

Loading
Loading