/* ==========================================================================
   design-v2.css — the v2 design system
   --------------------------------------------------------------------------
   the rules for using this file live in .claude/skills/ui-design-v2/SKILL.md.
   quick version:
   - only pages that have been migrated load this file, and all their markup
     sits inside <div class="v2">. nothing here touches old pages.
   - no extra wrapper boxes: the master page's container is the page box,
     so .v2-page is opt-in and cards are never nested inside cards.
   - components only use the --v2-* variables below, never hex colors.
   - brand colors come from the client's theme (--factor360* variables set by
     the theme file, the client file, or the database custom styling), so
     every theme works without editing the theme files.
   - sizes are in px on purpose: bootstrap 3 sets html { font-size: 10px },
     so rem values would come out wrong.
   ========================================================================== */


/* --------------------------------------------------------------------------
   Variables
   --------------------------------------------------------------------------
   declared with :where(:root), which has zero specificity. that means any
   theme or client file that sets a --v2-* variable in a plain :root block
   wins, whether it loads before or after this file.
   -------------------------------------------------------------------------- */
:where(:root) {
    /* brand: follows the theme. the database styling sets --factor360V2PrimaryColor (and its text color)
       to the client's first brand color that is not near-white (Client.ClientStyling.GetV2PrimaryColorPair),
       because a white main navigation color made checked switches and selected options white on white */
    --v2-primary: var(--factor360V2PrimaryColor, var(--factor360ButtonBackgroundColor, #1565d8));
    /* themes set ButtonColor; the database styling sometimes only sets ButtonForeColor */
    --v2-on-primary: var(--factor360V2OnPrimaryColor, var(--factor360ButtonColor, var(--factor360ButtonForeColor, #ffffff)));
    /* use the client's own hover color when the database styling sets one */
    --v2-primary-hover: var(--factor360ButtonHoverBackgroundColor, color-mix(in srgb, var(--v2-primary) 85%, black));
    /* darker shade for icons, links and text on light backgrounds. light brand
       colors (orange, green) are unreadable as text, this keeps them readable */
    --v2-primary-strong: color-mix(in srgb, var(--v2-primary) 70%, black);
    --v2-primary-subtle: color-mix(in srgb, var(--v2-primary) 10%, white);
    --v2-primary-border: color-mix(in srgb, var(--v2-primary) 25%, white);
    --v2-focus-ring: color-mix(in srgb, var(--v2-primary) 35%, transparent);
    /* the admin menu bar's own colors (themes and the database styling set them), for a piece that should
       match the menu (e.g. the band on .v2-card-banded). without them it falls back to the brand pair */
    --v2-nav: var(--factor360MainNavigationBackgroundColor, var(--v2-primary));
    --v2-on-nav: var(--factor360MainNavigationColor, var(--v2-on-primary));

    /* neutrals: the same for every theme */
    --v2-page-bg: #f5f7fb;
    --v2-surface: #ffffff;
    --v2-surface-muted: #f8fafc;
    /* background of a greyed-out (unavailable) card, a bit darker than
       surface-muted so it's clearly different from the white cards next to it */
    --v2-surface-disabled: #f1f3f6;
    --v2-border: #e3e8ef;
    --v2-border-strong: #cfd6e0;
    --v2-text: #0f1b2d;
    --v2-text-muted: #4b5565;
    --v2-text-subtle: #6b7483;

    /* decorative tones, only for telling peer options apart (e.g. icon circles).
       never used for buttons or status */
    --v2-tone-blue: #1d63d8;
    --v2-tone-blue-subtle: #e8f0fd;
    --v2-tone-navy: #1e3a8a;
    --v2-tone-navy-subtle: #e8edf7;
    --v2-tone-green: #15803d;
    --v2-tone-green-subtle: #eaf7ef;
    --v2-tone-purple: #7c3aed;
    --v2-tone-purple-subtle: #f3edfe;
    /* icon circles only: 3.2:1 on its tint is enough for an icon, but too low for
       pill text (4.5:1), so there's deliberately no .v2-pill.v2-tone-orange */
    --v2-tone-orange: #ea580c;
    --v2-tone-orange-subtle: #fff1e6;
    /* dark amber on a pale yellow, 6.4:1, so it's readable as pill text too */
    --v2-tone-amber: #92400e;
    --v2-tone-amber-subtle: #fef3c7;

    /* light blue box for a group of settings (.v2-card-soft). fixed, the same under
       every theme, like the old "well-blue" boxes; a theme can override these two */
    --v2-card-soft-bg: var(--v2-tone-blue-subtle);
    --v2-card-soft-border: color-mix(in srgb, var(--v2-tone-blue) 18%, white);

    /* status: the same for every theme, so an error never looks like brand color */
    --v2-success: #1f8a4c;
    --v2-success-subtle: #eaf6ef;
    --v2-warning: #9a6212;
    --v2-warning-subtle: #fdf4e3;
    /* a yellow warning for big figures and their icon circles (e.g. the warning count on Validate
       Upsell Rule), where the dark --v2-warning reads as brown. 3.4:1 on white is enough for a
       figure-size number and an icon, but too low for body text, so text keeps --v2-warning */
    --v2-warning-figure: #b58100;
    --v2-warning-figure-subtle: #fef9c3;
    --v2-danger: #c0392b;
    --v2-danger-subtle: #fcecea;
    --v2-info: #1d5fa8;
    --v2-info-subtle: #e9f1fb;
    /* October 22, 2026 Awan Soekamto (https://app.asana.com/1/5405414973450/project/218888662811289/task/1219734038162140)
       a learner's started course: the same blue as the Semantic UI "ui blue label" badge that marks a started course
       in the bundle's list (Constant.SemanticCss.BlueLabel), so a started count on the same page reads as the same
       thing. on- is the text on a fill of it (white, like the badge's), -strong is for its text on a light background */
    --v2-status-started: #2185d0;
    --v2-on-status-started: #ffffff;
    --v2-status-started-strong: color-mix(in srgb, var(--v2-status-started) 70%, black);

    /* spacing scale: only use these steps */
    --v2-space-1: 4px;
    --v2-space-2: 8px;
    --v2-space-3: 12px;
    --v2-space-4: 16px;
    --v2-space-5: 20px;
    --v2-space-6: 24px;
    --v2-space-7: 32px;
    --v2-space-8: 40px;

    /* radius */
    --v2-radius-sm: 6px;
    --v2-radius-md: 12px;
    --v2-radius-lg: 16px;
    --v2-radius-pill: 999px;

    /* elevation */
    --v2-shadow-sm: 0 1px 2px rgba(15, 27, 45, 0.06);
    --v2-shadow-md: 0 4px 16px rgba(15, 27, 45, 0.06), 0 1px 3px rgba(15, 27, 45, 0.05);
    --v2-shadow-button: 0 2px 6px color-mix(in srgb, var(--v2-primary) 30%, transparent);
    /* a box floating above the page (e.g. .v2-dialog) */
    --v2-shadow-lg: 0 24px 48px rgba(15, 27, 45, 0.18), 0 2px 6px rgba(15, 27, 45, 0.08);
    /* the dimmed page behind a .v2-dialog */
    --v2-backdrop: rgba(15, 27, 45, 0.45);

    /* type scale */
    /* one clear step above the 20px card titles; 28px looked too loud */
    --v2-font-page-title: 24px;
    --v2-font-page-subtitle: 17px;
    --v2-font-section-title: 20px;
    --v2-font-card-title: 20px;
    --v2-font-body: 15px;
    --v2-font-small: 13px;
    /* a big standalone figure in a summary box (e.g. a dashboard's revenue or a count of errors),
       one step above the page title so the number is what the eye lands on. 26px on phones */
    --v2-font-figure: 32px;
}


/* --------------------------------------------------------------------------
   Root wrapper
   --------------------------------------------------------------------------
   every migrated page wraps its content in <div class="v2">. this resets the
   few global site.css rules (headings, paragraphs, underlined links) that
   would otherwise leak into the new components. font-family is inherited on
   purpose, because clients can pick their own font in the database styling.
   -------------------------------------------------------------------------- */
.v2 {
    color: var(--v2-text);
    font-size: var(--v2-font-body);
    line-height: 1.5;
}

    .v2 *,
    .v2 *::before,
    .v2 *::after {
        box-sizing: border-box;
    }

    /* v2 components set display (e.g. .v2-form-hint is a block), which would beat
       the browser's own [hidden] rule; this keeps the hidden attribute working */
    .v2 [hidden] {
        display: none !important;
    }

    .v2 h1,
    .v2 h2,
    .v2 h3,
    .v2 h4,
    .v2 p {
        margin: 0;
        padding: 0;
        color: inherit;
        text-transform: none;
    }


/* --------------------------------------------------------------------------
   Page shell
   -------------------------------------------------------------------------- */

/* optional white panel around the whole page. NOT used by default: most master
   pages (Admin.master, for one) already wrap MainContent in their own white
   wrapper_container box, and nesting this inside it draws a box inside a box.
   only use it when someone asks for it, or when the master page has no content
   container of its own */
.v2-page {
    background: var(--v2-surface);
    border-radius: var(--v2-radius-lg);
    box-shadow: var(--v2-shadow-md);
    padding: var(--v2-space-8);
}

/* title block on the left, page-level actions on the right. wraps on narrow screens.
   the 24px bottom margin is one step more than the 20px grid gap, so the header
   still reads as its own block above the first row of cards */
.v2-page-header {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--v2-space-4);
    margin-bottom: var(--v2-space-6);
}

.v2 .v2-page-title {
    font-size: var(--v2-font-page-title);
    font-weight: 700;
    line-height: 1.2;
    color: var(--v2-text);
}

.v2 .v2-page-subtitle {
    margin-top: var(--v2-space-2);
    font-size: var(--v2-font-page-subtitle);
    color: var(--v2-text-muted);
}

/* a page title that is a record's name, which can be long (e.g. an upsell rule's name, up to
   250 characters). the title block takes the room the actions leave instead of pushing them
   under it, and the title stops at two lines with an ellipsis; the code-behind puts the full
   name in the title's tooltip. on narrow screens the actions still wrap under the title */
.v2-page-header-title {
    flex: 1 1 320px;
    min-width: 0;
}

    .v2 .v2-page-title-clamp {
        display: -webkit-box;
        -webkit-box-orient: vertical;
        -webkit-line-clamp: 2;
        overflow: hidden;
        overflow-wrap: anywhere;
    }

/* the page title with a status pill right after it (e.g. an upsell rule's name, then Active). the
   pill keeps its size and stays beside the title, while a long title wraps or clamps next to it */
.v2-page-title-row {
    display: flex;
    align-items: center;
    gap: var(--v2-space-3);
}

    .v2-page-title-row > .v2-page-title {
        min-width: 0;
    }

    .v2-page-title-row > .v2-status {
        flex-shrink: 0;
    }

/* short facts on one line, split by middle dots (e.g. an upsell rule's name, status and course groups
   under the page title). each fact is its own child element, so a long line wraps between facts.
   a <strong> fact (e.g. the record's name) reads in the regular text color */
.v2-inline-list > * + *::before {
    content: "\00B7";
    margin: 0 var(--v2-space-2);
    color: var(--v2-text-subtle);
    font-weight: 400;
}

    .v2 .v2-inline-list > strong {
        color: var(--v2-text);
    }

    /* an arrow inside a fact (e.g. the trigger group, an arrow, then the recommended group) */
    .v2-inline-list i {
        margin: 0 var(--v2-space-1);
        font-size: 15px;
    }

.v2-page-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--v2-space-3);
}

/* breadcrumb trail above the page header. wraps the bootstrap breadcrumb html that
   HTMLUtility.GetBootstrapBreadcrumbHTML builds and only restyles it: no grey box,
   small muted text, brand-strong links for the pages above this one */
.v2 .v2-breadcrumb .breadcrumb {
    margin: 0 0 var(--v2-space-3);
    padding: 0;
    background: transparent;
    font-size: var(--v2-font-small);
    color: var(--v2-text-muted);
}

    .v2 .v2-breadcrumb .breadcrumb > li + li::before {
        color: var(--v2-text-subtle);
    }

    .v2 .v2-breadcrumb .breadcrumb a,
    .v2 .v2-breadcrumb .breadcrumb a:visited {
        color: var(--v2-primary-strong);
        font-weight: 600;
        text-decoration: none;
    }

        .v2 .v2-breadcrumb .breadcrumb a:hover {
            text-decoration: underline;
        }

    .v2 .v2-breadcrumb .breadcrumb > .active {
        color: var(--v2-text-muted);
    }

/* a v2 block placed inside a page that hasn't been migrated yet (e.g. the Agent Ally
   author bar under the edit question page's header). the old page's own spacing
   doesn't leave room for it, so it brings a little space above and below itself */
.v2.v2-island {
    margin: var(--v2-space-3) 0;
}

    /* an island card under an unmigrated page header (e.g. the Agent Ally author
       actions card). the island's own margin does the spacing, so the card's top
       margin goes; padding stays a little tighter than a full page card */
    .v2-island > .v2-card {
        margin-top: 0;
        padding: var(--v2-space-3);
    }


/* --------------------------------------------------------------------------
   Grid
   -------------------------------------------------------------------------- */
.v2-grid {
    display: grid;
    gap: var(--v2-space-5);
}

/* "at most N columns, each at least X wide". the column count follows the grid's
   own width rather than the viewport, so it adapts to whatever space the master
   page's navigation leaves. min(100%, ...) stops overflow on very narrow screens.
   gotcha: the space subtracted inside calc() MUST be the same variable as the
   gap above. if it's smaller, N columns never fit and everything stacks in one */
.v2-grid-2 {
    grid-template-columns: repeat(auto-fill, minmax(min(100%, max(460px, calc((100% - var(--v2-space-5)) / 2))), 1fr));
}

.v2-grid-3 {
    grid-template-columns: repeat(auto-fill, minmax(min(100%, max(300px, calc((100% - 2 * var(--v2-space-5)) / 3))), 1fr));
}

/* as many equal columns as fit (each at least 200px). auto-fit, not auto-fill, so a
   short set (e.g. 3 summary boxes when one is hidden) stretches to fill the row
   instead of leaving an empty slot at the end. for small peer boxes like .v2-stat-card */
.v2-grid-fit {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
}

/* a vertical stack of cards inside one grid cell (e.g. two short cards next to a
   tall one). same gap as the grid, so the spacing matches in both directions */
.v2-stack {
    display: flex;
    flex-direction: column;
    gap: var(--v2-space-5);
    min-width: 0;
}

    /* a tighter stack (12px), for a page of small boxes (e.g. Suggest Question) */
    .v2-stack.v2-stack-compact {
        gap: var(--v2-space-3);
    }


/* --------------------------------------------------------------------------
   Card
   -------------------------------------------------------------------------- */
.v2-card {
    position: relative;
    background: var(--v2-surface);
    border: 1px solid var(--v2-border);
    border-radius: var(--v2-radius-md);
    /* 16px on every screen size, so cards next to each other (main + side, summary
       boxes, compact cards, record boxes) always line up without their own
       padding overrides */
    padding: var(--v2-space-4);
}

/* a card that stays in view while the page scrolls past a taller neighbour in the same grid row or
   layout (e.g. the assigned users list next to long search results in the Assigned Users popup), so
   what an action next to it changed shows without scrolling back up. align-self keeps it at its own
   height instead of the row's, which sticky needs to have room to move. a stacked card has none */
.v2-card.v2-card-sticky {
    position: sticky;
    top: var(--v2-space-4);
    align-self: start;
}

/* a region waiting on a server round trip (e.g. the cards in an ajax panel while a search runs): it stops
   taking clicks, so a second click can't fire the same action twice, and its lists fade, so a slow answer
   reads as "working". the fade waits 200ms, so a quick answer doesn't flicker. the page script adds the
   class when the request starts; the re-rendered content comes back without it */
.v2-busy {
    pointer-events: none;
}

    .v2-busy :is(.v2-table-wrap, .v2-pager, .v2-tip) {
        opacity: .55;
        transition: opacity .2s ease .2s;
    }

/* "Searching…" with a spinner, shown in place of a list's hint while the busy region waits.
   the spinner takes the brand color so it reads as activity, not as a muted note */
.v2 .v2-busy-status {
    display: flex;
    align-items: center;
    gap: var(--v2-space-2);
}

    .v2 .v2-busy-status > i {
        color: var(--v2-primary-strong);
    }

.v2 .v2-card-title {
    font-size: var(--v2-font-card-title);
    font-weight: 700;
    line-height: 1.3;
    color: var(--v2-text);
    margin-bottom: var(--v2-space-1);
}

.v2 .v2-card-text {
    color: var(--v2-text-muted);
    line-height: 1.6;
    margin-bottom: var(--v2-space-5);
}

/* a badge pinned to the card's top-right corner (e.g. "Agent Ally"). keep its
   text short (about 14 characters); the title reserves room so a long title
   wraps before it runs under the badge */
.v2-card-corner {
    position: absolute;
    top: var(--v2-space-4);
    right: var(--v2-space-4);
}

.v2 .v2-action-card-body:has(> .v2-card-corner) > .v2-card-title {
    padding-right: 120px;
}

/* disabled card: the card's main feature is unavailable (e.g. AI Generate when
   Agent Ally is off). everything goes neutral grey so the card reads as
   inactive under every theme. the card itself stays hoverable so the disabled
   button's "why" tooltip still works. a secondary action that still works (like
   Existing Tasks) keeps its normal style, so it's clear it's still clickable */
.v2-card.is-disabled {
    background: var(--v2-surface-disabled);
    border-color: var(--v2-border);
}

    .v2 .v2-card.is-disabled .v2-card-title {
        color: var(--v2-text-muted);
    }

    .v2 .v2-card.is-disabled .v2-card-text {
        color: var(--v2-text-subtle);
    }

    /* overrides both the theme tone and all the decorative tones */
    .v2-card.is-disabled .v2-icon-circle {
        background: var(--v2-border);
        color: var(--v2-text-subtle);
    }

    .v2-card.is-disabled .v2-badge {
        background: var(--v2-surface);
        border-color: var(--v2-border-strong);
        color: var(--v2-text-subtle);
    }

    /* the disabled primary button turns flat grey instead of a faded brand color */
    .v2 .v2-card.is-disabled .v2-btn-primary.aspNetDisabled,
    .v2 .v2-card.is-disabled .v2-btn-primary[disabled],
    .v2 .v2-card.is-disabled .v2-btn-primary.is-disabled,
    .v2 .v2-card.is-disabled .v2-btn-primary.aspNetDisabled:hover,
    .v2 .v2-card.is-disabled .v2-btn-primary[disabled]:hover,
    .v2 .v2-card.is-disabled .v2-btn-primary.is-disabled:hover {
        background: var(--v2-border);
        border-color: var(--v2-border-strong);
        color: var(--v2-text-subtle);
        opacity: 1;
    }

/* action card: big icon circle on the left, title + text + buttons on the right.
   the card is a size container, so its contents shrink based on the card's own
   width (see the @container rules at the bottom). the gap is a margin on the
   icon rather than flex gap, because a container query can only restyle the
   card's children, not the card itself.
   gotcha: a size container can't take its width from its content, so put action
   cards in a .v2-grid or another block that gives them a width */
.v2-action-card {
    display: flex;
    align-items: center;
    container-type: inline-size;
}

    /* the same 16px as the card's padding, so the icon sits evenly between the
       card's edge and the text */
    .v2-action-card > .v2-icon-circle {
        margin-right: var(--v2-space-4);
    }

.v2-action-card-body {
    flex: 1 1 auto;
    min-width: 0;
}


/* --------------------------------------------------------------------------
   Icon circle
   --------------------------------------------------------------------------
   the default tone follows the theme. the blue/navy/green/purple tones are
   decorative, fixed colors (the same under every theme) that only exist to
   tell peer options apart, e.g. the four ways to add a question.
   -------------------------------------------------------------------------- */
.v2-icon-circle {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 96px;
    height: 96px;
    border-radius: 50%;
    background: var(--v2-primary-subtle);
    color: var(--v2-primary-strong);
    font-size: 38px;
    line-height: 1;
}

.v2-icon-circle-sm {
    width: 56px;
    height: 56px;
    font-size: 22px;
}

/* two letters instead of an icon (e.g. "BR" for a Bronze membership level), at body size */
.v2-icon-circle.v2-icon-circle-initials {
    font-size: var(--v2-font-body);
    font-weight: 700;
    letter-spacing: 0.02em;
}

.v2-icon-circle.v2-tone-blue {
    background: var(--v2-tone-blue-subtle);
    color: var(--v2-tone-blue);
}

.v2-icon-circle.v2-tone-navy {
    background: var(--v2-tone-navy-subtle);
    color: var(--v2-tone-navy);
}

.v2-icon-circle.v2-tone-green {
    background: var(--v2-tone-green-subtle);
    color: var(--v2-tone-green);
}

.v2-icon-circle.v2-tone-purple {
    background: var(--v2-tone-purple-subtle);
    color: var(--v2-tone-purple);
}

.v2-icon-circle.v2-tone-orange {
    background: var(--v2-tone-orange-subtle);
    color: var(--v2-tone-orange);
}

.v2-icon-circle.v2-tone-amber {
    background: var(--v2-tone-amber-subtle);
    color: var(--v2-tone-amber);
}

/* status icon circles, for a summary box that reports a state (e.g. "2 errors" or "5 checks passed"
   on Validate Upsell Rule). they use the status colors, so a state reads the same under every theme;
   the decorative tones above are never used for a state */
.v2-icon-circle.v2-icon-circle-danger {
    background: var(--v2-danger-subtle);
    color: var(--v2-danger);
}

.v2-icon-circle.v2-icon-circle-warning {
    background: var(--v2-warning-figure-subtle);
    color: var(--v2-warning-figure);
}

.v2-icon-circle.v2-icon-circle-success {
    background: var(--v2-success-subtle);
    color: var(--v2-success);
}


/* --------------------------------------------------------------------------
   Badge
   -------------------------------------------------------------------------- */
.v2-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--v2-space-2);
    padding: var(--v2-space-1) var(--v2-space-3);
    border: 1px solid var(--v2-primary-border);
    border-radius: var(--v2-radius-pill);
    background: var(--v2-primary-subtle);
    color: var(--v2-primary-strong);
    font-size: var(--v2-font-small);
    font-weight: 600;
    line-height: 1.4;
    white-space: nowrap;
}

/* Agent Ally badge (purple, see .v2-card-ai) */
.v2-badge-ai {
    background: var(--v2-tone-purple-subtle);
    border-color: color-mix(in srgb, var(--v2-tone-purple) 20%, white);
    color: var(--v2-tone-purple);
}

/* a small label pill for a value (e.g. a Bloom level). neutral by default; add a
   decorative tone class (v2-tone-blue/green/purple/navy/amber) to tell peer values apart */
.v2-pill {
    display: inline-block;
    padding: 2px var(--v2-space-3);
    border-radius: var(--v2-radius-sm);
    background: var(--v2-surface-muted);
    border: 1px solid var(--v2-border);
    color: var(--v2-text);
    font-size: var(--v2-font-small);
    font-weight: 600;
    line-height: 1.6;
}

    .v2-pill.v2-tone-blue {
        background: var(--v2-tone-blue-subtle);
        border-color: transparent;
        color: var(--v2-tone-blue);
    }

    .v2-pill.v2-tone-navy {
        background: var(--v2-tone-navy-subtle);
        border-color: transparent;
        color: var(--v2-tone-navy);
    }

    .v2-pill.v2-tone-green {
        background: var(--v2-tone-green-subtle);
        border-color: transparent;
        color: var(--v2-tone-green);
    }

    .v2-pill.v2-tone-purple {
        background: var(--v2-tone-purple-subtle);
        border-color: transparent;
        color: var(--v2-tone-purple);
    }

    .v2-pill.v2-tone-amber {
        background: var(--v2-tone-amber-subtle);
        border-color: transparent;
        color: var(--v2-tone-amber);
    }

/* status pill with a leading dot (e.g. "Matches planned topic" in a table) */
.v2-status {
    display: inline-flex;
    align-items: center;
    gap: var(--v2-space-2);
    padding: 2px var(--v2-space-3);
    border-radius: var(--v2-radius-pill);
    font-size: var(--v2-font-small);
    font-weight: 600;
    line-height: 1.6;
}

    .v2-status::before {
        content: "";
        width: 8px;
        height: 8px;
        border-radius: 50%;
        background: currentColor;
    }

.v2-status-success {
    background: var(--v2-success-subtle);
    color: var(--v2-success);
}

.v2-status-danger {
    background: var(--v2-danger-subtle);
    color: var(--v2-danger);
}

/* something that needs a look before it can go ahead (e.g. an upsell rule that is Not Validated) */
.v2-status-warning {
    background: var(--v2-warning-subtle);
    color: var(--v2-warning);
}

/* something still running or waiting on a next step (e.g. "In Progress", "Copied to editor — not saved") */
.v2-status-info {
    background: var(--v2-info-subtle);
    color: var(--v2-info);
}

/* a plain state that needs no attention (e.g. "Draft"). grey rather than a status color, so it doesn't
   read as an error or as done. surface-disabled, not surface-muted, since surface-muted is almost
   white and the pill would vanish on a white card */
.v2-status-neutral {
    background: var(--v2-surface-disabled);
    color: var(--v2-text-muted);
}

/* a status pill right after the page subtitle text (e.g. the task name + "Completed") */
.v2 .v2-page-subtitle > .v2-status {
    margin-left: var(--v2-space-2);
    vertical-align: middle;
}

/* a read-only value in a muted box, so it reads as data next to editable inputs */
.v2-value-box {
    display: block;
    padding: var(--v2-space-2) var(--v2-space-3);
    border-radius: var(--v2-radius-sm);
    background: var(--v2-surface-muted);
    border: 1px solid var(--v2-border);
    font-size: var(--v2-font-small);
}


/* --------------------------------------------------------------------------
   Buttons
   --------------------------------------------------------------------------
   work on <a>, <button> and <input type="submit">. selectors are written as
   ".v2 a.v2-btn..." as well so they beat site.css's a:visited / a:hover
   colors and underline.
   -------------------------------------------------------------------------- */
.v2 .v2-btn,
.v2 a.v2-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--v2-space-2);
    min-width: 104px;
    min-height: 42px;
    padding: 0 var(--v2-space-5);
    border: 1px solid transparent;
    border-radius: var(--v2-radius-sm);
    font-size: var(--v2-font-body);
    font-weight: 600;
    line-height: 1;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}

    .v2 .v2-btn:focus-visible,
    .v2 a.v2-btn:focus-visible {
        outline: none;
        box-shadow: 0 0 0 3px var(--v2-focus-ring);
    }

    .v2 .v2-btn i {
        font-size: 15px;
    }

/* primary: the one main action in an area */
.v2 .v2-btn-primary,
.v2 a.v2-btn-primary,
.v2 a.v2-btn-primary:link,
.v2 a.v2-btn-primary:visited {
    background: var(--v2-primary);
    border-color: var(--v2-primary);
    color: var(--v2-on-primary);
}

    .v2 .v2-btn-primary:hover,
    .v2 a.v2-btn-primary:hover,
    .v2 a.v2-btn-primary:active {
        background: var(--v2-primary-hover);
        border-color: var(--v2-primary-hover);
        color: var(--v2-on-primary);
    }

/* secondary: supporting actions next to a primary button */
.v2 .v2-btn-secondary,
.v2 a.v2-btn-secondary,
.v2 a.v2-btn-secondary:link,
.v2 a.v2-btn-secondary:visited {
    background: var(--v2-surface);
    border-color: var(--v2-border-strong);
    color: var(--v2-primary-strong);
}

    .v2 .v2-btn-secondary:hover,
    .v2 a.v2-btn-secondary:hover,
    .v2 a.v2-btn-secondary:active {
        background: var(--v2-primary-subtle);
        border-color: var(--v2-primary-border);
        color: var(--v2-primary-strong);
    }

