/* Manual compatibility layer only. */
/* Authoritative theme tokens are generated into _content/AppPlatform.DesignTokens/css/tokens.generated.css. */

:root {
  /* Explicit allowlisted manual token (large data URI not yet moved into JSON source). */
  --ap-mask-warning-icon: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDg2IDY2LjAxODMgMjYzLjU4NiA2Ni4wMTgzIE0yNjMuNTc2IDg2LjA1NDdDMjYxLjA0OSA4Ni4wNTQ3IDI1OS43ODYgODcuMzAwNSAyNTkuNzg2IDg5Ljc5MjEgMjU5Ljc4NiA5Mi4yODM3IDI2MS4wNDkgOTMuNTI5NSAyNjMuNTc2IDkzLjUyOTUgMjY2LjExNiA5My41Mjk1IDI2Ny4zODcgOTIuMjgzNyAyNjcuMzg3IDg5Ljc5MjEgMjY3LjM4NyA4Ny4zMDA1IDI2Ni4xMTYgODYuMDU0NyAyNjMuNTc2IDg2LjA1NDdaIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiLz48L2c+PC9zdmc+");
}

/* === RadzenButton — spacing === */
/* Moved from app.css: .rz-* overrides belong in this file per CSS architecture */
.rz-button { margin-block: var(--ap-space-1); }
.ap-form-actions .rz-button { margin-block: 0; }

/* === RadzenButton — transitions, focus ring, disabled state (AC-03, AC-04) === */
.rz-button,
.rz-button:hover,
.rz-button:active {
    transition:
        background-color var(--ap-motion-duration-base) var(--ap-motion-ease-standard),
        opacity var(--ap-motion-duration-fast) var(--ap-motion-ease-standard);
    background-image: none !important;
}

.rz-button:focus-visible {
    outline: var(--ap-focus-ring-width) solid var(--ap-focus-ring-color);
    outline-offset: var(--ap-focus-ring-offset);
    background-image: none !important;
}

.rz-button:focus,
.rz-button.rz-state-focused {
    background-image: none !important;
}

.rz-button .rz-button-box,
.rz-button .rz-button-text,
.rz-button .rz-button-icon-left,
.rz-button .rz-button-icon-only,
.rz-button .rzi,
.rz-button i {
    background-image: none !important;
}

.rz-button.rz-variant-filled.rz-primary,
.rz-button.rz-variant-filled.rz-primary.rz-shade-default,
.rz-button.rz-variant-filled.rz-primary:hover,
.rz-button.rz-variant-filled.rz-primary:active,
.rz-button.rz-variant-filled.rz-primary:focus,
.rz-button.rz-variant-filled.rz-primary.rz-state-focused {
    color: var(--ap-text-on-accent) !important;
}

.rz-button.rz-variant-filled.rz-primary .rz-button-box,
.rz-button.rz-variant-filled.rz-primary .rz-button-text,
.rz-button.rz-variant-filled.rz-primary .rz-button-icon-left,
.rz-button.rz-variant-filled.rz-primary .rz-button-icon-only,
.rz-button.rz-variant-filled.rz-primary .rzi,
.rz-button.rz-variant-filled.rz-primary i {
    color: var(--ap-text-on-accent) !important;
}

.rz-button.rz-variant-filled.rz-danger,
.rz-button.rz-variant-filled.rz-danger.rz-shade-default,
.rz-button.rz-variant-filled.rz-danger:hover,
.rz-button.rz-variant-filled.rz-danger:active,
.rz-button.rz-variant-filled.rz-danger:focus,
.rz-button.rz-variant-filled.rz-danger.rz-state-focused {
    background-color: var(--ap-action-danger-filled-bg) !important;
    color: var(--ap-text-on-accent) !important;
    border-color: var(--ap-action-danger-filled-border) !important;
}

