/*
   Autonomycs design system - the primitives every Autonomycs web app shares.

   Class names in this file are a PUBLISHED CONTRACT: My.Web (and later Admin.Web and the docs
   site) reference them from markup, so renaming one is a breaking change across every consumer.

   Rules keep the source order they had in the MY portal's app.css they were extracted from - the
   cascade was audited once for that order, and reordering re-rolls those dice. App-specific
   styling (shell chrome, screen-specific families) stays in each app's own stylesheet, which
   loads AFTER this one.

   The .rz-* rules at the bottom skin Radzen components onto the brand. They are inert unless the
   consumer links Radzen's material-base.css BEFORE this file; this library deliberately takes no
   Radzen package dependency.
*/

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

html,
body {
    margin: 0;
    padding: 0;
}

body {
    background: var(--canvas);
    font-family: var(--font);
    color: var(--ink);
    -webkit-font-smoothing: antialiased;
}

/* FocusOnNavigate (App.razor) moves focus to the page's h1 after every navigation so assistive
   tech announces the new page - and the browser then paints its focus ring on a heading nobody
   can tab to. Keep the focus, drop the ring. */
h1:focus {
    outline: none;
}

a {
    color: var(--brand);
    text-decoration: none;
}

a:hover {
    color: var(--accent);
}

input,
select,
button,
textarea {
    font-family: inherit;
}

h1 {
    margin: 0;
    font: 500 38px/1.1 var(--font);
    letter-spacing: -.02em;
}

h2 {
    margin: 0;
    font: 500 24px/1.2 var(--font);
}

/* -------------------------------------------------- boot splash
   Shown by index.html before the runtime downloads, and replaced by the app. A WebAssembly cold
   start is seconds rather than milliseconds, so this is a real screen the user sees, not a
   formality. */
.app-boot {
    position: fixed;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 28px;
    background: var(--brand);

    /* The mark inside is drawn with fill="currentColor" - see BrandMark.razor - so this is what
       makes it white rather than the body's ink. */
    color: #fff;
}

.app-boot__mark {
    width: 64px;
    height: auto;
    display: block;
}

.app-boot__bar {
    width: 180px;
    height: 4px;
    border-radius: 999px;
    background: rgba(255, 255, 255, .22);
    overflow: hidden;
}

.app-boot__bar span {
    display: block;
    width: 40%;
    height: 100%;
    border-radius: 999px;
    background: #fff;
    animation: app-boot-slide 1.1s ease-in-out infinite;
}

@keyframes app-boot-slide {
    0% { transform: translateX(-100%); }
    100% { transform: translateX(350%); }
}

.page {
    max-width: 1220px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    gap: 24px;
}

.page__lead {
    margin: 0;
    font: 400 18px/1.5 var(--font);
    color: var(--muted);
}

.page__title {
    margin: 0 0 6px;
}

/* Two-column content. auto-fit rather than a fixed pair, so the sidebar column drops beneath the
   main one before the cards themselves get too narrow to read. */
.split {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
    gap: 24px;
    align-items: start;
}

.stack {
    display: flex;
    flex-direction: column;
    gap: 24px;
}

/* Self-contained, NOT a modifier that needs .stack alongside it. Every use site applies it on its
   own, and while it only set `gap` that did nothing at all: without display:flex there is no gap, so
   the home page's cards sat flush against each other and its document links - being inline elements -
   ran together on one line. */
.stack--tight {
    display: flex;
    flex-direction: column;
    gap: 20px;
}

.langpicker {
    position: relative;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 9px 14px;
    background: var(--canvas);
    border-radius: var(--radius-control);
    cursor: pointer;
}

.langpicker__label {
    font: 500 14px/1 var(--font);
    color: var(--muted);
}

/* The Radzen dropdown, blended into the pill: the pill is the control's chrome, so the control
   itself loses its own. The open panel keeps the theme, which is already mapped to the brand
   tokens at the bottom of this file.

   The hover/focus selectors are not optional: the theme's own hover rule ADDS a border at higher
   specificity, which grew the control by a pixel and made the whole top bar jump on mouse-over. */
.langpicker .rz-dropdown,
.langpicker .rz-dropdown:not(.rz-state-disabled):hover,
.langpicker .rz-dropdown:not(.rz-state-disabled):focus-within,
.langpicker .rz-dropdown.rz-state-focused {
    border: 0;
    border-block-end: 0;
    background: transparent;
    box-shadow: none;
    height: auto;
    min-height: 0;
}

/* Wide enough for "Nederlands" so the pill does not resize per language. A separate rule, NOT part
   of the state group above: the mobile override below must be able to win from the base width
   without also having to out-rank the hover/focus selectors. */
.langpicker .rz-dropdown {
    min-width: 118px;
}

.langpicker .rz-dropdown .rz-dropdown-label {
    padding: 0;
    font: 500 15px/1.2 var(--font);
    color: var(--ink);
}

.langpicker .rz-dropdown .rzi {
    color: var(--muted);
}

/* -------------------------------------------------- cards */
.card {
    background: #fff;
    border: 1px solid var(--card-border);
    border-radius: var(--radius-card);
    padding: 28px;
}

.card--tight {
    padding: 24px;
}

.card--list {
    padding: 12px 28px 20px;
}

.card__head {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 16px;
    justify-content: space-between;
    align-items: baseline;
    margin-bottom: 20px;
}

.card__eyebrow {
    font: 500 13px/1 var(--font);
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .1em;
}

