﻿/*
 * BrandSync Component Styles — Membercare.SelfService.Web
 *
 * Uses semantic tokens defined in Variables.css and ConfiguredVariables.css.
 * Customer CSS provides the actual color values — never hardcode hex here.
 *
 * Token layers:
 *   ConfiguredVariables.css  → defines --color-* semantic tokens
 *   This file                → BEM class names consume those tokens
 */

/*
 * File organisation (multi-consumer CSS only — single-consumer CSS lives in the
 * owning component's scoped .razor.css; promote here on the second consumer):
 *   1. Utilities            — single-purpose helper classes
 *   2. Component library    — Bs* component styles
 *   3. Dashboard framework  — shared chassis for dashboard pages and modules
 *   4. Cross-component overrides — rules that must stay global because they
 *      target other components' internals
 *   5. Pages                — page-level shared styles
 */

/* ################################################################
   1. UTILITIES
   ################################################################ */

/* ============================================================
   Content width cap (bs-content-container)
   Single source of truth for the max content width used to keep
   page content readable on wide screens. Apply .bs-content-container
   to any top-level flow/page container that should be capped and
   centred (e.g. the dashboard rows, the enrollment flow root).
   Below the cap the container stays full-width; margin-inline:auto
   only centres once the viewport exceeds --bs-content-max-width.
   ============================================================ */

:root {
    --bs-content-max-width: 1280px;

    /* The heights every module in a dashboard row shares so cards bottom-align, and the
       sidebar column width the dashboard layout's max-width calc is derived from. */
    --dashboard-module-height: 264px;
    --dashboard-module-details-height: 288px;
    --dashboard-sidebar-width: 300px;
}

.bs-content-container {
    max-width: var(--bs-content-max-width);
    margin-inline: auto;
}

/* ============================================================
   Screen-reader-only utility (sr-only)
   Visually hides content while keeping it in the accessibility tree,
   for labels announced to screen readers (e.g. BsBadge / BsChip
   AriaLabel). Bootstrap 5 renamed its own helper to .visually-hidden,
   so this project defines .sr-only globally as the shared utility.
   ============================================================ */

.sr-only {
    position: absolute;
    width: 0.0625rem;
    height: 0.0625rem;
    padding: 0;
    margin: -0.0625rem;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ============================================================
   Touch-target utility (.bs-tap-target)
   BrandSync requires interactive elements to offer a tap area of
   at least 44x44px (2.75rem) with >=8px spacing on this
   mobile-first portal. Apply to small text/icon buttons (btn-link,
   icon-only remove buttons, breadcrumb links) whose content would
   otherwise collapse below that. Composes with any display/layout
   utilities — it only enforces the minimum hit area.
   ============================================================ */
.bs-tap-target {
    min-height: 2.75rem;
    min-width: 2.75rem;
}

/* ============================================================
   Icon button (.bs-icon-button)
   Round, icon-only button with the 44x44px tap area: row actions (the
   message board's "..." menus, the thread-mute bell).
   Always give it an aria-label; the icon inside is aria-hidden.
   Coloured like a Subtle BsButton. WCAG: --color-primary-default icon on the surface
   (non-text, 3:1); hover and aria-pressed="true" fill --color-primary-container.
   ============================================================ */
.bs-icon-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.75rem;
    min-height: 2.75rem;
    border: 0;
    border-radius: 50%;
    background: none;
    color: var(--color-primary-default);
    cursor: pointer;
}

.bs-icon-button:hover,
.bs-icon-button[aria-pressed="true"] {
    background-color: var(--color-primary-container);
}

.bs-icon-button:active {
    background-color: var(--color-primary-container-hover);
}

.bs-icon-button:disabled {
    color: var(--color-text-on-disabled, var(--color-neutrals-hover));
    background: none;
    cursor: not-allowed;
}

.bs-icon-button:focus-visible {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

.bs-icon-button .bs-icon {
    font-size: 1.5rem;
}

/* ============================================================
   Field trigger (.bs-field-trigger)
   A button that looks like a one-line text field and opens an editor
   in its place ("Write a message...", "Write a comment..."). No dark
   pressed state: it should feel like clicking into a field.
   WCAG pair: --color-neutrals-hover prompt text on --color-neutrals-container-hover
   (4.4:1 on the default palette; a prompt, not content, and chosen over the ink
   colour for its look) and --color-neutrals-default on hover.
   ============================================================ */
.bs-field-trigger {
    display: flex;
    align-items: center;
    width: 100%;
    min-height: 2.75rem;
    padding: 0.5rem 1rem;
    overflow: hidden;
    border: 1px solid var(--color-neutrals-container-hover);
    border-radius: 100px;
    background-color: var(--color-neutrals-container-hover);
    color: var(--color-neutrals-hover);
    font: inherit;
    text-align: left;
    text-overflow: ellipsis;
    white-space: nowrap;
    cursor: text;
}

.bs-field-trigger:hover,
.bs-field-trigger:active {
    background-color: var(--color-neutrals-container-pressed);
    color: var(--color-neutrals-default);
}

.bs-field-trigger:focus-visible {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

/* ============================================================
   File rows (.bs-file-rows / .bs-file-row)
   A list of files with the file archive's row anatomy: a type icon,
   the name with one supporting line, and trailing icon buttons.
   Used for message attachments and for files being attached.
   WCAG pairs: --color-neutrals-pressed name and --color-neutrals-hover
   supporting line / icon on the surface; archive icons use
   --color-primary-default.
   ============================================================ */
.bs-file-rows {
    margin: 0;
    padding: 0;
    list-style: none;
    border-top: 1px solid var(--color-border, var(--bs-border-color));
}

.bs-file-row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    min-height: 3.75rem;
    padding: 0.5rem 0.25rem;
    border-bottom: 1px solid var(--color-border, var(--bs-border-color));
}

.bs-file-row__type {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 2.5rem;
    height: 2.5rem;
    font-size: 1.75rem;
    color: var(--color-neutrals-hover);
}

.bs-file-row__type--archive {
    color: var(--color-primary-default);
}

.bs-file-row__main {
    display: flex;
    flex: 1;
    flex-direction: column;
    min-width: 0;
}

.bs-file-row__name {
    overflow: hidden;
    color: var(--color-neutrals-pressed);
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ============================================================
   Two-column list grid utility (.bs-list-grid-2col)
   Shared card-list layout: single column on mobile, two columns from
   768px up. Used by any list of fixed-shape cards (subscriptions,
   memberships, ...) — promoted here after the third component repeated
   the identical rule in its own scoped .razor.css.
   ============================================================ */
.bs-list-grid-2col {
    display: grid;
    grid-template-columns: 1fr;
    gap: 16px;
}

@media (min-width: 768px) {
    .bs-list-grid-2col {
        grid-template-columns: repeat(2, 1fr);
    }
}

/* ============================================================
   Pill surface utility (.bs-pill)
   Shared rounded "pill" surface: a lightly primary-tinted chip shape used for
   compact, secondary information (e.g. the enrollment subtotal pill and the
   companion chips). Consumers add their own content layout, typography and any
   interactive children on top of this surface.
   WCAG pair: place --color-primary-text / --color-neutrals-pressed text on
   the --color-primary-container background.
   ============================================================ */
.bs-pill {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    max-width: 100%;
    padding: 4px 12px;
    border: 1px solid var(--color-primary-container-hover);
    border-radius: 100px;
    background: var(--color-primary-container);
}

/* ============================================================
   Shared text utilities (.bs-heading-sm / .bs-body-text-muted)
   Promoted from the enrollment-flow step components (MEM-8246), where the
   identical rules were duplicated across several scoped stylesheets.
   ============================================================ */

/* Small step/section heading (heading-sm scale). Margin reset so it can sit on
   any <h1>–<h6> element without UA margins breaking the flex-gap rhythm.
   WCAG pair: --color-neutrals-pressed text on the page / card background. */
.bs-heading-sm {
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-heading-sm-size, 24px);
    line-height: var(--font-heading-sm-line-height, 32px);
    font-weight: 600;
    color: var(--color-neutrals-pressed);
    margin: 0;
}

/* Muted body text for empty states, hints and secondary notices.
   WCAG pair: --color-neutrals-hover text on the page / card background. */
.bs-body-text-muted {
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-body-md-regular-size, 16px);
    line-height: var(--font-body-md-regular-line-height, 24px);
    color: var(--color-neutrals-hover);
    margin: 0;
}

/* Quiet inline marker for a secondary fact about an item — "Obligatorisk" on a service,
   hotel room or session. Deliberately not a badge: the fact is worth stating but should not
   compete with the item name for attention.
   WCAG pair: --color-neutrals-hover text on the page / card background. */
.bs-item-meta {
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-caption-md-size, 14px);
    line-height: var(--font-caption-md-line-height, 20px);
    font-weight: 400;
    color: var(--color-neutrals-hover);
}

/* ============================================================
   Padded card (.bs-card--padded)
   A bs-card with the content inset the group page's tabs share: 1rem on a
   phone, 1.5rem from 768px, on the card surface.
   ============================================================ */
.bs-card--padded {
    padding: 1rem;
    background-color: var(--color-neutrals-container);
}

@media (min-width: 768px) {
    .bs-card--padded {
        padding: 1.5rem;
    }
}

/* ============================================================
   Section heading (.bs-section-heading)
   The h2 over a tab's list (messages, announcements): h6 tier, no margin of
   its own so the layout's gap spaces it. WCAG pair: --color-neutrals-pressed
   on the page background.
   ============================================================ */
.bs-section-heading {
    margin: 0;
    font-size: var(--font-h6-size, 1.25rem);
    line-height: var(--font-h6-line-height, 2rem);
    font-weight: 600;
    color: var(--color-neutrals-pressed);
}

/* ============================================================
   Empty state (.bs-empty-state)
   Icon, title and an optional hint in a dashed box where a list would be.
   WCAG pairs: --color-neutrals-default title and --color-neutrals-hover hint
   on the page background; the icon is decorative.
   ============================================================ */
.bs-empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    padding: 2.5rem 1rem;
    border: 2px dashed var(--color-border, var(--bs-border-color));
    border-radius: 0.75rem;
    text-align: center;
}

.bs-empty-state__icon {
    font-size: 3rem;
    color: var(--color-primary-default);
    opacity: 0.7;
}

.bs-empty-state__title {
    margin: 0;
    font-size: var(--font-body-lg-size, 1.125rem);
    line-height: var(--font-body-lg-line-height, 1.75rem);
    font-weight: 600;
}

.bs-empty-state__text {
    margin: 0;
    max-width: 40ch;
    font-size: var(--font-caption-md-size, 0.875rem);
    line-height: var(--font-caption-md-line-height, 1.25rem);
    color: var(--color-neutrals-hover);
}

/* ============================================================
   Fieldset utility (.bs-fieldset / .bs-fieldset__legend)
   A bordered group whose label sits as a legend on the top border; the surface background shows
   through behind the label to cut the border line. Shared by the Interests group and the
   custom-fields group (whose markup comes from the shared BsSelectCustomFields component, so its
   classes are hooked in here rather than by editing that markup). Consumers add their own
   padding/typography.
   ============================================================ */
.bs-fieldset,
.custom-fields-detail-module div.bs-select-custom-fields__group {
    position: relative;
    border: 1px solid var(--color-primary-default);
    border-radius: 8px;
}

/* WCAG pair: --color-primary-default text on --color-neutrals-container (surface) background.
   max-width keeps a long legend inside the box (no horizontal overflow on narrow viewports). */
.bs-fieldset__legend,
.custom-fields-detail-module div.bs-select-custom-fields__group-header {
    position: absolute;
    top: -12px;
    left: 12px;
    z-index: 1;
    max-width: calc(100% - 24px);
    padding: 0 8px;
    background-color: var(--color-neutrals-container);
    color: var(--color-primary-default);
    font-family: var(--font-family, Roboto, sans-serif);
    /* Keep a long legend on one line and inside the box (no horizontal overflow on mobile).
       white-space inherits to a nested title element; text-overflow needs to sit on whichever
       element directly holds the text (the span here; the <h3> for the custom-fields variant). */
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}


/* ################################################################
   2. COMPONENT LIBRARY
   ################################################################ */

/* ============================================================
   Shared input element styles (bs-input__*)
   Used by BsInputText, BsInputTextArea, etc.
   Each component owns only its wrapper class (e.g. .bs-input-text).
   ============================================================ */