/* opt-in (add v2-btn-site-font): a v2 button set among the site's own .button buttons on an unmigrated page (e.g. Use
   My Credit under Add to Cart on a course page) keeps the site button's lettering (Site.css .button), so the two read as
   one set. a page rule with !important (the course layouts' .btnAccessClass) still sets the size, as it does for its
   own buttons */
.v2 .v2-btn.v2-btn-site-font {
    font-size: 14px;
    font-weight: 400;
}

/* destructive action (e.g. "Delete Task"): outlined in the danger color, the same
   under every theme, so it never reads as the brand's main action */
.v2 .v2-btn-danger,
.v2 a.v2-btn-danger,
.v2 a.v2-btn-danger:link,
.v2 a.v2-btn-danger:visited {
    background: var(--v2-surface);
    border-color: color-mix(in srgb, var(--v2-danger) 45%, white);
    color: var(--v2-danger);
}

    .v2 .v2-btn-danger:hover,
    .v2 a.v2-btn-danger:hover,
    .v2 a.v2-btn-danger:active {
        background: var(--v2-danger-subtle);
        border-color: var(--v2-danger);
        color: var(--v2-danger);
    }

/* an icon-only button in a table row (e.g. remove a learning activity from a membership level): a small
   bordered square, quiet until hovered. it has no text, so give it a tooltip and an aria-label. add
   is-danger to a destructive one, so it turns the danger red on hover */
.v2 .v2-icon-btn,
.v2 a.v2-icon-btn,
.v2 a.v2-icon-btn:link,
.v2 a.v2-icon-btn:visited {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    padding: 0;
    border: 1px solid var(--v2-border-strong);
    border-radius: var(--v2-radius-sm);
    background: var(--v2-surface);
    color: var(--v2-text-muted);
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

    .v2 .v2-icon-btn:hover,
    .v2 a.v2-icon-btn:hover {
        background: var(--v2-surface-muted);
        color: var(--v2-text);
    }

    .v2 .v2-icon-btn.is-danger:hover,
    .v2 a.v2-icon-btn.is-danger:hover {
        background: var(--v2-danger-subtle);
        border-color: var(--v2-danger);
        color: var(--v2-danger);
    }

    .v2 .v2-icon-btn:focus-visible {
        outline: none;
        box-shadow: 0 0 0 3px var(--v2-focus-ring);
    }

/* adds a soft colored shadow, used for the page-level action in the header */
.v2 .v2-btn-raised {
    box-shadow: var(--v2-shadow-button);
}

/* disabled. asp.net adds "aspNetDisabled" to a HyperLink/LinkButton with
   Enabled="False" and removes its href. pointer events stay on so the
   "why is this disabled" tooltip still shows */
.v2 .v2-btn.aspNetDisabled,
.v2 .v2-btn[disabled],
.v2 .v2-btn.is-disabled {
    opacity: 0.5;
    cursor: not-allowed;
    box-shadow: none;
}

    .v2 .v2-btn-primary.aspNetDisabled:hover,
    .v2 .v2-btn-primary[disabled]:hover,
    .v2 .v2-btn-primary.is-disabled:hover {
        background: var(--v2-primary);
        border-color: var(--v2-primary);
    }

    .v2 .v2-btn-secondary.aspNetDisabled:hover,
    .v2 .v2-btn-secondary[disabled]:hover,
    .v2 .v2-btn-secondary.is-disabled:hover {
        background: var(--v2-surface);
        border-color: var(--v2-border-strong);
    }

    .v2 .v2-btn-danger.aspNetDisabled:hover,
    .v2 .v2-btn-danger[disabled]:hover,
    .v2 .v2-btn-danger.is-disabled:hover {
        background: var(--v2-surface);
        border-color: color-mix(in srgb, var(--v2-danger) 45%, white);
    }

/* a row of buttons */
.v2-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--v2-space-3);
}

/* button row pushed to the right, or centered (e.g. inside a drop zone) */
.v2-actions-end {
    justify-content: flex-end;
}

.v2-actions-center {
    justify-content: center;
}

/* a plain text link, optionally with an icon (e.g. trash + "Remove" in a table
   row). brand-strong so light brand colors stay readable, no underline until hovered */
.v2 .v2-link,
.v2 a.v2-link:link,
.v2 a.v2-link:visited {
    display: inline-flex;
    align-items: center;
    gap: var(--v2-space-2);
    color: var(--v2-primary-strong);
    font-weight: 600;
    text-decoration: none;
}

    .v2 .v2-link:hover,
    .v2 a.v2-link:hover {
        color: var(--v2-primary-strong);
        text-decoration: underline;
    }

    /* a <button type="button"> styled as a link (e.g. "Expand all"), with no button chrome */
    .v2 button.v2-link {
        padding: 0;
        border: 0;
        background: none;
        font: inherit;
        font-weight: 600;
        cursor: pointer;
    }

    /* a HyperLink with Enabled="False" (e.g. a question that isn't generated yet):
       it has no href, so it reads as plain text rather than a broken link */
    .v2 a.v2-link.aspNetDisabled,
    .v2 a.v2-link.aspNetDisabled:hover {
        color: var(--v2-text);
        text-decoration: none;
        cursor: default;
    }

    /* a link in a table cell that has to read as a link before it is hovered, e.g. a count
       that opens a list (purchase credit list: users with credit, eligible courses) */
    .v2 a.v2-link.v2-link-underline:link,
    .v2 a.v2-link.v2-link-underline:visited,
    .v2 .v2-link.v2-link-underline {
        text-decoration: underline;
    }


/* --------------------------------------------------------------------------
   Card extras
   --------------------------------------------------------------------------
   pieces for content cards (forms, side panels), as opposed to action cards.
   -------------------------------------------------------------------------- */

/* card header: icon tile + title + subtitle, with an optional badge on the right.
   the divider under it separates the header from the card's first section */
.v2-card-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--v2-space-4);
    padding-bottom: var(--v2-space-5);
    margin-bottom: var(--v2-space-2);
    border-bottom: 1px solid var(--v2-border);
}

    .v2-card-header > div {
        flex: 1 1 240px;
        min-width: 0;
    }

/* card title size, one step under the page title (h1), so the page keeps a
   clear heading order. text inside the card stays smaller than this */
.v2 .v2-card-header .v2-card-title {
    font-size: var(--v2-font-card-title);
}

.v2 .v2-card-subtitle {
    color: var(--v2-text-muted);
}

/* badge at the end of a card header (e.g. "Powered by Agent Ally"). it wraps
   under the title on narrow cards instead of overlapping it */
.v2-card-header-badge {
    align-self: flex-start;
}

/* narrow side-column cards: a smaller title and tighter text spacing
   (the padding is the card's own 16px) */
.v2 .v2-card-compact .v2-card-title {
    font-size: var(--v2-font-page-subtitle);
    margin-bottom: 0;
}

.v2 .v2-card-compact .v2-card-text {
    margin-bottom: var(--v2-space-4);
}

    .v2 .v2-card-compact .v2-card-text:last-child {
        margin-bottom: 0;
    }

/* warning-tinted card, for something the user has to do before they can go on (e.g. buy a
   certification before it can be completed, on the learner's certification progress page).
   the status colors are the same under every theme, so it never reads as brand news; its
   button stays brand-colored. a warning icon circle inside gets a stronger tint, so it
   still stands out from the card's own pale yellow */
.v2-card.v2-card-warning {
    background: var(--v2-warning-subtle);
    border-color: color-mix(in srgb, var(--v2-warning) 25%, white);
}

    .v2-card-warning .v2-icon-circle.v2-icon-circle-warning {
        background: color-mix(in srgb, var(--v2-warning-figure) 20%, white);
    }

/* a price shown next to its buy button (e.g. the certification purchase banner): bold at
   card-title size, and kept on one line so the currency never splits from the amount */
.v2 .v2-price {
    font-size: var(--v2-font-card-title);
    font-weight: 700;
    white-space: nowrap;
}

/* brand-tinted card, for an "about this step" explanation next to a form */
.v2-card-tinted {
    background: var(--v2-primary-subtle);
    border-color: var(--v2-primary-border);
}

/* soft card: a light blue box with tighter padding, for a group of settings above
   the page's main choices (e.g. the prompt and writing-guideline boxes on Suggest
   Question). the blue is fixed (see --v2-card-soft-bg), so it reads the same under
   every theme; brand color still comes through its buttons */
.v2-card.v2-card-soft {
    padding: var(--v2-space-3) var(--v2-space-4);
    background: var(--v2-card-soft-bg);
    border-color: var(--v2-card-soft-border);
}

    /* icons sit straight on the blue, so a brand-tinted tile doesn't clash with it */
    .v2-card-soft .v2-icon-tile {
        background: transparent;
    }

    /* rows and sections inside are spaced to match the tighter padding */
    .v2-card-soft .v2-setting-bar + .v2-setting-bar {
        margin-top: var(--v2-space-3);
        padding-top: var(--v2-space-3);
    }

    .v2-card-soft .v2-section {
        margin-top: var(--v2-space-3);
    }

    /* indented rows line up under a regular 44px tile (44 + the bar's 16px gap) */
    .v2-card-soft .v2-setting-bar.v2-setting-bar-indent {
        padding-left: 60px;
    }

/* "about Agent Ally" card: explains what the AI does with the user's input.
   uses the fixed purple tone (same under every theme) because purple marks
   Agent Ally across the AI pages: this card, .v2-badge-ai and .v2-icon-tile-ai */
.v2-card-ai {
    background: var(--v2-tone-purple-subtle);
    border-color: color-mix(in srgb, var(--v2-tone-purple) 20%, white);
}

/* small icon + title on one line, for compact cards. the icon follows the card
   variant: neutral on a plain card, brand on a tinted one, purple on an AI one */
.v2-card-heading {
    display: flex;
    align-items: center;
    gap: var(--v2-space-3);
    margin-bottom: var(--v2-space-3);
    color: var(--v2-text);
    font-size: 20px;
}

    .v2-card-tinted .v2-card-heading {
        color: var(--v2-primary-strong);
    }

    /* a heading icon that reports a state (e.g. the Validation card on Edit Upsell Rule): add
       is-danger, is-warning or is-success to the icon and it takes that status color. change the
       glyph with the state too, so the state doesn't rest on color alone */
    .v2-card-heading > i.is-danger {
        color: var(--v2-danger);
    }

    .v2-card-heading > i.is-warning {
        color: var(--v2-warning);
    }

    .v2-card-heading > i.is-success {
        color: var(--v2-success);
    }

    /* a link or button row at the end of the heading (e.g. View All over a report on View Upsell Rule)
       sits on the right at body size, and wraps under the title when the card is too narrow for both */
    .v2-card-heading:has(> .v2-actions) {
        flex-wrap: wrap;
    }

    .v2-card-heading > .v2-actions {
        margin-left: auto;
        font-size: var(--v2-font-body);
    }

    .v2-card-ai .v2-card-heading > i,
    .v2 .v2-card-ai .v2-tip > i {
        color: var(--v2-tone-purple);
    }

    .v2 .v2-card-ai .v2-card-text + .v2-tip {
        border-top-color: color-mix(in srgb, var(--v2-tone-purple) 20%, white);
    }

/* header of a step whose content is a grid of cards: same look as a card header,
   but it sits on its own, above the cards, since cards can't be nested */
.v2-step-header {
    margin-bottom: var(--v2-space-5);
}

/* a titled card in a grid of repeated records (e.g. one per question) */
.v2 .v2-item-card-title {
    margin-bottom: var(--v2-space-4);
    padding-bottom: var(--v2-space-3);
    border-bottom: 1px solid var(--v2-border);
    font-size: var(--v2-font-page-subtitle);
    font-weight: 700;
}

/* a record card whose header is a band in the admin menu's color, with a border in the same color
   (e.g. one card per team on the MathK Team Competition Registration page), so each record stands
   apart from the next in a grid of look-alike cards. the band is the card's first child: it pulls
   out over the card's padding to reach the edges, and the content under it keeps the padding.
   everything in the band reads in the menu's text color, a facts line on it a step softer */
.v2-card.v2-card-banded {
    border: 2px solid var(--v2-nav);
}

.v2-card-band {
    margin: calc(-1 * var(--v2-space-4)) calc(-1 * var(--v2-space-4)) var(--v2-space-4);
    padding: var(--v2-space-3) var(--v2-space-4);
    /* the inner corner of a 2px border, so the band's corners sit flush in the card's */
    border-radius: calc(var(--v2-radius-md) - 2px) calc(var(--v2-radius-md) - 2px) 0 0;
    background: var(--v2-nav);
    color: var(--v2-on-nav);
}

    .v2 .v2-card-band .v2-card-heading,
    .v2 .v2-card-band .v2-card-title {
        margin-bottom: 0;
        color: var(--v2-on-nav);
    }

    /* a status pill next to the title (e.g. "No Team ID") keeps its own colors on the band */
    .v2-card-band .v2-card-heading {
        flex-wrap: wrap;
    }

    .v2 .v2-card-band .v2-card-text {
        margin: var(--v2-space-1) 0 0;
        color: color-mix(in srgb, var(--v2-on-nav) 85%, var(--v2-nav));
    }

        .v2-card-band .v2-inline-list > * + *::before {
            color: inherit;
        }

/* one setting laid out as a full-width bar: icon, label + hint, control on the right */
.v2-setting-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--v2-space-3) var(--v2-space-4);
    margin-top: var(--v2-space-5);
}

.v2-setting-bar-label {
    flex: 1 1 240px;
    min-width: 0;
}

/* a badge right after a title's text (e.g. "Custom Prompt" next to the prompt card's title, or
   "Default level" after a membership level's name in a .v2-item-list) */
.v2 .v2-card-title > .v2-badge,
.v2 .v2-item-title > .v2-badge {
    margin-left: var(--v2-space-2);
    vertical-align: middle;
}

/* setting bars listed under a header bar (e.g. one row per writing guideline): the
   rows are indented so their icons line up under the header's title. the 72px is the
   header's large icon tile (56px) plus the bar's 16px gap */
.v2-setting-bar.v2-setting-bar-indent {
    padding-left: 72px;
}

/* a setting that only makes sense under the one above it (e.g. Checkout Recommendations under
   Purchase Experience): indented so its icon lines up under that setting's title (the 44px icon
   tile + the bar's 16px gap) */
.v2-setting-bar.v2-setting-bar-child {
    padding-left: 60px;
}

/* setting bars inside a plain card (e.g. the prompt card on Suggest Question): the
   first one starts at the card's own padding, and the ones after it get a divider,
   the same way form rows are separated */
.v2-card > .v2-setting-bar:first-child {
    margin-top: 0;
}

.v2-setting-bar + .v2-setting-bar {
    padding-top: var(--v2-space-5);
    border-top: 1px solid var(--v2-border);
}

/* a setting whose feature is unavailable to this client (e.g. Promote Membership Savings
   without tier pricing): the icon and text go grey, the same way .v2-card.is-disabled
   greys a whole card. the bar has to be an asp:Panel so the code-behind can add the class */
.v2 .v2-setting-bar.is-disabled .v2-card-title,
.v2 .v2-setting-bar.is-disabled .v2-card-subtitle {
    color: var(--v2-text-subtle);
}

.v2-setting-bar.is-disabled .v2-icon-tile {
    background: var(--v2-border);
    color: var(--v2-text-subtle);
}

/* the controls on the right of a setting bar (e.g. a status pill and a button) */
.v2-setting-bar-control {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--v2-space-3);
}

/* a note under a setting bar's label and control, lined up under the label
   (the bar's 44px icon tile + its 16px gap) */
.v2 .v2-setting-bar > .v2-note {
    flex: 1 1 100%;
    margin-left: 60px;
}

/* a button row inside a card header (e.g. "Use Suggested Question" next to the
   title) sits on the right, and wraps under the title on narrow cards */
.v2-card-header > .v2-actions {
    justify-content: flex-end;
}

/* opt-in (add v2-card-header-fit-actions): the buttons take only the room they need, so a long subtitle gets the rest
   of the header instead of sharing it half and half with them (e.g. the Learners and User Groups cards of a purchase credit) */
.v2-card-header.v2-card-header-fit-actions > .v2-actions {
    flex: 0 0 auto;
}

/* inside a .v2-stack the stack's gap already spaces the step header from the cards */
.v2-stack > .v2-step-header {
    margin-bottom: 0;
}

/* settings card: a group of many small settings in a dense list (e.g. Bundle Behavior on the
   bundle settings page). a muted header strip (a plain icon, the title and a one-line subtitle),
   then one .v2-settings-row per setting: the label on the left, the control on the right, split
   by thin lines. denser than .v2-setting-bar, which gives every setting an icon tile and a hint.
   the card is a size container, so on a narrow card the control drops under its label (see the
   @container rule at the bottom) */
.v2-card.v2-settings-card {
    padding: 0 0 var(--v2-space-2);
    container-type: inline-size;
}

.v2-settings-card-header {
    display: flex;
    align-items: flex-start;
    gap: var(--v2-space-3);
    margin-bottom: var(--v2-space-1);
    padding: var(--v2-space-3) var(--v2-space-4);
    border-bottom: 1px solid var(--v2-border);
    border-radius: var(--v2-radius-md) var(--v2-radius-md) 0 0;
    background: var(--v2-surface-muted);
}

    .v2-settings-card-header > i {
        flex: 0 0 24px;
        margin-top: 3px;
        color: var(--v2-text);
        font-size: 20px;
        text-align: center;
    }

    .v2 .v2-settings-card-header .v2-card-title {
        margin-bottom: 0;
        font-size: var(--v2-font-page-subtitle);
    }

    .v2 .v2-settings-card-header .v2-card-subtitle {
        font-size: var(--v2-font-small);
    }

/* one setting: label column (up to 360px) and control column. a hint under the label is a
   span.v2-form-hint inside .v2-settings-label */
.v2-settings-row {
    display: grid;
    grid-template-columns: minmax(180px, 360px) minmax(0, 1fr);
    gap: var(--v2-space-2) var(--v2-space-4);
    align-items: center;
    margin: 0 var(--v2-space-4);
    padding: var(--v2-space-2) 0;
}

    .v2-settings-row + .v2-settings-row {
        border-top: 1px solid var(--v2-border);
    }

.v2-settings-label {
    font-weight: 600;
    color: var(--v2-text);
}

/* the control column keeps short controls on one line (a switch, a number and its words, an
   input and its button) and wraps them on a narrow card. a hint or a note in it takes a line
   of its own. use .v2-form-control instead when the controls should stack */
.v2-settings-control {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--v2-space-2) var(--v2-space-3);
    min-width: 0;
}

    .v2-settings-control > :is(.v2-form-hint, .v2-note) {
        flex: 1 1 100%;
    }

    .v2 .v2-settings-control > .v2-form-hint {
        margin-top: 0;
    }

/* a line of controls inside a stacked column (e.g. the limit's switch, then its number and
   frequency) uses the column's full width, so its hints can take a whole line */
.v2-form-control > .v2-settings-control {
    align-self: stretch;
}

/* a setting worth pointing out (e.g. a new one): the row becomes a light blue box of its own,
   the same fixed blue as .v2-card-soft. its label column lines up with the rows around it */
.v2-settings-row.v2-settings-row-soft {
    align-items: start;
    margin: var(--v2-space-2) var(--v2-space-2);
    padding: var(--v2-space-3) var(--v2-space-2);
    border: 1px solid var(--v2-card-soft-border);
    border-radius: var(--v2-radius-sm);
    background: var(--v2-card-soft-bg);
}

    .v2-settings-row-soft + .v2-settings-row {
        border-top: 0;
    }

/* free content under the rows (e.g. a message editor and its Update button) */
.v2-settings-body {
    display: flex;
    flex-direction: column;
    gap: var(--v2-space-3);
    padding: var(--v2-space-3) var(--v2-space-4) var(--v2-space-2);
}

/* a switch that says its state after it ("On" / "Off"), for a list of on/off settings where
   the label sits in another column. asp:CheckBox only renders a label when it has Text, so the
   word comes from css and follows the checkbox as it changes, with no script */
.v2 .v2-switch.v2-switch-state::after {
    content: "Off";
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
}

.v2 .v2-switch.v2-switch-state:has(input:checked)::after {
    content: "On";
}

/* an input with its own button on the right (e.g. a label and its Update button). the input
   takes the free room, and a telerik input's width="100%" fills its cell */
.v2-input-with-action {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--v2-space-3);
    align-items: start;
    flex: 1 1 100%;
}

/* a section inside a card (no border of its own; spacing and a heading do the grouping) */
.v2-section {
    margin-top: var(--v2-space-6);
}

.v2 .v2-section-title {
    font-size: var(--v2-font-page-subtitle);
    font-weight: 700;
    margin-bottom: var(--v2-space-2);
}

/* peer sections laid out two per row inside a card, each in its own soft panel, e.g. one section per picked
   credit on the CME Vacations intercept page. markup:
   div.v2-section-grid > div.v2-section-grid-items > .v2-section (one per group).
   - a panel is a tinted area, not a card (like .v2-selection-summary), so it can sit inside a card: the page
     background color, a hairline border and rounded corners, with a gap between panels. white inputs stand out on it
   - a panel that is the only one showing takes the whole width; with two or more showing every panel is half
     the width (a third one alone on its row stays half); panels in the same row share a height
   - a panel with the hidden attribute drops out of the grid and the rest close up in order, so a page script can
     show and hide them without leaving holes
   - the outer div is the size container: under 720px the panels stack in one column (see the @container rule
     at the bottom)
   a section title can lead with a small icon tile (v2-icon-tile v2-icon-tile-sm); it gets a divider under it,
   so each panel reads like a small titled form */
.v2-section-grid {
    container-type: inline-size;
    margin-top: var(--v2-space-6);
}

.v2-section-grid-items {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--v2-space-5);
}

    .v2-section-grid-items > .v2-section {
        min-width: 0;
        margin-top: 0;
        padding: var(--v2-space-5);
        border: 1px solid var(--v2-border);
        border-radius: var(--v2-radius-md);
        background: var(--v2-page-bg);
    }

    /* the only panel showing (first and last of the ones that aren't hidden) takes the whole width;
       two or more showing go back to half width each */
    .v2-section-grid-items > .v2-section:nth-child(1 of :not([hidden])):nth-last-child(1 of :not([hidden])) {
        grid-column: 1 / -1;
    }

    .v2 .v2-section-grid-items > .v2-section > .v2-section-title {
        display: flex;
        align-items: center;
        gap: var(--v2-space-3);
        margin-bottom: 0;
        padding-bottom: var(--v2-space-4);
        border-bottom: 1px solid var(--v2-border);
    }

    /* the icon tile in the title sits on the white card color, so it doesn't melt into the tinted panel */
    .v2 .v2-section-grid-items > .v2-section > .v2-section-title > .v2-icon-tile {
        background: var(--v2-surface);
        border: 1px solid var(--v2-border);
    }


/* --------------------------------------------------------------------------
   Icon tile
   --------------------------------------------------------------------------
   a rounded square with an icon, for form rows and card headers. smaller and
   quieter than the icon circle, which is for action cards.
   -------------------------------------------------------------------------- */
.v2-icon-tile {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: var(--v2-radius-md);
    background: var(--v2-primary-subtle);
    color: var(--v2-primary-strong);
    font-size: 20px;
    line-height: 1;
}

.v2-icon-tile-lg {
    width: 56px;
    height: 56px;
    font-size: 26px;
}

/* solid brand tile, for the icon in a card header. the form rows under it use the
   light tinted tile, so a filled header icon keeps it from blending in with them */
.v2-icon-tile-solid {
    background: var(--v2-primary);
    color: var(--v2-on-primary);
}

/* Agent Ally tile (purple, see .v2-card-ai); white so it stands out on the purple card */
.v2-icon-tile-ai {
    background: var(--v2-surface);
    color: var(--v2-tone-purple);
}

/* small tile for a row in a list (e.g. one selected topic in .v2-selection-summary) */
.v2-icon-tile-sm {
    width: 32px;
    height: 32px;
    border-radius: var(--v2-radius-sm);
    font-size: 15px;
}

/* decorative tones on a tile, to tell two peer values apart at a glance (e.g. the trigger
   and recommended course groups of an upsell rule). same fixed colors as the icon circle tones */
.v2-icon-tile.v2-tone-blue {
    background: var(--v2-tone-blue-subtle);
    color: var(--v2-tone-blue);
}

.v2-icon-tile.v2-tone-navy {
    background: var(--v2-tone-navy-subtle);
    color: var(--v2-tone-navy);
}

.v2-icon-tile.v2-tone-green {
    background: var(--v2-tone-green-subtle);
    color: var(--v2-tone-green);
}

.v2-icon-tile.v2-tone-purple {
    background: var(--v2-tone-purple-subtle);
    color: var(--v2-tone-purple);
}

.v2-icon-tile.v2-tone-amber {
    background: var(--v2-tone-amber-subtle);
    color: var(--v2-tone-amber);
}

/* warning tile, for the header of a "before you go on" message (e.g. the PDF mismatch
   dialog). status colors, so it reads as a warning under every theme */
.v2-icon-tile-warning {
    background: var(--v2-warning-subtle);
    color: var(--v2-warning);
}

/* a file-type image (e.g. the pdf icon) on a white tile */
.v2-file-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 68px;
    height: 68px;
    border: 1px solid var(--v2-border);
    border-radius: var(--v2-radius-md);
    background: var(--v2-surface);
    box-shadow: var(--v2-shadow-sm);
}

    .v2-file-icon > img {
        width: 44px;
        height: 44px;
    }


/* --------------------------------------------------------------------------
   Context list
   --------------------------------------------------------------------------
   short "keep in mind" lines under the page header, each a plain sentence with
   its value in bold right after it on the same line (e.g. "The question is
   assigned with the topic: Finance\Financial Analysis" on Suggest Question). a
   long value wraps onto the next line. no box: it's read-only context that reads
   as part of the header, not something to act on.
   -------------------------------------------------------------------------- */
.v2-context-list {
    display: flex;
    flex-direction: column;
    gap: var(--v2-space-2);
    margin-bottom: var(--v2-space-4);
}

.v2-context-value {
    margin-left: var(--v2-space-1);
    color: var(--v2-text);
    font-weight: 700;
    overflow-wrap: anywhere;
}


/* --------------------------------------------------------------------------
   Selection summary
   --------------------------------------------------------------------------
   a read-only recap, inside a card, of what the user picked on an earlier step
   (e.g. the topics chosen on Step 1, shown on the PDF upload step so the admin
   knows which PDFs fit). a brand-tinted area rather than a card, so it can sit
   inside the step's card: a title + hint with the count on the right, then one
   row per value (small icon tile, parent path in bold, then the name). long
   lists show the first few rows; the rest carry the hidden attribute and a
   .v2-selection-summary-more pill button under the list shows them.
   -------------------------------------------------------------------------- */
.v2-selection-summary {
    margin-top: var(--v2-space-2);
    padding: var(--v2-space-4);
    border-radius: var(--v2-radius-md);
    background: var(--v2-primary-subtle);
}

.v2-selection-summary-header {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--v2-space-2) var(--v2-space-4);
    margin-bottom: var(--v2-space-2);
}

    .v2 .v2-selection-summary-header .v2-section-title {
        margin-bottom: 0;
    }

    .v2 .v2-selection-summary-header .v2-form-hint {
        margin-top: 0;
    }

/* "7 topics selected" on the right of the title */
.v2-selection-summary-count {
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
    white-space: nowrap;
}

