/* EDULIBRARY KIT — the whole visual system.
 *
 * Replaces thirteen page stylesheets. Canvas 1 of the 2026-09-11 handoff is the
 * authority: "Anything not on this canvas is not a token: no new colour, size or
 * radius may appear on a later canvas until it is added here and named."
 *
 * Retired here and NOT to be reintroduced:
 *   #12669B, #0C315B, #EF866D  — all failed 4.5:1 on white
 *   #173D59 → #1479AD gradient — the masthead is flat field-navy
 *   12px card radius            — cards are 10px
 *   1300px content cap          — one cap, 1360px
 *   shadows                     — elevation is border + surface, nothing else
 *   breakpoints 350/430/520/620/900 — four breakpoints, not nine
 *
 * Owner direction 2026-09-11: sourcing is carried quietly. Provenance stays
 * (source panels, read dates, explicit "Not published" cells); the narration
 * about our own reading does not. There is no evidence banner, no per-fact
 * evidence glyph and no evidence-state ordering control in this system.
 */

/* ── Family ────────────────────────────────────────────────────────────────
   One family. Bold-italic is synthesised; there is no fourth file. */

@font-face {
  font-family: 'Atkinson Hyperlegible';
  src: url('/fonts/AtkinsonHyperlegible-Regular.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Atkinson Hyperlegible';
  src: url('/fonts/AtkinsonHyperlegible-Italic.woff2') format('woff2');
  font-weight: 400; font-style: italic; font-display: swap;
}
@font-face {
  font-family: 'Atkinson Hyperlegible';
  src: url('/fonts/AtkinsonHyperlegible-Bold.woff2') format('woff2');
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Atkinson Hyperlegible';
  src: url('/fonts/AtkinsonHyperlegible-BoldItalic.woff2') format('woff2');
  font-weight: 700; font-style: italic; font-display: swap;
}
/* Devanagari has no Atkinson coverage, so it is the one permitted fallback and
   it is scoped to Devanagari codepoints only. */
@font-face {
  font-family: 'Noto Sans Devanagari';
  src: url('/fonts/NotoSansDevanagari.ttf') format('truetype');
  font-weight: 400 700; font-display: swap;
  unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FF;
}

/* ── Tokens ─────────────────────────────────────────────────────────────── */

:root {
  /* Colour */
  --ink: #1C2A38;
  --slate: #526575;
  --field-navy: #0B2F4A;
  --field-navy-deep: #0F2B44;
  --action-blue: #0B78AD;
  --action-blue-hover: #08587F;
  --orientation-sky: #8ED7FF;
  --pale-sky: #DFF2FB;
  --coral-route: #BD4938;
  --coral-ink: #8E3527;
  --coral-surface: #FBF1EF;
  --coral-border: #E3BDB4;
  --paper: #F5F8FB;
  --surface: #FFFFFF;
  --surface-alt: #EDF3F8;
  --rule: #D6E2EB;
  --rule-soft: #EDF3F8;
  --on-navy: #EEF5FB;
  --on-navy-dim: #CFE1F1;
  --on-navy-faint: #9FC8E6;

  /* Type */
  --font: 'Atkinson Hyperlegible', 'Noto Sans Devanagari', ui-sans-serif, system-ui, sans-serif;
  --font-code: ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Space — 4 8 12 16 24 32 48 64, nothing between */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 24px; --s6: 32px; --s7: 48px; --s8: 64px;

  /* Shape */
  --r-control: 2px;
  --r-card: 10px;
  --r-chip: 999px;

  /* Reach */
  --target: 44px;
  --target-row: 48px;

  /* Frame */
  --cap: 1360px;
  --gutter: 40px;
  --rail: 300px;

  /* Motion */
  --t-action: 160ms ease;
  --t-disclosure: 180ms ease;
}

/* Dark is a token swap and nothing else. Light is v1; dark ships behind an
   explicit opt-in until the full set is drawn. */
[data-theme="dark"] {
  --ink: #EEF5FB;
  --slate: #9FB3C7;
  --paper: #101B26;
  --surface: #162533;
  --surface-alt: #1B2C3C;
  --rule: #26384A;
  --rule-soft: #1F3143;
  --field-navy: #162533;
  --field-navy-deep: #101B26;
  --action-blue: #6FC2EC;
  --action-blue-hover: #9BD6F3;
  --pale-sky: #17303F;
  --coral-surface: #2A1A17;
  --coral-border: #7E4B41;
  --coral-ink: #F0A897;
}

/* ── Base ───────────────────────────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }

/* ⚠️ THIS ONE LINE IS LOAD-BEARING FOR EVERY INTERACTIVE FEATURE ON THE SITE.
   `edulibrary-directory.js`, `edulibrary-courses.js` and `edulibrary-compare.js`
   all hide things by setting the `hidden` ATTRIBUTE (`card.hidden = true`),
   which depends on the user-agent rule `[hidden] { display: none }`. A UA rule
   loses to any author rule — so the moment this kit said
   `.course-v2-card { display: grid }`, `hidden` stopped doing anything.

   The symptom was silent and total: all 1,654 course cards rendered at once
   with the pager counting "1–32 of 1,654" beside them, directory filtering
   changed nothing, and /compare stayed on its empty state forever because the
   empty block ships `hidden` and the script un-hides the table. Nothing threw,
   so the console was clean.

   `!important` is correct here rather than lazy: `hidden` is a statement that
   the element is not relevant, and no layout rule may outrank it. */
[hidden] { display: none !important; }

/* `overflow-x: clip` on BOTH html and body. On body alone the full-bleed
   hero's 100vw still widened the document by the scrollbar's width, because
   the viewport unit counts the scrollbar and the propagated overflow lands on
   the root. `clip` rather than `hidden` so sticky positioning survives. */
html { -webkit-text-size-adjust: 100%; overflow-x: clip; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 16px/1.55 var(--font);
  text-rendering: optimizeLegibility;
  /* `clip`, not `hidden`: it contains the full-bleed hero's 100vw without
     creating a scroll container, so sticky table headers keep working. */
  overflow-x: clip;
}

a { color: var(--action-blue); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--action-blue-hover); }

