@aranova/tracking-next 0.19.2 → 0.20.1

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.
@@ -0,0 +1,2303 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/blog-server.ts
31
+ var blog_server_exports = {};
32
+ __export(blog_server_exports, {
33
+ BASE_BLOG_THEME: () => BASE_BLOG_THEME,
34
+ BLOG_BASE_CSS: () => BLOG_BASE_CSS,
35
+ BLOG_BREAKPOINTS: () => BLOG_BREAKPOINTS,
36
+ BLOG_BREAKPOINT_MIN_WIDTHS_PX: () => BLOG_BREAKPOINT_MIN_WIDTHS_PX,
37
+ BLOG_MDX_ALLOWED_COMPONENTS: () => BLOG_MDX_ALLOWED_COMPONENTS,
38
+ BLOG_MDX_COMPONENT_GRAMMAR: () => BLOG_MDX_COMPONENT_GRAMMAR,
39
+ BLOG_MDX_MAX_DEPTH: () => BLOG_MDX_MAX_DEPTH,
40
+ BLOG_MDX_MAX_NODES: () => BLOG_MDX_MAX_NODES,
41
+ BLOG_MDX_MAX_SIZE: () => BLOG_MDX_MAX_SIZE,
42
+ BLOG_THEME_PRESETS: () => BLOG_THEME_PRESETS,
43
+ BlogBreadcrumbs: () => BlogBreadcrumbs,
44
+ BlogPost: () => BlogPost2,
45
+ BlogPostMeta: () => BlogPostMeta,
46
+ BlogPostView: () => BlogPostView,
47
+ BlogTocShell: () => BlogTocShell,
48
+ DEFAULT_BLOG_LOCALE: () => DEFAULT_BLOG_LOCALE,
49
+ blogHeroImageSchema: () => blogHeroImageSchema,
50
+ blogImageSchema: () => blogImageSchema,
51
+ blogManifestEntrySchema: () => blogManifestEntrySchema,
52
+ blogManifestSchema: () => blogManifestSchema,
53
+ blogManifestWireSchema: () => blogManifestWireSchema,
54
+ blogPostContentSchema: () => blogPostContentSchema,
55
+ blogPostContentWireSchema: () => blogPostContentWireSchema,
56
+ blogSeoSchema: () => blogSeoSchema,
57
+ blogThemeColorsSchema: () => blogThemeColorsSchema,
58
+ blogThemeOverrideSchema: () => blogThemeOverrideSchema,
59
+ blogThemeSchema: () => blogThemeSchema,
60
+ blogThemeSpacingSchema: () => blogThemeSpacingSchema,
61
+ blogThemeToCssVars: () => blogThemeToCssVars,
62
+ blogThemeTypographySchema: () => blogThemeTypographySchema,
63
+ buildBlogPostMetadata: () => buildBlogPostMetadata,
64
+ buildBlogPostingJsonLd: () => buildBlogPostingJsonLd,
65
+ buildBlogSitemapEntries: () => buildBlogSitemapEntries,
66
+ buildFaqJsonLd: () => buildFaqJsonLd,
67
+ createBlogClient: () => createBlogClient,
68
+ createBlogFontTheme: () => createBlogFontTheme,
69
+ createDefaultComponents: () => createDefaultComponents,
70
+ estimateReadingTime: () => estimateReadingTime,
71
+ extractBlogHeadings: () => extractBlogHeadings,
72
+ extractFaqPairs: () => extractFaqPairs,
73
+ joinClassNames: () => joinClassNames,
74
+ nullishField: () => nullishField,
75
+ parseBlogManifest: () => parseBlogManifest,
76
+ parseBlogPostContent: () => parseBlogPostContent,
77
+ resolveBlogTheme: () => resolveBlogTheme,
78
+ responsive: () => responsive,
79
+ sanitizeBlogTheme: () => sanitizeBlogTheme,
80
+ serializeJsonLd: () => serializeJsonLd,
81
+ toBlogBreakpointMap: () => toBlogBreakpointMap,
82
+ validateBlogMdx: () => validateBlogMdx
83
+ });
84
+ module.exports = __toCommonJS(blog_server_exports);
85
+ var import_server_only = require("server-only");
86
+
87
+ // ../tracking-core/src/resources/blog/schema.ts
88
+ var import_zod2 = require("zod");
89
+
90
+ // ../tracking-core/src/resources/blog/theme.ts
91
+ var import_zod = require("zod");
92
+ var BLOG_BREAKPOINTS = ["sm", "md", "lg", "xl", "2xl", "3xl"];
93
+ var BLOG_BREAKPOINT_MIN_WIDTHS_PX = {
94
+ sm: 640,
95
+ md: 768,
96
+ lg: 1024,
97
+ xl: 1280,
98
+ "2xl": 1536,
99
+ "3xl": 1920
100
+ };
101
+ function toBlogBreakpointMap(value) {
102
+ return typeof value === "object" ? value : { base: value };
103
+ }
104
+ var HEX_COLOR = /^#[0-9a-fA-F]{3,8}$/;
105
+ var FUNCTIONAL_COLOR = /^(rgb|rgba|hsl|hsla|oklch)\([0-9a-zA-Z ,.%/-]*\)$/;
106
+ var LENGTH = /^-?\d+(\.\d+)?(px|rem|em|%|ch|vw)$/;
107
+ var LINE_HEIGHT = /^-?\d+(\.\d+)?(px|rem|em|%|ch|vw)?$/;
108
+ var FONT_FAMILY_TOKEN = /^['"]?[a-zA-Z0-9 -]+['"]?$/;
109
+ var FONT_FAMILY_VAR_TOKEN = /^var\(--[a-zA-Z0-9-]+\)$/;
110
+ var colorValueSchema = import_zod.z.string().refine((v) => HEX_COLOR.test(v) || FUNCTIONAL_COLOR.test(v), {
111
+ message: "must be a #hex or rgb()/rgba()/hsl()/hsla()/oklch() color"
112
+ });
113
+ var lengthValueSchema = import_zod.z.string().regex(LENGTH, {
114
+ message: "must be a CSS length (px|rem|em|%|ch|vw)"
115
+ });
116
+ var lineHeightValueSchema = import_zod.z.string().regex(LINE_HEIGHT, {
117
+ message: "must be a CSS length or unitless line-height"
118
+ });
119
+ var fontWeightSchema = import_zod.z.number().int().min(100).max(1e3);
120
+ var alignValueSchema = import_zod.z.enum(["left", "center", "right"]);
121
+ var fontFamilySchema = import_zod.z.string().refine(
122
+ (v) => {
123
+ const tokens = v.split(",").map((t) => t.trim());
124
+ return tokens.length > 0 && tokens.every((t) => FONT_FAMILY_TOKEN.test(t) || FONT_FAMILY_VAR_TOKEN.test(t));
125
+ },
126
+ { message: "must be a comma-separated list of font-family or var(--custom-property) tokens" }
127
+ );
128
+ function nullishField(schema) {
129
+ return schema.nullish().transform((v) => v ?? void 0);
130
+ }
131
+ function breakpointMapSchema(valueSchema) {
132
+ return import_zod.z.object({
133
+ base: valueSchema,
134
+ sm: nullishField(valueSchema).catch(void 0),
135
+ md: nullishField(valueSchema).catch(void 0),
136
+ lg: nullishField(valueSchema).catch(void 0),
137
+ xl: nullishField(valueSchema).catch(void 0),
138
+ "2xl": nullishField(valueSchema).catch(void 0),
139
+ "3xl": nullishField(valueSchema).catch(void 0)
140
+ });
141
+ }
142
+ function responsive(valueSchema) {
143
+ return import_zod.z.union([valueSchema, breakpointMapSchema(valueSchema)]);
144
+ }
145
+ var blogThemeColorsShape = {
146
+ background: colorValueSchema,
147
+ surface: colorValueSchema,
148
+ text: colorValueSchema,
149
+ text_muted: colorValueSchema,
150
+ primary: colorValueSchema,
151
+ primary_foreground: colorValueSchema,
152
+ link: colorValueSchema,
153
+ border: colorValueSchema,
154
+ code_background: colorValueSchema,
155
+ heading: colorValueSchema
156
+ };
157
+ var blogThemeColorsSchema = import_zod.z.object({
158
+ ...blogThemeColorsShape,
159
+ // The ONE optional color in a resolved theme: when unset, headings fall back
160
+ // to `text` in the stylesheet (`--_heading-color`), so `blogThemeToCssVars`
161
+ // only emits `--ara-blog-color-heading` when a theme actually sets it.
162
+ heading: nullishField(colorValueSchema)
163
+ });
164
+ var blogThemeTypographySchema = import_zod.z.object({
165
+ heading_family: fontFamilySchema,
166
+ body_family: fontFamilySchema,
167
+ heading_weight: fontWeightSchema,
168
+ body_weight: fontWeightSchema,
169
+ h1_size: responsive(lengthValueSchema),
170
+ h2_size: responsive(lengthValueSchema),
171
+ h3_size: responsive(lengthValueSchema),
172
+ h4_size: responsive(lengthValueSchema),
173
+ body_size: responsive(lengthValueSchema),
174
+ body_line_height: responsive(lineHeightValueSchema),
175
+ small_size: responsive(lengthValueSchema),
176
+ // Scalar (non-responsive) length; the grammar allows negative em values.
177
+ // h1/h2 consume it directly, h3/h4 at half strength (calc in the base CSS).
178
+ heading_letter_spacing: lengthValueSchema,
179
+ // Figure caption text alignment (left/center/right). Site-owner override;
180
+ // default center in the base CSS if unset.
181
+ caption_align: alignValueSchema
182
+ });
183
+ var blogThemeSpacingSchema = import_zod.z.object({
184
+ content_max_width: responsive(lengthValueSchema),
185
+ block_gap: responsive(lengthValueSchema),
186
+ section_gap: responsive(lengthValueSchema)
187
+ });
188
+ var blogThemeColorsPartialSchema = import_zod.z.object(
189
+ Object.fromEntries(
190
+ Object.keys(blogThemeColorsShape).map((key) => [key, nullishField(colorValueSchema)])
191
+ )
192
+ );
193
+ var blogThemeSchema = import_zod.z.object({
194
+ colors: blogThemeColorsSchema,
195
+ colors_dark: nullishField(blogThemeColorsPartialSchema),
196
+ typography: blogThemeTypographySchema,
197
+ spacing: blogThemeSpacingSchema,
198
+ radius: responsive(lengthValueSchema)
199
+ });
200
+ function dropInvalidFields(shape) {
201
+ const out = {};
202
+ for (const [key, schema] of Object.entries(shape)) {
203
+ out[key] = nullTolerant(schema).catch(void 0);
204
+ }
205
+ return out;
206
+ }
207
+ function nullTolerant(schema) {
208
+ if (schema instanceof import_zod.z.ZodUnion) {
209
+ const options = schema.options;
210
+ return import_zod.z.union([...options, import_zod.z.null()]).optional().transform((v) => v ?? void 0);
211
+ }
212
+ return nullishField(schema);
213
+ }
214
+ var blogThemeColorsOverrideSchema = import_zod.z.object(dropInvalidFields(blogThemeColorsShape));
215
+ var blogThemeTypographyOverrideSchema = import_zod.z.object(
216
+ dropInvalidFields(blogThemeTypographySchema.shape)
217
+ );
218
+ var blogThemeSpacingOverrideSchema = import_zod.z.object(dropInvalidFields(blogThemeSpacingSchema.shape));
219
+ var blogThemeOverrideSchema = import_zod.z.object(
220
+ dropInvalidFields({
221
+ colors: blogThemeColorsOverrideSchema,
222
+ colors_dark: blogThemeColorsOverrideSchema,
223
+ typography: blogThemeTypographyOverrideSchema,
224
+ spacing: blogThemeSpacingOverrideSchema,
225
+ radius: responsive(lengthValueSchema)
226
+ })
227
+ );
228
+ var BASE_BLOG_THEME = {
229
+ colors: {
230
+ background: "#ffffff",
231
+ surface: "#f5f5f5",
232
+ text: "#171717",
233
+ text_muted: "#6b6b6b",
234
+ primary: "#171717",
235
+ primary_foreground: "#ffffff",
236
+ link: "#1d4ed8",
237
+ border: "#e5e5e5",
238
+ code_background: "#f5f5f5"
239
+ },
240
+ typography: {
241
+ heading_family: "system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif",
242
+ body_family: "system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif",
243
+ heading_weight: 700,
244
+ body_weight: 400,
245
+ // Responsive defaults: comfortable mobile sizes, larger from `md` up.
246
+ // This replaces the hardcoded mobile media query the base CSS used to need.
247
+ h1_size: { base: "1.9rem", md: "2.25rem" },
248
+ h2_size: { base: "1.5rem", md: "1.75rem" },
249
+ h3_size: "1.25rem",
250
+ h4_size: "1.125rem",
251
+ body_size: "1rem",
252
+ body_line_height: "1.7",
253
+ small_size: "0.875rem",
254
+ heading_letter_spacing: "-0.02em",
255
+ caption_align: "center"
256
+ },
257
+ spacing: {
258
+ content_max_width: "42rem",
259
+ block_gap: "1.25rem",
260
+ section_gap: "3rem"
261
+ },
262
+ radius: "8px"
263
+ };
264
+ function sanitizeBlogTheme(raw) {
265
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
266
+ const parsed = blogThemeOverrideSchema.safeParse(raw);
267
+ return parsed.success ? parsed.data : {};
268
+ }
269
+ function mergeGroup(base, over) {
270
+ if (!over) return base;
271
+ const out = { ...base };
272
+ for (const [key, value] of Object.entries(over)) {
273
+ if (value !== void 0) out[key] = value;
274
+ }
275
+ return out;
276
+ }
277
+ function resolveBlogTheme(...layers) {
278
+ let resolved = {
279
+ colors: { ...BASE_BLOG_THEME.colors },
280
+ colors_dark: BASE_BLOG_THEME.colors_dark ? { ...BASE_BLOG_THEME.colors_dark } : void 0,
281
+ typography: { ...BASE_BLOG_THEME.typography },
282
+ spacing: { ...BASE_BLOG_THEME.spacing },
283
+ radius: BASE_BLOG_THEME.radius
284
+ };
285
+ for (const layer of layers) {
286
+ if (!layer) continue;
287
+ resolved = {
288
+ colors: mergeGroup(resolved.colors, layer.colors),
289
+ colors_dark: layer.colors_dark || resolved.colors_dark ? mergeGroup(resolved.colors_dark ?? {}, layer.colors_dark) : void 0,
290
+ typography: mergeGroup(resolved.typography, layer.typography),
291
+ spacing: mergeGroup(resolved.spacing, layer.spacing),
292
+ radius: layer.radius ?? resolved.radius
293
+ };
294
+ }
295
+ return resolved;
296
+ }
297
+ function kebab(key) {
298
+ return key.split("_").join("-");
299
+ }
300
+ function emitResponsiveVar(vars, name, value) {
301
+ const map = toBlogBreakpointMap(value);
302
+ vars[name] = String(map.base);
303
+ for (const bp of BLOG_BREAKPOINTS) {
304
+ const bpValue = map[bp];
305
+ if (bpValue !== void 0) vars[`${name}-${bp}`] = String(bpValue);
306
+ }
307
+ }
308
+ function blogThemeToCssVars(theme) {
309
+ const vars = {};
310
+ const dark = theme.colors_dark;
311
+ const hasDark = !!dark && Object.values(dark).some((v) => v !== void 0);
312
+ const colorKeys = /* @__PURE__ */ new Set([...Object.keys(theme.colors), ...dark ? Object.keys(dark) : []]);
313
+ for (const key of colorKeys) {
314
+ const light = theme.colors[key];
315
+ const darkValue = hasDark ? dark?.[key] : void 0;
316
+ const lightValue = light ?? (darkValue !== void 0 ? theme.colors.text : void 0);
317
+ if (lightValue === void 0) continue;
318
+ vars[`--ara-blog-color-${kebab(key)}`] = darkValue ? `light-dark(${lightValue}, ${darkValue})` : lightValue;
319
+ }
320
+ if (hasDark) vars.colorScheme = "light dark";
321
+ for (const [key, value] of Object.entries(theme.typography)) {
322
+ emitResponsiveVar(vars, `--ara-blog-type-${kebab(key)}`, value);
323
+ }
324
+ for (const [key, value] of Object.entries(theme.spacing)) {
325
+ emitResponsiveVar(vars, `--ara-blog-space-${kebab(key)}`, value);
326
+ }
327
+ emitResponsiveVar(vars, "--ara-blog-radius", theme.radius);
328
+ return vars;
329
+ }
330
+
331
+ // ../tracking-core/src/resources/blog/schema.ts
332
+ var schemaVersionSchema = import_zod2.z.literal(1);
333
+ var blogHeroImageSchema = import_zod2.z.object({
334
+ url: import_zod2.z.string(),
335
+ alt: import_zod2.z.string().catch(""),
336
+ width: import_zod2.z.number().int(),
337
+ height: import_zod2.z.number().int()
338
+ });
339
+ var blogManifestEntrySchema = import_zod2.z.object({
340
+ slug: import_zod2.z.string(),
341
+ title: import_zod2.z.string(),
342
+ description: import_zod2.z.string().catch(""),
343
+ content_hash: import_zod2.z.string(),
344
+ content_key: import_zod2.z.string(),
345
+ published_at: import_zod2.z.string(),
346
+ updated_at: import_zod2.z.string(),
347
+ // Estimated read time in whole minutes (computed at publish from the post
348
+ // body). 0 when unknown; index cards render "N min read".
349
+ reading_minutes: import_zod2.z.number().int().catch(0),
350
+ // A malformed tags array / hero_image degrades gracefully instead of
351
+ // dropping the whole entry.
352
+ tags: import_zod2.z.array(import_zod2.z.string()).catch([]),
353
+ hero_image: blogHeroImageSchema.nullable().catch(null)
354
+ });
355
+ var blogManifestWireSchema = import_zod2.z.object({
356
+ schema_version: schemaVersionSchema,
357
+ namespace: import_zod2.z.string(),
358
+ generated_at: import_zod2.z.string(),
359
+ posts: import_zod2.z.array(blogManifestEntrySchema).catch([])
360
+ });
361
+ var blogManifestSchema = blogManifestWireSchema.extend({
362
+ // A single malformed entry is dropped; the rest of the manifest survives.
363
+ posts: import_zod2.z.array(blogManifestEntrySchema.nullable().catch(null)).transform((entries) => entries.filter((e) => e !== null)).catch([])
364
+ });
365
+ var blogImageSchema = import_zod2.z.object({
366
+ id: import_zod2.z.string(),
367
+ url: import_zod2.z.string(),
368
+ alt: import_zod2.z.string().catch(""),
369
+ caption: import_zod2.z.string().nullable().catch(null),
370
+ role: import_zod2.z.enum(["hero", "inline"]).catch("inline"),
371
+ width: import_zod2.z.number().int(),
372
+ height: import_zod2.z.number().int(),
373
+ mime: import_zod2.z.string().catch("")
374
+ });
375
+ var blogSeoSchema = import_zod2.z.object({
376
+ meta_title: import_zod2.z.string().catch(""),
377
+ meta_description: import_zod2.z.string().catch(""),
378
+ canonical_path: import_zod2.z.string().catch(""),
379
+ og_image: import_zod2.z.string().nullable().catch(null)
380
+ });
381
+ var blogPostContentWireSchema = import_zod2.z.object({
382
+ schema_version: schemaVersionSchema,
383
+ slug: import_zod2.z.string(),
384
+ title: import_zod2.z.string(),
385
+ description: import_zod2.z.string().catch(""),
386
+ mdx: import_zod2.z.string(),
387
+ images: import_zod2.z.array(blogImageSchema).catch([]),
388
+ // Already schema-validated at publish time, but re-sanitized on read (and
389
+ // again before rendering) — the override schema itself drops invalid values.
390
+ // Nullish: the backend serializes an unset override as an explicit `null`
391
+ // (Pydantic `model_dump()`); it folds into `undefined` for TS consumers.
392
+ theme_overrides: nullishField(blogThemeOverrideSchema).catch(void 0),
393
+ seo: blogSeoSchema.catch({
394
+ meta_title: "",
395
+ meta_description: "",
396
+ canonical_path: "",
397
+ og_image: null
398
+ }),
399
+ jsonld: import_zod2.z.record(import_zod2.z.unknown()).catch({}),
400
+ published_at: import_zod2.z.string(),
401
+ // The applied_at of the version this content was published from; `null`
402
+ // when the backend has no separate modified date. Nullish on the wire
403
+ // (Pydantic `model_dump()` emits explicit `null`s), folded to `null` for a
404
+ // single TS sentinel; a malformed value degrades to `null` instead of
405
+ // dropping the post.
406
+ updated_at: import_zod2.z.string().nullish().transform((v) => v ?? null).catch(null),
407
+ version_number: import_zod2.z.number().int().catch(0),
408
+ content_hash: import_zod2.z.string()
409
+ });
410
+ var blogPostContentSchema = blogPostContentWireSchema.extend({
411
+ // A single malformed image record is dropped; the post survives.
412
+ images: import_zod2.z.array(blogImageSchema.nullable().catch(null)).transform((images) => images.filter((i) => i !== null)).catch([])
413
+ });
414
+ function parseBlogManifest(raw) {
415
+ const parsed = blogManifestSchema.safeParse(raw);
416
+ return parsed.success ? parsed.data : null;
417
+ }
418
+ function parseBlogPostContent(raw) {
419
+ const parsed = blogPostContentSchema.safeParse(raw);
420
+ return parsed.success ? parsed.data : null;
421
+ }
422
+
423
+ // ../tracking-core/src/resources/blog/client.ts
424
+ function createBlogClient(config) {
425
+ const base = config.cdnBaseUrl.replace(/\/+$/, "");
426
+ const doFetch = (...args) => (config.fetchImpl ?? fetch)(...args);
427
+ async function fetchJson(url, init) {
428
+ try {
429
+ const response = await doFetch(url, init);
430
+ if (!response.ok) return null;
431
+ return await response.json();
432
+ } catch {
433
+ return null;
434
+ }
435
+ }
436
+ async function getManifest() {
437
+ const init = {
438
+ next: { revalidate: config.manifestRevalidateSeconds ?? 300 }
439
+ };
440
+ const raw = await fetchJson(`${base}/blogs/v1/${config.namespace}/manifest.json`, init);
441
+ return raw === null ? null : parseBlogManifest(raw);
442
+ }
443
+ async function listPosts() {
444
+ const manifest = await getManifest();
445
+ if (!manifest) return [];
446
+ return [...manifest.posts].sort((a, b) => b.published_at.localeCompare(a.published_at));
447
+ }
448
+ async function getPost(slug) {
449
+ const manifest = await getManifest();
450
+ const entry = manifest?.posts.find((post) => post.slug === slug);
451
+ if (!entry) return null;
452
+ const init = { cache: "force-cache" };
453
+ const raw = await fetchJson(`${base}/${entry.content_key.replace(/^\/+/, "")}`, init);
454
+ return raw === null ? null : parseBlogPostContent(raw);
455
+ }
456
+ async function getStaticParams() {
457
+ const posts = await listPosts();
458
+ return posts.map((post) => ({ slug: post.slug }));
459
+ }
460
+ return { getManifest, listPosts, getPost, getStaticParams };
461
+ }
462
+
463
+ // ../tracking-core/src/resources/blog/presets.ts
464
+ var BLOG_THEME_PRESETS = {
465
+ /** Serif display headings over a humanist sans body — long-form magazine feel. */
466
+ editorial: {
467
+ typography: {
468
+ heading_family: "'Source Serif 4', 'Iowan Old Style', 'Palatino Linotype', Georgia, serif",
469
+ body_family: "'Source Sans 3', 'Segoe UI', 'Helvetica Neue', Arial, sans-serif",
470
+ heading_weight: 600,
471
+ body_weight: 400,
472
+ h1_size: { base: "2rem", md: "2.5rem" },
473
+ h2_size: { base: "1.5rem", md: "1.875rem" },
474
+ h3_size: "1.3rem",
475
+ h4_size: "1.125rem",
476
+ body_size: "1.0625rem",
477
+ body_line_height: "1.75",
478
+ heading_letter_spacing: "-0.015em"
479
+ },
480
+ spacing: {
481
+ block_gap: "1.35rem",
482
+ section_gap: "3.25rem"
483
+ },
484
+ radius: "6px"
485
+ },
486
+ /** Geometric sans pairing (Inter-first) — tight, contemporary product voice. */
487
+ modern: {
488
+ typography: {
489
+ heading_family: "'Inter', 'SF Pro Display', 'Segoe UI', Roboto, sans-serif",
490
+ body_family: "'Inter', 'SF Pro Text', 'Segoe UI', Roboto, sans-serif",
491
+ heading_weight: 700,
492
+ body_weight: 400,
493
+ h1_size: { base: "2.1rem", md: "2.75rem" },
494
+ h2_size: { base: "1.5rem", md: "1.75rem" },
495
+ h3_size: "1.25rem",
496
+ h4_size: "1.125rem",
497
+ body_size: "1rem",
498
+ body_line_height: "1.7",
499
+ heading_letter_spacing: "-0.03em"
500
+ },
501
+ spacing: {
502
+ block_gap: "1.25rem",
503
+ section_gap: "3rem"
504
+ },
505
+ radius: "12px"
506
+ },
507
+ /** Rounded-friendly pairing with a softer scale — approachable, human tone. */
508
+ warm: {
509
+ typography: {
510
+ heading_family: "'Nunito', 'Avenir Next', 'Trebuchet MS', Verdana, sans-serif",
511
+ body_family: "'Nunito Sans', 'Avenir Next', 'Trebuchet MS', Verdana, sans-serif",
512
+ heading_weight: 700,
513
+ body_weight: 400,
514
+ h1_size: { base: "1.9rem", md: "2.35rem" },
515
+ h2_size: { base: "1.45rem", md: "1.7rem" },
516
+ h3_size: "1.25rem",
517
+ h4_size: "1.125rem",
518
+ body_size: "1.0625rem",
519
+ body_line_height: "1.75",
520
+ heading_letter_spacing: "-0.01em"
521
+ },
522
+ spacing: {
523
+ block_gap: "1.35rem",
524
+ section_gap: "3.25rem"
525
+ },
526
+ radius: "16px"
527
+ },
528
+ /** Workmanlike sans with a prominent code surface — docs/engineering feel. */
529
+ technical: {
530
+ colors: {
531
+ // The one preset that ships a color: a cool code surface is part of the
532
+ // "technical" identity. Everything else stays on the site palette.
533
+ code_background: "#eef1f5"
534
+ },
535
+ colors_dark: {
536
+ code_background: "#1b1f27"
537
+ },
538
+ typography: {
539
+ heading_family: "'IBM Plex Sans', 'Segoe UI', 'Helvetica Neue', Arial, sans-serif",
540
+ body_family: "'IBM Plex Sans', 'Segoe UI', 'Helvetica Neue', Arial, sans-serif",
541
+ heading_weight: 600,
542
+ body_weight: 400,
543
+ h1_size: { base: "1.85rem", md: "2.25rem" },
544
+ h2_size: { base: "1.45rem", md: "1.65rem" },
545
+ h3_size: "1.2rem",
546
+ h4_size: "1.0625rem",
547
+ body_size: "1rem",
548
+ body_line_height: "1.65",
549
+ heading_letter_spacing: "-0.01em"
550
+ },
551
+ spacing: {
552
+ block_gap: "1.15rem",
553
+ section_gap: "2.75rem"
554
+ },
555
+ radius: "6px"
556
+ }
557
+ };
558
+ function prependFamily(front, stack) {
559
+ if (!front) return stack;
560
+ return stack ? `${front}, ${stack}` : front;
561
+ }
562
+ function createBlogFontTheme(preset, fonts) {
563
+ const heading_family = prependFamily(fonts.heading, preset.typography?.heading_family);
564
+ const body_family = prependFamily(fonts.body, preset.typography?.body_family);
565
+ return {
566
+ ...preset,
567
+ typography: {
568
+ ...preset.typography,
569
+ ...heading_family !== void 0 ? { heading_family } : {},
570
+ ...body_family !== void 0 ? { body_family } : {}
571
+ }
572
+ };
573
+ }
574
+
575
+ // ../tracking-core/src/resources/blog/view/class-names.ts
576
+ function joinClassNames(base, extra) {
577
+ return extra ? `${base} ${extra}` : base;
578
+ }
579
+
580
+ // ../tracking-core/src/resources/blog/seo.ts
581
+ function joinUrl(base, ...paths) {
582
+ let out = base.replace(/\/+$/, "");
583
+ for (const path of paths) {
584
+ const segment = path?.replace(/^\/+/, "").replace(/\/+$/, "");
585
+ if (segment) out += `/${segment}`;
586
+ }
587
+ return out;
588
+ }
589
+ function canonicalUrl(post, opts) {
590
+ return joinUrl(opts.siteUrl, opts.basePath ?? "/blog", post.slug);
591
+ }
592
+ function ogImages(post) {
593
+ if (post.seo.og_image) return [{ url: post.seo.og_image }];
594
+ const hero = post.images.find((image) => image.role === "hero");
595
+ if (!hero) return void 0;
596
+ return [{ url: hero.url, width: hero.width, height: hero.height, alt: hero.alt }];
597
+ }
598
+ function buildBlogPostMetadata(post, opts) {
599
+ const url = canonicalUrl(post, opts);
600
+ const title = post.seo.meta_title || post.title;
601
+ const description = post.seo.meta_description || post.description;
602
+ const images = ogImages(post);
603
+ return {
604
+ title,
605
+ description,
606
+ alternates: { canonical: url },
607
+ openGraph: {
608
+ title,
609
+ description,
610
+ type: "article",
611
+ url,
612
+ ...images ? { images } : {},
613
+ ...post.published_at ? { publishedTime: post.published_at } : {}
614
+ }
615
+ };
616
+ }
617
+ function buildBlogPostingJsonLd(post, opts) {
618
+ const url = canonicalUrl(post, opts);
619
+ const images = post.images.map((image) => image.url);
620
+ const doc = {
621
+ headline: post.title,
622
+ description: post.seo.meta_description || post.description,
623
+ datePublished: post.published_at,
624
+ dateModified: post.updated_at ?? post.published_at,
625
+ ...images.length > 0 ? { image: images } : {},
626
+ ...opts.publisherName ? { publisher: { "@type": "Organization", name: opts.publisherName } } : {}
627
+ };
628
+ const backend = post.jsonld;
629
+ const merged = backend && typeof backend === "object" && Object.keys(backend).length > 0 ? { ...doc, ...backend } : doc;
630
+ return {
631
+ ...merged,
632
+ "@context": "https://schema.org",
633
+ "@type": "BlogPosting",
634
+ url,
635
+ mainEntityOfPage: { "@type": "WebPage", "@id": url }
636
+ };
637
+ }
638
+ var FAQ_ITEM_RE = /<FaqItem\s+question=(?:"([^"]*)"|'([^']*)')\s*>([\s\S]*?)<\/FaqItem>/g;
639
+ var CODE_FENCE_RE = /^ {0,3}(`{3,}|~{3,})[^\n]*\n[\s\S]*?(?:^ {0,3}\1`* *$|(?![\s\S]))/gm;
640
+ var ANSWER_TAG_RE = /<[^>]*>/g;
641
+ function extractFaqPairs(mdx) {
642
+ const withoutCode = mdx.replaceAll(CODE_FENCE_RE, "");
643
+ const pairs = [];
644
+ for (const match of withoutCode.matchAll(FAQ_ITEM_RE)) {
645
+ const question = (match[1] ?? match[2] ?? "").trim();
646
+ const answer = match[3].replaceAll(ANSWER_TAG_RE, " ").replaceAll(/\s+/g, " ").trim();
647
+ if (question && answer) pairs.push({ question, answer });
648
+ }
649
+ return pairs;
650
+ }
651
+ function buildFaqJsonLd(pairs) {
652
+ if (pairs.length === 0) return null;
653
+ return {
654
+ "@context": "https://schema.org",
655
+ "@type": "FAQPage",
656
+ mainEntity: pairs.map((pair) => ({
657
+ "@type": "Question",
658
+ name: pair.question,
659
+ acceptedAnswer: { "@type": "Answer", text: pair.answer }
660
+ }))
661
+ };
662
+ }
663
+ var JSONLD_ESCAPES = {
664
+ "<": "\\u003c",
665
+ ">": "\\u003e",
666
+ "&": "\\u0026",
667
+ "\u2028": "\\u2028",
668
+ "\u2029": "\\u2029"
669
+ };
670
+ function serializeJsonLd(data) {
671
+ return JSON.stringify(data).replaceAll(/[<>&\u2028\u2029]/g, (ch) => JSONLD_ESCAPES[ch] ?? ch);
672
+ }
673
+ function buildBlogSitemapEntries(posts, opts) {
674
+ return posts.map((post) => ({
675
+ url: joinUrl(opts.siteUrl, opts.basePath ?? "/blog", post.slug),
676
+ lastModified: post.updated_at
677
+ }));
678
+ }
679
+
680
+ // ../tracking-core/src/resources/blog/toc.ts
681
+ var import_github_slugger = __toESM(require("github-slugger"));
682
+ var import_remark_mdx = __toESM(require("remark-mdx"));
683
+ var import_remark_parse = __toESM(require("remark-parse"));
684
+ var import_unified = require("unified");
685
+ var TEXT_LEAF_TYPES = /* @__PURE__ */ new Set(["text", "inlineCode"]);
686
+ function headingText(node) {
687
+ let text = "";
688
+ const stack = [];
689
+ const roots = node.children ?? [];
690
+ for (let i = roots.length - 1; i >= 0; i--) {
691
+ stack.push(roots[i]);
692
+ }
693
+ while (stack.length > 0) {
694
+ const current = stack.pop();
695
+ if (!current) break;
696
+ if (TEXT_LEAF_TYPES.has(current.type) && typeof current.value === "string") {
697
+ text += current.value;
698
+ }
699
+ if (Array.isArray(current.children)) {
700
+ for (let i = current.children.length - 1; i >= 0; i--) {
701
+ stack.push(current.children[i]);
702
+ }
703
+ }
704
+ }
705
+ return text;
706
+ }
707
+ function extractBlogHeadings(mdx) {
708
+ let tree;
709
+ try {
710
+ tree = (0, import_unified.unified)().use(import_remark_parse.default).use(import_remark_mdx.default).parse(mdx);
711
+ } catch {
712
+ return [];
713
+ }
714
+ const slugger = new import_github_slugger.default();
715
+ const headings = [];
716
+ const stack = [tree];
717
+ while (stack.length > 0) {
718
+ const node = stack.pop();
719
+ if (!node) break;
720
+ if (node.type === "heading" && typeof node.depth === "number") {
721
+ const text = headingText(node);
722
+ const id = slugger.slug(text);
723
+ if (node.depth === 2 || node.depth === 3) {
724
+ headings.push({ depth: node.depth, text, id });
725
+ }
726
+ }
727
+ if (Array.isArray(node.children)) {
728
+ for (let i = node.children.length - 1; i >= 0; i--) {
729
+ stack.push(node.children[i]);
730
+ }
731
+ }
732
+ }
733
+ return headings;
734
+ }
735
+ var WORDS_PER_MINUTE = 220;
736
+ var MDX_SYNTAX_RE = /<[^>]*>|```[^`]*```|`|^[#>\-*+\s]+|\]\([^)]*\)|[[\]!]/gm;
737
+ function estimateReadingTime(mdx) {
738
+ const words = mdx.replaceAll(MDX_SYNTAX_RE, " ").split(/\s+/).filter((w) => w.length > 0).length;
739
+ return Math.max(1, Math.round(words / WORDS_PER_MINUTE));
740
+ }
741
+
742
+ // ../tracking-core/src/resources/blog/validate-mdx.ts
743
+ var import_remark_mdx2 = __toESM(require("remark-mdx"));
744
+ var import_remark_parse2 = __toESM(require("remark-parse"));
745
+ var import_unified2 = require("unified");
746
+ var BLOG_MDX_MAX_SIZE = 5e5;
747
+ var BLOG_MDX_MAX_DEPTH = 64;
748
+ var BLOG_MDX_MAX_NODES = 1e5;
749
+ var MAX_VIOLATIONS = 50;
750
+ var BLOG_MDX_COMPONENT_GRAMMAR = [
751
+ {
752
+ name: "Callout",
753
+ attrs: [],
754
+ urlAttrs: [],
755
+ selfClosing: false,
756
+ cssClass: "aranova-blog-callout",
757
+ promptUsage: "<Callout>...</Callout>",
758
+ promptDescription: "a highlighted tip/note block; no attributes."
759
+ },
760
+ {
761
+ name: "Cta",
762
+ attrs: ["href", "label"],
763
+ urlAttrs: ["href"],
764
+ selfClosing: true,
765
+ cssClass: "aranova-blog-cta",
766
+ promptUsage: '<Cta href="..." label="..." />',
767
+ promptDescription: "a call-to-action button (self-closing)."
768
+ },
769
+ {
770
+ name: "BlogImage",
771
+ attrs: ["id"],
772
+ urlAttrs: [],
773
+ selfClosing: true,
774
+ promptUsage: '<BlogImage id="..." />',
775
+ promptDescription: "a generated image placeholder (self-closing). The `id` references an image brief id from the plan; the renderer resolves it."
776
+ },
777
+ {
778
+ name: "FaqItem",
779
+ attrs: ["question"],
780
+ urlAttrs: [],
781
+ selfClosing: false,
782
+ cssClass: "aranova-blog-faq-item",
783
+ promptUsage: '<FaqItem question="...">...</FaqItem>',
784
+ promptDescription: "one FAQ entry: the required `question` attribute carries the question, the children are the prose answer. Group consecutive `<FaqItem>`s under an FAQ heading near the end of the post when the topic suits a Q&A section."
785
+ },
786
+ {
787
+ name: "KeyTakeaways",
788
+ attrs: [],
789
+ urlAttrs: [],
790
+ selfClosing: false,
791
+ cssClass: "aranova-blog-key-takeaways",
792
+ promptUsage: "<KeyTakeaways>...</KeyTakeaways>",
793
+ promptDescription: "a TL;DR box containing a standard markdown bullet list of 3-5 takeaways; place it right after the H1 (and hero image)."
794
+ }
795
+ ];
796
+ var BLOG_MDX_ALLOWED_COMPONENTS = BLOG_MDX_COMPONENT_GRAMMAR.map((entry) => entry.name);
797
+ var ALLOWED_NODE_TYPES = /* @__PURE__ */ new Set([
798
+ "root",
799
+ "heading",
800
+ "paragraph",
801
+ "text",
802
+ "emphasis",
803
+ "strong",
804
+ "delete",
805
+ "link",
806
+ "image",
807
+ "linkReference",
808
+ "imageReference",
809
+ "definition",
810
+ "list",
811
+ "listItem",
812
+ "blockquote",
813
+ "code",
814
+ "inlineCode",
815
+ "table",
816
+ "tableRow",
817
+ "tableCell",
818
+ "thematicBreak",
819
+ "break"
820
+ ]);
821
+ var FORBIDDEN_NODE_TYPES = {
822
+ html: { rule: "raw-html", message: "Raw HTML is not allowed" },
823
+ mdxjsEsm: { rule: "esm", message: "import/export statements are not allowed" },
824
+ mdxFlowExpression: { rule: "expression", message: "JSX expressions ({\u2026}) are not allowed" },
825
+ mdxTextExpression: { rule: "expression", message: "JSX expressions ({\u2026}) are not allowed" }
826
+ };
827
+ var COMPONENT_SPECS = Object.fromEntries(
828
+ BLOG_MDX_COMPONENT_GRAMMAR.map((entry) => [
829
+ entry.name,
830
+ { attrs: entry.attrs, urlAttrs: entry.urlAttrs, allowChildren: !entry.selfClosing }
831
+ ])
832
+ );
833
+ var URL_SCHEME_RE = /^([A-Za-z][A-Za-z0-9+.-]*):/;
834
+ var ALLOWED_URL_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "mailto"]);
835
+ function forbiddenUrlScheme(rawUrl) {
836
+ const cleaned = rawUrl.replaceAll(/[\u0000-\u0020\u007F]/g, "");
837
+ const match = URL_SCHEME_RE.exec(cleaned);
838
+ if (!match) return null;
839
+ const scheme = match[1].toLowerCase();
840
+ return ALLOWED_URL_SCHEMES.has(scheme) ? null : scheme;
841
+ }
842
+ function nodeLine(node) {
843
+ return node.position?.start?.line;
844
+ }
845
+ function checkComponent(node, violations) {
846
+ const line = nodeLine(node);
847
+ const name = node.name;
848
+ if (!name) {
849
+ violations.push({
850
+ rule: "unknown-component",
851
+ message: "JSX fragments (<>\u2026</>) are not allowed",
852
+ line
853
+ });
854
+ return;
855
+ }
856
+ const spec = COMPONENT_SPECS[name];
857
+ if (!spec) {
858
+ violations.push({
859
+ rule: "unknown-component",
860
+ message: `<${name}> is not an allowed element (allowed: ${BLOG_MDX_ALLOWED_COMPONENTS.join(", ")})`,
861
+ line
862
+ });
863
+ return;
864
+ }
865
+ const seen = /* @__PURE__ */ new Set();
866
+ for (const attr of node.attributes ?? []) {
867
+ if (attr.type !== "mdxJsxAttribute" || typeof attr.name !== "string") {
868
+ violations.push({
869
+ rule: "component-attribute",
870
+ message: `<${name}> has a non-literal attribute`,
871
+ line
872
+ });
873
+ continue;
874
+ }
875
+ if (!spec.attrs.includes(attr.name)) {
876
+ violations.push({
877
+ rule: "component-attribute",
878
+ message: `<${name}> does not accept attribute "${attr.name}"`,
879
+ line
880
+ });
881
+ continue;
882
+ }
883
+ if (typeof attr.value !== "string") {
884
+ violations.push({
885
+ rule: "component-attribute",
886
+ message: `<${name} ${attr.name}> must be a quoted string literal`,
887
+ line
888
+ });
889
+ continue;
890
+ }
891
+ seen.add(attr.name);
892
+ if (spec.urlAttrs.includes(attr.name)) {
893
+ const scheme = forbiddenUrlScheme(attr.value);
894
+ if (scheme !== null) {
895
+ violations.push({
896
+ rule: "url-scheme",
897
+ message: `<${name} ${attr.name}> uses forbidden URL scheme "${scheme}:"`,
898
+ line
899
+ });
900
+ }
901
+ }
902
+ }
903
+ for (const required of spec.attrs) {
904
+ if (!seen.has(required)) {
905
+ violations.push({
906
+ rule: "component-attribute",
907
+ message: `<${name}> requires attribute "${required}"`,
908
+ line
909
+ });
910
+ }
911
+ }
912
+ if (!spec.allowChildren && (node.children?.length ?? 0) > 0) {
913
+ violations.push({
914
+ rule: "component-attribute",
915
+ message: `<${name}> must be self-closing (no children)`,
916
+ line
917
+ });
918
+ }
919
+ }
920
+ function validateBlogMdx(mdx, options) {
921
+ const violations = [];
922
+ if (typeof mdx !== "string" || mdx.length > BLOG_MDX_MAX_SIZE) {
923
+ return {
924
+ ok: false,
925
+ violations: [
926
+ {
927
+ rule: "size-limit",
928
+ message: `Document exceeds the ${BLOG_MDX_MAX_SIZE}-character limit`
929
+ }
930
+ ]
931
+ };
932
+ }
933
+ let tree;
934
+ try {
935
+ tree = (0, import_unified2.unified)().use(import_remark_parse2.default).use(import_remark_mdx2.default).parse(mdx);
936
+ } catch (error) {
937
+ const line = error.line;
938
+ return {
939
+ ok: false,
940
+ violations: [
941
+ {
942
+ rule: "parse-error",
943
+ message: "Document is not valid MDX",
944
+ ...typeof line === "number" ? { line } : {}
945
+ }
946
+ ]
947
+ };
948
+ }
949
+ if (options?.requireLeadingH1) {
950
+ const firstChild = tree.children?.[0];
951
+ if (!firstChild || firstChild.type !== "heading" || firstChild.depth !== 1) {
952
+ violations.push({
953
+ rule: "leading-h1",
954
+ message: "Document must start with the H1 title (`# ...`) \u2014 no frontmatter or metadata block before it",
955
+ line: nodeLine(firstChild ?? tree)
956
+ });
957
+ }
958
+ }
959
+ let nodeCount = 0;
960
+ const stack = [{ node: tree, depth: 0 }];
961
+ while (stack.length > 0 && violations.length < MAX_VIOLATIONS) {
962
+ const item = stack.pop();
963
+ if (!item) break;
964
+ const { node, depth } = item;
965
+ nodeCount += 1;
966
+ if (nodeCount > BLOG_MDX_MAX_NODES) {
967
+ violations.push({ rule: "size-limit", message: "Document has too many nodes" });
968
+ break;
969
+ }
970
+ if (depth > BLOG_MDX_MAX_DEPTH) {
971
+ violations.push({ rule: "depth-limit", message: "Document nests too deeply" });
972
+ break;
973
+ }
974
+ const forbidden = FORBIDDEN_NODE_TYPES[node.type];
975
+ if (forbidden) {
976
+ violations.push({ ...forbidden, line: nodeLine(node) });
977
+ continue;
978
+ }
979
+ if (node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") {
980
+ checkComponent(node, violations);
981
+ } else if (!ALLOWED_NODE_TYPES.has(node.type)) {
982
+ violations.push({
983
+ rule: "disallowed-node",
984
+ message: `Markdown construct "${node.type}" is not allowed`,
985
+ line: nodeLine(node)
986
+ });
987
+ continue;
988
+ }
989
+ if ((node.type === "link" || node.type === "image" || node.type === "definition") && typeof node.url === "string") {
990
+ const scheme = forbiddenUrlScheme(node.url);
991
+ if (scheme !== null) {
992
+ violations.push({
993
+ rule: "url-scheme",
994
+ message: `${node.type} uses forbidden URL scheme "${scheme}:"`,
995
+ line: nodeLine(node)
996
+ });
997
+ }
998
+ }
999
+ if (Array.isArray(node.children)) {
1000
+ for (const child of node.children) {
1001
+ stack.push({ node: child, depth: depth + 1 });
1002
+ }
1003
+ }
1004
+ }
1005
+ return violations.length === 0 ? { ok: true } : { ok: false, violations };
1006
+ }
1007
+
1008
+ // ../tracking-core/src/resources/blog/view/blog-post-view.tsx
1009
+ var import_rsc = require("next-mdx-remote/rsc");
1010
+ var import_rehype_slug = __toESM(require("rehype-slug"));
1011
+
1012
+ // ../tracking-core/src/resources/blog/view/components.tsx
1013
+ var import_jsx_runtime = require("react/jsx-runtime");
1014
+ function createDefaultComponents(images, classNames) {
1015
+ const imagesById = new Map(images.map((image) => [image.id, image]));
1016
+ return {
1017
+ // `children` is destructured explicitly so static a11y analysis
1018
+ // (heading-has-content, anchor-has-content) can see the content flow.
1019
+ h1: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { ...props, children }),
1020
+ h2: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { ...props, children }),
1021
+ h3: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { ...props, children }),
1022
+ h4: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h4", { ...props, children }),
1023
+ h5: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h5", { ...props, children }),
1024
+ h6: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h6", { ...props, children }),
1025
+ p: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { ...props, children }),
1026
+ a: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { ...props, children }),
1027
+ img: ({ alt = "", ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { loading: "lazy", alt, ...props }),
1028
+ pre: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { ...props, children }),
1029
+ code: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { ...props, children }),
1030
+ blockquote: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("blockquote", { ...props, children }),
1031
+ ul: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { ...props, children }),
1032
+ ol: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { ...props, children }),
1033
+ li: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { ...props, children }),
1034
+ // Wrapped so wide tables scroll horizontally inside the article instead of
1035
+ // overflowing the host page (the wrapper also carries border + radius).
1036
+ table: ({ children, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: joinClassNames("aranova-blog-table-wrap", classNames?.tableWrap), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("table", { ...props, children }) }),
1037
+ hr: (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", { ...props }),
1038
+ Callout: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("aside", { className: joinClassNames("aranova-blog-callout", classNames?.callout), children }),
1039
+ Cta: ({ href, label }) => (
1040
+ // `data-aranova-cta` opts this into the tracking SDK's cta_click
1041
+ // auto-capture (default selector `[data-aranova-cta]`) with the label as
1042
+ // the cta_name — zero site config. Inert if auto-capture is off.
1043
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1044
+ "a",
1045
+ {
1046
+ className: joinClassNames("aranova-blog-cta", classNames?.cta),
1047
+ href,
1048
+ "data-aranova-cta": label,
1049
+ "data-aranova-section": "blog",
1050
+ children: label
1051
+ }
1052
+ )
1053
+ ),
1054
+ BlogImage: ({ id }) => {
1055
+ const image = imagesById.get(id);
1056
+ if (!image) return null;
1057
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("figure", { className: classNames?.figure, children: [
1058
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1059
+ "img",
1060
+ {
1061
+ src: image.url,
1062
+ alt: image.alt,
1063
+ width: image.width,
1064
+ height: image.height,
1065
+ loading: "lazy"
1066
+ }
1067
+ ),
1068
+ image.caption ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("figcaption", { children: image.caption }) : null
1069
+ ] });
1070
+ },
1071
+ // Native disclosure accordion — no JS, server-renderable. The h3 inside
1072
+ // <summary> is valid HTML and keeps the document outline / a11y heading.
1073
+ FaqItem: ({ question, children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { className: joinClassNames("aranova-blog-faq-item", classNames?.faqItem), children: [
1074
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("summary", { className: joinClassNames("aranova-blog-faq-question", classNames?.faqQuestion), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: question }) }),
1075
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: joinClassNames("aranova-blog-faq-answer", classNames?.faqAnswer), children })
1076
+ ] }),
1077
+ KeyTakeaways: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("aside", { className: joinClassNames("aranova-blog-key-takeaways", classNames?.keyTakeaways), children })
1078
+ };
1079
+ }
1080
+
1081
+ // ../tracking-core/src/resources/blog/view/styles.ts
1082
+ var BLOG_BASE_CSS = `/* Aranova blog post base stylesheet \u2014 authored source of truth.
1083
+ *
1084
+ * Scoped under \`.aranova-blog-post\` and driven entirely by the sanitized
1085
+ * \`--ara-blog-*\` CSS custom properties emitted by \`blogThemeToCssVars\` on the
1086
+ * article element. Host sites usually run Tailwind preflight, so:
1087
+ *
1088
+ * - element rules use \`.aranova-blog-post :where(el)\` \u2014 one class of
1089
+ * specificity (0,1,0), enough to beat preflight's bare element resets
1090
+ * while every rule in this sheet stays flat and order-resolvable;
1091
+ * - component classes (\`.aranova-blog-callout\`, \u2026) keep their natural
1092
+ * class-on-class specificity.
1093
+ *
1094
+ * Responsive-capable tokens (h1/h2/h3/h4/body/small sizes, body line-height,
1095
+ * content max-width, block gap, section gap, radius) are NEVER consumed via their raw
1096
+ * \`--ara-blog-*\` vars here \u2014 the generated block below re-points internal
1097
+ * \`--_*\` vars per breakpoint, and all rules consume those.
1098
+ *
1099
+ * Rhythm rules near the end target BOTH \`.aranova-blog-post\` (content is a
1100
+ * direct child of the article today) and \`.aranova-blog-content\` (a later
1101
+ * phase wraps content in that div) so that phase needs no CSS changes.
1102
+ *
1103
+ * No raw colors anywhere: every color flows through the theme vars, which
1104
+ * already carry \`light-dark(...)\` values when a dark palette is present.
1105
+ */
1106
+
1107
+ /* Responsive token plumbing \u2014 GENERATED by build-styles.ts from theme.ts
1108
+ (BLOG_BREAKPOINT_MIN_WIDTHS_PX + the responsive() token set). Mobile-first:
1109
+ each breakpoint falls back through every narrower key down to the base var.
1110
+ Authored rules consume ONLY the --_* internal vars for these tokens. */
1111
+ .aranova-blog-post {
1112
+ --_h1-size: var(--ara-blog-type-h1-size);
1113
+ --_h2-size: var(--ara-blog-type-h2-size);
1114
+ --_h3-size: var(--ara-blog-type-h3-size);
1115
+ --_h4-size: var(--ara-blog-type-h4-size);
1116
+ --_body-size: var(--ara-blog-type-body-size);
1117
+ --_body-line-height: var(--ara-blog-type-body-line-height);
1118
+ --_small-size: var(--ara-blog-type-small-size);
1119
+ --_content-max-width: var(--ara-blog-space-content-max-width);
1120
+ --_block-gap: var(--ara-blog-space-block-gap);
1121
+ --_section-gap: var(--ara-blog-space-section-gap);
1122
+ --_radius: var(--ara-blog-radius);
1123
+ }
1124
+ @media (min-width: 640px) {
1125
+ .aranova-blog-post {
1126
+ --_h1-size: var(--ara-blog-type-h1-size-sm, var(--ara-blog-type-h1-size));
1127
+ --_h2-size: var(--ara-blog-type-h2-size-sm, var(--ara-blog-type-h2-size));
1128
+ --_h3-size: var(--ara-blog-type-h3-size-sm, var(--ara-blog-type-h3-size));
1129
+ --_h4-size: var(--ara-blog-type-h4-size-sm, var(--ara-blog-type-h4-size));
1130
+ --_body-size: var(--ara-blog-type-body-size-sm, var(--ara-blog-type-body-size));
1131
+ --_body-line-height: var(--ara-blog-type-body-line-height-sm, var(--ara-blog-type-body-line-height));
1132
+ --_small-size: var(--ara-blog-type-small-size-sm, var(--ara-blog-type-small-size));
1133
+ --_content-max-width: var(--ara-blog-space-content-max-width-sm, var(--ara-blog-space-content-max-width));
1134
+ --_block-gap: var(--ara-blog-space-block-gap-sm, var(--ara-blog-space-block-gap));
1135
+ --_section-gap: var(--ara-blog-space-section-gap-sm, var(--ara-blog-space-section-gap));
1136
+ --_radius: var(--ara-blog-radius-sm, var(--ara-blog-radius));
1137
+ }
1138
+ }
1139
+ @media (min-width: 768px) {
1140
+ .aranova-blog-post {
1141
+ --_h1-size: var(--ara-blog-type-h1-size-md, var(--ara-blog-type-h1-size-sm, var(--ara-blog-type-h1-size)));
1142
+ --_h2-size: var(--ara-blog-type-h2-size-md, var(--ara-blog-type-h2-size-sm, var(--ara-blog-type-h2-size)));
1143
+ --_h3-size: var(--ara-blog-type-h3-size-md, var(--ara-blog-type-h3-size-sm, var(--ara-blog-type-h3-size)));
1144
+ --_h4-size: var(--ara-blog-type-h4-size-md, var(--ara-blog-type-h4-size-sm, var(--ara-blog-type-h4-size)));
1145
+ --_body-size: var(--ara-blog-type-body-size-md, var(--ara-blog-type-body-size-sm, var(--ara-blog-type-body-size)));
1146
+ --_body-line-height: var(--ara-blog-type-body-line-height-md, var(--ara-blog-type-body-line-height-sm, var(--ara-blog-type-body-line-height)));
1147
+ --_small-size: var(--ara-blog-type-small-size-md, var(--ara-blog-type-small-size-sm, var(--ara-blog-type-small-size)));
1148
+ --_content-max-width: var(--ara-blog-space-content-max-width-md, var(--ara-blog-space-content-max-width-sm, var(--ara-blog-space-content-max-width)));
1149
+ --_block-gap: var(--ara-blog-space-block-gap-md, var(--ara-blog-space-block-gap-sm, var(--ara-blog-space-block-gap)));
1150
+ --_section-gap: var(--ara-blog-space-section-gap-md, var(--ara-blog-space-section-gap-sm, var(--ara-blog-space-section-gap)));
1151
+ --_radius: var(--ara-blog-radius-md, var(--ara-blog-radius-sm, var(--ara-blog-radius)));
1152
+ }
1153
+ }
1154
+ @media (min-width: 1024px) {
1155
+ .aranova-blog-post {
1156
+ --_h1-size: var(--ara-blog-type-h1-size-lg, var(--ara-blog-type-h1-size-md, var(--ara-blog-type-h1-size-sm, var(--ara-blog-type-h1-size))));
1157
+ --_h2-size: var(--ara-blog-type-h2-size-lg, var(--ara-blog-type-h2-size-md, var(--ara-blog-type-h2-size-sm, var(--ara-blog-type-h2-size))));
1158
+ --_h3-size: var(--ara-blog-type-h3-size-lg, var(--ara-blog-type-h3-size-md, var(--ara-blog-type-h3-size-sm, var(--ara-blog-type-h3-size))));
1159
+ --_h4-size: var(--ara-blog-type-h4-size-lg, var(--ara-blog-type-h4-size-md, var(--ara-blog-type-h4-size-sm, var(--ara-blog-type-h4-size))));
1160
+ --_body-size: var(--ara-blog-type-body-size-lg, var(--ara-blog-type-body-size-md, var(--ara-blog-type-body-size-sm, var(--ara-blog-type-body-size))));
1161
+ --_body-line-height: var(--ara-blog-type-body-line-height-lg, var(--ara-blog-type-body-line-height-md, var(--ara-blog-type-body-line-height-sm, var(--ara-blog-type-body-line-height))));
1162
+ --_small-size: var(--ara-blog-type-small-size-lg, var(--ara-blog-type-small-size-md, var(--ara-blog-type-small-size-sm, var(--ara-blog-type-small-size))));
1163
+ --_content-max-width: var(--ara-blog-space-content-max-width-lg, var(--ara-blog-space-content-max-width-md, var(--ara-blog-space-content-max-width-sm, var(--ara-blog-space-content-max-width))));
1164
+ --_block-gap: var(--ara-blog-space-block-gap-lg, var(--ara-blog-space-block-gap-md, var(--ara-blog-space-block-gap-sm, var(--ara-blog-space-block-gap))));
1165
+ --_section-gap: var(--ara-blog-space-section-gap-lg, var(--ara-blog-space-section-gap-md, var(--ara-blog-space-section-gap-sm, var(--ara-blog-space-section-gap))));
1166
+ --_radius: var(--ara-blog-radius-lg, var(--ara-blog-radius-md, var(--ara-blog-radius-sm, var(--ara-blog-radius))));
1167
+ }
1168
+ }
1169
+ @media (min-width: 1280px) {
1170
+ .aranova-blog-post {
1171
+ --_h1-size: var(--ara-blog-type-h1-size-xl, var(--ara-blog-type-h1-size-lg, var(--ara-blog-type-h1-size-md, var(--ara-blog-type-h1-size-sm, var(--ara-blog-type-h1-size)))));
1172
+ --_h2-size: var(--ara-blog-type-h2-size-xl, var(--ara-blog-type-h2-size-lg, var(--ara-blog-type-h2-size-md, var(--ara-blog-type-h2-size-sm, var(--ara-blog-type-h2-size)))));
1173
+ --_h3-size: var(--ara-blog-type-h3-size-xl, var(--ara-blog-type-h3-size-lg, var(--ara-blog-type-h3-size-md, var(--ara-blog-type-h3-size-sm, var(--ara-blog-type-h3-size)))));
1174
+ --_h4-size: var(--ara-blog-type-h4-size-xl, var(--ara-blog-type-h4-size-lg, var(--ara-blog-type-h4-size-md, var(--ara-blog-type-h4-size-sm, var(--ara-blog-type-h4-size)))));
1175
+ --_body-size: var(--ara-blog-type-body-size-xl, var(--ara-blog-type-body-size-lg, var(--ara-blog-type-body-size-md, var(--ara-blog-type-body-size-sm, var(--ara-blog-type-body-size)))));
1176
+ --_body-line-height: var(--ara-blog-type-body-line-height-xl, var(--ara-blog-type-body-line-height-lg, var(--ara-blog-type-body-line-height-md, var(--ara-blog-type-body-line-height-sm, var(--ara-blog-type-body-line-height)))));
1177
+ --_small-size: var(--ara-blog-type-small-size-xl, var(--ara-blog-type-small-size-lg, var(--ara-blog-type-small-size-md, var(--ara-blog-type-small-size-sm, var(--ara-blog-type-small-size)))));
1178
+ --_content-max-width: var(--ara-blog-space-content-max-width-xl, var(--ara-blog-space-content-max-width-lg, var(--ara-blog-space-content-max-width-md, var(--ara-blog-space-content-max-width-sm, var(--ara-blog-space-content-max-width)))));
1179
+ --_block-gap: var(--ara-blog-space-block-gap-xl, var(--ara-blog-space-block-gap-lg, var(--ara-blog-space-block-gap-md, var(--ara-blog-space-block-gap-sm, var(--ara-blog-space-block-gap)))));
1180
+ --_section-gap: var(--ara-blog-space-section-gap-xl, var(--ara-blog-space-section-gap-lg, var(--ara-blog-space-section-gap-md, var(--ara-blog-space-section-gap-sm, var(--ara-blog-space-section-gap)))));
1181
+ --_radius: var(--ara-blog-radius-xl, var(--ara-blog-radius-lg, var(--ara-blog-radius-md, var(--ara-blog-radius-sm, var(--ara-blog-radius)))));
1182
+ }
1183
+ }
1184
+ @media (min-width: 1536px) {
1185
+ .aranova-blog-post {
1186
+ --_h1-size: var(--ara-blog-type-h1-size-2xl, var(--ara-blog-type-h1-size-xl, var(--ara-blog-type-h1-size-lg, var(--ara-blog-type-h1-size-md, var(--ara-blog-type-h1-size-sm, var(--ara-blog-type-h1-size))))));
1187
+ --_h2-size: var(--ara-blog-type-h2-size-2xl, var(--ara-blog-type-h2-size-xl, var(--ara-blog-type-h2-size-lg, var(--ara-blog-type-h2-size-md, var(--ara-blog-type-h2-size-sm, var(--ara-blog-type-h2-size))))));
1188
+ --_h3-size: var(--ara-blog-type-h3-size-2xl, var(--ara-blog-type-h3-size-xl, var(--ara-blog-type-h3-size-lg, var(--ara-blog-type-h3-size-md, var(--ara-blog-type-h3-size-sm, var(--ara-blog-type-h3-size))))));
1189
+ --_h4-size: var(--ara-blog-type-h4-size-2xl, var(--ara-blog-type-h4-size-xl, var(--ara-blog-type-h4-size-lg, var(--ara-blog-type-h4-size-md, var(--ara-blog-type-h4-size-sm, var(--ara-blog-type-h4-size))))));
1190
+ --_body-size: var(--ara-blog-type-body-size-2xl, var(--ara-blog-type-body-size-xl, var(--ara-blog-type-body-size-lg, var(--ara-blog-type-body-size-md, var(--ara-blog-type-body-size-sm, var(--ara-blog-type-body-size))))));
1191
+ --_body-line-height: var(--ara-blog-type-body-line-height-2xl, var(--ara-blog-type-body-line-height-xl, var(--ara-blog-type-body-line-height-lg, var(--ara-blog-type-body-line-height-md, var(--ara-blog-type-body-line-height-sm, var(--ara-blog-type-body-line-height))))));
1192
+ --_small-size: var(--ara-blog-type-small-size-2xl, var(--ara-blog-type-small-size-xl, var(--ara-blog-type-small-size-lg, var(--ara-blog-type-small-size-md, var(--ara-blog-type-small-size-sm, var(--ara-blog-type-small-size))))));
1193
+ --_content-max-width: var(--ara-blog-space-content-max-width-2xl, var(--ara-blog-space-content-max-width-xl, var(--ara-blog-space-content-max-width-lg, var(--ara-blog-space-content-max-width-md, var(--ara-blog-space-content-max-width-sm, var(--ara-blog-space-content-max-width))))));
1194
+ --_block-gap: var(--ara-blog-space-block-gap-2xl, var(--ara-blog-space-block-gap-xl, var(--ara-blog-space-block-gap-lg, var(--ara-blog-space-block-gap-md, var(--ara-blog-space-block-gap-sm, var(--ara-blog-space-block-gap))))));
1195
+ --_section-gap: var(--ara-blog-space-section-gap-2xl, var(--ara-blog-space-section-gap-xl, var(--ara-blog-space-section-gap-lg, var(--ara-blog-space-section-gap-md, var(--ara-blog-space-section-gap-sm, var(--ara-blog-space-section-gap))))));
1196
+ --_radius: var(--ara-blog-radius-2xl, var(--ara-blog-radius-xl, var(--ara-blog-radius-lg, var(--ara-blog-radius-md, var(--ara-blog-radius-sm, var(--ara-blog-radius))))));
1197
+ }
1198
+ }
1199
+ @media (min-width: 1920px) {
1200
+ .aranova-blog-post {
1201
+ --_h1-size: var(--ara-blog-type-h1-size-3xl, var(--ara-blog-type-h1-size-2xl, var(--ara-blog-type-h1-size-xl, var(--ara-blog-type-h1-size-lg, var(--ara-blog-type-h1-size-md, var(--ara-blog-type-h1-size-sm, var(--ara-blog-type-h1-size)))))));
1202
+ --_h2-size: var(--ara-blog-type-h2-size-3xl, var(--ara-blog-type-h2-size-2xl, var(--ara-blog-type-h2-size-xl, var(--ara-blog-type-h2-size-lg, var(--ara-blog-type-h2-size-md, var(--ara-blog-type-h2-size-sm, var(--ara-blog-type-h2-size)))))));
1203
+ --_h3-size: var(--ara-blog-type-h3-size-3xl, var(--ara-blog-type-h3-size-2xl, var(--ara-blog-type-h3-size-xl, var(--ara-blog-type-h3-size-lg, var(--ara-blog-type-h3-size-md, var(--ara-blog-type-h3-size-sm, var(--ara-blog-type-h3-size)))))));
1204
+ --_h4-size: var(--ara-blog-type-h4-size-3xl, var(--ara-blog-type-h4-size-2xl, var(--ara-blog-type-h4-size-xl, var(--ara-blog-type-h4-size-lg, var(--ara-blog-type-h4-size-md, var(--ara-blog-type-h4-size-sm, var(--ara-blog-type-h4-size)))))));
1205
+ --_body-size: var(--ara-blog-type-body-size-3xl, var(--ara-blog-type-body-size-2xl, var(--ara-blog-type-body-size-xl, var(--ara-blog-type-body-size-lg, var(--ara-blog-type-body-size-md, var(--ara-blog-type-body-size-sm, var(--ara-blog-type-body-size)))))));
1206
+ --_body-line-height: var(--ara-blog-type-body-line-height-3xl, var(--ara-blog-type-body-line-height-2xl, var(--ara-blog-type-body-line-height-xl, var(--ara-blog-type-body-line-height-lg, var(--ara-blog-type-body-line-height-md, var(--ara-blog-type-body-line-height-sm, var(--ara-blog-type-body-line-height)))))));
1207
+ --_small-size: var(--ara-blog-type-small-size-3xl, var(--ara-blog-type-small-size-2xl, var(--ara-blog-type-small-size-xl, var(--ara-blog-type-small-size-lg, var(--ara-blog-type-small-size-md, var(--ara-blog-type-small-size-sm, var(--ara-blog-type-small-size)))))));
1208
+ --_content-max-width: var(--ara-blog-space-content-max-width-3xl, var(--ara-blog-space-content-max-width-2xl, var(--ara-blog-space-content-max-width-xl, var(--ara-blog-space-content-max-width-lg, var(--ara-blog-space-content-max-width-md, var(--ara-blog-space-content-max-width-sm, var(--ara-blog-space-content-max-width)))))));
1209
+ --_block-gap: var(--ara-blog-space-block-gap-3xl, var(--ara-blog-space-block-gap-2xl, var(--ara-blog-space-block-gap-xl, var(--ara-blog-space-block-gap-lg, var(--ara-blog-space-block-gap-md, var(--ara-blog-space-block-gap-sm, var(--ara-blog-space-block-gap)))))));
1210
+ --_section-gap: var(--ara-blog-space-section-gap-3xl, var(--ara-blog-space-section-gap-2xl, var(--ara-blog-space-section-gap-xl, var(--ara-blog-space-section-gap-lg, var(--ara-blog-space-section-gap-md, var(--ara-blog-space-section-gap-sm, var(--ara-blog-space-section-gap)))))));
1211
+ --_radius: var(--ara-blog-radius-3xl, var(--ara-blog-radius-2xl, var(--ara-blog-radius-xl, var(--ara-blog-radius-lg, var(--ara-blog-radius-md, var(--ara-blog-radius-sm, var(--ara-blog-radius)))))));
1212
+ }
1213
+ }
1214
+
1215
+ .aranova-blog-post {
1216
+ /* Internal spacing scale, derived from the responsive block gap. */
1217
+ --_flow: var(--_block-gap);
1218
+ --_flow-sm: calc(var(--_block-gap) * 0.5);
1219
+ --_flow-lg: calc(var(--_block-gap) * 1.75);
1220
+ --_flow-section: var(--_section-gap);
1221
+ /* Optional heading color token \u2014 themes that don't set it fall back to text. */
1222
+ --_heading-color: var(--ara-blog-color-heading, var(--ara-blog-color-text));
1223
+
1224
+ max-width: var(--_content-max-width);
1225
+ margin-inline: auto;
1226
+ background: var(--ara-blog-color-background);
1227
+ color: var(--ara-blog-color-text);
1228
+ font-family: var(--ara-blog-type-body-family);
1229
+ font-weight: var(--ara-blog-type-body-weight);
1230
+ font-size: var(--_body-size);
1231
+ line-height: var(--_body-line-height);
1232
+ overflow-wrap: break-word;
1233
+ }
1234
+ @media (max-width: 639.99px) {
1235
+ .aranova-blog-post {
1236
+ padding-inline: 1.25rem;
1237
+ }
1238
+ }
1239
+
1240
+ /* --- Headings ------------------------------------------------------------ */
1241
+ .aranova-blog-post :where(h1, h2, h3, h4, h5, h6) {
1242
+ font-family: var(--ara-blog-type-heading-family);
1243
+ font-weight: var(--ara-blog-type-heading-weight);
1244
+ line-height: 1.25;
1245
+ margin-block: 0;
1246
+ color: var(--_heading-color);
1247
+ scroll-margin-top: 5rem;
1248
+ }
1249
+ .aranova-blog-post :where(h1, h2, h3) {
1250
+ /* \`pretty\` fills each line greedily (only the LAST line is massaged to avoid
1251
+ * an orphan word) \u2014 so a heading uses the full measure. \`balance\` was leaving
1252
+ * short first lines. */
1253
+ text-wrap: pretty;
1254
+ }
1255
+ .aranova-blog-post :where(h1) {
1256
+ font-size: var(--_h1-size);
1257
+ letter-spacing: var(--ara-blog-type-heading-letter-spacing);
1258
+ }
1259
+ .aranova-blog-post :where(h2) {
1260
+ font-size: var(--_h2-size);
1261
+ letter-spacing: var(--ara-blog-type-heading-letter-spacing);
1262
+ }
1263
+ .aranova-blog-post :where(h3) {
1264
+ font-size: var(--_h3-size);
1265
+ letter-spacing: calc(var(--ara-blog-type-heading-letter-spacing) * 0.5);
1266
+ }
1267
+ .aranova-blog-post :where(h4) {
1268
+ font-size: var(--_h4-size);
1269
+ font-weight: 600;
1270
+ letter-spacing: calc(var(--ara-blog-type-heading-letter-spacing) * 0.5);
1271
+ }
1272
+ .aranova-blog-post :where(h5) {
1273
+ font-size: var(--_body-size);
1274
+ font-weight: 600;
1275
+ }
1276
+ .aranova-blog-post :where(h6) {
1277
+ font-size: var(--_small-size);
1278
+ font-weight: 600;
1279
+ text-transform: uppercase;
1280
+ letter-spacing: 0.05em;
1281
+ color: var(--ara-blog-color-text-muted);
1282
+ }
1283
+
1284
+ /* --- Text ---------------------------------------------------------------- */
1285
+ .aranova-blog-post :where(p) {
1286
+ margin-block: 0;
1287
+ }
1288
+ /* Lead paragraph: the one directly after the post title. */
1289
+ .aranova-blog-post :where(h1) + :where(p) {
1290
+ font-size: 1.15em;
1291
+ color: color-mix(in srgb, var(--ara-blog-color-text) 72%, var(--ara-blog-color-text-muted));
1292
+ }
1293
+ .aranova-blog-post :where(a) {
1294
+ color: var(--ara-blog-color-link);
1295
+ text-decoration: underline;
1296
+ text-underline-offset: 0.15em;
1297
+ }
1298
+ .aranova-blog-post :where(a):focus-visible {
1299
+ outline: 2px solid var(--ara-blog-color-link);
1300
+ outline-offset: 2px;
1301
+ border-radius: 2px;
1302
+ }
1303
+
1304
+ /* --- Lists ---------------------------------------------------------------- */
1305
+ .aranova-blog-post :where(ul, ol) {
1306
+ margin-block: 0;
1307
+ padding-inline-start: 1.5em;
1308
+ }
1309
+ .aranova-blog-post :where(ul) {
1310
+ list-style-type: disc;
1311
+ }
1312
+ .aranova-blog-post :where(ul ul) {
1313
+ list-style-type: circle;
1314
+ }
1315
+ .aranova-blog-post :where(ul ul ul) {
1316
+ list-style-type: square;
1317
+ }
1318
+ .aranova-blog-post :where(ol) {
1319
+ list-style-type: decimal;
1320
+ }
1321
+ .aranova-blog-post :where(ol ol) {
1322
+ list-style-type: lower-alpha;
1323
+ }
1324
+ .aranova-blog-post :where(ul) > li::marker {
1325
+ color: var(--ara-blog-color-text-muted);
1326
+ }
1327
+ .aranova-blog-post :where(ol) > li::marker {
1328
+ font-weight: 600;
1329
+ color: var(--_heading-color);
1330
+ }
1331
+ .aranova-blog-post :where(li + li) {
1332
+ margin-block-start: 0.35em;
1333
+ }
1334
+ .aranova-blog-post :where(li > ul, li > ol) {
1335
+ margin-block-start: 0.35em;
1336
+ }
1337
+
1338
+ /* --- Blockquote ------------------------------------------------------------ */
1339
+ .aranova-blog-post :where(blockquote) {
1340
+ margin: 0;
1341
+ padding-block: 0.25em;
1342
+ padding-inline: 1.25em 0.5em;
1343
+ border-inline-start: 3px solid var(--ara-blog-color-primary);
1344
+ color: var(--ara-blog-color-text-muted);
1345
+ font-style: italic;
1346
+ }
1347
+
1348
+ /* --- Code ------------------------------------------------------------------ */
1349
+ .aranova-blog-post :where(code) {
1350
+ background: var(--ara-blog-color-code-background);
1351
+ border-radius: calc(var(--_radius) / 2);
1352
+ padding: 0.15em 0.35em;
1353
+ font-size: 0.9em;
1354
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
1355
+ }
1356
+ .aranova-blog-post :where(pre) {
1357
+ margin: 0;
1358
+ background: var(--ara-blog-color-code-background);
1359
+ border: 1px solid var(--ara-blog-color-border);
1360
+ border-radius: var(--_radius);
1361
+ padding: 1em;
1362
+ overflow-x: auto;
1363
+ line-height: 1.5;
1364
+ }
1365
+ .aranova-blog-post :where(pre code) {
1366
+ background: none;
1367
+ border-radius: 0;
1368
+ padding: 0;
1369
+ font-size: var(--_small-size);
1370
+ }
1371
+
1372
+ /* --- Media ------------------------------------------------------------------ */
1373
+ .aranova-blog-post :where(img) {
1374
+ max-width: 100%;
1375
+ height: auto;
1376
+ display: block;
1377
+ border: 1px solid var(--ara-blog-color-border);
1378
+ border-radius: var(--_radius);
1379
+ }
1380
+ .aranova-blog-post :where(figure) {
1381
+ margin: 0;
1382
+ }
1383
+ .aranova-blog-post :where(figcaption) {
1384
+ margin-block-start: 0.5em;
1385
+ font-size: var(--_small-size);
1386
+ color: var(--ara-blog-color-text-muted);
1387
+ text-align: var(--ara-blog-type-caption-align, center);
1388
+ }
1389
+
1390
+ /* --- Tables ------------------------------------------------------------------ */
1391
+ .aranova-blog-post .aranova-blog-table-wrap {
1392
+ overflow-x: auto;
1393
+ border: 1px solid var(--ara-blog-color-border);
1394
+ border-radius: var(--_radius);
1395
+ }
1396
+ .aranova-blog-post :where(table) {
1397
+ border-collapse: collapse;
1398
+ width: 100%;
1399
+ font-size: var(--_small-size);
1400
+ }
1401
+ .aranova-blog-post :where(th, td) {
1402
+ border: 0;
1403
+ border-block-end: 1px solid var(--ara-blog-color-border);
1404
+ padding: 0.6em 0.85em;
1405
+ text-align: left;
1406
+ }
1407
+ .aranova-blog-post :where(th) {
1408
+ background: var(--ara-blog-color-surface);
1409
+ border-block-end: 2px solid var(--ara-blog-color-border);
1410
+ font-weight: 600;
1411
+ }
1412
+ .aranova-blog-post :where(tbody tr:nth-child(even) td) {
1413
+ background: color-mix(
1414
+ in srgb,
1415
+ var(--ara-blog-color-surface) 55%,
1416
+ var(--ara-blog-color-background)
1417
+ );
1418
+ }
1419
+ .aranova-blog-post :where(tbody tr:last-child) :where(th, td) {
1420
+ border-block-end: 0;
1421
+ }
1422
+
1423
+ /* --- Section break ------------------------------------------------------------ */
1424
+ .aranova-blog-post :where(hr) {
1425
+ border: none;
1426
+ border-top: 2px solid var(--ara-blog-color-border);
1427
+ width: 4rem;
1428
+ margin-inline: auto;
1429
+ }
1430
+
1431
+ /* --- Callout ------------------------------------------------------------------ */
1432
+ .aranova-blog-post .aranova-blog-callout {
1433
+ background: var(--ara-blog-color-surface);
1434
+ border: 1px solid var(--ara-blog-color-border);
1435
+ border-inline-start: 4px solid var(--ara-blog-color-primary);
1436
+ border-radius: var(--_radius);
1437
+ padding: 0.85em 1.1em;
1438
+ }
1439
+ .aranova-blog-post .aranova-blog-callout > * + * {
1440
+ margin-block-start: var(--_flow-sm);
1441
+ }
1442
+
1443
+ /* --- CTA ------------------------------------------------------------------ */
1444
+ .aranova-blog-post .aranova-blog-cta {
1445
+ display: inline-block;
1446
+ background: var(--ara-blog-color-primary);
1447
+ color: var(--ara-blog-color-primary-foreground);
1448
+ border-radius: var(--_radius);
1449
+ padding: 0.6em 1.2em;
1450
+ font-weight: 600;
1451
+ text-decoration: none;
1452
+ transition:
1453
+ filter 150ms ease,
1454
+ transform 150ms ease;
1455
+ }
1456
+ .aranova-blog-post .aranova-blog-cta:hover {
1457
+ filter: brightness(1.08);
1458
+ transform: translateY(-1px);
1459
+ }
1460
+ .aranova-blog-post .aranova-blog-cta:active {
1461
+ filter: none;
1462
+ transform: none;
1463
+ }
1464
+ .aranova-blog-post .aranova-blog-cta:focus-visible {
1465
+ outline: 2px solid var(--ara-blog-color-primary);
1466
+ outline-offset: 2px;
1467
+ }
1468
+
1469
+ /* --- FAQ (details/summary accordion) ------------------------------------------ */
1470
+ .aranova-blog-post .aranova-blog-faq-item {
1471
+ border-block-start: 1px solid var(--ara-blog-color-border);
1472
+ padding-block: calc(var(--_flow) * 0.75);
1473
+ }
1474
+ .aranova-blog-post .aranova-blog-faq-question {
1475
+ cursor: pointer;
1476
+ list-style: none;
1477
+ position: relative;
1478
+ padding-inline-end: 1.75em;
1479
+ }
1480
+ .aranova-blog-post .aranova-blog-faq-question::-webkit-details-marker {
1481
+ display: none;
1482
+ }
1483
+ /* Rotating chevron (border trick \u2014 colors via theme vars only). */
1484
+ .aranova-blog-post .aranova-blog-faq-question::after {
1485
+ content: "";
1486
+ position: absolute;
1487
+ inset-block-start: 50%;
1488
+ inset-inline-end: 0.35em;
1489
+ width: 0.45em;
1490
+ height: 0.45em;
1491
+ border-inline-end: 2px solid var(--ara-blog-color-text-muted);
1492
+ border-block-end: 2px solid var(--ara-blog-color-text-muted);
1493
+ transform: translateY(-70%) rotate(45deg);
1494
+ transition: transform 150ms ease;
1495
+ }
1496
+ .aranova-blog-post .aranova-blog-faq-item[open] > .aranova-blog-faq-question::after {
1497
+ transform: translateY(-30%) rotate(225deg);
1498
+ }
1499
+ .aranova-blog-post .aranova-blog-faq-question:focus-visible {
1500
+ outline: 2px solid var(--ara-blog-color-link);
1501
+ outline-offset: 2px;
1502
+ border-radius: 2px;
1503
+ }
1504
+ .aranova-blog-post .aranova-blog-faq-answer {
1505
+ padding-block-start: var(--_flow-sm);
1506
+ }
1507
+ .aranova-blog-post .aranova-blog-faq-answer > * + * {
1508
+ margin-block-start: var(--_flow-sm);
1509
+ }
1510
+ /* Progressive open/close animation where the platform supports it. */
1511
+ @supports (interpolate-size: allow-keywords) {
1512
+ .aranova-blog-post .aranova-blog-faq-item {
1513
+ interpolate-size: allow-keywords;
1514
+ }
1515
+ .aranova-blog-post .aranova-blog-faq-item::details-content {
1516
+ block-size: 0;
1517
+ overflow-y: clip;
1518
+ transition:
1519
+ block-size 200ms ease,
1520
+ content-visibility 200ms allow-discrete;
1521
+ }
1522
+ .aranova-blog-post .aranova-blog-faq-item[open]::details-content {
1523
+ block-size: auto;
1524
+ }
1525
+ }
1526
+
1527
+ /* --- Key takeaways ------------------------------------------------------------ */
1528
+ .aranova-blog-post .aranova-blog-key-takeaways {
1529
+ background: var(--ara-blog-color-surface);
1530
+ border: 1px solid var(--ara-blog-color-border);
1531
+ border-radius: var(--_radius);
1532
+ padding: 1em 1.25em;
1533
+ }
1534
+ .aranova-blog-post .aranova-blog-key-takeaways :where(ul) {
1535
+ margin-block: 0;
1536
+ }
1537
+ .aranova-blog-post .aranova-blog-key-takeaways :where(ul) > li::marker {
1538
+ color: var(--ara-blog-color-primary);
1539
+ }
1540
+
1541
+ /* --- Article layout (content column + optional aside rail) -----------------------
1542
+ * \`.aranova-blog-layout\` is a plain block on narrow viewports (the aside, when
1543
+ * present, stacks after the content). On wide viewports a grid opens ONLY when
1544
+ * an aside exists, and the article widens by exactly the rail + gap so the
1545
+ * content column keeps its \`content_max_width\`. */
1546
+ .aranova-blog-post .aranova-blog-layout {
1547
+ /* Flex column (not block) so the aside can be VISUALLY hoisted above the
1548
+ * content on narrow viewports while the content stays first in DOM
1549
+ * (reading order / SEO). */
1550
+ display: flex;
1551
+ flex-direction: column;
1552
+ }
1553
+ .aranova-blog-post .aranova-blog-layout > .aranova-blog-aside {
1554
+ order: -1;
1555
+ margin-block-end: var(--_flow-lg);
1556
+ }
1557
+ @media (min-width: 1100px) {
1558
+ .aranova-blog-post:has(.aranova-blog-aside) {
1559
+ max-width: calc(var(--_content-max-width) + 240px + 3rem);
1560
+ }
1561
+ .aranova-blog-post .aranova-blog-layout:has(.aranova-blog-aside) {
1562
+ display: grid;
1563
+ grid-template-columns: minmax(0, 1fr) 240px;
1564
+ column-gap: 3rem;
1565
+ align-items: start;
1566
+ }
1567
+ .aranova-blog-post .aranova-blog-layout:has(.aranova-blog-aside) > .aranova-blog-aside {
1568
+ /* Reset the mobile hoist: grid auto-placement honors \`order\`, so -1 would
1569
+ * drop the aside into the FIRST column. */
1570
+ order: 0;
1571
+ margin-block-end: 0;
1572
+ position: sticky;
1573
+ top: 5rem;
1574
+ align-self: start;
1575
+ max-height: calc(100vh - 6rem);
1576
+ overflow: auto;
1577
+ }
1578
+ /* Rail on the LEFT: flip the column order (content DOM stays first for
1579
+ * reading order / SEO; only the visual columns swap). */
1580
+ .aranova-blog-post .aranova-blog-layout[data-toc-side="left"]:has(.aranova-blog-aside) {
1581
+ grid-template-columns: 240px minmax(0, 1fr);
1582
+ }
1583
+ /* Horizontal collapse, CLOSED: the rail column shrinks to the compact toggle
1584
+ * (auto) and the article reflows into the reclaimed space. */
1585
+ .aranova-blog-post
1586
+ .aranova-blog-layout:has(
1587
+ .aranova-blog-toc-panel[data-collapse="horizontal"]:not([data-open="true"])
1588
+ ) {
1589
+ grid-template-columns: minmax(0, 1fr) auto;
1590
+ }
1591
+ .aranova-blog-post
1592
+ .aranova-blog-layout[data-toc-side="left"]:has(
1593
+ .aranova-blog-toc-panel[data-collapse="horizontal"]:not([data-open="true"])
1594
+ ) {
1595
+ grid-template-columns: auto minmax(0, 1fr);
1596
+ }
1597
+ .aranova-blog-post
1598
+ .aranova-blog-layout[data-toc-side="left"]:has(.aranova-blog-aside)
1599
+ > .aranova-blog-content {
1600
+ order: 2;
1601
+ }
1602
+ .aranova-blog-post
1603
+ .aranova-blog-layout[data-toc-side="left"]:has(.aranova-blog-aside)
1604
+ > .aranova-blog-aside {
1605
+ order: 1;
1606
+ }
1607
+ }
1608
+
1609
+ /* --- Byline / meta row (chrome) --------------------------------------------------- */
1610
+ .aranova-blog-post .aranova-blog-meta {
1611
+ display: flex;
1612
+ flex-wrap: wrap;
1613
+ align-items: center;
1614
+ gap: 0.35em 0.5em;
1615
+ font-size: var(--_small-size);
1616
+ color: var(--ara-blog-color-text-muted);
1617
+ padding-block-end: var(--_flow);
1618
+ border-block-end: 1px solid var(--ara-blog-color-border);
1619
+ }
1620
+ .aranova-blog-post .aranova-blog-meta-item {
1621
+ display: inline-flex;
1622
+ align-items: baseline;
1623
+ gap: 0.5em;
1624
+ white-space: nowrap;
1625
+ }
1626
+ .aranova-blog-post .aranova-blog-meta-tags {
1627
+ display: inline-flex;
1628
+ flex-wrap: wrap;
1629
+ gap: 0.35em;
1630
+ margin-inline-start: auto;
1631
+ }
1632
+ .aranova-blog-post .aranova-blog-meta-tag {
1633
+ background: var(--ara-blog-color-surface);
1634
+ border: 1px solid var(--ara-blog-color-border);
1635
+ border-radius: 999px;
1636
+ padding: 0.1em 0.7em;
1637
+ white-space: nowrap;
1638
+ }
1639
+
1640
+ /* --- Breadcrumbs (chrome) ---------------------------------------------------------- */
1641
+ .aranova-blog-post .aranova-blog-breadcrumbs {
1642
+ font-size: var(--_small-size);
1643
+ color: var(--ara-blog-color-text-muted);
1644
+ }
1645
+ .aranova-blog-post .aranova-blog-breadcrumbs ol {
1646
+ list-style: none;
1647
+ margin: 0;
1648
+ padding: 0;
1649
+ display: flex;
1650
+ flex-wrap: wrap;
1651
+ align-items: baseline;
1652
+ gap: 0.2em 0.5em;
1653
+ }
1654
+ .aranova-blog-post .aranova-blog-breadcrumbs li {
1655
+ display: inline-flex;
1656
+ align-items: baseline;
1657
+ gap: 0.5em;
1658
+ }
1659
+ .aranova-blog-post .aranova-blog-breadcrumbs a {
1660
+ color: var(--ara-blog-color-text-muted);
1661
+ text-decoration: none;
1662
+ }
1663
+ .aranova-blog-post .aranova-blog-breadcrumbs a:hover {
1664
+ color: var(--ara-blog-color-link);
1665
+ text-decoration: underline;
1666
+ text-underline-offset: 0.15em;
1667
+ }
1668
+ .aranova-blog-post .aranova-blog-breadcrumbs [aria-current="page"] {
1669
+ color: var(--ara-blog-color-text);
1670
+ }
1671
+
1672
+ /* --- Table of contents (chrome) -----------------------------------------------------
1673
+ * Zero-JS shell: a native details/summary disclosure. Chevron follows the FAQ
1674
+ * accordion's border-trick pattern (theme vars only, no raw colors). */
1675
+ .aranova-blog-post .aranova-blog-toc {
1676
+ font-size: var(--_small-size);
1677
+ background: var(--ara-blog-color-surface);
1678
+ border: 1px solid var(--ara-blog-color-border);
1679
+ border-radius: var(--_radius);
1680
+ padding: 0.85em 1.1em;
1681
+ }
1682
+ .aranova-blog-post .aranova-blog-toc-title {
1683
+ cursor: pointer;
1684
+ list-style: none;
1685
+ position: relative;
1686
+ padding-inline-end: 1.75em;
1687
+ font-weight: 600;
1688
+ color: var(--_heading-color);
1689
+ }
1690
+ .aranova-blog-post .aranova-blog-toc-title::-webkit-details-marker {
1691
+ display: none;
1692
+ }
1693
+ .aranova-blog-post .aranova-blog-toc-title::after {
1694
+ content: "";
1695
+ position: absolute;
1696
+ inset-block-start: 50%;
1697
+ inset-inline-end: 0.35em;
1698
+ width: 0.45em;
1699
+ height: 0.45em;
1700
+ border-inline-end: 2px solid var(--ara-blog-color-text-muted);
1701
+ border-block-end: 2px solid var(--ara-blog-color-text-muted);
1702
+ transform: translateY(-70%) rotate(45deg);
1703
+ transition: transform 150ms ease;
1704
+ }
1705
+ .aranova-blog-post .aranova-blog-toc[open] > .aranova-blog-toc-title::after {
1706
+ transform: translateY(-30%) rotate(225deg);
1707
+ }
1708
+ .aranova-blog-post .aranova-blog-toc-title:focus-visible {
1709
+ outline: 2px solid var(--ara-blog-color-link);
1710
+ outline-offset: 2px;
1711
+ border-radius: 2px;
1712
+ }
1713
+ .aranova-blog-post .aranova-blog-toc ol {
1714
+ list-style: none;
1715
+ margin: 0.75em 0 0;
1716
+ padding: 0;
1717
+ }
1718
+ .aranova-blog-post .aranova-blog-toc li {
1719
+ margin: 0;
1720
+ }
1721
+ .aranova-blog-post .aranova-blog-toc li + li {
1722
+ margin-block-start: 0.3em;
1723
+ }
1724
+ .aranova-blog-post .aranova-blog-toc li[data-depth="3"] {
1725
+ padding-inline-start: 0.9em;
1726
+ font-size: 0.94em;
1727
+ }
1728
+ .aranova-blog-post .aranova-blog-toc a {
1729
+ display: -webkit-box;
1730
+ -webkit-box-orient: vertical;
1731
+ -webkit-line-clamp: 2;
1732
+ overflow: hidden;
1733
+ line-height: 1.35;
1734
+ color: var(--ara-blog-color-text-muted);
1735
+ text-decoration: none;
1736
+ }
1737
+ .aranova-blog-post .aranova-blog-toc a:hover {
1738
+ color: var(--ara-blog-color-link);
1739
+ text-decoration: underline;
1740
+ text-underline-offset: 0.15em;
1741
+ }
1742
+
1743
+ /* --- Table of contents (client island) ------------------------------------------
1744
+ * Animated companion to the zero-JS shell above: same box, but a real <button>
1745
+ * header (aria-expanded) and a grid-template-rows 0fr\u21941fr collapse. The global
1746
+ * reduced-motion block at the end of this sheet disables the transition. */
1747
+ .aranova-blog-post .aranova-blog-toc-panel {
1748
+ font-size: var(--_small-size);
1749
+ background: var(--ara-blog-color-surface);
1750
+ border: 1px solid var(--ara-blog-color-border);
1751
+ border-radius: var(--_radius);
1752
+ padding: 0.85em 1.1em;
1753
+ }
1754
+ .aranova-blog-post .aranova-blog-toc-panel-title {
1755
+ /* Button reset \u2014 host sites may or may not run Tailwind preflight. */
1756
+ appearance: none;
1757
+ background: none;
1758
+ border: 0;
1759
+ margin: 0;
1760
+ padding: 0;
1761
+ padding-inline-end: 1.75em;
1762
+ width: 100%;
1763
+ text-align: start;
1764
+ font: inherit;
1765
+ cursor: pointer;
1766
+ position: relative;
1767
+ font-weight: 600;
1768
+ color: var(--_heading-color);
1769
+ }
1770
+ /* Rotating chevron (border trick \u2014 same pattern as the FAQ / zero-JS TOC). */
1771
+ .aranova-blog-post .aranova-blog-toc-panel-title::after {
1772
+ content: "";
1773
+ position: absolute;
1774
+ inset-block-start: 50%;
1775
+ inset-inline-end: 0.35em;
1776
+ width: 0.45em;
1777
+ height: 0.45em;
1778
+ border-inline-end: 2px solid var(--ara-blog-color-text-muted);
1779
+ border-block-end: 2px solid var(--ara-blog-color-text-muted);
1780
+ transform: translateY(-70%) rotate(45deg);
1781
+ transition: transform 250ms ease;
1782
+ }
1783
+ .aranova-blog-post
1784
+ .aranova-blog-toc-panel[data-open="true"]
1785
+ > .aranova-blog-toc-panel-title::after {
1786
+ transform: translateY(-30%) rotate(225deg);
1787
+ }
1788
+ .aranova-blog-post .aranova-blog-toc-panel-title:focus-visible {
1789
+ outline: 2px solid var(--ara-blog-color-link);
1790
+ outline-offset: 2px;
1791
+ border-radius: 2px;
1792
+ }
1793
+ /* Collapsible body: the grid row animates 0fr\u21941fr; the clip wrapper hides overflow. */
1794
+ .aranova-blog-post .aranova-blog-toc-panel-body {
1795
+ display: grid;
1796
+ grid-template-rows: 0fr;
1797
+ transition: grid-template-rows 250ms ease;
1798
+ }
1799
+ .aranova-blog-post .aranova-blog-toc-panel[data-open="true"] > .aranova-blog-toc-panel-body {
1800
+ grid-template-rows: 1fr;
1801
+ }
1802
+ /* Horizontal collapse: a clean binary toggle along the inline axis. Open = the
1803
+ * normal rail; closed = the whole panel is a compact chevron chip and the rail
1804
+ * grid column reclaims the space (via \`:has()\` on the layout, below). No width
1805
+ * animation: grid \`auto\` tracks can't interpolate, and a sliding clip either
1806
+ * overflows the sticky aside (horizontal scrollbar) or reflows the entries \u2014
1807
+ * instant + correct beats janky. */
1808
+ .aranova-blog-post
1809
+ .aranova-blog-toc-panel[data-collapse="horizontal"]
1810
+ > .aranova-blog-toc-panel-body {
1811
+ display: block;
1812
+ grid-template-rows: none;
1813
+ }
1814
+ .aranova-blog-post
1815
+ .aranova-blog-toc-panel[data-collapse="horizontal"]:not([data-open="true"])
1816
+ > .aranova-blog-toc-panel-body {
1817
+ display: none;
1818
+ }
1819
+ /* Closed panel compacts to a small rounded chevron chip: the label is clipped
1820
+ * out of a square toggle box and the panel hugs it (the rail's \`auto\` grid
1821
+ * column then shrinks to the chip). */
1822
+ .aranova-blog-post .aranova-blog-toc-panel[data-collapse="horizontal"]:not([data-open="true"]) {
1823
+ width: fit-content;
1824
+ padding: 0.6em;
1825
+ }
1826
+ .aranova-blog-post
1827
+ .aranova-blog-toc-panel[data-collapse="horizontal"]:not([data-open="true"])
1828
+ > .aranova-blog-toc-panel-title {
1829
+ display: block;
1830
+ width: 1.4em;
1831
+ height: 1.4em;
1832
+ overflow: hidden;
1833
+ text-indent: 150%;
1834
+ white-space: nowrap;
1835
+ padding-inline-end: 0;
1836
+ }
1837
+ /* Horizontal chevron points along the fold axis (\u2039/\u203A), flipping on open/close
1838
+ * per side, instead of the vertical \u2304/\u2303. */
1839
+ .aranova-blog-post
1840
+ .aranova-blog-toc-panel[data-collapse="horizontal"]
1841
+ > .aranova-blog-toc-panel-title::after {
1842
+ inset-inline: auto 0.5em;
1843
+ }
1844
+ .aranova-blog-post
1845
+ .aranova-blog-toc-panel[data-collapse="horizontal"]:not([data-open="true"])
1846
+ > .aranova-blog-toc-panel-title::after {
1847
+ inset-inline: 0;
1848
+ margin-inline: auto;
1849
+ }
1850
+ /* Left rail: open folds left (\u2039 = rotate 135deg), closed expands right (\u203A = -45deg). */
1851
+ .aranova-blog-post
1852
+ .aranova-blog-toc-panel[data-collapse="horizontal"][data-side="left"][data-open="true"]
1853
+ > .aranova-blog-toc-panel-title::after {
1854
+ transform: translateY(-30%) rotate(135deg);
1855
+ }
1856
+ .aranova-blog-post
1857
+ .aranova-blog-toc-panel[data-collapse="horizontal"][data-side="left"]:not([data-open="true"])
1858
+ > .aranova-blog-toc-panel-title::after {
1859
+ transform: translateY(-70%) rotate(-45deg);
1860
+ }
1861
+ /* Right rail: mirror \u2014 open folds right (\u203A), closed expands left (\u2039). */
1862
+ .aranova-blog-post
1863
+ .aranova-blog-toc-panel[data-collapse="horizontal"][data-side="right"][data-open="true"]
1864
+ > .aranova-blog-toc-panel-title::after {
1865
+ transform: translateY(-70%) rotate(-45deg);
1866
+ }
1867
+ .aranova-blog-post
1868
+ .aranova-blog-toc-panel[data-collapse="horizontal"][data-side="right"]:not([data-open="true"])
1869
+ > .aranova-blog-toc-panel-title::after {
1870
+ transform: translateY(-30%) rotate(135deg);
1871
+ }
1872
+ .aranova-blog-post .aranova-blog-toc-panel-clip {
1873
+ overflow: hidden;
1874
+ min-height: 0;
1875
+ }
1876
+ .aranova-blog-post .aranova-blog-toc-panel ol {
1877
+ list-style: none;
1878
+ margin: 0.75em 0 0;
1879
+ padding: 0;
1880
+ }
1881
+ .aranova-blog-post .aranova-blog-toc-panel li {
1882
+ margin: 0;
1883
+ }
1884
+ .aranova-blog-post .aranova-blog-toc-panel li + li {
1885
+ margin-block-start: 0.3em;
1886
+ }
1887
+ .aranova-blog-post .aranova-blog-toc-panel li[data-depth="3"] {
1888
+ padding-inline-start: 0.9em;
1889
+ font-size: 0.94em;
1890
+ }
1891
+ .aranova-blog-post .aranova-blog-toc-panel-link {
1892
+ display: -webkit-box;
1893
+ -webkit-box-orient: vertical;
1894
+ -webkit-line-clamp: 2;
1895
+ overflow: hidden;
1896
+ line-height: 1.35;
1897
+ color: var(--ara-blog-color-text-muted);
1898
+ text-decoration: none;
1899
+ border-inline-start: 2px solid transparent;
1900
+ padding-inline-start: 0.6em;
1901
+ margin-inline-start: -0.75em;
1902
+ transition: color 150ms ease;
1903
+ }
1904
+ .aranova-blog-post .aranova-blog-toc-panel-link:hover {
1905
+ color: var(--ara-blog-color-link);
1906
+ text-decoration: underline;
1907
+ text-underline-offset: 0.15em;
1908
+ }
1909
+ /* Scroll-spy active entry: primary color + inline-start accent. */
1910
+ .aranova-blog-post .aranova-blog-toc-panel-link[data-active="true"] {
1911
+ color: var(--ara-blog-color-primary);
1912
+ border-inline-start-color: var(--ara-blog-color-primary);
1913
+ font-weight: 600;
1914
+ }
1915
+
1916
+ /* --- Reading progress (client island) --------------------------------------------
1917
+ * Fixed strip along the top of the viewport; fill = scaleX(scrolled fraction),
1918
+ * driven directly from rAF (no transition \u2014 it tracks position, so the
1919
+ * reduced-motion block has nothing to disable and the bar stays useful). */
1920
+ .aranova-blog-post .aranova-blog-progress {
1921
+ position: fixed;
1922
+ inset-block-start: 0;
1923
+ inset-inline: 0;
1924
+ z-index: 60;
1925
+ background: var(--ara-blog-color-primary);
1926
+ transform: scaleX(0);
1927
+ transform-origin: 0 50%;
1928
+ pointer-events: none;
1929
+ }
1930
+
1931
+ /* --- Vertical rhythm ------------------------------------------------------------
1932
+ * Kept AFTER the element rules: the owl selector and the :where()-wrapped
1933
+ * element rules all sit at (0,1,0), so source order lets rhythm win over the
1934
+ * elements' margin resets, and the per-heading overrides below win over the
1935
+ * owl. \`:is(...)\` targets both today's direct-child layout and the later
1936
+ * \`.aranova-blog-content\` wrapper at class specificity. */
1937
+ :is(.aranova-blog-post, .aranova-blog-content) > * + * {
1938
+ margin-block-start: var(--_flow);
1939
+ }
1940
+ :is(.aranova-blog-post, .aranova-blog-content) > :where(h2) {
1941
+ margin-block-start: var(--_flow-section);
1942
+ }
1943
+ :is(.aranova-blog-post, .aranova-blog-content) > :where(h3, h4) {
1944
+ margin-block-start: var(--_flow-lg);
1945
+ }
1946
+ :is(.aranova-blog-post, .aranova-blog-content) > :where(figure) {
1947
+ margin-block: var(--_flow-lg);
1948
+ }
1949
+ :is(.aranova-blog-post, .aranova-blog-content) > :where(hr) {
1950
+ margin-block: var(--_flow-section);
1951
+ }
1952
+
1953
+ /* --- Motion ------------------------------------------------------------------
1954
+ * Opt-in: a later phase stamps data-motion="subtle" on the article; without
1955
+ * the attribute nothing animates. */
1956
+ @media (prefers-reduced-motion: no-preference) {
1957
+ .aranova-blog-post[data-motion="subtle"] {
1958
+ animation: aranova-blog-fade-up 300ms ease-out both;
1959
+ }
1960
+ }
1961
+ @keyframes aranova-blog-fade-up {
1962
+ from {
1963
+ opacity: 0;
1964
+ transform: translateY(8px);
1965
+ }
1966
+ to {
1967
+ opacity: 1;
1968
+ transform: none;
1969
+ }
1970
+ }
1971
+ @media (prefers-reduced-motion: reduce) {
1972
+ .aranova-blog-post,
1973
+ .aranova-blog-post *,
1974
+ .aranova-blog-post *::before,
1975
+ .aranova-blog-post *::after {
1976
+ animation: none !important;
1977
+ transition: none !important;
1978
+ }
1979
+ }
1980
+ `;
1981
+
1982
+ // ../tracking-core/src/resources/blog/view/blog-post-view.tsx
1983
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1984
+ async function BlogPostView({
1985
+ mdx,
1986
+ theme = BASE_BLOG_THEME,
1987
+ components,
1988
+ header,
1989
+ aside,
1990
+ motion = "none",
1991
+ classNames,
1992
+ requireLeadingH1 = true,
1993
+ tocSide = "right"
1994
+ }) {
1995
+ const style = blogThemeToCssVars(theme);
1996
+ const merged = { ...createDefaultComponents([], classNames), ...components };
1997
+ const validation = validateBlogMdx(mdx, { requireLeadingH1 });
1998
+ if (!validation.ok) {
1999
+ const rules = Array.from(new Set(validation.violations.map((v) => v.rule))).join(", ");
2000
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2001
+ "article",
2002
+ {
2003
+ className: joinClassNames("aranova-blog-post", classNames?.article),
2004
+ "data-error": "mdx-validation-failed",
2005
+ style,
2006
+ children: [
2007
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: BLOG_BASE_CSS }),
2008
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { children: [
2009
+ "This post could not be displayed. (blocked: ",
2010
+ rules,
2011
+ ")"
2012
+ ] })
2013
+ ]
2014
+ }
2015
+ );
2016
+ }
2017
+ let content;
2018
+ try {
2019
+ const compiled = await (0, import_rsc.compileMDX)({
2020
+ source: mdx,
2021
+ components: merged,
2022
+ options: { mdxOptions: { rehypePlugins: [import_rehype_slug.default] } }
2023
+ });
2024
+ content = compiled.content;
2025
+ } catch {
2026
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2027
+ "article",
2028
+ {
2029
+ className: joinClassNames("aranova-blog-post", classNames?.article),
2030
+ "data-error": "mdx-render-failed",
2031
+ style,
2032
+ children: [
2033
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: BLOG_BASE_CSS }),
2034
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: "This post could not be displayed." })
2035
+ ]
2036
+ }
2037
+ );
2038
+ }
2039
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2040
+ "article",
2041
+ {
2042
+ className: joinClassNames("aranova-blog-post", classNames?.article),
2043
+ "data-motion": motion,
2044
+ style,
2045
+ children: [
2046
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: BLOG_BASE_CSS }),
2047
+ header,
2048
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2049
+ "div",
2050
+ {
2051
+ className: joinClassNames("aranova-blog-layout", classNames?.layout),
2052
+ "data-toc-side": tocSide,
2053
+ children: [
2054
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: joinClassNames("aranova-blog-content", classNames?.content), children: content }),
2055
+ aside ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("aside", { className: joinClassNames("aranova-blog-aside", classNames?.aside), children: aside }) : null
2056
+ ]
2057
+ }
2058
+ )
2059
+ ]
2060
+ }
2061
+ );
2062
+ }
2063
+
2064
+ // ../tracking-core/src/resources/blog/view/chrome/breadcrumbs.tsx
2065
+ var import_jsx_runtime3 = require("react/jsx-runtime");
2066
+ function BlogBreadcrumbs({
2067
+ items,
2068
+ currentLabel,
2069
+ className
2070
+ }) {
2071
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("nav", { "aria-label": "Breadcrumbs", className: joinClassNames("aranova-blog-breadcrumbs", className), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("ol", { children: [
2072
+ items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("li", { children: [
2073
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("a", { href: item.href, children: item.label }),
2074
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "aria-hidden": "true", className: "aranova-blog-breadcrumbs-separator", children: "/" })
2075
+ ] }, `${item.href} ${item.label}`)),
2076
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "aria-current": "page", children: currentLabel }) })
2077
+ ] }) });
2078
+ }
2079
+
2080
+ // ../tracking-core/src/resources/blog/view/chrome/meta.tsx
2081
+ var import_jsx_runtime4 = require("react/jsx-runtime");
2082
+ var DEFAULT_BLOG_LOCALE = "en-CA";
2083
+ function formatBlogDate(iso, locale) {
2084
+ if (!iso) return null;
2085
+ const date = new Date(iso);
2086
+ if (Number.isNaN(date.getTime())) return null;
2087
+ let formatter;
2088
+ try {
2089
+ formatter = new Intl.DateTimeFormat(locale, { dateStyle: "long", timeZone: "UTC" });
2090
+ } catch {
2091
+ formatter = new Intl.DateTimeFormat(DEFAULT_BLOG_LOCALE, {
2092
+ dateStyle: "long",
2093
+ timeZone: "UTC"
2094
+ });
2095
+ }
2096
+ return { dateTime: date.toISOString().slice(0, 10), label: formatter.format(date) };
2097
+ }
2098
+ function BlogPostMeta({
2099
+ publishedAt,
2100
+ updatedAt,
2101
+ readingTimeMinutes,
2102
+ tags,
2103
+ locale = DEFAULT_BLOG_LOCALE,
2104
+ className
2105
+ }) {
2106
+ const published = formatBlogDate(publishedAt, locale);
2107
+ const updated = formatBlogDate(updatedAt, locale);
2108
+ const showUpdated = updated !== null && updated.dateTime !== published?.dateTime;
2109
+ const showReadingTime = typeof readingTimeMinutes === "number" && readingTimeMinutes > 0;
2110
+ const shownTags = (tags ?? []).filter((tag) => tag.length > 0);
2111
+ const items = [];
2112
+ if (published) {
2113
+ items.push({
2114
+ key: "published",
2115
+ node: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("time", { dateTime: published.dateTime, children: published.label })
2116
+ });
2117
+ }
2118
+ if (showUpdated && updated) {
2119
+ items.push({
2120
+ key: "updated",
2121
+ node: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
2122
+ "Updated ",
2123
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("time", { dateTime: updated.dateTime, children: updated.label })
2124
+ ] })
2125
+ });
2126
+ }
2127
+ if (showReadingTime) {
2128
+ items.push({ key: "reading-time", node: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
2129
+ readingTimeMinutes,
2130
+ " min read"
2131
+ ] }) });
2132
+ }
2133
+ if (items.length === 0 && shownTags.length === 0) return null;
2134
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: joinClassNames("aranova-blog-meta", className), children: [
2135
+ items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "aranova-blog-meta-item", children: [
2136
+ index > 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": "true", className: "aranova-blog-meta-separator", children: "\xB7" }) : null,
2137
+ item.node
2138
+ ] }, item.key)),
2139
+ shownTags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "aranova-blog-meta-tags", children: shownTags.map((tag) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "aranova-blog-meta-tag", children: tag }, tag)) }) : null
2140
+ ] });
2141
+ }
2142
+
2143
+ // ../tracking-core/src/resources/blog/view/chrome/toc.tsx
2144
+ var import_jsx_runtime5 = require("react/jsx-runtime");
2145
+ function BlogTocShell({
2146
+ headings,
2147
+ title = "On this page",
2148
+ className
2149
+ }) {
2150
+ if (headings.length === 0) return null;
2151
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("details", { open: true, className: joinClassNames("aranova-blog-toc", className), children: [
2152
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("summary", { className: "aranova-blog-toc-title", children: title }),
2153
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("nav", { "aria-label": "Table of contents", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ol", { children: headings.map((heading) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { "data-depth": heading.depth, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("a", { href: `#${heading.id}`, title: heading.text, children: heading.text }) }, heading.id)) }) })
2154
+ ] });
2155
+ }
2156
+
2157
+ // ../tracking-core/src/resources/blog/view/blog-post.tsx
2158
+ var import_jsx_runtime6 = require("react/jsx-runtime");
2159
+ async function BlogPost({
2160
+ slug,
2161
+ client,
2162
+ defaultTheme,
2163
+ components,
2164
+ notFound,
2165
+ showMeta = false,
2166
+ showToc = false,
2167
+ tocSide = "right",
2168
+ showBreadcrumbs = false,
2169
+ breadcrumbs,
2170
+ locale,
2171
+ tocSlot,
2172
+ progressSlot,
2173
+ classNames
2174
+ }) {
2175
+ const post = await client.getPost(slug);
2176
+ if (!post) {
2177
+ return notFound ?? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("article", { className: "aranova-blog-post", "data-error": "not-found", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: "This post could not be found." }) });
2178
+ }
2179
+ const theme = resolveBlogTheme(
2180
+ sanitizeBlogTheme(defaultTheme),
2181
+ sanitizeBlogTheme(post.theme_overrides)
2182
+ );
2183
+ const merged = { ...createDefaultComponents(post.images, classNames), ...components };
2184
+ const crumbs = showBreadcrumbs && breadcrumbs && breadcrumbs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2185
+ BlogBreadcrumbs,
2186
+ {
2187
+ items: breadcrumbs,
2188
+ currentLabel: post.title,
2189
+ className: classNames?.breadcrumbs
2190
+ }
2191
+ ) : null;
2192
+ const meta = showMeta ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2193
+ BlogPostMeta,
2194
+ {
2195
+ publishedAt: post.published_at,
2196
+ updatedAt: post.updated_at,
2197
+ readingTimeMinutes: estimateReadingTime(post.mdx),
2198
+ locale,
2199
+ className: classNames?.meta
2200
+ }
2201
+ ) : null;
2202
+ const progress = progressSlot ? progressSlot() : null;
2203
+ const header = progress || crumbs || meta ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2204
+ progress,
2205
+ crumbs,
2206
+ meta
2207
+ ] }) : void 0;
2208
+ const headings = showToc ? extractBlogHeadings(post.mdx) : [];
2209
+ const aside = headings.length > 0 ? tocSlot?.(headings) ?? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(BlogTocShell, { headings, className: classNames?.toc }) : void 0;
2210
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2211
+ BlogPostView,
2212
+ {
2213
+ mdx: post.mdx,
2214
+ theme,
2215
+ components: merged,
2216
+ header,
2217
+ aside,
2218
+ classNames,
2219
+ tocSide
2220
+ }
2221
+ );
2222
+ }
2223
+
2224
+ // src/blog-post.tsx
2225
+ var import_blog_client = require("@aranova/tracking-next/blog-client");
2226
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2227
+ function BlogPost2({
2228
+ showProgress = false,
2229
+ tocCollapse = "vertical",
2230
+ ...props
2231
+ }) {
2232
+ const tocSide = props.tocSide ?? "right";
2233
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2234
+ BlogPost,
2235
+ {
2236
+ ...props,
2237
+ tocSlot: (headings) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2238
+ import_blog_client.TocPanel,
2239
+ {
2240
+ headings,
2241
+ className: props.classNames?.toc,
2242
+ collapse: tocCollapse,
2243
+ side: tocSide
2244
+ }
2245
+ ),
2246
+ progressSlot: showProgress ? () => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_blog_client.ReadingProgress, { className: props.classNames?.progress }) : void 0
2247
+ }
2248
+ );
2249
+ }
2250
+ // Annotate the CommonJS export names for ESM import in node:
2251
+ 0 && (module.exports = {
2252
+ BASE_BLOG_THEME,
2253
+ BLOG_BASE_CSS,
2254
+ BLOG_BREAKPOINTS,
2255
+ BLOG_BREAKPOINT_MIN_WIDTHS_PX,
2256
+ BLOG_MDX_ALLOWED_COMPONENTS,
2257
+ BLOG_MDX_COMPONENT_GRAMMAR,
2258
+ BLOG_MDX_MAX_DEPTH,
2259
+ BLOG_MDX_MAX_NODES,
2260
+ BLOG_MDX_MAX_SIZE,
2261
+ BLOG_THEME_PRESETS,
2262
+ BlogBreadcrumbs,
2263
+ BlogPost,
2264
+ BlogPostMeta,
2265
+ BlogPostView,
2266
+ BlogTocShell,
2267
+ DEFAULT_BLOG_LOCALE,
2268
+ blogHeroImageSchema,
2269
+ blogImageSchema,
2270
+ blogManifestEntrySchema,
2271
+ blogManifestSchema,
2272
+ blogManifestWireSchema,
2273
+ blogPostContentSchema,
2274
+ blogPostContentWireSchema,
2275
+ blogSeoSchema,
2276
+ blogThemeColorsSchema,
2277
+ blogThemeOverrideSchema,
2278
+ blogThemeSchema,
2279
+ blogThemeSpacingSchema,
2280
+ blogThemeToCssVars,
2281
+ blogThemeTypographySchema,
2282
+ buildBlogPostMetadata,
2283
+ buildBlogPostingJsonLd,
2284
+ buildBlogSitemapEntries,
2285
+ buildFaqJsonLd,
2286
+ createBlogClient,
2287
+ createBlogFontTheme,
2288
+ createDefaultComponents,
2289
+ estimateReadingTime,
2290
+ extractBlogHeadings,
2291
+ extractFaqPairs,
2292
+ joinClassNames,
2293
+ nullishField,
2294
+ parseBlogManifest,
2295
+ parseBlogPostContent,
2296
+ resolveBlogTheme,
2297
+ responsive,
2298
+ sanitizeBlogTheme,
2299
+ serializeJsonLd,
2300
+ toBlogBreakpointMap,
2301
+ validateBlogMdx
2302
+ });
2303
+ //# sourceMappingURL=blog-server.js.map