@medialane/ui 0.37.0 → 0.38.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.
@@ -1,5 +1,6 @@
1
+ import * as React from 'react';
1
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { ServiceStatus, ServiceDefinition } from '../data/launchpad-services.js';
3
+ import { ServiceStatus, ServiceGroup, ServiceDefinition, ServiceGroupDefinition } from '../data/launchpad-services.js';
3
4
  import 'lucide-react';
4
5
 
5
6
  interface ServiceOverride {
@@ -14,14 +15,14 @@ interface ServiceOverride {
14
15
  }
15
16
  type ServiceOverrides = Record<string, ServiceOverride>;
16
17
  interface ServiceHue {
17
- /** icon tint (600 for light surfaces, 400 for dark) */
18
+ /** icon glyph tint (600 for light surfaces, 400 for dark) */
18
19
  text: string;
19
- /** solid fill — icon glow */
20
+ /** solid fill — CTA button background */
20
21
  solid: string;
21
- /** thin card border tint */
22
+ /** static card border (visible on both themes, not just on hover) */
22
23
  border: string;
23
- /** vivid two-stop gradient for the action pill (asset-page button language) */
24
- pill: string;
24
+ /** soft icon-tile background (10% tint, both themes) */
25
+ tint: string;
25
26
  }
26
27
  declare const SERVICE_HUES: Record<string, ServiceHue>;
27
28
  interface LaunchpadServiceCardProps {
@@ -33,14 +34,35 @@ interface LaunchpadServiceCardProps {
33
34
  index?: number;
34
35
  }
35
36
  declare function LaunchpadServiceCard({ def, override, featured, index }: LaunchpadServiceCardProps): react_jsx_runtime.JSX.Element;
37
+ /**
38
+ * Owns the search + group-filter state for the launchpad page. A page renders
39
+ * `LaunchpadFilterBar` wherever it wants (e.g. right under the h1) wired to
40
+ * this hook's fields, then passes `query`/`activeGroups` straight through to
41
+ * `LaunchpadGroupedSections` so the grid reacts to the same state.
42
+ */
43
+ declare function useLaunchpadFilter(): {
44
+ query: string;
45
+ setQuery: React.Dispatch<React.SetStateAction<string>>;
46
+ activeGroups: Set<ServiceGroup>;
47
+ toggleGroup: (key: ServiceGroup) => void;
48
+ filterableGroups: ServiceGroupDefinition[];
49
+ totalMatches: number;
50
+ clear: () => void;
51
+ };
36
52
  interface LaunchpadGroupedSectionsProps {
37
53
  /** Per-app hrefs / rollout flips, keyed by service key. */
38
54
  overrides: ServiceOverrides;
55
+ /** Current search query — from `useLaunchpadFilter()`. */
56
+ query: string;
57
+ /** Current active group filters — from `useLaunchpadFilter()`. */
58
+ activeGroups: Set<ServiceGroup>;
59
+ /** Reset both — from `useLaunchpadFilter()`. */
60
+ onClearFilters: () => void;
39
61
  className?: string;
40
62
  }
41
- /** The full grouped launchpad services block — section order comes from
63
+ /** The full grouped launchpad services grid — section order comes from
42
64
  * LAUNCHPAD_SERVICE_GROUPS; cards from LAUNCHPAD_SERVICE_DEFINITIONS.
43
- * Owns the search/filter state so the grid can react to it live. */
44
- declare function LaunchpadGroupedSections({ overrides, className }: LaunchpadGroupedSectionsProps): react_jsx_runtime.JSX.Element;
65
+ * Fully controlled by `useLaunchpadFilter()` state passed in from the page. */
66
+ declare function LaunchpadGroupedSections({ overrides, query, activeGroups, onClearFilters, className }: LaunchpadGroupedSectionsProps): react_jsx_runtime.JSX.Element;
45
67
 
46
- export { LaunchpadGroupedSections, type LaunchpadGroupedSectionsProps, LaunchpadServiceCard, type LaunchpadServiceCardProps, SERVICE_HUES, type ServiceOverride, type ServiceOverrides };
68
+ export { LaunchpadGroupedSections, type LaunchpadGroupedSectionsProps, LaunchpadServiceCard, type LaunchpadServiceCardProps, SERVICE_HUES, type ServiceOverride, type ServiceOverrides, useLaunchpadFilter };
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useMemo, useState } from "react";
4
4
  import Link from "next/link";
5
5
  import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
@@ -9,29 +9,28 @@ import {
9
9
  LAUNCHPAD_SERVICE_DEFINITIONS,
10
10
  LAUNCHPAD_SERVICE_GROUPS
11
11
  } from "../data/launchpad-services.js";
12
- import { LaunchpadFilterBar } from "./launchpad-filter-bar.js";
13
12
  const DEFAULT_HUE = {
14
13
  text: "text-sky-600 dark:text-sky-400",
15
- solid: "bg-sky-500",
16
- border: "border-sky-500/25",
17
- pill: "bg-gradient-to-r from-sky-500 to-blue-600"
14
+ solid: "bg-sky-600",
15
+ border: "border-sky-500/30 dark:border-sky-400/25",
16
+ tint: "bg-sky-500/10"
18
17
  };
19
18
  const SERVICE_HUES = {
20
19
  // Single Editions — blue + green
21
- "mint-ip-asset": { text: "text-blue-600 dark:text-blue-400", solid: "bg-blue-500", border: "border-blue-500/25", pill: "bg-gradient-to-r from-blue-500 to-sky-600" },
22
- "create-collection": { text: "text-green-600 dark:text-green-400", solid: "bg-green-500", border: "border-green-500/25", pill: "bg-gradient-to-r from-green-500 to-emerald-600" },
20
+ "mint-ip-asset": { text: "text-blue-600 dark:text-blue-400", solid: "bg-blue-600", border: "border-blue-500/30 dark:border-blue-400/25", tint: "bg-blue-500/10" },
21
+ "create-collection": { text: "text-green-600 dark:text-green-400", solid: "bg-green-600", border: "border-green-500/30 dark:border-green-400/25", tint: "bg-green-500/10" },
23
22
  // Limited Editions — purple + red
24
- "ip-collection-1155": { text: "text-purple-600 dark:text-purple-400", solid: "bg-purple-500", border: "border-purple-500/25", pill: "bg-gradient-to-r from-purple-500 to-violet-600" },
25
- "mint-editions": { text: "text-red-600 dark:text-red-400", solid: "bg-red-500", border: "border-red-500/25", pill: "bg-gradient-to-r from-red-500 to-rose-600" },
23
+ "ip-collection-1155": { text: "text-purple-600 dark:text-purple-400", solid: "bg-purple-600", border: "border-purple-500/30 dark:border-purple-400/25", tint: "bg-purple-500/10" },
24
+ "mint-editions": { text: "text-red-600 dark:text-red-400", solid: "bg-red-600", border: "border-red-500/30 dark:border-red-400/25", tint: "bg-red-500/10" },
26
25
  // Creator Coins & Memecoins — yellow + orange
27
- "creator-coins": { text: "text-yellow-600 dark:text-yellow-400", solid: "bg-yellow-500", border: "border-yellow-500/25", pill: "bg-gradient-to-r from-yellow-500 to-amber-500" },
28
- "claim-memecoin": { text: "text-orange-600 dark:text-orange-400", solid: "bg-orange-500", border: "border-orange-500/25", pill: "bg-gradient-to-r from-orange-500 to-amber-600" },
29
- "collection-drop": { text: "text-orange-600 dark:text-orange-400", solid: "bg-orange-500", border: "border-orange-500/25", pill: "bg-gradient-to-r from-orange-500 to-red-500" },
30
- "pop-protocol": { text: "text-emerald-600 dark:text-emerald-400", solid: "bg-emerald-500", border: "border-emerald-500/25", pill: "bg-gradient-to-r from-emerald-500 to-green-600" },
31
- "remix-asset": { text: "text-indigo-600 dark:text-indigo-400", solid: "bg-indigo-500", border: "border-indigo-500/25", pill: "bg-gradient-to-r from-indigo-500 to-blue-600" },
32
- "claim-username": { text: "text-violet-600 dark:text-violet-400", solid: "bg-violet-500", border: "border-violet-500/25", pill: "bg-gradient-to-r from-violet-500 to-fuchsia-600" },
33
- "claim-collection": { text: "text-cyan-600 dark:text-cyan-400", solid: "bg-cyan-500", border: "border-cyan-500/25", pill: "bg-gradient-to-r from-cyan-500 to-sky-600" },
34
- "claim-collection-name": { text: "text-pink-600 dark:text-pink-400", solid: "bg-pink-500", border: "border-pink-500/25", pill: "bg-gradient-to-r from-pink-500 to-rose-600" }
26
+ "creator-coins": { text: "text-yellow-600 dark:text-yellow-400", solid: "bg-yellow-600", border: "border-yellow-500/30 dark:border-yellow-400/25", tint: "bg-yellow-500/10" },
27
+ "claim-memecoin": { text: "text-orange-600 dark:text-orange-400", solid: "bg-orange-600", border: "border-orange-500/30 dark:border-orange-400/25", tint: "bg-orange-500/10" },
28
+ "collection-drop": { text: "text-orange-600 dark:text-orange-400", solid: "bg-orange-600", border: "border-orange-500/30 dark:border-orange-400/25", tint: "bg-orange-500/10" },
29
+ "pop-protocol": { text: "text-emerald-600 dark:text-emerald-400", solid: "bg-emerald-600", border: "border-emerald-500/30 dark:border-emerald-400/25", tint: "bg-emerald-500/10" },
30
+ "remix-asset": { text: "text-indigo-600 dark:text-indigo-400", solid: "bg-indigo-600", border: "border-indigo-500/30 dark:border-indigo-400/25", tint: "bg-indigo-500/10" },
31
+ "claim-username": { text: "text-violet-600 dark:text-violet-400", solid: "bg-violet-600", border: "border-violet-500/30 dark:border-violet-400/25", tint: "bg-violet-500/10" },
32
+ "claim-collection": { text: "text-cyan-600 dark:text-cyan-400", solid: "bg-cyan-600", border: "border-cyan-500/30 dark:border-cyan-400/25", tint: "bg-cyan-500/10" },
33
+ "claim-collection-name": { text: "text-pink-600 dark:text-pink-400", solid: "bg-pink-600", border: "border-pink-500/30 dark:border-pink-400/25", tint: "bg-pink-500/10" }
35
34
  };
36
35
  function LaunchpadServiceCard({ def, override = {}, featured = false, index = 0 }) {
37
36
  const { key, icon: Icon, title, browseLinkLabel, features } = def;
@@ -51,107 +50,65 @@ function LaunchpadServiceCard({ def, override = {}, featured = false, index = 0
51
50
  viewport: { once: true, margin: "-40px" },
52
51
  transition: { duration: 0.45, delay: index * 0.06, ease: [0.25, 0.46, 0.45, 0.94] },
53
52
  className: cn("flex", featured && "sm:col-span-2"),
54
- children: /* @__PURE__ */ jsxs(
53
+ children: /* @__PURE__ */ jsx(
55
54
  "div",
56
55
  {
57
56
  className: cn(
58
- "group relative flex flex-1 rounded-2xl transition-transform duration-300 ease-out",
59
- // the hue gradient frame lives in the 1px padding; press + lift are the interaction
60
- live && "p-px active:scale-[0.985] sm:hover:-translate-y-1 motion-reduce:transform-none motion-reduce:transition-none"
57
+ "group relative flex flex-1 flex-col overflow-hidden min-h-[200px] rounded-2xl border bg-card transition-all duration-300 ease-out",
58
+ live ? cn("active:scale-[0.985] sm:hover:-translate-y-1 motion-reduce:transform-none motion-reduce:transition-none", hue.border, "sm:hover:shadow-md sm:hover:shadow-black/5 dark:sm:hover:shadow-black/20") : "border-border/30 opacity-75"
61
59
  ),
62
- children: [
63
- live && /* @__PURE__ */ jsx(
60
+ children: /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col flex-1 p-5 sm:p-6 gap-3 sm:gap-4", children: [
61
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
62
+ live ? /* @__PURE__ */ jsx("span", { className: cn("flex h-12 w-12 items-center justify-center rounded-xl", hue.tint), children: /* @__PURE__ */ jsx(Icon, { className: cn("h-6 w-6", hue.text) }) }) : /* @__PURE__ */ jsx(Icon, { className: "h-8 w-8 shrink-0 text-muted-foreground/50" }),
63
+ !live && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-[11px] font-medium text-muted-foreground/60 pt-1", children: [
64
+ /* @__PURE__ */ jsx(Lock, { className: "h-3 w-3" }),
65
+ "Coming soon"
66
+ ] })
67
+ ] }),
68
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
69
+ /* @__PURE__ */ jsx("h3", { className: "text-xl sm:text-2xl font-black tracking-tight leading-snug", children: title }),
70
+ /* @__PURE__ */ jsx("p", { className: cn("text-sm leading-relaxed", live ? "text-muted-foreground" : "text-muted-foreground/60", !featured && "max-w-[36ch]"), children: blurb })
71
+ ] }),
72
+ live && features.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: features.slice(0, 2).map((feature) => /* @__PURE__ */ jsxs(
64
73
  "span",
65
74
  {
66
- "aria-hidden": true,
67
- className: cn(
68
- "absolute inset-0 rounded-2xl opacity-40 sm:group-hover:opacity-100 transition-opacity duration-500 pointer-events-none",
69
- hue.pill
70
- )
71
- }
72
- ),
73
- /* @__PURE__ */ jsxs(
74
- "div",
75
- {
76
- className: cn(
77
- "relative flex flex-1 flex-col overflow-hidden min-h-[200px]",
78
- live ? "rounded-[15px] bg-card" : "rounded-2xl border border-border/30 bg-card dark:bg-white/[0.02] opacity-75"
79
- ),
75
+ className: "inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-muted/40 border border-border/30 text-xs font-medium text-muted-foreground",
80
76
  children: [
81
- live && /* @__PURE__ */ jsxs(Fragment, { children: [
82
- /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: cn("absolute -top-16 -left-12 h-48 w-48 rounded-full blur-3xl opacity-[0.18] sm:group-hover:opacity-30 transition-opacity duration-500 pointer-events-none", hue.solid) }),
83
- /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: cn("absolute -bottom-24 -right-16 h-56 w-56 rounded-full blur-3xl opacity-[0.10] sm:group-hover:opacity-20 transition-opacity duration-500 pointer-events-none", hue.solid) })
84
- ] }),
85
- /* @__PURE__ */ jsx(
86
- "div",
87
- {
88
- "aria-hidden": true,
89
- className: cn(
90
- "absolute -right-8 -bottom-10 select-none pointer-events-none transition-all duration-500",
91
- live ? "opacity-[0.05] sm:group-hover:opacity-[0.09] sm:group-hover:-translate-y-1 motion-reduce:transform-none" : "opacity-[0.03]"
92
- ),
93
- children: /* @__PURE__ */ jsx(Icon, { className: cn("h-32 w-32", live ? hue.text : "text-muted-foreground") })
94
- }
95
- ),
96
- /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col flex-1 p-5 sm:p-6 gap-3 sm:gap-4", children: [
97
- /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
98
- live ? /* @__PURE__ */ jsxs("div", { className: "relative", children: [
99
- /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: cn("absolute -inset-2 rounded-2xl blur-xl opacity-40 sm:group-hover:opacity-60 transition-opacity duration-500", hue.solid) }),
100
- /* @__PURE__ */ jsx("span", { className: cn("relative flex h-12 w-12 items-center justify-center rounded-2xl text-white shadow-lg shadow-black/25 transition-transform duration-300 sm:group-hover:scale-105 motion-reduce:transform-none", hue.pill), children: /* @__PURE__ */ jsx(Icon, { className: "h-6 w-6" }) })
101
- ] }) : /* @__PURE__ */ jsx(Icon, { className: "h-8 w-8 shrink-0 text-muted-foreground/50" }),
102
- !live && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-[11px] font-medium text-muted-foreground/60 pt-1", children: [
103
- /* @__PURE__ */ jsx(Lock, { className: "h-3 w-3" }),
104
- "Coming soon"
105
- ] })
106
- ] }),
107
- /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
108
- /* @__PURE__ */ jsx("h3", { className: "text-xl sm:text-2xl font-black tracking-tight leading-snug", children: title }),
109
- /* @__PURE__ */ jsx("p", { className: cn("text-sm leading-relaxed", live ? "text-muted-foreground" : "text-muted-foreground/60", !featured && "max-w-[36ch]"), children: blurb })
110
- ] }),
111
- live && features.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: features.slice(0, 2).map((feature) => /* @__PURE__ */ jsxs(
112
- "span",
113
- {
114
- className: "inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-muted/40 border border-border/30 text-xs font-medium text-muted-foreground",
115
- children: [
116
- /* @__PURE__ */ jsx(Check, { className: cn("h-3 w-3 shrink-0", hue.text) }),
117
- feature
118
- ]
119
- },
120
- feature
121
- )) }),
122
- live && href && /* @__PURE__ */ jsx(Link, { href, "aria-label": `${def.cta} \u2014 ${title}`, className: "absolute inset-0 z-10" }),
123
- /* @__PURE__ */ jsxs("div", { className: "mt-auto pt-1 flex items-end justify-between gap-3", children: [
124
- live && browseHref && browseLinkLabel ? /* @__PURE__ */ jsxs(
125
- Link,
126
- {
127
- href: browseHref,
128
- className: "relative z-20 inline-flex items-center gap-1 text-xs font-medium text-muted-foreground active:text-foreground sm:hover:text-foreground transition-colors",
129
- children: [
130
- browseLinkLabel,
131
- /* @__PURE__ */ jsx(ArrowRight, { className: "h-3 w-3" })
132
- ]
133
- }
134
- ) : /* @__PURE__ */ jsx("span", {}),
135
- live && /* @__PURE__ */ jsxs(
136
- "span",
137
- {
138
- className: cn(
139
- "inline-flex items-center gap-2 h-10 px-5 rounded-full",
140
- "text-sm font-semibold text-white shadow-lg shadow-black/25",
141
- hue.pill
142
- ),
143
- children: [
144
- def.cta,
145
- /* @__PURE__ */ jsx(ArrowRight, { className: "h-4 w-4 transition-transform duration-300 sm:group-hover:translate-x-0.5 motion-reduce:transform-none" })
146
- ]
147
- }
148
- )
149
- ] })
150
- ] })
77
+ /* @__PURE__ */ jsx(Check, { className: cn("h-3 w-3 shrink-0", hue.text) }),
78
+ feature
151
79
  ]
