PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m3s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 13s
PR Checks / Build Frontend (no push) (pull_request) Successful in 44s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 10s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 4m29s
Implements the supplied brand guideline: the pin-and-leaf mark, the lowercase schoolcompare wordmark, Manrope for headings and Inter for everything a person reads a number from, the deep-teal/coral/mustard/sage palette, and the rounder geometry the guideline asks for. The landing hero is rebuilt to the concept: a Sand panel carrying the proposition, the search and a trust line, with the brand landscape bleeding to the panel's right edge, followed by the four value props. Concept vs reality — where the guideline describes something this product does not have, the real feature takes its place and the decision is recorded in a code comment next to it: - The Thriving Score is not implemented. Four of its five sub-scores (pastoral, enrichment, community, facilities) have no source in DfE or Ofsted data, and its band names are Ofsted's own, which would sit beside the real Ofsted grade and be indistinguishable from it. - Guides, About, Favourites and Sign in are not added to the nav — there are no such pages and no accounts system. Rankings and Admissions, which the guideline omits, keep their places because they exist. - The fourth value prop is "Never miss a deadline" rather than "Save & revisit", which would need shortlisting. Two deviations from the supplied swatches, both forced by contrast and both confined to text and fills. Coral #F97360 under a white label measures 2.75:1 against a 4.5 floor, so --action is a darkened #BE3C27 and the original coral survives as --coral for tints, borders and illustration. Mustard #F2C94C is 1.5:1 as text, so --mustard-ink carries labels while --mustard stays exact for fills and rings. Every token clears AA on all three light grounds; Sand is the binding one, so several values sit a step darker than white alone would need. The guideline is light-only, but the site already ships a dark theme, so it is rebuilt from the same tokens rather than dropped. Verified: tsc clean, 159/159 unit tests, production build green, and the generated share card and app icon render from the new mark. The hero and the site chrome were rendered from the compiled CSS and the prerendered markup to confirm Manrope and Inter actually resolve — a font token that silently falls back has shipped here before.
575 lines
20 KiB
CSS
575 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;
|
|
--action-bg: rgba(249, 115, 96, 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;
|
|
--status-below-bg: rgba(249, 115, 96, 0.15);
|
|
--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;
|
|
}
|
|
}
|