.v2 .v2-selection-summary-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

    .v2-selection-summary-list > li {
        display: flex;
        align-items: center;
        gap: var(--v2-space-3);
        padding: var(--v2-space-2) 0;
        border-bottom: 1px solid var(--v2-primary-border);
    }

        .v2-selection-summary-list > li:last-child {
            border-bottom: 0;
        }

    /* the tinted tile would disappear on the tinted box, so it's white here */
    .v2-selection-summary-list .v2-icon-tile {
        background: var(--v2-surface);
    }

.v2-selection-summary-text {
    min-width: 0;
    overflow-wrap: anywhere;
}

/* the parent path ("Cardiology") in bold, then a quiet separator before the name */
.v2-selection-summary-parent {
    font-weight: 700;
}

    .v2-selection-summary-parent::after {
        content: " / ";
        color: var(--v2-text-muted);
        font-weight: 400;
    }

/* "+3 more selected topics": a small pill button under the list */
.v2 .v2-selection-summary-more {
    display: inline-flex;
    align-items: center;
    margin-top: var(--v2-space-2);
    padding: var(--v2-space-1) var(--v2-space-3);
    border: 1px solid var(--v2-primary-border);
    border-radius: var(--v2-radius-pill);
    background: var(--v2-surface);
    color: var(--v2-primary-strong);
    font: inherit;
    font-size: var(--v2-font-small);
    font-weight: 600;
    line-height: 1.4;
    cursor: pointer;
}

    .v2 .v2-selection-summary-more:hover {
        border-color: var(--v2-primary-strong);
    }

    .v2 .v2-selection-summary-more:focus-visible {
        outline: none;
        box-shadow: 0 0 0 3px var(--v2-focus-ring);
    }


/* --------------------------------------------------------------------------
   Centered heading
   --------------------------------------------------------------------------
   a title + subtitle centered above a centered row of choices (e.g. "Choose how
   you want to continue" above the option cards). uses the card title and card
   subtitle sizes, so it sits one step under the page title.
   -------------------------------------------------------------------------- */
.v2-center-heading {
    text-align: center;
}

    /* it starts a new section, so it gets a little extra room from the box above it */
    .v2-stack-compact .v2-center-heading {
        margin-top: var(--v2-space-3);
    }


/* --------------------------------------------------------------------------
   Option card
   --------------------------------------------------------------------------
   a small box that is one button (an asp:LinkButton or HyperLink with
   CssClass="v2-option-card"), for picking how to continue from a small set of
   peer options (e.g. Regenerate Question / Regenerate from Uploaded Article /
   Interactive Edit). an icon on the left, a title and one short line of text.
   put them in a .v2-option-list, which centers them in a row and wraps.
   outlined like a secondary button by default; add .v2-option-card-primary to
   the one main option (filled with the brand color). keep one primary per list.
   use an action card instead when a box needs more than one button.
   -------------------------------------------------------------------------- */
.v2-option-list {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--v2-space-4);
}

    .v2-option-list > .v2-option-card {
        flex: 0 1 320px;
    }

.v2 .v2-option-card,
.v2 a.v2-option-card:link,
.v2 a.v2-option-card:visited {
    display: flex;
    align-items: center;
    gap: var(--v2-space-4);
    min-height: 84px;
    padding: var(--v2-space-4) var(--v2-space-5);
    border: 1px solid var(--v2-border-strong);
    border-radius: var(--v2-radius-md);
    background: var(--v2-surface);
    color: var(--v2-primary-strong);
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}

    .v2 .v2-option-card:hover,
    .v2 a.v2-option-card:hover {
        border-color: var(--v2-primary-border);
        background: var(--v2-primary-subtle);
        color: var(--v2-primary-strong);
        text-decoration: none;
    }

    .v2 .v2-option-card:focus-visible {
        outline: none;
        box-shadow: 0 0 0 3px var(--v2-focus-ring);
    }

/* the one main option: filled like a primary button */
.v2 .v2-option-card.v2-option-card-primary,
.v2 a.v2-option-card.v2-option-card-primary:link,
.v2 a.v2-option-card.v2-option-card-primary:visited {
    border-color: var(--v2-primary);
    background: var(--v2-primary);
    box-shadow: var(--v2-shadow-button);
    color: var(--v2-on-primary);
}

    .v2 .v2-option-card.v2-option-card-primary:hover,
    .v2 a.v2-option-card.v2-option-card-primary:hover {
        border-color: var(--v2-primary-hover);
        background: var(--v2-primary-hover);
        color: var(--v2-on-primary);
    }

/* the icon takes the card's text color (brand-strong, or on-primary when filled) */
.v2-option-card-icon {
    flex: 0 0 auto;
    font-size: 28px;
    line-height: 1;
}

.v2-option-card-body {
    flex: 1 1 auto;
    min-width: 0;
}

.v2-option-card-title {
    display: block;
    font-size: var(--v2-font-page-subtitle);
    font-weight: 700;
    line-height: 1.3;
}

.v2-option-card-text {
    display: block;
    margin-top: var(--v2-space-1);
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
    line-height: 1.4;
}

    .v2-option-card-primary .v2-option-card-text {
        color: inherit;
        opacity: 0.9;
    }

/* disabled (Enabled="False" adds aspNetDisabled and removes the href): neutral
   grey like a disabled card, with the tooltip still showing on hover */
.v2 .v2-option-card.aspNetDisabled,
.v2 .v2-option-card.aspNetDisabled:hover {
    border-color: var(--v2-border);
    background: var(--v2-surface-disabled);
    box-shadow: none;
    color: var(--v2-text-subtle);
    cursor: not-allowed;
}

    .v2 .v2-option-card.aspNetDisabled .v2-option-card-text {
        color: var(--v2-text-subtle);
        opacity: 1;
    }


/* --------------------------------------------------------------------------
   Main + side layout
   --------------------------------------------------------------------------
   a wide main card with a narrow column of side cards. no breakpoints: the
   side column wraps under the main one when there isn't room for both (the
   main column needs ~560px, the side ~300px), so it follows the space the
   master page leaves, like .v2-grid does.
   -------------------------------------------------------------------------- */
.v2-layout-aside {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--v2-space-5);
}

/* the main card; it keeps the card's 16px padding, the same as the side cards,
   so the two columns line up */
.v2-layout-main {
    flex: 999 1 560px;
    min-width: 0;
}

.v2-layout-side {
    flex: 1 1 300px;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: var(--v2-space-5);
}

/* a wider side column (380px instead of 300px), for side cards with longer text such as the Best
   Practices tips next to the upsell rule form. the two columns still wrap when there's no room for both */
.v2-layout-side.v2-layout-side-wide {
    flex-basis: 380px;
}

/* a card across both columns, above them (e.g. How It Works over the new upsell rule form). put it
   first inside .v2-layout-aside; the layout's own gap spaces it from the columns */
.v2-layout-full {
    flex: 1 1 100%;
    min-width: 0;
}


/* --------------------------------------------------------------------------
   Form rows
   --------------------------------------------------------------------------
   one setting per row: icon tile, label + hint, then the control. rows are
   separated by thin lines. the form is a size container, so on a narrow card
   the control drops under the label (see the @container rule at the bottom).
   -------------------------------------------------------------------------- */
.v2-form {
    container-type: inline-size;
}

.v2-form-row {
    display: grid;
    grid-template-columns: 44px minmax(160px, 260px) minmax(0, 1fr);
    gap: var(--v2-space-3) var(--v2-space-4);
    align-items: start;
    padding: var(--v2-space-5) 0;
    border-bottom: 1px solid var(--v2-border);
}

    .v2-form-row:last-child {
        border-bottom: 0;
        padding-bottom: 0;
    }

/* a button row right after a form (e.g. Save under the questions on the AIA PA intercept page).
   the last form row has no bottom padding, so the row needs its own room from the last input.
   dialog footers keep their own spacing and divider */
.v2-form + .v2-actions:not(.v2-dialog-footer) {
    margin-top: var(--v2-space-6);
}

.v2-form-label {
    font-weight: 700;
    color: var(--v2-text);
}

.v2 .v2-form-hint {
    display: block;
    font-size: var(--v2-font-small);
    font-weight: 400;
    color: var(--v2-text-muted);
    margin-top: var(--v2-space-1);
}

/* the control column stacks the input, its notes and its validators */
.v2-form-control {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--v2-space-2);
    min-width: 0;
}

    /* notes, tree views and tables use the full width of the column (e.g. the learner search results in the
       grant credits dialog), instead of shrinking to their content */
    .v2-form-control > .v2-note,
    .v2-form-control > .RadTreeView,
    .v2-form-control > .v2-table-wrap {
        align-self: stretch;
    }

/* opt-in (add v2-form-validators-inline to the .v2-form): a validator message ("*Required", "Please remove the < and >
   characters") sits to the right of its input instead of on a new line under it, which saves height on a long form
   (e.g. the credit sections on the CME Vacations intercept page).
   - the input's flex-basis is 0, so it gives up room while a message shows and takes the whole width back once the
     message hides; a Display="Dynamic" validator that is valid is display:none, so it adds no gap
   - a message too long for the room that's left wraps under the input; max-width keeps it inside the column
   - fixed-width telerik pickers (RadDatePicker, RadMaskedTextBox) keep their width, the message just follows them */
.v2-form.v2-form-validators-inline .v2-form-control {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--v2-space-2) var(--v2-space-3);
}

    .v2-form.v2-form-validators-inline .v2-form-control > .v2-input {
        flex: 1 1 0;
        min-width: 128px;
    }

    .v2-form.v2-form-validators-inline .v2-form-control > .validatorErrorBox {
        flex: 0 0 auto;
        max-width: 100%;
    }

/* a row whose hint is too long for the label column (e.g. the rule name, status and priority on
   Add/Edit Upsell Rule): the icon, the label and the control share the first line, and the hint
   takes the whole line under them. put the .v2-form-hint after the .v2-form-control in the markup */
.v2-form-row.v2-form-row-hint-below {
    align-items: center;
}

    .v2 .v2-form-row-hint-below > .v2-form-hint {
        grid-column: 1 / -1;
        margin-top: 0;
    }

/* opt-in: a row whose label is one short question that shouldn't wrap (e.g. "Which credit types would you like to
   claim?" on the CME Vacations intercept page). the label column sizes to the label's text instead of stopping at
   260px, and the control takes the rest. pair it with v2-form-row-hint-below so a long hint gets the whole row.
   narrow forms still stack it like any other row (see the 620px / 400px @container rules at the bottom) */
.v2-form-row.v2-form-row-label-fit {
    grid-template-columns: 44px max-content minmax(0, 1fr);
}

    .v2-form-row-label-fit > .v2-form-label {
        white-space: nowrap;
    }

/* opt-in (add v2-form-row-stacked): the answer goes under the question instead of beside it, for a question with
   something to read before answering (e.g. the list of boards over Yes / No on the ASSH intercept page). the label
   and the control share the column next to the icon, the same way a narrow form stacks them */
.v2-form-row.v2-form-row-stacked {
    grid-template-columns: 44px minmax(0, 1fr);
}

    .v2-form-row-stacked > .v2-form-control {
        grid-column: 2 / -1;
    }

/* opt-in (add v2-form-label-center to the .v2-form): each label's first line sits on the middle of its icon tile
   instead of along the tile's top edge, so the title reads as belonging to its icon (e.g. the ASSH intercept page).
   a hint under the label still follows the title. 1lh is the label's own line height; a browser without the lh
   unit drops the rule and keeps the label on the top edge */
.v2-form.v2-form-label-center .v2-form-label {
    padding-top: calc((44px - 1lh) / 2);
}

/* small "i" next to a label that shows a tooltip */
.v2-help-icon {
    margin-left: var(--v2-space-1);
    color: var(--v2-text-subtle);
    font-weight: 400;
    cursor: help;
}

/* the asterisk after the label of a required field (e.g. Plan Name on the subscription plan forms). an
   asp:Label with Text="*", so the code-behind can hide it when a client makes the field optional */
.v2 .v2-required {
    margin-left: 2px;
    color: var(--v2-danger);
    font-weight: 700;
}

/* plain text input / textarea */
.v2 .v2-input {
    display: block;
    width: 100%;
    padding: var(--v2-space-2) var(--v2-space-3);
    border: 1px solid var(--v2-border-strong);
    border-radius: var(--v2-radius-sm);
    background: var(--v2-surface);
    color: var(--v2-text);
    font: inherit;
    line-height: 1.5;
}

    /* textareas grow with their text (where the browser supports field-sizing;
       elsewhere they keep their Rows height and scroll) */
    .v2 textarea.v2-input {
        field-sizing: content;
        min-height: 84px;
        resize: vertical;
    }

    .v2 .v2-input:focus {
        outline: none;
        border-color: var(--v2-primary-strong);
        box-shadow: 0 0 0 3px var(--v2-focus-ring);
    }

    /* a short value (a count, a date) doesn't need the column's full width */
    .v2 .v2-input.v2-input-short {
        max-width: 160px;
    }

    /* a medium value (a state, a license number): room for its longest usual form without stretching across a
       wide card (e.g. State and State License Number on the ASSH intercept page). it still shrinks with a narrow column */
    .v2 .v2-input.v2-input-medium {
        max-width: 280px;
    }

    /* a long value (a person's full name, an email address): wider than an ID, but not the whole column
       (e.g. Full Name on the ASSH intercept page) */
    .v2 .v2-input.v2-input-long {
        max-width: 440px;
    }

/* search box: a v2-input with a magnifying glass inside on the left. it takes the
   column's width up to 360px, so it lines up with the controls around it */
.v2-search {
    position: relative;
    align-self: stretch;
    max-width: 360px;
}

    .v2-search > i {
        position: absolute;
        top: 50%;
        left: var(--v2-space-3);
        transform: translateY(-50%);
        color: var(--v2-text-subtle);
        pointer-events: none;
    }

    .v2 .v2-search > .v2-input {
        padding-left: 36px;
    }

    /* a search box in a button row (e.g. next to its Search button) takes the free room,
       still up to its 360px */
    .v2-actions > .v2-search {
        flex: 1 1 240px;
    }

    /* a search box and its button inside a form control (e.g. the Find rows on Merge User Account): the form
       control lets its children shrink to fit, which squeezed this row until the button wrapped under the box.
       stretching the row to the control's width keeps the button right next to the search box (still up to
       its 360px). only rows holding a .v2-search stretch, so other button rows in a form keep their size */
    .v2-form-control > .v2-actions:has(> .v2-search) {
        align-self: stretch;
    }

/* a long list of checkboxes to pick from (asp:CheckBoxList with RepeatLayout="UnorderedList",
   e.g. the learning activities to add to a membership level): one per line between dividers,
   in a bordered box that scrolls past 360px. a .v2-search above it can filter the rows by
   setting their hidden attribute. a .v2-pick-list-hint in the item text is a quiet second line */
.v2 .v2-pick-list {
    max-height: 360px;
    margin: 0;
    padding: 0;
    border: 1px solid var(--v2-border);
    border-radius: var(--v2-radius-sm);
    list-style: none;
    overflow-y: auto;
}

    .v2 .v2-pick-list > li {
        display: flex;
        align-items: flex-start;
        gap: var(--v2-space-3);
        padding: var(--v2-space-3) var(--v2-space-4);
        border-bottom: 1px solid var(--v2-border);
    }

        .v2 .v2-pick-list > li:last-child {
            border-bottom: 0;
        }

    .v2 .v2-pick-list input[type="checkbox"] {
        flex: 0 0 auto;
        margin: 3px 0 0;
        accent-color: var(--v2-primary);
    }

    .v2 .v2-pick-list label {
        flex: 1 1 auto;
        margin: 0;
        font-weight: 400;
        cursor: pointer;
    }

.v2-pick-list-hint {
    display: block;
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
}

/* checkbox list (asp:CheckBoxList with RepeatLayout="UnorderedList"),
   laid out inline and wrapping */
.v2 .v2-choice-list {
    display: flex;
    flex-wrap: wrap;
    gap: var(--v2-space-3) var(--v2-space-6);
    margin: 0;
    padding: 0;
    list-style: none;
}

    .v2 .v2-choice-list li {
        display: inline-flex;
        align-items: center;
        gap: var(--v2-space-2);
    }

    .v2 .v2-choice-list input[type="checkbox"],
    .v2 .v2-choice-list input[type="radio"] {
        width: 18px;
        height: 18px;
        margin: 0;
        accent-color: var(--v2-primary);
        cursor: pointer;
    }

    /* bootstrap makes labels bold */
    .v2 .v2-choice-list label {
        margin: 0;
        font-weight: 400;
        cursor: pointer;
    }

    /* opt-in (add v2-choice-list-nowrap): each choice keeps its text on one line, and a choice that doesn't fit
       moves to the next line whole, so a name like "AMA PRA Category 1 Credit" never breaks in the middle.
       only for short choices: a label wider than the column would overflow it */
    .v2 .v2-choice-list.v2-choice-list-nowrap label {
        white-space: nowrap;
    }

    /* opt-in (add v2-choice-list-stacked): one choice per line, for choices that read best one under the other
       (e.g. the boards on the ASSH intercept page) */
    .v2 .v2-choice-list.v2-choice-list-stacked {
        flex-direction: column;
        align-items: flex-start;
        gap: var(--v2-space-3);
    }

    /* opt-in (add v2-choice-list-last-apart to a stacked list): the last choice sits apart under a thin line, for a
       "none of these" answer that can't be combined with the others (e.g. Other (Allied Health Professional) under the
       boards on the ASSH intercept page). the line runs the width of the list */
    .v2 .v2-choice-list.v2-choice-list-stacked.v2-choice-list-last-apart > li:last-child {
        align-self: stretch;
        margin-top: var(--v2-space-1);
        padding-top: var(--v2-space-3);
        border-top: 1px solid var(--v2-border);
    }

/* a short set of answers (e.g. Yes / No) drawn as one joined toggle, the same look as
   .v2-segmented, but for an asp:RadioButtonList with RepeatLayout="UnorderedList" and
   CssClass="v2-choice-toggle". the radios are only hidden visually, so the arrow keys, the
   focus ring, AutoPostBack and a RequiredFieldValidator still work, and nothing is picked
   until the user picks. use it instead of a .v2-switch when the answer is required and
   "not answered yet" has to stay possible (e.g. "Are you an AIA Member?") */
.v2 .v2-choice-toggle {
    display: inline-flex;
    flex-wrap: wrap;
    margin: 0;
    padding: 0;
    list-style: none;
}

    .v2 .v2-choice-toggle li {
        position: relative;
    }

    .v2 .v2-choice-toggle input[type="radio"] {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: 0;
        opacity: 0;
    }

    /* each answer is a button-like box; neighbours share a border, like .v2-segmented.
       bootstrap makes labels bold and adds a bottom margin */
    .v2 .v2-choice-toggle label {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 110px;
        min-height: 38px;
        margin: 0 0 0 -1px;
        padding: 0 var(--v2-space-5);
        border: 1px solid var(--v2-border-strong);
        background: var(--v2-surface);
        color: var(--v2-text);
        font-size: var(--v2-font-body);
        font-weight: 500;
        cursor: pointer;
        transition: background-color 0.15s ease, border-color 0.15s ease;
    }

    .v2 .v2-choice-toggle li:first-child label {
        margin-left: 0;
        border-radius: var(--v2-radius-sm) 0 0 var(--v2-radius-sm);
    }

    .v2 .v2-choice-toggle li:last-child label {
        border-radius: 0 var(--v2-radius-sm) var(--v2-radius-sm) 0;
    }

        .v2 .v2-choice-toggle label:hover {
            background: var(--v2-primary-subtle);
            color: var(--v2-primary-strong);
        }

    /* the picked answer; it sits above its neighbours so its border isn't hidden by theirs */
    .v2 .v2-choice-toggle input[type="radio"]:checked + label {
        position: relative;
        z-index: 1;
        background: var(--v2-primary);
        border-color: var(--v2-primary);
        color: var(--v2-on-primary);
        font-weight: 600;
    }

    .v2 .v2-choice-toggle input[type="radio"]:focus-visible + label {
        position: relative;
        z-index: 2;
        box-shadow: 0 0 0 3px var(--v2-focus-ring);
    }

    .v2 .v2-choice-toggle input[type="radio"]:disabled + label {
        opacity: 0.5;
        cursor: not-allowed;
    }

/* segmented control. wraps the __multiStateToggleButton user control, which
   renders a bootstrap btn-group of buttons with btn-primary (selected) or
   btn-default. this only restyles those buttons; the control is unchanged */
.v2 .v2-segmented .btn-group {
    display: flex;
    flex-wrap: wrap;
}

    .v2 .v2-segmented .btn-group > .btn {
        float: none;
        min-width: 110px;
        min-height: 38px;
        margin: 0 0 0 -1px;
        padding: 0 var(--v2-space-5);
        border: 1px solid var(--v2-border-strong);
        border-radius: 0;
        background: var(--v2-surface);
        color: var(--v2-text);
        font-size: var(--v2-font-body);
        font-weight: 500;
        box-shadow: none;
        text-shadow: none;
    }

        /* first / last button, ignoring the hidden inputs between them */
        .v2 .v2-segmented .btn-group > .btn:not(.btn ~ .btn) {
            margin-left: 0;
            border-radius: var(--v2-radius-sm) 0 0 var(--v2-radius-sm);
        }

        .v2 .v2-segmented .btn-group > .btn:not(:has(~ .btn)) {
            border-radius: 0 var(--v2-radius-sm) var(--v2-radius-sm) 0;
        }

        .v2 .v2-segmented .btn-group > .btn:not(.btn ~ .btn):not(:has(~ .btn)) {
            border-radius: var(--v2-radius-sm);
        }

        .v2 .v2-segmented .btn-group > .btn:hover {
            background: var(--v2-primary-subtle);
            color: var(--v2-primary-strong);
        }

        .v2 .v2-segmented .btn-group > .btn:focus-visible {
            outline: none;
            box-shadow: 0 0 0 3px var(--v2-focus-ring);
            position: relative;
            z-index: 2;
        }

        .v2 .v2-segmented .btn-group > .btn.btn-primary,
        .v2 .v2-segmented .btn-group > .btn.btn-primary:hover {
            position: relative;
            z-index: 1;
            background: var(--v2-primary);
            border-color: var(--v2-primary);
            color: var(--v2-on-primary);
            font-weight: 600;
        }

        .v2 .v2-segmented .btn-group > .btn[disabled],
        .v2 .v2-segmented .btn-group > .btn.aspNetDisabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

/* on/off switch. an asp:CheckBox with CssClass="v2-switch" renders a span around the
   checkbox and its label; the checkbox itself is drawn as the track and the knob, so the
   label click, keyboard focus and AutoPostBack all keep working as a plain checkbox */
.v2 .v2-switch {
    display: inline-flex;
    align-items: center;
    gap: var(--v2-space-3);
}

    .v2 .v2-switch input[type="checkbox"] {
        appearance: none;
        position: relative;
        flex: 0 0 auto;
        width: 44px;
        height: 24px;
        margin: 0;
        border-radius: var(--v2-radius-pill);
        background: var(--v2-border-strong);
        cursor: pointer;
        transition: background-color 0.15s ease;
    }

        .v2 .v2-switch input[type="checkbox"]::before {
            content: "";
            position: absolute;
            top: 3px;
            left: 3px;
            width: 18px;
            height: 18px;
            border-radius: 50%;
            background: var(--v2-surface);
            box-shadow: var(--v2-shadow-sm);
            transition: transform 0.15s ease;
        }

        .v2 .v2-switch input[type="checkbox"]:checked {
            background: var(--v2-primary);
        }

            .v2 .v2-switch input[type="checkbox"]:checked::before {
                transform: translateX(20px);
            }

        .v2 .v2-switch input[type="checkbox"]:focus-visible {
            outline: none;
            box-shadow: 0 0 0 3px var(--v2-focus-ring);
        }

    /* bootstrap makes labels bold and gives them a bottom margin */
    .v2 .v2-switch label {
        margin: 0;
        font-weight: 600;
        cursor: pointer;
    }

    /* Enabled="False" disables the checkbox; the "why" goes in a hint next to it, since a
       disabled input shows no tooltip */
    .v2 .v2-switch input[type="checkbox"]:disabled,
    .v2 .v2-switch input[type="checkbox"]:disabled + label {
        opacity: 0.5;
        cursor: not-allowed;
    }

/* the one value picked in a picker (e.g. the course group chosen for an upsell rule): a
   bordered row with a small icon tile, the name, a count pill and an icon-only remove button */
.v2-selected-item {
    display: inline-flex;
    align-items: center;
    gap: var(--v2-space-3);
    max-width: 100%;
    padding: var(--v2-space-2) var(--v2-space-2) var(--v2-space-2) var(--v2-space-3);
    border: 1px solid var(--v2-border);
    border-radius: var(--v2-radius-md);
    background: var(--v2-surface);
}

.v2-selected-item-name {
    min-width: 0;
    font-weight: 600;
    overflow-wrap: anywhere;
}

.v2 .v2-selected-item-remove,
.v2 a.v2-selected-item-remove:link,
.v2 a.v2-selected-item-remove:visited {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: var(--v2-radius-sm);
    color: var(--v2-text-subtle);
    font-size: 16px;
    text-decoration: none;
}

    .v2 .v2-selected-item-remove:hover,
    .v2 a.v2-selected-item-remove:hover {
        background: var(--v2-surface-muted);
        color: var(--v2-text);
    }

    .v2 .v2-selected-item-remove:focus-visible {
        outline: none;
        box-shadow: 0 0 0 3px var(--v2-focus-ring);
    }

/* rating scale (e.g. accuracy 1 to 5 on Rate AI Question). an asp:RadioButtonList with
   RepeatLayout="UnorderedList" and CssClass="v2-rating-scale": each value is a box, and
   the picked one fills with the brand color. the radio itself is only hidden
   visually, so the arrow keys, the focus ring and a RequiredFieldValidator still work.
   put it in a .v2-rating with a .v2-rating-legend after it, so the legend's two ends
   ("Poor" / "Excellent") sit under the first and last box whatever the scale's length.
   the scale fills the width of its column: every box widens by the same amount (like
   horizontal padding around each number) and they stay 16px apart, so they read as one
   row instead of five boxes spread far apart. that also leaves room for longer end labels
   (e.g. "Obvious AI" / "Indistinguishable from Human"). a box never gets narrower than
   44px, so a 1 to 5 scale still fits a phone-width dialog */
.v2-rating {
    display: flex;
    flex-direction: column;
    gap: var(--v2-space-1);
    width: 100%;
}

.v2 .v2-rating-scale {
    display: flex;
    gap: var(--v2-space-4);
    margin: 0;
    padding: 0;
    list-style: none;
}

    /* each value takes an equal share of the row's width */
    .v2 .v2-rating-scale li {
        position: relative;
        flex: 1 1 0;
        min-width: 44px;
    }

    .v2 .v2-rating-scale input[type="radio"] {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: 0;
        opacity: 0;
    }

    /* the box; bootstrap makes labels bold and adds a bottom margin */
    .v2 .v2-rating-scale label {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 100%;
        min-width: 44px;
        height: 44px;
        margin: 0;
        border: 1px solid var(--v2-border-strong);
        border-radius: var(--v2-radius-sm);
        background: var(--v2-surface);
        color: var(--v2-text);
        font-size: var(--v2-font-body);
        font-weight: 600;
        cursor: pointer;
        transition: background-color 0.15s ease, border-color 0.15s ease;
    }

        .v2 .v2-rating-scale label:hover {
            background: var(--v2-primary-subtle);
            border-color: var(--v2-primary-border);
            color: var(--v2-primary-strong);
        }

    .v2 .v2-rating-scale input[type="radio"]:checked + label {
        background: var(--v2-primary);
        border-color: var(--v2-primary);
        color: var(--v2-on-primary);
    }

    .v2 .v2-rating-scale input[type="radio"]:focus-visible + label {
        box-shadow: 0 0 0 3px var(--v2-focus-ring);
    }

    .v2 .v2-rating-scale input[type="radio"]:disabled + label {
        opacity: 0.5;
        cursor: not-allowed;
    }

