PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m2s
PR Checks / Backend Smoke (pull_request) Successful in 6s
PR Checks / Build Backend (no push) (pull_request) Successful in 10s
PR Checks / Build Frontend (no push) (pull_request) Successful in 43s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 3m19s
Three review findings. --status-below-bg carried the raw --coral tuple (249,115,96) instead of the hue it belongs to (#A9481F → 169,72,31), so "below average" chips rendered a coral-pink wash behind terracotta text — the one solid/tint pair in the file whose hues disagreed. Alpha drops 0.15 → 0.12 to match --status-above-bg, so the two chips read as equal weight; left at 0.15 the darker terracotta tint takes the label to 4.45:1 on Warm White, under the 4.5 floor. At 0.12 it is 4.86:1 on cards and 4.67:1 on Warm White. Auditing the whole file for the same class of error turned up one more: --action-bg was also a --coral tint while --action is the darkened #BE3C27. It is unused today and the dark-theme value was already correct, but it is aligned so a future coral ghost button gets a matching label and ground. The five phase pairs also "mismatch" by the same test and are deliberately left alone: those pair a pastel ground (sage, sky, mustard, lavender) with a deeper text hue, which is exactly what the guideline's Chips/Tags show. The lowercase wordmark had only reached the visual chrome. Every user-visible occurrence now matches: page title and template, appleWebApp title, authors, OpenGraph and Twitter titles and siteName, the school page's siteName, the PWA manifest name and short_name, the footer copyright line, the nav's aria-label, the Web Share API strings, and the editorial paragraph. Comments in lib/*.ts and AdmissionsView still say "SchoolCompare"; they describe the codebase rather than the mark, so they are left to keep this diff to the reported problem. The comment explaining why the font classes belong on <html> still described --font-schibsted, which no longer exists. It now names --font-manrope and --font-inter, and records that the e2e typeface assertion is the only guard — this failure mode throws nothing and fails no unit test. Verified: tsc clean, 159/159 tests, build green, and the rendered title is now "School Admissions Guide | schoolcompare". Status and phase labels re-checked on their own composited tints over both light grounds — all clear AA.
583 lines
20 KiB
CSS
583 lines
20 KiB
CSS
/*
|
|
* schoolcompare — "Find the right school. For their future."
|
|
*
|
|
* Implements the schoolcompare brand guideline. Colour has four jobs and they
|
|
* never borrow each other's hues:
|
|
* brand (deep teal) identity, navigation, headings, links, primary buttons
|
|
* action (coral) the one decisive action on a page — search, submit
|
|
* status (green / valence against the England average. Never a CTA.
|
|
* terracotta)
|
|
* phase (desaturated) category chips, deliberately subordinate.
|
|
*
|
|
* Two deviations from the supplied swatches, both forced by contrast, both
|
|
* confined to text and fills. Coral #F97360 carrying a white label measures
|
|
* 2.75:1 where 4.5 is the floor, so --action is a darkened #BE3C27; the
|
|
* original coral survives as --coral for tints, borders and illustration.
|
|
* Mustard #F2C94C is 1.5:1 as text, so --mustard-ink carries any label while
|
|
* --mustard stays exact for fills, rings and badges.
|
|
*
|
|
* Every value below clears WCAG AA on all three light grounds — Warm White,
|
|
* white cards and Sand. Sand is the binding one; several values are a step
|
|
* darker than they would need to be on white alone.
|
|
*/
|
|
|
|
:root {
|
|
color-scheme: light;
|
|
|
|
/* ── Ground ─────────────────────────────────────────────────────── */
|
|
--bg-primary: #FAFAF8; /* Warm White */
|
|
--bg-secondary: #F5EFE6; /* Sand — hero panels, sunken rows */
|
|
--bg-card: #FFFFFF;
|
|
--surface-inverse: #0F766E;
|
|
|
|
/* ── Ink ────────────────────────────────────────────────────────── */
|
|
--text-primary: #1C2731; /* Ink */
|
|
--text-secondary: #4A5560;
|
|
--text-muted: #5F6A75; /* 4.8:1 on Sand, the worst ground */
|
|
--text-inverse: #FFFFFF;
|
|
|
|
/* ── Line ───────────────────────────────────────────────────────── */
|
|
--border: #E5E7EB; /* Light Grey */
|
|
--border-strong: #D3D7DD;
|
|
|
|
/* ── Brand: deep teal ───────────────────────────────────────────── */
|
|
--brand: #0F766E; /* exact brand value; 4.8:1 on Sand */
|
|
--brand-strong: #0C5F58;
|
|
--brand-stronger: #0A4D47;
|
|
--brand-bg: rgba(15, 118, 110, 0.10);
|
|
--brand-on: #FFFFFF;
|
|
|
|
/* ── Action: coral. One per screen, and only for the decisive one. ── */
|
|
--action: #BE3C27; /* darkened coral — 5.4:1 under a white label */
|
|
--action-strong: #A33320;
|
|
--action-stronger: #8C2B1B;
|
|
/* Tint of --action, not of --coral, so a coral ghost button's label and
|
|
ground share a hue. Unused today; the dark value was already correct. */
|
|
--action-bg: rgba(190, 60, 39, 0.14);
|
|
--action-on: #FFFFFF;
|
|
|
|
/* ── Palette accents: exact brand values, for fills and tints only ── */
|
|
--coral: #F97360;
|
|
--mustard: #F2C94C;
|
|
--mustard-ink: #806200; /* any label that has to sit on or beside mustard */
|
|
--sage: #A7D7C5;
|
|
--sky: #C7EBF5;
|
|
--lavender: #DDD8F5;
|
|
--sand: #F5EFE6;
|
|
|
|
/* ── Status: above / at / below the England average ─────────────── */
|
|
--status-above: #36743F;
|
|
--status-above-bg: rgba(54, 116, 63, 0.12);
|
|
--status-below: #A9481F;
|
|
/* The tint is the solid hue at low alpha, exactly as every other pair in
|
|
this file is. It briefly carried the raw --coral tuple instead, which
|
|
put a coral-pink wash behind terracotta text — the one mismatched pair
|
|
in the palette. Alpha matches --status-above-bg so the two chips read
|
|
as equal weight; at 0.15 the darker terracotta tint drops the label to
|
|
4.45:1 on Warm White. */
|
|
--status-below-bg: rgba(169, 72, 31, 0.12);
|
|
--status-at: #5F6A75;
|
|
--status-at-bg: rgba(95, 106, 117, 0.10);
|
|
|
|
/* ── Charts ─────────────────────────────────────────────────────── */
|
|
--chart-1: #0F766E;
|
|
--chart-2: #36743F;
|
|
--chart-3: #806200;
|
|
--chart-4: #A9481F;
|
|
--chart-5: #2F6F8F;
|
|
--chart-6: #5F4FA8;
|
|
--chart-grid: #E5E7EB;
|
|
--chart-reference: #5F6A75;
|
|
|
|
/* ── Series: up to 8 schools compared at once ───────────────────────
|
|
Categorical, so wider than the four-hue rule — but every step is held
|
|
to the same mid-dark tone and clears AA on the card, so they read as
|
|
one family and work as legend text as well as chart lines. */
|
|
--series-1: #0F766E;
|
|
--series-2: #36743F;
|
|
--series-3: #806200;
|
|
--series-4: #A9481F;
|
|
--series-5: #2F6F8F;
|
|
--series-6: #5F4FA8;
|
|
--series-7: #0E7A86;
|
|
--series-8: #8A4A6B;
|
|
|
|
/* ── Phase: category, desaturated so it stays under the status hues ── */
|
|
--phase-primary: #0F766E;
|
|
--phase-primary-bg: rgba(167, 215, 197, 0.40);
|
|
--phase-primary-text: #0C5F58;
|
|
--phase-secondary: #2F6F8F;
|
|
--phase-secondary-bg: rgba(199, 235, 245, 0.55);
|
|
--phase-secondary-text: #2A6180;
|
|
--phase-all-through: #36743F;
|
|
--phase-all-through-bg: rgba(54, 116, 63, 0.12);
|
|
--phase-all-through-text: #2F6738;
|
|
--phase-post16: #806200;
|
|
--phase-post16-bg: rgba(242, 201, 76, 0.26);
|
|
--phase-post16-text: #6E5500;
|
|
--phase-nursery: #5F4FA8;
|
|
--phase-nursery-bg: rgba(221, 216, 245, 0.55);
|
|
--phase-nursery-text: #544396;
|
|
|
|
/* ── Sunken surface: the footer, and anything meant to read as a deep
|
|
teal anchor band. Stays teal in both themes on purpose — it is the
|
|
"brand in action" card from the guideline. ─────────────────── */
|
|
--surface-sunken: #0C5F58;
|
|
--on-sunken: #FFFFFF;
|
|
--on-sunken-muted: #C9E4DB;
|
|
--on-sunken-faint: #A9D6CA;
|
|
--on-sunken-link: #C7EBF5;
|
|
|
|
/* ── Medals: rankings podium ────────────────────────────────────── */
|
|
--medal-gold: #806200;
|
|
--medal-silver: #6B7580;
|
|
--medal-bronze: #8A5A2B;
|
|
|
|
/* ── Channels ───────────────────────────────────────────────────────
|
|
CSS can't interpolate a hex token into rgba(), so any tint that needs
|
|
an arbitrary alpha reads the raw channels from here. Keep these in
|
|
sync with the hex tokens above — they are the same colours. */
|
|
--brand-rgb: 15, 118, 110;
|
|
--action-rgb: 190, 60, 39;
|
|
--coral-rgb: 249, 115, 96;
|
|
--sage-rgb: 167, 215, 197;
|
|
--status-above-rgb: 54, 116, 63;
|
|
--status-below-rgb: 169, 72, 31;
|
|
--ink-rgb: 28, 39, 49;
|
|
--text-inverse-rgb: 255, 255, 255;
|
|
--shadow-rgb: 28, 39, 49;
|
|
--muted-rgb: 95, 106, 117;
|
|
--phase-secondary-rgb: 47, 111, 143;
|
|
--medal-silver-rgb: 107, 117, 128;
|
|
--medal-bronze-rgb: 138, 90, 43;
|
|
|
|
/* ── Elevation ──────────────────────────────────────────────────── */
|
|
--shadow-soft: 0 1px 2px rgba(28, 39, 49, 0.04), 0 2px 8px rgba(28, 39, 49, 0.05);
|
|
--shadow-medium: 0 4px 18px rgba(28, 39, 49, 0.09);
|
|
--shadow-strong: 0 10px 34px rgba(28, 39, 49, 0.13);
|
|
--scrim: rgba(28, 39, 49, 0.5);
|
|
|
|
/* ── Type ───────────────────────────────────────────────────────────
|
|
Manrope for headings and key messaging; Inter for body copy, every
|
|
control and every figure. next/font expands each variable to the
|
|
family plus its metric-matched fallback, and the classes sit on
|
|
<html> so :root can see them — see app/layout.tsx. */
|
|
--font-display: var(--font-manrope), -apple-system, BlinkMacSystemFont, sans-serif;
|
|
--font-ui: var(--font-inter), -apple-system, BlinkMacSystemFont, sans-serif;
|
|
--font-data: var(--font-inter), -apple-system, BlinkMacSystemFont, sans-serif;
|
|
--font-prose: var(--font-inter), -apple-system, BlinkMacSystemFont, sans-serif;
|
|
|
|
/* Type scale, straight from the guideline: H1 40/48, H2 32/40, H3 24/32,
|
|
body large 16/24, body 14/20, small 12/16. */
|
|
--step--2: 0.75rem;
|
|
--step--1: 0.875rem;
|
|
--step-0: 1rem;
|
|
--step-1: 1.125rem;
|
|
--step-2: 1.5rem;
|
|
--step-3: 2rem;
|
|
--step-4: 2.5rem;
|
|
--step-5: 3rem;
|
|
|
|
/* ── Geometry & motion ──────────────────────────────────────────────
|
|
"Soft shapes, rounded corners" — the guideline's geometry is markedly
|
|
rounder than the old system's 3/6/10/16. */
|
|
--radius-sm: 8px;
|
|
--radius-md: 12px;
|
|
--radius-lg: 16px;
|
|
--radius-xl: 22px;
|
|
|
|
--transition: 0.2s ease;
|
|
--transition-slow: 0.4s ease;
|
|
}
|
|
|
|
/*
|
|
* Dark theme. Not in the guideline, which is light-only — but the site already
|
|
* ships one, so removing it would be a visible regression. Built from the same
|
|
* tokens: teal and coral are lifted independently so each still clears AA on
|
|
* the dark ground, and the ground itself keeps the Ink hue rather than going
|
|
* to black.
|
|
*
|
|
* Tokens only. Never style a component from inside this block, or the two
|
|
* themes drift apart the first time someone edits one of them.
|
|
*/
|
|
@media (prefers-color-scheme: dark) {
|
|
:root {
|
|
color-scheme: dark;
|
|
|
|
--bg-primary: #111A20;
|
|
--bg-secondary: #16222A;
|
|
--bg-card: #18242C;
|
|
--surface-inverse: #E9EEF0;
|
|
|
|
--text-primary: #E9EEF0;
|
|
--text-secondary: #B4C2C7;
|
|
--text-muted: #8B9AA1; /* 5.5:1 on the card */
|
|
--text-inverse: #111A20;
|
|
|
|
--border: #26343D;
|
|
--border-strong: #35454F;
|
|
|
|
--brand: #5FC7BB;
|
|
--brand-strong: #7BD6CC;
|
|
--brand-stronger: #9BE3DB;
|
|
--brand-bg: rgba(95, 199, 187, 0.14);
|
|
--brand-on: #0A1418;
|
|
|
|
--action: #F08A72;
|
|
--action-strong: #F5A492;
|
|
--action-stronger: #F8BFB1;
|
|
--action-bg: rgba(240, 138, 114, 0.16);
|
|
--action-on: #241009;
|
|
|
|
--coral: #F08A72;
|
|
--mustard: #F2C94C;
|
|
--mustard-ink: #EFC658;
|
|
--sage: #8FCBB7;
|
|
--sky: #9CD4E6;
|
|
--lavender: #C2BBEA;
|
|
--sand: #1B2730;
|
|
|
|
--status-above: #7FCB8A;
|
|
--status-above-bg: rgba(127, 203, 138, 0.14);
|
|
--status-below: #EFA184;
|
|
--status-below-bg: rgba(239, 161, 132, 0.14);
|
|
--status-at: #8B9AA1;
|
|
--status-at-bg: rgba(139, 154, 161, 0.14);
|
|
|
|
--chart-1: #5FC7BB;
|
|
--chart-2: #7FCB8A;
|
|
--chart-3: #EFC658;
|
|
--chart-4: #EFA184;
|
|
--chart-5: #8FB4D9;
|
|
--chart-6: #B6A9DD;
|
|
--chart-grid: #26343D;
|
|
--chart-reference: #8B9AA1;
|
|
|
|
--series-1: #5FC7BB;
|
|
--series-2: #7FCB8A;
|
|
--series-3: #EFC658;
|
|
--series-4: #EFA184;
|
|
--series-5: #8FB4D9;
|
|
--series-6: #B6A9DD;
|
|
--series-7: #6FD0DC;
|
|
--series-8: #D99BB8;
|
|
|
|
--phase-primary: #5FC7BB;
|
|
--phase-primary-bg: rgba(95, 199, 187, 0.16);
|
|
--phase-primary-text: #8ADACF;
|
|
--phase-secondary: #8FB4D9;
|
|
--phase-secondary-bg: rgba(143, 180, 217, 0.16);
|
|
--phase-secondary-text: #AAC8E4;
|
|
--phase-all-through: #7FCB8A;
|
|
--phase-all-through-bg: rgba(127, 203, 138, 0.16);
|
|
--phase-all-through-text: #9AD8A3;
|
|
--phase-post16: #EFC658;
|
|
--phase-post16-bg: rgba(242, 201, 76, 0.16);
|
|
--phase-post16-text: #F2D179;
|
|
--phase-nursery: #B6A9DD;
|
|
--phase-nursery-bg: rgba(182, 169, 221, 0.16);
|
|
--phase-nursery-text: #C8BEE6;
|
|
|
|
--surface-sunken: #0C5F58;
|
|
--on-sunken: #FFFFFF;
|
|
--on-sunken-muted: #C9E4DB;
|
|
--on-sunken-faint: #A9D6CA;
|
|
--on-sunken-link: #C7EBF5;
|
|
|
|
--medal-gold: #EFC658;
|
|
--medal-silver: #A9B6BC;
|
|
--medal-bronze: #C99070;
|
|
|
|
--brand-rgb: 95, 199, 187;
|
|
--action-rgb: 240, 138, 114;
|
|
--coral-rgb: 240, 138, 114;
|
|
--sage-rgb: 143, 203, 183;
|
|
--status-above-rgb: 127, 203, 138;
|
|
--status-below-rgb: 239, 161, 132;
|
|
--ink-rgb: 233, 238, 240;
|
|
--text-inverse-rgb: 17, 26, 32;
|
|
--shadow-rgb: 0, 0, 0;
|
|
--muted-rgb: 139, 154, 161;
|
|
--phase-secondary-rgb: 143, 180, 217;
|
|
--medal-silver-rgb: 169, 182, 188;
|
|
--medal-bronze-rgb: 201, 144, 112;
|
|
|
|
--shadow-soft: 0 1px 2px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
--shadow-medium: 0 4px 18px rgba(0, 0, 0, 0.45);
|
|
--shadow-strong: 0 10px 34px rgba(0, 0, 0, 0.55);
|
|
--scrim: rgba(4, 8, 10, 0.65);
|
|
}
|
|
}
|
|
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
html {
|
|
scroll-behavior: smooth;
|
|
}
|
|
|
|
body {
|
|
font-family: var(--font-ui);
|
|
background: var(--bg-primary);
|
|
color: var(--text-primary);
|
|
line-height: 1.6;
|
|
/* dvh (dynamic viewport) accounts for iOS Safari's collapsing toolbar;
|
|
fall back to vh on older engines that don't recognise dvh. */
|
|
min-height: 100vh;
|
|
min-height: 100dvh;
|
|
/* Suppress the iOS Safari grey tap flash — explicit :active states
|
|
below carry the press feedback instead. */
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
|
|
/* Form controls don't inherit font or colour from their parent — the UA
|
|
supplies its own. Without this they render in the system font at pure
|
|
black, which is nearly invisible against --text-primary in light mode and
|
|
completely invisible on the dark ground. */
|
|
input,
|
|
select,
|
|
textarea,
|
|
button {
|
|
font-family: inherit;
|
|
color: inherit;
|
|
}
|
|
|
|
/* Every digit that could line up in a column does. This is a data product;
|
|
proportional numerals in a results table read as amateur. */
|
|
/* The interface is mostly figures, so tabular is the default here rather
|
|
than something each component has to remember. Running prose opts back out
|
|
below — proportional figures read better in a sentence. */
|
|
.main,
|
|
table,
|
|
[data-numeric] {
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
/* Prose gets the serif; the interface never does. */
|
|
.prose {
|
|
font-family: var(--font-prose);
|
|
font-variant-numeric: normal;
|
|
font-size: var(--step-0);
|
|
line-height: 1.65;
|
|
}
|
|
|
|
/* Provide a baseline press feedback for the most common interactive
|
|
elements — replaces the suppressed default tap highlight. Buttons and
|
|
.btn-* classes carry their own :active states already; this handles
|
|
plain anchors used as inline links and bare button elements. */
|
|
@media (hover: none) and (pointer: coarse) {
|
|
a:active,
|
|
button:active {
|
|
opacity: 0.7;
|
|
}
|
|
}
|
|
|
|
/* Reserve space for the fixed mobile bottom tab bar (56px + safe-area inset). */
|
|
@media (max-width: 640px) {
|
|
body {
|
|
padding-bottom: calc(56px + env(safe-area-inset-bottom, 0px));
|
|
}
|
|
}
|
|
|
|
/* Skip link — visible only on focus for keyboard users */
|
|
.skip-link {
|
|
position: absolute;
|
|
top: -100px;
|
|
left: 1rem;
|
|
z-index: 10000;
|
|
padding: 0.5rem 1rem;
|
|
background: var(--surface-inverse);
|
|
color: var(--text-inverse);
|
|
font-size: 0.875rem;
|
|
font-weight: 600;
|
|
border-radius: var(--radius-md);
|
|
text-decoration: none;
|
|
transition: top 0.15s ease;
|
|
}
|
|
.skip-link:focus {
|
|
top: 0.5rem;
|
|
}
|
|
|
|
/* Focus indicators — brand hue, visible on both grounds */
|
|
:focus-visible {
|
|
outline: 2px solid var(--brand);
|
|
outline-offset: 2px;
|
|
border-radius: var(--radius-sm);
|
|
}
|
|
|
|
/* ================================================================
|
|
Shared button classes — use these across all components
|
|
================================================================ */
|
|
.btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.375rem;
|
|
padding: 0.5rem 1rem;
|
|
font-family: var(--font-ui);
|
|
font-size: 0.875rem;
|
|
font-weight: 600;
|
|
line-height: 1;
|
|
border-radius: var(--radius-md);
|
|
border: 1px solid transparent;
|
|
cursor: pointer;
|
|
transition: background var(--transition), border-color var(--transition), color var(--transition);
|
|
text-decoration: none;
|
|
white-space: nowrap;
|
|
}
|
|
.btn:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
/* Primary: brand fill — main CTAs (Search, Compare now) */
|
|
.btn-primary {
|
|
background: var(--brand);
|
|
color: var(--brand-on);
|
|
border-color: var(--brand);
|
|
}
|
|
.btn-primary:hover:not(:disabled) {
|
|
background: var(--brand-strong);
|
|
border-color: var(--brand-strong);
|
|
}
|
|
.btn-primary:active:not(:disabled) {
|
|
background: var(--brand-stronger);
|
|
border-color: var(--brand-stronger);
|
|
}
|
|
|
|
/*
|
|
* Action: coral fill — the one decisive action on a page, and only that.
|
|
*
|
|
* The guideline gives coral to "calls to action, highlights, important
|
|
* buttons" while its own UI-components sheet draws the primary button in
|
|
* teal. Both are honoured by making this a scarce, separate class: teal is
|
|
* the default primary, coral marks the single action a page exists for —
|
|
* the search submit. Use it more than once per screen and it stops meaning
|
|
* anything.
|
|
*/
|
|
.btn-action {
|
|
background: var(--action);
|
|
color: var(--action-on);
|
|
border-color: var(--action);
|
|
}
|
|
.btn-action:hover:not(:disabled) {
|
|
background: var(--action-strong);
|
|
border-color: var(--action-strong);
|
|
}
|
|
.btn-action:active:not(:disabled) {
|
|
background: var(--action-stronger);
|
|
border-color: var(--action-stronger);
|
|
}
|
|
|
|
/* Secondary: brand outline — supporting actions (Add to shortlist) */
|
|
.btn-secondary {
|
|
background: transparent;
|
|
color: var(--brand);
|
|
border: 1px solid var(--brand);
|
|
}
|
|
.btn-secondary:hover:not(:disabled) {
|
|
background: var(--brand-bg);
|
|
}
|
|
|
|
/* Tertiary: quiet — low-emphasis (View, Clear) */
|
|
.btn-tertiary {
|
|
background: var(--bg-secondary);
|
|
color: var(--text-secondary);
|
|
border: 1px solid var(--border);
|
|
}
|
|
.btn-tertiary:hover:not(:disabled) {
|
|
background: var(--border);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
/* Active toggle state — the shortlist button once a school is on the list */
|
|
.btn-active {
|
|
background: var(--brand-bg);
|
|
color: var(--brand);
|
|
border: 1px solid var(--brand);
|
|
}
|
|
.btn-active:hover:not(:disabled) {
|
|
background: transparent;
|
|
color: var(--text-secondary);
|
|
border-color: var(--border-strong);
|
|
}
|
|
|
|
/* Small variant */
|
|
.btn-sm {
|
|
padding: 0.3rem 0.625rem;
|
|
font-size: 0.8125rem;
|
|
}
|
|
|
|
/*
|
|
* Leaflet ships its own palette — a #ddd tile backdrop, #333 attribution text
|
|
* and a #0078A8 link blue that is the most saturated colour anywhere on the
|
|
* site and belongs to no part of this system. Pull the map chrome onto the
|
|
* tokens. This matters most in dark mode, where Leaflet's white attribution
|
|
* bar would otherwise sit on a near-black page.
|
|
*
|
|
* The tiles themselves stay as OSM renders them; only the chrome is ours.
|
|
*
|
|
* Every selector here is prefixed with `html` on purpose. leaflet.css is
|
|
* imported from a client component, so its chunk loads AFTER globals.css; at
|
|
* equal specificity the later sheet wins and these overrides lose silently.
|
|
* The `html` prefix takes them to 0,1,1 so load order stops mattering.
|
|
*/
|
|
html .leaflet-container {
|
|
background: var(--bg-secondary);
|
|
font-family: var(--font-ui);
|
|
}
|
|
|
|
html .leaflet-control-attribution {
|
|
background: rgba(var(--text-inverse-rgb), 0.82);
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
html .leaflet-control-attribution a {
|
|
color: var(--brand);
|
|
}
|
|
|
|
html .leaflet-bar a {
|
|
background: var(--bg-card);
|
|
color: var(--text-primary);
|
|
border-bottom-color: var(--border);
|
|
}
|
|
|
|
html .leaflet-bar a:hover {
|
|
background: var(--bg-secondary);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
/* Main content column */
|
|
.main {
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
padding: 1.25rem 1.5rem;
|
|
min-width: 0;
|
|
overflow-x: clip;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.main {
|
|
padding: 1rem;
|
|
}
|
|
}
|
|
|
|
/* Honour the OS setting. Transitions collapse to near-instant rather than
|
|
zero so state changes still register as changes. */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
html {
|
|
scroll-behavior: auto;
|
|
}
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: 0.01ms !important;
|
|
scroll-behavior: auto !important;
|
|
}
|
|
}
|