
:root {
    /* Public type is the platform system font (SF Pro on Apple devices, Segoe UI
       on Windows, Roboto on Android), so no web font is downloaded. Sizes get
       their own tracking and leading: large titles are tight, body stays at 0. */
    --font-sans-public: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif;
    /* 5.56:1 against white, so it works as text and behind white text. */
    --color-brand-primary: #0967C7;
    /* Space-separated channels for rgb(var(...) / alpha). */
    --color-brand-primary-rgb: 9 103 199;
    /* Hover goes darker, so white labels and blue links keep their contrast. */
    --color-brand-primary-hover: #07559F;
    /* Pale enough for brand-blue text on it (4.58:1). */
    --color-brand-primary-soft: #DCEBF8;
    --color-brand-primary-grey: #EBF3FA;
    --color-brand-primary-border: #8BBDEB;
    --color-brand-white: #FFFFFF;
    --color-focus-ring: #F59E0B;
    /* Amber alone is 2.1:1 on white. The near-black line inside it carries the
       ring on light surfaces (16:1) and the amber carries it on dark ones. */
    --color-focus-ring-inner: #111827;
    --color-surface-navigation: #FFFFFF;
    --color-surface-hero-control: #F8FAFC;
    --color-text-on-hero: #F8FAFC;
    --shadow-public-card:
        0 1px 3px rgb(29 57 86 / 0.12),
        0 7px 16px -4px rgb(var(--color-brand-primary-rgb) / 0.16);
    --shadow-public-card-hover:
        0 2px 4px rgb(29 57 86 / 0.14),
        0 9px 20px -5px rgb(var(--color-brand-primary-rgb) / 0.2);
}

.public-home .bg-section-primary {
    background-color: var(--color-brand-white);
}
.public-home .bg-section-alt {
    background-color: var(--color-brand-primary-grey);
}

/* Shared heading hierarchy for homepage content sections. */
.public-home .home-section-label {
    margin: 0;
    color: var(--color-brand-primary);
    font-size: clamp(0.875rem, 0.75rem + 0.5vw, 1.25rem);
    font-weight: 600;
    line-height: 1.5;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}
.public-home .home-section-title {
    margin: 0.75rem 0 0;
    color: #000;
    font-size: 2rem;
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: -0.02em;
    text-wrap: balance;
}
/* Quote-sized text (18-20px): regular weight, no tracking adjustment. From xl the
   quote is the hero of the row, so it steps up with tighter leading and tracking. */
.public-home .testimonial-quote {
    font-weight: 400;
    letter-spacing: 0;
}
@media (min-width: 1280px) {
    .public-home .testimonial-quote {
        font-size: 1.75rem;
        font-weight: 500;
        line-height: 1.3;
        letter-spacing: -0.02em;
    }
}
.public-home #section1 .home-section-title,
.public-home #school-dna-title,
.public-home #learning-partners-title,
.public-home #stories-title,
.public-home #testimonials-title,
.public-home #faq-title {
    font-size: clamp(1.5rem, 0.78rem + 2.475vw, 3rem);
}
/* These titles have no eyebrow label above them, so the label-to-title
   spacing baked into .home-section-title's margin-top would otherwise just
   add extra gap from the previous section. */
.public-home #stories-title,
.public-home #testimonials-title,
.public-home #faq-title {
    margin-top: 0;
}
@media (min-width: 640px) {
    .public-home .home-section-title { font-size: 2.5rem; }
}
@media (min-width: 1024px) {
    .public-home .home-section-title {
        margin-top: 1rem;
        font-size: 3rem;
    }
}

/* FAQ: the + turns into a − as its vertical bar collapses, and the answer fades
   in. Opacity/transform only, and neither runs under reduced motion. */
.public-home .faq-plus-bar {
    transform-box: fill-box;
    transform-origin: center;
}
@media (prefers-reduced-motion: no-preference) {
    .public-home .faq-plus-bar {
        transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    }
    .public-home details[open] .faq-answer {
        animation: faq-answer-in 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    }
}
.public-home details[open] .faq-plus-bar,
.public-home .dna-card:is(:hover, :focus-within) .faq-plus-bar {
    transform: scaleY(0);
}
@keyframes faq-answer-in {
    from { opacity: 0; transform: translateY(-4px); }
}
/* Where the browser can animate a <details> body (::details-content), the answer
   folds shut along the same path it opened on instead of vanishing. Elsewhere it
   closes instantly, as before. */
@supports selector(::details-content) {
    @media (prefers-reduced-motion: no-preference) {
        .public-home #faq {
            interpolate-size: allow-keywords;
        }
        .public-home #faq details::details-content {
            block-size: 0;
            overflow: hidden;
            transition: block-size 0.3s cubic-bezier(0.4, 0, 0.2, 1), content-visibility 0.3s allow-discrete;
        }
        .public-home #faq details[open]::details-content {
            block-size: auto;
        }
    }
}

/* Why SMP cards. Below lg they are a scroll-snap carousel (after cloaked.com on mobile):
   an image tile with the copy underneath, always open, the next card peeking at the edge.
   From lg they are overlay cards (also after cloaked.com): the artwork blurs under a veil,
   the title rides up and the copy unfolds beneath it (grid row 0fr -> 1fr), and the + becomes
   a white −. Hover and keyboard focus (the card itself) open a card; touch has no hover, so there
   every card rests open. Opacity, grid-row and transform only; none run under reduced motion. */