/* "Poor" under the first box, "Excellent" under the last. the scale fills the same width,
   so each end label starts or ends exactly where its box does */
.v2-rating-legend {
    display: flex;
    justify-content: space-between;
    gap: var(--v2-space-3);
    font-size: var(--v2-font-small);
    color: var(--v2-text-muted);
}

    /* if an end label is long enough to wrap, it stays lined up with its own end of the scale */
    .v2-rating-legend > :last-child {
        text-align: right;
    }


/* --------------------------------------------------------------------------
   Notes, tips and alerts
   -------------------------------------------------------------------------- */

/* a quiet explanation box under a control (e.g. what a choice means) */
.v2-note {
    padding: var(--v2-space-3) var(--v2-space-4);
    border-radius: var(--v2-radius-sm);
    background: var(--v2-surface-muted);
    border: 1px solid var(--v2-border);
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
    line-height: 1.6;
}

    .v2 .v2-note strong {
        color: var(--v2-text);
    }

    .v2 .v2-note ul {
        margin: var(--v2-space-1) 0 0;
        padding-left: var(--v2-space-5);
    }

/* a note at body size, for longer text the user actually reads (e.g. an uploaded
   article's summary) rather than a quiet explanation under a control */
.v2-note.v2-note-regular {
    font-size: var(--v2-font-body);
}

/* adds a brand-colored bar on the left, for guidelines worth reading */
.v2-note-accent {
    border-left: 3px solid var(--v2-primary);
}

/* an info icon followed by a short tip. no box of its own, so it can sit inside
   a card or a drop zone; it gets a divider line above it when it follows text */
.v2 .v2-tip {
    display: flex;
    align-items: flex-start;
    gap: var(--v2-space-3);
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
    line-height: 1.6;
    text-align: left;
}

    .v2 .v2-tip > i {
        flex: 0 0 auto;
        margin-top: 2px;
        color: var(--v2-primary-strong);
        font-size: 17px;
    }

    .v2 .v2-card-text + .v2-tip {
        padding-top: var(--v2-space-4);
        border-top: 1px solid var(--v2-primary-border);
    }

/* the tip as a full-width tinted strip (e.g. "this step is optional" at the
   bottom of a drop zone). purple, as it's about the Agent Ally step */
.v2 .v2-tip-banner {
    align-self: stretch;
    justify-content: center;
    align-items: center;
    padding: var(--v2-space-3) var(--v2-space-4);
    border-radius: var(--v2-radius-sm);
    background: var(--v2-tone-purple-subtle);
    color: var(--v2-text);
    font-size: var(--v2-font-body);
}

    .v2 .v2-tip-banner > i {
        margin-top: 0;
        color: var(--v2-tone-purple);
    }

/* page-level message above the content. the code-behind can switch the
   variant (e.g. info vs danger) by setting CssClass */
.v2-alert {
    display: flex;
    align-items: flex-start;
    gap: var(--v2-space-3);
    margin-bottom: var(--v2-space-5);
    padding: var(--v2-space-3) var(--v2-space-4);
    border: 1px solid transparent;
    border-radius: var(--v2-radius-sm);
    line-height: 1.5;
}

    .v2-alert > i {
        flex: 0 0 auto;
        margin-top: 3px;
    }

    /* an alert placed between cards in a .v2-stack (e.g. "No duplicate courses found" on Merge User Account):
       the stack's gap already spaces it, so its own bottom margin would double the room under it */
    .v2-stack > .v2-alert {
        margin-bottom: 0;
    }

.v2-alert-info {
    background: var(--v2-info-subtle);
    border-color: color-mix(in srgb, var(--v2-info) 25%, white);
    color: var(--v2-info);
}

.v2-alert-success {
    background: var(--v2-success-subtle);
    border-color: color-mix(in srgb, var(--v2-success) 25%, white);
    color: var(--v2-success);
}

.v2-alert-warning {
    background: var(--v2-warning-subtle);
    border-color: color-mix(in srgb, var(--v2-warning) 25%, white);
    color: var(--v2-warning);
}

.v2-alert-danger {
    background: var(--v2-danger-subtle);
    border-color: color-mix(in srgb, var(--v2-danger) 25%, white);
    color: var(--v2-danger);
}

/* several alerts listed together (e.g. the validation findings on an upsell rule):
   closer to each other than page-level alerts, and no gap after the last one */
.v2-alert-list > .v2-alert {
    margin-bottom: var(--v2-space-2);
}

    .v2-alert-list > .v2-alert:last-child {
        margin-bottom: 0;
    }

/* a quiet detail line inside an alert (e.g. the course names behind a finding) */
.v2 .v2-alert-detail {
    display: block;
    margin-top: var(--v2-space-1);
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
}

/* an alert with a button on the right (e.g. the Validation line and its Review button at the top of
   Edit Upsell Rule): the text takes the free room, and the button drops under it on a narrow page */
.v2-alert.v2-alert-with-action {
    flex-wrap: wrap;
    align-items: center;
}

    .v2-alert.v2-alert-with-action > span {
        flex: 1 1 240px;
        min-width: 0;
    }

    .v2-alert.v2-alert-with-action > i {
        margin-top: 0;
    }


/* --------------------------------------------------------------------------
   Lists
   -------------------------------------------------------------------------- */

/* guideline list with a check icon per line */
.v2 .v2-check-list {
    display: flex;
    flex-direction: column;
    gap: var(--v2-space-3);
    margin: 0;
    padding: 0;
    list-style: none;
}

    .v2 .v2-check-list li {
        display: flex;
        align-items: flex-start;
        gap: var(--v2-space-3);
        line-height: 1.5;
    }

        .v2 .v2-check-list li > i {
            flex: 0 0 auto;
            margin-top: 3px;
            color: var(--v2-text);
            font-size: 17px;
        }

/* checks that passed (e.g. on Validate Upsell Rule): one per line between dividers, each with a
   success-colored <i class="fa-regular fa-check"> */
.v2 .v2-check-list.v2-check-list-passed {
    gap: 0;
    border-top: 1px solid var(--v2-border);
}

    .v2 .v2-check-list.v2-check-list-passed li {
        padding: var(--v2-space-3) 0;
        border-bottom: 1px solid var(--v2-border);
    }

        .v2 .v2-check-list.v2-check-list-passed li > i {
            color: var(--v2-success);
        }

/* a few numbers side by side (label above, value below). they stay on one row
   and the labels wrap instead, so the dividers between them always line up */
.v2-stat-list {
    display: flex;
    gap: var(--v2-space-4);
}

.v2-stat {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    flex: 1 1 0;
    min-width: 0;
}

    /* a thin divider between side-by-side numbers */
    .v2-stat + .v2-stat {
        padding-left: var(--v2-space-4);
        border-left: 1px solid var(--v2-border-strong);
    }

.v2-stat-label {
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
}

.v2-stat-value {
    color: var(--v2-text);
    font-size: var(--v2-font-card-title);
    font-weight: 700;
    line-height: 1.2;
}

/* repeated records (e.g. one per question), separated by lines */
.v2-item-list {
    margin-top: var(--v2-space-2);
}

.v2-item {
    padding: var(--v2-space-4) 0;
    border-bottom: 1px solid var(--v2-border);
}

.v2-item-title {
    display: block;
    margin-bottom: var(--v2-space-2);
    font-weight: 700;
}

/* label / value pairs, two columns. the value column can hold a control */
.v2-field-grid {
    display: grid;
    grid-template-columns: minmax(100px, 160px) minmax(0, 1fr);
    gap: var(--v2-space-2) var(--v2-space-4);
    align-items: baseline;
}

    /* a long unbroken value (e.g. a purchase link) wraps inside its column instead of widening the card */
    .v2-field-grid > * {
        min-width: 0;
        overflow-wrap: anywhere;
    }

/* label, optionally with a small icon in front */
.v2-field-label {
    display: inline-flex;
    align-items: baseline;
    gap: var(--v2-space-2);
    color: var(--v2-text-muted);
    font-weight: 600;
}

    .v2-field-label > i {
        width: 16px;
        color: var(--v2-text);
        text-align: center;
    }

/* a small action on the right, just above a field's value (e.g. "View prompt" over the teaching
   point box). put it first in the value cell: the grid lines labels up with the cell's first
   line, so the label then sits level with the link and the box starts right under both */
.v2-field-value-action {
    display: flex;
    justify-content: flex-end;
    margin-bottom: var(--v2-space-2);
}

    .v2-field-value-action .v2-link {
        font-size: var(--v2-font-small);
    }

/* a simple data table. the wrapper scrolls sideways on phones so the page doesn't */
.v2-table-wrap {
    overflow-x: auto;
}

.v2 .v2-table {
    width: 100%;
    border-collapse: collapse;
}

    .v2 .v2-table th,
    .v2 .v2-table td {
        padding: var(--v2-space-3);
        border-bottom: 1px solid var(--v2-border);
        text-align: left;
        vertical-align: top;
    }

    .v2 .v2-table th {
        color: var(--v2-text-muted);
        font-size: var(--v2-font-small);
        font-weight: 600;
        background: var(--v2-surface-muted);
    }

    .v2 .v2-table td {
        vertical-align: middle;
    }

    .v2 .v2-table tbody tr:last-child td {
        border-bottom: 0;
    }

/* adds a rounded border around the table */
.v2-table-bordered {
    border: 1px solid var(--v2-border);
    border-radius: var(--v2-radius-md);
}

/* caps a list that can grow without limit (e.g. a faculty's assigned users) at about eight rows and
   scrolls it inside its own box, so it can't stretch the page or popup. the header row stays on top */
.v2-table-wrap.v2-table-scroll {
    max-height: 480px;
    overflow-y: auto;
}

    .v2 .v2-table-scroll .v2-table th {
        position: sticky;
        top: 0;
        z-index: 1;
    }

/* paging under a list: "Showing 11–20 of 305 users" on the left, Previous / Next on the right.
   the summary and the buttons wrap onto two lines in a narrow card */
.v2-pager {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--v2-space-3);
    margin-top: var(--v2-space-3);
}

    .v2-pager-summary {
        color: var(--v2-text-muted);
        font-size: var(--v2-font-small);
    }

/* file-type icon + file name in a table cell */
.v2-file-name {
    display: inline-flex;
    align-items: center;
    gap: var(--v2-space-3);
    word-break: break-word;
}

    .v2-file-name > img {
        flex: 0 0 auto;
        width: 24px;
        height: 24px;
    }

/* two field grids in a row are two groups of fields (e.g. settings, then dates);
   a divider between them keeps the groups apart without another box */
.v2-field-grid + .v2-field-grid {
    margin-top: var(--v2-space-4);
    padding-top: var(--v2-space-4);
    border-top: 1px solid var(--v2-border);
}

/* a stacked list of values, each in a tinted box (e.g. a task's target topics) */
.v2-value-list {
    display: flex;
    flex-direction: column;
    gap: var(--v2-space-2);
}

    .v2-value-list > .v2-value-box {
        background: var(--v2-primary-subtle);
        border-color: var(--v2-primary-border);
        color: var(--v2-text);
        font-size: var(--v2-font-body);
    }

/* a value box with a short figure on the right (e.g. a target topic and how many
   questions it gets). a long value wraps; the figure keeps its own line width */
.v2-value-box.v2-value-box-split {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--v2-space-3);
}

.v2-value-box-count {
    flex-shrink: 0;
    color: var(--v2-primary-strong);
    font-size: var(--v2-font-small);
    font-weight: 600;
    white-space: nowrap;
}

/* "A to B" summary (e.g. an upsell rule's trigger courses to its recommended courses): two
   values, each an icon circle over a figure and a label, with an arrow between them */
.v2-flow {
    display: flex;
    align-items: center;
    gap: var(--v2-space-3);
    padding: var(--v2-space-4) var(--v2-space-3);
    border-radius: var(--v2-radius-sm);
    background: var(--v2-surface-muted);
}

.v2-flow-item {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--v2-space-1);
    text-align: center;
}

    .v2-flow-item > .v2-icon-circle {
        margin-bottom: var(--v2-space-1);
    }

.v2-flow-value {
    color: var(--v2-text);
    font-weight: 700;
}

.v2-flow-label {
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
    overflow-wrap: anywhere;
}

.v2-flow-arrow {
    flex: 0 0 auto;
    color: var(--v2-text-subtle);
    font-size: 20px;
}

/* the fields under a flow summary in the same card (e.g. an upsell rule's priority and message) */
.v2-flow + .v2-field-grid {
    margin-top: var(--v2-space-5);
}

/* a wrapping row of pills (e.g. a task's target Bloom levels) */
.v2-pill-list {
    display: flex;
    flex-wrap: wrap;
    gap: var(--v2-space-2);
}

/* the first block after a card header gets a little room from the header's divider */
.v2-card-header + :is(.v2-field-grid, .v2-value-list, .v2-pill-list) {
    margin-top: var(--v2-space-4);
}

/* a summary box: small icon circle + label + one big value (e.g. "Completed 3").
   put a few of them in a .v2-grid.v2-grid-fit. the value uses .v2-stat-value, so it
   stays at card-title size like the other numbers inside cards */
.v2-stat-card {
    display: flex;
    align-items: center;
    gap: var(--v2-space-4);
}

    .v2-stat-card > div {
        display: flex;
        flex-direction: column;
        min-width: 0;
    }

    /* a summary box has room for a body-size label; the small one in .v2-stat-list
       stays small because several of those sit side by side inside a card */
    .v2-stat-card .v2-stat-label {
        font-size: var(--v2-font-body);
    }

/* a summary box that reports a state (e.g. the error, warning and passed counts on Validate Upsell
   Rule): the figure comes first, at figure size and in the state's status color, with the label
   under it. add is-danger, is-warning or is-success to the card, and give its icon circle the
   matching .v2-icon-circle-danger / -warning / -success */
.v2 .v2-stat-card-status .v2-stat-value {
    font-size: var(--v2-font-figure);
    line-height: 1.1;
}

.v2 .v2-stat-card-status.is-danger .v2-stat-value {
    color: var(--v2-danger);
}

.v2 .v2-stat-card-status.is-warning .v2-stat-value {
    color: var(--v2-warning-figure);
}

.v2 .v2-stat-card-status.is-success .v2-stat-value {
    color: var(--v2-success);
}


/* --------------------------------------------------------------------------
   Tabs (RadTabStrip)
   --------------------------------------------------------------------------
   put CssClass="v2-tabs" on a telerik:RadTabStrip (drop factor360TabStrip: the
   theme files restyle that class with !important). it sits in a .v2-tab-bar with
   the page's tab-level actions (e.g. Refresh) on the right, and the bar's bottom
   line is the edge the tabs stand on. the lightweight renderer puts rtsSelected
   on the li in some versions and on the link in others, so both are covered.
   -------------------------------------------------------------------------- */
.v2-tab-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--v2-space-3);
    margin-bottom: var(--v2-space-5);
    border-bottom: 1px solid var(--v2-border);
}

    /* the actions keep off the line the tabs stand on */
    .v2-tab-bar > .v2-actions {
        margin-bottom: var(--v2-space-2);
    }

    /* with no tabs to show (e.g. a client that sells only one kind of subscription plan, whose tab strip
       is hidden) the bar is just the list's filters, still on the right */
    .v2-tab-bar > .v2-actions:only-child {
        margin-left: auto;
    }

/* space above the tab bar when a grid of summary boxes comes before it */
.v2-grid + .v2-tab-bar {
    margin-top: var(--v2-space-6);
}

.v2 .v2-tabs.RadTabStrip {
    font: inherit;
    min-width: 0;
}

    .v2 .v2-tabs.RadTabStrip .rtsLevel1 {
        border: 0;
        background: none;
    }

    .v2 .v2-tabs.RadTabStrip .rtsUL {
        display: flex;
        flex-wrap: wrap;
        gap: var(--v2-space-1);
        margin: 0;
        padding: 0;
        list-style: none;
    }

    .v2 .v2-tabs.RadTabStrip .rtsLevel1 .rtsLI {
        float: none;
        margin: 0;
        padding: 0;
        border: 0;
        background: none;
    }

    .v2 .v2-tabs.RadTabStrip .rtsLevel1 .rtsLink {
        display: inline-flex;
        align-items: center;
        gap: var(--v2-space-2);
        min-height: 44px;
        margin: 0 0 -1px;
        padding: 0 var(--v2-space-5);
        border: 1px solid transparent;
        border-radius: var(--v2-radius-md) var(--v2-radius-md) 0 0;
        background: none;
        box-shadow: none;
        color: var(--v2-text-muted);
        font-size: var(--v2-font-body);
        font-weight: 600;
        line-height: 1;
        text-decoration: none;
    }

        .v2 .v2-tabs.RadTabStrip .rtsLevel1 .rtsLink:hover,
        .v2 .v2-tabs.RadTabStrip .rtsLevel1 .rtsHovered .rtsLink {
            background: var(--v2-surface-muted);
            color: var(--v2-text);
        }

        .v2 .v2-tabs.RadTabStrip .rtsLevel1 .rtsLink:focus-visible {
            outline: none;
            box-shadow: 0 0 0 3px var(--v2-focus-ring);
        }

    /* the selected tab: white, joined to the content below, with a brand bar on top */
    .v2 .v2-tabs.RadTabStrip .rtsLevel1 .rtsSelected .rtsLink,
    .v2 .v2-tabs.RadTabStrip .rtsLevel1 .rtsLink.rtsSelected,
    .v2 .v2-tabs.RadTabStrip .rtsLevel1 .rtsSelected .rtsLink:hover,
    .v2 .v2-tabs.RadTabStrip .rtsLevel1 .rtsLink.rtsSelected:hover {
        border-color: var(--v2-border);
        border-bottom-color: var(--v2-surface);
        background: var(--v2-surface);
        box-shadow: inset 0 3px 0 var(--v2-primary);
        color: var(--v2-primary-strong);
    }

    .v2 .v2-tabs.RadTabStrip .rtsLink i {
        font-size: 16px;
    }


/* --------------------------------------------------------------------------
   Record list
   --------------------------------------------------------------------------
   repeated records that read like table rows, each in its own box, under one
   header row (e.g. one box per generated question). cells get their width
   from a size class (sm / md / lg) and one cell grows, so the header and every
   box line up without a table. under 960px of list width the header hides and
   each cell shows its own .v2-record-cell-label above the value.

   <div class="v2-record-list">
       <div class="v2-record-head">
           <span class="v2-record-cell v2-record-cell-sm">ID</span>
           <span class="v2-record-cell v2-record-cell-grow">Topic</span>
       </div>
       <div class="v2-card">
           <div class="v2-record-row">
               <div class="v2-record-cell v2-record-cell-sm">
                   <span class="v2-record-cell-label">ID</span> ...value...
               </div>
               ...
           </div>
           <div class="v2-record-note">...optional strip under the row...</div>
       </div>
   </div>
   -------------------------------------------------------------------------- */
.v2-record-list {
    container-type: inline-size;
    display: flex;
    flex-direction: column;
    gap: var(--v2-space-3);
}

/* the header row, lined up with the cells inside the boxes (box padding + border) */
.v2-record-head,
.v2-record-row {
    display: flex;
    align-items: center;
    gap: var(--v2-space-3);
}

.v2-record-head {
    padding: var(--v2-space-2) calc(var(--v2-space-4) + 1px);
    border-radius: var(--v2-radius-sm);
    background: var(--v2-surface-muted);
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
    font-weight: 600;
}

.v2-record-cell {
    flex: 0 0 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}

.v2-record-cell-sm {
    width: 100px;
}

.v2-record-cell-md {
    width: 120px;
}

.v2-record-cell-lg {
    width: 150px;
}

.v2-record-cell-grow {
    flex: 1 1 200px;
}

/* two links side by side in a cell (e.g. the question ID and its details icon) */
.v2-record-cell .v2-link + .v2-link {
    margin-left: var(--v2-space-2);
}

/* action links stacked one per line in a cell (e.g. View Prompt over View Reference),
   so a row with a missing action doesn't leave a gap in the middle of the cell */
.v2-record-cell-actions {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--v2-space-1);
}

    /* stacked, so the side-by-side spacing above doesn't apply */
    .v2-record-cell-actions .v2-link + .v2-link {
        margin-left: 0;
    }

/* the cell's own label, only shown once the header row is hidden (narrow list) */
.v2-record-cell-label {
    display: none;
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
    font-weight: 600;
}

/* a strip under the row with an icon, a small title and text (e.g. the teaching point) */
.v2-record-note {
    display: flex;
    align-items: flex-start;
    gap: var(--v2-space-3);
    margin-top: var(--v2-space-3);
    padding: var(--v2-space-3) var(--v2-space-4);
    border-radius: var(--v2-radius-sm);
    background: var(--v2-primary-subtle);
    color: var(--v2-text);
    font-size: var(--v2-font-small);
    line-height: 1.6;
}

    .v2-record-note > i {
        flex: 0 0 auto;
        margin-top: 2px;
        color: var(--v2-primary-strong);
        font-size: 17px;
    }

.v2-record-note-title {
    display: block;
    font-weight: 700;
}


/* --------------------------------------------------------------------------
   Collapsible card and timeline
   --------------------------------------------------------------------------
   <details class="v2-card v2-card-compact v2-collapse" open> is a card whose
   <summary class="v2-collapse-header"> toggles the rest of it; native details,
   so it works without script and from the keyboard. a link inside the summary
   still navigates instead of toggling. .v2-timeline is a dated list of events
   joined by a line, newest first.
   -------------------------------------------------------------------------- */
.v2-collapse-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--v2-space-3);
    cursor: pointer;
    list-style: none;
}

    /* hide the browser's own disclosure triangle; the chevron replaces it */
    .v2-collapse-header::-webkit-details-marker {
        display: none;
    }

    .v2-collapse-header:focus-visible {
        outline: none;
        border-radius: var(--v2-radius-sm);
        box-shadow: 0 0 0 3px var(--v2-focus-ring);
    }

.v2 .v2-collapse-title {
    font-size: var(--v2-font-page-subtitle);
    font-weight: 700;
}

/* the chevron sits at the far right and points up while the card is open */
.v2-collapse-icon {
    margin-left: auto;
    color: var(--v2-primary-strong);
    transition: transform 0.15s ease;
}

.v2-collapse[open] .v2-collapse-icon {
    transform: rotate(180deg);
}

/* a long block of generated text (e.g. an AI prompt): long words and urls wrap
   instead of widening the card. as the body of a .v2-collapse it gets the same
   divider under the header as the timeline does */
.v2-long-text {
    line-height: 1.6;
    overflow-wrap: anywhere;
}

.v2-collapse-header + .v2-long-text {
    margin-top: var(--v2-space-4);
    padding-top: var(--v2-space-4);
    border-top: 1px solid var(--v2-border);
}

/* a collapsible group with its own list (e.g. one membership level on Membership Mapping). the header holds
   an icon circle, a .v2-icon-text-body (title + subtitle) and a .v2-collapse-aside on the right: a quiet
   count, the chevron and the group's own button, which acts instead of toggling. the aside wraps under the
   title on a narrow card. the list goes in a .v2-collapse-body under the same divider as the timeline */
.v2-collapse-header > .v2-icon-text-body {
    flex: 1 1 240px;
}

.v2-collapse-aside {
    display: flex;
    align-items: center;
    gap: var(--v2-space-4);
    margin-left: auto;
}

    /* the aside already sits on the right, so the chevron no longer pushes itself there */
    .v2-collapse-aside > .v2-collapse-icon {
        margin-left: 0;
    }

.v2-collapse-count {
    color: var(--v2-text-muted);
    white-space: nowrap;
}

/* a badge after the group's title (e.g. "Default level") */
.v2 .v2-collapse-title .v2-badge {
    margin-left: var(--v2-space-2);
    vertical-align: middle;
}

.v2-collapse-body {
    display: flex;
    flex-direction: column;
    gap: var(--v2-space-3);
    margin-top: var(--v2-space-4);
    padding-top: var(--v2-space-4);
    border-top: 1px solid var(--v2-border);
}

    /* the group's search box runs the full width of its list */
    .v2-collapse-body > .v2-search {
        max-width: none;
    }

.v2 .v2-timeline {
    margin: var(--v2-space-4) 0 0;
    padding: var(--v2-space-4) 0 0;
    border-top: 1px solid var(--v2-border);
    list-style: none;
}

/* date column + text column, with a dot on the line to the left */
.v2-timeline-item {
    position: relative;
    display: grid;
    grid-template-columns: 170px minmax(0, 1fr);
    gap: var(--v2-space-1) var(--v2-space-4);
    padding: 0 0 var(--v2-space-3) var(--v2-space-6);
}

    /* the line joining the dots; it stops at the last event */
    .v2-timeline-item::before {
        content: "";
        position: absolute;
        top: 12px;
        bottom: -4px;
        left: 5px;
        width: 2px;
        background: var(--v2-border);
    }

    .v2-timeline-item:last-child::before {
        display: none;
    }

    .v2-timeline-item::after {
        content: "";
        position: absolute;
        top: 6px;
        left: 1px;
        width: 10px;
        height: 10px;
        border-radius: 50%;
        background: var(--v2-primary);
    }

.v2-timeline-date {
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
    line-height: 1.8;
    white-space: nowrap;
}

.v2-timeline-text {
    min-width: 0;
    overflow-wrap: anywhere;
}


/* --------------------------------------------------------------------------
   Drop zone
   --------------------------------------------------------------------------
   a dashed area files can be dropped on (RadAsyncUpload's DropZones points at
   it). the upload control itself keeps its telerik skin.
   -------------------------------------------------------------------------- */
.v2-dropzone {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--v2-space-4);
    margin-top: var(--v2-space-5);
    padding: var(--v2-space-7) var(--v2-space-5) var(--v2-space-5);
    border: 2px dashed var(--v2-primary-border);
    border-radius: var(--v2-radius-md);
    background: var(--v2-surface);
    text-align: center;
}

/* the prompt text block: optional empty-state title, the drag-and-drop line, "or" */
.v2-dropzone-text {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--v2-space-1);
}

/* kept under the card header title (20px), since the drop zone sits inside that card */
.v2-dropzone-title {
    font-size: var(--v2-font-page-subtitle);
    font-weight: 700;
}

/* under the empty-state title the drag-and-drop line is a muted sub-line; once
   files exist (no title) it becomes the bold lead line itself */
.v2-dropzone-lead {
    color: var(--v2-text-muted);
    font-size: var(--v2-font-body);
}

    .v2-dropzone-text:not(:has(.v2-dropzone-empty)) > .v2-dropzone-lead {
        color: var(--v2-text);
        font-size: var(--v2-font-page-subtitle);
        font-weight: 700;
    }

/* the long "what's supported" text under the buttons, with a divider below it */
.v2-dropzone-hint {
    align-self: stretch;
    max-width: 640px;
    margin: 0 auto;
    padding-bottom: var(--v2-space-4);
    border-bottom: 1px solid var(--v2-border);
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
    line-height: 1.7;
}

/* compact drop zone: one row instead of the tall centered block, for a single file
   picked right next to the action that uses it (e.g. Upload PDF + Generate Question
   on Suggest Question), so that action stays in view. small pdf tile, the drop text
   (bold lead + one hint line) on the left, the buttons on the right. it wraps on
   narrow cards, and the picked file is listed under the buttons as usual */
