/* ==========================================================================
   JSM 2026 — Jabatan Standard Malaysia portal template
   Loaded on top of Cassiopeia's compiled CSS.

   Everything is written against custom properties. index.php redefines any of
   them from the template style, after this file, so the whole portal can be
   recoloured and re-spaced without touching a stylesheet.
   ========================================================================== */

:root {
    /* Palette */
    --jsm-navy:      #21395c;
    --jsm-navy-d:    #1a2e4a;
    --jsm-blue:      #0e4a86;
    --jsm-blue-d:    #0b3c6e;
    --jsm-blue-l:    #1c82b8;
    --jsm-red:       #e0242a;
    --jsm-yellow:    #f2b307;
    --jsm-gold:      #f0a92b;

    --jsm-ink:       #333d4d;
    --jsm-muted:     #5d6b7c;
    --jsm-line:      #e2e6ec;
    --jsm-surface:   #f5f7fa;
    --jsm-white:     #ffffff;

    /* Type */
    --jsm-font-head: "Poppins", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
    --jsm-font-body: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
    --jsm-base-size: 16px;

    /* Measure */
    --jsm-wrap:      1240px;
    --jsm-gutter:    24px;
    --jsm-band-pad:  4.5rem;

    --jsm-radius-lg: 18px;
    --jsm-radius-md: 12px;
    --jsm-radius-sm: 8px;

    --jsm-shadow:    0 1px 2px rgba(33, 57, 92, .06), 0 10px 28px -18px rgba(33, 57, 92, .45);
    --jsm-shadow-lg: 0 2px 6px rgba(33, 57, 92, .08), 0 22px 48px -24px rgba(33, 57, 92, .55);

    --jsm-hero-min:   clamp(26rem, 46vw, 38rem);
    --jsm-hero-focus: center 38%;

    --jsm-focus: 3px solid var(--jsm-blue-l);
}

/* ==========================================================================
   Base
   ========================================================================== */

body.jsm {
    font-family: var(--jsm-font-body);
    font-size: var(--jsm-base-size);
    line-height: 1.6;
    color: var(--jsm-ink);
    background: var(--jsm-white);
    -webkit-font-smoothing: antialiased;
}

/**
 * Element defaults, pitched at exactly 0,0,1 — the prefix is wrapped in
 * :where() so it adds nothing, and the bare element supplies the one point.
 *
 * That number is the whole trick. `body.jsm a` scores 0,1,1 and quietly
 * outranks `.jsm-btn-primary` at 0,1,0, which paints the hero buttons blue on
 * blue. Dropping to 0,0,0 goes too far the other way: Cassiopeia's own `a` and
 * `h1` are 0,0,1 and win instead. At 0,0,1 these tie with Cassiopeia and take
 * it on load order, while any class rule below still beats them outright.
 */
:where(body.jsm) h1,
:where(body.jsm) h2,
:where(body.jsm) h3,
:where(body.jsm) h4 {
    font-family: var(--jsm-font-head);
    font-weight: 700;
    line-height: 1.18;
    letter-spacing: -.015em;
    color: var(--jsm-navy);
}

:where(body.jsm) a {
    color: var(--jsm-blue);
    text-underline-offset: 2px;
}

body.jsm :focus-visible {
    outline: var(--jsm-focus);
    outline-offset: 2px;
    border-radius: 4px;
}

:where(body.jsm) :where(img) {
    max-width: 100%;
    height: auto;
}

.jsm .wrap {
    max-width: var(--jsm-wrap);
    margin-inline: auto;
    padding-inline: var(--jsm-gutter);
}

.jsm.wrapper-fluid .wrap {
    max-width: none;
}

.jsm-visually-hidden {
    position: absolute !important;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* ---------------------------------------------------------------- Buttons */

.jsm-btn {
    display: inline-flex;
    align-items: center;
    gap: .5em;
    padding: .8em 1.6em;
    border: 2px solid transparent;
    border-radius: 999px;
    font-family: var(--jsm-font-head);
    font-weight: 600;
    font-size: .95rem;
    line-height: 1.1;
    text-decoration: none;
    cursor: pointer;
    transition: background-color .15s ease, color .15s ease, border-color .15s ease, transform .1s ease;
}

.jsm-btn svg {
    width: 1.05em;
    height: 1.05em;
    flex: none;
}

.jsm-btn:active {
    transform: translateY(1px);
}

.jsm-btn-primary {
    color: var(--jsm-white);
    background: var(--jsm-blue);
    border-color: var(--jsm-blue);
}

.jsm-btn-primary:hover,
.jsm-btn-primary:focus {
    color: var(--jsm-white);
    background: var(--jsm-blue-d);
    border-color: var(--jsm-blue-d);
}

.jsm-btn-ghost {
    color: var(--jsm-white);
    border-color: rgba(255, 255, 255, .7);
}

.jsm-btn-ghost:hover,
.jsm-btn-ghost:focus {
    color: var(--jsm-navy);
    background: var(--jsm-white);
    border-color: var(--jsm-white);
}

/* Solid white on a photograph — the inverse of the ghost. */
.jsm-btn-light {
    color: var(--jsm-navy);
    background: var(--jsm-white);
    border-color: var(--jsm-white);
}

.jsm-btn-light:hover,
.jsm-btn-light:focus {
    color: var(--jsm-navy-d);
    background: var(--jsm-surface);
    border-color: var(--jsm-surface);
}

/* Solid red — the hero's first call to action. */
.jsm-btn-red {
    color: var(--jsm-white);
    background: var(--jsm-red);
    border-color: var(--jsm-red);
}

.jsm-btn-red:hover,
.jsm-btn-red:focus {
    color: var(--jsm-white);
    /* No darker red token, so this is the one mixed on the spot. */
    background: color-mix(in srgb, var(--jsm-red) 84%, #000);
    border-color: color-mix(in srgb, var(--jsm-red) 84%, #000);
}

/* Solid gold — the landing page's call to action. */
.jsm-btn-gold {
    color: var(--jsm-navy-d);
    background: var(--jsm-yellow);
    border-color: var(--jsm-yellow);
}

.jsm-btn-gold:hover,
.jsm-btn-gold:focus {
    color: var(--jsm-navy-d);
    background: var(--jsm-gold);
    border-color: var(--jsm-gold);
}

/* The chevron the landing design uses in place of the arrow glyph. */
.jsm-btn-chev {
    font-size: 1.35em;
    line-height: 0;
    translate: 0 -.04em;
}

.jsm-textlink {
    display: inline-flex;
    align-items: center;
    gap: .4em;
    font-weight: 600;
    text-decoration: none;
    border-bottom: 2px solid currentColor;
    padding-bottom: 2px;
}

.jsm-textlink svg {
    width: 1em;
    height: 1em;
}

/* ---------------------------------------------------------------- Skip links */

.jsm-skiplinks {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 999;
}

.jsm-skiplink {
    position: absolute;
    left: -9999px;
    padding: .6rem 1rem;
    background: var(--jsm-navy);
    color: var(--jsm-white);
    text-decoration: none;
    white-space: nowrap;
}

.jsm-skiplink:focus {
    left: .5rem;
    top: .5rem;
    color: var(--jsm-white);
}

/* ==========================================================================
   Government identification bar
   ========================================================================== */

.jsm-govbar {
    background: var(--jsm-surface);
    border-bottom: 1px solid var(--jsm-line);
    font-size: .8rem;
}

.jsm-govbar-inner {
    padding-block: .45rem;
}

.jsm-govbar-summary {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem 1rem;
    cursor: pointer;
    list-style: none;
    color: var(--jsm-muted);
}

.jsm-govbar-summary::-webkit-details-marker {
    display: none;
}

.jsm-govbar-brand {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
}

.jsm-govbar-flag {
    width: 26px;
    height: auto;
    border-radius: 2px;
}

.jsm-govbar-toggle {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    color: var(--jsm-blue);
    font-weight: 600;
}

.jsm-govbar-chevron {
    width: 0;
    height: 0;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 5px solid currentColor;
    transition: transform .15s ease;
}

.jsm-govbar-inner[open] .jsm-govbar-chevron {
    transform: rotate(180deg);
}

.jsm-govbar-panel {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
    gap: 1rem 2rem;
    padding: .9rem 0 .6rem;
}

.jsm-govbar-item {
    display: flex;
    gap: .6rem;
}

.jsm-govbar-ico {
    font-size: 1.1rem;
    line-height: 1.2;
}

.jsm-govbar-h {
    margin: 0 0 .15rem;
    font-weight: 700;
    color: var(--jsm-navy);
}

.jsm-govbar-p {
    margin: 0;
    color: var(--jsm-muted);
}

/* ==========================================================================
   Header
   ========================================================================== */

.jsm-header {
    background: var(--jsm-white);
    border-bottom: 1px solid var(--jsm-line);
}

.jsm-header.is-sticky {
    position: sticky;
    top: 0;
    z-index: 100;
}

/**
 * The masthead block — identity row plus search — and the soft shadow it casts
 * onto the navigation below.
 *
 * A radial gradient rather than a box-shadow, because the design feathers it
 * away at both ends instead of running it edge to edge: an ellipse centred on
 * the page, strongest in the middle, gone by the margins. z-index keeps it
 * painting over the nav, which is a later sibling and would otherwise cover it.
 */
.jsm-masthead {
    position: relative;
    z-index: 2;
    background: var(--jsm-white);
    border-bottom: 1px solid var(--jsm-line);
}

.jsm-masthead::after {
    content: '';
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    height: 24px;
    pointer-events: none;
    background: radial-gradient(58% 100% at 50% 0%,
        rgba(13, 31, 54, .18) 0%,
        rgba(13, 31, 54, .08) 42%,
        rgba(13, 31, 54, .02) 68%,
        rgba(13, 31, 54, 0) 100%);
}

/**
 * flex-wrap matters here: the search bar is a sibling at flex 1 1 100%, and
 * without wrapping it cannot claim a line of its own — it squeezes in beside
 * the icon rail and forces that into two rows instead.
 */
.jsm-identity {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding-block: 1.1rem;
}

/**
 * The wordmark is the masthead, so it gets the room: a tall lockup that keeps
 * its own width rather than being squeezed by the icon rail beside it. 62px is
 * the height the design draws it at — roughly 540px wide for the full
 * crest-plus-ministry lockup.
 */
.jsm-brand {
    display: inline-flex;
    align-items: center;
    gap: .75rem;
    flex: 0 1 auto;
    min-width: 0;
    text-decoration: none;
}

.jsm-brand-jata {
    max-height: 68px;
    width: auto;
    flex: none;
}

.jsm-brand-mark {
    max-height: 62px;
    width: auto;
    flex: none;
}

.jsm-brand-text {
    font-size: .8rem;
    line-height: 1.3;
    color: var(--jsm-muted);
    max-width: 18ch;
}

.jsm-identity-actions {
    display: flex;
    align-items: center;
    gap: 1.2rem;
}

/* ---------------------------------------------------- Utility icon rail */

/**
 * Icon over label, which is why the labels are so small: four of them have to
 * sit across the top right without crowding the wordmark.
 */
.jsm-utilrail {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 1.1rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.jsm-utilrail a {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .25rem;
    max-width: 4.6rem;
    color: var(--jsm-ink);
    font-size: .62rem;
    line-height: 1.2;
    text-align: center;
    text-decoration: none;
}

.jsm-utilrail a:hover,
.jsm-utilrail a:focus {
    color: var(--jsm-red);
}

.jsm-utilrail-icon {
    position: relative;
    display: grid;
    place-items: center;
    height: 1.5rem;
}

.jsm-utilrail-icon img {
    max-height: 1.5rem;
    width: auto;
    object-fit: contain;
}

.jsm-utilrail-badge {
    position: absolute;
    top: -.45em;
    left: -.8em;
    padding: .12em .32em;
    border-radius: 2px;
    background: var(--jsm-red);
    color: var(--jsm-white);
    font-size: .5rem;
    font-style: normal;
    font-weight: 700;
    line-height: 1.3;
}

/* ------------------------------------------------------------- Controls */

.jsm-identity-controls {
    display: flex;
    align-items: center;
    gap: .9rem;
    padding-left: 1.2rem;
    border-left: 1px solid var(--jsm-line);
}

.jsm-ctrl {
    display: grid;
    place-items: center;
    padding: 0;
    border: 0;
    background: none;
    color: var(--jsm-blue-l);
    line-height: 1;
    cursor: pointer;
}

.jsm-ctrl svg {
    width: 1.3rem;
    height: 1.3rem;
}

.jsm-ctrl img {
    max-height: 1.35rem;
    width: auto;
    object-fit: contain;
}

.jsm-lang {
    font-size: .9rem;
    font-weight: 600;
}

.jsm-lang a {
    color: var(--jsm-blue-l);
    text-decoration: none;
}

.jsm-lang ul {
    display: flex;
    gap: .4rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

/* ------------------------------------------------------------ Search bar */

/**
 * A row of its own under the identity line rather than a popover, so the field
 * is the width of the page when it opens.
 */
.jsm-searchbar {
    flex: 1 1 100%;
    padding-top: .9rem;
}

.jsm-searchbar[hidden] {
    display: none;
}

.jsm-searchbar form {
    display: flex;
    align-items: stretch;
    gap: .5rem;
}

/**
 * mod_finder puts the field and the button inside .input-group, so that
 * wrapper is the form's flex child — not the input. Stretching the input alone
 * does nothing; the group has to be the thing that grows, and the label beside
 * it is visually hidden and must not claim a share.
 */
.jsm-searchbar .input-group,
.jsm-searchbar .mod-finder__search {
    flex: 1 1 auto;
    display: flex;
    align-items: stretch;
    min-width: 0;
}

.jsm-searchbar label {
    flex: none;
}

.jsm-searchbar input[type="text"],
.jsm-searchbar input[type="search"] {
    flex: 1 1 auto;
    min-width: 0;
    padding: .75em 1em;
    font: inherit;
    color: var(--jsm-ink);
    background: var(--jsm-white);
    border: 1px solid var(--jsm-line);
    border-right: 0;
    border-radius: var(--jsm-radius-sm) 0 0 var(--jsm-radius-sm);
}

.jsm-searchbar input:focus {
    outline: none;
    border-color: var(--jsm-blue-l);
    box-shadow: 0 0 0 3px rgba(28, 130, 184, .18);
}

.jsm-searchbar button,
.jsm-searchbar .btn {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: .45em;
    padding: .75em 1.6em;
    border: 0;
    border-radius: 0 var(--jsm-radius-sm) var(--jsm-radius-sm) 0;
    background: var(--jsm-navy);
    color: var(--jsm-white);
    font: inherit;
    cursor: pointer;
    transition: background-color .15s ease;
}

.jsm-searchbar button:hover,
.jsm-searchbar .btn:hover {
    background: var(--jsm-blue);
    color: var(--jsm-white);
}

/* ==========================================================================
   Navigation
   ========================================================================== */

/**
 * A white bar with a rule under it, not a coloured one: the hero below is navy
 * and two blocks of navy stacked read as one slab. The active item is carried
 * by colour and weight instead.
 */
.jsm-nav {
    background: var(--jsm-white);
    border-bottom: 1px solid var(--jsm-line);
}

.jsm-nav-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.jsm-menu {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    margin: 0;
    padding: 0;
    list-style: none;
}

.jsm-menu-item {
    position: relative;
}

.jsm-menu-link {
    display: flex;
    align-items: center;
    padding: 1.05rem 1.15rem;
    font-family: var(--jsm-font-head);
    font-weight: 500;
    font-size: 1rem;
    color: var(--jsm-ink);
    text-decoration: none;
    transition: color .18s ease;
}

.jsm-menu-item:hover > .jsm-menu-link,
.jsm-menu-item:focus-within > .jsm-menu-link {
    color: var(--jsm-red);
}

.jsm-menu-item.is-active > .jsm-menu-link {
    color: var(--jsm-red);
    font-weight: 700;
}

.jsm-menu-audience .jsm-menu-link {
    font-weight: 500;
}

.jsm-menu-toggle {
    position: absolute;
    top: 50%;
    right: .25rem;
    transform: translateY(-50%);
    display: none;
    width: 1.8rem;
    height: 1.8rem;
    padding: 0;
    border: 0;
    border-radius: 4px;
    background: none;
    color: var(--jsm-muted);
    cursor: pointer;
}

.jsm-menu-toggle svg {
    width: 1rem;
    height: 1rem;
}

.jsm-submenu {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 70;
    min-width: 15rem;
    margin: 0;
    padding: .4rem 0;
    list-style: none;
    background: var(--jsm-white);
    border: 1px solid var(--jsm-line);
    border-radius: 0 0 var(--jsm-radius-md) var(--jsm-radius-md);
    box-shadow: var(--jsm-shadow-lg);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px);
    transition: opacity .15s ease, transform .15s ease, visibility .15s;
}

.jsm-menu-item:hover > .jsm-submenu,
.jsm-menu-item:focus-within > .jsm-submenu {
    opacity: 1;
    visibility: visible;
    transform: none;
}

.jsm-submenu .jsm-menu-link {
    padding: .55rem 1rem;
    color: var(--jsm-ink);
    font-weight: 500;
}

.jsm-submenu .jsm-menu-item:hover > .jsm-menu-link {
    background: var(--jsm-surface);
    color: var(--jsm-blue);
}

.jsm-nav-toggle {
    display: none;
}

.jsm-nav-backdrop[hidden] {
    display: none;
}

/* ==========================================================================
   Hero
   ========================================================================== */

.jsm-hero {
    position: relative;
    display: grid;
    /*
     * minmax(0, 1fr), not the implicit `auto`. An auto track takes its width
     * from its widest content, so the promo rail's columns could make the hero
     * — and with it the page — wider than the window.
     */
    grid-template-columns: minmax(0, 1fr);
    align-items: center;
    min-height: var(--jsm-hero-min);
    isolation: isolate;
    color: var(--jsm-white);
}

.jsm-hero-bg {
    position: absolute;
    inset: 0;
    z-index: -2;
    overflow: hidden;
}

.jsm-hero-bg img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: var(--jsm-hero-focus);
    /* The crossfade when the promo slideshow changes slide. */
    transition: opacity .18s ease;
}

.jsm-hero-bg img.is-swapping {
    opacity: 0;
}

/**
 * Two overlays, because one cannot do both jobs. The horizontal ramp keeps the
 * headline column dark while the photograph stays visible on the right; the
 * vertical wash stops a bright patch of sky washing out the top line. The
 * photograph is uncontrolled — editors swap it — so this has to hold for any
 * image, not just the one shipped.
 */
.jsm-hero::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background:
        linear-gradient(180deg, rgba(26, 46, 74, .22) 0%, rgba(26, 46, 74, 0) 35%),
        linear-gradient(100deg,
            rgba(26, 46, 74, .95) 0%,
            rgba(26, 46, 74, .86) 42%,
            rgba(26, 46, 74, .44) 68%,
            rgba(26, 46, 74, .08) 100%);
}

.jsm-hero-inner {
    padding-block: 3.5rem;
    width: 100%;
}

/*
 * 24ch and a smaller ramp. The copy here is a sentence rather than a slogan,
 * and at slogan size it filled the hero on its own; this leaves room for the
 * buttons and keeps the photograph in the picture.
 */
.jsm-hero h1 {
    margin: 0 0 1.1rem;
    max-width: 24ch;
    font-size: clamp(1.5rem, 2.5vw, 2.1rem);
    font-weight: 800;
    line-height: 1.22;
    color: var(--jsm-white);
    text-wrap: balance;
    /* The crossfade when the promo slideshow changes slide. */
    transition: opacity .18s ease;
}

.jsm-hero h1.is-swapping {
    opacity: 0;
}

.jsm-hero h1 .accent {
    color: var(--jsm-yellow);
}

.jsm-lede {
    margin: 0 0 1.6rem;
    max-width: 48ch;
    font-size: 1.05rem;
    color: rgba(255, 255, 255, .9);
}

.jsm-hero-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
}

/* ---------------------------------------------------------- Hero promo rail */

/**
 * The upcoming headlines, standing against the hero photograph. It is inside
 * the hero rather than under it, so the hero keeps its stated height and the
 * rail reads as part of the image.
 *
 * Two arrangements, chosen on the style: a column down the right, which is the
 * approved design, or a strip along the foot.
 */
.jsm-hero {
    position: relative;
    padding-bottom: 0;
}

.jsm-hero-rail {
    position: relative;
    z-index: 1;
    /* So a grid item can be narrower than its content and scroll instead. */
    min-width: 0;
}

.jsm-rail-item {
    display: flex;
    text-decoration: none;
    color: rgba(255, 255, 255, .82);
    background: rgba(13, 25, 48, .5);
    transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}

.jsm-rail-item:hover,
.jsm-rail-item:focus-visible {
    color: var(--jsm-white);
    background: rgba(13, 25, 48, .78);
}

.jsm-rail-item.is-active {
    color: var(--jsm-white);
    background: rgba(13, 25, 48, .82);
}

.jsm-rail-thumb {
    overflow: hidden;
}

.jsm-rail-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.jsm-rail-title {
    font-size: .84rem;
    font-weight: 600;
    line-height: 1.35;
}

/* ------------------------------------------------- The rail as a foot strip */

/*
 * Unscoped, so this is also what the right-hand column falls back to below the
 * breakpoint — a narrow screen has no width to give a column away. Putting
 * these behind the `bottom` modifier left the column variant with no styling
 * at all on a phone.
 */
.jsm-hero-rail {
    margin-top: auto;
    background: linear-gradient(180deg, rgba(26, 46, 74, 0) 0%, rgba(26, 46, 74, .72) 42%);
}

.jsm-hero-rail-inner {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: 1px;
    padding-block: 0;
}

.jsm-rail-item {
    align-items: center;
    gap: .7rem;
    padding: 1rem;
    border-top: 3px solid transparent;
}

.jsm-rail-item.is-active {
    border-top-color: var(--jsm-yellow);
}

.jsm-rail-thumb {
    flex: none;
    width: 3.1rem;
    height: 2.3rem;
    border-radius: 5px;
}

/*
 * On a phone four equal columns leave about 90px each, which wraps the titles
 * to four lines. Give each item a readable width instead and let the strip
 * scroll sideways.
 */
@media (max-width: 48rem) {
    .jsm-hero-rail-inner {
        grid-auto-columns: minmax(10.5rem, 1fr);
        overflow-x: auto;
        overscroll-behavior-x: contain;
        scrollbar-width: none;
    }

    .jsm-hero-rail-inner::-webkit-scrollbar {
        display: none;
    }
}

/* --------------------------------------------- The rail as a right-hand column */

@media (min-width: 62.0625rem) {
    .jsm-hero--rail-right {
        /*
         * The second track is the rail. It is sized in vw rather than fr so
         * that a long headline cannot squeeze it, and capped so it does not
         * swallow the photograph on a very wide screen.
         */
        grid-template-columns: minmax(0, 1fr) clamp(15rem, 19vw, 21rem);
    }

    .jsm-hero--rail-right .jsm-hero-inner {
        grid-area: 1 / 1 / 2 / 2;
    }

    /* Full height of the hero, against its right edge. */
    .jsm-hero--rail-right .jsm-hero-rail {
        grid-area: 1 / 2 / 2 / -1;
        align-self: stretch;
        display: flex;
        /* The strip's gradient and its auto top margin both belong to the foot. */
        margin-top: 0;
        background: rgba(13, 25, 48, .9);
    }

    .jsm-hero--rail-right .jsm-hero-rail-inner {
        /* It is a .wrap; the content measure is wrong inside a column. */
        max-width: none;
        padding: 0;
        flex: 1 1 auto;
        display: grid;
        grid-auto-flow: row;
        /*
         * A floor so each cell has room to be a picture, and 1fr so that when
         * the headline column is taller than the rail the cells share the
         * difference rather than leaving a gap at the foot.
         */
        grid-auto-rows: minmax(9rem, 1fr);
        grid-auto-columns: auto;
        gap: 0;
    }

    /*
     * Each cell is the picture, with the title laid over its foot — not a
     * thumbnail with a caption beneath. Nothing is drawn between cells: the
     * photographs and the dark gaps between them do that work themselves.
     */
    .jsm-hero--rail-right .jsm-rail-item {
        position: relative;
        display: grid;
        align-content: end;
        padding: 1.15rem 1.25rem;
        overflow: hidden;
        background: none;
        border: 0;
    }

    /*
     * The picture, stretched behind the cell. It is a grid area rather than a
     * background-image because the markup gives us a real <img>, which keeps
     * the alt text and the lazy loading the template already sets.
     */
    .jsm-hero--rail-right .jsm-rail-thumb {
        position: absolute;
        inset: 0;
        z-index: 0;
        width: auto;
        height: auto;
        border-radius: 0;
    }

    /*
     * A wash under the title. Without it a bright patch of sky takes the words
     * with it, and the photographs are swapped by editors — so this has to hold
     * for any image, not just the ones shipped.
     */
    /*
     * Held back by default so the slide that is showing stands out — the rail
     * is the slideshow's index as well as its controls, and the current slide
     * has to be readable as such at a glance.
     */
    .jsm-hero--rail-right .jsm-rail-item::after {
        content: '';
        position: absolute;
        inset: 0;
        z-index: 1;
        background: linear-gradient(180deg,
            rgba(13, 25, 48, .5) 0%,
            rgba(13, 25, 48, .72) 55%,
            rgba(13, 25, 48, .92) 100%);
        transition: background .25s ease;
    }

    .jsm-hero--rail-right .jsm-rail-title {
        position: relative;
        z-index: 2;
        font-size: .95rem;
        text-shadow: 0 1px 10px rgba(13, 25, 48, .6);
    }

    /* A cell with no picture still needs to read as one of the set. */
    .jsm-hero--rail-right .jsm-rail-item:not(:has(.jsm-rail-thumb)) {
        background: rgba(13, 25, 48, .55);
    }

    .jsm-hero--rail-right .jsm-rail-item:hover::after,
    .jsm-hero--rail-right .jsm-rail-item:focus-visible::after {
        background: linear-gradient(180deg,
            rgba(13, 25, 48, .1) 0%,
            rgba(13, 25, 48, .4) 55%,
            rgba(13, 25, 48, .8) 100%);
    }

    .jsm-hero--rail-right .jsm-rail-item.is-active::after {
        background: linear-gradient(180deg,
            rgba(13, 25, 48, .05) 0%,
            rgba(13, 25, 48, .3) 55%,
            rgba(13, 25, 48, .8) 100%);
    }

    /* A thin mark down the showing slide, so it is not colour alone. */
    .jsm-hero--rail-right .jsm-rail-item.is-active::before {
        content: '';
        position: absolute;
        inset: 0 auto 0 0;
        z-index: 2;
        width: 3px;
        background: var(--jsm-yellow);
    }
}

/* A slider module takes the band whole; it brings its own height. */
.jsm-showcase > * {
    display: block;
}

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

.jsm-page-hero {
    background: var(--jsm-navy);
    color: var(--jsm-white);
    padding-block: 2rem 2.2rem;
}

.jsm-page-title {
    margin: .4rem 0 0;
    font-family: var(--jsm-font-head);
    font-weight: 700;
    font-size: clamp(1.5rem, 3vw, 2.2rem);
    line-height: 1.2;
    color: var(--jsm-white);
}

.jsm-page-hero .jsm-lede {
    margin-top: .5rem;
    margin-bottom: 0;
    font-size: .98rem;
}

.jsm-page-hero nav,
.jsm-page-hero .breadcrumb {
    margin: 0;
    padding: 0;
    background: none;
    font-size: .82rem;
}

.jsm-page-hero .breadcrumb {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
    list-style: none;
}

.jsm-page-hero .breadcrumb a,
.jsm-page-hero .breadcrumb li {
    color: rgba(255, 255, 255, .78);
}

/* ==========================================================================
   Bands
   ========================================================================== */

.jsm-band {
    padding-block: var(--jsm-band-pad);
}

.jsm-pad-tight {
    padding-block: calc(var(--jsm-band-pad) * .45);
}

.jsm-pad-none {
    padding-block: 0;
}

.jsm-bg-surface {
    background: var(--jsm-surface);
}

.jsm-bg-navy {
    background: var(--jsm-navy);
    color: rgba(255, 255, 255, .88);
}

.jsm-bg-navy h2,
.jsm-bg-navy h3 {
    color: var(--jsm-white);
}

.jsm-bg-yellow {
    background: var(--jsm-yellow);
    color: var(--jsm-navy-d);
}

/* Two navy bands in a row read as one: drop the seam between them. */
.jsm-bg-navy + .jsm-bg-navy {
    padding-top: 0;
}

/**
 * Two sections sharing a band. The column widths come from the row's span as an
 * inline grid-template-columns, so the ratio is the editor's and only the
 * collapse point is ours.
 */
.jsm-band-cols {
    display: grid;
    gap: 2rem;
    align-items: start;
}

@media (max-width: 62rem) {
    .jsm-band-cols {
        grid-template-columns: minmax(0, 1fr) !important;
    }
}

/* ---------------------------------------------------------------- Band head */

.jsm-band-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: .75rem 2rem;
    margin-bottom: 2rem;
}

