/* Add here all your CSS customizations */

.header .logo {
    float: left;
    margin: 10px 0 0 15px;
    width: 150px;
}

.card-header {
    padding-bottom: 34px;
}

/* The global 34px card-header padding is too tall for the onboarding review
   console; reset card headers within it to the theme's standard padding. */
.onboarding-review .card-header {
    padding-bottom: 18px;
}

.card-header-border {
    padding-left: 12px;
}

.rm-card-bg {
    background-color: transparent !important;
    border: 1px solid lightgray;
}

.livewire-sort-by>thead>tr>th:hover {
    cursor: pointer;
}

.select2-search__field:focus {
    outline: none;
}

.card {
    display: block;
}

#datatable-default_wrapper>.row>.col-lg-6 {
    padding-left: 0;
}

.userbox-for-mobile {
    float: right;
}

.width-100-percent {
    width: 100%;
}

@media only screen and (max-width: 567px) {

    /* For small mobile screens: */
    .set-height {
        height: 30px;
    }
}

@media only screen and (max-width: 667px) {

    /* tablets screens: */
    .float-left-on-mobile {
        float: left !important;
    }

    .page-header h2 {
        display: none;
    }
}


@media only screen and (max-width: 766px) {

    .logo-on-mobile {
        text-align: center;
        width: 100%;
        display: unset;
    }

    .logo-container {
        text-align: center;
        width: 100%;
    }

    .userbox {
        padding-top: 0.3rem !important;
    }
}

@media only screen and (max-width: 767px) {

    .logo-on-mobile {
        vertical-align: top;
        padding-top: 8px;
    }

    .logo-container {
        padding-top: 2px;
        padding-bottom: 2px;
    }

    .the-logo {
        max-width: 90%;
    }

    .make-it-small {
        font-size: 8px !important;
    }

    .tighten-row-margins-top {
        margin-top: -16px;
    }
}

@media only screen and (min-width: 768px) {

    .tighten-row-margins {
        margin-left: -34px;
        margin-right: -36px;
    }

    .tighten-row-margins-top {
        margin-top: -36px;
    }

    .tighten-alert {
        margin-bottom: 32px;
        margin-top: -30px;
        margin-left: -30px;
        margin-right: -33px;
    }
}

.pagination-scroll {
    overflow-x: auto;
}

/* pretty json  */
.json-editor-blackbord {
    font-size: 14px !important;
    /* pointer-events: none !important; */
}


pre {
    outline: 1px solid #ccc;
    background-color: whitesmoke;
    border: 1px solid silver;
    padding: 10px 20px;
    margin: 10px;
    white-space: pre-wrap
}


.json-pre{
    overflow-x: scroll !important;
    white-space: revert;
}

.display-none{
    display: none !important;
}

