@sableui/react 0.1.1 → 0.3.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/AGENTS.md +3 -1
- package/CHANGELOG.md +120 -0
- package/CONTRIBUTING.md +8 -0
- package/README.md +1 -0
- package/bin/sable-lint.mjs +0 -0
- package/bin/sable.mjs +68 -26
- package/dist/{_contract-CYScOdf5.d.ts → _contract-o3tUc8Ig.d.ts} +1 -1
- package/dist/blocks/index.d.ts +396 -0
- package/dist/blocks/index.js +1997 -0
- package/dist/chunk-6LCIBMMG.js +4908 -0
- package/dist/{chunk-J23ELQQZ.js → chunk-CKNM7KSB.js} +1664 -4270
- package/dist/chunk-K6LII3OE.js +5093 -0
- package/dist/{chunk-RWP4XBSK.js → chunk-KFZ43RKU.js} +1 -1
- package/dist/{chunk-PAOZBZME.js → chunk-NGBDP3BM.js} +1 -1
- package/dist/{chunk-5Z5KLOAU.js → chunk-TH563WLW.js} +14 -1
- package/dist/{chunk-JI7WJP2L.js → chunk-X276MF2W.js} +5 -0
- package/dist/foundations/index.js +1 -1
- package/dist/icon/index.js +2 -2
- package/dist/icon-sets/reicon.js +1 -1
- package/dist/index.d.ts +624 -133
- package/dist/index.js +767 -4964
- package/dist/ios/index.d.ts +14 -2
- package/dist/ios/index.js +10 -9
- package/dist/ios/preview.d.ts +11 -1
- package/dist/ios/preview.js +14 -9
- package/dist/ios/styles.css +26 -6
- package/dist/{meta-6dUzgC6M.d.ts → meta-CcE_SeFu.d.ts} +37 -1
- package/dist/patterns/index.d.ts +1 -1
- package/dist/patterns/index.js +1684 -471
- package/dist/presets/index.d.ts +1 -1
- package/dist/presets/index.js +1 -1
- package/dist/styles.css +238 -40
- package/dist/{theme-provider-BWcizhAt.d.ts → theme-provider-BE-KqUG-.d.ts} +1 -1
- package/dist/themes/atelier/atelier.css +3 -0
- package/dist/themes/atelier/index.d.ts +1 -1
- package/dist/themes/base/base.css +34 -4
- package/dist/themes/base/index.d.ts +1 -1
- package/dist/themes/ember/ember.css +3 -0
- package/dist/themes/ember/index.d.ts +1 -1
- package/dist/themes/index.d.ts +2 -2
- package/dist/themes/index.js +2 -2
- package/dist/themes/meridian/index.d.ts +1 -1
- package/dist/themes/meridian/meridian.css +10 -5
- package/dist/themes/poppy/index.d.ts +1 -1
- package/dist/themes/poppy/poppy.css +16 -0
- package/dist/themes/prism/index.d.ts +1 -1
- package/dist/themes/prism/prism.css +4 -1
- package/dist/themes/verdant/index.d.ts +1 -1
- package/dist/themes/verdant/verdant.css +7 -2
- package/llms/blocks/api-keys-block.md +79 -0
- package/llms/blocks/chat-thread-block.md +51 -0
- package/llms/blocks/command-palette-block.md +48 -0
- package/llms/blocks/cta-band-block.md +62 -0
- package/llms/blocks/danger-zone-block.md +69 -0
- package/llms/blocks/device-comparison-block.md +91 -0
- package/llms/blocks/event-card-block.md +84 -0
- package/llms/blocks/faq-block.md +72 -0
- package/llms/blocks/feature-grid-block.md +75 -0
- package/llms/blocks/feedback-form-block.md +66 -0
- package/llms/blocks/goal-progress-block.md +54 -0
- package/llms/blocks/hero-block.md +65 -0
- package/llms/blocks/invite-team-block.md +61 -0
- package/llms/blocks/invoice-history-block.md +84 -0
- package/llms/blocks/kpi-sparkline-block.md +57 -0
- package/llms/blocks/media-player-block.md +100 -0
- package/llms/blocks/newsletter-block.md +64 -0
- package/llms/blocks/not-found-block.md +56 -0
- package/llms/blocks/notification-list-block.md +63 -0
- package/llms/blocks/notification-settings-block.md +54 -0
- package/llms/blocks/order-tracking-block.md +62 -0
- package/llms/blocks/pricing-card-block.md +82 -0
- package/llms/blocks/pricing-table-block.md +129 -0
- package/llms/blocks/product-card-block.md +69 -0
- package/llms/blocks/profile-card-block.md +103 -0
- package/llms/blocks/profile-form-block.md +114 -0
- package/llms/blocks/revenue-breakdown-block.md +70 -0
- package/llms/blocks/sign-in-block.md +64 -0
- package/llms/blocks/sign-up-block.md +78 -0
- package/llms/blocks/site-footer-block.md +96 -0
- package/llms/blocks/stat-row-block.md +59 -0
- package/llms/blocks/steps-block.md +73 -0
- package/llms/blocks/task-list-block.md +81 -0
- package/llms/blocks/team-members-block.md +91 -0
- package/llms/blocks/testimonial-block.md +78 -0
- package/llms/blocks/traffic-sources-block.md +63 -0
- package/llms/blocks/traffic-trend-block.md +95 -0
- package/llms/blocks/upload-list-block.md +66 -0
- package/llms/blocks/verify-code-block.md +59 -0
- package/llms/blocks/wallet-block.md +84 -0
- package/llms/components/area-chart.md +112 -0
- package/llms/components/bar-chart.md +131 -0
- package/llms/components/bubble.md +5 -5
- package/llms/components/button.md +1 -1
- package/llms/components/card.md +2 -2
- package/llms/components/checkbox-card.md +1 -1
- package/llms/components/context-card.md +2 -2
- package/llms/components/context-menu.md +1 -1
- package/llms/components/date-tile.md +79 -0
- package/llms/components/dialog.md +1 -1
- package/llms/components/empty-state.md +4 -4
- package/llms/components/entity.md +5 -3
- package/llms/components/fieldset.md +1 -1
- package/llms/components/kbd.md +18 -6
- package/llms/components/line-chart.md +101 -0
- package/llms/components/menu.md +1 -1
- package/llms/components/{dots-menu.md → overflow-menu.md} +9 -9
- package/llms/components/pie-chart.md +103 -0
- package/llms/components/pill.md +1 -1
- package/llms/components/radar-chart.md +106 -0
- package/llms/components/radial-chart.md +102 -0
- package/llms/components/segmented-bar.md +102 -0
- package/llms/components/settings-menu.md +2 -2
- package/llms/components/side-nav.md +1 -1
- package/llms/components/skeleton.md +1 -1
- package/llms/components/sparkline.md +80 -0
- package/llms/components/table.md +1 -1
- package/llms/components/theme-selector.md +1 -1
- package/llms/foundations/shape.md +1 -1
- package/llms/patterns/auth-page.md +132 -42
- package/llms/patterns/brand-landing-page.md +271 -175
- package/llms/patterns/chat-panel.md +120 -39
- package/llms/patterns/dashboard-page.md +276 -65
- package/llms/patterns/feed-page.md +245 -64
- package/llms/patterns/landing-page.md +473 -75
- package/llms/patterns/list-detail-page.md +377 -68
- package/llms/patterns/settings-page.md +268 -67
- package/llms/reference/token-intent.md +4 -2
- package/llms/reference/utilities.md +20 -5
- package/llms/tasks/building-an-ios-prototype.md +4 -1
- package/llms/tasks/choosing-a-component.md +2 -2
- package/llms-full.txt +6428 -892
- package/llms.txt +67 -16
- package/package.json +5 -1
- package/registry.json +3217 -552
- package/targets/ios.json +88 -18
- package/llms/components/description.md +0 -96
- package/dist/{chunk-L6Y46C62.js → chunk-MXJX3NZI.js} +1 -1
|
@@ -1,79 +1,113 @@
|
|
|
1
1
|
# BrandLandingPage
|
|
2
2
|
|
|
3
|
-
High-fidelity brand/product landing page: pill navigation, editorial hero type, a dark product scene,
|
|
3
|
+
High-fidelity brand/product landing page: pill navigation, editorial hero type, a dark product scene, a value section, a how-it-works sequence, a customer story with outcomes, a closing CTA band, and a complete branded footer.
|
|
4
4
|
|
|
5
5
|
**Intent:** Use this for a polished product marketing route that still needs to show a credible app surface. It is warmer and more tactile than the canonical LandingPage, but avoids trend-driven neon and generic hype cues.
|
|
6
6
|
|
|
7
7
|
## Notes
|
|
8
8
|
|
|
9
|
-
-
|
|
9
|
+
- The story runs promise → product → value → process → proof → ask: hero, product scene, 'Why Cove' value cards, a three-step how-it-works list, a customer story with outcome figures, then one closing CTA. Each section adds new evidence; no number is repeated from the product scene.
|
|
10
|
+
- One display headline owns the first viewport; supporting copy stays in body-lg at prose measure. Section headings are heading-1 under a `text-overline` eyebrow.
|
|
10
11
|
- The nav spends one compact pill surface, with a clear brand signal and two primary paths: sign in and request access.
|
|
11
|
-
- The product scene
|
|
12
|
-
- The
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
12
|
+
- The product scene and the closing CTA band are dark islands: a `dark` class on the wrapper re-scopes every semantic token, so surfaces, muted ink, Badge tones and Buttons take the theme's own dark values in either page mode. Never fake a dark panel with overlay ink at reduced opacity — status tones tuned for a light page vanish on it.
|
|
13
|
+
- The scene is a picture of the product: `aria-hidden` + `inert` keep its sample button and figures out of the tab order and the outline. On narrow screens it drops the side column and keeps the signal map, so it scales instead of stacking into a second page.
|
|
14
|
+
- The chart keeps past weeks on the quiet `secondary` fill and gives the current week the one accent, so the signal reads at a glance.
|
|
15
|
+
- Value cards are real Cards with a neutral `sable-well` icon tile; the customer story is a Card rendered as a figure, with outcome figures split by Separator. The scene's nested panels take `rounded-concentric-xl/<inset>` from the shell's radius, so the corners stay parallel at any radius preset.
|
|
16
|
+
- The footer closes the page with brand reassurance, a plain status Badge, a three-column sitemap (three columns at every width), a digest signup, and legal links.
|
|
17
|
+
- All figures, names and the testimonial are sample content; replace them with real, verifiable claims.
|
|
16
18
|
|
|
17
19
|
## Accessibility
|
|
18
20
|
|
|
19
21
|
- The brand nav is a labeled nav landmark with real links and visible text.
|
|
22
|
+
- Each content section is a labelled region (by its h2, or aria-label for the customer story); the decorative product scene is hidden from assistive tech and inert.
|
|
23
|
+
- The how-it-works steps are an ordered list; the testimonial is a figure with a blockquote and a figcaption naming the speaker.
|
|
20
24
|
- The footer is a real footer landmark with a labeled footer nav, visible link text, and a labeled email field.
|
|
21
|
-
- TextReveal preserves the full headline as one accessible label and honors reduced-motion policy.
|
|
22
|
-
- Status dots are paired with visible text labels so color is never the only signal.
|
|
25
|
+
- TextReveal preserves the full headline as one accessible label and honors reduced-motion policy; Reveal shows the value cards immediately when motion is off.
|
|
26
|
+
- Status dots and the status Badge are paired with visible text labels so color is never the only signal.
|
|
23
27
|
|
|
24
28
|
## Source
|
|
25
29
|
|
|
26
30
|
```tsx
|
|
27
|
-
import { Badge, Button, Card,
|
|
31
|
+
import { Avatar, Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Grid, Icon, Input, Region, Reveal, Row, Separator, Stack, StatusDot, TextReveal } from "@sableui/react"
|
|
28
32
|
|
|
29
|
-
const navItems = [
|
|
33
|
+
const navItems = [
|
|
34
|
+
{ label: "Platform", href: "#platform" },
|
|
35
|
+
{ label: "How it works", href: "#how-it-works" },
|
|
36
|
+
{ label: "Customers", href: "#customers" },
|
|
37
|
+
{ label: "Pricing", href: "/pricing" },
|
|
38
|
+
]
|
|
30
39
|
|
|
31
|
-
const
|
|
32
|
-
{
|
|
33
|
-
{
|
|
34
|
-
{
|
|
40
|
+
const spaces = [
|
|
41
|
+
{ name: "Waypoint", value: "98.4%", label: "clear routes" },
|
|
42
|
+
{ name: "Fielddesk", value: "12", label: "open holds" },
|
|
43
|
+
{ name: "Archive", value: "4.8k", label: "sealed records" },
|
|
35
44
|
]
|
|
36
45
|
|
|
37
|
-
const
|
|
46
|
+
const routes = [
|
|
38
47
|
{ label: "Intake", state: "Open", tone: "info" as const },
|
|
39
48
|
{ label: "Review", state: "Live", tone: "edition" as const },
|
|
40
49
|
{ label: "Archive", state: "Safe", tone: "success" as const },
|
|
41
50
|
]
|
|
42
51
|
|
|
52
|
+
// Weekly approvals: past weeks recede, the current week carries the signal.
|
|
43
53
|
const bars = [
|
|
44
|
-
{ height: "38%",
|
|
45
|
-
{ height: "56%",
|
|
46
|
-
{ height: "44%",
|
|
47
|
-
{ height: "68%",
|
|
48
|
-
{ height: "52%",
|
|
49
|
-
{ height: "78%",
|
|
50
|
-
{ height: "60%",
|
|
51
|
-
{
|
|
52
|
-
{
|
|
54
|
+
{ week: "w1", height: "38%", current: false },
|
|
55
|
+
{ week: "w2", height: "56%", current: false },
|
|
56
|
+
{ week: "w3", height: "44%", current: false },
|
|
57
|
+
{ week: "w4", height: "68%", current: false },
|
|
58
|
+
{ week: "w5", height: "52%", current: false },
|
|
59
|
+
{ week: "w6", height: "78%", current: false },
|
|
60
|
+
{ week: "w7", height: "60%", current: false },
|
|
61
|
+
{ week: "w8", height: "72%", current: false },
|
|
62
|
+
{ week: "w9", height: "88%", current: true },
|
|
53
63
|
]
|
|
54
64
|
|
|
55
|
-
const
|
|
65
|
+
const sceneMetrics = [
|
|
66
|
+
{ label: "Cleared", value: "1.8M", detail: "last 30 days" },
|
|
67
|
+
{ label: "Review", value: "42s", detail: "median path" },
|
|
68
|
+
{ label: "Spaces", value: "284", detail: "active teams" },
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
const values = [
|
|
56
72
|
{
|
|
57
73
|
icon: "lock" as const,
|
|
58
74
|
title: "Private by default",
|
|
59
|
-
body: "
|
|
75
|
+
body: "Roles, approvals, and handoff history stay with the people who need them. No shared inboxes, no forwarded screenshots.",
|
|
60
76
|
},
|
|
61
77
|
{
|
|
62
|
-
icon: "
|
|
63
|
-
title: "
|
|
64
|
-
body: "
|
|
78
|
+
icon: "bell" as const,
|
|
79
|
+
title: "Signals before surprises",
|
|
80
|
+
body: "Cove watches every route and surfaces stalled reviews and expiring approvals while there is still time to act.",
|
|
65
81
|
},
|
|
66
82
|
{
|
|
67
|
-
icon: "
|
|
68
|
-
title: "
|
|
69
|
-
body: "
|
|
83
|
+
icon: "clock" as const,
|
|
84
|
+
title: "A trail for every decision",
|
|
85
|
+
body: "Each approval keeps its context, reviewer, and timestamp, so an audit takes an afternoon instead of a quarter.",
|
|
70
86
|
},
|
|
71
87
|
]
|
|
72
88
|
|
|
73
|
-
const
|
|
74
|
-
{
|
|
75
|
-
|
|
76
|
-
|
|
89
|
+
const steps = [
|
|
90
|
+
{
|
|
91
|
+
number: "01",
|
|
92
|
+
title: "Connect your spaces",
|
|
93
|
+
body: "Bring teams, vendors, and record stores into one workspace, with roles that mirror how you already work.",
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
number: "02",
|
|
97
|
+
title: "Route every approval",
|
|
98
|
+
body: "Requests follow the path you define, with reviewers, deadlines, and escalation built in from the start.",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
number: "03",
|
|
102
|
+
title: "Seal the record",
|
|
103
|
+
body: "Approved work is archived with its full context and is ready for review the moment it closes.",
|
|
104
|
+
},
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
const outcomes = [
|
|
108
|
+
{ value: "3.2×", label: "faster approvals in the first quarter" },
|
|
109
|
+
{ value: "68%", label: "fewer follow-ups on handoffs" },
|
|
110
|
+
{ value: "1 day", label: "to prepare a quarterly audit" },
|
|
77
111
|
]
|
|
78
112
|
|
|
79
113
|
const footerGroups = [
|
|
@@ -81,9 +115,9 @@ const footerGroups = [
|
|
|
81
115
|
title: "Platform",
|
|
82
116
|
links: [
|
|
83
117
|
{ label: "Overview", href: "#platform" },
|
|
84
|
-
{ label: "Signals", href: "
|
|
85
|
-
{ label: "Approvals", href: "
|
|
86
|
-
{ label: "Records", href: "
|
|
118
|
+
{ label: "Signals", href: "/signals" },
|
|
119
|
+
{ label: "Approvals", href: "/approvals" },
|
|
120
|
+
{ label: "Records", href: "/records" },
|
|
87
121
|
],
|
|
88
122
|
},
|
|
89
123
|
{
|
|
@@ -91,7 +125,7 @@ const footerGroups = [
|
|
|
91
125
|
links: [
|
|
92
126
|
{ label: "About", href: "/about" },
|
|
93
127
|
{ label: "Security", href: "/security" },
|
|
94
|
-
{ label: "Customers", href: "
|
|
128
|
+
{ label: "Customers", href: "#customers" },
|
|
95
129
|
{ label: "Careers", href: "/careers" },
|
|
96
130
|
],
|
|
97
131
|
},
|
|
@@ -106,10 +140,10 @@ const footerGroups = [
|
|
|
106
140
|
},
|
|
107
141
|
]
|
|
108
142
|
|
|
109
|
-
const
|
|
110
|
-
{ label: "
|
|
111
|
-
{ label: "
|
|
112
|
-
{ label: "
|
|
143
|
+
const legalLinks = [
|
|
144
|
+
{ label: "Privacy", href: "/privacy" },
|
|
145
|
+
{ label: "Terms", href: "/terms" },
|
|
146
|
+
{ label: "Contact", href: "/contact" },
|
|
113
147
|
]
|
|
114
148
|
|
|
115
149
|
export function BrandLandingPage() {
|
|
@@ -130,8 +164,8 @@ export function BrandLandingPage() {
|
|
|
130
164
|
<div className="hidden lg:block">
|
|
131
165
|
<Row gap={1}>
|
|
132
166
|
{navItems.map((item) => (
|
|
133
|
-
<Button key={item} variant="ghost" size="sm" render={<a href={
|
|
134
|
-
{item}
|
|
167
|
+
<Button key={item.label} variant="ghost" size="sm" render={<a href={item.href} />}>
|
|
168
|
+
{item.label}
|
|
135
169
|
</Button>
|
|
136
170
|
))}
|
|
137
171
|
</Row>
|
|
@@ -149,16 +183,16 @@ export function BrandLandingPage() {
|
|
|
149
183
|
</header>
|
|
150
184
|
|
|
151
185
|
<main>
|
|
152
|
-
<Region as="section" max="wide" className="pb-
|
|
153
|
-
<div className="grid items-center gap-
|
|
154
|
-
<Stack gap={
|
|
186
|
+
<Region as="section" max="wide" className="pb-20 pt-14 sm:pt-20 lg:pb-28">
|
|
187
|
+
<div className="grid items-center gap-12 lg:grid-cols-[1fr_1.1fr] lg:gap-10">
|
|
188
|
+
<Stack gap={3} className="max-w-2xl">
|
|
155
189
|
<Badge appearance="outline" icon={<Icon name="lock" className="text-sm" />}>
|
|
156
190
|
Private operations, made legible
|
|
157
191
|
</Badge>
|
|
158
192
|
<TextReveal
|
|
159
193
|
as="h1"
|
|
160
|
-
text="A calmer command center for high-trust
|
|
161
|
-
className="
|
|
194
|
+
text="A calmer command center for high-trust work"
|
|
195
|
+
className="text-balance text-display-lg sm:text-display-2xl"
|
|
162
196
|
once
|
|
163
197
|
/>
|
|
164
198
|
<p className="max-w-(--measure-prose) text-body-lg text-muted-foreground">
|
|
@@ -166,89 +200,96 @@ export function BrandLandingPage() {
|
|
|
166
200
|
surface: approvals, signals, records, and handoffs in one
|
|
167
201
|
understandable flow.
|
|
168
202
|
</p>
|
|
169
|
-
<Row gap={2} wrap>
|
|
203
|
+
<Row gap={2} wrap className="pt-3">
|
|
170
204
|
<Button size="cta" render={<a href="/demo" />}>
|
|
171
205
|
Book a walkthrough
|
|
172
206
|
</Button>
|
|
173
207
|
<Button size="cta" variant="outline" render={<a href="#platform" />}>
|
|
174
|
-
Explore platform
|
|
208
|
+
Explore the platform
|
|
175
209
|
</Button>
|
|
176
210
|
</Row>
|
|
177
211
|
</Stack>
|
|
178
212
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
213
|
+
{/* The product scene is a dark island: `dark` re-scopes every
|
|
214
|
+
semantic token, so its surfaces, muted ink and status tones
|
|
215
|
+
are the theme's own dark values in either page mode. It is a
|
|
216
|
+
picture of the product, so it stays out of the tab order and
|
|
217
|
+
the accessibility tree; the hero copy carries its meaning. */}
|
|
218
|
+
<div className="dark" aria-hidden inert>
|
|
219
|
+
<div className="overflow-hidden rounded-xl border border-border-floating bg-popover p-3 text-popover-foreground shadow-xl">
|
|
220
|
+
{/* Nested surfaces take concentric corners: each level's radius is
|
|
221
|
+
the shell's xl minus every inset between it and the shell edge. */}
|
|
222
|
+
<div className="rounded-concentric-xl/3 border border-border bg-card">
|
|
223
|
+
<div className="flex items-center justify-between border-b border-border px-4 py-3">
|
|
185
224
|
<Row gap={1}>
|
|
186
225
|
<StatusDot tone="warning" />
|
|
187
226
|
<StatusDot tone="edition" />
|
|
188
227
|
<StatusDot tone="success" />
|
|
189
228
|
</Row>
|
|
190
|
-
<span className="text-label-sm text-
|
|
229
|
+
<span className="text-label-sm text-muted-foreground">Live workspace</span>
|
|
191
230
|
</div>
|
|
192
231
|
<div className="grid gap-3 p-3 md:grid-cols-[0.7fr_1.3fr]">
|
|
193
|
-
<div className="space-y-3">
|
|
194
|
-
<div className="rounded-concentric-xl/6 border border-
|
|
195
|
-
<p className="text-label-sm text-
|
|
232
|
+
<div className="space-y-3 max-md:hidden">
|
|
233
|
+
<div className="rounded-concentric-xl/6 border border-border bg-background p-4">
|
|
234
|
+
<p className="text-label-sm text-muted-foreground">Spaces</p>
|
|
196
235
|
<div className="mt-4 space-y-2">
|
|
197
|
-
{
|
|
198
|
-
<div key={
|
|
236
|
+
{spaces.map((space) => (
|
|
237
|
+
<div key={space.name} className="rounded-concentric-xl/10 bg-card p-3">
|
|
199
238
|
<Row justify="between">
|
|
200
|
-
<span className="text-title-sm">{
|
|
201
|
-
<span className="text-mono-sm
|
|
239
|
+
<span className="text-title-sm">{space.name}</span>
|
|
240
|
+
<span className="text-mono-sm">{space.value}</span>
|
|
202
241
|
</Row>
|
|
203
|
-
<p className="mt-1 text-body-xs text-
|
|
242
|
+
<p className="mt-1 text-body-xs text-muted-foreground">{space.label}</p>
|
|
204
243
|
</div>
|
|
205
244
|
))}
|
|
206
245
|
</div>
|
|
207
246
|
</div>
|
|
208
|
-
<div className="rounded-concentric-xl/6 border border-
|
|
247
|
+
<div className="rounded-concentric-xl/6 border border-border bg-background p-4">
|
|
209
248
|
<Row justify="between">
|
|
210
249
|
<span className="text-title-sm">Route status</span>
|
|
211
|
-
<Badge tone="success" size="sm">
|
|
250
|
+
<Badge tone="success" size="sm" dot>
|
|
251
|
+
Healthy
|
|
252
|
+
</Badge>
|
|
212
253
|
</Row>
|
|
213
254
|
<div className="mt-4 space-y-2">
|
|
214
|
-
{
|
|
215
|
-
<Row key={
|
|
255
|
+
{routes.map((route) => (
|
|
256
|
+
<Row key={route.label} justify="between" className="rounded-full bg-card px-3 py-2">
|
|
216
257
|
<Row gap={1}>
|
|
217
|
-
<StatusDot tone={
|
|
218
|
-
<span className="text-body-sm">{
|
|
258
|
+
<StatusDot tone={route.tone} />
|
|
259
|
+
<span className="text-body-sm">{route.label}</span>
|
|
219
260
|
</Row>
|
|
220
|
-
<span className="text-label-sm text-
|
|
261
|
+
<span className="text-label-sm text-muted-foreground">{route.state}</span>
|
|
221
262
|
</Row>
|
|
222
263
|
))}
|
|
223
264
|
</div>
|
|
224
265
|
</div>
|
|
225
266
|
</div>
|
|
226
267
|
|
|
227
|
-
<div className="rounded-concentric-xl/6 border border-
|
|
228
|
-
<Row justify="between" className="border-b border-
|
|
268
|
+
<div className="rounded-concentric-xl/6 border border-border bg-background p-4">
|
|
269
|
+
<Row justify="between" className="border-b border-border pb-4">
|
|
229
270
|
<div>
|
|
230
|
-
<p className="text-label-sm text-
|
|
231
|
-
<
|
|
271
|
+
<p className="text-label-sm text-muted-foreground">Signal map</p>
|
|
272
|
+
<p className="mt-1 text-heading-4 sm:text-heading-3">Every approval path in view</p>
|
|
232
273
|
</div>
|
|
233
274
|
<Button variant="secondary" size="sm">Review</Button>
|
|
234
275
|
</Row>
|
|
235
|
-
<div className="mt-
|
|
236
|
-
<div className="grid grid-cols-9 items-end gap-
|
|
237
|
-
{bars.map((bar
|
|
238
|
-
<div key={
|
|
276
|
+
<div className="mt-5 grid gap-4 sm:mt-6 sm:gap-5">
|
|
277
|
+
<div className="grid grid-cols-9 items-end gap-1.5 rounded-concentric-xl/10 bg-card p-4 sm:gap-2">
|
|
278
|
+
{bars.map((bar) => (
|
|
279
|
+
<div key={bar.week} className="flex h-36 items-end sm:h-56">
|
|
239
280
|
<div
|
|
240
|
-
className={`w-full rounded-full ${bar.
|
|
281
|
+
className={`w-full rounded-full ${bar.current ? "bg-success" : "bg-secondary"}`}
|
|
241
282
|
style={{ height: bar.height }}
|
|
242
283
|
/>
|
|
243
284
|
</div>
|
|
244
285
|
))}
|
|
245
286
|
</div>
|
|
246
|
-
<div className="grid
|
|
247
|
-
{
|
|
248
|
-
<div key={metric.label} className="rounded-concentric-xl/10 border border-
|
|
249
|
-
<p className="text-label-sm text-
|
|
250
|
-
<p className="mt-2 text-heading-3">{metric.value}</p>
|
|
251
|
-
<p className="mt-1 text-body-xs text-
|
|
287
|
+
<div className="grid grid-cols-3 gap-2">
|
|
288
|
+
{sceneMetrics.map((metric) => (
|
|
289
|
+
<div key={metric.label} className="rounded-concentric-xl/10 border border-border bg-card p-3">
|
|
290
|
+
<p className="truncate text-label-sm text-muted-foreground">{metric.label}</p>
|
|
291
|
+
<p className="mt-2 text-heading-4 sm:text-heading-3">{metric.value}</p>
|
|
292
|
+
<p className="mt-1 text-body-xs text-muted-foreground max-sm:hidden">{metric.detail}</p>
|
|
252
293
|
</div>
|
|
253
294
|
))}
|
|
254
295
|
</div>
|
|
@@ -261,54 +302,136 @@ export function BrandLandingPage() {
|
|
|
261
302
|
</div>
|
|
262
303
|
</Region>
|
|
263
304
|
|
|
264
|
-
<section id="platform" className="border-y border-border bg-muted/40 py-
|
|
305
|
+
<section id="platform" aria-labelledby="brand-platform-title" className="border-y border-border bg-muted/40 py-20 lg:py-28">
|
|
265
306
|
<Region max="wide">
|
|
266
|
-
<
|
|
267
|
-
<Stack gap={
|
|
268
|
-
<
|
|
269
|
-
<h2 className="text-heading-1">
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
307
|
+
<Stack gap={6}>
|
|
308
|
+
<Stack gap={2} className="max-w-2xl">
|
|
309
|
+
<p className="text-overline text-muted-foreground">Why Cove</p>
|
|
310
|
+
<h2 id="brand-platform-title" className="text-balance text-heading-1">
|
|
311
|
+
One private workspace for the decisions that matter.
|
|
312
|
+
</h2>
|
|
313
|
+
<p className="max-w-(--measure-prose) text-body-lg text-muted-foreground">
|
|
314
|
+
Most operations work lives in threads, spreadsheets, and memory.
|
|
315
|
+
Cove gives it a home: every request routed, every signal visible,
|
|
316
|
+
every outcome on the record.
|
|
274
317
|
</p>
|
|
275
318
|
</Stack>
|
|
276
|
-
<
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
<
|
|
280
|
-
<
|
|
281
|
-
<
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
319
|
+
<Reveal>
|
|
320
|
+
<Grid minItemWidth="calc(var(--spacing) * 64)" gap={2}>
|
|
321
|
+
{values.map((value) => (
|
|
322
|
+
<Card key={value.title} render={<article />} className="h-full">
|
|
323
|
+
<CardContent className="flex flex-col gap-2">
|
|
324
|
+
<span className="mb-4 grid size-11 place-items-center rounded-md border border-border sable-well">
|
|
325
|
+
<Icon name={value.icon} className="text-xl" />
|
|
326
|
+
</span>
|
|
327
|
+
<h3 className="text-title-lg">{value.title}</h3>
|
|
328
|
+
<p className="text-body-sm text-muted-foreground">{value.body}</p>
|
|
329
|
+
</CardContent>
|
|
330
|
+
</Card>
|
|
331
|
+
))}
|
|
332
|
+
</Grid>
|
|
333
|
+
</Reveal>
|
|
334
|
+
</Stack>
|
|
290
335
|
</Region>
|
|
291
336
|
</section>
|
|
292
337
|
|
|
293
|
-
<Region as="section" max="wide" className="py-
|
|
294
|
-
<div className="grid gap-
|
|
295
|
-
{
|
|
296
|
-
<
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
338
|
+
<Region as="section" id="how-it-works" aria-labelledby="brand-steps-title" max="wide" className="py-20 lg:py-28">
|
|
339
|
+
<div className="grid gap-12 lg:grid-cols-[1fr_2fr] lg:gap-16">
|
|
340
|
+
<Stack gap={2} className="max-w-md">
|
|
341
|
+
<p className="text-overline text-muted-foreground">How it works</p>
|
|
342
|
+
<h2 id="brand-steps-title" className="text-balance text-heading-1">
|
|
343
|
+
From request to sealed record.
|
|
344
|
+
</h2>
|
|
345
|
+
<p className="text-body-md text-muted-foreground">
|
|
346
|
+
Teams are usually live within a week. Cove adapts to the routes
|
|
347
|
+
you already run instead of asking you to start over.
|
|
348
|
+
</p>
|
|
349
|
+
<div className="pt-3">
|
|
350
|
+
<Button variant="outline" iconEnd={<Icon name="arrow-right" />} render={<a href="/guides/rollout" />}>
|
|
351
|
+
Read the rollout guide
|
|
352
|
+
</Button>
|
|
353
|
+
</div>
|
|
354
|
+
</Stack>
|
|
355
|
+
<ol className="grid gap-10 sm:grid-cols-3 sm:gap-6">
|
|
356
|
+
{steps.map((step) => (
|
|
357
|
+
<li key={step.number} className="flex flex-col gap-2 border-t border-border-strong pt-6">
|
|
358
|
+
<span className="text-mono-sm text-muted-foreground">{step.number}</span>
|
|
359
|
+
<h3 className="mt-3 text-title-lg sm:mt-6">{step.title}</h3>
|
|
360
|
+
<p className="text-body-sm text-muted-foreground">{step.body}</p>
|
|
361
|
+
</li>
|
|
362
|
+
))}
|
|
363
|
+
</ol>
|
|
364
|
+
</div>
|
|
365
|
+
</Region>
|
|
366
|
+
|
|
367
|
+
<Region as="section" id="customers" aria-label="Customer story" max="wide" className="pb-20 lg:pb-28">
|
|
368
|
+
<Card render={<figure />}>
|
|
369
|
+
<div className="grid lg:grid-cols-[1.5fr_1fr]">
|
|
370
|
+
<CardContent className="flex flex-col justify-between gap-10 sm:p-10">
|
|
371
|
+
<Stack gap={3}>
|
|
372
|
+
<p className="text-overline text-muted-foreground">Customer story</p>
|
|
373
|
+
<blockquote className="text-balance text-heading-3 sm:text-heading-2">
|
|
374
|
+
“We used to chase approvals across four tools. Now every request
|
|
375
|
+
has an owner, a deadline, and a record, and our auditors stopped
|
|
376
|
+
asking for screenshots.”
|
|
377
|
+
</blockquote>
|
|
378
|
+
</Stack>
|
|
379
|
+
<figcaption className="flex items-center gap-3">
|
|
380
|
+
<Avatar size="lg" fallback="MO" />
|
|
381
|
+
<div>
|
|
382
|
+
<p className="text-title-sm">Maya Okafor</p>
|
|
383
|
+
<p className="text-body-sm text-muted-foreground">Head of operations, Fielddesk</p>
|
|
384
|
+
</div>
|
|
385
|
+
</figcaption>
|
|
386
|
+
</CardContent>
|
|
387
|
+
<div className="border-border max-lg:border-t lg:border-s">
|
|
388
|
+
<CardContent className="flex h-full flex-col justify-center gap-6 sm:p-10">
|
|
389
|
+
{outcomes.map((outcome, index) => (
|
|
390
|
+
<Stack key={outcome.label} gap={3}>
|
|
391
|
+
{index > 0 && <Separator />}
|
|
392
|
+
<div>
|
|
393
|
+
<p className="text-heading-1 sm:text-display-lg">{outcome.value}</p>
|
|
394
|
+
<p className="mt-1 text-body-sm text-muted-foreground">{outcome.label}</p>
|
|
395
|
+
</div>
|
|
396
|
+
</Stack>
|
|
397
|
+
))}
|
|
301
398
|
</CardContent>
|
|
302
|
-
</
|
|
303
|
-
|
|
399
|
+
</div>
|
|
400
|
+
</div>
|
|
401
|
+
</Card>
|
|
402
|
+
</Region>
|
|
403
|
+
|
|
404
|
+
<Region as="section" aria-labelledby="brand-cta-title" max="wide" className="pb-20 lg:pb-28">
|
|
405
|
+
<div className="dark">
|
|
406
|
+
<div className="rounded-xl border border-border bg-card px-6 py-12 text-card-foreground sm:px-12 sm:py-16">
|
|
407
|
+
<div className="flex flex-col gap-8 lg:flex-row lg:items-end lg:justify-between">
|
|
408
|
+
<Stack gap={3} className="max-w-2xl">
|
|
409
|
+
<h2 id="brand-cta-title" className="text-balance text-heading-1">
|
|
410
|
+
See your own approval paths in Cove.
|
|
411
|
+
</h2>
|
|
412
|
+
<p className="text-body-lg text-muted-foreground">
|
|
413
|
+
A 30-minute walkthrough with your routes, your roles, and your
|
|
414
|
+
records. No slides.
|
|
415
|
+
</p>
|
|
416
|
+
</Stack>
|
|
417
|
+
<Row gap={2} wrap>
|
|
418
|
+
<Button size="cta" render={<a href="/demo" />}>
|
|
419
|
+
Book a walkthrough
|
|
420
|
+
</Button>
|
|
421
|
+
<Button size="cta" variant="ghost" render={<a href="/pricing" />}>
|
|
422
|
+
View pricing
|
|
423
|
+
</Button>
|
|
424
|
+
</Row>
|
|
425
|
+
</div>
|
|
426
|
+
</div>
|
|
304
427
|
</div>
|
|
305
428
|
</Region>
|
|
306
429
|
</main>
|
|
307
430
|
|
|
308
431
|
<footer className="border-t border-border bg-muted/35">
|
|
309
|
-
<Region max="wide" className="py-
|
|
310
|
-
<div className="grid gap-
|
|
311
|
-
<Stack gap={
|
|
432
|
+
<Region max="wide" className="py-12 sm:py-16">
|
|
433
|
+
<div className="grid gap-10 lg:grid-cols-[1fr_1.35fr_1fr] lg:gap-8">
|
|
434
|
+
<Stack gap={4} className="max-w-sm">
|
|
312
435
|
<a href="/" className="flex w-fit items-center gap-2 rounded-full motion-interactive motion-press">
|
|
313
436
|
<span className="grid size-9 place-items-center rounded-full bg-primary text-primary-foreground">
|
|
314
437
|
<Icon name="sparkles" className="text-sm" />
|
|
@@ -319,20 +442,15 @@ export function BrandLandingPage() {
|
|
|
319
442
|
A calm operating surface for teams that need trust, speed, and
|
|
320
443
|
context without the noise.
|
|
321
444
|
</p>
|
|
322
|
-
<
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
</Badge>
|
|
326
|
-
<Badge tone="success" size="sm" icon={<Icon name="circle-check" />}>
|
|
327
|
-
Live
|
|
328
|
-
</Badge>
|
|
329
|
-
</Row>
|
|
445
|
+
<Badge appearance="plain" tone="success" dot>
|
|
446
|
+
All systems normal
|
|
447
|
+
</Badge>
|
|
330
448
|
</Stack>
|
|
331
449
|
|
|
332
|
-
<nav aria-label="Footer" className="grid
|
|
450
|
+
<nav aria-label="Footer" className="grid grid-cols-3 gap-6">
|
|
333
451
|
{footerGroups.map((group) => (
|
|
334
452
|
<div key={group.title}>
|
|
335
|
-
<h2 className="text-label-md
|
|
453
|
+
<h2 className="text-label-md">{group.title}</h2>
|
|
336
454
|
<ul className="mt-3 space-y-1">
|
|
337
455
|
{group.links.map((link) => (
|
|
338
456
|
<li key={link.label}>
|
|
@@ -349,22 +467,17 @@ export function BrandLandingPage() {
|
|
|
349
467
|
))}
|
|
350
468
|
</nav>
|
|
351
469
|
|
|
352
|
-
<Card>
|
|
470
|
+
<Card className="h-fit">
|
|
353
471
|
<CardHeader>
|
|
354
472
|
{/* CardTitle carries the type; the h2 inside it keeps the outline. */}
|
|
355
473
|
<CardTitle>
|
|
356
|
-
<h2>
|
|
474
|
+
<h2>The Cove digest</h2>
|
|
357
475
|
</CardTitle>
|
|
358
476
|
<CardDescription>
|
|
359
|
-
One
|
|
477
|
+
One note a month on trusted workflows and product releases.
|
|
360
478
|
</CardDescription>
|
|
361
|
-
<CardAction>
|
|
362
|
-
<span className="grid size-9 place-items-center rounded-full bg-highlight text-highlight-foreground">
|
|
363
|
-
<Icon name="sparkles" className="text-sm" />
|
|
364
|
-
</span>
|
|
365
|
-
</CardAction>
|
|
366
479
|
</CardHeader>
|
|
367
|
-
<CardContent
|
|
480
|
+
<CardContent>
|
|
368
481
|
<form action="/newsletter" className="grid gap-2 sm:grid-cols-[1fr_auto]">
|
|
369
482
|
<label htmlFor="brand-footer-email" className="sr-only">
|
|
370
483
|
Work email
|
|
@@ -377,42 +490,25 @@ export function BrandLandingPage() {
|
|
|
377
490
|
placeholder="you@company.com"
|
|
378
491
|
/>
|
|
379
492
|
<Button type="submit" className="w-full sm:w-auto">
|
|
380
|
-
|
|
493
|
+
Subscribe
|
|
381
494
|
</Button>
|
|
382
495
|
</form>
|
|
383
|
-
<div className="grid grid-cols-3 gap-2">
|
|
384
|
-
{footerSignals.map((signal) => (
|
|
385
|
-
<div key={signal.label} className="rounded-md bg-muted p-3">
|
|
386
|
-
<p className="text-mono-sm">{signal.value}</p>
|
|
387
|
-
<p className="mt-1 text-caption text-muted-foreground">{signal.label}</p>
|
|
388
|
-
</div>
|
|
389
|
-
))}
|
|
390
|
-
</div>
|
|
391
496
|
</CardContent>
|
|
392
497
|
</Card>
|
|
393
498
|
</div>
|
|
394
499
|
|
|
395
|
-
<div className="mt-
|
|
500
|
+
<div className="mt-12 flex flex-col gap-3 border-t border-border pt-6 text-caption text-muted-foreground sm:flex-row sm:items-center sm:justify-between">
|
|
396
501
|
<p>© 2026 Cove. All rights reserved.</p>
|
|
397
|
-
<Row gap={
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
>
|
|
408
|
-
Terms
|
|
409
|
-
</a>
|
|
410
|
-
<a
|
|
411
|
-
href="/contact"
|
|
412
|
-
className="flex min-h-9 min-w-9 items-center motion-interactive hover:text-foreground"
|
|
413
|
-
>
|
|
414
|
-
Contact
|
|
415
|
-
</a>
|
|
502
|
+
<Row gap={4} wrap>
|
|
503
|
+
{legalLinks.map((link) => (
|
|
504
|
+
<a
|
|
505
|
+
key={link.label}
|
|
506
|
+
href={link.href}
|
|
507
|
+
className="flex min-h-9 min-w-9 items-center motion-interactive hover:text-foreground"
|
|
508
|
+
>
|
|
509
|
+
{link.label}
|
|
510
|
+
</a>
|
|
511
|
+
))}
|
|
416
512
|
</Row>
|
|
417
513
|
</div>
|
|
418
514
|
</Region>
|