/*
 * ciraApps Platform — Component & Layout Styles
 * ------------------------------------------------------------------
 * All rules here consume ONLY the semantic tokens defined in
 * theme-tokens.css. No colors, fonts, spacing, radii or shadows are
 * hardcoded — this keeps the platform re-skinnable by swapping the
 * token layer alone.
 *
 * Sections:
 *   0. Base reset & typography      6. Forms / inputs / toggles
 *   1. Layout primitives            7. Source catalog & cards
 *   2. Wizard step header           8. Auth blocks
 *   3. Panels & surfaces            9. Sync run / connectors
 *   4. Case selection options      10. Dashboard
 *   5. Buttons                     11. Modals & dialogs
 *                                  12. Top nav / user chip / Billing page
 */

/* ============================================================
 * 0. BASE RESET & TYPOGRAPHY
 * ============================================================ */
*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    font-size: 16px;
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    font-family: var(--font-sans);
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-normal);
    line-height: var(--line-height-normal);
    color: var(--color-text);
    background-color: var(--color-bg);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

/* Kendo SVG icon stroke weight — kendo-theme-default's own rule for this
   (`.k-svg-icon.k-svg-icon-outline > svg [fill=none]`) never matches in practice: Blazor's
   <TelerikSvgIcon> only ever emits the base .k-svg-icon class, never a -outline/-solid/-duotone
   modifier (per Telerik.Blazor.xml, Variant only swaps which SVG path content renders — it isn't
   reflected as a CSS class). Verified live via the browser's CSSOM (2026-09-10): the default rule
   matched zero elements and computed stroke-width sat at the browser's SVG-initial 1px regardless
   of --kendo-icon-stroke-width. This broader selector (no modifier-class requirement) is what
   actually reaches our icons — mirrors kendo-theme-default's own icon *color* rule
   (`.k-svg-icon > svg [fill=none] { stroke: var(--kendo-icon-color) }`), which has never had this
   gating problem. */
.k-svg-icon > svg [fill=none] {
    stroke-width: var(--kendo-icon-stroke-width);
}

.platform-heading--large,
.platform-heading--medium,
.platform-heading--small {
    margin: 0;
    color: var(--color-heading);
    font-family: var(--font-heading);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
}

.platform-heading--large  { font-size: var(--font-size-2xl); } /* 28px */
.platform-heading--medium { font-size: var(--font-size-lg); }  /* 18px */
.platform-heading--small  { font-size: var(--font-size-base); }/* 14px */

p {
    margin: 0;
}

a {
    color: var(--color-primary);
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}

ul, ol {
    margin: 0;
}

:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
    border-radius: var(--radius-sm);
}

/* FocusOnNavigate temporarily adds tabindex="-1" to the h1 on route change so
   AT users get moved to the heading; PlatformDialog does the same with its title.
   Neither is Tab-reachable, so the visible ring has no keyboard-user benefit here. */
h1[tabindex="-1"]:focus-visible,
.platform-dialog__title-text:focus-visible {
    box-shadow: none;
}

/* Thin scrollbars everywhere — Firefox */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--color-border-strong) transparent;
}

/* Thin scrollbars everywhere — WebKit/Chromium/Edge */
*::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

*::-webkit-scrollbar-track {
    background: transparent;
}

*::-webkit-scrollbar-thumb {
    background-color: var(--color-border-strong);
    border-radius: var(--radius-full, 9999px);
    border: 2px solid transparent;
    background-clip: content-box;
}

*::-webkit-scrollbar-thumb:hover {
    background-color: var(--color-text-subtle);
}

/* ============================================================
 * 1. LAYOUT PRIMITIVES
 * ============================================================ */
.platform-stack-0 { display: flex; flex-direction: column; gap: var(--space-0); }
.platform-stack-1 { display: flex; flex-direction: column; gap: var(--space-1); }
.platform-stack-2 { display: flex; flex-direction: column; gap: var(--space-2); }
.platform-stack-3 { display: flex; flex-direction: column; gap: var(--space-3); }
.platform-stack-4 { display: flex; flex-direction: column; gap: var(--space-4); }
.platform-stack-5 { display: flex; flex-direction: column; gap: var(--space-5); }
.platform-stack-6 { display: flex; flex-direction: column; gap: var(--space-6); }
.platform-stack-10 { display: flex; flex-direction: column; gap: var(--space-10); }

.platform-shell {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    background-color: var(--color-bg);
}

.platform-shell__body {
    flex: 1 1 auto;
}

/* Branded top bar — present across all onboarding/pre-dashboard screens
 * (CiraSync logo left). Approved chrome per the Figma frames (present in
 * 100% of mirrored frames). */
.platform-topbar {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-4) var(--space-6);
    background-color: var(--color-surface);
}

/* Opt-in via .platform-page--sticky-header (Dashboard/Billing today) — pins the topbar to the
   viewport while the page content scrolls underneath it. Everywhere else the header scrolls
   with the document, same as .platform-page--fixed's opt-in pattern below. */
body:has(.platform-page--sticky-header) .platform-topbar {
    position: sticky;
    top: 0;
    z-index: 10;
}

.platform-topbar__brand {
    display: inline-flex;
    align-items: center;
    text-decoration: none;
}

.platform-topbar__logo {
    height: 16px;
    width: 135px;
}

/* ------------------------------------------------------------
 * HEADER STATUS PILL (R5) — trial/plan indicator relocated from the old
 * in-content TrialBillingBanner into .platform-topbar__nav. Renders
 * nothing when no trial state applies (no empty pill/layout shift).
 * max-width + nowrap + ellipsis covers long tenant/plan names (EC3);
 * no fixed width/min-width per §5.8 so the topbar's existing flex-only,
 * no-media-query narrow-viewport behavior stays unaffected.
 * ------------------------------------------------------------ */
.platform-topbar__nav {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.platform-topbar__trial {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-3);
    background-color: var(--color-tertiary);
    color: var(--p-white);
    border-radius: var(--radius-pill);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    text-decoration: none;
    max-width: 20rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.platform-topbar__trial-label {
    line-height: var(--line-height-tight);
}

.platform-topbar__trial:hover {
    color: var(--p-white);
    text-decoration: none;
}

.platform-topbar__trial:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}

.platform-topbar__trial--expired {
    background-color: var(--color-warning-bg);
    color: var(--color-warning);
    border-color: var(--color-warning);
}

.platform-page {
    width: 100%;
    max-width: var(--layout-max-width);
    margin: 0 auto;
    padding: var(--space-8) var(--space-6);
    color: var(--color-text);
}

.platform-dashboard-layout {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-5);
}

.platform-note {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-normal);
}

.platform-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 6rem;
    padding: var(--space-6);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    text-align: center;
    background-color: var(--color-surface-sunken);
    border: var(--border-width) dashed var(--color-border-strong);
    border-radius: var(--radius-md);
}

/* ------------------------------------------------------------
 * STUB PAGE CHROME (R1) — shared "coming soon" panel wrapper so the
 * Billing and Account destination pages share identical chrome (both
 * reuse .platform-page + .platform-panel + .platform-note already; this
 * is only the thin composition wrapper, no colors/sizes of its own).
 * ------------------------------------------------------------ */
.platform-page-stub {
    align-items: flex-start;
}

@media (min-width: 768px) {
    .platform-two-pane {
        display: grid;
        grid-template-columns: minmax(18rem, 1fr) minmax(0, 1.4fr);
        align-items: start;
    }

    /* Step 2 (compact): the source-cards content panel is dominant and the
     * Selected systems / Preview panel is noticeably narrower. Both columns
     * stretch to the same height so the divider between them spans the full
     * content area (not just the taller column's natural height). */
    .platform-two-pane--content-dominant {
        grid-template-columns: minmax(0, 2fr) minmax(15rem, 1fr);
        align-items: stretch;
    }

    /* Full-height vertical divider between the left source catalog and the
     * right "Selected" panel. Drawn on the right column's leading edge so it
     * sits in the gutter; padding keeps the panel content off the line. */
    .platform-two-pane--content-dominant .platform-panel-right {
        border-left: var(--border-width) solid var(--color-border);
        padding-top: var(--space-8);
        padding-right: var(--space-6);
        padding-left: var(--space-6);
    }

    /* ------------------------------------------------------------
     * FIXED-VIEWPORT WIZARD LAYOUT — opt-in via .platform-page--fixed,
     * set by WizardShell when Compact (Sources/Authorize/Run today). Pins
     * the brand topbar in place and turns each two-pane panel into its own
     * scroll region, so a long catalog or auth-block list scrolls in place
     * instead of scrolling the whole document. The back button/step
     * progress/title and the page's primary CTA live in the fixed flow
     * footer (WizardFooter, position:fixed, see .platform-page__footer) —
     * not part of this in-flow scroll-region layout at all.
     *
     * Desktop-only (this media query) — small screens keep the original
     * single-column, whole-page-scrolls behavior; independently
     * -scrolling regions in a phone-sized fixed viewport is worse UX
     * than just scrolling the page.
     * ------------------------------------------------------------ */
    body:has(.platform-page--fixed) {
        height: 100vh;
        overflow: hidden;
    }

    body:has(.platform-page--fixed) .platform-shell {
        height: 100vh;
    }

    body:has(.platform-page--fixed) .platform-shell__body {
        display: flex;
        flex-direction: column;
        min-height: 0;
        overflow: hidden;
    }

    .platform-page--fixed {
        flex: 1 1 auto;
        min-height: 0;
        overflow: hidden;
        gap: 0;
        /* Mock has zero page-level padding here — each panel insets its own
           content instead (see .platform-panel-left/-right below). Bottom
           edge still gets reserved via .platform-page--has-footer's later,
           more specific padding-bottom. */
        padding: 0;
    }

    .platform-page--fixed .platform-two-pane {
        flex: 1 1 auto;
        min-height: 0;
        grid-template-rows: minmax(0, 1fr);
        overflow: hidden;
        gap: 0;
    }

    .platform-page--fixed .platform-panel {
        border-radius: 0;
    }

    .platform-page--fixed .platform-panel-left {
        min-height: 0;
        overflow-y: auto;
    }

    .platform-page--fixed .platform-panel-right {
        min-height: 0;
        overflow-y: auto;
    }

    /* Case has no two-pane split, so its own outer wrapper plays the same role
     * .platform-panel-left/-right play elsewhere: the one scroll region, full
     * page width so its scrollbar sits at the true page edge (not hugging the
     * narrow .platform-case-content card below), sized to fill the space the
     * pinned footer doesn't need. */
    .platform-page--fixed .platform-case-scroll {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        padding: var(--space-8) var(--space-6);
    }

    .platform-dashboard-layout {
        grid-template-columns: minmax(15rem, 1fr) minmax(0, 3fr);
        align-items: start;
    }

    /* Desktop-only: the footer becomes a normal in-flow flex sibling of .platform-two-pane
       instead of position:fixed (see the comment on .platform-page__footer's base rule for why).
       .platform-two-pane's own flex:1 1 auto / min-height:0 then absorbs exactly whatever space
       the footer doesn't need — no padding-bottom magic number, and it self-adjusts if the
       footer's real height ever changes again. */
    .platform-page--fixed .platform-page__footer {
        position: static;
        left: auto;
        right: auto;
        bottom: auto;
        z-index: auto;
    }

    .platform-page--fixed.platform-page--has-footer {
        padding-bottom: 0;
    }
}

/* ============================================================
 * 2. WIZARD STEP HEADER
 * ============================================================ */

.platform-step-header__steps {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    list-style: none;
    padding: 0;
    margin: 0;
}

.platform-step-header__step {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: var(--radius-pill);
    background-color: var(--color-surface);
    border: var(--border-width-strong) solid var(--color-border-strong);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    transition: background-color var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
}

.platform-step-header__step + .platform-step-header__step {
    position: relative;
    margin-left: var(--space-4);
}

.platform-step-header__step + .platform-step-header__step::before {
    content: "";
    position: absolute;
    left: calc(-1 * var(--space-4) - 0.125rem);
    width: var(--space-4);
    height: var(--border-width-strong);
    background-color: var(--color-border-strong);
}

.platform-step-header__step.is-current {
    background-color: var(--color-primary);
    border-color: var(--color-primary);
    color: var(--color-on-primary);
}

.platform-step-header__step.is-done {
    background-color: var(--color-primary-soft-bg);
    border-color: var(--color-selected-border);
    color: var(--color-primary);
}

/* ------------------------------------------------------------
 * 2b. WIZARD COMPACT HEADER — rendered inline inside the fixed flow
 * footer (WizardFooter, see platform-page__footer section) rather than
 * as a standalone top-of-page block. A single segmented track (filled =
 * current + completed steps) replaces the old split before/after dot
 * lists + pill; "Step X of Y" and the step title render as plain text.
 * ------------------------------------------------------------ */
.platform-compact-header {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

/* .platform-compact-header__back retired — the wizard back button now renders via
   PlatformButton (Variant="Base" Outlined="false" IconOnly="true"). -back-icon (the inner SVG
   sizing rule) is retired too — it only ever set display:block, a workaround for icon-only
   centering that .platform-cta .k-button-text now fixes for every PlatformButton, not just this
   one. */

/* Segment geometry is fixed (10x4 with a 4px gap in every mockup), so a 2-step indicator and a
   4-step one draw identical dashes. Stretching them to fill a fixed track made the width depend on
   the step count. */
.platform-compact-header__progress {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    list-style: none;
    padding: 0;
    margin: 0;
}

.platform-compact-header__dot {
    flex: 0 0 auto;
    width: 10px;
    height: 4px;
    border-radius: var(--radius-pill);
    background-color: color-mix(in srgb, var(--color-heading) 12%, transparent);
    transition: background-color var(--transition-fast);
}

.platform-compact-header__dot.is-done {
    background-color: var(--color-heading);
}

.platform-compact-header__label,
.platform-compact-header__title {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    white-space: nowrap;
}

/* ============================================================
 * 3. PANELS & SURFACES
 * ============================================================ */
.platform-panel {
    padding: var(--space-6);
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--color-border);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-sm);
}

.platform-panel-right {
    background: transparent;
    border: none;
    box-shadow: none;
    padding: 0;
}

/* Step 2 (compact / content-dominant): the source-selection column sits flush on the page like the
   approved mockup — no card border, background or shadow around the grid. Other wizard steps keep
   the default framed panel. */
.platform-two-pane--content-dominant .platform-panel-left {
    background: transparent;
    border: none;
    box-shadow: none;
    padding: 0;
    padding-bottom: var(--space-3);
}

/* Desktop-only: give the left panel back its own top/left/right inset now that
   .platform-page--fixed no longer supplies it at the page level (mock's
   panels each pad their own content). Same selector/specificity as the rule
   above and declared later, so it wins at this breakpoint without touching
   the mobile default (padding:0 + padding-bottom above). */
@media (min-width: 768px) {
    .platform-two-pane--content-dominant .platform-panel-left {
        padding-top: var(--space-8);
        padding-left: var(--space-6);
        padding-right: var(--space-6);
    }
}

.platform-two-pane--content-dominant .platform-panel-right {
    padding-bottom: var(--space-3);
}

.platform-case-intro {
    gap: var(--space-4);
}

.platform-widget,
.platform-finish-setup-card,
.platform-support-card,
.platform-hub-tunnel-widget {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-5);
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--color-border);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-sm);
}

/* ------------------------------------------------------------
 * CARD BOX PRIMITIVE — utility classes for the shared "box"
 * visual properties (background / border / radius / shadow)
 * that several card families in the app hand-roll. Combine with
 * a family's own content/layout class, e.g.
 * class="platform-source-card platform-card platform-card--radius-lofty".
 * ------------------------------------------------------------ */
.platform-card {
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--color-card-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
}

.platform-card--elevation-high  { box-shadow: var(--shadow-lg); }
.platform-card--elevation-mid   { box-shadow: var(--shadow-md); }
.platform-card--elevation-low   { box-shadow: var(--shadow-sm); }
.platform-card--elevation-flat  { box-shadow: none; }

/* Compound selector so this deterministically beats any single-class
   tone/surface-selected border-color, regardless of source order. */
.platform-card.platform-card--outline-none {
    border-color: transparent;
}

.platform-card--surface-subdued  { background-color: var(--color-card-surface-subdued); }
.platform-card--surface-sunken   { background-color: var(--color-surface-sunken); }
.platform-card--surface-selected { background-color: var(--color-selected-bg); border-color: var(--color-selected-border); }
.platform-card--surface-flat     { background-color: transparent; }

/* Tone modifiers set border-color only, not background — a tinted card
   background is a separate, explicit Surface choice above, not something a
   message-type tone implies on its own. "info" has no dedicated token
   today; it reuses --color-primary (this app's brand blue). */
.platform-card--tone-success { border-color: var(--color-success); }
.platform-card--tone-warning { border-color: var(--color-warning); }
.platform-card--tone-danger  { border-color: var(--color-danger); }
.platform-card--tone-info    { border-color: var(--color-primary); }

.platform-card--radius-lofty { border-radius: var(--radius-xl); }

/* ============================================================
 * 4. CASE SELECTION OPTIONS (radio cards)
 * ============================================================ */

.platform-case-option {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-areas:
        "radio title"
        "radio desc";
    column-gap: var(--space-3);
    row-gap: var(--space-1);
    padding: var(--space-4) var(--space-5);
    background-color: var(--color-surface);
    border: var(--border-width-strong) solid var(--color-card-border);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: border-color var(--transition-fast), background-color var(--transition-fast), box-shadow var(--transition-fast);
}

.platform-case-option:hover {
    border-color: var(--color-border-strong);
    background-color: var(--color-surface-hover);
}

.platform-case-option input[type="radio"] {
    grid-area: radio;
    align-self: center;
    width: 1.15rem;
    height: 1.15rem;
    accent-color: var(--color-primary);
    cursor: pointer;
}