/* Never removed. 2px action-blue, 2px offset; sky when the ground is navy. */
:focus-visible {
  outline: 2px solid var(--action-blue);
  outline-offset: 2px;
  border-radius: var(--r-control);
}
.el-navy :focus-visible,
.el-masthead :focus-visible,
.el-footer :focus-visible,
.el-hero :focus-visible { outline-color: var(--orientation-sky); }

img { max-width: 100%; height: auto; }

/* ⚠️ D-13 IS AN INVARIANT AND IT IS NOT SELF-ENFORCING.
   A sweep at 390 found 2,879 result links at 21px on /colleges alone, and form
   controls at 21–26px on / and /search. Standalone controls get the floor
   here rather than in each surface, because every surface got it wrong.
   Inline anchors inside running prose are deliberately excluded: a 44px line
   box inside a paragraph would wreck the measure, and a text link in a
   sentence is not the target this rule is about. */
input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
select,
button,
summary { min-height: var(--target); }

/* 16px is the floor on mobile controls too — below it iOS zooms the page on
   focus, which is a worse failure than a slightly large field. */
main input, main select, main textarea { font-size: max(16px, 1rem); }

h1, h2, h3, h4 { margin: 0; font-weight: 700; letter-spacing: -.02em; text-wrap: balance; }
p { margin: 0; text-wrap: pretty; }
ul, ol { margin: 0; padding: 0; list-style: none; }
/* ⚠️ `dd` carries a 40px default `margin-inline-start`. The record's facts are
   description lists, so every label sat indented under its value until this
   line existed. `dl` carries a default block margin for the same reason. */
dl, dd, dt, figure, blockquote { margin: 0; }

/* ── Type ramp ──────────────────────────────────────────────────────────── */

.t-hero    { font: 700 40px/1.15 var(--font); letter-spacing: -.02em; }
.t-page    { font: 700 34px/1.15 var(--font); letter-spacing: -.02em; }
.t-title   { font: 700 26px/1.2 var(--font); }
.t-section { font: 700 21px/1.22 var(--font); }
.t-lead    { font: 400 18px/1.5 var(--font); }
.t-body    { font: 400 16px/1.58 var(--font); }
.t-control { font: 700 16px/1 var(--font); }
.t-meta    { font: 400 13px/1.4 var(--font); color: var(--slate); }
.t-label   {
  font: 700 11px/1 var(--font);
  letter-spacing: .14em; text-transform: uppercase; color: var(--slate);
}
.t-code    { font: 400 12px/1.4 var(--font-code); }

