diff --git a/nextjs-app/app/globals.css b/nextjs-app/app/globals.css
index 19cca8a..ae80291 100644
--- a/nextjs-app/app/globals.css
+++ b/nextjs-app/app/globals.css
@@ -58,6 +58,23 @@
--transition: 0.2s ease;
--transition-slow: 0.4s ease;
+
+ /* Phase indicators */
+ --phase-primary: #5b8cbf;
+ --phase-primary-bg: rgba(91, 140, 191, 0.10);
+ --phase-primary-text: #3d6a99;
+ --phase-secondary: #9b6bb0;
+ --phase-secondary-bg: rgba(155, 107, 176, 0.10);
+ --phase-secondary-text: #7a4f93;
+ --phase-all-through: #7a9a6d;
+ --phase-all-through-bg: rgba(122, 154, 109, 0.10);
+ --phase-all-through-text: #5a7a4d;
+ --phase-post16: #c4915e;
+ --phase-post16-bg: rgba(196, 145, 94, 0.10);
+ --phase-post16-text: #9a6d3a;
+ --phase-nursery: #e0a0b0;
+ --phase-nursery-bg: rgba(224, 160, 176, 0.10);
+ --phase-nursery-text: #b06070;
}
* {
diff --git a/nextjs-app/components/SchoolRow.module.css b/nextjs-app/components/SchoolRow.module.css
index 0b0e7ba..939b2a5 100644
--- a/nextjs-app/components/SchoolRow.module.css
+++ b/nextjs-app/components/SchoolRow.module.css
@@ -12,10 +12,14 @@
}
.row:hover {
- border-left-color: var(--accent-coral, #e07256);
box-shadow: 0 2px 8px rgba(26, 22, 18, 0.06);
}
+/* Phase border colours */
+.phasePrimary { border-left-color: var(--phase-primary, #5b8cbf); }
+.phaseAllThrough { border-left-color: var(--phase-all-through, #7a9a6d); }
+.phaseNursery { border-left-color: var(--phase-nursery, #e0a0b0); }
+
.rowInCompare {
border-left-color: var(--accent-teal, #2d7d7d);
background: var(--bg-secondary, #f3ede4);
@@ -59,6 +63,21 @@
color: var(--accent-coral, #e07256);
}
+/* Phase label pill */
+.phaseLabel {
+ display: inline-block;
+ padding: 0.0625rem 0.375rem;
+ font-size: 0.6875rem;
+ font-weight: 600;
+ border-radius: 3px;
+ white-space: nowrap;
+ margin-right: 0.25rem;
+}
+
+.phaseLabelPrimary { background: var(--phase-primary-bg); color: var(--phase-primary-text); }
+.phaseLabelAllThrough { background: var(--phase-all-through-bg); color: var(--phase-all-through-text); }
+.phaseLabelNursery { background: var(--phase-nursery-bg); color: var(--phase-nursery-text); }
+
/* Line 2: context tags */
.line2 {
display: flex;
diff --git a/nextjs-app/components/SchoolRow.tsx b/nextjs-app/components/SchoolRow.tsx
index 2e555e0..6ff255b 100644
--- a/nextjs-app/components/SchoolRow.tsx
+++ b/nextjs-app/components/SchoolRow.tsx
@@ -9,7 +9,7 @@
*/
import type { School } from '@/lib/types';
-import { formatPercentage, formatProgress, calculateTrend, schoolUrl } from '@/lib/utils';
+import { formatPercentage, formatProgress, calculateTrend, getPhaseStyle, schoolUrl } from '@/lib/utils';
import { progressBand } from '@/lib/metrics';
import styles from './SchoolRow.module.css';
@@ -36,6 +36,7 @@ export function SchoolRow({
onRemoveFromCompare,
}: SchoolRowProps) {
const trend = calculateTrend(school.rwm_expected_pct, school.prev_rwm_expected_pct);
+ const phase = getPhaseStyle(school.phase);
// Use reading progress as representative; fall back to writing, then maths
const progressScore =
@@ -55,7 +56,7 @@ export function SchoolRow({
school.religious_denomination !== 'Does not apply';
return (
-
+
{/* Left: four content lines */}
@@ -78,6 +79,11 @@ export function SchoolRow({
{/* Line 2: Context tags */}
+ {phase.label && (
+
+ {phase.label}
+
+ )}
{school.school_type &&
{school.school_type}}
{school.age_range &&
{school.age_range}}
{showDenomination &&
{school.religious_denomination}}
diff --git a/nextjs-app/components/SecondarySchoolRow.module.css b/nextjs-app/components/SecondarySchoolRow.module.css
index f31ff4a..64a234a 100644
--- a/nextjs-app/components/SecondarySchoolRow.module.css
+++ b/nextjs-app/components/SecondarySchoolRow.module.css
@@ -12,10 +12,14 @@
}
.row:hover {
- border-left-color: var(--accent-coral, #e07256);
box-shadow: 0 2px 8px rgba(26, 22, 18, 0.06);
}
+/* Phase border colours */
+.phaseSecondary { border-left-color: var(--phase-secondary, #9b6bb0); }
+.phaseAllThrough { border-left-color: var(--phase-all-through, #7a9a6d); }
+.phasePost16 { border-left-color: var(--phase-post16, #c4915e); }
+
.rowInCompare {
border-left-color: var(--accent-teal, #2d7d7d);
background: var(--bg-secondary, #f3ede4);
@@ -144,6 +148,21 @@
border-radius: 3px;
}
+/* Phase label pill */
+.phaseLabel {
+ display: inline-block;
+ padding: 0.0625rem 0.375rem;
+ font-size: 0.6875rem;
+ font-weight: 600;
+ border-radius: 3px;
+ white-space: nowrap;
+ margin-right: 0.25rem;
+}
+
+.phaseLabelSecondary { background: var(--phase-secondary-bg); color: var(--phase-secondary-text); }
+.phaseLabelAllThrough { background: var(--phase-all-through-bg); color: var(--phase-all-through-text); }
+.phaseLabelPost16 { background: var(--phase-post16-bg); color: var(--phase-post16-text); }
+
.provisionTag {
display: inline-block;
padding: 0.0625rem 0.375rem;
diff --git a/nextjs-app/components/SecondarySchoolRow.tsx b/nextjs-app/components/SecondarySchoolRow.tsx
index ccabda3..9debdc6 100644
--- a/nextjs-app/components/SecondarySchoolRow.tsx
+++ b/nextjs-app/components/SecondarySchoolRow.tsx
@@ -11,7 +11,7 @@
'use client';
import type { School } from '@/lib/types';
-import { schoolUrl } from '@/lib/utils';
+import { getPhaseStyle, schoolUrl } from '@/lib/utils';
import styles from './SecondarySchoolRow.module.css';
const OFSTED_LABELS: Record
= {
@@ -58,6 +58,7 @@ export function SecondarySchoolRow({
}
};
+ const phase = getPhaseStyle(school.phase);
const att8 = school.attainment_8_score ?? null;
const laDelta =
att8 != null && laAvgAttainment8 != null ? att8 - laAvgAttainment8 : null;
@@ -67,7 +68,7 @@ export function SecondarySchoolRow({
const showGender = school.gender && school.gender.toLowerCase() !== 'mixed';
return (
-
+
{/* Left: four content lines */}
@@ -90,6 +91,11 @@ export function SecondarySchoolRow({
{/* Line 2: Context tags */}
+ {phase.label && (
+
+ {phase.label}
+
+ )}
{school.school_type && {school.school_type}}
{school.age_range && {school.age_range}}
{showGender && (
diff --git a/nextjs-app/lib/utils.ts b/nextjs-app/lib/utils.ts
index 25653b6..a0d1823 100644
--- a/nextjs-app/lib/utils.ts
+++ b/nextjs-app/lib/utils.ts
@@ -364,6 +364,25 @@ export function parseQueryString(search: string): Record {
* Handles both 4-digit start years (2023 → "2023/24") and
* 6-digit EES codes (202526 → "2025/26").
*/
+export function getPhaseStyle(phase?: string | null): { key: string; label: string } {
+ switch (phase?.toLowerCase()) {
+ case 'primary':
+ case 'middle deemed primary':
+ return { key: 'Primary', label: 'Primary' };
+ case 'secondary':
+ case 'middle deemed secondary':
+ return { key: 'Secondary', label: 'Secondary' };
+ case 'all-through':
+ return { key: 'AllThrough', label: 'All-through' };
+ case '16 plus':
+ return { key: 'Post16', label: 'Post-16' };
+ case 'nursery':
+ return { key: 'Nursery', label: 'Nursery' };
+ default:
+ return { key: '', label: '' };
+ }
+}
+
export function formatAcademicYear(year: number): string {
const s = year.toString();
if (s.length === 6) {