/* ------------------------------------------------------------------------
   FinVentures page components: cards, tables, badges, buttons, form
   controls, filters, switches.

   ---- How to use it -----------------------------------------------------

   Every rule in this file is scoped to .fv-ui. A screen opts in by putting
   that class on its <body>:

       <body class="fv-ui d-flex min-vh-100 page-background">

   Screens that have not been converted yet carry on rendering exactly as
   they did. That is what makes this migration safe to do one page at a
   time: the stylesheet is loaded everywhere, but it only reaches the pages
   that have asked for it.

   The scope also settles the Bootstrap question. .card, .btn and .badge
   exist in both systems, and a bare .card here would fight Bootstrap's on
   all nineteen screens at the same specificity — last-loaded wins, which is
   a coin toss nobody wants to be managing. .fv-ui .card is (0,2,0) against
   Bootstrap's (0,1,0), so on a converted page ours wins outright and
   elsewhere Bootstrap is untouched.

   One consequence worth knowing: on a converted page, .fv-ui .card also
   catches any *Bootstrap* card still in that page's markup. That is a
   feature — it is the checklist. If something on a freshly converted page
   looks half-dressed, it is Bootstrap markup that has not been rewritten
   yet.

   ---- Where it came from ------------------------------------------------

   Generated from tools/design-system/fv-components.css — the merchant
   portal's app.css, copied verbatim and never edited — by prefixing every
   top-level selector:

       awk -f prefix.awk < selected-ranges-of-fv-components.css

   It is a *selection*, not the whole file. Three things were deliberately
   left behind:

     - The shell (.shell, .sidebar*, .nav*, .content). Those are in
       fv-shell.css under .fv-* names, because the sidebar renders on every
       screen and cannot be scoped to converted ones.

     - The .ph placeholder convention — the portal's red marking for figures
       that did not come from an API. That is a merchant-portal development
       convention and means nothing here; worse, this project already uses
       --text-placeholder for the ordinary grey of an empty input, so
       importing .ph would have put two opposite meanings on one idea.

     - The chart rules (.bars__bar, .donut, .legend__dot, svg.trend) and the
       portal's responsive breakpoints, which are written against .shell and
       .content rather than anything on this side.

   Add to the selection as pages need it rather than pulling the rest in on
   spec — every rule here is one more thing that can surprise a page.

   No raw hex values: every colour, radius and duration is a token from
   tokens.css, so a rebrand stays a one-file change.
   ------------------------------------------------------------------------ */

/* One-shot confirmation banner at the top of the content column.
   Uses the existing success tokens rather than inventing a green ramp:
   --state-success-bg is the same tint the alert icons use. */
.fv-ui .page-notice {
    display: flex;
    align-items: center;
    gap: 9px;
    margin-bottom: 20px;
    padding: 11px 14px;
    border-radius: var(--radius-md);
    font-size: 14px;
    background: var(--state-success-bg);
    border-left: 3px solid var(--fv-success);
    color: var(--text-body);
}

.fv-ui .page-notice i {
    color: var(--fv-success);
}

/* Same banner, used to report that something could not be loaded rather than
   that something succeeded. Warning rather than danger: the merchant has lost
   nothing, the screen just cannot show it right now. */
.fv-ui .page-notice--warn {
    background: var(--state-warning-bg);
    border-left-color: var(--fv-gold);
}

.fv-ui .page-notice--warn i {
    color: var(--fv-gold);
}

/* Pushes the sign-out button to the far edge of the user row. */
.fv-ui .page-header {
    margin-bottom: 28px;
}

.fv-ui .page-title {
    font-size: 32px;
    font-weight: var(--weight-bold);
    color: var(--text-heading);
}

.fv-ui .page-subtitle {
    color: var(--text-muted);
    margin-top: 6px;
    font-size: 16px;
}


/* --- Card --------------------------------------------------------------- */

.fv-ui .card {
    background: var(--surface-card);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-sm);
    padding: 24px;
    transition: box-shadow var(--motion-base) var(--ease);
}

.fv-ui .card:hover {
    box-shadow: var(--shadow-md);
}

/* Table cards carry no padding; their header and table supply their own. */
.fv-ui .card--flush {
    padding: 0;
}

.fv-ui .card__header {
    padding: 20px 24px;
    font-size: 16px;
    font-weight: var(--weight-bold);
    color: var(--text-heading);
    border-bottom: 1px solid var(--border-subtle);
}

.fv-ui .card__title {
    font-size: 16px;
    font-weight: var(--weight-bold);
    color: var(--text-heading);
    margin-bottom: 20px;
}


/* --- Grids -------------------------------------------------------------
   Always auto-fit + minmax, never a fixed track count, so columns reflow
   and collapse by available width. */

.fv-ui .grid {
    display: grid;
    gap: 20px;
    margin-bottom: 20px;
}

.fv-ui .grid--kpi {
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}

.fv-ui .grid--charts {
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}

/* Three-up stat tiles: slightly wider minimum than the four-up KPI grid. */
.fv-ui .grid--stats {
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
}


/* --- KPI tile ----------------------------------------------------------- */

.fv-ui .kpi__label {
    color: var(--text-muted);
    font-size: 14px;
}

.fv-ui .kpi__value {
    font-size: 28px;
    font-weight: var(--weight-bold);
    color: var(--text-heading);
    margin-top: 10px;
}

.fv-ui .kpi__sub {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 10px;
    font-size: 14px;
    font-weight: var(--weight-semibold);
}


/* --- Table -------------------------------------------------------------
   Wrapped in .table-wrap so wide tables scroll inside their card rather
   than clipping or stretching the page. */

.fv-ui .table-wrap {
    overflow-x: auto;
}

.fv-ui table.data {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
}

.fv-ui table.data th {
    text-align: left;
    padding: 12px 24px;
    font-size: 11px;
    font-weight: var(--weight-bold);
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: var(--tracking-caps);
    border-bottom: 1px solid var(--border-subtle);
    white-space: nowrap;
}

.fv-ui table.data td {
    padding: 14px 24px;
    color: var(--text-body);
    border-bottom: 1px solid var(--border-subtle);
    white-space: nowrap;
}

.fv-ui table.data tbody tr:last-child td {
    border-bottom: none;
}


/* --- Badge -------------------------------------------------------------- */

.fv-ui .badge {
    display: inline-block;
    padding: 2px 10px;
    border-radius: var(--radius-pill);
    font-size: 12px;
    font-weight: var(--weight-semibold);
    text-transform: capitalize;
}

.fv-ui .badge--success {
    background: var(--state-success-bg);
    color: var(--fv-success);
}

.fv-ui .badge--warning {
    background: var(--state-warning-bg);
    color: var(--fv-warning);
}

.fv-ui .badge--danger {
    background: var(--state-danger-bg);
    color: var(--fv-danger);
}

.fv-ui .badge--neutral {
    background: var(--fv-slate-200);
    color: var(--text-muted);
}

.fv-ui .badge--navy {
    background: var(--fv-navy);
    color: var(--fv-white);
}


/* --- Not-yet-built screens ---------------------------------------------- */

.fv-ui .placeholder {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 64px 24px;
    color: var(--text-muted);
}

.fv-ui .placeholder__icon {
    font-size: 28px;
    color: var(--fv-slate-400);
    margin-bottom: 16px;
}

.fv-ui .placeholder__title {
    font-size: 16px;
    font-weight: var(--weight-bold);
    color: var(--text-heading);
    margin-bottom: 8px;
}

.fv-ui .placeholder__body {
    font-size: 14px;
    max-width: 48ch;
    margin: 0;
}


/* --- Page header with a right-aligned action --------------------------- */

.fv-ui .page-header--split {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}


/* --- Buttons -----------------------------------------------------------
   Two variants from the design: alt (navy fill) and outline. Press scales
   to 98%; hover lifts navy to navy-800 and gives outline a slate wash. */

.fv-ui .btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 44px;
    padding: 0 18px;
    border-radius: var(--radius-input);
    font-family: inherit;
    font-size: 14px;
    font-weight: var(--weight-semibold);
    cursor: pointer;
    border: 1px solid transparent;
    transition: background var(--motion-fast) var(--ease),
                border-color var(--motion-fast) var(--ease),
                transform var(--motion-fast) var(--ease);
}

.fv-ui .btn:active {
    transform: scale(.98);
}

.fv-ui .btn:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgba(15, 23, 42, .35);
}

.fv-ui .btn--alt {
    background: var(--fv-navy);
    color: var(--fv-white);
}

.fv-ui .btn--alt:hover {
    background: var(--fv-navy-800);
}

.fv-ui .btn--outline {
    background: transparent;
    color: var(--text-heading);
    border-color: var(--border-default);
}

.fv-ui .btn--outline:hover {
    background: var(--fv-slate-100);
}


/* --- Form controls ------------------------------------------------------ */

.fv-ui .input {
    width: 100%;
    height: 44px;
    padding: 0 12px;
    font-family: inherit;
    font-size: 14px;
    color: var(--text-body);
    background: var(--surface-card);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-input);
}