152
- }
153
- )
154
- ]
80
+ },
81
+ feature
82
+ )) }),
83
+ live && href && /* @__PURE__ */ jsx(Link, { href, "aria-label": `${def.cta} \u2014 ${title}`, className: "absolute inset-0 z-10" }),
84
+ /* @__PURE__ */ jsxs("div", { className: "mt-auto pt-1 flex items-end justify-between gap-3", children: [
85
+ live && browseHref && browseLinkLabel ? /* @__PURE__ */ jsxs(
86
+ Link,
87
+ {
88
+ href: browseHref,
89
+ className: "relative z-20 inline-flex items-center gap-1 text-xs font-medium text-muted-foreground active:text-foreground sm:hover:text-foreground transition-colors",
90
+ children: [
91
+ browseLinkLabel,
92
+ /* @__PURE__ */ jsx(ArrowRight, { className: "h-3 w-3" })
93
+ ]
94
+ }
95
+ ) : /* @__PURE__ */ jsx("span", {}),
96
+ live && /* @__PURE__ */ jsxs(
97
+ "span",
98
+ {
99
+ className: cn(
100
+ "inline-flex items-center gap-2 h-10 px-5 rounded-full",
101
+ "text-sm font-semibold text-white",
102
+ hue.solid
103
+ ),
104
+ children: [
105
+ def.cta,
106
+ /* @__PURE__ */ jsx(ArrowRight, { className: "h-4 w-4 transition-transform duration-300 sm:group-hover:translate-x-0.5 motion-reduce:transform-none" })
107
+ ]
108
+ }
109
+ )
110
+ ] })
111
+ ] })
155
112
  }
