/* One type system for the whole app — matches MudTheme.Typography so Mud
   components and raw HTML (login, error pages) render in the same font.
   Inter when it loads, Segoe UI / system as the native Windows fallback. */
html, body {
    font-family: "Inter", "Segoe UI", system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
    font-feature-settings: "cv02", "cv03", "cv04", "ss01";  /* Inter's cleaner letterforms (no-op on fallback) */
}

/* Motion tokens — strong custom curves (the built-in CSS easings are too weak). */
:root {
    --ease-out: cubic-bezier(0.23, 1, 0.32, 1);       /* crisp UI entrances & hover lifts */
    --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);   /* on-screen movement */
}

/* Tactile press feedback on every action button (subtle, fast). */
.mud-button-root {
    transition: transform 120ms var(--ease-out);
}
.mud-button-root:active {
    transform: scale(0.98);
}
@media (prefers-reduced-motion: reduce) {
    .mud-button-root { transition: none; }
    .mud-button-root:active { transform: none; }
}

a, .btn-link {
    color: var(--mud-palette-primary, #1b6ec2);
}

/* Blazor form validation (harmless if unused) */
.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

/* ---- Shared layout / typography tokens ---- */

/* Single width source for the attendee detail surfaces (was a magic number
   duplicated inline on every card/panel). */
.detail-shell {
    max-width: 1600px;
}

/* Label/value pair — small muted label above a normal-weight value. */
.tpni-field {
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.tpni-field-label {
    font-size: 0.72rem;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
}

.tpni-field-value {
    font-size: 0.95rem;
    color: var(--mud-palette-text-primary);
    word-break: break-word;
}

/* Attendee view — Registration Details. Section band separates the field groups; the Q&A block
   is for long demographic questions (full-width row, sentence-case label) so they read as a
   question instead of an uppercase label crammed into a narrow column. */
.tpni-detail-section {
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--mud-palette-primary);
    margin: 20px 0 8px;
    padding-bottom: 5px;
    border-bottom: 1px solid var(--mud-palette-lines-default);
}
.tpni-detail-section:first-child {
    margin-top: 4px;
}
.tpni-qa-label {
    font-size: 0.82rem;
    line-height: 1.35;
    color: var(--mud-palette-text-secondary);
    margin-bottom: 2px;
}
.tpni-qa-value {
    font-size: 0.95rem;
    color: var(--mud-palette-text-primary);
    white-space: pre-wrap;
    word-break: break-word;
}

/* Centered empty / initial state (Search + the shared EmptyState component). */
.search-empty,
.tpni-empty {
    text-align: center;
    padding: 56px 16px;
    color: var(--mud-palette-text-secondary);
}

/* Right-align a MudDataGrid numeric column HEADER over its right-aligned values.
   HeaderStyle="text-align:right" alone doesn't bite because the sortable header
   title sits in a .column-header / .sortable-column-header wrapper. Force that
   inner wrapper to a flex row justified to the end so the title + sort arrow sit
   over the right-aligned $$ values (text-align is the non-flex fallback). */
.tpni-th-right .column-header {
    justify-content: flex-end;   /* push the whole header block (title + sort arrow) to the right */
}
.tpni-th-right .sortable-column-header {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    text-align: right;
}
.tpni-th-right { text-align: right; }

/* Search results as compact tap cards on phone / narrow tablet.
   Replaces the MudDataGrid auto-stacked label:value rows, which run very tall. */
.search-cards {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.search-card {
    padding: 12px 14px;
    cursor: pointer;
    border-radius: 10px;
    transition: background-color 120ms ease;
}
.search-card:active {
    background-color: var(--mud-palette-action-default-hover);
}
.search-card-top {
    display: flex;
    align-items: baseline;
    gap: 10px;
}
.search-card-name {
    font-weight: 700;
    font-size: 1.05rem;
    line-height: 1.25;
    flex: 1 1 auto;
    min-width: 0;
    word-break: break-word;
}
.search-card-reg {
    flex: 0 0 auto;
    font-weight: 700;
    font-size: 0.8rem;
    letter-spacing: 0.02em;
    color: var(--mud-palette-primary);
    background-color: var(--mud-palette-primary-hover);
    padding: 2px 8px;
    border-radius: 6px;
    white-space: nowrap;
}
.search-card-line {
    margin-top: 2px;
    color: var(--mud-palette-text-primary);
}
.search-card-sub {
    color: var(--mud-palette-text-secondary);
    font-size: 0.85rem;
    word-break: break-all;
}
.search-card-money {
    display: flex;
    align-items: baseline;
    gap: 14px;
    margin-top: 6px;
    font-size: 0.9rem;
    color: var(--mud-palette-text-primary);
}
.search-card-money-label {
    color: var(--mud-palette-text-secondary);
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}
.search-card-id {
    color: var(--mud-palette-text-secondary);
    font-size: 0.8rem;
    white-space: nowrap;
}

/* Generic admin-list cards (ListCard) for phone / narrow tablet — the reusable version of the
   search cards, used by Reg Types / Products / Product Groups / Demographics / Source Codes, etc.
   Replaces the MudDataGrid's tall auto-stacked label:value rows with a compact tap-to-edit card. */
.tpni-cards {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.tpni-card {
    padding: 12px 14px;
    cursor: pointer;
    border-radius: 10px;
    background: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    transition: background-color 120ms ease;
}
.tpni-card:active {
    background-color: var(--mud-palette-action-default-hover);
}
.tpni-card-code {
    font-weight: 700;
    font-size: 1.02rem;
    line-height: 1.25;
    word-break: break-word;
}
.tpni-card-desc {
    margin-top: 2px;
    color: var(--mud-palette-text-primary);
    word-break: break-word;
}
.tpni-card-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin-top: 8px;
}
.tpni-card-chip {
    font-size: 0.75rem;
    padding: 2px 9px;
    border-radius: 999px;
    background: var(--mud-palette-action-default-hover);
    color: var(--mud-palette-text-secondary);
    white-space: nowrap;
}
.tpni-card-chip b {
    font-weight: 700;
    color: var(--mud-palette-text-primary);
}
.tpni-card-chip.on {
    background: var(--mud-palette-primary-hover);
    color: var(--mud-palette-primary);
    font-weight: 600;
}
.tpni-card-chip.warn {
    background: var(--mud-palette-warning-hover);
    color: var(--mud-palette-warning);
    font-weight: 600;
}
.tpni-card-chip.ok {
    background: var(--mud-palette-success-hover);
    color: var(--mud-palette-success);
    font-weight: 600;
}
.tpni-card-chip.info {
    background: var(--mud-palette-info-hover);
    color: var(--mud-palette-info);
    font-weight: 600;
}
.tpni-card-chip.err {
    background: var(--mud-palette-error-hover);
    color: var(--mud-palette-error);
    font-weight: 600;
}

/* Home module cards: a quiet hover lift to hint they're interactive.
   Gated so a touch tap doesn't trigger a sticky hover. */
.module-card {
    transition: transform 150ms var(--ease-out), box-shadow 150ms ease;
}

@media (hover: hover) and (pointer: fine) {
    .module-card:hover {
        transform: translateY(-2px);
        box-shadow: var(--mud-elevation-6);
    }
}

@media (prefers-reduced-motion: reduce) {
    .module-card {
        transition: box-shadow 150ms ease;
    }
    .module-card:hover {
        transform: none;
    }
}

/* Section header inside dialogs: label that never wraps + a rule filling the row. */
.section-label {
    display: flex;
    align-items: center;
    margin-top: 16px;
    margin-bottom: 8px;
}
.section-label-text {
    white-space: nowrap;
    flex-shrink: 0;       /* don't let the rule squeeze the label into a vertical stack */
}
.section-label-rule {
    flex: 1 1 auto;
    margin-left: 12px;
}

/* Editor dialogs: keep MudGrid gutters from producing a stray horizontal scrollbar. */
.tpni-editor-dialog .mud-dialog-content {
    overflow-x: hidden;
}

/* Email "Design" contenteditable + "Preview" surface. */
.tpni-rte,
.tpni-preview {
    min-height: 320px;
    max-height: 460px;
    overflow-y: auto;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 4px;
    padding: 12px 14px;
    background: var(--mud-palette-surface);
}
.tpni-rte:focus {
    outline: none;
    border-color: var(--mud-palette-primary);
}
.tpni-rte img, .tpni-preview img { max-width: 100%; }

/* Monospace input for code/HTML bodies. */
.tpni-mono .mud-input-slot,
.tpni-mono textarea {
    font-family: "Cascadia Code", "Consolas", "SFMono-Regular", monospace;
    font-size: 0.85rem;
}

/* MudBlazor 9.5 SYSTEMIC FIELD-HEIGHT BUG (Nick 2026-07-21, confirmed live via DevTools on Sender
   setup). A MudTextField's `Style` attribute is applied to the inner `.mud-input` element — which is
   a flex COLUMN child of the control — so an inline `flex:1 1 Npx` (written to size the field's WIDTH
   in a row) is interpreted as an N-px HEIGHT, ballooning single-line fields (SendGrid key, webhook
   URL, CNAME host/value, verification-to) into giant empty boxes. Neutralize that flex-basis on the
   inner element; width still comes from the field's own max-width and the row layout. Scoped to the
   exact broken pattern (`flex:1 1` in an inline style) so it can't touch a legitimately-tall field
   (those use Lines=, never an inline flex-basis). One rule fixes every affected field, this page or
   any other. */
.mud-input-control .mud-input[style*="flex:1 1"] { flex: initial !important; }

/* Reg Type editor: scrollable question / product assignment lists. */
.assign-list {
    max-height: 340px;
    overflow-y: auto;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 6px;
    padding: 4px 8px;
}
.assign-list .mud-input-control {
    margin-top: 0;
    margin-bottom: 0;
}

/* Standard page-header band — every non-Home route uses SetupPageHeader, which renders a manual
   breadcrumb (not MudBreadcrumbs — needed prominent styling we couldn't get from defaults).
   Background tint + left-border accent make the nav path read as a distinct "you are here"
   panel rather than getting lost as small grey text near the title. */
.tpni-page-breadcrumb {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    padding: 9px 14px;
    margin-bottom: 14px;
    background: var(--mud-palette-background-grey, #f5f7fa);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 8px;
    font-size: 13.5px;
}
.tpni-breadcrumb-link {
    color: var(--mud-palette-primary, #1b6ec2);
    text-decoration: none;
    font-weight: 500;
    transition: color 100ms var(--ease-out);
}
.tpni-breadcrumb-link:hover {
    color: var(--mud-palette-primary-darken, #145a99);
    text-decoration: underline;
}
.tpni-breadcrumb-sep {
    color: var(--mud-palette-text-secondary, rgba(0,0,0,0.6));
    font-weight: 400;
    margin: 0 2px;
}
.tpni-breadcrumb-current {
    font-weight: 700;
    color: var(--mud-palette-text-primary, rgba(0,0,0,0.87));
}
/* Title row sits right under the breadcrumb. Keep it compact so the breadcrumb still anchors the
   eye at the top of the page. */
.tpni-page-titlebar {
    min-height: 40px;
    /* Title + action buttons share one flex row. On a phone the buttons (Refresh / Export …) would
       otherwise overflow past the title and push the whole page wider than the viewport (the
       horizontal "floating" / drift). Wrapping lets them drop to a second line instead. No effect on
       desktop, where everything fits on one line and wrap never triggers. */
    flex-wrap: wrap;
    row-gap: 6px;
}

/* ---- Mobile horizontal "drift" guard (page rendering wider than the phone) ----
   On a phone the Onsite analytics page exceeded the viewport width and the whole page
   drifted sideways (left edge clipped off-screen). The page-titlebar wrap above wasn't
   enough — there were two more leaks, both fixed here:
   (1) Long company / source-code names in the Onsite tap-card rows: `text-truncate`
       only ellipsizes if its flex item is allowed to shrink, but a flex item's
       min-width defaults to `auto`, so the nowrap name forced the row — and the whole
       page — wider than the screen. min-width:0 lets it truncate instead.
   (2) Belt-and-suspenders: clip any residual horizontal overflow (e.g. MudGrid's
       negative gutters) on the page's scroll container so it can never scroll/drift
       sideways. `clip` (NOT `hidden`) so the sticky "Jump to" bar + vertical scroll
       keep working. Scoped to phone/tablet (<=960px); desktop is untouched. */
.tpni-onsite-card .text-truncate { min-width: 0; }
@media (max-width: 960px) {
    .mud-main-content { overflow-x: clip; }
}

/* MudTabs active-state highlighting — the MudBlazor default slider bar alone wasn't loud enough
   to tell the user which tab they're on (especially on the User dialog where there's a colored
   toolbar background). The active tab becomes a solid white "pill" floating above the blue toolbar
   so it reads from across the room. Inactive tabs fade significantly to underline the contrast. */
.mud-tabs .mud-tab.mud-tab-active {
    font-weight: 700 !important;
    letter-spacing: 0.4px !important;
}
.mud-tabs .mud-tab-slider {
    height: 5px !important;
    border-radius: 3px 3px 0 0;
    box-shadow: 0 0 0 1px rgba(255,255,255,0.5);
}

/* When the tab toolbar has a colored background (e.g. the User dialog uses Color.Primary on
   its outer MudTabs), the active tab takes a solid white pill with primary-blue text + icon so it
   pops off the blue band. Inactive tabs get a real white-with-alpha look (not bleached) and a
   bright underline accent on hover. */
.mud-tabs-toolbar .mud-tab.mud-tab-active {
    background-color: #ffffff !important;
    color: var(--mud-palette-primary, #1976d2) !important;
    border-radius: 8px 8px 0 0;
    box-shadow: 0 -2px 0 0 #ffffff, 0 4px 12px -2px rgba(0,0,0,0.25);
    transform: translateY(-1px);
    z-index: 2;
}
.mud-tabs-toolbar .mud-tab.mud-tab-active .mud-icon-root,
.mud-tabs-toolbar .mud-tab.mud-tab-active .mud-tab-icon {
    color: var(--mud-palette-primary, #1976d2) !important;
}

/* SR-00206 E2: 0.55 on an inactive TAB is navigation the user cannot read. An inactive tab still has
   to be legible enough to choose — the active-tab underline and colour already carry "which one am I
   on", so the opacity only needs to say "not this one", not "don't read this". */
.mud-tabs-toolbar .mud-tab:not(.mud-tab-active) {
    opacity: 0.82;
}
.mud-tabs-toolbar .mud-tab:not(.mud-tab-active):hover {
    opacity: 0.95;
    background-color: rgba(255,255,255,0.08);
}
@media (prefers-reduced-motion: reduce) {
    .mud-tabs-toolbar .mud-tab { transition: none; transform: none; }
    .tpni-breadcrumb-link { transition: none; }
}

/* User dialog → Client/Event Access sub-tabs — each multi-select list lives in a fixed-height
   scroll container so the dialog doesn't grow indefinitely when there are 100+ clients/events. */
.access-scroll {
    max-height: 50vh;
    overflow-y: auto;
    border: 1px solid var(--mud-palette-lines-default, rgba(0,0,0,0.12));
    border-radius: 6px;
    padding: 4px;
}

/* HTML Pages folder tree — indented div rows so we get total control over rendering and don't
   depend on MudTreeView (which 9.5 rendered empty when bound to TreeItemData<string>). Each row is
   a single line: icon + name + file count chip; depth comes from inline padding-left. Hover gives
   a faint primary tint; selected row gets a stronger one + bolder text. */
.html-folder-tree {
    display: flex;
    flex-direction: column;
}
.html-folder-row {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 8px;
    border-radius: 4px;
    cursor: pointer;
    user-select: none;
    transition: background-color 100ms var(--ease-out);
}
.html-folder-row:hover {
    background-color: var(--mud-palette-action-default-hover, rgba(0, 0, 0, 0.04));
}
.html-folder-row--selected {
    background-color: var(--mud-palette-primary-hover, rgba(25, 118, 210, 0.10));
    font-weight: 600;
}
.html-folder-name {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.html-folder-count {
    font-size: 11px;
    color: var(--mud-palette-text-secondary, rgba(0, 0, 0, 0.6));
    background: var(--mud-palette-action-default-hover, rgba(0, 0, 0, 0.06));
    border-radius: 10px;
    padding: 1px 8px;
    min-width: 24px;
    text-align: center;
}
@media (prefers-reduced-motion: reduce) {
    .html-folder-row { transition: none; }
}

/* Master-detail pattern (Current Lists ↔ List details, future: HTML Pages list ↔ editor).
   Panels match height so the two cards read as one composed unit, and the names list inside
   the left panel is independently scrollable when it overflows. */
.customlist-panel {
    height: 100%;
    min-height: 480px;
}
.customlist-names {
    max-height: 60vh;
    overflow-y: auto;
}
/* Selected list-name row picks up a subtle primary tint so it's obvious which list the
   right panel is showing. (Mud's default selection style is a faint underline — too soft
   for this two-column reading.) */
.customlist-names .mud-selected-item {
    background-color: var(--mud-palette-primary-hover, rgba(25, 118, 210, 0.08));
}

/* Campaign Setup landing: each group is its own elevated card with breathing room
   below — turns the landing into a chunkable hierarchy instead of a wall of
   uniform tiles. The shadow stays subtle (Material elevation-1 equivalent) so
   the cards inside the group remain the visual focus, not the container. */
.setup-group {
    background: var(--mud-palette-surface, #fff);
    border-radius: 10px;
    padding: 20px 20px 8px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.06);
    margin-bottom: 28px;
}
.setup-group:last-of-type {
    margin-bottom: 0;
}
.setup-group .section-label {
    margin-top: 0;     /* the group's own padding handles the top breathing room */
}

/* Campaign Setup section cards. Enabled ones lift on hover + dip on press; disabled
   stay flat with only their text muted (the "Coming soon" chip stays legible). */
.setup-card {
    height: 100%;
    transition: transform 150ms var(--ease-out), box-shadow 150ms ease;
}
.setup-card--off .setup-card-text {
    opacity: 0.6;
}
/* Chevron that nudges right on hover to signal "go". */
.setup-card-go {
    transition: transform 150ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
    .setup-card--on:hover {
        transform: translateY(-2px);
        box-shadow: var(--mud-elevation-6);
    }
    .setup-card--on:hover .setup-card-go {
        transform: translateX(4px);
    }
}
.setup-card--on:active {
    transform: scale(0.98);
}
@media (prefers-reduced-motion: reduce) {
    .setup-card,
    .setup-card-go {
        transition: box-shadow 150ms ease;
    }
    .setup-card--on:hover,
    .setup-card--on:active,
    .setup-card--on:hover .setup-card-go {
        transform: none;
    }
}

/* Register Application "Current Menus" tree — indented div tree (MudTreeView 9.5 renders empty when
   data-bound, so we hand-roll it). Groups expand to reveal their options + a "New Menu Option" row. */
.tpni-menu-tree { font-size: 0.9rem; }
.tpni-menu-tree .tpni-tree-root {
    display: flex; align-items: center; font-weight: 600;
    padding: 4px 4px 6px; color: var(--mud-palette-text-primary, rgba(0,0,0,0.87));
}
.tpni-menu-tree .tpni-tree-group,
.tpni-menu-tree .tpni-tree-option {
    display: flex; align-items: center; cursor: pointer; border-radius: 6px;
    padding: 4px 6px; transition: background-color 120ms ease;
}
.tpni-menu-tree .tpni-tree-group { padding-left: 18px; }
.tpni-menu-tree .tpni-tree-option { padding-left: 48px; }
.tpni-menu-tree .tpni-tree-group:hover,
.tpni-menu-tree .tpni-tree-option:hover { background-color: var(--mud-palette-action-default-hover, rgba(0,0,0,0.04)); }
.tpni-menu-tree .tpni-tree-group.selected,
.tpni-menu-tree .tpni-tree-option.selected { background-color: var(--mud-palette-primary-hover); font-weight: 600; }
.tpni-menu-tree .tpni-tree-toggle { margin-right: 4px; color: var(--mud-palette-text-secondary, rgba(0,0,0,0.54)); }
.tpni-menu-tree .tpni-tree-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tpni-menu-tree .tpni-tree-option .tpni-tree-label { color: var(--mud-palette-primary, #1976d2); }
.tpni-menu-tree .tpni-tree-add .tpni-tree-label { color: var(--mud-palette-success, #2e7d32); font-style: italic; }

/* ============================================================================
   TPNI Console visual system — brand v1 token pass (2026-07-18, locked)
   Charcoal neutrals + Pulse teal #0C7C84 primary, per-workspace accents
   (--tpni-accent-*), Urbanist headings, the three-blade mark as favicon/
   lockup/loader (assets.tpni.com/brand — never recreate a logo). Built ON
   TOP of the MudTheme palette (MainLayout.razor) so the var(--mud-palette-*)
   usages elsewhere in this file track the brand automatically.
   ========================================================================== */
:root {
    /* Layered, cool-tinted depth (toward the palette ink, never flat grey/black). */
    --tpni-shadow-sm: 0 1px 2px rgba(22,28,29,0.06), 0 1px 3px rgba(22,28,29,0.05);
    --tpni-shadow-md: 0 2px 6px rgba(22,28,29,0.06), 0 8px 18px rgba(22,28,29,0.08);
    --tpni-shadow-lg: 0 12px 36px rgba(22,28,29,0.15), 0 4px 10px rgba(22,28,29,0.07);
    --tpni-ring: 0 0 0 3px rgba(12,124,132,0.30);
    --tpni-radius-card: 12px;
    /* Brand v1 workspace accents (assets.tpni.com/brand) — one accent blade per product. */
    --tpni-accent-pulse: #0C7C84;   /* CM / Pulse (the primary) */
    --tpni-accent-leads: #A23640;   /* Lead Retrieval */
    --tpni-accent-events: #7C9BB3;  /* organizer / Events */
    --tpni-accent-ams: #6E4F8E;     /* AMS / Membership */
}

/* ---- Depth: replace MudBlazor's flat grey elevations with the cool stack ---- */
.mud-paper.mud-elevation-1 { box-shadow: var(--tpni-shadow-sm); }
.mud-paper.mud-elevation-2,
.mud-paper.mud-elevation-3 { box-shadow: var(--tpni-shadow-md); }
.mud-paper.mud-elevation-4,
.mud-paper.mud-elevation-6,
.mud-paper.mud-elevation-8 { box-shadow: var(--tpni-shadow-lg); }
/* Outlined surfaces (most of the app's panels) get a crisper hairline + soft lift. */
.mud-paper.mud-paper-outlined {
    border-color: var(--mud-palette-lines-default);
    box-shadow: var(--tpni-shadow-sm);
}
/* Slightly softer card corners read more modern than the default 4px. */
.mud-card, .mud-paper.mud-paper-outlined, .mud-table-container { border-radius: var(--tpni-radius-card); }

/* ---- App bar: brand-ink charcoal with a deep-teal cast off the right edge (brand v1) ---- */
.mud-appbar.mud-appbar-fixed-top,
.mud-appbar {
    background-image: linear-gradient(100deg, #1b1d1f 0%, #26282b 52%, #0a5560 125%);
    box-shadow: 0 1px 0 rgba(22,28,29,0.10), 0 6px 18px -8px rgba(22,28,29,0.45);
}
.mud-appbar .mud-typography { letter-spacing: -0.01em; }

/* ---- Page content must clear the FIXED app bar ----------------------------------------------
   MudMainContent carries its own top offset for the fixed app bar, but MainLayout renders it as
   <MudMainContent Class="pa-6"> and the pa-6 utility sets padding on ALL FOUR sides with
   !important, clobbering that offset. Result: every page's title row (.tpni-page-titlebar) laid
   out at y=24 against a 64px-tall fixed bar — the page TITLE was hidden underneath it and only a
   few pixels poked out below, which reads as a stray artifact under the black bar.
   ⭐ Measured live on dev 2026-07-21 (DevTools, not guessed): --mud-appbar-height=64px,
   .mud-main-content padding-top=24px, titlebar top=24px → after this rule 88px and it clears.
   ⚠️ The !important is REQUIRED — a plain .mud-main-content.pa-6 rule is more specific but still
   loses to the utility's !important (verified: it silently did nothing). */
.mud-main-content.pa-6 { padding-top: calc(var(--mud-appbar-height, 64px) + 24px) !important; }

/* Brand v1 — the app-bar lockup (white corporate lockup SVG + the surface name in Urbanist). */
.tpni-appbar-brand { display: flex; align-items: center; gap: 10px; text-decoration: none; color: inherit; }
.tpni-appbar-brand img { height: 24px; display: block; }
.tpni-appbar-brand span {
    font-family: 'Urbanist', 'Inter', 'Segoe UI', system-ui, sans-serif;
    font-weight: 600; font-size: 1.02rem; letter-spacing: 0.012em; color: #fff; opacity: .92;
    border-left: 1px solid rgba(255,255,255,.28); padding-left: 10px; line-height: 1;
}

/* Brand v1 — kinetic-mark loader (BrandLoader.razor); the SVG carries its own reduced-motion guard. */
.tpni-brand-loader { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; padding: 26px 0; }
.tpni-brand-loader span { font-size: .8rem; color: var(--mud-palette-text-secondary); letter-spacing: .02em; }

/* ---- Drawer / nav: cool panel + an active-item pill (no side stripe) ---- */
.mud-drawer {
    border-right: 1px solid var(--mud-palette-lines-default);
}
.mud-drawer .mud-navmenu .mud-nav-link {
    border-radius: 8px;
    margin: 1px 8px;
    transition: background-color 120ms var(--ease-out), color 120ms var(--ease-out);
}
.mud-drawer .mud-navmenu .mud-nav-link:hover {
    background-color: var(--mud-palette-action-default-hover);
}
.mud-drawer .mud-navmenu .mud-nav-link.active,
.mud-drawer .mud-navmenu .mud-nav-link.mud-nav-link-active {
    background-color: var(--mud-palette-primary-hover);
    color: var(--mud-palette-primary);
    font-weight: 600;
}
.mud-drawer .mud-navmenu .mud-nav-link.active .mud-icon-root {
    color: var(--mud-palette-primary);
}
.mud-drawer-header .mud-typography {
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: 0.72rem;
    font-weight: 700;
    color: var(--mud-palette-text-secondary);
}

/* ---- Branded focus ring: replace the default browser outline app-wide ---- */
.mud-button-root:focus-visible,
.mud-icon-button:focus-visible,
.mud-input-control .mud-input-slot:focus-visible,
.mud-nav-link:focus-visible,
.tpni-tree-group:focus-visible,
.tpni-tree-option:focus-visible,
a:focus-visible {
    outline: none;
    box-shadow: var(--tpni-ring);
    border-radius: 8px;
}
/* Outlined inputs draw their own focused border — the inner slot ring would show a second
   nested box (e.g. the Support "describe your problem" textarea). */
.mud-input.mud-input-outlined .mud-input-slot:focus-visible {
    box-shadow: none;
}

/* ---- Buttons: a touch more presence on filled primary ---- */
.mud-button-filled.mud-button-filled-primary {
    box-shadow: 0 1px 2px rgba(10,85,96,0.30), 0 2px 6px -1px rgba(10,85,96,0.28);
}
.mud-button-filled.mud-button-filled-primary:hover {
    box-shadow: 0 2px 4px rgba(10,85,96,0.34), 0 6px 14px -2px rgba(10,85,96,0.34);
}

/* ---- Data grids / tables: quieter header, clearer hover + selection ---- */
.mud-table-root .mud-table-head .mud-table-cell,
.mud-data-grid .mud-table-head .mud-table-cell {
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-size: 0.71rem;
    font-weight: 700;
    color: var(--mud-palette-text-secondary);
    background-color: var(--mud-palette-background-grey);
    border-bottom: 1px solid var(--mud-palette-lines-default);
}
.mud-table-row:hover > .mud-table-cell,
.mud-data-grid .mud-table-row:hover > .mud-table-cell {
    background-color: var(--mud-palette-table-hover);
}

/* ---- Row action icons sit side by side (Edit then Delete), never stacked ----
   The grid's auto table-layout was shrinking the Actions column to a single button's
   min-content width, which forced the second icon to wrap onto a new line. nowrap keeps
   both icons on one line and lets the column size to fit. Markup order is Edit then Delete,
   so horizontal layout puts Delete on the right. Scoped to cells that actually contain an
   icon button (the action cells) so text columns still wrap normally. */
.mud-table-cell:has(.mud-icon-button) { white-space: nowrap; }

/* ---- Chips / status: a hair more saturation + weight so state reads fast ---- */
.mud-chip { font-weight: 600; letter-spacing: 0.01em; }

/* ---- Dialogs: softer corners + the elevated cool shadow ---- */
.mud-dialog { border-radius: 14px; box-shadow: var(--tpni-shadow-lg); }
.mud-dialog-title { font-weight: 650; letter-spacing: -0.01em; }

/* ---- Tab pill polish: track the new brand surface instead of pure white ---- */
.mud-tabs-toolbar .mud-tab.mud-tab-active {
    background-color: var(--mud-palette-surface) !important;
    box-shadow: 0 -2px 0 0 var(--mud-palette-surface), 0 6px 16px -4px rgba(24,30,60,0.30);
}

/* ---- Setup-group cards: adopt the cool depth instead of the old grey shadow ---- */
.setup-group { box-shadow: var(--tpni-shadow-sm); }

/* ---- Page content gets a touch more horizontal breathing room on wide screens ---- */
@media (min-width: 1600px) {
    .mud-main-content > .mud-container,
    .mud-main-content { max-width: 1680px; }
}

/* ==== #151 Lane B — customer home (CmCustomerHome / OrganizerCustomerHome) ==== */
.tpni-cust-stats { display: flex; gap: 12px; margin-top: 18px; flex-wrap: wrap; }
.tpni-cust-stat { border-radius: var(--tpni-radius-card); padding: 12px 18px; min-width: 132px; }
.tpni-cust-stat .n { font-size: 22px; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.tpni-cust-stat .l { font-size: 11.5px; color: var(--mud-palette-text-secondary); margin-top: 2px; }
.tpni-cust-stat .d { font-size: 11px; color: var(--mud-palette-success); font-weight: 600; margin-top: 2px; }

.tpni-cust-sect { display: block; margin: 26px 0 2px; color: var(--mud-palette-text-secondary); font-weight: 700; letter-spacing: .09em; }
.tpni-cust-card { border-radius: var(--tpni-radius-card); }

/* Dashboard "Your Campaigns" rows (Nick 2026-07-21: campaigns are LISTED on the dashboard now,
   not just linked to). Same row rhythm as the Campaigns screen so the two read as one product. */
.tpni-cust-camprow { display:flex; align-items:center; gap:12px; padding:14px 18px;
                     border-bottom:1px solid var(--mud-palette-lines-default); }
.tpni-cust-camprow:last-child { border-bottom:0; }

.tpni-cust-nums { display: flex; gap: 26px; margin-top: 14px; flex-wrap: wrap; }
.tpni-cust-nums .n { font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; }
.tpni-cust-nums .l { font-size: 11px; color: var(--mud-palette-text-secondary); }
.tpni-cust-nums .d { font-size: 11px; color: var(--mud-palette-success); font-weight: 600; }

.tpni-cust-live { animation: tpni-cust-pulse 1.6s infinite; }
@keyframes tpni-cust-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }
@media (prefers-reduced-motion: reduce) { .tpni-cust-live { animation: none; } }

/* checklist rows */
.tpni-cust-step { display: flex; align-items: flex-start; gap: 14px; padding: 14px 20px; border-top: 1px solid var(--mud-palette-divider); }
.tpni-cust-step .mark { width: 26px; height: 26px; border-radius: 50%; flex-shrink: 0; display: grid; place-items: center; font-size: 13px; font-weight: 700; margin-top: 1px; }
.tpni-cust-step.done .mark { background: rgba(17,160,107,.14); color: var(--mud-palette-success); }
.tpni-cust-step.todo .mark { background: var(--mud-palette-background-gray); color: var(--mud-palette-text-secondary); border: 1.5px dashed var(--mud-palette-lines-inputs); }
.tpni-cust-step .st { flex: 1; min-width: 0; }
.tpni-cust-step .st b { font-size: 13.5px; display: block; }
.tpni-cust-step.done .st b { color: var(--mud-palette-text-secondary); }
.tpni-cust-step .st span { font-size: 12.5px; color: var(--mud-palette-text-secondary); display: block; margin-top: 2px; max-width: 62ch; }
.tpni-cust-step .act { flex-shrink: 0; align-self: center; }

/* organizer-new hero (brand v1: charcoal -> teal) */
.tpni-cust-hero { border-radius: var(--tpni-radius-card); background-image: linear-gradient(120deg, #232526 0%, #0A5560 62%, #0C7C84 100%); }

/* the visible-but-locked Campaign Launch teaser */
.tpni-cust-locked { border: 1px solid #D8DEE6; background: linear-gradient(135deg, #FBFCFC, #F1F5F6); }
.tpni-cust-locked .lockb { width: 26px; height: 26px; border-radius: 7px; background: var(--mud-palette-tertiary); color: #fff; display: grid; place-items: center; flex-shrink: 0; }

/* #151 LR dashboard — the "hot" live-leads KPI tile (rgba over surface = theme-safe) + the live spotlight */
.tpni-cust-stat.hot { background: rgba(232,89,12,.10); border: 1px solid rgba(232,89,12,.34); }
.tpni-cust-stat.hot .n { color: #E8590C; }
.tpni-cust-stat.hot .l .dot { color: #E8590C; font-size: 9px; margin-right: 3px; animation: tpni-cust-pulse 1.6s infinite; }
.tpni-spot { border: 1px solid var(--mud-palette-lines-default); }
.tpni-spot-nums { display: flex; gap: 44px; margin-top: 16px; flex-wrap: wrap; align-items: flex-end; }
.tpni-spot-nums .n { font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; }
.tpni-spot-nums .big .n { font-size: 40px; line-height: 1; letter-spacing: -0.02em; color: var(--mud-palette-primary); }
.tpni-spot-nums .l { font-size: 12px; color: var(--mud-palette-text-secondary); margin-top: 3px; }
.tpni-spot-nums .ago { font-size: 12px; color: var(--mud-palette-success); font-weight: 600; margin-top: 5px; }
@media (prefers-reduced-motion: reduce) { .tpni-cust-stat.hot .l .dot { animation: none; } }

/* ==== #151 — reusable wizard shell (WizardShell / WizardStep; docs/CM-WIZARD-SURFACES.md · S0) ==== */
.tpni-wiz-rail { display: flex; align-items: center; flex-wrap: wrap; gap: 2px; }
.tpni-wiz-main { display: block; }
.tpni-wiz-main.has-preview { display: grid; grid-template-columns: minmax(0, 1fr) minmax(280px, 380px); gap: 20px; align-items: start; }
.tpni-wiz-body { border-radius: var(--tpni-radius-card); }
.tpni-wiz-preview { position: sticky; top: 16px; }
.tpni-wiz-step { display: block; }
.tpni-wiz-foot { display: flex; align-items: center; gap: 8px; margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--mud-palette-lines-default); }
@media (max-width: 960px) {
    .tpni-wiz-main.has-preview { grid-template-columns: 1fr; }
    .tpni-wiz-preview { position: static; }
}

/* #151 · S2 — CM Setup hub cards + the booth-app qualifying-questions preview */
.tpni-setup-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(258px, 1fr)); gap: 14px; margin-top: 4px; }
.tpni-setup-card { transition: box-shadow .12s ease, transform .12s ease; }
.tpni-setup-card:hover { box-shadow: 0 6px 18px rgba(0,0,0,.10); transform: translateY(-1px); }
.tpni-setup-soon { opacity: .72; }
.tpni-booth { display: flex; justify-content: center; }
.tpni-booth-screen { width: 100%; max-width: 300px; border: 1px solid var(--mud-palette-lines-default); border-radius: 18px; background: var(--mud-palette-surface); padding: 12px; box-shadow: 0 4px 16px rgba(0,0,0,.08); }
.tpni-booth-head { font-weight: 700; font-size: 13px; padding: 2px 2px 10px; border-bottom: 1px solid var(--mud-palette-divider); margin-bottom: 6px; }
.tpni-booth-q { padding: 7px 2px; }
.tpni-booth-q .ql { font-size: 12.5px; font-weight: 600; }
.tpni-booth-q .qf { margin-top: 4px; font-size: 11.5px; color: var(--mud-palette-text-secondary); border: 1px dashed var(--mud-palette-lines-inputs); border-radius: 6px; padding: 5px 8px; }
.tpni-booth-q .qo { margin-top: 5px; display: flex; flex-wrap: wrap; gap: 5px; }
.tpni-booth-q .qo span { padding: 2px 8px; font-size: 10.5px; line-height: 1.5; border: 1px solid var(--mud-palette-lines-inputs); border-radius: 11px; color: var(--mud-palette-text-secondary); }

/* #151 (Nick 2026-07-08, "carry the color inward") — each module's colored home tile carries its
   accent into its inner landing so the inside matches the colored home page. Wrap a landing's cards
   in .tpni-accent-cards and set --card-accent / --card-accent2; the enabled setup-cards then get the
   module's gradient icon square + a matching top border, chevron and section label. "Coming soon"
   cards stay muted (only .setup-card--on is accented). */
.tpni-accent-cards .setup-card--on {
    border-top: 3px solid var(--card-accent, var(--mud-palette-primary));
}
.tpni-accent-cards .setup-card--on .mud-avatar {
    background-image: linear-gradient(135deg, var(--card-accent) 0%, var(--card-accent2, var(--card-accent)) 100%) !important;
    border-radius: 12px !important;
}
.tpni-accent-cards .setup-card--on .setup-card-go { color: var(--card-accent) !important; }
.tpni-accent-cards .section-label { color: var(--card-accent, var(--mud-palette-primary)); }

/* #151 ground-0 (Nick 2026-07-08): ONE console — the accent "Work on this campaign" tiles are
   shared by the customer Campaign home AND the staff Home picker, so staff and client see the same
   colorful doors. Promoted here from CampaignHome so both pages render identical tiles. */
/* Bigger tiles (Nick 2026-07-08): wider min column, more padding, larger accent icon square. */
.tpni-camp-tiles { display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); gap:16px; }
.tpni-camp-tile { cursor:pointer; padding:22px !important; min-height:104px;
                  transition:box-shadow .15s, transform .15s; border-top:3px solid var(--tile-accent, var(--mud-palette-primary)); }
.tpni-camp-tile:hover { box-shadow:0 2px 6px rgba(24,30,60,.06), 0 10px 22px rgba(24,30,60,.14); transform:translateY(-1px); }
.tpni-camp-tile.tpni-camp-tile--off { cursor:default; opacity:.6; }
.tpni-camp-tile.tpni-camp-tile--off:hover { box-shadow:none; transform:none; }
.tpni-tile-ic { width:54px; height:54px; border-radius:14px; flex-shrink:0; display:grid; place-items:center;
                box-shadow:0 3px 8px rgba(24,30,60,.18); }
.tpni-tile-ic .mud-icon-root { font-size:1.7rem; }
.tpni-camp-tile .mud-typography-subtitle1 { font-size:1.06rem; }

/* ---------- Webinar readiness rows (WebinarStepsList) ----------
   The WHOLE ROW is the link (Nick 2026-07-25 — it used to highlight on click and do nothing). The
   right-hand "Change / Set it up / Open" is a LABEL, not a nested button: an <a> inside an <a> is
   invalid and the parser hoists it out. These rules are what keep it READING as the control it is. */
.mud-list-item[href] .tpni-step-go { color:var(--mud-palette-primary); opacity:.85; transition:opacity .12s; }
.mud-list-item[href]:hover .tpni-step-go { opacity:1; text-decoration:underline; }
.mud-list-item[href] .tpni-step-go .mud-typography { font-size:.8125rem; letter-spacing:.02em; }

/* ---------- Journey spine (the registration-journey stepper on /event-setup/registration) ---------- */
.tpni-journey-spine { display:flex; flex-direction:column; }
.tpni-jstep { display:flex; gap:16px; align-items:stretch; }
.tpni-jstep__rail { display:flex; flex-direction:column; align-items:center; flex:0 0 auto; }
.tpni-jstep__node { width:40px; height:40px; border-radius:12px; display:grid; place-items:center; flex:0 0 auto;
                    border:1px solid color-mix(in srgb, var(--tpni-jtone,#6b7280) 35%, transparent);
                    background:color-mix(in srgb, var(--tpni-jtone,#6b7280) 14%, transparent); }
.tpni-jstep__node .mud-icon-root { color:var(--tpni-jtone,#6b7280); }
.tpni-jstep__line { width:2px; flex:1 1 auto; min-height:12px; background:var(--mud-palette-lines-default); margin:6px 0; }
.tpni-jstep:last-child .tpni-jstep__line { display:none; }
.tpni-jstep__card { flex:1 1 auto; margin-bottom:14px; }
.tpni-tone-editable  { --tpni-jtone:var(--mud-palette-success); }
.tpni-tone-managed   { --tpni-jtone:var(--mud-palette-info); }
.tpni-tone-attention { --tpni-jtone:var(--mud-palette-warning); }
.tpni-tone-bespoke   { --tpni-jtone:var(--mud-palette-text-secondary); }
.tpni-tone-muted     { --tpni-jtone:var(--mud-palette-text-secondary); }

/* Support board: fixed column widths so every stage-group table aligns */
.tpni-support-grid table { table-layout: fixed; width: 100%; }
.tpni-support-grid td, .tpni-support-grid th { overflow: hidden; }

/* Campaign-context strip in the customer drawer (Nick 2026-07-21: "you get lost whether you are in
   a campaign or not"). Two states, visually distinct at a glance: IN a campaign carries the primary
   accent bar and the campaign's name; OUT (the all-campaigns dashboard) stays flat and muted so the
   drawer never implies a scope the page doesn't have. */
.tpni-camp-ctx { margin:0 12px 6px; padding:9px 12px; border-radius:8px;
                 background:var(--mud-palette-gray-lighter); border-left:3px solid transparent; }
.tpni-camp-ctx.is-in { border-left-color:var(--mud-palette-primary);
                       background:color-mix(in srgb, var(--mud-palette-primary) 8%, transparent); }
/* SR-00206 E2: was a hard-coded #8B93A8 (~3.6:1) at 9.5px — small AND washed out, and it did not
   follow the theme into dark mode either. Use the token the rest of the app's quiet text uses. */
.tpni-camp-ctx-label { font-size:9.5px; letter-spacing:.09em; text-transform:uppercase;
                       color:var(--mud-palette-text-secondary); font-weight:700; }
.tpni-camp-ctx.is-in .tpni-camp-ctx-label { color:var(--mud-palette-primary); }
.tpni-camp-ctx-name { font-size:13px; font-weight:650; line-height:1.25; margin-top:2px;
                      color:var(--mud-palette-text-primary); overflow-wrap:anywhere; }
.tpni-camp-ctx-hint { font-size:11.5px; color:var(--mud-palette-text-secondary); margin-top:2px; }
.tpni-camp-ctx-link { display:inline-block; margin-top:5px; font-size:11.5px; font-weight:600;
                      color:var(--mud-palette-primary); text-decoration:none; }
.tpni-camp-ctx-link:hover { text-decoration:underline; }