.card__link {
    font: 500 16px/1 var(--font);
}

.alert {
    background: var(--surface-orange);
    border: 1px solid var(--accent);
    border-radius: var(--radius-card);
    padding: 24px;
}

.alert__title {
    margin: 0;
    font: 500 19px/1.3 var(--font);
}

.alert__body {
    margin: 8px 0 18px;
    font: 400 16px/1.45 var(--font);
    color: var(--muted);
}

/* -------------------------------------------------- rows */
.row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 16px 20px;
    padding: 18px 0;
    border-bottom: 1px solid var(--hairline);
}

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

.row--topline {
    padding: 20px 0;
    gap: 14px 18px;
    border-bottom: 0;
    border-top: 1px solid var(--hairline);
}

.row__main {
    flex: 1 1 190px;
    min-width: 190px;
}

.row__title {
    margin: 0;
    font: 500 19px/1.3 var(--font);
}

.row__meta {
    margin: 2px 0 0;
    font: 400 16px/1.4 var(--font);
    color: var(--muted);
}

.row__button {
    background: transparent;
    color: var(--brand);
    padding: 12px 18px;
    border-radius: var(--radius-control);
    font: 500 15px/1 var(--font);
    cursor: pointer;
    border: 1px solid var(--hairline-strong);
}

.row__button:hover {
    background: var(--canvas);
}

.row__button--primary {
    border-color: var(--brand);
}

.row__button--primary:hover {
    background: var(--brand);
    color: #fff;
}

.dot {
    width: 10px;
    height: 10px;
    flex: none;
    border-radius: 50%;
    background: var(--olive);
}

.dot--next {
    background: var(--accent);
}

.dot--measurement {
    background: var(--green-text);
}

/* -------------------------------------------------- badges, buttons */
.badge {
    padding: 7px 13px;
    border-radius: 999px;
    background: var(--canvas);
    color: var(--deep);
    font: 500 14px/1 var(--font);
    white-space: nowrap;
}

.badge--measurement {
    background: var(--surface-green);
    color: var(--green-text);
}

/* A cancelled place. Muted rather than struck through: the row is still information the member wants
   to read, it just is not something they are attending. */
.badge--cancelled {
    background: var(--surface-orange);
    color: var(--rust);
}

.badge--next {
    background: var(--surface-orange);
    color: var(--rust);
    padding: 8px 14px;
}

.btn {
    border: 1px solid var(--hairline-strong);
    background: transparent;
    color: var(--brand);
    padding: 16px 24px;
    border-radius: 13px;
    font: 500 17px/1 var(--font);
    cursor: pointer;
}

.btn:hover:not(:disabled) {
    background: var(--canvas);
}

.btn:disabled {
    opacity: .45;
    cursor: not-allowed;
}

.btn--primary {
    border: 0;
    background: var(--accent);
    color: #fff;
}

.btn--primary:hover:not(:disabled) {
    background: #e96a15;
    color: #fff;
}

.btn--block {
    width: 100%;
    border: 0;
    background: var(--accent);
    color: #fff;
    padding: 15px;
    border-radius: var(--radius-control);
    font: 500 16px/1 var(--font);
    cursor: pointer;
}

.btn--block:disabled {
    opacity: .5;
    cursor: not-allowed;
}

.tabs {
    display: flex;
    gap: 6px;
    padding: 16px 0 12px;
    flex-wrap: wrap;
}

.tab {
    padding: 10px 18px;
    border: 0;
    border-radius: 999px;
    background: var(--canvas);
    color: var(--muted);
    font: 500 15px/1 var(--font);
    cursor: pointer;
}

.tab--active {
    background: var(--brand);
    color: #fff;
}

/*
    ValueBandBar: a Radzen chart, skinned. One stacked column on its 0..max scale with the normal
    band's bounds across it, at a fixed height so a row of items lines up.

    THE COLOURS LIVE HERE rather than on the component's attributes, because this project's palette is
    tokens.css and C# in this assembly cannot read it - Ui.Web references nothing, BrandColors
    included. material-base.css is linked before this file, so an equal-specificity rule here wins on
    order alone.
*/
.vbar {
    /* Wide enough that the value axis on the left does not squeeze the plot into a sliver: the
       numbers take about a third of it, and what is left is the column's room. */
    width: 176px;
    height: 200px;
    justify-self: end;

    /* Radzen's chart variables, which is how the axis, its numbers and the gridlines are reached:
       they are drawn as SVG with no class worth targeting per instance. */
    --rz-chart-axis-color: var(--border);
    --rz-chart-axis-label-color: var(--muted);
    --rz-chart-grid-line-color: var(--border);
}

/* The chart's own default box is 300px wide. */
.vbar .rz-chart {
    width: 100%;
    height: 100%;
}

/*
    The column's colour, set two ways on purpose.

    --rz-series-1 is the real mechanism: the theme's own rule for the first series reads it
    (`.rz-scheme-pastel .rz-series-item-0 { fill: var(--rz-series-1) }`), so feeding the variable
    colours whatever element Radzen decides to paint, without a specificity fight and whatever the
    class on it turns out to be.

    The rule below it is the belt to that brace, for a colour scheme that hardcodes its hexes instead
    of reading the variable: same specificity as the theme's, and this file is linked after
    material-base.css, so it wins on order.

    It is NOT widened to descendants, however tempting. The band's reference lines are children of the
    series and carry no class of their own, so a `.rz-column-series *` would paint them the bar's
    colour and lose the band.
*/
.vbar--good {
    --rz-series-1: var(--green-text);
    --vbar-colour: var(--green-text);
}