.jsm-eyebrow {
    margin: 0 0 .35rem;
    font-family: var(--jsm-font-head);
    font-size: .76rem;
    font-weight: 600;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--jsm-blue-l);
}

.jsm-band-title {
    margin: 0;
    font-size: clamp(1.45rem, 2.6vw, 2.1rem);
}

.jsm-band-lead {
    margin: .5rem 0 0;
    max-width: 52ch;
    color: var(--jsm-muted);
}

.jsm-bg-navy .jsm-band-lead {
    color: rgba(255, 255, 255, .72);
}

.jsm-band-more {
    display: inline-flex;
    align-items: center;
    gap: .4em;
    flex: none;
    font-weight: 600;
    font-size: .92rem;
    text-decoration: none;
}

.jsm-band-more svg {
    width: 1em;
    height: 1em;
}

.jsm-bg-navy .jsm-band-more {
    color: var(--jsm-yellow);
}

/* ==========================================================================
   Section: highlights
   ========================================================================== */

.jsm-highlights {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding-block: .85rem;
}

.jsm-highlights-label {
    flex: none;
    margin: 0;
    padding: .3rem .8rem;
    border-radius: 999px;
    background: var(--jsm-navy);
    color: var(--jsm-white);
    font-family: var(--jsm-font-head);
    font-size: .74rem;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
}

.jsm-highlights-track {
    flex: 1;
    min-width: 0;
}

.jsm-highlights-item {
    display: flex;
    align-items: baseline;
    gap: .75rem;
    color: var(--jsm-navy-d);
    font-weight: 600;
    text-decoration: none;
    min-width: 0;
}

.jsm-highlights-date {
    flex: none;
    font-size: .78rem;
    font-weight: 700;
    letter-spacing: .02em;
    opacity: .72;
}

.jsm-highlights-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.jsm-highlights-more {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: .35em;
    font-size: .82rem;
    font-weight: 700;
    color: var(--jsm-navy-d);
    text-decoration: none;
    border-bottom: 2px solid currentColor;
}

.jsm-highlights-more svg {
    width: .95em;
    height: .95em;
}

.jsm-highlights-item[hidden] {
    display: none;
}

.jsm-highlights-item:hover {
    text-decoration: underline;
}

.jsm-highlights-nav {
    display: flex;
    gap: .25rem;
    flex: none;
}

.jsm-highlights-nav button {
    display: grid;
    place-items: center;
    width: 1.9rem;
    height: 1.9rem;
    padding: 0;
    border: 1px solid rgba(26, 46, 74, .3);
    border-radius: 50%;
    background: none;
    color: var(--jsm-navy-d);
    cursor: pointer;
}

.jsm-highlights-nav button:hover {
    background: rgba(255, 255, 255, .5);
}

.jsm-highlights-nav svg {
    width: .9rem;
    height: .9rem;
}

/* ==========================================================================
   Section: Easy Access cards
   ========================================================================== */

/**
 * Accent colours, set per card. They tint the caption bar in the overlay shape
 * and the rule under the title in the stacked one, and they are what the
 * featured link card fills itself with.
 */