.v2-dropzone.v2-dropzone-compact {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--v2-space-3) var(--v2-space-4);
    padding: var(--v2-space-4);
    text-align: left;
}

    .v2-dropzone-compact > .v2-file-icon {
        width: 52px;
        height: 52px;
    }

        .v2-dropzone-compact > .v2-file-icon > img {
            width: 32px;
            height: 32px;
        }

    .v2-dropzone-compact > .v2-dropzone-text {
        flex: 1 1 260px;
        align-items: flex-start;
        min-width: 0;
    }

    .v2-dropzone-compact > .v2-actions {
        flex: 0 1 auto;
        justify-content: flex-end;
    }

/* the button row under a drop zone (e.g. Generate Question centered at the bottom of
   the card), with some room from whatever sits above it */
.v2-dropzone-actions {
    margin-top: var(--v2-space-5);
}

/* a v2-upload-action button in that row follows the drop zone's picked files, the same
   way one inside the drop zone does: hidden until a file is picked (pure css). a file
   that failed (e.g. not a pdf) or was cancelled doesn't count, since there is nothing
   to act on */
.v2 .v2-dropzone:has(.v2-upload):not(:has(.ruFileLI:not(.ruUploadFailure):not(.ruUploadCancelled))) ~ .v2-dropzone-actions > .v2-upload-action {
    display: none;
}

/* a section after the drop zone that shows the file already in use (e.g. "Article in
   use" on Suggest Question) hides while a new file is picked, since the new one will
   take its place. it comes back if the pick is removed */
.v2 .v2-dropzone:has(.v2-upload):has(.ruFileLI) ~ .v2-dropzone-current {
    display: none;
}

/* a button in that row can carry two labels that follow the picked files too:
   .v2-upload-label-idle while nothing is picked, .v2-upload-label-picked once a file
   is (e.g. "Re-generate Question" for the article already read, "Generate Question"
   for a newly picked PDF) */
.v2 .v2-dropzone:has(.v2-upload):not(:has(.ruFileLI)) ~ .v2-dropzone-actions .v2-upload-label-picked,
.v2 .v2-dropzone:has(.v2-upload):has(.ruFileLI) ~ .v2-dropzone-actions .v2-upload-label-idle {
    display: none;
}

/* the opposite of v2-upload-action: a v2-upload-blocked button in that row stays in view but
   is greyed out and can't be clicked while a file is picked, because the pick has to go
   through another step first (e.g. Generate Question waits for Verify PDF on Suggest
   Question). it looks like the regular disabled button; the server still checks too */
.v2 .v2-dropzone:has(.v2-upload):has(.ruFileLI) ~ .v2-dropzone-actions > .v2-btn.v2-upload-blocked {
    opacity: 0.5;
    cursor: not-allowed;
    box-shadow: none;
    pointer-events: none;
}

/* RadAsyncUpload: its "Select" button becomes a v2 primary button with an upload
   icon, and its own file list sits under it. put CssClass="v2-upload" on the
   control; the invisible file input telerik lays over the button still takes the
   click, so behavior is unchanged */
.v2 .v2-upload.RadAsyncUpload {
    width: auto;
    font: inherit;
}

    .v2 .v2-upload.RadAsyncUpload .ruInputs {
        margin: 0;
        padding: 0;
        list-style: none;
    }

    .v2 .v2-upload.RadAsyncUpload .ruFileWrap {
        display: inline-block;
        position: relative;
        height: auto;
        vertical-align: middle;
    }

    /* the empty file-name box some render modes draw beside the button; the picked file is listed under the
       button instead, so the box only reads as a second, broken input */
    .v2 .v2-upload.RadAsyncUpload .ruFakeInput {
        display: none;
    }

    .v2 .v2-upload.RadAsyncUpload .ruButton.ruBrowse {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: var(--v2-space-2);
        width: auto;
        min-width: 104px;
        height: auto;
        min-height: 42px;
        margin: 0;
        padding: 0 var(--v2-space-5);
        border: 1px solid var(--v2-primary);
        border-radius: var(--v2-radius-sm);
        background: var(--v2-primary);
        box-shadow: var(--v2-shadow-button);
        color: var(--v2-on-primary);
        font-size: var(--v2-font-body);
        font-weight: 600;
        line-height: 1;
        text-indent: 0;
        cursor: pointer;
    }

        /* font awesome "arrow-up-from-bracket" (f093) drawn from the site's font */
        .v2 .v2-upload.RadAsyncUpload .ruButton.ruBrowse::before {
            content: "\f093";
            font-family: "Font Awesome 7 Pro";
            font-weight: 400;
        }

    .v2 .v2-upload.RadAsyncUpload .ruFileWrap:hover .ruButton.ruBrowse {
        background: var(--v2-primary-hover);
        border-color: var(--v2-primary-hover);
    }

    .v2 .v2-upload.RadAsyncUpload .ruFileWrap:focus-within .ruButton.ruBrowse {
        box-shadow: 0 0 0 3px var(--v2-focus-ring);
    }

/* the select button as a secondary button, for a row where another button is the
   main action (e.g. Upload PDF next to Generate Question), so the card keeps one primary */
.v2 .v2-upload.v2-upload-secondary.RadAsyncUpload .ruButton.ruBrowse {
    border-color: var(--v2-border-strong);
    background: var(--v2-surface);
    box-shadow: none;
    color: var(--v2-primary-strong);
}

.v2 .v2-upload.v2-upload-secondary.RadAsyncUpload .ruFileWrap:hover .ruButton.ruBrowse {
    border-color: var(--v2-primary-border);
    background: var(--v2-primary-subtle);
}

/* inside a .v2-actions row, the control and its list step aside (display:
   contents) so their items join the row itself: [Upload PDFs] [other buttons]
   first, then one picked file per line under them. telerik inserts picked files
   *before* its select button, so order puts the button back in front. the
   control is only regrouped visually; its markup and behavior are unchanged */
.v2 .v2-actions:has(> .v2-upload) {
    row-gap: var(--v2-space-2);
}

.v2 .v2-actions > .v2-upload.RadAsyncUpload,
.v2 .v2-actions > .v2-upload.RadAsyncUpload > .ruInputs {
    display: contents;
}

.v2 .v2-actions > .v2-upload.RadAsyncUpload .ruSelectWrap {
    order: -1;
}

/* a button that acts on the picked files (e.g. Verify PDFs) only shows while
   there is at least one. telerik adds a .ruFileLI per picked file and removes
   it on Remove, so this follows the list live, without a postback. a file that
   failed (not a pdf, too big) or was cancelled keeps its row with telerik's
   .ruUploadFailure / .ruUploadCancelled class but has nothing to verify, so it
   doesn't count */
.v2 .v2-actions:has(> .v2-upload):not(:has(.ruFileLI:not(.ruUploadFailure):not(.ruUploadCancelled))) > .v2-upload-action {
    display: none;
}

/* ...and it also waits while a picked file is still uploading, since the server
   can't act on a file that hasn't arrived yet. telerik has no reliable class for
   that state, so the page script adds is-uploading to the drop zone from the
   RadAsyncUpload client events (OnClientFileSelected until OnClientFileUploaded /
   OnClientFileUploadFailed) and removes it once every picked file has finished */
.v2 .v2-dropzone.is-uploading .v2-upload-action,
.v2 .v2-dropzone.is-uploading ~ .v2-dropzone-actions > .v2-upload-action {
    display: none;
}

.v2 .v2-actions > .v2-upload.RadAsyncUpload .ruFileLI {
    order: 1;
    flex: 0 0 100%;
    max-width: 560px;
}

    /* the first picked file (first in telerik's list) gets a bit more room under the buttons */
    .v2 .v2-actions > .v2-upload.RadAsyncUpload .ruFileLI:first-child {
        margin-top: var(--v2-space-2);
    }

/* in a compact drop zone the button row steps aside too (display: contents), so its
   buttons and the picked file join the drop zone's own row. the buttons stay in
   place after the text, and the picked file drops to a full-width line along the
   bottom of the drop zone instead of squeezing in under the buttons */
.v2 .v2-dropzone-compact > .v2-actions:has(> .v2-upload) {
    display: contents;
}

    /* keep the upload button after the text (the plain rule moves it to the front) */
    .v2 .v2-dropzone-compact > .v2-actions > .v2-upload.RadAsyncUpload .ruSelectWrap {
        order: 0;
    }

    /* the telerik skin gives the list item content-box sizing, so its padding pushed the
       full-width line past the drop zone's border; border-box keeps it inside */
    .v2 .v2-dropzone-compact > .v2-actions > .v2-upload.RadAsyncUpload .ruFileLI {
        box-sizing: border-box;
        min-width: 0;
        max-width: 100%;
        margin-left: 0;
        margin-right: 0;
    }

        .v2 .v2-dropzone-compact > .v2-actions > .v2-upload.RadAsyncUpload .ruFileLI:first-child {
            margin-top: 0;
        }

    /* one picked file (before Verify): status dot, name, Remove */
    .v2 .v2-upload.RadAsyncUpload .ruFileLI {
        display: flex;
        align-items: center;
        gap: var(--v2-space-2);
        padding: var(--v2-space-2) var(--v2-space-3);
        border: 1px solid var(--v2-border);
        border-radius: var(--v2-radius-sm);
        background: var(--v2-surface);
        font-size: var(--v2-font-small);
        text-align: left;
    }

        .v2 .v2-upload.RadAsyncUpload .ruFileLI .ruFileWrap {
            flex: 1 1 auto;
            min-width: 0;
            overflow-wrap: anywhere;
        }

    /* Remove as a small v2 text link with a trash icon */
    .v2 .v2-upload.RadAsyncUpload .ruButton.ruRemove {
        flex: 0 0 auto;
        display: inline-flex;
        align-items: center;
        gap: var(--v2-space-1);
        width: auto;
        height: auto;
        margin: 0;
        padding: 0;
        border: 0;
        background: none;
        color: var(--v2-primary-strong);
        font-size: var(--v2-font-small);
        font-weight: 600;
        line-height: 1.4;
        text-indent: 0;
        cursor: pointer;
    }

        /* font awesome "trash-can" (f2ed) */
        .v2 .v2-upload.RadAsyncUpload .ruButton.ruRemove::before {
            content: "\f2ed";
            position: static;
            margin: 0;
            /* the skin colors this icon orange; follow the link color instead */
            color: inherit;
            font-family: "Font Awesome 7 Pro";
            font-weight: 400;
        }

        .v2 .v2-upload.RadAsyncUpload .ruButton.ruRemove:hover {
            text-decoration: underline;
        }


/* --------------------------------------------------------------------------
   Wizard (RadWizard)
   --------------------------------------------------------------------------
   put CssClass="v2-wizard" on a telerik:RadWizard (and drop the old
   customRadWizardStyle) to turn its step bar into the v2 stepper: a number
   circle per step joined by lines. the step bar still handles navigation, so
   only its look changes. the circles are numbered by a css counter; for a
   title + hint, set each step's Title to
   <span class="v2-wizard-step-title">..</span><span class="v2-wizard-step-hint">..</span>
   (RadWizard renders Title as html, so encode the text in the code-behind).
   selectors start with ".v2 .v2-wizard.RadWizard" to beat the telerik skin.
   -------------------------------------------------------------------------- */
.v2 .v2-wizard.RadWizard {
    padding: 0;
    min-width: 0;
}

    .v2 .v2-wizard.RadWizard .rwzBreadCrumb {
        display: flex;
        width: 100%;
        margin: 0 0 var(--v2-space-6);
        padding: var(--v2-space-4) 0 var(--v2-space-5);
        border-bottom: 1px solid var(--v2-border);
        overflow: visible;
        counter-reset: v2-wizard-step;
    }

    .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzLI {
        display: flex;
        align-items: center;
        flex: 1 1 0;
        min-width: 0;
        counter-increment: v2-wizard-step;
    }

        .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzLI:last-child {
            flex: 0 1 auto;
        }

        /* the line joining one step to the next */
        .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzLI::after {
            content: "";
            flex: 1 1 auto;
            min-width: var(--v2-space-5);
            height: 2px;
            margin: 0 var(--v2-space-4);
            border-radius: 1px;
            background: var(--v2-border);
        }

        .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzLI:last-child::after {
            display: none;
        }

    /* reset the telerik skin's arrow-shaped buttons */
    .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzLI .rwzLink {
        display: flex;
        align-items: center;
        gap: var(--v2-space-3);
        min-width: 0;
        margin: 0;
        padding: 0;
        border: 0;
        border-radius: 0;
        background: none;
        color: var(--v2-text);
        text-decoration: none;
        white-space: normal;
    }

        /* the number circle */
        .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzLI .rwzLink::before {
            content: counter(v2-wizard-step);
            position: static;
            flex: 0 0 auto;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            width: 36px;
            height: 36px;
            margin: 0;
            border: 1px solid var(--v2-border-strong);
            border-radius: 50%;
            background: var(--v2-surface);
            box-shadow: none;
            transform: none;
            color: var(--v2-text-muted);
            font-size: var(--v2-font-body);
            font-weight: 700;
        }

    .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzText {
        display: flex;
        flex-direction: column;
        min-width: 0;
        line-height: 1.35;
        overflow: visible;
    }

    .v2 .v2-wizard .v2-wizard-step-title {
        font-weight: 700;
    }

    .v2 .v2-wizard .v2-wizard-step-hint {
        color: var(--v2-text-muted);
        font-size: var(--v2-font-small);
    }

    /* steps the user can click back to */
    .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzLI.rwzHovered .rwzLink {
        cursor: pointer;
    }

        .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzLI.rwzHovered .rwzLink .v2-wizard-step-title {
            color: var(--v2-primary-strong);
        }

    /* current step: filled brand circle, brand title */
    .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzLI.rwzSelected .rwzLink::before {
        background: var(--v2-primary);
        border-color: var(--v2-primary);
        color: var(--v2-on-primary);
    }

    .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzLI.rwzSelected .v2-wizard-step-title {
        color: var(--v2-primary-strong);
    }

    /* steps before the current one are done: filled brand circle with a check,
       brand title, and a brand line after them */
    .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzLI:has(~ .rwzSelected) .rwzLink::before {
        content: "\2713";
        background: var(--v2-primary);
        border-color: var(--v2-primary);
        color: var(--v2-on-primary);
    }

    .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzLI:has(~ .rwzSelected) .v2-wizard-step-title {
        color: var(--v2-primary-strong);
    }

    .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzLI:has(~ .rwzSelected)::after,
    .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzLI.rwzSelected::after {
        background: var(--v2-primary);
    }

    /* a step before the current one that the admin passed without doing (e.g. the PDF
       upload after "Use AI Only" or Skip This Step): the code-behind adds v2-wizard-step-skipped to its
       title. a skip icon on a dashed grey circle and a muted title instead of the brand
       check, so it doesn't read as done. the line after it stays brand-colored, since
       the wizard did move past it */
    .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzLI:has(~ .rwzSelected):has(.v2-wizard-step-skipped) .rwzLink::before {
        /* font awesome "forward-step" (f051) drawn from the site's font */
        content: "\f051";
        border-style: dashed;
        border-color: var(--v2-border-strong);
        background: var(--v2-surface-muted);
        color: var(--v2-text-subtle);
        font-family: "Font Awesome 7 Pro";
        font-size: 14px;
        font-weight: 400;
    }

    .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzLI:has(~ .rwzSelected) .v2-wizard-step-skipped {
        color: var(--v2-text-muted);
    }

    /* let the step content size itself (the skin adds scrollbars) */
    .v2 .v2-wizard.RadWizard .rwzContentWrapper,
    .v2 .v2-wizard.RadWizard .rwzContent {
        overflow: visible;
    }

/* --------------------------------------------------------------------------
   Tree (RadTreeView)
   --------------------------------------------------------------------------
   put CssClass="v2-tree" on a telerik:RadTreeView to make it fit its column:
   the skin keeps every node on one line (white-space: nowrap on the tree, and
   lists sized to their content), so long names ran out of the card. here node
   text wraps next to its checkbox, and the tree uses the page font. the expand
   arrows and dotted lines are absolutely positioned by telerik, so they are
   left alone.
   -------------------------------------------------------------------------- */
.v2 .v2-tree.RadTreeView {
    max-width: 100%;
    white-space: normal;
    color: var(--v2-text);
    font: inherit;
}

    .v2 .v2-tree.RadTreeView .rtUL {
        display: block;
    }

    /* checkbox + text side by side; the text column wraps */
    .v2 .v2-tree.RadTreeView .rtOut {
        display: flex;
        align-items: flex-start;
    }

    .v2 .v2-tree.RadTreeView .rtChk {
        flex: 0 0 auto;
        width: 16px;
        height: 16px;
        margin: 11px var(--v2-space-1) 0 0;
        accent-color: var(--v2-primary);
        cursor: pointer;
    }

    .v2 .v2-tree.RadTreeView .rtIn {
        flex: 1 1 auto;
        min-width: 0;
        overflow-wrap: anywhere;
    }

    .v2 .v2-tree.RadTreeView .rtHover .rtIn {
        border-color: transparent;
        background: var(--v2-surface-muted);
        color: var(--v2-text);
    }

    .v2 .v2-tree.RadTreeView .rtSelected .rtIn {
        border-color: var(--v2-primary-border);
        background: var(--v2-primary-subtle);
        color: var(--v2-primary-strong);
    }

/* the buttons under a wizard step: back on the left, forward on the right */
.v2-wizard-footer {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--v2-space-3);
    margin-top: var(--v2-space-5);
    padding-top: var(--v2-space-5);
    border-top: 1px solid var(--v2-border);
}

    /* a single group (e.g. only Next) still sits on the right */
    .v2-wizard-footer > .v2-actions:last-child {
        margin-left: auto;
    }


/* --------------------------------------------------------------------------
   Dialog
   --------------------------------------------------------------------------
   a native <dialog class="v2-dialog">, opened from the page script with
   showModal(), for a question the user has to answer before going on (e.g. "some
   PDFs don't match your topics" when they click Next). showModal puts it in the
   browser's top layer, so it sits above the master page, the stepper and any
   z-index without fighting them, closes on Esc and keeps keyboard focus inside.
   keep the <dialog> inside .v2 (and inside the form, so its asp:Buttons still post
   back); the top layer keeps it in the same place in the document, so the v2
   styles and variables still apply.
   inside: .v2-dialog-header (icon tile, title + text, a .v2-dialog-close button),
   the content, then a .v2-dialog-footer button row (primary first). a click on the
   backdrop should close it too (the page script checks the click position).
   -------------------------------------------------------------------------- */
.v2 .v2-dialog {
    width: min(560px, calc(100vw - 2 * var(--v2-space-4)));
    max-height: calc(100vh - 2 * var(--v2-space-4));
    margin: auto;
    padding: var(--v2-space-6);
    border: 1px solid var(--v2-border);
    border-radius: var(--v2-radius-lg);
    background: var(--v2-surface);
    box-shadow: var(--v2-shadow-lg);
    color: var(--v2-text);
    overflow-y: auto;
}

    /* older browsers don't pass the page's variables to ::backdrop, so the fallback
       keeps the page dimmed there too (it's the same color as --v2-backdrop) */
    .v2 .v2-dialog::backdrop {
        background: var(--v2-backdrop, rgba(15, 27, 45, 0.45));
    }

.v2-dialog-header {
    display: flex;
    align-items: flex-start;
    gap: var(--v2-space-4);
}

    .v2-dialog-header > div {
        flex: 1 1 auto;
        min-width: 0;
    }

/* the x in the top-right corner: an icon-only button, quiet until hovered */
.v2 .v2-dialog-close {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    margin: calc(-1 * var(--v2-space-2)) calc(-1 * var(--v2-space-2)) 0 0;
    padding: 0;
    border: 0;
    border-radius: var(--v2-radius-sm);
    background: none;
    color: var(--v2-text-subtle);
    font-size: 18px;
    cursor: pointer;
}

    .v2 .v2-dialog-close:hover {
        background: var(--v2-surface-muted);
        color: var(--v2-text);
    }

    .v2 .v2-dialog-close:focus-visible {
        outline: none;
        box-shadow: 0 0 0 3px var(--v2-focus-ring);
    }

/* an item list in the dialog (e.g. the mismatched files) lines up under the title,
   past the header's icon tile (44px + the header's 16px gap) */
.v2-dialog .v2-item-list {
    margin: var(--v2-space-3) 0 0 60px;
}

    .v2-dialog .v2-item:last-child {
        border-bottom: 0;
    }

/* the answer buttons, on the right under a divider */
.v2-dialog-footer {
    justify-content: flex-end;
    margin-top: var(--v2-space-5);
    padding-top: var(--v2-space-5);
    border-top: 1px solid var(--v2-border);
}

/* the dialog's content right under its header, set off from the subtitle */
.v2-dialog-body {
    margin-top: var(--v2-space-5);
}

/* a little wider than the default, for a confirmation whose key sentence should stay on
   one line (e.g. the bundle's "will count as one of your 5 selections") */
.v2 .v2-dialog.v2-dialog-medium {
    width: min(680px, calc(100vw - 2 * var(--v2-space-4)));
}

/* a form dialog with long names in it (e.g. learners and user groups in a purchase credit's Grant Credits), between
   the medium and the wide one */
.v2 .v2-dialog.v2-dialog-large {
    width: min(960px, calc(100vw - 2 * var(--v2-space-4)));
}

/* a wider dialog, for content that needs the room (e.g. a long prompt, or a question
   beside its rating form). it's also a size container, so .v2-dialog-columns inside it
   can tell whether two columns fit (see the @container rules at the bottom) */
.v2 .v2-dialog.v2-dialog-wide {
    width: min(1120px, calc(100vw - 2 * var(--v2-space-4)));
    container-type: inline-size;
}

/* nearly the whole window (up to 1440px), for long read-only text such as an AI prompt,
   where every extra column means fewer wrapped lines to scroll through */
.v2 .v2-dialog.v2-dialog-full {
    width: min(1440px, calc(100vw - 2 * var(--v2-space-4)));
}

/* October 19, 2026 Gevin Valerian (https://app.asana.com/1/5405414973450/project/218888662811289/task/1219572175539329)
   image preview: one picture shown large in a dialog body (the cover wizard's "see it bigger"). it fits the dialog's
   width and the window's height, keeps its own shape, and sits centered on a muted strip so a white image still
   shows its edges */
.v2 .v2-image-preview {
    display: flex;
    justify-content: center;
    padding: var(--v2-space-3);
    border-radius: var(--v2-radius-md);
    background: var(--v2-surface-muted);
}

    .v2 .v2-image-preview img {
        display: block;
        max-width: 100%;
        max-height: calc(100vh - 260px);
        border-radius: var(--v2-radius-sm);
        box-shadow: var(--v2-shadow-sm);
    }

/* October 22, 2026 Gevin Valerian (https://app.asana.com/1/5405414973450/project/218888662811289/task/1219734038162132)
   prompt view: div.v2-prompt-view holding a .v2-prompt-view-copy button and a .v2-prompt-view-text, for one long
   read-only prompt the admin copies to use elsewhere (the cover wizard's View prompt). the text keeps the prompt's
   own line breaks and scrolls inside the box; the copy button sits in the corner, outside the scrolling part, so it
   stays put while the text moves, and the text keeps room on the right so the button never covers it */
.v2 .v2-prompt-view {
    position: relative;
    border: 1px solid var(--v2-border-strong);
    border-radius: var(--v2-radius-md);
    background: var(--v2-surface-muted);
}

    .v2 .v2-prompt-view > .v2-prompt-view-copy {
        position: absolute;
        top: var(--v2-space-3);
        right: var(--v2-space-5);
    }

    .v2 .v2-prompt-view > .v2-prompt-view-text {
        min-height: 160px;
        max-height: calc(100vh - 380px);
        overflow-y: auto;
        padding: var(--v2-space-4) 170px var(--v2-space-4) var(--v2-space-4);
        color: var(--v2-text);
        font-family: Consolas, "Cascadia Mono", monospace;
        font-size: 13px;
        line-height: 1.55;
        white-space: pre-wrap;
        overflow-wrap: anywhere;
    }

/* in a dialog body laid out as a .v2-stack, the stack's gap already spaces an alert from what's next to it */
.v2 .v2-dialog-body.v2-stack > .v2-alert {
    margin-bottom: 0;
}

/* a group of buttons pushed to the start of a row of actions (e.g. View prompt and Download image on the left of a
   dialog footer, apart from the buttons that use or keep the image on the right) */
.v2 .v2-actions > .v2-actions-start {
    margin-right: auto;
}

/* two columns in a wide dialog (e.g. the original AI question beside the rating form),
   with a line between them. on a narrow dialog they stack, and the line moves on top */
.v2-dialog-columns {
    display: grid;
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
}

    .v2-dialog-columns > * {
        min-width: 0;
    }

    .v2-dialog-columns > :first-child {
        padding-right: var(--v2-space-6);
    }

    .v2-dialog-columns > * + * {
        padding-left: var(--v2-space-6);
        border-left: 1px solid var(--v2-border);
    }

/* the answer options of a question shown read-only (e.g. the original AI question in the
   Rate Question dialog): one bordered row each, lettered A, B, C. the letter is drawn inside
   the row with a counter rather than the list marker, so the border and the correct option's
   tint cover it too. the correct one also carries a "Correct answer" tag on the right */
.v2 .v2-answer-list {
    display: flex;
    flex-direction: column;
    gap: var(--v2-space-2);
    margin: var(--v2-space-1) 0 0;
    padding-left: 0;
    list-style: none;
    counter-reset: v2-answer-option;
}

    .v2-answer-list > li {
        display: flex;
        align-items: baseline;
        gap: var(--v2-space-3);
        padding: var(--v2-space-2) var(--v2-space-3);
        border: 1px solid var(--v2-border);
        border-radius: var(--v2-radius-sm);
        overflow-wrap: anywhere;
        counter-increment: v2-answer-option;
    }

        .v2-answer-list > li::before {
            content: counter(v2-answer-option, upper-alpha) ".";
            flex: 0 0 auto;
            color: var(--v2-text-muted);
            font-weight: 600;
        }

        .v2-answer-list > li.is-correct {
            border-color: var(--v2-success);
            background: var(--v2-success-subtle);
        }

    /* the option text takes the room between the letter and the tag, and wraps there */
    .v2-answer-list-text {
        flex: 1 1 auto;
        min-width: 0;
    }

    /* kept on one line so a long option wraps its text, not the tag */
    .v2-answer-list-correct {
        flex: 0 0 auto;
        margin-left: auto;
        color: var(--v2-success);
        font-size: var(--v2-font-small);
        font-weight: 600;
        white-space: nowrap;
    }

/* a list of written comments (e.g. raters' comments in the AI Ratings dialog): each comment
   in its own muted box, with who wrote it small in the bottom-right corner */
.v2-comment-list {
    display: flex;
    flex-direction: column;
    gap: var(--v2-space-3);
}

.v2-comment {
    padding: var(--v2-space-2) var(--v2-space-3);
    border: 1px solid var(--v2-border);
    border-radius: var(--v2-radius-sm);
    background: var(--v2-surface-muted);
}

.v2-comment-text {
    display: block;
    white-space: pre-line;
    overflow-wrap: anywhere;
}

.v2-comment-author {
    display: block;
    margin-top: var(--v2-space-2);
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
    text-align: right;
}


/* --------------------------------------------------------------------------
   Quota (a limit the user uses up)
   --------------------------------------------------------------------------
   a soft card that sums up a limit the user uses up as they go, e.g. a bundle's "choose
   10 of 15 courses" on the learner's bundle page (UserControls/__bundleCourseSelectionLimit.ascx):
   an icon circle, a .v2-quota-summary (title + .v2-quota-facts bullets), a .v2-quota-track of three
   figures (used, left, the limit), then a .v2-tip.v2-quota-note on the right behind a thin
   line. the card is a size container, so on a narrow card the note drops under the figures
   and the track takes the full width (see the @container rules at the bottom).
   -------------------------------------------------------------------------- */
