@nswds/app 1.104.0 → 1.105.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -18269,8 +18269,9 @@ function ColorPairingToolV3Content({
18269
18269
  const [copiedKey, setCopiedKey] = React5.useState(null);
18270
18270
  const copiedKeyTimeoutRef = React5.useRef(null);
18271
18271
  const resultSectionRef = React5.useRef(null);
18272
+ const technicalDetailsSectionRef = React5.useRef(null);
18272
18273
  const noValidStateRef = React5.useRef(/* @__PURE__ */ new Set());
18273
- const technicalDetailsOpenedRef = React5.useRef(false);
18274
+ const technicalDetailsViewedRef = React5.useRef(false);
18274
18275
  const emitAnalyticsEvent = React5.useCallback(
18275
18276
  (event) => {
18276
18277
  onAnalyticsEvent(event);
@@ -18599,16 +18600,1602 @@ function ColorPairingToolV3Content({
18599
18600
  themeCategory
18600
18601
  ]);
18601
18602
  React5.useEffect(() => {
18602
- if (technicalDetailsOpenedRef.current) {
18603
+ if (technicalDetailsViewedRef.current) {
18603
18604
  return;
18604
18605
  }
18605
- technicalDetailsOpenedRef.current = true;
18606
+ const section = technicalDetailsSectionRef.current;
18607
+ if (!section) {
18608
+ return;
18609
+ }
18610
+ const emitViewedEvent = () => {
18611
+ if (technicalDetailsViewedRef.current) {
18612
+ return;
18613
+ }
18614
+ technicalDetailsViewedRef.current = true;
18615
+ emitAnalyticsEvent({
18616
+ name: "technical_details_viewed",
18617
+ ...buildAnalyticsContext({
18618
+ source: "details"
18619
+ })
18620
+ });
18621
+ };
18622
+ if (typeof IntersectionObserver === "undefined") {
18623
+ const rect = section.getBoundingClientRect();
18624
+ const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
18625
+ if (rect.top < viewportHeight && rect.bottom > 0) {
18626
+ emitViewedEvent();
18627
+ }
18628
+ return;
18629
+ }
18630
+ const observer = new IntersectionObserver(
18631
+ (entries) => {
18632
+ const [entry] = entries;
18633
+ if (entry?.isIntersecting) {
18634
+ emitViewedEvent();
18635
+ observer.disconnect();
18636
+ }
18637
+ },
18638
+ {
18639
+ threshold: 0.2
18640
+ }
18641
+ );
18642
+ observer.observe(section);
18643
+ return () => {
18644
+ observer.disconnect();
18645
+ };
18646
+ }, [buildAnalyticsContext, emitAnalyticsEvent]);
18647
+ if (!selectedBackground) {
18648
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "px-6 py-6", children: [
18649
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 2, size: 5, className: "text-foreground", trim: "normal", children: "No approved background tones available" }),
18650
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: 2, className: "mt-3", children: "No approved tones are available for the current palette and colour selection." })
18651
+ ] });
18652
+ }
18653
+ const compactControlsSummary = `${themeCategory === "brand" ? "Brand" : "Aboriginal"} palette, ${context.primary.label} primary, ${context.accent.label} accent, ${getPairingColorDisplayName3(
18654
+ selectedBackground
18655
+ )} background.`;
18656
+ const renderControlsPanel = (isOverlay = false) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
18657
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b border-grey-200 px-5 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4", children: [
18658
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 2, size: 5, className: "text-foreground", trim: "normal", children: "Configuration" }),
18659
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
18660
+ !isFirstDrawerStep ? /* @__PURE__ */ jsxRuntime.jsxs(Button2, { variant: "ghost", color: "grey", size: "sm", onClick: goToPreviousDrawerStep, children: [
18661
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.west, { "data-slot": "icon", className: "size-5" }),
18662
+ "Back"
18663
+ ] }) : null,
18664
+ isOverlay ? /* @__PURE__ */ jsxRuntime.jsx(
18665
+ Button2,
18666
+ {
18667
+ variant: "ghost",
18668
+ color: "grey",
18669
+ size: "icon",
18670
+ onClick: () => setIsCompactControlsOpen(false),
18671
+ "aria-label": "Close configuration",
18672
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icons.close, { "data-slot": "icon", className: "size-5" })
18673
+ }
18674
+ ) : null
18675
+ ] })
18676
+ ] }) }),
18677
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-grey-200 px-5 py-4", children: [
18678
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
18679
+ COLOR_PAIRING_TOOL_DRAWER_STEPS.map((step, index) => /* @__PURE__ */ jsxRuntime.jsx(
18680
+ "button",
18681
+ {
18682
+ type: "button",
18683
+ "aria-current": index === drawerStepIndex ? "step" : void 0,
18684
+ "aria-label": `${step.eyebrow}: ${step.title}`,
18685
+ onClick: () => goToDrawerStep(index),
18686
+ className: "flex-1 rounded-sm focus-visible:ring-2 focus-visible:ring-primary-700 focus-visible:ring-offset-2 focus-visible:outline-hidden",
18687
+ children: /* @__PURE__ */ jsxRuntime.jsx(
18688
+ "span",
18689
+ {
18690
+ className: cn(
18691
+ "block h-1.5 rounded-full transition-colors",
18692
+ index <= drawerStepIndex ? "bg-primary-800" : "bg-grey-200"
18693
+ )
18694
+ }
18695
+ )
18696
+ },
18697
+ step.id
18698
+ )),
18699
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-2 basis-full text-[0.68rem] font-semibold tracking-[0.12em] text-primary-800 uppercase sm:mt-0 sm:ml-2 sm:basis-auto sm:text-[0.72rem]", children: activeDrawerStep.eyebrow })
18700
+ ] }),
18701
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 space-y-2", children: [
18702
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 3, size: 5, className: "text-foreground", trim: "normal", children: activeDrawerStep.title }),
18703
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: 2, children: activeDrawerStep.description })
18704
+ ] })
18705
+ ] }),
18706
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex-1 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsxs(
18707
+ "div",
18708
+ {
18709
+ className: "flex h-full transition-transform duration-300 ease-out",
18710
+ style: { transform: `translateX(-${drawerStepIndex * 100}%)` },
18711
+ children: [
18712
+ /* @__PURE__ */ jsxRuntime.jsx("section", { className: "h-full w-full shrink-0 overflow-y-auto px-5 py-5", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6 pr-1", children: [
18713
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-3", children: [
18714
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 4, size: 6, className: "mb-3 text-foreground", trim: "normal", children: "Palette" }),
18715
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2", children: ["brand", "aboriginal"].map((palette) => {
18716
+ const isSelected = themeCategory === palette;
18717
+ const label = palette === "brand" ? "Brand palette" : "Aboriginal palette";
18718
+ return /* @__PURE__ */ jsxRuntime.jsx(
18719
+ SelectorButton,
18720
+ {
18721
+ label,
18722
+ isSelected,
18723
+ onClick: () => handleThemeCategoryChange(palette)
18724
+ },
18725
+ palette
18726
+ );
18727
+ }) })
18728
+ ] }),
18729
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-3", children: [
18730
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 4, size: 6, className: "mb-3 text-foreground", trim: "normal", children: "Primary colour family" }),
18731
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2", children: selectableFamilies.map((family) => {
18732
+ const label = getFamilySelectorLabel3(family, themeCategory, "primary colour");
18733
+ return /* @__PURE__ */ jsxRuntime.jsx(
18734
+ SelectorButton,
18735
+ {
18736
+ label,
18737
+ isSelected: family.key === context.primary.key,
18738
+ onClick: () => handlePrimaryColorChange(family.key),
18739
+ swatch: getFamilySwatchColor3(family, 800)
18740
+ },
18741
+ family.key
18742
+ );
18743
+ }) })
18744
+ ] }),
18745
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-3", children: [
18746
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 4, size: 6, className: "mb-3 text-foreground", trim: "normal", children: "Accent colour family" }),
18747
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2", children: selectableAccentFamilies.map((family) => {
18748
+ const label = getFamilySelectorLabel3(family, themeCategory, "accent colour");
18749
+ return /* @__PURE__ */ jsxRuntime.jsx(
18750
+ SelectorButton,
18751
+ {
18752
+ label,
18753
+ isSelected: family.key === context.accent.key,
18754
+ onClick: () => handleAccentColorChange(family.key),
18755
+ swatch: getFamilySwatchColor3(family, 600)
18756
+ },
18757
+ family.key
18758
+ );
18759
+ }) })
18760
+ ] }),
18761
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-grey-200 pt-4", children: [
18762
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-foreground", children: "Grey is always included" }),
18763
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "Neutral grey options are automatically added so every selection includes a practical fallback family." })
18764
+ ] })
18765
+ ] }) }),
18766
+ /* @__PURE__ */ jsxRuntime.jsx("section", { className: "h-full w-full shrink-0 overflow-y-auto px-5 py-5", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-6 pr-1", children: context.backgroundGroups.map((group, index) => /* @__PURE__ */ jsxRuntime.jsxs(
18767
+ "section",
18768
+ {
18769
+ className: cn("space-y-3", index > 0 && "border-t border-grey-200 pt-6"),
18770
+ children: [
18771
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
18772
+ /* @__PURE__ */ jsxRuntime.jsxs(Heading, { level: 4, size: 6, className: "text-foreground", trim: "normal", children: [
18773
+ group.family.label,
18774
+ " backgrounds"
18775
+ ] }),
18776
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: group.label })
18777
+ ] }),
18778
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid gap-3", children: group.backgrounds.map((background) => /* @__PURE__ */ jsxRuntime.jsx(
18779
+ BackgroundSwatchButton,
18780
+ {
18781
+ background,
18782
+ hasPairs: (context.pairsByBackground[background.token]?.length ?? 0) > 0,
18783
+ isSelected: selectedBackground.token === background.token,
18784
+ onClick: () => handleBackgroundChange(background.token)
18785
+ },
18786
+ background.token
18787
+ )) })
18788
+ ]
18789
+ },
18790
+ group.key
18791
+ )) }) }),
18792
+ /* @__PURE__ */ jsxRuntime.jsx("section", { className: "h-full w-full shrink-0 overflow-y-auto px-5 py-5", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4 pr-1", children: recommendationItems.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: recommendationItems.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
18793
+ RecommendationCard,
18794
+ {
18795
+ copiedKey,
18796
+ item,
18797
+ isSelected: item.pair.id === selectedPair?.id,
18798
+ onCopyPairing: () => copyValue(
18799
+ `pair:${item.pair.id}`,
18800
+ getPairingCopyText(item.pair),
18801
+ "Pairing copied",
18802
+ item.pair.id === bestRecommendedPair?.id ? {
18803
+ name: "best_recommendation_copied",
18804
+ ...buildAnalyticsContext({
18805
+ foregroundToken: item.pair.foreground.token,
18806
+ pairId: item.pair.id,
18807
+ source: "recommendations-list"
18808
+ })
18809
+ } : void 0
18810
+ ),
18811
+ onSelect: () => handlePairChange(item.pair.id, "recommendations-list")
18812
+ },
18813
+ item.pair.id
18814
+ )) }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-sm border border-grey-200 bg-white px-5 py-5", children: [
18815
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 4, size: 6, className: "text-foreground", trim: "normal", children: "No valid combinations available" }),
18816
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: 2, className: "mt-3", children: "No AAA-compliant foreground options are available for this background." })
18817
+ ] }) }) })
18818
+ ]
18819
+ }
18820
+ ) }),
18821
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-grey-200 px-5 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [
18822
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-10 flex-1", children: isLastDrawerStep ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Select a combination to update the result immediately." }) : null }),
18823
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full items-center gap-2 sm:w-auto sm:flex-none", children: [
18824
+ isOverlay && isLastDrawerStep ? /* @__PURE__ */ jsxRuntime.jsx(
18825
+ Button2,
18826
+ {
18827
+ color: "primary",
18828
+ className: "w-full sm:w-auto sm:min-w-40",
18829
+ onClick: () => setIsCompactControlsOpen(false),
18830
+ children: "Review result"
18831
+ }
18832
+ ) : null,
18833
+ !isLastDrawerStep ? /* @__PURE__ */ jsxRuntime.jsxs(Button2, { color: "primary", className: "w-full sm:w-auto", onClick: goToNextDrawerStep, children: [
18834
+ "Continue",
18835
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.east, { "data-slot": "icon", className: "size-5" })
18836
+ ] }) : null
18837
+ ] })
18838
+ ] }) })
18839
+ ] });
18840
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-8 xl:grid-cols-[minmax(18rem,24rem)_minmax(0,1fr)]", children: [
18841
+ /* @__PURE__ */ jsxRuntime.jsx(
18842
+ "div",
18843
+ {
18844
+ "data-slot": "tool-sidebar",
18845
+ className: "hidden xl:sticky xl:top-[var(--tool-sidebar-top)] xl:block xl:self-start",
18846
+ style: desktopSidebarStyle,
18847
+ children: /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "h-[40rem] max-h-[40rem] gap-0 overflow-hidden py-0 sm:h-[44rem] sm:max-h-[44rem] xl:h-[var(--tool-sidebar-max-height)] xl:max-h-[var(--tool-sidebar-max-height)]", children: renderControlsPanel() })
18848
+ }
18849
+ ),
18850
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-slot": "tool-results", className: "space-y-6", children: [
18851
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-4 xl:hidden", children: [
18852
+ /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "gap-4 px-5 py-5", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between", children: [
18853
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
18854
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 2, size: 5, className: "text-foreground", trim: "normal", children: "Configuration" }),
18855
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: 2, children: "Open the colour pairing drawer to change palette, background, and recommended combinations." }),
18856
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: compactControlsSummary })
18857
+ ] }),
18858
+ /* @__PURE__ */ jsxRuntime.jsxs(
18859
+ Button2,
18860
+ {
18861
+ color: "primary",
18862
+ className: "w-full sm:w-auto",
18863
+ onClick: () => setIsCompactControlsOpen(true),
18864
+ children: [
18865
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.edit_square, { "data-slot": "icon", className: "size-5" }),
18866
+ "Open configuration"
18867
+ ]
18868
+ }
18869
+ )
18870
+ ] }) }),
18871
+ /* @__PURE__ */ jsxRuntime.jsx(Sheet, { open: isCompactControlsOpen, onOpenChange: setIsCompactControlsOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(
18872
+ SheetContent,
18873
+ {
18874
+ side: "left",
18875
+ showClose: false,
18876
+ className: "w-[30rem] max-w-[90vw] overflow-hidden p-0",
18877
+ children: [
18878
+ /* @__PURE__ */ jsxRuntime.jsx(SheetTitle, { className: "sr-only", children: "Colour pairing configuration" }),
18879
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full flex-col bg-background", children: renderControlsPanel(true) })
18880
+ ]
18881
+ }
18882
+ ) })
18883
+ ] }),
18884
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { ref: resultSectionRef, className: "space-y-6", "aria-label": "Current result", children: [
18885
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sr-only", "aria-live": "polite", children: liveAnnouncement }),
18886
+ /* @__PURE__ */ jsxRuntime.jsx(
18887
+ CurrentResultCard,
18888
+ {
18889
+ bestPair: bestRecommendedPair,
18890
+ familySummary,
18891
+ pair: previewPair,
18892
+ selectedBackground
18893
+ }
18894
+ )
18895
+ ] }),
18896
+ bestRecommendedPair ? /* @__PURE__ */ jsxRuntime.jsx("section", { className: "space-y-4", children: /* @__PURE__ */ jsxRuntime.jsx(
18897
+ BestRecommendationCard,
18898
+ {
18899
+ copiedKey,
18900
+ isCurrentSelection: selectedPair?.id === bestRecommendedPair.id,
18901
+ pair: bestRecommendedPair,
18902
+ reason: getBestRecommendationReason(bestRecommendedPair, context),
18903
+ onCopyPairing: () => copyValue(
18904
+ "best-pairing",
18905
+ getPairingCopyText(bestRecommendedPair),
18906
+ "Pairing copied",
18907
+ {
18908
+ name: "best_recommendation_copied",
18909
+ ...buildAnalyticsContext({
18910
+ foregroundToken: bestRecommendedPair.foreground.token,
18911
+ pairId: bestRecommendedPair.id,
18912
+ source: "best-recommendation"
18913
+ })
18914
+ }
18915
+ ),
18916
+ onUsePairing: () => handlePairChange(bestRecommendedPair.id, "best-recommendation")
18917
+ }
18918
+ ) }) : /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "px-6 py-6", children: [
18919
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 2, size: 5, className: "text-foreground", trim: "normal", children: "Best recommended pairing" }),
18920
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: 2, className: "mt-3", children: "No AAA-compliant foreground options available for this selection." })
18921
+ ] }),
18922
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { ref: technicalDetailsSectionRef, className: "space-y-4", children: [
18923
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
18924
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 2, size: 5, className: "text-foreground", trim: "normal", children: "Technical colour values" }),
18925
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: 2, children: "Token, tone, HEX, RGB, HSL, and OKLCH values for the current selection." })
18926
+ ] }),
18927
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [
18928
+ /* @__PURE__ */ jsxRuntime.jsx(
18929
+ TechnicalDetailsPanel,
18930
+ {
18931
+ title: "Background values",
18932
+ color: selectedBackground,
18933
+ visibleFormats,
18934
+ copiedKey,
18935
+ onCopyValue: (copyKey, value, toastLabel) => copyValue(copyKey, value, toastLabel)
18936
+ }
18937
+ ),
18938
+ /* @__PURE__ */ jsxRuntime.jsx(
18939
+ TechnicalDetailsPanel,
18940
+ {
18941
+ title: "Foreground values",
18942
+ color: detailForeground,
18943
+ visibleFormats,
18944
+ copiedKey,
18945
+ onCopyValue: (copyKey, value, toastLabel) => copyValue(copyKey, value, toastLabel)
18946
+ }
18947
+ )
18948
+ ] }),
18949
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
18950
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 3, size: 6, className: "mb-3 text-foreground", trim: "normal", children: "Share colour pairing" }),
18951
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-sm border border-grey-200 bg-white p-4", children: [
18952
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start gap-3 sm:flex-row sm:items-start sm:justify-between", children: [
18953
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Copy this URL to share your current colour pairing:" }),
18954
+ /* @__PURE__ */ jsxRuntime.jsx(
18955
+ Button2,
18956
+ {
18957
+ variant: "ghost",
18958
+ color: "grey",
18959
+ size: "sm",
18960
+ className: "shrink-0",
18961
+ onClick: () => copyValue(
18962
+ "share-url",
18963
+ typeof window === "undefined" ? shareUrl : new URL(shareUrl, window.location.origin).toString(),
18964
+ "Colour pairing link copied"
18965
+ ),
18966
+ "aria-label": copiedKey === "share-url" ? "Colour pairing URL copied to clipboard" : "Copy colour pairing URL to clipboard",
18967
+ title: copiedKey === "share-url" ? "Copied" : "Copy URL",
18968
+ children: copiedKey === "share-url" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
18969
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.check, { "data-slot": "icon", className: "size-5" }),
18970
+ "Copied"
18971
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
18972
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.content_copy, { "data-slot": "icon", className: "size-5" }),
18973
+ "Copy URL"
18974
+ ] })
18975
+ }
18976
+ )
18977
+ ] }),
18978
+ /* @__PURE__ */ jsxRuntime.jsx("code", { className: "mt-3 block w-full rounded-sm border border-grey-200 bg-background px-3 py-2 font-mono text-[11px] break-all text-foreground sm:text-xs", children: shareUrl })
18979
+ ] })
18980
+ ] })
18981
+ ] })
18982
+ ] })
18983
+ ] });
18984
+ }
18985
+ function ColorPairingToolV3({
18986
+ onAnalyticsEvent = () => {
18987
+ },
18988
+ visibleFormats = DEFAULT_VISIBLE_FORMATS3
18989
+ } = {}) {
18990
+ const normalizedVisibleFormats = [...new Set(visibleFormats)];
18991
+ return /* @__PURE__ */ jsxRuntime.jsx(React5.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(ColorPairingToolV3Loading, {}), children: /* @__PURE__ */ jsxRuntime.jsx(
18992
+ ColorPairingToolV3Content,
18993
+ {
18994
+ onAnalyticsEvent,
18995
+ visibleFormats: normalizedVisibleFormats
18996
+ }
18997
+ ) });
18998
+ }
18999
+ var PREFERRED_BACKGROUND_TONES4 = [400, 600, 200, 800, 100, 50];
19000
+ var DEFAULT_VISIBLE_FORMATS4 = ["hex", "rgb", "hsl", "oklch"];
19001
+ var DEFAULT_INITIAL_BACKGROUND_TOKEN4 = "nsw-blue-800";
19002
+ var DEFAULT_INITIAL_PAIR_ID4 = "nsw-blue-800:nsw-blue-200";
19003
+ var COLOR_PAIRING_TOOL_V4_PATH = "/core/colour/colour-pairing-tool-4";
19004
+ var MOBILE_RESULT_SCROLL_QUERY2 = "(max-width: 1023px)";
19005
+ var PERSISTENT_DRAWER_MIN_WIDTH_QUERY2 = "(min-width: 1280px)";
19006
+ var COLOR_PAIRING_TOOL_DRAWER_STEPS2 = [
19007
+ {
19008
+ id: "colours",
19009
+ title: "Choose colours",
19010
+ eyebrow: "Step 1 of 3",
19011
+ description: "Select the palette, primary family, and accent family."
19012
+ },
19013
+ {
19014
+ id: "backgrounds",
19015
+ title: "Choose background",
19016
+ eyebrow: "Step 2 of 3",
19017
+ description: "Pick the background tone that you want to pair."
19018
+ },
19019
+ {
19020
+ id: "refine",
19021
+ title: "Refine or override recommendation",
19022
+ eyebrow: "Step 3 of 3",
19023
+ description: "Use the result panel to review the NSW default pairing and override it only when a different approved option is needed."
19024
+ }
19025
+ ];
19026
+ var COLOR_PAIRING_TOOL_V4_ANALYTICS_EVENT = "nsw-colour-pairing-tool-v4";
19027
+ function getToneFromToken4(token) {
19028
+ if (!token) return null;
19029
+ const match = token.match(/-(\d+)$/);
19030
+ return match ? Number.parseInt(match[1], 10) : null;
19031
+ }
19032
+ function getFamilySwatchColor4(family, preferredTone = 600) {
19033
+ const exactMatch = family.colors.find((color2) => color2.tone === preferredTone);
19034
+ if (exactMatch) {
19035
+ return exactMatch.hex;
19036
+ }
19037
+ const closestMatch = [...family.colors].sort(
19038
+ (left, right) => Math.abs(left.tone - preferredTone) - Math.abs(right.tone - preferredTone)
19039
+ )[0];
19040
+ return closestMatch?.hex ?? "transparent";
19041
+ }
19042
+ function getFamilySelectorLabel4(family, themeCategory, selectionRole) {
19043
+ if (themeCategory !== "aboriginal") {
19044
+ return family.label;
19045
+ }
19046
+ const preferredTone = selectionRole === "primary colour" ? 800 : 600;
19047
+ return family.colors.find((color2) => color2.tone === preferredTone)?.name ?? family.label;
19048
+ }
19049
+ function getPairingColorDisplayName4(color2) {
19050
+ return color2.name ?? `${color2.familyLabel} ${color2.tone}`;
19051
+ }
19052
+ function isWhiteForegroundPair4(pair) {
19053
+ return pair.foreground.token === "white";
19054
+ }
19055
+ function isLargeTextOnlyPair2(pair) {
19056
+ return pair.passes.aaaLarge && !pair.passes.aaaText;
19057
+ }
19058
+ function getWhiteForegroundGuidance4(pair) {
19059
+ if (pair.passes.aaaText) {
19060
+ return "White is approved for headings, body copy, and calls to action on this background.";
19061
+ }
19062
+ if (pair.passes.aaaLarge) {
19063
+ return "Use white only for WCAG large text on this background, such as headings at 24px+ or bold text at 18.5px+. Keep sentence-case body copy at 16px+ and use a darker recommended foreground instead.";
19064
+ }
19065
+ return "Do not use white on this background. Choose one of the recommended foregrounds below instead.";
19066
+ }
19067
+ function getPreviewGuidance4(pair, isRecommended) {
19068
+ if (!isWhiteForegroundPair4(pair)) {
19069
+ return "Use only AAA-recommended combinations across your selected primary, accent, and grey families.";
19070
+ }
19071
+ if (isRecommended) {
19072
+ return "Use white text on dark colour only when it meets AAA for headings, body copy, and calls to action.";
19073
+ }
19074
+ return getWhiteForegroundGuidance4(pair);
19075
+ }
19076
+ function getPreferredPairForBackground4(pairs, preferredPairId) {
19077
+ if (preferredPairId) {
19078
+ const preferredPair = pairs.find((pair) => pair.id === preferredPairId);
19079
+ if (preferredPair) {
19080
+ return preferredPair;
19081
+ }
19082
+ }
19083
+ return pairs.find((pair) => !isWhiteForegroundPair4(pair)) ?? pairs[0] ?? null;
19084
+ }
19085
+ function getDefaultBackgroundToken4(context) {
19086
+ for (const tone of PREFERRED_BACKGROUND_TONES4) {
19087
+ for (const group of context.backgroundGroups) {
19088
+ const match = group.backgrounds.find(
19089
+ (background) => background.tone === tone && (context.pairsByBackground[background.token]?.length ?? 0) > 0
19090
+ );
19091
+ if (match) {
19092
+ return match.token;
19093
+ }
19094
+ }
19095
+ }
19096
+ for (const tone of PREFERRED_BACKGROUND_TONES4) {
19097
+ for (const group of context.backgroundGroups) {
19098
+ const match = group.backgrounds.find((background) => background.tone === tone);
19099
+ if (match) {
19100
+ return match.token;
19101
+ }
19102
+ }
19103
+ }
19104
+ return context.backgrounds[0]?.token ?? "";
19105
+ }
19106
+ function resolveBackgroundToken4(context, preferredToken, preferredTone) {
19107
+ if (preferredToken && context.backgrounds.some((background) => background.token === preferredToken)) {
19108
+ return preferredToken;
19109
+ }
19110
+ if (preferredTone !== null && preferredTone !== void 0) {
19111
+ for (const group of context.backgroundGroups) {
19112
+ const match = group.backgrounds.find((background) => background.tone === preferredTone);
19113
+ if (match) {
19114
+ return match.token;
19115
+ }
19116
+ }
19117
+ }
19118
+ return getDefaultBackgroundToken4(context);
19119
+ }
19120
+ function getInitialPairingState4(searchParams) {
19121
+ const paletteParam = searchParams.get("palette");
19122
+ const primaryParam = searchParams.get("primary");
19123
+ const accentParam = searchParams.get("accent");
19124
+ const pairParam = searchParams.get("pair");
19125
+ const backgroundParam = searchParams.get("background");
19126
+ const themeCategory = paletteParam === "brand" || paletteParam === "aboriginal" ? paletteParam : "brand";
19127
+ const context = getPairingContext(themeCategory, primaryParam, accentParam);
19128
+ const shouldUseDefaultBrandExample = !backgroundParam && !pairParam && themeCategory === "brand" && context.primary.key === "blue" && context.accent.key === "red";
19129
+ const defaultBackgroundToken = shouldUseDefaultBrandExample ? context.backgrounds.some(
19130
+ (background) => background.token === DEFAULT_INITIAL_BACKGROUND_TOKEN4
19131
+ ) ? DEFAULT_INITIAL_BACKGROUND_TOKEN4 : null : null;
19132
+ const defaultPairId = shouldUseDefaultBrandExample && defaultBackgroundToken && context.pairsByBackground[defaultBackgroundToken]?.some(
19133
+ (pair) => pair.id === DEFAULT_INITIAL_PAIR_ID4
19134
+ ) ? DEFAULT_INITIAL_PAIR_ID4 : null;
19135
+ const pairBackgroundToken = context.recommendedPairs.find((pair) => pair.id === pairParam)?.background.token ?? null;
19136
+ const selectedBackgroundToken = resolveBackgroundToken4(
19137
+ context,
19138
+ backgroundParam ?? pairBackgroundToken ?? defaultBackgroundToken,
19139
+ getToneFromToken4(backgroundParam ?? pairBackgroundToken ?? defaultBackgroundToken)
19140
+ );
19141
+ const selectedPairId = getPreferredPairForBackground4(
19142
+ context.pairsByBackground[selectedBackgroundToken] ?? [],
19143
+ pairParam ?? defaultPairId
19144
+ )?.id ?? "";
19145
+ return {
19146
+ accentKey: context.accent.key,
19147
+ primaryKey: context.primary.key,
19148
+ selectedBackgroundToken,
19149
+ selectedPairId,
19150
+ themeCategory
19151
+ };
19152
+ }
19153
+ function getReadableTextColor3(tone) {
19154
+ return tone >= 600 ? "#ffffff" : "#002664";
19155
+ }
19156
+ function getBackgroundOptionName2(background) {
19157
+ return background.name ?? `${background.familyLabel} ${background.tone}`;
19158
+ }
19159
+ function getMainPairingLabel(pair, bestPair) {
19160
+ if (!pair) {
19161
+ return "No AAA pairing available";
19162
+ }
19163
+ if (pair.id === bestPair?.id) {
19164
+ return "NSW recommended pairing";
19165
+ }
19166
+ if (isWhiteForegroundPair4(pair) && isLargeTextOnlyPair2(pair)) {
19167
+ return "Large-text example only";
19168
+ }
19169
+ if (isWhiteForegroundPair4(pair) && !pair.passes.aaaLarge) {
19170
+ return "Example only";
19171
+ }
19172
+ return "Approved alternative";
19173
+ }
19174
+ function getMainPairingSupportCopy(pair, bestPair, context) {
19175
+ if (!pair) {
19176
+ return "No AAA-compliant foreground available for this background.";
19177
+ }
19178
+ if (pair.id === bestPair?.id) {
19179
+ return getBestRecommendationReason2(pair, context);
19180
+ }
19181
+ if (isWhiteForegroundPair4(pair) && isLargeTextOnlyPair2(pair)) {
19182
+ return "White works here for large text only. Use the NSW default for standard body copy and smaller interface text.";
19183
+ }
19184
+ if (isWhiteForegroundPair4(pair) && !pair.passes.aaaLarge) {
19185
+ return "White does not meet AAA on this background. Choose a different approved background or foreground.";
19186
+ }
19187
+ return "Approved AAA alternative when a different tone or emphasis is needed.";
19188
+ }
19189
+ function getBestRecommendationReason2(pair, context) {
19190
+ if (isWhiteForegroundPair4(pair)) {
19191
+ return "Recommended because this background is dark enough to support white text at AAA contrast.";
19192
+ }
19193
+ if (pair.foreground.familyKey === pair.background.familyKey) {
19194
+ return "Recommended because it stays within the same colour family while keeping clear AAA contrast.";
19195
+ }
19196
+ if (pair.foreground.familyKey === context.grey.key) {
19197
+ return "Recommended because grey provides a strong neutral contrast without competing with the background.";
19198
+ }
19199
+ if (pair.foreground.familyKey === context.accent.key) {
19200
+ return "Recommended because the selected accent family adds emphasis while preserving AAA contrast.";
19201
+ }
19202
+ if (pair.foreground.familyKey === context.primary.key) {
19203
+ return "Recommended because the selected primary family gives the clearest AAA-compliant contrast for this background.";
19204
+ }
19205
+ return "Recommended because it is the clearest NSW-approved AAA pairing for this background.";
19206
+ }
19207
+ function getRecommendationCategory2(pair, bestPair, context) {
19208
+ if (pair.id === bestPair?.id) return "best";
19209
+ if (isWhiteForegroundPair4(pair)) return "white";
19210
+ if (pair.foreground.familyKey === pair.background.familyKey) return "same-family";
19211
+ if (pair.foreground.familyKey === context.accent.key) return "accent-family";
19212
+ if (pair.foreground.familyKey === context.grey.key) return "grey-option";
19213
+ if (pair.foreground.familyKey === context.primary.key) return "primary-family";
19214
+ return "approved";
19215
+ }
19216
+ function getRecommendationCategoryLabel2(category) {
19217
+ switch (category) {
19218
+ case "best":
19219
+ return "Best recommended";
19220
+ case "same-family":
19221
+ return "Same family";
19222
+ case "accent-family":
19223
+ return "Accent family";
19224
+ case "grey-option":
19225
+ return "Grey option";
19226
+ case "primary-family":
19227
+ return "Primary family";
19228
+ case "white":
19229
+ return "White";
19230
+ default:
19231
+ return "Approved option";
19232
+ }
19233
+ }
19234
+ function getRecommendationSortRank2(category) {
19235
+ switch (category) {
19236
+ case "best":
19237
+ return 0;
19238
+ case "same-family":
19239
+ return 1;
19240
+ case "accent-family":
19241
+ return 2;
19242
+ case "grey-option":
19243
+ return 3;
19244
+ case "white":
19245
+ return 4;
19246
+ case "primary-family":
19247
+ return 5;
19248
+ default:
19249
+ return 6;
19250
+ }
19251
+ }
19252
+ function getRecommendationGroupId(category) {
19253
+ switch (category) {
19254
+ case "best":
19255
+ case "same-family":
19256
+ case "accent-family":
19257
+ case "grey-option":
19258
+ case "primary-family":
19259
+ return "good-alternatives";
19260
+ case "white":
19261
+ case "approved":
19262
+ return "edge-options";
19263
+ default:
19264
+ return null;
19265
+ }
19266
+ }
19267
+ function buildRecommendationGroups(items) {
19268
+ const groupedItems = items.reduce(
19269
+ (accumulator, item) => {
19270
+ const groupId = getRecommendationGroupId(item.category);
19271
+ if (!groupId) {
19272
+ return accumulator;
19273
+ }
19274
+ accumulator[groupId].push(item);
19275
+ return accumulator;
19276
+ },
19277
+ {
19278
+ "edge-options": [],
19279
+ "good-alternatives": []
19280
+ }
19281
+ );
19282
+ const groups = [
19283
+ {
19284
+ id: "good-alternatives",
19285
+ title: "Good alternatives",
19286
+ description: "These pairings still meet AAA and stay close to the NSW recommendation in tone, neutrality, or emphasis.",
19287
+ items: groupedItems["good-alternatives"]
19288
+ },
19289
+ {
19290
+ id: "edge-options",
19291
+ title: "Edge and less common options",
19292
+ description: "These are valid AAA pairings, but they are less typical NSW defaults and should be used more deliberately.",
19293
+ items: groupedItems["edge-options"]
19294
+ }
19295
+ ];
19296
+ return groups.filter((group) => group.items.length > 0);
19297
+ }
19298
+ function getAvailabilityMeta2(isSelected, hasPairs) {
19299
+ if (isSelected && hasPairs) {
19300
+ return {
19301
+ description: "Selected background with AAA foreground options.",
19302
+ icon: Icons.check_circle,
19303
+ label: "Selected",
19304
+ tone: "selected"
19305
+ };
19306
+ }
19307
+ if (isSelected) {
19308
+ return {
19309
+ description: "Selected background with no AAA-compliant foreground available.",
19310
+ icon: Icons.warning,
19311
+ label: "Selected, no AAA",
19312
+ tone: "unavailable"
19313
+ };
19314
+ }
19315
+ if (hasPairs) {
19316
+ return {
19317
+ description: "AAA foreground options available.",
19318
+ icon: Icons.radio_button_checked,
19319
+ label: "Available",
19320
+ tone: "available"
19321
+ };
19322
+ }
19323
+ return {
19324
+ description: "No AAA-compliant foreground available for this background.",
19325
+ icon: Icons.close,
19326
+ label: "No AAA",
19327
+ tone: "unavailable"
19328
+ };
19329
+ }
19330
+ function getPairingCopyText2(pair) {
19331
+ const accessibilityLine = pair.passes.aaaText ? "Accessibility: Meets AAA for normal and large text" : pair.passes.aaaLarge ? "Accessibility: Meets AAA for large text only" : "Accessibility: Fails AAA for normal and large text";
19332
+ return [
19333
+ `Background: ${pair.background.token} (${pair.background.hex})`,
19334
+ `Foreground: ${pair.foreground.token} (${pair.foreground.hex})`,
19335
+ `Contrast ratio: ${pair.contrastRatio.toFixed(2)}:1`,
19336
+ accessibilityLine
19337
+ ].join("\n");
19338
+ }
19339
+ function getLiveAnnouncement2(pair, background) {
19340
+ if (!background) {
19341
+ return "No approved background tones available.";
19342
+ }
19343
+ if (!pair) {
19344
+ return `${background.token} selected. No AAA-compliant foreground options available for this selection.`;
19345
+ }
19346
+ if (isWhiteForegroundPair4(pair) && isLargeTextOnlyPair2(pair)) {
19347
+ return `${background.token} with white. Contrast ratio ${pair.contrastRatio.toFixed(2)} to 1. Meets AAA for large text only.`;
19348
+ }
19349
+ if (isWhiteForegroundPair4(pair) && !pair.passes.aaaLarge) {
19350
+ return `${background.token} with white. Contrast ratio ${pair.contrastRatio.toFixed(2)} to 1. Fails AAA for normal and large text.`;
19351
+ }
19352
+ return `${background.token} with ${pair.foreground.token}. Contrast ratio ${pair.contrastRatio.toFixed(2)} to 1. Meets AAA for normal and large text.`;
19353
+ }
19354
+ function formatValueRows2(color2, visibleFormats) {
19355
+ const hasDisplayTone = color2.token !== "white";
19356
+ return [
19357
+ { key: "token", label: "Token", value: color2.token, copyable: true },
19358
+ {
19359
+ key: "tone",
19360
+ label: "Tone",
19361
+ value: hasDisplayTone ? String(color2.tone) : "Not applicable",
19362
+ copyable: hasDisplayTone
19363
+ },
19364
+ ...visibleFormats.map((format) => ({
19365
+ key: format,
19366
+ label: format.toUpperCase(),
19367
+ value: color2[format],
19368
+ copyable: true
19369
+ }))
19370
+ ];
19371
+ }
19372
+ function SelectorButton2({
19373
+ description,
19374
+ isSelected,
19375
+ label,
19376
+ onClick,
19377
+ swatch
19378
+ }) {
19379
+ return /* @__PURE__ */ jsxRuntime.jsx(
19380
+ "button",
19381
+ {
19382
+ type: "button",
19383
+ "aria-pressed": isSelected,
19384
+ onClick,
19385
+ className: cn(
19386
+ "w-full rounded-sm border px-3 py-3 text-left transition-colors focus-visible:ring-2 focus-visible:ring-primary-700 focus-visible:ring-offset-2 focus-visible:outline-hidden",
19387
+ isSelected ? "border-primary-800 bg-primary-50" : "border-grey-300 bg-white hover:border-primary-500 hover:bg-grey-50"
19388
+ ),
19389
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3", children: [
19390
+ swatch ? /* @__PURE__ */ jsxRuntime.jsx(
19391
+ "span",
19392
+ {
19393
+ "aria-hidden": "true",
19394
+ className: "mt-0.5 size-4 shrink-0 rounded-full border border-black/10",
19395
+ style: { backgroundColor: swatch }
19396
+ }
19397
+ ) : null,
19398
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 space-y-1", children: [
19399
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-foreground", children: label }),
19400
+ description ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs/5 text-muted-foreground", children: description }) : null
19401
+ ] })
19402
+ ] })
19403
+ }
19404
+ );
19405
+ }
19406
+ function BackgroundSwatchButton2({
19407
+ background,
19408
+ hasPairs,
19409
+ isSelected,
19410
+ onClick
19411
+ }) {
19412
+ const status = getAvailabilityMeta2(isSelected, hasPairs);
19413
+ const StatusIcon = status.icon;
19414
+ return /* @__PURE__ */ jsxRuntime.jsxs(
19415
+ "button",
19416
+ {
19417
+ type: "button",
19418
+ "aria-pressed": isSelected,
19419
+ "aria-label": `Select ${background.token} background. ${status.description}`,
19420
+ onClick,
19421
+ className: cn(
19422
+ "w-full rounded-sm border bg-white p-3 text-left transition-colors focus-visible:ring-2 focus-visible:ring-primary-700 focus-visible:ring-offset-2 focus-visible:outline-hidden",
19423
+ isSelected ? "border-primary-800 bg-primary-50" : hasPairs ? "border-grey-300 hover:border-primary-500 hover:bg-grey-50" : "border-grey-300 hover:border-grey-500"
19424
+ ),
19425
+ children: [
19426
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
19427
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
19428
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-foreground", children: getBackgroundOptionName2(background) }),
19429
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 font-mono text-[0.72rem] break-all text-muted-foreground", children: background.token })
19430
+ ] }),
19431
+ /* @__PURE__ */ jsxRuntime.jsxs(
19432
+ "span",
19433
+ {
19434
+ className: cn(
19435
+ "inline-flex items-center gap-1 rounded-sm border px-2 py-1 text-[0.68rem] font-semibold uppercase",
19436
+ status.tone === "selected" && "border-primary-800 bg-primary-800 text-white dark:border-primary-500 dark:bg-primary-500",
19437
+ status.tone === "available" && "border-grey-300 bg-grey-50 text-foreground",
19438
+ status.tone === "unavailable" && "border-danger-300 bg-danger-50 text-danger-900 dark:border-danger-800 dark:bg-danger-950/20 dark:text-danger-200"
19439
+ ),
19440
+ children: [
19441
+ /* @__PURE__ */ jsxRuntime.jsx(StatusIcon, { "data-slot": "icon", className: "size-4" }),
19442
+ status.label
19443
+ ]
19444
+ }
19445
+ )
19446
+ ] }),
19447
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 overflow-hidden rounded-[2px] border border-black/10", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-14 w-full", style: { backgroundColor: background.hex } }) }),
19448
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-3 text-xs/5 text-muted-foreground", children: status.description })
19449
+ ]
19450
+ }
19451
+ );
19452
+ }
19453
+ function CompactAccessibilityStatus({ label, passes }) {
19454
+ const status = passes === null ? "unavailable" : passes ? "pass" : "fail";
19455
+ return /* @__PURE__ */ jsxRuntime.jsxs(
19456
+ "div",
19457
+ {
19458
+ className: cn(
19459
+ "flex items-center justify-between gap-3 rounded-sm border px-4 py-3",
19460
+ status === "pass" && "border-success-200 bg-success-50",
19461
+ status === "fail" && "border-danger-200 bg-danger-50",
19462
+ status === "unavailable" && "border-grey-200 bg-white"
19463
+ ),
19464
+ children: [
19465
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-foreground", children: label }),
19466
+ /* @__PURE__ */ jsxRuntime.jsxs(
19467
+ "span",
19468
+ {
19469
+ className: cn(
19470
+ "inline-flex items-center gap-1 rounded-sm px-2.5 py-1 text-[0.72rem] font-semibold tracking-[0.12em] uppercase",
19471
+ status === "pass" && "bg-success-700 text-white",
19472
+ status === "fail" && "bg-danger-700 text-white",
19473
+ status === "unavailable" && "bg-grey-100 text-foreground"
19474
+ ),
19475
+ children: [
19476
+ status === "pass" ? /* @__PURE__ */ jsxRuntime.jsx(Icons.check, { "data-slot": "icon", className: "size-4" }) : status === "fail" ? /* @__PURE__ */ jsxRuntime.jsx(Icons.close, { "data-slot": "icon", className: "size-4" }) : /* @__PURE__ */ jsxRuntime.jsx(Icons.info, { "data-slot": "icon", className: "size-4" }),
19477
+ status === "pass" ? "Pass" : status === "fail" ? "Fail" : "Unavailable"
19478
+ ]
19479
+ }
19480
+ )
19481
+ ]
19482
+ }
19483
+ );
19484
+ }
19485
+ function AccessibilitySummaryCard({ pair }) {
19486
+ return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "gap-4 px-5 py-5", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-5 lg:flex-row lg:items-start lg:justify-between", children: [
19487
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
19488
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 2, size: 6, className: "text-foreground", trim: "normal", children: "Accessibility summary" }),
19489
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
19490
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-grey-700", children: "Contrast ratio" }),
19491
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-4xl leading-none font-bold text-foreground", children: pair ? `${pair.contrastRatio.toFixed(2)}:1` : "N/A" })
19492
+ ] })
19493
+ ] }),
19494
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 lg:max-w-[26rem] lg:min-w-[22rem]", children: [
19495
+ /* @__PURE__ */ jsxRuntime.jsx(
19496
+ CompactAccessibilityStatus,
19497
+ {
19498
+ label: "AAA normal text",
19499
+ passes: pair ? pair.passes.aaaText : null
19500
+ }
19501
+ ),
19502
+ /* @__PURE__ */ jsxRuntime.jsx(
19503
+ CompactAccessibilityStatus,
19504
+ {
19505
+ label: "AAA large text",
19506
+ passes: pair ? pair.passes.aaaLarge : null
19507
+ }
19508
+ )
19509
+ ] })
19510
+ ] }) });
19511
+ }
19512
+ function CurrentResultCard2({
19513
+ bestPair,
19514
+ copiedKey,
19515
+ onCopyPairing,
19516
+ pair,
19517
+ supportText,
19518
+ selectedBackground
19519
+ }) {
19520
+ const previewForeground = pair?.foreground.hex ?? getReadableTextColor3(selectedBackground.tone);
19521
+ const isRecommended = pair ? pair.id === bestPair?.id : false;
19522
+ const whiteForeground = pair ? isWhiteForegroundPair4(pair) : false;
19523
+ const pairingLabel = getMainPairingLabel(pair, bestPair);
19524
+ const fauxButtonStyle = pair ? {
19525
+ "--btn-bg": pair.foreground.hex,
19526
+ "--btn-border": pair.foreground.hex,
19527
+ "--btn-text": pair.background.hex,
19528
+ "--btn-icon": pair.background.hex,
19529
+ "--btn-hover-overlay": pair.background.hex
19530
+ } : null;
19531
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: cn("gap-0 overflow-hidden py-0", isRecommended && "border-primary-200"), children: [
19532
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b border-grey-200 px-4 py-4 sm:px-6 sm:py-5", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between", children: [
19533
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 space-y-3", children: [
19534
+ /* @__PURE__ */ jsxRuntime.jsxs(
19535
+ "span",
19536
+ {
19537
+ className: cn(
19538
+ "inline-flex items-center gap-2 rounded-sm px-3 py-1 text-[0.72rem] font-semibold tracking-[0.12em] uppercase",
19539
+ isRecommended ? "bg-primary-800 text-white" : "border border-grey-300 bg-grey-50 text-foreground"
19540
+ ),
19541
+ children: [
19542
+ isRecommended ? /* @__PURE__ */ jsxRuntime.jsx(Icons.check_circle, { "data-slot": "icon", className: "size-4" }) : /* @__PURE__ */ jsxRuntime.jsx(Icons.info, { "data-slot": "icon", className: "size-4" }),
19543
+ pairingLabel
19544
+ ]
19545
+ }
19546
+ ),
19547
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
19548
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 2, size: 4, className: "text-foreground", trim: "normal", children: pair ? `${getPairingColorDisplayName4(pair.foreground)} on ${getPairingColorDisplayName4(pair.background)}` : `${getPairingColorDisplayName4(selectedBackground)} selected` }),
19549
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: 2, className: "max-w-3xl text-grey-700", children: supportText })
19550
+ ] })
19551
+ ] }),
19552
+ pair ? /* @__PURE__ */ jsxRuntime.jsx(
19553
+ Button2,
19554
+ {
19555
+ color: "primary",
19556
+ className: "w-full justify-center lg:w-auto lg:min-w-48",
19557
+ onClick: onCopyPairing,
19558
+ children: copiedKey === "current-pairing" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
19559
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.check, { "data-slot": "icon", className: "size-5" }),
19560
+ "Pairing copied"
19561
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
19562
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.content_copy, { "data-slot": "icon", className: "size-5" }),
19563
+ "Copy pairing"
19564
+ ] })
19565
+ }
19566
+ ) : null
19567
+ ] }) }),
19568
+ /* @__PURE__ */ jsxRuntime.jsx(
19569
+ "div",
19570
+ {
19571
+ className: "p-4 sm:min-h-[26rem] sm:p-8",
19572
+ style: {
19573
+ backgroundColor: selectedBackground.hex,
19574
+ color: previewForeground
19575
+ },
19576
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[18rem] flex-col justify-center gap-6 sm:min-h-[22rem]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-xl space-y-4", children: [
19577
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold tracking-[0.22em] uppercase", children: pair ? whiteForeground && !isRecommended ? "White on colour example" : whiteForeground ? "White on colour" : "Colour on colour" : "Approved background" }),
19578
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
19579
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "max-w-lg text-3xl leading-tight font-bold text-current sm:text-5xl sm:leading-none", children: "Pair colour with confidence." }),
19580
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "max-w-full text-base/6 sm:max-w-md sm:text-base/7", children: pair ? getPreviewGuidance4(pair, isRecommended) : "This approved background tone does not currently have a recommended AAA foreground in this tool. Choose another approved background tone to continue." }),
19581
+ pair && fauxButtonStyle ? /* @__PURE__ */ jsxRuntime.jsx(
19582
+ "span",
19583
+ {
19584
+ "aria-hidden": "true",
19585
+ "data-variant": "solid",
19586
+ className: cn(
19587
+ buttonVariants({ variant: "solid", size: "default" }),
19588
+ "pointer-events-none cursor-default px-6 text-[16px] font-[700] select-none sm:px-6 sm:text-[16px] sm:font-[700]"
19589
+ ),
19590
+ style: fauxButtonStyle,
19591
+ children: "Get started"
19592
+ }
19593
+ ) : null
19594
+ ] })
19595
+ ] }) })
19596
+ }
19597
+ )
19598
+ ] });
19599
+ }
19600
+ function RecommendationCard2({
19601
+ copiedKey,
19602
+ isSelected,
19603
+ item,
19604
+ onCopyPairing,
19605
+ onSelect
19606
+ }) {
19607
+ const { pair } = item;
19608
+ return /* @__PURE__ */ jsxRuntime.jsxs(
19609
+ "div",
19610
+ {
19611
+ className: cn(
19612
+ "relative rounded-sm border bg-white p-4 text-left transition-colors focus-visible:ring-2 focus-visible:ring-primary-700 focus-visible:ring-offset-2 focus-visible:outline-hidden",
19613
+ isSelected ? "border-primary-800 bg-primary-50/60 ring-1 ring-primary-700" : "border-grey-200 hover:border-primary-500 hover:bg-grey-50"
19614
+ ),
19615
+ children: [
19616
+ /* @__PURE__ */ jsxRuntime.jsx(
19617
+ "button",
19618
+ {
19619
+ type: "button",
19620
+ "aria-pressed": isSelected,
19621
+ "aria-label": `Use ${pair.foreground.token} on ${pair.background.token}`,
19622
+ onClick: onSelect,
19623
+ className: "absolute inset-0 rounded-sm focus-visible:ring-2 focus-visible:ring-primary-700 focus-visible:ring-offset-2 focus-visible:outline-hidden"
19624
+ }
19625
+ ),
19626
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pointer-events-none relative z-10", children: [
19627
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start gap-3 sm:flex-row sm:items-start sm:justify-between", children: [
19628
+ /* @__PURE__ */ jsxRuntime.jsxs(
19629
+ "span",
19630
+ {
19631
+ className: cn(
19632
+ "inline-flex items-center gap-2 rounded-sm px-2.5 py-1 text-[0.68rem] font-semibold tracking-[0.12em] uppercase",
19633
+ item.category === "best" ? "bg-primary-800 text-white" : "border border-grey-300 bg-grey-50 text-foreground"
19634
+ ),
19635
+ children: [
19636
+ item.category === "best" ? /* @__PURE__ */ jsxRuntime.jsx(Icons.check_circle, { "data-slot": "icon", className: "size-4" }) : /* @__PURE__ */ jsxRuntime.jsx(Icons.info, { "data-slot": "icon", className: "size-4" }),
19637
+ item.categoryLabel
19638
+ ]
19639
+ }
19640
+ ),
19641
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-auto shrink-0 self-start", children: /* @__PURE__ */ jsxRuntime.jsxs(
19642
+ Button2,
19643
+ {
19644
+ variant: "ghost",
19645
+ color: "grey",
19646
+ size: "sm",
19647
+ onClick: onCopyPairing,
19648
+ "aria-label": `Copy pairing ${pair.background.token} and ${pair.foreground.token}`,
19649
+ children: [
19650
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.content_copy, { "data-slot": "icon", className: "size-5" }),
19651
+ copiedKey === `pair:${pair.id}` ? "Copied" : "Copy"
19652
+ ]
19653
+ }
19654
+ ) })
19655
+ ] }),
19656
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4", children: [
19657
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-4 sm:grid-cols-[7.5rem_minmax(0,1fr)]", children: [
19658
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "overflow-hidden rounded-[2px] border border-black/10", children: [
19659
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-20 w-full", style: { backgroundColor: pair.background.hex } }),
19660
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 w-full", style: { backgroundColor: pair.foreground.hex } })
19661
+ ] }),
19662
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 space-y-2", children: [
19663
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
19664
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-base font-semibold text-foreground", children: getPairingColorDisplayName4(pair.foreground) }),
19665
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 font-mono text-[0.74rem] break-all text-muted-foreground", children: pair.foreground.token })
19666
+ ] }),
19667
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-muted-foreground", children: [
19668
+ pair.foreground.familyLabel,
19669
+ " on ",
19670
+ pair.background.familyLabel
19671
+ ] })
19672
+ ] })
19673
+ ] }),
19674
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 flex flex-col items-start gap-3 sm:flex-row sm:flex-wrap sm:items-center sm:justify-between", children: [
19675
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
19676
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex rounded-sm border border-grey-300 px-2.5 py-1 text-sm font-semibold text-foreground", children: [
19677
+ pair.contrastRatio.toFixed(2),
19678
+ ":1"
19679
+ ] }),
19680
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex rounded-sm bg-success-700 px-2.5 py-1 text-[0.72rem] font-semibold tracking-[0.12em] text-white uppercase", children: "AAA" })
19681
+ ] }),
19682
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold text-primary-800", children: isSelected ? "Selected answer" : "Use pairing" })
19683
+ ] })
19684
+ ] })
19685
+ ] })
19686
+ ]
19687
+ }
19688
+ );
19689
+ }
19690
+ function RecommendationGroupSection({
19691
+ children,
19692
+ description,
19693
+ title
19694
+ }) {
19695
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-3", children: [
19696
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
19697
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 3, size: 6, className: "text-foreground", trim: "normal", children: title }),
19698
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: 2, children: description })
19699
+ ] }),
19700
+ children
19701
+ ] });
19702
+ }
19703
+ function DrawerSummaryItem({ label, value }) {
19704
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1 border-t border-grey-200 pt-3 first:border-t-0 first:pt-0", children: [
19705
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[0.72rem] font-semibold tracking-[0.12em] text-muted-foreground uppercase", children: label }),
19706
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-foreground", children: value })
19707
+ ] });
19708
+ }
19709
+ function TechnicalDetailsPanel2({
19710
+ color: color2,
19711
+ copiedKey,
19712
+ onCopyValue,
19713
+ title,
19714
+ visibleFormats
19715
+ }) {
19716
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-sm border border-grey-200 bg-white", children: [
19717
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-grey-200 px-4 py-4 sm:px-5", children: [
19718
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 3, size: 6, className: "text-foreground", trim: "normal", children: title }),
19719
+ color2 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
19720
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: getPairingColorDisplayName4(color2) }),
19721
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 overflow-hidden rounded-sm border border-black/10 bg-white", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-20 w-full sm:h-24", style: { backgroundColor: color2.hex } }) })
19722
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "No AAA foreground available." })
19723
+ ] }),
19724
+ color2 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-grey-200", children: formatValueRows2(color2, visibleFormats).map((row) => {
19725
+ const copyKey = `${title}-${row.key}`;
19726
+ return /* @__PURE__ */ jsxRuntime.jsxs(
19727
+ "div",
19728
+ {
19729
+ className: "flex flex-col items-start gap-3 px-4 py-4 sm:flex-row sm:items-start sm:justify-between sm:gap-4 sm:px-5",
19730
+ children: [
19731
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
19732
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[0.72rem] font-semibold tracking-[0.12em] text-muted-foreground uppercase", children: row.label }),
19733
+ /* @__PURE__ */ jsxRuntime.jsx(
19734
+ "p",
19735
+ {
19736
+ className: cn(
19737
+ "mt-2 text-sm text-foreground",
19738
+ row.key !== "tone" && "font-mono break-all"
19739
+ ),
19740
+ children: row.value
19741
+ }
19742
+ )
19743
+ ] }),
19744
+ row.copyable ? /* @__PURE__ */ jsxRuntime.jsx(
19745
+ Button2,
19746
+ {
19747
+ variant: "ghost",
19748
+ color: "grey",
19749
+ size: "sm",
19750
+ className: "self-start",
19751
+ onClick: () => onCopyValue(copyKey, row.value, `${row.label} copied`),
19752
+ "aria-label": `Copy ${title.toLowerCase()} ${row.label.toLowerCase()}`,
19753
+ children: copiedKey === copyKey ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
19754
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.check_circle, { "data-slot": "icon", className: "size-5" }),
19755
+ "Copied"
19756
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
19757
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.content_copy, { "data-slot": "icon", className: "size-5" }),
19758
+ "Copy"
19759
+ ] })
19760
+ }
19761
+ ) : null
19762
+ ]
19763
+ },
19764
+ row.key
19765
+ );
19766
+ }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 py-5", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "No technical values are shown because there is no AAA-compliant foreground for this background." }) })
19767
+ ] });
19768
+ }
19769
+ function resolveSelectionState2(nextThemeCategory, nextPrimaryKey, nextAccentKey, preferredBackgroundToken, preferredPairId) {
19770
+ const context = getPairingContext(nextThemeCategory, nextPrimaryKey, nextAccentKey);
19771
+ const selectedBackgroundToken = resolveBackgroundToken4(
19772
+ context,
19773
+ preferredBackgroundToken,
19774
+ getToneFromToken4(preferredBackgroundToken)
19775
+ );
19776
+ const selectedPairId = getPreferredPairForBackground4(
19777
+ context.pairsByBackground[selectedBackgroundToken] ?? [],
19778
+ preferredPairId
19779
+ )?.id ?? "";
19780
+ return {
19781
+ accentKey: context.accent.key,
19782
+ context,
19783
+ primaryKey: context.primary.key,
19784
+ selectedBackgroundToken,
19785
+ selectedPairId,
19786
+ themeCategory: nextThemeCategory
19787
+ };
19788
+ }
19789
+ function ColorPairingToolV4Loading() {
19790
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-8 xl:grid-cols-[minmax(18rem,24rem)_minmax(0,1fr)]", children: [
19791
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
19792
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-52 rounded-sm border border-grey-200 bg-grey-50" }),
19793
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[32rem] rounded-sm border border-grey-200 bg-grey-50" })
19794
+ ] }),
19795
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
19796
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-96 rounded-sm border border-grey-200 bg-grey-50" }),
19797
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-64 rounded-sm border border-grey-200 bg-grey-50" }),
19798
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-72 rounded-sm border border-grey-200 bg-grey-50" })
19799
+ ] })
19800
+ ] });
19801
+ }
19802
+ function ColorPairingToolV4Content({
19803
+ onAnalyticsEvent,
19804
+ visibleFormats
19805
+ }) {
19806
+ const searchParams = navigation.useSearchParams();
19807
+ const stickyOffset = useStickyOffset(24);
19808
+ const [initialState] = React5.useState(() => getInitialPairingState4(searchParams));
19809
+ const [themeCategory, setThemeCategory] = React5.useState(initialState.themeCategory);
19810
+ const [primaryFamilyKey, setPrimaryFamilyKey] = React5.useState(initialState.primaryKey);
19811
+ const [accentFamilyKey, setAccentFamilyKey] = React5.useState(initialState.accentKey);
19812
+ const [selectedBackgroundToken, setSelectedBackgroundToken] = React5.useState(
19813
+ initialState.selectedBackgroundToken
19814
+ );
19815
+ const [selectedPairId, setSelectedPairId] = React5.useState(initialState.selectedPairId);
19816
+ const [drawerStepIndex, setDrawerStepIndex] = React5.useState(0);
19817
+ const [isCompactControlsOpen, setIsCompactControlsOpen] = React5.useState(false);
19818
+ const [, copyToClipboardRaw] = usehooks.useCopyToClipboard();
19819
+ const [copiedKey, setCopiedKey] = React5.useState(null);
19820
+ const copiedKeyTimeoutRef = React5.useRef(null);
19821
+ const resultSectionRef = React5.useRef(null);
19822
+ const technicalDetailsSectionRef = React5.useRef(null);
19823
+ const noValidStateRef = React5.useRef(/* @__PURE__ */ new Set());
19824
+ const technicalDetailsViewedRef = React5.useRef(false);
19825
+ const emitAnalyticsEvent = React5.useCallback(
19826
+ (event) => {
19827
+ onAnalyticsEvent(event);
19828
+ if (typeof window !== "undefined") {
19829
+ window.dispatchEvent(
19830
+ new CustomEvent(COLOR_PAIRING_TOOL_V4_ANALYTICS_EVENT, {
19831
+ detail: event
19832
+ })
19833
+ );
19834
+ }
19835
+ },
19836
+ [onAnalyticsEvent]
19837
+ );
19838
+ React5.useEffect(() => {
19839
+ return () => {
19840
+ if (copiedKeyTimeoutRef.current) {
19841
+ clearTimeout(copiedKeyTimeoutRef.current);
19842
+ }
19843
+ };
19844
+ }, []);
19845
+ React5.useEffect(() => {
19846
+ if (typeof window === "undefined") {
19847
+ return;
19848
+ }
19849
+ const mediaQuery = window.matchMedia(PERSISTENT_DRAWER_MIN_WIDTH_QUERY2);
19850
+ const handleChange = (event) => {
19851
+ if (event?.matches ?? mediaQuery.matches) {
19852
+ setIsCompactControlsOpen(false);
19853
+ }
19854
+ };
19855
+ handleChange();
19856
+ mediaQuery.addEventListener("change", handleChange);
19857
+ return () => {
19858
+ mediaQuery.removeEventListener("change", handleChange);
19859
+ };
19860
+ }, []);
19861
+ const themeFamilies = React5.useMemo(() => getPairingFamilies(themeCategory), [themeCategory]);
19862
+ const context = React5.useMemo(
19863
+ () => getPairingContext(themeCategory, primaryFamilyKey, accentFamilyKey),
19864
+ [themeCategory, primaryFamilyKey, accentFamilyKey]
19865
+ );
19866
+ const selectableFamilies = React5.useMemo(
19867
+ () => themeFamilies.filter((family) => family.key !== context.grey.key),
19868
+ [themeFamilies, context.grey.key]
19869
+ );
19870
+ const selectableAccentFamilies = React5.useMemo(
19871
+ () => selectableFamilies.filter((family) => family.key !== context.primary.key),
19872
+ [selectableFamilies, context.primary.key]
19873
+ );
19874
+ const selectedBackground = React5.useMemo(
19875
+ () => context.backgrounds.find((background) => background.token === selectedBackgroundToken) ?? context.backgrounds[0] ?? null,
19876
+ [context.backgrounds, selectedBackgroundToken]
19877
+ );
19878
+ const selectedBackgroundPairs = React5.useMemo(
19879
+ () => selectedBackground ? context.pairsByBackground[selectedBackground.token] ?? [] : [],
19880
+ [context.pairsByBackground, selectedBackground]
19881
+ );
19882
+ const bestRecommendedPair = React5.useMemo(
19883
+ () => getPreferredPairForBackground4(selectedBackgroundPairs),
19884
+ [selectedBackgroundPairs]
19885
+ );
19886
+ const selectedPair = React5.useMemo(
19887
+ () => getPreferredPairForBackground4(selectedBackgroundPairs, selectedPairId),
19888
+ [selectedBackgroundPairs, selectedPairId]
19889
+ );
19890
+ const whiteForegroundExample = React5.useMemo(
19891
+ () => selectedBackground && supportsWhiteForegroundPreview(selectedBackground) ? getWhiteForegroundPair(selectedBackground) : null,
19892
+ [selectedBackground]
19893
+ );
19894
+ const previewPair = selectedPair ?? whiteForegroundExample ?? null;
19895
+ const detailForeground = selectedPair?.foreground ?? whiteForegroundExample?.foreground ?? null;
19896
+ const currentPairSupportText = React5.useMemo(
19897
+ () => getMainPairingSupportCopy(previewPair, bestRecommendedPair, context),
19898
+ [bestRecommendedPair, context, previewPair]
19899
+ );
19900
+ const recommendationItems = React5.useMemo(() => {
19901
+ return selectedBackgroundPairs.map((pair) => {
19902
+ const category = getRecommendationCategory2(pair, bestRecommendedPair, context);
19903
+ return {
19904
+ category,
19905
+ categoryLabel: getRecommendationCategoryLabel2(category),
19906
+ pair
19907
+ };
19908
+ }).sort(
19909
+ (left, right) => getRecommendationSortRank2(left.category) - getRecommendationSortRank2(right.category)
19910
+ );
19911
+ }, [bestRecommendedPair, context, selectedBackgroundPairs]);
19912
+ const alternativeRecommendationGroups = React5.useMemo(
19913
+ () => buildRecommendationGroups(
19914
+ recommendationItems.filter((item) => item.pair.id !== selectedPair?.id)
19915
+ ),
19916
+ [recommendationItems, selectedPair?.id]
19917
+ );
19918
+ const liveAnnouncement = React5.useMemo(
19919
+ () => getLiveAnnouncement2(previewPair, selectedBackground),
19920
+ [previewPair, selectedBackground]
19921
+ );
19922
+ const shareBackgroundToken = selectedBackground?.token ?? selectedBackgroundToken;
19923
+ const shareUrl = React5.useMemo(() => {
19924
+ const params = new URLSearchParams();
19925
+ params.set("palette", themeCategory);
19926
+ params.set("primary", context.primary.key);
19927
+ params.set("accent", context.accent.key);
19928
+ if (shareBackgroundToken) {
19929
+ params.set("background", shareBackgroundToken);
19930
+ }
19931
+ if (selectedPairId) {
19932
+ params.set("pair", selectedPairId);
19933
+ }
19934
+ const query = params.toString();
19935
+ return `${COLOR_PAIRING_TOOL_V4_PATH}${query ? `?${query}` : ""}`;
19936
+ }, [context.accent.key, context.primary.key, shareBackgroundToken, selectedPairId, themeCategory]);
19937
+ const activeDrawerStep = COLOR_PAIRING_TOOL_DRAWER_STEPS2[drawerStepIndex];
19938
+ const isFirstDrawerStep = drawerStepIndex === 0;
19939
+ const isLastDrawerStep = drawerStepIndex === COLOR_PAIRING_TOOL_DRAWER_STEPS2.length - 1;
19940
+ const desktopSidebarStyle = React5.useMemo(
19941
+ () => ({
19942
+ "--tool-sidebar-max-height": `calc(100vh - ${stickyOffset}px)`,
19943
+ "--tool-sidebar-top": `${stickyOffset}px`
19944
+ }),
19945
+ [stickyOffset]
19946
+ );
19947
+ const goToDrawerStep = React5.useCallback((stepIndex) => {
19948
+ setDrawerStepIndex(Math.max(0, Math.min(stepIndex, COLOR_PAIRING_TOOL_DRAWER_STEPS2.length - 1)));
19949
+ }, []);
19950
+ const goToNextDrawerStep = React5.useCallback(() => {
19951
+ setDrawerStepIndex(
19952
+ (currentStep) => Math.min(currentStep + 1, COLOR_PAIRING_TOOL_DRAWER_STEPS2.length - 1)
19953
+ );
19954
+ }, []);
19955
+ const goToPreviousDrawerStep = React5.useCallback(() => {
19956
+ setDrawerStepIndex((currentStep) => Math.max(currentStep - 1, 0));
19957
+ }, []);
19958
+ const updateUrlParams = React5.useCallback((nextState) => {
19959
+ const params = new URLSearchParams(window.location.search);
19960
+ params.delete("family");
19961
+ params.set("palette", nextState.themeCategory);
19962
+ params.set("primary", nextState.primaryKey);
19963
+ params.set("accent", nextState.accentKey);
19964
+ params.set("background", nextState.selectedBackgroundToken);
19965
+ if (nextState.selectedPairId) {
19966
+ params.set("pair", nextState.selectedPairId);
19967
+ } else {
19968
+ params.delete("pair");
19969
+ }
19970
+ window.history.replaceState(
19971
+ null,
19972
+ "",
19973
+ `${window.location.pathname}?${params.toString()}${window.location.hash}`
19974
+ );
19975
+ }, []);
19976
+ const applyResolvedSelection = React5.useCallback(
19977
+ (nextState) => {
19978
+ setThemeCategory(nextState.themeCategory);
19979
+ setPrimaryFamilyKey(nextState.primaryKey);
19980
+ setAccentFamilyKey(nextState.accentKey);
19981
+ setSelectedBackgroundToken(nextState.selectedBackgroundToken);
19982
+ setSelectedPairId(nextState.selectedPairId);
19983
+ updateUrlParams(nextState);
19984
+ },
19985
+ [updateUrlParams]
19986
+ );
19987
+ const buildAnalyticsContext = React5.useCallback(
19988
+ (overrides) => ({
19989
+ accentKey: context.accent.key,
19990
+ backgroundToken: selectedBackground?.token,
19991
+ foregroundToken: selectedPair?.foreground.token,
19992
+ pairId: selectedPair?.id,
19993
+ palette: themeCategory,
19994
+ primaryKey: context.primary.key,
19995
+ ...overrides
19996
+ }),
19997
+ [context.accent.key, context.primary.key, selectedBackground, selectedPair, themeCategory]
19998
+ );
19999
+ const copyValue = React5.useCallback(
20000
+ (copyKey, value, toastLabel, analyticsEvent) => {
20001
+ copyToClipboardRaw(value);
20002
+ setCopiedKey(copyKey);
20003
+ sonner.toast(toastLabel, { duration: 2e3 });
20004
+ if (analyticsEvent) {
20005
+ emitAnalyticsEvent(analyticsEvent);
20006
+ }
20007
+ if (copiedKeyTimeoutRef.current) {
20008
+ clearTimeout(copiedKeyTimeoutRef.current);
20009
+ }
20010
+ copiedKeyTimeoutRef.current = setTimeout(() => {
20011
+ setCopiedKey(null);
20012
+ copiedKeyTimeoutRef.current = null;
20013
+ }, 2e3);
20014
+ },
20015
+ [copyToClipboardRaw, emitAnalyticsEvent]
20016
+ );
20017
+ const handleThemeCategoryChange = (nextThemeCategory) => {
20018
+ const nextState = resolveSelectionState2(
20019
+ nextThemeCategory,
20020
+ primaryFamilyKey,
20021
+ accentFamilyKey,
20022
+ selectedBackgroundToken,
20023
+ null
20024
+ );
20025
+ applyResolvedSelection(nextState);
20026
+ emitAnalyticsEvent({
20027
+ name: "palette_change",
20028
+ accentKey: nextState.accentKey,
20029
+ backgroundToken: nextState.selectedBackgroundToken,
20030
+ pairId: nextState.selectedPairId,
20031
+ palette: nextState.themeCategory,
20032
+ primaryKey: nextState.primaryKey
20033
+ });
20034
+ };
20035
+ const handlePrimaryColorChange = (nextPrimaryKey) => {
20036
+ const nextAccentKey = nextPrimaryKey === accentFamilyKey ? getDefaultAccentFamilyKey(themeCategory, nextPrimaryKey) : accentFamilyKey;
20037
+ const nextState = resolveSelectionState2(
20038
+ themeCategory,
20039
+ nextPrimaryKey,
20040
+ nextAccentKey,
20041
+ selectedBackgroundToken,
20042
+ null
20043
+ );
20044
+ applyResolvedSelection(nextState);
20045
+ emitAnalyticsEvent({
20046
+ name: "primary_change",
20047
+ accentKey: nextState.accentKey,
20048
+ backgroundToken: nextState.selectedBackgroundToken,
20049
+ pairId: nextState.selectedPairId,
20050
+ palette: nextState.themeCategory,
20051
+ primaryKey: nextState.primaryKey
20052
+ });
20053
+ };
20054
+ const handleAccentColorChange = (nextAccentKey) => {
20055
+ if (nextAccentKey === primaryFamilyKey) return;
20056
+ const nextState = resolveSelectionState2(
20057
+ themeCategory,
20058
+ primaryFamilyKey,
20059
+ nextAccentKey,
20060
+ selectedBackgroundToken,
20061
+ null
20062
+ );
20063
+ applyResolvedSelection(nextState);
20064
+ emitAnalyticsEvent({
20065
+ name: "accent_change",
20066
+ accentKey: nextState.accentKey,
20067
+ backgroundToken: nextState.selectedBackgroundToken,
20068
+ pairId: nextState.selectedPairId,
20069
+ palette: nextState.themeCategory,
20070
+ primaryKey: nextState.primaryKey
20071
+ });
20072
+ };
20073
+ const handleBackgroundChange = (nextSelectedBackgroundToken) => {
20074
+ const nextSelectedPairId = getPreferredPairForBackground4(context.pairsByBackground[nextSelectedBackgroundToken] ?? [])?.id ?? "";
20075
+ const nextState = {
20076
+ accentKey: context.accent.key,
20077
+ context,
20078
+ primaryKey: context.primary.key,
20079
+ selectedBackgroundToken: nextSelectedBackgroundToken,
20080
+ selectedPairId: nextSelectedPairId,
20081
+ themeCategory
20082
+ };
20083
+ applyResolvedSelection(nextState);
20084
+ emitAnalyticsEvent({
20085
+ name: "background_selection",
20086
+ ...buildAnalyticsContext({
20087
+ backgroundToken: nextSelectedBackgroundToken,
20088
+ foregroundToken: context.pairsByBackground[nextSelectedBackgroundToken]?.find(
20089
+ (pair) => pair.id === nextSelectedPairId
20090
+ )?.foreground.token,
20091
+ pairId: nextSelectedPairId
20092
+ })
20093
+ });
20094
+ if (typeof window !== "undefined" && window.matchMedia(MOBILE_RESULT_SCROLL_QUERY2).matches) {
20095
+ requestAnimationFrame(() => {
20096
+ resultSectionRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
20097
+ });
20098
+ }
20099
+ };
20100
+ const handlePairChange = (nextSelectedPairId, source) => {
20101
+ if (!selectedBackground) return;
20102
+ const nextPair = selectedBackgroundPairs.find((pair) => pair.id === nextSelectedPairId) ?? null;
20103
+ setSelectedPairId(nextSelectedPairId);
20104
+ updateUrlParams({
20105
+ accentKey: context.accent.key,
20106
+ context,
20107
+ primaryKey: context.primary.key,
20108
+ selectedBackgroundToken: selectedBackground.token,
20109
+ selectedPairId: nextSelectedPairId,
20110
+ themeCategory
20111
+ });
20112
+ emitAnalyticsEvent({
20113
+ name: "foreground_selection",
20114
+ ...buildAnalyticsContext({
20115
+ foregroundToken: nextPair?.foreground.token,
20116
+ pairId: nextSelectedPairId,
20117
+ source
20118
+ })
20119
+ });
20120
+ if (bestRecommendedPair && nextSelectedPairId !== bestRecommendedPair.id) {
20121
+ emitAnalyticsEvent({
20122
+ name: "alternative_combination_selected",
20123
+ ...buildAnalyticsContext({
20124
+ foregroundToken: nextPair?.foreground.token,
20125
+ pairId: nextSelectedPairId,
20126
+ source
20127
+ })
20128
+ });
20129
+ }
20130
+ };
20131
+ React5.useEffect(() => {
20132
+ if (!selectedBackground || selectedBackgroundPairs.length > 0) {
20133
+ return;
20134
+ }
20135
+ const key = `${themeCategory}:${context.primary.key}:${context.accent.key}:${selectedBackground.token}`;
20136
+ if (noValidStateRef.current.has(key)) {
20137
+ return;
20138
+ }
20139
+ noValidStateRef.current.add(key);
18606
20140
  emitAnalyticsEvent({
18607
- name: "technical_details_opened",
20141
+ name: "no_valid_combination_state",
18608
20142
  ...buildAnalyticsContext({
18609
- source: "details"
20143
+ backgroundToken: selectedBackground.token
18610
20144
  })
18611
20145
  });
20146
+ }, [
20147
+ buildAnalyticsContext,
20148
+ context.accent.key,
20149
+ context.primary.key,
20150
+ emitAnalyticsEvent,
20151
+ selectedBackground,
20152
+ selectedBackgroundPairs.length,
20153
+ themeCategory
20154
+ ]);
20155
+ React5.useEffect(() => {
20156
+ if (technicalDetailsViewedRef.current) {
20157
+ return;
20158
+ }
20159
+ const section = technicalDetailsSectionRef.current;
20160
+ if (!section) {
20161
+ return;
20162
+ }
20163
+ const emitViewedEvent = () => {
20164
+ if (technicalDetailsViewedRef.current) {
20165
+ return;
20166
+ }
20167
+ technicalDetailsViewedRef.current = true;
20168
+ emitAnalyticsEvent({
20169
+ name: "technical_details_viewed",
20170
+ ...buildAnalyticsContext({
20171
+ source: "details"
20172
+ })
20173
+ });
20174
+ };
20175
+ if (typeof IntersectionObserver === "undefined") {
20176
+ const rect = section.getBoundingClientRect();
20177
+ const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
20178
+ if (rect.top < viewportHeight && rect.bottom > 0) {
20179
+ emitViewedEvent();
20180
+ }
20181
+ return;
20182
+ }
20183
+ const observer = new IntersectionObserver(
20184
+ (entries) => {
20185
+ const [entry] = entries;
20186
+ if (entry?.isIntersecting) {
20187
+ emitViewedEvent();
20188
+ observer.disconnect();
20189
+ }
20190
+ },
20191
+ {
20192
+ threshold: 0.2
20193
+ }
20194
+ );
20195
+ observer.observe(section);
20196
+ return () => {
20197
+ observer.disconnect();
20198
+ };
18612
20199
  }, [buildAnalyticsContext, emitAnalyticsEvent]);
18613
20200
  if (!selectedBackground) {
18614
20201
  return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "px-6 py-6", children: [
@@ -18616,7 +20203,7 @@ function ColorPairingToolV3Content({
18616
20203
  /* @__PURE__ */ jsxRuntime.jsx(Text, { size: 2, className: "mt-3", children: "No approved tones are available for the current palette and colour selection." })
18617
20204
  ] });
18618
20205
  }
18619
- const compactControlsSummary = `${themeCategory === "brand" ? "Brand" : "Aboriginal"} palette, ${context.primary.label} primary, ${context.accent.label} accent, ${getPairingColorDisplayName3(
20206
+ const compactControlsSummary = `${themeCategory === "brand" ? "Brand" : "Aboriginal"} palette, ${context.primary.label} primary, ${context.accent.label} accent, ${getPairingColorDisplayName4(
18620
20207
  selectedBackground
18621
20208
  )} background.`;
18622
20209
  const renderControlsPanel = (isOverlay = false) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -18642,7 +20229,7 @@ function ColorPairingToolV3Content({
18642
20229
  ] }) }),
18643
20230
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-grey-200 px-5 py-4", children: [
18644
20231
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
18645
- COLOR_PAIRING_TOOL_DRAWER_STEPS.map((step, index) => /* @__PURE__ */ jsxRuntime.jsx(
20232
+ COLOR_PAIRING_TOOL_DRAWER_STEPS2.map((step, index) => /* @__PURE__ */ jsxRuntime.jsx(
18646
20233
  "button",
18647
20234
  {
18648
20235
  type: "button",
@@ -18682,7 +20269,7 @@ function ColorPairingToolV3Content({
18682
20269
  const isSelected = themeCategory === palette;
18683
20270
  const label = palette === "brand" ? "Brand palette" : "Aboriginal palette";
18684
20271
  return /* @__PURE__ */ jsxRuntime.jsx(
18685
- SelectorButton,
20272
+ SelectorButton2,
18686
20273
  {
18687
20274
  label,
18688
20275
  isSelected,
@@ -18695,14 +20282,14 @@ function ColorPairingToolV3Content({
18695
20282
  /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-3", children: [
18696
20283
  /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 4, size: 6, className: "mb-3 text-foreground", trim: "normal", children: "Primary colour family" }),
18697
20284
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2", children: selectableFamilies.map((family) => {
18698
- const label = getFamilySelectorLabel3(family, themeCategory, "primary colour");
20285
+ const label = getFamilySelectorLabel4(family, themeCategory, "primary colour");
18699
20286
  return /* @__PURE__ */ jsxRuntime.jsx(
18700
- SelectorButton,
20287
+ SelectorButton2,
18701
20288
  {
18702
20289
  label,
18703
20290
  isSelected: family.key === context.primary.key,
18704
20291
  onClick: () => handlePrimaryColorChange(family.key),
18705
- swatch: getFamilySwatchColor3(family, 800)
20292
+ swatch: getFamilySwatchColor4(family, 800)
18706
20293
  },
18707
20294
  family.key
18708
20295
  );
@@ -18711,14 +20298,14 @@ function ColorPairingToolV3Content({
18711
20298
  /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-3", children: [
18712
20299
  /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 4, size: 6, className: "mb-3 text-foreground", trim: "normal", children: "Accent colour family" }),
18713
20300
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2", children: selectableAccentFamilies.map((family) => {
18714
- const label = getFamilySelectorLabel3(family, themeCategory, "accent colour");
20301
+ const label = getFamilySelectorLabel4(family, themeCategory, "accent colour");
18715
20302
  return /* @__PURE__ */ jsxRuntime.jsx(
18716
- SelectorButton,
20303
+ SelectorButton2,
18717
20304
  {
18718
20305
  label,
18719
20306
  isSelected: family.key === context.accent.key,
18720
20307
  onClick: () => handleAccentColorChange(family.key),
18721
- swatch: getFamilySwatchColor3(family, 600)
20308
+ swatch: getFamilySwatchColor4(family, 600)
18722
20309
  },
18723
20310
  family.key
18724
20311
  );
@@ -18742,7 +20329,7 @@ function ColorPairingToolV3Content({
18742
20329
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: group.label })
18743
20330
  ] }),
18744
20331
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid gap-3", children: group.backgrounds.map((background) => /* @__PURE__ */ jsxRuntime.jsx(
18745
- BackgroundSwatchButton,
20332
+ BackgroundSwatchButton2,
18746
20333
  {
18747
20334
  background,
18748
20335
  hasPairs: (context.pairsByBackground[background.token]?.length ?? 0) > 0,
@@ -18755,37 +20342,41 @@ function ColorPairingToolV3Content({
18755
20342
  },
18756
20343
  group.key
18757
20344
  )) }) }),
18758
- /* @__PURE__ */ jsxRuntime.jsx("section", { className: "h-full w-full shrink-0 overflow-y-auto px-5 py-5", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4 pr-1", children: recommendationItems.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: recommendationItems.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
18759
- RecommendationCard,
18760
- {
18761
- copiedKey,
18762
- item,
18763
- isSelected: item.pair.id === selectedPair?.id,
18764
- onCopyPairing: () => copyValue(
18765
- `pair:${item.pair.id}`,
18766
- getPairingCopyText(item.pair),
18767
- "Pairing copied",
18768
- item.pair.id === bestRecommendedPair?.id ? {
18769
- name: "best_recommendation_copied",
18770
- ...buildAnalyticsContext({
18771
- foregroundToken: item.pair.foreground.token,
18772
- pairId: item.pair.id,
18773
- source: "recommendations-list"
18774
- })
18775
- } : void 0
18776
- ),
18777
- onSelect: () => handlePairChange(item.pair.id, "recommendations-list")
18778
- },
18779
- item.pair.id
18780
- )) }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-sm border border-grey-200 bg-white px-5 py-5", children: [
18781
- /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 4, size: 6, className: "text-foreground", trim: "normal", children: "No valid combinations available" }),
18782
- /* @__PURE__ */ jsxRuntime.jsx(Text, { size: 2, className: "mt-3", children: "No AAA-compliant foreground options are available for this background." })
18783
- ] }) }) })
20345
+ /* @__PURE__ */ jsxRuntime.jsx("section", { className: "h-full w-full shrink-0 overflow-y-auto px-5 py-5", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6 pr-1", children: [
20346
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-sm border border-grey-200 bg-white px-4 py-4", children: [
20347
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
20348
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 4, size: 6, className: "text-foreground", trim: "normal", children: "Current configuration" }),
20349
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: 2, children: "The drawer only sets the palette, families, and background. The result panel is where the pairing is shown and, if needed, overridden." })
20350
+ ] }),
20351
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-5 space-y-3", children: [
20352
+ /* @__PURE__ */ jsxRuntime.jsx(
20353
+ DrawerSummaryItem,
20354
+ {
20355
+ label: "Palette",
20356
+ value: themeCategory === "brand" ? "Brand palette" : "Aboriginal palette"
20357
+ }
20358
+ ),
20359
+ /* @__PURE__ */ jsxRuntime.jsx(DrawerSummaryItem, { label: "Primary family", value: context.primary.label }),
20360
+ /* @__PURE__ */ jsxRuntime.jsx(DrawerSummaryItem, { label: "Accent family", value: context.accent.label }),
20361
+ /* @__PURE__ */ jsxRuntime.jsx(
20362
+ DrawerSummaryItem,
20363
+ {
20364
+ label: "Background",
20365
+ value: getPairingColorDisplayName4(selectedBackground)
20366
+ }
20367
+ )
20368
+ ] })
20369
+ ] }),
20370
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-sm border border-grey-200 bg-grey-50 px-4 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
20371
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 4, size: 6, className: "text-foreground", trim: "normal", children: "Review the result on the right" }),
20372
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: 2, children: "The right panel always shows the current NSW default pairing for this selection. Alternative AAA options stay there as a secondary override path." })
20373
+ ] }) })
20374
+ ] }) })
18784
20375
  ]
18785
20376
  }
18786
20377
  ) }),
18787
20378
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-grey-200 px-5 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [
18788
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-10 flex-1", children: isLastDrawerStep ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Select a combination to update the result immediately." }) : null }),
20379
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-10 flex-1", children: isLastDrawerStep ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Review the NSW recommendation in the result panel. Use alternatives there only if you need a different approved treatment." }) : null }),
18789
20380
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full items-center gap-2 sm:w-auto sm:flex-none", children: [
18790
20381
  isOverlay && isLastDrawerStep ? /* @__PURE__ */ jsxRuntime.jsx(
18791
20382
  Button2,
@@ -18810,7 +20401,7 @@ function ColorPairingToolV3Content({
18810
20401
  "data-slot": "tool-sidebar",
18811
20402
  className: "hidden xl:sticky xl:top-[var(--tool-sidebar-top)] xl:block xl:self-start",
18812
20403
  style: desktopSidebarStyle,
18813
- children: /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "h-[40rem] max-h-[40rem] gap-0 overflow-hidden py-0 sm:h-[44rem] sm:max-h-[44rem] xl:h-[var(--tool-sidebar-max-height)] xl:max-h-[var(--tool-sidebar-max-height)]", children: renderControlsPanel() })
20404
+ children: /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "h-[40rem] max-h-[40rem] gap-0 overflow-hidden border-grey-200 bg-grey-50/60 py-0 sm:h-[44rem] sm:max-h-[44rem] xl:h-[var(--tool-sidebar-max-height)] xl:max-h-[var(--tool-sidebar-max-height)]", children: renderControlsPanel() })
18814
20405
  }
18815
20406
  ),
18816
20407
  /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-slot": "tool-results", className: "space-y-6", children: [
@@ -18818,7 +20409,7 @@ function ColorPairingToolV3Content({
18818
20409
  /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "gap-4 px-5 py-5", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between", children: [
18819
20410
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
18820
20411
  /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 2, size: 5, className: "text-foreground", trim: "normal", children: "Configuration" }),
18821
- /* @__PURE__ */ jsxRuntime.jsx(Text, { size: 2, children: "Open the colour pairing drawer to change palette, background, and recommended combinations." }),
20412
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: 2, children: "Open the drawer to change palette, colour families, or background. The result panel always shows the current NSW pairing for this selection." }),
18822
20413
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: compactControlsSummary })
18823
20414
  ] }),
18824
20415
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -18850,49 +20441,78 @@ function ColorPairingToolV3Content({
18850
20441
  /* @__PURE__ */ jsxRuntime.jsxs("section", { ref: resultSectionRef, className: "space-y-6", "aria-label": "Current result", children: [
18851
20442
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sr-only", "aria-live": "polite", children: liveAnnouncement }),
18852
20443
  /* @__PURE__ */ jsxRuntime.jsx(
18853
- CurrentResultCard,
20444
+ CurrentResultCard2,
18854
20445
  {
18855
20446
  bestPair: bestRecommendedPair,
18856
- familySummary,
20447
+ copiedKey,
20448
+ onCopyPairing: () => previewPair ? copyValue(
20449
+ "current-pairing",
20450
+ getPairingCopyText2(previewPair),
20451
+ "Pairing copied",
20452
+ previewPair.id === bestRecommendedPair?.id ? {
20453
+ name: "best_recommendation_copied",
20454
+ ...buildAnalyticsContext({
20455
+ foregroundToken: previewPair.foreground.token,
20456
+ pairId: previewPair.id,
20457
+ source: "current-result"
20458
+ })
20459
+ } : void 0
20460
+ ) : void 0,
18857
20461
  pair: previewPair,
20462
+ supportText: currentPairSupportText,
18858
20463
  selectedBackground
18859
20464
  }
18860
20465
  )
18861
20466
  ] }),
18862
- bestRecommendedPair ? /* @__PURE__ */ jsxRuntime.jsx("section", { className: "space-y-4", children: /* @__PURE__ */ jsxRuntime.jsx(
18863
- BestRecommendationCard,
18864
- {
18865
- copiedKey,
18866
- isCurrentSelection: selectedPair?.id === bestRecommendedPair.id,
18867
- pair: bestRecommendedPair,
18868
- reason: getBestRecommendationReason(bestRecommendedPair, context),
18869
- onCopyPairing: () => copyValue(
18870
- "best-pairing",
18871
- getPairingCopyText(bestRecommendedPair),
18872
- "Pairing copied",
18873
- {
18874
- name: "best_recommendation_copied",
18875
- ...buildAnalyticsContext({
18876
- foregroundToken: bestRecommendedPair.foreground.token,
18877
- pairId: bestRecommendedPair.id,
18878
- source: "best-recommendation"
18879
- })
18880
- }
18881
- ),
18882
- onUsePairing: () => handlePairChange(bestRecommendedPair.id, "best-recommendation")
18883
- }
18884
- ) }) : /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "px-6 py-6", children: [
18885
- /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 2, size: 5, className: "text-foreground", trim: "normal", children: "Best recommended pairing" }),
18886
- /* @__PURE__ */ jsxRuntime.jsx(Text, { size: 2, className: "mt-3", children: "No AAA-compliant foreground options available for this selection." })
18887
- ] }),
18888
- /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-4", children: [
20467
+ /* @__PURE__ */ jsxRuntime.jsx("section", { children: /* @__PURE__ */ jsxRuntime.jsx(AccessibilitySummaryCard, { pair: previewPair }) }),
20468
+ bestRecommendedPair || alternativeRecommendationGroups.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-6", children: [
18889
20469
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
20470
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 2, size: 5, className: "text-foreground", trim: "normal", children: "Alternative AAA options" }),
20471
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: 2, children: "These approved alternatives can be used when a different tone, stronger emphasis, or a more neutral system treatment is needed." })
20472
+ ] }),
20473
+ alternativeRecommendationGroups.length > 0 ? alternativeRecommendationGroups.map((group) => /* @__PURE__ */ jsxRuntime.jsx(
20474
+ RecommendationGroupSection,
20475
+ {
20476
+ title: group.title,
20477
+ description: group.description,
20478
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: group.items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
20479
+ RecommendationCard2,
20480
+ {
20481
+ copiedKey,
20482
+ item,
20483
+ isSelected: item.pair.id === selectedPair?.id,
20484
+ onCopyPairing: () => copyValue(
20485
+ `pair:${item.pair.id}`,
20486
+ getPairingCopyText2(item.pair),
20487
+ "Pairing copied",
20488
+ item.pair.id === bestRecommendedPair?.id ? {
20489
+ name: "best_recommendation_copied",
20490
+ ...buildAnalyticsContext({
20491
+ foregroundToken: item.pair.foreground.token,
20492
+ pairId: item.pair.id,
20493
+ source: "recommendations-list"
20494
+ })
20495
+ } : void 0
20496
+ ),
20497
+ onSelect: () => handlePairChange(item.pair.id, "recommendations-list")
20498
+ },
20499
+ item.pair.id
20500
+ )) })
20501
+ },
20502
+ group.id
20503
+ )) : /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "px-6 py-6", children: [
20504
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 3, size: 6, className: "text-foreground", trim: "normal", children: "No other AAA alternatives" }),
20505
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { size: 2, className: "mt-3", children: "The NSW recommended pairing is the only AAA-compliant foreground option for this background." })
20506
+ ] })
20507
+ ] }) : null,
20508
+ /* @__PURE__ */ jsxRuntime.jsx("section", { ref: technicalDetailsSectionRef, className: "space-y-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-sm border border-grey-200 bg-white", children: [
20509
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 border-b border-grey-200 px-4 py-4 sm:px-5", children: [
18890
20510
  /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 2, size: 5, className: "text-foreground", trim: "normal", children: "Technical colour values" }),
18891
20511
  /* @__PURE__ */ jsxRuntime.jsx(Text, { size: 2, children: "Token, tone, HEX, RGB, HSL, and OKLCH values for the current selection." })
18892
20512
  ] }),
18893
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [
20513
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-4 sm:px-5", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [
18894
20514
  /* @__PURE__ */ jsxRuntime.jsx(
18895
- TechnicalDetailsPanel,
20515
+ TechnicalDetailsPanel2,
18896
20516
  {
18897
20517
  title: "Background values",
18898
20518
  color: selectedBackground,
@@ -18902,7 +20522,7 @@ function ColorPairingToolV3Content({
18902
20522
  }
18903
20523
  ),
18904
20524
  /* @__PURE__ */ jsxRuntime.jsx(
18905
- TechnicalDetailsPanel,
20525
+ TechnicalDetailsPanel2,
18906
20526
  {
18907
20527
  title: "Foreground values",
18908
20528
  color: detailForeground,
@@ -18911,51 +20531,51 @@ function ColorPairingToolV3Content({
18911
20531
  onCopyValue: (copyKey, value, toastLabel) => copyValue(copyKey, value, toastLabel)
18912
20532
  }
18913
20533
  )
18914
- ] }),
18915
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
18916
- /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 3, size: 6, className: "mb-3 text-foreground", trim: "normal", children: "Share colour pairing" }),
18917
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-sm border border-grey-200 bg-white p-4", children: [
18918
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start gap-3 sm:flex-row sm:items-start sm:justify-between", children: [
18919
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Copy this URL to share your current colour pairing:" }),
18920
- /* @__PURE__ */ jsxRuntime.jsx(
18921
- Button2,
18922
- {
18923
- variant: "ghost",
18924
- color: "grey",
18925
- size: "sm",
18926
- className: "shrink-0",
18927
- onClick: () => copyValue(
18928
- "share-url",
18929
- typeof window === "undefined" ? shareUrl : new URL(shareUrl, window.location.origin).toString(),
18930
- "Colour pairing link copied"
18931
- ),
18932
- "aria-label": copiedKey === "share-url" ? "Colour pairing URL copied to clipboard" : "Copy colour pairing URL to clipboard",
18933
- title: copiedKey === "share-url" ? "Copied" : "Copy URL",
18934
- children: copiedKey === "share-url" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
18935
- /* @__PURE__ */ jsxRuntime.jsx(Icons.check, { "data-slot": "icon", className: "size-5" }),
18936
- "Copied"
18937
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
18938
- /* @__PURE__ */ jsxRuntime.jsx(Icons.content_copy, { "data-slot": "icon", className: "size-5" }),
18939
- "Copy URL"
18940
- ] })
18941
- }
18942
- )
18943
- ] }),
18944
- /* @__PURE__ */ jsxRuntime.jsx("code", { className: "mt-3 block w-full rounded-sm border border-grey-200 bg-background px-3 py-2 font-mono text-[11px] break-all text-foreground sm:text-xs", children: shareUrl })
18945
- ] })
20534
+ ] }) })
20535
+ ] }) }),
20536
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-3", children: [
20537
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 3, size: 6, className: "mb-4 text-foreground", trim: "normal", children: "Share colour pairing" }),
20538
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-sm border border-grey-200 bg-white p-4", children: [
20539
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-row items-center justify-between", children: [
20540
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Copy this URL to share your current colour pairing:" }),
20541
+ /* @__PURE__ */ jsxRuntime.jsx(
20542
+ Button2,
20543
+ {
20544
+ variant: "ghost",
20545
+ color: "grey",
20546
+ size: "sm",
20547
+ className: "shrink-0 self-center",
20548
+ onClick: () => copyValue(
20549
+ "share-url",
20550
+ typeof window === "undefined" ? shareUrl : new URL(shareUrl, window.location.origin).toString(),
20551
+ "Colour pairing link copied"
20552
+ ),
20553
+ "aria-label": copiedKey === "share-url" ? "Colour pairing URL copied to clipboard" : "Copy colour pairing URL to clipboard",
20554
+ title: copiedKey === "share-url" ? "Copied" : "Copy URL",
20555
+ children: copiedKey === "share-url" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
20556
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.check, { "data-slot": "icon", className: "size-5" }),
20557
+ "Copied"
20558
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
20559
+ /* @__PURE__ */ jsxRuntime.jsx(Icons.content_copy, { "data-slot": "icon", className: "size-5" }),
20560
+ "Copy URL"
20561
+ ] })
20562
+ }
20563
+ )
20564
+ ] }),
20565
+ /* @__PURE__ */ jsxRuntime.jsx("code", { className: "mt-3 block w-full rounded-sm border border-grey-200 bg-background px-3 py-2 font-mono text-[11px] break-all text-foreground sm:text-xs", children: shareUrl })
18946
20566
  ] })
18947
20567
  ] })
18948
20568
  ] })
18949
20569
  ] });
18950
20570
  }
18951
- function ColorPairingToolV3({
20571
+ function ColorPairingToolV4({
18952
20572
  onAnalyticsEvent = () => {
18953
20573
  },
18954
- visibleFormats = DEFAULT_VISIBLE_FORMATS3
20574
+ visibleFormats = DEFAULT_VISIBLE_FORMATS4
18955
20575
  } = {}) {
18956
20576
  const normalizedVisibleFormats = [...new Set(visibleFormats)];
18957
- return /* @__PURE__ */ jsxRuntime.jsx(React5.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(ColorPairingToolV3Loading, {}), children: /* @__PURE__ */ jsxRuntime.jsx(
18958
- ColorPairingToolV3Content,
20577
+ return /* @__PURE__ */ jsxRuntime.jsx(React5.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(ColorPairingToolV4Loading, {}), children: /* @__PURE__ */ jsxRuntime.jsx(
20578
+ ColorPairingToolV4Content,
18959
20579
  {
18960
20580
  onAnalyticsEvent,
18961
20581
  visibleFormats: normalizedVisibleFormats
@@ -21586,7 +23206,7 @@ function FormatToggle({ format, setFormat }) {
21586
23206
 
21587
23207
  // package.json
21588
23208
  var package_default = {
21589
- version: "1.103.0"};
23209
+ version: "1.104.0"};
21590
23210
  function Logo(props) {
21591
23211
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
21592
23212
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "NSW Government" }),
@@ -37349,8 +38969,20 @@ function getHeadings(slugify$1 = slugify.slugifyWithCounter()) {
37349
38969
  });
37350
38970
  return result;
37351
38971
  }
38972
+ function areHeadingNodesEqual(left, right) {
38973
+ if (left.level !== right.level || left.id !== right.id || left.title !== right.title) {
38974
+ return false;
38975
+ }
38976
+ if (left.children.length !== right.children.length) {
38977
+ return false;
38978
+ }
38979
+ return left.children.every((child, index) => areHeadingNodesEqual(child, right.children[index]));
38980
+ }
37352
38981
  function areHeadingTreesEqual(left, right) {
37353
- return JSON.stringify(left) === JSON.stringify(right);
38982
+ if (left.length !== right.length) {
38983
+ return false;
38984
+ }
38985
+ return left.every((heading, index) => areHeadingNodesEqual(heading, right[index]));
37354
38986
  }
37355
38987
  function usePageHeadings(enabled = true) {
37356
38988
  const [headings, setHeadings] = React5.useState([]);
@@ -37557,6 +39189,7 @@ exports.ColorCard = ColorCard;
37557
39189
  exports.ColorPairingTool = ColorPairingTool;
37558
39190
  exports.ColorPairingToolV2 = ColorPairingToolV2;
37559
39191
  exports.ColorPairingToolV3 = ColorPairingToolV3;
39192
+ exports.ColorPairingToolV4 = ColorPairingToolV4;
37560
39193
  exports.ColorSwatches = ColorSwatches;
37561
39194
  exports.ColourScale = ColourScale;
37562
39195
  exports.ComboChart = ComboChart;