/* A reading measure, used by the article body and every lede. */
.measure { max-width: 66ch; }

/* ── Frame ──────────────────────────────────────────────────────────────── */

.el-cap { width: 100%; max-width: var(--cap); margin-inline: auto; padding-inline: var(--gutter); }

/* main + rail. Below 1180 the rail leaves the side and follows in flow. */
.el-with-rail { display: grid; grid-template-columns: minmax(0, 1fr) var(--rail); gap: var(--s6); }
.el-rail { display: grid; gap: var(--s4); align-content: start; }

.el-stack   { display: grid; gap: var(--s5); }
.el-stack-4 { display: grid; gap: var(--s4); }
.el-stack-2 { display: grid; gap: var(--s2); }

/* ⚠️ `min-width: auto` IS THE DEFAULT ON A GRID ITEM, and it is why /courses
   overflowed its column by 16px at 390. A grid item refuses to shrink below
   its content's intrinsic width unless told to, so one wide descendant — a
   long unbroken course name, a flex row of fields — pushes the whole track
   past the viewport. Every container in this system is a grid, so this is a
   floor rather than a patch. */
main > *, main > * > * { min-width: 0; }

/* ── Surfaces ───────────────────────────────────────────────────────────── */

.el-card {
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: var(--r-card);
}
.el-card__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s4); flex-wrap: wrap;
  padding: var(--s5) var(--s5) var(--s3);
}
.el-card__body { padding: 0 var(--s5) var(--s5); }
.el-card__foot {
  border-top: 1px solid var(--rule);
  padding: var(--s3) var(--s5);
}

.el-plate {
  background: var(--pale-sky); color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: var(--r-card);
  padding: var(--s4) var(--s5);
}

/* One exception or warning per view. Never decorative. */
.el-exception {
  background: var(--coral-surface);
  border: 1px solid var(--coral-border);
  border-radius: var(--r-card);
  padding: var(--s4) var(--s5);
  color: var(--coral-ink);
}
.el-exception a { color: var(--coral-ink); }

.el-well {
  background: var(--surface-alt); color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: var(--r-card);
}

/* ── Controls ───────────────────────────────────────────────────────────── */