.public-home .dna-rail {
    scrollbar-width: none;
    overscroll-behavior-inline: contain;
}
.public-home .dna-rail::-webkit-scrollbar {
    display: none;
}
.public-home .dna-card-scrim {
    background: linear-gradient(to top, rgb(7 23 40 / 0.8), transparent 60%);
}
.public-home .dna-card-veil {
    opacity: 0;
    background: rgb(7 23 40 / 0.55);
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
    -webkit-mask-image: linear-gradient(to top, #000 60%, transparent 100%);
    mask-image: linear-gradient(to top, #000 60%, transparent 100%);
}
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
    .public-home .dna-card-veil {
        background: rgb(7 23 40 / 0.85);
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
    }
}
.public-home .dna-card-reveal {
    grid-template-rows: 1fr;
}
@media (min-width: 1024px) {
    .public-home .dna-card-reveal {
        grid-template-rows: 0fr;
    }
}
@media (min-width: 1024px) and (prefers-reduced-motion: no-preference) {
    .public-home .dna-card-veil { transition: opacity 0.3s ease; }
    .public-home .dna-card-reveal { transition: grid-template-rows 0.3s ease; }
    .public-home .dna-card-icon { transition: background-color 0.3s ease, color 0.3s ease; }
}
.public-home .dna-card:is(:hover, :focus-within) .dna-card-veil { opacity: 1; }
.public-home .dna-card:is(:hover, :focus-within) .dna-card-icon {
    background: #fff;
    color: var(--color-brand-primary);
}
@media (min-width: 1024px) {
    .public-home .dna-card:is(:hover, :focus-within) .dna-card-reveal { grid-template-rows: 1fr; }
}
@media (min-width: 1024px) and (hover: none) {
    .public-home .dna-card-veil { opacity: 1; }
    .public-home .dna-card-reveal { grid-template-rows: 1fr; }
    .public-home .dna-card .faq-plus-bar { transform: scaleY(0); }
    .public-home .dna-card-icon { background: #fff; color: var(--color-brand-primary); }
}

/* Nothing on public pages should ever be wider than the viewport; without this,
   full-bleed elements (e.g. the homepage's .gallery-rail) let
   the whole page pan sideways instead of just clipping at the edge. */
html[class^="public-"],
body[class^="public-"] {
    overflow-x: hidden;
}

/* Learning partners logo strip: a free-dragging slide, like fundaacps's "We
   work with businesses..." row, not a paged rail with arrows. Native touch
   swipe and the pointer-drag handler in public.js (enableDragScroll) both
   just move scrollLeft, so there is no snapping to fight against. */
.public-home .partners-rail {
    display: grid;
    grid-auto-columns: max-content;
    grid-auto-flow: column;
    align-items: center;
    justify-content: safe center;
    gap: 3rem;
    overflow-x: auto;
    padding: 0.25rem 0.25rem 1rem;
    cursor: grab;
    scrollbar-width: none;
    overscroll-behavior-inline: contain;
}
.public-home .partners-rail::-webkit-scrollbar {
    display: none;
}
.public-home .partners-rail:focus-visible {
    outline: 3px solid var(--color-focus-ring);
    outline-offset: 2px;
    box-shadow: 0 0 0 2px var(--color-focus-ring-inner);
}
.public-home .partners-rail.is-dragging {
    cursor: grabbing;
    scroll-behavior: auto;
    user-select: none;
}
.public-home .partners-rail-item {
    min-width: 0;
    margin: 0;
}

/* Homepage */
html.public-home,
body.public-home {
    font-family: var(--font-sans-public);
    font-optical-sizing: auto;
    height: auto;
    margin: 0;
    padding: 0;
}

/* Anchor jumps are handled by the browser; this keeps the fixed header from
   covering the section that has just been jumped to. */
html.public-home,
html.public-detail,
html.public-news {
    scroll-padding-top: 5.5rem;
}

@media (prefers-reduced-motion: no-preference) {
    html.public-home,
    html.public-detail,
    html.public-news {
        scroll-behavior: smooth;
    }
}

html.public-news,
body.public-news {
    font-family: var(--font-sans-public);
    font-optical-sizing: auto;
    margin: 0;
    padding: 0;
}

:is(.public-home, .public-detail, .public-news) .homepage-header {
    position: fixed;
    inset: 0 0 auto;
    z-index: 50;
    height: 5rem;
    padding: 0.5rem 1rem;
    border-bottom: 1px solid transparent;
    background: var(--homepage-navbar-bg, rgb(255 255 255 / 0.92));
    /* A soft edge where content slides under the frosted bar, not a hard divider. */
    box-shadow:
        0 1px 0 rgb(var(--color-brand-primary-rgb) / 0.08),
        0 6px 16px rgb(var(--color-brand-primary-rgb) / 0.08);
    -webkit-backdrop-filter: blur(18px) saturate(150%);
    backdrop-filter: blur(18px) saturate(150%);
    /* The blur eases in with the colour, so the bar materialises over the hero's
       edge instead of snapping from clear to frosted. */
    transition: background-color 220ms ease, border-color 220ms ease, box-shadow 220ms ease,
        -webkit-backdrop-filter 220ms ease, backdrop-filter 220ms ease, transform 220ms ease;
}

/* Frosted surfaces go solid when the visitor asks for less transparency, and get
   a defined edge when they ask for more contrast. */
@media (prefers-reduced-transparency: reduce) {
    :is(.public-home, .public-detail, .public-news) .homepage-header:not(.is-over-hero) {
        background: var(--homepage-navbar-bg-solid, var(--color-surface-navigation));
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
    }
}
@media (prefers-contrast: more) {
    :is(.public-home, .public-detail, .public-news) .homepage-header:not(.is-over-hero) {
        border-bottom-color: var(--color-brand-primary);
        background: var(--homepage-navbar-bg-solid, var(--color-surface-navigation));
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
    }
}

/* Hides the navbar while scrolling down toward the bottom of the page, and
   brings it back the moment the visitor scrolls up. Toggled by the scroll
   listener in initializeHomepage() (public.js); the .site-header class is
   shared with the detail-page header below so both animate the same way. */
.site-header {
    transition: transform 220ms ease;
}
.site-header.is-nav-hidden {
    transform: translateY(-100%);
}
/* Keyboard focus inside the header brings it back, so Tab never lands on a
   control that is off-screen. Mouse clicks do not match :focus-visible, so a
   clicked link or the language switcher does not pin the header open. */
.site-header.is-nav-hidden:has(:focus-visible) {
    transform: none;
}

:is(.public-home, .public-detail, .public-news) .homepage-header.is-over-hero {
    border-color: transparent;
    background: transparent;
    box-shadow: none;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
}

:is(.public-home, .public-detail, .public-news) .homepage-nav-shell {
    display: flex;
    width: min(100%, 80rem);
    height: 4rem;
    margin: 0 auto;
    padding: 0 0.75rem 0 1rem;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    background: transparent;
}

:is(.public-home, .public-detail, .public-news) .homepage-header.is-over-hero .homepage-nav-shell {
    background: transparent;
}

:is(.public-home, .public-detail, .public-news) .homepage-brand {
    display: inline-flex;
    min-width: 0;
    min-height: 44px;
    align-items: center;
    padding: 0.25rem 0.5rem;
    border-radius: 999px;
}

:is(.public-home, .public-detail, .public-news) .homepage-brand img {
    width: auto;
    max-width: min(12rem, 34vw);
    max-height: 2.5rem;
}

:is(.public-home, .public-detail, .public-news) .homepage-logo-white {
    display: none;
}

:is(.public-home, .public-detail, .public-news) .homepage-header.is-over-hero .homepage-logo-blue {
    display: none;
}

:is(.public-home, .public-detail, .public-news) .homepage-header.is-over-hero .homepage-logo-white {
    display: block;
}

:is(.public-home, .public-detail, .public-news) .homepage-desktop-nav {
    min-width: 0;
    gap: 0.125rem;
    white-space: nowrap;
}

:is(.public-home, .public-detail, .public-news) .homepage-nav-item {
    display: inline-flex;
    min-height: 44px;
    align-items: center;
    padding: 0.625rem 0.75rem;
    border-radius: 999px;
    font-size: 0.875rem;
    font-weight: 700;
    line-height: 1;
    transition: color 180ms ease, background-color 180ms ease, transform 180ms ease;
}

@media (min-width: 1280px) {
    :is(.public-home, .public-detail, .public-news) .homepage-desktop-nav {
        margin-left: auto;
    }

    :is(.public-home, .public-detail, .public-news) .homepage-desktop-nav + .language-switcher {
        margin-left: 0.5rem;
    }

    :is(.public-home, .public-detail, .public-news) .homepage-nav-item {
        font-size: 1rem;
    }
}

:is(.public-home, .public-detail, .public-news) .homepage-nav-item:hover,
:is(.public-home, .public-detail, .public-news) .homepage-nav-item[aria-expanded='true'] {
    background: rgb(var(--color-brand-primary-rgb) / 0.09);
}

:is(.public-home, .public-detail, .public-news) .homepage-header.is-over-hero .homepage-desktop-nav .homepage-nav-item,
:is(.public-home, .public-detail, .public-news) .homepage-header.is-over-hero .homepage-menu-button {
    color: #ffffff !important;
}

:is(.public-home, .public-detail, .public-news) .homepage-header.is-over-hero .homepage-desktop-nav .homepage-nav-item:hover,
:is(.public-home, .public-detail, .public-news) .homepage-header.is-over-hero .homepage-desktop-nav .homepage-nav-item[aria-expanded='true'],
:is(.public-home, .public-detail, .public-news) .homepage-header.is-over-hero .homepage-menu-button:hover,
:is(.public-home, .public-detail, .public-news) .homepage-header.is-over-hero .homepage-menu-button[aria-expanded='true'] {
    background: rgb(255 255 255 / 0.14);
}

/* Press feedback: every public control gives slightly on pointer-down, the same
   way everywhere, and lands within 100ms. `.public-press` is the hook for controls
   that have no class of their own; cards give less than buttons. Colour feedback
   stays under reduced motion; the scale does not. */
@media (prefers-reduced-motion: no-preference) {
    :is(.public-home, .public-detail, .public-news) :is(
        .public-press,
        .homepage-nav-item,
        .homepage-menu-button,
        .homepage-hero-button,
        .homepage-slider-button,
        .detail-pill
    ):active {
        transform: scale(0.97);
        transition-duration: 100ms;
        transition-timing-function: ease-out;
    }
    :is(.public-home, .public-detail, .public-news) :is(.public-press-card, .public-press-card:hover):active {
        transform: scale(0.99);
        transition-duration: 100ms;
        transition-timing-function: ease-out;
    }
    :is(.public-home, .public-detail, .public-news) .public-press {
        transition-property: color, background-color, border-color, text-decoration-color, opacity, filter, transform;
        transition-duration: 150ms;
    }
}

:is(.public-home, .public-detail, .public-news) .homepage-dropdown-menu {
    z-index: 50;
    overflow: hidden;
    border: 1px solid var(--color-brand-primary-border);
    border-radius: 1rem;
    background: var(--color-surface-navigation);
    box-shadow: 0 18px 48px rgb(var(--color-brand-primary-rgb) / 0.18);
}

:is(.public-home, .public-detail, .public-news) .homepage-dropdown-link {
    color: #374151 !important;
    transition: color 180ms ease, background-color 180ms ease;
}

:is(.public-home, .public-detail, .public-news) .homepage-dropdown-link:hover {
    color: var(--color-brand-primary);
    background: var(--color-brand-primary-soft);
}

:is(.public-home, .public-detail, .public-news) .language-switcher {
    margin-left: auto;
}

:is(.public-home, .public-detail, .public-news) .homepage-header.is-over-hero .language-switcher a {
    color: #ffffff !important;
}

:is(.public-home, .public-detail, .public-news) .homepage-header.is-over-hero .language-switcher a[aria-current='true'] {
    color: var(--color-brand-primary) !important;
    background: #ffffff !important;
}

:is(.public-home, .public-detail, .public-news) .homepage-header.is-over-hero .language-switcher a:not([aria-current='true']):hover {
    background: rgb(255 255 255 / 0.14) !important;
}

:is(.public-home, .public-detail, .public-news) .homepage-menu-button {
    display: inline-flex;
    width: 44px;
    height: 44px;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    transition: background-color 180ms ease, transform 180ms ease;
}

:is(.public-home, .public-detail, .public-news) .homepage-menu-button:hover,
:is(.public-home, .public-detail, .public-news) .homepage-menu-button[aria-expanded='true'] {
    background: rgb(var(--color-brand-primary-rgb) / 0.09);
}
/* The hamburger turns into a close mark while the menu is open. */
.homepage-menu-button .menu-icon-close,
.homepage-menu-button[aria-expanded='true'] .menu-icon-open {
    display: none;
}
.homepage-menu-button[aria-expanded='true'] .menu-icon-close {
    display: inline;
}

:is(.public-home, .public-detail, .public-news) .homepage-mobile-menu {
    position: absolute;
    top: 4.75rem;
    right: 1rem;
    left: 1rem;
    max-height: calc(100dvh - 6rem);
    overflow-y: auto;
    border: 1px solid var(--color-brand-primary-border);
    border-radius: 1rem;
    background: var(--color-surface-navigation);
    box-shadow: 0 18px 48px rgb(var(--color-brand-primary-rgb) / 0.2);
}

:is(.public-home, .public-detail, .public-news) .homepage-mobile-menu .homepage-mobile-nav-item {
    color: #1f2937 !important;
}

:is(.public-home, .public-detail, .public-news) .homepage-mobile-nav-item {
    min-height: 44px;
    padding: 0.75rem 1rem;
    border-radius: 999px;
    font-weight: 700;
    transition: background-color 180ms ease;
}

:is(.public-home, .public-detail, .public-news) .homepage-mobile-nav-item:hover,
:is(.public-home, .public-detail, .public-news) .homepage-mobile-nav-item[aria-expanded='true'] {
    background: var(--color-brand-primary-soft);
}

:is(.public-home, .public-detail, .public-news) .homepage-mobile-submenu-link {
    min-height: 44px;
    color: #4b5563;
}

:is(.public-home, .public-detail, .public-news) .homepage-mobile-submenu-link:hover {
    color: var(--color-brand-primary);
}

/* "You are here". `aria-current="page"` is the page itself; `true` is the
   section it sits in (an article under Berita, a programme under Program). Both
   get an underline and a tint, so the cue does not depend on colour, and the CMS
   text colour is left alone. */
:is(.public-home, .public-detail, .public-news) :is(.homepage-nav-item, .homepage-mobile-nav-item):is([aria-current='page'], [aria-current='true']) {
    background: rgb(var(--color-brand-primary-rgb) / 0.09);
    text-decoration: underline;
    text-decoration-thickness: 2px;
    text-underline-offset: 0.45em;
}
:is(.public-home, .public-detail, .public-news) .homepage-header.is-over-hero .homepage-desktop-nav .homepage-nav-item:is([aria-current='page'], [aria-current='true']) {
    background: rgb(255 255 255 / 0.14);
}
:is(.public-home, .public-detail, .public-news) .homepage-mobile-nav-item:is([aria-current='page'], [aria-current='true']) {
    background: var(--color-brand-primary-soft);
}
:is(.public-home, .public-detail, .public-news) .homepage-dropdown-link:is([aria-current='page'], [aria-current='true']) {
    color: var(--color-brand-primary) !important;
    background: var(--color-brand-primary-soft);
    font-weight: 700;
}
:is(.public-home, .public-detail, .public-news) .homepage-mobile-submenu-link:is([aria-current='page'], [aria-current='true']) {
    border-radius: 0.5rem;
    color: var(--color-brand-primary);
    background: var(--color-brand-primary-soft);
    font-weight: 700;
}

/* The admissions buttons' `lift`: they rise on hover. Only transform and colour
   animate; the deeper shadow switches with it rather than being tweened. */
:is(.public-home, .public-detail, .public-news) .nav-item {
    transition: color 150ms ease, background-color 150ms ease, border-color 150ms ease,
        transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
:is(.public-home, .public-detail, .public-news) .nav-item:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
/* Pressing wins over the hover lift. */
@media (prefers-reduced-motion: no-preference) {
    :is(.public-home, .public-detail, .public-news) .nav-item:active {
        transform: scale(0.97);
        transition-duration: 100ms;
    }
}

/* No double-tap zoom on controls, so a tap commits without waiting for a second one. */
:is(.public-home, .public-detail, .public-news) :is(a, button, summary) {
    touch-action: manipulation;
}

.public-home :focus-visible,
.public-detail :focus-visible,
.public-news :focus-visible {
    /* Two tones: the dark line fills the 2px gap between the element and the
       amber outline, so the ring reads on any surface (WCAG 1.4.11, 3:1). */
    outline: 3px solid var(--color-focus-ring);
    outline-offset: 2px;
    box-shadow: 0 0 0 2px var(--color-focus-ring-inner);
}

/* Off-screen until focused. `.public-news` covers the article page, the 404
   page and the notice pages, which share this link but not the other classes. */
:is(.public-home, .public-detail, .public-news) .skip-link {
    position: fixed;
    top: 1rem;
    left: 1rem;
    z-index: 100;
    transform: translateY(-200%);
}
:is(.public-home, .public-detail, .public-news) .skip-link:focus {
    transform: translateY(0);
}

:is(.public-home, .public-detail, .public-news) .homepage-hero {
    position: relative;
    min-height: 100svh;
    overflow: hidden;
    background: var(--color-brand-primary);
}
/* The page keeps vertical scrolling and pinch-zoom over the hero; horizontal
   drags go to the slide swipe in public.js. */
.public-home .homepage-hero {
    touch-action: pan-y pinch-zoom;
}

.public-home #slide-container {
    display: flex;
    min-height: 100svh;
    align-items: stretch;
    transition: transform 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

:is(.public-home, .public-detail, .public-news) .slide {
    flex: 0 0 100%;
    min-height: 100svh;
    position: relative;
    overflow: hidden;
}

/* The slide photo is a real <img> rather than a CSS background so the preload
   scanner finds the LCP image in the markup instead of waiting for public.css to
   parse, and so it can carry a srcset. */
:is(.public-home, .public-detail, .public-news) .slide-image {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center 42%;
}
:is(.public-home, .public-detail, .public-news) .slide::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
    /* The radial scrim is anchored bottom-left, so the top bar only gets 0.2
       alpha and the header's white logo, hamburger and the end of the H1 fall
       below 3:1 on a light slide. Slides are CMS-uploaded, so the top band
       needs its own floor rather than relying on whichever photo is active. */
    background:
        linear-gradient(
            to bottom,
            rgb(7 23 40 / 0.7) 0%,
            rgb(7 23 40 / 0.36) 14%,
            transparent 28%
        ),
        linear-gradient(
            to top,
            rgb(7 23 40 / 0.72) 0%,
            rgb(7 23 40 / 0.42) 26%,
            transparent 52%
        ),
        radial-gradient(
            ellipse 105% 92% at 0% 100%,
            rgb(7 23 40 / 0.86) 0%,
            rgb(7 23 40 / 0.62) 35%,
            rgb(7 23 40 / 0.2) 72%,
            rgb(7 23 40 / 0.34) 100%
        );
}

:is(.public-home, .public-detail, .public-news) .homepage-hero-inner {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    width: min(100%, 80rem);
    min-height: 100svh;
    margin: 0 auto;
    padding: 7rem clamp(1.25rem, 5vw, 4rem) 6.5rem;
    align-items: flex-end;
}

:is(.public-home, .public-detail, .public-news) .homepage-hero-copy {
    width: min(100%, 43rem);
    color: var(--color-text-on-hero);
}

:is(.public-home, .public-detail, .public-news) .homepage-hero-title {
    max-width: 16ch;
    margin: 0;
    color: var(--color-text-on-hero);
    font-size: clamp(2.5rem, 6vw, 5rem);
    font-weight: 700;
    letter-spacing: -0.03em;
    line-height: 1.05;
    text-wrap: balance;
}

/* A sentence in the title slot (the programmes hub): the same type, set smaller
   and wider so it holds in about six lines instead of a dozen. */
.public-detail .homepage-hero-title.homepage-hero-statement {
    max-width: 24ch;
    font-size: clamp(2rem, 4vw, 3.5rem);
    line-height: 1.1;
}

.public-home .homepage-hero-subtitle {
    max-width: 32ch;
    margin: 0.75rem 0 0;
    color: var(--color-text-on-hero);
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.4;
}

/* Desktop has room for both facts on one line; below lg it still wraps at the bullet. */
@media (min-width: 1024px) {
    .public-home .homepage-hero-subtitle {
        max-width: none;
    }
}

:is(.public-home, .public-detail, .public-news) .homepage-hero-actions {
    display: flex;
    flex-wrap: wrap;
    margin-top: 1.75rem;
    gap: 0.75rem;
}

:is(.public-home, .public-detail, .public-news) .homepage-hero-button {
    display: inline-flex;
    min-height: 48px;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.75rem 1.5rem;
    border: 1px solid rgb(255 255 255 / 0.72);
    border-radius: 999px;
    color: var(--color-brand-primary);
    background: var(--color-surface-hero-control);
    box-shadow: 0 12px 30px rgb(8 27 48 / 0.22);
    font-weight: 700;
    line-height: 1.2;
    white-space: nowrap;
    transition: color 180ms ease, background-color 180ms ease, transform 180ms ease;
}

:is(.public-home, .public-detail, .public-news) .homepage-hero-button:hover {
    color: var(--color-text-on-hero);
    background: var(--color-brand-primary-hover);
}

.public-home .homepage-hero-button-secondary {
    color: var(--color-text-on-hero);
    background: transparent;
}

.public-home .homepage-hero-button-secondary:hover {
    color: var(--color-brand-primary);
    background: var(--color-surface-hero-control);
}

.public-home .homepage-slider-navigation {
    position: absolute;
    right: clamp(1.25rem, 5vw, 4rem);
    bottom: 2rem;
    z-index: 3;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.5rem;
}

.public-home .homepage-slider-dots,
.public-home .homepage-slider-arrows {
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

.public-home .homepage-slider-dots .dot {
    display: inline-flex;
    width: 44px;
    height: 44px;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    background: transparent !important;
}

/* The bar is drawn at its active width and scaled down when inactive, so the
   change animates on the compositor instead of re-laying out the width. */
.public-home .homepage-slider-dots .dot::before {
    content: '';
    width: 2.25rem;
    height: 0.25rem;
    border-radius: 999px;
    background: rgb(255 255 255 / 0.5);
    transform: scaleX(0.6667);
    transition: transform 180ms ease, background-color 180ms ease;
}

.public-home .homepage-slider-dots .dot[aria-pressed='true']::before {
    transform: none;
    background: var(--color-surface-hero-control);
}

.public-home .homepage-slider-button {
    display: inline-flex;
    width: 48px;
    height: 48px;
    align-items: center;
    justify-content: center;
    border: 1px solid rgb(255 255 255 / 0.48);
    border-radius: 999px;
    color: var(--color-text-on-hero);
    background: rgb(8 27 48 / 0.46);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    font-size: 2rem;
    line-height: 1;
    transition: background-color 180ms ease, transform 180ms ease;
}

.public-home .homepage-slider-button:hover {
    background: var(--color-brand-primary-hover);
}

@media (prefers-reduced-transparency: reduce) {
    .public-home .homepage-slider-button:not(:hover) {
        background: rgb(8 27 48);
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
    }
}
@media (prefers-contrast: more) {
    .public-home .homepage-slider-button {
        border-color: #fff;
    }
    .public-home .homepage-slider-button:not(:hover) {
        background: rgb(8 27 48);
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
    }
}

/* The pause control shows the icon for what pressing it will do next. */
.public-home .homepage-slider-toggle {
    font-size: 1rem;
}

.public-home .homepage-slider-toggle .homepage-slider-icon-play,
.public-home .homepage-slider-toggle.is-paused .homepage-slider-icon-pause {
    display: none;
}

.public-home .homepage-slider-toggle.is-paused .homepage-slider-icon-play {
    display: block;
}

.public-home .homepage-slider-button:focus-visible,
.public-home .homepage-slider-dots .dot:focus-visible {
    outline: 3px solid #f8fafc;
    outline-offset: 3px;
    box-shadow: 0 0 0 5px rgb(7 23 40 / 0.72);
}

@media (max-width: 767px) {
    :is(.public-home, .public-detail, .public-news) .homepage-header {
        padding-inline: 0.75rem;
    }

    :is(.public-home, .public-detail, .public-news) .homepage-nav-shell {
        padding-right: 0.5rem;
        padding-left: 0.75rem;
    }

    :is(.public-home, .public-detail, .public-news) .homepage-brand img {
        max-width: 7rem;
        max-height: 2.25rem;
    }

    :is(.public-home, .public-detail, .public-news) .homepage-mobile-menu {
        right: 0.75rem;
        left: 0.75rem;
    }

    :is(.public-home, .public-detail, .public-news) .slide-image {
        object-position: center 34%;
    }

    :is(.public-home, .public-detail, .public-news) .slide::before {
        background:
            linear-gradient(
                to bottom,
                rgb(7 23 40 / 0.5) 0%,
                rgb(7 23 40 / 0.22) 12%,
                transparent 22%
            ),
            radial-gradient(
                ellipse 125% 88% at 18% 94%,
                rgb(7 23 40 / 0.9) 0%,
                rgb(7 23 40 / 0.68) 38%,
                rgb(7 23 40 / 0.24) 72%,
                rgb(7 23 40 / 0.4) 100%
            );
    }

    :is(.public-home, .public-detail, .public-news) .homepage-hero-inner {
        padding: 7rem 1.25rem;
        align-items: flex-end;
    }

    :is(.public-home, .public-detail, .public-news) .homepage-hero-title {
        max-width: 14ch;
        font-size: 2.5rem;
        line-height: 1.1;
    }

    .public-home .homepage-slider-navigation {
        right: 1.25rem;
        bottom: 1.25rem;
    }

    .public-home .homepage-slider-dots .dot::before {
        transform: scaleX(0.5556);
    }

    .public-home .homepage-slider-button {
        width: 44px;
        height: 44px;
    }
}

.public-home #section1 {
    position: relative;
    z-index: 1;
}
/* Desktop submenus grow out of their trigger (the menu hangs from the right edge
   of its button) and fold back into it on the same path. Visibility flips at the
   end of the close, so the fade is not cut short. */
:is(.public-home, .public-detail, .public-news) .dropdown-menu {
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px) scale(0.96);
    transform-origin: top right;
    transition: opacity 200ms ease-out, transform 200ms cubic-bezier(0.2, 0.8, 0.2, 1), visibility 0s linear 200ms;
}
:is(.public-home, .public-detail, .public-news) .dropdown.is-open .dropdown-menu {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition-delay: 0s;
}

/* The mobile menu grows from the hamburger; public.js animates it and its scrim
   in and out (see setMobileMenuOpen). */
:is(.public-home, .public-detail, .public-news) #mobile-menu {
    max-height: calc(100dvh - 6rem);
    overflow-y: auto;
    overscroll-behavior: contain;
    transform-origin: top right;
}
/* Dims the page behind the open menu, which also locks its scroll: the menu is a
   task of its own, so the page steps back. It sits under the header. */
