diff --git a/nextjs-app/components/SecondarySchoolDetailView.module.css b/nextjs-app/components/SecondarySchoolDetailView.module.css
index 4ae1c12..92bd5fa 100644
--- a/nextjs-app/components/SecondarySchoolDetailView.module.css
+++ b/nextjs-app/components/SecondarySchoolDetailView.module.css
@@ -802,6 +802,207 @@
color: var(--text-muted, #8a847a);
}
+/* ── GCSE hero stat cards (mirrors primary heroStatCard) ─ */
+.heroStatGrid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 0.85rem;
+ margin-bottom: 0.25rem;
+}
+
+.heroStatCard {
+ background: rgba(45, 125, 125, 0.1);
+ border: 1px solid rgba(45, 125, 125, 0.2);
+ border-radius: 12px;
+ padding: 1rem 1.1rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+}
+
+.heroStatCard .heroStatLabel {
+ font-size: 0.6rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ color: var(--text-muted, #6d685f);
+}
+
+.heroStatCard .heroStatValue {
+ font-family: var(--font-playfair), 'Playfair Display', serif;
+ font-size: 2.1rem;
+ font-weight: 700;
+ line-height: 1;
+ color: var(--accent-teal, #2d7d7d);
+ display: flex;
+ align-items: baseline;
+ gap: 0.4rem;
+ flex-wrap: wrap;
+}
+
+.heroStatCard .heroStatHint {
+ font-size: 0.7rem;
+ color: var(--text-muted, #6d685f);
+ font-style: normal;
+ margin-top: 0;
+}
+
+/* ── Attainment 8 visual bar ─────────────────────────── */
+.att8Viz {
+ margin: 1.25rem 0 0.5rem;
+}
+
+.att8VizLabel {
+ font-size: 0.6875rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.07em;
+ color: var(--text-muted, #6d685f);
+ margin-bottom: 0.5rem;
+}
+
+.att8VizTrack {
+ position: relative;
+ height: 14px;
+ background: rgba(45, 125, 125, 0.08);
+ border: 1px solid var(--border-color, #e5dfd5);
+ border-radius: 4px;
+ overflow: visible;
+}
+
+.att8VizFill {
+ height: 100%;
+ background: var(--accent-teal, #2d7d7d);
+ border-radius: 4px 0 0 4px;
+ transition: width 0.6s ease;
+}
+
+.att8VizNatLine {
+ position: absolute;
+ top: -4px;
+ bottom: -4px;
+ width: 2px;
+ background: var(--accent-coral, #e07256);
+ border-radius: 2px;
+ z-index: 2;
+}
+
+.att8VizNatPill {
+ position: absolute;
+ top: -20px;
+ transform: translateX(-50%);
+ background: var(--accent-coral, #e07256);
+ color: #fff;
+ font-size: 0.6rem;
+ font-weight: 700;
+ padding: 0.1rem 0.3rem;
+ border-radius: 3px;
+ white-space: nowrap;
+}
+
+.att8VizTicks {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 0.25rem;
+ font-size: 0.6rem;
+ color: var(--text-muted, #6d685f);
+}
+
+/* ── Progress 8 number line ──────────────────────────── */
+.p8Viz {
+ margin: 1.25rem 0 0.5rem;
+}
+
+.p8VizLabel {
+ font-size: 0.6875rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.07em;
+ color: var(--text-muted, #6d685f);
+ margin-bottom: 0.5rem;
+}
+
+.p8VizTrack {
+ position: relative;
+ height: 14px;
+ background: rgba(45, 125, 125, 0.06);
+ border: 1px solid var(--border-color, #e5dfd5);
+ border-radius: 4px;
+ overflow: visible;
+}
+
+.p8VizCi {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ background: rgba(45, 125, 125, 0.18);
+ border-radius: 3px;
+}
+
+.p8VizZero {
+ position: absolute;
+ top: -4px;
+ bottom: -4px;
+ width: 2px;
+ background: var(--border-color, #e5dfd5);
+ z-index: 1;
+}
+
+.p8VizDot {
+ position: absolute;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+ background: var(--accent-teal, #2d7d7d);
+ border: 2px solid white;
+ box-shadow: 0 1px 4px rgba(0,0,0,0.2);
+ z-index: 3;
+}
+
+.p8VizDotNeg {
+ background: var(--accent-coral, #e07256);
+}
+
+.p8VizTicks {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 0.25rem;
+ font-size: 0.6rem;
+ color: var(--text-muted, #6d685f);
+}
+
+/* ── History accordion ───────────────────────────────── */
+.historyDisclosure {
+ margin-top: 1rem;
+}
+
+.historyToggle {
+ list-style: none;
+ cursor: pointer;
+ font-size: 0.8125rem;
+ font-weight: 600;
+ color: var(--text-muted, #6d685f);
+ padding: 0.5rem 0;
+ display: flex;
+ align-items: center;
+ gap: 0.375rem;
+ user-select: none;
+}
+
+.historyToggle::-webkit-details-marker { display: none; }
+
+.historyToggle::before {
+ content: '▶';
+ font-size: 0.6rem;
+ transition: transform 0.2s ease;
+}
+
+.historyDisclosure[open] .historyToggle::before {
+ transform: rotate(90deg);
+}
+
/* ── Responsive ──────────────────────────────────────── */
@media (max-width: 768px) {
.header {
@@ -848,6 +1049,14 @@
font-size: 1rem;
}
+ .heroStatGrid {
+ grid-template-columns: 1fr;
+ }
+
+ .heroStatCard .heroStatValue {
+ font-size: 1.85rem;
+ }
+
.chartContainer {
height: 220px;
}
diff --git a/nextjs-app/components/SecondarySchoolDetailView.tsx b/nextjs-app/components/SecondarySchoolDetailView.tsx
index 003b895..ec86bce 100644
--- a/nextjs-app/components/SecondarySchoolDetailView.tsx
+++ b/nextjs-app/components/SecondarySchoolDetailView.tsx
@@ -494,61 +494,153 @@ export function SecondarySchoolDetailView({
)}
-
+ {/* Hero stat cards — top GCSE metrics */}
+
{latestResults.attainment_8_score != null && (
-
-
- Attainment 8
+
+
+ Attainment 8 score
-
{latestResults.attainment_8_score.toFixed(1)}
+
+ {latestResults.attainment_8_score.toFixed(1)}
+ {secondaryAvg.attainment_8_score != null && (
+
+ )}
+
{secondaryAvg.attainment_8_score != null && (
-
National avg: {secondaryAvg.attainment_8_score.toFixed(1)}
+
National avg: {secondaryAvg.attainment_8_score.toFixed(1)}
)}
)}
{latestResults.progress_8_score != null && (
-
-
- Progress 8
+
+
+ Progress 8 score
-
+
{formatProgress(latestResults.progress_8_score)}
- {(latestResults.progress_8_lower_ci != null || latestResults.progress_8_upper_ci != null) && (
-
- CI: {latestResults.progress_8_lower_ci?.toFixed(2) ?? '?'} to {latestResults.progress_8_upper_ci?.toFixed(2) ?? '?'}
+ {(latestResults.progress_8_lower_ci != null && latestResults.progress_8_upper_ci != null) ? (
+
+ CI: {latestResults.progress_8_lower_ci.toFixed(2)} to {latestResults.progress_8_upper_ci.toFixed(2)}
- )}
-
- )}
- {latestResults.english_maths_standard_pass_pct != null && (
-
-
- English & Maths Grade 4+
-
-
-
{formatPercentage(latestResults.english_maths_standard_pass_pct)}
- {secondaryAvg.english_maths_standard_pass_pct != null && (
-
National avg: {secondaryAvg.english_maths_standard_pass_pct.toFixed(0)}%
+ ) : (
+
National baseline: 0.0
)}
)}
{latestResults.english_maths_strong_pass_pct != null && (
-
-
+
+
English & Maths Grade 5+
-
{formatPercentage(latestResults.english_maths_strong_pass_pct)}
+
+ {formatPercentage(latestResults.english_maths_strong_pass_pct)}
+ {secondaryAvg.english_maths_strong_pass_pct != null && (
+
+ )}
+
{secondaryAvg.english_maths_strong_pass_pct != null && (
-
National avg: {secondaryAvg.english_maths_strong_pass_pct.toFixed(0)}%
+
National avg: {secondaryAvg.english_maths_strong_pass_pct.toFixed(0)}%
+ )}
+
+ )}
+ {latestResults.english_maths_standard_pass_pct != null && (
+
+
+ English & Maths Grade 4+
+
+
+
+ {formatPercentage(latestResults.english_maths_standard_pass_pct)}
+ {secondaryAvg.english_maths_standard_pass_pct != null && (
+
+ )}
+
+ {secondaryAvg.english_maths_standard_pass_pct != null && (
+
National avg: {secondaryAvg.english_maths_standard_pass_pct.toFixed(0)}%
)}
)}
+ {/* Attainment 8 visual bar (0–80 scale) */}
+ {latestResults.attainment_8_score != null && (
+
+
Attainment 8 — school vs national
+
+
+ {secondaryAvg.attainment_8_score != null && (
+
+
+ Nat avg {secondaryAvg.attainment_8_score.toFixed(1)}
+
+
+ )}
+
+
+ 020406080
+
+
+ )}
+
+ {/* Progress 8 number line with CI */}
+ {latestResults.progress_8_score != null && !p8Suspended && (
+
+
Progress 8 — relative to national baseline (0)
+ {(() => {
+ const p8 = latestResults.progress_8_score!;
+ const lo = latestResults.progress_8_lower_ci ?? p8;
+ const hi = latestResults.progress_8_upper_ci ?? p8;
+ const range = 6; // −3 to +3
+ const toX = (v: number) => `${Math.min(Math.max(((v + 3) / range) * 100, 0), 100)}%`;
+ return (
+
+ {/* CI band */}
+
+ {/* Zero line */}
+
+ {/* Score dot */}
+
+
+ );
+ })()}
+
+ −3−2−10+1+2+3
+
+
+ )}
+
{/* Progress 8 component breakdown */}
{(latestResults.progress_8_english != null || latestResults.progress_8_maths != null ||
latestResults.progress_8_ebacc != null || latestResults.progress_8_open != null) && (
@@ -608,21 +700,6 @@ export function SecondarySchoolDetailView({
>
)}
- {/* Performance chart */}
- {yearlyData.length > 0 && (
- <>
-
Results Over Time
-
- >
- )}
)}
@@ -695,33 +772,33 @@ export function SecondarySchoolDetailView({
{(latestResults?.sen_support_pct != null || latestResults?.sen_ehcp_pct != null) && (
<>
Special Educational Needs (SEN)
-
+
{latestResults?.sen_support_pct != null && (
-
-
- Pupils receiving SEN support
+
+
+ SEN support
-
{formatPercentage(latestResults.sen_support_pct)}
-
SEN support without an EHCP
+
{formatPercentage(latestResults.sen_support_pct)}
+
Without an EHCP
)}
{latestResults?.sen_ehcp_pct != null && (
-
-
- Pupils with an EHCP
+
+
+ Pupils with EHCP
-
{formatPercentage(latestResults.sen_ehcp_pct)}
-
Education, Health and Care Plan
+
{formatPercentage(latestResults.sen_ehcp_pct)}
+
Education, Health and Care Plan
)}
{(schoolInfo.total_pupils != null || latestResults?.total_pupils != null) && (
-
-
Total pupils
-
{(schoolInfo.total_pupils ?? latestResults!.total_pupils!).toLocaleString()}
+
+
Total pupils
+
{(schoolInfo.total_pupils ?? latestResults!.total_pupils!).toLocaleString()}
{schoolInfo.capacity != null && (
-
Capacity: {schoolInfo.capacity}
+
Capacity: {schoolInfo.capacity}
)}
)}
@@ -808,30 +885,47 @@ export function SecondarySchoolDetailView({
{yearlyData.length > 1 && (
Historical Results
-
-
-
-
- | Year |
- Attainment 8 |
- Progress 8 |
- Eng & Maths 4+ |
- EBacc entry % |
-
-
-
- {yearlyData.map((result) => (
-
- | {formatAcademicYear(result.year)} |
- {result.attainment_8_score != null ? result.attainment_8_score.toFixed(1) : '-'} |
- {result.progress_8_score != null ? formatProgress(result.progress_8_score) : '-'} |
- {result.english_maths_standard_pass_pct != null ? formatPercentage(result.english_maths_standard_pass_pct) : '-'} |
- {result.ebacc_entry_pct != null ? formatPercentage(result.ebacc_entry_pct) : '-'} |
+ {yearlyData.length > 0 && (
+ <>
+ Results Over Time
+
+ >
+ )}
+
+ View raw year-by-year data
+
+
+
+
+ | Year |
+ Attainment 8 |
+ Progress 8 |
+ Eng & Maths 4+ |
+ EBacc entry % |
- ))}
-
-
-
+
+
+ {yearlyData.map((result) => (
+
+ | {formatAcademicYear(result.year)} |
+ {result.attainment_8_score != null ? result.attainment_8_score.toFixed(1) : '-'} |
+ {result.progress_8_score != null ? formatProgress(result.progress_8_score) : '-'} |
+ {result.english_maths_standard_pass_pct != null ? formatPercentage(result.english_maths_standard_pass_pct) : '-'} |
+ {result.ebacc_entry_pct != null ? formatPercentage(result.ebacc_entry_pct) : '-'} |
+
+ ))}
+
+
+
+
)}