/*
 * Nzeru Hub landing page styles.
 *
 * Institutional, editorial palette inspired by UNDP / UN agency design
 * language: deep institutional blue with a refined gold accent, editorial
 * serif display type, tight column rhythm, and subtle architectural patterns.
 *
 * All selectors are scoped with the `nh-` prefix so they never leak into the
 * surrounding Moodle theme. No inline styles are required except a single
 * data-driven CSS custom property (`--nh-hero-image`) for the optional hero
 * photograph, which cannot be expressed statically.
 *
 * @package    local_nzeruhub
 * @copyright  2026 UNDP Malawi IDT4M
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

/* ============================================================ Tokens. */
/*
 * The palette, geometry, elevation and motion scale now live in ONE place for
 * the whole platform: theme/nzeru/scss/tokens.scss, which emits them as `--nh-*`
 * custom properties on :root. Because the theme stylesheet loads on every page,
 * they are already in scope here.
 *
 * That is what keeps the homepage and the learner dashboard from drifting apart
 * — both read the same variables, so changing a brand hex in the theme's
 * pre.scss recolours Discover, Join and Learn together.
 *
 * The `var(..., fallback)` values below are a safety net only, so these pages
 * still render sensibly if the site is switched to a theme that does not supply
 * the tokens. Do not treat them as the source of truth: edit
 * theme/nzeru/scss/pre.scss instead.
 *
 * Tokens that remain landing-page-only are declared here because nothing else
 * uses them.
 */
.nh-page,
.nh-nav,
.nh-footer {
    /* Landing-page-only accents. */
    --nh-lavender: #e9e2ff;

    /* Local aliases with fallbacks, resolved from the site-wide :root tokens. */
    --nh-radius: var(--nh-radius-xs, 8px);
    --nh-shadow: 0 1px 0 rgba(11, 35, 71, .04), 0 18px 40px -24px rgba(11, 35, 71, .25);
    --nh-focus: 0 0 0 3px rgba(0, 91, 187, .32);

    color: var(--nh-ink, #0b2347);
    font-family: var(--nh-font-body, 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/*
 * Full-bleed landing treatment.
 *
 * Keyed on `nzeru-fullbleed`, which the landing, blog, FAQ and contact pages add
 * for themselves — NOT on `path-local-nzeruhub`, which Moodle adds automatically
 * to EVERY page under /local/nzeruhub/. Keyed on the automatic class, the rule
 * below hid the navbar and drawers on learning.php as well, which is an ordinary
 * Moodle page that needs its chrome.
 */
body.nzeru-fullbleed {
    background: #fff;
}
body.nzeru-fullbleed #page,
body.nzeru-fullbleed #page-content,
body.nzeru-fullbleed #region-main,
body.nzeru-fullbleed #region-main-box {
    padding: 0;
    margin: 0;
    max-width: none;
    background: #fff;
}
body.nzeru-fullbleed #page-wrapper > *:not(#page) {
    display: none;
}

/*
 * Hold the footer at the bottom of the window on a page too short to fill it.
 *
 * THE PROBLEM. These pages are only as tall as their content, and some of them
 * are very short: the Blog with nothing published yet is a heading, one line of
 * "Nothing here yet", and that is all. The footer then sits wherever the content
 * happens to stop -- halfway up the screen, with empty white below it. It reads
 * as a page that failed to load the rest of itself.
 *
 * THE FIX. Make the column from <body> down to the footer's parent a flex
 * column that is at least as tall as the viewport, and let the <main> in the
 * middle absorb whatever space is left over. On a short page that pushes the
 * footer to the bottom; on a long one <main> is already taller than the space
 * and nothing moves. The footer keeps scrolling with the page either way.
 *
 * NOT `position: fixed`. That would pin the footer over the content and keep it
 * there while reading a long article, which is not what is wanted -- the footer
 * should be at the end of the page, and on a short page the end of the page
 * should be the bottom of the window.
 *
 * THE SELECTOR CHAIN is the embedded layout's, which is the one a signed-out
 * visitor gets (see local_nzeruhub_apply_public_page_chrome): body > #page >
 * section.embedded-main > the [role="main"] wrapper Moodle puts around
 * main_content > our nav, main and footer. Every step has to grow, because a
 * flex child only fills its own parent -- one link left at its natural height
 * and the whole chain collapses back to content height.
 *
 * Scoped to .nzeru-fullbleed: a signed-in learner gets the standard layout and
 * the theme's own footer, which is not ours to reposition.
 */
body.nzeru-fullbleed {
    /* dvh first for mobile browsers, where 100vh is the height WITHOUT the
       address bar and leaves the footer cut off below the fold. The vh line is
       the fallback for browsers that do not know dvh, and must come first. */
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
}
body.nzeru-fullbleed #page,
body.nzeru-fullbleed .embedded-main,
body.nzeru-fullbleed .embedded-main > [role="main"] {
    display: flex;
    flex-direction: column;
    /* grow to fill, never shrink below content: a long page must still be able
       to run past the bottom of the window. */
    flex: 1 0 auto;
}
/* The element that takes up the slack, so the empty space carries the page's
   own background rather than showing through to the body. */
body.nzeru-fullbleed #nh-main {
    flex: 1 0 auto;
}
body.nzeru-fullbleed .nh-footer {
    flex: 0 0 auto;
}

.nh-container {
    width: 100%;
    max-width: 1240px;
    margin: 0 auto;
    padding: 0 32px;
}
@media (max-width: 640px) {
    .nh-container { padding: 0 20px; }
}

/* Smooth in-page scrolling, scoped to this plugin's pages, with an offset so
   the sticky header never covers the section heading we jump to. */
html:has(body.nzeru-fullbleed) { scroll-behavior: smooth; }
.nh-page section[id],
#home { scroll-margin-top: 92px; }

/* ---- Entrance & motion ---- */
@keyframes nh-fade-up {
    from { opacity: 0; transform: translateY(18px); }
    to   { opacity: 1; transform: none; }
}
@keyframes nh-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}
/* Staggered hero entrance on load. */
.nh-hero__content > * { animation: nh-fade-up .7s cubic-bezier(.2, .7, .2, 1) both; }
.nh-hero__content > *:nth-child(1) { animation-delay: .05s; }
.nh-hero__content > *:nth-child(2) { animation-delay: .12s; }
.nh-hero__content > *:nth-child(3) { animation-delay: .19s; }
.nh-hero__content > *:nth-child(4) { animation-delay: .26s; }
.nh-hero__content > *:nth-child(5) { animation-delay: .33s; }
.nh-hero__contour { animation: nh-fade-in 1.2s ease both .2s; }

/* Consistent, visible keyboard focus across every interactive element. */
.nh-page a:focus-visible,
.nh-page button:focus-visible,
.nh-page input:focus-visible,
.nh-page textarea:focus-visible,
.nh-nav a:focus-visible,
.nh-nav button:focus-visible,
.nh-footer a:focus-visible {
    outline: 2px solid var(--nh-primary);
    outline-offset: 2px;
    border-radius: 2px;
}

/* ============================================================ Skip link. */
.nh-skiplink {
    position: absolute;
    left: -10000px;
    top: auto;
}
.nh-skiplink:focus {
    left: 16px;
    top: 16px;
    background: var(--nh-ink);
    color: #fff;
    padding: 10px 14px;
    border-radius: var(--nh-radius);
    z-index: 1000;
}

/* ============================================================ Navigation. */
.nh-nav {
    position: sticky;
    top: 0;
    z-index: 60;
    background: rgba(255, 255, 255, .97);
    backdrop-filter: saturate(160%) blur(10px);
    border-bottom: 1px solid var(--nh-line-soft);
    transition: box-shadow .2s ease, border-color .2s ease;
}
.nh-nav.is-scrolled {
    box-shadow: 0 10px 30px -22px rgba(15, 23, 42, .45);
    border-bottom-color: var(--nh-line);
}
.nh-nav__inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 32px;
    height: 76px;
}
.nh-nav__brand {
    display: inline-flex;
    align-items: center;
    gap: 12px;
    text-decoration: none;
    color: var(--nh-ink);
}
.nh-nav__brand-mark {
    width: 38px;
    height: 38px;
    background: var(--nh-primary);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: var(--nh-font-head);
    font-weight: 700;
    font-size: 16px;
    letter-spacing: .02em;
    position: relative;
    border-radius: 3px;
}
.nh-nav__brand-mark::after {
    content: '';
    position: absolute;
    inset: auto -3px -3px auto;
    width: 10px;
    height: 10px;
    background: var(--nh-accent);
    border-radius: 1px;
}
.nh-nav__brand-text {
    font-family: var(--nh-font-head);
    font-size: 19px;
    font-weight: 600;
    letter-spacing: -.005em;
}
.nh-nav__brand:hover .nh-nav__brand-text { color: var(--nh-primary); }

.nh-nav__links {
    display: flex;
    gap: 32px;
    flex: 1;
    justify-content: center;
}
.nh-nav__links a {
    color: var(--nh-ink-soft);
    font-weight: 500;
    text-decoration: none;
    font-size: 14px;
    letter-spacing: .01em;
    position: relative;
    padding: 4px 0;
}
.nh-nav__links a::after {
    content: '';
    position: absolute;
    left: 0;
    right: 100%;
    bottom: 0;
    height: 2px;
    background: var(--nh-accent);
    transition: right .2s ease;
}
.nh-nav__links a:hover { color: var(--nh-primary); }
.nh-nav__links a:hover::after { right: 0; }

/* Never show a text underline on nav or brand links, in any state
   (overrides Moodle theme defaults that underline anchors on hover/focus). */
.nh-nav__brand,
.nh-nav__brand:hover,
.nh-nav__brand:focus,
.nh-nav__links a,
.nh-nav__links a:link,
.nh-nav__links a:visited,
.nh-nav__links a:hover,
.nh-nav__links a:focus,
.nh-nav__links a:active,
.nh-mobilemenu__links a,
.nh-mobilemenu__links a:hover,
.nh-mobilemenu__links a:focus,
.nh-mobilemenu__links a:active,
.nh-btn,
.nh-btn:hover,
.nh-btn:focus { text-decoration: none !important; }

.nh-nav__actions { display: flex; gap: 10px; align-items: center; }

/* Mobile menu toggle (hidden on desktop). */
.nh-nav__toggle {
    display: none;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    width: 44px;
    height: 44px;
    padding: 0 10px;
    background: transparent;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius);
    cursor: pointer;
}
.nh-nav__toggle-bar {
    display: block;
    height: 2px;
    width: 100%;
    background: var(--nh-ink);
    transition: transform .2s ease, opacity .2s ease;
}
.nh-nav.is-open .nh-nav__toggle-bar:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nh-nav.is-open .nh-nav__toggle-bar:nth-child(2) { opacity: 0; }
.nh-nav.is-open .nh-nav__toggle-bar:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* Mobile drawer. Hidden by default on all viewports; shown only when the
   nav is toggled open (JS adds .is-open). The base display:none guarantees it
   never appears alongside the desktop nav even if the [hidden] attribute is
   stripped. */
.nh-mobilemenu {
    display: none;
    border-top: 1px solid var(--nh-line-soft);
    background: #fff;
    padding: 16px 0 24px;
}
.nh-mobilemenu[hidden] { display: none; }
.nh-nav.is-open .nh-mobilemenu { display: block; }
.nh-mobilemenu__links { display: flex; flex-direction: column; }
.nh-mobilemenu__links a {
    padding: 13px 32px;
    color: var(--nh-ink);
    text-decoration: none;
    font-weight: 500;
    border-bottom: 1px solid var(--nh-line-soft);
}
.nh-mobilemenu__links a:hover { background: var(--nh-paper); color: var(--nh-primary); }
.nh-mobilemenu__actions {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 18px 32px 0;
}

@media (max-width: 960px) {
    .nh-nav__links,
    .nh-nav__actions { display: none; }
    .nh-nav__toggle { display: inline-flex; }
}
/* Tighten link spacing on mid-size screens so the bar never wraps. */
@media (max-width: 1180px) and (min-width: 961px) {
    .nh-nav__inner { gap: 20px; }
    .nh-nav__links { gap: 22px; }
}
/* Belt-and-braces: the drawer and hamburger are mobile-only affordances. */
@media (min-width: 961px) {
    .nh-mobilemenu { display: none !important; }
    .nh-nav__toggle { display: none !important; }
}
@media (max-width: 640px) {
    .nh-mobilemenu__links a { padding-left: 20px; padding-right: 20px; }
    .nh-mobilemenu__actions { padding-left: 20px; padding-right: 20px; }
}

/* ============================================================ Buttons. */
/* ONE HEIGHT FOR EVERY BUTTON.

   A button is as long as its text and no longer — that part is right, and
   unchanged. What was not consistent was the other dimension: buttons drawn
   in different places ended up different thicknesses, because each was sized
   by its own padding and font rather than to a shared measure, and a row of
   them read as a row of slightly different components.

   min-height pins that to one number — the 2.4rem of "Continue learning" on
   My learning, which is the button this set is measured against. Padding
   still does the horizontal work, so nothing about the length changes.

   The deliberately smaller variant, .nh-btn--sm, sets its own below. */
.nh-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    min-height: 2.4rem;
    border-radius: var(--nh-radius);
    padding: 11px 20px;
    font-weight: 600;
    font-size: 14px;
    text-decoration: none;
    border: 1px solid transparent;
    cursor: pointer;
    transition: transform .15s ease, background .15s ease, color .15s ease, border-color .15s ease, box-shadow .15s ease;
    line-height: 1;
    letter-spacing: .01em;
    font-family: inherit;
}
.nh-btn--primary {
    background: var(--nh-primary);
    color: #fff;
}
.nh-btn--primary:hover {
    background: #005a96;
    color: #fff;
    box-shadow: 0 12px 24px -14px rgba(0, 110, 181, .7);
}
.nh-btn__arrow { transition: transform .2s ease; }
.nh-btn:hover .nh-btn__arrow { transform: translateX(3px); }

.nh-btn--ghost {
    background: transparent;
    color: var(--nh-ink);
    border-color: var(--nh-line);
}
.nh-btn--ghost:hover {
    border-color: var(--nh-primary);
    color: var(--nh-primary);
}

.nh-btn--outline-light {
    background: transparent;
    color: #fff;
    border-color: rgba(255, 255, 255, .55);
}
.nh-btn--outline-light:hover {
    background: rgba(255, 255, 255, .08);
    border-color: #fff;
    color: #fff;
}

.nh-btn--lg { padding: 15px 26px; font-size: 15px; }
/* The one deliberate exception, used where a button sits inside a dense row
   or beside a caption — it keeps its own, smaller, equally consistent height. */
.nh-btn--sm { min-height: 2rem; padding: 8px 14px; font-size: 13px; gap: 6px; }
.nh-btn--block { width: 100%; }

/* ============================================================ Kickers / eyebrows. */
.nh-eyebrow {
    text-transform: uppercase;
    letter-spacing: .22em;
    font-size: 22px;
    font-weight: 600;
    color: var(--nh-primary);
    margin: 0 0 14px;
    display: inline-flex;
    align-items: center;
    gap: 12px;
}
.nh-eyebrow::before {
    /* content: ''; */
    width: 28px;
    height: 1px;
    background: var(--nh-accent);
}
.nh-eyebrow--light { color: #f0d68a; }
.nh-eyebrow--light::before { background: var(--nh-accent); }

/* ============================================================ Hero. */
.nh-hero {
    position: relative;
    overflow: hidden;
    background-color: var(--nh-primary-darkest);
    background-size: cover;
    background-position: center;
    color: #fff;
    padding: clamp(110px, 14vw, 180px) 0 clamp(96px, 12vw, 150px);
    border-bottom: 1px solid var(--nh-line-soft);
}
.nh-hero--pattern {
    background-image:
        radial-gradient(800px 500px at 90% -10%, rgba(201, 161, 74, .22), transparent 55%),
        radial-gradient(900px 700px at 0% 110%, rgba(0, 110, 181, .55), transparent 60%),
        linear-gradient(135deg, #06182f 0%, #0c2d57 55%, #0a4f8a 100%);
}
.nh-hero--photo {
    background-image:
        linear-gradient(115deg, rgba(8, 22, 52, .92) 0%, rgba(8, 22, 52, .72) 45%, rgba(8, 22, 52, .35) 100%),
        var(--nh-hero-image);
}
.nh-hero__grid {
    position: absolute;
    inset: 0;
    background-image:
        linear-gradient(rgba(255, 255, 255, .045) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, .045) 1px, transparent 1px);
    background-size: 88px 88px;
    mask-image: linear-gradient(180deg, rgba(0, 0, 0, .9), rgba(0, 0, 0, .15) 75%, transparent);
    pointer-events: none;
}
.nh-hero__contour {
    position: absolute;
    right: -120px;
    top: -120px;
    width: 720px;
    height: 720px;
    border-radius: 50%;
    background:
        radial-gradient(closest-side, transparent 58%, rgba(201, 161, 74, .35) 59%, transparent 60%),
        radial-gradient(closest-side, transparent 68%, rgba(255, 255, 255, .08) 69%, transparent 70%),
        radial-gradient(closest-side, transparent 78%, rgba(255, 255, 255, .05) 79%, transparent 80%),
        radial-gradient(closest-side, transparent 88%, rgba(255, 255, 255, .03) 89%, transparent 90%);
    pointer-events: none;
    mix-blend-mode: screen;
}
.nh-hero__inner { position: relative; z-index: 1; }
.nh-hero__content { max-width: 760px; }
.nh-hero__title {
    font-family: 'Source Serif 4', Georgia, 'Times New Roman', serif;
    font-size: clamp(40px, 5.4vw, 68px);
    line-height: 1.05;
    letter-spacing: -.018em;
    margin: 0 0 22px;
    font-weight: 600;
    color: #fff;
}
.nh-hero__subtitle {
    font-size: clamp(17px, 1.4vw, 19px);
    color: rgba(232, 234, 241, .92);
    margin: 0 0 36px;
    max-width: 640px;
    line-height: 1.6;
}
.nh-hero__actions { display: flex; gap: 12px; flex-wrap: wrap; }

.nh-hero__metrics {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 0;
    margin: 56px 0 0;
    padding: 28px 0 0;
    border-top: 1px solid rgba(255, 255, 255, .15);
    max-width: 720px;
}
.nh-hero__metric { padding-right: 24px; }
.nh-hero__metric + .nh-hero__metric {
    border-left: 1px solid rgba(255, 255, 255, .12);
    padding-left: 24px;
}
.nh-hero__metric dt {
    font-family: var(--nh-font-head);
    font-size: clamp(28px, 2.6vw, 36px);
    font-weight: 600;
    color: #fff;
    margin: 0 0 4px;
    letter-spacing: -.01em;
}
.nh-hero__metric dd {
    margin: 0;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: .14em;
    color: rgba(232, 234, 241, .7);
}

/* ============================================================ Sections. */
.nh-section {
    /* padding: clamp(72px, 9vw, 112px) 0; */
    /* padding: clamp(48px, 6vw, 88px) 0; */
    padding: 28px 0;
    position: relative;
}
.nh-section--muted {
    background: var(--nh-paper);
    border-top: 1px solid var(--nh-line-soft);
    border-bottom: 1px solid var(--nh-line-soft);
}
.nh-section--soft {
    background:
        radial-gradient(900px 480px at 85% -10%, rgba(0, 110, 181, .07), transparent 60%),
        radial-gradient(700px 420px at 0% 110%, rgba(201, 161, 74, .08), transparent 55%),
        var(--nh-paper-warm);
    border-top: 1px solid var(--nh-line-soft);
    border-bottom: 1px solid var(--nh-line-soft);
}
.nh-section--dark {
    background: var(--nh-primary-deep);
    color: #fff;
    position: relative;
    overflow: hidden;
}
.nh-section--dark::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image:
        linear-gradient(rgba(255, 255, 255, .04) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, .04) 1px, transparent 1px);
    background-size: 88px 88px;
    mask-image: linear-gradient(180deg, rgba(0, 0, 0, .6), transparent 70%);
    pointer-events: none;
}
.nh-section > .nh-container { position: relative; z-index: 1; }

.nh-section__intro { max-width: 780px; margin: 0 0 56px; }
.nh-section__intro--center { margin-left: auto; margin-right: auto; text-align: center; }
.nh-section__intro--center .nh-eyebrow { justify-content: center; }

.nh-section__title {
    font-family: var(--nh-font-head);
    font-size: clamp(28px, 3.2vw, 44px);
    line-height: 1.1;
    margin: 0 0 16px;
    letter-spacing: -.012em;
    font-weight: 600;
    color: var(--nh-ink);
}
.nh-section__title--light { color: #fff; }
.nh-section__lede {
    font-size: 17px;
    color: var(--nh-ink-soft);
    margin: 0;
    line-height: 1.7;
    max-width: 64ch;
}
.nh-section__intro--center .nh-section__lede { margin-left: auto; margin-right: auto; }
.nh-section--dark .nh-section__lede { color: rgba(232, 234, 241, .82); }

/* ============================================================ Grids and cards. */
.nh-grid { display: grid; gap: 1px; background: var(--nh-line-soft); border: 1px solid var(--nh-line-soft); }
.nh-grid--3 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.nh-grid--4 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }

.nh-card,
.nh-role {
    background: #fff;
    padding: 36px 32px;
    position: relative;
    transition: background .2s ease;
}
.nh-section--muted .nh-card,
.nh-section--muted .nh-role { background: #fff; }
.nh-card::before,
.nh-role::before {
    content: '';
    position: absolute;
    left: 32px;
    right: 32px;
    top: 0;
    height: 2px;
    background: var(--nh-accent);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform .25s ease;
}
.nh-card:hover::before,
.nh-role:hover::before { transform: scaleX(1); }

.nh-card__icon,
.nh-role__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    background: var(--nh-paper-warm);
    color: var(--nh-primary);
    margin-bottom: 22px;
    font-size: 20px;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius);
}
.nh-card__title,
.nh-role__title {
    font-family: var(--nh-font-head);
    font-size: 21px;
    margin: 0 0 10px;
    font-weight: 600;
    color: var(--nh-ink);
    letter-spacing: -.005em;
}
.nh-card__body,
.nh-role__body { color: var(--nh-ink-soft); margin: 0; line-height: 1.65; font-size: 15px; }

/* Role / journey call-to-action. */
.nh-role { display: flex; flex-direction: column; }
.nh-role__body { flex: 1; }
.nh-role__cta {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-top: 20px;
    color: var(--nh-primary);
    font-weight: 600;
    font-size: 14px;
    text-decoration: none;
    align-self: flex-start;
}
.nh-role__cta-arrow { transition: transform .2s ease; }
.nh-role__cta:hover { color: #005a96; }
.nh-role__cta:hover .nh-role__cta-arrow { transform: translateX(3px); }

/* ============================================================ Learning areas. */
.nh-learn {
    display: flex;
    flex-direction: column;
    background: #fff;
    padding: 36px 32px;
    position: relative;
    text-decoration: none;
    color: inherit;
    transition: background .2s ease, transform .2s ease;
}
.nh-learn::before {
    content: '';
    position: absolute;
    left: 32px;
    right: 32px;
    top: 0;
    height: 2px;
    background: var(--nh-accent);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform .25s ease;
}
.nh-learn:hover::before { transform: scaleX(1); }
.nh-learn:hover { background: var(--nh-paper); }
.nh-learn__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 52px;
    height: 52px;
    border-radius: var(--nh-radius);
    background: var(--nh-primary);
    color: #fff;
    font-size: 22px;
    margin-bottom: 22px;
}
.nh-learn__title {
    font-family: var(--nh-font-head);
    font-size: 21px;
    margin: 0 0 10px;
    font-weight: 600;
    color: var(--nh-ink);
}
.nh-learn__body { color: var(--nh-ink-soft); margin: 0 0 18px; line-height: 1.65; font-size: 15px; flex: 1; }
.nh-learn__link {
    color: var(--nh-primary);
    font-weight: 600;
    font-size: 13px;
    letter-spacing: .02em;
}

/* ============================================================ Stats / impact. */
.nh-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 0;
    border-top: 1px solid rgba(255, 255, 255, .12);
}
.nh-stat {
    padding: 40px 24px 8px;
    text-align: left;
    border-right: 1px solid rgba(255, 255, 255, .08);
    position: relative;
}
.nh-stat:last-child { border-right: 0; }
.nh-stat::before {
    content: '';
    position: absolute;
    left: 0;
    top: -1px;
    width: 40px;
    height: 2px;
    background: var(--nh-accent);
}
.nh-stat__value {
    display: block;
    font-family: var(--nh-font-head);
    font-size: clamp(40px, 5vw, 64px);
    font-weight: 600;
    letter-spacing: -.02em;
    color: #fff;
    line-height: 1;
}
.nh-stat__label {
    display: block;
    margin-top: 14px;
    color: rgba(232, 234, 241, .72);
    font-size: 12px;
    letter-spacing: .18em;
    text-transform: uppercase;
}
@media (max-width: 720px) {
    .nh-stat { border-right: 0; border-bottom: 1px solid rgba(255, 255, 255, .08); }
    .nh-stat:last-child { border-bottom: 0; }
}

/* ============================================================ Partners. */
.nh-partners {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 1px;
    background: var(--nh-line-soft);
    border: 1px solid var(--nh-line-soft);
}
.nh-partners__item {
    background: #fff;
    min-height: 120px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 28px 20px;
    transition: background .2s ease;
}
.nh-partners__item:hover { background: var(--nh-paper); }
.nh-partners__item img {
    max-height: 48px;
    max-width: 80%;
    object-fit: contain;
    filter: grayscale(1) contrast(.9);
    opacity: .75;
    transition: filter .2s ease, opacity .2s ease;
}
.nh-partners__item:hover img { filter: none; opacity: 1; }
.nh-partners__fallback {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
}
.nh-partners__initials {
    width: 46px;
    height: 46px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--nh-paper-warm);
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius);
    font-family: var(--nh-font-head);
    font-weight: 700;
    color: var(--nh-primary);
    font-size: 18px;
}
.nh-partners__name {
    font-family: var(--nh-font-head);
    font-weight: 600;
    color: var(--nh-ink);
    text-align: center;
    font-size: 16px;
}
.nh-partners__item a { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; text-decoration: none; }

/* ============================================================ Testimonials. */
.nh-carousel { position: relative; }
.nh-carousel__track {
    list-style: none;
    margin: 0;
    padding: 4px;
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(320px, 1fr);
    gap: 24px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    scrollbar-width: thin;
}
@media (min-width: 961px) {
    /* Show all three side by side on wide screens; carousel takes over below. */
    .nh-carousel__track { grid-auto-columns: 1fr; overflow-x: visible; }
}
.nh-quote {
    scroll-snap-align: start;
    background: #fff;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-lg);
    padding: 32px;
    display: flex;
    flex-direction: column;
    box-shadow: var(--nh-shadow);
}
.nh-quote__stars { color: var(--nh-accent); font-size: 14px; margin-bottom: 18px; letter-spacing: 2px; }
.nh-quote__star--empty { color: var(--nh-line); }
.nh-quote__text {
    margin: 0 0 24px;
    font-family: var(--nh-font-head);
    font-size: 18px;
    line-height: 1.6;
    color: var(--nh-ink);
    flex: 1;
}
.nh-quote__text::before { content: '\201C'; }
.nh-quote__text::after { content: '\201D'; }
.nh-quote__person { display: flex; align-items: center; gap: 14px; padding-top: 20px; border-top: 1px solid var(--nh-line-soft); }
.nh-quote__avatar {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: var(--nh-primary);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 18px;
    flex: 0 0 auto;
}
.nh-quote__meta { display: flex; flex-direction: column; }
.nh-quote__name { font-weight: 600; color: var(--nh-ink); font-size: 15px; }
.nh-quote__role { color: var(--nh-ink-muted); font-size: 13px; }

