@dreamtree-org/landing-kit 0.4.0 → 0.6.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 CHANGED
@@ -39,15 +39,19 @@ __export(index_exports, {
39
39
  FeatureSection: () => FeatureSection,
40
40
  Header: () => Header,
41
41
  Hero: () => Hero,
42
+ HeroSlider: () => HeroSlider,
42
43
  HowItWorks: () => HowItWorks,
43
44
  LandingPage: () => LandingPage,
44
45
  LandingProvider: () => LandingProvider,
45
46
  MarketingLayout: () => MarketingLayout,
47
+ NamedIcon: () => NamedIcon,
46
48
  PageHero: () => PageHero,
47
49
  PreviewFrame: () => PreviewFrame,
48
50
  Section: () => Section,
49
51
  SectionIntro: () => SectionIntro,
50
52
  SmartLink: () => SmartLink,
53
+ TENANT_BLOCKS: () => TENANT_BLOCKS,
54
+ TenantPage: () => TenantPage,
51
55
  TextLink: () => TextLink,
52
56
  Wordmark: () => Wordmark,
53
57
  Wrap: () => Wrap,
@@ -60,9 +64,12 @@ __export(index_exports, {
60
64
  resolveHref: () => resolveHref,
61
65
  stashDemoFill: () => stashDemoFill,
62
66
  takeDemoFill: () => takeDemoFill,
67
+ themeVars: () => themeVars,
63
68
  toLandingBrand: () => toLandingBrand,
64
69
  useLanding: () => useLanding,
65
- useTapReveal: () => useTapReveal
70
+ useTapReveal: () => useTapReveal,
71
+ validatePage: () => validatePage,
72
+ validateTheme: () => validateTheme
66
73
  });
67
74
  module.exports = __toCommonJS(index_exports);
68
75
 
@@ -195,7 +202,7 @@ function brandStyle(brand) {
195
202
  }
196
203
  function AuthShell({
197
204
  brand,
198
- title,
205
+ title: title2,
199
206
  subtitle,
200
207
  children,
201
208
  footer,
@@ -240,7 +247,7 @@ function AuthShell({
240
247
  ] }),
241
248
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { className: "dt-auth-card", "aria-labelledby": headingId, children: [
242
249
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("header", { className: "dt-auth-head", children: [
243
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { id: headingId, ...demoAccounts ? tapProps : {}, children: title }),
250
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { id: headingId, ...demoAccounts ? tapProps : {}, children: title2 }),
244
251
  subtitle ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: subtitle }) : null
245
252
  ] }),
246
253
  children,
@@ -382,16 +389,16 @@ function Eyebrow({ children, className = "" }) {
382
389
  if (!children) return null;
383
390
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: `dt-lp-eyebrow ${className}`.trim(), children });
384
391
  }
385
- function SectionIntro({ id, eyebrow, title, body, align = "start", children }) {
392
+ function SectionIntro({ id, eyebrow, title: title2, body, align = "start", children }) {
386
393
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `dt-lp-intro dt-lp-intro--${align}`, children: [
387
394
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Eyebrow, { children: eyebrow }),
388
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { id, className: "dt-lp-h2", children: title }),
395
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { id, className: "dt-lp-h2", children: title2 }),
389
396
  body ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "dt-lp-lead", children: body }) : null,
390
397
  children
391
398
  ] });
392
399
  }
393
- function Section({ id, labelledBy, tone = "plain", className = "", children, narrow }) {
394
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("section", { id, "aria-labelledby": labelledBy, className: `dt-lp-section dt-lp-tone--${tone} ${className}`.trim(), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Wrap, { narrow, children }) });
400
+ function Section({ id, labelledBy, tone: tone2 = "plain", className = "", children, narrow }) {
401
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("section", { id, "aria-labelledby": labelledBy, className: `dt-lp-section dt-lp-tone--${tone2} ${className}`.trim(), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Wrap, { narrow, children }) });
395
402
  }
396
403
  function TextLink({ href, children, className = "", ...rest }) {
397
404
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(SmartLink, { href, className: `dt-lp-textlink ${className}`.trim(), ...rest, children: [
@@ -533,7 +540,7 @@ function MarketingLayout({
533
540
  var import_jsx_runtime11 = require("react/jsx-runtime");
534
541
  function Hero({
535
542
  eyebrow,
536
- title,
543
+ title: title2,
537
544
  subtitle,
538
545
  lead,
539
546
  chips,
@@ -544,7 +551,7 @@ function Hero({
544
551
  id = "hero",
545
552
  backdrop
546
553
  }) {
547
- const [first, hi] = Array.isArray(title) ? title : [title, null];
554
+ const [first, hi] = Array.isArray(title2) ? title2 : [title2, null];
548
555
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("section", { id, "aria-labelledby": "hero-title", className: `dt-lp-hero${overlap ? " dt-lp-hero--overlap" : ""}`, children: [
549
556
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "dt-lp-hero-glow", "aria-hidden": "true" }),
550
557
  backdrop ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "dt-lp-hero-backdrop", "aria-hidden": "true", children: backdrop }) : null,
@@ -581,8 +588,8 @@ function Items({ items, kind }) {
581
588
  ] })
582
589
  ] }, f.title)) });
583
590
  }
584
- function Checklist({ items, icon: Icon = Check, tone = "good", labelledBy }) {
585
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("ul", { className: `dt-lp-checklist dt-lp-checklist--${tone}`, "aria-labelledby": labelledBy, children: items.map((x) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("li", { children: [
591
+ function Checklist({ items, icon: Icon = Check, tone: tone2 = "good", labelledBy }) {
592
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("ul", { className: `dt-lp-checklist dt-lp-checklist--${tone2}`, "aria-labelledby": labelledBy, children: items.map((x) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("li", { children: [
586
593
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Icon, { size: 18 }),
587
594
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: x })
588
595
  ] }, x)) });
@@ -611,10 +618,10 @@ function Chips({ items, label, className = "" }) {
611
618
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("ul", { className: `dt-lp-chips dt-lp-chips--start ${className}`.trim(), "aria-label": label, children: items.map((c) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("li", { children: c }, c)) });
612
619
  }
613
620
  function FeatureSection(props) {
614
- const { id, variant = "cards", tone, eyebrow, title, body, items = [], cta, footnote, media, reverse, bullets } = props;
615
- const hid = `${id || variant}-title`;
621
+ const { id, variant = "cards", tone: tone2, eyebrow, title: title2, body, items = [], cta, footnote, media, reverse, bullets } = props;
622
+ const hid2 = `${id || variant}-title`;
616
623
  const defaultTone = { highlight: "dark", split: "tinted", compare: "muted" }[variant] || "plain";
617
- const shell = (children, extra = {}) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Section, { id, labelledBy: hid, tone: tone || defaultTone, className: `dt-lp-feature dt-lp-feature--${variant}`, ...extra, children });
624
+ const shell = (children, extra = {}) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Section, { id, labelledBy: hid2, tone: tone2 || defaultTone, className: `dt-lp-feature dt-lp-feature--${variant}`, ...extra, children });
618
625
  switch (variant) {
619
626
  case "split":
620
627
  return shell(
@@ -623,7 +630,7 @@ function FeatureSection(props) {
623
630
  {
624
631
  className: "dt-lp-split",
625
632
  reverse,
626
- text: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(SectionIntro, { id: hid, eyebrow, title, body, children: [
633
+ text: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(SectionIntro, { id: hid2, eyebrow, title: title2, body, children: [
627
634
  media ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(InlineItems, { items }) : null,
628
635
  (bullets == null ? void 0 : bullets.length) ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Checklist, { items: bullets }) : null,
629
636
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Cta, { cta, className: "dt-lp-mt" })
@@ -635,7 +642,7 @@ function FeatureSection(props) {
635
642
  case "list":
636
643
  return shell(
637
644
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
638
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionIntro, { id: hid, eyebrow, title, body, align: "center" }),
645
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionIntro, { id: hid2, eyebrow, title: title2, body, align: "center" }),
639
646
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Items, { items, kind: "list" }),
640
647
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Cta, { cta, className: "dt-lp-mt" })
641
648
  ] })
@@ -645,14 +652,14 @@ function FeatureSection(props) {
645
652
  return shell(
646
653
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "dt-lp-intro-grid", children: [
647
654
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
648
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionIntro, { id: hid, eyebrow, title, body }),
655
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionIntro, { id: hid2, eyebrow, title: title2, body }),
649
656
  subtitle ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { className: "dt-lp-h3 dt-lp-h3--lg", children: subtitle }) : null,
650
657
  detail ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "dt-lp-body", children: detail }) : null,
651
658
  note ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "dt-lp-note", children: note }) : null,
652
659
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Cta, { cta, className: "dt-lp-mt" })
653
660
  ] }),
654
- glance.length ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("aside", { className: "dt-lp-glance", "aria-labelledby": `${hid}-glance`, children: [
655
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { id: `${hid}-glance`, className: "dt-lp-eyebrow", children: glanceTitle }),
661
+ glance.length ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("aside", { className: "dt-lp-glance", "aria-labelledby": `${hid2}-glance`, children: [
662
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { id: `${hid2}-glance`, className: "dt-lp-eyebrow", children: glanceTitle }),
656
663
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Checklist, { items: glance }),
657
664
  props.glanceNote ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "dt-lp-note", children: props.glanceNote }) : null,
658
665
  link ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(TextLink, { href: link.href, className: "dt-lp-mt", children: link.label }) : null
@@ -664,7 +671,7 @@ function FeatureSection(props) {
664
671
  const { note, noteLabel = "Important:" } = props;
665
672
  const text = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "dt-lp-intro", children: [
666
673
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Eyebrow, { children: eyebrow }),
667
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { id: hid, className: "dt-lp-h2", children: title }),
674
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { id: hid2, className: "dt-lp-h2", children: title2 }),
668
675
  body ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "dt-lp-lead", children: body }) : null,
669
676
  note ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "dt-lp-callout", children: [
670
677
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Alert, { size: 20 }),
@@ -686,7 +693,7 @@ function FeatureSection(props) {
686
693
  case "band": {
687
694
  const { examples, examplesLabel = "Examples" } = props;
688
695
  const text = /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
689
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionIntro, { id: hid, eyebrow, title, body }),
696
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionIntro, { id: hid2, eyebrow, title: title2, body }),
690
697
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Chips, { items: examples, label: examplesLabel, className: "dt-lp-mt" }),
691
698
  media ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(InlineItems, { items }) : null,
