/* =============================================================================
   University page v3 — the four university tabs and the seven programme tabs.
   Reference: docs/university-page-redesign/ (spec + 11 screenshots).

   Built entirely on the semantic tokens in style.css (--bg-surface, --border,
   --text-primary …). Nothing here hard-codes a light-mode colour, which is what
   makes dark mode work without a second stylesheet — and what stops these pages
   from drifting away from the rest of the site the next time the palette moves.

   Icons are Lucide, from theme/new/icons/lucide-sprite.svg. See v3_icon().
   ========================================================================== */

:root {
    --v3-accent:      #1d4ed8;   /* the blue the reference uses for figures and links */
    --v3-accent-weak: rgba(29, 78, 216, 0.08);
    --v3-radius:      14px;
    --v3-radius-sm:   9px;
    --v3-gap:         18px;
}

:root[data-theme="dark"] {
    --v3-accent:      #7aa2ff;
    --v3-accent-weak: rgba(122, 162, 255, 0.12);
}

/* ------------------------------------------------------------------ layout */

.v3-page {
    background: var(--bg-page);
    /* The site header is `position: fixed`, and the global clearance for it is
       `body:not(.home-new) { padding-top: 60px }` — but the header actually
       stands 86px tall (logo 58px + 14px padding top and bottom). The missing
       26px used to eat this panel's entire top padding, which is why the
       breadcrumb sat flush against the navigation bar. 26px of real space plus
       the 26px the body is short = 52px. */
    padding: 52px 0 60px;
    min-height: 60vh;
}

.v3-wrap {
    /* The reference draws these pages at ~1810px. 1240 (the rest of the site's
       width) squeezes rows the design keeps on one line — five course domains,
       a full timeline date. 1400 is the compromise: wide enough for those rows,
       close enough to the site's other pages not to read as a different site. */
    max-width: 1400px;
    margin: 0 auto;
    padding: 0 20px;
}

.v3-panel {
    background: var(--bg-surface);
    border: 1px solid var(--border);
    border-radius: var(--v3-radius);
    box-shadow: var(--shadow-card);
    margin-bottom: var(--v3-gap);
    overflow: hidden;
}

.v3-panel__body { padding: 22px 26px; }

.v3-panel h2 {
    font-size: 19px;
    font-weight: 700;
    color: var(--text-primary);
    margin: 0 0 12px;
}

.v3-panel h3 {
    font-size: 15px;
    font-weight: 700;
    color: var(--text-primary);
    margin: 22px 0 10px;
}

.v3-muted  { color: var(--text-muted); }
.v3-note   { color: var(--text-secondary); font-size: 13px; line-height: 1.6; }

/* ------------------------------------------------------------------- icons */

.v3-i {
    width: 18px;
    height: 18px;
    stroke: currentColor;
    fill: none;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
    flex: none;
    vertical-align: -3px;
}

.v3-icon-circle {
    width: 42px;
    height: 42px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--v3-accent-weak);
    color: var(--v3-accent);
    flex: none;
}

.v3-icon-circle .v3-i { width: 20px; height: 20px; }

/* The outlined ring. Used where the icon is the only thing carrying the row —
   fact cards, course domains, learning formats, "other costs" — which is where
   the reference draws a large open circle rather than a small filled disc. */
.v3-icon-circle--outline {
    width: 56px;
    height: 56px;
    background: transparent;
    border: 1.5px solid var(--v3-accent-line, var(--border-strong));
    color: var(--text-primary);
}

.v3-icon-circle--outline .v3-i { width: 26px; height: 26px; }

/* A row of icon + text items with no card border, separated by hairlines.
   "Learning approach", "Core learning formats" and "Other costs to plan for"
   are all this shape in the reference — a card border there would read as four
   separate facts rather than one list. */
.v3-iconrow {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: 0;
    margin: 14px 0 4px;
}

.v3-iconrow > div {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 6px 22px;
    color: var(--text-primary);
    font-size: 14px;
    line-height: 1.45;
}

.v3-iconrow > div + div { border-left: 1px solid var(--border); }
.v3-iconrow > div:first-child { padding-left: 0; }

/* The same row inside a bordered band — how the reference closes the Student
   Life tab. */
.v3-iconrow--boxed {
    border: 1px solid var(--border);
    border-radius: var(--v3-radius-sm);
    padding: 8px 20px;
    margin-top: 20px;
}

.v3-iconrow--boxed > div:first-child { padding-left: 0; }
.v3-iconrow a { color: var(--v3-accent); text-decoration: none; }
.v3-iconrow a:hover { text-decoration: underline; }
.v3-iconrow strong { display: block; font-weight: 500; margin-bottom: 3px; }
.v3-iconrow small { display: block; font-size: 13px; color: var(--text-secondary); }

/* --------------------------------------------------------- breadcrumb / back */

.v3-crumbs {
    font-size: 13px;
    color: var(--text-muted);
    margin-bottom: 10px;
}

.v3-crumbs a { color: var(--v3-accent); text-decoration: none; }
.v3-crumbs a:hover { text-decoration: underline; }
.v3-crumbs span { margin: 0 7px; }

.v3-back {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    font-weight: 600;
    color: var(--v3-accent);
    text-decoration: none;
    margin-bottom: 14px;
}

/* -------------------------------------------------------------------- hero */

/* No padding on the hero itself: the campus photo runs to the panel's own edge
   in the reference, so the padding belongs to the blocks beside it. */