.platform-case-option__title {
    grid-area: title;
    font-weight: var(--font-weight-semibold);
    color: var(--color-heading);
}

.platform-case-option__desc {
    grid-area: desc;
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
}

.platform-case-option.is-selected {
    border-color: var(--color-selected-border);
    background-color: var(--color-selected-bg);
    box-shadow: var(--focus-ring);
}

.platform-case-help {
    color: var(--color-text-muted);
}

.platform-route-resolving {
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Step 1 single-column centered composition (approved mockup). Narrowing lives
 * on .platform-case-content, not .platform-case-page itself, so the page root
 * (and the footer, its sibling) stay at the page's normal width — matching
 * the footer width on Sources/Authorize/Run — while just the content card
 * narrows and centers. */
.platform-case-content {
    max-width: 32rem;
    margin: 0 auto;
    text-align: center;
}

.platform-case-page .platform-case-options {
    width: 100%;
    text-align: left;
}

/* Sign-in landing page — standalone (NOT a wizard step): centered single-column
 * composition mirroring the Case Selection page pattern. No stepper/step-pill chrome. */
.platform-sign-in-page {
    align-items: center;
    max-width: 32rem;
    text-align: center;
}

/* Real ciraApps.Web ciraHub sign-in page ground truth (sign-in.component.html/.scss): ONE
 * single centered column of plain, borderless sign-in buttons — no per-provider heading, no
 * description paragraph, no bordered per-option card. */
.platform-sign-in-page .platform-sign-in-buttons {
    align-items: center;
    width: 100%;
}

/* Caps width so sign-in buttons don't stretch edge-to-edge. */
.platform-sign-in-buttons .platform-btn--block {
    max-width: 22rem;
}

.platform-sign-in-buttons__icon {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}

.platform-sign-in-page .platform-sign-in-buttons .platform-btn {
    color: var(--p-neutral-900);
    line-height: 19.5px;
}

.platform-sign-in-legal {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    line-height: 19.5px;
    text-align: center;
    text-wrap: balance;
}

.platform-sign-in-legal a {
    color: var(--color-primary);
    font-weight: var(--font-weight-semibold);
    text-decoration: none;
}

.platform-sign-in__error {
    width: 100%;
    min-height: 67px;
    padding: var(--space-4) var(--space-5);
    border-radius: var(--radius-md);
    background-color: var(--color-danger-bg);
    color: var(--color-danger);
    font-size: var(--font-size-sm);
    text-align: left;
}

/* ============================================================
 * 5. BUTTONS
 * ============================================================ */
.platform-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    font-family: inherit;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
    color: var(--color-text);
    background-color: var(--color-control-bg);
    border: var(--border-width) solid var(--color-control-border);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: background-color var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast), box-shadow var(--transition-fast);
}

.platform-btn:hover {
    background-color: var(--kendo-color-base-hover);
    border-color: var(--color-border-strong);
    text-decoration: none;
}

.platform-btn:active {
    background-color: var(--color-surface-sunken);
    transform: translateY(1px);
}

/* Top navigation progress bar (platform-nav-progress.js) — signals that a click registered and a
   page transition is in flight. Especially important on long polling, where the circuit round-trip
   adds latency before the new page renders. */
.platform-nav-progress {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    transform: scaleX(0);
    transform-origin: 0 50%;
    background-color: var(--color-primary);
    opacity: 0;
    pointer-events: none;
    z-index: 2147483001;
    will-change: transform, opacity;
}

/* Reusable skeleton primitive — same 1.5s shimmer tempo as the boot skeleton and the CiraHub
   loaders, so all loading blinks feel like one system. Apply .platform-skeleton to any block. */
.platform-skeleton {
    display: block;
    position: relative;
    overflow: hidden;
    background-color: #e2e5ec;
    border-radius: var(--radius-md);
}
.platform-skeleton--circle { border-radius: 50%; }
.platform-skeleton--text { border-radius: var(--radius-sm); }
.platform-skeleton::after {
    content: "";
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0, rgba(255, 255, 255, .65) 50%, rgba(255, 255, 255, 0) 100%);
    animation: platform-skeleton-shimmer 1.5s linear infinite;
}
@keyframes platform-skeleton-shimmer {
    100% { transform: translateX(100%); }
}
@media (prefers-reduced-motion: reduce) {
    .platform-skeleton::after { animation-duration: 3s; }
}

/* Dashboard loading placeholder (DashboardLoadingSkeleton) shown while widget data is fetched over
   the circuit — avoids the "empty dashboard" look while metrics/flows load on long polling. */
.platform-dashboard-skeleton__cards {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--space-4);
}
.platform-dashboard-skeleton__metric {
    height: 132px;
    border-radius: var(--radius-lg);
}
.platform-dashboard-skeleton__line {
    display: block;
    height: 20px;
    width: 320px;
    max-width: 100%;
}
.platform-dashboard-skeleton__block {
    display: block;
    height: 132px;
    border-radius: var(--radius-lg);
}
@media (max-width: 860px) {
    .platform-dashboard-skeleton__cards { grid-template-columns: repeat(2, 1fr); }
}

/* Top-bar chrome placeholder shown while SessionInitializer resolves the signed-in account (fresh
   circuit on a full load): keeps the nav pills + trial pill + user chip from popping in after the
   page title, which otherwise leaves a bare header for a moment. Dimensions mirror the real chrome
   (44px nav pills, 32px avatar). */
.platform-topbar-skeleton__nav {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}
.platform-topbar-skeleton__nav-item {
    height: 44px;
    width: 104px;
    border-radius: var(--radius-lg);
}
.platform-topbar-skeleton__pill {
    height: 32px;
    width: 128px;
    border-radius: 999px;
}
.platform-topbar-skeleton__user {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}
.platform-topbar-skeleton__user-lines {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 6px;
}
.platform-topbar-skeleton__user-line {
    height: 12px;
    border-radius: var(--radius-sm);
}
.platform-topbar-skeleton__user-line--name { width: 120px; }
.platform-topbar-skeleton__user-line--email { width: 160px; height: 10px; }
.platform-topbar-skeleton__avatar {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    flex-shrink: 0;
}
@media (max-width: 900px) {
    .platform-topbar-skeleton__nav { display: none; }
    .platform-topbar-skeleton__user-lines { display: none; }
}

.platform-btn:disabled,
.platform-btn[disabled],
.platform-btn[aria-disabled="true"] {
    cursor: not-allowed;
    color: var(--color-control-disabled-text);
    background-color: var(--color-control-disabled-bg);
    border-color: var(--color-border);
    box-shadow: none;
}

.platform-btn--ghost {
    background-color: transparent;
    border-color: transparent;
    color: var(--color-primary);
}

.platform-btn--ghost:hover {
    background-color: var(--color-primary-soft-bg);
    border-color: transparent;
}

.platform-btn--flat {
    background-color: transparent;
    border-color: transparent;
}

.platform-btn--flat:hover {
    background-color: var(--kendo-color-base-hover);
}

.k-button-flat-base:hover {
    background-color: var(--kendo-color-base-hover) !important;
}

.k-button {
    font-weight: var(--font-weight-semibold);
}

/* min-width matches width on every tier below — Kendo's own .k-button sets a min-width formula
   derived from each size's line-height (e.g. ~34px at Medium, ~50px at Large), which silently
   overrides a plain `width` (min-width always wins over width when larger, regardless of
   specificity). Without pinning min-width too, the circle stops being square at Medium+ sizes.

   padding: 0 is re-asserted on every 3-class tier override below (not just the 2-class base rule)
   because .platform-btn--{xs,sm,md,lg}'s own padding-inline/padding-block (declared further down
   this file) has the SAME specificity as the 2-class base rule here — equal specificity falls back
   to source order, and the size-tier rule comes later, so it silently wins and reintroduces padding
   inside the fixed-width circle. The 3-class selector below outranks it outright. */
.platform-btn--icon-only.k-button {
    width: 2rem;
    height: 2rem;
    min-width: 2rem;
    padding: 0;
}

.platform-btn--icon-only.platform-btn--xs.k-button {
    width: 1.25rem;
    height: 1.25rem;
    min-width: 1.25rem;
    padding: 0;
}

.platform-btn--icon-only.platform-btn--sm.k-button {
    width: 1.75rem;
    height: 1.75rem;
    min-width: 1.75rem;
    padding: 0;
}

.platform-btn--icon-only.platform-btn--md.k-button {
    padding: 0;
}

.platform-btn--icon-only.platform-btn--lg.k-button {
    width: 2.25rem;
    height: 2.25rem;
    min-width: 2.25rem;
    padding: 0;
}

.platform-cta {
    align-self: flex-start;
}

/* Size-scoped font-size, padding, and line-height; also applies to plain .platform-btn anchors.
   Kendo (as of Telerik.UI.for.Blazor 15.0.1) now scopes its own equivalent padding via a
   compound .k-button.k-button-{size} selector (previously a bare .k-button-{size}, which is why
   a single-class override here used to be enough) — a bare .platform-btn--{size} class alone is
   now less specific and silently loses on TelerikButton instances. Compounded with .k-button to
   match/beat that specificity; the bare fallback stays for plain <a class="platform-btn ..."> CTAs
   that carry no .k-button class at all. */
.k-button.platform-btn--xs,
.platform-btn--xs {
    font-size: var(--font-size-xs);
    padding-inline: var(--space-1);
    padding-block: var(--space-1);
    line-height: var(--line-height-tight);
}

.k-button.platform-btn--sm,
.platform-btn--sm {
    font-size: var(--font-size-xs);
    padding-inline: var(--space-2);
    padding-block: var(--space-2);
    line-height: var(--line-height-tight);
}

.k-button.platform-btn--md,
.platform-btn--md {
    padding-inline: var(--space-4);
    padding-block: var(--space-3);
    line-height: var(--line-height-tight);
}

.k-button.platform-btn--lg,
.platform-btn--lg {
    font-size: var(--font-size-md);
    padding-inline: var(--space-6);
    padding-block: var(--space-3);
    line-height: var(--line-height-tight);
}

.platform-cta.platform-cta--block {
    align-self: center;
    width: 100%;
    justify-content: center;
}

.platform-btn--block {
    width: 100%;
    justify-content: center;
}

.platform-btn.platform-cta--dark {
    color: var(--color-on-dark);
    background-color: var(--color-surface-dark);
    border-color: var(--color-surface-dark);
}

.platform-btn.platform-cta--dark:hover {
    background-color: var(--p-neutral-800);
    border-color: var(--p-neutral-800);
}

/* Danger/Success render tonal — status text on its own tinted surface — rather than as a filled
   status block. Solid supplies the background/border these repaint; hover and active take Kendo's
   "subtle" ramp, which is the tonal step it prescribes for this look and which derives from the
   same red/green kendo-theme-custom.css seeds --color-danger/--color-success are built from.
   Compounded with .k-button and paired :pseudo/.k-state the way Kendo writes its own solid rules,
   to match their specificity. Hover and active take the ramp's on-subtle text with it, since the
   tint they darken to is what that pairing is contrast-tuned against. */
.k-button.platform-cta.k-button-error {
    background-color: var(--color-danger-bg);
    border-color: var(--color-danger-bg);
    color: var(--color-danger);
}

.k-button.platform-cta.k-button-error:hover,
.k-button.platform-cta.k-button-error.k-hover {
    background-color: var(--kendo-color-error-subtle-hover);
    border-color: var(--kendo-color-error-subtle-hover);
    color: var(--kendo-color-error-on-subtle);
}

.k-button.platform-cta.k-button-error:active,
.k-button.platform-cta.k-button-error.k-active {
    background-color: var(--kendo-color-error-subtle-active);
    border-color: var(--kendo-color-error-subtle-active);
    color: var(--kendo-color-error-on-subtle);
}

.k-button.platform-cta.k-button-success {
    background-color: var(--color-success-bg);
    border-color: var(--color-success-bg);
    color: var(--color-success);
}

.k-button.platform-cta.k-button-success:hover,
.k-button.platform-cta.k-button-success.k-hover {
    background-color: var(--kendo-color-success-subtle-hover);
    border-color: var(--kendo-color-success-subtle-hover);
    color: var(--kendo-color-success-on-subtle);
}

.k-button.platform-cta.k-button-success:active,
.k-button.platform-cta.k-button-success.k-active {
    background-color: var(--kendo-color-success-subtle-active);
    border-color: var(--kendo-color-success-subtle-active);
    color: var(--kendo-color-success-on-subtle);
}

/* Kendo's focus ring is a 30% mix of the full-strength status color, which reads as a filled
   block against the tint, so it is remixed from the text color at the same ratio. */
.k-button.platform-cta.k-button-error:focus,
.k-button.platform-cta.k-button-error.k-focus {
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-danger) 30%, transparent);
}

.k-button.platform-cta.k-button-success:focus,
.k-button.platform-cta.k-button-success.k-focus {
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-success) 30%, transparent);
}

/* ============================================================
 * 6. FORMS / INPUTS / TOGGLES
 * ============================================================ */
input[type="text"],
input[type="email"],
input[type="number"],
input[type="password"],
input[type="search"],
input[type="tel"],
input[type="url"],
select,
textarea {
    font-family: inherit;
    font-size: var(--font-size-base);
    color: var(--color-text);
    background-color: var(--color-control-bg);
    border: var(--border-width) solid var(--color-control-border);
    border-radius: var(--radius-md);
    padding: var(--space-2) var(--space-3);
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

input:focus-visible,
select:focus-visible,
textarea:focus-visible {
    border-color: var(--color-primary);
    box-shadow: var(--focus-ring);
}

.platform-field--toggle {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
}

.platform-field label {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--color-text);
}

/* Label text for PlatformTextInput/PlatformSelect specifically (the first <span> inside their
   .platform-field wrapper) — distinct from .platform-field label above, which targets a nested
   <label> tag that doesn't exist in either component's markup, so it never actually applied to
   them. Scoped to a dedicated class rather than fixing that rule directly, since doing so would
   also reskin every other existing .platform-field usage (DashboardPage's schedule fields,
   SpokePreviewSubBlock's preview fields) that wasn't part of this request. */
.platform-field__label {
    font-family: var(--font-sans);
    font-weight: var(--font-weight-medium);
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
}

/* Quantity stepper */
.platform-quantity {
    display: inline-flex;
    align-items: stretch;
    border: var(--border-width) solid var(--color-control-border);
    border-radius: var(--radius-md);
    overflow: hidden;
}

/* Un-retired: QuantityInput's +/- controls came off PlatformButton so they can keep their
   pinned 16px icon regardless of PlatformButton's enforced icon-size ladder (see
   PlatformButton.razor.cs's ResolvedIconSize). Circular hover/focus target to match how a
   circular Kendo icon-only button used to reveal one — the circle only shows on interaction,
   at rest there's no border/background, same as before. */
.platform-quantity__btn {
    align-self: center;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    min-width: 2rem;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: none;
    color: inherit;
    cursor: pointer;
}

.platform-quantity__btn:hover:not(:disabled),
.platform-quantity__btn:focus-visible:not(:disabled) {
    background-color: var(--color-surface-hover);
}

.platform-quantity__btn:disabled {
    color: var(--color-text-muted);
    cursor: not-allowed;
}

.platform-quantity__input {
    width: 2.75rem;
    border: none;
    border-left: var(--border-width) solid var(--color-border);
    border-right: var(--border-width) solid var(--color-border);
    border-radius: 0;
    text-align: center;
    padding: var(--space-2) 0;
}

/* Toggle switch */
.platform-toggle-switch {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    cursor: pointer;
}

.platform-toggle-switch--disabled {
    pointer-events: none;
    cursor: default;
}

.platform-toggle-switch__label {
    font-size: var(--font-size-sm);
    color: var(--color-text);
}

/* Kendo TelerikCheckBox override — Kendo's stock size/color already match via the bridged
   --kendo-color-* tokens, so only the focus ring (persists after mouse click on plain :focus)
   needs correcting, same fix as the switch below. */
.platform-checkbox:focus,
.platform-checkbox.k-focus {
    outline: none;
    box-shadow: none;
}

.platform-checkbox:focus-visible {
    box-shadow: var(--focus-ring);
}

/* Kendo TelerikTextBox override — same focus-ring parity fix as the checkbox/switch above.
   The visible border/background live on this wrapper, but real DOM focus lands on the inner
   <input class="k-input-inner"> (it carries tabindex, the wrapper doesn't) — so Kendo's own
   generic `input:focus-visible` rule below rings that inner element directly, which sits
   recessed inside this wrapper's padding. Left alone, that draws the ring inset in the middle
   of the control instead of around its visible edge. Suppress it there and re-draw it on the
   wrapper via :has() so it hugs the actual border. */
.platform-textbox:focus,
.platform-textbox.k-focus {
    outline: none;
    box-shadow: none;
}

.platform-textbox .k-input-inner:focus-visible {
    outline: none;
    box-shadow: none;
}

.platform-textbox:has(.k-input-inner:focus-visible) {
    box-shadow: var(--focus-ring);
}

/* Placeholder color for every Kendo text-input/picker-family control in the app — GLOBAL, same
   decision as the padding rules below. kendo-theme-default paints .k-input-inner::placeholder from
   --kendo-color-subtle, which is not bridged to a platform token; this points it at
   --color-text-placeholder instead. The .k-input/.k-picker compound prefixes carry two classes so
   this keeps winning if Telerik re-scopes its own rule tighter (it did exactly that to the padding
   rules in 15.0.1); the bare .k-input-inner fallback covers any wrapper that carries neither. */
.k-input .k-input-inner::placeholder,
.k-picker .k-input-inner::placeholder,
.k-input-inner::placeholder {
    color: var(--color-text-placeholder);
}

