@canopy-iiif/app 1.9.13 → 1.9.15

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.
@@ -28,6 +28,94 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  mod
29
29
  ));
30
30
 
31
+ // lib/default-locale.js
32
+ var require_default_locale = __commonJS({
33
+ "lib/default-locale.js"(exports, module) {
34
+ "use strict";
35
+ module.exports = {
36
+ common: {
37
+ actions: {
38
+ open: "Open",
39
+ close: "Close",
40
+ clear: "Clear",
41
+ clear_all: "Clear all",
42
+ done: "Done",
43
+ show: "Show",
44
+ hide: "Hide"
45
+ },
46
+ nouns: {
47
+ search: "Search",
48
+ filters: "Filters",
49
+ results: "results",
50
+ values: "values",
51
+ types: "types",
52
+ items: "items",
53
+ navigation: "navigation",
54
+ section_navigation: "section navigation",
55
+ content_navigation: "content navigation",
56
+ primary_navigation: "Primary navigation",
57
+ metadata: "metadata",
58
+ gallery: "gallery",
59
+ gallery_thumbnails: "gallery thumbnails",
60
+ map: "map",
61
+ map_data: "map data",
62
+ map_locations: "map locations",
63
+ item_label: "Item",
64
+ details: "details",
65
+ breadcrumb: "Breadcrumb",
66
+ home: "Home"
67
+ },
68
+ types: {
69
+ work: "Works",
70
+ page: "Pages",
71
+ docs: "Docs"
72
+ },
73
+ statuses: {
74
+ loading: "Loading\u2026",
75
+ empty_short: "No {content}",
76
+ empty_detail: "No {content} available.",
77
+ unavailable: "{content} unavailable.",
78
+ unavailable_detail: "{content} is unavailable.",
79
+ failed: "Unable to load {content}.",
80
+ loading_content: "Loading {content}\u2026",
81
+ summary_content: "Showing {shown} of {total} {content}",
82
+ search_summary: 'Found {shown} of {total} in {type} for "{query}"',
83
+ no_matches: "No matches found."
84
+ },
85
+ phrases: {
86
+ placeholder_search: "Search\u2026",
87
+ results_label: "Search results",
88
+ open_content: "Open {content}",
89
+ close_content: "Close {content}",
90
+ show_content: "Show {content}",
91
+ hide_content: "Hide {content}",
92
+ nav_label: "{content} navigation",
93
+ search_content: "Search {content}",
94
+ filter_values: "Filter {content} values",
95
+ clear_content_search: "Clear {content} search",
96
+ none_applied: "No {content} applied",
97
+ applied_count: "{count} {content} applied",
98
+ toggle_content: "Toggle {content}",
99
+ scroll_direction_content: "Scroll {direction} through {content}",
100
+ step_content: "{direction} {content}",
101
+ item_numbered: "{content} {index}",
102
+ content_key: "{content} key"
103
+ },
104
+ directions: {
105
+ left: "left",
106
+ right: "right",
107
+ previous: "Previous",
108
+ next: "Next"
109
+ },
110
+ misc: {
111
+ referenced_by: "Referenced by",
112
+ on_this_page: "On this page"
113
+ }
114
+ }
115
+ };
116
+ }
117
+ });
118
+
31
119
  // ../../node_modules/slugify/slugify.js