.fv-ui .input:focus {
    outline: none;
    border-color: var(--fv-navy);
    box-shadow: 0 0 0 3px rgba(15, 23, 42, .35);
}


/* --- Filter bar --------------------------------------------------------- */

.fv-ui .filters {
    margin-bottom: 24px;
}

.fv-ui .filters__row {
    display: flex;
    gap: 16px;
    align-items: flex-end;
    flex-wrap: wrap;
}

.fv-ui .filters__search {
    flex: 1;
    min-width: 240px;
}

.fv-ui .filters__select {
    width: 160px;
}

.fv-ui .filters__select--wide {
    width: 200px;
}

.fv-ui .filters__meta {
    margin-top: 14px;
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px;
    font-size: 13px;
    color: var(--text-muted);
}


/* --- Table link --------------------------------------------------------- */

.fv-ui .tx-link {
    font-weight: var(--weight-semibold);
    text-decoration: none;
}

.fv-ui .tx-link:hover {
    text-decoration: underline;
}


/* --- Small uppercase section label -------------------------------------- */

.fv-ui .eyebrow {
    font-size: 11px;
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    color: var(--text-muted);
}


/* --- Three-line stat tile ----------------------------------------------
   The settlements and fees variant: 26px value, coloured sub-line, and a
   muted second line. Distinct from the dashboard's 28px KPI with an arrow. */

.fv-ui .stat__label {
    color: var(--text-muted);
    font-size: 14px;
}

.fv-ui .stat__value {
    font-size: 26px;
    font-weight: var(--weight-bold);
    color: var(--text-heading);
    margin-top: 10px;
}

.fv-ui .stat__sub {
    font-size: 14px;
    font-weight: var(--weight-semibold);
    margin-top: 8px;
}

.fv-ui .stat__sub2 {
    color: var(--text-muted);
    font-size: 13px;
    margin-top: 4px;
}


/* --- Small button ------------------------------------------------------- */

.fv-ui .btn--sm {
    height: 36px;
    padding: 0 14px;
    font-size: 13px;
}


/* --- Negative money in a table cell ------------------------------------- */

.fv-ui .amount--negative {
    color: var(--fv-danger);
}


/* --- Table cell variants -------------------------------------------------- */

/* Long prose in a table should wrap rather than force a scrollbar. */
.fv-ui .cell--wrap {
    white-space: normal;
    min-width: 280px;
}

.fv-ui .cell--actions {
    display: flex;
    gap: 14px;
}

/* Two lines in one cell: identifier above, human name beneath. */
.fv-ui .cell__primary {
    font-weight: var(--weight-semibold);
    color: var(--text-heading);
}

.fv-ui .cell__secondary {
    font-size: 12px;
    color: var(--text-muted);
}


/* --- Amount emphasis in tables -------------------------------------------- */

.fv-ui .amount--positive {
    color: var(--fv-success);
    font-weight: var(--weight-bold);
}

/* An em dash where a figure would be: muted, not alarming. */
.fv-ui .amount--none {
    color: var(--text-muted);
}

.fv-ui .icon--danger {
    color: var(--fv-danger);
    margin-right: 8px;
}

.fv-ui .icon--muted {
    color: var(--text-muted);
    margin-right: 8px;
}


/* --- Switch --------------------------------------------------------------
   Built on a real <input type="checkbox">: focusable, operable with Space,
   and announced correctly by screen readers. The input is made transparent
   and the track and thumb are painted over it. A div-based toggle would look
   the same and lose every one of those behaviours. */

.fv-ui .switches {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.fv-ui .switches--roomy {
    gap: 20px;
}

.fv-ui .switch {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    margin: 0;
}

.fv-ui .switch__control {
    position: relative;
    display: inline-block;
    width: 40px;
    height: 22px;
    flex: 0 0 auto;
    margin-top: 1px;
}

.fv-ui .switch__input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
    z-index: 1;
}

.fv-ui .switch__track {
    position: absolute;
    inset: 0;
    background: var(--fv-slate-200);
    border-radius: var(--radius-pill);
    pointer-events: none;
    transition: background var(--motion-base) var(--ease);
}

.fv-ui .switch__thumb {
    position: absolute;
    top: 3px;
    left: 3px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--fv-white);
    pointer-events: none;
    box-shadow: var(--shadow-sm);
    transition: transform var(--motion-base) var(--ease);
}

.fv-ui .switch__input:checked ~ .switch__track {
    background: var(--fv-navy);
}

.fv-ui .switch__input:checked ~ .switch__thumb {
    transform: translateX(18px);
}

.fv-ui .switch__input:focus-visible ~ .switch__track {
    box-shadow: 0 0 0 3px rgba(15, 23, 42, .35);
}

.fv-ui .switch__text {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.fv-ui .switch__label {
    font-size: 14px;
    color: var(--text-heading);
    font-weight: var(--weight-semibold);
}

.fv-ui .switch__desc {
    font-size: 12px;
    color: var(--text-muted);
}




/* ========================================================================
   FvOnboard additions
   ------------------------------------------------------------------------
   Everything above is the merchant portal's, transformed mechanically.
   Below are the few things this project needs that the portal never did.

   Keep this section small. Anything that turns out to be generally useful
   belongs upstream in the portal's app.css instead, so both products get it
   and neither drifts.
   ======================================================================== */

/* Vertical rhythm between the blocks of a page. A flex column with a gap
   rather than a margin on each card: margins between siblings collapse in
   ways that depend on what is above and below, and the last card ends up
   with trailing space it did not ask for. */
.fv-ui .stack {
    display: flex;
    flex-direction: column;
    gap: 20px;
}

/* --- One-line form -----------------------------------------------------
   A label, a field that takes the remaining width, and a button on the
   same baseline. The portal has no equivalent because nothing in it
   creates records; this side does it on six screens.
   ---------------------------------------------------------------------- */

.fv-ui .fv-form-row {
    display: flex;
    /* flex-end, so the button's bottom edge lines up with the input's
       rather than with the label above it. */
    align-items: flex-end;
    gap: 12px;
    flex-wrap: wrap;
}

/* flex:1 with min-width means the field takes the space left over but
   stops shrinking at 220px and wraps to its own line instead, so the
   button never ends up beside a two-character input. */
.fv-ui .fv-form-row__field {
    flex: 1;
    min-width: 220px;
}

.fv-ui .fv-label {
    display: block;
    margin-bottom: 7px;
    font-size: 13px;
    font-weight: var(--weight-semibold);
    color: var(--text-heading);
}

/* --- Undoing Bootstrap's Reboot inside the scope -----------------------
   Reboot gives h1..h6 and p a bottom margin. The portal has no Bootstrap,
   so every component it defines assumes none and sets its own spacing —
   .page-header carries the 28px below the title, .card__title the 20px
   below itself. Left alone, each of those gains an extra half-rem from
   underneath and the page grows slack.

   (0,1,1) against Reboot's (0,0,1), so this wins; and the components that
   do want a margin, such as .page-subtitle's 6px, are (0,2,0) and win over
   this in turn. That ordering is why this can be one blunt rule rather than
   a list of exceptions.
   ---------------------------------------------------------------------- */
.fv-ui h1,
.fv-ui h2,
.fv-ui h3,
.fv-ui h4,
.fv-ui h5,
.fv-ui h6,
.fv-ui p {
    margin-top: 0;
    margin-bottom: 0;
}


/* ========================================================================
   List-page components — added with the Terminals screen, 2026-09-29
   ------------------------------------------------------------------------
   Terminals is the first list page converted, and Merchants, Bin Files,
   Firmware and API Keys are the same page with different columns. So these
   are written as shared parts rather than as Terminals styles: the next page
   should need markup and no new CSS.
   ======================================================================== */

/* Copied from the portal's app.css with its values unchanged. They live in
   its screen-specific section rather than the shared layer this file was cut
   from, which is why the first conversion to need them found them missing. */
.fv-ui .card__header--split {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

.fv-ui .card__subtitle {
    color: var(--text-muted);
    font-size: 13px;
    margin-top: 4px;
    margin-bottom: 20px;
    font-weight: var(--weight-regular);
}

.fv-ui .card__header .card__subtitle {
    margin-bottom: 0;
}

/* --- Page-level actions, top right ------------------------------------ */
.fv-ui .fv-toolbar {
    display: flex;
    align-items: center;
    gap: 10px;
}

/* --- Filter bar ------------------------------------------------------- */

/* The per-page select holds a two-digit number; a 160px select for "10"
   pulls the eye to the least important control in the bar. */
.fv-ui .fv-filters__narrow {
    width: 140px;           /* fits "30 per page" without clipping it */
}

.fv-ui .fv-hint {
    margin-top: 12px;
    font-size: 13px;
    color: var(--text-muted);
}

/* --- Sortable column headings ----------------------------------------
   They inherit the heading's muted uppercase rather than being link-blue: a
   heading that looks like a link reads as navigation away from the table.
   The column currently sorted is darker, so the order is visible without
   reading the icon. */
.fv-ui .fv-sort {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: inherit;
    text-decoration: none;
}

.fv-ui .fv-sort:hover,
.fv-ui .fv-sort.is-active {
    color: var(--text-heading);
}

.fv-ui .fv-sort:focus-visible {
    outline: 2px solid var(--fv-navy);
    outline-offset: 2px;
    border-radius: 2px;
}

/* The idle two-way arrow is faint, so five of them do not form a pattern
   across the header; the active one is full strength. */
.fv-ui .fv-sort__icon {
    font-size: 11px;
    opacity: .45;
}

.fv-ui .fv-sort.is-active .fv-sort__icon {
    opacity: 1;
}

/* --- Identifiers ------------------------------------------------------
   Serials, ids and codes. These were set in a monospaced face until
   2026-10-06; the whole site now reads in the one typeface, so the class
   only lines digits up down a column. Kept on the markup so one rule can
   change how identifiers look everywhere. */
.fv-ui .fv-mono {
    font-family: inherit;
    font-variant-numeric: tabular-nums;
}

/* --- Row actions ------------------------------------------------------ */
.fv-ui .fv-col-actions {
    text-align: right;
    width: 1%;              /* shrink to the buttons; the data gets the rest */
    white-space: nowrap;
}

.fv-ui .fv-row-actions {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

/* Each action sits in a fixed-width slot, even when the slot is empty for
   this row — a terminal with no onboarding id has no OTP button — so every
   row's buttons line up with the rows above and below it. */
.fv-ui .fv-slot {
    width: 32px;
    display: flex;
    justify-content: center;
}

/* An icon-only button, squared off rather than padded like a labelled one.
   Its name comes from aria-label, and title shows it on hover. */
.fv-ui .fv-icon-btn {
    width: 32px;
    height: 32px;
    padding: 0;
    justify-content: center;
}

.fv-ui .icon--success { color: var(--fv-success); }
.fv-ui .icon--warning { color: var(--fv-warning); }

/* Bootstrap's dropdown, given the design system's surface and spacing. The
   markup stays Bootstrap's because Bootstrap's JavaScript positions it. */
.fv-ui .fv-menu {
    min-width: 200px;
    padding: 6px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
    font-size: 14px;
}

.fv-ui .fv-menu .dropdown-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: 6px;
    color: var(--text-body);
}

.fv-ui .fv-menu .dropdown-item:hover,
.fv-ui .fv-menu .dropdown-item:focus {
    background: var(--surface-subtle);
    color: var(--text-heading);
}

.fv-ui .fv-menu__icon {
    width: 16px;
    text-align: center;
    color: var(--text-muted);
}

/* --- The foot of a table: the count, then the pages ------------------ */
.fv-ui .fv-table-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
    padding: 14px 16px;
    border-top: 1px solid var(--border-subtle);
}

.fv-ui .fv-table-foot__count {
    font-size: 13px;
    color: var(--text-muted);
}

.fv-ui .fv-pager {
    display: flex;
    margin-left: auto;      /* stays right even when there is no count */
}

.fv-ui .fv-pager .pagination {
    gap: 4px;
}

.fv-ui .fv-pager .page-link {
    min-width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0 10px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-input);
    color: var(--text-heading);
    font-size: 13px;
    background: var(--surface-card);
}

