/* ===== Image library ===== */
.ui-refresh .image-library {
    max-width: 1440px;
    margin: 0 auto;
    padding: var(--space-6);
    color: var(--fg-default);
}
/* Inside the picker modal (App.loadModal(), web/js/app.js), the shared
   `.list-page` rule (below, later in the cascade — same specificity) wins
   the padding property and replaces this rule's intended 24px with its own
   `padding: var(--space-2) 0`. The apparent side "padding" people see instead
   comes from `.list-page`'s centered-column treatment (max-width 1240px,
   auto margins up to 40px), which doesn't line up with the modal header's
   fixed 24px (--space-6) padding — "Image library" ends up well right of
   "Choose Your Image". The modal already supplies a fixed-width frame, so
   drop the centering here and match the header's own edge exactly. Higher
   specificity (3 classes) wins outright — no reliance on source order. */
.ui-refresh .image-library.image-library--picker.list-page {
    width: auto;
    max-width: none;
    margin: 0;
    padding: var(--space-6);
}
.ui-refresh :is(.image-library .fileinput-button, .subpage-head .image-library-upload) {
    position: relative;
    overflow: hidden;
    margin: 0;
}
.ui-refresh :is(.image-library .fileinput-button, .subpage-head .image-library-upload) input {
    position: absolute;
    top: 0;
    right: 0;
    min-width: 100%;
    min-height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}
.ui-refresh :is(.image-library .list-toggle, .subpage-head .image-library-view-toggle) {
    display: inline-flex;
}
.ui-refresh :is(.image-library .list-toggle, .subpage-head .image-library-view-toggle) .btn {
    min-width: var(--btn-height);
    padding: 0 var(--space-3);
}
.ui-refresh :is(.image-library .list-toggle, .subpage-head .image-library-view-toggle) .btn + .btn {
    margin-left: calc(var(--space-1) * -1);
}
.ui-refresh :is(.image-library .list-toggle, .subpage-head .image-library-view-toggle) .btn.active {
    position: relative;
    z-index: 1;
    background: var(--accent-subtle) !important;
    border-color: var(--accent-primary) !important;
    color: var(--accent-primary);
}
.ui-refresh :is(.subpage-head, .list-intro-actions) .image-library-view-toggle {
    gap: 0;
    padding: var(--space-1);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    background: var(--bg-muted);
}
.ui-refresh :is(.subpage-head .setup-actions, .list-intro-actions) .image-library-view-toggle .btn {
    width: 30px;
    min-width: 30px;
    height: 30px !important;
    padding: 0;
    border: 0 !important;
    border-radius: var(--radius-sm) !important;
    background: transparent !important;
    color: var(--fg-muted);
    box-shadow: none !important;
}
.ui-refresh :is(.subpage-head, .list-intro-actions) .image-library-view-toggle .btn + .btn {
    margin-left: 0;
}
.ui-refresh :is(.subpage-head, .list-intro-actions) .image-library-view-toggle .btn:hover,
.ui-refresh :is(.subpage-head, .list-intro-actions) .image-library-view-toggle .btn:focus {
    background: var(--state-hover) !important;
    color: var(--fg-default);
}
.ui-refresh :is(.subpage-head, .list-intro-actions) .image-library-view-toggle .btn.active,
.ui-refresh :is(.subpage-head, .list-intro-actions) .image-library-view-toggle .btn.active:hover,
.ui-refresh :is(.subpage-head, .list-intro-actions) .image-library-view-toggle .btn.active:focus {
    background: var(--accent-subtle) !important;
    color: var(--accent-primary);
}
.ui-refresh .image-library-pixabay-logo {
    display: block;
    width: 90px;
    height: auto;
}
.ui-refresh :is(.image-library--picker .image-library-pixabay-logo, .file-library--picker .file-library-pixabay-logo) {
    height: var(--line-height-base);
    object-fit: contain;
}
.ui-refresh[data-surface="dark"] :is(.image-library--picker .image-library-pixabay-logo, .file-library--picker .file-library-pixabay-logo) {
    filter: brightness(0) invert(1);
    opacity: 0.78;
}
.ui-refresh .image-library #image-tabs {
    margin-bottom: var(--space-6);
}
.ui-refresh .image-library #image-tabs + .tab-content {
    padding: 0;
}
.ui-refresh .image-library #image-tabs-tab0 > .row {
    display: grid;
    grid-template-columns: minmax(220px, 260px) minmax(0, 1fr);
    gap: var(--space-5);
    margin: 0;
}
.ui-refresh .image-library #image-tabs-tab0 > .row > .col-sm-3,
.ui-refresh .image-library #image-tabs-tab0 > .row > .image-library-results {
    float: none;
    width: auto;
    padding: 0;
}
.ui-refresh .image-library #image-tabs-tab0 > .row > .col-sm-3 {
    grid-column: 1;
    grid-row: 1;
}
.ui-refresh .image-library #image-tabs-tab0 > .row > .image-library-results {
    grid-column: 2;
    grid-row: 1;
}
.ui-refresh .image-library .left-sidebar {
    width: 100% !important;
    padding: var(--space-5);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    box-shadow: var(--shadow-sm);
    --control-bg-color: var(--bg-page);
}
.ui-refresh .image-library .left-sidebar.affix {
    position: sticky;
    top: var(--space-4);
    left: auto;
}
.ui-refresh .image-library .left-sidebar .form-group:last-of-type {
    margin-bottom: var(--space-3);
}
.ui-refresh .image-library .image-library-advanced-search {
    margin-top: var(--space-1);
    overflow: hidden;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
}
.ui-refresh .image-library .left-sidebar #toggle-advanced {
    display: inline-flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    margin: 0;
    padding: var(--space-2) var(--space-3);
    border: 0;
    border-radius: 0;
    background: transparent;
    color: var(--fg-default);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    text-decoration: none;
    transition: border-color var(--transition-fast), background var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .image-library .left-sidebar #toggle-advanced:hover,
.ui-refresh .image-library .left-sidebar #toggle-advanced:focus {
    background: var(--accent-subtle);
    color: var(--accent-primary);
    text-decoration: none;
}
.ui-refresh .image-library .left-sidebar #toggle-advanced i {
    transition: transform var(--transition-fast);
}
.ui-refresh .image-library .left-sidebar #toggle-advanced:has(+ #advanced-search.in) i,
.ui-refresh .image-library .left-sidebar #toggle-advanced:has(+ #advanced-search.collapsing) i {
    transform: rotate(180deg);
}
.ui-refresh .image-library .left-sidebar .image-library-advanced-search-content {
    padding: var(--space-3);
    border-top: 1px solid var(--border-subtle);
    background: transparent;
}
.ui-refresh .image-library .image-library-advanced-search-content .input-group-addon {
    display: table-cell;
    vertical-align: middle;
    text-align: center;
    line-height: 1;
    padding-right: var(--space-2);
    padding-left: var(--space-2);
}
.ui-refresh .image-library .image-library-advanced-search-content > .row {
    margin-right: calc(var(--space-1) * -1);
    margin-left: calc(var(--space-1) * -1);
}
.ui-refresh .image-library .image-library-advanced-search-content > .row > .col-xs-6 {
    padding-right: var(--space-1);
    padding-left: var(--space-1);
}
.ui-refresh .image-library .left-sidebar .text-center {
    margin-top: var(--space-4);
    text-align: left;
}
.ui-refresh .image-library .left-sidebar .text-center br {
    display: none;
}
.ui-refresh .image-library .left-sidebar .text-center .btn {
    width: 100%;
}
.ui-refresh .image-library .left-sidebar .header,
.ui-refresh .image-library .image-library-results .header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin: var(--space-6) 0 var(--space-3);
    padding-bottom: var(--space-3);
    border-bottom: 1px solid var(--border-subtle);
    color: var(--fg-default);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-lg);
}
.ui-refresh .image-library .left-sidebar .header b,
.ui-refresh .image-library .image-library-results .header b {
    font-weight: inherit;
}
.ui-refresh .image-library .left-sidebar .header {
    min-height: 36px;
    margin-top: var(--space-5);
    padding-bottom: 0;
    border-bottom: 0;
}
.ui-refresh .image-library #create-folder {
    margin: 0;
    float: none;
    flex: 0 0 auto;
}
.ui-refresh .image-library .image-library-create-folder {
    width: 36px;
    height: 36px;
    min-width: 36px;
    padding: 0;
    border-color: transparent !important;
    background: transparent !important;
    color: var(--fg-muted);
    box-shadow: none !important;
}
.ui-refresh .image-library .image-library-create-folder:hover,
.ui-refresh .image-library .image-library-create-folder:focus {
    border-color: transparent !important;
    background: var(--accent-subtle) !important;
    color: var(--accent-primary);
}
.ui-refresh .image-library-results {
    padding: 0;
}
.ui-refresh .image-library-results #my-images {
    min-height: 0;
}
.ui-refresh .image-library-grid-footer {
    margin-top: var(--space-2);
}
.ui-refresh .image-library .image-library-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    min-height: 480px;
    padding: var(--space-8);
    border: 1px dashed var(--border-default);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    color: var(--fg-muted);
    text-align: center;
}
.ui-refresh .image-library .image-library-empty i {
    display: grid;
    width: 48px;
    height: 48px;
    place-items: center;
    margin-bottom: var(--space-4);
    border-radius: var(--radius-lg);
    background: var(--accent-subtle);
    color: var(--accent-primary);
    font-size: var(--font-size-xl);
}
.ui-refresh .image-library .image-library-empty h2 {
    margin: 0;
    color: var(--fg-default);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-lg);
}
.ui-refresh .image-library .image-library-empty p {
    max-width: 300px;
    margin: var(--space-2) 0 0;
    font-size: var(--font-size-sm);
    line-height: var(--line-height-sm);
}
.ui-refresh .image-library ul.image.folders,
.ui-refresh .image-library ul.image.folders ul {
    margin: 0 !important;
    padding: 0;
}
.ui-refresh .image-library ul.image.folders li {
    margin: 0 !important;
    padding: 0;
    list-style: none;
}
.ui-refresh .image-library ul.image.folders ul {
    padding-left: var(--space-4);
}
.ui-refresh .image-library .folder {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    width: 100%;
    margin: 0;
    padding: var(--space-2);
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    color: var(--fg-default);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}
.ui-refresh .image-library .folder:hover {
    margin: 0;
    border-color: transparent;
    background: transparent;
}
.ui-refresh .image-library .folder.active {
    background: var(--accent-subtle);
    color: var(--accent-primary);
    font-weight: var(--font-weight-semibold);
}
.ui-refresh .image-library .folder.folder-hover {
    border-color: var(--accent-primary);
    background: var(--accent-subtle);
    box-shadow: inset 0 0 0 1px var(--accent-primary);
    color: var(--accent-primary);
}
.ui-refresh .image-library .folder.folder-hover:hover #folder-edit-panel {
    display: none !important;
}
.ui-refresh .image-library .folder:has(#folder-edit-panel) {
    padding-right: calc(var(--space-2) + 60px);
}
.ui-refresh .image-library #folder-edit-panel {
    top: 50%;
    right: var(--space-1);
    gap: var(--space-1);
    align-items: center;
    transform: translateY(-50%);
}
.ui-refresh .image-library .folder:hover #folder-edit-panel {
    display: flex !important;
}
.ui-refresh .image-library .folder.folder-editing {
    display: block;
    position: relative;
    min-height: calc(var(--input-height) + var(--space-2) + var(--btn-height) + var(--space-6));
    padding: var(--space-3);
}
.ui-refresh .image-library .folder.folder-editing #folder-edit-panel {
    display: none !important;
}
.ui-refresh .image-library .folder.folder-editing .name {
    display: contents;
}
.ui-refresh .image-library .folder.folder-editing #folder-edit-input {
    display: flex !important;
    flex-direction: column;
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    left: var(--space-3);
    width: auto;
    margin: 0;
    gap: var(--space-2);
}
.ui-refresh .image-library .folder.folder-editing #folder-name {
    width: 100%;
}
.ui-refresh .image-library .folder.folder-editing .input-group-btn {
    display: flex;
    width: 100%;
    gap: 0;
}
.ui-refresh .image-library .folder.folder-editing .input-group-btn .btn {
    flex: 1 1 0;
    border-radius: 0;
}
.ui-refresh .image-library .folder.folder-editing .input-group-btn .btn-cancel {
    border-bottom-left-radius: var(--btn-radius);
    border-top-left-radius: var(--btn-radius);
}
.ui-refresh .image-library .folder.folder-editing .input-group-btn .btn-save {
    margin-left: -1px;
    border-bottom-right-radius: var(--btn-radius);
    border-top-right-radius: var(--btn-radius);
}
.ui-refresh .image-library #folder-edit-panel .btn {
    width: 28px;
    height: 28px;
    min-width: 28px;
    padding: 0;
    border-color: transparent !important;
    background: transparent !important;
    color: var(--fg-muted);
}
.ui-refresh .image-library #folder-edit-panel .btn:hover,
.ui-refresh .image-library #folder-edit-panel .btn:focus {
    border-color: var(--border-default) !important;
    background: var(--bg-muted) !important;
    color: var(--fg-default);
}
.ui-refresh .image-library .flex-images .item {
    box-sizing: border-box;
    overflow: hidden;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    background: var(--bg-muted);
    box-shadow: none;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.ui-refresh .image-library .draggable {
    cursor: grab;
}
.ui-refresh .image-library .ui-draggable-dragging {
    cursor: grabbing;
}
.ui-refresh .image-library .flex-images .item img {
    display: block;
    width: 100% !important;
    height: 100%;
    object-fit: cover;
}
.ui-refresh .image-library .flex-images .item:hover {
    border-color: var(--accent-primary);
    box-shadow: var(--sh-md);
}
.ui-refresh .image-library .flex-images .item.selected {
    border-color: var(--accent-primary);
    box-shadow: var(--focus-ring);
}
.ui-refresh .image-library .flex-images .item .over.name {
    max-height: none;
    padding: var(--space-5) var(--space-3) var(--space-2);
    background: linear-gradient(transparent, var(--overlay-bg));
    color: var(--fg-on-accent);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-xs);
    text-shadow: none;
}
.ui-refresh #upload-progress .inner {
    height: auto;
    max-height: 240px;
    padding: var(--space-4);
    overflow-y: auto;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    box-shadow: var(--shadow-sm);
}
.ui-refresh .image-library #upload-progress {
    margin-bottom: var(--space-6);
}
.ui-refresh #upload-msg {
    color: var(--fg-default);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}
.ui-refresh #close-upload-error {
    padding: var(--space-2);
    border: 1px solid var(--border-default);
    border-top: 0;
    background: var(--bg-muted);
}
.ui-refresh #image-menu {
    position: fixed;
    width: 240px;
    overflow: hidden;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    box-shadow: var(--sh-lg);
}
.ui-refresh #image-menu h4 {
    margin: 0;
    padding: var(--space-4) var(--space-5);
    border: 0;
    border-bottom: 1px solid var(--border-subtle);
    border-radius: 0;
    background: var(--bg-surface);
    color: var(--fg-default);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
}
.ui-refresh #image-menu .list-group-item {
    border-color: var(--border-subtle);
    background: var(--bg-surface);
    color: var(--fg-default);
    font-size: var(--font-size-sm);
}
.ui-refresh #image-menu .list-group-item:hover,
.ui-refresh #image-menu .list-group-item:focus {
    background: var(--accent-subtle);
    color: var(--accent-primary);
}
.ui-refresh #image-menu .close {
    margin: var(--space-2);
    color: var(--fg-muted);
    opacity: 1;
}
@media (max-width: 767px) {
    .ui-refresh .image-library {
        padding: var(--space-4);
    }
    .ui-refresh .subpage-head .image-library-upload {
        flex: 1 1 auto;
    }
    .ui-refresh .image-library .left-sidebar {
        margin-bottom: var(--space-5);
        /* The legacy affix plugin writes the desktop width inline after the
           Bootstrap columns stack, which otherwise creates mobile overflow. */
        width: 100% !important;
    }
    .ui-refresh .image-library #image-tabs-tab0 > .row {
        display: block;
    }
    .ui-refresh .image-library #image-tabs-tab0 > .row > .col-sm-3,
    .ui-refresh .image-library #image-tabs-tab0 > .row > .image-library-results {
        grid-column: auto;
        grid-row: auto;
    }
    .ui-refresh .image-library .left-sidebar.affix {
        position: static;
    }
    .ui-refresh .image-library-results {
        min-height: 0;
    }
    .ui-refresh .image-library .image-library-empty {
        min-height: 300px;
        padding: var(--space-6) var(--space-4);
    }
}

/* ===== File library ===== */
.ui-refresh .file-library {
    max-width: 1440px;
    margin: 0 auto;
    padding: var(--space-6);
    color: var(--fg-default);
}
/* Same cascade collision with the shared `.list-page` rule as .image-library
   above — see that comment for the full explanation. */
.ui-refresh .file-library.file-library--picker.list-page {
    width: auto;
    max-width: none;
    margin: 0;
    padding: var(--space-6);
}
.ui-refresh :is(.file-library .fileinput-button, .subpage-head .file-library-upload) {
    position: relative;
    overflow: hidden;
    margin: 0;
}
.ui-refresh :is(.file-library .fileinput-button, .subpage-head .file-library-upload) input {
    position: absolute;
    top: 0;
    right: 0;
    min-width: 100%;
    min-height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}
.ui-refresh :is(.file-library .list-toggle, .subpage-head .file-library-view-toggle) {
    display: inline-flex;
}
.ui-refresh :is(.file-library .list-toggle, .subpage-head .file-library-view-toggle) .btn {
    min-width: var(--btn-height);
    padding: 0 var(--space-3);
}
.ui-refresh :is(.subpage-head, .list-intro-actions) .file-library-view-toggle {
    gap: 0;
    padding: var(--space-1);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    background: var(--bg-muted);
}
.ui-refresh :is(.subpage-head .setup-actions, .list-intro-actions) .file-library-view-toggle .btn {
    width: 30px;
    min-width: 30px;
    height: 30px !important;
    padding: 0;
    border: 0 !important;
    border-radius: var(--radius-sm) !important;
    background: transparent !important;
    color: var(--fg-muted);
    box-shadow: none !important;
}
.ui-refresh :is(.subpage-head, .list-intro-actions) .file-library-view-toggle .btn + .btn {
    margin-left: 0;
}
.ui-refresh :is(.subpage-head, .list-intro-actions) .file-library-view-toggle .btn:hover,
.ui-refresh :is(.subpage-head, .list-intro-actions) .file-library-view-toggle .btn:focus {
    background: var(--state-hover) !important;
    color: var(--fg-default);
}
.ui-refresh :is(.subpage-head, .list-intro-actions) .file-library-view-toggle .btn.active,
.ui-refresh :is(.subpage-head, .list-intro-actions) .file-library-view-toggle .btn.active:hover,
.ui-refresh :is(.subpage-head, .list-intro-actions) .file-library-view-toggle .btn.active:focus {
    background: var(--accent-subtle) !important;
    color: var(--accent-primary);
}
.ui-refresh .file-library-pixabay-logo {
    display: block;
    width: 90px;
    height: auto;
}
.ui-refresh .file-library-warning {
    margin-bottom: var(--space-5);
}
.ui-refresh .file-library #file-tabs {
    margin-bottom: var(--space-6);
}
.ui-refresh .file-library #file-tabs + .tab-content {
    padding: 0;
}
.ui-refresh .file-library #file-tabs-tab0 > .row {
    display: grid;
    grid-template-columns: minmax(220px, 260px) minmax(0, 1fr);
    gap: var(--space-5);
    margin: 0;
}
.ui-refresh .file-library #file-tabs-tab0 > .row > .col-sm-3,
.ui-refresh .file-library #file-tabs-tab0 > .row > .file-library-results {
    float: none;
    width: auto;
    padding: 0;
}
.ui-refresh .file-library #file-tabs-tab0 > .row > .col-sm-3 {
    grid-column: 1;
    grid-row: 1;
}
.ui-refresh .file-library #file-tabs-tab0 > .row > .file-library-results {
    grid-column: 2;
    grid-row: 1;
}
.ui-refresh .file-library .left-sidebar {
    width: 100% !important;
    padding: var(--space-5);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    box-shadow: var(--shadow-sm);
    --control-bg-color: var(--bg-page);
}
.ui-refresh .file-library .left-sidebar.affix {
    position: sticky;
    top: var(--space-4);
    left: auto;
}
.ui-refresh .file-library .left-sidebar .form-group {
    margin-bottom: var(--space-3);
}
.ui-refresh .file-library .left-sidebar .btn {
    margin: 0;
}
.ui-refresh .file-library .left-sidebar form > .btn {
    width: 100%;
}
.ui-refresh .file-library .left-sidebar form > .btn + .btn {
    margin-top: var(--space-2);
}
.ui-refresh .file-library .left-sidebar .header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    min-height: 36px;
    margin: var(--space-5) 0 var(--space-3);
    border-bottom: 0;
    color: var(--fg-default);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-lg);
}
.ui-refresh .file-library .left-sidebar .header b {
    font-weight: inherit;
}
.ui-refresh .file-library #create-folder {
    margin: 0;
    float: none;
    flex: 0 0 auto;
}
.ui-refresh .file-library .file-library-create-folder {
    width: 36px;
    height: 36px;
    min-width: 36px;
    padding: 0;
    border-color: transparent !important;
    background: transparent !important;
    color: var(--fg-muted);
    box-shadow: none !important;
}
.ui-refresh .file-library .file-library-create-folder:hover,
.ui-refresh .file-library .file-library-create-folder:focus {
    border-color: transparent !important;
    background: var(--accent-subtle) !important;
    color: var(--accent-primary);
}
.ui-refresh .file-library-results {
    min-height: 0;
    padding: 0;
}
.ui-refresh .file-library-grid-footer {
    margin-top: var(--space-2);
}
.ui-refresh .file-library ul.file.folders,
.ui-refresh .file-library ul.file.folders ul {
    margin: 0 !important;
    padding: 0;
}
.ui-refresh .file-library ul.file.folders li {
    margin: 0 !important;
    padding: 0;
    list-style: none;
}
.ui-refresh .file-library ul.file.folders ul {
    padding-left: var(--space-4);
}
.ui-refresh .file-library .folder {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    width: 100%;
    margin: 0;
    padding: var(--space-2);
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    color: var(--fg-default);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}
.ui-refresh .file-library .folder:hover {
    margin: 0;
    border-color: transparent;
    background: transparent;
}
.ui-refresh .file-library .folder.active {
    background: var(--accent-subtle);
    color: var(--accent-primary);
    font-weight: var(--font-weight-semibold);
}
.ui-refresh .file-library .folder.folder-hover {
    border-color: var(--accent-primary);
    background: var(--accent-subtle);
    box-shadow: inset 0 0 0 1px var(--accent-primary);
    color: var(--accent-primary);
}
.ui-refresh .file-library .folder.folder-hover:hover #folder-edit-panel {
    display: none !important;
}
.ui-refresh .file-library .folder:has(#folder-edit-panel) {
    padding-right: calc(var(--space-2) + 60px);
}
.ui-refresh .file-library #folder-edit-panel {
    top: 50%;
    right: var(--space-1);
    gap: var(--space-1);
    align-items: center;
    transform: translateY(-50%);
}
.ui-refresh .file-library .folder:hover #folder-edit-panel {
    display: flex !important;
}
.ui-refresh .file-library .folder.folder-editing {
    display: block;
    position: relative;
    min-height: calc(var(--input-height) + var(--space-2) + var(--btn-height) + var(--space-6));
    padding: var(--space-3);
}
.ui-refresh .file-library .folder.folder-editing #folder-edit-panel {
    display: none !important;
}
.ui-refresh .file-library .folder.folder-editing .name {
    display: contents;
}
.ui-refresh .file-library .folder.folder-editing #folder-edit-input {
    display: flex !important;
    flex-direction: column;
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    left: var(--space-3);
    width: auto;
    margin: 0;
    gap: var(--space-2);
}
.ui-refresh .file-library .folder.folder-editing #folder-name {
    width: 100%;
}
.ui-refresh .file-library .folder.folder-editing .input-group-btn {
    display: flex;
    width: 100%;
    gap: 0;
}
.ui-refresh .file-library .folder.folder-editing .input-group-btn .btn {
    flex: 1 1 0;
    border-radius: 0;
}
.ui-refresh .file-library .folder.folder-editing .input-group-btn .btn-cancel {
    border-bottom-left-radius: var(--btn-radius);
    border-top-left-radius: var(--btn-radius);
}
.ui-refresh .file-library .folder.folder-editing .input-group-btn .btn-save {
    margin-left: -1px;
    border-bottom-right-radius: var(--btn-radius);
    border-top-right-radius: var(--btn-radius);
}
.ui-refresh .file-library #folder-edit-panel .btn {
    width: 28px;
    height: 28px;
    min-width: 28px;
    padding: 0;
    border-color: transparent !important;
    background: transparent !important;
    color: var(--fg-muted);
}
.ui-refresh .file-library #folder-edit-panel .btn:hover,
.ui-refresh .file-library #folder-edit-panel .btn:focus {
    border-color: var(--border-default) !important;
    background: var(--bg-muted) !important;
    color: var(--fg-default);
}
.ui-refresh .file-library .draggable {
    cursor: grab;
}
.ui-refresh .file-library .ui-draggable-dragging {
    cursor: grabbing;
}
.ui-refresh .file-library #my-files:not(:has(.grid-view)) {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
    gap: var(--space-3);
}
.ui-refresh .file-library #my-files > .empty {
    grid-column: 1 / -1;
}
.ui-refresh .file-library #my-files > .empty .file-library-empty {
    width: 100%;
}
.ui-refresh .file-library #my-files > .swoogo-file-preview {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 132px;
    margin: 0;
    padding: 0;
    overflow: hidden;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    background: var(--bg-muted);
    color: var(--fg-muted);
    box-shadow: none;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .file-library #my-files > .swoogo-file-preview:hover {
    border-color: var(--accent-primary);
    color: var(--accent-primary);
    box-shadow: var(--shadow-md);
}
.ui-refresh .file-library #my-files > .swoogo-file-preview.selected {
    border-color: var(--accent-primary);
    box-shadow: var(--focus-ring);
}
.ui-refresh .file-library .swoogo-file-preview .over {
    max-height: none;
    padding: var(--space-5) var(--space-3) var(--space-2);
    background: linear-gradient(transparent, var(--overlay-bg));
    color: var(--fg-on-accent);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-xs);
    text-shadow: none;
}
.ui-refresh .file-library .file-library-empty {
    display: flex;
    grid-column: 1 / -1;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    min-height: 480px;
    padding: var(--space-8);
    border: 1px dashed var(--border-default);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    color: var(--fg-muted);
    text-align: center;
}
.ui-refresh .file-library .file-library-empty i {
    display: grid;
    width: 48px;
    height: 48px;
    place-items: center;
    margin-bottom: var(--space-4);
    border-radius: var(--radius-lg);
    background: var(--accent-subtle);
    color: var(--accent-primary);
    font-size: var(--font-size-xl);
}
.ui-refresh .file-library .file-library-empty h2 {
    margin: 0;
    color: var(--fg-default);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-lg);
}
.ui-refresh .file-library .file-library-empty p {
    max-width: 300px;
    margin: var(--space-2) 0 0;
    font-size: var(--font-size-sm);
    line-height: var(--line-height-sm);
}
.ui-refresh #file-menu {
    position: fixed;
    width: 240px;
    overflow: hidden;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    box-shadow: var(--shadow-lg);
}
.ui-refresh #file-menu h4 {
    margin: 0;
    padding: var(--space-4) var(--space-5);
    border: 0;
    border-bottom: 1px solid var(--border-subtle);
    border-radius: 0;
    background: var(--bg-surface);
    color: var(--fg-default);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
}
.ui-refresh #file-menu .list-group-item {
    border-color: var(--border-subtle);
    background: var(--bg-surface);
    color: var(--fg-default);
    font-size: var(--font-size-sm);
}
.ui-refresh #file-menu .list-group-item:hover,
.ui-refresh #file-menu .list-group-item:focus {
    background: var(--accent-subtle);
    color: var(--accent-primary);
}
.ui-refresh #file-menu .close {
    margin: var(--space-2);
    color: var(--fg-muted);
    opacity: 1;
}
.ui-refresh .file-library #upload-progress .inner {
    height: auto;
    max-height: 240px;
    padding: var(--space-4);
    overflow-y: auto;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    box-shadow: var(--shadow-sm);
}
.ui-refresh .file-library #upload-progress {
    margin-bottom: var(--space-6);
}
.ui-refresh .file-library #upload-msg {
    color: var(--fg-default);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}
.ui-refresh .file-library #close-upload-error {
    padding: var(--space-2);
    border: 1px solid var(--border-default);
    border-top: 0;
    background: var(--bg-muted);
}
@media (max-width: 767px) {
    .ui-refresh .file-library {
        padding: var(--space-4);
    }
    .ui-refresh .subpage-head .file-library-upload {
        flex: 1 1 auto;
    }
    .ui-refresh .file-library .left-sidebar {
        width: 100% !important;
        margin-bottom: var(--space-5);
    }
    .ui-refresh .file-library #file-tabs-tab0 > .row {
        display: block;
    }
    .ui-refresh .file-library #file-tabs-tab0 > .row > .col-sm-3,
    .ui-refresh .file-library #file-tabs-tab0 > .row > .file-library-results {
        grid-column: auto;
        grid-row: auto;
    }
    .ui-refresh .file-library .left-sidebar.affix {
        position: static;
    }
    .ui-refresh .file-library .file-library-empty {
        min-height: 300px;
        padding: var(--space-6) var(--space-4);
    }
}

/* ===== Inline text links ===== */
.ui-refresh a:where(:not(.btn)):where(:not(.nav-item)) {
    color: var(--link-fg);
    transition: color var(--transition-fast), text-decoration-color var(--transition-fast);
}
.ui-refresh a:where(:not(.btn)):where(:not(.nav-item)):hover,
.ui-refresh a:where(:not(.btn)):where(:not(.nav-item)):focus {
    color: var(--link-fg-hover);
}
.ui-refresh .panel-swoogo-collapse .panel-title a {
    color: var(--fg-default);
}

/* ===== Panels (Cards) ===== */
/* Scoped to #content-container to avoid overriding the sidebar nav panels */
.ui-refresh :is(#content-container, #report-content) .panel {
    border: 1px solid var(--panel-border);
    border-radius: var(--panel-radius);
    box-shadow: var(--shadow-sm);
    background: transparent;
    /* .form-control reads background from --control-bg-color; declare the
       flipped value here so inputs inside panels get --bg-page (cream) against
       the white card surface instead of painting themselves transparent. */
    --control-bg-color: var(--bg-page);
}
.ui-refresh :is(#content-container, #report-content) .panel-heading {
    background: var(--bg-surface);
    border-bottom: 1px solid var(--border-subtle);
    font-weight: var(--font-weight-medium);
    font-size: var(--font-size-lg);
    padding: var(--space-3) 0;
    color: var(--fg-default);
}

/* ===== Flat panels (no card fill on dark surface tones) =====
   Opt-in modifier for panels that should keep their border/radius for
   structure but drop the card fill on dark surfaces, where the dark --card
   and --paper are close enough in value that a filled panel just reads as
   an unnecessary box-within-a-box. Identical to a plain .panel on light
   surfaces. See --panel-bg-flat / --panel-flat-control-bg in tokens.css. */
.ui-refresh :is(#content-container, #report-content) .panel.panel--flat-dark {
    background: var(--panel-bg-flat);
    --control-bg-color: var(--panel-flat-control-bg);
}
.ui-refresh :is(#content-container, #report-content) .panel.panel--flat-dark .panel-heading {
    background: var(--panel-bg-flat);
}

.ui-refresh #main-content .event-settings-doc [id^="event-categories"] .panel-heading {
    padding-left: 0;
    padding-right: 0;
    border: 0;
}

.ui-refresh :is(#content-container, #report-content) .panel-body {
    padding: var(--panel-padding);
}
.ui-refresh .intl-tel-input .country-list,
.ui-refresh .iti__country-list {
    background-color: var(--bg-surface);
    border: 1px solid var(--input-border);
    border-radius: var(--radius-md);
    box-shadow: var(--sh-md);
}
.ui-refresh .iti--inline-dropdown .iti__dropdown-content {
    border: none;
}
.ui-refresh .iti__dropdown-content {
    background-color: transparent;
}
.ui-refresh .intl-tel-input .country-list .divider,
.ui-refresh .iti__country-list .iti__divider {
    border-bottom: 1px solid var(--input-border);
}
.ui-refresh .intl-tel-input .country-list .country .dial-code,
.ui-refresh .iti__country .iti__dial-code {
    color: inherit;
}
.ui-refresh .intl-tel-input .country-list .country.active,
.ui-refresh .iti__country.iti__active {
    background-color: var(--accent-hover) !important;
    color: var(--accent-primary) !important;
}
.ui-refresh .intl-tel-input .country-list .country.highlight:not(.active),
.ui-refresh .iti__country.iti__highlight:not(.iti__active) {
    background-color: var(--bg-page) !important;
    color: var(--fg-default) !important;
}
.ui-refresh .krajee-default.file-preview-frame {
    border: none;
    box-shadow: none;
}

/* ===== Buttons ===== */
/* The legacy breadcrumb save button (breadcrumb-btn) uses position:fixed with
   hardcoded top/height/width from active-form.css. It and the newer
   breadcrumb-action-btn pattern MUST be excluded from all ui-refresh button
   overrides or they will overflow the breadcrumb bar. */
.ui-refresh .btn:not(.breadcrumb-btn, .fileinput-remove-button, .fileinput-upload-button, .fileinput-cancel-button, .event-stat-wrap, .daily-stat-wrap) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
/* .btn-block means full-width by contract (Bootstrap sets width: 100% at
   lower specificity), so it must be excluded from the shrink-wrap here. */
.ui-refresh .btn:not(.breadcrumb-btn, .input-group-addon, .btn-block) {
    width: fit-content;
}
.ui-refresh .btn:not(.breadcrumb-btn) {
    border-radius: var(--btn-radius);
    font-weight: var(--font-weight-medium);
    font-family: var(--font-family);
    height: var(--btn-height);
    padding: var(--btn-padding);
    font-size: var(--font-size-btn);
    transition: all var(--transition-fast);
    cursor: pointer;
    gap: var(--space-1);
    position: relative;
}
.ui-refresh .input-group-btn:last-child > .btn {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
}
.ui-refresh .btn:focus-visible {
    box-shadow: var(--focus-ring);
}
/* Suppress Bootstrap 3's blue `-webkit-focus-ring-color` outline that fires
   on mouse clicks (btn:focus / btn:active:focus). Keyboard users still get
   the :focus-visible ring above. */
.ui-refresh .btn:focus:not(:focus-visible),
.ui-refresh .btn:active:focus:not(:focus-visible),
.ui-refresh .btn.active:focus:not(:focus-visible) {
    outline: none;
}
/* Same for all anchors — suppresses the white/blue stroke on nav items after
   a mouse click while keeping keyboard focus outlines intact. */
.ui-refresh a:focus:not(:focus-visible) {
    outline: none;
}
/* Primary CTA follows the active accent (purple, amber, or user-chosen
   custom). This diverges from the v4 spec's editorial ink-fill direction
   in favor of bold accent-driven primaries — Save / Create / Continue
   inherit whatever color the user has picked in the accent toggle.
   !important is needed because backend.css declares .btn-primary's bg
   with !important using --swoogo-lavender-100. */
.ui-refresh .btn-primary {
    background: var(--primary) !important;
    border-color: var(--primary) !important;
    color: var(--fg-on-accent) !important;
}
.ui-refresh #content-container .btn-primary:hover,
.ui-refresh #content-container .btn-primary:focus,
.ui-refresh .btn-primary:hover {
    background: var(--primary-ink) !important;
    border-color: var(--primary-ink) !important;
    color: var(--fg-on-accent) !important;
}
.ui-refresh .btn-default,
.ui-refresh .new-form .btn.dropdown-toggle {
    background: var(--bg-page) !important;
    border-color: var(--panel-border) !important;
    color: var(--fg-default);
}
.ui-refresh .btn-default:hover {
    background: var(--bg-muted) !important;
}
/* Neutral Button (Filled) — design-language.md 8.1. Fills with the same
   surface color as an input (white on light surfaces, dark card on dark
   surfaces) so it reads as a solid control rather than the plain outlined
   .btn-default, without hardcoding a light-only fill. */
.ui-refresh .btn-neutral {
    background: var(--bg-surface) !important;
    border-color: var(--border-default) !important;
    color: var(--fg-default);
}
.ui-refresh .btn-neutral:hover {
    background: var(--bg-muted) !important;
}
/* Floating "Back to top" pill (widgets/views/ScrollToTopButton.php) — layout.css
   pins legacy lavender/pacifica colors directly on `.js-scrollbar #scrollable-btn`
   (ID-level specificity), which leaves the button reading as a navy-on-purple
   blob in both light and dark mode. Re-token to neutral surfaces and add a
   subtle elevation; bump specificity with body.ui-refresh so we beat the
   plain-id legacy rule. */

/* Position the wrapper in the bottom-right region of the viewport — the
   modern docs-page convention (MDN, GitHub, Stripe, Linear). layout.css
   sets `right: 40%` which only worked in the legacy col-lg-9 layout where
   40%-from-right landed in the empty 25% sidebar gutter; under the new
   full-width content it parks the pill in the middle of form fields.
   Specificity bumps via body.ui-refresh to beat `.js-scrollbar`'s legacy
   rule. */
body.ui-refresh .js-scrollbar {
    /* `right: var(--space-4)` (16px) parks the FAB's right edge at
       viewport-16, which visually overlaps the scrollbar's rightmost
       ~12-18px lane (#main-content's overflow-y:auto track). Bump to
       --space-8 so the pill clears the scroll track with comfortable
       breathing room — the FAB still reads as bottom-right anchored,
       just not crowding the scrollbar. */
    right: var(--space-8);
    bottom: var(--space-4);
}
/* On pages with the centered .event-settings-doc, anchor the button next
   to the content column instead of letting it float in the far-right
   gutter. The calc() resolves to "distance from viewport's right edge to
   the doc's right edge, minus the pill's 150px width, minus a small gap"
   — landing the pill's LEFT edge ~12px outside the doc's right edge, so
   it visually pairs with the content/TOC column. max() falls back to the
   corner-safe value above on narrow viewports where the calc would push
   the button past the right edge.
   The 150px tracks the literal width of `#scrollable-btn` (set in
   layout.css:735); if that pill ever resizes, update both spots. */
body.ui-refresh:has(.event-settings-doc) .js-scrollbar {
    right: max(
        var(--space-4),
        calc((100vw - var(--doc-chrome-width) - var(--doc-max-width)) / 2 - 150px - var(--space-3))
    );
}
body.ui-refresh .js-scrollbar #scrollable-btn {
    background: var(--bg-surface);
    border-color: var(--border-default);
    color: var(--fg-default);
    box-shadow: var(--shadow-sm);
}
body.ui-refresh .js-scrollbar #scrollable-btn:hover {
    background: var(--bg-muted);
    border-color: var(--border-strong);
    color: var(--fg-default);
}

.ui-refresh .btn-sm:not(.breadcrumb-btn) {
    height: 30px;
    padding: 0 var(--space-3);
    font-size: var(--font-size-xs);
}
/* Event Fees' Save button (.fee-save-btn) carries .breadcrumb-btn only to
   pick up interactive-grid.js's saving-spinner swap during autosave (it
   targets `.breadcrumb-btn` globally) — it is not the legacy fixed-position
   breadcrumb button, so re-apply the ui-refresh button layout the rules
   above exclude for `.breadcrumb-btn`. */
.ui-refresh .fee-save-btn.breadcrumb-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: fit-content;
    border-radius: var(--btn-radius);
    font-weight: var(--font-weight-medium);
    font-family: var(--font-family);
    height: var(--btn-height);
    padding: var(--btn-padding);
    font-size: var(--font-size-btn);
    transition: all var(--transition-fast);
    cursor: pointer;
    gap: var(--space-1);
}

/* interactive-grid.js's "creating a row" scrim (initCreateButtons: adds
   .loading, then .show 250ms later, to the nearest [data-pjax-container]
   while the create POST + pjax reload are in flight) hardcodes a white
   overlay in backend.css — flashes white over the grid on dark surfaces
   (e.g. creating a meeting time slot or location). Swap in a surface-aware
   scrim so it blends instead of flashing. */
.ui-refresh[data-surface="dark"] .loading[data-pjax-container]::before {
    background: color-mix(in srgb, var(--bg-surface) 70%, transparent);
}
.ui-refresh .btn:disabled,
.ui-refresh .btn.disabled {
    opacity: var(--state-disabled-opacity);
    cursor: not-allowed;
    pointer-events: none;
}

.ui-refresh .go-attend-page-settings .sidebar-collapse-stack {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

/* ===== Form Controls ===== */
.ui-refresh .form-control {
    height: var(--input-height);
    border: 1px solid var(--input-border);
    border-radius: var(--input-radius) !important;
    padding: var(--input-padding);
    font-family: var(--font-family), sans-serif;
    font-size: var(--font-size-input);
    color: var(--fg-default);
    background-color: var(--card, transparent) !important;
    box-shadow: none;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.ui-refresh .input-group-btn.input-group-append .btn {
    border-bottom-left-radius: 0;
    border-top-left-radius: 0;
    height: var(--input-height) !important;
}
.ui-refresh .has-success.highlight-addon .input-group-btn .btn:not(:disabled):not(.disabled),
.ui-refresh .form-group.has-success .input-group-btn .btn-default:hover,
.ui-refresh .form-group.has-success .input-group-btn .btn-default:hover {
    color: inherit !important;
    background-color: inherit !important;
    border-color: var(--input-border) !important;
}
.ui-refresh .file-preview {
    border-color: var(--input-border);
}
.ui-refresh .intl-tel-input,
.ui-refresh .iti {
    width: 100%;
}
.ui-refresh .contact-reconcile {
    display: flex;
    gap: var(--space-4);
    margin-bottom: var(--space-5);
}
.ui-refresh .reconcile-column {
    flex: 1 0 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}
.ui-refresh .reconcile-card {
    flex: 1 1 auto;
    padding: var(--space-4);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
}
.ui-refresh .reconcile-action {
    text-align: center;
}
.ui-refresh .reconcile-card-title {
    margin-bottom: var(--space-3);
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--panel-border);
    font-size: var(--font-size-sm);
    font-weight: 600;
    color: var(--fg-default);
}
.ui-refresh .reconcile-field + .reconcile-field {
    margin-top: var(--space-3);
}
.ui-refresh .reconcile-field-label {
    margin-bottom: var(--space-1);
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
}
.ui-refresh .reconcile-field-value {
    font-size: var(--font-size-sm);
    color: var(--fg-default);
    word-break: break-word;
}
.ui-refresh .reconcile-field-value.is-empty {
    color: var(--fg-subtle);
    font-style: italic;
}
.ui-refresh .reconcile-field-value img {
    max-width: 100%;
    height: auto;
    border-radius: var(--radius-md);
}
.ui-refresh .kv-fileinput-caption {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}
.ui-refresh .form-control:focus,
.ui-refresh .twitter-typeahead .tt-input:focus {
    border: 1px solid var(--accent-primary) !important;
    box-shadow: var(--focus-ring) !important;
    outline: none !important;
}
.ui-refresh .form-control::placeholder,
.ui-refresh .tagify__input::before,
.ui-refresh .tagify__input:focus:empty::before {
    color: var(--fg-subtle);
}
.ui-refresh .form-control:disabled,
.ui-refresh .form-control[readonly] {
    background-color: var(--bg-muted);
    color: var(--fg-muted);
    cursor: not-allowed;
}

.ui-refresh .new-form .input-group-addon {
    background: transparent;
}
.ui-refresh .new-form .input-group .twitter-typeahead .tt-input {
    border-radius: 0 var(--input-radius) var(--input-radius) 0 !important;
}

.ui-refresh .input-group .form-control:first-child {
    border-top-right-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
}

/* ===== Account Logo Upload (Basic Details) =====
   Drag-and-drop uploader mirroring the profile avatar uploader
   (profile-upload.css), but with a rectangular logo box + neutral placeholder
   — a company logo isn't a person, so no circular crop / initials / silhouette.
   Preview, drag/drop and clear are handled by account-logo-upload.js; the file
   submits with the form as Account[image_to_upload]. */
.ui-refresh .account-logo-dropzone {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: var(--space-6);
    border: 1px dashed var(--border-default);
    border-radius: var(--radius-xl);
    background: var(--bg-page);
    transition: background var(--transition-fast), border-color var(--transition-fast);
}
.ui-refresh .account-logo-dropzone.drag-over {
    background: var(--accent-subtle);
    border-color: var(--accent-primary);
}
.ui-refresh .account-logo-dropzone-text {
    color: var(--fg-muted);
    font-family: var(--font-family);
    font-size: var(--font-size-label);
    font-weight: var(--font-weight-semibold);
    letter-spacing: var(--letter-space-label);
    text-transform: uppercase;
}
.ui-refresh .account-logo-preview {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 260px;
    height: 130px;
    margin: 0 auto var(--space-4);
    padding: var(--space-3);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    overflow: hidden;
}
.ui-refresh .account-logo-img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}
.ui-refresh .account-logo-placeholder {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    color: var(--fg-subtle);
    font-family: var(--font-family);
    font-size: var(--font-size-sm);
}
.ui-refresh .account-logo-placeholder i {
    font-size: var(--space-8);
}
/* Choose Image overlay — shown on hover only while the box is empty. */
.ui-refresh .account-logo-choose-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-lg);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--transition-fast);
}
.ui-refresh .account-logo-preview:not(.has-custom-image):hover .account-logo-choose-overlay {
    opacity: 1;
    pointer-events: auto;
}
.ui-refresh .account-logo-choose-overlay-bg {
    position: absolute;
    inset: 0;
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    opacity: 0.85;
}
.ui-refresh .account-logo-choose-overlay .btn {
    position: relative;
    z-index: 1;
}
/* Remove button — shown on hover only while an image is present. */
.ui-refresh .account-logo-remove {
    position: absolute;
    top: var(--space-2);
    right: var(--space-2);
    z-index: 1;
    width: 32px;
    height: 32px;
    border-radius: var(--radius-full);
    border: 1px solid var(--border-default);
    background: var(--bg-surface);
    color: var(--fg-default);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    cursor: pointer;
    opacity: 0;
    pointer-events: none;
    transition: background var(--transition-fast), opacity var(--transition-fast);
}
.ui-refresh .account-logo-preview.has-custom-image:hover .account-logo-remove {
    opacity: 1;
    pointer-events: auto;
}
.ui-refresh .account-logo-remove:hover {
    background: var(--bg-muted);
}

.ui-refresh .form-group,
.ui-refresh .ajax-drawer .user-input .form-group:last-child,
.ui-refresh .ajax-drawer .toggle-group.collect_cvc,
.ui-refresh .ajax-drawer .cs-soap-fields .form-group:last-child,
.ui-refresh .ajax-drawer .cs-rest-fields .form-group:last-child {
    margin-bottom: var(--space-5);
}
.ui-refresh .form-control-static {
    padding-top: var(--space-1);
}
/* Override active-form.css .new-form indentation for static fields */
.ui-refresh .new-form .form-control-static {
    padding-left: 0;
}
.ui-refresh .new-form div:not(.checkbox, .radio, .btn-group) > .form-control-static {
    padding-left: 0;
}
.ui-refresh .form-group .multiselect-native-select .multiselect-container li label {
    padding-left: 1.5rem;
}
/* Override active-form.css:194 — the legacy `.new-form` UI indents every label
   by 4px, which leaves the label text visually offset from the input's left
   border. Match the culprit's selector with `.ui-refresh` prepended so we win
   on specificity (0,0,4,2 > 0,0,3,2) and align labels with their inputs. */
.ui-refresh .new-form div:not(.checkbox, .radio, .btn-group) > label:not(.radio-inline):not(.lang-card):not([class*='-color-chip']) {
    padding-left: 0;
}
/* active-form.css sets padding-left: 4px on .new-form labels — restore the full
   card padding for lang-card labels using a selector that beats that rule. */
.ui-refresh .new-form .lang-card-grid > label.lang-card {
    padding-left: var(--space-8);
}
/* Plain checkboxes inherit legacy backend.css colors. Keep these defaults at
   legacy specificity so the toggle, chip and other component skins still win. */
:where(.ui-refresh) input[type="checkbox"] {
    border-color: var(--border-strong);
    background-color: var(--bg-surface);
}
:where(.ui-refresh) input[type="checkbox"]:active:not(:disabled),
:where(.ui-refresh) input[type="checkbox"]:not(:disabled):hover {
    border-color: var(--accent-primary);
}
:where(.ui-refresh) input[type="checkbox"]:checked {
    background-color: var(--accent-primary);
    border-color: var(--accent-primary);
}
:where(.ui-refresh) input[type="checkbox"]:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

/* ===== Chip Picker (checkboxList multi-select) ===== */
/* When a container holds 2+ .checkbox children (Yii2 checkboxList output),
   render them as selectable pill/chip toggles in a wrapping flex layout. */
.ui-refresh div:has(> .checkbox + .checkbox) {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}
.ui-refresh div:has(> .checkbox + .checkbox) > .checkbox {
    padding: 0;
    border-bottom: none;
    margin: 0;
}
.ui-refresh div:has(> .checkbox + .checkbox) > .checkbox label {
    display: inline-flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    width: auto;
    cursor: pointer;
    margin: 0;
    padding: 10px var(--space-4);
    padding-left: var(--space-4) !important; /* override .new-form .checkbox label !important */
    border-radius: var(--radius-full);
    border: 1px solid var(--input-border);
    background: var(--control-bg-color);
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    line-height: normal;
    transition: all var(--transition-fast);
    white-space: nowrap;
}
.ui-refresh div:has(> .checkbox + .checkbox) > .checkbox label:hover {
    border-color: var(--accent-primary);
    color: var(--accent-primary);
}
/* Hide the native checkbox */
.ui-refresh div:has(> .checkbox + .checkbox) > .checkbox input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    width: 0;
    height: 0;
    margin: 0;
    padding: 0;
    border: none;
    position: absolute;
    opacity: 0;
}
/* Checkmark icon via ::before on the label — hidden by default */
.ui-refresh div:has(> .checkbox + .checkbox) > .checkbox label::before {
    content: '\f00c'; /* fa-check */
    font-family: 'Font Awesome 6 Pro';
    font-weight: 400;
    font-size: var(--font-size-xs);
    display: none;
}
/* Selected chip state */
.ui-refresh div:has(> .checkbox + .checkbox) > .checkbox:has(input:checked) label {
    background: var(--accent-subtle);
    border-color: var(--accent-primary);
    color: var(--accent-on-tint);
}
.ui-refresh div:has(> .checkbox + .checkbox) > .checkbox:has(input:checked) label::before {
    display: inline;
}
.ui-refresh div:has(> .checkbox + .checkbox) > .checkbox label::after {
    content: none;
}

.ui-refresh .chip-picker-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
    margin-bottom: var(--space-3);
}
.ui-refresh .chip-picker-head .control-label {
    margin-bottom: 0;
}
.ui-refresh .chip-picker-actions {
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-2);
    flex-shrink: 0;
}
.ui-refresh .chip-picker-action {
    padding: 0;
    border: 0;
    background: none;
    color: var(--fg-muted);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    cursor: pointer;
    transition: color var(--transition-fast);
}
.ui-refresh .chip-picker-action:hover {
    color: var(--accent-primary);
}
.ui-refresh .chip-picker-sep {
    color: var(--border-strong);
    font-size: var(--font-size-xs);
}

/* ===== Toggle Switches (standalone checkboxes) ===== */
/* CSS-only: restyle single checkboxes inside .checkbox wrappers as toggle switches.
   Only applies when NOT inside a multi-checkbox group (chip picker handles those). */
.ui-refresh .checkbox input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    width: 36px;
    height: 20px;
    background: var(--border-default);
    /* Legacy active-form.css sets a 0.5px hardcoded-blue border on
       .new-form input[type="checkbox"]:checked. Zero it out so the toggle
       pill renders as a clean shape that picks up only --accent-primary. */
    border: 0;
    border-radius: var(--radius-full);
    position: relative;
    cursor: pointer;
    transition: background var(--transition-fast);
    vertical-align: middle;
    margin: 0;
    flex-shrink: 0;
}
.ui-refresh .checkbox input[type="checkbox"]::before {
    content: '';
    position: absolute;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: white;
    top: 50%;
    left: 3px;
    transform: translateY(-50%);
    transition: transform var(--transition-fast), left var(--transition-fast);
}
.ui-refresh .checkbox input[type="checkbox"]:checked {
    background: var(--accent-primary);
}
.ui-refresh .new-form input[type=checkbox]:checked {
    background-color: var(--accent-primary);
    border-color: var(--accent-primary);
}
.ui-refresh .checkbox input[type="checkbox"]:checked::before {
    left: calc(100% - 17px);
}
/* Toggle rows — compact, left-aligned with form fields */
.ui-refresh .checkbox {
    padding: 0;
    border-bottom: none;
    margin: 0;
}
/* Form-groups wrapping checkboxes don't need the full 20px bottom margin.
   Also collapse nested .form-group wrappers around checkboxes (some views
   wrap $form->field() in an extra <div class="form-group">). */
.ui-refresh .form-group:has(> .checkbox),
.ui-refresh .form-group:has(> .form-group > .checkbox) {
    margin-bottom: var(--space-1);
}
.ui-refresh .form-group > .form-group:has(> .checkbox) {
    margin-bottom: 0;
}
.ui-refresh .checkbox:last-child {
    border-bottom: none;
}
/* Natural DOM order: toggle on left, label text on right — aligns with form fields.
   padding-left reset overrides active-form.css .new-form .checkbox label { padding-left: 20px !important } */
.ui-refresh .checkbox label {
    display: flex;
    align-items: center;
    cursor: pointer;
    margin: 0;
    padding-left: 0 !important;
    gap: var(--space-2);
}
/* Bootstrap Multiselect renders label.checkbox rows inside its dropdown. Reset the
   global toggle treatment there so these stay standard checklist controls. */
.ui-refresh .multiselect-container .checkbox {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    margin: 0;
    cursor: pointer;
    font-size: var(--font-size-sm);
    font-weight: 400;
    color: var(--fg-default);
}
.ui-refresh .multiselect-container .checkbox input[type="checkbox"] {
    appearance: auto;
    -webkit-appearance: checkbox;
    width: 16px;
    min-width: 16px;
    height: 16px;
    margin: 0;
    background: transparent;
    border-radius: 0;
    position: static;
    vertical-align: middle;
    flex-shrink: 0;
    accent-color: var(--accent-primary);
}
.ui-refresh .multiselect-container .checkbox input[type="checkbox"]::before,
.ui-refresh .multiselect-container .checkbox input[type="checkbox"]::after {
    content: none;
    display: none;
}

.ui-refresh .layout {
    color: var(--fg-subtle);
    font-size: var(--font-size-xs);
}

.ui-refresh .layout.selected {
    color: var(--fg-default);
    font-weight: var(--font-weight-semibold);
}

.ui-refresh .layout-bg:hover,
.ui-refresh .selected .layout-bg {
    box-shadow: var(--sh-md);
}

.ui-refresh .merge-fields-footer {
    background-color: transparent;
    border: none;
    margin-bottom: 0;
    border-top: 1px solid var(--input-border);
}

.ui-refresh .merge-fields-footer .footer-items {
    display: flex;
}

.ui-refresh .merge-fields-footer .footer-items .item {
    border: none;
    display: flex;
    align-items: center;
}

.ui-refresh .merge-fields-footer .footer-items .item:has(.translations, .google-translate) {
    flex: 1;
    justify-content: flex-end;
}

.ui-refresh .translations-input .merge-fields-footer {
    border-radius: 0 !important;
}

.ui-refresh .translations-input .item .lang-name {
    color: var(--primary-ink);
    width: fit-content !important;
}

.ui-refresh .help-block {
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
    margin-bottom: 0;
    margin-top: var(--space-1);
}
/* Empty error containers (help-block-error) take up margin space even when empty.
   Collapsing them prevents layout shift when AJAX validation injects/clears content.
   !important is required because jQuery .show() leaves a stale inline style="display:block"
   on the element after $error.empty() clears the content. */
.ui-refresh .help-block:empty {
    display: none !important;
}
.ui-refresh .has-error .form-control {
    border-color: var(--color-error);
}
.ui-refresh .has-error .help-block {
    color: var(--color-error);
}
/* Some fields (e.g. the slug availability check on Event Website URL) surface a
   validation message in .help-block-error without toggling .has-error on the
   container, so the message inherited the muted .help-block color and the input
   kept its normal border. Make a populated error block read as an error — text
   and input border — while excluding the transient "validating" spinner state.
   Two spinner states exist: the keystroke/blur path adds .validating to the
   form-group, but on form submit Yii never adds that class and app.js still
   injects the "Checking availability" .fa-spinner into .help-block-error — so
   also exclude a block whose only content is that spinner, otherwise every
   Save flashes the field red until the ajax response clears it. */
.ui-refresh .form-group:not(.validating) .help-block-error:not(:empty):not(:has(.fa-spinner)) {
    color: var(--color-error);
}
/* Plain fields carry their border on .form-control; input-group fields (e.g.
   the URL slug) carry the visible border on the .input-group wrapper and its
   addon, with the inner .form-control border:none. Turn all of them red. */
.ui-refresh .form-group:not(.validating):has(.help-block-error:not(:empty)):not(:has(.help-block-error .fa-spinner)) .form-control,
.ui-refresh .form-group:not(.validating):has(.help-block-error:not(:empty)):not(:has(.help-block-error .fa-spinner)) .input-group,
.ui-refresh .form-group:not(.validating):has(.help-block-error:not(:empty)):not(:has(.help-block-error .fa-spinner)) .input-group-addon {
    border-color: var(--color-error) !important;
}

/* ===== Input Group Addon ===== */
.ui-refresh .input-group-addon {
    background-color: var(--control-bg-color) !important;
    border-color: var(--input-border);
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
    transition: border-color var(--transition-fast), background-color var(--transition-fast), color var(--transition-fast);
}
/* .form-control sets border-radius with !important (see Form Controls above),
   which overrides Bootstrap's input-group corner-flattening — the input keeps
   all four corners rounded, so a leading/trailing addon (e.g. the "Minutes"
   tail on the timeout field, or the URL field's https:// / .swoogo.com) can't
   sit flush. Re-flatten the input's inner corners and normalize the addon's
   outer corners to the same radius so the group reads as one pill. */
.ui-refresh .input-group .form-control:first-child {
    border-top-right-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
}
.ui-refresh .input-group .form-control:last-child {
    border-top-left-radius: 0 !important;
    border-bottom-left-radius: 0 !important;
}
.ui-refresh .input-group .form-control:not(:first-child):not(:last-child) {
    border-radius: 0 !important;
}
.ui-refresh .input-group-addon:first-child {
    border-top-left-radius: var(--input-radius);
    border-bottom-left-radius: var(--input-radius);
}
.ui-refresh .input-group-addon:last-child {
    border-top-right-radius: var(--input-radius);
    border-bottom-right-radius: var(--input-radius);
}
/* Currency / unit addons (General::getCurrencySymbolAddOn) wrap the symbol in
   a <label>. base.css's generic `.form-group label` rule turns that label into
   a block with a 4px bottom margin and label typography, which lifts the "$"
   off the input's vertical centre. Put it back to inline text so Bootstrap's
   table-cell addon can centre it. padding needs !important to beat
   active-form.css's `.new-form .label-add-on .input-group-addon label`. */
.ui-refresh .form-group .input-group-addon label {
    display: inline;
    width: auto;
    margin: 0;
    padding: 0 !important;
    font-size: inherit;
    font-weight: inherit;
    line-height: 1;
    color: inherit;
}
/* active-form.css indents #transaction-form hints by 50% to clear the legacy
   horizontal label column. The ui-refresh transaction form is vertical, so
   the "Balance Due Today" hint should sit under the input like any other. */
.ui-refresh #transaction-form .hint-block {
    margin-left: 0;
}

/* ===== AJAX Validation States ===== */
/* Hide Bootstrap feedback icons — they cause content shift during validation */
.ui-refresh .form-control-feedback {
    display: none !important;
}
/* Prevent Bootstrap's .has-feedback from adding padding-right to inputs,
   which causes text shift when validation state changes. */
.ui-refresh .has-feedback .form-control {
    padding-right: var(--space-3);
}
/* Validating: accent border + inline spinner, no content shift.
   app.js injects "Checking availability..." into .help-block with .show() (inline style).
   !important is required to override the inline display:block from jQuery .show(). */
.ui-refresh .validating .form-control {
    border-color: var(--accent-primary);
}
.ui-refresh .validating .input-group-addon {
    border-color: var(--accent-primary);
}
/* Hide help-blocks containing a spinner (injected by app.js "Checking availability").
   :has(.fa-spinner) catches both keystroke validation AND form submission validation,
   since the .validating class is only added in the keystroke/blur path. */
.ui-refresh .help-block:has(.fa-spinner) {
    display: none !important;
}
.ui-refresh .validating .help-block {
    display: none !important;
}
/* CSS spinner inside the input-group during validation. position:absolute on the
   ::after removes it from the table layout so it overlays the input's right edge. */
.ui-refresh .validating .input-group {
    position: relative;
}
.ui-refresh .validating .input-group::after {
    content: '';
    position: absolute;
    right: 12px;
    top: 50%;
    width: 14px;
    height: 14px;
    margin-top: -7px;
    border: 2px solid var(--border-subtle);
    border-top-color: var(--accent-primary);
    border-radius: 50%;
    animation: ui-refresh-spin 0.6s linear infinite;
    z-index: 3;
    pointer-events: none;
}
/* When the input-group ends in an addon (e.g. the URL field's `.swoogo.com`
   append), the generic ::after — offset from the input-group's right edge —
   lands on top of that addon. Suppress it there and anchor the spinner to the
   input's right edge instead, which is the trailing addon's left edge. */
.ui-refresh .validating .input-group:has(> .input-group-addon:last-child)::after {
    display: none;
}
.ui-refresh .validating .input-group > .input-group-addon:last-child {
    position: relative;
}
.ui-refresh .validating .input-group > .input-group-addon:last-child::before {
    content: '';
    position: absolute;
    right: calc(100% + 12px);
    top: 50%;
    width: 14px;
    height: 14px;
    margin-top: -7px;
    border: 2px solid var(--border-subtle);
    border-top-color: var(--accent-primary);
    border-radius: 50%;
    animation: ui-refresh-spin 0.6s linear infinite;
    z-index: 3;
    pointer-events: none;
}
@keyframes ui-refresh-spin {
    to { transform: rotate(360deg); }
}
/* Success: no visual change — modern forms only highlight errors, not valid fields.
   Reset to default border so fields don't turn green after every blur. */
.ui-refresh .has-success .form-control {
    border-color: var(--input-border);
    box-shadow: none;
}
.ui-refresh .has-success .form-control:focus {
    border-color: var(--accent-primary);
    box-shadow: var(--focus-ring);
}
.ui-refresh .has-success .input-group-addon {
    border-color: var(--input-border);
    background-color: var(--bg-muted);
    color: var(--fg-muted);
}
/* Error addon styling to match */
.ui-refresh .has-error .input-group-addon {
    border-color: var(--color-error);
}
.ui-refresh .has-error.highlight-addon .input-group-btn .btn-default:not(:disabled):not(.disabled),
.ui-refresh .has-error.highlight-addon .input-group-btn .btn-default:not(:disabled):not(.disabled):hover {
    color: var(--color-error);
    background-color: var(--color-error-bg) !important;
    border-color: var(--color-error) !important;
}
/* Hint blocks (manual hint text, not Yii help-block) */
.ui-refresh .hint-block {
    font-size: var(--font-size-xs);
    color: var(--fg-subtle);
    margin-top: var(--space-1);
    line-height: var(--line-height-xs);
}
/* Inside the account form the field .form-group already supplies the vertical
   rhythm (margin-bottom: --space-5). The Default Currency field's hint adds a
   second --space-5 below itself, opening a gap before Account Logo that's
   larger than between the other fields — zero it so the spacing matches. */
.ui-refresh .account-form .hint-block {
    margin-bottom: 0;
}
/* Account settings sections (e.g. Policies) — logical groups that flow flat on
   the page bg like the Basic Details form, separated by a heading rather than a
   card, per the v4 "forms float on the body" direction.

   Spacing hierarchy: sections are separated by --space-8 (32px) while fields
   inside a section share a tighter, uniform --space-4 (16px) rhythm, so the
   grouping reads clearly without cards. */
.ui-refresh .account-section + .account-section,
.ui-refresh .account-section .account-mapping {
    margin-top: var(--space-8);
}
/* CFS Sign-up form's "New user role" section sits directly above the
   field-builder grid — but the grid is wrapped in _field-builder-ui-refresh's
   `Pjax::begin(['id' => 'account-questions'])`, so that wrapper div (not
   .rf-page) is the actual sibling here. A tad tighter than the standard
   --space-8 section gap: this boundary is section-to-grid, not
   section-to-section. */
.ui-refresh .account-section + #account-questions {
    margin-top: var(--space-6);
}
/* SpotMe's "Changing Workspace" section is injected via AJAX into an
   #changing-workspace-info div sitting between two .account-section siblings
   (spot-me-workspace/link/index.php), so the adjacent-sibling rule above never
   matches — the wrapper div breaks the direct-sibling relationship. Give the
   injected section the same top margin directly. */
.ui-refresh #changing-workspace-info .account-section {
    margin-top: var(--space-8);
}
/* When a section leads the form (no hidden id field before it, e.g. Analytics),
   give it a top margin so the page header gets the same breathing room that the
   leading hidden field's form-group lends Basic Details / Policies. Collapses
   with the .list-intro bottom margin, so the header→content gap matches. */
.ui-refresh .account-form > .account-section:first-child {
    margin-top: var(--space-9);
}
.ui-refresh .account-section-heading {
    font-family: var(--font-family);
    font-size: 16px;
    line-height: 1.2;
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
    margin: 0 0 var(--space-4);
}
/* When a description follows the heading, tighten the heading→description gap;
   the description then owns the uniform gap down to the fields. */
.ui-refresh .account-section-heading:has(+ .account-section-desc) {
    margin-bottom: var(--space-2);
}
.ui-refresh .account-section-desc {
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
    line-height: 1.55;
    margin: 0 0 var(--space-4);
}
/* Uniform field rhythm within a section — slightly tighter than the default
   --space-5 form-group gap so fields read as a group. Scoped to .account-section
   so the single-group Basic Details form keeps its own spacing. */
.ui-refresh .account-section .form-group {
    margin-bottom: var(--space-4);
}
.ui-refresh .account-section .form-group:last-child {
    margin-bottom: 0;
}
/* Fields that suppress their label with ->label('') (e.g. Favicon, Swoogo
   Virtual Logo image pickers) still emit an empty <label> whose height + margin
   opens an unwanted gap under the section heading. Collapse it. */
.ui-refresh .account-section .control-label:empty {
    display: none;
}
/* A single select that is its section's only field (e.g. CFS Sign-up form's
   "New user role") otherwise stretches to fill .list-page's full 1240px
   measure — cap it to a sane reading width instead. */
.ui-refresh .cfs-role-field .form-control {
    max-width: 320px;
}
/* Sets of related switches (MFA authentication methods, Shared Reports) are
   grouped into bordered .toggle-group cards — see the shared toggle-group rules
   below, which own their row spacing/dividers. */
/* When a field-style label introduces a toggle-group (e.g. MFA "Available
   authentication methods", a custom field's "Visibility" toggles), sit the
   group as tight to the label as a field label sits to its input
   (--space-1), instead of the toggle-group's default --space-5 top margin. */
.ui-refresh .form-label:has(+ .toggle-group) {
    margin-bottom: var(--space-1);
}
.ui-refresh .form-label + .toggle-group {
    margin-top: 0;
}
/* `.ajax-drawer .panel-body` is a flex column with its own --space-3 (12px)
   gap between direct children — that gap stacks on top of the label's own
   margin-bottom above (12px + 4px = 16px) since flex `gap` isn't affected by
   a child's margin. Cancel the gap entirely here so the net label-to-group
   distance matches the non-flex `.account-section` case (just the label's
   own --space-1 margin) — don't touch the group's own row padding to close
   this gap instead, or the first row loses the symmetric top/bottom padding
   the other rows have and its content sits off-center relative to them. */
.ui-refresh .ajax-drawer .form-label + .toggle-group {
    margin-top: calc(-1 * var(--space-3));
}
/* Adjacent .virtual-settings blocks in the event Where section sit flush
   together by default — give them the same 20px rhythm as form-groups. */
.ui-refresh .event-settings-content .virtual-settings + .virtual-settings {
    margin-top: var(--space-5);
}
/* Reset Address sits at the bottom of #location-settings inside its own
   .form-group, whose default 20px margin-bottom stacks with whatever
   spacing the AddressInput rows above add and produces a chasm before
   the Swoogo Virtual Link callout. Zero it out so the callout sits at a
   tight, intentional distance from the address grid. */
.ui-refresh .event-settings-content #location-settings .form-group:has(.reset-location) {
    margin-bottom: 0;
}
/* Popover info icons — muted by default, accent on hover.
   The icon sits inside an <a> trigger; size that <a> to match the icon
   so the focus ring draws as a tight circle on it.

   Scoped to :has(.help-message) so only the default info-circle popover
   (emitted by General::popover() with default linkText) gets the icon-sized
   box. Text-content popovers (EmailBlast "View other conditions", Submission
   "Some Contacts", etc.) and custom-icon popovers (integration warning
   triangles) fall through to native anchor sizing. */
.ui-refresh a[data-toggle="popover"]:has(.help-message) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: var(--font-size-xs);
    width: 1em;
    height: 1em;
    line-height: 1;
    border-radius: 50%;
    outline-offset: 3px;
    vertical-align: middle;
}
.ui-refresh .help-message {
    color: var(--fg-subtle);
    font-size: var(--font-size-xs);
    transition: color var(--transition-fast);
}
/* backend.css sets .help-message { min-width: 25px } which overflows the icon
   inside the 1em popover anchor and offsets the focus ring. Scope the reset
   to the popover context so standalone .help-message usages keep their gutter. */
.ui-refresh a[data-toggle="popover"] .help-message {
    min-width: 0;
}
.ui-refresh .help-message:hover {
    color: var(--accent-primary);
}
/* ===== Segmented Control (radio button group) ===== */
/* Restyle Bootstrap btn-group radio toggles into segmented controls per design spec */
.ui-refresh .segmented-control {
    display: inline-flex !important;
    width: fit-content !important;
    table-layout: auto;
    align-items: center;
    justify-content: flex-start;
    background: var(--bg-surface) !important;
    border: 1px solid var(--input-border) !important;
    border-radius: 10px !important;
    padding: 3px !important;
    gap: 2px !important;
}
.ui-refresh .segmented-control .btn {
    border: none !important;
    border-radius: 7px !important;
    background: transparent !important;
    color: var(--fg-muted) !important;
    font-weight: 500 !important;
    font-size: 13px !important;
    padding: 6px 14px !important;
    /* Bootstrap defaults `label { margin-bottom: 5px }` — this trails the pill
       inside its flex item and pushes the track taller than the visible pill,
       making the active pill appear top-aligned. Zero it out. */
    margin: 0 !important;
    height: auto !important;
    line-height: 1.5 !important;
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    transition: background var(--transition-fast), color var(--transition-fast) !important;
    box-shadow: none !important;
    /* Inter ships proportional figures by default, so `1` is ~3px narrower than
       `0/2/3` and each pill sizes to its own digit — the row of numbers lands on
       an uneven rhythm and the active pill changes width as you click through.
       Tabular figures give every digit the same advance; min-width keeps the
       pills square-ish if a fallback face has no tabular set. */
    font-variant-numeric: tabular-nums;
    min-width: 36px;
}
/* The radio input rendered inside each label by Html::radio can contribute
   baseline/ghost space even when visually unstyled. Remove it from layout. */
.ui-refresh .segmented-control input[type="radio"] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
    width: 0;
    height: 0;
    margin: 0;
}
.ui-refresh .segmented-control .btn:hover {
    background: transparent !important;
    color: var(--fg-default);
}
/* Chain .btn-group.segmented-control to beat the generic .btn-group .btn-default.active
   override on specificity — same-specificity ties lose because that rule
   is defined later. Needed because radioButtonGroup renders both classes on the same
   element: <div class="btn-group segmented-control">. */
.ui-refresh .btn-group.segmented-control .btn.active,
.ui-refresh .btn-group.segmented-control .btn-default.active,
.ui-refresh .btn-group.segmented-control .btn-default.active:hover,
.ui-refresh .btn-group.segmented-control .btn-default.active:focus {
    background: var(--fg-default) !important;
    color: var(--bg-page) !important;
    border-color: transparent !important;
}
.ui-refresh .btn-group.segmented-control .btn:has(> input[type="radio"]:checked) {
    background: var(--accent-primary) !important;
    color: var(--bg-page) !important;
    border-color: transparent !important;
}
/* Keyboard focus for the segmented control. Gate to :has(input:focus-visible)
   so only keyboard navigation draws the ring, not mouse clicks. */
.ui-refresh .btn-group.segmented-control .btn:has(input:focus-visible),
.ui-refresh .btn-group.segmented-control .btn-default.active:has(input:focus-visible) {
    box-shadow: var(--focus-ring) !important;
    outline: none;
}

/* ===== Justified button group (radio toggles) =====
   Bootstrap's .btn-group-justified relies on `display: table` + `display:
   table-cell` on each .btn to give equal-width buttons. Ui-refresh's global
   .btn { display: inline-flex } override (line ~26) breaks the table-cell
   layout, leaving buttons stacked at content width with overlapping icons
   (visible in the swoogo_link Link Settings form). Restore equal-width
   layout via a flex-row instead of resurrecting the legacy table layout. */
.ui-refresh .btn-group-justified {
    display: flex !important;
    width: 100%;
    gap: var(--space-1);
}
.ui-refresh .btn-group-justified > .btn,
.ui-refresh .btn-group-justified > .btn-group {
    flex: 1 1 0;
    min-width: 0;
    width: auto !important;
}
.ui-refresh .btn-group-justified > .btn-group > .btn {
    width: 100%;
}

/* ===== Tables ===== */
.ui-refresh .table {
    border-collapse: separate;
    border-spacing: 0;
}
.ui-refresh .table > thead > tr > th {
    font-weight: var(--font-weight-semibold);
    color: var(--fg-muted);
    border-bottom: 1px solid var(--border-default);
    padding: var(--space-3) var(--space-4);
}
/* Sortable-column header links (Yii2 DataColumn wraps the label in <a>) should
   read exactly like a plain header label, not an accent-colored link — this
   was one more contributor to the review's "too many accent colors" complaint.
   !important: this table can be nested inside .rd-profile .panel/.rd-card__body,
   whose own "table a { color: accent }" rule (4 classes) otherwise outranks this
   one (2 classes) regardless of source order. */
.ui-refresh .table > thead > tr > th a {
    color: inherit !important;
}
/* Report DataTables (AjaxDataTables) ship their own header chrome — the
   DataTables/Bootstrap default column-title colour (#343a40) and sort glyphs
   are dark-on-dark and effectively invisible on dark surfaces. Re-point the
   titles to --fg-default and the sort affordance to --muted via tokens so
   reports stay legible in every surface tone (and unchanged on light, where
   --fg-default ≈ the old #343a40). .comm-report keeps its own muted-uppercase
   header via higher specificity, so it's unaffected. */
.ui-refresh table.dataTable > thead > tr > th,
.ui-refresh table.dataTable > thead > tr > td {
    color: var(--fg-default);
}
.ui-refresh table.dataTable > thead .sorting:after,
.ui-refresh table.dataTable > thead .sorting_asc:after,
.ui-refresh table.dataTable > thead .sorting_desc:after,
.ui-refresh table.dataTable > thead .sorting_asc_disabled:after,
.ui-refresh table.dataTable > thead .sorting_desc_disabled:after {
    color: var(--muted);
    opacity: 1;
}
.ui-refresh .table > tbody > tr > td {
    padding: var(--space-3) var(--space-4);
    /* Bootstrap draws a raw #ddd border-TOP per cell; we row-separate with a
       tokenized border-bottom instead. Left in place, the #ddd top border
       doubles every separator and reads as a bright white line on dark. */
    border-top: 0;
    border-bottom: 1px solid var(--border-subtle);
    vertical-align: middle;
}
/* `.table-hover` fills the row itself with an opaque #f5f5f5, which shows
   straight through the translucent overlay below and reads as a white flash on
   dark paper. The overlay owns hover; clear the fill underneath it. */
.ui-refresh .table.table-hover > tbody > tr:hover {
    background-color: transparent;
}
.ui-refresh .table > tbody > tr:hover > td {
    background-color: var(--state-hover);
}
.ui-refresh .table-striped > tbody > tr:nth-of-type(odd) {
    background-color: transparent;
}
.ui-refresh .table .filters {
    background: var(--bg-page);
}

/* floatThead grids (GridView with floatThead:true, e.g. scan-location/index).
   The GridView component injects `.floatthead-grid { background:#FFF }` so the
   sticky cloned header is opaque — but that paints the whole table white, and
   since the cells are transparent the body reads as a white block on dark
   surfaces. Override the background with a surface token at a higher
   specificity (the component's rule is left intact for legacy). The
   .floatThead-container is the runtime-injected fixed wrapper for the floated
   header; pin it to the same surface. All tokenised, so light is unchanged. */
.ui-refresh .floatthead-grid {
    background: var(--bg-surface);
}
.ui-refresh .floatThead-container {
    background: var(--bg-surface);
}

/* ===== Alerts ===== */
.ui-refresh .alert {
    border-radius: var(--radius-lg);
    border: 1px solid;
    font-size: var(--font-size-sm);
    padding: var(--space-3) var(--space-4);
}
.ui-refresh .alert-success {
    background-color: var(--color-success-bg);
    border-color: var(--color-success);
    color: var(--color-success);
}
.ui-refresh .alert-danger {
    background-color: var(--color-error-bg);
    border-color: var(--color-error);
    color: var(--color-error);
}
.ui-refresh .alert-warning {
    background-color: var(--color-warning-bg);
    border: 1px solid color-mix(in srgb, var(--color-warning) 30%, transparent);
    color: var(--fg-default);
    border-radius: var(--radius-lg);
    font-size: var(--font-size-sm);
    padding: var(--space-4) var(--space-5);
    line-height: 1.5;
}
/* Replace the widget's fa-flag icon with fa-triangle-exclamation via CSS,
   and size it up so it anchors the alert visually. */
.ui-refresh .alert-warning .fa-flag::before {
    content: '\f071';
}
.ui-refresh .alert-warning .alert-container > div:first-child i {
    font-size: var(--font-size-xl);
    color: var(--color-warning);
}
.ui-refresh .alert-warning .alert-link {
    color: var(--accent-primary);
    font-weight: var(--font-weight-medium);
    text-decoration: none;
}
.ui-refresh .alert-warning .alert-link:hover {
    text-decoration: underline;
}
.ui-refresh .alert-info {
    background-color: var(--color-info-bg);
    border-color: var(--color-info);
    color: var(--color-info);
}
/* Survey schedule drawer: the "reusable account survey" note. Keep it inset
   like the panels below (no edge bleed) with clean, even padding. */
.ui-refresh .sidebar-body > .alert.sv-template-note {
    padding: var(--space-3);
}

.ui-refresh .alert > .close {
    color: currentColor;
    opacity: 0.72;
    text-shadow: none;
    transition: opacity var(--transition-fast);
}
.ui-refresh .alert > .close:hover,
.ui-refresh .alert > .close:focus {
    color: currentColor;
    opacity: 1;
}
.ui-refresh .alert p:last-child,
.ui-refresh .alert ul:last-child {
    margin-bottom: 0;
}
.ui-refresh .alert i {
    margin-right: var(--space-1);
}

/* ===== Inline alerts (InlineAlert widget) =====
   Left-bordered, tinted message box with a leading status icon. Four tones:
   default (accent), ok (success), warn (warning), danger (error). The icon
   color tracks the tone. See app\widgets\InlineAlert. */
/* Subtle throughout: a neutral card background with just a colored left
   stripe + icon + action button carrying the tone. No variant class (the
   "testing" state) stays fully neutral rather than defaulting to the accent
   color, which read as an unintentional green/purple tint depending on the
   account's accent choice. */
.ui-refresh .inline-alert {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--input-border);
    border-left: 3px solid var(--border-strong);
    border-radius: var(--radius-md);
    background: var(--card);
    color: var(--fg-default);
    font-size: var(--font-size-input);
}
.ui-refresh .inline-alert.mb {
    margin-bottom: var(--space-4);
}
.ui-refresh .inline-alert.mt {
    margin-top: var(--space-4);
}
/* InlineAlert is also used as a validation-error banner that starts hidden
   via Bootstrap's .collapse (e.g. the automation drawer's empty error box on
   load) — the unconditional `display: flex` above beats .collapse's plain
   `display: none` since it loads later in the cascade, so the empty alert
   renders instead of staying hidden until .in is added. */
.ui-refresh .inline-alert.collapse:not(.in) {
    display: none;
}
.ui-refresh .inline-alert__icon {
    color: var(--fg-muted);
    line-height: 1;
}
.ui-refresh .inline-alert__icon i {
    font-size: var(--font-size-lg);
}
.ui-refresh .inline-alert__body strong {
    font-weight: var(--font-weight-semibold);
}
/* Validation-error banner (automation drawer) injects <p>/<ul> lists into the
   body — trim their default margins so the alert stays compact. */
.ui-refresh .inline-alert__body p {
    margin: 0 0 var(--space-1);
}
.ui-refresh .inline-alert__body p:last-child,
.ui-refresh .inline-alert__body ul {
    margin-bottom: 0;
}
.ui-refresh .inline-alert__body ul {
    padding-left: var(--space-5);
}
.ui-refresh .inline-alert.ok {
    border-left-color: var(--color-success);
}
.ui-refresh .inline-alert.ok .inline-alert__icon {
    color: var(--color-success);
}
.ui-refresh .inline-alert.warn {
    border-left-color: var(--color-warning);
}
.ui-refresh .inline-alert.warn .inline-alert__icon {
    color: var(--color-warning);
}
.ui-refresh .inline-alert.danger {
    border-left-color: var(--color-error);
}
.ui-refresh .inline-alert.danger .inline-alert__icon {
    color: var(--color-error);
}
/* Links inside tinted alert variants inherit the global accent (purple), which
   has poor contrast on the warm/green/red backgrounds. Tint each variant's
   links to its own accent and underline them so they stay distinguishable. */
.ui-refresh .inline-alert__body a {
    font-weight: var(--font-weight-medium);
    text-decoration: underline;
}
.ui-refresh .inline-alert.ok .inline-alert__body a {
    color: var(--color-success);
}
.ui-refresh .inline-alert.warn .inline-alert__body a {
    color: var(--color-warning);
}
.ui-refresh .inline-alert.danger .inline-alert__body a {
    color: var(--color-error);
}

/* Guidance variant — a multi-line list of helper notes rather than a single
   message. The base inline-alert vertically centers its icon (right for one
   line); anchor it to the first line here and match the icon to the body text
   size so they align. The list gets token-based rhythm in place of browser
   defaults. */
.ui-refresh .inline-alert-guidance ul {
    margin: 0;
    padding-left: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}
/* Right-aligned action slot (e.g. the webhook Test / Stop test button). */
.ui-refresh .inline-alert__action {
    margin-left: auto;
    flex: none;
    display: flex;
    align-items: center;
    gap: var(--space-2);
}
/* Neutral button in every banner state — now that the banner itself is a
   quiet neutral card (no colored fill), a solid neutral surface with full-
   strength ink text reads as a proper button; a transparent/muted-text
   ghost blended into the card and looked washed out. */
.ui-refresh .inline-alert__action .btn:not(.breadcrumb-btn) {
    height: 32px;
    padding: 0 var(--space-3);
    background: var(--bg-page) !important;
    border-color: var(--border-strong) !important;
    color: var(--fg-default);
}
.ui-refresh .inline-alert__action .btn:not(.breadcrumb-btn):hover {
    background: var(--bg-muted) !important;
    color: var(--fg-default);
}

.ui-refresh .sidebar-body .inline-alert {
    margin-bottom: var(--space-3);
}

/* Account-form pages (e.g. the Stripe full-page gateway form) render fields
   directly under an .account-section heading rather than inside
   .sidebar-body, so they miss the rule above — add the same breathing room
   before whatever follows the alert (a checkbox list, a toggle-group). */
.ui-refresh .account-form .inline-alert {
    margin-bottom: var(--space-3);
}

/* Enablement banner at the top of a record-detail profile (webhook view):
   the flex gap alone reads tight, add a nudge below the banner. */
.ui-refresh .rd-profile > .inline-alert {
    margin-bottom: var(--space-2);
}

/* ===== Sync status note (plain text) =====
   Routine, expected sync state ("will be imported in a few minutes", "synced
   again at {date}") isn't a thing the user needs to act on or notice, so it
   doesn't get the boxed .inline-alert treatment — that's reserved for errors
   and warnings. This is a quiet icon + muted text line instead. */
.ui-refresh .sync-status-note {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0;
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
}
.ui-refresh .sync-status-note i {
    flex: none;
}

/* ===== Modals ===== */
.ui-refresh .modal-content,
body.iframe-modal.ui-refresh {
    background-color: var(--bg-page) !important;
}
.ui-refresh .modal-content iframe {
    margin-bottom: 0;
}
.ui-refresh #sidebar-modal .modal-body {
    flex: 1 0 0;
}

.ui-refresh .modal-content {
    /* Bootstrap hardcodes `.modal-content { background-color: #fff; color:#333 }`,
       which leaks a white dialog in dark mode. Surface/ink tokens flip. */
    background: var(--bg-surface);
    color: var(--fg-default);
    border-radius: var(--radius-xl);
    border: 1px solid var(--panel-border);
    box-shadow: var(--shadow-xl);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
body.iframe-modal.ui-refresh .credential-picker-label,
body.iframe-modal.ui-refresh .credential-picker-list {
    padding: 0 var(--space-6);
}
/* SpotMe's small "Saving" indicator (spot-me-workspace/_saving.php) has no
   inner padded wrapper of its own — unlike the other modals above, which own
   their content's padding via their own scoped rules. */
.ui-refresh #loading-modal .modal-body {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-4) var(--space-5);
    font-size: var(--font-size-sm);
}
.ui-refresh #loading-modal .modal-body .fa-spinner {
    color: var(--accent-primary);
}
.ui-refresh #delete_confirmation .modal-content .inline-alert,
.ui-refresh #email-form .list-page .inline-alert {
    margin-bottom: var(--space-5);
}
.ui-refresh .modal-header {
    border-bottom: 1px solid var(--panel-border);
    padding: var(--space-4) var(--space-6);
}
.ui-refresh .modal-header .modal-title {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--fg-default);
    margin: 0;
    line-height: 32px; /* Match close button height for vertical alignment */
}
.ui-refresh .modal-header .modal-title i {
    color: var(--fg-muted);
    margin-right: var(--space-2);
}
/* Image and file libraries render inside App.loadModal() across the product.
   Their picker-specific class is assigned from the URL in app.js so the
   simplified dialog chrome does not alter unrelated iframe modals. */
.ui-refresh .modal.library-picker-modal .modal-content {
    border: 0;
    box-shadow: var(--sh-md);
}
.ui-refresh .modal.library-picker-modal .modal-header {
    border-bottom: 0;
}
.ui-refresh.library-picker-modal-open .modal-backdrop.in {
    opacity: 0.64;
}
.ui-refresh .modal-footer {
    background-color: var(--bg-page);
    border-top: 1px solid var(--panel-border);
    padding: var(--space-4) var(--space-6);
}
.ui-refresh .modal-message {
    padding: var(--space-4) var(--space-6);
}

/* ===== Hotel details popup (hotel/show-details.php) =====
   Iframed via Hotel::getMoreInfoUrl -> App.loadModal — the modal chrome
   Rendered inside a UiConfirmDialog.openContent() dialog (see
   hotel-details-dialog.js), whose chrome (title bar, close button, paper
   card) is already styled above — only this content was ever fully
   unstyled. Single stacked column instead of the legacy two-column grid,
   which left a blank half-width column whenever there were no photos or
   website. */
.ui-refresh .hotel-details {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}
.ui-refresh .hotel-details-address {
    font-size: var(--font-size-sm);
    line-height: var(--line-height-md);
    color: var(--ink);
}
.ui-refresh .hotel-details-address address {
    margin: 0;
    font-style: normal;
}
/* No fill: the dialog itself already paints --bg-surface, so a matching fill
   here would show only a border with no contrast. The border alone still
   frames the rows as a distinct group. */
.ui-refresh .hotel-details .rd-kv {
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    padding: 0 var(--space-4);
    overflow: hidden;
}
.ui-refresh .hotel-details .rd-kv__key i {
    margin-right: var(--space-1);
    color: var(--muted);
}
.ui-refresh .hotel-details-media {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}
.ui-refresh .hotel-details-media #myCarousel {
    border-radius: var(--radius-lg);
    overflow: hidden;
}

/* ===== Send email (registrant/send-email.php) =====
   Designer emails preview in a raw <iframe width="100%" height="400">; give
   it the card edge the rest of the page uses instead of a bare browser frame. */
.ui-refresh .email-preview-frame {
    display: block;
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
}

/* GitHub integration help is a long-form, instructional modal. Keep its
   supporting details grouped so the required repository structure and token
   permission remain easy to scan without changing the shared modal chrome. */
.ui-refresh .github-help-modal .modal-dialog {
    max-width: 800px;
}
.ui-refresh .github-help-modal.in {
    align-items: center;
    display: flex !important;
    justify-content: center;
    padding: var(--space-4);
}
.ui-refresh .github-help-modal.in .modal-dialog {
    margin: 0;
}
.ui-refresh .github-help-modal .modal-content {
    background-color: var(--bg-surface) !important;
    border: 0;
    box-shadow: none;
}
.ui-refresh .github-help-modal .modal-header {
    border-bottom: 0;
    padding: var(--space-6) var(--space-6) var(--space-2);
}
.ui-refresh .github-help-modal .modal-body {
    max-height: calc(100vh - 176px);
    overflow-y: auto;
    padding: var(--space-2) var(--space-6) var(--space-6);
}
.ui-refresh .github-help-modal .modal-footer {
    background: transparent;
    border-top: 0;
    padding: 0 var(--space-6) var(--space-4);
    text-align: right;
}
.ui-refresh .github-help-modal__footer-divider {
    border-top: 1px solid var(--border-subtle);
    margin-bottom: var(--space-4);
}
.ui-refresh .github-help-modal .modal-footer .pull-right {
    float: right;
}
.ui-refresh .github-help-modal__body {
    display: grid;
    gap: var(--space-6);
}
.ui-refresh .github-help-modal__section + .github-help-modal__section {
    border-top: 1px solid var(--border-subtle);
    padding-top: var(--space-6);
}
.ui-refresh .github-help-modal__heading {
    color: var(--fg-default);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-lg);
    margin: 0 0 var(--space-2);
}
.ui-refresh .github-help-modal p {
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-base);
    margin: 0;
}
.ui-refresh .github-help-modal__requirements {
    background: var(--bg-muted);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    margin-top: var(--space-4);
    padding: var(--space-4);
}
.ui-refresh .github-help-modal__requirements-title {
    color: var(--fg-default) !important;
    font-weight: var(--font-weight-semibold);
}
.ui-refresh .github-help-modal ul {
    color: var(--fg-default);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-base);
    margin: var(--space-2) 0 0;
    padding-left: var(--space-5);
}
.ui-refresh .github-help-modal ul ul {
    margin-top: var(--space-1);
}
.ui-refresh .github-help-modal li + li {
    margin-top: var(--space-1);
}
.ui-refresh .github-help-modal code {
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    color: var(--fg-default);
    font-family: var(--font-family);
    font-size: var(--font-size-xs);
    padding: 1px 4px;
}
.ui-refresh .github-help-modal__permission {
    align-items: center;
    background: var(--accent-subtle);
    border: 1px solid var(--accent-muted);
    border-radius: var(--radius-lg);
    color: var(--fg-default);
    display: flex;
    gap: var(--space-3);
    justify-content: space-between;
    margin-top: var(--space-4);
    padding: var(--space-3) var(--space-4);
}
.ui-refresh .github-help-modal__permission-label {
    color: var(--fg-muted);
    font-size: var(--font-size-xs);
}
.ui-refresh .github-help-modal__permission strong {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    white-space: nowrap;
}
.ui-refresh .github-help-modal__note {
    font-size: var(--font-size-xs) !important;
    margin-top: var(--space-3) !important;
}
@media (max-width: 767px) {
    .ui-refresh .github-help-modal.in {
        padding: var(--space-2);
    }
    .ui-refresh .github-help-modal .modal-body {
        max-height: calc(100vh - 156px);
        padding: var(--space-4);
    }
    .ui-refresh .github-help-modal .modal-header {
        padding: var(--space-5) var(--space-4) var(--space-2);
    }
    .ui-refresh .github-help-modal .modal-footer {
        padding: 0 var(--space-4) var(--space-4);
    }
    .ui-refresh .github-help-modal__permission {
        align-items: flex-start;
        flex-direction: column;
        gap: var(--space-1);
    }
}
/* Integration event-setup "invalid credentials" confirmation: keep the
   warning decision focused while making the account-hub route a quiet,
   secondary instruction. Shared by every integration wizard that lets you
   pick a credential set with invalid `valid_credentials` (Marketo, Avalara,
   …) via a `modalClass` of `<integration>-invalid-credentials-modal`. */
.ui-refresh .marketo-invalid-credentials-modal .modal-header,
.ui-refresh .avalara-invalid-credentials-modal .modal-header {
    padding: var(--space-5) var(--space-6);
}
.ui-refresh .marketo-invalid-credentials-modal .modal-header .modal-title,
.ui-refresh .avalara-invalid-credentials-modal .modal-header .modal-title {
    padding-right: var(--space-6);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-lg);
}
.ui-refresh .marketo-invalid-credentials-modal .modal-body,
.ui-refresh .avalara-invalid-credentials-modal .modal-body {
    padding: var(--space-5) var(--space-6);
    color: var(--fg-muted);
}
.ui-refresh .marketo-invalid-credentials-modal .modal-message,
.ui-refresh .avalara-invalid-credentials-modal .modal-message {
    padding: 0;
}
.ui-refresh .marketo-invalid-credentials-modal .modal-body p,
.ui-refresh .avalara-invalid-credentials-modal .modal-body p {
    margin: 0;
}
.ui-refresh .marketo-invalid-credentials-modal .modal-body p + p,
.ui-refresh .avalara-invalid-credentials-modal .modal-body p + p {
    margin-top: var(--space-3);
}
.ui-refresh .marketo-invalid-credentials-modal .modal-footer,
.ui-refresh .avalara-invalid-credentials-modal .modal-footer {
    padding: var(--space-4) var(--space-6);
}
.ui-refresh .modal-backdrop.in {
    opacity: 0.4;
}
.ui-refresh .marketo-registration-enable-lede {
    margin: var(--space-2) 0 0;
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
}
.ui-refresh .marketo-registration-enable .toggle-group > .toggle-row + .reg-settings > .toggle-row,
.ui-refresh .marketo-registration-enable .toggle-group > .reg-settings > .toggle-row + .session-program-settings > .toggle-row,
.ui-refresh .marketo-registration-enable .toggle-group > .reg-settings > .session-program-settings + .toggle-row,
.ui-refresh .marketo-registration-enable .toggle-group > .reg-settings + .toggle-row {
    border-top: 1px solid var(--panel-border);
}
/* Marketo registration settings continue below the master enablement group.
   These are workflow sections, not independent cards: flatten the legacy
   panels into a single vertical document while retaining their conditional
   collapse behavior. */
.ui-refresh #content-container .marketo-registration-settings > .panel.reg-settings {
    margin: 0 0 var(--space-8);
    padding: 0;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    background: transparent !important;
}
.ui-refresh #content-container .marketo-registration-settings > .panel.reg-settings > .panel-heading {
    margin: 0 0 var(--space-2);
    padding: 0;
    border: 0;
    background: transparent;
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
}
.ui-refresh #content-container .marketo-registration-settings > .panel.reg-settings > .panel-body {
    padding: 0;
}
.ui-refresh #content-container .marketo-registration-settings > .panel.reg-settings > .panel-body > .form-group:has(.checkbox) {
    margin: 0 0 var(--space-5);
    padding: 0 var(--space-4);
    background: var(--bg-surface);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
}
.ui-refresh #content-container .marketo-registration-settings > .panel.reg-settings > .panel-body > .form-group:has(.checkbox) .checkbox {
    margin: 0;
    padding: var(--space-4) 0;
}
.ui-refresh #content-container .marketo-registration-settings > .panel.reg-settings > .panel-body > .form-group:has(.checkbox) .checkbox > label {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    margin: 0;
    padding-left: 0;
    color: var(--fg-default);
    font-size: var(--font-size-toggle);
    font-weight: var(--font-weight-medium);
}
.ui-refresh #content-container .marketo-registration-settings > .panel.reg-settings > .panel-body > .form-group:has(.checkbox) .checkbox input[type="checkbox"] {
    order: 99;
    margin-left: auto !important;
}
.ui-refresh #content-container .marketo-registration-settings .marketo-visibility-group {
    margin: 0;
    padding: 0 var(--space-4);
    background: var(--bg-surface);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
}
.ui-refresh #content-container .marketo-registration-settings .marketo-visibility-group > .checkbox {
    margin: 0;
    padding: var(--space-4) 0;
}
.ui-refresh #content-container .marketo-registration-settings .marketo-visibility-group > .checkbox > .hidden-xs {
    display: none;
}
.ui-refresh #content-container .marketo-registration-settings .marketo-visibility-group > .checkbox > label {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    margin: 0;
    padding-left: 0;
    color: var(--fg-default);
    font-size: var(--font-size-toggle);
    font-weight: var(--font-weight-medium);
}
.ui-refresh #content-container .marketo-registration-settings .marketo-visibility-group > .checkbox input[type="checkbox"] {
    order: 99;
    margin-left: auto !important;
}
.ui-refresh #content-container .marketo-registration-settings .marketo-visibility-group .condition-input {
    padding: 0 0 var(--space-4) !important;
}
.ui-refresh #content-container .marketo-registration-settings .marketo-mapping-table {
    margin: 0;
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    overflow: hidden;
}
.ui-refresh #content-container .marketo-registration-settings .marketo-mapping-table > tbody > tr > td,
.ui-refresh #content-container .marketo-registration-settings .marketo-mapping-table > tbody > tr > th {
    padding: var(--space-3) var(--space-4);
    border-color: var(--border-subtle);
    vertical-align: middle;
}
.ui-refresh #content-container .marketo-registration-settings .session-campaign-settings > tbody > tr:not(:first-child) > td {
    vertical-align: top;
}
.ui-refresh #content-container .marketo-registration-settings .marketo-mapping-table > tbody > tr > td:first-child {
    color: var(--fg-default);
    font-weight: var(--font-weight-medium);
}
.ui-refresh #content-container .marketo-registration-settings .marketo-mapping-table > tbody > tr > td:nth-child(2) {
    color: var(--fg-muted);
    font-size: var(--font-size-xs);
}
.ui-refresh #content-container .marketo-registration-settings .marketo-mapping-table .form-group {
    margin: 0;
}
.ui-refresh #content-container .marketo-registration-settings .marketo-program-field-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin-bottom: var(--space-2);
}
.ui-refresh #content-container .marketo-registration-settings .marketo-program-field-header .control-label {
    margin: 0;
}
.ui-refresh #content-container .marketo-registration-settings .marketo-program-field-action .btn {
    height: 30px;
    padding: 0 var(--space-3);
    font-size: var(--font-size-xs);
}
.ui-refresh #content-container .marketo-registration-settings .marketo-session-options {
    display: grid;
    gap: var(--space-4);
    padding: var(--space-4) 0;
}
.ui-refresh #content-container .marketo-registration-settings .marketo-session-dedupe {
    max-width: 360px;
}
.ui-refresh #content-container .marketo-registration-settings .marketo-session-dedupe .form-group {
    margin: 0;
}
.ui-refresh #content-container .marketo-registration-settings .marketo-session-import {
    width: fit-content;
}
.ui-refresh #content-container .marketo-registration-settings .marketo-mapping-table > tbody > tr:first-child > th {
    padding: var(--space-3) var(--space-4);
    background: var(--bg-muted);
    color: var(--fg-default);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    white-space: nowrap;
}
.ui-refresh #content-container .marketo-registration-settings .marketo-mapping-table > tbody > tr:first-child > th[colspan] {
    text-align: left;
    font-size: var(--font-size-sm);
}
.ui-refresh #content-container .marketo-registration-settings .marketo-session-retrieve {
    display: flex;
    justify-content: flex-end;
    margin-top: var(--space-4);
}
.ui-refresh #content-container .marketo-registration-settings .marketo-session-retrieve .btn {
    height: 30px;
    padding: 0 var(--space-3);
    font-size: var(--font-size-xs);
}
.ui-refresh #content-container .marketo-munchkin-code .toggle-row > .form-group {
    display: flex;
    align-items: center;
    height: 20px;
    margin: 0;
    padding: 0;
}
.ui-refresh #content-container .marketo-munchkin-code .toggle-row .help-block:empty {
    display: none;
}
.ui-refresh #content-container .marketo-munchkin-code .toggle-row .checkbox {
    display: flex;
    align-items: center;
    height: 20px;
    width: 100%;
}
.ui-refresh #content-container .marketo-configuration-picker > .form-group,
.ui-refresh #content-container .avalara-credentials-picker > .form-group,
.ui-refresh #content-container .avalara-tax-settings > .form-group {
    margin-left: 0;
    margin-right: 0;
}
.ui-refresh #content-container .marketo-configuration-picker > .form-group .form-control,
.ui-refresh #content-container .avalara-credentials-picker > .form-group .form-control,
.ui-refresh #content-container .avalara-tax-settings > .form-group .form-control {
    width: calc(100% + var(--space-4)) !important;
}
/* ===== AJAX Drawer (dashlet edit) ===== */
.ui-refresh #ajax-drawer-backdrop {
    position: fixed;
    top: 0; left: 0; right: 0; bottom: 0;
    background: rgba(0, 0, 0, 0.4);
    z-index: 1040;
    opacity: 0;
    transition: opacity 0.2s ease;
}
.ui-refresh #ajax-drawer-backdrop.in {
    opacity: 1;
}
.ui-refresh .card {
    background-color: var(--bg-surface);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    padding: var(--space-5);
    --control-bg-color: var(--bg-page);
}
.ui-refresh .ajax-drawer {
    position: fixed;
    top: 0; right: 0;
    width: 480px;
    height: 100vh;
    background: var(--bg-page);
    z-index: 1050;
    transform: translateX(100%);
    transition: transform 0.25s ease;
    display: flex;
    flex-direction: column;
    box-shadow: var(--sh-drawer);
    --control-bg-color: var(--bg-subtle);
    --sidebar-x-padding: var(--space-6);
}
.ui-refresh .ajax-drawer.open {
    transform: translateX(0);
}
/* Wide variant for filter-heavy dashlet editors */
.ui-refresh .ajax-drawer.ajax-drawer--wide {
    width: 680px;
}
/* Extra-wide variant for the automation node form — its step content
   (nested field pickers, multi-column condition rows) is cramped at 680px. */
.ui-refresh .ajax-drawer.ajax-drawer--xwide {
    width: 860px;
    max-width: 100vw;
}
/* Narrow variant for compact forms (single column, few fields) */
.ui-refresh .ajax-drawer.ajax-drawer--narrow {
    width: 300px;
}
/* Barebones-mode scroll region: a consumer that renders its own header
   markup (a `.sidebar-title` block, e.g. via `_slideout_panel_header.php`)
   directly into the drawer — rather than opting into the `.ajax-drawer-body`
   scaffolding below — gets no scrollable region of its own otherwise. `#ajax-
   drawer` is a fixed-height (100vh) flex column, so without this, content
   taller than the viewport just overflows past the panel's bottom edge with
   no way to reach it. Targets whatever follows the header, regardless of its
   own class, so it isn't tied to any one consumer's markup. */
.ui-refresh .ajax-drawer > .sidebar-title + * {
    flex: 1;
    min-height: 0; /* let this shrink below its content size so overflow (and thus the scrollbar) actually kicks in */
    overflow-y: auto;
}

.ui-refresh .ajax-drawer .panel {
    background: transparent !important;
    border: none;
    box-shadow: none;
    margin-bottom: 0;
}
/* Divider between stacked panels, carried on the *upper* panel as a bottom
   rule and gated on "is any later panel still visible?". Conditionally-hidden
   panels (e.g. the payment-method Service Fees panel, or Capacities once a
   session is Included) then cost nothing: they paint no box of their own, and
   they no longer strip the spacing off whatever follows them. A top-based
   `+ .panel` rule could only see its immediate previous sibling, so a hidden
   panel mid-stack left the next panel flush against the one above it. */
.ui-refresh .ajax-drawer .panel:has(~ .panel:not([style*="display: none"]):not([style*="display:none"])) {
    border-bottom: 1px solid var(--border-subtle);
    padding-bottom: var(--space-4);
    margin-bottom: var(--space-4);
}
.ui-refresh .ajax-drawer .panel-body {
    padding: 0;
}
/* Campaign mapping drawer: the campaign search field + "Create Campaign"
   button stretch edge-to-edge on a `wide` drawer, reading like an oversized,
   near-empty search bar. Cap it to a compact width instead. */
.ui-refresh .ajax-drawer .campaign-picker-search {
    max-width: 400px;
}
/* "Create Campaign" is an addon *button* (kartik's asButton wraps it in
   .input-group-btn, not .input-group-addon), so it misses the
   .input-group-addon corner-rounding rule above and Bootstrap's own
   .input-group-btn:last-child > .btn reset (border-radius: 0 on the inner
   corners) wins the cascade tie on the outer corners too, leaving the
   trailing edge square instead of matching the search field. Round it to
   the same radius so the pair reads as one pill. Also match its height to
   the select2 box beside it — same 36px vs 40px .btn/.form-control mismatch
   fixed elsewhere via .hubspot-add-mapping-row .btn. */
.ui-refresh .campaign-picker-search .input-group-btn:last-child > .btn {
    border-top-left-radius: 0 !important;
    border-bottom-left-radius: 0 !important;
    border-top-right-radius: var(--input-radius) !important;
    border-bottom-right-radius: var(--input-radius) !important;
    height: var(--input-height);
}
/* Campaign mapping drawer: refreshing campaign statuses swaps the whole
   status table for a bare FontAwesome 4 icon (`fa fa-spinner`, a syntax this
   app no longer uses elsewhere) with no spacing. Give it the same
   ring-spinner idiom used elsewhere in ui-refresh (`ui-refresh-spin`, above)
   and enough padding that the panel doesn't collapse into a cramped
   one-liner while the table is away. */
.ui-refresh .ajax-drawer .campaign-status-loading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-6) 0;
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
}
.ui-refresh .ajax-drawer .campaign-status-loading::before {
    content: '';
    flex: 0 0 auto;
    width: 16px;
    height: 16px;
    border: 2px solid var(--border-subtle);
    border-top-color: var(--accent-primary);
    border-radius: 50%;
    animation: ui-refresh-spin 0.6s linear infinite;
}
/* Panel headings → terse section labels with subtitle */
.ui-refresh .ajax-drawer .panel-heading,
body.iframe-modal .panel-heading {
    background: transparent;
    border: none;
    padding: 0 0 var(--space-3) 0;
    display: block;
    cursor: default;
}
.ui-refresh .ajax-drawer .panel-heading,
.ui-refresh .ajax-drawer .panel-title,
body.iframe-modal .panel-heading {
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
    text-transform: none;
    letter-spacing: normal;
}

/* Icon-only delete button in an ajax-drawer grid row — error-tinted on hover, same
   treatment as query-builder's own rule-delete buttons. Base state is left as plain
   .btn-default (matches sibling action buttons); only the hover state is overridden. */
.ui-refresh .ajax-drawer .btn.action-delete:hover {
    color: var(--color-error) !important;
    border-color: var(--color-error) !important;
    background: var(--color-error-bg) !important;
}

/* Checkbox list rows → clean rows with dividers, label left / toggle right */
.ui-refresh .ajax-drawer .checkbox:not(.multiselect-container .checkbox) {
    padding-left: 0;
    margin: 0;
}
.ui-refresh .ajax-drawer .checkbox:not(.multiselect-container .checkbox):last-child {
    border-bottom: none;
    padding-bottom: 0;
}

/* Chip picker inside drawers — restore centered chip layout over toggle-switch overrides */
.ui-refresh .ajax-drawer div:has(> .checkbox + .checkbox) > .checkbox:not(.multiselect-container .checkbox) {
    border-bottom: none;
    padding: 0;
}
.ui-refresh .ajax-drawer div:has(> .checkbox + .checkbox) > .checkbox:not(.multiselect-container .checkbox) label {
    justify-content: center;
    flex-direction: row;
    width: auto;
    padding-left: var(--space-4) !important;
}
.ui-refresh .ajax-drawer div:has(> .checkbox + .checkbox) > .checkbox:not(.multiselect-container .checkbox) input[type="checkbox"] {
    position: absolute;
    width: 0;
    min-width: 0;
    height: 0;
    opacity: 0;
}
/* Single standalone toggle (e.g. "Only show future events") — collapse extra spacing
   so it reads as part of the same section as the field above it */
.ui-refresh .ajax-drawer .form-group:has(> .checkbox:not(.multiselect-container .checkbox):only-of-type) {
    padding-top: 0;
    margin-top: 0;
    margin-bottom: 0;
}
.ui-refresh .ajax-drawer .form-group:has(> .checkbox:not(.multiselect-container .checkbox):only-of-type) .checkbox:not(.multiselect-container .checkbox) {
    padding-top: var(--space-2);
    border-bottom: none;
}

/* Chained toggle rows (Zoom "Other Options", GoToWebinar email toggles) —
   the :only-of-type collapse above unintentionally matches every single
   checkbox form-group (because each contains exactly one .checkbox). When
   multiple checkbox form-groups appear as adjacent siblings, restore row
   padding and dividers so they read as a clean list. */
.ui-refresh .ajax-drawer .form-group:has(> .checkbox:not(.multiselect-container .checkbox)):has(+ .form-group:has(> .checkbox:not(.multiselect-container .checkbox))),
.ui-refresh .ajax-drawer .form-group:has(> .checkbox:not(.multiselect-container .checkbox)) + .form-group:has(> .checkbox:not(.multiselect-container .checkbox)) {
    padding-top: 0;
    margin-top: 0;
    margin-bottom: 0;
}
.ui-refresh .ajax-drawer .form-group:has(> .checkbox:not(.multiselect-container .checkbox)):has(+ .form-group:has(> .checkbox:not(.multiselect-container .checkbox))) > .checkbox:not(.multiselect-container .checkbox),
.ui-refresh .ajax-drawer .form-group:has(> .checkbox:not(.multiselect-container .checkbox)) + .form-group:has(> .checkbox:not(.multiselect-container .checkbox)) > .checkbox:not(.multiselect-container .checkbox) {
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--border-subtle);
}
/* Last row in a chain — strip the divider */
.ui-refresh .ajax-drawer .form-group:has(> .checkbox:not(.multiselect-container .checkbox)) + .form-group:has(> .checkbox:not(.multiselect-container .checkbox)):not(:has(+ .form-group:has(> .checkbox:not(.multiselect-container .checkbox)))) > .checkbox:not(.multiselect-container .checkbox) {
    border-bottom: none;
}
.ui-refresh .ajax-drawer .checkbox:not(.multiselect-container .checkbox) label {
    display: flex;
    flex-direction: row-reverse;
    align-items: center;
    justify-content: space-between;
    font-size: var(--font-size-sm);
    color: var(--fg-default);
    font-weight: 400;
    margin: 0;
    /* Override active-form.css: .new-form .checkbox label { padding-left: 20px !important } */
    padding-left: 0 !important;
    cursor: pointer;
    width: 100%;
}
/* Toggle switch — modern pill style */
/* Bootstrap 3 absolutely positions the checkbox with margin-left: -20px and
   adds padding-left: 20px to the label to compensate. Since we restyle the
   checkbox as a flex toggle, reset both so the label text aligns flush. */
.ui-refresh .ajax-drawer .checkbox:not(.multiselect-container .checkbox) input[type="checkbox"] {
    position: relative;
    margin: 0;
    margin-left: 0;
    flex-shrink: 0;
    appearance: none;
    -webkit-appearance: none;
    width: 36px;
    min-width: 36px;
    height: 20px;
    background: var(--gray-30);
    border-radius: var(--radius-full);
    cursor: pointer;
    transition: background 0.2s ease;
}
.ui-refresh .ajax-drawer .checkbox:not(.multiselect-container .checkbox) input[type="checkbox"]::after {
    content: '';
    display: block;
    position: absolute;
    top: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    background: white;
    border-radius: 50%;
    transition: transform 0.2s ease;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}
.ui-refresh .ajax-drawer .checkbox:not(.multiselect-container .checkbox) input[type="checkbox"]:checked {
    background: var(--accent-primary);
}
.ui-refresh .ajax-drawer .checkbox:not(.multiselect-container .checkbox) input[type="checkbox"]:checked::after {
    transform: translateX(16px);
}
/* ToggleRow widget (label + .checkbox-toggle pill): label left, switch right,
   hairline divider between consecutive rows. Keyed on the .toggle-group card
   rather than on a surface, so a group renders the same in the drawer, on an
   event settings page, or in page-widget settings. `.ajax-drawer .toggle-row`
   keeps the treatment for standalone rows that sit outside a group. */
.ui-refresh .ajax-drawer .toggle-row,
.ui-refresh .toggle-group .toggle-row {
    display: flex;
    flex-direction: row-reverse;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) 0;
}
/* Rows that own a dependent collapse panel (the Virtual tab's waiting room,
   on-demand and session-registration settings) keep the pill and its label on
   the first line and drop the panel full-width beneath them. Wrapping is
   opt-in per row: forcing it on every row would let a long label push the
   switch onto a second line. */
.ui-refresh .ajax-drawer .toggle-row:has(> .panel-expanded-settings),
.ui-refresh .toggle-group .toggle-row:has(> .panel-expanded-settings) {
    flex-wrap: wrap;
    /* No row-gap: a collapsing panel is still a flex item until Bootstrap
       swaps .collapsing for .collapse, so any row-gap survives the whole
       height animation and then disappears in a single frame — a visible jump
       at the end of the close. The gap above the panel comes from the margin
       on its inner first child instead, which is inside the animated box and
       shrinks with it. Column-gap is untouched. */
    row-gap: 0;
}
/* …the panel only. `flex-basis: 0`, not `auto`, makes a long label wrap its text
   rather than drop below the pill: line breaking measures basis, pre-shrink. */
.ui-refresh .ajax-drawer .toggle-row:has(> .panel-expanded-settings) > label:not(.checkbox-toggle),
.ui-refresh .toggle-group .toggle-row:has(> .panel-expanded-settings) > label:not(.checkbox-toggle) {
    flex: 1 1 0;
    min-width: 0;
}
.ui-refresh .ajax-drawer .toggle-row > .panel-expanded-settings,
.ui-refresh .toggle-group .toggle-row > .panel-expanded-settings {
    flex: 0 0 100%;
    padding-left: 0;
    margin-left: 0;
}
/* A dependent toggle inside an expanded panel is a sub-row, not a row of the
   card: the panel already spaces it (margin on its first child) and the parent
   row already pads the bottom, so the top-level row padding would double up. */
.ui-refresh .ajax-drawer .panel-expanded-settings > .toggle-row,
.ui-refresh .toggle-group .panel-expanded-settings > .toggle-row {
    padding: 0;
}
/* Sibling toggle-rows stacked inside the same expanded panel (e.g. the admin
   user form's "super admin" + "Include in Sales/Account/CSM" rows) get no
   spacing from the rule above, which assumes a single dependent toggle per
   panel. Restore a gap between them without reintroducing the divider look
   of a .toggle-group card. */
.ui-refresh .ajax-drawer .panel-expanded-settings > .toggle-row ~ .toggle-row,
.ui-refresh .toggle-group .panel-expanded-settings > .toggle-row ~ .toggle-row {
    margin-top: var(--space-3) !important;
}
/* Rows whose toggle owns a sibling collapse panel (Schedule Go Live on the
   clone + settings Registration groups; the Registration Timeout Period
   override on the Website section, whose toggle is a $form->field()
   checkbox — a .form-group — rather than a raw .checkbox div): the row is
   row-reverse flex, so without wrapping the toggle lands flush right and the
   open panel squeezes beside it on one line. Wrap instead: the toggle line
   spans the full row (label left, pill pushed right by the input's
   margin-left: auto) and the panel drops full-width beneath. The .collapsing
   variants keep the layout stable while Bootstrap swaps classes during the
   height animation. No row-gap for the same reason as the
   panel-expanded-settings rows above — the gap comes from the panel's inner
   first-child margin. */
.ui-refresh .ajax-drawer .toggle-row:has(> .checkbox + .collapse),
.ui-refresh .toggle-group .toggle-row:has(> .checkbox + .collapse),
.ui-refresh .ajax-drawer .toggle-row:has(> .checkbox + .collapsing),
.ui-refresh .toggle-group .toggle-row:has(> .checkbox + .collapsing),
.ui-refresh .ajax-drawer .toggle-row:has(> .form-group + .collapse),
.ui-refresh .toggle-group .toggle-row:has(> .form-group + .collapse),
.ui-refresh .ajax-drawer .toggle-row:has(> .form-group + .collapsing),
.ui-refresh .toggle-group .toggle-row:has(> .form-group + .collapsing) {
    flex-wrap: wrap;
    row-gap: 0;
}
.ui-refresh .ajax-drawer .toggle-row > .checkbox:has(+ .collapse),
.ui-refresh .toggle-group .toggle-row > .checkbox:has(+ .collapse),
.ui-refresh .ajax-drawer .toggle-row > .checkbox:has(+ .collapsing),
.ui-refresh .toggle-group .toggle-row > .checkbox:has(+ .collapsing),
.ui-refresh .ajax-drawer .toggle-row > .form-group:has(+ .collapse),
.ui-refresh .toggle-group .toggle-row > .form-group:has(+ .collapse),
.ui-refresh .ajax-drawer .toggle-row > .form-group:has(+ .collapsing),
.ui-refresh .toggle-group .toggle-row > .form-group:has(+ .collapsing) {
    flex: 1 1 100%;
}
.ui-refresh .ajax-drawer .toggle-row > .checkbox + .collapse,
.ui-refresh .toggle-group .toggle-row > .checkbox + .collapse,
.ui-refresh .ajax-drawer .toggle-row > .checkbox + .collapsing,
.ui-refresh .toggle-group .toggle-row > .checkbox + .collapsing,
.ui-refresh .ajax-drawer .toggle-row > .form-group + .collapse,
.ui-refresh .toggle-group .toggle-row > .form-group + .collapse,
.ui-refresh .ajax-drawer .toggle-row > .form-group + .collapsing,
.ui-refresh .toggle-group .toggle-row > .form-group + .collapsing {
    flex: 0 0 100%;
}
/* Divider only between rows that are genuinely consecutive. The chained `+`
   variants step over the unchecked-fallback <input type="hidden"> that
   usually precedes a ToggleRow (e.g. custom-field/_form.php) — that input is
   markup plumbing, not a visual break. Anything else between two rows (a
   dependent .form-group, a sibling collapse panel) *is* a break, so the
   following row starts clean: a hairline under an unrelated text field reads
   as a stray line, not as a row separator. Rows inside a .toggle-group get
   their own divider rules further down, including form-group adjacency. */
.ui-refresh .toggle-group > .toggle-row + .toggle-row,
.ui-refresh .toggle-group > .form-group + .form-group,
.ui-refresh .toggle-group > .toggle-row + .form-group,
.ui-refresh .toggle-group > .form-group + .toggle-row,
.ui-refresh .toggle-group > .toggle-row + input[type="hidden"] + .toggle-row,
.ui-refresh .toggle-group > .toggle-row + input[type="hidden"] + input[type="hidden"] + .toggle-row {
    border-top: 1px solid var(--panel-border);
}
.ui-refresh .ajax-drawer .toggle-row > label,
.ui-refresh .toggle-group .toggle-row > label {
    margin-bottom: 0;
    /* Bootstrap's .form-horizontal .control-label adds 7px top padding at
       >=768px (e.g. import/index.php is a horizontal form); the pill has no
       such padding, so the label text sat below the row's flex center. */
    padding-top: 0;
    padding-bottom: 0;
    text-transform: none;
    letter-spacing: normal;
    cursor: pointer;
}
/* `.toggle-row > label` above also matches the switch itself — ToggleSwitch
   renders a bare `<label class="checkbox-toggle">`, not just the text label —
   so growing every direct-child label pushed the switch's own label around
   too and threw off its position. Scope the fill-the-gap growth to the text
   label specifically (`.toggle-label`, always present per ToggleRow) so the
   whole row toggles via its native `for` binding without disturbing the
   switch (PR #2537 review). */
.ui-refresh .ajax-drawer .toggle-row > label.toggle-label,
.ui-refresh .toggle-group .toggle-row > label.toggle-label {
    flex: 1 1 auto;
    /* A staff-indicator badge appended to the label text (e.g. Debug Mode)
       is an inline-flex pill with its own line-height — as a plain inline
       run next to text it baseline-aligns instead of centering, reading as
       vertically offset. Make the label itself the flex container so the
       text and badge center against each other and the row. */
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}
.ui-refresh .ajax-drawer .toggle-row > label.toggle-label.hintable-label,
.ui-refresh .toggle-group .toggle-row > label.toggle-label.hintable-label {
    gap: 0;
}
/* Override backend.css's display:none !important so native toggles remain
   keyboard-reachable while the adjacent span supplies the visual control. */
.ui-refresh .checkbox-toggle input[type="checkbox"] {
    display: block !important;
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}
.ui-refresh .checkbox-toggle input[type="checkbox"]:focus-visible + span {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}
/* Upsize the mini pill switch to match the drawer checkbox-row switches
   (36x20 / 16px thumb) instead of the 30x18 default used elsewhere.
   .etf-mod-row is the event-type drawer's module list, whose rows use a
   real ToggleSwitch instead of ToggleRow. */
.ui-refresh .ajax-drawer .toggle-row .checkbox-toggle,
.ui-refresh .ajax-drawer .toggle-row .checkbox-toggle span,
.ui-refresh .ajax-drawer .etf-mod-row .checkbox-toggle,
.ui-refresh .ajax-drawer .etf-mod-row .checkbox-toggle span,
.ui-refresh .toggle-group .toggle-row .checkbox-toggle,
.ui-refresh .toggle-group .toggle-row .checkbox-toggle span {
    width: 36px;
    height: 20px;
    flex-shrink: 0;
}
.ui-refresh .ajax-drawer .toggle-row .checkbox-toggle span:after,
.ui-refresh .ajax-drawer .etf-mod-row .checkbox-toggle span:after,
.ui-refresh .toggle-group .toggle-row .checkbox-toggle span:after {
    width: 16px;
    height: 16px;
}
.ui-refresh .ajax-drawer .toggle-row .checkbox-toggle input[type="checkbox"]:checked + span:after,
.ui-refresh .ajax-drawer .etf-mod-row .checkbox-toggle input[type="checkbox"]:checked + span:after,
.ui-refresh .toggle-group .toggle-row .checkbox-toggle input[type="checkbox"]:checked + span:after {
    left: 18px;
}
/* backend.css hardcodes the checked-state fill to --swoogo-green-300, which
   doesn't follow the account's chosen accent (purple/amber/custom) the way
   every other ui-refresh control does. Re-point it here, same scope as the
   size override above. */
.ui-refresh .ajax-drawer .toggle-row .checkbox-toggle input[type="checkbox"]:checked + span,
.ui-refresh .ajax-drawer .etf-mod-row .checkbox-toggle input[type="checkbox"]:checked + span,
.ui-refresh .toggle-group .toggle-row .checkbox-toggle input[type="checkbox"]:checked + span {
    background: var(--accent-primary);
}
/* Automation card list's enable/disable toggle (a ToggleSwitch widget,
   .automation-toggle marks it for JS/openEdit's click-guard) — same 36x20
   sizing and accent-following color as the drawer toggles above, so both
   read as the same control. Legacy (non-ui-refresh) keeps the plain green
   checkbox-toggle, matching every other legacy toggle in the app. */
.ui-refresh .automation-card .checkbox-toggle,
.ui-refresh .automation-card .checkbox-toggle span {
    width: 36px;
    height: 20px;
    flex-shrink: 0;
}
.ui-refresh .automation-card .checkbox-toggle span:after {
    width: 16px;
    height: 16px;
}
.ui-refresh .automation-card .checkbox-toggle input[type="checkbox"]:checked + span:after {
    left: 18px;
}
.ui-refresh .automation-card .checkbox-toggle input[type="checkbox"]:checked + span {
    background: var(--accent-primary);
}
/* Form-group spacing — tighter to keep sections visually connected */
.ui-refresh.sidebar .form-group,
body.ui-refresh.iframe-modal .form-group {
    margin-top: var(--space-1);
    margin-bottom: var(--space-1);
}
.ui-refresh.sidebar .form-group:first-child,
.ui-refresh .ajax-drawer .form-group:first-child,
body.ui-refresh.iframe-modal .form-group:first-child {
    margin-top: 0;
}
.ui-refresh.sidebar .form-group:last-child,
.ui-refresh .ajax-drawer .form-group:last-child,
body.ui-refresh.iframe-modal .form-group:last-child {
    margin-bottom: 0;
}
/* Barebones is the default — no form-group spacing. Scaffolded drawers
   opt in to the legacy padding via ajax-drawer--scaffolded. */
.ui-refresh .ajax-drawer .form-group + .form-group {
    border-top: none;
}
.ui-refresh .ajax-drawer--scaffolded .form-group + .form-group {
    padding-top: var(--space-2);
}
.ui-refresh .ajax-drawer--scaffolded .form-group.new-form {
    padding-bottom: 2px;
}
/* Static (read-only) fields — compact display, no extra height */
.ui-refresh .ajax-drawer .form-control-static {
    min-height: 0;
    padding: 0;
    line-height: var(--leading-normal);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--fg-default);
}

/* Bootstrap Multiselect inside the drawer should stay a checklist and not inherit
   the standalone toggle-switch treatment used for normal checkbox rows. */
.ui-refresh .ajax-drawer .multiselect-native-select {
    width: 100%;
}
.ui-refresh .ajax-drawer .multiselect-native-select > .btn-group {
    width: 100%;
}
.ui-refresh .ajax-drawer .multiselect-native-select > .btn-group > .multiselect {
    width: 100%;
    min-height: 44px;
    padding: var(--space-2) 40px var(--space-2) var(--space-3);
    display: flex;
    align-items: center;
    justify-content: flex-start;
    text-align: left !important;
    border-radius: var(--radius-md);
    border-color: var(--input-border);
    background-color: var(--bg-surface)!important;
    color: var(--fg-default);
    box-shadow: none;
}
.ui-refresh .ajax-drawer .multiselect-native-select > .btn-group > .multiselect .multiselect-selected-text {
    display: block;
    flex: 1 1 auto;
    min-width: 0;
    text-align: left !important;
    font-size: var(--font-size-sm);
    line-height: 1.4;
}
.ui-refresh .multiselect-native-select > .btn-group > .multiselect[title="None selected"] .multiselect-selected-text,
.ui-refresh .multiselect-native-select > .btn-group > .multiselect .multiselect-selected-text:empty {
    color: var(--fg-muted);
}
.ui-refresh .ajax-drawer .multiselect-native-select > .btn-group > .multiselect:hover,
.ui-refresh .ajax-drawer .multiselect-native-select > .btn-group > .multiselect:focus,
.ui-refresh .ajax-drawer .multiselect-native-select > .btn-group.open > .multiselect {
    border-color: var(--border-strong);
    background: var(--bg-surface);
    color: var(--fg-default);
    box-shadow: none;
}
.ui-refresh .ajax-drawer .multiselect-native-select > .btn-group > .multiselect .caret {
    position: absolute;
    right: var(--space-3);
    top: 50%;
    margin-top: -2px;
}
.ui-refresh .ajax-drawer .multiselect-container.dropdown-menu {
    min-width: 100%;
    width: fit-content;
    max-height: 320px;
    overflow-y: auto;
    padding: var(--space-1) 0;
    border-radius: var(--radius-lg);
    border: 1px solid var(--border-subtle);
    box-shadow: var(--sh-md);
    background: var(--bg-surface);
}
.ui-refresh .ajax-drawer .multiselect-container > li > a,
.ui-refresh .ajax-drawer .multiselect-container > li > label {
    padding: 0;
}
.ui-refresh .ajax-drawer .multiselect-container .checkbox {
    justify-content: flex-start;
    border-bottom-color: var(--border-subtle);
    transition: background-color var(--transition-fast);
}
.ui-refresh .ajax-drawer .multiselect-container .checkbox:hover {
    background: var(--state-hover);
}

/* ===== Field Picker (inside drawer) ===== */

/* Clean row styling — divider lines, compact padding */
.ui-refresh .ajax-drawer .field-picker-selected-fields {
    border: none;
    margin-bottom: var(--space-2);
}
.ui-refresh .ajax-drawer .field-picker-selected-fields > tbody > tr {
    border-bottom: 1px solid var(--border-subtle);
    transition: background 0.1s ease;
}
.ui-refresh .ajax-drawer .field-picker-selected-fields > tbody > tr:last-child {
    border-bottom: none;
}
.ui-refresh .ajax-drawer .field-picker-selected-fields > tbody > tr:hover {
    background: var(--state-hover);
}
.ui-refresh .ajax-drawer .field-picker-selected-fields > tbody > tr > td {
    border-top: none;
    padding: var(--space-2) var(--space-2);
    vertical-align: middle;
    font-size: var(--font-size-sm);
    color: var(--fg-default);
}
/* Modern drag handle — replace gripper.png with icon */
.ui-refresh .ajax-drawer .field-picker-selected-fields .handle {
    cursor: grab;
    padding: 0;
    width: 24px;
    color: var(--fg-subtle);
    transition: color 0.1s ease;
}
.ui-refresh .ajax-drawer .field-picker-selected-fields tr:hover .handle {
    color: var(--fg-muted);
}
.ui-refresh .ajax-drawer .field-picker-selected-fields .handle:active {
    cursor: grabbing;
}
.ui-refresh .ajax-drawer .field-picker-selected-fields .handle div {
    background: none;
    height: auto;
    width: auto;
}
.ui-refresh .ajax-drawer .field-picker-selected-fields .handle i {
    font-size: var(--font-size-xs);
}
/* Delete button — invisible until row hover */
.ui-refresh .ajax-drawer .field-picker-selected-fields .delete-fields-btn {
    background: none;
    border: none;
    box-shadow: none;
    color: var(--fg-subtle);
    padding: var(--space-1);
    height: auto;
    width: auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-sm);
    opacity: 0;
    transition: opacity 0.1s ease, color 0.15s ease, background 0.15s ease;
}
.ui-refresh .ajax-drawer .field-picker-selected-fields tr:hover .delete-fields-btn {
    opacity: 1;
}
.ui-refresh .ajax-drawer .field-picker-selected-fields .delete-fields-btn:hover {
    color: var(--fg-default);
    background: var(--bg-muted);
}
/* Disabled delete button (required fields) — fully hidden */
.ui-refresh .ajax-drawer .field-picker-selected-fields .delete-fields-btn[disabled] {
    display: none;
}
.ui-refresh .ajax-drawer .panel-expanded-settings {
    padding-left: 0;
    margin-left: 0;
}
.ui-refresh .panel-expanded-settings {
    padding-left: var(--space-4);
    padding-right: 0;
}
/* backend.css's own border-left uses --swoogo-gray-400, a static light gray
   with no dark-mode counterpart. Scoped to this page's own marker class
   (not the bare rule above, which also reaches ~45 other, unrelated pages
   this PR never touches) so the fix can't affect any other feature. */
.ui-refresh .spotme-session-tag-section .panel-expanded-settings {
    border-left-color: var(--border-subtle);
}
body.iframe-modal.ui-refresh .panel-expanded-settings {
    margin-left: 0;
    padding-left: 0;
}
/* Description text — muted hint style */
.ui-refresh .ajax-drawer .panel-body > p {
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
    margin-bottom: var(--space-2);
}
.ui-refresh .ajax-drawer .panel-body p.p-label {
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
}
.ui-refresh .ajax-drawer .panel-body .session-settings {
    padding: var(--space-3) 0;
}
.ui-refresh .ajax-drawer .panel-body .session-settings .input-group-addon,
.ui-refresh .ajax-drawer .panel-body .session-settings .form-control {
    background-color: var(--paper) !important;
}
.ui-refresh .ajax-drawer .panel-body ul li,
.ui-refresh .ajax-drawer .panel-body ol li {
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
    line-height: 1.5;
}

.ui-refresh .ajax-drawer .panel-body ul li:last-child,
.ui-refresh .ajax-drawer .panel-body ol li:last-child {
    margin-bottom: 0;
}
.ui-refresh .ajax-drawer .panel-body > p.empty-chat-rooms {
    margin-bottom: 0;
}

/* "Choose Fields" button — full-width dashed add row */
.ui-refresh .ajax-drawer .field-picker-add-btn {
    text-align: center;
}
.ui-refresh .ajax-drawer .field-picker-add-btn .btn {
    border: 1px dashed var(--panel-border);
    background: transparent;
    color: var(--fg-muted);
    font-size: var(--font-size-xs);
    border-radius: var(--radius-md);
    padding: var(--space-2) 0;
    width: 100%;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.ui-refresh .ajax-drawer .field-picker-add-btn .btn:hover {
    border-color: var(--accent-primary);
    color: var(--accent-on-tint);
    background: var(--accent-subtle);
}

/* ===== Drawer Multi-Screen (field picker) ===== */
.ui-refresh .ajax-drawer .drawer-screen-main {
    transition: transform 0.25s ease, opacity 0.25s ease;
}
.ui-refresh .ajax-drawer .drawer-screen-fields {
    position: absolute;
    top: 0; left: 0; right: 0; bottom: 0;
    padding: var(--space-4) var(--space-6);
    overflow-y: auto;
    transform: translateX(100%);
    transition: transform 0.25s ease;
}
.ui-refresh .ajax-drawer.drawer-fields-open .drawer-screen-main {
    transform: translateX(-30%);
    opacity: 0;
    pointer-events: none;
}
.ui-refresh .ajax-drawer.drawer-fields-open .drawer-screen-fields {
    transform: translateX(0);
}
/* Drawer-native second screens (object-audit/twig-history.php's detail,
   email-blast/ajax-drawer/form.php's #recipients-screen) sit as a later
   sibling of #sidebar-pjax-form with z-index:auto, so any positioned child of
   the form that carries its own z-index — swoogo-switch-toggle's labels and
   input (1/2), the multiselect caret (10) — paints through the screen.
   Isolating the form traps those inside its own stacking context so the
   screen wins on DOM order alone. isolation, not position: the folder-access
   screen's absolute insets must keep resolving against #ajax-drawer. */
.ui-refresh .ajax-drawer.drawer-fields-open #sidebar-pjax-form {
    isolation: isolate;
}
/* Back button */
.ui-refresh .ajax-drawer .drawer-back-btn {
    background: none;
    border: none;
    color: var(--fg-muted);
    cursor: pointer;
    padding: var(--space-1);
    margin-right: var(--space-2);
    display: inline-flex;
    align-items: center;
    border-radius: var(--radius-sm);
    line-height: 1;
}
.ui-refresh .ajax-drawer .drawer-back-btn:hover {
    background: var(--state-hover);
    color: var(--fg-default);
}
.ui-refresh .ajax-drawer.drawer-fields-open .ajax-drawer-title {
    display: flex;
    align-items: center;
}

/* ===== Folder Access Screen (drawer-native second screen) =====
   folder/ajax-drawer/_access-screen.php reuses the .drawer-screen-fields /
   .drawer-back-btn / #ajax-drawer.drawer-fields-open contract above — the
   generic .drawer-screen-fields rule already position:absolute/insets it
   against #ajax-drawer (its nearest positioned ancestor, since the folder
   drawer is barebones and #sidebar-pjax-form isn't positioned), so it
   already covers the drawer's own header and footer with no extra CSS.
   What's missing for this specific screen is the internal flex layout
   (pinned header/footer, only the body scrolling) and the header/body/
   footer chrome — scoped by id, not by the shared .drawer-screen-fields
   class, so it can't affect the dashlet field-picker screen above, which
   deliberately has neither (it just scrolls the whole region over a flat
   legacy modal). */
.ui-refresh .ajax-drawer #folder-access-screen {
    display: flex;
    flex-direction: column;
    padding: 0;
    overflow: hidden;
    background: var(--bg-page);
}
.ui-refresh .ajax-drawer #folder-access-screen .drawer-screen-header {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--sidebar-x-padding);
    border-bottom: 1px solid var(--border-subtle);
    flex-shrink: 0;
}
.ui-refresh .ajax-drawer #folder-access-screen .drawer-screen-title {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
}
.ui-refresh .ajax-drawer #folder-access-screen .drawer-screen-body {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    overflow: hidden;
    padding: 0;
}
.ui-refresh .ajax-drawer #folder-access-screen .drawer-screen-footer {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
    padding: var(--space-3) var(--sidebar-x-padding);
    border-top: 1px solid var(--border-subtle);
    background: var(--bg-surface);
    flex-shrink: 0;
}

/* Two-pane Available/Selected layout, mirroring the retired
   folder-manage-users-modal.js modal's .fmu-body/.fmu-available/.fmu-granted
   structure so the visual approach carries over into the drawer screen.
   web/js/ui-refresh/folder-access-screen.js adds `ajax-drawer--wide` to
   #ajax-drawer while this screen is open so both columns have room, even
   though the folder drawer normally opens at the 'normal' (480px) size. */
.ui-refresh .ajax-drawer #folder-access-screen .fas-search {
    padding: var(--space-3) var(--sidebar-x-padding);
    flex-shrink: 0;
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-panes {
    display: flex;
    flex: 1;
    min-height: 0;
    overflow: hidden;
    border-top: 1px solid var(--border-subtle);
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-available,
.ui-refresh .ajax-drawer #folder-access-screen .fas-granted {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-granted {
    border-left: 1px solid var(--border-subtle);
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-2) var(--space-3);
    min-height: 40px;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
    border-bottom: 1px solid var(--border-subtle);
    flex-shrink: 0;
}
/* Badge::widget() renders a block-level .badges-container div — inline-flex
   it (same fix .sidebar-panel-title-row applies) so the count badge sits on
   the same line as the pane title instead of forcing a break. */
.ui-refresh .ajax-drawer #folder-access-screen .fas-panel-header-title {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-select-all-label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: var(--font-size-xs);
    font-weight: normal;
    color: var(--fg-muted);
    cursor: pointer;
    margin: 0;
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-select-all-label input[type="checkbox"] {
    margin: 0;
}
/* Native checkboxes render with the browser's default border color, which
   has no contrast against a dark background — same fix the retired
   .fmu-role-checkbox/.fmu-user-checkbox rules already applied. */
.ui-refresh .ajax-drawer #folder-access-screen .fas-role-checkbox,
.ui-refresh .ajax-drawer #folder-access-screen .fas-user-checkbox,
.ui-refresh .ajax-drawer #folder-access-screen .fas-select-all-label input[type="checkbox"] {
    border-color: var(--fg-subtle);
    accent-color: var(--accent-primary);
}
.ui-refresh .ajax-drawer #folder-access-screen #fas-available-list,
.ui-refresh .ajax-drawer #folder-access-screen #fas-granted-list {
    flex: 1;
    overflow-y: auto;
    padding: var(--space-1) 0;
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-role-group {
    margin-bottom: 2px;
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-role-header {
    display: flex;
    align-items: center;
    padding: 6px 14px;
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
    cursor: pointer;
    user-select: none;
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-role-header:hover {
    background: var(--state-hover);
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-role-checkbox {
    margin: 0 0 0 auto;
    flex-shrink: 0;
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-chevron {
    font-size: 10px;
    width: 12px;
    display: inline-block;
    cursor: pointer;
    padding: var(--space-1);
    margin-right: var(--space-1);
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-role-count {
    margin-left: var(--space-1);
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-role-group.collapsed .fas-role-users {
    display: none;
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-user-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 5px 14px 5px 32px;
    margin: 0;
    cursor: pointer;
    font-weight: normal;
    font-size: var(--font-size-xs);
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-user-row:hover {
    background: var(--state-hover);
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-user-name {
    flex: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-user-row .fas-user-checkbox {
    margin: 0 0 0 auto;
    flex-shrink: 0;
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-granted-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 5px 14px;
    font-size: var(--font-size-xs);
}
.ui-refresh .ajax-drawer #folder-access-screen .fas-empty {
    padding: var(--space-5) 14px;
    color: var(--fg-subtle);
    font-size: var(--font-size-sm);
    text-align: center;
}

/* ===== Pjax loading overlay (generic) ===== */
.ui-refresh .pjax-loading-container {
    position: relative;
}
.ui-refresh .pjax-loading-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-page);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s ease;
    z-index: 2;
}
.ui-refresh .pjax-loading-overlay.open {
    opacity: 0.85;
    pointer-events: auto;
}
/* Spinner-only variant (no box/text) — same ring-spinner idiom as #dt-processing's,
   the standard loading indicator for any .pjax-loading-overlay (e.g. the User
   view's Recent Uploads), the Update User drawer's Accounts grid, and its
   "Add Account"/"Create in Zendesk" modals (user-account-add-modal.js,
   user-zendesk-modal.js). */
.ui-refresh .pjax-loading-overlay__spinner {
    width: 18px;
    height: 18px;
    border: 3px solid var(--border-subtle);
    border-top-color: var(--accent-primary);
    border-radius: 50%;
    animation: ui-refresh-spin 0.6s linear infinite;
}
/* Centers a .pjax-loading-overlay__spinner inside a confirm-dialog--form body
   while its content is still loading. */
.ui-refresh .confirm-dialog-loading {
    display: flex;
    justify-content: center;
    padding: var(--space-6) 0;
}

/* ===== Sidebar Sub-Screen (in-iframe drawer-screen-fields) =====
   Same `.drawer-screen-fields` / `.drawer-back-btn` / `.drawer-fields-open`
   contract as the ajax-drawer multi-screen above, but for screens hosted
   inside a sidebar iframe (reg-type pricing → "Add Early Bird Date"). The
   sidebar form doesn't itself act as a screen container, so the sub-screen
   is anchored via `position: fixed` to the iframe viewport — covering the
   tab bar + body + footer wholesale while it's active. Driven from the
   in-view JS in fee/_addEarlyDate.php; opens by adding `.open` to the
   screen element and `.drawer-fields-open` to the iframe body. */
body.iframe.ui-refresh .drawer-screen-fields,
body.sidebar.ui-refresh .drawer-screen-fields {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    flex-direction: column;
    background: var(--bg-surface);
    transform: translateX(100%);
    transition: transform 0.25s ease;
    z-index: 1040;
}
body.iframe.ui-refresh .drawer-screen-fields.open,
body.sidebar.ui-refresh .drawer-screen-fields.open {
    transform: translateX(0);
}
body.iframe.ui-refresh .drawer-screen-fields .drawer-screen-header,
body.sidebar.ui-refresh .drawer-screen-fields .drawer-screen-header {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border-subtle);
    flex-shrink: 0;
}
body.iframe.ui-refresh .drawer-screen-fields .drawer-screen-title,
body.sidebar.ui-refresh .drawer-screen-fields .drawer-screen-title {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
}
body.iframe.ui-refresh .drawer-screen-fields .drawer-screen-body,
body.sidebar.ui-refresh .drawer-screen-fields .drawer-screen-body {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: var(--space-4);
}
body.iframe.ui-refresh .drawer-screen-fields .drawer-screen-meta,
body.sidebar.ui-refresh .drawer-screen-fields .drawer-screen-meta {
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
    margin: var(--space-3) 0 0;
}
body.iframe.ui-refresh .drawer-screen-fields .drawer-screen-footer,
body.sidebar.ui-refresh .drawer-screen-fields .drawer-screen-footer {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    border-top: 1px solid var(--border-subtle);
    background: var(--bg-surface);
    flex-shrink: 0;
}
/* Back button reuses the same look as the ajax-drawer back button. */
body.iframe.ui-refresh .drawer-screen-fields .drawer-back-btn,
body.sidebar.ui-refresh .drawer-screen-fields .drawer-back-btn {
    background: none;
    border: none;
    color: var(--fg-muted);
    cursor: pointer;
    padding: var(--space-1);
    display: inline-flex;
    align-items: center;
    border-radius: var(--radius-sm);
    line-height: 1;
}
body.iframe.ui-refresh .drawer-screen-fields .drawer-back-btn:hover,
body.sidebar.ui-refresh .drawer-screen-fields .drawer-back-btn:hover {
    background: var(--state-hover);
    color: var(--fg-default);
}

/* Force the modal visible — Bootstrap's .modal sets display:none, position:fixed,
   and .fade adds opacity:0 + transform on .modal-dialog. Override all of it. */
.ui-refresh .ajax-drawer .drawer-screen-fields .modal.fade,
.ui-refresh .ajax-drawer .drawer-screen-fields .modal {
    display: block !important;
    position: static !important;
    overflow: visible !important;
    opacity: 1 !important;
    background: transparent !important;
    z-index: auto !important;
    top: auto !important;
    left: auto !important;
    right: auto !important;
    bottom: auto !important;
}
.ui-refresh .ajax-drawer .drawer-screen-fields .modal.fade .modal-dialog,
.ui-refresh .ajax-drawer .drawer-screen-fields .modal .modal-dialog {
    transform: none !important;
    transition: none !important;
}
/* Strip modal chrome inside drawer */
.ui-refresh .ajax-drawer .drawer-screen-fields .modal-content {
    border: none;
    box-shadow: none;
    background: transparent;
    border-radius: 0;
}
.ui-refresh .ajax-drawer .drawer-screen-fields .modal-dialog {
    margin: 0;
    width: 100%;
}
.ui-refresh .ajax-drawer .drawer-screen-fields .modal-header {
    border: none;
    padding: 0 0 var(--space-3) 0;
    display: block;
}
.ui-refresh .ajax-drawer .drawer-screen-fields .modal-header .modal-title {
    display: none;
}
/* Search bar — full width, top of screen, pull-right override */
.ui-refresh .ajax-drawer .drawer-screen-fields .modal-header .pull-right {
    float: none !important;
    margin: 0 !important;
}
.ui-refresh .ajax-drawer .drawer-screen-fields .modal-header .pull-right .form-group {
    margin: 0;
}
.ui-refresh .input-group.input-group-flex {
    display: flex;
}
.ui-refresh .ajax-drawer .drawer-screen-fields #field-search {
    width: 100% !important;
    border-radius: var(--radius-md) !important;
    border: 1px solid var(--border-default);
    font-size: var(--font-size-sm);
    padding: var(--space-2) var(--space-3);
    background: var(--bg-surface);
}
.ui-refresh .ajax-drawer .drawer-screen-fields #field-search:focus {
    border-color: var(--accent-primary);
    outline: none;
    box-shadow: 0 0 0 2px var(--accent-subtle);
}
.ui-refresh .ajax-drawer .drawer-screen-fields #clear-search {
    right: var(--space-3) !important;
    top: 50%;
    transform: translateY(-50%);
    font-size: var(--font-size-lg);
    color: var(--fg-subtle);
    cursor: pointer;
}
.ui-refresh .ajax-drawer .drawer-screen-fields .modal-body {
    padding: 0;
}
.ui-refresh .ajax-drawer .drawer-screen-fields .modal-footer {
    display: none;
}
/* Group section headers — quiet, not competing with content */
.ui-refresh .ajax-drawer .drawer-screen-fields .field-section {
    margin-bottom: 0;
}
.ui-refresh .ajax-drawer .drawer-screen-fields .field-section .header {
    font-size: var(--font-size-xs);
    font-weight: 600;
    color: var(--fg-muted);
    margin-bottom: 0;
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--border-subtle);
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.ui-refresh .ajax-drawer .drawer-screen-fields .field-section .header > a {
    color: var(--fg-muted) !important;
    text-decoration: none !important;
    font-size: var(--font-size-xs);
    font-weight: 600;
}
.ui-refresh .ajax-drawer .drawer-screen-fields .field-section .header .toggle-icon {
    font-size: 10px;
    width: 12px !important;
    transition: transform 0.15s ease;
}
/* "Select All" — smaller, muted, integrated */
.ui-refresh .ajax-drawer .drawer-screen-fields .field-section .select-section {
    font-size: var(--font-size-xs);
    color: var(--fg-subtle);
}
.ui-refresh .ajax-drawer .drawer-screen-fields .field-section .select-section label {
    font-size: var(--font-size-xs);
    color: var(--fg-subtle);
    cursor: pointer;
    margin: 0;
    display: flex;
    align-items: center;
    gap: var(--space-1);
    padding-left: 0 !important;
}
/* Two-column field grid — keep the table layout but tighten it */
.ui-refresh .ajax-drawer .drawer-screen-fields .field-table {
    width: 100%;
}
.ui-refresh .ajax-drawer .drawer-screen-fields .field-table td {
    width: 50%;
    padding: var(--space-1);
    vertical-align: top;
}
/* Each field row — clean, compact, with hover */
.ui-refresh .ajax-drawer .drawer-screen-fields .field-table label {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--font-size-sm);
    color: var(--fg-default);
    font-weight: 400;
    cursor: pointer;
    padding: var(--space-2) var(--space-2) !important;
    margin: 0;
    border-radius: var(--radius-sm);
    transition: background 0.1s ease;
}
.ui-refresh .ajax-drawer .drawer-screen-fields .field-table label:hover {
    background: var(--state-hover);
}
/* Checked fields get a subtle accent background */
.ui-refresh .ajax-drawer .drawer-screen-fields .field-table input[type="checkbox"]:checked + label,
.ui-refresh .ajax-drawer .drawer-screen-fields .field-table label:has(input[type="checkbox"]:checked) {
    background: var(--accent-subtle);
    color: var(--accent-on-tint);
}
/* Checkboxes — standard style, NOT toggle switches */
.ui-refresh .ajax-drawer .drawer-screen-fields .field-table input[type="checkbox"],
.ui-refresh .ajax-drawer .drawer-screen-fields .select-all {
    appearance: auto !important;
    -webkit-appearance: auto !important;
    width: 16px !important;
    min-width: 16px !important;
    height: 16px !important;
    background: initial !important;
    border-radius: var(--radius-sm) !important;
    position: static !important;
    margin: 0 !important;
    flex-shrink: 0;
    cursor: pointer;
    accent-color: var(--accent-primary);
}
.ui-refresh .ajax-drawer .drawer-screen-fields .field-table input[type="checkbox"]::after,
.ui-refresh .ajax-drawer .drawer-screen-fields .select-all::after,
.ui-refresh .ajax-drawer .drawer-screen-fields .field-table input[type="checkbox"]::before,
.ui-refresh .ajax-drawer .drawer-screen-fields .select-all::before {
    content: none !important;
}
/* Collapsible group panels — tighter */
.ui-refresh .ajax-drawer .drawer-screen-fields .panel-collapse .panel-body {
    padding: var(--space-1) 0 var(--space-4);
}
/* Space between groups */
.ui-refresh .ajax-drawer .drawer-screen-fields .panel-collapse + .field-section {
    margin-top: var(--space-2);
}

/* ===== Confirm Dialog ===== */
/* Shared centered confirm dialog — used by delete dashlet, cancel go-live,
 * delete registrant type, and any other destructive/confirmation prompt. */
.ui-refresh .confirm-dialog-backdrop {
    position: fixed;
    top: 0; left: 0; right: 0; bottom: 0;
    background: rgba(0, 0, 0, 0.4);
    z-index: 1050;
    opacity: 0;
    transition: opacity 0.2s ease;
}
.ui-refresh .confirm-dialog-backdrop.in {
    opacity: 1;
}
/* X close button for centered (text-align: center) confirm dialogs */
.ui-refresh .confirm-dialog-close-centered {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    background: none;
    border: none;
    color: var(--fg-muted);
    font-size: var(--font-size-base);
    cursor: pointer;
    padding: var(--space-1);
    line-height: 1;
}
.ui-refresh .confirm-dialog-close-centered:hover {
    color: var(--fg-default);
}
.ui-refresh .confirm-dialog {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%) scale(0.95);
    z-index: 1060;
    background: var(--bg-surface);
    border-radius: var(--radius-xl);
    box-shadow: 0 16px 48px rgba(0, 0, 0, 0.16);
    padding: var(--space-8) var(--space-8) var(--space-6);
    width: 380px;
    max-width: 90vw;
    text-align: center;
    opacity: 0;
    transition: opacity 0.2s ease, transform 0.2s ease;
}
.ui-refresh .confirm-dialog ul li {
    margin-top: var(--space-4);
    text-align: left;
}
.ui-refresh .confirm-dialog.open {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1);
}
.ui-refresh .confirm-dialog__title {
    font-size: var(--font-size-lg);
    font-weight: 600;
    color: var(--fg-default);
    margin: 0 0 var(--space-1);
    /* Titles interpolate a resource name (e.g. Delete "…"?) that can be a
       single long unbroken string with no natural wrap point — without this,
       it overflows the dialog's fixed width instead of wrapping. */
    overflow-wrap: break-word;
    line-height: var(--line-height-lg);
}
.ui-refresh .confirm-dialog__title:has(+ .confirm-dialog__actions) {
    margin-bottom: var(--space-6);
}
.ui-refresh .confirm-dialog__subtitle {
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
    margin: 0 0 var(--space-6);
}
.ui-refresh .confirm-dialog__subtitle ul {
    margin: var(--space-2) 0 0;
    padding-left: var(--space-5);
    text-align: left;
}
/* Stacked key/value detail block for a bodyHtml confirm (e.g. system-actions
   cronlock delete) — the dialog is a fixed 380px, too narrow for
   Bootstrap's dl-horizontal float layout (160px label + 180px margin). */
.ui-refresh .confirm-dialog__subtitle dl {
    margin: var(--space-2) 0 0;
    text-align: left;
}
.ui-refresh .confirm-dialog__subtitle dt {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-bold);
    color: var(--fg-muted);
    margin: var(--space-2) 0 0;
}
.ui-refresh .confirm-dialog__subtitle dt:first-child {
    margin-top: 0;
}
.ui-refresh .confirm-dialog__subtitle dd {
    margin: 0;
    word-break: break-word;
}
.ui-refresh .confirm-dialog__subtitle dl + p {
    margin-top: var(--space-4);
}
.ui-refresh .confirm-dialog__actions {
    display: flex;
    gap: var(--space-3);
    justify-content: center;
}
.ui-refresh .confirm-dialog__actions .btn {
    flex: 1;
    border-radius: var(--radius-md);
}
.ui-refresh .confirm-dialog__actions .btn-danger,
.ui-refresh .confirm-dialog--form .confirm-dialog-footer .btn-danger {
    /* --color-error is tuned as a foreground/text color (intentionally
       lightened in dark mode for legibility on dark paper), which washes out
       when used as a solid button fill. Use the stable red primitive instead
       so the button stays a consistent rich red across themes; hover already
       did this correctly with --red-70, so escalate from --red-60. */
    background: var(--red-60);
    border-color: var(--red-60);
    color: white;
}
.ui-refresh .confirm-dialog__actions .btn-danger:hover,
.ui-refresh .confirm-dialog--form .confirm-dialog-footer .btn-danger:hover {
    background: var(--red-70);
    border-color: var(--red-70);
    color: white;
}

/* Left-aligned confirm dialog variant for form-heavy content (go-live confirmation, etc.) */
.ui-refresh .confirm-dialog--form {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%) scale(0.95);
    z-index: 1060;
    background: var(--bg-surface);
    border-radius: var(--radius-xl);
    box-shadow: 0 16px 48px rgba(0, 0, 0, 0.16);
    padding: var(--space-6);
    width: 480px;
    max-width: 90vw;
    max-height: 80vh;
    overflow-y: auto;
    text-align: left;
    opacity: 0;
    transition: opacity 0.2s ease, transform 0.2s ease;
}
.ui-refresh .confirm-dialog--form.open {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1);
}
/* Content dialog (UiConfirmDialog.openContent) — wider variant for
   detail/log views with more to show than a confirmation prompt. */
.ui-refresh .confirm-dialog--form.confirm-dialog--wide {
    width: 680px;
}
/* Content dialog with a pinned header/footer (UiConfirmDialog.openContent
   adds .confirm-dialog--content to every dialog it builds). Overrides the
   base .confirm-dialog--form "scroll the whole box" behaviour so the header
   (title + X) and footer (Close) stay fixed while only the body scrolls —
   same idea as .acct-switch-modal below, generalized for every content
   dialog instead of just the account switcher. */
.ui-refresh .confirm-dialog--form.confirm-dialog--content {
    display: flex;
    flex-direction: column;
    padding: 0;
    overflow: hidden;
}
.ui-refresh .confirm-dialog--content .confirm-dialog-header {
    flex-shrink: 0;
    padding: var(--space-6) var(--space-6) 0;
}
.ui-refresh .confirm-dialog--content .confirm-dialog-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 0 var(--space-6);
}
.ui-refresh .confirm-dialog--content .confirm-dialog-footer {
    flex-shrink: 0;
    margin-top: 0;
    padding: var(--space-4) var(--space-6) var(--space-6);
}
.ui-refresh .confirm-dialog-loading {
    display: flex;
    justify-content: center;
    padding: var(--space-8) 0;
    color: var(--fg-muted);
    font-size: var(--font-size-lg);
}
.ui-refresh .confirm-dialog--form .confirm-dialog-header {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
}
.ui-refresh .confirm-dialog--form .confirm-dialog-icon {
    width: 40px;
    height: 40px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-full);
    font-size: var(--font-size-lg);
}
.ui-refresh .confirm-dialog--form .confirm-dialog-icon--accent {
    background: var(--accent-subtle);
    color: var(--accent-on-tint);
}
.ui-refresh .confirm-dialog--form .confirm-dialog-title {
    font-size: var(--font-size-lg);
    font-weight: 600;
    color: var(--fg-default);
    margin: 0;
}
.ui-refresh .confirm-dialog--form .confirm-dialog-body {
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
    line-height: var(--line-height-sm);
}
.ui-refresh .confirm-dialog--form .confirm-dialog-body p {
    margin: 0 0 var(--space-3);
}
.ui-refresh .confirm-dialog--form .confirm-dialog-body p:last-child {
    margin-bottom: 0;
}
.ui-refresh .confirm-dialog--form .confirm-dialog-body strong {
    color: var(--fg-default);
    font-weight: 600;
}
/* A prompt that spells out several consequences reads as a list; the browser
   default indent sits far off the message it belongs to. */
.ui-refresh .confirm-dialog--form .confirm-dialog-body ul {
    margin: var(--space-2) 0 0;
    padding-left: var(--space-5);
}
.ui-refresh .confirm-dialog--form .confirm-dialog-body li + li {
    margin-top: var(--space-1);
}
.ui-refresh .confirm-dialog--form .confirm-dialog-footer {
    display: flex;
    gap: var(--space-3);
    justify-content: flex-end;
    margin-top: var(--space-5);
    padding-top: var(--space-4);
    border-top: 1px solid var(--border-subtle);
}
.ui-refresh .confirm-dialog--form .confirm-dialog-footer .btn {
    border-radius: var(--radius-md);
}
/* Form elements inside the dialog */
.ui-refresh .confirm-dialog--form .form-group {
    margin-bottom: var(--space-3);
}
.ui-refresh .confirm-dialog--form .form-control {
    font-size: var(--font-size-sm);
}
.ui-refresh .confirm-dialog--form .help-block {
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
}
.ui-refresh .confirm-dialog--form .alert {
    font-size: var(--font-size-sm);
    margin-bottom: var(--space-3);
}
/* Checkbox toggle inside the dialog should sit inline next to label, not far-right.
   Bootstrap + active-form.css set padding-left: 20px on .checkbox label — must use
   !important to override since active-form.css uses !important on .new-form .checkbox label. */
.ui-refresh .confirm-dialog--form .checkbox {
    padding: 0;
    border-bottom: none;
    margin-bottom: 0;
}
.ui-refresh .confirm-dialog--form .checkbox label {
    flex-direction: row;
    justify-content: flex-start;
    gap: var(--space-2);
    width: auto;
    padding-left: 0 !important;
}
/* bootstrap-multiselect dropdown (e.g. "Add Account"'s account picker): same
   overrides as .ajax-drawer .multiselect-container below, needed here too since
   this dialog isn't inside #ajax-drawer — Bootstrap's own defaults otherwise leave
   the option/filter rows with their stock padding-left and li+li margin-top. */
.ui-refresh .confirm-dialog--form .multiselect-container.dropdown-menu {
    min-width: 100%;
    width: fit-content;
    max-height: 320px;
    overflow-y: auto;
    padding: var(--space-1) 0;
    border-radius: var(--radius-lg);
    border: 1px solid var(--border-subtle);
    box-shadow: var(--sh-md);
    background: var(--bg-surface);
}
.ui-refresh .confirm-dialog--form .multiselect-container > li > a,
.ui-refresh .confirm-dialog--form .multiselect-container > li > label {
    padding: 0;
}
.ui-refresh .confirm-dialog--form .multiselect-container .checkbox {
    justify-content: flex-start;
    border-bottom-color: var(--border-subtle);
    transition: background-color var(--transition-fast);
}
.ui-refresh .confirm-dialog--form .multiselect-container .checkbox:hover {
    background: var(--state-hover);
}
.ui-refresh .confirm-dialog--form .confirm-dialog-body .multiselect-container li + li {
    margin-top: 0;
}
/* The filter row is the dropdown's first li (no preceding sibling), so the
   li + li reset above never reaches it — it needs its own. */
.ui-refresh .confirm-dialog--form .confirm-dialog-body .multiselect-container .multiselect-filter,
.ui-refresh .confirm-dialog--form .confirm-dialog-body .multiselect-container .multiselect-item.filter {
    margin-top: 0;
}
/* Hide legacy <br> spacers — the dialog has its own spacing via tokens */
.ui-refresh .confirm-dialog--form .confirm-dialog-body br {
    display: none;
}
/* Test Response dialog: breathing room under the result alert, and the legacy
   Bootstrap striped table from transaction/_response.php restyled as a
   key/value card. Scoped via .response-kv so shared legacy pages that render
   the same partial keep their current look. */
.ui-refresh .confirm-dialog--form .inline-alert {
    margin-bottom: var(--space-4);
}
.ui-refresh .response-kv {
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
}
.ui-refresh .response-kv .table {
    width: 100%;
    margin: 0;
    background: transparent;
}
.ui-refresh .response-kv .table > tbody > tr {
    background: transparent;
}
.ui-refresh .response-kv .table > tbody > tr > td {
    /* Legacy backend CSS draws a border-bottom on every td — inside the card
       it doubles up with the card's own bottom edge. Rows get their divider
       from the tr + tr border-top rule below instead. */
    border-top: none;
    border-bottom: none;
    padding: var(--space-3) var(--space-4);
    vertical-align: top;
    font-size: var(--font-size-sm);
    line-height: var(--line-height-sm);
    color: var(--fg-default);
    word-break: break-word;
}
.ui-refresh .response-kv .table > tbody > tr + tr > td {
    border-top: 1px solid var(--line);
}
/* Key column: narrow and muted. Colspan rows ("no details", duplicate
   transaction) span the card full-width and are excluded. */
.ui-refresh .response-kv .table > tbody > tr > td:first-child:not([colspan]) {
    width: 1%;
    white-space: nowrap;
    font-weight: 500;
    color: var(--fg-muted);
}
.ui-refresh .response-kv .table p {
    margin: 0;
}
/* Footer without the hairline: when the dialog body already ends in a
   bordered card (Test Response), the shared footer border-top reads as a
   stray double line. */
.ui-refresh .confirm-dialog--form .confirm-dialog-footer--flush {
    border-top: none;
    padding-top: 0;
}
/* Non-scalar values render as a <details><pre> JSON dump. */
.ui-refresh .response-kv .table pre {
    margin: var(--space-2) 0 0;
    padding: var(--space-3);
    background: var(--bg-page);
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    font-size: var(--font-size-xs);
    overflow-x: auto;
}
/* Hide the iframe-modal-footer from the AJAX-loaded form — we use our own footer */
.ui-refresh .confirm-dialog--form .iframe-modal-footer,
.ui-refresh .confirm-dialog--form .modal-footer {
    display: none;
}
/* Force horizontal forms to stack vertically inside the narrow dialog.
   Kartik ActiveForm TYPE_HORIZONTAL wraps inputs in col-sm-* divs and floats labels.
   We need to undo all of that with !important since Bootstrap grid uses !important on some widths. */
.ui-refresh .confirm-dialog--form .form-horizontal .control-label {
    text-align: left !important;
    float: none !important;
    width: 100% !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
}
.ui-refresh .confirm-dialog--form .form-horizontal [class*="col-"] {
    float: none !important;
    width: 100% !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
}
.ui-refresh .confirm-dialog--form .form-horizontal .form-group {
    margin-left: 0 !important;
    margin-right: 0 !important;
}
/* Close button in header */
.ui-refresh .confirm-dialog--form .confirm-dialog-close {
    margin-left: auto;
    background: none;
    border: none;
    color: var(--fg-muted);
    font-size: var(--font-size-lg);
    cursor: pointer;
    padding: var(--space-1);
    line-height: 1;
}
.ui-refresh .confirm-dialog--form .confirm-dialog-close:hover {
    color: var(--fg-default);
}

/* ── Automation save-confirmation dialog (confirm-dialog--form variant) ── */
.ui-refresh .automation-confirm-summary {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: var(--space-2) var(--space-4);
    margin: var(--space-4) 0 0;
}
.ui-refresh .automation-confirm-summary dt {
    color: var(--fg-muted);
    font-weight: var(--font-weight-medium);
    margin-top: 0;
}
.ui-refresh .automation-confirm-summary dd {
    margin: 0;
    color: var(--fg-default);
}
.ui-refresh .automation-confirm-na {
    color: var(--fg-subtle);
    font-style: italic;
}
.ui-refresh .automation-confirm-status--active {
    color: var(--color-success);
    font-weight: var(--font-weight-semibold);
}
.ui-refresh .automation-confirm-status--inactive {
    color: var(--color-error);
    font-weight: var(--font-weight-semibold);
}
.ui-refresh .automation-confirm-warn {
    color: var(--color-warning);
    font-style: italic;
}
.ui-refresh .confirm-dialog--form .inline-alert {
    margin-top: var(--space-4);
}

/* ===== Translate Button Addon (Translation Input Group) =====
 *
 * `TranslationInput` widget renders the field as a Bootstrap input-group with
 * a `.input-group-addon.translations.btn.btn-default` toggle on the right
 * (label: "Translate"). In dark mode the inherited Bootstrap `.btn-default`
 * leaves the addon's LEFT border at the muted text color (`rgb(194,189,175)`)
 * while top/right/bottom borders use a much darker shade — producing a sharp
 * bright vertical line between the input and the toggle that reads as broken.
 * Normalize all four borders to the subtle border token in dark mode so the
 * addon visually merges with the input it's part of. Same fix applies to the
 * `.google-translate` sibling addon and to the per-language `lang-name` addons
 * that appear when translations are expanded. */
/* The Translate toggle is an `.input-group-addon` that ALSO has `.btn` on it
 * (Bootstrap markup). Our ui-refresh `.btn:not(.x):not(.y)...` rule globally
 * forces `display: inline-flex` on every button — and the 6-deep `:not()`
 * chain inflates its specificity to (0,8,0), so a normal-weight override
 * with class selectors alone can't beat it. The inline-flex display breaks
 * out of the Bootstrap `.input-group { display: table }` layout, dropping
 * the addon to a new line under the input. Force `table-cell` so the
 * addon sits flush to the right of the field as Bootstrap intended. */
.ui-refresh .input-group-addon.btn {
    display: table-cell !important;
}

.ui-refresh .input-group-addon.google-translate {
    box-shadow: none;
}

.ui-refresh .translation-langs .input-group:first-child,
.ui-refresh .translation-langs .input-group {
    margin-top: 0;
}
.ui-refresh .translations-input .input-group-addon:first-child {
    border-top-left-radius: var(--input-radius) !important;
    border-bottom-left-radius: var(--input-radius) !important;
}
.ui-refresh .ui-refresh .input-group-addon {
    background-color: var(--bg-muted) !important;
}

/* TranslationInput inside the slide-out sidebar form (reg-type drawer
 * and similar): the legacy widget renders `[English pill][input][Translate ↓]`
 * — the "English" lang-name addon on the left squares off the input's
 * leading edge (Bootstrap's `.input-group .form-control:not(:first-child)`
 * radius reset) and reads as heavy chrome inside the drawer's tighter
 * layout. Hide the pill so the input behaves like a normal text field with
 * a rounded leading edge. The right-side "Translate" toggle stays — it's
 * how the user reveals the additional-language rows. The consolidated
 * event-Settings page does the same thing via a tab strip; this is the
 * sidebar's lighter version of that idea.
 *
 * Re-round the input's leading edge explicitly: with the addon `display:none`
 * the input is still `:not(:first-child)` per the DOM (`:first-child`
 * doesn't care about visibility), so Bootstrap's middle-of-input-group
 * reset keeps firing. `.new-form .form-control` already sets all-around
 * 8px with `!important`, but the legacy `.translations-input:not(.lang-hidden)
 * .form-control` rule zeroes the left corners (also `!important`) the
 * moment the user clicks Translate. Pin the leading-edge radius here so
 * the rounded corner survives both the collapsed and expanded states. */
body.sidebar.ui-refresh .translations-input .input-group-addon.lang-name {
    display: none;
}
body.sidebar.ui-refresh .translations-input .input-group > .form-control {
    border-top-left-radius: var(--input-radius) !important;
    border-bottom-left-radius: var(--input-radius) !important;
}

/* ===== Visibility Tab Sections =====
 *
 * The reg-type visibility tab stacks six `.vis-section` blocks (visibility to
 * all / new vs modifying / primary vs secondary / dates / admin-only / devices).
 * Toggle groups now provide their own card boundaries, so the sections only
 * need spacing; an additional divider between cards would be redundant. */
.ui-refresh .sidebar-tab-panel[data-tab-panel="visibility"] .vis-section {
    padding: var(--space-1) 0;
    /* Legacy backend.css adds `margin-bottom: 15px` + `padding-bottom: 20px`
     * to `.panel-body .vis-section` — combined with my padding+border that's
     * ~40px of dead space between sections. Reset margin so spacing comes
     * solely from my padding rule. */
    margin: 0;
    border-bottom: 0;
}

/* Same `.vis-section` stack, but on the full-page fees-and-payments setup
 * forms (e.g. the Payment Plan create/update Visibility Settings section,
 * which renders the shared visibility partials inside `.fee-grid-section-body`
 * rather than the drawer's visibility tab). Without this the toggle of one
 * question butts directly against the lede of the next. Give each section the
 * same padded, separated rhythm and heading-weight lede. */
.ui-refresh .list-page .fee-grid-section-body .vis-section {
    padding: var(--space-2) 0;
    margin: 0;
}
.ui-refresh .list-page .fee-grid-section-body .vis-section:first-child {
    padding-top: 0;
}
.ui-refresh .list-page .fee-grid-section-body .vis-section:last-child {
    padding-bottom: 0;
}
.ui-refresh .list-page .fee-grid-section-body .vis-section > p:first-child {
    font-weight: var(--font-weight-medium);
    color: var(--fg-default);
    margin: 0 0 var(--space-1);
}

/* Smooth out the Bootstrap collapse jumps for "switches that open other
 * things" inside the Visibility tab (visible-to-all toggle exposing the
 * condition builder, date range toggles, admin-only toggle, etc.).
 *
 * Two things cause "snap-at-end-of-animation" jumps:
 *   1. Margin on the .collapse element — not part of animated height.
 *   2. Padding on the .collapse element — persists as residual height when
 *      Bootstrap finishes the animation and applies `display: none`, then
 *      that residual height snaps away (visible y-shift of next content).
 *
 * Defence: zero both margin and padding on the collapse element itself.
 * All vertical spacing comes from the inner `.condition-input-body`
 * wrapper (added in _form.php), which IS part of the animated height. */
.ui-refresh .sidebar-tab-panel[data-tab-panel="visibility"] .collapse,
.ui-refresh .vis-section .collapse,
.ui-refresh .vis-section .collapsing {
    margin: 0;
    padding: 0;
}
.ui-refresh .vis-section .collapse > :first-child,
.ui-refresh .vis-section .collapsing > :first-child {
    margin-top: var(--space-3);
}
.ui-refresh .sidebar-tab-panel[data-tab-panel="visibility"] .collapse > .form-group:last-child,
.ui-refresh .sidebar-tab-panel[data-tab-panel="visibility"] .collapse > *:last-child {
    margin-bottom: 0;
}

.ui-refresh .sidebar-tab-panel[data-tab-panel="visibility"] .condition-input-body {
    /* Replaces the legacy inline `style="padding-top:10px"` on the
     * collapse element. Padding here lives inside the collapse, so its
     * height is part of the animated transition — no snap. */
    padding-top: var(--space-2);
}
.ui-refresh .sidebar-tab-panel[data-tab-panel="visibility"] .vis-section:first-child {
    padding-top: 0;
}
.ui-refresh .sidebar-tab-panel[data-tab-panel="visibility"] .vis-section:last-child {
    padding-bottom: 0;
}
/* The section's lede sentence ("Should the reg type be visible to ...?")
 * should read as a section heading, not body copy. */
.ui-refresh .ajax-drawer .vis-section > p:first-child {
    font-weight: var(--font-weight-medium);
    font-size: var(--font-size-input);
    color: var(--fg-default);
    margin: 0 0 var(--space-2);
}
/* Legacy indent spacers (`<span class="hidden-xs">&nbsp;&nbsp;</span>`) inside
   shared visibility-section views are only needed for the old non-flex modal
   layout. In the sidebar they push checkboxes out of alignment. */
.ui-refresh .sidebar-tab-panel .checkbox .hidden-xs {
    display: none !important;
}
/* Nested cards inside a tab — keep modest chrome so they read as a
 * grouped sub-section but use design tokens so they're dark-mode-clean. */
.ui-refresh .sidebar-tab-panel .panel.panel-default .panel.panel-default {
    background: var(--bg-muted);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    box-shadow: none;
}

/* ===== Groups Tab tidy-up =====
   The min/max group-size fields render in a Bootstrap `.row > .col-sm-6`
   pair that stacks at the sidebar's xs width — 148px of vertical space for
   two number inputs. There's plenty of horizontal room in the drawer, so
   force the row into a true 2-col flex layout regardless of viewport
   width. That alone halves the row height. Also tighten the form-group
   margins, demote the intro `<p>` to a smaller section-caption read, and
   flatten the nested `#additional-attendee-limits` panel chrome so the
   "limit reg types group members can choose" sub-section reads as a
   continuation of the tab rather than a noisy box-in-box. */
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] .panel-body > .row,
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] #additional-attendee-limits .row {
    display: flex;
    flex-wrap: nowrap;
    gap: var(--space-3);
    margin-left: 0;
    margin-right: 0;
}
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] #additional-attendee-limits .row {
    margin-bottom: var(--space-1);
}
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] #additional-attendee-limits .row .form-control {
    background: var(--paper) !important;
}
/* Bootstrap's `.row::before` / `.row::after` are clearfix pseudo-elements
   (`content: " "; display: table`). They're harmless in legacy float-based
   rows but become flex items when we switch the row to flex — the `::before`
   takes position 0 and pushes the real first col 12px right of the row's
   leading edge, putting the field out of alignment with the section
   heading. Hide them for the flex rows. */
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] .panel-body > .row::before,
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] .panel-body > .row::after,
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] #additional-attendee-limits .row::before,
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] #additional-attendee-limits .row::after {
    display: none;
}
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] .panel-body > .row > [class*="col-"],
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] #additional-attendee-limits .row > [class*="col-"] {
    flex: 1;
    padding-left: 0;
    padding-right: 0;
    width: auto;
    min-width: 0;
}
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] .form-group {
    margin-bottom: var(--space-3);
}
/* Flatten the conditional "limit reg types" nested panel — drop the
   subtle bg + border that the generic nested-card rule applies, since
   here it reads as a noisy box-in-box rather than a grouping cue. */
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] #additional-attendee-limits.panel.panel-default {
    background: transparent;
    border: none;
    border-radius: 0;
    box-shadow: none;
    margin-bottom: 0;
}

.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] #additional-attendee-limits.panel.panel-default > .panel-body {
    padding: 0;
}

/* Inline reg-type limit rows — checkbox + name on the left, QuantityInput
   on the right. The QuantityInput's `<select>` is the only "form control"
   here (the companion text input shows only for the "10+" / custom case),
   so narrow it down and add the missing chevron + ui-refresh chrome — the
   legacy native select reads dramatically out of place at 688px wide. */
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] .additional-reg-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-2) 0;
    border-bottom: 1px solid var(--border-subtle);
}
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] .additional-reg-row:last-child {
    border-bottom: none;
}
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] .additional-reg-row > .checkbox {
    flex: 1;
    min-width: 0;
    margin: 0;
}
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] .additional-reg-quantity {
    flex: 0 0 auto;
    width: 200px;
    max-width: 200px;
}
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] .additional-reg-quantity select.form-control {
    width: 100%;
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2212%22%20height%3D%2212%22%20viewBox%3D%220%200%2012%2012%22%3E%3Cpath%20d%3D%22M10.293%2C3.293%2C6%2C7.586%2C1.707%2C3.293A1%2C1%2C0%2C0%2C0%2C.293%2C4.707l5%2C5a1%2C1%2C0%2C0%2C0%2C1.414%2C0l5-5a1%2C1%2C0%2C1%2C0-1.414-1.414Z%22%20fill%3D%22%23a8a39a%22%2F%3E%3C%2Fsvg%3E");
    background-size: 0.6em;
    background-position: calc(100% - 12px) center;
    background-repeat: no-repeat;
    padding-right: 30px;
}
/* When unchecked, the QuantityInput stays in place but reads as
   inactive: muted text + slightly dimmer bg so the user understands
   they need to tick the row to make it editable. */
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] .additional-reg-row:not(.is-checked) .additional-reg-quantity select.form-control,
.ui-refresh .sidebar-tab-panel[data-tab-panel="groups"] .additional-reg-row:not(.is-checked) .additional-reg-quantity input.form-control {
    color: var(--fg-subtle);
    opacity: 0.7;
}

/* ===== Sidebar Popover Sizing =====
   The sidebar renders in an iframe ~720px wide, which trips the
   `@media (max-width: 768px) .popover { min-width: 250px;
   max-width: calc(100vw - 20px) }` rule in legacy backend.css. That rule
   exists for true mobile and inflates popovers to ~700px wide — far too
   large for a narrow drawer, so `placement: 'left'` popovers near the right
   edge get clipped off the left of the iframe. Pin sidebar popovers to a
   reasonable, drawer-appropriate width regardless of the iframe's pixel
   width. */
body.sidebar.ui-refresh .popover {
    min-width: 0 !important;
    max-width: 276px !important;
}

/* Bootstrap popovers hardcode a white body, light border, and white arrow
   fill, so they render as bright panels in dark mode. Point the surface,
   ink, border, and arrow-fill at flipping tokens so a single base rule is
   correct in both light and dark. The :has(.popup-img)/:has(.popover-skeleton)
   variants above carry higher specificity and keep their bespoke styling. */
.ui-refresh .popover {
    background: var(--bg-surface);
    color: var(--fg-default);
    border-color: var(--border-strong);
    box-shadow: var(--sh-lg);
}
.ui-refresh .popover-title {
    background: var(--bg-muted);
    color: var(--fg-default);
    border-bottom-color: var(--border-strong);
}
.ui-refresh .popover-content {
    color: var(--fg-default);
}

/* Bootstrap Confirmation popover (e.g. the resend-invitation "Are you sure?"
   prompt). The .popover chrome above already tokenises the box/title; this
   restyles the confirm/deny buttons — which are raw Bootstrap .btn-success /
   .btn-danger — into the ui-refresh dialog pattern: the confirming action is a
   filled accent button, the dismiss is a quiet neutral button. Override bg +
   border + color together to beat Bootstrap's contextual button rules. */
.ui-refresh .popover.confirmation .confirmation-buttons {
    display: flex;
    gap: var(--space-2);
    padding-top: var(--space-1);
}
.ui-refresh .popover.confirmation .confirmation-buttons .btn-group {
    display: flex;
    gap: var(--space-2);
    width: 100%;
}
/* Bootstrap .btn-group merges adjacent buttons — removes inner border-radii and
   adds a 1px divider between them. Undo that so each button keeps its own
   independent rounded corners and no seam appears. */
.ui-refresh .popover.confirmation .confirmation-buttons .btn-group > .btn + .btn {
    border-left: inherit;
    margin-left: 0;
}
.ui-refresh .popover.confirmation .btn {
    flex: 1;
    border-radius: var(--radius-md) !important;
    font-weight: var(--font-weight-medium);
}
/* Confirm ("Yes") — filled accent. */
.ui-refresh .popover.confirmation .btn-success {
    background: var(--accent-primary);
    border-color: var(--accent-primary);
    color: var(--fg-on-accent);
}
.ui-refresh .popover.confirmation .btn-success:hover,
.ui-refresh .popover.confirmation .btn-success:focus {
    background: var(--accent-hover);
    border-color: var(--accent-hover);
    color: var(--fg-on-accent);
}
/* Dismiss ("No") — quiet neutral. */
.ui-refresh .popover.confirmation .btn-danger {
    background: var(--bg-surface);
    border-color: var(--border-default);
    color: var(--fg-default);
}
.ui-refresh .popover.confirmation .btn-danger:hover,
.ui-refresh .popover.confirmation .btn-danger:focus {
    background: var(--state-hover);
    border-color: var(--border-default);
    color: var(--fg-default);
}

.ui-refresh .popover.top > .arrow { border-top-color: var(--border-strong); }
.ui-refresh .popover.top > .arrow::after { border-top-color: var(--bg-surface); }
.ui-refresh .popover.bottom > .arrow { border-bottom-color: var(--border-strong); }
.ui-refresh .popover.bottom > .arrow::after { border-bottom-color: var(--bg-muted); }
.ui-refresh .popover.left > .arrow { border-left-color: var(--border-strong); }
.ui-refresh .popover.left > .arrow::after { border-left-color: var(--bg-surface); }
.ui-refresh .popover.right > .arrow { border-right-color: var(--border-strong); }
.ui-refresh .popover.right > .arrow::after { border-right-color: var(--bg-surface); }

/* Code popover (JS Snippets "view code"): a titled header + a monospaced,
   scrollable code block, replacing the legacy striped-table markup. Allow the
   popover to widen past the default 276px so code has room to read. */
.ui-refresh .popover:has(.code-popover) {
    max-width: 600px;
}
/* The code popover has no muted title bar (its top is the surface-coloured
   header), so point the bottom-placement arrow fill at the surface, not the
   --bg-muted the base rule assumes. */
.ui-refresh .popover.bottom:has(.code-popover) > .arrow::after {
    border-bottom-color: var(--bg-surface);
}
.ui-refresh .popover .code-popover {
    position: relative;
}
.ui-refresh .popover .code-popover__code {
    margin: 0;
    min-width: 320px;
    max-width: 560px;
    max-height: 460px;
    overflow: auto;
    padding: var(--space-3);
    background: var(--bg-muted);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    font-family: var(--font-mono);
    font-size: 12px;
    line-height: 1.5;
    color: var(--fg-default);
    white-space: pre;
    tab-size: 2;
}
/* Floating copy button — bottom-right corner of the code block. */
.ui-refresh .popover .code-popover__copy {
    position: absolute;
    bottom: var(--space-2);
    right: var(--space-2);
    display: grid;
    place-items: center;
    width: 26px;
    height: 26px;
    padding: 0;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--bg-surface);
    color: var(--muted);
    font-size: 12px;
    cursor: pointer;
    opacity: 0.85;
    transition: color var(--transition-fast), background var(--transition-fast),
                border-color var(--transition-fast), opacity var(--transition-fast);
}
.ui-refresh .popover .code-popover__copy:hover {
    color: var(--ink);
    background: var(--bg-muted);
    opacity: 1;
}
/* Copied confirmation state (JS toggles .is-copied and swaps the icon). */
.ui-refresh .popover .code-popover__copy.is-copied {
    color: var(--accent-primary);
    border-color: var(--accent-muted);
    opacity: 1;
}

/* ===== Early Bird Dates Table (sidebar) =====
   The legacy table at fee/_form.php has TWO column layouts driven by
   `hidden-xs` / `visible-xs`:
   - sm+: 2-col table with "If Registered By | Price" headers.
   - xs : date column hidden; an inline `By [date]` label stacks above each
          price input (verbose, vertical, ~140px per row).
   The sidebar iframe is 720px (xs by Bootstrap's breakpoint) so we get the
   stacked version even though there's plenty of horizontal room. Force the
   table back into its 2-col form, tighten cell padding, neutralize the
   table-striped tint, and re-style the header + footer-row Add link so it
   reads as a clean, dense list. Scoped to the sidebar so the legacy modal
   flow at xs (real mobile) is untouched. */
.ui-refresh #early-date-table {
    margin: var(--space-3) 0 0;
    border-collapse: separate;
    border-spacing: 0;
}
/* Bootstrap's `.hidden-xs { display: none !important }` and
   `.visible-xs { display: block !important }` both use !important, so
   the overrides have to as well. */
.ui-refresh #early-date-table tr.hidden-xs {
    display: table-row !important;
}
.ui-refresh #early-date-table td.hidden-xs,
.ui-refresh #early-date-table th.hidden-xs {
    display: table-cell !important;
}
.ui-refresh #early-date-table label.visible-xs {
    display: none !important;
}
.ui-refresh #early-date-table thead th {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-muted);
    border-bottom: 1px solid var(--border-subtle);
    padding: var(--space-2) var(--space-3);
}
/* Neutralize Bootstrap's `.table-striped tbody tr:nth-of-type(odd)` tint —
   it reads as accidental highlight against the panel-body background. */
.ui-refresh #early-date-table.table-striped > tbody > tr:nth-of-type(odd),
.ui-refresh #early-date-table.table-striped > tbody > tr:nth-of-type(even) {
    background-color: transparent;
}
.ui-refresh #early-date-table tbody td {
    vertical-align: middle;
    padding: var(--space-2) var(--space-3);
    border-top: 1px solid var(--border-subtle);
    color: var(--fg-default);
    font-size: var(--font-size-sm);
}
/* When date is in the first cell, keep it left-aligned and prevent the long
   date string from wrapping into multiple lines. */
.ui-refresh #early-date-table thead th:first-child,
.ui-refresh #early-date-table tbody td:first-child {
    white-space: nowrap;
    width: 1%;
    color: var(--fg-default);
    font-weight: var(--font-weight-medium);
}
/* The price field cell hosts an ActiveForm `.form-group` that comes with
   default margin-bottom 20px. In a single-line row that pushes the input
   well below the date label and creates uneven row heights. Zero the
   form-group margin so the input centers cleanly. */
.ui-refresh #early-date-table tbody td .form-group {
    margin-bottom: 0;
}
/* Footer: a single colspan=2 cell hosts the timezone caption + a
   full-width dashed "Add Early Bird Date" ghost button (markup in
   fee/_form.php is gated on isUiRefreshEnabled). The button matches the
   existing `.field-picker-add-btn` pattern in the ajax-drawer field
   picker — full-width dashed border, muted text, accent tint on hover. */
.ui-refresh #early-date-table tfoot td.ebd-tfoot-cell {
    padding: var(--space-2) var(--space-3);
    border-top: 1px solid var(--border-subtle);
}
.ui-refresh #early-date-table .ebd-tfoot-tz {
    text-align: right;
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
    margin-bottom: var(--space-2);
}
.ui-refresh #early-date-table .ebd-tfoot-add .btn-default {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    width: 100%;
    border: 1px dashed var(--border-default);
    background: transparent;
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    padding: var(--space-2) 0;
    border-radius: var(--radius-md);
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.ui-refresh #early-date-table .ebd-tfoot-add .btn-default:hover {
    border-color: var(--accent-primary);
    color: var(--accent-on-tint);
    background: var(--accent-subtle);
}
/* Hide the xs-only duplicate "Add Early Bird Date" button rendered below
   the table — the tfoot version is visible now. */
.ui-refresh .early-date-settings > .visible-xs.text-center {
    display: none !important;
}

/* ===== Reorder Drawer ===== */
/* Dense vertical list used by reg-type's reorder sidebar (and intended to
 * be the shared shape for any future "drag-to-reorder a long list" drawer).
 * Each row carries a `.reorder-drag` handle on the left, the item name in
 * the middle, and a hidden sort[] input that rides along with the DOM
 * order. jQuery UI sortable rewrites that order on drop. */
.ui-refresh .ajax-drawer .reorder-lede {
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
    margin: 0 0 var(--space-4);
}
.ui-refresh .reorder-list {
    list-style: none;
    padding: 0;
    margin: 0;
}
.ui-refresh .reorder-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    margin-bottom: var(--space-2);
    cursor: grab;
    user-select: none;
    transition: background-color var(--transition-fast),
                box-shadow var(--transition-fast),
                border-color var(--transition-fast);
}
.ui-refresh .reorder-row:hover {
    background: var(--bg-muted);
    border-color: var(--border-default);
}
.ui-refresh .reorder-row.ui-sortable-helper {
    cursor: grabbing;
    box-shadow: var(--shadow-md);
    background: var(--bg-surface);
}
/* The sortable placeholder is the ghost that marks the drop target. */
.ui-refresh .reorder-row-placeholder {
    background: var(--bg-muted);
    border: 1px dashed var(--border-default);
    box-shadow: none;
    visibility: visible !important;
}
.ui-refresh .reorder-drag {
    color: var(--fg-muted);
    flex-shrink: 0;
    user-select: none;
    cursor: grab;
    letter-spacing: -2px;
    line-height: 1;
}
.ui-refresh .reorder-row.ui-sortable-helper .reorder-drag {
    cursor: grabbing;
}
.ui-refresh .reorder-name {
    flex: 1;
    color: var(--fg-default);
    font-weight: var(--font-weight-medium);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Two-line row variant (automations reorder drawer): name + a muted
   trigger/action preview line underneath, plus an optional trailing state
   label (e.g. "Disabled"). */
.ui-refresh .reorder-text {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    min-width: 0;
    flex: 1;
}
.ui-refresh .reorder-text .reorder-name {
    flex: none;
}
.ui-refresh .reorder-meta {
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ui-refresh .reorder-state {
    color: var(--fg-subtle);
    font-size: var(--font-size-sm);
    flex-shrink: 0;
}
/* Speaker reorder drawer: the event's speaker display format can include the
   photo field, so Speaker::getFormattedName() hands us a name with an <img>
   baked into it. Render it as an avatar chip rather than letting a full-size
   upload set the row height — the legacy iframe modal capped the same image
   with an inline #speaker-order img rule (speaker/order.php). */
.ui-refresh .speaker-reorder-list .reorder-name img {
    width: 24px;
    height: 24px;
    border-radius: var(--radius-full);
    object-fit: cover;
    vertical-align: middle;
    margin-right: var(--space-2);
}

/* ===== Modal: Close Button ===== */
/* App.loadModal() creates <button class="close">x</button> in JS.
   Hide the text "x" and replace with a Font Awesome icon via pseudo-element. */
.ui-refresh .modal-header .close {
    font-size: 0;
    width: 32px;
    height: 32px;
    border-radius: var(--radius-md);
    opacity: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background var(--transition-fast);
    margin: 0;
    padding: 0;
}
.ui-refresh .modal-header .close::after {
    content: '\f00d';
    font-family: "Font Awesome 6 Pro", sans-serif;
    font-weight: 400;
    font-size: var(--font-size-base);
    color: var(--fg-muted);
}
.ui-refresh .modal-header .close:hover {
    background: var(--state-hover);
}
.ui-refresh .modal-header .close:hover::after {
    color: var(--fg-default);
}

/* ===== Dropdowns ===== */
.ui-refresh .dropdown-menu {
    /* Bootstrap 3 hardcodes `.dropdown-menu { background-color: #fff }`, which
       leaks a white panel in dark mode. Point at the surface token so it
       flips automatically; --sh-lg (unlike --shadow-lg) also has a dark
       override so the shadow stays visible on dark paper. */
    background-color: var(--bg-surface);
    border-radius: var(--radius-lg);
    border: 1px solid var(--border-default);
    box-shadow: var(--sh-lg);
    padding: var(--space-1) 0;
}
.ui-refresh .dropdown-menu > li > a {
    padding: var(--space-2) var(--space-4);
    font-size: var(--font-size-sm);
    color: var(--fg-default);
    transition: background-color var(--transition-fast);
}
.ui-refresh .dropdown-menu > li > a:hover,
.ui-refresh .dropdown-menu > li > a:focus {
    background-color: var(--state-hover);
    color: var(--fg-default);
}
/* Active item: subtle accent tint instead of solid purple block */
.ui-refresh .dropdown-menu > li.active > a,
.ui-refresh .dropdown-menu > li.active > a:hover,
.ui-refresh .dropdown-menu > li.active > a:focus {
    background-color: var(--accent-subtle) !important;
    color: var(--accent-on-tint) !important;
    font-weight: var(--font-weight-medium);
}
/* Disabled item (e.g. "Delete Field" on a standard, non-deletable field):
   Bootstrap 3's own .disabled rule only dims the text color, which barely
   reads on ink-on-paper surfaces. Fade the whole row instead, matching the
   opacity-based disabled treatment used elsewhere (.rf-icon-btn.disabled in
   reg-form.css). The item's own linkOptions carry disabled semantics and a
   focusable tooltip; this only handles the visual. */
.ui-refresh .dropdown-menu > li.disabled > a,
.ui-refresh .dropdown-menu > li.disabled > a:hover,
.ui-refresh .dropdown-menu > li.disabled > a:focus {
    opacity: .45;
    cursor: not-allowed;
    background-color: transparent;
    color: var(--fg-default);
}
/* Dropdown submenu (hover flyout, flies left) */
.ui-refresh .dropdown-submenu {
    position: relative;
}
.ui-refresh .dropdown-submenu > ul {
    display: none;
    position: absolute;
    top: 0;
    right: 100%;
    margin-top: -4px;
    margin-right: 2px;
    max-height: 400px;
    overflow-y: auto;
    border-radius: var(--radius-lg);
    border: 1px solid var(--border-default);
    box-shadow: var(--shadow-lg);
    background: var(--bg-surface);
    padding: var(--space-1) 0;
    min-width: 220px;
    z-index: 1051;
    list-style: none;
}
.ui-refresh .dropdown-submenu:hover > ul {
    display: block;
}
.ui-refresh .dropdown-submenu > ul > li > a {
    display: block;
    padding: var(--space-2) var(--space-4);
    font-size: var(--font-size-sm);
    color: var(--fg-default);
    text-decoration: none;
    white-space: nowrap;
    transition: background-color var(--transition-fast);
}
.ui-refresh .dropdown-submenu > ul > li > a:hover,
.ui-refresh .dropdown-submenu > ul > li > a:focus {
    background-color: var(--state-hover);
    color: var(--fg-default);
}
/* Divider: subtle and thin.
   Yii's Dropdown widget adds `dropdown-header` to items with null label/url
   (bootstrap-contributed padding: 3px 20px), which inflates the divider.
   Neutralize that padding so the divider stays 1px tall. */
.ui-refresh .dropdown-menu > .divider {
    margin: var(--space-1) 0;
    padding: 0;
    background-color: var(--border-subtle);
}
/* Group headers inside dropdown menus (e.g. Hotel Links on the hotel report
   pages): backend.css paints `.btn-group .dropdown-header` as a grey band
   with lavender text, which reads as a hoverable item. Restyle as a muted
   eyebrow label — same treatment the record-detail actions dropdown already
   ships (.rd-header__actions .dropdown-header). */
.ui-refresh .dropdown-menu > li.dropdown-header {
    padding: var(--space-2) var(--space-4);
    font-size: 10.5px;
    font-weight: var(--font-weight-semibold);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--muted-2);
    background: none;
    border-top: 1px solid var(--line);
    border-bottom: 0;
}
.ui-refresh .dropdown-menu > li.dropdown-header:first-child {
    border-top: 0;
}
/* Flat-panel modifier for .list-page form/settings pages (Configure Change
   Report, Update Hotel, …): panels render as flat sections on the paper — no
   card fill, border, or shadow. A filled panel inside these single-column
   forms reads as a box-within-a-box, especially on dark surfaces where
   --card sits close to --paper. Same treatment as .comm-form panels. */
.ui-refresh .list-page--flat-panels .panel.panel-default {
    background: transparent !important;
    border: 0;
    box-shadow: none;
    display: flow-root;
    margin-bottom: var(--space-8);
}
/* #content-container qualifier so this outranks the ID-scoped base
   .panel-heading rule that paints --bg-surface. */
.ui-refresh #content-container .list-page--flat-panels .panel.panel-default > .panel-heading {
    background: transparent;
    border-radius: 0;
}
/* Room-block tables (registrant-hotel/room-block, room-block-all,
   hotel-room-block/index): one .list-table holding a <thead>/<tbody> pair per
   date. The column-header row (tr.rbr-cols) and the body cells take the shared
   .list-table look; only the date row (flat ink section label, no band) and the
   totals row are bespoke. Bootstrap's .table/.table-condensed classes are
   dropped under ui-refresh (see the two views) because `.ui-refresh .table > …`
   out-ranks .list-table. */
.ui-refresh .room-block-report > thead > tr.rbr-date > th {
    padding: var(--space-6) 0 var(--space-2);
    /* .list-table thead th paints --paper-2 and rounds the first/last th. */
    background: transparent;
    border: 0;
    border-radius: 0;
    white-space: normal;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
    letter-spacing: -0.01em;
    color: var(--ink);
}
.ui-refresh .room-block-report > thead:first-child > tr.rbr-date > th {
    padding-top: var(--space-3);
}
.ui-refresh .room-block-report > tbody > tr > td {
    border-bottom: 1px solid var(--list-table-line);
}
/* Room type name is the row label — same emphasis as .lt-cell-name elsewhere. */
.ui-refresh .room-block-report > tbody > tr > td:first-child {
    color: var(--ink);
    font-weight: var(--font-weight-medium);
}
.ui-refresh .room-block-report > tbody > tr > td .form-group {
    margin-bottom: 0;
}
.ui-refresh .room-block-report > tbody > tr.rbr-total > td {
    border-top: 1px solid var(--line);
    border-bottom: 0;
    font-weight: var(--font-weight-semibold);
    color: var(--ink);
}
.ui-refresh .room-block-report th b,
.ui-refresh .room-block-report td b {
    font-weight: inherit;
}
/* All Hotels room-block page: hotel names read as section titles, not the
   uppercase collapse labels the shared .panel-swoogo-collapse rule gives
   them (same size/tracking as .report-link-section__title). */
.ui-refresh .room-block-all .panel-swoogo-collapse .panel-default > .panel-heading a {
    font-size: var(--font-size-lg);
    text-transform: none;
    letter-spacing: -0.01em;
}
/* The heading's own bottom hairline (from the ID-scoped base .panel-heading
   rule) doubled the .panel-title::after filler beside the Hotel Links button.
   Keep the filler, drop the full-width line. #content-container qualifier so
   this outranks that ID-level base rule. */
.ui-refresh #content-container .room-block-all .panel-swoogo-collapse .panel-default > .panel-heading {
    border-bottom: 0;
}
/* Open/active button state: don't turn solid purple */
.ui-refresh .open > .btn.dropdown-toggle,
.ui-refresh .open > .btn.dropdown-toggle:hover,
.ui-refresh .open > .btn.dropdown-toggle:focus {
    background-color: var(--state-hover) !important;
    color: var(--fg-default) !important;
    border-color: var(--border-strong) !important;
    box-shadow: none !important;
}
/* Dropdown icon items */
.ui-refresh .dropdown-menu > li > a > i,
.ui-refresh .dropdown-submenu > ul > li > a > i {
    color: var(--fg-muted);
    margin-right: var(--space-1);
}

/* ===== Time Picker (jquery.timepicker) =====
   active-form.css hardcodes the hover and selected states to swoogo-lavender
   with !important, leaking purple into amber/custom accents and rendering
   on a hardcoded white background that doesn't survive dark mode. Re-skin
   the wrapper, list items, hover, and selected to follow the active accent
   and surface tokens. */
.ui-refresh .ui-timepicker-wrapper {
    background: var(--bg-surface);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
}
.ui-refresh .ui-timepicker-wrapper .ui-timepicker-list > li {
    background-color: var(--bg-surface);
    color: var(--fg-default);
}
.ui-refresh .ui-timepicker-wrapper .ui-timepicker-list > li:hover {
    background-color: var(--primary) !important;
    color: var(--fg-on-accent) !important;
}
.ui-refresh .ui-timepicker-wrapper li.ui-timepicker-selected {
    background-color: var(--primary);
    color: var(--fg-on-accent);
}

/* ===== Pagination ===== */
.ui-refresh .pagination {
    display: flex;
    justify-content: center;
    gap: var(--space-1);
    margin: var(--space-3) 0;
}
/* `!important` on the border: ajax-datatables.css's `.pagination > li:hover a`
   draws a grey 1px frame at the same specificity and loads later, so an
   unweighted `border: none` here loses the tie and the frame appears on hover. */
.ui-refresh .pagination > li > a,
.ui-refresh .pagination > li > span {
    border: none !important;
    border-radius: var(--radius-md);
    padding: var(--space-1) var(--space-2);
    min-width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
    background: transparent;
    transition: background 0.1s ease, color 0.1s ease;
}
.ui-refresh .pagination > li > a:hover {
    background: var(--state-hover);
    color: var(--fg-default);
}
.ui-refresh .pagination > .active > a,
.ui-refresh .pagination > .active > a:focus,
.ui-refresh .pagination > .active > a:hover,
.ui-refresh .pagination > .active > span,
.ui-refresh .pagination > .active > span:hover {
    background: var(--accent-subtle) !important;
    color: var(--accent-on-tint) !important;
    border: none !important;
    font-weight: 600;
}
.ui-refresh .pagination > .disabled > a,
.ui-refresh .pagination > .disabled > span {
    color: var(--fg-subtle);
    opacity: 0.5;
    background: transparent;
}

/* ===== Tabs =====
   Same active/inactive treatment as the sessions/speakers/tracks/locations
   .tr-tab row (sessions.css): active text goes to --ink (not accent), only
   the underline carries the accent color. */
.ui-refresh .nav-tabs > li > a {
    border-radius: 0;
    border: none;
    border-bottom: 2px solid transparent;
    color: var(--fg-muted);
    font-weight: var(--font-weight-medium);
    padding: var(--space-2) var(--space-4);
    transition: all var(--transition-fast);
}
.ui-refresh .nav-tabs > li.active > a,
.ui-refresh .nav-tabs > li.active > a:hover,
.ui-refresh .nav-tabs > li.active > a:focus {
    border: none;
    border-bottom: 2px solid var(--accent-primary);
    color: var(--ink);
    font-weight: var(--font-weight-semibold);
    background: transparent;
}
.ui-refresh .nav-tabs > li > a:hover,
.ui-refresh .nav-tabs > li > a:focus {
    border: none;
    border-bottom: 2px solid var(--border-default);
    background: transparent;
}
.ui-refresh .nav-tabs {
    border-bottom: 1px solid var(--border-subtle);
}
.ui-refresh .salesforce-account-navigation {
    margin-bottom: var(--space-8);
    margin-top: var(--space-4);
}
.ui-refresh .tab-content > .tab-pane {
    padding: var(--space-4) 0;
}
/* Kill Bootstrap's default 150ms opacity cross-fade on tab switch — it reads
   as lag rather than polish. Matches .rd-tab-panel, which already toggles
   instantly via display:none/block with no transition. */
.ui-refresh .tab-pane.fade {
    transition: none;
}
/* Event pages: reduce panel-body padding so form labels align with the
   registration-closed banner and the sticky header Save button edge-to-edge. */
.ui-refresh #content-container:not(.account) .panel-body {
    padding-left: var(--space-1);
    padding-right: var(--space-1);
}
.ui-refresh #content-container:not(.account) .panel-body:not(:has(> .nav-tabs)) {
    padding-top: var(--space-2);
}
/* When a panel contains tabs, reduce further so form labels align with
   the sticky header breadcrumbs (both at ~24px from #main-content border). */
.ui-refresh #content-container .panel-body:has(> .nav-tabs) {
    padding-left: var(--space-1);
    padding-right: var(--space-1);
}
/* Panel directly after the subpage head: merge visually with the head
   by removing the top border (the head's border-bottom is the divider).
   Applies to Event Settings, Registrant Types, and Registration Form. */
.ui-refresh .subpage-head ~ .panel {
    border-top: none;
}
/* Pull tabs flush with the reduced panel-body top padding */
.ui-refresh .panel-body > .nav-tabs {
    margin-top: calc(-1 * var(--space-6));
    margin-left: calc(-1 * var(--space-1));
    margin-right: calc(-1 * var(--space-1));
    padding-left: var(--space-1);
}

/* ===== Plain panels — form sections, not cards =====
   ui-refresh form pages present their sections as a heading on the page
   surface with the fields flowing directly beneath (see Event Settings'
   .event-settings-section-head / -heading). A filled card per section reads
   as clutter, and on dark paper --card and --paper sit close enough that
   each section becomes a box-within-a-box.

   This is an opt-in modifier rather than a rewrite of the markup because
   legacy JS still keys off the .panel hook — registrant-inline-updates.js
   queries `.page.panel.hidden` to skip hidden pages. .panel--plain keeps
   that hook and drops every piece of card chrome.

   Placed after the event-page .panel-body padding rules above: the
   `:not(:has(> .nav-tabs))` selector there has equal specificity, so this
   must win on source order.

   Contrast with .panel--flat-dark, which keeps the border and radius and
   only drops the fill on dark surfaces. */
.ui-refresh :is(#content-container, #report-content) .panel.panel--plain {
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    margin-bottom: var(--space-8);
    --control-bg-color: var(--bg-surface);
}
.ui-refresh :is(#content-container, #report-content) .panel.panel--plain > .panel-heading {
    background: none;
    border: 0;
    padding: 0;
    margin: 0 0 var(--space-5);
    font-size: 16px;
    line-height: 1.2;
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
}
.ui-refresh :is(#content-container, #report-content) .panel.panel--plain > .panel-body {
    padding: 0;
}
/* active-form.css hard-codes `padding: 0 30px 15px !important` on the billing
   address container. Nested in Bootstrap's -15px row margins that lands the
   address sub-fields 15px inboard of every other label on the page — masked
   before by the card padding, obvious once the section went flat. 15px (not a
   --space-* token) because it cancels Bootstrap's 15px gutter exactly rather
   than expressing a spacing decision of our own. */
.ui-refresh .new-form .billing_address .address-widget-container {
    padding-left: 15px !important;
    padding-right: 15px !important;
}
/* Registrant update, tabbed mode: the tab row replaces the section heading,
   so give it the heading's bottom gap — otherwise the first field crowds the
   tab underline (the plain panel adds no padding of its own). */
.ui-refresh .registrant-page-tabs {
    margin-bottom: var(--space-5);
}
/* Submit Payment / Submit Refund / Update Transaction: the last field in each
   plain section carried its own 20px .form-group margin on top of the
   section's 32px margin, so the next heading sat 52px below the previous
   input. Let the section margin own the boundary (same fix as .saml-section). */
.ui-refresh #transaction-form .panel--plain > .panel-body > :last-child,
.ui-refresh #registrant-attendance-form .panel--plain > .panel-body > :last-child {
    margin-bottom: 0;
}
/* Edit attendance (registrant-attendance/_update_ui_refresh.php): the
   earliest-arrival and latest-departure rows are separate questions, not one
   date+time cluster, so keep the full form-group gap between them (same
   reasoning as .cfs-settings above). */
.ui-refresh #registrant-attendance-form .form-row:has(+ .form-row) {
    margin-bottom: var(--space-5);
}

/* ===== Add attendance: session chooser (registrant-attendance/create.php) =====
   A plain GridView wrapped in `.list-grid` (the canonical list-page table
   treatment further down). Only the day-group rows, the two-line name cell
   and the Choose column need anything extra. The day rows outrank the
   .list-grid td reset (transparent background) by the extra .extrarow class. */
.ui-refresh .attendance-choice-grid .grid-view tbody > tr.extrarow > td {
    padding: var(--space-4) var(--space-4) var(--space-2);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
    background: var(--bg-subtle);
}
.ui-refresh .attendance-choice-grid .ac-name {
    color: var(--fg-default);
    font-weight: var(--font-weight-medium);
}
.ui-refresh .attendance-choice-grid .ac-time {
    margin-top: 2px;
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
}
.ui-refresh .attendance-choice-grid .grid-view tbody > tr > td.ac-action-col {
    width: 1%;
    white-space: nowrap;
    text-align: right;
}

/* ===== Registrant Update page (registrant/update.php, #registrant-form) =====
   The page renders the live registration form through PageWidgetQuestion, so
   the markup is the frontend's: Yii radioList choice rows, AddressInput's
   .address-widget-container and SessionInput's .agenda-table. The flat
   .panel--plain sections expose legacy spacing that card padding used to
   hide, so each block below pins the layout at ID specificity. */

/* --- Choice radio lists ("Select your package", reg type, radio questions).
   active-form.css indents `.form-group .radioList` by 15px and Bootstrap adds
   .radio margins + a 20px label padding on top of the generic ui-refresh
   .radio rules, so rows floated well right of their question with uneven
   gaps. Checkbox lists are left alone: the chip-picker rule
   (div:has(> .checkbox + .checkbox)) already owns those. */
.ui-refresh #registrant-form .form-group > .radioList {
    margin: 0;
    padding: 0;
}
.ui-refresh #registrant-form .form-group > .radioList > .radio {
    margin: 0;
    padding: 0;
    min-height: 0;
}
.ui-refresh #registrant-form .form-group > .radioList > .radio + .radio {
    margin-top: var(--space-2);
}
/* display/padding carry !important to match the existing generic rule
   (`.ui-refresh .form-group .radio label`) that beats base.css the same way. */
.ui-refresh #registrant-form .form-group > .radioList > .radio > label {
    display: flex !important;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--space-2);
    width: fit-content;
    max-width: 100%;
    margin: 0;
    padding: 0 !important;
    min-height: 0;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-normal);
    line-height: var(--line-height-sm);
    color: var(--fg-default);
}
.ui-refresh #registrant-form .form-group > .radioList > .radio > label > input[type="radio"] {
    position: static;
    flex-shrink: 0;
    margin: 2px 0 0;
}
/* A choice description / waitlist block rendered after the name span wraps
   onto its own line, indented past the 16px control + gap. */
.ui-refresh #registrant-form .form-group > .radioList > .radio > label > span:first-of-type ~ :not(input) {
    flex-basis: 100%;
    padding-left: calc(16px + var(--space-2));
    color: var(--fg-muted);
}

/* --- Address questions (Work / Home / Billing address). AddressInput nests
   its sub-fields in `.col-sm-12.address-details.form-horizontal`, so they
   inherit a 15px gutter, stacked .form-group margins, and city/state/zip
   `.col-sm-4` floats that the #content-container form-horizontal reset forces
   to 100% width. Same treatment as the AjaxDrawer (layout.css
   `.ajax-drawer .address-details`): a framed sub-card under the address label,
   one flex gap, a three-up city/state/zip row. The card paints --bg-surface,
   so its inputs flip to --bg-page. `!important` on the container padding
   beats active-form.css's own `!important` padding on the same element. */
.ui-refresh #registrant-form .address-widget-container {
    margin: 0;
    padding: 0 !important;
}
.ui-refresh #registrant-form .address-widget-container .address-details {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    float: none;
    width: 100%;
    margin: 0;
    padding: var(--space-4) var(--space-5) var(--space-5);
    background: var(--bg-surface);
    --control-bg-color: var(--bg-page);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
}
.ui-refresh #registrant-form .address-widget-container .address-details .form-group {
    margin: 0;
    padding: 0;
}
/* Bootstrap's .form-horizontal .form-group clearfix pseudo-elements become
   flex items and open phantom gaps. */
.ui-refresh #registrant-form .address-widget-container .address-details .form-group::before,
.ui-refresh #registrant-form .address-widget-container .address-details .form-group::after {
    display: none;
}
.ui-refresh #registrant-form .address-widget-container .address-details .address-inline {
    display: flex;
    gap: var(--space-4);
}
.ui-refresh #registrant-form .address-widget-container .address-details .address-inline > [class*="col-"] {
    float: none;
    width: auto;
    flex: 1 1 0;
    min-width: 0;
    margin: 0;
    padding: 0;
}
/* The phone/website row is emitted even when neither field is requested. */
.ui-refresh #registrant-form .address-widget-container .address-details .address-inline:not(:has(> *)) {
    display: none;
}
.ui-refresh #registrant-form .address-widget-container .map-location-action-buttons {
    align-self: flex-start;
}

/* --- "Choose your sessions" table (SessionInput, .agenda-table.session-input).
   Bootstrap .table-condensed + <b> headers + a <th> group-heading row per date
   read as heavy black headers over a stark rule, with the checkbox column
   floating. Restyle as the record-detail inner table: muted xs column headers,
   hairline dividers, date rows as section labels, a tight checkbox column and
   the more-info magnifier as a quiet action. session-picker.js /
   session-filters.js only read .agenda-table, tr.group-heading, .session and
   .sw_input — none of which change. */
.ui-refresh #registrant-form .agenda-table {
    margin: var(--space-2) 0 0;
    background: transparent;
    border-collapse: collapse;
}
.ui-refresh #registrant-form .agenda-table > thead > tr > th,
.ui-refresh #registrant-form .agenda-table > tbody > tr > th,
.ui-refresh #registrant-form .agenda-table > tbody > tr > td {
    padding: var(--space-3) var(--space-4);
    vertical-align: middle;
    line-height: var(--line-height-sm);
    border-top: 0;
    border-bottom: 1px solid var(--border-subtle);
    background: transparent;
}
.ui-refresh #registrant-form .agenda-table > thead > tr > th {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    color: var(--fg-muted);
    border-bottom: 1px solid var(--border-default);
}
.ui-refresh #registrant-form .agenda-table > thead > tr > th > b,
.ui-refresh #registrant-form .agenda-table > tbody > tr.group-heading > th > b {
    font-weight: inherit;
}
.ui-refresh #registrant-form .agenda-table > thead > tr > th.select_session,
.ui-refresh #registrant-form .agenda-table > thead > tr > th.more_info,
.ui-refresh #registrant-form .agenda-table > thead > tr > th.addToCalendar {
    width: 1%;
    white-space: nowrap;
    text-align: center;
}
.ui-refresh #registrant-form .agenda-table > tbody > tr.group-heading > th {
    padding: var(--space-5) var(--space-4) var(--space-2);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
}
.ui-refresh #registrant-form .agenda-table > tbody > tr.session > td {
    font-size: var(--font-size-sm);
    color: var(--fg-default);
}
.ui-refresh #registrant-form .agenda-table > tbody > tr:last-child > td {
    border-bottom: 0;
}
/* The session checkbox is wrapped in a <label> (Yii Html::checkbox with a
   hidden text label); base.css makes every .form-group label a block with a
   bottom margin, which knocks it off the row's centre line. */
.ui-refresh #registrant-form .agenda-table td > label {
    display: inline-flex;
    align-items: center;
    width: auto;
    margin: 0;
    padding: 0;
    vertical-align: middle;
}
.ui-refresh #registrant-form .agenda-table td input[type="checkbox"] {
    margin: 0;
}
/* More-info magnifier: quiet by default, accent on hover, sized as a button. */
.ui-refresh #registrant-form .agenda-table td.text-center > a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-md);
    color: var(--fg-muted);
    transition: color var(--transition-fast), background-color var(--transition-fast);
}
.ui-refresh #registrant-form .agenda-table td.text-center > a:hover,
.ui-refresh #registrant-form .agenda-table td.text-center > a:focus-visible {
    color: var(--accent-primary);
    background: var(--bg-subtle);
    text-decoration: none;
}
.ui-refresh #registrant-form .agenda-table td.text-center > a > .fa-lg {
    font-size: var(--font-size-sm);
    line-height: 1;
}

/* --- Hotel selection radios (registrant/update-hotel.php). Each hotel label
   ends in a "[More Info]" link, so the flex label's children are the input,
   a text run ending in "[", the <a>, and a "]" run — and the 8px gap above
   opens up around the link ("[ More Info ]"). Drop the gap for labels that
   carry a link and let the control's own margin space the text instead. */
.ui-refresh #registrant-form .form-group > .radioList > .radio > label:has(> a) {
    gap: 0;
}
.ui-refresh #registrant-form .form-group > .radioList > .radio > label:has(> a) > input[type="radio"] {
    margin-right: var(--space-2);
}

/* ===== Form Layout: Two-Column Row ===== */
.ui-refresh .form-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-4);
    margin-bottom: var(--space-5);
}
/* Three-up variant for a row of short, related fields (the Swoogo Admin
   registrant search's Email / ID / Event ID). minmax(0, 1fr) rather than 1fr so
   a long value can't push a column past its share. The single-column mobile
   rule below (max-width: 767px) matches `.form-row` and is declared later, so
   it collapses this variant too — keep any new variant declared here, above it. */
.ui-refresh .form-row--3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}
/* Adjacent form-rows (e.g. date pair followed by time pair) stay tight as
   a visual cluster; the LAST form-row in a sequence keeps the full 20px
   so it lines up with the form-group rhythm. */
.ui-refresh .form-row:has(+ .form-row) {
    margin-bottom: var(--space-1);
}
/* CFS settings (Submitter / Reviewer dates): the date row and the time row are
   separate questions, not one cluster — give them the same breathing room as
   the event Settings "When" section instead of the tight 4px pair. Scoped to
   .cfs-settings so other adjacent form-row pairs keep the cluster behaviour. */
.ui-refresh .cfs-settings .form-row:has(+ .form-row) {
    margin-bottom: var(--space-5);
}
.ui-refresh .form-row .form-group {
    margin-bottom: 0;
}

/* ===== Panel Heading Enhancements ===== */
.ui-refresh :is(#content-container, #report-content) .panel-heading h2,
.ui-refresh :is(#content-container, #report-content) .panel-heading h3 {
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    margin: 0;
    padding: 0;
}
.ui-refresh .panel-heading i:not(.btn i) {
    margin-right: var(--space-2);
    color: var(--fg-muted);
}
/* Subtitles under headings are no longer rendered in ui-refresh. The
   markup is still emitted in many places for now (and may stay), but the
   visual is suppressed so heads read as eyebrow + title only. Reversal of
   the prior "always add subtitles" direction; see memory
   feedback_no_panel_subtitles.md for context. */
.ui-refresh .panel-heading-subtitle {
    display: none !important;
}

/* ===== Slack notification configuration ===== */
.ui-refresh .slack-config-page .slack-config-add-form {
    margin-bottom: var(--space-6);
}
.ui-refresh .slack-config-page .field-with-action {
    flex-wrap: wrap;
    margin-bottom: 0;
}
.ui-refresh .slack-config-page .field-with-action .select2-container {
    flex: 0 1 400px;
    min-width: 0;
}
.ui-refresh .slack-config-page .field-with-action .btn {
    margin-bottom: 0;
}
.ui-refresh .slack-config-page .slack-config-staff-field label {
    display: block;
    margin-bottom: var(--space-2);
    color: var(--fg-muted);
    font-size: var(--font-size-xs);
}
.ui-refresh .slack-config-page .slack-config-staff-field .select2-container,
.ui-refresh .slack-config-page .slack-config-staff-field .form-group {
    margin-bottom: 0;
}
.ui-refresh .slack-config-page .slack-config-staff-field .help-block:empty {
    display: none;
}
.ui-refresh .slack-config-page .slack-config-grid-wrap {
    overflow-x: auto;
}
.ui-refresh .slack-config-page .summary {
    margin-bottom: var(--space-3);
    color: var(--muted);
    font-size: var(--font-size-xs);
}
.ui-refresh .slack-config-page .slack-config-help {
    align-items: flex-start;
    margin: 0 0 var(--space-6);
}
.ui-refresh .slack-config-page .slack-config-help ul {
    margin-top: var(--space-2);
}
@media (max-width: 767px) {
    .ui-refresh .slack-config-page .field-with-action .select2-container {
        flex-basis: 100%;
    }
    .ui-refresh .slack-config-page .list-pagination-row {
        grid-template-columns: 1fr;
        justify-items: center;
        gap: var(--space-3);
    }
    .ui-refresh .slack-config-page .list-pagination-row .pagination {
        flex-wrap: wrap;
    }
}

/* ===== Inline Field with Action ===== */
.ui-refresh .field-with-action {
    display: flex;
    align-items: flex-end;
    gap: var(--space-3);
    margin-bottom: 5px;
}
.ui-refresh .field-with-action .form-group {
    flex: 1;
    margin-bottom: 0;
}
.ui-refresh .field-with-action .form-group .help-block:empty {
    display: none;
}
.ui-refresh .field-with-action .btn {
    height: var(--input-height);
    margin-bottom: 2px;
    white-space: nowrap;
}

/* ===== Code Example ===== */
.ui-refresh .code-example {
    font-family: 'SF Mono', 'Fira Code', 'Fira Mono', monospace;
    font-size: var(--font-size-xs);
    line-height: var(--line-height-sm);
    color: var(--fg-default);
    background-color: var(--bg-muted);
    border-radius: var(--radius-md);
    padding: var(--space-3);
}
.ui-refresh textarea.code-example {
    width: 100%;
    height: auto;
    min-height: 140px;
    resize: vertical;
    border: 1px solid var(--border-subtle);
    white-space: pre;
}
/* Small inline variant of .code-example for a short label/value called out
   mid-sentence (e.g. SpotMe's "Dropdown | Checkbox | Radio Buttons" field
   types) — .code-example's own padding (--space-3) is sized for a block,
   not a word-length inline chip. */
.ui-refresh .inline-code {
    font-family: 'SF Mono', 'Fira Code', 'Fira Mono', monospace;
    font-size: var(--font-size-xs);
    color: var(--fg-default);
    background-color: var(--bg-muted);
    border-radius: var(--radius-sm);
    padding: 1px var(--space-2);
}

/* ===== Code Block with Copy Button ===== */
.ui-refresh .code-block-wrapper {
    position: relative;
    margin-bottom: var(--space-6);
}
.ui-refresh .code-block-wrapper .copy-btn {
    position: absolute;
    top: var(--space-2);
    right: var(--space-2);
    z-index: 1;
}

/* ===== Copy Button ===== */
.ui-refresh .copy-btn {
    outline: none;
    color: var(--fg-muted);
    transition: color var(--transition-fast);
}
.ui-refresh .copy-btn:hover {
    color: var(--accent-primary);
}
.ui-refresh .copy-btn--success {
    color: var(--color-success);
}
.ui-refresh .copy-btn--success:hover {
    color: var(--color-success);
}
.ui-refresh .copyable {
    overflow-wrap: anywhere;
}

/* ===== Sidebar User Context ===== */
.ui-refresh .sidebar-user-context {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3);
    margin-bottom: var(--space-2);
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    padding-bottom: var(--space-4);
}
.ui-refresh .sidebar-user-avatar img {
    width: 36px;
    height: 36px;
    border-radius: var(--radius-full);
    object-fit: cover;
    border: 2px solid rgba(255, 255, 255, 0.2);
}
.ui-refresh .sidebar-user-initials {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: var(--radius-full);
    background: var(--accent-hover);
    color: var(--fg-on-accent);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    border: 2px solid rgba(255, 255, 255, 0.2);
}
.ui-refresh .sidebar-user-info {
    overflow: hidden;
    min-width: 0;
}
.ui-refresh .sidebar-user-name {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.ui-refresh .sidebar-user-email {
    font-size: var(--font-size-xs);
    color: var(--ink-2);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ===== Google Charts refinement ===== */
.ui-refresh .chart svg text {
    cursor: pointer;
}

.ui-refresh .google-visualization-tooltip {
    border: 1px solid var(--border-subtle) !important;
    border-radius: var(--radius-lg) !important;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08) !important;
    padding: var(--space-2) var(--space-3) !important;
    font-family: Inter, sans-serif !important;
    font-size: var(--font-size-xs) !important;
    color: var(--fg-muted) !important;
    background: var(--bg-surface) !important;
    pointer-events: none !important;
}

/* ===== Dashlet Grid Tables (shared across all dashlets) ===== */
/* Compact, clean rows with subtle borders */
.ui-refresh :is(#content-container, #report-content) .dash-panel .grid-view table {
    margin-bottom: 0;
}
.ui-refresh :is(#content-container, #report-content) .dash-panel .grid-view thead th {
    font-size: var(--font-size-xs);
    font-weight: 500;
    color: var(--fg-subtle);
    border-top: none;
    border-bottom: 1px solid var(--border-subtle);
    padding: var(--space-2) var(--space-4);
    background: none;
}
.ui-refresh :is(#content-container, #report-content) .dash-panel .grid-view tbody td {
    padding: var(--space-2) var(--space-4);
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
    border-top: none;
    border-bottom: none;
    vertical-align: middle;
}
.ui-refresh :is(#content-container, #report-content) .dash-panel .grid-view tbody tr:not(.extrarow) {
    transition: background 0.1s ease;
}
.ui-refresh :is(#content-container, #report-content) .dash-panel .grid-view tbody tr:not(.extrarow):hover {
    background: var(--state-hover);
}
/* Links in dashlet grids — default color, accent on hover */
.ui-refresh :is(#content-container, #report-content) .dash-panel .grid-view tbody td a {
    color: var(--fg-default);
    text-decoration: none;
}
.ui-refresh :is(#content-container, #report-content) .dash-panel .grid-view tbody td a:hover {
    color: var(--accent-primary);
}
/* Group header rows (extrarow) — styled to match dashlet thead column headers.
   Reset both the tr-level background (legacy backend.css sets tr.extrarow {
   background: #fff }) and the td-level background, so the dashlet card
   surface shows through and the row reads as a quiet section break. */
.ui-refresh :is(#content-container, #report-content) .dash-panel .grid-view .extrarow {
    background: transparent !important;
}
.ui-refresh :is(#content-container, #report-content) .dash-panel .grid-view .extrarow td {
    background: none;
    background-image: none;
    border-top: none;
    border-bottom: 1px solid var(--border-subtle);
    padding: var(--space-2) var(--space-4);
    font-size: var(--font-size-xs);
    font-weight: 500;
    color: var(--fg-subtle);
    vertical-align: middle;
}
.ui-refresh :is(#content-container, #report-content) .dash-panel .grid-view .extrarow strong {
    font-weight: 500;
}

/* AjaxDataTables (.new-grid-theme) in dashlets — e.g. Registration Periods.
   ajax-datatables.css applies border-left + border-bottom to cells and
   border-top to the first header row, but never a border-right on the last
   column. Against the ui-refresh dash-panel's clean white background this
   reads as the table being cut off on the right. Close the right edge.
   Note: ajax-datatables.css currently hardcodes #E5E7EB for the other three
   sides; once that file is themed the whole table will share --border-subtle. */
.ui-refresh :is(#content-container, #report-content) .dash-panel .new-grid-theme .table-condensed > thead > tr > th:last-child,
.ui-refresh :is(#content-container, #report-content) .dash-panel .new-grid-theme .table-condensed > tbody > tr > td:last-child {
    border-right: 1px solid var(--border-subtle) !important;
}

/* AjaxDataTables active-sort icon: ajax-datatables.css hardcodes `color: blue`
   on the FA sort glyphs (\f15d / \f882). Retint to the accent so it tracks the
   accent picker like the rest of the refreshed UI. Scoped to .ui-refresh so the
   legacy UI keeps the original blue. */
.ui-refresh table.dataTable thead > tr.header-row > th.dt-ordering-asc span.dt-column-order:after,
.ui-refresh table.dataTable thead > tr.header-row > th.dt-ordering-desc span.dt-column-order:after {
    color: var(--accent-primary) !important;
}

/* ===== Bookmarks Dashlet (bookmark-specific) ===== */
/* Bookmark links get a small icon prefix */
.ui-refresh :is(#content-container, #report-content) .dash-panel[data-type="bookmarks"] .grid-view tbody td a {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}
.ui-refresh :is(#content-container, #report-content) .dash-panel[data-type="bookmarks"] .grid-view tbody td a::before {
    content: '\f02e';
    font-family: 'Font Awesome 6 Pro';
    font-weight: 400;
    font-size: var(--font-size-xs);
    color: var(--fg-subtle);
    flex-shrink: 0;
}
/* Bookmarks has no thead — hide it */
.ui-refresh :is(#content-container, #report-content) .dash-panel[data-type="bookmarks"] .grid-view thead {
    display: none;
}

/* ===== Bookmark Menu ===== */
.ui-refresh .bookmark-menu-manage {
    border-top: 1px solid var(--border-subtle);
    display: block;
    padding-top: var(--space-2);
    margin-top: var(--space-2);
}

/* ===== Panel Footer ===== */
.ui-refresh :is(#content-container, #report-content) .panel-footer {
    background: var(--bg-surface);
    border-top: 1px solid var(--border-subtle);
    padding: var(--space-3) var(--space-6);
}

/* ===== Nested Panels (reduced visual weight) ===== */
.ui-refresh :is(#content-container, #report-content) .panel .panel {
    box-shadow: none;
    border-color: var(--border-subtle);
}
.ui-refresh :is(#content-container, #report-content) .panel .panel .panel-heading {
    padding: var(--space-3) var(--space-6);
}

/* ===== Outlined Buttons ===== */
.ui-refresh .btn-outline-primary {
    background: transparent;
    border: 1px solid var(--accent-primary);
    color: var(--accent-primary);
}
.ui-refresh .btn-outline-primary:hover,
.ui-refresh .btn-outline-primary:focus {
    background: var(--accent-subtle);
    border-color: var(--accent-primary);
    color: var(--accent-on-tint);
    outline: none;
    box-shadow: none;
}

.ui-refresh .btn-warning {
    background: var(--color-warning);
    border-color: var(--color-warning);
    color: #fff;
}
.ui-refresh .btn-warning:hover,
.ui-refresh .btn-warning:focus {
    background: var(--yellow-70, #ca8a04);
    border-color: var(--yellow-70, #ca8a04);
    color: #fff;
    outline: none;
    box-shadow: none;
}
.ui-refresh .btn-outline-warning {
    background: transparent;
    border: 1px solid var(--color-warning);
    color: var(--color-warning);
}
.ui-refresh .btn-outline-warning:hover,
.ui-refresh .btn-outline-warning:focus {
    background: var(--color-warning-bg, rgba(234, 179, 8, 0.1));
    border-color: var(--color-warning);
    color: var(--color-warning);
    outline: none;
    box-shadow: none;
}
.ui-refresh .btn-outline-danger {
    background: transparent;
    border: 1px solid var(--color-error);
    color: var(--color-error);
}
.ui-refresh .btn-outline-danger:hover {
    background: var(--color-error-bg);
    color: var(--color-error);
}

/* ===== MFA Method Panels ===== */
/* backend.css sets .mfa-method-panel .panel-heading to display:flex,
   which puts the subtitle inline with the heading. Override to block. */
.ui-refresh .mfa-method-panel .panel-heading {
    display: block;
}

/* ===== MFA Method Cards (equal-height flex) ===== */
.ui-refresh #methods .row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    margin-top: var(--space-4);
}
.ui-refresh #methods .row > [class*="col-"] {
    display: flex;
}
.ui-refresh #methods .row .mfa-method-panel {
    display: flex;
    flex-direction: column;
    height: 100%;
}
.ui-refresh #methods .row .mfa-method-panel .panel-heading {
    display: block;
}
.ui-refresh #methods .row .mfa-method-panel .panel-body {
    flex: 1;
    padding: var(--space-6);
    min-height: 80px;
    display: flex;
    flex-direction: column;
    justify-content: center;
}
.ui-refresh #methods .row .mfa-method-panel .panel-footer {
    padding: var(--space-4) var(--space-6);
}

/* ===== MFA Method List (replaces card grid in ui-refresh) ===== */
.ui-refresh .mfa-method-list {
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    overflow: hidden;
    margin-top: var(--space-4);
}
.ui-refresh .mfa-method-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-5) var(--space-6);
    border-bottom: 1px solid var(--border-subtle);
}
.ui-refresh .mfa-method-item:last-child {
    border-bottom: none;
}
.ui-refresh .mfa-method-info {
    flex: 1;
}
.ui-refresh .mfa-method-name {
    font-weight: var(--font-weight-medium);
    font-size: var(--font-size-sm);
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-1);
}
.ui-refresh .mfa-method-name i {
    color: var(--fg-muted);
}
.ui-refresh .badge-accent {
    background: var(--accent-subtle);
    color: var(--accent-on-tint);
}

.ui-refresh .inline-staff-indicator {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}
.ui-refresh .staff-indicator {
    margin-left: 0;
}

/* ===== MFA Status Indicators ===== */
.ui-refresh .mfa-status {
    font-size: var(--font-size-sm);
}
.ui-refresh .mfa-status--enabled {
    color: var(--color-success);
}
.ui-refresh .mfa-status--enabled i {
    margin-right: var(--space-1);
}
.ui-refresh .mfa-status--disabled {
    color: var(--fg-muted);
}

/* ===== Trusted Device Current Row ===== */
.ui-refresh .trusted-device-current {
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    padding: var(--space-4) var(--space-6);
    margin-bottom: var(--space-4);
}
.ui-refresh .trusted-device-label {
    font-weight: var(--font-weight-medium);
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
    margin-bottom: var(--space-2);
}
.ui-refresh .trusted-device-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.ui-refresh .trusted-device-info {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    font-size: var(--font-size-sm);
}
.ui-refresh .trusted-device-info i {
    color: var(--fg-muted);
}
.ui-refresh .trusted-device-detail {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

/* ===== Trusted Devices: Other Devices grid wrapper ===== */
.ui-refresh .trusted-devices-other {
    margin-top: var(--space-4);
}
.ui-refresh .trusted-devices-other .trusted-device-label {
    margin-bottom: var(--space-3);
}

/* ===== Trusted Devices Section Heading ===== */
.ui-refresh .trusted-devices-section-heading {
    border-bottom: 1px solid var(--border-subtle);
    padding: var(--space-3) 0;
    margin: var(--space-4) 0 var(--space-3);
}
.ui-refresh .trusted-devices-section-heading h3 {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--fg-default);
    margin: 0;
}
.ui-refresh .trusted-devices-section-heading i {
    margin-right: var(--space-2);
    color: var(--fg-muted);
}

/* ===== Section Heading with Action ===== */
.ui-refresh .section-heading-with-action {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
}
/* .account-section-heading carries its own bottom margin for the plain-heading
   case; inside the flex row that margin belongs to the row instead, or the
   section body starts at a different offset depending on whether the heading
   carries an action (EventMobi's four "Retrieve … from EventMobi" buttons). */
.ui-refresh .account-section > .section-heading-with-action {
    margin-bottom: var(--space-4);
}
.ui-refresh .section-heading-with-action > .account-section-heading {
    margin-bottom: 0;
}
.ui-refresh .section-heading-with-action:has(+ .account-section-desc) {
    margin-bottom: var(--space-2);
}
.ui-refresh .eventmobi-settings-section .section-heading-with-action {
    align-items: center;
}
.ui-refresh .eventmobi-personal-schedule-options {
    margin-top: var(--space-6);
}

/* Zoom Events settings and mapping pages. */
.ui-refresh .zoom-events-detail-value {
    display: block;
    min-height: 36px;
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--bg-muted);
    color: var(--fg-default);
}
.ui-refresh .zoom-events-mapping-table .form-group {
    margin-bottom: 0;
}
.ui-refresh .zoom-events-mapping-group-heading {
    margin: var(--space-5) 0 var(--space-2);
    color: var(--fg-default);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
}
.ui-refresh .zoom-events-mapping-warning {
    margin-bottom: var(--space-4);
}

/* ===== Ghost Table Action Buttons ===== */
.ui-refresh .table .btn-group-sm .btn {
    background: transparent;
    border: none;
    color: var(--fg-muted);
    box-shadow: none;
    transition: color var(--transition-fast);
}
.ui-refresh .table .btn-group-sm .btn:hover {
    color: var(--accent-primary);
    background: transparent;
}

/* ===== Clickable Charts (GeoChart, ColumnChart) ===== */
.ui-refresh .dash-panel .chart svg path[fill]:not([fill="none"]) {
    cursor: pointer;
}

/* ===== Empty States ===== */
.ui-refresh .empty-text,
.ui-refresh .grid-view .empty {
    text-align: center;
    padding: var(--space-8) var(--space-4);
    color: var(--fg-muted);
    font-size: var(--font-size-base);
}
/* Disable row hover on empty state rows */
.ui-refresh :is(#content-container, #report-content) .dash-panel .grid-view tbody tr:has(.empty):hover,
.ui-refresh :is(#content-container, #report-content) .dash-panel .grid-view tbody tr:has(.empty-text):hover,
.ui-refresh :is(#content-container, #report-content) .dash-panel .grid-view tbody tr:has(.empty):hover td,
.ui-refresh :is(#content-container, #report-content) .dash-panel .grid-view tbody tr:has(.empty-text):hover td {
    background: transparent !important;
}
.ui-refresh .empty-text i {
    font-size: var(--font-size-2xl);
    display: block;
    margin-bottom: var(--space-2);
    color: var(--fg-subtle);
}

/* ===== Form Section Divider ===== */
.ui-refresh .form-section {
    border-top: 1px solid var(--border-subtle);
    padding-top: var(--space-4);
    margin-top: var(--space-4);
}
/* First .form-section in any wrapper: no separator — there's nothing above
   it inside the wrapper to divide from. Covers legacy panels (.panel-body)
   and the consolidated Settings layout (e.g. #location-settings, .virtual-settings). */
.ui-refresh .form-section:first-child {
    border-top: none;
    padding-top: 0;
    margin-top: 0;
}
.ui-refresh .form-section-label {
    font-size: 11px;
    font-weight: var(--font-weight-semibold);
    color: var(--fg-muted);
    margin-bottom: var(--space-3);
}

/* ===== API Credentials Table ===== */
.ui-refresh .api-credentials-table .table {
    margin-bottom: 0;
}
.ui-refresh .api-credentials-table .table td:first-child {
    font-weight: var(--font-weight-medium);
    color: var(--fg-muted);
    white-space: nowrap;
    width: 120px;
}

/* ===== Table Condensed Override ===== */
.ui-refresh .table-condensed > thead > tr > th,
.ui-refresh .table-condensed > tbody > tr > td {
    padding: var(--space-2) var(--space-3);
}

/* ===== Event Fees grid (fee/index.php _grid partials) =====
   The fee page is an inline price EDITOR, not a display list: each item that
   can carry a fee (a reg type, package, session…) is a group-header band
   followed by editable price rows (standard price + any early-bird prices),
   each with a CurrencyInput. Round 1 flattened the legacy collapsible panels
   into `.fee-grid-section`; round 2 polishes the editor so the two-column
   rhythm (item label / price) reads cleanly: each group sits in its own
   bordered card, the header band separates groups, and price rows get hairline
   dividers with the input column right-aligned to a fixed width. */
.ui-refresh .fee-grid-section {
    margin-bottom: var(--space-5);
}
.ui-refresh .fee-grid-section-title {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
    margin: 0 0 var(--space-2);
}
/* A fee/tax GROUP grid gets a hosting card — matches the .list-grid surface
   treatment so fees, tax rates, payment methods, and discounts all read as the
   same kind of table. `.fee-grid-section-body` is reused for other things too:
   plain form sections (fee "Other Settings", the tax "Tax Settings" panel) and
   the payment-plan Payment Schedule section (which embeds a milestone editor
   table but is really a form section). Those must stay flat — so the card is
   gated on `.extrarow`, the group-header row that only the fee and tax grids
   render, not on the mere presence of any `<table>`. */
.ui-refresh .fee-grid-section-body:has(.extrarow) {
    background: var(--bg-surface);
    --control-bg-color: var(--bg-page);
    border: 1px solid var(--border-subtle);
    border-radius: var(--panel-radius);
    box-shadow: var(--shadow-card);
    overflow: hidden;
}
.ui-refresh .fee-grid-table {
    margin-bottom: 0;
    background: transparent;
}
/* No row hover anywhere in the fee editor — every row is either a group header
   or an editable price row, none are clickable, so a hover highlight (from
   Bootstrap `.table-hover` / legacy grid styling) is misleading. */
.ui-refresh .fee-grid-table > tbody > tr:hover,
.ui-refresh .fee-grid-table > tbody > tr:hover > td {
    background: transparent;
}
/* Group-header row: the item name + its edit button. Rendered as a plain row
   (NOT a filled band — a tinted band on a dark card reads as a text input),
   distinguished from the price rows by weight and a divider beneath.
   Backend.css paints `.grid-view .extrarow { background:white; color:gray-800 }`
   which, in dark mode, becomes a glaring white row with unreadable text — so we
   must reset the background AND color on the `tr` itself (not just the td), at
   matching specificity, to token-driven values that follow the surface mode. */
.ui-refresh .fee-grid-table > tbody > tr.extrarow,
.ui-refresh .grid-view .fee-grid-table > tbody > tr.extrarow {
    background: transparent;
    background-image: none;
    color: var(--fg-default);
}
/* Bootstrap `table-condensed` paints a hardcoded light `#ddd` `border-top` on
   every cell — harsh and non-dimming in dark mode. Reset ALL fee-table cell
   borders with `!important`, then re-add only the quiet token dividers below. */
.ui-refresh .fee-grid-table > tbody > tr > td {
    border: none !important;
}
.ui-refresh .fee-grid-table .extrarow > td {
    background: transparent;
    border-bottom: 1px solid var(--border-subtle) !important;
    padding: var(--space-3) var(--space-4);
    vertical-align: middle;
}
/* A subsequent group-header (more than one item in a card) gets a heavier top
   rule so groups read as distinct blocks. Consolidated single-rows are
   excluded — they use their own bottom hairline (below) to avoid doubling. */
.ui-refresh .fee-grid-table > tbody > tr.extrarow:not(.fee-single-row) ~ tr.extrarow:not(.fee-single-row) > td {
    border-top: 1px solid var(--border-subtle) !important;
}
.ui-refresh .fee-grid-table .extrarow > td b {
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
    font-size: var(--font-size-sm);
}
.ui-refresh .fee-grid-table .fee-action-cell {
    width: 1%;
    white-space: nowrap;
    text-align: right;
}
/* ui-refresh consolidated single-row (item with only a standard price):
   name | price | edit in one row. One hairline between rows, none after the
   last, and the price cell keeps its own comfortable fixed width. */
.ui-refresh .fee-grid-table > tbody > tr.fee-single-row > td {
    border-top: none !important;
    border-bottom: 1px solid var(--border-subtle) !important;
    padding: var(--space-3) var(--space-4);
}
.ui-refresh .fee-grid-table > tbody > tr.fee-single-row:last-child > td {
    border-bottom: none !important;
}
/* Price rows (standard + early bird): label column muted + indented, price
   column a fixed width so every input lines up down the card. No row hover —
   these are editable input rows, not clickable list items, so a hover
   highlight is misleading. */
.ui-refresh .fee-grid-table > tbody > tr:not(.extrarow) > td {
    border-bottom: none;
    padding: var(--space-2) var(--space-4);
    vertical-align: middle;
}
.ui-refresh .fee-grid-table .fee-indent {
    padding-left: var(--space-6);
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
    vertical-align: middle;
}
.ui-refresh .fee-grid-table .fee-price-cell {
    width: 220px;
    min-width: 220px;
    vertical-align: middle;
}
.ui-refresh .fee-grid-table .fee-price-cell .form-group {
    margin-bottom: 0;
}
/* The price cell hosts an ActiveForm `.form-group` (and kartik's field
   wrapper) around the input-group. Force that whole chain to fill the cell
   width — otherwise the shrink-wrapped wrapper collapses and the flex
   input-group inside has no width to distribute. (The `<td>` itself stays a
   table-cell; only the inner wrappers become full-width blocks.) */
.ui-refresh .fee-grid-table .fee-price-cell > .form-group,
.ui-refresh .fee-grid-table .fee-price-cell > .form-group > div {
    width: 100%;
    display: block;
}
/* The CurrencyInput renders a kartik input-group (`$` addon + text field), but
   the widget's input is `display:block`, which breaks Bootstrap's
   `.input-group .form-control { display:table-cell }` model — the text field
   collapses to near-zero width and the value can't be seen. The sidebar solves
   this by switching the group to flex layout (see the `.currency-label` block
   in layout.css); mirror that here for the full-page fee editor. */
.ui-refresh .fee-grid-table .fee-price-cell .label-add-on.input-group,
.ui-refresh .fee-grid-table .fee-price-cell .currency-label.input-group {
    display: flex !important;
    align-items: stretch;
    width: 100% !important;
}
.ui-refresh .fee-grid-table .fee-price-cell .label-add-on > input.form-control,
.ui-refresh .fee-grid-table .fee-price-cell .currency-label > input.form-control {
    flex: 1 1 auto !important;
    width: 1% !important;
}
.ui-refresh .fee-grid-table .fee-price-cell .label-add-on > .input-group-addon,
.ui-refresh .fee-grid-table .fee-price-cell .currency-label > .input-group-addon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 36px;
    flex-grow: 0;
    flex-shrink: 0;
    flex-basis: max-content;
    padding: 0 var(--space-3);
}
.ui-refresh .fee-grid-table .fee-price-cell .input-group-addon > label {
    margin: 0;
    padding: 0;
    line-height: 1;
    white-space: nowrap;
    flex-shrink: 0;
}
/* Dim the price-field + addon borders to match the row dividers and the other
   buttons. In dark mode `--input-border` resolves to `--line-2` (a lighter
   grey for card-edge definition) which reads as harsh against these quiet
   rows — pin to `--border-subtle` (the `--line` value) instead. */
.ui-refresh .fee-grid-table .fee-price-cell .form-control,
.ui-refresh .fee-grid-table .fee-price-cell .input-group-addon {
    border-color: var(--border-subtle);
}
/* The edit button on the group-header row is a plain `.btn.btn-default`; tone
   its border to match. */
.ui-refresh .fee-grid-table .fee-action-cell .btn-default {
    border-color: var(--border-subtle);
}
/* Keep the currency static-value (conditional-price popover link) legible at
   the same size as an input — replaces the legacy inline font-size registerCss. */
.ui-refresh .fee-grid-table .form-control-static {
    font-size: var(--font-size-sm);
}

/* ===== Early Bird Pricing disclosure card (Edit Fees drawer) =====
   The `enable_early_price` toggle now sits INSIDE the card as its header, with
   the revealed early-bird settings as the card body. `.early-bird-card` is the
   single card; the toggle's checkbox row is the header and the inner
   `.visibility-conditions` drops its own card chrome (no card-in-card). */
.ui-refresh .ajax-drawer .early-bird-card {
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--panel-radius);
    box-shadow: var(--shadow-card);
    overflow: hidden;
    margin-top: var(--space-4);
    margin-bottom: var(--space-4);
}
/* The toggle row = card header: give it card padding and a divider below when
   the disclosure is open. The field is a `.form-group > .checkbox`.
   `!important` is required because the generic single-toggle collapse rule
   (`.ajax-drawer .form-group:has(> .checkbox:only-of-type)`) zeroes the
   form-group's top padding and pushes the checkbox's own padding to
   `--space-2` — both more specific than a plain `.early-bird-card >` selector,
   so they'd otherwise win and the header would hug the card's top edge. */
.ui-refresh .ajax-drawer .early-bird-card > .form-group:first-child {
    margin: 0 !important;
    padding: var(--space-4) !important;
}
.ui-refresh .ajax-drawer .early-bird-card > .form-group:first-child .checkbox {
    padding: 0 !important;
    border-bottom: none !important;
    margin: 0 !important;
}
/* Divider under the header only while the body is expanded (`.collapse.in`). */
.ui-refresh .ajax-drawer .early-bird-card:has(> #early_price_settings.in) > .form-group:first-child {
    border-bottom: 1px solid var(--border-subtle);
}
/* The revealed body: strip the inner `.visibility-conditions` card chrome so it
   reads as the card's body, not a nested box. Overrides the shared
   `.visibility-conditions` card rule (layout.css) at higher specificity. */
.ui-refresh .ajax-drawer .early-bird-card > #early_price_settings.visibility-conditions {
    border: none !important;
    border-radius: 0 !important;
    background: transparent !important;
    padding: 0 var(--space-4) !important;
}

/* ===== Payment-plan milestone schedule table =====
   The milestone editor (`#child-pjax-grid` GridView, in the Payment Plan
   Payment Schedule section) ships Bootstrap `.table-bordered`/`.table-striped`
   cell borders — a hardcoded light `#ddd` that never dims in dark mode and
   reads as harsh dividers. Reset the cell borders and re-add only quiet
   token-driven row dividers, matching the fee/list-grid treatment. */
.ui-refresh .list-page #child-pjax-grid .grid-view > table {
    border: none !important;
    margin-bottom: 0;
}
.ui-refresh .list-page #child-pjax-grid .grid-view thead > tr > th {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-subtle);
    border: none !important;
    border-bottom: 1px solid var(--border-subtle) !important;
    padding: var(--space-2) var(--space-3);
}
.ui-refresh .list-page #child-pjax-grid .grid-view tbody > tr > td {
    border: none !important;
    border-bottom: 1px solid var(--border-subtle) !important;
    padding: var(--space-2) var(--space-3);
    vertical-align: middle;
}
.ui-refresh .list-page #child-pjax-grid .grid-view tbody > tr:last-child > td {
    border-bottom: none !important;
}
/* No zebra striping — the quiet dividers carry the row separation. */
.ui-refresh .list-page #child-pjax-grid .grid-view tbody > tr {
    background: transparent !important;
}

/* ===== List-page GridView (.list-grid) =====
   The canonical list table is the registration page's AjaxDataTables widget
   (`.new-grid-theme`), but that CSS is coupled to the DataTables DOM
   (`tr.header-row`, `.dt-scroll-body`, `.table-condensed`, the `.dt-*`
   toolbar). Simpler list pages that use a plain Yii `GridView` (Payment
   Methods, Discount codes) render none of that scaffolding, so they can't
   inherit `.new-grid-theme`. This block reproduces the registration table's
   VISUAL treatment on a plain `.grid-view` table: quiet header row, roomy
   cells, hairline row dividers, hover, and a hosting card. Opt in by wrapping
   the GridView in `<div class="list-grid">`. */
.ui-refresh .list-grid {
    background: var(--bg-surface);
    --control-bg-color: var(--bg-page);
    border: 1px solid var(--border-subtle);
    border-radius: var(--panel-radius);
    box-shadow: var(--shadow-card);
    overflow: hidden;
}
.ui-refresh .list-grid .grid-view,
.ui-refresh .list-grid .grid-view table {
    margin-bottom: 0;
    background: transparent;
}
/* The GridView table ships Bootstrap `.table-bordered`, whose cell borders are
   a hardcoded light `#ddd` that never dims in dark mode and reads as harsh.
   Reset all cell borders with `!important` (mirroring how the registration
   `.new-grid-theme` neutralizes the same borders) and re-add only quiet
   token-driven row dividers. */
.ui-refresh .list-grid .grid-view > table {
    border: none !important;
}
/* Header row — quiet eyebrow, matching the registration thead treatment.
   No bottom border here: the single divider between the whole header block
   (title row + optional filter row) and the body is drawn once, on the LAST
   thead row (below), so header→filter and filter→body don't stack two lines. */
.ui-refresh .list-grid .grid-view thead > tr > th {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-subtle);
    background: var(--bg-subtle);
    border: none !important;
    padding: var(--space-3) var(--space-4);
    vertical-align: middle;
}
/* Kill every internal thead border. The bright line between the header row and
   the filter row can come from several places depending on GridView/Bootstrap
   theming: the header th's 2px bottom border, the filter row th's top/bottom,
   the `<tr>` itself, or ajax-datatables' `.has-filter` lavender rule. Reset all
   of them (th AND tr, top AND bottom) so only our single divider remains. */
.ui-refresh .list-grid .grid-view thead > tr,
.ui-refresh .list-grid .grid-view thead > tr > th,
.ui-refresh .list-grid .grid-view thead > tr.filters,
.ui-refresh .list-grid .grid-view thead > tr.filters > th,
.ui-refresh .list-grid .grid-view thead > tr > th.has-filter {
    border-top: 0 !important;
    border-bottom: 0 !important;
    box-shadow: none !important;
}
/* Single quiet divider under the last header row (filter row if present, else
   the title row). */
.ui-refresh .list-grid .grid-view thead > tr:last-child > th {
    border-bottom: 1px solid var(--border-subtle) !important;
}
/* Body cells — 14px, muted, roomy padding, hairline bottom divider only. */
.ui-refresh .list-grid .grid-view tbody > tr > td {
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
    background: transparent;
    border: none !important;
    border-bottom: 1px solid var(--border-subtle) !important;
    padding: var(--space-3) var(--space-4);
    vertical-align: middle;
}
.ui-refresh .list-grid .grid-view tbody > tr:last-child > td {
    border-bottom: none !important;
}
.ui-refresh .list-grid .grid-view tbody > tr {
    transition: background var(--transition-fast);
}
.ui-refresh .list-grid .grid-view tbody > tr:hover {
    background: var(--state-hover);
}
.ui-refresh .list-grid .grid-view tbody > tr > td a:not(.btn) {
    color: var(--fg-default);
    text-decoration: none;
}
.ui-refresh .list-grid .grid-view tbody > tr > td a:not(.btn):hover {
    color: var(--accent-primary);
}
/* Filter row inputs sit tight under the header (divider handled by the
   thead `tr:last-child` rule above). */
.ui-refresh .list-grid .grid-view thead > tr.filters > th {
    background: var(--bg-surface);
    padding: var(--space-2) var(--space-4);
}
/* Action column: right-aligned icon buttons in a quiet group. */
.ui-refresh .list-grid .grid-view .btn-group-sm > .btn {
    color: var(--fg-muted);
}
.ui-refresh .list-grid .grid-view .btn-group-sm > .btn:hover {
    color: var(--fg-default);
}
/* "With Checked Records" mass-action dropdown below the grid, and any other
   `.btn-default` toolbar buttons on these list pages, use the harsher
   card-edge border (`--line-2`) in dark mode. Tone to `--border-subtle` to
   match the quiet dividers and the other refreshed buttons. */
.ui-refresh .list-page .discount-buttons .btn-default,
.ui-refresh .list-page .btn-group > .btn-default {
    border-color: var(--border-subtle);
}
/* The mass-action button row sits directly under the table card with no gap.
   Push it down so it reads as a separate toolbar below the table. */
.ui-refresh .list-page .list-grid + .row {
    margin-top: var(--space-4);
}
/* Empty state fills the card cleanly. */
.ui-refresh .list-grid .grid-view .empty {
    border-bottom: none;
}
/* Toggle-switch cells (Visible / Admin Only): the checkbox is wrapped in a
   `.checkbox > label` so it picks up the shared toggle-switch styling. Drop
   Bootstrap's block margins/min-height and center the switch in the cell. */
.ui-refresh .list-grid .grid-view td .checkbox {
    margin: 0;
    min-height: 0;
    padding: 0;
    display: flex;
    justify-content: center;
}
.ui-refresh .list-grid .grid-view td .checkbox > label {
    padding: 0;
    min-height: 0;
    display: inline-flex;
}

/* ===== MFA Status Detail ===== */
.ui-refresh .mfa-status-detail {
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
    margin-top: var(--space-1);
}

/* ===== Skip MFA Link ===== */
.ui-refresh .skip-mfa-link {
    text-align: right;
}
.ui-refresh .skip-mfa-link .skip_link_btn {
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
    background: none;
    border: none;
    text-decoration: underline;
    cursor: pointer;
    transition: color var(--transition-fast);
}
.ui-refresh .skip-mfa-link .skip_link_btn:hover {
    color: var(--fg-default);
}

/* ===== Trusted Devices Icon Grid (horizontal in ui-refresh) ===== */
.ui-refresh .panel-trusted-devices .icon-grid-wrap {
    flex-flow: row;
    gap: var(--space-2);
}
.ui-refresh .panel-trusted-devices .icon-grid-wrap .icon-grid-icon {
    font-size: 1em;
}

/* ===== API Credentials Sections ===== */
.ui-refresh .api-section-heading {
    font-weight: var(--font-weight-medium);
    font-size: var(--font-size-sm);
    color: var(--fg-default);
    margin-bottom: var(--space-3);
    margin-top: var(--space-6);
}
.ui-refresh .api-section-heading h3 {
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    margin: 0;
    padding: 0;
}
.ui-refresh .section-heading-with-action .api-section-heading {
    margin-top: 0;
}
.ui-refresh .api-section-heading i {
    margin-right: var(--space-2);
    color: var(--fg-muted);
}
.ui-refresh .api-section-subtitle {
    display: block;
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
    font-weight: var(--font-weight-normal);
    margin-top: var(--space-1);
}
.ui-refresh .api-doc-link {
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
    margin-top: var(--space-4);
}

/* ===== MFA Enable Form: Override legacy icon sizing in panel-heading ===== */
/* backend.css sets .mfa-method-panel .panel-heading > i to font-size: 1.5em;
   which is too large alongside the ui-refresh heading text. Reset to inherit. */
.ui-refresh .mfa-method-panel .panel-heading > i {
    font-size: inherit;
}

/* ===== MFA Form: QR Wrapper Refinement ===== */
.ui-refresh .qr-wrapper {
    border-color: var(--border-default);
    border-radius: var(--radius-lg);
    margin-bottom: var(--space-4);
}
.ui-refresh .qr-wrapper .qr {
    border-right-color: var(--border-default);
}

/* ===== MFA Form: Code Entry Spacing ===== */
.ui-refresh .mfa-code-entry {
    margin-top: var(--space-4);
}

/* ===== Trusted Devices: Section heading subtitle ===== */
.ui-refresh .trusted-devices-section-heading .panel-heading-subtitle {
    display: block;
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
    font-weight: var(--font-weight-normal);
    margin-top: var(--space-1);
}

/* ===== MFA SMS Form: Phone Input Spacing ===== */
.ui-refresh .mfa-sms-phone-row {
    padding-bottom: 175px;
}

/* ===== Radio Button Group Toggles ===== */
/* active-form.css sets .btn-group .btn-default.active:hover to --swoogo-blue-700
   with !important, so we must match that specificity and use !important to override. */
.ui-refresh .btn-group .btn-default.active,
.ui-refresh .btn-group .btn-default.active:hover,
.ui-refresh .btn-group .btn-default.active:focus {
    background: var(--accent-subtle) !important;
    border-color: var(--accent-primary);
    color: var(--accent-on-tint);
    font-weight: var(--font-weight-medium);
}

/* ===== Event Settings: Waitlist Sub-Panel ===== */
.ui-refresh #content-container #waitlist-settings .panel-heading {
    display: block;
}

/* ===== Event Settings: Inline Duration Row ===== */
.ui-refresh .kv-fieldset-inline {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--space-3);
}
.ui-refresh .kv-fieldset-inline .form-group {
    margin-bottom: 0;
}

/* ===== Theme Chooser ===== */
/* In ui-refresh the redundant .theme-title is hidden (sticky header provides
   the title). These rules are kept for any edge cases. */
.ui-refresh #theme-page .theme-title h2 {
    font-size: var(--font-size-xl);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
    margin: 0 0 var(--space-1) 0;
}
.ui-refresh #theme-page .theme-title p {
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
}

/* --- Filter dropdown in the sticky header --- */
/* Theme filter pill tabs */
.ui-refresh .theme-filter-tabs {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    background: transparent;
    border: 1px solid var(--panel-border);
    border-radius: calc(var(--radius-xl) - 2px);
    padding: 3px;
}
.ui-refresh .tft-pill {
    display: inline-flex;
    align-items: center;
    padding: 6px 14px;
    border-radius: calc(var(--radius-xl) - 5px);
    font-size: var(--font-size-segmented);
    font-weight: var(--font-weight-medium);
    color: var(--fg-muted) !important;
    text-decoration: none;
    transition: background var(--transition-fast), color var(--transition-fast);
    white-space: nowrap;
    height: var(--space-8);
}
.ui-refresh .tft-pill:hover {
    color: var(--fg-default) !important;
    text-decoration: none;
}
.ui-refresh .tft-pill.tft-active {
    background: var(--fg-default);
    color: var(--bg-page) !important;
    box-shadow: var(--shadow-sm);
    border: 1px solid var(--border-strong);
}

/* Toolbar row between intro and card grid */
.ui-refresh .themes-toolbar {
    display: flex;
    align-items: center;
    margin-bottom: var(--space-4);
}

/* --- Theme page container — .container-fluid adds 15px horizontal padding
   that misaligns content with the sticky header. Zero it out so theme cards
   align with the header breadcrumbs, then restore vertical spacing. --- */
.ui-refresh #theme-page.container-fluid {
    padding-left: 0;
    padding-right: 0;
    padding-top: var(--space-6);
}

/* --- Theme card grid --- */
/* theme-db.css uses margin-right: -3rem + flex-basis: calc(X - 3rem) to
   fake a grid with margins. We use gap instead — zero the negative margin
   and fix the flex-basis math: for N columns with 24px gap, subtract
   (N-1)*24/N per item. */
.ui-refresh .theme-list-view {
    margin-top: 0;
    margin-right: 0;
    gap: var(--space-6);
}
/* 2-col breakpoint: subtract 12px (one 24px gap / 2 items) */
@media (min-width: 768px) {
    .ui-refresh .theme-list-view .item {
        flex-basis: calc(50% - 12px);
    }
}
/* 3-col breakpoint: subtract 16px (two 24px gaps / 3 items) */
@media (min-width: 1200px) {
    .ui-refresh .theme-list-view .item {
        flex-basis: calc(33.33% - 16px);
    }
}
.ui-refresh .item .choose-theme {
    border-color: var(--border-default);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}
.ui-refresh .item .choose-theme:hover {
    box-shadow: var(--shadow-md);
}
.ui-refresh .item .choose-theme.selected {
    border-color: var(--accent-primary);
    box-shadow: 0 0 0 1px var(--accent-primary), var(--shadow-sm);
}

/* --- Theme card image area --- */
/* Inner radius must be (outer radius - border width) so the image's rounded
   corners sit flush inside the card's 1px border instead of leaving a visible
   notch where the two same-radius curves diverge. */
.ui-refresh .item .theme-image {
    border-radius: calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px) 0 0;
    border-bottom-color: var(--border-subtle);
}

/* --- Clean fallback when no real screenshot exists --- */
.ui-refresh .item .theme-image-fallback {
    background: linear-gradient(135deg, var(--bg-muted) 0%, var(--bg-page) 100%);
}
.ui-refresh .item .theme-image-fallback-content {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    color: var(--fg-subtle);
}
.ui-refresh .item .theme-image-fallback-content i {
    font-size: 32px;
}
.ui-refresh .item .theme-image-fallback-content span {
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
}
.ui-refresh .tc-empty-preview {
    width: min(46%, 190px);
    min-width: 132px;
    aspect-ratio: 4 / 3;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: var(--bg-surface);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
}
.ui-refresh .tc-empty-preview-top {
    display: flex;
    align-items: center;
    gap: 5px;
    height: 22px;
    padding: 0 9px;
    border-bottom: 1px solid var(--border-subtle);
}
.ui-refresh .tc-empty-preview-top span {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--fg-subtle);
    opacity: 0.45;
}
.ui-refresh .tc-empty-preview-hero {
    height: 42%;
    margin: 14px 14px 10px;
    border-radius: var(--radius-sm);
    background: linear-gradient(135deg, var(--accent-subtle), var(--bg-muted));
}
.ui-refresh .tc-empty-preview-lines {
    display: grid;
    gap: 7px;
    padding: 0 14px;
}
.ui-refresh .tc-empty-preview-lines span {
    display: block;
    height: 7px;
    border-radius: 999px;
    background: var(--border-default);
}
.ui-refresh .tc-empty-preview-lines span:last-child {
    width: 68%;
}
.ui-refresh .tc-empty-title {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-muted);
}

/* --- Ribbon → clean badge --- */
/* Transform the diagonal rotated ribbon into a top-left badge.
   Offsets subtract 1px to compensate for .theme-image living inside the card's
   1px border — otherwise the ribbon sits 1px lower/right than .theme-version,
   which is positioned against .choose-theme directly. */
.ui-refresh .item .ribbon {
    transform: none;
    -webkit-transform: none;
    position: absolute;
    top: calc(var(--space-3) - 1px);
    left: calc(var(--space-3) - 1px);
    right: auto;
    background-color: var(--accent-primary);
    color: var(--fg-on-accent);
    border-radius: var(--radius-md);
    padding: var(--space-1) var(--space-3);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-xs);
    box-shadow: var(--shadow-sm);
    text-shadow: none;
    white-space: nowrap;
    margin: 0;
    border: none;
    display: inline-block;
    z-index: 2;
    width: auto;
    text-align: center;
    text-decoration: none;
}

/* --- Theme card content area --- */
.ui-refresh .item .choose-theme .theme-select {
    padding: var(--space-3) var(--space-4) calc(var(--space-3) + 40px);
}
.ui-refresh .item .choose-theme .theme-name h3 {
    margin-top: 0;
    margin-bottom: var(--space-1);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
}
.ui-refresh .item .theme-description {
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
}
/* Bootstrap version badge — moved to top-right corner of the card as a small pill.
   Legacy CSS positioned this absolutely right:5px inside the content area,
   leaving awkward empty space above the theme name.
   .choose-theme is position:relative — absolute here escapes .theme-select and
   places the badge in the top-right of the image area (opposite the Active ribbon). */
/* Padding and radius match .ribbon above so the two corner badges on the
   image area (Active Theme + Bootstrap version) read as the same size. */
.ui-refresh .item .choose-theme .theme-version {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    background: var(--black-60);
    color: var(--fg-on-accent);
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-md);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-xs);
    z-index: 2;
}
/* Draft / Swoogo labels */
.ui-refresh .item .label-draft {
    background-color: var(--color-warning-bg);
    color: var(--color-warning);
}
.ui-refresh .item .label-swoogo {
    background-color: var(--accent-subtle);
    color: var(--accent-on-tint);
}
/* --- Theme empty state --- */
/* #main-content max-height is calc(100svh - 210px); inside we subtract
   sticky header (~66px) + #theme-page padding (24px) + #main-content
   padding-bottom (20px) ≈ 110px. Use svh to match #main-content's unit
   so the empty state fills the available area without causing scroll. */
.ui-refresh .theme-list-view .empty {
    flex-basis: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: calc(100svh - 320px);
}
.ui-refresh .theme-empty-state {
    text-align: left;
    padding: var(--space-10) var(--space-6);
}
.ui-refresh .theme-empty-state i {
    font-size: 40px;
    color: var(--fg-subtle);
    margin-bottom: var(--space-4);
}
.ui-refresh .theme-empty-state-title {
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
    margin-bottom: var(--space-2);
}
.ui-refresh .theme-empty-state-text {
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
}
.ui-refresh .theme-empty-state-text a {
    color: var(--accent-primary);
    text-decoration: none;
}
.ui-refresh .theme-empty-state-text a:hover {
    text-decoration: underline;
}

/* Card action buttons */
.ui-refresh .item .theme-select-btn {
    left: var(--space-4);
    right: var(--space-4);
    bottom: var(--space-3);
}
.ui-refresh .item .theme-select-btn .btn {
    height: 32px;
    font-size: var(--font-size-sm);
}

/* ===== Event Themes — refreshed card grid ===== */

/* Themes page content wrapper — same max-width + auto-margin centering as the
   .subpage-head breadcrumb row, so the intro title and grid left-edge align. */
.ui-refresh .themes-page-content {
    width: min(var(--doc-max-width), 100% - (var(--doc-gutter) * 2));
    max-width: var(--doc-max-width);
    margin: var(--space-3) auto var(--space-12);
}
/* Remove the redundant padding-top/bottom that .theme-card-grid owned before
   the wrapper took over vertical spacing. */
.ui-refresh .themes-page-content .theme-card-grid {
    padding-top: 0;
    padding-bottom: 0;
}

/* Themes page intro header */
.ui-refresh .themes-intro {
    margin-bottom: 32px;
}
.ui-refresh .themes-title {
    font-family: var(--font-family);
    font-weight: var(--font-weight-semibold);
    font-size: 34px;
    letter-spacing: -0.02em;
    line-height: 1.1;
    margin: 0 0 12px;
    color: var(--fg-default);
}
.ui-refresh .themes-lede {
    font-size: 15px;
    line-height: 1.55;
    color: var(--fg-muted);
    margin: 0;
}
.ui-refresh .themes-lede a {
    color: var(--accent-primary);
    text-decoration: none;
}
.ui-refresh .themes-lede a:hover {
    text-decoration: underline;
}

/* 2-column grid */
.ui-refresh .theme-card-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-6);
    padding-top: var(--space-6);
    padding-bottom: var(--space-12);
}
@media (max-width: 900px) {
    .ui-refresh .theme-card-grid {
        grid-template-columns: 1fr;
    }
}

/* Empty state */
.ui-refresh .theme-card-grid-empty {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: calc(100svh - 520px);
}

/* Card */
.ui-refresh .theme-card {
    background: var(--card);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-xl);
    padding: 22px;
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.ui-refresh .theme-card:hover {
    border-color: var(--border-strong);
    box-shadow: 0 8px 24px -8px rgba(23, 22, 19, 0.08);
}
.ui-refresh .theme-card.applied {
    border-color: var(--accent-primary);
    box-shadow: 0 0 0 3px var(--accent-subtle);
}

/* Card header row */
.ui-refresh .tc-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.ui-refresh .tc-num {
    font-family: var(--font-family);
    font-size: var(--font-size-xs);
    letter-spacing: 0.06em;
    color: var(--fg-muted);
}
.ui-refresh .tc-tag {
    font-size: var(--font-size-badge);
    font-family: var(--font-family);
    padding: 2px var(--space-2);
    border-radius: var(--radius-full);
    background: var(--bg-subtle);
    color: var(--fg-muted);
    border: 1px solid var(--input-border);
    line-height: var(--line-height-xs);
}

/* Preview tile — 16:10 with the theme's screenshot */
.ui-refresh .tc-preview {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 10;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-lg);
    overflow: hidden;
    cursor: pointer;
    background: var(--bg-subtle);
    display: block;
}
.ui-refresh .tc-screenshot {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: top center;
}
.ui-refresh .tc-screenshot-fallback {
    position: absolute;
    inset: 0;
    background: linear-gradient(135deg, var(--bg-subtle), var(--bg-page));
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-3);
    color: var(--fg-subtle);
}
.ui-refresh .tc-screenshot-fallback i {
    font-size: 32px;
}
.ui-refresh .tc-screenshot-fallback span {
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
}

/* Hover overlay inside preview tile */
.ui-refresh .tc-overlay {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transition: opacity 0.18s ease;
    border-radius: inherit;
}
.ui-refresh .tc-preview:hover .tc-overlay,
.ui-refresh .tc-preview:focus-visible .tc-overlay {
    opacity: 1;
}
.ui-refresh .tc-overlay-name {
    font-size: 22px;
    font-weight: var(--font-weight-bold);
    letter-spacing: -0.015em;
    color: var(--fg-default);
    line-height: 1.1;
}
.ui-refresh .tc-overlay-vibe {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    color: var(--fg-muted);
}
.ui-refresh .tc-overlay-swatches {
    display: flex;
    gap: 6px;
    margin-top: auto;
}
.ui-refresh .tc-sw {
    width: 18px;
    height: 18px;
    border-radius: 3px;
    display: inline-block;
    border: 1px solid rgba(0, 0, 0, 0.06);
    flex-shrink: 0;
}
.ui-refresh .tc-overlay-actions {
    display: flex;
    gap: var(--space-2);
}

.ui-refresh .tc-btn.loading,
.ui-refresh .tc-btn-primary.loading {
    pointer-events: none;
    opacity: 0.7;
}
.ui-refresh .tc-btn.loading::before,
.ui-refresh .tc-btn-primary.loading::before {
    content: '';
    width: 13px;
    height: 13px;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: tc-spin 0.6s linear infinite;
    flex-shrink: 0;
}
@keyframes tc-spin {
    to { transform: rotate(360deg); }
}

/* Card footer action row */
.ui-refresh .tc-actions {
    display: flex;
    gap: var(--space-2);
    align-items: center;
    flex-wrap: wrap;
    margin-top: auto;
}
.ui-refresh .tc-active-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: 4px var(--space-2);
    background: var(--accent-subtle);
    color: var(--accent-on-tint);
    border: 1px solid var(--accent-muted);
    border-radius: var(--btn-radius);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    height: var(--btn-height);
    white-space: nowrap;
}

/* ===== Fullscreen theme preview overlay =====
   Iframe fills the viewport — the only chrome is the X close button. */
.ui-refresh .tpm-overlay {
    position: fixed;
    inset: 0;
    z-index: 1100;
    background: #fff;
    display: none;
}
.ui-refresh .tpm-overlay.open {
    display: block;
}
.ui-refresh .tpm-iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
    background: #fff;
}
/* Loading veil shown while the iframe is fetching the preview URL */
.ui-refresh .tpm-loading {
    position: absolute;
    inset: 0;
    display: none;
    align-items: center;
    justify-content: center;
    background: rgba(10, 10, 10, 0.85);
    backdrop-filter: blur(8px);
    z-index: 5;
}
.ui-refresh .tpm-loading.open {
    display: flex;
}
.ui-refresh .tpm-spinner {
    width: 40px;
    height: 40px;
    border: 3px solid rgba(255, 255, 255, 0.15);
    border-top-color: #fff;
    border-radius: 50%;
    animation: tpm-spin 0.8s linear infinite;
}
@keyframes tpm-spin {
    to { transform: rotate(360deg); }
}
.ui-refresh .tpm-close {
    position: absolute;
    top: 18px;
    right: 18px;
    z-index: 10;
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    background: rgba(20, 20, 20, 0.92);
    border: 1px solid rgba(255, 255, 255, 0.10);
    border-radius: 50%;
    color: #fff;
    cursor: pointer;
    backdrop-filter: blur(12px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
    transition: background var(--transition-fast), border-color var(--transition-fast), transform var(--transition-fast);
}
.ui-refresh .tpm-close:hover {
    background: rgba(40, 40, 40, 0.95);
    border-color: rgba(255, 255, 255, 0.25);
}
.ui-refresh .tpm-close:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 2px;
}
/* Floating bottom navigation bar inside the preview overlay */
.ui-refresh .tpm-floating {
    position: absolute;
    bottom: 24px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 10;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 8px;
    background: rgba(18, 18, 18, 0.82);
    border: 1px solid rgba(255, 255, 255, 0.10);
    border-radius: 999px;
    backdrop-filter: blur(20px);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.55);
    white-space: nowrap;
}
.ui-refresh .tpm-nav-btn {
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.10);
    border-radius: 50%;
    color: #fff;
    cursor: pointer;
    flex-shrink: 0;
    transition: background var(--transition-fast), border-color var(--transition-fast);
}
.ui-refresh .tpm-nav-btn:hover {
    background: rgba(255, 255, 255, 0.14);
    border-color: rgba(255, 255, 255, 0.22);
}
.ui-refresh .tpm-nav-btn:disabled {
    opacity: 0.30;
    cursor: default;
    background: rgba(255, 255, 255, 0.04);
}
.ui-refresh .tpm-bar-id {
    display: flex;
    align-items: center;
    gap: 5px;
    padding: 0 10px;
    min-width: 130px;
    justify-content: center;
}
.ui-refresh .tpm-bar-num {
    font-family: var(--font-mono, monospace);
    font-size: 10px;
    color: rgba(255, 255, 255, 0.50);
}
.ui-refresh .tpm-bar-dot {
    font-size: 10px;
    color: rgba(255, 255, 255, 0.30);
}
.ui-refresh .tpm-bar-name {
    font-family: var(--font-family);
    font-size: 13px;
    font-weight: 600;
    color: #fff;
}
.ui-refresh .tpm-bar-sep {
    width: 1px;
    height: 22px;
    background: rgba(255, 255, 255, 0.12);
    margin: 0 4px;
    flex-shrink: 0;
}
.ui-refresh .tpm-bar-apply {
    display: inline-flex;
    align-items: center;
    padding: 7px 16px;
    background: #f5f5f0;
    color: #0a0a0a;
    border: 1px solid #f5f5f0;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: background var(--transition-fast), border-color var(--transition-fast);
    white-space: nowrap;
}
.ui-refresh .tpm-bar-apply:hover {
    background: #fff;
    border-color: #fff;
}
.ui-refresh .tpm-bar-apply[data-applied="true"] {
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
    border-color: rgba(255, 255, 255, 0.16);
    cursor: default;
}
.ui-refresh .tpm-bar-apply.loading {
    pointer-events: none;
    opacity: 0.7;
}
.ui-refresh .tpm-bar-apply.loading::before {
    content: '';
    width: 13px;
    height: 13px;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: tc-spin 0.6s linear infinite;
    flex-shrink: 0;
    display: inline-block;
    vertical-align: middle;
    margin-right: 6px;
}

/* ===== Address Widget (Location search) ===== */
/* More space between the search input and "Can't find address?" link */
.ui-refresh .autocomplete-div .manual-input {
    display: inline-block;
    margin-top: var(--space-2);
}
/* The autocomplete div is a nested .form-group inside the field's own
   .form-group — zero out the inner margin to avoid double spacing. */
.ui-refresh .address-widget-container .autocomplete-div {
    margin-bottom: 0;
}
/* Reset Address button — styled as a destructive outline button. The
   wrapping form-group's bottom margin is zeroed out in the event-settings
   scope above (see `#location-settings .form-group:has(.reset-location)`)
   so the button doesn't open a chasm before the Virtual Link callout. */
.ui-refresh .address-widget-container .reset-location {
    color: var(--color-error);
    border-color: var(--color-error);
    background-color: transparent;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .address-widget-container .reset-location:hover {
    background-color: var(--color-error-bg);
    color: var(--color-error);
}
.ui-refresh .ajax-drawer #link-builder-form .pre-load-toggle-text {
    margin-bottom: var(--space-4);
}

/* ===== Swoogo Virtual Link card ===== */
/* The Swoogo Virtual Link callout. Sits on the page surface and was nearly
   invisible when its bg matched --bg-page exactly — the 3px accent rail
   was the only thing holding it together. Re-cast as an accent-tinted
   panel: surrounding border at --accent-muted defines all four edges, the
   tint bg gives it real presence, and a thicker accent rail on the left
   preserves the "highlight" feel. */
.ui-refresh .virtual-link-card {
    border: 1px solid var(--accent-muted);
    border-left: 4px solid var(--accent-primary);
    background-color: var(--accent-subtle);
    border-radius: var(--radius-md);
    padding: var(--space-4) var(--space-5);
    margin-bottom: var(--space-5);
}
.ui-refresh .virtual-link-header {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
}
.ui-refresh .virtual-link-label {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
}
.ui-refresh .virtual-link-header a {
    color: var(--fg-muted);
}
.ui-refresh .virtual-link-body {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-width: 0;
}
.ui-refresh .virtual-link-icon {
    flex-shrink: 0;
    color: var(--accent-primary);
    font-size: var(--font-size-sm);
}
.ui-refresh .virtual-link-text {
    font-size: var(--font-size-sm);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
.ui-refresh .virtual-link-text a {
    color: var(--accent-primary);
    text-decoration: none;
}
.ui-refresh .virtual-link-text a:hover {
    text-decoration: underline;
}
.ui-refresh .virtual-link-copy {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    background-color: transparent;
    color: var(--fg-muted);
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .virtual-link-copy:hover {
    background-color: var(--state-hover);
    color: var(--accent-primary);
}

/* ===== Virtual Location Cards (ui-refresh refinements) ===== */
.ui-refresh #virtual-location-container .radio-card {
    border: 1px solid var(--border-default);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-xs);
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast), background-color var(--transition-fast);
}
.ui-refresh #virtual-location-container .radio-header {
    color: var(--fg-default);
    font-weight: var(--font-weight-semibold);
}
.ui-refresh #virtual-location-container .radio-description {
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
}

/* ===== Location Panel Heading ===== */
.ui-refresh #content-container .panel-heading.location {
    display: block;
}

/* ===== Virtual Location: .well (provider settings) =====
   Bootstrap 3 wells wrap each virtual provider's collapsible settings.
   In ui-refresh the redundant labels are stripped (see VirtualEventInput),
   so the well becomes an invisible collapse container — content renders
   inline below the card grid. */
/* Bootstrap paints `.well` with a grey card background; these virtual-setting
   wells are just collapsibles wrapping a paragraph + button, not card
   surfaces, so force them transparent to sit invisibly on the page bg. */
.ui-refresh .well.virtual-setting {
    background-color: transparent !important;
    border: none !important;
    border-radius: 0;
    box-shadow: none;
    padding: 0;
    margin-bottom: 0;
}
.ui-refresh #virtual_event_url_setting .field-virtual-location-container {
    margin-bottom: 0 !important;
}
.ui-refresh .well.virtual-setting.swoogo.in {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}
.ui-refresh .pigeonhole-settings,
.ui-refresh .pigeonhole-session-id.in {
    display: flex;
    flex-direction: column;
    margin-left: 0;
    padding-left: 0;
    /* No row-gap: both of these wrap Bootstrap collapses (the session-id block
       and the reactions panel), and a gap sits outside the animated height — it
       holds full size all the way down to height 0, then vanishes when
       display:none lands, jumping the panel the last 8px. Measured in Chrome:
       container 247px at height 0 vs 239px settled, 0px after this change.
       Spacing moves to margins on the siblings that never animate; don't put it
       back on a `.collapse` — Bootstrap swaps that class for `.collapsing`
       during the transition, so a `:not(.collapse)` guard re-creates the jump. */
    row-gap: 0;
    column-gap: var(--space-2);
}
.ui-refresh .pigeonhole-session-id.in > .toggle-group {
    margin-top: var(--space-2);
}
/* Lead-in gap for the session-id collapse lives on its first child, inside the
   animated box. The reactions panel gets the same from the
   .panel-expanded-settings.collapse rules above. */
.ui-refresh .pigeonhole-settings > .pigeonhole-session-id > :first-child {
    margin-top: var(--space-2);
}
.ui-refresh .reaction_settings {
    margin-left: 0;
    padding-left: 0;
    margin-top: var(--space-2);
}
.ui-refresh .well.virtual-setting p {
    color: var(--fg-muted);
    margin-bottom: var(--space-3);
}
.ui-refresh .well.virtual-setting p:last-child {
    margin-bottom: 0;
}
.ui-refresh .well.virtual-setting .form-group:last-child {
    margin-bottom: 0;
}

/* ===== Confirmation modal wells =====
   Several shared confirmation dialogs wrap their message/field in a Bootstrap
   `.well` — the delete dialog (components/views/modal/delete.php,
   #delete_confirmation) and the "add contacts/speakers to X" dialogs
   (invitation-list-contact/_confirm_add.php, speaker/_confirm_add.php,
   cfs-contact/partials/_confirm_add.php — all share id #add_confirmation).
   Bootstrap's `.well` is a hardcoded light-grey fill with dark text, which
   stays white-on-dark. Re-point it to surface tokens so it reads correctly in
   every tone (they resolve per surface, so no data-surface rules are needed). */
.ui-refresh #delete_confirmation .well,
.ui-refresh #add_confirmation .well {
    background: var(--bg-muted);
    border: 1px solid var(--panel-border);
    box-shadow: none;
    color: var(--ink);
    /* Bootstrap's .well ships asymmetric padding (19px top, 25px bottom), which
       leaves single-line content sitting high. Normalize to symmetric padding
       so the well body reads vertically centered. */
    padding: var(--space-5);
    /* Drop Bootstrap's 20px bottom margin — the modal footer already provides
       the gap; the trailing margin just leaves a white band before the buttons. */
    margin-bottom: 0;
}
.ui-refresh #delete_confirmation .well .control-label,
.ui-refresh #add_confirmation .well .control-label {
    color: var(--muted);
}
.ui-refresh #delete_confirmation .well .form-control-static,
.ui-refresh #add_confirmation .well .form-control-static {
    color: var(--ink);
}
/* The static field-groups are styled for editable forms — a label + value plus
   a 20px bottom margin on every group (including the last), which leaves a tall
   empty band inside the well. Tighten to a compact read-only key/value list so
   the well hugs its content. */
.ui-refresh #delete_confirmation .well .form-group,
.ui-refresh #add_confirmation .well .form-group {
    margin-bottom: var(--space-3);
}
.ui-refresh #delete_confirmation .well .form-group:last-child,
.ui-refresh #add_confirmation .well .form-group:last-child {
    margin-bottom: 0;
}
.ui-refresh #delete_confirmation .well .control-label,
.ui-refresh #add_confirmation .well .control-label {
    margin-bottom: var(--space-1);
    font-size: var(--font-size-xs);
}
.ui-refresh #delete_confirmation .well .form-control-static,
.ui-refresh #add_confirmation .well .form-control-static {
    min-height: 0;
    padding: 0;
}

/* ===== Speaker Settings: .well.preview (display-format live preview) =====
   speaker/settings.php's formatting builder renders a sample preview string
   into a bare Bootstrap `.well` (populatePreviewData(), speaker/settings.php).
   Same hardcoded light-grey-fill problem as the confirmation-modal wells
   above — re-point to surface tokens so it flips correctly across all four
   ui-refresh tones instead of staying a fixed light box in dark mode. */
.ui-refresh .well.preview {
    background: var(--bg-muted);
    border: 1px solid var(--panel-border);
    box-shadow: none;
    color: var(--ink);
}


/* ===== Custom domain: CNAME configuration =====
   The custom-domain form uses a Bootstrap `.well` to present the DNS record.
   Bootstrap fixes that surface to a pale grey while ui-refresh correctly
   switches the inherited text to light ink in dark mode, leaving the record
   effectively invisible. Treat it as a tokenized callout instead and align it
   with the vertical ui-refresh form. */
.ui-refresh .cname-details {
    background: var(--bg-muted);
    border: 1px solid var(--panel-border);
    border-radius: var(--panel-radius);
    box-shadow: none;
    color: var(--fg-default);
    padding: var(--space-5);
}
.ui-refresh .cname-details.col-sm-offset-2 {
    margin-left: 0;
}
.ui-refresh .cname-details p {
    color: inherit;
}
.ui-refresh .cname-details p:last-child {
    margin-bottom: 0;
}

/* ===== Credential Picker (choose-credentials drawer screen) ===== */
.ui-refresh .credential-picker-label {
    font-size: var(--font-size-sm) !important;
    font-weight: var(--font-weight-semibold) !important;
    color: var(--fg-default) !important;
    margin-bottom: var(--space-2) !important;
}
.ui-refresh .credential-picker-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}
.ui-refresh .ajax-drawer .reg-settings {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}
.ui-refresh .credential-picker-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-4) var(--space-5);
    background: var(--bg-surface);
    border: 1px solid var(--input-border);
    border-radius: var(--radius-md);
    text-decoration: none;
    color: var(--fg-default);
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast), background-color var(--transition-fast);
}
.ui-refresh .credential-picker-row:hover {
    border-color: var(--accent-primary);
    box-shadow: var(--shadow-xs);
    background-color: var(--accent-subtle);
    text-decoration: none;
    color: var(--fg-default);
}
.ui-refresh .credential-picker-name {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
}
.ui-refresh .credential-picker-arrow {
    color: var(--fg-muted);
    transition: color var(--transition-fast), transform var(--transition-fast);
}
.ui-refresh .credential-picker-row:hover .credential-picker-arrow {
    color: var(--accent-primary);
    transform: translateX(2px);
}

/* ===== Virtual Location: .panel-expanded-settings (nested toggles) =====
   Used by waiting-room and on-demand settings. backend.css sets a 15px
   margin-left + border-left with raw gray. Override with token-based
   spacing and colors so nested settings feel cohesive with the form. */
.ui-refresh #content-container .panel-expanded-settings {
    border-left: 2px solid var(--border-default);
    margin-left: var(--space-3);
    margin-bottom: var(--space-3);
    padding: var(--space-2) var(--space-4);
}
.ui-refresh #content-container .panel-expanded-settings p {
    color: var(--fg-muted);
    margin-bottom: var(--space-2);
}
.ui-refresh #content-container .panel-expanded-settings .form-group:last-child {
    margin-bottom: 0;
}
.ui-refresh .panel-expanded-settings {
    border-left: none;
}
/* A panel nested inside a ToggleRow (e.g. the SAML Debug Mode "Debug Until"
   field) already sits inside a bordered .toggle-group card — the indent +
   rule from the #content-container treatment above reads as a second nested
   card and shifts the field out of alignment with the rest of the row. */
.ui-refresh #content-container .toggle-row > .panel-expanded-settings {
    border-left: none;
    margin-left: 0;
    padding-left: 0;
    padding-right: 0;
}
/* Configure Change Report: the compare-date reveal is driven by a radio
   group, not a toggle, so it's never inside a .toggle-row — but it
   shouldn't carry the toggle-nesting indent rail either. */
.ui-refresh #content-container .compare-settings.panel-expanded-settings {
    border-left: none;
    margin-left: 0;
    padding-left: 0;
    padding-right: 0;
}
/* Bootstrap collapse animates height and nothing else, so a panel's own
   vertical padding and margin stay at full size all the way down to height 0
   and then disappear in one frame when .collapse (display: none) lands — the
   jump you see at the end of the close. Hand that space to the inner children,
   which live inside the animated box and shrink with it. Horizontal padding,
   the indent and the left rule are untouched. Panels inside a .toggle-row get
   the equivalent treatment separately below, since their padding comes from
   the toggle-row-scoped rule above rather than this one. */
.ui-refresh :not(.toggle-row) > .panel-expanded-settings.collapse,
.ui-refresh :not(.toggle-row) > .panel-expanded-settings.collapsing {
    padding-top: 0;
    padding-bottom: 0;
    margin-top: 0;
    margin-bottom: 0;
}
.ui-refresh :not(.toggle-row) > .panel-expanded-settings.collapse > :first-child,
.ui-refresh :not(.toggle-row) > .panel-expanded-settings.collapsing > :first-child {
    margin-top: var(--space-3);
}
.ui-refresh :not(.toggle-row) > .panel-expanded-settings.collapse > :last-child,
.ui-refresh :not(.toggle-row) > .panel-expanded-settings.collapsing > :last-child {
    margin-bottom: var(--space-3);
}
/* Same fix, scoped to toggle-row panels (e.g. Debug Until, Remember Me
   duration): their only child is a plain field, not a row the flex rules
   already zero, so without this the field's own margin still collapses
   inconsistently across the collapsing box's overflow:hidden boundary and
   snaps at the start/end of the transition. The bare `.toggle-row >` selector
   covers ajax-drawer/toggle-group panels (it ties on specificity with their
   base rule at ~1726 and wins on source order); reaching panels inside
   #content-container additionally needs that id in the selector to
   out-specificity the base panel-expanded-settings padding/margin rule above
   (an id beats any number of classes) — without it this block is silently
   shadowed and never actually applies there. */
.ui-refresh .toggle-row > .panel-expanded-settings.collapse,
.ui-refresh .toggle-row > .panel-expanded-settings.collapsing,
.ui-refresh #content-container .toggle-row > .panel-expanded-settings.collapse,
.ui-refresh #content-container .toggle-row > .panel-expanded-settings.collapsing {
    padding-top: 0;
    padding-bottom: 0;
    margin-top: 0;
    margin-bottom: 0;
    max-width: 100%;
}
.ui-refresh .toggle-row > .panel-expanded-settings.collapse > :first-child,
.ui-refresh .toggle-row > .panel-expanded-settings.collapsing > :first-child,
.ui-refresh #content-container .toggle-row > .panel-expanded-settings.collapse > :first-child,
.ui-refresh #content-container .toggle-row > .panel-expanded-settings.collapsing > :first-child {
    margin-top: var(--space-3);
}
.ui-refresh .toggle-row > .panel-expanded-settings.collapse > :last-child,
.ui-refresh .toggle-row > .panel-expanded-settings.collapsing > :last-child,
.ui-refresh #content-container .toggle-row > .panel-expanded-settings.collapse > :last-child,
.ui-refresh #content-container .toggle-row > .panel-expanded-settings.collapsing > :last-child {
    margin-bottom: var(--space-3);
}

/* ===== Main Content Container ===== */
/* layout.css sets #main-content { background-color: white; border-radius: 8px;
   overflow-x: hidden }. The outer .page-body owns the refreshed page canvas,
   so clear the legacy white instead of painting a second canvas here. This
   keeps Midnight uniform when #main-content ends before the page does. */
.ui-refresh #main-content {
    background-color: transparent !important;
}
/* Dashboard-specific: kill the radius so dashlet panels at the container
   corners don't get clipped. */
.ui-refresh.dashboard #main-content {
    border-radius: 0 !important;
    min-height: calc(100svh - 210px);
}
/* The earlier dashboard override capped #main-content at calc(100svh - 140px)
   to budget for: 60px top nav + 53px legacy event-details bar + 12px margin +
   ~15px buffer "so the back-to-top button floats over the last ~30px of scroll
   content." None of that math holds in ui-refresh anymore:
     - event.php skips the _event-header render (line 30), so there's no 53px
       bar above #main-content.
     - The back-to-top FAB now lives in the viewport's bottom-right corner
       (`bottom: 16px` from .js-scrollbar earlier in this file), not floating
       40%-from-right over scroll content — so reserving a bottom strip for it
       just leaves an ~80px dead zone with the FAB visible in the void.
   Removing the override lets layout.css's general rule
   (.ui-refresh #content-container:not(.account) #main-content {
        max-height: calc(100svh - 60px)
    }) take effect on the dashboard too, so #main-content reaches the viewport
   bottom the same way it does on the consolidated Settings page. The FAB's
   corner placement now overlaps only a tiny bottom-right slice of scroll
   content, which is the standard modern FAB behavior. */

/* Event detail pages (identified by .subpage-head — the Themes tab is the
   only event setup page that carries `body.dashboard`) need to match the
   non-dashboard event pages: 20px side padding and white background.
   layout.css: body.dashboard #main-content { padding: 0; background: #F5F5FA }
   overrides both — restore them here. `!important` is only needed on
   background-color because the layout.css rule sets it with equal specificity;
   padding wins on specificity alone thanks to :has(). */
.ui-refresh.dashboard #main-content:has(.subpage-head) {
    padding: 0 20px 20px;
    background-color: var(--bg-surface) !important;
}
/* Dashboard pages (event dashboard + account dashboard) zero out
   #main-content's padding via `body.dashboard #main-content { padding: 0 }`
   in layout.css, leaving the 12px gutter on `#dash-row > .col` as the only
   breathing room between dashlets and the scroll edge. That 12px feels
   cramped against the overlay scrollbar — bump #main-content's side
   padding to 12px so the rightmost dashlet sits ~24px clear of the
   scrollbar (and the leftmost stays the same distance from the icon-nav
   chrome for symmetry). The Settings rule above already overrides this
   with `:has(.subpage-head)` for full-page form layouts. */
body.ui-refresh.dashboard #main-content {
    padding-left: var(--space-3);
    padding-right: var(--space-3);
}
/* Account dashboard wraps the page header above a 9/3 row (dashlets +
   activity stream). The activity-stream-sidebar provides its own internal
   padding, but the page header and dashlet columns rely on #main-content's
   side padding for the outer gutter. Double the side padding here so the
   scrollbar sits the same ~24px clear of content as on the event dashboard,
   where the col adds its own 12px on top. Scoped via
   `#content-container.account` so the event dashboard is unaffected. */
body.ui-refresh.dashboard #content-container.account #main-content {
    padding-left: var(--space-6);
    padding-right: var(--space-6);
}
/* Neutralize the nested Bootstrap .row negative margins on #dash-row so
   dashlet panels don't extend into #main-content's overflow-x:hidden clip zone.
   The default -15px margins push content to the left edge where shadows and
   rounded corners get clipped. */
.ui-refresh #dash-row.row {
    margin-left: 0 !important;
    margin-right: 0 !important;
}
.ui-refresh #dash-row > :is(.col, .col-notmovable) {
    padding-left: var(--space-3) !important;
    padding-right: var(--space-3) !important;
}

/* ===== Dashboard: Dash Panel Refinement ===== */
/* Flat dashlet look — no shadow, just border + radius. Explicitly set on
   .dash-panel because the base .panel rule can be overridden by dashboard.css
   load order (DashboardAsset loads after UiRefreshAsset).
   overflow:visible on .panel-body so stat card content isn't clipped,
   while the outer panel keeps overflow:hidden for border-radius clipping. */
.ui-refresh :is(#content-container, #report-content) .panel.dash-panel {
    border-radius: var(--panel-radius) !important;
    box-shadow: none !important;
    background: var(--bg-surface);
    /* !important is needed to beat active-form.css which sets
       `border-color: currentcolor` on #content-container .panel — and
       currentcolor inherits from body.ui-refresh's color: var(--fg-default),
       making panel borders pick up the ink color in dark mode. */
    border: 1px solid var(--panel-border) !important;
    overflow: visible;
}
.ui-refresh :is(#content-container, #report-content) .dash-panel .panel-body {
    overflow: visible;
    padding: var(--space-3) 0;
}
/* Inline dashlet config form — align content with panel heading text */
.ui-refresh :is(#content-container, #report-content) .dash-panel .panel-body #update-dashlet-inline {
    padding: var(--space-2) var(--space-2) 0;
}
/* No divider between dashlet name and content. Horizontal padding at --space-4
   (16px) for comfortable breathing room; stat cards offset to match.
   Bottom padding reduced so heading-to-content gap stays tight without the
   visual break a divider line used to provide. */
.ui-refresh :is(#content-container, #report-content) .dash-panel .panel-heading {
    border-bottom: none;
    padding-left: var(--space-4);
    padding-right: var(--space-4);
    padding-bottom: var(--space-1);
}

/* ===== Dashboard: Dash Panel Heading ===== */
/* Preserve cursor:move for jQuery UI Sortable drag-and-drop.
   Use flex to vertically center the title and dropdown button. */
.ui-refresh .dash-panel-heading {
    cursor: move;
    display: flex;
    align-items: center;
}
.ui-refresh .dash-panel-heading .panel-title {
    flex: 1;
    order: 0;
}
.ui-refresh .dash-panel-heading .btn-group.pull-right {
    float: none;
    order: 1;
    margin-left: var(--space-2);
}
.ui-refresh .readonly-dashboard .dash-panel-heading {
    cursor: auto;
}
.ui-refresh .dash-panel .panel-title,
.ui-refresh .dashlet-skeleton .panel-title {
    display: block;
    font-weight: var(--font-weight-medium);
    font-size: var(--font-size-sm);
    color: var(--fg-default);
}
.ui-refresh .dash-panel .panel-title h2,
.ui-refresh .dashlet-skeleton .panel-title h2 {
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    margin: 0;
    padding: 0;
}
.ui-refresh .dash-panel .panel-title i:not(.btn i),
.ui-refresh .dashlet-skeleton .panel-title i:not(.btn i) {
    margin-right: var(--space-2);
    color: var(--fg-muted);
}
.ui-refresh .dashlet-title-text {
    cursor: text;
}
.ui-refresh .dashlet-edit-icon {
    opacity: 0;
    font-size: var(--text-xs);
    color: var(--fg-muted);
    margin-left: var(--space-1);
    transition: opacity 0.15s ease;
    pointer-events: none;
}
.ui-refresh .dashlet-title-text:hover .dashlet-edit-icon {
    opacity: 1;
}
/* Inline rename field for dashlet titles. Now a <textarea rows="1"> (see
   web/plugins/dashboard/dashboard.js startInlineRename) so long titles
   wrap during editing exactly the way the display-mode <span> wraps —
   <input> couldn't do that because the spec hardcodes it to one line.
   `resize: none` + `overflow: hidden` lets the JS autosize the height
   to scrollHeight without the user-resize handle or a scrollbar fighting
   it; box-sizing keeps padding/border out of the height math. */
.ui-refresh .dashlet-title-input {
    background: transparent;
    border: none;
    outline: none;
    font-family: inherit;
    font-weight: inherit;
    color: var(--fg-default);
    font-size: var(--font-size-base);
    letter-spacing: normal;
    text-transform: none;
    width: 100%;
    padding: 0;
    line-height: var(--line-height-base);
    display: block;
    box-sizing: border-box;
    resize: none;
    overflow: hidden;
}

/* ===== Dashboard: Heading Dropdown → Ghost Style ===== */
/* dashboard.css uses !important on color and background, so we must match */
.ui-refresh .dash-panel-heading .btn-group .btn {
    background: transparent;
    border: none;
    color: var(--fg-muted) !important;
    box-shadow: none;
    margin-top: 0;
    height: 32px;
    width: 32px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-md);
    font-size: var(--font-size-base);
}
.ui-refresh .dash-panel-heading .btn-group .btn:hover {
    color: var(--fg-default) !important;
    background: var(--state-hover);
}
.ui-refresh .dash-panel-heading .open .dropdown-toggle,
.ui-refresh .dash-panel-heading .dropdown-toggle:hover {
    background-color: var(--state-hover) !important;
}

/* ===== Dashboard: Stat Card Styling (Event Stats + Daily Stats) ===== */
/* backend.css sets .dash-panel .btn { border: 0 } and .event-stat-wrap { border: 0 }
   so we need !important on border to override */
.ui-refresh .btn.event-stat-wrap,
.ui-refresh .btn.daily-stat-wrap {
    display: block;
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle) !important;
    border-radius: var(--radius-md);
    padding: var(--space-4) var(--space-5);
    height: auto;
    max-width: none;
    text-align: left;
    white-space: normal;
    transition: border-color var(--transition-fast), background-color var(--transition-fast);
}
.ui-refresh .btn.event-stat-wrap:hover,
.ui-refresh .btn.daily-stat-wrap:hover {
    border-color: var(--accent-primary) !important;
    background: var(--accent-subtle);
    text-decoration: none;
}

/* ===== Dashboard: Override legacy table-row layout on stat children ===== */
/* backend.css sets display:table-row on .event-stat-wrap > [class^=es-] and
   .daily-stat-wrap > [class^=ds-], which stretches children to full width.
   Use specific selectors per child to set the correct display for inline icon+label layout. */
/* Icons are no longer rendered on reg-stat cards; hide defensively in case
   a cached partial still emits them. */
.ui-refresh .btn.event-stat-wrap > .es-icon,
.ui-refresh .btn.daily-stat-wrap > .ds-icon {
    display: none;
}
.ui-refresh .btn.event-stat-wrap > .es-info,
.ui-refresh .btn.daily-stat-wrap > .ds-info {
    display: inline;
    vertical-align: middle;
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    color: var(--fg-muted);
}
.ui-refresh .btn.event-stat-wrap > .es-stat,
.ui-refresh .btn.daily-stat-wrap > .ds-stat {
    display: block;
    text-align: left;
    margin-top: var(--space-2);
}

/* ===== Dashboard: Stat Count Typography ===== */
/* Do NOT set opacity — daily stats counter animation depends on opacity: 0 → jQuery → 1 */
.ui-refresh .daily-stat-wrap > .ds-stat > .ds-count,
.ui-refresh .event-stat-wrap > .es-stat > .es-count {
    font-size: var(--font-size-2xl);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-muted);
    line-height: 1.2;
}
.ui-refresh .daily-stat-wrap > .ds-stat > .ds-count.ft-sm,
.ui-refresh .event-stat-wrap > .es-stat > .es-count.ft-sm {
    font-size: var(--font-size-lg);
}

/* ===== Dashboard: Dashlet Grid Spacing ===== */
/* padding-inline matches the dash-panel heading's --space-4 so stat card
   left edges align with the dashlet title text. Panel-body has 0 horizontal
   padding, so all inset comes from here / table cell padding. */
.ui-refresh .dashlet-actions {
    gap: var(--space-4);
    padding-left: var(--space-4);
    padding-right: var(--space-4);
}
.ui-refresh .dashlet-item {
    flex: 1 1 calc(33.33% - var(--space-4));
    max-width: none;
    min-width: 160px;
}

/* ===== Dashboard: Stat Icon (plain inline glyph, no bubble) ===== */
.ui-refresh .btn .es-icon,
.ui-refresh .btn .ds-icon {
    display: inline;
    background: none;
    width: auto;
    height: auto;
    border-radius: 0;
    color: var(--fg-muted);
    font-size: var(--font-size-xs);
    margin: 0 var(--space-1) 0 0;
    vertical-align: baseline;
}

/* ===== Dashboard: Panel Footer (filter info) ===== */
.ui-refresh .dash-panel .panel-footer {
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
}
.ui-refresh .dash-panel .fa-info-circle {
    color: var(--accent-primary);
    font-size: var(--font-size-base);
}

/* ===== Dashboard: Sortable Placeholder ===== */
.ui-refresh.dashboard .ui-sortable-placeholder {
    background: var(--accent-subtle) !important;
    border: 2px dashed var(--accent-primary) !important;
    border-radius: var(--panel-radius);
    opacity: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--font-size-sm) !important;
    color: var(--accent-primary);
    font-weight: var(--font-weight-medium);
    padding-top: 0 !important;
}
.ui-refresh.dashboard .ui-sortable-placeholder::before {
    content: 'Drop dashlet here';
}

/* ===== Dashboard: Sidebar Styling ===== */
.ui-refresh.dashboard .sidebar {
    padding-top: var(--space-4);
    margin-bottom: 0;
    padding-left: var(--space-5);
}

/* ===== Dashboard: Dashlet Heading Subtitle ===== */
.ui-refresh .dash-panel .panel-heading-subtitle,
.ui-refresh .dashlet-skeleton .panel-heading-subtitle {
    display: block;
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-normal);
    color: var(--fg-muted);
    margin-top: var(--space-1);
    line-height: var(--line-height-snug);
}

/* ===== Dashboard: Skeleton Loader ===== */
@keyframes skeleton-pulse {
    0%, 100% { opacity: 0.4; }
    50% { opacity: 1; }
}
.ui-refresh .dashlet-skeleton {
    background: var(--bg-surface);
    border: 1px solid var(--panel-border);
    border-radius: var(--panel-radius);
    box-shadow: none;
    overflow: hidden;
    /* Match the real .panel margin so replacement with the loaded dashlet
       doesn't shift items below. */
    margin-bottom: var(--space-5);
}
/* The skeleton reuses the real .dash-panel-heading structure so it's
   pixel-identical to the loaded dashlet and does not jump on swap. */
.ui-refresh #content-container .dashlet-skeleton .dash-panel-heading {
    border-bottom: none;
    cursor: default;
    padding-left: var(--space-4);
    padding-right: var(--space-4);
    padding-bottom: var(--space-1);
}
.ui-refresh .skeleton-body {
    padding: var(--space-3);
}
.ui-refresh .skeleton-bar {
    background: var(--bg-muted);
    border-radius: var(--radius-sm);
    animation: skeleton-pulse 1.5s ease-in-out infinite;
}
.ui-refresh .skeleton-line-full {
    height: 12px;
    width: 100%;
    margin-bottom: var(--space-3);
}
.ui-refresh .skeleton-line-medium {
    height: 12px;
    width: 75%;
    margin-bottom: var(--space-3);
}
.ui-refresh .skeleton-line-short {
    height: 12px;
    width: 45%;
}

/* ===== Drawer: Skeleton Loader ===== */
.ui-refresh .ajax-drawer-body > .ajax-drawer-skeleton {
    padding: var(--space-6);
}
.ui-refresh .ajax-drawer-skeleton--inset {
    padding: var(--space-4);
}
.ui-refresh .skeleton-form-group {
    margin-bottom: var(--space-5);
}
.ui-refresh .skeleton-label {
    height: 12px;
    width: 30%;
    margin-bottom: var(--space-2);
}
.ui-refresh .skeleton-input {
    height: var(--input-height);
    width: 100%;
    border-radius: var(--input-radius);
}

.ui-refresh .ajax-drawer-skeleton {
    position: absolute;
    inset: 0;
    z-index: 9999;
    background: var(--bg-page);
    overflow: auto;
    transition: opacity 0.2s ease;
}

.ui-refresh .ajax-drawer-error {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-6) var(--space-4);
    color: var(--ink-2);
    text-align: center;
}

/* ===== Iframe Modal: Panel Overrides ===== */
/* iframe.php has no #content-container, so panels need separate selectors.
   These mirror the #content-container .panel rules from the top of this file. */
body.iframe.iframe-modal.ui-refresh {
    /* transparent only affects this document's body, not the parent's <iframe> element, which stays opaque white */
    background-color: var(--bg-surface);
    padding: var(--space-6) var(--space-5) 60px !important;
}
/* Image/File library, opened as a picker in App.loadModal()'s iframe modal:
   .image-library--picker / .file-library--picker (below) already supply
   their own 24px (--space-6) padding matching this modal's header exactly.
   The shared body padding above stacks another 24px/20px/60px on top of
   that, so "Image library" sits well right of (and below) "Choose Your
   Image"/"Images" instead of lining up with it. Same approach as
   report-form.css's `body.ui-refresh.iframe-modal:has(.report-form)`: zero
   the shared padding for this specific content rather than touch the rule
   every other iframe modal still relies on. */
body.ui-refresh.iframe-modal:has(.image-library--picker),
body.ui-refresh.iframe-modal:has(.file-library--picker) {
    padding: 0 !important;
}
/* .modal-message's own top/bottom padding (components.css ~1418) doubles up
   with the body padding-top right above it — this body is the message's
   direct parent, with no .panel between them to absorb one of the two. Zero
   the vertical half here and keep only the horizontal padding it exists for. */
body.iframe.iframe-modal.ui-refresh .modal-message {
    padding-top: 0;
    padding-bottom: 0;
}
body.iframe.ui-refresh {
    background-color: var(--bg-surface);
    /* backend.css sets `margin: 15px 15px 80px` and `padding-bottom: 400px` on
       body.iframe. Drop the margin entirely so drawer chrome (e.g. the
       .sidebar-title bottom border) spans the full width; keep a small
       padding-bottom reserve for the fixed footer. */
    padding-bottom: 60px;
    margin: 0;
}
body.iframe.ui-refresh .panel {
    border: none;
    border-radius: none;
    box-shadow: none;
    background: var(--bg-page);
    padding: 0 var(--space-6);
    overflow: hidden;
}
/* The overflow:hidden above is a leftover: 4ffd1ade0d added it to clip panel
 * content to the card's `border-radius`, and a later commit flattened the
 * card to `border: none; border-radius: none; box-shadow: none` without
 * dropping the clip. An overflow ancestor still clips absolutely-positioned
 * descendants regardless of z-index, so bootstrap-multiselect's
 * `.multiselect-container` and jQuery QueryBuilder's `.dropdown-menu` (both
 * position:absolute inside the panel) get cut to the panel's box — visible
 * on Badges & Labels > Pre-Print > Pre-print settings, whose "Standard
 * filters" panel is one form row tall.
 *
 * Gated on the menu actually being open rather than removed outright:
 * body.iframe covers every IFRAME_LAYOUT surface plus every legacy
 * slide-out (layouts/sidebar.php wraps iframe.php), and in the
 * non-.iframe-modal ones Bootstrap's -15px .form-horizontal .form-group
 * margins are still live and currently clipped here — unclipping
 * unconditionally would spill that content past the panel padding.
 * Bootstrap 3 puts `.open` on the toggle's parent — `.btn-group` for
 * bootstrap-multiselect (see the `.btn-group.open > .multiselect-container`
 * contract in bootstrap-multiselect.css) and `.dropdown` for QueryBuilder's
 * add-rule menu — so this matches both, and covers `.dropup` too since
 * jquery-advanced-rule-input.js toggles `.dropup` on that same .btn-group.
 * Named explicitly rather than a bare `:has(.open)` because `.open` is
 * overloaded elsewhere in this stylesheet (`.confirm-dialog.open`,
 * `.ajax-drawer.open`, and `.drawer-screen-fields.open` a few hundred lines
 * up, itself an iframe context). See also the page-scoped
 * `.comm-overflow-visible` marker class further down, which solves the same
 * problem for one Communicate screen. */
body.iframe.ui-refresh .panel:has(.btn-group.open, .dropdown.open) {
    overflow: visible;
}
body.iframe.ui-refresh .report-search-drawer-body .sidebar-body .panel {
    height: auto;
    min-height: 100%;
    padding-left: 0;
    padding-right: 0;
    padding-bottom: 50%;
    margin: 0;
}
body.iframe.ui-refresh .panel:first-child {
    padding-top: var(--space-6);
}
body.iframe-modal.ui-refresh .panel-body {
    padding: 0;
}
body.iframe.ui-refresh .panel-heading h3 {
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    margin: 0;
    padding: 0;
}

/* ===== Pre-Print Settings modal (badge/_generate_settings.php) =====
 * The form renders inside the legacy iframe modal (App.loadModal ->
 * iframe.php -> body.iframe.iframe-modal.ui-refresh), so it already inherits
 * the shared iframe-modal skin above. What that skin doesn't supply is a
 * scroll frame for a form taller than the fixed footer's reserve — same
 * problem report-form.css solves for the Create/Edit Report modal, and the
 * same fix: make the body the only scroll container and the footer a static
 * flex sibling (the structure .sidebar-body / .sidebar-footer use),
 * rather than a `position: fixed` bar over a scrolling document with a
 * hand-tuned padding reserve.
 *
 * Scoped to `.preprint-settings` (a class the view puts on its <form>)
 * rather than to body.iframe-modal, so nothing here leaks into the dozens of
 * other iframe modals sharing the same body classes. */
body.ui-refresh.iframe-modal:has(.preprint-settings) {
    height: 100vh;
    overflow: hidden;
    padding: 0 !important;
    display: flex;
    flex-direction: column;
}
body.ui-refresh.iframe-modal:has(.preprint-settings) > #modal-pjax-div,
body.ui-refresh.iframe-modal .preprint-settings {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}
body.ui-refresh.iframe-modal .preprint-settings-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    /* Top gap only: the shared `body.iframe.ui-refresh .panel:first-child`
       rule already gives the first panel its own padding-top, and this is
       its :first-child — a second top padding here would double the gap
       above "Standard filters" to 48px. */
    padding: 0 0 var(--space-6);
}
body.ui-refresh.iframe-modal .preprint-settings .iframe-modal-footer {
    position: static;
    flex: 0 0 auto;
    width: auto;
}
/* Replaces the legacy inline `style="padding-top:10px"` on the collapse
 * element. Padding on a `.collapse` itself persists as residual height when
 * Bootstrap finishes the collapse animation, producing a visible snap once
 * `display: none` is applied — `.ui-refresh .vis-section .collapse` already
 * zeroes margin/padding on the collapse element for this reason. Putting the
 * padding on the inner `.condition-input-body` wrapper instead keeps it part
 * of the animated height, so there's no snap. Same pattern as the reg-type
 * visibility tab's `.condition-input-body` rule. */
.ui-refresh .preprint-settings .condition-input-body {
    padding-top: var(--space-2);
}

/* Bootstrap's .form-horizontal .form-group carries a -15px/-15px margin to
   offset the col-sm-* gutters inside a .row. The iframe-modal case above
   already zeroes it; the same offset shows up in plain full-page horizontal
   forms (e.g. event/self-scan) that aren't wrapped in a .row, so the negative
   margin just eats into the panel padding instead of being cancelled out. */
body.iframe-modal.ui-refresh .form-horizontal .form-group,
.ui-refresh #content-container .form-horizontal .form-group {
    margin-left: 0;
    margin-right: 0;
}
/* Kartik's horizontal form fields float a fixed-width label column
   (.col-sm-2) beside a fixed-width control column (.col-sm-10) — fine for
   single-line inputs, but a multi-line textarea (event/self-scan's message
   fields) squeezes its label into that narrow column, and a checkbox/toggle
   row renders an EMPTY label-width spacer before its control column
   (Kartik's autoOffset), indenting the toggle under an invisible label
   gutter instead of lining up with the section heading above it. Un-float
   both columns so the label stacks above its control (or collapses away
   entirely when it's the empty offset spacer) and the control takes the
   full row width. */
.ui-refresh #content-container .form-horizontal .form-group > .control-label,
.ui-refresh #content-container .form-horizontal .form-group > [class*="col-"] {
    float: none;
    width: 100%;
    margin-left: 0;
    padding-left: 0;
    padding-right: 0;
}
.ui-refresh #content-container .form-horizontal .form-group > .control-label {
    text-align: left;
    padding-top: 0;
}
.ui-refresh #content-container .form-horizontal .form-group > .control-label:empty,
.ui-refresh #content-container .form-horizontal .form-group > [class*="col-"]:empty {
    display: none;
}
body.iframe.ui-refresh .panel-heading i:not(.btn i) {
    margin-right: var(--space-2);
    color: var(--fg-muted);
}

/* ===== Form Validation: Label/Help Color Pin =====
   Yii ActiveForm tags a .form-group with .has-success once the field
   validates on blur. Legacy active-form.css forces .has-success .control-label
   to gray-800 with !important, so muted labels darken on first interaction —
   visible in the Link Settings drawer as labels jumping color when touched.
   Pin label color so it stays stable across has-success / has-error states.
   Scoped to body.iframe.ui-refresh and body.sidebar.ui-refresh so it covers
   every iframe-modal AND slide-out sidebar form (which renders inside an
   iframe with `body.sidebar`, not `body.iframe`) without bleeding into
   in-page forms that may want validation feedback. */
body.iframe.ui-refresh .has-success .control-label,
body.iframe.ui-refresh .has-error .control-label,
body.iframe.ui-refresh .has-success.checkbox label,
body.iframe.ui-refresh .has-success.radio label,
body.iframe.ui-refresh .has-success .radio,
body.iframe.ui-refresh .has-success .radio-inline,
body.iframe.ui-refresh .has-success .help-block {
    color: var(--fg-muted) !important;
}

/* Kartik kv-hint-icon (the info-circle next to labels with popovers) sits
 * about 2px below the label text — the wrapper has `vertical-align: middle`
 * but the inner `<a>` and `<i>` are at baseline, pulling the glyph down.
 * Make the wrapper + anchor flex-center the icon and inherit the label's
 * font size so the glyph reads as part of the label rather than a
 * low-floating afterthought. Applied globally, not just in sidebar. */
.ui-refresh .control-label .kv-hint-icon {
    display: inline-flex;
    align-items: center;
    vertical-align: middle;
    line-height: 1;
    position: relative;
    top: -1px;
}
.ui-refresh .control-label .kv-hint-icon a {
    display: inline-flex;
    align-items: center;
    vertical-align: middle;
    line-height: 1;
}
.ui-refresh .control-label .kv-hint-icon i.fa-info-circle {
    font-size: 12px;
    line-height: 1;
    vertical-align: middle;
}

/* Sidebar labels are split in two:
 *
 *   1) Field labels (`.control-label`, `.form-group > label` that isn't a
 *      choice label) follow the Swoogo Design System spec: 12px / medium /
 *      natural casing / muted color. No uppercase — hierarchy comes from
 *      weight and size, not casing.
 *
 *   2) Choice labels (checkbox / radio / their -inline variants) stay at
 *      14px default ink color.
 *
 * Both still pin to a single state-stable color (`--fg-default` on
 * choice, `--fg-muted` on field) — multiple legacy rules try to recolor
 * labels on focus/blur/has-success/has-error, which reads as a bug.
 * !important keeps every interaction state put. .has-error overrides
 * below still win because they carry an extra qualifier. */

/* Field labels. `.form-group > label` and `.form-group .control-label`
 * are field labels (direct child of form-group). Choice labels live under
 * `.form-group > .checkbox > label` (or `.radio`) — their parent isn't a
 * direct child of form-group, so those don't match these selectors and
 * keep their own styling below. */
body.sidebar.ui-refresh .form-group > label,
body.sidebar.ui-refresh .form-group .control-label {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    letter-spacing: normal;
    text-transform: none;
    color: var(--fg-muted) !important;
}

/* Choice labels — sentence case, readable size */
body.sidebar.ui-refresh .checkbox > label,
body.sidebar.ui-refresh .checkbox-inline,
body.sidebar.ui-refresh .radio > label,
body.sidebar.ui-refresh .radio-inline {
    color: var(--fg-default) !important;
    font-size: var(--font-size-sm);
}

/* ===== Stacked radioList rows (drawer + full-page setup forms) =====
   A Yii `radioList()` with no `radio-inline` renders a
   `<div role="radiogroup">` wrapper (a direct child of the `.form-group`)
   containing stacked `<div class="radio"><label><input type="radio"> text
   </label></div>` rows — so the `.radio` rows are GRANDCHILDREN of the
   form-group, not direct children. (The inline variant is different: the
   radiogroup div itself carries `.radio` and is handled by the segmented-pill
   rule below.) Bootstrap's legacy `.radio input` is `position:absolute;
   margin-left:-20px`, so the native dot floats 20px left of its label and, on
   the refresh surfaces, gets clipped out of view entirely. `.visibility-
   conditions` already fixes this locally (layout.css) — generalize that
   flex-row treatment to every stacked radio group in a refreshed AjaxDrawer
   and on the full-page tax/fees setup forms, so groups like the discount
   `type`/`apply_to_all` fields and the tax `net_or_gross`/`line_item_taxes`
   fields all line up consistently: input inline-left, label text wrapping
   under the text. Match the `[role="radiogroup"]` container directly (NOT via
   `.form-group >`): a field that passes `options.class` — e.g. the discount
   `type` field with `['options' => ['class' => 'required']]` — has its
   `form-group` class REPLACED by that class, so `.form-group > radiogroup`
   silently misses it. Exclude only TRUE inline groups (`:has(> .radio-inline)`)
   so this doesn't fight the segmented-pill treatment. */
.ui-refresh .ajax-drawer [role="radiogroup"]:not(:has(> .radio-inline)) > .radio,
.ui-refresh .list-page [role="radiogroup"]:not(:has(> .radio-inline)) > .radio,
.ui-refresh .event-settings-content [role="radiogroup"]:not(:has(> .radio-inline)) > .radio {
    padding-left: 0;
    margin-top: var(--space-1);
    margin-bottom: var(--space-1);
}
.ui-refresh .ajax-drawer [role="radiogroup"]:not(:has(> .radio-inline)) > .radio > label,
.ui-refresh .list-page [role="radiogroup"]:not(:has(> .radio-inline)) > .radio > label,
.ui-refresh .event-settings-content [role="radiogroup"]:not(:has(> .radio-inline)) > .radio > label {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    padding-left: 0;
    font-weight: var(--font-weight-normal);
}
.ui-refresh .ajax-drawer [role="radiogroup"]:not(:has(> .radio-inline)) > .radio input[type="radio"],
.ui-refresh .list-page [role="radiogroup"]:not(:has(> .radio-inline)) > .radio input[type="radio"],
.ui-refresh .event-settings-content [role="radiogroup"]:not(:has(> .radio-inline)) > .radio input[type="radio"] {
    position: static;
    margin: 0;
    flex-shrink: 0;
    /* Optical center against the first line of label text (see the
       equivalent note in layout.css visibility-conditions block). */
    margin-top: 2px;
}

/* ===== Framed radio-list group =====
   Shared surface for mutually exclusive choices whose labels are too long for
   a segmented control. Render through RadioListGroup so the field, optional
   hint and card treatment remain consistent across full-page forms. */
.ui-refresh .radio-list-group {
    padding: var(--space-4);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--bg-subtle);
}
.ui-refresh .radio-list-group > .form-group {
    margin: 0;
}

/* `.has-error` is the one validation state where the user explicitly
 * needs visual feedback — submission was blocked. The label-pin above
 * stops Yii's `.has-success` churn (labels darkening on every blur)
 * but otherwise has-error labels would be muted to default text. Match
 * the form-group/label combos with one extra class so this overrides
 * the pin without losing the success-stability behaviour. */
body.sidebar.ui-refresh .form-group.has-error > label,
body.sidebar.ui-refresh .form-group.has-error .control-label,
body.sidebar.ui-refresh .has-error.checkbox > label,
body.sidebar.ui-refresh .has-error.checkbox-inline,
body.sidebar.ui-refresh .has-error.radio > label,
body.sidebar.ui-refresh .has-error.radio-inline {
    color: var(--color-error) !important;
}

/* "Open In: Same window / New window" — Yii's radioList renders inline
   <label class="radio-inline"><input type="radio">label text</label> rows.
   Without ui-refresh styling, the native browser radio shows as a small
   purple dot beside text and reads as broken. Restyle as a segmented pill
   group matching the .segmented-control aesthetic above. Scoped to
   body.iframe.ui-refresh so it covers every iframe form's inline radios. */
body.iframe.ui-refresh .form-group > .radio[role="radiogroup"],
body.iframe.ui-refresh .form-group > div.radio:has(> .radio-inline),
.ui-refresh .segmented-radio-group {
    display: inline-flex;
    background: var(--paper-2);
    border: 1px solid var(--input-border);
    border-radius: var(--radius-lg);
    padding: 3px;
    gap: 2px;
    margin-top: var(--space-1);
}
body.iframe.ui-refresh .form-group > .radio[role="radiogroup"] > .radio-inline,
body.iframe.ui-refresh .form-group > div.radio:has(> .radio-inline) > .radio-inline,
.ui-refresh .segmented-radio-group > .radio-inline {
    display: inline-flex;
    align-items: center;
    margin: 0 !important;
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-md);
    cursor: pointer;
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    transition: var(--transition-fast);
    line-height: 1;
    min-height: 28px;
}
body.iframe.ui-refresh .form-group > .radio[role="radiogroup"] > .radio-inline:hover,
body.iframe.ui-refresh .form-group > div.radio:has(> .radio-inline) > .radio-inline:hover,
.ui-refresh .segmented-radio-group > .radio-inline:hover {
    color: var(--fg-default);
}
body.iframe.ui-refresh .form-group > .radio[role="radiogroup"] > .radio-inline:has(input:checked),
body.iframe.ui-refresh .form-group > div.radio:has(> .radio-inline) > .radio-inline:has(input:checked),
.ui-refresh .segmented-radio-group > .radio-inline:has(input:checked) {
    background: var(--primary);
    color: #fff;
}
body.iframe.ui-refresh .form-group > .radio[role="radiogroup"] input[type="radio"],
body.iframe.ui-refresh .form-group > div.radio:has(> .radio-inline) input[type="radio"],
.ui-refresh .segmented-radio-group input[type="radio"] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
    width: 0;
    height: 0;
    margin: 0;
}
.ui-refresh .segmented-radio-group input[type="radio"] {
    position: absolute !important;
}
body.iframe.ui-refresh .form-group > .radio[role="radiogroup"] > .radio-inline:focus-within,
body.iframe.ui-refresh .form-group > div.radio:has(> .radio-inline) > .radio-inline:focus-within,
.ui-refresh .segmented-radio-group > .radio-inline:focus-within {
    box-shadow: 0 0 0 3px var(--primary-ring);
    outline: 0;
}
/* A group label above a segmented control: the control is inline-flex, so an
   inline .control-label would sit beside it. Force the label onto its own line
   so it reads as a heading above the group. */
body.iframe.ui-refresh .field-virtualevent-s_auto_recording_location > .control-label {
    display: block;
}

/* ===== Iframe Modal: Bootstrap Collapse Jump Fix =====
   Bootstrap's collapse plugin animates ONLY `height` on the element. So
   anything outside the height box (margin) — and anything that contributes
   to the rendered height but isn't part of `height` (padding, border,
   min-height) — sits at full size while height transitions to 0. When the
   transition ends and `display: none` kicks in, that residual size vanishes
   instantly. That's the jump near the end of the animation.

   On the .well used for "pre-load details": padding 19px+25px + 1px border
   = ~40px of vertical space that doesn't animate, plus min-height 20px
   clamping the close, plus a 20px outer margin. All of it has to go for
   the animation to actually run cleanly to 0.

   Strip the well's box treatment on the collapse, replace the gap-below-
   the-well with padding on the stable .preload-check-* wrapper. The form-
   groups inside the well bring their own spacing. */
body.iframe.ui-refresh .well.collapse,
body.iframe.ui-refresh .well.collapsing {
    min-height: 0 !important;
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    background: transparent !important;
    box-shadow: none !important;
}
body.iframe.ui-refresh [class^="preload-check-"],
body.iframe.ui-refresh [class*=" preload-check-"] {
    padding-bottom: var(--space-5);
}

/* ===== Dashboard: Page Header ===== */
.ui-refresh .dashboard-page-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-6);
    margin-top: 18px;
    margin-bottom: 18px;
    padding: var(--space-2) var(--space-3);
    border-bottom: none;
}
/* Account dashboard: the page header sits above a Bootstrap .row, whose
   negative side margins extend its right edge 15px past #main-content's
   inner box. Pull the header's right edge by the same amount so the
   header-actions buttons align with the activity stream / right dashlet
   column underneath. */
.ui-refresh #content-container.account .dashboard-page-header {
    margin-right: -15px;
}
/* The dashlet columns inside #dash-row use 12px gutters. Bootstrap's default
   15px right padding on col-lg-9 adds an extra 15px between the rightmost
   dashlet panel and the activity stream panel. Zero it out so that gap
   matches the 24px dashlet-to-dashlet gap (12 + 12). */
.ui-refresh .dashboard-main-row > .col-lg-9 {
    padding-right: 0;
}
.ui-refresh .dashboard-page-header h1 {
    font-size: var(--font-size-xl);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
    margin: 0;
    line-height: var(--line-height-xl);
}
.ui-refresh .dashboard-page-header h1 i {
    color: var(--fg-muted);
    margin-right: var(--space-2);
}
.ui-refresh .dashboard-page-header .panel-heading-subtitle {
    margin-top: var(--space-1);
}
.ui-refresh .dashboard-page-header-actions {
    flex-shrink: 0;
    display: flex;
    gap: var(--space-3);
}
/* Split the button group into separate buttons with a gap between them */
.ui-refresh .dashboard-page-header-actions .btn-group {
    display: flex;
    gap: var(--space-3);
}
.ui-refresh .dashboard-page-header-actions .btn {
    border-radius: var(--radius-lg) !important;
}

/* ===== Direct Report: Page Header ===== */
.ui-refresh .report-page-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-6);
    margin-top: 18px;
    margin-bottom: 18px;
    padding: var(--space-2) 0;
    border-bottom: none;
}
.ui-refresh .report-page-header h1 {
    font-size: var(--font-size-xl);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
    margin: 0;
    line-height: var(--line-height-xl);
}
.ui-refresh .report-page-header-actions {
    flex-shrink: 0;
    display: flex;
    gap: var(--space-3);
}
.ui-refresh .report-page-header-actions .btn {
    border-radius: var(--radius-lg) !important;
}

/* ===== Dashboard: Inline Selector (/ Dashboard Name in h1) ===== */
.ui-refresh .dashboard-title-sep {
    color: var(--fg-subtle);
    font-weight: var(--font-weight-normal);
    margin: 0 var(--space-2);
}
.ui-refresh .dashboard-inline-selector {
    display: inline-block;
    vertical-align: baseline;
    position: relative;
}
/* The toggle is styled as plain text in accent color at h1 size */
.ui-refresh .dashboard-selector-name {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    color: var(--accent-primary);
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    text-decoration: none;
    transition: opacity var(--transition-fast);
}
.ui-refresh .dashboard-selector-name:hover,
.ui-refresh .dashboard-selector-name:focus {
    color: var(--accent-primary);
    text-decoration: none;
    opacity: 0.75;
}
/* Chevron sits small beside the name */
.ui-refresh .dashboard-selector-name > .fal {
    font-size: 0.6em;
    transition: transform var(--transition-fast);
}
.ui-refresh .dashboard-inline-selector.open > .dashboard-selector-name > .fal {
    transform: rotate(180deg);
}
/* System dashboard (no custom selected): render title in normal text color,
   not accent — the label "Account Dashboard" / "Event Dashboard" is just
   a fallback, not a named entity worth emphasizing. */
.ui-refresh .dashboard-inline-selector--system a.dashboard-selector-name.dropdown-toggle,
.ui-refresh .dashboard-inline-selector--system a.dashboard-selector-name.dropdown-toggle:hover,
.ui-refresh .dashboard-inline-selector--system a.dashboard-selector-name.dropdown-toggle:focus {
    color: var(--fg-default);
    opacity: 1;
}

/* ===== Dashboard: Dashlet Error State ===== */
.ui-refresh .dashlet-error {
    text-align: center;
    padding: var(--space-8) var(--space-4);
    color: var(--color-error);
    font-size: var(--font-size-sm);
    background: var(--color-error-bg);
    border-radius: var(--panel-radius);
    border: 1px solid var(--color-error);
}

/* ===== Dashboard: Empty Column State ===== */
/* Hide legacy "Weird, an empty column" text */
.ui-refresh #dash-row .col:empty::before {
    content: none;
}
/* Give empty columns enough height to be a valid drop target for drag-and-drop */
.ui-refresh #dash-row > .col {
    min-height: 200px;
}
/* Dashed CTA card for empty columns */
.ui-refresh .dashboard-empty-column {
    border: 2px dashed var(--border-default);
    border-radius: var(--panel-radius);
    padding: var(--space-6) var(--space-5);
    text-align: left;
    color: var(--fg-subtle);
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
}
.ui-refresh .dashboard-empty-column i {
    font-size: var(--font-size-xl);
}
.ui-refresh .dashboard-empty-column span {
    font-size: var(--font-size-sm);
    font-weight: 500;
}

/* ===== Activity Stream Sidebar ===== */
/* The activity stream is presented as a flat dashlet-style card so it
   visually aligns with the dashlet column to its left. Mirrors the
   .dash-panel chrome (border, radius, no shadow) without inheriting the
   sortable/drag affordances. */
.ui-refresh .activity-stream-sidebar {
    padding-top: 0;
    padding-left: var(--space-3);
    padding-right: var(--space-3);
}
.ui-refresh .activity-stream-panel {
    background: var(--bg-surface);
    border: 1px solid var(--panel-border);
    border-radius: var(--panel-radius);
}
.ui-refresh .activity-stream-panel-heading {
    padding: var(--space-3) var(--space-4);
    border-bottom: none;
}
.ui-refresh .activity-stream-panel-heading h2 {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--fg-default);
    margin: 0;
    line-height: var(--line-height-base);
}
.ui-refresh .activity-stream-panel-body {
    padding: var(--space-3);
}

/* ===== Dashboard: Status Banners (Event Dashboard) ===== */
/* Used for registration status and CFS draft messages on the event dashboard.
   Replaces the old Bootstrap panel + stacked-icon layout with a clean flex banner. */
/* When both the CFS and Registration banners are shown, lay them out in a
   two-column grid matching the legacy 50/50 split. auto-fit + minmax means:
     - single banner  → fills the full row
     - two banners on wide viewport → side-by-side 50/50
     - two banners on narrow viewport → stack (no media query needed)
   Grid items stretch to equal height so mismatched content lengths align. */
.ui-refresh .dashboard-status-banners {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(420px, 1fr));
    gap: var(--space-6);
    margin: 0 var(--space-3) var(--space-6) var(--space-3);
}
.ui-refresh .dashboard-status-banners .dashboard-status-banner {
    margin: 0;
}
.ui-refresh .dashboard-status-banner {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-lg);
    border: 1px solid var(--border-subtle);
    background: var(--bg-surface);
    margin: 0 var(--space-3) var(--space-3) var(--space-3);
}
.ui-refresh .dashboard-status-banner-icon {
    flex-shrink: 0;
    width: 2rem;
    height: 2rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-full);
    font-size: var(--font-size-base);
    color: var(--fg-muted);
    background: var(--bg-muted);
}
.ui-refresh .dashboard-status-banner--draft .dashboard-status-banner-icon {
    color: var(--color-warning);
    background: var(--color-warning-bg);
}
.ui-refresh .dashboard-status-banner--warning .dashboard-status-banner-icon {
    color: var(--color-warning);
    background: var(--color-warning-bg);
}
.ui-refresh .dashboard-status-banner--info .dashboard-status-banner-icon {
    color: var(--color-info);
    background: var(--color-info-bg);
}
.ui-refresh .dashboard-status-banner--muted .dashboard-status-banner-icon {
    color: var(--fg-muted);
    background: var(--bg-muted);
}
.ui-refresh .dashboard-status-banner-content {
    flex: 1;
    min-width: 0;
}
.ui-refresh .dashboard-status-banner-header {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
}
.ui-refresh .dashboard-status-banner-summary {
    min-width: 0;
}
.ui-refresh .dashboard-status-banner-heading {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
    margin: 0;
    line-height: var(--line-height-sm);
}
.ui-refresh .dashboard-status-banner .panel-heading-subtitle {
    display: block;
    margin: var(--space-1) 0 0 0;
    font-size: var(--font-size-sm);
}
.ui-refresh .dashboard-status-banner-copy {
    margin: var(--space-2) 0 0 0;
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
    line-height: var(--line-height-base);
}
.ui-refresh .dashboard-status-banner-copy strong {
    color: var(--fg-default);
    font-weight: 600;
}
.ui-refresh .dashboard-status-banner-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin-top: var(--space-2);
}
.ui-refresh .dashboard-status-banner-actions .btn {
    margin: 0;
}
.ui-refresh .dashboard-status-banner-detail {
    margin: var(--space-2) 0 0 0;
    font-size: var(--font-size-xs);
    color: var(--fg-subtle);
    line-height: var(--line-height-sm);
}
.ui-refresh .dashboard-status-banner-detail strong {
    color: var(--fg-default);
    font-weight: 600;
}

/* ===== Activity Stream ===== */
/* Modernize the timeline: softer borders, token-based colors, cleaner cards */
/* Hide the legacy full-height bar — replaced by per-row segments so the line
   doesn't dangle past the final icon. */
.ui-refresh #activity-stream-bar {
    display: none;
}
/* Draw a line from each row's icon down toward the next row's icon. The last
   row has no ::before, so the timeline ends cleanly at the last icon.
   Icon is 36x36 at top:0 / left:0 of .activity-row — center at (18, 18).
   `bottom: -20px` overshoots slightly into the inter-row gap (16px margin
   from .activity-row-inner) to visually meet the next icon. */
.ui-refresh #activity-stream .activity-row:not(:last-child)::before {
    content: '';
    position: absolute;
    top: 18px;         /* start at icon vertical center (36 / 2) */
    bottom: -20px;     /* extend into the gap to meet the next icon */
    left: 17px;        /* 2px line centered horizontally on the 36px icon */
    width: 2px;
    background-color: var(--border-subtle);
    pointer-events: none;
    z-index: 0;
}
.ui-refresh #activity-stream-inner {
    margin-top: var(--space-1);
}
/* The outer .activity-stream-panel already provides a card, so per-row chrome
   is intentionally flat — no border, background, or shadow. The timeline
   rail + the row content (title, faces, timeago) carry the visual structure. */
.ui-refresh #activity-stream .activity-row-inner {
    background-color: transparent;
    border: none;
    border-radius: 0;
    margin: 0 0 var(--space-4) 40px;
    padding: var(--space-1) var(--space-2) var(--space-2) 0;
    box-shadow: none;
}
/* Hide the legacy triangle pointers */
.ui-refresh #activity-stream .activity-row-inner::before,
.ui-refresh #activity-stream .activity-row-inner::after {
    display: none;
}
.ui-refresh #activity-stream .title {
    color: var(--fg-default);
    font-weight: var(--font-weight-medium);
    font-size: var(--font-size-sm);
}
.ui-refresh #activity-stream .activity-row .timeago {
    color: var(--fg-subtle);
    font-size: var(--font-size-xs);
}
.ui-refresh #activity-stream .activity-row .faces {
    margin: var(--space-2) 0 0;
}
.ui-refresh #activity-stream .activity-row .faces img:not(.popup-img) {
    border-radius: var(--radius-full);
    height: 24px;
    width: 24px;
    border: 1px solid var(--border-subtle);
    transition: transform var(--transition-fast);
}
.ui-refresh #activity-stream .activity-row .faces img:not(.popup-img):hover {
    transform: scale(1.15);
}
.ui-refresh #activity-stream .activity-row .activity-row-icon {
    /* color-mix bakes the accent tint into the card surface so the icon
       reads opaque in both light and dark modes — var(--accent-subtle)
       resolves to a translucent rgba() in dark mode, which let the
       timeline rail behind the icon bleed through. Do NOT add
       position: relative here — backend.css positions the icon absolute
       at top:0/left:0 of .activity-row; overriding that to relative
       reflows the icon and misaligns the timeline rail. */
    background-color: color-mix(in srgb, var(--primary) 22%, var(--card)) !important;
    color: var(--accent-primary);
    border: 1px solid var(--accent-muted);
    border-radius: var(--radius-full);
    height: 36px;
    width: 36px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--font-size-xs);
    box-shadow: var(--shadow-xs);
}

/* ===== Activity Stream Popover ===== */
/* Modernize the AJAX popover card that appears when clicking a registrant avatar.
   backend.css sets .popover { min-width: 400px; border: none } globally —
   !important is needed on competing properties. */
.ui-refresh .popover:has(.popup-img) {
    min-width: 260px !important;
    max-width: 300px !important;
    border-radius: var(--radius-xl) !important;
    box-shadow: var(--shadow-lg) !important;
    border: 1px solid var(--border-subtle) !important;
    padding: 0 !important;
}
.ui-refresh .popover:has(.popup-img) > .arrow {
    display: none;
}
.ui-refresh .popover:has(.popup-img) .popover-content {
    padding: var(--space-5);
}
/* Close button — top-right, subtle */
.ui-refresh .popover:has(.popup-img) .close {
    float: right;
    font-size: var(--font-size-xl);
    color: var(--fg-subtle);
    opacity: 1;
    font-weight: var(--font-weight-normal);
    line-height: 1;
    transition: color var(--transition-fast);
}
.ui-refresh .popover:has(.popup-img) .close:hover {
    color: var(--fg-default);
}
/* Avatar — circular, constrained */
.ui-refresh .popover:has(.popup-img) img.popup-img {
    width: 56px !important;
    height: 56px !important;
    max-width: 56px !important;
    max-height: 56px !important;
    object-fit: cover;
    border-radius: var(--radius-full);
    margin-right: var(--space-3);
    margin-bottom: var(--space-3);
    border: 2px solid var(--border-subtle);
}
/* Name */
.ui-refresh .popover:has(.popup-img) b {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
}
/* Metadata text */
.ui-refresh .popover:has(.popup-img) p {
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
    line-height: var(--line-height-sm);
    margin-bottom: var(--space-3);
}
/* View Registration button — full-width, accent */
.ui-refresh .popover:has(.popup-img) p:last-child {
    margin-bottom: 0;
}
.ui-refresh .popover:has(.popup-img) .btn {
    width: 100%;
    justify-content: center;
    border-radius: var(--radius-md);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}

/* ===== Activity Stream Popover Skeleton ===== */
@keyframes skeleton-pulse {
    0%, 100% { opacity: 0.4; }
    50% { opacity: 1; }
}
.ui-refresh .popover-skeleton {
    width: 224px;
    padding: 6px 0 0;
}
.ui-refresh .popover:has(.popover-skeleton) {
    min-width: 260px !important;
    max-width: 300px !important;
    border-radius: var(--radius-xl) !important;
    box-shadow: var(--shadow-lg) !important;
    border: 1px solid var(--border-subtle) !important;
    padding: 0 !important;
}
.ui-refresh .popover:has(.popover-skeleton) > .arrow {
    display: none;
}
.ui-refresh .popover:has(.popover-skeleton) .popover-content {
    padding: var(--space-5);
}
.ui-refresh .popover-skeleton-header {
    display: flex;
    align-items: center;
    margin-bottom: var(--space-3);
}
.ui-refresh .popover-skeleton-avatar {
    width: 56px;
    height: 56px;
    border-radius: var(--radius-full);
    background: var(--bg-muted);
    flex-shrink: 0;
    margin-right: var(--space-3);
    animation: skeleton-pulse 1.5s ease-in-out infinite;
}
.ui-refresh .popover-skeleton-name {
    height: 16px;
    width: 100px;
    border-radius: var(--radius-sm);
    background: var(--bg-muted);
    animation: skeleton-pulse 1.5s ease-in-out infinite;
    animation-delay: 0.1s;
}
.ui-refresh .popover-skeleton-line {
    height: 12px;
    border-radius: var(--radius-sm);
    background: var(--bg-muted);
    margin-bottom: var(--space-2);
    animation: skeleton-pulse 1.5s ease-in-out infinite;
}
.ui-refresh .popover-skeleton-line--long {
    width: 100%;
    animation-delay: 0.2s;
}
.ui-refresh .popover-skeleton-line--medium {
    width: 70%;
    animation-delay: 0.3s;
}
.ui-refresh .popover-skeleton-btn {
    height: 34px;
    width: 100%;
    border-radius: var(--radius-md);
    background: var(--bg-muted);
    margin-top: var(--space-4);
    animation: skeleton-pulse 1.5s ease-in-out infinite;
    animation-delay: 0.4s;
}

/* ===== Progress Bar (dashlet percent bar) ===== */
.ui-refresh .progress-container {
    display: flex;
    align-items: center;
}
.ui-refresh .progress-text {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    margin-right: var(--space-2);
    white-space: nowrap;
    color: var(--fg-default);
}
.ui-refresh .progress {
    flex: 1;
    margin-bottom: 0;
    height: 6px;
    background: var(--paper-2);
    border-radius: 999px;
    overflow: hidden;
    border: 1px solid var(--input-border);
    box-shadow: none;
}
.ui-refresh .progress-bar-success {
    background-color: var(--color-success);
    border-radius: var(--radius-full);
}
.ui-refresh .progress-bar-warning {
    background-color: var(--color-warning);
    border-radius: var(--radius-full);
}

/* ===== A2A Dashlet Footer ===== */
.ui-refresh .a2a-dashlet-footer {
    border-top: 1px solid var(--border-subtle);
}

/* ===== Schedule Go Live toggle — spacing before the collapsible dates ===== */
/* padding-bottom (not margin) prevents margin-collapsing jump when collapse
   element transitions to display:none on close. */
.ui-refresh .checkbox:has(+ .go-live-dates) {
    padding-bottom: var(--space-3);
}
/* ===== Registration Closed Banner (collapsible) ===== */
/* On the Settings page the banner renders inside the .event-settings-doc grid
   between identity and chips, so it inherits the content column width. On
   other tabs (date_and_time, location, themes) it renders above the form
   content full-width, so the max-width constraint keeps it aligned with the
   panels underneath. Background uses --bg-surface (white card / dark card)
   to lift above the page paper, matching the hierarchy of the form inputs. */
.ui-refresh .registration-closed-banner {
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    max-width: 960px;
    margin: 0 auto var(--space-4);
}
.ui-refresh .registration-closed-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-3) var(--space-4);
    cursor: pointer;
    user-select: none;
}
.ui-refresh .registration-closed-header-left {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--fg-default);
}
.ui-refresh .registration-closed-header-left i {
    font-size: var(--font-size-base);
    color: var(--fg-muted);
}
.ui-refresh .registration-closed-chevron {
    color: var(--fg-muted);
    font-size: var(--font-size-xs);
    transition: transform var(--transition-fast);
}
.ui-refresh .registration-closed-header[aria-expanded="true"] .registration-closed-chevron {
    transform: rotate(180deg);
}
.ui-refresh .registration-closed-body {
    padding: 0 var(--space-4) var(--space-4);
}
.ui-refresh .registration-closed-body p {
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
    margin-bottom: var(--space-3);
}
/* List-page context (e.g., reg-types index) where the banner is a sibling of
   wide list rows and a flush-left page heading instead of a 960px form-doc
   grid. Remove the max-width clamp and centering so the banner aligns with
   its neighbors edge-to-edge. */
.ui-refresh .registration-closed-banner.is-full-width {
    max-width: none;
    margin-left: 0;
    margin-right: 0;
}

/* ===== Event Settings — Save button error tooltip =====
   Event Settings and Clone Event share the same `.subpage-head` chrome as
   Registrant Types and the Registration Form (chip breadcrumbs on the left,
   action row on the right). The pieces below scope Save-button behavior via
   the `.save-btn-wrap` hook, which only those two pages emit — the filled
   .btn-primary Save and its validation-error tooltip. */
.ui-refresh .subpage-head .save-btn-wrap .btn-primary {
    flex-shrink: 0;
}
/* ===== Save Button Error Tooltip ===== */
/* Shows validation errors on hover over the disabled Save button.
   Positioned relative to the button's parent in the sticky header. */
.ui-refresh .save-btn-errors {
    display: none;
    position: absolute;
    top: calc(100% + var(--space-2));
    right: 0;
    z-index: 20;
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: var(--space-2);
    min-width: 280px;
    max-width: 400px;
}
/* Invisible bridge between button and tooltip to keep hover alive */
.ui-refresh .save-btn-errors::before {
    content: '';
    position: absolute;
    top: calc(-1 * var(--space-2));
    left: 0;
    right: 0;
    height: var(--space-2);
}
.ui-refresh .save-btn-errors:empty {
    display: none !important;
}
/* The Save button uses pointer-events:none when disabled, so it can't receive
   hover. Instead, detect hover on the parent (which wraps both button + tooltip).
   :focus-within covers keyboard users — when the button is disabled, event-save-button.js
   adds tabindex="0" to the wrap so Tab lands here and reveals the error list. */
/* The wrap stays in the .subpage-head flow — the whole head is sticky on
   doc pages (see `#main-content:has(.event-settings-doc) .subpage-head`),
   so the Save button stays reachable while scrolling. The wrap is only the
   positioning anchor for the absolutely-positioned .save-btn-errors tooltip
   and the hover/focus target that reveals it. */
.ui-refresh .subpage-head .save-btn-wrap {
    position: relative;
}
.ui-refresh .save-btn-wrap:hover .save-btn-errors,
.ui-refresh .save-btn-wrap:focus-within .save-btn-errors {
    display: block;
}
.ui-refresh .save-btn-wrap:focus {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
    border-radius: var(--radius-md);
}
.ui-refresh .save-btn-wrap:focus:not(:focus-visible) {
    outline: none;
}
.ui-refresh .save-btn-errors-title {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    color: var(--fg-muted);
    padding: var(--space-1) var(--space-2);
    margin-bottom: var(--space-1);
}
.ui-refresh .save-btn-error-item {
    display: block;
    padding: var(--space-1) var(--space-2);
    font-size: var(--font-size-xs);
    color: var(--color-error);
    text-decoration: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background-color 0.15s ease;
}
.ui-refresh .save-btn-error-item:hover,
.ui-refresh .save-btn-error-item:focus {
    background: var(--color-error-bg);
    color: var(--color-error);
    text-decoration: none;
    outline: none;
}
/* Stronger indicator for keyboard users — overrides the outline:none above
   only when focus came from the keyboard, not a click. */
.ui-refresh .save-btn-error-item:focus-visible {
    outline: 2px solid var(--color-error);
    outline-offset: -1px;
}
.ui-refresh .save-btn-error-item i {
    margin-right: var(--space-1);
}
.ui-refresh .save-btn-error-msg {
    color: var(--fg-muted);
    font-weight: var(--font-weight-normal);
}

/* ===================================================================
   RESPONSIVE — Mobile overrides (max-width: 767px)
   Matches the responsive-grid breakpoint for consistency.
   =================================================================== */
@media only screen and (max-width: 767px) {

    /* Webhook enablement banner: let the action button wrap under the text. */
    .ui-refresh .inline-alert {
        flex-wrap: wrap;
    }

    /* --- AJAX Drawer — full width on mobile --- */
    .ui-refresh .ajax-drawer {
        width: 100vw;
    }

    /* --- Confirm dialog — tighter on mobile --- */
    .ui-refresh .confirm-dialog {
        width: calc(100vw - var(--space-8));
        padding: var(--space-6) var(--space-6) var(--space-4);
    }

    /* --- Dashlet panel-body — tighter padding on mobile --- */
    .ui-refresh #content-container .dash-panel .panel-body {
        padding: var(--space-2);
    }

    /* --- Pagination — wrap if needed --- */
    .ui-refresh .pagination {
        flex-wrap: wrap;
    }

    /* --- Dashboard page header — spacing and layout on mobile --- */
    .ui-refresh .dashboard-page-header {
        flex-direction: column;
        align-items: flex-start;
        gap: var(--space-3);
        margin-top: var(--space-5);
        padding: var(--space-2) calc(var(--space-3) + var(--space-6));
    }
    .ui-refresh .dashboard-page-header-actions {
        width: 100%;
    }
    .ui-refresh .dashboard-page-header-actions .btn {
        flex: 1;
    }

    /* --- Direct Report page header — spacing and layout on mobile --- */
    .ui-refresh .report-page-header {
        flex-direction: column;
        align-items: flex-start;
        gap: var(--space-3);
        margin-top: var(--space-5);
        padding: var(--space-2) 0;
    }
    .ui-refresh .report-page-header-actions {
        width: 100%;
    }
    .ui-refresh .report-page-header-actions .btn {
        flex: 1;
    }

    /* --- Activity stream sidebar — stack below dashlets on mobile --- */
    .ui-refresh .activity-stream-sidebar {
        margin-top: var(--space-4);
    }
    .ui-refresh #activity-stream-bar,
    .ui-refresh #activity-stream .activity-row:not(:last-child)::before {
        display: none;
    }
    .ui-refresh #activity-stream .activity-row-inner {
        margin-left: 0;
    }
    .ui-refresh #activity-stream .activity-row .activity-row-icon {
        display: none;
    }

    /* --- Status banners — stack icon and content vertically on mobile --- */
    .ui-refresh .dashboard-status-banner {
        flex-direction: column;
        gap: var(--space-2);
        padding: var(--space-3) var(--space-4);
    }
    .ui-refresh .dashboard-status-banner-header {
        gap: var(--space-2);
    }

    /* --- Wide drawer — full width on mobile --- */
    .ui-refresh .ajax-drawer.ajax-drawer--wide {
        width: 100vw;
    }

    /* --- Form row — single column on mobile --- */
    .ui-refresh .form-row {
        grid-template-columns: 1fr;
        gap: var(--space-3);
    }

    /* --- Nav tabs — horizontal scroll on mobile --- */
    .ui-refresh .nav-tabs {
        display: flex;
        flex-wrap: nowrap;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
    }
    .ui-refresh .nav-tabs::-webkit-scrollbar {
        display: none;
    }
    .ui-refresh .nav-tabs > li {
        flex-shrink: 0;
    }
    .ui-refresh .nav-tabs > li > a {
        white-space: nowrap;
        font-size: var(--font-size-xs);
        padding: var(--space-2) var(--space-3);
    }

    /* --- Panel-body with tabs — restore side padding on mobile so fields
       have enough touch space --- */
    .ui-refresh #content-container .panel-body:has(> .nav-tabs) {
        padding-left: var(--space-3);
        padding-right: var(--space-3);
    }
    .ui-refresh .panel-body > .nav-tabs {
        margin-top: 0;
        margin-left: calc(-1 * var(--space-3));
        margin-right: calc(-1 * var(--space-3));
        padding-left: var(--space-3);
    }

    /* --- Save button error tooltip — appears above the fixed bottom bar --- */
    .ui-refresh .save-btn-errors {
        bottom: calc(100% + var(--space-2));
        top: auto;
        min-width: 0;
        max-width: calc(100vw - var(--space-8));
    }

    /* --- Registration closed banner — tighter padding on mobile --- */
    .ui-refresh .registration-closed-header {
        padding: var(--space-2) var(--space-3);
    }
    .ui-refresh .registration-closed-body {
        padding: 0 var(--space-3) var(--space-3);
    }

    /* --- Waitlist duration row — keep side-by-side on mobile --- */
    .ui-refresh .waitlist-duration-input {
        width: 80px;
    }

    /* --- Toast notifications — sit above the fixed bottom sticky header --- */
    .ui-refresh .ui-toast-container {
        bottom: 60px;
        right: var(--space-3);
        left: var(--space-3);
    }
    .ui-refresh .ui-toast {
        max-width: none;
    }

    /* --- Virtual Location wells — transparent, no mobile override needed --- */

    /* --- Panel-expanded-settings — tighten indentation on mobile so nested
       settings don't eat too much horizontal space --- */
    .ui-refresh #content-container .panel-expanded-settings {
        margin-left: var(--space-2);
        padding: var(--space-1) var(--space-3);
    }

    /* --- Theme chooser — single column, filter adjustments --- */
    .ui-refresh .theme-list-view .item {
        flex-basis: 100% !important;
    }
    .ui-refresh .theme-list-view {
        margin-right: 0 !important;
        gap: var(--space-4);
    }
    /* Hide the filter label on mobile; just show the dropdown button */
    .ui-refresh .theme-filter-wrap .dropdown > label {
        display: none;
    }
    /* On mobile the sticky header is a fixed bottom bar. The filter dropdown
       menu needs to open upward (dropup) so it doesn't get clipped. */
    .ui-refresh .theme-filter-wrap .dropdown-menu {
        top: auto;
        bottom: 100%;
        margin-bottom: var(--space-1);
    }
}

/* Dashlet responsive-grid cleanup. Matches responsive-grid.css's own
   two-part breakpoint exactly (phones OR tablet-class devices by
   device-width), not the 767px-only block above: tablets get the legacy
   stacked-card layout too, and without these overrides they keep its
   #f9f9f9 zebra cells, which are unreadable in dark mode. */
@media only screen and (max-width: 767px),
       (min-device-width: 768px) and (max-device-width: 1024px) {
    /* --- Bookmarks dashlet — override responsive-grid's mobile transformation ---
       Bookmarks has a simple single-column list that doesn't need the responsive
       block layout with inline "Name" labels. Keep our clean styling instead. */
    .ui-refresh :is(#content-container, #report-content) .dash-panel[data-type="bookmarks"] .responsive-grid table,
    .ui-refresh :is(#content-container, #report-content) .dash-panel[data-type="bookmarks"] .responsive-grid thead,
    .ui-refresh :is(#content-container, #report-content) .dash-panel[data-type="bookmarks"] .responsive-grid tbody,
    .ui-refresh :is(#content-container, #report-content) .dash-panel[data-type="bookmarks"] .responsive-grid th,
    .ui-refresh :is(#content-container, #report-content) .dash-panel[data-type="bookmarks"] .responsive-grid td,
    .ui-refresh :is(#content-container, #report-content) .dash-panel[data-type="bookmarks"] .responsive-grid tr {
        display: revert !important;
    }
    .ui-refresh :is(#content-container, #report-content) .dash-panel[data-type="bookmarks"] .responsive-grid tr {
        border: none !important;
    }
    .ui-refresh :is(#content-container, #report-content) .dash-panel[data-type="bookmarks"] .responsive-grid tbody td:not(.handle) {
        padding-left: var(--space-4) !important;
        position: static !important;
    }
    .ui-refresh :is(#content-container, #report-content) .dash-panel[data-type="bookmarks"] .responsive-grid tbody td:not(.handle):before {
        content: none !important;
    }
    .ui-refresh :is(#content-container, #report-content) .dash-panel[data-type="bookmarks"] .responsive-grid > tbody > tr > td:nth-of-type(2n+1) {
        background-color: transparent !important;
    }
    .ui-refresh :is(#content-container, #report-content) .dash-panel[data-type="bookmarks"] .responsive-grid thead tr {
        position: absolute !important;
        top: -9999px !important;
        left: -9999px !important;
    }

    /* --- Multi-column dashlets — clean up the responsive-grid mobile card look ---
       Keep the block layout but apply our design tokens instead of legacy styles. */
    /* Remove the border around the entire table and grid-view wrapper */
    .ui-refresh :is(#content-container, #report-content) .dash-panel .responsive-grid,
    .ui-refresh :is(#content-container, #report-content) .dash-panel .grid-view table,
    .ui-refresh :is(#content-container, #report-content) .dash-panel .grid-view {
        border: none !important;
    }
    .ui-refresh :is(#content-container, #report-content) .dash-panel .responsive-grid tr {
        border: 1px solid var(--border-subtle) !important;
        border-radius: var(--radius-md);
        margin-bottom: var(--space-3);
        overflow: hidden;
        background: var(--bg-surface);
    }
    /* Kill zebra striping */
    .ui-refresh :is(#content-container, #report-content) .dash-panel .responsive-grid > tbody > tr > td,
    .ui-refresh :is(#content-container, #report-content) .dash-panel .responsive-grid > tbody > tr > td:nth-of-type(2n+1) {
        background-color: transparent !important;
    }
    /* Tighter cell padding, no stray borders */
    .ui-refresh :is(#content-container, #report-content) .dash-panel .responsive-grid tbody td:not(.handle) {
        padding: var(--space-2) var(--space-3) var(--space-2) 40% !important;
        min-height: 20px !important;
        border: none !important;
        border-bottom: 1px solid var(--border-subtle) !important;
    }
    .ui-refresh :is(#content-container, #report-content) .dash-panel .responsive-grid tbody td:last-child {
        border-bottom: none !important;
    }
    /* Labels — left-aligned, muted, smaller */
    .ui-refresh :is(#content-container, #report-content) .dash-panel .responsive-grid tbody td:not(.handle):before {
        font-weight: 500 !important;
        color: var(--fg-muted) !important;
        font-size: var(--font-size-xs) !important;
        text-align: left !important;
        top: var(--space-2) !important;
        /* Title text sits at --space-4 from the panel edge; the card's 1px
           border is outside the td, so subtract it to line the label up. */
        left: calc(var(--space-4) - 1px) !important;
    }
    /* Value text */
    .ui-refresh :is(#content-container, #report-content) .dash-panel .responsive-grid tbody td {
        font-size: var(--font-size-sm) !important;
        color: var(--fg-default);
    }
    /* Disable hover on mobile — touch devices don't hover */
    .ui-refresh :is(#content-container, #report-content) .dash-panel .responsive-grid tbody tr:hover {
        background: var(--bg-surface) !important;
    }

    /* --- Remove Bootstrap's .table-responsive border on mobile --- */
    .ui-refresh #content-container .dash-panel .table-responsive {
        border: none !important;
    }

    /* --- Don't pull grids up on mobile — cards need spacing from the heading --- */
    .ui-refresh #content-container .dash-panel .panel-body > div:first-child > .grid-view,
    .ui-refresh #content-container .dash-panel .panel-body > .grid-view:first-child {
        margin-top: 0 !important;
    }

    /* --- Cards run edge-to-edge so labels line up with the dashlet title ---
       Beats the phone-only `padding: var(--space-2)` on .panel-body above
       (later in source, higher specificity), giving phones and tablets the
       same inset. :has() keeps non-grid dashlets' phone padding untouched. */
    .ui-refresh :is(#content-container, #report-content) .dash-panel .panel-body:has(.responsive-grid) {
        padding-left: 0;
        padding-right: 0;
    }
}

/* ===== Toast Notifications ===== */
.ui-refresh .ui-toast-container {
    position: fixed;
    bottom: var(--space-6);
    right: var(--space-6);
    z-index: 1080;
    display: flex;
    flex-direction: column-reverse;
    gap: var(--space-2);
    pointer-events: none;
}
.ui-refresh .ui-toast {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    background: var(--bg-surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    font-size: var(--font-size-sm);
    color: var(--fg-default);
    pointer-events: auto;
    opacity: 0;
    transform: translateY(8px);
    transition: opacity var(--transition-normal), transform var(--transition-normal);
    max-width: 400px;
}
.ui-refresh .ui-toast--in {
    opacity: 1;
    transform: translateY(0);
}
.ui-refresh .ui-toast--out {
    opacity: 0;
    transform: translateY(8px);
    transition: opacity 300ms ease, transform 300ms ease;
}
/* Toast type is conveyed by the colored icon; no colored left border
   (design cleanup #15). */

.ui-refresh .ui-toast-icon {
    flex-shrink: 0;
    font-size: var(--font-size-base);
}
.ui-refresh .ui-toast--success .ui-toast-icon { color: var(--color-success); }
.ui-refresh .ui-toast--error   .ui-toast-icon { color: var(--color-error); }
.ui-refresh .ui-toast--warning .ui-toast-icon { color: var(--color-warning); }
.ui-refresh .ui-toast--info    .ui-toast-icon { color: var(--color-info); }

.ui-refresh .ui-toast-message {
    flex: 1;
    min-width: 0;
    overflow-wrap: break-word;
    line-height: var(--line-height-sm);
}
.ui-refresh .ui-toast--blocked-sessions .ui-toast-message,
.ui-refresh .ui-toast--item-list .ui-toast-message {
    white-space: pre-line;
}
.ui-refresh .ui-toast-message ul {
    margin: var(--space-2) 0 0;
    padding-left: var(--space-4);
}
.ui-refresh .ui-toast-message li + li {
    margin-top: var(--space-1);
}
.ui-refresh .ui-toast-close {
    flex-shrink: 0;
    background: none;
    border: none;
    color: var(--fg-subtle);
    cursor: pointer;
    padding: var(--space-1);
    font-size: var(--font-size-xs);
    line-height: 1;
    transition: color var(--transition-fast);
}
.ui-refresh .ui-toast-close:hover {
    color: var(--fg-default);
}

/* Collapse containers: remove bottom margin on the last child so Bootstrap's
   slide animation doesn't overshoot then jump when overflow:hidden is removed. */
.ui-refresh .collapse > :last-child,
.ui-refresh .collapsing > :last-child {
    margin-bottom: 0;
}

/* ===== Event Settings: Free/Paid radio top margin fix ===== */
/* Replaces inline registerCss in _general.php */
.ui-refresh .field-event-free_event {
    margin-top: 0 !important;
    text-align: left;
}
.ui-refresh .field-event-free_event .radio-inline {
    padding-top: 0;
}

/* Waitlist Duration — number input + unit toggle on one row */
.ui-refresh .waitlist-duration-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}
.ui-refresh .waitlist-duration-input {
    width: 100px;
    margin-bottom: 0 !important;
}
.ui-refresh .waitlist-duration-units {
    margin-bottom: 0 !important;
}

/* ===== UI Refresh Toggle (profile dropdown) ===== */
.ui-refresh-divider {
    border-top: 1px solid var(--panel-border);
    margin: 0;
    padding: 0;
    list-style: none;
}
.ui-refresh-toggle__heading {
    list-style: none;
    padding: var(--space-3) var(--space-4) 0;
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
    font-weight: var(--font-weight-medium);
    white-space: nowrap;
}
.ui-refresh-toggle {
    list-style: none;
}
.ui-refresh-toggle__container {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 40px;
    padding: var(--space-2) var(--space-4);
    margin: 0;
    font-weight: normal;
}
.ui-refresh-toggle__label {
    color: var(--fg-default);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    white-space: nowrap;
}
.ui-refresh .surface-mode-current {
    display: block;
    margin-top: 0;
    color: var(--fg-muted);
    font-size: 11px;
    font-weight: var(--font-weight-normal);
    line-height: 12px;
}
.ui-refresh .ui-refresh-toggle__label--with-subtext {
    line-height: 14px;
}
/* Readonly variant — the seeded SEM "Logo" / "company logo" fields lock the
 * group list. Render allowed groups as pills instead of a bulleted list. */
.ui-refresh .sidebar-upload-groups-pills {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.ui-refresh .sidebar-upload-groups-pill {
    display: inline-flex;
    align-items: center;
    padding: 2px var(--space-2);
    border-radius: var(--radius-full);
    background: var(--bg-muted);
    color: var(--fg-default);
    font-size: var(--font-size-xs);
    font-weight: 500;
    line-height: 1.4;
}

/* ===== Tagify chip input (vendored web/css/designer/tagify.css)
 * The ui-refresh .form-control rule pins height to var(--input-height) which
 * is wrong for Tagify's <tags> wrapper — the field needs to grow as chips
 * are added. Higher specificity (3 classes) wins against both the vendor's
 * `.tagify.form-control` and the ui-refresh `.form-control` rule. */
.ui-refresh :is(.sidebar-form, .event-settings-content) tags.tagify.form-control {
    height: auto;
    padding: 0;
    line-height: 1.25;
    /* Tagify's chip background uses these CSS variables — point them at
     * semantic tokens so chips match the rest of ui-refresh. */
    --tag-bg: var(--bg-muted);
    --tag-hover: var(--bg-muted);
}
.ui-refresh :is(.sidebar-form, .event-settings-content) tags.tagify.form-control:hover {
    border-color: var(--input-border);
}
.ui-refresh :is(.sidebar-form, .event-settings-content) tags.tagify.form-control.tagify--focus {
    border-color: var(--accent-primary);
    box-shadow: var(--focus-ring);
}
.ui-refresh .tagify__tag>div::before {
    background-color: var(--bg-page);
    box-shadow: none;
    border: 1px solid var(--panel-border);
}
.ui-refresh .tagify__tag__removeBtn {
    background-color: transparent;
    color: var(--fg-default);
}
.ui-refresh .tagify__tag > div {
    color: var(--fg-default);
    border-radius: var(--radius-sm);
    font-family: var(--font-family);
    font-size: var(--font-size-xs);
    min-width: 0;
}
.ui-refresh .tagify__tag {
    max-width: calc(100% - 10px);
}
.ui-refresh .tagify__tag__removeBtn {
    flex-shrink: 0;
}
.ui-refresh .tagify__tag__removeBtn::after {
    position: static;
    top: auto;
    left: auto;
}
.ui-refresh tags.tagify.form-control {
    align-items: center;
}

/* ===== jQuery Timepicker dropdown (web/plugins/timepicker)
 * The picker is body-injected as .ui-timepicker-wrapper; .ui-refresh on
 * <body> covers it via the cascade. Tighten the dropdown items to xs. */
.ui-refresh .ui-timepicker-wrapper {
    border-radius: var(--space-2);
}
.ui-refresh .ui-timepicker-list li {
    font-size: var(--font-size-xs);
}

/* ===== Segmented control (Bootstrap btn-group with data-toggle="buttons")
 * Renders for kartik\widgets\ActiveField::radioButtonGroup() — used for
 * Date Input Type, Page Widget settings, search forms, etc.
 *
 * iOS-style pill: muted-gray track, white "card" on the active segment with
 * a subtle shadow. Same pattern as Stripe / Linear / iOS segmented controls.
 * Higher specificity (3 classes + 1 attribute = 0,4,1) than the generic
 * `.ui-refresh .btn` rules (0,2,0) so we win without !important. */
.ui-refresh .btn-group[data-toggle="buttons"] {
    display: inline-flex;
    padding: 2px;
    border-radius: var(--radius-md);
    box-shadow: none;
    border: none;
    gap: 0;
    /* Reset BS3 collapsed-margin between adjacent .btn children. */
}
.ui-refresh .btn-group[data-toggle="buttons"] > .btn,
.ui-refresh .btn-group[data-toggle="buttons"] > .btn.btn-default {
    flex: 0 0 auto;
    padding: 0 var(--space-3);
    height: 28px;
    margin: 0;
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    font-family: var(--font-family);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    color: var(--fg-muted);
    background: var(--bg-muted);
    border: none;
    /* border-radius: var(--radius-sm); */
    border-radius: 0;
    box-shadow: none;
    cursor: pointer;
    transition: background-color var(--transition-fast),
                color var(--transition-fast),
                box-shadow var(--transition-fast);
}
.ui-refresh .btn-group[data-toggle="buttons"] > .btn:first-child {
    border-top-left-radius: var(--radius-sm);
    border-bottom-left-radius: var(--radius-sm);
}
.ui-refresh .btn-group[data-toggle="buttons"] > .btn:last-child {
    border-top-right-radius: var(--radius-sm);
    border-bottom-right-radius: var(--radius-sm);
}
.ui-refresh .btn-group[data-toggle="buttons"] > .btn:not(:last-child) {
  border-right-width: 1px;
  border-right-style: solid;
}
.ui-refresh .btn-group[data-toggle="buttons"] > .btn:hover {
    color: var(--fg-default);
    background: transparent;
}
.ui-refresh .btn-group[data-toggle="buttons"] > .btn.active,
.ui-refresh .btn-group[data-toggle="buttons"] > .btn.active:hover,
.ui-refresh .btn-group[data-toggle="buttons"] > .btn.active:focus {
    color: var(--fg-default);
    font-weight: var(--font-weight-semibold);
    box-shadow: var(--shadow-sm);
}
.ui-refresh .field-question-s_date_format .control-label {
    margin-right: var(--space-3);
}
/* Hide the underlying radio inputs — Bootstrap's button toggle reads them
 * but they shouldn't render. */
.ui-refresh .btn-group[data-toggle="buttons"] > .btn input[type="radio"],
.ui-refresh .btn-group[data-toggle="buttons"] > .btn input[type="checkbox"] {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    border: 0;
    pointer-events: none;
}

/* ===== Image & File Pickers (widgets/ImagePicker, widgets/FilePicker) ===== */
.ui-refresh .image-picker-div,
.ui-refresh .file-picker-div {
    background: none;
    padding: 0;
    border: none;
    border-radius: 0 !important;
    max-height: none;
}
.ui-refresh .image-picker-div img {
    margin-bottom: 0;
}

.ui-refresh .ui-image-picker,
.ui-refresh .ui-file-picker {
    width: 100%;
}
.ui-refresh .ui-image-picker.is-empty .ui-image-picker-filled,
.ui-refresh .ui-image-picker.is-filled .ui-image-picker-empty,
.ui-refresh .ui-file-picker.is-empty .ui-file-picker-filled,
.ui-refresh .ui-file-picker.is-filled .ui-file-picker-empty {
    display: none;
}

/* Empty state — compact one-line row with icon, label, hint, and Choose button. */
.ui-refresh .ui-image-picker-empty,
.ui-refresh .ui-file-picker-empty {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3);
    background: var(--bg-surface);
    border: 1px solid var(--input-border);
    border-radius: var(--radius-md);
}
.ui-refresh .ui-image-picker-empty-meta,
.ui-refresh .ui-file-picker-empty-meta {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}
.ui-refresh .ui-image-picker-empty-icon,
.ui-refresh .ui-file-picker-empty-icon {
    width: 36px;
    height: 36px;
    border-radius: var(--radius-md);
    background: var(--accent-subtle);
    color: var(--accent-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--font-size-sm);
    flex-shrink: 0;
}
.ui-refresh .ui-image-picker-empty-text,
.ui-refresh .ui-file-picker-empty-text {
    display: flex;
    flex-direction: column;
}
.ui-refresh .ui-image-picker-empty-title,
.ui-refresh .ui-file-picker-empty-title {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--fg-default);
}
.ui-refresh .ui-image-picker-empty-hint,
.ui-refresh .ui-file-picker-empty-hint {
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
    margin-top: 2px;
}
.ui-refresh .ui-image-picker-choose,
.ui-refresh .ui-file-picker-choose {
    flex-shrink: 0;
}

/* Filled state — fixed-height frame shared by both pickers. */
.ui-refresh .ui-image-picker-filled,
.ui-refresh .ui-file-picker-filled {
    position: relative;
    width: 100%;
    height: 160px;
    border: 1px solid var(--input-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
}
/* Image picker: the chosen image fits via object-fit: contain. */
.ui-refresh .ui-image-picker-filled img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    display: block;
}
/* File picker: the file-type icon (or video thumbnail) and filename are
 * centered inside the frame. */
.ui-refresh .ui-file-picker-preview {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    max-width: 100%;
    padding: var(--space-4);
    text-align: center;
}
.ui-refresh .ui-file-picker-icon {
    color: var(--fg-muted);
}
.ui-refresh .ui-file-picker-thumb {
    max-width: 100%;
    max-height: 96px;
    object-fit: contain;
    display: block;
    border-radius: var(--radius-sm);
}
.ui-refresh .ui-file-picker-name {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--fg-muted);
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ui-refresh .ui-image-picker-actions {
    position: absolute;
    top: var(--space-2);
    right: var(--space-2);
    display: flex;
    gap: var(--space-1);
}
.ui-refresh .ui-image-picker-icon-btn {
    width: var(--space-8) !important;
    height: var(--space-8) !important;
    padding: 0 !important;
    display: flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
    padding: 0 !important;
}

/* =====================================================================
   PAPER DESIGN SYSTEM — Patterns from swoogo-design-system.html / v4
   ===================================================================== */

/* ===== Eyebrow label =====
   Small uppercase letter-spaced label used above page titles, in card heads,
   and as section dividers. Pair with a "·" separator span between two eyebrows. */
.ui-refresh .ds-eyebrow {
    font-family: var(--font-family);
    font-size: 11px;
    font-weight: 600;
    color: var(--muted);
    line-height: 1;
}
.ui-refresh .ds-eyebrow-line {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}
.ui-refresh .ds-eyebrow-line .sep {
    color: var(--muted-2);
}

/* ===== Metric row =====
   4-up KPI grid above the dashlet area. Numerals are large with tight
   tracking; labels are uppercase letter-spaced eyebrows. */
.ui-refresh .dash-metric-row {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--space-3);
    margin: 0 var(--space-3) var(--space-4);
}
.ui-refresh .dash-metric {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    padding: 18px 18px 16px;
    min-height: 120px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.ui-refresh .dash-metric:hover {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-tint);
}
.ui-refresh .dash-metric .label {
    font-size: 11px;
    font-weight: 600;
    color: var(--muted);
    line-height: 1;
}
.ui-refresh .dash-metric .value {
    font-size: 38px;
    font-weight: 500;
    letter-spacing: -0.02em;
    line-height: 1;
    margin-top: 10px;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}
.ui-refresh .dash-metric .sub {
    font-size: 12px;
    color: var(--muted);
    margin-top: var(--space-1);
}
.ui-refresh .dash-metric .sub strong {
    color: var(--ink-2);
    font-weight: 600;
}
@media (max-width: 992px) {
    .ui-refresh .dash-metric-row {
        grid-template-columns: repeat(2, 1fr);
    }
}

/* ===== Card-head pattern for dashlet headings =====
   Restyle the existing .dash-panel-heading title to the v4 small uppercase
   letter-spaced label, with a right-aligned .panel-heading-hint for context
   text (replaces the inline subtitle row in the cards where it makes sense). */
.ui-refresh .dash-panel .dash-panel-heading {
    padding: var(--space-3) var(--space-4) var(--space-2);
    border-bottom: 1px solid var(--line);
    background: transparent;
}
/* Dashlet titles render as proper-case content names rather than as
   eyebrow-style category labels. The earlier rule pinned them to 11px
   600 ALL-CAPS muted, which read as a structural category — but a
   dashlet title is a name the user authored ("Event Stats", or whatever
   they typed for a custom dashlet), not a section header. The general
   `.ui-refresh .dash-panel .panel-title` rule above (~line 3629) already
   sets 14px medium/fg-default; with this more-specific override removed
   it takes effect. line-height stays close to its previous 1.4 via the
   inherited body line-height; explicit re-pin only if the heading row
   layout drifts. */
.ui-refresh .dash-panel .dash-panel-heading .panel-heading-subtitle,
.ui-refresh .dashlet-skeleton .dash-panel-heading .panel-heading-subtitle {
    /* Subtitle stays below title — keep it readable but slightly muted, no caps */
    text-transform: none;
    letter-spacing: 0;
    font-weight: 400;
    color: var(--muted);
    font-size: 12px;
    margin-top: 4px;
}
/* Hint variant: right-aligned context text in the dashlet head (v4-style).
   Used in place of the subtitle when the dashlet head is single-line. */
.ui-refresh .dash-panel-heading .panel-heading-hint {
    flex: 0 0 auto;
    font-size: 12px;
    color: var(--muted);
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
    margin-left: var(--space-3);
}

/* ===== Buttons — paper design system variants =====
   Add new explicit classes; don't override Bootstrap .btn-primary etc. since
   those are used widely outside ui-refresh. The new variants follow v4:
   - .ds-btn-ink (primary CTA, dark filled)
   - .ds-btn-accent (accent CTA, purple filled)
   - .ds-btn-soft (purple-tint pill)
   - .ds-btn-ghost (transparent, hover paper-2) */
.ui-refresh .ds-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: 7px 12px;
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    background: var(--card);
    font-size: 13px;
    font-weight: 500;
    color: var(--ink);
    line-height: 1.4;
    cursor: pointer;
    transition: background var(--transition-fast), border-color var(--transition-fast),
                color var(--transition-fast), box-shadow var(--transition-fast);
}
.ui-refresh .ds-btn:hover {
    background: var(--paper-2);
    color: var(--ink);
    text-decoration: none;
}
.ui-refresh .ds-btn-ink {
    background: var(--ink);
    color: var(--paper);
    border-color: var(--ink);
}
.ui-refresh .ds-btn-ink:hover {
    background: #1a1d24;
    color: var(--paper);
    border-color: #1a1d24;
}
.ui-refresh .ds-btn-accent {
    background: var(--primary-ink);
    color: #fff;
    border-color: var(--primary-ink);
}
.ui-refresh .ds-btn-accent:hover {
    background: color-mix(in srgb, var(--primary-ink), black 18%);
    color: #fff;
    border-color: color-mix(in srgb, var(--primary-ink), black 18%);
}
.ui-refresh .ds-btn-soft {
    background: var(--primary-tint);
    color: var(--primary-ink);
    border-color: var(--primary-tint-2);
}
.ui-refresh .ds-btn-soft:hover {
    background: var(--primary-tint-2);
    color: var(--primary-ink);
}
.ui-refresh .ds-btn-ghost,
.ui-refresh .btn-ghost {
    background: transparent;
    border-color: transparent;
    color: var(--muted);
}
.ui-refresh .ds-btn-ghost:hover,
.ui-refresh .btn-ghost:hover {
    color: var(--ink);
    background: var(--paper-2);
}

/* ===== Page background tint =====
   The dashboard page area gets a paper background; #content-container is
   already set up by layout.css for the body, but the dashboard region itself
   benefits from a subtle inset hue when the surface mode shifts.
   .ui-refresh.dashboard #main-content already exists in components.css; the
   token shift to --paper handles the color automatically. */

/* ===== Banner — v4 draft mode style (extends existing dashboard-status-banner) =====
   The current .dashboard-status-banner uses a pill icon; the v4 version uses
   a 56x56 circular glyph. Add a .dashboard-status-banner--paper variant that
   swaps in the larger circular glyph for the new design. */
.ui-refresh .dashboard-status-banner--paper {
    grid-template-columns: 56px 1fr auto;
    display: grid;
    align-items: center;
    gap: var(--space-5);
    padding: 20px 22px;
    background: var(--bg-surface);
    border: 1px solid var(--panel-border);
    border-radius: var(--panel-radius);
}
.ui-refresh .dashboard-status-banner--paper .dashboard-status-banner-icon {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    border: 1px solid var(--line-2);
    background: var(--paper-2);
    color: var(--ink-2);
    font-size: 22px;
}
.ui-refresh .dashboard-status-banner--paper .dashboard-status-banner-heading {
    font-size: 14px;
    font-weight: 600;
    color: var(--ink);
    display: flex;
    align-items: center;
    gap: var(--space-1);
}
.ui-refresh .dashboard-status-banner--paper .dashboard-status-banner-copy {
    color: var(--muted);
    max-width: 68ch;
    margin: 4px 0 0;
}
.ui-refresh .banner-draft-badge {
    display: inline-flex;
    align-items: center;
    font-size: 10px;
    font-weight: var(--font-weight-medium);
    line-height: 1;
    padding: 3px var(--space-2) 2px;
    border-radius: var(--radius-full);
    border: 1px solid var(--accent-muted);
    background: var(--accent-subtle);
    color: var(--accent-hover);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    vertical-align: middle;
    margin: 0 var(--space-1);
}

/* ===== Draft-mode strip =====
   Final full-width grid row of #organizer-header (see OrganizerHeader), so
   organizer-header.js's height measurement includes it and everything offset
   by --organizer-header-height follows automatically. */
/* The header's min-height and gap were written for a single nav row: pin the
   nav row to its full height and drop the row gap so the strip doesn't
   squeeze the nav or open a seam below it. */
body.ui-refresh #organizer-header:has(.event-draft-strip) {
    grid-template-rows: minmax(60px, auto) auto;
    row-gap: 0;
}
@media (max-width: 992px) {
    body.ui-refresh #organizer-header:has(.event-draft-strip) {
        grid-template-rows: minmax(56px, auto) auto auto;
    }
}
.ui-refresh .event-draft-strip {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    height: 40px;
    padding: 0 15px;
    background: var(--accent-subtle);
    border-top: 1px solid var(--accent-muted);
    font-size: 12.5px;
    color: var(--ink);
}
.ui-refresh .event-draft-strip > .fa-pencil {
    flex: 0 0 auto;
    color: var(--accent-hover);
    font-size: 12px;
}
.ui-refresh .event-draft-strip-copy {
    color: var(--muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ui-refresh .event-draft-strip-copy strong {
    color: var(--ink);
    font-weight: var(--font-weight-semibold);
}
.ui-refresh .event-draft-strip-action {
    flex: 0 0 auto;
    margin-left: var(--space-2);
    border: none;
    background: none;
    padding: 0;
    color: var(--accent-primary);
    font-size: 12.5px;
    font-weight: var(--font-weight-medium);
    white-space: nowrap;
    cursor: pointer;
    transition: color var(--transition-fast) ease-out;
}
.ui-refresh .event-draft-strip-action:hover {
    color: var(--accent-hover);
    text-decoration: underline;
}
/* Phones: keep the label and the action, drop the icon and summary sentence. */
@media (max-width: 767px) {
    .ui-refresh .event-draft-strip > .fa-pencil,
    .ui-refresh .event-draft-strip-summary { display: none; }
    .ui-refresh .event-draft-strip { justify-content: space-between; }
    .ui-refresh .event-draft-strip-action { margin-left: 0; }
}

/* ===== Event Settings doc layout =====
   Mirrors v4's production setup layout: a left-anchored 720px content rail
   with a flexible spacer column that ends in a compact right-aligned TOC.
   Active TOC link is driven by
   IntersectionObserver scroll-spy in web/js/event-settings-toc.js.

   The layout constants are declared on body.ui-refresh (below) so they're
   also visible to the doc-aligned chrome, which is a sibling of
   .event-settings-doc inside the form (see modules/loggedin/views/event/update.php).
   Chrome width = event icon-nav rail + secondary-nav (249px). Event pages
   get two body classes (has-event-icon-nav, nav-collapsed) that switch the
   variable; account-hub pages use the secondary-nav-only default.

   `--doc-max-width: 1240px` matches the `.list-page` max-width used by
   Registrant Types and the Registration Form so all three pages share an
   outer content boundary; the inner TOC sidebar still shrinks the form
   column accordingly. */
body.ui-refresh {
    --doc-max-width: 1240px;
    /* Minimum horizontal gutter kept on either side of the centered content
       column on event-setup pages — content never touches the viewport edge. */
    --doc-gutter: 40px;
    --doc-toc-width: 180px;
    --doc-toc-gap: 106px;
    /* Default: no event icon-nav (account-hub pages). Secondary-nav only. */
    --doc-chrome-width: 249px;
}
/* Event pages: icon-nav rail + secondary-nav. Mirrors the margin-left
   values in the "Secondary-nav + content offsets" @media block. */
body.ui-refresh.has-event-icon-nav:not(.nav-collapsed) {
    --doc-chrome-width: 439px; /* 190px rail + 249px secondary */
}
body.ui-refresh.has-event-icon-nav.nav-collapsed {
    --doc-chrome-width: 305px; /* 56px rail + 249px secondary */
}
.ui-refresh .event-settings-doc {
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--doc-toc-width);
    gap: var(--doc-toc-gap);
    row-gap: var(--space-5);
    width: min(var(--doc-max-width), 100% - (var(--doc-gutter) * 2));
    max-width: var(--doc-max-width);
    margin: 0 auto;
    padding-top: 6px;
    align-items: start;
}
.ui-refresh #event-wizard-container .event-settings-doc {
    width: 100%;
}
.ui-refresh .event-settings-content [id^="event-categories-"] .panel,
.ui-refresh #content-container .event-settings-content [id^="event-categories-"] .panel {
    background: none;
}
.ui-refresh .event-settings-content [id^="event-categories-"] .panel-heading,
.ui-refresh #content-container .event-settings-content [id^="event-categories-"] .panel-heading {
    color: var(--fg-default);
}
.ui-refresh .event-settings-content {
    min-width: 0;
}
/* Account-hub doc pages (e.g. custom-domain/update) reuse the doc + TOC layout
   with `.account-section` groups; offset their anchor targets below the sticky
   subpage-head so a TOC click doesn't tuck the heading under it. */
.ui-refresh .event-settings-content .account-section {
    scroll-margin-top: 90px;
}
/* ===== Shared TOC scroll-spy rail =====
   Both Event Settings (.event-settings-toc) and the Registration Form
   builder (.rf-toc) use the same TOC pattern: a sticky right-rail with
   muted links, an accent left-border + muted-bg active state, and a
   right-rounded chip when selected. The JS scroll-spy behavior is also
   shared via `web/js/ui-refresh/toc-scroll-spy.js`.

   Page-specific overrides (container top padding, mobile breakpoint /
   collapse behavior) stay on each page's own selectors — those are tied
   to the parent layout's grid, not the TOC itself. */
.ui-refresh .event-settings-toc,
.ui-refresh .rf-toc {
    position: sticky;
    top: 90px;
    align-self: start;
    width: auto;
    padding-top: var(--space-3);
    max-height: calc(100vh - 100px);
    overflow: auto;
}
.ui-refresh .event-settings-toc a,
.ui-refresh .rf-toc a {
    display: block;
    position: relative;
    padding: 6px 6px 6px 8px;
    font-size: 12px;
    line-height: 1.4;
    /* `--fg-default` so the TOC reads at the same strength as body
       text. Active state stays distinguishable via the accent
       left-stripe + bg-muted + semibold weight. */
    color: var(--fg-default);
    border-left: 2px solid transparent;
    border-radius: 0 var(--radius-md) var(--radius-md) 0;
    transition: background var(--transition-fast),
                color var(--transition-fast),
                border-color var(--transition-fast);
    text-decoration: none;
}

/* Error indicator: with auto-scroll-to-error off, this is the only pointer to
   which section a validation error landed in. Set via
   window.EventSettingsToc.markErrorSections() (event-settings-toc.js). */
.ui-refresh .event-settings-toc a.has-error {
    padding-right: 24px;
}
.ui-refresh .event-settings-toc a.has-error::after {
    content: "\f06a"; /* fa-circle-exclamation */
    font-family: "Font Awesome 6 Pro";
    font-weight: 400;
    font-size: 12px;
    position: absolute;
    top: 50%;
    right: 8px;
    transform: translateY(-50%);
    color: var(--color-error);
}

.ui-refresh .field-picker-container {
    background: var(--bg-surface);
    --control-bg-color: var(--bg-page);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
}
/* Negative bottom margin pulls the table up by 1px so the header's
 * border-bottom and the first row's border-top occupy the same pixel row
 * instead of stacking. position: relative keeps the header in front so its
 * border paints over the table's. */
.ui-refresh .field-picker-container-header {
    color: var(--fg-muted);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-bold);
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--panel-border);
    margin-bottom: -1px;
    position: relative;
    z-index: 1;
}
.ui-refresh .field-picker-container .field-picker-selected-fields {
    width: 100%;
    margin: 0;
    border-collapse: collapse;
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    color: var(--fg-muted);
}
.ui-refresh .field-picker-container .field-picker-selected-fields tbody td,
.ui-refresh .ajax-drawer .field-picker-container .field-picker-selected-fields tbody td {
    padding: var(--space-2) var(--space-3);
    border-top: 1px solid var(--panel-border);
    border-bottom: 1px solid var(--panel-border);
    vertical-align: middle;
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    color: var(--fg-muted);
}
.ui-refresh .field-picker-container .field-picker-selected-fields tr:last-child td {
    border-bottom: 0;
}
.ui-refresh .field-picker-container .field-picker-selected-fields > tbody > tr:hover,
.ui-refresh .field-picker-container .field-picker-selected-fields > tbody > tr:hover > td {
    background-color: transparent;
}
.ui-refresh .field-picker-container .field-picker-selected-fields td.handle {
    width: var(--space-5);
    text-align: center;
    color: var(--fg-subtle);
    cursor: grab;
}
.ui-refresh .field-picker-container .field-picker-selected-fields td.text-right {
    width: 36px;
    padding-left: 0;
    padding-right: var(--space-2);
    text-align: right;
}
/* Suppress the "No fields currently selected" empty-state row.
 * field-picker.js toggles its visibility via inline display, so !important
 * is needed for the rule to win against runtime style changes. */
.ui-refresh .field-picker-container .field-picker-selected-fields tr.empty_message {
    display: none !important;
}
/* Flat icon-button delete — strips the legacy .btn.btn-default chrome. Shared
   box model with .delete-multi-fields-btn (components.css ~16482); each keeps
   its own border/transition/hover since those genuinely differ. */
.ui-refresh .field-picker-container .delete-fields-btn,
.ui-refresh .field-picker-container .delete-multi-fields-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 0;
    background: none;
    box-shadow: none;
    border-radius: var(--radius-md);
    color: var(--fg-subtle);
    cursor: pointer;
}
.ui-refresh .field-picker-container .delete-fields-btn {
    border: none;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .field-picker-container .delete-fields-btn:hover {
    background: var(--bg-muted);
    color: var(--fg-default);
}
/* Sortable placeholder — the <td> cells populated in the sortable start
 * callback give it the row's height; no background needed, the gap itself
 * shows the drop target. Visibility override is harmless if jQuery UI's
 * default visibility:hidden ever ships through. */
.ui-refresh .field-picker-container .field-picker-selected-fields .ui-sortable-placeholder {
    visibility: visible !important;
}
/* The widget's `.text-center` button wrapper sits inside the container as a
 * footer — repurpose it as a right-aligned action row. */
.ui-refresh .field-picker-container .text-center {
    text-align: right;
    padding: var(--space-2);
    border-top: 1px solid var(--panel-border);
}

.ui-refresh .sortable-collection-card {
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: var(--bg-surface);
    --control-bg-color: var(--bg-page);
}
.ui-refresh .sortable-collection-card .sortable-collection-table {
    width: 100%;
    margin: 0;
    border-collapse: collapse;
    table-layout: fixed;
}
.ui-refresh .sortable-collection-card .sortable-collection-table > thead > tr > th {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    color: var(--fg-subtle);
    text-align: left;
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--panel-border);
}
.ui-refresh .sortable-collection-card .sortable-collection-table > tbody > tr > td {
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--panel-border);
    vertical-align: middle;
}
.ui-refresh .sortable-collection-card .sortable-collection-table > tbody > tr:last-child > td {
    border-bottom: 0;
}
.ui-refresh .sortable-collection-card .form-control {
    background-color: var(--bg-page) !important;
}
.ui-refresh .sortable-collection-card .sortable-collection-table .handle,
.ui-refresh .sortable-collection-card .sortable-collection-table .sortable-collection-handle-header {
    padding-left: var(--space-2);
    padding-right: 0;
    width: var(--space-8);
    text-align: center;
    color: var(--fg-subtle);
    cursor: grab;
}
.ui-refresh .sortable-collection-card .sortable-collection-table .handle:active {
    cursor: grabbing;
}
.ui-refresh .sortable-collection-card .sortable-collection-table > thead > tr > th:last-child,
.ui-refresh .sortable-collection-card .sortable-collection-table > tbody > tr > td:last-child {
    width: 44px;
    padding-left: 0;
    padding-right: 0;
    text-align: center;
}
.ui-refresh .sortable-collection-card .delete-row {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    background: none;
    border: none;
    box-shadow: none;
    border-radius: var(--radius-md);
    color: var(--fg-subtle);
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .sortable-collection-card .delete-row:hover {
    background: var(--bg-muted);
    color: var(--fg-default);
}
.ui-refresh .sortable-collection-card .row.text-center {
    margin: 0;
    display: flex;
    justify-content: flex-end;
    padding: var(--space-2);
    border-top: 1px solid var(--panel-border);
}
.ui-refresh .sortable-collection-card .add-new-row {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}
.ui-refresh .ajax-drawer .sortable-collection-card .add-new-row {
    margin-top: 0;
}
.ui-refresh .sortable-collection-card .ui-sortable-placeholder {
    visibility: visible !important;
}

/* Small selection-card multi-select group used by the Visibility tab's
 * Registrants and Devices rows. Cards are equal width via flex:1; text on the
 * left, selection state circle/check on the right. Underlying checkbox stays
 * in the DOM so the form submits the value; the label is the click target. */
/* Page editor layout picker — lay the cards out in a wrapping flex row.
   The legacy .layout cards float; inside this flex container the float is
   ignored and the gap handles spacing. */
.ui-refresh .sidebar-layout-grid {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
}
.ui-refresh .sidebar-layout-grid .layout {
    float: none;
    margin-right: 0;
}

.ui-refresh .sidebar-chip-group {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin: 0;
}
.ui-refresh label.sidebar-chip {
    flex: 0 0 106px;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-2) !important;
    border: 1px solid var(--input-border);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
    font-size: var(--font-size-xs);
    font-weight: 500;
    color: var(--fg-subtle);
    cursor: pointer;
    user-select: none;
    margin: 0;
    transition: background var(--transition-fast), border-color var(--transition-fast);
}
.ui-refresh label.sidebar-chip:hover {
    border-color: var(--accent-primary);
}
.ui-refresh label.sidebar-chip.active {
    background: var(--accent-subtle);
    border-color: var(--accent-muted);
    color: var(--primary-ink);
}
.ui-refresh label.sidebar-chip > input[type=checkbox] {
    display: none;
}
.ui-refresh label.sidebar-chip .sidebar-chip-label {
    flex: 1;
    text-align: left;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.ui-refresh label.sidebar-chip .sidebar-chip-icon-off,
.ui-refresh label.sidebar-chip .sidebar-chip-icon-on {
    font-size: var(--font-size-sm);
    flex-shrink: 0;
}
.ui-refresh label.sidebar-chip .sidebar-chip-icon-on {
    display: none;
    color: var(--accent-primary);
}
.ui-refresh label.sidebar-chip.active .sidebar-chip-icon-off {
    display: none;
}
.ui-refresh label.sidebar-chip.active .sidebar-chip-icon-on {
    display: inline-block;
}

/* Inset card for nested controls inside a sidebar panel (e.g. the date/time
 * pickers revealed when the "Limit to dates and times" toggle is on). */
.ui-refresh .sidebar-inset-card {
    padding: var(--space-3);
    border: 1px solid var(--input-border);
    border-radius: var(--radius-md);
    /* 3-column grid: label sized to longest, then two equal-width input columns.
     * Each form-group row uses `display: contents` so its label + two inner
     * field-groups land as direct grid items in the same 3 tracks. */
    display: grid;
    grid-template-columns: max-content 1fr 1fr;
    column-gap: var(--space-2);
    row-gap: var(--space-2);
    align-items: center;
}
.ui-refresh .sidebar-inset-card > .form-group {
    display: contents;
}
.ui-refresh .sidebar-inset-card > .form-group > .form-group {
    margin-bottom: 0;
}
.ui-refresh .sidebar-inset-card .control-label {
    margin-bottom: 0;
}
/* Inline "scope" pill that annotates the card with the event timezone. */
.ui-refresh .sidebar-inset-card-meta {
    grid-column: 1 / -1;
    justify-self: start;
    font-weight: 500;
}

.ui-refresh .event-settings-toc a:hover,
.ui-refresh .rf-toc a:hover {
    color: var(--fg-default);
    text-decoration: none;
}
.ui-refresh .event-settings-toc a.active,
.ui-refresh .rf-toc a.active {
    color: var(--fg-default);
    font-weight: var(--font-weight-semibold);
    border-left-color: var(--accent-primary);
    background: var(--bg-muted);
}

/* Each section flows directly on the page bg — no card surface. The earlier
   bg-surface + border + radius created a white panel-on-cream look that
   doesn't match v4 where forms float on the body. The section-heading's
   border-bottom is the only visible separator. scroll-margin-top still
   offsets the sticky navbar so the heading isn't hidden when jumped to. */
.ui-refresh .event-settings-content section {
    background: transparent;
    border: 0;
    border-radius: 0;
    padding: 0;
    margin-bottom: var(--space-6);
    scroll-margin-top: 80px;
}
.ui-refresh .event-settings-content section:last-child {
    margin-bottom: 0;
    padding-bottom: var(--space-12);
}
/* Section header: a proper heading, not an eyebrow. The earlier 11px
   uppercase eyebrow read at the same visual weight as the sub-section
   labels (.form-section-label) and field control-labels below it,
   flattening the hierarchy — you couldn't tell at a glance which
   "WHERE" was the section vs which "LOCATION" was the sub-section.
   Bumped to 22px / weight 600 / natural casing so the section name
   declares itself as the top of its block while sub-section labels
   keep their quiet 11px eyebrow role. Trailing rule still draws a
   divider edge across the section. */
.ui-refresh .event-settings-section-head {
    margin: 0 0 var(--space-5);
}
.ui-refresh .event-settings-section-head:first-child {
    margin-top: var(--space-5);
}
.ui-refresh .event-settings-section-heading,
.ui-refresh .comm-form .panel.panel-default > .panel-heading:not(.ajax-drawer *) {
    font-family: var(--font-family);
    font-size: 16px;
    line-height: 1.2;
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
    margin: 0;
    padding: 0;
    border: 0;
}

/* Sub-section eyebrow inside a section (Website & URL, Content & Social, etc.).
   Same eyebrow + rule pattern, slightly more breathing room above so the
   sub-section feels distinct from the fields above it. */
.ui-refresh .event-settings-content .form-section-label {
    font-family: var(--font-family);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-muted);
    margin: var(--space-6) 0 var(--space-4);
}

.ui-refresh .event-settings-content .form-group {
    margin-bottom: 18px;
}

/* Required-asterisk: keep the warning hue but match v4's lighter weight. */
.ui-refresh .event-settings-content .control-label .required,
.ui-refresh .event-settings-content .form-label .required {
    color: var(--accent-primary);
    font-weight: var(--font-weight-normal);
}
/* Yii ActiveForm tags a .form-group with .has-success once the field validates
   on blur. Legacy active-form.css forces .has-success .control-label to
   gray-800 with !important, which makes our muted eyebrow label darken on
   first interaction. Pin the label color so it stays stable across states. */
.ui-refresh .event-settings-content .has-success .control-label,
.ui-refresh .event-settings-content .has-error .control-label {
    color: var(--fg-muted) !important;
}
/* Same pin for account forms (Basic Details, data-list, js-snippet, …), which
   wrap their fields in .account-form rather than .event-settings-content — so a
   valid field's label doesn't darken on focus/blur. */
.ui-refresh .account-form .has-success .control-label,
.ui-refresh .account-form .has-error .control-label {
    color: var(--fg-muted) !important;
}
/* font/index "Install Font" dropdown glyphs read a touch large at 1em — nudge down. */
.ui-refresh .dropdown-menu .fa-google,
.ui-refresh .dropdown-menu .fa-font {
    font-size: 0.85em;
}
/* font/view "Font Details" key/value list — a clean token-styled definition
   list instead of the legacy striped table. */
.ui-refresh .font-detail-list {
    margin: 0;
}
.ui-refresh .font-detail-list > div {
    display: flex;
    gap: var(--space-4);
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--border-subtle);
    font-size: var(--font-size-sm);
}
.ui-refresh .font-detail-list > div:last-child {
    border-bottom: 0;
}
.ui-refresh .font-detail-list dt {
    flex: 0 0 140px;
    color: var(--fg-muted);
    font-weight: var(--font-weight-medium);
}
.ui-refresh .font-detail-list dd {
    margin: 0;
    min-width: 0;
    color: var(--fg-default);
    word-break: break-word;
}
/* Google Font install modal (iframe): the modal card is --bg-surface, but the
   iframe body (--bg-page) and the form (#modal-pjax-form, painted --paper by the
   generic iframe-modal rule below) are darker. Paint both the surface tone so the
   modal reads as one surface; flip --control-bg-color so the select contrasts. */
.ui-refresh.font-install-modal,
.ui-refresh.font-install-modal #modal-pjax-form {
    background: var(--bg-surface) !important;
}
.ui-refresh.font-install-modal {
    --control-bg-color: var(--bg-page);
}

/* Inputs: tighten the radius/padding to match v4 .input (10px 12px), and
   pull the bg to --bg-surface which is the warmer card surface. The focus
   accent ring is already wired via .form-control:focus. */
.ui-refresh .event-settings-content .form-control {
    background-color: var(--bg-surface);
    border-radius: var(--radius-lg);
    padding: 10px 12px;
    height: auto;
    min-height: var(--input-height);
}

/* Bootstrap input-group prefix (e.g. "https://local.demo.swoogo.com/" on
   the website URL field). Match v4 .ig-pre: paper-2 bg, hairline divider,
   13px muted text. The whole group gets a unified border so the prefix
   reads as part of the input, not a separate chip. Bootstrap defaults
   .input-group to display:table; override to flex so children sit inline
   regardless of their own display values. */
.ui-refresh .event-settings-content .input-group {
    display: flex;
    align-items: stretch;
    border: 1px solid var(--input-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: var(--bg-surface);
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
    min-height: var(--input-height);
}
/* The overflow:hidden above clips children to the group's rounded border, but
   it also clips any absolutely positioned dropdown a child opens — the
   bootstrap-multiselect menu on the waiting-room duration field lays out just
   below the group's box and gets erased entirely (no z-index can rescue it,
   clipping happens first). Opt those groups out; their segments carry correct
   radii of their own, so there is nothing left for the clip to do. */
.ui-refresh .event-settings-content .input-group:has(.multiselect-native-select) {
    overflow: visible;
}
.ui-refresh .event-settings-content .input-group:focus-within {
    border-color: var(--accent-primary);
    box-shadow: var(--focus-ring);
}
.ui-refresh .event-settings-content .input-group:focus-within:has(input) {
    box-shadow: none;
}
.ui-refresh .event-settings-content .input-group .input-group-addon {
    /* width:auto overrides Bootstrap's `.input-group-addon { width: 1% }`
       hack — that value is for the table-cell layout we replaced with flex,
       and would otherwise squeeze the addon to ~7px. */
    flex: 0 0 auto;
    width: auto;
    background: var(--bg-muted);
    border: 0;
    border-right: 1px solid var(--border-subtle);
    border-radius: 0;
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
    padding: 0 12px;
    display: flex;
    align-items: center;
    white-space: nowrap;
}
.ui-refresh .event-settings-content .input-group .input-group-btn {
    /* Same `width: 1%` table-cell hack as the addon above — left alone it
       collapses the button segment (e.g. the "minutes before the start time"
       multiselect on the waiting-room field) to ~5px, and the group's
       overflow:hidden clips it to a sliver. */
    flex: 0 0 auto;
    width: auto;
}
.ui-refresh .event-settings-content .input-group .form-control {
    flex: 1 1 auto;
    width: auto;
    min-width: 0;
    border: 0 !important;
    border-radius: 0;
    background: transparent;
    box-shadow: none !important;
}
.ui-refresh .event-settings-content .input-group .form-control:focus {
    box-shadow: none !important;
}

/* Field-helper text under the input ("This is the URL where..."). Smaller
   muted body text, matches v4 .fld-hint. */
.ui-refresh .event-settings-content .form-group .help-block,
.ui-refresh .event-settings-content .form-group .form-helper {
    font-size: 12px;
    color: var(--fg-muted);
    line-height: 1.5;
    margin-top: var(--space-2);
}

/* Mobile: collapse to a single column; TOC sits above content as a
   horizontal pill nav. */
@media (max-width: 1100px) {
    .ui-refresh .event-settings-doc {
        grid-template-columns: 1fr;
        gap: var(--space-4);
    }
    .ui-refresh .event-settings-toc {
        position: static;
        max-height: none;
        padding-top: 0;
        border-bottom: 1px solid var(--border-default);
        padding-bottom: var(--space-3);
        margin-bottom: var(--space-3);
        display: flex;
        gap: var(--space-1);
        overflow-x: auto;
    }
    .ui-refresh .event-settings-toc .toc-group {
        display: none;
    }
    .ui-refresh .event-settings-toc .toc-children {
        display: contents;
    }
    .ui-refresh .event-settings-toc a {
        white-space: nowrap;
        border-left: 0;
        border-bottom: 2px solid transparent;
        border-radius: var(--radius-md) var(--radius-md) 0 0;
    }
    .ui-refresh .event-settings-toc a.active {
        border-left-color: transparent;
        border-bottom-color: var(--accent-primary);
    }
}
.ui-refresh #virtual_event_url_setting {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}
/* Expanded panels sit flush inside their row on every surface. Stated
   explicitly rather than left to inheritance: backend.css gives these an
   indent + left rule, and `#content-container` re-styles them again, so a card
   rendered on an event page would otherwise not match the same card in the
   drawer. Vertical spacing comes from the row above and the animated margin on
   the panel's first child. */
.ui-refresh .virtual-settings-group .panel-expanded-settings {
    border-left: none;
    margin: 0;
    padding: 0;
}
.ui-refresh .ajax-drawer #virtual_event_url_setting label > a {
    margin-left: calc(-1 * var(--space-2));
    order: -1;
}
.ui-refresh #virtual_event_url_setting .toggle-group {
    margin: var(--space-1) 0 0;
}
.ui-refresh #virtual-location-container .radio-option {
    background: transparent;
}
.ui-refresh #virtual-location-container .radio-card {
    border: 1px solid var(--border-default);
    box-shadow: none;
    transition: border-color var(--transition-fast),
                background var(--transition-fast),
                transform var(--transition-fast);
    position: relative;
    background: var(--bg-surface);
}
.ui-refresh #virtual-location-container .radio-card:hover {
    background: var(--bg-surface);
    border-color: var(--border-strong);
}
.ui-refresh #virtual-location-container .radio-card:hover .radio-name,
.ui-refresh #virtual-location-container .radio-card:hover .radio-description {
    background: transparent;
    color: inherit;
}
.ui-refresh #virtual-location-container .radio-card .radio-name {
    color: var(--fg-default);
    font-weight: var(--font-weight-semibold);
}
.ui-refresh #virtual-location-container .radio-card .radio-description {
    color: var(--fg-muted);
}
/* Active: thin accent ring, surface tint barely shifted, check pip
   appears in the top-right corner. */
.ui-refresh #virtual-location-container .radio-card.active {
    border: 1.5px solid var(--accent-primary) !important;
    background: var(--bg-surface) !important;
}
.ui-refresh #virtual-location-container .radio-card.active .radio-name {
    background: transparent !important;
    color: var(--fg-default) !important;
}
.ui-refresh #virtual-location-container .radio-card.active .radio-description {
    background: transparent !important;
    color: var(--fg-muted) !important;
}
.ui-refresh #virtual-location-container .radio-card.active::after {
    content: "";
    position: absolute;
    top: 10px;
    right: 10px;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--accent-primary)
        url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>")
        center / 11px no-repeat;
}

/* ===== Select2 Component ===== */

.ui-refresh .select2-container--krajee-bs3 .select2-selection,
.ui-refresh .select2-container--krajee-bs3 .select2-selection:focus {
    background-color: var(--bg-surface);
    border: 1px solid var(--input-border);
    box-shadow: none;
    color: var(--fg-default);
    font-size: var(--font-size-input);
}

.ui-refresh .select2-container--krajee-bs3 .select2-selection--single {
    display: flex;
    align-items: center;
    height: var(--input-height);
}

.ui-refresh .select2-container--krajee-bs3 .select2-selection--single .select2-selection__arrow {
    border-left: 1px solid var(--input-border);
    top: 50%;
    transform: translateY(-50%);
}
.ui-refresh .select2-container--krajee-bs3.select2-container--open .select2-selection,
.ui-refresh .select2-container--krajee-bs3 .select2-dropdown {
    border-color: var(--input-border);
    box-shadow: none;
    outline: none;
}
.ui-refresh .select2-container--krajee-bs3 .select2-search--dropdown {
    padding: var(--space-3) 0 var(--space-2) 0;
}
.ui-refresh .select2-container--krajee-bs3 .select2-search--dropdown .select2-search__field:focus {
    border-color: var(--accent-primary);
    box-shadow: var(--focus-ring);
    outline: none;
}
.ui-refresh .select2-container--krajee-bs3 .select2-selection {
    border-radius: var(--input-radius);
}
.ui-refresh .select2-container--krajee-bs3 .select2-selection--single .select2-selection__rendered,
.ui-refresh .select2-container--krajee-bs3 .select2-selection__clear {
    color: var(--fg-default);
}
.ui-refresh .select2-container--krajee-bs3 .select2-selection--single .select2-selection__clear {
    right: var(--space-7);
    top: 50%;
    transform: translateY(-50%);
    cursor: pointer;
}
.ui-refresh .select2-container--krajee-bs3 .select2-selection--single .select2-selection__placeholder {
    color: var(--fg-subtle);
}
.ui-refresh .select2-dropdown {
    background-color: var(--bg-surface) !important;
    border-radius: var(--input-radius);
    font-size: var(--font-size-input);
    padding: 0 var(--space-2) var(--space-2);
    --control-bg-color: var(--bg-page);
}
.ui-refresh .select2-container--open .select2-dropdown--below {
    border-top: 1px solid var(--input-border);
}
.ui-refresh .select2-container--krajee-bs3 li.select2-results__option {
    background-color: transparent;
    color: var(--fg-default);
}

.ui-refresh .select2-container--krajee-bs3 li.select2-results__option.select2-results__message {
    color: var(--fg-muted);
}

.ui-refresh .select2-container--krajee-bs3 .select2-search--dropdown .select2-search__field {
    background: var(--fg-muted) url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAYAAABy6+R8AAAACXBIWXMAAC4jAAAuIwF4pT92AAAAv0lEQVR42mNgQAIdHR28QCwDwu3t7YwMhABQYTUQvwbi/1B8AYgD8GmYDVV4H4jLgHgKkuZwbBo8oJJ7gJgfSdwMiN8D8VsgFkHXNAeI/wKxBhYDK6EGeqBLrALiH9g8juSKAHSJHqiEDxZNC6GusEaX0IJqAoWcO1SMB4groOIPcYVePFJo3QDiF0h8kE1duDRaAPFyIL4CxJeAuB6Iu5E09zIQC4CK25A0TgBiLmI11gLxL6hGZ1Js3AX1nyYA7Vu2Ihp52ewAAAAASUVORK5CYII=) right var(--space-2) center no-repeat;
    background-color: var(--control-bg-color) !important;
    border: 1px solid var(--input-border);
    border-radius: var(--input-radius);
    color: var(--fg-default);
    height: var(--input-height);
}

/* Kartik Select2's "toggle all" control for multi-selects — a plain span
   (`.s2-togall-button`, id `s2-togall-<inputId>`) that the widget's JS
   prepends as the first child of `.select2-dropdown` on open. The vendor
   CSS (select2-addl.css) hardcodes Bootstrap's link blue (#337ab7), which
   reads as an unstyled link against a dark dropdown. Re-point to tokens;
   leave the vendor's .s2-togall-select/.s2-togall-unselect show/hide rules
   alone since they still gate which of the two child labels is visible. */
.ui-refresh .s2-togall-button {
    display: block;
    padding: var(--space-2) var(--space-3);
    margin: 0 0 var(--space-1);
    color: var(--accent-primary);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    border-bottom: 1px solid var(--border-subtle);
    cursor: pointer;
}
.ui-refresh .s2-togall-button:hover,
.ui-refresh .s2-togall-button:focus {
    color: var(--accent-hover);
    background: var(--bg-muted);
    text-decoration: none;
}

/* ===== Identity block =====
   Notion-style inline-editable headline at the top of the consolidated
   event Settings page. The `<h1 class="edit-title editable">` is
   contenteditable; event-identity.js mirrors its text into the underlying
   `<input name="Event[name]">` (which lives hidden in
   .event-identity-name-field) and dispatches `input`/`change` so Yii2
   ActiveForm validation and the TranslationInput plumbing still fire.
   Clicking the "Translate" link toggles `.translating` on the parent,
   which unhides the form-group so the user can open the existing
   per-language panel inside the TranslationInput widget. */
.ui-refresh .event-identity {
    margin: 0 0 var(--space-2);
}
.ui-refresh .event-identity .edit-title {
    display: block;
    position: relative;
    font-family: var(--font-family);
    font-size: 40px;
    font-weight: 700;
    letter-spacing: -0.025em;
    line-height: 1.05;
    color: var(--fg-default);
    margin: 0 -10px var(--space-1);
    padding: 6px 32px 6px 10px;
    border: 1px solid transparent;
    border-radius: 10px;
    outline: none;
    cursor: text;
    transition: background var(--transition-fast),
                border-color var(--transition-fast),
                box-shadow var(--transition-fast);
}
.ui-refresh .event-identity .edit-title:hover {
    background: var(--bg-muted);
}
.ui-refresh .event-identity .edit-title:focus {
    background: var(--bg-surface);
    border-color: var(--accent-primary);
    box-shadow: 0 0 0 3px var(--accent-subtle);
}
.ui-refresh .event-identity .edit-title:empty::before {
    content: attr(data-placeholder);
    color: var(--fg-subtle);
    font-weight: 600;
}
.ui-refresh .event-identity .edit-title::after {
    content: "";
    position: absolute;
    right: 10px;
    top: 50%;
    transform: translateY(-50%);
    width: 16px;
    height: 16px;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236f6c63' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'><path d='M4 20l4-1 10-10-3-3L5 16l-1 4z'/><path d='M14 6l3 3'/></svg>");
    background-size: contain;
    background-repeat: no-repeat;
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--transition-fast);
}
.ui-refresh .event-identity .edit-title:hover::after {
    opacity: 0.55;
}
.ui-refresh .event-identity .edit-title:focus::after {
    opacity: 0;
}

/* The Event Name form-group is rendered solely so each language has a
   hidden input the form can post. The H1 above (with its language tabs)
   is the visible UI — keep the form-group permanently hidden. */
.ui-refresh .event-identity .event-identity-name-field {
    display: none;
}

/* Validation error surface for the H1. Because the underlying form-group
   is permanently hidden, Yii ActiveForm's built-in `.help-block` would
   never reach the user; event-identity.js mirrors any error here, and the
   server also renders an initial value if the form posted with errors.
   Visual treatment matches the legacy `.has-error .help-block` (red text)
   and sits flush below the H1 / language tabs. */
.ui-refresh .event-identity-error {
    color: var(--color-error);
    font-size: 13px;
    line-height: 1.5;
    margin: 0 0 var(--space-2);
}
.ui-refresh .event-identity-error:empty {
    display: none;
}
/* Mirror the H1's focus styling (border + soft ring) but in the error
   color so the validation state is as visible as the focus state.
   `.edit-title` has a transparent 1px border by default (see the base
   rule above), which this rule colors red; the ring uses the light
   error tint to match the accent-tint focus pattern. */
.ui-refresh .event-identity.has-error .edit-title {
    border-color: var(--color-error);
    box-shadow: 0 0 0 3px var(--color-error-bg);
}

/* ===== Translation widget redesign (event Settings page) =====
   The legacy `.translations-input` widget (widgets/views/TranslationInput.php)
   stacks every language vertically as its own bordered `.input-group`
   with a heavy "input-group-addon" gray label on the left and a "Translate"
   pill on the right. backend.css enforces this with a stack of `!important`
   rules (rounded corners, fixed widths, gray bg, blue text).

   In ui-refresh we replace that with a Notion/Sanity-style segmented tab
   strip above each translatable field — one full-width input/editor below
   that swaps content based on which language tab is active. The tabs are
   built at runtime by `web/js/event-translation-tabs.js`, which adds a
   `.has-tabs` marker class to each upgraded widget.

   FOUC strategy: most of the visual treatment below is unconditional (it
   engages from page load on every `.translations-input` inside
   `.event-settings-content`) so the user never sees the legacy gray
   "English | Translate" pills or the multi-row stack flash through. The
   only rules gated on `.has-tabs` are the ones that genuinely depend on
   JS having upgraded the widget — the row-swap (`:not(.is-active)`) and
   the override of the legacy `.enter-translations { display: none }`
   inline style. Until JS runs, `.enter-translations` stays hidden and
   only the primary row is visible — which is what we'd want anyway. */

/* Tabs strip — segmented pill control. */
.ui-refresh .translations-tabs {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 2px;
    background: var(--bg-muted);
    padding: 4px;
    border-radius: var(--radius-md);
    margin: 0 0 var(--space-2);
}
.ui-refresh .translations-tab {
    appearance: none;
    border: 0;
    background: transparent;
    border-radius: var(--radius-sm);
    padding: 6px 12px;
    font-family: var(--font-family);
    font-size: 13px;
    font-weight: 500;
    color: var(--fg-muted);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    transition: color var(--transition-fast),
                background var(--transition-fast),
                box-shadow var(--transition-fast);
}
.ui-refresh .translations-tab:hover {
    color: var(--fg-default);
}
.ui-refresh .translations-tab:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}
.ui-refresh .translations-tab.is-active {
    background: var(--bg-surface);
    color: var(--accent-primary);
    box-shadow: var(--shadow-sm);
}
/* Filled/empty status dot — green when the language has a value, gray
   when empty. Updates live on input/change via the JS. */
.ui-refresh .translations-tab .dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--green-40); /* language has a value */
    flex-shrink: 0;
    transition: background var(--transition-fast);
}
.ui-refresh .translations-tab.is-empty .dot {
    background: var(--line-2);
}

/* ----- Unconditional rules (engage from page load, no JS required) -----
   Hide the legacy in-row "English" pill and the right-side "Translate"
   pill on every translation widget inside the consolidated Settings page.
   The tab strip (built by JS) replaces both — they would only ever flash
   in briefly before being hidden, so kill them outright. */
.ui-refresh .event-settings-content .translations-input .input-group-addon.lang-name,
.ui-refresh .event-settings-content .translations-input .input-group-addon.translations.btn-default,
.ui-refresh [data-translation-collapse] .translations-input .input-group-addon.lang-name,
.ui-refresh [data-translation-collapse] .translations-input .input-group-addon.translations.btn-default {
    display: none !important;
}

/* Primary (English) row: render as the "active" flex layout from page
   load — input + any trailing addon (Google "G" icon) on one line, no
   card chrome. Pre-upgrade this is the only visible row; post-upgrade
   it's hidden via the `:not(.is-active)` rule below when another tab is
   selected. Without this rule the user briefly sees the legacy bordered
   input-group before JS paints the new chrome. */
.ui-refresh .event-settings-content .translations-input > .en.input-group,
.ui-refresh [data-translation-collapse] .translations-input > .en.input-group {
    display: flex;
    align-items: flex-start;
    column-gap: var(--space-2);
    background: transparent;
    border: 0;
    border-radius: 0;
    overflow: visible;
    min-height: 0;
    margin: 0;
}
.ui-refresh .event-settings-content .translations-input > .en.input-group:focus-within,
.ui-refresh [data-translation-collapse] .translations-input > .en.input-group:focus-within {
    border-color: transparent;
    box-shadow: none;
}

/* The actual input/editor: keep its own border & radius even when
   backend.css's `.noradius` class is applied by app.js's legacy
   click-to-expand handler. Applied to every row inside the widget so
   single-language widgets and additional-language rows both render
   correctly without depending on `.has-tabs`. */
.ui-refresh .event-settings-content .translations-input .input-group .form-control,
.ui-refresh [data-translation-collapse] .translations-input .input-group .form-control {
    flex: 1 1 auto;
    width: auto;
    border: 1px solid var(--border-default) !important;
    border-radius: var(--input-radius) !important;
    background: var(--bg-surface);
    box-shadow: none !important;
}
/* CKEditor replaces the `.form-control.html-editor` textarea with its own
   `.cke`/`.cke_chrome` chrome as a sibling — the flex rule above only ever
   reaches the (now-hidden) original textarea. Without an explicit width the
   editor renders at CKEditor's own default rather than filling the row,
   leaving it visibly narrower than the fields below it. The wizard's
   quick-create flow already carries this fix (event-wizard-header.css);
   apply the same one here for the full event edit / consolidated Settings
   page, where the description field goes through this same widget. */
.ui-refresh .event-settings-content .cke,
.ui-refresh .event-settings-content .html-editor,
.ui-refresh [data-translation-collapse] .cke,
.ui-refresh [data-translation-collapse] .html-editor {
    width: 100%;
    max-width: 100%;
}
.ui-refresh .event-settings-content .translations-input .input-group .form-control:focus {
    border-color: var(--accent-primary) !important;
    box-shadow: var(--focus-ring) !important;
}
.ui-refresh .event-settings-content .translations-input .input-group .form-control.noradius {
    border-radius: var(--input-radius) !important;
}
.ui-refresh .ajax-drawer .translations-input.lang-hidden .input-group .form-control {
    border-bottom-left-radius: var(--input-radius) !important;
    border-top-left-radius: var(--input-radius) !important;
}

/* Google Translate "G" icon — transparent accent glyph, no box. */
.ui-refresh .event-settings-content .translations-input .input-group .input-group-addon.google-translate,
.ui-refresh [data-translation-collapse] .translations-input .input-group .input-group-addon.google-translate {
    flex: 0 0 auto;
    width: auto;
    background: transparent !important;
    border: 0 !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding: 9px 0 0;
    cursor: pointer;
    align-self: flex-start;
}
.ui-refresh .event-settings-content .translations-input .input-group .input-group-addon.google-translate i,
.ui-refresh [data-translation-collapse] .translations-input .input-group .input-group-addon.google-translate i {
    color: var(--accent-primary);
    font-size: 18px;
    transition: opacity var(--transition-fast);
}
.ui-refresh .event-settings-content .translations-input .input-group .input-group-addon.google-translate:hover i,
.ui-refresh [data-translation-collapse] .translations-input .input-group .input-group-addon.google-translate:hover i {
    opacity: 0.75;
}

/* Defeat backend.css `.translation-langs .input-group { margin-top: 15px }`
   — with tabs the rows are mutually exclusive, so the legacy stacking
   margin would leave dead space above the active row. */
.ui-refresh .event-settings-content .translations-input .translation-langs .input-group,
.ui-refresh [data-translation-collapse] .translations-input .translation-langs .input-group {
    margin-top: 0;
}

/* ----- .has-tabs gated rules (engage after JS upgrades the widget) ----- */

/* Reveal the `.enter-translations` container so individual rows can paint.
   Pre-upgrade it stays hidden via the legacy widget's inline
   `style="display:none"`, which keeps additional-language rows out of
   sight (and out of CKEditor's measurement pass — `harmonizeCkEditorHeights`
   in event-translation-tabs.js handles the height match post-instanceReady). */
.ui-refresh .event-settings-content .translations-input.has-tabs .enter-translations {
    display: block !important;
}

/* Hide language rows that aren't the active tab. Only meaningful once JS
   has marked one row `.is-active`; pre-upgrade the parent
   `.enter-translations` is itself hidden so this is moot. */
.ui-refresh .event-settings-content .translations-input.has-tabs > .en.input-group:not(.is-active),
.ui-refresh .event-settings-content .translations-input.has-tabs .translation-langs > .input-group:not(.is-active) {
    display: none !important;
}

/* Additional-language row when active: same flex layout as the primary
   row. Primary already has it unconditionally (above), so we only need
   to extend it to additional rows here. */
.ui-refresh .event-settings-content .translations-input.has-tabs .translation-langs > .input-group.is-active {
    display: flex;
    align-items: flex-start;
    column-gap: var(--space-2);
    background: transparent;
    border: 0;
    border-radius: 0;
    overflow: visible;
    min-height: 0;
    margin: 0;
}
.ui-refresh .event-settings-content .translations-input.has-tabs .translation-langs > .input-group.is-active:focus-within {
    border-color: transparent;
    box-shadow: none;
}

/* "Use default translations" checkbox — give it some breathing room. */
.ui-refresh .event-settings-content .translations-input.has-tabs .enter-translations > .checkbox {
    margin-top: var(--space-3);
}

/* ===== Translation collapse (condensed link) =====
   Replaces the per-language tab strip with a single "Translations (N)" link
   under the field that expands the .enter-translations panel (every language
   at once) plus a "Generate all" button. Engages via .has-translation-collapse
   added by event-translation-tabs.js. */

/* Anchored to the label row, not the input: `bottom: calc(100% + space)`
   sits the toggle above the widget's own top edge (where the label lives)
   instead of overlapping the input at `top: -0.5rem`, which clipped into
   the field. Hidden until hover/focus (below) so it doesn't clutter every
   translatable field at rest. */
.ui-refresh .translation-collapse-toggle {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    width: fit-content;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--accent-primary);
    font-family: var(--font-family), sans-serif;
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    cursor: pointer;
    position: absolute;
    right: 0;
    bottom: calc(100% + var(--space-1));
    z-index: 19;
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--transition-fast);
}
/* The H1 wraps to as many lines as the event name needs, so a fixed offset
   from `.event-identity` (the old `top: 3rem`, sized for one line) either
   clips into wrapped text on long names or leaves a permanent gap under
   short ones. `.event-identity-title` wraps just the H1 (event-identity.js
   appends the toggle inside it), so anchoring to *that* box's own bottom
   edge tracks the H1's real rendered height either way, while staying
   `position: absolute` so it reserves no space while hidden. */
.ui-refresh .event-identity-title {
    position: relative;
}
.ui-refresh .event-identity:has(h1.edit-title) .translation-collapse-toggle {
    right: 0;
    top: calc(100% + var(--space-1));
    bottom: auto;
}

/* Reveal on hover of the field/identity block, while editing, for keyboard
   focus, and while the panel is expanded — otherwise it'd vanish mid-use. */
.ui-refresh .form-group:hover .translation-collapse-toggle,
.ui-refresh .event-identity:hover .translation-collapse-toggle,
.ui-refresh .translations-input:focus-within .translation-collapse-toggle,
.ui-refresh .translation-collapse-toggle:focus-visible,
.ui-refresh .translation-collapse-toggle[aria-expanded="true"] {
    opacity: 1;
    pointer-events: auto;
}
.ui-refresh .translation-collapse-toggle:hover {
    color: var(--accent-hover);
    text-decoration: underline;
}
.ui-refresh .translation-collapse-toggle:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
    border-radius: var(--radius-sm);
}
/* Expanded panel */
.ui-refresh .event-settings-content .translations-input.has-translation-collapse .enter-translations,
.ui-refresh [data-translation-collapse] .translations-input.has-translation-collapse .enter-translations {
    margin-top: var(--space-3);
    padding: var(--space-4);
    background: var(--bg-subtle);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
}
.ui-refresh .translation-panel-actions {
    display: flex;
    justify-content: flex-end;
    margin-bottom: var(--space-3);
}
.ui-refresh .translation-generate-all i {
    color: var(--accent-primary);
    margin-right: var(--space-1);
}

/* Per-language rows: [label] [input] [G] on one line. */
.ui-refresh .event-settings-content .translations-input.has-translation-collapse .translation-langs > .input-group,
.ui-refresh [data-translation-collapse] .translations-input.has-translation-collapse .translation-langs > .input-group {
    display: flex;
    align-items: flex-start;
    column-gap: var(--space-3);
    background: transparent;
    border: 0;
    border-radius: 0;
    overflow: visible;
    min-height: 0;
    margin: 0 0 var(--space-3);
}
.ui-refresh .event-settings-content .translations-input.has-translation-collapse .translation-langs > .input-group:last-child,
.ui-refresh [data-translation-collapse] .translations-input.has-translation-collapse .translation-langs > .input-group:last-child {
    margin-bottom: 0;
}
/* Re-show the per-language label (hidden globally for the old tab design) as a
   compact fixed-width caption so the rows are distinguishable. */
.ui-refresh .event-settings-content .translations-input.has-translation-collapse .translation-langs .input-group-addon.lang-name,
.ui-refresh [data-translation-collapse] .translations-input.has-translation-collapse .translation-langs .input-group-addon.lang-name {
    display: inline-flex !important;
    align-items: center;
    flex: 0 0 96px;
    width: 96px;
    /* active-form.css pins `span.lang-name { max-width: max-content !important }`,
       which clamps the label to its text width and defeats the flex basis above.
       Override it here (this selector is more specific) so the label keeps its
       fixed column width. */
    max-width: none !important;
    padding: 8px 0 0;
    background: transparent !important;
    border: 0;
    color: var(--fg-muted);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    text-align: left;
    white-space: normal;
}
.ui-refresh .event-settings-content .translations-input.has-translation-collapse .enter-translations > .checkbox,
.ui-refresh [data-translation-collapse] .translations-input.has-translation-collapse .enter-translations > .checkbox {
    margin: var(--space-3) 0 0;
}

/* Event Name title: reveal the (otherwise fully hidden) name field so its
   translation panel can show under the H1, while keeping the field label, the
   English row and the Yii help-block hidden — the H1 is the English editor and
   the condensed link toggles the panel. */
.ui-refresh .has-name-translations {
    position: relative;
}
.ui-refresh .event-identity.has-name-translations .event-identity-name-field {
    display: block;
    margin: 0;
}
/* The "Translations" toggle floats absolutely just below `.event-identity-title`
   (see the `.translation-collapse-toggle` rules above) so it reserves no space
   while closed — but once the panel opens, this (now-visible) name field starts
   flowing right there too, directly under the toggle's own box, and the two
   overlap. Push the field down by the toggle's height + its gap only while
   `.is-translations-open` (set by event-identity.js) so the collapsed state is
   unaffected. */
.ui-refresh .event-identity.is-translations-open .event-identity-name-field {
    margin-top: 28px;
}
.ui-refresh .event-identity.has-name-translations .event-identity-name-field > label,
.ui-refresh .event-identity.has-name-translations .event-identity-name-field .translations-input > .en,
.ui-refresh .event-identity.has-name-translations .event-identity-name-field .help-block {
    display: none !important;
}
/* Loading / saving spinner overlay (App.showSpinner). backend.css hardcodes
   `background: white`, so in dark mode the box stayed white while the text
   followed the light --fg — white-on-white. Token-ize it so it adapts to every
   surface tone. */
.ui-refresh #spinner {
    background: var(--bg-surface);
    color: var(--fg-default);
    border-color: var(--border-default);
    box-shadow: var(--shadow-lg);
}

/* ===== Property chip strip =====
   Notion-style chip row at the top of the consolidated event Settings
   page. Each chip is a button + dropdown that writes back to the form
   field of the same name elsewhere in the page (see chip-promoted-field).
   Mirrors v4 .props / .prop / .prop-trigger / .prop-menu spec but scoped
   to .event-property-chips so it doesn't bleed into other pages. */
.ui-refresh .event-property-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 0 0 var(--space-5);
}
.ui-refresh .event-property-chips .prop-trigger {
    position: relative;
    display: inline-flex;
}
.ui-refresh .event-property-chips .prop {
    appearance: none;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 6px 10px;
    border-radius: var(--radius-lg);
    font-family: var(--font-family);
    font-size: 12.5px;
    font-weight: var(--font-weight-medium);
    color: var(--fg-default);
    background: var(--bg-surface);
    border: 1px solid var(--border-default);
    cursor: pointer;
    font-variant-numeric: tabular-nums;
    transition: background var(--transition-fast),
                border-color var(--transition-fast),
                box-shadow var(--transition-fast);
}
.ui-refresh .event-property-chips .prop:hover {
    border-color: var(--border-strong);
}
.ui-refresh .event-property-chips .prop svg {
    width: 13px;
    height: 13px;
    color: var(--accent-primary);
    flex-shrink: 0;
}
.ui-refresh .event-property-chips .prop .prop-caret {
    display: none;
    color: var(--fg-subtle);
    margin-left: 2px;
    line-height: 1;
    transition: transform .15s, color var(--transition-fast);
}
.ui-refresh .event-property-chips .prop .prop-caret svg {
    width: 10px;
    height: 10px;
    color: inherit;
}
.ui-refresh .event-property-chips .prop-trigger.open > .prop {
    border-color: var(--accent-primary);
    background: var(--bg-muted);
}
.ui-refresh .event-property-chips .prop-trigger.open > .prop .prop-caret {
    transform: rotate(180deg);
    color: var(--accent-primary);
}

/* Dropdown menu under each chip */
.ui-refresh .event-property-chips .prop-menu {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 50;
    min-width: 180px;
    max-height: 280px;
    overflow-y: auto;
    padding: 4px;
    background: var(--bg-surface);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md, 0 8px 24px rgba(0, 0, 0, 0.18));
    display: none;
}
.ui-refresh .event-property-chips .prop-menu-wide {
    min-width: 240px;
}
.ui-refresh .event-property-chips .prop-trigger.open .prop-menu {
    display: block;
}
.ui-refresh .event-property-chips .prop-menu .qmi {
    appearance: none;
    border: 0;
    background: transparent;
    width: 100%;
    text-align: left;
    padding: 7px 10px;
    border-radius: 6px;
    font-family: var(--font-family);
    font-size: 13px;
    color: var(--fg-default);
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 8px;
}
/* Hover and selected need clearly distinct backgrounds — when both used
   --bg-muted the only difference was text weight/color, which under amber
   accent (tint ≈ bg-muted) became visually indistinguishable. Hover uses a
   neutral overlay; selected uses the accent tint so the "this is on" state
   reads as accent-flavored. */
.ui-refresh .event-property-chips .prop-menu .qmi:hover {
    background: var(--state-hover);
}
.ui-refresh .event-property-chips .prop-menu .qmi[aria-checked="true"] {
    background: var(--accent-subtle);
    color: var(--accent-on-tint);
    font-weight: var(--font-weight-semibold);
}
.ui-refresh .event-property-chips .prop-menu .qmi[aria-checked="true"]:hover {
    background: var(--accent-muted);
}
.ui-refresh .event-property-chips .prop-menu .qmi:disabled {
    color: var(--fg-muted);
    cursor: not-allowed;
    opacity: .5;
}
.ui-refresh .event-property-chips .prop-menu .qmi:disabled:hover {
    background: transparent;
}

/* Hide the form-group rows for fields that have been promoted to a chip.
   The underlying inputs stay in the DOM so the form still posts them,
   but the row is invisible. */
.ui-refresh .chip-promoted-field {
    display: none !important;
}

/* ===== Toggle group =====
   Bordered card that groups related on/off settings into rows. Mirrors
   v4's .rf-card + .rf-switch-row pattern: card chrome (paper-2/card bg,
   line border, 8–10px radius), with hairline dividers between rows.
   Wrap each toggle (and its optional collapse panel) in .toggle-row so
   the collapse stays inside the row; simple toggles without nested
   settings can be a direct child .form-group. */
.ui-refresh .toggle-group {
    background: var(--bg-surface);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    margin: var(--space-5) 0;
    padding: 0 var(--space-4);
}
.ui-refresh .toggle-group:not(:has(> .toggle-row:not([style*="display: none"]), > .form-group:not([style*="display: none"]), > .additional-package-row:not([style*="display: none"]))) {
    border: none;
    padding: 0;
    margin: 0;
}
/* Drawer panel bodies already establish their own 12px vertical rhythm with
   flex gap. Do not stack the page-level group margins on top of that gap. */
.ui-refresh .ajax-drawer .toggle-group {
    margin: 0;
}
.ui-refresh .toggle-group > .toggle-row,
.ui-refresh .toggle-group > .form-group {
    margin: 0;
    padding: var(--space-4) 0;
}
/* ToggleRow renders its label directly under .toggle-row instead of inside
   Bootstrap's .checkbox wrapper. Give both shapes the same grouped-row type
   treatment so drawer groups match the established Event Details cards. */
.ui-refresh .toggle-row > label.toggle-label {
    color: var(--fg-default) !important;
    font-size: var(--font-size-toggle) !important;
    font-weight: var(--font-weight-medium) !important;
}
/* Compact variant — tighter row padding for short, toggle-only groups
   (e.g. CFS Role's drawer, which has just two rows and no nested settings).
   The surrounding margin is tightened to match — the default --space-5
   reads as too loose once the internal row rhythm is this dense. */
.ui-refresh .toggle-group--compact > .toggle-row,
.ui-refresh .toggle-group--compact > .form-group {
    padding: var(--space-2) 0;
}
.ui-refresh .toggle-group--compact {
    margin: var(--space-4) 0;
}
/* Caption for a toggle-group with no section heading above it (e.g. CFS
   Role's "Permissions") — reuses .form-label directly (kartik's default
   field-label class; see base.css) so it matches the surrounding field
   labels exactly instead of approximating their look. Sits tight to the
   group instead of the toggle-group's default top margin, matching the
   .form-label + .toggle-group override above for the MFA methods case. */
.ui-refresh .toggle-group-caption + .toggle-group {
    margin-top: 0;
}
/* The caption sits flush against its group (margin-top: 0 above), so the
   preceding field's full --space-5 (20px) bottom margin reads as lopsided —
   a big gap above the caption, none below it. Tighten it to match. */
.ui-refresh .form-group:has(+ .toggle-group-caption) {
    margin-bottom: var(--space-3);
}
/* Suppress orphan divider when the previous sibling row is hidden via
   inline display:none (e.g. #capacity-settings before capacity is set). */
.ui-refresh .toggle-group > [style*="display: none"] + .toggle-row,
.ui-refresh .toggle-group > [style*="display:none"] + .toggle-row,
.ui-refresh .toggle-group > [style*="display: none"] + .form-group,
.ui-refresh .toggle-group > [style*="display:none"] + .form-group {
    border-top: 0;
}
.ui-refresh .toggle-row > .form-group {
    margin-bottom: 0;
    width: 100%;
}
/* Spacing between the toggle row label and the panel below it. We push the
   gap onto the collapse's *inner* first child rather than the .collapse
   element itself — Bootstrap's `.collapsing` sets `overflow: hidden` which
   makes the inner margin part of the animated height. Putting the margin on
   the collapse element directly would render fine while open but vanish
   instantly when Bootstrap toggles `display: none` at the end of the close
   transition, producing a visible "jump". With the margin inside, the gap
   shrinks smoothly together with the panel height. */
.ui-refresh .toggle-row > .form-group + .collapse > :first-child,
.ui-refresh .toggle-row > .form-group + .collapsing > :first-child,
.ui-refresh .toggle-row > .checkbox + .collapse > :first-child,
.ui-refresh .toggle-row > .checkbox + .collapsing > :first-child,
.ui-refresh .toggle-row > label + .collapse > :first-child,
.ui-refresh .toggle-row > label + .collapsing > :first-child {
    margin-top: var(--space-4);
}
/* Unify toggle rows to match the v4 .rf-switch-row mockup: label on left,
   switch flush right, with the label text using a single consistent
   treatment (13px medium ink, sentence case). Yii2 emits labels in three
   different shapes (.form-label, .has-star, plain) — force them all here.
   Inner .control-label inside collapse panels (e.g. waitlist invitation
   type) is unaffected because we scope to .checkbox > label. */
.ui-refresh .toggle-group .checkbox,
.ui-refresh .toggle-row .checkbox {
    padding: 0;
}
.ui-refresh .toggle-group .checkbox > label,
.ui-refresh .toggle-row .checkbox > label {
    display: flex;
    align-items: center;
    gap: 14px;
    width: 100%;
    font-size: var(--font-size-toggle);
    font-weight: var(--font-weight-medium);
    text-transform: none;
    margin-bottom: 0;
    padding-left: 0;
    cursor: pointer;
    /* A field-bound checkbox (e.g. allow_registrant_timezone, wrapped in a
       Yii2 .form-group) otherwise inherits the muted `.form-group label`
       color from base.css, reading lighter than a plain-markup toggle like
       Schedule Go Live. Force full ink so every row in the group matches
       regardless of whether it's backed by an ActiveField. */
    color: var(--fg-default);
}
/* Push the toggle pill to the right edge of the row. */
.ui-refresh .toggle-group .checkbox > label > input[type="checkbox"],
.ui-refresh .toggle-row .checkbox > label > input[type="checkbox"] {
    order: 99;
    margin-left: auto !important;
}
.ui-refresh div.required .toggle-row label:first-child:after {
    content: " ";
}

/* ===== Tone picker ===== */
.ui-refresh .surface-mode-segmented {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}
.ui-refresh .surface-mode-segmented .surface-segment {
    appearance: none;
    position: relative;
    width: 26px;
    height: 26px;
    padding: 0;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast), transform var(--transition-fast);
}
.ui-refresh .surface-mode-segmented .surface-segment::after {
    position: absolute;
    right: 4px;
    bottom: 4px;
    left: 4px;
    height: 4px;
    border-radius: 1px;
    content: '';
}
.ui-refresh .surface-mode-segmented .surface-segment:hover {
    transform: translateY(-1px);
}
.ui-refresh .surface-mode-segmented .surface-segment.is-active {
    border-color: var(--fg-default);
    box-shadow: 0 0 0 1px var(--fg-default);
}
.ui-refresh .surface-mode-segmented .surface-segment:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}
.ui-refresh .surface-mode-segmented .surface-segment--morning {
    background: var(--appearance-tone-day);
}
.ui-refresh .surface-mode-segmented .surface-segment--morning::after {
    background: var(--appearance-tone-day-bar);
}
.ui-refresh .surface-mode-segmented .surface-segment--afternoon {
    background: var(--appearance-tone-afternoon);
}
.ui-refresh .surface-mode-segmented .surface-segment--afternoon::after {
    background: var(--appearance-tone-afternoon-bar);
}
.ui-refresh .surface-mode-segmented .surface-segment--evening {
    background: var(--appearance-tone-evening);
}
.ui-refresh .surface-mode-segmented .surface-segment--evening::after {
    background: var(--appearance-tone-evening-bar);
}
.ui-refresh .surface-mode-segmented .surface-segment--midnight {
    background: var(--appearance-tone-midnight);
}
.ui-refresh .surface-mode-segmented .surface-segment--midnight::after {
    background: var(--appearance-tone-midnight-bar);
}

/* ===== Accent picker =====
   Three circular swatches in the profile dropdown alongside the surface
   picker: Purple, Amber, Custom. The Custom swatch is a <label> wrapping
   an invisible <input type="color"> — clicking the visible circle opens
   the OS color picker. The label's background reflects the user's saved
   custom hex (or the purple default) so they can see what "custom" is set
   to without opening the picker. */
.ui-refresh .accent-picker {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.ui-refresh .accent-swatch {
    appearance: none;
    -webkit-appearance: none;
    border: 1px solid rgba(0, 0, 0, 0.10);
    width: 22px;
    height: 22px;
    border-radius: 50%;
    margin: 0;
    padding: 0;
    cursor: pointer;
    position: relative;
    display: inline-block;
    transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}
.ui-refresh .accent-swatch:hover {
    box-shadow: 0 0 0 2px var(--card), 0 0 0 3px var(--border-strong);
}
.ui-refresh .accent-swatch.is-active {
    box-shadow: 0 0 0 2px var(--card), 0 0 0 3px var(--ink);
}
.ui-refresh .accent-swatch--custom {
    overflow: hidden;
}
.ui-refresh .accent-swatch__input {
    /* Stretch the native picker over the swatch but keep it visually hidden;
       the label's background is what the user sees. */
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    border: 0;
    margin: 0;
    padding: 0;
    cursor: pointer;
}
.ui-refresh .ui-refresh-toggle__label--with-warning {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}
.ui-refresh .accent-contrast-warning {
    display: none;
    color: var(--color-warning);
    cursor: help;
    line-height: 1;
}
.ui-refresh .accent-contrast-warning.is-visible {
    display: inline-flex;
}
/* Matches the token-based Bootstrap tooltips used elsewhere in UI refresh. */
body.ui-refresh .tooltip.accent-contrast-tooltip {
    opacity: 1;
}
/* Portaled by Bootstrap so the warning is never clipped by the profile menu. */
body.ui-refresh .tooltip.accent-contrast-tooltip .tooltip-inner {
    background: var(--bg-surface);
    border: 1px solid var(--border-strong);
    box-shadow: var(--sh-md);
    color: var(--fg-muted);
    font-family: var(--font-family);
    font-size: var(--font-size-xs);
    max-width: 240px;
}
body.ui-refresh .tooltip.accent-contrast-tooltip.top .tooltip-arrow {
    border-top-color: var(--border-strong);
}
body.ui-refresh .tooltip.accent-contrast-tooltip.top .tooltip-arrow::after {
    content: '';
    position: absolute;
    bottom: 1px;
    left: -5px;
    border-width: 5px 5px 0;
    border-style: solid;
    border-color: var(--bg-surface) transparent transparent;
}

/* ===== Google Places Autocomplete (.pac-container) =====
   Google injects the autocomplete dropdown at the end of <body> with inline
   light-mode styles (white bg, dark text). It portals outside .ui-refresh, so
   scope to body.ui-refresh and point at flipping tokens so it tracks dark mode.
   Base rule works in light too (--bg-surface = white there). */
body.ui-refresh .pac-container {
    /* Google appends this at the end of <body>, so inside an AjaxDrawer
     * (fixed, z-index: 1050) it's a sibling of the drawer, not a
     * descendant, and loses the stacking fight without help. The legacy
     * iframe-based sidebar never had this problem since <body> there was
     * the iframe's own isolated document. !important beats whatever
     * inline/dynamic styling Google's script applies. */
    z-index: 1060 !important;
    background: var(--bg-surface);
    border-color: var(--border-strong);
    box-shadow: var(--sh-lg);
    z-index: 2000; /* needs to be higher than the ajax-drawer's z-index */
}
body.ui-refresh .pac-item {
    color: var(--fg-muted);
    border-top-color: var(--border-default);
}
body.ui-refresh .pac-item:first-child {
    border-top-color: transparent;
}
body.ui-refresh .pac-item-query {
    color: var(--fg-default);
}
body.ui-refresh .pac-item:hover,
body.ui-refresh .pac-item-selected,
body.ui-refresh .pac-item-selected:hover {
    background: var(--bg-muted);
}

.ui-refresh .input-group .form-control:first-child {
    border-bottom-right-radius: 0 !important;
    border-top-right-radius: 0 !important;
}

/* Side support pill + slide-out menu (legacy `.support-dropdown` rules in
   web/css/layout.css hardcode `var(--swoogo-lavender-100)` for the pill
   chrome and `white` for the menu, locked in with !important — pre-dating
   any theming). Re-token to the active accent so purple/amber/custom all
   recolor the pill, and to the surface tokens so dark mode produces a
   dark menu instead of a stark white one against a dark page. The matching
   Zendesk iframe — launched from inside this menu — is recolored
   server-side via `getUiAccentHex()` in functions.php; see
   modules/loggedin/views/layouts/main.php's `window.zESettings` block. */
body.ui-refresh .support-dropdown > .support-button,
body.ui-refresh .support-dropdown.open > .support-button,
body.ui-refresh .support-dropdown:not(.open) > .support-button:hover {
    background: var(--accent-primary) !important;
    color: var(--fg-on-accent) !important;
}
body.ui-refresh .support-dropdown ul.support-list {
    background: var(--bg-surface-raised) !important;
    border-color: var(--accent-primary) !important;
    box-shadow: var(--shadow-lg) !important;
}
body.ui-refresh .support-dropdown ul.support-list > li > a {
    color: var(--fg-default) !important;
}
/* Bootstrap 3's default `.dropdown-menu > li > a:hover` paints a generic
   gray; legacy CSS only sets border-radius for first/last child. Replace
   the hover fill with the accent tint so it reads as part of the same
   color story as the pill, and lift the text to `--accent-on-tint`
   (purple in light mode, lifted toward white in dark — see tokens.css
   line 431) so it stays legible on the tinted bg. */
body.ui-refresh .support-dropdown ul.support-list > li > a:hover,
body.ui-refresh .support-dropdown ul.support-list > li > a:focus {
    background: var(--accent-subtle) !important;
    color: var(--accent-on-tint) !important;
}

.ui-refresh input[type="text"],
.ui-refresh input[type="email"],
.ui-refresh input[type="password"],
.ui-refresh input[type="search"],
.ui-refresh input[type="number"],
.ui-refresh input[type="url"],
.ui-refresh textarea,
.ui-refresh select {
    border-color: var(--input-border);
    color: var(--ink);
}
.ui-refresh .form-control::placeholder,
.ui-refresh input::placeholder,
.ui-refresh textarea::placeholder {
    color: var(--muted-2);
}
.ui-refresh .cke_inner {
    border-radius: var(--space-2);
    border: 1px solid var(--input-border);
    overflow: hidden;
}
.ui-refresh .sidebar-translations-input-group .cke_inner,
.ui-refresh .sidebar-translations-input-group textarea {
    border-radius: 0;
    border: none;
}
/* No dark-mode override needed for .btn-primary — the base rule uses
   var(--primary) which is the active accent in both light and dark modes
   (with --primary-ring/-tint values surface-tuned in tokens.css). The old
   paper-2/ink override here was a workaround for when .btn-primary used
   var(--ink) and would render as a glaring cream button on dark; it no
   longer applies and would actively suppress the accent if kept. */

/* CKEditor toolbar (.cke_top) sits above its iframe and uses a hardcoded
   light background. Pin it to --bg-page so the toolbar reads as chrome
   sitting on paper rather than a near-twin of the white input card below;
   --bg-page re-resolves automatically across light and dark surfaces, so
   this single rule covers both. A hairline divider gives the editing
   iframe a clean visual edge. The iframe content (.cke_wysiwyg_frame) is
   a separate document and would need a CKEditor `contentsCss` config to
   theme — leave that for now since it's the actual editing surface and
   inverting it would surprise users mid-edit. */
.ui-refresh .cke_top {
    background: var(--bg-page) !important;
    border-bottom: 1px solid var(--border-default) !important;
}

/* The Event Settings Description field is primarily read-first. Keep its
   formatting controls out of the way until CKEditor's focus manager marks the
   field as actively being edited. This is intentionally scoped to the
   ui-refresh event form so rich-text fields elsewhere retain their current
   always-visible toolbar. Grid rows let the toolbar expand to its natural
   height, including when it wraps on narrower screens. */
.ui-refresh .event-settings-content .field-event-description .cke_top {
    display: grid;
    grid-template-rows: 1fr;
    max-height: 200px;
    opacity: 1;
    overflow: hidden;
    transition: grid-template-rows var(--transition-normal), opacity var(--transition-normal),
        padding var(--transition-normal), border-bottom-width var(--transition-normal),
        visibility 0s;
}
.ui-refresh .event-settings-content .field-event-description .cke_top > * {
    min-height: 0;
}
.ui-refresh .event-settings-content .field-event-description .cke:not(.cke_focus) .cke_top {
    grid-template-rows: 0fr;
    max-height: 0;
    padding-top: 0;
    padding-bottom: 0;
    border-bottom-width: 0 !important;
    opacity: 0;
    pointer-events: none;
    visibility: hidden;
    transition: grid-template-rows var(--transition-normal), opacity var(--transition-normal),
        padding var(--transition-normal), border-bottom-width var(--transition-normal),
        visibility 0s var(--transition-normal);
}
.ui-refresh .event-settings-content .field-event-description .cke_bottom {
    max-height: 32px;
    opacity: 1;
    overflow: hidden;
    transition: max-height var(--transition-normal), opacity var(--transition-normal),
        border-top-width var(--transition-normal), visibility 0s;
}
.ui-refresh .event-settings-content .field-event-description .cke:not(.cke_focus) .cke_bottom {
    max-height: 0;
    border-top-width: 0 !important;
    opacity: 0;
    pointer-events: none;
    visibility: hidden;
    transition: max-height var(--transition-normal), opacity var(--transition-normal),
        border-top-width var(--transition-normal), visibility 0s var(--transition-normal);
}
@media (prefers-reduced-motion: reduce) {
    .ui-refresh .event-settings-content .field-event-description .cke_top,
    .ui-refresh .event-settings-content .field-event-description .cke:not(.cke_focus) .cke_top,
    .ui-refresh .event-settings-content .field-event-description .cke_bottom,
    .ui-refresh .event-settings-content .field-event-description .cke:not(.cke_focus) .cke_bottom {
        transition: none;
    }
}

/* ===== CKEditor 4 toolbar chrome =====
   Only .cke_top had a token-based base; the moono-lisa skin hardcodes light
   greys for the frame, buttons, combos, path bar, separators, and resizer,
   so they all leaked light chrome in dark mode once the surface overrides
   were removed. Re-point each to a flipping token so one base rule is correct
   in both modes (in light the tokens resolve to ~the skin's own greys). The
   toolbar icons are a dark sprite sheet, so they invert via --cke-icon-filter
   (none in light). !important matches the skin's own specificity. */
.ui-refresh .cke,
.ui-refresh .cke_chrome {
    border-color: var(--border-strong) !important;
    border: none;
    box-shadow: none !important;
}
.ui-refresh .cke_inner,
.ui-refresh .cke_bottom {
    background: var(--bg-surface) !important;
    border-color: var(--border-strong) !important;
}
.ui-refresh .cke_bottom {
    border-top: 1px solid var(--border-default) !important;
}
.ui-refresh .cke_button_label,
.ui-refresh .cke_combo_text,
.ui-refresh .cke_combo_inlinelabel,
.ui-refresh .cke_combo_button .cke_combo_open .cke_combo_arrow {
    color: var(--fg-default) !important;
}
.ui-refresh .cke_button_icon {
    filter: var(--cke-icon-filter);
}
.ui-refresh .cke_button_disabled .cke_button_icon {
    opacity: 0.45;
}
.ui-refresh .cke_combo_button {
    background: transparent !important;
    border-color: transparent !important;
}
.ui-refresh .cke_combo_button:hover,
.ui-refresh .cke_combo_button:focus,
.ui-refresh .cke_combo_on .cke_combo_button,
.ui-refresh .cke_button_on,
.ui-refresh a.cke_button_on:hover,
.ui-refresh a.cke_button_on:focus,
.ui-refresh a.cke_button_on:active,
.ui-refresh a.cke_button_off:hover,
.ui-refresh a.cke_button_off:focus,
.ui-refresh a.cke_button_off:active {
    background: var(--bg-muted) !important;
    border-color: var(--border-strong) !important;
    box-shadow: none !important;
}
.ui-refresh .cke_path_item,
.ui-refresh .cke_path_empty,
.ui-refresh .cke_resizer {
    color: var(--fg-muted) !important;
}
.ui-refresh .cke_toolbar_separator {
    background: var(--border-strong) !important;
}
/* Source mode swaps the iframe for a plain textarea. The skin pins its
   background to #fff, but `.ui-refresh textarea` above flips its text to
   --ink, so dark mode rendered light-on-white. Re-point the background at
   the surface token to match the cke-evening wysiwyg body (contents.css).
   border-box keeps the padding inside CKEditor's inline 100% width/height
   (.cke_reset forces content-box). */
.ui-refresh textarea.cke_source {
    background-color: var(--bg-surface);
    padding: var(--space-2) var(--space-3);
    box-sizing: border-box;
}

/* Sessions/speakers tab-row: pin the tab links to the muted/ink text tokens
   (matching the base .tr-tab colours in sessions.css) so they don't pick up
   the generic anchor accent colour. Tokens flip with the surface. */
body.ui-refresh .tab-row a.tr-tab {
    color: var(--muted);
}
body.ui-refresh .tab-row a.tr-tab:hover {
    color: var(--ink);
}
body.ui-refresh .tab-row a.tr-tab.active {
    color: var(--ink);
}

/* ---------- BRIGHT (Afternoon) MODE ---------- */

/* Cream → pure white is a small visual delta on its own; reinforce card
   boundaries so dashlets remain visible against the white page. */

/* ---------- jQuery UI Datepicker — Accent Tokenization ----------
   The plugin's CSS (web/plugins/datepicker/date-input.css) hardcodes
   var(--swoogo-lavender-100) for hover/selected/today-text/Done-button/list
   states. Re-point those to var(--primary) so the picker follows the body
   accent (purple, amber, custom). Scoped to body.ui-refresh — the picker
   is portaled to body, but body.ui-refresh exposes --primary either way. */
body.ui-refresh .ui-datepicker-calendar .ui-datepicker-current-day:not(.ui-datepicker-today),
body.ui-refresh .ui-datepicker-calendar tbody td:hover {
    background: var(--primary);
}
body.ui-refresh .ui-datepicker-calendar tbody .ui-datepicker-today a {
    color: var(--primary);
}
body.ui-refresh .ui-datepicker .ui-datepicker-buttonpane .ui-datepicker-close {
    background-color: var(--primary);
}
body.ui-refresh .ui-datepicker .ui-datepicker-months li:hover,
body.ui-refresh .ui-datepicker .ui-datepicker-years li:hover,
body.ui-refresh .ui-datepicker .ui-datepicker-months li.ui-state-active,
body.ui-refresh .ui-datepicker .ui-datepicker-years li.ui-state-active {
    background-color: var(--primary);
}
body.ui-refresh .ui-datepicker li.ui-present-date {
    color: var(--primary);
}

/* ---------- jQuery UI Datepicker — Surface Tokenization ----------
   date-input.css also hardcodes the picker's light surface: white panel,
   #F8F7FA tints, #111928/#19181A ink, #E5E7EB border, #6E7375 SVG chevrons.
   layout.css already re-points the title/day ink at --fg-* tokens, so in
   dark mode the text flipped to light ink while the panel stayed hardcoded
   white — an unreadable half-theme. Re-point every surface literal at
   tokens (same treatment as .dropdown-menu) so the whole picker follows
   the active surface; the --dp-*-icon tokens carry the light/dark SVG
   variants (tokens.css). */
body.ui-refresh .ui-datepicker.ui-widget {
    background-color: var(--bg-surface);
    border: 1px solid var(--border-default);
    box-shadow: var(--sh-lg);
}
body.ui-refresh .ui-datepicker thead th {
    color: var(--fg-default);
}
body.ui-refresh .ui-datepicker-calendar tbody .ui-datepicker-today:not(.ui-datepicker-unselectable) {
    background: var(--bg-muted);
}
/* The plugin pins other-month day color with !important — match it to win. */
body.ui-refresh .ui-datepicker-calendar tbody td.ui-datepicker-other-month a {
    color: var(--fg-subtle) !important;
}
body.ui-refresh .ui-datepicker td.ui-datepicker-unselectable.ui-state-disabled,
body.ui-refresh .ui-datepicker tbody td.ui-datepicker-unselectable.ui-state-disabled:hover span,
body.ui-refresh .ui-datepicker tbody td.ui-datepicker-unselectable.ui-state-disabled:hover a {
    background-color: var(--bg-muted);
}
body.ui-refresh .ui-datepicker tbody td.ui-datepicker-unselectable.ui-state-disabled:hover span,
body.ui-refresh .ui-datepicker tbody td.ui-datepicker-unselectable.ui-state-disabled:hover a {
    color: var(--fg-default);
}
/* "Today" button (the outlined half of the buttonpane; "Done" is accent-filled above). */
body.ui-refresh .ui-datepicker .ui-datepicker-buttonpane .ui-datepicker-current {
    background-color: var(--bg-surface);
    color: var(--fg-default);
    border: 1px solid var(--input-border);
}
/* Month/year dropdown lists portal as ul panels inside the title. */
body.ui-refresh .ui-datepicker .ui-datepicker-months,
body.ui-refresh .ui-datepicker .ui-datepicker-years {
    color: var(--fg-default);
}
body.ui-refresh ul.ui-datepicker-months,
body.ui-refresh ul.ui-datepicker-years {
    background: var(--bg-surface);
    box-shadow: var(--sh-lg);
}
body.ui-refresh .ui-datepicker .ui-datepicker-months li,
body.ui-refresh .ui-datepicker .ui-datepicker-years li {
    background-color: var(--bg-surface);
}
body.ui-refresh .ui-datepicker li.ui-present-date {
    background-color: var(--bg-muted);
}
body.ui-refresh .ui-datepicker-years::-webkit-scrollbar-track {
    background-color: var(--bg-muted);
}
body.ui-refresh .ui-datepicker-years::-webkit-scrollbar-thumb {
    background-color: var(--fg-subtle);
}
/* Prev/next chevrons + month/year caret bake #6E7375 into SVG data-URLs —
   swap the whole image via token (same approach as --select-chevron-image). */
body.ui-refresh .ui-datepicker-prev {
    background-image: var(--dp-prev-icon);
}
body.ui-refresh .ui-datepicker-next {
    background-image: var(--dp-next-icon);
}
body.ui-refresh .ui-datepicker .ui-swoogo-icon {
    background-image: var(--dp-caret-icon);
}

/* ===========================================================================
   CKEditor dialogs (link, anchor, image, etc.)
   ===========================================================================
   CKEditor 4 dialogs are rendered as .cke_dialog divs at the document root,
   not iframed — so they sit alongside the page content and can be styled
   directly. The moono-lisa skin ships hardcoded #fff/#f8f8f8/#d1d1d1 chrome,
   which glares on dark surfaces and clashes with the design-system inputs.

   Strategy:
   1. Re-skin every CKE dialog to match ui-refresh tokens (covers anchor +
      image + any other dialog automatically).
   2. Tokens auto-resolve in dark mode, so most rules need no dark-mode
      override. Exceptions: hardcoded close-icon PNG and the OK-button green
      (overridden below).
   3. Tag the link dialog with data-cke-dialog="link" via app.js JS hook and
      transform that one into a right-side drawer.
   ========================================================================= */

/* ---- Backdrop ---- */
.ui-refresh .cke_dialog_background_cover {
    background: rgba(0, 0, 0, 0.4) !important;
    opacity: 1 !important;
}

/* ---- Dialog body (the actual box) ---- */
.ui-refresh .cke_dialog_body {
    background: var(--card) !important;
    color: var(--ink) !important;
    border: 1px solid var(--line) !important;
    border-radius: var(--radius-xl) !important;
    box-shadow: var(--shadow-xl) !important;
    overflow: hidden;
}

/* ---- Title bar ---- */
.ui-refresh .cke_dialog_title {
    background: var(--paper-2) !important;
    color: var(--ink) !important;
    border-bottom: 1px solid var(--line) !important;
    font-family: var(--font-family) !important;
    font-size: var(--font-size-sm) !important;
    font-weight: 500 !important;
    letter-spacing: normal !important;
    padding: var(--space-4) var(--space-6) !important;
}

/* Close button — moono-lisa uses a dark PNG that vanishes on dark surfaces
   and reads as a tiny X on light surfaces. Replace with an inline SVG that
   tracks --ink so it follows the surface. */
.ui-refresh a.cke_dialog_close_button {
    /* --cke-dialog-close-icon flips the baked SVG fill (dark X in light,
       light X in dark) so the close control stays visible on either header. */
    background-image: var(--cke-dialog-close-icon) !important;
    background-size: 14px !important;
    background-position: 50% !important;
    opacity: 0.7;
    top: var(--space-3) !important;
}
.ui-refresh a.cke_dialog_close_button:hover {
    opacity: 1;
}

/* ---- Tabs ---- */
.ui-refresh a.cke_dialog_tab {
    background: var(--paper-2) !important;
    color: var(--ink-2) !important;
    border: 1px solid var(--line) !important;
    border-bottom: none !important;
    border-radius: var(--radius-sm) var(--radius-sm) 0 0 !important;
    font-family: var(--font-family) !important;
    font-size: var(--font-size-xs) !important;
    font-weight: 500 !important;
    letter-spacing: normal !important;
}
.ui-refresh a.cke_dialog_tab:hover {
    background: var(--card) !important;
    color: var(--ink) !important;
}
.ui-refresh a.cke_dialog_tab_selected,
.ui-refresh a.cke_dialog_tab_selected:hover,
.ui-refresh a.cke_dialog_tab_selected:focus {
    background: var(--card) !important;
    color: var(--ink) !important;
    border-bottom-color: var(--card) !important;
}

/* ---- Content area ---- */
.ui-refresh .cke_dialog_contents {
    background: var(--card) !important;
    border-top: 1px solid var(--line) !important;
}
.ui-refresh .cke_dialog_contents_body {
    color: var(--ink);
}
.ui-refresh .cke_dialog_body label,
.ui-refresh .cke_dialog_ui_labeled_label {
    color: var(--ink-2) !important;
    font-family: var(--font-family) !important;
    font-size: var(--font-size-xs) !important;
    font-weight: 500 !important;
    letter-spacing: normal !important;
}

/* ---- Form inputs inside dialogs ---- */
.ui-refresh input.cke_dialog_ui_input_text,
.ui-refresh input.cke_dialog_ui_input_password,
.ui-refresh input.cke_dialog_ui_input_tel,
.ui-refresh textarea.cke_dialog_ui_input_textarea,
.ui-refresh select.cke_dialog_ui_input_select {
    background: var(--card) !important;
    color: var(--ink) !important;
    border: 1px solid var(--line) !important;
    border-radius: var(--radius-sm) !important;
    font-family: var(--font-family) !important;
    font-size: var(--font-size-sm) !important;
    padding: var(--space-2) var(--space-3) !important;
    min-height: 36px !important;
    box-shadow: none !important;
}
.ui-refresh input.cke_dialog_ui_input_text:hover,
.ui-refresh input.cke_dialog_ui_input_password:hover,
.ui-refresh input.cke_dialog_ui_input_tel:hover,
.ui-refresh textarea.cke_dialog_ui_input_textarea:hover {
    border-color: var(--muted) !important;
}
.ui-refresh input.cke_dialog_ui_input_text:focus,
.ui-refresh input.cke_dialog_ui_input_password:focus,
.ui-refresh input.cke_dialog_ui_input_tel:focus,
.ui-refresh textarea.cke_dialog_ui_input_textarea:focus,
.ui-refresh select.cke_dialog_ui_input_select:focus {
    border: 1px solid var(--primary) !important;
    box-shadow: 0 0 0 3px var(--primary-ring) !important;
    outline: 0 !important;
}

/* Fieldsets (e.g. anchor "Select an Anchor", popup features in link) */
.ui-refresh .cke_dialog fieldset {
    border: 1px solid var(--line) !important;
    border-radius: var(--radius-md) !important;
    padding: var(--space-3) var(--space-4) !important;
}
.ui-refresh .cke_dialog fieldset legend {
    color: var(--ink-2) !important;
    font-size: var(--font-size-xs) !important;
    font-weight: 500 !important;
}

/* ---- Footer ---- */
.ui-refresh .cke_dialog_footer {
    background: var(--paper-2) !important;
    border-top: 1px solid var(--line) !important;
    padding: var(--space-3) var(--space-4) !important;
}
.ui-refresh .cke_dialog_footer .cke_resizer {
    display: none !important; /* drag-resize doesn't fit the design system */
}

/* ---- Footer buttons ---- */
.ui-refresh a.cke_dialog_ui_button {
    background: var(--card) !important;
    color: var(--ink) !important;
    border: 1px solid var(--line) !important;
    border-radius: var(--radius-sm) !important;
    font-family: var(--font-family) !important;
    letter-spacing: normal !important;
    padding: var(--space-2) var(--space-1) !important;
    line-height: 20px !important;
}
.ui-refresh .cke_dialog_footer_buttons a.cke_dialog_ui_button span {
    color: inherit !important;
    font-size: var(--font-size-sm) !important;
    font-weight: 500 !important;
    padding: 0 var(--space-3) !important;
}
.ui-refresh a.cke_dialog_ui_button:hover {
    background: var(--paper-2) !important;
    border-color: var(--muted) !important;
}
.ui-refresh a.cke_dialog_ui_button:focus,
.ui-refresh a.cke_dialog_ui_button:active {
    border: 1px solid var(--primary) !important;
    box-shadow: 0 0 0 3px var(--primary-ring) !important;
    outline: 0 !important;
    padding: var(--space-2) var(--space-1) !important;
}

/* OK / primary button — replaces the moono-lisa #09863e green */
.ui-refresh a.cke_dialog_ui_button_ok {
    background: var(--primary) !important;
    border-color: var(--primary) !important;
    color: #fff !important;
}
.ui-refresh a.cke_dialog_ui_button_ok:hover {
    background: var(--primary-hover, var(--primary)) !important;
    border-color: var(--primary-hover, var(--primary)) !important;
    filter: brightness(1.05);
}
.ui-refresh a.cke_dialog_ui_button_ok:focus,
.ui-refresh a.cke_dialog_ui_button_ok:active {
    border-color: var(--primary) !important;
    box-shadow: 0 0 0 3px var(--primary-ring) !important;
}
.ui-refresh a.cke_dialog_ui_button_ok.cke_disabled {
    background: var(--line) !important;
    border-color: var(--line) !important;
    opacity: 0.7;
}

/* Browse-server / "Upload" buttons (link dialog) — keep neutral */
.ui-refresh a.cke_dialog_ui_button.cke_dialog_image_browse {
    background: var(--card) !important;
    color: var(--ink) !important;
}

/* Backgrounds inside dialog (selectors used by some plugins) */
.ui-refresh .cke_dialog .cke_light_background {
    background-color: var(--paper-2) !important;
}

/* ---------- CKEditor "Modal as Drawer" ----------
   Two CKEditor flows (Link Settings via swoogo_link plugin, and All Merge
   Fields via mergefield plugin) load their content through App.loadModal(),
   which produces a Bootstrap .modal with an iframe. Both pass the shared
   class .modal-cke-drawer to opt into right-side-drawer styling.

   Bootstrap's .modal.fade applies `opacity: 0` + a translateY transform on
   .modal-dialog; we override with translateX so the panel slides in from
   the right edge. The modal-content becomes a flex column so the iframe
   fills the height between header and (hidden) footer. */
.ui-refresh .modal.modal-cke-drawer {
    padding: 0 !important;
}
.ui-refresh .modal.modal-cke-drawer .modal-dialog {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: auto;
    margin: 0;
    width: 560px;
    max-width: 100vw;
    height: 100vh;
    transform: translateX(100%);
    transition: transform 0.25s ease;
}
.ui-refresh .modal.modal-cke-drawer.fade .modal-dialog {
    transform: translateX(100%);
}
.ui-refresh .modal.modal-cke-drawer.in .modal-dialog,
.ui-refresh .modal.modal-cke-drawer.fade.in .modal-dialog {
    transform: translateX(0);
}
.ui-refresh .modal.modal-cke-drawer .modal-content {
    height: 100vh;
    border-radius: 0;
    border: none;
    border-left: 1px solid var(--line);
    box-shadow: -4px 0 24px rgba(0, 0, 0, 0.18);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.ui-refresh .modal.modal-cke-drawer .modal-header {
    flex: 0 0 auto;
}
/* The iframe + skeleton are wrapped in .cke-drawer-body so the skeleton can
   sit absolutely positioned over the iframe slot, fading out once the iframe
   content has loaded. */
.ui-refresh .modal.modal-cke-drawer .cke-drawer-body {
    flex: 1 1 auto;
    position: relative;
    overflow: hidden;
    background: var(--card);
}
.ui-refresh .modal.modal-cke-drawer .cke-drawer-body iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100% !important;
    border: 0;
    background: var(--card);
}
.ui-refresh .modal.modal-cke-drawer .modal-footer {
    flex: 0 0 auto;
}

/* Drawer skeleton — sits over the iframe until the load event fires.
   Pulls bg + animation from the existing .skeleton-bar primitive so it
   matches the dashlet/popover skeletons elsewhere in the app. */
.ui-refresh .modal.modal-cke-drawer .cke-drawer-skeleton {
    position: absolute;
    inset: 0;
    z-index: 1;
    background: var(--card);
    padding: var(--space-5) var(--space-6);
    overflow: hidden;
    transition: opacity 0.25s ease;
}
.ui-refresh .modal.modal-cke-drawer.cke-drawer-loaded .cke-drawer-skeleton {
    opacity: 0;
    pointer-events: none;
}
.ui-refresh .modal.modal-cke-drawer .cke-drawer-skeleton-section + .cke-drawer-skeleton-section {
    margin-top: var(--space-6);
}
.ui-refresh .modal.modal-cke-drawer .cke-drawer-skeleton-heading {
    height: 10px;
    width: 35%;
    margin-bottom: var(--space-4);
}
.ui-refresh .modal.modal-cke-drawer .cke-drawer-skeleton-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) 0;
    border-bottom: 1px solid var(--line);
}
.ui-refresh .modal.modal-cke-drawer .cke-drawer-skeleton-row:last-child {
    border-bottom: none;
}
.ui-refresh .modal.modal-cke-drawer .cke-drawer-skeleton-row .skeleton-bar {
    height: 14px;
}
.ui-refresh .modal.modal-cke-drawer .cke-drawer-skeleton-row .skeleton-bar:first-child {
    flex: 0 0 40%;
}
.ui-refresh .modal.modal-cke-drawer .cke-drawer-skeleton-row .skeleton-bar:last-child {
    flex: 1 1 auto;
    max-width: 55%;
}
/* Vary the row widths slightly so the skeleton reads as content rather
   than a perfect grid. */
.ui-refresh .modal.modal-cke-drawer .cke-drawer-skeleton-row:nth-child(2n) .skeleton-bar:first-child {
    flex-basis: 32%;
}
.ui-refresh .modal.modal-cke-drawer .cke-drawer-skeleton-row:nth-child(3n) .skeleton-bar:first-child {
    flex-basis: 48%;
}
.ui-refresh .modal.modal-cke-drawer .cke-drawer-skeleton-row:nth-child(2n) .skeleton-bar:last-child {
    max-width: 50%;
}
.ui-refresh .modal.modal-cke-drawer .cke-drawer-skeleton-row:nth-child(3n) .skeleton-bar:last-child {
    max-width: 60%;
}

/* ---------- All Merge Fields drawer — interior styling ----------
   Iframe content is the merge-fields.php view. Tighten its presentation:
   monospace token chips, cleaner section headings, comfortable row spacing.
   Tokens auto-flip with the active surface mode, inherited into the iframe. */
.ui-refresh .merge-fields-drawer {
    padding: var(--space-5) var(--space-6);
    max-width: 100%;
    color: var(--ink);
}
.ui-refresh .merge-fields-drawer .panel-group {
    margin-bottom: 0;
}
.ui-refresh .merge-fields-drawer .panel-default {
    border: none !important;
    box-shadow: none !important;
    background: transparent !important;
    margin-bottom: var(--space-2);
}
.ui-refresh .merge-fields-drawer .panel-heading {
    background: transparent;
    border: none;
    padding: var(--space-3) 0 var(--space-2);
    cursor: default;
}
.ui-refresh .merge-fields-drawer .panel-title {
    font-size: var(--font-size-xs);
    font-weight: 600;
    color: var(--ink-2);
    margin: 0;
}
.ui-refresh .merge-fields-drawer .panel-title a {
    color: inherit;
    text-decoration: none;
    display: flex;
    align-items: center;
    gap: var(--space-2);
}
.ui-refresh .merge-fields-drawer .panel-title a i {
    color: var(--muted);
    font-size: 0.85em;
}
.ui-refresh .merge-fields-drawer .panel-body {
    padding: 0;
    background: transparent;
}
.ui-refresh .merge-fields-drawer .panel-body p.header {
    font-size: var(--font-size-xs);
    font-weight: 500;
    color: var(--muted);
    margin: var(--space-4) 0 var(--space-2);
    padding-bottom: var(--space-1);
    border-bottom: 1px solid var(--line);
}
.ui-refresh .merge-fields-drawer .panel-body p.header:first-child {
    margin-top: 0;
}
.ui-refresh .merge-fields-drawer table.table {
    margin-bottom: 0;
    background: transparent;
}
.ui-refresh .merge-fields-drawer table.table-striped > tbody > tr:nth-of-type(odd) > td {
    background: transparent;
}
.ui-refresh .merge-fields-drawer table.table > tbody > tr > td {
    border: none;
    padding: var(--space-2) 0;
    vertical-align: middle;
}
.ui-refresh .merge-fields-drawer table.table > tbody > tr {
    border-bottom: 1px solid var(--line);
}
.ui-refresh .merge-fields-drawer table.table > tbody > tr:last-child {
    border-bottom: none;
}
.ui-refresh .merge-fields-drawer table.table > tbody > tr > td:first-child {
    color: var(--ink-2);
    font-size: var(--font-size-sm);
    width: 45% !important;
    padding-right: var(--space-3);
}
.ui-refresh .merge-fields-drawer table.table > tbody > tr > td:last-child {
    font-family: var(--font-mono, ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace);
    font-size: 12px;
    color: var(--ink);
    word-break: break-all;
}
.ui-refresh .merge-fields-drawer .merge-token-btn {
    display: inline-block;
    background: var(--paper-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 2px var(--space-2);
    color: var(--ink);
    cursor: pointer;
    transition: var(--transition-fast);
    text-align: left;
    font: inherit;
    line-height: 1.4;
    max-width: 100%;
}
.ui-refresh .merge-fields-drawer .merge-token-btn:hover {
    background: var(--primary-tint);
    border-color: var(--primary);
    color: var(--primary);
}
.ui-refresh .merge-fields-drawer .merge-token-btn.copied {
    background: var(--primary);
    border-color: var(--primary);
    color: #fff;
}
.ui-refresh .merge-fields-drawer .merge-fields-intro {
    font-size: var(--font-size-sm);
    color: var(--ink-2);
    margin: 0 0 var(--space-4);
}

.ui-refresh .sp-picker-container {
    border-left: 1px solid var(--border-strong);
}
/* ===========================================================================
   Communicate — visibility choice (audience radio cards + condition builder).
   Shared by Automated / Session / Planner-alert (email/_form.php) and the three
   email-blast forms (_reg / _invitation / _cfs) via common/_visibility_choice.php.
   Modeled on the Apply-to-Attend "Who Applies" cards, rebuilt on ui-refresh tokens
   so it follows all four surface tones. See .ai/specs/visibility-choice-pattern.md.
   =========================================================================== */
.ui-refresh .comm-vis-cards {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}
.ui-refresh .comm-vis-card,
.ui-refresh .form-group .comm-vis-card {
    display: grid;
    width: auto;
    grid-template-columns: auto 1fr auto;
    gap: var(--space-3);
    align-items: flex-start;
    margin: 0;
    /* !important defeats the aggressive `.new-form … > label` padding-left reset
       (specificity 0,7,2) that otherwise flattens the card's left padding to 0. */
    padding: var(--space-3) var(--space-4) !important;
    border: 1px solid var(--line-2);
    border-radius: var(--radius-lg);
    background: var(--card);
    cursor: pointer;
    font-weight: var(--font-weight-medium);
    transition: border-color var(--transition-fast), background var(--transition-fast);
}
.ui-refresh .comm-vis-card:hover {
    border-color: var(--muted-2);
}
.ui-refresh .comm-vis-input {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
    margin: 0;
    pointer-events: none;
}
.ui-refresh .comm-vis-input:focus-visible + .comm-vis-card {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}
.ui-refresh .comm-vis-radio {
    width: 16px;
    height: 16px;
    margin-top: 2px;
    border-radius: 50%;
    border: 1.5px solid var(--line-2);
    background: var(--card);
    position: relative;
    flex: none;
    transition: border-color var(--transition-fast);
}
/* Selected tint is driven by the choice-level .is-open state class (toggled in JS),
   targeting the matching card via a descendant combinator. We deliberately avoid
   `:checked +` / `:has(:checked)`: Blink drops their style invalidation when any
   `:has(:checked)` rule exists elsewhere on the page, so the tint would not repaint
   when the radio changes. A container-class descendant rule invalidates reliably. */
.ui-refresh .comm-vis-choice:not(.is-open) .comm-vis-card--default,
.ui-refresh .comm-vis-choice.is-open .comm-vis-card--conditions {
    border-color: var(--accent-primary);
    background: var(--accent-subtle);
}
.ui-refresh .comm-vis-choice:not(.is-open) .comm-vis-card--default .comm-vis-radio,
.ui-refresh .comm-vis-choice.is-open .comm-vis-card--conditions .comm-vis-radio {
    border-color: var(--accent-primary);
}
.ui-refresh .comm-vis-choice:not(.is-open) .comm-vis-card--default .comm-vis-radio::after,
.ui-refresh .comm-vis-choice.is-open .comm-vis-card--conditions .comm-vis-radio::after {
    content: "";
    position: absolute;
    inset: 3px;
    border-radius: 50%;
    background: var(--accent-primary);
}
.ui-refresh .comm-vis-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}
.ui-refresh .comm-vis-title {
    font-weight: var(--font-weight-semibold);
    font-size: var(--font-size-base);
    color: var(--ink);
}
.ui-refresh .comm-vis-desc {
    font-weight: var(--font-weight-normal);
    font-size: var(--font-size-sm);
    line-height: 1.45;
    color: var(--muted);
}
/* The "Default" pill is the shared .badge component — this is purely the card
   grid placing it, not badge styling. The card is align-items: flex-start so the
   radio and text stack from the top; the badge is a single line and reads better
   centred against the two-line title + description. */
.ui-refresh .comm-vis-card > .badge {
    align-self: center;
}
/* Condition builder is revealed only when the "specific conditions" card (value 0)
   is selected — pure CSS, no JS toggle. */
.ui-refresh .comm-vis-builder {
    display: none;
    margin-top: var(--space-5);
}
.ui-refresh .comm-vis-choice.is-open .comm-vis-builder {
    display: block;
}
.ui-refresh .comm-vis-builder-head {
    margin: 0 0 var(--space-3);
    padding-bottom: var(--space-3);
    border-bottom: 1px solid var(--line);
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
    color: var(--ink);
}
/* The choice is frequently the LAST section on the page (blasts, planner alerts).
   Guarantee breathing room below so the builder/cards aren't cramped against the
   footer or page edge. Page-level only — an AjaxDrawer sets its own bottom
   spacing, so the extra margin just leaves a gap above the drawer footer. */
.ui-refresh .comm-vis-choice:not(.ajax-drawer *) {
    margin-bottom: var(--space-6);
}

/* Merge-field text components (TranslationInput WITH merge fields — e.g. Subject /
   Preview Text on email-designer/settings). The widget stacks a selector row
   (.merge-fields-dropdowns), the text field (.merge-field-text-input), then a
   Translate footer (.merge-fields-footer). backend.css rounds BOTH rows to 8px on
   every corner but forces the middle field to radius 0, so the group reads as three
   mismatched boxes with square notches between them — and active-form.css only fixes
   the textarea case, leaving the Subject <input> square. Coordinate the radii so the
   group is one cohesive card: round only the top of the first piece and the bottom of
   the last piece, flatten everything in between. (Corner-level rounding rather than a
   container overflow clip, because .merge-field-container is display:table where
   overflow:hidden doesn't reliably clip.) */
.ui-refresh .merge-field-container > .merge-fields-dropdowns {
    border-bottom-left-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
    border-top-left-radius: var(--radius-lg) !important;
    border-top-right-radius: var(--radius-lg) !important;
}
.ui-refresh .merge-field-container > .merge-field-text-input {
    border-radius: 0 !important;
}
.ui-refresh .merge-field-container > .merge-fields-footer {
    border-top-left-radius: 0 !important;
    border-top-right-radius: 0 !important;
    border-bottom-left-radius: var(--radius-lg) !important;
    border-bottom-right-radius: var(--radius-lg) !important;
}

/* ===== Setup list pages (Registrant types, Sessions, Sponsors, etc.) ===== */

/* Subpage head: a thin row at the top of a list/setup page with the
   breadcrumb chain on the left and primary actions on the right. Replaces
   the legacy split between the layout's #breadcrumb-container and the
   floating ButtonContainer for ui-refresh event pages. */
.ui-refresh .subpage-head {
    display: flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    margin-top: var(--space-6);
    margin-bottom: var(--space-4);
    /* 40px matches the standard .btn height — so the row keeps the same
       vertical footprint whether or not its right-side actions are inline
       (reg-types / reg-form) or position-fixed (event settings). Without
       this, event settings' row collapses to 36px and the breadcrumb chips
       sit 2px higher than they do on the other two pages. */
    min-height: 40px;
}
/* Sticky subpage header for setup list pages (Registrant Types, Sessions,
   Speakers, Tracks, Packages, Locations, Hotels, Sponsors, Languages). This
   mirrors the Registration Form's sticky header exactly — the reg-form is the
   template (its own copy lives in reg-form.css under `:has(.rf-page)`). The
   whole breadcrumb row (crumbs + actions) sticks to the top of #main-content
   so all actions stay reachable while the list scrolls.

   top: 0 — #main-content's padding-top is 0 (layout.css) and it is the scroll
   container, so the bar flush-sticks under the 60px top-nav. The margin→padding
   conversion keeps rest geometry identical (24px above + 40px bar + 16px below)
   while giving the opaque background an approach gap to cover as it locks in.
   `.is-stuck` is toggled by subpage-head-sticky.js via a sentinel + IO — the
   same mechanism the reg-form uses. Event Settings is excluded (`.list-page`
   only) since it gets its own `:has(.event-settings-doc)` sticky rule below.
   `.account-form` is included so account-level form pages (Webhooks, Basic
   Details, Policies) get the same sticky breadcrumb bar as list pages. */
.ui-refresh #main-content:has(.list-page, .account-form) .subpage-head,
.ui-refresh #main-content:has(.event-settings-doc) .subpage-head {
    position: sticky;
    top: 0;
    z-index: 10;
    background: var(--bg-page);
    margin-top: 0;
    margin-bottom: 0;
    padding-top: var(--space-6);
    padding-bottom: var(--space-4);
    /* The margin→padding conversion above eats the base rule's min-height: 40px
       (border-box), letting the row collapse to the crumbs' natural ~37px when
       the actions slot is empty (e.g. App Info with no CTA). Re-add the padding
       on top so the content row stays 40px, matching the static doc-page head. */
    min-height: calc(40px + var(--space-6) + var(--space-4));
    border-bottom: 1px solid transparent;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
/* Stuck state: bottom border + shadow appear once the bar has left its in-flow
   position. --shadow-subpage-stuck flips per surface (the blue-tinted light
   shadow is invisible on dark paper), so one declaration covers both. */
.ui-refresh #main-content:has(.list-page, .account-form) .subpage-head.is-stuck {
    border-bottom-color: var(--panel-border);
    box-shadow: var(--shadow-subpage-stuck);
}
/* Natural-cased crumbs with "/" separators. Linked crumbs get an underline
   on hover so navigable ancestors read as links, not static labels. Matches
   the same treatment applied to the legacy `.ui-refresh .breadcrumb` and
   event-wizard header crumbs elsewhere in ui-refresh. */
.ui-refresh .subpage-head .crumbs {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-family: var(--font-family);
    font-size: 13px;
    font-weight: var(--font-weight-medium);
    color: var(--muted);
    min-width: 0;
    min-height: 36px;
}
.ui-refresh .subpage-head .crumbs a {
    color: inherit;
    text-decoration: none;
    transition: color var(--transition-fast);
}
.ui-refresh .subpage-head .crumbs a:hover {
    color: var(--ink);
    text-decoration: underline;
    text-underline-offset: 3px;
}
.ui-refresh .subpage-head .crumbs .current {
    color: var(--ink);
    font-weight: var(--font-weight-semibold);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ui-refresh .subpage-head .crumbs .sep {
    color: var(--muted-2);
    flex-shrink: 0;
}
/* Small pill for a status/link badge next to a crumb's label — e.g. the
   "linked to repository" indicator on theme/update. Modeled on .badge.primary
   (layout.css) / #event-navbar-details .event-status-pill. */
.ui-refresh .subpage-head .crumbs .crumb-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    flex-shrink: 0;
    font-size: 11px;
    font-weight: var(--font-weight-medium);
    line-height: 1;
    padding: 3px var(--space-2);
    border-radius: var(--radius-full);
    background: var(--accent-subtle);
    color: var(--accent-hover);
    border: 1px solid var(--accent-muted);
}
.ui-refresh .subpage-head .crumbs .crumb-badge i {
    font-size: 11px;
}
.ui-refresh .subpage-head .setup-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-shrink: 0;
}
/* Normalize the height/box-model of every action in the row so a plain
   .btn-default anchor (Other Discount Settings), a submit .btn-primary (Save),
   and a .btn-group dropdown toggle (Add Codes, Add Field) all line up at
   --btn-height. The base .btn rule sets height but not box-sizing, so a
   bordered anchor can render 1–2px shorter than its neighbors; pin box-sizing
   + height here. This also beats active-form.css's and layout.css's
   `.new-form .btn.dropdown-toggle { height: ... }` (3-class selectors, tied
   with `.ui-refresh .btn:not(.breadcrumb-btn)` below) which otherwise treat
   any dropdown-toggle inside a .new-form as an input-sized control. */
.ui-refresh .subpage-head .setup-actions > .btn,
.ui-refresh .subpage-head .setup-actions > .btn-group > .btn {
    box-sizing: border-box;
    height: var(--btn-height);
}
/* ButtonDropdown toggles (Add Codes, Add Payment Plan, Add Payment Method) in
   the subpage head must match the dim neutral outline of the plain .btn-default
   buttons beside them. A legacy .btn-default border rule in form context wins
   over the base ui-refresh .btn-default, leaving these with a heavier outline;
   pin them to the dim --input-border here. */
.ui-refresh .subpage-head .setup-actions .btn-group > .btn.dropdown-toggle {
    border: 1px solid var(--input-border);
}
.ui-refresh .subpage-head .setup-actions .btn-group > .btn.dropdown-toggle:hover,
.ui-refresh .subpage-head .setup-actions .btn-group.open > .btn.dropdown-toggle {
    border-color: var(--input-border);
}
/* Brand-outlined secondary action in the subpage head. Reorder inherits
   the active accent for border so it reads as a first-class action next
   to the filled Save primary, instead of the muted neutral .btn-default
   chrome. Hover fills with the accent for a confident press affordance.
   Create keeps the default .btn-default chrome at rest. */
.ui-refresh .reg-type-reorder-btn:hover,
.ui-refresh .reg-type-reorder-btn:focus,
.ui-refresh .reg-type-create-btn:hover,
.ui-refresh .reg-type-create-btn:focus {
    background: var(--accent-primary) !important;
    border-color: var(--accent-primary);
    color: var(--fg-on-accent);
}

/* Save button in-flight state. The in-flight HTML is set by reg-type-list.js
   on form beforeSubmit and reverts automatically when the Pjax reload
   re-renders the button. The class hook dims the filled primary chip and
   keeps the spinner aligned with the label. !important is needed to beat
   the .btn-primary base rule's own !important declarations. */
.ui-refresh .reg-type-save-btn.is-saving,
.ui-refresh .fee-save-btn.is-saving,
.ui-refresh .pm-save-btn.is-saving,
.ui-refresh .pp-save-btn.is-saving,
.ui-refresh .discount-save-btn.is-saving,
.ui-refresh .sponsor-mass-update-save-btn.is-saving,
.ui-refresh .session-save-btn.is-saving,
.ui-refresh .registrant-hotel-mass-update-save-btn.is-saving {
    background: var(--bg-page) !important;
    border-color: var(--border-default) !important;
    color: var(--muted) !important;
    cursor: progress;
    pointer-events: none;
}
.ui-refresh .reg-type-save-btn .fa-spinner,
.ui-refresh .fee-save-btn .fa-spinner,
.ui-refresh .pm-save-btn .fa-spinner,
.ui-refresh .pp-save-btn .fa-spinner,
.ui-refresh .discount-save-btn .fa-spinner,
.ui-refresh .sponsor-mass-update-save-btn .fa-spinner,
.ui-refresh .session-save-btn .fa-spinner,
.ui-refresh .registrant-hotel-mass-update-save-btn .fa-spinner {
    margin-right: 4px;
    color: var(--muted-2);
}

/* Save indicator (used when autosave is wired up; hidden by default since
   reg-types still uses an explicit Save button). Green dot + muted label. */
.ui-refresh .subpage-head .save-indicator {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    font-size: 12px;
    color: var(--color-success);
    padding: 0 var(--space-2);
}
.ui-refresh .subpage-head .save-indicator .dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--color-success);
    display: inline-block;
}

.ui-refresh .list-page {
    padding: var(--space-2) 0;
    width: min(var(--doc-max-width), 100% - (var(--doc-gutter) * 2));
    max-width: var(--doc-max-width);
    margin-left: auto;
    margin-right: auto;
}
/* Session-flash banner (new-admin.php's common/_flash-inline-alerts), rendered
   above every page's own content: matches .list-page's own width formula so it
   lines up with the content below instead of stretching the full #main-content
   width — its previous wrapper used undefined `.mx-auto`/`.p-0` utility classes
   (neither exists in this codebase), so nothing constrained or centered it. */
.ui-refresh .flash-inline-alerts-wrap {
    width: min(var(--doc-max-width), 100% - (var(--doc-gutter) * 2));
    max-width: var(--doc-max-width);
    margin: var(--space-5) auto 0;
}
.ui-refresh .flash-inline-alerts-wrap:empty {
    margin: 0;
}
/* Report-grid pages (registrant, session, sponsor, etc.) render very wide,
   horizontally-scrolling tables — the shared --doc-max-width (1240px) above
   is sized for prose/settings pages and cramps a wide grid badly. Added only
   by _report-grid-page-open.php: drop the width cap entirely and use a
   simple edge margin instead of the centered-column treatment. */
.ui-refresh .list-page.list-page--wide {
    width: auto;
    max-width: none;
    margin-left: var(--doc-gutter);
    margin-right: var(--doc-gutter);
}
/* "With Checked Records" (ReportHelper::massGridActionButton) — legacy
   ajax-datatables.css pulls its `.grid-action` up with `margin-top:-50px` to
   overlap the bottom edge of the old narrow, non-scrolling table. Against
   our wide/scrolling .comm-report layout that overlap instead floats the
   button on top of the horizontal scrollbar (or, with fewer than 10 rows,
   on top of the table body — see communicate.js's paging-row hide), and also
   cancelled the button's own height in the flex row, collapsing `.bottom-row`
   to 0px. Reset it to a normal gap. Not scoped to `.list-page--wide`: invitees
   hand-rolls a plain `.list-page` for this button (a sibling of the Pjax grid,
   not nested inside it) and needs the same reset — plain `.list-page` also
   matches wide pages, since they carry both classes. */
.ui-refresh .list-page .bottom-row .grid-action {
    margin-top: 0;
}
.ui-refresh .list-page .bottom-row {
    margin-top: var(--space-4);
}
/* Contacts renders this button INSIDE the grid's own bottom layout row now
   (AjaxDataTables::$bottomLeftHtml, wrapped in .dt-mass-actions — see
   contact/_grid.php), sharing the row with pagination and page-length. That
   row is `align-items: center`, so the space-4 top margin above displaces
   the button's centred position by half its value instead of just adding a
   gap below a standalone block. Cancel it here; .dt-mass-actions only ever
   comes from that one caller. */
.ui-refresh .dt-mass-actions .bottom-row {
    margin-top: 0;
}
/* Mirror the centering on the head/alert siblings so the breadcrumb +
   action buttons line up with the list below; the full-bleed wrapper goes
   viewport-wide (see layout.css) so the scrollbar sits at the window edge.
   Pjax wraps everything in #reg-type-form > form, so descendant combinators
   (not direct-child) are required to find the head and alerts.

   Scoped to #main-content-inner (the column wrapper added in
   layouts/event.php): the layout's own Alert::widget() is a direct child of
   that wrapper, so the `> .alert` selector must hang off it.

   Event Settings (`.event-settings-doc`) is included so its `.subpage-head`
   centers to the same width — without this, the breadcrumb chips would sit
   at the viewport's left edge while the doc itself centers, leaving them
   visually disconnected from the content column below.
   `.theme-card-grid` is included for the same reason: `.themes-page-content`
   uses the same horizontal centering formula, so the breadcrumbs must match. */
.ui-refresh #main-content-inner:has(.list-page) .subpage-head,
.ui-refresh #main-content-inner:has(.list-page) > .alert,
.ui-refresh #main-content-inner:has(.list-page) form > .alert,
.ui-refresh #main-content-inner:has(.event-settings-doc) .subpage-head,
.ui-refresh #main-content-inner:has(.event-settings-doc) > .alert,
.ui-refresh #main-content-inner:has(.event-settings-doc) form > .alert,
.ui-refresh #main-content:has(.list-page) .subpage-head,
.ui-refresh #main-content:has(.list-page) > .alert,
.ui-refresh #main-content:has(.list-page) form > .alert,
.ui-refresh #main-content:has(.event-settings-doc) .subpage-head,
.ui-refresh #main-content:has(.event-settings-doc) > .alert,
.ui-refresh #main-content:has(.event-settings-doc) form > .alert,
.ui-refresh #main-content:has(.theme-card-grid) .subpage-head,
.ui-refresh #main-content:has(.lang-card-grid) .subpage-head,
.ui-refresh #main-content:has(.lang-card-grid) > .alert,
.ui-refresh #main-content:has(.lang-card-grid) form > .alert,
.ui-refresh #main-content-inner:has(.list-page) .feature-toggle-banner--status {
    width: min(var(--doc-max-width), 100% - (var(--doc-gutter) * 2));
    max-width: var(--doc-max-width);
    margin-left: auto;
    margin-right: auto;
}
/* Account form/view pages center their content to a consistent column.
   IMPORTANT: this is scoped to `.account-form` (the body) and `:has(.account-form)`
   (the chrome), NOT `form:has(.account-form)` — a form-ancestor selector silently
   failed on non-form pages (e.g. font/view has no outer <form>), leaving them
   full width. The subpage-head crumbs/Save and the list-intro are centered to the
   same column so every account form/view page shares one width, form or not. */
.ui-refresh #content-container.account #main-content:has(.account-form) .subpage-head,
.ui-refresh #content-container.account #main-content:has(.account-form) .list-intro,
.ui-refresh #content-container.account #main-content .account-form {
    width: min(900px, 100% - (var(--doc-gutter) * 2));
    max-width: 900px;
    margin-left: auto;
    margin-right: auto;
}
/* Wider variant (custom-domain, js-snippet, data-list, font/view): opt into the
   list-page --doc-max-width boundary. The chrome widens with the body so crumbs
   and content keep the same edges. */
.ui-refresh #content-container.account #main-content:has(.account-form--wide) .subpage-head,
.ui-refresh #content-container.account #main-content:has(.account-form--wide) .list-intro,
.ui-refresh #content-container.account #main-content .account-form--wide {
    width: min(var(--doc-max-width), 100% - (var(--doc-gutter) * 2));
    max-width: var(--doc-max-width);
}
/* Report-grid pages: the subpage-head/breadcrumb bar (and any flash Alert
   rendered before .list-page, e.g. registrant/index.php's Alert::widget())
   above .list-page--wide must match its dropped width cap, or they stay
   narrow while the table below goes full width. Covers both ancestor forms
   (#main-content-inner, added by layouts/event.php, and plain #main-content
   on layouts that don't nest it) since report-grid pages appear under both
   depending on layout. `:has(.list-page)` above still matches too
   (list-page--wide keeps the plain `list-page` class) — this rule must come
   after it to win on source order (both are equal specificity). */
.ui-refresh #main-content-inner:has(.list-page--wide) .subpage-head,
.ui-refresh #main-content-inner:has(.list-page--wide) > .alert,
.ui-refresh #main-content-inner:has(.list-page--wide) form > .alert,
.ui-refresh #main-content:has(.list-page--wide) .subpage-head,
.ui-refresh #main-content:has(.list-page--wide) > .alert,
.ui-refresh #main-content:has(.list-page--wide) form > .alert {
    width: auto;
    max-width: none;
    margin-left: var(--doc-gutter);
    margin-right: var(--doc-gutter);
}
/* Reports pages only (#3128): with the secondary-nav narrowed (see the
   `:has(#report-menu-pjax)` rules near #secondary-nav below), the full
   `--doc-gutter` (40px, shared by every other centered page — left as a
   value override here, not a token change) reads as an oversized margin on
   top of the space the narrower nav already gave back, so it's dropped to 0
   for Reports (verified live: no dead strip between nav and grid). The grid
   (`.list-page--wide` above) and the breadcrumb bar this rule targets are two
   independent rules that both read `var(--doc-gutter)`, so they must be kept
   in sync — editing one without the other desyncs the breadcrumb from the
   table again. `body:has(#report-menu-pjax)` reaches across from the
   (sibling) secondary-nav since #content-container and #secondary-nav share
   no closer common ancestor. */
body.ui-refresh:has(#report-menu-pjax) #content-container .list-page.list-page--wide {
    margin-left: 0;
}
body.ui-refresh:has(#report-menu-pjax) #main-content-inner:has(.list-page--wide) .subpage-head,
body.ui-refresh:has(#report-menu-pjax) #main-content-inner:has(.list-page--wide) > .alert,
body.ui-refresh:has(#report-menu-pjax) #main-content-inner:has(.list-page--wide) form > .alert,
body.ui-refresh:has(#report-menu-pjax) #main-content:has(.list-page--wide) .subpage-head,
body.ui-refresh:has(#report-menu-pjax) #main-content:has(.list-page--wide) > .alert,
body.ui-refresh:has(#report-menu-pjax) #main-content:has(.list-page--wide) form > .alert {
    margin-left: 0;
}
/* With the gutter above at 0, the remaining gap was `#main-content`'s own
   base `padding: 20px` (layout.css) — foundational spacing every event page
   uses, not something #3128 introduced (confirmed live by disabling the
   declaration in DevTools). Overrides only `padding-left`, leaving the
   `padding: 20px` shorthand's other three sides alone. Scoped to Reports via
   the same `:has(#report-menu-pjax)` gate, so this is a deliberate exception
   making Reports sit tighter than every other secondary-nav page — confirm
   with design before extending it elsewhere. */
body.ui-refresh:has(#report-menu-pjax) #main-content {
    padding-left: 0;
}
/* Account hub/list pages with a subpage-head but no account-form (from master):
   center the head to the same --doc-max-width column. Excludes
   `.list-page--wide` pages (`:not(:has(.list-page--wide))`) — this selector
   is 2 IDs + 5 classes, higher specificity than the wide-width rule above
   (1 ID + 3 classes) despite coming later in the file, so without the
   exclusion it would win on both specificity and source order and re-center
   a report-grid page's breadcrumb bar to --doc-max-width while the grid
   below it stays uncapped. */
.ui-refresh #content-container.account #main-content:has(.subpage-head):not(:has(.account-form)):not(:has(.list-page--wide)) > .subpage-head {
    width: min(var(--doc-max-width), 100% - (var(--doc-gutter) * 2));
    max-width: var(--doc-max-width);
    margin-left: auto;
    margin-right: auto;
}

/* ── Event Types cards (ui-refresh) ─────────────────────────────
   The page ships its own event-type.css built on local --et-* hex tokens
   that don't flip for surface mode. In ui-refresh we re-point the card,
   chip, pill and button styles to semantic tokens so they match the system
   and flip across all four surfaces. Legacy accounts keep event-type.css.
   The per-card accent bar/swatch keep their inline --card-accent (user color). */
.ui-refresh .et-grid {
    gap: var(--space-4);
}
.ui-refresh .et-card {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    padding: var(--space-6);
    gap: var(--space-4);
    box-shadow: var(--sh-sm);
    transition: box-shadow var(--transition-fast), border-color var(--transition-fast);
}
.ui-refresh .et-card:hover {
    border-color: var(--line-2);
    box-shadow: var(--sh-md);
}
.ui-refresh .et-swatch {
    border-radius: var(--radius-sm);
    border-color: var(--line);
}
.ui-refresh .et-card-head h3 {
    font-family: var(--font-family);
    font-size: var(--font-size-lg);
    font-weight: 600;
    color: var(--ink);
}
.ui-refresh .et-all-pill {
    background: var(--success-soft);
    color: var(--success-primary);
    font-family: var(--font-family);
    font-size: var(--font-size-label);
    font-weight: 600;
    border-radius: var(--radius-md);
}
.ui-refresh .et-mod-count {
    font-family: var(--font-family);
    font-size: var(--font-size-xs);
    color: var(--muted);
    margin-bottom: var(--space-2);
}
.ui-refresh .et-chip {
    font-family: var(--font-family);
    font-size: var(--font-size-label);
    font-weight: 500;
    color: var(--accent-hover);
    background: var(--accent-subtle);
    border: 1px solid var(--accent-muted);
    border-radius: var(--radius-sm);
}
.ui-refresh .et-chip.more {
    background: var(--card);
    color: var(--muted);
    border-color: var(--line);
}
.ui-refresh .et-card-foot {
    border-top: 1px solid var(--line);
    padding-top: var(--space-3);
    gap: var(--space-2);
}
.ui-refresh .btn-et-neutral {
    background: var(--paper-2);
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    font-family: var(--font-family);
    font-size: var(--font-size-btn);
    font-weight: 500;
    transition: background var(--transition-fast), border-color var(--transition-fast);
}
.ui-refresh .btn-et-neutral:hover {
    background: var(--line);
    border-color: var(--line-2);
}
.ui-refresh .btn-et-icon {
    border-radius: var(--radius-md);
    color: var(--muted);
    transition: background var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .btn-et-icon:hover {
    background: var(--paper-2);
    color: var(--ink);
}
.ui-refresh .btn-et-icon.danger:hover {
    background: var(--danger-soft);
    color: var(--danger-primary);
}
.ui-refresh .et-count {
    font-family: var(--font-family);
    font-size: var(--font-size-xs);
    color: var(--muted);
}
.ui-refresh .et-count b {
    color: var(--ink);
    font-weight: 600;
}
.ui-refresh .et-no-results {
    font-family: var(--font-family);
    font-size: var(--font-size-sm);
    color: var(--muted);
}

/* ── Event Types "Configure" slideout form (ui-refresh) ─────────
   The create/update form renders inside the legacy iframe sidebar, whose
   body carries .ui-refresh (see layouts/iframe.php). Restyle its etf-* body
   to semantic tokens so it matches the drawer system and flips per surface;
   event-type.css remains the non-ui-refresh fallback. */
/* Form body wrapper (name field → language toggle). The iframe body no longer
   carries a 15px margin, so the fields would otherwise sit flush to the drawer
   edges. Pad here — NOT on the header or footer, which keep their own
   --sidebar-x-padding — so the fields align under the header text. */
.ui-refresh .etf-drawer-body {
    padding: var(--space-4) var(--sidebar-x-padding);
}
.ui-refresh .etf-field {
    margin-bottom: var(--space-5);
}
.ui-refresh .etf-label {
    font-family: var(--font-family);
    font-size: var(--font-size-label);
    font-weight: 600;
    color: var(--ink);
    margin-bottom: var(--space-2);
}
/* Design rule: no uppercase — restyle the section eyebrow in natural casing. */
.ui-refresh .etf-section-label {
    font-family: var(--font-family);
    font-size: var(--font-size-label);
    font-weight: 600;
    text-transform: none;
    letter-spacing: 0;
    color: var(--muted);
    margin: var(--space-6) 0 var(--space-1);
}
.ui-refresh .etf-section-help {
    font-family: var(--font-family);
    font-size: var(--font-size-xs);
    color: var(--muted);
    margin-bottom: var(--space-3);
}
.ui-refresh .etf-mod-count-lbl {
    font-family: var(--font-family);
    font-size: var(--font-size-xs);
    color: var(--muted);
}
.ui-refresh .etf-mod-count-lbl b {
    color: var(--ink);
    font-weight: 600;
}
.ui-refresh .etf-enable-all {
    font-family: var(--font-family);
    font-size: var(--font-size-btn);
    font-weight: 600;
    color: var(--accent-primary);
}
.ui-refresh .etf-mod-row {
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-md);
}
.ui-refresh .etf-mod-row:hover {
    background: var(--paper-2);
}
.ui-refresh .etf-mod-ico {
    border-radius: var(--radius-md);
    background: var(--accent-subtle);
    color: var(--accent-hover);
}
.ui-refresh .etf-mod-text {
    font-family: var(--font-family);
    font-size: var(--font-size-sm);
    font-weight: 500;
    color: var(--ink);
}
.ui-refresh .etf-toggle {
    background: var(--line-2);
}
.ui-refresh .etf-mod-row[data-on="true"] .etf-toggle {
    background: var(--accent-primary);
}

/* The drawer panel-body's --space-3 flex gap stacks on the toolbar's own
   8px margin-bottom (event-type.css) and the first row's top padding,
   floating the module list too far from its "X of N enabled" toolbar.
   Cancel gap + margin so only the row's own padding separates them. */
.ui-refresh .ajax-drawer .etf-mod-toolbar {
    margin-bottom: calc(-1 * var(--space-3));
}
/* Drawer flow (AjaxDrawer): align module-row content with the "X of N
   enabled" toolbar while keeping the hover pill bleeding past the text
   edges — the horizontal padding above stays, the negative margin pulls
   the box back to the panel's text column. */
.ui-refresh .ajax-drawer .etf-mod-row {
    margin: 0 calc(-1 * var(--space-3));
}
/* The drawer rows use a real ToggleSwitch (label.checkbox-toggle); its
   36x20 upsizing shares the drawer switch rules near line 1729. The switch
   is the row's last flex child, so its default 5px bottom margin
   (backend.css .checkbox-toggle) would misalign it vertically in the row. */
.ui-refresh .ajax-drawer .etf-mod-row .checkbox-toggle {
    margin: 0;
}

.ui-refresh .list-intro {
    margin: 0 0 var(--space-4);
}
.ui-refresh .list-title {
    font-family: var(--font-family);
    font-weight: 700;
    font-size: 14.5px;
    letter-spacing: -0.015em;
    line-height: 1.15;
    margin: 0 0 var(--space-2);
    color: var(--ink);
}
.ui-refresh .list-lede {
    font-family: var(--font-family);
    font-size: var(--font-size-sm);
    line-height: 1.55;
    color: var(--muted);
    margin: 0;
}
.ui-refresh :is(.image-library--picker, .file-library--picker) .list-intro--with-actions {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
}
.ui-refresh :is(.image-library--picker, .file-library--picker) .list-intro-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: 0 0 auto;
}
@media (max-width: 767px) {
    .ui-refresh :is(.image-library--picker, .file-library--picker) .list-intro--with-actions {
        align-items: stretch;
        flex-direction: column;
    }
    .ui-refresh :is(.image-library--picker, .file-library--picker) .list-intro-actions {
        flex-wrap: wrap;
    }
}

/* Generic "intro line + primary action" row for a list-table sitting inside a
   plain settings panel (e.g. meeting-location's "These are all the available
   meeting locations." + Create Location) — same left-label/right-action flex
   recipe as .list-intro--with-actions above, but unscoped and single-line
   (align-items: center, not flex-start) since callers here have no stacked
   title+lede, just one line of muted copy. */
.ui-refresh .list-table-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    margin-bottom: var(--space-4);
}
.ui-refresh .list-table-toolbar .list-lede {
    flex: 1 1 auto;
}
@media (max-width: 767px) {
    .ui-refresh .list-table-toolbar {
        align-items: stretch;
        flex-direction: column;
    }
}
/* Wraps a .list-table-toolbar + its table (or .list-empty state) when the
   pair sits inside a plain settings panel alongside other fields — e.g.
   meeting-location's location list, followed by the "Location is a required
   field" toggle. Without this the dashed .list-empty box (no margin of its
   own, since it's also used flush against a page edge elsewhere) butts
   straight up against the next field. */
.ui-refresh .list-table-section {
    margin-bottom: var(--space-6);
}

/* Report-links landing pages (e.g. Hotel Reports index): plain sections on
   the page paper — a titled divider plus a column of link rows, no card
   chrome. Rows keep side padding for the hover pill; the list's negative
   margin pulls the row text flush with the section title. */
.ui-refresh .report-links .list-intro {
    margin-bottom: var(--space-6);
}
.ui-refresh .report-link-section + .report-link-section {
    margin-top: var(--space-8);
}
.ui-refresh .report-link-section__title {
    margin: 0 0 var(--space-2);
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--line);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    letter-spacing: -0.01em;
    color: var(--ink);
}
.ui-refresh .report-link-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin: 0 calc(-1 * var(--space-3));
}
.ui-refresh .report-link-list .report-link {
    display: block;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-md);
    color: var(--ink);
    font-weight: var(--font-weight-medium);
    text-decoration: none;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .report-link-list .report-link:hover,
.ui-refresh .report-link-list .report-link:focus {
    background: var(--state-hover);
    color: var(--ink);
}

/* Hotel Reports index (registrant-hotel/index.php): one .list-table row per
   hotel with the three report links as fixed-width cells, so each report
   type still lines up as a column. The links reuse .lv-report-link. */
.ui-refresh .hotel-reports-table tbody td {
    border-bottom: 1px solid var(--list-table-line);
}
.ui-refresh .hotel-reports-table .lt-th-reports {
    text-align: left;
}
.ui-refresh .hotel-reports-table .lt-cell-report {
    width: 160px;
    white-space: nowrap;
}
.ui-refresh .hotel-reports-table .lt-cell-report .lv-report-link {
    margin-right: 0;
}
.ui-refresh .hotel-reports-table .lt-cell-name .lt-secondary {
    display: block;
    margin-top: 2px;
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-normal);
}
/* Pinned "All hotels" summary row: no tint. On dark surfaces --paper-2 equals
   both the header band and --list-table-line, so a tinted first row fused with
   the header and hid its bottom edge. A --line divider beneath the row keeps
   it separated from the per-hotel rows instead. */
.ui-refresh .hotel-reports-table tr.hotel-reports-all td {
    border-bottom: 1px solid var(--line);
}
/* Header row needs the same fix: .list-table's thead border-bottom uses
   --list-table-line, which equals --paper-2 on dark surfaces (chosen so
   tbody row hairlines stay subtle — see tokens.css) — but the thead th
   background is ALSO --paper-2, so the border is invisible against its own
   background. Use --line instead, same as the border DataTables-based report
   grids (.comm-report table.dataTable) use for their header seam. The
   compound selector outranks the shared `.list-table thead th` rule
   (components.css, below) regardless of source order. */
.ui-refresh .list-table.hotel-reports-table thead th {
    border-bottom-color: var(--line);
}

/* Account fields page: the account-level popup-on-new-question setting sits
   between the intro and the filter/search toolbar. */
.ui-refresh .aq-popup-setting {
    margin: 0 0 var(--space-4);
}
/* Notices that sit between the list intro and the toolbar (e.g. the
   disabled-webhook banner). */
.ui-refresh .list-notice {
    margin: 0 0 var(--space-4);
}
/* Kartik's bs3 checkbox() reuses the field's labelOptions (default class
   `form-label`) for the enclosing <label> and only strips `control-label`
   from it (vendor kartik-v/yii2-widget-activeform ActiveField.php:1114/1143).
   `.form-label` forces uppercase/11px/semibold eyebrow-label styling
   !important in base.css, so this checkbox's label renders as a tiny caps
   eyebrow unless overridden here with matching specificity + !important.
   Values match the established toggle-row choice-label treatment
   (.toggle-group/.toggle-row .checkbox > label below) — 13px/medium/default
   ink, sentence case. */
.ui-refresh .aq-popup-setting .checkbox > label {
    font-size: 13px !important;
    font-weight: var(--font-weight-medium) !important;
    color: var(--fg-default) !important;
    text-transform: none !important;
    letter-spacing: normal !important;
}

/* List-tab-row: the toolbar that sits above the card list. The bottom
   border draws a thin separator before the list. With no tabs in this
   iteration the row only carries the expandable search affordance; the
   spacer pushes it to the right edge. */
.ui-refresh .list-tab-row {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0;
    border-bottom: 1px solid var(--line);
    margin-bottom: var(--space-3);
    padding-bottom: var(--space-2);
}
.ui-refresh .list-tab-row .lt-spacer {
    flex: 1;
}

/* No divider under the toolbar in card view — tighten spacing too */
.ui-refresh [data-list-view="card"] .list-tab-row {
    border-bottom: none;
    margin-bottom: 0;
    padding-bottom: var(--space-1);
}

/* Expandable search: collapsed = icon only; click expands the input. The
   input width animates so the affordance reveals smoothly. The form posts
   GET with the search params, picked up by RegTypeSearch on the server. */
.ui-refresh .search-expand {
    display: flex;
    align-items: center;
    position: relative;
    height: 30px;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    transition: background var(--transition-fast),
                border-color var(--transition-fast);
    margin: 0;
}
.ui-refresh .search-expand .search-btn {
    width: 30px;
    height: 30px;
    display: grid;
    place-items: center;
    color: var(--muted);
    background: transparent;
    border: 0;
    cursor: pointer;
    border-radius: var(--radius-sm);
    flex-shrink: 0;
}
.ui-refresh .search-expand .search-btn:hover {
    color: var(--ink);
    background: var(--paper-2);
}
.ui-refresh .search-expand .search-btn i {
    font-size: 14px;
}
.ui-refresh .search-expand .search-input {
    border: 0;
    outline: 0;
    background: transparent;
    width: 0;
    transition: width var(--transition-fast), padding var(--transition-fast);
    padding: 0;
    font-family: var(--font-family);
    font-size: 13px;
    color: var(--ink);
    height: 28px;
    min-width: 0;
}
.ui-refresh .search-expand.expanded {
    background: var(--card);
    border-color: var(--line);
}
.ui-refresh .search-expand.expanded .search-btn {
    color: var(--ink);
    background: transparent;
}
.ui-refresh .search-expand.expanded .search-input {
    width: 220px;
    padding: 0 var(--space-2) 0 0;
}
/* Account toolbars reserve tail room for the clear "X" so the input text never
   slides under it (and the box width stays fixed whether or not the X shows). */
.ui-refresh .list-tab-row--search .search-expand.expanded .search-input {
    padding-right: var(--space-6);
}
/* Space toolbar controls apart (.list-tab-row is gap:0). The lt-spacer still
   collapses the left free space, so this only shows between the right-aligned
   controls (filter select + search boxes). */
.ui-refresh .list-tab-row--search {
    gap: var(--space-2);
}

.ui-refresh .data-list-row-toolbar-actions {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}
/* Native <select> inside a filter popover (e.g. JS Snippets code position) —
   inherits the shared .fp-input framing; just make it read as clickable. */
.ui-refresh .filter-pop select.fp-select {
    cursor: pointer;
}
/* In-input clear "X" for the account search toolbars (data-list, js-snippet,
   translation). Absolutely positioned at the tail so it never adds to the box
   width — the search bar stays the same length with or without an active
   search. Only renders when there's a value; it's an <a> to the unfiltered
   index, so clicking it clears the search with no JS. */
.ui-refresh .search-expand .search-clear {
    position: absolute;
    right: var(--space-1);
    top: 50%;
    transform: translateY(-50%);
    display: grid;
    place-items: center;
    width: 20px;
    height: 20px;
    color: var(--muted);
    background: transparent;
    border: 0;
    border-radius: var(--radius-full);
    cursor: pointer;
    text-decoration: none;
    transition: color var(--transition-fast), background var(--transition-fast);
}
.ui-refresh .search-expand .search-clear:hover {
    color: var(--ink);
    background: var(--paper-2);
}
.ui-refresh .search-expand .search-clear i {
    font-size: 12px;
}

/* Filter icon button (right-side affordance next to the search-expand).
   `.has-filter` adds an accent dot; `.is-open` matches the popover open
   state so the button looks "pressed" while its menu is showing. */
.ui-refresh .filter-toggle {
    position: relative;
    width: 30px;
    height: 30px;
    display: grid;
    place-items: center;
    border: 0;
    background: transparent;
    border-radius: var(--radius-sm);
    color: var(--muted);
    cursor: pointer;
    flex-shrink: 0;
    margin: 0 var(--space-1) 0 var(--space-1);
}
.ui-refresh .filter-toggle:hover {
    background: var(--paper-2);
    color: var(--ink);
}
.ui-refresh .filter-toggle i {
    font-size: 14px;
}
.ui-refresh .filter-toggle.has-filter {
    color: var(--primary-ink);
}
.ui-refresh .filter-toggle.has-filter::after {
    content: "";
    position: absolute;
    top: 5px;
    right: 5px;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--primary);
    border: 1.5px solid var(--card);
}
.ui-refresh .filter-toggle.is-open {
    color: var(--primary-ink);
    background: var(--primary-tint);
}

/* Filter popover anchored to the toggle button. Sections stack vertically
   with hairline dividers; rows are click targets (mutually exclusive within
   each section since the underlying field is boolean). */
/* Position fixed so the popover escapes #main-content's `overflow-y: auto`
   clipping context — without this, a short list (e.g. one reg type) lets
   the popover render below #main-content's visible bottom and clips. JS
   sets `top` / `left` from the toggle button's bounding rect on open. */
.ui-refresh .filter-pop {
    position: fixed;
    box-sizing: border-box;
    overflow-y: auto;
    overscroll-behavior-y: contain;
    /* Above ButtonDropdown containers (e.g. Settings on the speakers tab). */
    z-index: 1100;
    min-width: 240px;
    max-width: 320px;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: var(--space-1);
    display: none;
}
.ui-refresh .filter-pop.is-open {
    display: block;
}
.ui-refresh .filter-pop .fp-section {
    padding: var(--space-2) var(--space-3);
}
.ui-refresh .filter-pop .fp-section + .fp-section {
    border-top: 1px solid var(--line);
}
.ui-refresh .filter-pop .fp-label {
    font-family: var(--font-family);
    font-size: 10px;
    font-weight: var(--font-weight-semibold);
    color: var(--muted);
    margin-bottom: var(--space-2);
}
.ui-refresh .filter-pop .fp-row {
    appearance: none;
    border: 0;
    background: transparent;
    width: 100%;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: 3px 4px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-family: var(--font-family);
    font-size: 13px;
    color: var(--ink);
    text-align: left;
}
.ui-refresh .filter-pop .fp-row:hover {
    background: var(--paper-2);
}
.ui-refresh .filter-pop .fp-row .fp-box {
    width: 16px;
    height: 16px;
    border: 1.5px solid var(--line-2);
    border-radius: 4px;
    display: grid;
    place-items: center;
    background: var(--card);
    flex-shrink: 0;
    color: var(--fg-on-accent);
    font-size: 9px;
}
.ui-refresh .filter-pop .fp-row .fp-box i {
    opacity: 0;
}
.ui-refresh .filter-pop .fp-row.is-on .fp-box {
    background: var(--primary);
    border-color: var(--primary);
}
.ui-refresh .filter-pop .fp-row.is-on .fp-box i {
    opacity: 1;
}
.ui-refresh .filter-pop .fp-row .fp-name {
    flex: 1;
    min-width: 0;
}
.ui-refresh .filter-pop .fp-input {
    width: 100%;
    padding: 6px 10px;
    font-family: var(--font-family);
    font-size: 13px;
    color: var(--ink);
    background: transparent;
    border: 1px solid var(--input-border);
    border-radius: var(--radius-lg);
    outline: none;
    font-variant-numeric: tabular-nums;
}
.ui-refresh .filter-pop .fp-input:focus {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-ring);
}
.ui-refresh .filter-pop .fp-foot {
    position: sticky;
    /* Cover the popup's padding too, so scrolling rows cannot peek through
       the strip below or beside the pinned action buttons. */
    bottom: calc(-1 * var(--space-1));
    z-index: 1;
    background: var(--card);
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) calc(var(--space-3) + var(--space-1)) calc(var(--space-2) + var(--space-1));
    border-top: 1px solid var(--input-border);
    margin-top: var(--space-1);
    margin-right: calc(-1 * var(--space-1));
    margin-bottom: calc(-1 * var(--space-1));
    margin-left: calc(-1 * var(--space-1));
}
.ui-refresh .filter-pop .fp-foot .fp-spacer {
    flex: 1;
}
.ui-refresh .filter-pop .fp-foot .fp-clear {
    appearance: none;
    border: 1px solid var(--input-border);
    background: transparent;
    color: var(--muted);
    font-family: var(--font-family);
    font-size: 12px;
    font-weight: var(--font-weight-semibold);
    cursor: pointer;
    padding: 6px 12px;
    border-radius: var(--radius-sm);
}
.ui-refresh .filter-pop .fp-foot .fp-clear:hover {
    color: var(--ink);
    background: var(--paper-2);
    border-color: var(--muted-2);
}
.ui-refresh .filter-pop .fp-foot .fp-apply {
    appearance: none;
    border: 0;
    background: var(--primary);
    color: var(--fg-on-accent);
    font-family: var(--font-family);
    font-size: 12px;
    font-weight: var(--font-weight-semibold);
    cursor: pointer;
    padding: 6px 12px;
    border-radius: var(--radius-sm);
}
.ui-refresh .filter-pop .fp-foot .fp-apply:hover {
    background: var(--primary-ink);
}

/* ===== Session-row (card-style list item) =====
   A flat row that reads as a card. Layout: [drag][body][toggles][divider][actions].
   `.no-toggles` collapses the toggles and divider columns back to [drag][body][actions].
   The body is two stacked rows: title on top, prop chips below. The whole row is clickable
   — click opens the update drawer. Chip triggers, the drag handle, and action buttons stop
   propagation so they don't trigger the row click. */
.ui-refresh .session-row {
    display: grid;
    grid-template-columns: 18px minmax(0, 1fr) auto auto auto;
    gap: var(--space-3);
    align-items: center;
    padding: var(--space-3);
    background: var(--card);
    border: 1px solid var(--panel-border);
    /* 10px radius is the design spec for this row pattern — sits between
       --radius-lg (8px) and --radius-xl (12px), so it's encoded literally
       rather than rounding to a token. */
    border-radius: 10px;
    margin-bottom: var(--space-2);
    cursor: pointer;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast), background var(--transition-fast);
}
/* Reg-type and other rows that omit the toggle + divider columns. */
.ui-refresh .session-row.no-toggles {
    grid-template-columns: 18px minmax(0, 1fr) auto;
}

/* Rows that never render a real .sr-drag handle (no manual ordering, e.g.
   locations) — narrow the reserved first column like has-avatar already
   does, instead of keeping the full drag-handle width empty. */
.ui-refresh .session-row.no-toggles.no-drag {
    grid-template-columns: minmax(0, 1fr) auto;
}
.ui-refresh .session-row.no-drag {
    grid-template-columns: minmax(0, 1fr) auto auto auto;
}
.ui-refresh .session-row.no-drag .sr-drag-spacer,
.ui-refresh .session-row.no-drag .sr-drag,
.ui-refresh .session-row.no-drag .sr-mark {
    display: none;
}

/* ===== rf-toggle pill buttons (Visible / Admin only) =====
   Typography mirrors the reg-form builder's .rf-toggle so the chip treatment
   reads the same across the two surfaces. Scoped to .session-row so it
   doesn't bleed onto the question builder's own .rf-toggle in reg-form.css. */

.ui-refresh .session-row .rf-toggle {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 9px;
    line-height: 1.4;
    border-radius: var(--radius-full);
    font-family: var(--font-family);
    font-size: 12px;
    font-weight: var(--font-weight-medium);
    letter-spacing: 0;
    text-transform: none;
    color: var(--fg-muted);
    background: var(--bg-muted);
    border: 1px solid var(--border-default);
    cursor: pointer;
    transition: color var(--transition-fast), background var(--transition-fast), border-color var(--transition-fast);
    white-space: nowrap;
    user-select: none;
}

.ui-refresh .session-row .rf-toggle .dot {
    display: none;
}

.ui-refresh .session-row .rf-toggle:hover {
    color: var(--fg-default);
    border-color: var(--border-strong);
}

.ui-refresh .session-row .rf-toggle.on {
    color: var(--accent-on-tint);
    background: var(--accent-subtle);
    border-color: var(--accent-muted);
}

.ui-refresh .session-row .rf-toggle.on:hover {
    border-color: var(--accent-primary);
}

.ui-refresh .session-row button.rf-toggle:disabled {
    cursor: wait;
    opacity: 0.6;
}

.ui-refresh .session-row .rf-toggle.rf-toggle-conditions {
    color: var(--fg-default);
    background: var(--bg-muted);
    border-color: var(--border-strong);
    cursor: help;
}
.ui-refresh .session-row .rf-toggle.rf-toggle-conditions a,
.ui-refresh .session-row .rf-toggle.rf-toggle-conditions a:hover {
    color: inherit;
    text-decoration: none;
    cursor: inherit;
}

/* ===== rf-toggle two-state labels =====
   ToggleChipHelper::labels() emits both the on and off wording; only the
   current one is displayed, so the chip always fits its own text (Admin
   only/Anyone and Available/Unavailable differ enough in length that a
   fixed shared width left visible dead space). Covers both the button
   variant (.on class, card rows) and the label variant
   (:has(input:checked), builders). */
.ui-refresh .rf-toggle .rf-toggle-on {
    display: none;
}
.ui-refresh .rf-toggle.on .rf-toggle-on,
.ui-refresh .rf-toggle:has(input:checked) .rf-toggle-on {
    display: inline;
}
.ui-refresh .rf-toggle.on .rf-toggle-off,
.ui-refresh .rf-toggle:has(input:checked) .rf-toggle-off {
    display: none;
}

/* Container that sits between sr-body and sr-divider */
.ui-refresh .session-row .sr-toggles {
    display: flex;
    gap: 6px;
    align-items: center;
}

/* Vertical rule separating sr-toggles from sr-actions */
.ui-refresh .session-row .sr-divider {
    width: 1px;
    align-self: stretch;
    background: var(--panel-border);
    margin: 6px 2px;
}

/* Hover-reveal: keep the row clean by default, surface toggles + divider +
   actions only when the user is on the row. Matches the .rf-fieldrow pattern
   in reg-form.css — `:has(:focus-visible)` rather than `:focus-within` so
   keyboard tabbing reveals the cluster but a mouse click on a chip doesn't
   pin the reveal on after the cursor leaves the row. */
.ui-refresh .session-row .sr-toggles,
.ui-refresh .session-row .sr-divider,
.ui-refresh .session-row .sr-actions {
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--transition-fast);
}
.ui-refresh .session-row:hover .sr-toggles,
.ui-refresh .session-row:hover .sr-divider,
.ui-refresh .session-row:hover .sr-actions,
.ui-refresh .session-row:has(:focus-visible) .sr-toggles,
.ui-refresh .session-row:has(:focus-visible) .sr-divider,
.ui-refresh .session-row:has(:focus-visible) .sr-actions {
    opacity: 1;
    pointer-events: auto;
}
/* Touch devices have no reliable hover, so keep the cluster visible. */
@media (hover: none), (pointer: coarse) {
    .ui-refresh .session-row .sr-toggles,
    .ui-refresh .session-row .sr-divider,
    .ui-refresh .session-row .sr-actions {
        opacity: 1;
        pointer-events: auto;
    }
}

/* .sr-toggles (and its divider) opt back out of the hover-gate above: a
   toggle that autosaves (badge's Active pill, via badge-list.js) replaces
   its own row's DOM on every click. If the row was only "hovered" via CSS
   :hover — not JS — some browsers don't recompute :hover against the new
   element while the cursor sits still, leaving `opacity:0; pointer-events:
   none` stuck until the mouse actually moves. That reads as the toggle
   "locking" until a full page reload. Keeping it always visible sidesteps
   the whole class of bug regardless of the exact browser behaviour.
   .sr-actions (edit/clone/delete) stays hover-gated — those aren't hidden
   by a stale hover state on click since they navigate/open a dialog rather
   than re-rendering the row in place. */
.ui-refresh .session-row .sr-toggles,
.ui-refresh .session-row .sr-divider {
    opacity: 1;
    pointer-events: auto;
}

.ui-refresh .session-row:hover {
    border-color: var(--line-2);
    box-shadow: var(--shadow-sm);
}
.ui-refresh .session-row.is-selected {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-ring);
}

/* Drag handle: hidden by default, fades in on row hover so rows look clean
   at rest. Uses two stacked colons to read as a 6-dot grip. */
.ui-refresh .session-row .sr-drag {
    color: var(--muted-2);
    font-family: var(--font-mono);
    font-size: 13px;
    user-select: none;
    cursor: grab;
    opacity: 0;
    transition: opacity var(--transition-fast);
    line-height: 1;
    justify-self: center;
}
.ui-refresh .session-row .sr-drag:active {
    cursor: grabbing;
}
.ui-refresh .session-row:hover .sr-drag,
.ui-refresh .session-row.is-selected .sr-drag {
    opacity: 1;
}

/* Body holds title + prop chips. The title row uses `align-items:center`
   so the editable input/text aligns to the chips below. */
.ui-refresh .session-row .sr-body {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    min-width: 0;
}
.ui-refresh .session-row .sr-title {
    font-family: var(--font-family);
    font-size: 15px;
    font-weight: 600;
    letter-spacing: -0.005em;
    color: var(--ink);
    margin: 0;
    align-self: flex-start;
}
/* When the title is an editable input (RegType name etc), strip the form
   chrome so it reads like static text until focused. */
.ui-refresh .session-row .sr-title-input {
    font-family: var(--font-family);
    font-size: 15px;
    font-weight: 600;
    letter-spacing: -0.005em;
    color: var(--ink);
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    /* Bootstrap's legacy .form-control rule applies an inset
       `box-shadow: 0 1px 1px rgba(0,0,0,.075)`, which paints a hairline at
       the top edge of the input — visible against the card surface as a
       faint horizontal line above the title. Strip it; the focused state
       below restores a focus-ring shadow. */
    box-shadow: none;
    padding: var(--space-1) var(--space-2);
    margin: 0;
    width: 100%;
    max-width: 480px;
    transition: background var(--transition-fast), border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.ui-refresh .session-row .sr-title-input:hover {
    background: var(--paper-2);
}
.ui-refresh .session-row .sr-title-input:focus {
    background: var(--card);
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-ring);
    outline: none;
}

/* Prop chips row: tight cluster of read-only chips and small inline
   inputs, all styled to share the same chip silhouette. */
.ui-refresh .session-row .sr-props {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    align-items: center;
}

/* Property chips inside the row body. Each chip is interactive — click
   opens a popover that edits one attribute inline (capacity gets an input
   popover, visible/admin get menu popovers). Mirrors the mockup's
   .prop / .prop-trigger / .prop-menu pattern, scoped to .sr-props so the
   same chip rules don't leak elsewhere. */
.ui-refresh .session-row .sr-props .prop-trigger {
    position: relative;
    display: inline-flex;
}
.ui-refresh .session-row .sr-props .prop,
.ui-refresh .list-table .lt-cell-prop .sr-props .prop,
.ui-refresh .badges-container .prop {
    appearance: none;
    display: inline-flex;
    align-items: center;
    /* Design-spec values: 7px icon gap, 6×10 padding, 12.5px label.
       None land on the 4px grid / --space-* tokens — encoded as literals. */
    gap: 7px;
    padding: 6px 10px;
    border-radius: var(--radius-lg);
    font-family: var(--font-family), sans-serif;
    font-size: 12.5px;
    font-weight: var(--font-weight-medium);
    color: var(--ink);
    background: var(--card);
    border: 1px solid var(--input-border);
    cursor: pointer;
    font-variant-numeric: tabular-nums;
    transition: background var(--transition-fast),
                border-color var(--transition-fast),
                box-shadow var(--transition-fast);
}
.ui-refresh .badges-container .prop {
    text-decoration: none;
    line-height: 1;
}
.ui-refresh .session-row .sr-props .prop:hover,
.ui-refresh .list-table .lt-cell-prop .sr-props .prop:hover,
.ui-refresh .badges-container .prop:hover {
    border-color: var(--muted-2);
}
.ui-refresh .session-row .sr-props .prop > i,
.ui-refresh .session-row .sr-props .prop > svg,
.ui-refresh .list-table .lt-cell-prop .sr-props .prop > i,
.ui-refresh .list-table .lt-cell-prop .sr-props .prop > svg,
.ui-refresh .badges-container .prop > i,
.ui-refresh .badges-container .prop > svg {
    color: var(--primary);
    /* 13px to match the design spec (chip icons are a hair larger than the
       12px label optic, so they read as visual anchors). */
    font-size: 13px;
    width: 13px;
    height: 13px;
    flex-shrink: 0;
}
.ui-refresh .session-row .sr-props .prop .sub,
.ui-refresh .session-row .sr-props .prop .prop-label,
.ui-refresh .list-table .lt-cell-prop .sr-props .prop .sub,
.ui-refresh .list-table .lt-cell-prop .sr-props .prop .prop-label,
.ui-refresh .badges-container .prop .sub,
.ui-refresh .badges-container .prop .prop-label {
    color: var(--muted);
    font-weight: 400;
}
/* Pull `.prop-label` (the Order chip's "Order" word) toward its icon. The
   chip uses a uniform gap: 6px, but fa-arrows-v is a narrow vertical glyph
   in a 12px box, so the optical gap reads ~10px. -3px restores it to ~6px
   without affecting other chips, which use `.prop-value` not `.prop-label`. */
.ui-refresh .session-row .sr-props .prop > i + .prop-label,
.ui-refresh .badges-container .prop > i + .prop-label {
    margin-left: -3px;
}
.ui-refresh .session-row .sr-props .prop .prop-caret {
    display: none;
    color: var(--muted-2);
    margin-left: 2px;
    line-height: 1;
    transition: transform var(--transition-fast), color var(--transition-fast);
    font-size: 9px;
}
.ui-refresh .session-row .sr-props .prop-trigger.open > .prop {
    border-color: var(--primary);
    background: var(--primary-tint);
    color: var(--primary-ink);
}
.ui-refresh .session-row .sr-props .prop-trigger.open > .prop > i,
.ui-refresh .session-row .sr-props .prop-trigger.open > .prop > svg {
    color: var(--primary-ink);
}
.ui-refresh .session-row .sr-props .prop-trigger.open > .prop .prop-caret {
    transform: rotate(180deg);
    color: var(--primary-ink);
}

/* Static (non-interactive) chip — used when a value can't be toggled
   inline (e.g. conditional visibility). No caret, no hover state. Also
   matches a bare .sr-props chip inside a list-table cell (no .session-row
   ancestor) — e.g. speaker's table session chip, chat room's Used/Theme
   chips — so it isn't left unstyled outside the card pane. */
.ui-refresh .session-row .sr-props .prop.is-static,
.ui-refresh .list-table .lt-cell-prop .sr-props .prop.is-static {
    cursor: default;
    background: transparent;
}
.ui-refresh .session-row .sr-props .prop.is-static:hover,
.ui-refresh .list-table .lt-cell-prop .sr-props .prop.is-static:hover {
    border-color: var(--line);
}
/* Static chip that links somewhere (e.g. early-bird price → settings drawer).
   Behaves like a normal interactive chip: pointer cursor, hover bg. */
.ui-refresh .session-row .sr-props .prop.is-static.is-linked,
.ui-refresh .list-table .lt-cell-prop .sr-props .prop.is-static.is-linked {
    cursor: pointer;
    text-decoration: none;
    color: inherit;
}
.ui-refresh .session-row .sr-props .prop.is-static.is-linked:hover,
.ui-refresh .list-table .lt-cell-prop .sr-props .prop.is-static.is-linked:hover {
    background: var(--card-raised);
    border-color: var(--accent-primary);
    color: inherit;
}
/* Small secondary label inside a linked static chip (e.g. "edit in settings") */
.ui-refresh .session-row .sr-props .prop.is-static.is-linked .prop-sub,
.ui-refresh .list-table .lt-cell-prop .sr-props .prop.is-static.is-linked .prop-sub {
    font-size: 10px;
    opacity: 0.6;
    margin-left: 2px;
}
/* Inline "edit" link shown next to a static price value in the table pane */
.ui-refresh .list-table .lt-price-edit-link {
    display: inline-block;
    margin-left: 4px;
    font-size: 11px;
    color: var(--accent-primary);
    text-decoration: none;
    white-space: nowrap;
    opacity: 0;
    transition: opacity var(--transition-fast);
}
.ui-refresh .list-table tr:hover .lt-price-edit-link {
    opacity: 1;
}
/* Flex wrapper keeps the editable price input and its early-bird hint
   on one line so the row height stays consistent. */
.ui-refresh .list-table .lt-price-field {
    display: flex;
    align-items: center;
}
.ui-refresh .list-table .lt-price-field .lt-input-price {
    flex: 1;
    min-width: 0;
    width: auto;
    max-width: 82px;
}
.ui-refresh .list-table .lt-price-field .lt-price-edit-link {
    flex-shrink: 0;
}
/* Dropdown menu under each chip. Mirrors .event-property-chips menu so the
   feel is identical across the app. */
.ui-refresh .session-row .sr-props .prop-menu {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 50;
    min-width: 180px;
    max-height: 280px;
    overflow-y: auto;
    padding: 4px;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    display: none;
}
.ui-refresh .session-row .sr-props .prop-menu.is-fixed {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 120;
}
.ui-refresh .session-row .sr-props .prop-trigger.open .prop-menu {
    display: block;
}
.ui-refresh .session-row .sr-props .prop-menu .prop-menu-label {
    font-family: var(--font-family);
    font-size: 10px;
    font-weight: var(--font-weight-semibold);
    color: var(--muted);
    padding: 8px 10px 6px;
    line-height: 1;
}
.ui-refresh .session-row .sr-props .prop-menu .qmi {
    appearance: none;
    border: 0;
    background: transparent;
    width: 100%;
    text-align: left;
    padding: 7px 10px;
    border-radius: var(--radius-sm);
    font-family: var(--font-family);
    font-size: 13px;
    color: var(--ink);
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: var(--space-2);
}
.ui-refresh .session-row .sr-props .prop-menu .qmi:hover {
    background: var(--paper-2);
}
.ui-refresh .session-row .sr-props .prop-menu .qmi[aria-checked="true"] {
    background: var(--paper-2);
    color: var(--primary);
    font-weight: var(--font-weight-semibold);
}

/* Input-style popover (used for capacity). Same chrome as the menu but
   the body holds a single input instead of `.qmi` items. */
.ui-refresh .session-row .sr-props .prop-menu.prop-menu-input {
    min-width: 200px;
    padding: var(--space-2);
}

.ui-refresh .session-row .sr-props .prop-menu.prop-menu-input .prop-menu-label {
    padding-bottom: 10px;
}

/* Field-name caption inside an open chip popover — answers "which field am I
   editing?" once the popover hides the chip's icon and placeholder. */
.ui-refresh .prop-menu .prop-menu-label {
    font-family: var(--font-family);
    font-size: 10px;
    font-weight: var(--font-weight-semibold);
    color: var(--muted);
    padding: 2px 4px var(--space-2);
}

.ui-refresh .session-row .sr-props .prop-menu input[data-chip-input] {
    width: 100%;
    padding: 7px 10px;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    font-family: var(--font-family);
    font-size: 13px;
    color: var(--ink);
    background: var(--paper);
    outline: none;
    font-variant-numeric: tabular-nums;
}
.ui-refresh .session-row .sr-props .prop-menu input[data-chip-input]:focus {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-ring);
}
.ui-refresh .session-row .sr-props .prop-menu .pp-hint {
    color: var(--muted);
    font-size: 11px;
    padding: 6px 4px 0;
    line-height: 1.4;
}
.ui-refresh .session-row .sr-props .prop-menu .pp-settings-link {
    display: block;
    margin-top: var(--space-2);
    padding: var(--space-2) var(--space-1) 0;
    border-top: 1px solid var(--line);
    font-size: 11px;
    font-weight: 500;
    color: var(--accent-primary);
    text-decoration: none;
    transition: color var(--transition-fast);
}
.ui-refresh .session-row .sr-props .prop-menu .pp-settings-link:hover {
    color: var(--accent-primary);
    text-decoration: underline;
}
.ui-refresh .session-row .sr-props .prop-menu .pp-settings-link i {
    margin-right: var(--space-1);
}

/* Action icon buttons (edit, clone, delete) sit at the row's right edge. */
.ui-refresh .session-row .sr-actions {
    display: flex;
    gap: 1px;
    align-items: center;
}
.ui-refresh .session-row .sr-actions .lr-icon {
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    border-radius: var(--radius-sm);
    background: transparent;
    border: 0;
    color: var(--muted);
    cursor: pointer;
    transition: background var(--transition-fast), color var(--transition-fast);
    text-decoration: none;
}
.ui-refresh .session-row .sr-actions .lr-icon:hover {
    background: var(--paper-2);
    color: var(--ink);
}
.ui-refresh .session-row .sr-actions .lr-icon.danger:hover {
    color: var(--color-error);
    background: var(--color-error-bg);
}
.ui-refresh .session-row .sr-actions .lr-icon i {
    font-size: 13px;
}

/* Empty state for the no-results case (filtered out, etc). */
.ui-refresh .list-empty {
    text-align: center;
    padding: var(--space-10) var(--space-6);
    border: 1px dashed var(--line-2);
    border-radius: var(--radius-md);
    background: var(--paper);
    color: var(--muted);
}
.ui-refresh .list-empty h3 {
    font-size: var(--font-size-base);
    font-weight: 600;
    color: var(--ink);
    margin: 0 0 var(--space-2);
}
/* Direct child only — decorative empty-state icon (_empty_list.php,
   integration/_empty_state.php). Buttons rendered inside the empty state
   (e.g. webhook headers/custom-fields "Create"/"Add" CTAs) carry their own
   nested <i> and must not be blockified/enlarged by this rule. */
.ui-refresh .list-empty > i {
    display: block;
    font-size: var(--font-size-xl);
    margin-bottom: var(--space-4);
    color: var(--line-2);
}

/* Reg-type page specific: the form-field error containers that ActiveForm
   wraps inputs in have margin/padding we don't want inside row chips. */
.ui-refresh .session-row .form-group {
    margin: 0;
    padding: 0;
}
.ui-refresh .session-row .form-group .help-block {
    display: none;
}

/* Reg-type page chrome: the existing `.btn-container` that holds Save +
   Create needs to align right within the new layout. */
.ui-refresh #reg-type-form .btn-container {
    margin-bottom: var(--space-5);
}

/* Drag placeholder while a row is being dragged via jQuery UI sortable.
   Outline-only so the user sees where the row will land without a solid
   block flashing in. */
.ui-refresh .session-row-placeholder {
    background: var(--paper-2);
    border: 1px dashed var(--line-2);
    box-shadow: none;
    visibility: visible !important;
}
.ui-refresh .session-row-placeholder > * {
    visibility: hidden;
}

/* Hidden form-state mirror — used to keep the underlying form input(s)
   for a chip in the DOM (so the bulk save sees them) while only the chip
   itself is visually presented. The chip JS writes to these inputs on
   commit. Replaces the previous inline `display:none` style attribute. */
.ui-refresh .chip-form-helper {
    display: none;
}

/* =====================================================================
   List view toggle + table view (reusable across list sections)
   ===================================================================== */

/* Pane visibility — driven by the wrapper's data-list-view attribute. */
.ui-refresh .list-view-wrap [data-list-view-pane] {
    display: none;
}
.ui-refresh .list-view-wrap[data-list-view="card"] [data-list-view-pane="card"],
.ui-refresh .list-view-wrap[data-list-view="table"] [data-list-view-pane="table"] {
    display: block;
}

/* Invitation Lists is the only page with both a _card_list card pane and a
   data-list-view-pane="table" table pane in the same toggle. The card pane
   gets its bottom margin from .comm-card-list:last-child (communicate.css);
   the table pane has no equivalent, so switching views made the gap before
   whatever follows (e.g. invitation-list's "Additional Settings" section)
   visibly shrink. Give it the same margin so the gap doesn't change with the
   toggle. Harmless no-op on other list-view-wrap pages, since none of them
   currently render a table pane. */
.ui-refresh .list-view-wrap[data-list-view="table"] [data-list-view-pane="table"] {
    margin-bottom: var(--space-5);
}

/* Segmented card/table toggle in the tab row. */
.ui-refresh .lv-toggle {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 2px;
    margin-right: var(--space-1);
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    background: var(--paper-2);
    flex-shrink: 0;
}
.ui-refresh .lv-toggle .lv-btn {
    width: 30px;
    height: 26px;
    display: grid;
    place-items: center;
    border: 0;
    background: transparent;
    border-radius: var(--radius-sm);
    color: var(--muted);
    cursor: pointer;
    transition: background var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .lv-toggle .lv-btn:hover {
    color: var(--ink);
}
.ui-refresh .lv-toggle .lv-btn i {
    font-size: 13px;
}
.ui-refresh .list-view-wrap[data-list-view="card"] .lv-btn[data-view="card"],
.ui-refresh .list-view-wrap[data-list-view="table"] .lv-btn[data-view="table"] {
    background: var(--card);
    color: var(--ink);
    box-shadow: var(--shadow-sm);
}

/* Optional report link (other sections; reg-types renders none). */
.ui-refresh .lv-report-link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    margin-right: var(--space-2);
    font-size: 13px;
    color: var(--muted);
}
.ui-refresh .lv-report-link:hover {
    color: var(--ink);
}

/* ---- Table ---- */
.ui-refresh .list-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-family: var(--font-family);
    min-width: 650px;
}
/* List tables may opt into a sticky header with `list-table--sticky-head`.
   Keep the whole thead together: Yii filter grids have two header rows, and
   pinning individual cells would make the filter row overlap sortable labels. */
.ui-refresh .list-table.list-table--sticky-head thead {
    position: sticky;
    top: var(--subpage-head-height, 0px);
    z-index: 2;
}
.ui-refresh .list-table thead th {
    text-align: left;
    padding: var(--space-2) var(--space-3);
    background: var(--paper-2);
    border-bottom: 1px solid var(--list-table-line);
    font-size: 11px;
    font-weight: var(--font-weight-semibold);
    color: var(--muted);
    white-space: nowrap;
}
/* Yii GridView's filter row is retained by list-table pages that need column
   filters. It shares the table's opaque header surface so a sticky thead does
   not show scrolling rows through its controls. */
.ui-refresh .list-table thead > tr.filters > td,
.ui-refresh .list-table thead > tr.filters > th {
    padding: var(--space-2) var(--space-3);
    background: var(--paper-2);
    border: 0;
    border-bottom: 1px solid var(--list-table-line);
}
.ui-refresh .list-table thead > tr.filters .form-control {
    width: 100%;
    min-width: 0;
    height: 32px;
    padding: var(--space-1) var(--space-2);
    font-size: var(--font-size-xs);
}
/* Bulk-update browser grids have a sticky .subpage-head (via .list-page), so
   their theads dock immediately below it. Plain CSS sticky avoids floatThead's
   cloned fixed-position header, which cannot account for the page header.
   --subpage-head-height is maintained by subpage-head-sticky.js's
   ResizeObserver, so this follows the header's real height. */
.ui-refresh .sponsor-mass-update-table thead th,
.ui-refresh .registrant-hotel-mass-update-table thead th {
    position: sticky;
    top: var(--subpage-head-height, 0px);
    z-index: 1;
}
/* Without a floor, .list-table's width:100% happily shrinks every column
   (and the form-control input inside it) to fit whatever space is left,
   rather than ever exceeding its container — which means the horizontal
   scroll wrapper below would never actually trigger and fields would just
   get squished on narrow viewports. Auto table layout still honors a
   per-cell min-width even under width:100%, so instead the table overflows
   its wrapper once there isn't enough room, scaling naturally with however
   many dynamic fields this report actually has (1 up to the 12-field cap in
   configure.php) rather than one hardcoded table-wide guess. The name
   column keeps its own larger floor from .lt-cell-name below. */
.ui-refresh .sponsor-mass-update-table th,
.ui-refresh .sponsor-mass-update-table td,
.ui-refresh .session-mass-update-table th,
.ui-refresh .session-mass-update-table td,
.ui-refresh .registrant-hotel-mass-update-table th,
.ui-refresh .registrant-hotel-mass-update-table td,
.ui-refresh .registrant-mass-update-table th,
.ui-refresh .registrant-mass-update-table td {
    min-width: 160px;
}
/* Below tablet width the table needs its own horizontal scroll — #main-
   content clips overflow-x, and this grid can have 12+ dynamic columns (see
   _grid.php). Scoped to the same breakpoint the legacy GridView 'responsive'
   stacked-card view used, and left OFF above it on purpose: an unconditional
   overflow-x:auto here would make this wrapper the nearest scroll container,
   which is also what position:sticky above computes its 'stuck' offset
   against — breaking the sticky thead on desktop too, since the wrapper
   never scrolls vertically on its own. */
@media only screen and (max-width: 767px),
       (min-device-width: 768px) and (max-device-width: 1024px) {
    .ui-refresh .sponsor-mass-update-table-wrap,
    .ui-refresh .registrant-hotel-mass-update-table-wrap {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }
}
/* Registrant's and session's reports carry enough selected fields in practice
   (session: 12 columns at 160px+ is ~2200px) to overflow even a wide desktop,
   so they need the horizontal scroll at every width, not just below tablet —
   which is why they're kept out of the sticky-thead rule above: an
   unconditional overflow-x:auto makes this wrapper the nearest scroll
   container, and it never scrolls vertically on its own (#main-content does),
   so position:sticky computed against it would just stop sticking. */
.ui-refresh .registrant-mass-update-table-wrap,
.ui-refresh .session-mass-update-table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}
/* Pin session's "Current Data" column while the grid scrolls sideways, so each
   row's session stays identifiable next to the field being edited (same
   sticky-column idea as .events-table/.admin-event-table). Above phone width
   only: its 260px .lt-cell-name floor would leave a phone almost no room for
   the scrolling columns. Pinned cells need an opaque background; the shared
   `.list-table tbody tr:hover td` rule still out-specifies the td fill.
   scroll-padding-left matches that 260px floor so Tab/Shift+Tab onto a field
   scrolled behind the pinned column brings it out from under it, rather than
   leaving the focused input hidden. */
@media (min-width: 768px) {
    .ui-refresh .session-mass-update-table-wrap {
        scroll-padding-left: 260px;
    }
    .ui-refresh .session-mass-update-table .lt-th-name,
    .ui-refresh .session-mass-update-table .lt-cell-name {
        position: sticky;
        left: 0;
        z-index: 2;
    }
    .ui-refresh .session-mass-update-table tbody > tr > td.lt-cell-name {
        background: var(--bg-page);
    }
}
/* Dynamic field cells render their <select>/<input> via Html::activeDropDownList()
   directly (session-multiple-update/_render-question.php, and the identical
   sponsor-multiple-update and registrant(-hotel)-multiple-update siblings), so —
   same root cause as the report-filter operator select in query-builder.css —
   they never pick up ActiveField::dropdownList()'s automatic `new-ui-select`
   class and fall back to the browser's native arrow. */
.ui-refresh .sponsor-mass-update-table select.form-control,
.ui-refresh .session-mass-update-table select.form-control,
.ui-refresh .registrant-hotel-mass-update-table select.form-control,
.ui-refresh .registrant-mass-update-table select.form-control {
    background-size: .6em;
    background-image: var(--select-chevron-image);
    background-position: calc(100% - 1.3em) center;
    background-repeat: no-repeat;
    appearance: none;
    padding-right: 30px;
}
/* A subordinate heading inside the Filter The Data section. Match the nearby
   flat-panel titles without inheriting the browser's larger h4 default. */
.ui-refresh .registrant-hotel-mass-update-section-heading {
    margin: var(--space-5) 0 var(--space-2);
    color: var(--fg-default);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    line-height: 1.2;
}
.ui-refresh .list-table thead th:first-child {
    border-top-left-radius: var(--radius-md);
}
.ui-refresh .list-table thead th:last-child {
    border-top-right-radius: var(--radius-md);
}
.ui-refresh .list-table thead th.lt-th-num {
    text-align: left;
}
.ui-refresh .list-table thead th.text-center {
    text-align: center;
}
/* Sort links rendered by Yii Sort::link(): strip anchor chrome, add a caret.
   Yii adds the `asc`/`desc` class to the active sort link. */
.ui-refresh .list-table thead a {
    color: inherit;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    cursor: pointer;
}
.ui-refresh .list-table thead a:hover {
    color: var(--ink);
}

/* Show a faint sortable hint only on hover; active sort column always shows its
   direction caret. This keeps the header clean when no sort is applied. */
.ui-refresh .list-table thead a::after {
    content: "▾";
    font-size: 13px;
    line-height: 1;
    opacity: 0;
    transition: opacity var(--transition-fast);
}
.ui-refresh .list-table thead th:hover a::after {
    opacity: 0.35;
}
/* Override backend.css's global `a.asc/a.desc::after` sort-alpha glyphs (which
   use FA Pro + !important) so this table shows the same clean triangles for the
   active column as the neutral state. */
.ui-refresh .list-table thead a.asc::after {
    content: "▴" !important;
    font-family: inherit !important;
    font-weight: normal;
    padding-left: 0;
    top: 0;
    opacity: 0.9;
}
.ui-refresh .list-table thead a.desc::after {
    content: "▾" !important;
    font-family: inherit !important;
    font-weight: normal;
    padding-left: 0;
    top: 0;
    opacity: 0.9;
}

.ui-refresh .list-table tbody td {
    padding: var(--space-3);
    vertical-align: middle;
    color: var(--fg-muted);
    font-size: var(--font-size-input);
}
/* Pointer only on genuinely click-navigable rows. Event-level tables carry
   data-update-url (the hook reg-type-list.js/account-question-list.js/
   track/etc. bind to). Account-level ListTableGridView tables opt in with
   `rowClickUpdate`, which stamps data-row-click and wires a handler that fires
   the row's own Update button (ListTableAsset). Views with their own bespoke
   row-click handler (session's is keyed on the edit link, not a data
   attribute) should add `.clickable-row` directly instead of inventing
   another attribute-based hook. Rows without any of these — including
   empty-state rows — keep the default cursor. */
.ui-refresh .list-table tbody tr {
    cursor: default;
}
.ui-refresh .list-table tbody tr[data-update-url],
.ui-refresh .list-table tbody tr[data-row-click],
.ui-refresh .list-table tbody tr.clickable-row {
    cursor: pointer;
}
/* Exclude Yii's empty-state row (its lone <td> always carries a colspan) —
   it isn't clickable/interactive, so it shouldn't show a hover background. */
.ui-refresh .list-table tbody tr:not(:has(td[colspan])):hover td {
    background: var(--paper-2);
}

.ui-refresh .list-table .lt-cell-name {
    font-weight: var(--font-weight-medium);
}
.ui-refresh .list-table .lt-cell-num {
    text-align: left;
}
/* Code preview cell (e.g. JS Snippets "Code" column): monospace the raw code
   so it reads as code, at a slightly smaller size than body text. */
.ui-refresh .list-table .lt-cell-code {
    font-family: var(--font-mono);
    font-size: 12px;
}

/* Column filter row (ListTableGridView with a filterModel — first consumer is
   the account Events list, event/_table.php). Yii renders it as a second
   <thead> row `tr.filters`. The header band's divider moves down under the
   filter row so title + filters read as one block. */
.ui-refresh .list-table thead tr:has(+ tr.filters) th {
    border-bottom: 0;
}
.ui-refresh .list-table thead tr.filters td {
    padding: 0 var(--space-3) var(--space-2);
    background: var(--paper-2);
    border-bottom: 1px solid var(--list-table-line);
    white-space: nowrap;
}
/* Styled from scratch (not form-control) — same framing as the filter
   popover's .fp-input, sized to sit inside a header band. */
.ui-refresh .list-table .lt-filter-input {
    width: 100%;
    min-width: 0;
    height: 28px;
    padding: 0 var(--space-2);
    font-family: var(--font-family);
    font-size: 13px;
    font-weight: var(--font-weight-medium);
    color: var(--ink);
    background: var(--card);
    border: 1px solid var(--input-border);
    border-radius: var(--radius-sm);
    outline: 0;
    font-variant-numeric: tabular-nums;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.ui-refresh .list-table .lt-filter-input::placeholder {
    color: var(--muted);
}
.ui-refresh .list-table .lt-filter-input:focus {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-ring);
}
/* Native <select>: same token-driven chevron the mass-update tables use. */
.ui-refresh .list-table select.lt-filter-input {
    cursor: pointer;
    appearance: none;
    padding-right: var(--space-6);
    background-image: var(--select-chevron-image);
    background-repeat: no-repeat;
    background-position: calc(100% - var(--space-2)) center;
    background-size: .6em;
}
.ui-refresh .list-table thead tr.filters td.has-error .lt-filter-input {
    border-color: var(--color-error);
}
.ui-refresh .list-table thead tr.filters .help-block {
    display: none;
}
/* In-table empty state (GridView emptyText wraps /common/_empty_list in a
   colspan cell). The dashed .list-empty card brings its own padding, so drop
   the generic .grid-view .empty padding and just give it a little air. */
.ui-refresh .list-table td > .lt-empty {
    padding: var(--space-4) 0 0;
}

/* Compact modifier for small containers (e.g. qtip popups) — tighter padding
   and type than the default .list-table sizing. */
.ui-refresh .list-table--compact {
    background-color: var(--paper);
    border: 1px solid var(--panel-border);
    border-radius: var(--space-2);
}
.ui-refresh .list-table--compact thead th {
    border-bottom: 1px solid var(--panel-border);
    color: var(--fg-default);
    padding: var(--space-2) var(--space-3);
    font-size: var(--font-size-sm);
}
.ui-refresh .list-table--compact tbody td {
    padding: var(--space-2) var(--space-3);
    font-size: var(--font-size-xs);
}

/* qtip2's "bootstrap" skin ships hardcoded light colors (#fff/#333/#f7f7f7)
   with no theme awareness. QtipAsset is only ever registered from
   session/schedule.php's calendar popup, so this is safe to theme globally
   under .ui-refresh — nothing else in the app uses it. qtip2's Tip plugin
   reads the popup's computed border/background color at render time to draw
   the matching arrow, so overriding these is enough; no JS/canvas change
   needed. */
.ui-refresh .qtip-bootstrap {
    background-color: var(--card);
    border-color: var(--panel-border);
    box-shadow: var(--sh-lg);
    color: var(--ink);
}
.ui-refresh .qtip-bootstrap .qtip-titlebar {
    background-color: var(--paper-2);
    border-bottom-color: var(--line);
}
.ui-refresh .qtip-bootstrap .qtip-content .btn-group {
    display: flex;
}
.ui-refresh .qtip-bootstrap .qtip-content .btn-group a.btn {
    flex: 1 0 0;
}
.qtip-bootstrap .qtip-content tbody td:first-child {
    color: var(--fg-default);
    font-weight: var(--font-weight-semibold);
    min-width: 120px;
    max-width: 150px;
}
.qtip-bootstrap .qtip-content p:empty {
    display: none;
}

.qtip-bootstrap .qtip-content {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    padding: var(--space-4);

}

/* Inline bulk-add-alternate-codes block (discount drawer). Sits below the
   alternate-codes table; the panel expands in place instead of a modal. */
.ui-refresh .alt-add {
    margin-top: var(--space-3);
}
.ui-refresh .alt-add-panel {
    margin-top: var(--space-2);
    padding: var(--space-3);
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--panel-radius);
}
.ui-refresh .alt-add-textarea {
    width: 100%;
    min-height: 140px;
    resize: vertical;
    font-family: var(--font-mono);
    font-size: 13px;
}
.ui-refresh .alt-add-error {
    color: var(--danger, #d33);
    margin-top: var(--space-1);
}

/* Inline-editable inputs. The negative left margin pulls the input's TEXT out
   to the cell's content edge so it lines up exactly with the column header,
   while the internal padding still gives the hover/focus ring breathing room.
   The ring is an inset box-shadow (not a border) so it never shifts the text.
   width compensates for the negative margin so the right edge stays put. */
.ui-refresh .list-table .lt-input {
    width: calc(100% + var(--space-2));
    /* Kartik merges the default `form-control` class onto these inputs, so its
       `!important` height/border-radius/focus rules would otherwise override the
       borderless look — hence the !important resets here and on :focus below. */
    height: auto !important;
    border: 0 !important;
    background: transparent;
    border-radius: var(--radius-sm) !important;
    padding: var(--space-1) var(--space-2);
    margin-left: calc(-1 * var(--space-2));
    font: inherit;
    color: var(--ink);
    cursor: text;
    box-shadow: none !important;
    transition: box-shadow var(--transition-fast), background var(--transition-fast);
}
.ui-refresh .list-table .lt-input:hover {
    box-shadow: inset 0 0 0 1px var(--line) !important;
}
.ui-refresh .list-table .lt-input:focus {
    box-shadow: inset 0 0 0 1px var(--accent-primary) !important;
    background: var(--card);
    outline: 0;
}
.ui-refresh .list-table .lt-input-capacity {
    max-width: 98px;
    text-align: left;
}
/* Borderless cell input with a muted unit affix — a currency symbol before,
   or "%" after (registrant Adjust Fees). The affix owns the leading gap, so
   drop .lt-input's negative left margin here. */
.ui-refresh .list-table .lt-affixed-input {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}
.ui-refresh .list-table .lt-affixed-input .lt-affix {
    color: var(--muted);
    font-size: var(--font-size-xs);
    white-space: nowrap;
}
.ui-refresh .list-table .lt-affixed-input .form-group {
    flex: 1;
    min-width: 0;
    margin: 0;
}
.ui-refresh .list-table .lt-affixed-input .lt-input {
    margin-left: 0;
    width: 100%;
}
/* Cell inputs hide their .help-block, so a failed client validation (e.g. a
   non-numeric fee) has to show on the input ring itself. */
.ui-refresh .list-table .lt-cell .has-error .lt-input {
    box-shadow: inset 0 0 0 1px var(--color-error) !important;
}
/* Adjust Fees: fee / tax-rate columns carry an affix + input, wider than the
   generic 110px numeric column. Placed after .lt-th-num so it wins on order. */
.ui-refresh .adjust-fees-table .lt-th-num,
.ui-refresh .adjust-fees-table .lt-cell-num {
    width: 160px;
}
/* Custom-domain redirect grid: the "from" cell shows the domain base URL as a
   muted, non-editable affix before the borderless path input. */
.ui-refresh .list-table .lt-redirect-from {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}
.ui-refresh .list-table .lt-redirect-from .lt-affix {
    color: var(--muted);
    white-space: nowrap;
    font-size: var(--font-size-xs);
}
.ui-refresh .list-table .lt-redirect-from .form-group {
    flex: 1;
    min-width: 0;
    margin: 0;
}
/* Space the "+ Create Redirect" action off the redirect table above it (the
   list-table has no bottom margin of its own). */
.ui-refresh #custom-redirects .create-btn {
    margin-top: var(--space-4);
}
/* Give the robots.txt textarea a taller resting height (rows=4 reads cramped);
   height:auto lets it honour this over the base .form-control fixed height. */
.ui-refresh #robots-file textarea {
    height: auto !important;
    min-height: 120px;
}
/* Strip the Yii .form-group margin / validation block inside cells. */
.ui-refresh .list-table .lt-cell .form-group {
    margin: 0;
}
/* shim for early bird date drawer to eliminate extra padding */
.ui-refresh #early-bird-date-form .list-table .lt-cell .form-group {
    padding: 0;
}
.ui-refresh .list-table .lt-cell .help-block {
    display: none;
}

.ui-refresh .list-table.discount-fee-table .lt-fee-current {
    display: block;
    color: var(--fg-muted);
    font-size: var(--font-size-xs);
}
.ui-refresh .list-table.discount-fee-table .lt-th-fee,
.ui-refresh .list-table.discount-fee-table .lt-cell-fee {
    width: 175px;
}
/* backend.css:515 gives EVERY `.select2-container` an unscoped
   `float: left; margin-bottom: 20px` (a legacy global from the old full-page
   forms). In a table cell that floats the box out of flow and adds 20px of
   dead space below it, inflating the row. Reset both for Select2 fields
   rendered inside a list-table cell. */
.ui-refresh .list-table .lt-cell .select2-container {
    float: none;
    margin-bottom: 0;
}
/* Hidden companion checkbox for conditional-visibility rows. */
.ui-refresh .list-table .lt-hidden-field {
    display: none;
}
/* Toggle cell: the .checkbox wrapper already renders the lavender switch. */
.ui-refresh .list-table .lt-cell-toggle .checkbox {
    padding: 0;
    margin: 0;
}
.ui-refresh .list-table .lt-cond-link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    color: var(--muted);
    font-size: 13px;
}
/* Group header row inside a .list-table (EventMobi's People/Sponsor field
   mapping tables split their rows into labelled groups via GridView::$beforeRow).
   Deliberately a new class, not the legacy `.extrarow` — backend.css paints
   `.grid-view .extrarow { background: white; color: gray-800 }`, which in dark
   mode is a glaring white band with unreadable text. Starting from a fresh
   class means there is nothing to reset. */
.ui-refresh .list-table .lt-group-row > th {
    text-align: left;
    padding: var(--space-3);
    background: var(--bg-muted);
    border-bottom: 1px solid var(--list-table-line);
    color: var(--fg-default);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
}

/* Fixed-width columns — name gets all remaining space; all others are pinned
   narrow so the name column dominates the layout. */
.ui-refresh .list-table .lt-th-name,
.ui-refresh .list-table .lt-cell-name {
    width: auto; /* takes remaining space */
    min-width: 260px;
}
.ui-refresh .list-table .lt-th-num,
.ui-refresh .list-table .lt-cell-num {
    width: 110px;
}
.ui-refresh .list-table .lt-th-sm,
.ui-refresh .list-table .lt-cell-sm {
    width: 110px;
}
/* Sessions table: cap ancillary columns so the name column gets priority */
.ui-refresh .list-table .lt-th-sdate,
.ui-refresh .list-table .lt-cell-sdate { width: 110px; }
.ui-refresh .list-table .lt-th-stime,
.ui-refresh .list-table .lt-cell-stime { width: 155px; }
.ui-refresh .list-table .lt-th-track,
.ui-refresh .list-table .lt-cell-track { width: 140px; }
.ui-refresh .list-table .lt-th-sloc,
.ui-refresh .list-table .lt-cell-sloc  { width: 140px; }
/* Speakers table: cap narrow label columns, prioritize name columns */
.ui-refresh .list-table .lt-th-spk-fname,
.ui-refresh .list-table .lt-cell-spk-fname,
.ui-refresh .list-table .lt-th-spk-lname,
.ui-refresh .list-table .lt-cell-spk-lname { min-width: 140px; width: auto; }
.ui-refresh .list-table .lt-th-jobtitle,
.ui-refresh .list-table .lt-cell-jobtitle,
.ui-refresh .list-table .lt-th-company,
.ui-refresh .list-table .lt-cell-company { max-width: 180px; }
.ui-refresh .list-table .lt-th-jobtitle,
.ui-refresh .list-table .lt-th-company { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-refresh .list-table .lt-cell-jobtitle,
.ui-refresh .list-table .lt-cell-company { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-refresh .list-table .lt-th-spk-sessions,
.ui-refresh .list-table .lt-cell-spk-sessions { width: 120px; }
.ui-refresh .list-table .lt-th-center {
    text-align: left;
}
.ui-refresh .list-table .lt-cell-center {
    text-align: left;
}
.ui-refresh .list-table .lt-th-center,
.ui-refresh .list-table .lt-cell-toggle {
    width: 90px;
}
/* Left-align the toggle switch so it lines up with its column header. */
.ui-refresh .list-table .lt-cell-center .checkbox {
    display: flex;
    justify-content: flex-start;
}

/* Avalara tax-code table: the Item Code / Tax Code columns hold a Select2
   field. Kartik defaults to BS3 here, which forces the plugin's
   `width: '100%'` — so the select2 box always fills the cell and text-align
   can never position it. Pin the columns narrow (as .lt-th-num does) so the
   Name column keeps priority, and leave both header and box left-aligned at
   the cell's content edge, where they line up exactly. */
.ui-refresh .avalara-tax-table .lt-th-taxcode,
.ui-refresh .avalara-tax-table .lt-cell-taxcode {
    width: 200px;
    text-align: left;
}

/* Static yes/no indicator icons (e.g. CFS Role's Submitter/Reviewer columns).
   "No" isn't an error state, so it gets the muted tone rather than danger. */
.ui-refresh .list-table .lt-bool-icon--yes {
    color: var(--color-success);
}
.ui-refresh .list-table .lt-bool-icon--no {
    color: var(--fg-muted);
}
/* Attention state for a column whose "no" *is* actionable (e.g. Marketo's
   invalid credentials). Bootstrap's .text-warning isn't token-aware and goes
   muddy on dark paper. */
.ui-refresh .list-table .lt-bool-icon--warn {
    color: var(--color-warning);
}

/* Display-order column — shown for large lists (>25) in place of drag. */
.ui-refresh .list-table .lt-th-order,
.ui-refresh .list-table .lt-cell-order {
    width: 56px;
    text-align: left;
}
.ui-refresh .list-table .lt-cell-order {
    color: var(--muted);
    font-variant-numeric: tabular-nums;
}
/* Price column — narrower than generic num columns; values are compact. */
.ui-refresh .list-table .lt-th-price,
.ui-refresh .list-table .lt-cell-price {
    width: 90px;
}
.ui-refresh .list-table .lt-cell-price .lt-static-value {
    display: block;
    max-width: 82px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Drag handle column — always rendered; handle hidden when sort active */
.ui-refresh .list-table .lt-th-drag,
.ui-refresh .list-table .lt-cell-drag {
    width: 28px;
    padding-left: var(--space-2);
    padding-right: 0;
}
.ui-refresh .list-table .lt-cell-drag .sr-drag {
    color: var(--muted);
    cursor: grab;
    opacity: 0;
    letter-spacing: -2px;
    user-select: none;
    transition: opacity var(--transition-fast);
}
.ui-refresh .list-table .lt-cell-drag .sr-drag:active {
    cursor: grabbing;
}
.ui-refresh .list-table tbody tr:hover .sr-drag {
    opacity: 1;
}
.ui-refresh .list-table tbody tr.ui-sortable-helper {
    display: table;
    background: var(--card);
    box-shadow: var(--shadow-md);
}
/* display:block lets height work on the placeholder <tr> (table-row layout
   ignores height). Height is set inline by JS from the captured row height. */
/* Placeholder <td>s get inline height set by JS from the pre-captured row height. */
.ui-refresh .lt-row-placeholder td {
    background: var(--primary-tint);
}

/* Checkbox column (e.g. discount codes bulk-select). */
.ui-refresh .list-table .lt-th-check,
.ui-refresh .list-table .lt-cell-check {
    width: 36px;
    padding-left: var(--space-2);
    padding-right: 0;
}
/* `.row-select` is an inline-block with `vertical-align: middle`, which centres
   the box on the parent font's baseline + half its x-height — NOT on the line
   box or the cell. The leftover offset scales with the cell's font-size, so the
   checkbox sat ~1.25px low in the 11px thead and ~0.45px low in a 13px tbody
   row, i.e. visibly off AND inconsistent between header and body. Making it a
   block removes the inline strut from the calculation entirely, so the cell's
   own `vertical-align: middle` centres it exactly. Scoped to the check column
   so the card rows and the bulk bar keep the shared inline treatment; wins over
   `.row-select.is-locked`'s inline-block (0,3,0) for locked rows too. */
.ui-refresh .list-table .lt-th-check .row-select,
.ui-refresh .list-table .lt-cell-check .row-select {
    display: block;
}

/* Filter row — sits below the header row when search/filter is active.
   backend.css:2673 gives every `table .filters` row an unscoped hardcoded
   white background (a legacy global from the old Bootstrap grids); since
   `.list-table` deliberately drops the `.table` class that the ui-refresh
   `.table .filters` override (above) targets, that legacy white bleeds
   through wherever a filter cell has no opaque control on top of it. Paint
   the row explicitly and declare --control-bg-color so any .form-control
   (or un-enhanced <select>) inside it reads the correct flipped background
   per the surface-container contract. */
.ui-refresh .list-table .lt-filter-row td {
    padding-top: var(--space-1);
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--border-default);
    background: var(--bg-surface);
    --control-bg-color: var(--bg-page);
}
/* Defensive Select2 override for filters rendered inside a list-table filter
   row (e.g. DropdownSearch): scoped by element class only (not the
   `--krajee-bs3` theme modifier) so it wins regardless of which Select2
   theme actually renders, at higher specificity than both the generic
   `.select2-container--krajee-bs3 .select2-selection` rule above and the
   vendor theme's own unscoped `background-color: #fff`. */
.ui-refresh .list-table .lt-filter-row .select2-selection,
.ui-refresh .list-table .lt-filter-row .select2-selection:focus {
    background-color: var(--bg-surface);
    border-color: var(--input-border);
    color: var(--fg-default);
}
.ui-refresh .list-table .lt-filter-row .select2-selection__rendered,
.ui-refresh .list-table .lt-filter-row .select2-selection__clear {
    color: var(--fg-default);
}
.ui-refresh .list-table .lt-filter-row .select2-selection__placeholder {
    color: var(--fg-subtle);
}
.ui-refresh .list-table .lt-filter-row .select2-selection__arrow {
    border-left-color: var(--input-border);
}
.ui-refresh .list-table .lt-filter-input {
    width: 100%;
    min-width: 80px;
    padding: 4px var(--space-2);
    border: 1px solid var(--input-border);
    border-radius: var(--input-radius);
    background: var(--bg-surface);
    color: var(--ink);
    font-size: var(--font-size-sm);
    line-height: 1.4;
}
.ui-refresh .list-table .lt-filter-input:focus {
    outline: none;
    border-color: var(--accent-primary);
    box-shadow: 0 0 0 2px var(--primary-tint);
}
/* <select> variant of .lt-filter-input: native arrow off, token chevron on
   (same treatment as .admin-event-table's filter selects). */
.ui-refresh .list-table select.lt-filter-input {
    appearance: none;
    -webkit-appearance: none;
    padding-right: var(--space-6);
    background-image: var(--select-chevron-image);
    background-repeat: no-repeat;
    background-position: calc(100% - var(--space-2)) center;
    background-size: .6em;
    cursor: pointer;
}
.ui-refresh .list-table .lt-filter-submit {
    text-align: right;
    white-space: nowrap;
}

/* Actions column — clone/delete, revealed on row hover. */
.ui-refresh .list-table .lt-cell-actions,
.ui-refresh .list-table .lt-th-actions {
    /* width: 1%; */
    white-space: nowrap;
    text-align: right;
}
.ui-refresh .list-table .lt-cell-actions .lr-icon {
    display: inline-grid;
    place-items: center;
    width: 28px;
    height: 28px;
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--muted);
    opacity: 0;
    /* Some of these render as a <button> or an <a> with no href, which
       browsers otherwise leave at the default cursor. */
    cursor: pointer;
    transition: opacity var(--transition-fast), background var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .list-table tbody tr:hover .lt-cell-actions .lr-icon,
.ui-refresh .list-table tbody tr:has(:focus-visible) .lt-cell-actions .lr-icon {
    opacity: 1;
}
/* Touch devices have no reliable hover, so keep row actions visible. */
@media (hover: none), (pointer: coarse) {
    .ui-refresh .list-table .lt-cell-actions .lr-icon {
        opacity: 1;
    }
}
.ui-refresh .list-table .lt-cell-actions a:hover {
    text-decoration: none;
}
.ui-refresh .list-table .lt-cell-actions .lr-icon:hover {
    background: var(--paper-2);
    color: var(--ink);
}
.ui-refresh .list-table .lt-cell-actions .lr-icon.danger:hover {
    color: var(--danger-primary);
}
/* Disabled delete renders as a real <button> (not <a>), which otherwise
   keeps the browser's native light "buttonface" background — the
   `background: transparent` above already fixes the base state, but the
   hover rule above still applies its own (--paper-2) background/color;
   :disabled buttons can't be hovered but aria-disabled ones still can,
   so pin both to the resting look and swap the cursor. */
.ui-refresh .list-table .lt-cell-actions .lr-icon.is-disabled,
.ui-refresh .list-table .lt-cell-actions .lr-icon.is-disabled:hover {
    background: transparent;
    color: var(--muted);
    cursor: not-allowed;
}

/* Events list (event/_table.php) ONLY: actions column is LEADING and pinned,
   matching the report grids (Contacts, Registrant reports, Swoogo Admin)
   instead of the trailing, hover-revealed default every other .list-table
   consumer above keeps (reg-type, tracks, locations, sponsors). Sits inside
   the .events-table-wrap scroll container below — #main-content's own
   overflow-x is `hidden` on account list pages (layout.css), so without a
   scroll wrapper a too-wide table would just get silently clipped. */
.ui-refresh .events-table-wrap {
    overflow-x: auto;
    overscroll-behavior-inline: contain;
}
.ui-refresh .list-table.events-table .lt-th-actions,
.ui-refresh .list-table.events-table .lt-cell-actions {
    position: sticky;
    left: 0;
    z-index: 2;
    width: 1%;
    text-align: left;
}
/* Opaque, token-driven background on every state so columns scrolling
   underneath can't bleed through the pinned column. The header th already
   gets --paper-2 from the shared `.list-table thead th` rule above, and so
   does this filter td from the shared `.list-table thead tr.filters td` rule
   (both are bare-tag selectors that already match every cell in their row) —
   restated here only because .lt-cell-actions's higher specificity would
   otherwise win with nothing, leaving it transparent. Plain tbody td has no
   such shared rule, so it needs --bg-page (the actual page background behind
   the table) stated for the first time. */
.ui-refresh .list-table.events-table thead > tr.filters > td.lt-cell-actions {
    background: var(--paper-2);
}
.ui-refresh .list-table.events-table tbody > tr > td.lt-cell-actions {
    background: var(--bg-page);
}
.ui-refresh .list-table.events-table tbody tr:not(:has(td[colspan])):hover > td.lt-cell-actions {
    background: var(--paper-2);
}
/* Divider on the pinned column's right edge, only while the wrapper is
   actually scrolled underneath it (event-list.js toggles this class) — at
   scrollLeft 0 there's nothing to separate it from. */
.ui-refresh .events-table-wrap.is-scrolled .lt-th-actions,
.ui-refresh .events-table-wrap.is-scrolled .lt-cell-actions {
    box-shadow: 1px 0 0 var(--list-table-line);
}
/* Always visible — not hover-revealed like every other .list-table's
   .lr-icon default above. */
.ui-refresh .list-table.events-table .lt-cell-actions .lr-icon {
    opacity: 1;
}

/* Pagination row — per-page selector left, page buttons centre, spacer right. */
.ui-refresh .list-pagination-row {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    padding: var(--space-3) 0 var(--space-4);
}
.ui-refresh .list-pagination-row .per-page-label {
    flex-shrink: 0;
    margin: 0;
}
.ui-refresh .list-pagination-row .per-page-label {
    font-weight: normal;
    text-align: left;
    white-space: nowrap;
    margin-bottom: 0;
}
.ui-refresh .list-pagination-row .per-page-select {
    height: var(--input-height);
    border: 1px solid var(--input-border);
    border-radius: var(--input-radius);
    background-color: transparent !important;
    color: var(--fg-default);
    font-size: var(--font-size-sm);
    padding: var(--space-2) 0 var(--space-2) var(--space-1);
}

.ui-refresh .list-pagination-row .pagination {
    margin: 0;
    flex: 1;
    display: flex;
    justify-content: center;
}

/* ---- Reusable cell treatments (other sections; reg-types uses none) ----
   Shipped now so the next section needs no new layer. */
/* Category / status pills inside a list-table (e.g. Domains "Type", email-domain
   status). Neutralize the loud legacy Bootstrap badge colors into subtle,
   token-based pills that sit quietly in the row. success → accent tint,
   danger/default → neutral. */
.ui-refresh .list-table .badge,
.ui-refresh .fchip .badge {
    display: inline-block;
    padding: 2px var(--space-2);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-full);
    background: var(--paper-2);
    color: var(--muted);
    font-family: var(--font-family);
    font-size: 11px;
    font-weight: var(--font-weight-medium);
    line-height: 1.6;
    letter-spacing: 0.01em;
    text-transform: none;
}
.ui-refresh .list-table .badge-success {
    border-color: var(--accent-muted);
    background: var(--accent-subtle);
    color: var(--accent-hover);
}
.ui-refresh .list-table .badge-warning,
.ui-refresh .fchip .badge-warning {
    border-color: var(--warn-line);
    background: var(--warn-bg);
    color: var(--warn);
}
.ui-refresh .list-table .badge-danger {
    border-color: var(--danger-line);
    background: var(--danger-bg);
    color: var(--danger);
}
.ui-refresh .list-table .api-key-lock-cell {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}
.ui-refresh .list-table .api-key-unlock-link {
    font-weight: var(--font-weight-medium);
}
.ui-refresh .list-table .lt-avatar {
    display: inline-grid;
    place-items: center;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-full);
    color: #fff;
    font-size: 11px;
    font-weight: var(--font-weight-semibold);
    margin-right: var(--space-2);
}
.ui-refresh .list-table .lt-swatch {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}
.ui-refresh .list-table .lt-swatch::before {
    content: "";
    width: 10px;
    height: 10px;
    border-radius: 3px;
    background: currentColor;
    flex-shrink: 0;
}
.ui-refresh .list-table .lt-secondary {
    color: var(--muted);
}
.ui-refresh .list-table .lt-add-link {
    color: var(--muted);
}
.ui-refresh .list-table .lt-add-link:hover {
    color: var(--accent-primary);
}

/* Webhooks list (webhook/_grid.php): keep long endpoint URLs to one line */
.ui-refresh .webhooks-table .lt-cell-url a {
    display: inline-block;
    max-width: 260px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: bottom;
}

/* Inline name editing: dim the input while its save is in flight. */
.ui-refresh .webhooks-table .lt-input-name.is-saving {
    opacity: 0.6;
}

/* Inline name editing: non-blocking error bubble (replaces alert()). Anchored
   to .lt-cell-name, which is given position:relative below so the bubble
   lands directly under the input rather than the page. Auto-dismissed by
   webhook-list.js — no hover/focus dependency, unlike the Save-button error
   tooltip elsewhere in this file. */
.ui-refresh .webhooks-table .lt-cell-name {
    position: relative;
}
.ui-refresh .webhooks-table .lt-name-error {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 20;
    margin-top: var(--space-1);
    padding: var(--space-2) var(--space-3);
    max-width: 320px;
    background: var(--bg-surface);
    border: 1px solid var(--danger-line);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    color: var(--danger);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-normal);
    line-height: var(--line-height-sm);
    opacity: 0;
    transform: translateY(-2px);
    transition: opacity var(--transition-fast), transform var(--transition-fast);
    pointer-events: none;
}
.ui-refresh .webhooks-table .lt-name-error.is-visible {
    opacity: 1;
    transform: translateY(0);
}

/* ===== Webhook update form (account-form shell) =====
   webhook/_form.php inside .account-form — see webhook/update.php. */
/* Alert banners at the top of any centered account-form column (motivating
   case: webhook/_enablement.php banners) need separation from the content
   below; the existing spacing rule is scoped to .rd-profile (view page). */
.ui-refresh .account-form .inline-alert {
    margin-bottom: var(--space-8);
}
/* Webhook sections are also rendered inside the AjaxDrawer, where they do not
   have the sponsor drawer's flex-based .panel-body rhythm. Give their direct
   content the same breathing room while keeping the section heading aligned
   with the first control. */
.ui-refresh .ajax-drawer .webhook-form .account-section {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}
.ui-refresh .ajax-drawer .webhook-form .account-section > .account-section-heading {
    margin-bottom: 0;
}
/* ===== Inline-edit tables (Webhook headers, Webhook custom-fields mapping) =====
   Both use `.list-table` (the same flat, bordered-row table used by
   Registrant Types/Sessions/Speakers/etc. and Contact/Registrant Fields —
   see account-question/_field-table.php) plus its `.lt-*` cell classes and
   `.lr-icon` action buttons, rather than a bespoke card treatment. */
/* These tables sit on the drawer's page surface, so an outside rule is needed
   to keep their bounds visible in addition to the internal row dividers. */
.ui-refresh .webhook-form .list-table,
.ui-refresh .saml-page .list-table {
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
}
.ui-refresh .webhook-form .list-table tbody tr:last-child td,
.ui-refresh .saml-page .list-table tbody tr:last-child td {
    border-bottom: 0;
}
/* Header Value cell: the ToggleHideTextInput widget injects its own \3c style>
   with a bordered gray input-group look; flatten it to match the Key column's
   borderless .lt-input, with the eye toggle as a quiet ghost icon button.
   !important mirrors the widget's own !important rules. */
.ui-refresh .list-table td.webhook-value-content {
    /* widget CSS sets display:flex on the class; align the pair */
    align-items: center;
    gap: var(--space-1);
}
/* .lt-input's width:calc(100% + space) assumes it is the cell's sole content;
   here it shares the flex row with the eye-toggle button, so let it shrink to
   fit instead of overflowing past the button. */
.ui-refresh .list-table .webhook-value-content .lt-input {
    width: auto;
    flex: 1 1 auto;
    min-width: 0;
}
.ui-refresh .list-table .webhook-value-content .btn {
    border: 0 !important;
    background: transparent !important;
    color: var(--fg-muted) !important;
    border-radius: var(--radius-sm) !important;
    padding: var(--space-1) var(--space-2);
    transition: background var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .list-table .webhook-value-content .btn:hover {
    background: var(--bg-muted) !important;
    color: var(--fg-default) !important;
}
/* Header keys and values are directly editable, unlike most list-table text.
   Keep their input boundary visible at rest so it does not disappear into the
   row background when the row itself is hovered. */
.ui-refresh .webhook-headers-wrapper .list-table .lt-input,
.ui-refresh .saml-mapping-table .lt-input {
    width: 100%;
    margin-left: 0;
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--input-border) !important;
    background: var(--control-bg-color);
}
/* Create/Add buttons under an inline-edit list-table need a gap; the tables
   have no bottom margin of their own. Same gap for a CTA inside a .list-empty
   box, which otherwise sits flush under the copy. */
.ui-refresh .lt-table-create,
.ui-refresh .list-empty .create-btn {
    margin-top: var(--space-4);
}
/* Custom-fields panel sits on the toggle-group card (--bg-surface); declare
   the flipped control bg so form controls don't paint transparent (the
   toggle-group itself doesn't declare the companion var). */
.ui-refresh .toggle-group .webhook-custom-fields-wrapper {
    --control-bg-color: var(--bg-page);
}
/* Gap between the wrap_object toggle row and the mapping table beneath it —
   the row's own margin-top rule (~10114) only covers its *own* first child,
   not later siblings, so the table/empty-state needs its own top margin. */
.ui-refresh .webhook-custom-fields-wrapper > .list-empty,
.ui-refresh .webhook-custom-fields-wrapper > .webhook-custom-fields {
    margin-top: var(--space-4);
}
/* Custom-fields column sizing: pin handle/source/actions, let value + name share. */
.ui-refresh .webhook-custom-fields .lt-th:first-child {
    width: 36px;
}
.ui-refresh .webhook-custom-fields .lt-th:nth-child(2) {
    width: 25%;
}
.ui-refresh .webhook-custom-fields .lt-th-actions {
    width: 48px;
}
/* Edit Property action's Field/Value Pairs table (automation drawer): pin the
   trailing delete column narrow so the two select columns split the rest
   evenly — same shape as .webhook-custom-fields above. */
.ui-refresh .edit-property-fields .lt-th-actions {
    width: 48px;
}
/* The two select2 field/value columns each carry a global min-width:300px
   (dropdown-search.css), so the pair regularly exceeds even the wide
   (680px) drawer. Scroll the table horizontally within its own wrapper
   instead of letting the whole drawer page scroll — same pattern as
   .sponsor-mass-update-table-wrap above. */
.ui-refresh .edit-property-fields-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}
/* Source/Field value selects: without this, the native caret renders flush
   against the border. Reserve right-side space and swap in the tokenized
   chevron used elsewhere (e.g. the additional-reg-quantity select). */
.ui-refresh .webhook-custom-fields select.form-control,
.ui-refresh .saml-params-table select.form-control {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background-image: var(--select-chevron-image);
    background-size: 0.6em;
    background-position: calc(100% - var(--space-3)) center;
    background-repeat: no-repeat;
    padding-right: var(--space-6);
}
/* Last section shouldn't sit flush against the viewport bottom. Scoped to the
   webhook form (form.webhook-form wraps .account-form in create/update/clone)
   so other account forms keep their current rhythm. */
.ui-refresh .webhook-form .account-form {
    padding-bottom: var(--space-10);
}
/* Drag handle: legacy used a baked gripper.png (backend.css:263), which can't
   flip for surface mode — matches the fa-grip-vertical glyph + styling used
   for every other reorderable table in ui-refresh (FieldPicker, LinkCollection). */
.ui-refresh .webhook-custom-fields .handle {
    cursor: grab;
    text-align: center;
    color: var(--fg-subtle);
}
.ui-refresh .webhook-custom-fields .handle:active {
    cursor: grabbing;
}
/* Dependent pickers under a webhook toggle row match the event Settings
   treatment (flush left, no rule) instead of the Virtual Location
   indent+left-rule that #content-container .panel-expanded-settings (~6053)
   would otherwise apply — the row's own switch already communicates the
   dependency, so the extra rule reads as visual noise. Scoped to the webhook
   form (same pattern as the .mobile-app-setup override below) so other
   toggle-group pages keep their indent. */
.ui-refresh #content-container .webhook-form .selection-handling,
.ui-refresh #content-container .webhook-form .toggle-group .toggle-row > .panel-expanded-settings,
.ui-refresh .ajax-drawer .webhook-form .selection-handling,
.ui-refresh .ajax-drawer .webhook-form .toggle-group .toggle-row > .panel-expanded-settings {
    border-left: none;
    margin: 0;
    padding: 0;
}
.ui-refresh .ajax-drawer .checkbox {
    min-width: 0;
    max-width: calc(50% - var(--space-1));
}
.ui-refresh .ajax-drawer div:has(> .checkbox + .checkbox) > .checkbox > label,
.ui-refresh .ajax-drawer div:has(> .checkbox + .checkbox) > .checkbox:not(.multiselect-container .checkbox) label {
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-align: left;
}
.ui-refresh .ajax-drawer .field-picker {
    max-width: 100%;
}
.ui-refresh .ajax-drawer div:has(> .checkbox + .checkbox) > .checkbox > label::before,
.ui-refresh .ajax-drawer div:has(> .checkbox + .checkbox) > .checkbox:not(.multiselect-container .checkbox) label::before {
    margin-right: var(--space-2);
    vertical-align: middle;
}

/* ===== Webhook events popover (webhook/_show-events.php) =====
   Loaded via General::popover(ajaxUrl) into a body-portaled .popover; the
   descendant scope still matches because .ui-refresh sits on <body>. */
.ui-refresh .popover .popover-events-title {
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
    margin: 0 0 var(--space-2);
}
.ui-refresh .popover .popover-events-list {
    margin: 0;
    padding: 0;
    list-style: none;
    /* Long event lists otherwise grow the popover unbounded. */
    max-height: 240px;
    overflow-y: auto;
}
.ui-refresh .popover .popover-events-list li {
    padding: var(--space-1) 0;
}
.ui-refresh .popover .popover-events-list li + li {
    border-top: 1px solid var(--border-subtle);
}

/* ===== Speaker conflict popover (speaker/_show-session-conflicts.php) =====
   Loaded via General::popover(ajaxUrl) into a body-portaled .popover; the
   descendant scope still matches because .ui-refresh sits on <body>. */
.ui-refresh .popover .popover-conflicts-head {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0 0 var(--space-1);
}
.ui-refresh .popover .popover-conflicts-head .fa-exclamation-triangle {
    color: var(--color-warning);
    font-size: 12px;
}
.ui-refresh .popover .popover-conflicts-title {
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
}
.ui-refresh .popover .popover-conflicts-meta {
    border-bottom: 1px solid var(--panel-border);
    color: var(--fg-muted);
    padding: 0 0 var(--space-3);
    margin-bottom: var(--space-4);
}
.ui-refresh .popover .popover-conflicts-group + .popover-conflicts-group {
    margin-top: var(--space-3);
}
.ui-refresh .popover .popover-conflicts-group-label {
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
    margin: 0 0 var(--space-2);
}
.ui-refresh .popover .popover-conflicts-list {
    margin: 0;
    padding: 0;
    list-style: none;
}
.ui-refresh .popover .popover-conflicts-list li {
    display: flex;
    justify-content: space-between;
    gap: var(--space-2);
    padding: var(--space-1) 0;
    color: var(--fg-default);
}
.ui-refresh .popover .popover-conflicts-list li .popover-conflicts-time {
    color: var(--fg-muted);
    white-space: nowrap;
}
.ui-refresh .popover .popover-conflicts-list li + li {
    border-top: 1px solid var(--border-subtle);
}

/* ===== Re-trigger upload input (webhook/re-trigger.php) =====
   No global ui-refresh file-input treatment exists yet; keep this scoped to
   the webhook form until other upload pages are refreshed. */
.ui-refresh .webhook-form input[type="file"] {
    color: var(--fg-muted);
}
.ui-refresh .webhook-form input[type="file"]::file-selector-button {
    background: var(--bg-page);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--fg-default);
    font-weight: var(--font-weight-medium);
    padding: var(--space-1) var(--space-3);
    margin-right: var(--space-3);
    cursor: pointer;
    transition: background var(--transition-fast);
}
.ui-refresh .webhook-form input[type="file"]::file-selector-button:hover {
    background: var(--bg-muted);
}

.ui-refresh .additional-reg-type-choices {
    border: 1px solid var(--input-border);
    border-radius: var(--input-radius);
    background-color: var(--bg-surface);
}

.ui-refresh .additional-reg-type-choices label {
    font-size: var(--font-size-xs) !important;
}

.ui-refresh .additional-reg-type-row {
    border-top: 1px solid var(--input-border);
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-3);
}

.ui-refresh .additional-reg-type-row:first-child {
    border-top: 0;
}

.ui-refresh .group-reg-row-container {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-3);
    background-color: var(--bg-surface);
    border-radius: var(--input-radius);
    border: 1px solid var(--input-border);
}
.ui-refresh .merge-fields-dropdowns {
    background-color: var(--bg-surface);
    border: none;
    border-bottom: 1px solid var(--input-border);
    border-radius: 0 !important;
    --control-bg-color: var(--bg-page);
}

.ui-refresh .merge-fields-dropdowns .merge-field {
    border-color: var(--input-border);
    border-radius: var(--btn-radius) !important;
}

/* Translate footer mirrors the merge-field selector row so it follows the surface
   tone (dark mode) instead of staying the hardcoded light-grey from backend.css.
   Dropdowns rule off the bottom edge; the footer rules off the top edge. */
.ui-refresh .merge-fields-footer {
    background-color: var(--bg-surface);
    border: none;
    border-top: 1px solid var(--input-border);
    border-radius: 0 !important;
    margin: 0;
}
.ui-refresh .merge-fields-footer .footer-items .item {
    border: 0;
    color: var(--fg-default);
}
/* Per-language label above each translation input so it's obvious which language
   each field is for (the footer lang-name is small and easy to miss). */
.ui-refresh .translation-input-group .comm-translation-lang {
    display: block;
    margin: var(--space-4) 0 var(--space-2);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
}

.ui-refresh .translations-input .lang-name {
    color: var(--fg-default);
}

.ui-refresh .merge-field-container {
    overflow: hidden;
    background-color: var(--bg-surface);
    border: 1px solid var(--input-border);
    border-radius: var(--space-2);
}

.ui-refresh .form-control.merge-field-text-input {
    border: none;
    border-radius: 0 !important;
}

.ui-refresh .translations-input,
.ui-refresh .translation-langs {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    position: relative;
}

.ui-refresh .translations-input .enter-translations .toggle-row {
    margin-bottom: var(--space-3);
}
.ui-refresh .panel-default>.panel-heading+.panel-collapse>.panel-body {
    border: none;
}
.ui-refresh a.collapse-toggle:hover,
.ui-refresh a.collapse-toggle:focus {
    color: inherit;
}

.ui-refresh a.collapse-toggle:focus {
    outline: none;
}

body.ui-refresh .panel:not(.dash-panel) .panel-heading a .collapse-chevron {
    display: inline-block;
    transition: transform var(--transition-fast);
}
body.ui-refresh .panel:not(.dash-panel) .panel-heading a[aria-expanded="true"] .collapse-chevron {
    transform: rotate(180deg);
}

.ui-refresh .panel-swoogo-collapse .panel-title {
    display: flex;
    gap: var(--space-4);
    align-items: center;
}
/* The generic panel-heading rule (below, scoped under :is(#content-container,
   #report-content)) pads to 0 horizontally (edge-to-edge alignment
   elsewhere) and carries an ID-level selector — a class-only override here
   would lose to it regardless of how many classes it stacks, since an ID
   always outranks classes. Match that ID-level scoping so this one wins.
   Also gives the heading its own top radius (the outer .panel has no
   overflow:hidden, so a flush-background heading otherwise shows square
   corners inside the panel's rounded border) — scoped to this variant rather
   than adding overflow:hidden to every .panel, which risks clipping
   dropdowns rendered in a short .panel-body elsewhere. */
.ui-refresh :is(#content-container, #report-content) .panel-swoogo-collapse > .panel-heading {
    padding: var(--space-4) var(--space-5);
    border-top-left-radius: var(--panel-radius);
    border-top-right-radius: var(--panel-radius);
}

.ui-refresh .panel-swoogo-collapse .panel-default > .panel-heading a {
    font-size: var(--font-size-xs);
    text-transform: uppercase;
}
.ui-refresh .panel-swoogo-collapse:not(:first-child) {
    margin-top: var(--space-6);
}
.ui-refresh .panel-swoogo-collapse .panel-title::after {
    content: "";
    flex: 1 1 auto;
    height: 1px;
    background: var(--input-border);
}

.ui-refresh .sidebar-tab-panel[data-tab-panel="custom-fields"] > .panel > .panel-body {
    margin-top: var(--space-3);
}

.ui-refresh .panel-swoogo-collapse i {
    margin-right: 0;
}
.ui-refresh .form-group .radio:first-child {
    margin-top: 0;
}
.ui-refresh .form-group .radio:last-child {
    margin-bottom: 0;
}
/* !important needed to beat base.css's `.form-group label { display: block !important }`
   catch-all reset, which also matches nested .radio/.checkbox item labels since it isn't
   scoped to direct children. */
.ui-refresh .form-group .radio label {
    display: flex !important;
    align-items: center;
    color: var(--fg-default) !important;
    gap: var(--space-3);
    width: fit-content;
}
.ui-refresh .radio input[type=radio] {
    position: relative;
    margin-left: 0;
}
/* Shared refreshed radio control. The legacy backend component uses a 13px
   translucent-black ring, which is too small and disappears on dark surfaces.
   Semantic tokens keep the control visible across every surface and accent. */
.ui-refresh input[type="radio"] {
    width: 16px;
    min-width: 16px;
    height: 16px;
    border: 2px solid var(--fg-muted);
    background: var(--bg-surface);
}
.ui-refresh input[type="radio"]:checked,
.ui-refresh input[type="radio"]:not(:disabled):hover,
.ui-refresh input[type="radio"]:not(:disabled):active {
    border-color: var(--accent-on-tint);
}
.ui-refresh input[type="radio"]:checked::before {
    width: 8px;
    height: 8px;
    background: var(--accent-on-tint);
}
.ui-refresh input[type="radio"]:focus-visible {
    box-shadow: var(--focus-ring);
}
.ui-refresh input[type="radio"]:disabled {
    border-color: var(--fg-subtle);
    background: var(--bg-muted);
    cursor: not-allowed;
    opacity: var(--state-disabled-opacity);
}
.ui-refresh .field-sponsor-chat_room_id {
    margin-top: var(--space-1);
}
/* =========================================================================
   Manage Event hub (event/manage)
   Full-width page with two interchangeable layouts: a card grid
   (integration-index inspired) and a stacked list (reg-type-index
   inspired). #main-content goes viewport-wide (layout.css
   :has(.manage-hub));
   ========================================================================= */
.ui-refresh .manage-hub {
    margin: 0 auto;
    padding: 0 0 var(--space-10);
}

/* ----- Card grid layout (integration-index inspired) ----- */
.ui-refresh .manage-card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
    gap: var(--space-4);
}
.ui-refresh .manage-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    min-height: 150px;
    padding: var(--panel-padding);
    background: var(--bg-surface);
    border: 1px solid var(--border-default);
    border-radius: var(--panel-radius);
    box-shadow: var(--shadow-card);
    color: inherit;
    text-decoration: none;
    transition: transform var(--transition-fast),
                box-shadow var(--transition-fast),
                border-color var(--transition-fast);
}
.ui-refresh .manage-card:hover {
    transform: translateY(-3px);
    border-color: var(--border-strong);
    box-shadow: var(--shadow-card-hover);
    text-decoration: none;
    color: inherit;
}
.ui-refresh .manage-card:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}
.ui-refresh .manage-card-header {
    display: flex;
    gap: 1rem;
    align-items: center;
}
.ui-refresh .manage-card-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: var(--radius-md);
    background: var(--accent-subtle);
    color: var(--accent-on-tint);
    font-size: 16px;
}
.ui-refresh .manage-card-body {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}
.ui-refresh .manage-card-title {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-sm);
    color: var(--fg-default);
}
.ui-refresh .manage-card-desc {
    font-size: var(--font-size-sm);
    line-height: var(--line-height-base);
    color: var(--fg-muted);
}

/* Wizard breadcrumb button: nudge to align with the ui-refresh breadcrumb row. */
.ui-refresh .wizard-breadcrumb-btn {
    top: 5px;
}

/* Iframed modal content: match the surrounding page paper on every surface. */
.ui-refresh.iframe #modal-pjax-form {
    background: var(--paper);
}

/* Global "Saving"/"Loading" spinner overlay (App.showSpinner, e.g. form.show-spinner
   on save). The base rule hardcodes background:white with no text colour, so in
   dark mode the light body text + spinner icon vanish on the white box
   (white-on-white). Re-point at surface tokens so it stays legible on every
   surface. */
.ui-refresh #spinner {
    background: var(--bg-surface);
    color: var(--fg-default);
    border-color: var(--border-default);
}

/* AjaxDataTables' own "loading" overlay (#dt-processing, shown while a grid's
 * AJAX request is in flight — this widget backs ~68 report grids app-wide,
 * not just ui-refresh's .comm-report ones, so it can't depend on a
 * comm-report-scoped asset bundle). ajax-datatables.css's base rule is a
 * plain bordered box with a spinning Font Awesome icon; re-skin it with the
 * same ring-spinner idiom used elsewhere in ui-refresh (`ui-refresh-spin`,
 * above — form-field validation spinner) so this loading state matches the
 * rest of the system, and re-point the box itself at surface tokens (fixing
 * the same dark-mode white-on-white text issue as #spinner above).
 *
 * `position: static` overrides that same base rule's `position: fixed` —
 * confirmed live (DOM inspection) this is what was actually causing
 * communicate.css's `.dt-processing`/`.dt-scroll` centering fixes to misplace
 * the box rather than just needing tuning: a fixed-position element is out of
 * normal flow entirely, so DataTables' own wrapper (`.dt-processing`, the
 * vendor div this sits inside) collapsed to zero height around it, and
 * `transform: translateY(-50%)` on that wrapper (communicate.css) turned it
 * into a NEW containing block for this fixed child (a `transform` on an
 * ancestor does that per spec), repositioning it somewhere else again — two
 * independently-reasonable fixes compounding into worse breakage. Back in
 * normal flow, the wrapper sizes to fit it correctly and both of those
 * mechanisms work as intended. `height: auto` similarly drops the base
 * rule's fixed `height: 50px`, which doesn't match this restyled content's
 * real height. */
.ui-refresh #dt-processing {
    position: static;
    height: auto;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    background: var(--bg-surface);
    color: var(--fg-default);
    border-color: var(--border-default);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
}

.ui-refresh .dt-processing.panel.panel-default {
    border-radius: var(--radius-md);
    padding-block: 0;
}

.ui-refresh #dt-processing i {
    display: none;
}
.ui-refresh #dt-processing::before {
    content: '';
    flex: 0 0 auto;
    width: 18px;
    height: 18px;
    border: 3px solid var(--border-subtle);
    border-top-color: var(--accent-primary);
    border-radius: 50%;
    animation: ui-refresh-spin 0.6s linear infinite;
}

/* ===== Seating chart list (manage tab) =====
   Rendered in the pane (event) layout like other manage listings. #main-content
   is an overflow-y:auto scroll container, which would clip the absolutely
   positioned "Actions" dropdown menu on the last row. The bottom padding adds
   scrollable room below the list so the menu is fully reachable/visible. */
.ui-refresh .seating-chart-index {
    padding-bottom: 240px;
}
/* Each saved chart is a .panel, and `.ui-refresh #content-container .panel`
   sets overflow:hidden (for the rounded corners). That clips the absolutely
   positioned "Actions" dropdown menu to the card, so its items can't show.
   Let the menu escape the card — the #content-container id keeps this at a
   higher specificity than the base panel rule. */
.ui-refresh #content-container .seating-chart-index .panel,
.ui-refresh #content-container .seating-chart-index .panel-body {
    overflow: visible;
}
.ui-refresh .seating-chart-index .dropdown-menu {
    z-index: 1000;
}

/* ===== Spectrum colour picker (Kartik ColorInput) =====
   The vendored spectrum.css hardcodes light chrome (#ECECEC container,
   #eee buttons, #333 text) that never adapts to the surface. Re-point the
   chrome at surface tokens — it resolves to the design-system light values
   on warm/bright and flips on dark automatically. The popup is appended to
   <body>, so .ui-refresh on <body> still scopes it. The colour gradients
   (sat/hue/alpha) stay untouched — only the surrounding chrome. */
.ui-refresh .sp-container {
    background-color: var(--bg-surface);
    border-color: var(--border-default);
}
.ui-refresh .sp-picker-container {
    border-left-color: var(--border-default);
}
.ui-refresh .sp-palette-container {
    border-right-color: var(--border-default);
}
.ui-refresh .sp-input {
    color: var(--fg-default);
    background: var(--bg-page);
    border-color: var(--input-border);
}
.ui-refresh .sp-container button {
    background-image: none;
    background-color: var(--bg-page);
    border-color: var(--border-default);
    color: var(--fg-default);
}
.ui-refresh .sp-container button:hover {
    background-image: none;
    background-color: var(--state-hover);
    border-color: var(--border-strong);
    color: var(--fg-default);
}
/* Inline swatch trigger (when shown as a replacer, not flat) */
.ui-refresh .sp-replacer {
    background: var(--bg-surface);
    border-color: var(--border-default);
    color: var(--fg-default);
}
.ui-refresh .sp-replacer:hover,
.ui-refresh .sp-replacer.sp-active {
    border-color: var(--border-strong);
    color: var(--fg-default);
}
/* The ColorInput container is itself an `.input-group`, so it inherits the
   `.event-settings-content .input-group` --bg-surface fill. Clear the container
   and hex input so they match the transparent sibling inputs, and keep the
   swatch chunk (.input-group-sp) on the surface tone. */
.ui-refresh #content-container.account .event-settings-content .spectrum-group,
.ui-refresh #content-container.account .event-settings-content .spectrum-group .spectrum-input {
    background: transparent !important;
}
.ui-refresh #content-container.account .event-settings-content .spectrum-group .input-group-sp.input-group-addon {
    background: var(--bg-surface) !important;
    /* Center the swatch trigger within the addon (Bootstrap addons default to
       table-cell, so switch to flex for justify-content to take effect). */
    display: flex;
    align-items: center;
    justify-content: center;
}
/* Header/accent color pickers (custom-domain update): drop the trigger swatch
   border (and its hover/active accent border) so the swatch reads flush against
   the transparent field. Scoped to these pickers, not the global .sp-replacer. */
.ui-refresh #content-container.account .event-settings-content .spectrum-group .sp-krajee.sp-replacer,
.ui-refresh #content-container.account .event-settings-content .spectrum-group .sp-krajee.sp-replacer:hover,
.ui-refresh #content-container.account .event-settings-content .spectrum-group .sp-krajee.sp-replacer.sp-active {
    border: none;
}
/* Color preview swatch: drop the vendor's 1px dark border and round all four
   corners evenly. `overflow: hidden` clips the absolutely-positioned
   .sp-preview-inner (the actual color fill) to the rounded shape. */
.ui-refresh #content-container.account .event-settings-content .spectrum-group .sp-preview {
    border: none;
    border-radius: var(--radius-md);
    overflow: hidden;
}

/* ===========================================================================
   Wizard step bar with inline actions (WizardStepCounter `actions` slot).
   When a wizard view folds its Next/Save button into the step bar, the .wizard
   wrapper becomes a flex row: steps on the left, the action button on the
   right — one cohesive control strip. Only applies when the modifier class is
   present, so the ~60 plain wizard views are unaffected.
   =========================================================================== */
.ui-refresh .wizard.wizard--with-actions {
    display: flex;
    align-items: stretch;
}
/* Drop the legacy clearfix table pseudo-elements — they'd become stray flex items. */
.ui-refresh .wizard.wizard--with-actions::before,
.ui-refresh .wizard.wizard--with-actions::after {
    content: none;
}
.ui-refresh .wizard.wizard--with-actions > .steps-container {
    flex: 1 1 auto;
    min-width: 0;
}
.ui-refresh .wizard.wizard--with-actions > .wizard-bar__actions {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    padding: 0 var(--space-4);
    /*background: var(--card);*/
    /* Mirror the steps-container's top rounding on the trailing edge. */
    border-radius: 0 4px 0 0;
}
/* Space between the "Next" label and its trailing arrow icon. */
.ui-refresh .wizard.wizard--with-actions > .wizard-bar__actions .wizard-next-btn {
    gap: var(--space-2);
}

@media (max-width: 768px) {
    .ui-refresh .wizard.wizard--with-actions {
        flex-direction: column;
        align-items: stretch;
    }
    .ui-refresh .wizard.wizard--with-actions > .wizard-bar__actions {
        justify-content: flex-end;
        padding: var(--space-2) var(--space-3);
    }
}

/* ===========================================================================
   Schedule form (email-blast). Renders in a slideout iframe that loads
   components.css but NOT communicate.css, so this styling lives here.
   - Date + Time share one row (.comm-dt-row); Timezone gets its own row.
   - The timezone choice renders as a segmented control (matching the
     Send Later / Send Now toggle) instead of bare radios, which read as
     plain text in this iframe and aren't obviously selectable.
   =========================================================================== */
.ui-refresh .comm-dt-row {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}
.ui-refresh .comm-dt-row > [class*="col-sm"] {
    flex: 0 0 auto;
    width: 100%;
    max-width: none;
    min-width: 0;
    padding-left: 0;
    padding-right: 0;
}

.ui-refresh .swoogo-switch-toggle {
    max-width: 100%;
}

/* Timezone label sits above the control (like Date / Time), not inline-left */
.ui-refresh .comm-tz-field > label,
.ui-refresh .comm-tz-field > .control-label {
    display: block;
    float: none;
    width: 100%;
    margin-bottom: var(--space-2);
}

/* Timezone as two separated segmented cards (two-line: bold name over the muted
   UTC label). Selected card uses the lavender accent tint, not a solid fill. */
.ui-refresh .comm-tz-field .radio {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    border: 0;
    background: transparent !important;
    padding: 0 !important;
    width: 100%;
}
.ui-refresh .comm-tz-field .radio label {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 2px;
    flex: 1 1 0;
    min-width: 0;
    margin: 0;
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--line-2);
    border-radius: var(--radius-lg);
    background: var(--card);
    color: var(--muted);
    cursor: pointer;
    font-weight: var(--font-weight-medium);
    transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}
.ui-refresh .comm-tz-field .radio label .comm-seg-title {
    font-weight: var(--font-weight-semibold);
    font-size: var(--font-size-base);
    line-height: 1.3;
}
.ui-refresh .comm-tz-field .radio label .comm-seg-sub {
    font-weight: var(--font-weight-normal);
    font-size: var(--font-size-sm);
    line-height: 1.3;
    opacity: .9;
}
.ui-refresh .comm-tz-field .radio input[type="radio"] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
    width: 0 !important;
    height: 0 !important;
    margin: 0;
}
.ui-refresh .comm-tz-field .radio label:has(input[type="radio"]:checked) {
    background: var(--accent-subtle);
    color: var(--accent-on-tint);
    border-color: color-mix(in srgb, var(--accent-primary) 45%, transparent);
}
/* The generic iframe segmented-radio rule (body.iframe.ui-refresh .form-group >
   .radio[role="radiogroup"]) is more specific and would otherwise re-pill the
   timezone container back into a connected group. Re-assert the separated-card
   container at matching specificity (and later in source) so the cards win. */
body.iframe.ui-refresh .comm-tz-field .radio[role="radiogroup"] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    border: 0;
    background: transparent;
    padding: 0;
    margin-top: 0;
}

/* Send Later / Send Now — render the binary toggle as two separated segmented
   buttons matching the timezone cards (lavender tint when active), instead of the
   default sliding pill. Scoped to .comm-form so other switch-toggles are untouched. */
.ui-refresh .comm-form .swoogo-switch-toggle_wrap {
    margin-top: var(--space-2);
}
.ui-refresh .comm-form .swoogo-switch-toggle {
    display: flex;
    gap: var(--space-2);
    width: 100%;
    max-width: none;
    border: 0;
    overflow: visible;
    background: transparent;
}
.ui-refresh .comm-form .swoogo-switch-toggle .toggle-background {
    display: none;
}
.ui-refresh .comm-form .swoogo-switch-toggle label.toggle-btn-off,
.ui-refresh .comm-form .swoogo-switch-toggle label.toggle-btn-on {
    flex: 1 1 0;
    margin: 0 !important;
    padding: var(--space-3) var(--space-4) !important;
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--line-2);
    border-radius: var(--radius-lg);
    background: var(--card);
    color: var(--muted) !important;
    font-weight: var(--font-weight-semibold);
    opacity: 1 !important;
    transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}
.ui-refresh .comm-form .swoogo-switch-toggle input.toggle-input:not(:checked) ~ label.toggle-btn-off,
.ui-refresh .comm-form .swoogo-switch-toggle input.toggle-input:checked ~ label.toggle-btn-on {
    background: var(--accent-subtle);
    color: var(--accent-on-tint) !important;
    border-color: color-mix(in srgb, var(--accent-primary) 45%, transparent);
}

/* Flush text panels — content panels (settings, grouped email sections) sit
   flush on the page surface instead of being a white box. Border/shadow are
   already absent in ui-refresh; this drops the white fill so the panel follows
   the surface tone. Cards keep their fill — only .panel is flattened. */
.ui-refresh .panel.comm-flush-panel,
.ui-refresh .panel.comm-group-panel {
    background: transparent !important;
}

/* Let the Invitation Statuses multiselect dropdown escape the panel — ui-refresh
   panels clip with overflow:hidden, which cut the open dropdown down to one row.
   !important beats the .panel.panel-default overflow rule. */
.ui-refresh .comm-overflow-visible,
.ui-refresh .comm-overflow-visible .panel-body {
    overflow: visible !important;
}

/* ===========================================================================
   Wizard step bar — surface-tone support. Base wizard.css hardcodes white steps,
   grey chevrons, a light-grey "complete" and a blue "active", none of which
   re-resolve, so the bar renders light-on-dark. Recolour every state with
   tokens (which re-resolve per surface) — no surface-specific selectors needed.
   Higher specificity than the base `.wizard > ul.steps li` rules so it wins
   regardless of stylesheet order.
   =========================================================================== */
.ui-refresh .wizard {
    box-shadow: var(--shadow-card);
    border-top-color: var(--line);
}
.ui-refresh .wizard ul.steps li {
    background: var(--card);
    color: var(--muted);
}
.ui-refresh .wizard ul.steps li .chevron {
    border-left-color: var(--line-2);
}
.ui-refresh .wizard ul.steps li .chevron:before {
    border-left-color: var(--card);
}
.ui-refresh .wizard ul.steps li.complete {
    background: var(--bg-muted);
    color: var(--accent-primary);
}
.ui-refresh .wizard ul.steps li.complete:hover {
    background: var(--accent-subtle);
}
.ui-refresh .wizard ul.steps li.complete .chevron:before {
    border-left-color: var(--bg-muted);
}
.ui-refresh .wizard ul.steps li.complete:hover .chevron:before {
    border-left-color: var(--accent-subtle);
}
.ui-refresh .wizard ul.steps li.active,
.ui-refresh .wizard ul.steps li.active:hover {
    background: var(--accent-primary);
    color: var(--fg-on-accent);
}
.ui-refresh .wizard ul.steps li.active > a {
    color: var(--fg-on-accent);
}
.ui-refresh .wizard ul.steps li.active .chevron:before {
    border-left-color: var(--accent-primary);
}
.ui-refresh .wizard ul.steps li.active .badge {
    background-color: var(--fg-on-accent);
    color: var(--accent-primary);
}
.ui-refresh .wizard .step-content {
    border-top-color: var(--line);
    /* .modal-content is already --bg-surface (card); painting this the same
       card fill made the step area visually indistinguishable from the
       modal shell around it. --bg-page reads as an inset well instead. */
    background: var(--bg-page);
    --control-bg-color: var(--bg-surface);
}

/* ===========================================================================
   Communicate forms — panels sit flush on the page surface (same treatment as
   the invitation-list settings panel). Tagging the form with .comm-form flushes
   every panel inside it in one rule. Cards (if any) are untouched. !important
   beats the white .panel.panel-default fill. Page-level comm-forms only — a
   comm-form in an AjaxDrawer keeps the drawer's own panel chrome.
   =========================================================================== */
.ui-refresh .comm-form .panel.panel-default:not(.ajax-drawer *) {
    /* Flat sections like event setup — no white box, border or shadow; just
       breathing room between sections. flow-root contains floated col-sm-* fields
       so following content can't wrap beside them. (Most panels are now real
       <section> elements; this covers any that remain, e.g. headerless footers
       and the nested meeting-types block.) */
    background: transparent !important;
    border: 0;
    box-shadow: none;
    display: flow-root;
    margin-bottom: var(--space-8);
}
/* The heading itself uses the shared .event-settings-section-heading rule above
   (uppercase eyebrow + trailing rule). The panel-heading has no
   .event-settings-section-head wrapper, so restore the section's bottom spacing
   and clear the legacy panel-heading chrome here. Page-level comm-forms only —
   inside an AjaxDrawer, panel headings keep the drawer's own
   .sidebar-panel-heading treatment. */
.ui-refresh .comm-form .panel.panel-default > .panel-heading:not(.ajax-drawer *) {
    margin-bottom: var(--space-5);
    background: transparent;
    border-radius: 0;
}
.ui-refresh .comm-form .panel.panel-default > .panel-body {
    padding: 0;
}

/* Flat <section> blocks (panel-free markup) — same rhythm as event setup.
   flow-root contains any floated Bootstrap col-sm-* fields inside the section so
   the next section can't wrap up beside them. */
.ui-refresh .sidebar-title + .comm-form-section {
    padding-top: 2rem;
}
/* The first section's own .event-settings-section-head:first-child rule
   below also adds a top margin, meant for main-content pages that don't sit
   right under a title bar. Stacked on top of the padding-top above, that
   doubled up to ~52px of dead space at the top of a sidebar form — zero it
   here so the sidebar-title rule is the only source of that gap. */
.ui-refresh .sidebar-title + .comm-form-section .event-settings-section-head:first-child {
    margin-top: 0;
}
.ui-refresh .comm-form-section {
    display: flow-root;
    margin-bottom: var(--space-4);
}
.ui-refresh .comm-form-section:last-of-type {
    margin-bottom: 0;
}

/* Discount view (and other read-only detail pages that opt in via
   `.detail-cards`) host each section in a card surface — the flat
   comm-form-section treatment reads too bare for a standalone detail page.
   Reuses the canonical card tokens (same as .fee-grid-section-body card). */
.ui-refresh .detail-cards .comm-form-section {
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--panel-radius);
    box-shadow: var(--shadow-card);
    padding: var(--space-4);
    margin-bottom: var(--space-4);
}
.ui-refresh .detail-cards .comm-form-section .event-settings-section-head {
    margin-bottom: var(--space-3);
}

/* Paired section row (e.g. email form: General Details + Who should receive
   the alert?, side by side). Each column keeps its own normal
   .comm-form-section (heading + trailing rule), just laid out side by side —
   margin-bottom is suppressed on the columns so the row's own bottom margin
   supplies the gap before the next section instead of doubling it up. */
.ui-refresh .comm-form-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-8);
    margin-bottom: var(--space-4);
}
.ui-refresh .comm-form-row .comm-form-section {
    margin-bottom: 0;
}
@media (max-width: 900px) {
    .ui-refresh .comm-form-row {
        grid-template-columns: 1fr;
        gap: 0;
    }
}

/* Internal 2-column field grid within a section — e.g. the email form's
   Sender, CC & BCC fields (From name/From email, Reply to/CC). */
.ui-refresh .comm-form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 var(--space-6);
}
@media (max-width: 900px) {
    .ui-refresh .comm-form-grid {
        grid-template-columns: 1fr;
    }
}

/* Collapsible section (e.g. email form "Advanced settings") — collapsed by
   default, a slim button-reset row with the same heading typography as a
   normal .comm-form-section, chevron rotates open/closed via aria-expanded. */
.ui-refresh .comm-collapsible {
    margin-bottom: var(--space-4);
}
.ui-refresh .comm-collapsible-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 0;
    background: transparent;
    border: 0;
    cursor: pointer;
    text-align: left;
}
.ui-refresh .comm-collapsible-chevron {
    color: var(--fg-muted);
    transition: transform var(--transition-fast);
}
.ui-refresh .comm-collapsible-toggle[aria-expanded="true"] .comm-collapsible-chevron {
    transform: rotate(180deg);
}
.ui-refresh .comm-collapsible-body {
    margin-top: var(--space-4);
}

/* Email create/update form (email-designer/settings.php, email/create.php,
   email/update.php). Sections are separated by whitespace and their headings
   only — no hairline dividers (design language: spacing + heading hierarchy
   define regions, not borders). Scoped to `.email-form-page` rather than the
   shared `.comm-form`/`.comm-form-section` classes, which many forms reuse. */
.ui-refresh .email-form-page {
    min-width: 0; /* grid column: let wide TranslationInput rows shrink, not overflow */
    padding-bottom: var(--space-12);
}
/* The sticky header (or the wizard bar on create/clone) already supplies the
   gap above the first section — don't stack the section-head's own top margin. */
.ui-refresh .email-form-page .event-settings-section-head:first-child {
    margin-top: 0;
}
/* One rhythm for every top-level block: plain sections, the paired Sender/CC
   row and the Advanced Settings collapsible. scroll-margin keeps a TOC jump
   target clear of the sticky header (subpage-head-sticky.js mirrors the bar's
   height into --subpage-head-height on #main-content). */
.ui-refresh .email-form-page .comm-form-section,
.ui-refresh .email-form-page .comm-form-row,
.ui-refresh .email-form-page .comm-collapsible {
    margin-bottom: var(--space-10);
    scroll-margin-top: calc(var(--subpage-head-height, 80px) + var(--space-4));
}
.ui-refresh .email-form-page .comm-form-row .comm-form-section {
    margin-bottom: 0;
}
/* The options card follows the Notes field; the field's own 20px bottom
   margin is the gap, not that plus the group's default 20px top margin. */
.ui-refresh .email-form-page .form-group + .toggle-group {
    margin-top: 0;
}
.ui-refresh .email-form-page > :last-child {
    margin-bottom: 0;
}
/* The doc grid (form column + TOC rail) nests inside .list-page, which already
   centres and gutters the content — same reset as #event-wizard-container. */
.ui-refresh .list-page > .event-settings-doc {
    width: 100%;
    padding-top: 0;
}
/* Create/clone: the wizard bar sits directly under the sticky breadcrumb bar.
   Its default 20px top margin and 16px tier-1 padding stacked on the bar's own
   16px bottom padding and .list-page's 8px top padding put the step pills 60px
   below the crumb row. Drop both so the pills sit 24px below it — the same gap
   the first section heading has on the update page. :has(.email-form-page)
   keeps this off invitation-list/create.php, which composes the same way. */
.ui-refresh .list-page:has(.email-form-page) > .wsc-header {
    margin-top: 0;
}
.ui-refresh .list-page:has(.email-form-page) > .wsc-header .wsc-tier1 {
    padding-top: 0;
}

/* ===========================================================================
   "Who should receive the alert?" — modern recipients component (ui-refresh).
   A stacked list of recipient rows (email icon + input + remove) with an
   "Add recipient" button, replacing the old striped table. Token-based.
   =========================================================================== */
.ui-refresh .comm-recipients-hint {
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
    margin-bottom: var(--space-3);
}
.ui-refresh .comm-recipients {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
    max-width: 520px;
}
.ui-refresh .comm-recipient-row {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}
.ui-refresh .comm-recipient-row.is-template {
    display: none;
}
.ui-refresh .comm-recipient-line {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}
.ui-refresh .comm-recipient-field {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
}
.ui-refresh .comm-recipient-icon {
    position: absolute;
    top: 50%;
    left: var(--space-3);
    transform: translateY(-50%);
    color: var(--fg-subtle);
    pointer-events: none;
}
.ui-refresh .comm-recipient-field > .form-control {
    width: 100%;
    padding-left: calc(var(--space-3) + 18px + var(--space-2));
}
.ui-refresh .comm-recipient-remove {
    flex: 0 0 auto;
    width: var(--input-height);
    height: var(--input-height);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--input-border);
    border-radius: var(--input-radius);
    background: transparent;
    color: var(--fg-muted);
    cursor: pointer;
    transition: color var(--transition-fast), border-color var(--transition-fast), background var(--transition-fast);
}
.ui-refresh .comm-recipient-remove:hover {
    color: var(--color-error);
    border-color: var(--color-error);
    background: var(--color-error-bg);
}
.ui-refresh .comm-recipient-error {
    flex: 1 1 100%;
    padding-left: calc(18px + var(--space-2));
    color: var(--color-error);
    font-size: var(--font-size-xs);
}
.ui-refresh .comm-recipient-row.has-error .form-control {
    border-color: var(--color-error);
}

/* "+ Add recipient" — a low-emphasis affordance at rest (dashed border, muted
   opacity) that firms up on hover so it doesn't compete with the recipient
   rows above it. */
.ui-refresh .comm-recipient-add {
    border-style: dashed;
    opacity: 0.65;
    transition: opacity var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .comm-recipient-add:hover,
.ui-refresh .comm-recipient-add:focus {
    opacity: 1;
    border-color: var(--fg-muted);
}

/* ===== Integration index cards (ui-refresh) =====
   Overrides integration-panels.css which uses !important throughout.
   Scoped to .ui-refresh so the non-refresh path is unaffected. */
.ui-refresh #integration-container .integration-box {
    background: var(--card);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg) !important;
    box-shadow: var(--shadow-sm) !important;
    padding: var(--space-2) var(--space-4) var(--space-4);
    min-height: 168px;
    transition: transform var(--transition-fast), box-shadow var(--transition-fast) !important;
}
.ui-refresh #integration-container .integration-box:hover {
    background: var(--card) !important;
    transform: translateY(-2px);
    box-shadow: var(--shadow-md) !important;
}

/* Flex header: logo + title left, Active badge right, vertically centered.
   Neutralizes the legacy pull-left / pull-right floats. */
.ui-refresh #integration-container .panel-heading {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    border-bottom: none;
    padding: var(--space-3) 0 var(--space-2);
}
/* The heading retains .clearfix from the legacy float layout. Its ::before
   becomes a phantom flex item that the gap pushes the icon 12px to the right.
   Kill both pseudo-elements since flex handles the layout now. */
.ui-refresh #integration-container .panel-heading::before,
.ui-refresh #integration-container .panel-heading::after {
    display: none;
}
.ui-refresh #integration-container .panel-heading .pull-left,
.ui-refresh #integration-container .panel-heading .pull-right {
    float: none !important;
}
.ui-refresh #integration-container .list-group-item-heading {
    width: 28px;
    height: 28px;
    margin: 0;
    flex-shrink: 0;
}
.ui-refresh #integration-container .panel-title {
    margin: 0;
    padding: 0;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    color: var(--ink);
    line-height: 1.2;
}
.ui-refresh #integration-container .panel-heading > .pull-right {
    display: flex;
    align-items: center;
    margin-left: auto;
    flex-shrink: 0;
    min-width: max-content;
}

/* Inactive badge — same shape as active, muted grey (matches draft event status pill) */
.ui-refresh #integration-container .inactive-label {
    display: inline-flex !important;
    align-items: center;
    position: static;
    top: auto;
    width: auto !important;
    height: auto !important;
    padding: 4px var(--space-2) 3px;
    font-family: var(--font-family);
    font-size: 10px;
    font-weight: var(--font-weight-medium);
    line-height: 1;
    vertical-align: middle;
    white-space: nowrap;
    color: var(--fg-muted);
    background: var(--bg-muted);
    border: 1px solid var(--input-border);
    border-radius: var(--radius-full);
}

/* Active badge — matches event-status-pill[data-status="live"] style.
   integration-panels.css locks width:38px/height:12px — override both. */
.ui-refresh #integration-container .active-label {
    display: inline-flex !important;
    align-items: center;
    position: static;
    top: auto;
    width: auto !important;
    height: auto !important;
    padding: 4px var(--space-2) 3px;
    font-family: var(--font-family);
    font-size: 10px;
    font-weight: var(--font-weight-medium);
    line-height: 1;
    vertical-align: middle;
    white-space: nowrap;
    color: var(--color-success);
    background: var(--color-success-bg);
    border: 1px solid var(--color-success-bg);
    border-radius: var(--radius-full);
}

/* Description */
.ui-refresh #integration-container .panel-body {
    padding: 0;
}
.ui-refresh #integration-container .list-group-item-text {
    margin: 0;
    color: var(--muted);
    font-size: var(--font-size-sm);
    line-height: 1.5;
    text-align: left;
}
.ui-refresh #integration-container .integration-inactive,
.ui-refresh #integration-container .integration-active {
    color: var(--muted);
}

/* Go Attend announcements list — status pill */
.ui-refresh .list-table .lt-status {
    display: inline-flex;
    align-items: center;
    padding: 4px var(--space-2) 3px;
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    line-height: 1;
    white-space: nowrap;
    border-radius: var(--radius-full);
    color: var(--fg-muted);
    background: var(--bg-muted);
    border: 1px solid var(--input-border);
}
.ui-refresh .list-table .lt-status[data-status="sent"] {
    color: var(--color-success);
    background: var(--color-success-bg);
    border-color: var(--color-success-bg);
}
.ui-refresh .list-table .lt-status[data-status="sending"] {
    color: var(--color-warning);
    background: var(--color-warning-bg);
    border-color: var(--color-warning-bg);
}

/* Go Attend staff full-sync console */
.ui-refresh .go-attend-sync-page {
    /* Clear the 35px back-to-top pill plus its 16px viewport offset. */
    padding-bottom: calc(var(--space-12) + var(--space-6));
}
.ui-refresh .go-attend-sync-warning,
.ui-refresh .go-attend-sync-error {
    margin-bottom: var(--space-4);
}
.ui-refresh .go-attend-sync-console {
    overflow: hidden;
    background: var(--card);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--sh-sm);
}
.ui-refresh .go-attend-sync-console__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-4) var(--space-6);
    border-bottom: 1px solid var(--line);
}
.ui-refresh .go-attend-sync-console__head h2 {
    margin: 0 0 var(--space-1);
    color: var(--ink);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-lg);
}
.ui-refresh .go-attend-sync-console__head p {
    margin: 0;
    color: var(--muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-sm);
}
.ui-refresh .go-attend-sync-console__count {
    flex: 0 0 auto;
    padding: 4px var(--space-2) 3px;
    color: var(--muted);
    background: var(--paper-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-full);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    line-height: 1;
}
.ui-refresh .go-attend-sync-list {
    display: flex;
    flex-direction: column;
}
.ui-refresh .go-attend-sync-row {
    display: grid;
    grid-template-columns: 40px minmax(0, 1fr) 116px 104px;
    align-items: center;
    gap: var(--space-4);
    min-height: 76px;
    padding: var(--space-3) var(--space-6);
    border-bottom: 1px solid var(--panel-border);
    transition: background var(--transition-fast);
}
.ui-refresh .go-attend-sync-row:last-child {
    border-bottom: 0;
}
.ui-refresh .go-attend-sync-row:hover {
    background: var(--paper-2);
}
.ui-refresh .go-attend-sync-row--all {
    background: var(--accent-subtle);
}
.ui-refresh .go-attend-sync-row--all:hover {
    background: var(--accent-muted);
}
.ui-refresh .go-attend-sync-row__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    color: var(--muted);
    background: var(--paper-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    font-size: var(--font-size-lg);
}
.ui-refresh .go-attend-sync-row--all .go-attend-sync-row__icon {
    color: var(--accent-primary);
    background: var(--card);
    border-color: var(--accent-muted);
}
.ui-refresh .go-attend-sync-row__content h3 {
    margin: 0 0 2px;
    color: var(--ink);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-sm);
}
.ui-refresh .go-attend-sync-row__content p {
    margin: 0;
    color: var(--muted);
    font-size: var(--font-size-xs);
    line-height: var(--line-height-xs);
}
.ui-refresh .go-attend-sync-row__status {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-warning);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    white-space: nowrap;
}
.ui-refresh .go-attend-sync-row__status-dot {
    width: 7px;
    height: 7px;
    background: var(--color-warning);
    border-radius: var(--radius-full);
    box-shadow: 0 0 0 4px var(--color-warning-bg);
}
.ui-refresh .go-attend-sync-row__button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    grid-column: 4;
    width: 104px;
    justify-self: end;
}

@media (max-width: 767px) {
    .ui-refresh .go-attend-sync-console__head {
        align-items: flex-start;
        padding: var(--space-4);
    }
    .ui-refresh .go-attend-sync-row {
        grid-template-columns: 40px minmax(0, 1fr) auto;
        gap: var(--space-3);
        padding: var(--space-4);
    }
    .ui-refresh .go-attend-sync-row__status {
        grid-column: 2;
    }
    .ui-refresh .go-attend-sync-row__button {
        grid-column: 3;
        grid-row: 1 / span 2;
        width: auto;
        min-width: 88px;
    }
}

@media (max-width: 520px) {
    .ui-refresh .go-attend-sync-console__count {
        display: none;
    }
    .ui-refresh .go-attend-sync-row {
        grid-template-columns: 40px minmax(0, 1fr);
    }
    .ui-refresh .go-attend-sync-row__button {
        grid-column: 2;
        grid-row: auto;
        justify-self: start;
        width: 104px;
    }
}

/* Go Attend announcements drawer (ajax-drawer/announcement-upsert.php) */
/* The Visibility panel's helper paragraph is the segment picker's
   description — it should hug the Select2 below it. Both gaps (heading→p
   and p→field) were --space-3/--space-2+--space-1, reading as equal
   spacing that made the field look detached from its own description
   instead of paired with it. */
.ui-refresh .ajax-drawer .panel-body > p:has(+ .field-goattendannouncement-segment_ids) {
    margin-bottom: var(--space-1);
}
.ui-refresh .ajax-drawer .field-goattendannouncement-segment_ids {
    margin-top: 0;
}
.ui-refresh .ajax-drawer .field-goattendannouncement-segment_ids .select2-search__field {
    padding: 6px 6px 3px 6px !important;
}
.ui-refresh .ajax-drawer .field-goattendannouncement-segment_ids .select2-selection__choice__remove {
    margin-top: -1px !important;
}
/* "Send Now / Schedule for Later" inline radio group — the AjaxDrawer
   equivalent of the segmented-pill treatment used for inline radio-inline
   groups in body.iframe.ui-refresh (see the "Open In" rule above); that
   selector never matches here because AjaxDrawer markup isn't inside an
   iframe, so without this the pair falls back to unstyled native radios
   (misaligned wrapping + OS-themed dot). */
.ui-refresh .ajax-drawer .send-timing-radios {
    display: inline-flex;
    background: var(--paper-2);
    border: 1px solid var(--input-border);
    border-radius: var(--radius-lg);
    padding: 3px;
    gap: 2px;
    margin-bottom: var(--space-2);
}
.ui-refresh .ajax-drawer .send-timing-radios .radio-inline {
    display: inline-flex;
    align-items: center;
    margin: 0 !important;
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-md);
    cursor: pointer;
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    transition: var(--transition-fast);
    line-height: 1;
    min-height: 28px;
}
.ui-refresh .ajax-drawer .send-timing-radios .radio-inline:hover {
    color: var(--fg-default);
}
.ui-refresh .ajax-drawer .send-timing-radios .radio-inline:has(input:checked) {
    background: var(--primary);
    color: #fff;
}
.ui-refresh .ajax-drawer .send-timing-radios input[type="radio"] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
    width: 0;
    height: 0;
    margin: 0;
}
.ui-refresh .ajax-drawer .send-timing-radios .radio-inline:focus-within {
    box-shadow: 0 0 0 3px var(--primary-ring);
    outline: 0;
}

/* Grid gutter */
.ui-refresh #integration-container .outer-box {
    margin-bottom: var(--space-4);
}
/* plugins/integration-panels/css/integration-panels.css sets .outer-box's
   side padding to 8px, but #integration-panels-row still carries Bootstrap's
   default .row margin (-15px), which assumes a 15px column padding to cancel
   it back out. The 15 vs 8 mismatch left every card inset 7px past
   .list-page's edge on each side — invisible on the legacy grid (nothing
   else to align to) but visible now that .subpage-head/.list-tab-row above
   it are centered precisely to .list-page's own width. Match the row's
   margin to the actual 8px gutter instead of touching the legacy padding. */
.ui-refresh #integration-panels-row {
    margin-left: -8px;
    margin-right: -8px;
}

/* Event rails leave too little room for three integration cards between the
   desktop Bootstrap breakpoint and 1200px. Use two columns through that
   intermediate range so each card retains enough header space for its status
   badge; three columns resume once the badge fits without overflow. */
@media (min-width: 993px) and (max-width: 1199px) {
    body.ui-refresh.has-event-icon-nav #integration-container .outer-box {
        width: 50%;
    }
}

/* The event rail makes the 993px–1450px integration cards narrow enough for
   longer descriptions to wrap beyond integration-panels.css's 210px height.
   Give every card in this grid the same taller height so text stays contained
   without letting Bootstrap's floated columns form an uneven masonry layout. */
@media (min-width: 993px) and (max-width: 1450px) {
    body.ui-refresh.has-event-icon-nav #integration-container .integration-box {
        height: 250px !important;
    }
}

/* ===== Languages page =====
   Intro mirrors .themes-intro (components.css:4241). Card grid uses the
   platform-card selectable-card pattern from the v4 prototype: whole card
   is the control, selected = accent border + tint + filled check circle.
   Checkboxes are kept in the DOM (form submission + JS) but hidden visually. */

/* ---- Card grid ---- */
.ui-refresh .lang-card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
    gap: var(--space-3);
    margin-bottom: var(--space-8);
}

/* ---- Selectable language card ---- */
.ui-refresh .lang-card {
    background: var(--card);
    border: 1px solid var(--input-border);
    border-radius: 10px;
    padding: var(--space-3) var(--space-8);
    display: flex;
    align-items: center;
    gap: var(--space-2);
    cursor: pointer;
    margin: 0;
    font-weight: var(--font-weight-medium);
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast), background var(--transition-fast);
    /* Needed so the absolutely-positioned hidden checkbox doesn't escape */
    position: relative;
}

/* Hide the real checkbox visually — keep it focusable for keyboard nav */
.ui-refresh .lang-card input[type="checkbox"] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
    width: 0;
    height: 0;
}

.ui-refresh .lang-card:not(.locked):hover {
    border-color: var(--line-2);
    box-shadow: var(--shadow-sm);
}

.ui-refresh .lang-card.selected {
    border-color: var(--primary);
    background: var(--primary-tint);
}

/* Keyboard focus ring: distinct from selected so they're not confused */
.ui-refresh .lang-card:has(input:focus-visible) {
    outline: none;
    box-shadow: var(--focus-ring);
    border-color: var(--primary);
}

.ui-refresh .lang-card.locked {
    cursor: default;
}

/* ---- Card internals ---- */
.ui-refresh .lang-card-flag {
    font-size: 16px;
    flex-shrink: 0;
    line-height: 1;
}

/* language-card-name: keep this class name — JS reads it for dropdown labels */
.ui-refresh .lang-card .language-card-name {
    font-size: 13px;
    color: var(--ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
    flex: 1;
}

.ui-refresh .lang-card-meta {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    flex-shrink: 0;
}

.ui-refresh .lang-tag {
    display: inline-block;
    font-family: var(--font-family);
    font-size: 10px;
    font-weight: var(--font-weight-medium);
    line-height: 1;
    padding: 4px var(--space-2) 3px;
    border-radius: var(--radius-full);
    border: 1px solid var(--accent-muted);
    background: var(--accent-subtle);
    color: var(--accent-hover);
    flex-shrink: 0;
    white-space: nowrap;
}
.ui-refresh .lang-tag--locked {
    background: var(--bg-muted);
    color: var(--fg-muted);
    border-color: var(--border-default);
}

/* Check circle — unfilled by default, filled when selected */
.ui-refresh .lang-card-check {
    width: 16px;
    height: 16px;
    border: 1.5px solid var(--line-2);
    border-radius: 50%;
    display: grid;
    place-items: center;
    flex-shrink: 0;
    margin-left: auto;
    transition: background var(--transition-fast), border-color var(--transition-fast);
}
.ui-refresh .lang-card-check i {
    font-size: 8px;
    color: var(--fg-on-accent);
    opacity: 0;
    transition: opacity var(--transition-fast);
}
.ui-refresh .lang-card.selected .lang-card-check {
    background: var(--primary);
    border-color: var(--primary);
}
.ui-refresh .lang-card.selected .lang-card-check i {
    opacity: 1;
}
/* Default/locked language: dim the checkmark to signal it can't be unselected */
.ui-refresh .lang-card.locked.selected .lang-card-check {
    background: var(--border-default);
    border-color: var(--border-default);
    opacity: 0.6;
}

/* ---- Other settings section ----
   No standalone top border — the heading uses a flex ::after line
   (same pattern as .event-settings-section-heading) so the divider
   runs inline to the right of the eyebrow label. */
.ui-refresh .lang-other-settings {
    padding-top: var(--space-6);
    margin-top: var(--space-2);
}
.ui-refresh .lang-other-settings-heading {
    font-size: 11px;
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
    margin: 0 0 var(--space-4);
    line-height: 1.2;
}
.ui-refresh .lang-other-settings-lede {
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
    margin: 0 0 var(--space-4);
    line-height: 1.5;
}
.ui-refresh .lang-other-settings .toggle-group {
    margin-top: var(--space-5);
}

/* Pin control-label color — active-form.css uses !important to darken
   .has-success/.has-error labels on every radio click. Same fix as
   body.iframe.ui-refresh (components.css ~4678). */
.ui-refresh .lang-other-settings .has-success .control-label,
.ui-refresh .lang-other-settings .has-error .control-label {
    color: var(--fg-muted) !important;
}

/* Label is flush with the toggle-row content edge (no padding).
   .radio wrappers get margin-left: 20px so Bootstrap's radio input
   (which uses margin-left: -20px) lands at the same left as the label text,
   rather than overflowing the card's left border. */
.ui-refresh .language-change-choice {
    padding-left: 0;
}
.ui-refresh .language-change-choice .radio {
    margin-left: var(--space-5);
}

/* ============================================================
   Sponsor setup page — ui-refresh list view
   ============================================================ */

/* ---- Tier group headers ---- */
.ui-refresh .sponsor-group + .sponsor-group {
    margin-top: var(--space-8);
}

.ui-refresh .lg-head {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) 0 var(--space-3);
}

.ui-refresh .lg-eye,
.ui-refresh .lg-head .lg-eye {
    order: 1;
    font-size: 13px;
    font-weight: 600;
    color: var(--ink-2, var(--muted));
    white-space: nowrap;
}

.ui-refresh .lg-count {
    order: 2;
    font-size: 13px;
    font-weight: 400;
    color: var(--muted);
    white-space: nowrap;
}

.ui-refresh .lg-rule {
    order: 3;
    flex: 1;
    height: 1px;
    background: var(--line);
    min-width: var(--space-4);
}

/* ---- Sponsor grid ---- */
.ui-refresh .sponsor-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--space-4);
}

@media (max-width: 1100px) {
    .ui-refresh .sponsor-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

@media (max-width: 760px) {
    .ui-refresh .sponsor-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 480px) {
    .ui-refresh .sponsor-grid {
        grid-template-columns: 1fr;
    }
}

/* ---- Sponsor card ---- */
.ui-refresh .sponsor-card {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-6);
    background: var(--card);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    text-align: center;
    cursor: pointer;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.ui-refresh .sponsor-card:hover {
    box-shadow: var(--shadow-card-hover);
}

/* Logo — image variant */
.ui-refresh .sponsor-logo-img {
    width: 64px;
    height: 64px;
    object-fit: contain;
    border-radius: var(--radius-md);
    border: 1px solid var(--panel-border);
    background: var(--paper-2, var(--paper));
}

/* Logo — monogram variant */
.ui-refresh .sponsor-logo-mono {
    width: 64px;
    height: 64px;
    border-radius: var(--radius-md);
    display: grid;
    place-items: center;
    font-size: 22px;
    font-weight: 600;
    color: var(--fg-default);
    letter-spacing: -0.01em;
    user-select: none;
    flex-shrink: 0;
}

/* Sponsor name — inline editable input, centered */
.ui-refresh .sponsor-card .sponsor-name.sr-title-input {
    display: block;
    width: 100%;
    text-align: center;
    font-family: var(--font-family);
    font-size: 13px;
    font-weight: 600;
    color: var(--ink);
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    box-shadow: none;
    padding: var(--space-1) var(--space-2);
    transition: background var(--transition-fast), border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.ui-refresh .sponsor-card .sponsor-name.sr-title-input:hover {
    background: var(--paper-2);
}

.ui-refresh .sponsor-card .sponsor-name.sr-title-input:focus {
    background: var(--card);
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-ring);
    outline: none;
}

/* ---- Tier chip (card + table) ---- */
.ui-refresh .sponsor-tier-chip {
    position: relative;
}

/* The chip trigger button */
.ui-refresh .sponsor-tier-chip > .prop {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 3px 8px;
    background: var(--paper-2, var(--paper));
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-full, 999px);
    font-family: var(--font-family);
    font-size: 12px;
    font-weight: 400;
    color: var(--ink-2, var(--muted));
    cursor: pointer;
    white-space: nowrap;
    max-width: 160px;
    transition: background var(--transition-fast), border-color var(--transition-fast);
}

.ui-refresh .sponsor-tier-chip > .prop .prop-value {
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

.ui-refresh .sponsor-tier-chip > .prop:hover {
    background: var(--paper-3, var(--line));
    border-color: var(--line-2);
}

.ui-refresh .sponsor-tier-chip > .prop.empty {
    color: var(--muted);
    border-style: dashed;
}

.ui-refresh .sponsor-tier-chip.open > .prop {
    background: var(--accent-subtle, var(--primary-light, var(--paper-3)));
    border-color: var(--accent-primary);
    color: var(--accent-primary);
}

.ui-refresh .sponsor-tier-chip > .prop .prop-caret {
    font-size: 9px;
    opacity: 0.6;
}

/* Hide the menu in its initial in-chip DOM position (before JS body-appends it) */
.ui-refresh .sponsor-tier-chip .prop-menu {
    display: none;
}

/* The chip dropdown menu — body-appended by JS, positioned via inline fixed styles */
.ui-refresh .sponsor-level-menu {
    display: none;
    min-width: 160px;
    max-height: 260px;
    overflow-y: auto;
    padding: 4px;
    background: var(--card);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
}

.ui-refresh .sponsor-level-menu .prop-menu-label {
    font-family: var(--font-family);
    font-size: 10px;
    font-weight: 600;
    color: var(--muted);
    padding: 2px 4px var(--space-2);
}

.ui-refresh .sponsor-level-menu .qmi {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    width: 100%;
    padding: 6px 10px;
    background: none;
    border: none;
    border-radius: var(--radius-sm);
    font-family: var(--font-family);
    font-size: 13px;
    font-weight: 500;
    color: var(--ink);
    cursor: pointer;
    text-align: left;
    white-space: nowrap;
    transition: background var(--transition-fast);
}

.ui-refresh .sponsor-level-menu .qmi:hover {
    background: var(--state-hover, var(--paper-2));
}

.ui-refresh .sponsor-level-menu .qmi.is-selected {
    color: var(--accent-primary);
    background: var(--accent-subtle, var(--primary-light));
}

/* Comps meta */
.ui-refresh .sponsor-card .sponsor-meta {
    font-size: 11px;
    font-weight: 500;
    color: var(--muted);
    letter-spacing: 0.03em;
}

/* Card action icons — top-right pill, visible on hover */
.ui-refresh .sponsor-card .sr-actions {
    position: absolute;
    top: var(--space-2);
    right: var(--space-2);
    display: flex;
    gap: 1px;
    align-items: center;
    background: var(--card);
    border: 1px solid var(--input-border);
    border-radius: var(--radius-md);
    padding: 2px;
    box-shadow: var(--shadow-sm);
    opacity: 0;
    transition: opacity var(--transition-fast);
}

.ui-refresh .sponsor-card:hover .sr-actions {
    opacity: 1;
}

.ui-refresh .sponsor-card .sr-actions .lr-icon {
    display: grid;
    place-items: center;
    width: 26px;
    height: 26px;
    border-radius: var(--radius-sm);
    background: transparent;
    border: 0;
    color: var(--ink-2);
    cursor: pointer;
    transition: background var(--transition-fast), color var(--transition-fast);
    text-decoration: none;
}

.ui-refresh .sponsor-card .sr-actions .lr-icon i {
    font-size: 12px;
}

.ui-refresh .sponsor-card .sr-actions .lr-icon:hover {
    background: var(--paper-2);
    color: var(--ink);
}

.ui-refresh .sponsor-card .sr-actions .lr-icon.danger:hover {
    color: var(--color-error);
    background: var(--color-error-bg);
}

/* ---- Tier chip sub-label (comps hint in dropdown) ---- */
.ui-refresh .sponsor-tier-chip .qmi-sub,
.ui-refresh .sponsor-list-table .qmi-sub {
    display: block;
    font-size: 11px;
    color: var(--muted);
    font-weight: 400;
    margin-top: 1px;
}

/* ---- Table pane — sponsor-specific column widths ---- */
.ui-refresh .sponsor-list-table {
    table-layout: fixed;
}

.ui-refresh .sponsor-list-table .lt-th-name,
.ui-refresh .sponsor-list-table .lt-cell-name {
    width: 30%;
}

.ui-refresh .sponsor-list-table .lt-th-website,
.ui-refresh .sponsor-list-table .lt-cell-website {
    width: 25%;
}

.ui-refresh .sponsor-list-table .lt-th-tier,
.ui-refresh .sponsor-list-table .lt-cell-tier {
    width: 18%;
}

.ui-refresh .sponsor-list-table .lt-th-logo,
.ui-refresh .sponsor-list-table .lt-cell-logo {
    width: 12%;
}

.ui-refresh .sponsor-list-table .lt-th-actions,
.ui-refresh .sponsor-list-table .lt-cell-actions {
    width: 15%;
}

.ui-refresh .sponsor-table-logo {
    max-height: 32px;
    max-width: 80px;
    object-fit: contain;
    border-radius: var(--radius-sm);
}

.ui-refresh .lt-empty-logo {
    color: var(--muted);
}


/* =====================================================================
   VIRTUAL EVENT CARD (ui-refresh) — replaces the GridView table for a
   configured zoom/on24/gotowebinar virtual event.
   See widgets/VirtualEventInput.php::getVirtualEventTable().
   ===================================================================== */
.ui-refresh .virtual-integration-form {
    --control-bg-color: transparent;
}
.ui-refresh .virtual-integration-form .form-control,
.ui-refresh .virtual-integration-form .input-group-addon,
.ui-refresh .virtual-integration-form .select2-selection,
.ui-refresh .virtual-integration-form .select2-search__field {
    background-color: transparent !important;
}

.ui-refresh .virtual-event-card {
    position: relative;
}
.ui-refresh .virtual-event-card__actions {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    display: flex;
    gap: var(--space-1);
}
.ui-refresh .virtual-event-card__iconbtn {
    width: var(--space-8) !important;
    height: var(--space-8) !important;
    padding: 0 !important;
    display: flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
}
.ui-refresh .virtual-event-card__head {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding-right: calc(2 * var(--space-8) + var(--space-2)); /* clear the corner action buttons */
}
.ui-refresh .virtual-event-card__logo {
    width: var(--space-7);
    height: var(--space-7);
    flex-shrink: 0;
    object-fit: contain;
}
.ui-refresh .virtual-event-card__name {
    font-size: var(--font-size-lg);
    line-height: var(--line-height-lg);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
}
.ui-refresh .virtual-event-card__rows {
    margin-top: var(--space-3);
    display: grid;
    grid-template-columns: auto 1fr; /* label column sizes to the longest label */
    column-gap: var(--space-2);
    row-gap: var(--space-2);
    font-size: var(--font-size-xs);
    line-height: var(--line-height-sm);
}
.ui-refresh .virtual-event-card__k {
    color: var(--fg-default);
    font-weight: var(--font-weight-semibold); /* label: heavier */
}
.ui-refresh .virtual-event-card__v {
    color: var(--fg-muted);
    font-weight: var(--font-weight-normal); /* value: normal */
}
.ui-refresh .virtual-event-card__alert {
    margin-top: var(--space-3);
}
.ui-refresh .virtual-event-card__footer {
    margin-top: var(--space-3);
}

/* add to wallet adjustments */
.ui-refresh .wallet-editor-layout .wallet-section .wallet-color-row .wallet-color-field .input-group {
    width: 100%;
}
.ui-refresh .wallet-editor-layout .wallet-section .wallet-section-body .checkbox label > input {
    position: absolute !important;
    right: 0;
}
.ui-refresh .wallet-editor-layout .wallet-section .wallet-hero-upload .image-picker {
    width: 100%;
    max-width: 100%;
}
.ui-refresh .wallet-editor-layout .wallet-section .wallet-color-row .spectrum-group .sp-preview {
    border: none;
}
.ui-refresh .wallet-editor-layout .wallet-section .wallet-color-row .spectrum-group .sp-dd {
    position: relative;
    left: -2px;
}

/* =========================================================================
   App Info pages (Mobile Apps → Go Onsite Pro / Go Attend) — subscription
   overview built from the shared rd-* vocabulary; only page-specific bits
   live here.
   ========================================================================= */
/* Feature list under the intro lede. */
.ui-refresh .rd-profile.go-attend-app-info {
    gap: var(--space-8);
    padding-bottom: var(--space-8);
}
.ui-refresh .go-attend-overview {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}
.ui-refresh .go-attend-overview > .list-intro {
    margin-bottom: 0;
}
.ui-refresh .go-attend-app-info .feature-enable-hint {
    margin-top: var(--space-2);
}
.ui-refresh .go-attend-app-info .rd-stats {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
}
.ui-refresh .go-attend-event-settings .feature-enable-card {
    margin-bottom: 0;
}
.ui-refresh .app-info-features {
    margin: var(--space-2) 0 0;
    padding-left: var(--space-5);
    font-size: var(--font-size-sm);
    line-height: 1.55;
    color: var(--muted);
}
.ui-refresh .app-info-features li {
    margin-bottom: var(--space-1);
}
/* The events table card is the collapse's inner first child — the margin
   lives inside the animated overflow:hidden box so expanding/collapsing
   never jumps (see Bootstrap Collapse pattern). */
.ui-refresh .app-info-events {
    margin-top: var(--space-3);
}
/* Purchase Subscription result screen inside the ajax drawer. */
#ajax-drawer .subscribe-result {
    text-align: center;
    padding: var(--space-6);
}
#ajax-drawer .subscribe-result .fa-party-horn {
    margin-bottom: var(--space-4);
    color: var(--accent-primary);
}
#ajax-drawer .subscribe-result p {
    margin: 0 0 var(--space-3);
}
#ajax-drawer .subscribe-result .btn {
    margin-top: var(--space-3);
}
/* Number stepper (Purchase Subscription drawer's "Number of events" field).
   The container carries the visible border/radius/height so it reads as one
   control; the inner .form-control is neutralized to a bare, centered value. */
.ui-refresh .ajax-drawer .number-stepper {
    display: inline-flex;
    align-items: stretch;
    height: var(--input-height);
    border: 1px solid var(--input-border);
    border-radius: var(--input-radius);
    overflow: hidden;
    background-color: var(--card, transparent);
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.ui-refresh .ajax-drawer .number-stepper:focus-within {
    border-color: var(--accent-primary);
    box-shadow: var(--focus-ring);
}
.ui-refresh .ajax-drawer .number-stepper button {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    border: none;
    background-color: var(--control-bg-color, transparent);
    color: var(--fg-muted);
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .ajax-drawer .number-stepper button:hover {
    background-color: var(--state-hover);
    color: var(--fg-default);
}
.ui-refresh .ajax-drawer .number-stepper button:active {
    background-color: var(--state-active);
}
.ui-refresh .ajax-drawer .number-stepper button:first-child {
    border-right: 1px solid var(--input-border);
}
.ui-refresh .ajax-drawer .number-stepper button:last-child {
    border-left: 1px solid var(--input-border);
}
.ui-refresh .ajax-drawer .number-stepper .form-control {
    width: 4rem;
    height: auto;
    border: none;
    border-radius: 0 !important;
    padding: 0;
    background-color: transparent !important;
    box-shadow: none;
    text-align: center;
    appearance: textfield;
    -webkit-appearance: textfield;
    -moz-appearance: textfield;
}
.ui-refresh .ajax-drawer .number-stepper .form-control:focus {
    border: none !important;
    box-shadow: none !important;
}
.ui-refresh .ajax-drawer .number-stepper input[type="number"]::-webkit-inner-spin-button,
.ui-refresh .ajax-drawer .number-stepper input[type="number"]::-webkit-outer-spin-button {
    -webkit-appearance: none;
}

/* ===== External (share-link) dashboard shell ===== */
.ui-refresh #report-content {
    padding: var(--space-4) var(--space-4) var(--space-8);
}
/* ===== Export progress dialog =====
   The Bootstrap modal controller remains as the transport so existing export
   entrypoints keep working, while the content uses the centered dialog design. */
.ui-refresh #export_progress .export-progress-dialog {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 420px;
    max-width: 90vw;
    margin: 0;
    transform: translate(-50%, -50%) scale(0.95);
    transition: transform var(--transition-normal);
}
.ui-refresh #export_progress.in .export-progress-dialog {
    transform: translate(-50%, -50%) scale(1);
}
.ui-refresh #export_progress .export-progress-dialog .modal-content {
    position: relative;
    padding: var(--space-8) var(--space-8) var(--space-6);
    border: 0;
    box-shadow: var(--sh-lg);
    text-align: center;
    overflow: visible;
}

.ui-refresh #export_progress .export-progress-close {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    padding: var(--space-1);
    border: 0;
    background: transparent;
    color: var(--fg-muted);
    line-height: 1;
}
.ui-refresh #export_progress .export-progress-close:hover,
.ui-refresh #export_progress .export-progress-close:focus {
    color: var(--fg-default);
}
.ui-refresh #export_progress .export-progress-body {
    margin-top: var(--space-5);
}
.ui-refresh #export_progress .export-progress-body .progress {
    height: 8px;
    margin-bottom: var(--space-5);
    border-radius: var(--radius-full);
    background: var(--bg-muted);
    box-shadow: none;
    overflow: hidden;
}
/* Some export entrypoints still render the legacy modal body. Keep its spacing
   and progress treatment aligned with the refreshed dialog. */
.ui-refresh #export_progress .modal-body {
    padding: var(--space-6);
}
.ui-refresh #export_progress .progress {
    height: 8px;
    margin-bottom: var(--space-5);
}
/* Accent fill regardless of the Bootstrap contextual class (-warning here).
   The inline "N%" label is illegible in an 8px bar, so hide it — the striped
   animation already communicates progress. */
.ui-refresh #export_progress .progress-bar {
    background-color: var(--accent-primary);
    color: transparent;
}
.ui-refresh #export_progress .export-progress-status {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    margin-bottom: var(--space-5);
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
}
.ui-refresh #export_progress .export-progress-status i {
    color: var(--accent-primary);
}
.ui-refresh #export_progress .export-progress-status--error,
.ui-refresh #export_progress .export-progress-status--error i {
    color: var(--color-error);
}
.ui-refresh #export_progress .export-progress-status--complete,
.ui-refresh #export_progress .export-progress-status--complete i {
    color: var(--color-success);
}
.ui-refresh #export_progress .export-progress-cancel-actions {
    margin-top: var(--space-4);
}
.ui-refresh #export_progress #export_complete .confirm-dialog__actions .btn,
.ui-refresh #export_progress .export-progress-cancel-actions .btn {
    width: 100%;
    flex: none;
}
.ui-refresh #export_progress #processing {
    font-size: var(--font-size-sm) !important;
    color: var(--fg-muted);
}
.ui-refresh #export_progress #processing i {
    color: var(--accent-primary);
}
/* Primary dialog action reads as a filled button, not the outline btn-default
   (design language: the dominant action in a dialog is filled). */

/* Admin Events keeps Yii GridView's filtering and export behavior but uses
   the same `.list-table` visual system as the other refreshed primary tables.
   This outer element only owns horizontal overflow; it adds no card surface. */
.ui-refresh .admin-event-grid {
    clear: both;
    overflow-x: auto;
    overscroll-behavior-inline: contain;
}

/* Base GridView forcibly appends Bootstrap's `.table` class. Its direct-child
   selectors carry one more element of specificity than `.list-table`, so
   restate the shared component metrics on this bridge rather than letting the
   legacy 12px/16px table padding win. */
.ui-refresh table.list-table.admin-event-table > thead > tr:first-child > th {
    padding: var(--space-2) var(--space-3);
    background: var(--paper-2);
    border-bottom: 1px solid var(--list-table-line);
    font-size: 11px;
    font-weight: var(--font-weight-semibold);
    color: var(--muted);
    white-space: nowrap;
}
.ui-refresh table.list-table.admin-event-table > tbody > tr > td {
    padding: var(--space-3);
    border-top: 0;
    border-bottom: 1px solid var(--list-table-line);
    background: transparent;
    color: var(--ink);
    font-size: 13px;
    vertical-align: middle;
}

/* This Yii GridView's generated filter row uses plain `.filters > td` rather
   than ListTableGridView's `.lt-filter-row > td` hook. Bridge that class-name
   difference so it follows the same compact table rhythm. */
.ui-refresh .admin-event-table thead > tr.filters > td {
    padding: var(--space-2) var(--space-3);
    background: var(--bg-page);
    border-radius: 0;
    border-top: 0 !important;
    border-bottom: 1px solid var(--list-table-line);
    --control-bg-color: var(--bg-surface);
}
.ui-refresh .admin-event-table thead > tr.filters > td .form-control,
.ui-refresh .admin-event-table thead > tr.filters > td .multiselect {
    height: var(--space-8) !important;
    min-height: var(--space-8);
    padding-top: var(--space-1);
    padding-bottom: var(--space-1);
    font-size: var(--font-size-xs);
}
.ui-refresh .admin-event-table thead > tr.filters > td select.form-control {
    appearance: none;
    background-image: var(--select-chevron-image);
    background-position: calc(100% - 1.3em) center;
    background-repeat: no-repeat;
    background-size: .6em;
    padding-right: 30px;
}

/* Match the readable 14px control scale used by refreshed drawer forms.
   Grid-specific filters are compact by design; the additional-search drawer
   is a full form and should not inherit their 12px table typography. */
.ui-refresh .admin-event-filter-form .form-group > label,
.ui-refresh .admin-event-filter-form [role="radiogroup"] > .radio > label,
.ui-refresh .admin-event-filter-form select.form-control {
    font-size: var(--font-size-sm);
}
.ui-refresh .admin-event-filter-form [role="radiogroup"] > .radio > label {
    line-height: 1.45;
}
.ui-refresh .admin-event-filter-form [role="radiogroup"] > .radio input[type="radio"] {
    -webkit-appearance: none;
    appearance: none;
    box-sizing: border-box;
    position: relative !important;
    width: 16px;
    height: 16px;
    margin: 2px 0 0 !important;
    border: 1.5px solid var(--input-border);
    border-radius: 50%;
    background: var(--bg-surface);
}
.ui-refresh .admin-event-filter-form [role="radiogroup"] > .radio input[type="radio"]:checked {
    border-color: var(--accent-primary);
}
/* backend.css supplies a positioned `::before` check dot for native radios.
   It remains after `appearance: none` and sits at the control origin, so hide
   that legacy layer before drawing the centered refresh indicator below. */
.ui-refresh .admin-event-filter-form [role="radiogroup"] > .radio input[type="radio"]:checked::before {
    content: none;
}
.ui-refresh .admin-event-filter-form [role="radiogroup"] > .radio input[type="radio"]:checked::after {
    content: '';
    position: absolute;
    inset: 3px;
    border-radius: 50%;
    background: var(--accent-primary);
}

/* Actions column — LEADING and pinned, matching the report grids (Contacts,
   Registrants, Admin › Users) where ActionColumn is $columns[0] and
   AjaxDataTables freezes it via FixedColumns (communicate.css `.action-col`).
   This grid is a plain Yii GridView inside the .admin-event-grid scroll
   wrapper above, so position: sticky does the freezing. _grid.php puts
   `.admin-event-actions` on the header, filter and body cells so the whole
   column sticks. Every pinned cell MUST paint an opaque, token-driven
   background or the columns scrolling underneath bleed through. `width: 1%`
   is the shrink-to-fit idiom .lt-cell-actions uses; left-aligned because a
   right-aligned leading column pools its slack between the icons and the
   table edge. */
.ui-refresh .admin-event-table .admin-event-actions {
    position: sticky;
    left: 0;
    z-index: 2;
    width: 1%;
    min-width: 0;
    white-space: nowrap;
    text-align: left;
    padding-left: var(--space-2);
    padding-right: var(--space-2);
}
.ui-refresh table.list-table.admin-event-table > thead > tr:first-child > th.admin-event-actions {
    background: var(--paper-2);
}
.ui-refresh .admin-event-table thead > tr.filters > td.admin-event-actions,
.ui-refresh table.list-table.admin-event-table > tbody > tr > td.admin-event-actions {
    background: var(--bg-page);
}
/* Out-specifies both the generic .list-table row-hover tint and the opaque
   fill above, so the pinned cell follows the row's hover like every other
   cell. */
.ui-refresh .list-table.admin-event-table tbody > tr:not(:has(td[colspan])):hover > td.admin-event-actions {
    background: var(--paper-2);
}
/* Right-edge divider only while something is scrolled underneath (toggled by
   admin-event-grid.js); at scrollLeft 0 there is nothing to separate. */
.ui-refresh .admin-event-grid.is-scrolled .admin-event-actions {
    box-shadow: 1px 0 0 var(--list-table-line);
}

/* The buttons: ALWAYS visible (not hover-revealed like .lr-icon), styled as
   the report grids' `.action-col .btn` — 28px ghost squares in --ink-2 with a
   --paper-2 hover. The .btn-group is laid out with a real gap because
   Bootstrap's -1px overlap and end-only corner rounding read wrong once the
   buttons are ghosted. `!important` on the radius is what beats Bootstrap's
   `.btn-group > .btn:first-child:not(:last-child)` corner rules. */
.ui-refresh .admin-event-table td.admin-event-actions .btn-group {
    display: inline-flex;
    gap: var(--space-1);
    background: transparent;
    box-shadow: none;
}
.ui-refresh .admin-event-table td.admin-event-actions .btn-group > .btn + .btn {
    margin-left: 0;
}
.ui-refresh .admin-event-table td.admin-event-actions .btn {
    display: inline-grid;
    place-items: center;
    width: 28px;
    height: 28px;
    padding: 0;
    background: transparent;
    border-color: transparent;
    box-shadow: none;
    border-radius: var(--radius-sm) !important;
    color: var(--ink-2);
    opacity: 1;
    transition: background var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .admin-event-table td.admin-event-actions .btn:hover,
.ui-refresh .admin-event-table td.admin-event-actions .btn:focus-visible {
    background: var(--paper-2);
    border-color: transparent;
    color: var(--ink);
}
.ui-refresh .admin-event-table td.admin-event-actions .btn.disabled {
    color: var(--muted);
    cursor: not-allowed;
}

/* Custom Domains uses the shared ListTableGridView and ListTableActionColumn
   controls. These page-specific sizes only protect its unusually long values. */
.ui-refresh .admin-custom-domain-grid {
    clear: both;
    overflow-x: auto;
    overscroll-behavior-inline: contain;
}
.ui-refresh .admin-custom-domain-table .lt-cell-domain {
    min-width: 200px;
}
.ui-refresh .admin-custom-domain-table .lt-cell-cname {
    min-width: 220px;
}
.ui-refresh .admin-custom-domain-table .lt-cell-account {
    min-width: 160px;
}
.ui-refresh .admin-custom-domain-table .lt-cell-api-key {
    max-width: 180px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ui-refresh .admin-custom-domain-table .lt-th-boolean,
.ui-refresh .admin-custom-domain-table .lt-cell-boolean {
    min-width: 104px;
    text-align: left;
}
.ui-refresh .admin-custom-domain-boolean {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--space-5);
    height: var(--space-5);
    border-radius: var(--radius-full);
    font-size: var(--font-size-xs);
}
.ui-refresh .admin-custom-domain-boolean--yes {
    color: var(--color-success);
    background: var(--color-success-bg);
}
.ui-refresh .admin-custom-domain-boolean--no {
    color: var(--fg-muted);
    background: var(--bg-muted);
}

.ui-refresh .admin-event-list > #pjax-grid > .breadcrumb-btn {
    float: none !important;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin: var(--space-3) 0 var(--space-2) !important;
}
.ui-refresh .admin-event-list > #pjax-grid > .breadcrumb-btn .mr-10 {
    margin-right: 0 !important;
}

.ui-refresh .admin-event-product {
    display: inline-flex;
    align-items: center;
    min-height: 22px;
    padding: 2px var(--space-2);
    border-radius: var(--radius-full);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    line-height: 1;
}
.ui-refresh .admin-event-product--attend {
    color: var(--accent-primary);
    background: color-mix(in srgb, var(--accent-primary) 14%, transparent);
}
.ui-refresh .admin-event-product--onsite {
    color: var(--color-success);
    background: var(--color-success-bg);
}
.ui-refresh .admin-event-product--kiosk {
    color: var(--fg-muted);
    background: var(--bg-muted);
}
.ui-refresh .admin-event-status {
    display: inline-flex;
    align-items: center;
    padding: 3px var(--space-2);
    border-radius: var(--radius-full);
    background: var(--bg-muted);
    color: var(--fg-muted);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    line-height: 1;
}
.ui-refresh .admin-event-status[data-status="draft"] {
    color: var(--accent-primary);
    background: var(--accent-subtle);
}
.ui-refresh .admin-event-status[data-status="live"] {
    color: var(--color-success);
    background: var(--color-success-bg);
}
.ui-refresh .admin-event-status[data-status="cancelled"] {
    color: var(--color-error);
    background: var(--color-error-bg);
}

/* ===== Mass Delete ("Delete Via File Upload") flyout =====
   Wizard (mass-delete-import/*) opened from the Contacts page as an
   AjaxDrawer flyout (web/js/ui-refresh/mass-delete-flyout.js), the same
   pattern as import-flyout.js. import.css's own header comment explains why
   the shared .wizard/.panel chrome carries no inset of its own: it's a
   cross-cutting component used by ~60 unrelated wizard flows, so any
   consumer that wants breathing room applies it on its own marker class
   instead (see .import-wizard's `padding-inline: 20px` there) — this is the
   .mass-delete-wizard equivalent, sized to --space-6 to line up with the
   drawer header's own inset (.sidebar-title in layout.css). Without
   it, .panel-body reads flush against the drawer edges (the generic
   `.ajax-drawer .panel-body { padding: 0 }` above assumes its container
   already supplies the inset). Same fix as #export_progress above: the
   global .progress reset (margin-bottom: 0, sized for dashlet percent bars)
   leaves "Processing - please wait" glued to the bar. Also tightens the
   vertical rhythm between the step-pill bar and the stacked .panel blocks —
   this wizard is denser than a typical drawer form. */
.ui-refresh #ajax-drawer .mass-delete-wizard {
    padding: 0 var(--space-6) var(--space-4);
}
.ui-refresh #ajax-drawer .mass-delete-wizard .wsc-header {
    margin: 0 0 var(--space-3);
}
.ui-refresh #ajax-drawer .mass-delete-wizard .panel {
    margin-bottom: var(--space-3);
}
.ui-refresh #ajax-drawer .mass-delete-wizard .panel:last-child {
    margin-bottom: 0;
}
.ui-refresh #ajax-drawer .mass-delete-wizard .progress {
    margin-bottom: var(--space-4);
}

.ui-refresh #export_progress #export_link {
    background: var(--accent-primary);
    border-color: var(--accent-primary);
    color: var(--fg-on-accent);
}
.ui-refresh #export_progress #export_link:hover,
.ui-refresh #export_progress #export_link:focus {
    background: var(--accent-hover);
    border-color: var(--accent-hover);
    color: var(--fg-on-accent);
}
.ui-refresh .external-login-card {
    max-width: 420px;
    margin: 0 auto 0;
    padding: var(--space-8);
    background: var(--bg-surface);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
}
.ui-refresh .external-login-card #login-form {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}
.ui-refresh .external-login-card #error-dashboardloginform-password,
.ui-refresh .external-login-card #error-directreportloginform-password {
    margin-bottom: 0;
}
.ui-refresh .external-login-card .login-submit {
    background: var(--bg-primary);
}
.ui-refresh .navbar-custom .logout-btn {
    margin: 0 10px;
    justify-self: flex-end;
}

/** Horizontal Rule <hr /> */
.ui-refresh hr {
    border-color: var(--border-default);
    margin-top: var(--space-6);
    margin-bottom: var(--space-6);
}

/* update for visibility conditions in ui-refresh */
.ui-refresh .visibility-conditions {
    padding: 15px;
}

/* adjust vertical checkbox and radio positions */
.ui-refresh .form-vertical .form-group .radio,
.ui-refresh .form-vertical .form-group .checkbox {
    margin-left: 5px;
}
/* ...except inside a toggle-group/toggle-row card: those rows are already
   flush-left by design (.checkbox > label sets padding-left: 0 above), so
   the 5px nudge above just misaligns a field-backed toggle (e.g. "Change the
   Registration Timeout Period for this event", "Allow registrants to choose
   their own time zone preference" — both $form->field()->checkbox() calls,
   so they still match .form-vertical .form-group .checkbox) against a plain
   sibling field label at the same left edge (e.g. "Timeout Period"). Needs
   one more class than the rule above to win on specificity. */
.ui-refresh .form-vertical .toggle-group .form-group .radio,
.ui-refresh .form-vertical .toggle-row .form-group .radio,
.ui-refresh .form-vertical .toggle-group .form-group .checkbox,
.ui-refresh .form-vertical .toggle-row .form-group .checkbox {
    margin-left: 0;
}

/* ---- Bookmark list (.bm-*) ------------------------------------------------
   Drag-reorder table for the ui-refresh bookmark management page.
   Mirrors the .list-table / .lt-row / .sr-drag pattern from reg-type-list.
   Uses semantic tokens only — no body[data-surface] overrides needed.
-------------------------------------------------------------------------- */

/* The drag handle inside the bookmark table inherits .lt-cell-drag / .sr-drag
   rules from the shared .list-table block above. The .bm-drag alias is used
   in JS (handle: '.bm-drag') so the sortable binding is scoped to this table
   and doesn't accidentally pick up sr-drag handles in other list-tables on
   the same page. No extra CSS needed beyond what .sr-drag already provides. */

/* Drag-state helpers */
.ui-refresh .bm-list tbody tr.ui-sortable-helper {
    display: table;
    background: var(--card);
    box-shadow: var(--shadow-md);
}

.ui-refresh .bm-row-placeholder td {
    background: var(--primary-tint);
}

/* Event/account column — muted, truncates gracefully */
.ui-refresh .bm-list .lt-th-event,
.ui-refresh .bm-list .lt-cell-event {
    color: var(--muted);
    font-size: 12px;
    max-width: 240px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Title text in the name cell — no special treatment needed beyond .lt-cell-name,
   but .bm-title gives JS a reliable hook for reading the name in the delete dialog. */
.ui-refresh .bm-list .bm-title {
    display: block;
}

.ui-refresh .bm-list .bm-empty-cell {
    padding: var(--space-6);
}

/* Toolbar above the bookmark table (search input) */
.ui-refresh .bm-toolbar {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    padding: var(--space-2) 0;
}

/* =========================================================================
   Record detail / profile view  (.rd-*)
   Reusable single-record detail layout: a subpage-head breadcrumb + a
   .rd-profile column of .rd-card sections (hero, stat tiles, 2-col
   info/activity, list, table/empty-state). Generalized from the
   invitation-list-contact profile; shared by contact, registrant,
   invitation-list-contact, etc. Token-based; scoped .ui-refresh .rd-*.
   Pattern doc: .claude/skills/ui-refresh/ui-refresh-patterns.md
   ("Record Detail / Profile View").
   ========================================================================= */
.ui-refresh .rd-profile {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

/* Card shell. Most cards clip to their rounded corners (needed so flush-edge
   content like .rd-table's thead/hover row backgrounds don't bleed past the
   radius); cards containing a dropdown that must escape the card bounds opt
   out via .rd-header or .rd-card--dropdown-safe. */
.ui-refresh .rd-card {
    background: var(--card);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    overflow: hidden;
}
.ui-refresh .rd-header,
.ui-refresh .rd-card--dropdown-safe {
    overflow: visible;
}
.ui-refresh .rd-card__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--panel-border);
}
.ui-refresh .rd-card__title {
    margin: 0;
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-bold);
    letter-spacing: -0.01em;
    color: var(--ink);
}
.ui-refresh .rd-card__body {
    padding: var(--space-3) var(--space-5) var(--space-4);
}
.ui-refresh .rd-muted {
    color: var(--muted);
}

/* ----- Header card ----- */
.ui-refresh .rd-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: var(--space-5);
    flex-wrap: wrap;
    padding: var(--space-6);
}
.ui-refresh .rd-header__main {
    display: flex;
    gap: var(--space-4);
    align-items: flex-start;
    min-width: 0;
}
.ui-refresh .rd-avatar {
    flex: none;
    width: 70px;
    height: 70px;
    border-radius: var(--radius-full);
    background: var(--accent-primary);
    color: var(--fg-on-accent);
    display: grid;
    place-items: center;
    font-size: 24px;
    font-weight: var(--font-weight-bold);
    letter-spacing: 0.01em;
    overflow: hidden;
}
.ui-refresh .rd-avatar--img {
    background: none;
}
.ui-refresh .rd-avatar img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.ui-refresh .rd-header__body {
    min-width: 0;
}
.ui-refresh .rd-header__name-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}
.ui-refresh .rd-name {
    margin: 0;
    font-size: 18px;
    font-weight: var(--font-weight-bold);
    letter-spacing: -0.02em;
    line-height: 1.1;
    color: var(--ink);
}
.ui-refresh .rd-email {
    display: inline-block;
    margin-top: var(--space-1);
    font-size: var(--font-size-sm);
    color: var(--accent-primary);
    text-decoration: none;
    word-break: break-all;
}
.ui-refresh .rd-email:hover {
    text-decoration: underline;
}
.ui-refresh .rd-meta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
    margin-top: var(--space-3);
}
/* Admin notes under the header meta — italic, like the legacy top-card
   notes line. Rendered by common/_rd-header-notes.php. */
.ui-refresh .rd-header__notes {
    margin: var(--space-3) 0 0;
    max-width: 72ch;
    font-size: var(--font-size-sm);
    font-style: italic;
    line-height: 1.5;
    color: var(--ink-2);
    overflow-wrap: anywhere;
}
/* Each meta fact reads as a quiet pill rather than a bare text run — gives
   the hero card's Trigger/Created/Updated facts a bit more visual structure
   without reaching for accent color (reserved for status/actions). */
.ui-refresh .rd-meta__item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 11px;
    border-radius: var(--radius-full);
    background: var(--bg-subtle);
    border: 1px solid var(--border-subtle);
    color: var(--muted);
    font-size: var(--font-size-xs);
}
.ui-refresh .rd-meta__item i {
    color: var(--muted-2);
    font-size: 11px;
}
.ui-refresh .rd-meta__item b {
    color: var(--ink-2);
    font-weight: var(--font-weight-semibold);
}
/* Pills carry their own separation via background + border + gap; the
   dot separator markup stays in the shared views for now but is redundant. */
.ui-refresh .rd-meta__sep {
    display: none;
}
.ui-refresh .rd-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 11px;
    border-radius: var(--radius-full);
    background: var(--accent-subtle);
    color: var(--accent-on-tint);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
}

/* Reused ActionColumn dropdown, restyled as the accent "Actions" button.
   .rd-header uses align-items: flex-start so a multi-line name/meta body
   stays top-aligned with the avatar; override just the actions slot so the
   button centers against the header's full height instead of hugging top. */
.ui-refresh .rd-header__actions {
    align-self: center;
}
.ui-refresh .rd-header__actions .dropdown-toggle {
    background: var(--accent-primary) !important;
    border-color: var(--accent-primary) !important;
    color: var(--fg-on-accent) !important;
    padding: 9px 16px;
    border-radius: var(--radius-md);
    font-weight: var(--font-weight-semibold);
}
.ui-refresh .rd-header__actions .dropdown-toggle:hover,
.ui-refresh .rd-header__actions .dropdown-toggle:focus,
.ui-refresh .rd-header__actions.open .dropdown-toggle {
    background: var(--accent-hover) !important;
    border-color: var(--accent-hover) !important;
    color: var(--fg-on-accent) !important;
}
.ui-refresh .rd-header__actions .dropdown-menu {
    right: 0;
    left: auto;
}
.ui-refresh .rd-header__actions .dropdown-menu .dropdown-header {
    padding: var(--space-2) var(--space-4);
    font-size: 10.5px;
    font-weight: var(--font-weight-semibold);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--muted-2);
    background: none;
    border-color: var(--line);
}
.ui-refresh .rd-header__actions .dropdown-menu > .dropdown-header:first-child {
    border-top: 0;
}
.ui-refresh .rd-header__actions .dropdown-menu .divider {
    height: 1px;
    margin: var(--space-1) var(--space-2);
    background: var(--line);
}

/* ----- Badges (status pills) ----- */
.ui-refresh .rd-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    border-radius: var(--radius-full);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    white-space: nowrap;
}
.ui-refresh .rd-badge__dot {
    width: 6px;
    height: 6px;
    border-radius: var(--radius-full);
    background: currentColor;
}
.ui-refresh .rd-badge--success {
    background: var(--color-success-bg);
    color: var(--color-success);
}
.ui-refresh .rd-badge--error {
    background: var(--color-error-bg);
    color: var(--color-error);
}
.ui-refresh .rd-badge--warning {
    background: var(--color-warning-bg);
    color: var(--color-warning);
}
.ui-refresh .rd-badge--accent {
    background: var(--accent-subtle);
    color: var(--accent-on-tint);
}
.ui-refresh .rd-badge--muted {
    background: var(--paper-2);
    color: var(--muted);
}

/* ----- Stats row ----- */
.ui-refresh .rd-stats {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-4);
}
.ui-refresh .rd-stat {
    padding: var(--space-5);
}
.ui-refresh .rd-stat__label {
    font-size: 11px;
    font-weight: var(--font-weight-semibold);
    color: var(--muted);
}
.ui-refresh .rd-stat__value {
    margin: var(--space-2) 0 var(--space-2);
    font-size: 18px;
    font-weight: var(--font-weight-bold);
    letter-spacing: -0.02em;
    line-height: 1;
    color: var(--ink);
}
.ui-refresh .rd-stat__sub {
    font-size: var(--font-size-xs);
    color: var(--muted-2);
}

/* Linked stat tiles (e.g. registrant profile's Invite List card) render as an
   <a class="rd-card rd-stat">, which looks identical to a plain, non-clickable
   tile otherwise. The icon gives it the same "opens elsewhere" affordance
   used for external links throughout the app (see fa-arrow-up-right-from-square
   on .address-summary-card-website, bookmark rows, etc.). */
.ui-refresh a.rd-stat {
    position: relative;
}
.ui-refresh .rd-stat__link-icon {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    font-size: 11px;
    color: var(--muted-2);
}

/* 4-tile stats row (webhook view). The 900px/560px media rules on .rd-stats
   below still win inside their breakpoints (equal specificity, later in
   file), collapsing this to 2 then 1 columns — no extra mobile rules. */
.ui-refresh .rd-stats--4 {
    grid-template-columns: repeat(4, 1fr);
}

/* 2-tile stats row (registrant profile: free event, group registration off,
   no invite list or link reference). Like --4 above, the 900px/560px media
   rules on .rd-stats still win inside their breakpoints, so no extra mobile
   rules are needed. */
.ui-refresh .rd-stats--2 {
    grid-template-columns: repeat(2, 1fr);
}

/* Variable-count stats row (session view: 3-6 tiles depending on waitlist/tax
   settings). Tiles size themselves instead of a fixed column count, so a
   trailing partial row still fills evenly instead of leaving a lone
   full-width orphan tile. */
.ui-refresh .rd-stats--auto {
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}

/* Chart cards: floor the container so the card does not collapse before the
   Google Charts SVG draws. Height matches the widget option (136px). */
.ui-refresh .rd-chart .chart {
    min-height: 136px;
}

/* ----- Priority summary cards (Balance due / Syncs / Recent) -----
   Condensed "quick glance" cards sitting above the .rd-tabs detail; each
   links down to its full tab via [data-rd-tab-jump] (record-detail-tabs.js). */
.ui-refresh .rd-priority {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-4);
}
/* 4-card row (contact profile: Registrations/Invitation lists/Emails sent/Recent). */
.ui-refresh .rd-priority--4 {
    grid-template-columns: repeat(4, 1fr);
}
.ui-refresh .rd-priority-card .rd-card__head {
    border-bottom-color: var(--panel-border);
}
.ui-refresh .rd-priority-card__link {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    color: var(--accent-primary);
    text-decoration: none;
    white-space: nowrap;
}
.ui-refresh .rd-priority-card__link:hover {
    text-decoration: underline;
}

/* Balance due card */
.ui-refresh .rd-balance__amount {
    font-size: 28px;
    font-weight: var(--font-weight-bold);
    letter-spacing: -0.02em;
    color: var(--ink);
    line-height: normal;
    margin-top: var(--space-2);
}
.ui-refresh .rd-balance-bar {
    margin: var(--space-3) 0;
    height: 6px;
    background: var(--paper-2);
    border-radius: 999px;
    overflow: hidden;
    border: 1px solid var(--input-border);
}
.ui-refresh .rd-balance-bar__fill {
    height: 100%;
    background: var(--color-success);
    border-radius: var(--radius-full);
}
.ui-refresh .rd-balance__subrow {
    display: flex;
    justify-content: space-between;
    font-size: var(--font-size-sm);
    color: var(--ink-2);
}
.ui-refresh .rd-balance__caption {
    margin: var(--space-2) 0 var(--space-3);
    font-size: var(--font-size-xs);
    color: var(--muted-2);
}
.ui-refresh .rd-balance__action {
    width: 100%;
}

/* Syncs / Recent cards: repeated condensed rows */
.ui-refresh .rd-sync-row,
.ui-refresh .rd-recent-row {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-2) 0;
    border-bottom: 1px solid var(--panel-border);
}
/* Recent rows stack title over detail so long editor/email names get the full card width. */
.ui-refresh .rd-recent-row {
    flex-direction: column;
    gap: 0;
}
.ui-refresh .rd-sync-row:last-child,
.ui-refresh .rd-recent-row:last-child {
    border-bottom: 0;
    padding-bottom: 0;
}
.ui-refresh .rd-sync-row__name,
.ui-refresh .rd-recent-row__title {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    color: var(--ink);
}
.ui-refresh .rd-sync-row__sub,
.ui-refresh .rd-recent-row__sub {
    margin-top: 2px;
    font-size: var(--font-size-xs);
    color: var(--muted-2);
}
.ui-refresh .rd-sync-row__status {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: none;
}
.ui-refresh .rd-sync-row__retry {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    color: var(--accent-primary);
    text-decoration: none;
}
.ui-refresh .rd-sync-row__retry:hover {
    text-decoration: underline;
}

/* ----- Two-column section ----- */
.ui-refresh .rd-columns {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-5);
    align-items: start;
}
/* A vertical stack of cards occupying one .rd-columns side, for pages with
   more sections than fit as a single card per column (e.g. session view).
   min-width: 0 overrides the grid item default of min-width: auto — without
   it, a long unbreakable value in any nested content (e.g. an activity-feed
   diff) forces this column, and the whole .rd-columns grid, past the
   viewport instead of wrapping. */
.ui-refresh .rd-columns__col {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    min-width: 0;
}
/* Asymmetric variant for a detail view whose primary card (key/value facts)
   needs more room than its secondary card (e.g. a timeline) — meeting
   location's Location details + Update history. Falls back to the base
   1fr/1fr rule below 900px like every other .rd-columns consumer. */
.ui-refresh .rd-columns--wide-left {
    grid-template-columns: 1.7fr 1fr;
}
/* Holds several .rd-chip badges as one .rd-kv value (e.g. a list of specific
   meeting types/time slots rather than a single "All ..." chip) — .rd-chip
   itself carries no margin, so without this multiple chips would run
   together with no gap. */
.ui-refresh .rd-chip-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

/* Sub-section heading inside a key/value list (e.g. "Status" within Personal
   info) — small uppercase label, with breathing room above it. */
.ui-refresh .rd-kv__subhead {
    padding: var(--space-5) 0 var(--space-1);
    font-size: 11px;
    font-weight: var(--font-weight-semibold);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--muted-2);
}
/* Status values: a small colour-coded dot + plain text (not a pill). */
.ui-refresh .rd-status-val {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}
.ui-refresh .rd-status-dot {
    flex: none;
    width: 7px;
    height: 7px;
    border-radius: var(--radius-full);
}
.ui-refresh .rd-status-dot--success {
    background: var(--color-success);
}
.ui-refresh .rd-status-dot--error {
    background: var(--color-error);
}
.ui-refresh .rd-status-dot--warning {
    background: var(--color-warning);
}
.ui-refresh .rd-status-dot--accent {
    background: var(--accent-primary);
}
.ui-refresh .rd-status-dot--muted {
    background: var(--muted-2);
}

/* ----- Photo strip (hotel view) ----- */
/* Address photos as fixed-ratio tiles so mixed Google/uploaded sizes line
   up; the card body's padding provides the outer gutter. */
.ui-refresh .rd-photos {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: var(--space-3);
}
.ui-refresh .rd-photos__item {
    display: block;
    aspect-ratio: 4 / 3;
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    background: var(--paper);
}
.ui-refresh .rd-photos__item img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* ----- Personal info key/value ----- */
.ui-refresh .rd-kv {
    margin: 0;
}
.ui-refresh .rd-kv__row {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--panel-border);
}
.ui-refresh .rd-kv__row:last-child {
    border-bottom: 0;
}
/* .rd-card__body's padding-top already gutters the first row — without this
   the leading row stacks both paddings and sits visibly low. */
.ui-refresh .rd-card__body > .rd-kv:first-child > .rd-kv__row:first-child {
    padding-top: 0;
}
/* ----- Distribution bars (submission view: reviews breakdown) -----
   Reuses the shared `.ui-refresh .progress` track; only the row layout and
   fill are defined here. */
.ui-refresh .rd-dist {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}
.ui-refresh .rd-dist__row {
    display: grid;
    grid-template-columns: 40px 1fr auto;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-1) 0;
    color: var(--ink);
    text-decoration: none;
}
.ui-refresh a.rd-dist__row:hover,
.ui-refresh a.rd-dist__row:focus {
    color: var(--ink);
    text-decoration: none;
}
.ui-refresh a.rd-dist__row:hover .rd-dist__label {
    color: var(--accent-primary);
}
.ui-refresh .rd-dist__label {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}
.ui-refresh .rd-dist__pct {
    font-size: var(--font-size-xs);
    color: var(--muted);
    white-space: nowrap;
}
.ui-refresh .rd-dist .progress-bar {
    background-color: var(--accent-primary);
    border-radius: var(--radius-full);
    box-shadow: none;
}
.ui-refresh .rd-kv__key {
    flex: none;
    width: 124px;
    margin: 0;
    font-size: var(--font-size-sm);
    color: var(--muted);
}
.ui-refresh .rd-kv__val {
    margin: 0;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--ink);
    word-break: break-word;
}
.ui-refresh .meeting-location-details-card .rd-kv__val {
    flex-grow: 1;
    text-align: right;
}
.ui-refresh .rd-kv__val a {
    color: var(--accent-primary);
}

/* ===== Pigeonhole event settings — design "1a, bordered card panels" =====
   Sections render as .rd-card (see settings.php $cardOpen); the rules below
   cover the content *inside* those cards. Shared idiom across the page: a
   bordered, radius-lg, clipped box whose rows are divided by --line and
   padded --space-3/--space-4 (.pigeonhole-kv, .pigeonhole-field-mapping,
   .pigeonhole-sessions all use it). Yii wraps every field in a .form-group
   carrying a 20px margin-bottom (see .ui-refresh .form-group above); inside
   these fixed-height rows that margin is dead space, so it's zeroed. */

/* ----- Card body: text-block rhythm -----
   .rd-card__body's own padding provides the card gutter; the leading <p> in a
   card ("Please select…", "Please set the mapping up…") just needs its
   Bootstrap default margin tightened to the mockup's 10-12px. */
.ui-refresh .rd-card__body > p:first-child {
    margin: 0 0 var(--space-3);
    color: var(--muted);
    font-size: var(--font-size-sm);
}

/* ----- Pigeonhole Information: name/passcode + description ----- */
.ui-refresh .pigeonhole-info-heading h4 {
    margin: 0 0 var(--space-1);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-bold);
    line-height: var(--line-height-lg);
    color: var(--ink);
}
.ui-refresh .pigeonhole-info-heading__code {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-normal);
    color: var(--muted);
}
.ui-refresh .pigeonhole-info-heading p {
    margin: 0 0 var(--space-4);
    color: var(--muted);
    font-size: var(--font-size-sm);
}

/* ----- Pigeonhole Information: key/value box -----
   Not .rd-kv: that shared component renders a muted key + ink value in a
   fixed 124px column with no enclosing box, which is the inverse of this
   design (bold ink key, muted value, pushed apart inside a bordered box). */
.ui-refresh .pigeonhole-kv,
.ui-refresh .pigeonhole-field-mapping,
.ui-refresh .pigeonhole-sessions {
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
}
.ui-refresh .pigeonhole-kv__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--line);
    font-size: var(--font-size-sm);
}
.ui-refresh .pigeonhole-kv__row:last-child {
    border-bottom: 0;
}
.ui-refresh .pigeonhole-kv__key {
    font-weight: var(--font-weight-semibold);
    color: var(--ink);
}
.ui-refresh .pigeonhole-kv__val {
    color: var(--muted);
    text-align: right;
    word-break: break-word;
}
.ui-refresh .pigeonhole-kv__status {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}
.ui-refresh .pigeonhole-kv__status--live {
    color: var(--color-success);
    font-weight: var(--font-weight-semibold);
}

/* ----- Pigeonhole Sessions ----- */
.ui-refresh .pigeonhole-sessions {
    margin: 0;
    padding: 0;
    list-style: none;
}
.ui-refresh .pigeonhole-sessions > li {
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--line);
    font-size: var(--font-size-sm);
    color: var(--ink);
}
.ui-refresh .pigeonhole-sessions > li:last-child {
    border-bottom: 0;
}

/* ----- Pigeonhole Commands: stacked blocks, divider on each sub-heading ----- */
.ui-refresh .pigeonhole-commands {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}
.ui-refresh .pigeonhole-command h4 {
    margin: 0 0 var(--space-2);
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--line);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    color: var(--ink);
}
.ui-refresh .pigeonhole-command p {
    margin: 0 0 var(--space-3);
    color: var(--muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-sm);
}
.ui-refresh .pigeonhole-command ul {
    margin: 0 0 var(--space-3);
    padding-left: var(--space-5);
    color: var(--muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-sm);
}
.ui-refresh .pigeonhole-command__action {
    margin: 0;
}

/* ----- Pigeonhole passcode picker (select + refresh button, gap-separated
   rather than a flush Bootstrap .input-group — matches design 1a) ----- */
.ui-refresh .pigeonhole-passcode-picker {
    display: flex;
    gap: var(--space-2);
}
.ui-refresh .pigeonhole-passcode-picker .form-control {
    flex: 1;
}
/* .btn's default height (--btn-height, 36px) is shorter than .form-control's
   (--input-height, 40px) — match it to the select for a level pair. */
.ui-refresh .pigeonhole-passcode-picker .btn {
    height: var(--input-height);
}

/* ----- Pigeonhole field mapping (label -> dropdown rows in a bordered box) -----
   Box chrome is shared with .pigeonhole-kv above. */
.ui-refresh .pigeonhole-field-mapping__row {
    display: grid;
    grid-template-columns: 1fr 1.6fr;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--line);
}
.ui-refresh .pigeonhole-field-mapping__row:last-child {
    border-bottom: 0;
}
.ui-refresh .pigeonhole-field-mapping__row:first-child {
    background: var(--bg-muted);
}
/* Yii wraps the select in a .form-group whose 20px margin-bottom would
   off-centre it against the label and inflate the row. */
.ui-refresh .pigeonhole-field-mapping__row .form-group {
    margin: 0;
}
.ui-refresh .pigeonhole-field-mapping__label {
    font-size: var(--font-size-sm);
    color: var(--ink);
}
.ui-refresh .pigeonhole-field-mapping__label.required::after {
    content: " *";
    color: var(--color-error);
    font-weight: var(--font-weight-semibold);
}

/* ----- Pigeonhole settings page: card grid/gaps -----
   .rd-card carries no margin of its own (it's normally stacked via a flex
   `gap` wrapper, e.g. .rd-columns__col) — this is that wrapper for this
   page's cards, with no fill/border of its own — the cards sit directly on
   the page background. */
.ui-refresh .pigeonhole-settings {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
    --control-bg-color: var(--bg-page);
}
.ui-refresh .pigeonhole-settings__grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-5);
    align-items: start;
}
.ui-refresh .pigeonhole-settings__col {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}
@media (max-width: 767px) {
    .ui-refresh .pigeonhole-settings__grid {
        grid-template-columns: 1fr;
    }
}

/* ----- Activity timeline ----- */
.ui-refresh .rd-timeline {
    list-style: none;
    margin: 0;
    padding: 0;
}
.ui-refresh .rd-timeline__item {
    position: relative;
    display: flex;
    gap: var(--space-3);
    padding-bottom: var(--space-4);
}
.ui-refresh .rd-timeline__item:last-child {
    padding-bottom: 0;
}
/* Connector line between dots. */
.ui-refresh .rd-timeline__item:not(:last-child)::before {
    content: '';
    position: absolute;
    left: 14px;
    top: 32px;
    bottom: 0;
    width: 2px;
    background: var(--line);
}
.ui-refresh .rd-timeline__dot {
    position: relative;
    z-index: 1;
    flex: none;
    width: 30px;
    height: 30px;
    border-radius: var(--radius-full);
    display: grid;
    place-items: center;
    color: var(--fg-on-accent);
    font-size: 13px;
}
.ui-refresh .rd-dot--accent {
    background: var(--accent-primary);
}
.ui-refresh .rd-dot--success {
    background: var(--color-success);
}
.ui-refresh .rd-dot--error {
    background: var(--color-error);
}
.ui-refresh .rd-timeline__title {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    line-height: 1.35;
    color: var(--ink);
}
.ui-refresh .rd-timeline__meta {
    margin-top: 2px;
    font-size: var(--font-size-xs);
    color: var(--muted-2);
}

/* ----- Registrations list ----- */
.ui-refresh .rd-reg-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-top: var(--space-2);
}
.ui-refresh .rd-reg-item {
    display: block;
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-md);
    color: var(--ink);
    text-decoration: none;
}
.ui-refresh .rd-reg-item:hover {
    background: var(--paper-2);
}

/* ----- Invitation emails table ----- */
.ui-refresh .rd-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--font-size-sm);
}
.ui-refresh .rd-table thead tr {
    background: var(--paper-2);
}
.ui-refresh .rd-table th {
    text-align: left;
    padding: var(--space-2) var(--space-5);
    font-size: 11px;
    font-weight: var(--font-weight-semibold);
    color: var(--muted);
    white-space: nowrap;
}
.ui-refresh .rd-table td {
    padding: var(--space-3) var(--space-5);
    border-top: 1px solid var(--line);
    color: var(--ink-2);
    vertical-align: middle;
}
.ui-refresh .rd-table__name {
    font-weight: var(--font-weight-semibold);
    color: var(--ink);
}
/* Current-row tint (e.g. registrant Group Info's own row). The enclosing
   `.rd-card` keeps its default `overflow: hidden`, so a fill on the last row
   is clipped to the card's rounded corners automatically — no extra
   border-radius rule needed here. */
.ui-refresh .rd-table > tbody > tr.is-current > td {
    background: var(--paper-2);
}
/* Group Info sits in a `.rd-tab-cols` half-width column (~430-460px), the
   narrowest place an `.rd-table` renders. Auto layout was giving the Name
   cell (name + company sub-line) too little room relative to Email, wrapping
   a plain two-word name across 2-3 lines. Scoped to this table only — the
   other `.rd-table` consumers (speaker Sessions, session Speakers,
   admin/sub-account tables) render in wider columns and don't need it. */
.ui-refresh .rd-group-info-table th:first-child,
.ui-refresh .rd-group-info-table td:first-child {
    min-width: 160px;
}
/* Row action buttons: plain ghost icon buttons rather than a dropdown (a
   per-row Bootstrap menu positioned by list-table-dropdown-fix.js's
   position:fixed portal still has nowhere to open once its trigger is near
   the bottom of the viewport — the portal escapes ancestor clipping, not the
   screen edge). No existing `.btn-group-sm .btn` ghost rule reaches a native
   `.rd-table` (they're scoped to bridged `.panel table:not(.rd-table)` or
   plain Bootstrap `.table` grids), so this table needs its own copy of the
   same treatment. */
.ui-refresh .rd-group-info-table .btn-group-sm {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}
.ui-refresh .rd-group-info-table .btn-group-sm .btn {
    float: none;
    background: transparent !important;
    border: 0 !important;
    box-shadow: none;
    color: var(--muted) !important;
    height: auto;
    padding: var(--space-1) var(--space-2);
}
.ui-refresh .rd-group-info-table .btn-group-sm .btn:hover,
.ui-refresh .rd-group-info-table .btn-group-sm .btn:focus-visible {
    color: var(--accent-primary) !important;
}

/* ----- Empty state ----- */
.ui-refresh .rd-empty {
    padding: var(--space-10) var(--space-5);
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
}
.ui-refresh .rd-empty__icon {
    width: 52px;
    height: 52px;
    border-radius: var(--radius-full);
    background: var(--accent-subtle);
    color: var(--accent-on-tint);
    display: grid;
    place-items: center;
    font-size: 22px;
    margin-bottom: var(--space-3);
}
.ui-refresh .rd-empty__title {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
    color: var(--ink);
}
.ui-refresh .rd-empty__sub {
    margin-top: var(--space-1);
    max-width: 320px;
    font-size: var(--font-size-sm);
    color: var(--muted);
}

/* ----- Responsive ----- */
@media (max-width: 900px) {
    .ui-refresh .rd-columns {
        grid-template-columns: 1fr;
    }
    .ui-refresh .rd-stats {
        grid-template-columns: repeat(2, 1fr);
    }
    .ui-refresh .rd-priority {
        grid-template-columns: 1fr;
    }
    .ui-refresh .rd-priority--4 {
        grid-template-columns: repeat(2, 1fr);
    }
}
@media (max-width: 560px) {
    .ui-refresh .rd-priority--4 {
        grid-template-columns: 1fr;
    }
    .ui-refresh .rd-stats {
        grid-template-columns: 1fr;
    }
}

/* ----- Bootstrap .panel -> .rd-card bridge -----
   Legacy sub-view / integration partials (registrant hotel/meeting/attendance,
   Salesforce/HubSpot/Marketo/... sync logs) render `.panel.panel-default`. When
   nested inside a .rd-profile they should read as .rd-card sections WITHOUT
   editing those shared partials — only the panel chrome is remapped; the inner
   content is untouched. Bridged panels are spaced by the .rd-profile flex gap. */
.ui-refresh .rd-profile .panel,
.ui-refresh .rd-profile .panel.report-view {
    background: var(--card) !important;
    border: 1px solid var(--panel-border) !important;
    border-radius: var(--radius-lg) !important;
    box-shadow: none !important;
    overflow: hidden;
    margin-bottom: 0 !important;
}
/* Descendant (not `>`) so it matches however the partial nests its heading; the
   legacy `.panel-default > .panel-heading` uses `!important`, so we match it to
   force the crisp bold card-head from the mockups (transparent fill, hairline
   rule, --ink bold title). */
.ui-refresh .rd-profile .panel .panel-heading {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    flex-wrap: wrap;
    padding: var(--space-4) var(--space-5) !important;
    background: transparent !important;
    border-bottom: 1px solid var(--line) !important;
    font-size: var(--font-size-lg) !important;
    font-weight: var(--font-weight-bold) !important;
    letter-spacing: -0.01em;
    color: var(--ink) !important;
}
.ui-refresh .rd-profile .panel .panel-body {
    padding: var(--space-3) var(--space-5) var(--space-4) !important;
    background: transparent;
}
/* A card heading with more than one action (e.g. registrant/_transactions.php:
   Submit payment + Submit refund) needs its buttons grouped in one wrapper —
   the heading itself is flex + space-between, so bare siblings would spread
   apart across the row instead of sitting next to each other. */
.ui-refresh .panel-heading-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

/* uiFormSectionOpen()-based partials (object-audit/_grid's Update History,
   sync logs, etc.) render `.comm-form-section` under ui-refresh instead of
   the legacy `.panel` bridged above. Same idea: bridge that markup into an
   .rd-card shell when nested in a .rd-profile, without editing the shared
   partial. The `+ *` targets whatever follows the head (typically the
   Pjax wrapper div) as the card body. */
.ui-refresh .rd-profile .comm-form-section {
    background: var(--card) !important;
    border: 1px solid var(--panel-border) !important;
    border-radius: var(--radius-lg) !important;
    box-shadow: var(--shadow-card) !important;
    overflow: hidden;
    margin-bottom: 0 !important;
}
.ui-refresh .rd-profile .comm-form-section .event-settings-section-head {
    margin: 0;
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--line);
}
.ui-refresh .rd-profile .comm-form-section .event-settings-section-heading {
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-bold);
    letter-spacing: -0.01em;
    color: var(--ink);
}
.ui-refresh .rd-profile .comm-form-section .event-settings-section-head + * {
    padding: var(--space-3) var(--space-5) var(--space-4);
}

/* ----- Tabbed variant (.rd-tabs) -----
   A card shell whose header is a tab bar and whose body is a stack of panels,
   one visible at a time. Content is toggled by the `.is-active` class only
   (panels stay in the DOM) so Pjax containers + inline JS inside hidden tabs
   keep working. Driven by web/js/ui-refresh/record-detail-tabs.js. */
.ui-refresh .rd-tabs {
    background: var(--card);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    overflow: hidden;
}
.ui-refresh .rd-tabbar {
    display: flex;
    gap: var(--space-1);
    padding: 0 var(--space-3);
    border-bottom: 1px solid var(--panel-border);
    flex-wrap: wrap;
}
.ui-refresh .rd-tab {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-4);
    background: none;
    border: 0;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    font-family: inherit;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    color: var(--muted);
    white-space: nowrap;
    cursor: pointer;
    transition: color 0.12s ease, border-color 0.12s ease;
}
.ui-refresh .rd-tab i {
    font-size: 15px;
}
.ui-refresh .rd-tab:hover {
    color: var(--ink);
}
.ui-refresh .rd-tab.is-active {
    color: var(--accent-primary);
    border-bottom-color: var(--accent-primary);
}
.ui-refresh .rd-tab-panel {
    display: none;
    padding: var(--space-5);
}
.ui-refresh .rd-tab-panel.is-active {
    display: block;
}
/* Layout helpers for content inside a tab panel. min-width: 0 overrides the
   flex-item default of min-width: auto — without it, wide unconstrained
   content (e.g. a table with nowrap headers) forces this column past its
   share of the row instead of wrapping/scrolling internally. */
.ui-refresh .rd-tab-stack {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    min-width: 0;
}
/* minmax(0, 1fr), not a bare 1fr, so a column's minimum size is 0 rather than
   its content's intrinsic (auto) width — otherwise a wide table/button label
   in one column can steal space from its 50/50 sibling instead of both
   columns staying equal and wrapping their own content. */
.ui-refresh .rd-tab-cols {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--space-5);
    align-items: start;
}
/* Cards/panels nested inside a tab drop the outer shadow (and their own bottom
   margin) for a lighter, contained look; the .rd-tab-stack gap spaces them. */
.ui-refresh .rd-tabs .rd-tab-panel .rd-card,
.ui-refresh .rd-tabs .rd-tab-panel .panel {
    box-shadow: none;
    margin-bottom: 0;
}
@media (max-width: 900px) {
    .ui-refresh .rd-tab-cols {
        grid-template-columns: 1fr;
    }
}

/* =========================================================================
   Swoogo Admin › Account view  (.rd-* record detail)
   modules/loggedin/modules/admin/views/account/_view_ui_refresh.php
   ========================================================================= */

/* .rd-card paints var(--card) but never declared the companion variable, so
   every .form-control inside a card (Users-grid role selects, sub-account
   limit inputs) rendered transparent. See "surface containers must declare
   --control-bg-color" in the /design skill. */
.ui-refresh .rd-card {
    --control-bg-color: var(--bg-page);
    /* FixedColumns cells must paint an opaque fill to occlude horizontally
       scrolled content — inside a card that fill has to be the card's tone,
       not the page tone. Consumed by communicate.css's .action-col / .dtfc
       rules, which fall back to --bg-page everywhere else. */
    --grid-sticky-bg: var(--card);
}

/* The hero actions slot now holds a secondary button beside the Actions
   dropdown. Inert for the single-child rd views (contact / registrant /
   webhook / session). */
.ui-refresh .rd-header__actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

/* The shared 124px key column wraps "Salesforce contract ID" / "Stytch
   organization ID" inside a two-column .rd-tab-cols card. */
.ui-refresh .account-detail-kv .rd-kv__key {
    width: 170px;
}

/* ----- Sub-accounts inline-edit table (web/js/subaccount-user-limits.js) ----- */
.ui-refresh .sub-account-table-scroll {
    overflow-x: auto;
}
.ui-refresh .sub-account-allocation {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-normal);
    color: var(--muted);
}

/* table-layout: fixed locks every column to the widths declared below,
   permanently -- the browser can no longer grow a column (or the table as a
   whole) to fit whatever happens to be rendered in a cell. That's what two
   separate problems traced back to under the default table-layout: auto:
   edit-mode content forced the Full/Limited/Reporting/Actions columns wider
   than declared (the stacking bug), and that same growth pushed the table
   past its container, popping a horizontal scrollbar in .sub-account-table-
   scroll even in view mode. Content now has to live inside its column --
   wrap, or scroll via that same wrapper on truly narrow viewports -- rather
   than resize it. sa-name-col is deliberately left with no declared width:
   under fixed layout the one column without one takes 100% of whatever
   space is left after the four fixed-px columns below, which is what used
   to take a `width: 100%` hint (and, unlike that hint, can no longer force
   the table wider than its container). */
.ui-refresh .subaccount-user-limits-table {
    table-layout: fixed;
}
.ui-refresh .rd-table th.sa-num-col,
.ui-refresh .rd-table td.sa-num-col {
    width: 140px;
    text-align: left;
    white-space: nowrap;
}
.ui-refresh .rd-table th.sa-actions-col,
.ui-refresh .rd-table td.sa-actions-col {
    width: 88px;
    white-space: nowrap;
}
/* These four columns run tighter gutters than the .rd-table default of
   --space-5 (20px a side). At 20px a 140px number cell leaves only 100px of
   content box, which is less than "999/" + the gap + the limit input, and the
   88px Actions cell leaves only 48px for two buttons -- so edit mode wrapped
   the input under the count and the save button under the cancel button.
   --space-3 gives the number cell 116px and the Actions cell 64px, which is
   what the pairs below are sized against. */
.ui-refresh .rd-table th.sa-num-col,
.ui-refresh .rd-table td.sa-num-col,
.ui-refresh .rd-table th.sa-actions-col,
.ui-refresh .rd-table td.sa-actions-col {
    padding-left: var(--space-3);
    padding-right: var(--space-3);
}

/* Mode is driven by the row's `.is-editing` class, not by jQuery's inline
   display. subaccount-user-limits.js still calls .show()/.hide() because the
   legacy view.php table depends on it, and these rules deliberately carry
   !important so they out-rank that inline style — that is what guarantees the
   ui-refresh table can never render both modes at once. */
.ui-refresh .subaccount-user-limits-table .sa-edit-mode {
    display: none !important;
}
.ui-refresh .subaccount-user-limits-table tr.is-editing .view-mode {
    display: none !important;
}
.ui-refresh .subaccount-user-limits-table tr.is-editing .sa-edit-mode {
    display: inline-flex !important;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-1);
}
/* 30px count + 4px gap + 80px input = 114px, inside the 116px content box the
   number column always has under table-layout: fixed above -- no min-width
   safeguard needed here, the column can't be squeezed by another column's
   content the way it could under auto layout. */
.ui-refresh .sa-num-col .user-count {
    flex: 0 0 auto;
}
.ui-refresh .sa-limit-input.form-control {
    flex: 0 0 auto;
    width: 80px;
    height: 30px;
    padding: 0 var(--space-2);
    /* Was text-align: center — for an empty number input that puts the caret
       dead in the middle of the box, which reads as broken. Left-aligned
       matches every other text input in the app. */
    text-align: left;
    appearance: textfield;
    -moz-appearance: textfield;
}
/* The native stepper arrows steal ~16px and clip the "No limit" placeholder to
   "No lim". Same treatment as .lt-input-capacity in sessions.css:1091-1097. */
.ui-refresh .sa-limit-input.form-control::-webkit-inner-spin-button,
.ui-refresh .sa-limit-input.form-control::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* Cancel/save read as one segmented control: no gap, a shared border, and only
   the outer corners rounded. Square 30px buttons are also what lets the pair
   fit the 64px content box of the fixed-width Actions column -- the default
   .btn-sm side padding of var(--space-3) made them ~80px together.
   float: none is required because Bootstrap 3 floats .btn-group > .btn left;
   here they must behave as plain flex items. */
.ui-refresh .subaccount-user-limits-table .sa-actions-col .btn-group {
    display: inline-flex;
}
.ui-refresh .subaccount-user-limits-table tr.is-editing .sa-actions-col .sa-edit-mode {
    flex-wrap: nowrap;
    gap: 0;
}
.ui-refresh .subaccount-user-limits-table .sa-actions-col .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    padding: 0;
    float: none;
}
.ui-refresh .subaccount-user-limits-table .sa-actions-col .btn + .btn {
    margin-left: -1px;
}
.ui-refresh .subaccount-user-limits-table .sa-actions-col .btn-group > .btn:first-child:not(:last-child) {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}
.ui-refresh .subaccount-user-limits-table .sa-actions-col .btn-group > .btn:last-child:not(:first-child) {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
}
/* Save is raised so its accent border isn't overpainted by the cancel button's. */
.ui-refresh .subaccount-user-limits-table .sa-actions-col .btn-save-limits {
    position: relative;
    z-index: 1;
}
/* flex-basis:100% drops the validation message onto its own line under the
   input instead of beside it. `display:none` stays un-!important here: the JS
   shows this element with jQuery .show(), which needs the inline style to win. */
.ui-refresh .sa-field-error {
    display: none;
    flex-basis: 100%;
    margin-top: var(--space-1);
    font-size: var(--font-size-xs);
    color: var(--color-error);
    /* td.sa-num-col sets nowrap; without this the message runs the column wide. */
    white-space: normal;
}

/* Role <select> in the Users grid: it's rendered by Html::activeDropDownList(),
   so it never picks up ActiveField::dropdownList()'s `new-ui-select` class and
   falls back to the browser's native caret hard against the border. House
   treatment, identical to .hubspot-mapping-table / .eventmobi-mapping-table. */
.ui-refresh select.user-role-update.form-control {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background-image: var(--select-chevron-image);
    background-size: 0.6em;
    background-position: calc(100% - var(--space-3)) center;
    background-repeat: no-repeat;
    padding-right: var(--space-6);
    /* Truncate with an ellipsis instead of the browser's raw text clip, so the
       column width below can be trimmed without "No Access To This Acc" being
       cut off mid-word. */
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* .min-width-260 (web/css/utility.css) forces the Role header/column wide
   enough to show the longest option ("No Access To This Account") in full;
   combined with Action/Full Name/Email/Last Login/User Type it makes the
   whole grid wider than the Users card and DataTables' scrollX kicks in. The
   select above now truncates with an ellipsis, so the column doesn't need to
   fit the full string. Scoped to .ui-refresh #user-grid: user_grid.php is
   also rendered by the legacy (non-ui-refresh) account view at view.php:451,
   which must keep the original 260px width, and nothing else in the app uses
   min-width-260 on this grid. */
.ui-refresh #user-grid th.min-width-260 {
    min-width: 180px;
}

/* ----- Inner data tables inside a record-detail view -----
   Tab/panel content reuses legacy GridView + Bootstrap tables. Give them the
   clean list look from the mockups (uppercase muted header on a --paper-2 fill,
   hairline rows, accent links, subtle summary) — scoped to .rd-profile so only
   detail views are affected and the shared grid partials are never edited.
   `.rd-table` (the flat-profile bespoke table) keeps its own styling untouched. */

/* Section headings that carry a right-side action (Add attendance, Send email,
   Group actions) align like the mockup's card heads. */
.ui-refresh .rd-profile .panel > .panel-heading {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    flex-wrap: wrap;
}

.ui-refresh .rd-profile .panel table:not(.rd-table),
.ui-refresh .rd-profile .rd-card__body table:not(.rd-table) {
    width: 100%;
    border-collapse: collapse;
    border-spacing: 0;
    margin: 0;
    font-size: var(--font-size-sm);
}
.ui-refresh .rd-profile .panel table:not(.rd-table) > thead > tr > th,
.ui-refresh .rd-profile .rd-card__body table:not(.rd-table) > thead > tr > th {
    background: var(--paper-2);
    color: var(--muted);
    font-size: 11px;
    font-weight: var(--font-weight-semibold);
    text-align: left;
    padding: var(--space-2) var(--space-5);
    border: 0;
    border-bottom: 1px solid var(--panel-border);
    white-space: nowrap;
    vertical-align: middle;
}
.ui-refresh .rd-profile .panel table:not(.rd-table) > tbody > tr > td,
.ui-refresh .rd-profile .rd-card__body table:not(.rd-table) > tbody > tr > td {
    padding: var(--space-3) var(--space-5);
    border: 0;
    border-top: 1px solid var(--panel-border);
    color: var(--ink-2);
    vertical-align: middle;
}
.ui-refresh .rd-profile .panel table:not(.rd-table) > tbody > tr:first-child > td,
.ui-refresh .rd-profile .rd-card__body table:not(.rd-table) > tbody > tr:first-child > td {
    border-top: 0;
}
.ui-refresh .rd-profile .panel table:not(.rd-table) > tbody > tr:hover > td,
.ui-refresh .rd-profile .rd-card__body table:not(.rd-table) > tbody > tr:hover > td {
    background: var(--paper-2);
}
.ui-refresh .rd-profile .panel table:not(.rd-table) a,
.ui-refresh .rd-profile .rd-card__body table:not(.rd-table) a {
    color: var(--accent-primary);
}
/* Quick-action links (e.g. "No, Verify" / "No, Select" on the User view's Accounts
   table) read as plain muted text instead of accent — reserves accent color in
   these tables for actual navigation links (e.g. the account name). */
.ui-refresh .rd-profile .panel table:not(.rd-table) a.cursor-pointer,
.ui-refresh .rd-profile .rd-card__body table:not(.rd-table) a.cursor-pointer {
    color: var(--fg-muted);
}
.ui-refresh .rd-profile .panel table:not(.rd-table) a.cursor-pointer:hover,
.ui-refresh .rd-profile .rd-card__body table:not(.rd-table) a.cursor-pointer:hover {
    color: var(--fg-default);
}
/* Day-group separator rows (GroupGridView `.extrarow`, session attendance/meetings). */
.ui-refresh .rd-profile .panel table:not(.rd-table) > tbody > tr.extrarow > td,
.ui-refresh .rd-profile .rd-card__body table:not(.rd-table) > tbody > tr.extrarow > td {
    background: var(--paper-2);
    color: var(--ink);
    font-weight: var(--font-weight-semibold);
    font-size: var(--font-size-xs);
}
/* Page-widget tables (Selections / Transactions on the registrant Financials
   tab) render inside the bridged panel's padded body, leaving a strip of
   --card between the heading hairline and the table's --paper-2 header band —
   a bright inset frame on light surfaces, invisible on dark where the two
   tones nearly match. Bleed the widget wrapper to the card edges so the header
   band docks under the heading rule, the way .rd-table already sits. Content
   that follows a flush table (Submit Payment / Adjust Fees buttons) gets its
   gap back via the sibling rule. */
/* The bleed below assumes the bridge's card-body padding, but on event pages
   the ID-level `#content-container:not(.account) .panel-body` rules (4px
   sides / 8px top) out-rank the bridge's class-only rule, so the -20px
   side bleed overshot by 16px and the card's overflow:hidden clipped most
   of each cell's 20px padding — table text landed ~4px from the card edge
   instead of aligning with the heading. Pin the card-body padding at the
   same ID level, but only for bodies that host a page widget, so the bleed
   is exact here and no other bridged panel changes. */
.ui-refresh :is(#content-container, #report-content) .rd-profile .panel .panel-body:has(> .widget) {
    padding: var(--space-3) var(--space-5) var(--space-4);
}
.ui-refresh .rd-profile .panel .panel-body > .widget {
    margin: calc(-1 * var(--space-3)) calc(-1 * var(--space-5)) 0;
}
.ui-refresh .rd-profile .panel .panel-body > .widget:last-child {
    margin-bottom: calc(-1 * var(--space-4));
}
.ui-refresh .rd-profile .panel .panel-body > .widget + * {
    margin-top: var(--space-4);
}
/* tfoot summary rows (Total / Balance Due / Due Today): the thead/tbody rules
   above never covered tfoot, so Bootstrap's raw #ddd border-top and 5px
   condensed padding leaked through. */
.ui-refresh .rd-profile .panel table:not(.rd-table) > tfoot > tr > td,
.ui-refresh .rd-profile .rd-card__body table:not(.rd-table) > tfoot > tr > td {
    padding: var(--space-3) var(--space-5);
    border: 0;
    border-top: 1px solid var(--line);
    color: var(--ink);
    vertical-align: middle;
}
/* Row action buttons (Edit / Delete / Update status) are `a.btn.btn-default`
   in a .btn-group-sm. Pin them as ghost icon buttons — muted ink, accent on
   hover — so neither .btn-default's --bg-page !important fill nor the
   accent-link rule above wins, and both surfaces render identically.
   Bootstrap lays the buttons out with `.btn-group > .btn { float: left }`,
   which relies on the table column being wide enough to fit them on one
   line; combined with the narrow `width: 1%` actions column (`.rd-col-actions`,
   further below) the browser sometimes sizes the column to a single button and
   wraps the second one underneath it. Lay the group out with flex instead
   so it can't wrap regardless of column width. */
.ui-refresh .rd-profile .panel table:not(.rd-table) .btn-group-sm,
.ui-refresh .rd-profile .rd-card__body table:not(.rd-table) .btn-group-sm {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}
.ui-refresh .rd-profile .panel table:not(.rd-table) .btn-group-sm .btn,
.ui-refresh .rd-profile .rd-card__body table:not(.rd-table) .btn-group-sm .btn {
    float: none;
    background: transparent !important;
    border: 0 !important;
    box-shadow: none;
    color: var(--muted) !important;
    height: auto;
    padding: var(--space-1) var(--space-2);
}
.ui-refresh .rd-profile .panel table:not(.rd-table) .btn-group-sm .btn:hover,
.ui-refresh .rd-profile .panel table:not(.rd-table) .btn-group-sm .btn:focus-visible,
.ui-refresh .rd-profile .rd-card__body table:not(.rd-table) .btn-group-sm .btn:hover,
.ui-refresh .rd-profile .rd-card__body table:not(.rd-table) .btn-group-sm .btn:focus-visible {
    color: var(--accent-primary) !important;
}
.ui-refresh .rd-profile .panel table:not(.rd-table) .btn-group-sm .btn.disabled,
.ui-refresh .rd-profile .rd-card__body table:not(.rd-table) .btn-group-sm .btn.disabled {
    opacity: 0.4;
}
/* Bootstrap key/value tables (hotel request, tracking) — left column reads as a label. */
.ui-refresh .rd-profile .view-details-table > tbody > tr > td:first-child {
    color: var(--muted);
    font-weight: var(--font-weight-normal);
    width: 40%;
}
/* GridView summary ("Showing 1–N of M item.") */
.ui-refresh .rd-profile .grid-summary,
.ui-refresh .rd-profile .grid-view .summary {
    color: var(--muted);
    font-size: var(--font-size-sm);
    text-align: left;
}
/* GridView's shared layout repeats {summary}/{pager} above AND below the
   table (so long lists keep pagination reachable from the bottom). Modern
   SaaS convention (Linear, Notion, GitHub, Stripe) keeps a single count +
   pager after the last row, where the user finishes scanning — so hide the
   pre-table copy here and keep only the bottom one. `:has(~ table)` selects
   the summary/pager that has a table AFTER it, i.e. the top copy; list
   pages elsewhere are untouched and keep both. */
.ui-refresh .rd-card__body .grid-view .grid-summary:has(~ table),
.ui-refresh .rd-card__body .grid-view .text-center:has(~ table) {
    display: none;
}

/* ----- Session attendance grid (registrant-attendance/_registrant-view.php) -----
   That bridged .panel uses the shared GridView layout too, but its body is
   `.panel-body`, not `.rd-card__body`, so the rule above never reached it and
   the count rendered twice around a two-row table. Under ui-refresh the
   partial switches to a compact layout (table first, then one `.rd-grid-foot`
   holding count + pager) and the footer hides itself whenever there is
   nothing to page: CustomLinkPager renders nothing below two pages, so
   `.rd-grid-pager` is empty. */
.ui-refresh .rd-profile .rd-grid-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-5) var(--space-4);
}
.ui-refresh .rd-profile .rd-grid-foot:has(> .rd-grid-pager:empty) {
    display: none;
}
.ui-refresh .rd-profile .rd-grid-foot .pagination {
    margin: 0;
}
/* Right-aligned column headers (the row-actions header below is the only
   current consumer of .text-right). Registered / Attended / On demand no
   longer carry .text-center — see _registrant-view.php — so their headers
   and cells both fall back to the default left alignment, matching Session. */
.ui-refresh .rd-profile .panel table:not(.rd-table) > thead > tr > th.text-right,
.ui-refresh .rd-profile .rd-card__body table:not(.rd-table) > thead > tr > th.text-right {
    text-align: right;
}
/* Row-actions column: as narrow as its buttons, right-aligned. Surface-
   agnostic on purpose — `.rd-col-actions` is an opt-in class used both by the
   bridged `.panel table:not(.rd-table)` grids (session attendance) and native
   `.rd-table`s (registrant Group Info), whose own `th`/`td` rules would
   otherwise force left alignment and auto width. */
.ui-refresh .rd-profile th.rd-col-actions,
.ui-refresh .rd-profile td.rd-col-actions {
    width: 1%;
    white-space: nowrap;
    text-align: right;
}
/* Two-line cells: a title line in ink over a muted sub line (session name
   over its times). Links inside keep the accent colour from the table rule. */
.ui-refresh .rd-profile .rd-cell-title {
    color: var(--ink);
    font-weight: var(--font-weight-medium);
}
.ui-refresh .rd-profile .rd-cell-sub {
    margin-top: 2px;
    font-size: var(--font-size-xs);
    color: var(--muted);
    /* A long unbroken value (a group-member email address) must break inside
       the cell rather than widen the table: `.rd-table` is `width: 100%` with
       auto layout, so an unbreakable token would otherwise force the table
       wider than its card and clip or scroll instead of wrapping. */
    overflow-wrap: anywhere;
}
/* Dock the table's header band under the card heading rule: the same bleed
   the page-widget tables get from the `.widget` rules above. Pin the body
   padding at ID level first so the negative margins are exact on event pages
   (the ID-level #content-container .panel-body rules otherwise win). Under
   ui-refresh the Pjax grid is the body's only child, so it also owns the
   bottom edge. */
.ui-refresh :is(#content-container, #report-content) .rd-profile .panel .panel-body:has(> #attendance-grid) {
    padding: var(--space-3) var(--space-5) var(--space-4);
}
.ui-refresh .rd-profile .panel .panel-body > #attendance-grid {
    margin: calc(-1 * var(--space-3)) calc(-1 * var(--space-5)) calc(-1 * var(--space-4));
}

/* Staff-only balance-mismatch alert (transactions panel): the badge sits above
   the alert with breathing room, so it reads as an intentional flag rather than
   a permanent panel ornament. Only rendered when there's a mismatch. */
.ui-refresh .rd-profile .rd-balance-alert {
    margin-top: var(--space-4);
}
.ui-refresh .rd-profile .rd-balance-alert .staff-indicator {
    display: inline-block;
    margin-bottom: var(--space-2);
}

/* ============================================================================
   Roles & Permissions (modules/loggedin/views/role) — ui-refresh
   ----------------------------------------------------------------------------
   Layout resembles event/update.php: a page-local roles sub-sidebar on the
   left (Standard / Custom roles) and FLAT permission content on the right
   (no outer card, no per-group cards — group sections sit on the page bg).
   The app-level #secondary-nav.account-nav rail is untouched and sits to the
   left of this whole block.

   All JS hooks are preserved (.permission-card, .role-group-expand,
   .role-group-link, .role-group-link-icon, chevron-drop, role-list-title-open,
   #permission-search, #clear-search, #permissions-container, #scrollable-content,
   #roles-list, .roles-list, .delete-tag, .dot-menu, #form-result,
   #role-name-display/-edit/-input, #rbac-save-role/-cancel-edit,
   #event-setup-count, #account-hub-count). This block only restyles them.
   ============================================================================ */

/* --- Sidebar + content shell --------------------------------------------- */
/* .role-layout is the top-level split: the roles rail runs the full height on
   the left (alongside BOTH the breadcrumb and the content), and .role-main is
   the right column that stacks the breadcrumb over the flat content. The rail
   is deliberately NOT nested under the breadcrumb so it scrolls on its own. */
.ui-refresh .role-page .role-layout {
    display: flex;
    align-items: flex-start;
}
.ui-refresh .role-page #roles-list.role-list-pane {
    flex: 0 0 250px;
    width: 250px;
    max-width: 250px;
    box-sizing: border-box;
    align-self: stretch;
    overflow-y: auto;
    padding: var(--space-6);
    border-right: 1px solid var(--line);
    height: calc(100vh - 60px);
}
/* Right column: breadcrumb on top, flat content below. */
.ui-refresh .role-page .role-main {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    height: calc(100vh - 60px);
    overflow-y: auto
}

.ui-refresh #content-container.account #main-content:has(#custom-roles) {
  padding: 0;
  overflow: hidden !important;
}

/* Flat content column — no card, sits on the page background. */
/* .ui-refresh .role-page .role-detail-pane { */
/*     overflow-y: auto; */
/* } */
.ui-refresh .role-page .role-detail-header {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    margin-bottom: var(--space-2);
}

/* --- Left list: section titles ("Standard Roles" / "Custom Roles") ------- */
.ui-refresh .role-page .secondary-menu-title {
    display: flex;
    flex-direction: column;
    justify-items: center;
    gap: var(--space-2);
    /* Natural casing — override the legacy uppercase treatment. */
    text-transform: none;
    color: var(--muted);
    font-family: var(--font-family);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    letter-spacing: 0;
    margin: var(--space-5) 0 var(--space-2);
}
.ui-refresh .role-page .secondary-menu-title .title-holder {
    display: flex;
    gap: var(--space-3);
}
.ui-refresh .role-page .secondary-menu-title .title-holder:first-child {
    margin-top: 0;
}
.ui-refresh .role-page .secondary-menu-title .title-holder .help-message,
.ui-refresh .role-page .secondary-menu-title .title-holder a {
    color: var(--fg-subtle);
    font-size: var(--font-size-xs);
    text-decoration: none;
    transition: color var(--transition-fast);
}
.ui-refresh .role-page .secondary-menu-title .title-holder a:hover {
    color: var(--accent-primary);
}
.ui-refresh .role-page .secondary-menu-title .title-holder a {
    margin-left: auto;
}
/* Injected create/clone row: a sibling <li> in the rail (NOT inside
   .secondary-menu-title). Keep it within the fixed-width rail — the input must
   be allowed to shrink (min-width:0) so its intrinsic width doesn't push the
   rail wider, and the confirm/cancel buttons stay compact. */
.ui-refresh .role-page .role-list-pane .role-input-row {
    display: flex;
    gap: var(--space-4);
    align-items: center;
    max-width: 100%;
}
.ui-refresh .role-page .role-list-pane .role-input-row .role-name-input {
    flex: 1 1 auto;
    min-width: 0;
    height: 36px;
}
.ui-refresh .role-page .role-list-pane .role-input-row .input-group-btn {
    flex: 0 0 auto;
}
.ui-refresh .role-page .role-list-pane .role-input-row .input-group-btn .btn {
    padding-left: var(--space-2);
    padding-right: var(--space-2);
}

/* --- Left list: role links ----------------------------------------------- */
.ui-refresh .role-page .roles-list {
    list-style: none;
    padding-left: 0;
    margin: 0 0 var(--space-2);
}
.ui-refresh .role-page .roles-list > li {
    display: flex;
    align-items: center;
    padding: 0;
    margin: 2px 0;
    border-radius: var(--radius-md);
    transition: background-color var(--transition-fast);
}
.ui-refresh .role-page .roles-list > li:hover {
    background-color: var(--state-hover);
}
/* The ui-refresh markup wraps the link in .title and the menu in .buttons. */
.ui-refresh .role-page .roles-list .delete-tag .title {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
}
.ui-refresh .role-page .roles-list .delete-tag .buttons {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
}
.ui-refresh .role-page .roles-list li a {
    display: block;
    flex: 1 1 auto;
    min-width: 0;
    padding: var(--space-2) var(--space-3);
    color: var(--ink);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    text-decoration: none;
    border-radius: var(--radius-md);
    transition: color var(--transition-fast);
}
.ui-refresh .role-page .roles-list li a.active {
    color: var(--accent-primary);
    font-weight: var(--font-weight-semibold);
    background-color: var(--accent-subtle);
}
.ui-refresh .role-page .roles-list li a:hover:not(.active) {
    color: var(--accent-primary);
}
.ui-refresh .role-page .roles-list .dot-menu {
    color: var(--fg-subtle);
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-sm);
}
.ui-refresh .role-page .roles-list .dot-menu:hover {
    color: var(--ink);
}

/* "Sub Accounts Roles" jump button sits below the lists. */
.ui-refresh .role-page .role-list-pane .btn-primary {
    width: 100%;
    margin-top: var(--space-4);
}

/* --- Detail: role title row (name + gear) -------------------------------- */
.ui-refresh .role-page .role-title-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}
.ui-refresh .role-page .role-title {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0;
    min-width: 0;
    color: var(--ink);
    font-family: var(--font-family);
    font-size: var(--font-size-xl);
    font-weight: var(--font-weight-semibold);
    letter-spacing: -0.01em;
}
.ui-refresh .role-page .role-title i {
    font-size: var(--font-size-lg);
    font-weight: 300;
    margin-right: 0;
    color: var(--muted);
}
/* Live "Saved" confirmation flash (populated by role-view.js) sits between
   the name and the gear, pushing the gear to the far right. */
.ui-refresh .role-page .role-title-row #form-result {
    flex: 1 1 auto;
    font-weight: var(--font-weight-semibold);
    font-size: var(--font-size-sm);
    color: var(--color-success);
    text-align: right;
}
/* Gear opens the role settings slideout. Sits top-right of the title row. */
.ui-refresh .role-page .role-settings {
    flex: 0 0 auto;
    margin-left: auto;
    padding: 0;
}
.ui-refresh .role-page .role-settings a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    font-size: 20px;
    border-radius: var(--radius-md);
    color: var(--muted);
    transition: all var(--transition-fast);
}
.ui-refresh .role-page .role-settings a:hover {
    color: var(--accent-primary);
    background-color: var(--state-hover);
}

/* --- Detail: permissions toolbar (scope switch + search) ----------------- */
.ui-refresh .role-page .permissions-toolbar {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}
.ui-refresh .role-page .permissions-scope-switch {
    flex: 0 0 auto;
    min-width: 0;
}
/* Event / Account Hub toggle rendered as a segmented pill pair. */
.ui-refresh .role-page .permissions-scope-switch .btn-group {
    display: inline-flex;
    background: var(--bg-muted);
    border-radius: var(--radius-md);
    padding: 2px;
    gap: 2px;
}
.ui-refresh .role-page .role-group-type {
    font-weight: var(--font-weight-medium);
    font-size: var(--font-size-sm);
    border: none;
    background: transparent;
    color: var(--muted);
    border-radius: var(--radius-sm);
    padding: var(--space-1) var(--space-3);
    box-shadow: none;
    transition: all var(--transition-fast);
}
.ui-refresh .role-page .role-group-type:hover {
    color: var(--ink);
}
.ui-refresh .role-page .role-group-type.btn-primary {
    background: var(--card);
    color: var(--ink);
    box-shadow: var(--shadow-xs);
}
/* Search input + inline clear "x", wrapped so the x can sit inside the field. */
.ui-refresh .role-page .permission-search-wrap {
    position: relative;
    flex: 1 1 200px;
    min-width: 160px;
}
.ui-refresh .role-page #permission-search {
    width: 100%;
    height: var(--input-height);
}
.ui-refresh .role-page .permission-search-wrap #clear-search {
    position: absolute;
    right: var(--space-3);
    top: 50%;
    transform: translateY(-50%);
    cursor: pointer;
    color: var(--fg-subtle);
    z-index: 2;
}
/* Autocomplete dropdown (role-view.js populates #suggestion-box with
   .suggestion rows). Anchored under the search input via the relative wrap. */
.ui-refresh .role-page #suggestion-box {
    position: absolute;
    top: calc(100% + var(--space-1));
    left: 0;
    right: 0;
    z-index: 20;
    max-height: 260px;
    overflow-y: auto;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: var(--space-1);
}
.ui-refresh .role-page #suggestion-box .suggestion {
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    font-size: var(--font-size-sm);
    color: var(--ink);
    cursor: pointer;
    transition: background-color var(--transition-fast);
}
.ui-refresh .role-page #suggestion-box .suggestion:hover,
.ui-refresh .role-page #suggestion-box .suggestion:focus {
    background-color: var(--state-hover);
    outline: none;
}
/* Legacy (non-wrapped) fallback for the AJAX-rendered / older markup. */
.ui-refresh .role-page .permissions-toolbar > #permission-search {
    flex: 1 1 200px;
    min-width: 160px;
}

/* --- Detail: FLAT permission sections ------------------------------------ */
.ui-refresh .role-page .permissions-container,
.ui-refresh .role-page .permissions-scroll {
    margin-top: var(--space-4);
    overflow: visible;
}
/* Each top-level permission group is a flat section (like About/When/Where
   on event/update.php) — no card border, separated by a hairline rule. */

.ui-refresh .role-page .permission-section {
    border-bottom: 1px solid var(--border-default);
}
.ui-refresh .role-page .permission-section:last-child {
    border-bottom: none;
}

/* A permission row (group header or a leaf/sub permission). The markup uses
   Bootstrap .row/.col-sm-* — neutralize the float+fixed widths so the row
   lays out as flex: auto-width chevron gutter + flexible description. */
.ui-refresh .role-page .permission-card {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) 0;
    position: relative;
    border-bottom: none;
}
.ui-refresh .role-page .permission-card.role-list-title-open {
  background-color: transparent !important;
}
.ui-refresh .role-page .permission-card > .permission-box {
    float: none;
    width: auto;
    flex: 0 0 auto;
    min-height: 1px;
    padding: 0;
}
.ui-refresh .role-page .permission-card > .description {
    float: none;
    width: auto !important;
    flex: 1 1 auto;
    min-width: 0;
    padding: 0;
}
/* Chevron gutter reserves a fixed width even when a leaf row has no chevron,
   so leaf titles line up under their group title. */
.ui-refresh .role-page .permission-card > .permission-box {
    width: 20px;
    text-align: center;
}
/* Top-level group header row — reads as a section title. */
.ui-refresh .role-page .permission-section > .permission-card {
    padding: var(--space-4) 0;
}
.ui-refresh .role-page .permission-card.is-sub,
.ui-refresh .role-page .permission-card.row-no-sub {
    padding-top: var(--space-2);
    padding-bottom: var(--space-2);
}

/* Depth-based nesting: children live in a `<div class="collapse">` wrapper, so
   indenting THAT wrapper makes each level compound through the DOM nesting —
   a grandchild (e.g. Event Settings under Attendee Concierge Agent) is indented
   twice, so its parent relationship is legible. Each level also carries a left
   guide rule so the hierarchy reads as a tree, not a flat list. */
.ui-refresh .role-page .permission-section .collapse,
.ui-refresh .role-page .permission-section .collapsing {
    margin-left: var(--space-5);
    padding-left: var(--space-4);
    border-left: 1px solid var(--line);
}
/* The individual sub row no longer owns the indent/guide (the wrapper does). */
.ui-refresh .role-page .permission-card.is-sub {
    margin-left: 0;
    padding-left: 0;
    border-left: none;
}

/* The ui-refresh path renders no .search-match-bar (a filtered result is
   self-evident), so no marker styling is needed here — see _permission_card.php. */

/* Group header title — larger + semibold, like a section heading. */
.ui-refresh .role-page .permission-section > .permission-card .permission-title {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
    color: var(--ink);
}
.ui-refresh .role-page .permission-title-description-wrapper {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.ui-refresh .role-page .permission-title {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--ink);
}
.ui-refresh .role-page .permission-sub-title .permission-title {
    font-weight: var(--font-weight-normal);
    color: var(--fg-muted);
}
.ui-refresh .role-page .role-group-description {
    margin: 0;
    font-size: var(--font-size-xs);
    color: var(--muted);
}
.ui-refresh .role-page .role-group-link,
.ui-refresh .role-page .role-group-link:hover,
.ui-refresh .role-page .role-group-link:focus {
    display: block;
    text-decoration: none;
    color: inherit;
}
/* Chevron toggles rotate via .chevron-drop → fa-chevron-down (JS-driven). */
.ui-refresh .role-page .role-group-link i {
    font-weight: 900;
    margin-right: var(--space-2);
    color: var(--fg-subtle);
    transition: transform var(--transition-fast);
}
.ui-refresh .role-page .perm-title-soft span {
    color: var(--muted);
    font-weight: var(--font-weight-normal);
}
.ui-refresh .role-page .role-list-title-open .permission-title {
    color: var(--ink);
}
.ui-refresh .role-page .permissions-empty {
    padding: var(--space-6) 0;
    color: var(--muted);
    font-size: var(--font-size-sm);
}

/* ============================================================================
   Standard permissions checkbox tree (_standard_permissions.php)
   Rendered by the admin PermissionSet form — full ui-refresh treatment.
   ============================================================================ */
.ui-refresh .standard-permissions-well {
    background: var(--bg-page);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: none;
}
.ui-refresh .standard-permission-sub {
    padding: var(--space-2) var(--space-3) var(--space-3);
    border: 1px solid var(--line);
    background: var(--card);
    border-radius: var(--radius-md);
}
.ui-refresh .standard-permission-toggle-all {
    font-size: var(--font-size-xs);
    color: var(--muted);
    margin-bottom: var(--space-2);
}
.ui-refresh .standard-permission-toggle-all a.check-all,
.ui-refresh .standard-permission-toggle-all a.uncheck-all {
    cursor: pointer;
    color: var(--accent-primary);
    text-decoration: none;
}
.ui-refresh .standard-permission-toggle-all a:hover {
    text-decoration: underline;
}
.ui-refresh .sub-perm-shorter-border:not(.role-list-title-open) .description {
  border-bottom: none;
}
/* Legacy sub-wrapper keeps its original inline look via this class so the
   non-ui-refresh path is visually unchanged. */
.standard-permission-sub-legacy {
    padding: 0 5px 5px;
    border: 1px solid #CCC;
    background: white;
    border-radius: 4px;
}

/* ============================================================================
   Role settings AjaxDrawer (role/ajax-drawer/update.php) — section dividers
   ----------------------------------------------------------------------------
   The drawer renders inside #ajax-drawer, so the .ajax-drawer chrome already
   supplies the freebies: transparent/borderless panels, narrow width, and the
   .sidebar-body horizontal inset (--sidebar-x-padding). The only thing it
   doesn't give a shared `.panel-heading` is a full-bleed divider. The role
   drawer reuses shared partials (_event_type_restriction's `.panel`, _form's
   SAML `uiFormSectionOpen` header), so add an edge-to-edge rule under each
   section heading — pulled out to the drawer edges with a negative margin that
   cancels the .sidebar-body inset, while the label text stays inset.
   Scoped to `.role-drawer-body` so other ajax drawers are untouched.
   ============================================================================ */
.ui-refresh #ajax-drawer .role-drawer-body .panel > .panel-heading,
.ui-refresh #ajax-drawer .role-drawer-body .comm-form-section .event-settings-section-head {
    margin-left: calc(var(--sidebar-x-padding) * -1);
    margin-right: calc(var(--sidebar-x-padding) * -1);
    padding-left: var(--sidebar-x-padding);
    padding-right: var(--sidebar-x-padding);
    padding-bottom: var(--space-3);
    margin-bottom: var(--space-4);
    border-bottom: 1px solid var(--line);
}
.ui-refresh .account-section #salesforce-sync-data {
    margin-bottom: var(--space-4);
    margin-top: var(--space-4);
}
.ui-refresh  .input-group .input-group-btn.input-group-append .btn {
    border-bottom-right-radius: var(--input-radius);
    border-top-right-radius: var(--input-radius);

}
.ui-refresh .input-group-btn {
    width: auto;
}
.ui-refresh .input-group-btn .btn {
    border-radius: var(--btn-radius);
}
.ui-refresh .input-group-btn .btn:first-child {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}
.ui-refresh .input-group-btn .btn:last-child {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
}

.ui-refresh .table > tbody > tr.info > td,
.ui-refresh .table > tfoot > tr.info > td,
.ui-refresh .table > thead > tr.info > th {
    background-color: var(--accent-subtle) !important;
}
/* On dark surfaces, --accent-subtle resolves to an 18%-alpha saturated
   accent wash — fine for a one-off badge, but far brighter than this table's
   own neutral --state-hover/--state-active overlays when used as a
   persistent "current row" highlight. Tone it down to the same alpha range
   while keeping the accent tint so it still reads as distinct from a plain
   hover. Higher specificity than the base rule above (adds body[data-surface]),
   so it wins regardless of source order. */
body[data-surface="dark"] .ui-refresh .table > tbody > tr.info > td,
body[data-surface="dark"] .ui-refresh .table > tfoot > tr.info > td,
body[data-surface="dark"] .ui-refresh .table > thead > tr.info > th {
    background-color: rgba(103, 98, 192, 0.1) !important;
}
.ui-refresh .rd-tabs .visit-summary {
    padding-top: var(--space-1);
}
.ui-refresh .rd-tabs .tl-item,
.ui-refresh .rd-tabs .visit-summary {
    padding-left: var(--space-4);
    padding-right: var(--space-4);
}
.ui-refresh :is(#content-container, #report-content) .panel-heading > .row {
    flex: 1;
}

/* -------------------------------------------------------------------------- */
/* User & Roles — rbac/index stat cards (modules/loggedin/views/user-account)  */
/* -------------------------------------------------------------------------- */
/* The panel rules are scoped to #content-container so they outweigh           */
/* active-form.css's `#content-container .panel:not(...) { border: none        */
/* !important }` (an ID selector) — a plain `.ui-refresh .users-stats .panel`  */
/* loses on specificity even with !important. Same reason we must !important    */
/* border/radius/shadow here. See the "scope panel overrides to               */
/* #content-container" note in the ui-refresh skill.                           */

.ui-refresh .users-stats {
    gap: var(--space-3);
    padding-top: 0;
    display: flex;
}

.ui-refresh #content-container .users-stats a.panel-container {
    background: transparent;
    border-bottom: none;
    max-width: 100% !important;
    flex: auto !important;
    margin: auto !important;
}

.ui-refresh #content-container .users-stats a.panel-container .panel {
    width: 100%;
    background-color: var(--bg-surface) !important;
    border: 1px solid var(--border-subtle) !important;
    border-radius: var(--radius-lg) !important;
    box-shadow: var(--shadow-sm) !important;
    transition: border-color 0.12s ease, background-color 0.12s ease;
}

/* Hover/active: accent border paired with accent-subtle background (never a
   gray fill behind an accent border — see the ui-refresh accent-hover rule). */
.ui-refresh #content-container .users-stats a.panel-container:hover > .panel,
.ui-refresh #content-container .users-stats a.panel-container.active > .panel {
    background-color: var(--accent-subtle) !important;
    border-color: var(--accent-primary) !important;
    color: var(--fg-default) !important;
    border-radius: var(--radius-lg) !important;
}

.ui-refresh #content-container .users-stats a.panel-container:hover > .panel .user-type-remaining,
.ui-refresh #content-container .users-stats a.panel-container.active > .panel .user-type-remaining {
    color: var(--fg-muted) !important;
}

.ui-refresh #content-container .users-stats .panel-heading {
    text-transform: none;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--fg-muted);
}

.ui-refresh #content-container .users-stats .user-type-count {
    font-size: var(--font-size-2xl);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
}

.ui-refresh #content-container .users-stats .user-type-remaining {
    font-size: var(--font-size-xs);
    color: var(--success-primary);
}

/* Active-filter close chip — tint to the accent instead of the legacy red.
   Flex-center the × glyph (line-height centering is unreliable at small sizes).
   Explicit width/height/font-size override the legacy values so the circle is
   consistent; line-height reset prevents the inherited 24px from offsetting
   the glyph inside the flex container. */
.ui-refresh .users-stats > a.panel-container.active > .close-icon {
    color: var(--accent-primary);
    background: var(--bg-surface);
    box-shadow: var(--shadow-sm);
    display: inline-flex !important;
    align-items: center;
    justify-content: center;
    line-height: 1;
}

.ui-refresh .users-stats > a.panel-container.active > .close-icon:hover {
    color: var(--fg-on-accent);
    background: var(--accent-primary);
}

.ui-refresh .users-and-roles {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

/* Neutralise the legacy divider under the stat row — spacing/background
   contrast defines the region without an <hr> (ui-refresh restraint). */
.ui-refresh .users-and-roles .hr {
    display: none;
}

/* ── Change History empty state ── */
.ui-refresh .activity-feed-empty {
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
    text-align: center;
    padding: var(--space-6) var(--space-4);
    margin: 0;
}

/* ── Seat quantity stepper (add-seats drawer) ──
   number-input.css uses hardcoded #CCC borders and relies on line-height
   centering for the +/- spans. Re-skin to surface tokens and center the
   wrapper so it reads correctly in both light and dark modes. */
.ui-refresh .add-seats-form .spots-input-wrapper {
    border-color: var(--input-border);
    border-radius: var(--radius-md);
    display: inline-flex;
    align-items: stretch;
}
.ui-refresh .add-seats-form .spots-input-wrapper span {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    color: var(--fg-default);
    background: var(--bg-muted);
    height: auto;
}
.ui-refresh .add-seats-form .spots-input-wrapper span:first-child {
    border-top-left-radius: var(--radius-md);
    border-bottom-left-radius: var(--radius-md);
}
.ui-refresh .add-seats-form .spots-input-wrapper span:last-child {
    border-top-right-radius: var(--radius-md);
    border-bottom-right-radius: var(--radius-md);
}
.ui-refresh .add-seats-form .spots-input-wrapper span:hover {
    background-color: var(--state-hover);
}
.ui-refresh .add-seats-form .spots-input-wrapper input[type=number] {
    border-color: var(--input-border) !important;
    color: var(--fg-default);
    background: var(--bg-surface);
    height: auto;
}
/* Center the whole stepper block horizontally */
.ui-refresh .add-seats-form .text-center {
    display: flex;
    justify-content: center;
    align-items: center;
}

.ui-refresh .tt-menu {
    background-color: var(--bg-surface);
    box-shadow: var(--sh-md);
}
.ui-refresh .tt-menu .tt-suggestion {
    color: var(--fg-default);
}
.ui-refresh .has-error .twitter-typeahead .tt-input,
.ui-refresh .has-error .twitter-typeahead .tt-hint {
    border-color: var(--color-error);
}
.ui-refresh .has-success .twitter-typeahead .tt-input,
.ui-refresh .has-success .twitter-typeahead .tt-hint {
    border-color: var(--input-border);
}
.ui-refresh .has-success .control-label {
    color: var(--fg-muted) !important;
}
.ui-refresh .add-multi-images-img {
    margin-top: 0;
    margin-right: 0;
}

/* Row delete button for the inline tables inside a drawer — shared by the Room
 * Types table (.srt-delete), the choices tables (.sct-delete) and the reg-type
 * date table (.hrt-delete), which each carried their own copy of this treatment.
 * Mirrors .close-icon-btn in layout.css (rounded background tint on hover) at the
 * smaller square size an inline table row wants. Lives here rather than in any
 * one feature stylesheet because those ship in separate asset bundles
 * (SidebarRoomTypesTableAsset / SidebarChoicesTableAsset / FeeUiRefreshFormAsset
 * / HotelRegTypeDatesAsset) and none is guaranteed when another loads. */
.ui-refresh .sidebar-row-delete {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 0;
    background: none;
    border: none;
    border-radius: var(--radius-md);
    cursor: pointer;
    color: var(--fg-subtle);
    transition: color var(--transition-fast), background-color var(--transition-fast);
}
.ui-refresh .sidebar-row-delete:hover {
    color: var(--fg-default);
    background-color: var(--bg-muted);
}

/* Rows that can't be removed (e.g. a room type with live bookings) — has to read
 * as clearly inert on its own, since the tooltip explaining why only appears on
 * hover/focus. */
.ui-refresh .sidebar-row-delete[disabled] {
    color: var(--fg-subtle);
    opacity: var(--state-disabled-opacity);
    cursor: not-allowed;
    /* Browsers swallow hover events on a disabled control, so the tooltip has to
     * be bound to an enclosing wrapper instead (.srt-tooltip-wrap in the Room
     * Types table); forwarding the events to it is what makes the explanation
     * reachable at all. */
    pointer-events: none;
}
.ui-refresh .sidebar-row-delete[disabled]:hover {
    color: var(--fg-subtle);
    background-color: transparent;
}

/* ===== Mobile app setup pages (Go Onsite / Go Attend setup) ===== */

/* Breathing room at the bottom of the scroll area — #main-content zeroes its
   own padding for .event-settings-doc / .list-page pages (layout.css ~81), so
   without this the last section/card touches the viewport edge exactly. */
.ui-refresh .mobile-app-setup {
    padding-bottom: var(--space-10);
}

/* Section rhythm — mirrors .account-section values (components.css ~893).
   padding-top (not margin-top): sections nest inside the "Enable Go Onsite"
   .collapse panel, which animates height only — a margin here would collapse
   through the panel's top edge and pop at the end of the reveal/close
   transition instead of animating smoothly with it. */
.ui-refresh .mobile-app-setup .mobile-setup-section {
    padding-top: var(--space-8);
}
.ui-refresh .mobile-setup-section-heading {
    font-family: var(--font-family);
    font-size: 14px;
    line-height: 1.3;
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
    margin: 0 0 var(--space-4);
}
.ui-refresh .mobile-setup-section-heading:has(+ .mobile-setup-section-desc) {
    margin-bottom: var(--space-2);
}
.ui-refresh .mobile-setup-section-desc {
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
    line-height: 1.55;
    margin: 0 0 var(--space-4);
}

/* Hero enable card (common/_feature-enable-card) — the page's primary
   control, so it gets heavier chrome than the .toggle-group rows it gates.
   An optional footer (e.g. store badges) sits below a divider. */
.ui-refresh .feature-enable-card {
    background: var(--bg-surface);
    --control-bg-color: var(--bg-page);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    padding: var(--space-5);
    margin-bottom: var(--space-6);
}
.ui-refresh .feature-enable-main {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
}
.ui-refresh .feature-enable-controls {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}
.ui-refresh .feature-enable-controls > .checkbox-toggle {
    flex-shrink: 0;
}
.ui-refresh .feature-enable-save-status {
    min-width: 7ch;
    max-width: 240px;
    color: var(--fg-muted);
    font-size: var(--font-size-xs);
    text-align: right;
}
.ui-refresh .feature-enable-save-status:empty {
    visibility: hidden;
}
.ui-refresh .feature-enable-save-status.is-error {
    color: var(--color-error);
}
.ui-refresh .feature-enable-heading {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}
.ui-refresh .feature-enable-label {
    margin: 0;
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
}
.ui-refresh .feature-status-pill {
    display: inline-flex;
    align-items: center;
    font-size: 10px;
    font-weight: var(--font-weight-medium);
    line-height: 1;
    padding: 4px var(--space-2) 3px;
    border-radius: var(--radius-full);
    border: 1px solid var(--border-default);
    background: var(--bg-muted);
    color: var(--fg-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.ui-refresh .feature-status-pill[data-state="on"] {
    background: var(--color-success-bg);
    color: var(--color-success);
    border-color: var(--color-success-bg);
}
.ui-refresh .feature-enable-hint {
    margin: var(--space-1) 0 0;
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
    line-height: 1.55;
}
.ui-refresh .feature-enable-footer {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-4);
    margin-top: var(--space-4);
    padding-top: var(--space-4);
    border-top: 1px solid var(--panel-border);
}
.ui-refresh .feature-enable-footer .get-app-label {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--fg-default);
}
.ui-refresh .feature-enable-footer .get-app-badges {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-left: auto;
}
.ui-refresh .feature-enable-footer .play-store-badge img {
    display: block;
    height: 50px;
}
.ui-refresh .feature-enable-footer .app-store-badge img {
    display: block;
    height: 35px;
}

/* Badge-by-reg-type table card */
.ui-refresh .badge-reg-type-card {
    margin-top: var(--space-3);
}
.ui-refresh .badge-reg-type-card select.form-control {
    width: 300px;
    max-width: 100%;
    display: inline-block;
}

/* Badge printing default-badge select — replaces the legacy inline width */
.ui-refresh .go-onsite-badge-options .badge-printing-select {
    max-width: 320px;
}

/* Walk-in registrant-type select — replaces the legacy inline width */
.ui-refresh .mobile-app-setup .walk-in-reg-type-select {
    max-width: 320px;
}

/* Multi-column field picker: extend the .field-picker-container card to the
   variant with a thead + extra columns (Registrant Fields, Go Onsite) */
.ui-refresh .field-picker-container .field-picker-selected-fields thead th {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-subtle);
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--panel-border);
    text-align: left;
}
.ui-refresh .field-picker-container .field-picker-selected-fields thead th.text-right {
    text-align: right;
}
/* Extra columns (e.g. "Editable") left-align with their checkbox instead of
   the trailing delete-button column's right alignment. Only applied in
   ui-refresh — the legacy render keeps .text-right instead (see the PHP view
   and buildRow() in multi-column-field-picker.js). width: 1% shrinks the
   column to its content (the "Editable" header, its widest cell) instead of
   auto-layout splitting leftover table width evenly between this and the
   Field column. */
.ui-refresh .field-picker-container .field-picker-selected-fields thead th.field-picker-extra-col,
.ui-refresh .field-picker-container .field-picker-selected-fields td.field-picker-extra-col {
    width: 1%;
    white-space: nowrap;
    text-align: left;
}
.ui-refresh .field-picker-container .field-picker-selected-fields td.field-picker-extra-col {
    padding-left: var(--space-3);
    padding-right: var(--space-3);
}
/* Error-on-hover outline, matching the query-builder pattern (query-builder.css ~145) */
.ui-refresh .field-picker-container .delete-multi-fields-btn {
    border: 1px solid transparent;
    transition: background-color var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}
.ui-refresh .field-picker-container .delete-multi-fields-btn:hover {
    background: var(--color-error-bg);
    border-color: var(--color-error);
    color: var(--color-error);
}
/* "Choose Fields" button — full-width dashed add row, page scope (mirrors
   the drawer-scoped .ajax-drawer .field-picker-add-btn rule above) */
.ui-refresh .field-picker-container .field-picker-add-btn {
    text-align: center;
}
.ui-refresh .field-picker-container .field-picker-add-btn .btn {
    border: 1px dashed var(--panel-border);
    background: transparent;
    color: var(--fg-muted);
    font-size: var(--font-size-xs);
    border-radius: var(--radius-md);
    padding: var(--space-2) 0;
    width: 100%;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.ui-refresh .field-picker-container .field-picker-add-btn .btn:hover {
    border-color: var(--accent-primary);
    color: var(--accent-on-tint);
    background: var(--accent-subtle);
}

/* Bootstrap collapse animates height only, so margins that escape a section's
   bottom edge (a .toggle-group's or .form-group's margin-bottom collapsing
   through) pop when the transition ends. Sections space themselves with
   padding-top above; zero the escaping bottom margins so the rhythm is
   exactly the padding and nothing jumps. */
.ui-refresh .mobile-app-setup .mobile-setup-section > :last-child {
    margin-bottom: 0;
}
/* The first section sits below the "Enable X" .feature-enable-card, whose
   24px bottom margin is outside the animated panel and always present —
   8px + 24px reproduces the 32px section gap used everywhere else. Keyed off
   adjacency to .feature-enable-card (not a page-specific panel class) so it
   applies to every Go Onsite setup page's collapse wrapper — go-onsite.php's
   .go-onsite-collapsible-panel, go-onsite-kiosk.php's .kiosk_mode, etc. —
   without needing a matching entry per page. Targets the sibling `div`
   itself rather than its `.collapse` class: Bootstrap's collapse.js swaps
   `collapse` out for `collapsing` for the duration of the show/hide
   animation, so a `.collapse` requirement here would drop out mid-transition
   and pop the padding back to --space-8 until the animation finished.
   `div` never changes, so the compensation holds throughout. */
.ui-refresh .mobile-app-setup .feature-enable-card + div > .mobile-setup-section:first-child {
    padding-top: var(--space-2);
}
/* Dependent pickers under a toggle match the event Settings treatment
   (flush left, no rule) instead of the Virtual Location indent+left-rule
   that #content-container .panel-expanded-settings (see ~5996) would apply.
   Zeroing the non-animated padding/margins also removes the pop at the ends
   of the collapse animation; the gap above the revealed content comes from
   the shared `.toggle-row > label + .collapse > :first-child` rule. */
.ui-refresh #content-container .mobile-app-setup .toggle-row > .panel-expanded-settings {
    border-left: none;
    margin: 0;
    padding: 0;
}
/* ToC anchor offset below the sticky subpage head. Sections carry their gap
   as padding-top, so subtract it to land headings at the same optical 90px
   the .account-section pattern uses. */
.ui-refresh .event-settings-content .mobile-setup-section {
    scroll-margin-top: calc(90px - var(--space-8));
}
/* Every ToC target lives inside the Go Onsite collapse — while the feature
   is toggled off, keep the rail in the layout but dim it and make it inert
   so the page frame doesn't jump. Opacity transition matches Bootstrap's
   0.35s collapse animation so the rail fades in step with the panel. */
.ui-refresh .event-settings-doc:has(.go-onsite-collapsible-panel) .event-settings-toc {
    transition: opacity 0.35s ease;
}
.ui-refresh .event-settings-doc:has(.go-onsite-collapsible-panel:not(.in)) .event-settings-toc {
    opacity: 0.4;
    pointer-events: none;
}
/* Neutralize the scroll-spy's active chip while disabled — a dimmed
   "highlighted" item would still read as navigable. */
.ui-refresh .event-settings-doc:has(.go-onsite-collapsible-panel:not(.in)) .event-settings-toc a.active {
    background: none;
    border-left-color: transparent;
    font-weight: inherit;
}

/* ===== Go Onsite: Kiosk Mode ===== */

/* Field width caps — replace the legacy inline widths. The accent-color
   spectrum group is targeted via its ActiveField container class since
   Kartik owns the inner input markup. */
.ui-refresh .mobile-app-setup .field-kiosksettings-accent_color .spectrum-group {
    max-width: 320px;
}
.ui-refresh .mobile-app-setup .kiosk-payment-gateway-select {
    max-width: 320px;
}

/* Beta pill sitting inside a section heading */
.ui-refresh .mobile-setup-section-heading .rd-badge {
    margin-left: var(--space-2);
    vertical-align: middle;
}

/* The payment-options collapse is a section's last child; its inner
   .toggle-group carries the default --space-5 bottom margin, which lives
   outside the animated height and would pop at the end of the collapse
   transition. Sections space themselves with padding-top, so zero it. */
.ui-refresh .mobile-app-setup .kiosk_payment_options > .toggle-group {
    margin-bottom: 0;
}

/* ===== MultiImagePickerV2 (sponsor logos) =====
   The widget markup is shared with legacy pages (go-attend-whitelabel), so
   restyle purely via overrides of web/css/multiimagepicker-v2.css. */
.ui-refresh .mip-v2-label {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--fg-default);
}
.ui-refresh .mip-v2-container {
    background: var(--bg-surface);
    --control-bg-color: var(--bg-page);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    padding: var(--space-4);
}
.ui-refresh .mip-v2-placeholder img {
    border-radius: var(--radius-md);
}
.ui-refresh .mip-v2-item img {
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
}
/* Dashed add-row button, mirroring .field-picker-add-btn (~17116) */
.ui-refresh .mip-v2-add-btn {
    border: 1px dashed var(--panel-border);
    background: transparent;
    color: var(--fg-muted);
    font-size: var(--font-size-xs);
    border-radius: var(--radius-md);
    transition: border-color var(--transition-fast), color var(--transition-fast), background var(--transition-fast);
}
.ui-refresh .mip-v2-add-btn:hover {
    border-color: var(--accent-primary);
    color: var(--accent-on-tint);
    background: var(--accent-subtle);
}
/* Quiet danger for remove-all */
.ui-refresh .mip-v2-remove-all-btn {
    background: transparent;
    border: 1px solid transparent;
    color: var(--fg-muted);
    transition: background-color var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}
.ui-refresh .mip-v2-remove-all-btn:hover {
    background: var(--color-error-bg);
    border-color: var(--color-error);
    color: var(--color-error);
}
.ui-refresh .mip-v2-limit-warning {
    color: var(--color-error);
}

/* ===== Account events list (/loggedin/event/index) =====
   Two-column shell: a folder rail pinned to the viewport, and the list column
   that scrolls under it. .events-layout is the outermost element on the page
   (a direct child of #main-content) so the rail sits beside the account-level
   sidebar (#secondary-nav) — not nested under the page header, which lives
   inside .events-main instead. See PR2 of #2371.

   The rail is `position: fixed`, matching #secondary-nav exactly (same
   positioning model, same height formula) rather than `position: sticky`:
   sticky is bounded by its own containing block (here, the grid row), so once
   scrolled near the end of a table only a little taller than one screen, it
   ran out of row to stick within and detached — looking like the sidebar
   itself was scrolling. Fixed positioning has no such bound; it stays pinned
   for as long as the page has anything left to scroll, exactly like the
   account nav beside it.

   The folder tree markup inside the rail is still the legacy partial; only the
   container is refreshed here. */
.ui-refresh .events-layout {
    /* No longer a grid: .events-rail is position:fixed and out of flow, so
       .events-main is the only thing left to lay out — it reserves the
       rail's old column via its own margin-left below. */
    position: relative;
    /* Where #main-content's content box starts in the default (unpinned)
       account-nav state: #content-container's static left (295px, from
       .page-body's .nav-margin) + #main-content's own 20px left padding.
       Retuned below when the account nav is pinned to its 56px icon rail, or
       absent. A variable rather than `left` on .events-rail itself so the
       drag handle (.events-rail-resizer, below) can share it. */
    --events-rail-left: 315px;
    /* How far the rail is pulled left of that content-box start. Without it
       the rail sat 18px off the account nav's border but only 8px off its own
       resize line (padding-right below), so the tree read as lopsided. 10px
       leaves 8px on both sides, matching the account nav's own 8px item inset.
       .events-rail, .events-rail-resizer and .events-main all subtract it,
       so the rail-to-table gap is unchanged. */
    --events-rail-shift: 10px;
    /* Today's rail width — grows modestly on wide monitors (review feedback
       on #2617) — is now the CEILING, not the width. The rail auto-fits its
       widest folder row instead (measureRailFit(), event-list.js writes the
       measurement into --events-rail-fit), and the user can drag it anywhere
       between --events-rail-min and this. With no --events-rail-fit yet (first
       paint, no cookie) the fallback is the ceiling, i.e. exactly the old
       layout, so nothing regresses when JS is late. */
    --events-rail-max: clamp(240px, 20vw, 480px);
    /* Floor = the tree's own fitted width (measureRailFit(), event-list.js
       writes it into --events-rail-content), so the user can widen the rail
       from its fitted size but never squeeze the widest row — and its
       trailing count/action slot — past the visible edge. 180px is only the
       pre-JS fallback; min() keeps a tree wider than the ceiling from lifting
       the floor above it (clamp() returns its MIN when MIN > MAX). */
    --events-rail-min: min(var(--events-rail-content, 180px), var(--events-rail-max));
    /* Shared with .events-main's margin-left below so the two stay in sync.
       CSS does the clamping — JS/PHP only ever write a raw px into
       --events-rail-fit, so a stale cookie can't widen the rail past the old
       size or squash it under the floor. */
    --events-rail-width: clamp(var(--events-rail-min), var(--events-rail-fit, var(--events-rail-max)), var(--events-rail-max));
}
.ui-refresh .events-rail {
    position: fixed;
    /* Matches #secondary-nav's own `top: 60px !important` (components.css) —
       both start at the same viewport offset, the account top-nav's height. */
    top: 60px;
    /* Declared on .events-layout (with the pinned/no-nav retunes below) so
       .events-rail-resizer can sit on the same edge. */
    left: calc(var(--events-rail-left) - var(--events-rail-shift));
    width: var(--events-rail-width);
    /* .events-main reserves this rail's width, so it can inherit the page
       canvas instead of painting a separate near-black fixed surface. */
    background: transparent;
    z-index: 5;
    /* Aligns the first folder row with the .subpage-head breadcrumb in the
       next column. That head is padding-top var(--space-6) (24px) + a 40px
       button band, so its text centres 44px below the column top; the legacy
       tree contributes 4px of its own lead-in before a 20px text line
       (ul.folders li padding + .folder margin/padding, draggable-folders.css),
       so its text centres at padding-top + 14px. 24 + 6 = 30 puts both on the
       same line. */
    padding-top: calc(var(--space-6) + 6px);
    /* Viewport-exact instead of a height formula: `top: 60px` + a computed
       height (the previous `calc(100vh - 48px)`) put the box's bottom edge
       12px past the viewport (60 + 100vh - 48 = 100vh + 12), so the tree's
       own overflow was clipped by the viewport instead of by this scroller —
       the "scrollbar on the page" bug from #2413. `bottom: 0` also sidesteps
       the vh/svh mismatch against #main-content's own `calc(100svh - 60px)`
       (ui-refresh/layout.css) on mobile browsers with a collapsing URL bar. */
    bottom: 0;
    overflow-y: auto;
    /* Was `hidden` — reverted per review on #2694: a deep tree can still be
       wider than the rail (see .name below), so `auto` only shows a
       scrollbar when that genuinely happens, kept inside the rail. */
    overflow-x: auto;
    overscroll-behavior: contain;
    padding-right: var(--space-2);
    /* Without this, the last folder sits flush against the clip edge at full
       scroll — same gutter as padding-right, just on the opposite axis. */
    padding-bottom: var(--space-4);
    /* Same thin, fade-in-on-hover treatment as #secondary-nav.account-nav
       (below) — both-edges keeps the tree's left edge stable whether or not
       the scrollbar is currently rendered. */
    scrollbar-width: thin;
    scrollbar-color: transparent transparent;
    scrollbar-gutter: stable both-edges;
}
.ui-refresh .events-rail:hover {
    scrollbar-color: var(--gray-40) transparent;
}
.ui-refresh .events-rail::-webkit-scrollbar {
    width: 6px;
    /* Horizontal track, now that overflow-x is auto too. */
    height: 6px;
    background-color: transparent;
}
.ui-refresh .events-rail::-webkit-scrollbar-thumb {
    background-color: transparent;
    border-radius: var(--radius-full);
}
.ui-refresh .events-rail:hover::-webkit-scrollbar-thumb {
    background-color: var(--gray-40);
}
.ui-refresh .events-rail::-webkit-scrollbar-thumb:hover {
    background-color: var(--gray-50);
}
@media (min-width: 993px) {
    /* Mirrors the exact same account-nav-pinned selector shape used for the
       content offset elsewhere (.responsive-account-setup.nav-margin.account-
       nav-pinned, above): when the nav collapses to its 56px icon rail,
       #main-content's content starts at 56 + 20 = 76px instead of 315px. Only
       .account-nav-pinned is needed to scope this — .page-body can only carry
       that class when responsive-account-setup is also set (main.php), so it
       can't accidentally match an unrelated pinned rail elsewhere. */
    .ui-refresh .page-body.account-nav-pinned .events-layout {
        --events-rail-left: 76px;
    }
}
/* A user without canAccess('account_setup') gets no #secondary-nav at all —
   account-hub-full-width.php (the layout this page uses) then force-resets
   .page-body's margin to 0 (account-hub-full-width.php's own inline
   `.icon-nav-margin { margin-left: 0 !important }`), so #main-content's
   content box starts at just its own 20px padding instead of 315px. Without
   this, the rail stays parked at 315px while the table starts around 284px
   (main-content's 20px + .events-main's own margin-left below), so the rail
   visually overlaps the table's left columns instead of sitting beside them.
   :has() (already used elsewhere in this codebase, e.g. navigation.css's
   .icon-nav-margin:has(.wizard-container)) is the only reliable signal here:
   .page-body still carries the same .icon-nav-margin class other pages use
   for a real, present icon-only nav, so the class alone can't distinguish
   "no nav at all" from "icon nav present". */
.ui-refresh .page-body:not(:has(#secondary-nav)) .events-layout {
    --events-rail-left: var(--space-5);
}
/* layout.css collapses #main-content's content box to a flat 20px below
   992px regardless of account-nav state; none of the rules above branch on
   viewport, so the rail stayed parked at its wider offset. */
@media (max-width: 992px) {
    .ui-refresh .events-layout {
        --events-rail-left: var(--space-5);
    }
}
/* Tree rows — folder/_tree.php + _tree-node.php (PR4 of #2371). These are
   new ui-refresh-only partials, not the legacy folder/_folders.php +
   _folder.php pair (still shared, unmodified, by Images/Files), but they
   keep the same class names draggable-folders.js addresses by selector
   (ul.folders, .folder, .draggable, .expand, .name …), so
   draggable-folders.css's unscoped skin (negative ul/li margins, #EEE hover,
   bold active state, absolute #folder-edit-panel at top:1px) still applies
   underneath. Everything below overrides that skin, scoped to .events-rail
   only, so Images/Files keep the legacy look untouched. Tokens mirror
   .ui-refresh #secondary-nav.account-nav (above) so the two rails read as
   one system. */
.ui-refresh .events-rail ul.folders,
.ui-refresh .events-rail ul.folders ul {
    margin: 0;
    padding-left: 0;
}
.ui-refresh .events-rail ul.folders ul {
    /* One indent step per nesting level; the outer ul.folders (root row) and
       each folder's own child ul both match this selector, so depth N gets
       N * space-4 automatically. */
    padding-left: var(--space-4);
}
.ui-refresh .events-rail ul.folders li {
    margin: 0;
    padding: 0;
}
.ui-refresh .events-rail .folder {
    position: relative;
    display: flex;
    align-items: center;
    /* max-content, not a flat 100%: .name below can overflow the rail, and a
       flat 100% clipped the hover/active highlight short of the visible
       text. min-width keeps short rows at full column width as before. */
    width: max-content;
    min-width: 100%;
    /* Never overridden on hover/active, so it can't itself cause a shift —
       just a hairline gap between two adjacent highlighted pills. */
    margin: 0 0 1px;
    /* Left gutter is reserved unconditionally, so a row's label lines up the
       same whether or not its caret is currently visible. (The room the hover
       action panel needs is NOT padding — it's the .folder-trailing flex slot
       below, shared with the count badge.)
       Tighter vertical padding than a first pass (was 6px): a folder tree
       nests far deeper than a flat nav list, so row density matters more
       here — design feedback on the first cut called the 6px rows too loose. */
    padding: 3px var(--space-2) 3px 16px;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    font-family: var(--font-family);
    font-size: 13px;
    font-weight: var(--font-weight-medium);
    line-height: var(--line-height-sm);
    color: var(--ink-2);
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}
/* Overrides draggable-folders.css's `.folder:hover, .folder-hover { margin: 0px;
   background: #EEE; border: 1px solid #CCC }` — same selectors (.folder-hover
   is jQuery UI droppable's hoverClass during an actual drag), lighter fill,
   no 1px margin jump. The base rule above already reserves the same 1px
   transparent border, so background/border-color are the only things
   changing here — nothing about the box's own size does. */
.ui-refresh .events-rail .folder:hover,
.ui-refresh .events-rail .folder-hover {
    background: var(--state-hover);
    border-color: transparent;
}
/* Overrides draggable-folders.css's `.folder.active { font-weight: bold }`
   with a filled pill, matching #secondary-nav.account-nav's active leaf. */
.ui-refresh .events-rail .folder.active {
    background: var(--bg-muted);
    border-color: var(--border-subtle);
    font-weight: var(--font-weight-medium);
}
.ui-refresh .events-rail .folder .fa-fw.fa-folder,
.ui-refresh .events-rail .folder .fa-fw.fa-calendar-check,
.ui-refresh .events-rail .folder .fa-fw.fa-cloud,
.ui-refresh .events-rail .folder .fa-fw.fa-lock {
    flex: 0 0 auto;
    width: 18px;
    margin-right: 8px;
    font-size: 15px;
    color: var(--fg-subtle);
    text-align: center;
}
.ui-refresh .events-rail .folder.active .fa-fw.fa-folder,
.ui-refresh .events-rail .folder.active .fa-fw.fa-calendar-check,
.ui-refresh .events-rail .folder.active .fa-fw.fa-cloud {
    color: var(--accent-primary);
}
/* Chevron caret (fa-chevron-right collapsed / fa-chevron-down expanded, set
   in _tree-node.php and toggled by draggable-folders.js's now-data-driven
   folderExpandToggle) — replaces the legacy fa-plus/fa-minus glyph, which
   didn't read as "this expands/collapses" (#2413 design feedback). Absolutely
   positioned in the reserved gutter so it doesn't participate in the flex
   row's own spacing. */
.ui-refresh .events-rail .expand {
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 8px;
    font-size: 10px;
    text-align: center;
    color: var(--ink-2);
}
.ui-refresh .events-rail .name {
    /* Non-shrinking/non-wrapping on purpose: flex:1 1 auto + min-width:0 let
       a row always squeeze to fit, so .events-rail's overflow-x never had
       anything to actually scroll into. This lets deep rows genuinely
       overflow instead. */
    flex: 0 0 auto;
    white-space: nowrap;
}
/* Direct event count (Folder::folderCounts(), _tree-node.php) — a
   folder with zero events renders no badge at all rather than a "0", so an
   account with mostly-empty folders isn't wall-to-wall zeroes. Styled as a
   light pill, same family as the events-table's status badges, rather than
   bare text. */
/* One fixed-size trailing slot per row (_tree-node.php / _tree.php), sized
   to the hover action panel it gives way to (three 24px buttons + the
   panel's --space-1 inset). At rest it holds the count badge, left-aligned so
   the number still reads as "next to the name". Because every row carries
   it, the rail's fitted width (measureRailFit(), event-list.js) already
   includes exactly the room the buttons need — and no more.
   The height is fixed as well as the width: an empty slot (root row, folders
   with no events) would otherwise be 0px tall, leaving that row 4px shorter
   than one with a 24px badge, so swapping in the 24px-tall panel on hover
   grew the row and pushed every row below it down. */
.ui-refresh .events-rail .folder-trailing {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    width: calc(3 * 24px + var(--space-1));
    height: 24px;
    margin-left: var(--space-2);
}
.ui-refresh .events-rail .folder-count {
    letter-spacing: normal;
}
/* While the action panel is in the row (draggable-folders.js's showEditPanel()
   appends it on mouseenter) the slot steps aside for it: the panel is the
   same fixed width, so the swap can't reflow the row or leave a blank where
   the count was. Keyed on the panel's own visibility, NOT mere presence:
   hideEditPanel() only ever sets it `display: none` on mouseleave — it never
   detaches the node — so it stays a descendant of whichever .folder it was
   last shown in. `:has(#folder-edit-panel)` alone would therefore match (and
   hide the trailing slot) forever after that row's first hover, not just
   while hovered. `:not([style*="none"])` reads jQuery's own inline
   `display: none;` it writes on hide — the same landmine the pre-existing
   `.image-library`/`.file-library .folder:has(#folder-edit-panel)` rules
   above sidestep only because they merely reserve blank padding, which stays
   harmless even once stuck. */
.ui-refresh .events-rail .folder:has(#folder-edit-panel:not([style*="none"])) .folder-trailing {
    display: none;
}
/* #folder-edit-panel is a single DOM node draggable-folders.js's
   showEditPanel() moves into whichever .folder is hovered (and back out to
   <body> on mouseleave) — restyle it in place rather than assuming it lives
   in any one row. draggable-folders.css owns `display: none` and
   `position: absolute; top: 1px; right: 1px`; here it becomes a static flex
   item instead, sitting exactly where .folder-trailing just stepped aside
   (same width, same margin) — right after the name the pointer is on, not
   pinned to a short row's far-right edge. jQuery's .show() sets an inline
   display:block, which is fine for a block flex item; the buttons inside
   are inline-grid, styled to match .list-table .lt-cell-actions .lr-icon
   (components.css, events table). */
.ui-refresh .events-rail #folder-edit-panel {
    position: static;
    flex: 0 0 auto;
    width: calc(3 * 24px + var(--space-1));
    /* Match the trailing slot even when permissions leave the panel empty. */
    min-height: 24px;
    margin-left: var(--space-2);
    top: auto;
    right: auto;
    transform: none;
    white-space: nowrap;
}
.ui-refresh .events-rail #folder-edit-panel.btn-group-xs > .btn {
    display: inline-grid;
    place-items: center;
    width: 24px;
    height: 24px;
    padding: 0;
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--muted);
    font-size: 12px;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .events-rail #folder-edit-panel.btn-group-xs > .btn:hover {
    background: var(--paper-2);
    color: var(--ink);
}
.ui-refresh .events-rail #folder-edit-panel .btn-delete:hover {
    color: var(--danger-primary);
}
/* showEditPanel() adds .disabled for a non-empty folder or the root — a real
   <a>, so pointer-events has to do what a disabled <button> gets for free. */
.ui-refresh .events-rail #folder-edit-panel .btn-delete.disabled,
.ui-refresh .events-rail #folder-edit-panel .btn-delete.disabled:hover {
    background: transparent;
    color: var(--muted);
    opacity: var(--state-disabled-opacity);
    cursor: not-allowed;
    pointer-events: none;
}
/* min-width:0 lets the table shrink instead of forcing the row wider than
   available space. margin-left reserves the rail's (fixed, out-of-flow)
   column via the same --events-rail-width custom property, so the two can't
   drift out of sync. */
.ui-refresh .events-main {
    min-width: 0;
    /* Minus --events-rail-shift: the rail is pulled left by that much, so the
       column follows it and the rail-to-table gap stays the same. */
    margin-left: calc(var(--events-rail-width) + var(--space-6) - var(--events-rail-shift));
}
/* One-frame state measureRailFit() (event-list.js) toggles to read the
   tree's widest-POSSIBLE width: every collapsed branch is forced visible so
   the measurement covers the whole structure and expanding a folder later
   never shifts the layout, and the rail shrink-wraps its content for one
   synchronous getBoundingClientRect() — nothing paints in between. */
.ui-refresh .events-rail.is-measuring {
    width: max-content;
}
.ui-refresh .events-rail.is-measuring ul.folders ul {
    /* !important: beats _tree-node.php's inline style="display:none;" on a
       closed folder's child list. */
    display: block !important;
}
.ui-refresh .events-rail.is-measuring .folder {
    /* The base rule's min-width:100% is cyclic inside a max-content parent;
       zero it so each row contributes only its own content width. */
    min-width: 0;
}
/* Drag handle — a SIBLING of .events-rail (event/index.php), not a child: the
   rail is its own scroll container, so a child pinned to its right edge would
   scroll away with the tree. Same fixed model as the rail, one layer above
   it, glued to its right edge through the same two variables so it follows
   both a drag and the pinned-nav retunes above. Invisible until hovered,
   focused or dragging. event-list.js's bindRailResizer() owns the pointer /
   keyboard / dblclick behaviour. */
.ui-refresh .events-rail-resizer {
    position: fixed;
    top: 60px;
    bottom: 0;
    left: calc(var(--events-rail-left) - var(--events-rail-shift) + var(--events-rail-width) - 4px);
    width: 8px;
    z-index: 6;
    cursor: col-resize;
    /* The ::before line doubles as the focus ring (tabindex=0, arrow keys
       nudge the width), so the default outline would just stack on it. */
    outline: none;
}
.ui-refresh .events-rail-resizer::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    /* The handle starts 4px left of the rail edge, so this sits the line on
       the edge. 1px matches the account nav / top bar chrome borders. */
    left: 4px;
    width: 1px;
    /* Faint at rest so the handle reads as part of the layout before the user
       ever reaches for it — hover/focus/drag escalate below. */
    background: var(--border-subtle);
    transition: background-color var(--transition-fast);
}
.ui-refresh .events-rail-resizer:hover::before,
.ui-refresh .events-rail-resizer:focus-visible::before {
    background: var(--border-strong);
}
.ui-refresh .events-layout.is-resizing .events-rail-resizer::before {
    background: var(--accent-primary);
}
/* Floor == ceiling (a folder row as wide as --events-rail-max; the rail
   scrolls horizontally instead): nothing to drag, so the handle stops
   advertising it. pointer-events:none drops the col-resize cursor, the
   :hover line and the title tooltip in one go; event-list.js
   updateRailLock() toggles the class and pulls the handle out of the tab
   order. The at-rest 1px ::before divider stays. */
.ui-refresh .events-layout.is-rail-locked .events-rail-resizer {
    pointer-events: none;
    cursor: auto;
}
/* While dragging: no text selection, and neither column can steal hover or
   pointer events from the captured handle (folder rows would otherwise
   flicker their hover state under the moving pointer). */
.ui-refresh .events-layout.is-resizing {
    cursor: col-resize;
    user-select: none;
}
.ui-refresh .events-layout.is-resizing .events-rail,
.ui-refresh .events-layout.is-resizing .events-main {
    pointer-events: none;
}
/* .subpage-head and .list-page now live inside .events-main (the narrower
   grid track), not directly in #main-content — but the shared centering rule
   above (#main-content:has(.list-page) .subpage-head) still resolves its
   `100%` against #main-content, subtracting --doc-gutter from a column that's
   already inset by the rail + grid gap. That double-counts the gutter and
   leaves the table ~80px narrower than it should be. Reclaim it here; the rail
   and grid gap already provide the left separation this page needs.
   #main-content is included purely to match (and win the cascade tie against)
   the centering rule's specificity — this isn't really scoped to
   #main-content, just to .events-main within it.
   max-width: none (not --doc-max-width): with a cap, narrowing the resizable
   rail past the point where .events-main exceeds 1240px turned the slack into
   growing auto side margins. Filling the column keeps both gutters constant
   at every rail width. */
.ui-refresh #main-content .events-main .list-page,
.ui-refresh #main-content .events-main .subpage-head {
    width: 100%;
    max-width: none;
}
@media (max-width: 767px) {
    .ui-refresh .events-rail,
    .ui-refresh .events-rail-resizer {
        display: none;
    }
    /* No rail to reserve space for at this width — drop the desktop offset so
       the table isn't left with a large empty gutter on its left. */
    .ui-refresh .events-main {
        margin-left: 0;
    }
}

/* Path of the selected folder, above the toolbar. */
.ui-refresh .events-path {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1);
    margin: 0 0 var(--space-3);
    font-family: var(--font-family);
    font-size: var(--font-size-sm);
    color: var(--muted);
}
.ui-refresh .events-path-sep {
    color: var(--muted);
}
.ui-refresh .events-path-link {
    color: var(--muted);
    cursor: pointer;
}
.ui-refresh .events-path-link:hover {
    color: var(--ink);
    text-decoration: none;
}
.ui-refresh .events-path-current {
    color: var(--ink);
    font-weight: var(--font-weight-medium);
}

/* Drag ghost shown while dragging an event row onto a folder (jQuery UI
   draggable's `helper`, built in draggable-folders.js). draggable.js's
   default appendTo:"parent" puts the ghost in the DRAGGED ELEMENT's own
   parentNode (draggable.js:378-382) — for an event row that's .list-table's
   <tbody>, i.e. inside .events-main's own subtree — so scoping to
   .events-main here reaches only this page's ghost. draggable-folders.js is
   also shared by Images and Files, which render the identical .drag-helper
   markup for their own drag-and-drop, but under their own unrelated
   containers, never under .events-main — so this can't reach theirs, and
   deliberately doesn't try to: neither page has any ui-refresh treatment yet,
   and dropping a modernized ghost onto an otherwise fully legacy page would
   look like a stray glitch, not an improvement.
   The un-refreshed box is a fixed 120x120 card with a 3em icon (see
   .drag-helper / .drag-helper span in draggable-folders.css) — a heavy,
   dated treatment next to the flat .list-table rows it now floats over.
   Shrunk to a compact auto-sized card; tokens match .list-table's own row
   styling (13px, --ink, --line) so the ghost reads as part of the same
   system as what it's being dragged across.
   i.fa-3x is a literal class in the JS-generated helper HTML, not something
   this stylesheet controls — overriding its font-size directly is simplest,
   and safe: FontAwesome's own .fa-3x rule carries no !important, so normal
   specificity is enough to win. */
/* z-index:auto (the default here) always paints below a positioned sibling
   that sets an explicit z-index, no matter which one is later in the DOM —
   and .events-rail is exactly that (fixed, z-index:5). Without this, the
   ghost vanishes under the rail the moment the drag crosses into it.
   .folder-helper doesn't need this because draggable-folders.css already
   gives it z-index:50; matching that value here instead of just clearing
   .events-rail's 5 keeps both drag ghosts on the same visual layer. */
.ui-refresh .events-main .drag-helper {
    width: auto;
    height: auto;
    max-width: 200px;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--sh-md);
    padding: var(--space-2) var(--space-3);
    color: var(--ink);
    font-family: var(--font-family);
    font-size: 13px;
    overflow: visible;
    z-index: 50;
}
.ui-refresh .events-main .drag-helper i.fa-3x {
    font-size: 1.1em;
    color: var(--muted);
}

/* draggable-folders.css already sets cursor:move for .draggable at
   min-width:768px (the legacy row-level treatment), but at (0,1,0)
   specificity a single-class selector is easy for something else to outrank
   without anyone noticing. Redeclaring it here, scoped to the actual row,
   makes the hover affordance explicit rather than incidental — same
   defensive-redeclare reasoning as .dash-panel-heading's own cursor:move
   above. Action icons inside the row (.lr-icon, real <a> elements) keep
   their own pointer cursor regardless — inheritance only fills in where
   nothing more specific already applies, and an anchor's cursor is the
   browser's own UA default, not something this rule touches. */
.ui-refresh .events-main .list-table tbody tr.draggable {
    cursor: move;
}

/* ----- Session conflicts (accordion + time-axis grid) ----- */
/* Accordion header chevron/table conversion reuses .panel-swoogo-collapse and
   .list-table (declared above); this block covers only the custom
   absolute-positioned time-axis grid view, which has no other precedent in
   the app to reuse — replaces the inline hex/style attributes it used to
   carry (#ddd/#eee/#666/#fff → tokens; per-block background/border driven by
   state classes instead of PHP-built inline style strings). */
.ui-refresh .conflicts-grid-container .time-column {
    border-right: 1px solid var(--line);
    background: var(--card);
}
.ui-refresh .conflicts-grid-container .time-slot {
    border-bottom: 1px solid var(--line);
    color: var(--muted);
}
.ui-refresh .conflicts-grid-container .sessions-grid .grid-line {
    border-bottom: 1px solid var(--line);
}
.ui-refresh .conflicts-grid-container .session-block {
    background: var(--paper-2);
    border: none;
    border-radius: var(--radius-md);
    color: var(--ink);
}
.ui-refresh .conflicts-grid-container .session-block.has-conflict-speaker {
    background: color-mix(in srgb, var(--color-error) 12%, var(--paper));
}
.ui-refresh .conflicts-grid-container .session-block.has-conflict-location {
    background: color-mix(in srgb, var(--color-warning) 12%, var(--paper));
}
.ui-refresh .conflicts-grid-container .session-block.has-conflict-time {
    background: color-mix(in srgb, var(--color-info) 12%, var(--paper));
}
.ui-refresh .conflicts-grid-container .session-block.is-accepted {
    background: var(--paper);
    border: 2px dashed var(--border-strong);
    opacity: 0.6;
}
.ui-refresh .conflicts-grid-container .session-block .session-block__name {
    font-weight: var(--font-weight-semibold);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.ui-refresh .conflicts-grid-container .session-block .session-block__time {
    color: var(--muted);
    font-size: 11px;
}
.ui-refresh .conflicts-grid-container .session-block .session-block__badges {
    margin-top: var(--space-1);
}
.ui-refresh .conflicts-grid-container .session-block .conflict-badge {
    padding: 2px var(--space-2);
    font-size: 10px;
}
.ui-refresh .conflicts-row.is-accepted {
    opacity: 0.6;
}

/* Legacy .filter-badge (backend.css) is an absolutely-positioned corner
   count bubble, meant to overlay an icon/button. Inside .conflict-badge it's
   a plain inline count instead, so drop the positioning. Background stays
   neutral (ink/paper invert) rather than currentColor or an accent hue —
   the surrounding badges are speaker/location/time-colored, and a fixed
   neutral chip reads as "count" regardless of which badge it sits in. The
   ink/paper pairing also self-inverts with the theme (dark chip on light
   paper, light chip on dark paper) instead of needing a dark-mode override. */
.ui-refresh .conflict-badge .filter-badge {
    position: static;
    top: auto;
    right: auto;
    background-color: var(--black-70);
    color: var(--white-100);
}

/* Accordion header: chevron matches the heading text color (both muted,
   same de-emphasized treatment as .comm-collapsible-chevron) rather than
   the chevron alone reading lighter than the text beside it. */
.ui-refresh .session-conflicts .panel-swoogo-collapse .panel-title i.fa-chevron-down {
    color: var(--fg-muted);
}

/* "Filter by Conflict Type" / "Filter by Status" <select> elements: same
   custom chevron treatment as the query-builder's selects, so every select
   in the app draws the same chevron instead of the browser default. */
.ui-refresh .session-conflicts #conflict-type-filter,
.ui-refresh .session-conflicts #status-filter {
    background-size: .6em !important;
    background-image: var(--select-chevron-image);
    background-position: calc(100% - 1.3em) center !important;
    background-repeat: no-repeat;
    appearance: none;
    padding-right: 30px;
}

/* ajax data-tables */

.ui-refresh .new-grid-theme .table-condensed thead tr.header-row th,
.ui-refresh .new-grid-theme .table-condensed > tbody tr td:not(.data-type-full-name, .data-type-email),
.ui-refresh .new-grid-theme .table-condensed > tbody tr td a,
.ui-refresh .new-grid-theme .dt-length > select {
    color: var(--fg-default) !important;
}

.ui-refresh .new-grid-theme .table-condensed > thead > tr:first-of-type > th,
.ui-refresh .new-grid-theme .table-condensed > thead > tr > th,
.ui-refresh .new-grid-theme .table-condensed > tbody tr td {
    border-color: var(--border-subtle) !important;
}
/* ===== Download list (grouped-batch downloads, e.g. badge pre-print) ===== */

.ui-refresh .download-list {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: var(--card);
}

.ui-refresh .download-row {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--line);
}

.ui-refresh .download-row:last-child {
    border-bottom: none;
}

.ui-refresh .download-row__icon {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    flex-shrink: 0;
    border-radius: var(--radius-md);
    background: var(--primary-tint);
    color: var(--primary-ink);
}

.ui-refresh .download-row__body {
    flex: 1;
    min-width: 0;
}

.ui-refresh .download-row__title {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    color: var(--ink);
}

.ui-refresh .download-row__sub {
    margin-top: 1px;
    font-size: var(--font-size-xs);
    color: var(--ink-2);
}

/* Static badge-name cell (account-level badges viewed read-only from an
   event) — mirrors .lt-input's box model so the text lines up with the
   editable rows above/below it, minus the interactive affordances. */
.ui-refresh .list-table .lt-input-name-static {
    display: inline-block;
    /* An inline-block with overflow other than "visible" takes its baseline
       from its bottom margin edge instead of its text baseline (CSS 2.1
       §10.8.1) — vs. .lt-input's <input>, whose baseline sits mid-box like
       normal text. Left at the default "baseline", that mismatch inflates
       the anonymous line box the TD generates around it, making rows with
       an account-level (read-only) badge ~5.5px taller than rows with an
       editable name input. vertical-align: middle sidesteps the mismatch
       entirely instead of trying to compensate for it. */
    vertical-align: middle;
    width: 100%;
    padding: var(--space-1) var(--space-2);
    margin-left: calc(-1 * var(--space-2));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--ink-2);
}

/* ===== Event integration settings ==========================================
   Shared narrow reading width and vertical rhythm for simple event-level
   integration forms. Individual integrations supply their own content while
   reusing the list intro, flat account sections, and ToggleRow treatment.
   (SAML pages opted out of the 900px cap and use the standard .list-page
   width instead — see the Event SAML settings section below.) */
.ui-refresh .list-page.integration-settings-page {
    width: min(900px, 100% - (var(--doc-gutter) * 2));
    max-width: 900px;
    padding-bottom: var(--space-12);
}

.ui-refresh #main-content-inner:has(.integration-settings-page) .subpage-head,
.ui-refresh #main-content:has(.integration-settings-page) .subpage-head {
    width: min(900px, 100% - (var(--doc-gutter) * 2));
    max-width: 900px;
}

.ui-refresh .integration-settings-page .list-intro,
.ui-refresh .saml-page .list-intro {
    margin-bottom: var(--space-8);
}

.ui-refresh .integration-settings-page .integration-enable-toggle {
    margin-top: 0;
}

/* Navan's General Settings are a flat section on the page surface, so its
   controls should read as border-only rather than filled cards. Include the
   read-only secret key and its copy addon to keep the input group seamless. */
.ui-refresh .integration-settings-page #tripactions-settings .form-control,
.ui-refresh .integration-settings-page #tripactions-settings .input-group-addon {
    background-color: transparent !important;
}

.ui-refresh .integration-settings-page .is-hidden {
    display: none;
}

@media (max-width: 767px) {
    .ui-refresh .list-page.integration-settings-page {
        padding-bottom: var(--space-8);
    }

    .ui-refresh .integration-settings-page .list-intro {
        margin-bottom: var(--space-6);
    }
}

/* ===== Event SAML settings =================================================
   Uses the standard .list-page width (--doc-max-width). Revealed settings
   flow directly on the paper surface, separated by whitespace and quiet
   rules. */
.ui-refresh .saml-page .is-hidden,
.ui-refresh .saml-account-form .is-hidden {
    display: none;
}

.ui-refresh .file-container .file-input.is-hidden {
    display: none;
}

/* Whitespace-only section separation — no divider. .panel--plain already
   carries margin-bottom: var(--space-8) (32px); that's the single source of
   the content→next-header gap. padding-bottom: 0 keeps a stray padding
   declaration from ever stacking on top of that margin (padding doesn't
   collapse the way adjacent margins do, so 32px padding + 32px margin would
   silently double the gap). */
.ui-refresh .saml-page .saml-section {
    padding-bottom: 0;
}

/* Force the trailing gap to exactly 32px regardless of what ends the
   section — margin collapse only gives you max(a, b), so anything here
   with its own margin > 32px would blow the gap out. Zero it and let the
   panel's 32px own the boundary. */
.ui-refresh .saml-page .saml-section .toggle-group:last-child {
    margin-bottom: 0;
}

.ui-refresh .saml-page #saml-settings > .help-block {
    margin-bottom: 0;
}

/* .panel-body carries Bootstrap's clearfix (::before { content:" ";
   display:table }), which is itself a generated first-child box. That
   blocks the *real* first child's own top margin from collapsing through
   to merge with the panel-heading's margin-bottom above it, so a toggle
   card opening a section (Remember me, Invitation link) got heading mb
   (20px) *plus* its own untouched mt (20px) — 40px, not 20. Zero it here
   so the heading's margin is the only thing setting that gap. (General
   setup's own toggle skips this because .saml-enable-toggle already zeroes
   its margin-top, and it isn't :first-child there — a paragraph is.) */
.ui-refresh .saml-page .panel-body > .toggle-group:first-child {
    margin-top: 0;
}

/* IdP parameters' intro text is a plain .help-block with no engineered
   margin, immediately followed by the empty-state/table — two real zero
   margins collapsing to zero. Give it the same descriptive-text-to-control
   gap .saml-section-description uses for General setup's intro. */
.ui-refresh #saml-idp-params-panel .help-block {
    margin-bottom: var(--space-4);
}

.ui-refresh .saml-page .saml-section-description {
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-lg);
    margin: 0 0 var(--space-4);
}

.ui-refresh .saml-page .saml-enable-toggle,
.ui-refresh .saml-account-form .saml-enable-toggle {
    margin-top: 0;
}

.ui-refresh .remember-me-settings .input-group-btn:last-child > .btn-group.open {
    z-index: 3;
}

/* Flat reveal — no indent rail under the Enable toggle. margin-top pairs
   with the enable card's 20px bottom margin (adjacent siblings — they
   collapse to the larger 32px), keeping the toggle→heading gap on the
   same 32px rhythm as every other section boundary. */
.ui-refresh .saml-page #saml-settings {
    border-left: 0;
    margin-top: var(--space-8);
    margin-bottom: 0;
    margin-left: 0;
    padding: 0;
}

.ui-refresh .saml-page .saml-subsection-heading {
    color: var(--fg-default);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-lg);
    margin: var(--space-8) 0 var(--space-5);
}

/* First heading inside the reveal sits at the top — #saml-settings'
   own margin-top already provides the 32px. */
.ui-refresh .saml-page #saml-settings > .saml-subsection-heading:first-child {
    margin-top: 0;
}

.ui-refresh .saml-page .saml-event-config > .row,
.ui-refresh .saml-page .saml-event-config > .form-group {
    margin-bottom: var(--space-5);
}

.ui-refresh .saml-page .saml-event-config > .row {
    margin-left: 0;
    margin-right: 0;
}

.ui-refresh .saml-page .saml-event-config > .row > [class*="col-"],
.ui-refresh .saml-page .saml-event-config .form-group > .col-sm-10 {
    float: none;
    padding-left: 0;
    padding-right: 0;
    width: 100%;
}

.ui-refresh .saml-page .saml-event-config .form-group > .control-label.col-sm-2 {
    float: none;
    padding: 0;
    text-align: left;
    width: 100%;
}

.ui-refresh .saml-page .saml-event-config .file-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.ui-refresh .saml-page .saml-event-config .file-actions > .btn {
    margin: 0;
}

.ui-refresh .saml-page .responsive-grid,
.ui-refresh .saml-account-form .responsive-grid {
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
}

/* Event SAML in-form tables (IdP parameters, field mapping): pin the trailing
   delete column narrow so the content columns split the rest. */
.ui-refresh .saml-params-table .lt-th-actions,
.ui-refresh .saml-mapping-table .lt-th-actions {
    width: 48px;
}

.ui-refresh .saml-page .saml-debug-note {
    margin-top: var(--space-3);
}

/* color.css provides legacy-safe defaults for these SSO Log status icons.
   Override them here with the ui-refresh semantic tokens when this bundle is
   loaded. The header class is layout-only and remains harmless in legacy. */
.saml-status-icon--success {
    color: var(--color-success);
}

.saml-status-icon--error {
    color: var(--color-error);
}

.saml-debug-actions {
    max-width: 52px;
}

/* Debug Log list-table (saml-setup + saml-config forms): the two date-range
   filter inputs share one cell, so they can't take .lt-filter-input's
   width:100% — split the cell instead. Same specificity as the generic rule
   (components.css ~16162); this wins by source order. */
.ui-refresh .saml-debug-log-table .lt-filter-date {
    width: 48%;
    display: inline-block;
}

/* _saml_view.php renders this class on both the legacy Bootstrap modal and,
   in ui-refresh, inside the confirm-dialog--form template below — unscoped so
   the legacy modal keeps its zero-padding/border <pre> instead of falling
   back to Bootstrap's default boxed code block. */
.saml-metadata-code {
    border: 0;
    padding: 0;
}
/* ui-refresh's "View" dialog (saml-metadata-dialog.js) has no surrounding
   .modal-body/.response-kv card, so give the <pre> itself the same bordered,
   padded treatment as the Test Response dialog's JSON dump
   (.response-kv .table pre above) — otherwise the XML sits flush against the
   dialog edges. */
.ui-refresh .confirm-dialog--form .saml-metadata-code {
    display: block;
    margin: 0;
    padding: var(--space-3);
    background: var(--bg-page);
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    font-size: var(--font-size-xs);
    max-height: 50vh;
    overflow: auto;
}
/* highlight.js applies its "tomorrow" theme straight to the <code> element,
   which paints its own hardcoded white background + dark text over the <pre>
   above — invisible in light mode but a jarring white box on dark surfaces.
   Re-point every rule at the flipping --hljs-* tokens (tokens.css); these
   selectors mirror tomorrow.css's own groupings 1:1, just scoped to this
   dialog and higher-specificity so they win regardless of stylesheet order. */
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs {
    background: var(--hljs-bg);
    color: var(--hljs-fg);
    padding: 0;
}
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-comment,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-quote {
    color: var(--hljs-comment);
}
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-variable,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-template-variable,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-tag,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-name,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-selector-id,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-selector-class,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-regexp,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-deletion {
    color: var(--hljs-tag);
}
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-number,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-built_in,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-builtin-name,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-literal,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-type,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-params,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-meta,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-link {
    color: var(--hljs-number);
}
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-attribute {
    color: var(--hljs-attribute);
}
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-string,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-symbol,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-bullet,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-addition {
    color: var(--hljs-string);
}
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-title,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-section {
    color: var(--hljs-title);
}
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-keyword,
.ui-refresh .confirm-dialog--form .saml-metadata-code .hljs-selector-tag {
    color: var(--hljs-keyword);
}
/* The IDP Metadata dialog's XML has long unbroken base64 certificate lines —
   the default confirm-dialog--form width (480px) wraps them so aggressively
   the content is barely readable. Widen just this variant. */
.ui-refresh .confirm-dialog--wide {
    width: 760px;
    max-width: 94vw;
}

/* Account SAML follows the same centered, section-based form structure as
   Basic Details and Policies. The legacy horizontal-grid partials are shared
   with Event SAML, so normalize only their account-page rendering here.
   (.is-hidden / .saml-enable-toggle / .responsive-grid are shared with the
   saml-page rules above.) */
.ui-refresh .saml-account-form .saml-account-settings {
    margin-top: var(--space-6);
}

.ui-refresh .saml-account-form .saml-subsection-heading {
    color: var(--fg-default);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-sm);
    margin: 0 0 var(--space-4);
}

.ui-refresh .saml-account-form .saml-scim-heading {
    align-items: center;
    display: flex;
    justify-content: space-between;
}

.ui-refresh .saml-account-form .saml-child-config > .row {
    margin-left: 0;
    margin-right: 0;
    /* Matches the .account-section field rhythm (--space-4) — a bare .row has
       no margin of its own, so without this the IDP Metadata upload row sits
       right up against the Login Entry Point field below it. */
    margin-bottom: var(--space-4);
}

.ui-refresh .saml-account-form .saml-child-config > .row > [class*="col-"],
.ui-refresh .saml-account-form .saml-parent-config .panel-body > .row > [class*="col-"] {
    float: none;
    padding-left: 0;
    padding-right: 0;
    width: 100%;
}

.ui-refresh .saml-account-form .saml-upload-field .file-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.ui-refresh .saml-account-form .saml-upload-field .file-actions > .btn,
.ui-refresh .saml-account-form .saml-download-field .button-download {
    margin: 0;
}

.ui-refresh .saml-account-form .saml-download-field .form-label,
.ui-refresh .saml-account-form .saml-upload-field .form-label {
    display: block;
    margin-bottom: var(--space-1);
}

/* Encryption Certificate + Metadata downloads, consolidated under a single
   "Service Provider Files" label instead of two stacked fields that each
   just say "Download". */
.ui-refresh .saml-account-form .saml-sp-files .form-label,
.ui-refresh .saml-page .saml-sp-files .form-label {
    display: block;
    margin-bottom: var(--space-1);
}
.ui-refresh .saml-account-form .saml-sp-files-actions,
.ui-refresh .saml-page .saml-sp-files-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}
.ui-refresh .saml-account-form .saml-sp-files-actions > .btn,
.ui-refresh .saml-page .saml-sp-files-actions > .btn {
    margin: 0;
}

/* Native file input for IDP Metadata upload — style the OS-default "Choose
   File" button to match the app's button chrome instead of rendering unstyled.
   The input inherits .form-control's fixed --input-height; the button's own
   padding+border no longer fits inside that height, so it clips and the
   "No file chosen" text sits off-center. Switch to height:auto (floored at
   the usual input height) and center the button + text as flex items instead
   of relying on the fixed line box. */
.ui-refresh .saml-account-form .file-input input[type="file"],
.ui-refresh .saml-page .file-input input[type="file"] {
    display: flex;
    align-items: center;
    height: auto;
    min-height: var(--input-height);
    width: 100%;
    gap: var(--space-3);
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
}
.ui-refresh .saml-account-form .file-input input[type="file"]::file-selector-button,
.ui-refresh .saml-page .file-input input[type="file"]::file-selector-button {
    padding: var(--space-1) var(--space-4);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
    color: var(--fg-default);
    font-weight: var(--font-weight-medium);
    cursor: pointer;
    transition: border-color var(--transition-fast), background var(--transition-fast);
}
.ui-refresh .saml-account-form .file-input input[type="file"]:hover::file-selector-button,
.ui-refresh .saml-page .file-input input[type="file"]:hover::file-selector-button {
    border-color: var(--accent-primary);
    background: var(--bg-muted);
}

/* Breathing room below the last section (Test SAML Configuration/SCIM) so it
   doesn't sit flush against the bottom of the scroll container. */
.ui-refresh .saml-account-form {
    padding-bottom: var(--space-8);
}

/* Copy-to-clipboard icon (Login Entry Point, ACS URL, Entity ID) — briefly
   flips to a green checkmark instead of firing a toast, matching the
   feedback pattern on the API credentials page. _saml_field.php renders this
   unconditionally on both the legacy and ui-refresh pages, so it's unscoped
   here too — otherwise the legacy page gets the icon-flip behavior with no
   color feedback at all. */
.saml-copy-icon.copied {
    color: var(--ok);
}

/* Payment-gateway card-type rows: align the brand mark with its label text. */
.ui-refresh .card-type-row .toggle-label img {
    vertical-align: middle;
    margin-right: var(--space-2);
    border-radius: var(--radius-sm);
}

/* Integration field mappings — inline "map another field" control
   (dropdown + button), replacing the legacy Bootstrap modal. */
.ui-refresh .hubspot-add-mapping-row,
.ui-refresh .validar-add-mapping-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-bottom: var(--space-6);
}
.ui-refresh .hubspot-add-mapping-row select.form-control,
.ui-refresh .validar-add-mapping-row select.form-control,
.ui-refresh .hubspot-mapping-table select.form-control,
.ui-refresh .validar-mapping-table select.form-control,
.ui-refresh .validar-event-mapping-table select.form-control {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background-image: var(--select-chevron-image);
    background-size: 0.6em;
    background-position: calc(100% - var(--space-3)) center;
    background-repeat: no-repeat;
    padding-right: var(--space-6);
}
.ui-refresh .hubspot-add-mapping-row select.form-control,
.ui-refresh .validar-add-mapping-row select.form-control {
    max-width: 320px;
}
/* .btn's default height (--btn-height, 36px) is shorter than .form-control's
   (--input-height, 40px), so "Map another field" sits 4px shorter than the
   select next to it — match it to the select for a level pair. */
.ui-refresh .hubspot-add-mapping-row .btn,
.ui-refresh .validar-add-mapping-row .btn {
    height: var(--input-height);
}
/* #hubspot-mapping-wrapper is the last thing on the page — without this, the
   floating "Back to top" pill (body.ui-refresh .js-scrollbar above) sits over
   the delete button on the table's last row once scrolled to the bottom. */
#hubspot-mapping-wrapper,
#validar-mapping-wrapper {
    padding-bottom: calc(var(--space-12) + var(--space-6));
}

/* ===== HubSpot event wizard — flat sections following the step bar =====
   Wizard step pages render .account-section blocks as siblings of the
   .wsc-header inside .list-page; give every section after the step bar the
   standard --space-8 section gap (the base rule only covers
   .account-section + .account-section, so the first section — or one
   following a flash Alert — would otherwise sit flush under the bar). */
.ui-refresh .list-page .wsc-header ~ .account-section {
    margin-top: var(--space-8);
}
/* The sync-log section renders inside the #hubspot-me-synch Pjax wrapper,
   so the sibling rule above can't reach it. */
.ui-refresh .list-page #hubspot-me-synch .account-section {
    margin-top: var(--space-8);
}

/* ===== HubSpot wizard sync-status rows =====
   Flat replacement for the legacy clearfix/pull layout in
   integration/sync/_sync_status ($flat opt-in). .sync-status-group mirrors
   the .toggle-group card chrome — that class can't be reused here because
   its :not(:has(> .toggle-row)) escape hatch drops the border when a group
   holds no .toggle-row children. */
.ui-refresh .sync-status-group {
    background: var(--bg-surface);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    margin: var(--space-5) 0;
    padding: 0 var(--space-4);
}
.ui-refresh .sync-status-row {
    padding: 0;
}
/* Bootstrap animates the collapse element's height only. Keeping padding or a
   border on that element leaves a non-zero box at height: 0, which then snaps
   away when Bootstrap applies display: none. Put all row chrome inside the
   overflow-clipped animated box so opening and closing reach zero smoothly. */
.ui-refresh .sync-status-row__inner {
    padding: var(--space-4) 0;
}
.ui-refresh .sync-status-group > .sync-status-row + .sync-status-row > .sync-status-row__inner,
.ui-refresh .toggle-group > .toggle-row + .sync-status-row > .sync-status-row__inner {
    border-top: 1px solid var(--panel-border);
}
/* …and the reverse adjacency: EventMobi's step 2 stacks five (toggle +
   sync-status) pairs in one .toggle-group, so the next toggle-row follows a
   sync-status-row. Without this the five sync types run together with no
   separator between them. */
.ui-refresh .toggle-group > .sync-status-row + .toggle-row {
    border-top: 1px solid var(--panel-border);
}
.ui-refresh .sync-status-line {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
}
/* Groups "View sync queue" with any extra per-type action (e.g. SpotMe's
   retrieve-data buttons) passed via _sync_status.php's `actions` slot, so
   `.sync-status-line`'s justify-content: space-between treats them as one
   unit instead of spreading each button to its own edge. */
.ui-refresh .sync-status-line .sync-status-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}
.ui-refresh .sync-status-row .progress {
    margin: var(--space-3) 0 0;
}

/* ===== EventMobi event wizard =====
   Native <select> chevron for the People/Sponsor field mapping tables — there
   is no global `.ui-refresh select.form-control` chevron rule; every refreshed
   table that hosts a native select adds its own (see .hubspot-mapping-table
   above, .session-mass-update-table). */
.ui-refresh .eventmobi-mapping-table select.form-control {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background-image: var(--select-chevron-image);
    background-size: 0.6em;
    background-position: calc(100% - var(--space-3)) center;
    background-repeat: no-repeat;
    padding-right: var(--space-6);
}

/* ===== Expanded settings inside a toggle-group row =====
   The toggle-group card already frames the group, so the legacy left-rule
   indent (#content-container .panel-expanded-settings above) reads as
   noise inside it — full width, no rule. Also covers the deal-pipeline
   .deals panel and the new condition-input reveals.
   Zero ALL margin/padding here, not just horizontal — the base rule's
   vertical padding/margin sits directly on the .collapse element, which
   Bootstrap only animates for height. Leftover vertical padding stays full
   size for the whole animation and then snaps to zero in one frame when
   display:none lands, reading as a jump on open/close (matches the
   .webhook-form override of this same rule below). Space is handed to the
   first/last visible child instead, which lives inside the collapse's
   overflow:hidden box and shrinks with it. */
.ui-refresh #content-container .toggle-group .toggle-row > .panel-expanded-settings {
    border-left: none;
    margin: 0;
    padding: 0;
}
.ui-refresh #content-container .toggle-group .toggle-row > .panel-expanded-settings > :first-child {
    margin-top: var(--space-3);
}
.ui-refresh #content-container .toggle-group .toggle-row > .panel-expanded-settings > :last-child {
    margin-bottom: var(--space-3);
}
/* Breathing room between the unmapped-stages warning and the Deal Pipeline
   field below it. */
.ui-refresh .deals .inline-alert {
    margin-bottom: var(--space-5);
}

/* ===== Wizard pages: bottom breathing room =====
   Without this the last section sits flush against the viewport bottom and
   the floating back-to-top pill overlaps it. Keyed on the step bar so only
   wizard pages get it. */
.ui-refresh .list-page:has(.wsc-header) {
    padding-bottom: var(--space-12);
}

/* ===== Log detail content dialog (hubspot-marketing-event/_view-log) =====
   Flat key/value rows loaded into UiConfirmDialog.openContent()'s
   .confirm-dialog-body, replacing the legacy panel + col-lg-2/10 table +
   iframe modal. No padding of its own — the dialog shell already supplies
   the outer gutter (padding: var(--space-6) on .confirm-dialog--form). */
.ui-refresh .log-detail {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}
.ui-refresh .log-detail-meta {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
}
.ui-refresh .log-detail-row {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}
.ui-refresh .log-detail-label {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    color: var(--fg-muted);
}
.ui-refresh .log-detail-value {
    font-size: var(--font-size-sm);
    color: var(--fg-default);
    word-break: break-word;
}
.ui-refresh .log-detail-json summary {
    cursor: pointer;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--accent-primary);
}
.ui-refresh .log-detail-json pre {
    margin: var(--space-2) 0 0;
    padding: var(--space-3);
    background: var(--bg-muted);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    font-family: var(--font-mono);
    font-size: 12px;
    line-height: 1.5;
    color: var(--fg-default);
    white-space: pre-wrap;
    word-break: break-word;
    max-height: 300px;
    overflow: auto;
}

/* ===== Email preview content dialog (email/preview, ui-refresh branch) =====
   Loaded into UiConfirmDialog.openContent()'s .confirm-dialog-body from the
   "Send Email To Multiple People" recipient list. Key/value meta rows above a
   framed body preview. No outer padding of its own — the dialog's
   .confirm-dialog--content header/body/footer rules supply it.

   The framed preview is a cross-origin iframe (frontend/preview/email), so it
   cannot be auto-sized to its own content — it always scrolls internally.
   Rather than let the dialog scroll too (a scrollbar on the dialog AND one
   inside the iframe), the :has() rules below give the dialog a fixed height
   only when this fragment is present, so the iframe can flex to fill it and
   be the page's only scroller. The blocked-visibility notice (InlineAlert,
   no .email-preview-detail) and the loading/error states keep the dialog
   sized to their (short) content. */
.ui-refresh .confirm-dialog--content:has(.email-preview-detail) {
    height: min(85vh, 920px);
}
.ui-refresh .confirm-dialog--content:has(.email-preview-detail) .confirm-dialog-body {
    display: flex;
    flex-direction: column;
}
.ui-refresh .email-preview-detail {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    flex: 1 1 auto;
    min-height: 0;
}
.ui-refresh .email-preview-detail-meta {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    column-gap: var(--space-4);
    row-gap: var(--space-2);
    margin: 0;
}
.ui-refresh .email-preview-detail-meta dt,
.ui-refresh .email-preview-detail-label {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    color: var(--fg-muted);
}
.ui-refresh .email-preview-detail-meta dt {
    padding-top: 2px;
}
.ui-refresh .email-preview-detail-meta dd {
    margin: 0;
    font-size: var(--font-size-sm);
    color: var(--fg-default);
    word-break: break-word;
}
.ui-refresh .email-preview-detail-label {
    margin-bottom: var(--space-2);
}
.ui-refresh .email-preview-detail-body {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}
.ui-refresh .email-preview-detail-body iframe {
    display: block;
    width: 100%;
    flex: 1 1 auto;
    /* Floor for very short viewports, where min(85vh, 920px) isn't much
       taller than the meta rows above — the body then scrolls as a
       fallback rather than squeezing the iframe past readability. */
    min-height: 240px;
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
}

/* Salesforce v2 Contact/Lead Field Mapping — same inline "map another field"
   control as HubSpot's, replacing the legacy Bootstrap modal. Unlike
   #hubspot-mapping-wrapper this section isn't the last thing on the page
   (Campaign Field Mapping follows it), so no extra bottom padding is needed. */
.ui-refresh .salesforce-add-mapping-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-bottom: var(--space-6);
}
/* "This connection has access to" permissions table (_form.php) — the
   "Refresh Salesforce Permissions" button follows immediately with no
   wrapper, so it needs its own gap from the table above it. */
.ui-refresh .list-table.salesforce-permissions-table {
    margin-bottom: var(--space-6);
}
.ui-refresh .salesforce-add-mapping-row select.form-control,
.ui-refresh .salesforce-mapping-table select.form-control,
.ui-refresh .salesforce-campaign-mapping-table select.form-control,
.ui-refresh .salesforce-opportunity-mapping-table select.form-control,
.ui-refresh .salesforce-opportunity-stage-table select.form-control,
.ui-refresh .salesforce-session-campaign-table select.form-control,
.ui-refresh .salesforce-campaign-status-table select.form-control,
.ui-refresh .salesforce-campaign-member-table select.form-control {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background-image: var(--select-chevron-image);
    background-size: 0.6em;
    background-position: calc(100% - var(--space-3)) center;
    background-repeat: no-repeat;
    padding-right: var(--space-6);
}
/* The opportunity/contact-lead mapping table has no bottom margin of its own
   (list-tables never do), so the "Specify which Campaign..." paragraph right
   after it reads as glued to the table. */
.ui-refresh .salesforce-opportunity-mapping-table + p {
    margin-top: var(--space-4);
}
/* Gap between the campaign-mapping grid and the centered New Campaign Mapping button */
.ui-refresh .salesforce-campaign-mapping {
    margin-top: var(--space-6);
}
.ui-refresh .salesforce-add-mapping-row select.form-control {
    max-width: 320px;
}
.ui-refresh .salesforce-add-mapping-row .btn {
    height: var(--input-height);
}

/* seating chart updates */
.ui-refresh #content-container .seating-chart-index #my-charts .panel {
    padding: var(--space-0) var(--space-5);
    border-radius: var(--radius-lg) !important;
}
/* list updates */
.ui-refresh .list-group-item {
    background-color: var(--bg-surface);
    border-color: var(--panel-border);
    color: var(--fg-default);
}
.ui-refresh .list-group-item:hover {
    background-color: var(--panel-bg-flat);
}

/* OAuth integration choice screen (/loggedin/integration/oauth-choice) —
   two equal-width clickable cards instead of a stacked Bootstrap list-group. */
.ui-refresh .oauth-choice-grid {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-6);
}
.ui-refresh .oauth-choice-card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
    flex: 1 1 260px;
    text-decoration: none;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.ui-refresh .oauth-choice-card:hover,
.ui-refresh .oauth-choice-card:focus {
    border-color: var(--accent-primary);
    box-shadow: var(--sh-sm);
    text-decoration: none;
}
.ui-refresh .oauth-choice-title {
    margin: 0;
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
}
.ui-refresh .oauth-choice-text {
    margin: 0;
    font-size: var(--font-size-sm);
    color: var(--fg-muted);
}

/* ===== Chat themes (/loggedin/chat-theme) ===== */
.ui-refresh .chat-theme.is-hidden {
    display: none;
}
/* Color column swatch (_grid.php's $colorChip). The base .prop .sw sizing
   rule lives in sessions.css, shipped only by SessionSetupChromeAsset —
   scoped here to .chat-theme-table specifically rather than pulling in that
   whole (session/track/location-specific) stylesheet for one 14px box. */
.ui-refresh .chat-theme-table .prop .sw {
    display: inline-block;
    width: 14px;
    height: 14px;
    border-radius: var(--radius-sm);
    vertical-align: middle;
    flex-shrink: 0;
    border: 1px solid rgba(0, 0, 0, 0.12);
}
/* Same asset-scoping gap as the swatch above: .sr-props/.prop's table-cell
   sizing (shrink-to-fit wrapper + a capped width with ellipsis truncation
   instead of wrapping) also lives in sessions.css. Without it, a chip whose
   label runs to two words (e.g. "2 places") wraps onto a second line instead
   of staying on one, growing the row and breaking the chip's shape. */
.ui-refresh .chat-theme-table .sr-props {
    display: inline-flex;
}
.ui-refresh .chat-theme-table .prop {
    max-width: 120px;
}
.ui-refresh .chat-theme-table .prop .prop-value {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Disabled delete (in-use theme) reads too close to enabled on hover — the
   shared .lr-icon.is-disabled rule only recolors to --muted, no opacity.
   Scoped to this table rather than the shared rule (components.css's other
   .lt-cell-actions .lr-icon.is-disabled consumers — Tracks, Locations,
   Hotel, Chat Rooms, etc. — keep their current look).
   Must stay keyed off the same tr:hover/:has(:focus-visible) trigger as the
   base .lr-icon reveal rule (components.css:15707-15725) — an unconditional
   opacity override here has higher specificity than that rule's opacity:0
   at-rest state, so it would win even when the row isn't hovered, leaving
   the disabled delete icon permanently visible instead of hidden until
   hover like every other action icon. */
.ui-refresh .chat-theme-table tbody tr:hover .lt-cell-actions .lr-icon.is-disabled,
.ui-refresh .chat-theme-table tbody tr:has(:focus-visible) .lt-cell-actions .lr-icon.is-disabled {
    opacity: 0.4;
}
/* The Color column is a plain display, not a control — unlike the Used
   column's chip (which stays interactive via its popover), drop the shared
   .prop chrome (background/border) so it doesn't look clickable.
   Must match the base rule's full ancestor chain (components.css:14794
   `.list-table .lt-cell-prop .sr-props .prop`, and :14889's `.is-static:hover`)
   class-for-class — a shorter selector loses the specificity contest and the
   border silently stays, which is exactly what happened the first time this
   was scoped to just `.chat-theme-table .prop.chat-theme-color-chip`.

   Padding also needs adjusting, for the same underlying reason sessions.css's
   "filled state: plain text, no badge chrome" rule does the same (:1022,
   shipped only by SessionSetupChromeAsset — same asset-scoping gap as the
   swatch/wrap fixes above): the base .prop rule's `padding: 6px 10px` was
   tuned for a chip with a visible border acting as its own frame. Once the
   border/background go transparent there's nothing to visually contain that
   padding, so the swatch+label sat noticeably right of the column header.
   sessions.css cancels its left padding with an equal negative margin instead
   of zeroing it outright, because its chip keeps a hover background (the
   margin preserves that box's full hit/hover area while zeroing the *visual*
   inset) — this chip has no hover state at all (see the :hover selector
   above, also flattened), so there's nothing to preserve; zeroing
   padding-left directly is the simpler equivalent here. */
.ui-refresh .chat-theme-table .lt-cell-prop .sr-props .prop.is-static.chat-theme-color-chip,
.ui-refresh .chat-theme-table .lt-cell-prop .sr-props .prop.is-static.chat-theme-color-chip:hover {
    background: transparent;
    border-color: transparent;
    padding-left: 0;
}
/* Version History (chat-theme-audit). old_value can hold a long unbroken
   string (e.g. the full settings_json blob for that field), which would
   otherwise overflow the cell instead of wrapping onto a new line. */
.ui-refresh .chat-theme-audit-table .lt-cell-old-value {
    max-width: 360px;
    word-break: break-word;
    overflow-wrap: anywhere;
}
/* Paired compact fields (Single Color/Font Color, Chat Window Settings
   colors, Top/Bottom Bar, Font Color+Style+Size). minmax() floor instead of
   `1fr` so a hidden sibling (e.g. "Same Color" toggled) can't shrink a
   still-visible column too — with plain `1fr` on a width:fit-content
   container, all columns share the widest one's max-content, so one
   collapsing to 0 shrinks the row. 238px matches the ColorInput field. */
.ui-refresh .chat-theme-color-row {
    width: fit-content;
    grid-template-columns: repeat(2, minmax(238px, max-content));
}
/* Override the shared "adjacent form-rows cluster tight" rule — these rows
   are usually separate groups (e.g. colors, then font), not a date/time
   pair. Same specificity; wins on source order. */
.ui-refresh .chat-theme-color-row:has(+ .form-row) {
    margin-bottom: var(--space-5);
}
/* 3-column variant (Font Color+Style+Size). Exact-count match so it doesn't
   also catch the 4-child Top/Bottom Bar row, which uses the 2-column
   template above with auto-placement into 2 rows. */
.ui-refresh .chat-theme-color-row:has(> :nth-child(3)):not(:has(> :nth-child(4))) {
    grid-template-columns: repeat(3, minmax(238px, max-content));
}
/* Mobile: the fixed minmax() floor doesn't shrink like `1fr`, so stack to
   single column instead of overflowing. */
@media only screen and (max-width: 767px) {
    .ui-refresh .chat-theme-color-row,
    .ui-refresh .chat-theme-color-row:has(> :nth-child(3)):not(:has(> :nth-child(4))) {
        width: auto;
        grid-template-columns: 1fr;
        gap: var(--space-3);
    }
}
/* Bootstrap's .col-sm-12 carries float:left + a 15px gutter; the shared
   .control-label rule only resets width. Left floating, the Font Style
   label lets the segmented control flow up beside it instead of wrapping
   below, and it misaligns with sibling labels. Scoped to this row. */
.ui-refresh .chat-theme-color-row .control-label.col-sm-12 {
    float: none;
    padding-left: 0;
    padding-right: 0;
}
/* B/I/U keeps the tinted-accent active look used elsewhere instead of
   .segmented-control's dark fill (correct for mutually-exclusive choices,
   not for 3 independent toggles). Extra class wins over the shared rule. */
.ui-refresh .btn-group.segmented-control.chat-theme-font-style-toggle .btn.active,
.ui-refresh .btn-group.segmented-control.chat-theme-font-style-toggle .btn-default.active,
.ui-refresh .btn-group.segmented-control.chat-theme-font-style-toggle .btn-default.active:hover,
.ui-refresh .btn-group.segmented-control.chat-theme-font-style-toggle .btn-default.active:focus {
    background: var(--accent-subtle) !important;
    color: var(--accent-on-tint) !important;
    border-color: var(--accent-primary) !important;
}
/* Standalone toggles (Display Avatar, Group/Direct Chat Underline) aren't a
   grouped list like Chat Settings — restore normal spacing over the shared
   grouped-list rule. */
.ui-refresh .chat-theme-advanced-settings .panel-body > .form-group:has(> .checkbox) {
    margin-top: var(--space-5);
    margin-bottom: var(--space-5);
}
/* Panels that show together as one tab's group, not separate topics, want a
   tighter gap than the general rule below: Window Settings + Bars (share
   .advanced-settings_window), and Display Options -> Message Bubble
   (share .advanced-settings_message; the :not()s exclude Name and
   Timestamp -> Display Options, which stays at the general gap). */
.ui-refresh .chat-theme-advanced-settings .advanced-settings_window.panel--plain.in + .advanced-settings_bars.panel--plain.in,
.ui-refresh .chat-theme-advanced-settings .advanced-settings_message_display.panel--plain.in + .advanced-settings_message.panel--plain.in:not(.advanced-settings_message_display):not(.advanced-settings_msg_timestamp) {
    margin-top: var(--space-4);
}
/* Restore trailing margin for single-field collapse reveals (Reply Bubble
   Color, Bottom Bar Size/Color, Attendee avatar icon) — the shared
   anti-overshoot rule zeroes a collapse's last child, which here is the
   whole revealed field. */
.ui-refresh .chat-theme-advanced-settings .collapse > :last-child,
.ui-refresh .chat-theme-advanced-settings .collapsing > :last-child {
    margin-bottom: var(--space-5);
}
/* Exception: inside a chat-theme-color-row grid cell, spacing comes from
   the grid's own gap — restoring margin there inflates that cell's height,
   and every cell in the row shares the tallest one's height. */
.ui-refresh .chat-theme-advanced-settings .chat-theme-color-row .collapse > :last-child,
.ui-refresh .chat-theme-advanced-settings .chat-theme-color-row .collapsing > :last-child {
    margin-bottom: 0;
}
/* Consistent gap after Tabs Sections regardless of which tab is active:
   `+` only matches its literal next DOM sibling (Window Settings' panel),
   visible only for that one tab, but margin-collapse itself reaches
   whichever sibling is truly visible next, however far away in the DOM —
   so set it directly on the tabs panel instead. !important + matching
   #content-container.account is needed to beat active-form.css's
   `#content-container.account .panel:not(...) { margin-bottom: 0
   !important }`, which carries ID-level specificity. */
.ui-refresh #content-container.account .chat-theme-advanced-settings .advanced-settings_tabs.panel--plain {
    margin-bottom: 10px !important;
}
/* Update only: extra gap once the Style Theme dropdown becomes visible
   (collapsed on create until the theme is first saved). */
.ui-refresh .chat-theme-style-select.in + .s_chat_color .chat-theme-color-row {
    margin-top: var(--space-5);
}
/* Space before the first advanced-settings panel (Tabs, or the first Cards
   panel), which otherwise sits flush against General Details above it. */
.ui-refresh .chat-theme-advanced-settings {
    margin-top: var(--space-8);
}
/* Tabs Sections panel-body: shared .panel--plain > .panel-body zeroes
   padding, but this panel's only content is the nav-tabs strip. 16px is the
   closest token to the 15px confirmed on the live page. */
.ui-refresh :is(#content-container, #report-content) .chat-theme-advanced-settings .advanced-settings_tabs.panel--plain > .panel-body {
    padding: var(--space-4) 0;
}
/* B/I/U is 3 independent checkboxes reusing .segmented-control's look, not
   a radioButtonGroup — the shared rule only hides the radio input. */
.ui-refresh .chat-theme-font-style-toggle input[type="checkbox"] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
    width: 0;
    height: 0;
    margin: 0;
}
/* ===== CometChat preview isolation (chat-theme + chat-room forms) ===== */

.ui-refresh #cometChat.overlay {
    bottom: 90px !important;
}

.ui-refresh #chatExpand {
    bottom: 20px !important;
}

/* ===== Moneybird integration ===== */
.ui-refresh .moneybird-drawer-actions,
.ui-refresh .moneybird-section-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
}
.ui-refresh .moneybird-drawer-actions {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3);
}
.ui-refresh .moneybird-drawer-actions > .btn {
    justify-content: center;
    width: 100%;
}
.ui-refresh .moneybird-drawer-actions > .btn:first-child {
    grid-column: 1 / -1;
}
/* Drawer panel bodies already provide the standard --space-3 vertical gap.
   Remove component margins here so adjacent fields and controls do not add a
   second gap (notably Client ID / Client Secret and the tax mapping controls). */
.ui-refresh .moneybird-account-form .panel-body > .form-group,
.ui-refresh .moneybird-account-form .panel-body > .moneybird-copy-row,
.ui-refresh .moneybird-account-form .panel-body > .moneybird-drawer-toggle,
.ui-refresh .moneybird-account-form .panel-body > .moneybird-drawer-actions,
.ui-refresh .moneybird-account-form .panel-body > .moneybird-add-mapping-row {
    margin-top: 0;
    margin-bottom: 0;
}
.ui-refresh .moneybird-copy-row .btn {
    height: var(--input-height);
}
.ui-refresh .moneybird-tax-description {
    margin: 0;
}
.ui-refresh .moneybird-tax-refresh {
    display: flex;
}
.ui-refresh .moneybird-drawer-toggle {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}
.ui-refresh .moneybird-drawer-toggle .control-label {
    margin: 0;
}
.ui-refresh .moneybird-add-mapping-row {
    display: flex;
    align-items: flex-end;
    gap: var(--space-3);
}
.ui-refresh .moneybird-add-mapping-row .form-group,
.ui-refresh .moneybird-add-mapping-row .select2-container {
    flex: 1 1 280px;
    margin-bottom: 0;
    padding-bottom: 0;
}
/* active-form.css gives native selects inside .new-form an em-based height.
   Normalize every native control in this shared row to the UI-refresh token,
   just as the DropdownSearch selection below is normalized for tax mappings. */
.ui-refresh .moneybird-add-mapping-row .form-control {
    height: var(--input-height) !important;
    min-height: var(--input-height);
}
.ui-refresh .moneybird-add-mapping-row .btn {
    flex: 0 0 auto;
    height: var(--input-height);
}
/* DropdownSearch puts both classes on the selection itself and declares its
   em-based height as !important, so match that element directly. */
.ui-refresh .moneybird-add-mapping-row .swoogo-dropdown-search.select2-selection--single {
    height: var(--input-height) !important;
    min-height: var(--input-height);
    position: relative;
}
.ui-refresh .moneybird-add-mapping-row .swoogo-dropdown-search .select2-selection__clear {
    display: flex;
    align-items: center;
    justify-content: center;
    top: 50% !important;
    right: var(--space-4) !important;
    width: var(--space-6);
    height: var(--space-6);
    line-height: 1;
    transform: translateY(-50%);
}
.ui-refresh .moneybird-mapping-table select.form-control,
.ui-refresh .moneybird-event-settings .list-table select.form-control {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background-image: var(--select-chevron-image);
    background-size: 0.6em;
    background-position: calc(100% - var(--space-3)) center;
    background-repeat: no-repeat;
    padding-right: var(--space-6);
}
.ui-refresh .moneybird-mapping-empty-row td {
    color: var(--fg-muted);
    padding: var(--space-4);
}
.ui-refresh .moneybird-event-settings .account-section,
.ui-refresh .moneybird-account-picker {
    margin-top: var(--space-6);
}
.ui-refresh .moneybird-event-settings .moneybird-settings-section {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}
.ui-refresh .moneybird-event-settings .moneybird-tax-settings-section {
    gap: var(--space-6);
}
.ui-refresh .moneybird-event-settings .moneybird-settings-section > .section-heading-with-action,
.ui-refresh .moneybird-event-settings .moneybird-settings-section > .moneybird-section-heading,
.ui-refresh .moneybird-event-settings .moneybird-settings-section > .moneybird-method-selection,
.ui-refresh .moneybird-event-settings .moneybird-settings-section > .inline-alert,
.ui-refresh .moneybird-event-settings .moneybird-settings-section > .moneybird-tax-columns,
.ui-refresh .moneybird-event-settings .moneybird-settings-section > .moneybird-add-mapping-row {
    margin-top: 0;
    margin-bottom: 0;
}
.ui-refresh .moneybird-event-settings .section-heading-with-action,
.ui-refresh .moneybird-tax-panel .section-heading-with-action {
    margin-bottom: var(--space-4);
}
.ui-refresh .moneybird-payment-currency-alert {
    margin-bottom: var(--space-4);
}
.ui-refresh .moneybird-event-settings .section-heading-with-action h2,
.ui-refresh .moneybird-event-settings .section-heading-with-action h3,
.ui-refresh .moneybird-event-settings .moneybird-section-heading h2 {
    margin: 0;
}
.ui-refresh .moneybird-event-settings .section-heading-with-action .account-section-description,
.ui-refresh .moneybird-event-settings .moneybird-section-heading .account-section-description {
    margin: var(--space-2) 0 0;
}
.ui-refresh .moneybird-tax-panel .section-heading-with-action p {
    margin-top: 0;
}
.ui-refresh .moneybird-method-selection {
    padding: 0;
    border: 0;
    background: transparent;
}
.ui-refresh .moneybird-method-selection .form-group {
    margin-bottom: 0;
}
.ui-refresh .moneybird-tax-columns {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-4);
    margin: var(--space-5) 0;
}
.ui-refresh .moneybird-tax-summary,
.ui-refresh .moneybird-tax-overrides {
    padding: var(--space-4);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
}
.ui-refresh .moneybird-tax-summary h3,
.ui-refresh .moneybird-tax-overrides h3,
.ui-refresh .moneybird-country-heading h3 {
    margin-top: 0;
}
.ui-refresh .moneybird-contact-options {
    margin: var(--space-2) 0 0;
}
@media (max-width: 767px) {
    .ui-refresh .moneybird-tax-columns {
        grid-template-columns: 1fr;
    }
    .ui-refresh .moneybird-add-mapping-row {
        align-items: stretch;
        flex-direction: column;
    }
    .ui-refresh .moneybird-drawer-actions {
        grid-template-columns: 1fr;
    }
    .ui-refresh .moneybird-drawer-actions > .btn:first-child {
        grid-column: auto;
    }
}

/* Well backgrounds within sidebar form */
.ui-refresh .sidebar-form .well {
    background-color: var(--panel-flat-control-bg);
    border-color: var(--panel-border);
    margin-top: var(--space-3);
}
.ui-refresh .sidebar-form .form-group [role="radiogroup"] .radio:last-child .well {
    margin-bottom: 0;
}

/* dark mode pre */
.ui-refresh[data-surface="dark"] pre {
    background-color: var(--bg-muted);
    border-color: var(--panel-border);
    color: var(--fg-subtle);
}

/* version history table header */
.ui-refresh .ajax-drawer .version-history th {
    font-size: var(--font-size-label);
}
/* ==========================================================================
   Account switcher modal (account/switch, ui-refresh)
   --------------------------------------------------------------------------
   Centered .confirm-dialog--form (same family as the Payment Gateway "Test
   Response" and SAML "View Metadata" dialogs — see payment-gateway-list.js /
   saml-metadata-dialog.js) rather than a slide-in drawer. .acct-switch-head is
   pinned (current account, recently viewed, search box, status line) and
   .acct-switch-results scrolls, so the search field never leaves view while a
   long result list is being walked.

   .acct-switch-modal overrides the shared .confirm-dialog--form base (which
   scrolls header+body+footer together as one box) with its own flex layout so
   only the row list scrolls internally, and re-homes the horizontal inset that
   base rule normally provides via its own padding — see the header/body/head/
   results rules below.
   ========================================================================== */
.ui-refresh .confirm-dialog--form.acct-switch-modal {
    display: flex;
    flex-direction: column;
    width: 680px;
    max-width: 92vw;
    max-height: 82vh;
    padding: 0;
    overflow: hidden;
    text-align: left;
    /* This dialog paints --bg-surface (inherited from .confirm-dialog--form);
       flip the companion token so the search .form-control contrasts against
       it instead of reading as transparent. */
    --control-bg-color: var(--bg-page);
}
.ui-refresh .acct-switch-modal .confirm-dialog-header {
    flex-shrink: 0;
    padding: var(--space-6) var(--space-6) 0;
    margin-bottom: 0;
}
.ui-refresh .acct-switch-modal .confirm-dialog-body {
    flex: 1;
    /* Flex children default to min-height:auto and refuse to shrink below
       their content, which would push the results list past the dialog's
       max-height instead of scrolling inside it. */
    min-height: 0;
    display: flex;
    flex-direction: column;
    padding: 0;
}

/* Positioned wrapper for the two regions below: the "Switching…" overlay
   fills this rather than the whole dialog, so the header (title + close
   button) stays legible while the browser navigates. */
.ui-refresh .acct-switch-modal .acct-switch {
    position: relative;
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
}
.ui-refresh .acct-switch-modal .acct-switch-head {
    flex-shrink: 0;
    padding: var(--space-4) var(--space-6) var(--space-2);
}

/* --- Pinned current account --- */
.ui-refresh .acct-switch-current {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-lg);
    background: var(--bg-subtle);
    margin-bottom: var(--space-3);
}
.ui-refresh .acct-switch-current-body {
    min-width: 0;
    flex: 1;
}
.ui-refresh .acct-switch-current-label {
    display: block;
    font-size: var(--font-size-label);
    font-weight: var(--font-weight-semibold);
    color: var(--accent-on-tint);
}

/* --- Avatar tiles --- */
.ui-refresh .acct-switch-avatar {
    flex: none;
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    border-radius: var(--radius-full);
    background: var(--bg-muted);
    color: var(--fg-default);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
}
/* The current account's tile is the one accent-coloured element in the dialog —
   it marks "you are here" without a second badge. */
.ui-refresh .acct-switch-avatar--current {
    border-radius: var(--radius-md);
    background: var(--accent-primary);
    color: var(--fg-on-accent);
}

/* --- Recently viewed --- */
.ui-refresh .acct-switch-recent {
    padding: 0 0 var(--space-3);
    font-size: var(--font-size-xs);
    line-height: var(--line-height-sm);
    color: var(--fg-muted);
}
.ui-refresh .acct-switch-recent-label {
    color: var(--fg-subtle);
}
.ui-refresh .acct-switch-recent a {
    font-weight: var(--font-weight-medium);
}

/* --- Search box --- */
.ui-refresh .acct-switch-search {
    position: relative;
}
.ui-refresh .acct-switch-search .form-control {
    width: 100%;
    padding-left: var(--space-9);
    padding-right: var(--space-9);
}
.ui-refresh .acct-switch-search-icon,
.ui-refresh .acct-switch-search-spinner {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    color: var(--fg-subtle);
    font-size: var(--font-size-xs);
    pointer-events: none;
}
.ui-refresh .acct-switch-search-icon {
    left: var(--space-4);
}
/* Spinner shares the field's tail gutter with nothing else, so it can appear
   and disappear without shifting the input text. */
.ui-refresh .acct-switch-search-spinner {
    right: var(--space-4);
    display: none;
}
.ui-refresh .acct-switch-search.is-searching .acct-switch-search-spinner {
    display: block;
}

/* --- Status line --- */
.ui-refresh .acct-switch-status {
    padding: var(--space-2) 0 0;
    font-size: var(--font-size-xs);
    color: var(--fg-subtle);
    min-height: var(--line-height-xs);
}

/* --- Result list --- */
.ui-refresh .acct-switch-modal .acct-switch-results {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    /* Flex children default to min-height:auto and refuse to shrink below their
       content, which would push the list past the dialog instead of scrolling
       inside it. */
    min-height: 0;
    overflow-y: auto;
    padding: var(--space-1) var(--space-6) var(--space-6);
}
/* Dim + click-block while a search is in flight: the rows on screen are stale,
   and switching into the wrong account is not an undoable mistake. */
.ui-refresh .acct-switch-results.is-loading {
    opacity: 0.5;
    pointer-events: none;
}
.ui-refresh .acct-switch-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-2);
    border-radius: var(--radius-md);
    color: var(--fg-default);
    text-decoration: none;
    transition: background var(--transition-fast);
}
.ui-refresh .acct-switch-row:hover,
.ui-refresh .acct-switch-row:focus {
    background: var(--bg-subtle);
    color: var(--fg-default);
    text-decoration: none;
    outline: none;
}
.ui-refresh .acct-switch-row:focus-visible {
    box-shadow: inset 0 0 0 2px var(--accent-primary);
}
.ui-refresh .acct-switch-row-body {
    flex: 1;
    min-width: 0;
}
.ui-refresh .acct-switch-row-name {
    display: block;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ui-refresh .acct-switch-row-id {
    font-weight: var(--font-weight-normal);
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
}
.ui-refresh .acct-switch-row-meta {
    display: block;
    margin-top: var(--space-0);
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ui-refresh .acct-switch-row-meta i {
    margin-right: var(--space-1);
    color: var(--fg-subtle);
}
.ui-refresh .acct-switch-row-meta strong {
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
}
.ui-refresh .acct-switch-tag {
    display: inline-block;
    margin-left: var(--space-1);
    padding: 0 var(--space-1);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    font-size: var(--font-size-badge);
    font-weight: var(--font-weight-medium);
    color: var(--fg-muted);
    vertical-align: 1px;
}
/* Styled as a button but rendered as a span — the whole row is the anchor, and
   a <button> inside an <a> is invalid markup. */
.ui-refresh .acct-switch-row-action {
    flex: none;
    padding: var(--space-1) var(--space-3);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    font-size: var(--font-size-btn);
    font-weight: var(--font-weight-medium);
    color: var(--fg-muted);
    transition: border-color var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .acct-switch-row:hover .acct-switch-row-action,
.ui-refresh .acct-switch-row:focus .acct-switch-row-action {
    border-color: var(--accent-primary);
    color: var(--accent-on-tint);
}

/* --- Skeleton rows (slow search) — reuse the .skeleton-bar pulse primitive so
   the placeholder matches the real row geometry and doesn't jump on swap. --- */
.ui-refresh .acct-switch-row--skeleton {
    pointer-events: none;
}
.ui-refresh .acct-switch-skeleton-avatar {
    flex: none;
    width: 36px;
    height: 36px;
    border-radius: var(--radius-full);
}
.ui-refresh .acct-switch-skeleton-name,
.ui-refresh .acct-switch-skeleton-meta {
    display: block;
    height: 10px;
}
.ui-refresh .acct-switch-skeleton-name {
    width: 45%;
    margin-bottom: var(--space-2);
}
.ui-refresh .acct-switch-skeleton-meta {
    width: 65%;
}

/* --- Empty state --- */
.ui-refresh .acct-switch-empty {
    padding: var(--space-10) var(--space-4);
    text-align: center;
    color: var(--fg-muted);
}
.ui-refresh .acct-switch-empty i {
    font-size: var(--font-size-xl);
    color: var(--fg-subtle);
}
.ui-refresh .acct-switch-empty p {
    margin: var(--space-3) 0 0;
    font-size: var(--font-size-sm);
}

/* --- "Switching…" overlay: switching accounts is a full-page navigation that
   rebuilds the session, so the modal covers its own body for the duration
   rather than sitting there looking clickable. --- */
.ui-refresh .acct-switch-overlay {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: grid;
    place-items: center;
    background: var(--bg-page);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--fg-muted);
}

/* --- Account switcher modal — tighter on mobile, and give the capped
   row list more of the viewport since there's no side chrome to share it
   with. Kept next to the desktop rules above rather than in the shared
   767px block: the selectors are specificity-identical, so these only win
   by coming later in the file. --- */
@media only screen and (max-width: 767px) {
    .ui-refresh .confirm-dialog--form.acct-switch-modal {
        width: calc(100vw - var(--space-8));
        max-height: 88vh;
    }
    .ui-refresh .acct-switch-modal .confirm-dialog-header,
    .ui-refresh .acct-switch-modal .acct-switch-head {
        padding-left: var(--space-4);
        padding-right: var(--space-4);
    }
    .ui-refresh .acct-switch-modal .acct-switch-results {
        padding-left: var(--space-4);
        padding-right: var(--space-4);
    }
}

/* ===== Clone Event options (event/clone.php #clone-options) =====
   Groups of clone toggles as .toggle-group cards in a responsive grid —
   2-up in the doc column, 1-up on narrow screens without a media query. */
.ui-refresh .clone-options-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-3);
}
.ui-refresh .clone-options-actions {
    display: flex;
    gap: var(--space-2);
}
.ui-refresh .clone-options-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: var(--space-4);
    align-items: start;
}
/* The grid gap owns spacing; drop .toggle-group's page-level block margin. */
.ui-refresh .clone-options-grid > .clone-options-group {
    margin: 0;
}
.ui-refresh .clone-options-group-title {
    font-family: var(--font-family);
    font-size: 14px;
    line-height: 1.3;
    font-weight: var(--font-weight-semibold);
    color: var(--ink);
    margin: 0;
    padding: var(--space-3) 0;
}
/* .toggle-group only draws row-to-row dividers; separate the title too. */
.ui-refresh .clone-options-group-title + .toggle-row {
    border-top: 1px solid var(--panel-border);
}
/* Stack the option name over its hint ("Requires …" / "Always included"). */
.ui-refresh .clone-options-group .toggle-row > label.toggle-label:has(.clone-option-hint) {
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
}
.ui-refresh .clone-option-hint {
    display: block;
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-normal);
    line-height: 1.35;
    color: var(--muted);
}
/* Locked (always-cloned) options: dim the switch so it reads as fixed. */
.ui-refresh .clone-options-group .checkbox-toggle input[type="checkbox"][disabled] + span {
    opacity: 0.5;
}
.ui-refresh .clone-options-group .toggle-row:has(input[disabled]) > label.toggle-label {
    cursor: not-allowed;
}

/* ===== Clone Event progress / success (event/clone_progress.php) =====
   Centered narrow column shared by the waiting and success states. No form
   controls live here, so no --control-bg-color declaration is needed. */
.ui-refresh .clone-progress {
    max-width: 560px;
    margin: 0 auto;
    padding: var(--space-12) var(--space-6) var(--space-10);
}
.ui-refresh .clone-progress-hero {
    text-align: center;
}
/* Two stacked "event cards": the back copy sits offset up-right, the front
   card floats gently toward it to suggest duplication in progress. */
.ui-refresh .clone-progress-visual {
    position: relative;
    width: 96px;
    height: 72px;
    margin: 0 auto var(--space-6);
}
.ui-refresh .clone-visual-card {
    position: absolute;
    inset: 0;
    background: var(--bg-surface);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--sh-md);
}
.ui-refresh .clone-visual-card--back {
    transform: translate(10px, -10px) rotate(3deg);
    opacity: .55;
}
.ui-refresh .clone-visual-card--front {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;
    color: var(--accent-primary);
    animation: clone-card-float 2.4s ease-in-out infinite;
}
@keyframes clone-card-float {
    0%, 100% { transform: translate(0, 0); }
    50% { transform: translate(-4px, 4px); }
}
.ui-refresh .clone-progress-title {
    margin: 0 0 var(--space-2);
    font-family: var(--font-family);
    font-size: 24px;
    line-height: 1.2;
    font-weight: var(--font-weight-semibold);
    letter-spacing: -0.01em;
    color: var(--fg-default);
}
.ui-refresh .clone-progress-sub {
    margin: 0;
    font-size: var(--font-size-sm);
    line-height: 1.5;
    color: var(--fg-muted);
}
/* Event details card under the hero. The .form-section/.form-section-label
   eyebrow above it reuses the existing global styles. */
.ui-refresh .clone-progress-details {
    margin-top: var(--space-10);
}
.ui-refresh .clone-detail-list {
    margin: 0;
    background: var(--bg-surface);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--sh-sm);
    overflow: hidden; /* clips row dividers at the radius; nothing here hosts popovers */
}
.ui-refresh .clone-detail-row {
    display: grid;
    grid-template-columns: 140px minmax(0, 1fr);
    gap: var(--space-4);
    padding: var(--space-3) var(--space-4);
}
.ui-refresh .clone-detail-row + .clone-detail-row {
    border-top: 1px solid var(--border-subtle);
}
.ui-refresh .clone-detail-row dt {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    color: var(--fg-muted);
    align-self: center;
}
.ui-refresh .clone-detail-row dd {
    margin: 0;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--fg-default);
    overflow-wrap: anywhere;
}
/* Success state: everything centered, pop-in check. */
.ui-refresh .clone-progress--success {
    text-align: center;
    padding-top: calc(var(--space-12) * 2);
}
.ui-refresh .clone-success-check {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 72px;
    height: 72px;
    margin: 0 auto var(--space-6);
    border-radius: 50%;
    background: var(--color-success-bg);
    color: var(--color-success);
    font-size: 30px;
    animation: clone-check-pop 400ms ease-out;
}
@keyframes clone-check-pop {
    0% { transform: scale(.5); opacity: 0; }
    70% { transform: scale(1.08); opacity: 1; }
    100% { transform: scale(1); }
}
.ui-refresh .clone-success-continue {
    margin-top: var(--space-6);
}
@media (prefers-reduced-motion: reduce) {
    .ui-refresh .clone-visual-card--front,
    .ui-refresh .clone-success-check {
        animation: none;
    }
}
/* Event location "Where" field (issue #2790): resting-state summary card
   (map + address) shown in place of the always-expanded AddressInput fields,
   following the Info Card spec (design-language.md 8.9). */
.ui-refresh .address-summary-card {
    background: var(--bg-page);
    border-left: 3px solid var(--accent-primary);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    margin-bottom: var(--space-5);
    overflow: hidden;
}

.ui-refresh .address-summary-card-map-frame {
    display: block;
}

.ui-refresh .address-summary-card-body {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
}

.ui-refresh .address-summary-card-text {
    font-size: var(--font-size-sm);
    color: var(--fg-default);
}

.ui-refresh .address-summary-card-name {
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    margin-bottom: var(--space-2);
}

.ui-refresh .address-summary-card-name .fa-location-dot {
    color: var(--accent-primary);
    margin-right: var(--space-2);
}

.ui-refresh .address-summary-card-line-full,
.ui-refresh .address-summary-card-line-zip {
    color: var(--fg-muted);
}

.ui-refresh .address-summary-card-website {
    margin-top: var(--space-2);
}

.ui-refresh .address-summary-card-website .fa-arrow-up-right-from-square {
    color: var(--fg-muted);
    margin-right: var(--space-2);
}

.ui-refresh .map-location-action-buttons {
    display: flex;
    justify-content: flex-start;
    align-items: stretch;
    gap: 10px;
}

/* ==========================================================================
   Module activation hero (cfs-settings/partials/_activate-hero)

   The landing page for an event-level module that isn't switched on yet
   (Call for Speakers today). A one-button page reads as an empty settings
   screen when it's rendered as a plain section, so it gets a centred card
   that states what the module does and what turning it on unlocks, with the
   enable submit as the single obvious action.
   ========================================================================== */

.ui-refresh .module-activate {
    max-width: 520px;
    margin: var(--space-10) auto;
    padding: var(--space-10) var(--space-8);
    text-align: center;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius-xl);
    box-shadow: var(--sh-md);
}

.ui-refresh .module-activate-title {
    margin: 0 0 var(--space-3);
    font-size: 24px;
    line-height: 1.2;
    font-weight: var(--font-weight-semibold);
    letter-spacing: -0.01em;
    color: var(--ink);
}

.ui-refresh .module-activate-lede {
    margin: 0 auto;
    max-width: 42ch;
    font-size: var(--font-size-sm);
    line-height: 1.55;
    color: var(--muted);
}

/* Left-aligned inside a centred card: a ragged-right list of benefits is
   easier to scan than three centred lines of different widths. */
.ui-refresh .module-activate-points {
    display: grid;
    gap: var(--space-2);
    width: fit-content;
    margin: var(--space-6) auto 0;
    padding: 0;
    text-align: left;
    list-style: none;
}

.ui-refresh .module-activate-points li {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    font-size: var(--font-size-sm);
    color: var(--ink-2);
}

.ui-refresh .module-activate-points li i {
    color: var(--ok);
    font-size: 12px;
}

.ui-refresh .module-activate-btn {
    width: 100%;
    max-width: 260px;
    margin-top: var(--space-6);
}

@media (max-width: 767px) {
    .ui-refresh .module-activate {
        margin: var(--space-6) auto;
        padding: var(--space-6) var(--space-4);
    }

    .ui-refresh .module-activate-title {
        font-size: 20px;
    }

    .ui-refresh .module-activate-btn {
        max-width: none;
    }
}

/* ==========================================================================
   Status pill

   The event header's draft / live / cancelled badge, generalised so any
   read-only status that reads better as a state chip than as a disabled
   field can use it (the CFS site status on the Settings step). Moved here
   from layout.css, where it was ID-scoped to #event-navbar-details and
   therefore unreusable; that selector is kept so the event-header markup
   is untouched. Uppercase is deliberate — this is a state chip, not a label.
   ========================================================================== */

.ui-refresh .status-pill,
.ui-refresh #event-navbar-details .event-status-pill {
    display: inline-flex;
    align-items: center;
    font-family: var(--font-family);
    font-variant-numeric: tabular-nums;
    font-size: 10px;
    font-weight: var(--font-weight-medium);
    line-height: 1;
    padding: 4px var(--space-2) 3px;
    border-radius: var(--radius-full);
    border: 1px solid var(--border-default);
    background: var(--bg-muted);
    color: var(--fg-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.ui-refresh .status-pill[data-status="draft"],
.ui-refresh #event-navbar-details .event-status-pill[data-status="draft"] {
    background: var(--accent-subtle);
    color: var(--accent-hover);
    border-color: var(--accent-muted);
}

.ui-refresh .status-pill[data-status="live"],
.ui-refresh #event-navbar-details .event-status-pill[data-status="live"] {
    background: var(--color-success-bg);
    color: var(--color-success);
    border-color: var(--color-success-bg);
}

.ui-refresh .status-pill[data-status="cancelled"],
.ui-refresh #event-navbar-details .event-status-pill[data-status="cancelled"] {
    background: var(--color-error-bg);
    color: var(--color-error);
    border-color: var(--color-error-bg);
}

.ui-refresh .status-pill[data-status="closed"],
.ui-refresh .status-pill[data-status="archived"],
.ui-refresh #event-navbar-details .event-status-pill[data-status="closed"],
.ui-refresh #event-navbar-details .event-status-pill[data-status="archived"] {
    background: var(--bg-muted);
    color: var(--fg-muted);
    border-color: var(--border-default);
}

/* Swoogo Admin account view — mobile (see .rd-* block above) */
@media (max-width: 767px) {
    .ui-refresh .rd-header__actions {
        flex-wrap: wrap;
    }
    .ui-refresh .rd-header__actions > .btn,
    .ui-refresh .rd-header__actions > .btn-group {
        flex: 1 1 auto;
    }
    .ui-refresh .rd-tab-panel {
        padding: var(--space-3);
    }
    .ui-refresh .rd-table th,
    .ui-refresh .rd-table td {
        padding-left: var(--space-3);
        padding-right: var(--space-3);
    }
}

/* ===== Cancel registration (registrant/_cancel_ui_refresh.php) =====
   The partial-refund fee table sits in a Bootstrap collapse under the refund
   radio group. The gap above it lives on the collapse's inner wrapper — inside
   the animated box — so it grows and shrinks with the height instead of
   jumping when the panel opens or closes (design skill: never margin the
   .collapse element itself). The per-row CurrencyInput form-groups drop their
   trailing margin so the table rows keep the list-table rhythm. */
.ui-refresh .cancel-fees {
    margin-top: var(--space-4);
}
.ui-refresh .cancel-fees .list-table .form-group {
    margin-bottom: 0;
}

/* Section headings own the gap to their toggle card: the .panel-body
   clearfix (Bootstrap's :before/:after display:table) blocks margin
   collapse, so the card's own --space-5 top margin stacked on the heading's
   --space-5 bottom margin (40px, not 20). Same mechanism already fixed for
   .saml-page (see .saml-page .panel-body > .toggle-group:first-child above).
   The card's bottom margin likewise stacked on the section's --space-8
   margin-bottom, so the last child hands the boundary to the section margin
   instead (same fix as #transaction-form .panel--plain > .panel-body >
   :last-child above). */
.ui-refresh .registrant-cancel-form .panel--plain > .panel-body > .toggle-group:first-child {
    margin-top: 0;
}
.ui-refresh .registrant-cancel-form .panel--plain > .panel-body > :last-child {
    margin-bottom: 0;
}
/* InlineAlert carries no bottom margin by default (callers opt in with .mb,
   or a page scopes it like #email-form above) — here the group-membership
   warning sits directly above the first .panel--plain heading, which itself
   has no top margin (only margin-bottom, for the gap to its own body), so
   the alert butted flush against "Email options" with no gap at all. Same
   --space-5 the heading uses for its own bottom margin, so the alert reads
   as a section of its own rather than crowding the next one. */
.ui-refresh .registrant-cancel-form .inline-alert {
    margin-bottom: var(--space-5);
}

/* ===== Registrant bulk-action review pages =====
   registrant/send-mass-email, mass-cancel, mass-delete and
   registrant-multiple-update choose-field / update-info. A .list-page
   (`.bulk-action-page` marker passed through _list-page-open's `class`)
   whose sections are .panel--plain headings over .list-table rows. */

/* Destructive filled button — design-language.md 8.1. Bootstrap 3's own
   .btn-danger is a hardcoded red. --color-error is tuned as a *text*
   colour (lightened to a pale salmon on dark paper) and washes out as a
   solid fill — the same trap the confirm-dialog .btn-danger above hit — so
   fill from the stable red primitives instead; they read as one rich red
   on every surface, and hover escalates one step. */
.ui-refresh .btn-danger {
    background: var(--red-60);
    border-color: var(--red-60);
    color: var(--fg-on-accent);
}
.ui-refresh .btn-danger:hover,
.ui-refresh .btn-danger:focus {
    background: var(--red-70);
    border-color: var(--red-70);
    color: var(--fg-on-accent);
}
.ui-refresh .btn-danger:hover:not(:focus-visible),
.ui-refresh .btn-danger:focus:not(:focus-visible) {
    outline: none;
    box-shadow: none;
}

/* Alerts that sit directly on the page column, above the first section. */
.ui-refresh .bulk-action-page > .inline-alert {
    margin-bottom: var(--space-4);
}
.ui-refresh .bulk-action-page > .inline-alert + .panel--plain {
    margin-top: var(--space-6);
}
.ui-refresh .bulk-action-page .panel--plain .inline-alert {
    margin-bottom: var(--space-4);
}

/* A lone select / value input shouldn't stretch across the full 1240px
   column (same reasoning as the CFS "New user role" cap above). */
.ui-refresh .bulk-action-page .bulk-action-field {
    max-width: 560px;
}

/* Section heading with an inline action (update-info's Change Field).
   #content-container qualifier so it outranks the ID-scoped .panel-heading
   base rule. The 30px btn-sm inflates the row past the 19px heading text,
   so the shared --space-5 heading gap reads as ~26px of air over a lone
   control; pull it in so the field name sits over its input like a label. */
.ui-refresh :is(#content-container, #report-content) .bulk-action-page .panel--plain > .panel-heading.bulk-action-heading {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin-bottom: var(--space-3);
}

/* Registration-status email options under the value input. */
.ui-refresh .bulk-action-page .bulk-action-options {
    margin-top: var(--space-5);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}
.ui-refresh .bulk-action-page .bulk-action-options__label {
    margin: 0 0 var(--space-1);
    font-weight: var(--font-weight-semibold);
    color: var(--fg-default);
}

/* Table cells: numeric ID column, right-aligned actions column, and the
   muted Note / Reason text. Status colour is reserved for meaning:
   warn = paid transactions, danger = blocked / missing email. */
.ui-refresh .bulk-action-page .list-table .lt-cell-num {
    width: 1%;
    white-space: nowrap;
    color: var(--fg-subtle);
    font-variant-numeric: tabular-nums;
}
.ui-refresh .bulk-action-page .list-table .lt-th-actions,
.ui-refresh .bulk-action-page .list-table .lt-cell-actions {
    width: 1%;
    white-space: nowrap;
    text-align: right;
}
.ui-refresh .bulk-action-page .lt-cell-note {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
}
.ui-refresh .bulk-action-page .lt-cell-note--warn {
    color: var(--color-warning);
}
.ui-refresh .bulk-action-page .lt-cell-note--danger {
    color: var(--color-error);
}
/* Html::error() renders a <div class="…">; keep it inline in the cell. */
.ui-refresh .bulk-action-page .list-table div.lt-cell-note {
    display: inline-flex;
}

.ui-refresh .pm-fee-type {
    height: 100%;
}
.ui-refresh .pm-fee-type .btn-group-toggle {
    height: 100%;
    border: 1px solid var(--input-border);
}
.ui-refresh .pm-fee-type .btn-group-toggle .btn {
    height: 100% !important;
}

.ui-refresh .input-group .form-control.pm-fee-amount:not(:first-child):not(:last-child) {
    border-top-left-radius: var(--input-radius) !important;
    border-bottom-left-radius: var(--input-radius) !important;
    border-top-right-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
}

.ui-refresh .metadata-fields,
.ui-refresh .speaker-format-cards {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}
.ui-refresh .pm-metadata-card,
.ui-refresh .speaker-format-card {
    position: relative;
    padding: var(--space-3);
    border: 1px solid var(--input-border);
    border-radius: var(--radius-md);
}
.ui-refresh .pm-metadata-card .control-label,
.ui-refresh .speaker-format-card .control-label {
    margin-bottom: var(--space-1);
}
.ui-refresh .pm-metadata-card .control-label:first-of-type,
.ui-refresh .speaker-format-card .control-label:first-of-type {
    padding-right: var(--space-8);
}
.ui-refresh .pm-metadata-card .form-group:last-child,
.ui-refresh .speaker-format-card .form-group:last-child {
    margin-bottom: 0;
}
.ui-refresh .pm-metadata-card .lr-icon,
.ui-refresh .speaker-format-card .lr-icon {
    position: absolute;
    top: var(--space-1);
    right: var(--space-1);
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    background: transparent;
    border: 0;
    color: var(--fg-muted);
    cursor: pointer;
    transition: color var(--transition-fast);
}
.ui-refresh .pm-metadata-card .lr-icon:hover,
.ui-refresh .speaker-format-card .lr-icon:hover {
    color: var(--fg-default);
}
.ui-refresh .pm-metadata-card .lr-icon i,
.ui-refresh .speaker-format-card .lr-icon i {
    font-size: 13px;
}
/* ===== Speaker Formatting Settings drawer (speaker/ajax-drawer/settings.php)
   The display-format builder reuses the drawer's repeatable-row card (see the
   .pm-metadata-card selectors it is grouped with, further down). Only the bits
   metadata rows have no use for are declared here: a drag handle, the
   side-by-side prefix/suffix pair, and the drop placeholder. */
.ui-refresh .speaker-format-card {
    /* Room for the drag handle beside the absolutely-positioned remove icon. */
    padding-top: var(--space-6);
}
.ui-refresh .speaker-format-card .sft-drag {
    position: absolute;
    top: var(--space-2);
    left: var(--space-3);
    color: var(--fg-muted);
    cursor: grab;
    user-select: none;
    letter-spacing: -2px;
    line-height: 1;
}
.ui-refresh .speaker-format-card.ui-sortable-helper {
    cursor: grabbing;
    box-shadow: var(--sh-md);
    background: var(--bg-surface);
}
.ui-refresh .sft-card-placeholder {
    background: var(--bg-muted);
    border: 1px dashed var(--border-default);
    box-shadow: none;
    visibility: visible !important;
}
.ui-refresh .speaker-format-card .sft-pair {
    display: flex;
    gap: var(--space-3);
}
.ui-refresh .speaker-format-card .sft-pair > * {
    flex: 1;
    min-width: 0;
}
/* Each pair column's field is its own :last-child, so the shared card rule
   zeroes its bottom margin and the Leading space row would sit flush against
   the inputs.
   Restore the card's --space-4 rhythm below the pair, so all three groups sit
   18px apart. */
.ui-refresh .speaker-format-card .sft-pair {
    margin-bottom: var(--space-4);
}
/* Preview is its own section under the builder, not another field in it. */
.ui-refresh .speaker-format-preview {
    margin-top: var(--space-4);
}
.ui-refresh .speaker-format-preview .control-label {
    margin-bottom: var(--space-1);
}
/* Bootstrap's .well brings a 20px bottom margin and asymmetric 19/25px
   padding, which left the sample string sitting high in the box. Centre it
   instead, in a box sized like a form control — the preview is a single line
   of sample text, so it shouldn't stand taller than the inputs above it. */
.ui-refresh .speaker-format-preview .well.preview {
    display: flex;
    align-items: center;
    min-height: var(--space-10);
    padding: var(--space-3) var(--space-4);
    margin: 0;
}
.ui-refresh .test-response:empty {
    display: none;
}
.ui-refresh .ajax-drawer .test-response p {
    margin-bottom: var(--space-3);
}
.ui-refresh .ajax-drawer .discount.group-conditions {
    margin-top: var(--space-4) !important;
}
.ui-refresh .ajax-drawer .field-discount-apply_to_all:has(+ #line_items[style*="display: none"]),
.ui-refresh .ajax-drawer .field-discount-apply_to_all:has(+ #line_items[style*="display:none"]) {
    margin-bottom: 0;
}
.ui-refresh .ajax-drawer .form-control-static > .open-new-tab {
    color: var(--fg-muted);
    font-weight: var(--font-weight-medium);
    text-decoration: none;
    overflow-wrap: anywhere;
    transition: color var(--transition-fast);
}
.ui-refresh .ajax-drawer .form-control-static > .open-new-tab::after {
    content: '\f08e';
    margin-left: var(--space-2);
    font-family: 'Font Awesome 6 Pro';
    font-weight: 400;
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
    transition: color var(--transition-fast);
}
.ui-refresh .ajax-drawer .add-new-row {
    margin-top: var(--space-3);
}

.ui-refresh .payment-gateway-table .pg-th-tested,
.ui-refresh .payment-gateway-table .pg-cell-tested {
    text-align: center;
}

/* ===========================================================================
   Floating bulk-action bar
   =========================================================================== */
.ui-refresh .bulk-bar {
    position: fixed;
    left: 50%;
    bottom: var(--space-6);
    /* The organizer sidebar shell is z-index: 1000. Keep the selected-action
       bar reachable over that navigation, while leaving dialogs/drawers
       (1040+) above it. */
    z-index: 1001;
    display: flex;
    align-items: center;
    gap: var(--space-5);
    max-width: calc(100vw - var(--space-4));
    padding: var(--space-2) var(--space-3) var(--space-2) var(--space-4);
    overflow-x: auto;
    background: var(--bg-surface-raised);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-lg);
    box-shadow: var(--sh-lg);
    opacity: 0;
    pointer-events: none;
    transform: translateX(-50%) translateY(10px);
    transition: opacity var(--transition-normal), transform var(--transition-normal);
}
.ui-refresh .bulk-bar[hidden] {
    display: none;
}
.ui-refresh .bulk-bar.in {
    opacity: 1;
    pointer-events: auto;
    transform: translateX(-50%) translateY(0);
}
.ui-refresh .bulk-bar-selectall,
.ui-refresh .bulk-bar-actions {
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
}
.ui-refresh .bulk-bar-selectall {
    gap: var(--space-2);
    margin: 0;
    cursor: pointer;
}
.ui-refresh .bulk-bar .row-select {
    appearance: none;
    -webkit-appearance: none;
    position: relative;
    width: 16px;
    height: 16px;
    margin: 0;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--bg-surface);
    cursor: pointer;
}
.ui-refresh .bulk-bar .row-select:checked,
.ui-refresh .bulk-bar .row-select:indeterminate {
    border-color: var(--accent-primary);
    background: var(--accent-primary);
}
/* Checkmark / indeterminate-dash glyphs — this shared bulk bar (any page using
   common/_bulk-bar.php + report-bulk-actions.js or bulk-select.js) previously
   had no `::after` here at all, so a selection just painted a plain accent
   box with no icon. sessions.css defines this same glyph pair for `.row-select`
   already (tracks/sessions bulk bar), which is why that page "looked right" —
   pages that don't happen to also load sessions.css got the plain box. Ported
   here so every bulk bar gets it regardless of what other CSS is loaded.
   `:not(:indeterminate)` matters because a partially-selected select-all is
   checked AND indeterminate at once; without the guard the checkmark and dash
   both try to render and overlap. */
.ui-refresh .bulk-bar .row-select:checked:not(:indeterminate)::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 4px;
    height: 8px;
    border: solid #fff;
    border-width: 0 2px 2px 0;
    transform: translate(-50%, -60%) rotate(45deg);
}
.ui-refresh .bulk-bar .row-select:indeterminate::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 8px;
    height: 2px;
    background: #fff;
    transform: translate(-50%, -50%);
}
/* Legacy `input[type=checkbox]:checked::before` (backend.css) paints a second,
   Font-Awesome checkmark on top of ours — suppress it here the same way
   sessions.css does for its own `.row-select`. */
.ui-refresh .bulk-bar .row-select::before,
.ui-refresh .bulk-bar .row-select:checked::before {
    content: none;
    display: none;
}
.ui-refresh .bulk-bar-count {
    color: var(--fg-default);
    font-size: 13px;
    font-weight: var(--font-weight-medium);
    white-space: nowrap;
}
.ui-refresh .bulk-bar-actions {
    gap: var(--space-2);
}
/* Chrome/Safari draw the native select arrow flush against the border
   regardless of padding-right — it doesn't move inward with extra padding,
   it just leaves more dead space before it. Padding-right alone (tried
   first) had no visible effect for that reason. Drawing our own chevron
   (same glyph/color as the additional-reg-quantity select above) and
   controlling its `background-position` is the only way to actually give it
   room from the rounded corner. */
.ui-refresh .bulk-bar-action-select {
    width: auto;
    min-width: 0;
    max-width: 36vw;
    height: 36px;
    border-color: var(--border-default);
    background-color: var(--bg-surface);
    color: var(--fg-default);
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2212%22%20height%3D%2212%22%20viewBox%3D%220%200%2012%2012%22%3E%3Cpath%20d%3D%22M10.293%2C3.293%2C6%2C7.586%2C1.707%2C3.293A1%2C1%2C0%2C0%2C0%2C.293%2C4.707l5%2C5a1%2C1%2C0%2C0%2C0%2C1.414%2C0l5-5a1%2C1%2C0%2C1%2C0-1.414-1.414Z%22%20fill%3D%22%23a8a39a%22%2F%3E%3C%2Fsvg%3E");
    background-repeat: no-repeat;
    background-position: right var(--space-3) center;
    background-size: 10px;
    padding-right: var(--space-6);
}
.ui-refresh .bulk-bar-clear {
    padding: var(--space-2) var(--space-3);
    border: 0;
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--fg-muted);
    font-weight: var(--font-weight-medium);
    cursor: pointer;
    transition: background var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .bulk-bar-clear:hover {
    background: var(--bg-muted);
    color: var(--fg-default);
}
.ui-refresh .bulk-bar a.bulk-bar-action {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    white-space: nowrap;
}
.ui-refresh .bulk-bar :is(button, a):focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}
.ui-refresh .bulk-bar .row-select:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}
.ui-refresh .bulk-bar-delete i {
    margin-right: 6px;
}
@media (max-width: 480px) {
    .ui-refresh .bulk-bar {
        justify-content: flex-start;
        width: calc(100vw - var(--space-4));
        gap: var(--space-3);
    }
    .ui-refresh .bulk-bar-action-select {
        max-width: 48vw;
    }
}

/* Meeting report management uses the shared page header and record cards. */
.ui-refresh .meeting-detail__title {
    margin: 0 0 var(--space-3);
    font-size: var(--font-size-xl);
    overflow-wrap: anywhere;
}
.ui-refresh .meeting-schedule {
    overflow-x: auto;
    color: var(--ink);
}
.ui-refresh .meeting-schedule .fc-view-container {
    min-width: 36rem;
}
.ui-refresh .meeting-schedule .fc-unthemed th,
.ui-refresh .meeting-schedule .fc-unthemed td,
.ui-refresh .meeting-schedule .fc-unthemed thead,
.ui-refresh .meeting-schedule .fc-unthemed tbody,
.ui-refresh .meeting-schedule .fc-unthemed .fc-divider {
    border-color: var(--line);
}
.ui-refresh .meeting-schedule .fc-unthemed .fc-today,
.ui-refresh .meeting-schedule .fc-unthemed .fc-divider {
    background: var(--bg-muted);
}
.ui-refresh .meeting-schedule .fc-button {
    background: var(--card);
    border: 1px solid var(--line);
    color: var(--ink);
    box-shadow: none;
    text-shadow: none;
}
.ui-refresh .meeting-schedule .fc-button:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}
.ui-refresh .meeting-schedule .fc-toolbar h2 {
    font-size: var(--font-size-lg);
}
.ui-refresh .meeting-schedule .fc-time-grid .fc-slats td {
    height: 3em;
    border-bottom: 0;
}
.ui-refresh .meeting-schedule .fc-license-message {
    background: var(--card);
    border-color: var(--line);
    color: var(--ink);
}
.ui-refresh .meeting-schedule .fc-event {
    background: var(--bg-muted) !important; /* FullCalendar emits inline event colors. */
    border-color: var(--line) !important;
    color: var(--ink) !important;
}
.ui-refresh .meeting-schedule .meeting-schedule--current {
    background: var(--accent-subtle) !important;
    border-color: var(--accent-primary) !important;
}
.ui-refresh .meeting-schedule .meeting-schedule--accepted {
    background: var(--color-info-bg) !important;
    border-color: var(--color-info) !important;
}
/* Meeting management composes the shared drawer, record details and action icons. */
.ui-refresh .meeting-participants {
    display: grid;
    gap: 0;
}
.ui-refresh .meeting-participant {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--line);
    min-width: 0;
}
.ui-refresh .meeting-form .participant-panel .meeting-participant {
    padding: var(--space-3);
}
.ui-refresh .meeting-form .meeting-participants {
    --control-bg-color: var(--bg-page);
}
.ui-refresh .meeting-participant:first-child { padding-top: 0; }
.ui-refresh .meeting-participant:last-child { border-bottom: 0; }
.ui-refresh .meeting-participant > .avatar { flex-shrink: 0; }
.ui-refresh .meeting-participant__info {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}
.ui-refresh .meeting-participant__info > a {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--ink);
}
.ui-refresh .meeting-participant__role,
.ui-refresh .meeting-detail__meta {
    display: block;
    margin-top: var(--space-1);
    color: var(--muted);
    font-size: var(--font-size-xs);
}
.ui-refresh .meeting-participant__info .toggle-row {
    display: inline-flex;
    width: fit-content;
    max-width: 100%;
    justify-content: flex-start;
    gap: var(--space-2);
    padding: var(--space-1) 0 0;
}
.ui-refresh .meeting-participant__info .toggle-row > label.toggle-label {
    flex: 0 1 auto;
}
.ui-refresh .meeting-participant__actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
    justify-content: flex-end;
}
.ui-refresh .meeting-person-picker { padding: 0; }
.ui-refresh .meeting-row-actions {
    display: flex;
    gap: var(--space-1);
    align-items: center;
}
.ui-refresh .meeting-row-actions .lr-icon,
.ui-refresh .meeting-participant > .lr-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 var(--space-8);
    width: var(--space-8);
    height: var(--space-8);
    padding: 0;
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--muted);
    font-size: var(--font-size-sm);
    text-decoration: none;
    transition: background var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .meeting-row-actions .lr-icon:hover,
.ui-refresh .meeting-participant > .lr-icon:hover {
    background: var(--paper-2);
    color: var(--ink);
}
.ui-refresh .meeting-row-actions .lr-icon.danger:hover,
.ui-refresh .meeting-participant > .lr-icon.danger:hover {
    background: var(--color-error-bg);
    color: var(--color-error);
}
.ui-refresh .meeting-row-actions .lr-icon:focus-visible,
.ui-refresh .meeting-participant > .lr-icon:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}
.ui-refresh .meeting-report .meeting-action-col {
    width: 1%;
    min-width: calc(var(--space-8) * 3 + var(--space-1) * 2);
    white-space: nowrap;
    padding-inline: var(--space-2) !important;
}
.ui-refresh .meeting-report table.dataTable {
    font-family: var(--font-family);
    font-size: var(--font-size-sm);
}
.ui-refresh .meeting-report table.dataTable > tbody > tr > td {
    font-family: var(--font-family);
    font-weight: var(--font-weight-medium);
    vertical-align: middle;
}
.ui-refresh .meeting-report .dt-layout-cell { padding-inline: 0; }
/* The shared drawer panel-body supplies its 12px gap; field margins must not
   add a second gap (including fields replaced by scheduling updates). */
.ui-refresh .ajax-drawer .meeting-form .form-group {
    margin-top: 0;
    margin-bottom: 0;
}
.ui-refresh .meeting-form .form-group > .help-block:empty { display: none; }
.ui-refresh .meeting-detail__heading {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
}
.ui-refresh .meeting-detail__heading .meeting-detail__title {
    flex: 1;
    min-width: 0;
    margin-bottom: 0;
}
.ui-refresh .meeting-detail__heading > .badge {
    flex-shrink: 0;
    margin-top: var(--space-1);
}
.ui-refresh .meeting-form .help-block {
    margin-bottom: 0;
    line-height: 1.5;
}
/* Keep the picker in normal flow so the following hint's top margin applies.
   active-form.css floats Select2 containers for legacy forms. */
.ui-refresh .meeting-form .meeting-person-picker > .select2-container {
    float: none;
}
/* `.ajax-drawer p { margin: 0 }` zeroes the base .help-block top margin, and
   this hint sits inside .meeting-person-picker so the panel-body flex gap
   doesn't reach it either — without this it sits flush against the search
   field above. */
.ui-refresh .meeting-form .meeting-person-picker > .help-block {
    margin-top: var(--space-2);
    line-height: var(--line-height-xs);
}
.ui-refresh .meeting-detail__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    padding-top: var(--space-3);
}
.ui-refresh .meeting-detail .rd-card__head {
    margin: 0;
    font-weight: var(--font-weight-semibold);
    font-size: var(--font-size-sm);
    padding: var(--space-4) var(--space-5);
}
.ui-refresh .meeting-detail .rd-card__body { padding: var(--space-5); }
.ui-refresh .meeting-cancel-options { margin-top: var(--space-4); }
.ui-refresh .meeting-cancel-submit {
    display: flex;
    justify-content: flex-end;
    padding-top: var(--space-4);
}
.ui-refresh .meeting-schedule { position: relative; }
.ui-refresh .meeting-schedule .fc-loading { display: none !important; }
.ui-refresh .meeting-schedule-loader {
    position: absolute;
    inset: 0;
    z-index: 2;
    align-items: center;
    background: var(--card);
    opacity: .9;
    pointer-events: none;
}
.ui-refresh .meeting-schedule-loader[hidden],
.ui-refresh .meeting-form [hidden],
.ui-refresh #meeting-cancel-dialog [hidden],
.ui-refresh #meeting-status-dialog [hidden] { display: none !important; }
@media (max-width: 600px) {
    .ui-refresh .meeting-participant { flex-wrap: wrap; }
    .ui-refresh .meeting-participant__actions {
        flex: 1 1 100%;
        justify-content: flex-start;
    }
    .ui-refresh .meeting-detail .rd-kv__row,
    .ui-refresh #meeting-cancel-dialog .rd-kv__row {
        flex-direction: column;
        gap: var(--space-1);
    }
    .ui-refresh .meeting-detail .rd-card__body { padding: var(--space-4); }
}

/* ==========================================================================
   Go Attend white-label apps — account index page + app drawer
   ========================================================================== */
.ui-refresh .wl-steps {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
    gap: var(--space-3);
}
.ui-refresh .wl-step {
    display: grid;
    grid-template-columns: 28px 1fr;
    gap: var(--space-3);
    align-items: start;
    padding: var(--space-4);
    background: var(--bg-surface);
    --control-bg-color: var(--bg-page);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--sh-sm);
}
.ui-refresh .wl-step-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-full);
    background: var(--accent-subtle);
    color: var(--accent-hover);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    font-variant-numeric: tabular-nums;
}
.ui-refresh .wl-step-title {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    color: var(--ink);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    line-height: 1.3;
}
.ui-refresh .wl-step-desc {
    margin: var(--space-2) 0 0;
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
    line-height: 1.55;
}
.ui-refresh .wl-step-link {
    color: var(--accent-primary);
    text-decoration: none;
}
.ui-refresh .wl-step-link:hover,
.ui-refresh .wl-step-link:focus {
    color: var(--accent-hover);
    text-decoration: underline;
}
.ui-refresh .wl-step-link .wl-step-link-arrow {
    margin-left: 2px;
    font-size: var(--font-size-xs);
    transition: transform var(--transition-fast);
}
.ui-refresh .wl-step-link:hover .wl-step-link-arrow,
.ui-refresh .wl-step-link:focus .wl-step-link-arrow {
    transform: translateX(2px);
}

/* Drawer-only helpers — replace the legacy inline styles from update.php */
.ui-refresh .ajax-drawer .wl-meta {
    display: grid;
    grid-template-columns: max-content 1fr;
    align-items: baseline;
    gap: var(--space-2) var(--space-4);
    margin: 0;
    font-size: var(--font-size-sm);
}
.ui-refresh .ajax-drawer .wl-meta dt {
    color: var(--fg-muted);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
}
.ui-refresh .ajax-drawer .wl-meta dd {
    margin: 0;
    color: var(--fg-default);
}
.ui-refresh .ajax-drawer .wl-readonly-thumb {
    max-height: 80px;
    border-radius: var(--radius-md);
}
.ui-refresh .ajax-drawer .wl-readonly-graphic {
    max-width: 100%;
    max-height: 120px;
    border-radius: var(--radius-md);
}
.ui-refresh .ajax-drawer .wl-readonly-shots {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}
.ui-refresh .ajax-drawer .wl-readonly-shots img {
    max-height: 120px;
    border-radius: var(--radius-md);
}
.ui-refresh .ajax-drawer .wl-color-swatch {
    display: inline-block;
    width: 16px;
    height: 16px;
    margin-right: var(--space-1);
    vertical-align: middle;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--wl-swatch);
}

/* AppIdentity JSON: the copy button floats in the top-right corner of the
   read-only textarea (same idea as the JS Snippets code popover's floating
   copy button, moved to the top so it clears the resize handle). JS toggles
   .is-copied and swaps the icon to a check for 1.5s. */
.ui-refresh .ajax-drawer .wl-json-field {
    position: relative;
}
.ui-refresh .ajax-drawer .wl-json-field > textarea.form-control {
    padding-right: var(--space-10);
    font-family: var(--font-mono);
    font-size: var(--font-size-xs);
    resize: vertical;
}
.ui-refresh .ajax-drawer .wl-json-copy {
    position: absolute;
    top: var(--space-2);
    right: var(--space-2);
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
    color: var(--fg-muted);
    cursor: pointer;
    transition: border-color var(--transition-fast), color var(--transition-fast);
}
.ui-refresh .ajax-drawer .wl-json-copy:hover {
    border-color: var(--border-strong);
    color: var(--fg-default);
}
.ui-refresh .ajax-drawer .wl-json-copy.is-copied {
    border-color: var(--color-success);
    color: var(--color-success);
}

/* Drawer events tab — .list-table skin with toggle cells. Per-column filters
   (text for ID/name, Yes/No selects for the toggles) live in the standard
   .lt-filter-row; sorting and filtering are client-side in
   whitelabel-event-list.js because the checkboxes carry unsaved state. The
   filter controls have no name, so they never post with the form. */
.ui-refresh .ajax-drawer .wl-event-panel > p {
    margin-bottom: var(--space-5);
}
.ui-refresh .wl-event-table .lt-th-id,
.ui-refresh .wl-event-table .lt-cell-id {
    width: 110px;
}
.ui-refresh .wl-event-table .lt-cell-id {
    color: var(--fg-muted);
    font-variant-numeric: tabular-nums;
}
/* Header text and the toggle share a left edge; fixed width keeps the two
   toggle columns from stealing room from the event name. */
.ui-refresh .wl-event-table .lt-th-center,
.ui-refresh .wl-event-table .lt-cell-center {
    text-align: left;
    width: 150px;
}
/* The shared `.list-table .lt-filter-row td` rule only sets vertical padding
   (.list-table drops Bootstrap's .table, so these thead <td>s otherwise get
   the browser's 1px default). Match the header cells' horizontal rhythm so
   the four controls stop touching. Same specificity as the shared rule; wins
   by source order. */
.ui-refresh .wl-event-table .lt-filter-row td {
    padding: var(--space-2) var(--space-3);
}
/* The Yes/No selects only need room for "All" + chevron — don't stretch them
   across the 150px toggle column. */
.ui-refresh .wl-event-table .lt-filter-row select.lt-filter-input {
    width: auto;
    min-width: 88px;
}
/* The drawer-wide .checkbox rules (label row-reverse + width:100%, wrapper
   max-width 50%, --gray-30 off-track) are built for label+switch form rows.
   Inside a table cell they push the switch right and the raw palette gray
   does not flip on dark surfaces. Selector chains include .lt-cell-toggle so
   they tie the drawer rules' specificity and win by source order. */
.ui-refresh .ajax-drawer .wl-event-table .lt-cell-toggle .checkbox {
    min-width: 0;
    max-width: none;
}
.ui-refresh .ajax-drawer .wl-event-table .lt-cell-toggle .checkbox label {
    display: inline-flex;
    flex-direction: row;
    justify-content: flex-start;
    width: auto;
}
.ui-refresh .ajax-drawer .wl-event-table .lt-cell-toggle .checkbox input[type="checkbox"] {
    background: var(--border-default);
}
.ui-refresh .ajax-drawer .wl-event-table .lt-cell-toggle .checkbox input[type="checkbox"]:checked {
    background: var(--accent-primary);
}
.ui-refresh .wl-event-table .checkbox input[type="checkbox"]:disabled {
    opacity: 0.5;
    cursor: default;
}
.ui-refresh .wl-event-table .lt-row[hidden] {
    display: none;
}

/* ==========================================================================
   Go Attend white-label apps — upsell card + interest modal
   ========================================================================== */
/* Promotional card above "How it works": same card language as .wl-step,
   with a light accent wash so it reads as marketing rather than data. */
.ui-refresh .wl-upsell {
    display: flex;
    align-items: center;
    gap: var(--space-8);
    padding: var(--space-6);
    background:
        radial-gradient(120% 160% at 100% 0%, var(--accent-subtle), transparent 55%),
        var(--bg-surface);
    --control-bg-color: var(--bg-page);
    border: 1px solid var(--panel-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--sh-sm);
}
.ui-refresh .account-form > .wl-upsell-banner:first-child {
    margin-top: var(--space-4);
}
.ui-refresh .wl-upsell-content {
    flex: 1 1 auto;
    min-width: 0;
}
.ui-refresh .wl-upsell-actions {
    gap: var(--space-2);
}
.ui-refresh .wl-upsell-body {
    max-width: 640px;
}
.ui-refresh .wl-upsell-features {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: 0 0 var(--space-5);
}
.ui-refresh .wl-upsell-features li {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    color: var(--ink);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
}
.ui-refresh .wl-upsell-features li i {
    color: var(--accent-primary);
    font-weight: var(--font-weight-semibold);
}
/* The offer card carries the time-boxed pricing message, so it gets the one
   loud accent surface on the page — the main card stays paper. */
.ui-refresh .wl-upsell-offer {
    flex: 0 0 240px;
    display: flex;
    flex-direction: column;
    align-self: stretch;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-5);
    background: var(--accent-subtle);
    border: 1px solid var(--accent-muted);
    border-radius: var(--radius-lg);
}
.ui-refresh .wl-upsell-offer-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    margin-bottom: var(--space-1);
    color: var(--accent-hover);
    background: var(--card);
    border: 1px solid var(--accent-muted);
    border-radius: var(--radius-full);
    font-size: var(--font-size-base);
}
.ui-refresh .wl-upsell-offer-title {
    color: var(--ink);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    line-height: 1.4;
}
.ui-refresh .wl-upsell-offer-sub {
    color: var(--fg-muted);
    font-size: var(--font-size-xs);
    line-height: 1.5;
}
@media (max-width: 767px) {
    .ui-refresh .wl-upsell {
        flex-direction: column;
        align-items: stretch;
    }
    .ui-refresh .wl-upsell-offer {
        flex-basis: auto;
    }
}

/* Interest modal success state */
.ui-refresh .wl-interest-success-state {
    padding: var(--space-6) var(--space-4);
}
.ui-refresh .wl-interest-success-icon {
    display: block;
    margin-bottom: var(--space-3);
    color: var(--color-success);
    font-size: 40px;
}
.ui-refresh .wl-interest-success-state h4 {
    margin: 0 0 var(--space-2);
    color: var(--ink);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
}
.ui-refresh .wl-interest-success-state p {
    margin: 0;
}
.ui-refresh .dt-responsive .dt-column-header .dt-column-title select {
    width: 100% !important;
}

/* Audit Log Tracking has two action buttons. Hold its scroll head and body
   columns at the same width when DataTables recalculates them on redraw. */
.ui-refresh #audit-log-trackable-grid table.dataTable colgroup col[data-dt-column="0"] {
    width: 100px !important;
    min-width: 100px !important;
}

/*
 * Swoogo Admin OAuth client drawer (modules/loggedin/modules/admin/views/oauth-client/ajax-drawer/form.php).
 * Scoped to #admin-oauth-client-drawer-form so these rules can't leak into the many
 * other .input-group/.field-* usages across the app.
 */
.ui-refresh #admin-oauth-client-drawer-form .input-group {
    display: flex;
}
.ui-refresh #admin-oauth-client-drawer-form .input-group-btn {
    width: auto;
}
.ui-refresh #admin-oauth-client-drawer-form .input-group-btn button {
    height: 100%;
    border-top-right-radius: var(--input-radius) !important;
    border-bottom-right-radius: var(--input-radius) !important;
}
.ui-refresh #admin-oauth-client-drawer-form .client-secret-container {
    margin-bottom: var(--space-5);
}
.ui-refresh #admin-oauth-client-drawer-form .client-secret-container .form-group {
    margin-bottom: var(--space-2);
}
.ui-refresh #admin-oauth-client-drawer-form .reset-secret-row {
    margin-top: 8px;
}
.ui-refresh #admin-oauth-client-drawer-form .tagify__input::before {
    color: var(--text-muted);
}

/* Tagify suggestion dropdown is appended to <body>, outside the form.
 * The npm build themes itself through --tagify-dd-* variables. */
.ui-refresh .tagify__dropdown {
    --tagify-dd-bg-color: var(--bg-surface);
    --tagify-dd-text-color: var(--fg-default);
    --tagify-dd-color-primary: var(--accent-primary);
    font-family: var(--font-family);
    font-size: var(--font-size-xs);
}
.ui-refresh .tagify__dropdown__wrapper {
    background: var(--bg-surface);
    color: var(--fg-default);
    border-radius: 0 0 var(--radius-md) var(--radius-md);
    box-shadow: var(--shadow-md);
}
.ui-refresh .tagify__dropdown__item {
    border-radius: var(--radius-sm);
}
.ui-refresh .tagify__dropdown__item--active {
    background: var(--bg-muted);
    color: var(--fg-default);
}

/* Match the Users action icons; the shared report CSS right-aligns these cells. */
.ui-refresh .comm-report.system-fonts-report table.dataTable td.action-col {
    text-align: left !important;
}
/* Family and detail samples use the font being inspected; the rest of the
   admin UI keeps its normal typeface. */
.ui-refresh .system-fonts .system-fonts-sample {
    color: var(--ink);
    font-size: var(--font-size-lg);
}
.ui-refresh .system-fonts .system-fonts-previews {
    padding-top: 0;
    padding-bottom: 0;
}
.ui-refresh .system-fonts .system-fonts-preview {
    padding: var(--space-4) 0;
    border-bottom: 1px solid var(--border-subtle);
}
.ui-refresh .system-fonts .system-fonts-preview:last-child {
    border-bottom: 0;
}
.ui-refresh .system-fonts .system-fonts-preview__label {
    margin: 0 0 var(--space-2);
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
}
.ui-refresh .system-fonts .system-fonts-preview__sample {
    margin: 0;
    color: var(--ink);
    font-size: var(--font-size-2xl);
    line-height: var(--line-height-2xl);
    overflow-wrap: anywhere;
}
.ui-refresh .system-fonts .system-fonts-preview--regular,
.ui-refresh .system-fonts .system-fonts-preview--italic {
    font-weight: var(--font-weight-normal);
}
.ui-refresh .system-fonts .system-fonts-preview--bold,
.ui-refresh .system-fonts .system-fonts-preview--bold-italic {
    font-weight: var(--font-weight-bold);
}
.ui-refresh .system-fonts .system-fonts-preview--italic,
.ui-refresh .system-fonts .system-fonts-preview--bold-italic {
    font-style: italic;
}
.ui-refresh .system-fonts .system-fonts-badging {
    overflow-wrap: anywhere;
}
.ui-refresh .system-fonts .system-fonts-badging pre {
    overflow-x: auto;
    color: var(--fg-default);
    background: var(--bg-page);
    border-color: var(--border-subtle);
}
/* ===== System Actions (admin/system-actions) ===== */
.ui-refresh .sysact-feature-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--space-3);
    list-style: none;
    margin: 0 0 var(--space-5);
    padding: 0;
}
.ui-refresh .sysact-feature-list li {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--font-size-sm);
    color: var(--fg-default);
}
.ui-refresh .sysact-feature-list i {
    color: var(--color-success);
}
.ui-refresh .sysact-card {
    margin-bottom: var(--space-5);
}
.ui-refresh .sysact-card-heading {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    color: var(--ink);
    margin: 0 0 var(--space-2);
}
/* Collapsible variant wraps icon+label in their own <span> so
   justify-content: space-between (from .comm-collapsible-toggle) splits it
   from the chevron as one group — the outer gap above only spaces that span
   from the chevron, not the icon from the label inside it. */
.ui-refresh .sysact-card-heading > span {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}
.ui-refresh .sysact-card-desc {
    color: var(--fg-muted);
    font-size: var(--font-size-sm);
    margin: var(--space-2) 0 var(--space-4);
}
.ui-refresh .sysact-card-heading.comm-collapsible-toggle {
    margin: 0;
}
.ui-refresh .sysact-alert {
    margin-bottom: var(--space-3);
}
.ui-refresh .sysact-subheading {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
    color: var(--ink);
    margin: var(--space-5) 0 var(--space-2);
}
.ui-refresh .comm-collapsible-body > .sysact-subheading:first-child {
    margin-top: 0;
}
.ui-refresh .sysact-card-hint {
    font-size: var(--font-size-xs);
    color: var(--fg-muted);
    font-style: italic;
    margin: var(--space-4) 0 0;
}
.ui-refresh .sysact-settings-table th:first-child,
.ui-refresh .sysact-settings-table td:first-child {
    width: 60px;
}
.ui-refresh .sysact-settings-table th:nth-child(3),
.ui-refresh .sysact-settings-table td:nth-child(3) {
    width: 200px;
}
/* .table-bordered ships a hardcoded #ddd border around and between every
   cell — never dims in dark mode and reads as harsh (same issue fixed for
   #child-pjax-grid/.list-grid above). Drop it and re-add only a quiet
   token-driven row divider. */
.ui-refresh .sysact-settings-table {
    border: none !important;
    min-width: 650px;
}
.ui-refresh .sysact-settings-table > thead > tr > th,
.ui-refresh .sysact-settings-table > tbody > tr > td {
    border: none !important;
    border-bottom: 1px solid var(--border-subtle) !important;
}
.ui-refresh .sysact-settings-table > tbody > tr:last-child > td {
    border-bottom: none !important;
}
/* Reviewer-flagged mobile bug: the settings/cronlock tables have no
   inherent max-width, so on narrow viewports they forced the whole page to
   scroll horizontally instead of scrolling within their own card. Same
   fix/convention as .admin-custom-domain-grid — make this wrapper the
   nearest scroll container so the overflow is contained locally. */
.ui-refresh .sysact-table-scroll {
    overflow-x: auto;
    overscroll-behavior-inline: contain;
}
/* Bootstrap 3's float-based .row/.col-md-4 don't equalize sibling column
   heights on their own — .sysact-sync-item's height: 100% below had nothing
   to size against. Make the row a flex container and each column a single-
   child flex box so every column stretches to the tallest one. */
.ui-refresh .sysact-sync-grid {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
}
.ui-refresh .sysact-sync-grid > [class*="col-"] {
    display: flex;
}
@media (max-width: 767px) {
    /* .col-md-4 only stacks full-width via Bootstrap's own media query;
       overriding display: flex above (needed for the height-stretch fix)
       takes the column out of that block-level stacking behavior, so it
       has to be restated here — plus drop the now-pointless 15px gutter
       padding once each card is full width and butting against the next. */
    .ui-refresh .sysact-sync-grid > [class*="col-"] {
        width: 100%;
        padding-inline: 0;
    }
}
.ui-refresh .sysact-sync-item {
    height: 100%;
    width: 100%;
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: var(--space-4);
}
.ui-refresh .sysact-sync-item-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
}
.ui-refresh .sysact-sync-item-title {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-weight: var(--font-weight-semibold);
    color: var(--ink);
}
.ui-refresh .sysact-sync-item-title i {
    color: var(--accent-primary);
}
.ui-refresh .sysact-sync-item-desc {
    color: var(--fg-muted);
    font-size: var(--font-size-xs);
    margin: 0 0 var(--space-3);
    flex: 1;
}
.ui-refresh .sysact-badge {
    display: inline-flex;
    align-items: center;
    padding: 4px 10px;
    border-radius: var(--radius-full);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    white-space: nowrap;
}
.ui-refresh .sysact-badge--accent {
    background: var(--accent-subtle);
    color: var(--accent-on-tint);
}

/* Bootstrap hardcodes `.text-muted { color: #777 }`, which doesn't flip for
   dark surfaces. Point it at the flipping token instead. */
.ui-refresh .text-muted {
    color: var(--fg-muted);
}

.ui-refresh .feature-flag-count {
    font-size: var(--font-size-xs);
}

.ui-refresh #content-container .feature-flag-details .panel.panel--plain {
    margin-bottom: var(--space-8) !important;
}
.ui-refresh .card.feature-flag-meta {
    padding: var(--space-3) 0;
    overflow: hidden;
}
.ui-refresh .feature-flag-meta-item {
    display: flex;
    align-items: baseline;
    gap: var(--space-4);
    padding: var(--space-2) var(--space-5);
}
.ui-refresh .feature-flag-meta-label {
    flex: 0 0 90px;
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    color: var(--fg-muted);
}
.ui-refresh .feature-flag-meta-value {
    flex: 1 1 auto;
    min-width: 0;
    font-size: var(--font-size-sm);
    color: var(--fg-default);
}
.ui-refresh .targeting-rule-card {
    margin-bottom: var(--space-5);
}
.ui-refresh .feature-flag-targets-table-wrap {
    border: 1px solid var(--panel-border);
    border-radius: var(--space-2);
}
