diff --git a/app/contracts/page.tsx b/app/contracts/page.tsx
index ff64af5..e67bfd3 100644
--- a/app/contracts/page.tsx
+++ b/app/contracts/page.tsx
@@ -12,6 +12,10 @@ export default function ContractsPage() {
return (
Contracts
+ Contracts surface — product definition TBD.
+
+ Scaffold page — replace with production content, data loaders, and analytics.
+
Soroban contracts
Soroban contracts
On-chain modules that power ModelTrace settlement, attestation, and
diff --git a/app/docs/page.tsx b/app/docs/page.tsx
index 16afc71..68e5fae 100644
--- a/app/docs/page.tsx
+++ b/app/docs/page.tsx
@@ -12,6 +12,13 @@ export const metadata: Metadata = {
export default function DocsPage() {
return (
+
+ Documentation
+ Technical specs, governance, and integration guides.
+
+ Scaffold page — replace with production content, data loaders, and analytics.
+
+
<>
diff --git a/app/globals.css b/app/globals.css
index 4bafd6a..746c81f 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -285,7 +285,7 @@ a { color: inherit; text-decoration: none; }
padding: 24px 0 40px;
}
-.section h2 { margin-bottom: 10px; }
+.section h1, .section h2 { margin-bottom: 10px; }
.list {
padding-left: 18px;
@@ -490,6 +490,414 @@ a { color: inherit; text-decoration: none; }
letter-spacing: 0.04em;
}
+/* ── Prose utility ──────────────────────────────────────────────────────────
+ * Apply to any long-form content wrapper (compliance, docs, etc.).
+ * Sets a comfortable measure and the base reading line-height. */
+.prose {
+ max-width: var(--measure);
+ line-height: var(--lh-base);
+}
+
+/* ── Changelog ──────────────────────────────────────────────────────────────
+ *
+ * Shared tokens:
+ * --cl-badge-changelog → cool blue
+ * --cl-badge-post → soft violet
+ * --cl-badge-postmortem → amber warning
+ * ─────────────────────────────────────────────────────────────────────────── */
+:root {
+ --cl-badge-changelog: color-mix(in srgb, var(--accent) 60%, #1a2d45);
+ --cl-badge-post: color-mix(in srgb, #a78bfa 60%, #1a1235);
+ --cl-badge-postmortem: color-mix(in srgb, #fbbf24 55%, #231800);
+ --cl-badge-changelog-text: #b0e8ff;
+ --cl-badge-post-text: #ddd6fe;
+ --cl-badge-postmortem-text: #fde68a;
+}
+
+/* Index hero
+ ─────────────────────────────────────────────────────────────────────────── */
+.cl-index-hero {
+ position: relative;
+ overflow: hidden;
+ padding: 40px 0 32px;
+}
+
+.cl-index-hero-inner {
+ position: relative;
+ z-index: 2;
+ max-width: 640px;
+}
+
+/* RSS hint row beneath the lead paragraph */
+.cl-rss-hint {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: var(--text-sm);
+ line-height: var(--lh-sm);
+ color: var(--muted);
+ margin: 0;
+}
+
+.cl-rss-sep { opacity: 0.4; }
+
+.cl-rss-link {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ color: var(--accent);
+ font-weight: 500;
+ transition: opacity 0.15s ease;
+}
+.cl-rss-link:hover { opacity: 0.75; }
+
+/* Milestone filter bar
+ ─────────────────────────────────────────────────────────────────────────── */
+.cl-filter-bar {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ padding: 0 0 24px;
+}
+
+.cl-filter-pill {
+ display: inline-block;
+ padding: 5px 14px;
+ border-radius: 999px;
+ border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
+ color: var(--muted);
+ font-size: var(--text-xs);
+ line-height: var(--lh-xs);
+ font-weight: 500;
+ letter-spacing: 0.04em;
+ transition: background 0.14s ease, border-color 0.14s ease, color 0.14s ease;
+}
+
+.cl-filter-pill:hover {
+ background: color-mix(in srgb, var(--accent) 10%, transparent);
+ border-color: color-mix(in srgb, var(--accent) 48%, transparent);
+ color: var(--text);
+}
+
+.cl-filter-pill--active {
+ background: color-mix(in srgb, var(--accent) 16%, transparent);
+ border-color: color-mix(in srgb, var(--accent) 55%, transparent);
+ color: var(--accent);
+}
+
+/* Entry list
+ ─────────────────────────────────────────────────────────────────────────── */
+.cl-entry-list {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ padding-bottom: 56px;
+}
+
+.cl-empty {
+ color: var(--muted);
+ font-size: var(--text-sm);
+ padding: 24px 0;
+}
+
+/* Card
+ ─────────────────────────────────────────────────────────────────────────── */
+.cl-card {
+ background: linear-gradient(
+ 160deg,
+ color-mix(in srgb, var(--surface) 90%, var(--bg)) 0%,
+ var(--surface) 100%
+ );
+ border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
+ border-radius: 14px;
+ padding: 22px 24px 18px;
+ transition: border-color 0.15s ease, box-shadow 0.15s ease;
+}
+
+.cl-card:hover {
+ border-color: color-mix(in srgb, var(--accent) 40%, transparent);
+ box-shadow: 0 4px 24px color-mix(in srgb, var(--accent) 8%, transparent);
+}
+
+.cl-card-meta {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-bottom: 10px;
+}
+
+.cl-card-title {
+ font-size: var(--text-lg);
+ line-height: var(--lh-lg);
+ font-weight: var(--fw-lg);
+ margin: 0 0 8px;
+}
+
+.cl-card-title a {
+ color: var(--text);
+ transition: color 0.12s ease;
+}
+.cl-card-title a:hover { color: var(--accent); }
+
+.cl-card-summary {
+ font-size: var(--text-sm);
+ line-height: var(--lh-sm);
+ color: var(--muted);
+ margin: 0 0 14px;
+ max-width: 72ch;
+}
+
+.cl-read-more {
+ display: inline-block;
+ font-size: var(--text-sm);
+ font-weight: 600;
+ color: var(--accent);
+ transition: opacity 0.12s ease;
+}
+.cl-read-more:hover { opacity: 0.75; }
+
+/* Type badge
+ ─────────────────────────────────────────────────────────────────────────── */
+.cl-type-badge {
+ display: inline-block;
+ padding: 3px 10px;
+ border-radius: 999px;
+ font-size: var(--text-xs);
+ line-height: var(--lh-xs);
+ font-weight: 600;
+ letter-spacing: 0.05em;
+ text-transform: uppercase;
+}
+
+.cl-type-badge[data-type="changelog"] {
+ background: var(--cl-badge-changelog);
+ color: var(--cl-badge-changelog-text);
+}
+
+.cl-type-badge[data-type="post"] {
+ background: var(--cl-badge-post);
+ color: var(--cl-badge-post-text);
+}
+
+.cl-type-badge[data-type="postmortem"] {
+ background: var(--cl-badge-postmortem);
+ color: var(--cl-badge-postmortem-text);
+}
+
+/* Milestone chip
+ ─────────────────────────────────────────────────────────────────────────── */
+.cl-milestone-chip {
+ display: inline-block;
+ padding: 3px 10px;
+ border-radius: 999px;
+ border: 1px solid color-mix(in srgb, var(--accent-2) 40%, transparent);
+ background: color-mix(in srgb, var(--accent-2) 10%, transparent);
+ color: var(--accent-2);
+ font-size: var(--text-xs);
+ line-height: var(--lh-xs);
+ font-weight: 600;
+ letter-spacing: 0.04em;
+ transition: background 0.14s ease;
+}
+.cl-milestone-chip:hover {
+ background: color-mix(in srgb, var(--accent-2) 18%, transparent);
+}
+
+/* Date
+ ─────────────────────────────────────────────────────────────────────────── */
+.cl-date {
+ font-size: var(--text-xs);
+ line-height: var(--lh-xs);
+ color: var(--muted);
+ margin-left: auto;
+}
+
+/* Tag list
+ ─────────────────────────────────────────────────────────────────────────── */
+.cl-tag-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+ list-style: none;
+ padding: 0;
+ margin: 0 0 14px;
+}
+
+.cl-tag {
+ padding: 2px 9px;
+ border-radius: 999px;
+ background: color-mix(in srgb, var(--muted) 14%, transparent);
+ border: 1px solid color-mix(in srgb, var(--muted) 22%, transparent);
+ color: var(--muted);
+ font-size: var(--text-2xs);
+ line-height: var(--lh-2xs);
+}
+
+/* ── Full post layout ────────────────────────────────────────────────────── */
+.cl-post {
+ max-width: 740px;
+ padding: 32px 0 64px;
+}
+
+.cl-back-link {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ font-size: var(--text-sm);
+ color: var(--muted);
+ font-weight: 500;
+ margin-bottom: 28px;
+ transition: color 0.12s ease;
+}
+.cl-back-link:hover { color: var(--accent); }
+
+.cl-post-header {
+ margin-bottom: 36px;
+ padding-bottom: 28px;
+ border-bottom: 1px solid color-mix(in srgb, var(--accent) 18%, transparent);
+}
+
+.cl-post-meta {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-bottom: 16px;
+}
+
+.cl-post-title {
+ font-size: clamp(1.5rem, 3.5vw, 2.2rem);
+ line-height: 1.2;
+ font-weight: 800;
+ letter-spacing: -0.025em;
+ margin: 0 0 14px;
+}
+
+.cl-post-summary {
+ font-size: var(--text-md);
+ line-height: var(--lh-md);
+ color: var(--muted);
+ margin: 0 0 18px;
+ max-width: 62ch;
+}
+
+/* Prose body — inherits .prose max-width + line-height */
+.cl-post-body {
+ color: var(--text);
+}
+
+.cl-post-body h2 {
+ font-size: var(--text-xl);
+ line-height: var(--lh-xl);
+ font-weight: 700;
+ margin: 36px 0 12px;
+ padding-bottom: 8px;
+ border-bottom: 1px solid color-mix(in srgb, var(--accent) 15%, transparent);
+}
+
+.cl-post-body h3 {
+ font-size: var(--text-lg);
+ line-height: var(--lh-lg);
+ font-weight: var(--fw-lg);
+ margin: 28px 0 8px;
+}
+
+.cl-post-body p {
+ margin: 0 0 18px;
+ color: color-mix(in srgb, var(--text) 88%, var(--muted));
+}
+
+.cl-post-body a {
+ color: var(--accent);
+ text-decoration: underline;
+ text-decoration-color: color-mix(in srgb, var(--accent) 40%, transparent);
+ text-underline-offset: 3px;
+}
+.cl-post-body a:hover {
+ text-decoration-color: var(--accent);
+}
+
+.cl-post-body ul,
+.cl-post-body ol {
+ padding-left: 20px;
+ margin: 0 0 18px;
+ color: color-mix(in srgb, var(--text) 88%, var(--muted));
+}
+
+.cl-post-body li { margin-bottom: 6px; }
+
+.cl-post-body table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: var(--text-sm);
+ margin: 22px 0 28px;
+}
+
+.cl-post-body th,
+.cl-post-body td {
+ text-align: left;
+ padding: 10px 12px;
+ border-bottom: 1px solid color-mix(in srgb, var(--muted) 22%, transparent);
+}
+
+.cl-post-body th {
+ color: var(--muted);
+ font-weight: 600;
+ border-bottom-color: color-mix(in srgb, var(--accent) 28%, transparent);
+}
+
+.cl-post-body code {
+ font-family: ui-monospace, "Cascadia Code", "Fira Code", Consolas, monospace;
+ font-size: 0.875em;
+ background: color-mix(in srgb, var(--surface) 90%, var(--bg));
+ border: 1px solid color-mix(in srgb, var(--muted) 18%, transparent);
+ border-radius: 5px;
+ padding: 1px 6px;
+}
+
+.cl-post-body pre {
+ background: color-mix(in srgb, var(--surface) 70%, var(--bg));
+ border: 1px solid color-mix(in srgb, var(--accent) 20%, transparent);
+ border-radius: 10px;
+ padding: 18px 20px;
+ overflow-x: auto;
+ margin: 0 0 24px;
+ font-size: var(--text-sm);
+ line-height: 1.65;
+}
+
+.cl-post-body pre code {
+ background: none;
+ border: none;
+ padding: 0;
+ font-size: inherit;
+}
+
+.cl-post-body blockquote {
+ border-left: 3px solid color-mix(in srgb, var(--accent) 55%, transparent);
+ margin: 0 0 22px;
+ padding: 8px 0 8px 18px;
+ color: var(--muted);
+ font-style: italic;
+}
+
+.cl-post-body hr {
+ border: none;
+ border-top: 1px solid color-mix(in srgb, var(--muted) 22%, transparent);
+ margin: 32px 0;
+}
+
+/* Post footer */
+.cl-post-footer {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ gap: 12px;
+ margin-top: 48px;
+ padding-top: 24px;
+ border-top: 1px solid color-mix(in srgb, var(--muted) 18%, transparent);
/* === Responsive breakpoints ==========================================
*
* Until now this sheet had no media queries at all: every layout relied on
diff --git a/app/layout.tsx b/app/layout.tsx
index 7b85db8..8066db3 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -52,6 +52,17 @@ export const metadata: Metadata = {
},
};
+const nav = [
+ ["Product", "/product"],
+ ["Contracts", "/contracts"],
+ ["Operators", "/operators"],
+ ["Compliance", "/compliance"],
+ ["Roadmap", "/roadmap"],
+ ["Changelog", "/changelog"],
+ ["Contributors", "/contributors"],
+ ["Docs", "/docs"],
+] as const;
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
diff --git a/app/operators/page.tsx b/app/operators/page.tsx
index c04cb93..cb1b8e1 100644
--- a/app/operators/page.tsx
+++ b/app/operators/page.tsx
@@ -17,6 +17,11 @@ export default function Page() {
Operators
Operator dashboard
+
+ Attestation volume, usage by model, and quota headroom. Every chart on
+ this page ships its numbers as a table — an auditor asking for the
+ underlying figures should not have to ask anyone.
+
Operator dashboard
Attestation volume, usage by model, and quota headroom. Data is sourced directly from the testnet settlement layer.
diff --git a/app/product/page.tsx b/app/product/page.tsx
index 5b0fcba..9fe60b8 100644
--- a/app/product/page.tsx
+++ b/app/product/page.tsx
@@ -7,6 +7,13 @@ export const metadata: Metadata = {
function PlannedTag() {
return (
+
+ Product
+ Product surface — product definition TBD.
+
+ Scaffold page — replace with production content, data loaders, and analytics.
+
+
f.endsWith(".md"))
+ .sort()
+ .reverse(); // lexicographic desc → newest slug first
+ return files.map(parseEntry);
+}
+
+/** Single entry with rendered HTML body. */
+export async function getEntryBySlug(
+ slug: string
+): Promise<(ChangelogEntry & { body: string }) | null> {
+ const filepath = path.join(CONTENT_DIR, `${slug}.md`);
+ if (!fs.existsSync(filepath)) return null;
+ const raw = fs.readFileSync(filepath, "utf8");
+ const { data, content } = matter(raw);
+ const body = await marked(content, { async: true });
+ return {
+ slug,
+ title: data.title ?? slug,
+ date: data.date ? String(data.date) : "",
+ type: (data.type as EntryType) ?? "post",
+ milestone: data.milestone ?? undefined,
+ summary: data.summary ?? "",
+ tags: Array.isArray(data.tags) ? data.tags : [],
+ body,
+ };
+}
+
+/** Unique milestone labels found across all entries, sorted. */
+export function getMilestones(): string[] {
+ const entries = getAllEntries();
+ const set = new Set();
+ for (const e of entries) {
+ if (e.milestone) set.add(e.milestone);
+ }
+ return Array.from(set).sort();
+}
+
+/** Human-readable date string, e.g. "14 Nov 2025". */
+export function formatDate(iso: string): string {
+ if (!iso) return "";
+ const d = new Date(iso);
+ return d.toLocaleDateString("en-GB", {
+ day: "numeric",
+ month: "short",
+ year: "numeric",
+ });
+}
diff --git a/package-lock.json b/package-lock.json
index 7471df2..74d236b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,8 @@
"name": "modeltrace-web",
"version": "0.1.0",
"dependencies": {
+ "gray-matter": "^4.0.3",
+ "marked": "^18.0.11",
"@stellar/freighter-api": "^6.0.1",
"next": "^15.1.3",
"react": "^19.0.0",
@@ -4845,6 +4847,19 @@
"url": "https://opencollective.com/eslint"
}
},
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/esquery": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
@@ -4911,6 +4926,18 @@
"node": ">=12.0.0"
}
},
+ "node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "license": "MIT",
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -5273,6 +5300,43 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/gray-matter": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz",
+ "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==",
+ "license": "MIT",
+ "dependencies": {
+ "js-yaml": "^3.13.1",
+ "kind-of": "^6.0.2",
+ "section-matter": "^1.0.0",
+ "strip-bom-string": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/gray-matter/node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "license": "MIT",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/gray-matter/node_modules/js-yaml": {
+ "version": "3.15.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz",
+ "integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==",
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
"node_modules/has-bigints": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
@@ -5664,6 +5728,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -6104,6 +6177,15 @@
"json-buffer": "3.0.1"
}
},
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/language-subtag-registry": {
"version": "0.3.23",
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
@@ -6224,6 +6306,18 @@
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
+ "node_modules/marked": {
+ "version": "18.0.11",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.11.tgz",
+ "integrity": "sha512-HnslJfsZkRPBDJRHvVtAaWlZHEpSu7u8LgQuJCELjRKuWR+hpq4A7sLq3p8HaI9ypVoXDXxV34CsQJEe1+J5Aw==",
+ "license": "MIT",
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -7218,6 +7312,19 @@
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
"license": "MIT"
},
+ "node_modules/section-matter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
+ "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==",
+ "license": "MIT",
+ "dependencies": {
+ "extend-shallow": "^2.0.1",
+ "kind-of": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/semver": {
"version": "7.8.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
@@ -7468,6 +7575,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/stable-hash": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz",
@@ -7627,6 +7740,15 @@
"node": ">=4"
}
},
+ "node_modules/strip-bom-string": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz",
+ "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/strip-indent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
diff --git a/package.json b/package.json
index 4db239a..519c377 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,8 @@
"test:e2e:ui": "playwright test --ui"
},
"dependencies": {
+ "gray-matter": "^4.0.3",
+ "marked": "^18.0.11",
"@stellar/freighter-api": "^6.0.1",
"next": "^15.1.3",
"react": "^19.0.0",