/* kendo-theme-default gives the OUTER .k-input wrapper its own padding-inline/padding-block
   (a stock 0.5rem/0.25rem, re-added by a later rule after an earlier one resets it to 0 — see
   kendo-theme-default/all.css) on top of whatever .k-input-inner sets below. Wrapper and inner
   padding live on different elements, so they don't override each other in the cascade, they
   just stack — every Kendo input in the app has had 8px/4px of unaccounted-for extra inline/
   block padding beyond what .k-input-inner alone specifies, most visible on the search box
   where the icon's position is calculated against a separate origin. .k-picker is deliberately
   left alone: Kendo's own re-add rule only targets .k-input, so pickers were never affected. */
.k-input {
    padding: 0;
}

/* Padding/line-height for every Kendo text-input/picker-family control in the app (TextBox,
   DropDownList, ComboBox, ...) — deliberately GLOBAL, matching the same "leak to everything"
   decision already made for the popup styling below. Medium sits flush against the default
   PlatformButton height; both reuse .platform-btn--md's own tokens (--space-4/--space-3/
   --line-height-tight) rather than hardcoding px, so they stay in lockstep if button sizing ever
   changes. Was originally scoped to .platform-textbox/.platform-select only, which is why
   SchemaPreviewViewer's own TelerikDropDownList (Class="platform-flow__direction-select", not
   one of these two) rendered at Kendo's stock (shorter) size instead of matching — this also
   affects SourceSelectionPage's search TelerikTextBox, the other real .k-input-inner usage in
   the app, which previously wasn't touched.
   Keyed off .k-input-md/.k-picker-md (not just a bare .k-input-inner) to match the specificity
   of Kendo's own kendo-theme-default rule of the same shape — a bare single-class override
   loses to Kendo's two-class one regardless of stylesheet load order, so it silently never
   applied whenever the wrapper actually carried .k-input-md (e.g. an explicit Size="Medium").
   The bare .k-input-inner fallback stays for the case where Kendo adds no size class at all.
   Telerik.UI.for.Blazor 15.0.1 re-scoped Kendo's own rule one level tighter again — now
   .k-input.k-input-md/.k-picker.k-picker-md (3 classes) instead of the old .k-input-md/
   .k-picker-md (2 classes) — so this needed the same .k-input/.k-picker compound prefix to
   keep winning. */
.k-input.k-input-md .k-input-inner,
.k-picker.k-picker-md .k-input-inner,
.k-input-md .k-input-inner,
.k-picker-md .k-input-inner,
.k-input-inner {
    padding-inline: var(--space-4);
    padding-block: var(--space-3);
    line-height: var(--line-height-tight);
}

/* Size="Small" variant — matches .platform-btn--sm (8px padding both axes, 12px text). Keyed off
   Kendo's own k-input-sm/k-picker-sm, which every Kendo input/picker-family component sets from
   its own native Size parameter — not just PlatformTextInput/PlatformSelect's ResolvedSize — so
   this follows any Small-sized Kendo control app-wide (e.g. SchemaPreviewViewer's direction
   picker) automatically. Compound .k-input/.k-picker prefix added for Telerik.UI.for.Blazor
   15.0.1 — see the .k-input-md comment above for why. */
.k-input.k-input-sm .k-input-inner,
.k-picker.k-picker-sm .k-input-inner,
.k-input-sm .k-input-inner,
.k-picker-sm .k-input-inner {
    padding-inline: var(--space-2);
    padding-block: var(--space-2);
    font-size: var(--font-size-xs);
    line-height: var(--line-height-tight);
}

/* Kendo dropdown-style popup — deliberately GLOBAL (every Kendo popup in the app: DropDownList,
   ComboBox, MultiSelect, AutoComplete, ..., not just PlatformSelect's). Rounded popup, rounded +
   gapped + primary-light-blue-hover options, item padding/font-size matching each control's own
   Size. .k-popup is the actual visible/painted box (border-radius/overflow here, not on the
   invisible .k-animation-container wrapper one level up, which has no visible effect on its own). */
.k-popup {
    border-radius: var(--radius-md);
    overflow: hidden;
}

/* Telerik.UI.for.Blazor 15.0.1 added its own padding on this popup family's outer box
   (.k-list-container.k-popup — a 2-class compound rule, --kendo-spacing-2 = 8px), which didn't
   exist in the prior version and stacks on top of .k-list-ul's own 4px inset below, doubling the
   visible gap around the option list to 12px. .k-list-ul stays the single source of truth for
   that inset, so this is zeroed out rather than adjusted to a smaller non-zero value — one
   knob, not two that have to be kept in sync.
   !important because this specific selector is new as of 15.0.1 (didn't exist in 12.2.0 at all)
   and sits in the same part of the theme that's already been re-scoped to a tighter compound
   selector three other times this same release (input/list-item/button size classes) — unlike
   .k-list-ul's own padding:0 reset, which has held its single-class shape across three major
   versions, this one has no track record. !important makes the zero-out immune to Kendo adding
   yet another class to this selector in a future release, the same reasoning already applied to
   .k-child-animation-container below. */
.k-list-container.k-popup {
    padding: 0 !important;
}

/* Inset so the rounded items don't sit flush against the popup's own rounded edge. Kendo sets
   padding:0 on .k-list-ul itself (a single-class rule, same specificity as this one — wins on
   load order since platform-layout.css loads last).
   Gap: Telerik.UI.for.Blazor 15.0.1 made .k-list-ul a real flex container with its own native
   gap (it was a plain non-flex <ul> before, which is why item spacing used to be faked via
   margin-block-end on .k-list-item — see that rule below, now removed). Set explicitly to our
   own token rather than trusting Kendo's native --kendo-spacing-0.5 value to coincidentally
   match. */
.k-list-ul {
    padding: var(--space-1);
    gap: var(--space-1);
}

/* .k-list-optionlabel (the DefaultText/placeholder row) is a SIBLING of .k-list-ul, not one of
   its <li> children — confirmed via live DOM inspection — so it never receives the padding
   inset above and sat flush against the popup's edges while the real items didn't. It's a single
   element (not a list container), so margin (not padding — its own inner padding is already set
   by the .k-list-md/.k-list-sm rules below) is what actually pushes its box away from the popup
   edge, matching what .k-list-ul's padding does for its children. No margin-block-end here —
   optionlabel's actual next sibling is the padded <ul> itself, so its own top padding already
   supplies the gap before the first real item; stacking a bottom margin on top of that doubled
   the gap to 8px instead of the intended 4px (the shared :not(:last-child) rule further down was
   the culprit, and no longer includes .k-list-optionlabel — see its comment). */
.k-list-optionlabel {
    margin-inline: var(--space-1);
    margin-block-start: var(--space-1);
}

/* Kendo already scopes list-item padding per size via .k-list-sm/.k-list-md/.k-list-lg ancestor
   classes (2-class selectors, e.g. ".k-list-md .k-list-item{...}") — which are MORE specific
   than a flat ".k-list-item{...}" rule, so overriding padding there directly is what actually
   wins, and it automatically follows each control's own native Size (DropDownList/ComboBox/etc.
   all apply .k-list-{size} to the popup's list to match — confirmed empirically after a flat
   override silently lost to Kendo's own .k-list-md rule). Font-size isn't size-differentiated by
   Kendo itself (.k-list-sm and .k-list-md both just reference the same --kendo-font-size), so
   the small variant needs its own explicit font-size on top.
   Telerik.UI.for.Blazor 15.0.1 re-scoped Kendo's own rule one level tighter again — now
   .k-list.k-list-md/.k-list.k-list-sm (3 classes) instead of the old .k-list-md/.k-list-sm
   (2 classes) — so this needed the same .k-list compound prefix to keep winning. */
.k-list.k-list-md .k-list-item,
.k-list.k-list-md .k-list-optionlabel,
.k-list-md .k-list-item,
.k-list-md .k-list-optionlabel {
    padding-inline: var(--space-4);
    padding-block: var(--space-3);
}

.k-list.k-list-sm .k-list-item,
.k-list.k-list-sm .k-list-optionlabel,
.k-list-sm .k-list-item,
.k-list-sm .k-list-optionlabel {
    padding-inline: var(--space-2);
    padding-block: var(--space-2);
    font-size: var(--font-size-xs);
}

.k-list-item,
.k-list-optionlabel {
    border-radius: var(--radius-md);
}

.k-list-item:hover,
.k-list-item.k-hover,
.k-list-optionlabel:hover,
.k-list-optionlabel.k-hover {
    background-color: var(--color-primary-soft-bg);
}

/* DefaultText prompt color. kendo-theme-default paints .k-list-optionlabel (all three states,
   in one rule) from --kendo-color-subtle, the same unbridged variable that drove the input
   placeholder above; this points it at --color-text-placeholder so the prompt matches the
   collapsed field's own watermark. Mirrors Kendo's selector list state-for-state so each one
   matches its specificity and wins on load order. */
.k-list-optionlabel,
.k-list-optionlabel:hover,
.k-list-optionlabel.k-hover {
    color: var(--color-text-placeholder);
}

/* The DefaultText row (.k-list-optionlabel) is Kendo's placeholder prompt ("Select a Microsoft
   account") rendered at the top of the popup. When a real item is selected it is a plain header
   row (shown, like every other picker in the app). But while NOTHING is selected Kendo marks it
   .k-selected and paints it like a chosen option (solid fill / bordered box) — a visible duplicate
   of the same prompt already shown in the collapsed value area. Hide it in that empty state only
   (keyed off the .k-selected Kendo sets on it), so the list never shows the redundant, bordered
   placeholder while still keeping the DefaultText header once a real value is picked. GLOBAL like
   the rest of the popup styling. */
.k-list-optionlabel.k-selected {
    display: none;
}

/* Collapsed-field watermark: PlatformSelect adds .platform-select--empty while no value is
   selected (driven by its own Value, not Kendo internals), so the DefaultText prompt shown in the
   picker's value area is greyed like a placeholder. Only ever applied in the empty state, so it
   never tints a real selected value's text. */
.platform-select--empty .k-input-inner {
    color: var(--color-text-placeholder);
}
/* Kendo's own .k-list-item.k-selected (solid primary blue, white text) and
   .k-list-item.k-selected.k-hover (3 class selectors, more specific than the 2-class hover rule
   above) are untouched by this — the selected row keeps its own distinct color instead of
   picking up the hover tint, including while the mouse is over it. */

/* Kendo popup open/close animation — driven by an inline `transition-duration` style (not a
   stylesheet rule), so overriding it needs !important to win over an inline style. Global: shares
   the same .k-animation-container/.k-child-animation-container mechanism as other Kendo popup-style
   components (tooltips, context menus, dialogs), not just dropdown-family ones.
   transform: none cancels the translate end-states Kendo's animation classes set on this
   element (.k-slide-down-appear / .k-slide-up-appear = translateY(-100%) / translateY(100%),
   and .k-slide-*-exit-active re-applies the same offset on close). Zeroing transition-duration
   alone leaves those transforms applied, so the popup snaps a full popup-height in one frame
   instead of sliding -- clipped out of sight while .k-animation-container was overflow:hidden,
   visible as a jump on open and close once it went overflow:visible below. Covers every
   slide-/push- variant, including the flipped set Kendo swaps to for popups that open upward. */
.k-child-animation-container {
    transition-duration: 0s !important;
    transform: none !important;
}

/* .k-animation-container defaults to overflow:hidden and only flips to overflow:visible while its
   .k-animation-container-shown class is present — meant to clip the popup's elevation shadow
   (which extends past .k-popup's own border-box) during the brief slide/expand animation, then
   reveal it once the popup is fully open. Confirmed live (sampling computed style every 20ms
   across an open/close cycle) that the .k-animation-container-shown class is added/removed within
   one Blazor render tick (~20-40ms) of the click — near-instant — while Telerik's own JS keeps the
   popup's DOM (including the shadow-casting .k-popup) mounted for ~300-370ms regardless, on a
   timer that's independent of the transition-duration override above (tested at both 0s and 1ms
   with no change to that ~350ms figure, ruling out a transition-end handshake as the cause). That
   mismatch — overflow flipping in ~30ms vs. content unmounting in ~350ms — clips the shadow (via
   overflow:hidden) well before the popup content it belongs to actually disappears on close, and
   symmetrically delays the shadow's reveal past the content's on open, which is the exact stagger
   both PlatformSelect and SchemaPreviewViewer's kebab menu show. Forcing overflow permanently
   visible removes the premature clip entirely, so the shadow's visibility now tracks the popup's
   real mount/unmount instead of the early class toggle — shadow and content now appear/disappear
   in the same frame (confirmed live). Nothing else in the app relies on this wrapper clipping
   anything — the popups it wraps size themselves to their own content and scroll internally where
   needed (e.g. .k-popup .k-column-list's own max-height), never on this outer wrapper. */
.k-animation-container {
    overflow: visible !important;
}

/* Wrapper for a TelerikCheckBox + adjacent label text (used by PlatformCheckbox, and by any
   other checkbox+label pairing). Needs its own flex/align-items like .platform-toggle-switch
   above it — a bare <label> falls back to inline vertical-align defaults, and Kendo's checkbox
   (vertical-align: middle) and a plain <span> (vertical-align: baseline) don't share an
   alignment anchor. Distinct from .platform-field--toggle below, which is a label-vs-value
   space-between row, not this tight adjacency. */
.platform-checkbox-row {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    cursor: pointer;
}

.platform-checkbox-row__label {
    font-size: var(--font-size-sm);
    color: var(--color-text);
}

/* Kendo TelerikSwitch override — matches the Figma mockup's flat pill design
   (Platform-Mockups, node 1148:42097, round-number update) rather than Kendo's stock look
   (flat "On"/"Off" track text, brand-primary checked color). Confirmed against TelerikSwitch's
   actual rendered markup: <span class="k-switch ..."><span class="k-switch-track">...
   <span class="k-switch-thumb-wrap"><span class="k-switch-thumb" /></span></span></span>.

   Specificity note (Telerik 15.x): since 15.0, the theme doubles the base class in every
   sizing/state selector (e.g. ".k-switch.k-switch-sm", ".k-switch.k-switch-sm.k-switch-on
   .k-switch-thumb-wrap"), so all overrides below repeat .k-switch to stay ahead of the
   built-in rules regardless of stylesheet order. */
.platform-switch.k-switch.k-switch-sm {
    width: 2.5rem;
    height: 1.375rem;
}

.platform-switch.k-switch.k-switch-sm .k-switch-track {
    width: 2.5rem;
    height: 1.375rem;
    border: none;
    background-color: var(--color-track);
}

.platform-switch.k-switch.k-switch-sm.k-switch-on .k-switch-track {
    background-color: var(--kendo-color-on-app-surface);
}

.platform-switch.k-switch.k-switch-sm .k-switch-thumb {
    width: 1.125rem;
    height: 1.125rem;
    border: none;
    background-color: var(--color-surface);
    box-shadow: var(--shadow-sm);
}

/* .k-switch-thumb-wrap is a zero-size anchor; the thumb centers on it via
   translate(-50%, -50%), so these target the thumb's desired CENTER (0.125rem
   inset + half of the 1.125rem thumb), not its edge. Both variants must be
   qualified with .k-switch-on/.k-switch-off (not left as a bare fallback) to
   match Kendo's own same-specificity built-in rules for this element — an
   unqualified rule here loses outright to Kendo's
   .k-switch.k-switch-sm.k-switch-off selector regardless of stylesheet order. */
.platform-switch.k-switch.k-switch-sm.k-switch-off .k-switch-thumb-wrap {
    inset-inline-start: 0.6875rem;
}

.platform-switch.k-switch.k-switch-sm.k-switch-on .k-switch-thumb-wrap {
    inset-inline-start: calc(100% - 0.6875rem);
}

.platform-switch .k-switch-label-on,
.platform-switch .k-switch-label-off {
    display: none;
}

.platform-switch:focus .k-switch-track,
.platform-switch.k-focus .k-switch-track {
    outline: none;
    box-shadow: none;
}

.platform-switch:focus-visible .k-switch-track {
    box-shadow: var(--focus-ring);
}

/* ============================================================
 * 7. SOURCE CATALOG & CARDS
 * ============================================================ */

.platform-source-group__grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3);
}

@media (max-width: 420px) {
    .platform-source-group__grid {
        grid-template-columns: minmax(0, 1fr);
    }
}

.platform-source-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-4);
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.platform-source-card:hover {
    border-color: var(--color-border-strong);
    box-shadow: var(--shadow-md);
}

.platform-source-card__head {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.platform-source-card__icon {
    flex-shrink: 0;
}

/* A source-card icon that is not a composed <EntityIcon> gets a plain tinted square. An
   <EntityIcon> (.ic) brings its own box size and round plate from the icon system ladder. */
.platform-source-card__icon:not(.ic) {
    width: var(--size-icon-md);
    aspect-ratio: 1;
    border-radius: var(--radius-md);
    background-color: var(--color-primary-soft-bg);
    border: var(--border-width) solid var(--color-primary-soft-border);
}

/* Dashboard flow-row icon (R2/R3/R4, widget mode only): a small square placed between the shared
   dashed marker and the card body (marker = "this is a node", icon = "which system"). Capped at
   1rem so it can never grow the row past the single-line height every rail/elbow offset in this
   file assumes (KB: befed6cb95 — do not remove flex-shrink: 0 or raise this above 1rem). */
/* Card and box icons share one 24px column starting 12px in (Figma 806:13881 / 806:13900 put both
   icons at x=12), so the Hub/Tunnel glyphs line up with the spoke icons above and below. */
.platform-flow__card-icon:not(.ic) {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    flex-shrink: 0;
    overflow: hidden;
}

/* The composed icon carries its badges on the plate's edge, outside the circle's inscribed box,
   so it is not clipped. The box size comes from the ladder step the call site picks (24px in the
   widget, 40px in preview/configure). */
.platform-flow__card-icon.ic {
    flex-shrink: 0;
    overflow: visible;
}

/* Shared anchor-icon slot: a 1rem box for section headings and PlatformButton buttons — one
   convention, two consumers, no one-off inline SVGs. Each is optional and omitted entirely when
   no icon is supplied, so it never reserves a gap or shows a broken image. */
.platform-heading__icon,
.platform-cta__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
    overflow: hidden;
    vertical-align: -0.125em;
    margin-inline-end: var(--space-2);
}