.jsm-accent-yellow { --jsm-accent: var(--jsm-yellow); --jsm-accent-ink: var(--jsm-navy-d); }
.jsm-accent-red    { --jsm-accent: var(--jsm-red);    --jsm-accent-ink: #fff; }
.jsm-accent-blue   { --jsm-accent: var(--jsm-blue-l); --jsm-accent-ink: #fff; }
.jsm-accent-green  { --jsm-accent: #1f9d6b;           --jsm-accent-ink: #fff; }
.jsm-accent-navy   { --jsm-accent: var(--jsm-navy);   --jsm-accent-ink: #fff; }

.jsm-cards {
    display: grid;
    /* The row holds as many cards as the style defines, down to a floor where
       the caption stops fitting; below that it reflows. */
    grid-template-columns: repeat(var(--jsm-cards-cols, 5), minmax(0, 1fr));
    gap: 1.25rem;
}

@media (max-width: 78rem) {
    .jsm-cards {
        grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    }
}

.jsm-card {
    display: flex;
    flex-direction: column;
    background: var(--jsm-white);
    border: 1px solid var(--jsm-line);
    border-radius: var(--jsm-radius-lg);
    overflow: hidden;
    text-decoration: none;
    color: inherit;
    box-shadow: var(--jsm-shadow);
    transition: transform .15s ease, box-shadow .15s ease;
}

.jsm-card:hover,
.jsm-card:focus-visible {
    transform: translateY(-3px);
    box-shadow: var(--jsm-shadow-lg);
}

/**
 * The media box is a fixed shape. "cover" fills it and crops the overflow,
 * which suits a photograph; "contain" shows the whole graphic on a padding
 * colour, which is what a poster or a logo needs. background-repeat is set
 * explicitly — without it a contained graphic tiles to fill the box.
 */
.jsm-card-media {
    display: block;
    aspect-ratio: 4 / 3;
    background-position: center;
    background-repeat: no-repeat;
    background-size: cover;
    background-color: var(--jsm-surface);
}

.jsm-card--fit-contain .jsm-card-media {
    background-size: contain;
    background-color: var(--jsm-white);
    margin: .75rem .75rem 0;
    border-radius: var(--jsm-radius-md);
}

.jsm-card-body {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .6rem;
    padding: .9rem 1rem;
    flex: 1;
}

.jsm-card-title {
    font-family: var(--jsm-font-head);
    font-weight: 700;
    font-size: .95rem;
    line-height: 1.3;
    color: var(--jsm-navy);
}

.jsm-card-go {
    display: inline-flex;
    flex: none;
    color: var(--jsm-blue);
}

.jsm-card-go svg {
    width: 1.15rem;
    height: 1.15rem;
}

/**
 * Overlay: the caption sits on the photograph in an accent bar. The card is a
 * grid with one cell so the media and the caption stack in the same box.
 */
.jsm-cards--overlay .jsm-card {
    display: grid;
    position: relative;
}

.jsm-cards--overlay .jsm-card-media {
    grid-area: 1 / 1;
    aspect-ratio: 4 / 3;
}

.jsm-cards--overlay .jsm-card-body {
    grid-area: 1 / 1;
    align-self: end;
    background: color-mix(in srgb, var(--jsm-accent, var(--jsm-yellow)) 92%, transparent);
    color: var(--jsm-accent-ink, var(--jsm-navy-d));
}

.jsm-cards--overlay .jsm-card-title,
.jsm-cards--overlay .jsm-card-go {
    color: inherit;
}

/* Stacked: caption under the image, with the accent as a rule above it. */
.jsm-cards--stacked .jsm-card-body {
    border-top: 3px solid var(--jsm-accent, var(--jsm-yellow));
}

/* ==========================================================================
   Section: explainer columns
   ========================================================================== */

.jsm-infoduo {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
    gap: 1.5rem;
}

.jsm-info {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: .6rem;
    padding: 1.6rem;
    background: var(--jsm-white);
    border: 1px solid var(--jsm-line);
    border-radius: var(--jsm-radius-lg);
}

.jsm-info-icon {
    display: grid;
    place-items: center;
    width: 3.2rem;
    height: 3.2rem;
    border-radius: var(--jsm-radius-md);
    background: rgba(28, 130, 184, .12);
}

.jsm-info-icon img {
    width: 1.7rem;
    height: 1.7rem;
    object-fit: contain;
}

.jsm-info h3 {
    margin: 0;
    font-size: 1.15rem;
}

.jsm-info p {
    margin: 0;
    color: var(--jsm-muted);
    flex: 1;
}

/* ==========================================================================
   Section: link cards
   ========================================================================== */

.jsm-linkcards {
    display: grid;
    gap: .9rem;
    align-content: start;
}

.jsm-linkcard {
    display: flex;
    align-items: flex-start;
    gap: .85rem;
    padding: 1.1rem;
    background: var(--jsm-white);
    border: 1px solid var(--jsm-line);
    border-left: 4px solid var(--jsm-accent, var(--jsm-blue-l));
    border-radius: var(--jsm-radius-md);
    text-decoration: none;
    color: inherit;
    transition: transform .15s ease, box-shadow .15s ease;
}

.jsm-linkcard:hover,
.jsm-linkcard:focus-visible {
    transform: translateX(2px);
    box-shadow: var(--jsm-shadow);
}

/* The featured card fills with its accent instead of outlining. */
.jsm-linkcard.is-featured {
    background: var(--jsm-accent, var(--jsm-blue-l));
    border-color: var(--jsm-accent, var(--jsm-blue-l));
    color: var(--jsm-accent-ink, #fff);
}

.jsm-linkcard.is-featured .jsm-linkcard-title,
.jsm-linkcard.is-featured .jsm-linkcard-body,
.jsm-linkcard.is-featured .jsm-linkcard-go {
    color: inherit;
}

.jsm-linkcard-icon {
    flex: none;
    display: grid;
    place-items: center;
    width: 2.6rem;
    height: 2.6rem;
}

.jsm-linkcard-icon img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.jsm-linkcard-text {
    flex: 1;
    min-width: 0;
    display: grid;
    gap: .2rem;
}

.jsm-linkcard-title {
    font-family: var(--jsm-font-head);
    font-weight: 700;
    font-size: .98rem;
    color: var(--jsm-navy);
}

.jsm-linkcard-body {
    font-size: .85rem;
    line-height: 1.45;
    color: var(--jsm-muted);
}

.jsm-linkcard-go {
    flex: none;
    align-self: center;
    color: var(--jsm-blue);
}

.jsm-linkcard-go svg {
    width: 1.1rem;
    height: 1.1rem;
}

/* ==========================================================================
   Section: activities
   ========================================================================== */

.jsm-activities {
    position: relative;
    display: flex;
    align-items: center;
    gap: .5rem;
}

.jsm-activities-track {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(14rem, 1fr);
    gap: 1.1rem;
    overflow-x: auto;
    scroll-behavior: smooth;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    padding-bottom: .25rem;
}

.jsm-activities-track::-webkit-scrollbar {
    display: none;
}

.jsm-activity {
    scroll-snap-align: start;
    display: flex;
    flex-direction: column;
    gap: .6rem;
    text-decoration: none;
    color: inherit;
}

.jsm-activity-media {
    display: block;
    aspect-ratio: 4 / 5;
    border-radius: var(--jsm-radius-lg);
    overflow: hidden;
    background: var(--jsm-surface);
}

.jsm-bg-navy .jsm-activity-media {
    background: rgba(255, 255, 255, .08);
}

.jsm-activity-media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform .25s ease;
}

.jsm-activity:hover .jsm-activity-media img {
    transform: scale(1.04);
}

/**
 * These bands default to light and get their dark treatment from .jsm-bg-navy,
 * rather than the other way round. The background is a per-row setting, so a
 * band that assumes it will be dark goes white-on-white the moment someone
 * changes it — which is exactly what happened to the activities captions.
 */
.jsm-activity-caption {
    font-size: .88rem;
    line-height: 1.45;
    color: var(--jsm-ink);
}

.jsm-bg-navy .jsm-activity-caption {
    color: rgba(255, 255, 255, .82);
}

.jsm-activities-embed {
    min-height: 320px;
    overflow: hidden;
}

.jsm-activities-embed > * {
    max-width: 100%;
}

/* Carousel buttons, shared with the agency rail */

.jsm-carousel-btn {
    flex: none;
    display: grid;
    place-items: center;
    width: 2.4rem;
    height: 2.4rem;
    padding: 0;
    border: 1px solid var(--jsm-line);
    border-radius: 50%;
    background: var(--jsm-white);
    color: var(--jsm-navy);
    cursor: pointer;
    transition: background-color .15s ease, color .15s ease;
}

.jsm-carousel-btn:hover {
    background: var(--jsm-blue);
    border-color: var(--jsm-blue);
    color: var(--jsm-white);
}

.jsm-carousel-btn svg {
    width: 1.1rem;
    height: 1.1rem;
}

.jsm-carousel-btn[disabled] {
    opacity: .35;
    cursor: default;
}

.jsm-bg-navy .jsm-carousel-btn {
    background: transparent;
    border-color: rgba(255, 255, 255, .35);
    color: var(--jsm-white);
}

.jsm-bg-navy .jsm-carousel-btn:hover {
    background: var(--jsm-white);
    color: var(--jsm-navy);
}

/* ==========================================================================
   Section: statistics
   ========================================================================== */

.jsm-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: 1.5rem;
}

/**
 * Beside the activities carousel the statistics are a narrow column, so the
 * figures stack and read left-aligned rather than centred in a wide row.
 */
.jsm-col-stats .jsm-stats {
    grid-template-columns: minmax(0, 1fr);
    gap: 1.1rem;
}

.jsm-col-stats .jsm-stat {
    flex-direction: row;
    align-items: center;
    text-align: left;
    gap: .9rem;
    padding: .9rem 1rem;
    background: var(--jsm-surface);
    border: 1px solid var(--jsm-line);
    border-radius: var(--jsm-radius-md);
}

.jsm-bg-navy .jsm-col-stats .jsm-stat {
    background: rgba(255, 255, 255, .06);
    border-color: transparent;
}

.jsm-col-stats .jsm-stat-icon {
    margin-bottom: 0;
    width: 2.6rem;
    height: 2.6rem;
}

.jsm-col-stats .jsm-stat-value {
    font-size: 1.9rem;
}

.jsm-stat {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: .25rem;
    text-decoration: none;
    color: inherit;
}

.jsm-stat-icon {
    width: 3rem;
    height: 3rem;
    object-fit: contain;
    margin-bottom: .4rem;
}

/**
 * #f0a92b is a 2:1 contrast on white, so the light band takes the blue and the
 * gold is kept for the navy one where it was drawn to sit.
 */
.jsm-stat-value {
    display: block;
    margin: 0;
    font-family: var(--jsm-font-head);
    font-weight: 800;
    font-size: clamp(2rem, 4vw, 2.8rem);
    line-height: 1.05;
    color: var(--jsm-blue);
}

.jsm-bg-navy .jsm-stat-value {
    color: var(--jsm-gold);
}

.jsm-stat-label {
    display: block;
    margin: 0;
    font-size: .9rem;
    color: var(--jsm-muted);
}

.jsm-bg-navy .jsm-stat-label {
    color: rgba(255, 255, 255, .78);
}

/* ==========================================================================
   Section: official social media
   ========================================================================== */

.jsm-social {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    gap: 1.25rem;
}

.jsm-social-card {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--jsm-line);
    border-radius: var(--jsm-radius-lg);
    overflow: hidden;
    text-decoration: none;
    color: inherit;
    background: var(--jsm-white);
    box-shadow: var(--jsm-shadow);
    transition: transform .15s ease, box-shadow .15s ease;
}

.jsm-social-card:hover,
.jsm-social-card:focus-visible {
    transform: translateY(-3px);
    box-shadow: var(--jsm-shadow-lg);
}

.jsm-social-media {
    display: block;
    aspect-ratio: 4 / 3;
    overflow: hidden;
    background: var(--jsm-surface);
}

.jsm-social-media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.jsm-social-body {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .9rem 1.1rem;
}

.jsm-social-glyph {
    width: 1.3rem;
    height: 1.3rem;
    object-fit: contain;
}

.jsm-social-label {
    font-family: var(--jsm-font-head);
    font-weight: 600;
    color: var(--jsm-navy);
}

/* ==========================================================================
   Section: agency rail
   ========================================================================== */

.jsm-agencies {
    position: relative;
    display: flex;
    align-items: center;
    gap: .5rem;
}

.jsm-agencies-track {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(8rem, 1fr);
    align-items: center;
    gap: 2rem;
    overflow-x: auto;
    scroll-behavior: smooth;
    scrollbar-width: none;
}

.jsm-agencies-track::-webkit-scrollbar {
    display: none;
}

.jsm-agency img {
    width: 100%;
    max-height: 4.5rem;
    object-fit: contain;
    filter: grayscale(1);
    opacity: .75;
    transition: filter .2s ease, opacity .2s ease;
}

.jsm-agency img:hover {
    filter: none;
    opacity: 1;
}

/* ==========================================================================
   Section: page content
   ========================================================================== */

.jsm-s-content {
    padding-block: calc(var(--jsm-band-pad) * .7);
}

.jsm-content-grid {
    display: grid;
    gap: 2rem;
    grid-template-columns: minmax(0, 1fr);
}

.jsm-s-content.has-left .jsm-content-grid {
    grid-template-columns: minmax(0, 17rem) minmax(0, 1fr);
}

.jsm-s-content.has-right .jsm-content-grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 17rem);
}

.jsm-s-content.has-left.has-right .jsm-content-grid {
    grid-template-columns: minmax(0, 15rem) minmax(0, 1fr) minmax(0, 15rem);
}

.jsm-main {
    min-width: 0;
}

.jsm-sidebar {
    display: grid;
    gap: 1.25rem;
    align-content: start;
}

/* Article typography inside the content band */

.jsm-main h1 {
    margin-top: 0;
    font-size: clamp(1.6rem, 3vw, 2.2rem);
}

.jsm-main h2 {
    margin-top: 2rem;
    font-size: 1.4rem;
}

.jsm-main h3 {
    margin-top: 1.5rem;
    font-size: 1.15rem;
}

.jsm-main img {
    border-radius: var(--jsm-radius-md);
}

.jsm-main table {
    width: 100%;
    border-collapse: collapse;
}

.jsm-main table th,
.jsm-main table td {
    padding: .6rem .8rem;
    border: 1px solid var(--jsm-line);
    text-align: left;
    vertical-align: top;
}

.jsm-main table th {
    background: var(--jsm-surface);
    font-family: var(--jsm-font-head);
    font-size: .85rem;
    color: var(--jsm-navy);
}

/* A wide table should scroll rather than push the layout sideways. */
.jsm-main .table-responsive,
.jsm-main figure.table {
    overflow-x: auto;
}

/* ---------------------------------------------------------------- Module cards */

.jsm-modcard {
    background: var(--jsm-white);
    border: 1px solid var(--jsm-line);
    border-radius: var(--jsm-radius-lg);
    padding: 1.25rem;
}

.jsm-card-heading {
    margin: 0 0 .75rem;
    font-size: 1.05rem;
}

.jsm-modcard ul {
    margin: 0;
    padding-left: 1.1rem;
}

.jsm-modules,
.jsm-cards--module,
.jsm-social--module,
.jsm-infoduo--module {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: 1.25rem;
}

/* ==========================================================================
   Footer
   ========================================================================== */

.jsm-footer {
    background: var(--jsm-navy-d);
    color: rgba(255, 255, 255, .78);
    font-size: .92rem;
}

.jsm-footer a {
    color: rgba(255, 255, 255, .85);
    text-decoration: none;
}

.jsm-footer a:hover {
    color: var(--jsm-white);
    text-decoration: underline;
}

.jsm-footer-top {
    display: grid;
    grid-template-columns: minmax(0, 1.4fr) repeat(auto-fit, minmax(11rem, 1fr));
    gap: 2rem;
    padding-block: 3rem 2rem;
}

.jsm-footer-brand img {
    max-width: 13rem;
    margin-bottom: 1rem;
    /* The wordmark is dark-on-light artwork; on navy it needs inverting. */
    filter: brightness(0) invert(1);
}

.jsm-footer-address {
    margin: 0 0 .9rem;
    line-height: 1.55;
}

.jsm-footer-contact {
    margin: 0 0 1rem;
    padding: 0;
    list-style: none;
    display: grid;
    gap: .4rem;
}

.jsm-footer-contact li {
    display: flex;
    align-items: center;
    gap: .55rem;
}

.jsm-footer-contact img {
    width: 1rem;
    height: 1rem;
    object-fit: contain;
    filter: brightness(0) invert(1);
    opacity: .8;
}

.jsm-footer-social {
    display: flex;
    gap: .5rem;
}

.jsm-footer-social a {
    display: grid;
    place-items: center;
    width: 2.2rem;
    height: 2.2rem;
    border-radius: 50%;
    background: rgba(255, 255, 255, .1);
    transition: background-color .15s ease;
}

.jsm-footer-social a:hover {
    background: var(--jsm-blue-l);
}

.jsm-footer-social img {
    width: 1.05rem;
    height: 1.05rem;
    object-fit: contain;
    filter: brightness(0) invert(1);
}

.jsm-footer-title {
    margin: 0 0 .75rem;
    font-family: var(--jsm-font-head);
    font-size: .95rem;
    font-weight: 700;
    color: var(--jsm-white);
}

.jsm-footer-col ul {
    margin: 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: .4rem;
}

.jsm-footer-extra {
    padding-bottom: 1.5rem;
}

/* ---------------------------------------------------------- Visitor counter */

.jsm-visitors {
    margin: 0;
    display: grid;
    gap: .3rem;
    font-size: .86rem;
}

.jsm-visitors > div {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    padding-bottom: .3rem;
    border-bottom: 1px solid rgba(255, 255, 255, .1);
}

.jsm-visitors dt {
    margin: 0;
    color: rgba(255, 255, 255, .68);
}

.jsm-visitors dd {
    margin: 0;
    font-family: var(--jsm-font-head);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--jsm-white);
}

/* The portal's own counter module, when it is used instead. */
.jsm-footer-visitors .jsm-footer-title + * {
    font-size: .86rem;
}

/* ------------------------------------------------- Module beside the gov bar */

.jsm-govbar-extra {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    margin-left: .5rem;
}

.jsm-govbar-extra img {
    max-height: 1.1rem;
    width: auto;
}

/* ---------------------------------------------------------------- Footer bar */

.jsm-footerbar {
    border-top: 1px solid rgba(255, 255, 255, .12);
    font-size: .82rem;
}

.jsm-footerbar-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .5rem 1.5rem;
    padding-block: 1rem;
}

.jsm-footerbar-left,
.jsm-footerbar-legal {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem 1rem;
}

/* ---------------------------------------------------------------- Back to top */

.jsm-backtotop {
    position: fixed;
    right: 1.1rem;
    bottom: 1.1rem;
    z-index: 90;
    display: grid;
    place-items: center;
    width: 2.6rem;
    height: 2.6rem;
    border-radius: 50%;
    background: var(--jsm-blue);
    color: var(--jsm-white);
    text-decoration: none;
    box-shadow: var(--jsm-shadow-lg);
    opacity: 0;
    visibility: hidden;
    transition: opacity .2s ease, visibility .2s, background-color .15s ease;
}

.jsm-backtotop.is-visible {
    opacity: 1;
    visibility: visible;
}

.jsm-backtotop:hover {
    background: var(--jsm-navy);
    color: var(--jsm-white);
}

/* ------------------------------------------------------------ WhatsApp */

/*
 * The floating chat button. On the right it sits above back-to-top rather
 * than beside it — two round buttons side by side in the same corner read as
 * one control split in half — and it stays put while back-to-top fades in and
 * out underneath.
 */
.jsm-wa {
    position: fixed;
    bottom: 1.1rem;
    z-index: 91;
    display: grid;
    place-items: center;
    width: 3.1rem;
    height: 3.1rem;
    border-radius: 50%;
    background: #25d366;
    color: #fff;
    text-decoration: none;
    box-shadow: var(--jsm-shadow-lg);
    transition: transform .15s ease, background-color .15s ease;
}

.jsm-wa--right {
    right: 1.1rem;
    bottom: 4.6rem;
}

.jsm-wa--left {
    left: 1.1rem;
}

.jsm-wa svg {
    width: 1.7rem;
    height: 1.7rem;
}

.jsm-wa:hover,
.jsm-wa:focus-visible {
    background: #1da851;
    color: #fff;
    transform: scale(1.06);
}

/*
 * The scale is decoration. Someone who has asked for less movement gets the
 * colour change and nothing else.
 */
@media (prefers-reduced-motion: reduce) {
    .jsm-wa {
        transition: background-color .15s ease;
    }

    .jsm-wa:hover,
    .jsm-wa:focus-visible {
        transform: none;
    }
}

/* ==========================================================================
   Error, maintenance and offline
   ========================================================================== */

.jsm-error-shell {
    max-width: 34rem;
    margin: 0 auto;
    padding: 5rem var(--jsm-gutter);
    text-align: center;
}

.jsm-error-logo,
.jsm-error-brand img {
    max-width: 13rem;
    margin-bottom: 2rem;
}

/*
 * The Site Offline artwork. It had no rule at all, so a 2160px notice was
 * printed at its natural size and pushed the whole page sideways.
 */
.jsm-error-art {
    display: block;
    width: 100%;
    max-width: 30rem;
    height: auto;
    margin: 0 auto 2rem;
    border-radius: var(--jsm-radius-md);
}

.jsm-error-code {
    margin: 0;
    font-family: var(--jsm-font-head);
    font-size: 4.5rem;
    font-weight: 800;
    line-height: 1;
    color: var(--jsm-line);
}

.jsm-error-title {
    margin: .5rem 0 .75rem;
    font-size: 1.5rem;
}

.jsm-error-lead {
    margin: 0 0 1.75rem;
    color: var(--jsm-muted);
}

.jsm-offline-login {
    display: grid;
    gap: .85rem;
    text-align: left;
    margin-top: 2rem;
}

.jsm-field label {
    display: block;
    margin-bottom: .3rem;
    font-weight: 600;
    font-size: .88rem;
}

.jsm-field input {
    width: 100%;
    padding: .7rem .9rem;
    font: inherit;
    border: 1.5px solid var(--jsm-line);
    border-radius: var(--jsm-radius-sm);
}

.jsm-error-debug {
    margin-top: 2.5rem;
    text-align: left;
    font-size: .8rem;
}

/* ------------------------------------------------------- Maintenance page */

.jsm-maintenance {
    background: var(--jsm-surface);
}

.jsm-maint {
    display: grid;
    place-items: center;
    min-height: 100vh;
    min-height: 100dvh;
    padding: 2rem var(--jsm-gutter);
}

.jsm-maint-card {
    width: 100%;
    max-width: 34rem;
    padding: clamp(2rem, 5vw, 3.25rem);
    border-radius: var(--jsm-radius-lg);
    background: var(--jsm-white);
    box-shadow: var(--jsm-shadow-lg);
    text-align: center;
}

.jsm-maint-logo {
    max-width: 15rem;
    height: auto;
    margin-bottom: 1.75rem;
}

.jsm-maint-eyebrow {
    margin: 0 0 .5rem;
    font-family: var(--jsm-font-head);
    font-size: .7rem;
    font-weight: 700;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--jsm-blue-l);
}

.jsm-maint-card h1 {
    margin: 0 0 .9rem;
    font-size: clamp(1.4rem, 3vw, 1.9rem);
    line-height: 1.2;
}

.jsm-maint-lead {
    margin: 0;
    color: var(--jsm-muted);
    line-height: 1.7;
}

.jsm-maint-until,
.jsm-maint-contact {
    margin: 1.25rem 0 0;
    font-size: .92rem;
    color: var(--jsm-ink);
}

.jsm-maint-until {
    padding-top: 1.25rem;
    border-top: 1px solid var(--jsm-line);
}