:is(.public-home, .public-detail, .public-news) .homepage-menu-scrim {
    position: fixed;
    inset: 0;
    z-index: 40;
    background: rgb(7 23 40 / 0.4);
}

:is(.public-home, .public-detail, .public-news) .mobile-dropdown.active .mobile-dropdown-content {
    display: block !important;
}
:is(.public-home, .public-detail, .public-news) .mobile-dropdown.active .mobile-dropdown-btn svg {
    transform: rotate(180deg);
}

/* Testimonial switcher: thumbnails start faded and grey and come to full colour on
   hover or focus; only opacity and filter animate. */
.public-home .testimonial-thumb {
    opacity: 0.6;
    filter: grayscale(60%);
    transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1), filter 0.3s cubic-bezier(0.4, 0, 0.2, 1), transform 100ms ease-out;
}
.public-home .testimonial-thumb:hover,
.public-home .testimonial-thumb:focus-visible {
    opacity: 1;
    filter: none;
}
.public-home .testimonial-thumb:focus-visible {
    outline: 3px solid var(--color-focus-ring);
    outline-offset: 2px;
    box-shadow: 0 0 0 2px var(--color-focus-ring-inner);
}
/* bindTestimonials() toggles `hidden` on elements that also carry display
   utilities, which would otherwise win over the attribute. */