156
113
  )
157
114
  }
@@ -191,7 +148,7 @@ function ComingSoonStrip({ group, defs }) {
191
148
  ] }, key)) })
192
149
  ] });
193
150
  }
194
- function LaunchpadGroupedSections({ overrides, className }) {
151
+ function useLaunchpadFilter() {
195
152
  const [query, setQuery] = useState("");
196
153
  const [activeGroups, setActiveGroups] = useState(/* @__PURE__ */ new Set());
197
154
  const filterableGroups = LAUNCHPAD_SERVICE_GROUPS.filter((g) => g.key !== "coming-soon");
@@ -215,74 +172,75 @@ function LaunchpadGroupedSections({ overrides, className }) {
215
172
  // eslint-disable-next-line react-hooks/exhaustive-deps
216
173
  [query, activeGroups]
217
174
  );
218
- return /* @__PURE__ */ jsxs("div", { className: cn("space-y-8 sm:space-y-10", className), children: [
219
- /* @__PURE__ */ jsxs("div", { className: "pt-6 sm:pt-8 border-t border-border/40 space-y-3", children: [
220
- /* @__PURE__ */ jsx("p", { className: "text-[10px] font-bold uppercase tracking-widest text-muted-foreground", children: "Browse services" }),
221
- /* @__PURE__ */ jsx(
222
- LaunchpadFilterBar,
223
- {
224
- query,
225
- onQueryChange: setQuery,
226
- groups: filterableGroups,
227
- activeGroups,
228
- onToggleGroup: toggleGroup,
229
- resultCount: totalMatches
230
- }
231
- )
232
- ] }),
233
- totalMatches === 0 ? /* @__PURE__ */ jsxs("div", { className: "text-center py-16 space-y-3", children: [
234
- /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold", children: "No services match" }),
235
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Try a different search or clear your filters." }),
236
- /* @__PURE__ */ jsx(
237
- "button",
238
- {
239
- type: "button",
240
- onClick: () => {
241
- setQuery("");
242
- setActiveGroups(/* @__PURE__ */ new Set());
243
- },
244
- className: "inline-flex items-center h-9 px-4 rounded-full text-sm font-semibold bg-primary text-primary-foreground",
245
- children: "Clear filters"
246
- }
247
- )
248
- ] }) : /* @__PURE__ */ jsx("div", { className: "space-y-20 sm:space-y-28", children: /* @__PURE__ */ jsx(AnimatePresence, { children: LAUNCHPAD_SERVICE_GROUPS.map((group) => {
249
- if (group.key === "coming-soon") {
250
- const inActiveGroup = (d) => activeGroups.size === 0 || activeGroups.has(d.group);
251
- const inSearch = (d) => query.trim() === "" || `${d.title} ${d.blurb} ${d.subtitle}`.toLowerCase().includes(query.trim().toLowerCase());
252
- const comingSoonDefs = LAUNCHPAD_SERVICE_DEFINITIONS.filter(
253
- (d) => d.group === group.key && inActiveGroup(d) && inSearch(d)
254
- );
255
- if (comingSoonDefs.length === 0) return null;
256
- return /* @__PURE__ */ jsx(ComingSoonStrip, { group, defs: comingSoonDefs }, group.key);
175
+ const clear = () => {
176
+ setQuery("");
177
+ setActiveGroups(/* @__PURE__ */ new Set());
178
+ };
179
+ return { query, setQuery, activeGroups, toggleGroup, filterableGroups, totalMatches, clear };
180
+ }
181
+ function LaunchpadGroupedSections({ overrides, query, activeGroups, onClearFilters, className }) {
182
+ const matches = (def) => {
183
+ if (activeGroups.size > 0 && !activeGroups.has(def.group)) return false;
184
+ if (def.status !== "live") return false;
185
+ if (query.trim() === "") return true;
186
+ const haystack = `${def.title} ${def.blurb} ${def.subtitle}`.toLowerCase();
187
+ return haystack.includes(query.trim().toLowerCase());
188
+ };
189
+ const totalMatches = useMemo(
190
+ () => LAUNCHPAD_SERVICE_DEFINITIONS.filter(matches).length,
191
+ // eslint-disable-next-line react-hooks/exhaustive-deps
192
+ [query, activeGroups]
193
+ );
194
+ return /* @__PURE__ */ jsx("div", { className: cn("space-y-8 sm:space-y-10", className), children: totalMatches === 0 ? /* @__PURE__ */ jsxs("div", { className: "text-center py-16 space-y-3", children: [
195
+ /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold", children: "No services match" }),
196
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Try a different search or clear your filters." }),
197
+ /* @__PURE__ */ jsx(
198
+ "button",
199
+ {
200
+ type: "button",
201
+ onClick: onClearFilters,
202
+ className: "inline-flex items-center h-9 px-4 rounded-full text-sm font-semibold bg-primary text-primary-foreground",
203
+ children: "Clear filters"
257
204
  }
258
- const defs = LAUNCHPAD_SERVICE_DEFINITIONS.filter((d) => d.group === group.key && matches(d));
259
- if (defs.length === 0) return null;
260
- const showPopHowItWorks = group.key === "community" && defs.some((d) => d.key === "pop-protocol");
261
- return /* @__PURE__ */ jsxs(
262
- motion.div,
263
- {
264
- layout: true,
265
- initial: { opacity: 0, y: 8 },
266
- animate: { opacity: 1, y: 0 },
267
- exit: { opacity: 0 },
268
- transition: { duration: 0.3, ease: [0.25, 0.46, 0.45, 0.94] },
269
- className: "space-y-7 sm:space-y-10",
270
- children: [
271
- /* @__PURE__ */ jsx(GroupHeader, { group }),
272
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-5", children: [
273
- defs.map((def, i) => /* @__PURE__ */ jsx(LaunchpadServiceCard, { def, override: overrides[def.key], index: i }, def.key)),
274
- showPopHowItWorks && /* @__PURE__ */ jsx(PopHowItWorks, {})
275
- ] })
276
- ]
277
- },
278
- group.key
205
+ )
206
+ ] }) : /* @__PURE__ */ jsx("div", { className: "space-y-20 sm:space-y-28", children: /* @__PURE__ */ jsx(AnimatePresence, { children: LAUNCHPAD_SERVICE_GROUPS.map((group) => {
207
+ if (group.key === "coming-soon") {
208
+ const inActiveGroup = (d) => activeGroups.size === 0 || activeGroups.has(d.group);
209
+ const inSearch = (d) => query.trim() === "" || `${d.title} ${d.blurb} ${d.subtitle}`.toLowerCase().includes(query.trim().toLowerCase());
210
+ const comingSoonDefs = LAUNCHPAD_SERVICE_DEFINITIONS.filter(
211
+ (d) => d.group === group.key && inActiveGroup(d) && inSearch(d)
279
212
  );
280
- }) }) })
281
- ] });
213
+ if (comingSoonDefs.length === 0) return null;
214
+ return /* @__PURE__ */ jsx(ComingSoonStrip, { group, defs: comingSoonDefs }, group.key);
215
+ }
216
+ const defs = LAUNCHPAD_SERVICE_DEFINITIONS.filter((d) => d.group === group.key && matches(d));
217
+ if (defs.length === 0) return null;
218
+ const showPopHowItWorks = group.key === "community" && defs.some((d) => d.key === "pop-protocol");
219
+ return /* @__PURE__ */ jsxs(
220
+ motion.div,
221
+ {
222
+ layout: true,
223
+ initial: { opacity: 0, y: 8 },
224
+ animate: { opacity: 1, y: 0 },
225
+ exit: { opacity: 0 },
226
+ transition: { duration: 0.3, ease: [0.25, 0.46, 0.45, 0.94] },
227
+ className: "space-y-7 sm:space-y-10",
228
+ children: [
229
+ /* @__PURE__ */ jsx(GroupHeader, { group }),
230
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-5", children: [
231
+ defs.map((def, i) => /* @__PURE__ */ jsx(LaunchpadServiceCard, { def, override: overrides[def.key], index: i }, def.key)),
232
+ showPopHowItWorks && /* @__PURE__ */ jsx(PopHowItWorks, {})
233
+ ] })
234
+ ]
235
+ },
236
+ group.key
237
+ );
238
+ }) }) }) });
282
239
  }
