diff --git a/.gitignore b/.gitignore
index 71d35fc7..27019fe0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,3 +41,9 @@ node_modules
# Ignore manual test scripts
manual_tests/
+
+# Design working files (local only — back up to opentdf/brand-assets)
+design/
+
+# Claude instructions (local only)
+CLAUDE.md
diff --git a/docusaurus.config.ts b/docusaurus.config.ts
index 0f4d5478..945c5ed7 100644
--- a/docusaurus.config.ts
+++ b/docusaurus.config.ts
@@ -26,7 +26,7 @@ const javaSdkVersion = "0.11.1";
const config: Config = {
title: "OpenTDF",
tagline: "Enabling secure data sharing through open, data-centric security",
- favicon: "img/OpenTDF-Logo.png",
+ favicon: "img/favicon.svg",
// Set the production url of your site here
url: "https://docs.opentdf.io",
@@ -55,7 +55,7 @@ const config: Config = {
"@type": "Organization",
name: "OpenTDF",
url: "https://opentdf.io",
- logo: "https://docs.opentdf.io/img/opentdf-social.png",
+ logo: "https://opentdf.io/img/opentdf-icon.svg",
}),
},
],
@@ -92,7 +92,7 @@ const config: Config = {
},
blog: false,
theme: {
- customCss: "./src/css/custom.css",
+ customCss: ["./src/css/custom.css", "./src/css/landing.css"],
},
},
],
@@ -111,6 +111,7 @@ const config: Config = {
logo: {
alt: "OpenTDF - Protect the Data, Build the Future",
src: "img/OpenTDF-Logo.png",
+ srcDark: "img/OpenTDF-Logo-White.png",
width: 32,
height: 32,
},
@@ -122,77 +123,67 @@ const config: Config = {
label: "Docs",
},
{
- href: "https://github.com/opentdf",
- label: "GitHub",
- position: "right",
+ to: "/quickstart",
+ position: "left",
+ label: "Quickstart",
},
{
- type: "search",
+ to: "/sdks",
+ position: "left",
+ label: "SDKs",
+ },
+ {
+ to: "/spec",
+ position: "left",
+ label: "Specification",
+ },
+ {
+ href: "https://github.com/orgs/opentdf/discussions",
+ position: "left",
+ label: "Community",
+ },
+ {
+ href: "https://github.com/opentdf",
+ label: "GitHub",
position: "right",
},
],
},
footer: {
style: "dark",
- logo: {
- alt: "OpenTDF Logo",
- src: "img/opentdf-logo-horizontal.png",
- href: "https://opentdf.io",
- },
links: [
{
- title: "Sponsors",
+ title: "Project",
items: [
- {
- label: "Virtru",
- href: "https://virtru.com",
- },
+ { label: "About", to: "/introduction" },
+ { label: "Architecture", to: "/architecture" },
+ { label: "Specification", to: "/spec" },
+ { label: "License", href: "https://github.com/opentdf/platform/blob/main/LICENSE" },
],
},
{
- title: "Support",
+ title: "Developers",
items: [
- {
- label: "Getting Started",
- to: "/getting-started/configuration",
- },
- {
- label: "Documentation",
- to: "/introduction",
- },
- {
- label: "GitHub Discussions",
- href: "https://github.com/orgs/opentdf/discussions",
- },
+ { label: "Quickstart", to: "/quickstart" },
+ { label: "SDKs", to: "/sdks" },
+ { label: "Platform", href: "https://github.com/opentdf/platform" },
+ { label: "CLI Reference", href: "https://github.com/opentdf/otdfctl" },
],
},
{
- title: "More",
+ title: "Community",
items: [
- {
- label: "Showcase",
- href: "https://github.com/orgs/opentdf/discussions/categories/show-and-tell",
- },
- {
- label: "Roadmap",
- href: "https://opentdf.io/appendix/matrix",
- },
+ { label: "GitHub", href: "https://github.com/opentdf" },
+ { label: "Discussions", href: "https://github.com/orgs/opentdf/discussions" },
+ { label: "Contributing", href: "https://github.com/opentdf/platform/blob/main/CONTRIBUTING.md" },
+ { label: "Code of Conduct", href: "https://github.com/opentdf/platform/blob/main/CODE_OF_CONDUCT.md" },
],
},
{
- title: "Legal",
+ title: "Sponsor",
items: [
{
- label: "Privacy Policy",
- to: "/privacy-policy",
- },
- {
- label: "Cookie Policy",
- to: "/cookie-policy",
- },
- {
- label: "Terms of Service",
- to: "/terms-of-service",
+ html: `
`,
},
],
},
diff --git a/package-lock.json b/package-lock.json
index 64845a39..29a4f6f9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -41,6 +41,7 @@
"@docusaurus/tsconfig": "^3.6.3",
"@docusaurus/types": "^3.6.3",
"@iconify-icon/react": "^2.1.0",
+ "puppeteer-core": "^24.37.5",
"sharp": "^0.34.2",
"tsx": "^4.20.3",
"typescript": "^5.7.2"
@@ -7023,6 +7024,41 @@
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz",
"integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ=="
},
+ "node_modules/@puppeteer/browsers": {
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.0.tgz",
+ "integrity": "sha512-46BZJYJjc/WwmKjsvDFykHtXrtomsCIrwYQPOP7VfMJoZY2bsDF9oROBABR3paDjDcmkUye1Pb1BqdcdiipaWA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "debug": "^4.4.3",
+ "extract-zip": "^2.0.1",
+ "progress": "^2.0.3",
+ "proxy-agent": "^6.5.0",
+ "semver": "^7.7.4",
+ "tar-fs": "^3.1.1",
+ "yargs": "^17.7.2"
+ },
+ "bin": {
+ "browsers": "lib/cjs/main-cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@puppeteer/browsers/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/@redocly/ajv": {
"version": "8.11.2",
"resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz",
@@ -7423,6 +7459,13 @@
"node": ">=14.16"
}
},
+ "node_modules/@tootallnate/quickjs-emscripten": {
+ "version": "0.23.0",
+ "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
+ "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@trysound/sax": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
@@ -8065,6 +8108,17 @@
"integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
"license": "MIT"
},
+ "node_modules/@types/yauzl": {
+ "version": "2.10.3",
+ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
+ "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@ungap/structured-clone": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
@@ -8334,15 +8388,14 @@
}
},
"node_modules/ajv": {
- "version": "8.18.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
- "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
- "license": "MIT",
+ "version": "8.13.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz",
+ "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==",
"dependencies": {
"fast-deep-equal": "^3.1.3",
- "fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
- "require-from-string": "^2.0.2"
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.4.1"
},
"funding": {
"type": "github",
@@ -8582,6 +8635,19 @@
"node": ">=12.0.0"
}
},
+ "node_modules/ast-types": {
+ "version": "0.13.4",
+ "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
+ "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/astring": {
"version": "1.8.6",
"resolved": "https://registry.npmjs.org/astring/-/astring-1.8.6.tgz",
@@ -8848,6 +8914,16 @@
"baseline-browser-mapping": "dist/cli.js"
}
},
+ "node_modules/basic-ftp": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.1.0.tgz",
+ "integrity": "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
"node_modules/batch": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
@@ -9088,6 +9164,16 @@
"ieee754": "^1.1.13"
}
},
+ "node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -9442,6 +9528,20 @@
"node": ">=6.0"
}
},
+ "node_modules/chromium-bidi": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz",
+ "integrity": "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "mitt": "^3.0.1",
+ "zod": "^3.24.1"
+ },
+ "peerDependencies": {
+ "devtools-protocol": "*"
+ }
+ },
"node_modules/ci-info": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
@@ -11020,6 +11120,16 @@
"lodash-es": "^4.17.21"
}
},
+ "node_modules/data-uri-to-buffer": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
+ "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/dayjs": {
"version": "1.11.19",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz",
@@ -11032,9 +11142,9 @@
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
},
"node_modules/debug": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
- "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -11189,6 +11299,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/degenerator": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz",
+ "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ast-types": "^0.13.4",
+ "escodegen": "^2.1.0",
+ "esprima": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/delaunator": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
@@ -11283,6 +11408,13 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/devtools-protocol": {
+ "version": "0.0.1566079",
+ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1566079.tgz",
+ "integrity": "sha512-MJfAEA1UfVhSs7fbSQOG4czavUp1ajfg6prlAN0+cmfa2zNjaIbvq8VneP7do1WAQQIvgNJWSMeP6UyI90gIlQ==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
"node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@@ -12031,6 +12163,49 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/escodegen": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
+ "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esprima": "^4.0.1",
+ "estraverse": "^5.2.0",
+ "esutils": "^2.0.2"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.6.1"
+ }
+ },
+ "node_modules/escodegen/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/escodegen/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@@ -12354,6 +12529,43 @@
"node": ">=0.10.0"
}
},
+ "node_modules/extract-zip": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
+ "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "get-stream": "^5.1.0",
+ "yauzl": "^2.10.0"
+ },
+ "bin": {
+ "extract-zip": "cli.js"
+ },
+ "engines": {
+ "node": ">= 10.17.0"
+ },
+ "optionalDependencies": {
+ "@types/yauzl": "^2.9.1"
+ }
+ },
+ "node_modules/extract-zip/node_modules/get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/fast-content-type-parse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz",
@@ -12407,22 +12619,6 @@
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
"license": "MIT"
},
- "node_modules/fast-uri": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
- "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/fastify"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/fastify"
- }
- ],
- "license": "BSD-3-Clause"
- },
"node_modules/fastq": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
@@ -12455,6 +12651,16 @@
"node": ">=0.8.0"
}
},
+ "node_modules/fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pend": "~1.2.0"
+ }
+ },
"node_modules/feed": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz",
@@ -12511,10 +12717,9 @@
}
},
"node_modules/file-loader/node_modules/ajv": {
- "version": "6.14.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
- "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
- "license": "MIT",
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -12939,6 +13144,21 @@
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
+ "node_modules/get-uri": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz",
+ "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "basic-ftp": "^5.0.2",
+ "data-uri-to-buffer": "^6.0.2",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/github-from-package": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
@@ -13901,6 +14121,20 @@
"node": ">=8.0.0"
}
},
+ "node_modules/http-proxy-agent": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/http-proxy-middleware": {
"version": "2.0.9",
"resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz",
@@ -14186,6 +14420,16 @@
"loose-envify": "^1.0.0"
}
},
+ "node_modules/ip-address": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
+ "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
"node_modules/ipaddr.js": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz",
@@ -17352,6 +17596,13 @@
"node": ">=16 || 14 >=14.17"
}
},
+ "node_modules/mitt": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/mkdirp": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz",
@@ -17471,6 +17722,16 @@
"node": ">= 10"
}
},
+ "node_modules/netmask": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
+ "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
"node_modules/no-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
@@ -17665,9 +17926,9 @@
}
},
"node_modules/null-loader/node_modules/ajv": {
- "version": "6.14.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
- "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
@@ -18090,6 +18351,40 @@
"node": ">=8"
}
},
+ "node_modules/pac-proxy-agent": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz",
+ "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tootallnate/quickjs-emscripten": "^0.23.0",
+ "agent-base": "^7.1.2",
+ "debug": "^4.3.4",
+ "get-uri": "^6.0.1",
+ "http-proxy-agent": "^7.0.0",
+ "https-proxy-agent": "^7.0.6",
+ "pac-resolver": "^7.0.1",
+ "socks-proxy-agent": "^8.0.5"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/pac-resolver": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz",
+ "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "degenerator": "^5.0.0",
+ "netmask": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/package-json": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz",
@@ -18357,6 +18652,13 @@
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
"license": "MIT"
},
+ "node_modules/pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/periscopic": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz",
@@ -20135,6 +20437,16 @@
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
+ "node_modules/progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/prompts": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
@@ -20191,6 +20503,36 @@
"node": ">= 0.10"
}
},
+ "node_modules/proxy-agent": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz",
+ "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "^4.3.4",
+ "http-proxy-agent": "^7.0.1",
+ "https-proxy-agent": "^7.0.6",
+ "lru-cache": "^7.14.1",
+ "pac-proxy-agent": "^7.1.0",
+ "proxy-from-env": "^1.1.0",
+ "socks-proxy-agent": "^8.0.5"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/proxy-agent/node_modules/lru-cache": {
+ "version": "7.18.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+ "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@@ -20229,6 +20571,47 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/puppeteer-core": {
+ "version": "24.37.5",
+ "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.37.5.tgz",
+ "integrity": "sha512-ybL7iE78YPN4T6J+sPLO7r0lSByp/0NN6PvfBEql219cOnttoTFzCWKiBOjstXSqi/OKpwae623DWAsL7cn2MQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@puppeteer/browsers": "2.13.0",
+ "chromium-bidi": "14.0.0",
+ "debug": "^4.4.3",
+ "devtools-protocol": "0.0.1566079",
+ "typed-query-selector": "^2.12.0",
+ "webdriver-bidi-protocol": "0.4.1",
+ "ws": "^8.19.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/puppeteer-core/node_modules/ws": {
+ "version": "8.19.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
+ "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
"node_modules/pvtsutils": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz",
@@ -20382,9 +20765,9 @@
}
},
"node_modules/raw-loader/node_modules/ajv": {
- "version": "6.14.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
- "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
@@ -22281,6 +22664,17 @@
"node": ">=8.0.0"
}
},
+ "node_modules/smart-buffer": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
"node_modules/snake-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz",
@@ -22300,6 +22694,36 @@
"websocket-driver": "^0.7.4"
}
},
+ "node_modules/socks": {
+ "version": "2.8.7",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
+ "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ip-address": "^10.0.1",
+ "smart-buffer": "^4.2.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/socks-proxy-agent": {
+ "version": "8.0.5",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
+ "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "^4.3.4",
+ "socks": "^2.8.3"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/sort-css-media-queries": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz",
@@ -23206,6 +23630,13 @@
"node": ">= 0.6"
}
},
+ "node_modules/typed-query-selector": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz",
+ "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/typedarray-to-buffer": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
@@ -23604,10 +24035,9 @@
}
},
"node_modules/url-loader/node_modules/ajv": {
- "version": "6.14.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
- "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
- "license": "MIT",
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -23916,6 +24346,13 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/webdriver-bidi-protocol": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.1.tgz",
+ "integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
@@ -24614,6 +25051,17 @@
"node": ">=8"
}
},
+ "node_modules/yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
+ },
"node_modules/yocto-queue": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz",
@@ -24626,6 +25074,16 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/zod": {
+ "version": "3.25.76",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
+ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
"node_modules/zwitch": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
diff --git a/package.json b/package.json
index 50ee9c2d..88f87564 100644
--- a/package.json
+++ b/package.json
@@ -52,6 +52,7 @@
"@docusaurus/tsconfig": "^3.6.3",
"@docusaurus/types": "^3.6.3",
"@iconify-icon/react": "^2.1.0",
+ "puppeteer-core": "^24.37.5",
"sharp": "^0.34.2",
"tsx": "^4.20.3",
"typescript": "^5.7.2"
diff --git a/src/components/landing/Community.module.css b/src/components/landing/Community.module.css
new file mode 100644
index 00000000..32eb633d
--- /dev/null
+++ b/src/components/landing/Community.module.css
@@ -0,0 +1,125 @@
+.section {
+ position: relative;
+ padding: 6rem 0;
+ color: var(--otdf-text-primary);
+}
+
+.inner {
+ max-width: 80rem;
+ margin: 0 auto;
+ padding: 0 1rem;
+}
+
+.header {
+ max-width: 48rem;
+ margin: 0 auto 2.5rem;
+ text-align: center;
+}
+
+.heading {
+ font-size: 1.875rem;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ color: var(--otdf-text-primary);
+ margin: 0 0 1.5rem;
+}
+
+.body {
+ font-size: 1.125rem;
+ color: #9ca3af;
+ line-height: 1.7;
+ margin: 0;
+}
+
+/* Cards */
+.cards {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 1.25rem;
+ max-width: 48rem;
+ margin: 0 auto;
+}
+
+.card {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ padding: 1.5rem;
+ border-radius: 0.75rem;
+ border: 1px solid rgba(255, 255, 255, 0.05);
+ background: #161922;
+ text-decoration: none;
+ transition: border-color 0.2s ease, color 0.2s ease;
+ color: var(--otdf-text-primary);
+}
+
+.card:hover {
+ border-color: rgba(34, 211, 238, 0.2);
+ text-decoration: none;
+}
+
+.card:hover .cardIcon {
+ color: #22d3ee;
+}
+
+.card:hover .cardTitle {
+ color: #22d3ee;
+}
+
+.cardIcon {
+ width: 2rem;
+ height: 2rem;
+ color: #9ca3af;
+ transition: color 0.2s ease;
+}
+
+.cardTitle {
+ margin-top: 0.75rem;
+ font-weight: 600;
+ font-size: 1rem;
+ color: var(--otdf-text-primary);
+ transition: color 0.2s ease;
+}
+
+.cardDesc {
+ margin-top: 0.25rem;
+ font-size: 0.875rem;
+ color: #6b7280;
+}
+
+/* Responsive */
+@media (min-width: 640px) {
+ .cards {
+ grid-template-columns: repeat(3, 1fr);
+ }
+
+ .inner {
+ padding: 0 1.5rem;
+ }
+
+ .heading {
+ font-size: 2.25rem;
+ }
+}
+
+@media (min-width: 1024px) {
+ .inner {
+ padding: 0 2rem;
+ }
+}
+
+/* Light mode overrides */
+:global(html[data-theme='light']) .card {
+ background: #ffffff;
+ border-color: rgba(0, 0, 0, 0.07);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+}
+
+:global(html[data-theme='light']) .body {
+ color: #4b5563;
+}
+
+:global(html[data-theme='light']) .cardIcon {
+ color: #6b7280;
+}
diff --git a/src/components/landing/Community.tsx b/src/components/landing/Community.tsx
new file mode 100644
index 00000000..b40894d4
--- /dev/null
+++ b/src/components/landing/Community.tsx
@@ -0,0 +1,76 @@
+import React from "react";
+import styles from "./Community.module.css";
+
+const GitHubIcon = () => (
+
+
+
+);
+
+const DiscussionsIcon = () => (
+
+
+
+);
+
+const ContributeIcon = () => (
+
+
+
+);
+
+const links = [
+ {
+ href: "https://github.com/opentdf",
+ external: true,
+ Icon: GitHubIcon,
+ title: "GitHub",
+ description: "Browse the source",
+ },
+ {
+ href: "https://github.com/opentdf/platform/discussions",
+ external: true,
+ Icon: DiscussionsIcon,
+ title: "Discussions",
+ description: "Ask questions, share ideas",
+ },
+ {
+ href: "https://github.com/opentdf/platform/blob/main/CONTRIBUTING.md",
+ external: true,
+ Icon: ContributeIcon,
+ title: "Contribute",
+ description: "Help build OpenTDF",
+ },
+];
+
+export default function Community() {
+ return (
+
+
+
+
Join the Movement
+
+ Open source, open community. Shape the future of data-centric security with developers,
+ security professionals, and organizations from around the world. Contribute code, share
+ ideas, and help build the next generation of data protection.
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/landing/DeveloperFirst.module.css b/src/components/landing/DeveloperFirst.module.css
new file mode 100644
index 00000000..2c693cc5
--- /dev/null
+++ b/src/components/landing/DeveloperFirst.module.css
@@ -0,0 +1,239 @@
+.section {
+ position: relative;
+ padding: 6rem 0;
+ color: var(--otdf-text-primary);
+}
+
+.inner {
+ width: 100%;
+ max-width: 80rem;
+ margin: 0 auto;
+ padding: 0 1rem;
+}
+
+.header {
+ max-width: 48rem;
+ margin: 0 auto 3.5rem;
+ text-align: center;
+}
+
+.heading {
+ font-size: 1.875rem;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ color: var(--otdf-text-primary);
+ margin: 0 0 1.5rem;
+}
+
+.subheading {
+ font-size: 1.125rem;
+ color: #9ca3af;
+ line-height: 1.7;
+ margin: 0;
+}
+
+/* Cards grid */
+.cards {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 1.25rem;
+}
+
+.card {
+ min-width: 0;
+ border-radius: 0.75rem;
+ background: #161922;
+ padding: 1.25rem;
+ display: flex;
+ flex-direction: column;
+}
+
+.cardHeader {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ margin-bottom: 1rem;
+}
+
+.cardLeft {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+}
+
+.sdkIcon {
+ width: 2.5rem;
+ height: 2.5rem;
+ border-radius: 0.5rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 0.8125rem;
+ font-weight: 700;
+ flex-shrink: 0;
+}
+
+.sdkName {
+ display: block;
+ font-weight: 600;
+ color: var(--otdf-text-primary);
+ text-decoration: none;
+ font-size: 0.9375rem;
+ transition: color 0.2s ease;
+}
+
+.sdkName:hover {
+ color: #22d3ee;
+ text-decoration: none;
+}
+
+.sdkLang {
+ font-size: 0.75rem;
+ color: #6b7280;
+ margin-top: 0.125rem;
+}
+
+.badge {
+ flex-shrink: 0;
+ font-size: 0.6875rem;
+ padding: 0.125rem 0.5rem;
+ border-radius: 9999px;
+ border: 1px solid rgba(34, 211, 238, 0.3);
+ color: #22d3ee;
+ background: rgba(34, 211, 238, 0.1);
+ font-weight: 500;
+}
+
+/* Install row */
+.installRow {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin-top: auto;
+}
+
+.installCmd {
+ font-family: "JetBrains Mono", "Fira Code", monospace;
+ font-size: 0.75rem;
+ color: #9ca3af;
+ background: #0a0c10;
+ border-radius: 0.5rem;
+ padding: 0.5rem 0.75rem;
+ overflow-x: auto;
+ flex: 1;
+ min-width: 0;
+ white-space: nowrap;
+}
+
+.copyBtn {
+ flex-shrink: 0;
+ width: 2rem;
+ height: 2rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 0.5rem;
+ background: #0a0c10;
+ border: none;
+ color: #6b7280;
+ cursor: pointer;
+ transition: color 0.2s ease;
+}
+
+.copyBtn:hover {
+ color: #22d3ee;
+}
+
+.releaseLink {
+ font-size: 0.875rem;
+ color: #22d3ee;
+ text-decoration: none;
+ transition: color 0.2s ease;
+}
+
+.releaseLink:hover {
+ color: #67e8f9;
+ text-decoration: none;
+}
+
+/* CTAs */
+.ctas {
+ margin-top: 2.5rem;
+ display: flex;
+ justify-content: center;
+ gap: 1rem;
+ flex-wrap: wrap;
+}
+
+.btn {
+ display: inline-flex;
+ align-items: center;
+ padding: 0.625rem 1.25rem;
+ border-radius: 0.5rem;
+ font-weight: 600;
+ font-size: 0.9375rem;
+ text-decoration: none;
+ transition: all 0.2s ease;
+}
+
+.btnPrimary {
+ background: #22d3ee;
+ color: #0a0c10;
+}
+
+.btnPrimary:hover {
+ background: #67e8f9;
+ color: #0a0c10;
+ text-decoration: none;
+}
+
+.btnSecondary {
+ background: transparent;
+ color: var(--otdf-text-primary);
+ border: 1px solid rgba(255, 255, 255, 0.15);
+}
+
+.btnSecondary:hover {
+ border-color: #22d3ee;
+ color: #22d3ee;
+ text-decoration: none;
+}
+
+/* Responsive */
+@media (min-width: 640px) {
+ .cards {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .inner {
+ padding: 0 1.5rem;
+ }
+
+ .heading {
+ font-size: 2.25rem;
+ }
+}
+
+@media (min-width: 1024px) {
+ .cards {
+ grid-template-columns: repeat(4, 1fr);
+ }
+
+ .inner {
+ padding: 0 2rem;
+ }
+}
+
+/* Light mode overrides — install cmd and copy button intentionally stay dark (code) */
+:global(html[data-theme='light']) .card {
+ background: #ffffff;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
+}
+
+:global(html[data-theme='light']) .subheading {
+ color: #4b5563;
+}
+
+:global(html[data-theme='light']) .btnSecondary {
+ border-color: rgba(0, 0, 0, 0.15);
+}
diff --git a/src/components/landing/DeveloperFirst.tsx b/src/components/landing/DeveloperFirst.tsx
new file mode 100644
index 00000000..d83dd2e0
--- /dev/null
+++ b/src/components/landing/DeveloperFirst.tsx
@@ -0,0 +1,155 @@
+import React, { useState } from "react";
+import styles from "./DeveloperFirst.module.css";
+
+const sdks = [
+ {
+ name: "Web SDK",
+ lang: "TypeScript / JS",
+ install: "npm install @opentdf/sdk",
+ colorClass: "sdk-js",
+ icon: "TS",
+ href: "/sdks",
+ recommended: true,
+ },
+ {
+ name: "Platform SDK",
+ lang: "Go",
+ install: "go get github.com/opentdf/platform/sdk",
+ colorClass: "sdk-go",
+ icon: "Go",
+ href: "/sdks",
+ recommended: false,
+ },
+ {
+ name: "Java SDK",
+ lang: "Java",
+ install: "implementation 'io.opentdf:sdk:latest'",
+ colorClass: "sdk-java",
+ icon: "Jv",
+ href: "/sdks",
+ recommended: false,
+ },
+ {
+ name: "CLI",
+ lang: "otdfctl",
+ install: null,
+ releaseUrl: "https://github.com/opentdf/otdfctl/releases/latest",
+ colorClass: "sdk-cli",
+ icon: ">_",
+ href: "/sdks",
+ recommended: false,
+ },
+];
+
+const CopyIcon = () => (
+
+
+
+);
+
+const CheckIcon = () => (
+
+
+
+);
+
+export default function DeveloperFirst() {
+ const [copied, setCopied] = useState(null);
+
+ async function handleCopy(text: string, key: string) {
+ try {
+ await navigator.clipboard.writeText(text);
+ } catch {
+ // Fallback for older browsers
+ const ta = document.createElement("textarea");
+ ta.value = text;
+ ta.style.position = "fixed";
+ ta.style.opacity = "0";
+ document.body.appendChild(ta);
+ ta.select();
+ document.execCommand("copy");
+ document.body.removeChild(ta);
+ }
+ setCopied(key);
+ setTimeout(() => setCopied(null), 2000);
+ }
+
+ return (
+
+
+
+
Built for Developers
+
+ Pick your language. Native SDKs for TypeScript, Go, and Java — plus a CLI for scripting
+ and automation. Everything you need to get building.
+
+
+
+
+ {sdks.map((sdk) => (
+
+
+
+ {sdk.recommended && (
+
Start here
+ )}
+
+
+
+ {"releaseUrl" in sdk ? (
+
+ Download latest release →
+
+ ) : (
+ <>
+
+ {sdk.install}
+
+
handleCopy(sdk.install, sdk.name)}
+ aria-label="Copy install command"
+ title="Copy"
+ >
+ {copied === sdk.name ? : }
+
+ >
+ )}
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/src/components/landing/FinalCTA.module.css b/src/components/landing/FinalCTA.module.css
new file mode 100644
index 00000000..14b20ee6
--- /dev/null
+++ b/src/components/landing/FinalCTA.module.css
@@ -0,0 +1,129 @@
+.section {
+ position: relative;
+ padding: 6rem 0;
+ overflow: hidden;
+ color: var(--otdf-text-primary);
+}
+
+.glow {
+ position: absolute;
+ top: 0;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 37.5rem;
+ height: 37.5rem;
+ background: rgba(34, 211, 238, 0.05);
+ border-radius: 50%;
+ filter: blur(4rem);
+ pointer-events: none;
+}
+
+.inner {
+ position: relative;
+ max-width: 48rem;
+ margin: 0 auto;
+ padding: 0 1rem;
+ text-align: center;
+}
+
+.heading {
+ font-size: 1.875rem;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ color: var(--otdf-text-primary);
+ margin: 0 0 1rem;
+}
+
+.body {
+ font-size: 1.125rem;
+ color: #9ca3af;
+ margin: 0;
+}
+
+.ctas {
+ margin-top: 2.5rem;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: 1rem;
+}
+
+.btn {
+ display: inline-flex;
+ align-items: center;
+ padding: 0.75rem 1.5rem;
+ border-radius: 0.5rem;
+ font-weight: 600;
+ font-size: 1rem;
+ text-decoration: none;
+ transition: all 0.2s ease;
+}
+
+.btnPrimary {
+ background: #22d3ee;
+ color: #0a0c10;
+}
+
+.btnPrimary:hover {
+ background: #67e8f9;
+ color: #0a0c10;
+ text-decoration: none;
+}
+
+.btnSecondary {
+ background: transparent;
+ color: var(--otdf-text-primary);
+ border: 1px solid rgba(255, 255, 255, 0.15);
+}
+
+.btnSecondary:hover {
+ border-color: #22d3ee;
+ color: #22d3ee;
+ text-decoration: none;
+}
+
+.btnGhost {
+ background: transparent;
+ color: #6b7280;
+ border: 1px solid rgba(255, 255, 255, 0.06);
+}
+
+.btnGhost:hover {
+ color: var(--otdf-text-primary);
+ border-color: rgba(255, 255, 255, 0.2);
+ text-decoration: none;
+}
+
+/* Responsive */
+@media (min-width: 640px) {
+ .inner {
+ padding: 0 1.5rem;
+ }
+
+ .heading {
+ font-size: 2.25rem;
+ }
+}
+
+@media (min-width: 1024px) {
+ .inner {
+ padding: 0 2rem;
+ }
+}
+
+/* Light mode overrides */
+:global(html[data-theme='light']) .body {
+ color: #4b5563;
+}
+
+:global(html[data-theme='light']) .btnSecondary {
+ border-color: rgba(0, 0, 0, 0.15);
+}
+
+:global(html[data-theme='light']) .btnGhost {
+ border-color: rgba(0, 0, 0, 0.08);
+}
+
+:global(html[data-theme='light']) .btnGhost:hover {
+ border-color: rgba(0, 0, 0, 0.2);
+}
diff --git a/src/components/landing/FinalCTA.tsx b/src/components/landing/FinalCTA.tsx
new file mode 100644
index 00000000..f078f9c5
--- /dev/null
+++ b/src/components/landing/FinalCTA.tsx
@@ -0,0 +1,33 @@
+import React from "react";
+import styles from "./FinalCTA.module.css";
+
+export default function FinalCTA() {
+ return (
+
+ {/* Centered glow blob */}
+
+
+
+
Ready to Protect Your Data?
+
Choose your path and start building with OpenTDF today.
+
+
+
+
+ );
+}
diff --git a/src/components/landing/Hero.module.css b/src/components/landing/Hero.module.css
new file mode 100644
index 00000000..cd781f51
--- /dev/null
+++ b/src/components/landing/Hero.module.css
@@ -0,0 +1,225 @@
+.section {
+ position: relative;
+ display: flex;
+ align-items: center;
+ overflow: hidden;
+ padding: 6rem 0 6rem;
+ min-height: 100vh;
+ color: var(--otdf-text-primary);
+}
+
+.inner {
+ position: relative;
+ width: 100%;
+ max-width: 80rem;
+ margin: 0 auto;
+ padding: 0 1rem;
+}
+
+.grid {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 2.5rem;
+ align-items: center;
+}
+
+/* Badge */
+.badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.25rem 0.75rem;
+ margin-bottom: 1.5rem;
+ border-radius: 9999px;
+ border: 1px solid rgba(34, 211, 238, 0.2);
+ background: rgba(34, 211, 238, 0.05);
+ color: #22d3ee;
+ font-size: 0.875rem;
+}
+
+.badgeDot {
+ display: inline-block;
+ width: 0.5rem;
+ height: 0.5rem;
+ border-radius: 50%;
+ background: #22d3ee;
+ flex-shrink: 0;
+}
+
+/* Heading */
+.heading {
+ font-size: 2.5rem;
+ font-weight: 800;
+ line-height: 1.1;
+ letter-spacing: -0.02em;
+ color: var(--otdf-text-primary);
+ margin: 0 0 1.5rem;
+}
+
+/* Body */
+.bodyPrimary {
+ margin-top: 1.5rem;
+ font-size: 1.125rem;
+ color: #9ca3af;
+ line-height: 1.7;
+ max-width: 36rem;
+}
+
+.bodySecondary {
+ margin-top: 1.5rem;
+ font-size: 1rem;
+ color: #6b7280;
+ line-height: 1.7;
+ max-width: 36rem;
+}
+
+/* CTAs */
+.ctas {
+ margin-top: 2rem;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 1rem;
+}
+
+.btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0.75rem 1.5rem;
+ border-radius: 0.5rem;
+ font-weight: 600;
+ font-size: 1rem;
+ text-decoration: none;
+ transition: all 0.2s ease;
+}
+
+.btnPrimary {
+ background: #22d3ee;
+ color: #0a0c10;
+}
+
+.btnPrimary:hover {
+ background: #67e8f9;
+ color: #0a0c10;
+ text-decoration: none;
+}
+
+.btnSecondary {
+ background: transparent;
+ color: var(--otdf-text-primary);
+ border: 1px solid rgba(255, 255, 255, 0.15);
+}
+
+.btnSecondary:hover {
+ border-color: #22d3ee;
+ color: #22d3ee;
+ text-decoration: none;
+}
+
+/* Code window */
+.codeWrap {
+ position: relative;
+}
+
+.codeWindow {
+ position: relative;
+ border-radius: 0.75rem;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ background: #0f1117;
+ overflow: hidden;
+}
+
+.windowChrome {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.625rem 1rem;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+}
+
+.dot {
+ width: 0.625rem;
+ height: 0.625rem;
+ border-radius: 50%;
+ flex-shrink: 0;
+}
+
+.dotRed { background: rgba(239, 68, 68, 0.6); }
+.dotYellow { background: rgba(234, 179, 8, 0.6); }
+.dotGreen { background: rgba(34, 197, 94, 0.6); }
+
+.filename {
+ margin-left: 0.75rem;
+ font-size: 0.75rem;
+ font-family: monospace;
+ color: #6b7280;
+}
+
+.code {
+ padding: 1rem 1.25rem;
+ font-family: "JetBrains Mono", "Fira Code", monospace;
+ font-size: 0.8125rem;
+ line-height: 1.7;
+ overflow-x: auto;
+ white-space: nowrap;
+}
+
+.mt {
+ margin-top: 0.75rem;
+}
+
+.indent {
+ padding-left: 1rem;
+ display: block;
+}
+
+/* Syntax colors */
+.cPurple { color: #c084fc; }
+.cYellow { color: #fde047; }
+.cCyan { color: #67e8f9; }
+.cBlue { color: #93c5fd; }
+.cGreen { color: #86efac; }
+.cGray { color: #6b7280; }
+
+/* Responsive */
+@media (min-width: 1024px) {
+ .inner {
+ padding: 0 2rem;
+ }
+
+ .grid {
+ grid-template-columns: 1fr 1fr;
+ gap: 4rem;
+ }
+
+ .heading {
+ font-size: 3.75rem;
+ }
+
+ .section {
+ padding-top: 6rem;
+ }
+}
+
+@media (min-width: 640px) {
+ .heading {
+ font-size: 3rem;
+ }
+
+ .inner {
+ padding: 0 1.5rem;
+ }
+}
+
+/* Light mode overrides — code window intentionally stays dark */
+:global(html[data-theme='light']) .bodyPrimary {
+ color: #4b5563;
+}
+
+:global(html[data-theme='light']) .btnSecondary {
+ border-color: rgba(0, 0, 0, 0.15);
+}
+
+:global(html[data-theme='light']) .btnSecondary:hover {
+ border-color: #22d3ee;
+}
diff --git a/src/components/landing/Hero.tsx b/src/components/landing/Hero.tsx
new file mode 100644
index 00000000..3cd00798
--- /dev/null
+++ b/src/components/landing/Hero.tsx
@@ -0,0 +1,114 @@
+import React from "react";
+import styles from "./Hero.module.css";
+
+export default function Hero() {
+ return (
+
+
+
+
+
+ {/* Left: Copy */}
+
+
+
+ Open Source · BSD-3-Clause-Clear
+
+
+
+ Protect the Data,
+
+ Build the Future
+
+
+
+ Open-source data-centric security for developers. OpenTDF delivers the
+ Trusted Data Format (TDF) specification, foundational services for key
+ management and access control, and SDKs — the building blocks to
+ cryptographically bind protection directly to your data, wherever it goes.
+
+
+
+ Explore the standard. Prototype custom applications. Build architectures
+ where security travels with the data, not the network.
+
+
+
+
+
+ {/* Right: Code preview */}
+
+
+ {/* Window chrome */}
+
+
+
+
+ encrypt.ts
+
+
+ {/* Code */}
+
+
+ import
+ {" { "}
+ OpenTDF
+ {" } "}
+ from
+ {" "}
+ '@opentdf/sdk' ;
+
+
+ {"// Connect to the platform"}
+
+
+ const
+ {" "}
+ client
+ {" = "}
+ await
+ {" "}
+ OpenTDF
+ {"."}connect
+ {"("}
+ 'http://localhost:8080'
+ {");"}
+
+
+ {"// Encrypt with attribute-based policy"}
+
+
+ const
+ {" "}
+ ciphertext
+ {" = "}
+ await
+ {" "}
+ client
+ {"."}encrypt
+ {"(data, {"}
+
+
+
+ attributes
+ {": ["}
+ 'https://example.com/attr/class/value/secret'
+ {"],"}
+
+
+
{"});"}
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/landing/ProblemSolution.module.css b/src/components/landing/ProblemSolution.module.css
new file mode 100644
index 00000000..e5ffc25d
--- /dev/null
+++ b/src/components/landing/ProblemSolution.module.css
@@ -0,0 +1,160 @@
+.section {
+ position: relative;
+ padding: 6rem 0;
+ color: var(--otdf-text-primary);
+}
+
+.inner {
+ max-width: 80rem;
+ margin: 0 auto;
+ padding: 0 1rem;
+}
+
+.header {
+ max-width: 48rem;
+ margin: 0 auto 3rem;
+ text-align: center;
+}
+
+.heading {
+ font-size: 1.875rem;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ color: var(--otdf-text-primary);
+ margin: 0 0 1.5rem;
+}
+
+.bodyPrimary {
+ font-size: 1.125rem;
+ color: #9ca3af;
+ line-height: 1.7;
+ margin: 0 0 1rem;
+}
+
+.bodySecondary {
+ font-size: 1rem;
+ color: #6b7280;
+ margin: 0;
+}
+
+/* Cards */
+.cards {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 1.5rem;
+ max-width: 56rem;
+ margin: 0 auto;
+}
+
+.card {
+ border-radius: 0.75rem;
+ border: 1px solid rgba(255, 255, 255, 0.05);
+ background: #161922;
+ padding: 1.5rem;
+ transition: border-color 0.2s ease;
+}
+
+.card:hover {
+ border-color: rgba(34, 211, 238, 0.2);
+}
+
+.iconWrap {
+ width: 2.5rem;
+ height: 2.5rem;
+ border-radius: 0.5rem;
+ background: rgba(34, 211, 238, 0.1);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 1rem;
+}
+
+.cardIcon {
+ width: 1.25rem;
+ height: 1.25rem;
+ color: #22d3ee;
+}
+
+.cardTitle {
+ font-size: 1.125rem;
+ font-weight: 600;
+ color: var(--otdf-text-primary);
+ margin: 0 0 0.5rem;
+}
+
+.cardBody {
+ font-size: 0.875rem;
+ color: #9ca3af;
+ line-height: 1.6;
+ margin: 0;
+}
+
+/* Tagline + CTA */
+.tagline {
+ margin-top: 2.5rem;
+ text-align: center;
+ font-size: 0.875rem;
+ color: #6b7280;
+}
+
+.cta {
+ margin-top: 2rem;
+ display: flex;
+ justify-content: center;
+}
+
+.btn {
+ display: inline-flex;
+ align-items: center;
+ padding: 0.625rem 1.25rem;
+ border-radius: 0.5rem;
+ font-weight: 600;
+ font-size: 0.9375rem;
+ text-decoration: none;
+ color: var(--otdf-text-primary);
+ border: 1px solid rgba(255, 255, 255, 0.15);
+ transition: all 0.2s ease;
+}
+
+.btn:hover {
+ border-color: #22d3ee;
+ color: #22d3ee;
+ text-decoration: none;
+}
+
+/* Responsive */
+@media (min-width: 768px) {
+ .cards {
+ grid-template-columns: repeat(3, 1fr);
+ }
+
+ .heading {
+ font-size: 2.25rem;
+ }
+
+ .inner {
+ padding: 0 1.5rem;
+ }
+}
+
+@media (min-width: 1024px) {
+ .inner {
+ padding: 0 2rem;
+ }
+}
+
+/* Light mode overrides */
+:global(html[data-theme='light']) .card {
+ background: #ffffff;
+ border-color: rgba(0, 0, 0, 0.07);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
+}
+
+:global(html[data-theme='light']) .bodyPrimary,
+:global(html[data-theme='light']) .cardBody {
+ color: #4b5563;
+}
+
+:global(html[data-theme='light']) .btn {
+ border-color: rgba(0, 0, 0, 0.15);
+}
diff --git a/src/components/landing/ProblemSolution.tsx b/src/components/landing/ProblemSolution.tsx
new file mode 100644
index 00000000..14883fee
--- /dev/null
+++ b/src/components/landing/ProblemSolution.tsx
@@ -0,0 +1,75 @@
+import React from "react";
+import styles from "./ProblemSolution.module.css";
+
+const cards = [
+ {
+ title: "Revoke After Sharing",
+ body: "Revoke access after sharing — even after data has left your environment.",
+ icon: (
+
+
+
+ ),
+ },
+ {
+ title: "Zero-Trust Enforcement",
+ body: "Enforce controls in zero-trust environments — no VPN, no network dependency.",
+ icon: (
+
+
+
+ ),
+ },
+ {
+ title: "Complete Audit Trail",
+ body: "Maintain a complete audit trail — know who accessed what, when, and where.",
+ icon: (
+
+
+
+ ),
+ },
+];
+
+export default function ProblemSolution() {
+ return (
+
+
+
+
+ Traditional Security Fails When Data Leaves the Perimeter
+
+
+ Firewalls, VPNs, and network policies protect the boundary — not the data.
+ The moment a file is shared, downloaded, or moved to a new environment,
+ those controls vanish. Access decisions remain at the perimeter, while the
+ data moves on without them.
+
+
+ OpenTDF inverts this model. Policies travel with the data itself, so you can:
+
+
+
+
+ {cards.map((card) => (
+
+
{card.icon}
+
{card.title}
+
{card.body}
+
+ ))}
+
+
+
+ This is data-centric security: protection that's embedded, not bolted on.
+
+
+
+
+
+ );
+}
diff --git a/src/components/landing/Standards.module.css b/src/components/landing/Standards.module.css
new file mode 100644
index 00000000..237a2067
--- /dev/null
+++ b/src/components/landing/Standards.module.css
@@ -0,0 +1,199 @@
+.section {
+ position: relative;
+ padding: 6rem 0;
+ color: var(--otdf-text-primary);
+}
+
+.inner {
+ max-width: 80rem;
+ margin: 0 auto;
+ padding: 0 1rem;
+}
+
+.grid {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 3rem;
+ align-items: center;
+}
+
+/* Diagram */
+.diagramOrder {
+ order: 2;
+}
+
+.contentOrder {
+ order: 1;
+}
+
+.diagramWrap {
+ /* intentional wrapper */
+}
+
+.diagram {
+ border-radius: 0.75rem;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ background: #0f1117;
+ padding: 1.5rem;
+ font-family: "JetBrains Mono", "Fira Code", monospace;
+ font-size: 0.875rem;
+}
+
+.diagramComment {
+ color: #4b5563;
+ margin-bottom: 1rem;
+}
+
+.diagramBody {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+
+.diagramRow {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+}
+
+.pl4 { padding-left: 1rem; }
+.pl8 { padding-left: 2rem; }
+
+.cFaint { color: #4b5563; font-size: 0.75rem; margin-left: 0.5rem; }
+.cFaint2 { color: #4b5563; }
+.cCyan { color: #22d3ee; }
+.cLight { color: #d1d5db; }
+.cGreen { color: #86efac; }
+.cAmber { color: #fcd34d; }
+.cPurple { color: #c084fc; }
+
+/* Content */
+.heading {
+ font-size: 1.875rem;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ color: var(--otdf-text-primary);
+ margin: 0 0 1.5rem;
+}
+
+.body {
+ font-size: 1.125rem;
+ color: #9ca3af;
+ line-height: 1.7;
+ margin: 0 0 1.5rem;
+}
+
+.featureList {
+ list-style: none;
+ padding: 0;
+ margin: 0 0 2rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+}
+
+.featureItem {
+ display: flex;
+ align-items: flex-start;
+ gap: 0.75rem;
+ color: #9ca3af;
+ font-size: 1rem;
+}
+
+.checkIcon {
+ width: 1.25rem;
+ height: 1.25rem;
+ color: #22d3ee;
+ flex-shrink: 0;
+ margin-top: 0.125rem;
+}
+
+/* CTAs */
+.ctas {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 1rem;
+}
+
+.btn {
+ display: inline-flex;
+ align-items: center;
+ padding: 0.625rem 1.25rem;
+ border-radius: 0.5rem;
+ font-weight: 600;
+ font-size: 0.9375rem;
+ text-decoration: none;
+ transition: all 0.2s ease;
+}
+
+.btnSecondary {
+ background: transparent;
+ color: var(--otdf-text-primary);
+ border: 1px solid rgba(255, 255, 255, 0.15);
+}
+
+.btnSecondary:hover {
+ border-color: #22d3ee;
+ color: #22d3ee;
+ text-decoration: none;
+}
+
+.btnGhost {
+ background: transparent;
+ color: #6b7280;
+ border: 1px solid rgba(255, 255, 255, 0.06);
+}
+
+.btnGhost:hover {
+ color: var(--otdf-text-primary);
+ border-color: rgba(255, 255, 255, 0.2);
+ text-decoration: none;
+}
+
+/* Responsive */
+@media (min-width: 640px) {
+ .inner {
+ padding: 0 1.5rem;
+ }
+}
+
+@media (min-width: 1024px) {
+ .grid {
+ grid-template-columns: 1fr 1fr;
+ gap: 3rem;
+ }
+
+ .diagramOrder {
+ order: 1;
+ }
+
+ .contentOrder {
+ order: 2;
+ }
+
+ .inner {
+ padding: 0 2rem;
+ }
+
+ .heading {
+ font-size: 2.25rem;
+ }
+}
+
+/* Light mode overrides — diagram intentionally stays dark */
+:global(html[data-theme='light']) .body,
+:global(html[data-theme='light']) .featureItem {
+ color: #4b5563;
+}
+
+:global(html[data-theme='light']) .btnSecondary {
+ border-color: rgba(0, 0, 0, 0.15);
+}
+
+:global(html[data-theme='light']) .btnGhost {
+ border-color: rgba(0, 0, 0, 0.08);
+}
+
+:global(html[data-theme='light']) .btnGhost:hover {
+ border-color: rgba(0, 0, 0, 0.2);
+}
diff --git a/src/components/landing/Standards.tsx b/src/components/landing/Standards.tsx
new file mode 100644
index 00000000..f1bd305f
--- /dev/null
+++ b/src/components/landing/Standards.tsx
@@ -0,0 +1,99 @@
+import React from "react";
+import styles from "./Standards.module.css";
+
+const CheckIcon = () => (
+
+
+
+);
+
+const features = [
+ "AES-256-GCM authenticated encryption",
+ "NIST SP 800-162 ABAC model",
+ "Cryptographic policy binding",
+ "Open specification — not proprietary",
+];
+
+export default function Standards() {
+ return (
+
+
+
+ {/* Left: TDF diagram */}
+
+
+
{""}
+
+
+ ┌
+ TDF Object
+
+
+ ├─
+ manifest.json
+ policy + key access
+
+
+ ├─
+ encryptionMethod
+ AES-256-GCM
+
+
+ ├─
+ keyAccess[]
+ wrapped DEK + KAS URL
+
+
+ ├─
+ policy
+ ABAC attributes
+
+
+ └─
+ assertions[]
+ signed bindings
+
+
+ └─
+ payload
+ encrypted content
+
+
+ └
+
+
+
+
+
+ {/* Right: Content */}
+
+
Standards-Based Security
+
+ Built on the proven NIST ABAC model for interoperability and compliance. OpenTDF
+ follows established standards for attribute-based access control, ensuring your data
+ protection strategy is future-proof and audit-ready.
+
+
+
+ {features.map((f) => (
+
+
+ {f}
+
+ ))}
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/landing/index.ts b/src/components/landing/index.ts
new file mode 100644
index 00000000..e5b59856
--- /dev/null
+++ b/src/components/landing/index.ts
@@ -0,0 +1,6 @@
+export { default as Hero } from "./Hero";
+export { default as ProblemSolution } from "./ProblemSolution";
+export { default as DeveloperFirst } from "./DeveloperFirst";
+export { default as Standards } from "./Standards";
+export { default as Community } from "./Community";
+export { default as FinalCTA } from "./FinalCTA";
diff --git a/src/css/custom.css b/src/css/custom.css
index 6ed14ec6..0e55c7de 100644
--- a/src/css/custom.css
+++ b/src/css/custom.css
@@ -52,19 +52,18 @@
}
:root {
- --ifm-color-primary: #004987;
- --ifm-color-primary-dark: #003765;
- --ifm-color-primary-darker: #002544;
- --ifm-color-primary-darkest: #001e4a;
- --ifm-color-primary-light: #6aaae4;
- --ifm-color-primary-lighter: #92c0e9;
- --ifm-color-primary-lightest: #c2d5ed;
+ --ifm-color-primary: #0891b2;
+ --ifm-color-primary-dark: #0e7490;
+ --ifm-color-primary-darker: #155e75;
+ --ifm-color-primary-darkest: #164e63;
+ --ifm-color-primary-light: #22d3ee;
+ --ifm-color-primary-lighter: #67e8f9;
+ --ifm-color-primary-lightest: #a5f3fc;
--ifm-code-font-size: 95%;
- --ifm-blockquote-border-color: #B2D6FF;
- --ifm-blockquote-background-color: var(--ifm-color-white);
- ;
- --ifm-color-info-contrast-background: var(--ifm-color-white);
- --ifm-color-info-dark: var(--ifm-blockquote-border-color);
+ --ifm-blockquote-border-color: #0891b2;
+ --ifm-blockquote-background-color: #ecfeff;
+ --ifm-color-info-contrast-background: #ecfeff;
+ --ifm-color-info-dark: #0891b2;
--ifm-color-white: #FFF;
--ifm-heading-font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
--ifm-font-family-base: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
@@ -76,20 +75,46 @@
***************/
.docusaurus-highlight-code-line {
- background-color: rgba(0, 0, 0, 0.1);
+ background-color: rgba(8, 145, 178, 0.09);
display: block;
margin: 0 calc(-1 * var(--ifm-pre-padding));
padding: 0 var(--ifm-pre-padding);
+ border-left: 2px solid #0891b2;
+}
+
+/* Tip admonition — amber, light mode */
+div[class*="admonition-tip"],
+div[class*="admonitionTip"] {
+ --ifm-alert-background-color: #fffbeb;
+ --ifm-alert-border-color: #f59e0b;
+ --ifm-alert-color: #92400e;
+}
+
+/* Tip admonition — amber, dark mode */
+html[data-theme='dark'] div[class*="admonition-tip"],
+html[data-theme='dark'] div[class*="admonitionTip"] {
+ --ifm-alert-background-color: #451a03;
+ --ifm-alert-border-color: #d97706;
+ --ifm-alert-color: #fde68a;
}
html[data-theme='dark'] {
+ /* Override primary to teal in dark mode */
+ --ifm-color-primary: #22d3ee;
+ --ifm-color-primary-dark: #0891b2;
+ --ifm-color-primary-darker: #0e7490;
+ --ifm-color-primary-darkest: #155e75;
+ --ifm-color-primary-light: #67e8f9;
+ --ifm-color-primary-lighter: #a5f3fc;
+ --ifm-color-primary-lightest: #cffafe;
+
--ifm-heading-color: var(--ifm-font-color-base);
--ifm-blockquote-background-color: none;
--ifm-color-info-contrast-background: #001E4A;
--ifm-color-info-dark: #174EB6;
- --ifm-link-color: var(--ifm-color-primary-light);
- --ifm-menu-color-active: var(--ifm-color-primary-light);
- --ifm-breadcrumb-color-active: var(--ifm-color-primary-light);
+ --ifm-link-color: var(--ifm-color-primary);
+ --ifm-menu-color-active: var(--ifm-color-primary);
+ --ifm-breadcrumb-color-active: var(--ifm-color-primary);
}
html[data-theme='dark'] .hero {
@@ -102,7 +127,7 @@ html[data-theme='dark'] .docusaurus-highlight-code-line {
html[data-theme='dark'] .table-of-contents__link:hover,
html[data-theme='dark'] .table-of-contents__link--active {
- color: var(--ifm-color-primary-light);
+ color: var(--ifm-color-primary);
}
/* Dark mode overrides for custom VDS colors and homepage */
@@ -119,16 +144,6 @@ html[data-theme='dark'] {
--vds-homepage-subtitle-color: #aaa;
}
-/* Dark mode navbar for homepage */
-html[data-theme='dark'] .homepage .navbar {
- background-color: var(--vds-color-blue-lightest);
- box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
-}
-
-html[data-theme='dark'] .navbar .navbar__brand {
- color: var(--vds-color-blue-dark);
-}
-
/* Dark mode cookie consent */
html[data-theme='dark'] .cookie-consent-banner a {
color: var(--ifm-color-primary-light);
@@ -139,27 +154,6 @@ html[data-theme='dark'] .cookie-consent-accept-button {
color: #000;
}
-/* Dark mode footer */
-html[data-theme='dark'] .footer {
- background-color: #0a1929;
- color: #b3d4fc;
-}
-
-html[data-theme='dark'] .footer__link-item {
- color: #6aaae4;
-}
-
-html[data-theme='dark'] .footer__link-item:hover {
- color: #92c0e9;
-}
-
-html[data-theme='dark'] .footer__title {
- color: #6bbaff;
-}
-
-html[data-theme='dark'] .footer__copyright {
- color: #6aaae4;
-}
.star-logo {
width: 200px;
@@ -175,85 +169,100 @@ a[class*="embed_documentation_footer-"] {
***************/
.footer {
- height: auto;
- background-color: var(--vds-color-blue-darkest);
- color: var(--vds-color-blue-lighter);
-}
-.footer .container {
- display: flex;
- justify-content: space-between;
- gap: 3rem;
- flex-direction: row-reverse;
- padding: 1rem 0;
- align-items: flex-start;
+ background-color: #0a0c10;
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
}
-.footer__links {
- flex: 1;
- display: flex;
- justify-content: flex-end;
+.footer__title {
+ font-size: 0.75rem;
+ font-weight: 700;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ color: #fff;
+ margin-bottom: 1rem;
}
.footer__link-item {
- font-size: 1.1rem;
- color: var(--vds-color-blue-light);
+ font-size: 0.875rem;
+ color: #6b7280;
+ transition: color 0.2s ease;
}
-.footer__title {
- font-size: .9rem;
- color: var(--vds-color-blue-dark);
+.footer__link-item:hover {
+ color: #22d3ee;
+ text-decoration: none;
}
.footer__bottom {
- flex: 1;
- text-align: left;
+ margin-top: 3rem;
+ padding-top: 2rem;
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ flex-direction: row;
+ flex: unset;
+ text-align: unset;
+}
+
+.footer__copyright {
+ font-size: 0.75rem;
+ color: #374151;
+ display: block;
}
-.footer__bottom img {
- height: 46px;
+.footer__sponsor-text {
+ font-size: 0.875rem;
+ color: #6b7280;
+ line-height: 1.6;
margin: 0;
}
-.footer__copyright {
- display: flex;
- justify-content: space-between;
- color: var(--vds-color-blue-darker);
+.footer__sponsor-text a {
+ color: #22d3ee;
+ text-decoration: none;
}
-@media (min-width: 1201px) {
- /* Styles for xl breakpoint */
+.footer__sponsor-text a:hover {
+ color: #67e8f9;
}
-@media (max-width: 1200px) {
- .footer .container {
- padding: var(--vds-homepage-container-padding-lg);
- }
+/* Footer — light mode overrides */
+html[data-theme='light'] .footer {
+ background-color: #f1f5f9;
+ border-top: 1px solid rgba(0, 0, 0, 0.06);
}
-@media (max-width: 1024px) {
- .footer .container {
- padding: var(--vds-homepage-container-padding-md);
- }
+html[data-theme='light'] .footer__title {
+ color: #0f172a;
}
-@media (max-width: 768px) {
- .footer .container {
- justify-content: center;
- padding: var(--vds-homepage-container-padding-sm);
- }
+html[data-theme='light'] .footer__link-item {
+ color: #4b5563;
+}
+
+html[data-theme='light'] .footer__link-item:hover {
+ color: #0891b2;
+}
+
+html[data-theme='light'] .footer__bottom {
+ border-top: 1px solid rgba(0, 0, 0, 0.06);
+}
+
+html[data-theme='light'] .footer__copyright {
+ color: #6b7280;
+}
+
+html[data-theme='light'] .footer__sponsor-text {
+ color: #4b5563;
+}
- .footer__copyright {
- flex-direction: column;
- gap: 1rem;
- }
+html[data-theme='light'] .footer__sponsor-text a {
+ color: #0891b2;
}
-@media (max-width: 480px) {
- .footer .container {
- flex-direction: column;
- gap: 1rem;
- padding: var(--vds-homepage-container-padding-xs);
- }
+html[data-theme='light'] .footer__sponsor-text a:hover {
+ color: #0e7490;
}
/**************
@@ -348,14 +357,151 @@ Breakpoints:
line-height: var(--vds-homepage-body-line-height);
}
-.homepage .navbar {
- background-color: var(--vds-color-blue-lighter);
- border: 0;
- box-shadow: 0 1px 2px 0 var(--vds-color-blue-lighter);
+/**************
+** NAVBAR — THEME-AWARE (light + dark)
+***************/
+
+/* GitHub icon as a reusable CSS variable (avoids duplicating the long data URL) */
+:root {
+ --github-icon-svg: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.166 6.839 9.49.5.092.682-.217.682-.482 0-.237-.009-.866-.013-1.7-2.782.604-3.369-1.34-3.369-1.34-.454-1.156-1.11-1.463-1.11-1.463-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.578 9.578 0 0112 6.836c.85.004 1.705.115 2.504.337 1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.203 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.743 0 .267.18.578.688.48C19.138 20.163 22 16.418 22 12c0-5.523-4.477-10-10-10z'/%3E%3C/svg%3E");
+}
+
+/* Light mode: clean white glass */
+.navbar {
+ background-color: rgba(255, 255, 255, 0.9);
+ backdrop-filter: blur(12px);
+ -webkit-backdrop-filter: blur(12px);
+ border-bottom: 1px solid rgba(0, 0, 0, 0.06);
+ box-shadow: none;
+}
+
+/* Dark mode */
+html[data-theme='dark'] .navbar {
+ background-color: rgba(10, 12, 16, 0.9);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+}
+
+/* Brand: size/weight only; color inherits from Docusaurus theme */
+.navbar .navbar__brand .navbar__title {
+ font-size: 1.125rem;
+ font-weight: 700;
+}
+
+html[data-theme='dark'] .navbar .navbar__brand .navbar__title {
+ color: #fff;
+}
+
+.navbar .navbar__brand:hover .navbar__title {
+ color: #22d3ee;
+}
+
+/* Nav links: font size in both modes */
+.navbar .navbar__link {
+ font-size: 0.875rem;
+}
+
+/* Dark mode: muted gray nav links */
+html[data-theme='dark'] .navbar .navbar__link {
+ color: #9ca3af;
+}
+
+/* Hide external-link icon on navbar items */
+.navbar [class*="iconExternalLink"] {
+ display: none;
+}
+
+.navbar .navbar__link:hover,
+.navbar .navbar__link--active {
+ color: #22d3ee;
+ background: rgba(34, 211, 238, 0.07);
+ text-decoration: none;
+}
+
+/**************
+** NAVBAR LAYOUT — 3 columns: Logo (left) | Nav items (center) | GitHub (right)
+**
+** Docusaurus puts brand + nav links in the same .navbar__items container
+** (there is NO --left modifier). We pull the brand out with position:absolute
+** so only nav links remain in flow, center the container with the parent,
+** and absolute-position .navbar__items--right to the far right.
+***************/
+
+.navbar__inner {
+ position: relative;
+ display: flex;
+ justify-content: center;
+ align-items: center;
}
+
.navbar .navbar__brand {
- color: var(--vds-color-blue-006);
- font-size: 1.5rem;
+ position: absolute;
+ left: var(--ifm-navbar-padding-horizontal);
+ top: 50%;
+ transform: translateY(-50%);
+ display: flex;
+ align-items: center;
+}
+
+/* Left items container: shrink-to-fit so parent can center it.
+ max-width prevents centered links from overlapping absolutely-positioned brand/right items. */
+.navbar__inner > .navbar__items:not(.navbar__items--right) {
+ flex: 0 0 auto;
+ justify-content: center;
+ max-width: calc(100% - 2 * (var(--ifm-navbar-padding-horizontal) + 8rem));
+}
+
+.navbar .navbar__items--right {
+ position: absolute;
+ right: var(--ifm-navbar-padding-horizontal);
+ top: 50%;
+ transform: translateY(-50%);
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+/* GitHub navbar button — bordered pill style, scoped to right container only */
+.navbar__items--right .navbar__item.navbar__link[href*="github.com"] {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.375rem;
+ padding: 0.375rem 0.75rem;
+ border: 1px solid #d1d5db;
+ border-radius: 0.5rem;
+ font-size: 0.875rem;
+ transition: border-color 0.2s ease, color 0.2s ease;
+}
+
+html[data-theme='dark'] .navbar__items--right .navbar__item.navbar__link[href*="github.com"] {
+ border-color: #374151;
+ color: #9ca3af;
+}
+
+.navbar__items--right .navbar__item.navbar__link[href*="github.com"]::before {
+ content: "";
+ display: inline-block;
+ width: 1.125rem;
+ height: 1.125rem;
+ background-color: currentColor;
+ -webkit-mask-image: var(--github-icon-svg);
+ mask-image: var(--github-icon-svg);
+ -webkit-mask-size: contain;
+ mask-size: contain;
+ -webkit-mask-repeat: no-repeat;
+ mask-repeat: no-repeat;
+ flex-shrink: 0;
+}
+
+.navbar__items--right .navbar__item.navbar__link[href*="github.com"]:hover {
+ border-color: #22d3ee;
+ color: #22d3ee;
+ background: transparent;
+ text-decoration: none;
+}
+
+html[data-theme='dark'] .navbar__items--right .navbar__item.navbar__link[href*="github.com"]:hover {
+ border-color: #6b7280;
+ color: #fff;
}
/**************
@@ -384,3 +530,59 @@ html[data-theme='dark'] div[class*="language-shell"] code::before {
.footer__license-info a {
text-decoration: underline;
}
+
+/**************
+** OPENTDF LANDING — DARK DESIGN TOKENS
+***************/
+:root {
+ /* Backgrounds */
+ --otdf-bg-deepest: #0a0c10;
+ --otdf-bg-deep: #0f1117;
+ --otdf-bg-surface: #161922;
+ --otdf-bg-raised: #1e2230;
+ --otdf-bg-subtle: #272c3d;
+
+ /* Text */
+ --otdf-text-primary: #e8eaf0;
+ --otdf-text-secondary: #9ca3b8;
+ --otdf-text-tertiary: #636b82;
+
+ /* Accent: Electric Cyan */
+ --otdf-accent: #22d3ee;
+ --otdf-accent-light: #67e8f9;
+ --otdf-accent-dark: #0891b2;
+ --otdf-accent-muted: #164e63;
+ --otdf-accent-glow: rgba(34, 211, 238, 0.15);
+
+ /* Secondary: Warm Amber */
+ --otdf-secondary: #f59e0b;
+ --otdf-secondary-muted: #78350f;
+
+ /* SDK Colors */
+ --otdf-go: #22c55e;
+ --otdf-js: #3b82f6;
+ --otdf-java: #f59e0b;
+ --otdf-cli: #a78bfa;
+
+ /* Gradients */
+ --otdf-gradient-hero: linear-gradient(135deg, #0891b2 0%, #22d3ee 50%, #67e8f9 100%);
+ --otdf-gradient-mesh: radial-gradient(ellipse at 20% 50%, rgba(34, 211, 238, 0.08) 0%, transparent 50%),
+ radial-gradient(ellipse at 80% 20%, rgba(245, 158, 11, 0.05) 0%, transparent 50%);
+}
+
+/* Light mode: flip landing page design tokens */
+html[data-theme='light'] {
+ --otdf-bg-deepest: #f8fafc;
+ --otdf-bg-deep: #f1f5f9;
+ --otdf-bg-surface: #ffffff;
+ --otdf-bg-raised: #f8fafc;
+ --otdf-bg-subtle: #e2e8f0;
+
+ --otdf-text-primary: #0f172a;
+ --otdf-text-secondary: #334155;
+ --otdf-text-tertiary: #64748b;
+
+ --otdf-accent-glow: rgba(34, 211, 238, 0.08);
+ --otdf-gradient-mesh: radial-gradient(ellipse at 20% 50%, rgba(34, 211, 238, 0.05) 0%, transparent 50%),
+ radial-gradient(ellipse at 80% 20%, rgba(245, 158, 11, 0.03) 0%, transparent 50%);
+}
diff --git a/src/css/landing.css b/src/css/landing.css
new file mode 100644
index 00000000..9db6e8d4
--- /dev/null
+++ b/src/css/landing.css
@@ -0,0 +1,101 @@
+/* OpenTDF Landing Page — Shared Dark Theme Utilities */
+
+/* ---- Grid background pattern ---- */
+.bg-grid {
+ background-image: linear-gradient(rgba(34, 211, 238, 0.03) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(34, 211, 238, 0.03) 1px, transparent 1px);
+ background-size: 64px 64px;
+}
+
+/* ---- Gradient mesh backgrounds ---- */
+.bg-mesh {
+ background-image: radial-gradient(ellipse at 20% 50%, rgba(34, 211, 238, 0.08) 0%, transparent 50%),
+ radial-gradient(ellipse at 80% 20%, rgba(245, 158, 11, 0.05) 0%, transparent 50%);
+}
+
+.bg-mesh-reverse {
+ background-image: radial-gradient(ellipse at 80% 60%, rgba(34, 211, 238, 0.06) 0%, transparent 50%),
+ radial-gradient(ellipse at 20% 30%, rgba(245, 158, 11, 0.04) 0%, transparent 50%);
+}
+
+/* ---- Gradient text ---- */
+.text-gradient {
+ background: linear-gradient(135deg, #22d3ee 0%, #67e8f9 50%, #a78bfa 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+/* ---- Glow effects ---- */
+.glow-cyan {
+ box-shadow: 0 0 20px rgba(34, 211, 238, 0.15), 0 0 60px rgba(34, 211, 238, 0.05);
+}
+
+/* ---- Code block animation ---- */
+@keyframes otdfTypewriter {
+ from {
+ opacity: 0;
+ transform: translateY(4px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.code-line {
+ animation: otdfTypewriter 0.3s ease-out forwards;
+ opacity: 0;
+}
+
+.code-line:nth-child(1) { animation-delay: 0.1s; }
+.code-line:nth-child(2) { animation-delay: 0.3s; }
+.code-line:nth-child(3) { animation-delay: 0.5s; }
+.code-line:nth-child(4) { animation-delay: 0.7s; }
+.code-line:nth-child(5) { animation-delay: 0.9s; }
+.code-line:nth-child(6) { animation-delay: 1.1s; }
+.code-line:nth-child(7) { animation-delay: 1.3s; }
+
+/* ---- Section alternating backgrounds ---- */
+.section-dark {
+ background-color: #0a0c10;
+}
+
+.section-darker {
+ background-color: #0f1117;
+}
+
+/* ---- SDK card accent colors ---- */
+.sdk-go { --sdk-color: #22c55e; }
+.sdk-js { --sdk-color: #3b82f6; }
+.sdk-java { --sdk-color: #f59e0b; }
+.sdk-cli { --sdk-color: #a78bfa; }
+
+.sdk-card {
+ border: 1px solid rgba(255, 255, 255, 0.06);
+ transition: all 0.3s ease;
+}
+
+.sdk-card:hover {
+ border-color: var(--sdk-color, #22d3ee);
+ box-shadow: 0 0 30px color-mix(in srgb, var(--sdk-color, #22d3ee) 15%, transparent);
+}
+
+/* ---- Light mode overrides ---- */
+html[data-theme='light'] .section-dark {
+ background-color: #f8fafc;
+}
+
+html[data-theme='light'] .section-darker {
+ background-color: #ffffff;
+}
+
+html[data-theme='light'] .bg-grid {
+ background-image: linear-gradient(rgba(34, 211, 238, 0.05) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(34, 211, 238, 0.05) 1px, transparent 1px);
+ background-size: 64px 64px;
+}
+
+html[data-theme='light'] .sdk-card {
+ border-color: rgba(0, 0, 0, 0.08);
+}
diff --git a/src/openapi/preprocessing.ts b/src/openapi/preprocessing.ts
index 02fd8f20..fdfb2ee7 100644
--- a/src/openapi/preprocessing.ts
+++ b/src/openapi/preprocessing.ts
@@ -467,7 +467,11 @@ function renameInfoFilesToIndex() {
} else if (item.name.endsWith('.info.mdx')) {
const newPath = path.join(dir, 'index.mdx');
if (fs.existsSync(newPath)) {
- console.warn(`⚠️ Skipping rename of ${fullPath} because destination ${newPath} already exists.`);
+ // Plugin now generates both index.mdx and *.info.mdx with the same ID.
+ // The duplicate causes Docusaurus to lose the category index association.
+ // Remove the .info.mdx since index.mdx already has identical content.
+ fs.unlinkSync(fullPath);
+ console.log(` Removed duplicate: ${fullPath} (index.mdx already exists)`);
} else {
fs.renameSync(fullPath, newPath);
console.log(` Renamed: ${fullPath} → ${newPath}`);
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 5304464d..ddf6dfff 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -1,103 +1,37 @@
import React from "react";
import Layout from "@theme/Layout";
import Head from "@docusaurus/Head";
-import { Columns, Hero, Features, Feedback } from "../components/Homepage";
+import { Hero, ProblemSolution, DeveloperFirst, Standards, Community, FinalCTA } from "../components/landing";
export default function Home() {
return (
-
-
+ {/* OpenGraph */}
+
+
+
+
+
+
+
+
+ {/* Twitter / X */}
+
+
+
+
-
-
-
- OpenTDF is an open source system for implementing data centric security.
- It provides the basic services required to enable the definition, application,
- and enforcement of attribute based policies using the Trust Data Format (TDF).
- TDF is an open standard that enables you to cryptographically bind
- attribute based access control (ABAC) policy to a data object so that
- the policy travels with the data wherever it goes.
-
-
- OpenTDF builds upon a decade of experience at Virtru
- protecting data objects at scale using the Trusted Data Format
- for organizations of all sizes and across all industries.
-
-
-
-
-
- Today's cybersecurity landscape is increasingly adopting and requiring Zero Trust models and frameworks.
- Zero Trust operates on the principle of "never trust, always verify,"
- ensuring that every access request is authenticated, authorized, and encrypted,
- regardless of its origin. OpenTDF implements this model by providing an open-source framework, specification, and set of services
- that prioritizes the protection and integrity of data at every stage.
-
-
- By integrating OpenTDF’s data security features with a Zero Trust architecture,
- organizations can enforce strict access controls, ensure data is continuously monitored,
- and maintain comprehensive visibility into data interactions. This synergy not only
- minimizes the risk of data breaches but also fosters a secure environment where data
- can be shared and utilized with confidence. Together, Zero Trust and OpenTDF empower businesses
- to uphold the highest standards of data security in an interconnected world.
-
-
-
-
- In 2023, the OpenTDF team undertook a significant re-architecture
- of the OpenTDF platform to enhance its extensibility and interoperability,
- responding to the evolving needs of our diverse user base and the dynamic cybersecurity landscape.
- See our {" "}Github Organization Page to navigate the new repositories.
-
-
- This comprehensive overhaul involved simplifying core service components,
- adopting standardized policy schemas, and improving platform APIs and SDKs both in
- developer experience and in capability. By focusing on extensibility, we have enabled
- developers to customize and extend OpenTDF’s functionalities to suit specific use cases,
- fostering innovation and adaptability. As we continue to advance, our focus remains on empowering the community with a secure, adaptable,
- and interoperable platform that meets the highest standards of data protection and fosters collaborative innovation.
-
-
- Through the sponsorship of Virtru and its partners, the OpenTDF project has been
- meeting the needs of customers across industries and use cases. Check out{" "}
-
- Virtru Data Security Platform
- {" "}
- for more.
-
-
-
-
- {/* */}
-
-
-
-
-
- Virtru, the sponsor of the OpenTDF developer community, would love to hear from you!
-
-
- We're developers, too, and as we mature the project, we're curious what you're building, and what kind of problems you may be encountering or are trying to solve.
-
-
- You can provide anonymous feedback (name, email, and company are not required fields on this form), or share your contact information for access to curated resources, updates, and if you request a response.
-
-
+
+
+
+
+
+
+
);
diff --git a/static/img/OpenTDF-Logo-White.png b/static/img/OpenTDF-Logo-White.png
new file mode 100644
index 00000000..97f87fe9
Binary files /dev/null and b/static/img/OpenTDF-Logo-White.png differ
diff --git a/static/img/favicon.svg b/static/img/favicon.svg
new file mode 100644
index 00000000..e6906bc5
--- /dev/null
+++ b/static/img/favicon.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/static/img/filecontents.svg b/static/img/filecontents.svg
deleted file mode 100644
index 6059a070..00000000
--- a/static/img/filecontents.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
- my_document.ext.tdf (Zip)
-
-
-
- manifest.json
-
-
-
- 0.payload (Encrypted)
-
\ No newline at end of file
diff --git a/static/img/opentdf-social.png b/static/img/opentdf-social.png
index 91d45cf1..f1b5fb52 100644
Binary files a/static/img/opentdf-social.png and b/static/img/opentdf-social.png differ