.public-home #section7 [hidden] {
    display: none;
}
@media (min-width: 1280px) {
    /* The text column sits left of the portrait when more people are before the
       active one than after it; the text then hugs the portrait. */
    .public-home #testimonials[data-text-side="left"] [data-testimonial-panel] {
        flex-direction: row-reverse;
    }
    .public-home #testimonials[data-text-side="left"] [data-testimonial-slot] {
        text-align: right;
    }
    .public-home #testimonials[data-text-side="left"] .testimonial-stars,
    .public-home #testimonials[data-text-side="left"] .testimonial-row {
        justify-content: flex-end;
    }
    .public-home #testimonials[data-text-side="left"] .testimonial-rule {
        margin-left: auto;
    }
    /* A crowded group shrinks its thumbnails instead of widening the row or
       spilling past the text column (the last person has every thumbnail there). */
    .public-home #section7 .testimonial-row .testimonial-thumb,
    .public-home #testimonials [data-testimonial-outer] .testimonial-thumb {
        flex-shrink: 1;
        min-width: 3rem;
    }
}

/* News cards behave the same on the homepage and the news listing (a detail page). */
:is(.public-home, .public-detail, .public-news) .news-card {
    overflow: hidden;
    transition: transform 0.3s ease-out, border-color 0.3s ease-out, box-shadow 0.3s ease-out;
}
:is(.public-home, .public-detail, .public-news) .news-card:hover {
    transform: translateY(-3px);
    border-color: rgb(var(--color-brand-primary-rgb) / 0.35);
    box-shadow: 0 10px 24px rgb(var(--color-brand-primary-rgb) / 0.08);
}
:is(.public-home, .public-detail, .public-news) .news-card img {
    transition: transform 0.5s ease-out;
}
:is(.public-home, .public-detail, .public-news) .news-card:hover img {
    transform: scale(1.025);
}
/* Below lg the gallery is a native swipe strip. From lg the same photos stop
   scrolling and sit in one centred row. */
