The Brighton Secondary School System

A strategic view

Author

Adam Dennett, UCL Centre for Advanced Spatial Analysis

Published

October 16, 2026

Abstract

Brighton & Hove is deciding what to do about a secondary school system that is losing children faster than it is losing places. This document assembles what can be established about that system from published data alone: where the schools and the children are, how reachable the schools actually are by bus, how demand has moved over sixteen years, and what a model of the whole system says about the choices in front of the council. It is written to be argued with.

TipTry the options yourself

Everything in this document comes out of one model, and that model runs in a browser. Open the school places simulator to change admission numbers, catchments, Longhill’s site or the oversubscription priorities, and watch what happens to each school’s intake, to journey times, to the mix of pupils and to school budgets.

There is also a guide to the model, stage by stage, for anyone who wants to check how it works.

WarningBeta

This simulator is currently in beta test mode - outputs have not been validated fully, so nothing should, at this point, be taken as reliable, however the simulator shows what could be possible to develop and outputs that are possible.

NoteNo pupil-level data is used here

No pupil-level record is used anywhere in this document. Almost every figure comes from data the council, the DfE or the ONS has already published, or from a model built on top of it. The one exception is a table the council gave the Schools Adjudicator in the 2026/27 case: how many first, second and third preferences each catchment’s children gave each school, over three rounds (item 8.1 of its evidence). It was received as a party to that case rather than published; it is aggregated to catchment and identifies no one; and the full model in section 7 is calibrated to it (Section 7.8.3). Where a question can only be answered with data the council holds but has not released, that is said plainly rather than glossed over — and section 9 lists what those releases would be.

1 Executive summary

1.1 The situation

Brighton & Hove has a secondary school system built for more children than it now has, and the shortfall is going to get worse before anything the council decides can affect it. Year 7 offers peaked at 2,509 in 2019 and stood at 2,250 in 2026. The children who will enter Year 7 in 2033 are already in the city’s primary schools, and there are about 1,750 of them — a further fall of 22%. Against roughly 2,515 published places, that implies something like 765 surplus places, or two and a half average secondary schools’ worth.

The council’s own catchment forecasts sit above that projection in almost every year, and the gap is widest in the Longhill catchment — the place where the consequences of being wrong are largest.

1.2 What the evidence says

This is not a school quality problem. Across English secondaries, absence explains 56% of the variation in Attainment 8 and prior attainment 50%, while deprivation explains only 21%. Once intake is accounted for, Brighton’s schools mostly perform above what their intake predicts. The largest lever available to the city is absence, not social mixing — established in detail in How to Pull the Right Lever.

Moving children between schools is a weak instrument, and this is the single most important point for admissions policy. Decomposing the variance in Attainment 8 shows that a school can reach about 12% of what separates its results from another’s — consistent with sixty years of school-effectiveness research. The rest arrives with the children. Crucially, almost all of that reachable share runs through attendance: the workforce a school hires accounts for only 2.5%, while the school-controllable half of absence accounts for 9%. Redistribution touches neither. It changes which building a child attends without changing their attendance or their circumstances. This is not an argument that schools do not matter — it is an argument that the council’s lever is attendance, and attendance is not an admissions policy.

For disadvantaged pupils, concentration is not the harm it is assumed to be. Nationally, once absence and prior attainment are accounted for, disadvantaged pupils do slightly better in schools with more disadvantaged pupils, not worse. The effect is small, and it appears to run through schools organised to serve those pupils rather than through concentration itself — but it points the opposite way to the council’s aim of reducing concentrations. Brighton Aldridge is the local example: about half its pupils are disadvantaged, and its disadvantaged pupils leave with better GCSE results than comparable pupils elsewhere in England (Section 2.3.1). So where an option here raises the segregation index, that counts against it under the council’s policy aim; it is not, on the evidence, a cost to disadvantaged pupils’ results.

Admissions criteria only bind at some schools. 3 of the 10 schools offered a place to every first-preference applicant. For those schools the catchment rule, the sibling rule and the free school meals quota do nothing at all. The city runs two admissions systems under one set of rules, and public argument about criteria concerns only one of them.

Families are choosing on the wrong number, and the council’s own advice points them to it. Its secondary admissions guide tells parents to look in each school’s prospectus for its “National Curriculum test results, public exam results” and to read its Ofsted reports (Secondary school admissions guide 2027 to 2028) — raw figures that mostly describe who a school admits. Families do as they are advised. Headline Attainment 8 explains 84% of the variation in how heavily each school is preferred, and each point of it goes with about 7% more preferences per place. The value-added measure — what the school actually contributes — explains 20%.

That advice works against the council’s own position. A school serving a more disadvantaged intake posts a lower headline score, so it is chosen less; its intake shrinks and becomes more disadvantaged still; and its headline score falls again. Brighton Aldridge is last of the ten on the headline measure and fourth on value added. Longhill’s headline score is 20 points below the city’s highest, but on value added it is 4.6 points below the best — most of what separates it from the most popular schools is intake, not teaching. By steering families towards the headline number, the council lowers the pull of the very school whose viability it is trying to secure, shrinks the roll its funding depends on, and concentrates disadvantage in the schools its own guidance makes least attractive — the opposite of its stated aim. It is the one part of the Longhill problem the council could change at no cost (Section 5.5).

And it is the largest lever modelled here. Run the city with families reading the four-year value-added measure as closely as they now read the headline score, holding everything else — same places, same catchments, same rules — and the intakes move further than any boundary change in this document. Brighton Aldridge goes from 91 children to 175 on 180 places, from half empty to 97% full; Hove Park and Portslade Aldridge fill; mean journeys shorten, and the extra distance the most deprived children travel falls from 3.4 to 2.5 minutes each way. The share of the city’s empty Year 7 places sitting in just three schools falls from 93% to 75% — which is the difference between a surplus that is a budgeting problem and one that is a viability problem. It creates no places and removes none: what changes is which schools are empty (Section 5.8).

Physical access is unequal, and unequal in the worst direction. 29 of 179 neighbourhoods — about 385 cohort-aged children — can reach no secondary school place within 30 minutes by walking and bus. Accessibility and child poverty correlate at -0.33: 25 neighbourhoods sit in both the least-reachable third and the most deprived third.

Longhill has already been shrunk to about what geography supports. A model of where children would go if only distance and school size mattered gives it 194 children — 72% of its former admission number of 270, but 92% of the reduced 210 now in force. The reduction has already done most of what a reduction can do, and the remaining problem is a falling cohort against a reserve that supports about 3.7 more years.

But the money is not a Longhill problem. 9 of the city’s 10 secondary schools have been spending their reserves, 3 are now overdrawn, and the two in the deepest deficit are not Longhill — they are Hove Park and Cardinal Newman, one of them the largest and fastest-growing school in the city. Section 6 sets out every school’s position and which of them the pupil projections put next in line.

1.3 What follows

  1. Decide about Longhill on the financial timetable, not the consultation one. The horizon is about 3.7 years.
  2. Treat the instruments as a package. Moving the school, changing its admission number and redrawing catchments each fall short alone and compose when combined. Picking one and waiting is the worst available option. If the council puts access for disadvantaged children first, the package should be led by a smaller Longhill at the top of Elm Grove with the catchments redrawn round it (Section 11).
  3. Change the admissions guide’s advice, and publish a contextualised measure alongside Attainment 8. It costs nothing, needs no consultation, and is the largest single change modelled in this document: if families read what a school adds rather than the score it publishes, Brighton Aldridge fills (91 children to 175 on 180 places), the share of the city’s empty places concentrated in three schools falls from 93% to 75%, and deprived children’s extra travel falls by about a minute each way (Section 5.8).
  4. Treat the bus network as an admissions instrument. It is the only lever here that widens choice without taking anything from anyone.
  5. Release ten tables (§9) that would convert most of the bands in this document into estimates.

1.4 How confident to be

The demographic findings are the firmest: they are counts of children already in school, adjusted by a transfer rate that has varied by under two percentage points in a decade. The accessibility findings are firm in direction and approximate in magnitude — modelled journey times, not measured ones. The model results in sections 7 and 8 are the softest: the model is swept rather than calibrated, because calibration needs data the council has not released. Every comparison there is robust across the swept range; no individual number should be read as a prediction.

2 The secondary school system

Ten secondary schools serve Brighton & Hove: six community schools run by the local authority, two academies (Brighton Aldridge and Portslade Aldridge) and two church schools (King’s and Cardinal Newman) that set their own admissions. An eleventh, Peacehaven Community School, sits just outside the boundary but inside the travel-to-school geography, and is included throughout because families on the eastern edge of the city treat it as a real option.

One of the ten is not a single building. Hove Park teaches Years 7 and 8 at its Valley Campus on Hangleton Way, 1.65 km west of the Nevill Road site its address gives, and moves pupils to Nevill Road in Year 9. The council gave statutory notice on 8 June 2026 to close the Valley Campus on 31 August 2028 and bring the school onto one site. Because this document is about Year 7, every figure here for entry years up to 2027 puts Hove Park at Hangleton Way and every figure from 2028 puts it at Nevill Road. It makes little difference to how many children the school takes, and a large difference to which children: at Hangleton Way it draws about twice as many from Hangleton & Knoll and much less from Westdene and Poets’ Corner (the model guide).

The city allocates places by catchment area with a lottery tie-break for oversubscribed schools — an arrangement that is very unusual in England, where distance from the school gate usually decides priority. It dates from the closure of CoMArt in the east of the city in 2005, and the catchment system that replaced distance-based allocation in 2008. CoMArt is marked on the map below because the shape of the system is still partly the shape of that closure.

2.1 Where the schools, the catchments and the children are

Show code
# ---- Schools ---------------------------------------------------------
sch <- schools_sf(oi$schools)

sch_popup <- sprintf(
  "<b>%s</b><br>Admission number 2024: %s<br>Planned 2030: %s%s",
  sch$name, sch$pan2024, sch$pan2030,
  ifelse(is.na(sch$catchment),
         "<br><i>Faith school, admits city-wide</i>",
         paste0("<br>Catchment: ", CATCH_LABELS[sch$boundary_catchment])))

# ---- Children per LSOA ----------------------------------------------
# Oi is the cohort-aged child estimate per zone; zones are LSOA x
# catchment, so an LSOA split by a boundary has to be summed back up.
children <- oi$zones %>%
  group_by(lsoa) %>%
  summarise(children = sum(Oi, na.rm = TRUE), .groups = "drop")

lsoa_pts <- lsoa %>%
  left_join(children, by = c("lsoa21cd" = "lsoa")) %>%
  filter(!is.na(children), children > 0) %>%
  st_point_on_surface() %>%
  suppressWarnings()
pt_xy <- st_coordinates(lsoa_pts)
lsoa_pts$lon <- pt_xy[, 1]; lsoa_pts$lat <- pt_xy[, 2]

# Radius on the square root of the count, so circle *area* is
# proportional to the number of children rather than the radius being -
# the latter exaggerates big areas by a factor of the count.
r_scale <- function(n) 3 + 17 * sqrt(n / max(n, na.rm = TRUE))

pal_catch <- colorFactor(unname(CATCH_COLOURS), names(CATCH_COLOURS))

m <- leaflet(width = "100%", height = 620) %>%
  add_basemap() %>%
  setView(lng = -0.10, lat = 50.84, zoom = 12) %>%
  addPolygons(
    data = catch, group = "Catchments",
    fillColor = ~pal_catch(catchment), fillOpacity = 0.13,
    color = ~pal_catch(catchment), weight = 2.5, opacity = 0.85,
    label = ~unname(CATCH_LABELS[catchment]),
    highlightOptions = highlightOptions(weight = 4, fillOpacity = 0.25,
                                        bringToFront = FALSE)) %>%
  addCircleMarkers(
    data = lsoa_pts, lng = ~lon, lat = ~lat, group = "Children",
    radius = ~r_scale(children),
    fillColor = "#1f4e79", fillOpacity = 0.35,
    color = "#1f4e79", weight = 1, opacity = 0.55,
    label = ~sprintf("%s: about %s children in the cohort",
                     lsoa21nm, round(children)))

m <- add_school_layer(m, sch, group = "Schools", popup = sch_popup)

# CoMArt, closed 2005
m <- m %>% addCircleMarkers(
  lng = COMART$lon, lat = COMART$lat, group = "CoMArt (closed 2005)",
  radius = 6, fillColor = "#b2182b", fillOpacity = 0.9,
  color = "white", weight = 2,
  popup = "<b>CoMArt</b><br>Closed 2005. Its closure is why the city moved to catchments with a lottery tie-break.",
  label = COMART$name)
ic <- logo_icon(COMART$logo)
if (!is.null(ic))
  m <- m %>% addMarkers(lng = COMART$lon, lat = COMART$lat, icon = ic,
                        group = "CoMArt (closed 2005)",
                        label = COMART$name)

legend_html <- paste0(
  '<div style="background:white;padding:8px 12px;border-radius:4px;',
  'box-shadow:0 1px 5px rgba(0,0,0,.3);font-family:sans-serif;font-size:12px;line-height:1.6">',
  '<div style="font-weight:bold;margin-bottom:4px">Catchment areas, 2025/26</div>',
  paste0('<div style="display:flex;align-items:center;gap:6px">',
         '<span style="display:inline-block;width:14px;height:14px;background:',
         unname(CATCH_COLOURS), ';border-radius:2px;flex-shrink:0"></span><span>',
         unname(CATCH_LABELS), '</span></div>', collapse = ''),
  '<div style="margin-top:6px;padding-top:6px;border-top:1px solid #ddd">',
  '<span style="display:inline-block;width:14px;height:14px;background:#1f4e79;',
  'opacity:.35;border-radius:50%;margin-right:6px"></span>Children per LSOA',
  ' <span style="color:#777">(area &prop; count)</span></div></div>')

m %>%
  addLayersControl(
    overlayGroups = c("Catchments", "Schools", "Children", "CoMArt (closed 2005)"),
    options = layersControlOptions(collapsed = FALSE)) %>%
  hideGroup("CoMArt (closed 2005)") %>%
  addControl(html = legend_html, position = "bottomright")
Figure 1: Schools, catchment boundaries and the cohort-aged child population. Use the control at the top right to turn each layer on and off.

Three things are worth noticing on that map before any analysis begins.

The catchments are not equivalent objects. Four of them contain a single school; two contain a pair. A child in the Varndean / Dorothy Stringer or Hove Park / Blatchington Mill catchment has two schools to name and a realistic chance of one of them. A child in the Longhill, Patcham, BACA or PACA catchment has one. That asymmetry does more work in this system than almost anything else, and section 5 returns to it.

The children are not spread evenly. The cohort is concentrated in the centre and west; the east of the city, which contains Longhill’s entire catchment, is thin. Longhill’s problem is visible on this map before any model is run.

Two schools sit outside the geography entirely. King’s and Cardinal Newman admit city-wide on faith criteria. Together they hold 540 places, about 21% of the city total. Any statement about what catchments do has to be read against that.

2.2 Where the children actually are

The circles on the map above are LSOA totals, and an LSOA is a blunt unit — about 1,500 people, drawn for administrative convenience rather than to describe a neighbourhood. Two finer views of the same population follow. They are shown side by side because they answer slightly different questions, and it is worth deciding which one this document should carry.

Show code
BW <- 400          # kernel bandwidth in metres - a walkable neighbourhood
GRID <- 320        # grid resolution

# A weighted kernel density: each child contributes one point, so the
# surface is the density of children rather than of postcodes. Counts are
# apportioned estimates and so fractional; rounding for replication
# changes the total by well under a percent.
reps <- round(pcd$children)
kx <- rep(pcd$easting,  reps)
ky <- rep(pcd$northing, reps)

bb <- st_bbox(city)
k <- MASS::kde2d(kx, ky, h = c(BW, BW), n = GRID,
                 lims = c(bb["xmin"] - 500, bb["xmax"] + 500,
                          bb["ymin"] - 500, bb["ymax"] + 500))

# kde2d integrates to 1 over square metres. Scale to children per
# hectare so the legend means something to a reader.
dens <- k$z * sum(reps) * 1e4

brks <- c(0, 2, 5, 10, 15, 22, 30, max(45, ceiling(max(dens))))
pal_d <- viridisLite::viridis(length(brks) - 1, option = "magma",
                              direction = -1, begin = 0.08)

# isoband wants z indexed [y, x]; kde2d returns [x, y].
bands <- isoband::isobands(x = k$x, y = k$y, z = t(dens),
                           levels_low = brks[-length(brks)],
                           levels_high = brks[-1])

band_sf <- st_sf(
  lo = brks[-length(brks)], hi = brks[-1],
  geometry = st_sfc(isoband::iso_to_sfg(bands), crs = 27700)) %>%
  st_make_valid() %>%
  st_intersection(city) %>%
  filter(!st_is_empty(geometry)) %>%
  # Simplify while still in metres. Contour bands off a 320 x 320 grid
  # carry far more vertices than a screen can show; 15 m is invisible at
  # any zoom this map offers and cuts the embedded geometry sharply.
  # This has to happen before the transform - in 4326 the tolerance
  # would be read as degrees and flatten the whole surface.
  st_simplify(dTolerance = 15, preserveTopology = TRUE) %>%
  st_transform(4326)

band_sf$col <- pal_d[seq_len(nrow(band_sf))]
band_sf$lab <- sprintf("%g - %g children per hectare", band_sf$lo, band_sf$hi)

leaflet(width = "100%", height = 600,
        options = leafletOptions(preferCanvas = TRUE)) %>%
  add_basemap() %>%
  addPolygons(data = band_sf, fillColor = ~col, fillOpacity = 0.72,
              color = NA, weight = 0, label = ~lab, group = "Children") %>%
  addPolygons(data = catch, fill = FALSE, color = "#333333",
              weight = 1.6, opacity = 0.7, group = "Catchments",
              label = ~unname(CATCH_LABELS[catchment])) %>%
  add_school_layer(sch, group = "Schools", popup = sch_popup) %>%
  addLayersControl(overlayGroups = c("Children", "Catchments", "Schools"),
                   options = layersControlOptions(collapsed = TRUE)) %>%
  addLegend(colors = band_sf$col, labels = band_sf$lab,
            title = "Children 0-18<br>per hectare",
            position = "bottomright", opacity = 0.8)
Figure 2: Children aged 0-18 per hectare, smoothed from postcode-level estimates with a 400 m kernel and clipped to the built-up area. Census 2021 household composition apportioned to postcode centroids.
Show code
pcd_sf <- pcd %>%
  st_as_sf(coords = c("easting", "northing"), crs = 27700) %>%
  st_transform(4326) %>%
  filter(!is.na(idaci_decile))

pal_idaci <- colorFactor("RdYlBu", domain = sort(unique(pcd_sf$idaci_decile)))

# Radius on the square root of the count so circle area is proportional
# to children. Scaled well down from the LSOA circles in 2.1: there are
# 4,229 postcodes against 179 LSOAs, so the same per-circle size would
# cover the city in a single wash of ink. The median postcode draws at
# about 3.5 px and the largest at about 9.
#
# preferCanvas matters here rather than being a nicety. Leaflet's
# default renderer gives every marker its own SVG element, and four
# thousand of them is enough to hang or crash a browser tab. On canvas
# they are drawn into a single element and the map stays responsive.
leaflet(width = "100%", height = 600,
        options = leafletOptions(preferCanvas = TRUE)) %>%
  add_basemap() %>%
  addPolygons(data = catch, fill = FALSE, color = "#333333",
              weight = 1.6, opacity = 0.7, group = "Catchments",
              label = ~unname(CATCH_LABELS[catchment])) %>%
  addCircleMarkers(
    data = pcd_sf, group = "Postcodes",
    radius = ~0.5 + sqrt(children),
    fillColor = ~pal_idaci(idaci_decile), fillOpacity = 0.6,
    stroke = FALSE,
    label = ~sprintf("%s | %.0f children 0-18 | IDACI decile %s",
                     postcode, children, idaci_decile)) %>%
  add_school_layer(sch, group = "Schools", popup = sch_popup) %>%
  addLayersControl(overlayGroups = c("Postcodes", "Catchments", "Schools"),
                   options = layersControlOptions(collapsed = TRUE)) %>%
  addLegend(pal = pal_idaci, values = sort(unique(pcd_sf$idaci_decile)),
            title = "IDACI decile<br><span style='font-weight:normal'>1 = most deprived</span>",
            position = "bottomright", opacity = 0.9)
Figure 3: Every residential postcode in the city, sized by the number of children aged 0-18 and coloured by income deprivation affecting children. Decile 1 is the most deprived tenth of neighbourhoods nationally.

The surface and the dots are built from the same 4,229 postcodes and the same 47,850 children, and they show the same city — the smoothed surface tracks the number of children within 500 metres of each postcode at a rank correlation of 0.78. What differs is what each one makes easy to see.

The surface shows where children are concentrated: a dense central and western band, thinning sharply east of the marina. It answers “where is the demand?” and it does not pretend to precision it lacks.

The postcode map shows the same concentration but adds the social gradient, and it exposes something the surface cannot — that deprivation in this city is fine-grained. Deprived and comfortable postcodes sit within a few hundred metres of each other, particularly in the centre. That texture matters later: section 4 shows that poor transport access and child poverty coincide, and section 5 that catchment boundaries drawn at this scale will always cut through mixed ground rather than separating rich areas from poor ones.

NoteTwo limits on both maps

These are Census 2021 output-area household counts apportioned to postcode centroids, not a register of children. They are estimates, and at postcode level they are noisy estimates — reliable in aggregate, not for any individual postcode.

They also cover Brighton & Hove only. The Peacehaven and Telscombe area, which contributes about 215 cohort-aged children to the study area, is absent from both. The LSOA-level figures used everywhere else in this document do include it.

2.3 What the attainment data actually says

The debate about Brighton’s schools is usually conducted in the currency of headline attainment — Attainment 8, league table position, “good” and “bad” schools. That currency is close to worthless for the question the council actually faces, and it is worth being precise about why, because the usual explanation is also wrong.

The common account is that attainment tracks deprivation. It does, but weakly, and stopping there points the city at the wrong lever.

The specification below follows the model in How to Pull the Right Lever: Attainment 8 and the rate variables are logged, so their coefficients are elasticities, while prior attainment enters as a mean KS2 score because it is a score and not a rate.

Across 3,188 mainstream secondaries in England, the proportion of disadvantaged pupils explains 37% of the variation in Attainment 8. Prior attainment explains 56%. And the single strongest of the three is neither: absence alone explains 56%. All three together reach 68%.

The elasticities make the point more sharply than the R² values do. In the combined model a one per cent rise in absence is associated with a 0.29 per cent fall in Attainment 8; a one per cent rise in the disadvantaged share with a fall of just 0.04 per cent — roughly 8 times smaller.

That ordering is the finding. Who is poor matters least of the three. What children could already do at eleven matters more. Whether they are in the room matters most.

Show code
# Attainment 8 is on a log axis in every panel, matching the model. The
# x axis is logged for the two rates and left linear for the KS2 score,
# so each panel is drawn on the scale its term is actually fitted on.
panel <- function(xvar, xlab, ttl, logx) {
  d <- nat %>% filter(!is.na(.data[[xvar]]))
  b <- bh_latest %>% filter(!is.na(.data[[xvar]]))
  if (logx) { d <- d %>% filter(.data[[xvar]] > 0); b <- b %>% filter(.data[[xvar]] > 0) }
  p <- ggplot(d, aes(.data[[xvar]], ATT8SCR)) +
    geom_point(colour = "grey80", size = 0.9, alpha = 0.5) +
    geom_smooth(method = "lm", formula = y ~ x, se = FALSE,
                colour = "grey30", linewidth = 0.7, linetype = "22") +
    geom_point(data = b, colour = "#b2182b", size = 2.4) +
    ggrepel::geom_text_repel(
      data = b %>% filter(SCHNAME %in% c("Longhill High School", "King's School")),
      aes(label = SCHNAME), size = 2.9, colour = "#7f0f22",
      min.segment.length = 0, segment.size = 0.3, seed = 1) +
    scale_y_log10(breaks = c(10, 20, 30, 40, 50, 60, 70)) +
    labs(x = xlab, y = "Attainment 8", subtitle = ttl) +
    theme_bh(11)
  if (logx)
    p <- p + scale_x_log10(labels = label_percent(scale = 1),
                           breaks = c(1, 2, 5, 10, 20, 50))
  p
}

(panel("PTFSM6CLA1A", "Disadvantaged pupils",
       sprintf("Deprivation, logged (R² = %.2f)", summary(m_fsm)$r.squared), TRUE) |
 panel("ks2_c", "Mean KS2 prior attainment, centred",
       sprintf("Prior attainment, linear (R² = %.2f)", summary(m_prior)$r.squared), FALSE) |
 panel("PERCTOT", "Overall absence rate",
       sprintf("Absence, logged (R² = %.2f)", summary(m_abs1)$r.squared), TRUE)) +
  plot_annotation(
    title = "Attainment is a description of intake, and of who turns up",
    subtitle = sprintf("Mainstream secondaries in England, %s. Attainment 8 log scaled throughout; rates logged, the KS2 score linear. Red points are Brighton & Hove.",
                       perf$latest),
    caption = "Source: DfE performance tables. Specification follows How to Pull the Right Lever.",
    theme = theme_bh())
Figure 4: Attainment 8 against two features of intake, every mainstream secondary in England in the latest year, with Brighton & Hove schools highlighted. Prior attainment and absence both order schools far more sharply than deprivation does.

Two consequences follow, and they point in the same direction.

A headline score is mostly a description of who walks through the door. Once prior attainment and deprivation are accounted for, 8 of the 10 Brighton & Hove schools score above what the national relationship predicts for their intake. On the measure that isolates the school’s own contribution, this is not a city with a school quality problem.

The lever that moves is attendance, not social mix. Absence and deprivation travel together, and absence is the mechanism through which much of the deprivation effect actually operates — which is why redistributing disadvantaged children between schools is a far weaker instrument than it is usually assumed to be.

ImportantTwo cautions

Absence is partly an outcome as well as a cause. A child who is disengaged attends less and attains less, so a single-equation fit overstates how much of that 68% is absence doing the causal work. The Lever report models this properly — with school, year, region and inspection-rating effects, and a first stage that models absence itself — rather than reading it off a scatterplot. These three panels are an illustration of that model’s premise, not a substitute for it.

These are single-year, single-equation fits. The full model is fitted on a multi-year panel with random effects and a wider set of school characteristics, including English as an additional language, teacher retention and admissions policy. The ordering of the three terms here matches it; the individual R² values will not, and none of them should be quoted as a causal effect.

TipThe detail behind this is published separately

The contextualised analysis — value-added modelling across every local authority in England, and what it says about where Brighton’s schools really sit — is set out in How to Pull the Right Lever. Its central findings are that Brighton’s schools perform well once intake is accounted for, and that absence, not social mixing, is the largest single lever available to the city. This document takes those findings as given and turns to the spatial and demographic questions the Lever report does not address.

Show code
flip <- oi$attract %>%
  filter(!is.na(att8), !is.na(va_lever), name != "Peacehaven Community School") %>%
  mutate(rank_att8 = rank(-att8), rank_va = rank(-va_lever)) %>%
  select(name, rank_att8, rank_va) %>%
  pivot_longer(starts_with("rank_"), names_to = "measure", values_to = "rank") %>%
  mutate(measure = factor(measure, c("rank_att8", "rank_va"),
                          c("Attainment 8\n(headline)", "Value added\n(Lever report)")))

moved <- flip %>%
  pivot_wider(names_from = measure, values_from = rank) %>%
  rename(att8 = 2, va = 3) %>%
  mutate(shift = att8 - va)

ggplot(flip, aes(measure, rank, group = name)) +
  geom_line(aes(colour = name), linewidth = 1.1, alpha = 0.85, show.legend = FALSE) +
  geom_point(aes(colour = name), size = 3, show.legend = FALSE) +
  ggrepel::geom_text_repel(
    data = flip %>% filter(measure == levels(flip$measure)[1]),
    aes(label = name), hjust = 1, nudge_x = -0.08, size = 3,
    direction = "y", segment.size = 0.2, seed = 1) +
  ggrepel::geom_text_repel(
    data = flip %>% filter(measure == levels(flip$measure)[2]),
    aes(label = name), hjust = 0, nudge_x = 0.08, size = 3,
    direction = "y", segment.size = 0.2, seed = 1) +
  scale_y_reverse(breaks = 1:10) +
  scale_x_discrete(expand = expansion(add = c(0.85, 0.85))) +
  labs(x = NULL, y = "Rank (1 = highest)",
       title = "Which schools look good depends on which measure you use",
       subtitle = "Ranked by headline attainment, and by contribution to progress once intake is accounted for",
       caption = "Sources: DfE performance tables; How to Pull the Right Lever.") +
  theme_bh() +
  theme(panel.grid.major.x = element_blank())
Figure 5: The same ten schools ranked two ways: by headline Attainment 8, and by the value-added measure from the Lever report. Lines crossing means the two measures disagree about which schools are doing well.

The lines cross a great deal. Brighton Aldridge Community Academy rises 6 places when you move from the headline measure to the value-added one; Patcham High School and Cardinal Newman Catholic School falls 4. 3 of the ten schools move by three places or more.

The schools that fall are not bad schools, and the schools that rise are not suddenly good ones. The two measures are answering different questions: one asks what pupils at this school achieved, the other asks how much of that the school is responsible for. Families reading the published headline are answering the first question while believing they are answering the second — and section 5 shows that this has a measurable effect on where demand actually goes.

2.3.1 For disadvantaged pupils, the direction reverses

The fall in Attainment 8 that goes with a higher disadvantaged share is an average over all pupils. How to Pull the Right Lever fits the same model separately for disadvantaged and non-disadvantaged pupils, and the two disagree about direction.

  • For non-disadvantaged pupils a school with more disadvantaged pupils is associated with lower attainment, even after their own attendance and prior attainment are accounted for.
  • For disadvantaged pupils it is associated with slightly higher attainment, once absence and low prior attainment are accounted for. The report finds this in the four-year panel and in each year separately, and under different measures of disadvantaged attainment and of school disadvantage (robustness checks, model results). It is strongest in the non-GCSE qualifications of the Attainment 8 ‘open’ element.
  • The effect is small, and it is mostly about the schools. In the full multilevel model the coefficient shrinks towards zero once region, local authority and Ofsted rating are allowed for. The report’s reading is that disadvantaged pupils do not gain simply from being together; they gain because schools with many of them — in some places and with some inspection records — have learned to serve them well, including through well-targeted vocational routes.

The report is explicit that this is not an argument for concentrating disadvantaged pupils, and that mixed intakes may have benefits it does not measure. Its argument is narrower: where a city’s geography already concentrates disadvantage in some schools, deconcentration pursued at the cost of longer journeys is unlikely to help the pupils it is meant to help, and may harm them.

NoteBrighton Aldridge is the local example

Brighton Aldridge Community Academy has about half its pupils disadvantaged, some of the city’s highest absence, and around 30% of its pupils arriving with low prior attainment. On the same panel model’s contextualised results, a disadvantaged pupil leaving BACA in 2024–25 did about 5.7 Attainment 8 points better than a comparable pupil at a similar school elsewhere in England, and about 3.1 points better on average over four years — the strongest result in the city for disadvantaged pupils, and far larger than the fraction of a point that changing a school’s disadvantage mix would be expected to deliver. Ofsted rated the school Good in every area in April 2025.

Section 2.6 applies to BACA as to any school: a single year of disadvantaged results is one draw, and the four-year figure is the one to lean on. It is still well above anything the redistribution of pupils could deliver.

What follows for this document. Several options in Section 11 — Longhill at Elm Grove, Whitehawk back in Longhill’s catchment — raise the segregation index because they put more disadvantaged children in the same school. The council’s policy aim treats that as a cost. The national evidence does not: provided the school is organised and funded for the intake it gets, a more concentrated intake is not a worse one for its disadvantaged pupils. The segregation index is reported here because the council asks for it, not because it measures harm.

2.4 How much can a school change at all?

There is a prior question underneath all of this, and it is the one that bears most directly on what the council can achieve by moving children between schools.

Since Coleman et al. (1966), study after study has found that the share of variation in pupils’ outcomes attributable to differences between schools — as opposed to differences in the pupils they admit and the circumstances those pupils bring with them — is somewhere in the order of 8 to 15 per cent. The landmark English study settled on about a tenth (Smith and Tomlinson 1989), and the finding has held up across countries and phases (Teddlie and Reynolds 2000).

The multilevel model behind How to Pull the Right Lever decomposes that variance directly, and lands in the same place.

Show code
sed <- bh_data("school_effect_decomp.rds")
lev <- bh_data("school_leverage.rds")
LV  <- lev$leverage

GRP_COL <- c("Inherited (exogenous)"      = "#49a0c4",
             "Absence: structural"        = "#7FB3D5",
             "Absence: school-reachable"  = "#F2A03D",
             "Persistent school effect"   = "#6A4C93",
             "Workforce"                  = "#ED357D",
             "Place / noise"              = "#BBBBBB")

ABS_LAB <- "Absence (part inherited, part school-reachable)"
EXO_LAB <- "Inherited: cohort, prior attainment, neighbourhood"

# Absence is published inside the exogenous block, but it is the one
# term that is partly within a school's control. Split it out as its own
# row. The published totals are kept; the share of the exogenous block
# that is absence is taken from the fitted model, and the split of that
# absence into structural and school-reachable from the stage-1 model.
prep <- function(d, who_lab) {
  r        <- LV[LV$who == who_lab, ]
  abs_frac <- r$absence / r$exogenous
  exo_tot  <- d[["Share of total %"]][grepl("^Exogenous", d$Component)]
  abs_tot  <- exo_tot * abs_frac

  bind_rows(
    d %>%
      filter(!grepl("^Exogenous", Component)) %>%
      transmute(Component, share = `Share of total %`,
                grp_type = case_when(
                  grepl("^Endogenous", Component) ~ "Workforce",
                  Component == "School (persistent, unexplained)" ~ "Persistent school effect",
                  TRUE ~ "Place / noise")),
    tibble(Component = EXO_LAB, share = exo_tot - abs_tot,
           grp_type = "Inherited (exogenous)"),
    tibble(Component = ABS_LAB,
           share = c(abs_tot * lev$struct_share, abs_tot * lev$school_share),
           grp_type = c("Absence: structural", "Absence: school-reachable"))
  ) %>%
    mutate(who = who_lab,
           Component = sub("^Endogenous, within-school \\(workforce\\)$",
                           "Workforce (the part a school hires)", Component))
}

decomp <- bind_rows(prep(sed$decomp_all, "All pupils"),
                    prep(sed$decomp_dis, "Disadvantaged pupils"),
                    prep(sed$decomp_non, "Non-disadvantaged pupils")) %>%
  mutate(who = factor(who, c("All pupils", "Disadvantaged pupils",
                             "Non-disadvantaged pupils")),
         grp_type = factor(grp_type, levels = names(GRP_COL)))

# Order rows by the all-pupil total for each component, so the three
# panels read in the same order and can be compared row by row.
ord <- decomp %>%
  filter(who == "All pupils") %>%
  group_by(Component) %>% summarise(t = sum(share), .groups = "drop") %>%
  arrange(t) %>% pull(Component)
decomp <- decomp %>% mutate(Component = factor(Component, ord))

# One label per row, placed at the row total rather than per segment, so
# the split absence bar reads as a single quantity.
tot_lab <- decomp %>%
  group_by(who, Component) %>% summarise(share = sum(share), .groups = "drop")

ggplot(decomp, aes(share, Component, fill = grp_type)) +
  geom_col(width = 0.72) +
  geom_text(data = tot_lab, inherit.aes = FALSE, hjust = -0.15, size = 3,
            mapping = aes(x = share, y = Component,
                          label = sprintf("%.1f%%", share))) +
  facet_wrap(~ who, ncol = 1) +
  scale_fill_manual(values = GRP_COL, name = NULL) +
  scale_x_continuous(limits = c(0, 52), expand = expansion(mult = c(0, 0.02))) +
  guides(fill = guide_legend(nrow = 2)) +
  labs(x = "Share of total outcome variance (%)", y = NULL,
       title = "Most of what separates schools' results arrives with the children",
       subtitle = "Absence shown separately: it is the one term that is partly inherited and partly within a school's reach",
       caption = "Sources: How to Pull the Right Lever, school effect decomposition; RPE paper two-stage absence model.") +
  theme_bh(11) +
  theme(legend.position = "bottom", panel.grid.major.y = element_blank(),
        axis.text.y = element_text(size = 8),
        strip.text = element_text(face = "bold", hjust = 0))
Figure 6: Where the variance in school-level Attainment 8 sits, decomposed from the multilevel model, for all pupils and for the two pupil groups separately. Absence is shown as its own row rather than buried in the inherited block, split into the structural share a first-stage model predicts from intake and area, and the school-level residual it does not. The persistent school effect is an upper bound: it bundles genuine school ethos and management with anything unmeasured about the catchment, such as cultural capital or parental motivation.

For all pupils, the part a school directly controls through the staff it hires accounts for about 2.5% of the variance. What it inherits with the intake — cohort composition, prior attainment, neighbourhood — accounts for roughly 42%. Even the persistent school effect, at 15.6%, is an upper bound: it is what is left once everything measured is accounted for, so it bundles genuine school ethos and leadership together with anything unmeasured about the catchment — cultural capital, parental motivation, and the rest.

Absence has its own row because it is the one term that will not sit in either camp. The published decomposition files it with the inherited block, but a school’s attendance is partly a fact about its intake and partly a fact about what it does. The next section takes that apart, and it turns out to matter more than anything else on the chart.

For disadvantaged pupils — the group any mixing policy is aimed at — the workforce term falls to 1.1%, against 56.8% inherited, with a persistent school effect of 10.5%. The lever gets smaller, not larger, precisely where the policy is pointed. For non-disadvantaged pupils it is 3.8% against 60.6% — so the pattern is not an artefact of one group, and the school-controllable share is smallest for the children the policy is meant to help.

2.5 Absence is the term that moves

The published decomposition files absence inside the exogenous block — “cohort, prior attainment, absence, neighbourhood” — as though a school simply receives its attendance rate. The two-stage work in the RPE paper shows that it does not. A first-stage model of school absence on intake, area and segregation explains only about half of its variation; the rest is a residual that pastoral systems, attendance officers, family liaison and persistent-absentee follow-up act on directly. That is why it is drawn as its own split row above, and it is worth being precise about the size of each part.

Across 12,199 school-years, that first stage accounts for 53% of the variation in absence. The other 47% is school-level residual. Absence is not one thing: roughly half of it is structural and outside a school’s reach, and roughly half is not.

That changes the arithmetic, because absence is the largest single term in the exogenous block — 20.2% of total variance for all pupils, and 24.8% for disadvantaged pupils, where it is the biggest component of all.

Show code
LV %>%
  transmute(who = factor(who, LV$who),
            `Workforce` = workforce,
            `Absence, school-reachable` = absence_school,
            `Absence, structural` = absence_structural,
            `Other inherited` = exogenous - absence) %>%
  pivot_longer(-who, names_to = "part", values_to = "pct") %>%
  mutate(part = factor(part, c("Workforce", "Absence, school-reachable",
                               "Absence, structural", "Other inherited"))) %>%
  ggplot(aes(pct, fct_rev(who), fill = part)) +
  geom_col(width = 0.62) +
  geom_text(aes(label = ifelse(pct >= 3, sprintf("%.0f%%", pct), "")),
            position = position_stack(vjust = 0.5), size = 3.1,
            colour = "white", fontface = "bold") +
  scale_fill_manual(values = c("Workforce" = "#ED357D",
                               "Absence, school-reachable" = "#F2A03D",
                               "Absence, structural" = "#7FB3D5",
                               "Other inherited" = "#49a0c4"), name = NULL) +
  guides(fill = guide_legend(nrow = 2)) +
  labs(x = "Share of total outcome variance (%)", y = NULL,
       title = "What a school can actually act on is mostly attendance",
       subtitle = "Fixed-effect variance only; the random-effect components are omitted here",
       caption = "Sources: How to Pull the Right Lever; RPE paper two-stage absence model.") +
  theme_bh(11) +
  theme(legend.position = "bottom", panel.grid.major.y = element_blank())
Figure 7: The same variance, re-cut by what a school can actually act on. The absence term is split into the structural share the first-stage model predicts and the school-level residual it does not.

Adding the school-controllable half of absence to the workforce term gives a school-reachable share of 12.0% for all pupils, 12.5% for disadvantaged pupils and 12.2% for non-disadvantaged — strikingly consistent, and sitting squarely inside the 8–15 per cent that sixty years of school-effectiveness research has settled on.

NoteA small discrepancy worth naming

The workforce figures in this section are recomputed from the fitted models rather than read off the published table, so that the parts sum to the totals quoted beside them. They come out slightly lower than the published version — 2.5% against 3.3% for all pupils — because the cached table and the model objects available here are not from quite the same fit. The difference is under a percentage point and changes nothing in the argument, but the two numbers are not identical and it would be worse to present them as if they were.

So the earlier conclusion needs restating, more precisely and more usefully:

ImportantThe corrected version

A school can reach about 12% of the variance in its results — not the 3.3% the workforce term alone suggests. But almost all of that leverage runs through attendance, not through who is sitting in the classroom.

For disadvantaged pupils the point is sharper still. The workforce term is 0.8%; the school-reachable absence term is 11.7% — 14 times larger. The lever that works for the children the policy is aimed at is attendance, and it is not an admissions lever.

Moving children between schools does not touch either term. It changes which building a child attends without changing their attendance, and without changing the intake characteristics that account for the rest. That is the case against expecting much from redistribution — and, equally, the case for expecting a great deal from attendance work.

NoteDoes the shared variance change this?

It is a fair challenge. The two fixed blocks are not independent — schools with difficult intakes also tend to have less stable workforces — so the variance they share has to be allocated somehow, and the answer depends on how.

The decomposition above credits each block with its covariance against the total. An alternative is to split the shared part evenly, as a Shapley decomposition does. On the fitted models the two blocks correlate at about +0.3 to +0.4, and the choice is not immaterial: the workforce share moves from 2.5% to 7.5% for all pupils, from 0.8% to 3.1% for disadvantaged pupils, and from 3.2% to 8.1% for non-disadvantaged pupils — roughly a threefold difference.

It does not, however, change the conclusion. Under the more generous allocation the workforce share is still about eight times smaller than the inherited share for all pupils, and about seventeen times smaller for disadvantaged pupils. The random-effect components — the persistent school effect among them — are untouched either way, because variance components in a multilevel model are orthogonal by construction and have no shared part to argue over.

The figures quoted here are the published ones, on the covariance split. Read the workforce term as at most a few per cent rather than as a precise quantity.

Show code
tibble(term = LAB[three], share = shap_share) %>%
  ggplot(aes(reorder(term, share), share)) +
  geom_col(fill = "#2166ac", width = 0.6) +
  geom_text(aes(label = fmt_pct(share, 0)), hjust = -0.18, size = 3.4) +
  coord_flip() +
  scale_y_continuous(limits = c(0, max(shap_share) * 1.22)) +
  labs(x = NULL, y = "Share of explained variance (%)",
       title = "And within that inherited share, how it divides",
       subtitle = sprintf("Shapley decomposition of school-level Attainment 8 (R² = %.2f)", r2_all),
       caption = "Source: DfE performance tables.") +
  theme_bh(11) +
  theme(panel.grid.major.y = element_blank())
Figure 8: How the explained variance in school-level Attainment 8 divides between the three main intake measures, by Shapley decomposition.

Two things follow, and together they are the most important finding in this section for the decisions in front of the council.

Moving children between schools operates on the small term. Redistributing pupils changes which school a child attends. It does not change the intake characteristics and social circumstances that account for 62.2% of the variance, and it does not touch the workforce that accounts for 3.3%. A policy of social mixing through admissions is therefore working on the smaller term by construction — not because mixing is undesirable, but because the arithmetic limits what it can deliver.

The next section qualifies this in an important way. One large item inside that 62.2% is not inherited at all.

And the measured drivers cannot be cleanly separated anyway. Of the 68% of school-level variation the three measures jointly explain, 74% is variance they share rather than variance any one of them owns. Disadvantage, absence and prior attainment correlate at -0.68, -0.65 and 0.53 because they are substantially the same phenomenon seen three ways. Any policy that targets one of them in isolation — and an admissions policy targets disadvantage in isolation — is pulling on a rope attached to the other two.

ImportantWhat this does and does not say

It does not say schools do not matter. A persistent school effect of 15.6% across three and a half thousand secondary schools is a great deal, and section 2.3 showed Brighton’s schools mostly performing above what their intakes predict. Nor does it say that segregation between schools is acceptable; there are fairness arguments for mixed intakes that have nothing to do with attainment.

What it says is narrower and harder to argue with: the council should not expect a redistribution policy to move attainment much, because the mechanism it operates through is the smallest of the terms available. The instruments that reach the larger terms — attendance above all — are not admissions instruments at all.

2.6 A school’s disadvantaged results are a noisy signal

One row on that chart is easy to skip past and should not be. For disadvantaged pupils, transient cohort-year noise is the second-largest component of all — larger than the persistent school effect, larger than absence, larger than anything except what the children bring with them.

Show code
noise %>%
  pivot_longer(-who, names_to = "part", values_to = "pts") %>%
  mutate(part = factor(part, c("persistent", "transient"),
                       c("Persistent difference between schools",
                         "Year-to-year noise within a school"))) %>%
  ggplot(aes(pts, fct_rev(who), fill = part)) +
  geom_col(position = position_dodge(width = 0.72), width = 0.66) +
  geom_text(aes(label = sprintf("%.1f", pts)),
            position = position_dodge(width = 0.72), hjust = -0.25, size = 3.2) +
  scale_fill_manual(values = c("Persistent difference between schools" = "#6A4C93",
                               "Year-to-year noise within a school" = "#BBBBBB"),
                    name = NULL) +
  scale_x_continuous(limits = c(0, max(noise$transient) * 1.25),
                     expand = expansion(mult = c(0, 0.02))) +
  guides(fill = guide_legend(nrow = 2)) +
  labs(x = "Attainment 8 points (standard deviation)", y = NULL,
       title = "For disadvantaged pupils, the noise is bigger than the signal",
       caption = "Source: How to Pull the Right Lever, school effect decomposition.") +
  theme_bh(11) +
  theme(legend.position = "bottom", panel.grid.major.y = element_blank())
Figure 9: The persistent difference between schools against the year-to-year wobble within them, in Attainment 8 points. For disadvantaged pupils the wobble is the larger of the two.

For all pupils the school signal wins: the persistent difference between schools is 3.4 Attainment 8 points against 2.7 points of year-to-year noise. For disadvantaged pupils it reverses — 2.7 points of persistent difference against 3.6 points of noise.

The reason is arithmetic rather than mysterious. A secondary school’s disadvantaged cohort is small — often forty or fifty pupils — and small numbers move about. A handful of children having a bad year, or a good one, shifts the published figure by more than any real difference in what the school does.

The consequence is uncomfortable and worth stating plainly. Two schools doing genuinely equally well by their disadvantaged pupils will differ by around 5 Attainment 8 points in a given year for no reason at all. That is larger than most of the gaps that get argued about. A single year of disadvantaged results is not evidence about a school; it is one draw from a distribution.

ImportantWhat follows for how the city reads its own data

Never judge a school on one year of disadvantaged results. Three or more years, averaged, or the comparison is mostly noise. This applies to league tables, to consultation documents, and to the case made for or against any individual school.

Do not expect to detect an admissions policy’s effect this way either. If redistribution moves disadvantaged attainment by a point or two — which section 2.4 suggests is optimistic — that signal sits well inside 5 points of year-to-year variation. The policy could work exactly as intended and be invisible for years; it could also fail and appear to succeed. Evaluating it will need attendance and intake measures, not headline outcomes.

This bears directly on the free school meals review. The FSM admissions priority took effect for entry in September 2023. Those children entered Year 7 that autumn and will not sit GCSEs until summer 2028. So a review conducted now is not working with thin outcome evidence — it is working with none: not one cohort admitted under the policy has yet reached an examination. Even in 2028 there will be a single cohort, and a single cohort of disadvantaged pupils in one catchment carries the 3.6-point year-to-year variation described above.

That is not an argument against reviewing the policy. Admission patterns, intake composition by catchment, and the operation of the criteria itself can all be examined now, and section 5 does some of that. It is an argument against reviewing it on attainment, or against any conclusion of the form “results have or have not improved since the policy came in”. No honest analysis can support such a claim from the data that exists, in either direction.

This compounds the problem in section 2.3. Families are choosing on a measure that is largely a description of intake, and that measure is noisiest precisely for the pupils whose schools are most often criticised on it.

3 Demographic futures

The single most important fact about this system is that it is shrinking, and that the shrinkage is already locked in. The children who will enter Year 7 in 2033 have already started primary school. Their number is not a forecast in any meaningful sense — it is a count, adjusted for a transfer rate that has been remarkably stable for a decade.

3.1 What has already happened

Year 7 offers across the city peaked at 2,509 in 2019. In 2026 the figure was 2,250 — a fall of 10.3% from the peak. That decline is not a projection. It has happened.

Show code
bind_rows(
  y7  %>% transmute(year, offers = y7_offers, phase = "Year 7 offers"),
  rec %>% transmute(year, offers = rec_offers, phase = "Reception offers")) %>%
  ggplot(aes(year, offers, colour = phase)) +
  geom_line(linewidth = 1.1) +
  geom_point(size = 2) +
  scale_colour_manual(values = c("Reception offers" = "#2c7fb8",
                                 "Year 7 offers" = "#b2182b"), name = NULL) +
  scale_y_continuous(labels = label_comma(), limits = c(0, NA)) +
  labs(x = NULL, y = "Offers made",
       title = "Both phases are past their peak",
       subtitle = "Reception offers turn down first, and Year 7 follows seven years later",
       caption = "Source: BHCC published allocation factsheets.") +
  theme_bh()
Figure 10: Reception and Year 7 offers made across Brighton & Hove. The reception line leads the Year 7 line by seven years, so it is a preview of secondary demand rather than a separate story.

3.2 The cohort already in school

The transfer rate from a reception cohort to the Year 7 cohort seven years later has been strikingly stable. Across the six cohorts where both ends are observable, 88.4% of reception offers reappear as Year 7 offers, with a standard deviation of just 1.6%. The city loses about one child in nine between ages four and eleven — to independent schools, to moves out of the area, and to schools across the boundary.

Applying that rate to reception cohorts already in school gives a projection that depends on almost no assumptions:

Show code
ggplot() +
  geom_ribbon(data = proj, aes(entry_year, ymin = lo, ymax = hi),
              fill = "#b2182b", alpha = 0.18) +
  geom_line(data = proj, aes(entry_year, central),
            colour = "#b2182b", linewidth = 1.1, linetype = "22") +
  geom_point(data = proj, aes(entry_year, central), colour = "#b2182b", size = 2) +
  geom_line(data = y7, aes(year, y7_offers), colour = "grey25", linewidth = 1.1) +
  geom_point(data = y7, aes(year, y7_offers), colour = "grey25", size = 2) +
  annotate("text", x = 2016, y = 2480, label = "Observed", colour = "grey25",
           hjust = 0, fontface = "bold", size = 3.6) +
  annotate("text", x = 2029.5, y = 2180, label = "Projected from\nchildren already in school",
           colour = "#b2182b", hjust = 0, size = 3.4, lineheight = 0.95) +
  scale_y_continuous(labels = label_comma(), limits = c(0, NA)) +
  labs(x = NULL, y = "Year 7 offers",
       title = sprintf("A further %s fall is already in the primary schools",
                       fmt_pct(abs(drop_pct), 0)),
       subtitle = sprintf("From %s in %s to about %s by %s",
                          fmt_n(now$y7_offers), now$year,
                          fmt_n(last_proj$central), last_proj$entry_year),
       caption = "Sources: BHCC allocation factsheets; transfer rate estimated from six observed cohorts.") +
  theme_bh()
Figure 11: Year 7 demand projected from reception cohorts already in school. The band is the range implied by the observed year-to-year variation in the transfer rate.

By 2033 the city should expect about 1,750 Year 7 children, against 2,250 today — a further fall of 22%. Against a current published admission capacity of about 2,515 places, that is roughly 765 surplus places — the equivalent of two and a half average-sized secondary schools standing empty.

3.3 Where the council’s forecast differs

The council publishes its own catchment-level forecasts. They are consistently higher than what the primary cohorts support, and the gap widens with distance.

Show code
rc$comparison %>%
  mutate(gap = ons_demand - reception_based) %>%
  ggplot(aes(entry_year, gap)) +
  geom_col(fill = "#7b3294", alpha = 0.85, width = 0.65) +
  geom_hline(yintercept = 0, colour = "grey30") +
  geom_text(aes(label = sprintf("%+.0f", gap),
                vjust = ifelse(gap >= 0, -0.4, 1.3)), size = 3.2) +
  scale_y_continuous(expand = expansion(mult = c(0.15, 0.18))) +
  labs(x = "Year of Year 7 entry", y = "Council forecast minus cohort projection",
       title = "The council expects more children than the primary registers hold",
       subtitle = "Difference in Year 7 places, city-wide",
       caption = "Sources: BHCC published forecasts; reception-based projection as above.") +
  theme_bh()
Figure 12: The council’s own demand forecast against a projection built from reception cohorts already in school. Positive values mean the council expects more children than the primary registers contain.

The city-wide figure hides a good deal. Broken down by catchment, and set against the council’s own two successive forecasts, the picture is less tidy.

Show code
# Four series on one set of axes. The council labels its catchments
# differently from the model tables, so everything is converted to model
# keys first - see as_model_catchment() in R/00_core.R.
CATCH_DISPLAY <- c(CATCH_LABELS[unname(CATCH_FROM_MODEL)], "Religious schools")
names(CATCH_DISPLAY) <- c(names(CATCH_FROM_MODEL), "Religious schools")

council25 <- rc$council %>%
  transmute(catchment = as_model_catchment(CatchmentGroup),
            entry_year, offers = council, PAN,
            series = "Council forecast (Oct-25)")

council24 <- bh_data("council_forecast_oct24.csv") %>%
  transmute(catchment = as_model_catchment(CatchmentGroup),
            entry_year, offers = council_oct24,
            series = "Council forecast (Oct-24, superseded)")

series <- bind_rows(
  rc$observed_by_catchment %>%
    transmute(catchment, entry_year, offers = y7_offers,
              series = "Actual offers"),
  rc$by_catchment %>%
    transmute(catchment, entry_year, offers = projected,
              series = "Our projection (from primary cohorts)"),
  council25 %>% select(-PAN),
  council24) %>%
  filter(!is.na(catchment), entry_year >= 2016) %>%
  mutate(panel = factor(unname(CATCH_DISPLAY[catchment]),
                        unname(CATCH_DISPLAY)),
         series = factor(series, c(
           "Actual offers", "Our projection (from primary cohorts)",
           "Council forecast (Oct-25)",
           "Council forecast (Oct-24, superseded)")))

stopifnot(!any(is.na(series$panel)))

pan_lines <- council25 %>%
  distinct(catchment, PAN) %>%
  mutate(panel = factor(unname(CATCH_DISPLAY[catchment]), unname(CATCH_DISPLAY)))

ggplot(series, aes(entry_year, offers, colour = series)) +
  geom_hline(data = pan_lines, aes(yintercept = PAN),
             linetype = "dotted", colour = "grey30", linewidth = 0.45) +
  geom_line(linewidth = 0.75) +
  geom_point(size = 1.2) +
  facet_wrap(~ panel, ncol = 2, scales = "free_y") +
  scale_colour_manual(values = c(
    "Actual offers"                          = "#1f78b4",
    "Our projection (from primary cohorts)"  = "#33a02c",
    "Council forecast (Oct-25)"              = "#e31a1c",
    "Council forecast (Oct-24, superseded)"  = "#fb9a99"), name = NULL) +
  scale_y_continuous(limits = c(0, NA)) +
  guides(colour = guide_legend(nrow = 2)) +
  labs(x = NULL, y = "Year 7 offers",
       title = "Every catchment, four views of the same future",
       subtitle = "Dotted line is the catchment admission number",
       caption = "Sources: BHCC allocation factsheets; BHCC forecasts, Appendices 6 and 7.") +
  theme_bh(11) +
  theme(legend.position = "top", strip.text = element_text(face = "bold"),
        axis.text.x = element_text(angle = 45, hjust = 1))
Figure 13: Year 7 offers for each catchment: what has actually happened, our projection from primary cohorts already in school, and the council’s two most recent forecasts of the same years. Dotted line is the catchment’s admission number.

Two things stand out.

Our projection sits below the council’s in 4 of the 6 catchments — the city-wide gap of the previous chart, distributed. The closest agreement is in Patcham, where the two differ by under a place a year on average.

And the council’s own view has moved a long way in a single year. Between the Oct-24 and Oct-25 forecasts, for the years both cover, the figure for Varndean / Dorothy Stringer moved by an average of -18 places a year, with a largest single-year revision of 30.

The direction is the part worth noting. Every one of the six catchments was revised downward, none upward. That is not forecasting noise; it is a consistent correction in one direction, and it points the same way as the projection from primary cohorts. The council’s own figures are moving toward the lower number — they have not arrived at it yet, and decisions with twenty-year consequences are being taken against a quantity still in motion.

3.4 The forecast is not wrong in total. It is wrong by place

The comparison above sets one projection against another, and a reader is entitled to ask why either should be believed. For 2026 that question can be settled, because the offers have been made. Both the council’s forecast and our projection can be checked against what happened.

Show code
err26 <- rc$observed_by_catchment %>%
  filter(entry_year == 2026) %>%
  select(catchment, actual = y7_offers) %>%
  inner_join(council25 %>% filter(entry_year == 2026) %>%
               select(catchment, PAN, council = offers), by = "catchment") %>%
  left_join(rc$by_catchment %>% filter(entry_year == 2026) %>%
              select(catchment, ours = projected), by = "catchment") %>%
  mutate(council_err = council - actual, ours_err = round(ours - actual)) %>%
  arrange(desc(council_err))

err26 %>%
  transmute(Catchment = unname(CATCH_DISPLAY[catchment]),
            PAN, Actual = actual,
            `Council forecast` = council,
            `Council error` = sprintf("%+d", council_err),
            `Our projection` = round(ours),
            `Our error` = sprintf("%+d", ours_err)) %>%
  knitr::kable(align = "lrrrrrr")
Table 1: Year 7 offers in 2026: what the council forecast, what our projection gave, and what actually happened.
Catchment PAN Actual Council forecast Council error Our projection Our error
Longhill 210 81 162 +81 110 +29
PACA (Portslade Aldridge) 220 183 218 +35 186 +3
Patcham 225 204 194 -10 205 +1
BACA (Brighton Aldridge) 180 146 128 -18 122 -24
Varndean / Dorothy Stringer 630 630 601 -29 610 -20
Hove Park / Blatchington Mill 510 466 424 -42 470 +4
NoteRead our column with more suspicion than the council’s

The council’s forecast is a genuine prediction: published in advance, checked here against what happened. Ours is not, quite. The retention ratio it uses is fitted on cohorts that include the 2019 reception group, which is the 2026 Year 7 group — so 2026 is partly inside the window our method was calibrated on, and its apparent accuracy is flattered. The honest comparison is between the council’s forecast and the outturn; our column is there for orientation, not as a claim to have done better.

City-wide the council’s 2026 forecast was close: it was +17 places out in total, on a cohort of over two thousand. By catchment it was not close at all. The individual errors sum to 215 places in absolute terms — roughly 13 times the net error. The council is forecasting the right number of children and putting a great many of them in the wrong place.

Two catchments carry most of that, and they miss in opposite directions.

Longhill was forecast 162 and received 81 — 2.0 times too high. This is a catchment whose admission number is 210 and which filled 39% of it. The council’s own method already applies the largest leakage deduction in the city to this catchment, at over 22 per cent, and it is still not close.

Hove Park / Blatchington Mill was forecast 424 and received 466 — 42 places too low, the largest under-estimate of any catchment.

WarningQuestion for the council 1: why is the forecast wrong in these two places, and in opposite directions?

The Hove Park / Blatchington Mill under-estimate has a candidate explanation. The neighbouring PACA catchment was over-forecast by 35 places — close to the 42 that Hove Park / Blatchington Mill was under by. Children the model expected to appear in Portslade appearing in Hove instead would account for much of it. That is testable, and the council holds the data to test it.

Longhill has no such explanation. Its neighbours were not over-forecast in a way that could absorb an +81-place error — BACA was -18 and Patcham -10. The children the council expected to enrol at Longhill did not turn up in an adjacent catchment. They did not turn up at all.

So the questions are:

  1. What accounts for the Longhill over-forecast, if not cross-catchment movement? Out-of-city flow to Lewes district and the independent sector are the obvious candidates, and the council holds allocation records that would separate them.
  2. Is the leakage rate for the Longhill catchment being estimated on data old enough to predate the change it is meant to capture? A rate calibrated on historical patterns will lag a shift that is still happening.
  3. Is the Hove Park / Blatchington Mill under-estimate the counterpart of the PACA over-estimate? If so, the model’s catchment-of-residence assumption is diverging from where families actually apply, and the two errors should be corrected together rather than separately.

These matter because the Longhill decision is being taken against the forecast, and the forecast has been wrong about Longhill by a factor of 2.0 in the most recent year it can be checked against.

The city-wide gap is modest in most years. Broken down by catchment it is not, and Longhill is where it opens widest.

Show code
lh_join %>%
  select(entry_year, Council = council, `From primary cohorts` = projected) %>%
  pivot_longer(-entry_year, names_to = "series", values_to = "children") %>%
  ggplot(aes(entry_year, children, colour = series)) +
  geom_hline(yintercept = rc$council_lh_pan, linetype = "31", colour = "grey45") +
  annotate("text", x = min(lh_join$entry_year), y = rc$council_lh_pan + 7,
           label = sprintf("Admission number (%s)", rc$council_lh_pan),
           hjust = 0, size = 3.1, colour = "grey35") +
  geom_line(linewidth = 1.1) + geom_point(size = 2.4) +
  scale_colour_manual(values = c("Council" = "#7b3294",
                                 "From primary cohorts" = "#008837"), name = NULL) +
  scale_y_continuous(limits = c(0, NA)) +
  labs(x = "Year of Year 7 entry", y = "Children",
       title = "Longhill: the two forecasts disagree, and both sit below the admission number",
       subtitle = sprintf("Mean gap of %.0f children a year",
                          mean(lh_join$gap, na.rm = TRUE)),
       caption = "Sources: BHCC catchment forecasts; reception-based projection.") +
  theme_bh()
Figure 14: Longhill catchment: the council’s forecast, the projection from its own primary cohorts, and the school’s admission number.

The council’s forecast for the Longhill catchment runs an average of 40 children a year above what that catchment’s own primary cohorts support. Both series sit below the school’s admission number of 210 in every year. This matters because decisions about Longhill’s future are being taken against the higher of two numbers, and the higher one is the one the primary registers do not support.

3.5 The Peacehaven accession

The catchment system reaches beyond the city boundary. The Peacehaven and Telscombe area contributes 14 LSOAs and about 215 cohort-aged children, roughly 9% of the total the model covers.

In headcount terms this is close to self-supporting: Peacehaven Community School has an admission number of 180, against about 215 children in its area. The difficulty is not the arithmetic but the direction of travel. These families sit at the eastern end of a transport network that runs east–west along the coast, and section 4 shows that they are among the least well served in the whole study area — not because they are far from a school, but because they are far from most schools, and therefore have the fewest alternatives if their first choice is full.

4 How reachable are the schools?

Almost all of the public argument about Brighton’s schools is conducted in terms of admissions rules — catchments, priorities, tie-breaks. Underneath the rules sits a physical constraint that no rule can undo: how long it actually takes a eleven-year-old to get to each school on a bus. This section establishes that constraint before section 5 returns to the rules.

All journey times here are routed with r5r over the merged street network and bus timetable for the area, for a weekday morning arrival. They include walking to the stop, waiting, the ride itself, and any transfer.

4.1 What counts as a long journey to school?

Before mapping anything it is worth fixing what these minutes mean, because a number like “twenty-eight minutes” is only interpretable against something.

Benchmark Figure Source
Average one-way school trip, England 19 minutes National Travel Survey, five-year average to 2019
Average one-way trip distance 2.4 miles as above
Statutory maximum, secondary age 75 minutes each way DfE home-to-school travel guidance
Statutory maximum, primary age 45 minutes each way as above

Two cautions about the 19-minute average. It covers ages 5 to 16 together, and secondary journeys are longer than primary ones, so it understates the secondary figure. And it counts all modes, including the car — which carries 30% of secondary school trips nationally against 18% by local bus. The journeys modelled here are walking and bus only, so they describe the trip facing a family without a car. That is the right comparison for this document, but it is not a like-for-like one with the national number.

The 75-minute figure is firmer. It is the Department for Education’s guidance on how long a home-to-school journey should reasonably take for a child of secondary age, each way, including walking to a pick-up point.

ImportantA correction to the routed times, and why it matters

The routed matrix returns a walk-and-bus itinerary for every neighbourhood-school pair. Where the bus network is awkward it returns a poor one: 19% of the 2,299 neighbourhood-school pairs were modelled as taking longer than simply walking the distance would. In the worst case a journey of about four kilometres came out at 99 minutes.

That is a property of the router, not of the city. A family facing a 99-minute bus ride to a school four kilometres away walks instead. So every journey is now the lesser of the routed bus time and the time it would take to walk, and the bus is used only where it is genuinely faster.

The walking pace is not invented. Kent et al. (2026), a systematic review of active school travel, finds children’s mean walking trip to school is 1,030 metres in 14.2 minutes — 4.35 km/h — and that is the speed used, with a circuity factor of 1.3 to turn straight-line distance into street distance.

NoteThis is a ceiling, not a claim about how children travel

The same review finds children walk about a kilometre on average and seldom beyond 1,800 metres. Capping a four-kilometre journey at its walking time does not assert that a child walks four kilometres. It asserts that no journey should be modelled as costing more than walking would, because a family with that option would take it. The cap is an upper bound on modelled cost, not a mode assumption.

The correction is not cosmetic, and it is applied upstream, in the open model’s own inputs — so every section of this document and of the technical companion now uses the corrected costs. Sections 7 and 8 were re-run against them. It is worth being open that an earlier version of this analysis overstated the headline figures:

Uncorrected With the walk fallback
Journey to the nearest school, child-weighted 22.8 min 19.9 min
Neighbourhoods reaching no place within 30 min 37 29
Children in those neighbourhoods 474 385

The corrected figure is a useful check in itself. A child-weighted mean of 19.9 minutes to the nearest school sits almost exactly on the national average of 19 minutes — which is reassuring for a model built from timetables and street networks rather than from observed journeys.

4.1.1 Against the statutory guidance

The DfE limit applies to the journey a child actually has to make, so the test is the time to the school their catchment entitles them to — not to the nearest school of any kind.

Show code
acc$by_catch_stat %>%
  transmute(Catchment = unname(CATCH_DISPLAY[catchment]),
            `Mean minutes` = sprintf("%.1f", mean_min),
            `Worst zone` = sprintf("%.0f", max_min),
            `Zones over 45 min` = over_45,
            `Children affected` = sprintf("%.0f", children_over_45)) %>%
  knitr::kable(align = "lrrrr")
Table 2: Journey to the nearest school in the child’s own catchment, by catchment, against the DfE guidance.
Catchment Mean minutes Worst zone Zones over 45 min Children affected
Varndean / Dorothy Stringer 26.2 54 6 59
Longhill 25.8 40 0 0
BACA (Brighton Aldridge) 23.0 33 0 0
Hove Park / Blatchington Mill 22.4 38 0 0
Patcham 21.9 74 1 15
PACA (Portslade Aldridge) 18.3 33 0 0
NA 17.9 40 0 0

No neighbourhood in the city exceeds the 75-minute secondary limit to its own catchment school. The worst is 74 minutes. On that test the city passes, and it is worth saying so plainly given how much of this document is critical.

7 zones — about 74 children — exceed the 45-minute figure that applies to primary-age children. That is not a breach for secondary pupils, but it is a reasonable marker of a long journey, and it identifies where the pressure sits.

4.2 Journey time to a single school

Every figure in this section comes from routing each origin to each school over a street network and a bus timetable. The details matter, because they set the limits of what the numbers can support.

What was routed. r5r over a merged OpenStreetMap extract and a GTFS bus timetable, from 7,896 postcode origins to 15 destinations — the eleven schools in the expanded authority plus three East Sussex schools that Peacehaven families genuinely choose between, and one hypothetical site. Postcode results are then aggregated to the LSOA-by-catchment zones used everywhere else, weighted by child population.

The parameters. Walking and public transport combined; up to 60 minutes of walking; trips capped at 120 minutes; the median of a 1-minute departure window. Arrival is set for 08:00 at every school except Longhill, which uses 07:30 because its buses run earlier. That asymmetry is deliberate.

Two extracts, merged. Geofabrik splits its Sussex data along the county boundary, and the split runs straight through this study area: Brighton & Hove sits in the west extract, the Peacehaven expansion area in the east. r5r accepts exactly one .pbf file, so the two were merged into sussex-merged.osm.pbf. Supplying both separately does not error — it silently returns nothing.

The timetable is a timetable. This is the single most important caveat. The GTFS feed (gtfs.zip, from the Department for Transport’s Open Bus Data Service) describes the service that was scheduled, not the service that ran. The routing date is chosen automatically as the weekday with the most services active in the feed, because it is a multi-period export and only one window carries a full timetable. So these are timetabled journey times, not measured ones. They do not know about a bus that failed to arrive, one that was already full, or one running ten minutes late.

What is missing entirely. No school buses, no parental lifts, no cycling, no walking routes children actually prefer to the shortest one. Car journeys are excluded by design — the question here is what a family without a car faces — but nationally the car carries about 30% of secondary school trips, so these times describe a subset of families rather than the average one.

Where the school gate is. r5r snaps each school to the nearest point on the street network, and which point that is can matter more than it should. Dorothy Stringer and Varndean are 470 metres apart, yet the matrix makes Stringer slower from most of the city by a median of about five and a half minutes. That may be a real difference in bus access or an artefact of the snapping; it has not been established which, and it is large enough to affect anything said about Stringer specifically. Longhill’s position is untouched by it, because the anomaly sits between two central schools.

Bus stop coverage was checked at five points across the study area:

Show code
bmeta$gtfs_coverage %>%
  transmute(Place = place, `Stops within 2 km` = stops_within_2km) %>%
  knitr::kable(align = "lr")
Table 3: Bus stops within 2 km of each check point, in the feed used.
Place Stops within 2 km
Brighton 269
Saltdean 90
Peacehaven 55
Newhaven 35
Seaford 40

The feed thins eastwards but does not stop at the city boundary, which is what matters: the expansion area is genuinely served rather than appearing unreachable because the timetable runs out.

Show code
m_pick <- leaflet(width = "100%", height = 600,
                  options = leafletOptions(preferCanvas = TRUE)) %>%
  add_basemap()

layer_names <- c(sort(unique(oi$costs_now$name)), ELM_LABEL)

for (s in layer_names) {
  d <- lsoa_lite %>%
    inner_join(surf_all %>% filter(name == s), by = c("lsoa21cd" = "lsoa")) %>%
    filter(is.finite(mins))

  # The school's own marker, at Elm Grove for the relocation layer.
  if (identical(s, ELM_LABEL)) {
    px <- elm_xy[, 1]; py <- elm_xy[, 2]; plab <- oi$elm_grove$label
  } else {
    r <- sch_pts[sch_pts$name == s, ]
    px <- r$lon; py <- r$lat; plab <- s
  }

  m_pick <- m_pick %>%
    addPolygons(data = d, group = s,
                fillColor = ~pal_time(mins), fillOpacity = 0.8,
                color = "white", weight = 0.3,
                label = ~sprintf("%s: %.0f minutes", lsoa21nm, mins)) %>%
    addCircleMarkers(lng = px, lat = py, group = s,
                     radius = 7, fillColor = "#1a9850", fillOpacity = 1,
                     color = "white", weight = 2, label = plab)
}

m_pick %>%
  addPolygons(data = catch, group = "Catchment boundaries",
              fill = FALSE, color = "#111111", weight = 2.2, opacity = 0.85,
              label = ~unname(CATCH_LABELS[catchment])) %>%
  addLayersControl(baseGroups = layer_names,
                   overlayGroups = "Catchment boundaries",
                   options = layersControlOptions(collapsed = TRUE)) %>%
  hideGroup("Catchment boundaries") %>%
  addLegend(pal = pal_time, values = surf_all$mins, title = "Minutes",
            position = "bottomright", opacity = 0.85)
Figure 15: Walk-and-bus journey time from every neighbourhood, for a weekday morning arrival. Choose a school from the control at the top right. The colour scale is shared across all schools, so the maps are directly comparable. The last option places Longhill at the alternative Elm Grove site.
Show code
jt <- bind_rows(
  oi$costs_now %>% inner_join(zone_w, by = "zone") %>%
    group_by(name) %>%
    summarise(unw = mean(cij), wtd = weighted.mean(cij, Oi), .groups = "drop"),
  oi$costs_elm %>% filter(name == "Longhill High School") %>%
    inner_join(zone_w, by = "zone") %>%
    summarise(name = ELM_LABEL, unw = mean(cij), wtd = weighted.mean(cij, Oi))
) %>%
  arrange(wtd) %>%
  mutate(gap = wtd - unw)

jt %>%
  transmute(School = sub(" AT ELM GROVE \\(alternative site\\)",
                         " (at Elm Grove)", name),
            `Unweighted mean` = sprintf("%.1f", unw),
            `Child-weighted mean` = sprintf("%.1f", wtd),
            `Difference` = sprintf("%+.1f", gap)) %>%
  knitr::kable(align = "lrrr")
Table 4: Average walk-and-bus journey time to each school. The unweighted column treats every neighbourhood equally; the weighted column weights each by the number of cohort-aged children living there, which is the figure that describes what families actually face.
School Unweighted mean Child-weighted mean Difference
Cardinal Newman Catholic School 41.1 42.7 +1.6
Longhill (at Elm Grove) 41.6 45.3 +3.7
Hove Park School 47.3 47.2 -0.2
Varndean School 45.7 48.1 +2.4
Blatchington Mill School 49.9 49.1 -0.8
Dorothy Stringer School 48.5 50.4 +1.9
Patcham High School 51.6 53.8 +2.2
Portslade Aldridge Community Academy 55.3 55.5 +0.2
Brighton Aldridge Community Academy 52.5 56.7 +4.2
King’s School 59.0 58.0 -1.0
Longhill High School 57.9 61.4 +3.5
Peacehaven Community School 66.0 68.0 +2.1

Three things come out of that table.

Longhill is the least accessible school in the city, at 61.4 minutes on the child-weighted measure against 42.7 for Cardinal Newman Catholic School. Only Peacehaven, outside the authority, is further from the city’s children.

Weighting by where children live makes almost every school look worse. The weighted mean exceeds the unweighted one for 9 of the 12 rows. Children are not distributed evenly across the map: they are concentrated in places slightly further from the schools than the average neighbourhood is. The two largest gaps belong to Brighton Aldridge Community Academy and Longhill High School (+4.2 and +3.5 minutes) — which are also the two least full schools in the city. The children in those catchments live further from their own school than an average neighbourhood does, which is a different problem from the school simply being far away, and a harder one to fix with a bus route.

Moving Longhill to Elm Grove changes its position completely. The child-weighted journey time falls from 61.4 minutes to 45.3 — a saving of 16.1 minutes, which would take it from the least accessible school in the city to the second most accessible. That is the single largest effect of any intervention considered anywhere in this document, and section 8 returns to what it would and would not solve.

4.3 What one of those journeys actually looks like

A single number in minutes hides what the journey involves. These are seven real routed journeys, drawn leg by leg: walking legs follow the street network, bus legs follow the service’s own shape.

Show code
rg <- bh_data("route_geometries.rds")
legs <- rg$legs %>% st_transform(4326)

pal_mode <- colorFactor(c("BUS" = "#2166ac", "WALK" = "#e08214"),
                        domain = c("BUS", "WALK"))

mleg <- leaflet(width = "100%", height = 560) %>%
  add_basemap()

for (j in sort(unique(legs$journey))) {
  d <- legs %>% filter(journey == j)
  tot <- rg$summary$total_minutes[rg$summary$journey == j]
  mleg <- mleg %>%
    addPolylines(data = d, group = j,
                 color = ~pal_mode(leg_mode), weight = 5, opacity = 0.85,
                 label = ~sprintf("%s - %s leg, %.0f min%s", j, leg_mode, minutes,
                                  ifelse(nzchar(route), paste0(" (service ", route, ")"), "")),
                 popup = ~sprintf("<b>%s</b><br>%s minutes in total", j, tot))
}

mleg %>%
  addLayersControl(overlayGroups = sort(unique(legs$journey)),
                   options = layersControlOptions(collapsed = FALSE)) %>%
  addLegend(pal = pal_mode, values = c("BUS", "WALK"), title = "Leg",
            position = "bottomright", opacity = 0.9)
Figure 16: Seven worked journeys, routed leg by leg. Walking legs in orange, bus legs in blue. Each can be checked against local knowledge.
Show code
rg$summary %>%
  transmute(Journey = journey,
            `Total minutes` = total_minutes,
            Legs = legs,
            `Bus services used` = ifelse(nzchar(services), services, "walk only")) %>%
  arrange(`Total minutes`) %>%
  knitr::kable()
Table 5: The same seven journeys, summarised.
Journey Total minutes Legs Bus services used
Woodingdean to Longhill 11 3 2
Saltdean to Longhill 19 5 12A, 2
Kemptown to Longhill 25 5 12, 2
Peacehaven to Longhill 39 5 14, 2
Hove to Dorothy Stringer 43 3 5B
Whitehawk to Longhill 44 3 2
Whitehawk to Dorothy Stringer 52 5 21, 5B

Woodingdean to Longhill takes 11 minutes. Whitehawk to Dorothy Stringer takes 52, for a journey of about the same straight-line distance. The network is not symmetric, and neither is opportunity.

4.4 Which school is actually nearest

Show code
near <- lsoa %>%
  left_join(acc$lsoa %>% select(lsoa, nearest_school, nearest_min),
            by = c("lsoa21cd" = "lsoa")) %>%
  filter(!is.na(nearest_school))

pal_near <- colorFactor("Set3", domain = sort(unique(near$nearest_school)))

leaflet(width = "100%", height = 560) %>%
  add_basemap() %>%
  addPolygons(data = near, group = "Nearest school",
              fillColor = ~pal_near(nearest_school), fillOpacity = 0.72,
              color = "white", weight = 0.4,
              label = ~sprintf("%s: %s, %.0f min", lsoa21nm, nearest_school, nearest_min)) %>%
  # This is the map where the overlay earns its place: the colours are
  # the nearest school and the outlines are the catchment a child is
  # actually assigned to, so any mismatch is visible directly.
  addPolygons(data = catch, group = "Catchment boundaries",
              fill = FALSE, color = "#111111", weight = 2.2, opacity = 0.85,
              label = ~unname(CATCH_LABELS[catchment])) %>%
  addLayersControl(overlayGroups = c("Nearest school", "Catchment boundaries"),
                   options = layersControlOptions(collapsed = FALSE)) %>%
  hideGroup("Catchment boundaries") %>%
  addLegend(pal = pal_near, values = near$nearest_school, title = "Nearest school",
            position = "bottomright", opacity = 0.85)
Figure 17: The nearest secondary school to each neighbourhood by routed walk-and-bus time, regardless of catchment. Turn on the catchment boundaries to see where the two disagree.

This map is worth comparing with the catchment map in section 2. They are not the same shape. The nearest school by bus is not always the catchment school, and for 30% of neighbourhoods even the nearest school is more than 25 minutes away.

4.5 Potential accessibility: how much school is within reach

Nearest-school distance answers a narrow question. It says nothing about whether a neighbourhood has one school within reach or five — and that difference is the whole of what a family’s realistic options amount to when their first preference is full.

Two measures are used here, deliberately, because they fail in opposite directions.

4.5.1 First, what makes a school attractive?

Both measures need a weight for each school — some number saying how much school is there. The choice is not obvious, and it is worth setting out before the maps rather than burying it in a footnote afterwards.

There are four candidates, each answering a different question.

Specification What it measures Question it answers
Admission number places the school is allowed to fill What did the authority decide to provide?
First preferences families naming it first What did families ask for?
Places allocated offers actually made What did the system deliver?
Weighted preferences ranks 1–3 with geometric decay What did families want, allowing for the ordering the rules force on them?

The admission number is the obvious choice and the weakest. It is an administrative decision, revised occasionally by the authority, and it says nothing about whether anyone wants the places. Longhill has the third-largest admission number in the city and the fewest first preferences of any school; on a PAN weighting it counts as one of Brighton’s more attractive destinations, which is plainly wrong.

First preferences fix that but introduce a different distortion. In the two paired catchments a family has to rank the two local schools against each other, so the catchment’s first preferences are split between them while its second preferences accumulate. The signature is unmistakable in the data, and it means counting firsts alone marks down precisely the schools whose catchment obliges families to put them second.

Places allocated measures what happened rather than what was wanted. That makes it a poor attractiveness measure for exactly the schools this document is about: an undersubscribed school allocates every place it is asked for, so allocation tracks capacity rather than desire at the bottom of the range.

Weighted preferences — counting all three ranks with a geometric decay of \(\alpha = 0.5\) — is what the maps below use. It keeps the information in the second and third choices without treating them as equal to a first.

Show code
acc$attract %>%
  left_join(oi$schools %>% select(name, catchment), by = "name") %>%
  mutate(kind = case_when(
           is.na(catchment) ~ "Faith, city-wide",
           catchment %in% c("DS_Varndean", "Hove_Blatch") ~ "Paired",
           TRUE ~ "Single-school")) %>%
  arrange(desc(W_pref)) %>%
  transmute(School = name, Catchment = kind,
            `2nd / 1st` = ifelse(imputed, "--", sprintf("%.2f", p2 / p1)),
            `Admission no.` = sprintf("%.2f", W_pan),
            `1st prefs` = sprintf("%.2f", W_p1),
            `Allocated` = sprintf("%.2f", W_alloc),
            `Weighted prefs` = sprintf("%.2f", W_pref)) %>%
  knitr::kable(align = "llrrrrr")
Table 6: The four specifications, normalised so each averages 1 across the eleven schools. The ratio of second to first preferences separates the paired catchments from the single-school ones almost perfectly.
School Catchment 2nd / 1st Admission no. 1st prefs Allocated Weighted prefs
Varndean School Paired 1.00 1.20 1.87 1.36 1.82
Cardinal Newman Catholic School Faith, city-wide 0.45 1.45 2.05 1.62 1.71
Dorothy Stringer School Paired 1.89 1.32 1.15 1.51 1.51
Blatchington Mill School Paired 1.02 1.32 1.23 1.49 1.28
King’s School Faith, city-wide 1.04 0.66 1.05 0.77 1.10
Hove Park School Paired 1.96 0.72 0.55 0.73 0.81
Patcham High School Single-school 0.43 0.90 0.85 0.97 0.79
Peacehaven Community School Single-school – 0.72 0.71 0.71 0.72
Portslade Aldridge Community Academy Single-school 0.58 0.88 0.70 0.85 0.59
Brighton Aldridge Community Academy Single-school 0.37 0.72 0.44 0.50 0.35
Longhill High School Single-school 0.38 1.08 0.41 0.49 0.32

The four schools in paired catchments have second-to-first ratios between 1.0 and 2.0; the four in single-school catchments, between 0.4 and 0.6.

Show code
wlong <- acc$attract %>%
  select(name, W_pan, W_p1, W_alloc, W_pref) %>%
  pivot_longer(-name, names_to = "spec", values_to = "w") %>%
  mutate(spec = factor(spec, c("W_pan", "W_p1", "W_alloc", "W_pref"),
                       c("Admission number", "First preferences",
                         "Places allocated", "Weighted preferences")))

ord <- acc$attract %>% arrange(W_pref) %>% pull(name)

wlong %>%
  mutate(name = factor(name, ord)) %>%
  ggplot(aes(w, name)) +
  geom_line(aes(group = name), colour = "grey72", linewidth = 0.8) +
  geom_point(aes(colour = spec), size = 2.8) +
  geom_vline(xintercept = 1, linetype = "31", colour = "grey45") +
  scale_colour_manual(values = c("Admission number"    = "#999999",
                                 "First preferences"   = "#2166ac",
                                 "Places allocated"    = "#4daf4a",
                                 "Weighted preferences" = "#b2182b"),
                      name = NULL) +
  labs(x = "Attractiveness weight (1 = city average)", y = NULL,
       title = "The four specifications broadly agree, and disagree where it matters",
       subtitle = "Each school's four weights, joined by a grey line. The dashed line is the city average.",
       caption = "Sources: BHCC allocation factsheets; published admission numbers.") +
  theme_bh(11) +
  theme(legend.position = "top", panel.grid.major.y = element_blank())
Figure 18: The four specifications compared. Schools are ordered by the weighted-preference weight. Where the points for a school spread out, the choice of specification matters for that school.

The lines are mostly short: for most schools the four specifications agree closely, and the choice would not matter much. They spread furthest for Longhill High School, Varndean School and Cardinal Newman Catholic School, for three different reasons — Longhill because its admission number is generous relative to any measure of demand, Varndean because the paired catchment splits its first preferences with Dorothy Stringer, and Cardinal Newman because it admits city-wide on faith criteria, so what it is asked for and what it allocates diverge from what it is sized for.

Two choices sit inside \(W_j\) that are easy to skate over. Neither is obviously right, and both change what individual schools look like.

The decay, \(\alpha\). A first preference counts 1, a second \(\alpha\), a third \(\alpha^2\). At the value used, \(\alpha = 0.5\), a second choice is worth half a first and a third a quarter. The geometric form is a modelling convenience rather than a measured fact — it says the drop from first to second is the same proportion as the drop from second to third, which nothing in the data establishes.

\(\alpha = 0\) reduces to counting first preferences only; \(\alpha = 1\) counts all three equally.

Show code
w_alpha %>%
  arrange(desc(`0.50`)) %>%
  mutate(across(where(is.numeric), ~ sprintf("%.2f", .x))) %>%
  rename(School = name) %>%
  knitr::kable(align = "lrrrrr")
Table 7: Attractiveness weight under different decay values, five-year window. The paired-catchment schools move most, because their second preferences are the ones the catchment structure generates.
School 0.00 0.25 0.50 0.75 1.00
Varndean School 1.87 1.87 1.82 1.77 1.71
Cardinal Newman Catholic School 2.05 1.85 1.71 1.62 1.57
Dorothy Stringer School 1.15 1.38 1.51 1.58 1.61
Blatchington Mill School 1.23 1.26 1.28 1.30 1.31
King’s School 1.05 1.08 1.10 1.11 1.12
Hove Park School 0.55 0.69 0.81 0.91 0.99
Patcham High School 0.85 0.79 0.79 0.83 0.87
Peacehaven Community School 0.71 0.71 0.72 0.72 0.72
Portslade Aldridge Community Academy 0.70 0.64 0.59 0.56 0.53
Brighton Aldridge Community Academy 0.44 0.39 0.35 0.32 0.30
Longhill High School 0.41 0.36 0.32 0.29 0.27

Hove Park ranges from 0.55 at \(\alpha = 0\) to 0.99 at \(\alpha = 1\) — close to a doubling, and the clearest illustration of what the choice does. Schools whose demand arrives as second preferences gain as \(\alpha\) rises; schools whose demand is overwhelmingly first-choice, like Cardinal Newman, lose. The ordering is fairly stable throughout (Spearman 0.90 between the extremes); the magnitudes are not.

The window. The maps and models below use a five-year average, the 2022–2026 admissions rounds. That is a compromise, and it is worth seeing what the alternatives would give.

Show code
w_win %>%
  arrange(desc(`Last 5 years (used)`)) %>%
  mutate(across(where(is.numeric), ~ sprintf("%.2f", .x))) %>%
  rename(School = name) %>%
  knitr::kable(align = "lrrr")
Table 8: The same weight measured over three different windows. Averaging over the whole series flatters the schools whose demand has fallen, because it includes the years before it fell.
School Latest year only Last 5 years (used) Whole series
Varndean School 1.81 1.82 1.61
Cardinal Newman Catholic School 1.89 1.71 1.51
Dorothy Stringer School 1.56 1.51 1.76
Blatchington Mill School 1.24 1.28 1.51
King’s School 1.11 1.10 0.86
Hove Park School 0.66 0.81 0.94
Patcham High School 0.70 0.79 0.81
Peacehaven Community School 0.71 0.72 0.71
Portslade Aldridge Community Academy 0.60 0.59 0.50
Brighton Aldridge Community Academy 0.47 0.35 0.33
Longhill High School 0.26 0.32 0.45

The differences are not small. Longhill scores 0.26 on the most recent year and 0.45 across the whole series — and the two windows rank the schools differently (Spearman 0.86).

That gap is not noise. It is section 5.3 showing up in a different place: demand has moved so much over the published series that a sixteen-year average describes a city that no longer exists, and systematically flatters the schools that have declined.

So why not use the latest year alone? Because section 2.6’s point about small numbers applies here too. Across the five years used, the year-to-year coefficient of variation in \(W\) is 25% for Brighton Aldridge Community Academy and 20% for Longhill High School — the two smallest and least subscribed schools. A single year would make exactly the schools this document is about the least reliably measured.

Five years is short enough to describe the current system and long enough to damp that. It is a judgement, not a derivation, and a reader who preferred three or seven would not be wrong.

NoteAnd the surface barely notices

It is worth knowing how little of this propagates. Recomputing the accessibility surface under each of the four weightings gives neighbourhood-level rankings that correlate between 0.985 and 0.998 across all six pairs. The weighting matters a great deal for what you say about an individual school and very little for the map, because the measure sums over eleven of them.

Two limitations do matter more than the choice between the four. Peacehaven Community School is in Lewes district and does not appear in the council’s factsheets, so it has no preference or allocation data; its weight is imputed from its admission number at the city-average rate. And none of these counts is divided by the admission number, because what should be within reach is places families want, which scales with the size of the school.

4.5.2 Measure one: gravity accessibility

\[A_i = \sum_j W_j \, c_{ij}^{-\beta}\]

Term Meaning
\(A_i\) the accessibility of neighbourhood \(i\) — the quantity mapped below
\(j\) a school; the sum runs over all 11 in the study area
\(W_j\) the weighted-preference attractiveness set out above
\(c_{ij}\) routed walk-and-bus minutes from \(i\) to \(j\), weekday morning
\(\beta\) distance decay: how sharply demand falls away with travel time, set at 1.7

Every school contributes to every neighbourhood’s score, discounted by how far away it is. A neighbourhood with one school ten minutes away and nothing else scores lower than one with three schools at fifteen minutes, which is the point of the measure. The result has no units, so it is indexed with today’s child-weighted city average at 100.

NoteWhere \(\beta\) comes from

\(\beta\) is set at 1.7. This is a judgement rather than an estimate: calibrating it properly needs pupil-level flows, which the council has not released. It is a reasonable central value for a system of this kind — journeys are short and the alternatives sit close together — and it lies inside the 1.5–3.2 range the open model sweeps in section 7. Nothing in this section turns on the exact figure; the surface is stored at the ends of that range as well, and the ordering of neighbourhoods is stable across it.

Show code
acc_scenario_map("A_index", "A_index_elm", pal_grav,
                 "Gravity index<br>(today's city average = 100)")
Figure 19: Gravity accessibility to secondary school places. Indexed so today’s child-weighted city average is 100, and both scenarios use that same base, so the Elm Grove layer can be read against it directly.

4.5.3 Measure two: cumulative opportunity

\[P_i(t) = \sum_j \mathrm{PAN}_j \cdot \mathbf{1}\!\left(c_{ij} \le t\right)\]

Term Meaning
\(P_i(t)\) school places reachable from neighbourhood \(i\) inside \(t\) minutes
\(j\) a school; the sum again runs over all 11
\(\mathrm{PAN}_j\) school \(j\)’s admission number — the places it actually has
\(\mathbf{1}(\cdot)\) an indicator: 1 if the journey is within \(t\) minutes, 0 if not
\(c_{ij}\) routed walk-and-bus minutes from \(i\) to \(j\), as above
\(t\) the time threshold, set with the slider below

Where the gravity measure discounts smoothly, this one cuts off. A school 29 minutes away counts in full; the same school at 31 minutes counts for nothing. That is crude, but it is also how a threshold actually works for a family judging whether a journey is feasible — and unlike the gravity index, the number means something on its own.

The threshold is doing all the work, so it is worth being able to move it.

Show code
# The polygons are drawn in JavaScript rather than by addPolygons(),
# because the slider has to restyle them on every move. Going through R
# would mean reaching into leaflet's own layer registry, which is an
# undocumented internal; building the layer here means the handles are
# ours and the interaction is straightforward.
#
# Colours are precomputed in R for every combination of threshold,
# scenario and weighting, so the browser only swaps fills.

tl <- acc$thresh_long %>% mutate(scenario = factor(scenario, c("now", "elm")))

pal_t  <- colorNumeric("viridis", domain = c(0, max(tl$places)), reverse = TRUE)
# Places per child are heavily right-skewed, so that scale is built on
# the square root; without it nearly every neighbourhood sits in the
# bottom colour and the map says nothing.
pal_pc <- colorNumeric("viridis", domain = c(0, sqrt(max(tl$per_child))),
                       reverse = TRUE)

tl <- tl %>% mutate(col = pal_t(places), col_pc = pal_pc(sqrt(per_child)))

as_lookup <- function(col) {
  split(tl, tl$scenario) %>%
    lapply(function(s) split(s, s$t) %>%
             lapply(function(d) setNames(as.list(d[[col]]), d$lsoa)))
}

# A coarser simplification than the leaflet maps use, because this
# geometry is embedded in the page as JSON. 60 m is still finer than the
# rendered pixels at any zoom this map offers.
geo_js <- lsoa %>%
  st_transform(27700) %>%
  st_simplify(dTolerance = 60, preserveTopology = TRUE) %>%
  st_transform(4326) %>%
  select(lsoa21cd, lsoa21nm)

lh_pt <- sch_pts[sch_pts$short == "longhill", ]

payload <- list(
  geo        = jsonlite::fromJSON(geojsonsf::sf_geojson(geo_js),
                                  simplifyVector = FALSE),
  thresholds = acc$thresh_grid,
  colours    = as_lookup("col"),
  values     = as_lookup("places"),
  colours_pc = as_lookup("col_pc"),
  values_pc  = as_lookup("per_child"),
  city = split(acc$thresh_city, acc$thresh_city$scenario) %>%
    lapply(function(s) split(s, s$t) %>%
             lapply(function(d) list(mean = round(d$mean_places[1]),
                                     stranded = d$stranded[1],
                                     children = round(d$stranded_children[1])))),
  longhill = list(now = list(lat = lh_pt$lat, lng = lh_pt$lon),
                  elm = list(lat = elm_xy[, 2], lng = elm_xy[, 1])),
  catchments = jsonlite::fromJSON(
    geojsonsf::sf_geojson(catch %>% select(catchment)),
    simplifyVector = FALSE))

m_cum <- leaflet(width = "100%", height = 580) %>%
  add_basemap() %>%
  fitBounds(lng1 = min(sch_pts$lon) - 0.03, lat1 = min(sch_pts$lat) - 0.02,
            lng2 = max(sch_pts$lon) + 0.03, lat2 = max(sch_pts$lat) + 0.02) %>%
  addCircleMarkers(data = sch_pts %>% filter(short != "longhill"),
                   lng = ~lon, lat = ~lat, radius = 4,
                   fillColor = "#e31a1c", fillOpacity = 1,
                   color = "white", weight = 1.2, label = ~name) %>%
  addLegend(pal = pal_t, values = c(0, max(tl$places)),
            title = "Places within<br>the threshold",
            position = "bottomright", opacity = 0.85)

m_cum <- htmlwidgets::onRender(m_cum, "
function(el, x, data) {
  var map = this;

  var layers = {};
  L.geoJSON(data.geo, {
    style: function() {
      return {weight: 0.3, color: 'white', fillOpacity: 0.82};
    },
    onEachFeature: function(f, layer) {
      layers[f.properties.lsoa21cd] = layer;
      layer.bindTooltip('');
    }
  }).addTo(map);

  var lh = L.circleMarker([data.longhill.now.lat, data.longhill.now.lng],
    {radius: 6, fillColor: '#1a9850', fillOpacity: 1,
     color: 'white', weight: 2}).addTo(map)
    .bindTooltip('Longhill High School');

  // Outlines only, so the surface stays readable underneath. Kept off
  // until asked for: the point of the map is that reachability does not
  // follow the boundaries.
  var catchLayer = L.geoJSON(data.catchments, {
    style: function() {
      return {fill: false, color: '#111111', weight: 2.2, opacity: 0.85};
    },
    onEachFeature: function(f, layer) {
      layer.bindTooltip(f.properties.catchment);
    }
  });

  var ui = L.DomUtil.create('div', 'acc-slider');
  ui.style.cssText = 'background:white;padding:10px 14px;border-radius:4px;' +
    'box-shadow:0 1px 5px rgba(0,0,0,.3);font-family:sans-serif;' +
    'font-size:13px;min-width:290px';
  ui.innerHTML =
    '<div style=\"font-weight:bold;margin-bottom:6px\">Within ' +
      '<span id=\"tval\">30</span> minutes</div>' +
    '<input id=\"tslider\" type=\"range\" min=\"0\" max=\"' +
      (data.thresholds.length - 1) + '\" value=\"' +
      data.thresholds.indexOf(30) + '\" step=\"1\" style=\"width:100%\">' +
    '<div style=\"margin-top:8px\">' +
      '<label style=\"margin-right:10px\"><input type=\"radio\" name=\"scn\" ' +
        'value=\"now\" checked> Today</label>' +
      '<label><input type=\"radio\" name=\"scn\" value=\"elm\"> ' +
        'Longhill at Elm Grove</label></div>' +
    '<div style=\"margin-top:6px;padding-top:6px;border-top:1px solid #eee\">' +
      '<label><input type=\"checkbox\" id=\"perchild\"> ' +
        'Divide by children living there</label><br>' +
      '<label><input type=\"checkbox\" id=\"showcatch\"> ' +
        'Catchment boundaries</label></div>' +
    '<div id=\"citystat\" style=\"margin-top:8px;color:#444;line-height:1.45\">' +
    '</div>';

  var ctl = L.control({position: 'topright'});
  ctl.onAdd = function() { return ui; };
  ctl.addTo(map);
  L.DomEvent.disableClickPropagation(ui);
  L.DomEvent.disableScrollPropagation(ui);

  var names = {};
  data.geo.features.forEach(function(f) {
    names[f.properties.lsoa21cd] = f.properties.lsoa21nm;
  });

  function redraw() {
    var idx = +ui.querySelector('#tslider').value;
    var t = data.thresholds[idx];
    var scn = ui.querySelector('input[name=scn]:checked').value;
    var pc = ui.querySelector('#perchild').checked;
    ui.querySelector('#tval').textContent = t;

    var cols = pc ? data.colours_pc[scn][t] : data.colours[scn][t];
    var vals = pc ? data.values_pc[scn][t]  : data.values[scn][t];
    var unit = pc ? ' places per child' : ' places';

    Object.keys(layers).forEach(function(id) {
      if (cols[id] === undefined) return;
      layers[id].setStyle({fillColor: cols[id]});
      layers[id].setTooltipContent(
        names[id] + '<br>' +
        (pc ? (+vals[id]).toFixed(1) : Math.round(vals[id])) +
        unit + ' within ' + t + ' min');
    });

    lh.setLatLng([data.longhill[scn].lat, data.longhill[scn].lng]);
    lh.setTooltipContent(scn === 'elm' ?
      'Longhill (at Elm Grove)' : 'Longhill High School');

    // Restyling the fills can leave the outlines behind them.
    if (map.hasLayer(catchLayer)) catchLayer.bringToFront();

    var c = data.city[scn][t], o = data.city['now'][t];
    ui.querySelector('#citystat').innerHTML =
      '<b>' + c.mean + '</b> places for the average child' +
      (scn === 'elm' ? ' <span style=\"color:#888\">(' + o.mean +
        ' today)</span>' : '') +
      '<br><b>' + c.stranded + '</b> neighbourhoods reach none (' +
      c.children + ' children)' +
      (scn === 'elm' ? ' <span style=\"color:#888\">(' + o.stranded +
        ' today)</span>' : '');
  }

  ui.querySelector('#tslider').addEventListener('input', redraw);
  ui.querySelector('#perchild').addEventListener('change', redraw);
  ui.querySelector('#showcatch').addEventListener('change', function() {
    if (this.checked) { catchLayer.addTo(map); catchLayer.bringToFront(); }
    else { map.removeLayer(catchLayer); }
  });
  ui.querySelectorAll('input[name=scn]').forEach(function(r) {
    r.addEventListener('change', redraw);
  });
  redraw();
}
", data = payload)

m_cum
Figure 20: School places reachable within the chosen number of minutes by walking and bus. Drag the slider to change the threshold, switch Longhill between its current site and Elm Grove, and toggle whether the figure is divided by the number of children living there. Darker means fewer places within reach; the darkest areas can reach none at all.

Two things are worth doing with that slider.

Drag it down to 20 minutes. The map empties. At a threshold most parents would call a reasonable school-run, much of the city can reach almost nothing — and the pattern is not centred on the places the admissions debate concentrates on.

Switch scenarios at different thresholds. Below about 30 minutes, moving Longhill to Elm Grove is unambiguously better for the city. Above about 35 it stops being so: the far south east loses the only school it could previously reach at all, and neighbourhoods start appearing in the “reaching none” count that were not there before. The relocation is not a free improvement — it is a trade that is strongly favourable at short thresholds and turns against a small number of places at long ones.

The two maps agree on the broad shape of the city — they correlate at 0.69 — and disagree substantially in the detail, which is exactly what makes showing both worthwhile. A neighbourhood sits an average of 1.8 deciles apart between the two, and some sit six deciles apart.

The disagreement is not random. The cumulative map has a hard edge the gravity map does not: 0 neighbourhoods score in the better half of the city on the gravity measure and can still reach no school place at all within 30 minutes. Their nearest school sits a few minutes the wrong side of the threshold, so the gravity measure — which discounts smoothly rather than cutting off — records them as adequately served while a parent with a bus timetable would not.

Which map is right depends on the question. If you are asking how much school is notionally within reach, the gravity surface is the better description. If you are asking whether a child can get to a school in a morning, the threshold is the thing that matters, and the cumulative map is showing you something real that the gravity map hides.

4.5.4 What moving one school would do to the whole city

Both maps carry a second layer placing Longhill at Elm Grove. Because accessibility sums over every school, moving one of them changes the figure for every neighbourhood — not only for Longhill’s own catchment.

Show code
ct <- acc$city
tibble::tribble(
  ~Measure, ~Today, ~`At Elm Grove`, ~Change,
  "Gravity accessibility (index)",
    sprintf("%.0f", 100), sprintf("%.1f", 100 * ct$A_elm / ct$A_now),
    sprintf("%+.1f%%", ct$A_pct_change),
  "Places reachable within 30 minutes",
    sprintf("%.0f", ct$p30_now), sprintf("%.0f", ct$p30_elm),
    sprintf("%+.0f", ct$p30_elm - ct$p30_now),
  "Journey to the nearest school (minutes)",
    sprintf("%.1f", ct$near_now), sprintf("%.1f", ct$near_elm),
    sprintf("%+.1f", ct$near_elm - ct$near_now),
  "Neighbourhoods reaching no place in 30 minutes",
    sprintf("%d", ct$zero30_now), sprintf("%d", ct$zero30_elm),
    sprintf("%+d", ct$zero30_elm - ct$zero30_now),
  "Children in those neighbourhoods",
    sprintf("%.0f", ct$zero30_children_now), sprintf("%.0f", ct$zero30_children_elm),
    sprintf("%+.0f", ct$zero30_children_elm - ct$zero30_children_now)
) %>%
  knitr::kable(align = "lrrr")
Table 9: City-wide accessibility today and with Longhill relocated to Elm Grove. All figures are weighted by where cohort-aged children live.
Measure Today At Elm Grove Change
Gravity accessibility (index) 100 101.1 +1.1%
Places reachable within 30 minutes 452 482 +30
Journey to the nearest school (minutes) 19.9 18.8 -1.0
Neighbourhoods reaching no place in 30 minutes 29 15 -14
Children in those neighbourhoods 385 240 -145

The two measures disagree about how much this matters, and the disagreement is informative.

On the gravity measure the effect is small — +1.1% city-wide. That is because Longhill carries a low attractiveness weight: moving an unpopular school closer to people does not add much wanted school to the map.

On the cumulative measure it is substantial. Places reachable within half an hour rise from 452 to 482 for the average child, and the number of neighbourhoods that can reach nothing inside 30 minutes falls from 29 to 15. In children, that is 145 who would gain a reachable school place they do not currently have.

153 of the 179 neighbourhoods are better off under the move and 26 are worse off — the latter being the far south east, which loses its closest school. This is a genuine trade, but it is a lopsided one, and it is the strongest argument in this document for relocation on grounds that have nothing to do with Longhill’s own roll.

Three findings come out of this, and the first is the one that should change the conversation.

29 of the 179 neighbourhoods — about 385 cohort-aged children — cannot reach a single secondary school place within 30 minutes by walking and bus on a weekday morning. Not their catchment school; any school. For those neighbourhoods the admissions debate is somewhat beside the point.

The spread is nearly fivefold. The best-served neighbourhood scores 746 on the index against 26 for the worst. A child in the centre of the city has several schools genuinely within reach; a child at either end has one, or none.

The two measures broadly agree, but not closely. They correlate at 0.69 (Spearman), yet only 44% of neighbourhoods land within one decile of each other on both. Where they agree, the finding is robust to how you define access. Where they disagree, it is because one is counting a school just over the threshold that the other is discounting smoothly.

Show code
acc$lsoa %>%
  mutate(grp = ifelse(places_30 < 1, "No places within 30 min", "Some places within 30 min")) %>%
  ggplot(aes(A_index, places_30, colour = grp, size = Oi)) +
  geom_point(alpha = 0.75) +
  scale_colour_manual(values = c("No places within 30 min" = "#b2182b",
                                 "Some places within 30 min" = "#2166ac"),
                      name = NULL) +
  scale_size_area(max_size = 6, guide = "none") +
  scale_y_continuous(labels = label_comma()) +
  labs(x = "Gravity accessibility index (city average = 100)",
       y = "Places reachable within 30 minutes",
       title = "Two ways of asking the same question",
       subtitle = "Each point is a neighbourhood, sized by the number of cohort-aged children",
       caption = "Routed walk-and-bus times, weekday morning arrival.") +
  theme_bh()
Figure 21: The two measures against each other. Neighbourhoods along the bottom can reach no places at all inside 30 minutes.
WarningTwo caveats that bear on these numbers

Journey times are modelled, not measured. They come from r5r routing over the merged street network and published timetables for one weekday morning. They do not know about school buses, parental lifts, walking routes children actually use, or a bus that is full when it arrives.

One school’s times are suspect. Dorothy Stringer and Varndean are 470 metres apart, yet the routed matrix makes Stringer slower from most of the city by a median of about five and a half minutes. That may be real, or it may be an artefact of where the router snapped each school onto the network. It has not been established which, and it inflates the apparent inaccessibility of the areas whose nearest school is Stringer.

4.6 Accessibility and child poverty together

The question that matters for policy is not where access is poor, nor where deprivation is high, but where the two coincide — because those are the neighbourhoods where a difficult journey is least likely to be solved by a car in the family.

Show code
# Stevens-style bivariate palette, keyed "access-deprivation". Both
# terciles are oriented worst-to-best, so 1-1 is the concerning corner
# and gets the darkest colour, and 3-3 is the comfortable one and gets
# the lightest. Getting this the wrong way round is the classic way to
# misread a bivariate map, so the legend below spells out both axes.
BIV <- c(
  "1-1" = "#3b4994", "1-2" = "#8c62aa", "1-3" = "#be64ac",
  "2-1" = "#5698b9", "2-2" = "#a5add3", "2-3" = "#dfb0d6",
  "3-1" = "#5ac8c8", "3-2" = "#ace4e4", "3-3" = "#e8e8e8")

TER_ACC <- c("least reachable", "middle", "most reachable")
TER_DEP <- c("most deprived", "middle", "least deprived")

biv_map <- lsoa_lite %>%
  inner_join(acc$bivariate %>%
               select(lsoa, acc_t, dep_t, biv_key, idaci_score,
                      A_index, places_30, Oi),
             by = c("lsoa21cd" = "lsoa")) %>%
  mutate(fill = unname(BIV[biv_key]))

biv_lab <- sprintf(
  paste0("<b>%s</b><br>Accessibility: %s (index %.0f)",
         "<br>Deprivation: %s (IDACI %.2f)",
         "<br>%.0f places within 30 min | about %.0f children"),
  biv_map$lsoa21nm, TER_ACC[biv_map$acc_t], biv_map$A_index,
  TER_DEP[biv_map$dep_t], biv_map$idaci_score,
  biv_map$places_30, biv_map$Oi) %>%
  lapply(htmltools::HTML)

# The 3 x 3 key, drawn as HTML so it can sit on the map with both axes
# labelled in words rather than as tercile numbers.
key_cells <- paste0(
  sapply(3:1, function(a) paste0(
    '<tr>',
    if (a == 3) '<td rowspan="3" style="writing-mode:vertical-rl;transform:rotate(180deg);font-size:10px;padding-right:3px;color:#444">More reachable &rarr;</td>' else '',
    paste0(sapply(1:3, function(d) sprintf(
      '<td style="width:20px;height:20px;background:%s" title="%s / %s"></td>',
      BIV[[paste0(a, "-", d)]], TER_ACC[a], TER_DEP[d])), collapse = ''),
    '</tr>'), USE.NAMES = FALSE), collapse = '')

biv_legend <- paste0(
  '<div style="background:white;padding:8px 10px;border-radius:4px;',
  'box-shadow:0 1px 5px rgba(0,0,0,.3);font-family:sans-serif;font-size:11px">',
  '<div style="font-weight:bold;margin-bottom:5px">Both axes: worst &rarr; best</div>',
  '<table style="border-collapse:collapse">', key_cells, '</table>',
  '<div style="font-size:10px;color:#444;margin-top:3px;padding-left:16px">',
  'Less deprived &rarr;</div>',
  '<div style="margin-top:6px;font-size:10px;color:#666;max-width:150px">',
  'Dark blue, bottom left: least reachable <i>and</i> most deprived.',
  '</div></div>')

leaflet(width = "100%", height = 600) %>%
  add_basemap() %>%
  addPolygons(data = biv_map, group = "Accessibility x deprivation",
              fillColor = ~fill, fillOpacity = 0.85,
              color = "white", weight = 0.3,
              label = biv_lab) %>%
  addPolygons(data = catch, group = "Catchment boundaries",
              fill = FALSE, color = "#111111", weight = 2.2, opacity = 0.85,
              label = ~unname(CATCH_LABELS[catchment])) %>%
  addLayersControl(
    overlayGroups = c("Accessibility x deprivation", "Catchment boundaries"),
    options = layersControlOptions(collapsed = FALSE)) %>%
  hideGroup("Catchment boundaries") %>%
  addControl(html = biv_legend, position = "bottomright")
Figure 22: Accessibility and income deprivation affecting children, mapped together. Both axes run worst to best, so the darkest blue is the corner that matters: least reachable school places and highest child poverty at the same time. Hover for a neighbourhood’s figures on both.

There is a real gradient here, and it runs the wrong way. Accessibility and child poverty correlate at -0.33 (Spearman): the more deprived a neighbourhood, the less school it can reach.

25 neighbourhoods — about 400 cohort-aged children — sit in the worst corner, in the least accessible third of the city and the most deprived third at the same time. At the comfortable end, 27 neighbourhoods (441 children) are in the best third on both.

The diagonal is what makes this a finding rather than an artefact. Only 11 neighbourhoods are in the most deprived third and the best-served third — fewer than half the 25 in the worst corner. Deprivation and poor access are not merely coincident in this city; they line up.

This is the strongest argument in the document for treating transport as a school-admissions instrument rather than a separate departmental concern. A city that wanted to widen real choice for its poorest families could do more with a bus timetable than with a catchment boundary — and unlike a boundary change, it would take nothing away from anyone else.

NoteWhat pupil-level data would add here

This section works entirely with modelled journey times between neighbourhood centroids and school gates. The council holds the home postcode of every applicant and the school each was allocated. With that, the same analysis could be run on journeys children actually make rather than journeys they could in principle make — and the gap between the two is precisely the quantity that would tell the city whether its transport network is a real constraint on choice or merely a theoretical one. Section 9 sets out what that request would look like.

5 Admissions rules, and what families actually do

5.1 How a place is allocated

Families name up to three schools in order. Each school then ranks its applicants against its own criteria, and the authority runs an equal-preference matching so that a child is offered the highest-ranked school that will take them. The catchment criterion sits partway down most schools’ lists, below looked-after children and siblings and, since 2023, below a quota for children eligible for free school meals.

The critical point is one the rules themselves obscure. A criterion only does something if a school has to turn someone away.

Show code
conv <- bh_data("adjudicator_conversion.rds")

rat <- conv$conv %>%
  group_by(school) %>%
  summarise(p1_named = sum(p1_named), p1_offered = sum(p1_offered),
            .groups = "drop") %>%
  mutate(rate = p1_offered / p1_named,
         rations = rate < 0.999) %>%
  arrange(rate)

ggplot(rat, aes(reorder(school, rate), rate, fill = rations)) +
  geom_col(width = 0.7) +
  geom_hline(yintercept = 1, colour = "grey40", linetype = "31") +
  geom_text(aes(label = fmt_pct(100 * rate, 0)), hjust = -0.15, size = 3.2) +
  coord_flip() +
  scale_y_continuous(labels = label_percent(), limits = c(0, 1.12),
                     breaks = seq(0, 1, 0.25)) +
  scale_fill_manual(values = c(`TRUE` = "#b2182b", `FALSE` = "#4d9221"),
                    labels = c(`TRUE` = "Rations places",
                               `FALSE` = "Turns nobody away"), name = NULL) +
  labs(x = NULL, y = "First preferences that received an offer",
       title = "Only some of these schools ration anything",
       subtitle = "Pooled across three admissions rounds",
       caption = "Source: BHCC evidence to the Schools Adjudicator.") +
  theme_bh() +
  theme(panel.grid.major.y = element_blank())
Figure 23: For each school, the proportion of first-preference applicants who were offered a place. A school at 100% turned nobody away, so its admission criteria did no work at all.

3 of the 10 schools offered a place to every single first-preference applicant. For those schools — Brighton Aldridge Community Academy, Longhill High School, Portslade Aldridge Community Academy — the catchment criterion, the sibling criterion and the free school meals quota are all inert. They admit everyone who asks.

The city therefore runs two admissions systems under one set of rules. In 7 schools the criteria bind and the lottery matters. In the other 3 they are decoration. Debate about admissions criteria is, in practice, debate about a minority of the city’s schools — and never about the schools with the most spare capacity.

Since 2026/27 one more criterion sits in the list: priority 6, a share of places at the community schools for children from the four single-school catchments, set at 5% after a first proposal of 20%. It is meant to widen access. 1 finds that, in the full model, it makes almost no difference to how mixed the city’s intakes are.

5.2 What happens between March and September

Every figure in this document, and every fit reported for the model, is measured against national offer day. That is the right benchmark for what an allocation produces: it is the moment the rules in Section 5.1 have finished running. It is also the moment that matters most to families, because it is when they are told the answer and when the months of appeals, waiting lists and phone calls begin.

But it is not the system that exists in September. An FOI response from the council (26367489, 30 September 2026) gives the confirmed position once term had started for 2026 entry, and it can be set against the council’s own allocation factsheet for the same cohort. Like everything else here it is public information: a Freedom of Information response is disclosable to anyone who asks for it, and the figures below are the council’s own, released under that Act. Counts of five or fewer are suppressed in the response and are shown here at their midpoint.

Show code
foi <- tibble::tribble(
  ~name,                ~pan, ~offer, ~sep,  ~community,
  "Varndean",             300,   300,  299,  TRUE,
  "Blatchington Mill",    330,   330,  327,  TRUE,
  "Patcham",              225,   204,  200,  TRUE,
  "Dorothy Stringer",     330,   330,  303,  TRUE,
  "Hove Park",            180,   136,  111,  TRUE,
  "Longhill",             210,    81,   95,  TRUE,
  "Brighton Aldridge",    180,   146,  167,  FALSE,
  "Portslade Aldridge",   220,   183,  193,  FALSE,
  "Cardinal Newman",      360,   360,  374,  FALSE) |>
  dplyr::mutate(change = sep - offer,
                school = forcats::fct_reorder(name, change))
ggplot(foi, aes(change, school, fill = change > 0)) +
  geom_col(width = 0.66) +
  geom_vline(xintercept = 0, colour = "grey45") +
  geom_text(aes(label = sprintf("%+d", round(change)),
                hjust = ifelse(change > 0, -0.25, 1.25)), size = 3.6) +
  scale_fill_manual(values = c(`TRUE` = "#2b7a4b", `FALSE` = "#c2410c"), guide = "none") +
  scale_x_continuous(expand = expansion(mult = 0.16)) +
  labs(x = "Change in Year 7 pupils, offer day to September", y = NULL) +
  theme_bh(11)

Change in Year 7 numbers between national offer day and the confirmed September position, 2026 entry. Sources: BHCC Secondary Allocation Factsheet (Table 4); FOI 26367489.

The city total barely moves: 2070 places on offer day, 2069 in September, a net change of -1. But roughly 60 children — about three per cent of the cohort — are not where they were placed in March, and the direction is systematic. The oversubscribed schools lose; the schools with room gain.

Two flows are consistent with this, and the data cannot separate them. Children may be leaving the popular schools for the independent sector or for another authority, while late arrivals and in-year admissions enter a system with vacancies only at the other end. Question 3 of the same FOI asked where children who do not take up a Brighton & Hove place actually go. The council’s answer was that it does not hold that information.

5.2.1 What this means for a family that does not get its first choice

This is the practical question, and the answer is more encouraging than offer day alone suggests — with one important qualification.

Places do open up. Dorothy Stringer finished September 27 below its admission number, Hove Park 69 below, Patcham 25 below. Those are real vacancies at schools that were full, or nearly full, in March.

ImportantThe qualification

Dorothy Stringer was heavily oversubscribed on offer day and still finished the year 27 places below its admission number. If families were still waiting for a place in March, the arithmetic says some of them should have been offered one by September.

The likeliest explanation is timing. By the point a place falls vacant, the family that wanted it has committed elsewhere, and the waiting list has dissolved. Capacity and demand both exist; they simply stop overlapping. That is a fixable problem, and it is invisible in any offer-day figure.

5.2.2 The open admissions priority, in practice

The 5% open admissions quota was the most contested element of the 2024 reforms, and 2026 is the first year it has run. The FOI shows what it actually did.

Show code
tibble::tribble(
  ~School,             ~PAN, ~`Offer day`, ~September,
  "Blatchington Mill",  330,  17,  17,
  "Dorothy Stringer",   330,  17,  17,
  "Varndean",           300,  15,  15,
  "Patcham",            225,   8,  11,
  "Hove Park",          180,   9,   3,
  "Longhill",           210,   1,   3) |>
  dplyr::mutate(`Places available` = round(0.05 * PAN, 1),
                Filled = sprintf("%.0f%%", 100 * September / `Places available`)) |>
  dplyr::select(School, PAN, `Places available`, `Offer day`, September, Filled) |>
  knitr::kable(align = "lrrrrr")
Table 10: Open admissions (priority 6): places available under the 5% quota, against places filled. Sources: BHCC Secondary Allocation Factsheet (Table 4); FOI 26367489. Figures of five or fewer are suppressed in the FOI and shown here at their midpoint.
School PAN Places available Offer day September Filled
Blatchington Mill 330 16.5 17 17 103%
Dorothy Stringer 330 16.5 17 17 103%
Varndean 300 15.0 15 15 100%
Patcham 225 11.2 8 11 98%
Hove Park 180 9.0 9 3 33%
Longhill 210 10.5 1 3 29%

The scheme is fully subscribed wherever it can bite. At the four oversubscribed schools it runs at 98 to 103 per cent of the places available. It is barely used at Hove Park and Longhill — which is what should happen, because neither school is full, and a priority that helps you jump a queue is worth nothing where there is no queue.

So the criticism that the policy is not being taken up is not supported by this data. The more useful observation is its scale. Across the six community schools the quota moved about 66 children, or roughly 3 per cent of the Year 7 cohort. That is the whole of the council’s marquee admissions reform. Raising it would not have been free: at the 20 per cent originally proposed, the model puts displacement at roughly three times the level the 5 per cent settlement produces, and the city slightly more segregated rather than less.

5.2.3 Re-validating the model against September

The model is fitted to offer day, so it should be judged against offer day — but the September data is the first genuinely out-of-sample test it has had, using numbers that did not exist when it was built.

Show code
c6 <- foi |> dplyr::filter(community)
mi <- c(Varndean = 300, `Blatchington Mill` = 330, Patcham = 215,
        `Dorothy Stringer` = 330, `Hove Park` = 168, Longhill = 90)
c6$modelled <- unname(mi[as.character(c6$name)])
f <- function(a, b) c(R2 = cor(a, b)^2, MAE = mean(abs(a - b)), Bias = mean(a - b))
rbind(`Model vs offer day`     = f(c6$modelled, c6$offer),
      `Model vs September`     = f(c6$modelled, c6$sep),
      `Offer day vs September` = f(c6$offer,    c6$sep)) |>
  round(2) |> knitr::kable(align = "rrr")
Table 11: Model fit against offer day and against the confirmed September position, 2026 entry, for the six community schools.
R2 MAE Bias
Model vs offer day 0.99 8.67 8.67
Model vs September 0.95 18.00 16.33
Offer day vs September 0.98 12.33 7.67

Against offer day, for the six community schools, the model is doing what it was built to do. Against September it holds up reasonably — but it carries a positive bias of about 16 children per school: it systematically over-predicts the schools that are hard to get into. Almost all of that sits in two schools. Hove Park is modelled at 168 and finished at 111; Dorothy Stringer is modelled at its full 330 and finished at 303.

That is not a separate error from the churn described above. It is the same phenomenon: the model reproduces the allocation, and the allocation over-states where children end up. The modelled intakes in this document should be read as the offer-day position, which is what they are.

Two consequences follow. Longhill’s modelled 2026 intake of 90 sits inside the FOI’s September range of 91 to 99, so the viability arithmetic elsewhere in this document is, if anything, slightly optimistic about how much trouble the school is in rather than the reverse. And Hove Park’s position is materially worse than this document models — which bears directly on the Valley Campus consolidation, since a school finishing 69 below its admission number does not have a demand problem the model has captured.

NoteWhat would settle this

A single published table, each year, giving the September position by school alongside the offer-day position. It exists — the council produced it on request within a month. Publishing it routinely would let any family see how much movement to expect after offer day, and would let this model be calibrated on the system as it actually ends up rather than as it is first announced.

5.2.4 The free school meal priorities: a genuine out-of-sample test

The disadvantage layer in Section 5.6 is fitted to each school’s published disadvantaged share, so the fact that it reproduces that share is calibration, not evidence. The same FOI supplies something the model was never shown: how many children each community school actually admitted under priority 4 (free school meals, in catchment) and priority 5 (free school meals, out of catchment).

Show code
# Modelled figures are the 2026 baseline run under the council's
# priorities with the 5% open-admissions share (the configuration used
# throughout this document), taken from its priority-tier output.
# Reproduce with:
#   outcomes(inp, run_sim(inp, year = 2026,
#     rules = list(rule = "priorities", p6_share = 0.05, fsm = TRUE)))$tiers
fsm_oos <- tibble::tribble(
  ~School,              ~m4,  ~p4, ~m5,  ~p5,
  "Varndean",            56,   59,  34,    6,
  "Blatchington Mill",   39,   46,  10,   16,
  "Dorothy Stringer",    30,   32,  23,   32,
  "Hove Park",           21,   17,   9,   20,
  "Patcham",             19,   18,  15,   17,
  "Longhill",            13,   20,   3,    8)

fsm_oos %>%
  transmute(School,
            `P4 modelled` = m4, `P4 actual` = p4, `P4 error` = m4 - p4,
            `P5 modelled` = m5, `P5 actual` = p5, `P5 error` = m5 - p5) %>%
  knitr::kable(align = "lrrrrrr")
Table 12: Children admitted under the free school meal priorities, September 2026: the model’s 2026 baseline against the confirmed position. The model was fitted to neither column.
School P4 modelled P4 actual P4 error P5 modelled P5 actual P5 error
Varndean 56 59 -3 34 6 28
Blatchington Mill 39 46 -7 10 16 -6
Dorothy Stringer 30 32 -2 23 32 -9
Hove Park 21 17 4 9 20 -11
Patcham 19 18 1 15 17 -2
Longhill 13 20 -7 3 8 -5

The two priorities behave completely differently.

In catchment the model is good. Across the six community schools it reaches an R² of 0.94 against the confirmed figures, with a mean absolute error of 4.0 children. That is a real test passed: nothing in the fitting used these numbers.

Out of catchment it has no skill whatsoever. The R² is 0.00. The model expects 34 children on free school meals to travel into Varndean under priority 5; six did. It under-states Hove Park by the same order in the other direction.

That split is informative rather than embarrassing, and it falls exactly where the model’s own documentation says it should. Priority 4 is close to pure geography — disadvantaged children living in a catchment, choosing a school in it — and geography is what a spatial interaction model is for. Priority 5 requires a family to know the priority exists, to be willing to travel on it, and to name the school. That is the whole of what \(\lambda_j\) absorbs into a single number per school, and Section 5.6 is explicit that it “measures sorting; it does not explain it”. Here is the cost of that, quantified.

ImportantWhat this means for reading the disadvantage results

Where this document reports how disadvantage would be distributed under a scenario, treat the in-catchment component as modelled and the out-of-catchment component as an assumption carried forward from today’s sorting. A policy whose effect runs mainly through priority 5 — open admissions, the free school meal quota applied across catchments — is one this model is least equipped to forecast, and the place where its numbers should be treated as illustrative.

This check was only possible because somebody asked for the breakdown. It is the strongest argument in this document for publishing allocations by priority as a matter of course: it is the only way anyone, including the council, can tell which parts of an admissions model are working.

5.3 Sixteen years of demand

Show code
fp <- bh_data("factsheet_panel.rds")

fs <- fp$factsheets %>%
  filter(!is.na(pref1), name != "Total") %>%
  mutate(kind = ifelse(name %in% c("King's School", "Cardinal Newman Catholic School"),
                       "Faith schools (city-wide)", "Catchment schools"))

ggplot(fs, aes(year, pref1, colour = name)) +
  geom_line(linewidth = 0.95) +
  geom_point(size = 1.4) +
  facet_wrap(~ kind, ncol = 1, scales = "free_y") +
  scale_colour_brewer(palette = "Paired", name = NULL) +
  labs(x = NULL, y = "First preferences",
       title = "Demand has moved a long way between schools",
       subtitle = "First preferences expressed, by school",
       caption = "Source: BHCC published allocation factsheets.") +
  theme_bh() +
  theme(legend.position = "right", legend.text = element_text(size = 8))
Figure 24: First preferences expressed for each school, 2010 onwards. The two church schools admit city-wide and are shown separately.

5.4 Who gained and who lost

Comparing each school’s first five years in the published series with its last, the largest falls are at Blatchington Mill School and Dorothy Stringer School; the largest rises at King’s School and Varndean School. Figure 25 shows how each school got there. This is not a system in equilibrium. It is one where a shrinking cohort is being redistributed towards a small number of schools, and away from others, faster than the cohort itself is shrinking.

Show code
base_w <- fs_w %>%
  filter(year <= yr_rng[1] + 4) %>%
  group_by(name) %>% summarise(base = mean(wpref), .groups = "drop")

traj <- fs_w %>%
  inner_join(base_w, by = "name") %>%
  mutate(idx = 100 * wpref / base,
         kind = ifelse(name %in% c("King's School", "Cardinal Newman Catholic School"),
                       "Faith schools (city-wide)", "Catchment schools"))

ends <- traj %>% group_by(name) %>% slice_max(year, n = 1) %>% ungroup()

ggplot(traj, aes(year, idx, colour = name)) +
  geom_hline(yintercept = 100, linetype = "31", colour = "grey45") +
  geom_line(linewidth = 0.95) +
  ggrepel::geom_text_repel(
    data = ends, aes(label = name), size = 2.9, hjust = 0,
    direction = "y", nudge_x = 0.6, segment.size = 0.25,
    min.segment.length = 0, max.overlaps = 20, seed = 1) +
  facet_wrap(~ kind, ncol = 1, scales = "free_y") +
  scale_colour_brewer(palette = "Paired", guide = "none") +
  scale_x_continuous(expand = expansion(mult = c(0.02, 0.28))) +
  labs(x = NULL, y = "Weighted preferences, indexed to opening average = 100",
       title = "Where demand has moved under the catchment system",
       subtitle = sprintf(
         "Ranks 1-3 with decay %.2f. Above the dashed line is growth on the %d-%d baseline.\nThe whole series post-dates the 2008 move to catchments with a lottery tie-break.",
         DECAY, yr_rng[1], yr_rng[1] + 4),
       caption = "Source: BHCC published allocation factsheets.") +
  theme_bh() +
  theme(strip.text = element_text(face = "bold", hjust = 0))
Figure 25: Weighted preferences over the published series. Preferences are counted at all three ranks with the same geometric decay used in section 4, so a school that families consistently name second is not scored as though nobody wanted it. Each school is indexed to 100 at its own five-year opening average, so the lines show relative movement rather than size.
NoteWhy this uses weighted preferences rather than first ones

Counting only first preferences would give a different and, in two cases, a misleading picture.

Hove Park loses 38 first preferences between the two windows — which looks survivable — but 101 weighted preferences, a fall of 25%. Most of what it lost was second and third choices, and a first-preference count discards exactly that. A school families have stopped naming at all is in a different position from one they have stopped naming first.

Varndean moves the other way. On first preferences it more than doubles, up 104%; on weighted preferences it rises 33%. It has gained firsts partly by converting seconds it already had — real, but a smaller shift than the headline count suggests, and a predictable artefact of sitting in a paired catchment where families must rank two local schools against each other.

5.5 What families are actually choosing on

The council’s admissions guide points families at published performance data: parents are told to look in each school’s prospectus for its “National Curriculum test results, public exam results” and to read its Ofsted reports (Secondary school admissions guide 2027 to 2028). Nothing in that advice adjusts for who a school admits. It is worth asking which published number families respond to.

Show code
# Two panels sharing a set of buttons that swap the preference measure.
# Each panel holds all four measures as hidden trace pairs -- a fit line
# then its points -- so a button is a visibility switch rather than a
# redraw. Measure i therefore sits at traces 2i-1 and 2i within each
# panel's block of eight, which is what btn() below relies on.

# Both panels take the same vertical range, so switching measure does
# not silently rescale one against the other.
ch_range <- function(y) log10(range(y) * c(0.60, 1.60))

# plotly has no repel, and these ten schools cluster tightly enough that
# a fixed left/right rule collides three times per panel even at full
# page width. So each label is tried in six positions and takes the one
# that runs into the fewest markers and already-placed labels, most
# crowded point choosing first. Scoring is in normalised panel
# coordinates against a nominal panel size, so a browser that gives the
# widget a different width degrades the placement rather than breaking
# it. Positions are recomputed per measure, because the buttons move
# every point.
CH_PANEL_PX <- c(w = 495, h = 232)

ch_place <- function(x, y, labs) {
  xr <- range(x) + c(-0.13, 0.13) * diff(range(x))
  yr <- ch_range(y)
  nx <- (x - xr[1]) / diff(xr)
  ny <- (log10(y) - yr[1]) / diff(yr)

  wn <- (nchar(labs) * 5.6 + 6) / CH_PANEL_PX[["w"]]
  hn <- 13 / CH_PANEL_PX[["h"]]
  rx <- 6 / CH_PANEL_PX[["w"]]
  ry <- 6 / CH_PANEL_PX[["h"]]

  cand <- list(
    `middle right`  = function(i) c(nx[i] + rx, ny[i] - hn / 2,
                                    nx[i] + rx + wn[i], ny[i] + hn / 2),
    `middle left`   = function(i) c(nx[i] - rx - wn[i], ny[i] - hn / 2,
                                    nx[i] - rx, ny[i] + hn / 2),
    `top center`    = function(i) c(nx[i] - wn[i] / 2, ny[i] + ry,
                                    nx[i] + wn[i] / 2, ny[i] + ry + hn),
    `bottom center` = function(i) c(nx[i] - wn[i] / 2, ny[i] - ry - hn,
                                    nx[i] + wn[i] / 2, ny[i] - ry),
    `top right`     = function(i) c(nx[i], ny[i] + ry,
                                    nx[i] + wn[i], ny[i] + ry + hn),
    `bottom left`   = function(i) c(nx[i] - wn[i], ny[i] - ry - hn,
                                    nx[i], ny[i] - ry))

  ov <- function(a, b) max(0, min(a[3], b[3]) - max(a[1], b[1])) *
                       max(0, min(a[4], b[4]) - max(a[2], b[2]))
  mk <- lapply(seq_along(x), function(i)
    c(nx[i] - rx, ny[i] - ry, nx[i] + rx, ny[i] + ry))

  # The y span is visually shorter than the x span, so crowding is
  # measured with y weighted down before taking nearest neighbours.
  d <- as.matrix(stats::dist(cbind(nx, ny * 0.6)))
  diag(d) <- Inf

  pos <- character(length(x)); placed <- list()
  for (i in order(apply(d, 1, min))) {
    best <- NULL; best_box <- NULL; best_s <- Inf
    for (nm in names(cand)) {
      b <- cand[[nm]](i)
      s <- 3 * sum(vapply(mk[-i], ov, numeric(1), b = b)) +
           3 * sum(vapply(placed, ov, numeric(1), b = b)) +
           max(0, -b[1]) + max(0, b[3] - 1) +
           max(0, -b[2]) + max(0, b[4] - 1)
      if (s < best_s) { best_s <- s; best <- nm; best_box <- b }
    }
    pos[i] <- best
    placed <- c(placed, list(best_box))
  }
  pos
}

ch_panel <- function(pred, show_ylab) {
  x   <- cd[[pred]]
  col <- CH_COL[[pred]]

  p <- plotly::plot_ly()
  for (m in CH_MEASURES) {
    f    <- ch_one(m, pred)
    tpos <- ch_place(x, cd[[m]], cd$short)
    p <- p %>%
      plotly::add_lines(
        x = f$x, y = f$y, visible = (m == CH_DEFAULT), showlegend = FALSE,
        hoverinfo = "skip", color = I("#8c8c8c"),
        line = list(width = 1.4, dash = "dash")) %>%
      plotly::add_trace(
        x = x, y = cd[[m]], visible = (m == CH_DEFAULT), showlegend = FALSE,
        type = "scatter", mode = "markers+text", color = I(col),
        text = cd$short, textposition = tpos,
        textfont = list(size = 10, color = "#333333"),
        marker = list(size = 9, line = list(color = "white", width = 1)),
        customdata = cd$school,
        hovertemplate = paste0("<b>%{customdata}</b><br>", CH_PRED[[pred]],
                               ": %{x:.1f}<br>%{y:.2f} ",
                               if (m == "M5 attractiveness")
                                 "attractiveness (M5, city mean 1)"
                               else "preferences per place",
                               "<extra></extra>"))
  }

  plotly::layout(
    p,
    xaxis = list(title = list(text = CH_PRED[[pred]], font = list(size = 11.5)),
                 zeroline = FALSE, gridcolor = "#eeeeee",
                 range = range(x) + c(-0.13, 0.13) * diff(range(x))),
    yaxis = list(title = list(
                   text = if (show_ylab) "Preferences per place" else "",
                   font = list(size = 11.5)),
                 type = "log", zeroline = FALSE, gridcolor = "#eeeeee",
                 tickvals = c(0.1, 0.15, 0.25, 0.4, 0.6, 1, 1.5, 2.5, 4),
                 ticktext = c("0.1", "0.15", "0.25", "0.4", "0.6",
                              "1.0", "1.5", "2.5", "4.0")))
}

ch_ann <- function(m) list(
  list(x = 0, y = 1.30, xref = "paper", yref = "paper",
       xanchor = "left", yanchor = "top", showarrow = FALSE,
       font = list(size = 11.5, color = "#444444"),
       text = "Preferences counted:"),
  list(x = 0.01, y = 1.01, xref = "paper", yref = "paper",
       xanchor = "left", yanchor = "bottom", showarrow = FALSE,
       font = list(size = 12.5, color = CH_COL[["att8"]]),
       text = sprintf("<b>Headline attainment · R² = %.2f</b>",
                      ch_r2(m, "att8"))),
  list(x = 0.55, y = 1.01, xref = "paper", yref = "paper",
       xanchor = "left", yanchor = "bottom", showarrow = FALSE,
       font = list(size = 12.5, color = CH_COL[["va"]]),
       text = sprintf("<b>Value added · R² = %.2f</b>",
                      ch_r2(m, "va"))))

# Each panel holds a fit line and a point trace per measure, so the
# second panel's block starts after 2 x the number of measures.
ch_btn <- function(i) {
  n <- length(CH_MEASURES)
  vis <- rep(FALSE, 4 * n)
  vis[c((2 * i - 1):(2 * i), 2 * n + (2 * i - 1):(2 * i))] <- TRUE
  rg <- ch_range(cd[[CH_MEASURES[i]]])
  list(method = "update", label = CH_BUTTONS[i],
       args = list(list(visible = vis),
                   list(annotations = ch_ann(CH_MEASURES[i]),
                        yaxis.range = rg, yaxis2.range = rg,
                        yaxis.title.text = if (CH_MEASURES[i] == "M5 attractiveness")
                          "Attractiveness, M5 (city mean 1)" else "Preferences per place")))
}

plotly::subplot(ch_panel("att8", TRUE), ch_panel("va", FALSE),
                nrows = 1, shareY = FALSE, titleX = TRUE, titleY = TRUE,
                margin = 0.055) %>%
  plotly::layout(
    annotations = ch_ann(CH_DEFAULT),
    yaxis = list(range = ch_range(cd[[CH_DEFAULT]])),
    yaxis2 = list(range = ch_range(cd[[CH_DEFAULT]])),
    margin = list(t = 104, b = 54, l = 64, r = 14),
    hoverlabel = list(bgcolor = "white"),
    updatemenus = list(list(
      type = "buttons", direction = "right",
      active = which(CH_MEASURES == CH_DEFAULT) - 1,
      x = 0.135, xanchor = "left", y = 1.36, yanchor = "top",
      pad = list(t = 0, b = 0, l = 2, r = 2),
      bgcolor = "white", bordercolor = "#cccccc",
      font = list(size = 11),
      buttons = lapply(seq_along(CH_MEASURES), ch_btn)))) %>%
  plotly::config(displayModeBar = FALSE)
Figure 26: Preferences per place against headline attainment and against value added, for the ten Brighton & Hove secondary schools. Use the buttons to change which preference rank is counted, or switch to the full model’s attractiveness (M5). Five-year means; the vertical scale is logarithmic and the dashed line is the fitted log-linear relationship.
Show code
CH_FITS %>%
  mutate(r2 = sprintf("%.2f", r2),
         pred = unname(CH_PRED[pred])) %>%
  pivot_wider(id_cols = measure, names_from = pred, values_from = r2) %>%
  mutate(measure = factor(measure, CH_MEASURES)) %>%
  arrange(measure) %>%
  rename(`Preferences counted` = measure) %>%
  knitr::kable(align = "lrr")
Table 13: How much of each preference measure, and of the full model’s attractiveness, the two attainment measures explain. Log-linear fits on ten schools, so these are descriptive rather than inferential.
Preferences counted Attainment 8 (headline) Value added (Lever report)
Weighted (ranks 1-3) 0.84 0.20
First preferences 0.80 0.12
Second preferences 0.69 0.40
Third preferences 0.55 0.01
M5 attractiveness 0.58 0.03

The result is stark. Counting preferences the way section 4.5 does, headline Attainment 8 explains 84% of the variation in how heavily each school is preferred. The value-added measure — the one that actually isolates what the school contributes — explains 20%. Adding value added to the attainment model lifts the fit from 0.84 to 0.85, which is to say barely at all: once you know a school’s headline score, knowing what it contributes tells you almost nothing more about how heavily families ask for it.

Families are choosing on a number that is mostly a description of the existing intake. That makes school choice partly self-fulfilling: a school with a difficult intake posts a low headline score, is chosen less, and receives a still more difficult intake next year. Section 2 showed that Hove Park and BACA are among the schools most penalised by this conflation. This is a communications problem before it is an admissions problem, and the authority is not a neutral party in it, because the authority publishes the guide.

These are the same figures the open Brightopia model publishes, on the same specification and the same five-round window; the render fails if the two ever drift apart.

The same question, asked of the full model’s attractiveness. Section 7 builds a different measure of how much families want each school: the attractiveness M5 balances so that, once distance, the catchment term and competition between schools have done their work, the model’s demand for each school matches its share of the city’s first preferences (Section 7.8.3.2). It is the number the simulator’s attractiveness sliders multiply, and the M5 button on the chart above shows it against the same two scores.

On a log scale it follows headline attainment, R² 0.58, and value added not at all, 0.03. The log scale is the right one. A straight line explains only 0.39, and on the log scale each point of Attainment 8 goes with about 12% more attractiveness — a constant proportion, as with preferences per place, rather than a fixed amount. Refitting without each school in turn gives R² between 0.42 and 0.76. The simulator converts its attractiveness sliders into Attainment 8 points on this fit.

It is a looser fit than preferences per place, and the reason is worth seeing. M5’s attractiveness is what is left once geography and the catchment term have been accounted for, so the two faith schools, which have no catchment term to carry their demand, sit far above the rest — Cardinal Newman at 3.3 times the city mean and King’s at 2.6 — while Dorothy Stringer, at 0.55, and Hove Park, at 0.36, sit low because the catchment term already explains most of their demand. Across the eight catchment schools alone, attainment explains 0.48. Either way the finding stands: families ask for the headline score, not for what a school adds.

NoteOne round is not enough to carry a sign

The model used to fit this regression to the single 2024 admissions round, and on that round value added takes a negative coefficient once attainment is already in the model — which reads as families actively avoiding schools that add value.

Across the five rounds it does not: the coefficient is +0.033, small and positive. The negative sign was one year, ten schools, and no basis for that reading. It is recorded here because it was briefly in an earlier draft, and because the same trap is available to anyone reading a single year’s factsheet.

NoteWhat changes when you count a different preference

Section 4.5 argued that first preferences alone distort the picture, because the paired catchments force families to rank two local schools against each other. The buttons on the chart above are there so that objection can be tested rather than argued about, and Table 13 holds all eight fits at once.

The answer is that the finding survives, it strengthens, and there is one exception worth dwelling on.

It strengthens. The weighted measure gives the strongest association of all with headline attainment, R² 0.84 against 0.80 for first preferences alone. Counting the other ranks does not dilute the finding; it sharpens it. So the association is not an artefact of looking only at firsts — if anything, firsts understate it.

Attainment’s grip weakens down the ranks. It explains 0.80 of first preferences, 0.69 of seconds and 0.55 of thirds. That is what you would expect if the headline score is what families reach for when choosing freely, while lower preferences are increasingly shaped by catchment structure and proximity.

And then the exception. At second preferences, and only there, value added explains a substantial share — 0.40, against 0.12 on firsts and 0.01 on thirds, which is nothing at all.

That is robust to dropping any one school — refitting ten times without each in turn gives R² between 0.29 and 0.59, so it is not one point doing the work. What it means is a different question, and an honest answer is that this cannot settle it. A second preference is chosen with the first already committed, often between two schools in the same paired catchment that a family knows something about beyond the league table. Whether that is families accessing better information at the margin, or something about how paired catchments generate second preferences in the first place, ten schools cannot say.

WarningTen points

Every fit here is on ten schools. That is enough to show an association and nowhere near enough to separate attainment from the things correlated with it — intake, location, reputation, or the Ofsted grade families may actually be reading. Nothing here identifies what causes what.

The point is narrower: the relationship with the headline score is not an artefact of which preference rank you count. That is worth establishing, because it is the obvious objection to the chart above, and it is the objection the council could most easily raise.

The second-preference result should be read as a question rather than a finding. It is the kind of thing the council’s own preference records would settle in an afternoon, and section 9 asks for them.

5.6 Why Hove Park takes more disadvantaged children than Blatchington Mill

This question comes up repeatedly, and it is a good one. Hove Park and Blatchington Mill share a catchment and stand 0.5 km apart, yet Hove Park’s Year 7 is about 35.3% disadvantaged against Blatchington Mill’s 22.4% — a gap of 12.9 points between two schools with the same catchment and, on the face of it, the same geography.

The model can separate two explanations that are usually run together. The disadvantage layer has a term for each school, \(\lambda_j\), which is how strongly that school pulls the disadvantaged children of a neighbourhood it already draws from. Set every \(\lambda\) to one and each school simply takes its proportional share of each neighbourhood’s disadvantaged children: what is left is geography. The difference between that and the published shares is sorting within neighbourhoods.

Show code
hpc$gap %>%
  transmute(Treatment = case,
            `Hove Park` = paste0(hp_pts(hove_park), "%"),
            `Blatchington Mill` = paste0(hp_pts(blatchington), "%"),
            Gap = sprintf("%+.1f pts", 100 * gap)) %>%
  knitr::kable(align = "lrrr")
The disadvantaged share of each school’s modelled Year 7, 2026, under four treatments.
Treatment Hove Park Blatchington Mill Gap
As calibrated 35.3% 22.4% +12.9 pts
Geography only 25.2% 19.8% +5.4 pts
Geography only, Nevill Road 21.1% 21.2% -0.1 pts
Geography only, no faith schools 24.1% 20.6% +3.5 pts

Geography explains about 42% of the gap — roughly a third — and all of that comes from one thing: Year 7 is not taught at Hove Park’s registered address. It is taught at the Valley Campus on Hangleton Way, 1.5 km from Blatchington Mill and in Hangleton rather than central Hove. Put Year 7 at Nevill Road and the geographic part of the gap disappears entirely: -0.1 points, which is nothing. Two schools half a kilometre apart draw the same children, as you would expect.

The other two thirds is sorting, and the fitted \(\lambda\) is a measurement of it:

Show code
tibble::tibble(School = short_sch(names(hpc$lambda)),
               `Pull on disadvantaged children` = sprintf("%.2f", hpc$lambda)) %>%
  arrange(desc(as.numeric(`Pull on disadvantaged children`))) %>%
  knitr::kable(align = "lr")
How strongly each school draws the disadvantaged children of the neighbourhoods it already serves. One means its proportional share.
School Pull on disadvantaged children
Longhill 4.53
Hove Park 1.33
Patcham 1.14
Blatchington Mill 0.98
Varndean 0.93
Brighton Aldridge 0.88
Dorothy Stringer 0.85
Portslade Aldridge 0.72
Cardinal Newman 0.60
King’s 0.49

Hove Park’s 1.33 against Blatchington Mill’s 0.98 is the heart of it: from the same streets, Hove Park takes about 36% more of the disadvantaged children than its share of the intake implies. The two church schools sit at the other end — 0.49 and 0.60, so about half their proportional share — as do Varndean and Dorothy Stringer. Longhill is off the scale in the other direction.

That is the interaction worth naming. Closing King’s and Cardinal Newman in the model narrows the geography-only gap from 5.4 points to 3.5, so their pull is real but modest as a share of the whole. The larger effect is not that the church schools take children out of Hove Park’s catchment; it is that across the city the schools families compete hardest to enter take a smaller share of the disadvantaged children from every neighbourhood they serve, and the undersubscribed schools take the rest. Blatchington Mill fills; Hove Park takes those who did not get in, and those whose families did not name it first.

5.6.1 Will it even out when the Valley Campus closes?

This is the obvious follow-up, and it is a good theory: if the campus is what puts Hove Park’s Year 7 in Hangleton, bringing everyone to Nevill Road should bring the two schools together. The model’s answer is half right.

Show code
hpc$move %>%
  filter(year %in% c(2026, 2028, 2030)) %>%
  transmute(`Entry year` = year,
            `Hove Park` = sprintf("%.1f%% → %.1f%%", 100 * valley_dep, 100 * nevill_dep),
            `Blatchington Mill` = sprintf("%.1f%% → %.1f%%",
                                          100 * valley_dep_bm, 100 * nevill_dep_bm),
            `Gap` = sprintf("%.1f → %.1f pts", 100 * valley_gap, 100 * nevill_gap)) %>%
  knitr::kable(align = "lrrr")
The gap between the two schools’ modelled Year 7, with Hove Park’s Year 7 at each campus.
Entry year Hove Park Blatchington Mill Gap
2026 35.3% → 30.3% 22.4% → 24.1% 12.9 → 6.2 pts
2028 35.5% → 30.3% 22.8% → 24.2% 12.6 → 6.1 pts
2030 35.7% → 30.2% 22.9% → 24.2% 12.8 → 6.0 pts

The gap narrows from 12.9 points to 6.1 — about 52% of it — and it closes from both ends, Hove Park falling and Blatchington Mill rising as the two swap children. But it does not disappear. Hove Park’s intake stays around 6 points more disadvantaged, because the part of the gap that is sorting rather than geography does not care where the building is. Moving a school changes which streets it draws from; it does not change which families name it first, or which school fills and which takes those who did not get in.

One caveat runs the other way, and it matters. \(\lambda\) is fitted over years when the school was split across two sites. If the split site is itself part of why families sort as they do — a lower school in Hangleton shaping what parents think they are choosing — then some of what the model counts as sorting would move with the building too, and more of the gap would close. The model holds that fixed by construction, so 52% is a floor, not a ceiling. Which of the two readings is right will be visible in the September 2028 and 2029 intakes.

WarningWhat this is, and what it is not

\(\lambda_j\) is fitted to reproduce each school’s published disadvantaged share. It measures sorting; it does not explain it. The model can say how much of the Hove Park–Blatchington gap geography accounts for — about a third, all of it the campus — and how much it cannot. It cannot say whether the remaining two thirds is parental choice, the order preferences are given in, the lottery, the priorities, or something else, and nothing here should be read as saying which.

5.7 Does moving the school raise demand, or does the school have to change?

The consolidation on Nevill Road in September 2028 is as close to a natural experiment as this system offers: one school, one known date, one move of 1.65 km east, with everything else held. The model’s answer:

Show code
hpc$move %>%
  transmute(`Entry year` = year,
            `At the Valley Campus` = sprintf("%.0f", valley),
            `At Nevill Road` = sprintf("%.0f", nevill),
            Difference = sprintf("%+.0f", nevill - valley),
            `Disadvantaged share` = sprintf("%.0f%% → %.0f%%",
                                            100 * valley_dep, 100 * nevill_dep)) %>%
  knitr::kable(align = "lrrrr")
Hove Park’s modelled Year 7 with the entry year taught at each campus, and how disadvantaged that intake is.
Entry year At the Valley Campus At Nevill Road Difference Disadvantaged share
2026 168 180 +12 35% → 30%
2028 138 153 +15 35% → 30%
2030 126 140 +14 36% → 30%
2035 109 122 +14 36% → 30%

The move is worth about +15 children a year, an intake 11% larger, and it holds as the cohort shrinks. It is not nothing: it is most of the difference between a viable form entry and an empty one.

But set it against the other lever. Staying at Hangleton Way and making the school 21% more attractive produces exactly the same intake. On this document’s own choice fit (Section 5.5), that multiplier is worth about 2.7 Attainment 8 points. Hove Park’s Attainment 8 is ; the highest in the city is 56.9. So the whole benefit of moving the school across Hove is about what a quarter of its gap to the top of the city would buy — and a single Attainment 8 point is worth roughly 7% more demand, or about 10 children.

The answer, then, is: the site helps, and the school’s standing matters more. The move is a one-off gain of a fixed size. Attractiveness has four times the headroom and compounds.

And there is a loop here that the model does not close, which is the most interesting thing in this section. The move itself makes Hove Park’s intake less disadvantaged — 35% to 30% in 2028 — purely because Nevill Road draws different streets. A less disadvantaged intake raises the school’s raw Attainment 8, because that is largely what raw Attainment 8 measures (Section 2.3). A higher headline score raises demand, because that is what families read (Section 5.5). Which raises the intake again.

That is the Longhill loop (Section 1) running forwards instead of backwards, and the consolidation may start it. Nothing in this model predicts it: \(W_j\) is fixed in every run above, so the figures here are the move without the loop. What they do establish is where to look, and the September 2028 and 2029 factsheets will show whether it happens. This is a prediction worth recording now, because a natural experiment is only useful if the prediction is written down before the result arrives.

5.8 What if families read a different number?

Everything above turns on a single fact: the number families read is the headline score, and the headline score mostly describes the intake (Section 5.5, Section 2.3). That is a loop, and the council holds one end of it, because the council writes the admissions guide (Section 1). It cannot make families choose differently. It can stop pointing them at the wrong number, and publish a contextualised one beside it.

So it is worth asking what the city would look like if they read it. The model can answer, because attractiveness in it is calibrated to what families ask for, and that can be rebuilt on a different measure. This run puts the four-year value added score on the Attainment 8 scale and applies the response families currently show to Attainment 8 — about 7% more demand per place per point. Everything else is untouched: the same places, the same catchments, the same rules, the same journeys. Only the number families are taken to be reading changes.

It is a large change. Brighton Aldridge, whose value added is the city’s strongest relative to its headline score, becomes 2.6 times as attractive; Patcham, whose value added is the weakest, falls to 0.4 times.

Show code
va26 %>%
  transmute(School = short,
            `Places` = fmt_n(pan),
            `Reading the headline score` = fmt_n(round(att8_intake)),
            `Reading value added` = fmt_n(round(va_intake)),
            Change = sprintf("%+.0f", change),
            `How full it ends up` = sprintf("%.0f%% → %.0f%%",
                                            100 * att8_fill, 100 * va_fill)) %>%
  knitr::kable(align = "lrrrrr")
Year 7 intakes in 2026 with families reading each measure. Same cohort, same places, same rules.
School Places Reading the headline score Reading value added Change How full it ends up
Brighton Aldridge 180 91 175 +84 50% → 97%
Portslade Aldridge 220 153 179 +26 69% → 81%
Longhill 210 90 106 +15 43% → 50%
Hove Park 180 168 180 +12 93% → 100%
Dorothy Stringer 330 330 330 +0 100% → 100%
King’s 180 180 180 +0 100% → 100%
Varndean 300 300 300 +0 100% → 100%
Cardinal Newman 360 360 330 -30 100% → 92%
Blatchington Mill 330 330 289 -41 100% → 88%
Patcham 225 215 148 -67 96% → 66%

Brighton Aldridge is the headline. It goes from 91 children to 175, from half empty to 97% full, without a boundary moving or a place being cut. Portslade Aldridge, Longhill and Hove Park all fill further. The schools that lose are the ones whose headline scores flatter them: Patcham, whose four-year value added is the weakest in the city, and Cardinal Newman, whose intake by 2035 falls by 115.

Now the caveats, because they matter as much as the finding.

It does not create a single place or remove one. The cohort is what it is, so the city still has 299 empty Year 7 places either way. What changes is where they are: on the headline score the empty places sit at the schools whose viability is in question, and on value added they sit at schools with room to absorb it. That is the whole of the effect, and it is not nothing — it is the difference between a school that looks like it is failing and one that looks full.

The children sit more evenly across the places the city has — for now. There is a single number for this. Ask how many children would have to change school for every school to be equally full, given the places that exist. It is zero in a city where every school is at the same percentage, and it rises with the unevenness however that unevenness arises.

Show code
vac$even %>%
  transmute(`Entry year` = year,
            `Reading the headline score` = fmt_n(round(move_att8)),
            `Reading value added` = fmt_n(round(move_va)),
            `Spread of how full, now` = sprintf("%.0f%% to %.0f%%",
                                                100 * fill_lo_att8, 100 * fill_hi_att8),
            `Spread of how full, then` = sprintf("%.0f%% to %.0f%%",
                                                 100 * fill_lo_va, 100 * fill_hi_va),
            `Empty places in the three emptiest schools` =
              sprintf("%.0f%% → %.0f%%", 100 * top3_att8, 100 * top3_va)) %>%
  knitr::kable(align = "lrrrrr")
How evenly the city’s children sit across its places, reading each measure. The first column is the number who would have to change school for every school to be equally full.
Entry year Reading the headline score Reading value added Spread of how full, now Spread of how full, then Empty places in the three emptiest schools
2026 204 147 43% to 100% 50% to 100% 93% → 75%
2030 200 200 34% to 100% 40% to 100% 58% → 59%
2035 230 195 30% to 100% 37% to 100% 53% → 53%

In 2026 it falls from 204 children to 147, a reduction of about 28%, and the fullest-to-emptiest range narrows from 43% to 50% at the bottom. The sharpest number is the last column: today 93% of the city’s empty Year 7 places sit in just three schools, and on value added that falls to 75%. A surplus spread across ten schools is a budgeting problem; a surplus piled into three is a viability problem, and it is the second the city actually has.

But read the later years before making too much of it. By 2030 the measure is 200 either way: the unevenness does not shrink, it moves — out of Brighton Aldridge and Longhill and into Cardinal Newman and Patcham, which fall well below their numbers in turn. That is the honest limit of this lever. Changing what families read can redistribute children across the places that exist; it cannot conjure children into a city that has 720 more places than children by 2035. Only the places themselves can do that, which is section 10’s argument and not this one’s.

Journeys get shorter, and most for the children who travel furthest. Mean journey falls from 29.5 to 28.8 minutes, and the extra distance the most deprived children travel falls from 3.4 to 2.5 minutes each way. Children fill the schools near them.

Segregation rises slightly, from 0.117 to 0.127 on the index used throughout section 8. The schools that fill are the more disadvantaged ones, so their intakes grow. On the evidence in Section 2.3.1 that is not obviously a cost to the children concerned, but it is a real movement in the measure the council has said it cares about, and it should not be hidden.

WarningWhat this is not

It is not a forecast. It assumes families would respond to a contextualised measure exactly as strongly as they now respond to the headline one, which is an assumption about publicity and persuasion, not a fitted quantity. And value added for a single school is a noisy measure — Patcham’s fall here rests on one school’s four-year figure. The honest claim is narrower than the table looks: the number families are pointed at is doing a great deal of work in this system, and it is the cheapest thing in this document to change.

You can run this yourself. The simulator has a control for what families are choosing on, with the scenario “Families choose on value added”, and the per-school attractiveness sliders work on top of it.

5.9 What pupil-level data would add here

This is a school-level association across ten schools — ten points. It cannot distinguish families responding to attainment from families responding to something correlated with it, and it cannot say whether different kinds of family respond differently. The council holds the preference ordering of every applicant. With it, the same question becomes a discrete-choice model over thousands of decisions, and the answer would be worth acting on rather than merely worth noting.

6 Money

A school’s finances follow its roll, because funding is overwhelmingly per-pupil. A school losing children is therefore losing income against a cost base that does not shrink at the same rate — the building, the leadership team and the curriculum offer are all substantially fixed.

That is the mechanism. This section asks what it has already done to the city’s ten state secondary schools, and what the projections in Section 3 will do to them next.

6.1 Nine of the ten schools are spending their reserves

Across the three years every school has filed, the city’s ten secondary schools spent £2.3m of their combined revenue reserves — from £5.9m in 2021-22 to £3.6m in 2023-24, a fall of 39%. Schools with a negative reserve went from 1 to 3, and schools spending more in the year than they received went from 2 to 6.

9 of the 10 schools ran their reserve down over the period. 1 did not.

The 7 maintained schools have filed a fourth year, and it is worse: their combined reserve went from £4.6m in 2021-22 to −£395k in 2024-25, and their combined in-year balance from £1.3m to −£2.4m.

NoteThat aggregate is carried by two schools, and it matters which

The combined figure crosses zero because of Cardinal Newman alone. Take it out and the other 6 maintained schools still hold £1.4m between them.

The aggregate is worth reporting because it is the sum the council is responsible for, but the distribution is the finding. Two schools are in serious deficit, one is at the line, and the rest are comfortable and getting less so.

Show code
fex %>%
  arrange(reserve_pct) %>%
  transmute(School = name,
            Year = year_label,
            Roll = fmt_n(roll),
            Income = gbp(income),
            `Per pupil` = paste0("£", fmt_n(income_pp)),
            # Two columns called "— % of income" is one column: the
            # second silently replaced the first, and the table printed
            # the reserve percentage in the in-year column.
            `In-year balance` = gbp(balance_pct * income),
            `In-year, % of income` = pp(balance_pct),
            `Revenue reserve` = gbp(reserve),
            `Reserve, % of income` = pp(reserve_pct),
            `Staff costs, % of income` = sprintf("%.0f%%", 100 * staff_pct)) %>%
  knitr::kable(align = "llrrrrrrrr")
Table 14: Every state secondary school in the city, at its latest published return. Maintained schools report to 31 March on the Consistent Financial Reporting return; academies report to 31 August on the Academies Accounts Return, so their latest published year is one behind. The revenue reserve is the accumulated surplus or deficit carried forward; it is shown as a share of annual income, because a given sum of money means different things at a school of 723 and a school of 2,625. Ranked worst first.
School Year Roll Income Per pupil In-year balance In-year, % of income Revenue reserve Reserve, % of income Staff costs, % of income
Hove Park School and Sixth Form Centre 2024-25 948 £9.6m £10,083 −£546k −5.7% −£1.1m −11.0% 83%
Cardinal Newman Catholic School 2024-25 2,625 £17.6m £6,704 −£1.7m −9.9% −£1.8m −10.1% 91%
Patcham High School 2024-25 1,099 £9.0m £8,221 £371k +4.1% −£82k −0.9% 70%
Brighton Aldridge Community Academy 2023-24 802 £7.2m £8,929 £383k +5.3% £1k +0.0% 71%
Blatchington Mill School 2024-25 1,635 £12.4m £7,611 −£246k −2.0% £492k +4.0% 75%
Varndean School 2024-25 1,497 £12.2m £8,173 −£151k −1.2% £662k +5.4% 70%
Dorothy Stringer School 2024-25 1,626 £13.2m £8,134 £37k +0.3% £831k +6.3% 76%
Portslade Aldridge Community Academy 2023-24 974 £7.7m £7,859 £791k +10.3% £534k +7.0% 68%
Longhill High School 2024-25 723 £7.5m £10,358 −£168k −2.2% £531k +7.1% 76%
King’s School 2023-24 876 £6.5m £7,365 £795k +12.3% £1.0m +15.7% 71%

Source: DfE school income and expenditure — Consistent Financial Reporting (CFR) returns for maintained schools and Academies Accounts Returns (AAR) for academies, both published per school on the schools financial benchmarking service.

NoteWhy the year is not the same for every school

Every school files a return every year. The years differ here for two reasons, neither of them a gap in what schools publish.

They have different year ends. A maintained school reports on the CFR return to 31 March. An academy reports on the AAR to 31 August, because that is its financial year. A 2024-25 figure therefore means something slightly different on the two rows, and there is no way to make them the same without throwing away half the data.

The academy returns are published later in the cycle. The national finance panel behind this section was assembled on 10 September 2026 and contains CFR returns up to 2024-25 but no AAR returns for that year at all — not for Brighton and not for anywhere, nationally. So the 3 academies here (Brighton Aldridge, King’s, and Portslade Aldridge) show 2023-24. If a newer release is out by the time you read this, re-running R/01_assemble.R picks it up and the table closes.

The consequence for reading the table is small but real: the academies’ figures are a year staler, and a year in which several of these schools moved. It is one reason the funding allocations below, which are the same year for everybody, are worth having alongside.

Two schools are in serious deficit. Hove Park’s revenue reserve is −£1.1m, or −11.0% of its annual income, and it spent £546k more than it received in 2024-25. Cardinal Newman’s reserve is −£1.8m, or −10.1%, and its in-year deficit was £1.7m.

They are in trouble for opposite reasons, and the difference is the whole argument of this section. Hove Park’s roll fell from 1,170 to 948 over four years — 222 pupils, each of them carrying funding out of the building. Cardinal Newman’s roll rose, by 175. It is the largest school in the city and it is growing, and it is still the deepest deficit in the city.

The number that separates them from everybody else is the last column. Staff costs are 91% of income at Cardinal Newman and 83% at Hove Park, against 73% across the city. A school whose staffing is a fifth higher, as a share of income, than its neighbours’ has no room to absorb anything, and staffing is the cost that takes years and redundancies to move.

Show code
# Deficit is a status, not a category or a magnitude, so it takes a
# status colour and it ships with a label rather than being left as
# "the red one".
DEFICIT_COL <- "#d03b3b"

ord <- fex %>% arrange(reserve_pct) %>% pull(name)
rp <- fpan %>%
  mutate(school = factor(short_sch(name), short_sch(ord)),
         yr = year,
         neg = pmin(reserve_pct, 0))

ggplot(rp, aes(yr, reserve_pct)) +
  geom_hline(yintercept = 0, colour = "grey40", linewidth = 0.4) +
  geom_ribbon(aes(ymin = neg, ymax = 0), fill = DEFICIT_COL, alpha = 0.28) +
  geom_line(linewidth = 0.8, colour = "grey20") +
  geom_point(size = 1.1, colour = "grey20") +
  facet_wrap(~ school, ncol = 5) +
  scale_x_continuous(breaks = sort(unique(rp$yr)),
                     labels = function(x) paste0(substr(x, 3, 4), "/",
                                                 substr(x + 1, 3, 4))) +
  scale_y_continuous(labels = scales::label_percent()) +
  labs(x = NULL, y = "Revenue reserve as a share of annual income",
       title = "Every school but one has been spending its reserve",
       subtitle = str_wrap(paste("Shaded years are years the school was carrying a deficit.", "The line is the accumulated surplus, not the in-year balance."), 100),
       caption = "Source: DfE school income and expenditure (CFR returns for maintained schools, academy accounts returns for academies).") +
  theme_bh(9) +
  theme(axis.text.x = element_text(size = 6),
        panel.spacing.x = unit(7, "pt"))
Figure 27: Revenue reserve as a share of annual income, for every state secondary school in the city. Panels are ordered by the latest figure, worst first. Shading marks the years a school was carrying a deficit. The academies’ series ends a year earlier because their accounts are published on a different cycle — see the note under the table above.

Published per school on the schools financial benchmarking service.

6.2 Small schools receive more per pupil and are still likelier to be in deficit

The national picture says why a shrinking school is a financial problem rather than merely a smaller one.

Show code
sf_ <- os$size_fin %>%
  mutate(size_band = factor(size_band, levels = size_band))

pa <- ggplot(sf_, aes(size_band, income_pp)) +
  geom_col(fill = "#2166ac", width = 0.7) +
  scale_y_continuous(labels = label_dollar(prefix = "£", accuracy = 1)) +
  labs(x = NULL, y = NULL, subtitle = "Income per pupil") +
  theme_bh(11) + theme(axis.text.x = element_text(angle = 40, hjust = 1))

pb <- ggplot(sf_, aes(size_band, pct_deficit)) +
  geom_col(fill = "#b2182b", width = 0.7) +
  scale_y_continuous(labels = label_percent(scale = 1)) +
  labs(x = NULL, y = NULL, subtitle = "Schools in deficit") +
  theme_bh(11) + theme(axis.text.x = element_text(angle = 40, hjust = 1))

(pa | pb) +
  plot_annotation(
    title = "Small schools receive more per pupil and are still likelier to be in deficit",
    subtitle = "Secondary schools in England, by roll",
    caption = "Source: DfE school income and expenditure data.",
    theme = theme_bh())
Figure 28: Income per pupil and the proportion of schools in deficit, by size band, nationally.

Small schools are not starved of money per head — they receive 1,069 more per pupil than the largest band. They are nonetheless the likeliest to be in deficit: 38% of schools under 500 pupils, against 24% at the most favourable size. Extra funding per head does not compensate for the loss of scale.

6.2.1 The same argument, for Brighton’s own schools

The national picture is a correlation across thousands of schools. The funding formula says why, and it says it in cash, for these ten schools, in a single year.

Show code
fund %>%
  left_join(fex %>% select(urn, name), by = "urn") %>%
  arrange(fund_pupils) %>%
  transmute(School = name,
            `Pupils, 5–16` = fmt_n(fund_pupils),
            `Block allocation` = gbp(block),
            # Two columns with the same name is one column: dplyr keeps
            # the second and drops the first, silently. This table had
            # two called "— per pupil" until the render was read.
            `Follows pupils` = gbp(pupil_led),
            `Pupil-led, per pupil` = paste0("£", fmt_n(marginal_pp)),
            `Does not follow pupils` = gbp(fixed),
            `Fixed, per pupil` = paste0("£", fmt_n(fixed_pp)),
            `of which lump sum` = paste0("£", fmt_n(lump_pp))) %>%
  knitr::kable(align = "lrrrrrrr")
Table 15: The 2025-26 schools block allocation, split into the money that follows pupils and the money that does not. The lump sum is a flat cash amount paid to every secondary school whatever its size; rates, PFI and split-site payments belong to the site rather than the roll. Covers ages 5 to 16, so sixth forms are not in it. Ranked smallest school first.
School Pupils, 5–16 Block allocation Follows pupils Pupil-led, per pupil Does not follow pupils Fixed, per pupil of which lump sum
Brighton Aldridge Community Academy 700 £5.7m £5.5m £7,889 £152k £217 £217
Longhill High School 719 £5.7m £5.3m £7,440 £342k £475 £212
King’s School 867 £5.8m £5.7m £6,548 £152k £175 £175
Hove Park School and Sixth Form Centre 949 £7.5m £7.0m £7,331 £527k £555 £160
Portslade Aldridge Community Academy 950 £6.8m £6.7m £7,026 £152k £160 £160
Patcham High School 1,107 £7.8m £7.4m £6,675 £444k £401 £137
Varndean School 1,493 £10.4m £9.9m £6,643 £511k £342 £102
Blatchington Mill School 1,626 £11.1m £10.7m £6,570 £428k £263 £94
Dorothy Stringer School 1,643 £11.6m £11.1m £6,744 £566k £344 £93
Cardinal Newman Catholic School 1,851 £12.7m £12.4m £6,707 £236k £128 £82

Source: DfE school funding statistics, school-level allocations for 2025-26.

Every secondary school in England gets the same lump sum, and this year it is £152,113. Spread over 1,851 pupils at Cardinal Newman that is £82 a head. Spread over 700 at Brighton Aldridge it is £217. The smaller school is better funded per pupil, which is the formula working as designed — and it still has to run a full curriculum, a leadership team and a building on £5.7m against £12.7m.

This is also the number that matters for what a lost child costs. It is not a school’s average funding per pupil, which is what an income statement shows. A departing Year 7 takes the pupil-led money with them — between £6,548 and £7,889 a year across these ten schools — and leaves every pound of the lump sum, the rates and the PFI behind, to be carried by the children who remain. Section 6.3 uses the pupil-led rate for exactly that reason.

6.3 What the projections do to this

Section 3 projects the cohort. Section 7 turns it into intakes school by school. Putting the two next to the accounts gives the question this section exists to answer: which schools are heading into a smaller roll from a weak financial position.

The arithmetic is deliberately crude, and it is stated so that a reader can redo it. Each Year 7 place a school loses takes 5 pupils off its roll once the smaller cohort has worked through. Multiplied by that school’s own income per pupil, that is the annual income it is heading for in today’s money.

ImportantThe baseline is the intake that happened, not the modelled one

An earlier version of this section took the modelled 2026 intake as each school’s starting point. That was wrong, and the error was not small.

The model places every child in the projected cohort somewhere in the city. The schools that are full absorb only their admission number, so the surplus lands on the schools that are not full — which is exactly where this section is looking. Against the offers the council actually made for September 2026 it puts 183 children into Longhill, which admitted 81, and 197 into Cardinal Newman, which admitted 360.

In total it is close: 2,302 modelled against 2,250 offered across the ten schools, 2% out. The error is in the distribution, which is a fair thing to ask of a model with no calibration data and a fatal thing to build a financial table on.

So the baseline below is the published offer count, and the model supplies only the trajectory — the proportional change in a school’s intake to 2035, driven by the cohort in its catchment shrinking. Read the “modelled” column as a diagnostic, not as an intake.

The city has 2,515 Year 7 places in these ten schools and made 2,250 offers for 2026 entry. On the model’s trajectory that becomes 1,792 by 2035 — a fill rate falling from 89% to 71%, and 723 empty places. Carried through to the whole roll at each school’s own funding rate, that is about £15.7m a year of income gone from a system that currently receives £102.9m.

Show code
xr <- range(fex$intake_pct)
PT_MAX <- 11

ggplot(fex, aes(intake_pct, reserve_pct)) +
  annotate("rect", xmin = -Inf, xmax = 0, ymin = -Inf,
           ymax = sfin$reserve_thin, fill = DEFICIT_COL, alpha = 0.07) +
  # The shaded corner needs naming inside the plot, but the obvious
  # place for the label is on top of Longhill. It goes in the empty band
  # to the left of Patcham instead.
  annotate("text", x = xr[1] - 0.05, y = -0.055,
           label = "Thin reserve,\nand a shrinking intake", hjust = 0,
           vjust = 1, size = 3, lineheight = 0.95, colour = DEFICIT_COL,
           fontface = "bold") +
  geom_hline(yintercept = 0, colour = "grey45", linewidth = 0.4) +
  geom_hline(yintercept = sfin$reserve_thin, colour = "grey65",
             linetype = "31") +
  geom_vline(xintercept = 0, colour = "grey65", linetype = "31") +
  # Filled for the eight schools whose modelled intake can be read as a
  # projection, hollow for the two whose cannot. Setting fill outside
  # aes() drew every circle white and lost the distinction entirely.
  geom_point(aes(size = roll, fill = faith), shape = 21,
             colour = "#1f3b57", stroke = 0.9) +
  # ggrepel does not know how big a point is unless it is told, and it
  # wants that size in the same millimetres the point scale uses, not in
  # pupils: handed the raw roll it treated every circle as enormous and
  # threw the labels to the edges of the panel. scale_size_area() draws
  # a point at max_size * sqrt(value / max(value)), so that is what it
  # is given.
  ggrepel::geom_text_repel(
    aes(label = short, point.size = PT_MAX * sqrt(roll / max(roll))),
    size = 3, seed = 3, min.segment.length = 0, segment.size = 0.25,
    box.padding = 0.5, max.overlaps = Inf, colour = "grey15") +
  scale_fill_manual(values = c(`FALSE` = "#1f3b57", `TRUE` = "white"),
                    guide = "none") +
  scale_size_area(max_size = PT_MAX, guide = "none") +
  scale_x_continuous(labels = scales::label_percent(),
                     limits = c(xr[1] - 0.07, 0.07)) +
  scale_y_continuous(labels = scales::label_percent()) +
  labs(x = "Change in modelled Year 7 intake, 2026 to 2035",
       y = "Revenue reserve as a share of annual income",
       title = "Where the money is, and where the children are going",
       subtitle = str_wrap(paste("Circle area is the school's roll.", "Hollow circles are the two faith schools, whose modelled intake should not be read as a forecast."), 100),
       caption = "Sources: DfE school income and expenditure; modelled intakes, configuration A of the open scenarios.") +
  theme_bh(11)
Figure 29: Financial position against projected intake. The vertical axis is the revenue reserve as a share of annual income at the latest published return; the horizontal axis is the change in modelled Year 7 intake between 2026 and 2035 under the city as it stands. Circle area is proportional to the school’s roll. The shaded corner is a thin or negative reserve together with a shrinking intake. Hollow circles are the two faith schools, whose modelled intake is the least reliable figure in this document.
Show code
fex %>%
  arrange(income_pct) %>%
  transmute(School = paste0(name, if_else(faith, " †", "")),
            `Reserve, % of income` = pp(reserve_pct),
            `Offers 2026` = fmt_n(offers),
            `Modelled 2026` = fmt_n(intake_first),
            `Projected 2035` = fmt_n(intake_proj),
            Change = pp(intake_pct, 0),
            `Roll, steady state` = sub("-", "\u2212",
                                       sprintf("%+.0f", roll_change_ss),
                                       fixed = TRUE),
            `Income a year` = gbp(income_change),
            `— % of income` = pp(income_pct, 0)) %>%
  knitr::kable(align = "lrrrrrrrr")
Table 16: What the projected intakes are worth. The baseline is the offers the council published for 2026 entry. The modelled column is shown only so the gap between the two can be seen — it is the model’s own 2026 level, which is not an intake and is badly out for the schools that are not full. The projection applies the model’s proportional change to 2035 to the published baseline. The income column then applies each school’s own income per pupil to the steady-state change in roll, in today’s money: an order of magnitude, not a budget line.
School Reserve, % of income Offers 2026 Modelled 2026 Projected 2035 Change Roll, steady state Income a year — % of income
King’s School † +15.7% 180 163 94 −48% −432 −£2.8m −44%
Cardinal Newman Catholic School † −10.1% 360 197 190 −47% −849 −£5.7m −32%
Brighton Aldridge Community Academy +0.0% 146 173 88 −40% −292 −£2.3m −32%
Longhill High School +7.1% 81 183 51 −36% −148 −£1.1m −15%
Patcham High School −0.9% 204 225 169 −17% −176 −£1.2m −13%
Dorothy Stringer School +6.3% 330 330 283 −14% −235 −£1.6m −12%
Portslade Aldridge Community Academy +7.0% 183 220 171 −7% −62 −£436k −6%
Blatchington Mill School +4.0% 330 330 311 −6% −96 −£633k −5%
Hove Park School and Sixth Form Centre −11.0% 136 180 136 +0% +0 £0 +0%
Varndean School +5.4% 300 300 300 +0% +0 £0 +0%

† Cardinal Newman and King’s are faith schools. The model admits to them on distance alone, because their actual criteria are not in any published dataset, so their intake columns are the least reliable figures in this document. Cardinal Newman is currently over-subscribed and growing.

4 schools sit in the corner that matters — a reserve under 5% of income and an intake the model expects to fall. They are Blatchington Mill, Brighton Aldridge, Cardinal Newman, and Patcham.

Cardinal Newman is the largest school in the city and already −10.1% on its reserve — though it is also the school whose projected intake is least worth trusting. Patcham is at −0.9% with a projected fall of −17%, worth −£1.2m a year, or −13% of its income.

Blatchington Mill is the fourth, and it is there on a technicality worth stating: its reserve is +4.0%, just under the line, and its projected fall is only −6%. It is the least worrying school in the shaded corner and it is in it because the threshold is a line rather than a judgement.

Brighton Aldridge is the most exposed school whose numbers can be trusted. It has spent its reserve to £1k, its intake is projected to fall −40%, and that is worth −£2.3m a year, or −32% of its income.

Longhill is not, and that is the correction the baseline forced. Its remaining exposure is −15%, worth −£1.1m a year — the fourth largest share in the city, not the first. The reason is that Longhill has already taken most of the fall: it admitted 81 children in 2026 against an admission number of 210, and a school at 39% of its admission number has less left to lose than one that is full. Its reserve is still +7.1%, and Section 6.6 is about how long that lasts.

Hove Park is the same story further along. Its intake is projected not to fall at all, and it has the worst reserve position in the city as a share of income. Its roll has already dropped by 222 in four years and its costs have not followed.

So the two schools with the least to lose from the projections are two of the three already in deficit. The financial damage at both is done rather than coming, and no future intake number fixes it. What is coming lands on Brighton Aldridge, Patcham and Dorothy Stringer — the first two with nothing left to absorb it.

WarningFive things this arithmetic is not

It is not a forecast of any school’s budget. It is a projection of pupil numbers multiplied by a current funding rate. Real budgets move with pay awards, energy, the national funding formula, sixth-form funding, high-needs recoupment and one-off capital, none of which is here.

The baseline is published, the trajectory is modelled. The starting point is the offers the council made for 2026 entry. The path from there is the open model’s configuration A — the city as it stands, with today’s admission numbers held constant to 2035 — applied as a proportional change, not as a level. That is the point of the exercise rather than a prediction: it is what happens if nothing is decided. The council is already reducing admission numbers, which changes every column.

A proportional trajectory is an assumption in its own right. It says a school’s intake falls in step with the cohort the model sends towards it. A school that is already far below its admission number might fall faster, because the families still choosing it are the most local ones, or slower, because it has already lost everyone who was going to leave. Nothing here distinguishes those two.

The per-pupil rate is each school’s own average, and for the four schools with a sixth form it blends 11–16 and 16–18 funding. Their pound figures are rougher than the others’.

Two reporting cycles are being read side by side. Maintained schools file to 31 March and academies to 31 August, so 2024-25 figures sit next to 2023-24 ones. Nothing has been adjusted to hide the gap; the year is in the table.

6.4 Which schools are most at risk, and when

Section 6.3 gives each school’s exposure as a single figure for 2035. It does not say when the money runs out, and that is the question a council has to answer first. This section projects the roll year by year and asks what it does to each school’s annual position.

The arithmetic, in four lines. A school’s roll falls by some number of pupils. Each of them was carrying the pupil-led funding in Section 6.2.1, so the school’s income falls by that number times that rate. It responds by taking cost out — mostly teaching staff — but it cannot take out all of it, because the leadership team, the building and the curriculum offer do not shrink in step. Whatever it cannot take out is added to the deficit it is already running.

The one judgement in that is how much cost a school can shed, so it is shown at three values rather than one.

Show code
rgap <- risk_mid %>% mutate(ends_down = short %in% r_neg$short)

# A share of income rather than pounds. In cash, Cardinal Newman's
# -£3.1m sets the axis and squashes the other nine into the top fifth of
# the panel; as a share of income the range is a fifth as wide and every
# school is legible. It is also the comparison the rest of this section
# makes - reserve, staff costs and balance are all shares of income -
# and the cash figures are in the table below.
#
# Six lines, from the categorical set used elsewhere in this document,
# assigned in order of how bad the 2035 position is. The set's usual
# sixth colour is a second green, which sat indistinguishably next to
# the first here; purple replaces it and the six still validate.
RISK_COL <- setNames(
  c("#2a78d6", "#eb6834", "#1baf7a", "#eda100", "#e87ba4", "#6b4fbb")[
    seq_len(nrow(r_neg))],
  r_neg$short[order(r_neg$gap_pct)])

# One repel call for every label, not one per group: two calls do not
# know about each other's labels and "Patcham" landed on top of
# "Varndean".
ends <- rgap %>% filter(year == max(RISK_YEARS)) %>%
  mutate(lab_col = if_else(ends_down, unname(RISK_COL[short]), "grey45"))

ggplot(rgap, aes(year, gap_pct, group = short)) +
  geom_hline(yintercept = 0, colour = "grey35", linewidth = 0.4) +
  geom_line(data = rgap %>% filter(!ends_down),
            colour = "grey72", linewidth = 0.7) +
  geom_line(data = rgap %>% filter(ends_down),
            aes(colour = short), linewidth = 1) +
  ggrepel::geom_text_repel(
    data = ends, aes(label = short), colour = ends$lab_col,
    size = 2.6, hjust = 0, direction = "y", nudge_x = 0.3,
    segment.size = 0.22, min.segment.length = 0, seed = 4,
    box.padding = 0.12) +
  scale_colour_manual(values = RISK_COL, guide = "none") +
  scale_x_continuous(breaks = seq(min(RISK_YEARS), max(RISK_YEARS), 2),
                     limits = c(min(RISK_YEARS), max(RISK_YEARS) + 3.8)) +
  scale_y_continuous(labels = function(x) pp(x, 0)) +
  labs(x = NULL, y = "Annual surplus or deficit, as a share of income",
       title = paste(nrow(r_neg), "schools end below the line, and",
                     sum(rgap$gap[rgap$year == min(RISK_YEARS)] < 0),
                     "are already there"),
       subtitle = str_wrap(paste("Grey lines are the schools that stay in surplus.",
                                 "Middle assumption: a school takes three quarters of the cost out when the funding goes."), 105),
       caption = "Sources: DfE school income and expenditure; DfE school funding statistics 2025-26; BHCC allocation factsheets; modelled intakes, configuration A.") +
  theme_bh(9.5)
Figure 30: The annual gap between what each school receives and what it spends, as a share of its income, projected to 2035 on the middle assumption that a school takes out three quarters of the cost when it loses the funding. Each line starts at that school’s most recent in-year balance and deteriorates as the roll falls. The schools that end below zero are coloured; the rest stay in surplus and are drawn in grey.
Show code
risk_end %>%
  filter(scenario == SHED_MID) %>%
  arrange(gap_pct) %>%
  transmute(School = paste0(name, if_else(faith, " †", "")),
            `Roll 2026` = fmt_n(fund_pupils),
            `Roll 2035` = fmt_n(roll),
            `Change` = pp(d_roll / fund_pupils, 0),
            `Funding lost` = gbp(lost_funding),
            `Gap in 2035` = gbp(gap),
            `— % of income` = pp(gap_pct),
            `Reserve now` = gbp(reserve),
            `Years of reserve` = case_when(
              reserve <= 0 ~ "already overdrawn",
              is.na(years_left) ~ "still in surplus",
              TRUE ~ sprintf("%.1f", years_left))) %>%
  knitr::kable(align = "lrrrrrrrr")
Table 17: Where each school ends up by 2035, and how long its reserve lasts. The gap is the projected annual surplus or deficit at that point, on the middle assumption. Years of reserve divides today’s reserve by that gap; a school already overdrawn has none to divide. Ranked by the 2035 gap as a share of income.
School Roll 2026 Roll 2035 Change Funding lost Gap in 2035 — % of income Reserve now Years of reserve
Cardinal Newman Catholic School † 1,851 1,046 −43% £5.4m −£3.1m −17.6% −£1.8m already overdrawn
Longhill High School 719 442 −39% £2.1m −£683k −9.1% £531k 0.8
Hove Park School and Sixth Form Centre 949 823 −13% £924k −£777k −8.1% −£1.1m already overdrawn
Blatchington Mill School 1,626 1,615 −1% £70k −£264k −2.1% £492k 1.9
Dorothy Stringer School 1,643 1,497 −9% £985k −£209k −1.6% £831k 4.0
Varndean School 1,493 1,483 −1% £66k −£168k −1.4% £662k 4.0
Patcham High School 1,107 936 −15% £1.1m £86k +1.0% −£82k already overdrawn
Brighton Aldridge Community Academy 700 610 −13% £711k £205k +2.9% £1k still in surplus
King’s School † 867 504 −42% £2.4m £200k +3.1% £1.0m still in surplus
Portslade Aldridge Community Academy 950 870 −8% £562k £650k +8.5% £534k still in surplus

5 of the ten were already spending more than they received in their latest return, and the falling roll makes it worse for every one of them. Two are in a different category from the rest: Cardinal Newman and Hove Park are carrying accumulated deficits of £1.8m and £1.1m — both, on the measure Schools Week uses for the schools in the deepest trouble nationally, million-pound deficits already. Neither is a small school and neither got there by being unpopular: Cardinal Newman is the largest school in the city and its roll has been rising.

Longhill is the school the projection changes most. Its in-year position today is only −£168k, which does not look like a crisis. But its roll falls by 39% by 2035 — the steepest fall of any school whose projection can be trusted — and that takes £2.1m of funding with it. On the middle assumption its annual gap reaches −£683k, which its £531k reserve covers for 0.8 years.

Four schools absorb it. Brighton Aldridge, King’s, Patcham, and Portslade Aldridge are running surpluses large enough that the roll loss does not put them under, even by 2035. Two of them — Brighton Aldridge and Patcham — are doing that from reserves at or below zero, so they are repairing rather than comfortable.

Show code
risk_end %>%
  mutate(scenario = factor(scenario, names(SHED))) %>%
  select(name, faith, scenario, gap_pct) %>%
  tidyr::pivot_wider(names_from = scenario, values_from = gap_pct) %>%
  arrange(.data[[SHED_MID]]) %>%
  transmute(School = paste0(name, if_else(faith, " †", "")),
            across(all_of(names(SHED)), ~ pp(.x, 1))) %>%
  knitr::kable(align = "lrrr")
Table 18: How much the answer depends on the one assumption. Each cell is the projected annual gap in 2035 as a share of the school’s income. Ranked by the middle column.
School Sheds 90% of the cost Sheds 75% of the cost Sheds 50% of the cost
Cardinal Newman Catholic School † −13.0% −17.6% −25.2%
Longhill High School −5.0% −9.1% −16.0%
Hove Park School and Sixth Form Centre −6.7% −8.1% −10.5%
Blatchington Mill School −2.0% −2.1% −2.3%
Dorothy Stringer School −0.5% −1.6% −3.4%
Varndean School −1.3% −1.4% −1.5%
Patcham High School +2.8% +1.0% −2.2%
Brighton Aldridge Community Academy +4.4% +2.9% +0.4%
King’s School † +8.6% +3.1% −6.1%
Portslade Aldridge Community Academy +9.6% +8.5% +6.7%

The assumption is load-bearing and the ranking is not. Whether a school sheds half or nine tenths of the cost moves every number, and at the pessimistic end 8 of the ten are below the line rather than 6. What does not move is the order: the schools at the top of this table are at the top of it under every assumption.

WarningWhat this projection is and is not

It is a measure of pressure, not a forecast of any school’s accounts. Nothing here is allowed to happen. A maintained school whose reserve goes negative enters a licensed deficit and agrees a recovery plan with the council; an academy trust in that position answers to the Department for Education (Section 6.5). The figures say how large a hole has to be closed and roughly when, not what the balance sheet will read.

It holds everything else still. Today’s admission numbers, today’s funding rates in real terms, today’s in-year balance as the starting point, and no pay award, energy shock, capital receipt or restructuring. Each of those is larger than some of the differences in the table.

The two faith schools are marked, and their rolls are the least reliable figures here. The model admits to Cardinal Newman and King’s on distance alone. Cardinal Newman’s projected fall of −43% drives the largest gap in the table and should be read as an illustration of what that fall would cost, not as a claim that it will happen. Its current deficit is not modelled at all — it is what the school reported.

The starting balance is a single year. A school having one bad year looks permanently worse here than one with a steady small deficit, and Figure 27 is the better guide to which is which.

6.5 What being in deficit means, and what the council can do

Section 6.4 says how large each school’s gap becomes and roughly when. This section is about what follows once a school is in it: the rules that apply, who ends up holding the debt if the school changes status or closes, what schools do in practice to get out, and what that implies for a council whose problem is a cohort that is not coming back.

6.5.1 Who answers for which school

Show code
who %>%
  arrange(gap) %>%
  transmute(School = paste0(name, if_else(faith, " †", "")),
            Status = if_else(maintained, "Maintained", "Academy"),
            `A deficit is overseen by` = answers_to,
            `Reserve now` = gbp(reserve),
            `Gap in 2035` = gbp(gap),
            `Years of reserve` = case_when(
              reserve <= 0 ~ "already overdrawn",
              is.na(years_left) ~ "still in surplus",
              TRUE ~ sprintf("%.1f", years_left))) %>%
  knitr::kable(align = "llllrr")
Table 19: The ten schools by who oversees a deficit, with today’s reserve and the projected annual gap in 2035 on the middle assumption. Ranked by the 2035 gap.
School Status A deficit is overseen by Reserve now Gap in 2035 Years of reserve
Cardinal Newman Catholic School † Maintained The council (licensed deficit, recovery plan) −£1.8m −£3.1m already overdrawn
Hove Park School and Sixth Form Centre Maintained The council (licensed deficit, recovery plan) −£1.1m −£777k already overdrawn
Longhill High School Maintained The council (licensed deficit, recovery plan) £531k −£683k 0.8
Blatchington Mill School Maintained The council (licensed deficit, recovery plan) £492k −£264k 1.9
Dorothy Stringer School Maintained The council (licensed deficit, recovery plan) £831k −£209k 4.0
Varndean School Maintained The council (licensed deficit, recovery plan) £662k −£168k 4.0
Patcham High School Maintained The council (licensed deficit, recovery plan) −£82k £86k already overdrawn
King’s School † Academy The Department for Education, through the trust £1.0m £200k still in surplus
Brighton Aldridge Community Academy Academy The Department for Education, through the trust £1k £205k still in surplus
Portslade Aldridge Community Academy Academy The Department for Education, through the trust £534k £650k still in surplus

7 of the ten are maintained by the council, including Cardinal Newman as a voluntary aided school, and all 3 of the schools already overdrawn are among them: Cardinal Newman, Hove Park, and Patcham. Whatever the academies do, most of the city’s secondary deficit is the council’s to manage.

6.5.2 The rules for a maintained school in deficit

The Department for Education’s statutory guidance on local schemes for financing schools sets the framework, and each council writes its own scheme within it (DfE, Schemes for financing local authority maintained schools 2026 to 2027). Three provisions matter here.

  • A recovery plan is compulsory once the deficit passes 5%. A school must submit one when its revenue deficit is above 5% at 31 March.
  • A deficit can be licensed, not forgiven. The council can agree a licensed deficit backed by the collective balances it holds for all its schools, repaid over a set period, but the guidance is explicit that the council “cannot write off the deficit balance of any school”.
  • The council can take control. It can issue a notice of concern that restricts a school’s financial freedom and, in the last resort, suspend the school’s right to a delegated budget.

In Brighton & Hove this is not hypothetical. Local reporting in December 2024 put 31 schools on course to overspend that year, with 13 not on track to balance within five years, and in October 2025 reported licensed deficits of more than £8 million across 29 schools, with Cardinal Newman and Hove Park each forecast to end the year more than £1.7 million in deficit. The council has already cut primary admission numbers and closed three primary schools. The secondary sector is where that process has not yet started.

6.5.3 Where a deficit ends up if the school changes

A deficit does not disappear when a school stops being maintained. The Department’s guidance on academy conversion is specific (DfE, Treatment of surplus and deficit balances when maintained schools become academies, January 2024):

  • A school that chooses to convert takes its deficit with it. The Department reimburses the council and recovers the money from the new academy’s grant. The council may agree to absorb some or all of it if the deficit would otherwise stop the conversion.
  • A school that becomes a sponsored academy leaves its deficit behind. Where a school is eligible for intervention, or is judged not strong enough to convert without a sponsor, “the deficit remains with the LA, to be funded from its core budget”. It cannot be charged to the schools budget funded by the Dedicated Schools Grant, except from a contingency the schools forum has agreed to hold back for the purpose.

A school that closes is governed by the council’s own scheme, and schemes commonly provide that the closing school’s balance, surplus or deficit, reverts to the council. The practical point is the same either way: the two schools with accumulated deficits over a million pounds are both maintained, and the routes by which either might stop being maintained leave much or all of that debt with the council rather than with anyone else.

6.5.4 What schools actually do to get out

The best evidence on what schools cut is Ofsted’s survey of headteachers (Making the cut, February 2020). It predates the current funding round and rests on what heads reported, but it is consistent and specific, and secondary schools reported the deeper cuts.

Show code
tibble::tribble(
  ~`What they did`, ~Share, ~`What they reported it did`, ~`Share `,
  "Reduced teaching assistants", "82%", "Less individual support for pupils", "91%",
  "Reduced teachers", "81%", "Less enrichment", "83%",
  "Reduced non-classroom staff", "77%", "Worse pupil well-being", "80%",
  "Raised class sizes", "91%", "Narrower curriculum", "67%",
  "Cut learning resources", "82%", "Worse behaviour", "57%",
  "Cut professional development", "68%", "Lower quality of teaching", "55%") %>%
  knitr::kable(align = "lrlr")
Table 20: What secondary headteachers reported doing under financial pressure, and what they reported it did. Ofsted, Making the cut (2020), survey of 103 secondary heads.
What they did Share What they reported it did Share
Reduced teaching assistants 82% Less individual support for pupils 91%
Reduced teachers 81% Less enrichment 83%
Reduced non-classroom staff 77% Worse pupil well-being 80%
Raised class sizes 91% Narrower curriculum 67%
Cut learning resources 82% Worse behaviour 57%
Cut professional development 68% Lower quality of teaching 55%

The curriculum losses fell on design and technology, languages and music, and heads singled out pupils receiving SEN support without an education, health and care plan as the group losing most. Two further points from the same report are worth carrying. Nationally, results did not fall over the decade of real-terms cuts it covered, so the damage is real but does not show up where it is usually looked for. And the cuts are mostly to people, which fits what the Brighton & Hove accounts show: staff costs take between 68% and 91% of the ten schools’ income. When Section 6.4 assumes a school sheds three quarters of the funding it loses, it is assuming cuts of this kind.

6.5.5 What that implies for the city

Advice written for individual schools (Access Group, 2026; The Headteacher, 2025) runs from quick savings — benchmarking, procurement, energy, supply cover — through aligning staffing to the curriculum, to what one of them calls strategic shifts: federation, rationalising buildings, and changing admission numbers. For Brighton & Hove the order of those matters more than any one of them.

  1. A recovery plan cannot close a gap caused by a cohort that is not coming back. Recovery plans assume the pressure is temporary. The fall in the Year 7 cohort is already in the primary schools (Section 3). A school that balances its books by cutting to its smaller roll will do so by the cuts in the table above, and will do it again the following year.
  2. Taking places out has to be done across the system. The saving from removing places comes from schools whose rolls hold up. Cutting places only at the community schools does the opposite: children move to the academies and faith schools, and in the model the community schools’ combined position in 2035 goes from −£4.4m to −£5.6m. Shared across every school it improves to −£4.0m (Section 11). That is the case for asking the academies and the faith schools to take fewer places too: the council cannot solve this with the schools it runs alone.
  3. Federation and amalgamation are different savings. Two federated schools keep their own budgets and their own lump sums and share a head, a senior team and back-office costs. Two schools amalgamated into one receive one lump sum rather than two — currently £152k a school — so an amalgamation has to save more than that to come out ahead.
  4. Closure moves the problem as well as ending it. In the model, closing Longhill leaves 4 community schools in deficit in 2030 against 6 with it open, because its children fill places elsewhere. But its balance comes back to the council, its former pupils’ journeys lengthen, and the segregation and access costs are set out in Section 11.
  5. The timing is set by the reserves. Hove Park and Cardinal Newman are already past the point a recovery plan is required. Longhill’s reserve covers its projected gap for about 0.8 years and Blatchington Mill’s for about 1.9. Few of the options in this document could take effect that quickly, so the council will be managing licensed deficits while it decides, and should plan the decision around when each school’s reserve runs out rather than around the admissions calendar alone.
NoteWhat this section adds, and what it does not

It is a reading of the rules and the evidence, not a legal opinion. The council’s own scheme for financing schools governs the detail — repayment periods, the size of deficit it will license, what happens on closure — and should be checked for each of these points.

Nothing here changes the model. The projected gaps are the ones in Section 6.4. What the rules add is who holds them, and what the evidence adds is what closing them costs pupils.

6.6 Longhill’s position

Show code
os$lh_fin %>%
  transmute(Year = year_label,
            Roll = ifelse(is.na(roll), "--", fmt_n(roll)),
            `Income / pupil` = paste0("£", fmt_n(income_pp)),
            `Spend / pupil` = paste0("£", fmt_n(expenditure_pp)),
            `In-year balance` = paste0("£", fmt_n(balance)),
            `Reserve` = paste0("£", fmt_n(reserve))) %>%
  knitr::kable(align = "lrrrrr")
Table 21: Longhill High School: roll, per-pupil income and expenditure, in-year balance and accumulated reserve.
Year Roll Income / pupil Spend / pupil In-year balance Reserve
2021-22 908 £7,644 £6,980 £602,810 £960,852
2022-23 875 £7,978 £7,998 £-17,469 £943,383
2023-24 825 £8,748 £9,041 £-243,505 £699,878
2024-25 723 £10,358 £10,591 £-168,435 £531,443

Longhill’s roll fell from 908 to 723 across four years. Its per-pupil income rose over the same period — the funding system is doing what it is meant to do — and its per-pupil spending rose faster. The accumulated reserve has fallen from £960,852 to £531,443.

At the recent rate of depletion — about £143,136 a year — the reserve supports roughly 3.7 more years. That is the constraint that makes this a decision with a deadline rather than a decision that can be deferred.

7 Brightopia: a model of the whole system

TipEvery stage of this model, opened up

The model, stage by stage is a companion to this section. It takes the equation apart one term at a time — what each term is, where its numbers come from, how it is computed, a worked example, the checks that can be run against it, and what it assumes. The terms are \(O_i\), \(W_j\), \(c_{ij}\), \(\beta\), \(\gamma_h\), \(C_j\) and \(A_i\), followed by the admission-number ceiling and the re-offering of refused children. Anyone wanting to check a number in this document rather than take it on trust should start there.

7.1 Why model at all

Note“Brightopia” means one specific assumption: \(W_j = 1\)

The name is used throughout this section and it is worth pinning down, because the model is run under two different assumptions and they answer different questions.

Brightopia proper sets \(W_j\) to the same value for every school. Same size, same quality, same buildings, same staff, same lunches, no religious character. Children are identical too. The only thing that separates the schools is where they stand. Whenever this document says Brightopia, the distance-only model, or geography alone, that is the assumption in force, and every such figure is labelled \(W_j = 1\).

The runs with \(W_j\) varying are not Brightopia and are labelled separately, as with demand or by the specification they use. They are the same equation with a real attractiveness term.

The distinction matters because Brightopia’s strength is exactly its severity: it cannot be dismissed as an artefact of attainment proxies, Ofsted grades or preference data, because it uses none of them. That is also its limit — it cannot say how many children a school would draw, because it has assumed the question away.

Every argument in the sections above is about one part of the system: where the children are, how far the schools are, what families prefer. A model is what lets you ask what happens when you change one part and everything else adjusts. Close a school and its children do not vanish; they appear somewhere else, displacing others. Shrink one school’s admission number and demand redistributes to its neighbours. Nothing in a spreadsheet of admission numbers captures that.

Brightopia is a production-constrained spatial interaction model. It takes the children in each neighbourhood, the attractiveness of each school, and the travel cost between them, and predicts the flow:

\[T_{ij} = A_i O_i W_j^{\alpha} c_{ij}^{-\beta}\]

\(O_i\) is the number of cohort-aged children in neighbourhood \(i\); \(W_j\) is school \(j\)’s attractiveness; \(c_{ij}\) is the routed walk-and-bus cost; \(\beta\) governs how sharply demand falls away with travel time; and \(A_i\) is a balancing factor ensuring every child goes somewhere.

The model deliberately contains no catchment term. That is the point. It answers the question “where would children go if only geography and school size mattered?” — and the gap between that answer and reality is a measure of what the admissions system is doing.

It is run twice, and the difference between the two runs does a lot of work below. Figure 31 holds \(W_j\) identical for every school, which is the distance-only case: same size, same quality, same buildings, no religious character, and the only thing separating the schools is where they stand. Figure 34 lets \(W_j\) vary, setting it to the rank-weighted preference rate from Section 4.5.1 — what families actually ask for, counting all three ranks. The first is a statement about the geography of the city. The second is about the geography and the demand together, and the gap between them is the whole of what attractiveness contributes.

Show code
# The distance-only run at the bundle's central decay, not at the 1.5 the
# original Brightopia used. The open model keeps the 1.5 run for
# reproduction; here the point is that this figure and @fig-brightopia-demand
# report the same quantity, so they have to be at the same beta.
bt <- brt$geog_only_at_ref %>%
  mutate(fill_rate = modelled / pan2026,
         short = str_remove(name, " (School|High School|Community Academy|Catholic School).*"))

ggplot(bt, aes(pan2026, modelled)) +
  geom_abline(slope = 1, intercept = 0, colour = "grey45", linetype = "31") +
  geom_point(aes(colour = fill_rate >= 1), size = 3.5) +
  ggrepel::geom_text_repel(aes(label = name), size = 2.8, seed = 1,
                           max.overlaps = 20) +
  scale_colour_manual(values = c(`TRUE` = "#2166ac", `FALSE` = "#b2182b"),
                      labels = c(`TRUE` = "Geography would fill it",
                                 `FALSE` = "Geography would not"), name = NULL) +
  scale_x_continuous(limits = c(0, NA)) + scale_y_continuous(limits = c(0, NA)) +
  labs(x = "Published admission number, 2026", y = "Modelled intake",
       title = expression("What the map alone would do:  "*W[j]*" = 1"),
       subtitle = sprintf("Brightopia proper — every school equally attractive. Distance decay β = %.1f.\nThe dashed line is a school exactly full.",
                          brt$beta_ref),
       caption = "Brightopia, open-data specification.") +
  theme_bh()
Figure 31: Brightopia proper, with every school equally attractive: modelled intake against published admission number. A school above the line is one that geography alone would fill; below it, one that geography alone would not.

City-wide there are 2,560 places for 2,267 children — a fill rate of 89% before any child expresses a preference. The system is not short of capacity; it is short of children.

Two schools are worth reading off this chart carefully, and they point in opposite directions.

Longhill has already been resized to roughly what geography supports. Its modelled intake is 198 children. Against its former admission number of 270 that is 73%; against the reduced number of 210 now in force it is 94%. The distinction matters, and it is easy to quote the wrong one. Geography can fill 210 places at Ovingdean; it could not fill 270. The reduction has already done most of the work that a reduction can do — which is why section 8 is about what remains, and why the modelled intake is remarkably insensitive to the decay parameter, varying only between 191 and 243 across the entire swept range. Longhill’s catchment simply does not contain many more children to attract.

Hove Park’s problem is not its location. Geography alone would fill 142% of its places — it is the most over-subscribed school on the map, sitting in the densest part of the child population. Yet section 5 shows it among the largest losers of real demand over sixteen years. A school that the map says should be full and that families are nonetheless leaving is not a geography problem, and no catchment or relocation policy reaches it. That gap between what location predicts and what families do is the clearest evidence in this document that choice is operating on something other than distance.

7.2 How far geography alone actually gets you

The chart above is a model of a city that does not exist. It is worth putting next to the one that does: the offers Brighton & Hove actually made in the 2026 round, school by school.

Brightopia proper, with attractiveness switched off and no ceiling (Section 7.5.1 sets out the full form and what the later models add):

\[ T_{ij} \;=\; A_i\, O_i\; c_{ij}^{-\beta}, \qquad A_i = \Big[\textstyle\sum_j c_{ij}^{-\beta}\Big]^{-1}, \qquad W_j = 1,\;\; \beta = 1.7 \]

Show code
brt$observed_compare %>%
  arrange(diff_17) %>%
  transmute(School = str_remove(name, " (School|High School|Community Academy|Catholic School).*"),
            `PAN` = pan2026,
            `Offers made` = fmt_n(observed),
            `Brightopia` = fmt_n(modelled_17),
            `Difference` = sprintf("%+.0f", diff_17),
            `Offers, % of city` = sprintf("%.1f", share_obs),
            `Brightopia, % of city` = sprintf("%.1f", share_mod)) %>%
  knitr::kable(align = "lrrrrrr")
Table 22: Brightopia (\(W_j = 1\)) against the offers actually made. Every school is equally attractive in the model, so a difference here is everything except geography: reputation, results, faith character, sixth form, siblings, and the catchment rule. Brightopia applies no admission-number ceiling, so a modelled figure above the PAN is demand the real round would have turned away.
School PAN Offers made Brightopia Difference Offers, % of city Brightopia, % of city
Dorothy Stringer 330 330 230 -100 14.7 10.2
Blatchington Mill 330 330 232 -98 14.7 10.2
Cardinal Newman 360 360 285 -75 16.0 12.6
Varndean 300 300 242 -58 13.3 10.7
King’s 180 180 177 -3 8.0 7.8
Patcham 225 204 219 +15 9.1 9.7
Brighton Aldridge 180 146 183 +37 6.5 8.1
Portslade Aldridge 220 183 245 +62 8.1 10.8
Longhill 210 81 198 +117 3.6 8.7
Hove Park 180 136 256 +120 6.0 11.3

Geography alone explains 31% of the variation in offers made, with a root-mean-square error of 79 children per school. For a model that knows nothing except where the buildings are and where the children live, that is neither nothing nor much.

What matters is that the errors are not random. They run in one direction, and it is the direction the rest of this document is about.

The model over-predicts Hove Park by +120 and Longhill by +117. Both are schools that geography says should do well — Hove Park sits in the densest part of the child population — and that families do not choose. It under-predicts Dorothy Stringer by -100 and Blatchington Mill by -98, both of which filled to their admission number and would have taken more.

In share terms the same thing reads more starkly. Longhill takes 3.6% of the city’s offers; geography alone would give it 8.7%. Blatchington Mill takes 14.7% against a geographic 10.2%.

NoteWhy the gaps are not evidence of a broken model

A distance-only model is supposed to miss. It has been told that every school is identical, which is false, and the size of what it misses is the measurement being taken.

Two mechanical points inflate the gaps without meaning anything.

The popular schools are capped and the model is not. Blatchington Mill, Cardinal Newman, Dorothy Stringer, King’s, and Varndean all offered their admission number or more, so their observed figure is a ceiling rather than a measure of demand. The model under-predicts them; the real gap is larger, not smaller. Only the 5 schools that were not rationing places can be judged against their offers at all.

The catchment rule is in the offers and not in the model. Every offer above was made under a catchment system with a lottery tie-break. Brightopia has no catchment term at all. Some of what looks like preference here is the admissions rule doing its job — which is why this table cannot separate “families do not want Longhill” from “the rule does not send them there”, and why Section 5.5 has to answer that question with preference data rather than offers.

Show code
bo %>%
  filter(!at_ceiling) %>%
  arrange(abs(diff_w)) %>%
  transmute(School = str_remove(name, " (School|High School|Community Academy|Catholic School).*"),
            `Offers made` = fmt_n(observed),
            `Geography only` = fmt_n(modelled_17),
            `Gap` = sprintf("%+.0f", diff_17),
            `With demand` = fmt_n(modelled_w),
            `Gap ` = sprintf("%+.0f", diff_w),
            `Closer?` = if_else(abs(diff_w) < abs(diff_17), "yes", "no")) %>%
  knitr::kable(align = "lrrrrrr")
Table 23: The five schools that were not rationing places, against the two runs of the model. A school at its admission number is left out, because an uncapped model exceeding a capped observation is not an error.
School Offers made Geography only Gap With demand Gap Closer?
Portslade Aldridge 183 245 +62 181 -2 yes
Longhill 81 198 +117 75 -6 yes
Patcham 204 219 +15 195 -9 yes
Brighton Aldridge 146 183 +37 97 -49 no
Hove Park 136 256 +120 281 +145 no

Putting \(W_j\) back in does not simply fix this. The average gap across those 5 schools falls from 70 children to 42, but only 3 of the 5 actually get closer. Longhill and Portslade Aldridge land almost exactly on their observed figure — which is the striking result, given that geography alone missed Longhill by +117. The rest do not improve.

WarningHove Park breaks the weighted measure, and it is worth knowing why

Hove Park is the one school that gets worse when demand is added: from +120 on geography alone to +145. The model thinks it should be the most sought-after school in the city and it offered 136 places against a PAN of 180.

The reason is structural, and it applies to the weighted measure generally. A second preference only becomes an offer when the first one fails. Across the same five rounds Hove Park receives 123 first preferences and 242 seconds — 2.0 seconds for every first, the highest ratio in the city. Those seconds come largely from the Hove Park / Blatchington Mill paired catchment (Section 5.4), and Blatchington Mill fills, so they are never called on. Weighting a second at half a first says something true about what families would accept; it says much less about what they will be allocated.

So the weighted specification is a good measure of wanting and a poor measure of taking up. That does not undo the Longhill result, which runs the other way: Longhill is named 164 times at any of the three ranks against 270 places — 0.61 per place, the lowest in the city against a next-lowest of 1.03 and a city median of 2.57. There is no reservoir of unconverted seconds behind it. But it is a reason to read the with-demand column as a statement about demand rather than a forecast of offers.

7.3 Where the model sends the children

The tables say how many children each school draws. They do not say where from, and for Longhill that is most of the question: a school at the eastern edge of the city either recruits from its own corner or it does not recruit.

Show code
fl <- fm$net %>%
  filter(name == "Longhill High School") %>%
  mutate(model = factor(model, names(FM_LABELS), FM_LABELS))
fl_bb <- st_bbox(fl)

ggplot() +
  geom_sf(data = catch, fill = NA, colour = "grey80", linewidth = 0.3) +
  geom_sf(data = fl %>% arrange(flow),
          aes(linewidth = flow, colour = leg_mode), alpha = 0.75,
          lineend = "round") +
  geom_sf(data = fm_sch, colour = "grey45", size = 1.1) +
  geom_sf(data = fm_sch %>% filter(name == "Longhill High School"),
          colour = "black", fill = "white", shape = 21, size = 2.8, stroke = 1) +
  facet_wrap(~ model, nrow = 1) +
  scale_linewidth_continuous(range = c(0.15, 2.6), guide = "none") +
  scale_colour_manual(values = FM_COL, name = NULL) +
  coord_sf(xlim = c(fl_bb["xmin"], fl_bb["xmax"]),
           ylim = c(fl_bb["ymin"], fl_bb["ymax"])) +
  labs(title = "Longhill's modelled catchment contracts as the model gets more realistic",
       subtitle = "The white point is Longhill; grey points are the other nine schools. Grey outlines are the current catchments.",
       caption = "Open-data model. Modelled flows, not observed journeys.") +
  theme_bh(11) +
  theme(axis.text = element_blank(), axis.ticks = element_blank(),
        panel.grid = element_blank(), legend.position = "top",
        strip.text = element_text(face = "bold"))
Figure 32: Longhill’s modelled intake, routed over the walking and bus network, under each of the three models. Flows are summed onto shared segments, so a corridor used by several neighbourhoods draws thicker. Blue is a bus leg, orange a walking leg.
Show code
m <- leaflet(width = "100%", height = 620,
             options = leafletOptions(preferCanvas = TRUE)) %>%
  add_basemap() %>%
  addPolygons(data = catch, fill = FALSE, color = "#555555",
              weight = 1, opacity = 0.5, group = "Catchment boundaries")

for (i in seq_len(nrow(fm_groups))) {
  g <- fm_groups$label[i]
  d <- fm_net %>% filter(label == g) %>% arrange(flow)
  m <- m %>%
    addPolylines(data = d, group = g,
                 color = ~ unname(FM_COL[leg_mode]),
                 weight = ~ fm_w(flow), opacity = 0.75,
                 label = ~ sprintf("%s leg · %.0f children", leg_mode, flow)) %>%
    addCircleMarkers(
      data = fm_sch %>% filter(name == fm_groups$name[i]), group = g,
      radius = 6, color = "#111111", weight = 2, opacity = 1,
      fillColor = "#ffffff", fillOpacity = 1, label = ~ name)
}

# Every group is added at once, so leaflet's automatic bounds cover the
# whole routed network and open at a Sussex-wide zoom. Frame the city.
fm_bb <- st_bbox(fm_net)

m %>%
  fitBounds(lng1 = unname(fm_bb["xmin"]) - 0.01, lat1 = unname(fm_bb["ymin"]) - 0.005,
            lng2 = unname(fm_bb["xmax"]) + 0.01, lat2 = unname(fm_bb["ymax"]) + 0.005) %>%
  addLayersControl(baseGroups = fm_groups$label,
                   overlayGroups = "Catchment boundaries",
                   options = layersControlOptions(collapsed = FALSE)) %>%
  hideGroup("Catchment boundaries") %>%
  addLegend("bottomright", colors = unname(FM_COL), labels = names(FM_COL),
            title = "Leg", opacity = 0.8)
Figure 33: The same three panels, interactively, so the corridors can be traced and the segments read. Blue is a bus leg, orange a walking leg; hover a segment for the number of children on it. These are modelled flows from an open-data model: no pupil record is drawn here. The other nine schools are in Figure 35, under the full model.

Longhill’s modelled catchment contracts as the model gets more realistic, and that is the striking thing in this map. Under Brightopia, with every school equally attractive, it draws 197 children and only 51% of them come from its own catchment — the model has it reaching deep into Kemptown, Whitehawk and beyond. Add what families actually ask for and the total falls to 74; add the capacity ceiling and the other terms and it settles at 82 children, 97% of them from the Longhill catchment.

Show code
lh_catch %>%
  mutate(model = recode(model, !!!as.list(FM_LABELS)),
         flow = round(flow)) %>%
  select(catchment, model, flow) %>%
  pivot_wider(names_from = model, values_from = flow, values_fill = 0) %>%
  arrange(desc(.data[[FM_LABELS[["M5"]]]])) %>%
  rename(`Home catchment of the neighbourhood` = catchment) %>%
  knitr::kable(align = "lrrr")
Table 24: Where Longhill’s modelled intake comes from, by the home catchment of the neighbourhood it starts in, under each of the three models.
Home catchment of the neighbourhood Brightopia · Wj = 1 With demand · Wj = weighted preferences Full model · calibrated to catchment preferences
Longhill 101 45 79
BACA 10 3 1
DS_Varndean 52 16 1
Hove_Blatch 24 6 0
PACA 6 2 0
Patcham 4 1 0

Read west to east, the map is an argument about siting. The trunk into Longhill is a single corridor along the coast road and over the downs, and it is thick only at the eastern end. Under the full model there is almost nothing coming from west of the Steine — not because those children are forbidden to go, but because on a routed journey time they have closer schools that are also more wanted.

NoteHow to read the lines, and what they are not

The lines are real routes. Each zone-to-school journey is routed with r5r over the same merged OSM and GTFS network the journey times in Section 4.1 come from, on the same weekday morning. A walking leg follows the street network; a bus leg follows the service’s own shape.

The widths are added along the way. Flows are summed onto shared segments, so a road used by six neighbourhoods carries the sum of all six. That is what produces the trunk-and-branch shape, and it is why a line’s thickness is a statement about a corridor rather than about any one neighbourhood.

The flows are modelled, not observed. Nobody’s journey is drawn here. This is where the model sends children, which is precisely the thing Section 7.2 and Section 7.5 test against the offers actually made — and on that test the full model gets four of the five rationed schools right and misses Cardinal Newman and Hove Park. The map should be read with those two misses in mind.

Journeys that need more than 30 minutes of walking are not drawn. The routing caps walking legs, so a handful of zone-school pairs return no itinerary and drop out of the picture. They are in the tables.

7.4 What happens when you put demand back in

Figure 31 asked what the map alone would do. It is a deliberately severe assumption, and it is the assumption that makes Longhill’s position look most survivable: with every school equally wanted, the only thing that can hurt a school is being far away.

The same model, same geography, same routed times, same production constraint, but with \(W_j\) set to what families actually ask for:

Show code
bd <- brt$demand_compare %>%
  mutate(short = str_remove(name, " (School|High School|Community Academy|Catholic School).*"),
         gains = shift > 0) %>%
  arrange(shift) %>%
  mutate(short = factor(short, short))

ggplot(bd, aes(y = short)) +
  geom_segment(aes(x = geog_only, xend = with_demand, yend = short,
                   colour = gains), linewidth = 1.1,
               arrow = arrow(length = unit(0.16, "cm"), type = "closed")) +
  geom_point(aes(x = geog_only), colour = "grey35", size = 2.6) +
  geom_point(aes(x = pan2026), shape = 124, size = 5, colour = "#444444") +
  scale_colour_manual(values = c(`TRUE` = "#2166ac", `FALSE` = "#b2182b"),
                      labels = c(`TRUE` = "Gains once demand is added",
                                 `FALSE` = "Loses once demand is added"),
                      name = NULL) +
  labs(x = "Modelled intake", y = NULL,
       title = "What the map would do, and what families do to it",
       subtitle = sprintf(
         "Grey dot: every school equally attractive. Arrow: W set to weighted preferences. Tick: 2026 admission number.\nBoth runs at β = %.1f.",
         brt$beta_ref),
       caption = "Open-data specification. No capacity ceiling in either run.") +
  theme_bh() +
  theme(panel.grid.major.y = element_blank(),
        legend.position = "top")
Figure 34: Modelled intake with every school equally attractive, against modelled intake using the rank-weighted preference rate. Both runs are at the same distance decay, so the difference between the two points is attractiveness and nothing else. Neither run applies an admission-number ceiling, so these are natural demand rather than offers.

The same equation with a real attractiveness term and still no ceiling:

\[ T_{ij} \;=\; A_i\, O_i\; W_j^{\alpha}\; c_{ij}^{-\beta}, \qquad \alpha = 1,\;\; \beta = 1.7, \qquad W_j = \text{weighted preferences per place} \]

Show code
brt$observed_compare %>%
  arrange(diff_w) %>%
  transmute(School = str_remove(name, " (School|High School|Community Academy|Catholic School).*"),
            `PAN` = pan2026,
            `Offers made` = fmt_n(observed),
            `Brightopia, Wj = 1` = fmt_n(modelled_17),
            `With demand` = fmt_n(modelled_w),
            `Difference` = sprintf("%+.0f", diff_w),
            `Rationing?` = if_else(at_ceiling, "yes", "")) %>%
  knitr::kable(align = "lrrrrrc")
Table 25: The same comparison as the Brightopia table above, but with \(W_j\) set to weighted preferences rather than held at 1. Still no admission-number ceiling, so a figure above the PAN is demand that the real round would have turned away.
School PAN Offers made Brightopia, Wj = 1 With demand Difference Rationing?
Blatchington Mill 330 330 232 219 -111 yes
Dorothy Stringer 330 330 230 253 -77 yes
Brighton Aldridge 180 146 183 97 -49
Cardinal Newman 360 360 285 328 -32 yes
Patcham 225 204 219 195 -9
Longhill 210 81 198 75 -6
Portslade Aldridge 220 183 245 181 -2
Varndean 300 300 242 360 +60 yes
King’s 180 180 177 277 +97 yes
Hove Park 180 136 256 281 +145

This is the least comfortable figure in the document, and it is about Longhill. On geography alone the school draws 198 children — close to the 210 places now in force, which is what Section 7 reported. Once \(W_j\) carries what families ask for, it draws 75, a fall of 62%. That is the largest fall of any school in the city, and it means the reduction to 210 closed the gap to geography but not the gap to demand.

The same is true at BACA, the other end of the same story: from 183 on geography to 97 with demand.

In the other direction, Varndean and King’s gain 118 and 99 children respectively — both already well above their admission numbers on geography alone, and further above once preference is counted. Neither can actually take them: the ceiling is the admission number, and the children have to go somewhere.

Counting how many schools geography alone would fill to their 2026 admission number gives 3 of 10. Counting how many would fill once demand is added gives 3. The city’s problem is not that it is short of children in aggregate — it is that they are not distributed the way the buildings are.

ImportantWhat this does and does not license

It does not say Longhill would recruit 75 children. The model has no catchment term and no admission ceiling. In reality the catchment rule holds children in the east who would otherwise go west, which is exactly what the rule is for, and section 8 runs the configurations with catchments and ceilings in place.

It does say the two problems are separable, and only one of them moves. Relocation and admission-number changes act on the geography term. Nothing in this document acts on \(W_j\), because \(W_j\) is what families think of the school — and section 5 shows what families think is tracking a headline score that is mostly a description of intake. A school can be moved to where the children are and still not be chosen by them.

And it depends on the preference measure. Counting first preferences alone would put Longhill’s attractiveness at 0.41; counting all three ranks with the geometric discount puts it at 0.32 — so the weighted measure makes Longhill’s position look worse, not better. That is worth stating plainly, because the weighted measure was adopted in Section 4.5.1 for reasons that had nothing to do with Longhill, and it did not turn out to favour the argument.

7.5 What the model actually needs

Both runs above miss, and they miss in a particular way: inside each paired catchment they send children to the wrong one of the two schools. Blatchington Mill is under-predicted and Hove Park over-predicted; Dorothy Stringer is under-predicted and Varndean over-predicted once \(W_j\) is real. That is a specific failure with specific candidate causes, so they are added one at a time.

Three candidates, in the order they are tested.

A capacity ceiling. Schools cannot admit beyond their admission number. Brightopia has no ceiling at all, so it is free to send 255 children to a school with 180 places.

A catchment term. Living in a school’s catchment makes a place far more likely. Entered as \(\gamma\) in the exponent, so being in catchment multiplies a school’s pull by \(e^{\gamma}\).

A competing-destinations term. Fotheringham’s argument that a gravity model is misspecified when destinations cluster: families pick an area and then a school within it. Entered as \(C_j^{\delta}\), where \(C_j = \sum_{k \neq j} W_k d_{jk}^{-\sigma}\) is how much company a school has. A negative \(\delta\) is the competing-destinations prediction — rivals nearby take share.

Show code
lad %>%
  transmute(Model = model, `Term added` = label,
            `R²` = sprintf("%.2f", r2),
            RMSE = fmt_n(rmse),
            `MAE` = fmt_n(mae),
            `MAE, unrationed only` = fmt_n(mae_free)) %>%
  knitr::kable(align = "llrrrr")
Table 26: Fit against the offers actually made, adding one term at a time. The last column is the one to read: it scores only the five schools that were not rationing places, because from the capacity row onwards the model is told the admission numbers, and five of the ten observed figures are those admission numbers.
Model Term added R² RMSE MAE MAE, unrationed only
M0 Brightopia: every school identical 0.47 73 64 66
M1 + W = weighted preferences 0.51 68 54 41
M2 + capacity ceiling 0.94 24 18 25
M3 + catchment term (gamma = 0.4) 0.95 23 19 27
M4 + competing destinations (delta = +0.3) 0.95 21 14 24
M5 Calibrated to what each catchment asks for 0.95 23 14 28

The capacity ceiling does almost all of the work. It takes the fit from R² 0.51 to 0.94, and on the schools that were not rationing places it takes the average error from 41 children to 25.

That headline R² is flattered and should be discounted: five of the ten observed figures are the admission numbers, and from that row on the model is given them. But the improvement survives the discount — on the five schools free to be wrong, the error still falls by a third. Brightopia’s largest single omission is not a behavioural term. It is that offers are rationed and the model was not told.

The catchment term adds essentially nothing. Fitted at \(\gamma\) = 0.4, it moves R² from 0.94 to 0.95 and makes the unrationed error slightly worse, 25 to 27. Once children are held to real capacities in a city nine miles across, the catchment rule has little left to do that proximity was not already doing.

That is a finding about offers, and it does not survive being asked about preferences instead. Fitted to what each catchment’s children ask for, the catchment term is the strongest behavioural term in the model (Section 7.8.3).

The competing-destinations term helps a little, with the wrong sign.

Fitted at \(\delta\) = +0.3, it improves both scores. But a positive \(\delta\) is not competition — it says a school does better for having rivals close by, which is agglomeration, the opposite of what the term was added to test. Without the capacity ceiling the term does nothing at all: fitted on its own it lands at +0.1 and the RMSE is unchanged.

So the honest answer to “would a competing-destinations term fix the paired catchments” is no. What fixes them is the capacity ceiling.

7.5.1 The five models, written out

Every model in the ladder is the same equation with terms switched on. In full:

\[ T_{ij} \;=\; A_i\, O_i\; W_j^{\alpha}\; C_j^{\delta}\; e^{\gamma \kappa_{ij}}\; c_{ij}^{-\beta}, \qquad A_i \;=\; \Big[\textstyle\sum_j W_j^{\alpha}\, C_j^{\delta}\, e^{\gamma \kappa_{ij}}\, c_{ij}^{-\beta}\Big]^{-1} \]

\(T_{ij}\) is the flow of children from neighbourhood \(i\) to school \(j\); \(O_i\) the cohort-aged children living in \(i\); \(c_{ij}\) the routed walk-and-bus journey time; \(\kappa_{ij}\) is 1 if \(i\) lies in \(j\)’s catchment and 0 otherwise; and \(A_i\) is the balancing factor that sends every child somewhere. Throughout, \(\alpha = 1\) and \(\beta = 1.7\).

Setting \(W_j = 1\), \(\delta = 0\) and \(\gamma = 0\) collapses it to Brightopia. Each model below turns one more of them on.

7.5.2 M2 — adding the capacity ceiling

M1 lets every school take as many children as its attractiveness and its distance earn it. Real schools stop at their admission number. M2 adds that, and it is worth being precise about how, because the mechanism is not a cap applied after the fact — it changes where the children who cannot get in end up.

Flows are computed as in M1, then two balancing factors are found:

\[ T^{*}_{ij} \;=\; a_i\, b_j\, T_{ij} \]

  • \(a_i\) is the origin factor, one per neighbourhood. It is the number that makes neighbourhood \(i\) send exactly its own children and no more: \(\sum_j T^{*}_{ij} = O_i\). In M1 it is just the normaliser that turns relative attractions into shares; here it has to keep doing that while schools are being cut back underneath it.
  • \(b_j\) is the destination factor, one per school, and it is constrained to \(b_j \le 1\). It can shrink a school’s intake. It can never inflate one.

\(\overline{T}_j\) is school \(j\)’s published admission number for 2026, and the pair of factors is required to satisfy

\[ \sum_j T^{*}_{ij} = O_i \quad \text{(every child placed)}, \qquad \sum_i T^{*}_{ij} \;\le\; \overline{T}_j \quad \text{(no school over its number)} \]

7.6 The ceiling only exists where it binds

The \(b_j \le 1\) restriction is what makes this a destination constraint that switches on and off rather than one that is always active. For every school exactly one of two things is true:

\[ b_j = 1 \quad\text{(the school is left alone)} \qquad\text{or}\qquad \sum_i T^{*}_{ij} = \overline{T}_j \quad\text{(the school is exactly full)} \]

An undersubscribed school is never scaled up to fill itself — that is the difference between this and a doubly-constrained model, where both margins are forced to match and every school would be pushed to its admission number whether anyone wanted it or not. Here the model is production-constrained everywhere, and destination-constrained only at the schools that would otherwise overflow.

7.7 How the two factors are found

There is no closed form, because each factor depends on the other: cutting an oversubscribed school frees children who must go somewhere, which changes what every other school receives. They are found by iterative proportional fitting, which is two steps repeated until nothing moves:

  1. Scale each row. Multiply every flow out of neighbourhood \(i\) by whatever makes its row sum to \(O_i\) again.
  2. Scale each column, by \(\min(\overline{T}_j / \sum_i T_{ij},\, 1)\). A school over its number is cut back to it; a school under its number is multiplied by 1 and left exactly as it was.

Repeat. The published \(a_i\) and \(b_j\) are the accumulated products of those per-step factors. The loop stops when every school is at or under its admission number and every neighbourhood is placing all its children.

Step 1 is what makes this a rationing rule rather than a cull. A child cut from an oversubscribed school in step 2 is not lost from the model: the next row-scaling redistributes that neighbourhood’s whole demand across the schools it can still reach, in proportion to how attractive and how close they are. The displacement cascades, which is why the ceiling moves schools that are nowhere near full.

Show code
toy <- tibble(School = c("A", "B", "C"),
              `M1 flow` = c(60, 30, 10),
              Places = c(40, 200, 200))
toy <- toy %>%
  mutate(`Capped` = pmin(`M1 flow`, Places),
         free = sum(`M1 flow`) - sum(`Capped`),
         slack = if_else(`M1 flow` < Places, `M1 flow`, 0),
         `M2 flow` = `Capped` + free * slack / sum(slack)) %>%
  select(School, `M1 flow`, Places, `M2 flow`)
stopifnot(abs(sum(toy$`M2 flow`) - 100) < 1e-9)
toy %>%
  mutate(across(-School, ~ sprintf("%.0f", .x))) %>%
  knitr::kable(align = "lrrr")
One neighbourhood, three schools, to show the arithmetic. School A is the most attractive but has 40 places. The children it cannot take do not disappear: they are shared between B and C in the ratio those two already had, 3 to 1.
School M1 flow Places M2 flow
A 60 40 40
B 30 200 45
C 10 200 15

With one neighbourhood it settles in a single pass. With 179 of them competing for the same ten schools it takes several, because every redistribution can push another school over its number.

7.8 What it is not

It is not the admissions algorithm. There is no priority order in it: no catchment, no sibling, no distance tie-break. A school over its number is scaled back proportionally, so every neighbourhood sending to it loses the same fraction of its children, whether it is next door or across the city. The real rule takes in-catchment children first and then the nearest — Section 8.4 models that separately, on top of a catchment map. M2 is a capacity ceiling, not an allocation.

It does not know which schools actually rationed places. It applies the published admission number to every school equally. Whether that number binds in the model is an output, and comparing it against the schools that really did ration is one of the things this section is for.

Both constraints hold on the published flows: no school is over its admission number by more than a rounding error, every neighbourhood places all its children, and the ceiling is active at 0 of the 10 schools. At the other 10 it is inert — b_j is exactly 1 and those schools are left wherever M1 put them, the emptiest at 43% of its admission number.

Show code
mt_table("M2", "M1") %>% knitr::kable(align = "lrrrrrc")
Table 27: M2. Weighted preferences with a capacity ceiling, against the offers actually made. The change column is the size of the residual against M1: negative means the ceiling moved that school closer.
School PAN Offers made Modelled Residual Change from M1 Rationing?
Blatchington Mill 330 330 291 -39 -61 yes
Brighton Aldridge 180 146 119 -27 -20
Dorothy Stringer 330 330 313 -17 -55 yes
Cardinal Newman 360 360 360 +0 -19 yes
King’s 180 180 180 +0 -84 yes
Varndean 300 300 300 +0 -65 yes
Longhill 210 81 91 +10 +4
Patcham 225 204 225 +21 +15
Portslade Aldridge 220 183 208 +25 +11
Hove Park 180 136 180 +44 -88

R² 0.94, RMSE 24, and 25 children average error across the five schools that were not rationing places. Almost every school moves closer — 7 of 10 — and the biggest single correction is Hove Park, which the ceiling cuts by 88 children.

But the ceiling does not bind where the real round did. It binds on 5 schools — Cardinal Newman, Hove Park, King’s, Patcham, and Varndean — while the schools that actually rationed places were Blatchington Mill, Cardinal Newman, Dorothy Stringer, King’s, and Varndean. The overlap is imperfect in both directions, and the interesting half is Hove Park and Patcham: the model pushes them to the admission number and neither filled. That is the same puzzle as Section 7.2 — schools the model wants to send children to, and the city did not.

7.8.1 M3 — adding the catchment term

\(\gamma\) enters the exponent, so living inside a school’s catchment multiplies its pull by \(e^{\gamma}\):

\[ T_{ij} \;=\; A_i\, O_i\; W_j^{\alpha}\; e^{\gamma \kappa_{ij}}\; c_{ij}^{-\beta} \quad\text{then capped as in M2,}\qquad \gamma = 0.4 \]

At \(\gamma\) = 0.4 a catchment school is 49% more attractive than the same school out of catchment, other things equal.

Show code
mt_table("M3", "M2") %>% knitr::kable(align = "lrrrrrc")
Table 28: M3. The same, with a catchment term. The change column is against M2.
School PAN Offers made Modelled Residual Change from M2 Rationing?
Brighton Aldridge 180 146 114 -32 +4
Blatchington Mill 330 330 304 -26 -13 yes
Dorothy Stringer 330 330 313 -17 +1 yes
Cardinal Newman 360 360 349 -11 +11 yes
King’s 180 180 180 +0 +0 yes
Varndean 300 300 300 +0 +0 yes
Longhill 210 81 98 +17 +7
Patcham 225 204 225 +21 -0
Portslade Aldridge 220 183 204 +21 -4
Hove Park 180 136 180 +44 +0

R² 0.95, RMSE 23, and 27 children average error across the five schools that were not rationing places. The term moves almost nothing, and what it moves it moves in both directions. Against offers, this is the rung that fails; against preferences it is the one that matters most (Section 7.8.3).

7.8.2 M4 — adding competing destinations

\(C_j\) is Fotheringham’s competing-destinations term, the accessibility of each school to the other schools:

\[ C_j \;=\; \sum_{k \neq j} W_k\, d_{jk}^{-\sigma}, \qquad \sigma = 1.0 \]

\(d_{jk}\) is the straight-line distance in kilometres between two schools, not a journey time — nobody travels from one school to another, and what is being measured is only how clustered they are. It enters as \(C_j^{\delta}\):

\[ T_{ij} \;=\; A_i\, O_i\; W_j^{\alpha}\; C_j^{\delta}\; e^{\gamma \kappa_{ij}}\; c_{ij}^{-\beta} \quad\text{then capped,}\qquad \delta = +0.3 \]

Show code
mt_table("M4", "M3") %>% knitr::kable(align = "lrrrrrc")
Table 29: M4. The full form. The change column is against M3.
School PAN Offers made Modelled Residual Change from M3 Rationing?
Brighton Aldridge 180 146 101 -45 +13
Cardinal Newman 360 360 353 -7 -3 yes
Blatchington Mill 330 330 324 -6 -21 yes
Longhill 210 81 79 -2 -15
Varndean 300 300 300 +0 -0 yes
King’s 180 180 180 +0 +0 yes
Dorothy Stringer 330 330 330 +0 -17 yes
Portslade Aldridge 220 183 194 +11 -10
Patcham 225 204 225 +21 +0
Hove Park 180 136 180 +44 +0

R² 0.95, RMSE 21, and 24 children average error across the five schools that were not rationing places. The gains are concentrated in the paired catchments, which is where the term has most to work with — but they come from a \(\delta\) of +0.3, and a positive \(\delta\) says clustering helps. Read as a competing-destinations result it has the sign backwards; read as a fit, it is a small improvement bought with a parameter estimated from ten numbers.

The most useful thing in this table is which schools the model now rations. The ceiling binds on 5 schools, and the real round rationed 5. 3 of them are the same: Dorothy Stringer, King’s, and Varndean. The model swaps Blatchington Mill and Cardinal Newman, which filled and which it leaves 6, 7 short, for Hove Park and Patcham, which did not fill and which it fills.

Getting 3 of 5 right from published data and no admissions records is more than the fit statistics convey. Both errors are also explicable. Cardinal Newman is a faith school and this model has no faith restriction; the variant that adds one is discussed below. Hove Park is the school whose demand is second preferences that never convert (Section 7.2).

Show code
mt$pair_split %>%
  transmute(Pair = pair, Model = model,
            `Modelled total` = fmt_n(mod_total),
            `Offers made` = fmt_n(obs_total),
            `Modelled share` = fmt_pct(100 * mod_share, 0),
            `Observed share` = fmt_pct(100 * obs_share, 0)) %>%
  arrange(Pair, Model) %>%
  knitr::kable(align = "llrrrr")
Table 30: Each paired catchment splits between its two schools. The share is the one going to the second-named school. A model can get a catchment’s total right and still send the children to the wrong school within it.
Pair Model Modelled total Offers made Modelled share Observed share
Hove Park / Blatchington M0 487 466 50% 29%
Hove Park / Blatchington M1 497 466 54% 29%
Hove Park / Blatchington M2 471 466 38% 29%
Hove Park / Blatchington M3 484 466 37% 29%
Hove Park / Blatchington M4 504 466 36% 29%
Hove Park / Blatchington M5 495 466 33% 29%
Stringer / Varndean M0 479 630 51% 48%
Stringer / Varndean M1 623 630 59% 48%
Stringer / Varndean M2 613 630 49% 48%
Stringer / Varndean M3 613 630 49% 48%
Stringer / Varndean M4 630 630 48% 48%
Stringer / Varndean M5 630 630 48% 48%

Stringer and Varndean are fixed. The distance-only model splits that catchment 51% to Varndean against an observed 48%; weighted preferences make it worse at 59%; the capacity ceiling brings it to 49% and the full model lands on 48%. Stringer and Varndean are 470 metres apart and both fill, so what decides the split between them is which one runs out of places first — a constraint, not a preference.

Hove Park and Blatchington Mill are improved and not fixed. The observed split sends 29% to Hove Park. Brightopia says 50%, and the full model still says 36%. Hove Park remains the one school the model cannot place: it is pinned at its admission number of 180 when only 136 were offered.

WarningWhat this is not

These are not estimates. \(\gamma\) and \(\delta\) are fitted to ten destination totals. That is enough to say whether a term moves the model towards the offers and nowhere near enough to say what its value is; the identification problem the open model sets out has not gone away. A \(\delta\) of +0.3 fitted on ten numbers after a capacity constraint has already absorbed most of the variance should be read as “this term has little left to do”, not as a measurement of agglomeration.

M5 below does estimate the catchment term, from a different target: the catchment-level preference table, which has sixty cells rather than ten totals and is not already fixed by the admission numbers.

Adding terms until the fit improves is not validation. Each rung here has more freedom than the last, so some improvement is guaranteed. The reason the capacity result is worth stating is that it is not a fitted parameter at all — the admission numbers are published, and putting them in is a correction rather than a tuning.

The faith split makes things worse here, and that is informative. Holding half the city ineligible for Cardinal Newman and King’s, as Section 5.5’s identification work does, drops R² to 0.79: it leaves Cardinal Newman unable to fill, and Cardinal Newman filled. A refinement that helps against the published second-preference profile hurts against total offers. Both targets are real, and no single specification here is best at both.

7.8.3 M5 — calibrated to what each catchment asks for

Every rung so far is scored against the offers the council made, and from M2 onwards most of those offers are admission numbers the model has been told. That is why the catchment term came out at \(\gamma\) = 0.4: it was asked what is left for catchment to explain once capacity has done its work. That is not the question a catchment policy turns on. The question is how strongly families follow the map when they choose, and it has an answer in data.

The council’s evidence to the Schools Adjudicator includes, for each catchment and each of the 3 rounds from 2023/24 to 2025/26, how many first, second and third preferences its children gave each school. It is a flow matrix at catchment level — sixty cells, where the offers are ten totals, and not already fixed by the admission numbers — and it shows something M4 gets badly wrong.

Show code
cal$own %>%
  arrange(desc(observed)) %>%
  transmute(Catchment = unname(catch_lab[home]),
            `Families' first preferences` = fmt_pct(100 * observed, 0),
            M4 = fmt_pct(100 * M4, 0),
            M5 = fmt_pct(100 * M5, 0)) %>%
  knitr::kable(align = "lrrr")
Table 31: Share of each catchment’s first preferences going to its own catchment school or schools: what families did, where M4 sends that catchment’s demand, and M5. Pooled over three rounds.
Catchment Families’ first preferences M4 M5
Stringer / Varndean 81% 61% 83%
Patcham 65% 47% 70%
Portslade Aldridge 57% 42% 53%
Hove Park / Blatchington 44% 45% 44%
Brighton Aldridge 41% 25% 38%
Longhill 40% 18% 32%

Families follow their catchment far more closely than M4 allows. 81% of first preferences from the Stringer / Varndean catchment go to one of its two schools; M4 sends 61% of that catchment’s demand there and spreads the rest across the city. With the demand spread thin, the pressure that makes Varndean turn catchment children away never builds in the model — and Hove Park, next to the densest part of Hove, collects the demand Stringer and Varndean should have.

M5 fits three things to the table, all on uncapped demand — what families ask for — and then applies the capacity ceiling. Unlike M2, it re-offers the children a full school refuses the way a family’s second preference would, rather than in proportion to their first.

7.8.3.1 A catchment term for each catchment

\[ T_{ij} \;=\; A_i\, O_i\; W_j\; C_j^{\delta}\; e^{\gamma_{h(i)} \kappa_{ij}}\; c_{ij}^{-\beta} \]

where \(h(i)\) is the catchment neighbourhood \(i\) lies in. The six \(\gamma_h\) are fitted by multinomial deviance: how likely the first preferences each catchment’s children actually gave are, under the shares the model gives them. The fit uses the pre-2024 map the preferences were made under.

Show code
tibble(home = names(cal$gamma), gamma = unname(cal$gamma)) %>%
  arrange(desc(gamma)) %>%
  transmute(Catchment = unname(catch_lab[home]),
            `γ` = sprintf("%.1f", gamma),
            `Pull of the catchment school` = sprintf("×%.1f", exp(gamma))) %>%
  knitr::kable(align = "lrr")
Table 32: The catchment term, fitted for each catchment. e to the gamma is how many times more attractive a school is to a family living in its catchment than to an otherwise identical family outside it.
Catchment γ Pull of the catchment school
Stringer / Varndean 2.6 ×13.5
Longhill 2.3 ×10.0
Portslade Aldridge 1.7 ×5.5
Hove Park / Blatchington 1.6 ×5.0
Patcham 1.5 ×4.5
Brighton Aldridge 1.0 ×2.7

The terms run from 1.0 in Brighton Aldridge to 2.6 in Stringer / Varndean, against the single 0.4 fitted to offers. A catchment school is between 3 and 13 times as attractive to a family living in its catchment as to one outside it. Catchment is the strongest behavioural term in the model, not the weakest.

7.8.3.2 Attractiveness balanced to what families ask for

In M1 to M4, \(W_j\) is weighted preferences per place. Once distance, the catchment term and competing destinations act on it, the demand the model generates no longer matches the demand it was built from. M5 re-balances \(W_j\) so the model’s uncapped demand for each school is that school’s share of the city’s first preferences in the same table.

Show code
tibble(name = names(cal$W),
       W4 = unname(cal$W_wprefs[names(cal$W)]), W5 = unname(cal$W)) %>%
  mutate(W4 = W4 / mean(W4), W5 = W5 / mean(W5)) %>%
  left_join(cal$wanted, by = "name") %>%
  left_join(oi$schools %>% select(name, pan2026), by = "name") %>%
  arrange(desc(wanted / pan2026)) %>%
  transmute(School = short_sch(name),
            `W, M1 to M4` = sprintf("%.2f", W4),
            `W, M5` = sprintf("%.2f", W5),
            `Demand before the ceiling` = fmt_n(wanted),
            PAN = fmt_n(pan2026),
            `Demand per place` = sprintf("%.2f", wanted / pan2026)) %>%
  knitr::kable(align = "lrrrrr")
Table 33: Attractiveness before and after balancing, both scaled to a mean of one across the city’s ten schools, and the demand M5 puts on each school before the capacity ceiling, for the 2026 cohort.
School W, M1 to M4 W, M5 Demand before the ceiling PAN Demand per place
Varndean 1.53 1.03 421 300 1.40
King’s 1.67 2.57 232 180 1.29
Cardinal Newman 1.19 3.27 441 360 1.23
Patcham 0.89 0.78 191 225 0.85
Blatchington Mill 0.98 0.57 274 330 0.83
Hove Park 1.13 0.36 128 180 0.71
Dorothy Stringer 1.15 0.55 230 330 0.70
Portslade Aldridge 0.68 0.28 140 220 0.63
Brighton Aldridge 0.48 0.48 81 180 0.45
Longhill 0.30 0.10 78 210 0.37

Two schools move most. Cardinal Newman was under-weighted: families ask for it city-wide and it gets no catchment term, so a weight built from preferences per place left the model wanting fewer places there than it has. M5 puts 441 children’s demand on its 360 places, and it fills — as it always does. Hove Park was the opposite, and now draws 128 against 180 places, which is why it no longer fills in the model. It does not fill in reality either.

Balancing to weighted preferences instead of first preferences was tried. It fills Hove Park, which does not fill, so first preferences are used: they are also one unit per child, which is what the model’s flows are.

7.8.3.3 Families who would take only one of the pair

Every model up to M4 treats a child in a paired catchment as equally content with either school: refused at Varndean, the child falls back on Stringer. Some families would not. A child who names Varndean and not Stringer, and is refused at Varndean, is placed somewhere else — and that child is exactly who is displaced from the catchment. M5 carries those families as populations of their own, whose choice sets leave the other school out.

Show code
cal$exclusive %>%
  transmute(Catchment = unname(catch_lab[catchment]),
            `Would take only` = short_sch(school),
            `Named it, three rounds` = fmt_n(named),
            `Share used` = fmt_pct(100 * share, 1),
            Bounds = sprintf("%s to %s", fmt_pct(100 * share_lo, 1),
                             fmt_pct(100 * share_hi, 1))) %>%
  knitr::kable(align = "llrrr")
Table 34: Families in the two paired catchments who would accept only one school of the pair. The table counts how many children named each school at any rank, not who named both, so the share is bounded rather than observed. The share used sits midway between the most overlap the counts allow and what independent naming would give.
Catchment Would take only Named it, three rounds Share used Bounds
Hove Park / Blatchington Hove Park 1,344 5.2% 0.0% to 18.2%
Hove Park / Blatchington Blatchington Mill 1,919 29.7% 24.5% to 42.7%
Stringer / Varndean Dorothy Stringer 1,818 3.3% 0.0% to 7.4%
Stringer / Varndean Varndean 1,877 6.2% 2.9% to 10.4%

The default is not taken to either end. Families treat a pair as substitutes, so the real overlap is above what independent naming would produce, and the share of exclusive families below it. The simulator lets it be scaled.

7.8.3.4 Children who leave the city

Every rung up to here sends every child to one of the ten city schools. Some go elsewhere, and for Longhill that is most of the question. The council’s answer to a Freedom of Information request, published on WhatDoTheyKnow, gives every school each catchment’s children were offered a place at in the 2024 round, including schools outside the city: Longhill’s catchment was offered 38 places at Priory School in Lewes, and fewer than five each at Peacehaven and Seahaven.

So M5 carries four East Sussex schools — Priory, Peacehaven, Seahaven and Seaford Head — as destinations, each with an attractiveness of its own and one decay on straight-line distance, fitted to each catchment’s share of those offers. Straight-line rather than routed, because the bus network has no Woodingdean to Lewes service: the router sends those families through Brighton, 71 to 95 minutes, and a model on that cost sends Longhill’s leavers to Peacehaven instead of Priory. Counts the council suppressed enter the fit as the interval they are. The fitted decay is steep, 8.0: a school outside the city draws from the neighbourhoods nearest it and from almost nowhere else.

Show code
if (!is.null(cal$outside)) {
  cal$outside$fit %>%
    filter(published != "0" | expected_2024 >= 0.5) %>%
    transmute(Catchment = dplyr::coalesce(unname(catch_lab[catchment]), catchment),
              School = name, `Published, 2024` = published,
              `Modelled` = sprintf("%.1f", expected_2024)) %>%
    knitr::kable(align = "llrr")
}
Table 35: Offers at the four East Sussex schools in the 2024 round, published and modelled. Modelled at the 2024 scale: M5’s share of each catchment’s children, times the children that catchment was offered places. Rows where neither is above half a child are left out.
Catchment School Published, 2024 Modelled
Brighton Aldridge Priory School <5 5.8
Stringer / Varndean Priory School <5 1.1
Hove Park / Blatchington Priory School <5 0.3
Longhill Peacehaven Community School <5 2.2
Longhill Priory School 38 35.1
Longhill Seahaven Academy <5 2.1
Patcham Priory School 0 1.1

In 2026 M5 sends 40 children from Longhill’s catchment to these schools, 36 of them to Priory. Children offered places in West Sussex or London — a handful a year from Hove, Portslade and Stringer / Varndean — are not modelled, nor are those who go to independent schools.

7.8.3.5 Where refused children go

M2’s ceiling cuts every applicant to a full school back by the same share and then scales each neighbourhood’s flows back up until all its children are placed. That spreads a refused child across every other school in proportion to how much the neighbourhood wanted it first. It is a fair average of a random tie-break at the school that is full. It is not a fair account of where the refused child then goes, and in one place it produced a contradiction: in the Stringer / Varndean catchment the model counted families who would take either school as displaced from the catchment while Stringer itself was short of its admission number, because their refused Varndean demand had been spread across the city rather than offered to the school next door.

M5 re-offers refused demand in two steps, and only ever to schools with room:

  1. The other school of the pair first. A family in a paired catchment who would take either school, refused at one, goes to the other while it has places.
  2. Then second preferences. Everything else is shared among schools with room in proportion to the second preferences the family’s home catchment gives each school, in the same catchment preference table, adjusted for how near this neighbourhood is to each school compared with its catchment as a whole.

Demand re-offered to a school with little room can overfill it; the next round cuts that back and re-offers the excess. If no school anywhere has room, the children stay unplaced rather than being forced over an admission number. It is the average of what a round of deferred acceptance does when ties are broken at random, with second preferences standing in for the rest of each family’s list.

Show code
if (!is.null(cal$overflow)) {
  cal$overflow %>%
    mutate(Catchment = unname(catch_lab[catchment]), School = short_sch(school)) %>%
    select(Catchment, School, share) %>%
    group_by(Catchment) %>%
    arrange(desc(share), .by_group = TRUE) %>%
    slice_head(n = 3) %>%
    summarise(`Where its second preferences go, top three` =
                paste(sprintf("%s %s", School, fmt_pct(100 * share, 0)), collapse = ", "),
              .groups = "drop") %>%
    knitr::kable(align = "ll")
}
Table 36: Where each catchment’s second preferences go: the share of the catchment’s second preferences, over three rounds, naming each school. These are the weights refused children are re-offered with, among schools that still have places. Faith schools are included; they are full in every round, so they take none of it.
Catchment Where its second preferences go, top three
Brighton Aldridge Varndean 29%, Patcham 26%, Dorothy Stringer 22%
Hove Park / Blatchington Blatchington Mill 31%, Hove Park 25%, King’s 21%
Longhill Varndean 24%, Dorothy Stringer 21%, Longhill 17%
Patcham Varndean 42%, Dorothy Stringer 31%, Patcham 14%
Portslade Aldridge Hove Park 30%, King’s 27%, Blatchington Mill 18%
Stringer / Varndean Dorothy Stringer 55%, Varndean 32%, Cardinal Newman 6%

The kernel is a trait of where a family lives, like the catchment term, so under a redrawn map it stays with the neighbourhood. The same code runs in the simulator, which is checked against these runs.

7.8.3.6 What M5 changes

Show code
cal$fit %>%
  transmute(Model = model, Deviance = fmt_n(deviance),
            `Cell RMSE, percentage points` = sprintf("%.1f", cell_rmse_pp)) %>%
  knitr::kable(align = "lrr")
Table 37: Fit to the catchment preference table: multinomial deviance, and the root-mean-square gap between modelled and observed shares across the sixty catchment by school cells.
Model Deviance Cell RMSE, percentage points
M4 2,858 7.8
M5 697 3.3

Against what families ask for, the deviance falls from 2,858 to 697 and the typical gap in a catchment’s share for a school from 7.8 percentage points to 3.3.

Show code
mt_table("M5", "M4") %>% knitr::kable(align = "lrrrrrc")
Table 38: M5 against the offers made. The change column is against M4. M5 is not fitted to these numbers, so this is a test it was not built to pass.
School PAN Offers made Modelled Residual Change from M4 Rationing?
Brighton Aldridge 180 146 91 -55 +10
Portslade Aldridge 220 183 150 -33 +21
Cardinal Newman 360 360 360 -0 -7 yes
Blatchington Mill 330 330 330 +0 -6 yes
Dorothy Stringer 330 330 330 +0 -0 yes
King’s 180 180 180 +0 -0 yes
Varndean 300 300 300 +0 +0 yes
Longhill 210 81 87 +6 +5
Patcham 225 204 222 +18 -3
Hove Park 180 136 165 +29 -15

R² 0.95, RMSE 23, and 28 children average error across the five schools that were not rationing places. Against the offers it is not built to reproduce, M5 lands close to M4 overall; the difference is in which schools the ceiling binds on. Cardinal Newman fills and Hove Park does not, as in the real rounds, and the Stringer / Varndean split is 48% to Varndean against an observed 48%.

NoteWhat M5 still does not do

Distance decay is set, not fitted. A catchment-level table cannot separate how far families will travel within a catchment from how strongly they follow it. \(\beta\) stays at 1.7, the decay the accessibility work settles on.

The catchment terms are fitted to one map. They describe how families in each neighbourhood chose under the boundaries they had. Carried to a redrawn map, they assume families would follow a new boundary as closely as the old one, which no data here can confirm.

Two catchments are still under-fitted. Longhill’s children put Longhill first 40% of the time and M5 gives it 32%; Portslade Aldridge 57% against 53%. With one distance decay for the whole city, the model cannot make eastern and western families both follow their catchment this closely and travel as far as they do.

The exclusive families are a bounded assumption. The share is taken from inside the bounds the table allows, not measured.

7.8.4 The modelling choices, and why

Every modelling decision behind the full model and the simulator, in one place. “Set” means chosen and stated rather than estimated.

Component What is used Where it comes from Why, and what else was tried
What a flow is Preferences, not offers — Offers are rationed. A model fitted to them learns the admission numbers, which is what happened to \(\gamma\) in M3.
Who is modelled Brighton & Hove children; the ten city schools and four East Sussex schools as destinations Cohort estimates from section 3 Children do leave the city, about 70 a year, three in four of them from Longhill’s catchment and most of those for Priory School in Lewes. Priory, Peacehaven, Seahaven and Seaford Head are destinations. West Sussex and London schools, a handful a year, are not; the simulator shows them as an estimate, the adjudicator’s published count less what the model places in East Sussex. Children who go to independent schools or move away are in neither, so every city school’s intake is slightly high.
Schools outside the city An attractiveness for each, one decay on straight-line km, no catchment term, not rationed The council’s FOI response on WhatDoTheyKnow: 2024 offers by catchment and school Routed walk and bus time was tried and sends Longhill’s leavers to Peacehaven: there is no Woodingdean to Lewes bus in the network. The adjudicator’s catchment totals alone get the numbers right and the destinations wrong.
Journey cost Routed walk and bus minutes, \(c_{ij}^{-\beta}\) r5r over OpenStreetMap and bus timetables Straight-line distance sends families across hills and water no bus crosses.
Distance decay \(\beta\) = 1.7 Set Not identifiable from catchment-level data. It is the value the accessibility work settles on, inside the open model’s swept range.
School attractiveness \(W_j\) balanced so demand matches each school’s share of first preferences Catchment preference table, school totals Weighted preferences per place (M1 to M4) left Cardinal Newman short of its demand and gave Hove Park more than twice its own. Balancing to weighted preferences filled Hove Park, which does not fill.
Catchment A term for each catchment, \(e^{\gamma_h}\), by where a family lives Catchment preference table, by deviance One city-wide term fitted to offers came out at 0.4. Fitted to preferences the terms run from 1.0 to 2.6. Fitted on the pre-2024 map the preferences were made under; run on the map in force. Under a redrawn map the term stays with the neighbourhood.
Paired catchments A share of families who would take only one school of the pair Bounded by the same table; set midway The table counts who named each school, not who named both. Without it, a child refused at one school of the pair always falls back on the other, and displacement from those catchments is understated.
Competing destinations \(C_j^{\delta}\), \(\delta\) = +0.3 Fitted in the ladder, against offers Kept at its ladder value and not re-fitted. It is a small term; Section 7.8.2 sets out why its sign should not be read as a finding.
Faith schools Open to every family — Holding half the city ineligible fits the second-preference profile better but stops Cardinal Newman filling, and it fills.
Capacity A one-sided ceiling; refused demand re-offered to the other school of a pair, then by second preference, to schools with room Admission numbers in force; catchment preference table, second preferences Applicants to a full school are cut back in proportion, which is what a random tie-break averages to — and the council breaks ties at random. Re-offering refused children in proportion to their first preferences (M2 to M4) left Stringer short of its number while counting Stringer / Varndean families as displaced.
Admission rules (simulator only) Optionally, the council’s 2026/27 priorities as a tiered ceiling at the six community schools Admissions guide 2027–28 FSM eligibility is the IDACI score scaled by one take-up constant, set so FSM-priority offers at the three schools that ration match the 192 made in September 2026. Priority-6 places are not fitted.
Offer day, not September Every comparison is with national offer day Section 5.2 Six months of appeals and movement follow offer day, so September rolls are not what an allocation produces. The gap is now measured for 2026 entry: about 60 children, three per cent of the cohort, and the model over-predicts the oversubscribed schools against September by about 16 each.

7.9 Every school, under the full model

Section 7.3 showed Longhill under each rung of the ladder. This is the other nine, all under the full model, M5 (Section 7.8.3) — attractiveness balanced to first preferences, a catchment term for each catchment, competing destinations, the paired-catchment families who would take only one of the two schools, and the capacity ceiling.

Show code
ma <- leaflet(width = "100%", height = 660,
              options = leafletOptions(preferCanvas = TRUE)) %>%
  add_basemap() %>%
  addPolygons(data = catch, fill = FALSE, color = "#555555",
              weight = 1, opacity = 0.5, group = "Catchment boundaries")

for (i in seq_len(nrow(fa_groups))) {
  g <- fa_groups$label[i]
  d <- fa_net %>% filter(label == g) %>% arrange(flow)
  ma <- ma %>%
    addPolylines(data = d, group = g,
                 color = ~ unname(FM_COL[leg_mode]),
                 weight = ~ fm_w(flow), opacity = 0.75,
                 label = ~ sprintf("%s leg · %.0f children", leg_mode, flow)) %>%
    addCircleMarkers(
      data = fm_sch %>% filter(name == fa_groups$name[i]), group = g,
      radius = 6, color = "#111111", weight = 2, opacity = 1,
      fillColor = "#ffffff", fillOpacity = 1, label = ~ name) %>%
    addCircleMarkers(
      data = fm_sch %>% filter(name != fa_groups$name[i]), group = g,
      radius = 3, color = "#777777", weight = 1, opacity = 0.9,
      fillColor = "#cccccc", fillOpacity = 0.9, label = ~ name)
}

ma %>%
  fitBounds(lng1 = unname(fa_bb["xmin"]) - 0.01, lat1 = unname(fa_bb["ymin"]) - 0.005,
            lng2 = unname(fa_bb["xmax"]) + 0.01, lat2 = unname(fa_bb["ymax"]) + 0.005) %>%
  addLayersControl(baseGroups = fa_groups$label,
                   overlayGroups = "Catchment boundaries",
                   options = layersControlOptions(collapsed = FALSE)) %>%
  hideGroup("Catchment boundaries") %>%
  addLegend("bottomright", colors = unname(FM_COL), labels = names(FM_COL),
            title = "Leg", opacity = 0.8)
Figure 35: Modelled flows into each school under the full model, routed over the walking and bus network and summed onto shared segments. Every panel is the same model; only the school changes. Line widths are on the same scale as the Longhill map above, so a thick line means the same number of children in both.

Switched one at a time, the maps sort the city into two kinds of school, and the sorting is not the one you might expect.

Longhill draws more locally than any other school in the city. 97% of its modelled intake starts inside its own catchment, against 84% for Portslade Aldridge, the next most self-contained, and 62% for Brighton Aldridge, the least. Only 1 catchments supply even a twentieth of it, the narrowest base in the city.

That sounds like a virtue and is not. A school draws locally either because families nearby want it or because nobody else does, and Section 7.2 has already established which applies here: the same model that gives Longhill this tight local star gives it 82 children in total. A narrow base is only good news when it is a full one.

The faith schools have no catchment and draw along every corridor. Cardinal Newman’s largest single source is the Hove Park / Blatchington catchment at 54%, from the far side of the city. That is the geographic shape of the point Section 5.5 makes about how heavily they are preferred.

One more thing to watch for. The paired catchments share their corridors almost exactly. Switch between Blatchington Mill and Hove Park and the same roads carry both — they draw 83% and 77% from the same catchment. That is why Section 7.5 could not separate them: the model can get the pair’s total right and still split it wrongly, because geography barely distinguishes the two.

WarningThis is an open-data model, and it is not calibrated

\(\beta\) cannot be estimated without pupil-level flows, so it is swept across a plausible range rather than fitted. Figures here use the reference value; the range is 1.5 to 3.2, and section 8 reports the band rather than a point wherever the conclusion depends on it. A calibrated model built on the council’s own allocation records would replace every band in this document with an estimate.

8 Redrawing the catchments

The catchments Brighton uses were drawn round schools. Section 7.3 drew where the model actually sends children. This section asks the obvious next question — if you drew the boundaries round something else, what would they look like, and would they be better? — and answers it with four alternatives and the same statistics applied to each.

NoteTwo methods, and the difference between them is the point

The power diagram is a proximity design. Every neighbourhood goes to the catchment with the lowest journey time minus a price, and the prices are then adjusted until every catchment holds the children its schools have places for. It knows how long journeys take and how many places exist. It knows nothing about what families want.

The flow regions are a demand design, and a standard functional regionalisation of the kind used to build travel-to-work areas:

  1. Dominant flow. Every LSOA joins the school it sends most children to — the Nystuen–Dacey construction, the oldest method there is.
  2. Contiguity repair. A catchment has to be one piece. Fragments move to the adjacent region they send most flow to.
  3. Capacity balance. Regions trade LSOAs until each holds roughly as many children as its schools have places, always moving the boundary LSOA that costs the least flow, and never breaking contiguity.

Which flows go in is the choice that matters. The obvious input is the full model of Section 7.5, and it would be the wrong one: it contains a catchment term, so its flows already know the current boundaries and regionalising them would partly rediscover the map this is meant to replace. The input is the capacity-ceiling model with no catchment term — the closest published data comes to where would children go if the rule did not exist but the places still ran out.

The two faith schools are left out of the geography, as they are now. They admit across the city, and giving them a catchment would be a change of policy rather than a change of map.

Show code
fr_pal <- colorFactor("Set2", domain = sort(unique(fr$regions_sf$grp)))
fr_sch <- schools_sf() %>% filter(name != "Peacehaven Community School")
fr_designs <- unique(fr$regions_sf$design)

# Longhill's marker moves with it in the relocation design.
elm_pt <- st_as_sf(tibble(name = "Longhill High School (Elm Grove)",
                          lon = COMART$lon, lat = COMART$lat),
                   coords = c("lon", "lat"), crs = 4326)

# Five base groups make a tall layers control. Left at the default it
# reached down into the legend in the opposite corner and the last
# option could not be clicked, so the control goes top-left and the
# legend bottom-right, with the map given the height to hold both.
# height only takes effect when width is given too, which is why every
# other map in this document passes the pair.
mr <- leaflet(width = "100%", height = 720,
              options = leafletOptions(preferCanvas = TRUE)) %>%
  add_basemap()

for (d in fr_designs) {
  poly <- fr$regions_sf %>% filter(design == d)
  is_elm <- str_detect(d, "Elm Grove")
  pts <- if (is_elm) fr_sch %>% filter(name != "Longhill High School") else fr_sch
  mr <- mr %>%
    addPolygons(data = poly, group = d,
                fillColor = ~ fr_pal(grp), fillOpacity = 0.45,
                color = "#333333", weight = 1.2, opacity = 0.8,
                label = ~ sprintf("%s · %d LSOAs", grp, lsoas)) %>%
    addCircleMarkers(data = pts %>% filter(!name %in% fr$faith), group = d,
                     radius = 5, color = "#111111", weight = 2, opacity = 1,
                     fillColor = "#ffffff", fillOpacity = 1, label = ~ name) %>%
    addCircleMarkers(data = pts %>% filter(name %in% fr$faith), group = d,
                     radius = 5, color = "#111111", weight = 2, opacity = 1,
                     fillOpacity = 0, label = ~ paste0(name, " (no catchment)"))
  if (is_elm)
    mr <- mr %>% addCircleMarkers(data = elm_pt, group = d, radius = 6,
                                  color = "#b2182b", weight = 2, opacity = 1,
                                  fillColor = "#ffffff", fillOpacity = 1,
                                  label = ~ name)
}

mr %>%
  addLayersControl(baseGroups = fr_designs, position = "topleft",
                   options = layersControlOptions(collapsed = FALSE)) %>%
  addLegend("bottomright", pal = fr_pal,
            values = sort(unique(fr$regions_sf$grp)), title = "Region",
            opacity = 0.8)
Figure 36: Five catchment designs. Switch between them with the control. Points are schools; the two faith schools, which have no catchment under any design, are hollow. In the last design Longhill sits at the top of Elm Grove rather than at Ovingdean.

8.1 How the designs compare

Show code
fr$designs %>%
  left_join(fr$changed %>% group_by(design) %>%
              summarise(moved = sum(Oi[moved]) / sum(Oi), .groups = "drop"),
            by = "design") %>%
  transmute(Design = design,
            Regions = regions,
            `Self-containment` = fmt_pct(100 * self_containment, 0),
            `Mean journey` = sprintf("%.1f min", mean_journey),
            `Worst capacity gap` = sprintf("%+.0f%%", 100 * worst_gap),
            `Children moved` = if_else(is.na(moved), "—",
                                       fmt_pct(100 * moved, 0))) %>%
  knitr::kable(align = "lrrrrr")
Table 39: The five designs. Self-containment is the travel-to-work-area statistic: the share of a region’s children whose modelled first choice is inside it. The capacity gap is the worst region’s demand against its fair share of the places, counting only the children the geography could place — the faith schools take about a fifth of the cohort across no catchment at all.
Design Regions Self-containment Mean journey Worst capacity gap Children moved
Current catchments 6 64% 22.8 min +17% —
Power diagram (proximity and capacity) 6 60% 23.4 min +9% 16%
Flow regions, one per school 8 58% 22.7 min +34% —
Flow regions, pairs kept 6 65% 22.8 min +12% 14%
Flow regions, Elm Grove, PAN 150 6 59% 23.0 min +64% 16%
Flow regions, Elm Grove, PAN 210 6 59% 22.6 min +36% 15%
Show code
fr$designs %>%
  transmute(Design = design, Fragments = fragments, Enclaves = enclaves,
            `Ragged neighbourhoods` = ragged,
            `Schools outside their own catchment` = schools_outside) %>%
  knitr::kable(align = "lrrrr")
Table 40: Whether each design is a usable map. A fragment is a piece of a catchment detached from the rest of it. An enclave is a catchment wholly surrounded by one other, which a contiguity check misses because a ring is connected. A ragged neighbourhood has at most one neighbour in its own catchment — not an island, but a sliver. The last column counts schools that fall outside their own catchment. The current map is scored on its whole-LSOA approximation.
Design Fragments Enclaves Ragged neighbourhoods Schools outside their own catchment
Current catchments 1 1 2 0
Power diagram (proximity and capacity) 0 0 2 0
Flow regions, one per school 0 0 4 2
Flow regions, pairs kept 0 0 2 0
Flow regions, Elm Grove, PAN 150 0 0 2 0
Flow regions, Elm Grove, PAN 210 0 0 2 0

Three of the five designs are clean maps. The power diagram and both paired flow designs have no detached pieces, no islands and every school inside its own catchment. Getting there took three rules that a capacity-balancing algorithm does not enforce on its own, and each was added because the first attempt broke it:

  • A catchment must contain its own school. Without it the relocation design put the Elm Grove site inside BACA’s region and left Longhill with nine neighbourhoods on the far side of the city.
  • An island counts as a fault. A region can be in one piece and still enclose another; a contiguity test passes it because a ring is connected. The power diagram arrived with three.
  • Repair by accessibility, not by flow. When a piece has to move, it joins whichever neighbouring catchment its children can reach quickest. Repairing by modelled flow instead sent boundaries over hills.

Regions on the coast or at the city edge have one land neighbour and are not islands, so the edge of the study area is exempt — a test that did not exempt it flagged Longhill and PACA as enclaves in every design.

The ragged column is the one a reader notices, and it is where the current map is furthest from the alternatives: 2 neighbourhoods against 2 for the power diagram and 2 for the flow regions. Two of them face each other across Elm Grove. A tongue of BACA reaches down past St Luke’s pool, almost surrounded by Stringer/Varndean; two Stringer/Varndean neighbourhoods reach up just north of it, almost surrounded by BACA.

Both redesigns swap them, without being asked to. Under the current map the neighbourhood by the pool is Varndean / Dorothy Stringer and the two north of Elm Grove are Varndean / Dorothy Stringer. Under the power diagram and the flow regions alike they are the other way round — Varndean / Dorothy Stringer and BACA (Brighton Aldridge) — which is what accessibility says they should be, and it removes both slivers at once.

Splitting the paired catchments fails this audit, which is a cleaner answer than the statistics gave. One region per school leaves 2 schools outside their own catchment, because Dorothy Stringer and Varndean are 470 metres apart and no contiguous capacity-balanced boundary can separate them. That is the flows saying what the council already knows.

The proximity design balances capacity best. The power diagram takes the worst capacity gap from +17% to +9% — a quarter of what the current map carries — while moving the fewest neighbourhoods of any alternative, and it adds 0.7 minutes to the average journey to a catchment school.

The flow regions win the statistic they were built to win. Keeping the council’s pairings gives the best self-containment of any design, 65% against 64% now, for 0.1 extra minutes. But it does not improve on the power diagram anywhere else, and the power diagram does not improve on the current map’s self-containment at all — 60% against 64%.

So the two methods answer different questions and neither dominates. Proximity-and-capacity produces the better-balanced, more compact map. Demand produces the map more children’s first choice sits inside.

8.2 The deprivation profile of each design

The consultation profiles the current catchments by putting every postcode inside a catchment boundary and counting households with dependent children by IDACI decile. The same method is applied here to every design, so what follows compares against the consultation’s own figures rather than against some other statistic that happens to be about deprivation.

Show code
bd <- fr$idaci_bands %>%
  mutate(design = factor(design, fr_designs),
         band = factor(band, fr$idaci_band_levels),
         region = fr_lab(grp)) %>%
  group_by(design, region) %>%
  mutate(dep = sum(share[band == fr$idaci_band_levels[1]])) %>%
  ungroup() %>%
  arrange(design, dep)

# The row key has to be unique across designs, because a catchment
# appears in several of them; the label shown is just the catchment.
row_lv <- unique(paste(bd$design, bd$region, sep = "|"))
bd <- bd %>% mutate(row = factor(paste(design, region, sep = "|"), row_lv))
row_lab <- setNames(sub("^.*\\|", "", row_lv), row_lv)

# The share in the most deprived band is printed OUTSIDE the bar. Inside
# it sat on the darkest fill and could not be read.
lab <- bd %>% filter(band == fr$idaci_band_levels[1])

ggplot(bd, aes(share, row, fill = band)) +
  geom_col(width = 0.8, colour = "white", linewidth = 0.5,
           position = position_stack(reverse = TRUE)) +
  geom_text(data = lab, aes(x = 1.03, y = row,
                            label = sprintf("%.0f%%", 100 * share)),
            hjust = 0, size = 2.5, colour = "grey25", inherit.aes = FALSE) +
  facet_grid(design ~ ., scales = "free_y", space = "free_y",
             labeller = label_wrap_gen(18), switch = "y") +
  scale_y_discrete(labels = row_lab) +
  scale_x_continuous(labels = scales::label_percent(),
                     breaks = seq(0, 1, 0.25), limits = c(0, 1.13),
                     expand = expansion(mult = c(0, 0))) +
  scale_fill_manual(values = BAND_COL, name = NULL) +
  labs(x = "Households with dependent children", y = NULL,
       title = "What kind of neighbourhood each catchment holds",
       subtitle = str_wrap(paste("Each bar is one catchment, ordered within its design by how deprived it is.",
                                 "The figure at the right is the share in the three most deprived deciles in England."), 96),
       caption = "Sources: ONS postcode household counts; IMD 2019 IDACI.") +
  theme_bh(8) +
  theme(legend.position = "top",
        panel.grid.major.y = element_blank(),
        strip.placement = "outside",
        strip.text.y.left = element_text(angle = 0, hjust = 1, face = "bold",
                                         size = 7))
Figure 37: How each catchment’s households with dependent children divide between the most deprived three deciles nationally, the middle four, and the least deprived three. Every bar is one catchment; bars are grouped by design and ordered within it by how deprived the catchment is.

BACA’s catchment is the outlier under every design, and under the current map it is extreme: 84% of its households with dependent children are in the three most deprived deciles nationally, against 12% in the least deprived catchment. No alternative has a catchment above 61%.

That range is the obvious summary and it is a poor one, because it says nothing about how many households sit at each end. A design can have a narrow range and still concentrate deprivation, and one here does. The measure that does not have that problem is a segregation curve.

Show code
cv <- fr$idaci_curve %>% mutate(design = factor(design, fr_designs))
ends <- cv %>% group_by(design) %>% slice_max(x, n = 1) %>% ungroup()
worst <- cv %>% group_by(design) %>% slice_max(gap, n = 1) %>% ungroup()

# Six lines is past the point where direct-labelling every one is
# readable - four of them run through the same cluster around 45% and
# the labels sat on top of each other. Legend for identity, direct
# labels for the two that matter: the best and the worst.
mark <- worst %>%
  filter(design %in% c(fr$idaci_summary$design[which.min(fr$idaci_summary$gorard)],
                       fr$idaci_summary$design[which.max(fr$idaci_summary$gorard)]))

ggplot(cv, aes(x, y, colour = design)) +
  geom_abline(slope = 1, intercept = 0, colour = "grey55", linetype = "31") +
  geom_segment(data = mark, aes(x = x, xend = x, y = y, yend = x),
               linewidth = 0.6, alpha = 0.6) +
  geom_line(linewidth = 0.9) +
  geom_point(data = mark, size = 2.6) +
  ggrepel::geom_text_repel(
    data = mark, aes(label = sprintf("%s\nwidest gap %.2f", str_wrap(design, 24), gap)),
    size = 2.8, hjust = 0, nudge_x = 0.07, nudge_y = -0.09,
    segment.size = 0.25, min.segment.length = 0, seed = 1, lineheight = 0.95,
    show.legend = FALSE) +
  scale_colour_manual(values = DESIGN_COL, name = NULL,
                      guide = guide_legend(nrow = 2, byrow = TRUE)) +
  scale_x_continuous(labels = scales::label_percent(),
                     limits = c(0, 1.02), breaks = seq(0, 1, 0.25)) +
  scale_y_continuous(labels = scales::label_percent()) +
  labs(x = "Cumulative share of all households with dependent children",
       y = "Cumulative share of those in the three most deprived deciles",
       title = "How evenly each design spreads disadvantage",
       subtitle = str_wrap(paste("The dashed line is a perfectly even spread.", "The marked points are the most and least even designs, at their widest gap from it - which for catchments ordered this way is Gorard's index exactly."), 100),
       caption = "Sources: ONS postcode household counts; IMD 2019 IDACI.") +
  theme_bh(11) +
  theme(legend.position = "top", legend.text = element_text(size = 8))
Figure 38: Segregation curves. Catchments are ordered from least to most deprived, then the cumulative share of all households with dependent children is plotted against the cumulative share of those in the three most deprived deciles. A design that spread deprivation evenly would trace the diagonal; the further a line bows below it, the more one catchment carries.
Show code
# The widest gap on the curve above is NOT reported as a second column.
# On catchments ordered by deprivation it is arithmetically identical to
# Gorard, to every decimal, so printing both would be one measure
# dressed as two agreeing with each other. R/03_flow_regions.R asserts
# the identity rather than trusting this comment.
fr$idaci_summary %>%
  arrange(gorard) %>%
  transmute(Design = design,
            `Least deprived catchment` = fmt_pct(100 * lo, 0),
            `Most deprived catchment` = fmt_pct(100 * hi, 0),
            `Range` = sprintf("%.0f points", 100 * spread),
            `Gorard index` = sprintf("%.3f", gorard)) %>%
  knitr::kable(align = "lrrrr")
Table 41: How evenly each design spreads disadvantage. The share columns are households with dependent children in the three most deprived deciles, for the least and most deprived catchment of each design. Gorard’s segregation index is the measure the open model uses for the same question: half the sum of the absolute difference between each catchment’s share of the city’s deprived households and its share of all households. Zero would be a perfectly even spread. Ranked best first.
Design Least deprived catchment Most deprived catchment Range Gorard index
Power diagram (proximity and capacity) 14% 57% 43 points 0.175
Flow regions, pairs kept 10% 61% 51 points 0.186
Flow regions, Elm Grove, PAN 150 0% 61% 61 points 0.210
Current catchments 12% 84% 73 points 0.214
Flow regions, Elm Grove, PAN 210 0% 61% 61 points 0.218
Flow regions, one per school 6% 61% 55 points 0.292

The power diagram spreads disadvantage most evenly, and it is not close. Its Gorard index is 0.175 against 0.214 for the map in force — about a 18% reduction — and its curve sits closest to the diagonal at every point.

2 of the alternatives are worse than the current map, which the range column hides. Look at the flow-region design with one catchment per school: its range is 55 points, narrower than the current map’s 73, and its curve bows furthest from the diagonal of any design here. A narrow range across catchments of very different sizes is not evenness.

The current map’s extreme catchment is BACA, and BACA is small. Concentrating disadvantage in a small catchment moves fewer households than spreading a moderate excess across two large ones — which is what the curve shows and the range cannot. Gorard weights by size; maximum minus minimum does not.

8.2.1 A catchment is not an intake

ImportantThe intake figures here remain withdrawn

The catchment figures above measure the neighbourhoods each catchment contains, and stand. The intake figures below come from this section’s allocation model and score intakes by the neighbourhoods their children come from, which does not match the schools’ published shares of disadvantaged pupils (Section 11). The full model has been recalibrated to those shares; this allocation model has not, so these intake figures remain withdrawn.

Everything above measures the neighbourhoods a boundary encloses. That is what the consultation profiles and it is the right first question, but it is not the question a parent or a headteacher is actually asking, which is who ends up in the school. The two are not the same thing. A real minority of children cross a boundary, the two faith schools admit across the city with no catchment at all, and any school that fills before its catchment does forces the rest outward.

So the same index is computed a second way: on the modelled intake of each school under the over-subscription rule in Section 8.4, rather than on the population of each catchment.

Show code
# A validated two-colour categorical pair, deliberately outside the six
# used for designs elsewhere in this section: colour here means which
# measure, not which design, and reusing the design hues for a different
# job is how a reader comes to think blue means "current catchments"
# everywhere.
PAIR_COL <- c(`Across the catchments` = "#6b4fbb",
              `Across the school intakes` = "#c2701c")

dm <- fr$score_wide %>%
  transmute(design = factor(design, fr_designs),
            `Across the catchments` = gorard_catch,
            `Across the school intakes` = gorard_schools) %>%
  tidyr::pivot_longer(-design, names_to = "measure", values_to = "g") %>%
  mutate(measure = factor(measure, names(PAIR_COL))) %>%
  # Labels go on the OUTER side of each point rather than above it. Two
  # of these designs differ by four thousandths, and above the points
  # their labels printed on top of each other as "0.31014".
  group_by(design) %>% arrange(g, .by_group = TRUE) %>%
  mutate(side = c(-1, 1)) %>% ungroup()

seg <- dm %>% select(-side) %>%
  tidyr::pivot_wider(names_from = measure, values_from = g)

ggplot(dm, aes(g, fct_rev(design))) +
  geom_segment(data = seg, aes(x = `Across the catchments`,
                               xend = `Across the school intakes`,
                               y = fct_rev(design), yend = fct_rev(design)),
               inherit.aes = FALSE, colour = "grey70", linewidth = 1.1) +
  geom_point(aes(colour = measure), size = 3.4) +
  geom_text(aes(label = sprintf("%.3f", g), colour = measure,
                hjust = if_else(side > 0, -0.3, 1.3)),
            size = 2.8, show.legend = FALSE) +
  scale_colour_manual(values = PAIR_COL, name = NULL) +
  scale_x_continuous(limits = c(0.15, 0.38), breaks = seq(0.15, 0.35, 0.05)) +
  scale_y_discrete(labels = function(x) str_wrap(x, 26)) +
  labs(x = "Gorard's segregation index", y = NULL,
       title = "A catchment is not an intake",
       subtitle = str_wrap(paste("The catchments a design encloses, against the intakes its schools fill once children cross boundaries.", "Further right is more segregated."), 100),
       caption = "Sources: ONS postcode household counts; IMD 2019 IDACI; modelled allocation.") +
  theme_bh(11) +
  theme(legend.position = "top", panel.grid.major.y = element_blank())
Figure 39: Gorard’s segregation index computed two ways for each design: across the catchments the design draws, and across the intakes its schools fill once the over-subscription rule has run. Zero would be a perfectly even spread. Five of the six designs fill their schools less evenly than they draw their map.

Five of the six designs are more segregated at the school gate than on the map, and the current one by the widest margin: 0.214 across the catchments becomes 0.288 across the intakes. Choice and over-subscription do not dilute the geography here. They sharpen it.

The exception is the design that fails everything else. One catchment per school goes from 0.292 to 0.212 — unchanged to within four thousandths — because it is already so segregated on the map that the allocation has nothing left to add.

That is the finding worth carrying out of this section. A boundary review that scores itself on catchment populations will overstate what it has achieved, because the population of a catchment is not the intake of its school, and the gap between the two is large in every design that is worth drawing.

The ranking changes as well. The power diagram is still first — 0.229 against 0.288 now — but the flow regions move up to second on intakes, 0.172, and both relocation designs move down: putting Longhill at Elm Grove leaves 0.261 at an admission number of 150 and 0.306 at 210, both worse than the map in force.

Under the current map the modelled intakes run from 0% of children from the three most deprived deciles at Blatchington Mill to 90% at Brighton Aldridge. No alternative closes that; the best of them narrows it to 10% to 72%.

NoteRead the two faith schools out of this

The index above is computed across the eight schools that have a catchment, so it is like for like with the catchment figure. Adding the two faith schools moves it to 0.306 for the current map and 0.267 for the power diagram — the same ordering, slightly higher levels.

But that column should be read with more suspicion than the rest of this section. The model admits to the faith schools on distance alone, because their actual criteria are faith-based and no published dataset contains them. Their modelled intakes are therefore the least reliable numbers in this document, and they take about a fifth of the cohort.

ImportantA finding that reversed under a better measure

An earlier version of this section reported that every alternative was less segregated than the current map. That was computed as the range between the least and most deprived catchment, and on that statistic it was true.

It does not survive being measured properly. Once catchment size is taken into account, 2 of the alternatives spread disadvantage less evenly than the map in force and 0 matches it. The claim has been withdrawn and the ranking above is by Gorard.

What survives is narrower and still worth having: one design, the power diagram, is clearly better than the current map on this measure, and it is better on capacity and shape too.

And a caution against reading any of this as a solution. 0.175 is a large index, and the best curve here still bows a long way from the diagonal. No redrawing of a catchment map fixes residential segregation, because the segregation is in where families live, not in where the lines are. What a map can do is stop amplifying it, and the distance between the best and worst curves is the size of that amplification.

8.2.2 Opening places to single-school catchments

ImportantRecalibrated

The first version of this subsection measured intakes by the neighbourhoods their children come from, which did not match the schools’ published shares of disadvantaged pupils (Section 11). It found priority 6 made intakes noticeably more segregated. On the recalibrated measure the effect is much smaller, and the mechanisms the first version described are not repeated here.

The 2026/27 arrangements added priority 6: a share of places at each community school for children living in the four single-school catchments — Brighton Aldridge, Longhill, Patcham and Portslade — who apply outside them. The council first consulted on 20% and settled on 5%. The case for it is access: families in catchments with one school, one of them the most deprived in the city, get a route into the popular schools of the two paired catchments.

The simulator runs the council’s priorities as a tiered ceiling on the full model of section 7 (Section 7.8.3): free school meals first, up to 30% of places, then priority 6 up to its share, then the catchment.

Show code
P6_COL <- c(`2026` = "#0d366b", `2030` = "#c2701c")
p6s$sweep %>%
  mutate(year = factor(year), fsm = if_else(fsm, "With the FSM priority", "Without it")) %>%
  ggplot(aes(p6, gorard, colour = year, linetype = fsm)) +
  geom_line(linewidth = 0.9) +
  geom_point(size = 2) +
  scale_colour_manual(values = P6_COL, name = NULL) +
  scale_linetype_manual(values = c(`With the FSM priority` = "solid", `Without it` = "22"), name = NULL) +
  scale_x_continuous(breaks = unique(p6s$sweep$p6), labels = function(x) paste0(x, "%")) +
  labs(x = "Priority-6 share of each community school's places", y = "Gorard's segregation index",
       title = "More places for single-school catchments barely changes how mixed intakes are",
       subtitle = "Further up is more segregated. The lines flatten once nobody else wants the places.",
       caption = "Sources: modelled intakes (M5); IMD 2019 IDACI; ONS postcode household counts.") +
  theme_bh(11) +
  theme(legend.position = "top")
Figure 40: Gorard’s segregation index across the ten city schools’ modelled intakes, under the council’s priorities, as the priority-6 share rises. Solid lines keep the free school meals priority; dashed lines switch it off. Full model (M5) with the tiered ceiling the simulator runs. The index here is across all ten city schools, so its level is not comparable with the eight-school figures above; the direction is the point.

Priority 6 makes almost no difference to how mixed the intakes are. In 2026 the index is 0.114 with priority 6 switched off, 0.117 at the 5% in force, 0.126 at 15% and 0.127 at 20%, where the first proposal would have put it. Past about 15% it stops moving, because there are no more families in the single-school catchments who want the places: 151 children at 15%, 153 at 40%. Between none and 15% the index moves by +10%, and by 2030 by +2% — well inside what the model’s assumptions could move.

Show code
p6s$schools %>%
  mutate(d_con = contrib_at - contrib_0) %>%
  arrange(desc(d_con)) %>%
  transmute(School = short_sch(name),
            `Deprived share, off` = fmt_pct(100 * dep_0, 1),
            `At 15%` = fmt_pct(100 * dep_at, 1),
            `Change, points` = sprintf("%+.1f", 100 * (dep_at - dep_0)),
            `Contribution to the index, change` = sprintf("%+.4f", d_con)) %>%
  knitr::kable(align = "lrrrr")
Table 42: Each school’s modelled intake, with priority 6 off and at 15% of places, under the council’s priorities in 2026: its share of disadvantaged pupils (calibrated to the published shares), and its contribution to Gorard’s index (half the absolute gap between its share of the city’s disadvantaged pupils and its share of all pupils). Ordered by how much the school adds to the index.
School Deprived share, off At 15% Change, points Contribution to the index, change
Dorothy Stringer 25.9% 23.9% -1.9 +0.0054
Varndean 29.6% 30.8% +1.2 +0.0030
Hove Park 35.2% 35.8% +0.6 +0.0014
Patcham 27.0% 27.4% +0.4 +0.0008
Brighton Aldridge 48.0% 49.4% +1.3 +0.0008
Blatchington Mill 22.5% 22.3% -0.2 +0.0005
Longhill 49.0% 50.6% +1.5 +0.0001
Cardinal Newman 17.3% 17.2% -0.0 +0.0000
King’s 16.4% 16.4% +0.0 -0.0000
Portslade Aldridge 28.0% 27.8% -0.2 -0.0002

Who uses it depends on the catchment. From Longhill’s catchment the priority-6 places go mostly to the less disadvantaged families: 18% of them to disadvantaged children, where the catchment as a whole is 26% disadvantaged. From Portslade’s it is the other way round, 39% against 30%; from Brighton Aldridge’s, 63% against 58%. It does not move the city’s index because the places are few and go to schools on both sides of the city’s share.

WarningHow far to take this

The effect is small beside what a catchment redesign does, and it is a model result, not an observed one: the council has published the priority-6 offers by school for 2026 but not which neighbourhoods they went to. Each school’s pull on disadvantaged children is fitted to its 2026 Year 7 offers, or for the academies and faith schools its published share, and held fixed, which is an assumption about how the children who use priority 6 differ from their neighbours. And segregation is one objective among several. Priority 6 widens access for the children who use it — the 69 or so a year at 5% — and on this evidence it does so at almost no cost to how mixed the intakes are. The simulator shows the same comparison live in its Fairness tab for any setting.

8.3 And if Longhill moves

The last two designs re-run everything with Longhill at the top of Elm Grove, at the two admission numbers the scenario suite considers: 150, which is Section 9’s shrink-and-move, and 210, which is move-only and keeps the number now in force.

NoteThese two are seeded differently, and it matters

Every other design here assigns each neighbourhood to the school it sends most children to. For a school that has just moved, there is no such school. The modelled flows at a new site are shaped by attractiveness, and Longhill’s is 0.32 against a city average of 1. Even with no capacity ceiling it wins the dominant flow almost nowhere, so a flow-seeded design handed the east of the city to Stringer/Varndean — a school those neighbourhoods are not nearest to and cannot reach in under an hour — and the balancer, which can only trade neighbourhoods across an existing boundary, could never reach far enough east to take them back.

A catchment is a statement about geography and capacity, not a popularity contest. A school does not forfeit a catchment for being unpopular; that is what the over-subscription rule is for. So these two designs are seeded on which catchment can this neighbourhood reach quickest, and the capacity balance trades from there. The Ovingdean designs keep the flow seed, because there the flows describe a school that is where it is.

Show code
ec %>%
  group_by(design) %>%
  summarise(`To Longhill` = sum(assigned == "Longhill"),
            `To BACA` = sum(assigned == "BACA"),
            `To Stringer / Varndean` = sum(assigned == "DS_Varndean"),
            `Nearest is Longhill` = sum(nearest == "Longhill"),
            `Mean journey` = sprintf("%.0f min", weighted.mean(t_assigned, Oi)),
            `If each went to its nearest` =
              sprintf("%.0f min", weighted.mean(t_near, Oi)),
            .groups = "drop") %>%
  rename(Design = design) %>%
  knitr::kable(align = "lrrrrrr")
Table 43: Where the twelve easternmost neighbourhoods end up under each relocation, and how long their journey is. ‘Nearest’ is the catchment they could reach quickest.
Design To Longhill To BACA To Stringer / Varndean Nearest is Longhill Mean journey If each went to its nearest
Elm Grove, PAN 150 12 0 0 12 40 min 40 min
Elm Grove, PAN 210 12 0 0 12 40 min 40 min

Every one of the 12 easternmost neighbourhoods is now in Longhill’s catchment, at the journey it could not better. Rottingdean, Saltdean, Ovingdean and Woodingdean all sit with the school they can reach quickest, and the mean journey to an assigned catchment school is 40 minutes — identical to the 40 they would get if each simply went to its nearest. Under the first flow-seeded version they averaged sixty.

That took a second rule beyond the accessibility seed, because the capacity balancer promptly gave them away again.

NoteWhich neighbourhood a catchment gives up when it is over capacity

Ranking candidates by how much worse the receiving catchment is than the donating one is the obvious rule and it behaves badly at the edges of the city. Rottingdean and Saltdean are 30 to 52 minutes from Longhill at Elm Grove and around ten minutes more from Stringer/Varndean — a small difference, so they looked cheap to give away and went first. Somewhere in Hanover, five minutes from Elm Grove and twelve from Varndean, scored worse and was kept.

Two changes fix it, and both are confined to the relocation designs:

  • Rank on the journey the child would actually make, not on the change in it. A neighbourhood with a short alternative is given up before one whose only alternative is an hour away.
  • Never move a neighbourhood whose nearest catchment is already more than 35 minutes away further from it. The far east has no good option and keeps its least bad one. Everywhere with a reasonable alternative stays fully tradeable, which is the point: the inner neighbourhoods absorb the balancing instead.

A blanket tolerance was tried first and was worse than useless. Six minutes blocked the inner moves as well, the balancer could shed almost nothing, and Longhill finished 231% over its admission number.

8.3.1 What this costs, and what the admission number decides

Show code
sw %>%
  transmute(`Admission number` = pan,
            `Easternmost neighbourhoods in its catchment` =
              sprintf("%d of %d", east_in_longhill, n_east),
            `Mean journey for the east` = sprintf("%.0f min", east_minutes),
            `Longhill capacity gap` = sprintf("%+.0f%%", 100 * longhill_gap)) %>%
  knitr::kable(align = "rrrr")
Table 44: Longhill at Elm Grove, swept across admission numbers. The capacity gap is its catchment’s demand against its fair share of the city’s places: a positive figure means the area it is nearest to holds more children than it has room for.
Admission number Easternmost neighbourhoods in its catchment Mean journey for the east Longhill capacity gap
150 12 of 12 40 min +73%
180 12 of 12 40 min +55%
210 12 of 12 40 min +35%
240 12 of 12 40 min +29%
270 12 of 12 40 min +26%
300 12 of 12 40 min +16%
330 12 of 12 40 min +7%
360 12 of 12 40 min +4%

The admission number does not decide the map. It decides whether the map is affordable. The east is in Longhill’s catchment at every number in the sweep, from 150 to 360, because that is where the geography puts it. What changes is the gap between what the catchment holds and what the school can take: +73% at 150 places, +35% at 210, and still +4% at 360.

That is the real result of moving the school. A site in the middle of Brighton is close to a great many children; Ovingdean is close to few. Drawing an honest catchment round a relocated Longhill produces a catchment far larger than 150 or 210 places can serve, and the corridor between Elm Grove and the coast has to be inside it or the catchment is in two pieces.

And the journey times are the best on offer. At 210 places this design has a mean journey to a catchment school of 22.6 minutes, against 22.8 for the map in force — the shortest of any design here, because the east finally sits with the school it is nearest to.

The cost lands in the allocation. A catchment holding 36% more children than its school has places cannot admit them all, so they are bumped to their next preference: 39% of the city’s children cross a boundary under this design against 32% now, the highest of any design tested, and only 61% are placed in their own catchment.

ImportantYou can draw the boundary properly or size the school properly. 210 places cannot do both.

Reducing Stringer’s admission number was the obvious lever and it does not reach: targets are shares of the city’s places, so taking 120 places off the Stringer/Varndean pair moves Longhill’s target by about eleven children. The binding constraint is Longhill’s own number.

Three things, and only two can hold at once:

  • A school of 150 or 210 places at Elm Grove.
  • A catchment containing the children it is nearest to — which means Rottingdean and Saltdean, and the corridor connecting them to the site.
  • A catchment it can actually admit, so that in-catchment children are not bumped across the city.

This section takes the first two, because a boundary that sends Saltdean an hour west to a school it is not nearest to is not a boundary anyone should propose. The arithmetic then shows up in the allocation instead of on the map, which is the honest place for it: the school is too small for the geography it would serve, and that is a decision about the admission number, not about the lines.

On everything else the relocation designs are unremarkable or worse. They move 15% of the cohort, much the largest upheaval on offer; they have the longest journeys of any design, 22.6 minutes against 22.8 now; and on deprivation the PAN 210 version scores 0.218 against 0.214 for the map in force and 0.175 for the power diagram.

So the relocation designs are not the best for the city on any measure here except the capacity gap they create for themselves — and Section 9.0.1 finds that, once catchment terms are fitted to what families ask for, moving is not the best for Longhill either. That is not an argument against moving the school. It is an argument for deciding the admission number and the boundary together, and for asking what happens to Ovingdean, Rottingdean and Saltdean before rather than after.

8.4 What the over-subscription rule does

A catchment map is half a policy. The other half is what happens when a school is over-subscribed, and that is where the cross-catchment movement Brighton actually sees comes from.

The rule modelled here is the one England uses, in the simplified form published data supports: every child has a preference order taken from the model’s own utility; an over-subscribed school admits in-catchment children first, and within each priority group the nearest first; children who miss out cascade to their next preference. Schools hold offers provisionally and can bump a held child when a higher-priority applicant arrives, which is how the coordinated scheme behaves.

Show code
fr$alloc %>%
  mutate(design = factor(design, fr_designs)) %>%
  arrange(design) %>%
  transmute(Design = design,
            `Got their first preference` = fmt_pct(100 * first_pref, 0),
            `Placed in their own catchment` = fmt_pct(100 * local_share, 0),
            `Crossed a boundary` = fmt_pct(100 * cross_share, 0),
            `— of which to a faith school` = fmt_pct(100 * faith_share, 0),
            `Mean journey` = sprintf("%.1f min", mean_journey)) %>%
  knitr::kable(align = "lrrrrr")
Table 45: Where children end up under each design, with an in-catchment-then-distance over-subscription rule. Crossing a boundary includes the two faith schools, which have no catchment at all and take about a fifth of the cohort.
Design Got their first preference Placed in their own catchment Crossed a boundary — of which to a faith school Mean journey
Current catchments 71% 68% 32% 24% 22.4 min
Power diagram (proximity and capacity) 71% 67% 33% 24% 22.3 min
Flow regions, one per school 71% 63% 37% 24% 22.7 min
Flow regions, pairs kept 71% 70% 30% 24% 22.3 min
Flow regions, Elm Grove, PAN 150 70% 65% 35% 24% 23.8 min
Flow regions, Elm Grove, PAN 210 72% 61% 39% 24% 22.8 min

Most children draw locally and a real minority cross, which is what a catchment system with choice in it should produce. The power diagram places 67% in their own catchment against 68% now, and the flow regions 70%. Of the 33% who cross under the power diagram, 24 percentage points go to the two faith schools, which have no catchment to cross — leaving about 9% genuinely moving between geographic catchments.

First-preference success does not move. It is 71% now and between 70% and 72% across every design tested. A better-drawn map gets more children the school nearest to them. It does not get more of them the school they want, and the second is worth more than the first.

8.5 What each design costs in travel

Redrawing a catchment moves children between schools, and the schools are not in the same places. So every design has a travel bill, and it falls on households as time and on the council as home-to-school transport.

The figures here are for the journey to the school each child is actually offered under the rule above — not to the school whose catchment they live in, which is the number the comparison table in Section 8.1 reports and which nobody travels to unless they get that school. Every statistic is weighted by children, so a design gets no credit for shortening the journey of a neighbourhood with four children in it.

Show code
jc <- fr$journey_curve %>% mutate(design = factor(design, fr_designs))

# Six lines on one set of axes was six lines on top of each other: the
# four designs that leave the schools where they are never differ by
# more than a few points at any threshold, and drawing them separately
# said "these are different" when the finding is that they are not. The
# four become a band whose width IS that disagreement, and the two
# designs that do move the distribution keep their own colours from the
# rest of this section.
band <- jc %>% filter(!str_detect(design, "Elm")) %>%
  group_by(t) %>%
  summarise(lo = min(share), hi = max(share), .groups = "drop")
elm <- jc %>% filter(str_detect(design, "Elm")) %>% droplevels()
BAND_LAB <- "The four designs that leave the schools where they are"

ggplot() +
  geom_vline(xintercept = c(fr$nts_min, fr$long_min), colour = "grey65",
             linetype = "31") +
  annotate("text", x = fr$nts_min - 1, y = 0.06,
           label = sprintf("England average, %d min", fr$nts_min),
           hjust = 1, size = 2.7, colour = "grey35") +
  annotate("text", x = fr$long_min + 1, y = 0.06, label = "40 min",
           hjust = 0, size = 2.7, colour = "grey35") +
  geom_ribbon(data = band, aes(t, ymin = lo, ymax = hi, fill = BAND_LAB)) +
  geom_line(data = elm, aes(t, share, colour = design), linewidth = 0.9) +
  # The band is the reference the two lines are read against, so it
  # comes first in the legend rather than after them.
  scale_fill_manual(values = setNames("grey72", BAND_LAB), name = NULL,
                    guide = guide_legend(order = 1)) +
  scale_colour_manual(values = DESIGN_COL[levels(elm$design)], name = NULL,
                      guide = guide_legend(order = 2)) +
  scale_x_continuous(breaks = seq(0, 60, 10),
                     labels = function(x) paste0(x, " min")) +
  scale_y_continuous(labels = scales::label_percent(),
                     limits = c(0, 1), breaks = seq(0, 1, 0.25)) +
  labs(x = "Journey time to the school offered", y = NULL,
       title = "How long the journey is, and for how many children",
       subtitle = str_wrap(paste("Redrawing the boundaries barely moves the distribution - the band is how much the four in-place designs disagree at all.", "Moving a school moves the tail."), 100),
       caption = "Sources: routed walk-and-bus times over OSM and Brighton & Hove GTFS; modelled allocation.") +
  theme_bh(11) +
  theme(legend.position = "top", legend.box = "vertical",
        legend.spacing.y = unit(1, "pt"), legend.text = element_text(size = 8))
Figure 41: The share of children whose offered school is within a given journey time, walking and bus. The four designs that leave the schools where they are are drawn as one band, because they never differ by more than the width of it; the two relocation designs are drawn separately. The dashed marks are the National Travel Survey average one-way school trip and the forty-minute line used in the table below.

Redrawing the boundaries changes the travel bill by almost nothing. The four designs that leave the schools where they are run from 22.3 to 22.7 minutes on the mean, and at no journey time does the share of children served differ between them by more than 3 percentage points — the width of the grey band. The reason is straightforward: a boundary decides which school a child is entitled to, but most children are already at a school close to them, and the schools have not moved.

Moving a school changes it a lot. Longhill at Elm Grove adds 1.4 minutes to the average journey at an admission number of 150 and 0.4 at 210 — and much more in the tail. The longest tenth of journeys starts at 40 minutes now and at 46 under the relocation.

Show code
js %>%
  arrange(design) %>%
  transmute(Design = design,
            `Mean` = sprintf("%.1f min", mean_min),
            `Longest tenth, from` = sprintf("%.0f min", p90_min),
            `Over 40 minutes` = fmt_pct(100 * over_long, 0),
            `Mean distance` = sprintf("%.2f km", mean_km),
            `Child-km a day` = fmt_n(round(child_km_day)),
            `Deprived, against the rest` = sprintf("%+.1f min", dep_gap)) %>%
  knitr::kable(align = "lrrrrrr")
Table 46: Journeys to the school each child is offered. Child-kilometres are both ways, every school day, for every child placed — the quantity a transport budget and a carbon figure are counted in. The last column is the difference between the mean journey of children living in the 39 neighbourhoods in the three most deprived deciles nationally and the mean journey of everyone else; a positive figure means deprived children travel further.
Design Mean Longest tenth, from Over 40 minutes Mean distance Child-km a day Deprived, against the rest
Current catchments 22.4 min 40 min 10% 1.51 km 6,839 +7.7 min
Power diagram (proximity and capacity) 22.3 min 39 min 9% 1.50 km 6,807 +8.8 min
Flow regions, one per school 22.7 min 41 min 11% 1.57 km 7,115 +9.3 min
Flow regions, pairs kept 22.3 min 39 min 10% 1.50 km 6,797 +8.2 min
Flow regions, Elm Grove, PAN 150 23.8 min 46 min 12% 1.81 km 8,204 +0.7 min
Flow regions, Elm Grove, PAN 210 22.8 min 45 min 11% 1.71 km 7,768 -2.4 min

8.5.1 Who makes the long journeys

A mean journey for the city is not a measure of a fair system. The question that matters is whether the long journeys fall on the children least able to absorb them, and under the map in force they do.

Show code
eq <- js %>%
  transmute(design,
            `Everyone else` = min_rest,
            `Most deprived neighbourhoods` = min_deprived) %>%
  tidyr::pivot_longer(-design, names_to = "who", values_to = "min") %>%
  mutate(who = factor(who, c("Everyone else", "Most deprived neighbourhoods")))

# The same validated pair as the intake chart, and used the same way
# round: the purple point is the reference, the amber one is the group
# the section is asking about.
EQ_COL <- setNames(c("#6b4fbb", "#c2701c"), levels(eq$who))
eqw <- eq %>% tidyr::pivot_wider(names_from = who, values_from = min)

ggplot(eq, aes(min, fct_rev(design))) +
  geom_segment(data = eqw, aes(x = `Everyone else`,
                               xend = `Most deprived neighbourhoods`,
                               y = fct_rev(design), yend = fct_rev(design)),
               inherit.aes = FALSE, colour = "grey70", linewidth = 1.1) +
  geom_point(aes(colour = who), size = 3.4) +
  # One decimal, because whole minutes made the two relocation rows read
  # as a one-minute gap when the figure is 0.8.
  geom_text(aes(label = sprintf("%.1f", min), colour = who),
            vjust = -1.2, size = 2.8, show.legend = FALSE) +
  scale_colour_manual(values = EQ_COL, name = NULL) +
  scale_x_continuous(labels = function(x) paste0(x, " min"),
                     limits = c(18, 32)) +
  scale_y_discrete(labels = function(x) str_wrap(x, 26)) +
  labs(x = "Mean journey to the school offered", y = NULL,
       title = "Whose children make the long journeys",
       subtitle = str_wrap(paste("Under every design that leaves the schools where they are,", "children in the most deprived neighbourhoods travel further."), 100),
       caption = "Sources: routed walk-and-bus times; IMD 2019 IDACI; modelled allocation.") +
  theme_bh(11) +
  theme(legend.position = "top", panel.grid.major.y = element_blank())
Figure 42: Mean journey to the school offered, for children living in the 39 neighbourhoods in the three most deprived deciles nationally against everyone else. Both means are over children, so a large deprived neighbourhood counts for more than a small one.

Children in the most deprived neighbourhoods travel 8 minutes longer than everyone else, each way, under the current map — 28 minutes against 20. That is a 38% longer journey, twice a day, for the households with the least slack in them, and no redrawing of the boundaries closes it: the best of the in-place designs gets it to 7.7 minutes.

Moving Longhill to Elm Grove does close it, and that is the strongest argument for the relocation anywhere in this document. The gap goes from +7.7 minutes to +0.7: children in the deprived east stop travelling further than everyone else, because the school they are entitled to has moved towards them instead of sitting at the far edge of the city.

It is also the clearest illustration in this section of why one number is not enough. The relocation costs the city 20% more child-kilometres a day and makes the deprivation gap disappear. Both are true. Which matters more is a judgement and it is the council’s to make — but it should be made knowing that the two point in opposite directions, rather than being told that one design is simply better.

8.6 Every measure side by side

Show code
fmt_val <- function(v, f) dplyr::case_when(
  is.na(v)    ~ "—",
  f == "pct0" ~ fmt_pct(100 * v, 0),
  f == "n0"   ~ fmt_n(round(v)),
  TRUE        ~ sprintf(f, v))

fr$score_long %>%
  mutate(design = factor(design, fr_designs),
         txt = fmt_val(value, fmt),
         txt = if_else(!is.na(value) & best, paste0("**", txt, "**"), txt),
         Measure = paste0(label, if_else(better == "low",
                                         " (lower is better)",
                                         " (higher is better)"))) %>%
  # Row order comes from the spec, not from the alphabet, so the three
  # families stay in the order the section argues them.
  arrange(family, label) %>%
  mutate(Measure = factor(Measure, unique(Measure))) %>%
  select(family, Measure, design, txt) %>%
  tidyr::pivot_wider(names_from = design, values_from = txt) %>%
  arrange(family, Measure) %>%
  select(-family) %>%
  knitr::kable(align = "l")
Table 47: Every measure in this section, for every design. The best figure in each row is in bold. Where a lower number is better the row says so in its name; the direction is set in R/03_flow_regions.R and the ranking is done from it, not by hand.
Measure Current catchments Power diagram (proximity and capacity) Flow regions, one per school Flow regions, pairs kept Flow regions, Elm Grove, PAN 150 Flow regions, Elm Grove, PAN 210
Segregation between catchments (lower is better) 0.214 0.175 0.292 0.186 0.210 0.218
Segregation between school intakes (lower is better) 0.288 0.229 0.212 0.172 0.261 0.306
Mean journey to the school offered (lower is better) 22.4 min 22.3 min 22.7 min 22.3 min 23.8 min 22.8 min
Longest tenth of journeys, from (lower is better) 40 min 39 min 41 min 39 min 46 min 45 min
Children over 40 minutes (lower is better) 10% 9% 11% 10% 12% 11%
Mean distance (lower is better) 1.51 km 1.50 km 1.57 km 1.50 km 1.81 km 1.71 km
Child-kilometres a day, both ways (lower is better) 6,839 6,807 7,115 6,797 8,204 7,768
Deprived children’s journeys, against the rest (lower is better) +7.7 min +8.8 min +9.3 min +8.2 min +0.7 min -2.4 min
Self-containment (higher is better) 64% 60% 58% 65% 59% 59%
Worst capacity gap (lower is better) 17% 9% 34% 12% 64% 36%
Got their first preference (higher is better) 71% 71% 71% 71% 70% 72%
Placed in their own catchment (higher is better) 68% 67% 63% 70% 65% 61%
Shape faults (lower is better) 2 0 2 0 0 0
Neighbourhood children reassigned (lower is better) — 16% — 14% 16% 15%
Show code
beat %>%
  transmute(Design = design, `Better` = better, `Worse` = worse,
            `The same` = same) %>%
  knitr::kable(align = "lrrr")
Table 48: How each design scores against the map in force, counted over the measures the current map can be scored on. A measure counts as the same where the two differ only in the fourth decimal.
Design Better Worse The same
Flow regions, pairs kept 11 1 1
Power diagram (proximity and capacity) 9 3 1
Flow regions, Elm Grove, PAN 150 4 9 0
Flow regions, Elm Grove, PAN 210 3 10 0
Flow regions, one per school 1 10 2

No design wins outright, and the two that come closest win on different things. Of the 13 measures the current map can be scored on, the flow regions with the council’s pairings kept beat it on 11 and the power diagram on 9. Between them they take 13 of the 17 first places in the table above.

  • The power diagram is the design to take seriously if the criterion is fairness and balance. It is best on both segregation measures, best on capacity, has no shape faults, and moves the fewest neighbourhoods.
  • The flow regions win the measures about where children actually go: self-containment, being placed in your own catchment, and the shortest mean journey of any design.
  • One catchment per school loses on almost everything, and should be read as the negative result it is. The council’s pairings are load bearing.
  • The two relocation designs lose on travel and on segregation, and win the one measure no boundary can touch — who makes the long journeys.

The measure nobody moves is the one families care about most. First-preference success sits between 70% and 72% across all six designs. Every gain in this section is a gain in fairness of entitlement — who is nearest, who is balanced against whom, who travels furthest. None of it is a gain in getting children into the school their family chose, because that is set by how many places the popular schools have, and a boundary does not create places.

WarningWhat this is and is not

It is a demonstration that the geography could be better, not a proposal. The regions are built from modelled flows and an uncalibrated model. A real redesign would use the council’s own preference and allocation records, which would replace every modelled flow here with an observed one.

Nothing here is in the algorithm that should be. Sibling links keep families together and are a large share of real admissions. Transport cost falls on the council and on households. Existing pupils cannot be moved. Feeder-primary patterns matter to families in ways a flow matrix does not see. Any of these could overturn a boundary drawn here.

The comparison is fair, though. Every design is scored with the same statistics on the same flows and profiled with the same postcode method, so the differences between the columns are like for like even where the levels are uncertain.

And the headline is a qualified one. One design — the power diagram — improves capacity balance, spreads disadvantage substantially more evenly, and moves the fewest neighbourhoods of any alternative. That is a real gain and it is worth having. But it changes how many children get their first choice by nothing at all, and two of the other three alternatives are worse than the current map on deprivation. Redrawing the map is worth doing on its own terms, and only if the redrawing is done on the right criterion. It is not an answer to the problem the rest of this document is about.

9 Longhill

Longhill High School sits at Ovingdean, at the eastern edge of both its catchment and the city. Its roll has fallen by 20% in four years and its reserve supports about 3.7 more years.

Section 7 established the constraint this section has to work inside. The school draws about 198 children on geography alone, and that figure barely moves across the swept parameter range. Reducing the admission number to 210 has already closed most of the gap between capacity and catchment. What remains is a cohort that keeps falling (section 3) and a reserve that runs out (section 6).

So the question is not whether something must change, nor whether the admission number should come down again — that lever is largely spent. It is whether any combination of the available changes produces a school that is viable at the end of the projection period rather than merely smaller.

Show code
# os$lh_band holds FILL RATES, not intakes: the modelled intake divided
# by whatever admission number that configuration sets. So configuration
# B fills at 100% on 150 places while A is short of 210, and the two
# numbers are not comparable as recruitment. Natural recruitment, which
# is comparable, is os$natural and is quoted alongside below.
lb <- os$lh_band %>%
  mutate(config = factor(config, levels = os$configs))

ggplot(lb, aes(entry_year, pmin(med, 1))) +
  geom_hline(yintercept = 1, linetype = "31", colour = "grey45") +
  geom_ribbon(aes(ymin = pmin(lo, 1), ymax = pmin(hi, 1)),
              fill = "#2166ac", alpha = 0.2) +
  geom_line(colour = "#2166ac", linewidth = 1) +
  geom_point(colour = "#2166ac", size = 1.8) +
  facet_wrap(~ config, ncol = 2, labeller = label_wrap_gen(38)) +
  scale_y_continuous(limits = c(0, 1.02), labels = scales::label_percent()) +
  labs(x = "Year of Year 7 entry", y = "Fill rate against that option's PAN",
       title = "Seven futures for Longhill",
       subtitle = "Median and full range across the swept parameter space.\nThe dashed line is a school exactly full — on an admission number that differs between options.",
       caption = "Brightopia, open-data specification.") +
  theme_bh(11) +
  theme(strip.text = element_text(size = 8.5))
Figure 43: How full Longhill would be under seven configurations, across the projection period. Bands show the range across the swept parameter space; 100% is a school exactly at its admission number for that configuration, and the admission number differs between them.

By 2030 the configurations separate clearly. The status quo (A. Today: Ovingdean, PAN 210, current catchments) fills to a median of 75%, and draws about 125 children.

4 of the seven options fill completely at the median. That sounds decisive and is not, because there are two ways to fill a school and only one of them is worth having: lower the admission number until the school is full, or raise the number of children it draws. Fill rate alone cannot tell them apart — a school is trivially full at a PAN of one — which is why natural recruitment sits beside it here.

On that test the strongest are E. Shrink + move + redrawn catchments and G. Elm Grove, PAN 120, redrawn catchments, which fill and draw 148 children: 23 more than the status quo, from the same cohort. They tie because they put the school on the same site behind the same catchments, and differ only in the admission number — which is the lever this section has already argued is largely spent.

On the open scenario suite that reads as an argument that the instruments compose: moving the school changes its position in the travel network, reducing its admission number changes what “full” means, redrawing catchments changes which children are steered towards it, and together they reach a configuration that works. But that suite runs one catchment term for the whole city, with attractiveness taken straight from preferences per place. The full model of section 7 does not, and for this question the difference is decisive.

9.0.1 The same options under the full model

Show code
rl5$central %>%
  select(config, entry_year, natural) %>%
  mutate(natural = fmt_n(natural)) %>%
  pivot_wider(names_from = entry_year, values_from = natural) %>%
  rename(Option = config) %>%
  knitr::kable(align = "lrrr")
Table 49: Longhill’s natural recruitment under each option in the full model, M5: catchment terms fitted to what each catchment’s children ask for, attractiveness balanced to first preferences. Children drawn with the school’s own admission number unbinding, so options that differ only in that number draw the same children.
Option 2026 2030 2035
A. Today: Ovingdean, PAN 210, current catchments 87 71 64
B. Shrink only: Ovingdean, PAN 150 87 71 64
C. Move only: Elm Grove, PAN 210 92 75 67
D. Shrink + move: Elm Grove, PAN 150 92 75 67
E. Shrink + move + redrawn catchments 110 88 80
F. Redrawn catchments, Longhill stays at Ovingdean 76 58 52
G. Elm Grove, PAN 120, redrawn catchments 110 88 80

The catchment term is strongest in exactly the two places a move trades between. Longhill’s own families, at \(\gamma\) = 2.3, are the ones the move takes the school away from. Stringer / Varndean’s, at 2.6, are most of the families near Elm Grove it moves towards — and they are the most attached to their own schools of any in the city.

  • Moving on its own costs Longhill about 3 children a year. It draws 71 at Ovingdean in 2030 and 75 at Elm Grove behind today’s catchments.
  • Moving with redrawn catchments gains children: 88 in 2030 against 71 staying put. The redraw wins back what the move lost and a little more.
  • Redrawing without moving costs children too, 58 in 2030, because the designed map passes some of Longhill’s hinterland to its neighbours.
  • A move sends more of Longhill’s own catchment out of the city: 35 to the East Sussex schools in 2030 at Ovingdean, most of them to Priory, against 41 at Elm Grove behind today’s catchments.
  • Nothing draws 150 children after 2026. By 2035 the best option draws 80.
Show code
rl5$sens %>%
  mutate(natural = fmt_n(natural), Option = unname(rl5$labels[id])) %>%
  select(Option, Assumption = variant, entry_year, natural) %>%
  pivot_wider(names_from = entry_year, values_from = natural) %>%
  knitr::kable(align = "llrrr")
Table 50: How much the answer rests on two assumptions: the fitted catchment terms halved, and no paired-catchment families who would take only one of the two schools.
Option Assumption 2026 2030 2035
Today: Ovingdean, PAN 210, current catchments Catchment terms halved 57 45 39
Today: Ovingdean, PAN 210, current catchments No paired-school exclusivity 86 70 64
Move only: Elm Grove, PAN 210 Catchment terms halved 71 56 49
Move only: Elm Grove, PAN 210 No paired-school exclusivity 90 73 66
Shrink + move: Elm Grove, PAN 150 Catchment terms halved 71 56 49
Shrink + move: Elm Grove, PAN 150 No paired-school exclusivity 90 73 66
Shrink + move + redrawn catchments Catchment terms halved 88 65 58
Shrink + move + redrawn catchments No paired-school exclusivity 108 87 80
Elm Grove, PAN 120, redrawn catchments Catchment terms halved 88 65 58
Elm Grove, PAN 120, redrawn catchments No paired-school exclusivity 108 87 80

A move looks better the less families follow their catchment, and the evidence is that they follow it closely. Halve the catchment terms and the site stops mattering for a move on its own — 45 at Ovingdean against 56 at Elm Grove in 2030 — and a move with a redraw gains. At the strength families actually show, it does not.

So the instruments do not compose the way the open suite suggests. Size is the one lever that works on every model here, because it changes what “full” means rather than who comes — and under the full model even 150 places is more than Longhill draws after 2026. Relocation’s case is real, but it is the one section 8 makes on behalf of the city, that deprived children in the east would stop travelling further than everyone else (Section 8.5.1). It is not a case that the move would refill the school.

ImportantRead these figures with the models’ limits in mind

The figure and the first comparisons in this section come from the open scenario suite, which runs one catchment term for the whole city and is more optimistic about relocation than the full model. The full model’s figures rest on the assumptions stated in Section 7.8.3: distance decay is set rather than fitted, and the catchment terms carry over to a redrawn map at the strength families follow the map they have. Whether families would follow a new boundary as closely as the old one is the question that decides relocation, and no published data answers it. Nothing in this section is a prediction of a roll.

9.1 Re-opening CoMArt

CoMArt closed in 2005, and the city’s catchment system is partly the shape of that closure (Section 2). The east of Brighton now has two secondary schools, Longhill at Ovingdean and Brighton Aldridge on the Lewes Road, both well short of their admission numbers. A different answer to the east’s problem from moving or shrinking Longhill is to put a school back where CoMArt stood: three small schools in the east rather than two half-empty ones.

The scenario, run on the full model (Section 7.8.3) under the council’s 2026/27 priorities: CoMArt open again on its East Brighton site with 150 places, sharing Longhill’s catchment — the site is inside it on the map in force and on the one before it — with Longhill staying at Ovingdean, and Brighton Aldridge and Longhill both cut to 150. CoMArt’s journeys are routed on the same walk and bus network as every other school, on the same day and settings; re-routing Brighton Aldridge the same way reproduces its existing times to within 0.0 minutes. There are no preferences for a school that does not exist, so CoMArt starts as attractive as Brighton Aldridge, the nearest thing to it.

Show code
cms$runs %>%
  filter(year == 2026, rule == "priorities") %>%
  mutate(id = factor(id, names(cms$configs))) %>%
  arrange(id) %>%
  transmute(Scenario = label,
            Gorard = sprintf("%.3f", gorard),
            `Mean journey` = sprintf("%.1f min", mean_min),
            `Deprived children's journey` = sprintf("%.1f min", dep_min),
            `Gap to other children` = sprintf("%.1f min", dep_gap),
            CoMArt = ifelse(is.na(intake_comart), "---", fmt_n(intake_comart)),
            Longhill = fmt_n(intake_lh), `Brighton Aldridge` = fmt_n(intake_baca),
            Stringer = sprintf("%s / %s", fmt_n(intake_ds), fmt_n(pan_ds)),
            `Empty places` = fmt_n(empty)) %>%
  knitr::kable(align = "lrrrrrrrrr")
Table 51: The scenarios, under the council’s priorities in 2026. Gorard’s index is across the city’s schools’ modelled intakes; journeys are to city schools. Stringer’s intake is shown against its admission number in that scenario.
Scenario Gorard Mean journey Deprived children’s journey Gap to other children CoMArt Longhill Brighton Aldridge Stringer Empty places
Today 0.117 29.5 min 32.0 min 3.4 min — 90 91 330 / 330 299
Brighton Aldridge and Longhill at 150 0.117 29.5 min 32.0 min 3.4 min — 90 91 330 / 330 209
CoMArt open at 150, Brighton Aldridge and Longhill at 150 0.122 28.5 min 30.0 min 2.0 min 150 60 80 298 / 330 345
… and Dorothy Stringer at 300 0.122 28.5 min 30.0 min 2.0 min 150 60 80 298 / 300 315
… and Dorothy Stringer at 270 0.123 28.4 min 30.0 min 2.2 min 150 63 83 270 / 270 285
CoMArt open at 180, Brighton Aldridge and Longhill at 150 0.123 28.4 min 29.8 min 1.9 min 154 60 80 296 / 330 375

Shrinking the two eastern schools on its own does nothing. Neither fills, so cutting Brighton Aldridge and Longhill to 150 leaves every child where they were and only removes empty places.

Opening CoMArt shortens the journeys, and most for the children who travel furthest. The mean journey falls from 29.5 to 28.5 minutes. For children from the three most deprived deciles it falls from 32.0 to 30.0, and the gap between them and everyone else closes from 3.4 minutes to 2.0. CoMArt fills its 150 places. Fewer children leave the city: 37 go to the East Sussex schools, against 51 today, most of them from Longhill’s catchment.

Its effect on how mixed intakes are is small. Counting disadvantaged pupils as calibrated in Section 11, Gorard’s index goes from 0.117 to 0.122 in 2026, and from 0.109 to 0.108 in 2030. A school that recruits locally takes its neighbourhood with it: 36% of CoMArt’s intake is disadvantaged, against 27% across the city. But Longhill and Brighton Aldridge, the two most disadvantaged intakes, are already well above that, so a third school in the east between them does little to the city’s index. Brighton Aldridge goes from 48% to 50%, Longhill from 49% to 51%. What CoMArt changes is access: it shortens deprived children’s journeys without, on this evidence, concentrating them much more than today’s system already does.

Show code
cms$schools %>%
  filter(id %in% c("today", "comart")) %>%
  select(id, short, pan, intake, dep_share) %>%
  tidyr::pivot_wider(names_from = id, values_from = c(pan, intake, dep_share)) %>%
  arrange(desc(dep_share_comart)) %>%
  transmute(School = short,
            `Places today` = ifelse(is.na(pan_today), "---", fmt_n(pan_today)),
            `Intake today` = ifelse(is.na(intake_today), "---", fmt_n(intake_today)),
            `With CoMArt` = sprintf("%s / %s", fmt_n(intake_comart), fmt_n(pan_comart)),
            `Deprived share today` = ifelse(is.na(dep_share_today), "---", fmt_pct(100 * dep_share_today, 0)),
            `With CoMArt ` = fmt_pct(100 * dep_share_comart, 0)) %>%
  knitr::kable(align = "lrrrrr")
Table 52: Each school’s modelled intake and its share of disadvantaged pupils (calibrated to the published shares), today and with CoMArt open at 150 (Brighton Aldridge and Longhill at 150), council priorities, 2026.
School Places today Intake today With CoMArt Deprived share today With CoMArt
Longhill 210 90 60 / 150 49% 51%
Brighton Aldridge 180 91 80 / 150 48% 50%
CoMArt — — 150 / 150 — 36%
Hove Park 180 168 149 / 180 35% 35%
Varndean 300 300 300 / 300 30% 29%
Portslade Aldridge 220 153 144 / 220 28% 28%
Patcham 225 215 196 / 225 27% 25%
Dorothy Stringer 330 330 298 / 330 25% 24%
Blatchington Mill 330 330 313 / 330 22% 23%
Cardinal Newman 360 360 360 / 360 17% 17%
King’s 180 180 180 / 180 16% 17%

It would let Dorothy Stringer shrink. Today Stringer fills its 330 places. With CoMArt open it takes 298, because some of the East Brighton families who went there now go to CoMArt. Cut to 300 it takes 298, and cut to 270 it fills. Varndean still fills. So the central school that would like fewer places could have them without the city being short: Stringer’s lost places are more than covered by CoMArt’s. By 2030 the question answers itself either way — with CoMArt open, Stringer takes 218 children at 330 places.

Longhill is the school that pays. At 150 places it takes 60 children in 2026, against 90 today, and 49 in 2030. CoMArt sits between Longhill and the rest of the city, in Longhill’s own catchment, and takes families Longhill would otherwise draw. Three small schools in the east is, on this model, CoMArt full, Brighton Aldridge 53% full and Longhill 40% full.

WarningWhat this scenario borrows

CoMArt’s attractiveness is assumed. Given Longhill’s instead of Brighton Aldridge’s, CoMArt takes only 53 children, Longhill 79, and Gorard’s index is 0.119, against 0.117 today. At the city median it fills and the index is 0.118. Both the size and the direction of CoMArt’s effect on segregation depend on this assumption; its effect on journeys does not.

So is the catchment term. Families in Longhill’s catchment are assumed to follow a catchment with two schools in it as closely as they follow the one they have. CoMArt is run as a community school under the council’s priorities, which makes Longhill’s catchment a two-school one and takes priority 6 away from its children. It has no finance or attainment figures, so it is not in Section 6. The same scenario, at any places, attractiveness or map, is in the simulator.

10 What the council needs to do

10.1 Decisions

Decide about Longhill on a deadline, not on a consultation cycle. The reserve supports about 3.7 more years. A decision deferred past that point is a decision taken by insolvency rather than by the council.

Treat the instruments as a package. Section 8 shows they compose. Section 3 shows the cohort keeps falling regardless. Adjusting a single admission number and waiting to see what happens will consume years the finances do not have.

Stop pointing families at Attainment 8. The council publishes the admissions guide, and it tells parents to look at each school’s “National Curriculum test results, public exam results” (2027 to 2028). Section 5 shows families track the headline score at 84% and the value-added measure at 20%. Publishing a contextualised measure alongside the headline would cost nothing and would begin to unwind a self-fulfilling loop the authority currently sustains.

Treat transport as an admissions instrument. Section 4 found 29 neighbourhoods that can reach no school place inside 30 minutes, concentrated in the more deprived third of the city. A bus timetable can widen real choice without taking anything from anyone — which is more than can be said for any boundary change.

10.2 Questions raised in this document

Numbered questions appear in the sections above where the evidence raises them. They are gathered here so they can be put as a set. None requires new research; each is answerable from records the council already holds.

  1. Why is the catchment forecast wrong in Longhill and in Hove Park / Blatchington Mill, in opposite directions? (§3.4) In 2026 Longhill was forecast 162 and received 81, while Hove Park / Blatchington Mill was 42 places under. The second has a candidate explanation in the neighbouring PACA over-forecast; the first does not.
  2. Which neighbourhoods did the priority-6 places go to, and what did they do to the social mix of the community schools’ intakes?
    1. In the full model it makes almost no difference to how mixed intakes are, and who uses it varies by catchment. The council holds the offers and the home postcodes to test both.

10.3 Data the council could simply publish

Every “this cannot be done on published data” note in this document resolves to a small number of releases. None requires new collection; all are aggregates of data the council already holds.

# What What it would settle
1 Home LSOA of every applicant, by year and allocated school Calibrate \(\beta\); replace every band in section 8 with an estimate
2 Full preference ordering, anonymised, by year Turn section 5’s ten-point association into a discrete-choice model
3 Allocated school by home LSOA, including schools outside the city Measure the actual outflow the council’s forecasts assume
4 Criterion under which each place was allocated Establish directly which criteria bind, rather than inferring it
5 Children allocated a school they did not name, by school, year and home catchment Test the council’s stated “nearest school with places” practice, and complete each school’s intake: the published offer counts are counted by preference rank, so a child placed at a school they did not name does not appear in them
6 Journey actually made, or at least distance, per allocation Separate potential accessibility from realised accessibility
7 Offers by admission criterion for every school, every year The admissions guide gives the priority breakdown only for the schools that rationed places; the rest would show how the tiered ceiling behaves where it barely binds
8 Offers to children living outside the city, by school The model contains Brighton & Hove children only. Schools that admit across a wider area — the faith schools above all — fill some places the model cannot see
9 The catchment preference table for each round separately, not pooled Let each school’s attractiveness be fitted year by year, so a change such as a new Ofsted judgement shows up when it happens rather than being averaged away
10 In each paired catchment, how many applicants named one school of the pair, both, or neither Fit the share who would take only one of the pair each year, rather than holding it fixed

The last four follow from where the model’s remaining error sits. Its intakes for September 2026 match the council’s offers closely at most schools and miss at two: it gives Brighton Aldridge too few children and Hove Park too many (model guide). The published factsheets show why both are hard for a model fitted to a pooled table. Brighton Aldridge’s first preferences rose from 83 to 128 in the first round after its April 2025 Ofsted judgement, and Hove Park’s second preferences fell from 242 to 171 in the same round. A model whose attractiveness is an average of earlier rounds cannot follow a change that happens in one. Releases 9 and 10 would let it, and releases 5, 7 and 8 would let the model be checked against every child placed rather than only those offered a school they named.

LSOAs average about 1,500 residents and 650 households. A cohort-year count at LSOA level is not disclosive in the way a postcode-level count would be, and the council already publishes LSOA-level material elsewhere.

10.3.1 What one small table already settled

The case for these releases is not hypothetical, because one of them has in effect already happened. Appendix 1 of the council’s Educational Equity Framework, prepared for the People Overview & Scrutiny Committee, counts Key Stage 2 and Key Stage 4 pupils by home ward and by middle super output area, taken from the January 2025 school census. It was published to describe attainment, not pupil numbers, and the counts sit in it as a by-product.

Those counts are the first external check this model’s origins have ever had. Every other test in this document tests where the model sends children; nothing tested how many it starts with in each neighbourhood, and that is built from ONS ward estimates and a single state-sector share for the whole city. Set the council’s Key Stage 2 count against modelled intakes into city schools, ward by ward, and the geography holds: the two correlate at 0.96. The flat share does not. It implies a share of each ward’s children in city schools running from about 0.66 in Round Hill to about 1.21 in Woodingdean, against the 0.85 the model applies everywhere. Rebuilding the model’s origins on per-ward shares moves its intakes closer to the offers the city’s schools actually made in September 2026, on every measure of fit, although the offers play no part in deriving the shares. The model guide sets out the comparison in full; the results in this document still use the flat share.

Two things follow. The first is a correction the council could make to its own place planning: how many of an area’s children take a city state school place varies far more across the city than a single rate allows, and the variation is large enough to change which schools look full. The second is the general point. A table published for another purpose entirely, containing nothing more sensitive than counts of pupils by ward, answered a question that had been stated as unanswerable. The ten releases above are of the same kind.

10.4 Why now

The cohort projections in section 3 are not forecasts. Those children are in the city’s primary schools today. The financial position in section 6 has a horizon of about 3.7 years. The decisions in front of the council will shape the school system for the next fifteen to twenty years, and they are being taken without a picture of the whole system.

This document is an attempt to supply as much of that picture as published data allows. It stops well short of what the council’s own records would support — and the offer to do that work, properly and in the open, stands.

11 Weighing the priorities

The council is trying to keep a financially viable system of secondary schools, and it has more than one thing it wants from it. This section takes three priorities in turn — keeping Longhill open; better access for disadvantaged children, and less segregation; and, a parent’s priority more than a council one, children at a school in their own catchment, near home, and not displaced — and then weighs them against each other.

It is a recommendation from a model, not from the council’s records, and it carries every limit stated in section 7. It is also written knowing what the council can and cannot do. It sets the admission numbers of the six community schools, the catchment map, Longhill’s site and the oversubscription priorities. It cannot set the numbers at Cardinal Newman, King’s, Brighton Aldridge or Portslade Aldridge — but those schools take a third of the city’s children, and where the evidence says the rest of the system would be better for their taking fewer, this section says so.

ImportantHow segregation is measured here, and a check the first version failed
Show code
val <- bh_data("council_options.rds")$dis_fit %>%
  mutate(short = vapply(name, short_sch, character(1)),
         nb = 100 * neighbourhood, pub = 100 * published, cal = 100 * modelled,
         nb_side = ifelse(nb >= weighted.mean(nb, intake), "above", "below"),
         pub_side = ifelse(pub >= weighted.mean(pub, intake), "above", "below"))
val_r <- cor(val$nb, val$pub)
val_r_nobaca <- with(val %>% filter(short != "Brighton Aldridge"), cor(nb, pub))
val_same <- sum(val$nb_side == val$pub_side)
val_of <- function(s, col) val[[col]][val$short == s]
val %>%
  arrange(desc(pub)) %>%
  transmute(School = short,
            `First version: by neighbourhood` = sprintf("%s (%s)", fmt_pct(nb, 0), nb_side),
            `Published: disadvantaged pupils` = sprintf("%s (%s)", fmt_pct(pub, 0), pub_side),
            `Year 7 offers, 2026: free school meals` = ifelse(is.na(year7_fsm), "---", fmt_pct(100 * year7_fsm, 0)),
            `Model, Year 7 2026` = fmt_pct(cal, 0),
            `Pull: Year 7 offers` = sprintf("%.2f", lambda),
            `Pull: before the change` = sprintf("%.2f", lambda_pre),
            `Pull: today's map, whole school` = sprintf("%.2f", lambda_today_map)) %>%
  knitr::kable(align = "lrrrrrrr")
Each school’s share of disadvantaged pupils: as the first version of the model measured it, by the neighbourhoods its children come from; as the Department for Education publishes it for the whole school (the mean of 2022-23 to 2024-25, every cohort admitted before the 2024 map and the new priorities); the share of each community school’s September 2026 Year 7 offers that went to children eligible for free school meals (council allocation factsheet); and the model’s Year 7 share in 2026, which reproduces the offers for the community schools and the published share for the rest. The last three columns are each school’s pull on disadvantaged children fitted three ways: to the Year 7 offers (the model’s measure), to the published shares on the pre-2024 map and rules, and to the published shares on today’s map. Ordered by the published share.
School First version: by neighbourhood Published: disadvantaged pupils Year 7 offers, 2026: free school meals Model, Year 7 2026 Pull: Year 7 offers Pull: before the change Pull: today’s map, whole school
Brighton Aldridge 68% (above) 48% (above) — 48% 0.88 1.16 1.13
Longhill 22% (below) 34% (above) 49% 49% 4.53 1.31 1.89
Hove Park 23% (below) 32% (above) 35% 35% 1.33 1.68 1.68
Portslade Aldridge 39% (above) 28% (above) — 28% 0.72 1.18 1.18
Varndean 31% (above) 23% (above) 30% 30% 0.93 0.90 0.71
Patcham 22% (below) 22% (below) 27% 27% 1.14 1.05 1.07
Dorothy Stringer 26% (below) 19% (below) 25% 25% 0.85 0.76 0.68
Cardinal Newman 20% (below) 17% (below) — 17% 0.60 0.75 0.74
Blatchington Mill 13% (below) 17% (below) 22% 22% 0.98 0.97 0.97
King’s 32% (above) 16% (below) — 16% 0.49 0.64 0.63

The first version of this section measured how mixed a school’s intake is by the neighbourhoods its children come from. Checked against the share of disadvantaged pupils each school actually has, as the Department for Education publishes it, that measure put the schools in the wrong order. The two correlate at 0.74, and only because of Brighton Aldridge: without it the correlation is 0.17, and only 7 of the 10 schools are on the same side of the city average on both. Longhill and Hove Park, which the neighbourhood measure put below the average, have among the highest disadvantaged shares in the city — 34% and 32% — and Varndean, which it put well above, has about the average.

The gap is families sorting within neighbourhoods: in a mixed neighbourhood, the disadvantaged families and the better-off ones do not choose the same schools, and a neighbourhood measure cannot see it. That sorting is what segregation is made of, so the model has been recalibrated to it. Each neighbourhood’s disadvantaged children are placed across the schools its children attend, weighted by each school’s pull on disadvantaged children, and those pulls are fitted to what each school actually admits.

The published shares lag the policy. They average Years 7 to 11 over 2022-23 to 2024-25, and every one of those cohorts was admitted before September 2025, under the pre-2024 map with no free school meals priority and no priority 6. Fitted to them on today’s map, the pulls absorb the policy change itself. Fitted on the old map and rules, they still miss what the new rules do. So for the six community schools the target is the first intake admitted under today’s map and priorities: the share of each school’s September 2026 offers that went to children eligible for free school meals, which the council publishes in its allocation factsheet. The four academies and faith schools do not publish it and their arrangements did not change in 2025, so they keep their published whole-school shares. Free school meal eligibility is a narrower count than the Pupil Premium measure, so if anything this understates the community schools’ disadvantaged intakes. The pulls are then held fixed while maps, admission numbers and schools change. Every segregation figure in this section, in the priority-6 subsection (1) and in CoMArt’s (Section 9.1) is on that basis. The intake figures in section 8 come from a separate allocation model the recalibration does not cover, and remain withdrawn.

Several results change. On this measure returning Whitehawk to Longhill’s catchment raises segregation — the box under Priority 2 sets out why earlier versions said otherwise, and how far the finding holds. Priority 6 makes no measurable difference. Closing Longhill, removing the oversubscription priorities and trimming Cardinal Newman and King’s lower segregation; the power diagram, Elm Grove and the packages raise it. Several options differ by only a few thousandths on the index, which is inside what a change of measure has moved, so only the larger differences should be read as findings. The journey, displacement, catchment and Longhill results do not rest on this measure and are unchanged.

11.1 How the options were scored

Every option below was run on the simulator’s model — the full model of section 7, with the council’s 2026/27 priorities, the East Sussex schools children leave for, and refused children re-offered by second preference — in 2026, 2030 and 2035. Each is scored on all three priorities at once: Longhill’s intake and finances, and the community schools’ finances; Gorard’s segregation index and how much further deprived children travel than everyone else; and how many children are displaced, how many attend a school in their own catchment, and how long the longest journeys are. The same options, and any variation on them, can be run in the simulator.

One new map is in them. The 2024 catchment change moved the neighbourhoods around CoMArt’s old site in Whitehawk — six zones, about 60 children a year — out of Longhill’s catchment into Stringer / Varndean’s, while moving others further out the other way. “Whitehawk back to Longhill” is today’s map with only those neighbourhoods returned.

Show code
show <- c("today", "lh150", "central", "whitehawk", "whitehawk_central", "elm",
          "lh_wanted", "cn300", "cn270_kings", "academies", "p6_off", "p6_20",
          "equal", "power", "comart", "package_d", "package_e", "package_f", "close_lh")
cop$runs %>%
  filter(year == 2030, id %in% show) %>%
  mutate(id = factor(id, show)) %>% arrange(id) %>%
  transmute(Option = label,
            Longhill = d_or(lh_intake, function(x) fmt_n(x)),
            `Community schools' balance` = gbp_m(comm_gap),
            Gorard = sprintf("%.3f", gorard),
            `Deprived-journey gap` = sprintf("%.1f min", ifelse(abs(dep_gap) < 0.05, 0, dep_gap)),
            `Longest journeys (90th %ile)` = sprintf("%.0f min", p90_min),
            Displaced = fmt_n(displaced),
            `At a catchment school` = fmt_pct(100 * in_catchment, 0)) %>%
  knitr::kable(align = "lrrrrrrr")
Table 53: The options in 2030, under the council’s priorities. Community schools’ balance is the steady-state annual balance across the six schools the council runs (section 6). Gorard is across all city schools’ intakes, counting disadvantaged pupils calibrated to each school’s published share (see the box at the head of this section). The deprived-journey gap is the extra minutes children from the three most deprived deciles travel. Displaced children would have taken a place in their own catchment and could not. Where an option cuts a school’s admission number and that school is no longer full in 2030, the cut changes nothing that year.
Option Longhill Community schools’ balance Gorard Deprived-journey gap Longest journeys (90th %ile) Displaced At a catchment school
Today 71 −£3.5m 0.109 3.9 min 55 min 3 58%
Longhill cut to 150 71 −£3.5m 0.109 3.9 min 55 min 3 58%
Longhill 150; Stringer 300, Varndean 270, Blatchington Mill 300 71 −£3.5m 0.109 3.9 min 55 min 5 58%
Whitehawk back in Longhill’s catchment 85 −£3.5m 0.111 3.5 min 55 min 0 57%
Whitehawk back to Longhill; Longhill 150; Stringer 300, Varndean 270, Blatchington Mill 300 85 −£3.5m 0.114 3.5 min 55 min 3 57%
Longhill at Elm Grove at 150, catchments redrawn 86 −£3.7m 0.142 0.4 min 55 min 3 58%
Longhill at 150, and 1.5 times as attractive 89 −£3.4m 0.111 3.9 min 54 min 2 59%
Cardinal Newman cut to 300 73 −£3.0m 0.105 3.9 min 55 min 3 60%
Cardinal Newman cut to 270, King’s to 150 76 −£2.5m 0.100 4.0 min 54 min 3 62%
Brighton Aldridge cut to 150, Portslade Aldridge to 180 71 −£3.5m 0.109 3.9 min 55 min 3 58%
No priority 6 72 −£3.5m 0.108 3.9 min 54 min 1 58%
Priority 6 at 20% 67 −£3.4m 0.111 3.8 min 55 min 5 58%
No oversubscription priorities: an equal chance 68 −£3.4m 0.109 3.8 min 55 min 5 57%
Power diagram catchments 68 −£3.7m 0.108 4.1 min 55 min 4 58%
CoMArt open at 150; Brighton Aldridge and Longhill at 150 49 −£4.3m 0.108 2.7 min 52 min 1 60%
Whitehawk back to Longhill; Longhill 150; Stringer 300, Varndean 270, Blatchington Mill 300; no priority 6 87 −£3.5m 0.112 3.5 min 55 min 2 57%
Package D, with Cardinal Newman at 300 and Brighton Aldridge at 150 90 −£3.0m 0.111 3.5 min 55 min 2 59%
Longhill at Elm Grove at 150, catchments redrawn; Cardinal Newman 300, King’s 150, Brighton Aldridge 150 91 −£3.0m 0.137 0.5 min 54 min 3 61%
Longhill closed — −£2.2m 0.097 4.5 min 60 min 3 56%

11.2 Priority 1: keeping Longhill open

No option tested makes Longhill pay its way. On section 6’s funding assumptions Longhill breaks even at about 162 children a year; each child is worth about £9,300 a year to its balance. Today it takes 90 in 2026 and 63 by 2035. The best of the options below take it to around a hundred for a few years and to the eighties by 2035. Keeping Longhill open is therefore a decision to run a smaller school at a cost — to plan its staffing and curriculum for about a hundred children a year rather than to wait for 150 — and the council should take it as that, openly, rather than as a bet on recovery.

Cutting its admission number removes empty places; it does not add children. At 150 or 120 places Longhill takes exactly the children it takes at 210, and its balance is unchanged. The cut is still worth making — 60 empty places a year gone from the community schools — but it is housekeeping, not rescue.

The levers that do move children, ranked by what they add in 2030:

  • Whitehawk back in Longhill’s catchment adds 14 children (103 in 2026, 76 in 2035, against 90 and 63), and keeps most of that gain as the cohort falls. It barely changes journeys, and Priority 3 shows it lowers displacement. It raises segregation, by more than any other option short of moving a school (Priority 2).
  • Moving to Elm Grove with redrawn catchments adds 15 in 2030 and 16 in 2026, more than any other single change the council controls, and the map drawn round the new site puts 4 of the 6 Whitehawk neighbourhoods back in Longhill’s catchment. It needs a new site, which is not costed here, and it raises segregation (Priority 2).
  • Making Longhill half as attractive again adds 18. On section 5.4’s fit that is about 3.6 points of Attainment 8, which a school with a falling roll is poorly placed to deliver.
  • Cardinal Newman and King’s taking fewer children — Cardinal Newman at 270 and King’s at 150 — adds 5 in 2030 and 16 in 2026, and does more for the community schools’ finances than any other single change the council does not control: their combined balance improves by £0.8m a year by 2035. Cardinal Newman at 300 alone does about half of that.
  • Trimming the full central schools — Stringer to 300, Varndean to 270, Blatchington Mill to 300 — adds 14 in 2026 and nothing by 2030, because by then the falling cohort has left those schools with room of their own. It is a lever for the next two or three rounds only, and it lengthens journeys for the children it moves unless it goes with a redraw that gives them a nearer school.

Asking Brighton Aldridge and Portslade Aldridge to shrink does nothing for Longhill. Neither fills, so a lower number only removes their own empty places.

11.3 Priority 2: access and segregation

These are two different things, and the options that help one often hurt the other.

Access — how much further deprived children travel than everyone else — is improved most by putting a school where they live. With Longhill at Elm Grove the gap falls from 3.9 to 0.4 minutes; with CoMArt open, to 2.7. Nothing else in the table moves it by more than half a minute.

For Whitehawk the move does what the redraw cannot. With Longhill at Elm Grove, Whitehawk’s children travel 30 minutes on average in 2026 against 57 today, and its disadvantaged children 23 against 56. Returning Whitehawk to Longhill’s catchment with the school at Ovingdean leaves its disadvantaged children at 52 minutes, because Ovingdean is no nearer to them than Varndean is.

Segregation is lowest where the intakes are mixed, and a school placed among deprived neighbourhoods recruits from them: Gorard’s index rises from 0.109 to 0.142 with Longhill at Elm Grove and to 0.108 with CoMArt open (Section 9.1).

Returning Whitehawk to Longhill’s catchment raises segregation. In 2030 the index is 0.109 today and 0.111 with the redraw; in 2026, 0.117 and 0.126. That is the largest rise of any option the council can take without moving or opening a school. Earlier versions of this section found first that the redraw lowered segregation and then that it barely moved it; the box below sets out what changed, and how far the finding now holds.

NoteDoes returning Whitehawk to Longhill change segregation?

Who lives there. The 2024 catchment change moved 6 small areas around CoMArt’s old site in Whitehawk out of Longhill’s catchment and into Stringer / Varndean’s. About 78 children a year live in them, and 75% of them are disadvantaged — against 27% across the city, 23% in the rest of Stringer / Varndean’s catchment, and 26% in Longhill’s.

Where their children go. Today 14 go to Varndean and 24 to Stringer. Returned to Longhill’s catchment, 17 go to Longhill — in the model almost all of them disadvantaged ones, for the reason set out below — and Cardinal Newman takes 18 rather than 14. For the children who go to Longhill the journey is about 34 minutes, against 49 to Varndean.

Show code
whx$schools %>%
  filter(year == 2026, short %in% c("Longhill", "Varndean", "Dorothy Stringer")) %>%
  select(short, map, share, contrib) %>%
  tidyr::pivot_wider(names_from = map, values_from = c(share, contrib)) %>%
  transmute(School = short,
            `Disadvantaged, today` = fmt_pct(100 * share_today, 1),
            `With Whitehawk back` = fmt_pct(100 * share_whitehawk, 1),
            `Contribution to the index, today` = sprintf("%.4f", contrib_today),
            `With Whitehawk back ` = sprintf("%.4f", contrib_whitehawk)) %>%
  knitr::kable(align = "lrrrr")
Table 54: The three schools the Whitehawk redraw moves, 2026, council priorities: each school’s share of disadvantaged pupils, and its contribution to Gorard’s index (half the gap between its share of the city’s disadvantaged pupils and its share of all pupils).
School Disadvantaged, today With Whitehawk back Contribution to the index, today With Whitehawk back
Dorothy Stringer 25.5% 23.1% 0.0030 0.0093
Longhill 49.4% 55.9% 0.0175 0.0258
Varndean 30.0% 28.3% 0.0089 0.0047

What it does to the intakes. Across the city 26.5% of pupils are disadvantaged. Longhill’s intake, already above that at 49.4%, rises to 55.9%, which adds to the index. Varndean’s, also above it today at 30.0%, falls to 28.3%, passing below it. Stringer’s goes from 25.5% to 23.1%, further below it again. All three schools end further from the city’s share than they started, and Gorard’s index measures exactly that: it is 0.117 today and 0.126 with the redraw in 2026, 0.109 and 0.111 in 2030.

Why earlier versions said otherwise.

Show code
whx$measures %>%
  filter(year == 2026) %>%
  select(measure, map, gorard, longhill_share, varndean_share) %>%
  tidyr::pivot_wider(names_from = map, values_from = c(gorard, longhill_share, varndean_share)) %>%
  transmute(`Disadvantaged pupils counted` = measure,
            `Index, today` = sprintf("%.3f", gorard_today),
            `With Whitehawk back` = sprintf("%.3f", gorard_whitehawk),
            `Longhill` = sprintf("%s → %s", fmt_pct(100 * longhill_share_today, 0), fmt_pct(100 * longhill_share_whitehawk, 0)),
            `Varndean` = sprintf("%s → %s", fmt_pct(100 * varndean_share_today, 0), fmt_pct(100 * varndean_share_whitehawk, 0))) %>%
  knitr::kable(align = "lrrrr")
Table 55: Gorard’s index, and Longhill’s and Varndean’s shares of disadvantaged pupils, today and with Whitehawk returned to Longhill, 2026, council priorities, on each of the four ways this document has counted disadvantaged pupils. The last is the model’s measure.
Disadvantaged pupils counted Index, today With Whitehawk back Longhill Varndean
By neighbourhood 0.145 0.149 22% → 32% 31% → 28%
Whole-school shares, fitted on today’s map 0.117 0.136 33% → 42% 23% → 21%
Whole-school shares, fitted before the policy change 0.121 0.130 27% → 35% 25% → 24%
Year 7 offers, 2026 (the model’s measure) 0.117 0.126 49% → 56% 30% → 28%

Scored by the neighbourhoods their children come from, Varndean looked the more deprived school and Longhill the less, so moving Whitehawk looked like evening them out. The published figures put them the other way round, and the second version fitted each school’s pull on disadvantaged children to those figures — but on today’s map. The published figures are whole-school averages for cohorts admitted before the 2024 map and the new priorities, when Whitehawk was in Longhill’s catchment. Fitted on today’s map, Longhill’s pull (1.89) stood in for Whitehawk children it no longer admits, and Varndean’s (0.71) held down the ones it now does; moving Whitehawk then looked like concentrating disadvantage at Longhill. Fitted on the map and rules those cohorts were admitted under, the pulls are 1.31 and 0.90, and the redraw barely moves the index — but that fit still misses what the new rules do. Fitted to the first Year 7 offers under today’s map and priorities, the model’s measure, Longhill’s pull is far stronger, 4.53, because 49% of its September 2026 offers went to children eligible for free school meals; Varndean’s is 0.93. On that measure the redraw raises the index a little, because the model sends Longhill almost only the disadvantaged Whitehawk children: 17 of 17.

How firm that is. As fitted, the redraw changes the index by +0.009; at half the fitted pull by +0.009; with no pull by +0.004. It rises at the fitted pull and still rises at half of it, and only reverses when the pull is removed altogether — which is the version that no longer reproduces the council’s offers. The direction is as firm as the fitted pull is, and the size of it is not. Longhill’s pull rests on a single year of offers at a school that made fewer than a hundred of them on offer day, and it implies that almost every Whitehawk family choosing Longhill would be disadvantaged. A second year of Year 7 offers would test it; the published whole-school figures will not show the 2024 map in full until every year group has been admitted under it, in 2029-30.

What that leaves. The redraw is a genuine trade. Against the rise in the index sit a shorter journey for the children who would take a Longhill place, 14 more children a year for Longhill by 2030, and less displacement. For comparison, the whole pre-2024 map scores 0.123 in 2026, against 0.117 today. And a more or less mixed intake is not the same as better or worse results for the children in it: most of the difference between schools’ headline scores reflects who they teach rather than what they add (Section 2.4).

Priority 6 makes almost no difference. Switched off, the index is 0.108; at 20% it is 0.111 (the priority-6 subsection of section 8). Removing all oversubscription priorities gives 0.109 and displaces 18 children in 2026 against 6.

The power diagram raises segregation on this model — 0.108 — and costs Longhill 2 children. It is not recommended on this evidence.

11.4 Priority 3: catchments, journeys and displacement

For parents the first thing is a short journey to a school near home and friends, and the second is not being displaced from it.

Displacement is small and falling. 6 children in 2026 and 3 in 2030 would have taken a place in their catchment and cannot. Options that trim the full schools hard raise it — to 60 in 2026 with Stringer at 270, Varndean at 240 and Blatchington Mill at 270 — and so does removing the priorities. Whitehawk back to Longhill lowers it, to 4 in 2026, because the Whitehawk families who would be refused at a full central school now have a catchment school with room.

The share of children at a school in their own catchment is raised most by the faith schools taking fewer — 62% with Cardinal Newman at 270 and King’s at 150, against 58% — because every child they do not take goes to a catchment school instead, and usually a nearer one: the longest journeys fall to 54 minutes from 55.

Closing Longhill is the option parents would pay most for. It saves the most money — the community schools’ balance is −£0.5m in 2026 against −£1.4m, partly because Longhill’s own deficit leaves the books — but the longest journeys lengthen to 60 minutes, the share travelling over 40 minutes rises to 29%, and more children leave the city: 74 in 2030 against 60. It also lowers segregation more than any other option in the table, to 0.097 against 0.109: Longhill’s Year 7 intake is among the most disadvantaged in the city, and its children would spread across schools with less disadvantaged ones. That is a statistic about intakes, not a benefit to the children who would travel further. It is the counterfactual against which keeping Longhill open is a choice to spend money on shorter journeys in the east.

11.5 Fitting the system by 2035

Show code
fit <- c("today", "shrink", "shrink_all", "cn270_kings", "package_e", "close_lh")
cop$runs %>%
  filter(year == 2035, id %in% fit) %>%
  mutate(id = factor(id, fit)) %>% arrange(id) %>%
  transmute(Option = label,
            `Empty places` = fmt_n(empty),
            `Community schools' balance` = gbp_m(comm_gap),
            `City-wide balance` = gbp_m(city_gap),
            Displaced = fmt_n(displaced),
            `At a catchment school` = fmt_pct(100 * in_catchment, 0),
            `Longest journeys` = sprintf("%.0f min", p90_min)) %>%
  knitr::kable(align = "lrrrrrr")
Table 56: Removing places for 2035, when the cohort is at its lowest. Cutting only the community schools pushes children into the academies and faith schools, and the community schools lose; cutting every school in proportion keeps the children where they are. Council priorities, 2035.
Option Empty places Community schools’ balance City-wide balance Displaced At a catchment school Longest journeys
Today 720 −£4.4m −£5.6m 1 57% 55 min
Community schools cut in proportion, to 1,920 places in all by 2035 125 −£5.6m −£5.5m 78 54% 55 min
Every school cut in proportion, to 1,920 places in all by 2035 125 −£4.0m −£5.5m 20 62% 53 min
Cardinal Newman cut to 270, King’s to 150 600 −£3.5m −£5.6m 1 61% 54 min
Package D, with Cardinal Newman at 300 and Brighton Aldridge at 150 481 −£4.0m −£5.6m 0 58% 55 min
Longhill closed 523 −£3.2m −£4.3m 1 55% 60 min

By 2035 the city carries 720 empty Year 7 places a year, 494 of them in the community schools. Removing them from the community schools alone makes the community schools worse off: their balance goes from −£4.4m to −£5.6m, because the children they no longer have room for go to the academies and faith schools, and 78 are displaced from their catchment. Removing places from every school in proportion does the opposite: −£4.0m, with 20 displaced and 62% of children at a catchment school. A viable system in 2035 is one in which the schools the council does not run shrink with the ones it does, and the council’s case for asking them rests on exactly this comparison.

11.6 All three together

Where the priorities agree.

  • Cardinal Newman and King’s taking fewer helps Priority 1 and Priority 3 together, and the community schools’ finances most of all. It needs the dioceses’ agreement, and the case to put to them is that a city whose cohort is falling cannot keep every school full, and that the schools it does not run are part of the same system.
  • Reviewing priority 6 touches little. It makes almost no difference to segregation and a small one to displacement, and it gives something real — a route into the popular schools for the children of single-school catchments who use it. The case for keeping it at 5% is as good as the case against.

Where they compete, and which should give.

  • Longhill and journeys against mixed intakes. Returning Whitehawk to Longhill’s catchment gives Longhill 14 more children a year by 2030, lowers displacement and shortens the journeys of the children it moves — and raises the segregation index from 0.109 to 0.111, because the neighbourhoods it moves are 75% disadvantaged against 27% across the city. Which way that trade should go is a judgement, and the council should make it knowing both halves — and knowing that the higher index counts against the redraw under the council’s aim of evening out intakes, not on the evidence about disadvantaged pupils’ results (Section 2.3.1).
  • Access against segregation. Elm Grove all but closes the gap between deprived children’s journeys and everyone else’s, and it concentrates disadvantaged children in one school: the index rises from 0.109 to 0.142. A review of how to even out disadvantaged children’s chances has to choose between those two measures, because relocation improves one and worsens the other. This document’s evidence leans towards access: most of what separates schools’ headline results arrives with the children rather than being added by the school (Section 2.4), so a more mixed intake is a weak lever on outcomes, while the journey is made by every child twice a day. For disadvantaged pupils the national evidence goes further: they do slightly better, not worse, where they are concentrated in a school organised for them, and Brighton Aldridge is the city’s own example (Section 2.3.1). What the document cannot show is whether shorter journeys raise attendance; the council’s records could.
  • Longhill against short journeys. Trimming the central schools helps Longhill for two or three years and lengthens journeys for the children moved. It should be done only with the Whitehawk redraw, and only while those schools are full.
  • Money against journeys. Closing Longhill is the cheapest system and the one with the longest journeys in the east. Keeping it open is worth doing only as a deliberate choice to pay for shorter journeys and a school in the east, sized and staffed for the children it will get.

11.6.1 The recommendation

If the council’s inequalities review puts evening out disadvantaged children’s chances first, the order of the recommendations changes. The earlier version of this section treated a move to Elm Grove as an access decision to be weighed against its cost to Longhill. On the full model with the catchments redrawn, it is the option that does most for access and most for Longhill’s roll, and the map drawn round it settles the Whitehawk question as part of a redesign. So it leads.

  1. Make a smaller Longhill at the top of Elm Grove the lead option, and test it properly. At 150 places with the catchments redrawn round it, Longhill takes 106 children in 2026 and 86 in 2030, against 90 and 71 today; deprived children stop travelling further than everyone else, a gap of 0.4 minutes against 3.9; Whitehawk’s disadvantaged children’s journeys fall from 56 to 23 minutes; and the new map puts 4 of the 6 Whitehawk neighbourhoods back in Longhill’s catchment. A move needs a new map in any case, so Whitehawk’s return comes as part of a redesign built round a new school rather than as a stand-alone reversal of the 2024 change. The costs belong in the same paper: a site, not costed here; a segregation index of 0.142 against 0.109; 337 children a year in neighbourhoods that change catchment; longer journeys for Rottingdean, Saltdean and Woodingdean, more of whose children leave the city (66 from across the city in 2030 against 60); and a school that still does not break even.
  2. If the site cannot be had, return Whitehawk to Longhill’s catchment on its own. It gives Longhill 14 children a year by 2030 and lowers displacement, and it raises the segregation index from 0.109 to 0.111. It also does little for Whitehawk’s journeys while the school is at Ovingdean: its disadvantaged children travel 52 minutes against 56 now. It is the fallback, not the first choice.
  3. Set Longhill’s admission number at 150, on either site, and plan its budget and staffing for the children it will get — about a hundred at Ovingdean, somewhat more at Elm Grove — not for 150. Put the gap between that and break-even — about £0.8m a year at Ovingdean in 2030 and £0.7m at Elm Grove — to members as the cost of keeping a school in the east, and decide it as that.
  4. Ask Cardinal Newman to reduce to 300, and King’s to 150, and Brighton Aldridge to 150, and make the case for each school in the city giving up places in proportion by 2030. With Longhill at Elm Grove these asks add most to its roll.
  5. Trim Stringer, Varndean and Blatchington Mill only if Longhill stays at Ovingdean and only with a catchment redraw — to 300, 270 and 300, for the next two or three rounds while they are full, with Whitehawk returned to Longhill’s catchment. With Longhill at Elm Grove the trims add few children in the model’s runs and raise displacement.
  6. Publish where priority 6’s places went, so what it does to segregation can be measured rather than modelled.
  7. Ask the inequalities review to settle the measure. Relocation shortens disadvantaged children’s journeys and concentrates them in one school. Whether evening out chances means the first or the second, and whether shorter journeys raise attendance, are questions the review can answer from the council’s own records and this document cannot. It should weigh the national evidence that disadvantaged pupils do no worse — slightly better — in schools where they are concentrated and well served (Section 2.3.1), before treating a more concentrated intake as a cost.

Together — Longhill at Elm Grove at 150 with the catchments redrawn, Cardinal Newman at 300, King’s at 150 and Brighton Aldridge at 150 — that package gives Longhill 117 children in 2026 and 91 in 2030, against 90 and 71; a segregation index of 0.137 against 0.109; 7 children displaced in 2026 against 6; 61% at a catchment school against 58%; and, in 2026, longest journeys of 53 minutes against 54; and a deprived-journey gap of 0.5 minutes against 3.9. It does not make Longhill break even; that needs a decision about money. Without the move, the best package tested — Whitehawk back to Longhill, Longhill at 150, the central trims, no priority 6, Cardinal Newman at 300 and Brighton Aldridge at 150 — gives Longhill 129 in 2026 and 90 in 2030, and leaves the deprived-journey gap at 3.5 minutes: only a school nearer to them, or better buses, closes that (Section 10.1).

WarningWhat this rests on

Every number here is from a model calibrated to published catchment totals, not to individual applications. The finances are a steady state five years out, with a school shedding three quarters of the cost of a pupil it loses. The catchment terms are carried to redrawn maps at the strength families follow the maps they have, which is what decides every redraw result above. Segregation counts disadvantaged pupils placed by each school’s fitted pull on them, which reproduces the published shares for the cohorts admitted before the 2024 map and priorities, and assumes those pulls hold under today’s rules and when maps and schools change. Most options differ on the index by a few thousandths, which is inside what a change of measure has moved. And the academies and faith schools are modelled as responding to demand, not as making their own decisions about numbers. The Elm Grove runs were corrected in this version: competition is now measured from the Elm Grove site, and children refused elsewhere are re-offered places with the second preferences families gave shifted by how much the move changes each neighbourhood’s pull towards the school. Both raised Elm Grove’s figures a little. The site itself, and what building on it would cost, are not modelled. The direction of each finding is more reliable than its size.

12 Sources and data

Everything in this document is built from published data. Nothing here uses pupil-level records: no individual child, postcode-level application or named preference appears in any input, and the one place where postcode-level counts are used — the density map in Section 2.2 — is a census household count, not an admissions record.

Every chart and table names its immediate source in the caption. This section gives the links, says which sections rest on which data, and lists what is in the repository’s data/ folder and what made it.

12.1 What each section is built from

Show code
uses %>%
  group_by(number, section) %>%
  summarise(
    derived = paste(sort(unname(ds_title[file])), collapse = "; "),
    sources = paste(purrr::map_chr(sort(unique(unlist(ds_from[file]))),
                                   src_link), collapse = "; "),
    .groups = "drop") %>%
  transmute(Section = paste0(number, ". ", section),
            `Data used` = derived,
            `Published sources` = sources) %>%
  knitr::kable(align = "lll")
Table 57: Which published data each numbered section of this document rests on. Derived files are the ones this repository and the open model build; the sources column is what they are built from. Generated from the document’s own source rather than maintained by hand.
Section Data used Published sources
1. Executive summary Accessibility surfaces and benchmarks; Every school’s finances and exposure; LSOA boundaries; Scenario runs, and Longhill’s accounts School admissions: allocation factsheets; Bus Open Data Service; School income and expenditure; Get Information About Schools; Home-to-school travel and transport guidance; English indices of deprivation 2019; National Travel Survey; ONS Postcode Directory and boundaries; OpenStreetMap
2. The secondary school system Catchments in force; Households with children, by postcode; How much a school can reach; LSOA boundaries; Model inputs: zones, costs, schools; School attainment panel; Variance decomposition School admissions: allocation factsheets; Secondary catchment boundaries; Bus Open Data Service; Census 2021; Explore education statistics; Get Information About Schools; English indices of deprivation 2019; How to Pull the Right Lever; ONS Postcode Directory and boundaries; OpenStreetMap; School performance tables; Small area population estimates
3. Demographic futures LSOA boundaries; Model inputs: zones, costs, schools; Reception cohorts and the projection; The council’s October 2024 forecast School admissions: allocation factsheets; Secondary catchment boundaries; School place planning reports and forecasts; Bus Open Data Service; Get Information About Schools; ONS Postcode Directory and boundaries; OpenStreetMap; Small area population estimates
4. How reachable are the schools? Accessibility surfaces and benchmarks; Catchments in force; LSOA boundaries; Model inputs: zones, costs, schools; One journey, leg by leg; Preferences and offers, 2010 to 2026 School admissions: allocation factsheets; Secondary catchment boundaries; Bus Open Data Service; Get Information About Schools; Home-to-school travel and transport guidance; English indices of deprivation 2019; National Travel Survey; ONS Postcode Directory and boundaries; OpenStreetMap; Small area population estimates
5. Admissions rules, and what families actually do Accessibility surfaces and benchmarks; Preferences and offers, 2010 to 2026; What the adjudicator determined School admissions: allocation factsheets; School place planning reports and forecasts; Bus Open Data Service; Home-to-school travel and transport guidance; English indices of deprivation 2019; National Travel Survey; Schools Adjudicator determinations; OpenStreetMap
6. Money Every school’s finances and exposure; Funding allocations, 2025-26; Model inputs: zones, costs, schools; Preferences and offers, 2010 to 2026; Scenario runs, and Longhill’s accounts School admissions: allocation factsheets; Secondary catchment boundaries; Bus Open Data Service; School income and expenditure; School funding statistics; Get Information About Schools; ONS Postcode Directory and boundaries; OpenStreetMap; Small area population estimates
7. Brightopia: a model of the whole system Catchments in force; Model inputs: zones, costs, schools; Modelled flows along the road network; Preferences and offers, 2010 to 2026; Sensitivity across the parameter band; The distance-only model; The model ladder M0 to M5 School admissions: allocation factsheets; Secondary catchment boundaries; Where each catchment’s children were offered a place, 2024; Catchment-level preferences, evidence to the Schools Adjudicator; Bus Open Data Service; Get Information About Schools; ONS Postcode Directory and boundaries; OpenStreetMap; Small area population estimates
8. Redrawing the catchments Catchments redrawn from the flows; LSOA boundaries; Model inputs: zones, costs, schools; One journey, leg by leg; Priority 6 and the social mix of intakes School admissions: allocation factsheets; Secondary catchment boundaries; Where each catchment’s children were offered a place, 2024; Catchment-level preferences, evidence to the Schools Adjudicator; Bus Open Data Service; Census 2021; Get Information About Schools; English indices of deprivation 2019; ONS Postcode Directory and boundaries; OpenStreetMap; School performance tables; Small area population estimates
9. Longhill CoMArt re-opened: the scenario runs; Scenario runs, and Longhill’s accounts; The model ladder M0 to M5 School admissions: allocation factsheets; Secondary catchment boundaries; Where each catchment’s children were offered a place, 2024; Catchment-level preferences, evidence to the Schools Adjudicator; Bus Open Data Service; Census 2021; School income and expenditure; Get Information About Schools; English indices of deprivation 2019; ONS Postcode Directory and boundaries; OpenStreetMap; School performance tables
10. What the council needs to do Accessibility surfaces and benchmarks; LSOA boundaries; Scenario runs, and Longhill’s accounts School admissions: allocation factsheets; Bus Open Data Service; School income and expenditure; Home-to-school travel and transport guidance; English indices of deprivation 2019; National Travel Survey; ONS Postcode Directory and boundaries; OpenStreetMap
11. Weighing the priorities The council’s options, scored on its priorities; What the Whitehawk redraw does, and why School admissions: allocation factsheets; Where each catchment’s children were offered a place, 2024; Catchment-level preferences, evidence to the Schools Adjudicator; Bus Open Data Service; Census 2021; School income and expenditure; English indices of deprivation 2019; ONS Postcode Directory and boundaries; OpenStreetMap; School performance tables

12.2 The published sources

Links go to the publisher’s landing page for the dataset rather than to a particular file. Deep links to statistical releases rot within a year or two, and a landing page that has moved a file still gets a reader to it. The council’s committee papers are cited by title and date for the same reason.

Show code
SOURCES %>%
  arrange(publisher, title) %>%
  transmute(Source = sprintf("[%s](%s)", title, url),
            Publisher = publisher,
            `What it is` = what,
            Licence = licence) %>%
  knitr::kable(align = "llll")
Table 58: Every published source this document draws on.
Source Publisher What it is Licence
Catchment-level preferences, evidence to the Schools Adjudicator Brighton & Hove City Council First, second and third preferences and allocations by home catchment and school, 2023/24 to 2025/26 (item 8.1). Received as a party to the 2026/27 case, not published; aggregated to catchment and non-disclosive. The full model (M5) is calibrated to it. not published; used as a party to the case
Educational Equity Framework, Appendix 1 Brighton & Hove City Council Key Stage 2 and Key Stage 4 pupils and attainment by home ward and middle super output area, from the January 2025 school census (table 13.1.2). Section 2.7 checks the model’s origins against it. OGL v3
School admissions: allocation factsheets Brighton & Hove City Council Preferences and offers by school and rank, published each year after allocation. OGL v3
School place planning reports and forecasts Brighton & Hove City Council Catchment forecasts and admission-number proposals, in committee papers and consultation documents. OGL v3
Secondary catchment boundaries Brighton & Hove City Council The catchment map in force from September 2026 entry, and the map it replaced. OGL v3
Where each catchment’s children were offered a place, 2024 Brighton & Hove City Council Offers on national offer day in the 2024 round by home catchment and school, including schools outside the city, with small counts suppressed. The council’s answer to a Freedom of Information request, published on WhatDoTheyKnow. The full model’s out-of-city destinations are fitted to it. published FOI response
How to Pull the Right Lever Dennett and colleagues, UCL CASA The multilevel model of school-level attainment whose specification and decomposition section 2 follows. author’s own work
Explore education statistics Department for Education The underlying national datasets behind the performance and finance services. OGL v3
Get Information About Schools Department for Education The school register: URN, phase, age range, status, religious character, coordinates. OGL v3
Home-to-school travel and transport guidance Department for Education The statutory maximum journey times used as a benchmark in section 4. OGL v3
School funding statistics Department for Education School-level funding allocations for 2025-26, split into the formula’s own components. OGL v3
School income and expenditure Department for Education Consistent Financial Reporting returns for maintained schools and academy accounts returns. OGL v3
School performance tables Department for Education Attainment 8, Progress 8, absence, cohort characteristics and prior attainment, by school. OGL v3
Bus Open Data Service Department for Transport The Brighton & Hove timetable feed (GTFS) the journey times are routed over. OGL v3
National Travel Survey Department for Transport The England average school trip, used to make a number of minutes interpretable. OGL v3
World Light Gray Canvas Esri The base cartography under every map in this document. Keyless, unlike the CARTO tiles it replaced. Esri terms, attribution required
English indices of deprivation 2019 Ministry of Housing, Communities and Local Government IDACI: income deprivation affecting children, by LSOA. OGL v3
Census 2021 Office for National Statistics Household composition, used for households with dependent children by postcode. OGL v3
ONS Postcode Directory and boundaries Office for National Statistics Postcode centroids, LSOA lookups and the 2021 LSOA boundary geometry. OGL v3
Small area population estimates Office for National Statistics Population by single year of age, used to split each ward’s children between its LSOAs. OGL v3
Schools Adjudicator determinations Office of the Schools Adjudicator The 2025 determinations on Brighton & Hove’s admission arrangements (ADA4423 and others). OGL v3
OpenStreetMap OpenStreetMap contributors The walking and road network the journey times are routed over. ODbL

12.3 What is in data/, and what made it

The repository carries its inputs so that the document renders on any machine. Two of those files are published boundary or statistical files copied unchanged; the rest are derived, and the column says by what.

Show code
n_sec <- purrr::map_int(DATASETS$file,
                        ~ n_distinct(uses$number[uses$file == .x]))

# A file the document loads into an object but never uses is either dead
# code or a drifted register, and both are worth failing on. Two such
# loads were found this way and removed.
stopifnot(all(n_sec[DATASETS$file %in% QMD_VARS$file] > 0))

DATASETS %>%
  mutate(n_sections = n_sec) %>%
  arrange(built_by, file) %>%
  transmute(File = paste0("`", file, "`"),
            `What it is` = title,
            `Built by` = built_by,
            `From` = purrr::map_chr(from, ~ paste(SOURCES$title[match(.x, SOURCES$key)],
                                                  collapse = "; ")),
            Sections = if_else(n_sections == 0, "—", as.character(n_sections))) %>%
  knitr::kable(align = "lllr")
Table 59: Every file this repository reads, what it is, and what built it. The open Brightopia bundle is the open-data model published alongside this document; the school attainment tool is the national panel behind How to Pull the Right Lever. The last column counts the numbered sections that use the file directly; a dash means it is an input to one of the others rather than to the document.
File What it is Built by From Sections
adjudicator_conversion.rds What the adjudicator determined open Brightopia bundle Schools Adjudicator determinations; School place planning reports and forecasts 1
brightopia.rds The distance-only model open Brightopia bundle OpenStreetMap; Bus Open Data Service; School admissions: allocation factsheets 1
comart_costs.rds Routed journeys to CoMArt’s site open Brightopia bundle OpenStreetMap; Bus Open Data Service; ONS Postcode Directory and boundaries —
deprivation_open.rds IDACI by neighbourhood open Brightopia bundle English indices of deprivation 2019; ONS Postcode Directory and boundaries —
factsheet_panel.rds Preferences and offers, 2010 to 2026 open Brightopia bundle School admissions: allocation factsheets 4
flow_map.rds Modelled flows along the road network open Brightopia bundle OpenStreetMap; Bus Open Data Service; School admissions: allocation factsheets 1
guide_routes.rds One neighbourhood routed to every school open Brightopia bundle OpenStreetMap; Bus Open Data Service; ONS Postcode Directory and boundaries —
model_terms.rds The model ladder M0 to M5 open Brightopia bundle OpenStreetMap; Bus Open Data Service; School admissions: allocation factsheets; Secondary catchment boundaries; Catchment-level preferences, evidence to the Schools Adjudicator; Where each catchment’s children were offered a place, 2024; Get Information About Schools 2
open_inputs.rds Model inputs: zones, costs, schools open Brightopia bundle ONS Postcode Directory and boundaries; Small area population estimates; Get Information About Schools; Secondary catchment boundaries; School admissions: allocation factsheets; OpenStreetMap; Bus Open Data Service 6
open_scenarios.rds Scenario runs, and Longhill’s accounts open Brightopia bundle OpenStreetMap; Bus Open Data Service; School admissions: allocation factsheets; School income and expenditure 4
reception_cohort.rds Reception cohorts and the projection open Brightopia bundle School admissions: allocation factsheets; School place planning reports and forecasts 1
route_geometries.rds One journey, leg by leg open Brightopia bundle OpenStreetMap; Bus Open Data Service 2
school_finance.rds Every school’s finances and exposure open Brightopia bundle School income and expenditure; Get Information About Schools; School admissions: allocation factsheets 2
sensitivity_envelope.rds Sensitivity across the parameter band open Brightopia bundle OpenStreetMap; Bus Open Data Service; School admissions: allocation factsheets 1
ward_validation.rds The origins checked against the council’s ward count open Brightopia bundle Educational Equity Framework, Appendix 1; Small area population estimates; ONS Postcode Directory and boundaries; School admissions: allocation factsheets —
catchments_current.geojson Catchments in force published boundary file Secondary catchment boundaries 3
catchments_pre2024.geojson Catchments in force before 2026 entry published boundary file Secondary catchment boundaries —
lsoa.geojson LSOA boundaries published boundary file ONS Postcode Directory and boundaries 6
school-funding-statistics_2025-26/data/20260129_School_level_data_csv.csv Funding allocations, 2025-26 published dataset School funding statistics 1
performance_panel.rds School attainment panel school attainment tool School performance tables; Explore education statistics 1
school_effect_decomp.rds Variance decomposition school attainment tool School performance tables; How to Pull the Right Lever 1
school_leverage.rds How much a school can reach school attainment tool School performance tables; How to Pull the Right Lever 1
postcode_children.csv Households with children, by postcode this repository, R/01_assemble.R Census 2021; ONS Postcode Directory and boundaries; English indices of deprivation 2019 1
accessibility.rds Accessibility surfaces and benchmarks this repository, R/02_accessibility.R OpenStreetMap; Bus Open Data Service; National Travel Survey; Home-to-school travel and transport guidance; English indices of deprivation 2019 4
flow_regions.rds Catchments redrawn from the flows this repository, R/03_flow_regions.R ONS Postcode Directory and boundaries; English indices of deprivation 2019; Census 2021; Secondary catchment boundaries; OpenStreetMap; Bus Open Data Service 1
comart_scenarios.rds CoMArt re-opened: the scenario runs this repository, R/05_app_inputs.R OpenStreetMap; Bus Open Data Service; Catchment-level preferences, evidence to the Schools Adjudicator; Where each catchment’s children were offered a place, 2024; School admissions: allocation factsheets; English indices of deprivation 2019; Census 2021; ONS Postcode Directory and boundaries; School performance tables 1
council_options.rds The council’s options, scored on its priorities this repository, R/05_app_inputs.R OpenStreetMap; Bus Open Data Service; Catchment-level preferences, evidence to the Schools Adjudicator; Where each catchment’s children were offered a place, 2024; School admissions: allocation factsheets; English indices of deprivation 2019; Census 2021; ONS Postcode Directory and boundaries; School income and expenditure; School performance tables 1
hove_park_campus.rds Hove Park’s two campuses: the intake mix and the move this repository, R/05_app_inputs.R School place planning reports and forecasts; School admissions: allocation factsheets; OpenStreetMap; Bus Open Data Service; ONS Postcode Directory and boundaries; English indices of deprivation 2019; School performance tables —
priority6_sweep.rds Priority 6 and the social mix of intakes this repository, R/05_app_inputs.R Catchment-level preferences, evidence to the Schools Adjudicator; Where each catchment’s children were offered a place, 2024; School admissions: allocation factsheets; English indices of deprivation 2019; Census 2021; ONS Postcode Directory and boundaries; OpenStreetMap; Bus Open Data Service; School performance tables 1
whitehawk_explained.rds What the Whitehawk redraw does, and why this repository, R/05_app_inputs.R OpenStreetMap; Bus Open Data Service; Catchment-level preferences, evidence to the Schools Adjudicator; Where each catchment’s children were offered a place, 2024; School admissions: allocation factsheets; English indices of deprivation 2019; Census 2021; ONS Postcode Directory and boundaries; School performance tables 1
council_forecast_oct24.csv The council’s October 2024 forecast transcribed from the published appendix School place planning reports and forecasts 1

deprivation_open.rds carries the dash. The document does not read it; R/02_accessibility.R and R/03_flow_regions.R do, and it reaches these pages through the accessibility surfaces and the redrawn catchments they build.

12.4 Licences, attribution and reuse

Most of this is Open Government Licence v3. The DfE, DfT, ONS, MHCLG and council datasets above are all published under OGL v3, which permits reuse with attribution. The attribution is in the table above.

OpenStreetMap is ODbL. The walking and road network under every routed journey time is © OpenStreetMap contributors, available under the Open Database Licence.

The basemaps are Esri’s World Light Gray Canvas, used because it needs no API key. Every map carries the Esri and OpenStreetMap attribution in its corner, which the terms require, and R/99_verify_render.R fails the build if a map loses it. These tiles replaced CARTO’s, which are keyed: a render from a shell without the key produced watermarked maps and no other symptom, repeatedly.

The analysis is reproducible from these sources. R/01_assemble.R pulls every input into data/; R/02_accessibility.R and R/03_flow_regions.R build the accessibility surfaces and the redrawn catchments; the model outputs come from the open Brightopia bundle, which publishes its own code. What cannot be reproduced from this repository alone is the routed travel matrix, which needs an OSM extract and a GTFS feed and about an hour of compute — the matrix itself is carried in data/travel/, so nothing downstream of it requires the rebuild.

NoteWhat is deliberately not here

The council’s admissions records. Section 10.3 lists the releases that would replace a modelled flow with an observed one throughout sections 7 and 8. They are not public and this document does not use them.

Anything at individual level. The finest geography used anywhere is the postcode, and only for counts of households from the census. Every model input is at LSOA or catchment level.

Unpublished council material. Where a council figure is quoted it comes from a published report, a published factsheet or a determination of the Schools Adjudicator, and the section says which.

References

Coleman, James S., Ernest Q. Campbell, Carol J. Hobson, James McPartland, Alexander M. Mood, Frederic D. Weinfeld, and Robert L. York. 1966. “Equality of Educational Opportunity.” Washington, DC: U.S. Department of Health, Education,; Welfare, Office of Education.
Kent, Jennifer L., Laya Hossein Rashidi, Emily Moylan, Katherine Owen, Melanie Crane, Aitan M. Militão, Olivia Marrum, and Izabella Santos. 2026. “A Systematic Review of Active School Travel Distances and Durations.” Transport Reviews. https://doi.org/10.1080/01441647.2026.2647794.
Smith, David J., and Sally Tomlinson. 1989. The School Effect: A Study of Multi-Racial Comprehensives. London: Policy Studies Institute.
Teddlie, Charles, and David Reynolds. 2000. The International Handbook of School Effectiveness Research. London: Falmer Press.