diff --git a/nextjs-app/__tests__/components/CompareAdmissions.test.tsx b/nextjs-app/__tests__/components/CompareAdmissions.test.tsx
index abc1273..5c288d2 100644
--- a/nextjs-app/__tests__/components/CompareAdmissions.test.tsx
+++ b/nextjs-app/__tests__/components/CompareAdmissions.test.tsx
@@ -104,7 +104,7 @@ describe('CompareAdmissions', () => {
render();
expect(
- screen.getByText(/Entry is by entrance test — the school is selective/),
+ screen.getByText(/Entry is by entrance test\. The school is selective/),
).toBeInTheDocument();
expect(screen.queryByText(/non-faith primaries/)).toBeNull();
});
diff --git a/nextjs-app/__tests__/components/CutoffMapPanel.test.tsx b/nextjs-app/__tests__/components/CutoffMapPanel.test.tsx
index e37759f..cd4d40f 100644
--- a/nextjs-app/__tests__/components/CutoffMapPanel.test.tsx
+++ b/nextjs-app/__tests__/components/CutoffMapPanel.test.tsx
@@ -102,7 +102,7 @@ describe('CutoffMapPanel', () => {
// Explanation is supporting text, not part of the bold verdict line.
expect(result.querySelector('[class*="cutoffCheckHeadline"]')!.textContent)
.not.toMatch(/measurement error/);
- expect(result).not.toHaveTextContent(/^\S+ away — inside/);
+ expect(result).not.toHaveTextContent(/^\S+ away, inside/);
});
it('surfaces a postcode the geocoder cannot find', async () => {
diff --git a/nextjs-app/__tests__/components/noEmDashCopy.test.ts b/nextjs-app/__tests__/components/noEmDashCopy.test.ts
new file mode 100644
index 0000000..7798fb0
--- /dev/null
+++ b/nextjs-app/__tests__/components/noEmDashCopy.test.ts
@@ -0,0 +1,72 @@
+import fs from 'fs';
+import path from 'path';
+import ts from 'typescript';
+
+/**
+ * Keeps the em dash out of public copy.
+ *
+ * Every visitor-facing sentence was rewritten to use a colon, comma, full stop
+ * or parentheses instead. A new one slips back in unnoticed, because it reads
+ * fine to whoever wrote it, so this walks the source with the TypeScript parser
+ * and checks every string literal, template chunk and JSX text node. Comments
+ * are not nodes the walk visits, so they stay free to use it.
+ *
+ * Two uses are allowed:
+ * - a lone dash standing in for a missing value in a table cell or stat slot,
+ * which is a data convention rather than prose;
+ * - the message of a thrown Error, which only a developer reads.
+ */
+
+const ROOT = path.join(__dirname, '..', '..');
+const SCANNED = ['app', 'components', 'lib'];
+const SKIPPED = [path.join(ROOT, 'app', '(payload)')];
+
+function sources(dir: string): string[] {
+ if (SKIPPED.includes(dir)) return [];
+ return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
+ const full = path.join(dir, entry.name);
+ if (entry.isDirectory()) return sources(full);
+ return /\.tsx?$/.test(entry.name) ? [full] : [];
+ });
+}
+
+function isErrorMessage(node: ts.Node): boolean {
+ for (let p = node.parent; p; p = p.parent) {
+ if (ts.isNewExpression(p) && /Error$/.test(p.expression.getText())) return true;
+ if (ts.isStatement(p)) return false;
+ }
+ return false;
+}
+
+const EMPTY_VALUE = /^(—|—)$/;
+
+function dashedCopy(file: string): string[] {
+ const text = fs.readFileSync(file, 'utf8');
+ const kind = file.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS;
+ const sf = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true, kind);
+ const found: string[] = [];
+ const visit = (node: ts.Node) => {
+ if (
+ ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node) || ts.isJsxText(node)
+ || ts.isTemplateHead(node) || ts.isTemplateMiddle(node) || ts.isTemplateTail(node)
+ ) {
+ const copy = node.getText().replace(/\s+/g, ' ').trim();
+ const bare = node.text.trim();
+ if (/—|—/.test(copy) && !EMPTY_VALUE.test(bare) && !isErrorMessage(node)) {
+ const { line } = sf.getLineAndCharacterOfPosition(node.getStart());
+ found.push(`${path.relative(ROOT, file)}:${line + 1} ${copy}`);
+ }
+ }
+ ts.forEachChild(node, visit);
+ };
+ visit(sf);
+ return found;
+}
+
+describe('public copy', () => {
+ it('uses no em dashes', () => {
+ const files = SCANNED.flatMap((dir) => sources(path.join(ROOT, dir)));
+ expect(files.length).toBeGreaterThan(50);
+ expect(files.flatMap(dashedCopy)).toEqual([]);
+ });
+});
diff --git a/nextjs-app/__tests__/components/schoolDetail.characterization.test.tsx b/nextjs-app/__tests__/components/schoolDetail.characterization.test.tsx
index 944c086..c8470f8 100644
--- a/nextjs-app/__tests__/components/schoolDetail.characterization.test.tsx
+++ b/nextjs-app/__tests__/components/schoolDetail.characterization.test.tsx
@@ -108,7 +108,7 @@ describe('all-through schools', () => {
it('labels the KS2 block explicitly', () => {
renderSchoolDetail(allThroughFixture);
- expect(screen.getAllByText(/Primary — KS2 SATs/).length).toBeGreaterThan(0);
+ expect(screen.getAllByText(/Primary: KS2 SATs/).length).toBeGreaterThan(0);
});
});
diff --git a/nextjs-app/__tests__/lib/lastDistanceOffered.test.ts b/nextjs-app/__tests__/lib/lastDistanceOffered.test.ts
index 2f7e640..c148136 100644
--- a/nextjs-app/__tests__/lib/lastDistanceOffered.test.ts
+++ b/nextjs-app/__tests__/lib/lastDistanceOffered.test.ts
@@ -122,7 +122,7 @@ describe('compareToCutoff', () => {
it('reads back the reported case in one unit', () => {
expect(compareToCutoff(69, 270, 2026).headline)
- .toBe('0.04 miles away — inside the September 2026 cut-off of 0.17 miles.');
+ .toBe('0.04 miles away, inside the September 2026 cut-off of 0.17 miles.');
});
it('calls a clearly nearer home inside, and names the year', () => {
diff --git a/nextjs-app/app/(frontend)/compare/page.tsx b/nextjs-app/app/(frontend)/compare/page.tsx
index 9c7233e..ac99ff5 100644
--- a/nextjs-app/app/(frontend)/compare/page.tsx
+++ b/nextjs-app/app/(frontend)/compare/page.tsx
@@ -33,7 +33,7 @@ export async function generateMetadata(
// Deliberately not the homepage's phrase. Two pages chasing "compare
// schools" is how a site competes with itself; this one takes the tool
// phrasing instead.
- title: 'School Comparison Tool — Up to Five at Once | schoolcompare',
+ title: 'School Comparison Tool: Up to Five Schools at Once | schoolcompare',
description:
'Put up to five English schools in one table: SATs and GCSE results against the England average, Ofsted grades, and the distance places were offered.',
keywords:
diff --git a/nextjs-app/app/(frontend)/layout.tsx b/nextjs-app/app/(frontend)/layout.tsx
index 7507073..faee140 100644
--- a/nextjs-app/app/(frontend)/layout.tsx
+++ b/nextjs-app/app/(frontend)/layout.tsx
@@ -54,7 +54,7 @@ export const metadata: Metadata = {
template: '%s | schoolcompare',
},
description:
- 'Put five English schools on one screen — SATs, GCSE results, Ofsted grades, and how close you had to live to get a place. Free, no sign-up.',
+ 'Put five English schools on one screen: SATs, GCSE results, Ofsted grades, and how close you had to live to get a place. Free, no sign-up.',
keywords: 'school comparison, KS2 results, KS4 results, primary school, secondary school, England schools, SATs results, GCSE results',
authors: [{ name: 'schoolcompare' }],
manifest: '/manifest.json',
@@ -69,7 +69,7 @@ export const metadata: Metadata = {
type: 'website',
title: 'Compare Schools Side by Side | schoolcompare',
description:
- 'Put five English schools on one screen — SATs, GCSE results, Ofsted grades, and how close you had to live to get a place.',
+ 'Put five English schools on one screen: SATs, GCSE results, Ofsted grades, and how close you had to live to get a place.',
/*
* Declared, not inherited.
*
@@ -93,7 +93,7 @@ export const metadata: Metadata = {
card: 'summary_large_image',
title: 'Compare Schools Side by Side | schoolcompare',
description:
- 'Put five English schools on one screen — SATs, GCSE results, Ofsted grades, and how close you had to live to get a place.',
+ 'Put five English schools on one screen: SATs, GCSE results, Ofsted grades, and how close you had to live to get a place.',
// The card is summary_large_image; claiming that and supplying no image
// is worse than claiming a summary card.
images: ['/opengraph-image'],
diff --git a/nextjs-app/app/(frontend)/page.tsx b/nextjs-app/app/(frontend)/page.tsx
index f3fd365..378ae99 100644
--- a/nextjs-app/app/(frontend)/page.tsx
+++ b/nextjs-app/app/(frontend)/page.tsx
@@ -49,7 +49,7 @@ export const metadata: Metadata = {
*/
title: { absolute: 'Compare Schools Side by Side | schoolcompare' },
description:
- 'Put five English schools on one screen — SATs, GCSE results, Ofsted grades, and how close you had to live to get a place. Free, no sign-up.',
+ 'Put five English schools on one screen: SATs, GCSE results, Ofsted grades, and how close you had to live to get a place. Free, no sign-up.',
// This page reads eleven search params. They filter a result set; they do
// not make a new document. Collapsing every combination onto "/" stops the
// homepage competing with itself for its own head terms.
diff --git a/nextjs-app/app/(frontend)/schools/[place]/page.tsx b/nextjs-app/app/(frontend)/schools/[place]/page.tsx
index 08a6727..8ebab89 100644
--- a/nextjs-app/app/(frontend)/schools/[place]/page.tsx
+++ b/nextjs-app/app/(frontend)/schools/[place]/page.tsx
@@ -56,7 +56,7 @@ export async function generateMetadata({ params }: Props): Promise {
// absolute: the root layout's template appends '| schoolcompare' to a
// plain string, and this title already carries it. Without this every
// place title read '... | schoolcompare | schoolcompare'.
- title: { absolute: `Schools in ${name} — Compare ${count} Schools | schoolcompare` },
+ title: { absolute: `Schools in ${name}: Compare ${count} Schools | schoolcompare` },
description:
`Every school in ${name}, with SATs and GCSE results, Ofsted grades, the local `
+ `average against England, and how close you had to live to get a place.`,
diff --git a/nextjs-app/app/(frontend)/schools/authority/[la]/[phase]/page.tsx b/nextjs-app/app/(frontend)/schools/authority/[la]/[phase]/page.tsx
index 21cc736..53fed3b 100644
--- a/nextjs-app/app/(frontend)/schools/authority/[la]/[phase]/page.tsx
+++ b/nextjs-app/app/(frontend)/schools/authority/[la]/[phase]/page.tsx
@@ -41,7 +41,7 @@ export async function generateMetadata({ params }: Props): Promise {
// "Local Authority" stays in the title for the same reason it is on the
// bare authority page: 67 town names collide with an authority name, and
// a reader landing on both needs to know which set each covers.
- title: { absolute: `${word} Schools in ${name} — Local Authority | schoolcompare` },
+ title: { absolute: `${word} Schools in ${name} (Local Authority) | schoolcompare` },
description:
`Every ${phase} school in the ${name} local authority, with results, Ofsted `
+ `grades and the authority average against England.`,
diff --git a/nextjs-app/app/(frontend)/schools/authority/[la]/page.tsx b/nextjs-app/app/(frontend)/schools/authority/[la]/page.tsx
index b907b88..4af4d4d 100644
--- a/nextjs-app/app/(frontend)/schools/authority/[la]/page.tsx
+++ b/nextjs-app/app/(frontend)/schools/authority/[la]/page.tsx
@@ -43,7 +43,7 @@ export async function generateMetadata({ params }: Props): Promise {
const { name, count } = detail.place;
return {
- title: { absolute: `Schools in ${name} — Local Authority | schoolcompare` },
+ title: { absolute: `Schools in ${name} (Local Authority) | schoolcompare` },
description:
`All ${count} schools in the ${name} local authority, with SATs and GCSE results, `
+ `Ofsted grades and the authority average against England.`,
diff --git a/nextjs-app/app/opengraph-image.tsx b/nextjs-app/app/opengraph-image.tsx
index a62de42..20a6b79 100644
--- a/nextjs-app/app/opengraph-image.tsx
+++ b/nextjs-app/app/opengraph-image.tsx
@@ -11,7 +11,7 @@ import { ImageResponse } from 'next/og';
* and a suggestion of the illustration's rolling landscape along the bottom.
* It states the promise rather than decorating it.
*/
-export const alt = 'schoolcompare — find the school where your child will flourish';
+export const alt = 'schoolcompare: find the school where your child will flourish';
export const size = { width: 1200, height: 630 };
export const contentType = 'image/png';
diff --git a/nextjs-app/components/AdmissionsView.tsx b/nextjs-app/components/AdmissionsView.tsx
index cc01e1b..6fa08d2 100644
--- a/nextjs-app/components/AdmissionsView.tsx
+++ b/nextjs-app/components/AdmissionsView.tsx
@@ -64,11 +64,11 @@ interface Step {
const SECONDARY_STEPS: Step[] = [
{
title: 'Check entry criteria',
- body: 'Look at each school\'s admissions policy — catchment areas, faith criteria, sibling priority, and aptitude tests vary widely.',
+ body: 'Look at each school\'s admissions policy. Catchment areas, faith criteria, sibling priority and aptitude tests vary widely.',
callouts: [
{
title: 'Selective schools register earlier',
- body: 'Grammar and other selective schools need separate entrance-test registration months earlier — often by September, well before the 31 October deadline.',
+ body: 'Grammar and other selective schools need separate entrance-test registration months earlier, often by September and well before the 31 October deadline.',
},
{
title: 'Faith schools may need an extra form',
@@ -86,26 +86,26 @@ const SECONDARY_STEPS: Step[] = [
{
date: '31 October',
title: 'Application deadline',
- body: 'Submit your ranked list of schools — most councils allow three to six preferences (London boroughs typically six). Councils treat all preferences equally, so list schools in the genuine order you want them, not strategically.',
+ body: 'Submit your ranked list of schools. Most councils allow three to six preferences (London boroughs typically six). Councils treat all preferences equally, so list schools in the genuine order you want them, not strategically.',
highlight: 'deadline',
tool: { label: 'Compare performance to order your preferences', href: '/rankings' },
},
{
date: '1 March',
title: 'National Offer Day',
- body: 'Your allocated school appears on the council\'s online admissions portal and is sent by email. Release times are set by each council — often from late afternoon, though some publish overnight — so check your council\'s page rather than refreshing at midnight.',
+ body: 'Your allocated school appears on the council\'s online admissions portal and is sent by email. Release times are set by each council (often from late afternoon, though some publish overnight), so check your council\'s page rather than refreshing at midnight.',
highlight: 'offer',
tool: { label: 'Look up your allocated school', href: '/' },
},
{
date: '~15 March',
title: 'Accept or decline',
- body: 'Respond by the deadline your council gives — typically around 15 March. Accepting does not prevent you from keeping a place on a waiting list for a preferred school.',
+ body: 'Respond by the deadline your council gives, typically around 15 March. Accepting does not prevent you from keeping a place on a waiting list for a preferred school.',
tool: { label: 'Weigh your offer against your other choices', href: '/compare' },
},
{
title: 'Appeals',
- body: 'If unsuccessful, you can appeal within 20 school days of the refusal letter. Secondary appeals consider whether prejudice to the school outweighs your case — success rates vary.',
+ body: 'If unsuccessful, you can appeal within 20 school days of the refusal letter. Secondary appeals consider whether prejudice to the school outweighs your case, and success rates vary.',
tool: { label: 'Gather performance evidence for your case', href: '/rankings' },
},
];
@@ -163,7 +163,7 @@ interface Tip {
const TIPS: Tip[] = [
{
heading: 'Equal preference rule',
- body: 'Councils consider every school you list before making any offer, then give you the highest-ranked one you qualify for. Your order only decides which qualifying school you get — you can\'t game it, so list schools in the order you genuinely want them.',
+ body: 'Councils consider every school you list before making any offer, then give you the highest-ranked one you qualify for. Your order only decides which qualifying school you get. You can\'t game it, so list schools in the order you genuinely want them.',
},
{
heading: 'Late applications go to the back',
@@ -259,7 +259,7 @@ export function AdmissionsView() {
School Admissions Guide
- Everything parents need to know about applying for a school place in England — from opening dates to National Offer Day, with live countdowns to every key milestone.
+ Everything parents need to know about applying for a school place in England, from opening dates to National Offer Day, with live countdowns to every key milestone.
diff --git a/nextjs-app/components/ComparisonChart.tsx b/nextjs-app/components/ComparisonChart.tsx
index cd8af93..847ee17 100644
--- a/nextjs-app/components/ComparisonChart.tsx
+++ b/nextjs-app/components/ComparisonChart.tsx
@@ -258,15 +258,15 @@ export function ComparisonChart({ comparisonData, schools, metric, metricLabel,
// published by DfE but aren't in our dataset yet.
School-level GCSE figures for 2019/20 and 2020/21 weren't published (COVID
- grading), and more recent years aren't in our dataset yet where lines break — the
- England average is shown where available.
+ grading), and more recent years aren't in our dataset yet. Where lines break, the
+ England average is shown if available.
) : (
!isSecondary &&
built.showUnpublished202122Note && (
No national tests were held in 2019/20 and 2020/21 (COVID), and DfE didn't publish
- school-level figures for 2021/22 — the England average is shown for that year.
+ school-level figures for 2021/22. The England average is shown for that year.
)
)}
diff --git a/nextjs-app/components/ComparisonView.tsx b/nextjs-app/components/ComparisonView.tsx
index 3437d69..9b23799 100644
--- a/nextjs-app/components/ComparisonView.tsx
+++ b/nextjs-app/components/ComparisonView.tsx
@@ -263,7 +263,7 @@ export function ComparisonView({
Compare Schools
- Add schools to your comparison basket to see them side by side — inspection results,
+ Add schools to your comparison basket to see them side by side: inspection results,
academics, admissions and community.
@@ -300,8 +300,8 @@ export function ComparisonView({
Compare Schools
- {selectedSchools.length} school{selectedSchools.length !== 1 ? 's' : ''} side by side
- — each number anchored against the England average so you can tell at a glance
+ {selectedSchools.length} school{selectedSchools.length !== 1 ? 's' : ''} side by side,
+ each number anchored against the England average so you can tell at a glance
what's typical and what stands out.
diff --git a/nextjs-app/components/EditorialSection.tsx b/nextjs-app/components/EditorialSection.tsx
index 8f71b78..18209c9 100644
--- a/nextjs-app/components/EditorialSection.tsx
+++ b/nextjs-app/components/EditorialSection.tsx
@@ -47,7 +47,7 @@ export function EditorialSection({
School performance data in England is rich but fragmented. The Department for Education and Ofsted
publish Key Stage 2 SATs, GCSE attainment, inspection outcomes, progress scores, admissions figures
- and demographics — each in its own table, each with its own jargon.
+ and demographics, each in its own table with its own jargon.
schoolcompare brings it all into one place. Every school page shows performance against the national
diff --git a/nextjs-app/components/FilterBar.tsx b/nextjs-app/components/FilterBar.tsx
index 74e3eb4..b8d87c4 100644
--- a/nextjs-app/components/FilterBar.tsx
+++ b/nextjs-app/components/FilterBar.tsx
@@ -323,7 +323,7 @@ export function FilterBar({
{isHero && (
<>
- Search by school name — or use your{" "}
+ Search by school name, or use your{" "}
postcode for the nearest schools.
{onNearMe && (
diff --git a/nextjs-app/components/HomeView.tsx b/nextjs-app/components/HomeView.tsx
index 66632f4..d0f16c8 100644
--- a/nextjs-app/components/HomeView.tsx
+++ b/nextjs-app/components/HomeView.tsx
@@ -185,7 +185,7 @@ const VALUE_PROPS: ValueProp[] = [
icon: ,
tintClass: styles.propIconContext,
title: 'Beyond the numbers',
- body: 'SEN support, pupil premium and attendance — not just results.',
+ body: 'Not just results: SEN support, pupil premium and attendance too.',
},
{
icon: ,
@@ -478,8 +478,8 @@ export function HomeView({ initialSchools, filters, totalSchools, howItWorks, ed
every line above the fold costs. */}
{' '}Key Stage 2 SATs, GCSE results, Ofsted grades, progress scores
- and admissions data{coverageLabel && <> for {coverageLabel} schools>} — side by
- side, in one place.
+ and admissions data{coverageLabel && <> for {coverageLabel} schools>}, side by
+ side in one place.
diff --git a/nextjs-app/components/HowItWorksSection.tsx b/nextjs-app/components/HowItWorksSection.tsx
index 4e60563..53f0661 100644
--- a/nextjs-app/components/HowItWorksSection.tsx
+++ b/nextjs-app/components/HowItWorksSection.tsx
@@ -47,7 +47,7 @@ export function HowItWorksSection() {
Every school page
What you'll see on every school
-
Primary or secondary — the page adapts to the phase
+
The page adapts to primary or secondary
{/* Card 1 — Performance */}
@@ -123,7 +123,7 @@ export function HowItWorksSection() {
Judgement
Ofsted at a glance
-
Since November 2025, Ofsted rates each area of school life on a five-point scale rather than one overall word. We show every area and the inspection date — and, for schools not yet reinspected, the older overall grade they still carry.
+
Since November 2025, Ofsted rates each area of school life on a five-point scale rather than one overall word. We show every area, the inspection date and, for schools not yet reinspected, the older overall grade they still carry.
@@ -152,7 +152,7 @@ export function HowItWorksSection() {
{/* One verb for one feature. The site previously called this
"compare" in the nav, "shortlist" in the footer and "pin" here,
which reads as three separate things it does not have. */}
- Compare up to five schools and every metric aligns in the same columns — works for primary and secondary alike.
+ Compare up to five schools and every metric aligns in the same columns. It works for primary and secondary alike.
diff --git a/nextjs-app/components/PerformanceChart.tsx b/nextjs-app/components/PerformanceChart.tsx
index 9e43b34..f7b4690 100644
--- a/nextjs-app/components/PerformanceChart.tsx
+++ b/nextjs-app/components/PerformanceChart.tsx
@@ -114,7 +114,7 @@ export function PerformanceChart({
const delta = latest.rwm_expected_pct! - prev.rwm_expected_pct!;
const arrow = delta > 1 ? '↑' : delta < -1 ? '↓' : '→';
if (best.year === latest.year) {
- return `${arrow} Best year on record — ${latestPct}% met the expected standard in Reading, Writing & Maths`;
+ return `${arrow} Best year on record: ${latestPct}% met the expected standard in Reading, Writing & Maths`;
}
return `${arrow} Reading, Writing & Maths peaked at ${bestPct}% (${formatAcademicYear(best.year)}), currently ${latestPct}%`;
})();
@@ -451,15 +451,15 @@ export function PerformanceChart({
{hasGap && (
{isSecondary
- ? "School-level GCSE figures for 2019/20 and 2020/21 weren't published (COVID grading) where the line breaks — the England average is shown where available."
- : "No national tests were held in 2019/20 and 2020/21 (COVID), and DfE didn't publish school-level figures for 2021/22 — the England average is shown for those years where available."}
+ ? "School-level GCSE figures for 2019/20 and 2020/21 weren't published (COVID grading). Where the line breaks, the England average is shown if available."
+ : "No national tests were held in 2019/20 and 2020/21 (COVID), and DfE didn't publish school-level figures for 2021/22. Where available, the England average is shown for those years."}
)}
{/* Desktop-only hint about toggling progress in the legend */}
{!isSecondary && (
- Progress scores (Reading, Writing, Maths) are hidden by default — click them in the legend to show.
+ Progress scores (Reading, Writing, Maths) are hidden by default. Click them in the legend to show them.
)}
diff --git a/nextjs-app/components/RankingsView.tsx b/nextjs-app/components/RankingsView.tsx
index dbf7949..e6e6d9e 100644
--- a/nextjs-app/components/RankingsView.tsx
+++ b/nextjs-app/components/RankingsView.tsx
@@ -119,7 +119,7 @@ export function RankingsView({
School Rankings
Schools ranked by {metricLabel.toLowerCase()}
- {!selectedArea && rankings.length > 0 && — showing top {rankings.length}}
+ {!selectedArea && rankings.length > 0 && (showing top {rankings.length})}
@@ -145,7 +145,7 @@ export function RankingsView({
{isProgressScore && (
Progress scores: 0 = national average. Positive = above average.
- {isPrimary && ' KS2 progress isn’t published for 2023/24 or 2024/25 (there’s no key stage 1 baseline) — pick an earlier year to rank by it.'}
+ {isPrimary && ' KS2 progress isn’t published for 2023/24 or 2024/25 because there’s no key stage 1 baseline. Pick an earlier year to rank by it.'}
)}
diff --git a/nextjs-app/components/SpecialSchoolNote.tsx b/nextjs-app/components/SpecialSchoolNote.tsx
index f737328..cb5ca96 100644
--- a/nextjs-app/components/SpecialSchoolNote.tsx
+++ b/nextjs-app/components/SpecialSchoolNote.tsx
@@ -33,14 +33,14 @@ export function SpecialSchoolNote({ school }: { school: { school_type?: string |
<>
This is a special school. Its pupils have special educational needs and
work towards individual targets. They sit the same national assessments, but very few
- reach the mainstream “expected standard” these measures report — so a
+ reach the mainstream “expected standard” these measures report, so a
comparison with the England average isn’t a meaningful guide to the school.
>
)}
{kind === 'pru' && (
<>
This is a pupil referral unit. It teaches pupils educated outside a
- mainstream school — for example after exclusion, or for medical or behavioural reasons.
+ mainstream school, for example after exclusion or for medical or behavioural reasons.
The mainstream “expected standard” and the England-average comparison
aren’t a meaningful guide to the school.
>
diff --git a/nextjs-app/components/compare/CompareAcademics.tsx b/nextjs-app/components/compare/CompareAcademics.tsx
index 1db15b4..d3a330c 100644
--- a/nextjs-app/components/compare/CompareAcademics.tsx
+++ b/nextjs-app/components/compare/CompareAcademics.tsx
@@ -26,7 +26,7 @@ interface StripSpec {
const TIER1_PRIMARY: StripSpec[] = [
{
- label: 'Reading, writing & maths — expected standard',
+ label: 'Reading, writing & maths: expected standard',
metric: 'rwm_expected_pct',
anchorKey: 'rwm_expected_pct',
tip: '% of Year 6 pupils reaching the expected standard in reading, writing and maths.',
@@ -49,18 +49,18 @@ const TIER1_PRIMARY: StripSpec[] = [
const TIER2_PRIMARY: StripSpec[] = [
{
- label: 'Grammar, punctuation & spelling — expected standard',
+ label: 'Grammar, punctuation & spelling: expected standard',
metric: 'gps_expected_pct',
anchorKey: 'gps_expected_pct',
},
{
- label: 'Science — expected standard (teacher-assessed)',
+ label: 'Science: expected standard (teacher-assessed)',
metric: 'science_expected_pct',
anchorKey: 'science_expected_pct',
- tip: 'Teacher-assessed, like writing — there has been no KS2 science test since 2009, so comparisons are indicative.',
+ tip: 'Teacher-assessed, like writing. There has been no KS2 science test since 2009, so comparisons are indicative.',
},
{
- label: 'Average scaled score — reading',
+ label: 'Average scaled score: reading',
metric: 'reading_avg_score',
anchorKey: 'reading_avg_score',
min: 100,
@@ -68,7 +68,7 @@ const TIER2_PRIMARY: StripSpec[] = [
unit: '',
},
{
- label: 'Average scaled score — maths',
+ label: 'Average scaled score: maths',
metric: 'maths_avg_score',
anchorKey: 'maths_avg_score',
min: 100,
@@ -76,7 +76,7 @@ const TIER2_PRIMARY: StripSpec[] = [
unit: '',
},
{
- label: 'Average scaled score — grammar, punctuation & spelling',
+ label: 'Average scaled score: grammar, punctuation & spelling',
metric: 'gps_avg_score',
anchorKey: 'gps_avg_score',
min: 100,
@@ -193,7 +193,7 @@ export function CompareAcademics({
return (
Attainment 8
@@ -230,7 +230,7 @@ export function CompareAcademics({
) : p8NotPublished[i] ? (
- Not published — this GCSE year group sat no KS2 tests (COVID), so DfE has no
+ Not published: this GCSE year group sat no KS2 tests (COVID), so DfE has no
baseline to measure progress from
) : (
@@ -308,7 +308,7 @@ export function CompareAcademics({
return (
{TIER1_PRIMARY.map((spec) => (
@@ -324,7 +324,7 @@ export function CompareAcademics({
))}
- More measures — grammar, punctuation & spelling, science, average scaled scores
+ More measures: grammar, punctuation & spelling, science, average scaled scores
{TIER2_PRIMARY.map((spec) => (
From the most recent admissions round ({entryLabel}). "First choice" means
- families who ranked the school top of their application form — officially a "first
- preference". Schools never see your ranking: places are decided only by the
+ families who ranked the school top of their application form (officially a "first
+ preference"). Schools never see your ranking: places are decided only by the
school's admission criteria, so listing a school lower down never hurts your chances.
- These are National Offer Day offers — waiting lists and appeals can change the final
+ These are National Offer Day offers. Waiting lists and appeals can change the final
intake.
>
}
>
{schools.map((school, i) => {
@@ -126,7 +126,7 @@ export function CompareAdmissions({
// Selective schools: the entrance test decides, whatever the
// offer percentage looks like — never the distance template.
text =
- 'Entry is by entrance test — the school is selective; distance and preference rank don’t decide places.';
+ 'Entry is by entrance test. The school is selective, so distance and preference rank don’t decide places.';
} else if (summary.firstPrefPct >= 100) {
text = `Every family who put ${school.school_name} first got a place.`;
} else if (summary.firstPrefPct >= 90) {
@@ -134,9 +134,9 @@ export function CompareAdmissions({
} else if (a?.oversubscribed) {
text = isSecondary
? faith
- ? 'More first-choice applications than places — check the school’s admission criteria (faith-based criteria may apply).'
- : 'More first-choice applications than places — check the school’s admission criteria (catchment or distance often decides, but criteria vary).'
- : 'More first-choice applications than places — check the school’s admission criteria (for most non-faith primaries, distance decides).';
+ ? 'More first-choice applications than places. Check the school’s admission criteria (faith-based criteria may apply).'
+ : 'More first-choice applications than places. Check the school’s admission criteria (catchment or distance often decides, but criteria vary).'
+ : 'More first-choice applications than places. Check the school’s admission criteria (for most non-faith primaries, distance decides).';
} else {
text = `${summary.firstPrefPct}% of first-choice families received an offer.`;
}
diff --git a/nextjs-app/components/compare/CompareAtAGlance.tsx b/nextjs-app/components/compare/CompareAtAGlance.tsx
index 147ef06..0cafadc 100644
--- a/nextjs-app/components/compare/CompareAtAGlance.tsx
+++ b/nextjs-app/components/compare/CompareAtAGlance.tsx
@@ -70,7 +70,7 @@ export function CompareAtAGlance({
: benchmarks?.primary?.median_pupils;
return (
-
+
{schools.map((school, i) => {
diff --git a/nextjs-app/components/compare/CompareCommunity.tsx b/nextjs-app/components/compare/CompareCommunity.tsx
index 25e7ea7..d33ba24 100644
--- a/nextjs-app/components/compare/CompareCommunity.tsx
+++ b/nextjs-app/components/compare/CompareCommunity.tsx
@@ -56,12 +56,12 @@ export function CompareCommunity({
how={
<>
The school's community, from the latest school census. State-school averages are
- computed from our dataset and shown for context — there's no “right”
+ computed from our dataset and shown for context. There's no “right”
number here.
{anyAllThrough && (
<>
{' '}
- For all-through schools these figures cover the whole school, all ages — not just
+ For all-through schools these figures cover the whole school and all ages, not just
the {isSecondary ? 'secondary' : 'primary'} phase.
>
)}
@@ -79,7 +79,7 @@ export function CompareCommunity({
if (pupils != null && capacity != null && capacity > 0) {
capNote =
pupils >= capacity
- ? `${capacity.toLocaleString('en-GB')} places — at or above capacity`
+ ? `${capacity.toLocaleString('en-GB')} places (at or above capacity)`
: `of ${capacity.toLocaleString('en-GB')} places (${Math.round((pupils / capacity) * 100)}% full)`;
}
return (
@@ -111,7 +111,7 @@ export function CompareCommunity({
{schools.map((school, i) => {
@@ -145,7 +145,7 @@ export function CompareCommunity({
{schools.map((school, i) => {
diff --git a/nextjs-app/components/compare/CompareOfsted.tsx b/nextjs-app/components/compare/CompareOfsted.tsx
index 0d0f86e..ad61ac4 100644
--- a/nextjs-app/components/compare/CompareOfsted.tsx
+++ b/nextjs-app/components/compare/CompareOfsted.tsx
@@ -47,7 +47,7 @@ function ResultCell({ display }: { display: OfstedDisplay }) {
return (
<>
Report card
- New-style inspection — no overall grade is given
+ New-style inspection: no overall grade is given
>
);
}
@@ -127,7 +127,7 @@ function JudgementDetailCell({
if (published.length === 0) {
return (
- We don't hold area-by-area detail for this inspection — see {schoolName}'s
+ We don't hold area-by-area detail for this inspection. See {schoolName}'s
Ofsted page for the full report.
);
diff --git a/nextjs-app/components/school/AddToCompareButton.tsx b/nextjs-app/components/school/AddToCompareButton.tsx
index dd519ea..51bdd81 100644
--- a/nextjs-app/components/school/AddToCompareButton.tsx
+++ b/nextjs-app/components/school/AddToCompareButton.tsx
@@ -39,7 +39,7 @@ export function AddToCompareButton({ school }: { school: NearbySchool }) {
aria-pressed={selected}
>
{selected ? '✓ Added to compare' : '+ Add to compare'}
- — {school.school_name}
+ : {school.school_name}
);
}
diff --git a/nextjs-app/components/school/AdmissionsSection.tsx b/nextjs-app/components/school/AdmissionsSection.tsx
index fcac056..ceabf73 100644
--- a/nextjs-app/components/school/AdmissionsSection.tsx
+++ b/nextjs-app/components/school/AdmissionsSection.tsx
@@ -175,7 +175,7 @@ export function AdmissionsSection({
<>{admissions.first_preference_applications} families put it first for >
)}
{admissions.places_offered != null && <>{admissions.places_offered} places>}
- {admissions.total_applications != null && ` — ${admissions.total_applications.toLocaleString()} applications in total`}.
+ {admissions.total_applications != null && ` (${admissions.total_applications.toLocaleString()} applications in total)`}.
>
);
diff --git a/nextjs-app/components/school/DestinationsView.tsx b/nextjs-app/components/school/DestinationsView.tsx
index f538d57..a0c1d07 100644
--- a/nextjs-app/components/school/DestinationsView.tsx
+++ b/nextjs-app/components/school/DestinationsView.tsx
@@ -189,7 +189,7 @@ export function DestinationsView({
data-group={card ?? 'none'}
className={`${styles.segment} ${styles[seg.category]} ${dimmed(card) ? styles.dim : ''}`}
style={{ width: `${seg.widthPct}%` }}
- title={`${CATEGORY_LABELS[seg.category]} — ${seg.labelPct}% (${seg.pupils} pupils)`}
+ title={`${CATEGORY_LABELS[seg.category]}: ${seg.labelPct}% (${seg.pupils} pupils)`}
>
{seg.widthPct >= 9 ? `${seg.labelPct}%` : ''}
@@ -254,7 +254,7 @@ export function DestinationsView({
Shares are rounded and may not add up to 100%. A pupil counted under a
school sixth form may have moved to a different school's sixth
- form — the published data records the type of place, not which one.
+ form. The published data records the type of place, not which one.
);
diff --git a/nextjs-app/components/school/GcseSection.tsx b/nextjs-app/components/school/GcseSection.tsx
index 41be86c..c9fb550 100644
--- a/nextjs-app/components/school/GcseSection.tsx
+++ b/nextjs-app/components/school/GcseSection.tsx
@@ -129,7 +129,7 @@ export function GcseSection({
that comparison isn't meaningful. */}
{!suppressComparison && latestResults.attainment_8_score != null && (
-
Attainment 8 — school vs national
+
Attainment 8: school vs national
-
Progress 8 — relative to national baseline (0)
+
Progress 8: relative to national baseline (0)
{(() => {
const p8 = latestResults.progress_8_score!;
const lo = latestResults.progress_8_lower_ci ?? p8;
diff --git a/nextjs-app/components/school/HistorySection.tsx b/nextjs-app/components/school/HistorySection.tsx
index 4f3c041..b05e543 100644
--- a/nextjs-app/components/school/HistorySection.tsx
+++ b/nextjs-app/components/school/HistorySection.tsx
@@ -37,7 +37,7 @@ export function HistorySection({
<>
{hasKS2Results && (
<>
-
Primary — KS2 SATs
+
Primary: KS2 SATs
- Secondary — GCSEs
+ Secondary: GCSEs
{ofsted.grade_source === 'ungraded_carried_forward'
- ? 'This overall grade is carried forward from an earlier inspection — Ofsted has since visited without issuing a new overall grade. From September 2024, Ofsted no longer makes an overall effectiveness judgement.'
+ ? 'This overall grade is carried forward from an earlier inspection. Ofsted has since visited without issuing a new overall grade. From September 2024, Ofsted no longer makes an overall effectiveness judgement.'
: isSecondary
? 'From September 2024, Ofsted no longer makes an overall effectiveness judgement in inspections.'
: 'From September 2024, Ofsted no longer makes an overall effectiveness judgement in inspections of state-funded schools.'}
{oeifAllSameGrade ? (
- Rated {OFSTED_LABELS[ofsted.overall_effectiveness!]} across all inspected areas — Quality of Teaching, Behaviour, Pupils' Development and Leadership.
+ Rated {OFSTED_LABELS[ofsted.overall_effectiveness!]} across all inspected areas: Quality of Teaching, Behaviour, Pupils' Development and Leadership.
) : (
diff --git a/nextjs-app/components/school/ResultsSection.tsx b/nextjs-app/components/school/ResultsSection.tsx
index 1937cd4..b041080 100644
--- a/nextjs-app/components/school/ResultsSection.tsx
+++ b/nextjs-app/components/school/ResultsSection.tsx
@@ -41,7 +41,7 @@ export function ResultsSection({
? 'GCSE results for Year 11 pupils.'
: 'End-of-primary-school tests taken by Year 6 pupils.')
: isAllThrough
- ? 'KS2 SATs (end of Year 6) and GCSE results (Year 11) — this school covers both. England averages shown for comparison.'
+ ? 'This school covers both KS2 SATs (end of Year 6) and GCSE results (Year 11). England averages shown for comparison.'
: isSecondary
? 'GCSE results for Year 11 pupils. England averages shown for comparison.'
: 'End-of-primary-school tests taken by Year 6 pupils. England averages shown for comparison.'}
@@ -56,7 +56,7 @@ export function ResultsSection({
{hasKS2Results && (
<>
{isAllThrough && (
-
Primary — KS2 SATs (Year 6)
+
Primary: KS2 SATs (Year 6)
)}
{latestResults.rwm_expected_pct !== null && (
@@ -208,7 +208,7 @@ export function ResultsSection({
{hasKS4Results && (
<>
{isAllThrough && (
-
Secondary — GCSEs (Year 11)
+
Secondary: GCSEs (Year 11)
)}
{latestResults.attainment_8_score !== null && (
diff --git a/nextjs-app/components/school/SchoolDetailShell.tsx b/nextjs-app/components/school/SchoolDetailShell.tsx
index ad81630..ff031ca 100644
--- a/nextjs-app/components/school/SchoolDetailShell.tsx
+++ b/nextjs-app/components/school/SchoolDetailShell.tsx
@@ -229,8 +229,8 @@ export function SchoolDetailShell({
{isProposedToClose(schoolInfo) && (
- ⚠ Proposed to close — this school is proposed for closure,
- check with the local authority before applying.
+ ⚠ Proposed to close. Check with the local authority before
+ applying.
)}
{schoolInfo.address && (
diff --git a/nextjs-app/components/school/SecondaryAdmissionsSection.tsx b/nextjs-app/components/school/SecondaryAdmissionsSection.tsx
index b787a03..847be54 100644
--- a/nextjs-app/components/school/SecondaryAdmissionsSection.tsx
+++ b/nextjs-app/components/school/SecondaryAdmissionsSection.tsx
@@ -46,10 +46,10 @@ export function SecondaryAdmissionsSection({
{admissionsTag && (
- {admissionsTag}{' '}
+ {admissionsTag}:{' '}
{admissionsTag === 'Selective'
- ? '— Entry to this school is by selective examination (e.g. 11+).'
- : `— This school has a faith-based admissions priority (${schoolInfo.religious_denomination}).`}
+ ? 'entry to this school is by selective examination (e.g. 11+).'
+ : `this school has a faith-based admissions priority (${schoolInfo.religious_denomination}).`}
)}
diff --git a/nextjs-app/components/school/lastDistanceOffered.ts b/nextjs-app/components/school/lastDistanceOffered.ts
index cf90a39..3153240 100644
--- a/nextjs-app/components/school/lastDistanceOffered.ts
+++ b/nextjs-app/components/school/lastDistanceOffered.ts
@@ -32,7 +32,7 @@ export interface CutoffDisplay {
/** What the cut-off measures, and what it does not. Identical on both templates. */
export const CUTOFF_NOTE =
'The furthest home offered a place, after higher priorities such as siblings, ' +
- 'faith and EHCP were applied. It is not a fixed catchment — it moves every year ' +
+ 'faith and EHCP were applied. It is not a fixed catchment. It moves every year ' +
'with the number of applications.';
/** Straight-line, because that is how councils measure it. */
@@ -54,7 +54,7 @@ export function describeCutoff(
entryYear: formatEntryYear(distance.year),
routeNote:
routes > 1
- ? `Furthest of ${routes} admission routes at this school — the one that ` +
+ ? `Furthest of ${routes} admission routes at this school. The one that ` +
'applies to your child may have had a shorter cut-off.'
: null,
};
@@ -122,10 +122,10 @@ export function compareToCutoff(
const cutoffPhrase = `the September ${year} cut-off of ${cutoffLabel}`;
const headline =
verdict === 'inside'
- ? `${label} away — inside ${cutoffPhrase}.`
+ ? `${label} away, inside ${cutoffPhrase}.`
: verdict === 'outside'
- ? `${label} away — beyond ${cutoffPhrase}.`
- : `${label} away — too close to ${cutoffPhrase} to call.`;
+ ? `${label} away, beyond ${cutoffPhrase}.`
+ : `${label} away, too close to ${cutoffPhrase} to call.`;
const detail =
verdict === 'too-close'
@@ -156,11 +156,11 @@ export function compareToCutoff(
*/
export const CUTOFF_CHECK_CAVEAT =
'Distance is the last criterion applied. Places go first to children in care, '
- + 'EHCP places, siblings and — at faith schools — on faith criteria, so a home '
+ + 'EHCP places and siblings, and at faith schools by faith criteria, so a home '
+ 'inside the distance can still miss out. Figures are straight-line distances '
+ 'and not a catchment boundary; some authorities measure a walking route '
+ "instead, which is always longer for the same home. Next year's cut-off "
- + "depends on next year's applicants — always check the school's own "
+ + "depends on next year's applicants, so always check the school's own "
+ 'admissions policy.';
// ---------------------------------------------------------------------------
diff --git a/nextjs-app/lib/metrics.ts b/nextjs-app/lib/metrics.ts
index 56effec..dea2792 100644
--- a/nextjs-app/lib/metrics.ts
+++ b/nextjs-app/lib/metrics.ts
@@ -16,32 +16,32 @@ export const METRIC_EXPLANATIONS: Record
= {
detail: 'The national average is around 60%. Higher means more pupils reached the expected level.',
},
rwm_high_pct: {
- label: 'Higher Standard — Reading, Writing & Maths',
+ label: 'Higher Standard: Reading, Writing & Maths',
plain: '% of pupils exceeding the expected level in all three subjects',
detail: 'A more demanding threshold. The national average is around 8%.',
},
reading_expected_pct: {
- label: 'Reading — Expected Standard',
+ label: 'Reading: Expected Standard',
plain: '% of pupils achieving the expected standard in reading at age 11',
},
writing_expected_pct: {
- label: 'Writing — Expected Standard',
+ label: 'Writing: Expected Standard',
plain: '% of pupils achieving the expected standard in writing at age 11',
},
maths_expected_pct: {
- label: 'Maths — Expected Standard',
+ label: 'Maths: Expected Standard',
plain: '% of pupils achieving the expected standard in maths at age 11',
},
reading_high_pct: {
- label: 'Reading — Higher Standard',
+ label: 'Reading: Higher Standard',
plain: '% of pupils exceeding the expected level in reading',
},
writing_high_pct: {
- label: 'Writing — Higher Standard',
+ label: 'Writing: Higher Standard',
plain: '% of pupils exceeding the expected level in writing',
},
maths_high_pct: {
- label: 'Maths — Higher Standard',
+ label: 'Maths: Higher Standard',
plain: '% of pupils exceeding the expected level in maths',
},
gps_expected_pct: {
@@ -90,7 +90,7 @@ export const METRIC_EXPLANATIONS: Record = {
detail: 'Lower is better. Persistent absence can significantly affect attainment.',
},
rwm_expected_disadvantaged_pct: {
- label: 'Disadvantaged Pupils — Reading, Writing & Maths',
+ label: 'Disadvantaged Pupils: Reading, Writing & Maths',
plain: '% of disadvantaged pupils achieving the expected standard in all three subjects',
},
disadvantaged_gap: {
@@ -105,7 +105,7 @@ export const METRIC_EXPLANATIONS: Record = {
},
sen_ehcp_pct: {
label: 'Education, Health and Care Plan (EHCP)',
- plain: '% of pupils with a formal EHCP — a legal plan for pupils with significant additional needs',
+ plain: '% of pupils with a formal EHCP (a legal plan for pupils with significant additional needs)',
},
eal_pct: {
label: 'English as an Additional Language',
@@ -128,26 +128,26 @@ export const METRIC_EXPLANATIONS: Record = {
detail: '0 = national average. Positive means better-than-expected progress; negative means lower-than-expected. A score above +0.5 is considered well above average.',
},
english_maths_standard_pass_pct: {
- label: 'English & Maths — Grade 4+ (Standard Pass)',
+ label: 'English & Maths: Grade 4+ (Standard Pass)',
plain: '% of pupils achieving at least a grade 4 in both English and Maths',
detail: 'Grade 4 is the minimum "standard pass". Employers and colleges often require grade 4 in English and Maths.',
},
english_maths_strong_pass_pct: {
- label: 'English & Maths — Grade 5+ (Strong Pass)',
+ label: 'English & Maths: Grade 5+ (Strong Pass)',
plain: '% of pupils achieving at least a grade 5 in both English and Maths',
detail: 'Grade 5 is a "strong pass". Many sixth forms and universities expect grade 5 in English and Maths.',
},
ebacc_entry_pct: {
label: 'EBacc Entry',
- plain: '% of pupils who entered the English Baccalaureate — a set of GCSE subjects covering English, Maths, Sciences, a Language, and Humanities',
+ plain: '% of pupils who entered the English Baccalaureate (a set of GCSE subjects covering English, Maths, Sciences, a Language and Humanities)',
detail: 'EBacc entry keeps academic options open post-16. It is not a separate qualification.',
},
ebacc_standard_pass_pct: {
- label: 'EBacc — Grade 4+ (Standard Pass)',
+ label: 'EBacc: Grade 4+ (Standard Pass)',
plain: '% of pupils achieving grade 4 or above across all EBacc subjects',
},
ebacc_strong_pass_pct: {
- label: 'EBacc — Grade 5+ (Strong Pass)',
+ label: 'EBacc: Grade 5+ (Strong Pass)',
plain: '% of pupils achieving grade 5 or above across all EBacc subjects',
},
ebacc_avg_score: {
diff --git a/nextjs-app/lib/utils.ts b/nextjs-app/lib/utils.ts
index 42864ef..0cbe3ac 100644
--- a/nextjs-app/lib/utils.ts
+++ b/nextjs-app/lib/utils.ts
@@ -116,7 +116,7 @@ export function formatWithSuppression(value: number | null | undefined): { displ
return { display: formatPercentage(value), suppressed: false };
}
-export const SUPPRESSED_TOOLTIP = 'Data not available — may be suppressed to protect small cohorts.';
+export const SUPPRESSED_TOOLTIP = 'Data not available. It may be suppressed to protect small cohorts.';
/**
* Format a progress score (can be negative)
@@ -750,7 +750,7 @@ export function buildSchoolSummary(
if (admissions.first_preference_offer_pct != null) {
const pct = Math.round(admissions.first_preference_offer_pct);
parts.push(
- `oversubscribed — ${pct}% of first-choice applicants are offered a place`,
+ `oversubscribed (${pct}% of first-choice applicants are offered a place)`,
);
} else {
parts.push('oversubscribed');