283
240
  export {
284
241
  LaunchpadGroupedSections,
285
242
  LaunchpadServiceCard,
286
- SERVICE_HUES
243
+ SERVICE_HUES,
244
+ useLaunchpadFilter
287
245
  };
288
246
  //# sourceMappingURL=launchpad-services.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/launchpad-services.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * Launchpad grouped services — the single source for the /launchpad page UI\n * in medialane-io and medialane-starknet.\n *\n * Card philosophy (creator-first redesign 2026-06-10; \"living color cards\"\n * high-fidelity pass 2026-06-27): the whole card is the action. One title, one\n * creator-language sentence (def.blurb), one unique hue per service. The hue is\n * not locked in a thin border — it saturates the whole card as ambient light:\n * an aurora glow behind a gradient icon tile, a hairline gradient frame that\n * ignites on interaction, a staggered entrance reveal, and press/hover\n * microinteractions. Touch-first (press states are primary; hover is desktop\n * polish, gated to sm+ and degraded under reduced-motion).\n *\n * Apps own: hrefs + per-app rollout status flips. Everything else lives here.\n */\n\nimport { useMemo, useState } from \"react\";\nimport Link from \"next/link\";\nimport { AnimatePresence, motion, useReducedMotion } from \"framer-motion\";\nimport { Lock, ArrowRight, Check } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport {\n LAUNCHPAD_SERVICE_DEFINITIONS,\n LAUNCHPAD_SERVICE_GROUPS,\n type ServiceDefinition,\n type ServiceGroup,\n type ServiceGroupDefinition,\n type ServiceStatus,\n} from \"../data/launchpad-services.js\";\nimport { LaunchpadFilterBar } from \"./launchpad-filter-bar.js\";\n\n// ── Per-app injection points ─────────────────────────────────────────────────\n\nexport interface ServiceOverride {\n /** Primary destination — the whole card links here (required for live services) */\n href?: string;\n /** Secondary browse link href (pairs with the def's browseLinkLabel) */\n browseHref?: string;\n /** Per-app rollout flips (e.g. coins live on one app first) */\n status?: ServiceStatus;\n /** Per-app one-liner override (rarely needed) */\n blurb?: string;\n}\n\nexport type ServiceOverrides = Record<string, ServiceOverride>;\n\n// ── One unique hue per service — never repeated inside a group ───────────────\n\ninterface ServiceHue {\n /** icon tint (600 for light surfaces, 400 for dark) */\n text: string;\n /** solid fill — icon glow */\n solid: string;\n /** thin card border tint */\n border: string;\n /** vivid two-stop gradient for the action pill (asset-page button language) */\n pill: string;\n}\n\nconst DEFAULT_HUE: ServiceHue = {\n text: \"text-sky-600 dark:text-sky-400\",\n solid: \"bg-sky-500\",\n border: \"border-sky-500/25\",\n pill: \"bg-gradient-to-r from-sky-500 to-blue-600\",\n};\n\nexport const SERVICE_HUES: Record<string, ServiceHue> = {\n // Single Editions — blue + green\n \"mint-ip-asset\": { text: \"text-blue-600 dark:text-blue-400\", solid: \"bg-blue-500\", border: \"border-blue-500/25\", pill: \"bg-gradient-to-r from-blue-500 to-sky-600\" },\n \"create-collection\": { text: \"text-green-600 dark:text-green-400\", solid: \"bg-green-500\", border: \"border-green-500/25\", pill: \"bg-gradient-to-r from-green-500 to-emerald-600\" },\n // Limited Editions — purple + red\n \"ip-collection-1155\": { text: \"text-purple-600 dark:text-purple-400\", solid: \"bg-purple-500\", border: \"border-purple-500/25\", pill: \"bg-gradient-to-r from-purple-500 to-violet-600\" },\n \"mint-editions\": { text: \"text-red-600 dark:text-red-400\", solid: \"bg-red-500\", border: \"border-red-500/25\", pill: \"bg-gradient-to-r from-red-500 to-rose-600\" },\n // Creator Coins & Memecoins — yellow + orange\n \"creator-coins\": { text: \"text-yellow-600 dark:text-yellow-400\", solid: \"bg-yellow-500\", border: \"border-yellow-500/25\", pill: \"bg-gradient-to-r from-yellow-500 to-amber-500\" },\n \"claim-memecoin\": { text: \"text-orange-600 dark:text-orange-400\", solid: \"bg-orange-500\", border: \"border-orange-500/25\", pill: \"bg-gradient-to-r from-orange-500 to-amber-600\" },\n \"collection-drop\": { text: \"text-orange-600 dark:text-orange-400\", solid: \"bg-orange-500\", border: \"border-orange-500/25\", pill: \"bg-gradient-to-r from-orange-500 to-red-500\" },\n \"pop-protocol\": { text: \"text-emerald-600 dark:text-emerald-400\", solid: \"bg-emerald-500\", border: \"border-emerald-500/25\", pill: \"bg-gradient-to-r from-emerald-500 to-green-600\" },\n \"remix-asset\": { text: \"text-indigo-600 dark:text-indigo-400\", solid: \"bg-indigo-500\", border: \"border-indigo-500/25\", pill: \"bg-gradient-to-r from-indigo-500 to-blue-600\" },\n \"claim-username\": { text: \"text-violet-600 dark:text-violet-400\", solid: \"bg-violet-500\", border: \"border-violet-500/25\", pill: \"bg-gradient-to-r from-violet-500 to-fuchsia-600\" },\n \"claim-collection\": { text: \"text-cyan-600 dark:text-cyan-400\", solid: \"bg-cyan-500\", border: \"border-cyan-500/25\", pill: \"bg-gradient-to-r from-cyan-500 to-sky-600\" },\n \"claim-collection-name\": { text: \"text-pink-600 dark:text-pink-400\", solid: \"bg-pink-500\", border: \"border-pink-500/25\", pill: \"bg-gradient-to-r from-pink-500 to-rose-600\" },\n};\n\n// ── Service card — the whole card is the action ─────────────────────────────\n\nexport interface LaunchpadServiceCardProps {\n def: ServiceDefinition;\n override?: ServiceOverride;\n /** Showcase layout — spans the full grid width (e.g. POP Protocol) */\n featured?: boolean;\n /** Grid position — drives the staggered entrance reveal. */\n index?: number;\n}\n\nexport function LaunchpadServiceCard({ def, override = {}, featured = false, index = 0 }: LaunchpadServiceCardProps) {\n const { key, icon: Icon, title, browseLinkLabel, features } = def;\n const status = override.status ?? def.status;\n const blurb = override.blurb ?? def.blurb;\n const { href, browseHref } = override;\n const reduceMotion = useReducedMotion();\n\n const live = status === \"live\";\n const hue = SERVICE_HUES[key] ?? DEFAULT_HUE;\n\n return (\n <motion.div\n layout={!reduceMotion}\n initial={{ opacity: 0, y: reduceMotion ? 0 : 16 }}\n whileInView={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0 }}\n viewport={{ once: true, margin: \"-40px\" }}\n transition={{ duration: 0.45, delay: index * 0.06, ease: [0.25, 0.46, 0.45, 0.94] }}\n className={cn(\"flex\", featured && \"sm:col-span-2\")}\n >\n <div\n className={cn(\n \"group relative flex flex-1 rounded-2xl transition-transform duration-300 ease-out\",\n // the hue gradient frame lives in the 1px padding; press + lift are the interaction\n live && \"p-px active:scale-[0.985] sm:hover:-translate-y-1 motion-reduce:transform-none motion-reduce:transition-none\",\n )}\n >\n {/* Hue gradient frame — a quiet hairline that ignites on interaction (live only) */}\n {live && (\n <span\n aria-hidden\n className={cn(\n \"absolute inset-0 rounded-2xl opacity-40 sm:group-hover:opacity-100 transition-opacity duration-500 pointer-events-none\",\n hue.pill,\n )}\n />\n )}\n\n {/* Inner surface */}\n <div\n className={cn(\n \"relative flex flex-1 flex-col overflow-hidden min-h-[200px]\",\n live ? \"rounded-[15px] bg-card\" : \"rounded-2xl border border-border/30 bg-card dark:bg-white/[0.02] opacity-75\",\n )}\n >\n {/* Aurora light-leaks — the hue saturates the whole card as ambient light */}\n {live && (\n <>\n <span aria-hidden className={cn(\"absolute -top-16 -left-12 h-48 w-48 rounded-full blur-3xl opacity-[0.18] sm:group-hover:opacity-30 transition-opacity duration-500 pointer-events-none\", hue.solid)} />\n <span aria-hidden className={cn(\"absolute -bottom-24 -right-16 h-56 w-56 rounded-full blur-3xl opacity-[0.10] sm:group-hover:opacity-20 transition-opacity duration-500 pointer-events-none\", hue.solid)} />\n </>\n )}\n\n {/* Giant watermark icon, ghosted in the corner — drifts on hover */}\n <div\n aria-hidden\n className={cn(\n \"absolute -right-8 -bottom-10 select-none pointer-events-none transition-all duration-500\",\n live ? \"opacity-[0.05] sm:group-hover:opacity-[0.09] sm:group-hover:-translate-y-1 motion-reduce:transform-none\" : \"opacity-[0.03]\",\n )}\n >\n <Icon className={cn(\"h-32 w-32\", live ? hue.text : \"text-muted-foreground\")} />\n </div>\n\n <div className=\"relative flex flex-col flex-1 p-5 sm:p-6 gap-3 sm:gap-4\">\n <div className=\"flex items-start justify-between gap-3\">\n {live ? (\n <div className=\"relative\">\n <span aria-hidden className={cn(\"absolute -inset-2 rounded-2xl blur-xl opacity-40 sm:group-hover:opacity-60 transition-opacity duration-500\", hue.solid)} />\n <span className={cn(\"relative flex h-12 w-12 items-center justify-center rounded-2xl text-white shadow-lg shadow-black/25 transition-transform duration-300 sm:group-hover:scale-105 motion-reduce:transform-none\", hue.pill)}>\n <Icon className=\"h-6 w-6\" />\n </span>\n </div>\n ) : (\n <Icon className=\"h-8 w-8 shrink-0 text-muted-foreground/50\" />\n )}\n {!live && (\n <span className=\"flex items-center gap-1 text-[11px] font-medium text-muted-foreground/60 pt-1\">\n <Lock className=\"h-3 w-3\" />\n Coming soon\n </span>\n )}\n </div>\n\n <div className=\"space-y-1.5\">\n <h3 className=\"text-xl sm:text-2xl font-black tracking-tight leading-snug\">{title}</h3>\n <p className={cn(\"text-sm leading-relaxed\", live ? \"text-muted-foreground\" : \"text-muted-foreground/60\", !featured && \"max-w-[36ch]\")}>\n {blurb}\n </p>\n </div>\n\n {/* Feature showcase — plain-language chips, capped at 2 for card density */}\n {live && features.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5\">\n {features.slice(0, 2).map((feature) => (\n <span\n key={feature}\n className=\"inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-muted/40 border border-border/30 text-xs font-medium text-muted-foreground\"\n >\n <Check className={cn(\"h-3 w-3 shrink-0\", hue.text)} />\n {feature}\n </span>\n ))}\n </div>\n )}\n\n {/* Stretched link — the whole card is the action; the pill is the visual cue */}\n {live && href && <Link href={href} aria-label={`${def.cta} — ${title}`} className=\"absolute inset-0 z-10\" />}\n\n <div className=\"mt-auto pt-1 flex items-end justify-between gap-3\">\n {live && browseHref && browseLinkLabel ? (\n <Link\n href={browseHref}\n className=\"relative z-20 inline-flex items-center gap-1 text-xs font-medium text-muted-foreground active:text-foreground sm:hover:text-foreground transition-colors\"\n >\n {browseLinkLabel}\n <ArrowRight className=\"h-3 w-3\" />\n </Link>\n ) : (\n <span />\n )}\n {live && (\n <span\n className={cn(\n \"inline-flex items-center gap-2 h-10 px-5 rounded-full\",\n \"text-sm font-semibold text-white shadow-lg shadow-black/25\",\n hue.pill,\n )}\n >\n {def.cta}\n <ArrowRight className=\"h-4 w-4 transition-transform duration-300 sm:group-hover:translate-x-0.5 motion-reduce:transform-none\" />\n </span>\n )}\n </div>\n </div>\n </div>\n </div>\n </motion.div>\n );\n}\n\n// ── Group sections ───────────────────────────────────────────────────────────\n\n/** Vivid gradient titles give special sections identity without boxing them in\n * (no panels inside panels — color carries the distinction, space does the rest). */\nconst GROUP_TITLE_ACCENTS: Partial<Record<ServiceGroup, string>> = {\n \"coins\": \"bg-gradient-to-r from-yellow-500 via-amber-500 to-orange-500 bg-clip-text text-transparent\",\n \"community\": \"bg-gradient-to-r from-emerald-500 to-green-600 bg-clip-text text-transparent\",\n};\n\nfunction GroupHeader({ group }: { group: ServiceGroupDefinition }) {\n return (\n <div className=\"space-y-2\">\n <h2 className={cn(\"text-3xl sm:text-4xl font-black tracking-tight\", GROUP_TITLE_ACCENTS[group.key])}>\n {group.title}\n </h2>\n <p className=\"text-base sm:text-lg text-muted-foreground leading-relaxed max-w-2xl\">{group.tagline}</p>\n </div>\n );\n}\n\n/** POP Protocol companion column — open how-it-works steps, no extra panel. */\nconst POP_STEPS = [\n { title: \"Create your event badge\", body: \"Name it, add your artwork, and set who can claim — ready in minutes.\" },\n { title: \"Share the claim link\", body: \"Your community claims for free — one badge per person, no faking.\" },\n { title: \"It stays with them forever\", body: \"Badges can't be sold or transferred — lasting proof they were there.\" },\n];\n\nfunction PopHowItWorks() {\n return (\n <div className=\"flex flex-col justify-center gap-5 sm:gap-6 px-2 py-4 sm:py-0 sm:pl-6\">\n {POP_STEPS.map((step, i) => (\n <div key={step.title} className=\"flex gap-4\">\n <span className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 text-sm font-bold\">\n {i + 1}\n </span>\n <div className=\"space-y-0.5\">\n <p className=\"font-semibold\">{step.title}</p>\n <p className=\"text-sm text-muted-foreground leading-relaxed\">{step.body}</p>\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nfunction ComingSoonStrip({ group, defs }: { group: ServiceGroupDefinition; defs: ServiceDefinition[] }) {\n return (\n <div className=\"rounded-2xl border border-border/40 p-5\">\n <p className=\"font-semibold text-sm\">{group.title}</p>\n <p className=\"text-sm text-muted-foreground mt-0.5\">{group.tagline}</p>\n <div className=\"flex flex-wrap gap-2 mt-4\">\n {defs.map(({ key, icon: Icon, title }) => (\n <div key={key} className=\"flex items-center gap-2 px-3 py-2 rounded-full bg-muted/30 border border-border/25\">\n <Icon className=\"h-3.5 w-3.5 text-muted-foreground/60\" />\n <span className=\"text-xs font-medium text-muted-foreground\">{title}</span>\n </div>\n ))}\n </div>\n </div>\n );\n}\n\nexport interface LaunchpadGroupedSectionsProps {\n /** Per-app hrefs / rollout flips, keyed by service key. */\n overrides: ServiceOverrides;\n className?: string;\n}\n\n/** The full grouped launchpad services block — section order comes from\n * LAUNCHPAD_SERVICE_GROUPS; cards from LAUNCHPAD_SERVICE_DEFINITIONS.\n * Owns the search/filter state so the grid can react to it live. */\nexport function LaunchpadGroupedSections({ overrides, className }: LaunchpadGroupedSectionsProps) {\n const [query, setQuery] = useState(\"\");\n const [activeGroups, setActiveGroups] = useState<Set<ServiceGroup>>(new Set());\n\n const filterableGroups = LAUNCHPAD_SERVICE_GROUPS.filter((g) => g.key !== \"coming-soon\");\n\n const toggleGroup = (key: ServiceGroup) => {\n setActiveGroups((prev) => {\n const next = new Set(prev);\n if (next.has(key)) next.delete(key);\n else next.add(key);\n return next;\n });\n };\n\n const matches = (def: ServiceDefinition): boolean => {\n if (activeGroups.size > 0 && !activeGroups.has(def.group)) return false;\n if (def.status !== \"live\") return false;\n if (query.trim() === \"\") return true;\n const haystack = `${def.title} ${def.blurb} ${def.subtitle}`.toLowerCase();\n return haystack.includes(query.trim().toLowerCase());\n };\n\n const totalMatches = useMemo(\n () => LAUNCHPAD_SERVICE_DEFINITIONS.filter(matches).length,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [query, activeGroups],\n );\n\n return (\n <div className={cn(\"space-y-8 sm:space-y-10\", className)}>\n <div className=\"pt-6 sm:pt-8 border-t border-border/40 space-y-3\">\n <p className=\"text-[10px] font-bold uppercase tracking-widest text-muted-foreground\">\n Browse services\n </p>\n <LaunchpadFilterBar\n query={query}\n onQueryChange={setQuery}\n groups={filterableGroups}\n activeGroups={activeGroups}\n onToggleGroup={toggleGroup}\n resultCount={totalMatches}\n />\n </div>\n\n {totalMatches === 0 ? (\n <div className=\"text-center py-16 space-y-3\">\n <p className=\"text-lg font-semibold\">No services match</p>\n <p className=\"text-sm text-muted-foreground\">Try a different search or clear your filters.</p>\n <button\n type=\"button\"\n onClick={() => { setQuery(\"\"); setActiveGroups(new Set()); }}\n className=\"inline-flex items-center h-9 px-4 rounded-full text-sm font-semibold bg-primary text-primary-foreground\"\n >\n Clear filters\n </button>\n </div>\n ) : (\n <div className=\"space-y-20 sm:space-y-28\">\n <AnimatePresence>\n {LAUNCHPAD_SERVICE_GROUPS.map((group) => {\n if (group.key === \"coming-soon\") {\n // Coming-soon items are never \"live\" by definition, so they can't\n // go through matches() (which requires status === \"live\") — only\n // the group/search filters apply here, not the live-status gate.\n const inActiveGroup = (d: ServiceDefinition) => activeGroups.size === 0 || activeGroups.has(d.group);\n const inSearch = (d: ServiceDefinition) =>\n query.trim() === \"\" ||\n `${d.title} ${d.blurb} ${d.subtitle}`.toLowerCase().includes(query.trim().toLowerCase());\n const comingSoonDefs = LAUNCHPAD_SERVICE_DEFINITIONS.filter(\n (d) => d.group === group.key && inActiveGroup(d) && inSearch(d),\n );\n if (comingSoonDefs.length === 0) return null;\n return <ComingSoonStrip key={group.key} group={group} defs={comingSoonDefs} />;\n }\n\n const defs = LAUNCHPAD_SERVICE_DEFINITIONS.filter((d) => d.group === group.key && matches(d));\n if (defs.length === 0) return null;\n\n const showPopHowItWorks = group.key === \"community\" && defs.some((d) => d.key === \"pop-protocol\");\n\n return (\n <motion.div\n key={group.key}\n layout\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.3, ease: [0.25, 0.46, 0.45, 0.94] }}\n className=\"space-y-7 sm:space-y-10\"\n >\n <GroupHeader group={group} />\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-5\">\n {defs.map((def, i) => (\n <LaunchpadServiceCard key={def.key} def={def} override={overrides[def.key]} index={i} />\n ))}\n {showPopHowItWorks && <PopHowItWorks />}\n </div>\n </motion.div>\n );\n })}\n </AnimatePresence>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/launchpad-services.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * Launchpad grouped services — the single source for the /launchpad page UI\n * in medialane-io and medialane-starknet.\n *\n * Card philosophy (creator-first redesign 2026-06-10; flat card pass\n * 2026-07-05): the whole card is the action. One title, one creator-language\n * sentence (def.blurb), one unique hue per service. The hue lives in a soft\n * icon-tile tint and a static border — no ambient aurora glow, no blurred\n * light-leaks, no gradient wash behind the card. Those read as washed-out\n * noise on light backgrounds and don't match the rest of the design system\n * (flat surfaces, solid accents). A staggered entrance reveal and a quiet\n * border/lift microinteraction on hover are still here; hover is desktop\n * polish only (gated to sm+, degraded under reduced-motion).\n *\n * Filter state (search + group pills) lives in `useLaunchpadFilter()` so a\n * page can render `LaunchpadFilterBar` wherever it wants (e.g. right under\n * the h1) while `LaunchpadGroupedSections` — now fully controlled — renders\n * only the grid reacting to that same state.\n *\n * Apps own: hrefs + per-app rollout status flips. Everything else lives here.\n */\n\nimport { useMemo, useState } from \"react\";\nimport Link from \"next/link\";\nimport { AnimatePresence, motion, useReducedMotion } from \"framer-motion\";\nimport { Lock, ArrowRight, Check } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport {\n LAUNCHPAD_SERVICE_DEFINITIONS,\n LAUNCHPAD_SERVICE_GROUPS,\n type ServiceDefinition,\n type ServiceGroup,\n type ServiceGroupDefinition,\n type ServiceStatus,\n} from \"../data/launchpad-services.js\";\n\n// ── Per-app injection points ─────────────────────────────────────────────────\n\nexport interface ServiceOverride {\n /** Primary destination — the whole card links here (required for live services) */\n href?: string;\n /** Secondary browse link href (pairs with the def's browseLinkLabel) */\n browseHref?: string;\n /** Per-app rollout flips (e.g. coins live on one app first) */\n status?: ServiceStatus;\n /** Per-app one-liner override (rarely needed) */\n blurb?: string;\n}\n\nexport type ServiceOverrides = Record<string, ServiceOverride>;\n\n// ── One unique hue per service — never repeated inside a group ───────────────\n\ninterface ServiceHue {\n /** icon glyph tint (600 for light surfaces, 400 for dark) */\n text: string;\n /** solid fill — CTA button background */\n solid: string;\n /** static card border (visible on both themes, not just on hover) */\n border: string;\n /** soft icon-tile background (10% tint, both themes) */\n tint: string;\n}\n\nconst DEFAULT_HUE: ServiceHue = {\n text: \"text-sky-600 dark:text-sky-400\",\n solid: \"bg-sky-600\",\n border: \"border-sky-500/30 dark:border-sky-400/25\",\n tint: \"bg-sky-500/10\",\n};\n\nexport const SERVICE_HUES: Record<string, ServiceHue> = {\n // Single Editions — blue + green\n \"mint-ip-asset\": { text: \"text-blue-600 dark:text-blue-400\", solid: \"bg-blue-600\", border: \"border-blue-500/30 dark:border-blue-400/25\", tint: \"bg-blue-500/10\" },\n \"create-collection\": { text: \"text-green-600 dark:text-green-400\", solid: \"bg-green-600\", border: \"border-green-500/30 dark:border-green-400/25\", tint: \"bg-green-500/10\" },\n // Limited Editions — purple + red\n \"ip-collection-1155\": { text: \"text-purple-600 dark:text-purple-400\", solid: \"bg-purple-600\", border: \"border-purple-500/30 dark:border-purple-400/25\", tint: \"bg-purple-500/10\" },\n \"mint-editions\": { text: \"text-red-600 dark:text-red-400\", solid: \"bg-red-600\", border: \"border-red-500/30 dark:border-red-400/25\", tint: \"bg-red-500/10\" },\n // Creator Coins & Memecoins — yellow + orange\n \"creator-coins\": { text: \"text-yellow-600 dark:text-yellow-400\", solid: \"bg-yellow-600\", border: \"border-yellow-500/30 dark:border-yellow-400/25\", tint: \"bg-yellow-500/10\" },\n \"claim-memecoin\": { text: \"text-orange-600 dark:text-orange-400\", solid: \"bg-orange-600\", border: \"border-orange-500/30 dark:border-orange-400/25\", tint: \"bg-orange-500/10\" },\n \"collection-drop\": { text: \"text-orange-600 dark:text-orange-400\", solid: \"bg-orange-600\", border: \"border-orange-500/30 dark:border-orange-400/25\", tint: \"bg-orange-500/10\" },\n \"pop-protocol\": { text: \"text-emerald-600 dark:text-emerald-400\", solid: \"bg-emerald-600\", border: \"border-emerald-500/30 dark:border-emerald-400/25\", tint: \"bg-emerald-500/10\" },\n \"remix-asset\": { text: \"text-indigo-600 dark:text-indigo-400\", solid: \"bg-indigo-600\", border: \"border-indigo-500/30 dark:border-indigo-400/25\", tint: \"bg-indigo-500/10\" },\n \"claim-username\": { text: \"text-violet-600 dark:text-violet-400\", solid: \"bg-violet-600\", border: \"border-violet-500/30 dark:border-violet-400/25\", tint: \"bg-violet-500/10\" },\n \"claim-collection\": { text: \"text-cyan-600 dark:text-cyan-400\", solid: \"bg-cyan-600\", border: \"border-cyan-500/30 dark:border-cyan-400/25\", tint: \"bg-cyan-500/10\" },\n \"claim-collection-name\": { text: \"text-pink-600 dark:text-pink-400\", solid: \"bg-pink-600\", border: \"border-pink-500/30 dark:border-pink-400/25\", tint: \"bg-pink-500/10\" },\n};\n\n// ── Service card — the whole card is the action ─────────────────────────────\n\nexport interface LaunchpadServiceCardProps {\n def: ServiceDefinition;\n override?: ServiceOverride;\n /** Showcase layout — spans the full grid width (e.g. POP Protocol) */\n featured?: boolean;\n /** Grid position — drives the staggered entrance reveal. */\n index?: number;\n}\n\nexport function LaunchpadServiceCard({ def, override = {}, featured = false, index = 0 }: LaunchpadServiceCardProps) {\n const { key, icon: Icon, title, browseLinkLabel, features } = def;\n const status = override.status ?? def.status;\n const blurb = override.blurb ?? def.blurb;\n const { href, browseHref } = override;\n const reduceMotion = useReducedMotion();\n\n const live = status === \"live\";\n const hue = SERVICE_HUES[key] ?? DEFAULT_HUE;\n\n return (\n <motion.div\n layout={!reduceMotion}\n initial={{ opacity: 0, y: reduceMotion ? 0 : 16 }}\n whileInView={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0 }}\n viewport={{ once: true, margin: \"-40px\" }}\n transition={{ duration: 0.45, delay: index * 0.06, ease: [0.25, 0.46, 0.45, 0.94] }}\n className={cn(\"flex\", featured && \"sm:col-span-2\")}\n >\n <div\n className={cn(\n \"group relative flex flex-1 flex-col overflow-hidden min-h-[200px] rounded-2xl border bg-card transition-all duration-300 ease-out\",\n live\n ? cn(\"active:scale-[0.985] sm:hover:-translate-y-1 motion-reduce:transform-none motion-reduce:transition-none\", hue.border, \"sm:hover:shadow-md sm:hover:shadow-black/5 dark:sm:hover:shadow-black/20\")\n : \"border-border/30 opacity-75\",\n )}\n >\n <div className=\"relative flex flex-col flex-1 p-5 sm:p-6 gap-3 sm:gap-4\">\n <div className=\"flex items-start justify-between gap-3\">\n {live ? (\n <span className={cn(\"flex h-12 w-12 items-center justify-center rounded-xl\", hue.tint)}>\n <Icon className={cn(\"h-6 w-6\", hue.text)} />\n </span>\n ) : (\n <Icon className=\"h-8 w-8 shrink-0 text-muted-foreground/50\" />\n )}\n {!live && (\n <span className=\"flex items-center gap-1 text-[11px] font-medium text-muted-foreground/60 pt-1\">\n <Lock className=\"h-3 w-3\" />\n Coming soon\n </span>\n )}\n </div>\n\n <div className=\"space-y-1.5\">\n <h3 className=\"text-xl sm:text-2xl font-black tracking-tight leading-snug\">{title}</h3>\n <p className={cn(\"text-sm leading-relaxed\", live ? \"text-muted-foreground\" : \"text-muted-foreground/60\", !featured && \"max-w-[36ch]\")}>\n {blurb}\n </p>\n </div>\n\n {/* Feature showcase — plain-language chips, capped at 2 for card density */}\n {live && features.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5\">\n {features.slice(0, 2).map((feature) => (\n <span\n key={feature}\n className=\"inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-muted/40 border border-border/30 text-xs font-medium text-muted-foreground\"\n >\n <Check className={cn(\"h-3 w-3 shrink-0\", hue.text)} />\n {feature}\n </span>\n ))}\n </div>\n )}\n\n {/* Stretched link — the whole card is the action; the button is the visual cue */}\n {live && href && <Link href={href} aria-label={`${def.cta} — ${title}`} className=\"absolute inset-0 z-10\" />}\n\n <div className=\"mt-auto pt-1 flex items-end justify-between gap-3\">\n {live && browseHref && browseLinkLabel ? (\n <Link\n href={browseHref}\n className=\"relative z-20 inline-flex items-center gap-1 text-xs font-medium text-muted-foreground active:text-foreground sm:hover:text-foreground transition-colors\"\n >\n {browseLinkLabel}\n <ArrowRight className=\"h-3 w-3\" />\n </Link>\n ) : (\n <span />\n )}\n {live && (\n <span\n className={cn(\n \"inline-flex items-center gap-2 h-10 px-5 rounded-full\",\n \"text-sm font-semibold text-white\",\n hue.solid,\n )}\n >\n {def.cta}\n <ArrowRight className=\"h-4 w-4 transition-transform duration-300 sm:group-hover:translate-x-0.5 motion-reduce:transform-none\" />\n </span>\n )}\n </div>\n </div>\n </div>\n </motion.div>\n );\n}\n\n// ── Group sections ───────────────────────────────────────────────────────────\n\n/** Vivid gradient titles give special sections identity without boxing them in\n * (no panels inside panels — color carries the distinction, space does the rest). */\nconst GROUP_TITLE_ACCENTS: Partial<Record<ServiceGroup, string>> = {\n \"coins\": \"bg-gradient-to-r from-yellow-500 via-amber-500 to-orange-500 bg-clip-text text-transparent\",\n \"community\": \"bg-gradient-to-r from-emerald-500 to-green-600 bg-clip-text text-transparent\",\n};\n\nfunction GroupHeader({ group }: { group: ServiceGroupDefinition }) {\n return (\n <div className=\"space-y-2\">\n <h2 className={cn(\"text-3xl sm:text-4xl font-black tracking-tight\", GROUP_TITLE_ACCENTS[group.key])}>\n {group.title}\n </h2>\n <p className=\"text-base sm:text-lg text-muted-foreground leading-relaxed max-w-2xl\">{group.tagline}</p>\n </div>\n );\n}\n\n/** POP Protocol companion column — open how-it-works steps, no extra panel. */\nconst POP_STEPS = [\n { title: \"Create your event badge\", body: \"Name it, add your artwork, and set who can claim — ready in minutes.\" },\n { title: \"Share the claim link\", body: \"Your community claims for free — one badge per person, no faking.\" },\n { title: \"It stays with them forever\", body: \"Badges can't be sold or transferred — lasting proof they were there.\" },\n];\n\nfunction PopHowItWorks() {\n return (\n <div className=\"flex flex-col justify-center gap-5 sm:gap-6 px-2 py-4 sm:py-0 sm:pl-6\">\n {POP_STEPS.map((step, i) => (\n <div key={step.title} className=\"flex gap-4\">\n <span className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 text-sm font-bold\">\n {i + 1}\n </span>\n <div className=\"space-y-0.5\">\n <p className=\"font-semibold\">{step.title}</p>\n <p className=\"text-sm text-muted-foreground leading-relaxed\">{step.body}</p>\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nfunction ComingSoonStrip({ group, defs }: { group: ServiceGroupDefinition; defs: ServiceDefinition[] }) {\n return (\n <div className=\"rounded-2xl border border-border/40 p-5\">\n <p className=\"font-semibold text-sm\">{group.title}</p>\n <p className=\"text-sm text-muted-foreground mt-0.5\">{group.tagline}</p>\n <div className=\"flex flex-wrap gap-2 mt-4\">\n {defs.map(({ key, icon: Icon, title }) => (\n <div key={key} className=\"flex items-center gap-2 px-3 py-2 rounded-full bg-muted/30 border border-border/25\">\n <Icon className=\"h-3.5 w-3.5 text-muted-foreground/60\" />\n <span className=\"text-xs font-medium text-muted-foreground\">{title}</span>\n </div>\n ))}\n </div>\n </div>\n );\n}\n\n/**\n * Owns the search + group-filter state for the launchpad page. A page renders\n * `LaunchpadFilterBar` wherever it wants (e.g. right under the h1) wired to\n * this hook's fields, then passes `query`/`activeGroups` straight through to\n * `LaunchpadGroupedSections` so the grid reacts to the same state.\n */\nexport function useLaunchpadFilter() {\n const [query, setQuery] = useState(\"\");\n const [activeGroups, setActiveGroups] = useState<Set<ServiceGroup>>(new Set());\n\n const filterableGroups = LAUNCHPAD_SERVICE_GROUPS.filter((g) => g.key !== \"coming-soon\");\n\n const toggleGroup = (key: ServiceGroup) => {\n setActiveGroups((prev) => {\n const next = new Set(prev);\n if (next.has(key)) next.delete(key);\n else next.add(key);\n return next;\n });\n };\n\n const matches = (def: ServiceDefinition): boolean => {\n if (activeGroups.size > 0 && !activeGroups.has(def.group)) return false;\n if (def.status !== \"live\") return false;\n if (query.trim() === \"\") return true;\n const haystack = `${def.title} ${def.blurb} ${def.subtitle}`.toLowerCase();\n return haystack.includes(query.trim().toLowerCase());\n };\n\n const totalMatches = useMemo(\n () => LAUNCHPAD_SERVICE_DEFINITIONS.filter(matches).length,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [query, activeGroups],\n );\n\n const clear = () => { setQuery(\"\"); setActiveGroups(new Set()); };\n\n return { query, setQuery, activeGroups, toggleGroup, filterableGroups, totalMatches, clear };\n}\n\nexport interface LaunchpadGroupedSectionsProps {\n /** Per-app hrefs / rollout flips, keyed by service key. */\n overrides: ServiceOverrides;\n /** Current search query — from `useLaunchpadFilter()`. */\n query: string;\n /** Current active group filters — from `useLaunchpadFilter()`. */\n activeGroups: Set<ServiceGroup>;\n /** Reset both — from `useLaunchpadFilter()`. */\n onClearFilters: () => void;\n className?: string;\n}\n\n/** The full grouped launchpad services grid — section order comes from\n * LAUNCHPAD_SERVICE_GROUPS; cards from LAUNCHPAD_SERVICE_DEFINITIONS.\n * Fully controlled by `useLaunchpadFilter()` state passed in from the page. */\nexport function LaunchpadGroupedSections({ overrides, query, activeGroups, onClearFilters, className }: LaunchpadGroupedSectionsProps) {\n const matches = (def: ServiceDefinition): boolean => {\n if (activeGroups.size > 0 && !activeGroups.has(def.group)) return false;\n if (def.status !== \"live\") return false;\n if (query.trim() === \"\") return true;\n const haystack = `${def.title} ${def.blurb} ${def.subtitle}`.toLowerCase();\n return haystack.includes(query.trim().toLowerCase());\n };\n\n const totalMatches = useMemo(\n () => LAUNCHPAD_SERVICE_DEFINITIONS.filter(matches).length,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [query, activeGroups],\n );\n\n return (\n <div className={cn(\"space-y-8 sm:space-y-10\", className)}>\n {totalMatches === 0 ? (\n <div className=\"text-center py-16 space-y-3\">\n <p className=\"text-lg font-semibold\">No services match</p>\n <p className=\"text-sm text-muted-foreground\">Try a different search or clear your filters.</p>\n <button\n type=\"button\"\n onClick={onClearFilters}\n className=\"inline-flex items-center h-9 px-4 rounded-full text-sm font-semibold bg-primary text-primary-foreground\"\n >\n Clear filters\n </button>\n </div>\n ) : (\n <div className=\"space-y-20 sm:space-y-28\">\n <AnimatePresence>\n {LAUNCHPAD_SERVICE_GROUPS.map((group) => {\n if (group.key === \"coming-soon\") {\n // Coming-soon items are never \"live\" by definition, so they can't\n // go through matches() (which requires status === \"live\") — only\n // the group/search filters apply here, not the live-status gate.\n const inActiveGroup = (d: ServiceDefinition) => activeGroups.size === 0 || activeGroups.has(d.group);\n const inSearch = (d: ServiceDefinition) =>\n query.trim() === \"\" ||\n `${d.title} ${d.blurb} ${d.subtitle}`.toLowerCase().includes(query.trim().toLowerCase());\n const comingSoonDefs = LAUNCHPAD_SERVICE_DEFINITIONS.filter(\n (d) => d.group === group.key && inActiveGroup(d) && inSearch(d),\n );\n if (comingSoonDefs.length === 0) return null;\n return <ComingSoonStrip key={group.key} group={group} defs={comingSoonDefs} />;\n }\n\n const defs = LAUNCHPAD_SERVICE_DEFINITIONS.filter((d) => d.group === group.key && matches(d));\n if (defs.length === 0) return null;\n\n const showPopHowItWorks = group.key === \"community\" && defs.some((d) => d.key === \"pop-protocol\");\n\n return (\n <motion.div\n key={group.key}\n layout\n initial={{ opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.3, ease: [0.25, 0.46, 0.45, 0.94] }}\n className=\"space-y-7 sm:space-y-10\"\n >\n <GroupHeader group={group} />\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-5\">\n {defs.map((def, i) => (\n <LaunchpadServiceCard key={def.key} def={def} override={overrides[def.key]} index={i} />\n ))}\n {showPopHowItWorks && <PopHowItWorks />}\n </div>\n </motion.div>\n );\n })}\n </AnimatePresence>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}