.public-home .gallery-collage {
    display: flex;
    gap: 1rem;
    overflow-x: auto;
    padding: 0.25rem 1rem 1rem;
    scroll-padding-inline: 1rem;
    overscroll-behavior-inline: contain;
    cursor: grab;
    scrollbar-width: none;
}
.public-home .gallery-collage::-webkit-scrollbar {
    display: none;
}
.public-home .gallery-collage:focus-visible {
    outline: 3px solid var(--color-focus-ring);
    outline-offset: 2px;
    box-shadow: 0 0 0 2px var(--color-focus-ring-inner);
}
.public-home .gallery-collage.is-dragging {
    cursor: grabbing;
    scroll-behavior: auto;
    user-select: none;
}
.public-home .gallery-collage .gallery-rail-item {
    flex: 0 0 auto;
    height: clamp(11rem, 32vw, 13.25rem);
    margin: 0;
    overflow: hidden;
}
.public-home .gallery-collage .gallery-rail-item:nth-child(6n + 1),
.public-home .gallery-collage .gallery-rail-item:nth-child(6n + 5) {
    aspect-ratio: 5 / 7;
}
.public-home .gallery-collage .gallery-rail-item:nth-child(6n + 2),
.public-home .gallery-collage .gallery-rail-item:nth-child(6n) {
    aspect-ratio: 1;
}
.public-home .gallery-collage .gallery-rail-item:nth-child(6n + 3),
.public-home .gallery-collage .gallery-rail-item:nth-child(6n + 4) {
    aspect-ratio: 7 / 5;
}
.public-home .gallery-collage .gallery-rail-item > img,
.public-home .gallery-collage .gallery-rail-item > div {
    height: 100%;
}
.public-home .rail-scroll-indicator {
    width: 6rem;
    height: 2px;
    margin: 0.75rem auto 0;
    overflow: hidden;
    background: rgb(var(--color-brand-primary-rgb) / 0.18);
}
.public-home .rail-scroll-indicator span {
    display: block;
    height: 100%;
    background: var(--color-brand-primary);
    transition: transform 0.15s ease-out;
}
@media (min-width: 640px) {
    .public-home .gallery-collage {
        gap: 1.25rem;
        padding-inline: 1.5rem;
        scroll-padding-inline: 1.5rem;
    }
    .public-home .gallery-collage .gallery-rail-item {
        height: clamp(13rem, 22vw, 17.5rem);
    }
}
@media (min-width: 1024px) {
    /* One fixed row of the six photos. Each grows in proportion to its aspect
       ratio (×35), so all share one height and together fill the row; the
       max-width stops that height passing the 17.5rem the strip used. */
    .public-home .gallery-collage {
        max-width: 119rem;
        margin-inline: auto;
        overflow-x: visible;
        padding-inline: 2rem;
        cursor: auto;
    }
    .public-home .gallery-collage .gallery-rail-item {
        flex: 35 1 0;
        min-width: 0;
        height: auto;
    }
    .public-home .gallery-collage .gallery-rail-item:nth-child(6n + 1),
    .public-home .gallery-collage .gallery-rail-item:nth-child(6n + 5) {
        flex-grow: 25;
    }
    .public-home .gallery-collage .gallery-rail-item:nth-child(6n + 3),
    .public-home .gallery-collage .gallery-rail-item:nth-child(6n + 4) {
        flex-grow: 49;
    }
}
.public-home .testimonial-quote {
    display: -webkit-box;
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    line-clamp: 3;
}
/* Quotes are CMS-entered at any length; the clamp keeps a long one from pushing
   the thumbnails out of the fixed-height column beside the portrait. */