.fv-ui .fv-pager .page-link:hover {
    background: var(--fv-slate-100);
}

.fv-ui .fv-pager .page-item.active .page-link {
    background: var(--fv-navy);
    border-color: var(--fv-navy);
    color: var(--fv-white);
}

.fv-ui .fv-pager .page-item.disabled .page-link {
    color: var(--text-muted);
    background: transparent;
    border-color: var(--border-subtle);
}

/* --- Dialogs -----------------------------------------------------------
   The inside of a Bootstrap modal: white, a plain title, the close button top
   right, and the actions bottom right with the safe one on the left. The
   gradient header these replaced put a coloured band on every confirmation,
   which made Activate and Suspend look equally alarming. */
.fv-ui .fv-modal {
    border: none;
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-md);
    overflow: hidden;
}

.fv-ui .fv-modal__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    padding: 20px 24px;
    /* Solid navy: the brand's own colour, and the one the sign-in page's
       panel uses. Solid rather than the gradient it replaced, so the header
       carries weight without the colour shifting across it. */
    background: var(--fv-navy);
}

.fv-ui .fv-modal__title {
    font-size: 18px;
    font-weight: var(--weight-bold);
    color: var(--fv-white);
}

/* Not .btn. Inside .fv-ui, .btn sets font-family to inherit, which beats
   Font Awesome's own font on a button carrying the fa class, and the icon
   renders as a missing glyph. The icon is an <i> inside this instead. */
.fv-ui .fv-modal__close {
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: var(--fv-slate-400);
    cursor: pointer;
}

.fv-ui .fv-modal__close:hover {
    background: var(--fv-navy-800);
    color: var(--fv-white);
}

.fv-ui .fv-modal__close:focus-visible {
    outline: 2px solid var(--fv-navy);
    outline-offset: 2px;
}

.fv-ui .fv-modal__body {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 20px 24px;
    font-size: 14px;
    line-height: 1.6;
    color: var(--text-body);
}

.fv-ui .fv-modal__note {
    color: var(--text-muted);
    font-size: 13px;
}

.fv-ui .fv-modal__foot {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    padding: 16px 24px;
    border-top: 1px solid var(--border-subtle);
    background: var(--surface-subtle);
}

/* --- A choice between options, as whole clickable rows --------------- */
.fv-ui .fv-choices {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* A <label> wrapping its radio, so a click anywhere on the row selects it
   natively. No script is needed for that part, and it works from the
   keyboard for free. */
.fv-ui .fv-choice {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 12px 14px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: border-color var(--motion-fast) var(--ease),
                background var(--motion-fast) var(--ease);
}

.fv-ui .fv-choice:hover {
    background: var(--surface-subtle);
}

.fv-ui .fv-choice input {
    margin-top: 3px;
    accent-color: var(--fv-navy);
}

/* :has() for the checked state, with the .selected class the page's script
   sets as the fallback for a browser without :has(). */
.fv-ui .fv-choice.selected,
.fv-ui .fv-choice:has(input:checked) {
    border-color: var(--fv-navy);
    background: var(--surface-subtle);
}

.fv-ui .fv-choice__title {
    display: block;
    font-weight: var(--weight-semibold);
    color: var(--text-heading);
}

.fv-ui .fv-choice__desc {
    display: block;
    margin-top: 2px;
    font-size: 13px;
    color: var(--text-muted);
}


/* ========================================================================
   Terminals, second pass — 2026-09-29
   ======================================================================== */

/* --- Page header: undo the old one --------------------------------------
   MTP's style.css gives .page-header a 2px yellow rule underneath and a
   fixed 64px height, and both still reached converted pages: fv-ui only
   ever set the header's bottom margin. The height was the worse of the two
   — a title and a subtitle do not fit in 64px, so the subtitle ran into the
   rule. Reset here, for every converted page at once. */
.fv-ui .page-header {
    border-bottom: none;
    height: auto;
    min-height: 0;
    padding-bottom: 0;
}

/* --- A table with many columns ------------------------------------------
   The portal's 24px cell padding suits its four- and five-column tables.
   At six columns plus a row of actions it made the table wider than a
   laptop screen, and since the table scrolls sideways inside .table-wrap,
   the Action column disappeared off the right edge. */
.fv-ui table.data.data--dense th,
.fv-ui table.data.data--dense td {
    padding-left: 16px;
    padding-right: 16px;
}

/* table.data keeps every cell on one line. A cell marked fv-wrap may wrap
   instead, so a long merchant name makes its row taller rather than the
   table wider. The minimum stops it collapsing to a word per line. */
.fv-ui table.data td.fv-wrap {
    white-space: normal;
    min-width: 100px;
}

/* --- The heading at the top of a dropdown menu -------------------------- */
.fv-ui .fv-menu__header {
    padding: 6px 10px 4px;
    font-size: 11px;
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    color: var(--text-muted);
}

/* --- A large form in a dialog: the Add terminal dialog ------------------ */

/* Narrower than Bootstrap's modal-xl (1140px). Fourteen fields in four rows
   of four read comfortably at 960; at 1140 each row is wide enough that the
   eye loses its place travelling back to the start of the next. Bootstrap
   5.3 sizes dialogs through this variable, so setting it is the whole job. */
.fv-ui .fv-modal-wide {
    --bs-modal-width: 960px;
}

.fv-ui .fv-modal__subtitle {
    margin-top: 4px;
    font-size: 13px;
    color: var(--fv-slate-400);
}

.fv-ui .fv-form {
    display: flex;
    flex-direction: column;
    gap: 24px;
}

/* A <fieldset> per group, so a screen reader announces "Terminal, group"
   before its fields, and the four groups are separated by a rule rather than
   by four differently coloured icons. */
.fv-ui .fv-fieldset {
    min-width: 0;
    margin: 0;
    /* Each group on a faint panel, so the form reads as four blocks rather
       than one long column of fields. */
    padding: 18px 20px 20px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--surface-subtle);
}