.rz-button.rz-variant-filled.rz-danger .rz-button-box,
.rz-button.rz-variant-filled.rz-danger .rz-button-text,
.rz-button.rz-variant-filled.rz-danger .rz-button-icon-left,
.rz-button.rz-variant-filled.rz-danger .rz-button-icon-only,
.rz-button.rz-variant-filled.rz-danger .rzi,
.rz-button.rz-variant-filled.rz-danger i {
    color: var(--ap-text-on-accent) !important;
}

/* Radzen uses --rz-danger for both fills and text. The bridge assigns its fill
   role to the existing solid-danger pair; these transparent/text roles retain the
   semantic error foreground. Do not redefine --rz-danger on these elements: that
   inherited variable would recolor a nested filled badge back to the text color. */
.rz-variant-outlined:is(.rz-button.rz-danger, .rz-badge-danger, .rz-chip-danger).rz-shade-default,
.rz-variant-text:is(.rz-button.rz-danger, .rz-badge-danger, .rz-chip-danger).rz-shade-default,
.rz-timeline-point-outlined:is(.rz-timeline-point-danger, .rz-timeline-point-error),
.rz-timeline-point-text:is(.rz-timeline-point-danger, .rz-timeline-point-error),
.rz-color-danger,
.rzi-danger,
.rzi-error,
.invalid ~ .rz-form-field-label,
.rz-spreadsheet-validation-error-message,
.rz-spreadsheet-cell-error {
    color: var(--ap-semantic-error) !important;
}

.rz-variant-outlined:is(.rz-button.rz-danger, .rz-badge-danger, .rz-chip-danger).rz-shade-default {
    border-color: var(--ap-semantic-error);
    box-shadow: inset 0 0 0 var(--rz-border-width) var(--ap-semantic-error);
}

/* Hover/active must keep the same pair, rather than a fixed Radzen red on a
   translucent primitive. The page surface is an existing theme-owned role. */
:is(.rz-variant-outlined, .rz-variant-text):is(.rz-button.rz-danger, .rz-chip-danger).rz-shade-default:not(.rz-state-disabled):is(:hover, :active) {
    background-color: var(--ap-bg-page);
}

.rz-timeline-point-outlined:is(.rz-timeline-point-danger, .rz-timeline-point-error),
.rz-timeline-point-text:is(.rz-timeline-point-danger, .rz-timeline-point-error) {
    background-color: var(--ap-bg-elevated);
    border-color: var(--ap-semantic-error);
}

.rz-button.rz-state-disabled {
    opacity: var(--ap-state-disabled-opacity);
    cursor: not-allowed;
}

/* === RadzenFormField — container layout (AC-01) === */
.rz-form-field {
    width: 100%;
    margin-bottom: var(--ap-space-4);
}

/* === RadzenFormField — floating label (AC-01) === */
.rz-form-field-label,
.rz-form-field .rz-form-field-label,
.rz-form-field .rz-form-field-label.rz-text-truncate {
    font-size: var(--ap-text-sm);
    font-weight: 600;
    color: var(--ap-text-primary);
}

.rz-form-field.rz-variant-filled > .rz-form-field-content > :not(.rz-state-empty):not(.rz-form-field-start):not(.rz-form-field-end) ~ .rz-form-field-label,
.rz-form-field.rz-variant-flat > .rz-form-field-content > :not(.rz-state-empty):not(.rz-form-field-start):not(.rz-form-field-end) ~ .rz-form-field-label,
.rz-form-field.rz-state-focused .rz-form-field-label,
.rz-form-field:not(.rz-floating-label) .rz-form-field-label,
.rz-form-field .rz-form-field-content > :not(.rz-state-empty):not(.rz-form-field-start):not(.rz-form-field-end) ~ .rz-form-field-label {
    color: var(--ap-text-primary) !important;
}

/* === RadzenTextBox / RadzenPassword — input appearance (AC-01) === */
/* .rz-textbox is the <input> for both RadzenTextBox and RadzenPassword.
   It previously said the password input sat inside a .rz-password-icon-wrapper,
   "confirmed from Radzen 10.0.5 CSS patterns" — the rendered-DOM baseline shows
   11.1.4 renders no such element, so the claim and the rule that acted on it are
   both gone (spec section 7.2). */