/*
 * Image-only: the notice is the page. It is centred and held to its own size
 * rather than stretched, because these are usually exported at a fixed width
 * and blowing one up past it only softens the type on it.
 */
.jsm-maintenance.is-image-only {
    background: var(--jsm-navy-d);
}

.jsm-maint-only {
    display: grid;
    place-items: center;
    min-height: 100vh;
    min-height: 100dvh;
    padding: 1.5rem;
}

.jsm-maint-only img {
    max-width: min(100%, 64rem);
    max-height: calc(100dvh - 3rem);
    width: auto;
    height: auto;
    object-fit: contain;
    border-radius: var(--jsm-radius-md);
}

/* ==========================================================================
   Landing page — the splash door and the audience chooser
   ========================================================================== */

/*
 * The landing shell has no masthead, no navigation and no footer, so its band
 * fills the window rather than sitting in the content measure. The band
 * machinery is otherwise untouched: the 60/40 pair is the ordinary two-column
 * band, un-contained here and nowhere else.
 */
body.is-landing {
    background: var(--jsm-navy-d);
}

/*
 * The landing page is one grid at every width, not a stack of bands.
 *
 * Every wrapper between <main> and the pieces steps out of the layout with
 * `display: contents` so that the photograph, the marquee and the policy strip
 * can reach across both columns while the headline keeps to one — and so that
 * the reading order can be set here rather than being dictated by which band a
 * piece happens to live in. In source order the policy strip arrives before
 * the chooser and the service cards; stacked, it has to come last.
 */
body.is-landing .jsm-landing {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    background: var(--jsm-navy-d);
    /* Keeps the photograph's negative z-index inside the page. */
    isolation: isolate;
}

body.is-landing .jsm-band {
    padding-block: 0;
}

body.is-landing .jsm-landing > .jsm-s-splash,
body.is-landing .jsm-s-splash > .wrap,
body.is-landing .jsm-band-cols,
body.is-landing .jsm-band-cols > .jsm-col,
body.is-landing .jsm-col-splash > .jsm-splash {
    display: contents;
}

/* Stacked: headline, chooser, cards, marquee, policy strip. */
body.is-landing .jsm-splash::before,
body.is-landing .jsm-splash-media,
body.is-landing .jsm-splash-body {
    grid-area: 1 / 1 / 2 / -1;
    position: static;
}

body.is-landing .jsm-splash-media { z-index: -2; }
body.is-landing .jsm-splash::before { z-index: -1; }

body.is-landing .jsm-audience    { grid-area: 2 / 1 / 3 / -1; }
body.is-landing .jsm-s-products  { grid-area: 3 / 1 / 4 / -1; }
body.is-landing .jsm-ticker      { grid-area: 4 / 1 / 5 / -1; }
body.is-landing .jsm-splash-foot { grid-area: 5 / 1 / 6 / -1; }

body.is-landing .jsm-splash-body {
    min-height: 60vh;
}

/* ------------------------------------------------------------------ Splash */

.jsm-splash {
    position: relative;
    display: grid;
    /*
     * The explicit column matters. Without it the implicit track is `auto`,
     * which sizes to max-content — and the .wrap inside each row has a 1240px
     * max-width, so the ticker and the foot would both lay themselves out at
     * 1240px and hang over the audience column beside them.
     */
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: 1fr auto auto;
    min-height: 100%;
    isolation: isolate;
    color: var(--jsm-white);
    background: var(--jsm-navy-d);
}

.jsm-splash-media {
    position: absolute;
    inset: 0;
    z-index: -2;
    background-position: center;
    background-size: cover;
    background-repeat: no-repeat;
}

/*
 * The wash over the photograph. It is heavier at the bottom than the top so the
 * policy strip stays legible wherever the image is cropped, and heavier on the
 * left because that is where the headline sits.
 */
.jsm-splash::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background:
        linear-gradient(180deg, rgba(26, 46, 74, .35) 0%, rgba(26, 46, 74, .55) 45%, rgba(26, 46, 74, .92) 100%),
        linear-gradient(90deg, rgba(26, 46, 74, .72) 0%, rgba(26, 46, 74, .34) 55%, rgba(26, 46, 74, .18) 100%);
}

.jsm-splash--blue::before {
    background:
        linear-gradient(180deg, rgba(14, 74, 134, .34) 0%, rgba(11, 60, 110, .58) 45%, rgba(11, 60, 110, .93) 100%),
        linear-gradient(90deg, rgba(11, 60, 110, .74) 0%, rgba(11, 60, 110, .32) 55%, rgba(11, 60, 110, .16) 100%);
}

.jsm-splash--dark::before {
    background:
        linear-gradient(180deg, rgba(0, 0, 0, .38) 0%, rgba(0, 0, 0, .6) 45%, rgba(0, 0, 0, .9) 100%);
}

.jsm-splash--none::before {
    display: none;
}

.jsm-splash-body {
    display: flex;
    align-items: center;
    padding-block: clamp(3rem, 7vh, 6rem);
}

/*
 * The splash sits in a column of its own rather than across the page, so the
 * 24px content gutter leaves the headline against the window edge. Widen it
 * for every row of the splash.
 */
.jsm-splash .wrap {
    --jsm-gutter: clamp(1.5rem, 4vw, 3.25rem);
}

.jsm-splash-inner {
    width: 100%;
}

.jsm-splash-badge {
    display: inline-block;
    margin: 0 0 1.25rem;
    padding: .45em 1.1em;
    border: 1px solid rgba(255, 255, 255, .45);
    border-radius: 999px;
    font-family: var(--jsm-font-head);
    font-size: .72rem;
    font-weight: 600;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--jsm-white);
    background: rgba(255, 255, 255, .1);
    backdrop-filter: blur(2px);
}

/*
 * Pitched at 0,1,1 so it beats the `:where(body.jsm) h1` element default, which
 * would otherwise paint the headline navy on a navy wash.
 */
.jsm-splash .jsm-splash-title {
    margin: 0 0 1.1rem;
    font-size: clamp(2.1rem, 4.2vw, 3.6rem);
    line-height: 1.08;
    letter-spacing: -.01em;
    color: var(--jsm-white);
    text-wrap: balance;
    text-shadow: 0 2px 18px rgba(0, 0, 0, .3);
}

.jsm-splash-lead {
    max-width: 34rem;
    margin: 0 0 2rem;
    font-size: clamp(1rem, 1.2vw, 1.12rem);
    line-height: 1.65;
    color: rgba(255, 255, 255, .88);
}

.jsm-splash-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .85rem;
}

/* ------------------------------------------------------- Announcement marquee */

.jsm-ticker {
    position: relative;
    border-block: 1px solid rgba(255, 255, 255, .16);
    background: rgba(13, 31, 54, .55);
    backdrop-filter: blur(3px);
}

.jsm-ticker-inner {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding-block: .7rem;
}

.jsm-ticker-label {
    flex: none;
    margin: 0;
    padding: .35em .9em;
    border-radius: 999px;
    font-family: var(--jsm-font-head);
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--jsm-navy-d);
    background: var(--jsm-yellow);
}

.jsm-ticker-viewport {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    /* Fade the text out at both ends instead of clipping it mid-letter. */
    mask-image: linear-gradient(90deg, transparent 0, #000 2.5rem, #000 calc(100% - 2.5rem), transparent 100%);
}

/*
 * The rail holds the item list twice over. Translating it by exactly half its
 * own width lands on the start of the second copy, which is pixel-identical to
 * where the first one began — so the loop has no visible seam and needs no
 * measurement in script.
 */
.jsm-ticker-rail {
    display: flex;
    width: max-content;
    animation: jsm-marquee var(--jsm-ticker-speed, 34s) linear infinite;
}

.jsm-ticker:hover .jsm-ticker-rail,
.jsm-ticker:focus-within .jsm-ticker-rail,
.jsm-ticker.is-paused .jsm-ticker-rail {
    animation-play-state: paused;
}

@keyframes jsm-marquee {
    from { transform: translateX(0); }
    to   { transform: translateX(-50%); }
}

.jsm-ticker-track {
    display: flex;
    align-items: center;
    gap: 3rem;
    margin: 0;
    padding: 0 1.5rem 0 0;
    list-style: none;
}

.jsm-ticker-item {
    font-size: .9rem;
    white-space: nowrap;
}

.jsm-ticker-item::before {
    content: '\2022';
    margin-right: .9rem;
    color: var(--jsm-yellow);
}

.jsm-ticker .jsm-ticker-item a,
.jsm-ticker .jsm-ticker-item span {
    color: rgba(255, 255, 255, .9);
    text-decoration: none;
}

.jsm-ticker .jsm-ticker-item a:hover,
.jsm-ticker .jsm-ticker-item a:focus {
    color: var(--jsm-white);
    text-decoration: underline;
}

.jsm-ticker-pause {
    flex: none;
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    padding: 0;
    border: 1px solid rgba(255, 255, 255, .28);
    border-radius: 50%;
    color: var(--jsm-white);
    background: transparent;
    cursor: pointer;
}

.jsm-ticker-pause:hover,
.jsm-ticker-pause:focus {
    background: rgba(255, 255, 255, .14);
}

/* Two bars while running, one triangle while paused. */
.jsm-ticker-pause span {
    width: 9px;
    height: 10px;
    background: linear-gradient(90deg,
        currentColor 0 3px, transparent 3px 6px, currentColor 6px 9px);
}

.jsm-ticker.is-paused .jsm-ticker-pause span {
    width: 0;
    height: 0;
    margin-left: 2px;
    background: none;
    border-left: 9px solid currentColor;
    border-block: 5px solid transparent;
}

/* ------------------------------------------------------------- Splash foot */

.jsm-splash-foot {
    background: rgba(13, 31, 54, .72);
    backdrop-filter: blur(3px);
}

.jsm-splash-foot-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .75rem 1.5rem;
    padding-block: 1rem;
    font-size: .82rem;
}

.jsm-splash-policy,
.jsm-splash-copy {
    margin: 0;
}

.jsm-splash-policy {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .55rem;
}

.jsm-splash-foot .jsm-splash-policy a,
.jsm-splash-foot .jsm-splash-a11y {
    color: rgba(255, 255, 255, .85);
    text-decoration: none;
}

.jsm-splash-foot .jsm-splash-policy a:hover,
.jsm-splash-foot .jsm-splash-policy a:focus,
.jsm-splash-foot .jsm-splash-a11y:hover,
.jsm-splash-foot .jsm-splash-a11y:focus {
    color: var(--jsm-white);
    text-decoration: underline;
}

.jsm-sep {
    color: rgba(255, 255, 255, .35);
}

.jsm-splash-copy {
    margin-top: .35rem;
    color: rgba(255, 255, 255, .62);
    font-size: .76rem;
}