.fv-ui .fv-fieldset:first-of-type {
    /* Kept, and now identical to the rest: every group is a panel. */
    padding-top: 18px;
}

/* Bootstrap's Reboot floats a legend left at about 1.5rem. Undone here, so
   it reads as the small label above its group that it is. */
.fv-ui .fv-fieldset__legend {
    /* Floated, at full width: that is what pulls a legend inside its fieldset
       box instead of sitting on the top border and breaking the line. The
       next element clears it (Bootstrap's legend + * rule). */
    float: left;
    width: 100%;
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 14px;
    padding: 0;
    font-size: 12px;
    line-height: 1.4;
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    color: var(--fv-navy);
}

/* A short gold bar before each group's name: the brand's accent, used once
   per group and small enough that it marks the heading without competing
   with the fields. */
.fv-ui .fv-fieldset__legend::before {
    content: "";
    width: 3px;
    height: 14px;
    border-radius: 2px;
    background: var(--fv-gold);
}

/* Columns by available width, never a fixed count, so the dialog reflows on
   a narrow screen instead of squeezing fields to nothing. */
.fv-ui .fv-fields {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 16px 20px;
}

/* Only the two optional fields are marked, rather than an asterisk on the
   twelve that are required. Marking the exception is less noise. */
.fv-ui .fv-optional {
    margin-left: 4px;
    font-weight: var(--weight-regular);
    color: var(--text-muted);
}

.fv-ui .fv-field-hint {
    margin-top: 6px;
    font-size: 12px;
    color: var(--text-muted);
}

/* --- Searchable pickers --------------------------------------------------
   The list keeps .modern-dropdown from style.css, which hides it until the
   page's script shows it and positions it under the box. Only its look is
   restyled here, to match the rest of the dialog. */
.fv-ui .fv-picker__list {
    margin-top: 4px;
    padding: 4px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
}

.fv-ui .fv-picker__option {
    padding: 8px 10px;
    border-radius: 6px;
    font-size: 14px;
    color: var(--text-body);
    white-space: normal;
}

.fv-ui .fv-picker__option:hover {
    background: var(--surface-subtle);
    color: var(--text-heading);
}

/* The keyboard's current choice. The script has always added .highlighted
   on the arrow keys, and nothing ever styled it, so a keyboard user moving
   through the list could not see where they were. */
.fv-ui .fv-picker__option.highlighted {
    background: var(--fv-slate-100);
    color: var(--text-heading);
    box-shadow: inset 2px 0 0 var(--fv-navy);
}


/* ========================================================================
   Tables and small screens — 2026-09-29
   ======================================================================== */

/* --- Undo MTP's global table rules inside the design system -------------
   style.css styles every table on the site:

       table  { table-layout: fixed; width: 100%; }  "Distributes columns evenly"
       td, th { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

   table-layout: fixed ignores what is in a column and gives every column an
   equal share. That is why the Terminals columns were all the same width on
   a wide screen, and why the Action column vanished: it asked for width 1%,
   which under a fixed layout means 1% — about twenty pixels — and overflow:
   hidden then clipped its buttons away. Only the start of the word "Action"
   was left showing. Auto layout sizes each column to its content. */
.fv-ui table.data {
    table-layout: auto;
    word-wrap: normal;
}

.fv-ui table.data th,
.fv-ui table.data td {
    overflow: visible;
    text-overflow: clip;
}

/* The Action column stays in view. On a screen too narrow for the whole
   table, .table-wrap scrolls sideways; pinning this column to the right
   edge means the data slides under the buttons instead of the buttons
   sliding out of sight, which is how they were lost the first time. */
.fv-ui table.data .fv-col-actions {
    position: sticky;
    right: 0;
    z-index: 1;
    background: var(--surface-card);
}

/* The three text filters share one line with the rest of the bar down to
   about 1,050px, instead of pushing "Clear filters" onto a row of its own. */
.fv-ui .fv-filters--compact .filters__search {
    min-width: 180px;
}

/* --- Phones --------------------------------------------------------------
   Every filter full width, one per line, and the page actions under the
   title rather than squeezed beside it. */
@media (max-width: 680px) {
    .fv-ui .filters__row > * {
        flex: 1 1 100%;
        width: auto;
        min-width: 0;
    }

    .fv-ui .fv-toolbar {
        width: 100%;
    }

    .fv-ui .fv-toolbar .btn--alt {
        flex: 1;
        justify-content: center;
    }

    .fv-ui .fv-modal__head,
    .fv-ui .fv-modal__body,
    .fv-ui .fv-modal__foot {
        padding-left: 16px;
        padding-right: 16px;
    }

    .fv-ui .fv-fieldset {
        padding: 14px 14px 16px;
    }
}

/* --- Laptops ------------------------------------------------------------
   At 1024px the table was about fifty pixels wider than its card, so it
   scrolled and the pinned Action column sat on top of Status — "Act" and
   "Per" showing from underneath it, which read as broken. Tighter cells and
   slightly smaller action buttons make it fit outright at that width. */
@media (max-width: 1280px) {
    .fv-ui table.data.data--dense th,
    .fv-ui table.data.data--dense td {
        padding-left: 12px;
        padding-right: 12px;
    }

    .fv-ui .fv-row-actions {
        gap: 4px;
    }

    .fv-ui .fv-slot,
    .fv-ui .fv-icon-btn {
        width: 30px;
    }

    .fv-ui .fv-icon-btn {
        height: 30px;
    }
}

/* --- Tablets and below ----------------------------------------------------
   Here the table cannot fit and has to scroll sideways, and the pinned
   column will cover something. A soft edge on its left makes that read as
   a panel the rest of the row slides beneath, rather than a column that has
   been cut short. */
@media (max-width: 1100px) {
    .fv-ui table.data .fv-col-actions {
        box-shadow: -1px 0 0 var(--border-default),
                    -8px 0 10px -6px rgba(15, 23, 42, .22);
    }
}


/* ========================================================================
   Detail pages — OTP, Edit terminal, Certificate — 2026-09-29
   ------------------------------------------------------------------------
   The pages a Terminals row leads to. Written as shared parts, like the
   list-page block above, because Merchants will lead to the same kinds of
   page.
   ======================================================================== */

/* --- Back link above the title ------------------------------------------ */
.fv-ui .fv-back {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 12px;
    font-size: 14px;
    font-weight: var(--weight-semibold);
    color: var(--text-muted);
    text-decoration: none;
}

.fv-ui .fv-back:hover {
    color: var(--text-heading);
}

.fv-ui .fv-back:focus-visible {
    outline: 2px solid var(--fv-navy);
    outline-offset: 3px;
    border-radius: 4px;
}

/* --- Two cards side by side, stacking when there is no room ------------- */
.fv-ui .fv-split {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 20px;
    align-items: start;
}

.fv-ui .fv-card-heading {
    font-size: 16px;
    font-weight: var(--weight-bold);
    color: var(--text-heading);
}

/* A card's empty state, with less air than a whole-screen one. */
.fv-ui .fv-placeholder--inline {
    padding: 28px 16px;
}

/* --- Fields, one per line ----------------------------------------------- */
.fv-ui .fv-stack-fields {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

/* --- Locked fields look locked -------------------------------------------
   The Edit terminal page opens with every field read-only and unlocks most
   of them on Edit. Before, a locked field and an editable one looked alike,
   so nothing showed what Edit had done. A grey fill for locked, white for
   editable, and the change is visible the moment the button is pressed. */
.fv-ui .input[readonly],
.fv-ui .input:disabled {
    background: var(--surface-subtle);
    color: var(--text-body);
    border-color: var(--border-subtle);
    cursor: default;
    opacity: 1;              /* Bootstrap fades disabled controls; keep them readable */
}

/* --- A form group as a card on the page --------------------------------
   The Add terminal dialog shows its groups on a faint panel against white.
   On a page, the background is already that faint colour, so the same
   panel would vanish; here each group is a white card instead. */
.fv-ui .fv-fieldset.fv-fieldset--card {
    background: var(--surface-card);
    border-color: var(--border-subtle);
    box-shadow: var(--shadow-sm);
    padding: 20px 24px 24px;
}

/* --- The bar of buttons at the foot of a form -------------------------- */
.fv-ui .fv-actionbar {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 24px;
    padding: 16px 20px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-card);
    background: var(--surface-card);
}

.fv-ui .fv-actionbar__hint {
    margin-right: auto;
    font-size: 13px;
    color: var(--text-muted);
}

/* Bootstrap's d-none is !important and wins over the flex above, which is
   what lets the page's script swap the two bars by toggling it. */

/* --- The OTP -------------------------------------------------------------- */
.fv-ui .fv-otp {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
}

.fv-ui .fv-otp .card__title {
    align-self: stretch;
    text-align: left;
}

.fv-ui .fv-otp__ring {
    position: relative;
    width: 200px;
    height: 200px;
    margin: 4px 0 20px;
}

/* Rotated so the ring empties from twelve o'clock, like a clock face,
   rather than from three, where SVG starts a circle. */
.fv-ui .fv-otp__svg {
    transform: rotate(-90deg);
}

