/* * SchoolCompare.co.uk — "Cohort" * * A school's number means nothing on its own; its position in the national * distribution does. That idea drives the identity: the logo is the same * five-bar spread that appears inside every school row. * * Colour has exactly three jobs and they never borrow each other's hues: * brand (iris) interactive + identity. Never carries valence. * status (teal / amber) valence — teal is above/good, amber is * below/needs-attention. Never a CTA. * phase (desaturated) category chips, deliberately subordinate. * * Escalate within status by weight, not by reaching for a new hue: a tinted * amber chip for "requires improvement", a solid amber one for "inadequate". * * Teal/amber rather than green/red so the above/below signal survives every * form of colour blindness — the audience includes parents reading closely. */ :root { color-scheme: light; /* ── Ground ─────────────────────────────────────────────────────── */ --bg-primary: #f2f1ed; /* paper, not cream — warm enough to read as a document */ --bg-secondary: #e9e8e2; --bg-card: #fcfcfa; --surface-inverse: #16202a; /* dark fills: skip link, active nav, tooltips */ /* ── Ink ────────────────────────────────────────────────────────── */ --text-primary: #16202a; --text-secondary: #4a545f; --text-muted: #5c6570; /* 5.3:1 on --bg-primary */ --text-inverse: #f7f7f4; /* ── Line ───────────────────────────────────────────────────────── */ --border: #dedcd5; --border-strong: #cdcac1; /* ── Brand: iris ────────────────────────────────────────────────── */ --brand: #584a9b; /* 6.9:1 on --bg-primary */ --brand-strong: #473c80; /* hover on brand fills */ --brand-stronger: #3a3069; /* active/pressed */ --brand-bg: rgba(88, 74, 155, 0.10); --brand-on: #fcfcfa; /* text/icon sitting on a brand fill */ /* ── Status: above / at / below the comparison point ────────────── */ --status-above: #0e6e66; /* 5.2:1 on --bg-primary */ --status-above-bg: rgba(14, 110, 102, 0.10); --status-below: #9a5b00; /* 4.8:1 on --bg-primary */ --status-below-bg: rgba(154, 91, 0, 0.11); --status-at: var(--text-muted); --status-at-bg: rgba(92, 101, 112, 0.10); /* ── Charts: the same three hues, extended by lightness only ────── */ --chart-1: #584a9b; --chart-2: #0e6e66; --chart-3: #9a5b00; --chart-4: #8a7cc9; --chart-5: #3e9c92; --chart-6: #c9903d; --chart-grid: #dedcd5; --chart-reference: #5c6570; /* national/LA reference lines */ /* ── Series: up to 8 schools compared at once ─────────────────────── Categorical needs distinguishable hues, so this ramp is wider than the three-hue rule — but every step is held to the same mid-dark tone and moderate saturation, and every one clears AA on --bg-primary, so they read as one family and work as legend text as well as chart lines. */ --series-1: #584a9b; --series-2: #0e6e66; --series-3: #9a5b00; --series-4: #a03a5e; --series-5: #2f5f8f; --series-6: #4a6b2f; --series-7: #7a3f7a; --series-8: #97442a; /* ── Phase: category, desaturated so it stays under the status hues ── */ --phase-primary: #4a6072; --phase-primary-bg: rgba(74, 96, 114, 0.10); --phase-primary-text: #3b4e5e; --phase-secondary: #5f5480; --phase-secondary-bg: rgba(95, 84, 128, 0.10); --phase-secondary-text: #4e4570; --phase-all-through: #4b6b57; --phase-all-through-bg: rgba(75, 107, 87, 0.10); --phase-all-through-text: #3d5847; --phase-post16: #7a6242; --phase-post16-bg: rgba(122, 98, 66, 0.10); --phase-post16-text: #645036; --phase-nursery: #7a5560; --phase-nursery-bg: rgba(122, 85, 96, 0.10); --phase-nursery-text: #64454f; /* ── Medals: rankings podium. Metal, not palette — kept legible on both grounds rather than literally gold/silver/bronze. ───────── */ --medal-gold: #a67c00; --medal-silver: #6f7580; --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: 88, 74, 155; --status-above-rgb: 14, 110, 102; --status-below-rgb: 154, 91, 0; --ink-rgb: 22, 32, 42; --text-inverse-rgb: 247, 247, 244; --shadow-rgb: 22, 32, 42; --muted-rgb: 92, 101, 112; --phase-secondary-rgb: 95, 84, 128; --medal-silver-rgb: 111, 117, 128; --medal-bronze-rgb: 138, 90, 43; /* ── Elevation ──────────────────────────────────────────────────── */ --shadow-soft: 0 1px 2px rgba(22, 32, 42, 0.04), 0 2px 8px rgba(22, 32, 42, 0.05); --shadow-medium: 0 4px 20px rgba(22, 32, 42, 0.09); --shadow-strong: 0 8px 40px rgba(22, 32, 42, 0.14); --scrim: rgba(22, 32, 42, 0.55); /* ── Type ───────────────────────────────────────────────────────── */ --font-display: var(--font-schibsted), 'Schibsted Grotesk', -apple-system, BlinkMacSystemFont, sans-serif; --font-ui: var(--font-schibsted), 'Schibsted Grotesk', -apple-system, BlinkMacSystemFont, sans-serif; --font-data: var(--font-schibsted), 'Schibsted Grotesk', -apple-system, BlinkMacSystemFont, sans-serif; --font-prose: var(--font-literata), 'Literata', Georgia, serif; /* Type scale, 1.2 ratio off a 1rem base. New work should use these rather than inventing another font-size. */ --step--2: 0.694rem; --step--1: 0.833rem; --step-0: 1rem; --step-1: 1.2rem; --step-2: 1.44rem; --step-3: 1.728rem; --step-4: 2.074rem; --step-5: 2.488rem; /* ── Geometry & motion ──────────────────────────────────────────── */ --radius-sm: 3px; --radius-md: 6px; --radius-lg: 10px; --radius-xl: 16px; --transition: 0.2s ease; --transition-slow: 0.4s ease; } /* * Dark theme. Not an inversion — the accent and both status hues are lifted * independently so each still clears AA on the dark ground, and --brand-on * flips to dark because the brand fill is now the lighter of the pair. * * 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: #10151b; --bg-secondary: #161c24; --bg-card: #171d25; --surface-inverse: #e4e9ee; --text-primary: #e4e9ee; --text-secondary: #b4bec8; --text-muted: #8d99a6; /* 6.4:1 on --bg-primary */ --text-inverse: #10151b; --border: #28313b; --border-strong: #38434f; --brand: #9c8ce8; /* 6.5:1 on --bg-primary */ --brand-strong: #b0a3ee; --brand-stronger: #c2b8f3; --brand-bg: rgba(156, 140, 232, 0.14); --brand-on: #10151b; --status-above: #3fb3a4; /* 7.2:1 */ --status-above-bg: rgba(63, 179, 164, 0.14); --status-below: #d99b2e; /* 7.7:1 */ --status-below-bg: rgba(217, 155, 46, 0.14); --status-at-bg: rgba(141, 153, 166, 0.14); --chart-1: #9c8ce8; --chart-2: #3fb3a4; --chart-3: #d99b2e; --chart-4: #6f5fc4; --chart-5: #2b8a7e; --chart-6: #a97420; --chart-grid: #28313b; --chart-reference: #8d99a6; --series-1: #9c8ce8; --series-2: #3fb3a4; --series-3: #d99b2e; --series-4: #e07a9d; --series-5: #6fa8dc; --series-6: #94c46b; --series-7: #c48bc4; --series-8: #e08a6b; --phase-primary: #8aa4b8; --phase-primary-bg: rgba(138, 164, 184, 0.14); --phase-primary-text: #a3bacd; --phase-secondary: #a294c4; --phase-secondary-bg: rgba(162, 148, 196, 0.14); --phase-secondary-text: #b7abd6; --phase-all-through: #8bb098; --phase-all-through-bg: rgba(139, 176, 152, 0.14); --phase-all-through-text: #a2c2ad; --phase-post16: #bfa27c; --phase-post16-bg: rgba(191, 162, 124, 0.14); --phase-post16-text: #d0b795; --phase-nursery: #bf98a3; --phase-nursery-bg: rgba(191, 152, 163, 0.14); --phase-nursery-text: #d1aeb8; --medal-gold: #d4a72c; --medal-silver: #a8b0bb; --medal-bronze: #c08552; --brand-rgb: 156, 140, 232; --status-above-rgb: 63, 179, 164; --status-below-rgb: 217, 155, 46; --ink-rgb: 228, 233, 238; --text-inverse-rgb: 16, 21, 27; --shadow-rgb: 0, 0, 0; --muted-rgb: 141, 153, 166; --phase-secondary-rgb: 162, 148, 196; --medal-silver-rgb: 168, 176, 187; --medal-bronze-rgb: 192, 133, 82; --shadow-soft: 0 1px 2px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.3); --shadow-medium: 0 4px 20px rgba(0, 0, 0, 0.45); --shadow-strong: 0 8px 40px rgba(0, 0, 0, 0.6); --scrim: rgba(4, 7, 10, 0.7); } } * { 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; } /* Every digit that could line up in a column does. This is a data product; proportional numerals in a results table read as amateur. */ table, [data-numeric] { font-variant-numeric: tabular-nums; } /* Prose gets the serif; the interface never does. */ .prose { font-family: var(--font-prose); 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); } /* 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; } /* 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; } }