.v3-hero { display: flex; align-items: stretch; gap: 0; padding: 0; }

.v3-hero__logo {
    margin: 22px 0 22px 26px;
    width: 92px;
    height: 92px;
    border-radius: 50%;
    object-fit: contain;
    background: var(--white);
    border: 1px solid var(--border);
    flex: none;
}

.v3-hero__shot {
    width: 300px;
    align-self: stretch;
    min-height: 132px;
    object-fit: cover;
    border-radius: var(--v3-radius) 0 0 var(--v3-radius);
    flex: none;
}

/* ------------------------------------------------------- hero photo gallery */

/* The gallery occupies exactly the slot the single photo did — same 300px
   column, same rounded outer corners — so a university with one photo and one
   with nine draw the identical hero box. Controls only appear on hover (and
   always on touch, where there is no hover to wait for). */
.v3-gallery {
    position: relative;
    overflow: hidden;
    cursor: zoom-in;
    background: var(--bg-page);
}

.v3-gallery__track {
    position: absolute;
    inset: 0;
}

.v3-gallery__slide {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0;
    transition: opacity .35s ease;
    pointer-events: none;
}

.v3-gallery__slide.is-active { opacity: 1; }

.v3-gallery__nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 30px;
    height: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(15, 23, 42, 0.55);
    color: #fff;
    cursor: pointer;
    opacity: 0;
    transition: opacity .2s ease, background .2s ease;
}

.v3-gallery__nav .v3-i { width: 17px; height: 17px; stroke-width: 2.2; }
.v3-gallery__nav:hover { background: rgba(15, 23, 42, 0.8); }
.v3-gallery__nav--prev { left: 8px; }
.v3-gallery__nav--next { right: 8px; }

.v3-gallery:hover .v3-gallery__nav,
.v3-gallery__nav:focus-visible { opacity: 1; }

.v3-gallery__dots {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 9px;
    display: flex;
    justify-content: center;
    gap: 6px;
    z-index: 2;
}

.v3-gallery__dot {
    width: 6px;
    height: 6px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.5);
    cursor: pointer;
    transition: width .2s ease, background .2s ease;
}

.v3-gallery__dot.is-active {
    width: 16px;
    border-radius: 3px;
    background: #fff;
}

/* The only permanent control: without it nothing on a still photo says there
   are eight more behind it. */
.v3-gallery__count {
    position: absolute;
    top: 8px;
    right: 8px;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 8px;
    border-radius: 999px;
    background: rgba(15, 23, 42, 0.6);
    color: #fff;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.6;
    pointer-events: none;
}

.v3-gallery__count .v3-i { width: 13px; height: 13px; vertical-align: 0; }

/* ----------------------------------------------------------- photo lightbox */

.v3-lightbox {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(8, 11, 20, 0.92);
    opacity: 0;
    transition: opacity .2s ease;
}

.v3-lightbox.is-open { opacity: 1; }

.v3-lightbox__figure {
    max-width: min(1200px, 92vw);
    max-height: 86vh;
    display: flex;
    align-items: center;
    justify-content: center;
}

.v3-lightbox__img {
    max-width: min(1200px, 92vw);
    max-height: 86vh;
    object-fit: contain;
    border-radius: 10px;
    box-shadow: 0 24px 60px rgba(0, 0, 0, .5);
    user-select: none;
    -webkit-user-drag: none;
}

.v3-lightbox__nav,
.v3-lightbox__close {
    position: absolute;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
    cursor: pointer;
    transition: background .2s ease;
}

.v3-lightbox__nav:hover,
.v3-lightbox__close:hover { background: rgba(255, 255, 255, 0.26); }

.v3-lightbox__nav {
    top: 50%;
    transform: translateY(-50%);
    width: 48px;
    height: 48px;
}

.v3-lightbox__nav .v3-i { width: 26px; height: 26px; stroke-width: 2; }
.v3-lightbox__nav--prev { left: 20px; }
.v3-lightbox__nav--next { right: 20px; }

.v3-lightbox__close { top: 18px; right: 20px; width: 40px; height: 40px; }
.v3-lightbox__close .v3-i { width: 22px; height: 22px; stroke-width: 2; }

.v3-lightbox__count {
    position: absolute;
    bottom: 22px;
    left: 50%;
    transform: translateX(-50%);
    color: rgba(255, 255, 255, .85);
    font-size: 13px;
    font-weight: 600;
    letter-spacing: .02em;
}

/* A film strip only earns its space when there is something to scan. */
.v3-lightbox__strip {
    position: absolute;
    bottom: 52px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 8px;
    max-width: 92vw;
    overflow-x: auto;
    padding: 4px;
    scrollbar-width: none;
}

.v3-lightbox__strip::-webkit-scrollbar { display: none; }

.v3-lightbox__thumb {
    width: 64px;
    height: 44px;
    flex: none;
    padding: 0;
    border: 2px solid transparent;
    border-radius: 6px;
    overflow: hidden;
    background: none;
    opacity: .5;
    cursor: pointer;
    transition: opacity .2s ease, border-color .2s ease;
}

