astryx-dracula 0.5.0 → 0.6.2
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/BRAND.md +3 -3
- package/README.md +7 -4
- package/USAGE.md +49 -49
- package/astryx-dracula.d.ts +2 -2
- package/astryx-dracula.js +25 -9
- package/astryx-dracula.variants.d.ts +2 -2
- package/astryx.integration.mjs +1 -1
- package/icons.mjs +21 -17
- package/icons.tsx +69 -62
- package/package.json +125 -120
- package/shared/auth-chrome-config.ts +6 -6
- package/shared/auth-chrome.tsx +143 -144
- package/shared/auth-copy.ts +14 -14
- package/shared/chaptered-doc-config.ts +48 -51
- package/shared/chaptered-doc.tsx +267 -289
- package/shared/chart-hues.ts +25 -10
- package/shared/chart-labels.tsx +31 -30
- package/shared/chart-legend.tsx +55 -56
- package/shared/chart-panel-style.ts +3 -3
- package/shared/data-bar.tsx +97 -102
- package/shared/format-money.ts +1 -1
- package/shared/gallery-image.ts +6 -6
- package/shared/login-demo.ts +1 -1
- package/shared/metric-card.tsx +101 -105
- package/shared/metric-delta.tsx +28 -27
- package/shared/order-desk.tsx +205 -216
- package/shared/revenue-chart.tsx +145 -165
- package/shared/scene-castle.tsx +206 -291
- package/shared/scene-frame.tsx +33 -32
- package/shared/scene-hues.ts +17 -17
- package/shared/scene-tile.tsx +88 -92
- package/shared/settings-data.ts +119 -125
- package/shared/settings-rows.tsx +122 -159
- package/shared/sparkline.tsx +75 -88
- package/shared/sso-icons.tsx +10 -22
- package/shared/stars.tsx +16 -25
- package/templates/ai-chat-landing.template.mjs +5 -4
- package/templates/ai-chat-landing.tsx +473 -460
- package/templates/ai-chat.template.mjs +5 -4
- package/templates/ai-chat.tsx +543 -612
- package/templates/blank.template.mjs +5 -4
- package/templates/blank.tsx +18 -14
- package/templates/centered-hero.template.mjs +5 -4
- package/templates/centered-hero.tsx +55 -68
- package/templates/classic-gallery.template.mjs +5 -4
- package/templates/classic-gallery.tsx +97 -104
- package/templates/contact-form.template.mjs +5 -4
- package/templates/contact-form.tsx +247 -282
- package/templates/dashboard-portfolio.template.mjs +5 -4
- package/templates/dashboard-portfolio.tsx +717 -548
- package/templates/dashboard.template.mjs +5 -4
- package/templates/dashboard.tsx +596 -567
- package/templates/detail-page.template.mjs +5 -4
- package/templates/detail-page.tsx +546 -603
- package/templates/documentation-design.template.mjs +5 -4
- package/templates/documentation-design.tsx +231 -243
- package/templates/documentation-technical.template.mjs +5 -4
- package/templates/documentation-technical.tsx +236 -239
- package/templates/documentation.template.mjs +5 -4
- package/templates/documentation.tsx +267 -279
- package/templates/editor.template.mjs +5 -4
- package/templates/editor.tsx +844 -877
- package/templates/file-explorer.template.mjs +5 -4
- package/templates/file-explorer.tsx +566 -590
- package/templates/form-two-column.template.mjs +5 -4
- package/templates/form-two-column.tsx +195 -210
- package/templates/gallery-hero.template.mjs +5 -4
- package/templates/gallery-hero.tsx +93 -114
- package/templates/ide.template.mjs +5 -4
- package/templates/ide.tsx +407 -453
- package/templates/incident-console.template.mjs +5 -4
- package/templates/incident-console.tsx +593 -575
- package/templates/kanban-board.template.mjs +5 -4
- package/templates/kanban-board.tsx +665 -683
- package/templates/library.template.mjs +5 -4
- package/templates/library.tsx +409 -430
- package/templates/login-card.template.mjs +5 -4
- package/templates/login-card.tsx +63 -72
- package/templates/login-split.template.mjs +5 -4
- package/templates/login-split.tsx +192 -193
- package/templates/login-sso.template.mjs +5 -4
- package/templates/login-sso.tsx +257 -258
- package/templates/login.template.mjs +5 -4
- package/templates/login.tsx +53 -49
- package/templates/messaging-shell.template.mjs +5 -4
- package/templates/messaging-shell.tsx +556 -574
- package/templates/mixed-gallery.template.mjs +5 -4
- package/templates/mixed-gallery.tsx +60 -58
- package/templates/payment-form.template.mjs +5 -4
- package/templates/payment-form.tsx +882 -991
- package/templates/product-detail.template.mjs +5 -4
- package/templates/product-detail.tsx +261 -272
- package/templates/product-gallery.template.mjs +5 -4
- package/templates/product-gallery.tsx +126 -137
- package/templates/product-tour.template.mjs +5 -4
- package/templates/product-tour.tsx +741 -773
- package/templates/settings-dialog.template.mjs +5 -4
- package/templates/settings-dialog.tsx +688 -717
- package/templates/settings-sidebar.template.mjs +5 -4
- package/templates/settings-sidebar.tsx +762 -807
- package/templates/settings.template.mjs +5 -4
- package/templates/settings.tsx +289 -304
- package/templates/shell-nav.template.mjs +5 -4
- package/templates/shell-nav.tsx +212 -219
- package/templates/shell-side-nav.template.mjs +5 -4
- package/templates/shell-side-nav.tsx +135 -157
- package/templates/shell-top-nav.template.mjs +5 -4
- package/templates/shell-top-nav.tsx +182 -203
- package/templates/side-gallery.template.mjs +5 -4
- package/templates/side-gallery.tsx +85 -90
- package/templates/table-grouped.template.mjs +5 -4
- package/templates/table-grouped.tsx +1000 -1053
- package/templates/table-page-chart.template.mjs +5 -4
- package/templates/table-page-chart.tsx +400 -398
- package/templates/table-page-heatmap-status.template.mjs +5 -4
- package/templates/table-page-heatmap-status.tsx +483 -512
- package/templates/table-page-shoe-store-heatmap.template.mjs +5 -4
- package/templates/table-page-shoe-store-heatmap.tsx +748 -746
- package/templates/table-page.template.mjs +5 -4
- package/templates/table-page.tsx +396 -405
- package/templates/table.template.mjs +5 -4
- package/templates/table.tsx +92 -93
- package/templates/tech-report.template.mjs +5 -4
- package/templates/tech-report.tsx +1316 -1304
- package/templates/theme-showcase.template.mjs +5 -4
- package/templates/theme-showcase.tsx +871 -951
- package/theme.css +35 -77
- package/theme.css.d.ts +9 -0
- package/tokens.css +373 -380
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
type: "page",
|
|
3
|
+
name: "Two-column Form",
|
|
4
|
+
description: "Contact form with illustration and info strip",
|
|
5
|
+
keywords: ["form", "two column", "contact", "split", "signup", "lead"],
|
|
6
|
+
category: "forms",
|
|
6
7
|
};
|
|
@@ -1,58 +1,60 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
// XLE (canonical structure
|
|
3
|
-
//
|
|
2
|
+
// XLE (canonical structure of the tree below; the CLI had no XLE validator
|
|
3
|
+
// after `astryx layout` was removed in 0.7.0, so this is a reading aid and a
|
|
4
|
+
// human drift check — update it with the file, never alone):
|
|
5
|
+
// Ctr > S.transparent[mw=1100 w=100% p=10] > V[g=10] > (G[c={min:280,max:2} g=10 a=center] > (V[g=6] > (V[g=3] > Hd"Let's conjure together"[level=1 t=display-1] + Tx"Tell us what you're brewing"[t=body]) + (AR[ratio=4/3] > Scene{SceneCastle})) + (C[p=4] > V[g=4] > Hd"Your details"[level=2] + TI"Full name"[req] + (G[c={min:180} g=3] > TI"Email"[req] + TI"Company name") + (G[c={min:180} g=3] > TI"Job title" + TI"Phone number") + (V[g=2] > Tx"What are you reaching out about?"[t=label] + (H[g=2] > Tk"Reason"*7)) + SE"Budget range" + TA"Project details" + Bn.success"Sent into the night" + (V[a=stretch] > B.primary"Send it into the night"))) + (V[g=6] > D + (G[c={min:200} g=6] > (V[g=1 a=center] > Tx"General"[t=supporting] + Lk"hello@castle.dracula")*3))
|
|
4
6
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {Check} from
|
|
24
|
-
import {
|
|
7
|
+
import { AspectRatio } from "@astryxdesign/core/AspectRatio";
|
|
8
|
+
import { Banner } from "@astryxdesign/core/Banner";
|
|
9
|
+
import { Button } from "@astryxdesign/core/Button";
|
|
10
|
+
import { Card } from "@astryxdesign/core/Card";
|
|
11
|
+
import { Center } from "@astryxdesign/core/Center";
|
|
12
|
+
import { Divider } from "@astryxdesign/core/Divider";
|
|
13
|
+
import { Grid } from "@astryxdesign/core/Grid";
|
|
14
|
+
import { Icon } from "@astryxdesign/core/Icon";
|
|
15
|
+
import { HStack, VStack } from "@astryxdesign/core/Layout";
|
|
16
|
+
import { Link } from "@astryxdesign/core/Link";
|
|
17
|
+
import { Section } from "@astryxdesign/core/Section";
|
|
18
|
+
import { Selector } from "@astryxdesign/core/Selector";
|
|
19
|
+
import { Heading, Text } from "@astryxdesign/core/Text";
|
|
20
|
+
import { TextArea } from "@astryxdesign/core/TextArea";
|
|
21
|
+
import { TextInput } from "@astryxdesign/core/TextInput";
|
|
22
|
+
import { Token } from "@astryxdesign/core/Token";
|
|
23
|
+
import { galleryImageClip } from "astryx-dracula/shared/gallery-image";
|
|
24
|
+
import { SceneCastle } from "astryx-dracula/shared/scene-castle";
|
|
25
|
+
import { Check } from "lucide-react";
|
|
26
|
+
import { type CSSProperties, useState } from "react";
|
|
25
27
|
|
|
26
28
|
const INQUIRY_REASONS = [
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
"New business",
|
|
30
|
+
"General inquiry",
|
|
31
|
+
"Press & media",
|
|
32
|
+
"Partnerships",
|
|
33
|
+
"Product feedback",
|
|
34
|
+
"Technical support",
|
|
35
|
+
"Other",
|
|
34
36
|
];
|
|
35
37
|
|
|
36
38
|
const BUDGET_OPTIONS = [
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
"Under $10k",
|
|
40
|
+
"$10k – $50k",
|
|
41
|
+
"$50k – $100k",
|
|
42
|
+
"$100k – $500k",
|
|
43
|
+
"$500k+",
|
|
44
|
+
"Not sure yet",
|
|
43
45
|
];
|
|
44
46
|
|
|
45
47
|
const CONTACT_COLUMNS = [
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
{ label: "General inquiries", email: "hello@castle.dracula" },
|
|
49
|
+
{ label: "New business", email: "newbiz@castle.dracula" },
|
|
50
|
+
{ label: "Press & partnerships", email: "press@castle.dracula" },
|
|
49
51
|
];
|
|
50
52
|
|
|
51
53
|
// Castle illustration lives in shared/scene-castle (`card` variant:
|
|
52
54
|
// harvest-yellow moon, fg-stroke bats). Fill + radius-clip live in
|
|
53
55
|
// shared/gallery-image (no Image primitive, #2582).
|
|
54
56
|
const pageStyle: CSSProperties = {
|
|
55
|
-
|
|
57
|
+
minHeight: "100%",
|
|
56
58
|
};
|
|
57
59
|
|
|
58
60
|
/**
|
|
@@ -70,183 +72,166 @@ const pageStyle: CSSProperties = {
|
|
|
70
72
|
* from 504px, one column below.
|
|
71
73
|
*/
|
|
72
74
|
export default function TwoColumnForm() {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
const [fullName, setFullName] = useState("");
|
|
76
|
+
const [email, setEmail] = useState("");
|
|
77
|
+
const [company, setCompany] = useState("");
|
|
78
|
+
const [jobTitle, setJobTitle] = useState("");
|
|
79
|
+
const [phone, setPhone] = useState("");
|
|
80
|
+
const [inquiryReason, setInquiryReason] = useState("");
|
|
81
|
+
const [budget, setBudget] = useState("");
|
|
82
|
+
const [details, setDetails] = useState("");
|
|
83
|
+
const [submitted, setSubmitted] = useState(false);
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
85
|
+
const errors = submitted
|
|
86
|
+
? {
|
|
87
|
+
fullName: !fullName.trim() ? "Required" : undefined,
|
|
88
|
+
email: !email.trim() ? "Required" : undefined,
|
|
89
|
+
details: !details.trim() ? "Required" : undefined,
|
|
90
|
+
}
|
|
91
|
+
: {};
|
|
90
92
|
|
|
91
|
-
|
|
92
|
-
fullName.trim() !== '' &&
|
|
93
|
-
email.trim() !== '' &&
|
|
94
|
-
details.trim() !== '';
|
|
93
|
+
const isValid = fullName.trim() !== "" && email.trim() !== "" && details.trim() !== "";
|
|
95
94
|
|
|
96
|
-
|
|
95
|
+
const handleSubmit = () => setSubmitted(true);
|
|
97
96
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
97
|
+
return (
|
|
98
|
+
<Center style={pageStyle}>
|
|
99
|
+
<Section maxWidth={1100} width="100%" padding={10} variant="transparent">
|
|
100
|
+
<VStack gap={10}>
|
|
101
|
+
<Grid columns={{ minWidth: 280, max: 2 }} align="center" gap={10}>
|
|
102
|
+
<VStack gap={6}>
|
|
103
|
+
<VStack gap={3}>
|
|
104
|
+
<Heading level={1} type="display-1">
|
|
105
|
+
Let's conjure together
|
|
106
|
+
</Heading>
|
|
107
|
+
<Text type="body" color="secondary">
|
|
108
|
+
Tell us what you're brewing and we'll help you find the best path
|
|
109
|
+
through the night.
|
|
110
|
+
</Text>
|
|
111
|
+
</VStack>
|
|
112
|
+
<AspectRatio ratio={4 / 3} style={galleryImageClip}>
|
|
113
|
+
<SceneCastle variant="card" />
|
|
114
|
+
</AspectRatio>
|
|
115
|
+
</VStack>
|
|
117
116
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
/>
|
|
160
|
-
<TextInput
|
|
161
|
-
label="Phone number"
|
|
162
|
-
placeholder="Phone number"
|
|
163
|
-
value={phone}
|
|
164
|
-
onChange={setPhone}
|
|
165
|
-
/>
|
|
166
|
-
</Grid>
|
|
117
|
+
<Card padding={4}>
|
|
118
|
+
<VStack gap={4}>
|
|
119
|
+
<Heading level={2}>Your details</Heading>
|
|
120
|
+
<TextInput
|
|
121
|
+
label="Full name"
|
|
122
|
+
isRequired
|
|
123
|
+
placeholder="Full name"
|
|
124
|
+
value={fullName}
|
|
125
|
+
onChange={setFullName}
|
|
126
|
+
status={errors.fullName ? { type: "error", message: errors.fullName } : undefined}
|
|
127
|
+
/>
|
|
128
|
+
<Grid columns={{ minWidth: 180 }} gap={3}>
|
|
129
|
+
<TextInput
|
|
130
|
+
label="Email"
|
|
131
|
+
isRequired
|
|
132
|
+
placeholder="Email"
|
|
133
|
+
value={email}
|
|
134
|
+
onChange={setEmail}
|
|
135
|
+
status={errors.email ? { type: "error", message: errors.email } : undefined}
|
|
136
|
+
/>
|
|
137
|
+
<TextInput
|
|
138
|
+
label="Company name"
|
|
139
|
+
placeholder="Company name"
|
|
140
|
+
value={company}
|
|
141
|
+
onChange={setCompany}
|
|
142
|
+
/>
|
|
143
|
+
</Grid>
|
|
144
|
+
<Grid columns={{ minWidth: 180 }} gap={3}>
|
|
145
|
+
<TextInput
|
|
146
|
+
label="Job title"
|
|
147
|
+
placeholder="Job title"
|
|
148
|
+
value={jobTitle}
|
|
149
|
+
onChange={setJobTitle}
|
|
150
|
+
/>
|
|
151
|
+
<TextInput
|
|
152
|
+
label="Phone number"
|
|
153
|
+
placeholder="Phone number"
|
|
154
|
+
value={phone}
|
|
155
|
+
onChange={setPhone}
|
|
156
|
+
/>
|
|
157
|
+
</Grid>
|
|
167
158
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
)}
|
|
220
|
-
{/* hAlign="stretch" = full-width button workaround; Button
|
|
159
|
+
<VStack gap={2}>
|
|
160
|
+
<Text type="label">What are you reaching out about?</Text>
|
|
161
|
+
<HStack gap={2} wrap="wrap">
|
|
162
|
+
{INQUIRY_REASONS.map((reason) => {
|
|
163
|
+
const isSelected = inquiryReason === reason;
|
|
164
|
+
return (
|
|
165
|
+
<Token
|
|
166
|
+
key={reason}
|
|
167
|
+
label={reason}
|
|
168
|
+
color={isSelected ? "yellow" : "default"}
|
|
169
|
+
icon={
|
|
170
|
+
isSelected ? (
|
|
171
|
+
<Icon icon={Check} size="xsm" color="inherit" />
|
|
172
|
+
) : undefined
|
|
173
|
+
}
|
|
174
|
+
description={isSelected ? "Selected" : undefined}
|
|
175
|
+
onClick={() =>
|
|
176
|
+
setInquiryReason((prev) => (prev === reason ? "" : reason))
|
|
177
|
+
}
|
|
178
|
+
/>
|
|
179
|
+
);
|
|
180
|
+
})}
|
|
181
|
+
</HStack>
|
|
182
|
+
</VStack>
|
|
183
|
+
<Selector
|
|
184
|
+
label="Budget range"
|
|
185
|
+
options={BUDGET_OPTIONS}
|
|
186
|
+
value={budget}
|
|
187
|
+
onChange={setBudget}
|
|
188
|
+
placeholder="Select a budget range…"
|
|
189
|
+
/>
|
|
190
|
+
<TextArea
|
|
191
|
+
label="Project details"
|
|
192
|
+
isRequired
|
|
193
|
+
placeholder="Project details"
|
|
194
|
+
value={details}
|
|
195
|
+
onChange={setDetails}
|
|
196
|
+
status={errors.details ? { type: "error", message: errors.details } : undefined}
|
|
197
|
+
/>
|
|
198
|
+
{submitted && isValid && (
|
|
199
|
+
<Banner
|
|
200
|
+
status="success"
|
|
201
|
+
// Core 0.7.0 stopped deriving the live-region role from
|
|
202
|
+
// `status`, so this banner states its own. It mirrors the
|
|
203
|
+
// submit result, and a screen reader must hear it.
|
|
204
|
+
role="status"
|
|
205
|
+
title="Sent into the night"
|
|
206
|
+
description="The coven received your message and will answer after dark."
|
|
207
|
+
/>
|
|
208
|
+
)}
|
|
209
|
+
{/* hAlign="stretch" = full-width button workaround; Button
|
|
221
210
|
has no full-width prop (#2600). */}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
</VStack>
|
|
229
|
-
</VStack>
|
|
230
|
-
</Card>
|
|
231
|
-
</Grid>
|
|
211
|
+
<VStack hAlign="stretch">
|
|
212
|
+
<Button label="Send it into the night" variant="primary" onClick={handleSubmit} />
|
|
213
|
+
</VStack>
|
|
214
|
+
</VStack>
|
|
215
|
+
</Card>
|
|
216
|
+
</Grid>
|
|
232
217
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
218
|
+
<VStack gap={6}>
|
|
219
|
+
<Divider />
|
|
220
|
+
<Grid columns={{ minWidth: 200 }} gap={6}>
|
|
221
|
+
{CONTACT_COLUMNS.map((col) => (
|
|
222
|
+
<VStack key={col.label} gap={1} hAlign="center">
|
|
223
|
+
<Text type="supporting" color="secondary">
|
|
224
|
+
{col.label}
|
|
225
|
+
</Text>
|
|
226
|
+
<Link href={`mailto:${col.email}`} hasUnderline>
|
|
227
|
+
{col.email}
|
|
228
|
+
</Link>
|
|
229
|
+
</VStack>
|
|
230
|
+
))}
|
|
231
|
+
</Grid>
|
|
232
|
+
</VStack>
|
|
233
|
+
</VStack>
|
|
234
|
+
</Section>
|
|
235
|
+
</Center>
|
|
236
|
+
);
|
|
252
237
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
type: "page",
|
|
3
|
+
name: "Gallery Hero",
|
|
4
|
+
description: "Headline, CTAs, and three-image gallery",
|
|
5
|
+
keywords: ["gallery", "hero", "showcase", "carousel", "featured", "spotlight"],
|
|
6
|
+
category: "marketing",
|
|
6
7
|
};
|