.jsm-splash-foot-right {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.jsm-splash-social {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.jsm-splash-social a {
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    background: rgba(255, 255, 255, .12);
    transition: background-color .15s ease;
}

.jsm-splash-social a:hover,
.jsm-splash-social a:focus {
    background: rgba(255, 255, 255, .26);
}

.jsm-splash-social img {
    width: 1.05rem;
    height: 1.05rem;
    object-fit: contain;
}

/* ---------------------------------------------------------------- Audience */

.jsm-audience {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    min-height: 100%;
    padding: clamp(1.5rem, 3vw, 2.5rem) clamp(1.5rem, 3.5vw, 3.25rem) clamp(2rem, 4vw, 3rem);
    background: var(--jsm-white);
}

.jsm-audience-utility {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .75rem 1rem;
    padding-bottom: 1.1rem;
    border-bottom: 1px solid var(--jsm-line);
}

/*
 * Icon over label, the same arrangement and the same measurements as the
 * masthead's utility rail on the portal pages — these are the same four links,
 * and they should not be a different thing in two places.
 */
.jsm-audience-util {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 1.1rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.jsm-audience .jsm-audience-util a {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .25rem;
    max-width: 4.6rem;
    font-size: .62rem;
    font-weight: 500;
    line-height: 1.2;
    text-align: center;
    color: var(--jsm-ink);
    text-decoration: none;
}

.jsm-audience .jsm-audience-util a:hover,
.jsm-audience .jsm-audience-util a:focus {
    color: var(--jsm-red);
}

.jsm-audience-util img {
    /* A fixed box, so a tall icon and a wide one still sit on one baseline. */
    height: 1.5rem;
    width: auto;
    max-width: 2.4rem;
    object-fit: contain;
}

.jsm-audience .jsm-audience-lang {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    padding: .3em .85em .3em .35em;
    border: 1px solid var(--jsm-line);
    border-radius: 999px;
    font-size: .78rem;
    font-weight: 600;
    color: var(--jsm-navy);
    text-decoration: none;
}

.jsm-audience .jsm-audience-lang:hover,
.jsm-audience .jsm-audience-lang:focus {
    border-color: var(--jsm-blue-l);
    color: var(--jsm-blue);
}

.jsm-audience-lang img {
    width: 1.35rem;
    height: 1.35rem;
    border-radius: 50%;
    object-fit: cover;
}

/*
 * The mark takes the panel's full width. It is a wide lockup — jata, "Official
 * Portal", the department and the ministry on four lines of type — and at the
 * 22rem it used to be capped at, the ministry line was down at 5px and
 * unreadable. The height cap is only there so a square logo cannot run away
 * with the panel.
 */
.jsm-audience-identity img {
    max-width: 100%;
    max-height: 6.5rem;
    width: auto;
    height: auto;
}

/* ------------------------------------------------------------------- Tabs */

.jsm-audience-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    padding-bottom: .15rem;
    border-bottom: 2px solid var(--jsm-line);
}

.jsm-audience-tab {
    position: relative;
    padding: .65em 1.15em;
    border: 0;
    border-radius: var(--jsm-radius-sm) var(--jsm-radius-sm) 0 0;
    font-family: var(--jsm-font-head);
    font-size: .86rem;
    font-weight: 600;
    color: var(--jsm-muted);
    background: transparent;
    cursor: pointer;
    transition: color .15s ease, background-color .15s ease;
}

.jsm-audience-tab:hover {
    color: var(--jsm-blue);
    background: var(--jsm-surface);
}

.jsm-audience-tab.is-active {
    color: var(--jsm-navy);
}

/* Sits on the track's own 2px line rather than adding height to the button. */
.jsm-audience-tab.is-active::after {
    content: '';
    position: absolute;
    inset: auto 0 -.15rem;
    height: 3px;
    border-radius: 3px 3px 0 0;
    background: var(--jsm-red);
}

.jsm-audience-panels {
    flex: 1 1 auto;
}

.jsm-audience-panel {
    margin: 0;
    padding: 0;
    list-style: none;
}

.jsm-audience-panel[hidden] {
    display: none;
}

.jsm-audience-panel > li + li {
    border-top: 1px solid var(--jsm-line);
}

.jsm-audience .jsm-audience-panel a {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: .85rem .35rem;
    font-size: .92rem;
    font-weight: 500;
    color: var(--jsm-ink);
    text-decoration: none;
    transition: color .15s ease, background-color .15s ease, padding-left .15s ease;
}

.jsm-audience .jsm-audience-panel a:hover,
.jsm-audience .jsm-audience-panel a:focus {
    color: var(--jsm-blue);
    background: var(--jsm-surface);
    padding-left: .75rem;
}

.jsm-audience-label {
    display: inline-flex;
    align-items: center;
    gap: .55rem;
    min-width: 0;
}

/*
 * A small outlined square at the head of each link, as the design draws it.
 *
 * Drawn with an inset box-shadow rather than a border. A 1px border on an 8px
 * box is a hairline: on a 1x display it lands on a fractional pixel, gets
 * antialiased to about half strength, and at the opacity this started on it
 * disappeared altogether. A shadow is painted inside the box and survives that,
 * and the opacity is high enough now to read on the photograph behind it.
 */
.jsm-audience-bullet {
    flex: none;
    width: .55rem;
    height: .55rem;
    box-shadow: inset 0 0 0 1px currentColor;
    opacity: .8;
}

.jsm-audience-badge {
    flex: none;
    padding: .18em .55em;
    border-radius: 4px;
    font-family: var(--jsm-font-head);
    font-size: .62rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--jsm-white);
    background: var(--jsm-red);
}

.jsm-audience-go {
    flex: none;
    font-size: 1.3rem;
    line-height: 0;
    color: var(--jsm-blue-l);
}

.jsm-audience-empty {
    padding: .85rem .35rem;
    font-size: .88rem;
    color: var(--jsm-muted);
}

/* ==========================================================================
   Landing page — the approved gold scheme

   The second mockup turns the landing page warm: the photograph is tinted gold
   and runs behind the whole band, the chooser becomes a translucent blue panel
   floating on it with a yellow tab bar, and the marquee and the policy strip
   span the full page rather than the splash column.

   It is all opt-out. The splash side keys off the Overlay setting (`gold`) and
   the chooser off its Tone setting (`photo`); set Overlay back to Navy and Tone
   back to Light and the first scheme returns untouched.
   ========================================================================== */

/* ------------------------------------------- Letting the splash span the band */

/*
 * On a wide screen the page is one grid, not a stack of bands: the chooser runs
 * the full height of the left column's two rows, while the marquee, the policy
 * strip and the photograph reach across both columns.
 *
 * Rather than move any of that out of the sections it belongs to — an editor
 * should find the marquee with the splash, not somewhere else — every wrapper
 * between <main> and the pieces steps out of the layout with `display:
 * contents`, which hands them all to one grid. No markup changes, and the
 * mobile stack below is untouched because this is `min-width` scoped.
 *
 *   row 1   splash headline        │  chooser
 *   row 2   products               │  (same chooser, spanning)
 *   row 3   marquee, full width
 *   row 4   policy strip, full width
 */
@media (min-width: 62.0625rem) {
    body.is-landing .jsm-landing {
        grid-template-columns: minmax(0, 60fr) minmax(0, 40fr);
        grid-template-rows: 1fr auto auto auto;
        min-height: 100vh;
        min-height: 100dvh;
    }

    /*
     *   row 1   splash headline        │  chooser
     *   row 2   service cards          │  (same chooser, spanning)
     *   row 3   marquee, full width
     *   row 4   policy strip, full width
     */
    body.is-landing .jsm-splash::before,
    body.is-landing .jsm-splash-media {
        /* Behind the headline and the chooser, but not the cards. */
        grid-area: 1 / 1 / 3 / -1;
    }

    body.is-landing .jsm-splash-body {
        grid-area: 1 / 1 / 2 / 2;
        min-height: 0;
    }

    /* Opaque, which is what hides the photograph behind this row. */
    body.is-landing .jsm-s-products {
        grid-area: 2 / 1 / 3 / 2;
        padding: clamp(1.75rem, 3vw, 2.75rem) clamp(1.5rem, 4vw, 3.25rem);
        background: var(--jsm-white);
    }

    body.is-landing .jsm-audience    { grid-area: 1 / 2 / 3 / -1; }
    body.is-landing .jsm-ticker      { grid-area: 3 / 1 / 4 / -1; }
    body.is-landing .jsm-splash-foot { grid-area: 4 / 1 / 5 / -1; }

    /* Both strips run edge to edge now, so the content measure comes off. */
    body.is-landing .jsm-ticker-inner,
    body.is-landing .jsm-splash-foot-inner {
        max-width: none;
    }
}

/*
 * The cards keep their own padding when stacked; only the content measure goes,
 * so they fill the column rather than sitting in a 1240px well inside it.
 */
body.is-landing .jsm-s-products {
    background: var(--jsm-white);
}

body.is-landing .jsm-s-products > .wrap {
    max-width: none;
}

/* ------------------------------------------------------------- Gold splash */

/*
 * The photograph is graded rather than merely dimmed: a touch of sepia and a
 * lift in brightness is what makes the warm wash above it read as one image
 * instead of a yellow film laid over a cold one.
 */
.jsm-splash--gold .jsm-splash-media {
    filter: sepia(.4) saturate(.8) brightness(1.12) contrast(.94);
}

.jsm-splash--gold::before {
    background:
        /*
         * Shade for the white type, in bronze rather than navy. A navy veil
         * over a gold-graded photograph pulls the left third back to blue and
         * the two halves of the image stop looking like one picture.
         */
        linear-gradient(90deg, rgba(64, 44, 16, .46) 0%, rgba(64, 44, 16, .16) 46%, rgba(64, 44, 16, 0) 72%),
        linear-gradient(170deg, rgba(242, 179, 7, .38) 0%, rgba(240, 169, 43, .28) 52%, rgba(255, 238, 204, .22) 100%);
}

/* A solid plate, not the glassy outline the navy scheme uses. */
.jsm-splash--gold .jsm-splash-badge {
    border-color: var(--jsm-navy);
    border-radius: 5px;
    color: var(--jsm-white);
    background: var(--jsm-navy);
    backdrop-filter: none;
}

.jsm-splash--gold .jsm-splash-lead {
    color: rgba(255, 255, 255, .92);
    text-shadow: 0 1px 10px rgba(26, 46, 74, .45);
}

/* -------------------------------------------------------- Gold marquee strip */

.jsm-splash--gold .jsm-ticker {
    border-block: 1px solid var(--jsm-line);
    background: var(--jsm-white);
    backdrop-filter: none;
}

.jsm-splash--gold .jsm-ticker-inner {
    /* The label is flush to the page edge, so the row carries no gutter. */
    --jsm-gutter: 0;
    gap: 0;
    padding-block: 0;
}

.jsm-splash--gold .jsm-ticker-label {
    align-self: stretch;
    display: flex;
    align-items: center;
    padding: .9em 1.15em;
    border-radius: 0;
    color: var(--jsm-navy-d);
    background: var(--jsm-yellow);
}

.jsm-splash--gold .jsm-ticker-viewport {
    padding-inline: 1.35rem;
}

.jsm-splash--gold .jsm-ticker-item {
    color: var(--jsm-navy);
}

.jsm-splash--gold .jsm-ticker-item::before {
    color: var(--jsm-blue-l);
}

.jsm-splash--gold .jsm-ticker-item a,
.jsm-splash--gold .jsm-ticker-item span {
    color: var(--jsm-navy);
}

.jsm-splash--gold .jsm-ticker-item a:hover,
.jsm-splash--gold .jsm-ticker-item a:focus {
    color: var(--jsm-blue);
}

.jsm-splash--gold .jsm-ticker-pause {
    margin-right: 1.1rem;
    border-color: var(--jsm-line);
    color: var(--jsm-navy);
}

.jsm-splash--gold .jsm-ticker-pause:hover,
.jsm-splash--gold .jsm-ticker-pause:focus {
    background: var(--jsm-surface);
}

/* ------------------------------------------------------- Gold policy strip */

.jsm-splash--gold .jsm-splash-foot {
    background: var(--jsm-navy);
    backdrop-filter: none;
}

/*
 * Three tracks with nothing in the third: that is what centres the social row
 * against the page rather than against the text beside it.
 */
.jsm-splash--gold .jsm-splash-foot-inner {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    padding-block: 1.15rem;
}

/* Outlined discs, as the design draws them on the navy strip. */
.jsm-splash--gold .jsm-splash-social a {
    width: 1.95rem;
    height: 1.95rem;
    border: 1px solid rgba(255, 255, 255, .34);
    background: none;
}

.jsm-splash--gold .jsm-splash-social a:hover,
.jsm-splash--gold .jsm-splash-social a:focus {
    border-color: rgba(255, 255, 255, .7);
    background: rgba(255, 255, 255, .14);
}

@media (max-width: 40rem) {
    .jsm-splash--gold .jsm-splash-foot-inner {
        grid-template-columns: minmax(0, 1fr);
        justify-items: start;
        gap: .9rem;
    }
}

/* ------------------------------------------------- Main products & services */

.jsm-s-products .jsm-band-title {
    margin: 0 0 1.25rem;
    font-size: clamp(1.25rem, 2.2vw, 1.6rem);
}

.jsm-products {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: 1rem;
}

/*
 * The gold rule along the top is the card's whole decoration, so it is a border
 * rather than a pseudo-element: it keeps its place if a card grows a different
 * background later.
 */
.jsm-product {
    display: flex;
    flex-direction: column;
    gap: .7rem;
    padding: 1.1rem 1.1rem 1.25rem;
    border: 1px solid var(--jsm-line);
    border-top: 3px solid var(--jsm-yellow);
    border-radius: 0 0 var(--jsm-radius-sm) var(--jsm-radius-sm);
    background: var(--jsm-white);
    box-shadow: var(--jsm-shadow);
}

/*
 * The agency's service marks are wide lockups — around 360x150 — so the cap is
 * on height and the width follows. A fixed minimum keeps the three cards'
 * text on the same line whatever shape each mark turns out to be.
 */
.jsm-product-mark {
    display: flex;
    align-items: center;
    min-height: 3.4rem;
}

.jsm-product-mark img {
    max-width: 100%;
    max-height: 3.4rem;
    width: auto;
    object-fit: contain;
}

.jsm-product-name {
    margin: 0;
    font-family: var(--jsm-font-head);
    font-weight: 700;
    color: var(--jsm-navy);
}

.jsm-product-body {
    flex: 1 1 auto;
    margin: 0;
    font-size: .88rem;
    line-height: 1.55;
    color: var(--jsm-ink);
}

.jsm-s-products .jsm-product-go {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    font-weight: 600;
    font-size: .88rem;
    color: var(--jsm-blue);
    text-decoration: none;
}

.jsm-s-products .jsm-product-go:hover,
.jsm-s-products .jsm-product-go:focus {
    color: var(--jsm-blue-d);
    text-decoration: underline;
}

.jsm-product-chev {
    font-size: 1.2em;
    line-height: 0;
}

/* ---------------------------------------------- Chooser as a panel on the photo */

/*
 * Only the utility strip and the agency mark keep a white plate; everything
 * below floats on the photograph. The panel supplies its own padding, so the
 * wrapper gives all of it up.
 */
.jsm-audience--photo {
    position: relative;
    gap: 0;
    padding: 0;
    background: none;
}

/*
 * The announcement flag, stood on its end against the page's right edge —
 * inside the panel, not hanging off it: translated outwards it pushed past the
 * document width and put a horizontal scrollbar on the page.
 */
.jsm-audience-flag {
    position: absolute;
    top: 38%;
    right: 0;
    z-index: 2;
    padding: 1.1rem .5rem;
    border-radius: var(--jsm-radius-sm) 0 0 var(--jsm-radius-sm);
    writing-mode: vertical-rl;
    font-family: var(--jsm-font-head);
    font-size: .64rem;
    font-weight: 700;
    letter-spacing: .22em;
    text-transform: uppercase;
    color: var(--jsm-navy-d);
    background: var(--jsm-yellow);
    pointer-events: none;
}

/* Stacked, the panel is the full width and the flag would sit over the links. */
@media (max-width: 62rem) {
    .jsm-audience-flag {
        display: none;
    }
}

.jsm-audience--photo .jsm-audience-utility,
.jsm-audience--photo .jsm-audience-identity {
    background: var(--jsm-white);
    padding-inline: clamp(1.25rem, 2.6vw, 2.25rem);
}

.jsm-audience--photo .jsm-audience-utility {
    padding-block: 1.4rem 1.1rem;
    border-bottom: 0;
}

.jsm-audience--photo .jsm-audience-identity {
    padding-bottom: 1.6rem;
}

/* ------------------------------------------------------------ Yellow tab bar */

.jsm-audience--photo .jsm-audience-tabs {
    gap: 0;
    padding: 0;
    border-bottom: 0;
    background: var(--jsm-yellow);
}

.jsm-audience--photo .jsm-audience-tab {
    /* Equal shares of the bar, which is what makes it read as a bar. */
    flex: 1 1 0;
    padding: .95em .75em;
    border-radius: 0;
    font-size: .88rem;
    text-align: center;
    color: var(--jsm-navy);
}

.jsm-audience--photo .jsm-audience-tab:hover {
    color: var(--jsm-navy-d);
    background: rgba(255, 255, 255, .22);
}

.jsm-audience--photo .jsm-audience-tab.is-active {
    font-weight: 700;
    color: var(--jsm-navy-d);
    background: rgba(255, 255, 255, .38);
}

/* The red rule under the active tab is the light scheme's marker, not this one. */
.jsm-audience--photo .jsm-audience-tab.is-active::after {
    display: none;
}

/* ------------------------------------------------------- Links on the photo */

.jsm-audience--photo .jsm-audience-panels {
    padding: .35rem clamp(1.25rem, 2.6vw, 2.25rem) 2.25rem;
    background: rgba(14, 74, 134, .74);
}

.jsm-audience--photo .jsm-audience-panel > li + li {
    border-top-color: rgba(255, 255, 255, .17);
}

.jsm-audience--photo .jsm-audience-panel a {
    color: rgba(255, 255, 255, .94);
}

.jsm-audience--photo .jsm-audience-panel a:hover,
.jsm-audience--photo .jsm-audience-panel a:focus {
    color: var(--jsm-white);
    background: rgba(255, 255, 255, .13);
}

.jsm-audience--photo .jsm-audience-go {
    color: rgba(255, 255, 255, .6);
}

/* Inverted against the blue: white plate, red word. */
.jsm-audience--photo .jsm-audience-badge {
    color: var(--jsm-red);
    background: var(--jsm-white);
}

.jsm-audience--photo .jsm-audience-empty {
    color: rgba(255, 255, 255, .72);
}

/*
 * Stacked, the panel no longer has a photograph behind it — the splash ends
 * above it — so the blue has to stand on its own.
 */
@media (max-width: 62rem) {
    .jsm-audience--photo .jsm-audience-panels {
        background: var(--jsm-blue);
    }
}

/* ==========================================================================
   Login page — the intranet's sign-in door
   ========================================================================== */

:root {
    --jsm-login-from: #16205f;
    --jsm-login-mid:  #2b1e80;
    --jsm-login-to:   #553ac6;
}

body.is-login {
    background: var(--jsm-white);
}

.jsm-login {
    display: grid;
    grid-template-columns: 1.05fr .95fr;
    min-height: 100vh;
    min-height: 100dvh;
}

.jsm-login--brandwide { grid-template-columns: 1.4fr .95fr; }
.jsm-login--formwide  { grid-template-columns: .95fr 1.25fr; }

/* ------------------------------------------------------------ Brand panel */

.jsm-login-brand {
    position: relative;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    color: var(--jsm-white);
    background: linear-gradient(170deg,
        var(--jsm-login-from) 0%, var(--jsm-login-mid) 58%, var(--jsm-login-to) 100%);
}

/*
 * The photograph is a band, not a full-bleed background: the source is around
 * 537px wide, so stretched over a tall panel it goes soft. At band height it
 * sits close to its native size.
 */
.jsm-login-photo {
    position: relative;
    flex: 0 0 42%;
    min-height: 14rem;
    background-repeat: no-repeat;
    background-size: cover;

    /*
     * The band dissolves into the panel rather than stopping at an edge.
     *
     * It used to do this by fading a scrim to `--jsm-login-from`, which left a
     * visible seam: the panel behind is a 170deg gradient, so by the foot of
     * the photograph it is already most of the way to `--jsm-login-mid` and
     * the scrim was landing on a colour that is no longer there.
     *
     * Masking the photograph out instead means there is nothing to match. The
     * pixels go transparent and whatever the panel's gradient happens to be at
     * that height shows through, at every panel height and on both palettes.
     * A browser too old for masks ignores these two lines and gets the band
     * with its tint, which is what it had before.
     */
    /*
     * The axis leans a few degrees off vertical on purpose. A horizontal
     * dissolve still ends on a horizontal line, however soft; tilting it makes
     * the bottom of the photograph a shape, which is what stops it reading as
     * a crop.
     */
    -webkit-mask-image: linear-gradient(172deg, #000 0%, #000 58%, transparent 96%);
    mask-image: linear-gradient(172deg, #000 0%, #000 58%, transparent 96%);
}

/*
 * A light tint over the photograph, for cohesion with the panel. It no longer
 * has to reach solid at the foot — the mask above does that — so it stays a
 * tint the whole way down and the picture keeps its contrast.
 */
.jsm-login-photo::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--jsm-login-from) 34%, transparent) 0%,
        color-mix(in srgb, var(--jsm-login-from) 18%, transparent) 50%,
        color-mix(in srgb, var(--jsm-login-from) 46%, transparent) 100%);
}

/*
 * The geometry over the join.
 *
 * The dissolve above takes the hard edge off, but a fade on its own still
 * reads as a photograph running out. Shapes crossing the seam give the eye
 * something deliberate to read instead: a diagonal sweep along the line the
 * mask already leans on, and two soft discs straddling it — one in the gold
 * the brand uses for its tagline, one in white.
 *
 * All of it is one element and one `background`, so there is no markup for a
 * module's wording to collide with. It sits above the photograph and below the
 * text: `.jsm-login-brandbody` lifts itself with a z-index, and this keeps
 * `auto`, so paint order puts it between the two.
 *
 * Every layer is under 11% opacity. At brand weight these would be decoration
 * competing with the wordmark; at this weight they are texture.
 */
.jsm-login-brand::after {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    background:
        /*
         * A wedge off the foot of the panel, with one hard edge. The hard edge
         * is the point: everything else here is soft, and without a line the
         * eye has nothing to read as deliberate. It leans at the same angle as
         * the mask, so the two look like one decision.
         */
        linear-gradient(170deg,
            transparent 0 72%,
            rgba(255, 255, 255, .045) 72% 100%),
        /* A beam across the join, soft at both edges so it is light, not a stripe. */
        linear-gradient(104deg,
            transparent 0 40%,
            rgba(255, 255, 255, .018) 46%,
            rgba(255, 255, 255, .055) 51%,
            rgba(255, 255, 255, .018) 56%,
            transparent 62% 100%),
        /* Two discs straddling the seam, in the brand's gold and in white. */
        radial-gradient(34rem 34rem at 86% 44%,
            color-mix(in srgb, var(--jsm-gold) 11%, transparent), transparent 64%),
        radial-gradient(24rem 24rem at 6% 56%,
            rgba(255, 255, 255, .07), transparent 62%);
}

/* So the wording stays clear of the geometry above it. */
.jsm-login-brandbody {
    z-index: 1;
}

.jsm-login-brandbody {
    position: relative;
    flex: 1;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, .78fr);
    gap: 2.4rem;
    align-items: center;
    padding: 2rem clamp(1.5rem, 3.4vw, 3rem) 2.4rem;
}

/*
 * The brand side's wording arrives from a module, so the template cannot know
 * its markup — these rules dress whatever an editor puts there rather than a
 * fixed structure. Pitched at 0,1,1 so they beat the element defaults without
 * needing to out-specify a module's own classes.
 */
.jsm-login-brandmain :is(h1, h2, h3) {
    margin: 0 0 .5rem;
    font-family: var(--jsm-font-head);
    font-weight: 800;
    font-size: clamp(1.9rem, 3.1vw, 2.7rem);
    line-height: 1.08;
    letter-spacing: -.02em;
    max-width: 14ch;
    text-transform: uppercase;
    text-wrap: balance;
    color: var(--jsm-white);
}

.jsm-login-brandmain p {
    margin: 0 0 1rem;
    color: rgba(255, 255, 255, .82);
    max-width: 42ch;
}

.jsm-login-brandmain p:last-child {
    margin-bottom: 0;
}

.jsm-login-brandmain a {
    color: var(--jsm-white);
}

/*
 * The design's three-part opening: a small eyebrow over the name, then a gold
 * tagline under it. A module's editor marks them up with these classes.
 */
.jsm-login-brandmain .jsm-eyebrow {
    margin-bottom: .35rem;
    font-size: .95rem;
    font-weight: 500;
    letter-spacing: .02em;
    text-transform: none;
    color: rgba(255, 255, 255, .88);
}

.jsm-login-brandmain .jsm-tagline {
    margin-bottom: 1.6rem;
    font-weight: 600;
    font-size: 1rem;
    letter-spacing: .01em;
    color: var(--jsm-gold);
}

.jsm-login-brandmain .jsm-lead {
    padding-top: 1.4rem;
    border-top: 1px solid rgba(255, 255, 255, .18);
}

/*
 * The disclaimer, as the right column of the welcome block. A rule rather than
 * a filled band, so it reads as a footnote and not as another panel.
 */
.jsm-login-note {
    padding-left: 2.2rem;
    border-left: 1px solid rgba(255, 255, 255, .2);
}

.jsm-login-note :is(h1, h2, h3, h4, p.jsm-note-title) {
    margin: 0 0 .55rem;
    font-family: var(--jsm-font-body);
    font-size: .68rem;
    font-weight: 500;
    letter-spacing: .32em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, .62);
}

.jsm-login-note p {
    margin: 0;
    font-size: .78rem;
    line-height: 1.6;
    color: rgba(255, 255, 255, .82);
}

/* ------------------------------------------------------------- Form panel */

.jsm-login-panel {
    display: grid;
    place-items: center;
    padding: 2.5rem 2rem;
    background: var(--jsm-white);
}

.jsm-login-card {
    width: 100%;
    max-width: 25rem;
}

.jsm-login-card .jsm-login-title,
.jsm-login-card .page-header h1 {
    margin: 0 0 .3rem;
    font-family: var(--jsm-font-head);
    font-weight: 700;
    font-size: 1.7rem;
    letter-spacing: -.02em;
}

.jsm-login-sub {
    margin: 0 0 1.6rem;
    font-size: .95rem;
    color: var(--jsm-muted);
}

