@bearmenu/ui 0.8.9 → 0.8.11

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 (225) hide show
  1. package/dist/chunk-24RFI4RQ.js +167 -0
  2. package/dist/{chunk-UKBNQO6Q.js → chunk-2CXU5WZO.js} +1 -1
  3. package/dist/chunk-2XCZJWTV.js +121 -0
  4. package/dist/{chunk-62ES6N32.js → chunk-43RC7MHY.js} +1 -1
  5. package/dist/chunk-5RWTOT5Y.js +72 -0
  6. package/dist/{chunk-Q6QH52LJ.js → chunk-6TEEY5YA.js} +2 -2
  7. package/dist/{chunk-AXV5O2IZ.js → chunk-AKQ57NXY.js} +3 -3
  8. package/dist/chunk-CLLY6XB3.js +41 -0
  9. package/dist/chunk-DTXNDLSE.js +125 -0
  10. package/dist/chunk-F6FMZJDG.js +113 -0
  11. package/dist/{chunk-2WDJR6K7.js → chunk-G7IFL4Z4.js} +4 -1
  12. package/dist/chunk-H3ZZDX5L.js +61 -0
  13. package/dist/{chunk-H3BV4VAG.js → chunk-JS3EINZD.js} +2 -2
  14. package/dist/chunk-MZL7CGCL.js +109 -0
  15. package/dist/{chunk-TH6W2PWG.js → chunk-UDTG3RSE.js} +1 -1
  16. package/dist/chunk-WCTHZKU7.js +37 -0
  17. package/dist/{chunk-TT3WFS6S.js → chunk-YOKHBAIH.js} +11 -11
  18. package/dist/components/alert-dialog.js +1 -1
  19. package/dist/components/alert.d.ts +2 -2
  20. package/dist/components/avatar.d.ts +3 -3
  21. package/dist/components/badge.d.ts +1 -1
  22. package/dist/components/badge.js +1 -1
  23. package/dist/components/button.d.ts +2 -2
  24. package/dist/components/button.js +1 -1
  25. package/dist/components/calendar.js +1 -1
  26. package/dist/components/card.d.ts +4 -4
  27. package/dist/components/carousel.js +1 -1
  28. package/dist/components/combobox.js +1 -1
  29. package/dist/components/command.d.ts +2 -2
  30. package/dist/components/concept-compare-rail.d.ts +50 -0
  31. package/dist/components/concept-compare-rail.js +5 -0
  32. package/dist/components/concept-compare.d.ts +75 -38
  33. package/dist/components/concept-compare.js +209 -133
  34. package/dist/components/concept-convergence.d.ts +41 -0
  35. package/dist/components/concept-convergence.js +53 -0
  36. package/dist/components/convergence.d.ts +25 -0
  37. package/dist/components/convergence.js +4 -0
  38. package/dist/components/dialog.d.ts +2 -2
  39. package/dist/components/empty-state.js +1 -1
  40. package/dist/components/event-card.js +1 -1
  41. package/dist/components/event-compact-row.js +1 -1
  42. package/dist/components/event-detail-panel.js +2 -2
  43. package/dist/components/event-index-row.js +1 -1
  44. package/dist/components/event-list-row-wide.js +2 -2
  45. package/dist/components/event-list-row.js +2 -2
  46. package/dist/components/explainer-steps.d.ts +34 -0
  47. package/dist/components/explainer-steps.js +36 -0
  48. package/dist/components/filter-category-row.js +1 -1
  49. package/dist/components/glass-ground.d.ts +61 -0
  50. package/dist/components/glass-ground.js +3 -0
  51. package/dist/components/haptic-button.js +1 -1
  52. package/dist/components/hub-surface.js +11 -1
  53. package/dist/components/hue-chip.d.ts +70 -0
  54. package/dist/components/hue-chip.js +3 -0
  55. package/dist/components/input-group.d.ts +2 -2
  56. package/dist/components/input.d.ts +1 -1
  57. package/dist/components/item.d.ts +4 -4
  58. package/dist/components/kbd.d.ts +2 -2
  59. package/dist/components/kitchen-dossier.d.ts +88 -0
  60. package/dist/components/kitchen-dossier.js +186 -0
  61. package/dist/components/link-button.js +1 -1
  62. package/dist/components/menu-dead-end.d.ts +56 -0
  63. package/dist/components/menu-dead-end.js +49 -0
  64. package/dist/components/menu-family-block.d.ts +98 -0
  65. package/dist/components/menu-family-block.js +153 -0
  66. package/dist/components/menu-family-card.d.ts +33 -27
  67. package/dist/components/menu-family-card.js +41 -85
  68. package/dist/components/menu-field.js +92 -2
  69. package/dist/components/menu-filter-chips.d.ts +86 -0
  70. package/dist/components/menu-filter-chips.js +122 -0
  71. package/dist/components/menu-group-rows.d.ts +53 -0
  72. package/dist/components/menu-group-rows.js +67 -0
  73. package/dist/components/menu-hub-hero.js +10 -5
  74. package/dist/components/menu-index-header.d.ts +37 -0
  75. package/dist/components/menu-index-header.js +68 -0
  76. package/dist/components/menu-index-list.d.ts +48 -0
  77. package/dist/components/menu-index-list.js +96 -0
  78. package/dist/components/menu-intent-hero.d.ts +69 -0
  79. package/dist/components/menu-intent-hero.js +87 -0
  80. package/dist/components/menu-item.js +3 -3
  81. package/dist/components/menu-kitchen-list.d.ts +82 -0
  82. package/dist/components/menu-kitchen-list.js +5 -0
  83. package/dist/components/menu-mocks.d.ts +37 -2
  84. package/dist/components/menu-mocks.js +152 -27
  85. package/dist/components/menu-types.d.ts +182 -6
  86. package/dist/components/motion-icon-controls.js +2 -2
  87. package/dist/components/motion-icon-set.js +6 -6
  88. package/dist/components/motion-icon.d.ts +22 -1
  89. package/dist/components/motion-icon.js +1 -1
  90. package/dist/components/multi-select-combobox.js +2 -2
  91. package/dist/components/native-select.d.ts +2 -2
  92. package/dist/components/pagination.js +1 -1
  93. package/dist/components/photo-ground.d.ts +64 -0
  94. package/dist/components/photo-ground.js +3 -0
  95. package/dist/components/place-about-tab.js +2 -2
  96. package/dist/components/place-details-mobile.js +3 -3
  97. package/dist/components/place-types.d.ts +2 -2
  98. package/dist/components/qa-block.d.ts +36 -0
  99. package/dist/components/qa-block.js +24 -0
  100. package/dist/components/scroll-fade.js +3 -121
  101. package/dist/components/searchable-select.js +1 -1
  102. package/dist/components/select.d.ts +1 -1
  103. package/dist/components/signature-chip.d.ts +2 -2
  104. package/dist/components/signature-chip.js +2 -2
  105. package/dist/components/sliding-panels.js +1 -1
  106. package/dist/components/snap-rail.d.ts +26 -3
  107. package/dist/components/snap-rail.js +3 -109
  108. package/dist/components/spinner.d.ts +4 -4
  109. package/dist/components/story.d.ts +34 -0
  110. package/dist/components/story.js +3 -0
  111. package/dist/components/tag.d.ts +4 -4
  112. package/dist/components/text.d.ts +5 -5
  113. package/dist/components/toggle.d.ts +1 -1
  114. package/dist/components/top-comparison.d.ts +28 -0
  115. package/dist/components/top-comparison.js +76 -0
  116. package/dist/components/top-cross-link.d.ts +34 -0
  117. package/dist/components/top-cross-link.js +101 -0
  118. package/dist/components/top-ranking-entry.d.ts +53 -0
  119. package/dist/components/top-ranking-entry.js +217 -0
  120. package/dist/components/top-ranking-hero.d.ts +61 -0
  121. package/dist/components/top-ranking-hero.js +99 -0
  122. package/dist/components/top-ranking-legend.d.ts +69 -0
  123. package/dist/components/top-ranking-legend.js +124 -0
  124. package/dist/components/top-types.d.ts +109 -0
  125. package/dist/components/top-types.js +1 -0
  126. package/dist/components/venue-grade-dimensions.d.ts +3 -1
  127. package/dist/components/venue-grade-dimensions.js +1 -1
  128. package/dist/components/venue-quotes.d.ts +1 -1
  129. package/dist/components/venue-quotes.js +1 -1
  130. package/dist/components/venue-wall.js +5 -3
  131. package/dist/components/weekday-theme-banner.js +1 -1
  132. package/dist/components/weekend-panel.js +2 -2
  133. package/package.json +1 -1
  134. package/src/components/badge.tsx +1 -1
  135. package/src/components/button.tsx +2 -2
  136. package/src/components/concept-compare-rail.tsx +132 -0
  137. package/src/components/concept-compare.stories.tsx +122 -54
  138. package/src/components/concept-compare.tsx +368 -196
  139. package/src/components/concept-convergence.stories.tsx +53 -0
  140. package/src/components/concept-convergence.tsx +90 -0
  141. package/src/components/convergence.stories.tsx +54 -0
  142. package/src/components/convergence.tsx +177 -0
  143. package/src/components/explainer-steps.stories.tsx +46 -0
  144. package/src/components/explainer-steps.tsx +74 -0
  145. package/src/components/glass-ground.tsx +104 -0
  146. package/src/components/hub-surface.tsx +11 -1
  147. package/src/components/hue-chip.stories.tsx +76 -0
  148. package/src/components/hue-chip.test.tsx +62 -0
  149. package/src/components/hue-chip.tsx +99 -0
  150. package/src/components/kitchen-dossier.stories.tsx +108 -0
  151. package/src/components/kitchen-dossier.tsx +321 -0
  152. package/src/components/menu-dead-end.stories.tsx +67 -0
  153. package/src/components/menu-dead-end.tsx +127 -0
  154. package/src/components/menu-family-block.stories.tsx +91 -0
  155. package/src/components/menu-family-block.tsx +303 -0
  156. package/src/components/menu-family-card.stories.tsx +43 -35
  157. package/src/components/menu-family-card.tsx +115 -142
  158. package/src/components/menu-filter-chips.stories.tsx +107 -0
  159. package/src/components/menu-filter-chips.tsx +238 -0
  160. package/src/components/menu-group-rows.stories.tsx +54 -0
  161. package/src/components/menu-group-rows.tsx +133 -0
  162. package/src/components/menu-hub-hero.test.tsx +39 -0
  163. package/src/components/menu-hub-hero.tsx +17 -5
  164. package/src/components/menu-index-header.stories.tsx +72 -0
  165. package/src/components/menu-index-header.tsx +125 -0
  166. package/src/components/menu-index-list.stories.tsx +50 -0
  167. package/src/components/menu-index-list.tsx +149 -0
  168. package/src/components/menu-intent-hero.stories.tsx +67 -0
  169. package/src/components/menu-intent-hero.tsx +189 -0
  170. package/src/components/menu-kitchen-list.stories.tsx +67 -0
  171. package/src/components/menu-kitchen-list.test.tsx +78 -0
  172. package/src/components/menu-kitchen-list.tsx +284 -0
  173. package/src/components/menu-mocks.ts +198 -23
  174. package/src/components/menu-types.ts +189 -6
  175. package/src/components/motion-icon-controls.tsx +11 -11
  176. package/src/components/motion-icon-set.tsx +6 -5
  177. package/src/components/motion-icon.test.tsx +23 -1
  178. package/src/components/motion-icon.tsx +20 -0
  179. package/src/components/photo-ground.tsx +147 -0
  180. package/src/components/place-types.ts +2 -2
  181. package/src/components/qa-block.stories.tsx +43 -0
  182. package/src/components/qa-block.tsx +60 -0
  183. package/src/components/signature-chip.tsx +3 -3
  184. package/src/components/snap-rail.tsx +58 -15
  185. package/src/components/story.tsx +225 -0
  186. package/src/components/top-comparison.stories.tsx +78 -0
  187. package/src/components/top-comparison.tsx +151 -0
  188. package/src/components/top-cross-link.stories.tsx +84 -0
  189. package/src/components/top-cross-link.tsx +171 -0
  190. package/src/components/top-ranking-entry.stories.tsx +171 -0
  191. package/src/components/top-ranking-entry.test.tsx +105 -0
  192. package/src/components/top-ranking-entry.tsx +425 -0
  193. package/src/components/top-ranking-hero.stories.tsx +76 -0
  194. package/src/components/top-ranking-hero.tsx +218 -0
  195. package/src/components/top-ranking-legend.stories.tsx +78 -0
  196. package/src/components/top-ranking-legend.tsx +239 -0
  197. package/src/components/top-types.ts +110 -0
  198. package/src/components/venue-grade-dimensions.tsx +2 -2
  199. package/src/components/venue-quotes.stories.tsx +1 -1
  200. package/src/components/venue-quotes.tsx +4 -2
  201. package/src/components/venue-wall.tsx +9 -4
  202. package/src/components/weekday-theme-banner.tsx +1 -1
  203. package/dist/chunk-THOVPZNN.js +0 -37
  204. package/dist/chunk-YS6BHKZO.js +0 -92
  205. package/dist/components/ranked-venue-list.d.ts +0 -49
  206. package/dist/components/ranked-venue-list.js +0 -87
  207. package/dist/components/signature-line-list.d.ts +0 -45
  208. package/dist/components/signature-line-list.js +0 -99
  209. package/dist/components/venue-busyness-strip.d.ts +0 -35
  210. package/dist/components/venue-busyness-strip.js +0 -37
  211. package/dist/components/venue-signature-block.d.ts +0 -39
  212. package/dist/components/venue-signature-block.js +0 -6
  213. package/dist/components/venue-spotlight.d.ts +0 -74
  214. package/dist/components/venue-spotlight.js +0 -138
  215. package/src/components/ranked-venue-list.stories.tsx +0 -80
  216. package/src/components/ranked-venue-list.tsx +0 -177
  217. package/src/components/signature-line-list.stories.tsx +0 -85
  218. package/src/components/signature-line-list.tsx +0 -136
  219. package/src/components/venue-busyness-strip.stories.tsx +0 -66
  220. package/src/components/venue-busyness-strip.tsx +0 -64
  221. package/src/components/venue-signature-block.stories.tsx +0 -68
  222. package/src/components/venue-signature-block.tsx +0 -71
  223. package/src/components/venue-spotlight.stories.tsx +0 -141
  224. package/src/components/venue-spotlight.tsx +0 -236
  225. package/dist/{chunk-XWMRABZW.js → chunk-HSIYYA2E.js} +1 -1
