diff --git a/site/app/category/[slug]/page.tsx b/site/app/category/[slug]/page.tsx
index 3df6486..f94dad2 100644
--- a/site/app/category/[slug]/page.tsx
+++ b/site/app/category/[slug]/page.tsx
@@ -1,7 +1,8 @@
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
+import HeygrcCard from '@/components/HeygrcCard';
import ListPage from '@/components/ListPage';
-import { categories, getCategory, skillsInCategory } from '@/lib/content';
+import { categories, getCategory, skillsInCategory, HEYGRC_LINE, HEYGRC_LIST_PATHS } from '@/lib/content';
import { firstSentence } from '@/lib/text';
import { pageMeta } from '@/lib/seo';
@@ -35,6 +36,7 @@ export default async function CategoryPage({ params }: Props) {
crumbs={[{ name: 'Skills', href: '/skills/' }, { name: c.label, href: `/category/${c.slug}/` }]}
list={list}
path={`/category/${c.slug}/`}
+ after={HEYGRC_LIST_PATHS.includes(`/category/${c.slug}/`) ? : undefined}
/>
);
}
diff --git a/site/app/framework/[tag]/page.tsx b/site/app/framework/[tag]/page.tsx
index 29a4c65..1b49927 100644
--- a/site/app/framework/[tag]/page.tsx
+++ b/site/app/framework/[tag]/page.tsx
@@ -1,7 +1,8 @@
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
+import HeygrcCard from '@/components/HeygrcCard';
import ListPage from '@/components/ListPage';
-import { frameworks, getFramework, skillsWithFramework } from '@/lib/content';
+import { frameworks, getFramework, skillsWithFramework, HEYGRC_LINE, HEYGRC_LIST_PATHS } from '@/lib/content';
import { firstSentence } from '@/lib/text';
import { pageMeta } from '@/lib/seo';
@@ -40,6 +41,7 @@ export default async function FrameworkPage({ params }: Props) {
crumbs={[{ name: 'Skills', href: '/skills/' }, { name: f.label, href: `/framework/${f.tag}/` }]}
list={list}
path={`/framework/${f.tag}/`}
+ after={HEYGRC_LIST_PATHS.includes(`/framework/${f.tag}/`) ? : undefined}
/>
);
}
diff --git a/site/app/globals.css b/site/app/globals.css
index 72b63f7..c04d258 100644
--- a/site/app/globals.css
+++ b/site/app/globals.css
@@ -167,6 +167,13 @@ a.tag:hover { background: #e2e7ee; }
.card p { margin: 0; }
.card .quiet { font-size: 13px; color: var(--muted-2); margin-top: 10px; }
.actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 16px; }
+/* heyGRC, from the same team: one small touch of its violet, nothing more. */
+.heygrc { border-left: 4px solid #4D4CD7; }
+.heygrc p { color: var(--ink-2); font-size: 15px; }
+.heygrc .quiet { font-size: 13px; color: var(--muted-2); }
+.list-card { max-width: 720px; margin-top: 40px; }
+.heygrc-band { border-left: 4px solid #4D4CD7; padding-left: 28px; }
+.heygrc-band p.two-jobs { margin-top: 12px; font-size: 15px; color: var(--muted-2); }
.picker .tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 12px; }
.tab { border: 1px solid var(--line); background: transparent; border-radius: 999px; padding: 6px 14px; font-size: 13.5px; color: var(--muted); }
@@ -268,6 +275,7 @@ a.tag:hover { background: #e2e7ee; }
.prose h2 { font-size: 23px; margin-top: 36px; }
.ft { margin-top: 88px; }
.ft .by { margin-left: 0; width: 100%; }
+ .heygrc-band { padding-left: 18px; }
.pills legend { float: none; width: 100%; margin-bottom: 2px; }
}
diff --git a/site/app/layout.tsx b/site/app/layout.tsx
index 63cba95..cf4c65d 100644
--- a/site/app/layout.tsx
+++ b/site/app/layout.tsx
@@ -2,7 +2,7 @@ import type { Metadata, Viewport } from 'next';
import { Inter_Tight, JetBrains_Mono } from 'next/font/google';
import Link from 'next/link';
import { Analytics } from '@vercel/analytics/next';
-import { COPILOT_FOOTER_URL, meta, SITE_URL } from '@/lib/content';
+import { COPILOT_FOOTER_URL, HEYGRC_URL, meta, SITE_URL } from '@/lib/content';
import { HOME_DESCRIPTION, HOME_TITLE, pageMeta } from '@/lib/seo';
import './globals.css';
@@ -70,6 +70,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
llms.txtby ISMS Copilot
+ Also from us: heyGRC, compliance in your pull requests
ISO, IEC, AICPA, SOC 2, PCI DSS, NIST and other framework names appear only as tags saying which framework a skill relates to; they belong to their owners. grcskills is not affiliated with, endorsed by or sponsored by any standards body or regulator, and the skills contain no text from the standards.
diff --git a/site/app/page.tsx b/site/app/page.tsx
index 45093ab..d83a830 100644
--- a/site/app/page.tsx
+++ b/site/app/page.tsx
@@ -4,7 +4,7 @@ import CopyButton from '@/components/CopyButton';
import JsonLd from '@/components/JsonLd';
import SearchBox from '@/components/SearchBox';
import SkillCard from '@/components/SkillCard';
-import { categories, featuredSkill, meta, SITE_URL, skills, toRow } from '@/lib/content';
+import { categories, featuredSkill, HEYGRC_LINE, HEYGRC_URL, meta, SITE_URL, skills, toRow } from '@/lib/content';
import { HOME_DESCRIPTION, HOME_H1, HOME_SUBLINE, HOME_TITLE, pageMeta } from '@/lib/seo';
export const metadata: Metadata = pageMeta({
@@ -51,6 +51,15 @@ export default function Home() {
{featured && }
+
+
+
After the skill: evidence on each pull request
+
{HEYGRC_LINE}
+
Also from us. ISMS Copilot answers framework questions while a skill runs. heyGRC reviews each pull request after it, against the frameworks you pick.
ISMS Copilot answers framework questions for this skill. Optional; the skill works without it.
+ {heygrc && }
+
{author && (
Author
diff --git a/site/components/HeygrcCard.tsx b/site/components/HeygrcCard.tsx
new file mode 100644
index 0000000..9872d91
--- /dev/null
+++ b/site/components/HeygrcCard.tsx
@@ -0,0 +1,17 @@
+import { HEYGRC_URL } from '@/lib/content';
+
+// The heyGRC card: under the Install card on the skill pages in HEYGRC_SKILLS,
+// and below the list on the pages in HEYGRC_LIST_PATHS. Text only: the CSP
+// allows images from this origin alone.
+export default function HeygrcCard({ body, className = '' }: { body: string; className?: string }) {
+ return (
+
+
)}
+ {after}
getSkill(FEATURED_SLUG) ?? skills[0];
export const copilotUrl = (slug: string) => `https://www.ismscopilot.com/mcp?ref=grcskills-${slug}`;
export const COPILOT_FOOTER_URL = 'https://www.ismscopilot.com/?ref=grcskills-footer';
+
+// heyGRC, from the same team: reviews each pull request against the frameworks
+// the customer picks, after a skill has run. heyGRC measures by page path, so the link carries no query
+// string (founder decisions X3 and X4, 2026-10-20).
+export const HEYGRC_URL = 'https://heygrc.com/for/grc-teams';
+// The only skill pages that show the heyGRC card: the ones whose output lives
+// in pull requests. Any other skill page shows none.
+export const HEYGRC_SKILLS: Record = {
+ 'secure-development-policy-from-pipeline': 'Once you sign this policy off, heyGRC reviews each pull request against the frameworks you pick and cites the control behind each finding.',
+ 'change-management-evidence-sampling': 'Your sample shows how past changes went. heyGRC reviews each pull request as it opens and leaves its check and findings on it, so the next sample starts from records.',
+ 'policy-gap-review-against-practice': 'Once policy and practice match, look at the next changes too. heyGRC reviews each pull request and gives you the evidence, with the control behind each finding cited.',
+ 'grc-setup-review': 'Your review names what to fix. For the controls that live in code, heyGRC reviews each pull request and gives you the evidence.',
+};
+// List pages that show the same card, by path.
+export const HEYGRC_LIST_PATHS = ['/category/evidence/', '/category/policies/', '/framework/cra/'];
+export const HEYGRC_LINE = 'Your policy says every change is reviewed. heyGRC checks each pull request and gives you the evidence, with the control behind each finding cited.';
export const githubUrl = (repoPath: string) => `${meta.repoUrl}/blob/${meta.tag}/${repoPath}`;
export const githubFolderUrl = (repoPath: string) => `${meta.repoUrl}/tree/${meta.tag}/${repoPath.replace(/\/SKILL\.md$/, '')}`;
export const relatedTo = (s: Skill) => s.related.map(getSkill).filter((x): x is Skill => Boolean(x));
diff --git a/site/tests/built.test.mjs b/site/tests/built.test.mjs
index f24e242..7e93821 100644
--- a/site/tests/built.test.mjs
+++ b/site/tests/built.test.mjs
@@ -239,3 +239,43 @@ test('vercel.json CSP: strict script-src, no unsafe-inline or unsafe-eval for sc
assert.equal(all.find((h) => h.key === 'X-Content-Type-Options').value, 'nosniff');
assert.equal(all.find((h) => h.key === 'Referrer-Policy').value, 'strict-origin-when-cross-origin');
});
+
+// heyGRC cross-promotion (founder decisions X3 and X4, 2026-10-20).
+const HEYGRC = 'https://heygrc.com/for/grc-teams';
+const HEYGRC_SKILLS = ['secure-development-policy-from-pipeline', 'change-management-evidence-sampling', 'policy-gap-review-against-practice', 'grc-setup-review'];
+const HEYGRC_LISTS = ['category/evidence/index.html', 'category/policies/index.html', 'framework/cra/index.html'];
+const heygrcCard = (html) => / {
+ const home = read('index.html');
+ const band = home.match(//)?.[0];
+ assert.ok(band, 'home band');
+ assert.ok(band.includes(`href="${HEYGRC}"`), 'home band links to heyGRC');
+ assert.ok(home.indexOf('id="back"') < home.indexOf('heygrc-band') && home.indexOf('heygrc-band') < home.indexOf('id="find"'), 'band sits after What comes back');
+ for (const rel of pages()) {
+ const ft = read(rel).match(/