/* D-4 (spec section 9 item 16, PO option (a) 2026-07-22): the four in-category
   raw-primitive bindings take .rz-alert's defensive fallback form
   var(<semantic>, <primitive>), so the OVERRIDABLE token wins when a theme sets one
   and the primitive remains the fallback. Binding the primitive alone let a
   selector match while carrying a value no theme could move — which AC-04 cannot
   catch, because it asserts matching and not overridability. AC-29 binds this. */
.rz-textbox {
    background-color: var(--ap-bg-page, var(--ap-surface));
    /* Control boundary — WCAG 1.4.11 requires >= 3:1, so this takes the STRONG
       tier. The recessive --ap-border-default is for decorative rules only. */
    border: 1px solid var(--ap-border-strong);
    border-radius: var(--ap-radius-md);
    padding: var(--ap-space-2) var(--ap-space-3);
    font-size: var(--ap-text-base);
    color: var(--ap-text-primary);
    width: 100%;
    box-sizing: border-box;
    transition: border-color var(--ap-motion-duration-base) var(--ap-motion-ease-standard);
}

/* Focus: override Radzen's inset box-shadow with design-system outline ring (AC-02) */
.rz-textbox:not(:disabled):not(.rz-state-disabled):focus {
    border-color: var(--ap-primary);
    outline: var(--ap-focus-ring-width) solid var(--ap-focus-ring-color);
    outline-offset: var(--ap-focus-ring-offset);
    box-shadow: none;
}

/* === RadzenDataGrid — table appearance (AC-05) === */
.rz-datatable {
    border: 1px solid var(--ap-border-default);
    border-radius: var(--ap-radius-md);
    overflow: hidden;
}

/* Radzen 10 renders the grid as `table.rz-grid-table` with an UNCLASSED <thead>;
   the legacy `.rz-datatable-thead` selector matches nothing, so these header rules
   silently never applied. Target the real DOM (`.rz-grid-table thead`). */
.rz-grid-table thead {
    background-color: var(--ap-bg-surface);
    border-bottom: 1px solid var(--ap-border-default);
}

.rz-datatable .rz-grid-table thead th {
    background-color: var(--ap-bg-surface);
    color: var(--ap-text-primary);
    border-color: var(--ap-border-default);
}

.rz-data-row {
    border-bottom: 1px solid var(--ap-surface-border);
    transition: background-color var(--ap-motion-duration-base) var(--ap-motion-ease-standard);
}

.rz-data-row:hover,
.rz-datatable .rz-grid-table tbody tr:hover > td {
    background-color: var(--ap-primary-subtle);
}

.rz-datatable .rz-grid-table tbody tr > td,
.rz-datatable .rz-data-grid-data > tr > td {
    background-color: var(--ap-bg-surface);
    color: var(--ap-text-primary);
    border-color: var(--ap-border-default);
}

.rz-datatable .rz-grid-table tbody tr:nth-child(even) > td,
.rz-datatable .rz-data-grid-data > tr:nth-child(even) > td {
    background-color: var(--ap-bg-page);
}

.rz-cell-data {
    padding: var(--ap-space-2) var(--ap-space-3);
    font-size: var(--ap-text-sm);
    color: var(--ap-text-primary);
}

/* Finding P-5 (re-fixed): data-grid column headers were near-illegible — the
   `.rz-column-title` span carries a hard-coded light-theme colour (#212121) while
   body cells correctly resolve --ap-text-primary. The original fix used the
   `.rz-datatable-thead` selector, which does NOT exist in Radzen 10's DOM
   (confirmed via live computed-style inspection: header colour stuck at #212121 on
   the dark surface). Re-target the actual `.rz-grid-table thead` structure and pin
   the title span to the primary token — theme-agnostic, since the token flips
   per light/dark. */