.nh-carousel__controls { display: flex; justify-content: center; gap: 12px; margin-top: 28px; }
.nh-carousel__btn {
    width: 46px;
    height: 46px;
    border-radius: 50%;
    border: 1px solid var(--nh-line);
    background: #fff;
    color: var(--nh-primary);
    cursor: pointer;
    font-size: 15px;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.nh-carousel__btn:hover { background: var(--nh-primary); color: #fff; border-color: var(--nh-primary); }
.nh-carousel__btn[hidden] { display: none; }

/* ============================================================ About (two-column). */
.nh-about {
    display: grid;
    grid-template-columns: 1.05fr .95fr;
    gap: 56px;
    align-items: center;
    margin-bottom: 64px;
}
.nh-about__text .nh-section__title { margin-top: 6px; }
.nh-about__text .nh-section__lede { max-width: 52ch; }
.nh-about__media { position: relative; }
.nh-about__media img {
    display: block;
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    border-radius: var(--nh-radius-lg);
    box-shadow: var(--nh-shadow-lift);
}
.nh-about__media::after {
    /* Gold corner accent echoing the brand mark. */
    content: '';
    position: absolute;
    right: -10px;
    bottom: -10px;
    width: 64px;
    height: 64px;
    background: var(--nh-accent);
    border-radius: 2px;
    z-index: -1;
}
.nh-about__panel {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 4 / 3;
    border-radius: var(--nh-radius-lg);
    overflow: hidden;
    color: rgba(255, 255, 255, .9);
    background:
        radial-gradient(700px 400px at 80% -10%, rgba(201, 161, 74, .25), transparent 55%),
        linear-gradient(135deg, #06182f 0%, #0c2d57 60%, #0a4f8a 100%);
    box-shadow: var(--nh-shadow-lift);
}
.nh-about__panel i { font-size: clamp(56px, 8vw, 96px); opacity: .9; }
.nh-about__panel-mark {
    position: absolute;
    top: 18px;
    left: 18px;
    font-family: var(--nh-font-head);
    font-weight: 700;
    letter-spacing: .04em;
    color: rgba(255, 255, 255, .85);
    background: rgba(255, 255, 255, .12);
    padding: 6px 10px;
    border-radius: 3px;
    font-size: 14px;
}
.nh-about__rolesintro { margin-top: 72px; }
@media (max-width: 860px) {
    .nh-about { grid-template-columns: 1fr; gap: 32px; }
    .nh-about__media { order: -1; }
}

/* ============================================================ Blog cards.
   The card styles that used to be here are gone. Every one of them --
   .nh-post, its cover, body, title and excerpt -- is defined by the
   editorial blog design at the end of this file, and keeping a first
   definition here was not merely redundant: the old cover set a fixed
   height: 150px, and a later `aspect-ratio` does not clear an earlier
   `height`, so the new covers were silently pinned to the old size. */

/* ============================================================ FAQ. */
.nh-faq {
    max-width: 820px;
    margin: 0 auto;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-lg);
    overflow: hidden;
    background: #fff;
}
.nh-faq__item { border-bottom: 1px solid var(--nh-line-soft); }
.nh-faq__item:last-child { border-bottom: 0; }
.nh-faq__q {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    cursor: pointer;
    padding: 22px 24px;
    font-family: var(--nh-font-head);
    font-size: 18px;
    font-weight: 600;
    color: var(--nh-ink);
    list-style: none;
}
.nh-faq__q::-webkit-details-marker { display: none; }
.nh-faq__q:hover { color: var(--nh-primary); }
.nh-faq__icon {
    position: relative;
    flex: 0 0 auto;
    width: 18px;
    height: 18px;
}
.nh-faq__icon::before,
.nh-faq__icon::after {
    content: '';
    position: absolute;
    background: var(--nh-primary);
    transition: transform .2s ease, opacity .2s ease;
}
.nh-faq__icon::before { top: 8px; left: 0; width: 18px; height: 2px; }
.nh-faq__icon::after  { left: 8px; top: 0; width: 2px; height: 18px; }
.nh-faq__item[open] .nh-faq__icon::after { transform: scaleY(0); opacity: 0; }
.nh-faq__a {
    padding: 0 24px 24px;
    color: var(--nh-ink-soft);
    line-height: 1.7;
    font-size: 15px;
}
.nh-faq__a p { margin: 0; }
@media (max-width: 540px) {
    .nh-faq__q { padding: 18px; font-size: 16px; }
    .nh-faq__a { padding: 0 18px 18px; }
}

.nh-faq__cta {
    max-width: 820px;
    margin: 28px auto 0;
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
}
.nh-faq__cta p { margin: 0; color: var(--nh-ink-soft); font-size: 15px; }

/* ============================================================ Page header (sub-pages). */
.nh-pagehead {
    position: relative;
    overflow: hidden;
    background-image:
        radial-gradient(700px 400px at 85% -20%, rgba(201, 161, 74, .2), transparent 55%),
        linear-gradient(135deg, #06182f 0%, #0c2d57 60%, #0a4f8a 100%);
    color: #fff;
    padding: clamp(96px, 12vw, 140px) 0 clamp(48px, 6vw, 72px);
}
.nh-pagehead .nh-container { position: relative; z-index: 1; }
.nh-pagehead__title {
    font-family: var(--nh-font-head);
    font-size: clamp(34px, 4.4vw, 56px);
    line-height: 1.08;
    letter-spacing: -.015em;
    margin: 0 0 16px;
    font-weight: 600;
    color: #fff;
}
.nh-pagehead__lede {
    font-size: clamp(16px, 1.4vw, 19px);
    color: rgba(232, 234, 241, .88);
    margin: 0;
    max-width: 60ch;
    line-height: 1.6;
}

/* ============================================================ Hover lifts (subtle). */
.nh-learn:hover { transform: translateY(-4px); }
.nh-learn:hover .nh-learn__icon { transform: scale(1.06); }
.nh-learn__icon { transition: transform .2s ease; }
.nh-quote { transition: transform .2s ease, box-shadow .2s ease; }
.nh-quote:hover { transform: translateY(-4px); box-shadow: var(--nh-shadow-lift); }
.nh-card:hover,
.nh-role:hover { background: var(--nh-paper); }
.nh-card__icon,
.nh-role__icon { transition: transform .2s ease, background .2s ease; }
.nh-card:hover .nh-card__icon,
.nh-role:hover .nh-role__icon { transform: translateY(-2px); }

/* ============================================================ Contact. */
.nh-contact {
    display: grid;
    grid-template-columns: 1fr 1.25fr;
    gap: 64px;
    background: #fff;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-lg);
    padding: 56px;
    position: relative;
    box-shadow: var(--nh-shadow);
}
.nh-contact::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    width: 64px;
    height: 4px;
    background: var(--nh-accent);
    border-radius: var(--nh-radius-lg) 0 0 0;
}
@media (max-width: 960px) {
    .nh-contact { grid-template-columns: 1fr; padding: 36px; gap: 32px; }
}
.nh-contact__intro .nh-section__title { margin-top: 0; }
.nh-contact__details {
    list-style: none;
    margin: 28px 0 0;
    padding: 24px 0 0;
    border-top: 1px solid var(--nh-line-soft);
    display: grid;
    gap: 14px;
}
.nh-contact__details li {
    display: flex;
    align-items: center;
    gap: 12px;
    color: var(--nh-ink-soft);
    font-size: 14px;
}
.nh-contact__details i { color: var(--nh-primary); width: 18px; text-align: center; }
.nh-contact__details a {
    color: var(--nh-primary);
    text-decoration: none;
    font-weight: 600;
}
.nh-contact__details a:hover { text-decoration: underline; }

.nh-contact__form {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 18px;
}
.nh-contact__status { grid-column: 1 / -1; }
.nh-contact__status:empty { display: none; }
.nh-field { display: flex; flex-direction: column; }
.nh-field--full { grid-column: 1 / -1; }
.nh-field label {
    font-size: 11px;
    font-weight: 600;
    margin-bottom: 8px;
    color: var(--nh-ink-muted);
    text-transform: uppercase;
    letter-spacing: .14em;
}
.nh-field input,
.nh-field textarea {
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius);
    padding: 13px 14px;
    font-size: 15px;
    color: var(--nh-ink);
    background: #fff;
    transition: border-color .15s ease, box-shadow .15s ease;
    font-family: inherit;
}
.nh-field input:focus,
.nh-field textarea:focus {
    outline: none;
    border-color: var(--nh-primary);
    box-shadow: var(--nh-focus);
}

/* Filled in from the account and not editable — the contact form's name and
   email once you are signed in. It has to LOOK settled rather than broken: a
   quiet ground and a text cursor that does not invite typing, but full contrast
   on the value itself, because it is information the sender still has to be able
   to read and check. Focus is deliberately kept — a readonly field is still
   reachable by keyboard, and a ring that never appears is a lost caret. */
.nh-field input[readonly] {
    background: var(--nh-paper);
    border-color: var(--nh-line);
    color: var(--nh-ink-soft);
    cursor: default;
}

.nh-field input[readonly]:focus {
    border-color: var(--nh-line-strong, #d3dae6);
    box-shadow: none;
}
/* Validation errors are only shown after the user submits (JS adds
   .nh-field--error); fields are never red just for being empty. */
.nh-field--error input,
.nh-field--error textarea { border-color: #c0392b; }
.nh-field--error input:focus,
.nh-field--error textarea:focus { box-shadow: 0 0 0 3px rgba(192, 57, 43, .22); }
.nh-field__error {
    display: block;
    margin-top: 6px;
    font-size: 12.5px;
    font-weight: 500;
    color: #c0392b;
}
.nh-field__error[hidden] { display: none; }
.nh-field textarea { resize: vertical; min-height: 120px; }
@media (max-width: 540px) {
    .nh-contact__form { grid-template-columns: 1fr; }
}

.nh-alert {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    border-left: 3px solid;
    padding: 14px 16px;
    font-size: 14px;
    background: var(--nh-paper);
    border-radius: 0 var(--nh-radius) var(--nh-radius) 0;
}
.nh-alert i { margin-top: 2px; }
.nh-alert--success { border-color: #2f855a; color: #225b3e; }
.nh-alert--error   { border-color: #c0392b; color: #8e2a1f; }

/* ============================================================ Footer. */
.nh-footer {
    background:
        radial-gradient(900px 500px at 88% -20%, rgba(0, 110, 181, .25), transparent 60%),
        radial-gradient(700px 460px at 0% 120%, rgba(201, 161, 74, .12), transparent 55%),
        linear-gradient(180deg, #081a38 0%, #06122a 100%);
    color: #cbd5e1;
    /* The band opened with 72px of nothing above the first word. That much lead-in
       belongs to a section somebody scrolls INTO; the footer is where the page
       stops, and the space was reading as the page having ended early. Scaled
       with the viewport so the same judgement holds on a phone and on a desktop
       rather than needing a breakpoint to undo it. */
    padding: clamp(28px, 4vw, 44px) 0 0;
    position: relative;
    overflow: hidden;
}
.nh-footer::before {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    height: 3px;
    background: var(--nh-accent);
    z-index: 2;
}
.nh-footer::after {
    /* Faint architectural grid, matching the dark sections. */
    content: '';
    position: absolute;
    inset: 0;
    background-image:
        linear-gradient(rgba(255, 255, 255, .035) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, .035) 1px, transparent 1px);
    background-size: 80px 80px;
    mask-image: linear-gradient(180deg, rgba(0, 0, 0, .5), transparent 65%);
    pointer-events: none;
}
.nh-footer__inner,
.nh-footer__legal { position: relative; z-index: 1; }
.nh-footer__inner {
    display: grid;
    grid-template-columns: 1.6fr repeat(3, 1fr);
    gap: 48px;
    /* Enough to separate the columns from the copyright rule, not enough to read
       as a gap of its own. */
    padding-bottom: clamp(20px, 3vw, 32px);
}
@media (max-width: 820px) {
    .nh-footer__inner { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 520px) {
    .nh-footer__inner { grid-template-columns: 1fr; gap: 36px; }
}
/* The heading is the first thing in the column now that the logo has gone, so
   there is nothing above it to be separated from -- the 16px that used to sit here
   would leave the column starting lower than the three beside it. */
.nh-footer__brand strong {
    display: block;
    color: #fff;
    margin: 0 0 10px;
    font-family: var(--nh-font-head);
    font-size: 20px;
    font-weight: 600;
}
.nh-footer__brand p {
    color: #94a3b8;
    line-height: 1.65;
    margin: 0;
    font-size: 14px;
    max-width: 36ch;
}
.nh-footer__col h4 {
    color: #fff;
    font-size: 12px;
    margin: 0 0 18px;
    text-transform: uppercase;
    letter-spacing: .18em;
    font-weight: 600;
}
.nh-footer__col ul { list-style: none; margin: 0; padding: 0; }
.nh-footer__col li { margin-bottom: 10px; }
.nh-footer__col a {
    color: #cbd5e1;
    text-decoration: none;
    font-size: 14px;
    transition: color .15s ease;
}
.nh-footer__col a:hover { color: var(--nh-accent); }
.nh-footer__contact li { display: flex; align-items: flex-start; gap: 10px; }
.nh-footer__contact i { color: var(--nh-accent); margin-top: 3px; width: 16px; text-align: center; }
.nh-footer__contact span { font-size: 14px; }

.nh-social {
    list-style: none;
    margin: 24px 0 0;
    padding: 0;
    display: flex;
    gap: 10px;
}
.nh-social a {
    width: 38px;
    height: 38px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 255, 255, .06);
    color: #cbd5e1;
    text-decoration: none;
    transition: background .15s ease, color .15s ease, transform .15s ease;
}
.nh-social a:hover { background: var(--nh-primary); color: #fff; transform: translateY(-2px); }

.nh-footer__legal {
    border-top: 1px solid rgba(255, 255, 255, .06);
    padding: 16px 0;
    color: #64748b;
    font-size: 12px;
    letter-spacing: .04em;
}
.nh-footer__legal .nh-container {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
}

/* ============================================================ Scroll-reveal & counters. */
.nh-count[data-nh-count] { font-variant-numeric: tabular-nums; }

.nh-reveal {
    opacity: 0;
    transform: translateY(16px);
    transition: opacity .6s ease, transform .6s ease;
    will-change: opacity, transform;
}
.nh-reveal.is-visible { opacity: 1; transform: none; }

/* ============================================================ Reduced motion. */
@media (prefers-reduced-motion: reduce) {
    .nh-page *,
    .nh-nav *,
    .nh-footer * {
        animation-duration: .001ms !important;
        transition-duration: .001ms !important;
        scroll-behavior: auto !important;
    }
    .nh-reveal { opacity: 1; transform: none; }
}
/* ============================================================ Brand logo (nav + footer). */
.nh-nav__brand-logo { height: 38px; width: auto; max-width: 180px; display: block; object-fit: contain; }

/* ============================================================ KPI / impact cards. */
.nh-kpiband { padding-top: clamp(44px, 5vw, 68px); padding-bottom: clamp(44px, 5vw, 68px); }
.nh-kpis {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: 20px;
}
.nh-kpi {
    background: #fff;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-lg);
    padding: 26px 24px;
    box-shadow: var(--nh-shadow);
    transition: transform .2s ease, box-shadow .2s ease;
}
.nh-kpi:hover { transform: translateY(-4px); box-shadow: var(--nh-shadow-lift); }
.nh-kpi__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: 12px;
    font-size: 18px;
    margin-bottom: 16px;
    background: rgba(0, 110, 181, .1);
    color: var(--nh-primary);
}
.nh-kpi:nth-child(2) .nh-kpi__icon { background: rgba(124, 58, 237, .12); color: #7c3aed; }
.nh-kpi:nth-child(3) .nh-kpi__icon { background: rgba(220, 80, 40, .12); color: #d4541e; }
.nh-kpi:nth-child(4) .nh-kpi__icon { background: rgba(22, 163, 74, .12); color: #16a34a; }
.nh-kpi__value {
    display: block;
    font-family: var(--nh-font-head);
    font-size: clamp(30px, 3.4vw, 40px);
    font-weight: 700;
    line-height: 1;
    color: var(--nh-ink);
    letter-spacing: -.02em;
}
.nh-kpi__label {
    display: block;
    margin-top: 8px;
    font-size: 11px;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--nh-ink-muted);
    font-weight: 600;
}

/* ============================================================ About media (interactive). */
.nh-about__media {
    position: relative;
    overflow: hidden;
    border-radius: var(--nh-radius-lg);
    cursor: pointer;
    outline: none;
}
.nh-about__media::after { display: none; }
.nh-about__media img,
.nh-about__panel { transition: transform .55s cubic-bezier(.2, .7, .2, 1); }
.nh-about__media:hover img,
.nh-about__media:focus-visible img,
.nh-about__media:hover .nh-about__panel,
.nh-about__media:focus-visible .nh-about__panel { transform: scale(1.06); }
.nh-about__overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: flex-end;
    padding: 20px;
    color: #fff;
    font-weight: 600;
    font-size: 13px;
    letter-spacing: .02em;
    background: linear-gradient(to top, rgba(6, 24, 47, .72), rgba(6, 24, 47, 0) 55%);
    opacity: 0;
    transition: opacity .3s ease;
}
.nh-about__overlay i { margin-right: 8px; }
.nh-about__media:hover .nh-about__overlay,
.nh-about__media:focus-visible .nh-about__overlay { opacity: 1; }
.nh-about__media:focus-visible { box-shadow: var(--nh-focus); }

/* ============================================================ Choose Your Path (pathways). */
.nh-paths__tabs {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 6px;
    background: #fff;
    border: 1px solid var(--nh-line);
    border-radius: 999px;
    padding: 6px;
    margin-bottom: 28px;
}
.nh-paths__tab {
    border: 0;
    background: transparent;
    padding: 10px 20px;
    border-radius: 999px;
    font-family: inherit;
    font-weight: 600;
    font-size: 14px;
    color: var(--nh-ink-soft);
    cursor: pointer;
    transition: background .18s ease, color .18s ease;
}
.nh-paths__tab:hover { color: var(--nh-primary); }
.nh-paths__tab.is-active { background: var(--nh-primary); color: #fff; }
.nh-path {
    display: grid;
    grid-template-columns: 1.45fr .85fr;
    background: #fff;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-lg);
    overflow: hidden;
    box-shadow: var(--nh-shadow);
}
.nh-path[hidden] { display: none; }
.nh-path__main { padding: clamp(28px, 4vw, 44px); }
.nh-path__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 52px;
    height: 52px;
    border-radius: 14px;
    background: var(--nh-paper-warm);
    color: var(--nh-primary);
    font-size: 22px;
    margin-bottom: 18px;
}
.nh-path__title {
    font-family: var(--nh-font-head);
    font-size: clamp(24px, 2.6vw, 32px);
    font-weight: 600;
    margin: 0 0 12px;
    color: var(--nh-ink);
}
.nh-path__body { color: var(--nh-ink-soft); line-height: 1.65; margin: 0 0 24px; max-width: 52ch; }
.nh-path__courses {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}
.nh-path__courses li {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    font-size: 14px;
    color: var(--nh-ink);
    background: var(--nh-paper);
    border: 1px solid var(--nh-line-soft);
    border-radius: var(--nh-radius);
    padding: 12px 14px;
}
.nh-path__courses i { color: var(--nh-primary); margin-top: 2px; }
.nh-path__outcomes {
    background: var(--nh-primary-deep);
    color: #fff;
    padding: clamp(28px, 4vw, 44px);
    display: flex;
    flex-direction: column;
}
.nh-path__outcomes h4 {
    margin: 0 0 18px;
    font-size: 12px;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: #f0d68a;
}
.nh-path__outcomes ul { list-style: none; margin: 0 0 24px; padding: 0; flex: 1; }
.nh-path__outcomes li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 0;
    border-bottom: 1px solid rgba(255, 255, 255, .12);
    font-weight: 600;
    font-size: 15px;
}
.nh-path__outcomes li i { color: #f0d68a; font-size: 12px; }
@media (max-width: 760px) {
    .nh-path { grid-template-columns: 1fr; }
    .nh-path__courses { grid-template-columns: 1fr; }
}

/* ============================================================ Module catalogue. */
.nh-catalog__head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 24px;
    flex-wrap: wrap;
    margin-bottom: 24px;
}
.nh-catalog__head .nh-section__intro { margin-bottom: 0; }
.nh-catalog__search { position: relative; flex: 0 0 auto; }
.nh-catalog__search i {
    position: absolute;
    left: 16px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--nh-ink-muted);
    font-size: 14px;
}
.nh-catalog__search input {
    border: 1px solid var(--nh-line);
    border-radius: 999px;
    padding: 12px 18px 12px 40px;
    font-size: 14px;
    min-width: 280px;
    font-family: inherit;
    background: #fff;
    color: var(--nh-ink);
    transition: border-color .15s ease, box-shadow .15s ease;
}
.nh-catalog__search input:focus { outline: none; border-color: var(--nh-primary); box-shadow: var(--nh-focus); }
.nh-catalog__filters { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 28px; }
.nh-pill {
    border: 1px solid var(--nh-line);
    background: #fff;
    padding: 8px 16px;
    border-radius: 999px;
    font-family: inherit;
    font-size: 13px;
    font-weight: 600;
    color: var(--nh-ink-soft);
    cursor: pointer;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.nh-pill:hover { border-color: var(--nh-primary); color: var(--nh-primary); }
.nh-pill.is-active { background: var(--nh-primary-deep); border-color: transparent; color: #fff; }
.nh-modules { background: transparent; border: 0; gap: 24px; }
.nh-module {
    /* The positioning context for the two corner controls the card now
       carries. Without it they would hang off whichever ancestor happened to
       be positioned, which on the landing page is the carousel track. */
    position: relative;
    display: flex;
    flex-direction: column;
    background: #fff;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-lg);
    overflow: hidden;
    transition: transform .2s ease, box-shadow .2s ease;
}
.nh-module[hidden] { display: none; }
.nh-module:hover { transform: translateY(-4px); box-shadow: var(--nh-shadow-lift); }
.nh-module__cover {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 140px;
    color: rgba(255, 255, 255, .95);
    font-size: 38px;
    text-decoration: none;
    background: linear-gradient(135deg, #0c2d57 0%, #0a4f8a 100%);
}
.nh-module[data-category="ai"] .nh-module__cover { background: linear-gradient(135deg, #4c1d95 0%, #7c3aed 100%); }
.nh-module[data-category="cyber"] .nh-module__cover { background: linear-gradient(135deg, #0f766e 0%, #14b8a6 100%); }
.nh-module[data-category="business"] .nh-module__cover { background: linear-gradient(135deg, #9a3412 0%, #ea580c 100%); }
.nh-module[data-category="programming"] .nh-module__cover { background: linear-gradient(135deg, #064e3b 0%, #16a34a 100%); }
.nh-module__body { padding: 20px; display: flex; flex-direction: column; gap: 10px; flex: 1; }
/* The button sits at the foot of the card, so buttons line up across a rail of
   cards whose titles are different lengths.

   It is pinned here rather than by letting the title stretch into the gap (the
   title used to carry `flex: 1`). Same alignment, but the slack now belongs to
   the button's own margin instead of inflating the title's box — which is what
   made the space between a one-line title and its button read as a hole. */
.nh-module__body > .nh-btn { margin-top: auto; }
.nh-module__meta { display: flex; align-items: center; gap: 8px; margin: 0; font-size: 11px; }
.nh-module__cat { color: var(--nh-primary); font-weight: 700; text-transform: uppercase; letter-spacing: .1em; }
.nh-module__level { color: var(--nh-ink-muted); }
.nh-module__level::before { content: '·'; margin-right: 8px; color: var(--nh-line); }
.nh-module__title {
    font-family: var(--nh-font-head);
    font-size: 19px;
    line-height: 1.25;
    margin: 0;
    font-weight: 600;
    color: var(--nh-ink);
    /* Its natural height, and no more. `flex: 1` here used to make the title
       absorb every spare pixel in the card, which is why a one-line title left a
       tall empty band above its button. The button pins itself to the foot now. */
    flex: 0 0 auto;
    /* Two lines, so one long course name cannot make every card in the rail
       taller than it needs to be — the cards stretch to match the tallest, so
       that one outlier was paying for the gap on all of them. The same cap the
       library card uses, for the same reason. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    overflow-wrap: anywhere;
}
.nh-catalog__empty { text-align: center; color: var(--nh-ink-muted); padding: 32px 0 0; }
@media (max-width: 560px) {
    .nh-catalog__search,
    .nh-catalog__search input { width: 100%; min-width: 0; }
}

/* ============================================================ Testimonial avatar image. */
.nh-quote__avatar { overflow: hidden; }
.nh-quote__avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }

/* ============================================================ Optional section background images. */
.nh-section--bg {
    background-image: var(--nh-section-bg);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    position: relative;
    isolation: isolate;
}
.nh-section--bg::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 0;
    background: linear-gradient(rgba(247, 249, 252, .85), rgba(247, 249, 252, .92));
}
/* Soft-tinted section keeps a faint blue wash over the photo. */
.nh-section--soft.nh-section--bg::before {
    background: linear-gradient(rgba(238, 243, 249, .84), rgba(238, 243, 249, .92));
}
.nh-section--bg > .nh-container { position: relative; z-index: 1; }

.nh-footer--bg {
    background: linear-gradient(rgba(6, 18, 42, .88), rgba(6, 18, 42, .93)), var(--nh-section-bg);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}

/* ============================================================ Partners: full colour + scroll on smaller screens. */
.nh-partners__item img { filter: none !important; opacity: 1 !important; }

@media (max-width: 960px) {
    .nh-partners {
        display: flex;
        flex-wrap: nowrap;
        overflow-x: auto;
        gap: 14px;
        background: transparent;
        border: 0;
        padding-bottom: 10px;
        scroll-snap-type: x mandatory;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: thin;
    }
    .nh-partners__item {
        flex: 0 0 auto;
        width: 58%;
        min-width: 180px;
        max-width: 240px;
        scroll-snap-align: start;
        border: 1px solid var(--nh-line);
        border-radius: var(--nh-radius-lg);
        background: #fff;
    }
}

/* ============================================================ Pathway tabs: responsive. */
@media (max-width: 720px) {
    .nh-paths__tabs {
        display: flex;
        flex-wrap: nowrap;
        overflow-x: auto;
        width: 100%;
        border-radius: 16px;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
    }
    .nh-paths__tabs::-webkit-scrollbar { display: none; }
    .nh-paths__tab { flex: 0 0 auto; white-space: nowrap; }
}
/* ============================================================ Course cover image. */
.nh-module__cover--img { padding: 0; height: 150px; }
.nh-module__cover img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ============================================================ Pathway panel transition (professional). */
@keyframes nh-path-in {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
}
.nh-path.is-active { animation: nh-path-in .35s ease both; }

/* Slightly stagger revealed cards within a grid for a polished cascade. */
.nh-grid .nh-reveal { transition-delay: 0ms; }
.nh-grid > .nh-reveal:nth-child(2) { transition-delay: 60ms; }
.nh-grid > .nh-reveal:nth-child(3) { transition-delay: 120ms; }
.nh-grid > .nh-reveal:nth-child(4) { transition-delay: 180ms; }
.nh-kpis .nh-reveal:nth-child(2) { transition-delay: 60ms; }
.nh-kpis .nh-reveal:nth-child(3) { transition-delay: 120ms; }
.nh-kpis .nh-reveal:nth-child(4) { transition-delay: 180ms; }

@media (prefers-reduced-motion: reduce) {
    .nh-path.is-active { animation: none; }
}

/* ============================================================ Catalog carousel. */
.nh-catalog__viewport { position: relative; }
.nh-modules {
    display: flex;
    flex-wrap: nowrap;
    gap: 24px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    padding: 4px 2px 10px;
    scrollbar-width: none;
    background: transparent;
    border: 0;
}
.nh-modules::-webkit-scrollbar { display: none; }
/* Four across on desktop; cards keep their width even if only one matches. */
.nh-module { flex: 0 0 calc((100% - 72px) / 4); scroll-snap-align: start; }
@media (max-width: 1024px) { .nh-module { flex-basis: calc((100% - 24px) / 2); } }
@media (max-width: 600px)  { .nh-module { flex-basis: 86%; } }

.nh-module__cover--img { padding: 0; }
.nh-module__cover img { width: 100%; height: 100%; object-fit: cover; }
.nh-module[data-category="leadership"] .nh-module__cover { background: linear-gradient(135deg, #b45309 0%, #f59e0b 100%); }

.nh-cat__arrow {
    position: absolute;
    top: 78px;
    transform: translateY(-50%);
    width: 48px;
    height: 48px;
    border-radius: 50%;
    border: 1px solid var(--nh-line);
    background: #fff;
    color: var(--nh-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 16px;
    cursor: pointer;
    box-shadow: var(--nh-shadow-lift);
    z-index: 3;
    transition: background .15s ease, color .15s ease, transform .15s ease;
}
.nh-cat__arrow:hover { background: var(--nh-primary); color: #fff; transform: translateY(-50%) scale(1.06); }
.nh-cat__arrow[hidden] { display: none; }
.nh-cat__arrow--prev { left: -16px; }
.nh-cat__arrow--next { right: -16px; }
@media (max-width: 600px) {
    .nh-cat__arrow--prev { left: -6px; }
    .nh-cat__arrow--next { right: -6px; }
}

/* ============================================================ Catalog level filter row. */
.nh-catalog__filters--level {
    align-items: center;
    margin-top: -10px;
    margin-bottom: 28px;
}
.nh-catalog__filterlabel {
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .12em;
    color: var(--nh-ink-muted);
    margin-right: 4px;
}
.nh-pill--sm { padding: 6px 13px; font-size: 12px; }
.nh-pill--level.is-active { background: var(--nh-primary); border-color: transparent; color: #fff; }
/* ============================================================================
   ALX-style landing (nh-alx)
   ========================================================================== */

/* ---- Buttons ---- */
.nh-btn--lime { background: var(--nh-lime); color: var(--nh-navy); border: 0; font-weight: 600; }
.nh-btn--lime:hover { background: var(--nh-accent-deep); color: var(--nh-navy); }

/* "View course" on a finished card is .nh-btn--done — a faint green, so it
   cannot be mistaken for the gold of "Continue learning". Defined once, with
   the reasoning, under "The 'View' button on a completed course" below. */
.nh-btn--outline-light { background: transparent; color: #fff; border: 1.5px solid rgba(255, 255, 255, .5); }
.nh-btn--outline-light:hover { background: #fff; color: var(--nh-navy); border-color: #fff; }

/* ---- Hero ---- */
.nh-xhero { background: var(--nh-navy); color: #fff; overflow: hidden; }
.nh-xhero__inner {
    display: grid;
    grid-template-columns: 1fr 1.1fr;
    gap: 48px;
    align-items: center;
    padding-top: clamp(48px, 7vw, 104px);
    padding-bottom: clamp(48px, 7vw, 104px);
}
.nh-xhero__title {
    font-family: var(--nh-font-head);
    font-weight: 700;
    font-size: clamp(40px, 6.6vw, 78px);
    line-height: 1.02;
    letter-spacing: -.025em;
    margin: 0;
}
.nh-xhero__title span { display: block; }
.nh-xhero__accent { color: var(--nh-lime); }
.nh-xhero__lead {
    margin: 22px 0 32px;
    max-width: 46ch;
    font-size: clamp(15px, 1.5vw, 18px);
    line-height: 1.6;
    color: rgba(255, 255, 255, .82);
}
.nh-xhero__actions { display: flex; gap: 14px; flex-wrap: wrap; }
.nh-xhero__text { animation: nh-rise .7s cubic-bezier(.2, .7, .2, 1) both; }

/* Mosaic collage (decorative, works with or without images). */
.nh-mosaic {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-auto-rows: 56px;
    grid-auto-flow: dense;
    gap: 10px;
    perspective: 1200px;
}
.nh-mosaic__tile {
    position: relative;
    overflow: hidden;
    border-radius: 12px;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    opacity: 1;
    transform: none;
    animation: nh-pop .55s cubic-bezier(.2, .7, .2, 1) backwards;
    transition: transform .45s cubic-bezier(.2, .7, .2, 1), box-shadow .3s ease;
}
/* Interactive (image) tiles: gallery affordance. */
.nh-mosaic__tile.is-interactive { cursor: zoom-in; }
.nh-mosaic__tile.is-interactive:hover,
.nh-mosaic__tile.is-interactive:focus-visible {
    transform: scale(1.04);
    z-index: 3;
    box-shadow: 0 24px 50px -18px rgba(0, 0, 0, .55);
    outline: none;
}
.nh-mosaic__tile.is-interactive:focus-visible { box-shadow: 0 0 0 3px var(--nh-accent), 0 24px 50px -18px rgba(0, 0, 0, .55); }
.nh-mosaic__tile.is-swapping { transform: scale(.985); }
/* Cross-fade layer used during the clockwise rotation. */
.nh-mosaic__fade {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    opacity: 0;
    transition: opacity .8s ease;
    pointer-events: none;
}
.nh-mosaic__fade.is-shown { opacity: 1; }
.nh-mosaic__tile--a { grid-column: span 2; grid-row: span 3; background: linear-gradient(150deg, #2347ff, var(--nh-primary-deep)); animation-delay: .04s; }
.nh-mosaic__tile--b { grid-row: span 2; background: linear-gradient(150deg, var(--nh-lime), #6f8f1f); animation-delay: .10s; }
.nh-mosaic__tile--c { grid-row: span 2; background: linear-gradient(150deg, var(--nh-lavender), #8f7bd6); animation-delay: .16s; }
.nh-mosaic__tile--d { grid-column: span 2; grid-row: span 3; background: linear-gradient(150deg, #3a5bff, #16204d); animation-delay: .22s; }
.nh-mosaic__tile--e { grid-row: span 2; background: linear-gradient(150deg, #2347ff, #101a40); animation-delay: .28s; }
.nh-mosaic__tile--f { grid-row: span 2; background: linear-gradient(150deg, var(--nh-lime), #5d7a1a); animation-delay: .34s; }
.nh-mosaic__tile--g { grid-row: span 2; background: linear-gradient(150deg, var(--nh-lavender), #7b66c8); animation-delay: .40s; }
.nh-mosaic__tile--h { grid-row: span 2; background: linear-gradient(150deg, #28d07a, #0c5b38); animation-delay: .46s; }
.nh-mosaic__tile--i { grid-column: span 1; grid-row: span 2; background: linear-gradient(150deg, #3a5bff, var(--nh-primary-deep)); animation-delay: .52s; }

/* ---- Section headers ---- */
.nh-xhead { text-align: center; max-width: 760px; margin: 0 auto clamp(36px, 5vw, 60px); }
.nh-xhead__title {
    font-family: var(--nh-font-head);
    font-weight: 700;
    font-size: clamp(30px, 4.6vw, 54px);
    letter-spacing: -.02em;
    margin: 0 0 14px;
}
.nh-xhead__title--blue { color: var(--nh-primary); }
.nh-xhead__lead { margin: 0; font-size: clamp(15px, 1.6vw, 19px); color: rgba(255, 255, 255, .92); }

/* ---- Explore career pathways (bright blue) ---- */
.nh-xblue { background: var(--nh-blue-bright); color: #fff; padding: clamp(56px, 8vw, 108px) 0; }
.nh-tracks { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.nh-track {
    position: relative;
    display: block;
    border-radius: var(--nh-radius-xl);
    padding: 32px 30px 92px;
    min-height: 320px;
    text-decoration: none;
    color: var(--nh-navy);
    overflow: hidden;
    transition: transform .25s ease, box-shadow .25s ease;
}
.nh-track:hover { transform: translateY(-6px); box-shadow: var(--nh-shadow-lift); }
.nh-track--lime { background: var(--nh-lime); }
.nh-track--lavender { background: var(--nh-lavender); }
.nh-track--paper { background: #fff; }
.nh-track__badge {
    position: absolute;
    top: 26px;
    right: 26px;
    width: 64px;
    height: 64px;
    border-radius: 50%;
    background: rgba(10, 18, 48, .1);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;
    color: var(--nh-navy);
}
.nh-track__title {
    font-family: var(--nh-font-head);
    font-size: clamp(22px, 2.4vw, 28px);
    font-weight: 600;
    margin: 22px 0 12px;
    max-width: 11ch;
}
.nh-track__body { margin: 0; color: rgba(11, 20, 55, .78); line-height: 1.6; font-size: 15px; max-width: 34ch; }
.nh-track__go {
    position: absolute;
    left: 30px;
    bottom: 28px;
    width: 46px;
    height: 46px;
    border-radius: 50%;
    background: #fff;
    color: var(--nh-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 6px 16px rgba(10, 18, 48, .14);
    transition: transform .2s ease;
}
.nh-track:hover .nh-track__go { transform: translate(4px, -4px); }

/* ---- Transformation in action (light blue) ---- */
.nh-xlight { background: #e9f0ff; padding: clamp(40px, 5.5vw, 72px) 0; }
.nh-xlight--btm { padding-top: 0; }
.nh-trans { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin-bottom: 24px; }
.nh-trans__story {
    position: relative;
    min-height: 340px;
    border-radius: var(--nh-radius-xl);
    overflow: hidden;
    display: flex;
    align-items: flex-end;
    padding: 30px;
    text-decoration: none;
    color: #fff;
    background: linear-gradient(180deg, #1b2550, var(--nh-primary-deep));
    background-size: cover;
    background-position: center;
}
.nh-trans__play {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 66px;
    height: 66px;
    border-radius: 50%;
    background: #ff4133;
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 22px;
    transition: transform .2s ease;
}
.nh-trans__story:hover .nh-trans__play { transform: translate(-50%, -50%) scale(1.08); }
.nh-trans__cap {
    position: relative;
    font-family: var(--nh-font-head);
    font-size: clamp(20px, 2.2vw, 26px);
    font-weight: 600;
    max-width: 16ch;
    text-shadow: 0 2px 12px rgba(0, 0, 0, .4);
}
.nh-trans__quote {
    background: var(--nh-lavender);
    border-radius: var(--nh-radius-xl);
    padding: clamp(28px, 3.5vw, 42px);
    display: flex;
    flex-direction: column;
}
.nh-trans__quote blockquote {
    font-family: var(--nh-font-head);
    font-size: clamp(18px, 2.1vw, 24px);
    line-height: 1.42;
    margin: 14px 0 26px;
    color: var(--nh-navy);
    flex: 1;
}
.nh-trans__quote .nh-quote__person { display: flex; align-items: center; gap: 12px; }
.nh-trans__quote .nh-quote__avatar {
    width: 46px; height: 46px; border-radius: 50%; overflow: hidden;
    background: var(--nh-primary); color: #fff;
    display: flex; align-items: center; justify-content: center; flex: 0 0 auto;
}
.nh-trans__quote .nh-quote__avatar img { width: 100%; height: 100%; object-fit: cover; }
.nh-trans__quote .nh-quote__meta { display: flex; flex-direction: column; }
.nh-trans__quote .nh-quote__name { font-weight: 700; color: var(--nh-navy); }
.nh-trans__quote .nh-quote__role { font-size: 13px; color: rgba(11, 20, 55, .7); }
.nh-trans__quote .nh-quote__stars { color: var(--nh-accent); margin-bottom: 4px; display: flex; gap: 3px; }
.nh-trans__quote .nh-quote__star--empty { color: rgba(10, 18, 48, .2); }

/* ---- Stat cards ---- */
.nh-xstats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; }
.nh-xstat {
    position: relative;
    border-radius: var(--nh-radius-xl);
    padding: 22px;
    min-height: 172px;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    overflow: hidden;
}
.nh-xstat__icon {
    position: absolute;
    top: 22px;
    left: 22px;
    width: 44px;
    height: 44px;
    border-radius: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 17px;
    background: rgba(255, 255, 255, .22);
}
.nh-xstat__value { font-family: var(--nh-font-head); font-weight: 700; font-size: clamp(30px, 3.6vw, 46px); line-height: 1; }
.nh-xstat__desc { font-size: 12px; font-weight: 600; opacity: .82; line-height: 1.35; margin-bottom: 10px; }
.nh-xstat--img .nh-xstat__desc { position: relative; z-index: 2; }
.nh-xstat__label { margin-top: 8px; font-size: 13px; font-weight: 600; opacity: .92; }
.nh-stat--green { background: #16a34a; color: #fff; }
.nh-stat--navy  { background: var(--nh-navy); color: #fff; }
.nh-stat--lime  { background: var(--nh-lime); color: var(--nh-navy); }
.nh-stat--lime .nh-xstat__icon { background: rgba(10, 18, 48, .12); color: var(--nh-navy); }
.nh-stat--deep  { background: linear-gradient(160deg, #16224e, var(--nh-primary-deep)); color: #fff; }

/* Stat card with a customizable background photo. */
.nh-xstat--img { color: #fff; background: var(--nh-primary-deep); }
.nh-xstat__bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; }
.nh-xstat--img::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
    background: linear-gradient(180deg, rgba(8, 17, 49, .2) 0%, rgba(8, 17, 49, .82) 100%);
}
.nh-xstat--img .nh-xstat__icon { display: none; }
.nh-xstat--img .nh-xstat__value,
.nh-xstat--img .nh-xstat__label { position: relative; z-index: 2; }

/* ---- Subscribe CTA ---- */
.nh-cta {
    position: relative;
    overflow: hidden;
    background:
        radial-gradient(120% 150% at 8% 6%, rgba(0, 170, 235, .5) 0%, rgba(0, 170, 235, 0) 46%),
        radial-gradient(130% 150% at 96% 104%, rgba(99, 91, 255, .45) 0%, rgba(99, 91, 255, 0) 52%),
        linear-gradient(135deg, #06183a 0%, #0a3f80 50%, var(--nh-primary) 100%);
    color: #fff;
    border-radius: var(--nh-radius-xl);
    padding: clamp(26px, 3.4vw, 40px);
    display: flex;
    align-items: center;
    gap: 28px;
    flex-wrap: wrap;
}
.nh-cta > * { position: relative; z-index: 1; }
.nh-cta__mark {
    width: 86px;
    height: 86px;
    border-radius: 22px;
    background: rgba(255, 255, 255, .16);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 34px;
    flex: 0 0 auto;
    overflow: hidden;
}
.nh-cta__mark img { width: 100%; height: 100%; object-fit: cover; }
.nh-cta__text { flex: 1 1 320px; }
.nh-cta__title { font-family: var(--nh-font-head); font-size: clamp(24px, 3vw, 34px); font-weight: 700; margin: 0 0 8px; }
.nh-cta__body { margin: 0; color: rgba(255, 255, 255, .9); max-width: 54ch; }

/* ---- Animations ---- */
@keyframes nh-pop { from { opacity: 0; transform: scale(.85); } to { opacity: 1; transform: none; } }
@keyframes nh-rise { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }

/* ---- Responsive ---- */
@media (max-width: 1024px) {
    .nh-tracks { grid-template-columns: repeat(2, 1fr); }
    .nh-xstats { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 900px) {
    .nh-xhero__inner { grid-template-columns: 1fr; gap: 32px; }
    .nh-mosaic { grid-auto-rows: 46px; max-width: 520px; }
    .nh-trans { grid-template-columns: 1fr; }
}
@media (max-width: 680px) {
    .nh-tracks { grid-template-columns: 1fr; }
    .nh-cta { flex-direction: column; align-items: center; text-align: center; gap: 18px; }
    .nh-cta__body { margin-left: auto; margin-right: auto; }
    .nh-cta .nh-btn { width: 100%; justify-content: center; }
}
@media (max-width: 520px) {
    /* Two-up stat grid on phones (matches the mobile reference). */
    .nh-xstats { grid-template-columns: repeat(2, 1fr); gap: 14px; }
    .nh-xstat { min-height: 140px; padding: 16px; border-radius: var(--nh-radius-lg); }
    .nh-xstat__icon { width: 36px; height: 36px; top: 14px; left: 14px; font-size: 14px; }
    .nh-xstat__value { font-size: clamp(26px, 9vw, 34px); }
    .nh-xstat__label { font-size: 12px; }
}

/* ============================================================ Bold-sans display headings (punch). */
.nh-xhero__title,
.nh-section__title,
.nh-xhead__title,
.nh-cta__title,
.nh-trans__cap { font-weight: 800; letter-spacing: -.02em; }
.nh-section__title { line-height: 1.12; }

/* ============================================================ Transformation: cycling testimonial. */
.nh-trans__quotes { position: relative; min-width: 0; display: flex; flex-direction: column; }
.nh-trans__track {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    list-style: none;
    margin: 0;
    padding: 0;
    scrollbar-width: none;
    flex: 1;
}
.nh-trans__track::-webkit-scrollbar { display: none; }
.nh-trans__slide {
    flex: 0 0 100%;
    width: 100%;
    min-width: 0;
    scroll-snap-align: start;
    display: flex;
}
.nh-trans__slide .nh-trans__quote { width: 100%; }
.nh-trans__controls { display: flex; gap: 10px; justify-content: flex-end; margin-top: 16px; }
.nh-trans__nav {
    position: static;
    top: auto;
    left: auto;
    right: auto;
    transform: none;
    box-shadow: var(--nh-shadow);
}
.nh-trans__nav:hover { transform: scale(1.07); background: var(--nh-primary); color: #fff; }
@media (max-width: 760px) {
    .nh-trans__controls { justify-content: center; }
}

/* Catalogue section spacing within the ALX layout. */
.nh-catalog-sec { padding-top: clamp(48px, 6vw, 84px); padding-bottom: clamp(48px, 6vw, 84px); }

/* ============================================================ Catalogue covers: unify to UNDP blue. */
.nh-module[data-category="ai"] .nh-module__cover,
.nh-module[data-category="cyber"] .nh-module__cover,
.nh-module[data-category="business"] .nh-module__cover,
.nh-module[data-category="programming"] .nh-module__cover,
.nh-module[data-category="leadership"] .nh-module__cover,
.nh-module__cover { background: linear-gradient(135deg, #0c2d57 0%, var(--nh-primary) 100%); }
.nh-module__cover--img { background: #0c2d57; }

/* ============================================================ Explore Career Pathways on UNDP-blue. */
.nh-catalog-sec {
    position: relative;
    overflow: hidden;
    background:
        radial-gradient(120% 120% at 12% 8%, rgba(0, 170, 235, .45) 0%, rgba(0, 170, 235, 0) 42%),
        radial-gradient(110% 110% at 92% 96%, rgba(99, 91, 255, .40) 0%, rgba(99, 91, 255, 0) 46%),
        linear-gradient(135deg, #06183a 0%, #0a3f80 48%, var(--nh-primary) 100%);
}
/* Soft modern glow accents. */
.nh-catalog-sec::after {
    content: '';
    position: absolute;
    width: 520px;
    height: 520px;
    right: -140px;
    top: -120px;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(245, 166, 35, .22) 0%, rgba(245, 166, 35, 0) 70%);
    pointer-events: none;
    z-index: 0;
}
.nh-catalog-sec > .nh-container { position: relative; z-index: 1; }
/* Eyebrow as a modern translucent chip. */
.nh-catalog-sec .nh-eyebrow {
    display: inline-block;
    color: #fff;
    background: rgba(255, 255, 255, .12);
    border: 1px solid rgba(255, 255, 255, .22);
    padding: 6px 14px;
    border-radius: 999px;
    backdrop-filter: blur(4px);
}
.nh-catalog-sec .nh-section__title { color: #fff; }
.nh-catalog-sec .nh-section__lede { color: rgba(255, 255, 255, .82); }
.nh-catalog-sec .nh-catalog__empty { color: rgba(255, 255, 255, .88); }
/* Glassy filter pills. */
.nh-catalog-sec .nh-pill {
    background: rgba(255, 255, 255, .1);
    border-color: rgba(255, 255, 255, .24);
    color: rgba(255, 255, 255, .92);
    backdrop-filter: blur(4px);
}
.nh-catalog-sec .nh-pill:hover {
    background: rgba(255, 255, 255, .2);
    border-color: rgba(255, 255, 255, .4);
    color: #fff;
}
.nh-catalog-sec .nh-pill.is-active {
    background: var(--nh-accent);
    border-color: transparent;
    color: var(--nh-navy);
    box-shadow: 0 6px 18px -6px rgba(245, 166, 35, .6);
}
/* Search field reads as a soft glass control. */
.nh-catalog-sec .nh-catalog__search input {
    box-shadow: 0 10px 30px -16px rgba(0, 0, 0, .5);
    border-color: transparent;
}
/* Keep the blue tone (not the light wash) if an admin adds a background image here. */
.nh-catalog-sec.nh-section--bg::before { background: linear-gradient(rgba(6, 24, 58, .82), rgba(0, 114, 188, .82)); }

/* ============================================================ Hero background + customizable mosaic tiles. */
.nh-xhero--bg {
    background-image: linear-gradient(rgba(10, 18, 48, .86), rgba(10, 18, 48, .92)), var(--nh-section-bg);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}
/* Any tile holding a photo covers its area (overrides the gradient placeholder). */
.nh-mosaic__tile { background-size: cover; background-position: center; background-repeat: no-repeat; }
.nh-mosaic__tile--img { background-color: #0c2d57; }
/* ============================================================ Active nav link highlight. */
.nh-nav__links a.is-active { color: var(--nh-primary); font-weight: 600; }
.nh-nav__links a.is-active::after { right: 0; }
.nh-mobilemenu__links a.is-active { color: var(--nh-primary); font-weight: 600; }

/* ============================================================ Page header (blog/faq/contact) photo. */
.nh-pagehead--img {
    background-image:
        linear-gradient(135deg, rgba(6, 24, 47, .86), rgba(10, 79, 138, .76)),
        var(--nh-section-bg);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}

/* ============================================================ Flash messages (contact). */
.nh-flash {
    padding: 14px 16px;
    border-radius: var(--nh-radius);
    font-weight: 600;
    font-size: 14px;
}
.nh-flash--success { background: #e7f6ec; color: #14672f; border: 1px solid #b6e2c4; }
.nh-flash--error   { background: #fdecea; color: #a02417; border: 1px solid #f3c4bd; }

/* Social icons on a light background (contact page). */
.nh-social--ink { margin-top: 26px; }
.nh-social--ink a {
    background: var(--nh-paper);
    color: var(--nh-primary);
    border: 1px solid var(--nh-line);
}
.nh-social--ink a:hover { background: var(--nh-primary); color: #fff; border-color: transparent; }

/* ============================================================ Join CTA: tighter mobile spacing. */
@media (max-width: 680px) {
    .nh-cta { gap: 14px; }
    .nh-cta__text { flex: 0 0 auto; }
    .nh-cta__mark { width: 64px; height: 64px; border-radius: 18px; font-size: 28px; }
    .nh-cta__btn { margin-top: 2px; }
}
/* ============================================================ Lightbox gallery (hero photos). */
/* Body lock while the lightbox is open (keeps the page from scrolling on mobile). */
body.nh-scroll-locked {
    position: fixed;
    left: 0;
    right: 0;
    width: 100%;
    overflow: hidden;
}
/* Native <dialog> in the browser top layer. Reset the UA dialog defaults
   (border/padding/background/max-size/margin) and stretch it to fill the
   viewport. Because it lives in the top layer it is immune to any ancestor
   transform/filter, so it can no longer be pushed to the bottom of the page. */
.nh-lightbox {
    /* No token re-declaration needed: the dialog is appended outside .nh-page
       but still inherits the site-wide :root tokens through <body>. */
    position: fixed;
    inset: 0;
    width: 100vw;
    width: 100dvw;
    height: 100vh;
    height: 100dvh;
    max-width: none;
    max-height: none;
    margin: 0;
    border: 0;
    z-index: 2147483647;
    align-items: center;
    justify-content: center;
    padding: clamp(12px, 4vw, 48px);
    box-sizing: border-box;
    background: rgba(5, 11, 28, .82);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    overscroll-behavior: contain;
    opacity: 0;
    transition: opacity .3s ease;
}
.nh-lightbox[open] { display: flex; }
.nh-lightbox:not([open]) { display: none; }
.nh-lightbox.is-open { opacity: 1; }
/* We dim the viewport with the dialog's own background, so keep the UA
   backdrop transparent (avoids a double-darkened overlay). */
.nh-lightbox::backdrop { background: transparent; }
/* Fixed 3:2 frame, identical for every photo no matter what size/ratio it was
   uploaded at. Width is capped by the viewport width AND by the height budget
   (84vh x 3/2) so the frame always fits on screen while keeping its ratio. */
.nh-lightbox__stage {
    margin: 0;
    width: min(92vw, 84vh * 3 / 2, 1100px);
    width: min(92dvw, 84dvh * 3 / 2, 1100px);
    aspect-ratio: 3 / 2;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border-radius: 16px;
    /* Letterbox bars behind photos that don't match the 3:2 frame. */
    background: rgba(8, 14, 30, .55);
    box-shadow: 0 40px 90px -30px rgba(0, 0, 0, .8);
}
.nh-lightbox__img {
    display: block;
    width: 100%;
    height: 100%;
    /* Whole photo always fits inside the frame; no cropping, no distortion. */
    object-fit: contain;
    opacity: 0;
    transform: scale(.97);
    transition: opacity .35s ease, transform .35s cubic-bezier(.2, .7, .2, 1);
}
.nh-lightbox__img.is-in { opacity: 1; transform: scale(1); }
.nh-lightbox__close,
.nh-lightbox__nav {
    position: absolute;
    width: 52px;
    height: 52px;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, .25);
    background: rgba(255, 255, 255, .14);
    color: #fff;
    font-size: 20px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background .15s ease, transform .15s ease, border-color .15s ease;
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
    z-index: 2;
}
/* Arrows overlay the image edges so the photo always gets the full space. */
.nh-lightbox__nav { top: 50%; transform: translateY(-50%); }
.nh-lightbox__nav--prev { left: clamp(8px, 2.5vw, 28px); }
.nh-lightbox__nav--next { right: clamp(8px, 2.5vw, 28px); }
.nh-lightbox__nav:hover { background: var(--nh-primary); border-color: transparent; transform: translateY(-50%) scale(1.08); }
.nh-lightbox__close {
    top: clamp(12px, 3vw, 28px);
    right: clamp(12px, 3vw, 28px);
    width: 46px;
    height: 46px;
}
.nh-lightbox__close:hover { background: var(--nh-primary); border-color: transparent; transform: scale(1.08); }
.nh-lightbox__counter {
    position: absolute;
    bottom: clamp(12px, 3vw, 28px);
    left: 50%;
    transform: translateX(-50%);
    color: rgba(255, 255, 255, .85);
    font-size: 14px;
    font-weight: 600;
    letter-spacing: .08em;
    z-index: 2;
}
@media (max-width: 640px) {
    .nh-lightbox__nav { width: 42px; height: 42px; font-size: 16px; }
    .nh-lightbox__nav--prev { left: 6px; }
    .nh-lightbox__nav--next { right: 6px; }
    .nh-lightbox__close { width: 40px; height: 40px; font-size: 17px; }
    /* Same fixed 3:2 frame, sized against the phone viewport. */
    .nh-lightbox__stage {
        width: min(94vw, 78vh * 3 / 2);
        width: min(94dvw, 78dvh * 3 / 2);
    }
}

/* Honour reduced-motion: no transforms/transitions on swap or lightbox. */
@media (prefers-reduced-motion: reduce) {
    .nh-mosaic__fade { transition: none; }
    .nh-lightbox__img { transition: opacity .2s ease; transform: none; }
    .nh-lightbox__img.is-in { transform: none; }
    .nh-mosaic__tile.is-interactive:hover { transform: none; }
}
/* ============================================================
 * Generated course covers.
 *
 * A course with no usable image of its own gets a branded gradient cover as an
 * SVG data URI (see \local_nzeruhub\local\catalogue::placeholder_cover) with the
 * pathway icon laid on top. Because it is delivered as an <img>, the cover needs
 * `--img` sizing while still positioning the icon over it — hence the overlay
 * rules here rather than reusing the flex-centred icon-only cover.
 * ============================================================ */
.nh-module__cover--gen {
    position: relative;
    padding: 0;
    height: 150px;
}

.nh-module__cover--gen > i {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    /* Slightly smaller than the icon-only cover: it sits on artwork, so it is
       an accent rather than the whole composition. */
    font-size: 30px;
    color: rgba(255, 255, 255, .92);
    text-shadow: 0 2px 10px rgba(11, 35, 71, .45);
    pointer-events: none;
}

/* ============================================================ Authentication
 * dialog.
 *
 * Log in, create an account and reset a password, on the page the visitor is
 * already reading. The panes for creating an account and resetting a password
 * hold Moodle's own rendered forms, so the rules below deliberately reach into
 * .mform: that markup is core's, and this is the one place it appears inside
 * the landing design.
 */
.nh-authmodal {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
}

.nh-authmodal[hidden] { display: none; }

.nh-authmodal__scrim {
    position: absolute;
    inset: 0;
    background: rgba(8, 17, 49, .55);
    backdrop-filter: blur(2px);
}

.nh-authmodal__dialog {
    position: relative;
    width: 100%;
    max-width: 420px;
    max-height: calc(100vh - 40px);
    overflow-y: auto;
    padding: 32px 28px 28px;
    border-radius: 18px;
    background: #fff;
    box-shadow: 0 30px 70px -20px rgba(8, 17, 49, .5);
}

.nh-authmodal__dialog:focus { outline: none; }

.nh-authmodal__close {
    position: absolute;
    top: 12px;
    right: 12px;
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--nh-ink-muted);
    cursor: pointer;
    transition: background .15s ease, color .15s ease;
}

.nh-authmodal__close:hover,
.nh-authmodal__close:focus {
    background: var(--nh-primary-tint);
    color: var(--nh-primary);
}

.nh-authmodal__title {
    margin: 0;
    font-family: var(--nh-font-head);
    font-size: 24px;
    font-weight: 700;
    letter-spacing: -.02em;
    color: var(--nh-navy);
}

.nh-authmodal__lede {
    margin: 8px 0 20px;
    font-size: 14px;
    line-height: 1.55;
    color: var(--nh-ink-muted);
}

.nh-authmodal__alert {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin-bottom: 18px;
    padding: 13px 15px;
    border-radius: 8px;
    background: #fbdde5;
    color: #6b2130;
    font-size: 14px;
    line-height: 1.5;
}

/* The icon is drawn rather than marked up, so the message stays one plain
   string that a screen reader reads without a stray glyph name in it. */
.nh-authmodal__alert::before {
    content: '\f06a';
    font-family: 'Font Awesome 6 Free', 'FontAwesome';
    font-weight: 900;
    flex: none;
    margin-top: 1px;
    font-size: 15px;
    color: #c0304a;
}

/* ---- Fields. ---- */
.nh-authform__label {
    display: block;
    margin-bottom: 6px;
    font-size: 13px;
    font-weight: 600;
    color: var(--nh-navy);
}

.nh-authform__input {
    width: 100%;
    height: 44px;
    margin-bottom: 16px;
    padding: 0 14px;
    border: 1px solid var(--nh-line);
    border-radius: 8px;
    background: #fff;
    font-size: 14.5px;
    color: var(--nh-ink);
}

.nh-authform__input:focus {
    outline: none;
    border-color: var(--nh-primary);
    box-shadow: 0 0 0 3px rgba(0, 91, 187, .12);
}

/* The password field and its show/hide control share one row. */
.nh-authform__reveal { position: relative; }
.nh-authform__reveal .nh-authform__input { padding-right: 46px; }

.nh-authform__eye {
    position: absolute;
    top: 23px;
    right: 6px;
    transform: translateY(-50%);
    width: 34px;
    height: 34px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--nh-ink-muted);
    cursor: pointer;
}

.nh-authform__eye:hover,
.nh-authform__eye:focus { color: var(--nh-primary); }

.nh-authform__check {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 18px;
    font-size: 14px;
    color: var(--nh-ink-muted);
    cursor: pointer;
}

/* The login submit is the same control as the one core's forms get: the width
   of the field above it, and no taller. */
.nh-authform__submit {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 44px;
    margin-bottom: 4px;
    padding: 0 16px;
    border: 0;
    border-radius: 8px;
    background: var(--nh-primary);
    color: #fff;
    font-family: var(--nh-font-body);
    font-size: 14.5px;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    transition: background .15s ease;
}

.nh-authform__submit:hover { background: var(--nh-primary-deep); }

.nh-authform__submit:focus-visible {
    outline: 2px solid var(--nh-primary);
    outline-offset: 2px;
}

/* The secondary route out of the login pane matches it in size. */
.nh-authmodal__links .nh-btn--ghost {
    width: 100%;
    height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    font-size: 14.5px;
}

/* ---- Links under the form. ---- */
.nh-authmodal__links {
    margin-top: 18px;
    text-align: center;
    font-size: 14px;
}

.nh-authmodal__links a { color: var(--nh-primary); font-weight: 600; text-decoration: none; }
.nh-authmodal__links a:hover { text-decoration: underline; }

/* A rule with a word sitting in it, as on the shared reference. */
.nh-authmodal__rule {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 18px 0;
    color: var(--nh-ink-muted);
    font-size: 13px;
}

.nh-authmodal__rule::before,
.nh-authmodal__rule::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--nh-line);
}

/* ---- Moodle's own forms, in the dialog. ----
 *
 * Core renders these as a Bootstrap row: a label column beside a field column.
 * That is right for a settings page and wrong for a 420px dialog, so the row is
 * unwound into a stack - label, then field, then whatever core has to say about
 * it. These selectors reach into .fitem/.felement deliberately: that markup is
 * core's, and this is the one place it appears inside the landing design.
 */
.nh-authpane__slot .mform { margin: 0; }
.nh-authpane__slot fieldset { border: 0; padding: 0; margin: 0; }
.nh-authpane__slot legend { display: none; }

/* Unwind the row.
 *
 * !important is not decoration here. Core builds each row out of Bootstrap
 * utility classes - `d-flex`, `col-md-3`, `col-md-9` - and Bootstrap ships its
 * utilities as `display: flex !important`. No amount of specificity beats that,
 * which is why the first attempt at this silently did nothing: the row stayed a
 * flex container, `flex: none` made every column shrink to its content, and both
 * the label and the input collapsed to a stub. Overriding an !important rule
 * needs !important, and it is safe here because every selector is scoped inside
 * the dialog.
 */
.nh-authpane__slot .fitem,
.nh-authpane__slot .fitem > [class*='col-'],
.nh-authpane__slot .felement,
.nh-authpane__slot .felement > div {
    display: block !important;
    width: 100% !important;
    max-width: none !important;
    flex: 0 0 100% !important;
    margin-left: 0;
    margin-right: 0;
    padding-left: 0;
    padding-right: 0;
}

.nh-authpane__slot .fitem {
    margin: 0 0 14px;
}

/* A label column with nothing in it - the password policy note, the button row -
   would otherwise be an empty box adding height above the thing it labels.
   Placed after the rule above so it outranks it. */
.nh-authpane__slot .femptylabel > .col-form-label { display: none !important; }

/* Label, on its own. */
.nh-authpane__slot .col-form-label {
    display: flex !important;
    align-items: center;
    gap: 6px;
    margin-bottom: 6px;
    padding: 0;
    text-align: left;
}

.nh-authpane__slot .col-form-label label,
.nh-authpane__slot label.col-form-label {
    margin: 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--nh-navy);
}

.nh-authpane__slot .form-label-addon { margin: 0; }
.nh-authpane__slot .form-label-addon .icon {
    width: auto;
    height: auto;
    margin: 0;
    font-size: 12px;
}

/* Core's per-field "required" marker, off.
 *
 * It reads as an error, and that is not a matter of taste - `core:req` maps to
 * `fa-circle-exclamation text-danger`, so it is literally a red exclamation in a
 * circle, sitting on its own line directly above the input, in the same position
 * and the same colour the field's real validation message uses. On a form where a
 * genuine "Missing given name" appears right below the box, the same glyph above
 * the box is not a hint that the field is needed; it is a second, permanent,
 * meaningless error.
 *
 * Why it lands on its own line rather than beside the label, which is what the
 * rule above intends: the label column is `display: block !important` from
 * `.nh-authpane__slot .fitem > [class*='col-']`, and that selector counts three
 * classes against this one's two, so it wins the cascade. The addon is a
 * block-level flex box, so it drops below the label. Left alone, that is what
 * put a red icon above every required field in the dialog.
 *
 * Nothing is lost by removing it. Core marks the div `aria-hidden="true"`, so no
 * screen reader ever announced it - the requirement is carried by the input's own
 * validation and by the message shown when it fails. On this form the marker said
 * little anyway: every field in it is required.
 *
 * Scoped to the addon, not the whole label column, so the help buttons that share
 * it - the password field has one - are untouched. And applied to every pane of
 * the dialog rather than signup alone, because the same stray icon appeared above
 * the required fields on log in, reset password and change password too.
 */
.nh-authpane__slot .form-label-addon > .text-danger { display: none; }

/* Fields. */
.nh-authpane__slot input[type="text"],
.nh-authpane__slot input[type="email"],
.nh-authpane__slot input[type="password"],
.nh-authpane__slot select,
.nh-authpane__slot textarea {
    width: 100%;
    height: 44px;
    min-height: 44px;
    padding: 10px 14px;
    border: 1px solid var(--nh-line);
    border-radius: 8px;
    background: #fff;
    font-family: var(--nh-font-body);
    font-size: 14.5px;
    color: var(--nh-ink);
    transition: border-color .15s ease, box-shadow .15s ease;
}

.nh-authpane__slot input::placeholder,
.nh-authpane__slot textarea::placeholder { color: #9aa3b5; }

.nh-authpane__slot select {
    appearance: none;
    padding-right: 38px;
    background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%237d879c'%3e%3cpath d='M8 11 3 6h10z'/%3e%3c/svg%3e");
    background-repeat: no-repeat;
    background-position: right 14px center;
    background-size: 14px;
}

.nh-authpane__slot input:focus,
.nh-authpane__slot select:focus,
.nh-authpane__slot textarea:focus {
    outline: none;
    border-color: var(--nh-primary);
    box-shadow: 0 0 0 3px rgba(0, 91, 187, .12);
}

/* Field order.
 *
 * Core asks for a username first, then the password, then the name - the order a
 * database row is built in, not the order a person introduces themselves in.
 * Signing up should start with who you are, so the fields are re-sequenced to
 * name, then account, then where you are.
 *
 * Done with flex `order` rather than by forking login_signup_form: the form
 * object stays core's, every field keeps its own validation, and a custom
 * profile field an administrator adds still appears - it simply lands after the
 * ones named below, which is where an extra question belongs anyway.
 */
.nh-authpane__slot form.mform {
    display: flex;
    flex-wrap: wrap;
    column-gap: 12px;
}

/* EVERY direct child takes a full line by default, not only the .fitem ones.
 *
 * Core puts things in a form that are not fields: the "Required" key, the block
 * of hidden inputs, and on the reset form a whole fieldset per search method.
 * Without a width they are auto-sized flex items, so the first of them shares a
 * line with whatever comes next and squeezes it to a stub - which is exactly
 * what happened to First name.
 */
.nh-authpane__slot form.mform > * {
    flex: 0 0 100%;
    width: 100%;
    min-width: 0;
}

.nh-authpane__slot form.mform > .fitem {
    order: 50;
}

/* The "Required" key - core's "There are required fields in this form" line. It
   is a legend for the per-field markers, and in a dialog this narrow it is a line
   of noise above the first question, so it goes.

   The markers it explained are now gone too (see the note on
   .form-label-addon > .text-danger above), which makes removing this legend more
   obviously right rather than less: a key to symbols that are no longer on the
   page would be worse than the noise it already was. Nothing here is what a
   screen reader relies on - the markers were aria-hidden and this line is
   decorative prose; the requirement lives on the inputs themselves. */
.nh-authpane__slot .fdescription.required { display: none; }

/* THE ORDER OF THE FORM, AND WHY IT IS DONE HERE.

   Core builds the sign-up form as username, password policy, password, email,
   first name, last name, district. The design asks for a different order and,
   more importantly, for pairs:

       First name  | Last name
       Username    | Sex
       Institution | District
       Email address
       Password

   It is done in CSS rather than by moving the elements in PHP. That was tried:
   MoodleQuickForm::removeElement() plus insertElementBefore() corrupts the
   form - fields end up duplicated and others vanish - so the elements stay
   exactly where core put them and only their painted order changes.

   The two custom profile fields share one rule because a stylesheet cannot tell
   Sex from Institution; their shortnames are whatever an administrator chose.
   They do not need telling apart: both sit between Username and District, and
   because each is half width they fall into the two pairs above on their own.
   An `order` is essential for them even so - an unordered flex item defaults to
   0, which would sort them ABOVE First name. */
.nh-authpane__slot #fitem_id_firstname { order: 1; }
.nh-authpane__slot #fitem_id_lastname { order: 2; }
.nh-authpane__slot #fitem_id_username { order: 3; }
/* 4 is Sex, stamped inline by local_nzeruhub_auth_order_profile_fields(). */
.nh-authpane__slot #fitem_id_email { order: 5; }
.nh-authpane__slot #fitem_id_password { order: 6; }
.nh-authpane__slot #fitem_id_passwordpolicyinfo { order: 7; }
/* 8 is Institution, stamped inline for the same reason. */
.nh-authpane__slot #fitem_id_city { order: 9; }
.nh-authpane__slot #fitem_id_country { order: 10; }

/* Any OTHER custom profile field a site adds. After District, so an extra
   question never lands between the ones the design arranges. The two the design
   does arrange carry an inline order, which beats this. */
.nh-authpane__slot [id^="fitem_id_profile_field_"] { order: 11; }

/* THE EMPTIED CATEGORY FIELDSETS.

   Moodle groups custom profile fields by profile category and wraps each
   category in a collapsible <fieldset>. That fieldset is the flex item, not the
   field inside it, and an unordered flex item defaults to order 0 — which is
   what put Sex, Institution and District above First name however carefully the
   fields inside them were ordered.

   `display: contents` on the fieldset was the obvious fix and it does not work:
   a <fieldset> always generates a box and browsers ignore `display: contents`
   on it. So auth-modal.js moves the rows out into the form instead, and marks
   the empty wrapper with data-nh-lifted.

   Hidden by that attribute, never by the element — so with scripting off, when
   nothing has been lifted, the fieldsets still render and every field is still
   on screen. A rule that hid them outright would delete half the sign-up form
   for anyone without JavaScript. */
.nh-authpane__slot fieldset[data-nh-lifted] {
    display: none;
}

/* The category furniture, hidden whether or not the lift ran: the chevron, the
   "Other fields" heading, and the Collapse all / Expand all pair. All three are
   for managing a long settings form; this is a sign-up dialog. */
.nh-authpane__slot fieldset[id^="id_category"] > legend,
.nh-authpane__slot fieldset[id^="id_category"] > .d-flex,
.nh-authpane__slot .collapsible-actions {
    display: none;
}

/* The submit row last, whatever else the site has added above it. */
.nh-authpane__slot #fgroup_id_buttonar { order: 90; }

/* NO TRAILING SPACE UNDER THE SUBMIT BUTTON.

   The "or" separator below it carries its own 18px, the same as the gap to the
   button beneath it -- but core's button row added 33px of its own on top of
   that, so the space above the word was three times the space below it and the
   rule sat much closer to "Already have an account?" than to the button it was
   separating from.

   The 33px was in three parts, and all three are answered here: 6px of inline
   baseline gap under the button inside its wrapper, 11px of flex line box
   around it, and Bootstrap's own .mb-3 on the row. Blocks and a line-height of
   1 remove the first two; the margin removes the third. What is left is the
   separator's margin, which is what should have been deciding it all along. */
.nh-authpane__slot #fgroup_id_buttonar,
.nh-authpane__slot #fgroup_id_buttonar .fitem {
    /* !important because the row carries Bootstrap's own .mb-3, and every
       Bootstrap spacing utility is marked important -- an id selector does not
       outrank that on its own. */
    margin-bottom: 0 !important;
}

/* ...and the last six.

   With the three above removed, core's button row still stands 6px taller than
   the button inside it -- not margin, not padding, and it survives line-height,
   font-size, display and overflow being changed on the row and on its columns.
   Whatever it is, it is inside a box with no background, so the reader only
   sees it as space above the word.

   So the separator gives those 6px back rather than the row losing them: 12
   above plus the 6 that are there anyway is the 18 below. If core ever changes
   that row the worst case is a 6px difference, not a broken layout. */
[data-nh-auth-pane="signup"] .nh-authmodal__links {
    /* Zero, so the rule's own margin is what decides. Adjacent vertical margins
       collapse to the larger of the two: with the container still carrying 18px
       of its own, setting the rule to 12 changed nothing at all, because 18 won
       the collapse. Found by measuring rather than by reading -- the rule was in
       the stylesheet, served, and matching, and the gap did not move. */
    margin-top: 0;
}

[data-nh-auth-pane="signup"] .nh-authmodal__rule {
    margin-top: 12px;
}

.nh-authpane__slot #fgroup_id_buttonar,
.nh-authpane__slot #fgroup_id_buttonar .d-flex,
.nh-authpane__slot #fgroup_id_buttonar [class*='col-'],
.nh-authpane__slot #fgroup_id_buttonar > div > div,
.nh-authpane__slot #fgroup_id_buttonar span {
    /* .d-flex and .row are important for the same reason. The row itself is
       included because as a flex container it stood 6px taller than the one
       button inside it; as a block it hugs it, and the separator's own margin
       is then the whole of the gap. */
    display: block !important;
    line-height: 1;
}

/* TWO COLUMNS, IN PAIRS.

   Four fields are half width, and they pair up in the order the form is in:
   First name | Last name, then Username | Sex. Email and Password stay full
   width, because an address and a password are longer than half a modal and
   cramping them helps nobody.

   DISTRICT IS NOT IN THIS LIST ANY MORE. It used to pair with Institution,
   which has since been retired -- so it sat alone at half width next to a
   full-width Country, which is what it looked like: a field that had been
   squashed for no reason.

   It also could not have been fixed anywhere else. Marking the row as
   conditional gives it `flex: 0 0 100%` through
   `.nh-authpane__slot form [data-nzeru-conditional-row]`, and District carries
   that attribute -- but `#fitem_id_city` names an id, and an id outranks an
   attribute selector whatever !important either side carries. The only way to
   make District full width was to stop asking for it to be half.

   The custom fields are matched by prefix rather than by shortname, which this
   file cannot know - an administrator picks it. Which of them lands where is
   decided in PHP, by local_nzeruhub_signup_order_profile_fields(), where the
   field can actually be identified; here they only have to be half width. */
@media (min-width: 420px) {
    .nh-authpane__slot #fitem_id_firstname,
    .nh-authpane__slot #fitem_id_lastname,
    .nh-authpane__slot #fitem_id_username,
    .nh-authpane__slot [id^="fitem_id_profile_field_"] {
        /* flex-basis as well as width, and !important on both: the rule that
           unwinds core's grid has to shout to be heard over Bootstrap, and this
           is the one place that needs to be louder still. */
        flex: 0 0 calc(50% - 6px) !important;
        width: calc(50% - 6px) !important;
    }
}

/* Rejected fields. Border and message, not colour alone. */
.nh-authpane__slot input.is-invalid,
.nh-authpane__slot select.is-invalid,
.nh-authpane__slot .has-danger input,
.nh-authpane__slot .error input {
    border-color: #d3453a;
    background: #fffafa;
}

.nh-authpane__slot input.is-invalid:focus,
.nh-authpane__slot select.is-invalid:focus {
    box-shadow: 0 0 0 3px rgba(211, 69, 58, .14);
}

.nh-authpane__slot .invalid-feedback,
.nh-authpane__slot .form-control-feedback,
.nh-authpane__slot .error {
    display: block;
    width: 100%;
    margin-top: 6px;
    font-size: 13px;
    line-height: 1.45;
    color: #a02417;
}

/* The password policy core prints under the password field. */
.nh-authpane__slot .form-text,
.nh-authpane__slot .text-muted,
.nh-authpane__slot #fitem_id_passwordpolicyinfo {
    font-size: 12.5px;
    line-height: 1.55;
    color: var(--nh-ink-muted);
}

.nh-authpane__slot #fitem_id_passwordpolicyinfo { margin-top: -8px; }

/* Submit. */
.nh-authpane__slot .form-group.row.fitem:last-child,
.nh-authpane__slot .fitem_actionbuttons { margin-bottom: 0; }

.nh-authpane__slot .btn-primary,
.nh-authpane__slot input[type="submit"],
.nh-authpane__slot button[type="submit"] {
    /* A submit that is the width of the field above it reads as the end of the
       form rather than as a separate thing sitting under it. `flex` is needed
       as well as `width` because core wraps the buttons in a flex row. */
    flex: 1 1 100% !important;
    width: 100% !important;
    height: 44px;
    min-height: 0;
    margin-top: 4px;
    padding: 0 16px;
    border: 0;
    border-radius: 8px;
    background: var(--nh-primary);
    color: #fff;
    font-family: var(--nh-font-body);
    font-size: 14.5px;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    transition: background .15s ease;
}

/* Core's button row is a flex line built for a page; in a dialog it is one
   full-width control. */
.nh-authpane__slot #fgroup_id_buttonar .felement > div,
.nh-authpane__slot #fgroup_id_buttonar .d-flex {
    width: 100%;
    margin: 0;
}

.nh-authpane__slot .btn-primary:hover,
.nh-authpane__slot input[type="submit"]:hover,
.nh-authpane__slot button[type="submit"]:hover { background: var(--nh-primary-deep); }

.nh-authpane__slot .btn-primary:focus-visible {
    outline: 2px solid var(--nh-primary);
    outline-offset: 2px;
}

/* Cancel belongs to a page with somewhere to go back to; in a dialog the close
   button is that, so core's second button is dropped rather than left to sit
   there doing nothing useful. */
.nh-authpane__slot #id_cancel,
.nh-authpane__slot .btn-secondary { display: none; }

/* ---- Reset password: two ways to find an account. ----
 *
 * Core builds this as two fieldsets, "Search by username" and "Search by email
 * address", each with its own field and its own Search button. That shape is
 * right - they are genuinely two different lookups - but it arrives styled as a
 * page section, with a page-sized heading and a button that hugs its text. In
 * the dialog both blocks have to read as the same control the login pane uses.
 */
.nh-authpane__slot fieldset.clearfix {
    display: block;
    margin: 0 0 4px;
}

/* A hairline between the two forgot-password lookups ("by username" / "by
   email"), so it is obvious they are alternatives rather than two things to fill
   in. Scoped to the forgot pane only: on signup the same fieldset.clearfix marks
   each profile-field section, and a divider there just reads as an unwanted line
   between Sex and Institution. */
[data-nh-auth-pane="forgot"] fieldset.clearfix + fieldset.clearfix {
    margin-top: 22px;
    padding-top: 22px;
    border-top: 1px solid var(--nh-line-soft);
}

.nh-authpane__slot fieldset.clearfix h3 {
    margin: 0 0 14px;
    font-family: var(--nh-font-body);
    font-size: 13px;
    font-weight: 700;
    letter-spacing: .02em;
    text-transform: uppercase;
    color: var(--nh-ink-muted);
}

/* Core wraps that heading in a flex toggler built for collapsible sections. */
.nh-authpane__slot fieldset.clearfix .ftoggler,
.nh-authpane__slot fieldset.clearfix .ftoggler + * {
    margin: 0;
    padding: 0;
}

.nh-authpane__slot fieldset.clearfix .d-flex.align-items-center.mb-2 { margin-bottom: 0 !important; }

.nh-authpane__slot .fcontainer { display: block; width: 100%; }

/* Both Search buttons: the width of the field above them, and the same height
   as every other control in the dialog. */
.nh-authpane__slot #fitem_id_submitbuttonusername,
.nh-authpane__slot #fitem_id_submitbuttonemail { margin-bottom: 0; }

.nh-authpane__slot #fitem_id_submitbuttonusername button[type="submit"],
.nh-authpane__slot #fitem_id_submitbuttonemail button[type="submit"] {
    width: 100% !important;
    height: 44px;
}

/* Switching panes must feel like a change of view, not a page load. The forms
   are already in the DOM by then - see prefetch() - so this is the only thing
   between one pane and the next. */
.nh-authpane { animation: nh-authpane-in .18s ease both; }

@keyframes nh-authpane-in {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .nh-authpane { animation: none; }
}


/* ---- Working state. ----
 *
 * The spinner sits inside the button that was pressed, and that button is
 * disabled while it spins. Disabling is what stops a second submit; the dimming
 * is what makes the disabling something the user can see rather than something
 * only the browser knows.
 */
.nh-authform__submit,
.nh-authpane__slot button[type="submit"],
.nh-authpane__slot .btn-primary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
}

.nh-authform__submit.is-loading,
.nh-authform__submit:disabled,
.nh-authpane__slot button[type="submit"].is-loading,
.nh-authpane__slot button[type="submit"]:disabled {
    background: var(--nh-primary);
    opacity: .6;
    cursor: not-allowed;
}

.nh-authform__spin {
    flex: none;
    width: 15px;
    height: 15px;
    border: 2px solid rgba(255, 255, 255, .45);
    border-top-color: #fff;
    border-radius: 50%;
    animation: nh-authspin .7s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
    .nh-authform__spin { animation-duration: 2s; }
}

/* While a pane is submitting, its fields should not be edited underneath it. */
.nh-authpane__slot form[aria-busy="true"] input,
.nh-authpane__slot form[aria-busy="true"] select { opacity: .7; }

/* ---- Busy. ---- */
.nh-authmodal__busy {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 16px 0 4px;
    font-size: 14px;
    color: var(--nh-ink-muted);
}

.nh-authmodal__spinner {
    width: 18px;
    height: 18px;
    border: 2px solid var(--nh-line);
    border-top-color: var(--nh-primary);
    border-radius: 50%;
    animation: nh-authspin .7s linear infinite;
}

@keyframes nh-authspin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
    .nh-authmodal__spinner { animation-duration: 2s; }
}

/* ---- Small screens: a sheet rather than a floating card. ---- */
@media (max-width: 575.98px) {
    .nh-authmodal { padding: 0; align-items: flex-end; }

    .nh-authmodal__dialog {
        max-width: none;
        max-height: 92vh;
        padding: 28px 20px 24px;
        border-radius: 20px 20px 0 0;
    }
}

/* The count on a catalogue filter pill.
   ---------------------------------------------------------------------------
   How many cards this pill reveals. It is part of the control, not a separate
   badge floating beside it: a pill reading "Business 4" tells you what is behind
   a pathway before you spend a click finding out.

   Tinted rather than coloured, so it stays legible in all three states the pill
   has — resting on white, resting on the brand band, and active. */
.nh-pill__count {
    display: inline-block;
    margin-inline-start: 6px;
    padding: 1px 7px;
    border-radius: 999px;
    background: rgba(11, 35, 71, .08);
    font-size: 11px;
    font-weight: 700;
    line-height: 1.5;
    color: inherit;
}

/* Active: the ground under the pill is dark, so the chip has to lift off it. */
.nh-pill.is-active .nh-pill__count {
    background: rgba(255, 255, 255, .22);
}

/* On the brand band the resting pill is already translucent white. */
.nh-catalog-sec .nh-pill .nh-pill__count {
    background: rgba(255, 255, 255, .18);
}

/* …and its active pill is gold with navy text. */
.nh-catalog-sec .nh-pill.is-active .nh-pill__count {
    background: rgba(11, 35, 71, .14);
}

/* -----------------------------------------------------------------------
   Sign-up (and any core form in the auth modal): one clean, flat column.

   Core marks optional sections of the signup form as COLLAPSIBLE fieldsets
   with an "Expand all" control and a click-to-toggle heading. In a short modal
   that reads as broken — fields hidden behind a disclosure the user did not ask
   for. Force every section open and remove the toggles so every field is
   visible at once, without forking core's login_signup_form.
   ----------------------------------------------------------------------- */
.nh-authpane__slot fieldset.collapsible .fcontainer,
.nh-authpane__slot fieldset.collapsed .fcontainer {
    display: block !important;
    height: auto !important;
    overflow: visible !important;
}

/* Drop the "Expand all" link and the collapse chevrons. */
.nh-authpane__slot .collapsible-actions,
.nh-authpane__slot .fheader .icons-collapse-expand,
.nh-authpane__slot legend .ftoggler .icon {
    display: none !important;
}

/* The signup section headers ("Other fields", "Institution", "Choose your
   username and password", "More details", …) are hidden entirely, so the pane
   reads as one clean, flat column of labelled fields — the field labels alone are
   enough inside the dialog, and the disclosure headings only read as clutter.

   In Moodle 5.2 a form header (core_form/element-header) renders the title TWICE:
   a `<legend class="visually-hidden">` that is already hidden, and a VISIBLE
   `<h3>` inside a `.d-flex` toggler row that the old legend-only rule never
   reached — which is why "Other fields" kept showing. Hiding the whole header row
   is what removes it. Scoped to the signup pane so the forgot-password pane keeps
   its "by username / by email" section labels. */
[data-nh-auth-pane="signup"] fieldset > legend,
[data-nh-auth-pane="signup"] fieldset > .d-flex.align-items-center {
    display: none !important;
}

/* Account-created confirmation reuses the alert strip in a success colour,
   without the error icon. */
.nh-authmodal__alert--success {
    background: #e8f5ee;
    color: #1e7e34;
}

.nh-authmodal__alert--success::before {
    display: none;
}

/* ============================================================ Course library.
   /local/nzeruhub/library.php — every course on the platform, as one grid.

   The card is .nh-module, the same component the landing catalogue and the
   dashboard's "Trending on Nzeru Hub" row are built from; .nh-libcard adds only
   what a full-page grid needs that a scrolling rail does not — a wider cover, a
   clamped title, a footer rule — and the list view is that same card reflowed,
   not a second card.

   Colour comes entirely from the shared --nh-* tokens, so the library re-tints
   with the rest of the platform when the brand colour setting changes.
   ============================================================================ */
body.nzeru-library #page.drawers .main-inner {
    max-width: none;
    padding-left: 0;
    padding-right: 0;
    /* The library is served at /course/index.php, where the theme clips
       horizontal overflow on this element to contain core's category browser.
       `overflow-x: hidden` makes it a scroll container, and a scroll container
       is what the sticky control bar would then be sticky inside — which is to
       say, not sticky at all. Nothing in this grid overflows sideways, so the
       clipping has nothing to do here. */
    overflow-x: visible;
}

.nh-library {
    padding: 28px 0 48px;
}

/* --- Hero -------------------------------------------------------------------
   The front of the catalogue. A navy card in the same gradient the landing page
   opens with, so a learner crossing from the homepage into the library stays on
   one surface — and so the page starts with something, rather than with a
   heading floating on the canvas.

   It carries no controls and no counts. Everything that does anything is in the
   bar underneath, where it can be reached without reading past a picture. */
.nh-libhero {
    position: relative;
    overflow: hidden;
    margin-bottom: 20px;
    padding: clamp(26px, 4vw, 42px) clamp(20px, 3vw, 38px);
    border-radius: var(--nh-radius-lg, 16px);
    background-image:
        radial-gradient(680px 380px at 88% -30%, rgba(253, 184, 19, .22), transparent 58%),
        linear-gradient(135deg, #06182f 0%, #0c2d57 58%, #0a4f8a 100%);
    color: #fff;
}

.nh-libhero__inner {
    position: relative;
    z-index: 1;
    max-width: 62ch;
}

.nh-libhero__eyebrow {
    margin: 0 0 10px;
    font-size: 11px;
    font-weight: 800;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--nh-accent, #fdb813);
}

.nh-libhero__title {
    font-family: var(--nh-font-head);
    font-size: clamp(26px, 3.2vw, 40px);
    font-weight: 800;
    letter-spacing: -.025em;
    line-height: 1.1;
    margin: 0 0 10px;
    color: #fff;
}

.nh-libhero__lede {
    margin: 0;
    font-size: clamp(14px, 1.1vw, 16px);
    line-height: 1.6;
    color: rgba(255, 255, 255, .82);
}

/* The watermark. Decorative, hidden from assistive technology in the markup,
   and clipped by the card so it reads as texture rather than as an icon. */
.nh-libhero__mark {
    position: absolute;
    right: -18px;
    bottom: -34px;
    font-size: 190px;
    line-height: 1;
    color: rgba(255, 255, 255, .07);
    pointer-events: none;
}

/* --- The control bar -------------------------------------------------------
   One card, one line: the count on the left, everything that changes what is on
   screen on the right.

   It sticks to the top of the viewport while the grid scrolls under it. On a
   library of two hundred courses the controls are wanted at the bottom of page
   four as much as at the top of page one, and a bar that scrolls away turns
   every change of mind into a scroll back up. */
.nh-libbar {
    position: sticky;
    top: 12px;
    z-index: 20;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px 20px;
    flex-wrap: wrap;
    margin-bottom: 24px;
    padding: 12px 16px;
    background: var(--nh-surface);
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-lg);
    box-shadow: var(--nh-shadow);
    transition: box-shadow var(--nh-dur) var(--nh-ease);
}

/* Signed in, the theme's navbar is fixed over the top of the page, so the bar
   has to stop below it rather than under it. Signed out the page is full-bleed
   with no navbar, which is the 12px above. */
body.nzeru-inapp .nh-libbar {
    top: calc(var(--navbar-height, 60px) + 12px);
}

/* Lifted once it is actually holding position over the grid, so it reads as a
   layer above the cards rather than a card that stopped scrolling. The class is
   put on by assets/js/library.js — CSS cannot ask whether a sticky element is
   currently stuck. */
.nh-libbar.is-stuck {
    box-shadow: var(--nh-shadow-raised);
    border-color: var(--nh-line-strong);
}

.nh-libbar__count {
    margin: 0;
    min-width: 0;
    font-size: 15px;
    font-weight: 600;
    color: var(--nh-ink-soft);
}

/* The number itself, which is the part that changes as filters are used. */
.nh-libbar__n {
    color: var(--nh-primary);
    font-family: var(--nh-font-head);
    font-size: 19px;
    font-weight: 800;
    letter-spacing: -.01em;
}

.nh-libbar__tools {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 10px;
    flex: 1 1 auto;
    flex-wrap: wrap;
    min-width: 0;
}

/* --- Search ---------------------------------------------------------------- */
.nh-libsearch {
    position: relative;
    display: flex;
    align-items: center;
    flex: 1 1 200px;
    min-width: 170px;
    max-width: 320px;
}

.nh-libsearch__icon {
    position: absolute;
    left: 15px;
    font-size: 13px;
    color: var(--nh-ink-muted);
    pointer-events: none;
    transition: color var(--nh-dur) var(--nh-ease);
}

.nh-libsearch__input {
    width: 100%;
    height: 42px;
    padding: 0 38px 0 38px;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-pill);
    background: var(--nh-surface-2);
    color: var(--nh-ink);
    font: inherit;
    font-size: 14px;
    font-weight: 500;
    transition: border-color var(--nh-dur) var(--nh-ease),
                background var(--nh-dur) var(--nh-ease),
                box-shadow var(--nh-dur) var(--nh-ease);
}

.nh-libsearch__input::placeholder {
    color: var(--nh-ink-muted);
    font-weight: 500;
}

/* The browser's own clear affordance is replaced by the one below, which can be
   made to match everything else on this bar. */
.nh-libsearch__input::-webkit-search-cancel-button {
    display: none;
}

.nh-libsearch__input:hover {
    border-color: var(--nh-line-strong);
}

.nh-libsearch__input:focus {
    outline: none;
    background: var(--nh-surface);
    border-color: var(--nh-primary);
    box-shadow: var(--nh-focus);
}

/* focus-within, not a sibling selector: the icon is before the input in the
   markup, where a reader meets it first. */
.nh-libsearch:focus-within .nh-libsearch__icon {
    color: var(--nh-primary);
}

.nh-libsearch__clear {
    position: absolute;
    right: 6px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--nh-ink-muted);
    font-size: 13px;
    cursor: pointer;
    transition: background var(--nh-dur) var(--nh-ease), color var(--nh-dur) var(--nh-ease);
}

.nh-libsearch__clear:hover {
    background: var(--nh-paper);
    color: var(--nh-ink);
}

.nh-libsearch__clear[hidden] {
    display: none;
}

/* --- Selects ---------------------------------------------------------------
   The native control, with its arrow replaced: a <select> is still the best
   thing on a phone, and nothing here needs a custom listbox. */
.nh-libselect {
    position: relative;
    display: flex;
    align-items: center;
    min-width: 0;
}

.nh-libselect::after {
    content: "\f078"; /* chevron-down, solid. */
    font-family: "Font Awesome 6 Free";
    font-weight: 900;
    position: absolute;
    right: 14px;
    font-size: 10px;
    color: var(--nh-ink-muted);
    pointer-events: none;
}

.nh-libselect select {
    -webkit-appearance: none;
    appearance: none;
    height: 42px;
    max-width: 190px;
    padding: 0 34px 0 15px;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-pill);
    background: var(--nh-surface-2);
    color: var(--nh-ink);
    font: inherit;
    font-size: 14px;
    font-weight: 600;
    text-overflow: ellipsis;
    cursor: pointer;
    transition: border-color var(--nh-dur) var(--nh-ease),
                background var(--nh-dur) var(--nh-ease),
                box-shadow var(--nh-dur) var(--nh-ease);
}

.nh-libselect select:hover {
    border-color: var(--nh-line-strong);
}

.nh-libselect select:focus {
    outline: none;
    background: var(--nh-surface);
    border-color: var(--nh-primary);
    box-shadow: var(--nh-focus);
}

/* A select that is no longer on its "all" option is doing something, and says
   so — otherwise a filtered page looks identical to an unfiltered one. */
.nh-libselect select.is-set {
    background: var(--nh-primary-tint);
    border-color: var(--nh-primary-tint-2);
    color: var(--nh-primary);
}

/* --- Grid / list switch ----------------------------------------------------- */
.nh-libview {
    display: inline-flex;
    gap: 2px;
    padding: 3px;
    background: var(--nh-paper);
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-pill);
}

.nh-libview__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--nh-ink-muted);
    font-size: 13px;
    cursor: pointer;
    transition: background var(--nh-dur) var(--nh-ease), color var(--nh-dur) var(--nh-ease);
}

.nh-libview__btn:hover {
    color: var(--nh-primary);
}

.nh-libview__btn.is-active {
    background: var(--nh-primary);
    color: #fff;
    box-shadow: 0 6px 14px -8px rgba(0, 91, 187, .9);
}

.nh-libview__btn:focus-visible,
.nh-libsearch__clear:focus-visible {
    outline: 2px solid var(--nh-primary);
    outline-offset: 2px;
}

/* --- The grid ---------------------------------------------------------------
   Four across at the container's full width, and fewer as it narrows — from one
   rule rather than a breakpoint per step.

   The 260px is what sets the count. The column is 1176px wide (a 1240px
   container less its 32px gutters), so with a 22px gap auto-fill takes as many
   260px columns as fit: four need 4x260 + 3x22 = 1106, which fits; five need
   1388, which does not. Narrow the window and the same arithmetic drops it to
   three, then two, then one. Change this number and you change the column count
   — that is the knob, not a media query. */
.nh-library__list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr));
    gap: 22px;
    align-items: stretch;
}

/* --- The card --------------------------------------------------------------- */
.nh-libcard {
    position: relative;
    animation: nh-libcard-in var(--nh-dur-slow) var(--nh-ease) both;
}

/* Re-runs whenever a card comes back from `hidden`, so paging and filtering
   settle in rather than snapping. Motion is off under prefers-reduced-motion,
   which the tokens file handles globally. */
@keyframes nh-libcard-in {
    from {
        opacity: 0;
        transform: translateY(8px);
    }
    to {
        opacity: 1;
        transform: none;
    }
}

.nh-libcard:hover {
    border-color: var(--nh-line-strong);
}

/* The whole card reacts to the title being tabbed to, not just the link. */
.nh-libcard:focus-within {
    border-color: var(--nh-primary);
    box-shadow: var(--nh-focus);
}

.nh-libcard__cover {
    position: relative;
    display: block;
    overflow: hidden;
    aspect-ratio: 16 / 10;
    background: linear-gradient(135deg, var(--nh-primary-deep) 0%, var(--nh-primary) 100%);
}

.nh-libcard__cover img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform var(--nh-dur-slow) var(--nh-ease);
}

.nh-libcard:hover .nh-libcard__cover img {
    transform: scale(1.05);
}

/* The pathway icon, over a generated cover only — never over a photograph. */
.nh-libcard__ph {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 34px;
    color: rgba(255, 255, 255, .92);
    text-shadow: 0 2px 12px rgba(8, 17, 49, .35);
}

.nh-libcard__level,
.nh-libcard__flag {
    position: absolute;
    top: 12px;
    padding: 5px 10px;
    border-radius: var(--nh-radius-pill);
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: .07em;
    text-transform: uppercase;
    line-height: 1;
}

.nh-libcard__level {
    left: 12px;
    background: rgba(255, 255, 255, .94);
    color: var(--nh-primary-deep);
}

/* "Enrolled" is the one thing a learner scanning this grid wants to spot from
   across the page, so it is the only gold on the card and it sits opposite the
   level rather than queueing behind it. */
.nh-libcard__flag {
    right: 12px;
    background: var(--nh-accent);
    color: var(--nh-primary-deep);
}

/* --- THE TWO CORNERS, ON EVERY KIND OF COURSE CARD -------------------------

   Three templates draw course cards -- the landing page's .nh-module, the
   dashboard's .nh-ccard and the catalogue's .nh-libcard -- and all three now
   carry the same two things: what this course is to you, top left, and the
   question the card has no room to answer, top right. One pair of rules rather
   than three, so they cannot drift apart.

   Both are positioned against the CARD, which each of the three already makes
   a positioning context. */
/*
 * THE CARDS HAVE TO ANCHOR THEIR OWN CORNERS.
 *
 * Said here rather than trusted to each card's own rule, because .nh-ccard
 * computes to `static` on My Learning -- a later rule in this stylesheet wins
 * there -- and an absolutely positioned child of a static parent climbs until
 * it finds one that is not. It found the theme's drawer container, and put the
 * status chip 268 pixels above the card it belonged to.
 */
.nh-ccard,
.nh-module,
.nh-libcard {
    position: relative;
}

.nh-cstatus,
.nh-cinfo {
    position: absolute;
    top: 12px;
    z-index: 3;
}

.nh-cstatus {
    left: 12px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 11px;
    border-radius: var(--nh-radius-pill);
    background: rgba(255, 255, 255, .94);
    color: var(--nh-primary-deep);
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: .06em;
    line-height: 1;
    text-transform: uppercase;
    box-shadow: 0 2px 10px rgba(8, 17, 49, .18);
}

.nh-cstatus i { font-size: 11px; }

/* Three states, three treatments, so which one it is can be read from across
   the page rather than by stopping to read the word. */
.nh-cstatus--done { background: var(--nh-success); color: #fff; }
.nh-cstatus--going { background: var(--nh-accent); color: var(--nh-primary-deep); }
.nh-cstatus--joined { background: rgba(255, 255, 255, .94); color: var(--nh-primary-deep); }

.nh-cinfo {
    right: 12px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    /* A hairline as well as the shadow: on the dashboard cards this sits on a
       white body rather than over a cover, and a white circle on white needs an
       edge to be seen at all. */
    border: 1px solid var(--nh-line);
    border-radius: 50%;
    background: rgba(255, 255, 255, .94);
    color: var(--nh-primary-deep);
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
    box-shadow: 0 2px 10px rgba(8, 17, 49, .22);
    transition: background var(--nh-dur) var(--nh-ease),
                color var(--nh-dur) var(--nh-ease),
                border-color var(--nh-dur) var(--nh-ease),
                transform var(--nh-dur) var(--nh-ease);
}

.nh-cinfo:hover,
.nh-cinfo:focus-visible {
    border-color: var(--nh-primary-deep);
    background: var(--nh-primary-deep);
    color: #fff;
    transform: scale(1.08);
}

.nh-cinfo:focus-visible {
    outline: 3px solid var(--nh-accent);
    outline-offset: 2px;
}

/* The dashboard card keeps a bookmark star in that corner already, so the info
   button steps aside for it rather than sitting on top of it. */
.nh-ccard .nh-cinfo { right: 52px; }

/* The catalogue card's level chip had the top-left corner. The status chip is
   the more useful of the two -- "what is this to me" beats "how hard is it" --
   so the level moves under it. */
/* The level chip no longer sits on the cover, so nothing has to be pushed
   out of the status chip's way. Kept as a note rather than deleted in
   silence: the selector above still styles .nh-libcard__level, which other
   card layouts may yet use. */

/* ...and the "Enrolled" flag is now said by the status chip, so it goes. Left
   in the markup because apply_enrolment_state() still stamps it and other
   things read it; simply not drawn twice. */
.nh-libcard .nh-libcard__flag { display: none; }

/* --- inside the modal ------------------------------------------------------
   core/modal_cancel brings its own frame, position and backdrop -- the whole
   point of using it -- so this is only the type scale of what goes in it. */
.nh-cabout__eyebrow {
    margin: 0 0 10px;
    font-size: 11.5px;
    font-weight: 800;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--nh-primary);
}

.nh-cabout__body {
    font-size: 15px;
    line-height: 1.65;
    color: var(--nh-ink);
}

.nh-cabout__body :first-child { margin-top: 0; }
.nh-cabout__body :last-child { margin-bottom: 0; }
.nh-cabout__body p { margin: 0 0 12px; }
.nh-cabout__body ul,
.nh-cabout__body ol { margin: 0 0 12px; padding-left: 22px; }
.nh-cabout__body li { margin-bottom: 6px; }
.nh-cabout__body h1,
.nh-cabout__body h2,
.nh-cabout__body h3,
.nh-cabout__body h4 {
    margin: 18px 0 8px;
    font-size: 16px;
    font-weight: 800;
    color: var(--nh-primary-deep);
}
.nh-cabout__body a { color: var(--nh-primary); text-decoration: underline; }
.nh-cabout__body img { max-width: 100%; height: auto; border-radius: 10px; }

/* --- "Load more", under a certificates section ---------------------------
   Centred under its own grid rather than at the foot of the page: each section
   counts separately, and a single button at the bottom could not say which of
   the two it would grow. */
.nh-certs__more {
    margin: 1.1rem 0 0;
    text-align: center;
}

.nh-certs__morebtn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.6rem 1.25rem;
    border: 1px solid var(--nh-line, #E2E7EF);
    border-radius: 999px;
    background: #fff;
    color: var(--nh-ink, #14294F);
    font-size: 0.9rem;
    font-weight: 600;
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease;
}

.nh-certs__morebtn:hover {
    border-color: var(--nh-primary, #005bbb);
    background: var(--nh-surface-2, #F5F7FA);
}

/* How many are still behind it. A number beside the words answers "is it worth
   pressing" without making the reader press it to find out. */
.nh-certs__morecount {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.5rem;
    height: 1.5rem;
    padding: 0 0.4rem;
    border-radius: 999px;
    background: var(--nh-blue-tint, #e7f0fb);
    color: var(--nh-primary, #005bbb);
    font-size: 0.78rem;
    font-weight: 700;
}

/* --- The "about this course" dialog -------------------------------------
   Dressed to match .nh-confirm, which is the other dialog this card opens.
   The two are about the same course and were drawn by two different hands:
   one centred and styled, one pinned near the top in Bootstrap's own clothes.

   The .nh-cabout__go rule that used to sit here is gone with the button it
   held -- this popup answers a question and does not also sell. */
.nh-cabout .modal-dialog {
    max-width: 34rem;
}

.nh-cabout .modal-content {
    border: 0;
    border-radius: 18px;
    box-shadow: 0 30px 70px rgba(11, 27, 54, .35);
}

.nh-cabout .modal-header {
    padding: 1.25rem 1.35rem 0.5rem;
    border-bottom: 0;
}

.nh-cabout .modal-title {
    font-family: "Plus Jakarta Sans", Inter, system-ui, sans-serif;
    font-size: 1.2rem;
    font-weight: 800;
    letter-spacing: -0.01em;
    color: var(--nh-ink);
}

.nh-cabout .modal-body {
    padding: 0.25rem 1.35rem 1rem;
}

.nh-cabout .modal-footer {
    padding: 0.75rem 1.35rem 1.25rem;
    border-top: 0;
}

/* One button, and it is the way out -- so it is quiet, not a call to action. */
.nh-cabout .modal-footer .btn {
    min-width: 7rem;
    border-radius: 10px;
    font-weight: 600;
}

@media (max-width: 480px) {
    .nh-cstatus { font-size: 10px; padding: 4px 9px; }
    .nh-cinfo { width: 30px; height: 30px; font-size: 14px; }
    .nh-ccard .nh-cinfo { right: 48px; }
}

/* ...and green once it is finished. Gold says "you are in this one"; a course
   already completed is a different fact, and on a grid where several cards
   carry a badge the two states have to be tellable apart at a glance rather
   than by reading. */
.nh-libcard__flag--done {
    background: var(--nh-success);
    color: #fff;
}

.nh-libcard__body {
    display: flex;
    flex-direction: column;
    gap: 10px;
    flex: 1;
    padding: 16px 18px 18px;
    min-width: 0;
}

.nh-libcard__cat {
    align-self: flex-start;
    max-width: 100%;
    margin: 0;
    padding: 4px 10px;
    border-radius: var(--nh-radius-pill);
    background: var(--nh-primary-tint);
    color: var(--nh-primary);
    font-size: 10.5px;
    font-weight: 800;
    letter-spacing: .08em;
    text-transform: uppercase;
    line-height: 1.4;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.nh-libcard__title {
    margin: 0;
    font-family: var(--nh-font-head);
    font-size: 17px;
    font-weight: 700;
    line-height: 1.35;
    letter-spacing: -.01em;
    /* Two lines, so a long title cannot push one card taller than its row. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    overflow-wrap: anywhere;
    /* Text, not a link. The only thing on this card that goes anywhere is the
       button in its foot — see the note in the template. */
    color: var(--nh-ink);
}

.nh-libcard__foot {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: auto;
    padding-top: 14px;
    border-top: 1px solid var(--nh-line);
}

.nh-libcard__cta {
    width: 100%;
    white-space: nowrap;
}

/* --- List view --------------------------------------------------------------
   The same card, laid on its side. One markup, one set of behaviour; only the
   flow direction changes. */
.nh-library__list.is-list {
    grid-template-columns: 1fr;
    gap: 14px;
}

.is-list .nh-libcard {
    flex-direction: row;
    /* ONE HEIGHT FOR EVERY ROW.
       Left to itself a row card is as tall as its contents, so the list came out
       ragged: 145px for a course with no fields set, 148px with one line of
       chips, 186px when those chips wrapped onto a second line. In a grid that
       never shows, because the grid gives every card in a row the height of the
       tallest; in a single column each card is its own row and the raggedness is
       all there is to see. A list is read by running your eye down it, and that
       is exactly what an uneven left edge interrupts.
       The height is fixed here rather than equalised in JavaScript so it is
       right on the first paint, with no reflow to watch.

       THE NUMBER IS MEASURED, NOT CHOSEN. A card with a line of chips wants
       186px; the first version of this rule said 152px, and since the body is a
       flex column its children simply shrank to fit - which for a line of text
       means a 25px line rendered in an 11px box, so the course name was cut
       through the middle. Every card had the same height and one of them was
       unreadable. The value is the tallest a row card can want plus a little
       slack, because the heading font arrives asynchronously and line heights
       can settle a pixel or two either way. */
    height: var(--nh-libcard-row-h, 192px);
}

/* NOTHING WITH WORDS IN IT MAY BE SHRUNK TO FIT.
   The fixed height above is only safe because these three keep their natural
   size: a clipped line of text reads as a rendering fault, and the button is
   the only thing on the card that does anything. The chip line is the one part
   allowed to give way, because it is a summary and everything it says is on the
   course page a click later - so if a card ever does want more room than there
   is, the chips lose it rather than the title. */
.is-list .nh-libcard__cat,
.is-list .nh-libcard__title,
.is-list .nh-libcard__foot {
    flex: none;
}

.is-list .nh-cardmeta {
    flex: 0 1 auto;
    min-height: 0;
}

.is-list .nh-libcard__cover {
    flex: 0 0 clamp(180px, 24%, 260px);
    /* The cover takes the card's height instead of setting it. object-fit on the
       image inside means a portrait or a panorama both fill the same box. */
    aspect-ratio: auto;
    height: 100%;
}

.is-list .nh-libcard__body {
    padding: 18px 20px;
    gap: 8px;
    /* Nothing may spill past the fixed height. */
    overflow: hidden;
}

.is-list .nh-libcard__title {
    font-size: 18px;
    -webkit-line-clamp: 1;
    line-clamp: 1;
}

/* The chips are a summary, and a fourth one is not worth an uneven list, so in
   row mode they stay on one line and the overflow is clipped rather than
   wrapping. Everything they say is on the course page a click away. */
.is-list .nh-cardmeta {
    flex-wrap: nowrap;
    overflow: hidden;
    min-width: 0;
}

.is-list .nh-libcard__foot {
    padding-top: 12px;
    border-top: 0;
}

.is-list .nh-libcard__cta {
    width: auto;
}

/* --- Empty state ------------------------------------------------------------ */
.nh-library__empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding: 56px 0;
    text-align: center;
    color: var(--nh-ink-soft);
}

.nh-library__empty-icon {
    font-size: 30px;
    color: var(--nh-ink-muted);
}

/* --- Pager ------------------------------------------------------------------ */
.nh-library__pager {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 34px;
}

.nh-library__page {
    min-width: 42px;
    height: 42px;
    padding: 0 12px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-pill);
    background: var(--nh-surface);
    color: var(--nh-ink-soft);
    font: inherit;
    font-weight: 700;
    font-size: 14px;
    cursor: pointer;
    transition: color var(--nh-dur) var(--nh-ease),
                border-color var(--nh-dur) var(--nh-ease),
                background var(--nh-dur) var(--nh-ease),
                transform var(--nh-dur) var(--nh-ease);
}

.nh-library__page:hover:not(:disabled) {
    border-color: var(--nh-primary);
    color: var(--nh-primary);
    transform: translateY(var(--nh-lift));
}

.nh-library__page.is-active {
    background: var(--nh-primary);
    border-color: var(--nh-primary);
    color: #fff;
    box-shadow: 0 8px 18px -10px rgba(0, 91, 187, .9);
}

.nh-library__page:disabled {
    opacity: .4;
    cursor: default;
}

.nh-library__ellipsis {
    color: var(--nh-ink-muted);
    padding: 0 4px;
}

/* --- Responsive -------------------------------------------------------------
   The grid needs no breakpoints of its own — auto-fill already handles it. What
   follows is the control bar, which has to stop being one line at the width
   where one line would mean six controls at 40px each. */
@media (max-width: 1100px) {
    .nh-libbar__count {
        flex: 1 1 100%;
    }

    .nh-libbar__tools {
        justify-content: flex-start;
    }

    .nh-libsearch {
        max-width: none;
    }
}

@media (max-width: 900px) {
    .nh-library {
        padding: 22px 0 36px;
    }

    .nh-library__list {
        gap: 16px;
    }
}

@media (max-width: 640px) {
    .nh-library {
        padding: 16px 0 28px;
    }

    .nh-libhero {
        margin-bottom: 14px;
        border-radius: var(--nh-radius, 12px);
    }

    /* The watermark is texture on a wide card and clutter on a narrow one. */
    .nh-libhero__mark {
        display: none;
    }

    /* Unstuck on a phone: the viewport is short enough that a pinned bar costs
       more of the grid than it saves in scrolling. */
    .nh-libbar {
        position: static;
        padding: 12px;
        gap: 10px;
    }

    .nh-libbar__count {
        font-size: 14px;
    }

    /* Each control on its own full-width line, except the two that are small
       enough to share one — the sort select and the view switch. */
    .nh-libsearch,
    .nh-libselect {
        flex: 1 1 100%;
    }

    .nh-libselect select {
        width: 100%;
        max-width: none;
    }

    .nh-libselect--sort {
        flex: 1 1 0;
    }

    .nh-libview {
        flex: none;
    }

    /* A phone shows one card per row either way, so the switch would be a choice
       between two identical layouts. What list view still buys here is the even
       height from above, kept by pinning the cover instead of letting it be a
       ratio of the viewport - which is what made the stacked cards 495px and
       535px tall on the same screen. */
    .is-list .nh-libcard {
        flex-direction: column;
        /* Measured the same way: 337px is the tallest a stacked card wants. */
        height: var(--nh-libcard-stack-h, 344px);
    }

    .is-list .nh-libcard__cover {
        flex: 0 0 var(--nh-libcard-stack-cover-h, 150px);
        aspect-ratio: auto;
        height: auto;
    }

    /* Room for a wrapped chip line on a narrow card, still bounded by the card. */
    .is-list .nh-cardmeta {
        flex-wrap: wrap;
    }

    .is-list .nh-libcard__foot {
        border-top: 1px solid var(--nh-line);
    }

    .is-list .nh-libcard__cta {
        width: 100%;
    }

    .nh-library__pager {
        margin-top: 24px;
        gap: 6px;
    }

    .nh-library__page {
        min-width: 38px;
        height: 38px;
        padding: 0 9px;
    }
}


/* ---------------------------------------------------------------------------
   The "Enrol in this course?" dialog
   ---------------------------------------------------------------------------
   Core's modal_save_cancel, wearing this design language. The behaviour is
   core's on purpose — focus trapping, Escape, the backdrop and returning focus
   to the button afterwards are all things it already gets right — so only the
   surface is ours.

   Scoped to `.nh-confirm`, which local_nzeruhub/enrol_confirm puts on the dialog
   it opens, so no other modal on the site is touched.

   It lives in styles.css rather than dashboard.css because the library asks the
   same question from the same module, and dashboard.css is not loaded there.
   The dashboard loads both sheets (see \local_nzeruhub\hook_callbacks), so it
   still gets this.
   --------------------------------------------------------------------------- */
.nh-confirm .modal-dialog {
    max-width: 30rem;
}

.nh-confirm .modal-content {
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-lg, 16px);
    box-shadow: 0 24px 60px -24px rgba(11, 35, 71, .45);
    font-family: var(--nh-font-body);
    color: var(--nh-ink);
}

.nh-confirm .modal-header {
    align-items: center;
    gap: .85rem;
    padding: 1.35rem 1.35rem 0.6rem;
    border-bottom: 0;
}

/* A mortarboard on a tinted chip, so the dialog says what it is about before a
   word of it is read. Decorative, hence a pseudo-element rather than markup a
   screen reader would have to step past. */
.nh-confirm .modal-header::before {
    content: "\f19d"; /* graduation-cap, solid. */
    font-family: "Font Awesome 6 Free";
    font-weight: 900;
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 50%;
    background: var(--nh-primary-tint, #e7f0fb);
    color: var(--nh-primary);
    font-size: 1rem;
}

.nh-confirm .modal-title {
    font-family: var(--nh-font-head);
    font-size: 1.15rem;
    font-weight: 700;
    letter-spacing: -.01em;
    color: var(--nh-ink);
}

/* Core's close button sits at the end of the header row. */
.nh-confirm .modal-header .close,
.nh-confirm .modal-header .btn-close {
    margin-inline-start: auto;
}

.nh-confirm .modal-body {
    /* Lined up under the title, clear of the chip. */
    padding: 0 1.35rem 0.25rem 4.7rem;
}

/* The question, naming the course. */
.nh-confirm__lead {
    margin: 0;
    font-size: 1rem;
    line-height: 1.5;
    color: var(--nh-ink);
}

/* What the course is, when its description says. Sits between the question
   and the reassurance because that is the order they are read in: what am I
   joining, what is it, what happens if I say yes.

   BOUNDED BY CLAMPING, NOT BY SCROLLING.

   It used to be a fixed height with overflow-y: auto, which was the right
   instinct and the wrong control: a description written as one unbroken line
   would otherwise push the confirm button off a laptop screen, and a dialog
   whose button cannot be reached is worse than one with no description.

   But a scrollbar inside a confirmation is a thing to operate in the middle of
   a question -- it says there is more to read before deciding, in a panel whose
   whole job is one decision. Clamped instead: four lines and an ellipsis. The
   height is still bounded, nothing moves, and the full description is a click
   away on the card the reader came from. */
.nh-confirm__summary {
    display: -webkit-box;
    -webkit-line-clamp: 4;
    line-clamp: 4;
    -webkit-box-orient: vertical;
    overflow: hidden;
    margin: .75rem 0 0;
    padding: .7rem .85rem;
    border-left: 3px solid var(--nh-primary, #005bbb);
    border-radius: 0 8px 8px 0;
    background: var(--nh-surface-2, #fafbfe);
    color: var(--nh-ink-soft, #4a5670);
    font-size: .9rem;
    line-height: 1.55;
}

/* What saying yes actually does. Quieter than the question, because it is
   reassurance rather than the decision. */
.nh-confirm__note {
    margin: .5rem 0 0;
    font-size: .875rem;
    line-height: 1.5;
    color: var(--nh-ink-soft);
}

.nh-confirm .modal-footer {
    gap: .6rem;
    padding: 1rem 1.35rem 1.25rem;
    border-top: 0;
}

/* Cancel and Enrol, in the platform's own buttons rather than Bootstrap's. The
   destructive-looking one here is not Cancel — leaving is free — so Cancel is
   the quiet one and Enrol carries the brand. */
.nh-confirm .modal-footer .btn {
    min-width: 7rem;
    border-radius: var(--nh-radius-xs, 8px);
    padding: .55rem 1.1rem;
    font-weight: 600;
    font-size: .95rem;
}

.nh-confirm .modal-footer .btn-primary {
    background: var(--nh-primary);
    border-color: var(--nh-primary);
    color: #fff;
}

.nh-confirm .modal-footer .btn-secondary {
    background: transparent;
    border-color: var(--nh-line-strong, #d3dae6);
    color: var(--nh-ink);
}

.nh-confirm .modal-footer .btn-secondary:hover,
.nh-confirm .modal-footer .btn-secondary:focus {
    background: var(--nh-paper);
    color: var(--nh-ink);
}

/* On a phone the dialog is the screen: full width, and the two actions stack
   with the one you are most likely to want on top. */
@media (max-width: 575.98px) {
    /* The variable, not the margin itself: Bootstrap centres the dialog with
       `min-height: calc(100% - var(--bs-modal-margin) * 2)`, so setting only the
       margin would leave that calculation working from the old value and the
       dialog a few pixels off centre. */
    .nh-confirm .modal-dialog {
        --bs-modal-margin: .75rem;
    }

    /* The chip costs more than it gives at this width: it pushes the question
       into a narrow ribbon down the side of the screen. */
    .nh-confirm .modal-header::before {
        display: none;
    }

    .nh-confirm .modal-header {
        padding: 1.1rem 1.1rem 0.5rem;
    }

    .nh-confirm .modal-body {
        padding-left: 1.1rem;
        padding-right: 1.1rem;
    }

    .nh-confirm .modal-footer {
        flex-direction: column-reverse;
        align-items: stretch;
        padding: 1rem 1.1rem 1.15rem;
    }

    .nh-confirm .modal-footer .btn {
        width: 100%;
        margin: 0;
    }
}

/* ============================================================================
   My certificates (/certificates.php)

   Built from the reference design: a count box in the header, a chip row, and two
   sections — "Almost there" for what is still being worked on, "Earned
   certificates" for the shelf. Uses the --nh-* tokens so it inherits the brand
   rather than restating it, and `auto-fill` so the column count follows the width
   available instead of a ladder of breakpoints guessing at devices.
   ========================================================================== */

.nh-certs {
    /* IT MUST CENTRE ITSELF. The page carries `nzeru-inapp`, and that class
       deliberately takes the cap off the content column —
       `#page.drawers .main-inner { max-width: none }` in theme/nzeru — because the
       in-app public pages are full-bleed and each section inside them supplies its
       own centred container.
       This page is one of those, so this IS that container. Removing it on the
       reasoning that "the region already has a max-width" was wrong: on this
       layout the region has none, and the page went the full width of the glass.
       1320px via --nh-container, which is the same measure the dashboard and the
       landing sections use, so every signed-in page lines up. */
    width: 100%;
    max-width: var(--nh-container);
    /* A grid child, and a flex child on some layouts, must be allowed to shrink
       below its content or it pushes its container wider. See the note on the
       list layout below: this is what stopped the panel changing width when the
       layout was switched. */
    min-width: 0;
    margin: 0 auto;
    padding: 4px 0 48px;
}

/* --- The panel that holds the whole page ---------------------------------- */
/* One surface under the title, the toolbar and both sections. Without it the
   section headings and the chip row floated on the page background with nothing
   grouping them, and the page read as three unrelated strips. */
.nh-certs__panel {
    padding: 28px;
    background: var(--nh-surface);
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-xl);
    box-shadow: var(--nh-shadow-flat);
}

/* --- Header ---------------------------------------------------------------- */
.nh-certs__headrow {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 20px;
    flex-wrap: wrap;
    margin-bottom: 26px;
}

.nh-certs__headtext { min-width: 0; }

.nh-certs__title {
    margin: 0 0 6px;
    font-family: var(--nh-font-head);
    font-size: clamp(24px, 3.4vw, 32px);
    font-weight: 700;
    letter-spacing: -.02em;
    color: var(--nh-ink);
    overflow-wrap: anywhere;
}

.nh-certs__lede {
    margin: 0;
    /* The only run of continuous prose on the page, so it gets a measure. */
    max-width: 52ch;
    color: var(--nh-ink-soft);
    font-size: 14.5px;
    line-height: 1.6;
}

/* Two numbers side by side, divided by a hairline. */
.nh-certs__count {
    display: flex;
    flex: none;
    padding: 12px 4px;
    background: var(--nh-surface);
    border: 1px solid var(--nh-line);
    border-radius: 14px;
}

.nh-certs__count > div {
    padding: 0 20px;
    text-align: center;
}

.nh-certs__count > div + div { border-left: 1px solid var(--nh-line); }

.nh-certs__count b {
    display: block;
    font-family: var(--nh-font-head);
    font-size: 21px;
    font-weight: 700;
    line-height: 1.2;
    color: var(--nh-ink);
    font-variant-numeric: tabular-nums;
}

.nh-certs__count span {
    color: var(--nh-ink-muted);
    font-size: 11.5px;
    font-weight: 500;
    white-space: nowrap;
}

/* --- Chips, search, layout switch ----------------------------------------- */
.nh-certs__bar {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding-bottom: 24px;
}

.nh-certs__chips {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.nh-cchip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    /* 44px: this row is thumbed on a phone, and anything smaller is a miss. */
    min-height: 44px;
    padding: 8px 15px;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-pill);
    background: var(--nh-surface);
    color: var(--nh-ink-soft);
    font-family: inherit;
    font-size: 13.5px;
    font-weight: 500;
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--nh-dur) var(--nh-ease), color var(--nh-dur) var(--nh-ease),
                border-color var(--nh-dur) var(--nh-ease);
}

.nh-cchip:hover { border-color: var(--nh-primary-deep); color: var(--nh-primary-deep); }

.nh-cchip.is-active {
    background: var(--nh-primary-deep);
    border-color: var(--nh-primary-deep);
    color: #fff;
}

.nh-cchip i {
    padding: 1px 7px;
    border-radius: 20px;
    background: rgba(20, 40, 107, .08);
    font-style: normal;
    font-size: 11.5px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.nh-cchip.is-active i { background: rgba(255, 255, 255, .2); }

.nh-certs__tools {
    margin-left: auto;
    display: flex;
    gap: 10px;
    align-items: center;
    flex-wrap: wrap;
}

/* PROVIDER AND CERTIFICATION, on the certificates bar.

   The same control as the Explore filters and deliberately so — but written
   here rather than sharing that rule, because this bar's search input is 44px
   and Explore's is 42px, and a select two pixels shorter than the box beside it
   is the kind of thing you cannot unsee once noticed. */
.nh-certs__select {
    position: relative;
    display: flex;
    align-items: center;
    min-width: 0;
}

.nh-certs__select::after {
    content: "\f078"; /* chevron-down, solid. */
    font-family: "Font Awesome 6 Free";
    font-weight: 900;
    position: absolute;
    right: 14px;
    font-size: 10px;
    color: var(--nh-ink-muted);
    pointer-events: none;
}

.nh-certs__select select {
    -webkit-appearance: none;
    appearance: none;
    height: 44px;
    max-width: 190px;
    padding: 0 34px 0 15px;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-pill);
    background: var(--nh-surface);
    color: var(--nh-ink);
    font: inherit;
    font-size: 13.5px;
    font-weight: 600;
    text-overflow: ellipsis;
    cursor: pointer;
    transition: border-color var(--nh-dur) var(--nh-ease),
                background var(--nh-dur) var(--nh-ease),
                box-shadow var(--nh-dur) var(--nh-ease);
}

.nh-certs__select select:hover {
    border-color: var(--nh-primary);
}

.nh-certs__select select:focus {
    outline: none;
    background: var(--nh-surface);
    border-color: var(--nh-primary);
    box-shadow: var(--nh-focus);
}

/* A select that is no longer on its "all" option is doing something, and says
   so — otherwise a filtered shelf looks identical to an unfiltered one. */
.nh-certs__select select.is-set {
    background: var(--nh-primary-tint);
    border-color: var(--nh-primary-tint-2);
    color: var(--nh-primary);
}

.nh-certs__search {
    position: relative;
    display: flex;
    align-items: center;
}

.nh-certs__search i {
    position: absolute;
    inset-inline-start: 14px;
    color: var(--nh-ink-muted);
    font-size: 12.5px;
    pointer-events: none;
}

.nh-certs__search input {
    width: 220px;
    min-width: 0;
    height: 44px;
    padding: 0 16px 0 36px;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-pill);
    background: var(--nh-surface);
    font-family: inherit;
    font-size: 13.5px;
    color: var(--nh-ink);
}

.nh-certs__search input::placeholder { color: var(--nh-ink-muted); }

.nh-certs__search input:focus {
    outline: none;
    border-color: var(--nh-primary);
    box-shadow: var(--nh-focus);
}

.nh-certs__views {
    display: flex;
    gap: 2px;
    padding: 3px;
    background: var(--nh-surface);
    border: 1px solid var(--nh-line);
    border-radius: 11px;
}

.nh-cview {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 38px;
    padding: 7px 12px;
    border: 0;
    border-radius: 8px;
    background: none;
    color: var(--nh-ink-muted);
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
    transition: background var(--nh-dur) var(--nh-ease), color var(--nh-dur) var(--nh-ease);
}

.nh-cview:hover { background: var(--nh-surface-2); color: var(--nh-primary-deep); }

.nh-cview.is-active { background: var(--nh-primary-deep); color: #fff; }

/* --- Section heading with a rule running off it --------------------------- */
.nh-certs__section { padding-bottom: 36px; }

.nh-certs__sh {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0 0 16px;
}

.nh-certs__sh h2 {
    margin: 0;
    font-family: var(--nh-font-head);
    font-size: 16px;
    font-weight: 600;
    letter-spacing: -.01em;
    white-space: nowrap;
    color: var(--nh-ink);
}

.nh-certs__sh span {
    color: var(--nh-ink-muted);
    font-size: 13px;
    white-space: nowrap;
}

.nh-certs__sh::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--nh-line-strong);
}

/* --- Grids ---------------------------------------------------------------- */
.nh-certs__grid {
    display: grid;
    /* min(100%, 285px) so the floor cannot overflow a viewport narrower than it. */
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 285px), 1fr));
    gap: 20px;
}

.nh-certs__grid.is-list { grid-template-columns: 1fr; gap: 11px; }

[data-nh-cert-card][hidden] { display: none !important; }

/* --- In-progress card ----------------------------------------------------- */
.nh-wip {
    display: flex;
    flex-direction: column;
    padding: 20px;
    background: var(--nh-surface);
    border: 1px solid var(--nh-line);
    border-radius: 18px;
}

.nh-wip.is-locked { background: var(--nh-surface-2); }

.nh-wip__top {
    display: flex;
    align-items: center;
    gap: 11px;
    margin-bottom: 16px;
}

.nh-wip__mark {
    display: grid;
    place-items: center;
    flex: none;
    width: 38px;
    height: 38px;
    border-radius: 10px;
    background: var(--nh-primary-tint);
    color: var(--nh-primary-deep);
    font-size: 12px;
    font-weight: 700;
}

.nh-wip__id { min-width: 0; }

.nh-wip__issuer {
    margin: 0;
    color: var(--nh-ink-muted);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .07em;
    text-transform: uppercase;
    /* One line: the issuer is a label, not a sentence. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.nh-wip__name {
    margin: 1px 0 0;
    font-family: var(--nh-font-head);
    font-size: 15.5px;
    font-weight: 600;
    line-height: 1.3;
    letter-spacing: -.01em;
    color: var(--nh-ink);
    overflow-wrap: anywhere;
}

.nh-wip__activity {
    margin: 3px 0 0;
    color: var(--nh-ink-muted);
    font-size: 12.5px;
}

.nh-wip__steps {
    display: grid;
    gap: 9px;
    margin: 0 0 16px;
    padding: 0;
    list-style: none;
}

.nh-wip__steps li {
    display: flex;
    align-items: center;
    gap: 9px;
    font-size: 13.5px;
    line-height: 1.4;
}

.nh-wip__steps i { flex: none; font-size: 14px; }

.nh-wip__steps .is-done { color: var(--nh-ink-muted); }
.nh-wip__steps .is-done i { color: var(--nh-success); }
.nh-wip__steps .is-now { font-weight: 600; color: var(--nh-ink); }
.nh-wip__steps .is-now i { color: var(--nh-primary); }
.nh-wip__steps .is-todo { color: var(--nh-ink-muted); }
.nh-wip__steps .is-todo i { color: var(--nh-line-strong); }

/* The line above the bar: what the bar is, and what it is of. An unlabelled
   track leaves the reader to guess whether it means the course or the upload,
   and for a long time it meant the wrong one of those two. */
.nh-wip__meter {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin-bottom: 7px;
}

.nh-wip__pct {
    font-size: 15px;
    font-weight: 700;
    color: var(--nh-ink);
}

.nh-wip__of {
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--nh-ink-muted);
}

.nh-wip__count {
    margin-left: auto;
    font-size: 12.5px;
    color: var(--nh-ink-muted);
}

.nh-wip__track {
    height: 6px;
    margin-bottom: 14px;
    border-radius: 4px;
    background: #EDEFF5;
    overflow: hidden;
}

.nh-wip__track i {
    display: block;
    height: 100%;
    border-radius: 4px;
    background: var(--nh-primary-deep);
}

.nh-wip__locked {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 0 0 14px;
    padding: 9px 11px;
    border-radius: var(--nh-radius-xs);
    background: var(--nh-accent-tint);
    color: #6b4d00;
    font-size: 12.5px;
    line-height: 1.5;
}

.nh-wip__locked i { margin-top: 2px; flex: none; }

.nh-wip__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-top: auto;
}

.nh-wip__status {
    color: var(--nh-ink-muted);
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
}

/* --- Earned certificate card ---------------------------------------------- */
.nh-ccard {
    display: flex;
    flex-direction: column;
    background: var(--nh-surface);
    border: 1px solid var(--nh-line);
    border-radius: 18px;
    overflow: hidden;
    transition: transform var(--nh-dur) var(--nh-ease), box-shadow var(--nh-dur) var(--nh-ease);
}

.nh-ccard:hover {
    transform: translateY(var(--nh-lift));
    box-shadow: var(--nh-shadow-raised);
}

.nh-ccard__preview {
    flex: none;
    padding: 18px;
    background: #EEF1F8;
}

/* THE REAL UPLOADED CERTIFICATE, on the card.

   Same frame as the drawn representation it replaces — same grey mount, same
   paper proportions, same border and shadow — so a grid holding both kinds of
   card reads as one grid rather than two designs side by side.

   BOTH ARE INERT. The PDF is an <object>, which is a live viewer: left alone it
   takes the pointer, so a click meant for the card lands in a PDF reader that
   scrolls inside a 118px box, and the keyboard can tab into it and get stuck.
   `pointer-events: none` hands every click back to the card, where View and
   Download already do the real work. The image gets the same treatment for the
   same reason: dragging a thumbnail out of a card is not a gesture this page
   wants to answer for. */
.nh-ccard__preview--real .nh-ccard__pdf,
.nh-ccard__preview--real .nh-ccard__img {
    display: block;
    width: 100%;
    aspect-ratio: 1.45 / 1;
    border: 1px solid #DDE2ED;
    border-radius: 6px;
    background: #fff;
    box-shadow: 0 2px 8px rgba(20, 26, 46, .07);
    overflow: hidden;
    pointer-events: none;
}

/* WHICH OF THE TWO PDF RENDERINGS IS ON SHOW.

   Not a question about the device any more. The card used to ship an <object>
   viewer and a drawn stand-in and pick between them by viewport width, because
   a phone cannot draw a PDF inline — so a learner on a phone got a generic
   drawing rather than their own certificate. The canvas that replaced the
   viewer is painted by pdf.js, which every device can run, so there is one
   rendering and it is the real one.

   What decides is therefore whether a page has actually been painted, and the
   script says so by moving the `hidden` attribute: the drawn certificate is
   what the card holds until then, and after it, only the canvas. Both states
   show exactly one certificate, and a browser that never runs the script keeps
   the drawn one rather than an empty frame. */
.nh-ccard__pdfcard { display: block; }

.nh-ccard__pdfcard[hidden],
.nh-ccard__preview--real .nh-ccard__pdf[hidden] {
    display: none;
}

/* contain, not cover: a certificate cropped to fill the frame loses the name
   and the title, which are the two things that make it recognisable as yours.
   Letterboxing against the white paper is the honest trade. */
.nh-ccard__preview--real .nh-ccard__img {
    object-fit: contain;
}

/* The canvas is a replaced element with the page's own proportions behind it,
   and the frame is a fixed 1.45:1 mount. contain rather than fill, for the same
   reason as the photograph above: a certificate stretched to the frame is a
   certificate with the wrong shape, and letterboxing against white paper is the
   honest trade. */
.nh-ccard__preview--real .nh-ccard__pdf {
    border-style: solid;
    object-fit: contain;
    background: #fff;
}

/* The drawn certificate, used where the file is one no browser can render —
   a Word document — and where there is no file at all. */
.nh-paper {
    position: relative;
    display: grid;
    place-items: center;
    width: 100%;
    aspect-ratio: 1.45 / 1;
    background: #fff;
    border: 1px solid #DDE2ED;
    border-radius: 6px;
    box-shadow: 0 2px 8px rgba(20, 26, 46, .07);
    overflow: hidden;
}

.nh-paper::before,
.nh-paper::after {
    content: "";
    position: absolute;
    border-radius: 3px;
}

.nh-paper::before { inset: 8px; border: 1px solid var(--nh-primary-deep); opacity: .26; }
.nh-paper::after { inset: 12px; border: 1px solid var(--nh-accent); opacity: .38; }

.nh-paper__in {
    position: relative;
    padding: 0 18px;
    text-align: center;
    /* The whole preview must never widen the card. */
    min-width: 0;
    max-width: 100%;
}

.nh-paper__in em {
    display: block;
    font-family: Georgia, 'Times New Roman', serif;
    font-style: normal;
    font-size: 13px;
    letter-spacing: .05em;
    color: var(--nh-primary-deep);
}

.nh-paper__in strong {
    display: block;
    margin: 2px 0 9px;
    font-family: Georgia, 'Times New Roman', serif;
    font-size: 10px;
    font-weight: 400;
    letter-spacing: .13em;
    text-transform: uppercase;
    color: var(--nh-ink-muted);
}

.nh-paper__name {
    margin-bottom: 7px;
    padding-bottom: 5px;
    border-bottom: 1px solid #E2E6EF;
    font-family: Georgia, 'Times New Roman', serif;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--nh-ink);
    /* A long name is truncated rather than wrapped: this is a drawing of a
       certificate, and two lines of name would break the proportions. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.nh-paper__ln {
    width: 76%;
    height: 3px;
    margin: 0 auto 4px;
    border-radius: 2px;
    background: #EDF0F6;
}

.nh-paper__ln.is-short { width: 50%; }

.nh-paper__seal {
    position: absolute;
    right: 15px;
    bottom: 13px;
    display: grid;
    place-items: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: radial-gradient(circle at 32% 28%, #F3E0AF, var(--nh-accent) 60%, #9C7418);
    color: #4C3706;
    font-size: 9px;
}

.nh-ccard__body {
    display: flex;
    flex-direction: column;
    flex: 1;
    /* One inset for everything in here, so the issuer, the name, the date and the
       buttons all sit on one margin and nothing touches the edge. */
    padding: 18px 20px 20px;
}

.nh-ccard__issuer {
    align-self: flex-start;
    max-width: 100%;
    margin-bottom: 10px;
    padding: 4px 9px;
    border-radius: 6px;
    background: var(--nh-primary-tint);
    color: var(--nh-primary-deep);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .07em;
    text-transform: uppercase;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.nh-ccard__name {
    margin: 0 0 8px;
    font-family: var(--nh-font-head);
    font-size: 15.5px;
    font-weight: 600;
    line-height: 1.35;
    letter-spacing: -.01em;
    color: var(--nh-ink);
    overflow-wrap: anywhere;
}

.nh-ccard__date {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0 0 16px;
    color: var(--nh-ink-muted);
    font-size: 13px;
}

.nh-ccard__date i { flex: none; font-size: 12px; }

.nh-ccard__actions {
    display: flex;
    gap: 8px;
    margin-top: auto;
}

/* --- Buttons -------------------------------------------------------------- */
.nh-cbtn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    flex: 1;
    /* 44px on a control that gets tapped, always. */
    min-height: 44px;
    padding: 10px 14px;
    border: 1px solid transparent;
    border-radius: 10px;
    font-family: inherit;
    font-size: 13.5px;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--nh-dur) var(--nh-ease), color var(--nh-dur) var(--nh-ease);
}

.nh-cbtn i { font-size: 13px; }

.nh-cbtn--quiet {
    background: var(--nh-surface-2);
    border-color: var(--nh-line);
    color: var(--nh-primary-deep);
}

.nh-cbtn--quiet:hover,
.nh-cbtn--quiet:focus { background: #E7EBF4; color: var(--nh-primary-deep); }

.nh-cbtn--solid {
    background: var(--nh-primary-deep);
    color: #fff;
}

.nh-cbtn--solid:hover,
.nh-cbtn--solid:focus { background: var(--nh-primary-darkest); color: #fff; }

/* In-progress cards have one button, sized to its label rather than stretched. */
.nh-wip__foot .nh-cbtn { flex: none; min-height: 40px; padding: 9px 16px; }

/* --- List layout ---------------------------------------------------------- */
/* The same cards laid on their side. One markup, one set of behaviour; only the
   flow direction changes. */
.is-list .nh-ccard,
.is-list .nh-wip { border-radius: 13px; }

.is-list .nh-ccard { flex-direction: row; align-items: stretch; }
.is-list .nh-ccard:hover { transform: none; box-shadow: var(--nh-shadow); }

.is-list .nh-ccard__preview {
    display: grid;
    place-items: center;
    width: 118px;
    flex: none;
    padding: 11px;
}

.is-list .nh-paper { aspect-ratio: 1.42 / 1; }
.is-list .nh-paper::before { inset: 5px; }
.is-list .nh-paper::after { inset: 7px; }
.is-list .nh-paper__in { padding: 0 7px; }
.is-list .nh-paper__in em { font-size: 8.5px; }
.is-list .nh-paper__in strong { margin: 0 0 4px; font-size: 6px; letter-spacing: .1em; }
.is-list .nh-paper__name { margin-bottom: 4px; padding-bottom: 3px; font-size: 8px; }
.is-list .nh-paper__ln { height: 2px; margin-bottom: 3px; }
.is-list .nh-paper__seal { right: 7px; bottom: 6px; width: 13px; height: 13px; font-size: 6px; }

.is-list .nh-ccard__body {
    flex-direction: row;
    align-items: center;
    gap: 18px;
    padding: 14px 18px;
}

.is-list .nh-ccard__issuer { align-self: center; flex: none; width: 132px; margin: 0; text-align: center; }
.is-list .nh-ccard__name { flex: 1; margin: 0; font-size: 15px; }
.is-list .nh-ccard__date { flex: none; width: 160px; margin: 0; }
.is-list .nh-ccard__actions { flex: none; width: 210px; margin: 0; }

.is-list .nh-wip {
    flex-direction: row;
    align-items: center;
    gap: 18px;
    padding: 14px 18px;
}

.is-list .nh-wip__top { flex: 1; min-width: 0; margin: 0; }
/* The step list is the tile layout's detail; a row has no height for it. */
.is-list .nh-wip__steps,
.is-list .nh-wip__locked { display: none; }
.is-list .nh-wip__meter { flex: none; margin: 0; }
.is-list .nh-wip__of,
.is-list .nh-wip__count { display: none; }
.is-list .nh-wip__track { flex: none; width: 130px; margin: 0; }
.is-list .nh-wip__foot { flex: none; width: 210px; margin: 0; justify-content: flex-end; }

/* --- Empty states --------------------------------------------------------- */
.nh-certs__empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 54px 26px;
    background: var(--nh-surface);
    border: 1px solid var(--nh-line);
    border-radius: 18px;
    text-align: center;
}

.nh-certs__emptyicon {
    display: grid;
    place-items: center;
    width: 56px;
    height: 56px;
    margin-bottom: 16px;
    border-radius: 50%;
    background: var(--nh-primary-tint);
    color: var(--nh-primary-deep);
    font-size: 23px;
}

.nh-certs__emptytitle {
    margin: 0 0 6px;
    font-family: var(--nh-font-head);
    font-size: 18px;
    font-weight: 600;
    color: var(--nh-ink);
}

.nh-certs__emptybody {
    margin: 0 0 20px;
    max-width: 40ch;
    color: var(--nh-ink-soft);
    font-size: 14px;
    line-height: 1.6;
}

.nh-certs__empty .nh-cbtn { flex: none; padding: 11px 22px; }

/* --- Moodle's own page furniture on this page ----------------------------- */
/* The page sits in a user context, so core's context header renders the viewer's
   profile card: avatar, name, and the Message and Add-to-contacts buttons. That
   is a page about a person, and this is a page about their certificates — it
   duplicated the title and offered actions belonging somewhere else entirely.
   The page supplies its own heading. */
body.nzeru-certificates #page-header,
body.nzeru-certificates .page-context-header,
body.nzeru-certificates #region-main-settings-menu {
    display: none;
}

body.nzeru-certificates #region-main {
    border: 0;
    background: transparent;
    box-shadow: none;
}

/* --- Tablets and phones --------------------------------------------------- */
@media (max-width: 1000px) {
    /* Icon-only layout buttons: the labels are the first thing that can go. */
    .nh-cview span { display: none; }
    .nh-cview { padding: 8px 11px; }
}

@media (max-width: 820px) {
    /* List rows wrap rather than squeezing four columns into a tablet. */
    .is-list .nh-ccard__body { flex-wrap: wrap; gap: 8px 14px; }
    .is-list .nh-ccard__issuer { width: auto; }
    .is-list .nh-ccard__name { flex: 1 1 100%; order: -1; }
    .is-list .nh-ccard__date { width: auto; }
    .is-list .nh-ccard__actions { width: 100%; margin-top: 4px; }

    .is-list .nh-wip { flex-wrap: wrap; }
    .is-list .nh-wip__top { flex: 1 1 100%; }
    .is-list .nh-wip__track { flex: 1; width: auto; }
    .is-list .nh-wip__foot { width: auto; flex: none; }
}

@media (max-width: 700px) {
    /* Search takes the whole line: sharing a row with the chips leaves both too
       narrow to use. */
    .nh-certs__tools { margin-left: 0; width: 100%; }
    .nh-certs__search { flex: 1 1 100%; }
    .nh-certs__search input { width: 100%; }

    /* The two filters share the next line rather than each taking one:
       side by side they are still wide enough to read the option showing,
       and stacked they push the certificates themselves off the screen. */
    .nh-certs__select { flex: 1 1 9rem; }
    .nh-certs__select select { width: 100%; max-width: none; }
}

@media (max-width: 600px) {
    .nh-certs { padding-bottom: 32px; }

    .nh-certs__headrow { margin-bottom: 20px; }

    /* The count box goes full width under the title rather than being squeezed
       beside it. */
    .nh-certs__count { width: 100%; justify-content: space-around; }

    .nh-wip { padding: 16px; }
    .nh-ccard__body { padding: 16px; }

    /* Stacked and full width: the only shape that stays a comfortable target when
       there are two buttons on a 320px card. */
    .nh-ccard__actions { flex-direction: column; }
    .nh-ccard__actions .nh-cbtn { width: 100%; }
}

@media (max-width: 520px) {
    /* A list row becomes a stacked card again — below this width there is no
       sideways room to lay anything out. */
    .is-list .nh-ccard { flex-direction: column; }
    .is-list .nh-ccard__preview { width: 100%; padding: 14px; }
    .is-list .nh-paper__in em { font-size: 12px; }
    .is-list .nh-paper__in strong { font-size: 9px; }
    .is-list .nh-paper__name { font-size: 11px; }
    .is-list .nh-paper__seal { width: 19px; height: 19px; font-size: 8px; }
    .is-list .nh-ccard__body { flex-direction: column; align-items: stretch; }
    .is-list .nh-ccard__issuer { align-self: flex-start; text-align: start; }
    .is-list .nh-ccard__actions { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
    .nh-ccard:hover { transform: none; }
}

/* ============================================================================
   My certificates: the enclosing panel

   Everything on the page now sits inside .nh-certs__panel, which is white. The
   cards inside it were white too, so each one lost its edge against the surface
   it sits on — a card is only a card if you can see where it stops.
   ========================================================================== */

/* The cards step down one tone, and keep their hairline. Nearly white, so the
   design is unchanged in character; distinct enough that a row of them reads as
   separate objects rather than as one block of text. */
.nh-certs__panel .nh-wip,
.nh-certs__panel .nh-ccard {
    background: var(--nh-surface-2);
}

/* Hover lifts a card to pure white — brighter than its surroundings rather than
   the same as them, which is what makes the movement read as "this one". */
.nh-certs__panel .nh-ccard:hover {
    background: var(--nh-surface);
}

/* Quiet buttons were tinted against a white card. Inside a tinted card they need
   to be the lighter of the two, or the button disappears into the card. */
.nh-certs__panel .nh-cbtn--quiet {
    background: var(--nh-surface);
}

.nh-certs__panel .nh-cbtn--quiet:hover,
.nh-certs__panel .nh-cbtn--quiet:focus {
    background: var(--nh-primary-tint);
}

/* Same for the chip row, the search field and the layout switch: they sit
   directly on the panel, so white-on-white would leave them shapeless. */
/* :not(.is-active) IS THE WHOLE POINT OF THESE SELECTORS.
   Without it this block sat later in the file at the same specificity as
   `.nh-cchip.is-active`, so it repainted the selected chip near-white while the
   active rule kept the text white — which is why "All" rendered as an empty pill.
   The hover rule below did the same to the selected layout button, at higher
   specificity, so pointing at Tiles or List made the label vanish. */
.nh-certs__panel .nh-cchip:not(.is-active),
.nh-certs__panel .nh-certs__search input,
.nh-certs__panel .nh-certs__views,
.nh-certs__panel .nh-certs__count {
    background: var(--nh-surface-2);
}

.nh-certs__panel .nh-cview:not(.is-active):hover {
    background: var(--nh-surface);
}

/* Restated inside the panel so the selected state can never be outranked again,
   whatever else is added to this section later. */
.nh-certs__panel .nh-cchip.is-active {
    background: var(--nh-primary-deep);
    border-color: var(--nh-primary-deep);
    color: #fff;
}

.nh-certs__panel .nh-cview.is-active,
.nh-certs__panel .nh-cview.is-active:hover {
    background: var(--nh-primary-deep);
    color: #fff;
}

/* The two empty states are already full-width panels of their own; inside this
   one they only need the tone, not a second border. */
.nh-certs__panel .nh-certs__empty {
    background: var(--nh-surface-2);
}

/* The last section in the panel should not add padding below the panel's own. */
.nh-certs__panel .nh-certs__section:last-child {
    padding-bottom: 0;
}

/* --- The panel on smaller screens ----------------------------------------- */
/* The inset tightens; it never goes away. Text against the edge of a panel is
   worse on a phone than on a laptop, because there is no page margin left
   outside it to stand in for the padding. */
@media (max-width: 700px) {
    .nh-certs__panel {
        padding: 20px;
        border-radius: var(--nh-radius-lg);
    }
}

@media (max-width: 480px) {
    .nh-certs__panel {
        padding: 14px;
    }
}

/* ============================================================================
   My certificates: the panel must not change width with the layout

   WHY IT DID. In the list layout a row is a flex container with fixed-width
   children — the issuer chip, the date, the actions — and the course name set to
   `flex: 1`. A flex item's default `min-width: auto` refuses to shrink below its
   own content, so a long course name made the row wider than its grid column, the
   column pushed the grid, and the grid pushed the panel. Switching back to tiles
   released it, because `minmax(min(100%, 285px), 1fr)` cannot be pushed. The panel
   therefore appeared to resize with the layout when what it was really doing was
   being stretched by one long name.

   This is the same fault as the activity rows on the course page, and the same
   fix: whatever holds text and is allowed to grow must also be allowed to shrink.
   ========================================================================== */

/* Every grid child, in both layouts. Without this a card can never be narrower
   than its own content, whatever the column says. */
.nh-certs__grid > * {
    min-width: 0;
    max-width: 100%;
}

/* The list row and everything in it that carries text. */
.is-list .nh-ccard__body,
.is-list .nh-wip {
    min-width: 0;
}

.is-list .nh-ccard__name,
.is-list .nh-ccard__issuer,
.is-list .nh-ccard__date,
.is-list .nh-wip__top,
.is-list .nh-wip__id {
    min-width: 0;
}

/* The name is the one that grows, so it is the one that has to be told where to
   stop. One line with an ellipsis in the list layout: a row is a row, and a name
   that wrapped to three lines would make this row taller than its neighbours. */
.is-list .nh-ccard__name,
.is-list .nh-wip__name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The date sits in a fixed column and can wrap awkwardly at the boundary; keep it
   on one line and let the column give way instead. */
.is-list .nh-ccard__date { white-space: nowrap; }

/* ============================================================================
   Small screens

   The tiers above cover 1000 / 820 / 700 / 600 / 520. What follows is the bottom
   end — a 360px phone and narrower, where the fixed paddings in the header start
   to cost more than they earn.
   ========================================================================== */
@media (max-width: 400px) {
    .nh-certs__panel { padding: 12px; }

    .nh-certs__title { font-size: 22px; }

    /* The count box: two numbers on a 320px screen have no room for 20px of
       padding each side of each one. */
    .nh-certs__count > div { padding: 0 12px; }
    .nh-certs__count b { font-size: 19px; }

    /* Chips take a full line each rather than wrapping mid-row, which is what
       keeps them a full-width tap target instead of three stubs. */
    .nh-certs__chips { width: 100%; }
    .nh-cchip { flex: 1 1 auto; justify-content: center; }

    .nh-wip { padding: 14px; }
    .nh-wip__foot { flex-wrap: wrap; }
    .nh-wip__foot .nh-cbtn { flex: 1 1 100%; }

    .nh-ccard__body { padding: 14px; }

    /* The certificate drawing loses its inner rules at this size — at ~90px wide
       they are three lines of border around almost nothing. */
    .nh-paper::after { display: none; }
}

/* Nothing on this page may cause the page itself to scroll sideways. A card that
   overflows is a bug to fix at the card, which is what the rules above do; this is
   the backstop that keeps one from taking the whole layout with it. */
/* .nh-certs is deliberately NOT in this list. It carries the page's centred
   max-width (see the top of this section), and capping it at 100% here — later in
   the file, same specificity — would win on source order and put the page back to
   the full width of the screen. These two are meant to fill whatever it gives
   them. */
.nh-certs__panel,
.nh-certs__grid {
    max-width: 100%;
}

.nh-certs__bar,
.nh-certs__headrow {
    min-width: 0;
}

/* ============================================================================
   My certificates: reserve the scrollbar

   THE OTHER HALF OF "the panel changes size when I switch layout", and at a
   desktop width it is the whole of it.

   The two layouts are different heights for the same certificates. On a tall
   monitor the tile grid can fit without the page scrolling while the list, being
   one row per certificate, cannot — so switching adds or removes the vertical
   scrollbar, the viewport loses or regains ~15px, and every centred, width-limited
   thing on the page moves with it. The panel was not resizing; the window was.

   `scrollbar-gutter: stable` keeps the gutter reserved whether or not there is a
   bar in it, so the layout width is the same in both. Scoped to this page rather
   than set on every page of the site, because it costs that ~15px of usable width
   permanently and only a page with a layout switch on it has earned that trade.
   ========================================================================== */
html:has(body.nzeru-certificates) {
    scrollbar-gutter: stable;
}

/* ============================================================================
   My certificates: the list layout on a phone

   Below ~600px the sideways row cannot work. Its four columns — issuer, name,
   date, actions — need about 700px between them before anything is squeezed, so
   on a phone the honest thing is to stop pretending and let List fall back to the
   same stacked shape Tiles uses.

   Previously only the earned card did this, and only under 520px; the in-progress
   card kept its row layout all the way down, which is what left the cards looking
   broken in List on a phone.
   ========================================================================== */
@media (max-width: 600px) {

    /* --- Earned card ------------------------------------------------------- */
    .nh-certs__grid.is-list .nh-ccard {
        flex-direction: column;
    }

    .nh-certs__grid.is-list .nh-ccard__preview {
        width: 100%;
        padding: 14px;
    }

    .nh-certs__grid.is-list .nh-ccard__body {
        flex-direction: column;
        align-items: stretch;
        gap: 10px;
        padding: 16px;
    }

    .nh-certs__grid.is-list .nh-ccard__issuer {
        align-self: flex-start;
        width: auto;
        margin: 0;
        text-align: start;
    }

    /* The single ellipsised line is right for a wide row and wrong here: a stacked
       card has the height for the whole name, and a truncated certificate title is
       the one thing this page must not do. */
    .nh-certs__grid.is-list .nh-ccard__name,
    .nh-certs__grid.is-list .nh-wip__name {
        flex: none;
        overflow: visible;
        white-space: normal;
        text-overflow: clip;
    }

    .nh-certs__grid.is-list .nh-ccard__date {
        width: auto;
        white-space: normal;
    }

    .nh-certs__grid.is-list .nh-ccard__actions {
        width: 100%;
        flex-direction: column;
    }

    /* --- In-progress card -------------------------------------------------- */
    .nh-certs__grid.is-list .nh-wip {
        flex-direction: column;
        align-items: stretch;
        gap: 12px;
        padding: 16px;
    }

    .nh-certs__grid.is-list .nh-wip__top {
        flex: none;
        margin: 0;
    }

    /* The steps and the restriction notice are hidden in the row layout because a
       row has no height for them. Stacked, there is room, and they are the whole
       point of the card — they say what to do next. */
    .nh-certs__grid.is-list .nh-wip__steps {
        display: grid;
        margin: 0;
    }

    .nh-certs__grid.is-list .nh-wip__locked {
        display: flex;
        margin: 0;
    }

    .nh-certs__grid.is-list .nh-wip__meter {
        margin-bottom: 7px;
    }

    .nh-certs__grid.is-list .nh-wip__of,
    .nh-certs__grid.is-list .nh-wip__count {
        display: inline;
    }

    .nh-certs__grid.is-list .nh-wip__track {
        flex: none;
        width: auto;
    }

    .nh-certs__grid.is-list .nh-wip__foot {
        width: auto;
        justify-content: space-between;
    }
}

/* ============================================================================
   The "View" button on a completed course

   A course at 100% used to carry the same solid blue button as one still in
   progress, so a finished row and an unfinished row looked identical apart from
   their label — and blue is this product's "do this next", which a completed
   course is not.

   Green, because completion is already green everywhere else here: the progress
   meter, the Done chip on an activity, the "Completed" tab. Tinted rather than
   solid so it sits quieter than the primary action while still being clearly a
   button, and so a grid holding both kinds has one obvious lead.
   ========================================================================== */
.nh-btn--done {
    background: var(--nh-success-tint);
    color: #12693b;
    /* The border does the work the fill used to: without it a tinted button on a
       tinted card loses its edge. */
    border: 1px solid #bfe3cf;
    font-weight: 600;
}

.nh-btn--done:hover,
.nh-btn--done:focus {
    background: #d7efe1;
    border-color: #a5d8bb;
    color: #0e5730;
}

/* The eye sits with the label rather than after it, unlike the arrow it replaces:
   an arrow points at where you are going and belongs at the end, an icon that says
   what the action IS belongs in front of the word. */
.nh-btn--done i {
    font-size: .95em;
}

/* ============================================================ Blog: listing extras.
 *
 * The card itself is defined further up, under "Blog cards"; these are the
 * pieces the listing grew when it stopped showing three invented posts and
 * started showing the site's real ones — a cover that can be a photograph, an
 * editor-only flag, a section filter, a pager and an empty state.
 */

/* The cover-with-a-picture, the editor's "scheduled" flag, the section pills
   and the edit bar now live with the rest of the editorial layout at the end
   of this file, so the blog's look is described in one place. */

/* Pager. *//* Pager. */
.nh-pager {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 44px;
}
.nh-pager__step,
.nh-pager__num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-width: 40px;
    height: 40px;
    padding: 0 14px;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius);
    background: #fff;
    color: var(--nh-ink-soft);
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
    transition: border-color .15s ease, color .15s ease, background .15s ease;
}
.nh-pager__step:hover,
.nh-pager__num:hover { border-color: var(--nh-primary); color: var(--nh-primary); text-decoration: none; }
.nh-pager__num.is-active {
    background: var(--nh-primary-deep);
    border-color: transparent;
    color: #fff;
}
.nh-pager__pages { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.nh-pager__gap { color: var(--nh-ink-muted); padding: 0 2px; }

/* Empty state. A blank page reads as a broken one. */
.nh-empty {
    text-align: center;
    padding: 64px 24px;
    border: 1px dashed var(--nh-line);
    border-radius: var(--nh-radius-lg);
    background: var(--nh-paper, #f8fafc);
}
.nh-empty__icon { font-size: 34px; color: var(--nh-ink-muted); margin-bottom: 18px; display: block; }
.nh-empty__title { font-family: var(--nh-font-head); font-size: 22px; margin: 0 0 10px; }
.nh-empty__body { color: var(--nh-ink-soft); margin: 0 0 22px; }

/* The older single-column article styles lived here. They were replaced by
   the two-column sheet at the end of this file when the article page gained
   the blog's header and sidebar. */

/* ============================================================================
 * BLOG: the editorial front page.
 *
 * The site's dark page header (nh-pagehead, shared with FAQ and Contact and
 * with every article read from here), then a lead story and a river of cards
 * beside a sidebar of search, recent stories and a subscribe card.
 *
 * The body sits on the site's paper tone rather than white, so the white cards
 * read as cards rather than as areas of the page that happen to have a border.
 * ========================================================================= */

.nh-page--blog,
.nh-page--article {
    background: var(--nh-paper, #f4f6fa);
}
/* The gap under the "News, insights & learner stories" band, and the gap above
   the footer. 88px at the bottom was close to a screenful on a laptop: the last
   post scrolled away and the footer had not arrived yet, so the page looked
   like it had run out rather than ended. Both sides scale with the viewport,
   which is also what removes the need for the old phone-only override to pull
   them back down again. */
.nh-blogbody {
    padding-top: clamp(20px, 2.6vw, 28px);
    padding-bottom: clamp(28px, 4vw, 44px);
}

/* The header runs to the full width of the band, so its bottom edge meets the
   paper cleanly rather than through the page's side gutters. */
.nh-pagehead--blog { margin-bottom: 0; }

/* ------------------------------------------------------------ Two columns. */
.nh-bloglayout {
    display: grid;
    /* minmax(0, 1fr) rather than 1fr: a grid track defaults to min-content
       width, so one long unbroken title would otherwise push the column wider
       than the page instead of wrapping. */
    grid-template-columns: minmax(0, 1fr) 320px;
    gap: 40px;
    align-items: start;
}
@media (max-width: 1080px) {
    .nh-bloglayout { grid-template-columns: minmax(0, 1fr); gap: 40px; }
}

/* While a search is in flight. Dimmed rather than emptied, so the reader keeps
   something to look at and the page does not jump. */
.nh-bloglayout__main { transition: opacity .15s ease; }
.nh-bloglayout__main.is-loading { opacity: .45; pointer-events: none; }

.nh-editbar { margin: 0 0 20px; }

/* ------------------------------------------------------ Shared card shell. */
.nh-post {
    display: flex;
    flex-direction: column;
    background: #fff;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-lg);
    overflow: hidden;
    transition: box-shadow .2s ease, transform .2s ease;
}
.nh-post:hover { box-shadow: var(--nh-shadow-lift); transform: translateY(-2px); }

/*
 * The cover.
 *
 * Two states from one rule: a photograph when the post has one, and otherwise a
 * flat colour picked from the post id (see blog::cover_colour) with a dot grid
 * over it. The dots are a CSS gradient rather than an image so they cost nothing
 * and take the colour of whatever they sit on.
 */
.nh-post__cover {
    position: relative;
    display: block;
    text-decoration: none;
    background-color: var(--nh-cover, #1d3f6e);
    background-image:
        radial-gradient(rgba(255, 255, 255, .16) 1px, transparent 1px);
    background-size: 14px 14px;
}
/* With a photograph the dot grid would sit on top of it, so the whole
   background shorthand is replaced by the inline background-image. */
.nh-post__cover--img {
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    /* Behind a transparent PNG and while a slow connection is still fetching. */
    background-color: #0c2d57;
}
/* An aspect ratio, not a fixed height: a card cover is the widest thing in its
   column and a fixed height would crop differently at every width. */
.nh-post__cover { aspect-ratio: 16 / 9; }

/* The read-time pill, top-left on the cover. */
.nh-post__read {
    position: absolute;
    top: 12px;
    left: 12px;
    padding: 4px 10px;
    border-radius: 999px;
    background: rgba(8, 22, 42, .72);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .02em;
    /* Keeps the pill legible over a light photograph as well as a dark one. */
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}

.nh-post__body { padding: 20px 20px 16px; display: flex; flex-direction: column; flex: 1; }

.nh-post__title {
    font-family: var(--nh-font-head);
    font-size: 17px;
    line-height: 1.32;
    font-weight: 700;
    margin: 0 0 10px;
}
.nh-post__title a { color: var(--nh-ink); text-decoration: none; }
.nh-post__title a:hover { color: var(--nh-primary); }

.nh-post__excerpt {
    font-size: 14px;
    line-height: 1.6;
    color: var(--nh-ink-soft);
    margin: 0 0 18px;
    /* flex:1 pushes the byline to the bottom, so bylines line up across a row
       however long the excerpts above them are. */
    flex: 1;
}

/* Date and read time above the headline. */
.nh-postmeta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    margin: 0;
    font-size: 12px;
    color: var(--nh-ink-muted);
}
.nh-postmeta--top { margin-bottom: 10px; }
/* Dots between the parts, drawn rather than typed so they never end up at the
   start of a wrapped line or after a missing value. */
.nh-postmeta > * + *::before {
    content: '·';
    margin-right: 10px;
    color: var(--nh-line);
}

/* The byline strip at the foot of a card: who wrote it, and the way in. */
.nh-byline {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-top: auto;
    padding-top: 14px;
    border-top: 1px solid var(--nh-line);
}
.nh-byline__who { display: inline-flex; align-items: center; gap: 9px; min-width: 0; }
.nh-byline__avatar {
    flex: none;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--nh-primary-deep, #0c2d57);
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .02em;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.nh-byline__name {
    font-size: 13px;
    font-weight: 600;
    color: var(--nh-ink-soft);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.nh-byline__more {
    flex: none;
    font-size: 13px;
    font-weight: 700;
    color: var(--nh-primary);
    text-decoration: none;
}
.nh-byline__more span { display: inline-block; transition: transform .2s ease; }
.nh-byline__more:hover span { transform: translateX(3px); }

/* Only ever rendered for somebody who can manage the blog. */
.nh-post__flag {
    display: inline-block;
    margin: 0 0 10px;
    padding: 3px 10px;
    border-radius: 999px;
    background: #fdf3d8;
    color: #7a5a09;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
}

/* ------------------------------------------------------------- Toolbar. */
.nh-blogbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin: 36px 0 18px;
}
.nh-blogbar__label { display: flex; align-items: baseline; gap: 12px; margin: 0; flex-wrap: wrap; }
.nh-blogbar__title {
    font-family: var(--nh-font-head);
    font-size: 17px;
    font-weight: 700;
    color: var(--nh-ink);
}
.nh-blogbar__count { font-size: 13px; color: var(--nh-ink-muted); }

/* The order menu. A <details>, so the open/closed state, the keyboard handling
   and the accessible name all come from the browser. */
.nh-sort { position: relative; }
.nh-sort__button {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 8px 14px;
    border: 1px solid var(--nh-line);
    border-radius: 999px;
    background: #fff;
    font-size: 13px;
    font-weight: 600;
    color: var(--nh-ink-soft);
    cursor: pointer;
    /* The default disclosure triangle is replaced by the chevron in the markup,
       which can be rotated and matches the rest of the page. */
    list-style: none;
    white-space: nowrap;
}
.nh-sort__button::-webkit-details-marker { display: none; }
.nh-sort__button:hover { border-color: var(--nh-primary); color: var(--nh-primary); }
.nh-sort__button:focus-visible { outline: none; box-shadow: var(--nh-focus); }
.nh-sort__button i { font-size: 11px; transition: transform .2s ease; }
.nh-sort[open] .nh-sort__button i { transform: rotate(180deg); }
.nh-sort__menu {
    position: absolute;
    z-index: 20;
    top: calc(100% + 6px);
    right: 0;
    min-width: 180px;
    margin: 0;
    padding: 6px;
    list-style: none;
    background: #fff;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius);
    box-shadow: var(--nh-shadow-lift, 0 18px 40px -24px rgba(11, 35, 71, .35));
}
.nh-sort__menu a {
    display: block;
    padding: 9px 12px;
    border-radius: calc(var(--nh-radius-xs, 8px) - 2px);
    font-size: 13px;
    font-weight: 600;
    color: var(--nh-ink-soft);
    text-decoration: none;
    white-space: nowrap;
}
.nh-sort__menu a:hover { background: var(--nh-paper, #f4f6fa); color: var(--nh-primary); }
.nh-sort__menu a.is-active { color: var(--nh-primary); }

/*
 * The river of cards beside the sidebar.
 *
 * Two columns, not three. The page container is 1240px and the sidebar takes
 * 320 of it, which leaves about 840 for the river: three columns there would be
 * ~260px each, too narrow for a headline, an excerpt and a byline without
 * everything wrapping to three lines. Two gives each card ~400px.
 *
 * Below 1080px the sidebar drops beneath the posts and the river gets the whole
 * width back, so the three-across look appears there, where it fits.
 */
.nh-blogriver {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 22px;
    align-items: stretch;
}
@media (max-width: 1080px) {
    .nh-blogriver { grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); }
}
@media (max-width: 600px) {
    .nh-blogriver { grid-template-columns: minmax(0, 1fr); }
}
.nh-blogriver .nh-post { height: 100%; }

/* ------------------------------------------------------ Range and pager. */
.nh-showing {
    margin: 26px 0 0;
    text-align: center;
    font-size: 13px;
    color: var(--nh-ink-muted);
}
.nh-pager {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 16px;
}
.nh-pager__step,
.nh-pager__num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 38px;
    height: 38px;
    padding: 0 12px;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius);
    background: #fff;
    color: var(--nh-ink-soft);
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
    transition: border-color .15s ease, color .15s ease, background .15s ease;
}
.nh-pager__step:hover,
.nh-pager__num:hover { border-color: var(--nh-primary); color: var(--nh-primary); text-decoration: none; }
.nh-pager__num.is-active { background: var(--nh-primary-deep); border-color: transparent; color: #fff; }
.nh-pager__pages { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.nh-pager__gap { color: var(--nh-ink-muted); padding: 0 2px; }

/* --------------------------------------------------------- Empty state. */
.nh-empty {
    text-align: center;
    padding: 64px 24px;
    border: 1px dashed var(--nh-line);
    border-radius: var(--nh-radius-lg);
    background: #fff;
}
.nh-empty__icon { font-size: 34px; color: var(--nh-ink-muted); margin-bottom: 18px; display: block; }
.nh-empty__title { font-family: var(--nh-font-head); font-size: 22px; margin: 0 0 10px; }
.nh-empty__body { color: var(--nh-ink-soft); margin: 0 0 22px; }

/* ------------------------------------------------------------- Sidebar. */
.nh-blogside {
    display: flex;
    flex-direction: column;
    gap: 20px;
    /* Follows the reader down a long river of cards. Only where there is room:
       on one column it is below the posts and stickiness would be meaningless. */
    position: sticky;
    top: 96px;
}
@media (max-width: 1080px) {
    .nh-blogside { position: static; top: auto; }
}
.nh-blogside__card {
    background: #fff;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-lg);
    padding: 18px;
}
.nh-blogside__title {
    font-family: var(--nh-font-head);
    font-size: 15px;
    font-weight: 700;
    color: var(--nh-ink);
    margin: 0 0 14px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--nh-line);
}

/* Search. */
.nh-blogsearch { display: flex; align-items: center; gap: 10px; }
.nh-blogsearch__field { position: relative; display: block; flex: 1; min-width: 0; margin: 0; }
.nh-blogsearch__icon {
    position: absolute;
    left: 14px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--nh-ink-muted);
    font-size: 13px;
    pointer-events: none;
}
.nh-blogsearch input[type="search"] {
    width: 100%;
    /* Left padding clears the icon. */
    padding: 11px 14px 11px 38px;
    border: 1px solid var(--nh-line);
    border-radius: 999px;
    background: var(--nh-paper, #f4f6fa);
    font-family: inherit;
    font-size: 14px;
    color: var(--nh-ink);
    transition: border-color .15s ease, box-shadow .15s ease, background .15s ease;
    -webkit-appearance: none;
    appearance: none;
}
.nh-blogsearch input[type="search"]:focus {
    outline: none;
    background: #fff;
    border-color: var(--nh-primary);
    box-shadow: var(--nh-focus);
}
/* Recent stories: a thumbnail beside a title, no excerpt. The sidebar is a list
   of places to go next, and at this width a summary is a paragraph nobody reads. */
.nh-minilist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.nh-mini { display: flex; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--nh-line); }
.nh-mini:first-child { padding-top: 0; }
.nh-mini:last-child { padding-bottom: 0; border-bottom: 0; }
.nh-mini__cover {
    flex: none;
    width: 52px;
    height: 46px;
    border-radius: var(--nh-radius-xs, 8px);
    text-decoration: none;
    background-color: var(--nh-cover, #1d3f6e);
    background-image: radial-gradient(rgba(255, 255, 255, .16) 1px, transparent 1px);
    background-size: 10px 10px;
}
.nh-mini__cover--img { background-size: cover; background-position: center; background-repeat: no-repeat; }
.nh-mini__body { min-width: 0; }
.nh-mini__title {
    display: block;
    font-family: var(--nh-font-head);
    font-size: 13.5px;
    line-height: 1.35;
    font-weight: 600;
    color: var(--nh-ink);
    text-decoration: none;
    margin-bottom: 5px;
}
.nh-mini__title:hover { color: var(--nh-primary); }
.nh-mini__meta { display: flex; gap: 8px; margin: 0; font-size: 11.5px; color: var(--nh-ink-muted); }
.nh-mini__meta > * + *::before { content: '·'; margin-right: 8px; color: var(--nh-line); }

/* Subscribe: the one dark card, because it is the one thing on the page asking
   for something rather than offering it. */
.nh-subscribe {
    position: relative;
    border: 0;
    overflow: hidden;
    background:
        radial-gradient(420px 220px at 88% -30%, rgba(201, 161, 74, .32), transparent 60%),
        linear-gradient(150deg, #0c2d57 0%, #123a5c 100%);
    color: #fff;
    padding: 20px;
}
.nh-subscribe__title {
    font-family: var(--nh-font-head);
    font-size: 16px;
    font-weight: 700;
    margin: 0 0 6px;
    color: #fff;
}
.nh-subscribe__lede {
    margin: 0 0 14px;
    font-size: 13px;
    line-height: 1.5;
    color: rgba(232, 238, 248, .82);
}
.nh-subscribe__form { display: flex; flex-direction: column; gap: 10px; }
.nh-subscribe__form input[type="email"] {
    width: 100%;
    padding: 11px 14px;
    border: 1px solid rgba(255, 255, 255, .22);
    border-radius: var(--nh-radius);
    background: rgba(255, 255, 255, .08);
    font-family: inherit;
    font-size: 14px;
    color: #fff;
}
.nh-subscribe__form input[type="email"]::placeholder { color: rgba(232, 238, 248, .5); }
.nh-subscribe__form input[type="email"]:focus {
    outline: none;
    border-color: var(--nh-accent, #f0c14b);
    background: rgba(255, 255, 255, .14);
}
.nh-subscribe__btn {
    align-self: flex-start;
    border: 0;
    padding: 10px 20px;
    border-radius: var(--nh-radius);
    background: var(--nh-accent, #f0c14b);
    color: #16233a;
    font-family: inherit;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
    transition: filter .15s ease;
}
.nh-subscribe__btn:hover { filter: brightness(1.07); }
.nh-subscribe__note {
    margin: 12px 0 0;
    font-size: 11.5px;
    line-height: 1.5;
    color: rgba(232, 238, 248, .62);
}

/* ============================================================================
 * BLOG: one article.
 *
 * Same header band, same two columns, same sidebar as the listing. The reading
 * column is a white sheet on the page's paper, with the post's cover across the
 * top of it — the same colour and dot grid its card carried on the listing, so
 * a reader arriving from there recognises where they landed.
 * ========================================================================= */

/* The compact header. On an article the band shrinks: the eyebrow and tagline
   are the blog introducing itself, which competes with the headline below. */
.nh-pagehead--compact { padding-top: 34px; padding-bottom: 34px; }
.nh-pagehead__title--sm {
    font-family: var(--nh-font-head);
    font-size: clamp(20px, 2.2vw, 28px);
    line-height: 1.2;
    font-weight: 700;
    color: #fff;
    margin: 0;
    max-width: 30ch;
}
@media (max-width: 640px) {
    .nh-pagehead--compact { padding-top: 24px; padding-bottom: 24px; }
}

.nh-backlink { margin: 0 0 18px; font-size: 14px; font-weight: 600; }
.nh-backlink a { color: var(--nh-ink-soft); text-decoration: none; }
.nh-backlink a:hover { color: var(--nh-primary); }

/* Editor-only banners: why the public cannot see this post. */
.nh-notice {
    margin: 0 0 16px;
    padding: 12px 16px;
    border-radius: var(--nh-radius);
    border: 1px solid var(--nh-line);
    background: #fff;
    color: var(--nh-ink-soft);
    font-size: 14px;
    font-weight: 600;
}
.nh-notice--warn { background: #fdf3d8; border-color: #efd9a0; color: #7a5a09; }

/* ------------------------------------------------------------- The sheet. */
.nh-article {
    background: #fff;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-lg);
    overflow: hidden;
}
.nh-article__cover {
    /* Same treatment as a card cover, at the width of the sheet. */
    aspect-ratio: 16 / 6;
    background-color: var(--nh-cover, #1d3f6e);
    background-image: radial-gradient(rgba(255, 255, 255, .16) 1px, transparent 1px);
    background-size: 14px 14px;
}
/* The article's own picture, shown whole.

   No aspect-ratio and no cropping: the height follows the image, so a portrait
   photograph is a tall band and a wide one is a thin band, and neither loses an
   edge. max-height is the only limit — past it the picture is scaled down and
   centred against the cover colour rather than trimmed, because a cover tall
   enough to fill the screen pushes the headline off it.

   The listing cards still crop, deliberately: a grid of covers has to be a grid
   of one shape, and there the picture is a thumbnail rather than the article. */
.nh-article__img {
    display: block;
    width: 100%;
    height: auto;
    max-height: 560px;
    object-fit: contain;
    background-color: #0c2d57;
}
.nh-article__inner { padding: 30px 36px 40px; }
@media (max-width: 640px) {
    .nh-article { border-radius: var(--nh-radius); }
    .nh-article__inner { padding: 24px 20px 30px; }
    .nh-article__cover { aspect-ratio: 16 / 9; }
}

.nh-article__meta { margin: 0 0 10px; font-size: 12px; }
.nh-article__cat {
    text-transform: uppercase;
    letter-spacing: .1em;
    font-weight: 700;
    font-size: 11px;
    color: var(--nh-primary);
    text-decoration: none;
}
.nh-article__cat:hover { text-decoration: underline; }

.nh-article__title {
    font-family: var(--nh-font-head);
    font-size: clamp(26px, 3.2vw, 36px);
    line-height: 1.16;
    font-weight: 700;
    letter-spacing: -.01em;
    margin: 0 0 20px;
    color: var(--nh-ink);
}

/* Who wrote it, when — and the two things you might want to do with it. */
.nh-article__byline {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    padding-bottom: 20px;
    border-bottom: 1px solid var(--nh-line);
    margin-bottom: 26px;
}
.nh-article__who { display: inline-flex; align-items: center; gap: 11px; min-width: 0; }
.nh-byline__avatar--lg { width: 36px; height: 36px; font-size: 12px; }
.nh-article__whotext { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.nh-article__whotext strong { font-size: 14px; font-weight: 700; color: var(--nh-ink); }
.nh-article__when { display: flex; gap: 8px; font-size: 12.5px; color: var(--nh-ink-muted); }
.nh-article__when > * + *::before { content: '·'; margin-right: 8px; color: var(--nh-line); }

.nh-article__tools { display: inline-flex; gap: 8px; }
.nh-iconbtn {
    width: 34px;
    height: 34px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius);
    background: #fff;
    color: var(--nh-ink-muted);
    font-size: 13px;
    cursor: pointer;
    transition: border-color .15s ease, color .15s ease, background .15s ease;
}
.nh-iconbtn:hover { border-color: var(--nh-primary); color: var(--nh-primary); }
.nh-iconbtn:focus-visible { outline: none; box-shadow: var(--nh-focus); }
/* Confirmation that the copy worked, on the button itself. */
.nh-iconbtn.is-done {
    border-color: #b6e2c4;
    background: #e7f6ec;
    color: #14672f;
}

.nh-article__standfirst {
    font-size: 18px;
    line-height: 1.6;
    color: var(--nh-ink-soft);
    margin: 0 0 24px;
}

/* ---- The post body itself.
 *
 * Written in Moodle's editor, so the markup is whatever the author produced.
 * These rules give that arbitrary HTML the same typography as the rest of the
 * page instead of the theme's form-and-table defaults. */
.nh-article__content {
    font-size: 16.5px;
    line-height: 1.75;
    color: var(--nh-ink);
}
.nh-article__content > *:first-child { margin-top: 0; }
.nh-article__content p { margin: 0 0 1.35em; }
.nh-article__content h2,
.nh-article__content h3,
.nh-article__content h4 {
    font-family: var(--nh-font-head);
    font-weight: 700;
    line-height: 1.25;
    margin: 1.9em 0 .6em;
    color: var(--nh-ink);
}
.nh-article__content h2 { font-size: 22px; }
.nh-article__content h3 { font-size: 19px; }
.nh-article__content h4 { font-size: 17px; }
.nh-article__content ul,
.nh-article__content ol { margin: 0 0 1.35em; padding-left: 1.3em; }
.nh-article__content li { margin-bottom: .55em; }
.nh-article__content a { color: var(--nh-primary); text-underline-offset: 3px; }
.nh-article__content img { max-width: 100%; height: auto; border-radius: var(--nh-radius); }
.nh-article__content blockquote {
    margin: 1.8em 0;
    padding: 18px 22px;
    border-left: 3px solid var(--nh-accent, #c9a14a);
    border-radius: 0 var(--nh-radius) var(--nh-radius) 0;
    background: var(--nh-paper, #f4f6fa);
    color: var(--nh-ink-soft);
}
.nh-article__content blockquote p:last-child { margin-bottom: 0; }
.nh-article__content pre {
    background: var(--nh-paper, #f4f6fa);
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius);
    padding: 16px;
    /* Code and wide tables scroll inside their own box rather than pushing the
       whole page sideways on a phone. */
    overflow-x: auto;
    font-size: 14px;
}
.nh-article__content code { font-size: .92em; }
.nh-article__content table { width: 100%; border-collapse: collapse; margin: 0 0 1.35em; }
.nh-article__content th,
.nh-article__content td { border: 1px solid var(--nh-line); padding: 10px 12px; text-align: left; }
.nh-article__content hr { border: 0; border-top: 1px solid var(--nh-line); margin: 2.2em 0; }

.nh-article__subhead {
    font-family: var(--nh-font-head);
    font-size: 17px;
    font-weight: 700;
    margin: 0 0 12px;
}
.nh-article__files { margin-top: 36px; padding-top: 24px; border-top: 1px solid var(--nh-line); }
.nh-filelist { list-style: none; margin: 0; padding: 0; }
.nh-filelist li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 11px 0;
    border-bottom: 1px solid var(--nh-line);
    font-size: 15px;
}
.nh-filelist li:last-child { border-bottom: 0; }
.nh-filelist a { color: var(--nh-primary); text-decoration: none; font-weight: 600; }
.nh-filelist a:hover { text-decoration: underline; }
.nh-filelist__size { color: var(--nh-ink-muted); font-size: 13px; white-space: nowrap; }

.nh-article__admin {
    margin: 32px 0 0;
    padding-top: 22px;
    border-top: 1px solid var(--nh-line);
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

/* Related stories, in the same grid the listing uses. */
.nh-related { margin-top: 36px; }
.nh-related .nh-blogbar__title { display: block; margin: 0 0 16px; }

/* ---------------------------------------------------------------- Print.
 *
 * The print button is only worth having if the result is the article rather
 * than the article plus the navigation, the sidebar and the site footer. */
@media print {
    .nh-nav,
    .nh-footer,
    .nh-blogside,
    .nh-pagehead,
    .nh-backlink,
    .nh-article__tools,
    .nh-article__admin,
    .nh-related,
    .nh-notice { display: none !important; }

    .nh-bloglayout { display: block !important; }
    .nh-article {
        border: 0 !important;
        border-radius: 0 !important;
    }
    .nh-article__cover { display: none !important; }
    .nh-article__inner { padding: 0 !important; }
    .nh-page--article { background: #fff !important; }
    .nh-article__content { font-size: 11pt; }
    /* A link is useless in print unless its address is on the page. */
    .nh-article__content a::after {
        content: ' (' attr(href) ')';
        font-size: .85em;
        word-break: break-all;
    }
}

/* THE COURSE SUMMARY CHIPS ON A CARD.

   [ Microsoft ] [ Beginner ]
   [ Microsoft Certified ] [ Voucher ]

   One component, used by all four places a course card appears — the Explore
   library, "Trending on Nzeru Hub", the landing carousel and My Courses — so
   the same course reads identically wherever it is met.

   EACH FIELD IS ITS OWN CHIP. The four values answer four unrelated questions
   (who runs it, how hard, what you leave with, what it costs), and a chip
   boundary says that far more plainly than a bar between words: the eye can
   land on one without reading the others. Chips wrap freely, so a card that is
   narrow shows two rows rather than clipping — dropping the cost, usually the
   one a learner came to check.

   The icons are decorative. Each chip's word already names the thing, so the
   glyph carries aria-hidden and adds nothing for a screen reader to read. */
.nh-cardmeta {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 8px 0 0;
    padding: 0;
    list-style: none;
}

.nh-cardmeta__item {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    max-width: 100%;
    padding: 3px 9px;
    border: 1px solid var(--nh-line, #e2e7f0);
    border-radius: 999px;
    background: var(--nh-surface-2, #fafbfe);
    color: var(--nh-ink-soft, #4a5670);
    font-size: 11.5px;
    font-weight: 500;
    line-height: 1.45;
    /* A long provider name ("Malawi University of Business and Applied
       Sciences") must not push the chip past the card and force a scrollbar. */
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.nh-cardmeta__icon {
    flex: 0 0 auto;
    font-size: 11px;
    line-height: 1;
    opacity: .75;
}

/* Provider is the one part people scan for by name, so it carries the weight. */
.nh-cardmeta__item--provider {
    color: var(--nh-ink, #0b2347);
    font-weight: 600;
}

/* Cost is the only field whose value changes the decision, so it is the only
   one tinted — and it takes the platform's own accent green rather than a
   colour invented here, so a free course reads the same as any other
   affirmative on the page. */
.nh-cardmeta__item--cost {
    border-color: rgba(31, 138, 76, .28);
    background: var(--nh-success-tint, #e6f5ec);
    color: var(--nh-success, #1f8a4c);
    font-weight: 600;
}

.nh-cardmeta__item--cost .nh-cardmeta__icon {
    opacity: 1;
}

@media (max-width: 480px) {
    .nh-cardmeta {
        gap: 5px;
    }

    .nh-cardmeta__item {
        padding: 2px 8px;
        font-size: 11px;
    }
}

/* THE SUBSCRIBE ANSWER, AS A CENTRED DIALOG.

   Replaces a Moodle notification that rendered above the navigation: the
   subscribe box sits well down the blog sidebar, so the reply to using it
   appeared at the top of a long page, usually off screen. This is centred in
   the viewport instead — the reader's eye is already in the middle of the
   screen, and a modal is the one thing that cannot be scrolled away from.

   position: fixed with inset 0 rather than a top offset, so it is centred
   against the VIEWPORT and not against the document: on a blog listing several
   screens tall those are very different places. */
.nh-submodal {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
}

.nh-submodal__scrim {
    position: absolute;
    inset: 0;
    background: rgba(11, 35, 71, .55);
    /* Only the backdrop blurs. Blurring the dialog's own ancestor would blur
       the dialog with it. */
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
    animation: nh-submodal-fade .18s ease both;
}

.nh-submodal__box {
    position: relative;
    width: min(420px, 100%);
    /* A very short viewport — a phone in landscape — must still be able to
       reach the button, so the box scrolls inside itself rather than growing
       past the screen. */
    max-height: calc(100vh - 40px);
    overflow-y: auto;
    padding: 34px 28px 26px;
    border-radius: 18px;
    background: var(--nh-surface, #fff);
    box-shadow: 0 24px 60px -20px rgba(11, 35, 71, .45),
                0 2px 8px rgba(11, 35, 71, .08);
    text-align: center;
    animation: nh-submodal-rise .22s cubic-bezier(.22, .61, .36, 1) both;
}

/* The icon carries the outcome, so it is the one thing that changes colour.
   Green and amber rather than green and red: failing to send one email is a
   "try again", not an alarm. */
.nh-submodal__mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 62px;
    height: 62px;
    margin-bottom: 16px;
    border-radius: 50%;
    background: var(--nh-success-tint, #e6f5ec);
    color: var(--nh-success, #1f8a4c);
    font-size: 26px;
}

.nh-submodal--bad .nh-submodal__mark {
    background: var(--nh-accent-tint, #fff4dc);
    color: #b7791f;
}

.nh-submodal__title {
    margin: 0 0 8px;
    font-size: 1.25rem;
    line-height: 1.3;
    font-weight: 700;
    color: var(--nh-ink, #0b2347);
}

/* tabindex="-1" is on the heading so focus can move here; the ring that would
   draw around it is noise on something nobody clicked. */
.nh-submodal__title:focus {
    outline: none;
}

.nh-submodal__body {
    margin: 0 0 22px;
    font-size: .95rem;
    line-height: 1.55;
    color: var(--nh-ink-soft, #4a5670);
}

.nh-submodal__ok {
    width: 100%;
    min-height: 46px;
    padding: 0 22px;
    border: 0;
    border-radius: 999px;
    background: var(--nh-primary, #005bbb);
    color: #fff;
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    transition: filter var(--nh-dur, 180ms) var(--nh-ease, ease);
}

.nh-submodal__ok:hover { filter: brightness(1.08); }

.nh-submodal__ok:focus-visible {
    outline: 2px solid var(--nh-primary, #005bbb);
    outline-offset: 3px;
}

/* 44px square: the corner control has to be reachable with a thumb, and an
   icon-sized hit area is the usual reason a close button gets missed twice. */
.nh-submodal__x {
    position: absolute;
    top: 8px;
    right: 8px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--nh-ink-muted, #6b7280);
    font-size: 15px;
    cursor: pointer;
}

.nh-submodal__x:hover {
    background: var(--nh-surface-2, #fafbfe);
    color: var(--nh-ink, #0b2347);
}

.nh-submodal__x:focus-visible {
    outline: 2px solid var(--nh-primary, #005bbb);
    outline-offset: -2px;
}

@keyframes nh-submodal-fade { from { opacity: 0; } to { opacity: 1; } }

@keyframes nh-submodal-rise {
    from { opacity: 0; transform: translateY(14px) scale(.97); }
    to   { opacity: 1; transform: none; }
}

/* Motion is decoration here; the dialog must still arrive. */
@media (prefers-reduced-motion: reduce) {
    .nh-submodal__scrim,
    .nh-submodal__box {
        animation: none;
    }
}

@media (max-width: 480px) {
    .nh-submodal { padding: 14px; }

    .nh-submodal__box {
        padding: 30px 20px 22px;
        border-radius: 16px;
    }

    .nh-submodal__title { font-size: 1.15rem; }
}

/* CLEAR FILTERS.

   One control that puts every filter back, on Explore and on My certificates.
   It is deliberately quiet — an outline rather than a solid — because it undoes
   work rather than doing any, and it sits in a row of controls that are all
   more important than it.

   It is also deliberately ABSENT until something is actually filtered. A button
   that is always there and usually does nothing teaches people to stop seeing
   it, and this one has to be noticed in exactly one moment: when somebody has
   narrowed a list down to nothing and cannot work out how to get back. The
   hiding is done in JavaScript, which is where the filter state lives. */
.nh-libclear,
.nh-certs__clearall {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    min-height: 44px;
    padding: 0 16px;
    border: 1px solid var(--nh-line, #e6eaf0);
    border-radius: var(--nh-radius-pill, 999px);
    background: var(--nh-surface, #fff);
    color: var(--nh-ink-soft, #4a5670);
    font: inherit;
    font-size: 13.5px;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: border-color var(--nh-dur, 180ms) var(--nh-ease, ease),
                background var(--nh-dur, 180ms) var(--nh-ease, ease),
                color var(--nh-dur, 180ms) var(--nh-ease, ease);
}

.nh-libclear:hover,
.nh-certs__clearall:hover {
    border-color: var(--nh-primary, #005bbb);
    background: var(--nh-primary-tint, #e7f0fb);
    color: var(--nh-primary, #005bbb);
}

.nh-libclear:focus-visible,
.nh-certs__clearall:focus-visible {
    outline: 2px solid var(--nh-primary, #005bbb);
    outline-offset: 2px;
}

/* The arrow is a hint, not the message: the words carry it. */
.nh-libclear i,
.nh-certs__clearall i {
    font-size: 11px;
    opacity: .75;
}

@media (max-width: 700px) {
    /* Full width on a phone, where it sits under the controls it undoes. */
    .nh-libclear,
    .nh-certs__clearall {
        flex: 1 1 100%;
        justify-content: center;
    }
}

/* ---- The claim call to action on the dashboard level card ---------------- */
/* Gold, because it is the one thing on the card that can be acted on, and the
   rest of the card is a statement of where somebody stands. It sits above the
   quiet links rather than among them for the same reason. */
.nh-level__claim {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    min-height: 44px;
    margin: 14px 0 10px;
    padding: 10px 18px;
    border-radius: 10px;
    background: linear-gradient(135deg, #c98a00, var(--nh-accent, #c9a14a));
    color: #3a2600;
    font-weight: 700;
    font-size: 14.5px;
    text-decoration: none;
    text-align: center;
    transition: filter .15s ease, transform .1s ease;
}
.nh-level__claim:hover { filter: brightness(1.06); color: #3a2600; text-decoration: none; }
.nh-level__claim:active { transform: translateY(1px); }
.nh-level__claim:focus-visible {
    outline: 3px solid var(--nh-primary, #0c2d57);
    outline-offset: 2px;
}
.nh-level__links {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 18px;
    align-items: center;
}
@media (prefers-reduced-motion: reduce) {
    .nh-level__claim { transition: none; }
}

/* ---------------------------------------------------------------------------
   Confirmation dialog
   ---------------------------------------------------------------------------
   Used by the Contact form when a message goes out. Deliberately the same
   measurements as the rewards claim dialog and the saved-profile one: a reader
   who has seen any of the three has seen all of them, and three dialogs that
   nearly match read as a mistake where three that match exactly read as one
   idea.

   A real <dialog>, so the browser supplies the backdrop, the focus trap and the
   Escape key rather than this file pretending to.
   --------------------------------------------------------------------------- */
.nh-modal {
    border: 0;
    border-radius: 16px;
    padding: clamp(22px, 5vw, 32px);
    width: min(420px, calc(100vw - 32px));
    max-width: min(420px, calc(100vw - 32px));
    text-align: center;
    background: #fff;
    color: var(--nh-ink, #0b2347);
    box-shadow: 0 30px 70px -30px rgba(11, 35, 71, .45);
}
.nh-modal::backdrop {
    background: rgba(11, 27, 54, .55);
    backdrop-filter: blur(2px);
}
.nh-modal__icon {
    width: 62px;
    height: 62px;
    margin: 0 auto 14px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: #eaf6f0;
    color: #1e8a5f;
    font-size: 30px;
}
.nh-modal__title {
    margin: 0 0 8px;
    font-family: var(--nh-font-head);
    font-size: 1.3rem;
    font-weight: 800;
    letter-spacing: -.015em;
    line-height: 1.25;
}
.nh-modal__body {
    margin: 0 0 20px;
    color: var(--nh-ink-soft, #5d6c86);
    font-size: 15px;
    line-height: 1.5;
}
.nh-modal__close {
    width: 100%;
    justify-content: center;
}
@media (prefers-reduced-motion: no-preference) {
    .nh-modal[open] { animation: nh-modal-pop .18s ease-out; }
    @keyframes nh-modal-pop {
        from { opacity: 0; transform: translateY(6px) scale(.98); }
        to   { opacity: 1; transform: none; }
    }
}

/* ---------------------------------------------------------------------------
   Page not found

   A whole page rather than a strip of red, because that is what it is: the
   reader asked for something that is not here and the only useful thing on
   screen is the way onward. So the panel is centred, the sentence is short,
   and the three buttons are the point of the page.

   The number is large and very quiet — decoration that tells somebody who has
   seen a 404 before exactly what this is, without competing with the sentence
   underneath it for the attention of somebody who has not.
   --------------------------------------------------------------------------- */
.nh-404 {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: clamp(32px, 7vw, 88px) 16px;
}
.nh-404__panel {
    width: 100%;
    max-width: 640px;
    text-align: center;
}
.nh-404__code {
    margin: 0;
    font-family: var(--nh-font-head);
    font-size: clamp(64px, 15vw, 120px);
    font-weight: 800;
    line-height: 1;
    letter-spacing: -0.04em;
    color: var(--nh-line, #e6eaf2);
}
.nh-404__title {
    /* Carries the whole gap down to the buttons: the paragraph and the address
       line that used to sit in between are gone. */
    margin: 6px 0 28px;
    font-family: var(--nh-font-head);
    font-size: clamp(24px, 4.5vw, 34px);
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--nh-ink, #0b2347);
}
.nh-404__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px;
}
@media (max-width: 575.98px) {
    /* One column of full-width targets: three buttons side by side on a phone
       are three buttons too narrow to read and too narrow to hit. */
    .nh-404__actions { flex-direction: column; align-items: stretch; }
    .nh-404__actions .nh-btn { justify-content: center; }
}

/* ===========================================================================
   LEARNERS -- one institution's people, and the form that adds another.

   Lives inside Moodle's admin layout rather than the Nzeru Hub page frame, so
   everything here is self-contained and leans on the design tokens rather than
   on the page components around it.
   =========================================================================== */
/* --- Learners, and everything built like it --------------------------------
   WHY THIS NEEDED A PASS. Every piece of this page was white on white: the
   figures, the tab rail, the filter row and the table all had the page's own
   background, separated only by hairlines. Nothing said where one control ended
   and the next began, and the eye had nothing to land on.

   What changed is the surface underneath, not the components on top. The page
   sits on a tinted field; everything that is a THING -- a figure, the rail, the
   toolbar, the table -- is white and raised off it. That is the same treatment
   the dashboard and the certificates page already use, which is what makes
   these pages look like they belong to the same platform. */
.nh-lrn {
    max-width: 1180px;
    margin: 0 auto;
    color: var(--nh-ink);
}

/* The field the cards sit on. Negative margins so it reaches the page edges
   the admin layout gives us, without the page needing a wrapper of its own. */
body.path-local-nzeruhub #region-main,
body[class*="local_nzeruhub"] #region-main {
    background: var(--nh-surface-2, #F7F8FC);
}

/* ---------------------------------------------------------------- Masthead.

   THE SAME HEADER THE REPORTS PAGE HAS, AND THAT IS THE WHOLE POINT.

   This page used to open with a dark title on the page's own background and a
   grey line under it. Nothing was wrong with it except where it sits: a
   coordinator moves between /reports and /user-management all day, and one of
   them opened with a masthead while the other looked like the top of a form.
   Two halves of one job arriving as two products.

   The gradient, the uppercase eyebrow, the lede and the cropped watermark are
   copied from .nzr-hero in theme/nzeru/scss/post.scss rather than reused from
   it, and that is deliberate: .nzr-hero is scoped under .nzr, the analytics
   wrapper, so wrapping this page in it would drag in a tab rail, a card grid
   and a table skin it does not want. Same look, no coupling. */
.nh-lrn__head {
    position: relative;
    overflow: hidden;
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 20px;
    margin-bottom: 20px;
    padding: 30px 32px;
    border-radius: var(--nh-radius-lg, 18px);
    /* Literal hex, the same values the theme's masthead uses, so the two pages
       are one blue rather than two that nearly match. */
    background: linear-gradient(120deg, #0b2347 0%, #13305e 55%, #0f4d92 100%);
    color: #fff;
}

.nh-lrn__headtext {
    position: relative;
    z-index: 1;
    flex: 1 1 22rem;
    min-width: 15rem;
}

.nh-lrn__eyebrow {
    margin: 0 0 6px;
    font-size: 12px;
    font-weight: 800;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, .68);
}

.nh-lrn__title {
    margin: 0;
    font-size: 30px;
    font-weight: 800;
    line-height: 1.12;
    color: #fff;
}

.nh-lrn__lede {
    margin: 8px 0 0;
    max-width: 62ch;
    font-size: 14.5px;
    color: rgba(255, 255, 255, .8);
}

.nh-lrn__headside {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
}

/* Reversed out of the gradient: the filled navy pill it used to be would
   disappear against a navy header. */
.nh-lrn__chip {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 7px 14px;
    border-radius: var(--nh-radius-pill);
    border: 1px solid rgba(255, 255, 255, .45);
    background: rgba(255, 255, 255, .12);
    color: #fff;
    font-size: 13px;
    font-weight: 800;
    letter-spacing: .06em;
    text-transform: uppercase;
}

/* Cropped by the header's overflow on purpose: a whole icon floating in the
   corner reads as a button somebody forgot to wire up. */
.nh-lrn__headmark {
    position: absolute;
    right: -1rem;
    bottom: -2.5rem;
    font-size: 11rem;
    line-height: 1;
    color: rgba(255, 255, 255, .05);
    pointer-events: none;
}

.nh-lrn__alert {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin-bottom: 14px;
    padding: 12px 16px;
    border-radius: 12px;
    font-size: 14px;
}

.nh-lrn__alert--bad {
    background: #FDECEC;
    color: #8A1C1C;
}

/* --- who you are looking at ------------------------------------------------
   A rail of links, not a select: the tab is in the address, so it survives a
   reload and a bookmark. The count sits inside the tab because it belongs to
   that tab and nothing else on the row. */
.nh-lrn__tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 14px;
    padding: 5px;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-pill);
    background: #fff;
    box-shadow: 0 1px 2px rgba(11, 35, 71, .04);
}

.nh-lrn__tab {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 9px 16px;
    border-radius: var(--nh-radius-pill);
    font-size: 14px;
    font-weight: 600;
    color: var(--nh-ink-muted);
    text-decoration: none;
    white-space: nowrap;
}

.nh-lrn__tab:hover,
.nh-lrn__tab:focus {
    background: var(--nh-primary-tint, #eef4fb);
    color: var(--nh-primary-deep);
    text-decoration: none;
}

.nh-lrn__tab.is-active {
    background: var(--nh-primary-deep);
    color: #fff;
}

.nh-lrn__tabcount {
    min-width: 1.6em;
    padding: 1px 7px;
    border-radius: var(--nh-radius-pill);
    background: rgba(0, 0, 0, .07);
    font-size: 12.5px;
    font-weight: 700;
    text-align: center;
}

.nh-lrn__tab.is-active .nh-lrn__tabcount {
    background: rgba(255, 255, 255, .22);
}

/* A way back to everybody. Quiet, because it is an escape rather than an
   action: it should be findable without competing with Search. */
/* --- Add: one, or many ------------------------------------------------------
   A <details> styled as a split button. The summary keeps the solid Add look it
   had, so nothing moved for somebody who only ever adds one person. */
.nh-lrn__addmenu { position: relative; }

.nh-lrn__addmenu > summary {
    list-style: none;
    cursor: pointer;
}

.nh-lrn__addmenu > summary::-webkit-details-marker { display: none; }

.nh-lrn__addcaret {
    font-size: 11px;
    opacity: .8;
    transition: transform .15s ease;
}

.nh-lrn__addmenu[open] .nh-lrn__addcaret { transform: rotate(180deg); }

.nh-lrn__addlist {
    position: absolute;
    right: 0;
    z-index: 30;
    min-width: 268px;
    margin-top: 8px;
    padding: 6px;
    border: 1px solid var(--nh-line);
    border-radius: 14px;
    background: #fff;
    box-shadow: 0 18px 40px -18px rgba(11, 35, 71, .45);
}

.nh-lrn__addlist a {
    display: flex;
    align-items: flex-start;
    gap: 11px;
    padding: 10px 12px;
    border-radius: 10px;
    color: var(--nh-ink);
    text-decoration: none;
}

.nh-lrn__addlist a:hover,
.nh-lrn__addlist a:focus {
    background: var(--nh-primary-tint, #eef4fb);
    color: var(--nh-primary-deep);
    text-decoration: none;
}

.nh-lrn__addlist i { margin-top: 3px; }
.nh-lrn__addlist b { display: block; font-size: 14.5px; }
.nh-lrn__addlist small { display: block; color: var(--nh-ink-muted); font-size: 12.5px; }

/* --- Bulk upload ----------------------------------------------------------- */
.nh-bulk__step {
    padding: 22px 24px;
    margin-bottom: 18px;
    border: 1px solid var(--nh-line);
    border-radius: 16px;
    background: #fff;
}

.nh-bulk__step h2 {
    display: flex;
    align-items: center;
    gap: 11px;
    margin: 0 0 6px;
    font-size: 18px;
}

/* The step number, so the three sections read as an order rather than a pile. */
.nh-bulk__num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--nh-primary-deep);
    color: #fff;
    font-size: 13px;
    font-weight: 700;
}

.nh-bulk__hint {
    margin: 0 0 14px;
    color: var(--nh-ink-muted);
    font-size: 14px;
}

.nh-bulk__step input[type="file"] {
    width: 100%;
    max-width: 440px;
    padding: 10px;
    border: 1px dashed var(--nh-line);
    border-radius: 10px;
    background: #fff;
}

/* Created is the number somebody is looking for; the rest are exceptions and
   are coloured only where they mean something went wrong. */
.nh-bulk__stat--created b { color: #157347; }
.nh-bulk__stat--invalid b,
.nh-bulk__stat--failed b { color: #8A1C1C; }
.nh-bulk__stat--duplicate b,
.nh-bulk__stat--existing b { color: #8a5a00; }

/* WHY A ROW DID NOT BECOME AN ACCOUNT, in the colour of the thing that went
   wrong. It is the only reason anybody opens this table, and it was the same
   grey as the row number beside it. Not bold as well: a column of bold red is
   an alarm, and most of these are ordinary -- somebody already registered. */
.nh-bulk .nh-lrn__table tbody td:last-child {
    color: #8A1C1C;
}

/* The two columns it belongs to stay quiet so it carries. */
.nh-bulk .nh-lrn__table tbody td:first-child {
    color: var(--nh-ink-muted);
    font-variant-numeric: tabular-nums;
}

.nh-lrn__btn--plain {
    border: 0;
    background: none;
    color: var(--nh-ink-muted);
    text-decoration: underline;
    padding-inline: 8px;
}

.nh-lrn__btn--plain:hover,
.nh-lrn__btn--plain:focus {
    color: var(--nh-primary-deep);
}

/* --- the numbers ----------------------------------------------------------- */
.nh-lrn__stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
    gap: 12px;
    margin-bottom: 20px;
}

.nh-lrn__stat {
    position: relative;
    padding: 18px 20px;
    border: 1px solid var(--nh-line);
    border-radius: 16px;
    background: #fff;
    box-shadow: 0 1px 2px rgba(11, 35, 71, .04);
    transition: box-shadow .15s ease, transform .15s ease;
}

.nh-lrn__stat:hover {
    box-shadow: 0 10px 24px -14px rgba(11, 35, 71, .35);
}

/* A stripe along the top edge, so a row of figures reads as a row of cards
   rather than as a table with its lines removed. */
.nh-lrn__stat::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    border-radius: 16px 16px 0 0;
    background: var(--nh-primary-deep);
    opacity: .9;
}

.nh-lrn__stat b {
    display: block;
    font-size: 26px;
    font-weight: 800;
    line-height: 1.1;
    color: var(--nh-primary-deep);
}

.nh-lrn__stat span {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--nh-ink-muted);
}

/* --- search and add -------------------------------------------------------- */
/* The filters are one object, not six floating controls. */
.nh-lrn__tools {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin-bottom: 18px;
    padding: 14px 16px;
    border: 1px solid var(--nh-line);
    border-radius: 16px;
    background: #fff;
    box-shadow: 0 1px 2px rgba(11, 35, 71, .04);
}

.nh-lrn__search {
    position: relative;
    flex: 1 1 260px;
    min-width: 0;
}

.nh-lrn__search i {
    position: absolute;
    top: 50%;
    left: 14px;
    transform: translateY(-50%);
    color: var(--nh-ink-muted);
    font-size: 14px;
    pointer-events: none;
}

.nh-lrn__search input {
    width: 100%;
    height: 44px;
    padding: 0 14px 0 38px;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-pill);
    background: #fff;
    font-size: 14.5px;
    color: var(--nh-ink);
}

.nh-lrn__select {
    height: 44px;
    padding: 0 14px;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-pill);
    background: #fff;
    font-size: 14.5px;
    color: var(--nh-ink);
}

.nh-lrn__btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 44px;
    padding: 0 20px;
    border: 1px solid transparent;
    border-radius: var(--nh-radius-pill);
    font-size: 14.5px;
    font-weight: 700;
    line-height: 1;
    text-decoration: none;
    cursor: pointer;
    transition: background var(--nh-dur) var(--nh-ease),
                color var(--nh-dur) var(--nh-ease),
                border-color var(--nh-dur) var(--nh-ease);
}

.nh-lrn__btn--solid {
    background: var(--nh-primary-deep);
    color: #fff;
}

.nh-lrn__btn--solid:hover,
.nh-lrn__btn--solid:focus {
    background: var(--nh-primary);
    color: #fff;
    text-decoration: none;
}

.nh-lrn__btn--ghost {
    border-color: var(--nh-line-strong);
    background: #fff;
    color: var(--nh-ink);
}

.nh-lrn__btn--ghost:hover,
.nh-lrn__btn--ghost:focus {
    border-color: var(--nh-primary-deep);
    color: var(--nh-primary-deep);
    text-decoration: none;
}

.nh-lrn__add { margin-left: auto; }

/* --- the table ------------------------------------------------------------- */
.nh-lrn__tablewrap {
    border: 1px solid var(--nh-line);
    border-radius: 16px;
    background: #fff;
    overflow: hidden;
    box-shadow: 0 1px 2px rgba(11, 35, 71, .04);
}

.nh-lrn__table {
    width: 100%;
    margin: 0;
    border-collapse: collapse;
    font-size: 14.5px;
    background: #fff;
}

.nh-lrn__table thead th {
    padding: 13px 16px;
    border-bottom: 1px solid var(--nh-line);
    background: var(--nh-surface-2, #F7F8FC);
    font-size: 11.5px;
    font-weight: 800;
    letter-spacing: .07em;
    text-transform: uppercase;
    text-align: left;
    color: var(--nh-ink-muted);
    white-space: nowrap;
}

.nh-lrn__table tbody td {
    padding: 14px 16px;
    border-bottom: 1px solid var(--nh-line);
    vertical-align: middle;
}

/* A row somebody is reading. Not a colour change -- the row is not a state --
   just enough of a tint to keep the eye on one line across six columns. */
.nh-lrn__table tbody tr:hover td {
    background: var(--nh-primary-tint, #eef4fb);
}

.nh-lrn__table tbody tr:last-child td { border-bottom: 0; }

.nh-lrn__table tbody tr:last-child td { border-bottom: 0; }
.nh-lrn__table tbody tr:hover { background: var(--nh-surface-2, #F7F8FC); }

.nh-lrn__table .is-num { text-align: right; }
.nh-lrn__table .is-action { text-align: right; white-space: nowrap; }

.nh-lrn__who {
    display: flex;
    align-items: center;
    gap: 11px;
}

.nh-lrn__avatar {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--nh-primary-deep);
    color: #fff;
    font-size: 13px;
    font-weight: 800;
}

.nh-lrn__who b {
    display: block;
    font-weight: 700;
    line-height: 1.25;
}

.nh-lrn__who small {
    display: block;
    font-size: 12.5px;
    color: var(--nh-ink-muted);
}

/*
 * THE EMAIL COLUMN, GIVEN ROOM TO BE AN EMAIL ADDRESS.
 *
 * Breaking on any character alone let the browser split anywhere once the
 * column got tight, and adding a third row action made it tight: addresses
 * came out stacked four characters to a line. A minimum width stops the table
 * squeezing this column first, and breaking only at the punctuation an address
 * already has keeps it readable when it does wrap.
 */
.nh-lrn__mail {
    min-width: 13rem;
    overflow-wrap: anywhere;
    word-break: normal;
    line-break: anywhere;
}

/* The three actions sit on one line rather than wrapping under each other. */
.nh-lrn__table td.is-action { white-space: nowrap; }

/* Name and Sex do not need to grow; the space belongs to Email. */
.nh-lrn__table th:nth-child(4),
.nh-lrn__table td:nth-child(4) { width: 1%; white-space: nowrap; }

.nh-lrn__tag {
    display: inline-block;
    padding: 4px 10px;
    border-radius: var(--nh-radius-pill);
    background: var(--nh-surface-2, #EEF1F8);
    font-size: 12px;
    font-weight: 700;
    color: var(--nh-ink);
    white-space: nowrap;
}

.nh-lrn__tag--off {
    margin-left: 8px;
    background: #FDECEC;
    color: #8A1C1C;
}

/* Waiting on something, rather than switched off. Amber, not red: an account
   that has not been confirmed is not a problem with the account, it is a step
   nobody has finished -- and colouring it like a suspension would have people
   reaching for the wrong menu item. */
.nh-lrn__tag--wait {
    margin-left: 8px;
    background: #FFF4E0;
    color: #8A5A00;
}

.nh-lrn__link {
    font-weight: 700;
    color: var(--nh-primary);
}

.nh-lrn__empty {
    padding: 48px 24px;
    border: 1px dashed var(--nh-line-strong);
    border-radius: 16px;
    text-align: center;
    color: var(--nh-ink-muted);
}

.nh-lrn__empty i { font-size: 26px; }
.nh-lrn__empty p { margin: 10px 0 0; }

/* --- the form -------------------------------------------------------------- */
.nh-lrn__form {
    margin-top: 28px;
    padding: 26px;
    border: 1px solid var(--nh-line);
    border-radius: 18px;
    background: #fff;
}

.nh-lrn__formtitle {
    margin: 0 0 4px;
    font-size: 20px;
    font-weight: 800;
    color: var(--nh-primary-deep);
}

.nh-lrn__formlede {
    margin: 0 0 20px;
    max-width: 68ch;
    font-size: 14px;
    color: var(--nh-ink-muted);
}

/* The fieldset is a plain block and the CARDS are the grid: a <legend> inside
   a grid container becomes a grid item, which took the first cell and left the
   two role cards staggered across two rows. */
.nh-lrn__roles {
    margin: 0 0 22px;
    padding: 0;
    border: 0;
}

.nh-lrn__rolegrid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 12px;
}

.nh-lrn__roles legend {
    width: 100%;
    margin: 0 0 10px;
    padding: 0;
    font-size: 12px;
    font-weight: 800;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--nh-ink-muted);
}

.nh-lrn__role {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    margin: 0;
    padding: 14px 16px;
    border: 1.5px solid var(--nh-line);
    border-radius: 14px;
    cursor: pointer;
    transition: border-color var(--nh-dur) var(--nh-ease),
                background var(--nh-dur) var(--nh-ease);
}

.nh-lrn__role:hover { border-color: var(--nh-line-strong); }

.nh-lrn__role:has(input:checked),
.nh-lrn__role.is-on {
    border-color: var(--nh-primary-deep);
    background: var(--nh-surface-2, #F5F8FF);
}

.nh-lrn__role input { margin-top: 3px; }
.nh-lrn__role i { font-size: 17px; color: var(--nh-primary-deep); }
.nh-lrn__role b { display: block; font-weight: 700; }

.nh-lrn__role small {
    display: block;
    font-size: 12.5px;
    color: var(--nh-ink-muted);
}

.nh-lrn__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 16px;
}

.nh-lrn__field.is-wide { grid-column: 1 / -1; }

.nh-lrn__field label {
    display: block;
    margin-bottom: 6px;
    font-size: 13px;
    font-weight: 700;
    color: var(--nh-ink);
}

.nh-lrn__req { margin-left: 3px; color: #C1272D; }

.nh-lrn__field input,
.nh-lrn__field select {
    width: 100%;
    height: 44px;
    padding: 0 13px;
    border: 1px solid var(--nh-line);
    border-radius: 11px;
    background: #fff;
    font-size: 14.5px;
    color: var(--nh-ink);
}

.nh-lrn__field input:focus,
.nh-lrn__field select:focus,
.nh-lrn__search input:focus {
    border-color: var(--nh-primary);
    outline: 3px solid rgba(29, 78, 216, .16);
    outline-offset: 0;
}

.nh-lrn__field input[disabled] {
    background: var(--nh-surface-2, #F2F4F9);
    color: var(--nh-ink-muted);
    cursor: not-allowed;
}

.nh-lrn__hint {
    display: block;
    margin-top: 5px;
    font-size: 12.5px;
    color: var(--nh-ink-muted);
}

.nh-lrn__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 22px;
}

/* --- phones: the table becomes a stack of cards ----------------------------
   Seven columns do not fit a phone, and a table inside a sideways-scrolling box
   is a table whose last three columns nobody reads. Each cell already carries
   its own label, so the rows can simply restack.
   ------------------------------------------------------------------------- */
@media (max-width: 860px) {
    .nh-lrn__tablewrap {
        border: 0;
        background: transparent;
        overflow: visible;
    }

    .nh-lrn__table,
    .nh-lrn__table tbody,
    .nh-lrn__table tr,
    .nh-lrn__table td { display: block; width: 100%; }

    .nh-lrn__table thead { display: none; }

    .nh-lrn__table tbody tr {
        margin-bottom: 12px;
        padding: 6px 0;
        border: 1px solid var(--nh-line);
        border-radius: 14px;
        background: #fff;
    }

    .nh-lrn__table tbody tr:hover { background: #fff; }

    .nh-lrn__table tbody td {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 14px;
        padding: 9px 16px;
        border-bottom: 0;
        text-align: right;
    }

    .nh-lrn__table tbody td::before {
        content: attr(data-label);
        flex: none;
        font-size: 11.5px;
        font-weight: 800;
        letter-spacing: .06em;
        text-transform: uppercase;
        color: var(--nh-ink-muted);
        text-align: left;
    }

    /* The name is the card's heading, so it keeps the whole width and drops
       the label that would otherwise sit beside it.

       MATCHED BY CLASS, NOT BY POSITION. This used to be :first-child, which
       was the name right up until a checkbox column was put in front of it --
       and then the tick became the card's heading and the name grew a label.
       A rule about the name should say so. */
    .nh-lrn__table tbody td.nh-lrn__namecell {
        justify-content: flex-start;
        padding-top: 14px;
        text-align: left;
    }

    .nh-lrn__table tbody td.nh-lrn__namecell::before { content: none; }

    /* The tick rides at the top of the card, above the name, with the label
       the header row would have carried. Full width rather than floated, so
       it is a target a thumb can actually hit. */
    .nh-lrn__table tbody td.nh-lrn__tick {
        justify-content: flex-start;
        gap: 10px;
        padding: 12px 16px 0;
    }

    .nh-lrn__table tbody td.nh-lrn__tick:empty { display: none; }

    .nh-lrn__table .is-num,
    .nh-lrn__table .is-action { text-align: right; }

    .nh-lrn__add { margin-left: 0; }
    .nh-lrn__tools .nh-lrn__btn { flex: 1 1 auto; justify-content: center; }

    /* A pill shape only reads as one while it is a single row. Wrapped to
       three, the rounded ends look like a mistake, so the container becomes an
       ordinary rounded box and the tabs inside it keep their own pills. */
    .nh-lrn__tabs { border-radius: 18px; }

    /* Seven figures is a lot of screen before the list on a phone. */
    .nh-lrn__stat { padding: 12px 14px; }
    .nh-lrn__stat b { font-size: 22px; }

    .nh-lrn__tools select { flex: 1 1 140px; min-width: 0; }
}

@media (max-width: 560px) {
    .nh-lrn__head { padding: 22px 20px; }
    .nh-lrn__title { font-size: 23px; }
    /* Decoration, and on a phone it is decoration sitting under the words. */
    .nh-lrn__headmark { display: none; }
    .nh-lrn__form { padding: 18px; }
    .nh-lrn__actions .nh-lrn__btn { width: 100%; justify-content: center; }
}


/* --- learners: row actions, and the report card ---------------------------- */
.nh-lrn__rowacts {
    display: inline-flex;
    align-items: center;
    gap: 14px;
    white-space: nowrap;
}

.nh-lrn__rowacts .nh-lrn__link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13.5px;
}

.nh-lrn__link--danger { color: #C1272D; }

/*
 * The two states of the account control, which is one control saying two
 * different things. Suspending is the destructive direction, so it carries the
 * same red as every other destructive action here; activating is a restoration
 * and reads green. The icon says it too -- colour is never the only signal.
 */
.nh-lrn__link--off { color: #C1272D; }
.nh-lrn__link--on  { color: #1E8A5F; }

/* An unanswered cell, so the column reads as empty rather than broken. */
.nh-lrn__none { color: #98A2B3; }

/*
 * The confirm dialog. Narrower than the add/edit one because it asks one
 * question, and otherwise the same shell so it is recognisably the same page.
 */
.nh-lrnask .nh-authmodal__dialog { max-width: 30rem; }

.nh-lrnask__acts {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    justify-content: flex-end;
    margin-top: 1.4rem;
}

@media (max-width: 460px) {
    /* Half a phone is not a button, and the cancel must not be the easier
       one to hit by accident. */
    .nh-lrnask__acts { flex-direction: column-reverse; }
    .nh-lrnask__acts .nh-btn { width: 100%; }
}

.nh-lrn__hint--ok { color: var(--nh-success); }
.nh-lrn__hint--bad { color: #C1272D; }
.nh-lrn__field input.is-ok { border-color: var(--nh-success); }
.nh-lrn__field input.is-bad { border-color: #C1272D; }

.nh-lrep { max-width: 1100px; margin: 0 auto; color: var(--nh-ink); }
.nh-lrep__back { margin: 0 0 14px; font-size: 14px; }
.nh-lrep__back a { display: inline-flex; align-items: center; gap: 8px; font-weight: 700; }

.nh-lrep__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 16px;
    padding: 22px;
    border: 1px solid var(--nh-line);
    border-radius: 18px;
    background: #fff;
}

.nh-lrep__avatar {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 62px;
    height: 62px;
    border-radius: 50%;
    background: var(--nh-primary-deep);
    color: #fff;
    font-size: 21px;
    font-weight: 800;
}

.nh-lrep__who { flex: 1 1 240px; min-width: 0; }
.nh-lrep__name { margin: 0; font-size: 25px; font-weight: 800; color: var(--nh-primary-deep); }
.nh-lrep__sub { margin: 2px 0 0; font-size: 14px; color: var(--nh-ink-muted); }
.nh-lrep__chips { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 8px; }

.nh-lrep__chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 11px;
    border-radius: var(--nh-radius-pill);
    font-size: 11.5px;
    font-weight: 800;
    letter-spacing: .05em;
    text-transform: uppercase;
}

.nh-lrep__chip--star { background: rgba(245, 183, 0, .2); color: #7A5B00; }
.nh-lrep__chip--off { background: #FDECEC; color: #8A1C1C; }
.nh-lrep__actions { display: flex; flex-wrap: wrap; gap: 8px; }

.nh-lrep__facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 1px;
    margin-top: 16px;
    border: 1px solid var(--nh-line);
    border-radius: 14px;
    background: var(--nh-line);
    overflow: hidden;
}

.nh-lrep__fact { padding: 13px 16px; background: #fff; }

.nh-lrep__fact span {
    display: block;
    font-size: 11px;
    font-weight: 800;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--nh-ink-muted);
}

.nh-lrep__fact b { font-size: 14.5px; font-weight: 700; overflow-wrap: anywhere; }

.nh-lrep__stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 12px;
    margin-top: 16px;
}

.nh-lrep__stat {
    padding: 16px 18px;
    border: 1px solid var(--nh-line);
    border-radius: 14px;
    background: #fff;
}

.nh-lrep__stat b {
    display: block;
    font-size: 26px;
    font-weight: 800;
    line-height: 1.1;
    color: var(--nh-primary-deep);
}

.nh-lrep__stat span {
    font-size: 11.5px;
    font-weight: 800;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--nh-ink-muted);
}

.nh-lrep__stat small { display: block; margin-top: 3px; font-size: 11.5px; color: var(--nh-ink-muted); }

.nh-lrep__sec { margin-top: 30px; }
.nh-lrep__sec h2 { margin: 0 0 14px; font-size: 18px; font-weight: 800; color: var(--nh-primary-deep); }
.nh-lrep__empty { padding: 22px; border: 1px dashed var(--nh-line-strong); border-radius: 14px; color: var(--nh-ink-muted); }

.nh-lrep__list { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 12px; }

.nh-lrep__course {
    padding: 16px 18px;
    border: 1px solid var(--nh-line);
    border-radius: 14px;
    background: #fff;
}

.nh-lrep__coursetop { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.nh-lrep__coursetop h3 { margin: 0; font-size: 15.5px; font-weight: 700; line-height: 1.35; }

/* The chip is shared with the course cards, where it is absolutely positioned
   in a corner. Here it sits in the flow. */
.nh-lrep__coursestatus { position: static; flex: none; box-shadow: none; }

.nh-lrep__meter { display: flex; align-items: baseline; gap: 10px; margin: 12px 0 6px; }
.nh-lrep__pct { font-size: 15px; font-weight: 800; color: var(--nh-ink); }
.nh-lrep__count, .nh-lrep__seen { font-size: 12.5px; color: var(--nh-ink-muted); }
.nh-lrep__seen { margin-left: auto; }

.nh-lrep__track { height: 6px; border-radius: 4px; background: #EDEFF5; overflow: hidden; }
.nh-lrep__track i { display: block; height: 100%; border-radius: 4px; background: var(--nh-primary-deep); }

@media (max-width: 560px) {
    .nh-lrep__name { font-size: 21px; }
    .nh-lrep__actions { width: 100%; }
    .nh-lrep__actions .nh-lrn__btn { flex: 1 1 auto; justify-content: center; }
    .nh-lrn__rowacts { gap: 10px; }
    .nh-lrn__rowacts .nh-lrn__link span { display: none; }
    .nh-lrn__rowacts .nh-lrn__link i { font-size: 15px; }
}

/* --- the incentives queue --------------------------------------------------
   Built on the shared admin kit (.nh-lrn__*); only the parts that are peculiar
   to a queue live here: the three lists, the tick column, the bulk bar and the
   handover dialog. */
.nh-lrn__alert--info { background: #E8F1FE; color: #17418F; }

.nh-lrn__btn--danger {
    border-color: #F3C9CB;
    background: #fff;
    color: #C1272D;
}

.nh-lrn__btn--danger:hover,
.nh-lrn__btn--danger:focus {
    border-color: #C1272D;
    background: #C1272D;
    color: #fff;
    text-decoration: none;
}

.nh-lrn__btn--sm { height: 34px; padding: 0 13px; font-size: 13px; }

.nh-rwd__stat--pending b { color: #B26A00; }
.nh-rwd__stat--issued b { color: var(--nh-success); }

/* The three lists. A segmented control rather than Moodle's tab strip: the
   counts belong next to the names, and a tab strip has nowhere to put them. */
/* How many the search found. Quiet: it sits between the toolbar and the
   table and is read only when the number is a surprise. */
.nh-rwd__found {
    margin: 0 0 10px;
    font-size: .82rem;
    font-weight: 600;
    letter-spacing: .02em;
    text-transform: uppercase;
    color: var(--nh-muted, #6B7280);
}

/* Moodle's own paging bar, given room under the table. The bar itself is the
   theme's -- this only places it, so it keeps matching the learners list,
   which renders the same component. */
.nh-rwd > nav.pagination {
    margin-top: 18px;
}

.nh-rwd > nav.pagination .pagination {
    flex-wrap: wrap;
}

.nh-rwd__tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 0 0 18px;
}

.nh-rwd__tab {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    padding: 10px 16px;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-pill);
    background: #fff;
    font-size: 14px;
    font-weight: 700;
    color: var(--nh-ink);
    text-decoration: none;
    transition: border-color var(--nh-dur) var(--nh-ease),
                background var(--nh-dur) var(--nh-ease),
                color var(--nh-dur) var(--nh-ease);
}

.nh-rwd__tab:hover,
.nh-rwd__tab:focus { border-color: var(--nh-primary-deep); text-decoration: none; }

.nh-rwd__tab.is-on {
    border-color: var(--nh-primary-deep);
    background: var(--nh-primary-deep);
    color: #fff;
}

.nh-rwd__tabcount {
    min-width: 22px;
    padding: 2px 7px;
    border-radius: var(--nh-radius-pill);
    background: var(--nh-surface-2, #EEF1F8);
    font-size: 12px;
    font-weight: 800;
    text-align: center;
    color: var(--nh-ink);
}

.nh-rwd__tab.is-on .nh-rwd__tabcount { background: rgba(255, 255, 255, .22); color: #fff; }

.nh-rwd__tick { width: 42px; }
.nh-rwd__tick input { width: 17px; height: 17px; }

.nh-rwd__atclaim,
.nh-rwd__items,
.nh-rwd__by {
    display: block;
    font-size: 12.5px;
    font-weight: 400;
    color: var(--nh-ink-muted);
}

/* Why a claim was refused, under the tier it was for. Quiet and italic: it is
   context for the decision above it, not a heading of its own. */
.nh-rwd__reason {
    display: block;
    margin-top: 4px;
    font-size: 12.5px;
    font-weight: 400;
    font-style: italic;
    color: var(--nh-ink-muted);
}

/* Refused once, claimed again. Amber, because it is the one thing on the row
   somebody must not miss before deciding it a second time. */
.nh-rwd__again {
    display: block;
    margin-top: 4px;
    font-size: 12.5px;
    font-weight: 600;
    color: #8a5a00;
}

.nh-rwd__by { margin-bottom: 6px; }
.nh-rwd__none { color: var(--nh-ink-muted); font-size: 13.5px; }

.nh-rwd__acts { display: inline-flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; }

/* --- everything ticked, in one act ----------------------------------------- */
.nh-rwd__bulk {
    margin-top: 16px;
    padding: 18px;
    border: 1px solid var(--nh-line);
    border-radius: 16px;
    background: var(--nh-surface-2, #F7F8FC);
}

.nh-rwd__bulkhead {
    display: flex;
    align-items: center;
    gap: 9px;
    margin-bottom: 12px;
    font-size: 14px;
}

.nh-rwd__bulkhead i { color: var(--nh-primary-deep); }
.nh-rwd__bulkhead span { color: var(--nh-ink-muted); }

.nh-rwd__bulkrow { display: flex; flex-wrap: wrap; gap: 9px; }

.nh-rwd__note {
    flex: 1 1 260px;
    min-width: 0;
    height: 44px;
    padding: 0 14px;
    border: 1px solid var(--nh-line);
    border-radius: var(--nh-radius-pill);
    background: #fff;
    font-size: 14.5px;
}

/* --- when did it change hands? --------------------------------------------- */
.nh-rwd__dialog {
    width: min(460px, calc(100vw - 32px));
    padding: 24px;
    border: 0;
    border-radius: 18px;
    background: #fff;
    color: var(--nh-ink);
    box-shadow: 0 30px 80px rgba(8, 17, 49, .32);
}

.nh-rwd__dialog::backdrop { background: rgba(8, 17, 49, .55); }
.nh-rwd__dialog h2 { margin: 0 0 4px; font-size: 19px; font-weight: 800; color: var(--nh-primary-deep); }
.nh-rwd__dialogwho { margin: 0 0 16px; font-size: 14px; color: var(--nh-ink-muted); }
.nh-rwd__dialog label { display: block; margin-bottom: 6px; font-size: 13px; font-weight: 700; }

.nh-rwd__dialog input[type="datetime-local"] {
    width: 100%;
    height: 44px;
    padding: 0 13px;
    border: 1px solid var(--nh-line);
    border-radius: 11px;
    font-size: 14.5px;
}

.nh-rwd__dialoghint { margin: 7px 0 18px; font-size: 12.5px; color: var(--nh-ink-muted); }
.nh-rwd__dialogfoot { display: flex; justify-content: flex-end; gap: 9px; }

@media (max-width: 860px) {
    .nh-rwd__acts { justify-content: flex-end; }
    .nh-rwd__tab { flex: 1 1 auto; justify-content: center; }
}

@media (max-width: 560px) {
    .nh-rwd__bulkrow .nh-lrn__btn { flex: 1 1 auto; justify-content: center; }
    .nh-rwd__dialogfoot .nh-lrn__btn { flex: 1 1 auto; justify-content: center; }
}

/* --- the add/edit dialog ---------------------------------------------------
   The sign-up dialog's shell, widened: that one asks eight short questions in a
   column, this one asks the same questions in two columns plus a pair of role
   cards, and 420px would make a ladder of it. Everything else -- the scrim, the
   rounded card, the close button, the scroll-inside-itself behaviour -- is
   inherited unchanged. */
.nh-authmodal__dialog--wide { max-width: 720px; }

.nh-lrnmodal .nh-lrn__roles { margin-bottom: 18px; }
.nh-lrnmodal .nh-lrn__actions { margin-top: 20px; }

/* The alert the auth dialog shows in red, for the same reason. */
.nh-lrnmodal .nh-authmodal__alert { margin-bottom: 14px; }

@media (max-width: 720px) {
    /* A dialog that fills the phone rather than floating in a margin of scrim:
       there are eleven controls in it and every pixel of width is one less line
       of wrapping. */
    .nh-lrnmodal { padding: 0; }

    .nh-lrnmodal .nh-authmodal__dialog {
        max-width: none;
        min-height: 100%;
        max-height: 100%;
        border-radius: 0;
        padding: 56px 18px 24px;
    }

    .nh-lrnmodal .nh-lrn__grid { grid-template-columns: 1fr; }
    .nh-lrnmodal .nh-lrn__actions .nh-lrn__btn { flex: 1 1 auto; justify-content: center; }
}

/* The quiet "still typing" mark in the search box. Not a spinner: the wait is a
   page load of a few hundred milliseconds and a spinner would flash more than
   it would tell. */
.nh-lrn__searching {
    position: absolute;
    top: 50%;
    right: 14px;
    width: 13px;
    height: 13px;
    margin-top: -7px;
    border: 2px solid var(--nh-line-strong, #C7CEDD);
    border-top-color: var(--nh-primary-deep);
    border-radius: 50%;
    animation: nh-lrn-spin .7s linear infinite;
}

@keyframes nh-lrn-spin {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    .nh-lrn__searching { animation: none; opacity: .6; }
}

/* =====================================================================
 * User Management: the name cell and the row menu.
 * ===================================================================== */

/*
 * NAME, ADDRESS AND STATE IN ONE COLUMN.
 *
 * The avatar stays beside them all rather than beside the name alone, so the
 * three lines read as one person's block.
 */
.nh-lrn__whotext {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.nh-lrn__whotext > b {
    line-height: 1.3;
}

/*
 * The address, quieter than the name above it. A mailto link because it is
 * one -- there is no reason to make somebody copy an address out of a table.
 */
.nh-lrn__whotext .nh-lrn__mail {
    min-width: 0;
    font-size: 0.8125rem;
    line-height: 1.35;
    color: var(--nh-ink-faint, #5D6C86);
    text-decoration: none;
    overflow-wrap: anywhere;
}

.nh-lrn__whotext .nh-lrn__mail:hover,
.nh-lrn__whotext .nh-lrn__mail:focus {
    color: var(--nh-blue, #1B4F9C);
    text-decoration: underline;
}

/* The suspended tag sits with them rather than floating under the avatar. */
.nh-lrn__whotext .nh-lrn__tag { align-self: flex-start; margin-top: 2px; }

.nh-lrn__who { align-items: flex-start; }

/* ------------------------------------------------------- the row menu. */

.nh-lrn__menu {
    position: relative;
    display: inline-flex;
    justify-content: flex-end;
    width: 100%;
}

/*
 * The button. A 36px target because it is the only control in the row and a
 * three-dot glyph is a small thing to aim at, particularly on a phone.
 */
.nh-lrn__kebab {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 10px;
    background: none;
    color: var(--nh-ink-faint, #5D6C86);
    cursor: pointer;
    transition: background var(--nh-dur, .15s) var(--nh-ease, ease),
                color var(--nh-dur, .15s) var(--nh-ease, ease),
                border-color var(--nh-dur, .15s) var(--nh-ease, ease);
}

.nh-lrn__kebab:hover,
.nh-lrn__kebab:focus-visible,
.nh-lrn__kebab[aria-expanded="true"] {
    background: var(--nh-surface-2, #EEF1F8);
    border-color: var(--nh-line, #E2E7EF);
    color: var(--nh-ink, #0B2347);
}

/*
 * THE MENU, PLACED AGAINST THE WINDOW.
 *
 * `position: fixed` because the table wrapper is `overflow: hidden` -- see
 * rowMenuOpen() in assets/js/learners.js, which sets top and left from the
 * button's own rectangle. Inside the wrapper the last row's menu would be
 * clipped by the very border that rounds the table's corners.
 */
.nh-lrn__rowacts[data-nh-lrn-menulist] {
    align-items: stretch;
    gap: 2px;
}

.nh-lrn__rowacts[data-nh-lrn-menulist] .nh-lrn__link {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 9px 10px;
    border-radius: 8px;
    font-size: 0.875rem;
    text-decoration: none;
    white-space: nowrap;
}

.nh-lrn__rowacts[data-nh-lrn-menulist] .nh-lrn__link:hover,
.nh-lrn__rowacts[data-nh-lrn-menulist] .nh-lrn__link:focus-visible {
    background: var(--nh-surface-2, #EEF1F8);
}

/* A fixed-width icon slot, so three labels of different lengths line up. */
.nh-lrn__rowacts[data-nh-lrn-menulist] .nh-lrn__link i {
    flex: none;
    width: 1rem;
    text-align: center;
}

/*
 * The labels are the point of the menu, so they are never the thing hidden
 * to save room -- the narrow-screen rule that hides them in the row does not
 * reach in here.
 */
.nh-lrn__rowacts[data-nh-lrn-menulist] .nh-lrn__link span { display: inline; }

/*
 * The shape the menu takes when it is NOT open, which with the stylesheet's
 * default is simply "not displayed". These are the properties it has to be
 * reset to for the <noscript> fallback to lay it out as a row of links.
 */
.nh-lrn__rowacts[data-nh-lrn-menulist] {
    position: static;
    z-index: auto;
    flex-direction: row;
    min-width: 0;
    padding: 0;
    border: 0;
    background: none;
    box-shadow: none;
}

/*
 * THE DEFAULT IS THE FINAL STATE, AND THAT IS WHY THERE IS NO FLASH.
 *
 * This was the other way round at first: the stylesheet assumed no script,
 * and the script added a class to the body to say otherwise. The stylesheet
 * is in the head and the script is at the end of the body, so for the whole
 * of that gap every row rendered with its three links hanging open, and they
 * snapped shut once the script arrived. On a slow connection that is most of
 * the time the page is on screen.
 *
 * So the closed menu is simply what the stylesheet says, and the fallback
 * lives in a <noscript> block in the template -- which the browser applies
 * at parse time, with no script and no gap. See learners_list.mustache.
 */
.nh-lrn__kebab { display: inline-flex; }

.nh-lrn__rowacts[data-nh-lrn-menulist] { display: none; }

.nh-lrn__rowacts[data-nh-lrn-menulist].is-open {
    position: fixed;
    z-index: 1050;
    display: flex;
    flex-direction: column;
    min-width: 13rem;
    padding: 6px;
    border: 1px solid var(--nh-line, #E2E7EF);
    border-radius: 12px;
    background: #fff;
    box-shadow: 0 12px 28px rgba(11, 35, 71, .14);
}

/* ---------------------------------------------- Acting on several at once.

   A tick on every row, and a bar that appears when there is something to do
   with them. The bar is hidden while nothing is ticked -- a row of buttons
   that act on nothing is a row of buttons somebody presses to find out what
   they do, and two of these suspend accounts. */
.nh-lrn__tick {
    width: 44px;
    text-align: center;
    vertical-align: middle;
}

.nh-lrn__tick input[type="checkbox"] {
    width: 17px;
    height: 17px;
    cursor: pointer;
    accent-color: var(--nh-primary, #005BBB);
}

.nh-lrn__bulk {
    margin-top: 14px;
    padding: 14px 16px;
    border: 1px solid var(--nh-line);
    border-radius: 14px;
    background: var(--nh-surface-2, #F7F8FC);
}

.nh-lrn__bulkhead {
    display: flex;
    align-items: center;
    gap: 9px;
    margin-bottom: 10px;
    font-size: 14px;
    color: var(--nh-ink);
}

/* The running count sits apart from the label, in the quieter colour, because
   it is the part that changes and the label is not. */
.nh-lrn__bulkhead [data-nh-lrn-count] {
    margin-left: auto;
    color: var(--nh-ink-muted);
    font-size: 13px;
    font-weight: 600;
}

.nh-lrn__bulkrow {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

@media (max-width: 640px) {
    /* Every button on its own line and full width. Four verbs wrapped two to a
       row at thumb size is how the wrong one gets pressed -- and one of them
       suspends people. */
    .nh-lrn__bulkrow .nh-lrn__btn {
        flex: 1 1 100%;
        justify-content: center;
    }

    .nh-lrn__bulkhead {
        flex-wrap: wrap;
    }

    .nh-lrn__bulkhead [data-nh-lrn-count] {
        flex: 1 1 100%;
        margin-left: 0;
    }
}