.el-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--target); padding: 0 var(--s5);
  border: 1px solid transparent; border-radius: var(--r-control);
  font: 700 16px/1 var(--font); text-decoration: none; cursor: pointer;
  transition: background-color var(--t-action), color var(--t-action), border-color var(--t-action);
}
.el-btn--primary { background: var(--action-blue); color: #fff; }
.el-btn--primary:hover { background: var(--action-blue-hover); color: #fff; }
.el-btn--secondary {
  background: var(--surface); color: var(--action-blue); border-color: var(--rule);
}
.el-btn--secondary:hover { background: var(--surface-alt); }
.el-btn--onNavy { background: var(--orientation-sky); color: var(--field-navy); }
.el-btn--onNavy:hover { background: #B4E5FF; color: var(--field-navy); }
.el-btn--ghostNavy {
  background: transparent; color: var(--on-navy);
  border-color: rgb(255 255 255 / .3);
}
.el-btn--ghostNavy:hover { background: rgb(255 255 255 / .1); color: var(--on-navy); }
.el-btn[aria-disabled="true"], .el-btn:disabled {
  background: var(--surface-alt); color: var(--slate);
  border-color: var(--rule); cursor: not-allowed;
}

.el-chip {
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: var(--target); padding: 0 var(--s4);
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--rule); border-radius: var(--r-chip);
  font: 700 14px/1 var(--font); text-decoration: none;
  transition: background-color var(--t-action), border-color var(--t-action);
}
.el-chip:hover { background: var(--surface-alt); color: var(--ink); }
.el-chip[aria-pressed="true"], .el-chip--active {
  background: var(--field-navy); color: var(--on-navy); border-color: var(--field-navy);
}
.el-chip--onNavy {
  background: transparent; color: var(--on-navy); border-color: rgb(255 255 255 / .3);
}
.el-chip--onNavy:hover { background: rgb(255 255 255 / .12); color: var(--on-navy); }

/* An applied filter is removable. The × is inside the chip's own control. */
.el-chip__remove {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border: 0; background: transparent;
  color: inherit; font-size: 15px; line-height: 1; cursor: pointer; border-radius: var(--r-chip);
}

.el-field {
  display: grid; gap: var(--s1);
}
.el-field > span { font: 700 11px/1 var(--font); letter-spacing: .14em; text-transform: uppercase; color: var(--slate); }
.el-input, .el-select {
  width: 100%; min-height: var(--target);
  padding: 0 var(--s3);
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--rule); border-radius: var(--r-control);
  /* 16px floor is not a style choice — below it iOS zooms the page on focus. */
  font: 400 16px/1 var(--font);
}
.el-input::placeholder { color: var(--slate); }
.el-input[aria-invalid="true"] { border-color: var(--coral-route); }
.el-error { color: var(--coral-ink); font: 400 13px/1.4 var(--font); }

/* ── Ruled rows — the directory's unit, and the index's ─────────────────── */

.el-rows { border-top: 1px solid var(--rule-soft); }
.el-row {
  display: flex; align-items: center; gap: var(--s4);
  min-height: var(--target-row); padding: var(--s3) 0;
  border-bottom: 1px solid var(--rule-soft);
  text-decoration: none; color: inherit;
  transition: background-color var(--t-action);
}
a.el-row:hover { background: var(--surface-alt); }
.el-row__main { flex: 1 1 auto; min-width: 0; display: grid; gap: 2px; }
.el-row__title { font: 700 16px/1.3 var(--font); color: var(--ink); }
a.el-row:hover .el-row__title { color: var(--action-blue); }
.el-row__meta { font: 400 13px/1.4 var(--font); color: var(--slate); }
.el-row__end { flex: 0 0 auto; color: var(--slate); font: 400 13px/1 var(--font); }

/* ── Facts grid — "What is recorded" ───────────────────────────────────────
   A fact that was never recorded says so in its own cell. It is never a blank
   and never a dash. The cell carries the value; it does not carry a lecture
   about how we came to know it. */

.el-facts { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0 var(--s5); }
.el-fact {
  display: grid; gap: var(--s1);
  padding: 14px 0;
  border-bottom: 1px solid var(--rule-soft);
}
.el-fact__label { font: 700 11px/1 var(--font); letter-spacing: .14em; text-transform: uppercase; color: var(--slate); }
.el-fact__value { font: 700 16px/1.35 var(--font); color: var(--ink); }
.el-fact__value--absent { font-weight: 400; color: var(--slate); }
/* Contested or expired facts get the tint bleeding to the cell edge. */
.el-fact--flagged .el-fact__value {
  background: var(--coral-surface); color: var(--coral-ink);
  padding: var(--s2); margin-inline: calc(var(--s2) * -1);
  border-radius: var(--r-control);
}

/* ── Sources — quiet, present, and next to the claim where it belongs ───── */

.el-cite {
  font: 400 13px/1.4 var(--font); font-style: italic; color: var(--slate);
  text-decoration: none; border-bottom: 1px solid var(--rule);
}
.el-cite:hover { color: var(--action-blue); border-bottom-color: currentColor; }

.el-sources .el-row__title { font-style: italic; font-weight: 400; }

/* ── Tables ─────────────────────────────────────────────────────────────── */

.el-table { width: 100%; border-collapse: collapse; }
.el-table th, .el-table td {
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--rule-soft);
  text-align: left; vertical-align: top;
}
.el-table th {
  font: 700 11px/1 var(--font); letter-spacing: .14em; text-transform: uppercase;
  color: var(--slate); white-space: nowrap;
}
.el-table tbody th { font: 700 14px/1.35 var(--font); letter-spacing: 0; text-transform: none; color: var(--ink); }
.el-table tr > * { min-height: var(--target-row); }

/* Programme-and-intake table: name, then three narrow numeric columns. */
.el-progtable { display: grid; grid-template-columns: minmax(0, 1fr) 100px 100px 150px; gap: 0 var(--s4); }
.el-progtable > * { padding: var(--s3) 0; border-bottom: 1px solid var(--rule-soft); min-height: var(--target-row); display: flex; align-items: center; }

/* ── Breadcrumb ─────────────────────────────────────────────────────────── */