.rz-grid-table thead th,
.rz-grid-table thead .rz-column-title,
.rz-grid-table thead .rz-column-title-content,
.rz-datatable .rz-grid-table thead th .rz-column-title,
.rz-datatable .rz-grid-table thead th .rz-column-title-content {
    color: var(--ap-text-primary);
}

.rz-grid-table thead .rz-column-title {
    font-weight: 600;
}

.rz-datatable .rz-grid-table thead th .rz-grid-filter-icon,
.rz-datatable .rz-grid-table thead th button.rz-grid-filter-icon,
.rz-datatable .rz-grid-table thead th .rzi {
    color: var(--ap-text-secondary);
}

.rz-pager .rz-pager-page.rz-state-active,
.rz-pager .rz-pager-element.rz-state-active {
    background-color: var(--ap-primary-subtle);
    color: var(--ap-primary-on-subtle);
    border-color: var(--ap-primary-on-subtle);
}

/* rz-data-grid-loading and rz-grid-loading were removed rather than re-pointed
   (spec section 7.2): 11.1.4 emits neither, and both identifiers Radzen DOES emit
   for this state are already in this rule, so the two were redundant rather than
   wrong. Re-pointing would have produced a duplicate. */
.rz-datatable-loading,
.rz-datatable-loading-content {
    pointer-events: none;
}

/* Finding P-5b: Radzen's "Light" secondary button (ButtonStyle.Light — e.g. the
   admin grid's "Bearbeiten"/Edit button) styles itself with a fixed light fill
   (--rz-base-200 ≈ #eeeeee) plus --rz-text-color, which the token sheet maps to
   the dark-theme light text — light-grey text on a light-grey fill (~1.16:1,
   fails WCAG AA). --rz-base-200 is never remapped, so pin the Light button onto
   the design-system surface tokens so foreground/background always pair
   correctly (~10.9:1 dark / ~16:1 light). Mirrors the tour app's T-1 fix. */
.rz-button.rz-light {
    /* D-4 semantic-first — see the note above .rz-textbox. */
    background-color: var(--ap-bg-elevated, var(--ap-surface-elevated));
    color: var(--ap-text-primary);
    /* Control boundary — WCAG 1.4.11 requires >= 3:1, so this takes the STRONG
       tier. The recessive --ap-border-default is for decorative rules only. */
    border: 1px solid var(--ap-border-strong);
}

.rz-button.rz-light:hover {
    /* D-4 semantic-first — see the note above .rz-textbox. */
    background-color: var(--ap-bg-page, var(--ap-surface));
    color: var(--ap-text-primary);
}

/* Finding P-5c (SystemOK theme-selector sweep, SHA 892a67c1): the P-5b fix above
   covers the FILLED Light button (.rz-button.rz-light), but Radzen colours the
   TEXT variant with a separate, higher-specificity rule —
   `.rz-button.rz-variant-text.rz-light { color: var(--rz-base-200) }` (#eeeeee).
   The ThemeToggle icon button ships exactly this class set
   (`rz-button rz-button-md rz-variant-text rz-light`, see
   AppPlatform.Shared.UI/Components/Layout/ThemeToggle.razor), so its icon glyph
   rendered #eeeeee — 1.16:1 on the light `--ap-bg-surface` the ThemeToggle sits
   on (the header user-dropdown panel AND the Showroom theme-selector exhibit
   preview frame both paint --ap-bg-surface). --rz-base-200 is a fixed Radzen base
   primitive that never flips per theme, so the glyph was near-invisible in light
   theme. Pin the text-variant Light foreground to the theme-aware primary text
   token (keeping the variant's transparent fill) so the icon pairs correctly in
   BOTH bases (#212121 on #ffffff ≈ 16:1 light / #E0E0E0 on #2d2d2d ≈ 11.9:1 dark).
   Specificity 0,3,0 matches Radzen's own rule and this sheet loads after
   material-base.css, so the later, equal-specificity rule wins the cascade. */
