1import { av as useParams, aw as useLoaderData, r as reactExports, j as jsxRuntimeExports, L as Link, a8 as ArrowLeft, H as Helmet, ax as Tag, m as motion, ay as User, h as Clock, aj as Check, az as Share2 } from "./vendor-BnxyTOIt.js"; 2import { a as story } from "./main-DpFm2Mq5.js"; 3import "./preload-VwzZcMtp.js"; 4/* empty css */ 5function formatDate(iso) { 6 return new Date(iso).toLocaleDateString("en-US", { 7 weekday: "long", 8 year: "numeric", 9 month: "long", 10 day: "numeric" 11 }); 12} 13function StoryBody({ 14 content 15}) { 16 const hasHTML = /<(p|br|div|h[1-6]|ul|ol|li|strong|em|b|i|blockquote|a)\b/i.test(content); 17 if (hasHTML) { 18 const BLOCK_START = /^\s*<(ul|ol|li|div|h[1-6]|blockquote|p)\b/i; 19 const html = content.split("\n\n").map((chunk) => chunk.trim()).filter(Boolean).map((chunk) => { 20 if (BLOCK_START.test(chunk)) return chunk; 21 return `<p>${chunk.replace(/\n/g, "<br />")}</p>`; 22 }).join(""); 23 return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
24 /* @__PURE__ */ jsxRuntimeExports.jsx("style", { children: ` 25 .story-body p { margin-bottom: 1.25rem; line-height: 1.75; } 26 .story-body ul, .story-body ol { margin-bottom: 1.25rem; padding-left: 1.5rem; } 27 .story-body li { margin-bottom: 0.4rem; line-height: 1.75; } 28 .story-body ul { list-style-type: disc; } 29 .story-body ol { list-style-type: decimal; } 30 .story-body h1,.story-body h2,.story-body h3,.story-body h4 { margin-top: 1.5rem; margin-bottom: 0.75rem; font-weight: 700; } 31 .story-body strong, .story-body b { font-weight: 700; } 32 .story-body em, .story-body i { font-style: italic; } 33 .story-body blockquote { border-left: 4px solid hsl(var(--accent)); padding-left: 1rem; margin: 1.25rem 0; font-style: italic; } 34 .story-body a { color: hsl(var(--primary)); text-decoration: underline; } 35 ` }), 36 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "story-body max-w-none", style: { 37 fontFamily: "var(--font-sans)", 38 color: "hsl(var(--foreground))" 39 }, dangerouslySetInnerHTML: { 40 __html: html 41 } }) 42 ] }); 43 } 44 let paragraphs = content.split(/\n{2,}/).map((p) => p.trim()).filter(Boolean); 45 if (paragraphs.length <= 1) { 46 paragraphs = content.split(/\n/).map((p) => p.trim()).filter(Boolean); 47 } 48 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "story-body", children: paragraphs.map((p, i) => /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mb-5 leading-relaxed text-base", style: { 49 fontFamily: "var(--font-sans)", 50 color: "hsl(var(--foreground))" 51 }, children: p }, i)) }); 52} 53function StoryPage() { 54 const { 55 id 56 } = useParams(); 57 const loaderData = useLoaderData(); 58 const loaderStory = (loaderData == null ? void 0 : loaderData.story) ?? null; 59 const [story$1, setStory] = reactExports.useState(loaderStory); 60 const [loading, setLoading] = reactExports.useState(!loaderStory); 61 const [notFound, setNotFound] = reactExports.useState(false); 62 const [copied, setCopied] = reactExports.useState(false); 63 const site = "https://blackbusinessmonth.com"; 64 const defaultOgImage = `${site}/airo-assets/uploads/og-default.png`; 65 reactExports.useEffect(() => { 66 if (story$1) return; 67 if (!id) return; 68 setLoading(true); 69 fetch(`/api/stories/${id}`).then((r) => { 70 if (r.status === 404) { 71 setNotFound(true); 72 return null; 73 } 74 return r.json(); 75 }).then((data) => { 76 if (data) setStory(data); 77 }).catch(() => setNotFound(true)).finally(() => setLoading(false)); 78 }, [id]); 79 const handleShare = () => { 80 navigator.clipboard.writeText(window.location.href); 81 setCopied(true); 82 setTimeout(() => setCopied(false), 2e3); 83 }; 84 if (loading) { 85 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "container mx-auto px-4 py-20 flex justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center gap-4", children: [
86 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-8 h-8 border-2 border-primary border-t-transparent rounded-full animate-spin" }), 87 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-muted-foreground text-sm", style: { 88 whiteSpace: "pre-line", 89 fontFamily: "var(--font-sans)" 90 }, children: story.loadingText }) 91 ] }) }); 92 } 93 if (notFound || !story$1) { 94 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "container mx-auto px-4 py-20 text-center", children: [ 95 /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-2xl font-bold mb-4", style: { 96 whiteSpace: "pre-line", 97 fontFamily: "var(--font-heading)" 98 }, children: story.notFoundHeading }), 99 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-muted-foreground mb-8", style: { 100 whiteSpace: "pre-line", 101 fontFamily: "var(--font-sans)" 102 }, children: story.notFoundBody }), 103 /* @__PURE__ */ jsxRuntimeExports.jsxs(Link, { to: "/", className: "inline-flex items-center gap-2 font-semibold text-sm", style: { 104 color: "hsl(var(--primary))" 105 }, children: [
106 /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 15 }), 107 " ", 108 story.backToHome 109 ] }) 110 ] }); 111 } 112 const storyUrl = `${site}/stories/${story$1.id}`; 113 const ogImage = story$1.imageUrl ? story$1.imageUrl.startsWith("http") ? story$1.imageUrl : `${site}${story$1.imageUrl}` : defaultOgImage; 114 const pageTitle = story$1.seoTitle || `${story$1.headline} â Black Money Worldwide`; 115 const ogDescription = story$1.seoDescription || story$1.summary || story$1.headline; 116 return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 117 /* @__PURE__ */ jsxRuntimeExports.jsxs(Helmet, { children: [ 118 /* @__PURE__ */ jsxRuntimeExports.jsx("title", { children: pageTitle }), 119 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { name: "description", content: ogDescription }), 120 story$1.seoKeywords && /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { name: "keywords", content: story$1.seoKeywords }), 121 /* @__PURE__ */ jsxRuntimeExports.jsx("link", { rel: "canonical", href: storyUrl }),
122 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { property: "og:type", content: "article" }), 123 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { property: "og:site_name", content: "Black Money Worldwide" }), 124 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { property: "og:url", content: storyUrl }), 125 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { property: "og:title", content: pageTitle }), 126 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { property: "og:description", content: ogDescription }), 127 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { property: "og:image", content: ogImage }), 128 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { property: "og:image:url", content: ogImage }), 129 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { property: "og:image:secure_url", content: ogImage }), 130 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { property: "og:image:width", content: "1200" }), 131 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { property: "og:image:height", content: "630" }),
132 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { property: "og:image:alt", content: story$1.headline }), 133 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { name: "twitter:card", content: "summary_large_image" }), 134 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { name: "twitter:title", content: pageTitle }), 135 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { name: "twitter:description", content: ogDescription }), 136 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { name: "twitter:image", content: ogImage }), 137 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { name: "twitter:image:alt", content: story$1.headline }), 138 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { property: "article:published_time", content: story$1.publishedAt }), 139 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { property: "article:author", content: story$1.author }), 140 /* @__PURE__ */ jsxRuntimeExports.jsx("meta", { property: "article:section", content: story$1.category }) 141 ] }), 142 story$1.imageUrl && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full bg-black flex items-center justify-center", style: { 143 maxHeight: "480px" 144 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: story$1.imageUrl, alt: story$1.headline, className: "w-full object-contain", style: { 145 maxHeight: "480px" 146 } }) }), 147 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "container mx-auto px-4 py-10", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-3xl mx-auto", children: [
148 /* @__PURE__ */ jsxRuntimeExports.jsxs(Link, { to: "/", className: "inline-flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wider mb-6 hover:opacity-70 transition-opacity", style: { 149 color: "hsl(var(--primary))", 150 fontFamily: "var(--font-sans)" 151 }, children: [ 152 /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 13 }), 153 " ", 154 story.backLabel 155 ] }), 156 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-4", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-xs font-bold uppercase tracking-widest px-3 py-1", style: { 157 background: "hsl(var(--primary))", 158 color: "hsl(var(--accent))", 159 fontFamily: "var(--font-sans)" 160 }, children: [ 161 /* @__PURE__ */ jsxRuntimeExports.jsx(Tag, { size: 10 }), 162 " ", 163 story$1.category 164 ] }) }), 165 /* @__PURE__ */ jsxRuntimeExports.jsx(motion.h1, { className: "text-3xl md:text-4xl font-bold leading-tight mb-5", style: { 166 fontFamily: "var(--font-heading)", 167 color: "hsl(var(--foreground))" 168 }, initial: { 169 opacity: 0, 170 y: 16 171 }, animate: { 172 opacity: 1, 173 y: 0, 174 transition: { 175 duration: 0.4, 176 ease: "easeOut" 177 } 178 }, children: story$1.headline }), 179 story$1.summary && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-lg text-muted-foreground leading-relaxed mb-6 border-l-4 pl-4", style: { 180 fontFamily: "var(--font-sans)", 181 borderColor: "hsl(var(--accent))" 182 }, children: story$1.summary }),
183 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4 py-4 border-t border-b border-border mb-8", style: { 184 fontFamily: "var(--font-sans)" 185 }, children: [ 186 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-4 text-sm text-muted-foreground", children: [ 187 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5 font-semibold", style: { 188 color: "hsl(var(--primary))" 189 }, children: [ 190 /* @__PURE__ */ jsxRuntimeExports.jsx(User, { size: 13 }), 191 " ", 192 story$1.author 193 ] }), 194 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1.5", children: [ 195 /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { size: 13 }), 196 " ", 197 formatDate(story$1.publishedAt) 198 ] }) 199 ] }), 200 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: handleShare, className: "flex items-center gap-1.5 text-xs font-semibold px-3 py-1.5 border border-border hover:border-primary transition-colors", style: { 201 fontFamily: "var(--font-sans)" 202 }, children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
203 /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { size: 12 }), 204 " Copied!" 205 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 206 /* @__PURE__ */ jsxRuntimeExports.jsx(Share2, { size: 12 }), 207 " Share" 208 ] }) }) 209 ] }), 210 story$1.content ? /* @__PURE__ */ jsxRuntimeExports.jsx(motion.div, { initial: { 211 opacity: 0 212 }, animate: { 213 opacity: 1, 214 transition: { 215 duration: 0.5, 216 delay: 0.1 217 } 218 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(StoryBody, { content: story$1.content }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-muted-foreground italic", style: { 219 fontFamily: "var(--font-sans)" 220 }, children: "Full story content coming soon." }), 221 story$1.gallery && (() => { 222 try { 223 const galleryUrls = typeof story$1.gallery === "string" ? JSON.parse(story$1.gallery) : story$1.gallery; 224 if (Array.isArray(galleryUrls) && galleryUrls.length > 0) { 225 return /* @__PURE__ */ jsxRuntimeExports.jsxs(motion.div, { className: "mt-10", initial: { 226 opacity: 0 227 }, animate: { 228 opacity: 1, 229 transition: { 230 duration: 0.5, 231 delay: 0.2 232 } 233 }, children: [
234 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-xl font-bold mb-4", style: { 235 whiteSpace: "pre-line", 236 fontFamily: "var(--font-heading)", 237 color: "hsl(var(--foreground))" 238 }, children: story.photoGalleryHeading }), 239 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: galleryUrls.map((url, idx) => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "overflow-hidden border border-border", children: /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: url, alt: `${story$1.headline} - Photo ${idx + 1}`, className: "w-full h-auto object-cover" }) }, idx)) }) 240 ] }); 241 } 242 } catch (e) { 243 console.error("Failed to parse gallery:", e); 244 } 245 return null; 246 })(), 247 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-12 pt-6 border-t border-border flex items-center gap-4", style: { 248 fontFamily: "var(--font-sans)" 249 }, children: [
250 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-12 flex-shrink-0 flex items-center justify-center font-bold text-lg", style: { 251 background: "hsl(var(--primary))", 252 color: "hsl(var(--accent))" 253 }, children: story.authorInitials }), 254 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 255 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "font-bold text-sm", style: { 256 color: "hsl(var(--foreground))" 257 }, children: story$1.author }), 258 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", style: { 259 whiteSpace: "pre-line" 260 }, children: story.authorByline }) 261 ] }) 262 ] }), 263 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-10 text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Link, { to: "/", className: "inline-flex items-center gap-2 px-6 py-3 text-sm font-bold text-white transition-opacity hover:opacity-90", style: { 264 background: "hsl(var(--primary))" 265 }, children: [
266 /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 14 }), 267 " ", 268 story.backToSiteLabel 269 ] }) }) 270 ] }) }) 271 ] }); 272} 273export { 274 StoryPage as default 275};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.