import { SITE_URL, absoluteUrl } from '@/lib/site'; /** * The site's author entity. * * First name only, by choice — see /about. That makes this a weaker search * signal than a fully identified author would be, which is why the About page * carries a substantial methodology section: the credibility has to come from * stated provenance rather than from a corroborable identity. * * Everything that needs an author — the About page, every post byline — * references this one shape, so search engines resolve them all to one entity. */ export function personJsonLd() { return { '@type': 'Person', '@id': `${SITE_URL}/about#tudor`, name: 'Tudor', url: absoluteUrl('/about'), image: absoluteUrl('/brand/tudor.jpg'), description: 'Parent in south-west London who built schoolcompare while looking for a primary school.', } as const; } export function organizationJsonLd() { return { '@type': 'Organization', '@id': `${SITE_URL}#organization`, name: 'schoolcompare', url: SITE_URL, logo: absoluteUrl('/icon-512.png'), } as const; } interface PostSummary { title: string; slug: string; excerpt: string; publishedAt: string; } /** * References the Person and Organization by @id rather than repeating them, so * search engines resolve every post and the About page to the one author * entity. Repeating the shape would declare several people with one name. */ export function blogPostingJsonLd(post: PostSummary) { return { '@type': 'BlogPosting', headline: post.title, description: post.excerpt, url: absoluteUrl(`/blog/${post.slug}`), datePublished: post.publishedAt, author: { '@id': `${SITE_URL}/about#tudor` }, publisher: { '@id': `${SITE_URL}#organization` }, } as const; } export function breadcrumbJsonLd(post: PostSummary) { return { '@type': 'BreadcrumbList', itemListElement: [ { '@type': 'ListItem', position: 1, name: 'Blog', item: absoluteUrl('/blog'), }, { '@type': 'ListItem', position: 2, name: post.title, item: absoluteUrl(`/blog/${post.slug}`), }, ], } as const; }