.v3-lightbox__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.v3-lightbox__thumb:hover { opacity: .85; }
.v3-lightbox__thumb.is-active { opacity: 1; border-color: #fff; }

body.v3-lightbox-open { overflow: hidden; }

@media (max-width: 820px) {
    /* No hover on a phone — the arrows would otherwise never appear. */
    .v3-gallery__nav { opacity: 1; }

    .v3-lightbox__nav { width: 40px; height: 40px; }
    .v3-lightbox__nav--prev { left: 8px; }
    .v3-lightbox__nav--next { right: 8px; }
    .v3-lightbox__strip { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    .v3-gallery__slide,
    .v3-lightbox { transition: none; }
}

.v3-hero__main {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 22px 26px;
}

.v3-hero__logo + .v3-hero__main,
.v3-hero__shot + .v3-hero__main { padding-left: 30px; }

.v3-hero__name {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 30px;
    font-weight: 800;
    color: var(--text-primary);
    margin: 0 0 8px;
    line-height: 1.2;
}

.v3-verified {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 12px;
    font-weight: 600;
    color: var(--v3-accent);
    border: 1px solid var(--v3-accent);
    border-radius: 20px;
    padding: 3px 11px;
}

.v3-verified .v3-i { width: 14px; height: 14px; }

.v3-hero__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 18px;
    color: var(--text-secondary);
    font-size: 14px;
}

.v3-hero__meta span { display: inline-flex; align-items: center; gap: 6px; }
.v3-hero__meta .v3-sep { color: var(--border-strong); }

.v3-hero__side {
    display: flex;
    align-items: center;
    gap: 0;
    flex: none;
    text-align: center;
    padding: 22px 30px 22px 0;
}

/* A hairline between the ranking and the student count, as in the reference —
   without it the two numbers read as one four-part figure. */
.v3-hero__side > * + * { border-left: 1px solid var(--border); }
.v3-hero__stat { padding: 2px 26px; }
.v3-hero__stat:last-child { padding-right: 0; }

/* The student count is the one KPI the reference gives an icon to. */
.v3-hero__stat--icon { display: flex; align-items: center; gap: 12px; text-align: left; }
.v3-hero__stat--icon .v3-i { width: 28px; height: 28px; color: var(--text-primary); flex: none; }

.v3-hero__stat strong {
    display: block;
    font-size: 22px;
    font-weight: 800;
    color: var(--text-primary);
    line-height: 1.1;
}

.v3-hero__stat span { font-size: 12px; color: var(--text-muted); }

/* -------------------------------------------------------------------- tabs */

.v3-tabs {
    display: flex;
    gap: 38px;
    padding: 0 26px;
    border-top: 1px solid var(--border);
    overflow-x: auto;
    scrollbar-width: none;
}

/* The university tab bar is a panel of its own (see university-detail.php), so
   it carries no divider of its own and the row is its whole height. */
.v3-tabpanel .v3-tabs { border-top: 0; }
.v3-tabpanel { margin-bottom: 18px; }

.v3-tabs::-webkit-scrollbar { display: none; }

.v3-tab {
    padding: 15px 4px;
    font-size: 15px;
    font-weight: 500;
    color: var(--text-secondary);
    text-decoration: none;
    border-bottom: 3px solid transparent;
    white-space: nowrap;
}

.v3-tab:hover { color: var(--text-primary); }

.v3-tab.is-active {
    color: var(--v3-accent);
    border-bottom-color: var(--v3-accent);
}

/* ------------------------------------------------------------- stat strip */

.v3-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    border-bottom: 1px solid var(--border);
}

.v3-stat {
    padding: 18px 20px;
    text-align: center;
    border-right: 1px solid var(--border);
}

.v3-stat:last-child { border-right: 0; }
.v3-stat span { display: block; font-size: 13px; color: var(--text-secondary); margin-bottom: 5px; }

.v3-stat strong {
    font-size: 25px;
    font-weight: 800;
    color: var(--v3-accent);
    line-height: 1.1;
}

/* The PROGRAMME strip (Duration / Quota / Routes / Language / Start) is set in
   ink, not accent — those are five facts of equal weight, and colouring them
   all blue makes the row read as five links. The UNIVERSITY strip above keeps
   the accent, because there the numbers ARE the headline. */
.v3-stat small { font-size: 17px; color: var(--text-primary); font-weight: 700; }

/* --------------------------------------------------------- fact / mini cards */

.v3-cols {
    display: grid;
    grid-template-columns: 1.25fr 1fr 1.1fr;
    gap: 26px;
    align-items: start;
}

/* The reference separates the two fact columns with a hairline, which is what
   stops "Academic scale" and "Global community" reading as one long list. */
.v3-factlist { display: flex; gap: 14px; }
.v3-factlist + .v3-factlist { border-left: 1px solid var(--border); padding-left: 28px; }
.v3-factlist > div { min-width: 0; }

.v3-factlist h3 { margin-top: 0; }

.v3-fact-row {
    display: flex;
    align-items: baseline;
    gap: 10px;
    padding: 5px 0;
    font-size: 14px;
    color: var(--text-secondary);
}

.v3-fact-row b {
    color: var(--v3-accent);
    font-weight: 700;
    min-width: 34px;
    text-align: right;
    flex: none;
}

.v3-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 14px;
}

.v3-card {
    display: flex;
    align-items: center;
    gap: 13px;
    padding: 15px 17px;
    border: 1px solid var(--border);
    border-radius: var(--v3-radius-sm);
    background: var(--bg-surface);
}

.v3-card strong { display: block; font-size: 14px; color: var(--text-primary); }
.v3-card span   { font-size: 13px; color: var(--text-secondary); }
.v3-card b      { font-size: 21px; font-weight: 800; color: var(--v3-accent); }

.v3-card--tall { align-items: flex-start; }

/* Domains and learning formats are short labels; the reference fits five across.
   The default 240px track would break them onto a second row with one orphan. */