.vbar--low {
    --rz-series-1: var(--rust);
    --vbar-colour: var(--rust);
}

.vbar--neutral {
    --rz-series-1: var(--muted);
    --vbar-colour: var(--muted);
}

.vbar .rz-series-item-0 {
    fill: var(--vbar-colour);
    stroke: var(--vbar-colour);
}

/*
    The value above the column. It sits inside the series group, so without this it inherits the bar's
    colour and a green number lands on a green bar the moment the bar is tall enough to reach it.
*/
.vbar .rz-series-data-label,
.vbar .rz-series-data-label-fill {
    fill: var(--ink);
    stroke: none;
    font: 500 13px/1 var(--font);
}

.consentbody {
    font: 400 17px/1.6 var(--font);
    color: var(--ink);
    overflow-wrap: break-word;
}

.consentbody p {
    margin: 0 0 16px;
}

.consentbody h1,
.consentbody h2,
.consentbody h3 {
    margin: 20px 0 10px;
    font: 500 20px/1.3 var(--font);
    letter-spacing: 0;
}

.consentbody ul,
.consentbody ol {
    margin: 0 0 16px;
    padding-left: 24px;
}

.consentbody a {
    text-decoration: underline;
}

/* -------------------------------------------------- tables (payments, results)
   CSS-grid tables per the design; the column template comes from a per-table class. Below tablet
   width each row collapses into a two-column label-free stack. */
.table__head {
    display: grid;
    gap: 16px;
    padding: 20px 0 14px;
    border-bottom: 1px solid rgba(106, 97, 29, .16);
    font: 500 14px/1 var(--font);
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .08em;
}

.table__row {
    display: grid;
    gap: 16px;
    padding: 18px 0;
    border-bottom: 1px solid rgba(106, 97, 29, .1);
    align-items: center;
    font: 400 17px/1.3 var(--font);
}

.table__row:last-child {
    border-bottom: 0;
}

.table__strong {
    font: 500 19px/1.2 var(--font);
}

.table__muted {
    color: var(--muted);
}

@media (max-width: 700px) {
    .table__head {
        display: none;
    }

    .table__row {
        grid-template-columns: 1fr auto;
    }
}

.field {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.field__label {
    font: 500 16px/1 var(--font);
    color: var(--deep);
}

.field__input {
    padding: 16px 18px;
    border: 1px solid rgba(106, 97, 29, .28);
    border-radius: var(--radius-control);
    font: 400 18px/1.2 var(--font);
    background: #fff;
    color: var(--ink);
}

.field__error {
    font: 400 14px/1.3 var(--font);
    color: var(--rust);
}

/* A Radzen dropdown dressed as a .field__input. The border colour lives in a custom property so
   the invalid state only swaps the variable - the hover/focus rules below (needed because the
   theme's own hover rule adds a different border at higher specificity) then inherit it. */
.field .rz-dropdown.fieldselect {
    --fieldselect-border: rgba(106, 97, 29, .28);

    width: 100%;
}

.field .rz-dropdown.fieldselect,
.field .rz-dropdown.fieldselect:not(.rz-state-disabled):hover,
.field .rz-dropdown.fieldselect:not(.rz-state-disabled):focus-within,
.field .rz-dropdown.fieldselect.rz-state-focused {
    padding: 16px 18px;
    border: 1px solid var(--fieldselect-border);
    border-radius: var(--radius-control);
    background: #fff;
    box-shadow: none;
    height: auto;
    min-height: 0;
}

.fieldselect .rz-dropdown-label {
    padding: 0;
    font: 400 18px/1.2 var(--font);
    color: var(--ink);
}

.fieldselect .rzi {
    color: var(--muted);
}

.fieldselect .rz-placeholder {
    color: var(--muted);
}

.field .rz-dropdown.fieldselect.field__input--invalid {
    --fieldselect-border: var(--rust);
}

.field__input:disabled,
.field__input[readonly] {
    background: var(--surface-neutral);
    color: var(--muted);
    cursor: not-allowed;
    border-color: rgba(106, 97, 29, .18);
}

/* -------------------------------------------------- filter pills
   The design's two pill variants: filled on the results picker, outlined on the documents filters.
   Both share the active state. */
.pillrow {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.pill {
    border: 0;
    background: var(--canvas);
    padding: 9px 15px;
    border-radius: 999px;
    font: 500 14px/1 var(--font);
    color: var(--muted);
    cursor: pointer;
}

.pill:hover {
    background: var(--border);
}

.pill--outline {
    border: 1px solid rgba(106, 97, 29, .18);
    background: #fff;
    padding: 11px 19px;
    font-size: 15px;
}

.pill--outline:hover {
    background: var(--canvas);
}

.pill--active,
.pill--outline.pill--active {
    background: var(--brand);
    border-color: var(--brand);
    color: #fff;
}

/* -------------------------------------------------- page head with an action
   Title/lead on the left, a primary action on the right, wrapping below tablet width. */
.page__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px 24px;
    flex-wrap: wrap;
}

/* Sub-heading inside a list card, per the design's vouchers/payments sections. */
.card__subhead {
    margin: 24px 0 6px;
    font: 500 24px/1.2 var(--font);
}

.card__note {
    margin: 0 0 8px;
    font: 400 16px/1.4 var(--font);
    color: var(--muted);
}

.loadmore {
    display: flex;
    justify-content: center;
    padding: 18px 0 6px;
}

/* -------------------------------------------------- detail pane */
.detail__head {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: flex-start;
    gap: 16px 20px;
    margin-bottom: 24px;
}

.detail__title {
    margin: 0 0 6px;
    font: 500 26px/1.15 var(--font);
}

.detail__sub {
    margin: 0;
    font: 400 17px/1.4 var(--font);
    color: var(--muted);
}

.facts {
    display: flex;
    flex-direction: column;
    margin-bottom: 26px;
}

.fact {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    padding: 15px 0;
    border-bottom: 1px solid var(--hairline);
}

.fact:last-child {
    border-bottom: 0;
}

.fact__label {
    font: 400 17px/1.3 var(--font);
    color: var(--muted);
}

.fact__value {
    font: 500 17px/1.3 var(--font);
    text-align: right;
}

.actions {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
}

.footnote {
    margin: 18px 0 0;
    font: 400 15px/1.5 var(--font);
    color: var(--muted);
}

.empty {
    margin: 8px 0;
    font: 400 17px/1.5 var(--font);
    color: var(--muted);
}

/* -------------------------------------------------- states */
.spinner {
    display: flex;
    justify-content: center;
    padding: 26px 0;
}

.spinner::after {
    content: "";
    width: 34px;
    height: 34px;
    border-radius: 50%;
    border: 3px solid var(--border);
    border-top-color: var(--brand);
    animation: spinner-turn .8s linear infinite;
}

@keyframes spinner-turn {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    .spinner::after {
        animation-duration: 2.4s;
    }
}

.skeleton {
    background: linear-gradient(90deg, var(--surface-neutral) 25%, var(--border) 37%, var(--surface-neutral) 63%);
    background-size: 400% 100%;
    animation: skeleton 1.4s ease infinite;
    border-radius: 10px;
    height: 18px;
}

@keyframes skeleton {
    0% { background-position: 100% 50%; }
    100% { background-position: 0 50%; }
}

.loadfail {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 14px;
    padding: 18px 20px;
    border: 1px solid var(--accent);
    background: var(--surface-orange);
    border-radius: 14px;
    font: 400 16px/1.4 var(--font);
}

/* -------------------------------------------------- blazor error bar */
#blazor-error-ui {
    background: var(--surface-orange);
    border-top: 1px solid var(--accent);
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, .1);
    display: none;
    left: 0;
    padding: 14px 20px 14px 20px;
    position: fixed;
    width: 100%;
    z-index: 1000;
    font: 400 16px/1.4 var(--font);
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 18px;
    top: 12px;
}