/* Button loading spinner takes the button's text color. Kendo paints the loader segments with
   currentColor but sets color on .k-loader/.k-loader-{theme} itself. */
.platform-cta .k-loader {
    color: inherit;
}

/* ------------------------------------------------------------
 * AUTH-PROVIDER BRAND MARK — the icon beside an auth block's title.
 * AuthBlockCard renders <EntityIcon> off EntityIconCatalog.ForAuthProvider,
 * so the mark is composed from the icon system's atoms and its size comes
 * from the ladder step (.ic--24). Only the flex behaviour inside the card
 * head is this file's business.
 * ------------------------------------------------------------ */
.platform-auth-card__icon {
    flex-shrink: 0;
}

.platform-source-card__name {
    font-weight: var(--font-weight-semibold);
    color: var(--color-heading);
    line-height: var(--line-height-tight);
}

.platform-source-card__actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
}

/* ------------------------------------------------------------
 * PlatformPageHeader: shared page title + optional subtitle, used by
 * every Platform page (see PlatformPageHeader.razor).
 * ------------------------------------------------------------ */

.platform-page-header__row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.platform-page-header__row > h1 {
    flex: 1 1 auto;
    min-width: 0;
}

.platform-page-header__subtitle {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-normal);
    text-wrap: balance;
}

.platform-page-header__actions {
    margin-inline-start: auto;
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

/* ------------------------------------------------------------
 * Step 2 mockup: search, sample cards, sales intro, footer
 * ------------------------------------------------------------ */

.platform-search {
    position: relative;
    display: flex;
    align-items: center;
    width: 100%;
}

/* Centered independent of the input's height, same absolute+translateY(-50%) technique as
   .platform-flow__card-arrow — needed since the input grew from Kendo's stock ~28px to ~43.5px
   via the global .k-input-inner rule above and this icon was never explicitly centered. */
.platform-search__icon {
    position: absolute;
    top: 50%;
    left: var(--space-3);
    transform: translateY(-50%);
    z-index: 1;
    color: var(--color-icon-muted);
    pointer-events: none;
}

/* Higher specificity than the base `input[type="search"]` padding shorthand so the left padding
   that clears the absolutely-positioned magnifier icon is not reset (icon/text no longer overlap).
   The class lands on the Kendo TextBox wrapper, so stretch it and push padding onto the inner input. */
.platform-search .platform-search__input {
    width: 100%;
}

.platform-search .platform-search__input.k-input,
.platform-search .platform-search__input .k-input-inner {
    padding-left: calc(var(--space-3) + 1.5rem);
}

.platform-source-group__intro {
    color: var(--color-text);
    font-size: var(--font-size-md);
    line-height: var(--line-height-normal);
    margin: 0;
}

.platform-link {
    padding: 0.15rem 0.4rem;
    margin: -0.15rem -0.4rem;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--color-primary);
    background-color: transparent;
    font: inherit;
    font-weight: var(--font-weight-semibold);
    text-decoration: none;
    cursor: pointer;
    transition: background-color var(--transition-fast);
}

.platform-link:hover {
    background-color: var(--color-primary-soft-bg);
    text-decoration: underline;
}

/* Sample cards (compact single row: sync icon + title/subtitle + optional "+" add).
   Mockup fidelity: a short horizontal row (~one-third the old stacked-card height), NOT a
   tall stacked box. Icon on the left, bold title with a short muted subtitle beside it, and a
   right-aligned "+". */
.platform-source-card--sample {
    flex-direction: row;
    align-items: center;
    gap: var(--space-4);
    height: 105px;
    padding: var(--space-4);
    background-color: var(--p-neutral-100);
    border-color: var(--color-card-border);
    box-shadow: none;
}

/* Addable (Featured) cards act as a single large add target — pointer affordance on hover.
   Display-only (sales-team) cards keep the default cursor. */
.platform-source-card--addable {
    cursor: pointer;
}

/* Small circular sync glyph — replaces the old empty rounded square that read as a checkbox.
   Skipped for .ic: the icon component owns its own size/plate from the ladder. */
.platform-source-card--sample .platform-source-card__icon:not(.ic) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.29rem;
    height: 2.29rem;
    border-radius: var(--radius-full, 999px);
    color: var(--color-primary);
}

.platform-source-card__body {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: var(--space-1);
    min-width: 0;
    flex: 1 1 auto;
}

.platform-source-card--sample .platform-source-card__name {
    line-height: var(--line-height-tight);
}

.platform-source-card__heading {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
}

.platform-source-card__class {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 20px;
    width: 20px;
    height: 20px;
    border-radius: var(--radius-full, 999px);
    background: var(--color-surface);
    box-shadow: 0 1px 2px rgba(20, 50, 80, 0.06);
}

.platform-source-card__class svg {
    width: 16px;
    height: 16px;
}

.platform-source-card__class--advanced {
    color: var(--color-tertiary);
}

.platform-source-card__class--enterprise {
    color: var(--color-warning);
}

.platform-source-card__line {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-tight);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    min-width: 0;
}

/* Sized/shaped to match a Ghost IconOnly PlatformButton at Medium (32px circle for its 20px
   "Large"-tier icon, per PlatformButton.razor.cs's ResolvedIconSize ladder) — this button stays
   bespoke rather than a real PlatformButton (see the component-preview audit notes: it relies on
   @onclick:stopPropagation, which TelerikButton's OnClick can't carry), but should still look and
   behave identically to one. */
.platform-source-card__add--plus {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    min-width: 2rem;
    padding: 0;
    flex-shrink: 0;
    align-self: center;
    color: var(--color-primary);
    background-color: transparent;
    border: none;
    border-radius: 50%;
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

.platform-source-card__add--plus:hover {
    color: var(--color-primary);
    background-color: var(--color-primary-soft-bg);
}

/* Added state (Figma State=Added): brand-tinted card with a quantity stepper replacing "+". */
.platform-source-card--added {
    background-color: var(--color-selected-bg);
    border-color: var(--color-selected-border);
}

.platform-source-card__stepper {
    display: inline-flex;
    align-items: stretch;
    flex-shrink: 0;
    align-self: center;
    height: 58px;
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--color-border-strong);
    border-radius: var(--radius-md);
    overflow: hidden;
}

.platform-source-card__stepper-value {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    border-inline-end: var(--border-width) solid var(--color-border-strong);
    font-weight: var(--font-weight-semibold);
    color: var(--color-heading);
}

.platform-source-card__stepper-controls {
    display: flex;
    flex-direction: column;
    width: var(--size-icon-sm);
}

.platform-source-card__stepper-btn {
    flex: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    font-size: var(--font-size-base);
    line-height: 1;
    color: var(--color-text-muted);
    background-color: transparent;
    border: none;
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

.platform-source-card__stepper-btn:first-child {
    border-bottom: var(--border-width) solid var(--color-border-strong);
}

.platform-source-card__stepper-btn:disabled {
    color: var(--color-control-disabled-text);
    cursor: not-allowed;
}

.platform-source-card__stepper-btn:hover:not(:disabled) {
    color: var(--color-primary);
    background-color: var(--color-primary-soft-bg);
}

/* Fixed flow footer for the onboarding wizard (Case/Sources/Authorize/Run) — see
   WizardFooter.razor. Fixed on mobile (page scrolls underneath a pinned bottom
   action bar — a deliberate, still-desired pattern there), with
   .platform-page--has-footer reserving matching bottom padding so the fixed bar
   never covers content. On desktop (>=768px) this is overridden below: the
   surrounding .platform-page--fixed layout already disables document scrolling
   entirely (see body:has(.platform-page--fixed) and .platform-two-pane's own
   internal scroll regions), so position:fixed buys nothing there — the footer
   instead becomes a normal in-flow flex sibling of .platform-two-pane, sized to
   its own real content height, with no padding-bottom magic number needed. */
.platform-page__footer {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 10;
    background-color: var(--color-bg);
    border-top: var(--border-width) solid var(--color-border);
}

.platform-page__footer-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-2) var(--space-6);
}

.platform-page__footer-actions {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

/* Keep the "Get help" ghost link (and the CTA beside it) from shrinking/wrapping
   in the footer's tight flex row — .platform-btn--ghost itself doesn't set this
   since it's not normally used in such a tight row elsewhere. */
.platform-page__footer-actions > .platform-btn {
    flex-shrink: 0;
    white-space: nowrap;
}

/* Stacks a page's optional hint text above its block CTA as a single flex item next to "Get
   help", instead of both being flat row siblings. A flat row let the CTA's width:100% resolve
   its flex-basis against the *whole* footer row (way past its own content), so flex-shrink only
   partially clawed it back and the button ended up rendered far wider than its label needed.
   Stacked here, width:100% resolves against this wrapper's own shrink-to-fit size
   instead, which the button's own content already caps in the common (no-hint) case. */
.platform-page__footer-cta {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--space-1);
    min-width: 0;
}

.platform-page__footer-cta > .platform-cta--block {
    width: auto;
}

.platform-page--has-footer {
    padding-bottom: 4rem;
}

.platform-source-continue-hint {
    text-align: center;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    margin: 0 0 var(--space-2);
}

/* Selected sources */
/* Step 2 right column: keep the column footprint reserved (grid track stays) but hide the inner
   panel + CTA while nothing is selected, so the left grid does not reflow when the first system
   is added (visibility:hidden preserves layout; display:none would collapse it). */

.platform-source-sidebar--reserved {
    visibility: hidden;
}

.platform-selected-sources {
    padding: 0;
    background: transparent;
    border: none;
    box-shadow: none;
}

/* ---- Sync toggle sections ("Sync contacts" / "Sync calendars") ---------------------------- */

/* "Pick a sync type" prompt: shown in the Selected panel, above the toggle sections,
   in place of the easy-to-miss footer hint that used to sit next to the Continue CTA. */
.platform-sync-prompt {
    padding: var(--space-4) var(--space-5);
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--color-primary);
    border-radius: var(--radius-xl);
}

.platform-sync-prompt__title {
    margin: 0;
    font-weight: var(--font-weight-semibold);
    color: var(--color-primary);
}

.platform-sync-prompt__subtitle {
    margin: var(--space-1) 0 0;
    color: var(--color-text-muted);
}

.platform-sync-toggle {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    cursor: pointer;
    user-select: none;
}

.platform-sync-toggle__label {
    font-weight: var(--font-weight-semibold);
    color: var(--color-heading);
}

/* Nothing selected supplies this data type: the toggle keeps the user's choice but has nothing to
   act on, so it and its label read as inactive while the group says why. */
.platform-sync-toggle--inactive .platform-sync-toggle__label {
    color: var(--color-text-muted);
}

.platform-sync-group__empty {
    margin: 0;
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
}

/* OFF state (R5): the group's cards are visibly disabled/dimmed and non-interactive, but remain in
   the DOM so the underlying selection is retained and restored on toggle-back. */
.platform-sync-group--off .platform-sync-group__list {
    opacity: 0.4;
    filter: grayscale(1);
}

/* ---- Narrow "Selected" panel card: reuses the left card visual language at sidebar width ---- */
.platform-source-card--panel {
    height: auto;
    min-height: 64px;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-6);
    background-color: var(--color-surface);
    border: none;
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-lg);
}

/* R7: never ellipsis-truncate — the title wraps within the narrow column. */
.platform-source-card--panel .platform-source-card__name {
    white-space: normal;
    overflow-wrap: anywhere;
}

.platform-source-card--panel .platform-source-card__icon:not(.ic) {
    width: var(--size-icon-md);
    aspect-ratio: 1;
}

/* ============================================================
 * 8. AUTH BLOCKS  (Step 3 "Authenticate" panel)
 * ============================================================ */

.platform-hub-account-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
}

.platform-hub-account-row__label {
    font-weight: var(--font-weight-medium);
}

.platform-hub-account-row[data-status="Connected"] .platform-hub-account-row__status {
    color: var(--color-success);
}

.platform-auth-block-list {
    list-style: none;
    padding: 0;
    margin: 0;
}

.platform-auth-block-list__item {
    display: block;
}

.platform-auth-settings__footer {
    margin-top: var(--space-2);
}

/* ---- Auth card (active / dependent / connected states) ---------------------------------- */
/* 24 padding all round, 16px stack gap (Figma auth cards 915:31829 / 915:32143 / 1389:17972). */
.platform-auth-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    padding: var(--space-6);
    background-color: var(--color-surface);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-sm);
}

.platform-auth-card[data-state="blocked"] {
    background-color: var(--color-surface-subtle);
}

.platform-auth-card__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
}

/* Title hugs the provider icon; badge/controls keep the right edge (Figma auth card heads:
   icon+title sit together on the left in every state). */
.platform-auth-card__head .platform-auth-card__title {
    margin-right: auto;
}

/* Connected state (Figma 565:13350 / 565:13374): the pill and the account sit on one row under the
   head; the helper and the address inputs are not rendered. */
.platform-auth-card__status {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
}

.platform-auth-card__mailbox-address {
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
    text-align: right;
    overflow-wrap: anywhere;
}

/* Sentence-case "Connected" pill, 4x12 padding (Figma auth frames 915:31650 / 558:12349). */
.platform-auth-card__badge {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    letter-spacing: 0.03em;
    line-height: 1.5;
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-pill);
    background-color: var(--color-success-bg);
    color: var(--color-success);
}

.platform-auth-card__helper {
    font-size: var(--font-size-sm);
    line-height: var(--line-height-normal);
    color: var(--color-text-muted);
}

.platform-auth-card__blocked {
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
}

.platform-auth-card__connected {
    font-size: var(--font-size-sm);
    color: var(--color-text-annotation);
    text-align: right;
    overflow-wrap: anywhere;
}

.platform-auth-card__error {
    font-size: var(--font-size-sm);
    color: var(--color-danger);
    background-color: var(--color-danger-bg);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
}

.platform-auth-card__field {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.platform-auth-card__field-label {
    font-family: var(--font-sans);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    color: var(--color-text-muted);
}

/* Box model matched to Step 2/3's Kendo-driven .k-input-inner override (platform-layout.css,
   the "Padding/line-height for every Kendo text-input/picker-family control" rule above) and to
   the --kendo-* -> --color-* token bridge (kendo-theme-custom.css/theme-tokens.css), so these raw
   fields — used by the auth blocks that bypass AuthBlockCard's own template via ActiveContent/
   AdditionalFields (Apple's ID/password inputs, SyncTunnel's email input, Microsoft's mailbox
   fields, the GAL modal) — read as visually consistent with the migrated PlatformSelect/
   PlatformTextInput fields next to them, even though they stay native HTML. */
/* input.platform-auth-card__input (not just .platform-auth-card__input): Section 6's generic
   reset (input[type="email"], input[type="password"], ..., textarea { padding: var(--space-2)
   var(--space-3); ... }) has higher specificity than a bare class (element+attribute beats a
   single class), so it was silently winning over every property below — the padding/font-size/
   color/background/border/border-radius here never actually rendered. Matching its specificity
   with the input element type, and relying on source order (this rule loads later), makes ours
   win instead. */
input.platform-auth-card__input {
    padding-inline: var(--space-4);
    padding-block: var(--space-3);
    font-size: var(--font-size-base);
    line-height: var(--line-height-tight);
    color: var(--color-text);
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--color-border);
    border-radius: var(--radius-md);
}

input.platform-auth-card__input:focus {
    outline: none;
    border-color: var(--color-primary);
    box-shadow: var(--focus-ring);
}

.platform-auth-card__autocomplete {
    display: flex;
    max-height: 15rem;
    flex-direction: column;
    overflow-y: auto;
    border: var(--border-width) solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
}

.platform-auth-card__autocomplete-option {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-3) var(--space-4);
    border: 0;
    border-bottom: var(--border-width) solid var(--color-border);
    background: transparent;
    color: var(--color-text);
    text-align: left;
    cursor: pointer;
}

.platform-auth-card__autocomplete-option:last-child {
    border-bottom: 0;
}

.platform-auth-card__autocomplete-option:hover,
.platform-auth-card__autocomplete-option:focus-visible {
    background: var(--color-surface-hover);
}

.platform-auth-card__autocomplete-option span,
.platform-auth-card__search-status {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

/* .platform-auth-card__cta family retired — the "Connect"/"Authorize" CTA now renders via
   PlatformButton (Variant="Primary" Block="true"), which gets hover/disabled from Kendo's own
   .k-button state rules instead of a hand-authored :hover/:disabled pair. */

.platform-auth-card__fineprint {
    font-size: var(--font-size-xs);
    line-height: var(--line-height-normal);
    color: var(--color-text-muted);
}

/* ---- Company settings card (Step 3 "Confirm company settings") ------------------------- */
.platform-company-card {
    gap: var(--space-4);
}

.platform-company-card__form {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    /* 8 below the helper, against the card's own 16 stack (Figma 915:32143). */
    margin-top: calc(var(--space-2) - var(--space-4));
}

.platform-company-card .platform-field {
    gap: var(--space-1);
}

.platform-company-card select {
    min-height: 2.6875rem;
}

.platform-company-card .platform-auth-card__fineprint {
    margin-top: var(--space-1);
}

.platform-company-card__toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    padding: 0;
    background: none;
    border: none;
    cursor: pointer;
}

.platform-company-card__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    width: 100%;
    padding: 0;
    background: none;
    border: none;
    cursor: pointer;
    text-align: left;
}

/* Figma 915:31829: the card waits in a subdued fill until it is opened, like every other
   not-yet-active auth card. */
