@marktiderman/genesis-sites 0.1.0 → 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/README.md +241 -13
- package/dist/assets/islands.77d11f5697.js +1 -0
- package/dist/assets/sites-forms.732cc7e0c1.css +10 -0
- package/dist/assets/sites-forms.css +1 -1
- package/dist/assets/sites.716b4ac1a5.css +2 -0
- package/dist/assets/sites.css +1 -1
- package/dist/assets.generated-QSB7EJWX.js +3 -0
- package/dist/assets.generated-QSB7EJWX.js.map +1 -0
- package/dist/blocks.d.ts +1 -1
- package/dist/blocks.js +5 -1
- package/dist/blocks.js.map +1 -1
- package/dist/chunk-3VKTBE7S.js +69 -0
- package/dist/chunk-3VKTBE7S.js.map +1 -0
- package/dist/chunk-5IXDFFEG.js +465 -0
- package/dist/chunk-5IXDFFEG.js.map +1 -0
- package/dist/chunk-C375ZPU2.js +2241 -0
- package/dist/chunk-C375ZPU2.js.map +1 -0
- package/dist/{chunk-GA7U3CIN.js → chunk-FBQF2L7O.js} +317 -97
- package/dist/chunk-FBQF2L7O.js.map +1 -0
- package/dist/chunk-H6ED3K4R.js +18 -0
- package/dist/chunk-H6ED3K4R.js.map +1 -0
- package/dist/chunk-KPOX7D4S.js +250 -0
- package/dist/chunk-KPOX7D4S.js.map +1 -0
- package/dist/chunk-VZUDRPFG.js +13 -0
- package/dist/chunk-VZUDRPFG.js.map +1 -0
- package/dist/{chunk-KEF3V5ZN.js → chunk-ZGWGQGT2.js} +169 -73
- package/dist/chunk-ZGWGQGT2.js.map +1 -0
- package/dist/editor.d.ts +526 -0
- package/dist/editor.js +2480 -0
- package/dist/editor.js.map +1 -0
- package/dist/handler-QHH453YG.js +9 -0
- package/dist/handler-QHH453YG.js.map +1 -0
- package/dist/import.d.ts +130 -0
- package/dist/import.js +330 -0
- package/dist/import.js.map +1 -0
- package/dist/index.d.ts +47 -8
- package/dist/index.js +5 -250
- package/dist/index.js.map +1 -1
- package/dist/{registry-CdgVymsu.d.ts → registry-HBn1X60x.d.ts} +274 -8
- package/dist/sections.d.ts +422 -6
- package/dist/sections.js +4 -2
- package/dist/{store-BzGw2i5J.d.ts → store-BXiZeZzH.d.ts} +119 -53
- package/dist/supabase.d.ts +5 -5
- package/dist/supabase.js +115 -6
- package/dist/supabase.js.map +1 -1
- package/dist/web.d.ts +26 -8
- package/dist/web.js +7 -353
- package/dist/web.js.map +1 -1
- package/migrations/0001_genesis_sites.sql +64 -6
- package/migrations/0002_genesis_sites_posts.sql +404 -0
- package/migrations/tests/0001_genesis_sites.test.sql +30 -1
- package/migrations/tests/0002_genesis_sites_posts.test.sql +242 -0
- package/package.json +28 -10
- package/dist/assets/islands.230b20b9c4.js +0 -1
- package/dist/assets/sites-forms.5e6ebf31c3.css +0 -10
- package/dist/assets/sites.b28b796c6f.css +0 -2
- package/dist/chunk-43XUAO63.js +0 -1255
- package/dist/chunk-43XUAO63.js.map +0 -1
- package/dist/chunk-GA7U3CIN.js.map +0 -1
- package/dist/chunk-KEF3V5ZN.js.map +0 -1
|
@@ -0,0 +1,2241 @@
|
|
|
1
|
+
import { mailtoHref } from './chunk-VZUDRPFG.js';
|
|
2
|
+
import { Link, Surface, Copy, Line, defineSection, Image, Href, RichText, FileRef, registerBuiltInSection, safeJsonForHtml, postPath, opensInNewTab } from './chunk-ZGWGQGT2.js';
|
|
3
|
+
import { isSafeImageSrc, isSafeHref, isExternalHref } from './chunk-3VKTBE7S.js';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { BandLead, Band, Display } from '@marktiderman/genesis-ui/band';
|
|
6
|
+
import { buttonVariants } from '@marktiderman/genesis-ui/button';
|
|
7
|
+
import { cn } from '@marktiderman/genesis-ui/utils';
|
|
8
|
+
import { Zap, Users, Trophy, Target, Star, Sparkles, Smile, Shield, Phone, MessageCircle, MapPin, Mail, Leaf, Heart, Dumbbell, Clock, Check, Calendar, Award, Activity, ChevronDown, FileText, ArrowLeft, Menu, Download } from 'lucide-react';
|
|
9
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
10
|
+
import { parseForm, choiceOptions } from '@marktiderman/genesis-forms';
|
|
11
|
+
import { Form } from '@marktiderman/genesis-forms-web';
|
|
12
|
+
import { Input } from '@marktiderman/genesis-ui/input';
|
|
13
|
+
import { Textarea } from '@marktiderman/genesis-ui/textarea';
|
|
14
|
+
import { RichText as RichText$1, richTextClassName } from '@marktiderman/genesis-ui/rich-text';
|
|
15
|
+
|
|
16
|
+
var Anchor = z.string().regex(/^[a-z][a-z0-9-]{0,39}$/, "An anchor is lowercase letters, digits and hyphens.").optional().meta({ title: "Anchor (for #links)" });
|
|
17
|
+
var ICONS = {
|
|
18
|
+
activity: Activity,
|
|
19
|
+
award: Award,
|
|
20
|
+
calendar: Calendar,
|
|
21
|
+
check: Check,
|
|
22
|
+
clock: Clock,
|
|
23
|
+
dumbbell: Dumbbell,
|
|
24
|
+
heart: Heart,
|
|
25
|
+
leaf: Leaf,
|
|
26
|
+
mail: Mail,
|
|
27
|
+
"map-pin": MapPin,
|
|
28
|
+
message: MessageCircle,
|
|
29
|
+
phone: Phone,
|
|
30
|
+
shield: Shield,
|
|
31
|
+
smile: Smile,
|
|
32
|
+
sparkles: Sparkles,
|
|
33
|
+
star: Star,
|
|
34
|
+
target: Target,
|
|
35
|
+
trophy: Trophy,
|
|
36
|
+
users: Users,
|
|
37
|
+
zap: Zap
|
|
38
|
+
};
|
|
39
|
+
var IconName = z.enum(Object.keys(ICONS)).meta({ title: "Icon" });
|
|
40
|
+
function resolveHref(href, ctx) {
|
|
41
|
+
if (!isSafeHref(href)) {
|
|
42
|
+
console.error(`[genesis-sites] refused an unsafe link on ${ctx.pagePath}: ${JSON.stringify(String(href).slice(0, 80))}`);
|
|
43
|
+
return "#";
|
|
44
|
+
}
|
|
45
|
+
return ctx.href(href);
|
|
46
|
+
}
|
|
47
|
+
function linkAttrs(link, ctx, defaultEvent) {
|
|
48
|
+
let href = resolveHref(link.href, ctx);
|
|
49
|
+
const absolute = isExternalHref(href);
|
|
50
|
+
if (link.external && !absolute) {
|
|
51
|
+
console.error(`[genesis-sites] refused an outbound link that isn't http(s) on ${ctx.pagePath}.`);
|
|
52
|
+
href = "#";
|
|
53
|
+
}
|
|
54
|
+
const newTab = absolute && opensInNewTab(link);
|
|
55
|
+
const outbound = absolute && (link.external === true || newTab);
|
|
56
|
+
const event = link.event ?? defaultEvent;
|
|
57
|
+
return {
|
|
58
|
+
href,
|
|
59
|
+
...newTab ? { target: "_blank" } : {},
|
|
60
|
+
...outbound ? { rel: "noopener noreferrer", "data-gs-outbound": "" } : absolute ? { rel: "noopener" } : {},
|
|
61
|
+
...event ? { "data-gs-event": event } : {}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function withEventLast(attrs, className) {
|
|
65
|
+
const { "data-gs-event": event, ...rest } = attrs;
|
|
66
|
+
return { ...rest, className, ...event ? { "data-gs-event": event } : {} };
|
|
67
|
+
}
|
|
68
|
+
function NewTabHint({ link }) {
|
|
69
|
+
return opensInNewTab(link) ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: " (opens in a new tab)" }) : null;
|
|
70
|
+
}
|
|
71
|
+
var TONE = {
|
|
72
|
+
primary: buttonVariants({ variant: "default", size: "lg" }),
|
|
73
|
+
secondary: buttonVariants({ variant: "secondary", size: "lg" }),
|
|
74
|
+
outline: buttonVariants({ variant: "outline", size: "lg" }),
|
|
75
|
+
// On an accent or inverse band: the band's foreground as the button, its background as the text.
|
|
76
|
+
inverse: cn(buttonVariants({ variant: "default", size: "lg" }), "bg-background text-foreground hover:bg-background/90"),
|
|
77
|
+
ghost: buttonVariants({ variant: "ghost", size: "lg" })
|
|
78
|
+
};
|
|
79
|
+
function ButtonLink({ link, ctx, tone = "primary", className }) {
|
|
80
|
+
return /* @__PURE__ */ jsxs("a", { ...withEventLast(linkAttrs(link, ctx, "cta"), cn(TONE[tone], "min-h-11", className)), children: [
|
|
81
|
+
link.label,
|
|
82
|
+
/* @__PURE__ */ jsx(NewTabHint, { link })
|
|
83
|
+
] });
|
|
84
|
+
}
|
|
85
|
+
function TextLink({ link, ctx, className }) {
|
|
86
|
+
return /* @__PURE__ */ jsxs(
|
|
87
|
+
"a",
|
|
88
|
+
{
|
|
89
|
+
...linkAttrs(link, ctx),
|
|
90
|
+
className: cn("inline-flex min-h-6 items-center rounded-sm underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring", className),
|
|
91
|
+
children: [
|
|
92
|
+
link.label,
|
|
93
|
+
/* @__PURE__ */ jsx(NewTabHint, { link })
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
function Actions({ primary, secondary, ctx, onAccent: onAccent2, className }) {
|
|
99
|
+
if (!primary && !secondary) return null;
|
|
100
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-3 sm:flex-row sm:flex-wrap", className), children: [
|
|
101
|
+
primary ? /* @__PURE__ */ jsx(ButtonLink, { link: primary, ctx, tone: onAccent2 ? "inverse" : "primary" }) : null,
|
|
102
|
+
secondary ? /* @__PURE__ */ jsx(ButtonLink, { link: secondary, ctx, tone: onAccent2 ? "ghost" : "outline", className: onAccent2 ? "border border-current hover:bg-transparent hover:underline" : void 0 }) : null
|
|
103
|
+
] });
|
|
104
|
+
}
|
|
105
|
+
var FILE_KIND = {
|
|
106
|
+
"application/pdf": "PDF",
|
|
107
|
+
"image/png": "PNG image",
|
|
108
|
+
"image/jpeg": "JPEG image",
|
|
109
|
+
"image/webp": "WebP image",
|
|
110
|
+
"image/avif": "AVIF image",
|
|
111
|
+
"image/gif": "GIF image"
|
|
112
|
+
};
|
|
113
|
+
function fileDescription(file) {
|
|
114
|
+
const kind = FILE_KIND[file.mime] ?? "File";
|
|
115
|
+
if (!file.bytes) return kind;
|
|
116
|
+
const size = file.bytes >= 1024 * 1024 ? `${(file.bytes / (1024 * 1024)).toFixed(1)} MB` : `${Math.max(1, Math.round(file.bytes / 1024))} KB`;
|
|
117
|
+
return `${kind}, ${size}`;
|
|
118
|
+
}
|
|
119
|
+
function fileHref(file, ctx) {
|
|
120
|
+
if (!isSafeImageSrc(file.src)) {
|
|
121
|
+
console.error(`[genesis-sites] refused an unsafe file link on ${ctx.pagePath}.`);
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
return file.src.startsWith("/") ? ctx.href(file.src) : file.src;
|
|
125
|
+
}
|
|
126
|
+
function SiteImage({ image, className, sizes, priority, ctx }) {
|
|
127
|
+
if (!isSafeImageSrc(image.src)) {
|
|
128
|
+
console.error(`[genesis-sites] refused an unsafe image source on ${ctx.pagePath}.`);
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
const src = image.src.startsWith("/") ? ctx.href(image.src) : image.src;
|
|
132
|
+
return /* @__PURE__ */ jsx(
|
|
133
|
+
"img",
|
|
134
|
+
{
|
|
135
|
+
src,
|
|
136
|
+
alt: image.decorative ? "" : image.alt,
|
|
137
|
+
...image.width ? { width: image.width } : {},
|
|
138
|
+
...image.height ? { height: image.height } : {},
|
|
139
|
+
...sizes ? { sizes } : {},
|
|
140
|
+
loading: priority ? "eager" : "lazy",
|
|
141
|
+
decoding: priority ? "sync" : "async",
|
|
142
|
+
...priority ? { fetchPriority: "high" } : {},
|
|
143
|
+
style: image.focal ? { objectPosition: `${image.focal.x}% ${image.focal.y}%` } : void 0,
|
|
144
|
+
className
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
function SectionTitle({ ctx, id, size, className, children }) {
|
|
149
|
+
return /* @__PURE__ */ jsx(Display, { as: ctx.headingLevel === 1 ? "h1" : "h2", id, size: size ?? (ctx.headingLevel === 1 ? "xl" : "lg"), className, children });
|
|
150
|
+
}
|
|
151
|
+
function ItemTitle({ ctx, className, children }) {
|
|
152
|
+
const Tag = ctx.headingLevel === 1 ? "h2" : "h3";
|
|
153
|
+
return /* @__PURE__ */ jsx(Tag, { className: cn("font-display text-lg font-semibold leading-snug", className), children });
|
|
154
|
+
}
|
|
155
|
+
var titleId = (ctx, part = "title") => `${ctx.idPrefix}-${part}`;
|
|
156
|
+
var HIDE_ON = { mobile: "max-md:hidden", desktop: "md:hidden" };
|
|
157
|
+
function bandAttrs(node, ctx, anchor, labelled = true) {
|
|
158
|
+
const hide = (node.hideOn ?? []).map((h) => HIDE_ON[h]).filter(Boolean).join(" ");
|
|
159
|
+
return {
|
|
160
|
+
...anchor ? { id: anchor } : {},
|
|
161
|
+
...labelled ? { "aria-labelledby": titleId(ctx) } : {},
|
|
162
|
+
"data-gs-section": node.type,
|
|
163
|
+
"data-gs-variant": node.variant,
|
|
164
|
+
className: cn(anchor ? "scroll-mt-20" : void 0, hide || void 0)
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function SectionBand({
|
|
168
|
+
node,
|
|
169
|
+
ctx,
|
|
170
|
+
anchor,
|
|
171
|
+
surface = "base",
|
|
172
|
+
spacing = "md",
|
|
173
|
+
width = "lg",
|
|
174
|
+
labelled = true,
|
|
175
|
+
className,
|
|
176
|
+
children
|
|
177
|
+
}) {
|
|
178
|
+
const attrs = bandAttrs(node, ctx, anchor, labelled);
|
|
179
|
+
return /* @__PURE__ */ jsx(Band, { ...attrs, as: labelled ? "section" : "div", surface, spacing, width, className: cn(attrs.className, className), children });
|
|
180
|
+
}
|
|
181
|
+
function SectionIntro({
|
|
182
|
+
ctx,
|
|
183
|
+
eyebrow,
|
|
184
|
+
heading,
|
|
185
|
+
body,
|
|
186
|
+
align = "center",
|
|
187
|
+
tone = "muted",
|
|
188
|
+
size
|
|
189
|
+
}) {
|
|
190
|
+
if (!heading && !body) return null;
|
|
191
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex max-w-3xl flex-col gap-4", align === "center" ? "mx-auto items-center text-center" : "items-start text-left"), children: [
|
|
192
|
+
eyebrow ? /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold uppercase tracking-wider", children: eyebrow }) : null,
|
|
193
|
+
heading ? /* @__PURE__ */ jsx(SectionTitle, { ctx, id: titleId(ctx), size, children: heading }) : null,
|
|
194
|
+
body ? /* @__PURE__ */ jsx(BandLead, { tone, children: body }) : null
|
|
195
|
+
] });
|
|
196
|
+
}
|
|
197
|
+
function HiddenTitle({ ctx, children }) {
|
|
198
|
+
const Tag = ctx.headingLevel === 1 ? "h1" : "h2";
|
|
199
|
+
return /* @__PURE__ */ jsx(Tag, { id: titleId(ctx), className: "sr-only", children });
|
|
200
|
+
}
|
|
201
|
+
var onAccent = (surface) => surface === "accent" || surface === "inverse";
|
|
202
|
+
var schema = z.object({
|
|
203
|
+
heading: Line(90),
|
|
204
|
+
body: Copy(320).optional(),
|
|
205
|
+
primaryCta: Link,
|
|
206
|
+
secondaryCta: Link.optional(),
|
|
207
|
+
surface: Surface.default("accent"),
|
|
208
|
+
anchor: Anchor
|
|
209
|
+
});
|
|
210
|
+
var ctaSection = defineSection({
|
|
211
|
+
type: "cta",
|
|
212
|
+
version: 1,
|
|
213
|
+
label: "Call to action",
|
|
214
|
+
description: "One clear next step: a heading, a line and a button.",
|
|
215
|
+
category: "conversion",
|
|
216
|
+
schema,
|
|
217
|
+
variants: [
|
|
218
|
+
{ id: "banner", label: "Full-width banner" },
|
|
219
|
+
{ id: "card", label: "Card" },
|
|
220
|
+
{ id: "split", label: "Text left, buttons right" }
|
|
221
|
+
],
|
|
222
|
+
defaultVariant: "banner",
|
|
223
|
+
presets: [
|
|
224
|
+
{ name: "Banner", variant: "banner", props: { heading: "Ready to start?", primaryCta: { label: "Get started", href: "#contact" }, surface: "accent" } },
|
|
225
|
+
{ name: "Card", variant: "card", props: { heading: "Questions? We're here.", primaryCta: { label: "Contact us", href: "/contact" }, surface: "muted" } }
|
|
226
|
+
],
|
|
227
|
+
render: ({ props, variant, node, ctx }) => {
|
|
228
|
+
if (variant === "card") {
|
|
229
|
+
const inverted2 = onAccent(props.surface);
|
|
230
|
+
const card = props.surface === "accent" ? "bg-primary text-primary-foreground" : props.surface === "inverse" ? "bg-foreground text-background" : props.surface === "muted" ? "bg-muted" : "border border-border";
|
|
231
|
+
return /* @__PURE__ */ jsx(SectionBand, { node, ctx, anchor: props.anchor, width: "md", children: /* @__PURE__ */ jsxs("div", { className: `flex flex-col items-center gap-6 rounded-2xl px-6 py-12 text-center sm:px-12 ${card}`, children: [
|
|
232
|
+
/* @__PURE__ */ jsx(SectionTitle, { ctx, id: titleId(ctx), size: "lg", children: props.heading }),
|
|
233
|
+
props.body ? /* @__PURE__ */ jsx(BandLead, { tone: inverted2 ? "inherit" : "muted", children: props.body }) : null,
|
|
234
|
+
/* @__PURE__ */ jsx(Actions, { primary: props.primaryCta, secondary: props.secondaryCta, ctx, onAccent: inverted2, className: "sm:justify-center" })
|
|
235
|
+
] }) });
|
|
236
|
+
}
|
|
237
|
+
const inverted = onAccent(props.surface);
|
|
238
|
+
if (variant === "split") {
|
|
239
|
+
return /* @__PURE__ */ jsx(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-8 md:flex-row md:items-center md:justify-between", children: [
|
|
240
|
+
/* @__PURE__ */ jsxs("div", { className: "flex max-w-2xl flex-col gap-3", children: [
|
|
241
|
+
/* @__PURE__ */ jsx(SectionTitle, { ctx, id: titleId(ctx), size: "lg", children: props.heading }),
|
|
242
|
+
props.body ? /* @__PURE__ */ jsx(BandLead, { tone: inverted ? "inherit" : "muted", children: props.body }) : null
|
|
243
|
+
] }),
|
|
244
|
+
/* @__PURE__ */ jsx(Actions, { primary: props.primaryCta, secondary: props.secondaryCta, ctx, onAccent: inverted, className: "shrink-0" })
|
|
245
|
+
] }) });
|
|
246
|
+
}
|
|
247
|
+
return /* @__PURE__ */ jsx(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: /* @__PURE__ */ jsxs("div", { className: "mx-auto flex max-w-3xl flex-col items-center gap-6 text-center", children: [
|
|
248
|
+
/* @__PURE__ */ jsx(SectionTitle, { ctx, id: titleId(ctx), size: "lg", children: props.heading }),
|
|
249
|
+
props.body ? /* @__PURE__ */ jsx(BandLead, { tone: inverted ? "inherit" : "muted", children: props.body }) : null,
|
|
250
|
+
/* @__PURE__ */ jsx(Actions, { primary: props.primaryCta, secondary: props.secondaryCta, ctx, onAccent: inverted, className: "sm:justify-center" })
|
|
251
|
+
] }) });
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
var schema2 = z.object({
|
|
255
|
+
heading: Line(90),
|
|
256
|
+
body: Copy(320).optional(),
|
|
257
|
+
items: z.array(z.object({ question: Line(160), answer: Copy(1200).min(1) })).min(1).max(30),
|
|
258
|
+
surface: Surface.default("base"),
|
|
259
|
+
anchor: Anchor
|
|
260
|
+
});
|
|
261
|
+
function Answer({ text, muted }) {
|
|
262
|
+
return /* @__PURE__ */ jsx("div", { className: muted ? "flex flex-col gap-3 text-muted-foreground" : "flex flex-col gap-3", children: text.split(/\n{2,}/).map((p, i) => /* @__PURE__ */ jsx("p", { className: "whitespace-pre-line", children: p }, i)) });
|
|
263
|
+
}
|
|
264
|
+
var faqSection = defineSection({
|
|
265
|
+
type: "faq",
|
|
266
|
+
version: 1,
|
|
267
|
+
label: "FAQ",
|
|
268
|
+
description: "Questions and answers. Works without script: each answer opens in place.",
|
|
269
|
+
category: "content",
|
|
270
|
+
schema: schema2,
|
|
271
|
+
variants: [
|
|
272
|
+
{ id: "accordion", label: "Questions that open" },
|
|
273
|
+
{ id: "list", label: "All answers shown, two columns" }
|
|
274
|
+
],
|
|
275
|
+
defaultVariant: "accordion",
|
|
276
|
+
presets: [
|
|
277
|
+
{
|
|
278
|
+
name: "FAQ",
|
|
279
|
+
variant: "accordion",
|
|
280
|
+
props: { heading: "Questions", items: [{ question: "A question people ask?", answer: "A short, direct answer." }, { question: "Another question?", answer: "Another answer." }] }
|
|
281
|
+
}
|
|
282
|
+
],
|
|
283
|
+
render: ({ props, variant, node, ctx }) => {
|
|
284
|
+
const inverted = onAccent(props.surface);
|
|
285
|
+
const intro = /* @__PURE__ */ jsx(SectionIntro, { ctx, heading: props.heading, body: props.body, tone: inverted ? "inherit" : "muted" });
|
|
286
|
+
if (variant === "list") {
|
|
287
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: [
|
|
288
|
+
intro,
|
|
289
|
+
/* @__PURE__ */ jsx("div", { className: "mt-12 grid gap-x-12 gap-y-10 md:grid-cols-2", children: props.items.map((item, i) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
290
|
+
/* @__PURE__ */ jsx(ItemTitle, { ctx, children: item.question }),
|
|
291
|
+
/* @__PURE__ */ jsx(Answer, { text: item.answer, muted: !inverted })
|
|
292
|
+
] }, i)) })
|
|
293
|
+
] });
|
|
294
|
+
}
|
|
295
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, width: "md", children: [
|
|
296
|
+
intro,
|
|
297
|
+
/* @__PURE__ */ jsx("div", { className: "mx-auto mt-10 flex max-w-3xl flex-col divide-y divide-border border-y border-border", children: props.items.map((item, i) => /* @__PURE__ */ jsxs("details", { className: "group", children: [
|
|
298
|
+
/* @__PURE__ */ jsxs("summary", { className: "flex min-h-11 cursor-pointer list-none items-center justify-between gap-4 rounded-sm py-4 text-left font-display text-lg font-semibold focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring [&::-webkit-details-marker]:hidden", children: [
|
|
299
|
+
/* @__PURE__ */ jsx("span", { children: item.question }),
|
|
300
|
+
/* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": "true", className: "size-5 shrink-0 transition-transform group-open:rotate-180 motion-reduce:transition-none" })
|
|
301
|
+
] }),
|
|
302
|
+
/* @__PURE__ */ jsx("div", { className: "pb-5", children: /* @__PURE__ */ jsx(Answer, { text: item.answer, muted: !inverted }) })
|
|
303
|
+
] }, i)) })
|
|
304
|
+
] });
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
var Item = z.object({
|
|
308
|
+
title: Line(60),
|
|
309
|
+
body: Copy(320),
|
|
310
|
+
icon: IconName.optional(),
|
|
311
|
+
image: Image.optional(),
|
|
312
|
+
link: Link.optional(),
|
|
313
|
+
/** A button for this item: a step's own next action. */
|
|
314
|
+
cta: Link.optional(),
|
|
315
|
+
/** A short aside under the copy ("When: Saturdays at 8 am"). */
|
|
316
|
+
note: Copy(300).optional()
|
|
317
|
+
});
|
|
318
|
+
var schema3 = z.object({
|
|
319
|
+
eyebrow: Line(60).optional(),
|
|
320
|
+
heading: Line(90),
|
|
321
|
+
body: Copy(320).optional(),
|
|
322
|
+
items: z.array(Item).min(1).max(12),
|
|
323
|
+
columns: z.union([z.literal(2), z.literal(3), z.literal(4)]).default(3),
|
|
324
|
+
/** Number the items (01, 02, …) in place of their icons: an ordered list. The `steps` variant always is. */
|
|
325
|
+
numbered: z.boolean().default(false),
|
|
326
|
+
/** A button under the items. */
|
|
327
|
+
cta: Link.optional(),
|
|
328
|
+
surface: Surface.default("base"),
|
|
329
|
+
anchor: Anchor
|
|
330
|
+
});
|
|
331
|
+
var COLUMNS = { 2: "sm:grid-cols-2", 3: "sm:grid-cols-2 lg:grid-cols-3", 4: "sm:grid-cols-2 lg:grid-cols-4" };
|
|
332
|
+
var featuresSection = defineSection({
|
|
333
|
+
type: "features",
|
|
334
|
+
version: 1,
|
|
335
|
+
label: "Features",
|
|
336
|
+
description: "What you offer: a few items, each with a title, a line of copy and an icon or image.",
|
|
337
|
+
category: "content",
|
|
338
|
+
schema: schema3,
|
|
339
|
+
variants: [
|
|
340
|
+
{ id: "grid", label: "Cards in a grid" },
|
|
341
|
+
{ id: "list", label: "Icon list" },
|
|
342
|
+
{ id: "alternating", label: "Image and text, alternating" },
|
|
343
|
+
{ id: "steps", label: "Numbered steps, a button each" }
|
|
344
|
+
],
|
|
345
|
+
defaultVariant: "grid",
|
|
346
|
+
presets: [
|
|
347
|
+
{
|
|
348
|
+
name: "Three features",
|
|
349
|
+
variant: "grid",
|
|
350
|
+
props: {
|
|
351
|
+
heading: "What you get",
|
|
352
|
+
items: [
|
|
353
|
+
{ title: "First thing", body: "Why it matters.", icon: "check" },
|
|
354
|
+
{ title: "Second thing", body: "Why it matters.", icon: "star" },
|
|
355
|
+
{ title: "Third thing", body: "Why it matters.", icon: "heart" }
|
|
356
|
+
]
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
name: "Feature list",
|
|
361
|
+
variant: "list",
|
|
362
|
+
props: { heading: "Why people choose us", columns: 2, items: [{ title: "A reason", body: "In a sentence.", icon: "check" }, { title: "Another reason", body: "In a sentence.", icon: "check" }] }
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
name: "Numbered steps",
|
|
366
|
+
variant: "steps",
|
|
367
|
+
props: {
|
|
368
|
+
heading: "How to start",
|
|
369
|
+
items: [
|
|
370
|
+
{ title: "Come to a free class", body: "Meet the coaches and try a session.", cta: { label: "Book a free class", href: "#contact" } },
|
|
371
|
+
{ title: "Learn the basics", body: "A few sessions on the movements, with a coach." },
|
|
372
|
+
{ title: "Pick a plan", body: "Choose what fits your week.", cta: { label: "See the plans", href: "/plans" } }
|
|
373
|
+
]
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
],
|
|
377
|
+
render: ({ props, variant, node, ctx }) => {
|
|
378
|
+
const inverted = onAccent(props.surface);
|
|
379
|
+
const intro = /* @__PURE__ */ jsx(SectionIntro, { ctx, eyebrow: props.eyebrow, heading: props.heading, body: props.body, tone: inverted ? "inherit" : "muted" });
|
|
380
|
+
const numberBadge = (i) => /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn("inline-flex size-11 shrink-0 items-center justify-center rounded-lg font-display text-lg font-bold", inverted ? "bg-background text-foreground" : "bg-primary text-primary-foreground"), children: String(i + 1).padStart(2, "0") });
|
|
381
|
+
const extras = (item, className) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
382
|
+
item.note ? /* @__PURE__ */ jsx("p", { className: cn("border-l-2 border-current pl-3 text-sm italic", className), children: item.note }) : null,
|
|
383
|
+
item.link ? /* @__PURE__ */ jsx(TextLink, { link: item.link, ctx, className: "font-medium underline" }) : null,
|
|
384
|
+
item.cta ? /* @__PURE__ */ jsx(ButtonLink, { link: item.cta, ctx, tone: "outline", className: "self-start" }) : null
|
|
385
|
+
] });
|
|
386
|
+
const sectionCta = props.cta ? /* @__PURE__ */ jsx("div", { className: "mt-10 flex justify-center", children: /* @__PURE__ */ jsx(ButtonLink, { link: props.cta, ctx, tone: inverted ? "inverse" : "primary" }) }) : null;
|
|
387
|
+
const List = props.numbered ? "ol" : "ul";
|
|
388
|
+
const Icon = (name) => {
|
|
389
|
+
if (!name) return null;
|
|
390
|
+
const Glyph = ICONS[name];
|
|
391
|
+
return /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn("inline-flex size-11 shrink-0 items-center justify-center rounded-lg", inverted ? "bg-background text-foreground" : "bg-primary text-primary-foreground"), children: /* @__PURE__ */ jsx(Glyph, { className: "size-5" }) });
|
|
392
|
+
};
|
|
393
|
+
if (variant === "steps") {
|
|
394
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: [
|
|
395
|
+
intro,
|
|
396
|
+
/* @__PURE__ */ jsx("ol", { className: cn("mt-12 grid gap-6", props.items.length > 3 ? "md:grid-cols-2" : "", props.items.length === 3 && "lg:grid-cols-3"), children: props.items.map((item, i) => /* @__PURE__ */ jsxs("li", { className: "flex flex-col gap-4 rounded-xl border border-border bg-background p-6 text-foreground sm:p-8", children: [
|
|
397
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "font-display text-4xl font-bold leading-none", children: String(i + 1).padStart(2, "0") }),
|
|
398
|
+
item.image ? /* @__PURE__ */ jsx(SiteImage, { image: item.image, ctx, sizes: "(min-width: 1024px) 33vw, 100vw", className: "aspect-video w-full rounded-lg object-cover" }) : null,
|
|
399
|
+
/* @__PURE__ */ jsx(ItemTitle, { ctx, className: "text-2xl", children: item.title }),
|
|
400
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: item.body }),
|
|
401
|
+
item.note ? /* @__PURE__ */ jsx("p", { className: "border-l-2 border-primary pl-3 text-sm italic", children: item.note }) : null,
|
|
402
|
+
item.link ? /* @__PURE__ */ jsx(TextLink, { link: item.link, ctx, className: "font-medium underline" }) : null,
|
|
403
|
+
item.cta ? /* @__PURE__ */ jsx(ButtonLink, { link: item.cta, ctx, tone: "primary", className: "mt-auto self-start" }) : null
|
|
404
|
+
] }, i)) }),
|
|
405
|
+
sectionCta
|
|
406
|
+
] });
|
|
407
|
+
}
|
|
408
|
+
if (variant === "alternating") {
|
|
409
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: [
|
|
410
|
+
intro,
|
|
411
|
+
/* @__PURE__ */ jsx(List, { className: "mt-12 flex flex-col gap-12 md:gap-16", children: props.items.map((item, i) => /* @__PURE__ */ jsxs("li", { className: "grid items-center gap-8 md:grid-cols-2 md:gap-12", children: [
|
|
412
|
+
/* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-3", i % 2 === 1 && "md:order-2"), children: [
|
|
413
|
+
props.numbered ? numberBadge(i) : Icon(item.icon),
|
|
414
|
+
/* @__PURE__ */ jsx(ItemTitle, { ctx, className: "text-2xl", children: item.title }),
|
|
415
|
+
/* @__PURE__ */ jsx("p", { className: inverted ? void 0 : "text-muted-foreground", children: item.body }),
|
|
416
|
+
extras(item)
|
|
417
|
+
] }),
|
|
418
|
+
item.image ? /* @__PURE__ */ jsx(SiteImage, { image: item.image, ctx, sizes: "(min-width: 768px) 50vw, 100vw", className: "aspect-[4/3] w-full rounded-xl object-cover" }) : /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "hidden aspect-[4/3] w-full rounded-xl bg-muted md:block" })
|
|
419
|
+
] }, i)) }),
|
|
420
|
+
sectionCta
|
|
421
|
+
] });
|
|
422
|
+
}
|
|
423
|
+
if (variant === "list") {
|
|
424
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: [
|
|
425
|
+
intro,
|
|
426
|
+
/* @__PURE__ */ jsx(List, { className: cn("mt-12 grid gap-x-10 gap-y-8", props.columns >= 2 && "sm:grid-cols-2", props.columns >= 3 && "lg:grid-cols-3"), children: props.items.map((item, i) => /* @__PURE__ */ jsxs("li", { className: "flex gap-4", children: [
|
|
427
|
+
props.numbered ? numberBadge(i) : Icon(item.icon ?? "check"),
|
|
428
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
429
|
+
/* @__PURE__ */ jsx(ItemTitle, { ctx, children: item.title }),
|
|
430
|
+
/* @__PURE__ */ jsx("p", { className: inverted ? void 0 : "text-muted-foreground", children: item.body }),
|
|
431
|
+
extras(item, "mt-1")
|
|
432
|
+
] })
|
|
433
|
+
] }, i)) }),
|
|
434
|
+
sectionCta
|
|
435
|
+
] });
|
|
436
|
+
}
|
|
437
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: [
|
|
438
|
+
intro,
|
|
439
|
+
/* @__PURE__ */ jsx(List, { className: cn("mt-12 grid gap-6", COLUMNS[props.columns]), children: props.items.map((item, i) => /* @__PURE__ */ jsxs("li", { className: "flex flex-col gap-3 rounded-xl border border-border bg-background p-6 text-foreground", children: [
|
|
440
|
+
item.image ? /* @__PURE__ */ jsx(SiteImage, { image: item.image, ctx, sizes: "(min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw", className: "aspect-video w-full rounded-lg object-cover" }) : null,
|
|
441
|
+
props.numbered ? numberBadge(i) : item.image ? null : Icon(item.icon),
|
|
442
|
+
/* @__PURE__ */ jsx(ItemTitle, { ctx, children: item.title }),
|
|
443
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: item.body }),
|
|
444
|
+
item.note ? /* @__PURE__ */ jsx("p", { className: "border-l-2 border-primary pl-3 text-sm italic", children: item.note }) : null,
|
|
445
|
+
item.link ? /* @__PURE__ */ jsx(TextLink, { link: item.link, ctx, className: "mt-auto font-medium underline" }) : null,
|
|
446
|
+
item.cta ? /* @__PURE__ */ jsx(ButtonLink, { link: item.cta, ctx, tone: "outline", className: cn("self-start", !item.link && "mt-auto") }) : null
|
|
447
|
+
] }, i)) }),
|
|
448
|
+
sectionCta
|
|
449
|
+
] });
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
var schema4 = z.object({
|
|
453
|
+
tagline: Copy(200).optional(),
|
|
454
|
+
columns: z.array(z.object({ heading: Line(40), links: z.array(Link).min(1).max(8) })).max(4).default([]),
|
|
455
|
+
links: z.array(Link).max(12).default([]),
|
|
456
|
+
copyright: Line(120).optional(),
|
|
457
|
+
surface: Surface.default("inverse")
|
|
458
|
+
});
|
|
459
|
+
var footerSection = defineSection({
|
|
460
|
+
type: "footer",
|
|
461
|
+
version: 1,
|
|
462
|
+
label: "Footer",
|
|
463
|
+
description: "The site's footer: links, a line about you and the copyright.",
|
|
464
|
+
category: "footer",
|
|
465
|
+
placement: "footer",
|
|
466
|
+
limit: 1,
|
|
467
|
+
schema: schema4,
|
|
468
|
+
variants: [
|
|
469
|
+
{ id: "simple", label: "Links in a row" },
|
|
470
|
+
{ id: "columns", label: "Link columns" },
|
|
471
|
+
{ id: "minimal", label: "One line" }
|
|
472
|
+
],
|
|
473
|
+
defaultVariant: "simple",
|
|
474
|
+
presets: [
|
|
475
|
+
{ name: "Footer", variant: "simple", props: { links: [{ label: "Contact", href: "/contact" }], copyright: "\xA9 Your business" } },
|
|
476
|
+
{ name: "Footer with columns", variant: "columns", props: { columns: [{ heading: "Visit", links: [{ label: "Contact", href: "/contact" }] }], copyright: "\xA9 Your business" } }
|
|
477
|
+
],
|
|
478
|
+
render: ({ props, variant, node, ctx }) => {
|
|
479
|
+
const attrs = bandAttrs(node, ctx, void 0, false);
|
|
480
|
+
const quiet = props.surface === "base" || props.surface === "muted" ? "text-muted-foreground" : void 0;
|
|
481
|
+
const name = /* @__PURE__ */ jsx("p", { className: "font-display text-lg font-bold", children: ctx.site.name });
|
|
482
|
+
const copyright = props.copyright ? /* @__PURE__ */ jsx("p", { className: cn("text-sm", quiet), children: props.copyright }) : null;
|
|
483
|
+
const row = (links) => links.length ? /* @__PURE__ */ jsx("nav", { "aria-label": "Footer", children: /* @__PURE__ */ jsx("ul", { className: "flex flex-wrap gap-x-6 gap-y-2", children: links.map((link, i) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(TextLink, { link, ctx, className: "text-sm underline" }) }, i)) }) }) : null;
|
|
484
|
+
if (variant === "minimal") {
|
|
485
|
+
return /* @__PURE__ */ jsx(Band, { ...attrs, as: "footer", surface: props.surface, spacing: "sm", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", children: [
|
|
486
|
+
copyright ?? name,
|
|
487
|
+
row(props.links)
|
|
488
|
+
] }) });
|
|
489
|
+
}
|
|
490
|
+
if (variant === "columns") {
|
|
491
|
+
return /* @__PURE__ */ jsxs(Band, { ...attrs, as: "footer", surface: props.surface, spacing: "md", children: [
|
|
492
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-10 md:grid-cols-[2fr_repeat(3,1fr)]", children: [
|
|
493
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
494
|
+
name,
|
|
495
|
+
props.tagline ? /* @__PURE__ */ jsx("p", { className: cn("max-w-sm", quiet), children: props.tagline }) : null
|
|
496
|
+
] }),
|
|
497
|
+
props.columns.map((col, i) => /* @__PURE__ */ jsxs("nav", { "aria-label": col.heading, className: "flex flex-col gap-3", children: [
|
|
498
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold uppercase tracking-wider", children: col.heading }),
|
|
499
|
+
/* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-1", children: col.links.map((link, j) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(TextLink, { link, ctx, className: "text-sm underline" }) }, j)) })
|
|
500
|
+
] }, i))
|
|
501
|
+
] }),
|
|
502
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-10 flex flex-col gap-4 border-t border-current/20 pt-6 sm:flex-row sm:items-center sm:justify-between", children: [
|
|
503
|
+
copyright,
|
|
504
|
+
row(props.links)
|
|
505
|
+
] })
|
|
506
|
+
] });
|
|
507
|
+
}
|
|
508
|
+
return /* @__PURE__ */ jsxs(Band, { ...attrs, as: "footer", surface: props.surface, spacing: "sm", children: [
|
|
509
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6 md:flex-row md:items-start md:justify-between", children: [
|
|
510
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
511
|
+
name,
|
|
512
|
+
props.tagline ? /* @__PURE__ */ jsx("p", { className: cn("max-w-md", quiet), children: props.tagline }) : null
|
|
513
|
+
] }),
|
|
514
|
+
row(props.links)
|
|
515
|
+
] }),
|
|
516
|
+
copyright ? /* @__PURE__ */ jsx("div", { className: "mt-6", children: copyright }) : null
|
|
517
|
+
] });
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
function SiteFormHeader({ title, description, requiredNote }) {
|
|
521
|
+
return /* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-1", children: [
|
|
522
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: title }),
|
|
523
|
+
description ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: description }) : null,
|
|
524
|
+
requiredNote ? /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
|
|
525
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "* " }),
|
|
526
|
+
requiredNote
|
|
527
|
+
] }) : null
|
|
528
|
+
] });
|
|
529
|
+
}
|
|
530
|
+
var siteFormSlots = { header: SiteFormHeader };
|
|
531
|
+
var KINDS = {
|
|
532
|
+
"short-text": "text",
|
|
533
|
+
"long-text": "text",
|
|
534
|
+
email: "text",
|
|
535
|
+
phone: "text",
|
|
536
|
+
date: "text",
|
|
537
|
+
number: "number",
|
|
538
|
+
"single-choice": "single",
|
|
539
|
+
dropdown: "single",
|
|
540
|
+
"multi-choice": "multi",
|
|
541
|
+
"yes-no": "yes-no",
|
|
542
|
+
consent: "consent"
|
|
543
|
+
};
|
|
544
|
+
var hasRecall = (text) => typeof text === "string" && text.includes("{{");
|
|
545
|
+
function lightField(block) {
|
|
546
|
+
if (block.kind === "page-break") return false;
|
|
547
|
+
if (block.kind === "content") return (block.type === "heading" || block.type === "text") && !hasRecall(block.text);
|
|
548
|
+
if (!(block.type in KINDS)) return false;
|
|
549
|
+
if (hasRecall(block.label) || hasRecall(block.description) || hasRecall(block.placeholder)) return false;
|
|
550
|
+
const props = block.props ?? {};
|
|
551
|
+
if (props.optionsFrom !== void 0 || props.other !== void 0) return false;
|
|
552
|
+
return true;
|
|
553
|
+
}
|
|
554
|
+
function isLightForm(form) {
|
|
555
|
+
if (form.layout?.mode === "focus") return false;
|
|
556
|
+
if (form.logic?.length || form.hidden?.length) return false;
|
|
557
|
+
if (form.settings?.anonymous || form.settings?.saveProgress) return false;
|
|
558
|
+
return form.blocks.length > 0 && form.blocks.every(lightField);
|
|
559
|
+
}
|
|
560
|
+
var control = "text-base";
|
|
561
|
+
var choiceInput = "size-5 shrink-0 accent-[hsl(var(--primary))]";
|
|
562
|
+
var choiceRow = "flex min-h-11 items-center gap-3 rounded-md px-1";
|
|
563
|
+
function str(value) {
|
|
564
|
+
return typeof value === "string" && value ? value : typeof value === "number" ? String(value) : void 0;
|
|
565
|
+
}
|
|
566
|
+
function FieldControl({ field, id, describedBy }) {
|
|
567
|
+
const type = field.type;
|
|
568
|
+
const props = field.props ?? {};
|
|
569
|
+
const common = {
|
|
570
|
+
id,
|
|
571
|
+
name: field.id,
|
|
572
|
+
required: field.required === true,
|
|
573
|
+
...field.placeholder ? { placeholder: field.placeholder } : {},
|
|
574
|
+
...describedBy ? { "aria-describedby": describedBy } : {}
|
|
575
|
+
};
|
|
576
|
+
switch (type) {
|
|
577
|
+
case "short-text":
|
|
578
|
+
return /* @__PURE__ */ jsx(Input, { ...common, type: "text", maxLength: 500, className: control });
|
|
579
|
+
case "long-text":
|
|
580
|
+
return /* @__PURE__ */ jsx(Textarea, { ...common, maxLength: 5e3, rows: 4, className: control });
|
|
581
|
+
case "email":
|
|
582
|
+
return /* @__PURE__ */ jsx(Input, { ...common, type: "email", autoComplete: "email", inputMode: "email", maxLength: 320, className: control });
|
|
583
|
+
case "phone":
|
|
584
|
+
return /* @__PURE__ */ jsx(Input, { ...common, type: "tel", autoComplete: "tel", inputMode: "tel", maxLength: 40, className: control });
|
|
585
|
+
case "date":
|
|
586
|
+
return /* @__PURE__ */ jsx(Input, { ...common, type: "date", ...str(props.min) ? { min: str(props.min) } : {}, ...str(props.max) ? { max: str(props.max) } : {}, className: control });
|
|
587
|
+
case "number":
|
|
588
|
+
return /* @__PURE__ */ jsx(
|
|
589
|
+
Input,
|
|
590
|
+
{
|
|
591
|
+
...common,
|
|
592
|
+
type: "number",
|
|
593
|
+
inputMode: "decimal",
|
|
594
|
+
...str(props.min) ? { min: str(props.min) } : {},
|
|
595
|
+
...str(props.max) ? { max: str(props.max) } : {},
|
|
596
|
+
step: str(props.step) ?? "any",
|
|
597
|
+
className: control
|
|
598
|
+
}
|
|
599
|
+
);
|
|
600
|
+
case "dropdown":
|
|
601
|
+
return /* @__PURE__ */ jsxs(
|
|
602
|
+
"select",
|
|
603
|
+
{
|
|
604
|
+
...common,
|
|
605
|
+
defaultValue: "",
|
|
606
|
+
className: "flex h-11 w-full rounded-md border border-input bg-background px-3 text-base focus-visible:outline-2 focus-visible:outline-ring",
|
|
607
|
+
children: [
|
|
608
|
+
/* @__PURE__ */ jsx("option", { value: "", disabled: field.required === true, children: field.placeholder || "Choose one" }),
|
|
609
|
+
choiceOptions(field).map((o) => /* @__PURE__ */ jsx("option", { value: o.value, children: o.label }, o.value))
|
|
610
|
+
]
|
|
611
|
+
}
|
|
612
|
+
);
|
|
613
|
+
default:
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
function ChoiceGroup({ field, id, describedBy }) {
|
|
618
|
+
const type = field.type;
|
|
619
|
+
const required = field.required === true;
|
|
620
|
+
const options = type === "yes-no" ? [
|
|
621
|
+
{ value: "yes", label: "Yes" },
|
|
622
|
+
{ value: "no", label: "No" }
|
|
623
|
+
] : choiceOptions(field).map((o) => ({ value: o.value, label: o.label }));
|
|
624
|
+
const multi = type === "multi-choice";
|
|
625
|
+
return /* @__PURE__ */ jsxs("fieldset", { className: "flex flex-col gap-1", ...describedBy ? { "aria-describedby": describedBy } : {}, "data-gs-field": field.id, "data-gs-kind": KINDS[type], ...required ? { "data-gs-required": "" } : {}, children: [
|
|
626
|
+
/* @__PURE__ */ jsxs("legend", { className: "mb-1 font-medium", children: [
|
|
627
|
+
field.label,
|
|
628
|
+
required ? /* @__PURE__ */ jsx(RequiredMark, {}) : null
|
|
629
|
+
] }),
|
|
630
|
+
field.description ? /* @__PURE__ */ jsx("p", { id: `${id}-desc`, className: "mb-1 text-sm text-muted-foreground", children: field.description }) : null,
|
|
631
|
+
options.map((o, i) => /* @__PURE__ */ jsxs("label", { className: choiceRow, children: [
|
|
632
|
+
/* @__PURE__ */ jsx(
|
|
633
|
+
"input",
|
|
634
|
+
{
|
|
635
|
+
type: multi ? "checkbox" : "radio",
|
|
636
|
+
name: field.id,
|
|
637
|
+
value: o.value,
|
|
638
|
+
className: choiceInput,
|
|
639
|
+
...required && !multi && i === 0 ? { required: true } : {}
|
|
640
|
+
}
|
|
641
|
+
),
|
|
642
|
+
/* @__PURE__ */ jsx("span", { children: o.label })
|
|
643
|
+
] }, o.value))
|
|
644
|
+
] });
|
|
645
|
+
}
|
|
646
|
+
function RequiredMark() {
|
|
647
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
648
|
+
/* @__PURE__ */ jsxs("span", { "aria-hidden": "true", className: "text-muted-foreground", children: [
|
|
649
|
+
" ",
|
|
650
|
+
"*"
|
|
651
|
+
] }),
|
|
652
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: " (required)" })
|
|
653
|
+
] });
|
|
654
|
+
}
|
|
655
|
+
function FieldRow({ field, idPrefix }) {
|
|
656
|
+
const id = `${idPrefix}-${field.id}`;
|
|
657
|
+
const type = field.type;
|
|
658
|
+
const describedBy = field.description ? `${id}-desc` : void 0;
|
|
659
|
+
if (type === "single-choice" || type === "multi-choice" || type === "yes-no") return /* @__PURE__ */ jsx(ChoiceGroup, { field, id, ...describedBy ? { describedBy } : {} });
|
|
660
|
+
if (type === "consent") {
|
|
661
|
+
const text = str(field.props?.text) ?? field.label;
|
|
662
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", "data-gs-field": field.id, "data-gs-kind": "consent", children: [
|
|
663
|
+
/* @__PURE__ */ jsxs("label", { className: cn(choiceRow, "items-start py-1"), children: [
|
|
664
|
+
/* @__PURE__ */ jsx("input", { id, type: "checkbox", name: field.id, value: "true", required: field.required === true, className: cn(choiceInput, "mt-0.5"), ...describedBy ? { "aria-describedby": describedBy } : {} }),
|
|
665
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
666
|
+
text,
|
|
667
|
+
field.required ? /* @__PURE__ */ jsx(RequiredMark, {}) : null
|
|
668
|
+
] })
|
|
669
|
+
] }),
|
|
670
|
+
field.description ? /* @__PURE__ */ jsx("p", { id: `${id}-desc`, className: "text-sm text-muted-foreground", children: field.description }) : null
|
|
671
|
+
] });
|
|
672
|
+
}
|
|
673
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", "data-gs-field": field.id, "data-gs-kind": KINDS[type], children: [
|
|
674
|
+
/* @__PURE__ */ jsxs("label", { htmlFor: id, className: "font-medium", children: [
|
|
675
|
+
field.label,
|
|
676
|
+
field.required ? /* @__PURE__ */ jsx(RequiredMark, {}) : null
|
|
677
|
+
] }),
|
|
678
|
+
field.description ? /* @__PURE__ */ jsx("p", { id: `${id}-desc`, className: "-mt-1 text-sm text-muted-foreground", children: field.description }) : null,
|
|
679
|
+
/* @__PURE__ */ jsx(FieldControl, { field, id, ...describedBy ? { describedBy } : {} })
|
|
680
|
+
] });
|
|
681
|
+
}
|
|
682
|
+
function LightForm({ form, slug, submitUrl, submitLabel, idPrefix }) {
|
|
683
|
+
const ending = form.endings?.[0];
|
|
684
|
+
const statusId = `${idPrefix}--gs-status`;
|
|
685
|
+
return /* @__PURE__ */ jsxs(
|
|
686
|
+
"form",
|
|
687
|
+
{
|
|
688
|
+
method: "post",
|
|
689
|
+
action: submitUrl,
|
|
690
|
+
"data-gs-island": "light-form",
|
|
691
|
+
"data-gs-form": slug,
|
|
692
|
+
"data-gs-sent": ending ? `${ending.title}${ending.description ? ` ${ending.description}` : ""}` : "Thanks. We got it.",
|
|
693
|
+
className: "flex flex-col gap-5 text-left",
|
|
694
|
+
children: [
|
|
695
|
+
/* @__PURE__ */ jsx("input", { type: "hidden", name: "form", value: slug }),
|
|
696
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
697
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: form.title }),
|
|
698
|
+
form.description ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: form.description }) : null,
|
|
699
|
+
form.blocks.some((b) => b.kind === "field" && b.required) ? /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
|
|
700
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "* " }),
|
|
701
|
+
"Required"
|
|
702
|
+
] }) : null
|
|
703
|
+
] }),
|
|
704
|
+
form.blocks.map((block) => {
|
|
705
|
+
if (block.kind === "content") {
|
|
706
|
+
return block.type === "heading" ? /* @__PURE__ */ jsx("h4", { className: "font-semibold", children: block.text }, block.id) : /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: block.text }, block.id);
|
|
707
|
+
}
|
|
708
|
+
if (block.kind === "field") return /* @__PURE__ */ jsx(FieldRow, { field: block, idPrefix }, block.id);
|
|
709
|
+
return null;
|
|
710
|
+
}),
|
|
711
|
+
/* @__PURE__ */ jsx("p", { id: statusId, role: "status", "aria-live": "polite", "data-gs-status": "", className: "min-h-6 text-sm font-medium" }),
|
|
712
|
+
/* @__PURE__ */ jsx("button", { type: "submit", className: cn(buttonVariants({ size: "lg" }), "min-h-11 self-start px-8"), children: submitLabel || "Send" })
|
|
713
|
+
]
|
|
714
|
+
}
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
var schema5 = z.object({
|
|
718
|
+
heading: Line(90),
|
|
719
|
+
body: Copy(400).optional(),
|
|
720
|
+
/** The form's slug, which the host's forms resolver knows. */
|
|
721
|
+
form: z.string().regex(/^[a-z0-9][a-z0-9-]{0,79}$/, "A form slug is lowercase letters, digits and hyphens."),
|
|
722
|
+
surface: Surface.default("base"),
|
|
723
|
+
anchor: Anchor.default("contact")
|
|
724
|
+
});
|
|
725
|
+
function formDrawMode(resolved) {
|
|
726
|
+
if (!resolved) return null;
|
|
727
|
+
if (resolved.kind === "embed") return "embed";
|
|
728
|
+
if (resolved.kind === "mailto") return mailtoHref(resolved) ? "mailto" : null;
|
|
729
|
+
const parsed = parseForm(resolved.definition);
|
|
730
|
+
if (!parsed.ok) return null;
|
|
731
|
+
return isLightForm(parsed.form) ? "light" : "island";
|
|
732
|
+
}
|
|
733
|
+
function EmbeddedForm({ slug, heading, ctx }) {
|
|
734
|
+
const resolved = ctx.forms.get(slug);
|
|
735
|
+
if (!resolved) {
|
|
736
|
+
console.error(`[genesis-sites] the form "${slug}" on ${ctx.pagePath} is unknown to the host's forms resolver; the form embed renders nothing.`);
|
|
737
|
+
return ctx.mode === "preview" ? /* @__PURE__ */ jsxs("p", { role: "status", className: "rounded-lg border border-dashed border-border p-6 text-center", children: [
|
|
738
|
+
"The form \u201C",
|
|
739
|
+
slug,
|
|
740
|
+
"\u201D isn't available. Check its slug, or that the app passes a forms resolver."
|
|
741
|
+
] }) : null;
|
|
742
|
+
}
|
|
743
|
+
if (resolved.kind === "mailto") {
|
|
744
|
+
const href = mailtoHref(resolved);
|
|
745
|
+
if (!href) {
|
|
746
|
+
console.error(`[genesis-sites] the mailto form "${slug}" has no plain email address; it renders nothing.`);
|
|
747
|
+
return null;
|
|
748
|
+
}
|
|
749
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-start gap-4 rounded-xl border border-border bg-background p-6 text-foreground", children: [
|
|
750
|
+
/* @__PURE__ */ jsx("p", { children: resolved.note ?? "Write to us and we'll reply by email." }),
|
|
751
|
+
/* @__PURE__ */ jsx(ButtonLink, { link: { label: resolved.label ?? "Email us", href, event: "form-mailto" }, ctx })
|
|
752
|
+
] });
|
|
753
|
+
}
|
|
754
|
+
if (resolved.kind === "embed") {
|
|
755
|
+
if (!isSafeHref(resolved.url) || !resolved.url.startsWith("https://")) {
|
|
756
|
+
console.error(`[genesis-sites] refused the form embed URL for "${slug}": it must be https.`);
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
return /* @__PURE__ */ jsx(
|
|
760
|
+
"iframe",
|
|
761
|
+
{
|
|
762
|
+
src: resolved.url,
|
|
763
|
+
title: resolved.title || heading,
|
|
764
|
+
loading: "lazy",
|
|
765
|
+
referrerPolicy: "strict-origin-when-cross-origin",
|
|
766
|
+
sandbox: "allow-forms allow-scripts allow-same-origin allow-popups",
|
|
767
|
+
style: { height: `${Math.min(Math.max(resolved.height ?? 640, 200), 4e3)}px` },
|
|
768
|
+
className: "w-full rounded-xl border border-border bg-background"
|
|
769
|
+
}
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
const parsed = parseForm(resolved.definition);
|
|
773
|
+
if (!parsed.ok) {
|
|
774
|
+
console.error(`[genesis-sites] the form "${slug}" isn't a valid genesis-forms definition: ${parsed.issues.map((i) => `${i.path} ${i.message}`).join("; ")}`);
|
|
775
|
+
return null;
|
|
776
|
+
}
|
|
777
|
+
if (!isSafeHref(resolved.submitUrl) || !(resolved.submitUrl.startsWith("/") || resolved.submitUrl.startsWith("https://"))) {
|
|
778
|
+
console.error(`[genesis-sites] the form "${slug}" has no usable submitUrl (a site path or an https URL).`);
|
|
779
|
+
return null;
|
|
780
|
+
}
|
|
781
|
+
const submitUrl = resolved.submitUrl.startsWith("/") ? ctx.href(resolved.submitUrl) : resolved.submitUrl;
|
|
782
|
+
if (isLightForm(parsed.form)) {
|
|
783
|
+
return /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-border bg-background p-4 text-foreground sm:p-6", children: /* @__PURE__ */ jsx(LightForm, { form: parsed.form, slug, submitUrl, idPrefix: ctx.idPrefix, ...resolved.submitLabel ? { submitLabel: resolved.submitLabel } : {} }) });
|
|
784
|
+
}
|
|
785
|
+
return (
|
|
786
|
+
// The island: the islands script loads the form bundle only on pages that have one,
|
|
787
|
+
// reads the definition from the JSON beside it and makes the server-rendered form live.
|
|
788
|
+
/* @__PURE__ */ jsxs(
|
|
789
|
+
"div",
|
|
790
|
+
{
|
|
791
|
+
"data-gs-island": "form",
|
|
792
|
+
"data-gs-form": slug,
|
|
793
|
+
"data-gs-submit": submitUrl,
|
|
794
|
+
...resolved.layout ? { "data-gs-layout": resolved.layout } : {},
|
|
795
|
+
...resolved.submitLabel ? { "data-gs-submit-label": resolved.submitLabel } : {},
|
|
796
|
+
className: "rounded-xl border border-border bg-background p-4 text-foreground sm:p-6",
|
|
797
|
+
children: [
|
|
798
|
+
/* @__PURE__ */ jsx("div", { "data-gs-form-root": "", children: /* @__PURE__ */ jsx(Form, { form: parsed.form, layout: resolved.layout ?? "classic", presentation: "embedded", slots: siteFormSlots, ...resolved.submitLabel ? { submitLabel: resolved.submitLabel } : {} }) }),
|
|
799
|
+
/* @__PURE__ */ jsx("script", { type: "application/json", "data-gs-form-definition": "", dangerouslySetInnerHTML: { __html: safeJsonForHtml(parsed.form) } }),
|
|
800
|
+
/* @__PURE__ */ jsx("noscript", { children: /* @__PURE__ */ jsx("p", { className: "mt-4 text-sm", children: "This form needs JavaScript. Turn it on to send it." }) })
|
|
801
|
+
]
|
|
802
|
+
}
|
|
803
|
+
)
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
var formEmbedSection = defineSection({
|
|
807
|
+
type: "form-embed",
|
|
808
|
+
version: 1,
|
|
809
|
+
label: "Form",
|
|
810
|
+
description: "A genesis-forms form (by slug, through the app's forms resolver) or a hosted form in a frame.",
|
|
811
|
+
category: "conversion",
|
|
812
|
+
schema: schema5,
|
|
813
|
+
variants: [
|
|
814
|
+
{ id: "card", label: "Form in a card" },
|
|
815
|
+
{ id: "split", label: "Copy left, form right" }
|
|
816
|
+
],
|
|
817
|
+
defaultVariant: "card",
|
|
818
|
+
presets: [
|
|
819
|
+
{ name: "Contact form", variant: "split", props: { heading: "Get in touch", body: "Tell us a little about yourself.", form: "contact", anchor: "contact" } },
|
|
820
|
+
{ name: "Form card", variant: "card", props: { heading: "Book a free session", form: "contact", anchor: "contact" } }
|
|
821
|
+
],
|
|
822
|
+
render: ({ props, variant, node, ctx }) => {
|
|
823
|
+
const inverted = onAccent(props.surface);
|
|
824
|
+
const copy = /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-4", variant === "card" && "items-center text-center"), children: [
|
|
825
|
+
/* @__PURE__ */ jsx(SectionTitle, { ctx, id: titleId(ctx), size: "lg", children: props.heading }),
|
|
826
|
+
props.body ? /* @__PURE__ */ jsx(BandLead, { tone: inverted ? "inherit" : "muted", children: props.body }) : null
|
|
827
|
+
] });
|
|
828
|
+
if (variant === "split") {
|
|
829
|
+
return /* @__PURE__ */ jsx(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: /* @__PURE__ */ jsxs("div", { className: "grid gap-10 md:grid-cols-2 md:gap-12", children: [
|
|
830
|
+
copy,
|
|
831
|
+
/* @__PURE__ */ jsx(EmbeddedForm, { slug: props.form, heading: props.heading, ctx })
|
|
832
|
+
] }) });
|
|
833
|
+
}
|
|
834
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, width: "sm", children: [
|
|
835
|
+
copy,
|
|
836
|
+
/* @__PURE__ */ jsx("div", { className: "mt-8", children: /* @__PURE__ */ jsx(EmbeddedForm, { slug: props.form, heading: props.heading, ctx }) })
|
|
837
|
+
] });
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
var schema6 = z.object({
|
|
841
|
+
heading: Line(90).optional(),
|
|
842
|
+
body: Copy(320).optional(),
|
|
843
|
+
images: z.array(Image.extend({ caption: Line(140).optional() })).min(1).max(24),
|
|
844
|
+
surface: Surface.default("base"),
|
|
845
|
+
anchor: Anchor
|
|
846
|
+
});
|
|
847
|
+
var gallerySection = defineSection({
|
|
848
|
+
type: "gallery",
|
|
849
|
+
version: 1,
|
|
850
|
+
label: "Gallery",
|
|
851
|
+
description: "Photos in an even grid or a masonry layout, each with alt text.",
|
|
852
|
+
category: "content",
|
|
853
|
+
schema: schema6,
|
|
854
|
+
variants: [
|
|
855
|
+
{ id: "grid", label: "Even grid" },
|
|
856
|
+
{ id: "masonry", label: "Masonry" }
|
|
857
|
+
],
|
|
858
|
+
defaultVariant: "grid",
|
|
859
|
+
presets: [
|
|
860
|
+
{
|
|
861
|
+
name: "Photo grid",
|
|
862
|
+
variant: "grid",
|
|
863
|
+
props: { heading: "Inside", images: [{ src: "/photo-1.jpg", alt: "Describe the photo" }, { src: "/photo-2.jpg", alt: "Describe the photo" }, { src: "/photo-3.jpg", alt: "Describe the photo" }] }
|
|
864
|
+
}
|
|
865
|
+
],
|
|
866
|
+
render: ({ props, variant, node, ctx }) => {
|
|
867
|
+
const intro = props.heading ? /* @__PURE__ */ jsx(SectionIntro, { ctx, heading: props.heading, body: props.body, tone: onAccent(props.surface) ? "inherit" : "muted" }) : /* @__PURE__ */ jsx(HiddenTitle, { ctx, children: "Gallery" });
|
|
868
|
+
const masonry = variant === "masonry";
|
|
869
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: [
|
|
870
|
+
intro,
|
|
871
|
+
/* @__PURE__ */ jsx("ul", { className: masonry ? "mt-10 columns-1 gap-4 sm:columns-2 lg:columns-3 [&>li]:mb-4 [&>li]:break-inside-avoid" : "mt-10 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: props.images.map((image, i) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("figure", { className: "flex flex-col gap-2", children: [
|
|
872
|
+
/* @__PURE__ */ jsx(
|
|
873
|
+
SiteImage,
|
|
874
|
+
{
|
|
875
|
+
image,
|
|
876
|
+
ctx,
|
|
877
|
+
sizes: "(min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw",
|
|
878
|
+
className: masonry ? "w-full rounded-lg" : "aspect-square w-full rounded-lg object-cover"
|
|
879
|
+
}
|
|
880
|
+
),
|
|
881
|
+
image.caption ? /* @__PURE__ */ jsx("figcaption", { className: "text-sm", children: image.caption }) : null
|
|
882
|
+
] }) }, i)) })
|
|
883
|
+
] });
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
var schema7 = z.object({
|
|
887
|
+
eyebrow: Line(60).optional(),
|
|
888
|
+
heading: Line(90),
|
|
889
|
+
body: Copy(320).optional(),
|
|
890
|
+
primaryCta: Link.optional(),
|
|
891
|
+
secondaryCta: Link.optional(),
|
|
892
|
+
image: Image.optional(),
|
|
893
|
+
anchor: Anchor
|
|
894
|
+
});
|
|
895
|
+
var heroSection = defineSection({
|
|
896
|
+
type: "hero",
|
|
897
|
+
version: 1,
|
|
898
|
+
label: "Hero",
|
|
899
|
+
description: "The page's opening: its main heading, a line of copy and up to two buttons.",
|
|
900
|
+
category: "intro",
|
|
901
|
+
limit: 1,
|
|
902
|
+
providesH1: true,
|
|
903
|
+
schema: schema7,
|
|
904
|
+
variants: [
|
|
905
|
+
{ id: "centered", label: "Centred" },
|
|
906
|
+
{ id: "split", label: "Text and image side by side" },
|
|
907
|
+
{ id: "image-bg", label: "Text over an image" }
|
|
908
|
+
],
|
|
909
|
+
defaultVariant: "centered",
|
|
910
|
+
presets: [
|
|
911
|
+
{
|
|
912
|
+
name: "Centred hero",
|
|
913
|
+
variant: "centered",
|
|
914
|
+
props: { heading: "Say what you do, for whom", body: "One or two sentences on why it matters.", primaryCta: { label: "Get started", href: "#contact" } }
|
|
915
|
+
},
|
|
916
|
+
{
|
|
917
|
+
name: "Hero with image",
|
|
918
|
+
variant: "split",
|
|
919
|
+
props: { heading: "Say what you do, for whom", body: "One or two sentences on why it matters.", primaryCta: { label: "Get started", href: "#contact" } }
|
|
920
|
+
}
|
|
921
|
+
],
|
|
922
|
+
render: ({ props, variant, node, ctx }) => {
|
|
923
|
+
const intro = (align, tone = "muted") => /* @__PURE__ */ jsxs("div", { className: align === "center" ? "mx-auto flex max-w-3xl flex-col items-center gap-6 text-center" : "flex flex-col items-start gap-6", children: [
|
|
924
|
+
props.eyebrow ? /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold uppercase tracking-wider", children: props.eyebrow }) : null,
|
|
925
|
+
/* @__PURE__ */ jsx(SectionTitle, { ctx, id: titleId(ctx), size: "xl", children: props.heading }),
|
|
926
|
+
props.body ? /* @__PURE__ */ jsx(BandLead, { tone, children: props.body }) : null,
|
|
927
|
+
/* @__PURE__ */ jsx(Actions, { primary: props.primaryCta, secondary: props.secondaryCta, ctx, onAccent: tone === "inherit", className: align === "center" ? "sm:justify-center" : void 0 })
|
|
928
|
+
] });
|
|
929
|
+
if (variant === "image-bg" && props.image) {
|
|
930
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: "inverse", spacing: "lg", className: "isolate overflow-hidden", children: [
|
|
931
|
+
/* @__PURE__ */ jsx(SiteImage, { image: { ...props.image, decorative: true }, ctx, priority: true, sizes: "100vw", className: "absolute inset-0 -z-10 h-full w-full object-cover" }),
|
|
932
|
+
/* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "absolute inset-0 -z-10 bg-foreground/80" }),
|
|
933
|
+
intro("center", "inherit")
|
|
934
|
+
] });
|
|
935
|
+
}
|
|
936
|
+
if (variant === "split") {
|
|
937
|
+
return /* @__PURE__ */ jsx(SectionBand, { node, ctx, anchor: props.anchor, spacing: "lg", children: /* @__PURE__ */ jsxs("div", { className: "grid items-center gap-10 md:grid-cols-2 md:gap-12", children: [
|
|
938
|
+
intro("left"),
|
|
939
|
+
props.image ? /* @__PURE__ */ jsx(SiteImage, { image: props.image, ctx, priority: true, sizes: "(min-width: 768px) 50vw, 100vw", className: "aspect-[4/3] w-full rounded-xl object-cover" }) : /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "hidden aspect-[4/3] w-full rounded-xl bg-muted md:block" })
|
|
940
|
+
] }) });
|
|
941
|
+
}
|
|
942
|
+
return /* @__PURE__ */ jsx(SectionBand, { node, ctx, anchor: props.anchor, spacing: "lg", children: intro("center") });
|
|
943
|
+
}
|
|
944
|
+
});
|
|
945
|
+
var schema8 = z.object({
|
|
946
|
+
heading: Line(90).optional(),
|
|
947
|
+
items: z.array(
|
|
948
|
+
z.object({
|
|
949
|
+
/** The organisation's name, or a badge's words: the logo's alt text when the logo has none. */
|
|
950
|
+
name: Line(80),
|
|
951
|
+
/** Without one, the name shows as a text badge. */
|
|
952
|
+
image: Image.optional(),
|
|
953
|
+
href: Href.optional()
|
|
954
|
+
})
|
|
955
|
+
).min(1).max(24),
|
|
956
|
+
/** The trust bar's short claim ("Small classes, friendly coaches"). */
|
|
957
|
+
claim: Copy(200).optional(),
|
|
958
|
+
surface: Surface.default("base"),
|
|
959
|
+
anchor: Anchor
|
|
960
|
+
});
|
|
961
|
+
var logosSection = defineSection({
|
|
962
|
+
type: "logos",
|
|
963
|
+
version: 1,
|
|
964
|
+
label: "Logos",
|
|
965
|
+
description: "Partners, clients or press, as a row or grid of logos.",
|
|
966
|
+
category: "social-proof",
|
|
967
|
+
schema: schema8,
|
|
968
|
+
variants: [
|
|
969
|
+
{ id: "row", label: "One row" },
|
|
970
|
+
{ id: "grid", label: "Grid" },
|
|
971
|
+
{ id: "trust-bar", label: "Trust bar: a claim with badges" }
|
|
972
|
+
],
|
|
973
|
+
defaultVariant: "row",
|
|
974
|
+
presets: [
|
|
975
|
+
{
|
|
976
|
+
name: "Logo row",
|
|
977
|
+
variant: "row",
|
|
978
|
+
props: { heading: "Trusted by", items: [{ name: "Partner", image: { src: "/logo-partner.svg", alt: "Partner" } }] }
|
|
979
|
+
},
|
|
980
|
+
{
|
|
981
|
+
name: "Trust bar",
|
|
982
|
+
variant: "trust-bar",
|
|
983
|
+
props: { claim: "Why people trust us", items: [{ name: "Small classes" }, { name: "Friendly coaches" }, { name: "Free first visit" }], surface: "inverse" }
|
|
984
|
+
}
|
|
985
|
+
],
|
|
986
|
+
render: ({ props, variant, node, ctx }) => {
|
|
987
|
+
const inverted = onAccent(props.surface);
|
|
988
|
+
const badge = (item, compact) => item.image ? /* @__PURE__ */ jsx(SiteImage, { image: { ...item.image, alt: item.image.alt || item.name, decorative: false }, ctx, sizes: "160px", className: cn("mx-auto w-auto object-contain", compact ? "h-8 max-w-32" : "h-10 max-w-40") }) : /* @__PURE__ */ jsx("span", { className: cn("text-center font-semibold uppercase tracking-wider", compact ? "text-xs sm:text-sm" : "text-sm"), children: item.name });
|
|
989
|
+
const linked = (item, content) => {
|
|
990
|
+
if (!item.href) return content;
|
|
991
|
+
const href = resolveHref(item.href, ctx);
|
|
992
|
+
return /* @__PURE__ */ jsx("a", { href, ...isExternalHref(href) ? { rel: "noopener noreferrer" } : {}, className: "inline-flex min-h-11 items-center rounded-md focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring", children: content });
|
|
993
|
+
};
|
|
994
|
+
if (variant === "trust-bar") {
|
|
995
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, spacing: "sm", children: [
|
|
996
|
+
props.heading ? /* @__PURE__ */ jsx(SectionIntro, { ctx, heading: props.heading, size: "sm", tone: inverted ? "inherit" : "muted" }) : /* @__PURE__ */ jsx(HiddenTitle, { ctx, children: props.claim ?? "Why people trust us" }),
|
|
997
|
+
/* @__PURE__ */ jsxs("div", { className: cn("flex flex-col items-center gap-4 md:flex-row md:justify-between", props.heading && "mt-6"), children: [
|
|
998
|
+
props.claim ? /* @__PURE__ */ jsx("p", { className: "text-center font-display text-lg font-semibold md:text-left", children: props.claim }) : null,
|
|
999
|
+
/* @__PURE__ */ jsx("ul", { className: "flex flex-wrap items-center justify-center gap-x-6 gap-y-3 md:justify-end", children: props.items.map((item, i) => /* @__PURE__ */ jsx("li", { className: "flex items-center", children: linked(item, badge(item, true)) }, i)) })
|
|
1000
|
+
] })
|
|
1001
|
+
] });
|
|
1002
|
+
}
|
|
1003
|
+
const intro = props.heading ? /* @__PURE__ */ jsx(SectionIntro, { ctx, heading: props.heading, size: "md", tone: inverted ? "inherit" : "muted" }) : /* @__PURE__ */ jsx(HiddenTitle, { ctx, children: "Logos" });
|
|
1004
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, spacing: "sm", children: [
|
|
1005
|
+
intro,
|
|
1006
|
+
/* @__PURE__ */ jsx("ul", { className: cn("items-center", props.heading && "mt-8", variant === "grid" ? "grid grid-cols-2 gap-8 sm:grid-cols-3 lg:grid-cols-4" : "flex flex-wrap justify-center gap-x-10 gap-y-6"), children: props.items.map((item, i) => /* @__PURE__ */ jsx("li", { className: "flex justify-center", children: linked(item, badge(item, false)) }, i)) })
|
|
1007
|
+
] });
|
|
1008
|
+
}
|
|
1009
|
+
});
|
|
1010
|
+
var schema9 = z.object({
|
|
1011
|
+
/** Replaces the brand's logo in the header. */
|
|
1012
|
+
logo: Image.optional(),
|
|
1013
|
+
/** Show the site's name beside the logo (always shown when there is no logo). */
|
|
1014
|
+
showName: z.boolean().default(true),
|
|
1015
|
+
cta: Link.optional()
|
|
1016
|
+
});
|
|
1017
|
+
function isCurrent(item, ctx) {
|
|
1018
|
+
return item.href === ctx.pagePath;
|
|
1019
|
+
}
|
|
1020
|
+
function Brand({ props, ctx }) {
|
|
1021
|
+
const logo = props.logo ?? (ctx.site.logo ? { src: ctx.site.logo.src, alt: ctx.site.logo.alt, decorative: false } : void 0);
|
|
1022
|
+
const showName = props.showName || !logo;
|
|
1023
|
+
return /* @__PURE__ */ jsxs(
|
|
1024
|
+
"a",
|
|
1025
|
+
{
|
|
1026
|
+
href: ctx.href("/"),
|
|
1027
|
+
className: "inline-flex min-h-11 items-center gap-3 rounded-md font-display text-lg font-bold tracking-tight focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
|
|
1028
|
+
children: [
|
|
1029
|
+
logo ? /* @__PURE__ */ jsx(SiteImage, { image: { ...logo, alt: showName ? "" : logo.alt || ctx.site.name, decorative: showName }, ctx, priority: true, className: "h-8 w-auto" }) : null,
|
|
1030
|
+
showName ? /* @__PURE__ */ jsx("span", { children: ctx.site.name }) : null,
|
|
1031
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: ", home" })
|
|
1032
|
+
]
|
|
1033
|
+
}
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
function Items({ ctx, className, itemClassName }) {
|
|
1037
|
+
return /* @__PURE__ */ jsx("ul", { className, children: ctx.site.nav.map((item) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
1038
|
+
"a",
|
|
1039
|
+
{
|
|
1040
|
+
href: resolveHref(item.href, ctx),
|
|
1041
|
+
...isCurrent(item, ctx) ? { "aria-current": "page" } : {},
|
|
1042
|
+
className: cn(
|
|
1043
|
+
"inline-flex min-h-11 items-center rounded-md px-3 text-sm font-medium hover:bg-muted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring aria-[current=page]:underline aria-[current=page]:underline-offset-8",
|
|
1044
|
+
itemClassName
|
|
1045
|
+
),
|
|
1046
|
+
children: item.label
|
|
1047
|
+
}
|
|
1048
|
+
) }, `${item.label}-${item.href}`)) });
|
|
1049
|
+
}
|
|
1050
|
+
function NavSection({ props, variant, node, ctx }) {
|
|
1051
|
+
const attrs = bandAttrs(node, ctx, void 0, false);
|
|
1052
|
+
const hasMenu = variant !== "minimal" && ctx.site.nav.length > 0;
|
|
1053
|
+
const centered = variant === "centered";
|
|
1054
|
+
return /* @__PURE__ */ jsx(Band, { ...attrs, as: "header", spacing: "none", className: cn("border-b border-border", attrs.className), children: /* @__PURE__ */ jsxs("div", { className: cn("relative flex min-h-16 flex-wrap items-center justify-between gap-x-4 gap-y-2 py-2", centered && "md:flex-col md:justify-center md:py-4"), children: [
|
|
1055
|
+
/* @__PURE__ */ jsx(Brand, { props, ctx }),
|
|
1056
|
+
hasMenu ? /* @__PURE__ */ jsxs("nav", { "aria-label": "Main", className: cn("flex items-center gap-2", centered && "md:w-full md:justify-center"), children: [
|
|
1057
|
+
/* @__PURE__ */ jsx(Items, { ctx, className: "hidden items-center gap-1 md:flex" }),
|
|
1058
|
+
props.cta ? /* @__PURE__ */ jsx(ButtonLink, { link: props.cta, ctx, className: "hidden md:inline-flex" }) : null,
|
|
1059
|
+
/* @__PURE__ */ jsxs("details", { className: "group md:hidden", "data-gs-island": "menu", children: [
|
|
1060
|
+
/* @__PURE__ */ jsxs("summary", { className: "flex min-h-11 min-w-11 cursor-pointer list-none items-center justify-center rounded-md border border-border focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring [&::-webkit-details-marker]:hidden", children: [
|
|
1061
|
+
/* @__PURE__ */ jsx(Menu, { "aria-hidden": "true", className: "size-5" }),
|
|
1062
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Menu" })
|
|
1063
|
+
] }),
|
|
1064
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute inset-x-0 top-full z-50 border-b border-border bg-background px-4 pb-4 shadow-lg", children: [
|
|
1065
|
+
/* @__PURE__ */ jsx(Items, { ctx, className: "flex flex-col py-2", itemClassName: "w-full" }),
|
|
1066
|
+
props.cta ? /* @__PURE__ */ jsx(ButtonLink, { link: props.cta, ctx, className: "w-full" }) : null
|
|
1067
|
+
] })
|
|
1068
|
+
] })
|
|
1069
|
+
] }) : props.cta ? /* @__PURE__ */ jsx(ButtonLink, { link: props.cta, ctx }) : null
|
|
1070
|
+
] }) });
|
|
1071
|
+
}
|
|
1072
|
+
var navSection = defineSection({
|
|
1073
|
+
type: "nav",
|
|
1074
|
+
version: 1,
|
|
1075
|
+
label: "Navigation",
|
|
1076
|
+
description: "The site's header: logo, menu and a button. On phones the menu folds into a button.",
|
|
1077
|
+
category: "header",
|
|
1078
|
+
placement: "header",
|
|
1079
|
+
limit: 1,
|
|
1080
|
+
schema: schema9,
|
|
1081
|
+
variants: [
|
|
1082
|
+
{ id: "simple", label: "Logo left, menu right" },
|
|
1083
|
+
{ id: "centered", label: "Logo centred, menu below" },
|
|
1084
|
+
{ id: "minimal", label: "Logo and button only" }
|
|
1085
|
+
],
|
|
1086
|
+
defaultVariant: "simple",
|
|
1087
|
+
presets: [
|
|
1088
|
+
{ name: "Header with menu", variant: "simple", props: { showName: true, cta: { label: "Get started", href: "#contact" } } },
|
|
1089
|
+
{ name: "Centred header", variant: "centered", props: { showName: true } },
|
|
1090
|
+
{ name: "Landing page header", variant: "minimal", props: { showName: true, cta: { label: "Get started", href: "#contact" } } }
|
|
1091
|
+
],
|
|
1092
|
+
render: (input) => /* @__PURE__ */ jsx(NavSection, { ...input })
|
|
1093
|
+
});
|
|
1094
|
+
var Tier = z.object({
|
|
1095
|
+
name: Line(40),
|
|
1096
|
+
price: Line(20),
|
|
1097
|
+
/** "per month", "per session". */
|
|
1098
|
+
period: Line(30).optional(),
|
|
1099
|
+
description: Copy(200).optional(),
|
|
1100
|
+
features: z.array(Line(100)).max(12).default([]),
|
|
1101
|
+
cta: Link.optional(),
|
|
1102
|
+
highlighted: z.boolean().optional(),
|
|
1103
|
+
/** The highlighted plan's label (default "Most popular"). */
|
|
1104
|
+
badge: Line(30).optional()
|
|
1105
|
+
});
|
|
1106
|
+
var Group = z.object({
|
|
1107
|
+
eyebrow: Line(60).optional(),
|
|
1108
|
+
heading: Line(90),
|
|
1109
|
+
body: Copy(320).optional(),
|
|
1110
|
+
tiers: z.array(Tier).min(1).max(12),
|
|
1111
|
+
note: Copy(300).optional(),
|
|
1112
|
+
anchor: Anchor
|
|
1113
|
+
});
|
|
1114
|
+
var schema10 = z.object({
|
|
1115
|
+
eyebrow: Line(60).optional(),
|
|
1116
|
+
heading: Line(90),
|
|
1117
|
+
body: Copy(320).optional(),
|
|
1118
|
+
/** Plans with no group. */
|
|
1119
|
+
tiers: z.array(Tier).max(12).default([]),
|
|
1120
|
+
/** Plans in groups, each with its heading: many plans on one page. */
|
|
1121
|
+
groups: z.array(Group).max(8).default([]),
|
|
1122
|
+
note: Copy(300).optional(),
|
|
1123
|
+
surface: Surface.default("base"),
|
|
1124
|
+
anchor: Anchor
|
|
1125
|
+
}).refine((p) => p.tiers.length + p.groups.length > 0, { message: "Add a plan, or a group of plans.", path: ["tiers"] });
|
|
1126
|
+
var COLS = { 1: "max-w-md mx-auto", 2: "md:grid-cols-2 max-w-4xl mx-auto", 3: "md:grid-cols-3", 4: "md:grid-cols-2 lg:grid-cols-4" };
|
|
1127
|
+
var cols = (n) => n <= 4 ? COLS[Math.max(1, n)] : "md:grid-cols-2 lg:grid-cols-3";
|
|
1128
|
+
function GroupTitle({ ctx, id, children }) {
|
|
1129
|
+
const Tag = `h${ctx.headingLevel + 1}`;
|
|
1130
|
+
return /* @__PURE__ */ jsx(Tag, { id, className: "font-display text-2xl font-semibold tracking-tight", children });
|
|
1131
|
+
}
|
|
1132
|
+
function PlanTitle({ ctx, grouped, children }) {
|
|
1133
|
+
if (!grouped) return /* @__PURE__ */ jsx(ItemTitle, { ctx, children });
|
|
1134
|
+
const Tag = `h${ctx.headingLevel + 2}`;
|
|
1135
|
+
return /* @__PURE__ */ jsx(Tag, { className: "font-display text-lg font-semibold leading-snug", children });
|
|
1136
|
+
}
|
|
1137
|
+
var pricingSection = defineSection({
|
|
1138
|
+
type: "pricing",
|
|
1139
|
+
version: 1,
|
|
1140
|
+
label: "Pricing",
|
|
1141
|
+
description: "Up to four plans with a price, what's included and a button.",
|
|
1142
|
+
category: "conversion",
|
|
1143
|
+
schema: schema10,
|
|
1144
|
+
variants: [
|
|
1145
|
+
{ id: "cards", label: "Plan cards" },
|
|
1146
|
+
{ id: "compact", label: "Compact list" }
|
|
1147
|
+
],
|
|
1148
|
+
defaultVariant: "cards",
|
|
1149
|
+
presets: [
|
|
1150
|
+
{
|
|
1151
|
+
name: "Plan groups",
|
|
1152
|
+
variant: "cards",
|
|
1153
|
+
props: {
|
|
1154
|
+
heading: "Memberships",
|
|
1155
|
+
groups: [
|
|
1156
|
+
{ heading: "Start here", anchor: "start", tiers: [{ name: "Single visit", price: "$20", description: "One class", cta: { label: "Book a visit", href: "#contact" } }] },
|
|
1157
|
+
{ heading: "Every week", anchor: "weekly", tiers: [{ name: "Monthly", price: "$99", period: "per month", cta: { label: "Join monthly", href: "#contact" } }, { name: "Yearly", price: "$89", period: "per month", description: "Paid yearly", highlighted: true, cta: { label: "Join for a year", href: "#contact" } }] }
|
|
1158
|
+
]
|
|
1159
|
+
}
|
|
1160
|
+
},
|
|
1161
|
+
{
|
|
1162
|
+
name: "Three plans",
|
|
1163
|
+
variant: "cards",
|
|
1164
|
+
props: {
|
|
1165
|
+
heading: "Plans",
|
|
1166
|
+
tiers: [
|
|
1167
|
+
{ name: "Starter", price: "$29", period: "per month", features: ["One thing", "Another thing"], cta: { label: "Choose Starter", href: "#contact" } },
|
|
1168
|
+
{ name: "Regular", price: "$59", period: "per month", features: ["Everything in Starter", "More"], cta: { label: "Choose Regular", href: "#contact" }, highlighted: true },
|
|
1169
|
+
{ name: "Unlimited", price: "$99", period: "per month", features: ["Everything", "Most"], cta: { label: "Choose Unlimited", href: "#contact" } }
|
|
1170
|
+
]
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
],
|
|
1174
|
+
render: ({ props, variant, node, ctx }) => {
|
|
1175
|
+
const inverted = onAccent(props.surface);
|
|
1176
|
+
const intro = /* @__PURE__ */ jsx(SectionIntro, { ctx, eyebrow: props.eyebrow, heading: props.heading, body: props.body, tone: inverted ? "inherit" : "muted" });
|
|
1177
|
+
const noteOf = (text) => text ? /* @__PURE__ */ jsx("p", { className: cn("mt-8 text-center text-sm", !inverted && "text-muted-foreground"), children: text }) : null;
|
|
1178
|
+
const grouped = props.groups.length > 0;
|
|
1179
|
+
const compactList = (tiers, inGroup, className) => /* @__PURE__ */ jsx("ul", { className: cn("mx-auto flex max-w-3xl flex-col divide-y divide-border rounded-xl border border-border bg-background text-foreground", className), children: tiers.map((tier, i) => /* @__PURE__ */ jsxs("li", { className: "flex flex-col gap-4 p-6 sm:flex-row sm:items-center sm:justify-between", children: [
|
|
1180
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
1181
|
+
/* @__PURE__ */ jsx(PlanTitle, { ctx, grouped: inGroup, children: tier.name }),
|
|
1182
|
+
tier.description ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: tier.description }) : null
|
|
1183
|
+
] }),
|
|
1184
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
1185
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
1186
|
+
/* @__PURE__ */ jsx("span", { className: "font-display text-2xl font-bold", children: tier.price }),
|
|
1187
|
+
tier.period ? /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
|
|
1188
|
+
" ",
|
|
1189
|
+
tier.period
|
|
1190
|
+
] }) : null
|
|
1191
|
+
] }),
|
|
1192
|
+
tier.cta ? /* @__PURE__ */ jsx(ButtonLink, { link: tier.cta, ctx, tone: tier.highlighted ? "primary" : "outline" }) : null
|
|
1193
|
+
] })
|
|
1194
|
+
] }, i)) });
|
|
1195
|
+
const cardGrid = (tiers, inGroup, className) => /* @__PURE__ */ jsx("ul", { className: cn("grid gap-6", cols(tiers.length), className), children: tiers.map((tier, i) => /* @__PURE__ */ jsxs(
|
|
1196
|
+
"li",
|
|
1197
|
+
{
|
|
1198
|
+
className: cn(
|
|
1199
|
+
"flex flex-col gap-6 rounded-2xl border bg-background p-8 text-foreground",
|
|
1200
|
+
tier.highlighted ? "border-2 border-primary shadow-lg" : "border-border"
|
|
1201
|
+
),
|
|
1202
|
+
children: [
|
|
1203
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
1204
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
1205
|
+
/* @__PURE__ */ jsx(PlanTitle, { ctx, grouped: inGroup, children: tier.name }),
|
|
1206
|
+
tier.highlighted ? /* @__PURE__ */ jsx("span", { className: "rounded-full bg-primary px-3 py-1 text-xs font-semibold text-primary-foreground", children: tier.badge ?? "Most popular" }) : null
|
|
1207
|
+
] }),
|
|
1208
|
+
tier.description ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: tier.description }) : null
|
|
1209
|
+
] }),
|
|
1210
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
1211
|
+
/* @__PURE__ */ jsx("span", { className: "font-display text-4xl font-bold", children: tier.price }),
|
|
1212
|
+
tier.period ? /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
1213
|
+
" ",
|
|
1214
|
+
tier.period
|
|
1215
|
+
] }) : null
|
|
1216
|
+
] }),
|
|
1217
|
+
tier.features.length ? /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-3", children: tier.features.map((f, j) => /* @__PURE__ */ jsxs("li", { className: "flex gap-3", children: [
|
|
1218
|
+
/* @__PURE__ */ jsx(Check, { "aria-hidden": "true", className: "mt-0.5 size-5 shrink-0" }),
|
|
1219
|
+
/* @__PURE__ */ jsx("span", { children: f })
|
|
1220
|
+
] }, j)) }) : null,
|
|
1221
|
+
tier.cta ? /* @__PURE__ */ jsx(ButtonLink, { link: tier.cta, ctx, tone: tier.highlighted ? "primary" : "outline", className: "mt-auto w-full" }) : null
|
|
1222
|
+
]
|
|
1223
|
+
},
|
|
1224
|
+
i
|
|
1225
|
+
)) });
|
|
1226
|
+
const plans = (tiers, inGroup, className) => variant === "compact" ? compactList(tiers, inGroup, className) : cardGrid(tiers, inGroup, className);
|
|
1227
|
+
const groupBlock = (group, gi) => {
|
|
1228
|
+
const id = `${ctx.idPrefix}-group-${gi}`;
|
|
1229
|
+
return /* @__PURE__ */ jsxs("section", { "aria-labelledby": id, ...group.anchor ? { id: group.anchor } : {}, className: cn("flex flex-col gap-3", group.anchor && "scroll-mt-20"), children: [
|
|
1230
|
+
/* @__PURE__ */ jsxs("div", { className: "mx-auto flex max-w-3xl flex-col items-center gap-2 text-center", children: [
|
|
1231
|
+
group.eyebrow ? /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold uppercase tracking-wider", children: group.eyebrow }) : null,
|
|
1232
|
+
/* @__PURE__ */ jsx(GroupTitle, { ctx, id, children: group.heading }),
|
|
1233
|
+
group.body ? /* @__PURE__ */ jsx("p", { className: inverted ? void 0 : "text-muted-foreground", children: group.body }) : null
|
|
1234
|
+
] }),
|
|
1235
|
+
plans(group.tiers, true, "mt-4"),
|
|
1236
|
+
noteOf(group.note)
|
|
1237
|
+
] }, gi);
|
|
1238
|
+
};
|
|
1239
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, width: variant === "compact" && !grouped ? "md" : "lg", children: [
|
|
1240
|
+
intro,
|
|
1241
|
+
props.tiers.length ? plans(props.tiers, false, variant === "compact" ? "mt-10" : "mt-12") : null,
|
|
1242
|
+
grouped ? /* @__PURE__ */ jsx("div", { className: "mt-12 flex flex-col gap-16", children: props.groups.map(groupBlock) }) : null,
|
|
1243
|
+
noteOf(props.note)
|
|
1244
|
+
] });
|
|
1245
|
+
}
|
|
1246
|
+
});
|
|
1247
|
+
var schema11 = z.object({
|
|
1248
|
+
heading: Line(90).optional(),
|
|
1249
|
+
body: RichText,
|
|
1250
|
+
surface: Surface.default("base"),
|
|
1251
|
+
anchor: Anchor
|
|
1252
|
+
});
|
|
1253
|
+
var richTextSection = defineSection({
|
|
1254
|
+
type: "rich-text",
|
|
1255
|
+
version: 1,
|
|
1256
|
+
label: "Text",
|
|
1257
|
+
description: "Long copy with headings, lists, links and quotes. Sanitised when it renders.",
|
|
1258
|
+
category: "content",
|
|
1259
|
+
schema: schema11,
|
|
1260
|
+
variants: [
|
|
1261
|
+
{ id: "narrow", label: "Reading width" },
|
|
1262
|
+
{ id: "wide", label: "Wide" }
|
|
1263
|
+
],
|
|
1264
|
+
defaultVariant: "narrow",
|
|
1265
|
+
presets: [
|
|
1266
|
+
{
|
|
1267
|
+
name: "Text",
|
|
1268
|
+
variant: "narrow",
|
|
1269
|
+
props: {
|
|
1270
|
+
body: {
|
|
1271
|
+
type: "doc",
|
|
1272
|
+
content: [
|
|
1273
|
+
{ type: "heading", attrs: { level: 2 }, content: [{ type: "text", text: "A heading" }] },
|
|
1274
|
+
{ type: "paragraph", content: [{ type: "text", text: "Write here." }] }
|
|
1275
|
+
]
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
],
|
|
1280
|
+
render: ({ props, variant, node, ctx }) => {
|
|
1281
|
+
const intro = props.heading ? /* @__PURE__ */ jsx(SectionIntro, { ctx, heading: props.heading, align: "left", tone: onAccent(props.surface) ? "inherit" : "muted" }) : null;
|
|
1282
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, width: variant === "wide" ? "lg" : "prose", labelled: Boolean(props.heading), children: [
|
|
1283
|
+
intro,
|
|
1284
|
+
/* @__PURE__ */ jsx(
|
|
1285
|
+
RichText$1,
|
|
1286
|
+
{
|
|
1287
|
+
value: props.body,
|
|
1288
|
+
headingLevels: props.heading ? [3, 4] : [2, 3, 4],
|
|
1289
|
+
className: cn(richTextClassName, "text-base sm:text-lg [&_h2]:font-display [&_h2]:text-2xl [&_h3]:font-display [&_h3]:text-xl", props.heading && "mt-6")
|
|
1290
|
+
}
|
|
1291
|
+
)
|
|
1292
|
+
] });
|
|
1293
|
+
}
|
|
1294
|
+
});
|
|
1295
|
+
var schema12 = z.object({
|
|
1296
|
+
heading: Line(90).optional(),
|
|
1297
|
+
body: Copy(320).optional(),
|
|
1298
|
+
items: z.array(z.object({ value: Line(16), label: Line(60) })).min(1).max(6),
|
|
1299
|
+
surface: Surface.default("muted"),
|
|
1300
|
+
anchor: Anchor
|
|
1301
|
+
});
|
|
1302
|
+
var COLS2 = { 1: "", 2: "sm:grid-cols-2", 3: "sm:grid-cols-3", 4: "sm:grid-cols-2 lg:grid-cols-4", 5: "sm:grid-cols-3 lg:grid-cols-5", 6: "sm:grid-cols-3 lg:grid-cols-6" };
|
|
1303
|
+
var statsSection = defineSection({
|
|
1304
|
+
type: "stats",
|
|
1305
|
+
version: 1,
|
|
1306
|
+
label: "Stats",
|
|
1307
|
+
description: "A few numbers that matter, each with a label.",
|
|
1308
|
+
category: "social-proof",
|
|
1309
|
+
schema: schema12,
|
|
1310
|
+
variants: [
|
|
1311
|
+
{ id: "row", label: "In a row" },
|
|
1312
|
+
{ id: "cards", label: "Cards" }
|
|
1313
|
+
],
|
|
1314
|
+
defaultVariant: "row",
|
|
1315
|
+
presets: [
|
|
1316
|
+
{
|
|
1317
|
+
name: "Three numbers",
|
|
1318
|
+
variant: "row",
|
|
1319
|
+
props: { items: [{ value: "12", label: "years open" }, { value: "40+", label: "classes a week" }, { value: "6", label: "coaches" }] }
|
|
1320
|
+
}
|
|
1321
|
+
],
|
|
1322
|
+
render: ({ props, variant, node, ctx }) => {
|
|
1323
|
+
const inverted = onAccent(props.surface);
|
|
1324
|
+
const intro = props.heading ? /* @__PURE__ */ jsx(SectionIntro, { ctx, heading: props.heading, body: props.body, tone: inverted ? "inherit" : "muted" }) : /* @__PURE__ */ jsx(HiddenTitle, { ctx, children: "In numbers" });
|
|
1325
|
+
const cards = variant === "cards";
|
|
1326
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: [
|
|
1327
|
+
intro,
|
|
1328
|
+
/* @__PURE__ */ jsx("dl", { className: cn("grid gap-6 text-center", COLS2[props.items.length], props.heading && "mt-10"), children: props.items.map((item, i) => /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col-reverse gap-1", cards && "rounded-xl border border-border bg-background p-6 text-foreground"), children: [
|
|
1329
|
+
/* @__PURE__ */ jsx("dt", { className: cn("text-sm font-medium", !inverted || cards ? "text-muted-foreground" : void 0), children: item.label }),
|
|
1330
|
+
/* @__PURE__ */ jsx("dd", { className: "font-display text-4xl font-bold tracking-tight sm:text-5xl", children: item.value })
|
|
1331
|
+
] }, i)) })
|
|
1332
|
+
] });
|
|
1333
|
+
}
|
|
1334
|
+
});
|
|
1335
|
+
var Quote = z.object({
|
|
1336
|
+
quote: Copy(500).min(1),
|
|
1337
|
+
name: Line(80),
|
|
1338
|
+
role: Line(80).optional(),
|
|
1339
|
+
avatar: Image.optional()
|
|
1340
|
+
});
|
|
1341
|
+
var schema13 = z.object({
|
|
1342
|
+
heading: Line(90).optional(),
|
|
1343
|
+
body: Copy(320).optional(),
|
|
1344
|
+
items: z.array(Quote).min(1).max(12),
|
|
1345
|
+
surface: Surface.default("muted"),
|
|
1346
|
+
anchor: Anchor
|
|
1347
|
+
});
|
|
1348
|
+
var testimonialsSection = defineSection({
|
|
1349
|
+
type: "testimonials",
|
|
1350
|
+
version: 1,
|
|
1351
|
+
label: "Testimonials",
|
|
1352
|
+
description: "Quotes from real people, with their permission.",
|
|
1353
|
+
category: "social-proof",
|
|
1354
|
+
schema: schema13,
|
|
1355
|
+
variants: [
|
|
1356
|
+
{ id: "grid", label: "Quotes in a grid" },
|
|
1357
|
+
{ id: "single", label: "One large quote" }
|
|
1358
|
+
],
|
|
1359
|
+
defaultVariant: "grid",
|
|
1360
|
+
presets: [
|
|
1361
|
+
{ name: "Quotes", variant: "grid", props: { heading: "What people say", items: [{ quote: "A quote, in their words.", name: "Their name" }] } },
|
|
1362
|
+
{ name: "One quote", variant: "single", props: { items: [{ quote: "A quote, in their words.", name: "Their name", role: "Who they are" }] } }
|
|
1363
|
+
],
|
|
1364
|
+
render: ({ props, variant, node, ctx }) => {
|
|
1365
|
+
const inverted = onAccent(props.surface);
|
|
1366
|
+
const muted = inverted ? void 0 : "text-muted-foreground";
|
|
1367
|
+
const person = (item, large = false) => /* @__PURE__ */ jsxs("figcaption", { className: "flex items-center gap-3", children: [
|
|
1368
|
+
item.avatar ? /* @__PURE__ */ jsx(SiteImage, { image: item.avatar, ctx, sizes: "48px", className: cn("rounded-full object-cover", large ? "size-14" : "size-11") }) : null,
|
|
1369
|
+
/* @__PURE__ */ jsxs("span", { className: "flex flex-col text-left", children: [
|
|
1370
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold", children: item.name }),
|
|
1371
|
+
item.role ? /* @__PURE__ */ jsx("span", { className: cn("text-sm", muted), children: item.role }) : null
|
|
1372
|
+
] })
|
|
1373
|
+
] });
|
|
1374
|
+
const intro = props.heading ? /* @__PURE__ */ jsx(SectionIntro, { ctx, heading: props.heading, body: props.body, tone: inverted ? "inherit" : "muted" }) : /* @__PURE__ */ jsx(HiddenTitle, { ctx, children: "Testimonials" });
|
|
1375
|
+
if (variant === "single") {
|
|
1376
|
+
const item = props.items[0];
|
|
1377
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, width: "md", children: [
|
|
1378
|
+
intro,
|
|
1379
|
+
/* @__PURE__ */ jsxs("figure", { className: cn("mx-auto flex max-w-3xl flex-col items-center gap-8 text-center", props.heading && "mt-10"), children: [
|
|
1380
|
+
/* @__PURE__ */ jsx("blockquote", { className: "font-display text-2xl font-medium leading-snug text-balance sm:text-3xl", children: /* @__PURE__ */ jsxs("p", { children: [
|
|
1381
|
+
"\u201C",
|
|
1382
|
+
item.quote,
|
|
1383
|
+
"\u201D"
|
|
1384
|
+
] }) }),
|
|
1385
|
+
person(item, true)
|
|
1386
|
+
] })
|
|
1387
|
+
] });
|
|
1388
|
+
}
|
|
1389
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: [
|
|
1390
|
+
intro,
|
|
1391
|
+
/* @__PURE__ */ jsx("ul", { className: cn("grid gap-6 sm:grid-cols-2 lg:grid-cols-3", props.heading && "mt-12"), children: props.items.map((item, i) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("figure", { className: "flex h-full flex-col justify-between gap-6 rounded-xl border border-border bg-background p-6 text-foreground", children: [
|
|
1392
|
+
/* @__PURE__ */ jsx("blockquote", { className: "leading-relaxed", children: /* @__PURE__ */ jsxs("p", { children: [
|
|
1393
|
+
"\u201C",
|
|
1394
|
+
item.quote,
|
|
1395
|
+
"\u201D"
|
|
1396
|
+
] }) }),
|
|
1397
|
+
person(item)
|
|
1398
|
+
] }) }, i)) })
|
|
1399
|
+
] });
|
|
1400
|
+
}
|
|
1401
|
+
});
|
|
1402
|
+
var schema14 = z.object({
|
|
1403
|
+
eyebrow: Line(60).optional(),
|
|
1404
|
+
heading: Line(90),
|
|
1405
|
+
body: Copy(320).optional(),
|
|
1406
|
+
/** Posts per page. */
|
|
1407
|
+
perPage: z.number().int().min(1).max(24).default(6),
|
|
1408
|
+
/** Pages of posts (`?page=2`); off shows only the newest `perPage`. */
|
|
1409
|
+
paginate: z.boolean().default(true),
|
|
1410
|
+
/** Only posts in this category. */
|
|
1411
|
+
category: Line(60).optional(),
|
|
1412
|
+
showExcerpt: z.boolean().default(true),
|
|
1413
|
+
showDate: z.boolean().default(true),
|
|
1414
|
+
emptyText: Line(120).default("No posts yet. Check back soon."),
|
|
1415
|
+
/** A button under the list ("All posts"). */
|
|
1416
|
+
cta: Link.optional(),
|
|
1417
|
+
surface: Surface.default("base"),
|
|
1418
|
+
anchor: Anchor
|
|
1419
|
+
});
|
|
1420
|
+
function formatPostDate(iso, lang) {
|
|
1421
|
+
const date = new Date(iso);
|
|
1422
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
1423
|
+
try {
|
|
1424
|
+
return new Intl.DateTimeFormat(lang, { dateStyle: "medium", timeZone: "UTC" }).format(date);
|
|
1425
|
+
} catch {
|
|
1426
|
+
return date.toISOString().slice(0, 10);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
function pageHref(ctx, page) {
|
|
1430
|
+
return ctx.href(page <= 1 ? ctx.pagePath : `${ctx.pagePath}?page=${page}`);
|
|
1431
|
+
}
|
|
1432
|
+
function PostMeta({ post, ctx, showDate, muted }) {
|
|
1433
|
+
const date = showDate ? formatPostDate(post.publishedAt, ctx.site.lang) : "";
|
|
1434
|
+
if (!date && !post.category) return null;
|
|
1435
|
+
return /* @__PURE__ */ jsxs("p", { className: cn("flex flex-wrap gap-x-3 text-sm", muted && "text-muted-foreground"), children: [
|
|
1436
|
+
post.category ? /* @__PURE__ */ jsx("span", { className: "font-semibold uppercase tracking-wide", children: post.category }) : null,
|
|
1437
|
+
date ? /* @__PURE__ */ jsx("time", { dateTime: post.publishedAt, children: date }) : null
|
|
1438
|
+
] });
|
|
1439
|
+
}
|
|
1440
|
+
var blogListSection = defineSection({
|
|
1441
|
+
type: "blog-list",
|
|
1442
|
+
version: 1,
|
|
1443
|
+
label: "Blog posts",
|
|
1444
|
+
description: "The blog's posts, newest first, as a list or cards, with pages. Needs the site's post page.",
|
|
1445
|
+
category: "content",
|
|
1446
|
+
schema: schema14,
|
|
1447
|
+
variants: [
|
|
1448
|
+
{ id: "grid", label: "Cards with covers" },
|
|
1449
|
+
{ id: "list", label: "List" }
|
|
1450
|
+
],
|
|
1451
|
+
defaultVariant: "grid",
|
|
1452
|
+
presets: [
|
|
1453
|
+
{ name: "Blog index", variant: "grid", props: { heading: "Latest posts", perPage: 9 } },
|
|
1454
|
+
{ name: "Latest three", variant: "list", props: { heading: "From the blog", perPage: 3, paginate: false, cta: { label: "All posts", href: "/blog" } } }
|
|
1455
|
+
],
|
|
1456
|
+
render: ({ props, variant, node, ctx }) => {
|
|
1457
|
+
const inverted = onAccent(props.surface);
|
|
1458
|
+
const intro = /* @__PURE__ */ jsx(SectionIntro, { ctx, eyebrow: props.eyebrow, heading: props.heading, body: props.body, tone: inverted ? "inherit" : "muted" });
|
|
1459
|
+
const blog = ctx.posts;
|
|
1460
|
+
if (!blog) {
|
|
1461
|
+
console.error(`[genesis-sites] ${ctx.pagePath}: the post list has no posts to show (the site has no post page, or the store can't list posts).`);
|
|
1462
|
+
return ctx.mode === "preview" ? /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: [
|
|
1463
|
+
intro,
|
|
1464
|
+
/* @__PURE__ */ jsx("p", { role: "status", className: "mt-8 rounded-lg border border-dashed border-border p-6 text-center", children: "The post list needs the site's post page (a page of kind \u201Cpost\u201D, such as /blog)." })
|
|
1465
|
+
] }) : null;
|
|
1466
|
+
}
|
|
1467
|
+
const all = blog.list.filter((p) => p.slug !== blog.current?.slug && (!props.category || p.category === props.category));
|
|
1468
|
+
const pages = props.paginate ? Math.max(1, Math.ceil(all.length / props.perPage)) : 1;
|
|
1469
|
+
const page = props.paginate && !blog.current ? Math.min(Math.max(1, blog.page), pages) : 1;
|
|
1470
|
+
const shown = all.slice((page - 1) * props.perPage, page * props.perPage);
|
|
1471
|
+
const href = (p) => ctx.href(postPath(blog.path, p.slug));
|
|
1472
|
+
const grid = variant === "grid";
|
|
1473
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: [
|
|
1474
|
+
intro,
|
|
1475
|
+
shown.length ? /* @__PURE__ */ jsx("ul", { className: cn("mt-10", grid ? "grid gap-6 sm:grid-cols-2 lg:grid-cols-3" : "mx-auto flex max-w-3xl flex-col divide-y divide-border"), children: shown.map((post) => /* @__PURE__ */ jsx("li", { className: grid ? "flex" : "py-6 first:pt-0", children: /* @__PURE__ */ jsxs("article", { className: cn("flex w-full flex-col gap-3", grid && "rounded-xl border border-border bg-background text-foreground"), children: [
|
|
1476
|
+
grid ? post.cover ? (
|
|
1477
|
+
// The title says what the post is; its cover is decoration here.
|
|
1478
|
+
/* @__PURE__ */ jsx(SiteImage, { image: { ...post.cover, decorative: true }, ctx, sizes: "(min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw", className: "aspect-[16/10] w-full rounded-t-xl object-cover" })
|
|
1479
|
+
) : /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "aspect-[16/10] w-full rounded-t-xl bg-muted" }) : null,
|
|
1480
|
+
/* @__PURE__ */ jsxs("div", { className: cn("flex flex-1 flex-col gap-2", grid && "p-5 pt-2"), children: [
|
|
1481
|
+
/* @__PURE__ */ jsx(PostMeta, { post, ctx, showDate: props.showDate, muted: grid || !inverted }),
|
|
1482
|
+
/* @__PURE__ */ jsx(ItemTitle, { ctx, className: "text-xl", children: /* @__PURE__ */ jsx("a", { href: href(post), className: "rounded-sm underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring", children: post.title }) }),
|
|
1483
|
+
props.showExcerpt && post.excerpt ? /* @__PURE__ */ jsx("p", { className: cn(grid || !inverted ? "text-muted-foreground" : void 0), children: post.excerpt }) : null
|
|
1484
|
+
] })
|
|
1485
|
+
] }) }, post.slug)) }) : /* @__PURE__ */ jsx("p", { className: "mt-10 text-center", children: props.emptyText }),
|
|
1486
|
+
props.paginate && pages > 1 && !blog.current ? /* @__PURE__ */ jsxs("nav", { "aria-label": `${props.heading}: pages`, className: "mt-10 flex items-center justify-between gap-4", children: [
|
|
1487
|
+
page > 1 ? /* @__PURE__ */ jsx("a", { href: pageHref(ctx, page - 1), rel: "prev", className: "inline-flex min-h-11 items-center rounded-md px-2 font-medium underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring", children: "Newer posts" }) : /* @__PURE__ */ jsx("span", {}),
|
|
1488
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
|
|
1489
|
+
"Page ",
|
|
1490
|
+
page,
|
|
1491
|
+
" of ",
|
|
1492
|
+
pages
|
|
1493
|
+
] }),
|
|
1494
|
+
page < pages ? /* @__PURE__ */ jsx("a", { href: pageHref(ctx, page + 1), rel: "next", className: "inline-flex min-h-11 items-center rounded-md px-2 font-medium underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring", children: "Older posts" }) : /* @__PURE__ */ jsx("span", {})
|
|
1495
|
+
] }) : null,
|
|
1496
|
+
props.cta ? /* @__PURE__ */ jsx("div", { className: "mt-10 flex justify-center", children: /* @__PURE__ */ jsx(ButtonLink, { link: props.cta, ctx, tone: inverted ? "inverse" : "outline" }) }) : null
|
|
1497
|
+
] });
|
|
1498
|
+
}
|
|
1499
|
+
});
|
|
1500
|
+
var Item2 = z.object({
|
|
1501
|
+
label: Line(120),
|
|
1502
|
+
description: Copy(300).optional(),
|
|
1503
|
+
file: FileRef
|
|
1504
|
+
});
|
|
1505
|
+
var schema15 = z.object({
|
|
1506
|
+
eyebrow: Line(60).optional(),
|
|
1507
|
+
heading: Line(90),
|
|
1508
|
+
body: Copy(320).optional(),
|
|
1509
|
+
files: z.array(Item2).min(1).max(24),
|
|
1510
|
+
surface: Surface.default("base"),
|
|
1511
|
+
anchor: Anchor
|
|
1512
|
+
});
|
|
1513
|
+
function DownloadLink({ item, ctx, className }) {
|
|
1514
|
+
const href = fileHref(item.file, ctx);
|
|
1515
|
+
if (!href) return /* @__PURE__ */ jsx("span", { className, children: item.label });
|
|
1516
|
+
const sitePath = item.file.src.startsWith("/");
|
|
1517
|
+
return /* @__PURE__ */ jsxs(
|
|
1518
|
+
"a",
|
|
1519
|
+
{
|
|
1520
|
+
href,
|
|
1521
|
+
type: item.file.mime,
|
|
1522
|
+
...sitePath ? { download: "" } : { rel: "noopener" },
|
|
1523
|
+
"data-gs-event": "download",
|
|
1524
|
+
className: cn("inline-flex min-h-11 items-center gap-2 rounded-sm font-medium underline underline-offset-4 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring", className),
|
|
1525
|
+
children: [
|
|
1526
|
+
/* @__PURE__ */ jsx(Download, { "aria-hidden": "true", className: "size-4 shrink-0" }),
|
|
1527
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1528
|
+
item.label,
|
|
1529
|
+
" ",
|
|
1530
|
+
/* @__PURE__ */ jsxs("span", { className: "whitespace-nowrap text-sm font-normal", children: [
|
|
1531
|
+
"(",
|
|
1532
|
+
fileDescription(item.file),
|
|
1533
|
+
")"
|
|
1534
|
+
] })
|
|
1535
|
+
] })
|
|
1536
|
+
]
|
|
1537
|
+
}
|
|
1538
|
+
);
|
|
1539
|
+
}
|
|
1540
|
+
var fileSection = defineSection({
|
|
1541
|
+
type: "file",
|
|
1542
|
+
version: 1,
|
|
1543
|
+
label: "Downloads",
|
|
1544
|
+
description: "Files to download (PDFs), each with what it is and how big.",
|
|
1545
|
+
category: "content",
|
|
1546
|
+
schema: schema15,
|
|
1547
|
+
variants: [
|
|
1548
|
+
{ id: "list", label: "List" },
|
|
1549
|
+
{ id: "cards", label: "Cards" }
|
|
1550
|
+
],
|
|
1551
|
+
defaultVariant: "list",
|
|
1552
|
+
presets: [
|
|
1553
|
+
{ name: "Downloads", variant: "list", props: { heading: "Downloads", files: [{ label: "Timetable", file: { src: "/files/timetable.pdf", mime: "application/pdf" } }] } },
|
|
1554
|
+
{ name: "File cards", variant: "cards", props: { heading: "Forms to bring", files: [{ label: "Waiver", description: "Sign it before your first class.", file: { src: "/files/waiver.pdf", mime: "application/pdf" } }] } }
|
|
1555
|
+
],
|
|
1556
|
+
render: ({ props, variant, node, ctx }) => {
|
|
1557
|
+
const inverted = onAccent(props.surface);
|
|
1558
|
+
const intro = /* @__PURE__ */ jsx(SectionIntro, { ctx, eyebrow: props.eyebrow, heading: props.heading, body: props.body, tone: inverted ? "inherit" : "muted" });
|
|
1559
|
+
if (variant === "cards") {
|
|
1560
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: [
|
|
1561
|
+
intro,
|
|
1562
|
+
/* @__PURE__ */ jsx("ul", { className: "mt-10 grid gap-6 sm:grid-cols-2 lg:grid-cols-3", children: props.files.map((item, i) => /* @__PURE__ */ jsxs("li", { className: "flex flex-col gap-3 rounded-xl border border-border bg-background p-6 text-foreground", children: [
|
|
1563
|
+
/* @__PURE__ */ jsx(FileText, { "aria-hidden": "true", className: "size-8" }),
|
|
1564
|
+
/* @__PURE__ */ jsx(ItemTitle, { ctx, children: item.label }),
|
|
1565
|
+
item.description ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: item.description }) : null,
|
|
1566
|
+
/* @__PURE__ */ jsx(DownloadLink, { item: { ...item, label: `Download ${item.label}` }, ctx, className: "mt-auto" })
|
|
1567
|
+
] }, i)) })
|
|
1568
|
+
] });
|
|
1569
|
+
}
|
|
1570
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, width: "md", children: [
|
|
1571
|
+
intro,
|
|
1572
|
+
/* @__PURE__ */ jsx("ul", { className: "mx-auto mt-8 flex max-w-3xl flex-col divide-y divide-border", children: props.files.map((item, i) => /* @__PURE__ */ jsxs("li", { className: "flex flex-col gap-1 py-4", children: [
|
|
1573
|
+
/* @__PURE__ */ jsx(DownloadLink, { item, ctx }),
|
|
1574
|
+
item.description ? /* @__PURE__ */ jsx("p", { className: inverted ? void 0 : "text-muted-foreground", children: item.description }) : null
|
|
1575
|
+
] }, i)) })
|
|
1576
|
+
] });
|
|
1577
|
+
}
|
|
1578
|
+
});
|
|
1579
|
+
var MAP_PROVIDERS = {
|
|
1580
|
+
openstreetmap: { name: "OpenStreetMap", origin: "https://www.openstreetmap.org" },
|
|
1581
|
+
google: { name: "Google Maps", origin: "https://www.google.com" }
|
|
1582
|
+
};
|
|
1583
|
+
var schema16 = z.object({
|
|
1584
|
+
eyebrow: Line(60).optional(),
|
|
1585
|
+
heading: Line(90),
|
|
1586
|
+
body: Copy(320).optional(),
|
|
1587
|
+
/** The address, a line each. */
|
|
1588
|
+
address: z.array(Line(120)).min(1).max(5),
|
|
1589
|
+
provider: z.enum(["openstreetmap", "google"]).default("openstreetmap"),
|
|
1590
|
+
/** What the map looks up (Google): default, the address. */
|
|
1591
|
+
query: Line(200).optional(),
|
|
1592
|
+
lat: z.number().min(-90).max(90).optional(),
|
|
1593
|
+
lng: z.number().min(-180).max(180).optional(),
|
|
1594
|
+
zoom: z.number().int().min(3).max(19).default(15),
|
|
1595
|
+
/** The map frame's accessible name. Default: "Map of <first address line>". */
|
|
1596
|
+
title: Line(120).optional(),
|
|
1597
|
+
/** A picture of the map, shown until the map loads (and instead of it without script). */
|
|
1598
|
+
image: Image.optional(),
|
|
1599
|
+
/** Phone, email and the like, under the address. */
|
|
1600
|
+
contact: z.array(Link).max(4).default([]),
|
|
1601
|
+
/** Buttons under the address. */
|
|
1602
|
+
primaryCta: Link.optional(),
|
|
1603
|
+
secondaryCta: Link.optional(),
|
|
1604
|
+
loadLabel: Line(40).default("Show the map"),
|
|
1605
|
+
/** Said before the map loads: who it comes from. */
|
|
1606
|
+
consentNote: Copy(200).optional(),
|
|
1607
|
+
surface: Surface.default("base"),
|
|
1608
|
+
anchor: Anchor
|
|
1609
|
+
}).refine((p) => p.provider !== "openstreetmap" || p.lat !== void 0 && p.lng !== void 0, {
|
|
1610
|
+
message: "An OpenStreetMap map needs the place's latitude and longitude.",
|
|
1611
|
+
path: ["lat"]
|
|
1612
|
+
});
|
|
1613
|
+
var round = (n) => Math.round(n * 1e6) / 1e6;
|
|
1614
|
+
function mapEmbedUrl(props) {
|
|
1615
|
+
if (props.provider === "google") {
|
|
1616
|
+
const q = props.query ?? (props.lat !== void 0 && props.lng !== void 0 ? `${round(props.lat)},${round(props.lng)}` : props.address.join(", "));
|
|
1617
|
+
return `${MAP_PROVIDERS.google.origin}/maps?q=${encodeURIComponent(q)}&z=${props.zoom}&output=embed`;
|
|
1618
|
+
}
|
|
1619
|
+
const lat = round(props.lat ?? 0);
|
|
1620
|
+
const lng = round(props.lng ?? 0);
|
|
1621
|
+
const span = 180 / 2 ** props.zoom;
|
|
1622
|
+
const bbox = [lng - span, lat - span / 2, lng + span, lat + span / 2].map(round).join(",");
|
|
1623
|
+
return `${MAP_PROVIDERS.openstreetmap.origin}/export/embed.html?bbox=${encodeURIComponent(bbox)}&layer=mapnik&marker=${encodeURIComponent(`${lat},${lng}`)}`;
|
|
1624
|
+
}
|
|
1625
|
+
function mapPageUrl(props) {
|
|
1626
|
+
if (props.provider === "google") {
|
|
1627
|
+
const q = props.query ?? (props.lat !== void 0 && props.lng !== void 0 ? `${round(props.lat)},${round(props.lng)}` : props.address.join(", "));
|
|
1628
|
+
return `${MAP_PROVIDERS.google.origin}/maps/search/?api=1&query=${encodeURIComponent(q)}`;
|
|
1629
|
+
}
|
|
1630
|
+
const lat = round(props.lat ?? 0);
|
|
1631
|
+
const lng = round(props.lng ?? 0);
|
|
1632
|
+
return `${MAP_PROVIDERS.openstreetmap.origin}/?mlat=${lat}&mlon=${lng}#map=${props.zoom}/${lat}/${lng}`;
|
|
1633
|
+
}
|
|
1634
|
+
var mapSection = defineSection({
|
|
1635
|
+
type: "map",
|
|
1636
|
+
version: 1,
|
|
1637
|
+
label: "Map",
|
|
1638
|
+
description: "Your address with a map that loads only when the visitor asks for it (OpenStreetMap or Google Maps), a picture until then, and a link to the provider.",
|
|
1639
|
+
category: "content",
|
|
1640
|
+
islands: true,
|
|
1641
|
+
frameOrigins: (props) => [MAP_PROVIDERS[props.provider].origin],
|
|
1642
|
+
schema: schema16,
|
|
1643
|
+
variants: [
|
|
1644
|
+
{ id: "split", label: "Address beside the map" },
|
|
1645
|
+
{ id: "stacked", label: "Map above the address" }
|
|
1646
|
+
],
|
|
1647
|
+
defaultVariant: "split",
|
|
1648
|
+
presets: [
|
|
1649
|
+
{
|
|
1650
|
+
name: "Find us",
|
|
1651
|
+
variant: "split",
|
|
1652
|
+
props: { heading: "Find us", address: ["1 River Road", "Riverside"], provider: "openstreetmap", lat: 51.5007, lng: -0.1246 }
|
|
1653
|
+
},
|
|
1654
|
+
{
|
|
1655
|
+
name: "Map and address",
|
|
1656
|
+
variant: "stacked",
|
|
1657
|
+
props: { heading: "Visit", address: ["1 River Road", "Riverside"], provider: "google", query: "1 River Road, Riverside" }
|
|
1658
|
+
}
|
|
1659
|
+
],
|
|
1660
|
+
render: ({ props, variant, node, ctx }) => {
|
|
1661
|
+
const inverted = onAccent(props.surface);
|
|
1662
|
+
const provider = MAP_PROVIDERS[props.provider];
|
|
1663
|
+
const frameTitle = props.title ?? `Map of ${props.address[0]}`;
|
|
1664
|
+
const note = props.consentNote ?? `The map comes from ${provider.name}, which may set cookies. It loads only if you ask for it.`;
|
|
1665
|
+
const map = /* @__PURE__ */ jsxs(
|
|
1666
|
+
"div",
|
|
1667
|
+
{
|
|
1668
|
+
"data-gs-island": "map",
|
|
1669
|
+
"data-gs-map-src": mapEmbedUrl(props),
|
|
1670
|
+
"data-gs-map-title": frameTitle,
|
|
1671
|
+
className: "relative isolate flex aspect-[4/3] w-full flex-col justify-end overflow-hidden rounded-xl border border-border bg-muted text-foreground",
|
|
1672
|
+
children: [
|
|
1673
|
+
props.image ? /* @__PURE__ */ jsx("span", { "data-gs-map-drop": "", className: "absolute inset-0 -z-10", children: /* @__PURE__ */ jsx(SiteImage, { image: props.image, ctx, sizes: "(min-width: 768px) 50vw, 100vw", className: "h-full w-full object-cover" }) }) : /* @__PURE__ */ jsx(MapPin, { "data-gs-map-drop": "", "aria-hidden": "true", className: "absolute left-1/2 top-1/3 -z-10 size-12 -translate-x-1/2 text-muted-foreground" }),
|
|
1674
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex flex-col items-start gap-3 bg-background p-4 text-foreground", children: [
|
|
1675
|
+
/* @__PURE__ */ jsx("p", { "data-gs-map-drop": "", className: "text-sm", children: note }),
|
|
1676
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-4", children: [
|
|
1677
|
+
/* @__PURE__ */ jsx("button", { type: "button", "data-gs-map-load": "", "data-gs-map-drop": "", hidden: true, className: cn(buttonVariants({ variant: "default", size: "lg" }), "min-h-11"), children: props.loadLabel }),
|
|
1678
|
+
/* @__PURE__ */ jsx(TextLink, { link: { label: `Open in ${provider.name}`, href: mapPageUrl(props), external: true, target: "_blank" }, ctx, className: "text-sm font-medium underline" })
|
|
1679
|
+
] })
|
|
1680
|
+
] })
|
|
1681
|
+
]
|
|
1682
|
+
}
|
|
1683
|
+
);
|
|
1684
|
+
const copy = /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
1685
|
+
props.eyebrow ? /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold uppercase tracking-wider", children: props.eyebrow }) : null,
|
|
1686
|
+
/* @__PURE__ */ jsx(SectionTitle, { ctx, id: titleId(ctx), size: "lg", children: props.heading }),
|
|
1687
|
+
props.body ? /* @__PURE__ */ jsx("p", { className: inverted ? void 0 : "text-muted-foreground", children: props.body }) : null,
|
|
1688
|
+
/* @__PURE__ */ jsx("address", { className: "flex flex-col not-italic", children: props.address.map((line, i) => /* @__PURE__ */ jsx("span", { className: i === 0 ? "font-semibold" : void 0, children: line }, i)) }),
|
|
1689
|
+
props.contact.length ? /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-1", children: props.contact.map((link, i) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(TextLink, { link, ctx, className: "underline" }) }, i)) }) : null,
|
|
1690
|
+
/* @__PURE__ */ jsx(Actions, { primary: props.primaryCta, secondary: props.secondaryCta, ctx, onAccent: inverted })
|
|
1691
|
+
] });
|
|
1692
|
+
if (variant === "stacked") {
|
|
1693
|
+
return /* @__PURE__ */ jsx(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, width: "md", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-8", children: [
|
|
1694
|
+
copy,
|
|
1695
|
+
map
|
|
1696
|
+
] }) });
|
|
1697
|
+
}
|
|
1698
|
+
return /* @__PURE__ */ jsx(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: /* @__PURE__ */ jsxs("div", { className: "grid items-center gap-10 md:grid-cols-2 md:gap-12", children: [
|
|
1699
|
+
copy,
|
|
1700
|
+
map
|
|
1701
|
+
] }) });
|
|
1702
|
+
}
|
|
1703
|
+
});
|
|
1704
|
+
var schema17 = z.object({
|
|
1705
|
+
showCover: z.boolean().default(true),
|
|
1706
|
+
showAuthor: z.boolean().default(true),
|
|
1707
|
+
showDate: z.boolean().default(true),
|
|
1708
|
+
/** The link back to the blog's index above the title. Empty for none. */
|
|
1709
|
+
backLabel: Line(40).optional().default("All posts"),
|
|
1710
|
+
surface: Surface.default("base")
|
|
1711
|
+
});
|
|
1712
|
+
var postBodySection = defineSection({
|
|
1713
|
+
type: "post-body",
|
|
1714
|
+
version: 1,
|
|
1715
|
+
label: "Post",
|
|
1716
|
+
description: "Where each blog post goes on the post page: its title, date, author, cover and text.",
|
|
1717
|
+
category: "content",
|
|
1718
|
+
limit: 1,
|
|
1719
|
+
providesH1: true,
|
|
1720
|
+
enabledOn: ["post"],
|
|
1721
|
+
schema: schema17,
|
|
1722
|
+
variants: [
|
|
1723
|
+
{ id: "article", label: "Article" },
|
|
1724
|
+
{ id: "cover", label: "Title over the cover" }
|
|
1725
|
+
],
|
|
1726
|
+
defaultVariant: "article",
|
|
1727
|
+
presets: [
|
|
1728
|
+
{ name: "Article", variant: "article", props: {} },
|
|
1729
|
+
{ name: "Title over the cover", variant: "cover", props: { surface: "base" } }
|
|
1730
|
+
],
|
|
1731
|
+
render: ({ props, variant, node, ctx }) => {
|
|
1732
|
+
const post = ctx.posts?.current;
|
|
1733
|
+
if (!post || !ctx.posts) return null;
|
|
1734
|
+
const d = post.doc;
|
|
1735
|
+
const date = props.showDate ? formatPostDate(post.publishedAt, ctx.site.lang) : "";
|
|
1736
|
+
const author = props.showAuthor ? d.author?.name : void 0;
|
|
1737
|
+
const inverted = onAccent(props.surface);
|
|
1738
|
+
const back = props.backLabel ? /* @__PURE__ */ jsxs(
|
|
1739
|
+
"a",
|
|
1740
|
+
{
|
|
1741
|
+
href: ctx.href(ctx.posts.path),
|
|
1742
|
+
className: "inline-flex min-h-11 items-center gap-2 rounded-sm text-sm font-medium underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
|
|
1743
|
+
children: [
|
|
1744
|
+
/* @__PURE__ */ jsx(ArrowLeft, { "aria-hidden": "true", className: "size-4" }),
|
|
1745
|
+
props.backLabel
|
|
1746
|
+
]
|
|
1747
|
+
}
|
|
1748
|
+
) : null;
|
|
1749
|
+
const meta = date || author ? /* @__PURE__ */ jsxs("p", { className: cn("flex flex-wrap gap-x-3 text-sm", !inverted && variant !== "cover" && "text-muted-foreground"), children: [
|
|
1750
|
+
author ? /* @__PURE__ */ jsxs("span", { children: [
|
|
1751
|
+
"By ",
|
|
1752
|
+
author
|
|
1753
|
+
] }) : null,
|
|
1754
|
+
date ? /* @__PURE__ */ jsx("time", { dateTime: post.publishedAt, children: date }) : null
|
|
1755
|
+
] }) : null;
|
|
1756
|
+
const title = /* @__PURE__ */ jsx(SectionTitle, { ctx, id: titleId(ctx), size: "lg", children: d.title });
|
|
1757
|
+
const body = /* @__PURE__ */ jsx(
|
|
1758
|
+
RichText$1,
|
|
1759
|
+
{
|
|
1760
|
+
value: d.body,
|
|
1761
|
+
headingLevels: ctx.headingLevel === 1 ? [2, 3, 4] : [3, 4],
|
|
1762
|
+
className: cn(richTextClassName, "text-base sm:text-lg [&_h2]:font-display [&_h2]:text-2xl [&_h3]:font-display [&_h3]:text-xl")
|
|
1763
|
+
}
|
|
1764
|
+
);
|
|
1765
|
+
if (variant === "cover" && d.cover && props.showCover) {
|
|
1766
|
+
return /* @__PURE__ */ jsxs("article", { "aria-labelledby": titleId(ctx), "data-gs-section": node.type, "data-gs-variant": node.variant, children: [
|
|
1767
|
+
/* @__PURE__ */ jsxs(SectionBand, { node, ctx, surface: "inverse", spacing: "lg", labelled: false, className: "isolate overflow-hidden", children: [
|
|
1768
|
+
/* @__PURE__ */ jsx(SiteImage, { image: { ...d.cover, decorative: true }, ctx, priority: true, sizes: "100vw", className: "absolute inset-0 -z-10 h-full w-full object-cover" }),
|
|
1769
|
+
/* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "absolute inset-0 -z-10 bg-foreground/80" }),
|
|
1770
|
+
/* @__PURE__ */ jsxs("div", { className: "mx-auto flex max-w-3xl flex-col gap-4", children: [
|
|
1771
|
+
back,
|
|
1772
|
+
d.category ? /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold uppercase tracking-wider", children: d.category }) : null,
|
|
1773
|
+
title,
|
|
1774
|
+
meta
|
|
1775
|
+
] })
|
|
1776
|
+
] }),
|
|
1777
|
+
/* @__PURE__ */ jsx(SectionBand, { node, ctx, surface: props.surface, width: "prose", labelled: false, children: body })
|
|
1778
|
+
] });
|
|
1779
|
+
}
|
|
1780
|
+
return /* @__PURE__ */ jsx(SectionBand, { node, ctx, surface: props.surface, width: "prose", labelled: false, children: /* @__PURE__ */ jsxs("article", { "aria-labelledby": titleId(ctx), className: "flex flex-col gap-6", children: [
|
|
1781
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
1782
|
+
back,
|
|
1783
|
+
d.category ? /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold uppercase tracking-wider", children: d.category }) : null,
|
|
1784
|
+
title,
|
|
1785
|
+
meta
|
|
1786
|
+
] }),
|
|
1787
|
+
props.showCover && d.cover ? /* @__PURE__ */ jsx(SiteImage, { image: d.cover, ctx, priority: true, sizes: "(min-width: 768px) 48rem, 100vw", className: "aspect-[16/9] w-full rounded-xl object-cover" }) : null,
|
|
1788
|
+
body
|
|
1789
|
+
] }) });
|
|
1790
|
+
}
|
|
1791
|
+
});
|
|
1792
|
+
var WEEKDAYS = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"];
|
|
1793
|
+
var Day = z.enum(WEEKDAYS);
|
|
1794
|
+
var DAY_NAMES = { mon: "Monday", tue: "Tuesday", wed: "Wednesday", thu: "Thursday", fri: "Friday", sat: "Saturday", sun: "Sunday" };
|
|
1795
|
+
var DAY_SHORT = { mon: "Mon", tue: "Tue", wed: "Wed", thu: "Thu", fri: "Fri", sat: "Sat", sun: "Sun" };
|
|
1796
|
+
var Time = z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "A time is HH:MM on the 24-hour clock (06:15, 18:00).");
|
|
1797
|
+
var TypeId = z.string().regex(/^[a-z0-9][a-z0-9-]{0,39}$/, "A class type id is lowercase letters, digits and hyphens.");
|
|
1798
|
+
var CHIP_TONES = ["primary", "inverse", "soft", "outline", "plain"];
|
|
1799
|
+
var ClassType = z.object({
|
|
1800
|
+
id: TypeId,
|
|
1801
|
+
name: Line(60),
|
|
1802
|
+
description: Copy(300).optional(),
|
|
1803
|
+
tone: z.enum(CHIP_TONES).default("soft")
|
|
1804
|
+
});
|
|
1805
|
+
var Session = z.object({
|
|
1806
|
+
day: Day,
|
|
1807
|
+
start: Time,
|
|
1808
|
+
end: Time.optional(),
|
|
1809
|
+
title: Line(60),
|
|
1810
|
+
/** A class type's id, for its chip colour and the legend. */
|
|
1811
|
+
type: TypeId.optional(),
|
|
1812
|
+
coach: Line(60).optional(),
|
|
1813
|
+
location: Line(60).optional(),
|
|
1814
|
+
note: Line(80).optional(),
|
|
1815
|
+
/** Book this class (an outbound link, say). */
|
|
1816
|
+
link: Link.optional()
|
|
1817
|
+
});
|
|
1818
|
+
var schema18 = z.object({
|
|
1819
|
+
eyebrow: Line(60).optional(),
|
|
1820
|
+
heading: Line(90),
|
|
1821
|
+
body: Copy(320).optional(),
|
|
1822
|
+
/** The days the timetable shows, in order. */
|
|
1823
|
+
days: z.array(Day).min(1).max(7).default([...WEEKDAYS]),
|
|
1824
|
+
/** Day names in the site's language (default English). */
|
|
1825
|
+
dayNames: z.partialRecord(Day, Line(20)).optional(),
|
|
1826
|
+
clock: z.enum(["12h", "24h"]).default("12h"),
|
|
1827
|
+
types: z.array(ClassType).max(16).default([]),
|
|
1828
|
+
sessions: z.array(Session).max(200),
|
|
1829
|
+
/** What an empty slot or day says. */
|
|
1830
|
+
emptyText: Line(60).default("No classes"),
|
|
1831
|
+
footnote: Copy(300).optional(),
|
|
1832
|
+
surface: Surface.default("base"),
|
|
1833
|
+
anchor: Anchor
|
|
1834
|
+
});
|
|
1835
|
+
var TONE2 = {
|
|
1836
|
+
primary: "bg-primary text-primary-foreground",
|
|
1837
|
+
inverse: "bg-foreground text-background",
|
|
1838
|
+
soft: "bg-muted text-foreground",
|
|
1839
|
+
outline: "border-2 border-dashed border-primary text-foreground",
|
|
1840
|
+
plain: "border border-solid border-border text-foreground"
|
|
1841
|
+
};
|
|
1842
|
+
function formatTime(time, clock) {
|
|
1843
|
+
if (clock === "24h") return time;
|
|
1844
|
+
const [h, m] = time.split(":").map(Number);
|
|
1845
|
+
const suffix = h < 12 ? "am" : "pm";
|
|
1846
|
+
const hour = h % 12 === 0 ? 12 : h % 12;
|
|
1847
|
+
return `${hour}:${String(m).padStart(2, "0")} ${suffix}`;
|
|
1848
|
+
}
|
|
1849
|
+
function TimeText({ start, end, clock }) {
|
|
1850
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1851
|
+
/* @__PURE__ */ jsx("time", { dateTime: start, children: formatTime(start, clock) }),
|
|
1852
|
+
end ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1853
|
+
" to ",
|
|
1854
|
+
/* @__PURE__ */ jsx("time", { dateTime: end, children: formatTime(end, clock) })
|
|
1855
|
+
] }) : null
|
|
1856
|
+
] });
|
|
1857
|
+
}
|
|
1858
|
+
function Chip({ session, types }) {
|
|
1859
|
+
const type = session.type ? types.get(session.type) : void 0;
|
|
1860
|
+
const tone = type?.tone ?? "soft";
|
|
1861
|
+
const named = type && type.name.trim().toLowerCase() !== session.title.trim().toLowerCase();
|
|
1862
|
+
return /* @__PURE__ */ jsxs("span", { "data-type": type?.id, className: cn("inline-flex flex-col rounded-md px-2 py-1 text-sm font-semibold leading-tight", TONE2[tone]), children: [
|
|
1863
|
+
session.title,
|
|
1864
|
+
named ? /* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: type.name }) : null
|
|
1865
|
+
] });
|
|
1866
|
+
}
|
|
1867
|
+
function Details({ session, muted }) {
|
|
1868
|
+
const parts = [session.coach ? `with ${session.coach}` : null, session.location, session.note].filter(Boolean);
|
|
1869
|
+
if (!parts.length) return null;
|
|
1870
|
+
return /* @__PURE__ */ jsx("span", { className: cn("block text-xs", muted && "text-muted-foreground"), children: parts.join(" \xB7 ") });
|
|
1871
|
+
}
|
|
1872
|
+
function SessionItem({ session, props, types, ctx, withTime }) {
|
|
1873
|
+
return /* @__PURE__ */ jsxs("li", { className: "flex flex-col gap-1", children: [
|
|
1874
|
+
withTime ? /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tabular-nums", children: /* @__PURE__ */ jsx(TimeText, { start: session.start, end: session.end, clock: props.clock }) }) : null,
|
|
1875
|
+
/* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(Chip, { session, types }) }),
|
|
1876
|
+
/* @__PURE__ */ jsx(Details, { session, muted: true }),
|
|
1877
|
+
session.link ? /* @__PURE__ */ jsx(TextLink, { link: session.link, ctx, className: "text-sm font-medium underline" }) : null
|
|
1878
|
+
] });
|
|
1879
|
+
}
|
|
1880
|
+
var byTime = (a, b) => a.start.localeCompare(b.start) || a.title.localeCompare(b.title);
|
|
1881
|
+
function DayList({ day, props, types, ctx, id }) {
|
|
1882
|
+
const sessions = props.sessions.filter((s) => s.day === day).sort(byTime);
|
|
1883
|
+
return /* @__PURE__ */ jsxs("section", { "aria-labelledby": id, className: "flex flex-col gap-3", children: [
|
|
1884
|
+
/* @__PURE__ */ jsx(ItemTitle, { ctx, className: "text-base", children: /* @__PURE__ */ jsx("span", { id, children: props.dayNames?.[day] ?? DAY_NAMES[day] }) }),
|
|
1885
|
+
sessions.length ? /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-4", children: sessions.map((s, i) => /* @__PURE__ */ jsx(SessionItem, { session: s, props, types, ctx, withTime: true }, i)) }) : /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: props.emptyText })
|
|
1886
|
+
] });
|
|
1887
|
+
}
|
|
1888
|
+
function DayLabel({ day, props }) {
|
|
1889
|
+
const custom = props.dayNames?.[day];
|
|
1890
|
+
if (custom) return /* @__PURE__ */ jsx(Fragment, { children: custom });
|
|
1891
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1892
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: DAY_SHORT[day] }),
|
|
1893
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: DAY_NAMES[day] })
|
|
1894
|
+
] });
|
|
1895
|
+
}
|
|
1896
|
+
function Legend({ props, days }) {
|
|
1897
|
+
const used = new Set(props.sessions.filter((s) => s.type && days.includes(s.day)).map((s) => s.type));
|
|
1898
|
+
const shown = props.types.filter((t) => used.has(t.id));
|
|
1899
|
+
if (!shown.length) return null;
|
|
1900
|
+
return (
|
|
1901
|
+
// A list, not a <dl>: a type with no description has nothing to pair with.
|
|
1902
|
+
/* @__PURE__ */ jsx("ul", { "aria-label": "Class types", className: "mt-8 grid gap-4 sm:grid-cols-2", children: shown.map((t) => /* @__PURE__ */ jsxs("li", { className: "flex flex-col items-start gap-1", children: [
|
|
1903
|
+
/* @__PURE__ */ jsx("span", { className: cn("inline-flex rounded-md px-2 py-1 text-sm font-semibold", TONE2[t.tone]), children: t.name }),
|
|
1904
|
+
t.description ? /* @__PURE__ */ jsx("span", { className: "text-sm", children: t.description }) : null
|
|
1905
|
+
] }, t.id)) })
|
|
1906
|
+
);
|
|
1907
|
+
}
|
|
1908
|
+
var scheduleSection = defineSection({
|
|
1909
|
+
type: "schedule",
|
|
1910
|
+
version: 1,
|
|
1911
|
+
label: "Timetable",
|
|
1912
|
+
description: "The week's classes by time and day, with coach and place: a table on wider screens and a day picker on a phone.",
|
|
1913
|
+
category: "content",
|
|
1914
|
+
schema: schema18,
|
|
1915
|
+
variants: [
|
|
1916
|
+
{ id: "grid", label: "Week grid (day picker on a phone)" },
|
|
1917
|
+
{ id: "agenda", label: "Day by day" }
|
|
1918
|
+
],
|
|
1919
|
+
defaultVariant: "grid",
|
|
1920
|
+
presets: [
|
|
1921
|
+
{
|
|
1922
|
+
name: "Weekly timetable",
|
|
1923
|
+
variant: "grid",
|
|
1924
|
+
props: {
|
|
1925
|
+
heading: "Class times",
|
|
1926
|
+
types: [
|
|
1927
|
+
{ id: "strength", name: "Strength", description: "Barbell work in a coached group.", tone: "primary" },
|
|
1928
|
+
{ id: "open", name: "Open gym", description: "Train on your own; a coach is on the floor.", tone: "soft" }
|
|
1929
|
+
],
|
|
1930
|
+
sessions: [
|
|
1931
|
+
{ day: "mon", start: "06:00", title: "Strength", type: "strength", coach: "Coach name" },
|
|
1932
|
+
{ day: "wed", start: "06:00", title: "Strength", type: "strength", coach: "Coach name" },
|
|
1933
|
+
{ day: "sat", start: "09:00", title: "Open gym", type: "open" }
|
|
1934
|
+
]
|
|
1935
|
+
}
|
|
1936
|
+
},
|
|
1937
|
+
{
|
|
1938
|
+
name: "Day by day",
|
|
1939
|
+
variant: "agenda",
|
|
1940
|
+
props: { heading: "This week", days: ["mon", "tue", "wed", "thu", "fri"], sessions: [{ day: "mon", start: "18:00", end: "19:00", title: "Class name" }] }
|
|
1941
|
+
}
|
|
1942
|
+
],
|
|
1943
|
+
render: ({ props, variant, node, ctx }) => {
|
|
1944
|
+
const inverted = onAccent(props.surface);
|
|
1945
|
+
const types = new Map(props.types.map((t) => [t.id, t]));
|
|
1946
|
+
const days = props.days.filter((d, i, all) => all.indexOf(d) === i);
|
|
1947
|
+
const intro = /* @__PURE__ */ jsx(SectionIntro, { ctx, eyebrow: props.eyebrow, heading: props.heading, body: props.body, tone: inverted ? "inherit" : "muted" });
|
|
1948
|
+
const footer = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1949
|
+
/* @__PURE__ */ jsx(Legend, { props, days }),
|
|
1950
|
+
props.footnote ? /* @__PURE__ */ jsx("p", { className: "mt-6 text-sm", children: props.footnote }) : null
|
|
1951
|
+
] });
|
|
1952
|
+
const card = "rounded-xl border border-border bg-background p-4 text-foreground sm:p-6";
|
|
1953
|
+
if (variant === "agenda") {
|
|
1954
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: [
|
|
1955
|
+
intro,
|
|
1956
|
+
/* @__PURE__ */ jsx("div", { className: cn("mt-10 grid gap-6 sm:grid-cols-2 lg:grid-cols-4", card), children: days.map((day) => /* @__PURE__ */ jsx(DayList, { day, props, types, ctx, id: `${ctx.idPrefix}-${day}` }, day)) }),
|
|
1957
|
+
footer
|
|
1958
|
+
] });
|
|
1959
|
+
}
|
|
1960
|
+
const times = [...new Set(props.sessions.filter((s) => days.includes(s.day)).map((s) => s.start))].sort();
|
|
1961
|
+
const caption = `${props.heading}: classes by time and day`;
|
|
1962
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: [
|
|
1963
|
+
intro,
|
|
1964
|
+
/* @__PURE__ */ jsxs("div", { role: "region", "aria-label": caption, tabIndex: 0, className: cn("mt-10 overflow-x-auto max-md:hidden", card), children: [
|
|
1965
|
+
/* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-left text-sm", children: [
|
|
1966
|
+
/* @__PURE__ */ jsx("caption", { className: "sr-only", children: caption }),
|
|
1967
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
1968
|
+
/* @__PURE__ */ jsx("th", { scope: "col", className: "w-24 border-b border-border p-2 font-semibold", children: "Time" }),
|
|
1969
|
+
days.map((day) => /* @__PURE__ */ jsx("th", { scope: "col", className: "border-b border-border p-2 font-semibold", children: /* @__PURE__ */ jsx(DayLabel, { day, props }) }, day))
|
|
1970
|
+
] }) }),
|
|
1971
|
+
/* @__PURE__ */ jsx("tbody", { children: times.map((time) => /* @__PURE__ */ jsxs("tr", { className: "border-b border-border last:border-b-0", children: [
|
|
1972
|
+
/* @__PURE__ */ jsx("th", { scope: "row", className: "whitespace-nowrap p-2 align-top font-semibold tabular-nums", children: /* @__PURE__ */ jsx("time", { dateTime: time, children: formatTime(time, props.clock) }) }),
|
|
1973
|
+
days.map((day) => {
|
|
1974
|
+
const here = props.sessions.filter((s) => s.day === day && s.start === time).sort(byTime);
|
|
1975
|
+
return /* @__PURE__ */ jsx("td", { className: "p-2 align-top", children: here.length ? /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-2", children: here.map((s, i) => /* @__PURE__ */ jsx(SessionItem, { session: s, props, types, ctx, withTime: false }, i)) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1976
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "text-muted-foreground", children: "\xB7" }),
|
|
1977
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: props.emptyText })
|
|
1978
|
+
] }) }, day);
|
|
1979
|
+
})
|
|
1980
|
+
] }, time)) })
|
|
1981
|
+
] }),
|
|
1982
|
+
times.length === 0 ? /* @__PURE__ */ jsx("p", { className: "p-2 text-sm text-muted-foreground", children: props.emptyText }) : null
|
|
1983
|
+
] }),
|
|
1984
|
+
/* @__PURE__ */ jsxs("div", { "data-gs-schedule": "", className: cn("mt-8 md:hidden", card), children: [
|
|
1985
|
+
/* @__PURE__ */ jsxs("fieldset", { className: "min-w-0", children: [
|
|
1986
|
+
/* @__PURE__ */ jsx("legend", { className: "sr-only", children: "Show the classes on" }),
|
|
1987
|
+
/* @__PURE__ */ jsx("div", { className: "-mx-1 flex gap-1 overflow-x-auto px-1 pb-2", children: days.map((day, i) => /* @__PURE__ */ jsxs("label", { className: "relative shrink-0", children: [
|
|
1988
|
+
/* @__PURE__ */ jsx("input", { type: "radio", name: `${ctx.idPrefix}-day`, value: day, "data-day": i, defaultChecked: i === 0, "aria-controls": `${ctx.idPrefix}-panel-${day}`, className: "peer sr-only" }),
|
|
1989
|
+
/* @__PURE__ */ jsx("span", { className: "inline-flex min-h-11 min-w-11 cursor-pointer items-center justify-center rounded-md border border-border px-3 text-sm font-semibold peer-checked:border-primary peer-checked:bg-primary peer-checked:text-primary-foreground peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-ring", children: /* @__PURE__ */ jsx(DayLabel, { day, props }) })
|
|
1990
|
+
] }, day)) })
|
|
1991
|
+
] }),
|
|
1992
|
+
/* @__PURE__ */ jsx("div", { className: "mt-4 flex flex-col gap-8", children: days.map((day, i) => /* @__PURE__ */ jsx("div", { id: `${ctx.idPrefix}-panel-${day}`, "data-panel": i, children: /* @__PURE__ */ jsx(DayList, { day, props, types, ctx, id: `${ctx.idPrefix}-${day}` }) }, day)) })
|
|
1993
|
+
] }),
|
|
1994
|
+
footer
|
|
1995
|
+
] });
|
|
1996
|
+
}
|
|
1997
|
+
});
|
|
1998
|
+
var Member = z.object({
|
|
1999
|
+
name: Line(80),
|
|
2000
|
+
role: Line(80).optional(),
|
|
2001
|
+
/** Qualifications, as one line ("Level 2 coach, nutrition coach"). */
|
|
2002
|
+
credentials: Copy(300).optional(),
|
|
2003
|
+
photo: Image.optional(),
|
|
2004
|
+
/** A line or two, shown on the card. */
|
|
2005
|
+
summary: Copy(300).optional(),
|
|
2006
|
+
/** The full bio; a blank line starts a new paragraph. */
|
|
2007
|
+
bio: Copy(6e3).optional(),
|
|
2008
|
+
quote: z.object({ text: Copy(400).min(1), cite: Line(80).optional() }).optional(),
|
|
2009
|
+
/** Their own page (a site path). */
|
|
2010
|
+
profile: Href.optional(),
|
|
2011
|
+
cta: Link.optional(),
|
|
2012
|
+
/** An id for a link straight to this person (`#sam-lee`). */
|
|
2013
|
+
anchor: Anchor
|
|
2014
|
+
});
|
|
2015
|
+
var schema19 = z.object({
|
|
2016
|
+
eyebrow: Line(60).optional(),
|
|
2017
|
+
heading: Line(90),
|
|
2018
|
+
body: Copy(320).optional(),
|
|
2019
|
+
members: z.array(Member).min(1).max(40),
|
|
2020
|
+
columns: z.union([z.literal(2), z.literal(3), z.literal(4)]).default(3),
|
|
2021
|
+
surface: Surface.default("base"),
|
|
2022
|
+
anchor: Anchor
|
|
2023
|
+
});
|
|
2024
|
+
var COLUMNS2 = { 2: "sm:grid-cols-2", 3: "sm:grid-cols-2 lg:grid-cols-3", 4: "sm:grid-cols-2 lg:grid-cols-4" };
|
|
2025
|
+
function Paragraphs({ text, className }) {
|
|
2026
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col gap-3", className), children: text.split(/\n{2,}/).map((p, i) => /* @__PURE__ */ jsx("p", { className: "whitespace-pre-line", children: p }, i)) });
|
|
2027
|
+
}
|
|
2028
|
+
function Quote2({ quote, muted }) {
|
|
2029
|
+
return /* @__PURE__ */ jsxs("figure", { className: "border-l-4 border-primary pl-4", children: [
|
|
2030
|
+
/* @__PURE__ */ jsx("blockquote", { className: "italic", children: /* @__PURE__ */ jsx("p", { children: quote.text }) }),
|
|
2031
|
+
quote.cite ? /* @__PURE__ */ jsx("figcaption", { className: cn("mt-1 text-sm", muted && "text-muted-foreground"), children: quote.cite }) : null
|
|
2032
|
+
] });
|
|
2033
|
+
}
|
|
2034
|
+
function MemberLinks({ member, ctx }) {
|
|
2035
|
+
if (!member.cta && !member.profile) return null;
|
|
2036
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-4", children: [
|
|
2037
|
+
member.cta ? /* @__PURE__ */ jsx(ButtonLink, { link: member.cta, ctx, tone: "outline" }) : null,
|
|
2038
|
+
member.profile ? /* @__PURE__ */ jsx(TextLink, { link: { label: `${member.name}'s page`, href: member.profile }, ctx, className: "font-medium underline" }) : null
|
|
2039
|
+
] });
|
|
2040
|
+
}
|
|
2041
|
+
var teamSection = defineSection({
|
|
2042
|
+
type: "team",
|
|
2043
|
+
version: 1,
|
|
2044
|
+
label: "Team",
|
|
2045
|
+
description: "Your coaches or staff: a photo, a role, credentials and a long bio that opens in place, with a link to each person's page.",
|
|
2046
|
+
category: "content",
|
|
2047
|
+
schema: schema19,
|
|
2048
|
+
variants: [
|
|
2049
|
+
{ id: "cards", label: "Cards (bio opens in place)" },
|
|
2050
|
+
{ id: "list", label: "Long-form list" }
|
|
2051
|
+
],
|
|
2052
|
+
defaultVariant: "cards",
|
|
2053
|
+
presets: [
|
|
2054
|
+
{
|
|
2055
|
+
name: "Team cards",
|
|
2056
|
+
variant: "cards",
|
|
2057
|
+
props: {
|
|
2058
|
+
heading: "Meet the coaches",
|
|
2059
|
+
members: [
|
|
2060
|
+
{ name: "Coach name", role: "Head coach", summary: "A line about what they love to coach.", bio: "Their story, in a few short paragraphs.\n\nWhat they love about coaching." },
|
|
2061
|
+
{ name: "Coach name", role: "Coach", summary: "A line about what they love to coach." }
|
|
2062
|
+
]
|
|
2063
|
+
}
|
|
2064
|
+
},
|
|
2065
|
+
{
|
|
2066
|
+
name: "Team list",
|
|
2067
|
+
variant: "list",
|
|
2068
|
+
props: { heading: "The team", members: [{ name: "Coach name", credentials: "Their qualifications", bio: "Their story, in a few short paragraphs." }] }
|
|
2069
|
+
}
|
|
2070
|
+
],
|
|
2071
|
+
render: ({ props, variant, node, ctx }) => {
|
|
2072
|
+
const inverted = onAccent(props.surface);
|
|
2073
|
+
const muted = !inverted;
|
|
2074
|
+
const intro = /* @__PURE__ */ jsx(SectionIntro, { ctx, eyebrow: props.eyebrow, heading: props.heading, body: props.body, tone: inverted ? "inherit" : "muted" });
|
|
2075
|
+
if (variant === "list") {
|
|
2076
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: [
|
|
2077
|
+
intro,
|
|
2078
|
+
/* @__PURE__ */ jsx("ul", { className: "mt-12 flex flex-col gap-16", children: props.members.map((m, i) => /* @__PURE__ */ jsxs("li", { ...m.anchor ? { id: m.anchor } : {}, className: cn("grid gap-6 md:grid-cols-[minmax(0,16rem)_1fr] md:gap-10", m.anchor && "scroll-mt-20"), children: [
|
|
2079
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
2080
|
+
m.photo ? /* @__PURE__ */ jsx(SiteImage, { image: m.photo, ctx, sizes: "(min-width: 768px) 16rem, 100vw", className: "aspect-square w-full max-w-64 rounded-xl object-cover" }) : null,
|
|
2081
|
+
/* @__PURE__ */ jsx(ItemTitle, { ctx, className: "text-2xl", children: m.name }),
|
|
2082
|
+
m.role ? /* @__PURE__ */ jsx("p", { className: "font-medium", children: m.role }) : null,
|
|
2083
|
+
m.credentials ? /* @__PURE__ */ jsx("p", { className: cn("text-sm font-semibold uppercase tracking-wide", muted && "text-muted-foreground"), children: m.credentials }) : null
|
|
2084
|
+
] }),
|
|
2085
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5", children: [
|
|
2086
|
+
m.bio ? /* @__PURE__ */ jsx(Paragraphs, { text: m.bio }) : m.summary ? /* @__PURE__ */ jsx("p", { children: m.summary }) : null,
|
|
2087
|
+
m.quote ? /* @__PURE__ */ jsx(Quote2, { quote: m.quote, muted }) : null,
|
|
2088
|
+
/* @__PURE__ */ jsx(MemberLinks, { member: m, ctx })
|
|
2089
|
+
] })
|
|
2090
|
+
] }, i)) })
|
|
2091
|
+
] });
|
|
2092
|
+
}
|
|
2093
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: [
|
|
2094
|
+
intro,
|
|
2095
|
+
/* @__PURE__ */ jsx("ul", { className: cn("mt-12 grid gap-6", COLUMNS2[props.columns]), children: props.members.map((m, i) => /* @__PURE__ */ jsxs("li", { ...m.anchor ? { id: m.anchor } : {}, className: cn("flex flex-col gap-4 rounded-xl border border-border bg-background p-6 text-foreground", m.anchor && "scroll-mt-20"), children: [
|
|
2096
|
+
m.photo ? /* @__PURE__ */ jsx(SiteImage, { image: m.photo, ctx, sizes: "(min-width: 1024px) 25vw, (min-width: 640px) 50vw, 100vw", className: "aspect-square w-full rounded-lg object-cover" }) : null,
|
|
2097
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
2098
|
+
/* @__PURE__ */ jsx(ItemTitle, { ctx, children: m.name }),
|
|
2099
|
+
m.role ? /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: m.role }) : null,
|
|
2100
|
+
m.credentials ? /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: m.credentials }) : null
|
|
2101
|
+
] }),
|
|
2102
|
+
m.summary ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: m.summary }) : null,
|
|
2103
|
+
m.bio || m.quote ? (
|
|
2104
|
+
// The full bio, in place: a disclosure that needs no script.
|
|
2105
|
+
/* @__PURE__ */ jsxs("details", { className: "group", children: [
|
|
2106
|
+
/* @__PURE__ */ jsx("summary", { className: "cursor-pointer list-none rounded-sm font-medium underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring [&::-webkit-details-marker]:hidden", children: /* @__PURE__ */ jsxs("span", { className: "flex min-h-11 items-center justify-between gap-2", children: [
|
|
2107
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
2108
|
+
"More about ",
|
|
2109
|
+
m.name
|
|
2110
|
+
] }),
|
|
2111
|
+
/* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": "true", className: "size-5 shrink-0 transition-transform group-open:rotate-180 motion-reduce:transition-none" })
|
|
2112
|
+
] }) }),
|
|
2113
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 pt-3", children: [
|
|
2114
|
+
m.bio ? /* @__PURE__ */ jsx(Paragraphs, { text: m.bio }) : null,
|
|
2115
|
+
m.quote ? /* @__PURE__ */ jsx(Quote2, { quote: m.quote, muted: true }) : null
|
|
2116
|
+
] })
|
|
2117
|
+
] })
|
|
2118
|
+
) : null,
|
|
2119
|
+
/* @__PURE__ */ jsx("div", { className: "mt-auto", children: /* @__PURE__ */ jsx(MemberLinks, { member: m, ctx }) })
|
|
2120
|
+
] }, i)) })
|
|
2121
|
+
] });
|
|
2122
|
+
}
|
|
2123
|
+
});
|
|
2124
|
+
var Step = z.object({
|
|
2125
|
+
/** When or which: "Oct 4", "Week 1", "Day 3". */
|
|
2126
|
+
label: Line(40).optional(),
|
|
2127
|
+
title: Line(90),
|
|
2128
|
+
body: Copy(600).optional(),
|
|
2129
|
+
image: Image.optional(),
|
|
2130
|
+
cta: Link.optional()
|
|
2131
|
+
});
|
|
2132
|
+
var schema20 = z.object({
|
|
2133
|
+
eyebrow: Line(60).optional(),
|
|
2134
|
+
heading: Line(90),
|
|
2135
|
+
body: Copy(320).optional(),
|
|
2136
|
+
steps: z.array(Step).min(1).max(30),
|
|
2137
|
+
/** Show each step's number (on screen; a screen reader counts the list itself). */
|
|
2138
|
+
numbered: z.boolean().default(true),
|
|
2139
|
+
surface: Surface.default("base"),
|
|
2140
|
+
anchor: Anchor
|
|
2141
|
+
});
|
|
2142
|
+
var number = (i) => String(i + 1).padStart(2, "0");
|
|
2143
|
+
var timelineSection = defineSection({
|
|
2144
|
+
type: "timeline",
|
|
2145
|
+
version: 1,
|
|
2146
|
+
label: "Timeline",
|
|
2147
|
+
description: "A programme in order (a six-week course, an agenda): each step with a date or label, a title, copy and an optional button.",
|
|
2148
|
+
category: "content",
|
|
2149
|
+
schema: schema20,
|
|
2150
|
+
variants: [
|
|
2151
|
+
{ id: "vertical", label: "Down a line" },
|
|
2152
|
+
{ id: "cards", label: "Cards in a grid" }
|
|
2153
|
+
],
|
|
2154
|
+
defaultVariant: "vertical",
|
|
2155
|
+
presets: [
|
|
2156
|
+
{
|
|
2157
|
+
name: "Week by week",
|
|
2158
|
+
variant: "vertical",
|
|
2159
|
+
props: {
|
|
2160
|
+
heading: "Week by week",
|
|
2161
|
+
steps: [
|
|
2162
|
+
{ label: "Week 1", title: "Start", body: "What happens in the first week." },
|
|
2163
|
+
{ label: "Week 2", title: "Build", body: "What comes next." },
|
|
2164
|
+
{ label: "Week 3", title: "Test", body: "How it ends." }
|
|
2165
|
+
]
|
|
2166
|
+
}
|
|
2167
|
+
},
|
|
2168
|
+
{ name: "Steps as cards", variant: "cards", props: { heading: "How it works", steps: [{ title: "First step" }, { title: "Second step" }, { title: "Third step" }] } }
|
|
2169
|
+
],
|
|
2170
|
+
render: ({ props, variant, node, ctx }) => {
|
|
2171
|
+
const inverted = onAccent(props.surface);
|
|
2172
|
+
const muted = !inverted;
|
|
2173
|
+
const intro = /* @__PURE__ */ jsx(SectionIntro, { ctx, eyebrow: props.eyebrow, heading: props.heading, body: props.body, tone: inverted ? "inherit" : "muted" });
|
|
2174
|
+
if (variant === "cards") {
|
|
2175
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, children: [
|
|
2176
|
+
intro,
|
|
2177
|
+
/* @__PURE__ */ jsx("ol", { className: "mt-12 grid gap-6 sm:grid-cols-2 lg:grid-cols-3", children: props.steps.map((step, i) => /* @__PURE__ */ jsxs("li", { className: "flex flex-col gap-3 rounded-xl border border-border bg-background p-6 text-foreground", children: [
|
|
2178
|
+
step.image ? /* @__PURE__ */ jsx(SiteImage, { image: step.image, ctx, sizes: "(min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw", className: "aspect-video w-full rounded-lg object-cover" }) : null,
|
|
2179
|
+
/* @__PURE__ */ jsxs("p", { className: "flex items-baseline gap-3 text-sm font-semibold", children: [
|
|
2180
|
+
props.numbered ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "font-display text-2xl", children: number(i) }) : null,
|
|
2181
|
+
step.label ? /* @__PURE__ */ jsx("span", { className: "uppercase tracking-wide text-muted-foreground", children: step.label }) : null
|
|
2182
|
+
] }),
|
|
2183
|
+
/* @__PURE__ */ jsx(ItemTitle, { ctx, children: step.title }),
|
|
2184
|
+
step.body ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: step.body }) : null,
|
|
2185
|
+
step.cta ? /* @__PURE__ */ jsx(ButtonLink, { link: step.cta, ctx, tone: "outline", className: "mt-auto self-start" }) : null
|
|
2186
|
+
] }, i)) })
|
|
2187
|
+
] });
|
|
2188
|
+
}
|
|
2189
|
+
return /* @__PURE__ */ jsxs(SectionBand, { node, ctx, anchor: props.anchor, surface: props.surface, width: "md", children: [
|
|
2190
|
+
intro,
|
|
2191
|
+
/* @__PURE__ */ jsx("ol", { className: "mx-auto mt-12 flex max-w-3xl flex-col", children: props.steps.map((step, i) => /* @__PURE__ */ jsxs("li", { className: "relative grid grid-cols-[2.75rem_1fr] gap-x-4 pb-10 last:pb-0", children: [
|
|
2192
|
+
i < props.steps.length - 1 ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "absolute left-[1.375rem] top-11 bottom-0 w-px -translate-x-1/2 bg-border" }) : null,
|
|
2193
|
+
/* @__PURE__ */ jsx(
|
|
2194
|
+
"span",
|
|
2195
|
+
{
|
|
2196
|
+
"aria-hidden": "true",
|
|
2197
|
+
"data-marker": "",
|
|
2198
|
+
className: cn("z-10 inline-flex size-11 items-center justify-center rounded-full text-sm font-bold", inverted ? "bg-background text-foreground" : "bg-primary text-primary-foreground"),
|
|
2199
|
+
children: props.numbered ? number(i) : /* @__PURE__ */ jsx("span", { className: "size-3 rounded-full bg-current" })
|
|
2200
|
+
}
|
|
2201
|
+
),
|
|
2202
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 pt-2", children: [
|
|
2203
|
+
step.label ? /* @__PURE__ */ jsx("p", { className: cn("text-sm font-semibold uppercase tracking-wide", muted && "text-muted-foreground"), children: step.label }) : null,
|
|
2204
|
+
/* @__PURE__ */ jsx(ItemTitle, { ctx, children: step.title }),
|
|
2205
|
+
step.body ? /* @__PURE__ */ jsx("p", { className: muted ? "text-muted-foreground" : void 0, children: step.body }) : null,
|
|
2206
|
+
step.image ? /* @__PURE__ */ jsx(SiteImage, { image: step.image, ctx, sizes: "(min-width: 768px) 40rem, 100vw", className: "mt-2 aspect-video w-full rounded-lg object-cover" }) : null,
|
|
2207
|
+
step.cta ? /* @__PURE__ */ jsx(ButtonLink, { link: step.cta, ctx, tone: inverted ? "inverse" : "outline", className: "mt-2 self-start" }) : null
|
|
2208
|
+
] })
|
|
2209
|
+
] }, i)) })
|
|
2210
|
+
] });
|
|
2211
|
+
}
|
|
2212
|
+
});
|
|
2213
|
+
|
|
2214
|
+
// src/sections/index.ts
|
|
2215
|
+
var builtInSections = Object.freeze([
|
|
2216
|
+
navSection,
|
|
2217
|
+
heroSection,
|
|
2218
|
+
featuresSection,
|
|
2219
|
+
ctaSection,
|
|
2220
|
+
testimonialsSection,
|
|
2221
|
+
faqSection,
|
|
2222
|
+
pricingSection,
|
|
2223
|
+
logosSection,
|
|
2224
|
+
statsSection,
|
|
2225
|
+
gallerySection,
|
|
2226
|
+
richTextSection,
|
|
2227
|
+
formEmbedSection,
|
|
2228
|
+
footerSection,
|
|
2229
|
+
scheduleSection,
|
|
2230
|
+
teamSection,
|
|
2231
|
+
blogListSection,
|
|
2232
|
+
postBodySection,
|
|
2233
|
+
mapSection,
|
|
2234
|
+
timelineSection,
|
|
2235
|
+
fileSection
|
|
2236
|
+
]);
|
|
2237
|
+
for (const def of builtInSections) registerBuiltInSection(def);
|
|
2238
|
+
|
|
2239
|
+
export { Anchor, ButtonLink, CHIP_TONES, HiddenTitle, ICONS, IconName, ItemTitle, MAP_PROVIDERS, SectionBand, SectionIntro, SectionTitle, SiteFormHeader, SiteImage, TextLink, WEEKDAYS, blogListSection, builtInSections, ctaSection, faqSection, featuresSection, fileDescription, fileHref, fileSection, footerSection, formDrawMode, formEmbedSection, formatPostDate, formatTime, gallerySection, heroSection, linkAttrs, logosSection, mapEmbedUrl, mapPageUrl, mapSection, navSection, postBodySection, pricingSection, resolveHref, richTextSection, scheduleSection, siteFormSlots, statsSection, teamSection, testimonialsSection, timelineSection, titleId };
|
|
2240
|
+
//# sourceMappingURL=chunk-C375ZPU2.js.map
|
|
2241
|
+
//# sourceMappingURL=chunk-C375ZPU2.js.map
|