@media (max-width: 560px) {
    h1 {
        font-size: 30px;
    }

    .card,
    .card--list {
        padding: 20px;
    }

    /* The language pill drops its "Taal" label - the dropdown's own value says what it is. */
    .langpicker {
        padding: 9px 10px;
    }

    .langpicker__label {
        display: none;
    }

    .langpicker .rz-dropdown {
        min-width: 104px;
    }

    .fact {
        flex-direction: column;
        gap: 2px;
    }

    .fact__value {
        text-align: left;
    }
}

/* -------------------------------------------------- signed-out screen
   The design's login artboard, minus its email and password fields — B2C owns credentials, and a
   second set of fields here would either be decorative or collect what this app must not see. */

.signedout {
    max-width: 1100px;
    width: 100%;
    margin: auto;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    border-radius: 24px;
    overflow: hidden;
    border: 1px solid var(--card-border);
    min-height: 560px;
}

.signedout__panel {
    background: #fff;
    padding: 52px 48px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 24px;
}

.signedout__panel--brand {
    background: var(--brand);
    color: #fff;
    padding: 44px;
    justify-content: space-between;
}

.signedout__mark {
    width: 44px;
    height: auto;
    display: block;
}

.signedout__claim {
    margin: 0 0 12px;
    font: 500 34px/1.15 var(--font);
    color: #fff;
    letter-spacing: -.02em;
}

.signedout__blurb {
    margin: 0;
    font: 400 18px/1.55 var(--font);
    color: rgba(255, 255, 255, .82);
    max-width: 380px;
}

.signedout__title {
    margin: 0 0 8px;
    font: 500 30px/1.15 var(--font);
    letter-spacing: 0;
}

.signedout__title--minor {
    font-size: 22px;
}

.signedout__sub {
    margin: 0;
    font: 400 17px/1.5 var(--font);
    color: var(--muted);
}

/* -------------------------------------------------- Radzen
   Map Radzen's theme variables onto the brand tokens rather than restyling components one at a
   time — a component added later then inherits the brand instead of arriving in Material purple. */
:root {
    --rz-primary: var(--brand);
    --rz-primary-light: var(--olive);
    --rz-secondary: var(--accent);
    --rz-text-color: var(--ink);
    --rz-text-secondary-color: var(--muted);
    --rz-base-background-color: var(--canvas);
    --rz-body-background-color: var(--canvas);
    --rz-body-color: var(--ink);
    --rz-border-radius: var(--radius-control);
    --rz-text-font-family: var(--font);
    --rz-danger: var(--red);
    --rz-warning: var(--accent);
    --rz-success: var(--green-text);
}

.rz-dialog {
    border-radius: 22px;
    font-family: var(--font);
}