.rz-button.rz-variant-text.rz-light {
    color: var(--ap-text-primary);
}

.rz-button.rz-variant-text.rz-light:not(.rz-state-disabled):hover,
.rz-button.rz-variant-text.rz-light:not(.rz-state-disabled):active {
    color: var(--ap-text-primary);
}

.rz-alert .rz-button.rz-variant-text,
.rz-alert .rz-button.rz-variant-text .rzi,
.rz-alert .rz-button.rz-variant-text i {
    color: var(--ap-text-primary);
}

/* === RadzenAlert — container and message (AC-06) === */
.rz-alert {
    border-radius: var(--ap-radius-md);
    padding: var(--ap-space-3) var(--ap-space-4);
    background-color: var(--ap-bg-elevated, var(--ap-surface-elevated));
    color: var(--ap-text-primary);
    border: 1px solid var(--ap-border-default);
    box-shadow: var(--ap-shadow-sm);
}

.rz-alert-message {
    font-size: var(--ap-text-base);
}

.rz-alert .rz-alert-message,
.rz-alert .rz-alert-content,
.rz-alert .rz-alert-title,
.rz-alert .rz-alert-icon {
    color: inherit;
}

.rz-alert.rz-danger {
    border-left: 4px solid var(--ap-semantic-error);
}

.rz-alert.rz-warning {
    border-left: 4px solid var(--ap-semantic-warning);
}

.rz-alert.rz-info {
    border-left: 4px solid var(--ap-semantic-info);
}

.rz-alert.rz-success {
    border-left: 4px solid var(--ap-semantic-success);
}

/* === RadzenNotification — the toast surface ===
   The toast is the only confirmation many actions produce at the moment they finish, and it was
   the one Radzen surface this sheet never bound. Measured on deployed staging, driving a real
   import dry run, the success toast rendered:

       light   surface rgb(37, 53, 38)   text rgb(46, 125, 50)    2.53:1
       dark    surface rgb(37, 53, 38)   text rgb(102, 187, 106)  5.50:1

   Identical surface in BOTH themes, theme-dependent text: half the pair followed the theme and
   the other half did not, so the light theme put a dark-green foreground on a dark slab, under
   the 4.5:1 floor for its 16px text and under 3:1 for the close control.

   The asymmetry is exactly a mapping gap, and this sheet is where it belongs. Radzen writes
   `.rz-notification-item-wrapper { background-color: var(--rz-notification-container-background-color) }`
   and that variable resolves to `var(--rz-white)`, which nothing here maps. The FOREGROUND half
   is bound already — `tokens.generated.css` maps `--rz-success` onto `--ap-action-success-bg` —
   so the text moved with the theme while the surface underneath it could not.

   Consumers that link Radzen's DARK base (app-wodvtt links `material-dark-base.css` +
   `material-dark.css` unconditionally, at every theme) get `--rz-white` as a fixed dark value,
   which is why the surface measured the same in light and dark. Binding it here fixes every
   consumer at once and does not require an app to change which Radzen base it links — that
   choice affects every control in the app and is not this defect's to make.

   Bound with real selectors rather than by remapping the `--rz-*` variables alone. `RadzenTheme`
   also emits variables at runtime, so a `:root` remap here can be outranked by markup this sheet
   does not control; a two-class selector matching Radzen's own cannot be, because this sheet
   loads after Radzen's.

   Severity reads as a rail and not as a text colour, which is `.rz-alert`'s shape above and is
   deliberate: tinting the foreground is the failure mode being fixed, and a border is a shape
   cue that survives a theme whose semantic hue is low-contrast. */
.rz-notification-item-wrapper {
    /* D-4 semantic-first — see the note above .rz-textbox. */
    background-color: var(--ap-bg-elevated, var(--ap-surface-elevated));
    border: 1px solid var(--ap-border-default);
    border-radius: var(--ap-radius-md);
    box-shadow: var(--ap-shadow-md);
}