.xtb { display: table; font-family: monospace; }
.xtc { display: table-cell; }
.xmt { color: #0000CC; display: inline; }
.xel { color: #990000; display: inline; }
.xdt { color: #000000; display: inline; }
.xat { color: #FF0000; display: inline; }

/* Fix for Bootstrap accordion collapse visibility issue */
/* Override Tailwind's visibility: collapse with visibility: hidden for proper Bootstrap accordion behavior */
.accordion .collapse:not(.show) {
    visibility: hidden !important;
}

.accordion .collapse.show {
    visibility: visible !important;
}

/* ---------------------------------------------------------------------------
   Contextual help and manual
   --------------------------------------------------------------------------- */

/* The "?" in the page header.

   The theme ships this as a 50px block of #C3C3C3, which read as disabled and left
   dead space either side. It is now a solid brand-blue disc: the only coloured
   element in an otherwise grey header bar, so it is impossible to miss.

   Centring is done with flexbox on both the hit area and the disc. The theme relied
   on a hand-tuned line-height (53px) to fake vertical centring, which breaks the
   moment the glyph or the disc changes size, and was why the "?" sat high inside the
   circle. height:50px and vertical-align:top are kept because the sibling
   <ol class="breadcrumbs"> has line-height:50px, so both fill the same line box and
   line up exactly with the breadcrumb text. */
.page-header .sidebar-right-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: auto;
    height: 50px;
    margin: 0;
    padding: 0 22px 0 12px;
    vertical-align: top;
    color: #FFF;
}

/* The selector is doubled because theme.css carries
   "html.sidebar-light:not(.dark) .page-header .sidebar-right-toggle i { color: #333 }"
   (specificity 0,4,2), which beats a plain .page-header .sidebar-right-toggle i (0,2,1)
   and would leave a dark glyph on the blue disc. Matching that selector shape lets
   source order decide, and custom.css is loaded last. Preferred over !important so the
   rule stays overridable. */
.page-header .sidebar-right-toggle i,
html.sidebar-light:not(.dark) .page-header .sidebar-right-toggle i {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    line-height: 1;
    font-size: 14px;
    font-weight: 700;
    color: #FFF;
    background-color: #0088CC;
    border-radius: 50%;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
    transition: background-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, transform 0.15s ease-in-out;
}

.page-header .sidebar-right-toggle:hover {
    color: #FFF;
}

.page-header .sidebar-right-toggle:hover i,
html.sidebar-light:not(.dark) .page-header .sidebar-right-toggle:hover i {
    background-color: #006699;
    box-shadow: 0 3px 8px rgba(0, 102, 153, 0.45);
    transform: translateY(-1px);
}

.page-header .sidebar-right-toggle:active i,
html.sidebar-light:not(.dark) .page-header .sidebar-right-toggle:active i {
    transform: translateY(0);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
}

/* Keyboard focus needs to be visible, since this is a real control. */
.page-header .sidebar-right-toggle:focus-visible i,
html.sidebar-light:not(.dark) .page-header .sidebar-right-toggle:focus-visible i {
    outline: 2px solid #006699;
    outline-offset: 2px;
}

/* Help "start button" pinned to the bottom of the browser window.

   position:fixed rather than absolute, so it stays on the bottom edge of the viewport
   as the page scrolls instead of sitting at the foot of the document. Anchoring on the
   bottom edge also means expanding the accordion grows the panel upward, like a start
   menu opening above the button rather than running off the screen.

   The width has to be stated explicitly because a fixed element no longer inherits the
   sidebar's table-cell width. html.sidebar-left-sm, which this application sets, makes
   the sidebar 250px. */
.sidebar-left .nav-help {
    position: fixed;
    left: 0;
    bottom: 0;
    width: 250px;
    z-index: 1011;
    background: #FFF;
    border-top: 1px solid #E0E3E8;
    box-shadow: 0 -3px 10px rgba(0, 0, 0, 0.06);
}

/* Follow the sidebar when it collapses to icons. */
@media only screen and (min-width: 768px) {
    html.sidebar-left-collapsed .sidebar-left .nav-help {
        width: 73px;
    }

    /* The right-hand help drawer slides the whole layout 300px left. A fixed element does
       not inherit that margin, so it is matched here to keep the button with its sidebar. */
    html.sidebar-right-opened .sidebar-left .nav-help {
        left: -300px;
    }
}

/* On mobile the sidebar is itself position:fixed and slides in from off-canvas. Revert to
   absolute so the button travels with the panel instead of being stranded on screen. */
@media only screen and (max-width: 767px) {
    .sidebar-left .nav-help {
        position: absolute;
        left: 0;
        right: 0;
        width: auto;
    }
}

.sidebar-left .nav-help > ul.nav-main {
    margin: 0;
    padding: 0;
}

/* column-reverse puts the children above the button in the visual order, so the button
   itself stays welded to the bottom edge of the window and the menu opens above it. Without
   this the block still grows upward, but the button rides up with it, which feels like the
   control is running away from the pointer. */
.sidebar-left .nav-help > ul > li.nav-parent {
    display: flex;
    flex-direction: column-reverse;
}

/* The button face itself. Sized a little taller than a normal nav row so it reads as a
   distinct control rather than one more menu entry. */
.sidebar-left .nav-help > ul > li > .nav-link {
    /* The caret is absolutely positioned by the theme. Without a positioned ancestor here it
       resolves against .nav-help, which is fixed, and drifts to the top of the open panel. */
    position: relative;
    display: flex;
    align-items: center;
    padding: 14px 18px;
    font-weight: 600;
    letter-spacing: 0.01em;
    color: #33353F;
    transition: background-color 0.15s ease-in-out;
}

html.sidebar-light:not(.dark) .sidebar-left .nav-help > ul > li > .nav-link {
    color: #33353F;
}

.sidebar-left .nav-help > ul > li > .nav-link:hover,
.sidebar-left .nav-help > ul > li.nav-expanded > .nav-link {
    background-color: #F2F5F7;
}

/* Brand-blue disc on the icon, echoing the "?" in the page header so the two obviously
   belong to the same feature. */
.sidebar-left .nav-help > ul > li > .nav-link > i,
html.sidebar-light:not(.dark) .sidebar-left .nav-help > ul > li > .nav-link > i {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    margin-right: 10px;
    font-size: 15px;
    color: #FFF;
    background-color: #0088CC;
    border-radius: 50%;
    flex: 0 0 auto;
}

/* The caret is inverted relative to the rest of the navigation, because this panel opens
   upward. The theme draws it as a Font Awesome glyph in an ::after (\f107 down when
   closed, \f106 up when expanded), so the two are simply swapped rather than rotated. */
.sidebar-left .nav-help ul.nav-main li.nav-parent > a:after {
    content: '\f106';
}

.sidebar-left .nav-help ul.nav-main li.nav-parent.nav-expanded > a:after {
    content: '\f107';
}

.sidebar-left .nav-help .nav-children {
    border-top: 1px solid #EDEFF2;
    background-color: #FAFBFC;
}

.sidebar-left .nav-help .nav-children .nav-link {
    font-size: 0.82rem;
    padding-left: 54px;
}

/* Keep long navigation clear of the pinned button. */
.sidebar-left .nano-content {
    padding-bottom: 64px;
}

/* Collapsed sidebar: only the disc shows, centred, and the label is hidden the same way
   the theme hides the other nav labels. */
html.sidebar-left-collapsed .sidebar-left .nav-help > ul > li > .nav-link {
    justify-content: center;
    padding: 14px 0;
}

html.sidebar-left-collapsed .sidebar-left .nav-help > ul > li > .nav-link > i {
    margin-right: 0;
}

/* The label has to leave the flow, not merely become invisible (DEV-2039).

   theme.css:1470 hides nav labels with visibility:hidden, which keeps the word occupying its
   full width. On the 73px rail that makes the flex row about 116px wide, and justify-content
   centre splits the overflow evenly, so the disc was pushed to x = -27 and the button read as
   an empty white box. */
html.sidebar-left-collapsed .sidebar-left .nav-help > ul > li > .nav-link > span {
    display: none;
}

/* Opening the help menu on a collapsed sidebar (DEV-2039).

   theme.css:1466 hides every nav-children on a collapsed sidebar and holds every nav label at
   visibility:hidden, and the escape hatch it pairs that with is .nano:hover (theme.css:1479).
   .nav-help is a second <nav class="nav-main nav-help"> that sits OUTSIDE .nano, so it inherited
   the hiding and never the escape hatch. On the 73px rail the panel was therefore a 73px box
   whose links carry a 54px left indent, holding invisible labels: clicking the button appeared
   to do nothing at all.

   These restore the same four things .nano:hover restores, plus the width. .nav-help is
   position:fixed, so the widened panel overlays the page rather than reflowing it. Hover and
   focus-within cover pointer and keyboard; :has() keeps it open after a click once the pointer
   has moved away. */
@media only screen and (min-width: 768px) {

    html.sidebar-left-collapsed .sidebar-left .nav-help:is(:hover, :focus-within, :has(li.nav-parent.nav-expanded)) {
        width: 250px;
    }

    html.sidebar-left-collapsed .sidebar-left .nav-help:is(:hover, :focus-within, :has(li.nav-parent.nav-expanded)) .nav-expanded > ul.nav-children {
        display: block;
    }

    html.sidebar-left-collapsed .sidebar-left .nav-help:is(:hover, :focus-within, :has(li.nav-parent.nav-expanded)) li.nav-parent > a:after {
        display: inline-block;
    }

    html.sidebar-left-collapsed .sidebar-left .nav-help:is(:hover, :focus-within, :has(li.nav-parent.nav-expanded)) a span {
        visibility: visible;
    }

    html.sidebar-left-collapsed .sidebar-left .nav-help:is(:hover, :focus-within, :has(li.nav-parent.nav-expanded)) > ul > li > .nav-link > span {
        display: inline;
    }

    html.sidebar-left-collapsed .sidebar-left .nav-help:is(:hover, :focus-within, :has(li.nav-parent.nav-expanded)) > ul > li > .nav-link {
        justify-content: flex-start;
        padding: 14px 18px;
    }

    html.sidebar-left-collapsed .sidebar-left .nav-help:is(:hover, :focus-within, :has(li.nav-parent.nav-expanded)) > ul > li > .nav-link > i {
        margin-right: 10px;
    }
}

/* Offcanvas backdrop (DEV-2012).

   This Bootstrap build ships the .offcanvas panel styles but not .offcanvas-backdrop, so the
   backdrop element Bootstrap injects renders at zero size: no dimming, and no click-outside to
   close. These are the stock Bootstrap 5 rules, added here rather than by editing the vendor file. */
.offcanvas-backdrop {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 1040;
    width: 100vw;
    height: 100vh;
    background-color: #000;
}

.offcanvas-backdrop.fade {
    opacity: 0;
}

.offcanvas-backdrop.show {
    opacity: 0.5;
}

/* Card subtitle contrast (DEV-2012).

   theme.css:7827 sets .card-subtitle to #808697, which is 3.64:1 against white
   and fails WCAG AA for text under 18pt (4.5:1). #6b6b6b is 5.33:1 and stays
   clearly subordinate to the .card-title above it.

   Deliberately a global override rather than a scoped one, because the class
   means the same thing everywhere. Its blast radius is small and known: the
   subtitle only renders where a description is actually supplied, which is the
   ten cards on /admin-dashboard and the five sections on /user/profile. Every
   other screen using <x-section-title> passes a title alone. */
.card-subtitle {
    color: #6b6b6b;
}

/* ==========================================================================
   Admin Dashboard (DEV-2012)

   Scoped to the admin-dashboard screen. Brand colours are declared here rather
   than inline in the Blade, which is the rule: project overrides live in
   custom.css and markup uses semantic classes.

   Palette: primary #0088CC, secondary #E36159, tertiary #2BAAB1.
   ========================================================================== */

/* --- Hero band ---------------------------------------------------------- */

.admin-hero {
    border-top: 3px solid #0088CC;
}

.admin-hero-row {
    display: flex;
    flex-wrap: wrap;
}

.admin-hero-cell {
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 22px 26px;
    border-right: 1px solid #ededed;
}

.admin-hero-cell:last-child {
    border-right: 0;
}

.admin-hero-label {
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #6b6b6b;
    margin-bottom: 6px;
}

.admin-hero-value {
    font-size: 2rem;
    font-weight: 600;
    line-height: 1.1;
    color: #33353a;
    margin-bottom: 6px;
    word-break: break-word;
}

.admin-delta {
    font-size: 0.78rem;
    font-weight: 500;
}

.admin-delta .fas {
    font-size: 0.66rem;
}

.admin-hero-footer {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 22px;
    align-items: center;
    font-size: 0.78rem;
    background: #fbfbfb;
}

.admin-hero-warning {
    margin-left: auto;
    font-weight: 600;
    text-decoration: none;
}

/* --- Estate stat strip -------------------------------------------------- */

.admin-stat .card-body {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 16px 18px;
}

.admin-stat-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 40px;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: #f1f5f8;
    color: #8a95a0;
    font-size: 1rem;
}

.admin-stat-alert .admin-stat-icon {
    background: #fdf3e3;
    color: #e0a800;
}

.admin-stat-body {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.admin-stat-label {
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: #6b6b6b;
}

.admin-stat-value {
    font-size: 1.5rem;
    font-weight: 600;
    line-height: 1.2;
    color: #33353a;
}

.admin-stat-alert .admin-stat-value {
    color: #e0a800;
}

.admin-stat-note {
    font-size: 0.72rem;
    color: #767676;
}

/* --- Shared small label ------------------------------------------------- */

.admin-section-label {
    display: block;
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: #6b6b6b;
    margin-bottom: 10px;
}

/* --- Concentration: share bar ------------------------------------------ */

.admin-share-bar {
    display: flex;
    width: 100%;
    height: 34px;
    border-radius: 4px;
    overflow: hidden;
    background: #f1f1f1;
}

.admin-share-seg {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.72rem;
    font-weight: 600;
    color: #fff;
    white-space: nowrap;
    overflow: hidden;
    transition: opacity 0.15s ease;
}

.admin-share-seg:hover {
    opacity: 0.85;
}

.admin-share-danger { background: #E36159; }
.admin-share-warning { background: #f0ad4e; }
.admin-share-primary { background: #0088CC; }
.admin-share-tertiary { background: #2BAAB1; }

.admin-share-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 18px;
    list-style: none;
    padding: 0;
    margin: 12px 0 0;
    font-size: 0.78rem;
    color: #6b6b6b;
}

.admin-share-key {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 2px;
    margin-right: 6px;
}

.admin-compact-table th,
.admin-compact-table td {
    padding: 5px 8px;
    font-size: 0.82rem;
    border-top: 1px solid #f0f0f0;
}

.admin-compact-table thead th {
    border-top: 0;
    font-size: 0.68rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: #767676;
}

/* --- Concentration: Herfindahl meter ----------------------------------- */

.admin-hhi {
    display: flex;
    align-items: baseline;
    gap: 12px;
    margin-bottom: 12px;
}

.admin-hhi-value {
    font-size: 2.2rem;
    font-weight: 600;
    line-height: 1;
}

.admin-hhi-meter {
    position: relative;
    height: 10px;
    border-radius: 5px;
    background: #eef0f2;
    overflow: hidden;
}

.admin-hhi-fill {
    display: block;
    height: 100%;
    border-radius: 5px;
}

.admin-hhi-tick {
    position: absolute;
    top: 0;
    width: 2px;
    height: 100%;
    background: rgba(0, 0, 0, 0.35);
}

.admin-hhi-scale {
    display: flex;
    justify-content: space-between;
    font-size: 0.66rem;
    color: #767676;
    margin-top: 4px;
}

.admin-narrative {
    margin: 16px 0 0;
    padding: 12px 14px;
    border-left: 3px solid #0088CC;
    background: #f7fbfd;
    font-size: 0.84rem;
    color: #55585c;
}

/* --- Watchlist ---------------------------------------------------------- */

.admin-watchlist-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    padding: 12px 18px;
    border-bottom: 1px solid #ededed;
    background: #fbfbfb;
}

.admin-watchlist-chip {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 5px 11px;
    border: 1px solid #e4e4e4;
    border-radius: 20px;
    background: #fff;
    font-size: 0.74rem;
    color: #6b6b6b;
    text-decoration: none;
}

.admin-watchlist-chip:hover {
    border-color: #0088CC;
    color: #0088CC;
}

.admin-watchlist-chip-count {
    font-weight: 700;
}

.admin-watchlist-chip.is-clear .admin-watchlist-chip-count { color: #47a447; }
.admin-watchlist-chip.is-danger .admin-watchlist-chip-count { color: #d2322d; }
.admin-watchlist-chip.is-warning .admin-watchlist-chip-count { color: #e0a800; }
.admin-watchlist-chip.is-info .admin-watchlist-chip-count { color: #0088CC; }

/* The whole point of the redesign: twenty matches can no longer run for four
   screens and bury every widget below this card. */
.admin-watchlist-body {
    max-height: 460px;
    overflow-y: auto;
}

.admin-watchlist-group {
    margin: 0;
    border: 0;
    border-bottom: 1px solid #f0f0f0;
    box-shadow: none;
}

.admin-watchlist-group .card-header {
    padding: 0;
    background: #fff;
}

/* theme.css sets .card-title { letter-spacing: -1px }, which is a deliberate
   tightening for its 18px headings. These group headers reuse card-title at
   roughly 13px, where -1px crushes the letters into each other: "Approval drop"
   renders as "Approvaldrop". Reset it for this header and everything in it. */
.admin-watchlist-group .card-title,
.admin-watchlist-group .card-title * {
    letter-spacing: normal;
}

.admin-watchlist-group .card-title {
    margin: 0;
    font-size: 0.85rem;
}

.admin-watchlist-group .accordion-toggle {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 4px 12px;
    padding: 11px 18px;
    text-decoration: none;
}

.admin-watchlist-group .accordion-toggle > small {
    font-size: 0.73rem;
    font-weight: 400;
    color: #6b6b6b;
}

/* The badge is a flex item, so without this it gets squeezed until "Approval
   drop" renders as "Approvaldrop". */
.admin-watchlist-group .accordion-toggle .badge {
    flex: 0 0 auto;
    white-space: nowrap;
}

/* Paired cards in a row should end level with each other. Porto's .card is a
   block, so h-100 alone leaves the shorter card floating above the row's
   bottom edge with page background showing beneath it. */
.admin-equal-row > [class*="col-"],
.admin-equal-row > .col {
    display: flex;
}

.admin-equal-row > [class*="col-"] > .card,
.admin-equal-row > .col > .card {
    width: 100%;
    /* The card has to be a flex column itself, or the flex:1 1 auto below is inert and the
       body stops where its content stops, leaving dead space inside a card that is already
       the right height. That is what made the paired rows read as ragged. */
    display: flex;
    flex-direction: column;
}

.admin-watchlist-count {
    font-weight: 600;
    color: #55585c;
}

.admin-watchlist-empty {
    margin: 0;
    padding: 8px 18px 12px 18px;
    font-size: 0.8rem;
    color: #767676;
}

.admin-watchlist-list {
    list-style: none;
    margin: 0;
    padding: 0 0 6px 0;
}

/* Name on its own line, evidence beneath it, always. Letting the two share a
   line when the name happens to be short gave rows of three different heights
   in the same list, which reads as broken rather than compact. */
.admin-watchlist-list li {
    display: block;
    padding: 7px 18px 8px 34px;
    border-top: 1px solid #f7f7f7;
}

.admin-watchlist-name {
    font-weight: 600;
    font-size: 0.84rem;
    color: #33353a;
}

.admin-watchlist-name {
    display: block;
}

.admin-watchlist-evidence {
    display: block;
    font-size: 0.76rem;
    color: #767676;
    margin-top: 1px;
}

/* --- Movers ------------------------------------------------------------- */

.admin-movers-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.admin-movers-list li {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 0 10px;
    padding: 7px 0;
    border-bottom: 1px solid #f4f4f4;
}

.admin-movers-list li:last-child {
    border-bottom: 0;
}

.admin-movers-name {
    font-size: 0.84rem;
    font-weight: 500;
    color: #33353a;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.admin-movers-change {
    font-size: 0.84rem;
    font-weight: 600;
    white-space: nowrap;
}

.admin-movers-detail {
    grid-column: 1 / -1;
    font-size: 0.72rem;
    color: #767676;
}

.admin-movers-empty {
    margin: 0;
    font-size: 0.8rem;
    color: #767676;
}

/* --- Chart cards -------------------------------------------------------- */

.admin-chart-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
}

.admin-chart-period {
    flex: 0 0 auto;
}

.admin-chart-period .form-control {
    width: auto;
    min-width: 150px;
}

.admin-chart-card .card-body {
    padding-top: 10px;
}

/* Belt and braces. theme.css:9026 already sets this to 0, but the wrapper is
   load-bearing for the visibility mechanic and a stray padding here would push
   the chart out of its fixed-height container. */
.admin-chart-card .chart-data-selector {
    padding: 0;
}

/* --- Chart drill-down breadcrumb (DEV-2077) ------------------------------ */

/* The trail shares the card title's line. Put on its own line it would start this
   card's chart lower than its neighbour's in the same row, which is the
   misalignment DashboardLayoutTest was written about. It wraps rather than
   overflows once a deep trail and a hint together outrun a narrow card. */
.admin-chart-title-line {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 4px 12px;
    min-width: 0;
}

/* Sits beside the card title, inside .admin-chart-head's first column. */
.chart-drill {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px;
    font-size: 0.78rem;
    line-height: 1.3;
}

/* Porto gives buttons a background and padding everywhere. A crumb is a link
   that happens to be a button because it navigates nothing. */
.chart-drill-crumb {
    padding: 0;
    border: 0;
    background: none;
    color: #0088cc;
    font-size: inherit;
}

.chart-drill-crumb:hover,
.chart-drill-crumb:focus-visible {
    color: #005580;
    text-decoration: underline;
}

.chart-drill-sep {
    color: #b5b5b5;
}

.chart-drill-current {
    color: #6b6b6b;
    font-weight: 600;
}

/* The invitation to click, which has to be readable without being loud enough to
   compete with the card title above it. It trails the crumbs rather than being
   pushed to the right, because .admin-chart-head sizes this column to its content
   and there is no right edge here to push against. */
.chart-drill-hint {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    color: #8a8a8a;
    font-style: italic;
}

/* Only once there is a trail in front of it does it need separating from one. */
.chart-drill-hint:not(:first-child) {
    margin-left: 6px;
    padding-left: 10px;
    border-left: 1px solid #e0e0e0;
}

.chart-drill-hint .fas {
    font-style: normal;
    opacity: 0.75;
}

/* The chart keeps showing the level being left until the next payload lands, so
   the only thing that needs to say "busy" is the trail itself. */
.chart-drill-busy::after {
    content: "";
    width: 10px;
    height: 10px;
    margin-left: 4px;
    border: 2px solid #d5d5d5;
    border-top-color: #0088cc;
    border-radius: 50%;
    animation: chart-drill-spin 0.6s linear infinite;
}

@keyframes chart-drill-spin {
    to {
        transform: rotate(360deg);
    }
}

@media (prefers-reduced-motion: reduce) {
    .chart-drill-busy::after {
        animation: none;
    }
}

/* A bar you can drill into should say so before the tooltip does. The class is put
   on by dashboard.js only while the rendered payload has somewhere to go, so the
   merchant grain and a single-merchant viewer's day grain stay on the default
   arrow. It lands on the container rather than on the plot rects because the class
   names FusionCharts gives those are internal and change between builds. */
.chart-drillable svg {
    cursor: pointer;
}

.admin-chart-note {
    font-size: 0.76rem;
    color: #6b6b6b;
    background: #fbfbfb;
}

.admin-chart-note .bx {
    color: #8a8a8a;
}

/* --- League and revenue tables ------------------------------------------ */

.admin-table-card .card-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
}

.admin-sparkline {
    display: block;
    margin: 0 auto;
}

@media only screen and (max-width: 991px) {
    .admin-hero-cell {
        border-right: 0;
        border-bottom: 1px solid #ededed;
    }

    .admin-hero-value {
        font-size: 1.6rem;
    }
}

/* --- Profile page -------------------------------------------------------- */

/* Initials avatar. There is no photo upload feature, and the shared stock
   portrait in the header reads oddly as "you" on your own profile. */
.profile-avatar {
    width: 76px;
    height: 76px;
    line-height: 76px;
    text-align: center;
    font-size: 1.6rem;
    font-weight: 600;
    letter-spacing: 1px;
    border-radius: 50%;
    display: inline-block;
}

/* Settings groups inside a tab pane. Flat rules rather than nested cards, so a
   tab does not read as a box inside a box inside a box. */
.profile-group + .profile-group {
    margin-top: 2rem;
    padding-top: 2rem;
    border-top: 1px solid #ededed;
}

.profile-group-title {
    font-size: 1rem;
    font-weight: 600;
    margin-bottom: 0.25rem;
}

.profile-group-description {
    color: #999;
    font-size: 0.85rem;
    margin-bottom: 1.25rem;
}

/* The one destructive action on the page, marked out rather than hidden. */
.profile-group-danger {
    border-left: 3px solid #d2322d;
    padding-left: 1rem;
}

/* Status list in the left summary card. */
.profile-status-list {
    list-style: none;
    padding: 0;
    margin: 0;
}

.profile-status-list li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.5rem 0;
    font-size: 0.85rem;
    border-bottom: 1px solid #f2f2f2;
}

.profile-status-list li:last-child {
    border-bottom: 0;
}

.profile-meta-label {
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-size: 0.7rem;
    color: #aaa;
    margin-bottom: 0.1rem;
}

.profile-meta-value {
    font-size: 0.875rem;
    word-break: break-word;
}

/* Recovery codes block. */
.profile-recovery-codes {
    background: #f6f6f6;
    border-radius: 4px;
    padding: 1rem;
    font-family: monospace;
    font-size: 0.85rem;
}

.profile-recovery-codes div {
    padding: 0.15rem 0;
}

/* ---------------------------------------------------------------------------
   Rate grid (billing configuration and rate card authoring).

   The grid is a data-entry surface, not a form: a rate card is filled in the way
   a spreadsheet is, so the cells are borderless until you touch them and the
   figures line up in columns. Chrome around each input would put twelve boxes on
   every row and make the numbers, which are the point, the least visible thing
   on the screen.
   --------------------------------------------------------------------------- */

/* Heading strip. The name is the page title, so it is typed into directly
   rather than sitting in a labelled field halfway down a form. */
.rate-identity-name {
    font-size: 1.35rem;
    font-weight: 500;
    border: 0;
    border-bottom: 1px dashed transparent;
    padding: 0.15rem 0;
    background: transparent;
    width: 100%;
}

.rate-identity-name:hover {
    border-bottom-color: #d6d6d6;
}

.rate-identity-name:focus {
    outline: 0;
    border-bottom-color: #0088CC;
    background: transparent;
}

.rate-identity-description {
    font-size: 0.85rem;
    color: #777;
    border: 0;
    border-bottom: 1px dashed transparent;
    padding: 0.1rem 0;
    background: transparent;
    width: 100%;
}

.rate-identity-description:hover {
    border-bottom-color: #e4e4e4;
}

.rate-identity-description:focus {
    outline: 0;
    border-bottom-color: #0088CC;
}

.rate-identity-meta {
    text-transform: uppercase;
    letter-spacing: 0.5px;
    font-size: 0.65rem;
    color: #aaa;
    margin-bottom: 0.1rem;
}

/* Transaction type tabs carry a marker showing whether that type is priced yet,
   so nine tabs can be read without opening any of them. */
.rate-tab-dot {
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    margin-right: 0.4rem;
    vertical-align: middle;
    background: #d3d3d3;
}

.rate-tab-dot.is-priced {
    background: #2BAAB1;
}

/* The grid itself. */
.rate-grid {
    border-collapse: separate;
    border-spacing: 0;
}

.rate-grid th,
.rate-grid td {
    border: 0;
    border-bottom: 1px solid #ececec;
    padding: 0;
    vertical-align: middle;
}

.rate-grid thead th {
    border-bottom: 1px solid #e0e0e0;
    background: #fafbfc;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: #888;
    font-weight: 600;
    padding: 0.4rem 0.5rem;
    text-align: center;
    white-space: nowrap;
}

.rate-grid thead th.rate-grid-group {
    font-size: 0.75rem;
    color: #555;
    border-left: 1px solid #ececec;
}

/* The status column stays put while the dimension columns scroll sideways,
   otherwise a wide grid loses the only label that says which row you are on. */
.rate-grid .rate-grid-status {
    position: sticky;
    left: 0;
    z-index: 2;
    background: #fff;
    padding: 0.35rem 0.9rem 0.35rem 0.6rem;
    text-align: left;
    font-weight: 600;
    font-size: 0.8rem;
    white-space: nowrap;
    border-right: 1px solid #ececec;
}

.rate-grid thead .rate-grid-status {
    background: #fafbfc;
}

.rate-grid tbody tr:hover .rate-grid-status {
    background: #f6f9fb;
}

.rate-grid tbody tr:hover td {
    background: #f6f9fb;
}

/* Base is the only required column, so it is tinted to read as the primary one
   and the optional dimension columns recede behind it. */
.rate-grid td.is-base,
.rate-grid th.is-base {
    background: #fcfdfe;
}

.rate-grid td.is-group-start,
.rate-grid th.is-group-start {
    border-left: 1px solid #ececec;
}

.rate-grid input {
    border: 0;
    background: transparent;
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-size: 0.85rem;
    width: 100%;
    min-width: 4.5rem;
    padding: 0.4rem 0.6rem;
    border-radius: 0;
    box-shadow: none;
}

.rate-grid input::placeholder {
    color: #c2c2c2;
}

.rate-grid input:hover {
    background: #f0f6fa;
}

.rate-grid input:focus {
    outline: 0;
    background: #fff;
    box-shadow: inset 0 0 0 2px #0088CC;
}

.rate-grid input.is-invalid {
    box-shadow: inset 0 0 0 2px #E36159;
    background: #fdf5f5;
}

/* A cell carrying a value the operator typed reads darker than one showing an
   inherited figure, so typed and inherited are never confused. */
.rate-grid input.is-set {
    color: #33353a;
    font-weight: 500;
}

.rate-grid .rate-grid-remove {
    padding: 0 0.5rem;
    text-align: center;
}

.rate-grid .rate-grid-remove a {
    color: #cfcfcf;
    opacity: 0;
    transition: opacity 0.15s ease;
}

.rate-grid tbody tr:hover .rate-grid-remove a {
    opacity: 1;
}

.rate-grid .rate-grid-remove a:hover {
    color: #E36159;
}

/* The preview follows the grid down the page: it is the answer to "what does
   this charge", and it is useless once it has scrolled away. */
.rate-preview-sticky {
    position: sticky;
    top: 1rem;
}

.rate-preview-figure {
    font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   DEV-2039: dashboard density on smaller desktop screens (1440x900 and down)

   The three dashboard screens were designed against the viewport width, but
   the space they actually get is much smaller. At 1440px the 250px sidebar
   (theme.css:1012) and the 40px .content-body padding (theme.css:65) take
   330px before anything is drawn, so a row gets 1180px while Bootstrap's
   media queries are reporting xl and xxl as active. Every column-count
   decision on these pages was made against a number 260px too optimistic.

   These rules close that gap. They are scoped to body.dashboard-page so no
   other screen in the app shifts, except the handful of theme defect fixes
   below which are correct everywhere.
   ========================================================================== */

/* --- Reclaimed width ----------------------------------------------------- */

@media only screen and (max-width: 1599px) {

    body.dashboard-page .content-body {
        padding: 24px;
    }

    /* .page-header deliberately breaks out of .content-body's padding
       (theme.css:116 sets margin: -40px). Retune it or it bleeds 16px past
       the content edge on both sides. */
    body.dashboard-page .page-header {
        margin: -24px -24px 24px -24px;
    }

    /* The -34px/-36px pair these replace was hand tuned against the 40px
       padding, and was asymmetric for no reason anyone recorded. */
    body.dashboard-page .tighten-row-margins {
        margin-left: -24px;
        margin-right: -24px;
    }

    body.dashboard-page .tighten-row-margins-top {
        margin-top: -24px;
    }

    body.dashboard-page .tighten-alert {
        margin-bottom: 24px;
        margin-top: -18px;
        margin-left: -18px;
        margin-right: -18px;
    }
}

/* --- Chart heights ------------------------------------------------------- */

/* Chart height used to be written in three places per chart: the wrapper's
   inline style, the FusionCharts constructor in the Blade, and again in
   dashboard.js/admin-dashboard.js when the period changed. These tokens are
   the single source; the JS reads them back with getComputedStyle. */
:root {
    --chart-h-sm: 305px;
    --chart-h-md: 340px;
    --chart-h-lg: 420px;
    --chart-h-xl: 460px;
}

/* A 900px tall screen should not spend half of it on one donut. */
@media only screen and (max-height: 950px) {
    :root {
        --chart-h-sm: 250px;
        --chart-h-md: 285px;
        --chart-h-lg: 345px;
        --chart-h-xl: 375px;
    }
}

/* --- Theme defect: mid-word breaks in the KPI strip ----------------------- */

/* theme.css:18807 sets word-break: break-all on .widget-summary .summary,
   which is what produced "Successful Transac|tion Count" and "Avg
   Transaction V|alue". No amount of extra width fixes that; it has to be
   turned off. overflow-wrap: anywhere keeps the safety net for a genuinely
   unbreakable string without licensing a break mid-word everywhere else. */
.widget-summary .summary {
    word-break: normal;
    overflow-wrap: anywhere;
    min-height: 0;
}

/* theme.css also lets .summary-footer simply flow after a min-height:65px
   .summary, so a card whose title wrapped to two lines pushed its delta line
   below the others. Pinning the footer to the bottom puts all of them on one
   baseline however the titles wrap. */
.widget-summary {
    height: 100%;
}

.widget-summary .widget-summary-col:not(.widget-summary-col-icon) {
    display: flex;
    flex-direction: column;
    height: 100%;
}

.widget-summary .summary-footer {
    margin-top: auto;
}

/* --- KPI cards respond to their own width -------------------------------- */

/* A container query, not a media query, because the mismatch being fixed is
   precisely that the viewport is not the card's width. Containment is scoped
   to the KPI card bodies, which hold no popovers or dropdowns, so the
   containing-block side effect of contain:layout cannot bite. */
.dashboard-kpi .card-body {
    container-type: inline-size;
}

/* theme.css:18795 gives the icon 90px plus a 15px margin, over 40% of a
   250px card. It is decoration; the number is the point. */
@container (max-width: 300px) {
    .widget-summary .summary-icon {
        width: 56px;
        height: 56px;
        line-height: 56px;
        font-size: 1.75rem;
        margin-right: 12px;
    }
}

@container (max-width: 215px) {
    .widget-summary .widget-summary-col-icon {
        display: none;
    }
}

/* --- Chart card heads ---------------------------------------------------- */

.admin-chart-card .card-header {
    container-type: inline-size;
}

/* Below this the title and the period select fight for the same line, the
   title wraps under the select, and the chart body starts at a different
   height from its neighbour in the same row. Stacking keeps every chart in a
   row starting on the same line. */
@container (max-width: 360px) {
    .admin-chart-head {
        flex-direction: column;
        gap: 8px;
    }

    .admin-chart-period {
        width: 100%;
    }

    .admin-chart-period .form-control {
        width: 100%;
        min-width: 0;
    }
}

/* --- Rows that must end level -------------------------------------------- */

/* .admin-equal-row already makes the cards equal height. What was left ragged
   was the shorter card's content stopping early inside a tall card, so the
   body gets to stretch and its trailing block takes the slack. */
.admin-equal-row > [class*="col-"] > .card > .card-body,
.admin-equal-row > .col > .card > .card-body {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
}

.admin-equal-row .card-body > .admin-fill-last:last-child {
    margin-top: auto;
}

/* --- Movers and Shakers -------------------------------------------------- */

.admin-movers-grid {
    container-type: inline-size;
}

/* Four quadrants inside a col-lg-5 gives each about 230px, which truncates
   any merchant name longer than "Ontec Systems Pty Lt...". One column reads
   properly and the card has the height to spare. */
@container (max-width: 420px) {
    .admin-movers-grid > [class*="col-"] {
        flex: 0 0 100%;
        max-width: 100%;
    }
}

/* --- Estate tiles -------------------------------------------------------- */

.admin-stat .card-body {
    container-type: inline-size;
}

/* "Never Transacted" is the longest label and the only one that wrapped. */
@container (max-width: 190px) {
    .admin-stat-label {
        font-size: 0.65rem;
        letter-spacing: 0.03em;
    }

    .admin-stat-icon {
        display: none;
    }
}

/* --- Admin dashboard filter bar ------------------------------------------ */

/* Three inline-block selects in .right-wrapper.text-end had nothing stopping
   them running past the right edge of the viewport. */
/* inline-flex, not flex. The breadcrumb list and the help button are siblings of this form
   inside .right-wrapper, and a block-level flex box takes the whole line and shunts them onto
   a second one, which then overflows the 50px page header. */
.admin-filter-bar {
    display: inline-flex;
    vertical-align: middle;
    flex-wrap: wrap;
    justify-content: flex-end;
    align-items: center;
    gap: 6px;
    max-width: 100%;
}

.admin-filter-bar .form-control {
    width: auto;
    min-width: 84px;
    max-width: 100%;
    flex: 0 1 auto;
}

/* --- Transaction Monitor ------------------------------------------------- */

/* The table had no width control of any kind: no colgroup, no fixed layout,
   and one bare nowrap on Tran ID. A single long gateway message reflowed the
   whole grid, and every row was a different height on a feed whose entire
   job is to be scanned. */
#transactionMonitor {
    table-layout: fixed;
    width: 100%;
}

#transactionMonitor > tbody > tr > td,
#transactionMonitor > thead > tr > th {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: middle;
}

/* Fixed row height so the table does not jump as rows stream in. .set-height
   already existed for this but was trapped inside a max-width:567px query. */
#transactionMonitor > tbody > tr {
    height: 34px;
}

#transactionMonitor.monitor-compact > tbody > tr {
    height: 26px;
}

#transactionMonitor.monitor-compact > tbody > tr > td {
    padding-top: 2px;
    padding-bottom: 2px;
}

/* Mode reads LIVE on every row, so it is the first thing to go when the
   table is genuinely short of width. */
@media only screen and (max-width: 1599px) {
    #transactionMonitor .monitor-col-mode {
        display: none;
    }
}

.monitor-toolbar {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
}

/* Type is a word plus an optional qualifier. Rendering the qualifier as a
   chip keeps "SETTLEMENTNON3DS" on one line and reads better besides. */
.monitor-type-chip {
    display: inline-block;
    margin-left: 4px;
    padding: 0 4px;
    border-radius: 3px;
    background: #eceff1;
    color: #55606a;
    font-size: 9px;
    line-height: 14px;
    vertical-align: middle;
}

/* Period selectors drew two arrows (DEV-2039).

   bootstrap-multiselect builds its button with buttonClass 'form-select', which carries
   Bootstrap's own SVG chevron, and then bootstrap-multiselect.css:59 adds a CSS triangle
   through .multiselect.dropdown-toggle:after on top of it. Keep the chevron, which matches
   every other select in the app, and drop the triangle. */
.multiselect.dropdown-toggle::after {
    display: none;
}

/* The theme sizes these inside an <h2>; the shared chart card puts them in a real card header,
   so they need the ordinary control width back. */
.admin-chart-period .multiselect {
    width: 100%;
    min-width: 150px;
    text-align: left;
}

.admin-chart-period .btn-group {
    width: 100%;
}

/* The page header must contain its own floated title (DEV-2039).

   Porto floats the .page-header h2 left, and at 54px it is taller than the 50px header that
   holds it, so the float escapes out of the bottom. Nothing noticed while the first row sat
   40px below; with the tighter dashboard spacing the row rises into the float's range, and a
   .row is a block-level flex container, which avoids floats rather than overlapping them. The
   whole KPI strip was shunted 263px right - exactly the width of "Welcome back: Admin!" - and
   narrowed to match.

   flow-root makes the header contain the float, which is what it should always have done. */
.page-header {
    display: flow-root;
}

/* Chart card headers, tightened (DEV-2039).

   custom.css:9 gives every .card-header a 34px bottom padding, and the period control was a
   38px tall 16px control. Together that is a 91px header above a 250px chart: a third of the
   card spent on its own title. On a 900px screen that is the difference between seeing one row
   of charts and two.

   The head is floored at two title lines rather than sized to its content, so a card whose
   title wraps ("Transaction Volumes by Continent") costs nothing next to one whose title does
   not, and every card in a row starts its chart on the same line. */
.admin-chart-card .card-header {
    padding-top: 12px;
    padding-bottom: 12px;
}

.admin-chart-head {
    align-items: center;
    min-height: 36px;
}

.admin-chart-period .btn-group,
.admin-chart-period .multiselect,
.admin-chart-period .form-control {
    width: auto;
    min-width: 0;
}

.admin-chart-period .multiselect,
.admin-chart-period select.form-control {
    min-width: 118px;
    padding: 4px 26px 4px 10px;
    font-size: 12.5px;
    line-height: 1.4;
    text-align: left;
    background-position: right 0.6em center;
    background-size: 0.55em;
}

/* ==========================================================================
   Responsive border utilities (DEV-2041)

   Bootstrap 5 ships .border-start and friends, but no breakpoint variants of
   them, so a two column panel that wants a divider only once the columns sit
   side by side has nothing to reach for. .border-start-lg was already in use on
   two screens before this existed, and was doing precisely nothing: an
   undefined utility class is not an error, it is silence, which is the worst
   way for a style to fail.

   Naming follows the existing call sites, breakpoint last, rather than
   Bootstrap's own breakpoint-in-the-middle form (.d-lg-none). Bootstrap has
   already taken .border-start-0 for "remove the border", so its convention was
   not available here anyway.

   The whole family is defined rather than only the variants in use today. A
   missing one fails silently in exactly the way the original bug did, and the
   cost of the other four is a few lines.

   Colour and width are Bootstrap 5.0.2's own literals rather than a token:
   that release predates --bs-border-color, and .border-start there is a flat
   1px solid #dee2e6. These have to match, or a responsive divider will not sit
   flush with a static one beside it.
   ========================================================================== */

@media only screen and (min-width: 576px) {
    .border-start-sm  { border-left: 1px solid #dee2e6 !important; }
    .border-end-sm    { border-right: 1px solid #dee2e6 !important; }
    .border-top-sm    { border-top: 1px solid #dee2e6 !important; }
    .border-bottom-sm { border-bottom: 1px solid #dee2e6 !important; }
}

@media only screen and (min-width: 768px) {
    .border-start-md  { border-left: 1px solid #dee2e6 !important; }
    .border-end-md    { border-right: 1px solid #dee2e6 !important; }
    .border-top-md    { border-top: 1px solid #dee2e6 !important; }
    .border-bottom-md { border-bottom: 1px solid #dee2e6 !important; }
}

@media only screen and (min-width: 992px) {
    .border-start-lg  { border-left: 1px solid #dee2e6 !important; }
    .border-end-lg    { border-right: 1px solid #dee2e6 !important; }
    .border-top-lg    { border-top: 1px solid #dee2e6 !important; }
    .border-bottom-lg { border-bottom: 1px solid #dee2e6 !important; }
}

@media only screen and (min-width: 1200px) {
    .border-start-xl  { border-left: 1px solid #dee2e6 !important; }
    .border-end-xl    { border-right: 1px solid #dee2e6 !important; }
    .border-top-xl    { border-top: 1px solid #dee2e6 !important; }
    .border-bottom-xl { border-bottom: 1px solid #dee2e6 !important; }
}

@media only screen and (min-width: 1400px) {
    .border-start-xxl  { border-left: 1px solid #dee2e6 !important; }
    .border-end-xxl    { border-right: 1px solid #dee2e6 !important; }
    .border-top-xxl    { border-top: 1px solid #dee2e6 !important; }
    .border-bottom-xxl { border-bottom: 1px solid #dee2e6 !important; }
}

/* Deliberately border only, with no padding baked in. A divider does want air
   around it, but Bootstrap already ships responsive spacing (.ps-lg-4 and
   friends), so the call site asks for that separately. Folding padding into
   this class would make it change layout beyond what its name says, on some
   breakpoints and not others, which is the same silent surprise these rules
   exist to remove. */

/* Livewire loading overlay. Porto's own .loading-overlay is driven by its
   jQuery plugin and animates visibility, which fights wire:loading's display
   toggle, so this is a static twin that only reuses Porto's bounce-loader.
   The parent needs position: relative. The loader is sticky rather than
   centred, so it stays in view over a table taller than the screen. It sits
   below the fixed header (z-index 1000) so it never covers the navigation. */
.lp-livewire-loading {
    position: absolute;
    inset: 0;
    margin: 0 !important;
    padding: 0 !important;
    z-index: 20;
    background: rgba(255, 255, 255, 0.7);
    cursor: progress;
}

.lp-livewire-loading .bounce-loader {
    position: sticky;
    top: 45vh;
    left: auto;
    margin: 0 auto;
}

/* Reconciliation Workspace settlement feeds: one line per feed, so the feed
   names line up down the card whatever their summaries say. */
.lp-feed-name {
    min-width: 6.5rem;
}
