PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m6s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 12s
PR Checks / Build Frontend (no push) (pull_request) Successful in 49s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Failing after 3m13s
Implements the direction agreed from the identity board: Route C ("Cohort")
with the paper ground from C1, the Schibsted Grotesk / Literata pairing from
C2, and the iris accent from C3. Dark theme is in scope from the start rather
than retrofitted.
The audit found three things wrong beyond taste:
* No brand asset set. og:image was absent entirely, so every link shared into
a class WhatsApp group rendered as a bare grey card. apple-touch-icon pointed
at an SVG, which iOS ignores, and the manifest shipped no PNGs, so Android
installs had no icon. The header mark and the favicon had also drifted into
two different logos.
* No colour discipline. --primary and --trend-down were the same coral, so the
main CTA and "below average" shared a hue. 58 distinct hex values were spread
across component CSS, and the chart palette was still Chart.js's stock demo
colours.
* A dark theme that was declared but never built — themeColor announced a dark
variant with no dark styling behind it.
What changed:
Colour now has exactly three jobs that never borrow each other's hues: brand
(iris) for interactive and identity, status (teal/amber) for above/below a
comparison point, and phase for categories. Teal/amber rather than green/red
keeps the above/below signal readable for every form of colour blindness.
Every chromatic literal in component CSS is now a token, and the JS-painted
surfaces (Chart.js, Leaflet) read the tokens through lib/theme so they follow
the theme instead of ignoring it.
The mark is the five-bar cohort spread — the same object as the distribution
strip inside a school row, built from opacity steps so it inverts cleanly.
components/Logo.tsx is the single source; the favicon, apple-icon and share
card all derive from its geometry.
globals.css drops 123 dead global classes left over from the vanilla-JS app
(only the btn family, .skip-link and .main were still referenced), along with
the noise overlay. It also gains prefers-reduced-motion support, which was
missing entirely, and a type scale so the 54 ad-hoc font sizes have somewhere
to converge.
Verified: tsc clean, 159 unit tests pass, production build succeeds and
prerenders /icon.svg, /apple-icon and /opengraph-image. Three e2e journeys
added for the asset set, the themeColor/background match, and the dark theme
actually repainting — all silent failures that nothing on the page reveals.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1421 lines
26 KiB
CSS
1421 lines
26 KiB
CSS
.homeView {
|
||
width: 100%;
|
||
}
|
||
|
||
.heroSection {
|
||
text-align: center;
|
||
margin-bottom: 1.5rem;
|
||
padding-top: 2.5rem;
|
||
}
|
||
|
||
.heroEyebrow {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.4rem;
|
||
font-size: 0.72rem;
|
||
font-weight: 600;
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
color: var(--brand);
|
||
background: rgba(var(--brand-rgb), 0.1);
|
||
padding: 0.3rem 0.7rem;
|
||
border-radius: 999px;
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.heroEyebrowDot {
|
||
display: inline-block;
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 50%;
|
||
background: var(--brand);
|
||
}
|
||
|
||
.heroTitle {
|
||
font-size: 3rem;
|
||
font-weight: 700;
|
||
color: var(--text-primary);
|
||
margin-bottom: 0.85rem;
|
||
line-height: 1.08;
|
||
letter-spacing: -0.015em;
|
||
font-family: var(--font-display);
|
||
max-width: 840px;
|
||
margin-left: auto;
|
||
margin-right: auto;
|
||
}
|
||
|
||
.heroEmph {
|
||
color: var(--brand-strong);
|
||
font-style: italic;
|
||
}
|
||
|
||
.heroDescription {
|
||
font-size: 1.05rem;
|
||
color: var(--text-secondary);
|
||
margin: 0 auto 0.5rem;
|
||
max-width: 680px;
|
||
line-height: 1.55;
|
||
}
|
||
|
||
.heroDescription strong {
|
||
color: var(--text-primary);
|
||
font-weight: 700;
|
||
}
|
||
|
||
/* Copy variants: full paragraph ≥641px, compact merged line on phones. */
|
||
.heroDescriptionCompact {
|
||
display: none;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.heroSection {
|
||
padding-top: 1.5rem;
|
||
}
|
||
.heroTitle {
|
||
font-size: 2rem;
|
||
}
|
||
.heroDescription {
|
||
font-size: 0.95rem;
|
||
}
|
||
}
|
||
|
||
/* Above the fold on phones, every line costs. Drop the eyebrow tag and swap
|
||
the long descriptive paragraph for one compact line that carries coverage
|
||
("24,000+ schools") and freshness ("updated for 2026/27") — a first-time
|
||
visitor otherwise sees only a poetic h1 and a bare box, with no reason to
|
||
trust the data (audit P1.7, feeds the 46% home exit rate). */
|
||
@media (max-width: 640px) {
|
||
.heroSection {
|
||
padding-top: 0.75rem;
|
||
margin-bottom: 1rem;
|
||
}
|
||
.heroEyebrow,
|
||
.heroDescriptionFull {
|
||
display: none;
|
||
}
|
||
.heroDescriptionCompact {
|
||
display: block;
|
||
}
|
||
.heroDescription {
|
||
font-size: 0.85rem;
|
||
line-height: 1.4;
|
||
max-width: 320px;
|
||
margin-bottom: 0.25rem;
|
||
}
|
||
.heroTitle {
|
||
font-size: 1.65rem;
|
||
}
|
||
}
|
||
|
||
/* View Toggle */
|
||
.viewToggle {
|
||
display: flex;
|
||
gap: 0.2rem;
|
||
background: var(--bg-secondary);
|
||
padding: 0.2rem;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.viewToggleBtn {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.25rem;
|
||
padding: 0.375rem 0.625rem;
|
||
font-size: 0.8125rem;
|
||
font-weight: 500;
|
||
background: transparent;
|
||
border: none;
|
||
border-radius: 5px;
|
||
cursor: pointer;
|
||
color: var(--text-secondary);
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.viewToggleBtn:hover {
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.viewToggleBtn.active {
|
||
background: var(--bg-card);
|
||
color: var(--brand-strong);
|
||
box-shadow: 0 2px 4px rgba(var(--shadow-rgb), 0.08);
|
||
}
|
||
|
||
.viewToggleBtn svg {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.results {
|
||
margin-top: 1rem;
|
||
}
|
||
|
||
.mapViewResults {
|
||
margin-top: 0;
|
||
}
|
||
|
||
/* Map View Layout */
|
||
.mapViewContainer {
|
||
display: grid;
|
||
grid-template-columns: 1fr 340px;
|
||
gap: 1rem;
|
||
height: calc(100dvh - 280px);
|
||
min-height: 520px;
|
||
max-height: 800px;
|
||
}
|
||
|
||
.mapContainer {
|
||
border-radius: 10px;
|
||
overflow: hidden;
|
||
border: 1px solid var(--border);
|
||
height: 100%;
|
||
}
|
||
|
||
.compactList {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.5rem;
|
||
overflow-y: auto;
|
||
height: 100%;
|
||
padding-right: 0.375rem;
|
||
}
|
||
|
||
.compactList::-webkit-scrollbar {
|
||
width: 6px;
|
||
}
|
||
|
||
.compactList::-webkit-scrollbar-track {
|
||
background: var(--bg-secondary);
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.compactList::-webkit-scrollbar-thumb {
|
||
background: var(--border);
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.compactList::-webkit-scrollbar-thumb:hover {
|
||
background: var(--text-muted);
|
||
}
|
||
|
||
/* Compact School Item */
|
||
.compactItem {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
padding: 0.625rem 0.75rem;
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.compactItem:hover {
|
||
border-color: var(--brand);
|
||
box-shadow: 0 2px 6px rgba(var(--shadow-rgb), 0.05);
|
||
}
|
||
|
||
.compactItemContent {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.compactItemHeader {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.375rem;
|
||
margin-bottom: 0.125rem;
|
||
}
|
||
|
||
.compactItemName {
|
||
font-weight: 600;
|
||
font-size: 0.8125rem;
|
||
color: var(--text-primary);
|
||
text-decoration: none;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.compactItemName:hover {
|
||
color: var(--brand-strong);
|
||
}
|
||
|
||
.distanceBadge {
|
||
flex-shrink: 0;
|
||
padding: 0.0625rem 0.375rem;
|
||
font-size: 0.6875rem;
|
||
font-weight: 600;
|
||
background: var(--brand);
|
||
color: var(--brand-on);
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.compactItemMeta {
|
||
display: flex;
|
||
gap: 0.375rem;
|
||
font-size: 0.6875rem;
|
||
color: var(--text-secondary);
|
||
margin-bottom: 0.25rem;
|
||
}
|
||
|
||
.compactItemMeta span:not(:last-child)::after {
|
||
content: '·';
|
||
margin-left: 0.375rem;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.compactItemStats {
|
||
display: flex;
|
||
gap: 0.75rem;
|
||
font-size: 0.6875rem;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.compactStat strong {
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.compactItemActions {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.25rem;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
|
||
.sectionHeader {
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.sectionHeader h2 {
|
||
font-size: 1.5rem;
|
||
font-weight: 700;
|
||
margin-bottom: 0.5rem;
|
||
color: var(--text-primary);
|
||
font-family: var(--font-display);
|
||
}
|
||
|
||
/* Decorative coral bar under section headings */
|
||
.sectionHeader h2::after {
|
||
content: '';
|
||
display: block;
|
||
width: 50px;
|
||
height: 2px;
|
||
background: var(--brand);
|
||
border-radius: 1px;
|
||
margin-top: 0.5rem;
|
||
}
|
||
|
||
.sectionDescription {
|
||
font-size: 0.9375rem;
|
||
color: var(--text-secondary);
|
||
margin: 0;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.schoolList {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.5rem;
|
||
margin-bottom: 1.25rem;
|
||
}
|
||
|
||
/* Staggered fade-in for rows */
|
||
.schoolList > *:nth-child(1) { animation-delay: 0ms; }
|
||
.schoolList > *:nth-child(2) { animation-delay: 30ms; }
|
||
.schoolList > *:nth-child(3) { animation-delay: 60ms; }
|
||
.schoolList > *:nth-child(4) { animation-delay: 90ms; }
|
||
.schoolList > *:nth-child(5) { animation-delay: 120ms; }
|
||
.schoolList > *:nth-child(6) { animation-delay: 150ms; }
|
||
.schoolList > *:nth-child(7) { animation-delay: 180ms; }
|
||
.schoolList > *:nth-child(8) { animation-delay: 210ms; }
|
||
.schoolList > *:nth-child(9) { animation-delay: 240ms; }
|
||
.schoolList > *:nth-child(n+10) { animation-delay: 270ms; }
|
||
|
||
.emptyState {
|
||
text-align: center;
|
||
padding: 2.5rem 1.5rem;
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--border);
|
||
border-radius: 10px;
|
||
}
|
||
|
||
.emptyStateTitle {
|
||
font-size: 1.25rem;
|
||
font-weight: 600;
|
||
color: var(--text-primary);
|
||
margin-bottom: 0.375rem;
|
||
font-family: var(--font-display);
|
||
}
|
||
|
||
.emptyStateDescription {
|
||
font-size: 0.9375rem;
|
||
color: var(--text-secondary);
|
||
max-width: 380px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.resultsHeader {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.resultsHeaderActions {
|
||
width: 100%;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.viewToggle {
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* The sort <select> sizes to its widest option ("Highest Reading, Writing
|
||
& Maths %"), which overflows a phone viewport — beside the view toggle it
|
||
ran off the right edge. Let it flex into the remaining space and shrink;
|
||
the selected label truncates instead of pushing past the screen. */
|
||
.sortSelect {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.mapViewContainer {
|
||
grid-template-columns: 1fr;
|
||
grid-template-rows: 260px auto;
|
||
height: auto;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.mapContainer {
|
||
height: 260px;
|
||
}
|
||
|
||
.compactList {
|
||
height: auto;
|
||
max-height: 350px;
|
||
padding-right: 0;
|
||
}
|
||
|
||
.compactItem {
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.compactItemActions {
|
||
flex-direction: row;
|
||
}
|
||
|
||
.compactItemActions > * {
|
||
flex: 1;
|
||
}
|
||
|
||
.emptyState {
|
||
padding: 2rem 1.25rem;
|
||
}
|
||
}
|
||
|
||
/* Highlighted List Item */
|
||
.highlightedItem .compactItem {
|
||
border-color: var(--brand);
|
||
box-shadow: 0 0 0 1px var(--brand);
|
||
background: var(--bg-secondary);
|
||
}
|
||
|
||
/* Mobile Bottom Sheet */
|
||
.bottomSheetWrapper {
|
||
display: none;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.bottomSheetWrapper {
|
||
display: block;
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
z-index: 1000;
|
||
padding: 1rem;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.bottomSheet {
|
||
position: relative;
|
||
background: var(--bg-card);
|
||
border-radius: 12px;
|
||
box-shadow: 0 -4px 24px rgba(var(--shadow-rgb), 0.15);
|
||
pointer-events: auto;
|
||
animation: slideUpSheet 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||
}
|
||
|
||
.bottomSheet .compactItem {
|
||
border: none;
|
||
box-shadow: none;
|
||
background: transparent;
|
||
padding: 1rem;
|
||
}
|
||
|
||
.bottomSheet .compactItem:hover {
|
||
box-shadow: none;
|
||
}
|
||
|
||
.closeSheetBtn {
|
||
position: absolute;
|
||
top: -12px;
|
||
right: -12px;
|
||
width: 30px;
|
||
height: 30px;
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--border);
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 1.25rem;
|
||
color: var(--text-secondary);
|
||
cursor: pointer;
|
||
box-shadow: 0 2px 8px rgba(var(--shadow-rgb), 0.1);
|
||
z-index: 10;
|
||
}
|
||
|
||
@keyframes slideUpSheet {
|
||
from {
|
||
transform: translateY(120%);
|
||
}
|
||
to {
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
/* When map view on mobile, expand map and hide list */
|
||
.mapViewContainer {
|
||
grid-template-columns: 1fr;
|
||
grid-template-rows: 1fr;
|
||
height: calc(100dvh - 280px);
|
||
min-height: 400px;
|
||
}
|
||
|
||
.mapContainer {
|
||
height: 100%;
|
||
}
|
||
|
||
.compactList {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
.quickSearches {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 0.5rem;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.quickSearchLabel {
|
||
font-size: 0.875rem;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.quickSearchChip {
|
||
padding: 0.375rem 0.875rem;
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--border);
|
||
border-radius: 999px;
|
||
font-size: 0.875rem;
|
||
color: var(--text-secondary);
|
||
text-decoration: none;
|
||
transition: all var(--transition);
|
||
}
|
||
|
||
.quickSearchChip:hover {
|
||
background: var(--brand-strong);
|
||
color: var(--brand-on);
|
||
border-color: var(--brand-strong);
|
||
}
|
||
|
||
.exploringRow {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 0.6rem;
|
||
margin-top: 1rem;
|
||
}
|
||
|
||
.exploringLabel {
|
||
font-size: 0.72rem;
|
||
color: var(--text-muted);
|
||
font-weight: 500;
|
||
letter-spacing: 0.04em;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.exploringChips {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
justify-content: center;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.exploringChip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.4rem;
|
||
padding: 0.5rem 0.95rem;
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--border);
|
||
border-radius: 999px;
|
||
font-size: 0.82rem;
|
||
font-weight: 500;
|
||
color: var(--text-secondary);
|
||
text-decoration: none;
|
||
transition: all 0.15s ease;
|
||
}
|
||
|
||
.exploringChip:hover {
|
||
border-color: var(--brand);
|
||
color: var(--brand-strong);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.chipDot {
|
||
display: inline-block;
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 50%;
|
||
background: currentColor;
|
||
opacity: 0.55;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* ── How it works section ─────────────────────────────── */
|
||
|
||
.howItWorks {
|
||
padding: 3rem 0 1rem;
|
||
}
|
||
|
||
.hiwHeader {
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: space-between;
|
||
flex-wrap: wrap;
|
||
gap: 0.5rem;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.hiwHeading {
|
||
font-family: var(--font-display);
|
||
font-size: 1.75rem;
|
||
font-weight: 700;
|
||
color: var(--text-primary);
|
||
margin: 0;
|
||
}
|
||
|
||
.hiwSub {
|
||
font-size: 0.875rem;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.hiwGrid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 1rem;
|
||
}
|
||
|
||
.hiwCard {
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--border);
|
||
border-radius: 14px;
|
||
padding: 1.25rem;
|
||
box-shadow: 0 2px 8px rgba(var(--shadow-rgb), 0.06);
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.9rem;
|
||
}
|
||
|
||
.hiwVisual {
|
||
background: var(--bg-secondary);
|
||
border-radius: 10px;
|
||
padding: 0.9rem;
|
||
min-height: 180px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.hiwPhaseBlock {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.35rem;
|
||
}
|
||
|
||
.hiwPhaseLabel {
|
||
font-size: 0.58rem;
|
||
font-weight: 600;
|
||
letter-spacing: 0.1em;
|
||
text-transform: uppercase;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.hiwPhaseLabel strong {
|
||
color: var(--brand);
|
||
font-weight: 700;
|
||
}
|
||
|
||
.hiwCardBody {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.3rem;
|
||
}
|
||
|
||
.hiwStep {
|
||
font-size: 0.65rem;
|
||
font-weight: 700;
|
||
letter-spacing: 0.1em;
|
||
text-transform: uppercase;
|
||
color: var(--brand-strong);
|
||
}
|
||
|
||
.hiwTitle {
|
||
font-family: var(--font-display);
|
||
font-size: 1.1rem;
|
||
font-weight: 700;
|
||
color: var(--text-primary);
|
||
line-height: 1.25;
|
||
}
|
||
|
||
.hiwDesc {
|
||
font-size: 0.86rem;
|
||
color: var(--text-secondary);
|
||
line-height: 1.45;
|
||
margin: 0;
|
||
}
|
||
|
||
/* Mini cascade (performance card) */
|
||
.miniCascade {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 0.4rem;
|
||
}
|
||
|
||
.miniCascadeCol {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.15rem;
|
||
min-width: 0;
|
||
}
|
||
|
||
.miniSubj {
|
||
font-size: 0.5rem;
|
||
font-weight: 700;
|
||
letter-spacing: 0.1em;
|
||
text-transform: uppercase;
|
||
color: var(--text-muted);
|
||
margin-bottom: 0.1rem;
|
||
}
|
||
|
||
.miniRowHead {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: baseline;
|
||
gap: 0.2rem;
|
||
font-size: 0.44rem;
|
||
color: var(--text-muted);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.01em;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.miniRowHead strong {
|
||
font-family: var(--font-display);
|
||
font-size: 0.62rem;
|
||
color: var(--text-primary);
|
||
font-weight: 700;
|
||
letter-spacing: 0;
|
||
text-transform: none;
|
||
}
|
||
|
||
.miniTrack {
|
||
height: 5px;
|
||
border-radius: 2px;
|
||
background: rgba(var(--status-above-rgb), 0.08);
|
||
position: relative;
|
||
overflow: visible;
|
||
}
|
||
|
||
.miniBarExp {
|
||
height: 100%;
|
||
border-radius: 2px;
|
||
background: var(--brand);
|
||
}
|
||
|
||
.miniBarExc {
|
||
height: 100%;
|
||
border-radius: 2px;
|
||
background: var(--brand);
|
||
}
|
||
|
||
.miniNatPill {
|
||
position: absolute;
|
||
top: -9px;
|
||
transform: translateX(-50%);
|
||
background: var(--brand);
|
||
color: var(--text-inverse);
|
||
font-size: 0.4rem;
|
||
font-weight: 700;
|
||
padding: 0.05rem 0.2rem;
|
||
border-radius: 3px;
|
||
z-index: 2;
|
||
white-space: nowrap;
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* Attainment 8 mini bar */
|
||
.att8Row {
|
||
display: grid;
|
||
grid-template-columns: 1fr auto;
|
||
gap: 0.6rem;
|
||
align-items: center;
|
||
}
|
||
|
||
.att8BarWrap {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.2rem;
|
||
}
|
||
|
||
.att8BarHead {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
font-size: 0.5rem;
|
||
color: var(--text-muted);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
}
|
||
|
||
.att8Track {
|
||
height: 7px;
|
||
background: rgba(var(--status-above-rgb), 0.08);
|
||
border-radius: 3px;
|
||
position: relative;
|
||
}
|
||
|
||
.att8Fill {
|
||
height: 100%;
|
||
background: var(--brand);
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.att8NatLine {
|
||
position: absolute;
|
||
top: -2px;
|
||
bottom: -2px;
|
||
width: 1.5px;
|
||
background: rgba(var(--brand-rgb), 0.6);
|
||
z-index: 2;
|
||
}
|
||
|
||
.att8Score {
|
||
font-variant-numeric: tabular-nums;
|
||
text-align: right;
|
||
}
|
||
|
||
.att8Value {
|
||
font-variant-numeric: tabular-nums;
|
||
font-family: var(--font-display);
|
||
font-size: 1.5rem;
|
||
font-weight: 700;
|
||
color: var(--status-above);
|
||
line-height: 1;
|
||
}
|
||
|
||
.att8Delta {
|
||
font-variant-numeric: tabular-nums;
|
||
font-size: 0.55rem;
|
||
color: var(--status-above);
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.04em;
|
||
margin-top: 0.15rem;
|
||
}
|
||
|
||
/* Ofsted preview */
|
||
.ofstedPreview {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.5rem;
|
||
width: 100%;
|
||
padding: 0.4rem 0.2rem;
|
||
}
|
||
|
||
.ofstedHead {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
padding-bottom: 0.4rem;
|
||
border-bottom: 1.5px solid var(--border);
|
||
}
|
||
|
||
.ofstedBullet {
|
||
display: block;
|
||
width: 3px;
|
||
height: 1em;
|
||
background: var(--brand);
|
||
border-radius: 2px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.ofstedTitle {
|
||
font-family: var(--font-display);
|
||
font-size: 0.85rem;
|
||
font-weight: 600;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.ofstedFramework {
|
||
align-self: flex-start;
|
||
padding: 0.15rem 0.5rem;
|
||
border-radius: 4px;
|
||
background: rgba(var(--phase-secondary-rgb), 0.12);
|
||
color: var(--phase-secondary-text);
|
||
font-size: 0.5rem;
|
||
font-weight: 700;
|
||
letter-spacing: 0.05em;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.rcList {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.3rem;
|
||
}
|
||
|
||
.rcRow {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.rcArea {
|
||
font-size: 0.62rem;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.rcChip {
|
||
padding: 0.1rem 0.4rem;
|
||
border-radius: 3px;
|
||
font-size: 0.52rem;
|
||
font-weight: 700;
|
||
letter-spacing: 0.03em;
|
||
white-space: nowrap;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.rcChipGood {
|
||
background: rgba(var(--status-above-rgb), 0.12);
|
||
color: var(--status-above);
|
||
}
|
||
|
||
.rcChipNeutral {
|
||
background: var(--status-below-bg);
|
||
color: var(--status-below);
|
||
}
|
||
|
||
/* Safeguarding is a separate binary met/not-met judgement, not a graded area,
|
||
so it sits below the five-point list behind a divider with its own chip. */
|
||
.rcSafeguarding {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 0.5rem;
|
||
margin-top: 0.05rem;
|
||
padding-top: 0.35rem;
|
||
border-top: 1px dashed var(--border);
|
||
}
|
||
|
||
.rcSep {
|
||
color: var(--text-muted);
|
||
font-style: italic;
|
||
}
|
||
|
||
.rcChipMet {
|
||
background: transparent;
|
||
color: var(--status-above);
|
||
border: 1px solid rgba(var(--status-above-rgb), 0.4);
|
||
}
|
||
|
||
.ofstedMeta {
|
||
font-size: 0.6rem;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
/* Compare preview */
|
||
.comparePreview {
|
||
width: 100%;
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
font-size: 0.6rem;
|
||
}
|
||
|
||
.compareHead {
|
||
display: grid;
|
||
grid-template-columns: 1fr repeat(2, 1fr);
|
||
background: rgba(var(--status-above-rgb), 0.1);
|
||
padding: 0.35rem 0.5rem;
|
||
gap: 0.35rem;
|
||
}
|
||
|
||
.compareHeadCell {
|
||
font-family: var(--font-display);
|
||
font-size: 0.62rem;
|
||
font-weight: 700;
|
||
color: var(--status-above);
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.compareHeadLabel {
|
||
font-family: inherit;
|
||
font-size: 0.48rem;
|
||
color: var(--text-muted);
|
||
font-weight: 500;
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.compareRow {
|
||
display: grid;
|
||
grid-template-columns: 1fr repeat(2, 1fr);
|
||
padding: 0.3rem 0.5rem;
|
||
gap: 0.35rem;
|
||
border-top: 1px solid var(--border);
|
||
align-items: baseline;
|
||
}
|
||
|
||
.compareRowLabel {
|
||
font-size: 0.55rem;
|
||
color: var(--text-muted);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
}
|
||
|
||
.compareRowVal {
|
||
font-family: var(--font-display);
|
||
font-size: 0.72rem;
|
||
font-weight: 700;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.compareRowValHi {
|
||
color: var(--status-above);
|
||
}
|
||
|
||
.compareFoot {
|
||
font-size: 0.52rem;
|
||
color: var(--text-muted);
|
||
padding: 0.35rem 0.5rem;
|
||
background: var(--bg-secondary);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.06em;
|
||
text-align: center;
|
||
border-top: 1px solid var(--border);
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.hiwGrid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
.hiwHeader {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
}
|
||
.hiwHeading {
|
||
font-size: 1.4rem;
|
||
}
|
||
}
|
||
|
||
/* On phones, hide the scaled-down preview visuals (text becomes illegible
|
||
at this size) and let the explanatory text carry each card. */
|
||
@media (max-width: 640px) {
|
||
.hiwVisual {
|
||
display: none;
|
||
}
|
||
.hiwCard {
|
||
padding: 1rem 1.1rem;
|
||
gap: 0.45rem;
|
||
}
|
||
.hiwTitle {
|
||
font-size: 1.05rem;
|
||
}
|
||
.hiwDesc {
|
||
font-size: 0.9rem;
|
||
}
|
||
}
|
||
|
||
/* ── Editorial section ───────────────────────────────── */
|
||
|
||
.editorial {
|
||
padding: 2rem 0 3rem;
|
||
}
|
||
|
||
.editorialGrid {
|
||
display: grid;
|
||
grid-template-columns: 1.4fr 1fr;
|
||
gap: 2rem;
|
||
padding: 1.75rem;
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--border);
|
||
border-radius: 14px;
|
||
}
|
||
|
||
.editorialText {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.65rem;
|
||
}
|
||
|
||
.editorialKicker {
|
||
font-size: 0.68rem;
|
||
color: var(--brand-strong);
|
||
font-weight: 700;
|
||
letter-spacing: 0.1em;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.editorialHeading {
|
||
font-family: var(--font-display);
|
||
font-size: 1.35rem;
|
||
font-weight: 700;
|
||
color: var(--text-primary);
|
||
margin: 0;
|
||
line-height: 1.25;
|
||
}
|
||
|
||
/* Running prose is the one place the serif appears. Slightly larger than the
|
||
UI around it because Literata's x-height sits lower than the grotesque's. */
|
||
.editorialText p {
|
||
font-family: var(--font-prose);
|
||
font-size: 0.97rem;
|
||
color: var(--text-secondary);
|
||
line-height: 1.65;
|
||
margin: 0;
|
||
max-width: 62ch;
|
||
}
|
||
|
||
.factbox {
|
||
background: var(--bg-secondary);
|
||
border-radius: 10px;
|
||
padding: 1.25rem;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0;
|
||
}
|
||
|
||
.factboxHeading {
|
||
font-family: var(--font-display);
|
||
font-size: 1rem;
|
||
font-weight: 700;
|
||
color: var(--text-primary);
|
||
margin: 0 0 0.85rem;
|
||
}
|
||
|
||
.factRow {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: baseline;
|
||
padding: 0.5rem 0;
|
||
border-bottom: 1px solid var(--border);
|
||
font-size: 0.85rem;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.factRow:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.factKey {
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.factVal {
|
||
font-family: var(--font-display);
|
||
font-weight: 700;
|
||
color: var(--text-primary);
|
||
text-align: right;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.editorialGrid {
|
||
grid-template-columns: 1fr;
|
||
padding: 1.25rem;
|
||
gap: 1.25rem;
|
||
}
|
||
}
|
||
|
||
.resultsHeader {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
flex-wrap: wrap;
|
||
gap: 0.75rem;
|
||
padding: 0 0 1rem;
|
||
}
|
||
|
||
.resultsHeaderActions {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.sortSelect {
|
||
padding: 0.375rem 0.75rem;
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius-md);
|
||
background: var(--bg-card);
|
||
color: var(--text-secondary);
|
||
font-size: 0.875rem;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.activeFilters {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 0.5rem;
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.filterChip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.375rem;
|
||
padding: 0.25rem 0.625rem;
|
||
background: var(--bg-secondary);
|
||
border: 1px solid var(--border);
|
||
border-radius: 999px;
|
||
font-size: 0.8rem;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.chipRemove {
|
||
color: var(--text-muted);
|
||
text-decoration: none;
|
||
font-size: 0.9rem;
|
||
line-height: 1;
|
||
transition: color var(--transition, 0.2s ease);
|
||
}
|
||
|
||
.chipRemove:hover {
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.loadMoreSection {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
padding: 1.5rem 0;
|
||
}
|
||
|
||
.loadMoreCount {
|
||
font-variant-numeric: tabular-nums;
|
||
font-size: 0.875rem;
|
||
color: var(--text-muted);
|
||
margin: 0;
|
||
}
|
||
|
||
.loadMoreButton {
|
||
min-width: 160px;
|
||
}
|
||
|
||
/* =========================================================
|
||
Admissions Countdown Strip
|
||
========================================================= */
|
||
|
||
.admissionsStrip {
|
||
padding: 1.5rem 0 2rem;
|
||
border-top: 1px solid var(--border);
|
||
}
|
||
|
||
.stripHeader {
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: space-between;
|
||
margin-bottom: 1rem;
|
||
flex-wrap: wrap;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.stripLabel {
|
||
font-size: 0.72rem;
|
||
font-weight: 700;
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.stripCta {
|
||
font-size: 0.82rem;
|
||
color: var(--brand);
|
||
font-weight: 600;
|
||
text-decoration: none;
|
||
}
|
||
|
||
.stripCta:hover {
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.countdownRail {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.countdownChip {
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--border);
|
||
border-radius: 12px;
|
||
padding: 1rem 1.1rem 0.9rem;
|
||
box-shadow: 0 2px 8px rgba(var(--shadow-rgb), 0.06);
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.2rem;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.countdownChip::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 3px;
|
||
border-radius: 12px 12px 0 0;
|
||
}
|
||
|
||
.countdownChipDeadline::before {
|
||
background: var(--brand);
|
||
}
|
||
|
||
.countdownChipOffer::before {
|
||
background: var(--status-above);
|
||
}
|
||
|
||
.countdownChipUrgent {
|
||
border-color: rgba(var(--brand-rgb), 0.4);
|
||
background: rgba(var(--brand-rgb), 0.04);
|
||
}
|
||
|
||
.chipTrack {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.3rem;
|
||
font-size: 0.6rem;
|
||
font-weight: 700;
|
||
letter-spacing: 0.1em;
|
||
text-transform: uppercase;
|
||
margin-bottom: 0.15rem;
|
||
}
|
||
|
||
.chipTrackDeadline {
|
||
color: var(--brand-strong);
|
||
}
|
||
|
||
.chipTrackOffer {
|
||
color: var(--status-above);
|
||
}
|
||
|
||
.chipTrackDot {
|
||
width: 5px;
|
||
height: 5px;
|
||
border-radius: 50%;
|
||
background: currentColor;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.chipDays {
|
||
font-variant-numeric: tabular-nums;
|
||
font-family: var(--font-display);
|
||
font-size: 2.6rem;
|
||
font-weight: 700;
|
||
line-height: 1;
|
||
letter-spacing: -0.02em;
|
||
}
|
||
|
||
.countdownChipDeadline .chipDays,
|
||
.countdownChipUrgent .chipDays {
|
||
color: var(--brand-strong);
|
||
}
|
||
|
||
.countdownChipOffer .chipDays {
|
||
color: var(--status-above);
|
||
}
|
||
|
||
.chipDaysUnit {
|
||
font-family: var(--font-ui);
|
||
font-size: 0.78rem;
|
||
font-weight: 500;
|
||
color: var(--text-muted);
|
||
margin-left: 0.2rem;
|
||
vertical-align: bottom;
|
||
line-height: 2;
|
||
}
|
||
|
||
.chipMilestone {
|
||
font-size: 0.85rem;
|
||
font-weight: 600;
|
||
color: var(--text-primary);
|
||
line-height: 1.25;
|
||
margin-top: 0.1rem;
|
||
}
|
||
|
||
.chipDate {
|
||
font-size: 0.75rem;
|
||
color: var(--text-muted);
|
||
margin-top: 0.05rem;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.countdownRail {
|
||
grid-template-columns: repeat(2, 1fr);
|
||
}
|
||
}
|
||
|
||
/* On phones the 2×2 grid cramped each chip so badly the "Secondary ·
|
||
Deadline" track label dropped to 9.6px. Switch to a horizontal
|
||
snap-scroller — each card is full-width-ish and stays readable,
|
||
and the rightmost card peeks past the edge to signal there's more. */
|
||
@media (max-width: 640px) {
|
||
.countdownRail {
|
||
display: flex;
|
||
grid-template-columns: none;
|
||
overflow-x: auto;
|
||
scroll-snap-type: x mandatory;
|
||
scrollbar-width: none;
|
||
gap: 0.75rem;
|
||
padding-right: 1.25rem;
|
||
margin-inline: -1rem;
|
||
padding-inline: 1rem;
|
||
-webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
|
||
mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
|
||
}
|
||
.countdownRail::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
.countdownChip {
|
||
flex: 0 0 auto;
|
||
width: 78%;
|
||
min-width: 220px;
|
||
scroll-snap-align: start;
|
||
}
|
||
.chipTrack {
|
||
font-size: 0.7rem;
|
||
}
|
||
}
|