/* Radzen's own severity rules are `.rz-notification-<severity> .rz-notification-item`, two
   classes. These match that weight exactly and win on load order, which is why the severity
   selector is repeated here rather than collapsed into `.rz-notification-item` alone. */
.rz-notification-success .rz-notification-item,
.rz-notification-warn .rz-notification-item,
.rz-notification-error .rz-notification-item,
.rz-notification-info .rz-notification-item {
    background-color: transparent;
    color: var(--ap-text-primary);
}

.rz-notification-item .rz-notification-message,
.rz-notification-item .rz-notification-title,
.rz-notification-item .rz-notification-content,
.rz-notification-item .rz-notification-icon,
.rz-notification-item .rz-notification-close {
    color: inherit;
}

.rz-notification-item-wrapper.rz-notification-success {
    border-inline-start: 4px solid var(--ap-semantic-success);
}

.rz-notification-item-wrapper.rz-notification-warn {
    border-inline-start: 4px solid var(--ap-semantic-warning);
}

.rz-notification-item-wrapper.rz-notification-error {
    border-inline-start: 4px solid var(--ap-semantic-error);
}

.rz-notification-item-wrapper.rz-notification-info {
    border-inline-start: 4px solid var(--ap-semantic-info);
}

/* === RadzenDialog — overlay, container, titlebar, and content (AC-07) === */
.rz-dialog {
    /* D-4 semantic-first — see the note above .rz-textbox. */
    background-color: var(--ap-bg-elevated, var(--ap-surface-elevated));
    border: 1px solid var(--ap-border-default);
    border-radius: var(--ap-radius-lg);
    box-shadow: var(--ap-shadow-lg);
}

.rz-dialog-titlebar {
    padding: var(--ap-space-4);
    border-bottom: 1px solid var(--ap-border-default);
}

.rz-dialog-title {
    font-size: var(--ap-text-base);
    font-weight: 600;
    color: var(--ap-text-primary);
}

.rz-dialog-content {
    padding: var(--ap-space-4);
}

/* === RadzenSkeleton — loading placeholder (AC-08) === */
.rz-skeleton {
    background-color: var(--ap-surface-border);
    border-radius: var(--ap-radius-md);
}

.rz-skeleton-text {
    border-radius: var(--ap-radius-md);
}

/* === RadzenBadge — secondary === */
/* This rule used to pin the filled secondary badge to --ap-secondary by hand,
   because "the Radzen bridge maps --rz-primary/danger/warning/success/info but
   never --rz-secondary" and BadgeStyle.Secondary therefore fell back to Radzen's
   default magenta (#e31c65 — off-palette, ~no relation to any --ap token).

   The bridge now maps --rz-secondary and --rz-on-secondary, so Radzen's own
   `.rz-badge-secondary { background-color: var(--rz-secondary); color:
   var(--rz-on-secondary) }` resolves to the accent with a readable label by
   itself, and this rule has nothing left to add.

   The label half is what makes this a fix rather than a move. The old rule paired
   the accent with a FIXED --ap-text-on-accent, and --ap-secondary is whatever
   palette the consuming app ships: "white-on-teal stays >= AA" was true of ours
   and of nothing else. Measured on app-wodvtt staging, white on a filled
   Secondary badge: blood-moon 2.58, wraiths-veil 2.92, kindred-codex 2.93, dark
   3.00, elysium 3.62 — five of seven themes under the 4.5:1 floor for normal
   text. --rz-on-secondary now resolves through --ap-action-secondary-text, which
   OnAccentDerivation picks per theme against that theme's own --ap-secondary
   (platform.theming.action-on-accent.v1), so the label is legible whatever the
   accent is. Our own badges are unchanged: the derivation returns white for both
   our teals.

   Removing the rule also returns control to the consuming app, which pinning had
   taken away — at two classes to Radzen's one it outranked --rz-secondary no
   matter what the app mapped. */