32
120
  var require_slugify = __commonJS({
33
121
  "../../node_modules/slugify/slugify.js"(exports, module) {
@@ -680,7 +768,7 @@ __export(interstitials_exports, {
680
768
  });
681
769
 
682
770
  // ui/src/interstitials/Hero.jsx
683
- import React10 from "react";
771
+ import React12 from "react";
684
772
  import helpers from "@canopy-iiif/app/lib/components/featured.js";
685
773
  import navigationHelpers from "@canopy-iiif/app/lib/components/navigation.js";
686
774
 
@@ -743,8 +831,95 @@ function ButtonWrapper({
743
831
  return /* @__PURE__ */ React9.createElement("div", { className: classes, ...rest }, text && /* @__PURE__ */ React9.createElement("span", { className: "canopy-button-group__text" }, text), /* @__PURE__ */ React9.createElement("div", { className: "canopy-button-group__actions" }, children));
744
832
  }
745
833
 
834
+ // ui/src/locale/index.js
835
+ import React11 from "react";
836
+
837
+ // ui/src/layout/pageContext.js
838
+ import React10 from "react";
839
+ var CONTEXT_KEY = typeof Symbol === "function" ? Symbol.for("__CANOPY_PAGE_CONTEXT__") : "__CANOPY_PAGE_CONTEXT__";
840
+ function getSharedRoot() {
841
+ if (typeof globalThis !== "undefined") return globalThis;
842
+ if (typeof window !== "undefined") return window;
843
+ if (typeof global !== "undefined") return global;
844
+ return null;
845
+ }
846
+ function getSafePageContext() {
847
+ const root = getSharedRoot();
848
+ if (root && root[CONTEXT_KEY]) return root[CONTEXT_KEY];
849
+ const ctx = React10.createContext({ navigation: null, page: null, site: null });
850
+ if (root) root[CONTEXT_KEY] = ctx;
851
+ return ctx;
852
+ }
853
+
854
+ // ui/src/locale/index.js
855
+ var import_default_locale = __toESM(require_default_locale());
856
+ function getGlobalScope() {
857
+ if (typeof globalThis !== "undefined") return globalThis;
858
+ if (typeof window !== "undefined") return window;
859
+ if (typeof global !== "undefined") return global;
860
+ return null;
861
+ }
862
+ function readRuntimeMessages() {
863
+ const scope = getGlobalScope();
864
+ const candidate = scope && scope.CANOPY_LOCALE_MESSAGES;
865
+ if (candidate && typeof candidate === "object") return candidate;
866
+ return import_default_locale.default || {};
867
+ }
868
+ function accessPath(obj, path) {
869
+ if (!obj || typeof obj !== "object" || !path) return void 0;
870
+ const parts = Array.isArray(path) ? path : String(path).split(".");
871
+ let current = obj;
872
+ for (const part of parts) {
873
+ if (!current || typeof current !== "object") return void 0;
874
+ const key = String(part).trim();
875
+ if (!key || !(key in current)) return void 0;
876
+ current = current[key];
877
+ }
878
+ return current;
879
+ }
880
+ function formatTemplate(template, replacements = {}) {
881
+ if (template == null) return template;
882
+ const str = String(template);
883
+ if (!replacements || typeof replacements !== "object") return str;
884
+ return str.replace(/\{([^}]+)\}/g, (match, key) => {
885
+ if (!Object.prototype.hasOwnProperty.call(replacements, key)) return match;
886
+ const value = replacements[key];
887
+ if (value == null) return match;
888
+ return String(value);
889
+ });
890
+ }
891
+ function useLocaleMessages() {
892
+ const PageContext2 = getSafePageContext();
893
+ const context = PageContext2 ? React11.useContext(PageContext2) : null;
894
+ const siteMessages = context && context.site && context.site.localeMessages && typeof context.site.localeMessages === "object" ? context.site.localeMessages : null;
895
+ return siteMessages || readRuntimeMessages();
896
+ }
897
+ function useLocale2() {
898
+ const messages = useLocaleMessages();
899
+ const getString = React11.useCallback(
900
+ (path, fallback) => {
901
+ const value = path ? accessPath(messages, path) : void 0;
902
+ if (typeof value === "string" || typeof value === "number") {
903
+ return value;
904
+ }
905
+ return fallback;
906
+ },
907
+ [messages]
908
+ );
909
+ const formatString = React11.useCallback(
910
+ (path, fallback, replacements) => {
911
+ const template = path ? accessPath(messages, path) : void 0;
912
+ const resolved = template != null ? template : fallback;
913
+ if (resolved == null) return resolved;
914
+ return formatTemplate(resolved, replacements);
915
+ },
916
+ [messages]
917
+ );
918
+ return { messages, getString, formatString };
919
+ }
920
+
746
921
  // ui/src/interstitials/Hero.jsx
747
- var NavIconBase = ({ children, ...rest }) => /* @__PURE__ */ React10.createElement(
922
+ var NavIconBase = ({ children, ...rest }) => /* @__PURE__ */ React12.createElement(
748
923
  "svg",
749
924
  {
750
925
  width: "16",
@@ -758,7 +933,7 @@ var NavIconBase = ({ children, ...rest }) => /* @__PURE__ */ React10.createEleme
758
933
  },
759
934
  children
760
935
  );
761
- var PrevArrowIcon = (props) => /* @__PURE__ */ React10.createElement(NavIconBase, { ...props }, /* @__PURE__ */ React10.createElement(
936
+ var PrevArrowIcon = (props) => /* @__PURE__ */ React12.createElement(NavIconBase, { ...props }, /* @__PURE__ */ React12.createElement(
762
937
  "path",
763
938
  {
764
939
  d: "M10.5 3L5.5 8L10.5 13",
@@ -768,7 +943,7 @@ var PrevArrowIcon = (props) => /* @__PURE__ */ React10.createElement(NavIconBase
768
943
  strokeLinejoin: "round"
769
944
  }
770
945
  ));
771
- var NextArrowIcon = (props) => /* @__PURE__ */ React10.createElement(NavIconBase, { ...props }, /* @__PURE__ */ React10.createElement(
946
+ var NextArrowIcon = (props) => /* @__PURE__ */ React12.createElement(NavIconBase, { ...props }, /* @__PURE__ */ React12.createElement(
772
947
  "path",
773
948
  {
774
949
  d: "M5.5 3L10.5 8L5.5 13",
@@ -885,13 +1060,14 @@ function Hero({
885
1060
  style = {},
886
1061
  background = "theme",
887
1062
  variant = "featured",
888
- homeLabel = "Home",
1063
+ homeLabel,
889
1064
  ...rest
890
1065
  }) {
1066
+ const { getString } = useLocale2();
891
1067
  const normalizedVariant = normalizeVariant(variant);
892
1068
  const isBreadcrumbVariant = normalizedVariant === "breadcrumb";
893
1069
  const PageContext2 = navigationHelpers && typeof navigationHelpers.getPageContext === "function" ? navigationHelpers.getPageContext() : null;
894
- const pageContext = PageContext2 ? React10.useContext(PageContext2) : null;
1070
+ const pageContext = PageContext2 ? React12.useContext(PageContext2) : null;
895
1071
  let orderedSlides = [];
896
1072
  if (!isBreadcrumbVariant) {
897
1073
  const resolved = resolveFeaturedItem({ item, index, random });
@@ -949,10 +1125,10 @@ function Hero({
949
1125
  }
950
1126
  ].filter(Boolean);
951
1127
  const finalOverlayLinks = isBreadcrumbVariant ? normalizedLinks : overlayLinks;
952
- const breadcrumbItems = React10.useMemo(() => {
1128
+ const breadcrumbItems = React12.useMemo(() => {
953
1129
  if (!isBreadcrumbVariant) return [];
954
1130
  const items = [];
955
- const label = typeof homeLabel === "string" ? homeLabel.trim() : "";
1131
+ const label = (homeLabel != null ? homeLabel : getString("common.nouns.home", "Home")).trim();
956
1132
  if (label) {
957
1133
  items.push({ title: label, href: applyBasePath("/") });
958
1134
  }
@@ -972,19 +1148,26 @@ function Hero({
972
1148
  });
973
1149
  return items;
974
1150
  }, [isBreadcrumbVariant, pageContext, homeLabel]);
975
- const breadcrumbNode = isBreadcrumbVariant && breadcrumbItems.length ? /* @__PURE__ */ React10.createElement("nav", { className: "canopy-interstitial__breadcrumb", "aria-label": "Breadcrumb" }, breadcrumbItems.map((item2, idx) => {
976
- const isLast = idx === breadcrumbItems.length - 1;
977
- const key = `${item2.title || idx}-${idx}`;
978
- const content = !isLast && item2.href ? /* @__PURE__ */ React10.createElement("a", { href: item2.href }, item2.title) : /* @__PURE__ */ React10.createElement("span", { className: "canopy-interstitial__breadcrumb-current", "aria-current": "page" }, item2.title);
979
- return /* @__PURE__ */ React10.createElement(React10.Fragment, { key }, idx > 0 ? /* @__PURE__ */ React10.createElement(
980
- "span",
981
- {
982
- className: "canopy-interstitial__breadcrumb-separator",
983
- "aria-hidden": "true"
984
- },
985
- ">"
986
- ) : null, content);
987
- })) : null;
1151
+ const breadcrumbNode = isBreadcrumbVariant && breadcrumbItems.length ? /* @__PURE__ */ React12.createElement(
1152
+ "nav",
1153
+ {
1154
+ className: "canopy-interstitial__breadcrumb",
1155
+ "aria-label": getString("common.nouns.breadcrumb", "Breadcrumb")
1156
+ },
1157
+ breadcrumbItems.map((item2, idx) => {
1158
+ const isLast = idx === breadcrumbItems.length - 1;
1159
+ const key = `${item2.title || idx}-${idx}`;
1160
+ const content = !isLast && item2.href ? /* @__PURE__ */ React12.createElement("a", { href: item2.href }, item2.title) : /* @__PURE__ */ React12.createElement("span", { className: "canopy-interstitial__breadcrumb-current", "aria-current": "page" }, item2.title);
1161
+ return /* @__PURE__ */ React12.createElement(React12.Fragment, { key }, idx > 0 ? /* @__PURE__ */ React12.createElement(
1162
+ "span",
1163
+ {
1164
+ className: "canopy-interstitial__breadcrumb-separator",
1165
+ "aria-hidden": "true"
1166
+ },
1167
+ ">"
1168
+ ) : null, content);
1169
+ })
1170
+ ) : null;
988
1171
  const normalizedBackground = normalizeBackground(background);
989
1172
  const backgroundClassName = normalizedBackground === "transparent" ? "canopy-interstitial--bg-transparent" : "";
990
1173
  const variantClassName = isBreadcrumbVariant ? "canopy-interstitial--hero-breadcrumb" : "canopy-interstitial--hero-featured";
@@ -1027,7 +1210,7 @@ function Hero({
1027
1210
  };
1028
1211
  const wrapWithLink = (node) => {
1029
1212
  if (!safeHref) return node;
1030
- return /* @__PURE__ */ React10.createElement(
1213
+ return /* @__PURE__ */ React12.createElement(
1031
1214
  "a",
1032
1215
  {
1033
1216
  href: safeHref,
@@ -1038,39 +1221,39 @@ function Hero({
1038
1221
  );
1039
1222
  };
1040
1223
  if (isStaticCaption) {
1041
- return /* @__PURE__ */ React10.createElement("div", { className: "swiper-slide", key: safeHref || idx }, wrapWithLink(
1042
- /* @__PURE__ */ React10.createElement("article", { className: paneClassName }, slide.thumbnail ? /* @__PURE__ */ React10.createElement("div", { className: "canopy-interstitial__media-frame" }, /* @__PURE__ */ React10.createElement(
1224
+ return /* @__PURE__ */ React12.createElement("div", { className: "swiper-slide", key: safeHref || idx }, wrapWithLink(
1225
+ /* @__PURE__ */ React12.createElement("article", { className: paneClassName }, slide.thumbnail ? /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__media-frame" }, /* @__PURE__ */ React12.createElement(
1043
1226
  "img",
1044
1227
  {
1045
1228
  ...buildImageProps(
1046
1229
  "canopy-interstitial__media canopy-interstitial__media--static"
1047
1230
  )
1048
1231
  }
1049
- )) : null, slide.title ? /* @__PURE__ */ React10.createElement("div", { className: "canopy-interstitial__caption canopy-interstitial__caption--static" }, /* @__PURE__ */ React10.createElement("span", { className: "canopy-interstitial__caption-link" }, slide.title)) : null)
1232
+ )) : null, slide.title ? /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__caption canopy-interstitial__caption--static" }, /* @__PURE__ */ React12.createElement("span", { className: "canopy-interstitial__caption-link" }, slide.title)) : null)
1050
1233
  ));
1051
1234
  }
1052
- return /* @__PURE__ */ React10.createElement("div", { className: "swiper-slide", key: safeHref || idx }, wrapWithLink(
1053
- /* @__PURE__ */ React10.createElement("article", { className: paneClassName }, slide.thumbnail ? /* @__PURE__ */ React10.createElement("img", { ...buildImageProps("canopy-interstitial__media") }) : null, showVeil ? /* @__PURE__ */ React10.createElement("div", { className: "canopy-interstitial__veil", "aria-hidden": "true" }) : null, slide.title ? /* @__PURE__ */ React10.createElement("div", { className: "canopy-interstitial__caption" }, /* @__PURE__ */ React10.createElement("span", { className: "canopy-interstitial__caption-link" }, slide.title)) : null)
1235
+ return /* @__PURE__ */ React12.createElement("div", { className: "swiper-slide", key: safeHref || idx }, wrapWithLink(
1236
+ /* @__PURE__ */ React12.createElement("article", { className: paneClassName }, slide.thumbnail ? /* @__PURE__ */ React12.createElement("img", { ...buildImageProps("canopy-interstitial__media") }) : null, showVeil ? /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__veil", "aria-hidden": "true" }) : null, slide.title ? /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__caption" }, /* @__PURE__ */ React12.createElement("span", { className: "canopy-interstitial__caption-link" }, slide.title)) : null)
1054
1237
  ));
1055
1238
  };
1056
- const renderSlider = (options = {}) => /* @__PURE__ */ React10.createElement("div", { className: "canopy-interstitial__slider swiper" }, /* @__PURE__ */ React10.createElement("div", { className: "swiper-wrapper" }, orderedSlides.map((slide, idx) => renderSlide(slide, idx, options))), /* @__PURE__ */ React10.createElement("div", { className: "canopy-interstitial__nav" }, /* @__PURE__ */ React10.createElement(
1239
+ const renderSlider = (options = {}) => /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__slider swiper" }, /* @__PURE__ */ React12.createElement("div", { className: "swiper-wrapper" }, orderedSlides.map((slide, idx) => renderSlide(slide, idx, options))), /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__nav" }, /* @__PURE__ */ React12.createElement(
1057
1240
  "button",
1058
1241
  {
1059
1242
  type: "button",
1060
1243
  "aria-label": "Previous slide",
1061
1244
  className: "canopy-interstitial__nav-btn canopy-interstitial__nav-btn--prev swiper-button-prev"
1062
1245
  },
1063
- /* @__PURE__ */ React10.createElement(PrevArrowIcon, null)
1064
- ), /* @__PURE__ */ React10.createElement(
1246
+ /* @__PURE__ */ React12.createElement(PrevArrowIcon, null)
1247
+ ), /* @__PURE__ */ React12.createElement(
1065
1248
  "button",
1066
1249
  {
1067
1250
  type: "button",
1068
1251
  "aria-label": "Next slide",
1069
1252
  className: "canopy-interstitial__nav-btn canopy-interstitial__nav-btn--next swiper-button-next"
1070
1253
  },
1071
- /* @__PURE__ */ React10.createElement(NextArrowIcon, null)
1072
- )), /* @__PURE__ */ React10.createElement("div", { className: "canopy-interstitial__pagination swiper-pagination" }));
1073
- const overlayContent = /* @__PURE__ */ React10.createElement(React10.Fragment, null, overlayTitle ? /* @__PURE__ */ React10.createElement("h1", { className: "canopy-interstitial__headline" }, overlayTitle) : null, derivedDescription ? /* @__PURE__ */ React10.createElement("p", { className: "canopy-interstitial__description" }, derivedDescription) : null, finalOverlayLinks.length ? /* @__PURE__ */ React10.createElement(ButtonWrapper, { className: "canopy-interstitial__actions" }, finalOverlayLinks.map((link) => /* @__PURE__ */ React10.createElement(
1254
+ /* @__PURE__ */ React12.createElement(NextArrowIcon, null)
1255
+ )), /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__pagination swiper-pagination" }));
1256
+ const overlayContent = /* @__PURE__ */ React12.createElement(React12.Fragment, null, overlayTitle ? /* @__PURE__ */ React12.createElement("h1", { className: "canopy-interstitial__headline" }, overlayTitle) : null, derivedDescription ? /* @__PURE__ */ React12.createElement("p", { className: "canopy-interstitial__description" }, derivedDescription) : null, finalOverlayLinks.length ? /* @__PURE__ */ React12.createElement(ButtonWrapper, { className: "canopy-interstitial__actions" }, finalOverlayLinks.map((link) => /* @__PURE__ */ React12.createElement(
1074
1257
  Button,
1075
1258
  {
1076
1259
  key: `${link.href}-${link.title}`,
@@ -1092,34 +1275,42 @@ function Hero({
1092
1275
  } else {
1093
1276
  sectionProps["data-canopy-hero-variant"] = "breadcrumb";
1094
1277
  }
1095
- return /* @__PURE__ */ React10.createElement("section", { ...sectionProps }, isBreadcrumbVariant ? /* @__PURE__ */ React10.createElement("div", { className: "canopy-interstitial__layout canopy-interstitial__layout--breadcrumb" }, /* @__PURE__ */ React10.createElement("div", { className: "canopy-interstitial__panel" }, /* @__PURE__ */ React10.createElement("div", { className: "canopy-interstitial__body" }, breadcrumbNode, overlayContent))) : /* @__PURE__ */ React10.createElement("div", { className: "canopy-interstitial__layout" }, /* @__PURE__ */ React10.createElement("div", { className: "canopy-interstitial__panel" }, /* @__PURE__ */ React10.createElement("div", { className: "canopy-interstitial__body" }, overlayContent)), /* @__PURE__ */ React10.createElement("div", { className: "canopy-interstitial__media-group" }, renderSlider({ showVeil: false, captionVariant: "static" }))));
1278
+ return /* @__PURE__ */ React12.createElement("section", { ...sectionProps }, isBreadcrumbVariant ? /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__layout canopy-interstitial__layout--breadcrumb" }, /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__panel" }, /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__body" }, breadcrumbNode, overlayContent))) : /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__layout" }, /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__panel" }, /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__body" }, overlayContent)), /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__media-group" }, renderSlider({ showVeil: false, captionVariant: "static" }))));
1096
1279
  }
1097
1280
 
1098
1281
  // ui/src/layout/SubNavigation.jsx
1099
- import React12 from "react";
1282
+ import React14 from "react";
1100
1283
  import navigationHelpers2 from "@canopy-iiif/app/lib/components/navigation.js";
1101
1284
 
1102
1285
  // ui/src/layout/NavigationTree.jsx
1103
- import React11 from "react";
1286
+ import React13 from "react";
1104
1287
  function normalizeDepth(depth) {
1105
1288
  if (typeof depth !== "number") return 0;
1106
1289
  return Math.max(0, Math.min(5, depth));
1107
1290
  }
1108
- function NavigationTreeList({ nodes, depth, parentKey }) {
1291
+ function NavigationTreeList({
1292
+ nodes,
1293
+ depth,
1294
+ parentKey,
1295
+ buildToggleLabel,
1296
+ buildNavLabel
1297
+ }) {
1109
1298
  if (!Array.isArray(nodes) || !nodes.length) return null;
1110
1299
  const listClasses = ["canopy-nav-tree__list"];
1111
1300
  if (depth > 0) listClasses.push("canopy-nav-tree__list--nested");
1112
- return /* @__PURE__ */ React11.createElement("ul", { className: listClasses.join(" "), role: "list" }, nodes.map((node, index) => /* @__PURE__ */ React11.createElement(
1301
+ return /* @__PURE__ */ React13.createElement("ul", { className: listClasses.join(" "), role: "list" }, nodes.map((node, index) => /* @__PURE__ */ React13.createElement(
1113
1302
  NavigationTreeItem,
1114
1303
  {
1115
1304
  key: node.slug || node.href || node.title || `${parentKey}-${index}`,
1116
1305
  node,
1117
1306
  depth,
1118
- nodeKey: `${parentKey}-${index}`
1307
+ nodeKey: `${parentKey}-${index}`,
1308
+ buildToggleLabel,
1309
+ buildNavLabel
1119
1310
  }
1120
1311
  )));
1121
1312
  }
1122
- function NavigationTreeItem({ node, depth, nodeKey }) {
1313
+ function NavigationTreeItem({ node, depth, nodeKey, buildToggleLabel, buildNavLabel }) {
1123
1314
  if (!node) return null;
1124
1315
  const hasChildren = Array.isArray(node.children) && node.children.length > 0;
1125
1316
  const isInteractive = !!node.href;
@@ -1132,8 +1323,8 @@ function NavigationTreeItem({ node, depth, nodeKey }) {
1132
1323
  const panelId = hasChildren ? `canopy-section-${nodeKey}` : null;
1133
1324
  const allowToggle = hasChildren && !isRootLevel;
1134
1325
  const defaultExpanded = allowToggle ? !!node.isExpanded : true;
1135
- const toggleLabel = node.title ? `Toggle ${node.title} menu` : "Toggle section menu";
1136
- return /* @__PURE__ */ React11.createElement(
1326
+ const toggleLabel = buildToggleLabel ? buildToggleLabel(node.title || node.slug) : `Toggle ${node.title || "section"} menu`;
1327
+ return /* @__PURE__ */ React13.createElement(
1137
1328
  "li",
1138
1329
  {
1139
1330
  className: "canopy-nav-tree__item",
@@ -1142,7 +1333,7 @@ function NavigationTreeItem({ node, depth, nodeKey }) {
1142
1333
  "data-expanded": allowToggle ? defaultExpanded ? "true" : "false" : void 0,
1143
1334
  "data-default-expanded": allowToggle && defaultExpanded ? "true" : void 0
1144
1335
  },
1145
- /* @__PURE__ */ React11.createElement("div", { className: "canopy-nav-tree__row" }, /* @__PURE__ */ React11.createElement("div", { className: "canopy-nav-tree__link-wrapper" }, /* @__PURE__ */ React11.createElement(
1336
+ /* @__PURE__ */ React13.createElement("div", { className: "canopy-nav-tree__row" }, /* @__PURE__ */ React13.createElement("div", { className: "canopy-nav-tree__link-wrapper" }, /* @__PURE__ */ React13.createElement(
1146
1337
  Tag,
1147
1338
  {
1148
1339
  className: classes.join(" "),
@@ -1151,7 +1342,7 @@ function NavigationTreeItem({ node, depth, nodeKey }) {
1151
1342
  tabIndex: isInteractive ? void 0 : -1
1152
1343
  },
1153
1344
  node.title || node.slug
1154
- )), allowToggle ? /* @__PURE__ */ React11.createElement(
1345
+ )), allowToggle ? /* @__PURE__ */ React13.createElement(
1155
1346
  "button",
1156
1347
  {
1157
1348
  type: "button",
@@ -1161,7 +1352,7 @@ function NavigationTreeItem({ node, depth, nodeKey }) {
1161
1352
  "aria-label": toggleLabel,
1162
1353
  "data-canopy-nav-item-toggle": panelId || void 0
1163
1354
  },
1164
- /* @__PURE__ */ React11.createElement(
1355
+ /* @__PURE__ */ React13.createElement(
1165
1356
  "svg",
1166
1357
  {
1167
1358
  xmlns: "http://www.w3.org/2000/svg",
@@ -1171,7 +1362,7 @@ function NavigationTreeItem({ node, depth, nodeKey }) {
1171
1362
  strokeWidth: "1.5",
1172
1363
  className: "canopy-nav-tree__toggle-icon"
1173
1364
  },
1174
- /* @__PURE__ */ React11.createElement(
1365
+ /* @__PURE__ */ React13.createElement(
1175
1366
  "path",
1176
1367
  {
1177
1368
  strokeLinecap: "round",
@@ -1180,9 +1371,9 @@ function NavigationTreeItem({ node, depth, nodeKey }) {
1180
1371
  }
1181
1372
  )
1182
1373
  ),
1183
- /* @__PURE__ */ React11.createElement("span", { className: "sr-only" }, toggleLabel)
1374
+ /* @__PURE__ */ React13.createElement("span", { className: "sr-only" }, toggleLabel)
1184
1375
  ) : null),
1185
- hasChildren ? /* @__PURE__ */ React11.createElement(
1376
+ hasChildren ? /* @__PURE__ */ React13.createElement(
1186
1377
  "div",
1187
1378
  {
1188
1379
  id: panelId || void 0,
@@ -1190,12 +1381,14 @@ function NavigationTreeItem({ node, depth, nodeKey }) {
1190
1381
  "aria-hidden": allowToggle ? defaultExpanded ? "false" : "true" : "false",
1191
1382
  hidden: allowToggle ? !defaultExpanded : void 0
1192
1383
  },
1193
- /* @__PURE__ */ React11.createElement(
1384
+ /* @__PURE__ */ React13.createElement(
1194
1385
  NavigationTreeList,
1195
1386
  {
1196
1387
  nodes: node.children,
1197
1388
  depth: depth + 1,
1198
- parentKey: nodeKey
1389
+ parentKey: nodeKey,
1390
+ buildToggleLabel,
1391
+ buildNavLabel
1199
1392
  }
1200
1393
  )
1201
1394
  ) : null
@@ -1212,23 +1405,43 @@ function NavigationTree({
1212
1405
  ...rest
1213
1406
  }) {
1214
1407
  if (!root) return null;
1408
+ const { formatString, getString } = useLocale2();
1409
+ const navFallback = getString("common.nouns.navigation", "navigation");
1410
+ const buildNavLabel = React13.useCallback(
1411
+ (value) => formatString(
1412
+ "common.phrases.nav_label",
1413
+ "{content} navigation",
1414
+ { content: value || navFallback }
1415
+ ),
1416
+ [formatString, navFallback]
1417
+ );
1418
+ const buildToggleLabel = React13.useCallback(
1419
+ (value) => formatString(
1420
+ "common.phrases.toggle_content",
1421
+ "Toggle {content}",
1422
+ { content: buildNavLabel(value) }
1423
+ ),
1424
+ [buildNavLabel, formatString]
1425
+ );
1215
1426
  const nodes = includeRoot ? [root] : root.children;
1216
1427
  if (!Array.isArray(nodes) || !nodes.length) return null;
1217
1428
  const combinedClassName = ["canopy-nav-tree", className].filter(Boolean).join(" ");
1218
- return /* @__PURE__ */ React11.createElement(
1429
+ return /* @__PURE__ */ React13.createElement(
1219
1430
  Component,
1220
1431
  {
1221
1432
  className: combinedClassName,
1222
1433
  "data-canopy-nav-tree": "true",
1223
1434
  ...rest
1224
1435
  },
1225
- heading ? /* @__PURE__ */ React11.createElement("div", { className: headingClassName }, heading) : null,
1226
- /* @__PURE__ */ React11.createElement(
1436
+ heading ? /* @__PURE__ */ React13.createElement("div", { className: headingClassName }, heading) : null,
1437
+ /* @__PURE__ */ React13.createElement(
1227
1438
  NavigationTreeList,
1228
1439
  {
1229
1440
  nodes,
1230
1441
  depth: includeRoot ? -1 : 0,
1231
- parentKey
1442
+ parentKey,
1443
+ buildToggleLabel,
1444
+ buildNavLabel
1232
1445
  }
1233
1446
  )
1234
1447
  );
@@ -1252,12 +1465,12 @@ function SubNavigation({
1252
1465
  ariaLabel
1253
1466
  }) {
1254
1467
  const PageContext2 = navigationHelpers2 && navigationHelpers2.getPageContext ? navigationHelpers2.getPageContext() : null;
1255
- const context = PageContext2 ? React12.useContext(PageContext2) : null;
1468
+ const context = PageContext2 ? React14.useContext(PageContext2) : null;
1256
1469
  const contextNavigation = context && context.navigation ? context.navigation : null;
1257
1470
  const contextPage = context && context.page ? context.page : null;
1258
1471
  const effectiveNavigation = navigationProp || contextNavigation;
1259
1472
  const effectivePage = page || contextPage;
1260
- const resolvedNavigation = React12.useMemo(() => {
1473
+ const resolvedNavigation = React14.useMemo(() => {
1261
1474
  if (effectiveNavigation && effectiveNavigation.root)
1262
1475
  return effectiveNavigation;
1263
1476
  const candidate = resolveRelativeCandidate(effectivePage, current);
@@ -1282,14 +1495,14 @@ function SubNavigation({
1282
1495
  if (!Object.prototype.hasOwnProperty.call(inlineStyle, "--sub-nav-indent")) {
1283
1496
  inlineStyle["--sub-nav-indent"] = "0.85rem";
1284
1497
  }
1285
- return /* @__PURE__ */ React12.createElement(
1498
+ return /* @__PURE__ */ React14.createElement(
1286
1499
  "nav",
1287
1500
  {
1288
1501
  className: combinedClassName,
1289
1502
  style: inlineStyle,
1290
1503
  "aria-label": navLabel
1291
1504
  },
1292
- /* @__PURE__ */ React12.createElement(
1505
+ /* @__PURE__ */ React14.createElement(
1293
1506
  NavigationTree,
1294
1507
  {
1295
1508
  root: rootNode,
@@ -1304,11 +1517,11 @@ function SubNavigation({
1304
1517
  }
1305
1518
 
1306
1519
  // ui/src/layout/Layout.jsx
1307
- import React14 from "react";
1520
+ import React16 from "react";
1308
1521
  import navigationHelpers3 from "@canopy-iiif/app/lib/components/navigation.js";
1309
1522
 
1310
1523
  // ui/src/layout/ContentNavigation.jsx
1311
- import React13 from "react";
1524
+ import React15 from "react";
1312
1525
  var SCROLL_OFFSET_REM = 1.618;
1313
1526
  var MAX_HEADING_DEPTH = 3;
1314
1527
  function resolveDepth(value, fallback = 1) {
@@ -1328,10 +1541,11 @@ function ContentNavigation({
1328
1541
  pageTitle,
1329
1542
  ariaLabel
1330
1543
  }) {
1544
+ const { getString, formatString } = useLocale();
1331
1545
  const isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
1332
- const savedDepthsRef = React13.useRef(null);
1333
- const [isExpanded, setIsExpanded] = React13.useState(false);
1334
- const handleToggle = React13.useCallback(() => {
1546
+ const savedDepthsRef = React15.useRef(null);
1547
+ const [isExpanded, setIsExpanded] = React15.useState(false);
1548
+ const handleToggle = React15.useCallback(() => {
1335
1549
  setIsExpanded((prev) => !prev);
1336
1550
  }, []);
1337
1551
  if ((!items || !items.length) && !headingId) return null;
@@ -1341,10 +1555,43 @@ function ContentNavigation({
1341
1555
  className
1342
1556
  ].filter(Boolean).join(" ");
1343
1557
  const effectiveHeading = heading || pageTitle || null;
1344
- const navLabel = ariaLabel || (effectiveHeading ? `${effectiveHeading} navigation` : "Section navigation");
1345
- const toggleSrLabel = isExpanded ? "Hide section navigation" : "Show section navigation";
1558
+ const fallbackNavLabel = getString(
1559
+ "common.nouns.section_navigation",
1560
+ "Section navigation"
1561
+ );
1562
+ const computedNavLabel = effectiveHeading ? formatString(
1563
+ "common.phrases.nav_label",
1564
+ "{content} navigation",
1565
+ { content: effectiveHeading }
1566
+ ) : fallbackNavLabel;
1567
+ const navLabel = ariaLabel || computedNavLabel;
1568
+ const toggleSrLabel = isExpanded ? formatString("common.phrases.hide_content", "Hide {content}", {
1569
+ content: navLabel
1570
+ }) : formatString("common.phrases.show_content", "Show {content}", {
1571
+ content: navLabel
1572
+ });
1346
1573
  const toggleStateClass = isExpanded ? "is-expanded" : "is-collapsed";
1347
- const getSavedDepth = React13.useCallback((id, fallback) => {
1574
+ const toggleShowLabel = getString("common.actions.show", "Show");
1575
+ const toggleHideLabel = getString("common.actions.hide", "Hide");
1576
+ const contentNavLabel = getString(
1577
+ "common.nouns.content_navigation",
1578
+ "content navigation"
1579
+ );
1580
+ const onThisPageLabel = getString(
1581
+ "common.misc.on_this_page",
1582
+ "On this page"
1583
+ );
1584
+ const showContentNavLabel = formatString(
1585
+ "common.phrases.show_content",
1586
+ "Show {content}",
1587
+ { content: contentNavLabel }
1588
+ );
1589
+ const hideContentNavLabel = formatString(
1590
+ "common.phrases.hide_content",
1591
+ "Hide {content}",
1592
+ { content: contentNavLabel }
1593
+ );
1594
+ const getSavedDepth = React15.useCallback((id, fallback) => {
1348
1595
  if (!id) return fallback;
1349
1596
  if (!savedDepthsRef.current) savedDepthsRef.current = /* @__PURE__ */ new Map();
1350
1597
  const store = savedDepthsRef.current;
@@ -1352,7 +1599,7 @@ function ContentNavigation({
1352
1599
  store.set(id, fallback);
1353
1600
  return fallback;
1354
1601
  }, []);
1355
- const headingEntries = React13.useMemo(() => {
1602
+ const headingEntries = React15.useMemo(() => {
1356
1603
  const entries = [];
1357
1604
  const seen = /* @__PURE__ */ new Set();
1358
1605
  if (headingId) {
@@ -1380,15 +1627,15 @@ function ContentNavigation({
1380
1627
  return entries;
1381
1628
  }, [headingId, items, getSavedDepth]);
1382
1629
  const fallbackId = headingEntries.length ? headingEntries[0].id : headingId || null;
1383
- const [activeId, setActiveId] = React13.useState(fallbackId);
1384
- const activeIdRef = React13.useRef(activeId);
1385
- React13.useEffect(() => {
1630
+ const [activeId, setActiveId] = React15.useState(fallbackId);
1631
+ const activeIdRef = React15.useRef(activeId);
1632
+ React15.useEffect(() => {
1386
1633
  if (process.env.NODE_ENV !== "production" && activeIdRef.current !== activeId) {
1387
1634
  console.log("[ContentNavigation] activeId changed:", activeId);
1388
1635
  }
1389
1636
  activeIdRef.current = activeId;
1390
1637
  }, [activeId]);
1391
- React13.useEffect(() => {
1638
+ React15.useEffect(() => {
1392
1639
  if (!headingEntries.length) return;
1393
1640
  if (!headingEntries.some((entry) => entry.id === activeIdRef.current)) {
1394
1641
  const next = headingEntries[0].id;
@@ -1396,7 +1643,7 @@ function ContentNavigation({
1396
1643
  setActiveId(next);
1397
1644
  }
1398
1645
  }, [headingEntries]);
1399
- const computeOffsetPx = React13.useCallback(() => {
1646
+ const computeOffsetPx = React15.useCallback(() => {
1400
1647
  if (!isBrowser) return 0;
1401
1648
  try {
1402
1649
  const root = document.documentElement;
@@ -1406,8 +1653,8 @@ function ContentNavigation({
1406
1653
  return 0;
1407
1654
  }
1408
1655
  }, [isBrowser]);
1409
- const headingElementsRef = React13.useRef([]);
1410
- const updateActiveFromElements = React13.useCallback(
1656
+ const headingElementsRef = React15.useRef([]);
1657
+ const updateActiveFromElements = React15.useCallback(
1411
1658
  (elements) => {
1412
1659
  if (!elements || !elements.length) return;
1413
1660
  const offset = computeOffsetPx();
@@ -1439,7 +1686,7 @@ function ContentNavigation({
1439
1686
  },
1440
1687
  [computeOffsetPx]
1441
1688
  );
1442
- React13.useEffect(() => {
1689
+ React15.useEffect(() => {
1443
1690
  if (!isBrowser) return void 0;
1444
1691
  const elements = headingEntries.map((entry) => {
1445
1692
  const element = document.getElementById(entry.id);
@@ -1472,7 +1719,7 @@ function ContentNavigation({
1472
1719
  window.removeEventListener("resize", handle);
1473
1720
  };
1474
1721
  }, [headingEntries, isBrowser, updateActiveFromElements]);
1475
- const handleAnchorClick = React13.useCallback(
1722
+ const handleAnchorClick = React15.useCallback(
1476
1723
  (event, targetId, options = {}) => {
1477
1724
  var _a;
1478
1725
  try {
@@ -1504,7 +1751,7 @@ function ContentNavigation({
1504
1751
  },
1505
1752
  [computeOffsetPx, headingEntries, headingId, isBrowser]
1506
1753
  );
1507
- const navTreeRoot = React13.useMemo(() => {
1754
+ const navTreeRoot = React15.useMemo(() => {
1508
1755
  function mapNodes(nodes2, parentKey = "section") {
1509
1756
  if (!Array.isArray(nodes2) || !nodes2.length) return [];
1510
1757
  return nodes2.map((node, index) => {
@@ -1539,11 +1786,11 @@ function ContentNavigation({
1539
1786
  const nodes = mapNodes(items, "section");
1540
1787
  return {
1541
1788
  slug: "content-nav-root",
1542
- title: effectiveHeading || pageTitle || "On this page",
1789
+ title: effectiveHeading || pageTitle || onThisPageLabel,
1543
1790
  children: nodes
1544
1791
  };
1545
1792
  }, [items, effectiveHeading, pageTitle, activeId, getSavedDepth]);
1546
- return /* @__PURE__ */ React13.createElement(
1793
+ return /* @__PURE__ */ React15.createElement(
1547
1794
  "nav",
1548
1795
  {
1549
1796
  className: combinedClassName,
@@ -1551,7 +1798,7 @@ function ContentNavigation({
1551
1798
  "aria-label": navLabel,
1552
1799
  "data-canopy-content-nav": "true"
1553
1800
  },
1554
- /* @__PURE__ */ React13.createElement(
1801
+ /* @__PURE__ */ React15.createElement(
1555
1802
  "button",
1556
1803
  {
1557
1804
  type: "button",
@@ -1561,25 +1808,25 @@ function ContentNavigation({
1561
1808
  title: toggleSrLabel,
1562
1809
  onClick: handleToggle,
1563
1810
  "data-canopy-content-nav-toggle": "true",
1564
- "data-show-label": "Show",
1565
- "data-hide-label": "Hide",
1566
- "data-show-full-label": "Show content navigation",
1567
- "data-hide-full-label": "Hide content navigation"
1811
+ "data-show-label": toggleShowLabel,
1812
+ "data-hide-label": toggleHideLabel,
1813
+ "data-show-full-label": showContentNavLabel,
1814
+ "data-hide-full-label": hideContentNavLabel
1568
1815
  },
1569
- /* @__PURE__ */ React13.createElement(
1816
+ /* @__PURE__ */ React15.createElement(
1570
1817
  "span",
1571
1818
  {
1572
1819
  className: "canopy-content-navigation__toggle-icon",
1573
1820
  "aria-hidden": "true"
1574
1821
  },
1575
- /* @__PURE__ */ React13.createElement(
1822
+ /* @__PURE__ */ React15.createElement(
1576
1823
  "svg",
1577
1824
  {
1578
1825
  xmlns: "http://www.w3.org/2000/svg",
1579
1826
  className: "ionicon",
1580
1827
  viewBox: "0 0 512 512"
1581
1828
  },
1582
- /* @__PURE__ */ React13.createElement(
1829
+ /* @__PURE__ */ React15.createElement(
1583
1830
  "path",
1584
1831
  {
1585
1832
  fill: "none",
@@ -1590,7 +1837,7 @@ function ContentNavigation({
1590
1837
  d: "M160 144h288M160 256h288M160 368h288"
1591
1838
  }
1592
1839
  ),
1593
- /* @__PURE__ */ React13.createElement(
1840
+ /* @__PURE__ */ React15.createElement(
1594
1841
  "circle",
1595
1842
  {
1596
1843
  cx: "80",
@@ -1603,7 +1850,7 @@ function ContentNavigation({
1603
1850
  strokeWidth: "32"
1604
1851
  }
1605
1852
  ),
1606
- /* @__PURE__ */ React13.createElement(
1853
+ /* @__PURE__ */ React15.createElement(
1607
1854
  "circle",
1608
1855
  {
1609
1856
  cx: "80",
@@ -1616,7 +1863,7 @@ function ContentNavigation({
1616
1863
  strokeWidth: "32"
1617
1864
  }
1618
1865
  ),
1619
- /* @__PURE__ */ React13.createElement(
1866
+ /* @__PURE__ */ React15.createElement(
1620
1867
  "circle",
1621
1868
  {
1622
1869
  cx: "80",
@@ -1631,17 +1878,17 @@ function ContentNavigation({
1631
1878
  )
1632
1879
  )
1633
1880
  ),
1634
- /* @__PURE__ */ React13.createElement(
1881
+ /* @__PURE__ */ React15.createElement(
1635
1882
  "span",
1636
1883
  {
1637
1884
  className: "canopy-content-navigation__toggle-label",
1638
1885
  "data-canopy-content-nav-toggle-label": "true"
1639
1886
  },
1640
- /* @__PURE__ */ React13.createElement("span", { className: "sr-only" }, toggleSrLabel)
1887
+ /* @__PURE__ */ React15.createElement("span", { className: "sr-only" }, toggleSrLabel)
1641
1888
  ),
1642
- /* @__PURE__ */ React13.createElement("span", { className: "sr-only", "data-canopy-content-nav-toggle-sr": "true" }, toggleSrLabel)
1889
+ /* @__PURE__ */ React15.createElement("span", { className: "sr-only", "data-canopy-content-nav-toggle-sr": "true" }, toggleSrLabel)
1643
1890
  ),
1644
- /* @__PURE__ */ React13.createElement(
1891
+ /* @__PURE__ */ React15.createElement(
1645
1892
  NavigationTree,
1646
1893
  {
1647
1894
  root: navTreeRoot,
@@ -1682,10 +1929,10 @@ function buildHeadingTree(headings) {
1682
1929
  }
1683
1930
  function buildNavigationAside(sidebar, className) {
1684
1931
  if (!sidebar) {
1685
- return /* @__PURE__ */ React14.createElement(SubNavigation, { className });
1932
+ return /* @__PURE__ */ React16.createElement(SubNavigation, { className });
1686
1933
  }
1687
1934
  if (typeof sidebar === "function") {
1688
- return React14.createElement(sidebar);
1935
+ return React16.createElement(sidebar);
1689
1936
  }
1690
1937
  return sidebar;
1691
1938
  }
@@ -2002,7 +2249,7 @@ function ContentNavigationScript() {
2002
2249
  });
2003
2250
  })();
2004
2251
  `;
2005
- return /* @__PURE__ */ React14.createElement("script", { dangerouslySetInnerHTML: { __html: code } });
2252
+ return /* @__PURE__ */ React16.createElement("script", { dangerouslySetInnerHTML: { __html: code } });
2006
2253
  }
2007
2254
  function Layout({
2008
2255
  children,
@@ -2017,26 +2264,26 @@ function Layout({
2017
2264
  ...rest
2018
2265
  }) {
2019
2266
  const PageContext2 = navigationHelpers3 && typeof navigationHelpers3.getPageContext === "function" ? navigationHelpers3.getPageContext() : null;
2020
- const context = PageContext2 ? React14.useContext(PageContext2) : null;
2021
- const pageHeadings = React14.useMemo(() => {
2267
+ const context = PageContext2 ? React16.useContext(PageContext2) : null;
2268
+ const pageHeadings = React16.useMemo(() => {
2022
2269
  const headings = context && context.page ? context.page.headings : null;
2023
2270
  return Array.isArray(headings) ? headings : [];
2024
2271
  }, [context]);
2025
- const contentHeading = React14.useMemo(() => {
2272
+ const contentHeading = React16.useMemo(() => {
2026
2273
  const first = pageHeadings.find((heading) => {
2027
2274
  const depth = heading && (heading.depth || heading.level);
2028
2275
  return depth === 1;
2029
2276
  });
2030
2277
  return first && first.title ? first.title : null;
2031
2278
  }, [pageHeadings]);
2032
- const headingAnchorId = React14.useMemo(() => {
2279
+ const headingAnchorId = React16.useMemo(() => {
2033
2280
  const first = pageHeadings.find((heading) => {
2034
2281
  const depth = heading && (heading.depth || heading.level);
2035
2282
  return depth === 1;
2036
2283
  });
2037
2284
  return first && first.id ? first.id : null;
2038
2285
  }, [pageHeadings]);
2039
- const headingTree = React14.useMemo(
2286
+ const headingTree = React16.useMemo(
2040
2287
  () => buildHeadingTree(pageHeadings),
2041
2288
  [pageHeadings]
2042
2289
  );
@@ -2061,20 +2308,20 @@ function Layout({
2061
2308
  contentNavigationClassName
2062
2309
  ].filter(Boolean).join(" ");
2063
2310
  const sidebarNode = showLeftColumn ? buildNavigationAside(sidebar, sidebarClassName) : null;
2064
- return /* @__PURE__ */ React14.createElement("div", { className: containerClassName, ...rest }, showLeftColumn ? /* @__PURE__ */ React14.createElement("aside", { className: leftAsideClassName }, sidebarNode) : null, /* @__PURE__ */ React14.createElement("div", { className: contentClassNames }, children), hasContentNavigation ? /* @__PURE__ */ React14.createElement(React14.Fragment, null, /* @__PURE__ */ React14.createElement(
2311
+ return /* @__PURE__ */ React16.createElement("div", { className: containerClassName, ...rest }, showLeftColumn ? /* @__PURE__ */ React16.createElement("aside", { className: leftAsideClassName }, sidebarNode) : null, /* @__PURE__ */ React16.createElement("div", { className: contentClassNames }, children), hasContentNavigation ? /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(
2065
2312
  "aside",
2066
2313
  {
2067
2314
  className: contentNavigationAsideClassName,
2068
2315
  "data-canopy-content-nav-root": "true"
2069
2316
  },
2070
- /* @__PURE__ */ React14.createElement(
2317
+ /* @__PURE__ */ React16.createElement(
2071
2318
  "div",
2072
2319
  {
2073
2320
  "data-canopy-content-nav-sentinel": "true",
2074
2321
  "aria-hidden": "true"
2075
2322
  }
2076
2323
  ),
2077
- /* @__PURE__ */ React14.createElement(
2324
+ /* @__PURE__ */ React16.createElement(
2078
2325
  ContentNavigation,
2079
2326
  {
2080
2327
  items: headingTree,
@@ -2083,28 +2330,28 @@ function Layout({
2083
2330
  pageTitle: context && context.page ? context.page.title : void 0
2084
2331
  }
2085
2332
  ),
2086
- /* @__PURE__ */ React14.createElement(
2333
+ /* @__PURE__ */ React16.createElement(
2087
2334
  "div",
2088
2335
  {
2089
2336
  "data-canopy-content-nav-placeholder": "true",
2090
2337
  "aria-hidden": "true"
2091
2338
  }
2092
2339
  )
2093
- ), /* @__PURE__ */ React14.createElement(ContentNavigationScript, null)) : null);
2340
+ ), /* @__PURE__ */ React16.createElement(ContentNavigationScript, null)) : null);
2094
2341
  }
2095
2342
 
2096
2343
  // ui/src/layout/CanopyHeader.jsx
2097
- import React24 from "react";
2344
+ import React25 from "react";
2098
2345
 
2099
2346
  // ui/src/search/SearchPanel.jsx
2100
- import React18 from "react";
2347
+ import React20 from "react";
2101
2348
 
2102
2349
  // ui/src/Icons.jsx
2103
- import React15 from "react";
2104
- var MagnifyingGlassIcon = (props) => /* @__PURE__ */ React15.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 512 512", "aria-hidden": "true", focusable: "false", ...props }, /* @__PURE__ */ React15.createElement("path", { d: "M456.69 421.39L362.6 327.3a173.81 173.81 0 0034.84-104.58C397.44 126.38 319.06 48 222.72 48S48 126.38 48 222.72s78.38 174.72 174.72 174.72A173.81 173.81 0 00327.3 362.6l94.09 94.09a25 25 0 0035.3-35.3zM97.92 222.72a124.8 124.8 0 11124.8 124.8 124.95 124.95 0 01-124.8-124.8z" }));
2350
+ import React17 from "react";
2351
+ var MagnifyingGlassIcon = (props) => /* @__PURE__ */ React17.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 512 512", "aria-hidden": "true", focusable: "false", ...props }, /* @__PURE__ */ React17.createElement("path", { d: "M456.69 421.39L362.6 327.3a173.81 173.81 0 0034.84-104.58C397.44 126.38 319.06 48 222.72 48S48 126.38 48 222.72s78.38 174.72 174.72 174.72A173.81 173.81 0 00327.3 362.6l94.09 94.09a25 25 0 0035.3-35.3zM97.92 222.72a124.8 124.8 0 11124.8 124.8 124.95 124.95 0 01-124.8-124.8z" }));
2105
2352
 
2106
2353
  // ui/src/search/SearchPanelForm.jsx
2107
- import React16 from "react";
2354
+ import React18 from "react";
2108
2355
  function readBasePath2() {
2109
2356
  const normalize = (val) => {
2110
2357
  const raw = typeof val === "string" ? val.trim() : "";
@@ -2159,26 +2406,35 @@ function resolveSearchPath(pathValue) {
2159
2406
  }
2160
2407
  function SearchPanelForm(props = {}) {
2161
2408
  const {
2162
- placeholder = "Search\u2026",
2163
- buttonLabel = "Search",
2409
+ placeholder,
2410
+ buttonLabel,
2164
2411
  label,
2165
2412
  searchPath = "/search",
2166
2413
  inputId: inputIdProp,
2167
- clearLabel = "Clear search"
2414
+ clearLabel
2168
2415
  } = props || {};
2169
- const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
2170
- const action = React16.useMemo(
2416
+ const { getString, formatString } = useLocale2();
2417
+ const searchLabel = getString("common.nouns.search", "Search");
2418
+ const placeholderText = placeholder != null ? placeholder : getString("common.phrases.placeholder_search", "Search\u2026");
2419
+ const buttonText = buttonLabel != null ? buttonLabel : getString("common.nouns.search", "Search");
2420
+ const clearText = clearLabel != null ? clearLabel : formatString(
2421
+ "common.phrases.clear_content_search",
2422
+ "Clear {content} search",
2423
+ { content: searchLabel }
2424
+ );
2425
+ const text = typeof label === "string" && label.trim() ? label.trim() : buttonText;
2426
+ const action = React18.useMemo(
2171
2427
  () => resolveSearchPath(searchPath),
2172
2428
  [searchPath]
2173
2429
  );
2174
- const autoId = typeof React16.useId === "function" ? React16.useId() : void 0;
2175
- const [fallbackId] = React16.useState(
2430
+ const autoId = typeof React18.useId === "function" ? React18.useId() : void 0;
2431
+ const [fallbackId] = React18.useState(
2176
2432
  () => `canopy-search-form-${Math.random().toString(36).slice(2, 10)}`
2177
2433
  );
2178
2434
  const inputId = inputIdProp || autoId || fallbackId;
2179
- const inputRef = React16.useRef(null);
2180
- const [hasValue, setHasValue] = React16.useState(false);
2181
- const focusInput = React16.useCallback(() => {
2435
+ const inputRef = React18.useRef(null);
2436
+ const [hasValue, setHasValue] = React18.useState(false);
2437
+ const focusInput = React18.useCallback(() => {
2182
2438
  const el = inputRef.current;
2183
2439
  if (!el) return;
2184
2440
  if (document.activeElement === el) return;
@@ -2191,7 +2447,7 @@ function SearchPanelForm(props = {}) {
2191
2447
  }
2192
2448
  }
2193
2449
  }, []);
2194
- const handlePointerDown = React16.useCallback(
2450
+ const handlePointerDown = React18.useCallback(
2195
2451
  (event) => {
2196
2452
  const target = event.target;
2197
2453
  if (target && typeof target.closest === "function") {
@@ -2203,23 +2459,23 @@ function SearchPanelForm(props = {}) {
2203
2459
  },
2204
2460
  [focusInput]
2205
2461
  );
2206
- React16.useEffect(() => {
2462
+ React18.useEffect(() => {
2207
2463
  const el = inputRef.current;
2208
2464
  if (!el) return;
2209
2465
  if (el.value && el.value.trim()) {
2210
2466
  setHasValue(true);
2211
2467
  }
2212
2468
  }, []);
2213
- const handleInputChange = React16.useCallback((event) => {
2469
+ const handleInputChange = React18.useCallback((event) => {
2214
2470
  var _a;
2215
2471
  const nextHasValue = Boolean(
2216
2472
  ((_a = event == null ? void 0 : event.target) == null ? void 0 : _a.value) && event.target.value.trim()
2217
2473
  );
2218
2474
  setHasValue(nextHasValue);
2219
2475
  }, []);
2220
- const handleClear = React16.useCallback((event) => {
2476
+ const handleClear = React18.useCallback((event) => {
2221
2477
  }, []);
2222
- const handleClearKey = React16.useCallback(
2478
+ const handleClearKey = React18.useCallback(
2223
2479
  (event) => {
2224
2480
  if (event.key === "Enter" || event.key === " ") {
2225
2481
  event.preventDefault();
@@ -2228,7 +2484,7 @@ function SearchPanelForm(props = {}) {
2228
2484
  },
2229
2485
  [handleClear]
2230
2486
  );
2231
- return /* @__PURE__ */ React16.createElement(
2487
+ return /* @__PURE__ */ React18.createElement(
2232
2488
  "form",
2233
2489
  {
2234
2490
  action,
@@ -2240,7 +2496,7 @@ function SearchPanelForm(props = {}) {
2240
2496
  onPointerDown: handlePointerDown,
2241
2497
  "data-has-value": hasValue ? "1" : "0"
2242
2498
  },
2243
- /* @__PURE__ */ React16.createElement("label", { htmlFor: inputId, className: "canopy-search-form__label" }, /* @__PURE__ */ React16.createElement(MagnifyingGlassIcon, { className: "canopy-search-form__icon" }), /* @__PURE__ */ React16.createElement("span", { className: "sr-only" }, "Search"), /* @__PURE__ */ React16.createElement(
2499
+ /* @__PURE__ */ React18.createElement("label", { htmlFor: inputId, className: "canopy-search-form__label" }, /* @__PURE__ */ React18.createElement(MagnifyingGlassIcon, { className: "canopy-search-form__icon" }), /* @__PURE__ */ React18.createElement("span", { className: "sr-only" }, searchLabel), /* @__PURE__ */ React18.createElement(
2244
2500
  "input",
2245
2501
  {
2246
2502
  id: inputId,
@@ -2248,14 +2504,14 @@ function SearchPanelForm(props = {}) {
2248
2504
  name: "q",
2249
2505
  inputMode: "search",
2250
2506
  "data-canopy-search-form-input": true,
2251
- placeholder,
2507
+ placeholder: placeholderText,
2252
2508
  className: "canopy-search-form__input",
2253
2509
  ref: inputRef,
2254
2510
  onChange: handleInputChange,
2255
2511
  onInput: handleInputChange
2256
2512
  }
2257
2513
  )),
2258
- hasValue ? /* @__PURE__ */ React16.createElement(
2514
+ hasValue ? /* @__PURE__ */ React18.createElement(
2259
2515
  "button",
2260
2516
  {
2261
2517
  type: "button",
@@ -2263,12 +2519,12 @@ function SearchPanelForm(props = {}) {
2263
2519
  onClick: handleClear,
2264
2520
  onPointerDown: (event) => event.stopPropagation(),
2265
2521
  onKeyDown: handleClearKey,
2266
- "aria-label": clearLabel,
2522
+ "aria-label": clearText,
2267
2523
  "data-canopy-search-form-clear": true
2268
2524
  },
2269
2525
  "\xD7"
2270
2526
  ) : null,
2271
- /* @__PURE__ */ React16.createElement(
2527
+ /* @__PURE__ */ React18.createElement(
2272
2528
  "button",
2273
2529
  {
2274
2530
  type: "submit",
@@ -2281,11 +2537,11 @@ function SearchPanelForm(props = {}) {
2281
2537
  }
2282
2538
 
2283
2539
  // ui/src/search/SearchPanelTeaserResults.jsx
2284
- import React17 from "react";
2540
+ import React19 from "react";
2285
2541
  function SearchPanelTeaserResults(props = {}) {
2286
2542
  const { style, className } = props || {};
2287
2543
  const classes = ["canopy-search-teaser", "is-empty", className].filter(Boolean).join(" ");
2288
- return /* @__PURE__ */ React17.createElement(
2544
+ return /* @__PURE__ */ React19.createElement(
2289
2545
  "div",
2290
2546
  {
2291
2547
  "data-canopy-search-form-panel": true,
@@ -2293,65 +2549,49 @@ function SearchPanelTeaserResults(props = {}) {
2293
2549
  className: classes || void 0,
2294
2550
  style
2295
2551
  },
2296
- /* @__PURE__ */ React17.createElement("div", { id: "cplist", className: "canopy-search-teaser__list" })
2552
+ /* @__PURE__ */ React19.createElement("div", { id: "cplist", className: "canopy-search-teaser__list" })
2297
2553
  );
2298
2554
  }
2299
2555
 
2300
2556
  // ui/src/search/SearchPanel.jsx
2301
2557
  function SearchPanel(props = {}) {
2302
2558
  const {
2303
- placeholder = "Search\u2026",
2559
+ placeholder: placeholderProp,
2304
2560
  hotkey = "mod+k",
2305
2561
  maxResults = 8,
2306
2562
  groupOrder = ["work", "docs", "page"],
2307
2563
  // Kept for backward compat; form always renders submit
2308
2564
  button = true,
2309
2565
  // eslint-disable-line no-unused-vars
2310
- buttonLabel = "Search",
2566
+ buttonLabel: buttonLabelProp,
2311
2567
  label,
2312
2568
  searchPath = "/search"
2313
2569
  } = props || {};
2314
- const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
2570
+ const { getString } = useLocale2();
2571
+ const placeholder = placeholderProp != null ? placeholderProp : getString("common.phrases.placeholder_search", "Search\u2026");
2572
+ const resolvedButtonLabel = buttonLabelProp != null ? buttonLabelProp : getString("common.nouns.search", "Search");
2573
+ const text = typeof label === "string" && label.trim() ? label.trim() : resolvedButtonLabel;
2315
2574
  const resolvedSearchPath = resolveSearchPath(searchPath);
2316
2575
  const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
2317
- return /* @__PURE__ */ React18.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React18.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React18.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React18.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React18.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
2318
- }
2319
-
2320
- // ui/src/layout/CanopyBrand.jsx
2321
- import React20 from "react";
2322
-
2323
- // ui/src/layout/pageContext.js
2324
- import React19 from "react";
2325
- var CONTEXT_KEY = typeof Symbol === "function" ? Symbol.for("__CANOPY_PAGE_CONTEXT__") : "__CANOPY_PAGE_CONTEXT__";
2326
- function getSharedRoot() {
2327
- if (typeof globalThis !== "undefined") return globalThis;
2328
- if (typeof window !== "undefined") return window;
2329
- if (typeof global !== "undefined") return global;
2330
- return null;
2331
- }
2332
- function getSafePageContext() {
2333
- const root = getSharedRoot();
2334
- if (root && root[CONTEXT_KEY]) return root[CONTEXT_KEY];
2335
- const ctx = React19.createContext({ navigation: null, page: null, site: null });
2336
- if (root) root[CONTEXT_KEY] = ctx;
2337
- return ctx;
2576
+ return /* @__PURE__ */ React20.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React20.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React20.createElement(SearchPanelForm, { placeholder, buttonLabel: resolvedButtonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React20.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React20.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
2338
2577
  }
2339
2578
 
2340
2579
  // ui/src/layout/CanopyBrand.jsx
2580
+ import React21 from "react";
2341
2581
  var PageContext = getSafePageContext();
2342
2582
  function CanopyBrand(props = {}) {
2343
2583
  const { labelId, label: labelProp, href = "/", className, Logo } = props || {};
2344
- const context = React20.useContext(PageContext);
2584
+ const context = React21.useContext(PageContext);
2345
2585
  const contextSiteTitle = context && context.site && typeof context.site.title === "string" ? context.site.title.trim() : "";
2346
2586
  const normalizedLabel = typeof labelProp === "string" && labelProp.trim() ? labelProp : "";
2347
2587
  const resolvedLabel = normalizedLabel || contextSiteTitle || "Site title";
2348
2588
  const spanProps = labelId ? { id: labelId } : {};
2349
2589
  const classes = ["canopy-logo", className].filter(Boolean).join(" ");
2350
- return /* @__PURE__ */ React20.createElement("a", { href, className: classes }, typeof Logo === "function" ? /* @__PURE__ */ React20.createElement(Logo, null) : null, /* @__PURE__ */ React20.createElement("span", { ...spanProps }, resolvedLabel));
2590
+ return /* @__PURE__ */ React21.createElement("a", { href, className: classes }, typeof Logo === "function" ? /* @__PURE__ */ React21.createElement(Logo, null) : null, /* @__PURE__ */ React21.createElement("span", { ...spanProps }, resolvedLabel));
2351
2591
  }
2352
2592
 
2353
2593
  // ui/src/layout/CanopyModal.jsx
2354
- import React21 from "react";
2594
+ import React22 from "react";
2355
2595
  function CanopyModal(props = {}) {
2356
2596
  const {
2357
2597
  id,
@@ -2361,7 +2601,7 @@ function CanopyModal(props = {}) {
2361
2601
  label,
2362
2602
  logo: Logo,
2363
2603
  href = "/",
2364
- closeLabel = "Close",
2604
+ closeLabel,
2365
2605
  closeDataAttr,
2366
2606
  onClose,
2367
2607
  onBackgroundClick,
@@ -2387,10 +2627,12 @@ function CanopyModal(props = {}) {
2387
2627
  if (event.target === event.currentTarget) onBackgroundClick(event);
2388
2628
  };
2389
2629
  }
2630
+ const { getString } = useLocale2();
2631
+ const resolvedCloseLabel = closeLabel || getString("common.actions.close", "Close");
2390
2632
  const closeButtonProps = {
2391
2633
  type: "button",
2392
2634
  className: "canopy-modal__close",
2393
- "aria-label": closeLabel
2635
+ "aria-label": resolvedCloseLabel
2394
2636
  };
2395
2637
  if (typeof closeDataAttr === "string" && closeDataAttr) {
2396
2638
  closeButtonProps["data-canopy-header-close"] = closeDataAttr;
@@ -2402,7 +2644,7 @@ function CanopyModal(props = {}) {
2402
2644
  if (padded) bodyClasses.push("canopy-modal__body--padded");
2403
2645
  if (bodyClassName) bodyClasses.push(bodyClassName);
2404
2646
  const bodyClassNameValue = bodyClasses.join(" ");
2405
- return /* @__PURE__ */ React21.createElement("div", { ...modalProps }, /* @__PURE__ */ React21.createElement("div", { className: "canopy-modal__panel" }, /* @__PURE__ */ React21.createElement("button", { ...closeButtonProps }, /* @__PURE__ */ React21.createElement(
2647
+ return /* @__PURE__ */ React22.createElement("div", { ...modalProps }, /* @__PURE__ */ React22.createElement("div", { className: "canopy-modal__panel" }, /* @__PURE__ */ React22.createElement("button", { ...closeButtonProps }, /* @__PURE__ */ React22.createElement(
2406
2648
  "svg",
2407
2649
  {
2408
2650
  xmlns: "http://www.w3.org/2000/svg",
@@ -2412,8 +2654,8 @@ function CanopyModal(props = {}) {
2412
2654
  strokeWidth: "1.5",
2413
2655
  className: "canopy-modal__close-icon"
2414
2656
  },
2415
- /* @__PURE__ */ React21.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 6l12 12M6 18L18 6" })
2416
- ), /* @__PURE__ */ React21.createElement("span", { className: "sr-only" }, closeLabel)), /* @__PURE__ */ React21.createElement("div", { className: bodyClassNameValue }, label ? /* @__PURE__ */ React21.createElement("div", { className: "canopy-modal__brand" }, /* @__PURE__ */ React21.createElement(
2657
+ /* @__PURE__ */ React22.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 6l12 12M6 18L18 6" })
2658
+ ), /* @__PURE__ */ React22.createElement("span", { className: "sr-only" }, resolvedCloseLabel)), /* @__PURE__ */ React22.createElement("div", { className: bodyClassNameValue }, label ? /* @__PURE__ */ React22.createElement("div", { className: "canopy-modal__brand" }, /* @__PURE__ */ React22.createElement(
2417
2659
  CanopyBrand,
2418
2660
  {
2419
2661
  labelId: resolvedLabelId,
@@ -2426,10 +2668,10 @@ function CanopyModal(props = {}) {
2426
2668
  }
2427
2669
 
2428
2670
  // ui/src/layout/LanguageToggle.jsx
2429
- import React23 from "react";
2671
+ import React24 from "react";
2430
2672
 
2431
2673
  // ui/src/layout/languageToggleShared.jsx
2432
- import React22 from "react";
2674
+ import React23 from "react";
2433
2675
  function readBasePath3() {
2434
2676
  const normalize = (value) => {
2435
2677
  if (!value) return "";
@@ -2588,13 +2830,13 @@ function LanguageToggleControl({
2588
2830
  ].filter(Boolean).join(" ").trim();
2589
2831
  const ariaLabel = config.label || "Language";
2590
2832
  const resolvedControl = control === "list" ? "list" : "select";
2591
- const selectId = typeof React22.useId === "function" && resolvedControl === "select" ? React22.useId() : void 0;
2833
+ const selectId = typeof React23.useId === "function" && resolvedControl === "select" ? React23.useId() : void 0;
2592
2834
  const links = Array.isArray(config.links) ? config.links : [];
2593
2835
  if (!links.length) return null;
2594
2836
  const activeLink = links.find((link) => link.isActive);
2595
2837
  const fallbackHref = links[0].href;
2596
2838
  const selectedHref = activeLink ? activeLink.href : fallbackHref;
2597
- const navigate = React22.useCallback((href) => {
2839
+ const navigate = React23.useCallback((href) => {
2598
2840
  if (!href) return;
2599
2841
  try {
2600
2842
  if (typeof window !== "undefined" && window.location) {
@@ -2614,7 +2856,7 @@ function LanguageToggleControl({
2614
2856
  } catch (_) {
2615
2857
  }
2616
2858
  }, []);
2617
- const handleSelectChange = React22.useCallback(
2859
+ const handleSelectChange = React23.useCallback(
2618
2860
  (event) => {
2619
2861
  if (!event || !event.target) return;
2620
2862
  const nextHref = event.target.value;
@@ -2623,15 +2865,15 @@ function LanguageToggleControl({
2623
2865
  },
2624
2866
  [navigate, selectedHref]
2625
2867
  );
2626
- const labelElement = showLabel && config.label ? resolvedControl === "select" ? /* @__PURE__ */ React22.createElement(
2868
+ const labelElement = showLabel && config.label ? resolvedControl === "select" ? /* @__PURE__ */ React23.createElement(
2627
2869
  "label",
2628
2870
  {
2629
2871
  className: "canopy-language-toggle__label",
2630
2872
  htmlFor: selectId
2631
2873
  },
2632
2874
  config.label
2633
- ) : /* @__PURE__ */ React22.createElement("span", { className: "canopy-language-toggle__label" }, config.label) : null;
2634
- return /* @__PURE__ */ React22.createElement("div", { className: classes || void 0 }, labelElement, resolvedControl === "select" ? /* @__PURE__ */ React22.createElement("div", { className: "canopy-language-toggle__select" }, /* @__PURE__ */ React22.createElement(
2875
+ ) : /* @__PURE__ */ React23.createElement("span", { className: "canopy-language-toggle__label" }, config.label) : null;
2876
+ return /* @__PURE__ */ React23.createElement("div", { className: classes || void 0 }, labelElement, resolvedControl === "select" ? /* @__PURE__ */ React23.createElement("div", { className: "canopy-language-toggle__select" }, /* @__PURE__ */ React23.createElement(
2635
2877
  "select",
2636
2878
  {
2637
2879
  id: selectId,
@@ -2641,7 +2883,7 @@ function LanguageToggleControl({
2641
2883
  "aria-label": ariaLabel,
2642
2884
  "data-canopy-language-select": "true"
2643
2885
  },
2644
- links.map((link) => /* @__PURE__ */ React22.createElement(
2886
+ links.map((link) => /* @__PURE__ */ React23.createElement(
2645
2887
  "option",
2646
2888
  {
2647
2889
  key: link.lang,
@@ -2651,7 +2893,7 @@ function LanguageToggleControl({
2651
2893
  },
2652
2894
  link.label
2653
2895
  ))
2654
- )) : /* @__PURE__ */ React22.createElement("nav", { className: "canopy-language-toggle__nav", "aria-label": ariaLabel }, /* @__PURE__ */ React22.createElement("ul", { className: "canopy-language-toggle__list", role: "list" }, links.map((link) => /* @__PURE__ */ React22.createElement("li", { key: link.lang }, /* @__PURE__ */ React22.createElement(
2896
+ )) : /* @__PURE__ */ React23.createElement("nav", { className: "canopy-language-toggle__nav", "aria-label": ariaLabel }, /* @__PURE__ */ React23.createElement("ul", { className: "canopy-language-toggle__list", role: "list" }, links.map((link) => /* @__PURE__ */ React23.createElement("li", { key: link.lang }, /* @__PURE__ */ React23.createElement(
2655
2897
  "button",
2656
2898
  {
2657
2899
  type: "button",
@@ -2663,7 +2905,7 @@ function LanguageToggleControl({
2663
2905
  onClick: () => navigate(link.href)
2664
2906
  },
2665
2907
  link.label
2666
- ))))), /* @__PURE__ */ React22.createElement(LanguageToggleRuntime, null));
2908
+ ))))), /* @__PURE__ */ React23.createElement(LanguageToggleRuntime, null));
2667
2909
  }
2668
2910
  function LanguageToggleRuntime() {
2669
2911
  const code = `
@@ -2723,7 +2965,7 @@ function LanguageToggleRuntime() {
2723
2965
  }
2724
2966
  })();
2725
2967
  `;
2726
- return /* @__PURE__ */ React22.createElement("script", { dangerouslySetInnerHTML: { __html: code } });
2968
+ return /* @__PURE__ */ React23.createElement("script", { dangerouslySetInnerHTML: { __html: code } });
2727
2969
  }
2728
2970
 
2729
2971
  // ui/src/layout/LanguageToggle.jsx
@@ -2738,7 +2980,7 @@ function LanguageToggle({
2738
2980
  ariaLabel
2739
2981
  }) {
2740
2982
  const PageContext2 = getSafePageContext();
2741
- const context = React23.useContext(PageContext2);
2983
+ const context = React24.useContext(PageContext2);
2742
2984
  const siteLanguageToggle = context && context.site && context.site.languageToggle || null;
2743
2985
  const pageData = page || (context && context.page ? context.page : null);
2744
2986
  const resolvedToggle = languageToggle === false ? null : languageToggle === true || typeof languageToggle === "undefined" ? siteLanguageToggle : languageToggle;
@@ -2747,7 +2989,7 @@ function LanguageToggle({
2747
2989
  resolvedToggle && typeof resolvedToggle.control === "string" ? resolvedToggle.control : null
2748
2990
  );
2749
2991
  const resolvedControl = normalizeControl(typeof control === "string" ? control : null) || toggleControl || "select";
2750
- const config = React23.useMemo(() => {
2992
+ const config = React24.useMemo(() => {
2751
2993
  if (!resolvedToggle) return null;
2752
2994
  const base = buildLanguageToggleConfig(resolvedToggle, pageData);
2753
2995
  if (!base) return null;
@@ -2758,7 +3000,7 @@ function LanguageToggle({
2758
3000
  };
2759
3001
  }, [resolvedToggle, pageData, label, ariaLabel]);
2760
3002
  if (!config) return null;
2761
- return /* @__PURE__ */ React23.createElement(
3003
+ return /* @__PURE__ */ React24.createElement(
2762
3004
  LanguageToggleControl,
2763
3005
  {
2764
3006
  config,
@@ -3060,7 +3302,7 @@ function HeaderScript() {
3060
3302
  });
3061
3303
  })();
3062
3304
  `;
3063
- return /* @__PURE__ */ React24.createElement(
3305
+ return /* @__PURE__ */ React25.createElement(
3064
3306
  "script",
3065
3307
  {
3066
3308
  dangerouslySetInnerHTML: {
@@ -3128,16 +3370,17 @@ function getLinkNavigationData(link, navigationRoots, sectionNavigation) {
3128
3370
  function CanopyHeader(props = {}) {
3129
3371
  const {
3130
3372
  navigation: navLinksProp,
3131
- searchLabel = "Search",
3373
+ searchLabel: searchLabelProp,
3132
3374
  searchHotkey = "mod+k",
3133
- searchPlaceholder = "Search\u2026",
3375
+ searchPlaceholder: searchPlaceholderProp,
3134
3376
  brandHref = "/",
3135
3377
  title: titleProp,
3136
3378
  logo: SiteLogo,
3137
3379
  languageToggle: languageToggleProp
3138
3380
  } = props;
3139
3381
  const PageContext2 = getSafePageContext();
3140
- const context = React24.useContext(PageContext2);
3382
+ const context = React25.useContext(PageContext2);
3383
+ const { getString, formatString } = useLocale2();
3141
3384
  const contextPrimaryNav = context && Array.isArray(context.primaryNavigation) ? context.primaryNavigation : [];
3142
3385
  const navLinks = navLinksProp && navLinksProp.length ? ensureArray(navLinksProp) : ensureArray(contextPrimaryNav);
3143
3386
  const contextNavigation = context && context.navigation ? context.navigation : null;
@@ -3153,6 +3396,32 @@ function CanopyHeader(props = {}) {
3153
3396
  const usesDirectorySearchRoute = trimmedSearchRoute && trimmedSearchRoute !== (defaultSearchRoute || "search");
3154
3397
  const normalizedSearchRoute = usesDirectorySearchRoute ? `/${trimmedSearchRoute}/` : `/${(trimmedSearchRoute || defaultSearchRoute || "search").replace(/^\/+/, "")}`;
3155
3398
  const resolvedLanguageToggle = languageToggleProp === false ? null : languageToggleProp === true || typeof languageToggleProp === "undefined" ? siteLanguageToggle : languageToggleProp;
3399
+ const resolvedSearchLabel = searchLabelProp != null ? searchLabelProp : getString("common.nouns.search", "Search");
3400
+ const resolvedSearchPlaceholder = searchPlaceholderProp != null ? searchPlaceholderProp : getString("common.phrases.placeholder_search", "Search\u2026");
3401
+ const primaryNavigationLabel = getString(
3402
+ "common.nouns.primary_navigation",
3403
+ "Primary navigation"
3404
+ );
3405
+ const openSearchButtonLabel = formatString(
3406
+ "common.phrases.open_content",
3407
+ "Open {content}",
3408
+ { content: resolvedSearchLabel }
3409
+ );
3410
+ const openNavButtonLabel = formatString(
3411
+ "common.phrases.open_content",
3412
+ "Open {content}",
3413
+ { content: primaryNavigationLabel }
3414
+ );
3415
+ const closeNavLabel = formatString(
3416
+ "common.phrases.close_content",
3417
+ "Close {content}",
3418
+ { content: primaryNavigationLabel }
3419
+ );
3420
+ const closeSearchLabel = formatString(
3421
+ "common.phrases.close_content",
3422
+ "Close {content}",
3423
+ { content: resolvedSearchLabel }
3424
+ );
3156
3425
  const defaultHeaderTitle = contextSiteTitle || "Site title";
3157
3426
  const normalizedTitleProp = typeof titleProp === "string" ? titleProp.trim() : "";
3158
3427
  const resolvedTitle = normalizedTitleProp || defaultHeaderTitle;
@@ -3160,8 +3429,20 @@ function CanopyHeader(props = {}) {
3160
3429
  const navigationRoots = contextNavigation && contextNavigation.allRoots ? contextNavigation.allRoots : null;
3161
3430
  const sectionHeading = sectionNavigation && sectionNavigation.title || (sectionNavigation && sectionNavigation.root ? sectionNavigation.root.title : "");
3162
3431
  const hasSectionNav = !!(sectionNavigation && sectionNavigation.root && Array.isArray(sectionNavigation.root.children) && sectionNavigation.root.children.length);
3163
- const sectionLabel = sectionHeading ? `More in ${sectionHeading}` : "More in this section";
3164
- const sectionAriaLabel = sectionHeading ? `${sectionHeading} section navigation` : "Section navigation";
3432
+ const sectionNavFallback = getString(
3433
+ "common.nouns.section_navigation",
3434
+ "Section navigation"
3435
+ );
3436
+ const sectionLabel = sectionHeading ? formatString(
3437
+ "common.phrases.nav_label",
3438
+ "{content} navigation",
3439
+ { content: sectionHeading }
3440
+ ) : sectionNavFallback;
3441
+ const sectionAriaLabel = sectionHeading ? formatString(
3442
+ "common.phrases.nav_label",
3443
+ "{content} navigation",
3444
+ { content: sectionHeading }
3445
+ ) : sectionNavFallback;
3165
3446
  const defaultSectionLabel = sectionLabel;
3166
3447
  const defaultSectionAriaLabel = sectionAriaLabel;
3167
3448
  const shouldAttachSectionNav = (link) => {
@@ -3174,14 +3455,14 @@ function CanopyHeader(props = {}) {
3174
3455
  return !!(rootNode && Array.isArray(rootNode.children) && rootNode.children.length);
3175
3456
  };
3176
3457
  const hasIntegratedSectionNav = navLinks.some(shouldAttachSectionNav);
3177
- return /* @__PURE__ */ React24.createElement(React24.Fragment, null, /* @__PURE__ */ React24.createElement(
3458
+ return /* @__PURE__ */ React25.createElement(React25.Fragment, null, /* @__PURE__ */ React25.createElement(
3178
3459
  "header",
3179
3460
  {
3180
3461
  className: "canopy-header",
3181
3462
  "data-mobile-nav": "closed",
3182
3463
  "data-mobile-search": "closed"
3183
3464
  },
3184
- /* @__PURE__ */ React24.createElement("div", { className: "canopy-header__brand" }, /* @__PURE__ */ React24.createElement(
3465
+ /* @__PURE__ */ React25.createElement("div", { className: "canopy-header__brand" }, /* @__PURE__ */ React25.createElement(
3185
3466
  CanopyBrand,
3186
3467
  {
3187
3468
  label: resolvedTitle,
@@ -3190,22 +3471,22 @@ function CanopyHeader(props = {}) {
3190
3471
  Logo: SiteLogo
3191
3472
  }
3192
3473
  )),
3193
- /* @__PURE__ */ React24.createElement("div", { className: "canopy-header__desktop-search" }, /* @__PURE__ */ React24.createElement(
3474
+ /* @__PURE__ */ React25.createElement("div", { className: "canopy-header__desktop-search" }, /* @__PURE__ */ React25.createElement(
3194
3475
  SearchPanel,
3195
3476
  {
3196
- label: searchLabel,
3477
+ label: resolvedSearchLabel,
3197
3478
  hotkey: searchHotkey,
3198
- placeholder: searchPlaceholder,
3479
+ placeholder: resolvedSearchPlaceholder,
3199
3480
  searchPath: normalizedSearchRoute
3200
3481
  }
3201
3482
  )),
3202
- /* @__PURE__ */ React24.createElement(
3483
+ /* @__PURE__ */ React25.createElement(
3203
3484
  "nav",
3204
3485
  {
3205
3486
  className: "canopy-nav-links canopy-header__desktop-nav",
3206
- "aria-label": "Primary navigation"
3487
+ "aria-label": primaryNavigationLabel
3207
3488
  },
3208
- navLinks.map((link) => /* @__PURE__ */ React24.createElement(
3489
+ navLinks.map((link) => /* @__PURE__ */ React25.createElement(
3209
3490
  "a",
3210
3491
  {
3211
3492
  key: link.href,
@@ -3215,7 +3496,7 @@ function CanopyHeader(props = {}) {
3215
3496
  link.label || link.href
3216
3497
  ))
3217
3498
  ),
3218
- /* @__PURE__ */ React24.createElement("div", { className: "canopy-header__actions" }, resolvedLanguageToggle ? /* @__PURE__ */ React24.createElement(
3499
+ /* @__PURE__ */ React25.createElement("div", { className: "canopy-header__actions" }, resolvedLanguageToggle ? /* @__PURE__ */ React25.createElement(
3219
3500
  LanguageToggle,
3220
3501
  {
3221
3502
  languageToggle: resolvedLanguageToggle,
@@ -3223,17 +3504,17 @@ function CanopyHeader(props = {}) {
3223
3504
  variant: "desktop",
3224
3505
  className: "canopy-header__language-toggle canopy-header__language-toggle--desktop"
3225
3506
  }
3226
- ) : null, /* @__PURE__ */ React24.createElement(
3507
+ ) : null, /* @__PURE__ */ React25.createElement(
3227
3508
  "button",
3228
3509
  {
3229
3510
  type: "button",
3230
3511
  className: "canopy-header__icon-button canopy-header__search-trigger",
3231
- "aria-label": "Open search",
3512
+ "aria-label": openSearchButtonLabel,
3232
3513
  "aria-controls": "canopy-modal-search",
3233
3514
  "aria-expanded": "false",
3234
3515
  "data-canopy-header-toggle": "search"
3235
3516
  },
3236
- /* @__PURE__ */ React24.createElement(
3517
+ /* @__PURE__ */ React25.createElement(
3237
3518
  "svg",
3238
3519
  {
3239
3520
  xmlns: "http://www.w3.org/2000/svg",
@@ -3243,7 +3524,7 @@ function CanopyHeader(props = {}) {
3243
3524
  strokeWidth: "1.5",
3244
3525
  className: "canopy-header__search-icon"
3245
3526
  },
3246
- /* @__PURE__ */ React24.createElement(
3527
+ /* @__PURE__ */ React25.createElement(
3247
3528
  "path",
3248
3529
  {
3249
3530
  strokeLinecap: "round",
@@ -3252,17 +3533,17 @@ function CanopyHeader(props = {}) {
3252
3533
  }
3253
3534
  )
3254
3535
  )
3255
- ), /* @__PURE__ */ React24.createElement(
3536
+ ), /* @__PURE__ */ React25.createElement(
3256
3537
  "button",
3257
3538
  {
3258
3539
  type: "button",
3259
3540
  className: "canopy-header__icon-button canopy-header__menu",
3260
- "aria-label": "Open navigation",
3541
+ "aria-label": openNavButtonLabel,
3261
3542
  "aria-controls": "canopy-modal-nav",
3262
3543
  "aria-expanded": "false",
3263
3544
  "data-canopy-header-toggle": "nav"
3264
3545
  },
3265
- /* @__PURE__ */ React24.createElement(
3546
+ /* @__PURE__ */ React25.createElement(
3266
3547
  "svg",
3267
3548
  {
3268
3549
  xmlns: "http://www.w3.org/2000/svg",
@@ -3272,7 +3553,7 @@ function CanopyHeader(props = {}) {
3272
3553
  stroke: "currentColor",
3273
3554
  className: "canopy-header__menu-icon"
3274
3555
  },
3275
- /* @__PURE__ */ React24.createElement(
3556
+ /* @__PURE__ */ React25.createElement(
3276
3557
  "path",
3277
3558
  {
3278
3559
  strokeLinecap: "round",
@@ -3282,7 +3563,7 @@ function CanopyHeader(props = {}) {
3282
3563
  )
3283
3564
  )
3284
3565
  ))
3285
- ), /* @__PURE__ */ React24.createElement(
3566
+ ), /* @__PURE__ */ React25.createElement(
3286
3567
  CanopyModal,
3287
3568
  {
3288
3569
  id: "canopy-modal-nav",
@@ -3291,10 +3572,10 @@ function CanopyHeader(props = {}) {
3291
3572
  label: resolvedTitle,
3292
3573
  logo: SiteLogo,
3293
3574
  href: brandHref,
3294
- closeLabel: "Close navigation",
3575
+ closeLabel: closeNavLabel,
3295
3576
  closeDataAttr: "nav"
3296
3577
  },
3297
- resolvedLanguageToggle ? /* @__PURE__ */ React24.createElement(
3578
+ resolvedLanguageToggle ? /* @__PURE__ */ React25.createElement(
3298
3579
  LanguageToggle,
3299
3580
  {
3300
3581
  languageToggle: resolvedLanguageToggle,
@@ -3303,13 +3584,13 @@ function CanopyHeader(props = {}) {
3303
3584
  className: "canopy-header__language-toggle canopy-header__language-toggle--mobile"
3304
3585
  }
3305
3586
  ) : null,
3306
- /* @__PURE__ */ React24.createElement(
3587
+ /* @__PURE__ */ React25.createElement(
3307
3588
  "nav",
3308
3589
  {
3309
3590
  className: "canopy-nav-links canopy-modal__nav",
3310
- "aria-label": "Primary navigation"
3591
+ "aria-label": primaryNavigationLabel
3311
3592
  },
3312
- /* @__PURE__ */ React24.createElement("ul", { className: "canopy-modal__nav-list", role: "list" }, navLinks.map((link, index) => {
3593
+ /* @__PURE__ */ React25.createElement("ul", { className: "canopy-modal__nav-list", role: "list" }, navLinks.map((link, index) => {
3313
3594
  const navData = getLinkNavigationData(
3314
3595
  link,
3315
3596
  navigationRoots,
@@ -3318,9 +3599,18 @@ function CanopyHeader(props = {}) {
3318
3599
  const navRoot = navData && navData.root ? navData.root : null;
3319
3600
  const hasChildren = !!(navRoot && Array.isArray(navRoot.children) && navRoot.children.length);
3320
3601
  const nestedId = hasChildren ? `canopy-modal-section-${index}` : null;
3321
- const toggleLabel = link.label ? `Toggle ${link.label} menu` : "Toggle section menu";
3602
+ const toggleLabelTarget = link.label ? formatString(
3603
+ "common.phrases.nav_label",
3604
+ "{content} navigation",
3605
+ { content: link.label }
3606
+ ) : primaryNavigationLabel;
3607
+ const toggleLabel = formatString(
3608
+ "common.phrases.toggle_content",
3609
+ "Toggle {content}",
3610
+ { content: toggleLabelTarget }
3611
+ );
3322
3612
  const defaultExpanded = hasChildren && !!navRoot.isExpanded;
3323
- return /* @__PURE__ */ React24.createElement(
3613
+ return /* @__PURE__ */ React25.createElement(
3324
3614
  "li",
3325
3615
  {
3326
3616
  className: "canopy-modal__nav-item",
@@ -3329,7 +3619,7 @@ function CanopyHeader(props = {}) {
3329
3619
  "data-expanded": defaultExpanded ? "true" : "false",
3330
3620
  "data-default-expanded": defaultExpanded ? "true" : void 0
3331
3621
  },
3332
- /* @__PURE__ */ React24.createElement("div", { className: "canopy-modal__nav-row" }, /* @__PURE__ */ React24.createElement("a", { href: link.href }, link.label || link.href), hasChildren ? /* @__PURE__ */ React24.createElement(
3622
+ /* @__PURE__ */ React25.createElement("div", { className: "canopy-modal__nav-row" }, /* @__PURE__ */ React25.createElement("a", { href: link.href }, link.label || link.href), hasChildren ? /* @__PURE__ */ React25.createElement(
3333
3623
  "button",
3334
3624
  {
3335
3625
  type: "button",
@@ -3339,7 +3629,7 @@ function CanopyHeader(props = {}) {
3339
3629
  "aria-label": toggleLabel,
3340
3630
  "data-canopy-nav-item-toggle": nestedId || void 0
3341
3631
  },
3342
- /* @__PURE__ */ React24.createElement(
3632
+ /* @__PURE__ */ React25.createElement(
3343
3633
  "svg",
3344
3634
  {
3345
3635
  xmlns: "http://www.w3.org/2000/svg",
@@ -3349,7 +3639,7 @@ function CanopyHeader(props = {}) {
3349
3639
  strokeWidth: "1.5",
3350
3640
  className: "canopy-modal__nav-toggle-icon"
3351
3641
  },
3352
- /* @__PURE__ */ React24.createElement(
3642
+ /* @__PURE__ */ React25.createElement(
3353
3643
  "path",
3354
3644
  {
3355
3645
  strokeLinecap: "round",
@@ -3358,16 +3648,20 @@ function CanopyHeader(props = {}) {
3358
3648
  }
3359
3649
  )
3360
3650
  ),
3361
- /* @__PURE__ */ React24.createElement("span", { className: "sr-only" }, toggleLabel)
3651
+ /* @__PURE__ */ React25.createElement("span", { className: "sr-only" }, toggleLabel)
3362
3652
  ) : null),
3363
- hasChildren ? /* @__PURE__ */ React24.createElement(
3653
+ hasChildren ? /* @__PURE__ */ React25.createElement(
3364
3654
  NavigationTree,
3365
3655
  {
3366
3656
  root: navRoot,
3367
3657
  parentKey: navData && navData.rootSegment ? navData.rootSegment : `root-${index}`,
3368
3658
  component: "div",
3369
3659
  className: "canopy-modal__section-nav canopy-modal__section-nav--nested",
3370
- "aria-label": navData && navData.title ? `${navData.title} section navigation` : defaultSectionAriaLabel,
3660
+ "aria-label": navData && navData.title ? formatString(
3661
+ "common.phrases.nav_label",
3662
+ "{content} navigation",
3663
+ { content: navData.title }
3664
+ ) : defaultSectionAriaLabel,
3371
3665
  "aria-hidden": defaultExpanded ? "false" : "true",
3372
3666
  hidden: !defaultExpanded,
3373
3667
  id: nestedId || void 0
@@ -3376,7 +3670,7 @@ function CanopyHeader(props = {}) {
3376
3670
  );
3377
3671
  }))
3378
3672
  ),
3379
- hasSectionNav && !hasIntegratedSectionNav ? /* @__PURE__ */ React24.createElement(
3673
+ hasSectionNav && !hasIntegratedSectionNav ? /* @__PURE__ */ React25.createElement(
3380
3674
  NavigationTree,
3381
3675
  {
3382
3676
  root: sectionNavigation.root,
@@ -3386,7 +3680,7 @@ function CanopyHeader(props = {}) {
3386
3680
  parentKey: "fallback-nav"
3387
3681
  }
3388
3682
  ) : null
3389
- ), /* @__PURE__ */ React24.createElement(
3683
+ ), /* @__PURE__ */ React25.createElement(
3390
3684
  CanopyModal,
3391
3685
  {
3392
3686
  id: "canopy-modal-search",
@@ -3395,31 +3689,31 @@ function CanopyHeader(props = {}) {
3395
3689
  label: resolvedTitle,
3396
3690
  logo: SiteLogo,
3397
3691
  href: brandHref,
3398
- closeLabel: "Close search",
3692
+ closeLabel: closeSearchLabel,
3399
3693
  closeDataAttr: "search",
3400
3694
  bodyClassName: "canopy-modal__body--search"
3401
3695
  },
3402
- /* @__PURE__ */ React24.createElement(
3696
+ /* @__PURE__ */ React25.createElement(
3403
3697
  SearchPanel,
3404
3698
  {
3405
- label: searchLabel,
3699
+ label: resolvedSearchLabel,
3406
3700
  hotkey: searchHotkey,
3407
- placeholder: searchPlaceholder,
3701
+ placeholder: resolvedSearchPlaceholder,
3408
3702
  searchPath: normalizedSearchRoute
3409
3703
  }
3410
3704
  )
3411
- ), /* @__PURE__ */ React24.createElement(HeaderScript, null));
3705
+ ), /* @__PURE__ */ React25.createElement(HeaderScript, null));
3412
3706
  }
3413
3707
 
3414
3708
  // ui/src/layout/CanopyFooter.jsx
3415
- import React25 from "react";
3709
+ import React26 from "react";
3416
3710
  function CanopyFooter({ className = "", children }) {
3417
3711
  const footerClassName = ["canopy-footer", className].filter(Boolean).join(" ");
3418
- return /* @__PURE__ */ React25.createElement("footer", { className: footerClassName }, /* @__PURE__ */ React25.createElement("div", { className: "canopy-footer__inner" }, children));
3712
+ return /* @__PURE__ */ React26.createElement("footer", { className: footerClassName }, /* @__PURE__ */ React26.createElement("div", { className: "canopy-footer__inner" }, children));
3419
3713
  }
3420
3714
 
3421
3715
  // ui/src/layout/TeaserCard.jsx
3422
- import React26 from "react";
3716
+ import React27 from "react";
3423
3717
  function TeaserCard({
3424
3718
  href = "",
3425
3719
  title = "",
@@ -3440,7 +3734,7 @@ function TeaserCard({
3440
3734
  ].filter(Boolean).join(" ");
3441
3735
  const showThumb = type === "work" && thumbnail;
3442
3736
  const metaLine = (Array.isArray(metadata) && metadata.length ? metadata.filter(Boolean) : summary ? [summary] : []).filter(Boolean).slice(0, 2).join(" \u2022 ");
3443
- return /* @__PURE__ */ React26.createElement(
3737
+ return /* @__PURE__ */ React27.createElement(
3444
3738
  Tag,
3445
3739
  {
3446
3740
  className: classes,
@@ -3448,7 +3742,7 @@ function TeaserCard({
3448
3742
  "data-canopy-item": href ? "" : void 0,
3449
3743
  ...rest
3450
3744
  },
3451
- showThumb ? /* @__PURE__ */ React26.createElement("div", { className: "canopy-search-teaser__thumb" }, /* @__PURE__ */ React26.createElement(
3745
+ showThumb ? /* @__PURE__ */ React27.createElement("div", { className: "canopy-search-teaser__thumb" }, /* @__PURE__ */ React27.createElement(
3452
3746
  "img",
3453
3747
  {
3454
3748
  src: thumbnail,
@@ -3458,12 +3752,12 @@ function TeaserCard({
3458
3752
  className: "canopy-search-teaser__thumb-img"
3459
3753
  }
3460
3754
  )) : null,
3461
- /* @__PURE__ */ React26.createElement("div", { className: "canopy-search-teaser__text" }, /* @__PURE__ */ React26.createElement("span", { className: "canopy-search-teaser__title" }, title || href || "Untitled"), metaLine ? /* @__PURE__ */ React26.createElement("span", { className: "canopy-search-teaser__meta" }, metaLine) : null)
3755
+ /* @__PURE__ */ React27.createElement("div", { className: "canopy-search-teaser__text" }, /* @__PURE__ */ React27.createElement("span", { className: "canopy-search-teaser__title" }, title || href || "Untitled"), metaLine ? /* @__PURE__ */ React27.createElement("span", { className: "canopy-search-teaser__meta" }, metaLine) : null)
3462
3756
  );
3463
3757
  }
3464
3758
 
3465
3759
  // ui/src/layout/GoogleAnalytics.jsx
3466
- import React27 from "react";
3760
+ import React28 from "react";
3467
3761
  var GA_HOST = "https://www.googletagmanager.com/gtag/js";
3468
3762
  function GoogleAnalytics({ id }) {
3469
3763
  if (!id) return null;
@@ -3473,11 +3767,11 @@ function GoogleAnalytics({ id }) {
3473
3767
  gtag('js', new Date());
3474
3768
  gtag('config', '${id}');
3475
3769
  `;
3476
- return /* @__PURE__ */ React27.createElement(React27.Fragment, null, /* @__PURE__ */ React27.createElement("script", { async: true, src: `${GA_HOST}?id=${encodeURIComponent(id)}` }), /* @__PURE__ */ React27.createElement("script", { dangerouslySetInnerHTML: { __html: inlineConfig } }));
3770
+ return /* @__PURE__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement("script", { async: true, src: `${GA_HOST}?id=${encodeURIComponent(id)}` }), /* @__PURE__ */ React28.createElement("script", { dangerouslySetInnerHTML: { __html: inlineConfig } }));
3477
3771
  }
3478
3772
 
3479
3773
  // ui/src/layout/Container.jsx
3480
- import React28 from "react";
3774
+ import React29 from "react";
3481
3775
  function Container({
3482
3776
  className = "",
3483
3777
  variant = "content",
@@ -3486,7 +3780,7 @@ function Container({
3486
3780
  }) {
3487
3781
  const variantClass = variant === "wide" ? "max-w-wide" : "max-w-content";
3488
3782
  const classes = ["mx-auto", variantClass, "w-full", className].filter(Boolean).join(" ");
3489
- return /* @__PURE__ */ React28.createElement(
3783
+ return /* @__PURE__ */ React29.createElement(
3490
3784
  "div",
3491
3785
  {
3492
3786
  className: classes,
@@ -3498,7 +3792,7 @@ function Container({
3498
3792
  }
3499
3793
 
3500
3794
  // ui/src/layout/Card.jsx
3501
- import React29, { useEffect as useEffect6, useRef as useRef2, useState as useState5 } from "react";
3795
+ import React30, { useEffect as useEffect6, useRef as useRef2, useState as useState5 } from "react";
3502
3796
  var DEFAULT_CARD_ASPECT_RATIO = 4 / 3;
3503
3797
  function Card({
3504
3798
  href,
@@ -3562,8 +3856,8 @@ function Card({
3562
3856
  const hasDimensions = Number.isFinite(w) && w > 0 && Number.isFinite(h) && h > 0;
3563
3857
  const ratio = hasAspectRatio ? Number(aspectRatio) : hasDimensions ? w / h : src ? DEFAULT_CARD_ASPECT_RATIO : void 0;
3564
3858
  const paddingPercent = ratio ? 100 / ratio : 100;
3565
- const caption = /* @__PURE__ */ React29.createElement("figcaption", null, title && /* @__PURE__ */ React29.createElement("span", null, title), subtitle && /* @__PURE__ */ React29.createElement("span", null, subtitle), children);
3566
- return /* @__PURE__ */ React29.createElement(
3859
+ const caption = /* @__PURE__ */ React30.createElement("figcaption", null, title && /* @__PURE__ */ React30.createElement("span", null, title), subtitle && /* @__PURE__ */ React30.createElement("span", null, subtitle), children);
3860
+ return /* @__PURE__ */ React30.createElement(
3567
3861
  "a",
3568
3862
  {
3569
3863
  href,
@@ -3575,13 +3869,13 @@ function Card({
3575
3869
  "data-image-loaded": imageLoaded ? "true" : "false",
3576
3870
  ...rest
3577
3871
  },
3578
- /* @__PURE__ */ React29.createElement("figure", null, src ? ratio ? /* @__PURE__ */ React29.createElement(
3872
+ /* @__PURE__ */ React30.createElement("figure", null, src ? ratio ? /* @__PURE__ */ React30.createElement(
3579
3873
  "div",
3580
3874
  {
3581
3875
  className: "canopy-card-media",
3582
3876
  style: { "--canopy-card-padding": `${paddingPercent}%` }
3583
3877
  },
3584
- inView ? /* @__PURE__ */ React29.createElement(
3878
+ inView ? /* @__PURE__ */ React30.createElement(
3585
3879
  "img",
3586
3880
  {
3587
3881
  src,
@@ -3592,7 +3886,7 @@ function Card({
3592
3886
  onError: () => setImageLoaded(true)
3593
3887
  }
3594
3888
  ) : null
3595
- ) : /* @__PURE__ */ React29.createElement(
3889
+ ) : /* @__PURE__ */ React30.createElement(
3596
3890
  "img",
3597
3891
  {
3598
3892
  src,
@@ -3608,13 +3902,13 @@ function Card({
3608
3902
  }
3609
3903
 
3610
3904
  // ui/src/content/ReferencedItems.jsx
3611
- import React30 from "react";
3905
+ import React31 from "react";
3612
3906
  import navigationHelpers4 from "@canopy-iiif/app/lib/components/navigation.js";
3613
3907
  function useReferencedItems(itemsProp) {
3614
3908
  if (Array.isArray(itemsProp)) return itemsProp;
3615
3909
  const PageContext2 = navigationHelpers4 && typeof navigationHelpers4.getPageContext === "function" ? navigationHelpers4.getPageContext() : null;
3616
3910
  if (!PageContext2) return [];
3617
- const context = React30.useContext(PageContext2);
3911
+ const context = React31.useContext(PageContext2);
3618
3912
  const items = context && context.page ? context.page.referencedItems : null;
3619
3913
  return Array.isArray(items) ? items : [];
3620
3914
  }
@@ -3634,13 +3928,13 @@ function ReferencedItems({
3634
3928
  "referenced-items--empty",
3635
3929
  className
3636
3930
  ].filter(Boolean).join(" ");
3637
- return /* @__PURE__ */ React30.createElement("div", { className: emptyClass, ...rest }, typeof emptyLabel === "function" ? emptyLabel() : emptyLabel);
3931
+ return /* @__PURE__ */ React31.createElement("div", { className: emptyClass, ...rest }, typeof emptyLabel === "function" ? emptyLabel() : emptyLabel);
3638
3932
  }
3639
3933
  const containerClassName = ["referenced-items", className].filter(Boolean).join(" ");
3640
- return /* @__PURE__ */ React30.createElement("section", { className: containerClassName, ...rest }, children, /* @__PURE__ */ React30.createElement("div", { className: "referenced-items__grid", role: "list" }, items.map((item) => {
3934
+ return /* @__PURE__ */ React31.createElement("section", { className: containerClassName, ...rest }, children, /* @__PURE__ */ React31.createElement("div", { className: "referenced-items__grid", role: "list" }, items.map((item) => {
3641
3935
  if (!item) return null;
3642
3936
  const key = item.href || item.slug || item.id;
3643
- return /* @__PURE__ */ React30.createElement("div", { className: "referenced-items__item", role: "listitem", key }, /* @__PURE__ */ React30.createElement(
3937
+ return /* @__PURE__ */ React31.createElement("div", { className: "referenced-items__item", role: "listitem", key }, /* @__PURE__ */ React31.createElement(
3644
3938
  Card,
3645
3939
  {
3646
3940
  href: item.href,
@@ -3657,7 +3951,7 @@ function ReferencedItems({
3657
3951
  }
3658
3952
 
3659
3953
  // ui/src/content/References.jsx
3660
- import React31 from "react";
3954
+ import React32 from "react";
3661
3955
  import navigationHelpers5 from "@canopy-iiif/app/lib/components/navigation.js";
3662
3956
  import referenced from "@canopy-iiif/app/lib/components/referenced.js";
3663
3957
  function getPageContext() {
@@ -3681,14 +3975,16 @@ function resolveReferences(manifestId, contextList) {
3681
3975
  }
3682
3976
  function References({
3683
3977
  id = "",
3684
- title = "Referenced by",
3978
+ title,
3685
3979
  emptyLabel = null,
3686
3980
  className = "",
3687
3981
  children,
3688
3982
  ...rest
3689
3983
  }) {
3984
+ const { getString } = useLocale2();
3985
+ const resolvedTitle = title != null ? title : getString("common.misc.referenced_by", "Referenced by");
3690
3986
  const PageContext2 = getPageContext();
3691
- const context = PageContext2 ? React31.useContext(PageContext2) : null;
3987
+ const context = PageContext2 ? React32.useContext(PageContext2) : null;
3692
3988
  const contextPage = context && context.page ? context.page : null;
3693
3989
  const manifestId = id || contextPage && contextPage.manifestId || "";
3694
3990
  const contextReferences = !id && contextPage && Array.isArray(contextPage.referencedBy) ? contextPage.referencedBy : null;
@@ -3697,12 +3993,12 @@ function References({
3697
3993
  const entries = references && references.length ? references : null;
3698
3994
  if (!entries || !entries.length) return null;
3699
3995
  const containerClass = ["references", className].filter(Boolean).join(" ");
3700
- return /* @__PURE__ */ React31.createElement("dl", { className: containerClass, ...rest }, /* @__PURE__ */ React31.createElement("div", { className: "references__group" }, /* @__PURE__ */ React31.createElement("dt", null, title), entries.map((entry) => /* @__PURE__ */ React31.createElement("dd", { key: entry.href, className: "references__item" }, /* @__PURE__ */ React31.createElement("a", { href: entry.href }, entry.title || entry.href)))));
3996
+ return /* @__PURE__ */ React32.createElement("dl", { className: containerClass, ...rest }, /* @__PURE__ */ React32.createElement("div", { className: "references__group" }, /* @__PURE__ */ React32.createElement("dt", null, resolvedTitle), entries.map((entry) => /* @__PURE__ */ React32.createElement("dd", { key: entry.href, className: "references__item" }, /* @__PURE__ */ React32.createElement("a", { href: entry.href }, entry.title || entry.href)))));
3701
3997
  }
3702
3998
 
3703
3999
  // ui/src/content/Index.jsx
3704
4000
  var import_slugify = __toESM(require_slugify());
3705
- import React32 from "react";
4001
+ import React33 from "react";
3706
4002
  import metadataIndexHelpers from "@canopy-iiif/app/lib/components/metadata-index.js";
3707
4003
  var metadataModule = metadataIndexHelpers && typeof metadataIndexHelpers === "object" ? metadataIndexHelpers : null;
3708
4004
  var SLUG_OPTIONS = { lower: true, strict: true, trim: true };
@@ -3865,7 +4161,7 @@ function Index({
3865
4161
  const entries = filterByLabel(data, label);
3866
4162
  if (!entries.length) return null;
3867
4163
  const containerClass = ["canopy-index", className].filter(Boolean).join(" ");
3868
- return /* @__PURE__ */ React32.createElement("div", { className: containerClass, ...rest }, entries.map((entry) => /* @__PURE__ */ React32.createElement(
4164
+ return /* @__PURE__ */ React33.createElement("div", { className: containerClass, ...rest }, entries.map((entry) => /* @__PURE__ */ React33.createElement(
3869
4165
  IndexGroup,
3870
4166
  {
3871
4167
  key: entry.slug || entry.label,
@@ -3876,7 +4172,7 @@ function Index({
3876
4172
  expandLabel,
3877
4173
  collapseLabel
3878
4174
  }
3879
- )), /* @__PURE__ */ React32.createElement(
4175
+ )), /* @__PURE__ */ React33.createElement(
3880
4176
  "script",
3881
4177
  {
3882
4178
  "data-canopy-index-script": "",
@@ -3893,10 +4189,10 @@ function IndexGroup({ label, labelSlug, values, limit, expandLabel, collapseLabe
3893
4189
  const hiddenValues = hasOverflow ? values.slice(maxVisible) : [];
3894
4190
  const labelCollapsed = typeof expandLabel === "string" && expandLabel.trim() ? expandLabel.trim() : "Show more";
3895
4191
  const labelExpanded = typeof collapseLabel === "string" && collapseLabel.trim() ? collapseLabel.trim() : "Show less";
3896
- return /* @__PURE__ */ React32.createElement("dl", { className: "canopy-index__group", "data-canopy-index-group": "", "data-expanded": "0" }, /* @__PURE__ */ React32.createElement("dt", null, label), /* @__PURE__ */ React32.createElement("div", { className: "canopy-index__values" }, visibleValues.map((value) => {
4192
+ return /* @__PURE__ */ React33.createElement("dl", { className: "canopy-index__group", "data-canopy-index-group": "", "data-expanded": "0" }, /* @__PURE__ */ React33.createElement("dt", null, label), /* @__PURE__ */ React33.createElement("div", { className: "canopy-index__values" }, visibleValues.map((value) => {
3897
4193
  const href = buildSearchHref(labelSlug, value.slug);
3898
4194
  const key = `${labelSlug || label}-${value.slug || value.value}`;
3899
- return /* @__PURE__ */ React32.createElement("dd", { key }, href ? /* @__PURE__ */ React32.createElement(
4195
+ return /* @__PURE__ */ React33.createElement("dd", { key }, href ? /* @__PURE__ */ React33.createElement(
3900
4196
  "a",
3901
4197
  {
3902
4198
  href,
@@ -3909,7 +4205,7 @@ function IndexGroup({ label, labelSlug, values, limit, expandLabel, collapseLabe
3909
4205
  }), hiddenValues.map((value) => {
3910
4206
  const href = buildSearchHref(labelSlug, value.slug);
3911
4207
  const key = `${labelSlug || label}-hidden-${value.slug || value.value}`;
3912
- return /* @__PURE__ */ React32.createElement("dd", { key, "data-canopy-index-hidden": "", hidden: true }, href ? /* @__PURE__ */ React32.createElement(
4208
+ return /* @__PURE__ */ React33.createElement("dd", { key, "data-canopy-index-hidden": "", hidden: true }, href ? /* @__PURE__ */ React33.createElement(
3913
4209
  "a",
3914
4210
  {
3915
4211
  href,
@@ -3919,7 +4215,7 @@ function IndexGroup({ label, labelSlug, values, limit, expandLabel, collapseLabe
3919
4215
  },
3920
4216
  value.value
3921
4217
  ) : value.value);
3922
- })), hasOverflow && /* @__PURE__ */ React32.createElement("div", { className: "canopy-index__more-wrapper" }, /* @__PURE__ */ React32.createElement(
4218
+ })), hasOverflow && /* @__PURE__ */ React33.createElement("div", { className: "canopy-index__more-wrapper" }, /* @__PURE__ */ React33.createElement(
3923
4219
  "button",
3924
4220
  {
3925
4221
  type: "button",
@@ -3933,7 +4229,7 @@ function IndexGroup({ label, labelSlug, values, limit, expandLabel, collapseLabe
3933
4229
  }
3934
4230
 
3935
4231
  // ui/src/content/Bibliography.jsx
3936
- import React33 from "react";
4232
+ import React34 from "react";
3937
4233
  import bibliography from "@canopy-iiif/app/lib/components/bibliography.js";
3938
4234
  function resolveHeadingTag(tag, fallback) {
3939
4235
  if (typeof tag === "string" && tag.trim()) return tag;
@@ -3943,7 +4239,7 @@ function resolveHeadingTag(tag, fallback) {
3943
4239
  function NoteBody({ note }) {
3944
4240
  if (!note) return null;
3945
4241
  if (note.html) {
3946
- return /* @__PURE__ */ React33.createElement(
4242
+ return /* @__PURE__ */ React34.createElement(
3947
4243
  "div",
3948
4244
  {
3949
4245
  className: "bibliography__note-body",
@@ -3952,7 +4248,7 @@ function NoteBody({ note }) {
3952
4248
  );
3953
4249
  }
3954
4250
  if (note.text) {
3955
- return /* @__PURE__ */ React33.createElement("div", { className: "bibliography__note-body" }, note.text);
4251
+ return /* @__PURE__ */ React34.createElement("div", { className: "bibliography__note-body" }, note.text);
3956
4252
  }
3957
4253
  return null;
3958
4254
  }
@@ -3970,22 +4266,22 @@ function Bibliography({
3970
4266
  if (!entries.length) return null;
3971
4267
  const PageHeadingTag = resolveHeadingTag(pageHeadingTag, "h3");
3972
4268
  const rootClass = ["bibliography", className].filter(Boolean).join(" ");
3973
- return /* @__PURE__ */ React33.createElement("section", { className: rootClass }, /* @__PURE__ */ React33.createElement("div", { className: "bibliography__pages" }, entries.map((entry) => {
4269
+ return /* @__PURE__ */ React34.createElement("section", { className: rootClass }, /* @__PURE__ */ React34.createElement("div", { className: "bibliography__pages" }, entries.map((entry) => {
3974
4270
  const key = entry.href || entry.relativePath || entry.title;
3975
4271
  const pageTitle = entry.title || entry.href;
3976
- return /* @__PURE__ */ React33.createElement("article", { key, className: "bibliography__page" }, /* @__PURE__ */ React33.createElement("header", { className: "bibliography__page-header" }, pageTitle ? /* @__PURE__ */ React33.createElement(PageHeadingTag, { className: "bibliography__page-title" }, pageTitle) : null, entry.href ? /* @__PURE__ */ React33.createElement("a", { className: "bibliography__page-link", href: entry.href }, entry.href) : null), /* @__PURE__ */ React33.createElement("ol", { className: "bibliography__notes" }, (entry.footnotes || []).map((note, idx) => {
4272
+ return /* @__PURE__ */ React34.createElement("article", { key, className: "bibliography__page" }, /* @__PURE__ */ React34.createElement("header", { className: "bibliography__page-header" }, pageTitle ? /* @__PURE__ */ React34.createElement(PageHeadingTag, { className: "bibliography__page-title" }, pageTitle) : null, entry.href ? /* @__PURE__ */ React34.createElement("a", { className: "bibliography__page-link", href: entry.href }, entry.href) : null), /* @__PURE__ */ React34.createElement("ol", { className: "bibliography__notes" }, (entry.footnotes || []).map((note, idx) => {
3977
4273
  const noteKey = `${key || "entry"}-${note.identifier || idx}`;
3978
- return /* @__PURE__ */ React33.createElement("li", { key: noteKey, className: "bibliography__note" }, note.identifier ? /* @__PURE__ */ React33.createElement("span", { className: "bibliography__note-label" }, note.identifier) : null, /* @__PURE__ */ React33.createElement(NoteBody, { note }));
4274
+ return /* @__PURE__ */ React34.createElement("li", { key: noteKey, className: "bibliography__note" }, note.identifier ? /* @__PURE__ */ React34.createElement("span", { className: "bibliography__note-label" }, note.identifier) : null, /* @__PURE__ */ React34.createElement(NoteBody, { note }));
3979
4275
  })));
3980
4276
  })));
3981
4277
  }
3982
4278
 
3983
4279
  // ui/src/content/timeline/MdxTimeline.jsx
3984
- import React37 from "react";
4280
+ import React38 from "react";
3985
4281
  import ReactDOMServer from "react-dom/server";
3986
4282
 
3987
4283
  // ui/src/content/timeline/Timeline.jsx
3988
- import React35 from "react";
4284
+ import React36 from "react";
3989
4285
 
3990
4286
  // ui/src/content/timeline/date-utils.js
3991
4287
  var FALLBACK_LOCALE = (() => {
@@ -4146,7 +4442,7 @@ function clampProgress(value) {
4146
4442
  }
4147
4443
 
4148
4444
  // ui/src/layout/ReferencedManifestCard.jsx
4149
- import React34 from "react";
4445
+ import React35 from "react";
4150
4446
  function normalizeMetadata(metadata, summary) {
4151
4447
  if (Array.isArray(metadata) && metadata.length) {
4152
4448
  return metadata.filter(Boolean);
@@ -4180,7 +4476,7 @@ function ReferencedManifestCard({
4180
4476
  "canopy-referenced-manifest-card",
4181
4477
  className
4182
4478
  ].filter(Boolean).join(" ");
4183
- return /* @__PURE__ */ React34.createElement(
4479
+ return /* @__PURE__ */ React35.createElement(
4184
4480
  TeaserCard,
4185
4481
  {
4186
4482
  href: resolvedHref || void 0,
@@ -4362,14 +4658,14 @@ function TimelineConnector({ side, isActive, highlight }) {
4362
4658
  "canopy-timeline__connector-dot",
4363
4659
  highlight || isActive ? "is-active" : ""
4364
4660
  ].filter(Boolean).join(" ");
4365
- return /* @__PURE__ */ React35.createElement("span", { className: connectorClasses, "aria-hidden": "true" }, side === "left" ? /* @__PURE__ */ React35.createElement(React35.Fragment, null, /* @__PURE__ */ React35.createElement("span", { className: "canopy-timeline__connector-line" }), /* @__PURE__ */ React35.createElement("span", { className: dotClasses })) : /* @__PURE__ */ React35.createElement(React35.Fragment, null, /* @__PURE__ */ React35.createElement("span", { className: dotClasses }), /* @__PURE__ */ React35.createElement("span", { className: "canopy-timeline__connector-line" })));
4661
+ return /* @__PURE__ */ React36.createElement("span", { className: connectorClasses, "aria-hidden": "true" }, side === "left" ? /* @__PURE__ */ React36.createElement(React36.Fragment, null, /* @__PURE__ */ React36.createElement("span", { className: "canopy-timeline__connector-line" }), /* @__PURE__ */ React36.createElement("span", { className: dotClasses })) : /* @__PURE__ */ React36.createElement(React36.Fragment, null, /* @__PURE__ */ React36.createElement("span", { className: dotClasses }), /* @__PURE__ */ React36.createElement("span", { className: "canopy-timeline__connector-line" })));
4366
4662
  }
4367
4663
  function renderResourceSection(point) {
4368
4664
  if (!point) return null;
4369
4665
  const manifestCards = Array.isArray(point.manifests) ? point.manifests.filter(Boolean) : [];
4370
4666
  const legacyResources = Array.isArray(point.resources) ? point.resources.filter(Boolean) : [];
4371
4667
  if (!manifestCards.length && !legacyResources.length) return null;
4372
- return /* @__PURE__ */ React35.createElement("div", { className: "canopy-timeline__resources" }, /* @__PURE__ */ React35.createElement("div", { className: "canopy-timeline__resources-list" }, manifestCards.map((manifest) => /* @__PURE__ */ React35.createElement("div", { key: manifest.id || manifest.href }, /* @__PURE__ */ React35.createElement(ReferencedManifestCard, { manifest }))), legacyResources.map((resource, idx) => /* @__PURE__ */ React35.createElement("div", { key: resource.id || resource.href || `legacy-${idx}` }, /* @__PURE__ */ React35.createElement(
4668
+ return /* @__PURE__ */ React36.createElement("div", { className: "canopy-timeline__resources" }, /* @__PURE__ */ React36.createElement("div", { className: "canopy-timeline__resources-list" }, manifestCards.map((manifest) => /* @__PURE__ */ React36.createElement("div", { key: manifest.id || manifest.href }, /* @__PURE__ */ React36.createElement(ReferencedManifestCard, { manifest }))), legacyResources.map((resource, idx) => /* @__PURE__ */ React36.createElement("div", { key: resource.id || resource.href || `legacy-${idx}` }, /* @__PURE__ */ React36.createElement(
4373
4669
  TeaserCard,
4374
4670
  {
4375
4671
  href: resource.href,
@@ -4394,26 +4690,26 @@ function Timeline({
4394
4690
  ...rest
4395
4691
  }) {
4396
4692
  const payloadPoints = payload && Array.isArray(payload.points) ? payload.points : null;
4397
- const rawPoints = React35.useMemo(() => {
4693
+ const rawPoints = React36.useMemo(() => {
4398
4694
  if (Array.isArray(pointsProp) && pointsProp.length) return pointsProp;
4399
4695
  if (payloadPoints && payloadPoints.length) return payloadPoints;
4400
4696
  return [];
4401
4697
  }, [pointsProp, payloadPoints]);
4402
- const sanitizedPoints = React35.useMemo(
4698
+ const sanitizedPoints = React36.useMemo(
4403
4699
  () => sanitizePoints(rawPoints),
4404
4700
  [rawPoints]
4405
4701
  );
4406
4702
  const localeValue = payload && payload.locale ? payload.locale : localeProp;
4407
- const baseLocale = React35.useMemo(
4703
+ const baseLocale = React36.useMemo(
4408
4704
  () => createLocale(localeValue),
4409
4705
  [localeValue]
4410
4706
  );
4411
4707
  const rangeInput = payload && payload.range ? payload.range : rangeProp || {};
4412
- const rangeOverrides = React35.useMemo(
4708
+ const rangeOverrides = React36.useMemo(
4413
4709
  () => deriveRangeOverrides(sanitizedPoints, rangeInput),
4414
4710
  [sanitizedPoints, rangeInput]
4415
4711
  );
4416
- const effectiveRange = React35.useMemo(
4712
+ const effectiveRange = React36.useMemo(
4417
4713
  () => normalizeRange({
4418
4714
  ...rangeOverrides,
4419
4715
  locale: baseLocale
@@ -4422,7 +4718,7 @@ function Timeline({
4422
4718
  );
4423
4719
  const spanStart = effectiveRange.startDate.getTime();
4424
4720
  const span = effectiveRange.span;
4425
- const pointsWithPosition = React35.useMemo(() => {
4721
+ const pointsWithPosition = React36.useMemo(() => {
4426
4722
  if (!sanitizedPoints.length) return [];
4427
4723
  return sanitizedPoints.map((point, index) => {
4428
4724
  const timestamp = point.meta.timestamp;
@@ -4436,29 +4732,29 @@ function Timeline({
4436
4732
  };
4437
4733
  });
4438
4734
  }, [sanitizedPoints, spanStart, span]);
4439
- const [activeId, setActiveId] = React35.useState(
4735
+ const [activeId, setActiveId] = React36.useState(
4440
4736
  () => getActivePointId(pointsWithPosition)
4441
4737
  );
4442
- React35.useEffect(() => {
4738
+ React36.useEffect(() => {
4443
4739
  setActiveId(getActivePointId(pointsWithPosition));
4444
4740
  }, [pointsWithPosition]);
4445
4741
  const thresholdValue = typeof thresholdProp === "number" ? thresholdProp : payload && payload.threshold != null ? payload.threshold : null;
4446
4742
  const stepsValue = typeof steps === "number" ? Number(steps) : payload && typeof payload.steps === "number" ? Number(payload.steps) : null;
4447
- const thresholdMs = React35.useMemo(
4743
+ const thresholdMs = React36.useMemo(
4448
4744
  () => getThresholdMs(thresholdValue, effectiveRange.granularity),
4449
4745
  [thresholdValue, effectiveRange.granularity]
4450
4746
  );
4451
- const groupedEntries = React35.useMemo(
4747
+ const groupedEntries = React36.useMemo(
4452
4748
  () => buildGroupedEntries(pointsWithPosition, thresholdMs, {
4453
4749
  granularity: effectiveRange.granularity,
4454
4750
  locale: baseLocale
4455
4751
  }),
4456
4752
  [pointsWithPosition, thresholdMs, effectiveRange.granularity, baseLocale]
4457
4753
  );
4458
- const [expandedGroupIds, setExpandedGroupIds] = React35.useState(
4754
+ const [expandedGroupIds, setExpandedGroupIds] = React36.useState(
4459
4755
  () => /* @__PURE__ */ new Set()
4460
4756
  );
4461
- React35.useEffect(() => {
4757
+ React36.useEffect(() => {
4462
4758
  setExpandedGroupIds((prev) => {
4463
4759
  if (!prev || prev.size === 0) return prev;
4464
4760
  const validIds = new Set(
@@ -4473,7 +4769,7 @@ function Timeline({
4473
4769
  return changed ? next : prev;
4474
4770
  });
4475
4771
  }, [groupedEntries]);
4476
- const toggleGroup = React35.useCallback((groupId) => {
4772
+ const toggleGroup = React36.useCallback((groupId) => {
4477
4773
  setExpandedGroupIds((prev) => {
4478
4774
  const next = new Set(prev || []);
4479
4775
  if (next.has(groupId)) next.delete(groupId);
@@ -4496,7 +4792,7 @@ function Timeline({
4496
4792
  point.id === activeId ? "is-active" : "",
4497
4793
  point.highlight ? "is-highlighted" : ""
4498
4794
  ].filter(Boolean).join(" ");
4499
- const connector = /* @__PURE__ */ React35.createElement(
4795
+ const connector = /* @__PURE__ */ React36.createElement(
4500
4796
  TimelineConnector,
4501
4797
  {
4502
4798
  side: point.side,
@@ -4504,9 +4800,9 @@ function Timeline({
4504
4800
  highlight: point.highlight
4505
4801
  }
4506
4802
  );
4507
- const body = /* @__PURE__ */ React35.createElement("div", { className: "canopy-timeline__point-body" }, /* @__PURE__ */ React35.createElement("span", { className: "canopy-timeline__point-date" }, point.meta.label), /* @__PURE__ */ React35.createElement("span", { className: "canopy-timeline__point-title" }, point.title), point.summary ? /* @__PURE__ */ React35.createElement("span", { className: "canopy-timeline__point-summary" }, point.summary) : null);
4803
+ const body = /* @__PURE__ */ React36.createElement("div", { className: "canopy-timeline__point-body" }, /* @__PURE__ */ React36.createElement("span", { className: "canopy-timeline__point-date" }, point.meta.label), /* @__PURE__ */ React36.createElement("span", { className: "canopy-timeline__point-title" }, point.title), point.summary ? /* @__PURE__ */ React36.createElement("span", { className: "canopy-timeline__point-summary" }, point.summary) : null);
4508
4804
  const resourceSection = renderResourceSection(point);
4509
- return /* @__PURE__ */ React35.createElement(
4805
+ return /* @__PURE__ */ React36.createElement(
4510
4806
  "div",
4511
4807
  {
4512
4808
  key: point.id,
@@ -4514,7 +4810,7 @@ function Timeline({
4514
4810
  style: wrapperStyle,
4515
4811
  role: "listitem"
4516
4812
  },
4517
- point.side === "left" ? /* @__PURE__ */ React35.createElement(React35.Fragment, null, /* @__PURE__ */ React35.createElement("div", { className: cardClasses }, body, resourceSection), connector) : /* @__PURE__ */ React35.createElement(React35.Fragment, null, connector, /* @__PURE__ */ React35.createElement("div", { className: cardClasses }, body, resourceSection))
4813
+ point.side === "left" ? /* @__PURE__ */ React36.createElement(React36.Fragment, null, /* @__PURE__ */ React36.createElement("div", { className: cardClasses }, body, resourceSection), connector) : /* @__PURE__ */ React36.createElement(React36.Fragment, null, connector, /* @__PURE__ */ React36.createElement("div", { className: cardClasses }, body, resourceSection))
4518
4814
  );
4519
4815
  }
4520
4816
  function renderGroupEntry(entry) {
@@ -4525,7 +4821,7 @@ function Timeline({
4525
4821
  const wrapperStyle = { top: `${entry.progress * 100}%` };
4526
4822
  const isExpanded = expandedGroupIds.has(entry.id);
4527
4823
  const hasActivePoint = entry.points.some((point) => point.id === activeId);
4528
- const connector = /* @__PURE__ */ React35.createElement(
4824
+ const connector = /* @__PURE__ */ React36.createElement(
4529
4825
  TimelineConnector,
4530
4826
  {
4531
4827
  side: entry.side,
@@ -4539,7 +4835,7 @@ function Timeline({
4539
4835
  hasActivePoint ? "is-active" : ""
4540
4836
  ].filter(Boolean).join(" ");
4541
4837
  const countLabel = `${entry.count} event${entry.count > 1 ? "s" : ""}`;
4542
- const header = /* @__PURE__ */ React35.createElement("div", { className: "canopy-timeline__group-header" }, /* @__PURE__ */ React35.createElement("div", { className: "canopy-timeline__group-summary" }, /* @__PURE__ */ React35.createElement("span", { className: "canopy-timeline__point-date" }, entry.label), /* @__PURE__ */ React35.createElement("span", { className: "canopy-timeline__group-count" }, countLabel)), /* @__PURE__ */ React35.createElement(
4838
+ const header = /* @__PURE__ */ React36.createElement("div", { className: "canopy-timeline__group-header" }, /* @__PURE__ */ React36.createElement("div", { className: "canopy-timeline__group-summary" }, /* @__PURE__ */ React36.createElement("span", { className: "canopy-timeline__point-date" }, entry.label), /* @__PURE__ */ React36.createElement("span", { className: "canopy-timeline__group-count" }, countLabel)), /* @__PURE__ */ React36.createElement(
4543
4839
  "button",
4544
4840
  {
4545
4841
  type: "button",
@@ -4549,7 +4845,7 @@ function Timeline({
4549
4845
  },
4550
4846
  isExpanded ? "Hide details" : "Show details"
4551
4847
  ));
4552
- const groupPoints = isExpanded ? /* @__PURE__ */ React35.createElement("div", { className: "canopy-timeline__group-points" }, entry.points.map((point) => /* @__PURE__ */ React35.createElement(
4848
+ const groupPoints = isExpanded ? /* @__PURE__ */ React36.createElement("div", { className: "canopy-timeline__group-points" }, entry.points.map((point) => /* @__PURE__ */ React36.createElement(
4553
4849
  "button",
4554
4850
  {
4555
4851
  key: point.id,
@@ -4560,11 +4856,11 @@ function Timeline({
4560
4856
  ].filter(Boolean).join(" "),
4561
4857
  onClick: () => setActiveId(point.id)
4562
4858
  },
4563
- /* @__PURE__ */ React35.createElement("span", { className: "canopy-timeline__point-date" }, point.meta.label),
4564
- /* @__PURE__ */ React35.createElement("span", { className: "canopy-timeline__group-point-title" }, point.title)
4859
+ /* @__PURE__ */ React36.createElement("span", { className: "canopy-timeline__point-date" }, point.meta.label),
4860
+ /* @__PURE__ */ React36.createElement("span", { className: "canopy-timeline__group-point-title" }, point.title)
4565
4861
  ))) : null;
4566
- const groupCard = /* @__PURE__ */ React35.createElement("div", { className: groupClasses }, header, groupPoints);
4567
- return /* @__PURE__ */ React35.createElement(
4862
+ const groupCard = /* @__PURE__ */ React36.createElement("div", { className: groupClasses }, header, groupPoints);
4863
+ return /* @__PURE__ */ React36.createElement(
4568
4864
  "div",
4569
4865
  {
4570
4866
  key: entry.id,
@@ -4572,17 +4868,17 @@ function Timeline({
4572
4868
  style: wrapperStyle,
4573
4869
  role: "listitem"
4574
4870
  },
4575
- entry.side === "left" ? /* @__PURE__ */ React35.createElement(React35.Fragment, null, groupCard, connector) : /* @__PURE__ */ React35.createElement(React35.Fragment, null, connector, groupCard)
4871
+ entry.side === "left" ? /* @__PURE__ */ React36.createElement(React36.Fragment, null, groupCard, connector) : /* @__PURE__ */ React36.createElement(React36.Fragment, null, connector, groupCard)
4576
4872
  );
4577
4873
  }
4578
- return /* @__PURE__ */ React35.createElement("section", { className: containerClasses, ...rest }, title ? /* @__PURE__ */ React35.createElement("h2", { className: "canopy-timeline__title" }, title) : null, description ? /* @__PURE__ */ React35.createElement("p", { className: "canopy-timeline__description" }, description) : null, rangeLabel ? /* @__PURE__ */ React35.createElement("p", { className: "canopy-timeline__range", "aria-live": "polite" }, rangeLabel) : null, /* @__PURE__ */ React35.createElement("div", { className: "canopy-timeline__body" }, /* @__PURE__ */ React35.createElement(
4874
+ return /* @__PURE__ */ React36.createElement("section", { className: containerClasses, ...rest }, title ? /* @__PURE__ */ React36.createElement("h2", { className: "canopy-timeline__title" }, title) : null, description ? /* @__PURE__ */ React36.createElement("p", { className: "canopy-timeline__description" }, description) : null, rangeLabel ? /* @__PURE__ */ React36.createElement("p", { className: "canopy-timeline__range", "aria-live": "polite" }, rangeLabel) : null, /* @__PURE__ */ React36.createElement("div", { className: "canopy-timeline__body" }, /* @__PURE__ */ React36.createElement(
4579
4875
  "div",
4580
4876
  {
4581
4877
  className: "canopy-timeline__list",
4582
4878
  role: "list",
4583
4879
  style: { minHeight: trackHeight }
4584
4880
  },
4585
- /* @__PURE__ */ React35.createElement("div", { className: "canopy-timeline__spine", "aria-hidden": "true" }),
4881
+ /* @__PURE__ */ React36.createElement("div", { className: "canopy-timeline__spine", "aria-hidden": "true" }),
4586
4882
  renderSteps(stepsValue, effectiveRange),
4587
4883
  groupedEntries.map((entry) => {
4588
4884
  if (entry.type === "group") return renderGroupEntry(entry);
@@ -4597,7 +4893,7 @@ function renderSteps(stepSize, range) {
4597
4893
  const markers = [];
4598
4894
  if (startYear < endYear) {
4599
4895
  markers.push(
4600
- /* @__PURE__ */ React35.createElement(
4896
+ /* @__PURE__ */ React36.createElement(
4601
4897
  "span",
4602
4898
  {
4603
4899
  key: "timeline-step-start",
@@ -4605,12 +4901,12 @@ function renderSteps(stepSize, range) {
4605
4901
  style: { top: "0%" },
4606
4902
  "aria-hidden": "true"
4607
4903
  },
4608
- /* @__PURE__ */ React35.createElement("span", { className: "canopy-timeline__step-line" }),
4609
- /* @__PURE__ */ React35.createElement("span", { className: "canopy-timeline__step-label" }, startYear)
4904
+ /* @__PURE__ */ React36.createElement("span", { className: "canopy-timeline__step-line" }),
4905
+ /* @__PURE__ */ React36.createElement("span", { className: "canopy-timeline__step-label" }, startYear)
4610
4906
  )
4611
4907
  );
4612
4908
  markers.push(
4613
- /* @__PURE__ */ React35.createElement(
4909
+ /* @__PURE__ */ React36.createElement(
4614
4910
  "span",
4615
4911
  {
4616
4912
  key: "timeline-step-end",
@@ -4618,8 +4914,8 @@ function renderSteps(stepSize, range) {
4618
4914
  style: { top: "100%" },
4619
4915
  "aria-hidden": "true"
4620
4916
  },
4621
- /* @__PURE__ */ React35.createElement("span", { className: "canopy-timeline__step-line" }),
4622
- /* @__PURE__ */ React35.createElement("span", { className: "canopy-timeline__step-label" }, endYear)
4917
+ /* @__PURE__ */ React36.createElement("span", { className: "canopy-timeline__step-line" }),
4918
+ /* @__PURE__ */ React36.createElement("span", { className: "canopy-timeline__step-label" }, endYear)
4623
4919
  )
4624
4920
  );
4625
4921
  }
@@ -4629,7 +4925,7 @@ function renderSteps(stepSize, range) {
4629
4925
  const progress = (timestamp - range.startDate.getTime()) / range.span;
4630
4926
  if (progress <= 0 || progress >= 1) continue;
4631
4927
  markers.push(
4632
- /* @__PURE__ */ React35.createElement(
4928
+ /* @__PURE__ */ React36.createElement(
4633
4929
  "span",
4634
4930
  {
4635
4931
  key: `timeline-step-${year}`,
@@ -4637,8 +4933,8 @@ function renderSteps(stepSize, range) {
4637
4933
  style: { top: `calc(${progress * 100}% - 0.5px)` },
4638
4934
  "aria-hidden": "true"
4639
4935
  },
4640
- /* @__PURE__ */ React35.createElement("span", { className: "canopy-timeline__step-line" }),
4641
- /* @__PURE__ */ React35.createElement("span", { className: "canopy-timeline__step-label" }, year)
4936
+ /* @__PURE__ */ React36.createElement("span", { className: "canopy-timeline__step-line" }),
4937
+ /* @__PURE__ */ React36.createElement("span", { className: "canopy-timeline__step-label" }, year)
4642
4938
  )
4643
4939
  );
4644
4940
  }
@@ -4652,7 +4948,7 @@ function TimelinePoint() {
4652
4948
  TimelinePoint.displayName = "TimelinePoint";
4653
4949
 
4654
4950
  // ui/src/utils/manifestReferences.js
4655
- import React36 from "react";
4951
+ import React37 from "react";
4656
4952
  var CONTEXT_KEY2 = typeof Symbol === "function" ? Symbol.for("__CANOPY_PAGE_CONTEXT__") : "__CANOPY_PAGE_CONTEXT__";
4657
4953
  function getGlobalRoot() {
4658
4954
  if (typeof globalThis !== "undefined") return globalThis;
@@ -4663,7 +4959,7 @@ function getGlobalRoot() {
4663
4959
  function getPageContext2() {
4664
4960
  const root = getGlobalRoot();
4665
4961
  if (root && root[CONTEXT_KEY2]) return root[CONTEXT_KEY2];
4666
- const ctx = React36.createContext({
4962
+ const ctx = React37.createContext({
4667
4963
  navigation: null,
4668
4964
  page: null,
4669
4965
  site: null,
@@ -4687,7 +4983,7 @@ function normalizeManifestId(raw) {
4687
4983
  return String(raw || "").trim();
4688
4984
  }
4689
4985
  }
4690
- var PageContextFallback = React36.createContext(null);
4986
+ var PageContextFallback = React37.createContext(null);
4691
4987
  var referencedModule = null;
4692
4988
  var REFERENCED_SPEC = "@canopy-iiif/app/lib/components/referenced.js";
4693
4989
  function getReferencedModule() {
@@ -4713,9 +5009,9 @@ function getReferencedModule() {
4713
5009
  }
4714
5010
  function useReferencedManifestMap() {
4715
5011
  const PageContext2 = getPageContext2() || PageContextFallback;
4716
- const pageContext = React36.useContext(PageContext2);
5012
+ const pageContext = React37.useContext(PageContext2);
4717
5013
  const referencedItems = pageContext && pageContext.page && Array.isArray(pageContext.page.referencedItems) ? pageContext.page.referencedItems : [];
4718
- return React36.useMemo(() => {
5014
+ return React37.useMemo(() => {
4719
5015
  const map = /* @__PURE__ */ new Map();
4720
5016
  referencedItems.forEach((item) => {
4721
5017
  if (!item) return;
@@ -4826,7 +5122,7 @@ function normalizeResource(resource, index) {
4826
5122
  }
4827
5123
  function normalizePoint(child, index, options) {
4828
5124
  var _a, _b, _c, _d, _e, _f;
4829
- if (!React37.isValidElement(child)) return null;
5125
+ if (!React38.isValidElement(child)) return null;
4830
5126
  if (child.type !== TimelinePoint && ((_a = child.type) == null ? void 0 : _a.displayName) !== "TimelinePoint")
4831
5127
  return null;
4832
5128
  const props = child.props || {};
@@ -4842,7 +5138,7 @@ function normalizePoint(child, index, options) {
4842
5138
  try {
4843
5139
  if (props.children) {
4844
5140
  detailsHtml = ReactDOMServer.renderToStaticMarkup(
4845
- React37.createElement(React37.Fragment, null, props.children)
5141
+ React38.createElement(React38.Fragment, null, props.children)
4846
5142
  );
4847
5143
  }
4848
5144
  } catch (_) {
@@ -4891,7 +5187,7 @@ function MdxTimeline({ children, ...rest }) {
4891
5187
  const localeObj = createLocale(localeValue);
4892
5188
  const localeBase = typeof localeObj === "string" ? localeObj : localeObj.baseName || "en-US";
4893
5189
  const manifestMap = useReferencedManifestMap();
4894
- const childArray = React37.Children.toArray(children);
5190
+ const childArray = React38.Children.toArray(children);
4895
5191
  const points = childArray.map(
4896
5192
  (child, index) => normalizePoint(child, index, {
4897
5193
  range: rest.range || {},
@@ -4907,7 +5203,7 @@ function MdxTimeline({ children, ...rest }) {
4907
5203
  steps: rest.steps != null ? rest.steps : null
4908
5204
  };
4909
5205
  const json = serializeForScript(serializeProps(rest, payload, localeBase));
4910
- return /* @__PURE__ */ React37.createElement("div", { "data-canopy-timeline": "1" }, /* @__PURE__ */ React37.createElement(Timeline, { ...rest, __canopyTimeline: payload }), /* @__PURE__ */ React37.createElement(
5206
+ return /* @__PURE__ */ React38.createElement("div", { "data-canopy-timeline": "1" }, /* @__PURE__ */ React38.createElement(Timeline, { ...rest, __canopyTimeline: payload }), /* @__PURE__ */ React38.createElement(
4911
5207
  "script",
4912
5208
  {
4913
5209
  type: "application/json",
@@ -4917,7 +5213,7 @@ function MdxTimeline({ children, ...rest }) {
4917
5213
  }
4918
5214
 
4919
5215
  // ui/src/content/map/MdxMap.jsx
4920
- import React38 from "react";
5216
+ import React39 from "react";
4921
5217
  import ReactDOMServer2 from "react-dom/server";
4922
5218
 
4923
5219
  // ui/src/content/map/MapPoint.jsx
@@ -4952,7 +5248,7 @@ function renderDetailsHtml(children) {
4952
5248
  if (!children) return "";
4953
5249
  try {
4954
5250
  return ReactDOMServer2.renderToStaticMarkup(
4955
- React38.createElement(React38.Fragment, null, children)
5251
+ React39.createElement(React39.Fragment, null, children)
4956
5252
  );
4957
5253
  } catch (_) {
4958
5254
  return "";
@@ -4985,7 +5281,7 @@ function pickManifestSummary(manifest) {
4985
5281
  }
4986
5282
  function normalizeCustomPoint(child, index, manifestMap) {
4987
5283
  var _a;
4988
- if (!React38.isValidElement(child)) return null;
5284
+ if (!React39.isValidElement(child)) return null;
4989
5285
  if (child.type !== MapPoint && ((_a = child.type) == null ? void 0 : _a.displayName) !== "MapPoint") return null;
4990
5286
  const coords = normalizeCoordinates(child.props || {});
4991
5287
  if (!coords) return null;
@@ -5048,7 +5344,7 @@ function normalizeCustomPoint(child, index, manifestMap) {
5048
5344
  };
5049
5345
  }
5050
5346
  function normalizeCustomPoints(children, manifestMap) {
5051
- return React38.Children.toArray(children).map((child, index) => normalizeCustomPoint(child, index, manifestMap)).filter(Boolean);
5347
+ return React39.Children.toArray(children).map((child, index) => normalizeCustomPoint(child, index, manifestMap)).filter(Boolean);
5052
5348
  }
5053
5349
  function normalizeHeight(value) {
5054
5350
  if (value == null) return "600px";
@@ -5225,11 +5521,11 @@ function MdxMap({ children, ...rest }) {
5225
5521
  if (placeholderClass) placeholderProps.className = placeholderClass;
5226
5522
  if (rest.id) placeholderProps.id = rest.id;
5227
5523
  if (rest.style) placeholderProps.style = rest.style;
5228
- return /* @__PURE__ */ React38.createElement("div", { "data-canopy-map": "1" }, /* @__PURE__ */ React38.createElement("div", { ...placeholderProps, "aria-live": "polite" }, /* @__PURE__ */ React38.createElement("div", { className: "canopy-map__status" }, "Loading map\u2026")), /* @__PURE__ */ React38.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
5524
+ return /* @__PURE__ */ React39.createElement("div", { "data-canopy-map": "1" }, /* @__PURE__ */ React39.createElement("div", { ...placeholderProps, "aria-live": "polite" }, /* @__PURE__ */ React39.createElement("div", { className: "canopy-map__status" }, "Loading map\u2026")), /* @__PURE__ */ React39.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
5229
5525
  }
5230
5526
 
5231
5527
  // ui/src/content/gallery/Gallery.jsx
5232
- import React39 from "react";
5528
+ import React40 from "react";
5233
5529
  var INLINE_SCRIPT2 = `(() => {
5234
5530
  if (typeof window === 'undefined') return;
5235
5531
  if (window.__canopyGalleryBound) return;
@@ -5740,7 +6036,7 @@ function shuffleItems(list) {
5740
6036
  function renderMetaList(meta, className) {
5741
6037
  const entries = ensureArray2(meta).filter((entry) => entry || entry === 0);
5742
6038
  if (!entries.length) return null;
5743
- return /* @__PURE__ */ React39.createElement("ul", { className, role: "list" }, entries.map((entry, index) => /* @__PURE__ */ React39.createElement("li", { key: `meta-${index}` }, entry)));
6039
+ return /* @__PURE__ */ React40.createElement("ul", { className, role: "list" }, entries.map((entry, index) => /* @__PURE__ */ React40.createElement("li", { key: `meta-${index}` }, entry)));
5744
6040
  }
5745
6041
  function renderPreview(props = {}) {
5746
6042
  const source = typeof props.media === "string" && props.media || props.thumbnail || props.src || props.image && props.image.src || props.image;
@@ -5748,7 +6044,7 @@ function renderPreview(props = {}) {
5748
6044
  const alt = props.thumbnailAlt || props.imageAlt || props.alt || props.title || "";
5749
6045
  const width = props.thumbnailWidth || props.imageWidth || props.width;
5750
6046
  const height = props.thumbnailHeight || props.imageHeight || props.height;
5751
- return /* @__PURE__ */ React39.createElement(
6047
+ return /* @__PURE__ */ React40.createElement(
5752
6048
  "img",
5753
6049
  {
5754
6050
  src: source,
@@ -5759,7 +6055,7 @@ function renderPreview(props = {}) {
5759
6055
  }
5760
6056
  );
5761
6057
  }
5762
- return /* @__PURE__ */ React39.createElement("div", { className: "canopy-gallery__placeholder", "aria-hidden": "true" });
6058
+ return /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__placeholder", "aria-hidden": "true" });
5763
6059
  }
5764
6060
  function renderFlexibleContent(content, options = {}) {
5765
6061
  var _a, _b;
@@ -5772,25 +6068,25 @@ function renderFlexibleContent(content, options = {}) {
5772
6068
  if (content == null || content === false) return null;
5773
6069
  const isPrimitive = typeof content === "string" || typeof content === "number" || typeof content === "bigint";
5774
6070
  if (isPrimitive) {
5775
- return /* @__PURE__ */ React39.createElement(InlineTag, { className, id }, content);
6071
+ return /* @__PURE__ */ React40.createElement(InlineTag, { className, id }, content);
5776
6072
  }
5777
- if (React39.isValidElement(content)) {
5778
- if (content.type === React39.Fragment) {
5779
- return /* @__PURE__ */ React39.createElement(BlockTag, { className, id }, content);
6073
+ if (React40.isValidElement(content)) {
6074
+ if (content.type === React40.Fragment) {
6075
+ return /* @__PURE__ */ React40.createElement(BlockTag, { className, id }, content);
5780
6076
  }
5781
6077
  const mergedClassName = [(_a = content.props) == null ? void 0 : _a.className, className].filter(Boolean).join(" ");
5782
- return React39.cloneElement(content, {
6078
+ return React40.cloneElement(content, {
5783
6079
  ...content.props,
5784
6080
  className: mergedClassName || void 0,
5785
6081
  id: ((_b = content.props) == null ? void 0 : _b.id) || id
5786
6082
  });
5787
6083
  }
5788
6084
  const FallbackTag = BlockTag;
5789
- return /* @__PURE__ */ React39.createElement(FallbackTag, { className, id }, content);
6085
+ return /* @__PURE__ */ React40.createElement(FallbackTag, { className, id }, content);
5790
6086
  }
5791
6087
  function normalizeItem(child, index, galleryId, manifestMap) {
5792
6088
  var _a;
5793
- if (!React39.isValidElement(child)) return null;
6089
+ if (!React40.isValidElement(child)) return null;
5794
6090
  if (child.type !== GalleryItem && ((_a = child.type) == null ? void 0 : _a.displayName) !== "GalleryItem")
5795
6091
  return null;
5796
6092
  const props = child.props || {};
@@ -5865,7 +6161,7 @@ function buildCaptionContent(itemProps) {
5865
6161
  if (itemProps.caption) return itemProps.caption;
5866
6162
  const kicker = itemProps.kicker || itemProps.label || itemProps.eyebrow;
5867
6163
  const summary = itemProps.summary || itemProps.description;
5868
- return /* @__PURE__ */ React39.createElement(React39.Fragment, null, kicker ? /* @__PURE__ */ React39.createElement("span", { className: "canopy-gallery__kicker" }, kicker) : null, itemProps.title ? /* @__PURE__ */ React39.createElement("span", { className: "canopy-gallery__title-text" }, itemProps.title) : null, renderFlexibleContent(summary, {
6164
+ return /* @__PURE__ */ React40.createElement(React40.Fragment, null, kicker ? /* @__PURE__ */ React40.createElement("span", { className: "canopy-gallery__kicker" }, kicker) : null, itemProps.title ? /* @__PURE__ */ React40.createElement("span", { className: "canopy-gallery__title-text" }, itemProps.title) : null, renderFlexibleContent(summary, {
5869
6165
  inlineTag: "span",
5870
6166
  blockTag: "div",
5871
6167
  className: "canopy-gallery__summary"
@@ -5887,7 +6183,7 @@ function GalleryModal({ item, closeTargetId, navItems, navGroupName }) {
5887
6183
  const kicker = props.kicker || props.label || props.eyebrow;
5888
6184
  const summary = props.popupDescription || props.modalDescription || props.description || props.summary || null;
5889
6185
  const modalTitle = props.popupTitle || props.modalTitle || props.title || `Item ${index + 1}`;
5890
- return /* @__PURE__ */ React39.createElement(
6186
+ return /* @__PURE__ */ React40.createElement(
5891
6187
  "div",
5892
6188
  {
5893
6189
  id: modalId,
@@ -5900,14 +6196,14 @@ function GalleryModal({ item, closeTargetId, navItems, navGroupName }) {
5900
6196
  "data-canopy-gallery-modal": "true",
5901
6197
  "data-canopy-gallery-close": closeTargetId
5902
6198
  },
5903
- /* @__PURE__ */ React39.createElement("div", { className: "canopy-gallery__modal-scrim" }, /* @__PURE__ */ React39.createElement("div", { className: "canopy-gallery__modal-actions" }, /* @__PURE__ */ React39.createElement(
6199
+ /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__modal-scrim" }, /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__modal-actions" }, /* @__PURE__ */ React40.createElement(
5904
6200
  GalleryThumbnailNav,
5905
6201
  {
5906
6202
  items: navItems,
5907
6203
  activeModalId: modalId,
5908
6204
  groupName: `${navGroupName || "canopy-gallery"}-${modalId}`
5909
6205
  }
5910
- ), /* @__PURE__ */ React39.createElement(
6206
+ ), /* @__PURE__ */ React40.createElement(
5911
6207
  "a",
5912
6208
  {
5913
6209
  className: "canopy-gallery__modal-close",
@@ -5915,7 +6211,7 @@ function GalleryModal({ item, closeTargetId, navItems, navGroupName }) {
5915
6211
  "aria-label": `Close popup for ${modalTitle}`
5916
6212
  },
5917
6213
  "X"
5918
- )), /* @__PURE__ */ React39.createElement("div", { className: "canopy-gallery__modal-panel" }, /* @__PURE__ */ React39.createElement(
6214
+ )), /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__modal-panel" }, /* @__PURE__ */ React40.createElement(
5919
6215
  "button",
5920
6216
  {
5921
6217
  type: "button",
@@ -5923,7 +6219,7 @@ function GalleryModal({ item, closeTargetId, navItems, navGroupName }) {
5923
6219
  "aria-label": "Scroll left through gallery thumbnails",
5924
6220
  "data-canopy-gallery-nav-prev": "true"
5925
6221
  },
5926
- /* @__PURE__ */ React39.createElement(
6222
+ /* @__PURE__ */ React40.createElement(
5927
6223
  "span",
5928
6224
  {
5929
6225
  className: "canopy-gallery__nav-button-icon",
@@ -5932,8 +6228,8 @@ function GalleryModal({ item, closeTargetId, navItems, navGroupName }) {
5932
6228
  },
5933
6229
  "<"
5934
6230
  ),
5935
- /* @__PURE__ */ React39.createElement("span", { className: "canopy-gallery__visually-hidden" }, "Previous item")
5936
- ), /* @__PURE__ */ React39.createElement(
6231
+ /* @__PURE__ */ React40.createElement("span", { className: "canopy-gallery__visually-hidden" }, "Previous item")
6232
+ ), /* @__PURE__ */ React40.createElement(
5937
6233
  "button",
5938
6234
  {
5939
6235
  type: "button",
@@ -5941,7 +6237,7 @@ function GalleryModal({ item, closeTargetId, navItems, navGroupName }) {
5941
6237
  "aria-label": "Scroll right through gallery thumbnails",
5942
6238
  "data-canopy-gallery-nav-next": "true"
5943
6239
  },
5944
- /* @__PURE__ */ React39.createElement(
6240
+ /* @__PURE__ */ React40.createElement(
5945
6241
  "span",
5946
6242
  {
5947
6243
  className: "canopy-gallery__nav-button-icon",
@@ -5950,8 +6246,8 @@ function GalleryModal({ item, closeTargetId, navItems, navGroupName }) {
5950
6246
  },
5951
6247
  ">"
5952
6248
  ),
5953
- /* @__PURE__ */ React39.createElement("span", { className: "canopy-gallery__visually-hidden" }, "Next item")
5954
- ), /* @__PURE__ */ React39.createElement("header", { className: "canopy-gallery__modal-header" }, /* @__PURE__ */ React39.createElement("div", { className: "canopy-gallery__modal-text" }, kicker ? /* @__PURE__ */ React39.createElement("p", { className: "canopy-gallery__modal-kicker" }, kicker) : null, /* @__PURE__ */ React39.createElement("h3", { id: modalTitleId, className: "canopy-gallery__modal-title" }, modalTitle), renderFlexibleContent(summary, {
6249
+ /* @__PURE__ */ React40.createElement("span", { className: "canopy-gallery__visually-hidden" }, "Next item")
6250
+ ), /* @__PURE__ */ React40.createElement("header", { className: "canopy-gallery__modal-header" }, /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__modal-text" }, kicker ? /* @__PURE__ */ React40.createElement("p", { className: "canopy-gallery__modal-kicker" }, kicker) : null, /* @__PURE__ */ React40.createElement("h3", { id: modalTitleId, className: "canopy-gallery__modal-title" }, modalTitle), renderFlexibleContent(summary, {
5955
6251
  inlineTag: "p",
5956
6252
  blockTag: "div",
5957
6253
  className: "canopy-gallery__modal-summary",
@@ -5959,20 +6255,20 @@ function GalleryModal({ item, closeTargetId, navItems, navGroupName }) {
5959
6255
  }), renderMetaList(
5960
6256
  props.meta,
5961
6257
  "canopy-gallery__meta canopy-gallery__meta--modal"
5962
- ), manifests && manifests.length ? manifests.map((manifest) => /* @__PURE__ */ React39.createElement("a", { key: manifest.id || manifest.href, href: manifest.href }, manifest.title || manifest.href)) : null)), /* @__PURE__ */ React39.createElement("div", { className: "canopy-gallery__modal-body" }, props.children)))
6258
+ ), manifests && manifests.length ? manifests.map((manifest) => /* @__PURE__ */ React40.createElement("a", { key: manifest.id || manifest.href, href: manifest.href }, manifest.title || manifest.href)) : null)), /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__modal-body" }, props.children)))
5963
6259
  );
5964
6260
  }
5965
6261
  function GalleryFigure({ item }) {
5966
6262
  const { props, modalId, triggerLabel } = item;
5967
- return /* @__PURE__ */ React39.createElement(
6263
+ return /* @__PURE__ */ React40.createElement(
5968
6264
  "figure",
5969
6265
  {
5970
6266
  className: "canopy-gallery__item",
5971
6267
  "data-gallery-item-index": item.index
5972
6268
  },
5973
- /* @__PURE__ */ React39.createElement("div", { className: "canopy-gallery__media" }, renderPreview(props)),
5974
- /* @__PURE__ */ React39.createElement("figcaption", { className: "canopy-gallery__caption" }, buildCaptionContent(props)),
5975
- /* @__PURE__ */ React39.createElement(
6269
+ /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__media" }, renderPreview(props)),
6270
+ /* @__PURE__ */ React40.createElement("figcaption", { className: "canopy-gallery__caption" }, buildCaptionContent(props)),
6271
+ /* @__PURE__ */ React40.createElement(
5976
6272
  "a",
5977
6273
  {
5978
6274
  className: "canopy-gallery__trigger",
@@ -5982,27 +6278,27 @@ function GalleryFigure({ item }) {
5982
6278
  "aria-label": triggerLabel,
5983
6279
  "data-canopy-gallery-trigger": modalId
5984
6280
  },
5985
- /* @__PURE__ */ React39.createElement("span", { className: "canopy-gallery__trigger-label" }, triggerLabel)
6281
+ /* @__PURE__ */ React40.createElement("span", { className: "canopy-gallery__trigger-label" }, triggerLabel)
5986
6282
  )
5987
6283
  );
5988
6284
  }
5989
6285
  function GalleryThumbnailNav({ items, activeModalId, groupName }) {
5990
6286
  if (!items || items.length < 2) return null;
5991
6287
  const radioGroup = groupName || "canopy-gallery-nav";
5992
- return /* @__PURE__ */ React39.createElement(
6288
+ return /* @__PURE__ */ React40.createElement(
5993
6289
  "nav",
5994
6290
  {
5995
6291
  className: "canopy-gallery__nav",
5996
6292
  "aria-label": "Gallery navigation",
5997
6293
  "data-canopy-gallery-nav": "true"
5998
6294
  },
5999
- /* @__PURE__ */ React39.createElement(
6295
+ /* @__PURE__ */ React40.createElement(
6000
6296
  "div",
6001
6297
  {
6002
6298
  className: "canopy-gallery__nav-viewport",
6003
6299
  "data-canopy-gallery-nav-viewport": "true"
6004
6300
  },
6005
- /* @__PURE__ */ React39.createElement(
6301
+ /* @__PURE__ */ React40.createElement(
6006
6302
  "ul",
6007
6303
  {
6008
6304
  className: "canopy-gallery__nav-list",
@@ -6012,7 +6308,7 @@ function GalleryThumbnailNav({ items, activeModalId, groupName }) {
6012
6308
  items.map((item, index) => {
6013
6309
  const optionId = `${radioGroup}-${item.modalId || index}`;
6014
6310
  const isActive = item.modalId === activeModalId;
6015
- return /* @__PURE__ */ React39.createElement(
6311
+ return /* @__PURE__ */ React40.createElement(
6016
6312
  "li",
6017
6313
  {
6018
6314
  key: `${item.key}-nav`,
@@ -6020,7 +6316,7 @@ function GalleryThumbnailNav({ items, activeModalId, groupName }) {
6020
6316
  "data-canopy-gallery-nav-item": "true",
6021
6317
  "data-canopy-gallery-nav-selected": isActive ? "1" : void 0
6022
6318
  },
6023
- /* @__PURE__ */ React39.createElement(
6319
+ /* @__PURE__ */ React40.createElement(
6024
6320
  "input",
6025
6321
  {
6026
6322
  type: "radio",
@@ -6036,15 +6332,15 @@ function GalleryThumbnailNav({ items, activeModalId, groupName }) {
6036
6332
  "data-canopy-gallery-nav-selected": isActive ? "1" : void 0
6037
6333
  }
6038
6334
  ),
6039
- /* @__PURE__ */ React39.createElement(
6335
+ /* @__PURE__ */ React40.createElement(
6040
6336
  "label",
6041
6337
  {
6042
6338
  className: "canopy-gallery__nav-link",
6043
6339
  htmlFor: optionId,
6044
6340
  "data-canopy-gallery-nav-active": isActive ? "1" : void 0
6045
6341
  },
6046
- /* @__PURE__ */ React39.createElement("span", { className: "canopy-gallery__nav-thumb" }, renderPreview(item.props)),
6047
- /* @__PURE__ */ React39.createElement("span", { className: "canopy-gallery__nav-label" }, item.props.title || `Item ${item.index + 1}`)
6342
+ /* @__PURE__ */ React40.createElement("span", { className: "canopy-gallery__nav-thumb" }, renderPreview(item.props)),
6343
+ /* @__PURE__ */ React40.createElement("span", { className: "canopy-gallery__nav-label" }, item.props.title || `Item ${item.index + 1}`)
6048
6344
  )
6049
6345
  );
6050
6346
  })
@@ -6057,7 +6353,7 @@ function GalleryContent({ children, flex = false }) {
6057
6353
  "canopy-gallery-item__content",
6058
6354
  flex ? "canopy-gallery-item__content_flex" : null
6059
6355
  ].filter(Boolean).join(" ");
6060
- return /* @__PURE__ */ React39.createElement("div", { className: contentClassName }, children);
6356
+ return /* @__PURE__ */ React40.createElement("div", { className: contentClassName }, children);
6061
6357
  }
6062
6358
  function GalleryItem() {
6063
6359
  return null;
@@ -6081,7 +6377,7 @@ function Gallery({
6081
6377
  const galleryId = id ? String(id) : nextGalleryInstanceId();
6082
6378
  const HeadingTag = "h3";
6083
6379
  const closeTargetId = `${galleryId}-close`;
6084
- const childArray = React39.Children.toArray(children);
6380
+ const childArray = React40.Children.toArray(children);
6085
6381
  const items = childArray.map((child, index) => normalizeItem(child, index, galleryId, manifestMap)).filter(Boolean);
6086
6382
  if (!items.length) return null;
6087
6383
  const popupMode = normalizePopupSize(popupSize);
@@ -6093,7 +6389,7 @@ function Gallery({
6093
6389
  className
6094
6390
  ].filter(Boolean).join(" ");
6095
6391
  const navGroupName = `${galleryId}-nav`;
6096
- return /* @__PURE__ */ React39.createElement("section", { className: rootClassName, style, "data-canopy-gallery": "true" }, /* @__PURE__ */ React39.createElement(
6392
+ return /* @__PURE__ */ React40.createElement("section", { className: rootClassName, style, "data-canopy-gallery": "true" }, /* @__PURE__ */ React40.createElement(
6097
6393
  "div",
6098
6394
  {
6099
6395
  id: closeTargetId,
@@ -6101,7 +6397,7 @@ function Gallery({
6101
6397
  "aria-hidden": "true",
6102
6398
  tabIndex: -1
6103
6399
  }
6104
- ), (title || description) && /* @__PURE__ */ React39.createElement("div", { className: "canopy-gallery__header" }, title ? /* @__PURE__ */ React39.createElement(HeadingTag, { className: "canopy-gallery__heading" }, title) : null, description ? /* @__PURE__ */ React39.createElement("p", { className: "canopy-gallery__description" }, description) : null), /* @__PURE__ */ React39.createElement("div", { className: "canopy-gallery__grid" }, orderedItems.map((item) => /* @__PURE__ */ React39.createElement(GalleryFigure, { key: item.key, item }))), /* @__PURE__ */ React39.createElement("div", { className: "canopy-gallery__modals" }, orderedItems.map((item) => /* @__PURE__ */ React39.createElement(
6400
+ ), (title || description) && /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__header" }, title ? /* @__PURE__ */ React40.createElement(HeadingTag, { className: "canopy-gallery__heading" }, title) : null, description ? /* @__PURE__ */ React40.createElement("p", { className: "canopy-gallery__description" }, description) : null), /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__grid" }, orderedItems.map((item) => /* @__PURE__ */ React40.createElement(GalleryFigure, { key: item.key, item }))), /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__modals" }, orderedItems.map((item) => /* @__PURE__ */ React40.createElement(
6105
6401
  GalleryModal,
6106
6402
  {
6107
6403
  key: `${item.modalId}-modal`,
@@ -6110,7 +6406,7 @@ function Gallery({
6110
6406
  navItems: orderedItems,
6111
6407
  navGroupName
6112
6408
  }
6113
- ))), /* @__PURE__ */ React39.createElement(
6409
+ ))), /* @__PURE__ */ React40.createElement(
6114
6410
  "script",
6115
6411
  {
6116
6412
  "data-canopy-gallery-script": "true",
@@ -6122,7 +6418,7 @@ Gallery.Item = GalleryItem;
6122
6418
  Gallery.Content = GalleryContent;
6123
6419
 
6124
6420
  // ui/src/search/MdxSearchResults.jsx
6125
- import React40 from "react";
6421
+ import React41 from "react";
6126
6422
  function MdxSearchResults(props) {
6127
6423
  let json = "{}";
6128
6424
  try {
@@ -6130,11 +6426,11 @@ function MdxSearchResults(props) {
6130
6426
  } catch (_) {
6131
6427
  json = "{}";
6132
6428
  }
6133
- return /* @__PURE__ */ React40.createElement("div", { "data-canopy-search-results": "1" }, /* @__PURE__ */ React40.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
6429
+ return /* @__PURE__ */ React41.createElement("div", { "data-canopy-search-results": "1" }, /* @__PURE__ */ React41.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
6134
6430
  }
6135
6431
 
6136
6432
  // ui/src/search/SearchSummary.jsx
6137
- import React41 from "react";
6433
+ import React42 from "react";
6138
6434
  function SearchSummary(props) {
6139
6435
  let json = "{}";
6140
6436
  try {
@@ -6142,11 +6438,11 @@ function SearchSummary(props) {
6142
6438
  } catch (_) {
6143
6439
  json = "{}";
6144
6440
  }
6145
- return /* @__PURE__ */ React41.createElement("div", { "data-canopy-search-summary": "1" }, /* @__PURE__ */ React41.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
6441
+ return /* @__PURE__ */ React42.createElement("div", { "data-canopy-search-summary": "1" }, /* @__PURE__ */ React42.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
6146
6442
  }
6147
6443
 
6148
6444
  // ui/src/search/MdxSearchTabs.jsx
6149
- import React42 from "react";
6445
+ import React43 from "react";
6150
6446
  function MdxSearchTabs(props) {
6151
6447
  let json = "{}";
6152
6448
  try {
@@ -6154,11 +6450,11 @@ function MdxSearchTabs(props) {
6154
6450
  } catch (_) {
6155
6451
  json = "{}";
6156
6452
  }
6157
- return /* @__PURE__ */ React42.createElement("div", { "data-canopy-search-tabs": "1" }, /* @__PURE__ */ React42.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
6453
+ return /* @__PURE__ */ React43.createElement("div", { "data-canopy-search-tabs": "1" }, /* @__PURE__ */ React43.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
6158
6454
  }
6159
6455
 
6160
6456
  // ui/src/search/MdxSearch.jsx
6161
- import React43 from "react";
6457
+ import React44 from "react";
6162
6458
  function MdxSearch(props = {}) {
6163
6459
  const {
6164
6460
  layout,
@@ -6176,32 +6472,35 @@ function MdxSearch(props = {}) {
6176
6472
  resultsPayload.layout = layout;
6177
6473
  }
6178
6474
  const classes = ["canopy-search", className].filter(Boolean).join(" ");
6179
- return /* @__PURE__ */ React43.createElement("section", { className: classes, "data-canopy-search": "1" }, showTabs ? /* @__PURE__ */ React43.createElement(MdxSearchTabs, { ...tabsProps }) : null, showSummary ? /* @__PURE__ */ React43.createElement(SearchSummary, { ...summaryProps }) : null, showResults ? /* @__PURE__ */ React43.createElement(MdxSearchResults, { ...resultsPayload }) : null, children || null);
6475
+ return /* @__PURE__ */ React44.createElement("section", { className: classes, "data-canopy-search": "1" }, showTabs ? /* @__PURE__ */ React44.createElement(MdxSearchTabs, { ...tabsProps }) : null, showSummary ? /* @__PURE__ */ React44.createElement(SearchSummary, { ...summaryProps }) : null, showResults ? /* @__PURE__ */ React44.createElement(MdxSearchResults, { ...resultsPayload }) : null, children || null);
6180
6476
  }
6181
6477
 
6182
6478
  // ui/src/search-form/MdxSearchFormModal.jsx
6183
- import React44 from "react";
6479
+ import React45 from "react";
6184
6480
  function MdxSearchFormModal(props = {}) {
6185
6481
  const {
6186
- placeholder = "Search\u2026",
6482
+ placeholder: placeholderProp,
6187
6483
  hotkey = "mod+k",
6188
6484
  maxResults = 8,
6189
6485
  groupOrder = ["work", "page"],
6190
6486
  button = true,
6191
6487
  // kept for backward compat; ignored by teaser form
6192
- buttonLabel = "Search",
6488
+ buttonLabel: buttonLabelProp,
6193
6489
  label,
6194
6490
  searchPath = "/search"
6195
6491
  } = props || {};
6196
- const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
6492
+ const { getString } = useLocale2();
6493
+ const placeholder = placeholderProp != null ? placeholderProp : getString("common.phrases.placeholder_search", "Search\u2026");
6494
+ const resolvedButtonLabel = buttonLabelProp != null ? buttonLabelProp : getString("common.nouns.search", "Search");
6495
+ const text = typeof label === "string" && label.trim() ? label.trim() : resolvedButtonLabel;
6197
6496
  const resolvedSearchPath = resolveSearchPath(searchPath);
6198
6497
  const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
6199
- return /* @__PURE__ */ React44.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React44.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React44.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React44.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React44.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
6498
+ return /* @__PURE__ */ React45.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React45.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React45.createElement(SearchPanelForm, { placeholder, buttonLabel: resolvedButtonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React45.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React45.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
6200
6499
  }
6201
6500
 
6202
6501
  // ui/src/iiif/ManifestPrimitives.jsx
6203
6502
  var import_slugify2 = __toESM(require_slugify());
6204
- import React45 from "react";
6503
+ import React46 from "react";
6205
6504
  import {
6206
6505
  Label as CloverLabel,
6207
6506
  Metadata as CloverMetadata,
@@ -6342,7 +6641,7 @@ function MetadataFacetLink(props) {
6342
6641
  const valueSlug = facetSlug ? toValueSlug(text) : "";
6343
6642
  const href = facetSlug && valueSlug ? buildFacetSearchHref(facetSlug, valueSlug) : "";
6344
6643
  if (!href) return text;
6345
- return /* @__PURE__ */ React45.createElement(
6644
+ return /* @__PURE__ */ React46.createElement(
6346
6645
  "a",
6347
6646
  {
6348
6647
  href,
@@ -6368,7 +6667,7 @@ function buildFacetCustomValueContent(items, manifest) {
6368
6667
  seen.add(normalized);
6369
6668
  custom.push({
6370
6669
  matchingLabel: item.label,
6371
- Content: /* @__PURE__ */ React45.createElement(MetadataFacetLink, { facetSlug: facet.slug })
6670
+ Content: /* @__PURE__ */ React46.createElement(MetadataFacetLink, { facetSlug: facet.slug })
6372
6671
  });
6373
6672
  }
6374
6673
  return custom;
@@ -6400,12 +6699,12 @@ function mergeCustomValueContent(userContent, autoContent) {
6400
6699
  function Label({ manifest, label, ...rest }) {
6401
6700
  const intl = label || manifest && manifest.label;
6402
6701
  if (!hasInternationalValue(intl)) return null;
6403
- return /* @__PURE__ */ React45.createElement(CloverLabel, { label: intl, ...rest });
6702
+ return /* @__PURE__ */ React46.createElement(CloverLabel, { label: intl, ...rest });
6404
6703
  }
6405
6704
  function Summary({ manifest, summary, ...rest }) {
6406
6705
  const intl = summary || manifest && manifest.summary;
6407
6706
  if (!hasInternationalValue(intl)) return null;
6408
- return /* @__PURE__ */ React45.createElement(CloverSummary, { summary: intl, ...rest });
6707
+ return /* @__PURE__ */ React46.createElement(CloverSummary, { summary: intl, ...rest });
6409
6708
  }
6410
6709
  function Metadata({ manifest, metadata, customValueContent, ...rest }) {
6411
6710
  const items = ensureMetadata(metadata || manifest && manifest.metadata);
@@ -6415,7 +6714,7 @@ function Metadata({ manifest, metadata, customValueContent, ...rest }) {
6415
6714
  customValueContent,
6416
6715
  autoCustomContent
6417
6716
  );
6418
- return /* @__PURE__ */ React45.createElement(
6717
+ return /* @__PURE__ */ React46.createElement(
6419
6718
  CloverMetadata,
6420
6719
  {
6421
6720
  metadata: items,
@@ -6429,17 +6728,17 @@ function RequiredStatement({ manifest, requiredStatement, ...rest }) {
6429
6728
  if (!stmt || !hasInternationalValue(stmt.label) || !hasInternationalValue(stmt.value)) {
6430
6729
  return null;
6431
6730
  }
6432
- return /* @__PURE__ */ React45.createElement(CloverRequiredStatement, { requiredStatement: stmt, ...rest });
6731
+ return /* @__PURE__ */ React46.createElement(CloverRequiredStatement, { requiredStatement: stmt, ...rest });
6433
6732
  }
6434
6733
 
6435
6734
  // ui/src/iiif/Properties/Id.jsx
6436
- import React46 from "react";
6735
+ import React47 from "react";
6437
6736
  function Id({ title = "IIIF Manifest", id, ...props }) {
6438
- return /* @__PURE__ */ React46.createElement("dl", null, /* @__PURE__ */ React46.createElement("dt", null, title), /* @__PURE__ */ React46.createElement("dd", null, /* @__PURE__ */ React46.createElement("a", { href: id }, id)));
6737
+ return /* @__PURE__ */ React47.createElement("dl", null, /* @__PURE__ */ React47.createElement("dt", null, title), /* @__PURE__ */ React47.createElement("dd", null, /* @__PURE__ */ React47.createElement("a", { href: id }, id)));
6439
6738
  }
6440
6739
 
6441
6740
  // ui/src/docs/CodeBlock.jsx
6442
- import React47 from "react";
6741
+ import React48 from "react";
6443
6742
  function parseHighlightAttr(attr) {
6444
6743
  if (!attr) return /* @__PURE__ */ new Set();
6445
6744
  const cleaned = String(attr || "").trim();
@@ -6485,10 +6784,10 @@ var highlightBaseStyle = {
6485
6784
  };
6486
6785
  function DocsCodeBlock(props = {}) {
6487
6786
  const { children, ...rest } = props;
6488
- const childArray = React47.Children.toArray(children);
6489
- const codeElement = childArray.find((el) => React47.isValidElement(el));
6787
+ const childArray = React48.Children.toArray(children);
6788
+ const codeElement = childArray.find((el) => React48.isValidElement(el));
6490
6789
  if (!codeElement || !codeElement.props) {
6491
- return React47.createElement("pre", props);
6790
+ return React48.createElement("pre", props);
6492
6791
  }
6493
6792
  const {
6494
6793
  className = "",
@@ -6503,9 +6802,9 @@ function DocsCodeBlock(props = {}) {
6503
6802
  const highlightSet = parseHighlightAttr(highlightAttr);
6504
6803
  const copyAttr = codeProps["data-copy"];
6505
6804
  const enableCopy = copyAttr !== void 0 ? copyAttr === true || copyAttr === "true" || copyAttr === "" : false;
6506
- const [copied, setCopied] = React47.useState(false);
6507
- const buttonRef = React47.useRef(null);
6508
- const handleCopy = React47.useCallback(async () => {
6805
+ const [copied, setCopied] = React48.useState(false);
6806
+ const buttonRef = React48.useRef(null);
6807
+ const handleCopy = React48.useCallback(async () => {
6509
6808
  const text = trimmedCode;
6510
6809
  try {
6511
6810
  if (typeof navigator !== "undefined" && navigator.clipboard && navigator.clipboard.writeText) {
@@ -6527,12 +6826,12 @@ function DocsCodeBlock(props = {}) {
6527
6826
  setCopied(false);
6528
6827
  }
6529
6828
  }, [trimmedCode]);
6530
- React47.useEffect(() => {
6829
+ React48.useEffect(() => {
6531
6830
  if (buttonRef.current) {
6532
6831
  buttonRef.current.setAttribute("data-docs-copy-hydrated", "true");
6533
6832
  }
6534
6833
  }, []);
6535
- React47.useEffect(() => {
6834
+ React48.useEffect(() => {
6536
6835
  if (!buttonRef.current) return;
6537
6836
  if (copied) buttonRef.current.setAttribute("data-docs-copy-active", "true");
6538
6837
  else buttonRef.current.removeAttribute("data-docs-copy-active");
@@ -6595,27 +6894,27 @@ function DocsCodeBlock(props = {}) {
6595
6894
  const highlight = highlightSet.has(lineNumber);
6596
6895
  const style = highlight ? { ...baseLineStyle, ...highlightBaseStyle } : baseLineStyle;
6597
6896
  const displayLine = line === "" ? " " : line;
6598
- return React47.createElement(
6897
+ return React48.createElement(
6599
6898
  "span",
6600
6899
  {
6601
6900
  key: lineNumber,
6602
6901
  style,
6603
6902
  "data-docs-code-line": line
6604
6903
  },
6605
- React47.createElement("span", { style: lineContentStyle }, displayLine)
6904
+ React48.createElement("span", { style: lineContentStyle }, displayLine)
6606
6905
  );
6607
6906
  });
6608
- return React47.createElement(
6907
+ return React48.createElement(
6609
6908
  "div",
6610
6909
  {
6611
6910
  style: containerStyle,
6612
6911
  "data-docs-code-block": "true"
6613
6912
  },
6614
- showHeader ? React47.createElement(
6913
+ showHeader ? React48.createElement(
6615
6914
  "div",
6616
6915
  { style: headerStyle },
6617
- React47.createElement("span", null, showFilename ? filename : null),
6618
- enableCopy ? React47.createElement(
6916
+ React48.createElement("span", null, showFilename ? filename : null),
6917
+ enableCopy ? React48.createElement(
6619
6918
  "button",
6620
6919
  {
6621
6920
  ref: buttonRef,
@@ -6626,8 +6925,8 @@ function DocsCodeBlock(props = {}) {
6626
6925
  "data-docs-copy-button": "true",
6627
6926
  style: buttonStyle
6628
6927
  },
6629
- React47.createElement("span", null, "Copy"),
6630
- React47.createElement(
6928
+ React48.createElement("span", null, "Copy"),
6929
+ React48.createElement(
6631
6930
  "span",
6632
6931
  {
6633
6932
  "aria-hidden": "true",
@@ -6637,7 +6936,7 @@ function DocsCodeBlock(props = {}) {
6637
6936
  )
6638
6937
  ) : null
6639
6938
  ) : null,
6640
- enableCopy ? React47.createElement("textarea", {
6939
+ enableCopy ? React48.createElement("textarea", {
6641
6940
  "data-docs-copy-source": "true",
6642
6941
  tabIndex: -1,
6643
6942
  readOnly: true,
@@ -6652,29 +6951,29 @@ function DocsCodeBlock(props = {}) {
6652
6951
  pointerEvents: "none"
6653
6952
  }
6654
6953
  }) : null,
6655
- React47.createElement(
6954
+ React48.createElement(
6656
6955
  "pre",
6657
6956
  { ...preRest, className: preClassName, style: mergedPreStyle },
6658
- React47.createElement("code", { style: codeStyle }, lineElements)
6957
+ React48.createElement("code", { style: codeStyle }, lineElements)
6659
6958
  )
6660
6959
  );
6661
6960
  }
6662
6961
 
6663
6962
  // ui/src/docs/MarkdownTable.jsx
6664
- import React48 from "react";
6963
+ import React49 from "react";
6665
6964
  function MarkdownTable({ className = "", ...rest }) {
6666
6965
  const merged = ["markdown-table", className].filter(Boolean).join(" ");
6667
- return /* @__PURE__ */ React48.createElement("div", { className: "markdown-table__frame" }, /* @__PURE__ */ React48.createElement("table", { className: merged, ...rest }));
6966
+ return /* @__PURE__ */ React49.createElement("div", { className: "markdown-table__frame" }, /* @__PURE__ */ React49.createElement("table", { className: merged, ...rest }));
6668
6967
  }
6669
6968
 
6670
6969
  // ui/src/docs/Diagram.jsx
6671
- import React49 from "react";
6970
+ import React50 from "react";
6672
6971
  function CanopyDiagram() {
6673
- return /* @__PURE__ */ React49.createElement("div", { className: "canopy-diagram" }, /* @__PURE__ */ React49.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--collections" }, /* @__PURE__ */ React49.createElement("h3", null, "IIIF Providers"), /* @__PURE__ */ React49.createElement("span", { className: "canopy-diagram__section-summary" }, "Source collections contribute 45 manifests while 5 manifests are directly retrieved as-is via IIIF endpoints."), /* @__PURE__ */ React49.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React49.createElement("article", null, /* @__PURE__ */ React49.createElement("h4", null, "Collection A"), /* @__PURE__ */ React49.createElement("ul", null, /* @__PURE__ */ React49.createElement("li", null, "30 Manifests"), /* @__PURE__ */ React49.createElement("li", null, /* @__PURE__ */ React49.createElement("em", null, "Manuscripts")))), /* @__PURE__ */ React49.createElement("article", null, /* @__PURE__ */ React49.createElement("h4", null, "Collection B"), /* @__PURE__ */ React49.createElement("ul", null, /* @__PURE__ */ React49.createElement("li", null, "15 Manifests"), /* @__PURE__ */ React49.createElement("li", null, /* @__PURE__ */ React49.createElement("em", null, "Portraits")))), /* @__PURE__ */ React49.createElement("article", null, /* @__PURE__ */ React49.createElement("h4", null, "Manifests (direct)"), /* @__PURE__ */ React49.createElement("ul", null, /* @__PURE__ */ React49.createElement("li", null, "5 Manifests"), /* @__PURE__ */ React49.createElement("li", null, /* @__PURE__ */ React49.createElement("em", null, "Scrapbooks")))))), /* @__PURE__ */ React49.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React49.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React49.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React49.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--build" }, /* @__PURE__ */ React49.createElement("h3", null, "Canopy Build Process"), /* @__PURE__ */ React49.createElement("span", { className: "canopy-diagram__section-summary" }, "Canopy retrieves collections and syncs all manifests, page content, and annotations before bundling the site."), /* @__PURE__ */ React49.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React49.createElement("article", null, /* @__PURE__ */ React49.createElement("h4", null, "Automated content"), /* @__PURE__ */ React49.createElement("ul", null, /* @__PURE__ */ React49.createElement("li", null, "50 manifests \u2192 50 work pages"), /* @__PURE__ */ React49.createElement("li", null, "One page per manifest"), /* @__PURE__ */ React49.createElement("li", null, "Customize page layout"))), /* @__PURE__ */ React49.createElement("article", null, /* @__PURE__ */ React49.createElement("h4", null, "Contextual content"), /* @__PURE__ */ React49.createElement("ul", null, /* @__PURE__ */ React49.createElement("li", null, "Markdown & MDX pages"), /* @__PURE__ */ React49.createElement("li", null, "Author narratives"), /* @__PURE__ */ React49.createElement("li", null, "Reference manifests inline"))), /* @__PURE__ */ React49.createElement("article", null, /* @__PURE__ */ React49.createElement("h4", null, "Search index"), /* @__PURE__ */ React49.createElement("ul", null, /* @__PURE__ */ React49.createElement("li", null, "Combines works + pages"), /* @__PURE__ */ React49.createElement("li", null, "Customize result layout"), /* @__PURE__ */ React49.createElement("li", null, "Optional annotations"))))), /* @__PURE__ */ React49.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React49.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React49.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React49.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--output" }, /* @__PURE__ */ React49.createElement("h3", null, "Static Digital Project"), /* @__PURE__ */ React49.createElement("span", { className: "canopy-diagram__section-summary" }, "The output is a lightweight bundle of HTML, CSS, JS, and JSON assets that can deploy anywhere."), /* @__PURE__ */ React49.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React49.createElement("article", null, /* @__PURE__ */ React49.createElement("h4", null, "Work pages"), /* @__PURE__ */ React49.createElement("ul", null, /* @__PURE__ */ React49.createElement("li", null, "50 generated HTML pages"), /* @__PURE__ */ React49.createElement("li", null, "Each links back to source manifests"), /* @__PURE__ */ React49.createElement("li", null, "Styled with Canopy components"))), /* @__PURE__ */ React49.createElement("article", null, /* @__PURE__ */ React49.createElement("h4", null, "Custom pages"), /* @__PURE__ */ React49.createElement("ul", null, /* @__PURE__ */ React49.createElement("li", null, "Markdown & MDX-authored content"), /* @__PURE__ */ React49.createElement("li", null, "Reusable layouts for narratives"), /* @__PURE__ */ React49.createElement("li", null, "Embed IIIF media & interstitials"))), /* @__PURE__ */ React49.createElement("article", null, /* @__PURE__ */ React49.createElement("h4", null, "Search bundle"), /* @__PURE__ */ React49.createElement("ul", null, /* @__PURE__ */ React49.createElement("li", null, "Static FlexSearch index"), /* @__PURE__ */ React49.createElement("li", null, "Works + pages share records"), /* @__PURE__ */ React49.createElement("li", null, "Optional annotation dataset"))))));
6972
+ return /* @__PURE__ */ React50.createElement("div", { className: "canopy-diagram" }, /* @__PURE__ */ React50.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--collections" }, /* @__PURE__ */ React50.createElement("h3", null, "IIIF Providers"), /* @__PURE__ */ React50.createElement("span", { className: "canopy-diagram__section-summary" }, "Source collections contribute 45 manifests while 5 manifests are directly retrieved as-is via IIIF endpoints."), /* @__PURE__ */ React50.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React50.createElement("article", null, /* @__PURE__ */ React50.createElement("h4", null, "Collection A"), /* @__PURE__ */ React50.createElement("ul", null, /* @__PURE__ */ React50.createElement("li", null, "30 Manifests"), /* @__PURE__ */ React50.createElement("li", null, /* @__PURE__ */ React50.createElement("em", null, "Manuscripts")))), /* @__PURE__ */ React50.createElement("article", null, /* @__PURE__ */ React50.createElement("h4", null, "Collection B"), /* @__PURE__ */ React50.createElement("ul", null, /* @__PURE__ */ React50.createElement("li", null, "15 Manifests"), /* @__PURE__ */ React50.createElement("li", null, /* @__PURE__ */ React50.createElement("em", null, "Portraits")))), /* @__PURE__ */ React50.createElement("article", null, /* @__PURE__ */ React50.createElement("h4", null, "Manifests (direct)"), /* @__PURE__ */ React50.createElement("ul", null, /* @__PURE__ */ React50.createElement("li", null, "5 Manifests"), /* @__PURE__ */ React50.createElement("li", null, /* @__PURE__ */ React50.createElement("em", null, "Scrapbooks")))))), /* @__PURE__ */ React50.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React50.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React50.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React50.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--build" }, /* @__PURE__ */ React50.createElement("h3", null, "Canopy Build Process"), /* @__PURE__ */ React50.createElement("span", { className: "canopy-diagram__section-summary" }, "Canopy retrieves collections and syncs all manifests, page content, and annotations before bundling the site."), /* @__PURE__ */ React50.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React50.createElement("article", null, /* @__PURE__ */ React50.createElement("h4", null, "Automated content"), /* @__PURE__ */ React50.createElement("ul", null, /* @__PURE__ */ React50.createElement("li", null, "50 manifests \u2192 50 work pages"), /* @__PURE__ */ React50.createElement("li", null, "One page per manifest"), /* @__PURE__ */ React50.createElement("li", null, "Customize page layout"))), /* @__PURE__ */ React50.createElement("article", null, /* @__PURE__ */ React50.createElement("h4", null, "Contextual content"), /* @__PURE__ */ React50.createElement("ul", null, /* @__PURE__ */ React50.createElement("li", null, "Markdown & MDX pages"), /* @__PURE__ */ React50.createElement("li", null, "Author narratives"), /* @__PURE__ */ React50.createElement("li", null, "Reference manifests inline"))), /* @__PURE__ */ React50.createElement("article", null, /* @__PURE__ */ React50.createElement("h4", null, "Search index"), /* @__PURE__ */ React50.createElement("ul", null, /* @__PURE__ */ React50.createElement("li", null, "Combines works + pages"), /* @__PURE__ */ React50.createElement("li", null, "Customize result layout"), /* @__PURE__ */ React50.createElement("li", null, "Optional annotations"))))), /* @__PURE__ */ React50.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React50.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React50.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React50.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--output" }, /* @__PURE__ */ React50.createElement("h3", null, "Static Digital Project"), /* @__PURE__ */ React50.createElement("span", { className: "canopy-diagram__section-summary" }, "The output is a lightweight bundle of HTML, CSS, JS, and JSON assets that can deploy anywhere."), /* @__PURE__ */ React50.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React50.createElement("article", null, /* @__PURE__ */ React50.createElement("h4", null, "Work pages"), /* @__PURE__ */ React50.createElement("ul", null, /* @__PURE__ */ React50.createElement("li", null, "50 generated HTML pages"), /* @__PURE__ */ React50.createElement("li", null, "Each links back to source manifests"), /* @__PURE__ */ React50.createElement("li", null, "Styled with Canopy components"))), /* @__PURE__ */ React50.createElement("article", null, /* @__PURE__ */ React50.createElement("h4", null, "Custom pages"), /* @__PURE__ */ React50.createElement("ul", null, /* @__PURE__ */ React50.createElement("li", null, "Markdown & MDX-authored content"), /* @__PURE__ */ React50.createElement("li", null, "Reusable layouts for narratives"), /* @__PURE__ */ React50.createElement("li", null, "Embed IIIF media & interstitials"))), /* @__PURE__ */ React50.createElement("article", null, /* @__PURE__ */ React50.createElement("h4", null, "Search bundle"), /* @__PURE__ */ React50.createElement("ul", null, /* @__PURE__ */ React50.createElement("li", null, "Static FlexSearch index"), /* @__PURE__ */ React50.createElement("li", null, "Works + pages share records"), /* @__PURE__ */ React50.createElement("li", null, "Optional annotation dataset"))))));
6674
6973
  }
6675
6974
 
6676
6975
  // ui/src/docs/ThemeShowcase.jsx
6677
- import React50 from "react";
6976
+ import React51 from "react";
6678
6977
 
6679
6978
  // ../../node_modules/@radix-ui/colors/index.mjs
6680
6979
  var colors_exports = {};
@@ -10526,21 +10825,21 @@ var STEP_MAP = {
10526
10825
  800: 11,
10527
10826
  900: 12
10528
10827
  };
10529
- var Section = ({ title, description, children }) => /* @__PURE__ */ React50.createElement("div", { className: "canopy-theme-showcase__section" }, /* @__PURE__ */ React50.createElement("h3", { className: "canopy-theme-showcase__section-title" }, title), description ? /* @__PURE__ */ React50.createElement("p", { className: "canopy-theme-showcase__section-description" }, description) : null, children);
10530
- var ColorScaleRow = ({ label, prefix }) => /* @__PURE__ */ React50.createElement("div", { className: "canopy-theme-showcase__scale-row" }, /* @__PURE__ */ React50.createElement("div", { className: "canopy-theme-showcase__scale-label" }, /* @__PURE__ */ React50.createElement("strong", null, label)), /* @__PURE__ */ React50.createElement("div", { className: "canopy-theme-showcase__scale-track" }, COLOR_STOPS.map((stop) => /* @__PURE__ */ React50.createElement(
10828
+ var Section = ({ title, description, children }) => /* @__PURE__ */ React51.createElement("div", { className: "canopy-theme-showcase__section" }, /* @__PURE__ */ React51.createElement("h3", { className: "canopy-theme-showcase__section-title" }, title), description ? /* @__PURE__ */ React51.createElement("p", { className: "canopy-theme-showcase__section-description" }, description) : null, children);
10829
+ var ColorScaleRow = ({ label, prefix }) => /* @__PURE__ */ React51.createElement("div", { className: "canopy-theme-showcase__scale-row" }, /* @__PURE__ */ React51.createElement("div", { className: "canopy-theme-showcase__scale-label" }, /* @__PURE__ */ React51.createElement("strong", null, label)), /* @__PURE__ */ React51.createElement("div", { className: "canopy-theme-showcase__scale-track" }, COLOR_STOPS.map((stop) => /* @__PURE__ */ React51.createElement(
10531
10830
  "div",
10532
10831
  {
10533
10832
  key: `${label}-${stop}`,
10534
10833
  className: "canopy-theme-showcase__scale-stop"
10535
10834
  },
10536
- /* @__PURE__ */ React50.createElement(
10835
+ /* @__PURE__ */ React51.createElement(
10537
10836
  "span",
10538
10837
  {
10539
10838
  className: "canopy-theme-showcase__scale-chip",
10540
10839
  style: { backgroundColor: `var(${prefix}-${stop})` }
10541
10840
  }
10542
10841
  ),
10543
- /* @__PURE__ */ React50.createElement("span", { className: "canopy-theme-showcase__scale-token" }, stop)
10842
+ /* @__PURE__ */ React51.createElement("span", { className: "canopy-theme-showcase__scale-token" }, stop)
10544
10843
  ))));
10545
10844
  var AVAILABLE = new Set(
10546
10845
  Object.keys(colors_exports).filter(
@@ -10605,9 +10904,9 @@ var PREVIEW_DATA = buildPreviewData();
10605
10904
  function encodeJson(value) {
10606
10905
  return JSON.stringify(value).replace(/</g, "\\u003c");
10607
10906
  }
10608
- var ColorsLabeled = ({ colors, type, getRadixSwatch }) => /* @__PURE__ */ React50.createElement("div", { className: "canopy-theme-showcase__swatch-grid" }, colors.map((name) => {
10907
+ var ColorsLabeled = ({ colors, type, getRadixSwatch }) => /* @__PURE__ */ React51.createElement("div", { className: "canopy-theme-showcase__swatch-grid" }, colors.map((name) => {
10609
10908
  const colorValue = getRadixSwatch(name);
10610
- return /* @__PURE__ */ React50.createElement(
10909
+ return /* @__PURE__ */ React51.createElement(
10611
10910
  "button",
10612
10911
  {
10613
10912
  key: `${type}-${name}`,
@@ -10618,14 +10917,14 @@ var ColorsLabeled = ({ colors, type, getRadixSwatch }) => /* @__PURE__ */ React5
10618
10917
  "data-theme-swatch-value": name,
10619
10918
  "aria-pressed": "false"
10620
10919
  },
10621
- /* @__PURE__ */ React50.createElement(
10920
+ /* @__PURE__ */ React51.createElement(
10622
10921
  "span",
10623
10922
  {
10624
10923
  className: "canopy-theme-showcase__swatch-chip",
10625
10924
  style: { background: colorValue || "var(--color-gray-200)" }
10626
10925
  }
10627
10926
  ),
10628
- /* @__PURE__ */ React50.createElement("span", { className: "canopy-theme-showcase__swatch-label" }, name)
10927
+ /* @__PURE__ */ React51.createElement("span", { className: "canopy-theme-showcase__swatch-label" }, name)
10629
10928
  );
10630
10929
  }));
10631
10930
  function ThemeShowcase() {
@@ -10785,7 +11084,7 @@ function ThemeShowcase() {
10785
11084
  .canopy-theme-showcase__swatch-controls { display: none; }
10786
11085
  .canopy-theme-showcase__clear-button { display: none; }
10787
11086
  `;
10788
- return /* @__PURE__ */ React50.createElement("div", { className: "canopy-theme-showcase", "data-theme-showcase": true }, /* @__PURE__ */ React50.createElement("style", { dangerouslySetInnerHTML: { __html: styles } }), /* @__PURE__ */ React50.createElement(
11087
+ return /* @__PURE__ */ React51.createElement("div", { className: "canopy-theme-showcase", "data-theme-showcase": true }, /* @__PURE__ */ React51.createElement("style", { dangerouslySetInnerHTML: { __html: styles } }), /* @__PURE__ */ React51.createElement(
10789
11088
  "div",
10790
11089
  {
10791
11090
  style: {
@@ -10796,18 +11095,18 @@ function ThemeShowcase() {
10796
11095
  marginBottom: "1rem"
10797
11096
  }
10798
11097
  },
10799
- /* @__PURE__ */ React50.createElement(
11098
+ /* @__PURE__ */ React51.createElement(
10800
11099
  Section,
10801
11100
  {
10802
11101
  title: "Appearance",
10803
11102
  description: "Pick the base light or dark mode for the theme preview."
10804
11103
  },
10805
- /* @__PURE__ */ React50.createElement("div", { className: "canopy-theme-showcase__appearance-buttons" }, ["light", "dark"].map((mode) => {
11104
+ /* @__PURE__ */ React51.createElement("div", { className: "canopy-theme-showcase__appearance-buttons" }, ["light", "dark"].map((mode) => {
10806
11105
  const label = `${mode.charAt(0).toUpperCase()}${mode.slice(1)}`;
10807
11106
  const baseClass = "canopy-theme-showcase__appearance-button";
10808
11107
  const isDefault = mode === DEFAULTS.appearance;
10809
11108
  const className = isDefault ? `${baseClass} is-active` : baseClass;
10810
- return /* @__PURE__ */ React50.createElement(
11109
+ return /* @__PURE__ */ React51.createElement(
10811
11110
  "button",
10812
11111
  {
10813
11112
  key: mode,
@@ -10819,7 +11118,7 @@ function ThemeShowcase() {
10819
11118
  );
10820
11119
  }))
10821
11120
  ),
10822
- /* @__PURE__ */ React50.createElement(
11121
+ /* @__PURE__ */ React51.createElement(
10823
11122
  "button",
10824
11123
  {
10825
11124
  type: "button",
@@ -10828,13 +11127,13 @@ function ThemeShowcase() {
10828
11127
  },
10829
11128
  "Reset"
10830
11129
  )
10831
- ), /* @__PURE__ */ React50.createElement(
11130
+ ), /* @__PURE__ */ React51.createElement(
10832
11131
  Section,
10833
11132
  {
10834
11133
  title: "Color scales",
10835
11134
  description: "Accent and gray ramps from the active theme."
10836
11135
  },
10837
- /* @__PURE__ */ React50.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "1.5rem" } }, COLOR_SCALES.map((scale) => /* @__PURE__ */ React50.createElement(
11136
+ /* @__PURE__ */ React51.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "1.5rem" } }, COLOR_SCALES.map((scale) => /* @__PURE__ */ React51.createElement(
10838
11137
  ColorScaleRow,
10839
11138
  {
10840
11139
  key: scale.label,
@@ -10842,13 +11141,13 @@ function ThemeShowcase() {
10842
11141
  prefix: scale.prefix
10843
11142
  }
10844
11143
  )))
10845
- ), /* @__PURE__ */ React50.createElement(
11144
+ ), /* @__PURE__ */ React51.createElement(
10846
11145
  Section,
10847
11146
  {
10848
11147
  title: "Accent color palette options",
10849
11148
  description: "Click a swatch to temporarily override the accent palette."
10850
11149
  },
10851
- /* @__PURE__ */ React50.createElement(
11150
+ /* @__PURE__ */ React51.createElement(
10852
11151
  ColorsLabeled,
10853
11152
  {
10854
11153
  colors: accentColors,
@@ -10856,13 +11155,13 @@ function ThemeShowcase() {
10856
11155
  getRadixSwatch
10857
11156
  }
10858
11157
  )
10859
- ), /* @__PURE__ */ React50.createElement(
11158
+ ), /* @__PURE__ */ React51.createElement(
10860
11159
  Section,
10861
11160
  {
10862
11161
  title: "Gray color palette options",
10863
11162
  description: "Click a swatch to preview the neutral ramp for surfaces and text."
10864
11163
  },
10865
- /* @__PURE__ */ React50.createElement(
11164
+ /* @__PURE__ */ React51.createElement(
10866
11165
  ColorsLabeled,
10867
11166
  {
10868
11167
  colors: grayColors,
@@ -10870,7 +11169,7 @@ function ThemeShowcase() {
10870
11169
  getRadixSwatch
10871
11170
  }
10872
11171
  )
10873
- ), /* @__PURE__ */ React50.createElement(
11172
+ ), /* @__PURE__ */ React51.createElement(
10874
11173
  "script",
10875
11174
  {
10876
11175
  type: "application/json",