/* The fact and domain cards carry a sentence, not a heading — the reference
   sets them at normal weight. Only the campus and stage cards, where the first
   line really is a name, stay bold. */
.v3-cards--plain .v3-card strong { font-weight: 400; }

/* Cards that are three lines of text and no icon — the housing and living
   cards on the Costs tab. */
.v3-cards--text .v3-card { display: block; }
.v3-cards--text .v3-card strong { display: block; margin-bottom: 6px; }
.v3-cards--text .v3-card span { display: block; }
.v3-card__amount { color: var(--text-primary); margin-bottom: 4px; }

/* Title on the left, controls on the right, one line. */
.v3-sectionhead {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    flex-wrap: wrap;
}

.v3-sectionhead h2 { margin: 0; }
.v3-cards--plain .v3-card { align-items: center; gap: 18px; }

.v3-cards--tight { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 14px; }
.v3-cards--tight .v3-card { padding: 14px; gap: 12px; }
.v3-cards--tight .v3-icon-circle--outline { width: 40px; height: 40px; }
.v3-cards--tight .v3-icon-circle--outline .v3-i { width: 20px; height: 20px; }

/* --------------------------------------------------------------- catalogue */

.v3-catalog {
    display: grid;
    grid-template-columns: 268px 1fr;
    gap: var(--v3-gap);
    align-items: start;
}

.v3-filters { padding: 20px 22px; }

.v3-filters__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 14px;
}

.v3-filters__head h2 { margin: 0; }

.v3-linkbtn {
    background: none;
    border: 0;
    padding: 0;
    color: var(--v3-accent);
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
}

.v3-fgroup { border-top: 1px solid var(--border); padding: 15px 0; }
.v3-fgroup:first-of-type { border-top: 0; padding-top: 0; }

.v3-fgroup__title {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 14px;
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 10px;
}

/* The chevron is decoration, not a control: these groups are always open, and
   a filter that looks collapsible but is not would be worse than no chevron. */
.v3-fgroup__title .v3-i { width: 16px; height: 16px; color: var(--text-muted); }

.v3-check {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 5px 0;
    font-size: 14px;
    color: var(--text-secondary);
    cursor: pointer;
}

.v3-check input { width: 16px; height: 16px; accent-color: var(--v3-accent); }

.v3-selectwrap { position: relative; }

.v3-select {
    width: 100%;
    /* Room for the chevron drawn below — without it the text runs under the
       arrow on a long option like "Agriculture, Biology, Food Science…". */
    padding: 9px 36px 9px 11px;
    border: 1px solid var(--input-border);
    border-radius: var(--v3-radius-sm);
    background: var(--input-bg);
    color: var(--text-primary);
    font-size: 14px;
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
}

/* Edge/IE still draw their own expand arrow next to ours. */
.v3-select::-ms-expand { display: none; }

.v3-selectwrap > .v3-i {
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-50%);
    width: 16px;
    height: 16px;
    color: var(--text-muted);
    pointer-events: none;
}

.v3-fgroup small { display: block; margin-top: 7px; color: var(--text-muted); font-size: 12px; }

.v3-results__head {
    display: block;
    padding: 22px 26px 16px;
    border-bottom: 1px solid var(--border);
}

.v3-results__head h2 { margin: 0 0 6px; }
.v3-results__head span { font-size: 14px; color: var(--text-secondary); }

/* --------------------------------------------------------- programme row */

.v3-row {
    display: grid;
    /* Fixed columns, not fr-of-content. Each row is its own grid, so
       content-sized columns make every row a different shape and the "intake"
       and "application period" headings walk left and right down the list.
       Fixed widths are what make the list read as a table. */
    grid-template-columns: minmax(230px, 1fr) 168px 210px 132px;
    gap: 18px;
    align-items: center;
    padding: 18px 24px;
    border-top: 1px solid var(--border);
}

.v3-row__id { display: flex; align-items: center; gap: 14px; min-width: 0; }
.v3-row__id > div { min-width: 0; flex: 1 1 auto; }

.v3-row__logo {
    width: 52px;
    height: 52px;
    border-radius: 50%;
    object-fit: contain;
    background: var(--white);
    border: 1px solid var(--border);
    flex: none;
}

.v3-row__name {
    font-size: 16px;
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 6px;
    line-height: 1.3;
}

.v3-tag {
    display: inline-block;
    font-size: 12px;
    color: var(--v3-accent);
    border: 1px solid var(--v3-accent);
    border-radius: 20px;
    padding: 2px 11px;
    margin: 2px 5px 2px 0;
}

.v3-row__cell { font-size: 13px; color: var(--text-secondary); border-left: 1px solid var(--border); padding-left: 18px; }
.v3-row__cell strong { display: block; font-size: 13px; color: var(--text-primary); margin-bottom: 4px; }
.v3-row__cell div { padding: 2px 0; display: flex; align-items: center; gap: 6px; }

.v3-status {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: 6px;
    font-size: 12px;
    font-weight: 600;
    color: var(--accent-soft-green);
    background: rgba(16, 185, 129, 0.1);
    border-radius: 20px;
    padding: 3px 11px;
}

.v3-status .v3-i { width: 9px; height: 9px; fill: currentColor; }

/* ----------------------------------------------------------------- buttons */

.v3-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 11px 20px;
    border-radius: var(--v3-radius-sm);
    font-size: 14px;
    font-weight: 700;
    text-decoration: none;
    border: 1px solid transparent;
    cursor: pointer;
    white-space: nowrap;
}