/* Shared layout wrapper for all input components */
.bs-input {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* Id target for an input's aria-describedby that adds no box, so no extra gap inside .bs-input */
.bs-input__feedback-slot {
    display: contents;
}

/* Component-specific wrappers — add overrides here as needed */
.bs-input-text { }

.bs-input-email { }

.bs-input-cellphone { }

.bs-input-password { }

.bs-input-date { }

.bs-input-number { }

.bs-input-textarea { }

.bs-input-file { }

.bs-input-textarea .bs-input__control {
    resize: vertical;
    min-height: 80px;
}

/* ============================================================
   BsInputFile (bs-input-file)
   Drop zone wrapper for <input type="file">. The native input is
   positioned absolutely over the zone so the whole area is clickable.
   ============================================================ */

.bs-input-file__dropzone {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 24px 16px;
    border: 2px dashed var(--color-neutrals-hover);
    border-radius: 4px;
    background-color: var(--color-neutrals-container);
    cursor: pointer;
    text-align: center;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

.bs-input-file__dropzone:hover {
    border-color: var(--color-primary-focused);
    background-color: var(--color-primary-container);
}

.bs-input-file__dropzone:has(.bs-input-file__native:focus-visible) {
    border-color: var(--color-primary-focused);
    background-color: var(--color-primary-container);
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

/* WCAG pair: --color-error-default border on --color-neutrals-container background */
.bs-input--error .bs-input-file__dropzone {
    border-color: var(--color-error-default);
    box-shadow: 0 0 0 3px var(--color-error-container);
}

.bs-input-file__dropzone:has(.bs-input-file__native:disabled) {
    background-color: var(--color-neutrals-container-hover);
    border-color: var(--color-neutrals-hover);
    cursor: not-allowed;
}

.bs-input-file__icon {
    font-size: 24px;
    color: var(--color-primary-default);
}

.bs-input-file__prompt {
    font-family: var(--font-family, Roboto, sans-serif);
    font-weight: 400;
    font-size: var(--font-body-md-regular-size, 16px);
    line-height: var(--font-body-md-regular-line-height, 24px);
    color: var(--color-neutrals-hover);
}

.bs-input-file__selected {
    font-family: var(--font-family, Roboto, sans-serif);
    font-weight: 500;
    font-size: var(--font-body-md-regular-size, 16px);
    line-height: var(--font-body-md-regular-line-height, 24px);
    color: var(--color-neutrals-default);
}

.bs-input-file__native {
    opacity: 0;
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    cursor: pointer;
}

.bs-input-file__native:disabled {
    cursor: not-allowed;
}

/* Button variant (BsInputFile Variant="Button"): the same native input, wrapped by a
   BrandSync button-styled label instead of a dropzone, for places where a plain "Upload"
   action fits better than a permanent drop area (BrandSync File Upload: use a simple upload
   button when space is limited). The absolutely positioned native input covers the label,
   so the whole button is the click target and stays keyboard-focusable. */
.bs-input-file__button {
    position: relative;
    min-height: 2.75rem;
    margin: 0;
}

.bs-input-file__button:has(.bs-input-file__native:focus-visible) {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

.bs-input-file__button:has(.bs-input-file__native:disabled) {
    opacity: 0.6;
    cursor: not-allowed;
}

.bs-input__label {
    font-family: var(--font-family, Roboto, sans-serif);
    font-weight: 500;
    font-size: var(--font-caption-lg-size, 16px);
    line-height: var(--font-caption-lg-line-height, 24px);
    color: var(--color-neutrals-default);
}

/* Optional BsIcon hint before the label text (BsInputText/BsMultiSelect's LabelIcon fragment).
   8px gap matches the icon-text spacing already used elsewhere (e.g. .bs-card-detail-row). */
.bs-input__label .bs-icon {
    margin-right: 8px;
    color: var(--color-neutrals-hover);
}

.bs-input__required,
.bs-checkbox__required {
    color: var(--color-error-default);
    margin-left: 2px;
}

.bs-input__control {
    font-family: var(--font-family, Roboto, sans-serif);
    font-weight: 400;
    font-size: var(--font-body-md-regular-size, 16px);
    line-height: var(--font-body-md-regular-line-height, 24px);
    padding: 8px 12px;
    border: 1px solid var(--color-neutrals-border);
    border-radius: 0.375rem;
    background-color: var(--color-neutrals-container);
    color: var(--color-neutrals-default);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
    width: 100%;
    box-sizing: border-box;
    min-height: 2.75rem; /* 44px tap target */
}

.bs-input__control:where(:hover:not(:disabled):not([readonly])) {
    border-color: var(--color-neutrals-hover);
}

.bs-input__control::placeholder {
    font-weight: 400;
    color: var(--color-neutrals-hover);
}

.bs-input__control:focus-visible {
    border-color: var(--color-primary-focused);
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

.bs-input__control:disabled {
    background-color: var(--color-neutrals-container-hover);
    color: var(--color-neutrals-hover);
    border-color: var(--color-neutrals-hover);
    cursor: not-allowed;
}

.bs-input__control[readonly] {
    background-color: color-mix(in srgb, var(--color-neutrals-container-focused) 25%, var(--color-page-background));
    color: var(--color-neutrals-focused);
    border-color: var(--color-neutrals-hover);
}

/* Field row: the control with an optional in-field clear button, followed by trailing content
   (e.g. a submit button) on the same line. */
.bs-input__group {
    display: flex;
    align-items: stretch;
    gap: 0.5rem; /* 8px */
}

.bs-input__field {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
}

.bs-input__control--clearable {
    padding-right: 2.75rem; /* room for the clear button */
}

/* WCAG pair: --color-neutrals-default icon on --color-neutrals-container background. */
.bs-input__clear {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem; /* 44px tap target */
    padding: 0;
    border: none;
    border-radius: 0.375rem;
    background: transparent;
    color: var(--color-neutrals-default);
    cursor: pointer;
}

.bs-input__clear:hover {
    color: var(--color-primary-default);
}

.bs-input__clear:focus-visible {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: -2px;
}

/* Nothing to clear while the field is empty. */
.bs-input__control:placeholder-shown + .bs-input__clear {
    display: none;
}

/* WCAG pair: --color-neutrals-default text on --color-neutrals-container background */
.bs-input--error .bs-input__control {
    border-color: var(--color-error-default);
    box-shadow: 0 0 0 3px var(--color-error-container);
}

.bs-input--error .bs-input__control:focus-visible {
    border-color: var(--color-error-default);
    outline: 2px solid var(--color-error-default);
    outline-offset: 2px;
    box-shadow: 0 0 0 3px var(--color-error-container);
}

/* Native selects and dates inside .bs-input get Blazor's valid/invalid classes, which site.css outlines; BrandSync draws those states itself */
.bs-input__control.valid.modified:not([type=checkbox]):not([type=radio]),
.bs-input__control.invalid {
    outline: none;
}

.bs-input__control.valid.modified:not([type=checkbox]):not([type=radio]):focus-visible {
    outline: 2px solid var(--color-primary-focused);
}

.bs-input__control.invalid:focus-visible {
    outline: 2px solid var(--color-error-default);
}

/* Keep room for the select chevron that .bs-input__control's padding would otherwise cover */
.bs-input__control.form-select {
    padding-right: 2.25rem;
}

.bs-input__feedback {
    font-family: var(--font-family, Roboto, sans-serif);
    font-weight: 400;
    font-size: var(--font-caption-md-size, 14px);
    line-height: var(--font-caption-md-line-height, 20px);
    color: var(--color-neutrals-hover);
}

/* WCAG pair: --color-error-default on --color-neutrals-container background */
.bs-input__feedback--error {
    color: var(--color-error-default);
}

.bs-input__feedback .validation-message {
    color: inherit;
}

/* ============================================================
   BsCheckBox (bs-checkbox)
   ============================================================ */

.bs-checkbox {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.bs-checkbox__field {
    display: flex;
    align-items: flex-start;
    gap: 8px;
}

/* Nudge the box onto the label's first line ((24px line box - 16px control) / 2), so a wrapping
   label keeps it pinned there instead of centring on the whole block. Scoped to BsCheckBox's own
   markup: .bs-checkbox__field is also borrowed by HotelRoomCard and .bs-checkbox__control by
   BsCheckBoxList, both of which centre a single-line row and must not get the offset. */
.bs-checkbox > .bs-checkbox__field > .bs-checkbox__control {
    margin-top: 0.25rem;
}

.bs-checkbox__control {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    accent-color: var(--color-primary-default);
    cursor: pointer;
    transition: outline 0.15s ease, outline-offset 0.15s ease;
}

.bs-checkbox__control:hover:not(:disabled) {
    outline: 2px solid var(--color-primary-container);
    outline-offset: 2px;
}

.bs-checkbox__control:focus-visible {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

.bs-checkbox__control:disabled {
    cursor: not-allowed;
    opacity: 0.5;
}

.bs-checkbox__control:checked:disabled {
    opacity: 0.5;
    accent-color: var(--color-neutrals-hover);
}

.bs-checkbox__label {
    font-family: var(--font-family, Roboto, sans-serif);
    font-weight: 400;
    font-size: var(--font-body-md-regular-size, 16px);
    line-height: var(--font-body-md-regular-line-height, 24px);
    color: var(--color-neutrals-default);
    cursor: pointer;
}

.bs-checkbox:has(.bs-checkbox__control:disabled) .bs-checkbox__label {
    cursor: not-allowed;
}

/* Opt-in for a checkbox standing on its own in the page flow (terms, consent declarations). Not on
   .bs-checkbox itself — that would shift every checkbox already laid out by its container. */
.bs-checkbox--spaced {
    margin-block: 1rem;
}

.bs-checkbox--error .bs-checkbox__control {
    outline: 2px solid var(--color-error-default);
    outline-offset: 2px;
}

.bs-checkbox--error .bs-checkbox__control:focus-visible {
    outline: 2px solid var(--color-error-default);
    outline-offset: 2px;
}

.bs-checkbox__feedback {
    font-family: var(--font-family, Roboto, sans-serif);
    font-weight: 400;
    font-size: var(--font-caption-md-size, 14px);
    line-height: var(--font-caption-md-line-height, 20px);
    color: var(--color-neutrals-hover);
    padding-left: 24px;
}

/* WCAG pair: --color-error-default on --color-neutrals-container background */
.bs-checkbox__feedback--error {
    color: var(--color-error-default);
}

/* ============================================================
   BsButton (bs-button)
   Every size keeps a 2.75rem (44px) minimum tap target (WCAG 2.5.5).
   ============================================================ */

.bs-button {
    display: inline-flex;
    min-height: 2.75rem;
    min-width: 2.75rem;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.375rem 0.75rem;
    font-family: var(--font-family, Roboto, sans-serif);
    font-weight: 500;
    font-size: var(--font-body-md-regular-size, 1rem);
    line-height: var(--font-body-md-regular-line-height, 1.5rem);
    border-radius: 0.375rem;
    border: 1px solid transparent;
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
    white-space: nowrap;
    user-select: none;
    text-decoration: none;
}

.bs-button:disabled,
.bs-button--loading {
    cursor: not-allowed;
    opacity: 0.5;
}

/* Size modifiers */
.bs-button--small {
    padding: 0.25rem 0.5rem;
    font-size: var(--font-body-md-regular-size, 1rem);
    line-height: var(--font-body-md-regular-line-height, 1.5rem);
}

.bs-button--large {
    padding: 0.75rem 1.5rem;
    font-size: var(--font-body-lg-size, 1.125rem);
    line-height: var(--font-body-lg-line-height, 1.75rem);
}

/* Primary */
.bs-button--primary {
    background-color: var(--color-primary-default);
    color: var(--color-on-primary);
    border-color: var(--color-primary-default);
}

.bs-button--primary:hover:not(:disabled) {
    background-color: var(--color-primary-hover);
    color: var(--color-on-primary-hover);
    border-color: var(--color-primary-hover);
}

.bs-button--primary:focus-visible {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

.bs-button--primary:active:not(:disabled) {
    background-color: var(--color-primary-pressed);
    color: var(--color-on-primary-pressed);
    border-color: var(--color-primary-pressed);
}

/* Neutral */
.bs-button--neutral {
    background-color: var(--color-neutrals-container-hover);
    color: var(--color-neutrals-default);
    border-color: var(--color-neutrals-container-hover);
}

.bs-button--neutral:hover:not(:disabled) {
    background-color: var(--color-neutrals-container-pressed);
    border-color: var(--color-neutrals-container-pressed);
}

.bs-button--neutral:focus-visible {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

.bs-button--neutral:active:not(:disabled) {
    background-color: var(--color-neutrals-pressed);
    color: var(--color-neutrals-container);
    border-color: var(--color-neutrals-pressed);
}

/* Outlined */
.bs-button--outlined {
    background-color: transparent;
    color: var(--color-primary-default);
    border-color: var(--color-primary-default);
}

.bs-button--outlined:hover:not(:disabled) {
    background-color: var(--color-primary-container);
    color: var(--color-primary-text);
}

.bs-button--outlined:focus-visible {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

.bs-button--outlined:active:not(:disabled) {
    background-color: var(--color-primary-container-hover);
    color: var(--color-primary-text);
}

/* Subtle */
.bs-button--subtle {
    background-color: transparent;
    color: var(--color-primary-default);
    border-color: transparent;
}

.bs-button--subtle:hover:not(:disabled) {
    background-color: var(--color-primary-container);
    color: var(--color-primary-text);
}

.bs-button--subtle:focus-visible {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

.bs-button--subtle:active:not(:disabled) {
    background-color: var(--color-primary-container-hover);
    color: var(--color-primary-text);
}

/* Success */
.bs-button--success {
    background-color: var(--color-success-default);
    color: var(--color-neutrals-container);
    border-color: var(--color-success-default);
}

.bs-button--success:hover:not(:disabled) {
    filter: brightness(0.9);
}

.bs-button--success:active:not(:disabled) {
    filter: brightness(0.8);
}

.bs-button--success:focus-visible {
    outline: 2px solid var(--color-success-default);
    outline-offset: 2px;
}

/* Warning */
.bs-button--warning {
    background-color: var(--color-warning-default);
    color: var(--color-neutrals-container);
    border-color: var(--color-warning-default);
}

.bs-button--warning:hover:not(:disabled) {
    background-color: var(--color-warning-hover);
    border-color: var(--color-warning-hover);
}

.bs-button--warning:active:not(:disabled) {
    background-color: var(--color-warning-pressed);
    border-color: var(--color-warning-pressed);
}

.bs-button--warning:focus-visible {
    outline: 2px solid var(--color-warning-default);
    outline-offset: 2px;
}

/* Error */
.bs-button--error {
    background-color: var(--color-error-default);
    color: var(--color-neutrals-container);
    border-color: var(--color-error-default);
}

.bs-button--error:hover:not(:disabled) {
    filter: brightness(0.9);
}

.bs-button--error:active:not(:disabled) {
    filter: brightness(0.8);
}

.bs-button--error:focus-visible {
    outline: 2px solid var(--color-error-default);
    outline-offset: 2px;
}

/* Loading spinner */
.bs-button__spinner {
    width: 1rem;
    height: 1rem;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: bs-button-spin 0.6s linear infinite;
    flex-shrink: 0;
}

@keyframes bs-button-spin {
    to { transform: rotate(360deg); }
}

/* ============================================================
   BsCheckBoxList (bs-checkbox-list)
   ============================================================ */

/* The block is a <fieldset> and the label a <legend>: reset their UA and Bootstrap reboot styles so
   they render like plain block elements. min-width: 0 lets the fieldset shrink below its content
   width on narrow screens. A floated legend is laid out as an ordinary flex item, so the gap still
   applies between it and the items. */
.bs-checkbox-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-width: 0;
    margin: 0;
    padding: 0;
    border: 0;
}

.bs-checkbox-list__label {
    float: left;
    width: 100%;
    margin: 0;
    padding: 0;
    font-family: var(--font-family, Roboto, sans-serif);
    font-weight: 500;
    font-size: var(--font-caption-lg-size, 16px);
    line-height: var(--font-caption-lg-line-height, 24px);
    color: var(--color-neutrals-default);
}

.bs-checkbox-list__items {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.bs-checkbox-list__items--horizontal {
    flex-direction: row;
    flex-wrap: wrap;
}

.bs-checkbox-list__item {
    display: flex;
    align-items: center;
    gap: 8px;
}

.bs-checkbox-list__feedback {
    font-family: var(--font-family, Roboto, sans-serif);
    font-weight: 400;
    font-size: var(--font-caption-md-size, 14px);
    line-height: var(--font-caption-md-line-height, 21px);
    color: var(--color-neutrals-hover);
}

.bs-checkbox-list__feedback--error {
    color: var(--color-error-default);
}

/* Cross-block dependency: targets .bs-checkbox__control from the bs-checkbox block.
   If bs-checkbox element names change, update these selectors accordingly. */
.bs-checkbox-list--error .bs-checkbox__control {
    outline: 2px solid var(--color-error-default);
    outline-offset: 2px;
}

.bs-checkbox-list--error .bs-checkbox__control:focus-visible {
    outline: 2px solid var(--color-error-default);
    outline-offset: 2px;
}

.bs-checkbox-list__item:has(.bs-checkbox__control:disabled) .bs-checkbox__label {
    cursor: not-allowed;
}

/* ============================================================
   BsRadioGroup (bs-radio-group)
   ============================================================ */

/* Same <fieldset>/<legend> reset as .bs-checkbox-list. */
.bs-radio-group {
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-width: 0;
    margin: 0;
    padding: 0;
    border: 0;
}

.bs-radio-group__label {
    float: left;
    width: 100%;
    margin: 0;
    padding: 0;
    font-family: var(--font-family, Roboto, sans-serif);
    font-weight: 500;
    font-size: var(--font-caption-lg-size, 16px);
    line-height: var(--font-caption-lg-line-height, 24px);
    color: var(--color-neutrals-default);
}

/* Keeps the Label accessible to screen readers (via aria-labelledby) without showing
   it visually — for radio groups that act as a modifier on an adjacent labeled field. */
.bs-radio-group--label-visually-hidden .bs-radio-group__label {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.bs-radio-group__items {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.bs-radio-group__items--horizontal {
    flex-direction: row;
    flex-wrap: wrap;
}

.bs-radio-group__item {
    display: flex;
    align-items: center;
    gap: 8px;
}

.bs-radio-group__feedback {
    font-family: var(--font-family, Roboto, sans-serif);
    font-weight: 400;
    font-size: var(--font-caption-md-size, 14px);
    line-height: var(--font-caption-md-line-height, 24px);
    color: var(--color-neutrals-hover);
}

.bs-radio-group__feedback--error {
    color: var(--color-error-default);
}

/* Cross-block dependency: targets .bs-radio__control from the bs-radio block.
   If bs-radio element names change, update these selectors accordingly. */
.bs-radio-group--error .bs-radio__control {
    outline: 2px solid var(--color-error-default);
    outline-offset: 2px;
}

.bs-radio-group--error .bs-radio__control:focus-visible {
    outline: 2px solid var(--color-error-default);
    outline-offset: 2px;
}

.bs-radio-group__item:has(.bs-radio__control:disabled) .bs-radio__label {
    cursor: not-allowed;
}

/* ============================================================
   BsRadio (bs-radio)
   Individual radio control — used inside BsRadioGroup.
   ============================================================ */

.bs-radio__control {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    accent-color: var(--color-primary-default);
    cursor: pointer;
    transition: outline 0.15s ease, outline-offset 0.15s ease;
}

.bs-radio__control:hover:not(:disabled) {
    outline: 2px solid var(--color-primary-container);
    outline-offset: 2px;
}

.bs-radio__control:focus-visible {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

.bs-radio__control:disabled {
    cursor: not-allowed;
    opacity: 0.5;
}

.bs-radio__control:checked:disabled {
    opacity: 0.5;
    accent-color: var(--color-neutrals-hover);
}

.bs-radio__label {
    font-family: var(--font-family, Roboto, sans-serif);
    font-weight: 400;
    font-size: var(--font-body-md-regular-size, 16px);
    line-height: var(--font-body-md-regular-line-height, 24px);
    color: var(--color-neutrals-default);
    cursor: pointer;
}

/* MEM-8581: multi-line ItemTemplate content (e.g. a category card with seats + price) needs the
   label to grow full-width and top-align with the radio input, instead of the single-line
   center-aligned default. Additive — only applies when ItemTemplate is set. */
.bs-radio-group__item:has(.bs-radio__label--template) {
    align-items: flex-start;
}

.bs-radio__label--template {
    display: block;
    width: 100%;
}

/* ============================================================
   BsRadioGroup — SelectionCard variant (bs-radio-group--selection-card)
   Opt-in. Every selector below is prefixed with the variant modifier so the
   default radio groups are untouched.
   The native input stays in the DOM (focusable, arrow-key navigable) but is
   visually hidden; the label becomes the full-width click target, and selection
   is conveyed by an accent border plus a check mark — never by colour alone.
   ============================================================ */

.bs-radio-group--selection-card .bs-radio-group__items {
    flex-direction: column;
    gap: 8px;
}

/* MEM-9133: mobile-first — the base rule above always keeps SelectionCard options stacked,
   regardless of BsRadioGroup's Direction parameter (its own bs-radio-group__items--horizontal rule
   has lower specificity than the selector above and would otherwise never win). From the tablet
   breakpoint up, a group that opted into Direction="Horizontal" lays its cards out side by side and
   wraps to further rows as needed — auto-fit sizes each card from the number of options actually
   present (1-6 categories) instead of a fixed column count, so 2 options split the row evenly and 6
   wrap across multiple rows without needing per-count markup. */
@media (min-width: 768px) {
    .bs-radio-group--selection-card .bs-radio-group__items--horizontal {
        flex-direction: row;
        flex-wrap: wrap;
    }

    .bs-radio-group--selection-card .bs-radio-group__items--horizontal .bs-radio-group__item--selection-card {
        flex: 1 1 12rem;
        max-width: 18rem;
    }
}

.bs-radio-group--selection-card .bs-radio-group__item--selection-card {
    display: block;
    position: relative;
}

/* Clipped rather than display:none/visibility:hidden — those remove the input from the
   tab order and break the browser's native arrow-key navigation within the group. */
.bs-radio-group--selection-card .bs-radio-group__item--selection-card .bs-radio__control {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    padding: 0;
    opacity: 0;
    pointer-events: none;
}

/* The base block paints hover/focus/error outlines on the control itself; on a 1px clipped
   element that renders as a stray dot, so the affordances move to the card below. */
.bs-radio-group--selection-card .bs-radio-group__item--selection-card .bs-radio__control,
.bs-radio-group--selection-card .bs-radio-group__item--selection-card .bs-radio__control:hover:not(:disabled),
.bs-radio-group--selection-card .bs-radio-group__item--selection-card .bs-radio__control:focus-visible {
    outline: none;
}

.bs-radio-group--selection-card .bs-radio__label--selection-card {
    display: block;
    width: 100%;
    position: relative;
    /* Inline-end padding reserves room for the check mark so long option text cannot run under it. */
    padding: 16px 48px 16px 16px;
    border: 1px solid var(--color-neutrals-container-hover);
    border-radius: 8px;
    background: var(--color-neutrals-container);
    cursor: pointer;
    transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}

.bs-radio-group--selection-card .bs-radio-group__item--selection-card:hover .bs-radio__label--selection-card {
    border-color: var(--color-primary-container);
}

.bs-radio-group--selection-card .bs-radio-group__item--selection-card:has(.bs-radio__control:focus-visible) .bs-radio__label--selection-card {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

/* Selected: heavier accent border (inset ring keeps the box size stable) + the check mark. */
.bs-radio-group--selection-card .bs-radio-group__item--selection-card:has(.bs-radio__control:checked) .bs-radio__label--selection-card {
    border-color: var(--color-primary-default);
    box-shadow: inset 0 0 0 1px var(--color-primary-default);
}

.bs-radio-group--selection-card .bs-radio-group__item--selection-card:has(.bs-radio__control:disabled) .bs-radio__label--selection-card {
    cursor: not-allowed;
    opacity: 0.5;
}

.bs-radio-group--selection-card.bs-radio-group--error .bs-radio__label--selection-card {
    border-color: var(--color-error-default);
}

.bs-radio-group--selection-card .bs-radio__check {
    position: absolute;
    top: 16px;
    right: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    color: var(--color-primary-default);
}

.bs-radio-group--selection-card .bs-radio__check svg {
    width: 16px;
    height: 16px;
}

/* ============================================================
   BsRadioGroup: Chip variant (bs-radio-group--chip)
   Opt-in. Compact single-choice chips (e.g. an arrival weekday) that always wrap in rows,
   whatever the Direction parameter says. As with SelectionCard, the native input stays in
   the DOM (focusable, arrow-key navigable) but is visually hidden, and the label becomes a
   2.75rem (44px) tap target. Selection is a filled chip plus bold text, not colour alone.
   WCAG pair (checked): --color-on-primary text on --color-primary-default fill.
   ============================================================ */

.bs-radio-group--chip .bs-radio-group__items {
    flex-direction: row;
    flex-wrap: wrap;
    gap: 8px;
}

.bs-radio-group--chip .bs-radio-group__item--chip {
    position: relative;
    align-items: center;
}

.bs-radio-group--chip .bs-radio-group__item--chip .bs-radio__control {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    padding: 0;
    opacity: 0;
    pointer-events: none;
}

/* The base block paints hover/focus/error outlines on the control; on the clipped 1px input
   that is a stray dot, so the affordances move to the chip. */
.bs-radio-group--chip .bs-radio-group__item--chip .bs-radio__control,
.bs-radio-group--chip .bs-radio-group__item--chip .bs-radio__control:hover:not(:disabled),
.bs-radio-group--chip .bs-radio-group__item--chip .bs-radio__control:focus-visible,
.bs-radio-group--chip.bs-radio-group--error .bs-radio-group__item--chip .bs-radio__control {
    outline: none;
}

.bs-radio-group--chip .bs-radio__label--chip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: auto;
    min-height: 2.75rem;
    min-width: 2.75rem;
    padding: 8px 16px;
    border: 1px solid var(--color-neutrals-container-pressed);
    border-radius: 8px;
    background: var(--color-neutrals-container);
    color: var(--color-neutrals-default);
    white-space: nowrap;
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease;
}

.bs-radio-group--chip .bs-radio-group__item--chip:hover .bs-radio__label--chip {
    border-color: var(--color-primary-default);
}

.bs-radio-group--chip .bs-radio-group__item--chip:has(.bs-radio__control:focus-visible) .bs-radio__label--chip {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

.bs-radio-group--chip .bs-radio-group__item--chip:has(.bs-radio__control:checked) .bs-radio__label--chip {
    border-color: var(--color-primary-default);
    background: var(--color-primary-default);
    color: var(--color-on-primary);
    font-weight: 600;
}

.bs-radio-group--chip.bs-radio-group--error .bs-radio__label--chip {
    border-color: var(--color-error-default);
}

.bs-radio-group--chip .bs-radio-group__item--chip:has(.bs-radio__control:disabled) .bs-radio__label--chip {
    cursor: not-allowed;
    opacity: 0.5;
}

/* ============================================================
   BsSelect (bs-select)
   ============================================================ */

.bs-select { }

.bs-select .bs-select__control {
    font-family: var(--font-family, Roboto, sans-serif);
    font-weight: 400;
    font-size: var(--font-body-md-regular-size, 16px);
    line-height: var(--font-body-md-regular-line-height, 24px);
    border: 1px solid var(--color-neutrals-border);
    border-radius: 0.375rem;
    background-color: var(--color-neutrals-container);
    color: var(--color-neutrals-default);
    width: 100%;
    box-sizing: border-box;
    min-height: 2.75rem; /* 44px tap target */
}

.bs-select .bs-select__control .dxbl-text-edit-input {
    font-family: inherit;
    font-weight: inherit;
    font-size: inherit;
    line-height: inherit;
    color: inherit;
    background-color: transparent;
    padding: 8px 12px;
}

.bs-select .bs-select__control .dxbl-text-edit-input::placeholder {
    color: var(--color-neutrals-hover);
}

.bs-select .bs-select__control.dxbl-disabled {
    background-color: var(--color-neutrals-container-hover);
    border-color: var(--color-neutrals-hover);
    cursor: not-allowed;
}

.bs-select .bs-select__control.dxbl-disabled .dxbl-text-edit-input {
    color: var(--color-neutrals-hover);
    cursor: not-allowed;
}

.bs-select .bs-select__control:focus-within,
.bs-select .bs-select__control.dxbl-focused {
    border-color: var(--color-primary-focused);
    box-shadow: none;
}

.bs-select .bs-select__control:where(:hover:not(.dxbl-disabled)) {
    border-color: var(--color-neutrals-hover);
}

.bs-input--error .bs-select__control {
    border-color: var(--color-error-default);
    box-shadow: 0 0 0 3px var(--color-error-container);
}

/* DevExpress draws the focus state from these variables with a more specific selector than ours */
.bs-select .bs-select__control,
.bs-multi-select .bs-multi-select__control {
    --dxbl-text-edit-focus-border-color: var(--color-primary-focused);
    --dxbl-text-edit-focus-shadow-spread: 0;
}

.bs-input--error .bs-select__control,
.bs-input--error .bs-multi-select__control {
    --dxbl-text-edit-focus-border-color: var(--color-error-default);
    --dxbl-text-edit-focus-shadow-blur: 0;
    --dxbl-text-edit-focus-shadow-spread: 3px;
    --dxbl-text-edit-focus-shadow-color: var(--color-error-container);
}

dxbl-dropdown-dialog.dxbl-edit-dropdown {
    background-color: var(--color-neutrals-container);
    border-color: var(--color-neutrals-container-pressed);
}

/* ============================================================
   BsPersonSelector (bs-person-selector)
   Control chrome (border/background/focus/disabled/error) is
   inherited from BsSelect - BsPersonSelector composes on it and
   only needs its own dropdown item template styling here.
   ============================================================ */

.bs-person-selector__item {
    padding: 8px 0;
}

.bs-person-selector__item-primary {
    font-weight: 600;
}

.bs-person-selector__item-secondary {
    font-size: var(--font-body-sm-size, 14px);
    color: var(--color-neutrals-hover);
}

.dxbl-list-item.dxbl-list-item-selected {
    background-color: var(--color-primary-default);
    color: var(--color-on-primary);
}

.dxbl-list-item.dxbl-list-item-focused:not(.dxbl-list-item-selected) {
    background-color: var(--color-primary-container);
    color: var(--color-neutrals-default);
}

/* WCAG pair: --color-neutrals-default on --color-neutrals-container. */
dxbl-dropdown-dialog.dxbl-edit-dropdown .dxbl-listbox {
    background-color: var(--color-neutrals-container);
    color: var(--color-neutrals-default);
}

dxbl-dropdown-dialog.dxbl-edit-dropdown .dxbl-list-item {
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-body-md-regular-size, 1rem);
    line-height: var(--font-body-md-regular-line-height, 1.5rem);
    color: var(--color-neutrals-default);
    padding: 0.5rem 0.75rem;
}

/* The options of a select or tag box dropdown: 44px tap target. */
dxbl-dropdown-dialog.dxbl-edit-dropdown [role="option"] {
    min-height: 2.75rem;
}

dxbl-dropdown-dialog.dxbl-edit-dropdown .dxbl-list-item:hover:not(.dxbl-list-item-selected) {
    background-color: var(--color-neutrals-container-hover);
    color: var(--color-neutrals-default);
}

/* ============================================================
   BsMultiSelect (bs-multi-select)
   ============================================================ */

.bs-multi-select { }

.bs-multi-select .bs-multi-select__control {
    font-family: var(--font-family, Roboto, sans-serif);
    font-weight: 400;
    font-size: var(--font-body-md-regular-size, 1rem);
    line-height: var(--font-body-md-regular-line-height, 1.5rem);
    border: 1px solid var(--color-neutrals-border);
    border-radius: 0.375rem;
    background-color: var(--color-neutrals-container);
    color: var(--color-neutrals-default);
    width: 100%;
    box-sizing: border-box;
    padding: 8px 12px;
    min-height: 2.75rem; /* 44px tap target, the same as .bs-input__control */
}

.bs-multi-select .bs-multi-select__control .dxbl-text-edit-input {
    font-family: inherit;
    font-weight: inherit;
    font-size: inherit;
    line-height: inherit;
    color: inherit;
    background-color: transparent;
    padding: 0;
}

.bs-multi-select .bs-multi-select__control .dxbl-text-edit-input::placeholder {
    color: var(--color-neutrals-hover);
}

.bs-multi-select .bs-multi-select__control.dxbl-disabled {
    background-color: var(--color-neutrals-container-hover);
    border-color: var(--color-neutrals-hover);
    cursor: not-allowed;
}

.bs-multi-select .bs-multi-select__control.dxbl-disabled .dxbl-text-edit-input {
    color: var(--color-neutrals-hover);
    cursor: not-allowed;
}

.bs-multi-select .bs-multi-select__control:focus-within,
.bs-multi-select .bs-multi-select__control.dxbl-focused {
    border-color: var(--color-primary-focused);
    box-shadow: none;
}

.bs-multi-select .bs-multi-select__control:where(:hover:not(.dxbl-disabled)) {
    border-color: var(--color-neutrals-hover);
}

.bs-input--error .bs-multi-select__control {
    border-color: var(--color-error-default);
    box-shadow: 0 0 0 0.1875rem var(--color-error-container);
}

/* WCAG pair: --color-error-default on --color-neutrals-container background */
.bs-input--error .bs-multi-select__control:focus-within,
.bs-input--error .bs-multi-select__control.dxbl-focused {
    border-color: var(--color-error-default);
    box-shadow: 0 0 0 0.1875rem var(--color-error-container);
}


/* ============================================================
   Alert / Snackbar (bs-alert)
   Inline feedback banner styled after the BrandSync Snackbar spec.
   Color pairs: *-text on *-container background (see Variables.css).
   ============================================================ */
.bs-alert {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 16px;
    border-radius: 4px;
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-body-md-size, 16px);
    line-height: var(--font-body-md-line-height, 24px);
}

.bs-alert__body {
    flex: 1;
    padding-right: 8px;
}

.bs-alert__action {
    background: none;
    border: none;
    cursor: pointer;
    font-family: inherit;
    font-size: var(--font-body-md-size, 16px);
    font-weight: 500;
    color: inherit;
    padding: 0 16px;
    min-height: 44px;
    align-self: center;
    white-space: nowrap;
    text-decoration: underline;
    transition: opacity 0.15s ease;
}

.bs-alert__action:hover          { opacity: 0.8; }
.bs-alert__action:active         { opacity: 0.7; }
.bs-alert__action:focus-visible  { outline: 2px solid currentColor; outline-offset: 2px; }

.bs-alert__close {
    background: none;
    border: none;
    cursor: pointer;
    padding: 0 16px;
    min-height: 44px;
    min-width: 44px;
    align-self: center;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: var(--font-h6-size, 20px);
    line-height: var(--font-h6-line-height, 32px);
    color: inherit;
    transition: opacity 0.15s ease;
}

.bs-alert__close:hover          { opacity: 0.8; }
.bs-alert__close:active         { opacity: 0.9; }
.bs-alert__close:focus-visible  { outline: 2px solid currentColor; outline-offset: 2px; }

/* WCAG pair: --color-information-text on --color-information-container, >= 7:1 for any configured colour */
.bs-alert--information { background-color: var(--color-information-container); color: var(--color-information-text); }
/* WCAG pair: --color-success-text on --color-success-container, >= 7:1 for any configured colour */
.bs-alert--success     { background-color: var(--color-success-container); color: var(--color-success-text); }
/* WCAG pair: --color-warning-text on --color-warning-container, >= 7:1 for any configured colour */
.bs-alert--warning     { background-color: var(--color-warning-container); color: var(--color-warning-text); }
/* WCAG pair: --color-error-text on --color-error-container, >= 7:1 for any configured colour */
.bs-alert--error       { background-color: var(--color-error-container); color: var(--color-error-text); }
/* WCAG pair: --color-neutrals-default text on --color-neutrals-container-pressed background — customer must ensure 4.5:1 */
.bs-alert--neutral     { background-color: var(--color-neutrals-container-pressed); color: var(--color-neutrals-default); }

/* ============================================================
   Badge (bs-badge)
   Pill-shaped label for status, categories, and highlights.
   Color pairs: *-text on *-container background (see Variables.css).
   Sized in em relative to its own font-size (line-height: 1 + em padding),
   not a fixed px box, so it never renders taller than the text beside it.
   ============================================================ */
.bs-badge {
    display: inline-flex;
    align-items: center;
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-caption-md-size, 14px);
    line-height: 1;
    font-weight: 400;
    padding: 0.25em 0.65em;
    border-radius: 100px;
    white-space: nowrap;
}

.bs-badge--primary {
    background-color: var(--color-primary-container);
    color: var(--color-primary-text);
}

.bs-badge--neutral {
    background-color: var(--color-neutrals-container-pressed);
    color: var(--color-neutrals-default);
}

.bs-badge--success {
    background-color: var(--color-success-container);
    color: var(--color-success-text);
}


.bs-badge--error {
    background-color: var(--color-error-container);
    color: var(--color-error-text);
}

.bs-badge--warning {
    background-color: var(--color-warning-container);
    color: var(--color-warning-text);
}

.bs-badge--info {
    background-color: var(--color-information-container);
    color: var(--color-information-text);
}

/* ============================================================
   Event number (bs-event-number)
   Hashtag icon + arrangement number, shared by every member-facing
   surface that shows an event's InternalId (MEM-9113).
   WCAG: --color-neutrals-hover text on the surrounding container background.
   ============================================================ */
.bs-event-number {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: var(--font-caption-md-size, 14px);
    line-height: var(--font-caption-md-line-height, 20px);
    font-weight: 400;
    color: var(--color-neutrals-hover);
}

.bs-event-number__icon {
    font-size: 12px;
    width: 14px;
    text-align: center;
    flex-shrink: 0;
    color: var(--color-primary-default);
}

/* ============================================================
   BsIcon (bs-icon)
   Renders a <use> reference into the BrandSync icon sprite
   (Membercare.SelfService.Web.Components/wwwroot/icons/brandsync.svg,
   fill="currentColor" on each symbol) sized by font-size, so it inherits
   color from its context like a font icon would. Size is null by default
   (no modifier class), so the icon matches the surrounding text size -
   only Small/Medium/Large opt into a fixed BrandSync tier (16/24/32).
   The element itself is the <svg> root (see BsIcon.razor), so
   vertical-align applies directly - no flex wrapper needed.
   ============================================================ */
.bs-icon {
    display: inline-block;
    width: 1em;
    height: 1em;
    vertical-align: -0.125em;
}

.bs-icon--small {
    font-size: 1rem; /* BrandSync 16 */
}

.bs-icon--medium {
    font-size: 1.5rem; /* BrandSync 24 */
}

.bs-icon--large {
    font-size: 2rem; /* BrandSync 32 */
}

/* ============================================================
   BsProfileAvatar (bs-profile-avatar)
   Shows a member's picture or initials. Fills its parent, which
   owns the diameter, background and border.
   ============================================================ */

.bs-profile-avatar__image {
    width: 100%;
    height: 100%;
    border-radius: 50%;
    object-fit: cover;
}

/* WCAG pair: --color-on-primary text on the frame's --color-primary-default background.
   Initials size scales with the frame via a token so a smaller avatar can shrink the text. */
.bs-profile-avatar__initials {
    font-size: var(--profile-avatar-initials-size, 34px);
    font-weight: 600;
    color: var(--color-on-primary);
    line-height: 1; /* single-token avatar text, centered by the flex frame */
    text-transform: uppercase;
}

/* The circular frame BsProfileAvatar expects its parent to provide (diameter, clipping, background
   for the initials case). Used by the community-group messageboard; --sm matches the reply-bubble size. */
.bs-profile-avatar-frame {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 50px;
    height: 50px;
    border-radius: 50%;
    overflow: hidden;
    background-color: var(--color-primary-default);
    --profile-avatar-initials-size: 20px;
}

.bs-profile-avatar-frame--sm {
    width: 45px;
    height: 45px;
    --profile-avatar-initials-size: 18px;
}

/* Inline avatar next to a name in a list cell (the file archive's Uploaded by column). */
.bs-profile-avatar-frame--xs {
    width: 28px;
    height: 28px;
    --profile-avatar-initials-size: 12px;
}

/* Opt-in (ColorKey supplied): the component paints its own circular background instead of relying on
   the parent frame's fixed colour, so it fills the same space regardless of the frame's own background. */
.bs-profile-avatar__initials--decorative {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    border-radius: 50%;
}

.bs-profile-avatar--decorative-1 { background-color: var(--color-decorative-1); }
.bs-profile-avatar--decorative-2 { background-color: var(--color-decorative-2); }
.bs-profile-avatar--decorative-3 { background-color: var(--color-decorative-3); }
.bs-profile-avatar--decorative-4 { background-color: var(--color-decorative-4); }
.bs-profile-avatar--decorative-5 { background-color: var(--color-decorative-5); }
.bs-profile-avatar--decorative-6 { background-color: var(--color-decorative-6); }

/* ============================================================
   Collapsible — shared across SelfService pages
   ============================================================ */

.collapsible-chevron {
    flex-shrink: 0;
    font-size: 16px;
    transition: transform 0.2s ease;
}

.collapsible-chevron--expanded {
    transform: rotate(180deg);
}

.collapsible-body {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows 0.3s ease;
}

.collapsible-body.expanded {
    grid-template-rows: 1fr;
    border-top: var(--bs-border-width) solid var(--color-border, var(--bs-border-color));
}

.collapsible-body__inner {
    overflow: hidden;
    min-height: 0;
}

@media (prefers-reduced-motion: reduce) {
    .collapsible-chevron,
    .collapsible-body {
        transition: none;
    }
}

/* ============================================================
   BrandSync card — opt-in shadow/radius
   Add `bs-card` alongside Bootstrap's `card` where the BrandSync look is
   wanted. Never restyle the bare `.card` selector: a global override silently
   restyles every card in the app.
   ============================================================ */
.bs-card {
    box-shadow: var(--shadow-card);
    border-radius: var(--brand-border-radius, 1rem);
    border: none;
}

/* Add `bs-card--interactive` alongside `bs-card` on cards that respond to the pointer by
   elevating. A consumer that also transitions other properties (e.g. background-color) overrides
   the transition wholesale — its own transition list must restate box-shadow. */
.bs-card--interactive {
    transition: box-shadow 0.15s ease;
}

.bs-card--interactive:hover {
    box-shadow: var(--shadow-card-hover);
}

/* ============================================================
   BrandSync outline card — bordered, rounded, clipped list item
   Add `bs-outline-card` for bordered card rows (e.g. list items) that
   need rounded corners with clipped content, without the bs-card shadow.
   ============================================================ */
.bs-outline-card {
    border: 1px solid var(--color-border, var(--bs-border-color));
    border-radius: 0.5rem;
    overflow: hidden;
}

/* ============================================================
   BrandSync card row header — clickable/expandable card header
   Add `bs-card-row-header` to the clickable header row of a bs-outline-card
   list item (e.g. an expandable row). Combine with `bs-card-row-header--active`
   when expanded.
   ============================================================ */
.bs-card-row-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 1rem;
    cursor: pointer;
    user-select: none;
}

.bs-card-row-header:hover {
    /* WCAG pair: --color-neutrals-default on --color-neutrals-container-hover */
    background-color: var(--color-neutrals-container-hover);
}

.bs-card-row-header:focus-visible {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: -2px;
}

.bs-card-row-header--active {
    /* WCAG pair: --color-primary-text on --color-primary-container */
    background-color: var(--color-primary-container);
    color: var(--color-primary-text);
}

/* ============================================================
   BrandSync card header — stacks on mobile, splits at tablet width
   Add `bs-card-header` to a card's top row (e.g. title/category on one
   side, a value like an amount on the other). Stacks in a single column
   below md; becomes a row with the two sides pushed to opposite ends at
   md and up.
   ============================================================ */
.bs-card-header {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

@media (min-width: 768px) {
    .bs-card-header {
        flex-direction: row;
        align-items: flex-start;
        justify-content: space-between;
    }
}

/* ============================================================
   BrandSync card detail row — icon + text list line
   Add `bs-card-detail-row` to a line inside a card body that pairs a
   Bootstrap icon with a text value (e.g. contact info, dates, address).
   Add `bs-card-detail-row--wrap` when several such items should sit on
   the same line when there's room, wrapping to their own lines
   otherwise — each direct child becomes its own icon+text pair.
   ============================================================ */
/* WCAG pair: --color-neutrals-default text on the card's light surface
   background (the shared .bs-card/.card box). */
.bs-card-detail-row {
    display: flex;
    align-items: center;
    gap: 8px;
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-caption-md-size, 14px);
    line-height: var(--font-caption-md-line-height, 20px);
    color: var(--color-neutrals-default);
}

.bs-card-detail-row--wrap {
    flex-wrap: wrap;
    row-gap: 8px;
    column-gap: 16px;
}

.bs-card-detail-row--wrap > * {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* ============================================================
   BrandSync card amount — emphasized numeric value, e.g. a balance
   Add `bs-card-amount` to a card's headline numeric value. Combine with
   `bs-card-amount--negative` when the value is negative (e.g. arrears).
   ============================================================ */
.bs-card-amount {
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-body-lg-size, 18px);
    line-height: var(--font-body-lg-line-height, 28px);
}

/* WCAG pair: --color-error-default text on the card's light surface
   background (the shared .bs-card/.card box). */
.bs-card-amount--negative {
    color: var(--color-error-default);
}

/* ============================================================
   BrandSync progress indicator, linear (bs-progress) — BsProgress
   8px track per the spec. A styled bar rather than <progress>: the
   fill eases between updates instead of jumping, and the
   indeterminate state pulses in place instead of the native sweep.
   WCAG pair: --color-primary-default fill on --color-neutrals-container.
   ============================================================ */
.bs-progress {
    height: 8px;
    border-radius: 4px;
    background-color: var(--color-neutrals-container);
    overflow: hidden;
}

.bs-progress__fill {
    height: 100%;
    border-radius: 4px;
    background-color: var(--color-primary-default);
    transition: width 0.25s linear;
}

.bs-progress--indeterminate .bs-progress__fill {
    animation: bs-progress-pulse 1.6s ease-in-out infinite;
}

@keyframes bs-progress-pulse {
    50% { opacity: 0.45; }
}

@media (prefers-reduced-motion: reduce) {
    .bs-progress__fill {
        transition: none;
    }

    .bs-progress--indeterminate .bs-progress__fill {
        animation: none;
        opacity: 0.6;
    }
}

/* ============================================================
   BrandSync menu (bs-menu)
   Add to a Bootstrap dropdown-menu for the Menu spec's 48px items,
   24px icon slot and 12px horizontal padding. Global because the
   file archive's row menus and its breadcrumb overflow both use it.
   ============================================================ */
.bs-menu .dropdown-item {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 48px;
    padding: 8px 12px;
}

.bs-menu .dropdown-item i {
    width: 24px;
    text-align: center;
}

.bs-menu .dropdown-item .bs-icon {
    flex-shrink: 0;
    font-size: 1.5rem;
}

/* WCAG pair: --color-error-default text on the menu surface */
.bs-menu .bs-menu__item--danger {
    color: var(--color-error-default);
}

/* ################################################################
   3. DASHBOARD FRAMEWORK
   ################################################################ */

/* ============================================================
   Dashboard grid (dashboard-grid*)
   Two-column layout; height equalization comes from the fixed
   height on .dashboard-module, not from grid-auto-rows.
   ============================================================ */

/* Extend the dashboard background through the layout shell's padding */
#page-content-wrapper:has(.dashboard-page) {
    background-color: var(--color-page-background);
    padding-left: 0 !important;
    padding-right: 0 !important;
}

#page-body-container:has(.dashboard-page) {
    padding-top: 0 !important;
}

/* The BrandSync group page sits on the same background as the dashboard, so its cards stand out
   the same way. WCAG pair: page text uses --color-neutrals-default on --color-page-background. */
#page-content-wrapper:has(.community-group-page--brandsync) {
    background-color: var(--color-page-background);
    min-height: 100dvh;
}

.dashboard-page {
    background-color: var(--color-page-background);
    min-height: 100%;
    padding: 0 24px;
}

.dashboard-grid {
    display: flex;
    flex-direction: column;
}

/* WCAG pair: row text sits on --color-neutrals-container-focused mixed into --color-page-background */
.dashboard-row {
    padding: 0;
    margin-left: -24px;
    margin-right: -24px;
    background-color: color-mix(in srgb, var(--color-neutrals-container-focused) 5%, var(--color-page-background));
}

/* Modules that render nothing when they have no data still get a row wrapper from the page,
   which would otherwise show as a bare shaded stripe. */
.dashboard-row:not(:has(.dashboard-row__full-content > *, .dashboard-grid__item > *)) {
    display: none;
}

/* Shades alternate over the rows that render, so a collapsed row does not consume a step.
   The :has() test must stay the inverse of the collapse rule above. */
.dashboard-row:nth-child(odd of :has(.dashboard-row__full-content > *, .dashboard-grid__item > *)) {
    background-color: color-mix(in srgb, var(--color-neutrals-container-focused) 20%, var(--color-page-background));
}

.dashboard-row__inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 24px;
    padding-block: 24px;
    padding-inline: 24px;
    max-width: var(--bs-content-max-width);
    margin-inline: auto;
}

.dashboard-row__full-content {
    max-width: var(--bs-content-max-width);
    margin-inline: auto;
    padding-block: 24px;
    padding-inline: 24px;
}

@media (min-width: 1024px) {
    .dashboard-row__inner {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* ============================================================
   Dashboard list card (dashboard-list-card*)
   Framework-locked shell for list-style dashboard modules.
   Header (icon + title + optional action) is always rendered.
   Body shows skeleton, empty state, or <ul> with items.
   ============================================================ */

.dashboard-list-card {
    background-color: var(--color-neutrals-container);
    border-radius: var(--brand-border-radius, 16px);
    box-shadow: var(--shadow-card);
    display: flex;
    flex-direction: column;
    height: var(--dashboard-module-height, 264px);
    overflow: hidden;
}

.dashboard-list-card__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 24px;
    gap: 8px;
    background-color: var(--color-primary-container);
    flex-shrink: 0;
}

.dashboard-list-card__heading {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
}

/* WCAG pair: --color-primary-default text on --color-neutrals-container background */
.dashboard-list-card__action {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    margin-top: auto;
    padding-top: 8px;
    padding-bottom: 16px;
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-body-md-size, 16px);
    line-height: var(--font-body-md-line-height, 24px);
    font-weight: 500;
}

.dashboard-list-card__action-link {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--color-primary-default);
    text-decoration: none;
}

.dashboard-list-card__action-link:hover {
    color: var(--color-primary-hover);
    text-decoration: underline;
}

.dashboard-list-card__action-link:active {
    color: var(--color-primary-pressed);
}

.dashboard-list-card__action-link:focus-visible {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
    border-radius: 2px;
}

.dashboard-list-card__action-icon {
    font-size: var(--font-caption-md-size, 14px);
}

/* WCAG pair: --color-primary-hover text on --color-neutrals-container background */
.dashboard-list-card__action-count {
    color: var(--color-primary-hover);
    font-weight: 400;
}

.dashboard-list-card__action-separator {
    color: var(--color-neutrals-hover);
}

.dashboard-list-card__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    flex-shrink: 0;
    color: var(--color-primary-default);
    font-size: 20px;
}

/* WCAG pair: --color-neutrals-default text on --color-primary-container background */
.dashboard-list-card__title {
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-h6-size, 20px);
    line-height: var(--font-h6-line-height, 32px);
    font-weight: 600;
    color: var(--color-neutrals-default);
    margin: 0;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dashboard-list-card__body {
    padding: 16px 24px 0;
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* WCAG pair: --color-neutrals-hover text on --color-neutrals-container background */
.dashboard-list-card__empty {
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-body-md-size, 16px);
    line-height: var(--font-body-md-line-height, 24px);
    color: var(--color-neutrals-hover);
    margin: auto;
    text-align: center;
    align-self: center;
}

/* WCAG pair: --color-primary-default on --color-neutrals-container background */
.dashboard-list-card__empty a {
    color: var(--color-primary-default);
    text-decoration: underline;
}

.dashboard-list-card__empty a:hover {
    color: var(--color-primary-hover);
}

.dashboard-list-card__skeleton {
    flex: 1;
    border-radius: 8px;
    background: linear-gradient(
        90deg,
        var(--color-neutrals-container-hover) 25%,
        var(--color-neutrals-container-focused) 50%,
        var(--color-neutrals-container-hover) 75%
    );
    background-size: 200% 100%;
    animation: dashboard-shimmer 1.5s infinite;
}

/* ============================================================
   Dashboard module header (dashboard-module-header*)
   Header for detail modules that render their own chrome instead of a
   DashboardListCard. Icon box, gap and title ramp match the list card's
   header so the two module styles differ only by the card around them.
   ============================================================ */

.dashboard-module-header {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
    flex-shrink: 0;
}

.dashboard-module-header__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    flex-shrink: 0;
    color: var(--color-primary-default);
    font-size: 20px;
}

/* WCAG pair: --color-primary-default text on the row's shaded background */
.dashboard-module-header__title {
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-h6-size, 20px);
    line-height: var(--font-h6-line-height, 32px);
    font-weight: 600;
    color: var(--color-primary-default);
    margin: 0;
    min-width: 0;
    flex-grow: 1;
}

@media (min-width: 768px) {
    .dashboard-module-header__title {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

.dashboard-module-header__action {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
}

/* The caller's Action fragment may render nothing; without this the empty wrapper
   still contributes the header's gap. */
.dashboard-module-header__action:not(:has(*)) {
    display: none;
}

/* Custom subscription/listing glyph - not in the Font Awesome set, so it's masked from an inline
   SVG instead so it can still be passed as a DashboardModuleHeader IconCssClass and inherit
   currentColor like the fa-* icons do. */
.icon-subscriptions {
    display: inline-block;
    width: 20px;
    height: 20px;
    background-color: currentColor;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M21,3H7C6.4,3,6,3.4,6,4v3H3C2.4,7,2,7.4,2,8v10c0,1.7,1.3,3,3,3h13c2.2,0,4-1.8,4-4V4C22,3.4,21.6,3,21,3z M6,18c0,0.6-0.4,1-1,1s-1-0.4-1-1V9h2V18z M12,17h-1c-0.6,0-1-0.4-1-1c0-0.6,0.4-1,1-1h1c0.6,0,1,0.4,1,1C13,16.6,12.6,17,12,17z M12,13h-1c-0.6,0-1-0.4-1-1c0-0.6,0.4-1,1-1h1c0.6,0,1,0.4,1,1C13,12.6,12.6,13,12,13z M17,17h-1c-0.6,0-1-0.4-1-1c0-0.6,0.4-1,1-1h1c0.6,0,1,0.4,1,1C18,16.6,17.6,17,17,17z M17,13h-1c-0.6,0-1-0.4-1-1c0-0.6,0.4-1,1-1h1c0.6,0,1,0.4,1,1C18,12.6,17.6,13,17,13z M17,9h-6c-0.6,0-1-0.4-1-1c0-0.6,0.4-1,1-1h6c0.6,0,1,0.4,1,1C18,8.6,17.6,9,17,9z'/%3E%3C/svg%3E");
    mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M21,3H7C6.4,3,6,3.4,6,4v3H3C2.4,7,2,7.4,2,8v10c0,1.7,1.3,3,3,3h13c2.2,0,4-1.8,4-4V4C22,3.4,21.6,3,21,3z M6,18c0,0.6-0.4,1-1,1s-1-0.4-1-1V9h2V18z M12,17h-1c-0.6,0-1-0.4-1-1c0-0.6,0.4-1,1-1h1c0.6,0,1,0.4,1,1C13,16.6,12.6,17,12,17z M12,13h-1c-0.6,0-1-0.4-1-1c0-0.6,0.4-1,1-1h1c0.6,0,1,0.4,1,1C13,12.6,12.6,13,12,13z M17,17h-1c-0.6,0-1-0.4-1-1c0-0.6,0.4-1,1-1h1c0.6,0,1,0.4,1,1C18,16.6,17.6,17,17,17z M17,13h-1c-0.6,0-1-0.4-1-1c0-0.6,0.4-1,1-1h1c0.6,0,1,0.4,1,1C18,12.6,17.6,13,17,13z M17,9h-6c-0.6,0-1-0.4-1-1c0-0.6,0.4-1,1-1h6c0.6,0,1,0.4,1,1C18,8.6,17.6,9,17,9z'/%3E%3C/svg%3E");
}

/* ============================================================
   Dashboard detail card (dashboard-detail-card*)
   Flexible shell for detail-style dashboard modules.
   No enforced header — module controls internal layout.
   CSS token contract only.
   ============================================================ */

/* Shared primary item name style for detail modules (group name, board name, event name, etc.).
   Simple/list modules are excluded — they use a colored header band with a different text treatment. */
.dashboard-detail-module__item-name {
    color: var(--color-primary-default);
}

/* Shared subscription card shell — rendered both by SubscriptionCard.razor and, for the loading
   skeleton, directly by SubscriptionDetailModule.razor, so it can't live in a single component's
   scoped CSS (the module's own <li> never gets SubscriptionCard's scope attribute). */
.subscription-card {
    min-width: 0;
    /* Every card now always shows a date line and a payer line, so the only remaining height
       variance is the optional description — size for one line of it, not two, so cards without
       one don't end up mostly empty. */
    min-height: 160px;
    padding: 16px;
    background-color: var(--color-neutrals-container);
    border-radius: var(--brand-border-radius, 16px);
    box-shadow: var(--shadow-card);
}

.subscription-card__divider {
    border: none;
    border-top: 1px solid color-mix(in srgb, var(--color-neutrals-hover) 40%, transparent);
    margin: 0;
}

.dashboard-detail-card {
    background-color: var(--color-neutrals-container);
    border-radius: var(--brand-border-radius, 16px);
    box-shadow: var(--shadow-card);
    display: flex;
    flex-direction: column;
    height: var(--dashboard-module-details-height, 288px);
    overflow: hidden;
    padding: 24px;
}

/* Narrower side padding than the default card: leaves more row width for
   the field list. The banner bleed margins in the module's scoped CSS mirror this. */
.dashboard-detail-card.personal-info-module {
    padding-left: 16px;
    padding-right: 16px;
}

.dashboard-detail-card__skeleton {
    flex: 1;
    border-radius: 8px;
    background: linear-gradient(
        90deg,
        var(--color-neutrals-container-hover) 25%,
        var(--color-neutrals-container-focused) 50%,
        var(--color-neutrals-container-hover) 75%
    );
    background-size: 200% 100%;
    animation: dashboard-shimmer 1.5s infinite;
}

@keyframes dashboard-shimmer {
    0%   { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}

@media (prefers-reduced-motion: reduce) {
    .dashboard-list-card__skeleton,
    .dashboard-detail-card__skeleton,
    .skeleton-block,
    .skeleton-line,
    .dashboard-list__skeleton-block,
    .dashboard-list__skeleton-line {
        animation: none;
    }
}

/* ============================================================
   Dashboard layout (dashboard-layout*)
   Sidebar + main content layout for pages with a Sidebar module.
   ============================================================ */

.dashboard-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 24px;
    /* Sidebar + gap + a content column of exactly --bs-content-max-width, so the modules
       line up with the content edges every other page uses. */
    max-width: calc(var(--bs-content-max-width) + var(--dashboard-sidebar-width) + 24px);
    margin-inline: auto;
}

.dashboard-layout__sidebar {
    min-width: 0;
    padding-top: 56px;
    padding-bottom: 24px;
}

/* Content column; the rows inside carry their own spacing and shading. */
.dashboard-layout__content {
    min-width: 0;
}

/* The two-column grid only starts at 1024px; until then the sidebar sits at the top of the
   page and takes the same 24px the content rows carry. */
@media (min-width: 768px) {
    .dashboard-layout__sidebar {
        padding-top: 24px;
        padding-bottom: 0;
    }
}

@media (min-width: 1024px) {
    .dashboard-layout {
        grid-template-columns: var(--dashboard-sidebar-width) minmax(0, 1fr);
        align-items: start;
        gap: 0 24px;
    }

    /* sticky makes this a stacking context, trapping a modal declared inside a sidebar module
       (PersonalInfo's picture modal) behind the custom-fields legend, which is positioned at
       z-index 1. Sits above that but below the 1050+ modal layer, so a modal opened from the
       content column still covers the sidebar. */
    .dashboard-layout__sidebar {
        position: sticky;
        top: 56px;
        z-index: 2;
    }

    /* Rows inside the sidebar layout must not bleed into the sidebar column */
    .dashboard-layout__content .dashboard-row {
        margin-left: 0;
    }

    .dashboard-layout__content .dashboard-row__inner,
    .dashboard-layout__content .dashboard-row__full-content {
        margin-inline: 0;
        max-width: none;
    }
}

/* ============================================================
   Dashboard summary (dashboard-details*)
   Responsive grid container for detail-module field layouts.
   Consumers define their own field/label/value markup.
   ============================================================ */

.dashboard-details {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 16px;
    width: 100%;
    align-content: start;
}

/* ============================================================
   Dashboard list (dashboard-list*)
   Item list for Employment, Education, and similar modules.
   ============================================================ */

.dashboard-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    width: 100%;
    flex: 1;
    min-height: 0;
    overflow: hidden;
}

.dashboard-list__item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 0;
}

.dashboard-list__item:first-child {
    padding-top: 0;
}

.dashboard-list__item:last-child {
    padding-bottom: 0;
}

.dashboard-list__item-icon,
.dashboard-list__skeleton-icon {
    width: 32px;
    height: 32px;
    flex-shrink: 0;
}

.dashboard-list__item-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--color-primary-default);
    font-size: 14px;
}

.dashboard-list__item-content {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1;
}

/* WCAG pair: --color-neutrals-pressed text on --color-neutrals-container background */
.dashboard-list__item-name {
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-body-md-size, 16px);
    line-height: var(--font-body-md-line-height, 24px);
    font-weight: 500;
    color: var(--color-neutrals-pressed);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dashboard-list__item-subname {
    font-weight: normal;
    font-size: var(--font-caption-md-size, 0.875rem);
    margin-left: 0.5rem;
}

/* WCAG pair: --color-neutrals-hover text on --color-neutrals-container background */
.dashboard-list__item-description {
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-caption-md-size, 14px);
    line-height: var(--font-caption-md-line-height, 20px);
    font-weight: 400;
    color: var(--color-neutrals-hover);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dashboard-list__item-description::before {
    content: '\2013\00A0';
    content: '\2013\00A0' / '';
}

/* Split description: context and date range on their own lines, so neither truncates the other.
   The base rule's en-dash prefix is dropped here — the caption styling already marks both lines
   as secondary, and a prefix on only the first line would break the block's left alignment. */
.dashboard-list__item-description--split {
    display: flex;
    flex-direction: column;
}

.dashboard-list__item-description--split::before {
    content: none;
}

.dashboard-list__item-description-context,
.dashboard-list__item-description-dates {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ============================================================
   Event detail module (event-detail-module__)
   Detail-style dashboard module for upcoming event enrollments.
   ============================================================ */

.event-detail-module {
    font-family: var(--font-family, Roboto, sans-serif);
}

.event-detail-module__participation {
    background-color: var(--color-neutrals-container);
    border-radius: var(--brand-border-radius, 16px);
    padding: 16px;
    box-shadow: var(--shadow-card);
}

.event-detail-module__participation--featured {
    padding-bottom: 0;
}

.event-detail-module__actions {
    margin-top: auto;
    padding-top: 16px;
    padding-bottom: 16px;
}

.event-detail-module__nav-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    height: 40px;
    padding: 0 16px;
    border: 1.5px solid var(--color-primary-default);
    border-radius: 8px;
    background: transparent;
    color: var(--color-primary-default);
    font-size: var(--font-caption-md-size, 14px);
    font-weight: 500;
    cursor: pointer;
    white-space: nowrap;
    transition: background-color 0.15s, color 0.15s;
}

.event-detail-module__nav-button:hover {
    background-color: var(--color-primary-container);
}

.event-detail-module__nav-button:active {
    color: var(--color-primary-pressed);
}

.event-detail-module__nav-button:focus-visible {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

.event-detail-module__icon-button {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    min-width: 56px;
    min-height: 56px;
    padding: 8px;
    border: none;
    border-radius: 8px;
    background: transparent;
    color: var(--color-primary-default);
    cursor: pointer;
    transition: background-color 0.15s, color 0.15s;
}

.event-detail-module__icon-button:hover {
    background-color: var(--color-primary-container);
}

.event-detail-module__icon-button:focus-visible {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

.event-detail-module__icon-button-glyph {
    font-size: 18px;
    line-height: 1;
}

.event-detail-module__icon-button-label {
    font-size: var(--font-body-md-size, 16px);
    font-weight: 600;
    line-height: 1.5;
    white-space: nowrap;
}

.event-detail-module__icon-button--danger {
    color: var(--color-error-default);
}

.event-detail-module__icon-button--danger:hover {
    background-color: var(--color-error-container);
    color: var(--color-error-default);
}

.event-detail-module__action-divider {
    align-self: stretch;
    width: 1px;
    margin: 8px 4px;
    background-color: var(--color-border, #dde3e9);
}

.event-detail-module__date {
    width: 48px;
    min-width: 48px;
    height: 48px;
    background-color: var(--color-primary-container);
    border-radius: 8px;
}

.event-detail-module__date-day {
    font-size: 18px;
    line-height: 1.2;
    font-weight: 700;
    color: var(--color-primary-default);
}

.event-detail-module__date-month {
    font-size: 12px;
    line-height: var(--font-caption-md-line-height, 20px);
    font-weight: 500;
    color: var(--color-primary-default);
}

.event-detail-module__name {
    font-size: var(--font-body-md-size, 16px);
    line-height: var(--font-body-md-line-height, 24px);
    font-weight: 500;
}

.event-detail-module__badges {
    /* Only the loading skeleton uses this now — the real card renders badges in the title row.
       Reserves one badge-row of height so the skeleton matches the loaded card's height.
       28px = 20px line-height + 2×4px vertical padding. */
    min-height: 28px;
}

.event-detail-module__recurring-icon {
    margin-left: 8px;
    font-size: 14px;
    color: var(--color-primary-default);
}

/* Event id sits on the title row; muted like the meta rows.
   WCAG: --color-neutrals-hover text on the card's --color-neutrals-container background. */
.event-detail-module__event-id {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: var(--font-caption-md-size, 14px);
    line-height: var(--font-caption-md-line-height, 20px);
    font-weight: 400;
    color: var(--color-neutrals-hover);
}

.event-detail-module__skeleton-date {
    width: 48px;
    min-width: 48px;
    height: 48px;
    border-radius: 8px;
}

.event-detail-module__row-action {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: flex-end;
    width: 24px;
    height: 24px;
    font-size: 16px;
    color: var(--color-primary-default);
    background: transparent;
    border: none;
    border-radius: 8px;
    padding: 0;
    cursor: pointer;
}

.event-detail-module__row-action:hover {
    color: var(--color-primary-hover);
    background-color: var(--color-primary-container);
}

.event-detail-module__row-action:active {
    color: var(--color-primary-pressed);
}

.event-detail-module__row-action:focus-visible {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

.event-detail-module__qr-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border: none;
    border-radius: 8px;
    background-color: var(--color-primary-container);
    color: var(--color-primary-text);
    font-size: 18px;
    cursor: pointer;
    transition: background-color 0.15s, color 0.15s;
}

.event-detail-module__qr-button:hover {
    background-color: var(--color-primary-container-hover);
    color: var(--color-primary-text);
}

.event-detail-module__qr-button:active {
    color: var(--color-primary-text);
}

.event-detail-module__qr-button:focus-visible {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

.event-detail-module__empty {
    min-height: 160px;
    text-align: center;
    font-size: var(--font-body-md-size, 16px);
    line-height: var(--font-body-md-line-height, 24px);
    color: var(--color-neutrals-hover);
}

.event-detail-module__date--featured {
    width: 56px;
    min-width: 56px;
    height: 56px;
    background-color: var(--color-primary-default);
}

.event-detail-module__date--featured .event-detail-module__date-day {
    font-size: 24px;
    color: var(--color-on-primary);
}

.event-detail-module__date--featured .event-detail-module__date-month {
    font-size: 12px;
    color: var(--color-on-primary);
}

.event-detail-module__name--featured {
    font-size: var(--font-body-lg-size, 18px);
    font-weight: 600;
}

.event-detail-module__skeleton-date--featured {
    width: 56px;
    min-width: 56px;
    height: 56px;
}

.event-detail-module__meta-row {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: var(--font-caption-md-size, 14px);
    line-height: var(--font-caption-md-line-height, 20px);
    color: var(--color-neutrals-hover);
}

.event-detail-module__meta-icon {
    font-size: 12px;
    width: 14px;
    text-align: center;
    flex-shrink: 0;
    color: var(--color-primary-default);
}

.event-detail-module__split {
    display: grid;
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
    gap: 16px;
    align-items: stretch;
    min-width: 0;
}

.event-detail-module__split .event-detail-module__participation--featured {
    grid-row: span 2;
}

.event-detail-module__split .event-detail-module__participation--compact {
    align-self: stretch;
}

@media (max-width: 767px) {
    .event-detail-module__split {
        grid-template-columns: minmax(0, 1fr);
        align-items: start;
    }

    .event-detail-module__split .event-detail-module__participation--featured {
        grid-row: span 1;
    }

    .event-detail-module__split .event-detail-module__featured-body {
        height: auto;
    }

    .event-detail-module__split .event-detail-module__participation--compact {
        flex: unset;
    }
}

/* ============================================================
   Simple module shared — date stamp and meta row
   Reusable across EventParticipationsSimpleModule and future list modules.
   ============================================================ */

/* WCAG pair: --color-primary-text on --color-primary-container background */
.simple-module__date {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 40px;
    min-width: 40px;
    height: 40px;
    background-color: var(--color-primary-container);
    border-radius: 8px;
    flex-shrink: 0;
    overflow: hidden;
}

.simple-module__date-day {
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-body-md-size, 16px);
    line-height: 1.2; /* layout exception: decorative date digit constrained to 40px box */
    font-weight: 700;
    color: var(--color-primary-text);
}

/* WCAG pair: --color-primary-text on --color-primary-container background */
.simple-module__date-month {
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-caption-sm-size, 11px);
    line-height: var(--font-caption-sm-line-height, 16px);
    font-weight: 500;
    color: var(--color-primary-text);
    text-transform: uppercase;
}

.simple-module__meta {
    display: flex;
    align-items: center;
    gap: 4px;
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-caption-md-size, 14px);
    line-height: var(--font-caption-md-line-height, 20px);
    font-weight: 400;
    color: var(--color-neutrals-hover);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* WCAG pair: --color-primary-default icon on --color-neutrals-container background */
.simple-module__meta-icon {
    font-size: 11px;
    color: var(--color-primary-default);
    flex-shrink: 0;
}


/* ################################################################
   4. CROSS-COMPONENT OVERRIDES
   ################################################################ */

/* ============================================================
   Custom fields detail module
   Hosts the shared BsSelectCustomFields inside a DashboardListCard so it shares the dashboard's
   white-card chrome (primary-tinted header bar, shadow, radius). Two adjustments are scoped here:
   1) the list card is fixed-height/clipping by design — let this full-width form grow instead;
   2) flatten BsSelectCustomFields from its page grid of shadowed cards into a single-column,
      transparent form so it reads as one card body, not nested cards.
   Stays global: these selectors target DashboardListCard/BsSelectCustomFields internals, so under
   CSS isolation they cannot live in a scoped file without a ::deep redesign (planned follow-up).
   ============================================================ */

/* Let the form grow with its content instead of clipping to the fixed module height. */
.custom-fields-detail-module .dashboard-list-card {
    height: auto;
}

.custom-fields-detail-module .dashboard-list-card__body {
    padding-bottom: 24px;
}

.custom-fields-detail-module .dashboard-list {
    overflow: visible;
}

/* Element tags outweigh BsSelectCustomFields' scoped (.x[b-hash]) rules, which otherwise win the
   cascade at equal specificity. */
.custom-fields-detail-module div.bs-select-custom-fields {
    display: flex;
    flex-direction: column;
    gap: 24px;
    /* Room for the first group's legend, which overhangs the top border. */
    padding-top: 8px;
}

/* Box + legend mechanic come from the shared .bs-fieldset utility above; these override only the
   BsSelectCustomFields page defaults (filled background, shadow, clipping, wide padding). */
.custom-fields-detail-module div.bs-select-custom-fields__group {
    background: transparent;
    box-shadow: none;
    overflow: visible;
    padding: 16px;
}

/* WCAG pair: --color-primary-default text on --color-neutrals-container (card) background.
   Truncation lives on the shared .bs-fieldset__legend; the ellipsis is repeated here because this
   variant's text is the inner <h3>, and text-overflow only truncates the element directly holding
   the text (the header's nowrap is inherited; the ellipsis must be on the title). */
.custom-fields-detail-module h3.bs-select-custom-fields__group-title {
    font-size: var(--font-body-md-size, 16px);
    line-height: var(--font-body-md-line-height, 24px);
    font-weight: 700;
    color: var(--color-primary-default);
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
}

.custom-fields-detail-module div.bs-select-custom-fields__group-body {
    padding: 0;
}

/* Use the full-width module's horizontal space: lay each group's fields out in two columns on
   wider viewports. Mobile-first — single column at the base, two columns from 768px up. */
.custom-fields-detail-module div.bs-select-custom-fields__fields {
    display: grid;
    grid-template-columns: 1fr;
    gap: 16px 24px;
}

@media (min-width: 768px) {
    .custom-fields-detail-module div.bs-select-custom-fields__fields {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* Interest module
   The wrapper class is scoped on a div that wraps the whole card (present in every state,
   including loading), so the auto-height + bottom spacing apply to the skeleton too — otherwise
   the loading skeleton sat flush against the card bottom. Mirrors .custom-fields-detail-module. */
.interest-detail-module .dashboard-list-card {
    height: auto;
}

/* Bottom breathing room below the last group / footer (and below the skeleton while loading),
   matching the custom-fields module (the framework body has 0 bottom padding for fixed-height
   list cards). */
.interest-detail-module .dashboard-list-card__body {
    padding-bottom: 24px;
}

/* Groups stack in a single full-width column; padding-top gives the group labels room. The
   interests inside each group flow onto shared rows on wider screens — see .dashboard-list__interests
   below. */
.dashboard-list:has(.dashboard-list__group) {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    padding-top: 1rem;
    overflow: visible;
    min-height: auto;
}

/* Box + legend mechanic come from .bs-fieldset / .bs-fieldset__legend (in the markup); these add
   only the Interests-specific layout and label typography. */
.dashboard-list__group {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    list-style: none;
    padding: 1rem 0.75rem 0.75rem;
    min-width: 0;
}

.dashboard-list__group-label {
    font-size: var(--font-body-sm-size, 0.875rem);
    line-height: var(--font-body-sm-line-height, 1.25rem);
    font-weight: 600;
}

/* Interests within a group. Mobile-first: stacked one per row at the base so long names get the
   full width. From 768px up they flow onto shared rows wherever there's room, putting this
   full-width module's horizontal space to use instead of leaving it empty. */
/* Collapsed interest lists clamp by ROWS (max-height + overflow), not by item count, so a wide screen
   — where interests wrap several per row — shows more interests than a narrow one for the same height.
   "Vis mere" lifts the clamp; InterestModule.razor.js reports how many items each clamp hides so the
   expander only appears when something is actually clipped. Base = mobile (column) ≈ 3 rows; the 768px
   rule below switches to wrap and ≈ 2 rows. The Bootstrap info tooltip is portaled to <body>, so this
   overflow clip does not affect it. */
.dashboard-list__interests {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    overflow: hidden;
    /* 3 rows: 3 × 2.75rem interest height + 2 × 0.5rem row gap */
    max-height: calc(3 * 2.75rem + 2 * 0.5rem);
}

/* One interest = the star/name toggle plus its optional info trigger, presented as a single
   hoverable unit. The padding, rounding and hover background live here (not on the toggle) so the
   highlight envelops the info icon too. The 0.5rem gap matches the star-to-name gap inside the
   toggle, so the info icon sits the same distance from the name as the star does. */
.dashboard-list__interest {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    min-width: 0;
    padding-inline: 0.5rem;
    border-radius: 0.5rem;
    transition: background-color 0.15s;
}

@media (min-width: 768px) {
    .dashboard-list__interests {
        flex-direction: row;
        flex-wrap: wrap;
        /* row 8px, column 16px */
        gap: 0.5rem 1rem;
        /* 2 rows where interests pack several per row: 2 × 2.75rem + 1 × 0.5rem row gap */
        max-height: calc(2 * 2.75rem + 0.5rem);
    }

    /* Size to content with no grow, so short interests pack several per row and the leftover space
       stays empty; a long name wraps inside the toggle rather than claiming the whole row. */
    .dashboard-list__interest {
        flex: 0 1 auto;
        max-width: 100%;
    }
}

/* Expander lifts the row clamp to reveal the rest. Placed after the 768px rule so it overrides the
   desktop max-height too (equal specificity → later in source wins). */
.dashboard-list__interests--expanded {
    max-height: none;
    overflow: visible;
}

/* Star + name are the toggle target (min 2.75rem/44px tall for touch). It sizes to its content
   (flex-grow 0) so the info icon sits right after the name rather than being pushed to the far
   right; it can still shrink (min-width 0) so long names wrap instead of overflowing. No border and
   no background — the inline padding and hover highlight live on .dashboard-list__interest so they
   wrap the info icon too. The block padding stays here: it gives wrapped multi-line names breathing
   room from the top/bottom of the hover pill (a single line is unaffected — min-height sets its
   height). align-items:flex-start keeps the star beside the first line of a wrapped name. */
.dashboard-list__interest-toggle {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    flex: 0 1 auto;
    min-width: 0;
    min-height: 2.75rem;
    padding-block: 0.5rem;
    border: none;
    background-color: transparent;
    cursor: pointer;
    text-align: left;
    color: var(--color-neutrals-default);
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-body-md-size, 1rem);
    line-height: var(--font-body-md-line-height, 1.5rem);
}

.dashboard-list__interest-toggle:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Unselected interests show an outlined star in a muted tone; selected ones a filled brand star.
   The outline/fill swap is the far/fas icon in the markup; this sets the matching colour. */
.dashboard-list__interest-star {
    flex-shrink: 0;
    color: var(--color-neutrals-hover);
    line-height: var(--font-body-md-line-height, 1.5rem);
}

.dashboard-list__interest-toggle[aria-pressed="true"] .dashboard-list__interest-star {
    color: var(--color-primary-default);
}

.dashboard-list__interest-name {
    min-width: 0;
    overflow-wrap: break-word;
    hyphens: none;
}

/* The info tooltip trigger (the focusable wrapper from MembersiteTooltip): keeps the 44px-tall tap
   target but hugs the name (no wide centred box) so it sits one 0.5rem gap from the name — the same
   distance the star sits from the name. The inline padding widens the hit area toward the WCAG AAA
   44px target without reintroducing the wide centred box. */
.dashboard-list__interest-info {
    display: inline-flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    min-height: 2.75rem;
    padding-inline: 0.5rem;
    color: var(--color-primary-default);
    cursor: pointer;
}

/* Both expanders are borderless, brand-coloured text buttons with a 44px tap height. */
.dashboard-list__group-show-more,
.dashboard-list__show-more {
    align-items: center;
    gap: 0.5rem;
    min-height: 2.75rem;
    border: none;
    background-color: transparent;
    cursor: pointer;
    color: var(--color-primary-default);
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-body-sm-size, 0.875rem);
    line-height: var(--font-body-sm-line-height, 1.25rem);
    font-weight: 600;
}

/* Per-group expander: left-aligned inside the box. */
.dashboard-list__group-show-more {
    display: inline-flex;
    align-self: flex-start;
    padding: 0.25rem 0.5rem;
}

/* Module-level expander: centred under the groups. */
.dashboard-list__show-more {
    display: flex;
    width: fit-content;
    margin: 0.75rem auto 0;
    padding: 0.5rem 0.75rem;
}

/* Shared focus ring for every interactive element in the module. */
.dashboard-list__interest-toggle:focus-visible,
.dashboard-list__interest-info:focus-visible,
.dashboard-list__group-show-more:focus-visible,
.dashboard-list__show-more:focus-visible {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

/* Rotating chevron shared by both expanders. */
.dashboard-list__show-more-icon {
    transition: transform 0.15s;
}

.dashboard-list__show-more-icon--expanded {
    transform: rotate(180deg);
}

@media (hover: hover) {
    /* Highlight the whole unit (star + name + info icon), but not while it is saving (toggle disabled). */
    .dashboard-list__interest:has(.dashboard-list__interest-toggle:not(:disabled)):hover {
        background-color: var(--color-neutrals-container-hover);
    }

    /* Underline only the label, not the chevron icon. */
    .dashboard-list__group-show-more:hover span,
    .dashboard-list__show-more:hover span {
        text-decoration: underline;
    }
}

/* WCAG pair: --color-primary-text on --color-primary-container background */
.dashboard-list__badge {
    flex-shrink: 0;
    background-color: var(--color-primary-container);
    color: var(--color-primary-text);
    font-family: var(--font-family, Roboto, sans-serif);
    font-size: var(--font-caption-md-size, 14px);
    line-height: var(--font-caption-md-line-height, 20px);
    font-weight: 500;
    padding: 4px 8px;
    border-radius: 100px;
    white-space: nowrap;
}

.skeleton-block,
.dashboard-list__skeleton-block {
    border-radius: 8px;
    background: linear-gradient(
        90deg,
        var(--color-neutrals-container-hover) 25%,
        var(--color-neutrals-container-focused) 50%,
        var(--color-neutrals-container-hover) 75%
    );
    background-size: 200% 100%;
    animation: dashboard-shimmer 1.5s infinite;
}

.skeleton-line,
.dashboard-list__skeleton-line {
    height: 16px;
    border-radius: 4px;
    background: linear-gradient(
        90deg,
        var(--color-neutrals-container-hover) 25%,
        var(--color-neutrals-container-focused) 50%,
        var(--color-neutrals-container-hover) 75%
    );
    background-size: 200% 100%;
    animation: dashboard-shimmer 1.5s infinite;
}

.skeleton-line--short,
.dashboard-list__skeleton-line--short {
    width: 55%;
}

/* Announcements tab on the BrandSync group page (BsCommunityGroupAnnouncementList and the
   UseBrandSync layout of CommunityGroupAnnouncementView). Global rather than scoped, so the shared
   view renders unchanged with the flag off. */
.bs-announcement-list {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}

.bs-announcement-list__section {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.bs-announcement-list__heading {
    margin: 0;
}

/* WCAG pair: --color-neutrals-pressed heading text on --color-page-background. */
.bs-announcement-list__toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 2.75rem;
    padding: 0 0.5rem;
    margin-left: -0.5rem;
    border: 0;
    border-radius: 0.5rem;
    background: none;
    color: var(--color-neutrals-pressed);
    font: inherit;
    cursor: pointer;
}

.bs-announcement-list__toggle:hover {
    background-color: var(--color-neutrals-container-hover);
}

.bs-announcement-list__toggle:focus-visible {
    outline: 2px solid var(--color-primary-focused);
    outline-offset: 2px;
}

.bs-announcement-list__chevron {
    font-size: 0.875rem;
    transition: transform 0.15s ease;
}

.bs-announcement-list__toggle[aria-expanded="false"] .bs-announcement-list__chevron {
    transform: rotate(-90deg);
}

.bs-announcement-list__items {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.bs-announcement-list__skeleton {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.bs-announcement {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.bs-announcement__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.25rem 1rem;
}

.bs-announcement__actions {
    margin-left: auto;
}

@media (prefers-reduced-motion: reduce) {
    .bs-announcement-list__chevron {
        transition: none;
    }
}

@media (max-width: 1023px) {
    .dashboard-list__badge {
        width: 8px;
        height: 8px;
        padding: 0;
        border-radius: 50%;
        font-size: 0;
        line-height: 0;
        background-color: var(--color-primary-default);
    }
}


/* ============================================================
   PersonStatusBadges alignment patch
   ============================================================ */

/* PersonStatusBadges hardcodes ms-2 (for legacy non-flex callers) and its tooltip wrapper has no
   line-height of its own — both cause misalignment next to a sibling badge in a gap-based row.
   Cancel the margin and make the wrapper hug its badge. */
.d-flex.gap-2 > .person-status-badges__tooltip > .badge {
    margin-left: 0;
}

.d-flex.gap-2 > .person-status-badges__tooltip {
    display: inline-flex;
    align-items: center;
}

/* ============================================================
   BoardMemberSearchResultCard chip size
   ============================================================ */

/* Shrinks the board-function chip for this card only — BsChip sets the BrandSync-spec "Large" pill
   (32px/16px padding), used as-is everywhere else. The class reaches the chip through BsChip's Class
   passthrough, so BsChip renders the element and a scoped rule in the card can never match it; the
   override has to be global until BsChip grows a size parameter of its own.
   Stays within BrandSync minimums: 12px caption floor, line-height >= 1.5x font size, 8px-grid spacing. */
.bs-board-member-card__badge.bs-chip {
    min-height: 1.5rem;
    padding: 0 0.5rem;
    font-size: 12px;
    line-height: 18px;
    gap: 0;
}

/* Enrollment services step: the sticky total bar covers the bottom of the viewport, so
 * focus and scroll-into-view must stop short of it. Only the bottom longhand, so any top
 * padding for the navbar is untouched. */
html:has(.bs-services-step__summary) {
    scroll-padding-bottom: 5rem;
}

/* ################################################################
   5. PAGES
   ################################################################ */

/* ============================================================
   Info pages (contact, about, ...) — card rendering customer-configured HTML.
   Padding is handled by Bootstrap p-3 / p-md-4 utilities on the element.
   ============================================================ */

.info-page__card {
    background-color: var(--color-neutrals-container);
    border-radius: var(--brand-border-radius, 16px);
    box-shadow: var(--shadow-card);
    color: var(--color-neutrals-default);
    font-family: var(--font-family, 'Roboto', sans-serif);
    font-size: var(--font-body-md-size, 16px);
    line-height: var(--font-body-md-line-height, 1.5);
}