.fv-ui .fv-otp__track {
    stroke: var(--fv-slate-200);
}

.fv-ui .fv-otp__seconds {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-size: 40px;
    font-weight: var(--weight-bold);
    line-height: 1;
    color: var(--text-heading);
    font-variant-numeric: tabular-nums;
}

.fv-ui .fv-otp__unit {
    margin-top: 6px;
    font-size: 12px;
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    color: var(--text-muted);
}

/* The code itself: the one thing on the page, on the brand navy, with
   every digit the same width. */
.fv-ui .fv-otp__code {
    padding: 14px 28px;
    border-radius: var(--radius-md);
    background: var(--fv-navy);
    color: var(--fv-white);
    font-family: inherit;
    font-size: 34px;
    font-weight: 700;
    letter-spacing: .3em;
    font-variant-numeric: tabular-nums;
    /* The letter-spacing adds space after the last digit too; this puts it
       back so the code sits centred in its tile. */
    text-indent: .3em;
}

.fv-ui .fv-otp__note {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-top: 14px;
    font-size: 13px;
    color: var(--text-muted);
}

/* --- Certificate --------------------------------------------------------- */

/* Status and time left, side by side above the detail. */
.fv-ui .fv-cert-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 12px 40px;
    padding-bottom: 20px;
    margin-bottom: 4px;
    border-bottom: 1px solid var(--border-subtle);
}

.fv-ui .fv-cert-summary__label {
    margin-bottom: 6px;
    font-size: 11px;
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    color: var(--text-muted);
}

.fv-ui .fv-cert-summary .badge {
    font-size: 13px;
    padding: 4px 12px;
}

/* A definition list: label on the left, value on the right, one per line.
   <dl> rather than a two-column table, because that is what it is — pairs,
   not rows of records — and a screen reader announces it as such. */
.fv-ui .fv-dl {
    margin: 0;
}

.fv-ui .fv-dl__row {
    display: grid;
    grid-template-columns: minmax(140px, 220px) 1fr;
    gap: 16px;
    padding: 12px 0;
    border-bottom: 1px solid var(--border-subtle);
}

.fv-ui .fv-dl__row:last-child {
    border-bottom: none;
}

.fv-ui .fv-dl dt {
    font-size: 13px;
    font-weight: var(--weight-semibold);
    color: var(--text-muted);
}

.fv-ui .fv-dl dd {
    margin: 0;
    font-size: 14px;
    color: var(--text-heading);
    overflow-wrap: anywhere;   /* a long hex serial wraps rather than overflowing */
}

@media (max-width: 680px) {
    .fv-ui .fv-dl__row {
        grid-template-columns: 1fr;
        gap: 4px;
    }

    .fv-ui .fv-actionbar__hint {
        flex-basis: 100%;
    }
}

/* --- Collisions with Bootstrap and style.css, found on the detail pages --- */

/* Bootstrap 5.3 has its own .placeholder: a loading skeleton, painted solid
   in the text colour at half opacity. The design system uses the same name
   for an empty state, so every "nothing here yet" message on a converted
   page was drawn as a grey slab with its text sunk into it. This takes back
   the parts of Bootstrap's rule that do the damage. */
.fv-ui .placeholder {
    background-color: transparent;
    opacity: 1;
    min-height: 0;
    vertical-align: baseline;
    cursor: auto;
}

/* style.css makes .page-header a space-between flex row, which pushed a
   subtitle to the far right of its title on any page whose header is just a
   title and a line of text. A plain header stacks; the --split variant, with
   actions on the right, keeps its own flex layout. */
.fv-ui .page-header:not(.page-header--split) {
    display: block;
}

/* style.css has #otp { color: #343a40; margin-top: 20px; font-size: 2rem }.
   An id outranks any class, so the code came out dark grey on navy. Matching
   the id here is the only way to win without !important. */
.fv-ui #otp.fv-otp__code {
    margin-top: 0;
    font-size: 34px;
    color: var(--fv-white);
}


/* ========================================================================
   Merchants list and confirmations — 2026-09-29
   ======================================================================== */

/* --- Yes/no columns: Verified, PAX, TANGO -----------------------------
   Narrow and centred. The rest of the table is left-aligned so rows can be
   read down a common edge; a single icon has no edge to read down, and
   centred under a short heading it is easier to find. */
.fv-ui .data th.fv-col-flag,
.fv-ui .data td.fv-col-flag {
    width: 1%;
    text-align: center;
    white-space: nowrap;
}

.fv-ui .data td.fv-col-flag i {
    font-size: 16px;
}

/* --- The destructive button ------------------------------------------- */
.fv-ui .btn--danger {
    background: var(--fv-danger);
    color: var(--fv-white);
}

.fv-ui .btn--danger:hover {
    background: #B02A37;
    color: var(--fv-white);
}

.fv-ui .btn--danger:focus-visible {
    box-shadow: 0 0 0 3px rgba(220, 53, 69, .35);
}

/* --- A confirmation's body: an icon beside the question ----------------
   The icon says what kind of question it is before it is read: navy for an
   ordinary "are you sure", red for one that deletes or cannot be undone. */
.fv-ui .fv-confirm {
    display: flex;
    align-items: flex-start;
    gap: 16px;
}

.fv-ui .fv-confirm__icon {
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--fv-slate-100);
    color: var(--fv-navy);
    font-size: 16px;
}

.fv-ui .fv-confirm__icon--danger {
    background: var(--state-danger-bg);
    color: var(--fv-danger);
}

.fv-ui .fv-confirm p {
    margin: 0;
}