@@ -1,4 +1,4 @@
1
- import '../chunk-3GWWZKX7.js';
1
+ import { __spreadProps, __spreadValues } from '../chunk-3GWWZKX7.js';
2
2
 
3
3
  // src/components/menu-mocks.ts
4
4
  var mockGradients = {
@@ -312,19 +312,7 @@ var mockFamily = {
312
312
  price_low: 32,
313
313
  price_high: 84,
314
314
  price_median: 49,
315
- venue_photo_urls: [],
316
- signature: {
317
- item: {
318
- id: "f1",
319
- slug: "truffle-tagliatelle",
320
- name: "Truffle Tagliatelle",
321
- kind: "dish",
322
- mentions: 17,
323
- sizes_and_prices: [{ size: 320, price: 62 }]
324
- },
325
- why: "Salt & Stone",
326
- href: "/menu/dishes/truffle-tagliatelle"
327
- }
315
+ venue_photo_urls: []
328
316
  };
329
317
  var mockDrinkFamily = {
330
318
  slug: "cocktail",
@@ -340,18 +328,7 @@ var mockDrinkFamily = {
340
328
  price_low: 18,
341
329
  price_high: 54,
342
330
  price_median: 28,
343
- venue_photo_urls: [],
344
- signature: {
345
- item: {
346
- id: "f2",
347
- slug: "house-negroni",
348
- name: "House negroni",
349
- kind: "drink",
350
- mentions: 16,
351
- sizes_and_prices: [{ size: 150, price: 32 }]
352
- },
353
- why: "The Copper Yard"
354
- }
331
+ venue_photo_urls: []
355
332
  };
356
333
  var intentGradient = (hue) => `linear-gradient(150deg, oklch(0.52 0.14 ${hue}), oklch(0.43 0.13 ${hue - 4}))`;
357
334
  var mockIntentTiles = [
@@ -375,5 +352,153 @@ var mockHubLabels = {
375
352
  openNow: "Open now",
376
353
  until: "until"
377
354
  };
355
+ var mockKitchens = [
356
+ {
357
+ place: placeBySlug("salt-and-stone"),
358
+ href: "/places/salt-and-stone",
359
+ metaLabel: "courtyard \xB7 Centru",
360
+ hoursLabel: "kitchen until 23:00",
361
+ priceLabel: "62 lei",
362
+ dish: {
363
+ name: "Truffle Tagliatelle",
364
+ href: "/menu/dishes/truffle-tagliatelle",
365
+ kicker: "MOST NAMED HERE",
366
+ meta: "17 reviews name it"
367
+ }
368
+ },
369
+ {
370
+ place: placeBySlug("the-copper-yard"),
371
+ href: "/places/the-copper-yard",
372
+ metaLabel: "pizzeria \xB7 Centru",
373
+ hoursLabel: "kitchen until 23:30",
374
+ priceLabel: "54 lei",
375
+ dish: {
376
+ name: "Tagliatelle cu Hribi",
377
+ href: "/menu/dishes/tagliatelle-cu-hribi",
378
+ kicker: "MOST NAMED HERE",
379
+ meta: "14 reviews name it"
380
+ }
381
+ },
382
+ {
383
+ // Closed, and its must-try has no live menu row — the dish line is inert.
384
+ place: __spreadProps(__spreadValues({}, placeBySlug("the-quiet-hours")), { open_now: false }),
385
+ href: "/places/the-quiet-hours",
386
+ metaLabel: "bistro \xB7 Gheorgheni",
387
+ hoursLabel: "closed \xB7 opens 12:00",
388
+ priceLabel: "58 lei",
389
+ dish: { name: "Risotto ai Funghi", kicker: "MOST NAMED HERE" }
390
+ },
391
+ {
392
+ // No dish at all: the row must still read as a venue.
393
+ place: placeBySlug("green-room-canteen"),
394
+ href: "/places/green-room-canteen",
395
+ metaLabel: "bistro \xB7 Centru",
396
+ priceLabel: "38 lei"
397
+ }
398
+ ];
399
+ var mockMatchedKitchens = mockKitchens.slice(0, 3).map((kitchen) => __spreadProps(__spreadValues({}, kitchen), {
400
+ dish: kitchen.dish && __spreadProps(__spreadValues({}, kitchen.dish), {
401
+ kicker: "WHY IT MATCHES",
402
+ meta: "mid-range \xB7 300g",
403
+ matched: true,
404
+ matchLabel: "vegetarian"
405
+ })
406
+ }));
407
+ var mockIntentFigures = [
408
+ { key: "total", value: "247", label: "kitchens" },
409
+ { key: "pizza", value: "120", label: "make a pizza", onSelect: () => {
410
+ } },
411
+ { key: "pasta", value: "96", label: "make pasta", onSelect: () => {
412
+ } },
413
+ { key: "open", value: "58", label: "open now", onSelect: () => {
414
+ } }
415
+ ];
416
+ var mockFilterChips = [
417
+ { key: "open", label: "Open now", count: 58, active: true },
418
+ { key: "price", label: "Mid-range", count: 96 },
419
+ { key: "terrace", label: "Terrace", count: 31 },
420
+ { key: "veg", label: "Has a vegetarian dish", count: 84 },
421
+ { key: "late", label: "Serving past midnight", count: 4 },
422
+ { key: "centru", label: "Centru", count: 90 }
423
+ ];
424
+ var mockLoosenOptions = [
425
+ { key: "open", label: "Drop \u201COpen now\u201D", gainLabel: "+8 kitchens" },
426
+ { key: "price", label: "Drop \u201CMid-range\u201D", gainLabel: "+7 kitchens" },
427
+ { key: "veg", label: "Drop \u201CVegetarian\u201D", gainLabel: "+2 kitchens" }
428
+ ];
429
+ var mockIndexEntries = [
430
+ ["Breakfast & brunch", 314],
431
+ ["Romanian", 299],
432
+ ["Bakery", 250],
433
+ ["Italian", 247],
434
+ ["Pizza", 245],
435
+ ["Fast food", 229],
436
+ ["Vegetarian", 172],
437
+ ["American", 153],
438
+ ["Vegan", 108],
439
+ ["Mediterranean", 78],
440
+ ["Seafood", 77],
441
+ ["Eastern European", 67],
442
+ ["Fusion", 61],
443
+ ["Middle Eastern", 58],
444
+ ["BBQ", 57],
445
+ ["Deli", 51],
446
+ ["Steakhouse", 49],
447
+ ["Japanese", 34],
448
+ ["French", 26],
449
+ ["Sushi", 23],
450
+ ["Turkish", 22],
451
+ ["Chinese", 14],
452
+ ["Greek", 12],
453
+ ["Indian", 11],
454
+ ["Thai", 7],
455
+ ["German", 5],
456
+ ["Lebanese", 3],
457
+ ["Portuguese", 1]
458
+ ].map(([name, count], index) => ({
459
+ slug: String(name).toLowerCase().replace(/[^a-z]+/g, "-"),
460
+ name: String(name),
461
+ count: Number(count),
462
+ href: `/menu/cuisines/${String(name).toLowerCase().replace(/[^a-z]+/g, "-")}`,
463
+ gradient: Object.values(mockGradients)[index % 5]
464
+ }));
465
+ var mockFamilyBlock = {
466
+ slug: "italian",
467
+ name: "Italian",
468
+ venueCount: 247,
469
+ chips: [
470
+ { slug: "pizza", label: "Pizza", count: 120 },
471
+ { slug: "pasta", label: "Pasta", count: 96 },
472
+ { slug: "salad", label: "Salad", count: 88 }
473
+ ],
474
+ priceBandLabel: "22\u201386 lei",
475
+ photoUrls: [],
476
+ // Three, as the cuisines index sends, and stripped the same way: no dish and
477
+ // no price. The dish column is ~110px inside this card, which truncates the
478
+ // name and wraps its kicker to three lines; the price is already the band
479
+ // above.
480
+ venues: mockKitchens.slice(0, 3).map((kitchen) => __spreadProps(__spreadValues({}, kitchen), { dish: void 0, priceLabel: void 0 })),
481
+ href: "/menu/cuisines/italian"
482
+ };
483
+ var mockBareFamilyBlock = {
484
+ slug: "portuguese",
485
+ name: "Portuguese",
486
+ venueCount: 1,
487
+ href: "/menu/cuisines/portuguese"
488
+ };
489
+ var mockQa = [
490
+ {
491
+ question: "Which Italian kitchen is best in Cluj?",
492
+ answer: "Salt & Stone is named most often \u2014 seventeen reviews single out the truffle tagliatelle. The Copper Yard is second on volume and about eight lei cheaper across the menu."
493
+ },
494
+ {
495
+ question: "What does an Italian main cost?",
496
+ answer: "Pasta runs 38 to 84 lei and pizza 30 to 68 across these kitchens. Ninety-six of them sit in the mid-range price band."
497
+ },
498
+ {
499
+ question: "Anywhere open late?",
500
+ answer: "Four keep the kitchen going past midnight \u2014 The Copper Yard until 00:00 is the latest of the Italian rooms."
501
+ }
502
+ ];
378
503
 
379
- export { mockConcept, mockDrinkFamily, mockFamily, mockFormatters, mockGradients, mockHubLabels, mockIntentTiles, mockItems, mockPlaces, mockVenues };
504
+ export { mockBareFamilyBlock, mockConcept, mockDrinkFamily, mockFamily, mockFamilyBlock, mockFilterChips, mockFormatters, mockGradients, mockHubLabels, mockIndexEntries, mockIntentFigures, mockIntentTiles, mockItems, mockKitchens, mockLoosenOptions, mockMatchedKitchens, mockPlaces, mockQa, mockVenues };
@@ -148,10 +148,8 @@ type MenuFamilyPreview = {
148
148
  price_low?: number;
149
149
  price_high?: number;
150
150
  price_median?: number;
151
- /** Pre-resolved venue photos for the fanned deck. Venue photography only. */
151
+ /** Pre-resolved venue photos for the mosaic. Venue photography only. */
152
152
  venue_photo_urls?: string[];
153
- /** The item this family is most named for. */
154
- signature?: MenuFamilySignature;
155
153
  };
156
154
  type MenuFamilyChip = {
157
155
  slug: string;
@@ -159,8 +157,6 @@ type MenuFamilyChip = {
159
157
  /** Venue count. Absent where no facet backs the chip. */
160
158
  count?: number;
161
159
  };
162
- /** A family card's featured item. Same shape as a venue's, for the same reason. */
163
- type MenuFamilySignature = MenuVenueSignature;
164
160
  /**
165
161
  * A venue as the hub's venue-scoped surfaces consume it: the place itself plus
166
162
  * the pre-resolved bits a hub row needs and `PlacePreview` does not carry —
@@ -220,6 +216,186 @@ type MenuIntentTile = {
220
216
  /** Icon node, resolved by the caller — the DS does not pick icons for it. */
221
217
  icon?: ReactNode;
222
218
  };
219
+ /**
220
+ * One figure in the intent hero — a number the reader can tap to filter by.
221
+ *
222
+ * The value arrives pre-formatted because the figures are not all the same
223
+ * quantity: "247" is a count and "58" is a count of a subset, but a caller may
224
+ * also want "4 past midnight", and a component that formatted numbers itself
225
+ * would need the locale it is forbidden to own.
226
+ */
227
+ type MenuIntentFigure = {
228
+ key: string;
229
+ /** Pre-formatted, e.g. "247". */
230
+ value: string;
231
+ /** The unit, e.g. "kitchens" or "make a pizza". */
232
+ label: string;
233
+ /**
234
+ * The state this figure applies, as a URL.
235
+ *
236
+ * An `href` rather than only a callback because the filter state lives in the
237
+ * query string: a link is server-rendered, crawlable, middle-clickable and
238
+ * survives with no JavaScript, and `?open=now` is an indexable state we
239
+ * actively want followed. `onSelect` remains for consumers holding the state
240
+ * in memory; `href` wins when both are set.
241
+ */
242
+ href?: string;
243
+ /**
244
+ * Applies this figure as a filter without navigating. A figure with neither
245
+ * `href` nor `onSelect` renders as a static fact rather than as a control
246
+ * that does nothing — the total is usually one, since filtering to "all of
247
+ * them" is not a state.
248
+ */
249
+ onSelect?: () => void;
250
+ /** The filter this figure applies is already on. */
251
+ active?: boolean;
252
+ };
253
+ /**
254
+ * A filter chip on the intent page.
255
+ *
256
+ * Every chip counts KITCHENS, whatever facet backs it — a dietary chip reads
257
+ * "has a vegetarian dish" and counts venues, not dishes. Mixing venue counts
258
+ * and item counts in one row is what made the earlier version unreadable, so
259
+ * the type carries no unit: there is only ever one.
260
+ */
261
+ type MenuFilterChip = {
262
+ key: string;
263
+ label: string;
264
+ /** Kitchens this chip would leave. Absent where no facet backs it. */
265
+ count?: number;
266
+ active?: boolean;
267
+ /**
268
+ * Zero kitchens under the current filters — dimmed, but STILL TAPPABLE.
269
+ *
270
+ * Tapping swaps it in and drops whichever active filter conflicts, with a
271
+ * line saying which. A disabled chip would leave the reader guessing why the
272
+ * thing they want is unavailable; this one answers it by being pressed.
273
+ */
274
+ empty?: boolean;
275
+ /** Icon node, resolved by the caller. */
276
+ icon?: ReactNode;
277
+ };
278
+ /**
279
+ * A venue row on the intent page — the page's spine.
280
+ *
281
+ * Distinct from `MenuVenuePreview` (the hub's ranked row) in one structural
282
+ * way that is the whole point of the surface: the dish carries its own `href`,
283
+ * so the row has TWO targets. The row goes to the venue; the dish name opens
284
+ * the item drawer. `RankedVenueList` wraps its entire row in one link and
285
+ * therefore cannot express this, which is why this is a separate shape rather
286
+ * than a flag on that one.
287
+ */
288
+ type MenuKitchenPreview = {
289
+ place: PlacePreview;
290
+ /** The venue page. The row's primary target. */
291
+ href: string;
292
+ /** Venue type and district, composed and localised by the caller, e.g. "pizzeria · Centru". */
293
+ metaLabel?: string;
294
+ /**
295
+ * "kitchen until 23:00", or "closed · opens 12:00". Composed by the caller
296
+ * because the wording differs by scope — a bar is not a kitchen.
297
+ */
298
+ hoursLabel?: string;
299
+ dish?: MenuKitchenDish;
300
+ /** Pre-formatted, e.g. "62 lei". */
301
+ priceLabel?: string;
302
+ };
303
+ /** The dish a kitchen row leads with. The row's second target. */
304
+ type MenuKitchenDish = {
305
+ name: string;
306
+ /**
307
+ * Opens the item drawer. Omit for an inert dish line — a must-try the
308
+ * reviews named that has no live menu row behind it.
309
+ */
310
+ href?: string;
311
+ /** Kicker over the name: "MOST NAMED HERE", or "WHY IT MATCHES" when filtered. */
312
+ kicker: string;
313
+ /** Under the name: "17 reviews name it", or "mid-range · 300g" when matched. */
314
+ meta?: string;
315
+ /**
316
+ * This row is answering a filter rather than volunteering its most-named
317
+ * dish. Draws the match mark and tints the block, so a reader scanning a
318
+ * filtered list can see that every row changed its reason for being there.
319
+ */
320
+ matched?: boolean;
321
+ /** The filter it matched, e.g. "vegetarian". */
322
+ matchLabel?: string;
323
+ };
324
+ /**
325
+ * One row in a browse index — the complete set, at a glance.
326
+ *
327
+ * The index is the page's whole SEO job: it carries a crawlable link to every
328
+ * destination the city has, which is what frees the richer blocks below it to
329
+ * be progressive. So it holds nothing but what a reader needs to choose — a
330
+ * name, a count and a colour.
331
+ */
332
+ type MenuIndexEntry = {
333
+ slug: string;
334
+ name: string;
335
+ count: number;
336
+ href: string;
337
+ /** Finished CSS gradient for the mark. Falls back to the border colour. */
338
+ gradient?: string;
339
+ };
340
+ /**
341
+ * A family on the cuisines index — the hub's card, opened out sideways.
342
+ *
343
+ * Deliberately thin. The reader here is choosing a cuisine, not a venue, and
344
+ * every field the block does NOT carry is a decision the next page owns.
345
+ */
346
+ type MenuFamilyBlockPreview = {
347
+ slug: string;
348
+ name: string;
349
+ venueCount: number;
350
+ /** Facet chips. Labels only — the block never prints their counts. */
351
+ chips?: MenuFamilyChip[];
352
+ /** Composed price band, e.g. "22–86 lei". */
353
+ priceBandLabel?: string;
354
+ /** Venue photography only — item photography is AI-generated and never shown. */
355
+ photoUrls?: string[];
356
+ /**
357
+ * Venues, as kitchen rows. The block draws every one it is given — the caller
358
+ * decides how many, because how much of a claim the block makes is the page's
359
+ * decision, not the component's.
360
+ *
361
+ * The same `MenuKitchenPreview` the intent page's list takes, rendered by the
362
+ * same component — a family block that grew its own venue row would be the
363
+ * fourth near-identical row in the package, and they drift the moment one of
364
+ * them gets a fix.
365
+ */
366
+ venues?: MenuKitchenPreview[];
367
+ /** The family's own page. */
368
+ href: string;
369
+ };
370
+ /**
371
+ * One "loosen this" row on a dead end — a filter, and what dropping it returns.
372
+ *
373
+ * The count is the point: "drop Open now" is a guess, "+8 kitchens" is an
374
+ * answer, and a dead end that cannot say which filter costs the most is just
375
+ * an empty state with extra steps.
376
+ */
377
+ type MenuLoosenOption = {
378
+ key: string;
379
+ /** e.g. 'Drop "Open now"'. */
380
+ label: string;
381
+ /** e.g. "+8 kitchens". Pre-formatted. */
382
+ gainLabel: string;
383
+ /**
384
+ * The state without this filter, as a URL.
385
+ *
386
+ * An `href` because dropping a filter IS a navigation wherever the filter set
387
+ * lives in the query string — which makes it server-renderable, shareable and
388
+ * undoable with the back button. `onSelect` remains for consumers holding the
389
+ * state in memory; `href` wins when both are set.
390
+ */
391
+ href?: string;
392
+ onSelect?: () => void;
393
+ };
394
+ /** One question and its answer. Static prose — never an accordion; see `<QaBlock>`. */
395
+ type QaItem = {
396
+ question: string;
397
+ answer: string;
398
+ };
223
399
  /** Formatters injected into every menu surface. The DS owns no `Intl` locale. */
224
400
  type MenuFormatters = {
225
401
  /** 24 → "24 lei". */
@@ -242,4 +418,4 @@ type MenuHubLabels = {
242
418
  until: string;
243
419
  };
244
420
 
245
- export { type MenuConceptPreview, type MenuConceptRow, type MenuFamilyChip, type MenuFamilyPreview, type MenuFamilySignature, type MenuFormatters, type MenuHubLabels, type MenuIntentTile, type MenuItemPlaceRef, type MenuItemPreview, type MenuLinkComponent, type MenuLinkProps, MenuSizeAndPrice, type MenuVenuePreview, type MenuVenueSignature };
421
+ export { type MenuConceptPreview, type MenuConceptRow, type MenuFamilyBlockPreview, type MenuFamilyChip, type MenuFamilyPreview, type MenuFilterChip, type MenuFormatters, type MenuHubLabels, type MenuIndexEntry, type MenuIntentFigure, type MenuIntentTile, type MenuItemPlaceRef, type MenuItemPreview, type MenuKitchenDish, type MenuKitchenPreview, type MenuLinkComponent, type MenuLinkProps, type MenuLoosenOption, MenuSizeAndPrice, type MenuVenuePreview, type MenuVenueSignature, type QaItem };
@@ -1,4 +1,4 @@
1
- export { MotionBookmark, MotionCheck, MotionChevron, MotionCopy, MotionEye, MotionHeart, MotionPlayPause, MotionRefresh, MotionSearch, MotionShare, MotionSliders, MotionSort, MotionStar, MotionTheme } from '../chunk-TT3WFS6S.js';
2
- import '../chunk-2WDJR6K7.js';
1
+ export { MotionBookmark, MotionCheck, MotionChevron, MotionCopy, MotionEye, MotionHeart, MotionPlayPause, MotionRefresh, MotionSearch, MotionShare, MotionSliders, MotionSort, MotionStar, MotionTheme } from '../chunk-YOKHBAIH.js';
2
+ import '../chunk-G7IFL4Z4.js';
3
3
  import '../chunk-FEK5XGZW.js';
4
4
  import '../chunk-3GWWZKX7.js';
@@ -1,4 +1,4 @@
1
- import { beats, MotionIcon, returnTransition } from '../chunk-2WDJR6K7.js';
1
+ import { beats, pivot, MotionIcon, returnTransition } from '../chunk-G7IFL4Z4.js';
2
2
  import '../chunk-FEK5XGZW.js';
3
3
  import { __spreadProps, __spreadValues } from '../chunk-3GWWZKX7.js';
4
4
  import * as React from 'react';
@@ -28,11 +28,11 @@ function MotionHouse(props) {
28
28
  motion.path,
29
29
  {
30
30
  variants: houseRoof,
31
- style: { originX: "12px", originY: "10px" },
31
+ style: pivot(12, 10),
32
32
  d: "M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10"
33
33
  }
34
34
  ),
35
- /* @__PURE__ */ jsxs(motion.g, { variants: houseWalls, style: { originX: "12px", originY: "21px" }, children: [
35
+ /* @__PURE__ */ jsxs(motion.g, { variants: houseWalls, style: pivot(12, 21), children: [
36
36
  /* @__PURE__ */ jsx("path", { d: "M21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V10" }),
37
37
  /* @__PURE__ */ jsx("path", { d: "M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" })
38
38
  ] })
@@ -60,7 +60,7 @@ var knife = {
60
60
  transition: beats(UTENSILS_TIMES, 0.6)
61
61
  }
62
62
  };
63
- var CROSSING = { originX: "12px", originY: "12px" };
63
+ var CROSSING = pivot(12, 12);
64
64
  var HANDLE_CROSSED = "M2.1 21.8 8.5 15.4";
65
65
  var HANDLE_CLEAR = "M2.1 21.8 12 12";
66
66
  var forkHandle = {
@@ -108,13 +108,13 @@ var pinDot = {
108
108
  }
109
109
  };
110
110
  function MotionMapPin(props) {
111
- return /* @__PURE__ */ jsx(MotionIcon, __spreadProps(__spreadValues({}, props), { children: /* @__PURE__ */ jsxs(motion.g, { variants: pinBody, style: { originX: "12px", originY: "22px" }, children: [
111
+ return /* @__PURE__ */ jsx(MotionIcon, __spreadProps(__spreadValues({}, props), { children: /* @__PURE__ */ jsxs(motion.g, { variants: pinBody, style: pivot(12, 22), children: [
112
112
  /* @__PURE__ */ jsx("path", { d: "M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0" }),
113
113
  /* @__PURE__ */ jsx(
114
114
  motion.circle,
115
115
  {
116
116
  variants: pinDot,
117
- style: { originX: "12px", originY: "10px" },
117
+ style: pivot(12, 10),
118
118
  fill: "currentColor",
119
119
  fillOpacity: 0,
120
120
  cx: "12",
@@ -23,6 +23,27 @@ type MotionIconVariant = "rest" | "active" | "play";
23
23
  declare function beats(times: number[], duration?: number): Transition;
24
24
  /** Leaving is not a performance — it returns the glyph, firmly and fast. */
25
25
  declare const returnTransition: Transition;
26
+ /**
27
+ * A moving part's pivot, in the icon's 24-unit VIEWBOX coordinates.
28
+ *
29
+ * Every part in this family wrote `style={{ originX: "12px", originY: "12px" }}`
30
+ * meaning "the centre of the icon" — and every one of them was pivoting
31
+ * somewhere else. Framer Motion sets `transform-box: fill-box` on any SVG
32
+ * element it transforms, which makes `transform-origin` relative to that
33
+ * element's OWN bounding box, not the viewBox. The chevron `m6 9 6 6 6-6` has
34
+ * its box at (6, 9), so "12px 12px" resolved to user-space (18, 21): a 180°
35
+ * flip around a point outside the arrow, which is why it visibly slid as it
36
+ * turned. A house's walls pivoting at its floor, a pin at its tip, a lens at
37
+ * its centre — all of them missed by the same offset for the same reason.
38
+ *
39
+ * `transform-box: view-box` is the single switch that makes px origins mean
40
+ * what they say. Declared once here so the next icon cannot get it wrong.
41
+ */
42
+ declare function pivot(x: number, y: number): {
43
+ readonly originX: `${number}px`;
44
+ readonly originY: `${number}px`;
45
+ readonly transformBox: "view-box";
46
+ };
26
47
  type PoseValues = Record<string, number>;
27
48
  /**
28
49
  * Builds the three variants for a moving part.
@@ -41,4 +62,4 @@ declare function MotionIcon({ active, replayToken, className, children, }: Motio
41
62
  children: React.ReactNode;
42
63
  }): react_jsx_runtime.JSX.Element;
43
64
 
44
- export { MotionIcon, type MotionIconProps, type MotionIconVariant, beats, poses, returnTransition, useIconGesture };
65
+ export { MotionIcon, type MotionIconProps, type MotionIconVariant, beats, pivot, poses, returnTransition, useIconGesture };
@@ -1,3 +1,3 @@
1
- export { MotionIcon, beats, poses, returnTransition, useIconGesture } from '../chunk-2WDJR6K7.js';
1
+ export { MotionIcon, beats, pivot, poses, returnTransition, useIconGesture } from '../chunk-G7IFL4Z4.js';
2
2
  import '../chunk-FEK5XGZW.js';
3
3
  import '../chunk-3GWWZKX7.js';
@@ -2,9 +2,9 @@ import { Popover, PopoverTrigger, PopoverContent } from '../chunk-66MAPLXS.js';
2
2
  import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from '../chunk-XNA2INVQ.js';
3
3
  import '../chunk-FRPPIXMY.js';
4
4
  import { Checkbox } from '../chunk-D5QHEHXJ.js';
5
- import { Button } from '../chunk-H3BV4VAG.js';
5
+ import { Button } from '../chunk-JS3EINZD.js';
6
6
  import '../chunk-R47NEOSI.js';
7
- import { Badge } from '../chunk-UKBNQO6Q.js';
7
+ import { Badge } from '../chunk-2CXU5WZO.js';
8
8
  import { cn } from '../chunk-FEK5XGZW.js';
9
9
  import '../chunk-3GWWZKX7.js';
10
10
  import * as React from 'react';
@@ -3,10 +3,10 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const nativeSelectVariants: (props?: ({
6
- size?: "default" | "sm" | "lg" | null | undefined;
6
+ size?: "sm" | "default" | "lg" | null | undefined;
7
7
  } & class_variance_authority_types.ClassProp) | undefined) => string;
8
8
  declare const NativeSelect: React.ForwardRefExoticComponent<Omit<React.SelectHTMLAttributes<HTMLSelectElement>, "size"> & VariantProps<(props?: ({
9
- size?: "default" | "sm" | "lg" | null | undefined;
9
+ size?: "sm" | "default" | "lg" | null | undefined;
10
10
  } & class_variance_authority_types.ClassProp) | undefined) => string> & React.RefAttributes<HTMLSelectElement>>;
11
11
 
12
12
  export { NativeSelect, nativeSelectVariants };
@@ -1,4 +1,4 @@
1
- import { buttonVariants } from '../chunk-H3BV4VAG.js';
1
+ import { buttonVariants } from '../chunk-JS3EINZD.js';
2
2
  import '../chunk-R47NEOSI.js';
3
3
  import { cn } from '../chunk-FEK5XGZW.js';
4
4
  import { __objRest, __spreadValues, __spreadProps } from '../chunk-3GWWZKX7.js';
@@ -0,0 +1,64 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ /**
4
+ * PhotoGround — venue photography as the ground of a card.
5
+ *
6
+ * Three cards are built on photographs rather than on panels: the hub's family
7
+ * card, the cuisines index's family block and the concept card. Each had grown
8
+ * its own copy of the same stack — a dark ground, a grid of `<img>`s with an
9
+ * initial for the missing ones, a hue layer, an overlay — and the one rule that
10
+ * matters for all of them (the tint has to be `mix-blend-color`, the ground
11
+ * has to be a fixed literal) was written three times. This is the stack, once.
12
+ *
13
+ * ## What it owns and what it leaves to the card
14
+ *
15
+ * It owns the photographs and their colour: the ground, the tiles, the fallback
16
+ * initial, the family's hue and an optional uniform dark overlay. It does NOT
17
+ * own the ink — the scrims and bands that carry a card's type run in
18
+ * a different direction on every card (down the family block's phone, across
19
+ * its desktop, in from both ends of the concept card) and are the card's own.
20
+ *
21
+ * ## Two layouts
22
+ *
23
+ * `mosaic` tiles every photograph into a grid the caller shapes; `stack` shows
24
+ * ONE of them, `active`, and cross-fades to the next — a card whose ground
25
+ * follows a swipe. Same slots, same fallback, same tint either way.
26
+ *
27
+ * ## Why the ground is a literal, and why the tint is `color`
28
+ *
29
+ * The ground is the backdrop a blend or blur samples before a lazy image lands
30
+ * and the tile an empty slot shows, so it must not change with the theme —
31
+ * photography is not themed, and a card that flashed paper-white behind its
32
+ * photographs in light mode was wrong in exactly the mode most people use.
33
+ * `mix-blend-mode: color` takes hue and chroma from the tint layer and
34
+ * luminance from the photograph, so the room keeps its structure and the
35
+ * family keeps its colour; a plain overlay would have muddied both.
36
+ */
37
+ /** The ground under the photography. Fixed, and deliberately not a token. */
38
+ declare const PHOTO_GROUND = "bg-[oklch(0.21_0.012_55)]";
39
+ type PhotoGroundProps = {
40
+ /** One per slot. An absent one draws the slot's initial on the ground. */
41
+ photos: (string | undefined)[];
42
+ /** The fallback glyph per slot — a venue's first letter. One is reused for all. */
43
+ initials: string | string[];
44
+ /** `mosaic` tiles every slot; `stack` shows `active` and cross-fades. */
45
+ mode?: "mosaic" | "stack";
46
+ /** `stack` only: which slot is showing. */
47
+ active?: number;
48
+ /** `mosaic` only: the grid template, e.g. `"grid-cols-2 grid-rows-2 lg:grid-cols-3"`. */
49
+ mosaicClassName?: string;
50
+ /** `mosaic` only: slots from this index on are desktop-only tiles. */
51
+ phoneTiles?: number;
52
+ /** Finished CSS gradient — the family's colour, laid over the photographs. */
53
+ gradient?: string;
54
+ /** A uniform dark overlay on every photograph. */
55
+ overlay?: boolean;
56
+ /** Extra classes on the overlay, e.g. a hover that lifts it. */
57
+ overlayClassName?: string;
58
+ /** How eagerly the first photograph loads. `stack` grounds are above the fold. */
59
+ loading?: "lazy" | "eager";
60
+ className?: string;
61
+ };
62
+ declare function PhotoGround({ photos, initials, mode, active, mosaicClassName, phoneTiles, gradient, overlay, overlayClassName, loading, className, }: PhotoGroundProps): react_jsx_runtime.JSX.Element;
63
+
64
+ export { PHOTO_GROUND, PhotoGround, type PhotoGroundProps };
@@ -0,0 +1,3 @@
1
+ export { PHOTO_GROUND, PhotoGround } from '../chunk-5RWTOT5Y.js';
2
+ import '../chunk-FEK5XGZW.js';
3
+ import '../chunk-3GWWZKX7.js';
@@ -1,7 +1,7 @@
1
- export { PlaceAboutTab } from '../chunk-XWMRABZW.js';
1
+ export { PlaceAboutTab } from '../chunk-HSIYYA2E.js';
2
2
  import '../chunk-VG3PND3D.js';
3
- import '../chunk-WSRWFA6K.js';
4
3
  import '../chunk-ZDAHKI3A.js';
4
+ import '../chunk-WSRWFA6K.js';
5
5
  import '../chunk-7BWTYWM3.js';
6
6
  import '../chunk-FEK5XGZW.js';
7
7
  import '../chunk-3GWWZKX7.js';
@@ -1,9 +1,9 @@
1
- import { PlaceAboutTab } from '../chunk-XWMRABZW.js';
1
+ import { PlaceAboutTab } from '../chunk-HSIYYA2E.js';
2
2
  import '../chunk-VG3PND3D.js';
3
- import '../chunk-WSRWFA6K.js';
4
3
  import '../chunk-ZDAHKI3A.js';
4
+ import '../chunk-WSRWFA6K.js';
5
5
  import { Text } from '../chunk-7BWTYWM3.js';
6
- import { Badge } from '../chunk-UKBNQO6Q.js';
6
+ import { Badge } from '../chunk-2CXU5WZO.js';
7
7
  import { cn } from '../chunk-FEK5XGZW.js';
8
8
  import '../chunk-3GWWZKX7.js';
9
9
  import { useState } from 'react';
@@ -49,7 +49,7 @@ type PlacePreview = {
49
49
  }>;
50
50
  /**
51
51
  * The review distillation. Drives every menu-hub surface that speaks for a
52
- * venue — `<VenueSpotlight>`, `<RankedVenueList>`, `<SignatureLineList>` —
52
+ * venue — `<KitchenDossier>`, `<MenuKitchenList>`, `<OpenNowList>` —
53
53
  * because the signature dish, the pull-quotes and the graded dimensions all
54
54
  * live here rather than on the menu itself.
55
55
  */
@@ -168,7 +168,7 @@ type PlaceReviewsDigest = {
168
168
  tagline?: CitedText;
169
169
  verdict?: CitedText;
170
170
  caveat?: CitedText;
171
- /** 1–3 standout positives. The pull-quotes on `<VenueSpotlight>`. */
171
+ /** 1–3 standout positives. The pull-quotes on `<KitchenDossier>`. */
172
172
  highlights?: CitedText[];
173
173
  must_try?: PlaceMustTry[];
174
174
  /** Vibe codes (cozy, quiet, family_friendly, …). Localised by the caller. */