/*
    A DIALOG NEVER FILLS THE VIEWPORT. Radzen's own frame is max-height: 100% and takes whatever
    width it is given, so a dialog asked for 100vw, or one whose content grows, reaches the edges
    and stops reading as a dialog. This caps every dialog at --dialog-inset from each edge; the
    frame is overflow: hidden and .rz-dialog-content scrolls, so a capped dialog scrolls its body
    rather than the page. A dialog that wants to be as large as allowed asks for 100vw / 100vh and
    lets this rule inset it, so the number lives in one place.

    From 769px up only, and that breakpoint is Radzen's: at 768px and below it turns every
    non-alert dialog into a full-width sheet pinned to the top, which is right on a phone and what
    the MY portal relies on. A cap there would leave a narrowed sheet with square corners.
*/
@media (min-width: 769px) {
    .rz-dialog {
        max-width: calc(100vw - 2 * var(--dialog-inset));
        max-height: calc(100vh - 2 * var(--dialog-inset));
    }
}

.rz-dialog-titlebar {
    border-radius: 22px 22px 0 0;
}

/*
    A dropdown is clicked, closed and open alike, so it shows the hand. Radzen sets no cursor on the
    closed control - a form dropdown inherited the arrow from its label - and says cursor: default
    on its option rows outright. The :not() lifts specificity past those rows' own rules, so this
    wins whatever the order.
*/
.rz-dropdown:not(.rz-state-disabled),
.rz-dropdown-items li:not(.rz-state-disabled),
.rz-dropdown-item:not(.rz-state-disabled),
.rz-multiselect-items li:not(.rz-state-disabled) {
    cursor: pointer;
}

.rz-notification-item {
    font-family: var(--font);

    /* The severity colour is painted by an inner element whose corners poke past the item's own
       radius, leaving white triangles. Matching radius + clipping fixes both corners. */
    border-radius: var(--radius-control);
    overflow: hidden;
}

/* The item sits inside a wrapper that paints its OWN white rectangle, which showed as square
   corners behind the rounded item above. The item carries the visible surface; the wrapper is
   layout only. */
/*
    Radzen draws the close control as `content: "close"` on a ::before, relying on the Material
    Symbols LIGATURE font to turn that word into a cross. The rule above puts our own font on the
    notification, the button inherits it, ES Rebond Grotesque has no such ligature - and the glyph
    comes out as tofu.

    So the font is handed back to the button explicitly. Only the button: the message text beside it
    stays in the brand font, which is the reason the rule above exists at all.
*/
.rz-notification-close {
    font-family: var(--rz-icon-font-family, "Material Symbols");
}

.rz-notification-item-wrapper {
    background: transparent;
}

/* -------------------------------------------------- stat tile

   One number and its label, for a dashboard's summary row. Tone drives the value's colour: the
   admin design brief's seventh friction is a dashboard where every card weighed the same, so the
   whole point of this component is that a figure someone is waiting on does not look like a figure
   that is merely true. */
/* The row a dashboard puts them in. auto-fit rather than a fixed count, so four tiles sit in a
   row on a wide screen and reflow to two on a narrow one without the page choosing a breakpoint. */
.stattiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 16px;
}
.stattile {
    background: #fff;
    border: 1px solid var(--card-border);
    border-radius: var(--radius-card-sm);
    padding: 20px 22px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.stattile__label {
    font: 400 15px/1.35 var(--font);
    color: var(--muted);
}
/*
    The number and what qualifies it, side by side while they fit.

    Wrapping matters because the unit is two different lengths of thing: "/ 12" sits beside a figure
    happily, and "96 betalingen" beside "€ 18.406" does not - and the euro sign is glued to its
    amount, so the value cannot give way. Without the wrap the unit simply left the card.
*/
.stattile__figure {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 9px;
    min-width: 0;
}
.stattile__value {
    font: 500 34px/1 var(--font);
    letter-spacing: -.02em;
    /* Tabular figures so a row of tiles does not jitter as the numbers land. */
    font-variant-numeric: tabular-nums;
}
.stattile__value--neutral {
    color: var(--ink);
}
.stattile__value--attention {
    color: var(--accent);
}
/* --rust rather than --accent: it is the darker orange that keeps contrast against a tint, and it
   is what the artboards use for the figure someone is actually waiting on. */
.stattile__value--urgent {
    color: var(--rust);
}
.stattile__unit {
    min-width: 0;
    font: 400 15px/1.2 var(--font);
    color: var(--muted-soft);
}

/* -------------------------------------------------- status pill

   A state, named. The word is always rendered and the tint is the second signal - the admin design
   brief's fifth friction was status as a bare coloured dot, which is unreadable without the legend
   and says nothing at all to anyone who cannot separate the hues.

   The tints are the surface tokens rather than the status tokens: --success and friends are
   full-strength and made for an icon or a border, and a row of them at pill size turns a table into
   a traffic light. The text carries the saturated colour instead. */
.statuspill {
    display: inline-flex;
    align-items: baseline;
    gap: 5px;
    padding: 5px 11px;
    border-radius: 999px;
    font: 500 14px/1.2 var(--font);
    white-space: nowrap;
}
.statuspill__reason {
    font-weight: 400;
    opacity: .8;
}
/*
    The neutral pill carries a border, and the other three do not.

    Its background is --canvas, which is the page's own colour: on a card or in a dialog that is very
    nearly white on white, so "Overgeslagen" read as bare text beside a "Verzonden" that was clearly
    a chip. The tinted tones have enough contrast to draw their own edge; this one does not, and a
    pill that only looks like a pill on some surfaces is not a pill.
*/
.statuspill--neutral {
    background: var(--canvas);
    color: var(--deep);
    box-shadow: inset 0 0 0 1px var(--hairline-strong);
}
.statuspill--good {
    background: var(--surface-green);
    color: var(--green-text);
}
.statuspill--warning {
    background: var(--surface-orange);
    color: var(--rust);
}
.statuspill--bad {
    background: var(--surface-neutral);
    color: var(--red);
}

/* -------------------------------------------------- week planner

   WeekPlanner: Radzen's scheduler, skinned into the brand's week grid. The component hides Radzen's
   own header and draws a day header of its own above the grid, so the rules here are in two families:
   .planner__* for what this design system draws, and .planner .rz-* for what Radzen draws.

   THE COLOURS GO THROUGH RADZEN'S OWN VARIABLES where one exists, as .vbar does for the chart - so a
   Radzen restyle that moves a property still reads the brand from the same place. Only what has no
   variable is set as a property. */
.planner {
    /* The hours column, and the one value both the header row and the grid size it from. */
    --planner-hours-w: 76px;

    /* The pitch and the day count. The component writes all three inline (HourHeightPx,
       60 / MinutesPerSlot, 7 or 1); these are the values a bare .planner falls back to. */
    --planner-hour: 72px;
    --planner-slots: 2;
    --planner-days: 7;

    --rz-scheduler-border-color: var(--hairline);
    --rz-scheduler-minor-border-color: var(--hairline);
    --rz-scheduler-background-color: #fff;
    --rz-scheduler-color: var(--ink);
    --rz-scheduler-event-background-color: #fff;
    --rz-scheduler-event-color: var(--ink);
    --rz-scheduler-event-content-padding: 6px 9px;
    --rz-scheduler-event-font-size: 14px;
    --rz-scheduler-event-line-height: 1.25;
    --rz-scheduler-event-padding-block: 0 3px;
    --rz-scheduler-event-padding-inline: 0 3px;
    --rz-scheduler-border-radius: 8px;
    --rz-scheduler-shadow: none;
    --rz-scheduler-focus-outline: 2px solid var(--brand);

    /* Seven days need room; below this the card that holds the planner scrolls sideways rather than
       the columns crushing the cards to a sliver. */
    min-width: 940px;
    font-family: var(--font);
}

/* -- the header row this component draws -- */
.planner__head {
    display: grid;
    grid-template-columns: var(--planner-hours-w) repeat(var(--planner-days), minmax(0, 1fr));
    border-bottom: 1px solid var(--card-border);
    background: var(--surface-neutral);
}

/* One day needs no minimum; the min-width is for seven columns of cards. */
.planner--day,
.planner--agenda {
    min-width: 0;
}

.planner__day {
    padding: 12px 10px;
    text-align: center;
    border-left: 1px solid var(--hairline);
}

/*
    Today is marked with a rule and the brand colour on the number rather than a fill: a fill would
    compete with the tints inside the column, which are the ones carrying meaning.
*/
.planner__day--today {
    box-shadow: inset 0 -3px 0 var(--brand);
}

.planner__dow {
    display: block;
    font: 500 13px/1.2 var(--font);
    color: var(--muted-soft);
    text-transform: uppercase;
    letter-spacing: .08em;
}

.planner__num {
    display: block;
    margin-top: 5px;
    font: 500 22px/1 var(--font);
    font-variant-numeric: tabular-nums;
}

.planner__day--today .planner__num {
    color: var(--brand);
}

.planner__count {
    display: block;
    margin-top: 5px;
    font: 400 13px/1.2 var(--font);
    color: var(--muted-soft);
}

/* -- Radzen's grid, made to lie flat on the page --

   Radzen draws a 400px box that scrolls inside itself. The page is the scroll container here, so the
   box loses its height and its border, and its scroll box (.rz-view-content, which is height: 0 and
   overflow: auto - the one that actually collapses the grid if only the outer height is changed)
   becomes as tall as its hours. Its own header goes: the component draws one above. */
.planner .rz-scheduler {
    height: auto;
    border: 0;
    border-radius: 0;
    overflow: visible;
    background: transparent;
}

.planner .rz-view-content {
    height: auto;
    overflow: visible;
}

.planner .rz-view-header {
    display: none;
}

.planner .rz-slot-hours {
    flex: 0 0 var(--planner-hours-w);
}

/*
    THE PITCH. Radzen sizes a slot at 1.5em and places an event at 1.5em per slot, so the height of an
    hour is a font-size on the slot column: --planner-hour over 1.5 slots' worth. Slot titles and
    event text are sized by Radzen's absolute variables, so they do not grow with it.

    The hours column is the trap: Radzen sizes ITS rows in rem, not em, so changing this alone leaves
    the labels drifting down the column. The rule after this one keeps them on the hour lines.
*/
.planner .rz-slots {
    font-size: calc(var(--planner-hour) / (1.5 * var(--planner-slots)));
}

.planner .rz-slot-hours .rz-slot-header {
    height: calc(var(--planner-hour) / var(--planner-slots));
    padding: 6px 10px 0 0;
    border: 0;
    text-align: right;
    font: 400 14px/1 var(--font);
    color: var(--muted-soft);
    font-variant-numeric: tabular-nums;
}

.planner .rz-slot {
    cursor: pointer;
}

.planner .rz-slot:hover {
    background: var(--surface-neutral);
}

/* The half-hour line, lighter than the hour's without a token of its own. */
.planner .rz-slot-minor {
    border-block-start-style: dashed;
}

/* Never give .rz-slot a position: the cards paint over the slots only because .rz-events is
   positioned and the slots are not. This is the belt to that braces. */
.planner .rz-events {
    z-index: 1;
}

.planner__slot--today {
    background: var(--surface-neutral);
}

/* The grid's positioning box. The now line is absolute inside it, so it must be positioned and
   nothing between it and the line may be: on .planner itself the line would measure from the top of
   the header row, whose height is the consumer's. */
.planner__grid {
    position: relative;
}

/*
    THE NOW LINE. Radzen draws none (ShowTimeIndicator is Syncfusion's), so the component lays one
    over today's column. --planner-now-day is that column, zero-based, and --planner-now-min the
    minutes below the first row, both written inline by the component; the pitch is the same
    --planner-hour the slots are sized from, so the line and the cards agree on where a minute is.
    The columns are Radzen's flex: 1 day columns after the hours gutter - the arithmetic the header
    row mirrors as a grid. Above the cards (.rz-events is 1) and inert to the pointer, so a click
    under it still reaches the slot or the card.
*/
.planner__now {
    position: absolute;
    left: calc(var(--planner-hours-w) + (100% - var(--planner-hours-w)) * var(--planner-now-day) / var(--planner-days));
    width: calc((100% - var(--planner-hours-w)) / var(--planner-days));
    top: calc(var(--planner-hour) * var(--planner-now-min) / 60);
    height: 0;
    border-top: 2px solid var(--red);
    z-index: 2;
    pointer-events: none;
}

/* The dot at the column's left edge, centred on the line. */
.planner__now::before {
    content: "";
    position: absolute;
    left: -4px;
    top: -5px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--red);
}