.platform-company-card[data-state="idle"] {
    background-color: var(--color-card-surface-subdued);
}

/* The old .platform-cta.platform-cta--dark disabled rule (k-disabled/[disabled]/[aria-disabled])
   was removed here alongside the rest of the "platform-cta--dark" family above — orphaned for
   the same reason (no TelerikButton emits that class anymore). */

/* ============================================================
 * 9. SYNC RUN / CONNECTORS
 * ============================================================ */

.platform-run-result {
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--color-danger);
    background-color: var(--color-danger-bg);
    border: var(--border-width) solid var(--color-danger);
}

.platform-run-prerequisite,
.platform-run-interrupted {
    padding: var(--space-3) var(--space-4);
    color: var(--color-warning);
    background-color: var(--color-warning-bg);
    border: var(--border-width) solid var(--color-warning);
    border-radius: var(--radius-md);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}

/* Test-sync error card (Figma "run" error frame 805:13196, node 805:13759): white card with a
   red hairline, sitting between the panel intro and the progress groups. The extra top margin
   lifts the panel's 16px stack gap to the mockup's 24px intro-to-card distance. */
.platform-run-error-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-top: var(--space-2);
    padding: var(--space-5) var(--space-6) var(--space-6);
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--color-danger);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
}

.platform-run-error-card__title {
    color: var(--color-danger);
}

.platform-run-error-card__body {
    font-size: var(--font-size-sm);
    line-height: var(--line-height-normal);
    color: var(--color-text-muted);
}

.platform-run-error-card__link {
    font-weight: var(--font-weight-semibold);
}

/* Passed test sync (Figma "run" complete frame 578:17969, node 610:9140): the same card shape as
   the error card, with the teal hairline and title; #27b898 / #1f8b74 are off-token like the reds. */
.platform-run-passed-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-top: var(--space-2);
    padding: var(--space-5) var(--space-6) var(--space-6);
    background-color: var(--color-surface);
    border: var(--border-width) solid #27b898;
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
}

.platform-run-passed-card__title {
    color: #1f8b74;
}

.platform-run-passed-card__body {
    font-size: var(--font-size-sm);
    line-height: var(--line-height-normal);
    color: var(--color-text-muted);
}

/* Schema preview — top-down data-flow (source cards -> Hub -> Tunnel -> target cards). */

/* Stacks the per-data-type preview sections (Contacts / Calendars); each section is a fully
   self-contained Hub+Tunnel view, so a generous gap keeps them reading as distinct data-flows. */

.platform-schema-view__error {
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    font-size: var(--font-size-sm);
    color: var(--color-danger);
    background-color: var(--color-danger-bg);
    border: var(--border-width) solid var(--color-danger);
}

.platform-flow {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
}

/* Each stage (source spokes + Hub, or Tunnel + target spokes) is a tight group with a
   right-side gutter that holds the routing rail. position: relative anchors the rail. */
.platform-flow__group {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding-right: 2.75rem;
}

/* Right-side routing rail: a vertical bus in the gutter with a horizontal elbow into the
   box it feeds. The source arrows point at it; it turns left into the Hub / target. */
.platform-flow__rail {
    position: absolute;
    right: 0;
    width: 0.75rem;
    pointer-events: none;
    border-right: var(--border-width-strong) solid var(--color-card-border);
}

/* Sources -> Hub: bus spans the first source's arrow down to the Hub, elbowing in at the bottom.
   Flow rows and the Hub/Tunnel boxes are 68px tall, so centres sit 34px = 2.125rem from each edge.
   The bottom is an ELBOW (the rail's own border-right + border-bottom miter into a full corner) so
   it sits at the exact 2.125rem Hub centre. The top is a FREE end that only meets the source-arrow's
   separate 2px connector: pulling it up half a border-width (to the connector's top edge) fills the
   corner solid instead of leaving a 1px notch beside the arrow. */
.platform-flow__rail--in {
    top: calc(2.125rem - (var(--border-width-strong) / 2));
    bottom: 2.125rem;
    border-bottom: var(--border-width-strong) solid var(--color-card-border);
}

/* Tunnel -> targets: bus spans the Tunnel down to the target's arrow, elbowing in at the top.
   Mirror of --in: the top is the ELBOW (exact 2.125rem centre) and the bottom is the FREE end,
   pulled down half a border-width so the bus reaches the target connector's bottom edge and the
   corner reads as full, matching the elbow. */
.platform-flow__rail--out {
    top: 2.125rem;
    bottom: calc(2.125rem - (var(--border-width-strong) / 2));
    border-top: var(--border-width-strong) solid var(--color-card-border);
}

.platform-flow__cards {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    list-style: none;
    padding: 0;
    margin: 0;
}

.platform-flow__card {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    background-color: var(--p-white);
    border: var(--border-width) solid var(--color-card-border);
    border-radius: var(--radius-md);
}

.platform-flow__card--secondary {
    color: var(--color-text-muted);
}

/* Figma 549:6850: the card stops 88px short of the bus (group gutter 44 + margin 44), the
   direction bubble straddles its right edge and the 72px stub runs from the bubble to the bus. */
.platform-flow__card--rail-entry {
    margin-right: 2.75rem;
    padding-right: 2rem;
}

/* Merged tunnel block: the target row(s) and the test-mailbox row read as one card with two
   levels, so they drop their per-row gap and share a single outer border/radius. */
.platform-flow__cards--merged {
    gap: 0;
}

.platform-flow__cards--merged .platform-flow__card {
    border-radius: 0;
    border-top-width: 0;
}

.platform-flow__cards--merged .platform-flow__card:first-child {
    border-top-width: var(--border-width);
    border-top-left-radius: var(--radius-md);
    border-top-right-radius: var(--radius-md);
    background-color: var(--color-flow-tint-bg);
}

.platform-flow__cards--merged .platform-flow__card:first-child .platform-flow__card-title {
    color: var(--color-text-muted);
}

.platform-flow__cards--merged .platform-flow__card:first-child .platform-flow__card-icon {
    opacity: 0.8;
}

.platform-flow__cards--merged .platform-flow__card:last-child {
    border-bottom-left-radius: var(--radius-md);
    border-bottom-right-radius: var(--radius-md);
}

/* Nothing squeezes these rows (no direction picker), so the copy gets the full row width instead
   of the 12rem source-card budget — it still never wraps, so the rail elbows stay aligned. */
.platform-flow__cards--merged .platform-flow__card-body {
    flex: 1 1 auto;
    max-width: none;
}

/* The test-mailbox row carries no icon, but its copy stays aligned under the row above: the
   spacer is the icon slot it would otherwise occupy. */
.platform-flow__card-anchor-spacer {
    flex-shrink: 0;
    width: 1.5rem;
}

.platform-flow-row__body .platform-flow__card-anchor-spacer {
    width: 28px;
}

.platform-flow__card-body {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    flex: 0 0 auto;
    max-width: 12rem;
    min-width: 0;
    padding-right: var(--space-2);
}

/* Title/desc are truncated with an ellipsis (never wrapped to extra lines) so a wide trailing
   slot — e.g. the dashboard widget's long "Connect with Customer Success..." CTA in
   .platform-flow__card-controls, which never shrinks (flex-shrink: 0) or wraps (white-space:
   nowrap) — can squeeze .platform-flow__card-body without growing the row taller than the
   single-line height every rail/elbow offset in this file assumes (KB: the rail's connector to the
   arrow bubble is positioned with fixed rem offsets from the group's edges, not derived from the
   actual row height, so any extra wrapped line desyncs the elbow from the arrow it's meant to
   meet). Harmless when nothing squeezes the row (Step 3/4 previews, dashboard rows with no CTA,
   or a Done node): the short static label/description already fits on one line there, so this only
   changes behavior for the exact case that used to break alignment. */
.platform-flow__card-title {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    color: var(--color-heading);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.platform-flow__card-desc {
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Card-arrow slot: the outer run-status indicator. Always rendered (display is unconditional —
   never display:none) so its ::after pseudo-element keeps bridging the card to .platform-flow__rail
   in every state (Step 3, Step 4 pre-run, Step 4 post-run/"Step 5" active-run, dashboard widget).
   Only the glyph rendered *inside* this bubble changes with RunStatus — see
   SchemaPreviewViewer.razor — never the bubble's own layout size or its connector stub below. */
.platform-flow__card-arrow {
    position: absolute;
    top: 50%;
    right: -1rem;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--size-icon-md);
    height: var(--size-icon-md);
    flex-shrink: 0;
    border-radius: var(--radius-pill);
    background-color: var(--color-heading, #111);
    color: var(--p-white, #fff);
}

.platform-flow__card-arrow::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 100%;
    width: 4.5rem;
    border-top: var(--border-width-strong) solid var(--color-card-border);
    transform: translateY(-50%);
}

/* Idle AND Running share the same light bubble look (mockup: only the inner glyph changes while
   running — direction arrow swaps for a spinning "sync" icon — the ring/fill never recolors).
   Done/Failed/action-required instead override just the border-color via the --done/--stopped/
   --action classes below, so this stays the shared base for every other state. */
.platform-flow__card-arrow:not([data-run-status]),
.platform-flow__card-arrow[data-run-status="Running"] {
    color: var(--color-heading);
    background-color: var(--color-flow-bubble-bg);
    border: var(--border-width) solid var(--color-card-border);
}

/* Continuous rotation applied directly to the "sync" icon glyph while Running (replaces the old
   bordered-div loader — same keyframe, now spins the actual icon instead of drawing a ring). */
.platform-flow__card-arrow-spin {
    display: inline-flex;
    animation: platform-flow-spin 2s linear infinite;
}

@keyframes platform-flow-spin {
    to {
        transform: rotate(360deg);
    }
}

/* Figma 524:3136 toggle menu: 114x34, #cbd6e1 stroke, 8px radius, 12px label. */
.platform-flow__direction-select {
    flex: 0 0 auto;
    height: 34px;
    margin-left: auto;
    font-size: var(--font-size-xs);
    border-color: var(--color-border-strong);
}

.platform-flow__direction-label {
    flex: 0 0 auto;
    margin-left: auto;
    font-size: var(--font-size-xs);
    color: var(--color-text);
    white-space: nowrap;
}

.platform-flow__box {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    padding: var(--space-3) var(--space-4) var(--space-3) var(--space-3);
    background-color: transparent;
    border: var(--border-width) solid var(--color-card-border);
    border-radius: var(--radius-md);
}

.platform-flow__box-title {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    color: var(--color-heading);
}

.platform-flow__box-desc {
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
}

/* Groups the box title + description into a single text unit. In the base (wizard) stacked
   layout it is transparent (display:contents) so title/desc remain direct column children and
   the Steps 3/4/5 layout is unchanged; the widget row layout below turns it into a shrinkable
   text column beside the fixed marker. */
.platform-flow__box-text {
    display: contents;
}

/* Vertical connector line between flow stages; the --arrow variant carries a down chevron. */
.platform-flow__connector {
    align-self: center;
    width: var(--border-width-strong);
    height: var(--space-5);
    background-color: var(--color-card-border);
}

.platform-flow__connector--arrow {
    display: flex;
    align-items: center;
    justify-content: center;
    width: auto;
    background-color: transparent;
    color: var(--color-text);
}

/* Clear vertical separation between the Hub stage and the Tunnel stage: two short stems flank a
   down chevron. Kept tight to match the Figma connector (37px band, 28px bubble, short stems) so
   the two stages read as distinct without an oversized dead gap. */
/* Hub->Tunnel gap (806:13914 / 578:18021): 37px between the boxes, one continuous hairline stem
   behind the 28px bubble — no padding or segment gaps, so it never reads as a dashed line. */
.platform-flow__gap {
    flex-direction: column;
    gap: 0;
    height: 2.3125rem;
    padding: 0;
    align-self: center;
}

.platform-flow__gap-stem {
    flex: 1 1 auto;
    width: var(--border-width-strong);
    min-height: 0;
    background-color: var(--color-hairline);
}

/* Hub-to-Tunnel run-status bubble. Same idle/Running/Done/Failed state family and visual language
   as .platform-flow__card-arrow (dark idle bubble, spinner glyph, success/danger colors below), but
   kept as its OWN class rather than sharing .platform-flow__card-arrow: this is a single
   connector-level indicator (one per Hub→Tunnel gap), not a per-card slot, so it must not be swept
   up by .platform-flow__card-arrow queries used elsewhere to count/verify the per-card
   connector-rail slots on Steps 3/4 (see SyncRunPageTests). Laid out as a plain centered flex item —
   no absolute rail-bridging offset/::after stub — because the two .platform-flow__gap-stem lines
   above/below it already form the vertical connector. */
.platform-flow__connector-status {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--size-icon-md);
    aspect-ratio: 1;
    flex-shrink: 0;
    border-radius: var(--radius-pill);
    background-color: var(--color-heading, #111);
    color: var(--p-white, #fff);
}

/* Idle AND Running share the same light bubble look, matching .platform-flow__card-arrow above —
   only the inner glyph changes (down-arrow swaps for the spinning "sync" icon) while Running. */
.platform-flow__connector-status:not([data-run-status]),
.platform-flow__connector-status[data-run-status="Running"] {
    color: var(--color-heading);
    background-color: var(--color-flow-bubble-bg);
    border: var(--border-width) solid var(--color-card-border);
}

/* ============================================================
 * 10. DASHBOARD
 * ============================================================ */

/* Page header: "Dashboard" title + "Active" status pill (Version D/18). */
/* Pill geometry and 12/18 SemiBold type per the dashboard flow-row badges
   (Figma 876:15728 "Stopped", 4x12 padding, radius 999, letter-spacing 3%, no stroke). */
.platform-status-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-pill);
    color: var(--color-text);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    line-height: 1.5;
    letter-spacing: 0.03em;
}

/* "Not started" has no styled Figma pill yet — keep the old outlined look, height-compensated. */
.platform-status-badge--not-started {
    border: var(--border-width) solid var(--color-border);
    padding: calc(var(--space-1) - var(--border-width)) calc(var(--space-3) - var(--border-width));
}

.platform-status-badge__dot {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: var(--radius-pill);
    background-color: var(--color-text-muted);
    flex-shrink: 0;
}

.platform-status-badge--active .platform-status-badge__dot {
    background-color: var(--color-success);
}

/* Active flow badge: green fill + green text (Figma dashboard "Active" pill). */
.platform-status-badge--active {
    color: var(--color-success);
    background-color: var(--color-success-bg);
    border-color: var(--color-success-bg);
}

/* Action-required / needs-attention badge: amber fill (Figma dashboard "Action required" pill). */
.platform-status-badge--action-required,
.platform-status-badge--needs-attention {
    color: var(--color-warning);
    background-color: var(--color-warning-bg);
    border-color: var(--color-warning-bg);
}

.platform-status-badge--action-required .platform-status-badge__dot,
.platform-status-badge--needs-attention .platform-status-badge__dot {
    background-color: var(--color-warning);
}

/* Stopped/failed badge: red fill (Figma dashboard "Stopped" pill). */
.platform-status-badge--stopped {
    color: var(--color-danger);
    background-color: var(--color-danger-bg);
    border-color: var(--color-danger-bg);
}

.platform-status-badge--stopped .platform-status-badge__dot {
    background-color: var(--color-danger);
}

/* Shared circular dashed marker: the small "node" bullet used by every row of the
   dashboard's source -> Hub -> Tunnel -> target flow chain (SchemaPreviewViewer widget
   mode). Purely decorative (aria-hidden). NOTE (40d01d): removed from DashboardLeftMenu
   items — the semantic icon is now the sole leading affordance on menu rows; this class
   is retained solely for the flow-chain rows. */
/* Hub/Tunnel structural glyphs (asterisk / circle-dot) that replace the dashed marker in the
   flow boxes; fixed size so they never shrink beside the box text. */
.platform-flow__box-icon {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Finish-setup panel: title/schedule note above the flow chain, dark CTAs below. */
.platform-finish-setup-card__header {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.platform-finish-setup-card__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
}

/* Widget-mode (dashboard) flow chain: source/target cards and the Hub/Tunnel boxes gain the
   shared dashed marker as a leading visual anchor. Hub/Tunnel boxes switch to a single-line row
   (marker + title + muted parenthetical) instead of the stacked title/desc used by Steps 3/4/5 —
   scoped strictly to [data-mode="widget"] so the wizard preview/configure flows never regress. */
[data-mode="widget"] .platform-flow__card {
    gap: var(--space-3);
}

/* Hub/Tunnel boxes gain the marker-beside-text row layout in EVERY mode that renders the dashed
   anchor: the dashboard widget AND the wizard preview (Step 3) / configure (Step 4) flows. This
   was previously scoped strictly to [data-mode="widget"], so once 8b38b5 turned the anchor on
   everywhere (ShowFlowAnchors => true) the marker stacked ABOVE the title/subtitle in the wizard
   boxes. Broadening the row layout to preview/configure puts the marker beside the text there too,
   mirroring the source/target cards (which already render the marker beside a stacked title/desc). */
[data-mode="widget"] .platform-flow__box,
[data-mode="preview"] .platform-flow__box,
[data-mode="configure"] .platform-flow__box {
    flex-direction: row;
    align-items: center;
    gap: var(--space-3);
}

[data-mode="preview"] .platform-flow__box,
[data-mode="configure"] .platform-flow__box {
    margin-right: -2rem;
    /* Figma puts the text 8px after the 40px glyph (icon 12→52, body at 60) — the boxes used the
       dashboard's 12px gap. */
    gap: var(--space-2);
}

/* Step 3/4 icon column (Figma 578:17969): the wizard rows are 68px tall and carry a 40px glyph at
   x=12 — twice the dashboard's 24px column, which stays untouched. */
[data-mode="preview"] .platform-flow__card-icon,
[data-mode="configure"] .platform-flow__card-icon,
[data-mode="preview"] .platform-flow__card-anchor-spacer,
[data-mode="configure"] .platform-flow__card-anchor-spacer,
[data-mode="preview"] .platform-flow__box-icon,
[data-mode="configure"] .platform-flow__box-icon {
    width: 2.5rem;
    height: 2.5rem;
}

[data-mode="preview"] .platform-flow__card,
[data-mode="configure"] .platform-flow__card,
[data-mode="preview"] .platform-flow__box,
[data-mode="configure"] .platform-flow__box {
    min-height: 4.25rem;
}

/* Widget mode keeps the compact single-line "title (parenthetical)" text: beside the fixed dashed
   marker, title + muted parenthetical share one baseline-aligned, wrapping row (min-width:0 lets it
   narrow instead of overflowing under the icon; flex-wrap keeps the single-line look while letting a
   wrapped line stay inside the column, indented clear of the marker rather than under it). */
[data-mode="widget"] .platform-flow__box-text {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: var(--space-3);
    min-width: 0;
}

/* Wizard preview/configure keep the title stacked ABOVE its full-sentence description (the Step 3/4
   reading), now grouped into a single shrinkable column that sits BESIDE the marker instead of below
   it (min-width:0 lets the sentence wrap inside the column rather than push the box wider). */
[data-mode="preview"] .platform-flow__box-text,
[data-mode="configure"] .platform-flow__box-text {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    min-width: 0;
}

[data-mode="widget"] .platform-flow__box-desc {
    font-size: var(--font-size-sm);
}

/* Widget (dashboard) rows follow the Figma flow-card anatomy: a title-only body, a folder chip
   ("Contacts") and a kebab affordance on the right — no description line or direction dropdown
   (those belong to the Step-3 Review/preview layout). Scoped to widget mode so preview/configure
   never regress. */
[data-mode="widget"] .platform-flow__card-desc {
    display: none;
}

[data-mode="widget"] .platform-flow__card-body,
[data-mode="preview"] .platform-flow__card-body,
[data-mode="configure"] .platform-flow__card-body {
    flex: 1 1 auto;
    max-width: none;
}

.platform-flow__card-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    margin-left: auto;
    flex-shrink: 0;
    font-size: var(--font-size-xs);
    color: var(--color-flow-chip-fg);
    white-space: nowrap;
}

.platform-flow__card-kebab {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    flex-shrink: 0;
    border-radius: var(--radius-pill);
}

.platform-flow__box-kebab {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    margin-left: auto;
    flex-shrink: 0;
    border-radius: var(--radius-pill);
}

.platform-flow__kebab--action {
    padding: 0;
    color: inherit;
    background: none;
    border: none;
    cursor: pointer;
    text-decoration: none;
}

.platform-flow__kebab--action:hover,
.platform-flow__kebab--action:focus-visible,
.platform-flow__kebab--action[aria-expanded="true"] {
    background-color: var(--color-surface-hover);
}

/* Same focus-ring parity fix as the checkbox/textbox/switch above: suppress the ring that
   persists after a plain mouse click, then redraw it only for :focus-visible on the app's own
   --focus-ring token instead of the browser's default ring shape. */
.platform-flow__kebab--action:focus {
    outline: none;
    box-shadow: none;
}

.platform-flow__kebab--action:focus-visible {
    box-shadow: var(--focus-ring);
}

.platform-flow__menu-host {
    position: relative;
    display: inline-flex;
}

.platform-flow__box > .platform-flow__menu-host {
    margin-left: auto;
}

/* Invisible click-catcher under an open kebab menu: any click outside the menu closes it. */
.platform-flow__menu-backdrop {
    position: fixed;
    inset: 0;
    z-index: 19;
}

.platform-flow__menu {
    position: absolute;
    top: calc(100% + var(--space-1));
    right: 0;
    z-index: 20;
    min-width: 10rem;
    padding: var(--space-1);
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--color-card-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
}

.platform-flow__menu-item {
    display: flex;
    width: 100%;
    padding: var(--space-3) var(--space-4);
    color: var(--color-text);
    font: inherit;
    text-align: left;
    text-decoration: none;
    background: none;
    border: none;
    border-radius: var(--radius-md);
    cursor: pointer;
}

/* Matches PlatformSelect's own .k-list-item hover: same background token, same radius/padding
   scale. Explicit text-decoration:none here (not just on the base rule) is needed for the "Edit"
   item specifically — it's a real <a>, and the app's global `a:hover { text-decoration: underline }`
   reset (0,1,1 specificity) otherwise beats this class's own base rule (0,1,0) the instant it's
   hovered; matching it here at (0,2,0) wins outright. */
.platform-flow__menu-item:hover,
.platform-flow__menu-item:focus-visible {
    background-color: var(--color-primary-soft-bg);
    text-decoration: none;
}

.platform-flow__card-badge {
    position: absolute;
    top: -0.25rem;
    right: -0.25rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1rem;
    height: 1rem;
    border-radius: var(--radius-pill);
    background: var(--color-surface, #fff);
}

/* Action-required badge (Figma 663:4600, node 793:10036): a live Kendo WarningTriangle (solid),
   replacing the byte-exact SVG export that used to be painted over a hidden icon. */
.platform-flow__card-badge--action {
    top: -4px;
    right: -4px;
    color: var(--color-warning);
}

.platform-flow__card-arrow--action:not([data-run-status]),
.platform-flow__card-arrow:has(> .platform-flow__card-badge--action):not([data-run-status]) {
    border-color: var(--color-warning);
}

/* Stopped flow badge — a live Kendo XCircle (solid), colored via the real --color-danger token
   instead of the retired flow-badge-stopped.svg export's literal #b01a1a fill (close to but not
   exactly --color-danger's #b91c1c — switched to the token per design decision). */
.platform-flow__card-badge--stopped {
    top: -4px;
    right: -4px;
    color: var(--color-danger);
}

/* The failed edge keeps its direction glyph but the bubble ring turns red, matching the failed
   badge's --color-danger token (previously a deliberately off-token #a4070a; aligned so the ring
   and badge read as the same red per design review). */
.platform-flow__card-arrow--stopped:not([data-run-status]) {
    border-color: var(--color-danger);
}

/* Finished edge on the run frames (578:17969, nodes 675:4221 / 578:18022): teal ring around the
   direction glyph plus the check-circle badge on the rows; the Hub->Tunnel connector gets the
   ring and the same badge (578:18569). */
.platform-flow__card-arrow--done:not([data-run-status]),
.platform-flow__connector-status--done:not([data-run-status]) {
    border-color: var(--color-tertiary);
}

/* Finished badge — a live Kendo CheckCircle (solid), colored via the real --color-tertiary token
   instead of a hardcoded "off-token" hex that turned out to be an exact match for it all along. */
.platform-flow__card-badge--done {
    top: -4px;
    right: -4px;
    color: var(--color-tertiary);
}

.platform-flow__card-setup {
    margin-left: auto;
    font-size: var(--font-size-xs);
    /* Mockup #5a7086 sits between --p-neutral-400 and -500 (dE2000 3.4 to both) — exact value. */
    color: #5a7086;
    white-space: nowrap;
}

.platform-flow__card-setup + .platform-flow__card-kebab,
.platform-flow__card-setup ~ .k-button {
    margin-left: var(--space-3);
}

/* An empty heading (dashboard widget suppresses its own "Hub topology" title so it can sit
   directly inside the Finish-setup panel without a duplicate heading) collapses to nothing. */
.platform-schema-view h2:empty {
    display: none;
}

/* Optional per-node action slot rendered via SchemaPreviewViewer's TargetCardControls (e.g. the
   dashboard's "Connect with Customer Success to complete setup" CTA on a not-yet-Done target). */
.platform-flow__card-cta {
    white-space: nowrap;
}

.platform-promo-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: var(--space-5);
}

.platform-promo-card {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: var(--space-6);
    min-height: 169px;
    padding: var(--space-5) var(--space-6) var(--space-6);
}

.platform-promo-card p {
    color: var(--color-text);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-normal);
}