.fv-ui .fv-confirm > div {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

/* A message written with line breaks keeps them, as confirm() did. */
.fv-ui .fv-confirm__message {
    white-space: pre-line;
    padding-top: 8px;
}

/* --- A field that takes a whole row of a form grid --------------------- */
.fv-ui .fv-fields__full {
    grid-column: 1 / -1;
}

/* .input is sized for one line (44px tall); a textarea grows with its rows. */
.fv-ui textarea.input,
.fv-ui .fv-textarea {
    height: auto;
    min-height: 80px;
    padding: 10px 12px;
    line-height: 1.5;
    resize: vertical;
}

/* --- Compare merchant: one field per row, one system per column ------- */

/* The field name, pinned left, so on a narrow screen the system columns
   scroll under it and each value keeps its label in view. */
.fv-ui .fv-compare .fv-compare__field {
    position: sticky;
    left: 0;
    z-index: 1;
    width: 10%;
    min-width: 150px;
    white-space: nowrap;
    background: var(--surface-card);
    font-weight: var(--weight-bold);
    color: var(--text-heading);
    text-align: left;
    /* A heavier rule between the labels and the PAX / FvAuth / TANGO values.
       An inset shadow, not border-right: with border-collapse a sticky cell's
       border stays behind when the columns scroll under it. */
    box-shadow: inset -4px 0 0 var(--border-default);
}

/* The rule is drawn inside the cell, so keep the label clear of it. Matches
   table.data.data--dense's selector weight, which would otherwise win. */
.fv-ui table.data.data--dense.fv-compare .fv-compare__field {
    padding-right: 20px;
}

.fv-ui .fv-compare thead .fv-compare__field {
    background: inherit;
}

/* A row's field name is a label to read, not a column heading: sentence
   case at the same size as the values beside it, rather than the small
   capitals table.data gives th. */
.fv-ui .fv-compare tbody .fv-compare__field {
    font-size: 14px;
    text-transform: none;
    letter-spacing: normal;
}

/* table.data drops the rule under a table's last row. Here each group is its
   own tbody, so that dropped the rule between groups too; put it back on
   every group but the last. */
.fv-ui table.data.fv-compare tbody:not(:last-child) tr:last-child td {
    border-bottom: 1px solid var(--border-subtle);
}

/* Three system columns of equal width, so the same field lines up across
   PAX, FvAuth and TANGO and a difference is visible by eye. */
.fv-ui .fv-compare thead th:not(.fv-compare__field) {
    width: 30%;
}

/* A group's heading: a quiet band across the whole row, the section name in
   the same small capitals as the form legends. */
.fv-ui .fv-compare .fv-compare__group th {
    position: sticky;
    left: 0;
    padding-top: 18px;
    padding-bottom: 8px;
    background: var(--surface-subtle);
    font-size: 11px;
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    color: var(--text-heading);
    text-align: left;
}

/* ========================================================================
   Edit merchant — 2026-09-29
   ======================================================================== */

/* --- Tabs ----------------------------------------------------------------
   Bootstrap's tab script needs .nav, .nav-link and the data attributes; the
   look is all here. An underline for the open tab, in navy, on a rule that
   runs the width of the page, rather than Bootstrap's boxed folder tabs. */
.fv-ui .fv-tabs {
    display: flex;
    flex-wrap: nowrap;
    gap: 4px;
    margin: 0 0 24px;
    padding: 0;
    border-bottom: 1px solid var(--border-default);
    overflow-x: auto;              /* five tabs scroll on a phone rather than wrap */
    scrollbar-width: none;
}

.fv-ui .fv-tabs .nav-link {
    position: relative;
    padding: 12px 16px;
    border: none;
    border-radius: 0;
    background: transparent;
    font-family: inherit;
    font-size: 14px;
    font-weight: var(--weight-semibold);
    color: var(--text-muted);
    white-space: nowrap;
}

.fv-ui .fv-tabs .nav-link:hover {
    color: var(--text-heading);
}

.fv-ui .fv-tabs .nav-link.active {
    color: var(--text-heading);
}

.fv-ui .fv-tabs .nav-link.active::after {
    content: "";
    position: absolute;
    left: 12px;
    right: 12px;
    bottom: -1px;
    height: 3px;
    border-radius: 3px 3px 0 0;
    background: var(--fv-navy);
}

.fv-ui .fv-tabs .nav-link:focus-visible {
    outline: 2px solid var(--fv-navy);
    outline-offset: -2px;
    border-radius: 6px;
}

/* --- Form grid extras ---------------------------------------------------- */

/* Two columns' worth, for a field whose values run long (an address). */
.fv-ui .fv-fields__wide {
    grid-column: span 2;
}

@media (max-width: 520px) {
    .fv-ui .fv-fields__wide {
        grid-column: 1 / -1;
    }
}

.fv-ui .fv-fields--spaced {
    margin-top: 20px;
}

/* A small form whose submit button sits at the end of its row of fields. */
.fv-ui .fv-fields--with-action {
    align-items: start;
}

.fv-ui .fv-fields__action {
    align-self: start;
    padding-top: 27px;             /* the label's height, so the button lines up with the boxes */
}

/* A sub-heading inside one group: "Tip percentages" under Payment. */
.fv-ui .fv-subhead {
    margin: 24px 0 12px;
    font-size: 13px;
    font-weight: var(--weight-bold);
    color: var(--text-heading);
}

/* The legend style on a <div>: the fee-rule fragment's group is a div, not
   a fieldset, and a floated div is not cleared the way a legend is. */
.fv-ui div.fv-fieldset__legend {
    float: none;
}

/* The "Required" line the theme script shows over an unset colour. */
.fv-ui .fv-field-error {
    margin-bottom: 6px;
    font-size: 12px;
    font-weight: var(--weight-semibold);
    color: var(--fv-danger);
}

/* --- A value with its unit: $ 25.00, 15 % ------------------------------- */
.fv-ui .fv-affix {
    display: flex;
    align-items: stretch;
}

.fv-ui .fv-affix .input {
    flex: 1 1 auto;
    min-width: 0;
}

.fv-ui .fv-affix__unit {
    display: inline-flex;
    align-items: center;
    padding: 0 12px;
    border: 1px solid var(--border-default);
    background: var(--surface-subtle);
    font-size: 14px;
    font-weight: var(--weight-semibold);
    color: var(--text-muted);
}

.fv-ui .fv-affix__unit:first-child {
    border-right: none;
    border-radius: var(--radius-input) 0 0 var(--radius-input);
}

.fv-ui .fv-affix__unit:last-child {
    border-left: none;
    border-radius: 0 var(--radius-input) var(--radius-input) 0;
}

.fv-ui .fv-affix__unit:first-child + .input {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
}

.fv-ui .fv-affix .input:first-child {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

/* --- A secret with a show/hide button inside the box --------------------- */
.fv-ui .fv-secret {
    position: relative;
}

.fv-ui .fv-secret .input {
    padding-right: 44px;
    letter-spacing: .2em;
}

.fv-ui .fv-secret__toggle {
    position: absolute;
    top: 50%;
    right: 6px;
    transform: translateY(-50%);
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
}

.fv-ui .fv-secret__toggle:hover {
    background: var(--fv-slate-100);
    color: var(--text-heading);
}

/* --- Theme: colour swatches and logos ---------------------------------- */

/* style.css sizes the swatch and its "Select" overlay at 38px; keep them the
   same height as each other, with the design system's corners. */
.fv-ui .color-picker-wrapper input[type="color"] {
    border-radius: var(--radius-input);
    border-color: var(--border-default);
}

.fv-ui .fv-logo-preview {
    display: inline-flex;
    margin-top: 10px;
    padding: 10px 14px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--surface-card);
}

/* The dark logo is drawn for a dark background; show it on one. */
.fv-ui .fv-logo-preview--dark {
    background: var(--fv-navy);
    border-color: var(--fv-navy);
}

.fv-ui .fv-logo-preview img {
    max-height: 48px;
    width: auto;
}

/* --- Fee structure ------------------------------------------------------ */
.fv-ui .fv-profile-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 16px 24px;
}

.fv-ui .fv-profile-head .fv-field-hint {
    max-width: 60ch;
}

.fv-ui .fv-profile-head form {
    margin: 0;
}

.fv-ui .fv-num {
    font-variant-numeric: tabular-nums;
}

.fv-ui .fv-table-empty {
    padding: 28px 16px;
    text-align: center;
    color: var(--text-muted);
}

/* Tiers in a tiered rule: one per line, with a small remove button. */
.fv-ui .fv-tier {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    font-variant-numeric: tabular-nums;
}

.fv-ui .fv-tier form {
    display: inline;
    margin: 0;
}

.fv-ui .fv-tier__remove {
    width: 22px;
    height: 22px;
    border: none;
    border-radius: 4px;
    background: transparent;
    color: var(--fv-danger);
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
}

.fv-ui .fv-tier__remove:hover {
    background: var(--state-danger-bg);
}

.fv-ui .fv-tier-add {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 8px;
}

.fv-ui .input.input--sm {
    height: 32px;
    padding: 0 8px;
    font-size: 13px;
}

/* --- Dashboard login ---------------------------------------------------- */

/* The one-time password: the one thing on the tab that must not be missed. */
.fv-ui .fv-issued {
    padding: 20px 24px;
    border: 1px solid var(--fv-gold);
    border-left-width: 4px;
    border-radius: var(--radius-card);
    background: #FFFBEA;
    color: var(--text-body);
}

.fv-ui .fv-issued__title {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 8px;
    font-size: 16px;
    font-weight: var(--weight-bold);
    color: var(--text-heading);
}

.fv-ui .fv-issued p {
    margin: 0 0 12px;
    max-width: 70ch;
    font-size: 14px;
}

.fv-ui .fv-issued__code {
    display: inline-block;
    padding: 10px 16px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    background: var(--surface-card);
    color: var(--text-heading);
    font-family: inherit;
    font-size: 18px;
    letter-spacing: .05em;
    user-select: all;              /* one click selects the whole password for copying */
}

.fv-ui .fv-card-intro {
    max-width: 70ch;
    margin: 0 0 20px;
    font-size: 14px;
    color: var(--text-muted);
}

.fv-ui .fv-table-note {
    margin: 0;
    padding: 14px 20px;
    border-top: 1px solid var(--border-subtle);
    font-size: 13px;
    color: var(--text-muted);
}

/* Delete in red text on an outline button: destructive, but not loud until
   it is chosen. The confirmation it opens has the solid red button. */
.fv-ui .btn.fv-btn-danger-text {
    color: var(--fv-danger);
}

.fv-ui .btn.fv-btn-danger-text:hover {
    background: var(--state-danger-bg);
    border-color: var(--fv-danger);
}

.fv-ui .fv-row-actions form {
    margin: 0;
}

/* --- Action bars with nothing in them ----------------------------------
   The Edit bars hold only role-restricted buttons. For someone without the
   role the bar would be an empty white box; hide it instead. */
.fv-ui .fv-actionbar:not(:has(button)) {
    display: none;
}

.fv-ui .fv-modal__body--center {
    align-items: center;
}

/* style.css frames every .tab-content in a grey 2px box with 1rem padding
   and overflow:hidden, for the old folder tabs. Here the tabs are an
   underline and each group is its own card, so the frame goes; and
   overflow:hidden would clip the sticky Action column and open menus. */
.fv-ui .tab-content.fv-tab-panels {
    border: none;
    border-radius: 0;
    padding: 0;
    overflow: visible;
}

/* style.css gives every form inside .tab-content width:100% and
   overflow-x:hidden. A form that is a whole tab is unaffected, but the
   small inline ones inside table cells (Reset password, Delete, the tier
   add and remove) were cut off at the edge of their cell. */
.fv-ui .fv-tab-panels form {
    width: auto;
    max-width: none;
    overflow: visible;
}

/* A tier stays on one line. Its add form stacks its three boxes and button
   in a narrow column: side by side they made the rules table wider than
   the card, and the pinned Action column then covered them. The inline
   widths on the inputs are for the Fee template page's layout; here each
   box takes the column's width. */
.fv-ui .fv-tier {
    white-space: nowrap;
}

/* form.fv-tier-add, to outrank the width:auto given to tab forms above. */
.fv-ui .fv-tab-panels form.fv-tier-add,
.fv-ui .fv-tier-add {
    flex-direction: column;
    align-items: stretch;
    width: 150px;
}

.fv-ui .fv-tier-add .input {
    width: 100% !important;
}

/* The fee-rules table has nine columns. Letting the long headings wrap
   ("Min/Max (cents)", "Card network") keeps it inside the card, so the
   pinned Action column does not sit over the Tiers column. */