.jsm-login-image {
    max-width: 100%;
    height: auto;
    margin-bottom: 1.4rem;
}

/*
 * Joomla's system messages, as the design's compact inline alert rather than
 * the full-bleed banner Cassiopeia draws. This is the "please sign in first"
 * notice a visitor meets when they are bounced here from a protected page, so
 * it is the one piece of the card most people will actually read.
 */
/*
 * Joomla's system messages, as the design's compact inline alert rather than
 * the full-bleed banner Cassiopeia draws. This is the "please sign in first"
 * notice a visitor meets when they are bounced here from a protected page, so
 * it is the one piece of the card most people will actually read.
 *
 * Two shapes are covered. With scripting on, messages.js builds a
 * <joomla-alert> custom element; with it off, the server's <noscript> fallback
 * is a plain .alert div. Both are dressed, because the fallback is exactly the
 * case where a visitor most needs to be told what went wrong.
 */
.jsm-login-card #system-message-container {
    display: block;
    margin-bottom: 1.2rem;
}

.jsm-login-card #system-message-container:empty {
    margin-bottom: 0;
}

.jsm-login-card :is(joomla-alert, .alert) {
    display: flex;
    align-items: flex-start;
    gap: .55rem;
    margin: 0 0 .6rem;
    padding: .7rem .85rem;
    border: 1px solid var(--jsm-line);
    border-radius: 10px;
    background: var(--jsm-surface);
    font-size: .88rem;
    line-height: 1.5;
    color: var(--jsm-ink);
}

.jsm-login-card :is(joomla-alert, .alert):last-child {
    margin-bottom: 0;
}

/*
 * The heading is an icon plus a visually-hidden word — there is no visible
 * text in it — so it is sized as the glyph it actually is.
 */
.jsm-login-card .alert-heading {
    display: grid;
    place-items: center;
    flex: none;
    width: 1.2rem;
    height: 1.2rem;
    margin: .1rem 0 0;
    padding: 0;
    border-radius: 4px;
    color: inherit;
    font-size: 1rem;
    line-height: 1;
    /*
     * The fill stays Cassiopeia's — it is the severity badge, amber for a
     * warning and red for an error, and it is the only colour on the alert
     * that tells them apart. Only the size is taken in hand: left alone it is
     * a 42px block beside two lines of type.
     */
}

.jsm-login-card .alert-heading > span {
    display: block;
    width: 1em;
    height: 1em;
}

.jsm-login-card .alert-wrapper {
    flex: 1 1 auto;
    min-width: 0;
}

.jsm-login-card .alert-message :is(p, ul) {
    margin: 0;
}

.jsm-login-card .alert-message ul {
    padding-left: 1.1rem;
}

/* Warning and error carry the red; the body text stays readable ink. */
.jsm-login-card :is(joomla-alert[type="warning"], joomla-alert[type="danger"],
                    .alert-warning, .alert-danger, .alert-error) {
    border-color: color-mix(in srgb, var(--jsm-red) 35%, var(--jsm-line));
    background: color-mix(in srgb, var(--jsm-red) 7%, var(--jsm-white));
    color: var(--jsm-red);
}

.jsm-login-card :is(joomla-alert[type="success"], .alert-success) {
    border-color: color-mix(in srgb, #1f9d55 35%, var(--jsm-line));
    background: color-mix(in srgb, #1f9d55 7%, var(--jsm-white));
}

.jsm-login-card .alert-message {
    color: var(--jsm-ink);
}

.jsm-login-card .joomla-alert--close {
    flex: none;
    margin-left: auto;
    padding: 0 .15rem;
    border: 0;
    font-size: 1.15rem;
    line-height: 1;
    color: inherit;
    background: none;
    opacity: .55;
    cursor: pointer;
}

.jsm-login-card .joomla-alert--close:hover,
.jsm-login-card .joomla-alert--close:focus {
    opacity: 1;
}

.jsm-login-field {
    margin-bottom: 1rem;
}

.jsm-login-field label {
    display: block;
    margin-bottom: .35rem;
    font-size: .83rem;
    font-weight: 600;
    color: var(--jsm-muted);
}

/* Joomla marks a required field with a star inside the label. */
.jsm-login-field .star {
    color: var(--jsm-red);
}

.jsm-login-control {
    position: relative;
    display: flex;
    align-items: center;
}

.jsm-login-lead {
    position: absolute;
    left: .85rem;
    color: var(--jsm-muted);
    pointer-events: none;
}

.jsm-login-control input {
    width: 100%;
    font: inherit;
    font-size: .95rem;
    color: inherit;
    background: var(--jsm-surface);
    border: 1.5px solid var(--jsm-line);
    border-radius: 10px;
    /* Room for the glyph on the left and the reveal button on the right. */
    padding: .8rem 2.9rem;
    outline: none;
    transition: border-color .15s ease, box-shadow .15s ease, background-color .15s ease;
}

.jsm-login-control input::placeholder {
    color: var(--jsm-muted);
}

.jsm-login-control input:focus {
    border-color: var(--jsm-blue);
    background: var(--jsm-white);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--jsm-blue) 16%, transparent);
}

/*
 * Joomla's password field brings its own reveal button, wrapped in an
 * .input-group. Rather than suppress it and write another, the group is
 * flattened and the button dressed as the design's.
 */
/*
 * Joomla nests the password field twice — .password-group wrapping an
 * .input-group — and both are block boxes. Flattening only the inner one left
 * the field sized by its `size="25"` attribute inside the outer block, so both
 * have to go.
 */
.jsm-login-control .password-group,
.jsm-login-control .input-group {
    display: contents;
}

/*
 * With the group flattened, the input becomes a flex child of the control and
 * sizes to its `size` attribute rather than the row. width:100% does not help
 * — a flex item's base size wins — so it has to be told to grow.
 */
.jsm-login-control input {
    flex: 1 1 auto;
    min-width: 0;
}

.jsm-login-control .input-password-toggle {
    position: absolute;
    right: .45rem;
    display: grid;
    place-items: center;
    width: 2.1rem;
    height: 2.1rem;
    padding: 0;
    border: 0;
    border-radius: 8px;
    color: var(--jsm-muted);
    background: none;
    cursor: pointer;
}

.jsm-login-control .input-password-toggle:hover,
.jsm-login-control .input-password-toggle:focus {
    color: var(--jsm-blue);
    background: color-mix(in srgb, var(--jsm-blue) 12%, transparent);
}

.jsm-login-control .input-password-toggle .icon-eye-close::before,
.jsm-login-control .input-password-toggle .icon-eye::before {
    font-size: 1rem;
}

.jsm-login-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin: .2rem 0 1.4rem;
    font-size: .88rem;
}

.jsm-login-check {
    display: flex;
    align-items: center;
    gap: .5rem;
    cursor: pointer;
    color: var(--jsm-muted);
}

.jsm-login-check input {
    width: 1.05rem;
    height: 1.05rem;
    accent-color: var(--jsm-blue);
}

.jsm-login-card .jsm-login-forgot {
    font-weight: 600;
    text-decoration: none;
}

.jsm-login-card .jsm-login-forgot:hover,
.jsm-login-card .jsm-login-forgot:focus {
    text-decoration: underline;
}

.jsm-login-submit {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .55rem;
    width: 100%;
    padding: .85rem 1rem;
    font: inherit;
    font-family: var(--jsm-font-head);
    font-weight: 700;
    font-size: 1rem;
    color: var(--jsm-white);
    background: var(--jsm-blue);
    border: 0;
    border-radius: 12px;
    cursor: pointer;

    /*
     * The shadow is tinted with the button's own blue rather than grey. A grey
     * shadow under a saturated button reads as dirt; the same hue reads as
     * lift.
     */
    box-shadow: 0 1px 2px rgba(14, 74, 134, .18), 0 8px 18px -10px rgba(14, 74, 134, .55);
    transition: background-color .15s ease, box-shadow .15s ease, transform .1s ease;
}

.jsm-login-submit:hover {
    background: var(--jsm-blue-d);
    box-shadow: 0 2px 4px rgba(14, 74, 134, .22), 0 14px 26px -12px rgba(14, 74, 134, .6);
}

.jsm-login-submit:active {
    transform: translateY(1px);
    box-shadow: 0 1px 2px rgba(14, 74, 134, .2);
}

@media (prefers-reduced-motion: reduce) {
    .jsm-login-submit {
        transition: background-color .15s ease, box-shadow .15s ease;
    }

    .jsm-login-submit:active {
        transform: none;
    }
}

.jsm-login-extra {
    width: 100%;
    margin-bottom: .75rem;
    justify-content: center;
    color: var(--jsm-ink);
    border-color: var(--jsm-line);
}

.jsm-login-extra:hover,
.jsm-login-extra:focus {
    color: var(--jsm-blue);
    background: var(--jsm-surface);
    border-color: var(--jsm-blue);
}

/* A rule either side of a short caption. */
.jsm-login-sep {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: .8rem;
    margin: 1.5rem 0 1.1rem;
    font-size: .78rem;
    color: var(--jsm-muted);
}

.jsm-login-sep::before,
.jsm-login-sep::after {
    content: '';
    height: 1px;
    background: var(--jsm-line);
}

/* --------------------------------------------------- Already signed in */

/*
 * The same card, with the form replaced by a way out and a way on. The
 * sign-out button is the page's one action, so it keeps the submit's weight.
 */
.jsm-login-signedin .jsm-login-sub {
    margin-bottom: 1.8rem;
}

.jsm-login-signedin .jsm-login-sub :is(p, ul) {
    margin: 0;
}

.jsm-login-onward {
    margin-top: 1.1rem;
    padding-top: 1.1rem;
    border-top: 1px solid var(--jsm-line);
}

.jsm-login-links,
.jsm-login-help {
    display: grid;
    gap: .5rem;
    font-size: .88rem;
}

.jsm-login-help {
    margin-top: .5rem;
}

/*
 * The side links are cards, not text.
 *
 * As plain rows they read as small print next to a full-width primary button,
 * and on a sign-in page they are the things someone reaches for precisely when
 * the primary action has not worked. A bordered, rounded container with its
 * own icon and a chevron gives each one a target and says it is a place to go.
 *
 * Three columns: the icon chip, the wording, the chevron. The wording column
 * is the one that gives, so a long label wraps inside the card rather than
 * pushing the chevron out of it.
 */
.jsm-login-card :is(.jsm-login-links, .jsm-login-help) a {
    display: grid;
    grid-template-columns: 2rem minmax(0, 1fr) auto;
    gap: .75rem;
    align-items: center;
    padding: .7rem .85rem;
    border: 1px solid var(--jsm-line);
    border-radius: 12px;
    background: var(--jsm-white);
    box-shadow: 0 1px 2px rgba(33, 57, 92, .05);
    font-weight: 500;
    color: var(--jsm-ink);
    text-decoration: none;
    transition: border-color .15s ease, box-shadow .15s ease, transform .1s ease;
}

.jsm-login-card :is(.jsm-login-links, .jsm-login-help) a:hover,
.jsm-login-card :is(.jsm-login-links, .jsm-login-help) a:focus-visible {
    border-color: color-mix(in srgb, var(--jsm-blue) 45%, transparent);
    box-shadow: 0 2px 4px rgba(33, 57, 92, .07), 0 10px 20px -14px rgba(33, 57, 92, .5);
    transform: translateY(-1px);
    color: var(--jsm-blue);
}

/* The icon sits in a tinted chip, so it reads as a cue rather than a bullet. */
.jsm-login-card :is(.jsm-login-links, .jsm-login-help) a svg {
    box-sizing: border-box;
    flex: none;
    width: 2rem;
    height: 2rem;
    padding: .45rem;
    border-radius: 9px;
    color: var(--jsm-blue);
    background: color-mix(in srgb, var(--jsm-blue) 10%, transparent);
    transition: background-color .15s ease;
}

.jsm-login-card :is(.jsm-login-links, .jsm-login-help) a:hover svg,
.jsm-login-card :is(.jsm-login-links, .jsm-login-help) a:focus-visible svg {
    background: color-mix(in srgb, var(--jsm-blue) 18%, transparent);
}

/* The chevron: the affordance that says this goes somewhere. */
.jsm-login-card :is(.jsm-login-links, .jsm-login-help) a::after {
    content: "\f054";
    font-family: "Font Awesome 6 Free";
    font-size: .68rem;
    font-weight: 900;
    line-height: 1;
    color: var(--jsm-muted);
    transition: transform .15s ease, color .15s ease;
}

.jsm-login-card :is(.jsm-login-links, .jsm-login-help) a:hover::after,
.jsm-login-card :is(.jsm-login-links, .jsm-login-help) a:focus-visible::after {
    transform: translateX(2px);
    color: var(--jsm-blue);
}

@media (prefers-reduced-motion: reduce) {
    .jsm-login-card :is(.jsm-login-links, .jsm-login-help) a,
    .jsm-login-card :is(.jsm-login-links, .jsm-login-help) a::after {
        transition: border-color .15s ease, box-shadow .15s ease, color .15s ease;
    }

    .jsm-login-card :is(.jsm-login-links, .jsm-login-help) a:hover,
    .jsm-login-card :is(.jsm-login-links, .jsm-login-help) a:hover::after,
    .jsm-login-card :is(.jsm-login-links, .jsm-login-help) a:focus-visible,
    .jsm-login-card :is(.jsm-login-links, .jsm-login-help) a:focus-visible::after {
        transform: none;
    }
}

/* The help block comes from a module, so it may arrive as a list. */
.jsm-login-help ul {
    display: grid;
    gap: .5rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

/* ----------------------------------------------------------- Narrow login */

@media (max-width: 60rem) {
    .jsm-login,
    .jsm-login--brandwide,
    .jsm-login--formwide {
        grid-template-columns: minmax(0, 1fr);
        min-height: 0;
    }

    .jsm-login-photo {
        flex: 0 0 auto;
        min-height: 9rem;
    }

    .jsm-login-brandbody {
        grid-template-columns: minmax(0, 1fr);
        gap: 1.3rem;
        padding: 1.4rem 1.5rem 1.8rem;
    }

    .jsm-login-brandmain :is(h1, h2, h3) {
        font-size: 1.6rem;
    }

    .jsm-login-note {
        padding-left: 0;
        padding-top: 1.2rem;
        border-left: 0;
        border-top: 1px solid rgba(255, 255, 255, .2);
    }

    .jsm-login-note p {
        font-size: .74rem;
    }

    .jsm-login-panel {
        padding: 2rem 1.25rem 2.5rem;
    }
}

/* ==========================================================================
   Intranet dashboard
   ========================================================================== */

:root {
    --jsm-dash-width: 1440px;
    --jsm-dash-side:  17rem;
    --jsm-dash-rail:  20rem;
}

body.is-intranet {
    background: var(--jsm-surface);
}

/* ------------------------------------------------------------------- Bar */

.jsm-dash-bar {
    position: sticky;
    top: 0;
    z-index: 20;
    background: var(--jsm-white);
    border-bottom: 1px solid var(--jsm-line);
}

.jsm-dash-bar-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    max-width: var(--jsm-dash-width);
    margin-inline: auto;
    padding: .85rem clamp(1rem, 2.5vw, 2rem);
}

.jsm-dash-brand {
    display: inline-flex;
    align-items: center;
    flex: none;
    text-decoration: none;
}

.jsm-dash-brand img {
    max-height: 2.6rem;
    width: auto;
}

.jsm-dash-wordmark {
    font-family: var(--jsm-font-head);
    font-weight: 700;
    font-size: 1.05rem;
    color: var(--jsm-navy);
}

/*
 * Whatever an editor puts in the bar — a search box, a user menu, a
 * notifications bell. It is laid out as a row and otherwise left alone.
 */
.jsm-dash-tools {
    display: flex;
    align-items: center;
    gap: .6rem;
    min-width: 0;
}

.jsm-dash-tools > * {
    margin: 0;
}

/* --------------------------------------------------------- Three columns */

.jsm-dash-shell {
    display: grid;
    grid-template-columns: var(--jsm-dash-side) minmax(0, 1fr) var(--jsm-dash-rail);
    align-items: start;
    gap: clamp(1rem, 1.6vw, 1.5rem);
    max-width: var(--jsm-dash-width);
    margin-inline: auto;
    padding: clamp(1rem, 2vw, 1.75rem) clamp(1rem, 2.5vw, 2rem) 3rem;
}

/* A column with nothing published in it does not reserve space. */
.jsm-dash--noside {
    grid-template-columns: minmax(0, 1fr) var(--jsm-dash-rail);
}

.jsm-dash--norail {
    grid-template-columns: var(--jsm-dash-side) minmax(0, 1fr);
}

.jsm-dash--noside.jsm-dash--norail {
    grid-template-columns: minmax(0, 1fr);
}

.jsm-dash-side,
.jsm-dash-rail,
.jsm-dash-main {
    display: grid;
    align-content: start;
    gap: clamp(1rem, 1.4vw, 1.25rem);
    min-width: 0;
}

/*
 * The two outer columns follow the page rather than the scroll by default.
 * Sticking them is tempting on a dashboard, but the left one holds the
 * navigation and the right the day's odds and ends — both are taller than a
 * laptop viewport once a site fills them.
 */

/* ------------------------------------------------------------------ Cards */

.jsm-dash-card {
    position: relative;
    padding: clamp(1rem, 1.5vw, 1.35rem);
    border: 1px solid var(--jsm-line);
    border-radius: var(--jsm-radius-md);
    background: var(--jsm-card-bg, var(--jsm-white));
    box-shadow: var(--jsm-shadow);
}

/* --------------------------------------------- Cards with a background */

/*
 * A card's background comes from the module's Note field — a colour or an
 * image path — which the chrome turns into the two custom properties below.
 * See `html/layouts/chromes/jsmdash.php`.
 *
 * The named classes here are the same thing by another route: a site that
 * would rather pick from a palette than type a hex puts one of them in the
 * module's class suffix.
 */
