@bearmenu/ui 0.8.9 → 0.8.10

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.
Files changed (178) hide show
  1. package/dist/chunk-24RFI4RQ.js +167 -0
  2. package/dist/chunk-2XCZJWTV.js +121 -0
  3. package/dist/chunk-5RWTOT5Y.js +72 -0
  4. package/dist/{chunk-AXV5O2IZ.js → chunk-AKQ57NXY.js} +3 -3
  5. package/dist/chunk-CLLY6XB3.js +41 -0
  6. package/dist/chunk-DTXNDLSE.js +125 -0
  7. package/dist/chunk-F6FMZJDG.js +113 -0
  8. package/dist/{chunk-2WDJR6K7.js → chunk-G7IFL4Z4.js} +4 -1
  9. package/dist/chunk-MZL7CGCL.js +109 -0
  10. package/dist/chunk-SY6RV476.js +48 -0
  11. package/dist/chunk-UDN7UHJW.js +60 -0
  12. package/dist/{chunk-TT3WFS6S.js → chunk-YOKHBAIH.js} +11 -11
  13. package/dist/components/badge.d.ts +1 -1
  14. package/dist/components/button.d.ts +1 -1
  15. package/dist/components/command.d.ts +7 -7
  16. package/dist/components/concept-compare-rail.d.ts +50 -0
  17. package/dist/components/concept-compare-rail.js +5 -0
  18. package/dist/components/concept-compare.d.ts +75 -38
  19. package/dist/components/concept-compare.js +209 -133
  20. package/dist/components/concept-convergence.d.ts +41 -0
  21. package/dist/components/concept-convergence.js +53 -0
  22. package/dist/components/convergence.d.ts +25 -0
  23. package/dist/components/convergence.js +4 -0
  24. package/dist/components/date-input.d.ts +1 -1
  25. package/dist/components/event-card.js +1 -1
  26. package/dist/components/event-compact-row.js +1 -1
  27. package/dist/components/event-detail-panel.js +1 -1
  28. package/dist/components/event-index-row.js +1 -1
  29. package/dist/components/event-list-row-wide.js +1 -1
  30. package/dist/components/event-list-row.js +2 -2
  31. package/dist/components/explainer-steps.d.ts +34 -0
  32. package/dist/components/explainer-steps.js +36 -0
  33. package/dist/components/glass-ground.d.ts +66 -0
  34. package/dist/components/glass-ground.js +3 -0
  35. package/dist/components/hub-surface.js +11 -1
  36. package/dist/components/hue-chip.d.ts +70 -0
  37. package/dist/components/hue-chip.js +3 -0
  38. package/dist/components/kitchen-dossier.d.ts +88 -0
  39. package/dist/components/kitchen-dossier.js +187 -0
  40. package/dist/components/menu-dead-end.d.ts +56 -0
  41. package/dist/components/menu-dead-end.js +49 -0
  42. package/dist/components/menu-family-block.d.ts +99 -0
  43. package/dist/components/menu-family-block.js +155 -0
  44. package/dist/components/menu-family-card.d.ts +33 -27
  45. package/dist/components/menu-family-card.js +43 -85
  46. package/dist/components/menu-field.js +92 -2
  47. package/dist/components/menu-filter-chips.d.ts +86 -0
  48. package/dist/components/menu-filter-chips.js +122 -0
  49. package/dist/components/menu-group-rows.d.ts +53 -0
  50. package/dist/components/menu-group-rows.js +67 -0
  51. package/dist/components/menu-index-header.d.ts +37 -0
  52. package/dist/components/menu-index-header.js +68 -0
  53. package/dist/components/menu-index-list.d.ts +48 -0
  54. package/dist/components/menu-index-list.js +96 -0
  55. package/dist/components/menu-intent-hero.d.ts +69 -0
  56. package/dist/components/menu-intent-hero.js +87 -0
  57. package/dist/components/menu-kitchen-list.d.ts +82 -0
  58. package/dist/components/menu-kitchen-list.js +5 -0
  59. package/dist/components/menu-mocks.d.ts +37 -2
  60. package/dist/components/menu-mocks.js +152 -27
  61. package/dist/components/menu-types.d.ts +182 -6
  62. package/dist/components/motion-icon-controls.js +2 -2
  63. package/dist/components/motion-icon-set.js +6 -6
  64. package/dist/components/motion-icon.d.ts +22 -1
  65. package/dist/components/motion-icon.js +1 -1
  66. package/dist/components/photo-ground.d.ts +64 -0
  67. package/dist/components/photo-ground.js +3 -0
  68. package/dist/components/place-about-tab.js +2 -2
  69. package/dist/components/place-details-mobile.js +2 -2
  70. package/dist/components/place-types.d.ts +2 -2
  71. package/dist/components/qa-block.d.ts +36 -0
  72. package/dist/components/qa-block.js +24 -0
  73. package/dist/components/scroll-fade.js +3 -121
  74. package/dist/components/snap-rail.d.ts +24 -1
  75. package/dist/components/snap-rail.js +3 -109
  76. package/dist/components/spinner.d.ts +2 -2
  77. package/dist/components/story.d.ts +34 -0
  78. package/dist/components/story.js +3 -0
  79. package/dist/components/text.d.ts +3 -3
  80. package/dist/components/top-comparison.d.ts +28 -0
  81. package/dist/components/top-comparison.js +76 -0
  82. package/dist/components/top-cross-link.d.ts +34 -0
  83. package/dist/components/top-cross-link.js +101 -0
  84. package/dist/components/top-ranking-entry.d.ts +53 -0
  85. package/dist/components/top-ranking-entry.js +217 -0
  86. package/dist/components/top-ranking-hero.d.ts +61 -0
  87. package/dist/components/top-ranking-hero.js +99 -0
  88. package/dist/components/top-ranking-legend.d.ts +69 -0
  89. package/dist/components/top-ranking-legend.js +124 -0
  90. package/dist/components/top-types.d.ts +109 -0
  91. package/dist/components/top-types.js +1 -0
  92. package/dist/components/venue-grade-dimensions.d.ts +3 -1
  93. package/dist/components/venue-grade-dimensions.js +1 -1
  94. package/dist/components/weekend-panel.js +2 -2
  95. package/package.json +1 -1
  96. package/src/components/concept-compare-rail.tsx +131 -0
  97. package/src/components/concept-compare.stories.tsx +122 -54
  98. package/src/components/concept-compare.tsx +363 -196
  99. package/src/components/concept-convergence.stories.tsx +53 -0
  100. package/src/components/concept-convergence.tsx +90 -0
  101. package/src/components/convergence.stories.tsx +54 -0
  102. package/src/components/convergence.tsx +177 -0
  103. package/src/components/explainer-steps.stories.tsx +46 -0
  104. package/src/components/explainer-steps.tsx +74 -0
  105. package/src/components/glass-ground.tsx +129 -0
  106. package/src/components/hub-surface.tsx +11 -1
  107. package/src/components/hue-chip.stories.tsx +76 -0
  108. package/src/components/hue-chip.test.tsx +62 -0
  109. package/src/components/hue-chip.tsx +99 -0
  110. package/src/components/kitchen-dossier.stories.tsx +108 -0
  111. package/src/components/kitchen-dossier.tsx +322 -0
  112. package/src/components/menu-dead-end.stories.tsx +67 -0
  113. package/src/components/menu-dead-end.tsx +127 -0
  114. package/src/components/menu-family-block.stories.tsx +91 -0
  115. package/src/components/menu-family-block.tsx +314 -0
  116. package/src/components/menu-family-card.stories.tsx +43 -35
  117. package/src/components/menu-family-card.tsx +122 -142
  118. package/src/components/menu-filter-chips.stories.tsx +107 -0
  119. package/src/components/menu-filter-chips.tsx +238 -0
  120. package/src/components/menu-group-rows.stories.tsx +54 -0
  121. package/src/components/menu-group-rows.tsx +133 -0
  122. package/src/components/menu-index-header.stories.tsx +72 -0
  123. package/src/components/menu-index-header.tsx +125 -0
  124. package/src/components/menu-index-list.stories.tsx +50 -0
  125. package/src/components/menu-index-list.tsx +149 -0
  126. package/src/components/menu-intent-hero.stories.tsx +67 -0
  127. package/src/components/menu-intent-hero.tsx +189 -0
  128. package/src/components/menu-kitchen-list.stories.tsx +67 -0
  129. package/src/components/menu-kitchen-list.test.tsx +78 -0
  130. package/src/components/menu-kitchen-list.tsx +284 -0
  131. package/src/components/menu-mocks.ts +198 -23
  132. package/src/components/menu-types.ts +189 -6
  133. package/src/components/motion-icon-controls.tsx +11 -11
  134. package/src/components/motion-icon-set.tsx +6 -5
  135. package/src/components/motion-icon.test.tsx +23 -1
  136. package/src/components/motion-icon.tsx +20 -0
  137. package/src/components/photo-ground.tsx +147 -0
  138. package/src/components/place-types.ts +2 -2
  139. package/src/components/qa-block.stories.tsx +43 -0
  140. package/src/components/qa-block.tsx +60 -0
  141. package/src/components/snap-rail.tsx +58 -15
  142. package/src/components/story.tsx +225 -0
  143. package/src/components/top-comparison.stories.tsx +78 -0
  144. package/src/components/top-comparison.tsx +151 -0
  145. package/src/components/top-cross-link.stories.tsx +84 -0
  146. package/src/components/top-cross-link.tsx +171 -0
  147. package/src/components/top-ranking-entry.stories.tsx +171 -0
  148. package/src/components/top-ranking-entry.test.tsx +105 -0
  149. package/src/components/top-ranking-entry.tsx +425 -0
  150. package/src/components/top-ranking-hero.stories.tsx +76 -0
  151. package/src/components/top-ranking-hero.tsx +218 -0
  152. package/src/components/top-ranking-legend.stories.tsx +78 -0
  153. package/src/components/top-ranking-legend.tsx +239 -0
  154. package/src/components/top-types.ts +110 -0
  155. package/src/components/venue-grade-dimensions.tsx +2 -2
  156. package/dist/chunk-THOVPZNN.js +0 -37
  157. package/dist/chunk-YS6BHKZO.js +0 -92
  158. package/dist/components/ranked-venue-list.d.ts +0 -49
  159. package/dist/components/ranked-venue-list.js +0 -87
  160. package/dist/components/signature-line-list.d.ts +0 -45
  161. package/dist/components/signature-line-list.js +0 -99
  162. package/dist/components/venue-busyness-strip.d.ts +0 -35
  163. package/dist/components/venue-busyness-strip.js +0 -37
  164. package/dist/components/venue-signature-block.d.ts +0 -39
  165. package/dist/components/venue-signature-block.js +0 -6
  166. package/dist/components/venue-spotlight.d.ts +0 -74
  167. package/dist/components/venue-spotlight.js +0 -138
  168. package/src/components/ranked-venue-list.stories.tsx +0 -80
  169. package/src/components/ranked-venue-list.tsx +0 -177
  170. package/src/components/signature-line-list.stories.tsx +0 -85
  171. package/src/components/signature-line-list.tsx +0 -136
  172. package/src/components/venue-busyness-strip.stories.tsx +0 -66
  173. package/src/components/venue-busyness-strip.tsx +0 -64
  174. package/src/components/venue-signature-block.stories.tsx +0 -68
  175. package/src/components/venue-signature-block.tsx +0 -71
  176. package/src/components/venue-spotlight.stories.tsx +0 -141
  177. package/src/components/venue-spotlight.tsx +0 -236
  178. package/dist/{chunk-XWMRABZW.js → chunk-HSIYYA2E.js} +1 -1