692
699
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Cta, { cta, className: "dt-lp-mt" })
@@ -701,13 +708,13 @@ function FeatureSection(props) {
701
708
  const { columns = [] } = props;
702
709
  return shell(
703
710
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
704
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionIntro, { id: hid, eyebrow, title, body }),
711
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionIntro, { id: hid2, eyebrow, title: title2, body }),
705
712
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "dt-lp-compare", children: columns.map((col, i) => {
706
713
  const cid = `${id || "compare"}-${i + 1}`;
707
- const tone2 = col.tone || (i === 0 ? "good" : "check");
714
+ const tone3 = col.tone || (i === 0 ? "good" : "check");
708
715
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "dt-lp-card dt-lp-card--compare", children: [
709
716
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { id: cid, className: "dt-lp-h3 dt-lp-h3--lg", children: col.title }),
710
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Checklist, { items: col.items, tone: tone2, icon: tone2 === "good" ? Check : Help, labelledBy: cid })
717
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Checklist, { items: col.items, tone: tone3, icon: tone3 === "good" ? Check : Help, labelledBy: cid })
711
718
  ] }, cid);
712
719
  }) })
713
720
  ] })
@@ -716,7 +723,7 @@ function FeatureSection(props) {
716
723
  case "links":
717
724
  return shell(
718
725
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
719
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionIntro, { id: hid, eyebrow, title, body }),
726
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionIntro, { id: hid2, eyebrow, title: title2, body }),
720
727
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("ul", { className: "dt-lp-grid dt-lp-grid--links", children: items.map((s) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(SmartLink, { href: s.href, className: "dt-lp-linkcard", children: [
721
728
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "dt-lp-linkcard-title", children: [
722
729
  s.title,
@@ -730,7 +737,7 @@ function FeatureSection(props) {
730
737
  case "detail": {
731
738
  return shell(
732
739
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "dt-lp-detail", children: [
733
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { id: hid, className: "dt-lp-h2", children: title }),
740
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { id: hid2, className: "dt-lp-h2", children: title2 }),
734
741
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
735
742
  body ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "dt-lp-body", children: body }) : null,
736
743
  (bullets == null ? void 0 : bullets.length) ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Checklist, { items: bullets }) : null
@@ -741,7 +748,7 @@ function FeatureSection(props) {
741
748
  default:
742
749
  return shell(
743
750
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
744
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionIntro, { id: hid, eyebrow, title, body }),
751
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionIntro, { id: hid2, eyebrow, title: title2, body }),
745
752
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Items, { items, kind: "cards" }),
746
753
  footnote ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "dt-lp-footnote", children: footnote }) : null,
747
754
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Cta, { cta, className: "dt-lp-mt" })
@@ -752,10 +759,10 @@ function FeatureSection(props) {
752
759
 
753
760
  // src/Audiences.jsx
754
761
  var import_jsx_runtime13 = require("react/jsx-runtime");
755
- function Audiences({ id = "audiences", eyebrow, title, body, items = [], linkLabel = "See how it works", tone = "muted" }) {
756
- const hid = `${id}-title`;
757
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Section, { id, labelledBy: hid, tone, className: "dt-lp-audiences", children: [
758
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SectionIntro, { id: hid, eyebrow, title, body, align: "center" }),
762
+ function Audiences({ id = "audiences", eyebrow, title: title2, body, items = [], linkLabel = "See how it works", tone: tone2 = "muted" }) {
763
+ const hid2 = `${id}-title`;
764
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Section, { id, labelledBy: hid2, tone: tone2, className: "dt-lp-audiences", children: [
765
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SectionIntro, { id: hid2, eyebrow, title: title2, body, align: "center" }),
759
766
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: `dt-lp-grid dt-lp-grid--audiences dt-lp-grid--n${Math.min(items.length, 4)}`, children: items.map((a, i) => {
760
767
  var _a;
761
768
  const aid = a.id || `${id}-${i + 1}`;
@@ -784,11 +791,11 @@ function Audiences({ id = "audiences", eyebrow, title, body, items = [], linkLab
784
791
 
785
792
  // src/HowItWorks.jsx
786
793
  var import_jsx_runtime14 = require("react/jsx-runtime");
787
- function HowItWorks({ id = "how-it-works", eyebrow, title, body, steps, items, tone = "plain", cta }) {
794
+ function HowItWorks({ id = "how-it-works", eyebrow, title: title2, body, steps, items, tone: tone2 = "plain", cta }) {
788
795
  const list = steps || items || [];
789
- const hid = `${id}-title`;
790
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Section, { id, labelledBy: hid, tone, className: "dt-lp-steps", children: [
791
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SectionIntro, { id: hid, eyebrow, title, body }),
796
+ const hid2 = `${id}-title`;
797
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Section, { id, labelledBy: hid2, tone: tone2, className: "dt-lp-steps", children: [
798
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SectionIntro, { id: hid2, eyebrow, title: title2, body }),
792
799
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("ol", { className: "dt-lp-grid dt-lp-grid--steps", children: list.map((st, i) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("li", { className: "dt-lp-card", children: [
793
800
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "dt-lp-step-num", "aria-hidden": "true", children: i + 1 }),
794
801
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("h3", { className: "dt-lp-h3", children: st.name || st.title }),
@@ -800,10 +807,10 @@ function HowItWorks({ id = "how-it-works", eyebrow, title, body, steps, items, t
800
807
 
801
808
  // src/Faq.jsx
802
809
  var import_jsx_runtime15 = require("react/jsx-runtime");
803
- function Faq({ id = "faq", eyebrow, title = "Frequently asked questions", intro, items = [], openFirst = true, tone = "muted" }) {
804
- const hid = `${id}-title`;
805
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Section, { id, labelledBy: hid, tone, narrow: true, className: "dt-lp-faq", children: [
806
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SectionIntro, { id: hid, eyebrow, title, body: intro, align: "center" }),
810
+ function Faq({ id = "faq", eyebrow, title: title2 = "Frequently asked questions", intro: intro2, items = [], openFirst = true, tone: tone2 = "muted" }) {
811
+ const hid2 = `${id}-title`;
812
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Section, { id, labelledBy: hid2, tone: tone2, narrow: true, className: "dt-lp-faq", children: [
813
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SectionIntro, { id: hid2, eyebrow, title: title2, body: intro2, align: "center" }),
807
814
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "dt-lp-faq-list", children: items.map((f, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("details", { className: "dt-lp-faq-item", open: openFirst && i === 0, children: [
808
815
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("summary", { children: [
809
816
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h3", { className: "dt-lp-h3", children: f.q }),
@@ -816,10 +823,10 @@ function Faq({ id = "faq", eyebrow, title = "Frequently asked questions", intro,
816
823
 
817
824
  // src/DemoCta.jsx
818
825
  var import_jsx_runtime16 = require("react/jsx-runtime");
819
- function DemoCta({ id = "demo", eyebrow, title, body, label = "Book a demo", chips, chipsLabel = "Business types", note, link }) {
826
+ function DemoCta({ id = "demo", eyebrow, title: title2, body, label = "Book a demo", chips, chipsLabel = "Business types", note, link }) {
820
827
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("section", { id, "aria-labelledby": `${id}-title`, className: "dt-lp-section dt-lp-cta", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Wrap, { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "dt-lp-cta-panel", children: [
821
828
  eyebrow ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "dt-lp-eyebrow dt-lp-eyebrow--inverse", children: eyebrow }) : null,
822
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("h2", { id: `${id}-title`, className: "dt-lp-h2 dt-lp-h2--cta", children: title }),
829
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("h2", { id: `${id}-title`, className: "dt-lp-h2 dt-lp-h2--cta", children: title2 }),
823
830
  body ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "dt-lp-cta-body", children: body }) : null,
824
831
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(BookDemoButton, { size: "lg", variant: "inverse", children: label }),
825
832
  (chips == null ? void 0 : chips.length) ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "dt-lp-chips dt-lp-chips--inverse", "aria-label": chipsLabel, children: chips.map((k) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("li", { children: k }, k)) }) : null,
@@ -924,7 +931,7 @@ var DEFAULT_DEMO_FIELDS = [
924
931
  { name: "email", label: "Email", type: "email", maxLength: 190, autoComplete: "email" },
925
932
  { name: "company", label: "Business name", maxLength: 160, autoComplete: "organization" }
926
933
  ];
927
- var initial = (fields) => Object.fromEntries(fields.map((f) => [f.name, ""]));
934
+ var initial = (fields2) => Object.fromEntries(fields2.map((f) => [f.name, ""]));
928
935
  function Field({ f, value, onChange, error }) {
929
936
  const id = `dt-demo-${f.name}`;
930
937
  const errId = `${id}-error`;
@@ -974,9 +981,9 @@ function DemoDialog({
974
981
  onClose = () => {
975
982
  },
976
983
  onSubmit,
977
- fields = DEFAULT_DEMO_FIELDS,
978
- title,
979
- intro = "Tell us a little about your business and we will walk you through the workflows that matter to your team.",
984
+ fields: fields2 = DEFAULT_DEMO_FIELDS,
985
+ title: title2,
986
+ intro: intro2 = "Tell us a little about your business and we will walk you through the workflows that matter to your team.",
980
987
  fiduciary = "Dreamtree-Org",
981
988
  consentText,
982
989
  privacyHref = "/privacy",
@@ -986,7 +993,7 @@ function DemoDialog({
986
993
  validate
987
994
  }) {
988
995
  const ref = (0, import_react8.useRef)(null);
989
- const [form, setForm] = (0, import_react8.useState)(() => initial(fields));
996
+ const [form, setForm] = (0, import_react8.useState)(() => initial(fields2));
990
997
  const [website, setWebsite] = (0, import_react8.useState)("");
991
998
  const [consent, setConsent] = (0, import_react8.useState)(false);
992
999
  const [busy, setBusy] = (0, import_react8.useState)(false);
@@ -1006,7 +1013,7 @@ function DemoDialog({
1006
1013
  setForm((f) => ({ ...f, [k]: e.target.value }));
1007
1014
  if (fieldErrors[k]) setFieldErrors(({ [k]: _gone, ...rest }) => rest);
1008
1015
  };
1009
- const ready = consent && (typeof validate === "function" || fields.every((f) => !f.required || String(form[f.name] || "").trim()));
1016
+ const ready = consent && (typeof validate === "function" || fields2.every((f) => !f.required || String(form[f.name] || "").trim()));
1010
1017
  const submit = async (e) => {
1011
1018
  e.preventDefault();
1012
1019
  if (!ready || busy) return;
@@ -1043,16 +1050,16 @@ function DemoDialog({
1043
1050
  },
1044
1051
  children: [
1045
1052
  /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "dt-lp-dialog-head", children: [
1046
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h2", { id: "dt-demo-title", children: sent ? "Thank you" : title || `Book a ${name} demo` }),
1053
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h2", { id: "dt-demo-title", children: sent ? "Thank you" : title2 || `Book a ${name} demo` }),
1047
1054
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", className: "dt-lp-dialog-x", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Close, { size: 18 }) })
1048
1055
  ] }),
1049
1056
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "dt-lp-dialog-body", children: sent ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "dt-lp-stack", children: [
1050
1057
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { role: "status", children: thanks }),
1051
1058
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", className: "dt-lp-btn dt-lp-btn--outline dt-lp-btn--md", onClick: onClose, children: "Close" })
1052
1059
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("form", { className: "dt-lp-stack", onSubmit: submit, noValidate: true, children: [
1053
- intro ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "dt-lp-muted", children: intro }) : null,
1060
+ intro2 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "dt-lp-muted", children: intro2 }) : null,
1054
1061
  err ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { role: "alert", className: "dt-lp-error", children: err }) : null,
1055
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "dt-lp-fields", children: fields.map((f) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Field, { f, value: form[f.name], onChange: set(f.name), error: fieldErrors[f.name] }, f.name)) }),
1062
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "dt-lp-fields", children: fields2.map((f) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Field, { f, value: form[f.name], onChange: set(f.name), error: fieldErrors[f.name] }, f.name)) }),
1056
1063
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { "aria-hidden": "true", className: "dt-lp-honeypot", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("label", { children: [
1057
1064
  "Website",
1058
1065
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("input", { type: "text", name: "website", tabIndex: -1, autoComplete: "off", value: website, onChange: (e) => setWebsite(e.target.value) })
@@ -1089,11 +1096,11 @@ function Breadcrumbs({ items = [] }) {
1089
1096
  ] }, `${c.label}-${i}`);
1090
1097
  }) }) });
1091
1098
  }
1092
- function PageHero({ breadcrumbs, eyebrow, title, lead, cta = "Book a demo", secondary }) {
1099
+ function PageHero({ breadcrumbs, eyebrow, title: title2, lead, cta = "Book a demo", secondary }) {
1093
1100
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("section", { "aria-labelledby": "hero-title", className: "dt-lp-hero dt-lp-hero--page", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Wrap, { className: "dt-lp-hero-inner dt-lp-hero-inner--start", children: [
1094
1101
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Breadcrumbs, { items: breadcrumbs }),
1095
1102
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Eyebrow, { className: "dt-lp-mt-lg", children: eyebrow }),
1096
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("h1", { id: "hero-title", className: "dt-lp-h1 dt-lp-h1--page", children: title }),
1103
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("h1", { id: "hero-title", className: "dt-lp-h1 dt-lp-h1--page", children: title2 }),
1097
1104
  lead ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { id: "answer", className: "dt-lp-hero-lead", children: lead }) : null,
1098
1105
  /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "dt-lp-actions dt-lp-actions--start", children: [
1099
1106
  cta ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(BookDemoButton, { size: "lg", children: cta }) : null,
@@ -1280,3 +1287,1009 @@ function toLandingBrand(file, copy = {}) {
1280
1287
  };
1281
1288
  return Object.fromEntries(Object.entries(brand).filter(([, v]) => v !== void 0));
1282
1289
  }
1290
+
1291
+ // src/tenant/TenantPage.jsx
1292
+ var import_react11 = require("react");
1293
+
1294
+ // src/schema/sections.js
1295
+ var ICON_NAMES = [
1296
+ "shield-check",
1297
+ "badge-check",
1298
+ "truck",
1299
+ "wrench",
1300
+ "refresh",
1301
+ "battery",
1302
+ "cpu",
1303
+ "laptop",
1304
+ "star",
1305
+ "headset",
1306
+ "rupee",
1307
+ "award",
1308
+ "clock",
1309
+ "map-pin",
1310
+ "phone",
1311
+ "check",
1312
+ "sparkles",
1313
+ "tag",
1314
+ "gift",
1315
+ "heart"
1316
+ ];
1317
+ var TONES = [
1318
+ { value: "plain", label: "Plain" },
1319
+ { value: "tinted", label: "Brand tint" },
1320
+ { value: "muted", label: "Muted" },
1321
+ { value: "dark", label: "Dark" }
1322
+ ];
1323
+ var tone = (d = "plain") => ({ key: "tone", kind: "select", label: "Background", options: TONES, default: d });
1324
+ var title = (d = "", max = 90) => ({ key: "title", kind: "text", label: "Heading", max, default: d });
1325
+ var intro = { key: "intro", kind: "textarea", label: "Intro", max: 240, default: "" };
1326
+ var ctaLabel = { key: "cta_label", kind: "text", label: "Button text", max: 30, default: "" };
1327
+ var ctaHref = { key: "cta_href", kind: "url", label: "Button link", default: "" };
1328
+ var SECTION_TYPES = {
1329
+ hero_slider: {
1330
+ label: "Hero slider",
1331
+ description: "Full-width slides with an image, heading, text and a button.",
1332
+ fields: [
1333
+ {
1334
+ key: "slides",
1335
+ kind: "list",
1336
+ label: "Slides",
1337
+ min: 1,
1338
+ max: 6,
1339
+ itemLabel: "heading",
1340
+ of: [
1341
+ { key: "image", kind: "image", label: "Image (wide, 16:7)", default: null },
1342
+ { key: "mobile_image", kind: "image", label: "Phone image (4:5, optional)", default: null },
1343
+ { key: "eyebrow", kind: "text", label: "Small label above", max: 60, default: "" },
1344
+ { key: "heading", kind: "text", label: "Heading", max: 90, required: true, default: "Your headline" },
1345
+ { key: "text", kind: "textarea", label: "Text", max: 220, default: "" },
1346
+ ctaLabel,
1347
+ ctaHref,
1348
+ { key: "align", kind: "select", label: "Text position", options: [{ value: "start", label: "Left" }, { value: "center", label: "Centre" }], default: "start" },
1349
+ { key: "overlay", kind: "select", label: "Darken image", options: [{ value: "none", label: "None" }, { value: "light", label: "Light" }, { value: "strong", label: "Strong" }], default: "light" }
1350
+ ],
1351
+ default: [{ heading: "Your headline" }]
1352
+ },
1353
+ { key: "height", kind: "select", label: "Height", options: [{ value: "compact", label: "Compact" }, { value: "standard", label: "Standard" }, { value: "tall", label: "Tall" }], default: "standard" },
1354
+ { key: "autoplay", kind: "bool", label: "Move to the next slide automatically", default: true },
1355
+ { key: "interval", kind: "select", label: "Seconds per slide", options: [{ value: "4", label: "4" }, { value: "6", label: "6" }, { value: "8", label: "8" }], default: "6" }
1356
+ ]
1357
+ },
1358
+ featured: {
1359
+ label: "Featured products",
1360
+ description: "The store's newest products (filled in by the app).",
1361
+ slot: true,
1362
+ fields: [title("Featured"), intro, { key: "limit", kind: "select", label: "How many", options: [{ value: "4", label: "4" }, { value: "8", label: "8" }, { value: "12", label: "12" }], default: "8" }, tone()]
1363
+ },
1364
+ categories: {
1365
+ label: "Shop by category",
1366
+ description: "Category tiles (filled in by the app).",
1367
+ slot: true,
1368
+ fields: [title("Shop by category"), tone()]
1369
+ },
1370
+ promo_banner: {
1371
+ label: "Promo banner",
1372
+ description: "A highlighted offer with an optional image and button.",
1373
+ fields: [
1374
+ { key: "eyebrow", kind: "text", label: "Small label above", max: 60, default: "" },
1375
+ { key: "heading", kind: "text", label: "Heading", max: 90, required: true, default: "Festive offer" },
1376
+ { key: "text", kind: "textarea", label: "Text", max: 240, default: "" },
1377
+ ctaLabel,
1378
+ ctaHref,
1379
+ { key: "image", kind: "image", label: "Image (optional)", default: null },
1380
+ { key: "style", kind: "select", label: "Style", options: [{ value: "brand", label: "Brand colour" }, { value: "accent", label: "Accent colour" }, { value: "dark", label: "Dark" }], default: "brand" }
1381
+ ]
1382
+ },
1383
+ trust_badges: {
1384
+ label: "Trust badges",
1385
+ description: "Why buy from you: icon, title and a line each.",
1386
+ fields: [
1387
+ title("Why buy from us"),
1388
+ intro,
1389
+ {
1390
+ key: "items",
1391
+ kind: "list",
1392
+ label: "Badges",
1393
+ min: 1,
1394
+ max: 8,
1395
+ itemLabel: "title",
1396
+ of: [
1397
+ { key: "icon", kind: "icon", label: "Icon", default: "shield-check" },
1398
+ { key: "title", kind: "text", label: "Title", max: 50, required: true, default: "Inspected and graded" },
1399
+ { key: "text", kind: "textarea", label: "Text", max: 140, default: "" }
1400
+ ],
1401
+ default: [{ title: "Inspected and graded" }]
1402
+ },
1403
+ tone()
1404
+ ]
1405
+ },
1406
+ cta_cards: {
1407
+ label: "Action cards",
1408
+ description: "Two to four cards that each lead somewhere (repairs, sell your laptop\u2026).",
1409
+ fields: [
1410
+ title("", 90),
1411
+ {
1412
+ key: "items",
1413
+ kind: "list",
1414
+ label: "Cards",
1415
+ min: 1,
1416
+ max: 4,
1417
+ itemLabel: "title",
1418
+ of: [
1419
+ { key: "icon", kind: "icon", label: "Icon", default: "wrench" },
1420
+ { key: "title", kind: "text", label: "Title", max: 50, required: true, default: "Book a repair" },
1421
+ { key: "text", kind: "textarea", label: "Text", max: 140, default: "" },
1422
+ { key: "cta_label", kind: "text", label: "Link text", max: 30, default: "Learn more" },
1423
+ { key: "cta_href", kind: "url", label: "Link", required: true, default: "/" }
1424
+ ],
1425
+ default: [{ title: "Book a repair", cta_href: "/" }]
1426
+ },
1427
+ tone()
1428
+ ]
1429
+ },
1430
+ testimonials: {
1431
+ label: "Testimonials",
1432
+ description: "What customers say.",
1433
+ fields: [
1434
+ title("What customers say"),
1435
+ {
1436
+ key: "items",
1437
+ kind: "list",
1438
+ label: "Quotes",
1439
+ min: 1,
1440
+ max: 12,
1441
+ itemLabel: "name",
1442
+ of: [
1443
+ { key: "quote", kind: "textarea", label: "Quote", max: 400, required: true, default: "Great service." },
1444
+ { key: "name", kind: "text", label: "Name", max: 60, required: true, default: "A happy customer" },
1445
+ { key: "detail", kind: "text", label: "Detail (city, purchase)", max: 80, default: "" },
1446
+ { key: "rating", kind: "select", label: "Stars", options: ["5", "4", "3", "2", "1"].map((v) => ({ value: v, label: v })), default: "5" }
1447
+ ],
1448
+ default: [{ quote: "Great service.", name: "A happy customer" }]
1449
+ },
1450
+ tone("muted")
1451
+ ]
1452
+ },
1453
+ faq: {
1454
+ label: "FAQ",
1455
+ description: "Questions and answers (also used for search snippets).",
1456
+ fields: [
1457
+ title("Frequently asked questions"),
1458
+ intro,
1459
+ {
1460
+ key: "items",
1461
+ kind: "list",
1462
+ label: "Questions",
1463
+ min: 1,
1464
+ max: 20,
1465
+ itemLabel: "q",
1466
+ of: [
1467
+ { key: "q", kind: "text", label: "Question", max: 160, required: true, default: "A question?" },
1468
+ { key: "a", kind: "textarea", label: "Answer", max: 800, required: true, default: "The answer." }
1469
+ ],
1470
+ default: [{ q: "A question?", a: "The answer." }]
1471
+ },
1472
+ tone("muted")
1473
+ ]
1474
+ },
1475
+ rich_text: {
1476
+ label: "Text",
1477
+ description: "Free text with headings, lists and links.",
1478
+ fields: [title("", 90), { key: "html", kind: "richtext", label: "Text", max: 8e3, default: "<p>Write something about your store.</p>" }, { key: "width", kind: "select", label: "Width", options: [{ value: "narrow", label: "Narrow" }, { value: "wide", label: "Wide" }], default: "narrow" }, tone()]
1479
+ },
1480
+ usp_strip: {
1481
+ label: "Highlights strip",
1482
+ description: "A short row of selling points under the hero.",
1483
+ fields: [
1484
+ {
1485
+ key: "items",
1486
+ kind: "list",
1487
+ label: "Highlights",
1488
+ min: 2,
1489
+ max: 6,
1490
+ itemLabel: "title",
1491
+ of: [
1492
+ { key: "icon", kind: "icon", label: "Icon", default: "check" },
1493
+ { key: "title", kind: "text", label: "Title", max: 40, required: true, default: "Free delivery" },
1494
+ { key: "text", kind: "text", label: "Line", max: 80, default: "" }
1495
+ ],
1496
+ default: [{ title: "Free delivery" }, { title: "Warranty included" }]
1497
+ },
1498
+ tone("tinted")
1499
+ ]
1500
+ },
1501
+ brand_strip: {
1502
+ label: "Brands we sell",
1503
+ description: "Brand names or logos.",
1504
+ fields: [
1505
+ title("Brands we sell"),
1506
+ {
1507
+ key: "items",
1508
+ kind: "list",
1509
+ label: "Brands",
1510
+ min: 1,
1511
+ max: 16,
1512
+ itemLabel: "name",
1513
+ of: [
1514
+ { key: "name", kind: "text", label: "Brand", max: 40, required: true, default: "Brand" },
1515
+ { key: "logo", kind: "image", label: "Logo (optional)", default: null }
1516
+ ],
1517
+ default: [{ name: "Brand" }]
1518
+ },
1519
+ tone()
1520
+ ]
1521
+ },
1522
+ newsletter: {
1523
+ label: "Newsletter sign-up",
1524
+ description: "Email sign-up with consent; the app decides where it goes.",
1525
+ fields: [
1526
+ { key: "title", kind: "text", label: "Heading", max: 90, required: true, default: "Get new arrivals first" },
1527
+ { key: "text", kind: "textarea", label: "Text", max: 200, default: "" },
1528
+ { key: "button_label", kind: "text", label: "Button text", max: 30, required: true, default: "Subscribe" },
1529
+ { key: "consent_text", kind: "text", label: "Consent line", max: 160, required: true, default: "I agree to receive emails from this store. I can unsubscribe any time." },
1530
+ { key: "success_text", kind: "text", label: "Thank-you message", max: 120, default: "Thanks! You are on the list." },
1531
+ tone("tinted")
1532
+ ]
1533
+ },
1534
+ gallery: {
1535
+ label: "Gallery",
1536
+ description: "Photos of your store, workshop or products.",
1537
+ fields: [
1538
+ title("Inside our store"),
1539
+ {
1540
+ key: "items",
1541
+ kind: "list",
1542
+ label: "Photos",
1543
+ min: 1,
1544
+ max: 12,
1545
+ itemLabel: "caption",
1546
+ of: [
1547
+ { key: "image", kind: "image", label: "Photo", required: true, default: null },
1548
+ { key: "caption", kind: "text", label: "Caption", max: 100, default: "" }
1549
+ ],
1550
+ default: []
1551
+ },
1552
+ { key: "columns", kind: "select", label: "Columns", options: [{ value: "2", label: "2" }, { value: "3", label: "3" }, { value: "4", label: "4" }], default: "3" },
1553
+ tone()
1554
+ ]
1555
+ }
1556
+ };
1557
+ var SECTION_TYPE_KEYS = Object.keys(SECTION_TYPES);
1558
+ var PAGE_VERSION = 1;
1559
+ var MAX_SECTIONS = 20;
1560
+
1561
+ // src/schema/richtext.js
1562
+ var ALLOWED = /* @__PURE__ */ new Set(["p", "br", "h2", "h3", "h4", "strong", "b", "em", "i", "u", "ul", "ol", "li", "a", "blockquote"]);
1563
+ var VOID = /* @__PURE__ */ new Set(["br"]);
1564
+ var DROP_WITH_CONTENT = /<(script|style|iframe|object|embed|template|noscript|svg|math|textarea|select|title|head)\b[\s\S]*?<\/\1\s*>/gi;
1565
+ var TAG = /<!--[\s\S]*?-->|<\/?([a-zA-Z][a-zA-Z0-9]*)\b([^>]*)>/g;
1566
+ var escapeText = (s) => s.replace(/&(?!(?:[a-z][a-z0-9]{1,31}|#\d{1,7}|#x[0-9a-f]{1,6});)/gi, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1567
+ var escapeAttr = (s) => s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1568
+ var decode = (s) => s.replace(/&(amp|quot|#39|lt|gt|#x[0-9a-f]+|#\d+);/gi, (m, e) => {
1569
+ const k = e.toLowerCase();
1570
+ if (k === "amp") return "&";
1571
+ if (k === "quot") return '"';
1572
+ if (k === "#39") return "'";
1573
+ if (k === "lt") return "<";
1574
+ if (k === "gt") return ">";
1575
+ const code = k.startsWith("#x") ? parseInt(k.slice(2), 16) : parseInt(k.slice(1), 10);
1576
+ return Number.isFinite(code) && code < 1114112 ? String.fromCodePoint(code) : "";
1577
+ });
1578
+ function safeHref(raw) {
1579
+ const v = decode(String(raw || "")).replace(/[\u0000- \u007f-\u009f]/g, "");
1580
+ if (!v) return null;
1581
+ if (/^(https?:\/\/|mailto:|tel:)/i.test(v)) return v;
1582
+ if (/^\/(?!\/)/.test(v) || v.startsWith("#")) return v;
1583
+ return null;
1584
+ }
1585
+ function hrefOf(attrs) {
1586
+ var _a, _b;
1587
+ const m = /\bhref\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/i.exec(attrs);
1588
+ return m ? safeHref((_b = (_a = m[1]) != null ? _a : m[2]) != null ? _b : m[3]) : null;
1589
+ }
1590
+ function sanitizeRichText(html, { maxLength = 2e4 } = {}) {
1591
+ const src = String(html == null ? "" : html).slice(0, maxLength * 4).replace(/<[a-zA-Z/!][^>]*$/, "").replace(DROP_WITH_CONTENT, "");
1592
+ let out = "";
1593
+ let last = 0;
1594
+ for (const m of src.matchAll(TAG)) {
1595
+ out += escapeText(src.slice(last, m.index));
1596
+ last = m.index + m[0].length;
1597
+ const name = (m[1] || "").toLowerCase();
1598
+ if (!name || !ALLOWED.has(name)) continue;
1599
+ const closing = m[0][1] === "/";
1600
+ if (closing) {
1601
+ if (!VOID.has(name)) out += `</${name}>`;
1602
+ } else if (name === "a") {
1603
+ const href = hrefOf(m[2] || "");
1604
+ out += href ? `<a href="${escapeAttr(href)}" rel="nofollow noopener noreferrer">` : "<a>";
1605
+ } else {
1606
+ out += `<${name}>`;
1607
+ }
1608
+ }
1609
+ out += escapeText(src.slice(last));
1610
+ return out.length > maxLength ? out.slice(0, maxLength).replace(/<[^>]*$/, "") : out;
1611
+ }
1612
+
1613
+ // src/schema/validate.js
1614
+ var ID_RE = /^[a-z0-9][a-z0-9-]{0,39}$/;
1615
+ var MEDIA_RE = /^[A-Za-z0-9_-]{1,64}$/;
1616
+ var CONTROL = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g;
1617
+ function field(f, value, path, errors) {
1618
+ var _a, _b, _c, _d;
1619
+ const absent = value === void 0 || value === null;
1620
+ const missing = absent || value === "";
1621
+ switch (f.kind) {
1622
+ case "text":
1623
+ case "textarea": {
1624
+ let v = absent ? (_a = f.default) != null ? _a : "" : value;
1625
+ if (typeof v !== "string") {
1626
+ errors.push({ path, message: "must be text" });
1627
+ return (_b = f.default) != null ? _b : "";
1628
+ }
1629
+ v = v.replace(CONTROL, "");
1630
+ v = f.kind === "text" ? v.replace(/\s+/g, " ").trim() : v.trim();
1631
+ if (f.max && v.length > f.max) errors.push({ path, message: `must be at most ${f.max} characters` });
1632
+ if (f.required && !v) errors.push({ path, message: "is required" });
1633
+ return v.slice(0, f.max || v.length);
1634
+ }
1635
+ case "url": {
1636
+ if (missing) {
1637
+ if (f.required) errors.push({ path, message: "is required" });
1638
+ return "";
1639
+ }
1640
+ const v = typeof value === "string" ? safeHref(value.trim()) : null;
1641
+ if (!v || v.length > 500) {
1642
+ errors.push({ path, message: "must be a page on this site (/\u2026), a full https:// address, mailto: or tel:" });
1643
+ return "";
1644
+ }
1645
+ return v;
1646
+ }
1647
+ case "image": {
1648
+ if (missing) {
1649
+ if (f.required) errors.push({ path, message: "is required" });
1650
+ return null;
1651
+ }
1652
+ if (typeof value !== "object" || !MEDIA_RE.test(String((_c = value.media_id) != null ? _c : ""))) {
1653
+ errors.push({ path, message: "must be an uploaded image" });
1654
+ return null;
1655
+ }
1656
+ return { media_id: typeof value.media_id === "number" ? value.media_id : String(value.media_id), alt: String((_d = value.alt) != null ? _d : "").replace(CONTROL, "").slice(0, 120) };
1657
+ }
1658
+ case "icon": {
1659
+ const v = absent ? f.default : value;
1660
+ if (!ICON_NAMES.includes(v)) {
1661
+ errors.push({ path, message: `must be one of the icon names (${ICON_NAMES.slice(0, 4).join(", ")}\u2026)` });
1662
+ return f.default;
1663
+ }
1664
+ return v;
1665
+ }
1666
+ case "select": {
1667
+ const v = missing ? f.default : String(value);
1668
+ if (!f.options.some((o) => o.value === v)) {
1669
+ errors.push({ path, message: `must be one of ${f.options.map((o) => o.value).join(", ")}` });
1670
+ return f.default;
1671
+ }
1672
+ return v;
1673
+ }
1674
+ case "bool":
1675
+ if (missing) return f.default;
1676
+ if (typeof value !== "boolean") {
1677
+ errors.push({ path, message: "must be true or false" });
1678
+ return f.default;
1679
+ }
1680
+ return value;
1681
+ case "richtext": {
1682
+ const raw = absent ? f.default : value;
1683
+ if (typeof raw !== "string") {
1684
+ errors.push({ path, message: "must be text" });
1685
+ return f.default;
1686
+ }
1687
+ if (f.max && raw.length > f.max * 2) errors.push({ path, message: `must be at most ${f.max} characters` });
1688
+ return sanitizeRichText(raw, { maxLength: f.max });
1689
+ }
1690
+ case "list": {
1691
+ const arr = absent ? f.default || [] : value;
1692
+ if (!Array.isArray(arr)) {
1693
+ errors.push({ path, message: "must be a list" });
1694
+ return [];
1695
+ }
1696
+ if (f.min && arr.length < f.min) errors.push({ path, message: `needs at least ${f.min}` });
1697
+ if (f.max && arr.length > f.max) errors.push({ path, message: `can have at most ${f.max}` });
1698
+ return arr.slice(0, f.max || arr.length).map((item, i) => fields(f.of, item, `${path}[${i}]`, errors));
1699
+ }
1700
+ default:
1701
+ throw new Error(`Unknown field kind "${f.kind}" (${path})`);
1702
+ }
1703
+ }
1704
+ function fields(defs, obj, path, errors) {
1705
+ const src = obj && typeof obj === "object" && !Array.isArray(obj) ? obj : {};
1706
+ if (obj != null && src !== obj) errors.push({ path, message: "must be an object" });
1707
+ return Object.fromEntries(defs.map((f) => [f.key, field(f, Object.prototype.hasOwnProperty.call(src, f.key) ? src[f.key] : void 0, `${path}.${f.key}`, errors)]));
1708
+ }
1709
+ function validatePage(input) {
1710
+ const errors = [];
1711
+ const doc = input && typeof input === "object" && !Array.isArray(input) ? input : null;
1712
+ if (!doc) return { ok: false, errors: [{ path: "page", message: "must be an object" }], page: { version: PAGE_VERSION, sections: [] } };
1713
+ if (doc.version != null && doc.version !== PAGE_VERSION) errors.push({ path: "version", message: `must be ${PAGE_VERSION}` });
1714
+ const list = Array.isArray(doc.sections) ? doc.sections : [];
1715
+ if (!Array.isArray(doc.sections)) errors.push({ path: "sections", message: "must be a list" });
1716
+ if (list.length > MAX_SECTIONS) errors.push({ path: "sections", message: `can have at most ${MAX_SECTIONS}` });
1717
+ const seen = /* @__PURE__ */ new Set();
1718
+ const sections = [];
1719
+ list.slice(0, MAX_SECTIONS).forEach((s, i) => {
1720
+ var _a, _b;
1721
+ const path = `sections[${i}]`;
1722
+ if (!s || typeof s !== "object") {
1723
+ errors.push({ path, message: "must be an object" });
1724
+ return;
1725
+ }
1726
+ const def = SECTION_TYPES[s.type];
1727
+ if (!def) {
1728
+ errors.push({ path: `${path}.type`, message: `unknown section type "${s.type}"` });
1729
+ return;
1730
+ }
1731
+ const id = String((_a = s.id) != null ? _a : "");
1732
+ if (!ID_RE.test(id)) errors.push({ path: `${path}.id`, message: "must be 1\u201340 lowercase letters, digits or dashes" });
1733
+ else if (seen.has(id)) errors.push({ path: `${path}.id`, message: `duplicate id "${id}"` });
1734
+ seen.add(id);
1735
+ const enabled = s.enabled === void 0 ? true : s.enabled;
1736
+ if (typeof enabled !== "boolean") errors.push({ path: `${path}.enabled`, message: "must be true or false" });
1737
+ sections.push({ id, type: s.type, enabled: enabled !== false, props: fields(def.fields, (_b = s.props) != null ? _b : {}, `${path}.props`, errors) });
1738
+ });
1739
+ return { ok: errors.length === 0, errors, page: { version: PAGE_VERSION, sections } };
1740
+ }
1741
+
1742
+ // src/schema/color.js
1743
+ var HEX_RE = /^#([0-9a-f]{6})$/i;
1744
+ var isHex = (v) => typeof v === "string" && HEX_RE.test(v);
1745
+ function hexToRgb(hex) {
1746
+ const m = HEX_RE.exec(String(hex || ""));
1747
+ if (!m) throw new Error(`Not a #rrggbb colour: ${hex}`);
1748
+ const n = parseInt(m[1], 16);
1749
+ return [n >> 16 & 255, n >> 8 & 255, n & 255];
1750
+ }
1751
+ function rgbToHex([r, g, b]) {
1752
+ const c = (x) => Math.round(Math.min(255, Math.max(0, x))).toString(16).padStart(2, "0");
1753
+ return `#${c(r)}${c(g)}${c(b)}`;
1754
+ }
1755
+ var toLinear = (c) => {
1756
+ const x = c / 255;
1757
+ return x <= 0.04045 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4;
1758
+ };
1759
+ var fromLinear = (x) => 255 * (x <= 31308e-7 ? 12.92 * x : 1.055 * x ** (1 / 2.4) - 0.055);
1760
+ function hexToOklch(hex) {
1761
+ const [r, g, b] = hexToRgb(hex).map(toLinear);
1762
+ const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
1763
+ const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
1764
+ const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
1765
+ const L = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s;
1766
+ const A = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s;
1767
+ const B = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s;
1768
+ return { l: L, c: Math.hypot(A, B), h: Math.atan2(B, A) * 180 / Math.PI };
1769
+ }
1770
+ function oklchToLinear({ l, c, h }) {
1771
+ const a = c * Math.cos(h * Math.PI / 180);
1772
+ const b = c * Math.sin(h * Math.PI / 180);
1773
+ const l_ = (l + 0.3963377774 * a + 0.2158037573 * b) ** 3;
1774
+ const m_ = (l - 0.1055613458 * a - 0.0638541728 * b) ** 3;
1775
+ const s_ = (l - 0.0894841775 * a - 1.291485548 * b) ** 3;
1776
+ return [
1777
+ 4.0767416621 * l_ - 3.3077115913 * m_ + 0.2309699292 * s_,
1778
+ -1.2684380046 * l_ + 2.6097574011 * m_ - 0.3413193965 * s_,
1779
+ -0.0041960863 * l_ - 0.7034186147 * m_ + 1.707614701 * s_
1780
+ ];
1781
+ }
1782
+ var inGamut = (rgb) => rgb.every((x) => x >= -1e-4 && x <= 1 + 1e-4);
1783
+ function oklchToHex({ l, c, h }) {
1784
+ let lo = 0;
1785
+ let hi = c;
1786
+ let rgb = oklchToLinear({ l, c, h });
1787
+ if (!inGamut(rgb)) {
1788
+ for (let i = 0; i < 24; i += 1) {
1789
+ const mid = (lo + hi) / 2;
1790
+ if (inGamut(oklchToLinear({ l, c: mid, h }))) lo = mid;
1791
+ else hi = mid;
1792
+ }
1793
+ rgb = oklchToLinear({ l, c: lo, h });
1794
+ }
1795
+ return rgbToHex(rgb.map((x) => fromLinear(Math.min(1, Math.max(0, x)))));
1796
+ }
1797
+ var PALETTE_STEPS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
1798
+ var LIGHTNESS = { 50: 0.975, 100: 0.945, 200: 0.89, 300: 0.81, 400: 0.71, 500: 0.62, 700: 0.46, 800: 0.39, 900: 0.32, 950: 0.24 };
1799
+ var CHROMA = { 50: 0.18, 100: 0.32, 200: 0.55, 300: 0.78, 400: 0.92, 500: 1, 700: 0.92, 800: 0.82, 900: 0.7, 950: 0.58 };
1800
+ function brandPalette(hex) {
1801
+ const base3 = hexToOklch(hex);
1802
+ const out = { 600: rgbToHex(hexToRgb(hex)) };
1803
+ for (const step of PALETTE_STEPS) {
1804
+ if (step === 600) continue;
1805
+ const l = step < 600 ? Math.max(LIGHTNESS[step], Math.min(0.985, base3.l + (0.985 - base3.l) * ((600 - step) / 600))) : Math.min(LIGHTNESS[step], Math.max(0.12, base3.l - base3.l * 0.6 * ((step - 600) / 350)));
1806
+ out[step] = oklchToHex({ l, c: base3.c * CHROMA[step], h: base3.h });
1807
+ }
1808
+ return Object.fromEntries(PALETTE_STEPS.map((s) => [s, out[s]]));
1809
+ }
1810
+ function luminance(hex) {
1811
+ const [r, g, b] = hexToRgb(hex).map(toLinear);
1812
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
1813
+ }
1814
+ function contrastRatio(a, b) {
1815
+ const [x, y] = [luminance(a), luminance(b)].sort((p, q) => q - p);
1816
+ return (x + 0.05) / (y + 0.05);
1817
+ }
1818
+ function readableOn(hex) {
1819
+ return contrastRatio(hex, "#ffffff") >= contrastRatio(hex, "#101828") ? "#ffffff" : "#101828";
1820
+ }
1821
+
1822
+ // src/schema/theme.js
1823
+ var FONT_PAIRS = {
1824
+ system: { label: "System (fastest)", heading: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif", body: "ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif", href: null },
1825
+ inter: { label: "Inter", heading: "Inter, ui-sans-serif, system-ui, sans-serif", body: "Inter, ui-sans-serif, system-ui, sans-serif", href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" },
1826
+ poppins: { label: "Poppins + Inter", heading: "Poppins, ui-sans-serif, system-ui, sans-serif", body: "Inter, ui-sans-serif, system-ui, sans-serif", href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Poppins:wght@600;700&display=swap" },
1827
+ dmsans: { label: "DM Sans", heading: "'DM Sans', ui-sans-serif, system-ui, sans-serif", body: "'DM Sans', ui-sans-serif, system-ui, sans-serif", href: "https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&display=swap" },
1828
+ nunito: { label: "Nunito (rounded)", heading: "Nunito, ui-rounded, ui-sans-serif, system-ui, sans-serif", body: "Nunito, ui-rounded, ui-sans-serif, system-ui, sans-serif", href: "https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;800&display=swap" },
1829
+ serif: { label: "Merriweather + Source Sans", heading: "Merriweather, Georgia, 'Times New Roman', serif", body: "'Source Sans 3', ui-sans-serif, system-ui, sans-serif", href: "https://fonts.googleapis.com/css2?family=Merriweather:wght@700&family=Source+Sans+3:wght@400;600&display=swap" }
1830
+ };
1831
+ var RADII = { none: ["0", "0"], sm: ["0.375rem", "0.5rem"], md: ["0.75rem", "1rem"], lg: ["1rem", "1.5rem"], full: ["1.5rem", "2rem"] };
1832
+ var MODES = ["light", "dark", "auto"];
1833
+ var THEME_PRESETS = {
1834
+ classic: { label: "Classic blue", primary: "#2563eb", accent: "#f59e0b", font: "inter", radius: "md", mode: "light" },
1835
+ forest: { label: "Forest", primary: "#15803d", accent: "#ca8a04", font: "dmsans", radius: "lg", mode: "light" },
1836
+ sunset: { label: "Sunset", primary: "#c2410c", accent: "#0369a1", font: "poppins", radius: "md", mode: "light" },
1837
+ midnight: { label: "Midnight", primary: "#4338ca", accent: "#0891b2", font: "inter", radius: "lg", mode: "dark" },
1838
+ slate: { label: "Minimal slate", primary: "#334155", accent: "#0f766e", font: "system", radius: "sm", mode: "light" },
1839
+ berry: { label: "Berry", primary: "#be185d", accent: "#6d28d9", font: "nunito", radius: "full", mode: "light" }
1840
+ };
1841
+ var { label: _classicLabel, ...classic } = THEME_PRESETS.classic;
1842
+ var DEFAULT_THEME = { preset: "classic", ...classic, logo: null, favicon: null };
1843
+ var mediaRef = (v, path, errors, { alt = false } = {}) => {
1844
+ if (v == null || v === "") return null;
1845
+ if (typeof v !== "object" || v.media_id == null || !/^[A-Za-z0-9_-]{1,64}$/.test(String(v.media_id))) {
1846
+ errors.push({ path, message: "must be an uploaded image" });
1847
+ return null;
1848
+ }
1849
+ const ref = { media_id: typeof v.media_id === "number" ? v.media_id : String(v.media_id) };
1850
+ if (alt) ref.alt = String(v.alt == null ? "" : v.alt).slice(0, 120);
1851
+ return ref;
1852
+ };
1853
+ function validateTheme(input) {
1854
+ const errors = [];
1855
+ const warnings = [];
1856
+ const t = input && typeof input === "object" && !Array.isArray(input) ? input : {};
1857
+ if (input != null && t !== input) errors.push({ path: "theme", message: "must be an object" });
1858
+ const preset = t.preset == null || t.preset === "" ? null : String(t.preset);
1859
+ if (preset && !THEME_PRESETS[preset] && preset !== "custom") errors.push({ path: "preset", message: `must be one of ${Object.keys(THEME_PRESETS).join(", ")} or custom` });
1860
+ const base3 = preset && THEME_PRESETS[preset] || THEME_PRESETS.classic;
1861
+ const color = (key) => {
1862
+ const v = t[key] == null || t[key] === "" ? base3[key] : String(t[key]).toLowerCase();
1863
+ if (!isHex(v)) {
1864
+ errors.push({ path: key, message: "must be a colour like #2563eb" });
1865
+ return base3[key];
1866
+ }
1867
+ return v;
1868
+ };
1869
+ const pick = (key, allowed, fallback) => {
1870
+ const v = t[key] == null || t[key] === "" ? fallback : String(t[key]);
1871
+ if (!allowed.includes(v)) {
1872
+ errors.push({ path: key, message: `must be one of ${allowed.join(", ")}` });
1873
+ return fallback;
1874
+ }
1875
+ return v;
1876
+ };
1877
+ const theme = {
1878
+ preset: preset || "classic",
1879
+ primary: color("primary"),
1880
+ accent: color("accent"),
1881
+ font: pick("font", Object.keys(FONT_PAIRS), base3.font),
1882
+ radius: pick("radius", Object.keys(RADII), base3.radius),
1883
+ mode: pick("mode", MODES, base3.mode),
1884
+ logo: mediaRef(t.logo, "logo", errors, { alt: true }),
1885
+ favicon: mediaRef(t.favicon, "favicon", errors)
1886
+ };
1887
+ const onBrand = contrastRatio(theme.primary, readableOn(theme.primary));
1888
+ if (onBrand < 4.5) warnings.push({ path: "primary", message: `Button text on this colour is hard to read (contrast ${onBrand.toFixed(1)}:1, needs 4.5:1).` });
1889
+ const onPage = contrastRatio(theme.primary, "#ffffff");
1890
+ if (onPage < 3) warnings.push({ path: "primary", message: `Links in this colour are hard to see on white (contrast ${onPage.toFixed(1)}:1, needs 3:1).` });
1891
+ const accentText = contrastRatio(theme.accent, readableOn(theme.accent));
1892
+ if (accentText < 4.5) warnings.push({ path: "accent", message: `Text on the accent colour is hard to read (contrast ${accentText.toFixed(1)}:1, needs 4.5:1).` });
1893
+ return { ok: errors.length === 0, errors, warnings, theme };
1894
+ }
1895
+ function themeVars(input) {
1896
+ const { theme } = validateTheme(input);
1897
+ const font = FONT_PAIRS[theme.font];
1898
+ const [radius, radiusLg] = RADII[theme.radius];
1899
+ return {
1900
+ theme,
1901
+ font,
1902
+ palette: { primary: brandPalette(theme.primary), accent: brandPalette(theme.accent) },
1903
+ style: {
1904
+ "--dt-brand": theme.primary,
1905
+ "--dt-brand-accent": theme.accent,
1906
+ "--dt-brand-contrast": readableOn(theme.primary),
1907
+ "--dt-brand-accent-contrast": readableOn(theme.accent),
1908
+ "--dt-lp-font": font.body,
1909
+ "--dt-lp-font-heading": font.heading,
1910
+ "--dt-lp-radius": radius,
1911
+ "--dt-lp-radius-lg": radiusLg
1912
+ }
1913
+ };
1914
+ }
1915
+
1916
+ // src/tenant/HeroSlider.jsx
1917
+ var import_react9 = require("react");
1918
+
1919
+ // src/tenant/icons.jsx
1920
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1921
+ var base2 = {
1922
+ viewBox: "0 0 24 24",
1923
+ fill: "none",
1924
+ stroke: "currentColor",
1925
+ strokeWidth: 1.8,
1926
+ strokeLinecap: "round",
1927
+ strokeLinejoin: "round",
1928
+ "aria-hidden": "true",
1929
+ focusable: "false"
1930
+ };
1931
+ var PATHS = {
1932
+ "shield-check": /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
1933
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10Z" }),
1934
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "m9 12 2 2 4-4" })
1935
+ ] }),
1936
+ "badge-check": /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
1937
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" }),
1938
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "m9 12 2 2 4-4" })
1939
+ ] }),
1940
+ truck: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
1941
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M14 18V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v11a1 1 0 0 0 1 1h2" }),
1942
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M15 18H9" }),
1943
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M19 18h2a1 1 0 0 0 1-1v-3.65a1 1 0 0 0-.22-.62L18.3 9.38a1 1 0 0 0-.78-.38H14" }),
1944
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "17", cy: "18", r: "2" }),
1945
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "7", cy: "18", r: "2" })
1946
+ ] }),
1947
+ wrench: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z" }),
1948
+ refresh: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
1949
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M3 12a9 9 0 0 1 15-6.7L21 8" }),
1950
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M21 3v5h-5" }),
1951
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M21 12a9 9 0 0 1-15 6.7L3 16" }),
1952
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M8 16H3v5" })
1953
+ ] }),
1954
+ battery: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
1955
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("rect", { x: "2", y: "7", width: "16", height: "10", rx: "2" }),
1956
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M22 11v2M6 11v2M10 11v2" })
1957
+ ] }),
1958
+ cpu: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
1959
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("rect", { x: "4", y: "4", width: "16", height: "16", rx: "2" }),
1960
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("rect", { x: "9", y: "9", width: "6", height: "6" }),
1961
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M15 2v2M15 20v2M2 15h2M2 9h2M20 15h2M20 9h2M9 2v2M9 20v2" })
1962
+ ] }),
1963
+ laptop: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M20 16V7a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v9m16 0H4m16 0 1.28 2.55a1 1 0 0 1-.9 1.45H3.62a1 1 0 0 1-.9-1.45L4 16" }),
1964
+ star: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" }),
1965
+ headset: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M3 14h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-7a9 9 0 0 1 18 0v7a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3" }),
1966
+ rupee: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M6 3h12M6 8h12M6 13l8.5 8M6 13h3M9 13c6.67 0 6.67-10 0-10" }),
1967
+ award: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
1968
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "12", cy: "8", r: "6" }),
1969
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M15.48 12.89 17 22l-5-3-5 3 1.52-9.11" })
1970
+ ] }),
1971
+ clock: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
1972
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1973
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M12 6v6l4 2" })
1974
+ ] }),
1975
+ "map-pin": /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
1976
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z" }),
1977
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "12", cy: "10", r: "3" })
1978
+ ] }),
1979
+ phone: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z" }),
1980
+ check: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M20 6 9 17l-5-5" }),
1981
+ sparkles: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
1982
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M12 3l1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9L12 3z" }),
1983
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M5 3v4M3 5h4M19 17v4M17 19h4" })
1984
+ ] }),
1985
+ tag: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
1986
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M12.59 2.59A2 2 0 0 0 11.17 2H4a2 2 0 0 0-2 2v7.17a2 2 0 0 0 .59 1.42l8.7 8.7a2.43 2.43 0 0 0 3.42 0l6.58-6.58a2.43 2.43 0 0 0 0-3.42z" }),
1987
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "7.5", cy: "7.5", r: "1.5" })
1988
+ ] }),
1989
+ gift: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
1990
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("rect", { x: "3", y: "8", width: "18", height: "4", rx: "1" }),
1991
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M12 8v13M19 12v7a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-7" }),
1992
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M7.5 8a2.5 2.5 0 0 1 0-5C10 3 12 8 12 8s2-5 4.5-5a2.5 2.5 0 0 1 0 5" })
1993
+ ] }),
1994
+ heart: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z" }),
1995
+ // Controls (not offered to content).
1996
+ "chevron-left": /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "m15 18-6-6 6-6" }),
1997
+ "chevron-right": /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "m9 18 6-6-6-6" }),
1998
+ pause: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M8 5v14M16 5v14" }),
1999
+ play: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M7 4.5v15l12-7.5-12-7.5Z" })
2000
+ };
2001
+ function NamedIcon({ name, size = 20, className = "dt-lp-icon" }) {
2002
+ const paths = PATHS[name];
2003
+ if (!paths) return null;
2004
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("svg", { ...base2, width: size, height: size, className, children: paths });
2005
+ }
2006
+
2007
+ // src/tenant/HeroSlider.jsx
2008
+ var import_jsx_runtime23 = require("react/jsx-runtime");
2009
+ var reducedMotion = () => typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
2010
+ function Slide({ slide, index, count, active, headingTag: H, media, eager }) {
2011
+ const wide = media(slide.image, "wide");
2012
+ const narrow = media(slide.mobile_image, "narrow");
2013
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2014
+ "div",
2015
+ {
2016
+ className: `dt-lp-slide dt-lp-slide--${slide.align} dt-lp-slide--overlay-${wide || narrow ? slide.overlay : "none"}${wide || narrow ? " dt-lp-slide--image" : ""}`,
2017
+ role: "group",
2018
+ "aria-roledescription": "slide",
2019
+ "aria-label": `${index + 1} of ${count}`,
2020
+ "aria-hidden": active ? void 0 : "true",
2021
+ "data-active": active ? "" : void 0,
2022
+ children: [
2023
+ wide || narrow ? /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("picture", { className: "dt-lp-slide-media", children: [
2024
+ narrow && wide ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("source", { media: "(max-width: 640px)", srcSet: narrow }) : null,
2025
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("img", { src: wide || narrow, alt: slide.image && slide.image.alt || "", loading: eager ? "eager" : "lazy", decoding: "async" })
2026
+ ] }) : null,
2027
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "dt-lp-wrap dt-lp-slide-body", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "dt-lp-slide-copy", children: [
2028
+ slide.eyebrow ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "dt-lp-eyebrow dt-lp-slide-eyebrow", children: slide.eyebrow }) : null,
2029
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(H, { className: "dt-lp-slide-heading", children: slide.heading }),
2030
+ slide.text ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "dt-lp-slide-text", children: slide.text }) : null,
2031
+ slide.cta_label && slide.cta_href ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(SmartLink, { href: slide.cta_href, className: "dt-lp-btn dt-lp-btn--lg dt-lp-btn--primary dt-lp-slide-cta", tabIndex: active ? void 0 : -1, children: slide.cta_label }) : null
2032
+ ] }) })
2033
+ ]
2034
+ }
2035
+ );
2036
+ }
2037
+ function HeroSlider({ id, slides = [], height = "standard", autoplay = true, interval = "6", first = false, media }) {
2038
+ const count = slides.length;
2039
+ const [index, setIndex] = (0, import_react9.useState)(0);
2040
+ const [playing, setPlaying] = (0, import_react9.useState)(false);
2041
+ const [hold, setHold] = (0, import_react9.useState)(false);
2042
+ const touch = (0, import_react9.useRef)(null);
2043
+ const go = (0, import_react9.useCallback)((i) => setIndex((i % count + count) % count), [count]);
2044
+ (0, import_react9.useEffect)(() => {
2045
+ setPlaying(Boolean(autoplay) && count > 1 && !reducedMotion());
2046
+ }, [autoplay, count]);
2047
+ (0, import_react9.useEffect)(() => {
2048
+ if (!playing || hold || count < 2) return void 0;
2049
+ const t = setTimeout(() => go(index + 1), Math.max(3, Number(interval) || 6) * 1e3);
2050
+ return () => clearTimeout(t);
2051
+ }, [playing, hold, index, interval, count, go]);
2052
+ (0, import_react9.useEffect)(() => {
2053
+ if (index >= count) setIndex(0);
2054
+ }, [count, index]);
2055
+ if (!count) return null;
2056
+ const onKey = (e) => {
2057
+ if (e.key === "ArrowRight") {
2058
+ e.preventDefault();
2059
+ go(index + 1);
2060
+ }
2061
+ if (e.key === "ArrowLeft") {
2062
+ e.preventDefault();
2063
+ go(index - 1);
2064
+ }
2065
+ };
2066
+ const onTouchStart = (e) => {
2067
+ touch.current = e.touches[0].clientX;
2068
+ };
2069
+ const onTouchEnd = (e) => {
2070
+ if (touch.current == null) return;
2071
+ const dx = e.changedTouches[0].clientX - touch.current;
2072
+ touch.current = null;
2073
+ if (Math.abs(dx) > 40) go(index + (dx < 0 ? 1 : -1));
2074
+ };
2075
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2076
+ "section",
2077
+ {
2078
+ id,
2079
+ className: `dt-lp-hero-slider dt-lp-hero-slider--${height}`,
2080
+ "aria-roledescription": "carousel",
2081
+ "aria-label": "Highlights",
2082
+ onKeyDown: onKey,
2083
+ onMouseEnter: () => setHold(true),
2084
+ onMouseLeave: () => setHold(false),
2085
+ onFocus: () => setHold(true),
2086
+ onBlur: () => setHold(false),
2087
+ onTouchStart,
2088
+ onTouchEnd,
2089
+ children: [
2090
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "dt-lp-slides", "aria-live": playing && !hold ? "off" : "polite", children: slides.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Slide, { slide: s, index: i, count, active: i === index, eager: i === 0, headingTag: first && i === 0 ? "h1" : "h2", media }, i)) }),
2091
+ count > 1 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "dt-lp-slider-controls", children: [
2092
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: "dt-lp-slider-btn", "aria-label": "Previous slide", onClick: () => go(index - 1), children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(NamedIcon, { name: "chevron-left", size: 20 }) }),
2093
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "dt-lp-slider-dots", children: slides.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: "dt-lp-slider-dot", "aria-label": `Go to slide ${i + 1}`, "aria-current": i === index ? "true" : void 0, onClick: () => go(i) }, i)) }),
2094
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: "dt-lp-slider-btn", "aria-label": "Next slide", onClick: () => go(index + 1), children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(NamedIcon, { name: "chevron-right", size: 20 }) }),
2095
+ autoplay ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: "dt-lp-slider-btn", "aria-label": playing ? "Pause slides" : "Play slides", onClick: () => setPlaying((p) => !p), children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(NamedIcon, { name: playing ? "pause" : "play", size: 18 }) }) : null
2096
+ ] }) : null
2097
+ ]
2098
+ }
2099
+ );
2100
+ }
2101
+
2102
+ // src/tenant/blocks.jsx
2103
+ var import_react10 = require("react");
2104
+ var import_jsx_runtime24 = require("react/jsx-runtime");
2105
+ var hid = (id) => `${id}-title`;
2106
+ var Heading = ({ id, title: title2, intro: intro2, align = "center" }) => title2 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SectionIntro, { id: hid(id), title: title2, body: intro2, align }) : null;
2107
+ function SlotSection({ id, title: title2, intro: intro2, tone: tone2, slot, ...props }) {
2108
+ const body = typeof slot === "function" ? slot({ title: title2, intro: intro2, ...props }) : slot;
2109
+ if (!body) return null;
2110
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(Section, { id, labelledBy: title2 ? hid(id) : void 0, tone: tone2, className: "dt-lp-slot", children: [
2111
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Heading, { id, title: title2, intro: intro2, align: "start" }),
2112
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "dt-lp-slot-body", children: body })
2113
+ ] });
2114
+ }
2115
+ function PromoBanner({ id, eyebrow, heading, text, cta_label: label, cta_href: href, image, style = "brand", media }) {
2116
+ const src = media(image, "wide");
2117
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("section", { id, "aria-labelledby": hid(id), className: "dt-lp-section dt-lp-promo-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "dt-lp-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: `dt-lp-promo dt-lp-promo--${style}${src ? " dt-lp-promo--image" : ""}`, children: [
2118
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "dt-lp-promo-copy", children: [
2119
+ eyebrow ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "dt-lp-eyebrow dt-lp-eyebrow--inverse", children: eyebrow }) : null,
2120
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h2", { id: hid(id), className: "dt-lp-h2 dt-lp-promo-heading", children: heading }),
2121
+ text ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "dt-lp-promo-text", children: text }) : null,
2122
+ label && href ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SmartLink, { href, className: "dt-lp-btn dt-lp-btn--lg dt-lp-btn--inverse", children: label }) : null
2123
+ ] }),
2124
+ src ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("img", { className: "dt-lp-promo-img", src, alt: image.alt || "", loading: "lazy", decoding: "async" }) : null
2125
+ ] }) }) });
2126
+ }
2127
+ function TrustBadges({ id, title: title2, intro: intro2, items = [], tone: tone2 }) {
2128
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(Section, { id, labelledBy: title2 ? hid(id) : void 0, tone: tone2, children: [
2129
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Heading, { id, title: title2, intro: intro2 }),
2130
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("ul", { className: "dt-lp-tgrid dt-lp-tgrid--4", children: items.map((it, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("li", { className: "dt-lp-card dt-lp-tcard", children: [
2131
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "dt-lp-icon-tile", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(NamedIcon, { name: it.icon, size: 22 }) }),
2132
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "dt-lp-card-text", children: [
2133
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h3", { className: "dt-lp-h3", children: it.title }),
2134
+ it.text ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { children: it.text }) : null
2135
+ ] })
2136
+ ] }, i)) })
2137
+ ] });
2138
+ }
2139
+ function CtaCards({ id, title: title2, items = [], tone: tone2 }) {
2140
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(Section, { id, labelledBy: title2 ? hid(id) : void 0, tone: tone2, children: [
2141
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Heading, { id, title: title2 }),
2142
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("ul", { className: `dt-lp-tgrid dt-lp-tgrid--${Math.min(items.length, 4) || 1}`, children: items.map((it, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(SmartLink, { href: it.cta_href, className: "dt-lp-card dt-lp-tcard dt-lp-ctacard", children: [
2143
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "dt-lp-icon-tile", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(NamedIcon, { name: it.icon, size: 22 }) }),
2144
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "dt-lp-card-text", children: [
2145
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h3", { className: "dt-lp-h3", children: it.title }),
2146
+ it.text ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { children: it.text }) : null,
2147
+ it.cta_label ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { className: "dt-lp-textlink", children: [
2148
+ it.cta_label,
2149
+ " ",
2150
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(NamedIcon, { name: "chevron-right", size: 16 })
2151
+ ] }) : null
2152
+ ] })
2153
+ ] }) }, i)) })
2154
+ ] });
2155
+ }
2156
+ function Testimonials({ id, title: title2, items = [], tone: tone2 }) {
2157
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(Section, { id, labelledBy: title2 ? hid(id) : void 0, tone: tone2, children: [
2158
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Heading, { id, title: title2 }),
2159
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("ul", { className: "dt-lp-tgrid dt-lp-tgrid--3", children: items.map((it, i) => {
2160
+ const stars = Math.max(1, Math.min(5, Number(it.rating) || 5));
2161
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("li", { className: "dt-lp-card dt-lp-quote", children: [
2162
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "dt-lp-stars", "aria-label": `${stars} out of 5 stars`, children: Array.from({ length: 5 }, (_, s) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { "aria-hidden": "true", className: s < stars ? "dt-lp-star dt-lp-star--on" : "dt-lp-star", children: "\u2605" }, s)) }),
2163
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("blockquote", { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { children: it.quote }) }),
2164
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("p", { className: "dt-lp-quote-by", children: [
2165
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("strong", { children: it.name }),
2166
+ it.detail ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { children: [
2167
+ " \xB7 ",
2168
+ it.detail
2169
+ ] }) : null
2170
+ ] })
2171
+ ] }, i);
2172
+ }) })
2173
+ ] });
2174
+ }
2175
+ function FaqSection({ id, title: title2, intro: intro2, items = [], tone: tone2 }) {
2176
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Faq, { id, title: title2, intro: intro2, items, tone: tone2, openFirst: true });
2177
+ }
2178
+ function RichText({ id, title: title2, html, width = "narrow", tone: tone2 }) {
2179
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(Section, { id, labelledBy: title2 ? hid(id) : void 0, tone: tone2, narrow: width === "narrow", children: [
2180
+ title2 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h2", { id: hid(id), className: "dt-lp-h2", children: title2 }) : null,
2181
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "dt-lp-prose", dangerouslySetInnerHTML: { __html: sanitizeRichText(html) } })
2182
+ ] });
2183
+ }
2184
+ function UspStrip({ id, items = [], tone: tone2 = "tinted" }) {
2185
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("section", { id, "aria-label": "Highlights", className: `dt-lp-usp dt-lp-tone--${tone2}`, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "dt-lp-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("ul", { className: "dt-lp-usp-list", children: items.map((it, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("li", { className: "dt-lp-usp-item", children: [
2186
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(NamedIcon, { name: it.icon, size: 20, className: "dt-lp-icon dt-lp-usp-icon" }),
2187
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { children: [
2188
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("strong", { children: it.title }),
2189
+ it.text ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "dt-lp-usp-text", children: it.text }) : null
2190
+ ] })
2191
+ ] }, i)) }) }) });
2192
+ }
2193
+ function BrandStrip({ id, title: title2, items = [], tone: tone2, media }) {
2194
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(Section, { id, labelledBy: title2 ? hid(id) : void 0, tone: tone2, className: "dt-lp-brands", children: [
2195
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Heading, { id, title: title2 }),
2196
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("ul", { className: "dt-lp-brand-list", children: items.map((it, i) => {
2197
+ const src = media(it.logo, "logo");
2198
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("li", { className: "dt-lp-brand-item", children: src ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("img", { src, alt: it.name, loading: "lazy", decoding: "async" }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: it.name }) }, i);
2199
+ }) })
2200
+ ] });
2201
+ }
2202
+ function Newsletter({ id, title: title2, text, button_label: button, consent_text: consentText, success_text: success, tone: tone2, onSubscribe }) {
2203
+ const [email, setEmail] = (0, import_react10.useState)("");
2204
+ const [consent, setConsent] = (0, import_react10.useState)(false);
2205
+ const [state, setState] = (0, import_react10.useState)({ status: "idle", message: "" });
2206
+ const submit = async (e) => {
2207
+ e.preventDefault();
2208
+ if (!onSubscribe) return;
2209
+ setState({ status: "busy", message: "" });
2210
+ try {
2211
+ await onSubscribe({ email: email.trim(), consent, section: id });
2212
+ setState({ status: "done", message: success || "Thanks!" });
2213
+ } catch (err) {
2214
+ setState({ status: "error", message: err && err.message || "Could not subscribe. Please try again." });
2215
+ }
2216
+ };
2217
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(Section, { id, labelledBy: hid(id), tone: tone2, narrow: true, children: [
2218
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SectionIntro, { id: hid(id), title: title2, body: text, align: "center" }),
2219
+ state.status === "done" ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "dt-lp-news-done", role: "status", children: state.message }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("form", { className: "dt-lp-news", onSubmit: submit, children: [
2220
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "dt-lp-news-row", children: [
2221
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("label", { className: "dt-lp-sr", htmlFor: `${id}-email`, children: "Email" }),
2222
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("input", { id: `${id}-email`, type: "email", required: true, autoComplete: "email", placeholder: "you@example.com", value: email, onChange: (e) => setEmail(e.target.value), className: "dt-lp-input" }),
2223
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "submit", className: "dt-lp-btn dt-lp-btn--md dt-lp-btn--primary", disabled: !consent || state.status === "busy", "aria-busy": state.status === "busy" ? "true" : void 0, children: button })
2224
+ ] }),
2225
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("label", { className: "dt-lp-news-consent", children: [
2226
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("input", { type: "checkbox", checked: consent, onChange: (e) => setConsent(e.target.checked), required: true }),
2227
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: consentText })
2228
+ ] }),
2229
+ state.status === "error" ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "dt-lp-news-error", role: "alert", children: state.message }) : null
2230
+ ] })
2231
+ ] });
2232
+ }
2233
+ function Gallery({ id, title: title2, items = [], columns = "3", tone: tone2, media }) {
2234
+ const shown = items.map((it) => ({ ...it, src: media(it.image, "gallery") })).filter((it) => it.src);
2235
+ if (!shown.length) return null;
2236
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(Section, { id, labelledBy: title2 ? hid(id) : void 0, tone: tone2, children: [
2237
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Heading, { id, title: title2 }),
2238
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("ul", { className: `dt-lp-gallery dt-lp-gallery--${columns}`, children: shown.map((it, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("figure", { children: [
2239
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("img", { src: it.src, alt: it.image.alt || it.caption || "", loading: "lazy", decoding: "async" }),
2240
+ it.caption ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("figcaption", { children: it.caption }) : null
2241
+ ] }) }, i)) })
2242
+ ] });
2243
+ }
2244
+
2245
+ // src/tenant/TenantPage.jsx
2246
+ var import_jsx_runtime25 = require("react/jsx-runtime");
2247
+ var TENANT_BLOCKS = {
2248
+ hero_slider: HeroSlider,
2249
+ featured: SlotSection,
2250
+ categories: SlotSection,
2251
+ promo_banner: PromoBanner,
2252
+ trust_badges: TrustBadges,
2253
+ cta_cards: CtaCards,
2254
+ testimonials: Testimonials,
2255
+ faq: FaqSection,
2256
+ rich_text: RichText,
2257
+ usp_strip: UspStrip,
2258
+ brand_strip: BrandStrip,
2259
+ newsletter: Newsletter,
2260
+ gallery: Gallery
2261
+ };
2262
+ var SectionBoundary = class extends import_react11.Component {
2263
+ constructor(props) {
2264
+ super(props);
2265
+ this.state = { failed: false };
2266
+ }
2267
+ static getDerivedStateFromError() {
2268
+ return { failed: true };
2269
+ }
2270
+ render() {
2271
+ return this.state.failed ? null : this.props.children;
2272
+ }
2273
+ };
2274
+ var noMedia = () => null;
2275
+ function TenantPage({
2276
+ page,
2277
+ theme,
2278
+ slots = {},
2279
+ resolveMedia = noMedia,
2280
+ linkComponent = null,
2281
+ onSubscribe = null,
2282
+ storeName = "",
2283
+ className = "",
2284
+ headingStart = 1
2285
+ }) {
2286
+ const doc = (0, import_react11.useMemo)(() => validatePage(page).page, [page]);
2287
+ const vars = (0, import_react11.useMemo)(() => themeVars(theme), [theme]);
2288
+ const ctx = (0, import_react11.useMemo)(() => ({ brand: { id: "tenant", name: storeName, primary: vars.theme.primary }, linkComponent, onBookDemo: null, home: true }), [storeName, vars.theme.primary, linkComponent]);
2289
+ const media = (ref, variant) => ref && ref.media_id != null ? resolveMedia(ref, variant) : null;
2290
+ const visible = doc.sections.filter((s) => s.enabled && TENANT_BLOCKS[s.type]);
2291
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(LandingProvider, { value: ctx, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: `dt-landing dt-lp-tenant ${className}`.trim(), style: vars.style, "data-theme": vars.theme.mode === "auto" ? void 0 : vars.theme.mode, children: visible.map((s, i) => {
2292
+ const Block = TENANT_BLOCKS[s.type];
2293
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(SectionBoundary, { children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Block, { ...s.props, id: `s-${s.id}`, first: i === 0 && headingStart === 1, media, slot: slots[s.type], onSubscribe }) }, s.id);
2294
+ }) }) });
2295
+ }