diff --git a/apps/landing/public/og-image.png b/apps/landing/public/og-image.png
index c3af4c03..dc52060b 100644
Binary files a/apps/landing/public/og-image.png and b/apps/landing/public/og-image.png differ
diff --git a/apps/web/public/og-image.png b/apps/web/public/og-image.png
index c3af4c03..dc52060b 100644
Binary files a/apps/web/public/og-image.png and b/apps/web/public/og-image.png differ
diff --git a/branding/dashboard.gif b/branding/dashboard.gif
index 0167a9c0..d8ff7399 100644
Binary files a/branding/dashboard.gif and b/branding/dashboard.gif differ
diff --git a/branding/social-preview.png b/branding/social-preview.png
index c3af4c03..dc52060b 100644
Binary files a/branding/social-preview.png and b/branding/social-preview.png differ
diff --git a/scripts/branding/generate-social-preview.mjs b/scripts/branding/generate-social-preview.mjs
index 2c9f0f9e..a04f2b8a 100644
--- a/scripts/branding/generate-social-preview.mjs
+++ b/scripts/branding/generate-social-preview.mjs
@@ -2,7 +2,8 @@
// Echoes the landing hero: trust badges, the "Every file tool you need / Your files
// never leave your network" headline, the supporting subhead, and the five modality
// cards. Rendered from HTML with the real brand fonts via headless Chromium.
-// Run: node scripts/branding/generate-social-preview.mjs
+// Run (tsx, because it imports the shared TS catalog for live tool counts):
+// apps/api/node_modules/.bin/tsx scripts/branding/generate-social-preview.mjs
//
// The same output is the canonical OG image; sync it to apps/landing/public/og-image.png
// and apps/web/public/og-image.png (see scripts/branding/sync-og.sh).
@@ -11,6 +12,9 @@ import { rmSync, writeFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { chromium } from "@playwright/test";
+// Relative import (not the "@snapotter/shared" specifier) so the script runs
+// from the repo root under tsx without the workspace symlink on the resolve path.
+import { TOOLS, toolSection } from "../../packages/shared/src/index.ts";
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
const out = resolve(repoRoot, "branding/social-preview.png");
@@ -34,41 +38,42 @@ const ICON = {
'',
};
-// Mirrors CategoryCards.astro (label/blurb/color/icon). Counts are toolSection()
-// section counts (image 64, video 29, audio 17, pdf 25, files 22 = 157); refresh
-// with: tsx -e "import('@snapotter/shared')..." over toolSection (see git history).
+// Mirrors CategoryCards.astro (label/blurb/color/icon). Counts are derived live
+// from toolSection() over the shared TOOLS catalog -- same source the landing
+// cards use -- so the card never drifts as tools are added.
+const sectionCount = (section) => TOOLS.filter((t) => toolSection(t) === section).length;
const CARDS = [
{
label: "Image Tools",
- count: 64,
+ count: sectionCount("image"),
blurb: "Resize, convert, and enhance",
color: "#E07832",
icon: ICON.image,
},
{
label: "Video Tools",
- count: 29,
+ count: sectionCount("video"),
blurb: "Trim, convert, and caption",
color: "#BE4A3C",
icon: ICON.video,
},
{
label: "Audio Tools",
- count: 17,
+ count: sectionCount("audio"),
blurb: "Convert, trim, and transcribe",
color: "#2C7A75",
icon: ICON.music,
},
{
label: "PDF & Documents",
- count: 25,
+ count: sectionCount("pdf"),
blurb: "Merge, split, and convert",
color: "#44568C",
icon: ICON.fileText,
},
{
label: "File Tools",
- count: 22,
+ count: sectionCount("files"),
blurb: "Convert and transform",
color: "#5C8642",
icon: ICON.database,