@@ -0,0 +1,109 @@
1
+ import { stagger, ease, duration, springSnappy } from './chunk-LEAYBICI.js';
2
+ import { cn } from './chunk-FEK5XGZW.js';
3
+ import { __spreadValues, __spreadProps } from './chunk-3GWWZKX7.js';
4
+ import { motion, useReducedMotion } from 'framer-motion';
5
+ import { jsx, jsxs } from 'react/jsx-runtime';
6
+
7
+ var LINE_LEAD = 0.15;
8
+ var TARGET_DELAY = 0.38;
9
+ var sourcesContainer = {
10
+ hidden: {},
11
+ show: { transition: { delayChildren: 0.06, staggerChildren: stagger.tight } }
12
+ };
13
+ var sourceItem = {
14
+ hidden: { opacity: 0, x: -8 },
15
+ show: { opacity: 1, x: 0, transition: { duration: duration.quick, ease: ease.enter } }
16
+ };
17
+ var targetItem = {
18
+ hidden: { opacity: 0, scale: 0.92 },
19
+ show: { opacity: 1, scale: 1, transition: __spreadValues({ delay: TARGET_DELAY }, springSnappy) }
20
+ };
21
+ var LINES_WIDTH = 40;
22
+ var LINES_HEIGHT = 100;
23
+ function ConvergeLines({ count, animate = true, className }) {
24
+ const mid = LINES_HEIGHT / 2;
25
+ return /* @__PURE__ */ jsx(
26
+ "svg",
27
+ {
28
+ "aria-hidden": true,
29
+ viewBox: `0 0 ${LINES_WIDTH} ${LINES_HEIGHT}`,
30
+ preserveAspectRatio: "none",
31
+ className: cn("h-full w-10 text-border", className),
32
+ children: Array.from({ length: count }, (_, index) => {
33
+ const y = (index + 0.5) / count * LINES_HEIGHT;
34
+ const d = `M0 ${y} C ${LINES_WIDTH / 2} ${y}, ${LINES_WIDTH / 2} ${mid}, ${LINES_WIDTH} ${mid}`;
35
+ const stroke = {
36
+ d,
37
+ fill: "none",
38
+ stroke: "currentColor",
39
+ strokeWidth: 1.5,
40
+ strokeLinecap: "round",
41
+ vectorEffect: "non-scaling-stroke"
42
+ };
43
+ return animate ? /* @__PURE__ */ jsx(
44
+ motion.path,
45
+ __spreadProps(__spreadValues({}, stroke), {
46
+ initial: { pathLength: 0, opacity: 0 },
47
+ animate: { pathLength: 1, opacity: 1 },
48
+ transition: {
49
+ delay: LINE_LEAD + index * stagger.tight,
50
+ duration: duration.standard,
51
+ ease: ease.standard
52
+ }
53
+ }),
54
+ index
55
+ ) : /* @__PURE__ */ jsx("path", __spreadValues({}, stroke), index);
56
+ })
57
+ }
58
+ );
59
+ }
60
+ function Convergence({
61
+ sources,
62
+ target,
63
+ animate = true,
64
+ linesClassName,
65
+ className
66
+ }) {
67
+ const reduce = useReducedMotion();
68
+ const run = animate && !reduce;
69
+ const fadeOnly = animate && reduce;
70
+ return /* @__PURE__ */ jsxs(
71
+ motion.div,
72
+ {
73
+ className: cn("grid grid-cols-[minmax(0,1fr)_auto_auto] items-center", className),
74
+ initial: fadeOnly ? { opacity: 0 } : false,
75
+ animate: fadeOnly ? { opacity: 1 } : void 0,
76
+ transition: { duration: duration.quick, ease: ease.standard },
77
+ children: [
78
+ /* @__PURE__ */ jsx(
79
+ motion.div,
80
+ {
81
+ className: cn(
82
+ "flex min-w-0 flex-col items-start justify-center gap-2 py-1",
83
+ /* The fan: the middle sources sit a little further right than the
84
+ ends, so five chips read as one hand of cards, not as a menu. */
85
+ "[&>*:nth-child(2)]:translate-x-1.5 [&>*:nth-child(3)]:translate-x-3 [&>*:nth-child(4)]:translate-x-1.5"
86
+ ),
87
+ variants: run ? sourcesContainer : void 0,
88
+ initial: run ? "hidden" : false,
89
+ animate: run ? "show" : void 0,
90
+ children: sources.map((node, index) => /* @__PURE__ */ jsx(motion.div, { className: "flex max-w-full", variants: run ? sourceItem : void 0, children: node }, index))
91
+ }
92
+ ),
93
+ /* @__PURE__ */ jsx("div", { className: "self-stretch", children: /* @__PURE__ */ jsx(ConvergeLines, { count: sources.length, animate: run, className: linesClassName }) }),
94
+ /* @__PURE__ */ jsx(
95
+ motion.div,
96
+ {
97
+ className: "flex items-center",
98
+ variants: run ? targetItem : void 0,
99
+ initial: run ? "hidden" : false,
100
+ animate: run ? "show" : void 0,
101
+ children: target
102
+ }
103
+ )
104
+ ]
105
+ }
106
+ );
107
+ }
108
+
109
+ export { ConvergeLines, Convergence };
@@ -0,0 +1,48 @@
1
+ import { cn } from './chunk-FEK5XGZW.js';
2
+ import { __spreadProps, __spreadValues } from './chunk-3GWWZKX7.js';
3
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
+
5
+ var STOPS = [0, 0.25, 0.5, 0.75, 1];
6
+ var EASE = [0, 0.06, 0.25, 0.56, 1];
7
+ var FROST_SOFT = "[mask-image:linear-gradient(var(--glass-dir),transparent_var(--glass-s0),rgba(0,0,0,0.25)_var(--glass-s1),black_var(--glass-s2))] lg:[mask-image:linear-gradient(var(--glass-lg-dir),transparent_var(--glass-lg-s0),rgba(0,0,0,0.25)_var(--glass-lg-s1),black_var(--glass-lg-s2))]";
8
+ var FROST_HEAVY = "[mask-image:linear-gradient(var(--glass-dir),transparent_var(--glass-s1),rgba(0,0,0,0.25)_var(--glass-s2),rgba(0,0,0,0.6)_var(--glass-s3),black_var(--glass-s4))] lg:[mask-image:linear-gradient(var(--glass-lg-dir),transparent_var(--glass-lg-s1),rgba(0,0,0,0.25)_var(--glass-lg-s2),rgba(0,0,0,0.6)_var(--glass-lg-s3),black_var(--glass-lg-s4))]";
9
+ var INK = "bg-[linear-gradient(var(--glass-dir),oklch(0.16_0.012_55_/_0)_var(--glass-s0),oklch(0.16_0.012_55_/_var(--glass-a1))_var(--glass-s1),oklch(0.16_0.012_55_/_var(--glass-a2))_var(--glass-s2),oklch(0.16_0.012_55_/_var(--glass-a3))_var(--glass-s3),oklch(0.16_0.012_55_/_var(--glass-a4))_var(--glass-s4),oklch(0.16_0.012_55_/_var(--glass-a-peak))_100%)] lg:bg-[linear-gradient(var(--glass-lg-dir),oklch(0.16_0.012_55_/_0)_var(--glass-lg-s0),oklch(0.16_0.012_55_/_var(--glass-a1))_var(--glass-lg-s1),oklch(0.16_0.012_55_/_var(--glass-a2))_var(--glass-lg-s2),oklch(0.16_0.012_55_/_var(--glass-a3))_var(--glass-lg-s3),oklch(0.16_0.012_55_/_var(--glass-a4))_var(--glass-lg-s4),oklch(0.16_0.012_55_/_var(--glass-a-peak))_100%)]";
10
+ var BLUR = {
11
+ sm: ["backdrop-blur-[2px]", "backdrop-blur-sm"],
12
+ md: ["backdrop-blur-sm", "backdrop-blur-md"],
13
+ lg: ["backdrop-blur-md", "backdrop-blur-lg"],
14
+ xl: ["backdrop-blur-lg", "backdrop-blur-xl"]
15
+ };
16
+ function measure(value) {
17
+ const match = value.trim().match(/^(-?\d*\.?\d+)\s*([a-z%]*)$/i);
18
+ if (!match) throw new Error(`GlassGround: cannot read stop "${value}"`);
19
+ return [Number(match[1]), match[2] || "px"];
20
+ }
21
+ function positions(clear, full) {
22
+ const [from, unit] = measure(clear);
23
+ const [to] = measure(full);
24
+ return STOPS.map((fraction) => `${from + (to - from) * fraction}${unit}`);
25
+ }
26
+ function GlassGround({
27
+ stops,
28
+ lgStops = stops,
29
+ peak = 0.96,
30
+ blur = "xl",
31
+ className
32
+ }) {
33
+ const base = positions(stops.clear, stops.full);
34
+ const lg = positions(lgStops.clear, lgStops.full);
35
+ const style = __spreadProps(__spreadValues(__spreadValues(__spreadValues({
36
+ "--glass-dir": stops.direction,
37
+ "--glass-lg-dir": lgStops.direction
38
+ }, Object.fromEntries(base.map((position, index) => [`--glass-s${index}`, position]))), Object.fromEntries(lg.map((position, index) => [`--glass-lg-s${index}`, position]))), Object.fromEntries(EASE.slice(1).map((share, index) => [`--glass-a${index + 1}`, String(peak * 0.8 * share)]))), {
39
+ "--glass-a-peak": String(peak)
40
+ });
41
+ return /* @__PURE__ */ jsxs("span", { "aria-hidden": true, className: cn("pointer-events-none absolute inset-0 block", className), style, children: [
42
+ /* @__PURE__ */ jsx("span", { className: cn("absolute inset-0", BLUR[blur][0], FROST_SOFT) }),
43
+ /* @__PURE__ */ jsx("span", { className: cn("absolute inset-0", BLUR[blur][1], FROST_HEAVY) }),
44
+ /* @__PURE__ */ jsx("span", { className: cn("absolute inset-0", INK) })
45
+ ] });
46
+ }
47
+
48
+ export { GlassGround };
@@ -0,0 +1,60 @@
1
+ import { SnapRail, SnapRailDots } from './chunk-DTXNDLSE.js';
2
+ import { PhotoGround } from './chunk-5RWTOT5Y.js';
3
+ import { cn } from './chunk-FEK5XGZW.js';
4
+ import * as React from 'react';
5
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
6
+
7
+ var FOOT_GLASS = cn(
8
+ "before:pointer-events-none before:absolute before:inset-0 before:-z-10 before:content-[''] before:backdrop-blur-lg",
9
+ "before:bg-[oklch(0.16_0.012_55_/_0.9)]",
10
+ "lg:before:[mask-image:linear-gradient(to_top,black_60%,transparent_100%)] lg:before:bg-[linear-gradient(to_top,oklch(0.16_0.012_55_/_0.88)_0%,oklch(0.16_0.012_55_/_0.84)_60%,oklch(0.16_0.012_55_/_0.4)_85%,oklch(0.16_0.012_55_/_0)_100%)]"
11
+ );
12
+ function ConceptCompareRail({
13
+ backdrops,
14
+ railClassName,
15
+ foot,
16
+ children
17
+ }) {
18
+ const [active, setActive] = React.useState(0);
19
+ const count = React.Children.count(children);
20
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
21
+ /* @__PURE__ */ jsx(
22
+ PhotoGround,
23
+ {
24
+ className: "-z-10 lg:hidden",
25
+ mode: "stack",
26
+ active,
27
+ photos: backdrops.map((backdrop) => backdrop.src),
28
+ initials: backdrops.map((backdrop) => backdrop.initial),
29
+ overlay: true,
30
+ loading: "eager"
31
+ }
32
+ ),
33
+ /* @__PURE__ */ jsx(
34
+ SnapRail,
35
+ {
36
+ itemWidth: "full",
37
+ gap: "sm",
38
+ onActiveChange: setActive,
39
+ className: cn("pb-0 overscroll-x-contain", railClassName),
40
+ children
41
+ }
42
+ ),
43
+ foot && /* @__PURE__ */ jsxs(
44
+ "div",
45
+ {
46
+ className: cn(
47
+ "relative isolate flex flex-col gap-3 px-4 pb-4 pt-3",
48
+ "lg:absolute lg:inset-x-0 lg:bottom-0 lg:z-10 lg:flex-row lg:items-center lg:gap-5 lg:px-5 lg:pb-4 lg:pt-6",
49
+ FOOT_GLASS
50
+ ),
51
+ children: [
52
+ /* @__PURE__ */ jsx(SnapRailDots, { count, active, className: "lg:hidden" }),
53
+ foot
54
+ ]
55
+ }
56
+ )
57
+ ] });
58
+ }
59
+
60
+ export { ConceptCompareRail };
@@ -1,4 +1,4 @@
1
- import { beats, returnTransition, MotionIcon } from './chunk-2WDJR6K7.js';
1
+ import { beats, returnTransition, MotionIcon, pivot } from './chunk-G7IFL4Z4.js';
2
2
  import { __spreadProps, __spreadValues } from './chunk-3GWWZKX7.js';