.jsm-card-blue   { --jsm-card-bg: #4c6ef5; }
.jsm-card-indigo { --jsm-card-bg: #2f3a8f; }
.jsm-card-violet { --jsm-card-bg: #7950f2; }
.jsm-card-rose   { --jsm-card-bg: #e8685d; }
.jsm-card-mint   { --jsm-card-bg: #12b886; }
.jsm-card-amber  { --jsm-card-bg: #f59f00; }
.jsm-card-navy   { --jsm-card-bg: var(--jsm-navy); }
.jsm-card-sky    { --jsm-card-bg: #dfe8ff; }

/* The pale ones keep dark text; the rest need light. */
.jsm-dash-card:is(.jsm-card-blue, .jsm-card-indigo, .jsm-card-violet, .jsm-card-rose,
                  .jsm-card-mint, .jsm-card-amber, .jsm-card-navy) {
    --jsm-card-on: var(--jsm-white);
    border-color: transparent;
}

.jsm-dash-card.jsm-card-sky {
    border-color: transparent;
}

.jsm-dash-card.jsm-card-filled {
    border-color: transparent;
}

.jsm-dash-card.jsm-card-dark {
    --jsm-card-on: var(--jsm-white);
}

/*
 * Everything in a filled card inherits one text colour rather than each rule
 * naming it, so a card dropped on a new background does not need its contents
 * revisited.
 */
.jsm-dash-card:is(.jsm-card-filled, .jsm-card-blue, .jsm-card-indigo, .jsm-card-violet,
                  .jsm-card-rose, .jsm-card-mint, .jsm-card-amber, .jsm-card-navy, .jsm-card-sky) {
    color: var(--jsm-card-on, var(--jsm-ink));
}

.jsm-dash-card.jsm-card-dark :is(.jsm-dash-cardhead, h2, h3, h4, b, strong) {
    color: var(--jsm-white);
}

.jsm-dash-card.jsm-card-dark :is(p, span, li, time, small) {
    color: rgba(255, 255, 255, .82);
}

.jsm-dash-card.jsm-card-dark a {
    color: var(--jsm-white) !important;
}

.jsm-dash-card.jsm-card-dark :is(hr, li + li) {
    border-color: rgba(255, 255, 255, .24);
}

/* A picture needs a scrim, or the text lands on whatever the photo happens to be. */
.jsm-dash-card.jsm-card-art {
    overflow: hidden;
    background:
        linear-gradient(180deg, rgba(14, 20, 46, .25), rgba(14, 20, 46, .72)),
        var(--jsm-card-art) center / cover no-repeat;
}

/*
 * The light variant, for artwork that was drawn to be a background rather than
 * a photograph pressed into service. No scrim and no forced white text, so the
 * card goes on using the same ink as the ones beside it — add
 * `jsm-card-art--soft` to the module's class suffix.
 *
 * It anchors to the top because that is where this kind of illustration puts
 * its subject, and a card in the rail is far narrower than the drawing.
 */
/*
 * Artwork of this kind puts its subject along the top — bunting, a skyline, a
 * wave — so the card reserves a band for it and starts its content below.
 * Without that the heading lands in the middle of the picture, which is where
 * the picture is busiest.
 */
.jsm-dash-card.jsm-card-art--soft {
    padding-top: var(--jsm-card-art-top, 3.4rem);
    overflow: hidden;
    border-color: transparent;
    background: var(--jsm-card-art) top center / cover no-repeat;
}

/*
 * The rows sit on the artwork, so their separators have to be the ink's own
 * colour rather than the line colour, which was picked to show on white.
 */
.jsm-dash-card.jsm-card-art--soft li + li {
    border-color: rgba(33, 57, 92, .16);
}

/*
 * An illustration card: the module's own image fills the card edge to edge,
 * with nothing of the card showing around it.
 */
.jsm-dash-card.jsm-card-bleed {
    overflow: hidden;
    padding: 0;
}

.jsm-dash-card.jsm-card-bleed :is(img, picture, figure) {
    display: block;
    width: 100%;
    height: 100%;
    margin: 0;
    object-fit: cover;
}

.jsm-dash-card.jsm-card-bleed .jsm-dash-cardhead {
    position: absolute;
    z-index: 1;
    inset: clamp(1rem, 1.5vw, 1.35rem) auto auto clamp(1rem, 1.5vw, 1.35rem);
    color: var(--jsm-white);
    text-shadow: 0 1px 12px rgba(14, 20, 46, .6);
}

.jsm-dash-bare {
    min-width: 0;
}

.jsm-dash-cardhead {
    margin: 0 0 .9rem;
    font-family: var(--jsm-font-head);
    font-size: .95rem;
    font-weight: 700;
    letter-spacing: -.01em;
    color: var(--jsm-navy);
}

.jsm-dash-bare .jsm-dash-cardhead {
    margin-bottom: .6rem;
    font-size: .72rem;
    font-weight: 600;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--jsm-muted);
}

/*
 * The component's card is only there when the page has a component worth
 * showing. On the dashboard itself com_content renders an empty wrapper —
 * `<div class="blog-featured"></div>` — which is not nothing, so testing for
 * any child at all left a bordered box sitting in the middle of the column
 * looking like a fault.
 *
 * So the test is for something that actually paints. A component rendering
 * only an empty container is treated as having rendered nothing, which is what
 * it looks like to a reader.
 */
.jsm-dash-component:not(:has(:is(h1, h2, h3, h4, p, img, table, ul, ol, dl, form, article, a, iframe, .page-header))) {
    display: none;
}

/* -------------------------------------------------- What modules bring in */

/*
 * These dress whatever an extension renders, rather than a structure the
 * template controls — so they are deliberately shallow and easy to override
 * from a module's own class suffix.
 */
.jsm-dash-card :is(h3, h4) {
    margin: 0 0 .4rem;
    font-size: .92rem;
}

.jsm-dash-card p {
    margin: 0 0 .6rem;
}

.jsm-dash-card > :last-child {
    margin-bottom: 0;
}

.jsm-dash-card ul,
.jsm-dash-card ol {
    margin: 0;
    padding: 0;
    list-style: none;
}

.jsm-dash-card li + li {
    margin-top: .55rem;
    padding-top: .55rem;
    border-top: 1px solid var(--jsm-line);
}

.jsm-dash-side .jsm-dash-nav a,
.jsm-dash-card li > a {
    color: var(--jsm-ink);
    text-decoration: none;
}

.jsm-dash-side .jsm-dash-nav a:hover,
.jsm-dash-card li > a:hover,
.jsm-dash-card li > a:focus-visible {
    color: var(--jsm-blue);
}

/* ------------------------------------------------------- The side's menu */

.jsm-dash-nav ul {
    display: grid;
    gap: .15rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.jsm-dash-nav li + li {
    margin: 0;
    padding: 0;
    border: 0;
}

.jsm-dash-nav a {
    display: block;
    padding: .55rem .75rem;
    border-radius: var(--jsm-radius-sm);
    font-size: .92rem;
    font-weight: 500;
}

.jsm-dash-nav a:hover,
.jsm-dash-nav a:focus-visible {
    background: var(--jsm-surface);
}

.jsm-dash-nav .current > a,
.jsm-dash-nav .is-active > a,
.jsm-dash-nav .active > a {
    font-weight: 700;
    color: var(--jsm-blue);
    background: color-mix(in srgb, var(--jsm-blue) 10%, transparent);
}

/* ==========================================================================
   Inside the cards
   ==========================================================================

   Every block on the dashboard is a module, so each of these dresses what an
   extension renders rather than markup the template owns. They are keyed to
   the class a module carries in its own class suffix — so swapping one
   extension for another means moving a suffix, not editing CSS, and a block
   the site does not want simply loses its module.
   ========================================================================== */

/*
 * The design gives every icon tile its own colour, cycling through six. Doing
 * that per item would mean a colour field on every menu item and somebody
 * choosing them; cycling on :nth-child gives the same effect for nothing and
 * stays right when items are added, removed or reordered.
 */
.jsm-dash-tinted > :nth-child(6n + 1) { --jsm-tint: #4c6ef5; }
.jsm-dash-tinted > :nth-child(6n + 2) { --jsm-tint: #12b886; }
.jsm-dash-tinted > :nth-child(6n + 3) { --jsm-tint: #f59f00; }
.jsm-dash-tinted > :nth-child(6n + 4) { --jsm-tint: #f03e5e; }
.jsm-dash-tinted > :nth-child(6n + 5) { --jsm-tint: #7950f2; }
.jsm-dash-tinted > :nth-child(6n + 6) { --jsm-tint: #15aabf; }

:is(.jsm-dash-apps, .jsm-dash-pins, .jsm-dash-quick, .jsm-dash-feed) :is(ul, ol) {
    --jsm-tint: var(--jsm-blue);
}

:is(.jsm-dash-apps, .jsm-dash-pins, .jsm-dash-quick, .jsm-dash-feed) li:nth-child(6n + 1) { --jsm-tint: #4c6ef5; }
:is(.jsm-dash-apps, .jsm-dash-pins, .jsm-dash-quick, .jsm-dash-feed) li:nth-child(6n + 2) { --jsm-tint: #12b886; }
:is(.jsm-dash-apps, .jsm-dash-pins, .jsm-dash-quick, .jsm-dash-feed) li:nth-child(6n + 3) { --jsm-tint: #f59f00; }
:is(.jsm-dash-apps, .jsm-dash-pins, .jsm-dash-quick, .jsm-dash-feed) li:nth-child(6n + 4) { --jsm-tint: #f03e5e; }
:is(.jsm-dash-apps, .jsm-dash-pins, .jsm-dash-quick, .jsm-dash-feed) li:nth-child(6n + 5) { --jsm-tint: #7950f2; }
:is(.jsm-dash-apps, .jsm-dash-pins, .jsm-dash-quick, .jsm-dash-feed) li:nth-child(6n + 6) { --jsm-tint: #15aabf; }

/*
 * The icon chip. The template's menu override renders a menu item's Icon Class
 * as `<span class="jsm-menu-icon fa-solid fa-house">` inside the link, so that
 * span is what gets drawn as the chip.
 */
.jsm-dash-chip,
:is(.jsm-dash-apps, .jsm-dash-pins, .jsm-dash-quick) .jsm-menu-icon,
.jsm-dash-feed li::before,
.jsm-dash-bday li::before {
    display: grid;
    flex: none;
    place-items: center;
    width: 2rem;
    height: 2rem;
    padding: 0 !important;
    border-radius: var(--jsm-radius-sm);
    font-size: .85rem;
    line-height: 1;
    color: var(--jsm-tint, var(--jsm-blue));
    background: color-mix(in srgb, var(--jsm-tint, var(--jsm-blue)) 13%, transparent);
}

/* -------------------------------------------------- Bar: search, account */

/* mod_finder, as the bar's omnibox. */
.jsm-dash-search .mod-finder {
    margin: 0;
}

.jsm-dash-search .mod-finder__search {
    display: flex;
    gap: .35rem;
    align-items: center;
    padding: .3rem .3rem .3rem .85rem;
    border: 1px solid var(--jsm-line);
    border-radius: 999px;
    background: var(--jsm-surface);
}

.jsm-dash-search .mod-finder__search:focus-within {
    border-color: var(--jsm-blue);
    background: var(--jsm-white);
}

.jsm-dash-search .js-finder-search-query {
    min-width: 0;
    flex: 1 1 auto;
    height: auto;
    padding: .3rem 0;
    border: 0;
    background: none;
    font: inherit;
    font-size: .88rem;
    box-shadow: none;
}

.jsm-dash-search .js-finder-search-query:focus {
    outline: none;
    box-shadow: none;
}

/*
 * The search button is a circle, so its label has to go — but it is the
 * button's accessible name, so it is hidden by making it unpaintable rather
 * than removed, and a magnifier is drawn in its place.
 */
.jsm-dash-search .btn {
    display: grid;
    flex: none;
    place-items: center;
    width: 2rem;
    height: 2rem;
    padding: 0;
    overflow: hidden;
    border: 0;
    border-radius: 999px;
    background: var(--jsm-blue);
    font-size: 0;
    color: transparent;
}

.jsm-dash-search .btn > * {
    display: none;
}

.jsm-dash-search .btn::before {
    content: "\f002";
    font-family: "Font Awesome 6 Free";
    font-size: .78rem;
    font-weight: 900;
    color: var(--jsm-white);
}

.jsm-dash-search .mod-finder__advanced {
    display: none;
}

/*
 * The bar's logo strip. Each mark arrives at its own natural size — the
 * MyGovUC lockup is wide, the social marks are square — so the row sets a
 * common height and lets each keep its own width.
 */
.jsm-dash-links .jsm-menu {
    display: flex;
    /* Stated, because the mobile drawer also styles .jsm-menu. */
    flex-direction: row;
    flex-wrap: wrap;
    gap: .15rem;
    align-items: center;
}

.jsm-dash-links li {
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
}

.jsm-dash-links li > a {
    display: grid;
    place-items: center;
    padding: .3rem .35rem;
    border-radius: var(--jsm-radius-sm);
}

.jsm-dash-links li > a:hover,
.jsm-dash-links li > a:focus-visible {
    background: var(--jsm-surface);
}

/*
 * Two sizes, because there are two kinds of mark here.
 *
 * A square glyph — the bird, the camera, the f — is read by its silhouette and
 * works small. A lockup like MyGovUC or HRMIS has lettering in it, and lettering
 * set to the same height as a glyph is simply too small to read. So the squares
 * come down and the wordmarks, tagged `is-wide` in the menu item's Image Class
 * field, get the height back.
 */
.jsm-dash-links .jsm-menu-img {
    display: block;
    width: auto;
    height: 1.5rem;
    max-width: none;
}

.jsm-dash-links .jsm-menu-img.is-wide {
    height: 2.2rem;
}

/*
 * The strip is the first thing to go when the bar runs out of room: it is a
 * set of shortcuts that all exist elsewhere, while the search box and the
 * account menu do not. It goes while there is still room rather than when it
 * stops fitting, because a row of marks wrapped onto two lines reads as a
 * mistake and makes the bar twice as tall.
 */
@media (max-width: 68rem) {
    .jsm-dash-links {
        display: none;
    }
}

/*
 * A menu item whose mark is the whole link still has to say what it is. The
 * name is kept in the page and taken out of the layout, rather than dropped:
 * a row of logos with no text is unusable with a screen reader.
 */
.jsm-menu-sr {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* ------------------------------------------------- Social marks, in the side */

/*
 * The department's accounts, as a row of marks rather than a list of names —
 * these are recognised by their shape, and a column of "Facebook / Instagram /
 * TikTok" would take four times the height to say the same thing.
 */
.jsm-dash-social .jsm-menu {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    gap: .25rem;
    align-items: center;
}

.jsm-dash-social li {
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
}

.jsm-dash-social li > a {
    display: grid;
    place-items: center;
    padding: .3rem;
    border-radius: var(--jsm-radius-sm);
}

.jsm-dash-social li > a:hover,
.jsm-dash-social li > a:focus-visible {
    background: var(--jsm-surface);
}

.jsm-dash-social .jsm-menu-img {
    display: block;
    width: auto;
    height: 1.7rem;
    max-width: none;
}

/* mod_login, logged in: a name and a way out. */
.jsm-dash-account .mod-login-logout {
    display: flex;
    gap: .6rem;
    align-items: center;
}

.jsm-dash-account .login-greeting {
    margin: 0;
    font-size: .85rem;
    font-weight: 600;
    white-space: nowrap;
    color: var(--jsm-navy);
}

.jsm-dash-account .mod-login-logout__button {
    margin: 0;
}

.jsm-dash-account .btn {
    width: auto;
    padding: .4rem .85rem;
    border: 1px solid var(--jsm-line);
    border-radius: 999px;
    background: var(--jsm-white);
    font-size: .8rem;
    font-weight: 600;
    color: var(--jsm-ink);
}

.jsm-dash-account .btn:hover,
.jsm-dash-account .btn:focus-visible {
    border-color: var(--jsm-blue);
    color: var(--jsm-blue);
}

.jsm-dash-account .mod-login-logout__options {
    display: none;
}

/* ------------------------------------------------------- Link grids */

/*
 * Pins and quick links: a single column of small rows in the side, two across
 * in the rail where the column is wider than the list needs.
 */
:is(.jsm-dash-pins, .jsm-dash-quick) .jsm-menu {
    display: grid;
    gap: .3rem;
}

.jsm-dash-quick .jsm-menu {
    grid-template-columns: repeat(auto-fit, minmax(6.5rem, 1fr));
}

:is(.jsm-dash-pins, .jsm-dash-quick) li {
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
}

:is(.jsm-dash-pins, .jsm-dash-quick) li > a {
    display: flex;
    gap: .55rem;
    align-items: center;
    padding: .35rem;
    border-radius: var(--jsm-radius-sm);
    font-size: .85rem;
    font-weight: 500;
}

:is(.jsm-dash-pins, .jsm-dash-quick) li > a:hover,
:is(.jsm-dash-pins, .jsm-dash-quick) li > a:focus-visible {
    background: var(--jsm-surface);
}

/* The app tiles: icon, name, and the link's title attribute as the caption. */
.jsm-dash-apps .jsm-menu {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
    gap: .5rem;
}

.jsm-dash-apps li {
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
}

.jsm-dash-apps li > a {
    display: grid;
    grid-template-columns: 2rem minmax(0, 1fr);
    gap: .1rem .6rem;
    align-items: center;
    padding: .65rem;
    border: 1px solid var(--jsm-line);
    border-radius: var(--jsm-radius-md);
    font-size: .88rem;
    font-weight: 600;
}

.jsm-dash-apps li > a:hover,
.jsm-dash-apps li > a:focus-visible {
    border-color: color-mix(in srgb, var(--jsm-tint) 45%, transparent);
    background: color-mix(in srgb, var(--jsm-tint) 5%, transparent);
}

.jsm-dash-apps li > a > .jsm-menu-icon {
    grid-row: 1 / 3;
    width: 2.1rem;
    height: 2.1rem;
    font-size: .9rem;
}

/*
 * The caption comes from the menu item's Link Title Attribute, so a webmaster
 * fills it in the same form as the rest of the item — and it stays a real
 * tooltip at the same time.
 */
.jsm-dash-apps li > a[title]::after {
    content: attr(title);
    grid-column: 2;
    font-size: .74rem;
    font-weight: 500;
    color: var(--jsm-muted);
}

/* An app tile with no icon should still line up with the ones that have one. */
.jsm-dash-apps li > a:not(:has(> .jsm-menu-icon)) {
    grid-template-columns: minmax(0, 1fr);
}

.jsm-dash-apps li > a:not(:has(> .jsm-menu-icon))[title]::after {
    grid-column: 1;
}

/* --------------------------------------------------------- Slideshow */

/*
 * The slideshow sits in the card rather than on it: no padding, and the
 * corners clipped so the slides take the card's shape.
 *
 * Nothing in here sizes the slider. Smart Slider measures its container and
 * lays itself out from its own settings, and a height imposed from outside is
 * how a slider ends up letterboxed or cropped on one breakpoint and not
 * another. The card gives it a width and gets out of the way.
 */
.jsm-dash-card.jsm-dash-slider {
    padding: 0;
    overflow: hidden;
    border-color: transparent;
}

.jsm-dash-slider :is(.n2-ss-slider, .n2-section-smartslider) {
    margin: 0;
}

/* --------------------------------------------------------- Stat tiles */

.jsm-dash-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: .75rem;
}

/*
 * mod_custom wraps its HTML in a div of its own, which would otherwise be the
 * grid's only item and leave the tiles stacked in one column. Dissolving the
 * wrapper hands the tiles to the grid directly.
 */
.jsm-dash-stats .mod-custom {
    display: contents;
}

.jsm-dash-stats > .jsm-dash-cardhead {
    grid-column: 1 / -1;
    margin: 0;
}

.jsm-dash-stat:nth-child(6n + 1) { --jsm-tint: #4c6ef5; }
.jsm-dash-stat:nth-child(6n + 2) { --jsm-tint: #12b886; }
.jsm-dash-stat:nth-child(6n + 3) { --jsm-tint: #f59f00; }
.jsm-dash-stat:nth-child(6n + 4) { --jsm-tint: #f03e5e; }
.jsm-dash-stat:nth-child(6n + 5) { --jsm-tint: #7950f2; }
.jsm-dash-stat:nth-child(6n + 6) { --jsm-tint: #15aabf; }

.jsm-dash-stat {
    display: grid;
    gap: .1rem;
    padding: .9rem;
    border: 1px solid var(--jsm-line);
    border-radius: var(--jsm-radius-md);
    background: var(--jsm-white);
    box-shadow: var(--jsm-shadow);
}

.jsm-dash-stat .jsm-dash-chip {
    margin-bottom: .35rem;
}

/*
 * The filled variant: each tile takes the tint it was already cycling, as a
 * background rather than an accent. Add `jsm-dash-stats--filled` to the
 * module's class suffix.
 */
.jsm-dash-stats--filled .jsm-dash-stat {
    border-color: transparent;
    background: var(--jsm-tint);
}

.jsm-dash-stats--filled .jsm-dash-stat :is(b, span) {
    color: var(--jsm-white);
}

.jsm-dash-stats--filled .jsm-dash-stat span {
    color: rgba(255, 255, 255, .85);
}

.jsm-dash-stats--filled .jsm-dash-chip {
    color: var(--jsm-white);
    background: rgba(255, 255, 255, .22);
}

.jsm-dash-stats--filled .jsm-dash-stat a {
    color: var(--jsm-white);
    opacity: .9;
}

.jsm-dash-stat b {
    font-family: var(--jsm-font-head);
    font-size: 1.5rem;
    line-height: 1.1;
    letter-spacing: -.02em;
    color: var(--jsm-navy);
}

.jsm-dash-stat span {
    font-size: .78rem;
    color: var(--jsm-muted);
}

.jsm-dash-stat a {
    margin-top: .3rem;
    font-size: .74rem;
    font-weight: 600;
    color: var(--jsm-blue);
    text-decoration: none;
}

.jsm-dash-stat a:hover {
    text-decoration: underline;
}


/* ------------------------------------------------------------- The feed */

/* mod_articles_category: a title link, a date and an intro per row. */
.jsm-dash-feed .mod-articlescategory > li {
    display: grid;
    grid-template-columns: 2rem minmax(0, 1fr);
    gap: 0 .75rem;
    margin: 0;
    padding: .8rem 0 0;
    border: 0;
    border-top: 1px solid var(--jsm-line);
}

.jsm-dash-feed .mod-articlescategory > li:first-child {
    padding-top: 0;
    border-top: 0;
}

/*
 * The module gives every row the same markup, so the dot that stands in for the
 * mockup's per-item icon is drawn here rather than configured per article.
 */
.jsm-dash-feed li::before {
    content: "\f0a1";
    grid-row: 1 / 4;
    font-family: "Font Awesome 6 Free";
    font-weight: 900;
}

.jsm-dash-feed .mod-articles-category-title {
    grid-column: 2;
    font-family: var(--jsm-font-head);
    font-size: .9rem;
    font-weight: 600;
    line-height: 1.35;
    color: var(--jsm-navy) !important;
}

.jsm-dash-feed .mod-articles-category-date {
    grid-column: 2;
    margin-top: .15rem;
    font-size: .74rem;
    color: var(--jsm-muted);
}

.jsm-dash-feed .mod-articles-category-introtext {
    grid-column: 2;
    margin: .3rem 0 0;
    font-size: .82rem;
    color: var(--jsm-muted);
}

.jsm-dash-feed .mod-articles-category-readmore {
    display: none;
}

/* ------------------------------------------------------- Birthdays */

/*
 * mod_jevents_latest normally renders a table. Its row templates are set to
 * emit a list instead — see the setup script — and this styles that list.
 */
.jsm-dash-bday .jsm-bday-list {
    display: grid;
    gap: .5rem;
}

/*
 * A row arrives as date, a <br>, then the name. The name is what a reader looks
 * for, so `order` puts it first and the line break is dropped rather than
 * allowed to claim a cell of the grid.
 */
.jsm-dash-bday li {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 0 .55rem;
    align-items: center;
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    font-size: .85rem;
}

.jsm-dash-bday li::before {
    content: "\f1fd";
    grid-row: 1 / 3;
    order: 0;
    --jsm-tint: #f03e5e;
    font-family: "Font Awesome 6 Free";
    font-weight: 900;
}

.jsm-dash-bday br {
    display: none;
}

.jsm-dash-bday .mod_events_latest_content {
    order: 1;
    grid-column: 2;
    font-size: .82rem;
    line-height: 1.3;
}

.jsm-dash-bday a {
    font-weight: 600;
    color: var(--jsm-navy) !important;
}

.jsm-dash-bday .mod_events_latest_date {
    order: 2;
    grid-column: 2;
    font-size: .74rem;
    color: var(--jsm-muted);
}

.jsm-dash-bday :is(table, .mod_events_latest_navigation, .mod_events_latest_rsslink) {
    display: none;
}

/* ------------------------------------------------------------- The poll */

.jsm-dash-poll .form-sppoll {
    display: grid;
    gap: .45rem;
}

.jsm-dash-poll .radio {
    display: flex;
    gap: .5rem;
    align-items: center;
    font-size: .85rem;
}

.jsm-dash-poll .radio label {
    margin: 0;
}

.jsm-dash-poll :is(.btn-primary, .btn-success) {
    width: 100%;
    margin-top: .4rem;
    padding: .5rem;
    border: 0;
    border-radius: var(--jsm-radius-sm);
    font-size: .8rem;
    font-weight: 600;
}

.jsm-dash-poll .btn-primary {
    background: var(--jsm-blue);
}

.jsm-dash-poll .btn-success {
    background: var(--jsm-surface);
    color: var(--jsm-ink);
}

.jsm-dash-poll .sppoll-results :is(.progress, .bar) {
    height: .4rem;
    border-radius: 999px;
    background: var(--jsm-surface);
}

/* ------------------------------------------------- Who is online, weather */

.jsm-dash-online .mod-whosonline {
    font-size: .85rem;
    color: var(--jsm-muted);
}

.jsm-dash-online .mod-whosonline p {
    margin: 0 0 .3rem;
}

/*
 * The name list is capped and scrolls.
 *
 * mod_whosonline puts no limit on it, so switching the module to "names"
 * prints one row per person signed in — on a working day that is hundreds,
 * running the rail off the bottom of the page. The module ships set to show
 * the count instead, and this is what stops the other setting being a trap.
 */
.jsm-dash-online .nav {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    max-height: 12rem;
    margin: 0;
    padding: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    list-style: none;
}

.jsm-dash-online .nav li {
    margin: 0 !important;
    padding: .15rem .55rem !important;
    border: 0 !important;
    border-radius: 999px;
    background: var(--jsm-surface);
    font-size: .76rem;
    font-weight: 600;
    color: var(--jsm-navy);
}

.jsm-dash-weather .jsm-dash-wx {
    display: flex;
    gap: .75rem;
    align-items: center;
}

.jsm-dash-weather .jsm-dash-wx b {
    font-family: var(--jsm-font-head);
    font-size: 1.7rem;
    line-height: 1;
    letter-spacing: -.02em;
    color: var(--jsm-navy);
}

.jsm-dash-weather .jsm-dash-wx span {
    display: block;
    font-size: .78rem;
    color: var(--jsm-muted);
}

/* ------------------------------------------------ Editorial side cards */

/*
 * The pale wash is a default, not a decision — written through the same
 * property the Note field sets, so a card given its own background keeps it.
 */
.jsm-dash-culture,
.jsm-dash-help {
    border: 0;
    background: var(--jsm-card-bg,
        linear-gradient(160deg, color-mix(in srgb, var(--jsm-blue) 9%, var(--jsm-white)), var(--jsm-white)));
}

.jsm-dash-culture :is(b, strong),
.jsm-dash-help :is(b, strong) {
    display: block;
    margin-bottom: .3rem;
    font-family: var(--jsm-font-head);
    font-size: .95rem;
    line-height: 1.35;
    color: var(--jsm-navy);
}

.jsm-dash-culture p,
.jsm-dash-help p {
    font-size: .8rem;
    color: var(--jsm-muted);
}

.jsm-dash-help .jsm-dash-helpcta {
    display: inline-block;
    margin-top: .25rem;
    padding: .4rem .8rem;
    border-radius: 999px;
    background: var(--jsm-blue);
    font-size: .78rem;
    font-weight: 600;
    color: var(--jsm-white) !important;
    text-decoration: none;
}

/* ------------------------------------------------------- The foot */

.jsm-dash-foot {
    margin-top: clamp(1.25rem, 2vw, 2rem);
    border-top: 1px solid var(--jsm-line);
    background: var(--jsm-white);
}

.jsm-dash-foot-inner {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem 1.25rem;
    align-items: center;
    width: 100%;
    max-width: var(--jsm-dash-width);
    margin-inline: auto;
    padding: 1rem clamp(1rem, 2vw, 1.75rem);
    font-size: .78rem;
    color: var(--jsm-muted);
}

.jsm-dash-copy {
    font-weight: 600;
    color: var(--jsm-navy);
}

/* The legal links sit to the far side, as they do on the public footer. */
.jsm-dash-foot-legal {
    display: flex;
    flex-wrap: wrap;
    gap: .25rem 1rem;
    margin-left: auto;
}

.jsm-dash-foot-legal a {
    color: var(--jsm-muted);
    text-decoration: none;
}

.jsm-dash-foot-legal a:hover,
.jsm-dash-foot-legal a:focus-visible {
    color: var(--jsm-blue);
    text-decoration: underline;
}

.jsm-dash-foot-modules :is(p, ul, div) {
    margin: 0;
}

/* --------------------------------------------- The visitor counter card */

/*
 * mod_gacounter brings its own markup and its own stylesheet; this only
 * settles it into a card — the numbers in the template's display face, the
 * labels quiet, the rows a list rather than a table of their own.
 */
.jsm-dash-counter .gacounter {
    font-size: .85rem;
}

.jsm-dash-counter .gacounter__total {
    display: flex;
    gap: .5rem;
    align-items: baseline;
    margin-bottom: .6rem;
    padding-bottom: .6rem;
    border-bottom: 1px solid var(--jsm-line);
}

.jsm-dash-counter .gacounter__digits {
    display: flex;
    gap: .15rem;
}

.jsm-dash-counter .gacounter__digit {
    min-width: 1.1rem;
    padding: .2rem .1rem;
    border-radius: 4px;
    background: var(--jsm-navy);
    font-family: var(--jsm-font-head);
    font-size: 1rem;
    font-weight: 700;
    line-height: 1;
    text-align: center;
    color: var(--jsm-white);
    font-variant-numeric: tabular-nums;
}

.jsm-dash-counter .gacounter__total-label,
.jsm-dash-counter .gacounter__stat-label {
    font-size: .74rem;
    color: var(--jsm-muted);
}

.jsm-dash-counter .gacounter__stats {
    display: grid;
    gap: .3rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.jsm-dash-counter .gacounter__stat {
    display: flex;
    gap: .5rem;
    justify-content: space-between;
    align-items: baseline;
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
}

.jsm-dash-counter .gacounter__stat-value {
    font-family: var(--jsm-font-head);
    font-weight: 700;
    color: var(--jsm-navy);
    font-variant-numeric: tabular-nums;
}

.jsm-dash-counter .gacounter__updated {
    margin-top: .5rem;
    font-size: .7rem;
    color: var(--jsm-muted);
}

/* ------------------------------------------------------------ Narrow */

/*
 * Below 1100px the rail drops under the main column; below 860px the side
 * follows. The order is deliberate — the rail is the most dispensable of the
 * three, and the navigation should not be the thing that goes first.
 */
@media (max-width: 68.75rem) {
    .jsm-dash-shell,
    .jsm-dash--norail {
        grid-template-columns: var(--jsm-dash-side) minmax(0, 1fr);
    }

    .jsm-dash--noside {
        grid-template-columns: minmax(0, 1fr);
    }

    .jsm-dash-rail {
        grid-column: 1 / -1;
        grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    }
}

@media (max-width: 53.75rem) {
    .jsm-dash-shell,
    .jsm-dash--noside,
    .jsm-dash--norail {
        grid-template-columns: minmax(0, 1fr);
    }

    .jsm-dash-side {
        grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    }

    .jsm-dash-bar-inner {
        flex-wrap: wrap;
    }
}

/* ==========================================================================
   Narrow screens
   ========================================================================== */

@media (max-width: 62rem) {
    :root {
        --jsm-band-pad: 3rem;
    }

    .jsm-nav-toggle {
        display: grid;
    }

    .jsm-brand-text {
        display: none;
    }

    /**
     * The navigation becomes a drawer the toggle opens.
     *
     * visibility, not just the transform. A fixed element parked off-screen to
     * the right still counts toward the document's scroll width — the page
     * gained about 30px of sideways scroll with nothing in it — and body
     * overflow cannot clip a fixed box. visibility:hidden takes it out of the
     * scrollable area, and transitioning it keeps the slide.
     */
    .jsm-nav {
        position: fixed;
        top: 0;
        right: 0;
        bottom: 0;
        z-index: 120;
        width: min(21rem, 86vw);
        overflow-y: auto;
        border-bottom: 0;
        box-shadow: var(--jsm-shadow-lg);
        visibility: hidden;
        transform: translateX(100%);
        transition: transform .25s ease, visibility .25s;
    }

    .jsm-nav.is-open {
        visibility: visible;
        transform: none;
    }

    .jsm-nav .wrap {
        padding: 4rem 1.25rem 2rem;
    }

    .jsm-nav-inner {
        flex-direction: column;
        align-items: stretch;
    }

    /*
     * Scoped to the drawer. `.jsm-menu` is the markup every mod_menu on the
     * site renders, so turning all of them into columns here also stood the
     * dashboard's logo strips on end — a rule about this navigation reaching
     * menus that have nothing to do with it.
     */
    .jsm-nav .jsm-menu {
        flex-direction: column;
        align-items: stretch;
    }

    .jsm-nav .jsm-menu-link {
        padding: .8rem .25rem;
        border-bottom: 1px solid var(--jsm-line);
    }

    .jsm-nav .jsm-menu-toggle {
        display: grid;
        place-items: center;
    }

    /* A submenu opens in flow rather than floating over the drawer. */
    .jsm-nav .jsm-submenu {
        position: static;
        display: none;
        min-width: 0;
        margin: 0 0 .5rem;
        padding: .25rem 0 .25rem .9rem;
        background: none;
        border: 0;
        border-radius: 0;
        box-shadow: none;
        opacity: 1;
        visibility: visible;
        transform: none;
    }

    .jsm-nav .jsm-menu-item.is-open > .jsm-submenu {
        display: block;
    }

    .jsm-nav .jsm-menu-item:hover > .jsm-submenu,
    .jsm-nav .jsm-menu-item:focus-within > .jsm-submenu {
        display: none;
    }

    .jsm-nav .jsm-menu-item.is-open:hover > .jsm-submenu {
        display: block;
    }

    .jsm-nav .jsm-submenu .jsm-menu-link {
        color: var(--jsm-muted);
        border-bottom: 0;
        padding: .45rem .25rem;
    }

    .jsm-nav .jsm-submenu .jsm-menu-item:hover > .jsm-menu-link {
        background: none;
        color: var(--jsm-red);
    }

    .jsm-nav-backdrop {
        position: fixed;
        inset: 0;
        z-index: 110;
        background: rgba(26, 46, 74, .5);
    }

    .jsm-s-content.has-left .jsm-content-grid,
    .jsm-s-content.has-right .jsm-content-grid,
    .jsm-s-content.has-left.has-right .jsm-content-grid {
        grid-template-columns: minmax(0, 1fr);
    }

    .jsm-footer-top {
        grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    }

    /*
     * Stacked, the headline panel gets a share of the screen rather than a
     * whole one — a full viewport before anything can be chosen is two screens
     * of scrolling to reach the chooser.
     */
    body.is-landing .jsm-splash-body {
        min-height: 60vh;
    }

    body.is-landing .jsm-s-products {
        padding: 1.75rem 0 2.25rem;
    }
}

@media (max-width: 40rem) {
    /* Four labelled icons do not fit beside the wordmark on a phone. */
    .jsm-utilrail {
        display: none;
    }

    .jsm-identity-controls {
        padding-left: 0;
        border-left: 0;
    }

    .jsm-highlights {
        flex-wrap: wrap;
    }

    .jsm-highlights-item {
        white-space: normal;
    }

    .jsm-hero-inner {
        padding-block: 2.5rem;
    }

    .jsm-footerbar-inner {
        justify-content: flex-start;
    }

    .jsm-splash-foot-inner {
        flex-direction: column;
        align-items: flex-start;
    }

    .jsm-audience-utility {
        justify-content: flex-start;
    }

    /* Let the tab row scroll sideways rather than wrap onto three lines. */
    .jsm-audience-tabs {
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
    }

    .jsm-audience-tabs::-webkit-scrollbar {
        display: none;
    }

    .jsm-audience-tab {
        flex: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    body.jsm *,
    body.jsm *::before,
    body.jsm *::after {
        transition-duration: .01ms !important;
        animation-duration: .01ms !important;
        scroll-behavior: auto !important;
    }

    /*
     * The blanket rule above would leave the marquee restarting every .01ms,
     * which is worse than the motion it was meant to remove. Stop it outright,
     * drop the duplicate track, and let the strip scroll by hand instead.
     */
    .jsm-ticker-rail {
        animation: none !important;
        width: auto;
    }

    .jsm-ticker-viewport {
        overflow-x: auto;
        mask-image: none;
    }

    .jsm-ticker-track[aria-hidden="true"] {
        display: none;
    }

    .jsm-ticker-pause {
        display: none;
    }
}

@media print {
    .jsm-govbar,
    .jsm-header,
    .jsm-nav,
    .jsm-backtotop,
    .jsm-footer-social,
    .jsm-carousel-btn,
    .jsm-ticker,
    .jsm-splash-media {
        display: none !important;
    }

    /* Every tab's links on paper, since there is nothing to click. */
    .jsm-audience-panel[hidden] {
        display: block !important;
    }

    .jsm-band {
        padding-block: 1rem;
    }
}