.fv-ui table.data.fv-fee-rules th {
    white-space: normal;
    min-width: 64px;
    vertical-align: bottom;
}


/* ========================================================================
   Fixes from review — 2026-09-30
   ======================================================================== */

/* --- An open More menu sits above the rows below it --------------------
   Each Action cell is position: sticky with z-index 1, so each is its own
   layer, and a later row's layer paints over an earlier one. An open menu
   lives inside its row's cell, so the next row's Action cell — white, with
   its buttons — covered most of it: only "MC" of "More actions" showed,
   and the next row's buttons sat on top of the menu. Lifting the cell
   whose menu is open puts the whole menu on top. */
.fv-ui table.data .fv-col-actions:has(.dropdown-menu.show) {
    z-index: 5;
}

/* --- Icons that are the whole content of a cell or button --------------
   icon--muted and icon--danger carry an 8px right margin, for an icon set
   before a word. Alone and centred, that margin pushed the icon 4px left:
   the grey ✕ in Verified, PAX and TANGO sat left of the green ✓ above it,
   and the trash icons were off-centre in their buttons. */
.fv-ui td.fv-col-flag i,
.fv-ui .fv-icon-btn i {
    margin: 0;
}

/* --- Forms inside table cells ------------------------------------------
   style.css gives some forms width: 100% and overflow-x: hidden (every
   form inside .tab-content, for one). Inside a table cell that cut a
   button off at the cell's edge — Reset password showed as "Reset passwo".
   Set on the cell's forms directly, and firmly, so no page-level rule can
   squeeze them again. */
.fv-ui table.data td form {
    width: auto !important;
    max-width: none !important;
    overflow: visible !important;
}

/* --- Tab focus ------------------------------------------------------------
   Bootstrap adds a blue glow to a focused nav-link on top of the outline
   above; together they drew a heavy double frame round the tab. One
   outline, and only when focus came from the keyboard. */
.fv-ui .fv-tabs .nav-link:focus {
    box-shadow: none;
}

.fv-ui .fv-tabs .nav-link:focus:not(:focus-visible) {
    outline: none;
}

.fv-ui .fv-tabs .nav-link:focus-visible {
    outline: 2px solid var(--fv-navy);
    outline-offset: -4px;
    border-radius: 8px;
}

/* --- A badge at the start of a hint (FVLink passwords) ----------------- */
.fv-ui .fv-field-hint .badge {
    margin-right: 6px;
    vertical-align: 1px;
}

/* --- No vertical scrollbar inside a table ------------------------------
   overflow-x: auto on .table-wrap switches on vertical scrolling too (a
   box cannot scroll on one axis and be visible on the other). The wrap
   grows to its table, so it never needs to scroll down — but when the
   table is a fractional height (1448.5px) the wrap rounds to 1449 and
   reports half a pixel to scroll, and a second scrollbar appears inside
   the page's own. Only sideways scrolling is wanted here. Open More menus
   are unaffected: they are positioned fixed, outside the wrap. */
.fv-ui .table-wrap {
    overflow-y: hidden;
}


/* ========================================================================
   Parameter and security files — 2026-09-30
   ======================================================================== */

/* Text that says a value is absent ("Not uploaded"): quiet, not italic. */
.fv-ui .fv-muted {
    color: var(--text-muted);
}

/* Status badge and its download button side by side. The script writes
   the same wrapper back after an upload. */
.fv-ui .fv-status-cell {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

/* Bootstrap's small controls in a table row: the file picker keeps
   form-control because the script marks it is-valid / is-invalid, which
   are Bootstrap's states. Given the design system's border and corners so
   they sit with the rest of the page. */
.fv-ui .fv-files .form-control {
    border-color: var(--border-default);
    border-radius: var(--radius-input);
    font-size: 13px;
    color: var(--text-body);
}

.fv-ui .fv-files .form-control:focus {
    border-color: var(--fv-navy);
    box-shadow: 0 0 0 3px rgba(15, 23, 42, .12);
}

.fv-ui .fv-files .form-control.is-valid {
    border-color: var(--fv-success);
}

.fv-ui .fv-files .form-control.is-invalid {
    border-color: var(--fv-danger);
}

/* Wide enough for the picker's button and a file name; the error, when
   shown, wraps under it rather than widening the column. */
.fv-ui .fv-upload-cell {
    min-width: 230px;
}

.fv-ui .fv-upload-cell .filename-error {
    display: block;
    margin: 6px 0 0;
    white-space: normal;
}

.fv-ui .fv-upload-cell .filename-error.d-none {
    display: none;
}

.fv-ui .fv-notes-input {
    min-width: 160px;
}

/* While uploading, the button shows only its spinner: icon and spinner
   together do not fit a 32px button. */
.fv-ui .single-upload-btn:has(.spinner-border:not(.d-none)) > i {
    display: none;
}

.fv-ui #inlineError {
    margin-bottom: 16px;
}

/* A badge that shows a name the system uses — a folder, a path — keeps its
   exact case. The badge style capitalises its first letter, which turned
   the folder config into "Config", a folder that does not exist. */
.fv-ui .badge.fv-mono {
    text-transform: none;
}


/* ========================================================================
   Bin Files and Firmware — 2026-09-30
   ======================================================================== */

/* --- The drop area in Upload bin file ----------------------------------
   The page script clicks the hidden file input when this is clicked, and
   while a file is dragged over it or chosen it sets the border and
   background inline — those inline colours win over these, as intended. */
.fv-ui .fv-dropzone {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 28px 16px;
    border: 2px dashed var(--border-default);
    border-radius: var(--radius-card);
    background: var(--surface-subtle);
    text-align: center;
    cursor: pointer;
    transition: border-color var(--motion-fast) var(--ease),
                background var(--motion-fast) var(--ease);
}

.fv-ui .fv-dropzone:hover {
    border-color: var(--fv-navy);
}

.fv-ui .fv-dropzone__icon {
    font-size: 28px;
    color: var(--fv-navy);
}

.fv-ui .fv-dropzone__title {
    margin: 0;
    font-size: 14px;
    font-weight: var(--weight-semibold);
    color: var(--text-heading);
}

.fv-ui .fv-dropzone__hint {
    margin: 0;
    font-size: 12px;
    color: var(--text-muted);
}

/* "Selected: bins_ca.csv", written by the script. */
.fv-ui .fv-dropzone__chosen:not(:empty) {
    margin-top: 10px;
    font-size: 13px;
    color: var(--text-heading);
}

.fv-ui .fv-dropzone__ready {
    margin-top: 4px;
    font-size: 13px;
    font-weight: var(--weight-semibold);
    color: var(--fv-success);
}

/* --- Inputs that are not locked, though the browser calls them read-only --
   style.css has a global rule, input:read-only and input[readonly], that
   paints a grey background with !important and fades the field to 60%.
   Two kinds of input are caught by it without being locked:

     - date pickers: flatpickr sets readonly on its box so a click opens the
       calendar instead of the keyboard;
     - file pickers, checkboxes, radios and colour swatches: the browser
       counts every input that has no text to edit as :read-only, so the
       Choose File boxes were faded too.

   Both stay white and at full strength here. A field that is truly locked
   (the readonly attribute on a text box, or disabled) keeps the grey. The
   !important is only to answer the one in style.css. */
.fv-ui .input.flatpickr-input[readonly] {
    background-color: var(--surface-card) !important;
    border-color: var(--border-default) !important;
    color: var(--text-body) !important;
    opacity: 1;
    cursor: pointer;
}

.fv-ui input:read-only:not([readonly]):not(:disabled) {
    background-color: var(--surface-card) !important;
    color: var(--text-body) !important;
    opacity: 1;
}

/* The same rule set every border grey with !important, so the file
   picker's green (right name) and red (wrong name) never showed. */
.fv-ui .fv-files .form-control.is-valid {
    border-color: var(--fv-success) !important;
}

.fv-ui .fv-files .form-control.is-invalid {
    border-color: var(--fv-danger) !important;
}


/* ========================================================================
   API Keys — 2026-09-30
   ======================================================================== */

/* The masked key with its Copy and Show buttons, as one control. The
   buttons are not .btn: copyToken() adds and removes Bootstrap's
   btn-success / btn-outline-secondary on them, which only take effect on
   a .btn, so its flash of green does not fight this style. */
.fv-ui .fv-key {
    display: inline-flex;
    align-items: stretch;
    height: 32px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-input);
    background: var(--surface-card);
    overflow: hidden;
}

.fv-ui .fv-key__value {
    width: 150px;
    padding: 0 10px;
    border: none;
    background: transparent !important;
    font-family: inherit;
    font-size: 12px;
    color: var(--text-body) !important;
    opacity: 1;
}

.fv-ui .fv-key__value:focus {
    outline: none;
}

.fv-ui .fv-key__btn {
    width: 32px;
    border: none;
    border-left: 1px solid var(--border-subtle);
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
}

.fv-ui .fv-key__btn:hover {
    background: var(--surface-subtle);
    color: var(--text-heading);
}