.platform-promo-card__cta {
    align-self: flex-start;
    min-height: 34px;
}

.platform-dashboard__footer {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 63px;
    padding: var(--space-2) var(--space-6);
    text-align: center;
}

/* "Contacts synced" section: plain heading (no outer panel) above per-stage status cards. */
.platform-contacts-synced__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: var(--space-4);
}

.platform-status-summary-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-5);
    background-color: var(--color-surface-sunken);
    border: var(--border-width) solid var(--color-border);
    border-radius: var(--radius-xl);
}

.platform-status-summary-card__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.platform-status-summary-card__title {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}

/* .platform-status-summary-card__menu retired — the kebab button now renders via PlatformButton
   (Variant="Ghost" IconOnly="true"). */

.platform-status-summary-card__status {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
}

.platform-status-summary-card__status-text {
    font-size: var(--font-size-2xl);
    font-weight: var(--font-weight-bold);
    color: var(--color-heading);
}

.platform-status-summary-card__status-text.is-green {
    color: var(--color-success);
}

.platform-status-summary-card__status-text.is-progress {
    color: var(--color-warning);
}

.platform-status-summary-card__delta {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    color: var(--color-text-muted);
}

.platform-status-summary-card__progress {
    display: block;
    width: 100%;
    height: 0.375rem;
    border-radius: var(--radius-pill);
    background-color: var(--color-border);
    overflow: hidden;
}

.platform-status-summary-card__progress-fill {
    display: block;
    height: 100%;
    background-color: var(--color-text-muted);
    border-radius: var(--radius-pill);
}

/* ============================================================
 * 11. MODALS & DIALOGS
 * ============================================================ */
.platform-gal-control__new {
    align-self: center;
    padding: 0.15rem 0.4rem;
    margin: -0.15rem -0.4rem;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--color-primary);
    background-color: transparent;
    font: inherit;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    cursor: pointer;
    transition: background-color var(--transition-fast);
}

.platform-gal-control__new:hover {
    background-color: var(--color-primary-soft-bg);
    text-decoration: underline;
}

.platform-gal-modal__header,
.platform-gal-modal__progress-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
}

.platform-gal-modal__actions {
    display: flex;
    flex-direction: row-reverse;
    align-items: center;
    justify-content: flex-start;
    gap: var(--space-3);
}

.platform-gal-modal__eyebrow {
    display: block;
    margin-bottom: var(--space-1);
    color: var(--color-primary);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/* .platform-gal-modal__close retired — the modal close button now renders via PlatformButton
   (Variant="Ghost" IconOnly="true"). */

.platform-gal-modal__setup[hidden] {
    display: none;
}

.platform-gal-modal__section {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    padding: var(--space-4);
    background: var(--color-surface-sunken);
    border: var(--border-width) solid var(--color-border);
    border-radius: var(--radius-xl);
}

.platform-gal-modal__copy {
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-normal);
}

.platform-gal-modal__option {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3);
    background: var(--color-surface);
    border: var(--border-width) solid var(--color-border);
    border-radius: var(--radius-md);
    cursor: pointer;
}

.platform-gal-modal__option:has(input:checked) {
    border-color: var(--color-primary);
    box-shadow: var(--focus-ring);
}

.platform-gal-modal__option span {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.platform-gal-modal__option strong {
    color: var(--color-heading);
    font-size: var(--font-size-sm);
}

.platform-gal-modal__option small {
    color: var(--color-text-muted);
    font-size: var(--font-size-xs);
    line-height: var(--line-height-normal);
}

/* .platform-gal-modal__button family retired — Cancel/Continue/Accept/"Cancel setup" now render
   via PlatformButton (Variant="Base"/"Primary"). This also fixes the missing-hover gap the
   sticker sheet flagged: Kendo's own .k-button ships hover for every FillMode automatically. */

.platform-gal-modal__progress-head {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.platform-gal-modal__progress-head strong {
    color: var(--color-heading);
}

.platform-gal-modal__progress-track {
    height: 0.375rem;
    overflow: hidden;
    background: var(--color-surface-sunken);
    border-radius: var(--radius-pill);
}

.platform-gal-modal__progress-track span {
    display: block;
    height: 100%;
    background: var(--color-primary);
    border-radius: inherit;
    transition: width var(--transition-base);
}

.platform-gal-modal__steps {
    margin: 0;
    padding: 0;
    list-style: none;
}

.platform-gal-modal__steps li {
    display: grid;
    grid-template-columns: 2rem 1fr;
    align-items: flex-start;
    gap: var(--space-3);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-normal);
}

.platform-gal-modal__steps li[data-state="active"] {
    color: var(--color-heading);
    font-weight: var(--font-weight-medium);
}

.platform-gal-modal__steps li[data-state="complete"] {
    color: var(--color-success);
}

.platform-gal-modal__step-marker {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border: var(--border-width) solid var(--color-border-strong);
    border-radius: var(--radius-full);
    font-weight: var(--font-weight-semibold);
}

.platform-gal-modal__steps li[data-state="active"] .platform-gal-modal__step-marker {
    color: var(--color-on-primary);
    background: var(--color-primary);
    border-color: var(--color-primary);
}

.platform-gal-modal__steps li[data-state="complete"] .platform-gal-modal__step-marker {
    color: var(--color-success);
    background: var(--color-success-bg);
    border-color: var(--color-success);
}

.platform-gal-modal__accept {
    width: 100%;
    margin-top: var(--space-3);
}

.platform-gal-modal__warning {
    padding: var(--space-3);
    color: var(--color-warning);
    background: var(--color-warning-bg);
    border-radius: var(--radius-md);
    font-size: var(--font-size-xs);
    line-height: var(--line-height-normal);
}

.platform-preview-subblock__actions {
    display: flex;
    flex-direction: row-reverse;
    justify-content: flex-start;
    gap: var(--space-3);
}

/* PlatformDialog's Size parameter (ModalSize.Medium/Small) sets the dialog width. */
.platform-dialog--medium {
    width: min(38.75rem, calc(100vw - var(--space-6) * 2));
    max-width: 38.75rem;
}

.platform-dialog--small {
    width: min(25rem, calc(100vw - var(--space-6) * 2));
    max-width: 25rem;
}

/* PlatformDialog's Class parameter lands on the outer .k-window, not on the content pane, so
   the zero-padding has to be scoped down to .k-dialog-content explicitly — a bare `padding: 0`
   here would sit on .k-window (which has no padding of its own to zero) and leave the chrome's
   real content padding untouched, doubling up with this dialog's own margin-based insets.
   overflow is deliberately left alone: .k-window needs it at its default (hidden) to clip its
   content to the border-radius — setting it to visible here used to flatten the dialog's top
   corners. */
/* Compound selector (both ancestor classes on the same node, not two separate ancestor selectors)
   so this outranks .platform-dialog .k-window-content.k-dialog-content's own padding rule by
   specificity alone — both match the same element (ResolvedClass puts every class on the one
   .k-window node) with the same 3-class count, so without this they'd instead resolve by source
   order and silently lose regardless of which one is written later. */
.platform-dialog.platform-dialog--configuration .k-window-content.k-dialog-content {
    padding: 0;
}

/* ============================================================
 * TelerikDialog chrome.
 * PlatformDialog sets Class="platform-dialog" on every TelerikDialog it
 * renders; every rule below is scoped to that fingerprint so an unrelated,
 * un-opted-in TelerikDialog elsewhere in the app is unaffected. Width is
 * NOT set here — that's PlatformDialog's own Size parameter.
 * ============================================================ */
.k-overlay {
    background-color: color-mix(in srgb, var(--color-scrim) 50%, transparent);
}

.platform-dialog.k-window.k-dialog {
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-modal);
    border-color: var(--color-border);
    /* Telerik sizes the window to its content with no cap, so tall content pushes the dialog
       off-screen instead of scrolling. The titlebar/actions already have flex-shrink:0 and
       .k-dialog-content already has flex:1 1 auto + overflow:auto, so capping height here is
       enough to make only the content area scroll — header and footer stay put. */
    max-height: 90vh;
}

.platform-dialog .k-window-titlebar.k-dialog-titlebar {
    padding: var(--space-8) var(--space-8) var(--space-4);
    gap: var(--space-6);
    background-color: var(--color-surface);
    border-bottom: none;
}

/* PlatformDialog's <DialogTitle> supplies both the title text and the close PlatformButton
   together (Telerik's own close button is disabled via ShowCloseButton="false"), so this becomes
   the title+close flex row rather than just text. */
.platform-dialog .k-window-title.k-dialog-title {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-6);
    flex: 1 1 auto;
}

.platform-dialog__title-text {
    font-family: var(--font-heading);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    color: var(--color-heading);
    min-width: 0;
    white-space: normal;
    overflow-wrap: anywhere;
}