.v2-card.v2-quota {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--v2-space-4) var(--v2-space-6);
    container-type: inline-size;
}

.v2-quota-summary {
    flex: 1 1 260px;
    min-width: 0;
}

/* a step smaller than a card title, and balanced, so a title like "Choose 2 of 10 courses in
   this bundle" never leaves one word alone on its second line */
.v2 .v2-quota .v2-card-title {
    font-size: var(--v2-font-page-subtitle);
    text-wrap: balance;
}

/* the facts under the title, one bulleted line each (e.g. "Started 0 of 2 courses this year",
   "2 selections remaining") */
.v2 .v2-quota-facts {
    margin: var(--v2-space-1) 0 0;
    padding-left: var(--v2-space-5);
    color: var(--v2-text-muted);
    list-style: disc;
}

/* the figures as steps joined by a line, like a stepper. the used count (is-started) is filled
   with the started blue, so it matches the "Started" badges on the courses under it, and the
   limit (is-limit) with the brand color. the count left stays neutral */
.v2 .v2-quota-track {
    display: flex;
    flex: 0 0 auto;
    margin: 0;
    padding: 0;
    list-style: none;
}

    .v2-quota-track > li {
        position: relative;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: var(--v2-space-1);
        width: 112px;
    }

        /* the line from the step before, edge to edge: the centers are a step's width apart
           and the circles are 40px, so it stops 20px short of both centers */
        .v2-quota-track > li + li::before {
            content: "";
            position: absolute;
            top: 19px;
            left: calc(-50% + 20px);
            right: calc(50% + 20px);
            height: 2px;
            background: var(--v2-border-strong);
        }

.v2 .v2-quota-track-value {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border: 2px solid var(--v2-border-strong);
    border-radius: 50%;
    background: var(--v2-surface);
    color: var(--v2-text);
    font-weight: 600;
    line-height: 1;
}

.v2 .v2-quota-track-label {
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
}

.v2 .v2-quota-track > li.is-started .v2-quota-track-value {
    border-color: var(--v2-status-started);
    background: var(--v2-status-started);
    color: var(--v2-on-status-started);
}

.v2 .v2-quota-track > li.is-started .v2-quota-track-label {
    color: var(--v2-status-started-strong);
    font-weight: 600;
}

.v2 .v2-quota-track > li.is-limit .v2-quota-track-value {
    border-color: var(--v2-primary);
    background: var(--v2-primary);
    color: var(--v2-on-primary);
}

.v2 .v2-quota-track > li.is-limit .v2-quota-track-label {
    color: var(--v2-primary-strong);
    font-weight: 600;
}

/* what the limit means for the user, beside the figures behind a line in the soft card's border blue */
.v2 .v2-quota-note {
    flex: 1 1 260px;
    max-width: 360px;
    padding-left: var(--v2-space-6);
    border-left: 1px solid var(--v2-card-soft-border);
}


/* --------------------------------------------------------------------------
   Info tooltip (bootstrap 3 tooltip)
   --------------------------------------------------------------------------
   a light card-like bootstrap tooltip holding a small title and label / value
   rows (e.g. the original question's stats next to a Derived From id). the page
   script opens it with this template:

   $(...).tooltip({ html: true, container: 'body', placement: 'auto bottom',
       template: '<div class="tooltip v2-tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>' });

   the content goes in the trigger's title:
   <div class="v2-tooltip-title">..</div>
   <div class="v2-tooltip-row"><span class="v2-tooltip-label"><i ..></i>..</span><span class="v2-tooltip-value">..</span></div>

   gotcha: bootstrap appends the tooltip to <body> (container: 'body', so the
   record card's edge never cuts it off), which is outside .v2. that's why these
   rules aren't scoped under .v2; the v2-tooltip class alone keeps them off the
   site's other (black) tooltips. the --v2-* variables live on :root, so theming
   still works out there.
   -------------------------------------------------------------------------- */
.tooltip.v2-tooltip {
    /* bootstrap sets its own font stack on .tooltip; take the site's font back */
    font-family: inherit;
    font-size: var(--v2-font-small);
    line-height: 1.5;
    opacity: 1;
}

    /* a little more room between the arrow tip and the icon than bootstrap's 3px */
    .tooltip.v2-tooltip.top {
        margin-top: -6px;
    }

    .tooltip.v2-tooltip.bottom {
        margin-top: 6px;
    }

    .tooltip.v2-tooltip.left {
        margin-left: -6px;
    }

    .tooltip.v2-tooltip.right {
        margin-left: 6px;
    }

    .tooltip.v2-tooltip .tooltip-inner {
        min-width: 220px;
        max-width: 280px;
        padding: var(--v2-space-3) var(--v2-space-4);
        border: 1px solid var(--v2-border);
        border-radius: var(--v2-radius-sm);
        background: var(--v2-surface);
        box-shadow: var(--v2-shadow-md);
        color: var(--v2-text);
        text-align: left;
    }

    /* bootstrap draws the arrow from css borders, which can't carry an outline. this
       turns it into a small rotated square with the box's border on its two outer
       sides; it sits over the box's edge, so the arrow and the box read as one shape.
       bootstrap still sets its left / top inline to point at the icon */
    .tooltip.v2-tooltip .tooltip-arrow {
        width: 10px;
        height: 10px;
        border: 0 solid var(--v2-border);
        background: var(--v2-surface);
        transform: rotate(45deg);
    }

    .tooltip.v2-tooltip.top .tooltip-arrow {
        border-width: 0 1px 1px 0;
    }

    .tooltip.v2-tooltip.bottom .tooltip-arrow {
        border-width: 1px 0 0 1px;
    }

    .tooltip.v2-tooltip.left .tooltip-arrow {
        border-width: 1px 1px 0 0;
    }

    .tooltip.v2-tooltip.right .tooltip-arrow {
        border-width: 0 0 1px 1px;
    }

.v2-tooltip-title {
    margin-bottom: var(--v2-space-1);
    font-weight: 700;
}

/* one label / value pair; rows after the first get a divider */
.v2-tooltip-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--v2-space-4);
    padding: 6px 0;
}

    .v2-tooltip-row + .v2-tooltip-row {
        border-top: 1px solid var(--v2-border);
    }

    /* no padding under the last row, the box's own padding is enough */
    .v2-tooltip-row:last-child {
        padding-bottom: 0;
    }

.v2-tooltip-label {
    color: var(--v2-text-muted);
}

    /* fixed width, so the labels line up whatever the icon's shape */
    .v2-tooltip-label > i {
        width: 16px;
        margin-right: var(--v2-space-2);
        color: var(--v2-text-subtle);
        text-align: center;
    }

/* numbers line up on the right, digit under digit */
.v2-tooltip-value {
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* site.css hides every bootstrap tooltip on touch-size devices, so a first tap on
   a link isn't eaten by its tooltip. an info tooltip sits on an icon that does
   nothing else, and the tap is the only way to read it there, so it stays */
@media only screen and (max-device-width: 1024px) {
    .tooltip.v2-tooltip {
        display: block !important;
    }
}

/* the "i" that opens an info tooltip can be reached with tab (it opens on focus too) */
.v2-help-icon[tabindex]:focus-visible {
    outline: none;
    border-radius: 50%;
    box-shadow: 0 0 0 3px var(--v2-focus-ring);
}


/* --------------------------------------------------------------------------
   Results and findings
   --------------------------------------------------------------------------
   the outcome of a set of checks (e.g. Validate Upsell Rule). a .v2-result-heading with an icon and
   a count opens each group (errors, warnings, passed). each problem is a .v2-finding: a bordered box
   with a tinted header strip (icon, title, and a short reason on the right), then optionally plain
   lines and a table of the items behind it, each with its own link. danger blocks something, warning
   only asks for a look. put several findings in a .v2-finding-list
   -------------------------------------------------------------------------- */
.v2 .v2-result-heading {
    display: flex;
    align-items: center;
    gap: var(--v2-space-3);
    margin: var(--v2-space-6) 0 var(--v2-space-3);
    font-size: var(--v2-font-section-title);
    font-weight: 600;
}

    .v2 .v2-result-heading > i {
        font-size: 20px;
    }

/* the count after the heading's text */
.v2-result-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 28px;
    padding: 0 var(--v2-space-2);
    border: 1px solid var(--v2-border);
    border-radius: var(--v2-radius-pill);
    background: var(--v2-surface-muted);
    font-size: var(--v2-font-small);
    font-weight: 700;
    line-height: 1.7;
}

.v2-result-heading-danger > i,
.v2-result-heading-danger > .v2-result-count {
    color: var(--v2-danger);
}

.v2-result-heading-danger > .v2-result-count {
    background: var(--v2-danger-subtle);
    border-color: color-mix(in srgb, var(--v2-danger) 25%, white);
}

.v2-result-heading-warning > i,
.v2-result-heading-warning > .v2-result-count {
    color: var(--v2-warning);
}

.v2-result-heading-warning > .v2-result-count {
    background: var(--v2-warning-subtle);
    border-color: color-mix(in srgb, var(--v2-warning) 25%, white);
}

.v2-result-heading-success > i,
.v2-result-heading-success > .v2-result-count {
    color: var(--v2-success);
}

.v2-result-heading-success > .v2-result-count {
    background: var(--v2-success-subtle);
    border-color: color-mix(in srgb, var(--v2-success) 25%, white);
}

.v2-finding-list {
    display: flex;
    flex-direction: column;
    gap: var(--v2-space-4);
}

.v2-finding {
    border: 1px solid var(--v2-border);
    border-radius: var(--v2-radius-md);
    background: var(--v2-surface);
    overflow: hidden;
}

.v2-finding-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--v2-space-2) var(--v2-space-3);
    padding: var(--v2-space-3) var(--v2-space-5);
}

    .v2-finding-header > i {
        flex: 0 0 auto;
        font-size: 17px;
    }

.v2-finding-title {
    min-width: 0;
    font-size: var(--v2-font-page-subtitle);
    font-weight: 600;
}

/* the short reason, pushed to the right; it drops under the title on a narrow box */
.v2-finding-note {
    margin-left: auto;
    font-size: var(--v2-font-small);
}

.v2-finding-danger {
    border-color: color-mix(in srgb, var(--v2-danger) 25%, white);
}

    .v2-finding-danger > .v2-finding-header {
        background: var(--v2-danger-subtle);
        color: var(--v2-danger);
    }

.v2-finding-warning {
    border-color: color-mix(in srgb, var(--v2-warning) 25%, white);
}

    .v2-finding-warning > .v2-finding-header {
        background: var(--v2-warning-subtle);
        color: var(--v2-warning);
    }

/* plain lines under the header (e.g. "Enter a rule name.") */
.v2 .v2-finding-message-list {
    margin: 0;
    padding: var(--v2-space-3) var(--v2-space-5) var(--v2-space-3) var(--v2-space-8);
    border-top: 1px solid var(--v2-border);
}

/* the items table: small capital column headers, and the first and last columns lined up with the
   header strip's padding */
.v2 .v2-finding .v2-table th {
    border-top: 1px solid var(--v2-border);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.v2 .v2-finding .v2-table th:first-child,
.v2 .v2-finding .v2-table td:first-child {
    padding-left: var(--v2-space-5);
}

.v2 .v2-finding .v2-table th:last-child,
.v2 .v2-finding .v2-table td:last-child {
    padding-right: var(--v2-space-5);
}

/* table cells: a right-aligned figure (lined-up digits) and a right-aligned row action (e.g. "Open course") */
.v2 .v2-table .v2-table-number {
    text-align: right;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.v2 .v2-table .v2-table-action {
    text-align: right;
    white-space: nowrap;
}

/* a column header that sorts the table (e.g. Take Rate on View Upsell Rule): it reads as the header
   text, with a quiet sort icon, and the sorted column gets the text color and an arrow */
.v2 a.v2-table-sort,
.v2 a.v2-table-sort:visited {
    display: inline-flex;
    align-items: center;
    gap: var(--v2-space-1);
    color: var(--v2-text-muted);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}

    .v2 a.v2-table-sort:hover,
    .v2 a.v2-table-sort:focus-visible {
        color: var(--v2-primary-strong);
        text-decoration: none;
    }

    .v2 a.v2-table-sort.is-sorted {
        color: var(--v2-text);
    }

/* a cell with a value and a second line under it (e.g. a rule's take rate and its added revenue
   in the Upsell Rules list); works in a v2 table and in a RadGrid cell */
.v2-cell-stack {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--v2-space-1);
}

    /* both lines centered, for a stack in a .v2-table-center column */
    .v2-cell-stack.v2-cell-stack-center {
        align-items: center;
    }

/* a cell with a value and a pill on the same line (e.g. a course group's name and its course count
   in the Upsell Rules list). the pill keeps its size and drops under a long name when the column is
   too narrow; works in a v2 table and in a RadGrid cell */
.v2-cell-inline {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--v2-space-2);
}

    .v2-cell-inline > .v2-pill {
        flex-shrink: 0;
    }

/* a figure that is a gain (e.g. "+$12,450 revenue" in the Upsell Rules list), and the same figure
   while it is still zero */
.v2 .v2-figure-positive {
    color: var(--v2-success);
}

.v2 .v2-figure-empty {
    color: var(--v2-text-muted);
}

/* two or more links in a row split by a thin line (e.g. View | Edit in a table row) */
.v2-actions.v2-link-group {
    flex-wrap: nowrap;
    gap: 0;
}

    .v2-actions.v2-link-group > * + * {
        margin-left: var(--v2-space-3);
        padding-left: var(--v2-space-3);
        border-left: 1px solid var(--v2-border);
    }

/* a segmented filter above a list (e.g. All / Active / Inactive on Upsell Rules), on the right so it
   lines up with the page actions above it */
.v2 .v2-segmented.v2-list-filter {
    margin-bottom: var(--v2-space-4);
}

    .v2 .v2-segmented.v2-list-filter .btn-group {
        justify-content: flex-end;
    }

/* a RadGrid drawn like a bordered v2 table (e.g. the Upsell Rules list): a muted header row, roomy
   cells split by lines, no stripes. it keeps the grid's paging; the Telerik skin only shows in the
   pager. selectors start with .v2 .v2-radgrid.RadGrid to beat the skin's own rules. overflow: auto
   both rounds the corners and lets a wide grid scroll sideways on a phone instead of the page */
.v2 .v2-radgrid.RadGrid {
    border: 1px solid var(--v2-border);
    border-radius: var(--v2-radius-md);
    background: var(--v2-surface);
    color: var(--v2-text);
    font: inherit;
    overflow: auto;
}

    .v2 .v2-radgrid.RadGrid .rgMasterTable {
        width: 100%;
        border-collapse: collapse;
        font: inherit;
    }

    .v2 .v2-radgrid.RadGrid th.rgHeader {
        padding: var(--v2-space-4) var(--v2-space-5);
        border: 0;
        border-bottom: 1px solid var(--v2-border);
        background: var(--v2-surface-muted);
        color: var(--v2-text-muted);
        font-weight: 600;
        text-align: left;
        vertical-align: middle;
        white-space: nowrap;
    }

    .v2 .v2-radgrid.RadGrid .rgRow,
    .v2 .v2-radgrid.RadGrid .rgAltRow {
        background: var(--v2-surface);
    }

        .v2 .v2-radgrid.RadGrid .rgRow > td,
        .v2 .v2-radgrid.RadGrid .rgAltRow > td,
        .v2 .v2-radgrid.RadGrid .rgNoRecords > td {
            padding: var(--v2-space-4) var(--v2-space-5);
            border: 0;
            border-bottom: 1px solid var(--v2-border);
            vertical-align: middle;
        }

    .v2 .v2-radgrid.RadGrid .rgMasterTable > tbody > tr:last-child > td {
        border-bottom: 0;
    }

    .v2 .v2-radgrid.RadGrid .rgNoRecords > td {
        color: var(--v2-text-muted);
    }

/* a v2 RadGrid whose rows each open a record (add v2-row-link-table, e.g. the learning activities on
   Membership Mapping): the row's a.v2-row-link stretches over the whole row and the row lights up on
   hover. anything else in the row that reacts to the pointer (tooltip icons, a remove button) gets
   v2-row-link-above, so it sits over the link instead of under it */
.v2 .v2-radgrid.RadGrid.v2-row-link-table .rgRow,
.v2 .v2-radgrid.RadGrid.v2-row-link-table .rgAltRow {
    position: relative;
}

    .v2 .v2-radgrid.RadGrid.v2-row-link-table .rgRow:hover,
    .v2 .v2-radgrid.RadGrid.v2-row-link-table .rgAltRow:hover {
        background: var(--v2-surface-muted);
    }

.v2 a.v2-row-link::after {
    content: "";
    position: absolute;
    inset: 0;
}

.v2 .v2-row-link-above {
    position: relative;
    z-index: 1;
}

    /* the skin colors header links on its own, so the sort link's colors are restated here */
    .v2 .v2-radgrid.RadGrid th.rgHeader a.v2-table-sort {
        color: var(--v2-text-muted);
    }

        .v2 .v2-radgrid.RadGrid th.rgHeader a.v2-table-sort.is-sorted {
            color: var(--v2-text);
        }

        .v2 .v2-radgrid.RadGrid th.rgHeader a.v2-table-sort:hover,
        .v2 .v2-radgrid.RadGrid th.rgHeader a.v2-table-sort:focus-visible {
            color: var(--v2-primary-strong);
        }

/* a narrow first column that reads like a row number (e.g. a rule's priority), split from the rest by
   a line; put it on the header cell too. works in a v2 table and in a v2 RadGrid. the cell selectors
   carry th / td so its tighter side padding beats the tables' own cell padding, and 48px still fits a
   three-digit number */
.v2 .v2-table th.v2-table-index,
.v2 .v2-table td.v2-table-index,
.v2 .v2-radgrid.RadGrid th.v2-table-index,
.v2 .v2-radgrid.RadGrid td.v2-table-index {
    width: 48px;
    padding-left: var(--v2-space-2);
    padding-right: var(--v2-space-2);
    border-right: 1px solid var(--v2-border);
    color: var(--v2-text-muted);
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* a column of short lines that read best unbroken, such as figures and dates (e.g. Performance and
   Lifecycle in the Upsell Rules list): its lines don't wrap and it takes at least 200px, so long names
   in the other columns can't squeeze it. put it on the header cell and the item cells. works in a v2
   table and in a v2 RadGrid, which scrolls sideways when the columns no longer fit */
.v2 .v2-table .v2-table-wide,
.v2 .v2-radgrid.RadGrid .v2-table-wide {
    width: 200px;
    white-space: nowrap;
}

/* a column holding a learner's segmented bootstrap progress bar (e.g. Progress on Student Distribution):
   wide enough that each segment can still be told apart. bootstrap gives .progress a 20px bottom margin,
   which would push the bar above the row's middle, so it is taken off here */
.v2 .v2-table .v2-table-progress {
    width: 300px;
}

    .v2 .v2-table td.v2-table-progress .progress {
        margin-bottom: 0;
    }

/* a table whose body cells align to the top instead of the middle, so every cell in a row starts on
   the same line when some cells hold several lines (e.g. the lifecycle dates in the Upsell Rules list).
   the header row stays middle-aligned. put it next to v2-table, or in a v2 RadGrid's CssClass */
.v2 .v2-table.v2-table-align-top td,
.v2 .v2-radgrid.RadGrid.v2-table-align-top .rgRow > td,
.v2 .v2-radgrid.RadGrid.v2-table-align-top .rgAltRow > td {
    vertical-align: top;
}

/* a record's lifecycle in a table cell (e.g. an upsell rule): the status as colored text with its dot,
   an optional "Validated" in the same color after a middle dot (.v2-inline-list), then its dates in
   muted lines (a BulletedList) */
.v2-lifecycle {
    display: flex;
    flex-direction: column;
    gap: var(--v2-space-1);
}

    .v2 .v2-lifecycle .v2-status {
        padding: 0;
        background: transparent;
        font-size: var(--v2-font-body);
        font-weight: 500;
    }

    .v2 .v2-lifecycle .v2-lifecycle-validated {
        font-weight: 500;
    }

    .v2 .v2-lifecycle .v2-status-success ~ .v2-lifecycle-validated {
        color: var(--v2-success);
    }

    .v2 .v2-lifecycle .v2-status-info ~ .v2-lifecycle-validated {
        color: var(--v2-info);
    }

    .v2 .v2-lifecycle .v2-status-warning ~ .v2-lifecycle-validated {
        color: var(--v2-warning);
    }

    .v2 .v2-lifecycle .v2-status-neutral ~ .v2-lifecycle-validated {
        color: var(--v2-text-muted);
    }

    .v2 .v2-lifecycle .v2-lifecycle-date-list {
        margin: 0;
        padding: 0;
        list-style: none;
        color: var(--v2-text-muted);
    }

/* an icon tile next to a title and an optional quiet line under it (e.g. a course in a results table) */
.v2-icon-text {
    display: flex;
    align-items: center;
    gap: var(--v2-space-3);
    min-width: 0;
}

.v2-icon-text-body {
    min-width: 0;
}

.v2-icon-text-title {
    display: block;
    font-weight: 600;
    overflow-wrap: anywhere;
}

.v2-icon-text-subtitle {
    display: block;
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
    /* a long unbroken value (an email address) wraps instead of widening a narrow table past its card */
    overflow-wrap: anywhere;
}

/* the right-aligned figure and row action cells of .v2-table, in a v2 RadGrid too (e.g. the subscriber
   count in the Subscription Plans list). put the class on the header cell and the item cells; th / td in
   the selectors beat the grid's own header alignment */
.v2 .v2-radgrid.RadGrid th.v2-table-number,
.v2 .v2-radgrid.RadGrid td.v2-table-number {
    text-align: right;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.v2 .v2-radgrid.RadGrid th.v2-table-action,
.v2 .v2-radgrid.RadGrid td.v2-table-action {
    text-align: right;
    white-space: nowrap;
}

/* a centered column for a short mark (e.g. the .v2-check marks for Shipping and Auto Renewal in the
   Subscription Plans list). works in a v2 table and in a v2 RadGrid */
.v2 .v2-table th.v2-table-center,
.v2 .v2-table td.v2-table-center,
.v2 .v2-radgrid.RadGrid th.v2-table-center,
.v2 .v2-radgrid.RadGrid td.v2-table-center {
    text-align: center;
    white-space: nowrap;
}

/* a yes / no value in a table cell: a green check for yes, a grey dash for no. an empty asp:Label that
   Utility.DesignV2.ApplyCheckMark turns into the icon (fa-check / fa-minus) and gives a "Yes" / "No"
   tooltip and aria-label, so the meaning doesn't rest on the icon alone */
.v2 .v2-check {
    font-size: 16px;
    line-height: 1;
}

    .v2 .v2-check.is-yes {
        color: var(--v2-success);
    }

    .v2 .v2-check.is-no {
        color: var(--v2-text-subtle);
    }

/* a small quiet icon next to a value in a table cell (e.g. the arrow in front of a renewal plan's name, or
   between a promo code's original and discounted price). it says something about the value, not an action */
.v2 .v2-cell-icon {
    color: var(--v2-text-subtle);
    font-size: 14px;
}


/* --------------------------------------------------------------------------
   Guidance
   --------------------------------------------------------------------------
   tips that help an admin set something up well (e.g. Best Practices next to the new upsell rule
   form): a card with a brand-tinted border, a numbered list of tips, and a callout for the one rule
   of thumb worth remembering
   -------------------------------------------------------------------------- */
.v2-card.v2-card-accent {
    border-color: var(--v2-primary-border);
}

    .v2-card-accent .v2-card-heading > i {
        color: var(--v2-primary-strong);
    }

/* numbered tips: the number in a filled brand circle, then a bold title and its text */
.v2 .v2-step-list {
    display: flex;
    flex-direction: column;
    gap: var(--v2-space-5);
    margin: var(--v2-space-5) 0 0;
    padding: 0;
    list-style: none;
    counter-reset: v2-step;
}

    .v2 .v2-step-list > li {
        display: flex;
        align-items: flex-start;
        gap: var(--v2-space-3);
        counter-increment: v2-step;
    }

        .v2 .v2-step-list > li::before {
            content: counter(v2-step);
            flex: 0 0 auto;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            width: 32px;
            height: 32px;
            border-radius: 50%;
            background: var(--v2-primary);
            color: var(--v2-on-primary);
            font-weight: 700;
        }

.v2-step-list-title {
    display: block;
    margin-bottom: var(--v2-space-1);
    font-weight: 700;
}

.v2-step-list-text {
    display: block;
    color: var(--v2-text-muted);
    line-height: 1.6;
}

    .v2 .v2-step-list-text strong {
        color: var(--v2-text);
    }

/* a rule of thumb in a tinted box: an icon, a thin divider, then the text */
.v2-callout {
    display: flex;
    align-items: center;
    gap: var(--v2-space-4);
    margin-top: var(--v2-space-5);
    padding: var(--v2-space-4);
    border: 1px solid var(--v2-primary-border);
    border-radius: var(--v2-radius-md);
    background: var(--v2-primary-subtle);
    color: var(--v2-text-muted);
    line-height: 1.6;
}

    .v2-callout > i {
        flex: 0 0 auto;
        color: var(--v2-primary-strong);
        font-size: 28px;
    }

    .v2-callout > span {
        min-width: 0;
        padding-left: var(--v2-space-4);
        border-left: 1px solid var(--v2-primary-border);
    }


/* --------------------------------------------------------------------------
   Dashboard
   --------------------------------------------------------------------------
   figures and a trend for one record (e.g. an upsell rule's checkout results on View Upsell Rule):
   a .v2-section-header with the period picker, a .v2-highlight line, .v2-kpi-card boxes, a
   .v2-bar-chart, and tables with .v2-table-number columns. every figure is worked out in Core; the
   page only places them
   -------------------------------------------------------------------------- */

/* a section's own header: title and subtitle on the left, the filters that scope the section on the
   right (e.g. the period picker). it wraps the filters under the title on narrow screens */
.v2-section-header {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--v2-space-3) var(--v2-space-4);
    margin: var(--v2-space-7) 0 var(--v2-space-4);
}

    .v2-section-header > div:first-child {
        flex: 1 1 280px;
        min-width: 0;
    }

/* a drop-down with a leading icon (e.g. the calendar of a period picker), up to 340px wide */
.v2-select {
    position: relative;
    flex: 0 1 340px;
    min-width: 0;
}

    .v2-select > i {
        position: absolute;
        top: 50%;
        left: var(--v2-space-3);
        transform: translateY(-50%);
        color: var(--v2-text-subtle);
        pointer-events: none;
    }

    .v2 .v2-select > .v2-input {
        padding-left: 36px;
    }

    /* a drop-down in a button row (e.g. the status and price filters next to the plan tabs on Subscription
       Plans). the row sizes itself from its content, so without a width of its own the drop-down would
       shrink down to its icon */
    .v2-actions > .v2-select {
        flex: 0 1 220px;
        width: 220px;
        min-width: 160px;
    }

/* the period's headline on one line (put it on a .v2-card, tinted when the news is good): an icon
   circle, a bold title and a sentence, and the change against the previous period on the right.
   it sits in a .v2-stack with the boxes under it, so the stack's gap spaces them */
.v2-highlight {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--v2-space-4);
}

.v2-highlight-body {
    flex: 1 1 320px;
    min-width: 0;
}

.v2 .v2-highlight-title {
    margin-bottom: var(--v2-space-1);
    font-size: var(--v2-font-page-subtitle);
    font-weight: 700;
}