/*
    THE CARD SHELL. Single-class on purpose: the component adds planner__item to every card through
    Radzen's render hook, and a consumer's own single-class state modifier - loaded after this file -
    wins on order alone, without knowing that Radzen is underneath. Fill, colour, padding and radius
    come from the variables above; this is the frame.
*/
.planner__item {
    border: 1px solid var(--card-border);
    border-left: 3px solid var(--olive);
    text-align: left;
    cursor: pointer;
}

.planner .rz-event:hover .planner__item,
.planner .rz-event:focus-visible .planner__item {
    border-color: var(--brand);
}

/* The card is the focusable element (Radzen makes it a button); the ring sits just outside it. */
.planner .rz-event:focus-visible {
    outline: var(--rz-scheduler-focus-outline);
    outline-offset: 1px;
    border-radius: var(--rz-scheduler-border-radius);
}

/* -- the agenda: days as rows, each with its date beside it --

   Radzen lays a day out as a header (the number and the day name) beside a slot holding that day's
   cards in flow, so nothing here is positioned and the pitch does not apply. The date column takes
   the width the hour gutter has in the grid views, which keeps the cards' left edge where the eye
   already expects it when switching. */
.planner .rz-view-content-agenda {
    background: #fff;
}

.planner .rz-view-content-agenda > .rz-stack {
    border-bottom: 1px solid var(--hairline);
    padding: 12px 0;
}

.planner .rz-slot-header-agenda {
    width: var(--planner-hours-w);
    min-width: var(--planner-hours-w);
    margin-top: 0;
    padding: 6px 10px 0 0;
}

.planner .rz-slot-header-agenda .rz-slot-header-agenda-day-number {
    font: 500 22px/1 var(--font);
    color: var(--ink);
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.planner .rz-slot-header-agenda .rz-slot-header-agenda-day-title {
    margin-top: 5px;
    font: 500 13px/1.2 var(--font);
    color: var(--muted-soft);
    text-align: right;
    text-transform: uppercase;
    letter-spacing: .08em;
}

.planner .rz-slot-agenda {
    width: auto;
    min-width: 0;
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding-right: 10px;
    cursor: pointer;
}

/* An agenda card is in flow: give it the height its content needs and the whole row's width. */
.planner .rz-event-agenda {
    position: static;
    width: auto;
}

.planner .rz-event-agenda .rz-event-content {
    height: auto;
}

/* Radzen's own time column on an agenda row is off (ShowTimeDisplay=false): the card's template
   already says when, and two clocks in one row read as two things. */
.planner .rz-event-agenda-time {
    display: none;
}

/* -------------------------------------------------- results: MeasurementValues, SessionDeviceResults

   Extracted from the MY portal's Results page when the admin's member Analyse started drawing the
   same thing. Appended here rather than beside the rows and tables they build on: rule order in
   this file is load-bearing, and a new family at the end ties with nothing. */

/* A measure item as a member's result: text on the left, its ValueBandBar on the right. */
.measureitem {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 18px 28px;
    align-items: start;
    padding: 22px 0;
    border-bottom: 1px dashed rgba(106, 97, 29, .25);
}

.measureitem:last-child {
    border-bottom: 0;
}

.measureitem__desc {
    margin-top: 6px;

    /* The descriptions are authored with line breaks that JSON preserves and HTML collapses;
       without this every paragraph runs together on one line. */
    white-space: pre-line;
}

/* Why an item was not measured. Rust rather than muted: it is a gap somebody chose to leave, which
   is the one line in the list a trainer needs to notice. */
.measureitem__omitted {
    color: var(--rust);
}

/* Per-device results table. Nine columns is genuinely wide; the card scrolls it rather than the
   page, per the repo's own rule that wide content scrolls in its own container. */
.devicecard__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 4px;
}