.platform-dialog .k-window-content.k-dialog-content {
    padding: var(--space-4) var(--space-8);
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.platform-dialog .k-actions.k-dialog-actions {
    padding: var(--space-4) var(--space-8) var(--space-8);
    border-top: none;
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

/* Optional FooterStart content (e.g. a step indicator) packs at the natural flex-start (left);
   the button cluster always pushes to the far right via margin-left:auto regardless of whether
   FooterStart is present — when it's the only child this simply right-aligns it, same as before. */
.platform-dialog__footer-actions {
    display: flex;
    flex-direction: row-reverse;
    align-items: center;
    gap: var(--space-3);
    margin-left: auto;
}

/* Telerik's k-actions-stretched modifier grows every action button to share the row equally
   (flex-grow:1, flex-basis:0) — buttons end up full-width instead of sized to their own label. */
.platform-dialog .k-actions.k-dialog-actions .k-button {
    flex: 0 0 auto;
}

.platform-config-dialog__error {
    color: var(--color-danger);
    background-color: var(--color-danger-bg);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    font-size: var(--font-size-sm);
}

/* Available/Selected picker shared by the recipients modal and the "Filter data by group" modal.
   Fills whatever height the dialog's content pane has (instead of a fixed height) so the dialog
   itself never needs its own scrollbar — only the innermost list (.platform-two-column-picker__scroll)
   does. min-height:0 at every level lets the flex chain actually shrink under the dialog's 90vh cap. */
.platform-two-column-picker {
    display: flex;
    align-items: stretch;
    gap: var(--space-6);
    flex: 1 1 auto;
    min-height: 0;
}

.platform-two-column-picker__column {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    min-width: 0;
    min-height: 0;
}

.platform-two-column-picker__column h3 {
    margin: 0;
    color: var(--color-heading);
    font-family: var(--font-heading);
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
}

/* Search is the first 42px row inside the Available box (925:34211): icon at 12px, text at 53px,
   clear glyph 12px from the right edge, hairline under it — no focus ring, the box is the frame. */
.platform-two-column-picker__search {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
    gap: 1.5625rem;
    height: 2.625rem;
    padding: 0 var(--space-3);
    color: var(--color-text-muted);
    border-bottom: var(--border-width) solid var(--color-hairline);
}

.platform-two-column-picker__search input {
    flex: 1;
    min-width: 0;
    padding: 0;
    color: var(--color-heading);
    font-family: inherit;
    font-size: var(--font-size-base);
    background: none;
    border: none;
    outline: none;
}

.platform-two-column-picker__search input:focus,
.platform-two-column-picker__search input:focus-visible {
    outline: none;
    border-color: transparent;
    box-shadow: none;
}

.platform-two-column-picker__search input::placeholder {
    color: var(--color-text-placeholder);
}

/* 349px list boxes (925:34210 / 925:34493); rows are 56px with a 4px inset and no gap. */
.platform-two-column-picker__list {
    display: flex;
    flex-direction: column;
    /* flex-basis 21.8125rem (the old fixed height) is only a PREFERRED size — flex-shrink:1 lets it
       give way to whatever room the dialog actually has (down to min-height) instead of forcing the
       dialog's own content pane to scroll around a box that doesn't fit. min-height is a small floor
       just so a short/empty list doesn't collapse to nothing; it must stay well under the preferred
       height or it re-creates the same "doesn't fit, dialog scrolls around it" problem at that size. */
    flex: 1 1 21.8125rem;
    min-height: 8rem;
    overflow: hidden;
    border: var(--border-width) solid var(--color-hairline);
    border-radius: var(--radius-md);
}

.platform-two-column-picker__scroll {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    padding: var(--space-1);
    overflow-y: auto;
}

.platform-two-column-picker__options {
    display: flex;
    flex-direction: column;
}

.platform-btn--apply-filter {
    min-width: 7.5rem;
    justify-content: center;
}

.platform-two-column-picker__item {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    width: 100%;
    padding: var(--space-2) var(--space-3) var(--space-2) var(--space-1);
    text-align: left;
    color: inherit;
    font: inherit;
    background: none;
    border: none;
    border-radius: var(--radius-md);
    cursor: pointer;
}

.platform-two-column-picker__item-icon {
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    color: var(--color-icon-muted);
}

.platform-two-column-picker__item-text {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.platform-two-column-picker__item-text strong,
.platform-two-column-picker__item-text small {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.platform-two-column-picker__item-text strong {
    color: var(--color-heading);
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
}

.platform-two-column-picker__item-text small {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

/* The +/x affordance shows on hover only, in both columns. */
.platform-two-column-picker__item .platform-two-column-picker__item-action {
    flex: 0 0 auto;
    color: var(--color-primary);
    visibility: hidden;
}

.platform-two-column-picker__item:hover,
.platform-two-column-picker__item:focus-visible {
    background-color: var(--color-primary-soft-bg);
}

.platform-two-column-picker__item:hover .platform-two-column-picker__item-action,
.platform-two-column-picker__item:focus-visible .platform-two-column-picker__item-action {
    visibility: visible;
}

.platform-two-column-picker__empty {
    margin: 0;
    padding: var(--space-6) var(--space-4);
    color: var(--color-text-placeholder);
    font-size: var(--font-size-sm);
    text-align: center;
}

/* Entra ID/GAL dialog: scope summary + record-type cards. Geometry taken from the Figma frames
   (936:38352 / 936:38405): 12px insets, 4px chip gap, 100px-tall type cards with a 42px icon ring. */
.platform-gal-scope,
.platform-gal-types {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

/* Section labels share the display face with the dialog title, not the body face. */
.platform-gal-scope__label,
.platform-gal-types__label {
    color: var(--color-heading);
    font-family: var(--font-heading);
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
}

.platform-gal-scope__box {
    display: flex;
    flex-direction: column;
    border: var(--border-width) solid var(--color-hairline);
    border-radius: var(--radius-md);
    overflow: hidden;
}

.platform-gal-scope__row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 12px;
}

/* .platform-cta sets align-self: flex-start so buttons do not stretch in column layouts; in a
   horizontal row that beats align-items and tops-aligns any button that is not the tallest. */
.platform-gal-scope__row .platform-cta,
.platform-gal-scope__chips .platform-cta,
.platform-two-column-picker__search .platform-cta,
.platform-flow__card .platform-cta,
.platform-config-dialog__folder-node .platform-cta,
.platform-source-card--panel .platform-cta {
    align-self: center;
}

/* Telerik wraps the whole button body in .k-button-text, so an icon inside it is laid out as an
   inline box on the text baseline: the line box grows past the icon and nothing centres — most
   visible as a sub-pixel vertical offset on icon-only circles at the XSmall/Small tiers. Was
   previously scoped to .platform-config-dialog only; broadened app-wide since every PlatformButton
   has the same wrapper and the same bug. */
.platform-cta .k-button-text {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

/* Branded hover tooltip for every icon-only PlatformButton (see PlatformButton.razor's Title
   passthrough and the single global <TelerikTooltip> in MainLayout.razor that renders it).
   Overridden directly on .k-tooltip rather than by redefining --kendo-color-inverse, since that
   variable's reach into other Kendo components hasn't been audited. */
.k-tooltip {
    max-width: 200px;
    padding: var(--space-2);
    border-radius: var(--radius-md);
    background-color: var(--color-surface-dark);
    color: var(--color-on-dark);
    box-shadow: var(--shadow-md);
    font-size: var(--font-size-sm);
    word-break: break-word;
}

.platform-gal-scope__summary {
    flex: 1;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
    color: var(--color-heading);
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
    text-align: left;
}

.platform-gal-scope__summary--toggle {
    padding: 0;
    /* font-family only: the `font` shorthand would also reset the size and weight that
       .platform-gal-scope__summary sets just above. */
    font-family: inherit;
    background: none;
    border: none;
    cursor: pointer;
}

/* Same open/closed pair as the dashboard flow rows: pointing down when open, right when closed. */
.platform-gal-scope__chevron {
    display: inline-flex;
    flex: 0 0 auto;
    color: var(--color-icon-muted);
    transform: rotate(-90deg);
    transition: transform 0.15s ease;
}

.platform-gal-scope__chevron[data-open="true"] {
    transform: rotate(0deg);
}

.platform-gal-scope__chips {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    max-height: 13.125rem;
    overflow-y: auto;
    list-style: none;
    padding: 12px;
    margin: 0;
    background-color: var(--color-bg);
}

/* Height is hug (auto): the 28px remove button plus 4px padding and the 1px border make the 38px
   row, so the chip follows the button's size instead of restating it. flex-shrink:0 keeps the list
   scrolling past its max-height rather than crushing the rows, which a column flex container does
   by default. */
.platform-gal-scope__chips li {
    display: flex;
    flex-shrink: 0;
    align-self: stretch;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-1) var(--space-1) var(--space-2);
    color: var(--color-text-chip);
    font-size: var(--font-size-sm);
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--color-hairline);
    border-radius: var(--radius-md);
    overflow: hidden;
}

/* The label truncates on one line and the remove button keeps its own size, so a long group name
   cannot wrap the row or push the button past the chip's right edge (936:38429). */
.platform-gal-scope__chips li > span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.platform-gal-scope__chips li .platform-cta {
    flex: 0 0 auto;
}

.platform-gal-types__cards {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-4);
}

.platform-gal-types__card {
    position: relative;
    font: inherit;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100px;
    padding: var(--space-4);
    color: var(--color-heading);
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--color-hairline);
    border-radius: var(--radius-md);
    cursor: pointer;
}

.platform-gal-types__card.is-selected {
    background-color: var(--color-selected-bg);
    border-color: var(--color-primary);
}

.platform-gal-types__body {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-1);
    min-width: 0;
}

.platform-gal-types__ring {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    border: var(--border-width) solid var(--color-hairline);
    border-radius: var(--radius-pill);
}

.platform-gal-types__label-text {
    display: flex;
    align-items: center;
    height: 20px;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
}

/* Both states ship as their own glyph, pinned where the mockups place them (12px from top/right). */
.platform-gal-types__state {
    position: absolute;
    top: 12px;
    right: 12px;
    color: var(--color-icon-faint);
}

/* The two states color through different SVG properties but the same channel: CheckCircle is solid
   (fill: currentColor) and Circle is outline (stroke: currentColor on its [fill=none] paths), and
   Kendo points both at --kendo-icon-color, which is currentColor — so `color` drives both. */
.platform-gal-types__card.is-selected .platform-gal-types__state {
    color: var(--color-primary);
}

.platform-gal-types__hint {
    color: var(--color-text-annotation);
    font-size: var(--font-size-xs);
}

/* The two inline scope actions are 12px semibold in the mockups: "Clear all" in the annotation
   grey, "Edit groups" in the primary blue. */
.platform-gal-scope__action.k-button,
.platform-gal-scope__action-primary.k-button {
    font-weight: var(--font-weight-semibold);
}

/* "Clear all" is plain text in the mockups - no stroke, no fill - so the flat variant's 1px
   transparent border is invisible but still 2px of width, which pushed the row's whole right group
   out of position. Focus is drawn with a box-shadow, so dropping the border costs nothing. */
.platform-gal-scope__action.k-button {
    color: var(--color-text-annotation);
    border: 0;
}

.platform-gal-scope__action-primary.k-button {
    color: var(--color-primary);
}

.platform-preview-subblock {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-4);
    background-color: var(--color-surface-sunken);
    border: var(--border-width) solid var(--color-border);
    border-radius: var(--radius-md);
}

/* ============================================================
 * 12. STEP 4 — CONFIGURE + TEST (per-spoke controls + grouped run)
 * ============================================================ */

/* Trailing per-spoke controls slot inside a flow card (scope chip + pencil / target button). */
.platform-flow__card-controls {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    flex-shrink: 0;
    margin-left: auto;
}

.platform-flow__scope-control {
    display: inline-flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-1);
    height: 36px;
    padding: var(--space-1) var(--space-1) var(--space-1) var(--space-3);
    color: var(--color-text);
    background-color: var(--color-flow-pill-bg);
    border-radius: var(--radius-md);
    white-space: nowrap;
}

.platform-flow__scope-control[data-scope="conflict"] {
    color: var(--color-danger);
    background-color: var(--color-danger-bg);
    border-color: var(--color-danger);
}

.platform-flow__scope-control[data-scope="required"] {
    color: var(--color-warning);
    background-color: var(--color-warning-bg);
    border: var(--border-width) solid var(--color-warning);
}

.platform-flow__scope-value {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
}

.platform-flow__scope-value svg {
    flex-shrink: 0;
}

.platform-flow__scope-edit.k-button {
    border-radius: 50%;
}

.platform-flow__configure-later.platform-cta {
    height: 34px;
    padding: 0 var(--space-2);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-normal);
    color: var(--color-text-muted);
    background-color: transparent;
    border: var(--border-width) solid var(--color-card-border);
    white-space: nowrap;
}

/* Right pane — mirrored per-data-type test-sync groups. */
.platform-run-panel__intro {
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
    margin: 0;
}

.platform-run-group {
    padding-top: var(--space-3);
    padding-bottom: var(--space-4);
}

.platform-run-group__stages {
    list-style: none;
    padding: 0;
    margin: 0;
}

.platform-run-stage__label {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--color-heading);
}

/* 4px borderless track with a 2px inset fill (Figma run frames, progress-bar 558:9300/9301). */
.platform-run-stage__bar {
    position: relative;
    height: 0.25rem;
    width: 100%;
    background-color: var(--color-surface-sunken);
    border-radius: var(--radius-pill);
    overflow: hidden;
}

.platform-run-stage__bar-fill {
    position: absolute;
    inset: 1px auto 1px 1px;
    background-color: var(--color-primary, #2563eb);
    border-radius: var(--radius-sm);
    transition: width var(--transition-fast);
}

.platform-run-stage__bar[data-running="True"] .platform-run-stage__bar-fill {
    width: 35% !important;
    animation: platform-run-progress 1.4s ease-in-out infinite;
}

@keyframes platform-run-progress {
    from { transform: translateX(-110%); }
    to { transform: translateX(310%); }
}

.platform-run-stage[data-state="Done"] .platform-run-stage__bar-fill {
    background-color: var(--color-success, #16a34a);
}

/* Failed fill keeps the real progress width (Figma error frame 805:13196 readouts) and darkens
   to the mockup's #a4070a — deliberately off-token: dE2000 to --color-danger is 5.2. */
.platform-run-stage[data-state="Failed"] .platform-run-stage__bar-fill {
    background-color: #a4070a;
    min-width: var(--space-2);
}

.platform-run-stage__count {
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
}

.platform-run-stage__subitems {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    list-style: none;
    padding: 0;
    margin: var(--space-1) 0 0 var(--space-3);
}

.platform-run-stage__subitem {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
}

.platform-run-stage__subitem-label {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}

/* ------------------------------------------------------------
 * RUN-STAGE ROW ICONS (R2) — real semantic glyphs on each sync-status row and its device
 * sub-items, replacing the removed hollow-circle placeholder marker. Optional: the span is omitted entirely when no
 * IconRole is supplied, so it never reserves a gap or shows a broken image.
 * ------------------------------------------------------------ */
.platform-run-stage__icon:not(.ic),
.platform-run-stage__subitem-icon:not(.ic) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
    overflow: hidden;
}

.platform-run-stage__subitem-note {
    font-style: italic;
    opacity: 0.85;
}

/* ============================================================
 * DASHBOARD MOCKUP-3 ADDITIONS (be9e4f)
 * Two centralized blocks — Activity metric row (R1) and Contact
 * sync flow row (R2) — plus the shared "anchor" heading/CTA icon
 * role and the paused-flow status badge. All rules consume only
 * semantic tokens (R6/AC9): no scoped
 * *.razor.css, no inline styles, no new palette.
 * ============================================================ */

.platform-activity__grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-4);
}

.platform-activity-metric {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: 1.375rem;
}

.platform-activity-metric__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
}

.platform-activity-metric__label {
    /* Mockup #5a7086 sits between --p-neutral-400 and -500 (dE2000 3.4 to both) — exact value. */
    color: #5a7086;
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
}

.platform-activity-metric__dot {
    width: 0.75rem;
    height: 0.75rem;
    flex-shrink: 0;
    border-radius: var(--radius-pill);
    background-color: var(--color-status-healthy);
}

.platform-activity-metric__dot--neutral {
    background-color: var(--p-neutral-400);
}

.platform-activity-metric__dot--failed {
    background-color: var(--color-danger);
}

/* Dashboard schedule notice under the Activity metrics (Figma "Active syncs run every day..."). */
.platform-dashboard__schedule-note {
    margin: 0;
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
}

.platform-dashboard__schedule-note .platform-link {
    font-weight: var(--font-weight-semibold);
}

/* Stopped-state footer: Retry + support buttons sit side by side. */
.platform-flows__cta-group {
    display: flex;
    gap: var(--space-3);
}

/* Complete-setup modal (Figma "Complete Setup for 20 User Mailboxes"). */
.platform-setup-dialog {
    width: 100%;
}

.platform-setup-dialog__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
}

.platform-setup-dialog__body {
    margin: 0;
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
    line-height: 1.5;
}

.platform-setup-dialog__granted {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border: var(--border-width) solid var(--color-card-border);
    border-radius: var(--radius-md);
}

.platform-setup-dialog__granted-label {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    color: var(--color-success);
}

.platform-setup-dialog__granted-account {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}

.platform-setup-dialog__foot {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.platform-setup-dialog__foot .platform-compact-header {
    margin-right: auto;
}

.platform-setup-dialog__actions {
    display: flex;
    flex-direction: row-reverse;
    align-items: center;
    justify-content: flex-start;
    gap: var(--space-4);
}

.platform-activity-metric__value {
    color: var(--color-heading);
    font-family: var(--font-heading);
    font-size: var(--font-size-2xl);
    font-weight: var(--font-weight-semibold);
    line-height: 1.75rem;
}

.platform-activity-metric__caption {
    margin: 0;
    color: var(--color-text-subtle);
    font-size: var(--font-size-xs);
    line-height: 1.05rem;
}

.platform-activity-metric__caption-link {
    color: var(--color-primary);
    font-weight: var(--font-weight-semibold);
    text-decoration: none;
}

/* Dashboard flow card (806:13863): white 16px-radius shell without a border; 67px header; a
   #f9fcff body inset 20px whose rows are 52px with 28px icons, hairline rails and the section
   button inside the body. #f9fcff / #8fa3b5 are deliberately off-token (nearest tokens differ). */
.platform-flows__group {
    display: flex;
    flex-direction: column;
    border-radius: var(--radius-xl);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-lg);
}

.platform-flow-row {
    border: 0;
    border-radius: 0;
    background-color: var(--color-surface);
}

.platform-flow-row:not(:last-child) {
    border-bottom: 1px solid var(--p-neutral-50);
}

.platform-flow-row:first-child {
    border-top-left-radius: var(--radius-xl);
    border-top-right-radius: var(--radius-xl);
}