/* block, so a Label (a span) gets a line of its own like a p does, e.g. the description and the
   store note under the certification purchase banner's title */
.v2 .v2-highlight-text {
    display: block;
    color: var(--v2-text-muted);
}

.v2-highlight-aside {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--v2-space-1);
}

/* a figure box: its label with an icon circle on the right, the figure, then its change */
.v2-kpi-card {
    display: flex;
    flex-direction: column;
    gap: var(--v2-space-2);
    min-width: 0;
}

.v2-kpi-card-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--v2-space-3);
}

.v2-kpi-card-label {
    font-weight: 600;
}

.v2-kpi-card-value {
    color: var(--v2-text);
    font-size: var(--v2-font-figure);
    font-weight: 700;
    line-height: 1.1;
    overflow-wrap: anywhere;
}

.v2-kpi-card-change {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--v2-space-2);
}

/* a change against an earlier period: a small pill with an arrow. up is green and down is red,
   since every figure it is used for is better when it goes up; neutral when there is no change */
.v2-delta {
    display: inline-flex;
    align-items: center;
    gap: var(--v2-space-1);
    padding: 2px var(--v2-space-2);
    border-radius: var(--v2-radius-pill);
    background: var(--v2-surface-disabled);
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
    font-weight: 700;
    line-height: 1.6;
    white-space: nowrap;
}

    .v2-delta.is-up {
        background: var(--v2-success-subtle);
        color: var(--v2-success);
    }

    .v2-delta.is-down {
        background: var(--v2-danger-subtle);
        color: var(--v2-danger);
    }

.v2-delta-caption {
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
}

/* a column chart of one series (e.g. additional revenue per day). the y-axis steps are evenly spaced
   from 0, so each gridline and its label are one row of a flex column and need no position of their
   own. each bar is a list item as tall as the plot (that is its hover and focus target) and draws
   its column with ::before, at the height in its --v2-bar-value custom property (a percentage of the
   top step). that one data value is the only inline style a v2 page may set, because it is data,
   not styling. the bar's tooltip shows on hover and keyboard focus, and its aria-label carries the
   same text, so no value depends on hovering */
.v2-bar-chart {
    --v2-bar-chart-axis-width: 64px;
    margin-top: var(--v2-space-3);
}

.v2-bar-chart-plot {
    position: relative;
    height: 220px;
    margin-left: var(--v2-bar-chart-axis-width);
}

.v2-bar-chart-grid {
    position: absolute;
    inset: 0 0 0 calc(-1 * var(--v2-bar-chart-axis-width));
    display: flex;
    flex-direction: column-reverse;
    justify-content: space-between;
}

.v2-bar-chart-gridline {
    position: relative;
    height: 0;
    margin-left: var(--v2-bar-chart-axis-width);
    border-top: 1px solid var(--v2-border);
}

.v2-bar-chart-tick {
    position: absolute;
    top: 0;
    right: calc(100% + var(--v2-space-2));
    transform: translateY(-50%);
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.v2 .v2-bar-chart-bars {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: flex-end;
    gap: 2px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.v2-bar-chart-bar {
    position: relative;
    flex: 1 1 0;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    height: 100%;
    min-width: 0;
    cursor: default;
}

    .v2-bar-chart-bar::before {
        content: "";
        width: min(100%, 24px);
        height: var(--v2-bar-value, 0%);
        border-radius: 4px 4px 0 0;
        background: var(--v2-primary);
    }

    .v2-bar-chart-bar:hover::before,
    .v2-bar-chart-bar:focus-visible::before {
        background: var(--v2-primary-hover);
    }

    .v2-bar-chart-bar:focus-visible {
        outline: none;
        border-radius: var(--v2-radius-sm);
        box-shadow: 0 0 0 3px var(--v2-focus-ring);
    }

.v2-bar-chart-tooltip {
    position: absolute;
    bottom: calc(var(--v2-bar-value, 0%) + var(--v2-space-2));
    left: 50%;
    z-index: 2;
    display: none;
    transform: translateX(-50%);
    padding: var(--v2-space-2) var(--v2-space-3);
    border: 1px solid var(--v2-border);
    border-radius: var(--v2-radius-sm);
    background: var(--v2-surface);
    box-shadow: var(--v2-shadow-md);
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
    line-height: 1.5;
    white-space: nowrap;
    pointer-events: none;
}

    .v2-bar-chart-bar:hover > .v2-bar-chart-tooltip,
    .v2-bar-chart-bar:focus-visible > .v2-bar-chart-tooltip {
        display: block;
    }

    /* the value leads, the dates follow */
    .v2 .v2-bar-chart-tooltip strong {
        display: block;
        color: var(--v2-text);
        font-size: var(--v2-font-body);
    }

/* the date labels under the bars, one slot per bar so they line up; only some slots have text */
.v2 .v2-bar-chart-labels {
    display: flex;
    gap: 2px;
    margin: var(--v2-space-2) 0 0 var(--v2-bar-chart-axis-width);
    padding: 0;
    list-style: none;
}

    .v2-bar-chart-labels > li {
        flex: 1 1 0;
        min-width: 0;
        color: var(--v2-text-muted);
        font-size: var(--v2-font-small);
        text-align: center;
        white-space: nowrap;
    }

/* the bars in a decorative tone instead of the brand color, when the series has a color of its own
   elsewhere on the page (e.g. additional revenue, which is green on its figure box) */
.v2-bar-chart.v2-tone-green .v2-bar-chart-bar::before {
    background: var(--v2-tone-green);
}

    .v2-bar-chart.v2-tone-green .v2-bar-chart-bar:hover::before,
    .v2-bar-chart.v2-tone-green .v2-bar-chart-bar:focus-visible::before {
        background: color-mix(in srgb, var(--v2-tone-green) 80%, black);
    }

/* a 0 to 100% stacked column chart (e.g. how much of each day's trigger checkout revenue came from
   the upsell). every bar is full height: its --v2-bar-value share on top in green, the rest under it
   in blue, drawn as one gradient so the one data value is still the only inline style. a bar with
   nothing to split gets is-empty and keeps its slot. its tooltip sits at the top of the plot, since
   every bar reaches it */
.v2-bar-chart-stacked .v2-bar-chart-bar::before,
.v2-bar-chart-stacked .v2-bar-chart-bar:hover::before,
.v2-bar-chart-stacked .v2-bar-chart-bar:focus-visible::before {
    height: 100%;
    background: linear-gradient(to top, var(--v2-tone-blue) calc(100% - var(--v2-bar-value, 0%)), var(--v2-tone-green) 0);
}

    .v2-bar-chart-stacked .v2-bar-chart-bar:hover::before,
    .v2-bar-chart-stacked .v2-bar-chart-bar:focus-visible::before {
        filter: brightness(0.9);
    }

    .v2-bar-chart-stacked .v2-bar-chart-bar.is-empty::before {
        background: none;
    }

    .v2-bar-chart-stacked .v2-bar-chart-tooltip {
        top: var(--v2-space-2);
        bottom: auto;
    }

/* a chart's legend: one swatch and name per series, on a wrapping line under the chart */
.v2 .v2-chart-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--v2-space-2) var(--v2-space-5);
    margin: var(--v2-space-3) 0 0 var(--v2-bar-chart-axis-width, 0);
    padding: 0;
    list-style: none;
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
}

    .v2-chart-legend > li {
        display: inline-flex;
        align-items: center;
        gap: var(--v2-space-2);
    }

.v2-chart-legend-swatch {
    width: 12px;
    height: 12px;
    border-radius: 3px;
    background: var(--v2-border-strong);
}

    .v2-chart-legend-swatch.v2-tone-green {
        background: var(--v2-tone-green);
    }

    .v2-chart-legend-swatch.v2-tone-blue {
        background: var(--v2-tone-blue);
    }

/* a card whose chart sits next to another one (e.g. the two trend charts side by side in a .v2-grid-2
   on View Upsell Rule). the grid gives both cards the same height and each chart sits at the bottom of
   its card, so the spare room goes above the plot and the two plots line up even when the hints over
   them wrap to a different number of lines. give both charts a legend, or neither, so they end at the
   same height. a tip that replaces an empty chart stays right under the hint */
.v2-card-chart {
    display: flex;
    flex-direction: column;
}

    /* padding instead of the chart's own margin, which the auto margin replaces, so the longer hint
       still keeps its gap above the chart */
    .v2-card-chart > .v2-bar-chart {
        margin-top: auto;
        padding-top: var(--v2-space-3);
    }


/* --------------------------------------------------------------------------
   Compact popup, tabs, stepper, chip list, row list, colour picker, disclosure and image choice
   --------------------------------------------------------------------------
   October 16, 2026 Gevin Valerian
   https://app.asana.com/1/5405414973450/project/218888662811289/task/1219562954258983
   built for PopUpWindow/LearningActivity/UpdateLearningActivityCoverImage.aspx and its
   UserControls/__learningActivityCoverImageSettings.ascx. each is written to be reused; see the ui-design-v2 skill.
   -------------------------------------------------------------------------- */

/* compact popup: div.v2.v2-popup holding a .v2-popup-header (an h1.v2-popup-title and a v2-form-hint), an
   optional .v2-popup-tabs row, a .v2-popup-body and a .v2-popup-footer. it fills the popup's frame: the
   header, tabs and footer stay put and only the body scrolls, so Cancel / Update are always in view. the type
   scale and buttons are one step smaller than on a page, because a popup is about 950px wide. the page script
   sizes the fancybox frame to the content, up to the window (see fitCoverImagePopup) */
.v2.v2-popup {
    --v2-font-page-title: 18px;
    --v2-font-page-subtitle: 14px;
    --v2-font-section-title: 15px;
    --v2-font-card-title: 15px;
    --v2-font-body: 14px;
    --v2-font-small: 13px;
    display: flex;
    flex-direction: column;
    height: 100vh;
    background: var(--v2-surface);
}

/* the popup master (PopupWindow.master) wraps its content in a .wrapper_container with an inline 8px padding and
   Site.css's 3px border, radius and shadow: a box hugging the frame's edge around the popup's own. a v2-popup
   is the box, so that one steps aside. !important because the padding is inline in the master. these rules
   reach outside .v2 on purpose, and only for a container holding a v2-popup */
.wrapper_container:has(> .v2.v2-popup) {
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    border-radius: 0 !important;
    box-shadow: none !important;
}

.main:has(> .wrapper_container > .v2.v2-popup) {
    min-height: 0;
}

body:has(.v2.v2-popup) {
    margin: 0;
    overflow: hidden;
}

    .v2.v2-popup .v2-btn,
    .v2.v2-popup a.v2-btn {
        min-width: 0;
        min-height: 34px;
        padding: 0 var(--v2-space-4);
    }

        .v2.v2-popup .v2-btn i {
            font-size: 14px;
        }

    .v2.v2-popup .v2-upload.RadAsyncUpload .ruButton.ruBrowse {
        min-width: 0;
        min-height: 34px;
        padding: 0 var(--v2-space-4);
    }

.v2 .v2-popup-header {
    flex: none;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--v2-space-1) var(--v2-space-3);
    padding: var(--v2-space-5) var(--v2-space-6) var(--v2-space-3);
}

.v2 .v2-popup-title {
    margin: 0;
    font-size: var(--v2-font-page-title);
    font-weight: 700;
    line-height: 1.25;
}

.v2 .v2-popup-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
    padding: var(--v2-space-5) var(--v2-space-6) var(--v2-space-6);
}

.v2 .v2-popup-footer {
    flex: none;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--v2-space-3);
    padding: var(--v2-space-3) var(--v2-space-6);
    border-top: 1px solid var(--v2-border);
    background: var(--v2-surface);
}

    .v2 .v2-popup-footer > .v2-actions {
        margin-left: auto;
    }

/* a heading row inside a popup body: a title with a hint under it on the left, links or buttons on the right */
.v2 .v2-popup-section-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--v2-space-3);
    margin-bottom: var(--v2-space-4);
}

    /* October 20, 2026 Gevin Valerian: an alert as the head's instruction (the cover wizard's "Select the image...")
       takes the room left of the buttons and gets no margin of its own, the head already has one */
    .v2 .v2-popup-section-head > .v2-alert {
        flex: 1 1 320px;
        margin-bottom: 0;
        font-weight: 600;
    }

    /* October 21, 2026 Gevin Valerian: beside such an alert, the buttons sit on its middle, not its top */
    .v2 .v2-popup-section-head:has(> .v2-alert) {
        align-items: center;
    }

.v2 .v2-popup-section-title {
    margin: 0;
    font-size: var(--v2-font-section-title);
    font-weight: 700;
}

/* tabs made of links (asp:LinkButton), for switching what a popup shows: div.v2-popup-tabs of a.v2-popup-tab,
   each an <i> + a span; the code-behind adds is-selected (and aria-selected) to the one showing. a line runs
   under the row and the selected tab stands on it in the brand color */
.v2 .v2-popup-tabs {
    flex: none;
    display: flex;
    flex-wrap: wrap;
    gap: var(--v2-space-5);
    margin: 0 var(--v2-space-6);
    border-bottom: 1px solid var(--v2-border);
}

    .v2 .v2-popup-tabs a.v2-popup-tab,
    .v2 .v2-popup-tabs a.v2-popup-tab:link,
    .v2 .v2-popup-tabs a.v2-popup-tab:visited {
        display: inline-flex;
        align-items: center;
        gap: var(--v2-space-2);
        margin-bottom: -1px;
        padding: var(--v2-space-1) 2px var(--v2-space-3);
        border-bottom: 2px solid transparent;
        color: var(--v2-text-muted);
        font-weight: 600;
        text-decoration: none;
    }

        .v2 .v2-popup-tabs a.v2-popup-tab:hover {
            color: var(--v2-text);
        }

        .v2 .v2-popup-tabs a.v2-popup-tab.is-selected {
            border-bottom-color: var(--v2-primary);
            color: var(--v2-primary-strong);
        }

        .v2 .v2-popup-tabs a.v2-popup-tab:focus-visible {
            outline: none;
            box-shadow: 0 0 0 3px var(--v2-focus-ring);
        }

/* stepper without a RadWizard: div.v2-stepper > span.v2-stepper-step (+ is-current / is-done) holding a
   span.v2-stepper-title. small numbered circles in one line, joined by short lines, for a wizard whose steps
   are panels the code-behind shows. the circles are numbered by a css counter */
.v2 .v2-stepper {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--v2-space-2);
    counter-reset: v2-stepper-step;
}

    .v2 .v2-stepper .v2-stepper-step {
        display: inline-flex;
        align-items: center;
        gap: var(--v2-space-2);
        counter-increment: v2-stepper-step;
        color: var(--v2-text-subtle);
        font-size: var(--v2-font-small);
        font-weight: 600;
    }

        /* the number circle */
        .v2 .v2-stepper .v2-stepper-step::before {
            content: counter(v2-stepper-step);
            display: inline-flex;
            align-items: center;
            justify-content: center;
            width: 22px;
            height: 22px;
            border: 1px solid var(--v2-border-strong);
            border-radius: 50%;
            background: var(--v2-surface);
            font-size: 11px;
            font-weight: 700;
        }

        /* the line to the next step */
        .v2 .v2-stepper .v2-stepper-step::after {
            content: "";
            width: var(--v2-space-5);
            height: 1px;
            margin-left: var(--v2-space-1);
            background: var(--v2-border-strong);
        }

        .v2 .v2-stepper .v2-stepper-step:last-child::after {
            display: none;
        }

        .v2 .v2-stepper .v2-stepper-step.is-current {
            color: var(--v2-primary-strong);
        }

            .v2 .v2-stepper .v2-stepper-step.is-current::before {
                border-color: var(--v2-primary);
                background: var(--v2-primary);
                color: var(--v2-on-primary);
            }

        /* a step behind the current one shows a check instead of its number */
        .v2 .v2-stepper .v2-stepper-step.is-done {
            color: var(--v2-primary-strong);
        }

            .v2 .v2-stepper .v2-stepper-step.is-done::before {
                content: "\2713";
                border-color: var(--v2-primary-border);
                background: var(--v2-primary-subtle);
            }

/* chip list: an asp:RadioButtonList with RepeatLayout="Flow" and CssClass="v2-chip-list" draws each item
   as a pill, and the picked one fills with the brand color. one choice from a short set of peer values (e.g.
   an image's style or shape). the radios are only hidden visually, so the arrow keys, the focus ring and
   AutoPostBack still work. a disabled item (e.g. a shape before a style is picked) fades out */
.v2 .v2-chip-list {
    display: flex;
    flex-wrap: wrap;
    gap: var(--v2-space-2);
}

    .v2 .v2-chip-list input[type="radio"] {
        position: absolute;
        width: 1px;
        height: 1px;
        opacity: 0;
    }

    .v2 .v2-chip-list label {
        display: inline-flex;
        align-items: center;
        min-height: 30px;
        margin: 0;
        padding: 0 var(--v2-space-3);
        border: 1px solid var(--v2-border-strong);
        border-radius: var(--v2-radius-pill);
        background: var(--v2-surface);
        color: var(--v2-text);
        font-size: var(--v2-font-small);
        font-weight: 600;
        cursor: pointer;
        transition: background-color 0.15s ease, border-color 0.15s ease;
    }

        .v2 .v2-chip-list label:hover {
            border-color: var(--v2-primary-border);
            background: var(--v2-primary-subtle);
        }

    .v2 .v2-chip-list input[type="radio"]:checked + label {
        border-color: var(--v2-primary);
        background: var(--v2-primary);
        color: var(--v2-on-primary);
    }

    .v2 .v2-chip-list input[type="radio"]:focus-visible + label {
        box-shadow: 0 0 0 3px var(--v2-focus-ring);
    }

    .v2 .v2-chip-list input[type="radio"]:disabled + label {
        opacity: 0.45;
        cursor: not-allowed;
    }

        .v2 .v2-chip-list input[type="radio"]:disabled + label:hover {
            border-color: var(--v2-border-strong);
            background: var(--v2-surface);
        }

/* row list: div.v2-row-list of .v2-row-list-item, the same kind of thing several times in one bordered box with
   a line between (e.g. the images of the cover wizard), instead of a card each. an item is a
   .v2-row-list-name column (a title and an optional v2-form-hint), a .v2-row-list-body and an optional
   .v2-row-list-end on the right (e.g. a v2-switch). an item with is-off is switched off: it keeps its name and
   its switch on a muted row, and its body is hidden until it is switched on again */
.v2 .v2-row-list {
    border: 1px solid var(--v2-border);
    border-radius: var(--v2-radius-md);
    overflow: hidden;
}

.v2 .v2-row-list-item {
    display: grid;
    grid-template-columns: 96px minmax(0, 1fr) auto;
    align-items: start;
    gap: var(--v2-space-2) var(--v2-space-4);
    padding: var(--v2-space-3) var(--v2-space-4);
}

    .v2 .v2-row-list-item + .v2-row-list-item {
        border-top: 1px solid var(--v2-border);
    }

.v2 .v2-row-list-name {
    display: flex;
    flex-direction: column;
    padding-top: 5px;
    font-weight: 700;
}

.v2 .v2-row-list-body {
    display: flex;
    flex-direction: column;
    gap: var(--v2-space-2);
    min-width: 0;
}

.v2 .v2-row-list-end {
    grid-column: 3;
    padding-top: 3px;
}

/* stacked: the name runs across the top of the row, with a .v2-row-list-meta beside it on the same line
   (e.g. "Image 1  Icons and Shapes · Square"), and the body under it at full width */
.v2 .v2-row-list-item.v2-row-list-item-stacked {
    grid-template-columns: minmax(0, 1fr);
}

    .v2 .v2-row-list-item.v2-row-list-item-stacked .v2-row-list-name {
        flex-direction: row;
        flex-wrap: wrap;
        align-items: baseline;
        gap: var(--v2-space-1) var(--v2-space-3);
        padding-top: 0;
    }

.v2 .v2-row-list-meta {
    color: var(--v2-text-muted);
    font-weight: 700;
}

/* one setting inside a row's body: a .v2-field-label and its control on one line, wrapping on a narrow row */
.v2 .v2-row-list-field {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--v2-space-2) var(--v2-space-3);
}

    .v2 .v2-row-list-field > .v2-field-label {
        min-width: 180px;
    }

    .v2 .v2-row-list-field > .v2-input {
        width: auto;
        min-width: 200px;
    }

/* October 19, 2026 Gevin Valerian (https://app.asana.com/1/5405414973450/project/218888662811289/task/1219597889857541)
   option row: one image of the cover wizard's set-up page. the name column holds a v2-switch before the name
   (.v2-row-list-name-switch, Tom: "move the toggle to the left"), the body its settings, and the end column a draft
   tile with its buttons stacked under it (.v2-row-list-end-stack). the field labels are narrower than in a plain
   row, because the end column takes room too */
.v2 .v2-row-list-item.v2-row-list-item-option {
    grid-template-columns: 120px minmax(0, 1fr) 140px;
    align-items: start;
}

    /* every setting is one line of the same height, its label in one fixed column, so the controls start on one
       edge and the rows read as a form rather than as loose pieces */
    .v2 .v2-row-list-item.v2-row-list-item-option .v2-row-list-body {
        gap: var(--v2-space-3);
    }

    .v2 .v2-row-list-item.v2-row-list-item-option .v2-row-list-field {
        flex-wrap: nowrap;
        min-height: 32px;
    }

        .v2 .v2-row-list-item.v2-row-list-item-option .v2-row-list-field > .v2-field-label {
            flex: 0 0 140px;
            min-width: 0;
            color: var(--v2-text-muted);
            font-weight: 600;
        }

        /* a text box in the row (the prompt) fills the settings column and never runs past it */
        .v2 .v2-row-list-item.v2-row-list-item-option .v2-row-list-body textarea.v2-input {
            width: 100%;
            max-width: 100%;
            box-sizing: border-box;
        }

        /* the color box at the toggles' height */
        .v2 .v2-row-list-item.v2-row-list-item-option .v2-color-input {
            width: 44px;
            height: 32px;
        }

        /* a one-line text box in a setting (the cover's "Name on image") at the toggles' height, as wide as a
           long course name needs but never past the settings column */
        .v2 .v2-row-list-item.v2-row-list-item-option .v2-row-list-field > input.v2-input {
            flex: 0 1 360px;
            min-width: 0;
            height: 32px;
            padding-top: 0;
            padding-bottom: 0;
        }

        /* the hex code box beside the color box, at the same height */
        .v2 .v2-row-list-item.v2-row-list-item-option input.v2-color-code {
            height: 32px;
        }

/* the switch lines up with the first setting's line (32px tall, the switch 24px) */
.v2 .v2-row-list-name.v2-row-list-name-switch {
    flex-direction: row;
    align-items: center;
    gap: var(--v2-space-2);
    min-height: 32px;
    padding-top: 0;
}

    .v2 .v2-row-list-name.v2-row-list-name-switch .v2-switch label {
        margin: 0;
    }

    /* a row that can't be switched off (option 1) shows its switch on at full color rather than faded: it is the
       state, not something unavailable */
    .v2 .v2-row-list-name.v2-row-list-name-switch .v2-switch input[type="checkbox"]:disabled:checked {
        opacity: 1;
        cursor: default;
    }

/* the draft column: the tile and its buttons all 140px wide, so they stack as one block */
.v2 .v2-row-list-end.v2-row-list-end-stack {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--v2-space-2);
    padding-top: 0;
}

    .v2 .v2-row-list-end.v2-row-list-end-stack > .v2-image-choice {
        width: 140px;
        padding: 0;
        border: 0;
    }

        /* no draft yet: an outlined empty slot, the same size as the draft that will fill it */
        .v2 .v2-row-list-end.v2-row-list-end-stack > .v2-image-choice .v2-image-choice-placeholder:not(.is-pending):not(.is-failed) {
            border: 1px dashed var(--v2-border-strong);
            background: var(--v2-surface);
        }

    .v2 .v2-row-list-end.v2-row-list-end-stack > .v2-btn {
        width: 100%;
        min-width: 0;
    }

/* a narrow option row (the popup on a small window): the draft column drops under the settings. the list is the
   size container, so this follows the popup's width rather than the window's */
.v2 .v2-row-list:has(> .v2-row-list-item-option) {
    container-type: inline-size;
}

@container (max-width: 760px) {
    .v2 .v2-row-list-item.v2-row-list-item-option {
        grid-template-columns: 120px minmax(0, 1fr);
    }

    .v2 .v2-row-list-end.v2-row-list-end-stack {
        grid-column: 2;
        align-items: flex-start;
    }
}

/* October 19, 2026 Gevin Valerian
   filling image list: add v2-image-choice-list-fill to a v2-image-choice-list that never holds more than five pictures
   (a round of the cover wizard). they share the row five across and each is as big as that allows, keeping its shape
   (square, 3:2 rectangle, circle), instead of the fixed 140px tile. on a narrow list they wrap, at least 150px each.
   October 20, 2026 Gevin Valerian (https://app.asana.com/1/5405414973450/project/218888662811289/task/1219670014249332):
   Tom saw a rectangle beside a circle sit at the top of the row and look off, so the tiles are centred on the row's
   height instead */
.v2 .v2-image-choice-list.v2-image-choice-list-fill {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(max(150px, calc((100% - 4 * var(--v2-space-3)) / 5)), 1fr));
    align-items: center;
}

    .v2 .v2-image-choice-list.v2-image-choice-list-fill .v2-image-choice {
        width: auto;
    }

        .v2 .v2-image-choice-list.v2-image-choice-list-fill .v2-image-choice img,
        .v2 .v2-image-choice-list.v2-image-choice-list-fill .v2-image-choice .v2-image-choice-placeholder {
            width: 100%;
            height: auto;
            aspect-ratio: 1 / 1;
        }

        .v2 .v2-image-choice-list.v2-image-choice-list-fill .v2-image-choice.v2-image-choice-rectangle img,
        .v2 .v2-image-choice-list.v2-image-choice-list-fill .v2-image-choice.v2-image-choice-rectangle .v2-image-choice-placeholder {
            aspect-ratio: 3 / 2;
        }

/* compact choice toggle: the same joined toggle, 32px tall, for a row with several of them (the cover wizard's style,
   shape and options). every answer is one line and the answers of one toggle are all as wide as the widest, so a
   long name ("Symbolic Graphics") never wraps or makes its segment stand out. on a narrow row the answers go back to
   their own widths and the toggle wraps */
.v2 .v2-choice-toggle.v2-choice-toggle-compact {
    display: inline-grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
}

    .v2 .v2-choice-toggle.v2-choice-toggle-compact label {
        width: 100%;
        min-width: 96px;
        min-height: 32px;
        padding: 0 var(--v2-space-3);
        font-size: var(--v2-font-small);
        white-space: nowrap;
    }

@container (max-width: 760px) {
    .v2 .v2-choice-toggle.v2-choice-toggle-compact {
        display: inline-flex;
    }

    .v2 .v2-row-list-item.v2-row-list-item-option .v2-row-list-field {
        flex-wrap: wrap;
    }
}

/* file pick: a v2-link that opens the file dialog, for one small file read by the page script (the cover wizard's
   logo). the file input sits inside the label, hidden visually only, so the label opens it and keyboard focus still
   reaches it. also holds the picked file's name and a Remove link, on one line */