3
3
  import { motion } from 'framer-motion';
4
4
  import { jsx, jsxs } from 'react/jsx-runtime';
@@ -19,7 +19,7 @@ function MotionHeart(props) {
19
19
  motion.path,
20
20
  {
21
21
  variants: heartBody,
22
- style: { originX: "12px", originY: "13px" },
22
+ style: pivot(12, 13),
23
23
  fill: "currentColor",
24
24
  fillOpacity: 0,
25
25
  d: "M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5"
@@ -43,7 +43,7 @@ function MotionBookmark(props) {
43
43
  motion.path,
44
44
  {
45
45
  variants: bookmarkBody,
46
- style: { originX: "12px", originY: "21px" },
46
+ style: pivot(12, 21),
47
47
  fill: "currentColor",
48
48
  fillOpacity: 0,
49
49
  d: "M17 3a2 2 0 0 1 2 2v15a1 1 0 0 1-1.496.868l-4.512-2.578a2 2 0 0 0-1.984 0l-4.512 2.578A1 1 0 0 1 5 20V5a2 2 0 0 1 2-2z"
@@ -89,7 +89,7 @@ var found = {
89
89
  }
90
90
  };
91
91
  function MotionSearch(props) {
92
- return /* @__PURE__ */ jsx(MotionIcon, __spreadProps(__spreadValues({}, props), { children: /* @__PURE__ */ jsxs(motion.g, { variants: lens, style: { originX: "21px", originY: "21px" }, children: [
92
+ return /* @__PURE__ */ jsx(MotionIcon, __spreadProps(__spreadValues({}, props), { children: /* @__PURE__ */ jsxs(motion.g, { variants: lens, style: pivot(21, 21), children: [
93
93
  /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "8" }),
94
94
  /* @__PURE__ */ jsx("path", { d: "m21 21-4.34-4.34" }),
95
95
  /* @__PURE__ */ jsx(
@@ -97,7 +97,7 @@ function MotionSearch(props) {
97
97
  {
98
98
  variants: found,
99
99
  opacity: 0,
100
- style: { originX: "11px", originY: "11px" },
100
+ style: pivot(11, 11),
101
101
  fill: "currentColor",
102
102
  cx: "11",
103
103
  cy: "11",
@@ -207,7 +207,7 @@ var crescent = {
207
207
  }
208
208
  };
209
209
  function MotionTheme(props) {
210
- const origin = { originX: "12px", originY: "12px" };
210
+ const origin = pivot(12, 12);
211
211
  return /* @__PURE__ */ jsxs(MotionIcon, __spreadProps(__spreadValues({}, props), { children: [
212
212
  /* @__PURE__ */ jsx(motion.g, { variants: rays, style: origin, children: SUN_RAYS.map((d) => /* @__PURE__ */ jsx("path", { d }, d)) }),
213
213
  /* @__PURE__ */ jsx(motion.circle, { variants: disc, style: origin, cx: "12", cy: "12", r: "4" }),
@@ -232,7 +232,7 @@ var chevron = {
232
232
  }
233
233
  };
234
234
  function MotionChevron(props) {
235
- return /* @__PURE__ */ jsx(MotionIcon, __spreadProps(__spreadValues({}, props), { children: /* @__PURE__ */ jsx(motion.path, { variants: chevron, style: { originX: "12px", originY: "12px" }, d: "m6 9 6 6 6-6" }) }));
235
+ return /* @__PURE__ */ jsx(MotionIcon, __spreadProps(__spreadValues({}, props), { children: /* @__PURE__ */ jsx(motion.path, { variants: chevron, style: pivot(12, 12), d: "m6 9 6 6 6-6" }) }));
236
236
  }
237
237
  var PLAYPAUSE_TIMES = [0, 0.24, 0.72, 1];
238
238
  var playHead = {
@@ -255,7 +255,7 @@ function pauseBar(dx) {
255
255
  var barLeft = pauseBar(5);
256
256
  var barRight = pauseBar(-5);
257
257
  function MotionPlayPause(props) {
258
- const origin = { originX: "12px", originY: "12px" };
258
+ const origin = pivot(12, 12);
259
259
  return /* @__PURE__ */ jsxs(MotionIcon, __spreadProps(__spreadValues({}, props), { children: [
260
260
  /* @__PURE__ */ jsx(
261
261
  motion.path,
@@ -290,7 +290,7 @@ var slash = {
290
290
  }
291
291
  };
292
292
  function MotionEye(props) {
293
- const origin = { originX: "12px", originY: "12px" };
293
+ const origin = pivot(12, 12);
294
294
  return /* @__PURE__ */ jsxs(MotionIcon, __spreadProps(__spreadValues({}, props), { children: [
295
295
  /* @__PURE__ */ jsx(
296
296
  motion.path,
@@ -320,7 +320,7 @@ function MotionStar(props) {
320
320
  motion.path,
321
321
  {
322
322
  variants: starBody,
323
- style: { originX: "12px", originY: "12px" },
323
+ style: pivot(12, 12),
324
324
  fill: "currentColor",
325
325
  fillOpacity: 0,
326
326
  d: "M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z"
@@ -368,7 +368,7 @@ var refreshRing = {
368
368
  }
369
369
  };
370
370
  function MotionRefresh(props) {
371
- return /* @__PURE__ */ jsx(MotionIcon, __spreadProps(__spreadValues({}, props), { children: /* @__PURE__ */ jsxs(motion.g, { variants: refreshRing, style: { originX: "12px", originY: "12px" }, children: [
371
+ return /* @__PURE__ */ jsx(MotionIcon, __spreadProps(__spreadValues({}, props), { children: /* @__PURE__ */ jsxs(motion.g, { variants: refreshRing, style: pivot(12, 12), children: [
372
372
  /* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" }),
373
373
  /* @__PURE__ */ jsx("path", { d: "M21 3v5h-5" }),
374
374
  /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" }),
@@ -4,7 +4,7 @@ import * as React from 'react';
4
4
  import { VariantProps } from 'class-variance-authority';
5
5
 
6
6
  declare const badgeVariants: (props?: ({
7
- variant?: "default" | "secondary" | "destructive" | "outline" | "overlay" | "glass" | "success" | "warning" | "brown" | null | undefined;
7
+ variant?: "default" | "destructive" | "outline" | "secondary" | "glass" | "overlay" | "brown" | "success" | "warning" | null | undefined;
8
8
  live?: boolean | null | undefined;
9
9
  } & class_variance_authority_types.ClassProp) | undefined) => string;
10
10
  interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const buttonVariants: (props?: ({
6
- variant?: "default" | "secondary" | "destructive" | "outline" | "overlay" | "glass" | "brown" | "link" | "ghost" | "antagonist" | null | undefined;
6
+ variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | "glass" | "overlay" | "brown" | "antagonist" | null | undefined;
7
7
  size?: "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
8
8
  fullWidth?: boolean | null | undefined;
9
9
  } & class_variance_authority_types.ClassProp) | undefined) => string;
@@ -8,7 +8,7 @@ declare const Command: React.ForwardRefExoticComponent<Omit<{
8
8
  ref?: React.Ref<HTMLDivElement>;
9
9
  } & {
10
10
  asChild?: boolean;
11
- }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>> & {
11
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
12
12
  label?: string;
13
13
  shouldFilter?: boolean;
14
14
  filter?: (value: string, search: string, keywords?: string[]) => number;
@@ -38,7 +38,7 @@ declare const CommandList: React.ForwardRefExoticComponent<Omit<{
38
38
  ref?: React.Ref<HTMLDivElement>;
39
39
  } & {
40
40
  asChild?: boolean;
41
- }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>> & {
41
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
42
42
  label?: string;
43
43
  } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
44
44
  declare const CommandEmpty: React.ForwardRefExoticComponent<Omit<{
@@ -47,14 +47,14 @@ declare const CommandEmpty: React.ForwardRefExoticComponent<Omit<{
47
47
  ref?: React.Ref<HTMLDivElement>;
48
48
  } & {
49
49
  asChild?: boolean;
50
- }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>> & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
50
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
51
51
  declare const CommandLoading: React.ForwardRefExoticComponent<Omit<{
52
52
  children?: React.ReactNode;
53
53
  } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
54
54
  ref?: React.Ref<HTMLDivElement>;
55
55
  } & {
56
56
  asChild?: boolean;
57
- }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>> & {
57
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
58
58
  progress?: number;
59
59
  label?: string;
60
60
  } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
@@ -64,7 +64,7 @@ declare const CommandGroup: React.ForwardRefExoticComponent<Omit<{
64
64
  ref?: React.Ref<HTMLDivElement>;
65
65
  } & {
66
66
  asChild?: boolean;
67
- }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>>, "heading" | "value"> & {
67
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild">, "heading" | "value"> & {
68
68
  heading?: React.ReactNode;
69
69
  value?: string;
70
70
  forceMount?: boolean;
@@ -73,7 +73,7 @@ declare const CommandSeparator: React.ForwardRefExoticComponent<Omit<Pick<Pick<R
73
73
  ref?: React.Ref<HTMLDivElement>;
74
74
  } & {
75
75
  asChild?: boolean;
76
- }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>> & {
76
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
77
77
  alwaysRender?: boolean;
78
78
  } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
79
79
  declare const CommandItem: React.ForwardRefExoticComponent<Omit<{
@@ -82,7 +82,7 @@ declare const CommandItem: React.ForwardRefExoticComponent<Omit<{
82
82
  ref?: React.Ref<HTMLDivElement>;
83
83
  } & {
84
84
  asChild?: boolean;
85
- }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>>, "onSelect" | "disabled" | "value"> & {
85
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild">, "onSelect" | "disabled" | "value"> & {
86
86
  disabled?: boolean;
87
87
  onSelect?: (value: string) => void;
88
88
  value?: string;
@@ -0,0 +1,50 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+
4
+ /**
5
+ * ConceptCompareRail — the rooms as a rail, the card's backdrop following the
6
+ * active room, and the foot band that carries the page dots.
7
+ *
8
+ * On the phone the card IS the current room's photograph: the head, the rail
9
+ * and the foot all sit on it, and it cross-fades to the next room as the
10
+ * reader swipes. That needs the rail's active index, which is state — so this
11
+ * is the one client boundary in `ConceptCompare`. It takes only what can cross
12
+ * that boundary: photograph URLs, initials, and the pre-rendered rooms and
13
+ * foot as children. `labels`, `formatPrice` and the rest stay server-side.
14
+ *
15
+ * ## Why the backdrop lives here and not in each slide
16
+ *
17
+ * A slide carrying its own photograph is a boxed card sliding over a dark
18
+ * ground; the backdrop changing under a transparent slide is the reader
19
+ * turning to look at the next room. The second is the smoother thing, and it
20
+ * is also cheaper to look at — one photograph at a time instead of the edge of
21
+ * the next one peeking in.
22
+ *
23
+ * ## Why the foot is here too
24
+ *
25
+ * The dots need the rail's state, and on the phone they belong INSIDE the foot
26
+ * band's glass — under the rail they sat on a strip of bare photograph between
27
+ * the room's scrim and the foot's. So the foot renders here, with the dots as
28
+ * its first row below `lg`, and `ConceptCompare` hands it the foot's content.
29
+ *
30
+ * Desktop ignores the backdrop and the dots (`lg:hidden`); each room carries
31
+ * its own photograph there, side by side, and the foot is an absolute glass
32
+ * band over them.
33
+ */
34
+ type ConceptCompareBackdrop = {
35
+ /** The room's photograph. Absent: the ground with the venue's initial. */
36
+ src?: string;
37
+ /** The venue's first letter, for the no-photograph fallback. */
38
+ initial: string;
39
+ };
40
+ type ConceptCompareRailProps = {
41
+ backdrops: ConceptCompareBackdrop[];
42
+ /** Classes for the rail itself — the desktop grid lives here. */
43
+ railClassName?: string;
44
+ /** The foot's content: the note and the action. Omit for no foot. */
45
+ foot?: React.ReactNode;
46
+ children: React.ReactNode;
47
+ };
48
+ declare function ConceptCompareRail({ backdrops, railClassName, foot, children, }: ConceptCompareRailProps): react_jsx_runtime.JSX.Element;
49
+
50
+ export { type ConceptCompareBackdrop, ConceptCompareRail, type ConceptCompareRailProps };
@@ -0,0 +1,5 @@
1
+ export { ConceptCompareRail } from '../chunk-UDN7UHJW.js';
2
+ import '../chunk-DTXNDLSE.js';
3
+ import '../chunk-5RWTOT5Y.js';
4
+ import '../chunk-FEK5XGZW.js';
5
+ import '../chunk-3GWWZKX7.js';
@@ -1,73 +1,110 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { MenuItemLabels } from './menu-item.js';
3
2
  import { MenuConceptPreview, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
4
3
  import 'react';
5
4
  import './place-types.js';
6
5
 
7
6
  /**
8
- * ConceptCompare — one dish or drink across every kitchen that makes it, under
9
- * whatever name each of them uses.
7
+ * ConceptCompare — one dish or drink across the kitchens that make it, under
8
+ * whatever name each of them uses. No surfaces at all.
10
9
  *
11
10
  * The keystone module: a single menu can list papanași, only a catalogue can
12
- * say that seventy kitchens make them under fifty different names between 8 and
13
- * 44 lei. The alias column is what makes it honest — these are not the same row
14
- * seventy times, they are seventy differently-named rows we matched, and
15
- * showing each kitchen's own wording is the evidence for that.
11
+ * say that forty-five kitchens make them under eight names between 29 and 52
12
+ * lei. Each kitchen's own wording is the evidence that these are matched rows
13
+ * rather than one row repeated.
16
14
  *
17
- * ## The row is place-led
15
+ * ## The card IS the rooms
18
16
  *
19
- * Each row leads with the VENUE PHOTOGRAPH, which is real and licensed. It
20
- * replaced a price bar, and the trade is not close: the bar measured the one
21
- * value already printed beside it in words, while the thing a reader is
22
- * actually choosing between the restaurant had no image at all. Five
23
- * restaurants that disagree about a name is the story; five bars of similar
24
- * length is not.
17
+ * Every panel is gone. Each room is a VENUE photograph real and licensed
18
+ * where an item's would be generated with the kitchen's line floating over
19
+ * it, and the three readable things sit on glass: the identity in a band
20
+ * across the top, each kitchen's line over its own photograph, the foot along
21
+ * the bottom.
25
22
  *
26
- * ## Two layouts, one component
23
+ * ## Read across on desktop, one at a time on the phone
27
24
  *
28
- * From `lg` the row is a table: rank, venue, alias, mentions, price. Below it,
29
- * the rows become the production `<MenuItem>` the alias as the name, the
30
- * venue underneath because a five-column table does not survive 390px and
31
- * the item row already solves this exact shape everywhere else in the app.
25
+ * From `lg` the rooms stand side by side four kitchens, each with its own
26
+ * name for the dish and its own price, so comparing is a horizontal glance
27
+ * and the head and foot are ABSOLUTE glass bands over them, so the photographs
28
+ * run the full height of the card beneath both. The head's glass fades out
29
+ * downward on the same curve as its mask; the foot's fades out upward the same
30
+ * way. It was the ink stopping short of the mask that drew a line where a band
31
+ * ended.
32
+ *
33
+ * Below `lg` four rooms in a 2×2 was too much happening in one card, so the
34
+ * rooms become a `SnapRail`: one room per swipe, page dots under it, the head
35
+ * and foot static on the card's ground above and below. The dots are the
36
+ * rail's own — the same object the hub's other rails use.
37
+ *
38
+ * ## The dark overlay, and the hover that lifts it
39
+ *
40
+ * Four busy interiors are four competing subjects, and type over them had
41
+ * nothing to sit on but its own local scrim — the eye could not settle. Every
42
+ * photograph carries a uniform dark overlay so the words lead and the picture
43
+ * supports; hovering a room lifts the overlay off THAT room only, so the reader
44
+ * can look at the place they are about to open.
45
+ *
46
+ * ## Light and dark
47
+ *
48
+ * There is nothing in it to theme. Photographs and their scrims are identical
49
+ * in both, because text over an image needs the same treatment either way —
50
+ * the content is `.dark`-scoped for exactly that. What adapts is the card's
51
+ * EDGE: a soft shadow and hairline on light, a deeper shadow and a light
52
+ * hairline on dark. The edge lives on the outer wrapper, outside the scope.
53
+ *
54
+ * ## No room here is a room that does not serve it
55
+ *
56
+ * The earlier card led with an AI-generated picture of the dish, and a
57
+ * generated image at the head of a block whose whole argument is "we counted
58
+ * real menus" undercut the argument. The rooms are real.
32
59
  */
33
60
  /** @deprecated Alias of `MenuLinkProps`; kept so existing imports keep working. */
34
61
  type ConceptCompareLinkProps = MenuLinkProps;
35
62
  type ConceptCompareFigure = {
36
- /** Pre-formatted, e.g. "44". */
63
+ /** Pre-formatted, e.g. "44" or "29–52". */
37
64
  value: string;
38
65
  label: string;
66
+ /** Draws the value in amber — the spread, which is the number the card is about. */
67
+ accent?: boolean;
39
68
  };
40
69
  type ConceptCompareLabels = {
41
70
  /** Small caps kicker, e.g. "DISH CONCEPT". */
42
71
  kind: string;
43
- /** Column heading over the aliases, e.g. "ON THEIR MENU AS". */
44
- alias: string;
45
- /** Under each price, e.g. "per portion". */
46
- perPortion?: string;
47
- /** Badge on the lowest-priced row, e.g. "cheapest shown". */
48
- cheapest?: string;
72
+ /** The badge on the lowest-priced room, e.g. "CHEAPEST". */
73
+ cheapest: string;
74
+ /** The badge on every other room, e.g. "#2". Defaults to `#${rank}`. */
75
+ rank?: (rank: number) => string;
76
+ /** Beside each price, e.g. "12 mentions". Pluralised by the caller. */
77
+ mentions: (count: number) => string;
78
+ /** Accessible name for a room's link, e.g. "Papanași at Casa Maramureșeană". */
79
+ openRoom: (alias: string, venue: string) => string;
49
80
  };
50
81
  type ConceptCompareProps = {
51
82
  concept: MenuConceptPreview;
52
83
  labels: ConceptCompareLabels;
53
- /** Passed to the mobile `<MenuItem>` rows. */
54
- itemLabels: MenuItemLabels;
84
+ /** One line under the title, e.g. "45 kitchens make it, and they do not agree on the name". */
85
+ lede?: string;
55
86
  formatPrice: (amount: number) => string;
56
- formatCount?: (value: number) => string;
57
- /** CSS gradient for the header swatch, when there is no photograph. */
58
- gradient: string;
59
- /** A real photograph of the concept, when the matched menu row carries one. */
60
- imageUrl?: string;
61
87
  figures?: ConceptCompareFigure[];
62
- /** Destination per row, keyed by place slug. */
88
+ /** Destination per room, keyed by place slug. A room without one is inert. */
63
89
  hrefs?: Record<string, string>;
64
- /** The provenance note in the footer. */
90
+ /** The provenance note in the foot. */
65
91
  footnote?: string;
66
- /** Actions in the footer — pass rendered links or buttons. */
92
+ /** The action in the foot — pass a rendered link or button. */
67
93
  actions?: React.ReactNode;
68
94
  linkComponent?: MenuLinkComponent;
69
95
  className?: string;
70
96
  };
71
- declare function ConceptCompare({ concept, labels, itemLabels, formatPrice, formatCount, gradient, imageUrl, figures, hrefs, footnote, actions, linkComponent: LinkComponent, className, }: ConceptCompareProps): react_jsx_runtime.JSX.Element;
97
+ declare function ConceptCompare({ concept, labels, lede, formatPrice, figures, hrefs, footnote, actions, linkComponent: LinkComponent, className, }: ConceptCompareProps): react_jsx_runtime.JSX.Element | null;
98
+ /**
99
+ * The card's own loading state, at the card's footprint.
100
+ *
101
+ * The ground is the card's own dark surface, so the placeholder is already
102
+ * what the photographs will land on and nothing flashes from paper to ink.
103
+ * The phone shape is one room tall with the head above and the foot below;
104
+ * from `lg` it is the four-room row with the head and foot laid over it.
105
+ */
106
+ declare function ConceptCompareSkeleton({ className }: {
107
+ className?: string;
108
+ }): react_jsx_runtime.JSX.Element;
72
109
 
73
- export { ConceptCompare, type ConceptCompareFigure, type ConceptCompareLabels, type ConceptCompareLinkProps, type ConceptCompareProps };
110
+ export { ConceptCompare, type ConceptCompareFigure, type ConceptCompareLabels, type ConceptCompareLinkProps, type ConceptCompareProps, ConceptCompareSkeleton };