.platform-flow-row:last-child {
    border-bottom-left-radius: var(--radius-xl);
    border-bottom-right-radius: var(--radius-xl);
}

.platform-flow-row__body {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-5);
    border-top: 0;
    background-color: #f9fcff;
}

.platform-flow-row:last-child .platform-flow-row__body {
    border-radius: 0 0 var(--radius-xl) var(--radius-xl);
}

.platform-flow-row__body .platform-flow__group {
    gap: var(--space-3);
    /* Figma 806:13878: the bus runs on the group's right edge, its elbow is a 12px stub that touches
       the Hub / Tunnel box, and source/target cards stop 90px short of the bus. */
    padding-right: 14px;
}

.platform-flow-row__body .platform-flow__card {
    min-height: 52px;
    padding: 0 var(--space-8) 0 var(--space-3);
    gap: var(--space-4);
}

.platform-flow-row__body .platform-flow__card-icon:not(.ic),
.platform-flow-row__body .platform-flow__box-icon:not(.ic) {
    width: 28px;
    height: 28px;
}

.platform-flow-row__body .platform-flow__box {
    min-height: 52px;
    padding: 0 var(--space-3);
    gap: var(--space-2);
    background-color: transparent;
}

.platform-flow-row__body .platform-flow__box-text {
    column-gap: 2px;
}

.platform-flow-row__body .platform-flow__box-title {
    font-size: var(--font-size-base);
}

.platform-flow-row__body .platform-flow__box-desc,
.platform-flow-row__subtitle,
.platform-flow-row__stat {
    color: #8fa3b5;
}

.platform-flow-row__body .platform-flow__rail,
.platform-flow-row__body .platform-flow__rail--out,
.platform-flow-row__body .platform-flow__card-arrow::after {
    border-color: var(--color-hairline);
}

/* Figma 806:13878: the bus runs on the group's right edge, its elbow is a 12px stub that touches
   the Hub / Tunnel box, and source/target cards stop 90px short of the bus. */
.platform-flow-row__body .platform-flow__rail {
    right: 0;
    width: 14px;
}

.platform-flow-row__body .platform-flow__rail--in,
.platform-flow-row__body .platform-flow__rail--out {
    top: 25px;
    bottom: 25px;
}

.platform-flow-row__body .platform-flow__card--rail-entry {
    margin-right: 76px;
}

.platform-flow-row__body .platform-flow__card-arrow::after {
    width: 73px;
}

.platform-flow-row__body .platform-flows__cta--bottom {
    align-self: flex-start;
    margin: 0;
}

/* Mockup 806:13820 -> 1693:9410 "flow actions": the flow's own action (Pause/Activate) sits on
   the left, Add-connector/Add-distribution-tunnel on the right, both anchored to the bottom of
   the box, same 41px/12-16-padding size as the flow's own action button. */
.platform-flow-row__footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-3);
}

.platform-flow-row__footer .platform-flows__cta--bottom {
    align-self: center;
    margin: 0;
}

.platform-flow-row__header {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto auto;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    padding: var(--space-3) var(--space-4);
    background-color: transparent;
    border: 0;
    border-radius: 0;
    text-align: start;
    cursor: pointer;
    color: var(--color-text);
    font-family: inherit;
    font-size: var(--font-size-base);
    transition: background-color var(--transition-fast);
}

.platform-flow-row:first-child .platform-flow-row__header {
    border-top-left-radius: var(--radius-xl);
    border-top-right-radius: var(--radius-xl);
}

.platform-flow-row:last-child .platform-flow-row__header:last-child {
    border-bottom-left-radius: var(--radius-xl);
    border-bottom-right-radius: var(--radius-xl);
}

.platform-flow-row__header:hover {
    background-color: var(--color-surface-hover);
}

.platform-flows__empty {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-4);
    background-color: var(--color-surface);
    border-radius: var(--radius-xl);
}

.platform-flows__empty-text {
    margin: 0;
    color: var(--color-text-muted);
}

.platform-flow-row__actions {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    margin-left: auto;
}

.platform-flow-row__titles {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    min-width: 0;
}

.platform-flow-row__name {
    font-weight: var(--font-weight-semibold);
    color: var(--color-heading);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.platform-flow-row__subtitle {
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.platform-flow-row__pair {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-text-muted);
}

.platform-flow-row__ep {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.25rem;
    height: 1.25rem;
    flex-shrink: 0;
    overflow: hidden;
}

.platform-flow-row__arrow {
    color: var(--color-text-subtle);
}

.platform-flow-row__stat {
    color: var(--color-text-subtle);
    font-size: var(--font-size-sm);
    white-space: nowrap;
}

/* Same open/closed pair as .platform-gal-scope__chevron: pointing down when open, right when closed. */
.platform-flow-row__chevron {
    display: inline-flex;
    flex: 0 0 auto;
    color: var(--color-icon-muted);
    transform: rotate(-90deg);
    transition: transform 0.15s ease;
}

.platform-flow-row__chevron[data-open="true"] {
    transform: rotate(0deg);
}

.platform-flow-row__note {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-normal);
    margin: 0;
}

.platform-flows__cta {
    align-self: flex-start;
}

/* Dashboard bottom CTA (806:13820 -> 1791:9557): outlined, dimmed to 60% until the tunnel
   targets exist (Kendo's default .k-disabled opacity, confirmed against Figma). */
.platform-flows__cta.k-disabled,
.platform-flows__cta:disabled {
    opacity: 0.6;
}

.platform-schedule-dialog__fields {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 16rem));
    gap: var(--space-4);
}

/* Paused-flow status badge (be9e4f EC5) — amber semantic tokens, reusing the shared
   .platform-status-badge shell + dot. */
.platform-status-badge--paused {
    color: var(--color-badge-paused-fg);
    background-color: var(--color-badge-paused-bg);
    border-color: var(--color-badge-paused-bg);
}

.platform-status-badge--paused .platform-status-badge__dot {
    background-color: var(--color-badge-paused-dot);
}

@media (max-width: 720px) {
    .platform-schedule-dialog__fields {
        grid-template-columns: 1fr;
    }
}

/* ============================================================
 * 12. TOP NAV / USER CHIP / BILLING PAGE
 * ============================================================ */

/* Horizontal top nav replacing the old left-side DashboardLeftMenu (see TopNavMenu.razor). Icon
   glyphs render via the shared <Icon> component (wwwroot/assets/icons/ui/*.svg), sized 24x24
   inline in the markup — this section only controls the pill layout/styling around them. The
   topbar becomes a 1fr auto 1fr grid so the nav stays
   optically centered in the bar regardless of how wide the brand logo or the trial-pill/user-chip
   block on either side are, while still reserving each side its own column (no overlap). */
@media (min-width: 901px) {
    .platform-topbar {
        display: grid;
        grid-template-columns: 1fr auto 1fr;
    }

    .platform-topbar__brand {
        grid-column: 1;
        justify-self: start;
    }

    .platform-topnav-menu {
        grid-column: 2;
        justify-self: center;
    }

    .platform-topbar__nav {
        grid-column: 3;
        justify-self: end;
    }
}

.platform-topnav-menu__list {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    list-style: none;
    padding: 0;
    margin: 0;
}

.platform-topnav-menu__link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    cursor: pointer;
    height: 44px; /* deliberate literal — no --size-* token at 44px yet */
    padding: var(--space-2) var(--space-3);
    background-color: var(--color-tertiary-soft-bg);
    border: var(--border-width) solid var(--color-tertiary-soft-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-tertiary);
    color: var(--color-text);
    font-weight: var(--font-weight-semibold);
    font-size: var(--font-size-xs);
    text-decoration: none;
    white-space: nowrap;
}

.platform-topnav-menu__link:hover,
.platform-topnav-menu__link.is-current {
    background-color: var(--color-tertiary-soft-bg-hover);
    text-decoration: none;
}

/* Instant press feedback + held state while a navigation round-trip is in flight (long polling
   latency otherwise leaves the tap with no immediate response). .is-navigating is toggled by
   platform-nav-progress.js and keeps the button visibly pressed until the new page renders. */
.platform-topnav-menu__link:active,
.platform-topnav-menu__link.is-navigating {
    background-color: var(--color-tertiary-soft-bg-hover);
    transform: translateY(1px);
}

.platform-topnav-menu__link:focus-visible {
    outline: none;
    box-shadow: var(--shadow-tertiary), var(--focus-ring);
}

/* User chip (name + email) on the right of the top bar, replacing the plain "Account" tab — links
   to /account, with "Log out" nested underneath it. */
.platform-topbar__user {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    cursor: pointer;
}

.platform-topbar__user-avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 32px;
    height: 32px;
    border: none;
    border-radius: 50%;
    background-color: var(--color-primary);
    color: var(--p-white);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    cursor: pointer;
}

.platform-topbar__user-info {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    line-height: var(--line-height-tight);
    transition: color 0.2s ease;
}

.platform-topbar__user:hover .platform-topbar__user-info,
.platform-topbar__user.is-active .platform-topbar__user-info {
    color: var(--color-primary);
}

.platform-topbar__user-name {
    font-weight: var(--font-weight-medium);
    color: var(--color-heading);
    font-size: var(--font-size-sm);
}

.platform-topbar__user-email {
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
}

/* Invisible full-viewport layer rendered only while the dropdown is open — clicking anywhere
   outside the menu closes it (no JS interop needed). Sits
   below the menu's z-index (1000) but above all other page content. */
.platform-topbar__user-menu-overlay {
    position: fixed;
    inset: 0;
    z-index: 999;
    background: transparent;
    cursor: default;
}

/* Dropdown menu — toggles on .is-active state, positioned absolutely below user chip */
.platform-topbar__user-menu {
    position: absolute;
    top: 100%;
    right: 0;
    margin-top: var(--space-2);
    min-width: 160px;
    background-color: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
    overflow: hidden;
    z-index: 1000;
    animation: slideDown 0.15s ease-out;
}

@keyframes slideDown {
    from {
        opacity: 0;
        transform: translateY(-8px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.platform-topbar__user-menu-item {
    display: block;
    width: 100%;
    padding: var(--space-2) var(--space-3);
    text-align: left;
    font-size: var(--font-size-sm);
    color: var(--color-text);
    text-decoration: none;
    background: none;
    border: none;
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.platform-topbar__user-menu-item:hover {
    background-color: var(--color-surface-hover);
    color: var(--color-heading);
}

.platform-topbar__user-menu-item:first-child {
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}

.platform-topbar__user-menu-item:last-child {
    border-radius: 0 0 var(--radius-sm) var(--radius-sm);
}

.platform-topbar__user-menu-item--logout {
    color: var(--color-danger);
}

.platform-topbar__user-menu-item--logout:hover {
    background-color: var(--color-danger-bg);
    color: var(--color-danger);
}

@media (max-width: 900px) {
    .platform-topnav-menu__list {
        flex-wrap: wrap;
    }

    .platform-topbar__user-info {
        display: none;
    }
}

/* Billing page — plan cards + custom-pricing block. Card grid follows the same responsive pattern
   as .platform-source-group__grid (3 columns desktop, 1 column mobile). */

.platform-billing-subtitle p {
    margin: 0;
}

.platform-billing-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-6);
    align-items: stretch;
}

@media (max-width: 900px) {
    .platform-billing-grid {
        grid-template-columns: minmax(0, 1fr);
    }
}

.platform-billing-card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
    padding: var(--space-6);
}

.platform-billing-page .platform-cta.k-button {
    padding: var(--space-3) var(--space-2);
    font-size: var(--font-size-md);
    line-height: var(--line-height-tight);
}

.platform-billing-card__eyebrow {
    position: absolute;
    top: 0;
    right: var(--space-6);
    transform: translateY(-50%);
    margin: 0;
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-pill);
    background: var(--p-neutral-900);
    color: var(--p-white);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-tight);
}

.platform-billing-card__price {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0;
}

.platform-billing-card__price-value {
    font-family: var(--font-heading);
    font-size: var(--font-size-2xl);
    font-weight: var(--font-weight-semibold);
    line-height: 22.5px;
    color: var(--color-heading);
}

.platform-billing-card__features {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    list-style: none;
    padding: 0;
    margin: 0;
    border-top: var(--border-width) solid var(--color-card-border);
    padding-top: var(--space-4);
}

.platform-billing-card__feature {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    font-size: var(--font-size-sm);
}

.platform-billing-card__feature--muted {
    color: var(--color-text-muted);
    font-size: var(--font-size-xs);
}

.platform-billing-card__marker {
    color: var(--color-success);
    font-weight: var(--font-weight-bold);
    flex-shrink: 0;
}

.platform-billing-card__feature--muted .platform-billing-card__marker {
    color: var(--color-text-subtle);
}

/* Custom-pricing block: intro (heading/description) + feature list + CTA, laid out in a row on
   desktop and stacked on mobile — mirrors the wireframe's 3-part bottom panel. */
.platform-billing-custom {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    align-items: start;
    gap: var(--space-6) 70px;
    padding: var(--space-6);
    background-color: var(--color-bg);
}

.platform-billing-custom__features {
    list-style: none;
    padding: 0;
    margin: 0;
}

.platform-billing-custom__feature {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    font-size: var(--font-size-sm);
}

.platform-billing-custom__cta.platform-cta--block {
    align-self: start;
}

@media (max-width: 720px) {
    .platform-billing-custom {
        grid-template-columns: minmax(0, 1fr);
    }
}

.platform-billing-footer {
    text-align: center;
}

/* Downgrade dialog — Platform-Mockups node 2007:7804 "Modal / Downgrade to Standard Plan".
   Geometry sampled from the frame: body gap 24, section gap 8, one grouped list per section
   (fill #FAFCFF, radius 8) whose rows share a 1px border, row padding 16 with a 16 gap to the
   status pill. */
.platform-dialog.platform-downgrade-dialog .k-window-content.k-dialog-content {
    gap: var(--space-6);
}

.platform-downgrade__headline {
    font-size: var(--font-size-md);
    font-weight: var(--font-weight-semibold);
    line-height: 1.2;
}

.platform-downgrade__section {
    gap: var(--space-2);
}

.platform-downgrade__section-title {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
    color: var(--color-text);
    line-height: 1.3;
}

/* One card per section: the border belongs to the list so the rounded corners keep all four
   sides, and each row after the first only draws the divider. Bordering the rows themselves
   loses the vertical hairlines to the container's overflow clip. */
.platform-downgrade__list {
    list-style: none;
    margin: 0;
    padding: 0;
    background-color: var(--color-surface-subtle);
    border: var(--border-width) solid var(--color-border);
    border-radius: var(--radius-md);
    overflow: hidden;
}

.platform-downgrade__flow,
.platform-downgrade__account,
.platform-downgrade__feature {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-4);
}

.platform-downgrade__flow + .platform-downgrade__flow,
.platform-downgrade__flow + .platform-downgrade__account,
.platform-downgrade__feature + .platform-downgrade__feature {
    border-top: var(--border-width) solid var(--color-border);
}

/* Feature-loss rows (mockup 06.03, node 2025:15850): a 48px row holding a 32px icon disc, 16px
   to the label, a 16px info glyph 4px after it, and the benefit as a 33px ghost link button
   (9px padding, 12px semibold brand text) that the mockup lets overhang the 8px row padding. */
.platform-downgrade__feature {
    box-sizing: border-box;
    min-height: 3rem;
    padding: 0 var(--space-4);
}

.platform-downgrade__feature-name {
    display: flex;
    align-items: center;
    min-width: 0;
}

.platform-downgrade__feature-icon {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    margin-right: var(--space-4);
    border-radius: 50%;
}

.platform-downgrade__feature-icon--workspace {
    color: var(--color-primary);
    background-color: var(--color-primary-soft-bg);
}

.platform-downgrade__feature-icon--premium {
    color: var(--color-tertiary);
    background-color: var(--color-tertiary-soft-bg);
}

.platform-downgrade__feature-icon--enterprise {
    color: var(--color-warning);
    background-color: var(--color-warning-bg);
}

.platform-downgrade__feature-label {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
    color: var(--color-text);
    line-height: 1.3;
}

.platform-downgrade__feature-hint {
    display: inline-flex;
    flex: none;
    margin-left: var(--space-1);
    color: var(--color-text-subtle);
    cursor: help;
}

.platform-downgrade__feature-benefit {
    flex: none;
    padding: 9px;
    border-radius: var(--radius-md);
    color: var(--color-primary);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    line-height: 1.25;
    text-align: right;
}

.platform-downgrade__flow-copy {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    min-width: 0;
}

.platform-downgrade__flow-name {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
    line-height: 1.2;
}

.platform-downgrade__reasons {
    list-style: disc;
    margin: 0;
    padding-left: var(--space-4);
    color: var(--color-text-muted);
    font-size: var(--font-size-base);
    line-height: 20px;
}

/* Every stopped or changed flow wears the same calm pill; the difference is in the words, not in
   an alarm colour (mockup node 2013:8089). */
.platform-status-badge--neutral {
    color: var(--color-badge-neutral-fg);
    background-color: var(--color-badge-neutral-bg);
    border-color: var(--color-badge-neutral-bg);
    white-space: nowrap;
}

.platform-downgrade__tip {
    padding: var(--space-4);
    border-radius: var(--radius-md);
    background-color: var(--color-primary-soft-bg);
    color: var(--color-primary);
    font-size: var(--font-size-base);
    line-height: 20px;
}

.platform-downgrade-dialog .platform-dialog__footer-actions {
    gap: var(--space-2);
}

/* The mockup's confirm button is a soft danger tone, not the blue primary: it is the destructive
   choice of the two. The fill needs !important because Kendo's own .k-button-flat rule clears the
   background with !important. */
.platform-dialog.platform-downgrade-dialog .platform-downgrade__confirm.k-button {
    background-color: var(--color-danger-bg) !important;
    border-color: var(--color-danger-bg);
    color: var(--color-danger);
}