.v3-btn--primary { background: var(--navy-900, #0a1628); color: #fff; }
.v3-btn--primary:hover { background: var(--navy-800, #10233f); color: #fff; }

/* In dark mode the navy fill sits almost on top of the surface colour, so a
   primary button reads as an outline and the active route filter stops looking
   selected. The accent carries the emphasis instead. */
:root[data-theme="dark"] .v3-btn--primary,
:root[data-theme="dark"] .v3-routefilter a.is-active {
    background: var(--v3-accent);
    border-color: var(--v3-accent);
    color: #0b1020;
}

:root[data-theme="dark"] .v3-btn--primary:hover { background: #9dbcff; color: #0b1020; }

.v3-btn--ghost {
    background: transparent;
    color: var(--text-primary);
    border-color: var(--border-strong);
}

.v3-btn--ghost:hover { background: var(--bg-surface-2); color: var(--text-primary); }

.v3-btn--block { width: 100%; }

/* ------------------------------------------------------- programme header */

.v3-prog-head { padding: 24px 26px; display: flex; align-items: flex-start; gap: 20px; }
.v3-prog-head__main { flex: 1 1 auto; min-width: 0; }

.v3-prog-head h1 {
    font-size: 30px;
    font-weight: 800;
    color: var(--text-primary);
    margin: 0 0 12px;
    line-height: 1.2;
}

.v3-prog-head p { color: var(--text-secondary); font-size: 14px; line-height: 1.6; margin: 12px 0 0; max-width: 78ch; }

.v3-chips { display: flex; flex-wrap: wrap; gap: 9px; }

.v3-chip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 13px;
    font-weight: 600;
    color: var(--v3-accent);
    border: 1px solid var(--v3-accent);
    border-radius: var(--v3-radius-sm);
    padding: 6px 13px;
}

.v3-chip--muted { color: var(--text-secondary); border-color: var(--border-strong); }
.v3-chip--status { color: var(--accent-soft-purple); border-color: var(--border-strong); }
.v3-chip--status::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: currentColor; }

/* ------------------------------------------------------- split with side-nav */

/* `stretch`, not `start`: in the reference the section list runs the full height
   of the content beside it. Left at `start` the aside is a short card with a
   column of empty page under it. */
.v3-split { display: grid; grid-template-columns: 280px 1fr; gap: var(--v3-gap); align-items: stretch; }

.v3-sidenav { padding: 20px 0; }
.v3-sidenav h2 { margin: 0 18px 12px; }

.v3-sidenav a {
    display: block;
    padding: 10px 18px;
    font-size: 14px;
    color: var(--text-secondary);
    text-decoration: none;
    border-left: 3px solid transparent;
}

.v3-sidenav a.is-active {
    color: var(--v3-accent);
    background: var(--v3-accent-weak);
    border-left-color: var(--v3-accent);
    font-weight: 600;
}

.v3-sidenav hr { border: 0; border-top: 1px solid var(--border); margin: 12px 18px; }

/* ------------------------------------------------------------------ journey */

.v3-journey {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 18px;
    position: relative;
    margin: 22px 0 8px;
}

.v3-stage { position: relative; }

.v3-stage__label { font-size: 14px; font-weight: 700; color: var(--v3-accent); margin-bottom: 12px; }

/* Dot and connector on one flex line. Drawing the rail per stage rather than as
   one absolute line across the grid is what keeps the line meeting the dot
   centres — an earlier version guessed the offsets as percentages and missed
   both ends. */
.v3-stage__rail { display: flex; align-items: center; margin-bottom: 14px; }

.v3-stage__rail::after {
    content: "";
    flex: 1 1 auto;
    height: 2px;
    background: var(--v3-accent);
    opacity: 0.35;
    margin-right: -18px;   /* reach across the grid gap to the next dot */
}

.v3-stage:last-child .v3-stage__rail::after { display: none; }

.v3-stage__dot {
    width: 15px;
    height: 15px;
    border-radius: 50%;
    border: 2px solid var(--v3-accent);
    background: var(--bg-surface);
    position: relative;
    z-index: 1;
    flex: none;
}

.v3-stage ul { list-style: none; padding: 0; margin: 0; }

.v3-stage li {
    position: relative;
    padding-left: 15px;
    font-size: 13px;
    color: var(--text-secondary);
    line-height: 1.5;
    margin-bottom: 7px;
}

.v3-stage li::before {
    content: "";
    position: absolute;
    left: 0;
    top: 8px;
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background: var(--text-muted);
}

/* -------------------------------------------------------------- data table */

/* Horizontal rules only. The reference draws these tables as a bordered block
   with hairlines BETWEEN ROWS and nothing between columns — a full grid turns a
   five-row table into forty boxes, and the eye starts reading the lines instead
   of the values. */
.v3-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: 14px;
    border: 1px solid var(--border);
    border-radius: var(--v3-radius-sm);
    overflow: hidden;
}

.v3-table th {
    text-align: left;
    font-weight: 500;
    font-size: 13px;
    color: var(--text-secondary);
    padding: 12px 18px;
    background: var(--bg-surface-2);
    border-bottom: 1px solid var(--border);
}

.v3-table td {
    padding: 13px 18px;
    border-bottom: 1px solid var(--border);
    color: var(--text-secondary);
    vertical-align: top;
}

.v3-table tr:last-child td { border-bottom: 0; }

.v3-table td:first-child { font-weight: 400; color: var(--text-primary); }

/* Where the first column is a NAME rather than a label — the accepted tests and
   qualifications — the reference sets it in bold. */
.v3-table--named td:first-child { font-weight: 600; }

.v3-scroll-x { overflow-x: auto; }

/* The reference rules off the last block of a long tab. */
.v3-rule { border: 0; border-top: 1px solid var(--border); margin: 26px 0 0; }

/* Five short scores side by side — the English-proficiency block. */
.v3-scorerow {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    border: 1px solid var(--border);
    border-radius: var(--v3-radius-sm);
    overflow: hidden;
    margin: 14px 0 6px;
}

.v3-scorerow > div { padding: 14px 18px; }
.v3-scorerow > div + div { border-left: 1px solid var(--border); }
.v3-scorerow strong { display: block; color: var(--text-primary); font-weight: 600; margin-bottom: 6px; }
.v3-scorerow span { display: block; font-size: 14px; color: var(--text-secondary); }
.v3-scorerow small { display: block; font-size: 12px; color: var(--text-muted); margin-top: 4px; }

/* ---------------------------------------------------------------- timeline */

.v3-tl-controls { display: flex; justify-content: flex-end; margin: 12px 0 6px; }

.v3-tl-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    margin-bottom: 18px;
}

.v3-tl-head h2 { margin: 0; }

.v3-routefilter { display: flex; flex-wrap: wrap; gap: 8px; }

.v3-routefilter a {
    padding: 7px 15px;
    border-radius: var(--v3-radius-sm);
    border: 1px solid var(--border-strong);
    font-size: 13px;
    font-weight: 600;
    color: var(--text-secondary);
    text-decoration: none;
}

/* "All routes" is not a route: it stays neutral, and the reference fills it in
   the page's own dark navy. */
.v3-routefilter a.is-active {
    background: var(--navy-900, #0a1628);
    border-color: var(--navy-900, #0a1628);
    color: #fff;
}

/* A named route carries the SAME tone as the pills on the rows it filters —
   Route A blue, Route B violet. Outlined when it is an option, filled when it
   is the current view. Without this the filter and the rows look like two
   unrelated controls. */
.v3-routefilter--route-a    { border-color: var(--accent-soft-blue);   color: var(--accent-soft-blue); }
.v3-routefilter--route-b    { border-color: var(--accent-soft-purple); color: var(--accent-soft-purple); }
.v3-routefilter--external   { border-color: var(--accent-soft-orange); color: var(--accent-soft-orange); }
.v3-routefilter--embassy    { border-color: var(--accent-soft-gold);   color: var(--accent-soft-gold); }
.v3-routefilter--university { border-color: var(--accent-soft-green);  color: var(--accent-soft-green); }

.v3-routefilter--route-a.is-active    { background: var(--accent-soft-blue);   border-color: var(--accent-soft-blue); }
.v3-routefilter--route-b.is-active    { background: var(--accent-soft-purple); border-color: var(--accent-soft-purple); }
.v3-routefilter--external.is-active   { background: var(--accent-soft-orange); border-color: var(--accent-soft-orange); }
.v3-routefilter--embassy.is-active    { background: var(--accent-soft-gold);   border-color: var(--accent-soft-gold); }
.v3-routefilter--university.is-active { background: var(--accent-soft-green);  border-color: var(--accent-soft-green); }
.v3-routefilter a[class*="v3-routefilter--"].is-active { color: #fff; }

/* In dark mode the soft tones are pale, so white on them is unreadable —
   the same swap the primary button makes. */
:root[data-theme="dark"] .v3-routefilter a[class*="v3-routefilter--"].is-active { color: #0b1020; }

.v3-timeline { position: relative; padding-left: 30px; }

.v3-timeline::before {
    content: "";
    position: absolute;
    left: 7px;
    top: 12px;
    bottom: 12px;
    width: 1px;
    background: var(--border-strong);
}

.v3-tl-row {
    display: grid;
    /* Same reasoning as .v3-row — the date column is fixed so the event titles
       form a single left edge down the rail. */
    grid-template-columns: 330px 1fr 112px;
    gap: 16px;
    align-items: center;
    padding: 13px 0;
    border-bottom: 1px solid var(--border);
    position: relative;
}

.v3-tl-row:last-child { border-bottom: 0; }

.v3-tl-row::before {
    content: "";
    position: absolute;
    left: -30px;
    top: 20px;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    border: 1.5px solid var(--border-strong);
    background: var(--bg-surface);
}

.v3-tl-date  { font-size: 14px; color: var(--text-primary); font-weight: 400; }
/* Regular weight: the DATE is what the eye scans down this column, and
   bolding every title turns the whole page into emphasis. */
.v3-tl-event strong { display: block; font-size: 14px; font-weight: 400; color: var(--text-primary); }
.v3-tl-event small  { display: block; font-size: 13px; color: var(--text-muted); margin-top: 3px; }

.v3-pill {
    display: inline-block;
    justify-self: end;
    font-size: 12px;
    font-weight: 600;
    padding: 4px 12px;
    border-radius: 20px;
    background: var(--bg-surface-2);
    color: var(--text-secondary);
    white-space: nowrap;
}

.v3-pill--route-a    { background: rgba(29, 78, 216, 0.12); color: var(--accent-soft-blue); }
.v3-pill--route-b    { background: rgba(126, 34, 206, 0.12); color: var(--accent-soft-purple); }
.v3-pill--external   { background: rgba(194, 65, 12, 0.12);  color: var(--accent-soft-orange); }
.v3-pill--embassy    { background: rgba(180, 83, 9, 0.12);   color: var(--accent-soft-gold); }
.v3-pill--university { background: rgba(4, 120, 87, 0.12);   color: var(--accent-soft-green); }

/* ------------------------------------------------------------------ notices */

.v3-notice {
    display: flex;
    align-items: flex-start;
    gap: 11px;
    padding: 13px 16px;
    border-radius: var(--v3-radius-sm);
    background: var(--v3-accent-weak);
    color: var(--text-secondary);
    font-size: 13px;
    line-height: 1.55;
    margin: 16px 0;
}

.v3-notice .v3-i { color: var(--v3-accent); margin-top: 1px; }
.v3-notice--warn { background: rgba(245, 158, 11, 0.10); }
.v3-notice--warn .v3-i { color: var(--accent-soft-gold); }

.v3-footnote {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 18px;
    padding: 14px 26px;
    border-top: 1px solid var(--border);
    font-size: 12px;
    color: var(--text-muted);
}

/* The reference writes the figure period and the ranking year as one sentence
   separated by a middle dot, not as two loose fragments. */
.v3-footnote--inline span + span::before { content: '·'; margin-right: 18px; }

/* Where the reference puts one note on the left and the update stamp on the
   right of the same line. */
.v3-footnote--split { justify-content: space-between; flex-wrap: nowrap; gap: 24px; }
.v3-footnote--split > span:last-child { text-align: right; flex: none; }

.v3-sources { padding: 14px 26px; font-size: 12px; color: var(--text-muted); }
.v3-sources a { color: var(--v3-accent); overflow-wrap: break-word; }

/* ------------------------------------------------------------- empty state */

.v3-empty {
    text-align: center;
    padding: 46px 24px;
    color: var(--text-muted);
}

.v3-empty .v3-i { width: 30px; height: 30px; margin-bottom: 12px; color: var(--text-muted); }
.v3-empty p { font-size: 15px; font-weight: 600; color: var(--text-secondary); margin: 0 0 5px; }
.v3-empty span { font-size: 13px; }

/* ------------------------------------------------------- campuses & housing */

.v3-campus {
    display: grid;
    grid-template-columns: 180px 1fr auto;
    gap: 18px;
    align-items: center;
    padding: 16px 24px;
    border-top: 1px solid var(--border);
}

.v3-campus img { width: 100%; height: 104px; object-fit: cover; border-radius: var(--v3-radius-sm); }
.v3-campus strong { display: block; font-size: 16px; font-weight: 700; color: var(--text-primary); margin-bottom: 5px; }
.v3-campus p { font-size: 13px; color: var(--text-secondary); margin: 0; }
.v3-campus__actions { display: flex; flex-direction: column; gap: 8px; }

.v3-list-row {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 13px 0;
    border-bottom: 1px solid var(--border);
    font-size: 14px;
    color: var(--text-secondary);
}

.v3-list-row:last-child { border-bottom: 0; }

/* ---------------------------------------------------------------- currency */

.v3-currency { display: flex; gap: 8px; justify-content: flex-end; margin-bottom: 8px; }


/* ------------------------------------------------------- boxes and strips */

/* A bordered sub-panel INSIDE a panel — the reference uses one around the
   accommodation list and around each student-life group. */
.v3-box {
    border: 1px solid var(--border);
    border-radius: var(--v3-radius-sm);
    padding: 4px 18px;
    background: var(--bg-surface);
}

.v3-box__title {
    margin: 14px 0 6px;
    font-size: 15px;
    font-weight: 700;
    color: var(--text-primary);
}

.v3-box .v3-list-row:last-child { border-bottom: 0; }

/* Inside a themed student-life group the rows are a list of features, so they
   carry no dividers and no bold — the reference keeps the emphasis on the group
   title above them. */
.v3-box--plain .v3-list-row { border-bottom: 0; padding: 9px 0; }
.v3-box--plain .v3-list-row strong { font-weight: 400; }

/* The reference titles the two scholarship columns in accent blue — they are
   the two answers to one question, not two separate sections. */
.v3-panel .v3-box__title--accent,
.v3-panel .v3-h3--accent { color: var(--v3-accent); }

.v3-bullets { list-style: none; margin: 0 0 14px; padding: 0; }

.v3-bullets li {
    position: relative;
    padding-left: 18px;
    margin-bottom: 8px;
    font-size: 14px;
    line-height: 1.5;
    color: var(--text-primary);
}

.v3-bullets li::before {
    content: "";
    position: absolute;
    left: 2px;
    top: 9px;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--text-muted);
}

.v3-box__foot {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px 0 14px;
}

/* The three themed cards side by side (Gap Term / Studio / Housing). */
.v3-groupgrid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
    gap: 16px;
    align-items: start;
    margin-top: 20px;
}

/* Two wide columns instead of three — the scholarships tab. */
.v3-groupgrid--2 { grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); }

/* The numbered journey across the top of Student Life. */
/* One bordered band, not four cards. The reference draws the journey as a
   single strip with chevron notches between the steps — four separate cards
   read as four unrelated facts rather than one sequence. */
.v3-strip {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 0;
    margin: 18px 0 4px;
    border: 1px solid var(--border);
    border-radius: var(--v3-radius-sm);
    background: var(--bg-surface);
    overflow: hidden;
}

.v3-strip__step {
    position: relative;
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 18px 20px;
}

/* The chevron the reference draws between the steps. Decorative, so it is a
   pseudo-element and never reaches the accessibility tree. */
/* A FULL-HEIGHT chevron between the steps, not a small arrowhead floating in
   the middle — in the reference the notch runs the whole height of the band and
   is what makes the four steps read as one ribbon moving left to right. The
   clip-path draws a thin outline, so it stays a hairline in both themes. */
.v3-strip__step + .v3-strip__step::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 26px;
    background: var(--border);
    clip-path: polygon(
        0 0,
        100% 50%,
        0 100%,
        0 calc(100% - 2px),
        calc(100% - 4px) 50%,
        0 2px
    );
}

.v3-strip__step + .v3-strip__step { padding-left: 34px; }

.v3-strip__n {
    display: grid;
    place-items: center;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    border: 1.5px solid var(--v3-accent);
    font-size: 12px;
    font-weight: 700;
    color: var(--v3-accent);
    flex: none;
}

.v3-strip__step strong {
    display: block;
    font-size: 14px;
    font-weight: 500;
    color: var(--text-primary);
    line-height: 1.45;
}

.v3-strip__step span { font-size: 13px; color: var(--text-secondary); }

/* -------------------------------------------------------------- responsive */

@media (max-width: 1080px) {
    .v3-cols     { grid-template-columns: 1fr; }
    .v3-catalog,
    .v3-split    { grid-template-columns: 1fr; }
    .v3-sidenav  { display: none; }

    /* Stacked, the divider between the two fact columns becomes a stray line
       down the left of the second one. */
    .v3-factlist + .v3-factlist { border-left: 0; padding-left: 0; }
}

@media (max-width: 820px) {
    .v3-hero { flex-wrap: wrap; }
    /* Stacked, the photo sits on TOP of the card, so its rounded pair of
       corners moves from the left edge to the top edge. */
    .v3-hero__shot {
        width: 100%;
        height: 190px;
        min-height: 190px;
        border-radius: var(--v3-radius) var(--v3-radius) 0 0;
    }
    .v3-hero__side { width: 100%; justify-content: flex-start; text-align: left; }
    .v3-hero__name { font-size: 24px; }

    .v3-row {
        grid-template-columns: 1fr;
        gap: 12px;
    }
    .v3-row__cell { border-left: 0; padding-left: 0; border-top: 1px solid var(--border); padding-top: 12px; }
    .v3-row .v3-btn { width: 100%; }

    /* Stacked, the journey is a list of stages: no rail, because a connector
       drawn to the right of a dot with nothing after it is just a stray line. */
    .v3-journey { grid-auto-flow: row; }
    .v3-stage__rail::after { display: none; }

    .v3-strip { grid-auto-flow: row; }
    .v3-strip__step + .v3-strip__step { padding-left: 20px; }
    .v3-strip__step + .v3-strip__step::before { display: none; }
    .v3-strip__step + .v3-strip__step { border-top: 1px solid var(--border); }

    .v3-tl-row { grid-template-columns: 1fr; gap: 6px; }
    .v3-tl-row::before { top: 16px; }

    .v3-campus { grid-template-columns: 1fr; }
    .v3-campus__actions { flex-direction: row; }

    .v3-stat { border-right: 0; border-bottom: 1px solid var(--border); }
    .v3-panel__body { padding: 18px; }

    /* WRAP, don't scroll. Seven programme tabs cannot fit one line on a phone,
       and a row that scrolls sideways hides the tabs past the edge with nothing
       to say they are there. Two visible lines beat one line and a secret. */
    .v3-tabs {
        flex-wrap: wrap;
        overflow-x: visible;
        gap: 2px 18px;
        padding: 4px 14px 0;
    }

    .v3-tab { padding: 10px 2px; font-size: 14px; }

    .v3-iconrow > div { padding: 10px 0; }
    .v3-iconrow > div + div { border-left: 0; border-top: 1px solid var(--border); }
    .v3-iconrow--boxed { padding: 4px 16px; }

    .v3-scorerow { grid-template-columns: 1fr; }
    .v3-scorerow > div + div { border-left: 0; border-top: 1px solid var(--border); }

    .v3-tl-row { grid-template-columns: 1fr; }
    .v3-hero__side { padding: 0 20px 20px; }

    /* An odd last cell alone in a two-column strip reads as a missing cell. */
    .v3-stats > .v3-stat:last-child:nth-child(odd) { grid-column: 1 / -1; }

    /* Let the table SCROLL at a legible width instead of squeezing four words
       onto four lines. Squeezing loses the notes column silently; scrolling at
       least keeps every column readable — and the line below says so, because a
       column cut off at the edge with no hint reads as a broken table. */
    .v3-scroll-x .v3-table { min-width: 520px; }

    .v3-scroll-x::after {
        content: "Swipe to see all columns";
        position: sticky;
        left: 0;
        display: block;
        width: max-content;
        padding: 7px 2px 0;
        font-size: 12px;
        color: var(--text-muted);
    }

    .v3-footnote--inline span + span::before { display: none; }
    .v3-sectionhead { align-items: flex-start; }
    .v3-currency { flex-wrap: wrap; }
}

/* Keyboard focus must stay visible — these pages are a lot of links in a row. */
.v3-page a:focus-visible,
.v3-page button:focus-visible,
.v3-page select:focus-visible,
.v3-page input:focus-visible {
    outline: 2px solid var(--v3-accent);
    outline-offset: 2px;
}
