@utopia-studio-design/design-system-cli 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -10
- package/bin/utopia-ds-mcp.mjs +6 -2
- package/bin/utopia-ds.mjs +94 -21
- package/data/docs/arabic-friendly.md +19 -2
- package/data/docs/billing-setup.md +80 -0
- package/data/docs/cer-003-032-033-round-8-repair.md +41 -0
- package/data/docs/cer-007-checkout.md +51 -0
- package/data/docs/cer-008-011-qa-repair.md +64 -0
- package/data/docs/cer-016-023-round-6-repair.md +92 -0
- package/data/docs/cer-024-031-round-7-repair.md +51 -0
- package/data/docs/cer-033-035-036-round-12-repair.md +37 -0
- package/data/docs/cer-035-037-round-10-repair.md +34 -0
- package/data/docs/charts.md +34 -3
- package/data/docs/chat-service-methodology.md +181 -0
- package/data/docs/code-block.md +20 -0
- package/data/docs/creative-production.md +149 -0
- package/data/docs/designer-agent-backend-contract.md +552 -0
- package/data/docs/email-resend.md +29 -0
- package/data/docs/foundations.md +46 -8
- package/data/docs/guide.md +9 -9
- package/data/docs/handover-billing-aiden.md +132 -0
- package/data/docs/logo-maker-methodology.md +109 -0
- package/data/docs/presentation-asset-repository.md +53 -0
- package/data/docs/product-architecture.md +42 -0
- package/data/docs/quick-start-ai.md +4 -4
- package/data/docs/shadcn-conversion.md +1 -1
- package/data/docs/sidebar-placement.md +56 -0
- package/data/docs/theme-authoring.md +6 -3
- package/data/manifests/catalog.json +42 -46
- package/data/manifests/components.json +317 -66
- package/data/manifests/logo-skills.json +174 -0
- package/data/manifests/motion-profiles.json +2 -2
- package/data/manifests/patterns.json +201 -7
- package/data/manifests/presentation-assets.schema.json +44 -0
- package/data/manifests/templates.json +87 -1
- package/data/manifests/theme-dextrum.json +7 -7
- package/data/manifests/theme-prism-mono.json +19 -0
- package/data/manifests/theme-utopia-cloudblur.json +135 -0
- package/data/manifests/theme-utopia-default.json +31 -31
- package/data/manifests/theme-vyapti.json +17 -1
- package/data/manifests/theme-y2k-pop.json +18 -0
- package/data/manifests/themes.json +285 -57
- package/data/templates/analytics-dashboard/main.tsx +14 -1
- package/data/templates/calendar-application/main.tsx +14 -1
- package/data/templates/chat-workspace/README.md +12 -0
- package/data/templates/chat-workspace/index.html +11 -0
- package/data/templates/chat-workspace/main.tsx +97 -0
- package/data/templates/chat-workspace/styles.css +19 -0
- package/data/templates/chat-workspace/template.manifest.json +13 -0
- package/data/templates/composition-registry-lab/main.tsx +12 -1
- package/data/templates/dashboard-composer/main.tsx +14 -1
- package/data/templates/database-workspace/main.tsx +14 -1
- package/data/templates/interaction-lab/main.tsx +12 -1
- package/data/templates/quantum-verification-story/README.md +12 -0
- package/data/templates/quantum-verification-story/components.json +21 -0
- package/data/templates/quantum-verification-story/eslint.config.mjs +13 -0
- package/data/templates/quantum-verification-story/next-env.d.ts +7 -0
- package/data/templates/quantum-verification-story/next.config.mjs +11 -0
- package/data/templates/quantum-verification-story/package-lock.json +7535 -0
- package/data/templates/quantum-verification-story/package.json +46 -0
- package/data/templates/quantum-verification-story/postcss.config.js +6 -0
- package/data/templates/quantum-verification-story/src/app/assessment/page.tsx +138 -0
- package/data/templates/quantum-verification-story/src/app/ceramic-theme.css +48 -0
- package/data/templates/quantum-verification-story/src/app/deployment/page.tsx +178 -0
- package/data/templates/quantum-verification-story/src/app/fonts/IBMPlexSansArabic-Bold.ttf +0 -0
- package/data/templates/quantum-verification-story/src/app/fonts/IBMPlexSansArabic-Medium.ttf +0 -0
- package/data/templates/quantum-verification-story/src/app/fonts/IBMPlexSansArabic-Regular.ttf +0 -0
- package/data/templates/quantum-verification-story/src/app/fonts/ReadexPro-Variable.ttf +0 -0
- package/data/templates/quantum-verification-story/src/app/fonts/TWKLausanne-350.ttf +0 -0
- package/data/templates/quantum-verification-story/src/app/fonts/TWKLausanne-500.ttf +0 -0
- package/data/templates/quantum-verification-story/src/app/fonts/TWKLausanne-700.ttf +0 -0
- package/data/templates/quantum-verification-story/src/app/globals.css +175 -0
- package/data/templates/quantum-verification-story/src/app/layout.tsx +45 -0
- package/data/templates/quantum-verification-story/src/app/not-found.tsx +39 -0
- package/data/templates/quantum-verification-story/src/app/page.tsx +24 -0
- package/data/templates/quantum-verification-story/src/app/products/page.tsx +132 -0
- package/data/templates/quantum-verification-story/src/app/utopia-default.css +312 -0
- package/data/templates/quantum-verification-story/src/components/RotatingText.css +36 -0
- package/data/templates/quantum-verification-story/src/components/RotatingText.tsx +189 -0
- package/data/templates/quantum-verification-story/src/components/brand-mark.tsx +14 -0
- package/data/templates/quantum-verification-story/src/components/console-preview-section.tsx +57 -0
- package/data/templates/quantum-verification-story/src/components/console-preview.tsx +72 -0
- package/data/templates/quantum-verification-story/src/components/fade-up.tsx +54 -0
- package/data/templates/quantum-verification-story/src/components/floating-network.tsx +125 -0
- package/data/templates/quantum-verification-story/src/components/footer.tsx +83 -0
- package/data/templates/quantum-verification-story/src/components/harvest-timeline.tsx +70 -0
- package/data/templates/quantum-verification-story/src/components/lattice-diagram.tsx +73 -0
- package/data/templates/quantum-verification-story/src/components/lattice-field.tsx +129 -0
- package/data/templates/quantum-verification-story/src/components/nav.tsx +104 -0
- package/data/templates/quantum-verification-story/src/components/presentation-layout.tsx +91 -0
- package/data/templates/quantum-verification-story/src/components/readiness-estimator.tsx +135 -0
- package/data/templates/quantum-verification-story/src/components/scramble-text.tsx +60 -0
- package/data/templates/quantum-verification-story/src/components/section-backdrop.tsx +38 -0
- package/data/templates/quantum-verification-story/src/components/sections/clients.tsx +140 -0
- package/data/templates/quantum-verification-story/src/components/sections/contact.tsx +125 -0
- package/data/templates/quantum-verification-story/src/components/sections/hero.tsx +92 -0
- package/data/templates/quantum-verification-story/src/components/sections/moat.tsx +73 -0
- package/data/templates/quantum-verification-story/src/components/sections/offer.tsx +113 -0
- package/data/templates/quantum-verification-story/src/components/sections/pilot.tsx +63 -0
- package/data/templates/quantum-verification-story/src/components/sections/problem.tsx +53 -0
- package/data/templates/quantum-verification-story/src/components/text-particle.tsx +204 -0
- package/data/templates/quantum-verification-story/src/components/ticker.tsx +27 -0
- package/data/templates/quantum-verification-story/src/components/ui/badge.tsx +29 -0
- package/data/templates/quantum-verification-story/src/components/ui/button.tsx +55 -0
- package/data/templates/quantum-verification-story/src/components/ui/card.tsx +46 -0
- package/data/templates/quantum-verification-story/src/components/ui/cursor-driven-particle-typography.tsx +288 -0
- package/data/templates/quantum-verification-story/src/components/ui/input.tsx +20 -0
- package/data/templates/quantum-verification-story/src/components/ui/label.tsx +21 -0
- package/data/templates/quantum-verification-story/src/components/ui/separator.tsx +25 -0
- package/data/templates/quantum-verification-story/src/components/ui/textarea.tsx +19 -0
- package/data/templates/quantum-verification-story/src/lib/utils.ts +6 -0
- package/data/templates/quantum-verification-story/tailwind.config.js +95 -0
- package/data/templates/quantum-verification-story/template.manifest.json +23 -0
- package/data/templates/quantum-verification-story/tsconfig.json +42 -0
- package/data/templates/saas-solution-homepage/main.tsx +91 -14
- package/data/templates/saas-solution-homepage/privacy/index.html +1 -0
- package/data/templates/saas-solution-homepage/styles.css +47 -2
- package/data/templates/saas-solution-homepage/team/index.html +1 -0
- package/data/templates/saas-solution-homepage/template.manifest.json +41 -0
- package/data/templates/saas-solution-homepage/terms/index.html +1 -0
- package/lib/api.mjs +52 -3
- package/lib/template-runtime.mjs +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as SeparatorPrimitive from "@radix-ui/react-separator"
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
const Separator = React.forwardRef<
|
|
8
|
+
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
|
9
|
+
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
|
10
|
+
>(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (
|
|
11
|
+
<SeparatorPrimitive.Root
|
|
12
|
+
ref={ref}
|
|
13
|
+
decorative={decorative}
|
|
14
|
+
orientation={orientation}
|
|
15
|
+
className={cn(
|
|
16
|
+
"shrink-0 bg-rule",
|
|
17
|
+
orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
))
|
|
23
|
+
Separator.displayName = SeparatorPrimitive.Root.displayName
|
|
24
|
+
|
|
25
|
+
export { Separator }
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "@/lib/utils"
|
|
3
|
+
|
|
4
|
+
export type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>
|
|
5
|
+
|
|
6
|
+
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
7
|
+
({ className, ...props }, ref) => (
|
|
8
|
+
<textarea
|
|
9
|
+
className={cn(
|
|
10
|
+
"flex min-h-[100px] w-full bg-[var(--surface)] border border-[var(--border)] px-4 py-3 text-sm font-sans text-[var(--foreground)] placeholder:text-[var(--muted-foreground)] focus-visible:outline-none focus-visible:border-[var(--primary)] transition-colors duration-200 resize-vertical disabled:cursor-not-allowed disabled:opacity-50",
|
|
11
|
+
className
|
|
12
|
+
)}
|
|
13
|
+
ref={ref}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
)
|
|
17
|
+
)
|
|
18
|
+
Textarea.displayName = "Textarea"
|
|
19
|
+
export { Textarea }
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/** @type {import('tailwindcss').Config} */
|
|
2
|
+
module.exports = {
|
|
3
|
+
darkMode: ["class"],
|
|
4
|
+
content: [
|
|
5
|
+
'./pages/**/*.{js,ts,jsx,tsx}',
|
|
6
|
+
'./components/**/*.{js,ts,jsx,tsx}',
|
|
7
|
+
'./app/**/*.{js,ts,jsx,tsx}',
|
|
8
|
+
'./src/**/*.{js,ts,jsx,tsx}',
|
|
9
|
+
],
|
|
10
|
+
theme: {
|
|
11
|
+
extend: {
|
|
12
|
+
fontFamily: {
|
|
13
|
+
sans: ['var(--font-sans)'],
|
|
14
|
+
display: ['var(--font-sans)'],
|
|
15
|
+
serif: ['var(--font-sans)'],
|
|
16
|
+
mono: ['var(--font-mono)'],
|
|
17
|
+
},
|
|
18
|
+
colors: {
|
|
19
|
+
border: 'var(--border)',
|
|
20
|
+
input: 'var(--input)',
|
|
21
|
+
ring: 'var(--ring)',
|
|
22
|
+
background: 'var(--background)',
|
|
23
|
+
foreground: 'var(--foreground)',
|
|
24
|
+
primary: {
|
|
25
|
+
DEFAULT: 'var(--primary)',
|
|
26
|
+
foreground: 'var(--primary-foreground)',
|
|
27
|
+
},
|
|
28
|
+
secondary: {
|
|
29
|
+
DEFAULT: 'var(--secondary)',
|
|
30
|
+
foreground: 'var(--secondary-foreground)',
|
|
31
|
+
},
|
|
32
|
+
muted: {
|
|
33
|
+
DEFAULT: 'var(--muted)',
|
|
34
|
+
foreground: 'var(--muted-foreground)',
|
|
35
|
+
},
|
|
36
|
+
accent: {
|
|
37
|
+
DEFAULT: 'var(--accent)',
|
|
38
|
+
foreground: 'var(--accent-foreground)',
|
|
39
|
+
},
|
|
40
|
+
destructive: {
|
|
41
|
+
DEFAULT: 'var(--destructive)',
|
|
42
|
+
foreground: 'var(--destructive-foreground)',
|
|
43
|
+
},
|
|
44
|
+
card: {
|
|
45
|
+
DEFAULT: 'var(--surface)',
|
|
46
|
+
foreground: 'var(--foreground)',
|
|
47
|
+
},
|
|
48
|
+
popover: {
|
|
49
|
+
DEFAULT: 'var(--surface)',
|
|
50
|
+
foreground: 'var(--foreground)',
|
|
51
|
+
},
|
|
52
|
+
gold: { DEFAULT: '#b8933a', light: '#d0a055', pale: '#f7edda' },
|
|
53
|
+
deep: { DEFAULT: '#1a3a2a', mid: '#1e573b', light: '#daeee4' },
|
|
54
|
+
ink: '#1c1a17',
|
|
55
|
+
rule: '#e5e3df',
|
|
56
|
+
},
|
|
57
|
+
borderRadius: {
|
|
58
|
+
lg: 'var(--radius)',
|
|
59
|
+
md: 'calc(var(--radius) - 2px)',
|
|
60
|
+
sm: 'calc(var(--radius) - 4px)',
|
|
61
|
+
},
|
|
62
|
+
maxWidth: { container: '1200px' },
|
|
63
|
+
keyframes: {
|
|
64
|
+
ticker: {
|
|
65
|
+
from: { transform: 'translateX(0)' },
|
|
66
|
+
to: { transform: 'translateX(-50%)' },
|
|
67
|
+
},
|
|
68
|
+
'fade-up': {
|
|
69
|
+
from: { opacity: '0', transform: 'translateY(16px)' },
|
|
70
|
+
to: { opacity: '1', transform: 'translateY(0)' },
|
|
71
|
+
},
|
|
72
|
+
'pulse-dot': {
|
|
73
|
+
'0%, 100%': { opacity: '0.6', transform: 'scale(0.9)' },
|
|
74
|
+
'50%': { opacity: '1', transform: 'scale(1.1)' },
|
|
75
|
+
},
|
|
76
|
+
'accordion-down': {
|
|
77
|
+
from: { height: '0' },
|
|
78
|
+
to: { height: 'var(--radix-accordion-content-height)' },
|
|
79
|
+
},
|
|
80
|
+
'accordion-up': {
|
|
81
|
+
from: { height: 'var(--radix-accordion-content-height)' },
|
|
82
|
+
to: { height: '0' },
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
animation: {
|
|
86
|
+
ticker: 'ticker 35s linear infinite',
|
|
87
|
+
'fade-up': 'fade-up 0.5s ease-out',
|
|
88
|
+
'pulse-dot': 'pulse-dot 1.8s infinite',
|
|
89
|
+
'accordion-down': 'accordion-down 0.2s ease-out',
|
|
90
|
+
'accordion-up': 'accordion-up 0.2s ease-out',
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
plugins: [require('tailwindcss-animate')],
|
|
95
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "template-quantum-verification-story",
|
|
3
|
+
"name": "Quantum Verification Story",
|
|
4
|
+
"category": "website-page",
|
|
5
|
+
"status": "available",
|
|
6
|
+
"purpose": "A code-level Next.js and shadcn presentation-style website rebuilt from the vy3 project with neutral branding and Ceramic theme tokens.",
|
|
7
|
+
"sourceFormat": "nextjs-shadcn",
|
|
8
|
+
"themeable": true,
|
|
9
|
+
"sourcePath": "templates/quantum-verification-story/src/app/page.tsx",
|
|
10
|
+
"entryPath": "templates/quantum-verification-story/src/app/page.tsx",
|
|
11
|
+
"pages": [
|
|
12
|
+
{ "id": "home", "path": "src/app/page.tsx", "purpose": "Full-height verification narrative" },
|
|
13
|
+
{ "id": "products", "path": "src/app/products/page.tsx", "purpose": "Product detail" },
|
|
14
|
+
{ "id": "assessment", "path": "src/app/assessment/page.tsx", "purpose": "Readiness assessment" },
|
|
15
|
+
{ "id": "deployment", "path": "src/app/deployment/page.tsx", "purpose": "Continuous verification" }
|
|
16
|
+
],
|
|
17
|
+
"features": { "multiPage": true, "responsive": true, "shadcn": true, "presentationScroll": true, "ceramicThemeTokens": true },
|
|
18
|
+
"neverInvent": [
|
|
19
|
+
"Replace the Your Company placeholder with an approved brand name",
|
|
20
|
+
"Review all security, regulatory, and performance claims before release",
|
|
21
|
+
"Configure external console, form, and analytics integrations before production use"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": [
|
|
5
|
+
"dom",
|
|
6
|
+
"dom.iterable",
|
|
7
|
+
"esnext"
|
|
8
|
+
],
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"strict": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"esModuleInterop": true,
|
|
14
|
+
"module": "esnext",
|
|
15
|
+
"moduleResolution": "bundler",
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"isolatedModules": true,
|
|
18
|
+
"jsx": "react-jsx",
|
|
19
|
+
"incremental": true,
|
|
20
|
+
"plugins": [
|
|
21
|
+
{
|
|
22
|
+
"name": "next"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"baseUrl": ".",
|
|
26
|
+
"paths": {
|
|
27
|
+
"@/*": [
|
|
28
|
+
"./src/*"
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"include": [
|
|
33
|
+
"next-env.d.ts",
|
|
34
|
+
"**/*.ts",
|
|
35
|
+
"**/*.tsx",
|
|
36
|
+
".next/types/**/*.ts",
|
|
37
|
+
".next/dev/types/**/*.ts"
|
|
38
|
+
],
|
|
39
|
+
"exclude": [
|
|
40
|
+
"node_modules"
|
|
41
|
+
]
|
|
42
|
+
}
|
|
@@ -48,13 +48,24 @@ import {
|
|
|
48
48
|
} from '@utopia-studio-design/design-system'
|
|
49
49
|
import '@utopia-studio-design/design-system/core.css'
|
|
50
50
|
import '@utopia-studio-design/design-system/themes/utopia-default.css'
|
|
51
|
+
import '@utopia-studio-design/design-system/themes/dextrum.css'
|
|
52
|
+
import '@utopia-studio-design/design-system/themes/barrier-intelligence.css'
|
|
53
|
+
import '@utopia-studio-design/design-system/themes/renacore.css'
|
|
54
|
+
import '@utopia-studio-design/design-system/themes/vyapti.css'
|
|
55
|
+
import '@utopia-studio-design/design-system/themes/utopia-cloudblur.css'
|
|
51
56
|
import './styles.css'
|
|
52
57
|
|
|
53
|
-
const
|
|
58
|
+
const supportedThemes = ['utopia-default', 'dextrum', 'barrier-intelligence', 'renacore', 'vyapti', 'utopia-cloudblur'] as const
|
|
59
|
+
type TemplateTheme = (typeof supportedThemes)[number]
|
|
60
|
+
const defaultTheme: TemplateTheme = 'utopia-default'
|
|
61
|
+
const requestedTheme = new URLSearchParams(window.location.search).get('theme') as TemplateTheme | null
|
|
62
|
+
const activeTheme: TemplateTheme = requestedTheme && supportedThemes.includes(requestedTheme) ? requestedTheme : defaultTheme
|
|
63
|
+
document.documentElement.dataset.theme = activeTheme
|
|
64
|
+
document.documentElement.dataset.brand = activeTheme
|
|
54
65
|
|
|
55
66
|
type Locale = 'en' | 'ar'
|
|
56
67
|
type PreviewMode = 'overview' | 'automations' | 'insights'
|
|
57
|
-
type PageKey = 'home' | 'product' | 'agents' | 'integrations' | 'integration-detail' | 'customers' | 'customer-story' | 'pricing' | 'changelog' | 'contact-sales'
|
|
68
|
+
type PageKey = 'home' | 'product' | 'agents' | 'integrations' | 'integration-detail' | 'customers' | 'customer-story' | 'pricing' | 'team' | 'changelog' | 'contact-sales' | 'terms' | 'privacy'
|
|
58
69
|
|
|
59
70
|
type DemoData = {
|
|
60
71
|
product: string
|
|
@@ -78,14 +89,22 @@ function getPageKey(): PageKey {
|
|
|
78
89
|
if (route === 'customers') return 'customers'
|
|
79
90
|
if (route === 'customers/aster-labs') return 'customer-story'
|
|
80
91
|
if (route === 'pricing') return 'pricing'
|
|
92
|
+
if (route === 'team') return 'team'
|
|
81
93
|
if (route === 'changelog') return 'changelog'
|
|
82
94
|
if (route === 'contact-sales') return 'contact-sales'
|
|
95
|
+
if (route === 'terms') return 'terms'
|
|
96
|
+
if (route === 'privacy') return 'privacy'
|
|
83
97
|
return 'home'
|
|
84
98
|
}
|
|
85
99
|
|
|
86
100
|
function pageHref(page: string, seed: number) {
|
|
87
101
|
const suffix = page ? `/${page}/` : '/index.html'
|
|
88
|
-
return `${templateBase}${suffix}?seed=${seed}`
|
|
102
|
+
return `${templateBase}${suffix}?seed=${seed}&theme=${activeTheme}`
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function navigateTo(href: string) {
|
|
106
|
+
window.history.pushState(null, '', href)
|
|
107
|
+
window.dispatchEvent(new PopStateEvent('popstate'))
|
|
89
108
|
}
|
|
90
109
|
|
|
91
110
|
const copy = {
|
|
@@ -339,7 +358,7 @@ function PageCta({ locale, seed }: { locale: Locale; seed: number }) {
|
|
|
339
358
|
return (
|
|
340
359
|
<section className="section page-cta">
|
|
341
360
|
<div><p className="eyebrow">{locale === 'ar' ? 'الخطوة التالية' : 'Next step'}</p><h2>{locale === 'ar' ? 'حوّل أول سير عمل إلى نظام مشترك.' : 'Turn your first workflow into a shared system.'}</h2></div>
|
|
342
|
-
<Button endContent={<ArrowIcon />} onClick={() => {
|
|
361
|
+
<Button endContent={<ArrowIcon />} onClick={() => { navigateTo(pageHref('contact-sales', seed)) }} size="lg">{locale === 'ar' ? 'تحدث مع فريقنا' : 'Talk to our team'}</Button>
|
|
343
362
|
</section>
|
|
344
363
|
)
|
|
345
364
|
}
|
|
@@ -350,7 +369,7 @@ function ProductPage({ demo, locale, previewMode, seed, setPreviewMode }: { demo
|
|
|
350
369
|
: [['Intake', 'Turn email, forms, and events into structured work.'], ['Planning', 'Connect goals to projects and daily decisions.'], ['Execution', 'Keep ownership, approvals, and work state legible.'], ['Insights', 'Read bottlenecks and outcomes from one operating model.']]
|
|
351
370
|
return <>
|
|
352
371
|
<PageHero body={locale === 'ar' ? 'مساحة تشغيل واحدة تربط التخطيط والتنفيذ والأتمتة من دون فقدان السياق.' : 'One operating workspace connects planning, execution, and automation without losing context.'} eyebrow={locale === 'ar' ? 'المنتج' : 'Product'} title={locale === 'ar' ? 'خطّط للعمل وشغّله من النظام نفسه.' : 'Plan the work. Run it from the same system.'}>
|
|
353
|
-
<Button endContent={<ArrowIcon />} onClick={() => {
|
|
372
|
+
<Button endContent={<ArrowIcon />} onClick={() => { navigateTo(pageHref('contact-sales', seed)) }} size="lg">{locale === 'ar' ? 'اطلب عرضاً' : 'Request a demo'}</Button>
|
|
354
373
|
<Button onClick={() => document.getElementById('interface')?.scrollIntoView({ behavior: 'smooth' })} size="lg" variant="outline">{locale === 'ar' ? 'استكشف الواجهة' : 'Explore the interface'}</Button>
|
|
355
374
|
</PageHero>
|
|
356
375
|
<section className="section" id="interface"><SectionHeading body={locale === 'ar' ? 'تتغير الواجهات مع المهمة، لكنها تبقى متصلة بالبيانات نفسها.' : 'Views change with the task while staying connected to the same underlying data.'} eyebrow={locale === 'ar' ? 'نظام عمل حي' : 'A living work system'} title={locale === 'ar' ? 'كل فريق يرى ما يحتاجه، من دون إنشاء مصدر حقيقة جديد.' : 'Every team gets the view it needs without creating a new source of truth.'} /><ProductPreview locale={locale} mode={previewMode} product={demo.product} setMode={setPreviewMode} /></section>
|
|
@@ -365,7 +384,7 @@ function AgentsPage({ demo, locale, seed }: { demo: DemoData; locale: Locale; se
|
|
|
365
384
|
: [['Request triage', 'Read the request, set priority, and identify an owner before it reaches the team queue.'], ['Decision preparation', 'Collect context, options, and risk before the approval moment.'], ['Commitment follow-up', 'Watch deadlines and dependencies, escalating only when intervention is needed.']]
|
|
366
385
|
return <>
|
|
367
386
|
<PageHero body={locale === 'ar' ? 'وكلاء يعملون داخل حدود واضحة، ويتركون القرارات الحساسة للأشخاص المناسبين.' : 'Agents work inside explicit boundaries and leave consequential decisions to the right people.'} eyebrow={locale === 'ar' ? 'الوكلاء والأتمتة' : 'Agents & automation'} title={locale === 'ar' ? 'أتمت العمل المتكرر، لا المساءلة.' : 'Automate the repeatable work, not accountability.'}>
|
|
368
|
-
<Button endContent={<ArrowIcon />} onClick={() => {
|
|
387
|
+
<Button endContent={<ArrowIcon />} onClick={() => { navigateTo(pageHref('contact-sales', seed)) }} size="lg">{locale === 'ar' ? 'صمّم أول وكيل' : 'Design your first agent'}</Button>
|
|
369
388
|
</PageHero>
|
|
370
389
|
<section className="section agents-detail-layout"><div><p className="eyebrow">{locale === 'ar' ? 'داخل سير العمل' : 'Inside the workflow'}</p><h2>{locale === 'ar' ? 'كل إجراء قابل للفهم والمراجعة والإيقاف.' : 'Every action can be understood, reviewed, and stopped.'}</h2><p>{locale === 'ar' ? 'يشاهد الفريق الإشارة والقاعدة والنتيجة في المكان نفسه.' : 'The team sees the signal, rule, and outcome in one place.'}</p></div><div className="agent-console"><div className="console-head"><span><Bot aria-hidden="true" />{locale === 'ar' ? 'وكيل الفرز' : 'Triage agent'}</span><Badge variant="success">{locale === 'ar' ? 'نشط' : 'Active'}</Badge></div><div className="agent-flow"><div><span>01</span><p>{locale === 'ar' ? 'تم اكتشاف إشارة جديدة' : 'New signal detected'}</p><small>09:42</small></div><i /><div><span>02</span><p>{locale === 'ar' ? 'تم تجهيز السياق والاقتراح' : 'Context and recommendation prepared'}</p><small>09:43</small></div><i /><div className="requires-review"><span>03</span><p>{locale === 'ar' ? 'مطلوب اعتماد بشري' : 'Human approval required'}</p><Button size="sm">{locale === 'ar' ? 'مراجعة' : 'Review'}</Button></div></div><div className="console-note"><ShieldCheck aria-hidden="true" /><span>{locale === 'ar' ? `جميع الإجراءات مسجلة في ${demo.product}.` : `Every action is recorded in ${demo.product}.`}</span></div></div></section>
|
|
371
390
|
<section className="section"><SectionHeading body={locale === 'ar' ? 'نقاط بداية عملية يمكن مراجعتها قبل التوسع.' : 'Practical starting points that can be reviewed before they scale.'} eyebrow={locale === 'ar' ? 'أنماط الوكلاء' : 'Agent patterns'} title={locale === 'ar' ? 'ابدأ من عمل يعرفه فريقك بالفعل.' : 'Begin with work your team already understands.'} /><div className="security-grid">{useCases.map(([title, body]) => <Card accent="hover" key={title}><CardHeader><div className="security-icon"><Sparkles aria-hidden="true" /></div><CardTitle>{title}</CardTitle><CardDescription>{body}</CardDescription></CardHeader></Card>)}</div></section>
|
|
@@ -416,7 +435,7 @@ function PricingPage({ demo, locale, seed }: { demo: DemoData; locale: Locale; s
|
|
|
416
435
|
: [['Starter', `$${demo.price}`, 'For a team launching its first workflow.', ['5 core members', 'Native integrations', 'Activity history']], ['Scale', `$${demo.price + 30}`, 'For teams connecting multiple operations.', ['Unlimited members', 'Agents and automation', 'Advanced analytics']], ['Enterprise', 'Custom', 'For governance and regional scale.', ['Granular access', 'Data regions', 'Dedicated support']]]
|
|
417
436
|
return <>
|
|
418
437
|
<PageHero body={locale === 'ar' ? 'ابدأ صغيراً مع كل الأساسيات، ثم أضف الأتمتة والحوكمة عندما تحتاجها.' : 'Start small with the full foundation, then add automation and governance when needed.'} eyebrow={locale === 'ar' ? 'الأسعار' : 'Pricing'} title={locale === 'ar' ? 'سعر واضح لكل مرحلة من الاعتماد.' : 'Clear pricing for every stage of adoption.'} />
|
|
419
|
-
<section className="section pricing-page-grid">{plans.map(([name, price, description, features], index) => <Card accent={index === 1 ? 'always' : 'hover'} key={name as string}><CardHeader><Badge variant={index === 1 ? 'default' : 'outline'}>{index === 1 ? (locale === 'ar' ? 'الأكثر اختياراً' : 'Most selected') : (locale === 'ar' ? 'خطة' : 'Plan')}</Badge><CardTitle>{name as string}</CardTitle><CardDescription>{description as string}</CardDescription></CardHeader><CardContent><div className="plan-price"><strong>{price as string}</strong>{price !== 'Custom' && price !== 'مخصص' ? <span>{locale === 'ar' ? '/ شهرياً' : '/ month'}</span> : null}</div><ul>{(features as string[]).map((feature) => <li key={feature}><Check aria-hidden="true" />{feature}</li>)}</ul><Button onClick={() => {
|
|
438
|
+
<section className="section pricing-page-grid">{plans.map(([name, price, description, features], index) => <Card accent={index === 1 ? 'always' : 'hover'} key={name as string}><CardHeader><Badge variant={index === 1 ? 'default' : 'outline'}>{index === 1 ? (locale === 'ar' ? 'الأكثر اختياراً' : 'Most selected') : (locale === 'ar' ? 'خطة' : 'Plan')}</Badge><CardTitle>{name as string}</CardTitle><CardDescription>{description as string}</CardDescription></CardHeader><CardContent><div className="plan-price"><strong>{price as string}</strong>{price !== 'Custom' && price !== 'مخصص' ? <span>{locale === 'ar' ? '/ شهرياً' : '/ month'}</span> : null}</div><ul>{(features as string[]).map((feature) => <li key={feature}><Check aria-hidden="true" />{feature}</li>)}</ul><Button onClick={() => { navigateTo(pageHref('contact-sales', seed)) }} size="lg" variant={index === 1 ? 'default' : 'outline'}>{locale === 'ar' ? 'اختر الخطة' : 'Choose plan'}</Button></CardContent></Card>)}</section>
|
|
420
439
|
<section className="section comparison-section"><SectionHeading body={locale === 'ar' ? 'قارن الحدود التي تتغير مع حجم المؤسسة.' : 'Compare the controls that change with organizational scale.'} eyebrow={locale === 'ar' ? 'المقارنة' : 'Compare'} title={locale === 'ar' ? 'الأساس مشترك في كل الخطط.' : 'The foundation is shared across every plan.'} /><div className="comparison-table" role="table"><div role="row"><strong role="columnheader">{locale === 'ar' ? 'القدرة' : 'Capability'}</strong><strong role="columnheader">Starter</strong><strong role="columnheader">Scale</strong><strong role="columnheader">Enterprise</strong></div>{[['Workflows', '3', 'Unlimited', 'Unlimited'], ['Automation runs', '1k', '25k', 'Custom'], ['Audit history', '90 days', '1 year', 'Custom'], ['Data regions', '—', '2', '4']].map((row) => <div role="row" key={row[0]}>{row.map((cell) => <span role="cell" key={cell}>{cell}</span>)}</div>)}</div></section>
|
|
421
440
|
<section className="section faq-section"><SectionHeading body={locale === 'ar' ? 'إجابات مختصرة قبل البدء.' : 'Short answers before you begin.'} eyebrow={locale === 'ar' ? 'الأسئلة الشائعة' : 'FAQ'} title={locale === 'ar' ? 'ما تحتاج إلى معرفته.' : 'What you need to know.'} /><div>{(locale === 'ar' ? [['هل يمكن تغيير الخطة؟', 'نعم، يمكن الترقية أو التخفيض مع بداية دورة الفوترة التالية.'], ['هل يشمل السعر المشاهدين؟', 'المشاهدون بلا حد في جميع الخطط.'], ['هل تتوفر تجربة؟', 'تتضمن كل مساحة عمل فترة تقييم موجهة.']] : [['Can I change plans?', 'Yes. Upgrade or downgrade at the next billing cycle.'], ['Are viewers included?', 'Unlimited viewers are included on every plan.'], ['Is there a trial?', 'Every workspace includes a guided evaluation period.']]).map(([question, answer]) => <details key={question}><summary>{question}</summary><p>{answer}</p></details>)}</div></section>
|
|
422
441
|
</>
|
|
@@ -441,6 +460,31 @@ function ContactSalesPage({ demo, locale }: { demo: DemoData; locale: Locale })
|
|
|
441
460
|
return <section className="contact-layout" id="top"><div><p className="eyebrow">{locale === 'ar' ? 'تواصل مع المبيعات' : 'Contact sales'}</p><h1>{locale === 'ar' ? 'صمّم مسار اعتماد يناسب فريقك.' : 'Design an adoption path for your team.'}</h1><p>{locale === 'ar' ? 'اطلب عرضاً، اختر الخطة المناسبة، واحصل على مساعدة في الإعداد.' : 'Request a demo, choose the right plan, and get help with setup.'}</p><ul>{(locale === 'ar' ? ['عرض مخصص لسير عملك', 'توصية بالخطة والحوكمة', 'دعم للإعداد والتكامل'] : ['A demo tailored to your workflow', 'Plan and governance recommendation', 'Setup and integration support']).map((item) => <li key={item}><Check aria-hidden="true" />{item}</li>)}</ul></div><form onSubmit={(event) => { event.preventDefault(); setSubmitted(true) }}><label>{locale === 'ar' ? 'الاسم' : 'Name'}<TextInput name="name" required /></label><label>{locale === 'ar' ? 'البريد للعمل' : 'Work email'}<TextInput name="email" required type="email" /></label><label>{locale === 'ar' ? 'الشركة' : 'Company'}<TextInput name="company" required /></label><label>{locale === 'ar' ? 'حجم الفريق' : 'Team size'}<Select name="team-size"><SelectTrigger aria-label={locale === 'ar' ? 'حجم الفريق' : 'Team size'}><SelectValue placeholder={locale === 'ar' ? 'اختر' : 'Select'} /></SelectTrigger><SelectContent><SelectItem value="1-20">1–20</SelectItem><SelectItem value="21-100">21–100</SelectItem><SelectItem value="101-500">101–500</SelectItem><SelectItem value="500-plus">500+</SelectItem></SelectContent></Select></label><label>{locale === 'ar' ? 'ما سير العمل الذي تريد تحسينه؟' : 'Which workflow do you want to improve?'}<Textarea name="workflow" rows={5} /></label><Button endContent={<ArrowIcon />} size="lg" type="submit">{locale === 'ar' ? 'إرسال الطلب' : 'Submit request'}</Button><small>{locale === 'ar' ? 'بيانات تجريبية فقط — لا يتم إرسالها إلى خدمة خارجية.' : 'Demo only — data is not sent to an external service.'}</small></form></section>
|
|
442
461
|
}
|
|
443
462
|
|
|
463
|
+
function LegalPage({ demo, kind, locale }: { demo: DemoData; kind: 'terms' | 'privacy'; locale: Locale }) {
|
|
464
|
+
const isPrivacy = kind === 'privacy'
|
|
465
|
+
const sections = isPrivacy
|
|
466
|
+
? (locale === 'ar' ? [['المعلومات التي نجمعها', 'قد نجمع معلومات الحساب وبيانات الاتصال وسجلات الاستخدام والمعلومات التي تختار إرسالها إلى الخدمة.'], ['كيف نستخدم المعلومات', 'نستخدم المعلومات لتقديم الخدمة وتأمينها وتحسينها والتواصل معك والامتثال للالتزامات القانونية.'], ['المشاركة والمعالجون', 'قد نشارك البيانات مع مزودي الخدمة المعتمدين عند الحاجة لتشغيل المنتج، ولا نبيع معلوماتك الشخصية.'], ['الاحتفاظ والأمان', 'نحتفظ بالمعلومات للمدة اللازمة للأغراض الموضحة ونطبق ضوابط تقنية وتنظيمية مناسبة.'], ['اختياراتك وحقوقك', 'يمكنك طلب الوصول أو التصحيح أو الحذف أو التصدير، وفقاً للقانون المعمول به.'], ['تواصل معنا', 'للأسئلة أو الطلبات المتعلقة بالخصوصية، استخدم قناة الاتصال المنشورة من قبل شركتك.']] : [['Information we collect', 'We may collect account details, contact information, usage logs, and information you choose to submit to the service.'], ['How we use information', 'We use information to provide, secure, and improve the service, communicate with you, and meet legal obligations.'], ['Sharing and processors', 'We may share data with approved service providers when needed to operate the product. We do not sell personal information.'], ['Retention and security', 'We retain information for as long as necessary for the stated purposes and apply appropriate technical and organizational safeguards.'], ['Your choices and rights', 'You may request access, correction, deletion, or export where provided by applicable law.'], ['Contact us', 'For privacy questions or requests, use the contact channel published by your organization.']])
|
|
467
|
+
: (locale === 'ar' ? [['استخدام الخدمة', 'يجوز لك استخدام الخدمة فقط بما يتوافق مع هذه الشروط والقوانين المعمول بها ووثائق الحساب.'], ['الحسابات والوصول', 'أنت مسؤول عن بيانات الاعتماد ونشاط الحساب والأشخاص الذين تسمح لهم بالوصول إلى مساحة العمل.'], ['الاستخدام المقبول', 'لا يجوز إساءة استخدام الخدمة أو محاولة تجاوز ضوابط الأمان أو التدخل في تشغيلها أو حقوق الآخرين.'], ['المحتوى والملكية', 'تحتفظ بملكية المحتوى الذي تقدمه، بينما يحتفظ المزود بحقوقه في الخدمة والبرمجيات والعلامات.'], ['التوفر والضمانات', 'يتم توفير الخدمة وفق الخطة والاتفاق المعمول بهما، وقد تتغير الميزات مع استمرار تطوير المنتج.'], ['المسؤولية والإنهاء', 'تخضع حدود المسؤولية وحقوق التعليق أو الإنهاء للاتفاق المعمول به والقانون الساري.']] : [['Using the service', 'You may use the service only in accordance with these terms, applicable laws, and your account documentation.'], ['Accounts and access', 'You are responsible for credentials, account activity, and the people you authorize to access your workspace.'], ['Acceptable use', 'You may not misuse the service, bypass security controls, interfere with its operation, or violate the rights of others.'], ['Content and ownership', 'You retain ownership of content you provide. The provider retains its rights in the service, software, and brand.'], ['Availability and warranties', 'The service is provided under the applicable plan and agreement, and features may change as the product develops.'], ['Liability and termination', 'Liability limits and rights to suspend or terminate access are governed by the applicable agreement and law.']])
|
|
468
|
+
return <article className="legal-page" id="top"><header><p className="eyebrow">{locale === 'ar' ? 'قانوني' : 'Legal'}</p><h1>{isPrivacy ? (locale === 'ar' ? 'سياسة الخصوصية' : 'Privacy Policy') : (locale === 'ar' ? 'شروط الاستخدام' : 'Terms of Use')}</h1><p>{locale === 'ar' ? `مسودة نموذجية لـ ${demo.product}. يجب مراجعتها واعتمادها قانونياً قبل النشر.` : `A structured template draft for ${demo.product}. Review and approve it with legal counsel before publishing.`}</p><small>{locale === 'ar' ? 'آخر تحديث: 20 أغسطس 2026' : 'Last updated: August 20, 2026'}</small></header><div className="legal-layout"><aside aria-label={locale === 'ar' ? 'محتويات الصفحة' : 'On this page'}>{sections.map(([title], index) => <a href={`#legal-${index + 1}`} key={title}>{String(index + 1).padStart(2, '0')} · {title}</a>)}</aside><div>{sections.map(([title, body], index) => <section id={`legal-${index + 1}`} key={title}><span>{String(index + 1).padStart(2, '0')}</span><h2>{title}</h2><p>{body}</p></section>)}</div></div></article>
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function TeamPage({ demo, locale, seed }: { demo: DemoData; locale: Locale; seed: number }) {
|
|
472
|
+
const people = locale === 'ar'
|
|
473
|
+
? [[demo.leaders[0], 'المنتج والاستراتيجية', 'يربط احتياجات العملاء بنظام عمل واضح وقابل للقياس.'], [demo.leaders[1], 'الهندسة', 'يبني أنظمة موثوقة تحافظ على بساطة العمل المعقد.'], ['نور حداد', 'التصميم', 'تجعل القرارات والواجهات والعمليات مفهومة للجميع.'], ['آدم ريفيرا', 'أنظمة العملاء', 'يحوّل أنماط العمل الواقعية إلى عمليات قابلة للتوسع.']]
|
|
474
|
+
: [[demo.leaders[0], 'Product & strategy', 'Connects customer reality to a clear, measurable operating model.'], [demo.leaders[1], 'Engineering', 'Builds reliable systems that keep complex work calm and legible.'], ['Noor Haddad', 'Design', 'Makes decisions, interfaces, and operations understandable to everyone.'], ['Adam Rivera', 'Customer systems', 'Turns real working patterns into operations that can scale.']]
|
|
475
|
+
const principles = locale === 'ar'
|
|
476
|
+
? [['الوضوح قبل السرعة', 'نكتب القرار والسبب والمالك قبل أن نزيد وتيرة التنفيذ.'], ['المنتج في قلب العمل', 'يعمل الباحثون والمصممون والمهندسون معاً منذ اليوم الأول.'], ['الثقة تُبنى بالتفاصيل', 'نصمم الأذونات والسجل والمراجعة كجزء من التجربة نفسها.']]
|
|
477
|
+
: [['Clarity before velocity', 'We make the decision, reason, and owner visible before increasing execution speed.'], ['Product is a team sport', 'Research, design, and engineering work together from the first day.'], ['Trust lives in the details', 'Permissions, history, and review are designed into the experience itself.']]
|
|
478
|
+
return <>
|
|
479
|
+
<PageHero body={locale === 'ar' ? `فريق متعدد التخصصات يبني ${demo.product} من خلال فهم عميق لكيفية عمل الفرق فعلياً.` : `A multidisciplinary team building ${demo.product} from a deep understanding of how modern teams actually work.`} eyebrow={locale === 'ar' ? 'من نحن' : 'Company'} title={locale === 'ar' ? 'نبني أنظمة تجعل العمل الجيد أسهل.' : 'We build systems that make good work easier.'}><Button endContent={<ArrowIcon />} onClick={() => navigateTo(pageHref('contact-sales', seed))} size="lg">{locale === 'ar' ? 'تحدث معنا' : 'Talk with us'}</Button></PageHero>
|
|
480
|
+
<section className="section team-intro"><div><p className="eyebrow">{locale === 'ar' ? 'كيف نعمل' : 'How we work'}</p><h2>{locale === 'ar' ? 'صغير بما يكفي للتحرك معاً، وخبير بما يكفي للتعمق.' : 'Small enough to move together. Experienced enough to go deep.'}</h2></div><p>{locale === 'ar' ? 'نحن فريق موزع من المصممين والمهندسين والباحثين ومشغلي العملاء. نبدأ بالمشكلة كما تحدث في الواقع، ثم نبني أقل نظام قادر على جعلها أوضح وأكثر موثوقية.' : 'We are a distributed group of designers, engineers, researchers, and customer operators. We start with the problem as it happens in the real world, then build the smallest system that makes it clearer and more reliable.'}</p></section>
|
|
481
|
+
<section className="section"><SectionHeading body={locale === 'ar' ? 'مبادئ ثابتة تساعدنا على اتخاذ قرارات أفضل مع نمو المنتج.' : 'A few durable principles help us make better decisions as the product grows.'} eyebrow={locale === 'ar' ? 'مبادئنا' : 'Our principles'} title={locale === 'ar' ? 'طريقة العمل جزء من المنتج.' : 'How we work is part of what we ship.'} /><div className="team-principles">{principles.map(([title, body], index) => <article className="team-principle" key={title}><span>0{index + 1}</span><h3>{title}</h3><p>{body}</p></article>)}</div></section>
|
|
482
|
+
<section className="section"><SectionHeading body={locale === 'ar' ? 'أسماء ومحتوى تجريبيان قابلان للاستبدال ببيانات الفريق المعتمدة.' : 'Seeded demo profiles are ready to replace with approved team data.'} eyebrow={locale === 'ar' ? 'الفريق' : 'The team'} title={locale === 'ar' ? 'خبرات مختلفة، معيار واحد للعمل.' : 'Different disciplines. One standard for the work.'} /><div className="team-grid">{people.map(([name, role, body]) => <article className="team-card" key={name}><div className="team-avatar" aria-hidden="true">{name.split(' ').map((part) => part[0]).slice(0, 2).join('')}</div><div><p>{role}</p><h3>{name}</h3><span>{body}</span></div></article>)}</div></section>
|
|
483
|
+
<section className="section team-presence"><div><p className="eyebrow">{locale === 'ar' ? 'فريق موزع' : 'Distributed by design'}</p><h2>{locale === 'ar' ? 'سياقات محلية. معيار عالمي.' : 'Local context. One shared standard.'}</h2></div><div><strong>{locale === 'ar' ? 'الدوحة · لندن · نيويورك · دبي' : 'Doha · London · New York · Dubai'}</strong><p>{locale === 'ar' ? 'نتعاون عبر المناطق الزمنية ونلتقي حول القرارات التي تستحق الاهتمام الكامل.' : 'We collaborate across time zones and come together around the decisions that deserve full attention.'}</p></div></section>
|
|
484
|
+
<PageCta locale={locale} seed={seed} />
|
|
485
|
+
</>
|
|
486
|
+
}
|
|
487
|
+
|
|
444
488
|
function SecondaryPage({ demo, locale, page, previewMode, seed, setPreviewMode }: { demo: DemoData; locale: Locale; page: PageKey; previewMode: PreviewMode; seed: number; setPreviewMode: (mode: PreviewMode) => void }) {
|
|
445
489
|
if (page === 'product') return <ProductPage demo={demo} locale={locale} previewMode={previewMode} seed={seed} setPreviewMode={setPreviewMode} />
|
|
446
490
|
if (page === 'agents') return <AgentsPage demo={demo} locale={locale} seed={seed} />
|
|
@@ -449,15 +493,22 @@ function SecondaryPage({ demo, locale, page, previewMode, seed, setPreviewMode }
|
|
|
449
493
|
if (page === 'customers') return <CustomersPage demo={demo} locale={locale} seed={seed} />
|
|
450
494
|
if (page === 'customer-story') return <CustomerStoryPage demo={demo} locale={locale} seed={seed} />
|
|
451
495
|
if (page === 'pricing') return <PricingPage demo={demo} locale={locale} seed={seed} />
|
|
496
|
+
if (page === 'team') return <TeamPage demo={demo} locale={locale} seed={seed} />
|
|
452
497
|
if (page === 'changelog') return <ChangelogPage locale={locale} />
|
|
498
|
+
if (page === 'terms') return <LegalPage demo={demo} kind="terms" locale={locale} />
|
|
499
|
+
if (page === 'privacy') return <LegalPage demo={demo} kind="privacy" locale={locale} />
|
|
453
500
|
return <ContactSalesPage demo={demo} locale={locale} />
|
|
454
501
|
}
|
|
455
502
|
|
|
456
503
|
function App() {
|
|
457
504
|
const params = new URLSearchParams(window.location.search)
|
|
458
505
|
const seed = Number(params.get('seed')) || 1974341818
|
|
459
|
-
const page = getPageKey()
|
|
460
|
-
const [locale, setLocale] = useState<Locale>(() =>
|
|
506
|
+
const [page, setPage] = useState<PageKey>(() => getPageKey())
|
|
507
|
+
const [locale, setLocale] = useState<Locale>(() => {
|
|
508
|
+
const requestedLocale = new URLSearchParams(window.location.search).get('locale')
|
|
509
|
+
if (requestedLocale?.startsWith('ar')) return 'ar'
|
|
510
|
+
return window.localStorage.getItem('ceramic-saas-locale') === 'ar' ? 'ar' : 'en'
|
|
511
|
+
})
|
|
461
512
|
const [colorMode, setColorMode] = useState<'dark' | 'light'>(() => window.localStorage.getItem('ceramic-saas-mode') === 'light' ? 'light' : 'dark')
|
|
462
513
|
const [previewMode, setPreviewMode] = useState<PreviewMode>('overview')
|
|
463
514
|
const [isolated, setIsolated] = useState(false)
|
|
@@ -471,12 +522,38 @@ function App() {
|
|
|
471
522
|
{ key: 'integrations', label: locale === 'ar' ? 'التكاملات' : 'Integrations', href: pageHref('integrations', seed) },
|
|
472
523
|
{ key: 'customers', label: locale === 'ar' ? 'العملاء' : 'Customers', href: pageHref('customers', seed) },
|
|
473
524
|
{ key: 'pricing', label: locale === 'ar' ? 'الأسعار' : 'Pricing', href: pageHref('pricing', seed) },
|
|
525
|
+
{ key: 'team', label: locale === 'ar' ? 'الفريق' : 'Team', href: pageHref('team', seed) },
|
|
474
526
|
{ key: 'changelog', label: locale === 'ar' ? 'التحديثات' : 'Changelog', href: pageHref('changelog', seed) },
|
|
475
527
|
]
|
|
476
528
|
const goTo = (id: string) => {
|
|
477
529
|
window.history.pushState(null, '', `#${id}`)
|
|
478
530
|
document.getElementById(id)?.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
479
531
|
}
|
|
532
|
+
|
|
533
|
+
useEffect(() => {
|
|
534
|
+
const syncRoute = () => {
|
|
535
|
+
setPage(getPageKey())
|
|
536
|
+
setMenuOpen(false)
|
|
537
|
+
window.scrollTo({ top: 0 })
|
|
538
|
+
}
|
|
539
|
+
const onInternalLink = (event: MouseEvent) => {
|
|
540
|
+
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return
|
|
541
|
+
const anchor = (event.target as Element | null)?.closest<HTMLAnchorElement>('a[href]')
|
|
542
|
+
if (!anchor || anchor.target === '_blank' || anchor.hasAttribute('download')) return
|
|
543
|
+
const next = new URL(anchor.href, window.location.href)
|
|
544
|
+
if (next.origin !== window.location.origin) return
|
|
545
|
+
if (next.pathname === window.location.pathname && next.search === window.location.search && next.hash) return
|
|
546
|
+
event.preventDefault()
|
|
547
|
+
window.history.pushState(null, '', `${next.pathname}${next.search}${next.hash}`)
|
|
548
|
+
syncRoute()
|
|
549
|
+
}
|
|
550
|
+
document.addEventListener('click', onInternalLink)
|
|
551
|
+
window.addEventListener('popstate', syncRoute)
|
|
552
|
+
return () => {
|
|
553
|
+
document.removeEventListener('click', onInternalLink)
|
|
554
|
+
window.removeEventListener('popstate', syncRoute)
|
|
555
|
+
}
|
|
556
|
+
}, [])
|
|
480
557
|
const demo = useMemo(() => {
|
|
481
558
|
const set = seededIndex(seed, 11, demoSets.product.length)
|
|
482
559
|
return {
|
|
@@ -536,7 +613,7 @@ function App() {
|
|
|
536
613
|
{colorMode === 'dark' ? <Sun aria-hidden="true" /> : <Moon aria-hidden="true" />}
|
|
537
614
|
</Button>
|
|
538
615
|
<Button aria-label={locale === 'en' ? 'Switch to Arabic' : 'Switch to English'} className="language-button" onClick={() => setLocale(locale === 'en' ? 'ar' : 'en')} size="sm" startContent={<Languages aria-hidden="true" />} variant="ghost">{t.language}</Button>
|
|
539
|
-
<Button className="nav-cta" onClick={() => {
|
|
616
|
+
<Button className="nav-cta" onClick={() => { navigateTo(pageHref('contact-sales', seed)) }} size="sm">{t.contact}</Button>
|
|
540
617
|
<Button aria-expanded={menuOpen} aria-label={menuOpen ? t.closeMenu : t.menu} className="menu-button" isIconOnly onClick={() => setMenuOpen(!menuOpen)} size="icon" variant="ghost">
|
|
541
618
|
{menuOpen ? <X aria-hidden="true" /> : <Menu aria-hidden="true" />}
|
|
542
619
|
</Button>
|
|
@@ -549,10 +626,10 @@ function App() {
|
|
|
549
626
|
|
|
550
627
|
<AnimatePresence mode="wait">
|
|
551
628
|
<motion.main
|
|
552
|
-
animate={{
|
|
553
|
-
exit={pageMotion.enabled ? {
|
|
629
|
+
animate={{ y: 0 }}
|
|
630
|
+
exit={pageMotion.enabled ? { y: -6 } : undefined}
|
|
554
631
|
id="main-content"
|
|
555
|
-
initial={pageMotion.enabled ? {
|
|
632
|
+
initial={pageMotion.enabled ? { y: 8 } : false}
|
|
556
633
|
key={`${page}-${locale}`}
|
|
557
634
|
transition={pageMotion.transition}
|
|
558
635
|
>
|
|
@@ -638,7 +715,7 @@ function App() {
|
|
|
638
715
|
</motion.main>
|
|
639
716
|
</AnimatePresence>
|
|
640
717
|
|
|
641
|
-
<footer className="site-footer"><a className="product-logo" href={pageHref('', seed)}><span className="brand-mark" />{demo.product}</a><p>{t.footer}</p><div><a href={pageHref('product', seed)}>{locale === 'ar' ? 'المنتج' : 'Product'}</a><a href={pageHref('customers', seed)}>{locale === 'ar' ? 'العملاء' : 'Customers'}</a><a href={pageHref('pricing', seed)}>{locale === 'ar' ? 'الأسعار' : 'Pricing'}</a><a href={pageHref('contact-sales', seed)}>{locale === 'ar' ? 'اتصل بنا' : 'Contact'}</a></div><small>© 2026 {demo.product}. Seed {seed}.</small></footer>
|
|
718
|
+
<footer className="site-footer"><a className="product-logo" href={pageHref('', seed)}><span className="brand-mark" />{demo.product}</a><p>{t.footer}</p><div><a href={pageHref('product', seed)}>{locale === 'ar' ? 'المنتج' : 'Product'}</a><a href={pageHref('customers', seed)}>{locale === 'ar' ? 'العملاء' : 'Customers'}</a><a href={pageHref('pricing', seed)}>{locale === 'ar' ? 'الأسعار' : 'Pricing'}</a><a href={pageHref('contact-sales', seed)}>{locale === 'ar' ? 'اتصل بنا' : 'Contact'}</a><a href={pageHref('terms', seed)}>{locale === 'ar' ? 'الشروط' : 'Terms'}</a><a href={pageHref('privacy', seed)}>{locale === 'ar' ? 'الخصوصية' : 'Privacy'}</a></div><small>© 2026 {demo.product}. Seed {seed}.</small></footer>
|
|
642
719
|
</div>
|
|
643
720
|
)
|
|
644
721
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!doctype html><html lang="en" dir="ltr" data-theme="utopia-default" data-color-mode="dark"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="color-scheme" content="dark light" /><style>html{color-scheme:dark}html[data-color-mode="light"]{color-scheme:light}html,body,#root{min-block-size:100%;margin:0;background:Canvas;color:CanvasText}</style><script>try{const mode=localStorage.getItem("ceramic-saas-mode");const locale=localStorage.getItem("ceramic-saas-locale");if(mode==="light")document.documentElement.dataset.colorMode="light";if(locale==="ar"){document.documentElement.lang="ar";document.documentElement.dir="rtl"}}catch{}</script><meta name="description" content="Privacy Policy template." /><title>Privacy Policy — SaaS Solution Template</title></head><body><a class="skip-link" href="#main-content">Skip to content</a><div id="root"></div><script type="module" src="../main.tsx"></script></body></html>
|
|
@@ -398,7 +398,7 @@ a { -webkit-tap-highlight-color: transparent; }
|
|
|
398
398
|
.workflow-step { position: relative; min-block-size: 21rem; padding-block: var(--space-6); padding-inline: var(--space-6); border-block: var(--border-width-hairline) solid var(--border); border-inline-start: var(--border-width-hairline) solid var(--border); }
|
|
399
399
|
.workflow-step:last-child { border-inline-end: var(--border-width-hairline) solid var(--border); }
|
|
400
400
|
.workflow-step > span { color: var(--primary); font-family: var(--font-mono); font-size: var(--font-size-small); }
|
|
401
|
-
.workflow-step > div { display: grid;
|
|
401
|
+
.workflow-step > div { display: grid; margin-block-start: var(--space-24); }
|
|
402
402
|
.workflow-step h3 { margin: 0 0 var(--space-3); font-size: 1.6rem; font-weight: var(--font-weight-medium); }
|
|
403
403
|
.workflow-step p { margin: 0; color: var(--muted-foreground); }
|
|
404
404
|
.step-arrow { position: absolute; inset-block-start: var(--space-6); inset-inline-end: calc(-1 * var(--space-3)); z-index: 1; inline-size: var(--space-6); block-size: var(--space-6); padding: var(--space-1); background: var(--background); color: var(--muted-foreground); }
|
|
@@ -741,6 +741,40 @@ a { -webkit-tap-highlight-color: transparent; }
|
|
|
741
741
|
.contact-success h1 { max-inline-size: 54rem; font-size: clamp(3rem, 6vw, 6rem); }
|
|
742
742
|
.contact-success > p:last-child { color: var(--muted-foreground); font-size: var(--font-size-body-lg); }
|
|
743
743
|
|
|
744
|
+
.team-intro { display: grid; grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr); gap: var(--space-16); align-items: start; }
|
|
745
|
+
.team-intro h2 { margin: var(--space-4) 0 0; max-inline-size: 14ch; font-size: clamp(2.5rem, 5vw, 4.8rem); line-height: .98; letter-spacing: -.04em; }
|
|
746
|
+
.team-intro > p { margin: 0; max-inline-size: 42rem; color: var(--muted-foreground); font-size: clamp(1.15rem, 2vw, 1.5rem); line-height: 1.55; }
|
|
747
|
+
.team-principles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); border: var(--border-width-hairline) solid var(--border); }
|
|
748
|
+
.team-principle { min-block-size: 19rem; padding: var(--space-6); }
|
|
749
|
+
.team-principle + .team-principle { border-inline-start: var(--border-width-hairline) solid var(--border); }
|
|
750
|
+
.team-principle > span { color: var(--primary); font-family: var(--font-mono); font-size: var(--font-size-caption); }
|
|
751
|
+
.team-principle h3 { margin: var(--space-16) 0 var(--space-4); font-size: clamp(1.45rem, 2.2vw, 2rem); }
|
|
752
|
+
.team-principle p { margin: 0; color: var(--muted-foreground); line-height: 1.65; }
|
|
753
|
+
.team-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); border-block-start: var(--border-width-hairline) solid var(--border); border-inline-start: var(--border-width-hairline) solid var(--border); }
|
|
754
|
+
.team-card { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--space-6); min-block-size: 18rem; padding: var(--space-6); border-inline-end: var(--border-width-hairline) solid var(--border); border-block-end: var(--border-width-hairline) solid var(--border); align-items: end; }
|
|
755
|
+
.team-avatar { display: grid; place-items: center; inline-size: var(--space-16); block-size: var(--space-16); border: var(--border-width-hairline) solid var(--border); border-radius: 50%; background: color-mix(in srgb, var(--primary) 12%, var(--surface)); color: var(--primary); font-family: var(--font-mono); }
|
|
756
|
+
.team-card p { margin: 0 0 var(--space-2); color: var(--primary); font-family: var(--font-mono); font-size: var(--font-size-caption); text-transform: uppercase; letter-spacing: .08em; }
|
|
757
|
+
.team-card h3 { margin: 0 0 var(--space-3); font-size: clamp(1.5rem, 2vw, 2rem); }
|
|
758
|
+
.team-card span { display: block; max-inline-size: 32rem; color: var(--muted-foreground); line-height: 1.6; }
|
|
759
|
+
.team-presence { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-12); padding-block: var(--space-16); border-block: var(--border-width-hairline) solid var(--border); }
|
|
760
|
+
.team-presence h2 { margin: var(--space-4) 0 0; font-size: clamp(2.4rem, 4.5vw, 4rem); }
|
|
761
|
+
.team-presence strong { display: block; margin-block-end: var(--space-5); color: var(--primary); font-family: var(--font-mono); }
|
|
762
|
+
.team-presence p { margin: 0; max-inline-size: 36rem; color: var(--muted-foreground); line-height: 1.65; }
|
|
763
|
+
|
|
764
|
+
.legal-page { inline-size: min(100%, var(--template-max-inline)); margin-inline: auto; padding-block: calc(var(--template-header-block) + var(--space-16)) var(--space-16); padding-inline: var(--gutter); }
|
|
765
|
+
.legal-page > header { max-inline-size: 58rem; padding-block-end: var(--space-16); }
|
|
766
|
+
.legal-page h1 { margin-block: var(--space-5); font-size: clamp(3.5rem, 8vw, 7.5rem); line-height: .92; letter-spacing: -.055em; }
|
|
767
|
+
.legal-page > header > p:not(.eyebrow) { max-inline-size: 46rem; color: var(--muted-foreground); font-size: var(--font-size-body-lg); line-height: 1.6; }
|
|
768
|
+
.legal-page > header small { display: block; margin-block-start: var(--space-6); color: var(--muted-foreground); font-family: var(--font-mono); }
|
|
769
|
+
.legal-layout { display: grid; grid-template-columns: minmax(13rem, .55fr) minmax(0, 1.45fr); gap: var(--space-16); border-block-start: var(--border-width-hairline) solid var(--border); }
|
|
770
|
+
.legal-layout > aside { position: sticky; inset-block-start: calc(var(--template-header-block) + var(--space-6)); display: grid; align-content: start; gap: var(--space-3); block-size: fit-content; padding-block: var(--space-8); }
|
|
771
|
+
.legal-layout > aside a { color: var(--muted-foreground); font-size: var(--font-size-small); text-decoration: none; }
|
|
772
|
+
.legal-layout > aside a:hover { color: var(--foreground); }
|
|
773
|
+
.legal-layout section { scroll-margin-block-start: calc(var(--template-header-block) + var(--space-6)); padding-block: var(--space-12); border-block-end: var(--border-width-hairline) solid var(--border); }
|
|
774
|
+
.legal-layout section > span { color: var(--primary); font-family: var(--font-mono); font-size: var(--font-size-caption); }
|
|
775
|
+
.legal-layout section h2 { margin: var(--space-4) 0 var(--space-5); font-size: clamp(2rem, 4vw, 3.5rem); }
|
|
776
|
+
.legal-layout section p { max-inline-size: var(--template-reading-inline); margin: 0; color: var(--muted-foreground); font-size: var(--font-size-body-lg); line-height: 1.7; }
|
|
777
|
+
|
|
744
778
|
@keyframes row-in { from { opacity: 0; translate: 0 var(--space-2); } to { opacity: 1; translate: 0; } }
|
|
745
779
|
|
|
746
780
|
@media (max-width: 68rem) {
|
|
@@ -770,6 +804,12 @@ a { -webkit-tap-highlight-color: transparent; }
|
|
|
770
804
|
.featured-story { grid-template-columns: 1fr; }
|
|
771
805
|
.pricing-page-grid { grid-template-columns: 1fr; }
|
|
772
806
|
.pricing-page-grid .uds-card { min-block-size: auto; }
|
|
807
|
+
.legal-layout { grid-template-columns: 1fr; gap: 0; }
|
|
808
|
+
.legal-layout > aside { position: static; grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
809
|
+
.team-intro { grid-template-columns: 1fr; gap: var(--space-8); }
|
|
810
|
+
.team-principles { grid-template-columns: 1fr; }
|
|
811
|
+
.team-principle { min-block-size: auto; }
|
|
812
|
+
.team-principle + .team-principle { border-inline-start: 0; border-block-start: var(--border-width-hairline) solid var(--border); }
|
|
773
813
|
}
|
|
774
814
|
|
|
775
815
|
@media (max-width: 48rem) {
|
|
@@ -807,7 +847,7 @@ a { -webkit-tap-highlight-color: transparent; }
|
|
|
807
847
|
.workflow-grid { grid-template-columns: 1fr; }
|
|
808
848
|
.workflow-step { display: grid; grid-template-columns: auto 1fr; gap: var(--space-4); min-block-size: auto; border-inline-end: var(--border-width-hairline) solid var(--border); border-block-end: 0; }
|
|
809
849
|
.workflow-step:last-child { border-block-end: var(--border-width-hairline) solid var(--border); }
|
|
810
|
-
.workflow-step > div { block-
|
|
850
|
+
.workflow-step > div { margin-block-start: 0; }
|
|
811
851
|
.step-arrow { inset-block-end: calc(-1 * var(--space-3)); inset-block-start: auto; inset-inline-end: var(--space-6); transform: rotate(90deg); }
|
|
812
852
|
:dir(rtl) .step-arrow { transform: rotate(90deg) scaleX(-1); }
|
|
813
853
|
.agent-flow > div { grid-template-columns: auto minmax(0, 1fr); padding-block: var(--space-3); }
|
|
@@ -855,6 +895,11 @@ a { -webkit-tap-highlight-color: transparent; }
|
|
|
855
895
|
.changelog-layout article { grid-template-columns: 1fr; }
|
|
856
896
|
.contact-layout { padding-block-start: calc(var(--template-header-block) + var(--space-8)); padding-inline: var(--space-4); }
|
|
857
897
|
.contact-layout form { padding: var(--space-4); }
|
|
898
|
+
.team-grid,
|
|
899
|
+
.team-presence { grid-template-columns: 1fr; }
|
|
900
|
+
.team-card { min-block-size: 15rem; }
|
|
901
|
+
.legal-page { padding-inline: var(--space-4); }
|
|
902
|
+
.legal-layout > aside { grid-template-columns: 1fr 1fr; }
|
|
858
903
|
}
|
|
859
904
|
|
|
860
905
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!doctype html><html lang="en" dir="ltr" data-theme="utopia-default" data-color-mode="dark"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="color-scheme" content="dark light" /><style>html{color-scheme:dark}html[data-color-mode="light"]{color-scheme:light}html,body,#root{min-block-size:100%;margin:0;background:Canvas;color:CanvasText}</style><script>try{const mode=localStorage.getItem("ceramic-saas-mode");const locale=localStorage.getItem("ceramic-saas-locale");if(mode==="light")document.documentElement.dataset.colorMode="light";if(locale==="ar"){document.documentElement.lang="ar";document.documentElement.dir="rtl"}}catch{}</script><meta name="description" content="Team, principles, and company story for the Ceramic SaaS solution template." /><title>Team — SaaS Solution Template</title></head><body><a class="skip-link" href="#main-content">Skip to content</a><div id="root"></div><script type="module" src="../main.tsx"></script></body></html>
|