.public-home .testimonial-quote {
    -webkit-line-clamp: 6;
    line-clamp: 6;
}
@media (min-width: 1024px) {
    .public-home .testimonial-quote {
        -webkit-line-clamp: 4;
        line-clamp: 4;
    }
}
@media (min-width: 1280px) {
    .public-home .testimonial-quote {
        -webkit-line-clamp: 5;
        line-clamp: 5;
    }
}

/* The scroll reveal that used to live here is gone. It set opacity to 0 in CSS and
   depended on public.js adding `.is-visible`, so a blocked or failed script left
   the accreditation, school history and character traits invisible while still
   occupying 2,878px of layout. That content is the most trust-critical block on
   the page; it does not get gated behind a decorative effect that reduced-motion
   users never saw anyway. */

/* Explicit public content-card variants. */
main .public-card:not(.public-card-blue) {
    border-top: 4px solid var(--color-brand-primary);
    box-shadow: var(--shadow-public-card);
}

/* Only cards that are links lift on hover; a plain text card that reacts looks clickable. */
main a.public-card:hover {
    box-shadow: var(--shadow-public-card-hover);
}

main .public-card-blue {
    border-top: 4px solid #f8fafc;
}

/* Shared public detail pages */
html.public-detail {
    --public-header-height: 5rem;
    scroll-behavior: smooth;
}
/* The reduced-motion rule at the end of the file matches descendants only, and
   the viewport takes its scroll behaviour from <html> itself. */