/* === RadzenProfileMenu — theme-blind surface primitives (finding R6) ===
   Adversarial visual sweep of wodvtt staging (build e36196a3) measured the
   account/profile-menu trigger as a solid rgb(37,37,37) (#252525) slab in BOTH
   themes, with a chevron that DOES follow the theme — #212121 in light, #E0E0E0
   in dark. Measured on the region tiles: 1.05:1 in light, 11.61:1 in dark for
   the identical element. In light theme that is a black rectangle in a white
   header with an invisible glyph: the control reads as a broken image, not a
   menu.

   ROOT CAUSE — the same fixed-Radzen-primitive family as P-5b/P-5c above, but
   inverted: here it is the SURFACE that is theme-blind and the FOREGROUND that
   flips. Consuming apps load ONE Radzen sheet for both themes (wodvtt pins
   `material-dark.css`) and let the --ap-* tokens do the light/dark work.
   material-dark.css declares

       --rz-profile-menu-top-item-background-color: var(--rz-base-700);  // #252525
       --rz-profile-menu-background-color:          var(--rz-base-700);  // #252525
       --rz-profile-menu-toggle-button-color:       var(--rz-text-color);

   `--rz-base-700` is a fixed base primitive the token bridge never remaps, so
   the trigger AND the open dropdown panel stay #252525 in every theme, while
   `--rz-text-color` IS bridged (tokens.generated.css maps it to
   --ap-text-primary) and therefore flips. One half of the pair moved and the
   other did not.

   FIX — remap the whole profile-menu surface family onto --ap-* tokens so the
   surface and its foreground always flip together. Because these are Radzen's
   own profile-menu-scoped custom properties, nothing outside the profile menu
   is touched (--rz-text-color / --rz-menu-item-color stay as they are), and a
   future Radzen theme bump cannot re-introduce the split by changing only one
   side. Resulting contrast: chevron/item text #212121 on #ffffff ~ 16.1:1
   (light) and #E0E0E0 on #2d2d2d ~ 11.9:1 (dark).

   The four surface/foreground remaps live in the GENERATED bridge
   (tokens/theme.tokens.json -> tokens.generated.css), next to the other --rz-*
   mappings, because that sheet is the authoritative bridge and it loads after
   the Radzen theme sheet in every host. Only the structural rules Radzen does
   not express as a custom property are declared here.

   Regression test of record: AppPlatform.Tests/DesignTokens/ProfileMenuContrastTests.cs */

/* The trigger was only legible before because it was a black block; on a theme
   surface it needs a real boundary. Control boundary — WCAG 1.4.11 requires
   >= 3:1, so this takes the STRONG tier (#868686 on #ffffff ~ 3.5:1 light,
   #7d7d7d on #2d2d2d ~ 3.4:1 dark). */
ul.rz-profile-menu {
    border: 1px solid var(--ap-border-strong);
    border-radius: var(--ap-radius-md);
}

ul.rz-profile-menu .rz-navigation-menu {
    border: 1px solid var(--ap-border-strong);
}

/* Radzen pairs the hover fill with `--rz-menu-item-hover-color: var(--rz-white)`,
   which is white-on-white once the fill is the light-theme --ap-primary-subtle
   tint. --rz-menu-item-hover-color is NOT profile-menu-scoped (it also colours
   the side-rail panel menu), so pin the hover foreground here instead, using the
   --ap-primary-subtle / --ap-primary-on-subtle pair this repo already treats as
   a unit (see `.rz-pager .rz-state-active` above). */
ul.rz-profile-menu .rz-navigation-menu .rz-navigation-item-wrapper:hover .rz-navigation-item-link,
ul.rz-profile-menu .rz-navigation-menu .rz-navigation-item-wrapper:hover .rz-navigation-item-text,
ul.rz-profile-menu .rz-navigation-menu .rz-navigation-item-wrapper:hover .rzi {
    color: var(--ap-primary-on-subtle);
}