.devicecard__name {
    flex: 1;
}

.devicetable {
    overflow-x: auto;
}

.resultrow {
    grid-template-columns: 70px 80px repeat(5, minmax(96px, 1fr)) 80px 90px;
    min-width: 900px;
}

/* The device's picture. A sized box so a missing or slow image cannot reflow the card, and
   object-fit keeps arbitrary uploads presentable. */
.deviceimage {
    width: 56px;
    height: 56px;
    object-fit: cover;
    border-radius: 12px;
    border: 1px solid var(--border);
    flex: none;
}

@media (max-width: 700px) {
    .measureitem {
        grid-template-columns: 1fr;
    }

    /* Stacked under its text rather than hugging the right edge - centred reads as "this chart
       belongs to the item above", where right-aligned read as clutter. The parent class is not
       decoration: the bare .vbar base rule (justify-self: end) sits earlier in this file, and an
       equally specific override would only win by order - which it once lost. */
    .measureitem .vbar {
        justify-self: center;
    }

    /* The session-results grid is 900px wide and only scrolled on a phone, hiding the header row
       off-screen - so the data meant nothing. Stack instead: the header row disappears and every
       cell draws its own column label (data-label, set in the markup), label left, value right. */
    .resultrow.table__head {
        display: none;
    }

    .table__row.resultrow {
        display: flex;
        flex-direction: column;
        align-items: stretch;
        gap: 8px;
        min-width: 0;
    }

    .table__row.resultrow > span {
        display: flex;
        justify-content: space-between;
        align-items: baseline;
        gap: 16px;
    }

    .table__row.resultrow > span::before {
        content: attr(data-label);
        font: 400 14px/1.4 var(--font);
        color: var(--muted);
    }
}

/* The event strip above a result: a scrolling row of cards, one per session or measurement, the
   selected one tinted. The MY Results page's picker, and the admin's member Analyse is laid out
   the same way on purpose - the two screens show the same thing and should read the same. */
.resultstrip {
    display: flex;
    gap: 12px;
    overflow-x: auto;
    padding-bottom: 4px;
    scrollbar-width: thin;
}

.resultcard {
    flex: 0 0 210px;
    text-align: left;
    border: 1px solid var(--card-border);
    background: #fff;
    border-radius: 14px;
    padding: 15px 16px;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    font: inherit;
    color: inherit;
}

.resultcard:hover {
    border-color: var(--olive);
}

.resultcard--selected {
    background: var(--canvas);
    border-color: var(--brand);
}

/* A card about something that was undone - a session the member was taken off. Kept, because it
   is a fact about the history, and drawn back so it does not read as one they attended. */
.resultcard--muted .resultcard__name,
.resultcard--muted .resultcard__meta {
    color: var(--muted-soft);
}

.resultcard .dot {
    margin-bottom: 10px;
}

.resultcard--selected .dot {
    background: var(--accent);
}

.resultcard__name {
    font: 500 17px/1.3 var(--font);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}

.resultcard__meta {
    font: 400 15px/1.4 var(--font);
    color: var(--muted);
}

/* -------------------------------------------------- reschedule dialog
   Day chips across the top, the free slots for the chosen day below. Written for the MY portal's
   RescheduleDialog and moved here when the admin's RescheduleMemberDialog became its second
   consumer: a trainer moving a member and the member moving themselves look at the same rows. */
.slotdays {
    display: flex;
    gap: 8px;
    margin: 4px 0 18px;
    flex-wrap: wrap;
}

.slotday {
    border: 0;
    background: var(--canvas);
    padding: 12px 19px;
    border-radius: 999px;
    cursor: pointer;
    font: 500 15px/1 var(--font);
    color: var(--muted);
}

.slotday:hover {
    background: var(--border);
}

.slotday--active {
    background: var(--brand);
    color: #fff;
}

/* Fixed height, deliberately. The number of free slots differs per day, so sizing to content made
   the dialog jump every time a day chip was tapped - and the chips themselves move under the pointer
   as it grows, which is how you end up selecting a day you did not mean to. Overflow scrolls instead.
   Sized to show four slots, which is what the artboards draw. */
.slotlist {
    display: flex;
    flex-direction: column;
    gap: 10px;
    height: 340px;
    overflow-y: auto;
    padding-right: 4px;
}

.slot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 14px 18px;

    /* In a fixed-height flex column the default is to shrink children to fit, so a day with eight
       slots would squash them all instead of scrolling. */
    flex: none;
    border: 1px solid var(--border);
    background: #fff;
    border-radius: 14px;
    padding: 16px 18px;
}

.slot--last {
    border-color: var(--accent);
    background: var(--surface-orange);
}

.slot__time {
    margin: 0;
    font: 500 18px/1.2 var(--font);
}

.slot__meta {
    margin: 4px 0 0;
    font: 400 16px/1.3 var(--font);
    color: var(--muted);
}

.slot--last .slot__meta {
    color: var(--rust);
}

.slot__pick {
    border: 0;
    background: var(--brand);
    color: #fff;
    padding: 13px 20px;
    border-radius: var(--radius-control);
    font: 500 16px/1 var(--font);
    cursor: pointer;
}

.slot--last .slot__pick {
    background: var(--accent);
}