.fv-ui .fv-key__btn:focus-visible {
    outline: 2px solid var(--fv-navy);
    outline-offset: -2px;
}

.fv-ui .fv-key__btn .text-success {
    color: var(--fv-success) !important;
}

/* Fitting nine columns in the card: headings may wrap, an email may break
   anywhere (it has no spaces to break at), and a date takes two short
   lines — date, then time — instead of one long one. */
.fv-ui table.data.fv-api-keys th {
    white-space: normal;
    vertical-align: bottom;
}

.fv-ui table.data td.fv-email {
    min-width: 140px;
    white-space: normal;
    overflow-wrap: anywhere;
}

.fv-ui table.data td.fv-when > span:not(.fv-muted) {
    display: block;
    font-variant-numeric: tabular-nums;
}

.fv-ui .fv-when__time {
    font-size: 12px;
    color: var(--text-muted);
}

.fv-ui .fv-api-keys .fv-key__value {
    width: 90px;
}

.fv-ui table.data.data--dense.fv-api-keys th,
.fv-ui table.data.data--dense.fv-api-keys td {
    padding-left: 12px;
    padding-right: 12px;
}


/* ========================================================================
   Config, Trust Bundles, Audit Logs — 2026-09-30
   ======================================================================== */

/* A URL in a table cell: allowed to break at any character
   so a long base URL wraps instead of widening the table. */
.fv-ui table.data td.fv-url {
    max-width: 320px;
    white-space: normal;
    overflow-wrap: anywhere;
}

/* --- Trust Bundles ------------------------------------------------------ */
.fv-ui table.data .fv-col-toggle {
    width: 1%;
    padding-right: 0;
}

.fv-ui .fv-toggle {
    width: 28px;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
}

.fv-ui .fv-toggle:hover {
    background: var(--fv-slate-100);
    color: var(--text-heading);
}

.fv-ui .fv-toggle:focus-visible {
    outline: 2px solid var(--fv-navy);
    outline-offset: 1px;
}

.fv-ui .fv-tb-name {
    display: flex;
    align-items: flex-start;
    gap: 10px;
}

.fv-ui .fv-tb-name__icon {
    margin-top: 3px;
    color: var(--text-muted);
}

.fv-ui .fv-tb-name__file {
    margin-top: 2px;
    font-size: 12px;
    color: var(--text-muted);
}

.fv-ui .fv-tb-upload {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
}

.fv-ui .fv-tb-upload .form-control {
    max-width: 240px;
    border-color: var(--border-default);
    border-radius: var(--radius-input);
    font-size: 13px;
}

/* The detail row: a quiet panel under its file, set in from the edges. */
.fv-ui table.data tr.fv-tb-detail > td {
    padding: 0;
    white-space: normal;
    background: var(--surface-subtle);
}

.fv-ui .fv-tb-detail__inner {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 16px 20px 20px 56px;
}

.fv-ui .fv-tb-detail__desc {
    margin: 0;
    max-width: 75ch;
    font-size: 14px;
    color: var(--text-body);
}

.fv-ui .fv-tb-certs {
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--surface-card);
}

.fv-ui .fv-tb-detail__meta {
    margin: 0;
    font-size: 12px;
    color: var(--text-muted);
    overflow-wrap: anywhere;
}

/* A certificate past its end date. */
.fv-ui table.data td.fv-expired {
    color: var(--fv-danger);
    font-weight: var(--weight-bold);
}

/* --- Audit Logs ----------------------------------------------------------- */
.fv-ui .fv-audit-filters {
    display: grid;
    grid-template-columns: repeat(4, minmax(150px, 1fr));
    gap: 12px;
    align-items: end;
}

.fv-ui .fv-audit-date {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin: 0;
    font-size: 12px;
    font-weight: var(--weight-semibold);
    color: var(--text-muted);
}

.fv-ui .fv-audit-filters__actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
}

@media (max-width: 1100px) {
    .fv-ui .fv-audit-filters {
        grid-template-columns: repeat(2, minmax(150px, 1fr));
    }
}

@media (max-width: 520px) {
    .fv-ui .fv-audit-filters {
        grid-template-columns: 1fr;
    }
}

/* One entry per list item: what happened and when on the first line, the
   message under it, then the who/where/how-long grid. */
.fv-ui .fv-audit-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.fv-ui .fv-audit {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 16px 20px;
    border-bottom: 1px solid var(--border-subtle);
}

.fv-ui .fv-audit__head {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

.fv-ui .fv-audit__icon {
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--fv-slate-100);
    color: var(--fv-navy);
    font-size: 14px;
}

.fv-ui .fv-audit__title {
    display: flex;
    flex-direction: column;
    margin-right: auto;
}

.fv-ui .fv-audit__action {
    font-size: 14px;
    font-weight: var(--weight-bold);
    color: var(--text-heading);
}

.fv-ui .fv-audit__when {
    font-size: 12px;
    color: var(--text-muted);
}

.fv-ui .fv-audit__message {
    margin: 0;
    font-size: 14px;
    color: var(--text-body);
    overflow-wrap: anywhere;
}

.fv-ui .fv-audit__grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 8px 20px;
    margin: 0;
}

.fv-ui .fv-audit__grid dt {
    font-size: 11px;
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    color: var(--text-muted);
}

.fv-ui .fv-audit__grid dd {
    margin: 0;
    font-size: 13px;
    color: var(--text-heading);
    overflow-wrap: anywhere;
}

.fv-ui .fv-audit__more summary {
    width: fit-content;
    font-size: 13px;
    font-weight: var(--weight-semibold);
    color: var(--fv-navy);
    cursor: pointer;
}

.fv-ui .fv-audit__more[open] summary {
    margin-bottom: 10px;
}

.fv-ui .fv-audit__code {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 12px;
    margin-top: 10px;
}

.fv-ui .fv-audit__code-label {
    margin-bottom: 4px;
    font-size: 12px;
    font-weight: var(--weight-semibold);
    color: var(--text-muted);
}

.fv-ui .fv-audit__code pre {
    margin: 0;
    max-height: 220px;
    padding: 10px 12px;
    overflow: auto;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--surface-subtle);
    font-family: inherit;
    font-size: 12px;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.fv-ui .fv-audit__exception {
    color: var(--fv-danger);
}

.fv-ui .fv-audit-foot {
    justify-content: center;
}

.fv-ui .fv-audit-foot form {
    margin: 0;
}


/* ========================================================================
   In-page alerts — 2026-09-30
   ------------------------------------------------------------------------
   The error and warning boxes inside dialogs and on pages: a rejected Add
   terminal, a failed upload, the Config dialogs' messages. Their markup is
   Bootstrap's (.alert .alert-danger …) because the scripts that show them
   toggle d-none and fill .error-text / .danger-text; only the look changes
   here: the tinted panel with a coloured edge that page notices use, instead
   of Bootstrap's filled boxes.

   The top-right notices are also .alert, but fragments/message.html styles
   them under #globalMessageContainer, which outranks these.
   ======================================================================== */
/* A row: the icon beside the text, so a list of errors sits next to its
   icon rather than below it. d-none still hides it, since Bootstrap's is
   !important. */
.fv-ui .alert {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin: 0;
    padding: 12px 14px;
    border: 1px solid transparent;
    border-left-width: 3px;
    border-radius: var(--radius-md);
    font-size: 14px;
    font-weight: var(--weight-regular, 400);
    line-height: 1.5;
    color: var(--text-heading);
    box-shadow: none;
}

.fv-ui .alert > i {
    flex: 0 0 auto;
    margin-top: 3px;
}

/* The gap spaces the icon; the markup's me-2 would double it. */
.fv-ui .alert > i.me-2 {
    margin-right: 0 !important;
}

.fv-ui .alert-danger {
    background: var(--state-danger-bg);
    border-color: rgba(220, 53, 69, .22);
    border-left-color: var(--fv-danger);
}

.fv-ui .alert-danger > i {
    color: var(--fv-danger);
}

.fv-ui .alert-warning {
    background: var(--state-warning-bg);
    border-color: rgba(138, 109, 0, .2);
    border-left-color: var(--fv-warning);
}

.fv-ui .alert-warning > i {
    color: var(--fv-warning);
}

.fv-ui .alert-success {
    background: var(--state-success-bg);
    border-color: rgba(25, 135, 84, .2);
    border-left-color: var(--fv-success);
}

.fv-ui .alert-success > i {
    color: var(--fv-success);
}

.fv-ui .alert-info {
    background: var(--fv-slate-100);
    border-color: var(--border-subtle);
    border-left-color: var(--fv-navy);
}

.fv-ui .alert-info > i {
    color: var(--fv-navy);
}

/* A list of validation errors (the Add dialogs' .danger-text). */
.fv-ui .alert ul {
    margin: 0;
    padding-left: 18px;
}

/* Room for the close button on the dismissible ones. */
.fv-ui .alert-dismissible {
    padding-right: 40px;
}

.fv-ui .alert-dismissible .btn-close {
    top: 50%;
    right: 10px;
    padding: 6px;
    transform: translateY(-50%);
    background-size: 10px;
}
