Reported from staging: a Catholic primary showed six Catholic primaries, none of them close enough to be a real option, and omitted the community school down the road. Three causes, compounding. Ranking put tier before distance, so a faith match at 2.9 miles outranked a community school at 0.3. The ENOUGH=3 stopping rule — added so a cap of six would not drag in weak distant matches — filled the row from the best tier before it ever widened, which is what made every card Catholic. And a 3-mile tier-1 radius is sane for a secondary and most of a city for a primary, whose catchments are routinely under a mile. The premise was backwards. For a parent, distance is a constraint and intake is a preference; a school beyond a primary catchment is not a weaker option, it is not an option. So distance now decides the order and nothing else does. The hard filters are untouched — they were always where the defensibility lived. Similarity survives as chips on the card: reported, so a reader applies their own weighting, rather than ranked, so we apply ours for them. Reach is capped per phase (primary 2, secondary 6, post-16 10) as a sanity bound, not a target: ordering already handles density, so the cap only decides what happens where an area is sparse. A primary with nothing inside two miles now renders no section, which is the honest answer. Deleted: the tier system, the stopping rule, the tier-dependent lede, the `tier` field, the tier-3 fallback chip and its style. select_similar also stops taking is_secondary — it reads the phase from the subject's own row, so no caller can hand it one that disagrees with the data. The heading is now "Other schools nearby". The hard filters still guarantee a comparable set, but nothing ranks on likeness, so the heading no longer says it does. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
65 lines
5.1 KiB
CSS
65 lines
5.1 KiB
CSS
.heading { font-family: var(--font-display); font-size: 1.4rem; letter-spacing: -0.4px; margin: 0; }
|
|
.lede { margin: 0.5rem 0 1.25rem; color: var(--text-secondary); max-width: 64ch; }
|
|
.caption { margin: 1rem 0 0; font-size: 0.72rem; color: var(--text-muted); }
|
|
|
|
.top { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; }
|
|
.arrows { display: flex; gap: 0.5rem; flex: none; }
|
|
.arrow { width: 44px; height: 44px; display: grid; place-items: center; cursor: pointer; border: 1px solid var(--border-strong); border-radius: 999px; background: var(--bg-card); color: var(--brand); }
|
|
.arrow:hover:not(:disabled) { border-color: var(--brand); background: var(--brand-bg); }
|
|
.arrow:disabled { opacity: 0.35; cursor: default; }
|
|
.arrow svg { width: 17px; height: 17px; }
|
|
|
|
/* A scroller, not a paginated view: every card is in the DOM and the arrows
|
|
only move the viewport across them. The 2px padding keeps focus rings from
|
|
being clipped — and is why the arrows' edge test needs a tolerance. */
|
|
.scroller { display: grid; grid-auto-flow: column; grid-auto-columns: calc((100% - 1.8rem) / 3); gap: 0.9rem; overflow-x: auto; scroll-snap-type: x mandatory; padding: 2px; margin: -2px; list-style: none; scrollbar-width: none; -ms-overflow-style: none; }
|
|
.scroller::-webkit-scrollbar { display: none; }
|
|
@media (max-width: 820px) { .scroller { grid-auto-columns: calc((100% - 0.9rem) / 2); } }
|
|
/* Touch widths (MOBILE.md): the arrows would take 96px from a 328px card and
|
|
crush the lede into four lines, for a control swiping already provides. They
|
|
go, and the documented right-edge fade carries the affordance — lifting at
|
|
the end of the travel, where there is nothing more to hint at. */
|
|
@media (max-width: 640px) {
|
|
.top { display: block; }
|
|
.arrows { display: none; }
|
|
.scroller { grid-auto-columns: 86%; mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); }
|
|
.scroller[data-at-end="true"] { mask-image: none; }
|
|
}
|
|
|
|
.school { position: relative; display: flex; flex-direction: column; scroll-snap-align: start; border: 1px solid var(--border); border-radius: 8px; padding: 1rem; background: var(--bg-card); }
|
|
.school:hover { border-color: var(--border-strong); }
|
|
|
|
.distance { margin: 0 0 0.6rem; font-size: 0.75rem; color: var(--text-muted); }
|
|
.name { font-family: var(--font-display); font-size: 1rem; line-height: 1.35; margin: 0 0 0.35rem; }
|
|
.name a { color: var(--text-primary); text-decoration: none; }
|
|
/* The whole card is the link target; the button sits above it on z-index. */
|
|
.name a::after { content: ""; position: absolute; inset: 0; border-radius: 8px; }
|
|
.name a:hover { color: var(--brand); text-decoration: underline; }
|
|
.meta { margin: 0 0 0.75rem; font-size: 0.78rem; color: var(--text-muted); }
|
|
|
|
.shared { display: flex; flex-wrap: wrap; gap: 0.35rem; list-style: none; margin: 0 0 0.85rem; padding: 0; }
|
|
.chip { font-size: 0.72rem; line-height: 1.4; padding: 0.25rem 0.5rem; border-radius: 999px; background: var(--brand-bg); color: var(--brand); border: 1px solid transparent; }
|
|
|
|
.metric { margin-top: auto; padding-top: 0.8rem; border-top: 1px solid var(--border); }
|
|
/* No valence colour here, deliberately: green and terracotta mean "against the
|
|
England average" everywhere else on the site, and colouring a neighbour
|
|
against this school would read as ranking the neighbours. */
|
|
.value { font-family: var(--font-display); font-size: 1.6rem; font-weight: 700; letter-spacing: -0.6px; margin: 0; color: var(--text-primary); }
|
|
.valueAbsent { font-size: 0.95rem; font-weight: 600; margin: 0; color: var(--text-muted); }
|
|
.metricLabel { margin: 0.25rem 0 0; font-size: 0.75rem; color: var(--text-secondary); }
|
|
.metricRef { margin: 0.1rem 0 0; font-size: 0.75rem; color: var(--text-muted); }
|
|
|
|
.add { position: relative; z-index: 1; margin-top: 0.85rem; width: 100%; min-height: 44px; font: inherit; font-size: 0.82rem; font-weight: 500; cursor: pointer; border-radius: 8px; border: 1px solid var(--border-strong); background: var(--bg-card); color: var(--brand); }
|
|
.add:hover { border-color: var(--brand); background: var(--brand-bg); }
|
|
.add[aria-pressed="true"] { border-color: var(--brand); background: var(--brand-bg); font-weight: 600; }
|
|
|
|
.footer { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.85rem; margin-top: 1.25rem; padding-top: 1.1rem; border-top: 1px solid var(--border); }
|
|
.footer p { margin: 0; font-size: 0.78rem; color: var(--text-muted); }
|
|
.footer strong { display: block; font-size: 0.88rem; font-weight: 600; color: var(--text-primary); }
|
|
/* Coral is the one decisive action per screen, and in this section this is it. */
|
|
.compare { min-height: 44px; padding: 0 1.25rem; border-radius: 8px; font-size: 0.88rem; font-weight: 600; background: var(--action); color: var(--action-on); border: 1px solid var(--action); text-decoration: none; display: inline-flex; align-items: center; }
|
|
.compare:hover { background: var(--action-strong); border-color: var(--action-strong); }
|
|
.compare[aria-disabled="true"] { opacity: 0.45; pointer-events: none; }
|
|
|
|
.srOnly { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
|