trc-ui 0.0.0-stage → 0.1.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/dist/index.cjs ADDED
@@ -0,0 +1,737 @@
1
+ "use client";
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var src_exports = {};
23
+ __export(src_exports, {
24
+ AccentText: () => AccentText,
25
+ ArrowLeft: () => ArrowLeft,
26
+ ArrowRight: () => ArrowRight,
27
+ ArrowUp: () => ArrowUp,
28
+ BackToTop: () => BackToTop,
29
+ Card: () => Card,
30
+ Check: () => Check,
31
+ Checkbox: () => Checkbox,
32
+ ChevronDown: () => ChevronDown,
33
+ Close: () => Close,
34
+ Container: () => Container,
35
+ Field: () => Field,
36
+ Heading: () => Heading,
37
+ HeroVideo: () => HeroVideo,
38
+ Input: () => Input,
39
+ Menu: () => Menu,
40
+ PageHero: () => PageHero,
41
+ PillButton: () => PillButton,
42
+ PillLink: () => PillLink,
43
+ Section: () => Section,
44
+ Select: () => Select,
45
+ SiteFooter: () => SiteFooter,
46
+ SiteHeader: () => SiteHeader,
47
+ StepNumber: () => StepNumber,
48
+ Switch: () => Switch,
49
+ Text: () => Text,
50
+ Textarea: () => Textarea,
51
+ cx: () => cx,
52
+ plainText: () => plainText
53
+ });
54
+ module.exports = __toCommonJS(src_exports);
55
+
56
+ // src/utils/cx.ts
57
+ function cx(...classes) {
58
+ return classes.filter(Boolean).join(" ");
59
+ }
60
+
61
+ // src/components/typography.tsx
62
+ var import_jsx_runtime = require("react/jsx-runtime");
63
+ function AccentText({ text, accentLast = 0 }) {
64
+ if (text.includes("*")) {
65
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: text.split("*").map(
66
+ (part, i) => i % 2 === 1 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "trc-accent", children: part }, i) : part
67
+ ) });
68
+ }
69
+ const words = text.split(" ");
70
+ if (!accentLast || words.length < 2) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: text });
71
+ const n = Math.min(accentLast, words.length - 1);
72
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
73
+ words.slice(0, -n).join(" "),
74
+ " ",
75
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "trc-accent", children: words.slice(-n).join(" ") })
76
+ ] });
77
+ }
78
+ var plainText = (text) => text.replaceAll("*", "");
79
+ function Heading({ as: Tag = "h2", size = "xl", accentLast, className, children, ...props }) {
80
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, { className: cx("trc-display", `trc-heading--${size}`, className), ...props, children: typeof children === "string" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AccentText, { text: children, accentLast }) : children });
81
+ }
82
+ function Text({ as, size = "md", tone = "muted", className, ...props }) {
83
+ const Tag = as ?? "p";
84
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tag, { className: cx("trc-text", `trc-text--${size}`, `trc-text--${tone}`, className), ...props });
85
+ }
86
+ function StepNumber({ n, size = "md", className }) {
87
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": true, className: cx("trc-step-number", size === "sm" && "trc-step-number--sm", className), children: String(n).padStart(2, "0") });
88
+ }
89
+
90
+ // src/components/pill.tsx
91
+ var import_react = require("react");
92
+
93
+ // src/utils/icons.tsx
94
+ var import_jsx_runtime2 = require("react/jsx-runtime");
95
+ var base = {
96
+ xmlns: "http://www.w3.org/2000/svg",
97
+ viewBox: "0 0 24 24",
98
+ fill: "none",
99
+ stroke: "currentColor",
100
+ strokeWidth: 2,
101
+ strokeLinecap: "round",
102
+ strokeLinejoin: "round",
103
+ "aria-hidden": true
104
+ };
105
+ var ArrowRight = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...base, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5 12h14M13 6l6 6-6 6" }) });
106
+ var ArrowLeft = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...base, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19 12H5M11 18l-6-6 6-6" }) });
107
+ var Spinner = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...base, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 12a9 9 0 1 1-6.22-8.56" }) });
108
+ var ChevronDown = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...base, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m6 9 6 6 6-6" }) });
109
+ var Check = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...base, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M20 6 9 17l-5-5" }) });
110
+ var Menu = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...base, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M4 7h16M4 12h16M4 17h16" }) });
111
+ var Close = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...base, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M18 6 6 18M6 6l12 12" }) });
112
+ var ArrowUp = (props) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...base, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 19V5M6 11l6-6 6 6" }) });
113
+
114
+ // src/components/pill.tsx
115
+ var import_jsx_runtime3 = require("react/jsx-runtime");
116
+ var pillClass = ({ solid, size }, className) => cx("trc-pill", size === "sm" && "trc-pill--sm", solid && "trc-pill--solid", className);
117
+ function PillInner({ icon, children }) {
118
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
119
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "aria-hidden": true, className: "trc-pill__fill" }),
120
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "aria-hidden": true, className: "trc-pill__icon", children: icon ?? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ArrowRight, {}) }),
121
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "trc-pill__label", children })
122
+ ] });
123
+ }
124
+ var PillButton = (0, import_react.forwardRef)(function PillButton2({ solid, size = "md", icon, loading = false, disabled, type = "button", className, children, ...props }, ref) {
125
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
126
+ "button",
127
+ {
128
+ ref,
129
+ type,
130
+ disabled: disabled || loading,
131
+ "aria-busy": loading || void 0,
132
+ className: pillClass({ solid, size }, className),
133
+ ...props,
134
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PillInner, { icon: loading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Spinner, { className: "trc-spin" }) : icon, children })
135
+ }
136
+ );
137
+ });
138
+ var PillLink = (0, import_react.forwardRef)(function PillLink2({ as, solid, size = "md", icon, className, children, ...props }, ref) {
139
+ const Tag = as ?? "a";
140
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Tag, { ref, className: pillClass({ solid, size }, className), ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PillInner, { icon, children }) });
141
+ });
142
+
143
+ // src/components/surfaces.tsx
144
+ var import_jsx_runtime4 = require("react/jsx-runtime");
145
+ function Container({ as, size = "default", className, ...props }) {
146
+ const Tag = as ?? "div";
147
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Tag, { className: cx("trc-container", size !== "default" && `trc-container--${size}`, className), ...props });
148
+ }
149
+ function Section({
150
+ as,
151
+ spacing = "md",
152
+ inset = false,
153
+ className,
154
+ children,
155
+ ...props
156
+ }) {
157
+ const Tag = as ?? "section";
158
+ if (inset) {
159
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Tag, { className: cx("trc-section-inset", className), ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: cx("trc-section-inset__card", spacing !== "none" && `trc-section--${spacing}`), children }) });
160
+ }
161
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Tag, { className: cx(spacing !== "none" && `trc-section--${spacing}`, className), ...props, children });
162
+ }
163
+ function Card({
164
+ as,
165
+ tone = "default",
166
+ padding = "md",
167
+ interactive = false,
168
+ watermark,
169
+ className,
170
+ children,
171
+ ...props
172
+ }) {
173
+ const Tag = as ?? "div";
174
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
175
+ Tag,
176
+ {
177
+ className: cx(
178
+ "trc-card",
179
+ tone === "alt" && "trc-card--alt",
180
+ padding !== "none" && `trc-card--pad-${padding}`,
181
+ interactive && "trc-card--interactive",
182
+ !!watermark && "trc-card--watermark",
183
+ className
184
+ ),
185
+ ...props,
186
+ children: [
187
+ !!watermark && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": true, className: "trc-card__watermark", children: watermark }),
188
+ children
189
+ ]
190
+ }
191
+ );
192
+ }
193
+
194
+ // src/components/field.tsx
195
+ var import_react2 = require("react");
196
+ var import_jsx_runtime5 = require("react/jsx-runtime");
197
+ var FieldContext = (0, import_react2.createContext)(null);
198
+ function Field({ label, hint, error, required = false, id, className, children }) {
199
+ const generated = (0, import_react2.useId)();
200
+ const fieldId = id ?? generated;
201
+ const messageId = `${fieldId}-message`;
202
+ const message = error ?? hint;
203
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
204
+ FieldContext.Provider,
205
+ {
206
+ value: { id: fieldId, describedBy: message ? messageId : void 0, invalid: error != null, required },
207
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: cx("trc-field", className), children: [
208
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { htmlFor: fieldId, className: "trc-field__label", children: [
209
+ label,
210
+ required && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { "aria-hidden": true, className: "trc-field__required", children: [
211
+ " ",
212
+ "*"
213
+ ] })
214
+ ] }),
215
+ children,
216
+ message != null && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { id: messageId, className: cx("trc-field__message", error != null && "trc-field__message--error"), children: message })
217
+ ] })
218
+ }
219
+ );
220
+ }
221
+ function useFieldProps(props) {
222
+ const field = (0, import_react2.useContext)(FieldContext);
223
+ const invalid = props.invalid ?? field?.invalid ?? false;
224
+ return {
225
+ id: props.id ?? field?.id,
226
+ required: props.required ?? field?.required,
227
+ "aria-describedby": cx(field?.describedBy, props["aria-describedby"]) || void 0,
228
+ "aria-invalid": invalid || void 0
229
+ };
230
+ }
231
+ var Input = (0, import_react2.forwardRef)(function Input2({ invalid, className, ...props }, ref) {
232
+ const a11y = useFieldProps({ ...props, invalid });
233
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { ref, className: cx("trc-control", className), ...props, ...a11y });
234
+ });
235
+ var Textarea = (0, import_react2.forwardRef)(function Textarea2({ invalid, rows = 4, className, ...props }, ref) {
236
+ const a11y = useFieldProps({ ...props, invalid });
237
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
238
+ "textarea",
239
+ {
240
+ ref,
241
+ rows,
242
+ className: cx("trc-control", "trc-control--textarea", className),
243
+ ...props,
244
+ ...a11y
245
+ }
246
+ );
247
+ });
248
+ var Select = (0, import_react2.forwardRef)(function Select2({ invalid, placeholder, defaultValue, className, children, ...props }, ref) {
249
+ const a11y = useFieldProps({ ...props, invalid });
250
+ const initial = defaultValue ?? (props.value === void 0 && placeholder != null ? "" : void 0);
251
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: cx("trc-select", className), children: [
252
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("select", { ref, className: "trc-control trc-control--select", defaultValue: initial, ...props, ...a11y, children: [
253
+ placeholder != null && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: "", disabled: true, children: placeholder }),
254
+ children
255
+ ] }),
256
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChevronDown, { className: "trc-select__chevron" })
257
+ ] });
258
+ });
259
+
260
+ // src/components/toggle.tsx
261
+ var import_react3 = require("react");
262
+ var import_jsx_runtime6 = require("react/jsx-runtime");
263
+ function ToggleLabel({
264
+ kind,
265
+ label,
266
+ description,
267
+ className,
268
+ children
269
+ }) {
270
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: cx("trc-toggle", `trc-toggle--${kind}`, className), children: [
271
+ children,
272
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "trc-toggle__text", children: [
273
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "trc-toggle__label", children: label }),
274
+ description != null && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "trc-toggle__description", children: description })
275
+ ] })
276
+ ] });
277
+ }
278
+ var Checkbox = (0, import_react3.forwardRef)(function Checkbox2({ label, description, className, ...props }, ref) {
279
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ToggleLabel, { kind: "checkbox", label, description, className, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "trc-checkbox", children: [
280
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { ref, type: "checkbox", className: "trc-checkbox__input", ...props }),
281
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Check, { className: "trc-checkbox__check" })
282
+ ] }) });
283
+ });
284
+ var Switch = (0, import_react3.forwardRef)(function Switch2({ label, description, className, ...props }, ref) {
285
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ToggleLabel, { kind: "switch", label, description, className, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "trc-switch", children: [
286
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { ref, type: "checkbox", role: "switch", className: "trc-switch__input", ...props }),
287
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { "aria-hidden": true, className: "trc-switch__thumb" })
288
+ ] }) });
289
+ });
290
+
291
+ // src/components/site-header.tsx
292
+ var import_react5 = require("react");
293
+ var import_react_dom = require("react-dom");
294
+
295
+ // src/hooks/use-modal.ts
296
+ var import_react4 = require("react");
297
+ var FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
298
+ function useModal(open, panelRef, onClose) {
299
+ const close = (0, import_react4.useRef)(onClose);
300
+ close.current = onClose;
301
+ (0, import_react4.useEffect)(() => {
302
+ const panel = panelRef.current;
303
+ if (!open || !panel) return;
304
+ const previous = document.activeElement;
305
+ const overflow = document.body.style.overflow;
306
+ document.body.style.overflow = "hidden";
307
+ (panel.querySelector("[data-autofocus]") ?? panel.querySelector(FOCUSABLE) ?? panel).focus();
308
+ const onKeyDown = (e) => {
309
+ if (e.key === "Escape") {
310
+ e.stopPropagation();
311
+ close.current();
312
+ return;
313
+ }
314
+ if (e.key !== "Tab") return;
315
+ const items = [...panel.querySelectorAll(FOCUSABLE)].filter((el) => el.offsetParent !== null);
316
+ const first = items[0];
317
+ const last = items[items.length - 1];
318
+ if (!first || !last) {
319
+ e.preventDefault();
320
+ return;
321
+ }
322
+ if (e.shiftKey && document.activeElement === first) {
323
+ e.preventDefault();
324
+ last.focus();
325
+ } else if (!e.shiftKey && document.activeElement === last) {
326
+ e.preventDefault();
327
+ first.focus();
328
+ }
329
+ };
330
+ document.addEventListener("keydown", onKeyDown);
331
+ return () => {
332
+ document.removeEventListener("keydown", onKeyDown);
333
+ document.body.style.overflow = overflow;
334
+ previous?.focus();
335
+ };
336
+ }, [open, panelRef]);
337
+ }
338
+
339
+ // src/components/site-header.tsx
340
+ var import_jsx_runtime7 = require("react/jsx-runtime");
341
+ var isMenu = (item) => "items" in item;
342
+ function SiteHeader({
343
+ logo,
344
+ logoLabel,
345
+ homeHref = "/",
346
+ nav = [],
347
+ cta,
348
+ actions,
349
+ mobileFooter,
350
+ linkAs: LinkTag = "a",
351
+ scrollThreshold = 24,
352
+ menuLabel = "Menu",
353
+ closeLabel = "Close",
354
+ className
355
+ }) {
356
+ const [scrolled, setScrolled] = (0, import_react5.useState)(false);
357
+ const [open, setOpen] = (0, import_react5.useState)(false);
358
+ (0, import_react5.useEffect)(() => {
359
+ const update = () => setScrolled(window.scrollY > scrollThreshold);
360
+ update();
361
+ window.addEventListener("scroll", update, { passive: true });
362
+ return () => window.removeEventListener("scroll", update);
363
+ }, [scrollThreshold]);
364
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("header", { className: cx("trc-header", className), children: [
365
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: cx("trc-header__bar", scrolled && "trc-header__bar--scrolled"), children: [
366
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(LinkTag, { href: homeHref, "aria-label": logoLabel, className: "trc-header__logo", children: logo }),
367
+ nav.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("nav", { className: "trc-header__nav", children: nav.map(
368
+ (item, i) => isMenu(item) ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(NavDropdown, { menu: item, linkAs: LinkTag }, i) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
369
+ LinkTag,
370
+ {
371
+ href: item.href,
372
+ "aria-current": item.active ? "page" : void 0,
373
+ className: cx("trc-header__link", item.active && "trc-header__link--active"),
374
+ children: item.label
375
+ },
376
+ i
377
+ )
378
+ ) }),
379
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "trc-header__end", children: [
380
+ actions && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "trc-header__actions", children: actions }),
381
+ cta && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PillLink, { as: LinkTag, href: cta.href, size: "sm", className: "trc-header__cta", children: cta.label }),
382
+ (nav.length > 0 || cta || mobileFooter) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
383
+ "button",
384
+ {
385
+ type: "button",
386
+ className: "trc-header__burger",
387
+ "aria-label": menuLabel,
388
+ "aria-expanded": open,
389
+ onClick: () => setOpen(true),
390
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Menu, {})
391
+ }
392
+ )
393
+ ] })
394
+ ] }),
395
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
396
+ MobileMenu,
397
+ {
398
+ open,
399
+ onClose: () => setOpen(false),
400
+ nav,
401
+ cta,
402
+ footer: mobileFooter,
403
+ linkAs: LinkTag,
404
+ title: menuLabel,
405
+ closeLabel
406
+ }
407
+ )
408
+ ] });
409
+ }
410
+ function NavDropdown({ menu, linkAs: LinkTag }) {
411
+ const id = (0, import_react5.useId)();
412
+ const [open, setOpen] = (0, import_react5.useState)(false);
413
+ const closeTimer = (0, import_react5.useRef)(void 0);
414
+ const button = (0, import_react5.useRef)(null);
415
+ const show = () => {
416
+ window.clearTimeout(closeTimer.current);
417
+ setOpen(true);
418
+ };
419
+ const hide = () => {
420
+ closeTimer.current = window.setTimeout(() => setOpen(false), 120);
421
+ };
422
+ (0, import_react5.useEffect)(() => () => window.clearTimeout(closeTimer.current), []);
423
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
424
+ "div",
425
+ {
426
+ className: "trc-dropdown",
427
+ onMouseEnter: show,
428
+ onMouseLeave: hide,
429
+ onKeyDown: (e) => {
430
+ if (e.key === "Escape" && open) {
431
+ setOpen(false);
432
+ button.current?.focus();
433
+ }
434
+ },
435
+ onBlur: (e) => !e.currentTarget.contains(e.relatedTarget) && setOpen(false),
436
+ children: [
437
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
438
+ "button",
439
+ {
440
+ ref: button,
441
+ type: "button",
442
+ "aria-expanded": open,
443
+ "aria-controls": id,
444
+ onClick: () => setOpen((o) => !o),
445
+ className: cx("trc-header__link", "trc-dropdown__trigger", open && "trc-dropdown__trigger--open"),
446
+ children: [
447
+ menu.label,
448
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChevronDown, { className: "trc-dropdown__chevron" })
449
+ ]
450
+ }
451
+ ),
452
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { id, className: cx("trc-dropdown__panel", open && "trc-dropdown__panel--open"), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { className: "trc-dropdown__list", children: menu.items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
453
+ LinkTag,
454
+ {
455
+ href: item.href,
456
+ "aria-current": item.active ? "page" : void 0,
457
+ onClick: () => setOpen(false),
458
+ className: "trc-dropdown__item",
459
+ children: [
460
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "trc-dropdown__label", children: item.label }),
461
+ item.description != null && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "trc-dropdown__description", children: item.description })
462
+ ]
463
+ }
464
+ ) }, i)) }) })
465
+ ]
466
+ }
467
+ );
468
+ }
469
+ function MobileMenu({
470
+ open,
471
+ onClose,
472
+ nav,
473
+ cta,
474
+ footer,
475
+ linkAs: LinkTag,
476
+ title,
477
+ closeLabel
478
+ }) {
479
+ const titleId = (0, import_react5.useId)();
480
+ const panel = (0, import_react5.useRef)(null);
481
+ const [mounted, setMounted] = (0, import_react5.useState)(false);
482
+ (0, import_react5.useEffect)(() => setMounted(true), []);
483
+ useModal(open, panel, onClose);
484
+ if (!mounted) return null;
485
+ return (0, import_react_dom.createPortal)(
486
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: cx("trc-drawer", open && "trc-drawer--open"), "aria-hidden": !open, children: [
487
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "trc-drawer__overlay", onClick: onClose }),
488
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
489
+ "div",
490
+ {
491
+ ref: panel,
492
+ role: "dialog",
493
+ "aria-modal": "true",
494
+ "aria-labelledby": titleId,
495
+ tabIndex: -1,
496
+ className: "trc-drawer__panel",
497
+ children: [
498
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "trc-drawer__head", children: [
499
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { id: titleId, className: "trc-drawer__title", children: title }),
500
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "trc-drawer__close", "aria-label": closeLabel, onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Close, {}) })
501
+ ] }),
502
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("nav", { className: "trc-mobile-nav", children: nav.map(
503
+ (item, i) => isMenu(item) ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "trc-mobile-nav__group", children: [
504
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "trc-mobile-nav__heading", children: item.label }),
505
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { className: "trc-mobile-nav__sub", children: item.items.map((sub, j) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
506
+ LinkTag,
507
+ {
508
+ href: sub.href,
509
+ "aria-current": sub.active ? "page" : void 0,
510
+ onClick: onClose,
511
+ className: "trc-mobile-nav__sublink",
512
+ children: sub.label
513
+ }
514
+ ) }, j)) })
515
+ ] }, i) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
516
+ LinkTag,
517
+ {
518
+ href: item.href,
519
+ "aria-current": item.active ? "page" : void 0,
520
+ onClick: onClose,
521
+ className: cx("trc-mobile-nav__link", item.active && "trc-mobile-nav__link--active"),
522
+ children: item.label
523
+ },
524
+ i
525
+ )
526
+ ) }),
527
+ cta && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PillLink, { as: LinkTag, href: cta.href, onClick: onClose, className: "trc-drawer__cta", children: cta.label }),
528
+ footer != null && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "trc-drawer__footer", children: footer })
529
+ ]
530
+ }
531
+ )
532
+ ] }),
533
+ document.body
534
+ );
535
+ }
536
+
537
+ // src/components/site-footer.tsx
538
+ var import_jsx_runtime8 = require("react/jsx-runtime");
539
+ function SiteFooter({
540
+ logo,
541
+ logoLabel,
542
+ homeHref = "/",
543
+ tagline,
544
+ children,
545
+ columns = [],
546
+ columnsLabel = "Site map",
547
+ copyright,
548
+ legal = [],
549
+ legalLabel = "Legal",
550
+ linkAs: LinkTag = "a",
551
+ className
552
+ }) {
553
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("footer", { className: cx("trc-footer", className), children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "trc-footer__card", children: [
554
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "trc-footer__main", children: [
555
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "trc-footer__brand", children: [
556
+ logo != null && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(LinkTag, { href: homeHref, "aria-label": logoLabel, className: "trc-footer__logo", children: logo }),
557
+ tagline != null && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "trc-footer__tagline", children: tagline }),
558
+ children != null && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "trc-footer__extra", children })
559
+ ] }),
560
+ columns.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("nav", { className: "trc-footer__columns", "aria-label": columnsLabel, children: columns.map((col, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
561
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "trc-footer__heading", children: col.title }),
562
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "trc-footer__list", children: col.links.map((link, j) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(LinkTag, { href: link.href, className: "trc-footer__link", children: link.label }) }, j)) })
563
+ ] }, i)) })
564
+ ] }),
565
+ (copyright != null || legal.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "trc-footer__bottom", children: [
566
+ copyright != null && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "trc-footer__copyright", children: copyright }),
567
+ legal.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("nav", { "aria-label": legalLabel, className: "trc-footer__legal", children: legal.map((link, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(LinkTag, { href: link.href, className: "trc-footer__legal-link", children: link.label }, i)) })
568
+ ] })
569
+ ] }) });
570
+ }
571
+
572
+ // src/components/hero.tsx
573
+ var import_react6 = require("react");
574
+ var import_jsx_runtime9 = require("react/jsx-runtime");
575
+ var delay = (ms) => ({ "--trc-delay": `${ms}ms` });
576
+ function PageHero({
577
+ title,
578
+ accentLast,
579
+ description,
580
+ meta,
581
+ image,
582
+ imagePosition = "50% 50%",
583
+ children,
584
+ className
585
+ }) {
586
+ const words = title.split(" ").length;
587
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("section", { className: cx("trc-hero-inset", className), children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "trc-page-hero", children: [
588
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { "aria-hidden": true, className: "trc-hero__media", children: typeof image === "string" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("img", { src: image, alt: "", className: "trc-hero__image", style: { objectPosition: imagePosition } }) : image != null ? image : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "trc-page-hero__glow" }) }),
589
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { "aria-hidden": true, className: "trc-page-hero__veil" }),
590
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "trc-page-hero__content", children: [
591
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h1", { className: "trc-display trc-page-hero__title trc-enter", style: delay(150), children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AccentText, { text: title, accentLast: accentLast ?? (words > 3 ? 2 : 1) }) }),
592
+ description != null && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "trc-page-hero__description trc-enter", style: delay(250), children: description }),
593
+ meta != null && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "trc-page-hero__meta trc-enter", style: delay(350), children: meta }),
594
+ children != null && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "trc-page-hero__actions trc-enter", style: delay(450), children })
595
+ ] })
596
+ ] }) });
597
+ }
598
+ var prefersReducedMotion = () => window.matchMedia("(prefers-reduced-motion: reduce)").matches;
599
+ function HeroVideo({ clips, children, aside, fade = 1.2, parallax = true, className }) {
600
+ const videos = (0, import_react6.useRef)([]);
601
+ const section = (0, import_react6.useRef)(null);
602
+ const media = (0, import_react6.useRef)(null);
603
+ const [active, setActive] = (0, import_react6.useState)(-1);
604
+ (0, import_react6.useEffect)(() => {
605
+ if (clips.length === 0) return;
606
+ const start = Math.floor(Math.random() * clips.length);
607
+ const video = videos.current[start];
608
+ if (!prefersReducedMotion()) void video?.play().catch(() => {
609
+ });
610
+ const frame = requestAnimationFrame(() => setActive(start));
611
+ return () => {
612
+ cancelAnimationFrame(frame);
613
+ video?.pause();
614
+ };
615
+ }, [clips.length]);
616
+ (0, import_react6.useEffect)(() => {
617
+ const el = section.current;
618
+ const layer = media.current;
619
+ if (!parallax || !el || !layer || prefersReducedMotion()) return;
620
+ let frame = 0;
621
+ const update = () => {
622
+ frame = 0;
623
+ const rect = el.getBoundingClientRect();
624
+ const progress = Math.min(Math.max(-rect.top / rect.height, 0), 1);
625
+ layer.style.transform = `translate3d(0, ${progress * 120}px, 0)`;
626
+ };
627
+ const onScroll = () => {
628
+ if (!frame) frame = requestAnimationFrame(update);
629
+ };
630
+ update();
631
+ window.addEventListener("scroll", onScroll, { passive: true });
632
+ return () => {
633
+ window.removeEventListener("scroll", onScroll);
634
+ cancelAnimationFrame(frame);
635
+ };
636
+ }, [parallax]);
637
+ const playNext = (i) => {
638
+ const next = videos.current[(i + 1) % clips.length];
639
+ if (!next) return;
640
+ next.currentTime = 0;
641
+ void next.play().catch(() => {
642
+ });
643
+ setActive((i + 1) % clips.length);
644
+ };
645
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("section", { ref: section, className: cx("trc-hero-inset", className), children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "trc-hero-video", children: [
646
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { ref: media, "aria-hidden": true, className: "trc-hero__media trc-hero-video__media", children: clips.map((clip, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
647
+ "video",
648
+ {
649
+ ref: (el) => {
650
+ videos.current[i] = el;
651
+ },
652
+ className: cx("trc-hero__image", "trc-hero-video__clip", i === active && "trc-hero-video__clip--active"),
653
+ style: { transitionDuration: `${fade}s` },
654
+ muted: true,
655
+ playsInline: true,
656
+ preload: "metadata",
657
+ poster: clip.poster,
658
+ onTimeUpdate: (e) => {
659
+ const v = e.currentTarget;
660
+ if (i === active && v.duration && v.duration - v.currentTime <= fade) playNext(i);
661
+ },
662
+ onEnded: () => i === active && playNext(i),
663
+ children: [
664
+ clip.webm && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("source", { src: clip.webm, type: "video/webm" }),
665
+ clip.mp4 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("source", { src: clip.mp4, type: "video/mp4" })
666
+ ]
667
+ },
668
+ i
669
+ )) }),
670
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { "aria-hidden": true, className: "trc-hero-video__veil" }),
671
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "trc-hero-video__content", children: [
672
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "trc-hero-video__main", children }),
673
+ aside != null && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "trc-hero-video__aside", children: aside })
674
+ ] })
675
+ ] }) });
676
+ }
677
+
678
+ // src/components/back-to-top.tsx
679
+ var import_react7 = require("react");
680
+ var import_jsx_runtime10 = require("react/jsx-runtime");
681
+ function BackToTop({ label = "Back to top", threshold = 0.8, className }) {
682
+ const [visible, setVisible] = (0, import_react7.useState)(false);
683
+ (0, import_react7.useEffect)(() => {
684
+ const update = () => setVisible(window.scrollY > window.innerHeight * threshold);
685
+ update();
686
+ window.addEventListener("scroll", update, { passive: true });
687
+ return () => window.removeEventListener("scroll", update);
688
+ }, [threshold]);
689
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
690
+ "button",
691
+ {
692
+ type: "button",
693
+ onClick: () => window.scrollTo({
694
+ top: 0,
695
+ behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth"
696
+ }),
697
+ "aria-label": label,
698
+ "aria-hidden": !visible,
699
+ tabIndex: visible ? 0 : -1,
700
+ className: cx("trc-back-to-top", visible && "trc-back-to-top--visible", className),
701
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ArrowUp, {})
702
+ }
703
+ );
704
+ }
705
+ // Annotate the CommonJS export names for ESM import in node:
706
+ 0 && (module.exports = {
707
+ AccentText,
708
+ ArrowLeft,
709
+ ArrowRight,
710
+ ArrowUp,
711
+ BackToTop,
712
+ Card,
713
+ Check,
714
+ Checkbox,
715
+ ChevronDown,
716
+ Close,
717
+ Container,
718
+ Field,
719
+ Heading,
720
+ HeroVideo,
721
+ Input,
722
+ Menu,
723
+ PageHero,
724
+ PillButton,
725
+ PillLink,
726
+ Section,
727
+ Select,
728
+ SiteFooter,
729
+ SiteHeader,
730
+ StepNumber,
731
+ Switch,
732
+ Text,
733
+ Textarea,
734
+ cx,
735
+ plainText
736
+ });
737
+ //# sourceMappingURL=index.cjs.map