diff --git a/.agents/plans/happy-teal-tree-platform-signing-page-branding.md b/.agents/plans/happy-teal-tree-platform-signing-page-branding.md
new file mode 100644
index 000000000..efe3c75b7
--- /dev/null
+++ b/.agents/plans/happy-teal-tree-platform-signing-page-branding.md
@@ -0,0 +1,138 @@
+---
+date: 2026-05-06
+title: Platform Signing Page Branding
+---
+
+## What
+
+Platform-plan organisations (and their teams) can customise the **non-embed
+signing pages** (`/sign/:token`, `/d/:token`, and the sibling
+complete/expired/rejected/waiting pages) with:
+
+- Six brand colour tokens (background, foreground, primary, primary-foreground,
+ border, ring) plus a border-radius length.
+- A free-text custom CSS block (up to 256 KB).
+
+Settings live on `OrganisationGlobalSettings` and `TeamGlobalSettings`. Teams
+inherit from the org via the existing `brandingEnabled === null` mechanism.
+
+## Why
+
+- Embed customers already have white-label CSS; Platform customers want the
+ same coverage on direct signing URLs that they iframe or link to.
+- Persisting on org/team (not per envelope) means it's set-and-forget.
+- Sanitising **on save** lets us inline the verbatim string at SSR — no
+ per-render parsing cost, no `` block; it
+is regex-validated (`CSS_LENGTH_REGEX`) at both the Zod schema and the
+runtime `toNativeCssVars` call. Belt-and-braces against schema drift.
+
+### Render (`apps/remix/app/components/general/recipient-branding.tsx`)
+
+Each recipient loader calls `loadRecipientBrandingByTeamId` and threads the
+payload through to ``, which emits a single
+nonce-attributed `;
+};
diff --git a/apps/remix/app/root.tsx b/apps/remix/app/root.tsx
index 0c6341cad..096b1504e 100644
--- a/apps/remix/app/root.tsx
+++ b/apps/remix/app/root.tsx
@@ -17,6 +17,7 @@ import {
Scripts,
ScrollRestoration,
useLoaderData,
+ useMatches,
} from 'react-router';
import { PreventFlashOnWrongTheme, ThemeProvider, useTheme } from 'remix-themes';
@@ -110,6 +111,13 @@ export function LayoutContent({ children }: { children: React.ReactNode }) {
const [theme] = useTheme();
+ // Recipient routes (signing pages) put `documenso-branded` on so the
+ // inside a string value', () => {
+ const result = sanitizeBrandingCss('.x { font-family: "
"; }');
+
+ expect(result.css.toLowerCase()).not.toContain(' inside a CSS comment', () => {
+ const result = sanitizeBrandingCss('.x { color: red; /* */ }');
+
+ expect(result.css.toLowerCase()).not.toContain(' inside an at-rule params block', () => {
+ const result = sanitizeBrandingCss(
+ '@media screen and (foo: bar) { .x { color: red; } }',
+ );
+
+ expect(result.css.toLowerCase()).not.toContain(' in a value', () => {
+ const result = sanitizeBrandingCss('.x { font-family: "foo"; }');
+
+ expect(result.css.toLowerCase()).not.toContain(' in an attribute selector value', () => {
+ const result = sanitizeBrandingCss('[data-x=""] { color: red; }');
+
+ expect(result.css.toLowerCase()).not.toContain(' {
+ // `"; }');
+
+ // The output keeps the literal `` end tag's `<` for the same reason it'd escape ``.
+ expect(result.css).toContain('