.el-crumb {
  min-height: var(--target-row);
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
}
.el-crumb ol { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); min-height: var(--target-row); }
.el-crumb li { font: 400 13px/1.4 var(--font); color: var(--slate); }
.el-crumb li + li::before { content: '/'; margin-right: var(--s2); color: var(--rule); }
.el-crumb a { color: var(--action-blue); text-decoration: none; }
.el-crumb a:hover { text-decoration: underline; }
.el-crumb [aria-current="page"] { font-weight: 700; color: var(--ink); }

/* ── Placeholder media ──────────────────────────────────────────────────── */

.el-photo {
  position: relative; display: block; overflow: hidden;
  aspect-ratio: 16 / 9; border-radius: var(--r-card); background: var(--surface-alt);
}
.el-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.el-photo--placeholder {
  background: repeating-linear-gradient(135deg, #F5F8FB 0 10px, #E7EFF6 10px 20px);
  display: grid; place-items: center;
}
.el-photo__note { font: 400 12px/1.4 var(--font-code); color: var(--slate); }

/* Monogram tile — stands in for a mark we do not have a licence for. */
.el-mono {
  display: grid; place-items: center; flex: 0 0 auto;
  width: 44px; height: 44px; border-radius: var(--r-card);
  background: var(--field-navy-deep); color: var(--orientation-sky);
  font: 700 14px/1 var(--font); letter-spacing: .04em;
}
.el-mono--sm { width: 32px; height: 32px; font-size: 12px; }

/* ── Disclosure ─────────────────────────────────────────────────────────── */

.el-disclosure > summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  min-height: var(--target); cursor: pointer;
  font: 700 16px/1 var(--font); color: var(--action-blue);
  list-style: none;
}
.el-disclosure > summary::-webkit-details-marker { display: none; }
.el-disclosure > summary::after { content: '▾'; transition: transform var(--t-disclosure); }
.el-disclosure[open] > summary::after { transform: rotate(180deg); }

/* ── Pagination ─────────────────────────────────────────────────────────── */

.el-pager { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); }
.el-pager a, .el-pager span {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--target); min-height: var(--target); padding: 0 var(--s3);
  border: 1px solid var(--rule); border-radius: var(--r-control);
  font: 700 14px/1 var(--font); text-decoration: none; color: var(--action-blue);
  background: var(--surface);
}
.el-pager [aria-current="page"] { background: var(--field-navy); color: var(--on-navy); border-color: var(--field-navy); }

/* ── Utility ────────────────────────────────────────────────────────────── */

.el-visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.el-navy { background: var(--field-navy); color: var(--on-navy); }
.el-navy a { color: var(--orientation-sky); }
.el-divider { border: 0; border-top: 1px solid var(--rule); margin: 0; }

/* ── Breakpoints — four ─────────────────────────────────────────────────── */

/* 1180 · the rail leaves the side */
@media (max-width: 1180px) {
  .el-with-rail { grid-template-columns: minmax(0, 1fr); }
}

/* 1000 · masthead nav becomes a disclosure, utility strip hides */
@media (max-width: 1000px) {
  :root { --gutter: 24px; }
}

/* 760 · three columns to two, search fields stack, filters become a modal */
@media (max-width: 760px) {
  :root { --gutter: 16px; }
  .el-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .el-progtable { grid-template-columns: minmax(0, 1fr) 80px; }
  .el-progtable > *:nth-child(4n+3), .el-progtable > *:nth-child(4n) { display: none; }
  .t-hero { font-size: 26px; line-height: 1.3; }
  .t-page { font-size: 26px; line-height: 1.2; }
  .t-title { font-size: 21px; }
  .t-section { font-size: 18px; line-height: 1.2; }
}

/* 480 · two columns to one, cards go full width */
@media (max-width: 480px) {
  .el-facts { grid-template-columns: minmax(0, 1fr); }
  .el-card__head, .el-card__body { padding-inline: var(--s4); }
  .el-card__foot { padding-inline: var(--s4); }
}

@media (max-width: 430px) {
  :root { --gutter: 12px; }
}

/* ── Reduced motion — both tokens removed, no transform, no caret ───────── */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0s !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0s !important;
    scroll-behavior: auto !important;
  }
}