@media (prefers-reduced-motion: reduce) {
    html.public-detail {
        scroll-behavior: auto;
    }
}
body.public-detail {
    font-family: var(--font-sans-public);
    font-optical-sizing: auto;
    overflow-wrap: break-word;
}
.public-detail img {
    max-width: 100%;
}
.public-detail [id] {
    scroll-margin-top: calc(var(--public-header-height) + 1rem);
}
/* Section-nav and related-page pills: colour eases, and the shared press rule
   above gives them their scale on pointer-down. */
@media (prefers-reduced-motion: no-preference) {
    .public-detail .detail-pill {
        transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease, transform 100ms ease-out;
    }
}
.public-detail footer a,
.public-news footer a {
    display: inline-flex;
    min-height: 44px;
    align-items: center;
}

/* Article body copy from the CMS rich-text editor: plain HTML with no class
   names to hook, so it is styled by tag instead of a utility-class kit like
   Tailwind Typography (not installed; see docs/AGENTS.md on new dependencies). */
.public-news .article-content {
    font-size: 1.0625rem;
    line-height: 1.75;
    color: #374151;
}
.public-news .article-content > * + * {
    margin-top: 1.25em;
}
.public-news .article-content h2 {
    margin-top: 1.75em;
    font-size: 1.375rem;
    font-weight: 700;
    line-height: 1.35;
    color: #111827;
}
.public-news .article-content h3 {
    margin-top: 1.5em;
    font-size: 1.1875rem;
    font-weight: 700;
    line-height: 1.4;
    color: #111827;
}
.public-news .article-content a {
    color: var(--color-brand-primary);
    text-decoration: underline;
    text-underline-offset: 2px;
}
.public-news .article-content strong {
    color: #111827;
    font-weight: 700;
}
.public-news .article-content ul,
.public-news .article-content ol {
    padding-left: 1.375em;
}
.public-news .article-content ul {
    list-style: disc;
}
.public-news .article-content ol {
    list-style: decimal;
}
.public-news .article-content li + li {
    margin-top: 0.5em;
}
.public-news .article-content blockquote {
    border-left: 3px solid var(--color-brand-primary-border);
    padding-left: 1.25em;
    color: #4b5563;
    font-style: italic;
}
.public-news .article-content img {
    border-radius: 1rem;
}

/* Reduced motion removes movement, not feedback: transforms, sizes and grid rows
   change instantly, while opacity and colour keep a short cross-fade so a state
   change still reads as a change rather than a flash. */
@media (prefers-reduced-motion: reduce) {
    .public-home *, .public-home *::before, .public-home *::after,
    .public-detail *, .public-detail *::before, .public-detail *::after,
    .public-news *, .public-news *::before, .public-news *::after {
        scroll-behavior: auto !important;
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-property: opacity, color, background-color, border-color, filter !important;
        transition-duration: 150ms !important;
        transition-delay: 0s !important;
    }
}