.v2 .v2-file-pick {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--v2-space-2);
}

    /* drawn as a small secondary button, 32px like the toggles beside it, so it reads as a control and not a stray link */
    .v2 .v2-file-pick label {
        position: relative;
        display: inline-flex;
        align-items: center;
        gap: var(--v2-space-2);
        min-height: 32px;
        margin: 0;
        padding: 0 var(--v2-space-3);
        border: 1px solid var(--v2-border-strong);
        border-radius: var(--v2-radius-sm);
        background: var(--v2-surface);
        color: var(--v2-text);
        font-size: var(--v2-font-small);
        font-weight: 600;
        cursor: pointer;
    }

        .v2 .v2-file-pick label:hover {
            background: var(--v2-primary-subtle);
            color: var(--v2-primary-strong);
        }

    .v2 .v2-file-pick input[type="file"] {
        position: absolute;
        width: 1px;
        height: 1px;
        opacity: 0;
    }

    .v2 .v2-file-pick label:focus-within {
        box-shadow: 0 0 0 3px var(--v2-focus-ring);
        border-radius: var(--v2-radius-sm);
    }

/* a secondary button that is switched on (e.g. a draft's "Accepted"): success colors, the same under every theme,
   so it reads as done rather than as the next thing to press */
.v2 .v2-btn.v2-btn-secondary.is-on,
.v2 .v2-btn.v2-btn-secondary.is-on:hover {
    border-color: var(--v2-success);
    background: var(--v2-success-subtle);
    color: var(--v2-success);
}

.v2 .v2-row-list-item.is-off {
    background: var(--v2-surface-muted);
}

    .v2 .v2-row-list-item.is-off .v2-row-list-name {
        color: var(--v2-text-muted);
    }

    .v2 .v2-row-list-item.is-off .v2-row-list-body {
        display: none;
    }

/* colour picker: an asp:TextBox TextMode="Color" with CssClass="v2-color-input", then a .v2-color-code the
   page script fills with the picked code, in a span.v2-color-picker. the box only shows a swatch, so the code
   is written beside it. October 20, 2026 Gevin Valerian: the code can be an input[type=text].v2-color-code, so
   the admin can type or paste a hex code; the page script copies a valid one into the colour box */
.v2 .v2-color-picker {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--v2-space-2);
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
}

.v2 .v2-color-input {
    width: 32px;
    height: 26px;
    padding: 1px;
    border: 1px solid var(--v2-border-strong);
    border-radius: var(--v2-radius-sm);
    background: var(--v2-surface);
    cursor: pointer;
}

    .v2 .v2-color-input:focus-visible {
        outline: none;
        box-shadow: 0 0 0 3px var(--v2-focus-ring);
    }

.v2 .v2-color-code {
    font-family: Consolas, "Courier New", monospace;
    color: var(--v2-text);
}

    /* the editable code: a small box as tall as the swatch, wide enough for "#1F2E61" */
    .v2 input.v2-color-code {
        width: 10ch;
        height: 26px;
        padding: 0 var(--v2-space-2);
        border: 1px solid var(--v2-border-strong);
        border-radius: var(--v2-radius-sm);
        background: var(--v2-surface);
        font-size: var(--v2-font-small);
        text-transform: uppercase;
    }

        .v2 input.v2-color-code:focus-visible {
            outline: none;
            box-shadow: 0 0 0 3px var(--v2-focus-ring);
        }

        /* October 21, 2026 Gevin Valerian: the code as a grey tag, like the hex label beside the color pickers on the
           older admin pages (add v2-color-code-tag to the .v2-color-code) */
        .v2 .v2-color-code.v2-color-code-tag {
            display: inline-flex;
            align-items: center;
            min-height: 28px;
            padding: 0 var(--v2-space-3);
            border-radius: var(--v2-radius-sm);
            background: #e8e8e8;
            color: rgba(0, 0, 0, .6);
            font-weight: 700;
        }

        /* a code that is not a hex color yet, while it is being typed */
        .v2 input.v2-color-code.is-invalid {
            border-color: var(--v2-danger);
        }

/* disclosure: details.v2-disclosure with a summary.v2-disclosure-toggle, for a block of read-only text that
   is closed by default (the AI prompt in UI test mode). unlike .v2-collapse it is not a card, so it can sit
   inside one. the toggle reads as a small secondary button; put a .v2-disclosure-closed and a
   .v2-disclosure-open label in it to swap its words, and a .v2-long-text after it for the content */
.v2 .v2-disclosure > .v2-disclosure-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--v2-space-2);
    min-height: 30px;
    padding: 0 var(--v2-space-3);
    border: 1px solid var(--v2-border-strong);
    border-radius: var(--v2-radius-sm);
    background: var(--v2-surface);
    color: var(--v2-primary-strong);
    font-size: var(--v2-font-small);
    font-weight: 600;
    list-style: none;
    cursor: pointer;
}

    .v2 .v2-disclosure > .v2-disclosure-toggle::-webkit-details-marker {
        display: none;
    }

    .v2 .v2-disclosure > .v2-disclosure-toggle:hover {
        border-color: var(--v2-primary-border);
        background: var(--v2-primary-subtle);
    }

    .v2 .v2-disclosure > .v2-disclosure-toggle:focus-visible {
        outline: none;
        box-shadow: 0 0 0 3px var(--v2-focus-ring);
    }

.v2 .v2-disclosure[open] .v2-disclosure-closed,
.v2 .v2-disclosure:not([open]) .v2-disclosure-open {
    display: none;
}

.v2 .v2-disclosure > .v2-long-text {
    max-height: 260px;
    margin-top: var(--v2-space-2);
    padding: var(--v2-space-3);
    overflow: auto;
    border: 1px solid var(--v2-border);
    border-radius: var(--v2-radius-sm);
    background: var(--v2-surface-muted);
    font-size: 12px;
    white-space: pre-wrap;
}

/* image choice: div.v2-image-choice-list holding one .v2-image-choice per picture to pick from (gallery
   covers, generated covers). each is an <img> (or a .v2-image-choice-placeholder while it is not ready) and
   an optional .v2-image-choice-caption. the shape class sizes it: v2-image-choice-square (the default),
   -rectangle (3:2) or -circle (cropped round). is-selectable makes it pressable, and the page script adds
   is-selected to the one picked */
.v2 .v2-image-choice-list {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--v2-space-3);
}

/* October 20, 2026 Gevin Valerian (https://app.asana.com/1/5405414973450/project/218888662811289/task/1219572175539332)
   image choice badge: a span.v2-image-choice-badge (an <i> + a word) inside a .v2-image-choice, after the picture
   (and its caption). the cover wizard's "In gallery" and the gallery's "AI".
   October 21, 2026 Gevin Valerian: it sits under the picture as a small muted tag, not over it, because laid on the
   picture it read as part of the image */
.v2 .v2-image-choice-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--v2-space-1);
    padding: 1px var(--v2-space-2);
    border-radius: 999px;
    background: var(--v2-surface-muted, var(--v2-surface));
    border: 1px solid var(--v2-border);
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
    line-height: 1.4;
    white-space: nowrap;
}

    .v2 .v2-image-choice-badge > i {
        color: var(--v2-primary);
    }

.v2 .v2-image-choice {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--v2-space-2);
    width: 154px;
    max-width: 100%;
    padding: var(--v2-space-1);
    border: 2px solid transparent;
    border-radius: var(--v2-radius-md);
    color: var(--v2-text);
    text-align: center;
    text-decoration: none;
}

    /* the picture, or the place it will appear: the same box, so a tile does not change size as it finishes */
    .v2 .v2-image-choice img,
    .v2 .v2-image-choice .v2-image-choice-placeholder {
        flex: none;
        width: 140px;
        height: 140px;
        max-width: 100%;
        border-radius: var(--v2-radius-sm);
    }

    .v2 .v2-image-choice img {
        display: block;
        object-fit: cover;
        box-shadow: var(--v2-shadow-sm);
    }

    .v2 .v2-image-choice.v2-image-choice-rectangle img,
    .v2 .v2-image-choice.v2-image-choice-rectangle .v2-image-choice-placeholder {
        width: 144px;
        height: 96px;
    }

    .v2 .v2-image-choice.v2-image-choice-circle img,
    .v2 .v2-image-choice.v2-image-choice-circle .v2-image-choice-placeholder {
        width: 132px;
        height: 132px;
        border-radius: 50%;
    }

    .v2 .v2-image-choice.is-selectable {
        cursor: pointer;
    }

        .v2 .v2-image-choice.is-selectable:hover {
            background: var(--v2-surface-muted);
        }

        .v2 .v2-image-choice.is-selectable:focus-visible {
            outline: none;
            box-shadow: 0 0 0 3px var(--v2-focus-ring);
        }

    .v2 .v2-image-choice.is-selected,
    .v2 .v2-image-choice.is-selected:hover {
        border-color: var(--v2-primary);
        background: var(--v2-primary-subtle);
    }

/* one line, cut with an ellipsis, so every tile in a row is the same height; the full text is the tile's
   tooltip (its title) */
.v2 .v2-image-choice-caption {
    display: block;
    max-width: 100%;
    overflow: hidden;
    color: var(--v2-text-muted);
    font-size: 12px;
    line-height: 1.35;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.v2 .v2-image-choice.is-selected .v2-image-choice-caption {
    color: var(--v2-primary-strong);
    font-weight: 600;
}

/* the place an image will appear. is-pending while it is being made: a brand tint with a light sweeping
   across it, and in the middle a .v2-image-choice-label ("Drawing"), a .v2-image-choice-progress > i bar
   the page script widens and a .v2-image-choice-elapsed seconds count, stacked. is-failed when it could not
   be made */
.v2 .v2-image-choice-placeholder {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--v2-space-2);
    overflow: hidden;
    background: var(--v2-surface-muted);
    color: var(--v2-text-muted);
    font-size: 12px;
}

    .v2 .v2-image-choice-placeholder.is-pending {
        background: var(--v2-primary-subtle);
        color: var(--v2-primary-strong);
    }

        .v2 .v2-image-choice-placeholder.is-pending::before {
            content: "";
            position: absolute;
            inset: 0;
            background: linear-gradient(100deg, transparent 25%, color-mix(in srgb, var(--v2-surface) 65%, transparent) 50%, transparent 75%);
            transform: translateX(-100%);
            animation: v2-image-choice-sweep 2.2s ease-in-out infinite;
        }

    .v2 .v2-image-choice-placeholder.is-failed {
        padding: var(--v2-space-3);
        background: var(--v2-danger-subtle);
        color: var(--v2-danger);
    }

    /* everything inside sits above the sweep */
    .v2 .v2-image-choice-placeholder > * {
        position: relative;
    }

.v2 .v2-image-choice-label {
    font-size: var(--v2-font-small);
    font-weight: 700;
}

/* fills toward the usual wait and slows near the end, so a slow image never looks finished */
.v2 .v2-image-choice-progress {
    display: block;
    width: 70px;
    height: 4px;
    border-radius: var(--v2-radius-pill);
    background: color-mix(in srgb, currentColor 18%, transparent);
    overflow: hidden;
}

    .v2 .v2-image-choice-progress i {
        display: block;
        width: 0;
        height: 100%;
        border-radius: var(--v2-radius-pill);
        background: currentColor;
        transition: width 1s linear;
    }

.v2 .v2-image-choice-elapsed {
    font-size: 12px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

@keyframes v2-image-choice-sweep {
    to {
        transform: translateX(100%);
    }
}

/* October 21, 2026 Gevin Valerian (https://app.asana.com/1/5405414973450/project/218888662811289/task/1219670014249313)
   revealing image choice: add is-revealing to a .v2-image-choice that already has its <img> and also its is-pending
   placeholder, and set --v2-reveal-delay on it (e.g. style="--v2-reveal-delay: 900ms"). the placeholder lies over the
   picture with its bar filling for that long, then fades away as the picture fades in. for an image that was ready at
   once (the cover wizard's draft used as it is), so it still shows a short "drawing" moment. pure css: nothing to
   clean up when the tile is redrawn */
.v2 .v2-image-choice.is-revealing {
    position: relative;
}

    .v2 .v2-image-choice.is-revealing > .v2-image-choice-placeholder {
        position: absolute;
        top: var(--v2-space-1);
        left: var(--v2-space-1);
        right: var(--v2-space-1);
        width: auto;
        animation: v2-image-choice-reveal-out 250ms ease var(--v2-reveal-delay, 1s) forwards;
    }

    .v2 .v2-image-choice.is-revealing > img {
        animation: v2-image-choice-reveal-in 250ms ease var(--v2-reveal-delay, 1s) both;
    }

    .v2 .v2-image-choice.is-revealing .v2-image-choice-progress i {
        transition: none;
        animation: v2-image-choice-reveal-bar var(--v2-reveal-delay, 1s) linear forwards;
    }

@keyframes v2-image-choice-reveal-out {
    to {
        opacity: 0;
        visibility: hidden;
    }
}

@keyframes v2-image-choice-reveal-in {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

@keyframes v2-image-choice-reveal-bar {
    from {
        width: 0;
    }

    to {
        width: 100%;
    }
}

@media (prefers-reduced-motion: reduce) {
    .v2 .v2-image-choice-placeholder.is-pending::before {
        animation: none;
    }

    .v2 .v2-image-choice-progress i {
        transition: none;
    }
}

/* --------------------------------------------------------------------------
   Pick pair list (keep one of two cards, row by row)
   -------------------------------------------------------------------------- */
/* rows of two peer cards where the admin keeps one card per row (e.g. the merge source's and the merge
   target's copy of a duplicated course on Admin/User/MergeUserAccount.aspx). markup:
   div.v2-pick-pair > div.v2-pick-pair-head (two .v2-pick-pair-head-cell: a title + its "keep all" button),
   then div.v2-pick-pair-list > div.v2-pick-pair-row (one per pair) > div.v2-card.v2-pick-card,
   div.v2-pick-pair-divider ("Select one" over a two-headed arrow), div.v2-card.v2-pick-card.
   rows alternate white and muted so each pair reads as one, and both cards in a row share a height.
   .v2-pick-pair is a size container: under 640px the head hides and the two cards of a row stack
   with the arrow turned upright between them (each card names its side in a .v2-pick-card-side line,
   so nothing is lost) */
.v2-pick-pair {
    container-type: inline-size;
    margin-top: var(--v2-space-4);
}

.v2-pick-pair-head,
.v2-pick-pair-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 80px minmax(0, 1fr);
    gap: var(--v2-space-4);
}

.v2-pick-pair-head {
    margin-bottom: var(--v2-space-3);
    padding: 0 var(--v2-space-4);
}

    /* a column title on the left and its "keep all" button on the right, its right edge lined up with the right
       edge of the cards below (the head and the rows use the same side padding); the button wraps under a long title */
    .v2-pick-pair-head-cell {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        justify-content: space-between;
        gap: var(--v2-space-2) var(--v2-space-3);
    }

        .v2 .v2-pick-pair-head-cell .v2-section-title {
            margin-bottom: 0;
        }

        /* the account's email after the column name (e.g. "Merge Source: name@example.com"): regular weight so
           the column name still leads, and a long address wraps instead of pushing the button off the card */
        .v2 .v2-pick-pair-head-email {
            font-weight: 400;
            overflow-wrap: anywhere;
        }

        /* the second title skips the arrow column, so both titles sit right above their cards */
        .v2-pick-pair-head-cell + .v2-pick-pair-head-cell {
            grid-column: 3;
        }

.v2-pick-pair-list {
    overflow: hidden;
    border: 1px solid var(--v2-border);
    border-radius: var(--v2-radius-md);
}

.v2-pick-pair-row {
    padding: var(--v2-space-4);
    background: var(--v2-surface);
}

    .v2-pick-pair-row + .v2-pick-pair-row {
        border-top: 1px solid var(--v2-border);
    }

    .v2-pick-pair-row:nth-child(even) {
        background: var(--v2-surface-muted);
    }

/* one card in a row, pressed as a whole. the radio inside carries the pick: it is hidden visually only, so
   the arrow keys, the focus ring and the form post still use it, and the page script checks it when the card
   is clicked. the picked card takes the brand border and tint, and the circle in its corner fills with a
   check, so the state doesn't rest on color alone */
.v2 .v2-pick-card {
    display: flex;
    flex-direction: column;
    gap: var(--v2-space-3);
    min-width: 0;
    padding-right: var(--v2-space-8);
    border-width: 2px;
    cursor: pointer;
    transition: border-color 0.15s ease, background-color 0.15s ease, opacity 0.15s ease;
}

    .v2 .v2-pick-card:hover {
        border-color: var(--v2-border-strong);
    }

    .v2 .v2-pick-card:has(input:focus-visible) {
        box-shadow: 0 0 0 3px var(--v2-focus-ring);
    }

    .v2 .v2-pick-card:has(input:checked) {
        border-color: var(--v2-primary);
        background: var(--v2-primary-subtle);
    }

    .v2 .v2-pick-card-input {
        position: absolute;
        top: 0;
        left: 0;
    }

        .v2 .v2-pick-card-input input {
            width: 1px;
            height: 1px;
            margin: 0;
            opacity: 0;
            pointer-events: none;
        }

    .v2 .v2-pick-card-check {
        position: absolute;
        top: var(--v2-space-4);
        right: var(--v2-space-4);
        color: var(--v2-border-strong);
        font-size: 20px;
    }

        .v2 .v2-pick-card-check .fa-circle-check,
        .v2 .v2-pick-card:has(input:checked) .v2-pick-card-check .fa-circle {
            display: none;
        }

        .v2 .v2-pick-card:has(input:checked) .v2-pick-card-check .fa-circle-check {
            display: inline-block;
            color: var(--v2-primary-strong);
        }

    /* which side the card belongs to. the column titles above the list already say it, so this line is
       hidden on a wide page and only shows once the cards stack and those titles hide (container query below) */
    .v2 .v2-pick-card-side {
        display: none;
        color: var(--v2-text-muted);
        font-size: var(--v2-font-small);
        font-weight: 600;
        letter-spacing: 0.04em;
        text-transform: uppercase;
    }

    /* the card's flex gap spaces its parts, so the item title drops its own bottom margin */
    .v2 .v2-pick-card .v2-item-card-title {
        margin-bottom: 0;
    }

    /* once the row has a pick, the other card fades and says it will be deleted, so the outcome doesn't rest on
       the highlight alone. hovering or focusing it brings it back to full strength, since clicking it still
       switches the pick */
    .v2 .v2-pick-pair-row:has(.v2-pick-card input:checked) .v2-pick-card:not(:has(input:checked)) {
        opacity: 0.55;
    }

        .v2 .v2-pick-pair-row:has(.v2-pick-card input:checked) .v2-pick-card:not(:has(input:checked)):hover,
        .v2 .v2-pick-pair-row:has(.v2-pick-card input:checked) .v2-pick-card:not(:has(input:checked)):focus-within {
            opacity: 1;
        }

    /* "Will be deleted": a v2-status-danger pill at the top of a card, shown only on the card that loses */
    .v2 .v2-pick-card-lose {
        display: none;
        align-self: flex-start;
    }

    .v2 .v2-pick-pair-row:has(.v2-pick-card input:checked) .v2-pick-card:not(:has(input:checked)) .v2-pick-card-lose {
        display: inline-flex;
    }

/* the column between a row's two cards. it holds three states and shows one: "Select one" over a gray
   two-headed arrow until a card in the row is picked, then "Keeping" with a brand-colored arrow pointing at the
   kept card (.v2-pick-pair-divider-keep-start / -keep-end). the radios decide which through :has, so a click,
   the keyboard or a "keep all" button all update it with no script. decorative only (the heading says what to do) */
.v2-pick-pair-divider {
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--v2-text-muted);
    font-size: var(--v2-font-small);
    font-weight: 600;
    text-align: center;
}

    .v2-pick-pair-divider > span {
        display: none;
        flex-direction: column;
        align-items: center;
        gap: var(--v2-space-2);
    }

    .v2-pick-pair-divider i {
        font-size: 28px;
    }

    /* nothing picked yet */
    .v2-pick-pair-divider > .v2-pick-pair-divider-idle {
        display: flex;
    }

        .v2-pick-pair-divider-idle i {
            color: var(--v2-text-subtle);
        }

    .v2-pick-pair-row:has(.v2-pick-card input:checked) .v2-pick-pair-divider > .v2-pick-pair-divider-idle {
        display: none;
    }

    /* a card is picked: "Keeping" and a bigger solid arrow in the brand color, pointing at it */
    .v2-pick-pair-row:has(.v2-pick-card-start input:checked) .v2-pick-pair-divider > .v2-pick-pair-divider-keep-start,
    .v2-pick-pair-row:has(.v2-pick-card-end input:checked) .v2-pick-pair-divider > .v2-pick-pair-divider-keep-end {
        display: flex;
        color: var(--v2-primary-strong);
        font-weight: 700;
    }

        .v2-pick-pair-divider-keep-start i,
        .v2-pick-pair-divider-keep-end i {
            font-size: 32px;
        }

/* "12 of 30 picked" and the Next unpicked link, right above the list */
.v2-pick-pair-status {
    margin-top: var(--v2-space-3);
}

@container (max-width: 640px) {
    .v2-pick-pair-head {
        display: none;
    }

    /* the column titles are gone, so each card names its own side again */
    .v2 .v2-pick-card-side {
        display: block;
    }

    .v2-pick-pair-row {
        grid-template-columns: minmax(0, 1fr);
    }

    /* stacked cards: label and arrow side by side between them. the arrows turn upright, so "Keeping" points up at
       the card above (start) or down at the card below (end) */
    .v2-pick-pair-divider > span {
        flex-direction: row;
    }

        .v2-pick-pair-divider i {
            transform: rotate(90deg);
        }
}

/* --------------------------------------------------------------------------
   Responsive
   -------------------------------------------------------------------------- */

/* grids need no breakpoints; they collapse on their own (see .v2-grid-2) */

/* narrow action card: smaller icon, so a primary + secondary button pair still
   fits on one line. note that container queries measure the card's content width
   (after padding): 430px = 96px icon + 16px gap + about 300px of buttons, plus a
   little headroom */
@container (max-width: 430px) {
    .v2-action-card > .v2-icon-circle {
        width: 72px;
        height: 72px;
        font-size: 28px;
    }
}

/* very narrow action card (phone): smaller again, and the corner badge moves
   into normal flow above the title so it can't overlap it */
@container (max-width: 360px) {
    .v2-action-card > .v2-icon-circle {
        width: 56px;
        height: 56px;
        font-size: 22px;
    }

    .v2-card-corner {
        position: static;
        display: inline-flex;
        margin-bottom: var(--v2-space-3);
    }

    .v2 .v2-action-card-body:has(> .v2-card-corner) > .v2-card-title {
        padding-right: 0;
    }
}

/* narrow form (tablet, or a card squeezed by the side column): the control drops
   under the label instead of sharing its row. measured on .v2-form's own width */
@container (max-width: 620px) {
    .v2-form-row {
        grid-template-columns: 44px minmax(0, 1fr);
    }

    .v2-form-control {
        grid-column: 2 / -1;
    }

    /* a label-fit row stacks too, and its label may wrap again */
    .v2-form-row.v2-form-row-label-fit {
        grid-template-columns: 44px minmax(0, 1fr);
    }

        .v2-form-row-label-fit > .v2-form-label {
            white-space: normal;
        }
}

/* narrow settings card: each setting's control drops under its label. measured on the
   .v2-settings-card's own width */
@container (max-width: 560px) {
    .v2-settings-row {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* narrow quota card (e.g. beside the dashboard's side panel): the note takes a line of its
   own under the figures, with its line above it instead of beside it. measured on the
   .v2-quota card's own width */
@container (max-width: 860px) {
    .v2 .v2-quota-note {
        flex-basis: 100%;
        max-width: none;
        padding: var(--v2-space-3) 0 0;
        border-left: 0;
        border-top: 1px solid var(--v2-card-soft-border);
    }
}

/* phone: the track goes under the title and its steps share the full width */
@container (max-width: 520px) {
    .v2 .v2-quota-track {
        flex-basis: 100%;
    }

        .v2-quota-track > li {
            flex: 1 1 0;
            width: auto;
        }
}

/* phone: form rows lose the icon tile so the control gets the full width */
@container (max-width: 400px) {
    .v2-form-row,
    .v2-form-row.v2-form-row-label-fit,
    .v2-form-row.v2-form-row-stacked {
        grid-template-columns: minmax(0, 1fr);
    }

        .v2-form-row > .v2-icon-tile {
            display: none;
        }

    .v2-form-control,
    .v2-form-row-stacked > .v2-form-control {
        grid-column: 1 / -1;
    }

    /* no icon to line the label up with */
    .v2-form.v2-form-label-center .v2-form-label {
        padding-top: 0;
    }
}

/* a form inside a half-width .v2-section-grid section keeps its label and control side by side, with a narrower
   label column, instead of dropping the control under the label at 620px. it still stacks on a phone (the 400px
   rule above), which is why this range stops at 401px */
@container (min-width: 401px) and (max-width: 620px) {
    .v2-section-grid-items .v2-form-row {
        grid-template-columns: 44px minmax(110px, 180px) minmax(0, 1fr);
    }

    .v2-section-grid-items .v2-form-control {
        grid-column: auto;
    }
}

/* narrow record list: the header row hides and each box stacks its cells, with
   each cell's own label above its value. measured on .v2-record-list's own width */
@container (max-width: 960px) {
    .v2-record-head {
        display: none;
    }

    .v2-record-row {
        flex-wrap: wrap;
        align-items: flex-start;
        gap: var(--v2-space-3) var(--v2-space-5);
    }

    .v2-record-cell {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: var(--v2-space-1);
        width: auto;
        min-width: 120px;
    }

    .v2-record-cell-grow {
        flex-basis: 100%;
    }

    .v2-record-cell-label {
        display: block;
    }
}

/* narrow wide-dialog (a phone, or a small window): its two columns stack, and the line
   between them moves above the second one. measured on .v2-dialog-wide's content width */
@container (max-width: 720px) {
    .v2-dialog-columns {
        grid-template-columns: minmax(0, 1fr);
    }

        .v2-dialog-columns > :first-child {
            padding-right: 0;
        }

        .v2-dialog-columns > * + * {
            margin-top: var(--v2-space-5);
            padding-top: var(--v2-space-5);
            padding-left: 0;
            border-top: 1px solid var(--v2-border);
            border-left: 0;
        }
}

/* narrow section grid (a phone, or a small window): one panel per row. measured on .v2-section-grid's own width */
@container (max-width: 720px) {
    .v2-section-grid-items {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* narrow screens: the stepper keeps every number circle but only shows the
   current step's text, so the steps don't squash into one-word lines */
@media (max-width: 767px) {
    .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzLI:not(.rwzSelected) .rwzText {
        display: none;
    }

    .v2 .v2-wizard.RadWizard .rwzBreadCrumb .rwzLI::after {
        margin: 0 var(--v2-space-2);
    }

    .v2 .v2-wizard .v2-wizard-step-hint {
        display: none;
    }

    .v2-field-grid {
        grid-template-columns: minmax(0, 1fr);
        gap: 0;
    }

        .v2-field-grid > .v2-field-label {
            margin-top: var(--v2-space-2);
        }

    /* the timeline date goes above its text */
    .v2-timeline-item {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* phone: tighter page padding, smaller page title, and the action card
   icon sits at the top (text wraps to many lines here, so a centered icon floats
   in the middle of empty space) */
@media (max-width: 575px) {
    .v2-page {
        padding: var(--v2-space-5);
    }

    .v2-action-card {
        align-items: flex-start;
    }

    .v2 .v2-page-title {
        font-size: 22px;
    }

    /* big figures shrink with the page title */
    .v2 {
        --v2-font-figure: 26px;
    }

    /* indented setting rows would waste a third of a phone's width */
    .v2-setting-bar.v2-setting-bar-indent,
    .v2-card-soft .v2-setting-bar.v2-setting-bar-indent,
    .v2-setting-bar.v2-setting-bar-child {
        padding-left: 0;
    }

    /* the dialog gets tighter padding, and its list uses the full width */
    .v2 .v2-dialog {
        padding: var(--v2-space-5);
    }

    .v2-dialog .v2-item-list {
        margin-left: 0;
    }
}
