/* ══════════════════════════════════════════════════════════════════════
   VIP — Shared application shell
   The visual "marriage" layer between the landing page (login + cockpit
   chooser) and the Cockpit SPA. It supplies one account top bar so both
   surfaces read as a single application, plus the layout offsets and the
   refreshed sidebar styling the cockpit needs.

   Scope:
     · The .topbar component matches the landing chooser's bar (assets/
       login.css), reusing the exact .topbar/.brand/.who markup the landing
       controller (assets/app.js) already emits, so loading this file on the
       cockpit yields a pixel-identical bar without pulling in login.css.
     · Cockpit-only rules (layout offsets, sidebar refresh) are scoped under
       body.vip-cockpit so they never affect the landing page.

   Brand palette (mirrors assets/login.css):
     Navy #25273A · Navy-70 #696975 · Yellow #FFDF00 · Steel #79828C · White
   ══════════════════════════════════════════════════════════════════════ */

:root {
    /* Single source of truth for the shared chrome. Kept independent of both
       login.css (--navy …) and the cockpit's root_t5.00j.css (--primary_100 …)
       so this file is self-contained wherever it is loaded. */
    --vip-navy:     #25273A;
    --vip-navy-10:  #E8E8EB;   /* hairline borders, light fills, chip surfaces */
    --vip-navy-20:  #D2D2D6;   /* column separators, control borders */
    --vip-navy-30:  #BDBEC4;
    --vip-navy-40:  #A7A8B0;
    --vip-navy-50:  #91929C;
    --vip-navy-60:  #7D7D88;
    --vip-navy-70:  #696975;
    --vip-navy-80:  #535362;
    --vip-navy-90:  #3D3D4E;
    --vip-navy-100: #25273A;
    --vip-yellow:   #FFDF00;
    --vip-yellow-d: #F2D200;   /* pressed/hover state of the yellow primary */
    --vip-steel:    #79828C;
    --vip-white:    #FFFFFF;
    --vip-muted:    #79828C;   /* light text on navy (e.g. context label) */

    /* Typography. Single source of truth for both surfaces: the landing page
       (assets/login.css) and the cockpit (styles_website.css) both resolve their
       body font from these tokens, so the shared chrome renders identically.
       Numeric columns rely on tabular figures, applied once at body level.

       Per the Savills brand guideline, Gotham is the primary corporate
       typeface but requires a licence VIP does not currently hold; Arial is
       the guideline's own licence-free substitute, so it is used here rather
       than a system-UI stack. Domine is the guideline's sanctioned digital
       transition substitute for the Playfair Display headline serif — see
       the @font-face declaration below. */
    --vip-font-stack: 'Arial', Helvetica, sans-serif;
    --vip-font-mono:  'SFMono-Regular', Consolas, monospace;
    --vip-font-voice: 'Domine', Georgia, serif;

    /* Accent and status colours. Shared with the landing page (assets/login.css)
       so links, validation messages and focus rings read identically on both
       surfaces. */
    --vip-teal:     #238291;
    --vip-red:      #CE181E;
    --vip-green:    #2E7D32;

    /* Washed-out tints of the status colours, used as message backgrounds. */
    --vip-tint-red:   #FCE8E8;
    --vip-tint-green: #ECF6EC;

    /* Standard focus ring: a soft navy halo around form controls. */
    --vip-focus-ring: 0 0 0 3px rgba(37, 39, 58, .12);

    /* Surface tints used by the re-skinned boxes and datatables. */
    --vip-cream-50: #F5F2F0;   /* warm light surface (resting heads, zebra) */
    --vip-cream-25: #FAF8F6;   /* weaker cream tint (tenant group headers) */
    --vip-row-sel:  #FCF4B8;   /* soft yellow row selection */

    /* Height of the account bar. Drives both the bar and the cockpit layout
       offsets below — change in one place. */
    --vip-topbar-h: 56px;
    /* Inset of the "savills" wordmark within the logo artwork; puts the yellow
       claim on the wordmark's baseline in the brand lock-up. */
    --vip-brand-baseline: 2px;
    --vip-subsidebar-w: 240px;
    /* Two-tier tint: the navigation band at the top sits on a darker grey,
       the settings zone below on a slightly lighter one, so the sub-nav
       reads as the continuation of the main navigation while the controls
       read as the tool area. */
    --vip-subsidebar-bg: #F0F2F5;
    --vip-subnav-bg:     #DDE1E7;

    /* Width of the cockpit's fixed sidebar (mirrors the legacy
       sidebar-main_t5.00j.css column). Drives .bar-left so the context
       breadcrumb begins exactly at the sidebar's right edge. Adjust here if the
       legacy column width ever changes. */
    --vip-sidebar-w: 240px;

    /* Overlay stacking — the layer is regulated dynamically by an open-order stack
       (system_t5_00j.js), so the most recently opened overlay always sits on top and
       teardown is LIFO. These tokens parametrise that stack: --z-overlay is the base
       of the layer and --z-overlay-step the gap between successive overlays; the JS
       stamps z-index = base + position*step while an overlay is open. --z-overlay-close
       keeps the close chip above the card content within each overlay. */
    --z-overlay:       1035;   /* base of the overlay layer */
    --z-overlay-step:  2;      /* gap between stacked overlays */
    --z-overlay-close: 1599;   /* close chip, above inline gallery */
}

/* Domine (self-hosted, OFL licensed) — the brand guideline's sanctioned
   digital transition substitute for the Playfair Display headline serif.
   A single variable-font file covers weight 400–700, so both Regular and
   Bold headline treatments load from the one asset. Path is relative to
   this file's own location (assets/vip-shell.css); adjust if the font
   asset is placed elsewhere on the FTP host. */
@font-face {
    font-family: 'Domine';
    src: url('fonts/domine-variablefont_wght.woff2') format('woff2-variations'),
         url('fonts/domine-variablefont_wght.woff2') format('woff2');
    font-weight: 400 700;
    font-style: normal;
    font-display: swap;
}

/* ══════════════════════════════════════════════════════════════════════
   ACCOUNT TOP BAR  (shared by the landing chooser and the cockpit)
   ══════════════════════════════════════════════════════════════════════ */
.topbar {
    height: var(--vip-topbar-h);
    background: var(--vip-navy);
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 22px;
    flex-shrink: 0;
    /* Sit above page content but below every full-screen overlay/popup/modal
       (≥ 998), so those keep covering the whole viewport including this bar. */
    position: relative;
    z-index: 900;
}

/* Left cluster (cockpit): brand + navigation toggle. On the landing the bar
   has .brand directly, so .bar-left simply never appears there. */
.topbar .bar-left { display: flex; align-items: center; gap: 14px; }

.topbar .nav-toggle {
    appearance: none;
    border: 0;
    background: transparent;
    color: var(--vip-white);
    font-size: 14px;    
    line-height: 1;
    width: 20px;
    height: 36px;
    border-radius: 6px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background .18s ease;
}
.topbar .nav-toggle:hover { background: rgba(255, 255, 255, .10); }

/* Brand lock-up: the claim continues the "savills" wordmark on its own baseline,
   with the product name set quietly above it. Bottom alignment plus the baseline
   inset (matching the wordmark's inset within the logo artwork) put the yellow
   claim on the same optical footing as "savills"; tune --vip-brand-baseline if
   the logo asset changes. Defined under .topbar so it is independent of
   login.css's card styling. */
.topbar .brand {
    display: flex;
    align-items: flex-end;
    gap: 9px;
}
.topbar .brand .logo { height: 40px; width: auto; display: block; }
.topbar .brand-text {
    display: block;
    line-height: 1;
    padding-bottom: var(--vip-brand-baseline, 3px);
    white-space: nowrap;
}
/* The product name sits one step smaller and quieter than the claim (dimmed
   white, regular weight), so the eye lands on the yellow claim first. */
.topbar .brand-text .tool-name {
    display: block;
    font-family: var(--vip-font-voice);
    font-size: 10px;
    font-weight: 400;
    letter-spacing: .02em;
    color: rgba(255, 255, 255, .78);
    margin-bottom: 3px;
}
.topbar .brand-text .tool-tagline {
    display: block;
    font-family: var(--vip-font-voice);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: .01em;
    color: var(--vip-yellow);
}

.topbar .who { display: flex; align-items: center; gap: 10px; }

/* Landing chooser context label (which cockpit): truncates rather than wrapping so
   the bar height stays fixed. Carries .instruction_title too, so the engine keeps it
   in sync (incl. its instruction-name fallback). The cockpit shows the project in
   the brand lock-up instead (see below). */
.topbar .who .context {
    font-size: 12px;
    color: var(--vip-muted);
    max-width: 32ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The landing's user e-mail (chooser) uses the same muted treatment. */
.topbar .who .email { font-size: 12px; color: var(--vip-muted); }

/* Thin separator between groups of controls in the right-hand .who cluster. */
.topbar .who .sep {
    width: 1px;
    height: 22px;
    background: var(--vip-navy-70);
    margin: 0 2px;
}

/* Account icon actions: contacts, settings (cockpit), in the right-hand .who cluster. */
.topbar .who .icon-btn {
    appearance: none;
    border: 0;
    background: transparent;
    color: var(--vip-white);
    font-size: 15px;
    width: 34px;
    height: 34px;
    border-radius: 6px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background .18s ease;
}
.topbar .who .icon-btn:hover { background: rgba(255, 255, 255, .10); }

/* "Back to cockpits" (cockpit) and "Sign out" (both) share one outlined
   ghost-button style so the right-hand controls read as a set. */
.topbar .who .back,
.topbar .who .logout {
    font-size: 12px;
    font-weight: 600;
    color: var(--vip-white);
    background: transparent;
    border: 1px solid rgba(255, 255, 255, .35);
    border-radius: 6px;
    padding: 7px 12px;
    cursor: pointer;
    text-decoration: none;
    line-height: 1;
    white-space: nowrap;
    transition: background .18s ease, border-color .18s ease;
}
.topbar .who .back:hover,
.topbar .who .logout:hover {
    background: rgba(255, 255, 255, .10);
    border-color: var(--vip-white);
    text-decoration: none;
}
.topbar .who .logout:disabled { opacity: .6; cursor: default; }

/* ══════════════════════════════════════════════════════════════════════
   COCKPIT LAYOUT OFFSETS  (cockpit only — scoped under body.vip-cockpit)
   Seats the account bar above the cockpit's own fixed 260px sidebar.
   ══════════════════════════════════════════════════════════════════════ */
body.vip-cockpit { margin: 0; }

/* The shell fills whatever height the account bar leaves. */
body.vip-cockpit .page-wrapper {
    height: calc(100vh - var(--vip-topbar-h));
}

/* The fixed sidebar starts below the bar (height is viewport-relative). */
body.vip-cockpit #sidebar.sidebar-wrapper {
    top: var(--vip-topbar-h);
    height: calc(100% - var(--vip-topbar-h));
    max-height: calc(100% - var(--vip-topbar-h));
}

/* The sidebar's navigation anchors carry no href (avoids the browser's
   status-bar URL preview) and act as buttons; restore the pointer
   affordance they lose without an href. */
body.vip-cockpit #sidebar a[role="button"] { cursor: pointer; }

/* The floating "show sidebar" tab is retired — the top-bar toggle replaces it.
   The element stays in the DOM (the engine triggers its click to open the
   sidebar), so it is hidden rather than removed. */
body.vip-cockpit #show-sidebar { display: none !important; }

/* Initial branded boot loader: pin it to the viewport so it overlays the bar
   during load and never adds a block to the document flow. */
body.vip-cockpit #loader {
    position: fixed;
    inset: 0;
    width: auto;
    height: auto;
    z-index: 1100;
}

/* ══════════════════════════════════════════════════════════════════════
   COCKPIT TOPBAR — brand lock-up + portfolio context  (cockpit only)
   The top bar carries three clusters: .bar-left (brand lock-up + toggle),
   .bar-context (the current portfolio, sitting beyond the sidebar seam whose
   hairline doubles as the divider), and .who (cockpit tools + session actions)
   pushed to the far right. The shared base bar keeps space-between for the
   landing, so these overrides are scoped to the cockpit body.
   ══════════════════════════════════════════════════════════════════════ */
body.vip-cockpit .topbar {
    justify-content: flex-start;
    padding-left: 0;            /* the inset now lives inside .bar-left (see below) */
}

/* Left cluster spans the sidebar column (matching .sidebar-content), so its right
   edge meets the content area. The 22px the base bar applied as left padding is
   moved inside, keeping the brand in place. A hairline rule marks that edge,
   echoing the brand-text divider. */
body.vip-cockpit .bar-left {
    flex: 0 0 auto;
    box-sizing: border-box;
    width: var(--vip-sidebar-w);
    padding-left: 12px;
    border-right: 1px solid var(--vip-navy-70);
}

/* The navigation toggle is pushed to the right of the brand column so it rests
   on the seam between the sidebar and the content — reading as the handle that
   collapses or reveals the column beside it. margin-left:auto absorbs the slack
   regardless of the brand's width; the button box keeps the chevron clear of the
   hairline edge, so no extra inset is needed. */
body.vip-cockpit .bar-left .nav-toggle {
    margin-left: auto;
    outline: none;
}

/* Portfolio context: the current cockpit's name, single line by design (names are
   kept short; no ellipsis wanted). Carries .instruction_title so the engine keeps
   the text in sync. */
body.vip-cockpit .bar-context {
    display: flex;
    align-items: center;
    padding-left: 18px;
    min-width: 0;
}
body.vip-cockpit .bar-context .tool-project {
    color: var(--vip-white);
    font-size: 15px;
    font-weight: 600;
    letter-spacing: .3px;
    white-space: nowrap;
}

/* Account cluster hugs the far right. */
body.vip-cockpit .who { margin-left: auto; }

/* ══════════════════════════════════════════════════════════════════════
   COCKPIT SIDEBAR — refreshed navigation  (cockpit only)
   Quieter section heads, de-chipped icons, and an active state that echoes
   the landing chooser's tiles (yellow left accent). Behaviour is untouched;
   .menu_endpoint / .sidebar-dropdown / .selected wiring is preserved.
   ══════════════════════════════════════════════════════════════════════ */

/* Section heads: quiet upper-case labels. */
body.vip-cockpit .sidebar-menu .header-menu span,
body.vip-cockpit .sidebar-ident .header-menu span {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--secondary_50);
    padding: 8px 12px 2px;
}

/* Sub-headers inside dropdown submenus (e.g. Aggregated / Detail in the
   merged reversion group): the main nav's section-head voice, one size
   down and indented to the submenu's text column. Plain list items, so
   the theme's link styling does not apply. */
body.vip-cockpit .sidebar-submenu .vip-submenu-header span {
    display: block;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--secondary_50);
    padding: 10px 20px 2px 30px;
    cursor: default;
}

/* Rows: tighter rhythm, room for the active accent. */
body.vip-cockpit .sidebar-menu ul li a {
    padding: 8px 12px;
    border-radius: 0;
    position: relative;
    transition: background .15s ease, color .15s ease;
}
body.vip-cockpit .sidebar-menu ul li a .menu-text,

/* Icons: drop the steel chip; plain glyphs that inherit the row colour.
   (FontAwesome's JS swaps <i> for <svg>, hence the svg target.) */
body.vip-cockpit .sidebar-menu ul li a svg {
    background: transparent !important;
    padding: 0;
    width: 18px;
    height: 18px;
    margin-right: 12px;
    color: inherit;
}
/* Retire the dated hover "swing". */
body.vip-cockpit .sidebar-menu ul li a:hover > svg::before { animation: none; }

/* Hover: gentle wash on inactive rows. */
body.vip-cockpit .sidebar-menu ul li a:not(.selected):hover {
    background: rgba(255, 255, 255, .06);
    color: var(--vip-white);
}

/* Active leaf: yellow inset accent bar + faint wash — the chooser-tile cue. */
body.vip-cockpit .sidebar-menu a.menu_endpoint.selected {
    color: var(--vip-yellow);
    background: rgba(255, 255, 255, .06);
    box-shadow: inset 3px 0 0 var(--vip-yellow);
}
body.vip-cockpit .sidebar-menu a.menu_endpoint.selected svg { color: var(--vip-yellow); }

/* Footer: status line only now (contacts & settings moved to the top bar). */
/* Rent-roll background loader: a quiet, smoothly spinning status line that the engine
   self-clears when the fetch finishes. Scoped under .sidebar-wrapper to beat the
   theme's footer-status rule; the muted steel colour is left to the theme. */
body.vip-cockpit .sidebar-wrapper .sidebar-footer .footer-status { gap: 8px; padding: 10px 12px; font-size: 12px; }
body.vip-cockpit .sidebar-wrapper .sidebar-footer .footer-status i { font-size: 15px; }

/* ══════════════════════════════════════════════════════════════════════
   COCKPIT SIDEBAR — scope panel  (cockpit only)
   Filter and search are global tools, not navigation targets, so they sit in one
   enclosed, rounded panel — a distinct register from the flat nav rows below. The
   filter reads as a status chip: its body opens the overlay and shows the live
   scope, the trailing reset appears only while a filter is on and accents towards
   the brand yellow. Behaviour and the #btnGroupFilter / #btnResetFilter_sidebar
   hooks are unchanged; the search field styling below is preserved. */
body.vip-cockpit .vip-scope {
    margin: 4px 12px;
    padding: 10px;
    background: rgba(255, 255, 255, .04);
    border: 1px solid var(--vip-navy-70);
    border-radius: 12px;
}
body.vip-cockpit .vip-scope-label {
    margin: 0 2px 8px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--vip-muted);
}

/* Filter chip. The row carries the border; the two buttons are seamless fields
   inside it, split by a hairline. */
body.vip-cockpit .vip-scope #sidebar_filter { margin: 0; padding: 0; }
body.vip-cockpit .vip-scope-filter {
    display: flex;
    align-items: stretch;
    width: 100%;
    background: rgba(255, 255, 255, .05);
    border: 1px solid var(--vip-navy-70);
    border-radius: 8px;
    overflow: hidden;
    transition: border-color .18s ease;
}
body.vip-cockpit .vip-scope-filter-main {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 9px;
    background: transparent;
    border: 0;
    color: var(--vip-white);
    font-size: 13px;
    padding: 9px 11px;
    text-align: left;
    cursor: pointer;
    transition: background .18s ease, color .18s ease;
}
body.vip-cockpit .vip-scope-filter-main > i { font-size: 14px; color: var(--vip-muted); transition: color .18s ease; }
body.vip-cockpit .vip-scope-filter-status { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.vip-cockpit .vip-scope-filter-main:hover { background: rgba(255, 255, 255, .06); }

/* Reset (✕): shown only while a filter is active (#btnResetFilter_sidebar loses
   .hidden in filterData_main). */
body.vip-cockpit .vip-scope-filter-reset {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    background: transparent;
    border: 0;
    border-left: 1px solid var(--vip-navy-70);
    color: var(--vip-muted);
    font-size: 13px;
    cursor: pointer;
    transition: background .18s ease, color .18s ease;
}
body.vip-cockpit .vip-scope-filter-reset:hover { background: rgba(255, 255, 255, .06); color: var(--vip-white); }

/* Active state — a filter is applied (#btnGroupFilter.filtered): a restrained accent.
   The frame, the funnel and the "Filtered" word carry the cue; the count stays light
   and the reset stays muted, so the chip reads as accented rather than lit up. */
body.vip-cockpit #btnGroupFilter.filtered { border-color: var(--vip-yellow); }
body.vip-cockpit #btnGroupFilter.filtered .vip-scope-filter-main > i,
body.vip-cockpit #btnGroupFilter.filtered .vff-fstate { color: var(--vip-yellow); }
body.vip-cockpit .vff-fstate { font-weight: 600; }
body.vip-cockpit .vff-fcount { color: var(--vip-white); }

/* Keyboard focus. */
body.vip-cockpit .vip-scope-filter-main:focus-visible,
body.vip-cockpit .vip-scope-filter-reset:focus-visible { outline: 2px solid var(--vip-yellow); outline-offset: -2px; }

/* Property + tenant search aligned to the filter chip: the .input-group carries one hairline
   frame with a quiet dark fill; field and magnifier button sit transparent inside it,
   split by the field's right edge. Scoped under .vip-scope-search (shared by both search
   boxes) so it beats the theme's light .search-menu fill and the button's inline border
   reset. */
body.vip-cockpit .vip-scope .vip-scope-search { padding: 8px 0 0; }
body.vip-cockpit .vip-scope .vip-scope-search .input-group {
    margin: 0;
    background: rgba(255, 255, 255, .05);
    border: 1px solid var(--vip-navy-70);
    border-radius: 8px;
    overflow: hidden;
    transition: border-color .18s ease;
}
body.vip-cockpit .vip-scope .vip-scope-search input.search-menu {
    background: transparent;
    border: 0;
    border-right: 1px solid var(--vip-navy-70);
    border-radius: 0;
    color: var(--vip-white);
    box-shadow: none;
}
body.vip-cockpit .vip-scope .vip-scope-search input.search-menu::placeholder { color: var(--vip-muted); opacity: .85; }
body.vip-cockpit .vip-scope .vip-scope-search input.search-menu:focus { outline: none; box-shadow: none; }
body.vip-cockpit .vip-scope .vip-scope-search .vip-scope-search-btn {
    background: transparent;
    border: 0;
    border-radius: 0;
    color: var(--vip-muted);
    box-shadow: none;
    transition: background .18s ease, color .18s ease;
}
body.vip-cockpit .vip-scope .vip-scope-search .vip-scope-search-btn:hover { background: rgba(255, 255, 255, .06); color: var(--vip-white); }
/* Focus lifts the whole group towards the brand yellow. */
body.vip-cockpit .vip-scope .vip-scope-search .input-group:focus-within { border-color: var(--vip-yellow); }
body.vip-cockpit .vip-scope .vip-scope-search .input-group:focus-within .vip-scope-search-btn { color: var(--vip-yellow); }

/* ══════════════════════════════════════════════════════════════════════
   COCKPIT BOX + DATATABLE — colour tokens  (cockpit only)
   Box and datatable colours are driven by the semantic tokens below (the
   "steel" palette). Keeping them as tokens means future re-theming is a matter
   of editing this one block rather than the rules underneath.
   ══════════════════════════════════════════════════════════════════════ */
body.vip-cockpit {
    /* Box */
    --box-bg:         #F5F7F8;
    --box-content-fg: inherit;
    --box-border:     #D7DCE0;
    --box-shadow:     0 1px 3px rgba(37, 39, 58, .10);
    --box-head-bg:    #3C5669;
    --box-head-fg:    var(--vip-white);
    --box-head-rule:  #2F4655;
    --box-badge-bg:   #3C5669;
    --box-badge-fg:   var(--vip-white);
    --box-chart-bg:   #EAEEF1;
    /* Datatable */
    --dt-head-bg:     #3C5669;
    --dt-head-fg:     var(--vip-white);
    --dt-accent:      var(--vip-yellow);
    --dt-foot-bg:     #3C5669;
    --dt-foot-fg:     var(--vip-white);
    --dt-row-even:    #FFFFFF;
    --dt-row-odd:     #EEF1F3;
    --dt-row-sel:     var(--vip-row-sel);
    --dt-row-hover:   #E2E6E9;
    --dt-col-border:  #C7D0D6;
    --dt-all-bg:      #2F4655;
    --dt-all-fg:      var(--vip-white);
    --dt-state-bg:    #EEF1F3;
    --dt-state-fg:    #3C5669;
    --dt-btn-fg:      #3C5669;
}

/* ── Box — consumes the tokens above ─────────────────────────────────── */
body.vip-cockpit .box {
    background: var(--box-bg);
    color: var(--box-content-fg);
    border: 1px solid var(--box-border);
    border-radius: 10px;
    box-shadow: var(--box-shadow);
}
body.vip-cockpit .box-header {
    background: var(--box-head-bg);
    color: var(--box-head-fg);
    text-align: center;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .5px;
    text-transform: uppercase;
    padding: 9px 14px;
    border-bottom: 1px solid var(--box-head-rule);
}
/* Inline header icon: a glyph before the title, seated on a lighter circular disc.
   The disc is a translucent white so it reads as a lighter tint of whatever the
   header surface is, and the negative vertical margins keep the header height
   unchanged even though the circle is taller than the title's text line. */
body.vip-cockpit .box-header .icon.inside {
    float: left;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    margin: -6px 8px -6px 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, .14);
    color: var(--box-head-fg);
}
/* The outside badge overlaps the corner, so indent a left-aligned title past it. */
body.vip-cockpit .box-header:has(.icon.outside) { padding-left: 50px; }
body.vip-cockpit .box-header .icon.outside {
    background: var(--box-badge-bg);
    border-color: var(--box-bg);
    color: var(--box-badge-fg);
}
/* No collapse: drop the clickable affordance if a box still carries it. */
body.vip-cockpit .box.collapsible .box-header { cursor: default; }
/* Inner chart wells follow the surface. */
body.vip-cockpit .box-content .chartbox { background: var(--box-chart-bg); }

/* ══════════════════════════════════════════════════════════════════════
   COCKPIT DATATABLES — re-skinned rows  (cockpit only)
   Consumes the tokens above. Overrides mirror datatables.bootstrap4.
   adjustments_t5.00j.css (and the dt_row_* states in styles_website); the
   !important flags match the originals only.
   ══════════════════════════════════════════════════════════════════════ */


/* ══════════════════════════════════════════════════════════════════════
   PROPERTY / VALUATION POPUP NAV — match the main sidebar  (cockpit)
   The overlay popup nav is a Bootstrap nav-pills list inside
   .overlay-sidebar-wrapper .sidebar-menu. The steel hover/active fills sit on
   the <li class="nav-item"> and on .nav-link.active (shared_base + bootstrap
   adjustments) — which is why styling the <a> alone had no effect. These rules
   target the right elements so the popup behaves exactly like the main nav: a
   quiet white wash on hover and a yellow accent on the active item, with no
   steel fill and no stray text selection. Scoped to the overlay wrappers, so
   other .nav-pills (snapshot results, content tabs) are untouched.
   ══════════════════════════════════════════════════════════════════════ */

/* No accidental text-selection highlight on any nav label (main or popup). */
body.vip-cockpit .sidebar-menu ul li a { user-select: none; }

/* Hover lives on the list item — replace the steel fill with the wash. */
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-menu .nav-pills .nav-item:hover,
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-menu li.nav-item:hover {
    background: rgba(255, 255, 255, .06);
}
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-menu li.nav-item:hover > a { color: var(--vip-white); }
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-menu li.nav-item:hover > a svg { color: inherit; }

/* Pills carry data-target (no href) so the target URL never appears in the browser
   status bar on hover; add back the pointer the missing href would otherwise give.
   Shared by both overlays (Property + Valuation). */
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-menu .nav-link { cursor: pointer; }

/* Active tab — drop the steel fill (nav-item.active / nav-link.active) and show
   only the yellow accent, matching .menu_endpoint.selected in the main nav. */
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-menu .nav-item.active,
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-menu li.nav-item.active {
    background: rgba(255, 255, 255, .06);
    box-shadow: inset 3px 0 0 var(--vip-yellow);
}
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-menu .nav-pills .nav-link.active,
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-menu .nav-pills .show > .nav-link,
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-menu .nav-item.active > a {
    background: transparent;
    color: var(--vip-yellow);
    box-shadow: inset 3px 0 0 var(--vip-yellow);
}

/* The selected endpoint keeps the same yellow text + inset bar as the main nav. */
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-menu a.menu_endpoint.selected {
    color: var(--vip-yellow);
    background: transparent;
    box-shadow: inset 3px 0 0 var(--vip-yellow);
}
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-menu a.menu_endpoint.selected svg { color: var(--vip-yellow); }

/* ══════════════════════════════════════════════════════════════════════
   PROPERTY / VALUATION POPUP NAV — section heads  (cockpit)
   The popup nav is a single-level list (ul > li > a) matching the main
   sidebar and the Valuation overlay; the tile-grid layer was removed. Only
   the section-head padding is popup-specific and kept here.
   ══════════════════════════════════════════════════════════════════════ */

/* Section heads: quiet upper-case labels. */
body.vip-cockpit .overlay-sidebar-wrapper .header-menu span {
    padding: 8px 0px 4px;
}

/* ══════════════════════════════════════════════════════════════════════
   PROPERTY POPUP — PREVIEW SWITCHER + SEAMLESS SIDEBAR  (cockpit)
   The preview dropdown is replaced by a segmented switcher (the hidden <select>
   stays the source of truth). The two stacked sidebar panels (preview + nav)
   are squared along their shared edge and the gap is removed, so they read as
   one continuous navy column rather than two detached cards.
   ══════════════════════════════════════════════════════════════════════ */

/* Preview switcher — segmented buttons, active takes the nav's yellow accent. */
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-ident .preview-switch {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;    
    margin-top: 5px;
}
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-ident .preview-switch .preview-opt {
    flex: 1 1 auto;
    min-width: 0;
    padding: 6px 8px;
    font-size: 12px;
    line-height: 1;
    color: var(--vip-muted);
    background: rgba(255, 255, 255, .10);
    border: 0;
    border-radius: 6px;
    cursor: pointer;
    white-space: nowrap;
}
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-ident .preview-switch .preview-opt:hover {
    background: rgba(255, 255, 255, .10);
    color: var(--vip-white);
}
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-ident .preview-switch .preview-opt.active,
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-ident .preview-switch .preview-opt.active:hover {
    background: rgba(255, 255, 255, .05);
    color: var(--vip-yellow);
    /* Yellow outline with a heavier left edge — drawn with inset shadow so it adds
       no box size (no layout shift) and stays visually distinct from the filled
       yellow tiles of the navigation in the panel below. */
    box-shadow: inset 0 0 0 1px var(--vip-yellow), inset 3px 0 0 0 var(--vip-yellow);
}
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-ident .preview-switch .preview-opt.hidden { display: none; }

/* One navy column: the wrapper carries the background and fills the full overlay
   height; the two stacked panels are transparent, so they read as one surface. */
body.vip-cockpit .overlay-sidebar-wrapper {
    height: 100%;
    background: var(--primary_100);
}
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-item {
    background: transparent;
}

/* Focus ring — suppress the dotted outline that appears after a mouse click on
   a nav item (main and popup nav alike), mirroring the main nav's outline:none
   on .menu_endpoint.selected. Keyboard focus (:focus-visible) stays visible so
   the nav remains accessible. */
body.vip-cockpit .sidebar-menu ul li a:focus:not(:focus-visible) {
    outline: none;
}

/* Project-wide: buttons and button-like controls drop the outline that lingers
   after a mouse click, extending the nav rule above to every button. Keyboard
   focus (:focus-visible) keeps its ring so controls stay accessible. */
body.vip-cockpit button:focus:not(:focus-visible),
body.vip-cockpit [role="button"]:focus:not(:focus-visible),
body.vip-cockpit input[type="button"]:focus:not(:focus-visible),
body.vip-cockpit input[type="submit"]:focus:not(:focus-visible),
body.vip-cockpit input[type="reset"]:focus:not(:focus-visible),
body.vip-cockpit .btn:focus:not(:focus-visible) {
    outline: none;
}


/* ══════════════════════════════════════════════════════════════════════
   SHARED CLOSE CHIP  (cockpit)
   Both close controls — the $.Popup modal's .popup_close and the content-push
   overlays' .overlay-close — are styled here from a single grouped rule, so the
   two engines present an identical chip (navy wash, white glyph resolving to the
   brand yellow on hover). vip-shell.css loads last, so this beats overlay.css's
   structural .overlay .overlay-close as well as popup_t5.00j.css's .popup_close.
   Appearance is owned here; position stays per-control (popup_t5.00j.css frames
   the modal chip, overlay.css frames the overlay chip), with the overlay inset
   nudged below. The landing page keeps its own self-contained .popup_close in
   popup_t5.00j.css, since vip-shell.css is not loaded there.
   ══════════════════════════════════════════════════════════════════════ */
body.vip-cockpit .popup_close,
body.vip-cockpit .overlay-close {
    width: 30px;
    height: 30px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 18px;
    line-height: 1;
    color: var(--vip-white);
    /* Neutralise any legacy cross-image hack (text-indent/transparent/url) so the
       FontAwesome glyph is visible whatever the base stylesheet set. */
    text-indent: 0;
    overflow: visible;
    background-image: none;
    background: var(--vip-navy);
    border: 1px solid rgba(255, 255, 255, .28);
    border-radius: 8px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .25);
    cursor: pointer;
    transition: background .18s ease, color .18s ease, transform .12s ease;
}
body.vip-cockpit .popup_close:hover,
body.vip-cockpit .popup_close:focus,
body.vip-cockpit .overlay-close:hover,
body.vip-cockpit .overlay-close:focus {
    background: var(--vip-navy-90);
    border-color: var(--vip-white);
    outline: none;
}
/* Keyboard focus stays visible. */
body.vip-cockpit .popup_close:focus-visible,
body.vip-cockpit .overlay-close:focus-visible {
    outline: 2px solid var(--vip-yellow);
    outline-offset: 2px;
}
/* Overlay chip sits a touch further in from the full-screen corner. */
body.vip-cockpit .overlay-close {
    top: 8px;
    right: 8px;
}
/* The overlay control carries a legacy "Close" text label, unlike the modal's
   .popup_close which already renders a cross. Hide that label and draw a matching
   multiplication-sign glyph so both controls read as the same chip. A plain
   character is used rather than a FontAwesome glyph to stay font-version-agnostic. */
body.vip-cockpit .overlay-close {
    font-size: 0;
}
body.vip-cockpit .overlay-close::before {
    content: "\00d7";
    font-size: 24px;
    font-weight: 400;
    line-height: 1;
}

/* ══════════════════════════════════════════════════════════════════════
   AG-GRID OVERLAY POPUP  (cockpit)
   callAggridPopup() reuses the same self-contained card as the DataTable
   popup (.dt-popup → .dt-popup-head → .dt-popup-body, defined in
   popup_t5.00j.css) so both overlays share one outer UI. A DataTable simply
   scrolls inside the body, but an ag-Grid needs a parent of definite height
   or it collapses to nothing. The body is a vertical stack so mountAggridToolbar()
   — which inserts its bar directly before the grid mount — sits the toolbar above
   a grid that fills the remaining height, mirroring the DataTables toolbar layout.
   Scoped to the modifier, so the DataTable path and the legacy stylesheet stay
   untouched.
   ══════════════════════════════════════════════════════════════════════ */
body.vip-cockpit .dt-popup-body.is-aggrid {
    display: flex;
    flex-direction: column;
    min-height: 0;
}
body.vip-cockpit .dt-popup-body.is-aggrid > .vip-grid-toolbar {
    flex: 0 0 auto;   /* toolbar keeps its natural height above the grid */
}
body.vip-cockpit .dt-popup-body.is-aggrid > .vip-aggrid-mount {
    flex: 1 1 auto;   /* grid fills the height left under the toolbar */
    min-width: 0;
    min-height: 0;
}

/* ══════════════════════════════════════════════════════════════════════
   CONTENT-PUSH OVERLAY TITLE  (cockpit)
   The Property and Valuation-comparison overlays both render their title in a
   .popup_header bar (the only two users of that class). This brings it in line
   with the shared steel header language of the cockpit boxes and the data-table
   / ag-grid popup heads: a flush, left-aligned uppercase label with a leading
   glyph and the yellow accent rule. The glyph element is injected by each
   overlay's script; the close control is the shared chip defined above.
   The identity parts below (context chip, PID, separator, municipality, name)
   are deliberately NOT scoped to .popup_header: the tmp_modal header reuses the
   same vocabulary, so both surfaces read identically from one set of rules.
   ══════════════════════════════════════════════════════════════════════ */
body.vip-cockpit .popup_header {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 46px;
    box-sizing: border-box;
    margin: 0;
    padding: 9px 56px 9px 12px;      /* right gutter clears the overlay close chip */
    background: var(--vip-navy);
    color: var(--box-head-fg, #fff);
    border-bottom: 2px solid var(--vip-yellow, #FFDF00);
    border-radius: 0;                /* flush strip, not a rounded centred pill */
    font-size: 13px;
    font-weight: 500;
    letter-spacing: .5px;
    line-height: 1.2;
    text-transform: uppercase;
    text-align: left;
    white-space: nowrap;
    overflow: hidden;                /* the identity slot truncates before it wraps */
}
/* Context chip: names the view (Property / Valuation comparison) structurally via a
   hairline outline rather than colour, so the single yellow accent stays on the PID. */
body.vip-cockpit .popup_header-context {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 9px;
    border: 1px solid rgba(255, 255, 255, .35);
    border-radius: 3px;
    font-size: 11px;
    letter-spacing: .6px;
}
body.vip-cockpit .popup_header-context i {
    font-size: 13px;
    opacity: .95;
}
/* Property identifier: the sole yellow element, the primary index the user scans for. */
body.vip-cockpit .popup_header-pid {
    flex: 0 0 auto;
    color: var(--vip-yellow, #FFDF00);
    font-weight: 600;
}
/* Metadata separator and municipality are dimmed context, not identity. */
body.vip-cockpit .popup_header-sep {
    flex: 0 0 auto;
    opacity: .55;
}
body.vip-cockpit .popup_header-muni {
    flex: 0 0 auto;
    opacity: .75;
    font-weight: 400;
}
/* Asset identity (name or address): full weight, holds the line, truncates last. */
body.vip-cockpit .popup_header-name {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ══════════════════════════════════════════════════════════════════════
   CONTENT-PUSH OVERLAY FRAME  (cockpit)
   The Property and Valuation overlays carry a full-width title strip and are
   marked .has-titlebar. The overlay centres a card (.overlay_content, sized by
   openOverlay_Property / openOverlay_Valuation); the card stacks the steel
   title strip over a row holding the sidebar and the main display area, so the
   frame matches the data-table popup. The close chip stays absolute on the
   overlay. Title-less overlays (filter, tmp) keep the base row layout untouched.
   ══════════════════════════════════════════════════════════════════════ */
body.vip-cockpit .overlay.has-titlebar {
    display: flex;
    align-items: center;
    justify-content: center;
}
body.vip-cockpit .has-titlebar .overlay_content {
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    justify-content: flex-start;
    /* width/height are set per-overlay by the open functions */
}
body.vip-cockpit .has-titlebar .overlay-titlebar {
    flex: 0 0 auto;
    width: 100%;
    align-items: center;
    justify-content: center;
}
body.vip-cockpit .has-titlebar .overlay_body {
    flex: 1 1 auto;
    min-height: 0;
    width: 100%;
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: stretch;
}
/* The sidebar/wrapper heights are set inline (full overlay height) by the open
   functions; under the title strip they fill the body row instead. */
body.vip-cockpit .has-titlebar .overlay-sidebar-wrapper,
body.vip-cockpit .has-titlebar .overlay_wrapper {
    height: 100% !important;
}

/* ══════════════════════════════════════════════════════════════════════
   UNIFIED OVERLAY ENGINE  (cockpit)
   openOverlay() renders each migrated overlay into its OWN container
   (.vip-engine, id vip_overlay_<n>) using the shared .has-titlebar card, so
   several engine overlays can stack at once without colliding ids. Each card
   sits above the content-push overlays and anchors its own close chip (the chip
   lives inside .overlay_content, so it tracks the card rather than the screen
   corner). Body layout is "full" — one region under the title strip — with an
   is-aggrid variant that stacks the toolbar over a grid, mirroring the former
   dt-popup. Styling keys off classes only, never the per-instance id.
   ══════════════════════════════════════════════════════════════════════ */
/* Overlay z-index: a single resting base for every overlay. The open-order stack
   in system_t5_00j.js stamps the live z-index inline (base + position*step) while
   an overlay is open, so the stacking follows the open history rather than a fixed
   per-overlay value. This resting rule also overrides overlay.css's hard-coded
   ladder (this file loads last). The close chip rides above the card content. */
body.vip-cockpit .overlay                { z-index: var(--z-overlay); }
body.vip-cockpit .overlay .overlay-close { z-index: var(--z-overlay-close); }
/* The rounded, clipped card is shared by EVERY title-bar overlay — the engine
   containers (.vip-engine) and the static Property / Valuation overlays alike —
   so all three read as the same card. position:relative anchors each overlay's
   own close chip to its card (the chips now live inside .overlay_content), and
   overflow:hidden clips the full-bleed title strip to the rounded top corners.
   Title-less overlays (filter, tmp) are not .has-titlebar and keep their frame. */
body.vip-cockpit .has-titlebar .overlay_content {
    position: relative;          /* anchor the close chip to the card */
    border-radius: 8px;
    overflow: hidden;            /* clip the title strip to the rounded top */
}

/* Full layout: the body is a single scroll region under the title strip. */
body.vip-cockpit .has-titlebar.layout-full .overlay_body {
    display: block;
    overflow: auto;
    padding: 14px;
    background: #fff;
}
/* Full layout + ag-Grid: stack the toolbar over a grid that fills the rest. */
body.vip-cockpit .has-titlebar.layout-full .overlay_body.is-aggrid {
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
body.vip-cockpit .has-titlebar.layout-full .overlay_body.is-aggrid > .vip-grid-toolbar {
    flex: 0 0 auto;
}
body.vip-cockpit .has-titlebar.layout-full .overlay_body.is-aggrid > .vip-aggrid-mount {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 0;
}
/* Full layout + zoom-mode gallery: a two-pane flex body (tile sidebar beside
   the original), not the default padded scroll frame. Drops padding as the
   filter overlay does so the panes reach the card edges. */
body.vip-cockpit .has-titlebar.layout-full .overlay_body.is-vgal-zoom {
    display: flex;
    overflow: hidden;
    padding: 0;
}

/* Sidebar layout: a fixed-width nav column beside a main view that fills the
   rest. Shared by the engine sidebar variant and the static Property /
   Valuation overlays, which all carry .layout-sidebar. The base .has-titlebar
   .overlay_body already lays the body out as a row; here the column is pinned to
   a fixed width and the main view takes the remaining space. The static overlays
   set the column width inline (menu_offset) in their open functions, which wins
   over the token below; the engine variant has no inline width and falls back to
   --vip-sidebar-w. Each region keeps its own scroll; the card clip stays intact. */
body.vip-cockpit .has-titlebar.layout-sidebar .overlay_body {
    justify-content: flex-start;
}
body.vip-cockpit .has-titlebar.layout-sidebar .overlay-sidebar-wrapper {
    flex: 0 0 auto;                /* honour the inline width when present */
    width: var(--vip-sidebar-w);   /* engine default; an inline width overrides */
    min-width: 0;
    /* stack the identity panel above the nav menu; the wrapper no longer scrolls
       as a whole - the ident stays pinned and only the menu scrolls (rules below) */
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* Property/Valuation sidebar: pin the identity panel ('Context contents') to the
   top and let only the nav menu scroll. The wrapper is a flex column (above), so
   the ident keeps its natural height and the menu fills the remainder with its
   own scrollbar. */
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-ident {
    flex: 0 0 auto;
}
body.vip-cockpit .overlay-sidebar-wrapper .sidebar-menu {
    flex: 1 1 auto;
    min-height: 0;        /* allow the flex child to shrink so its overflow can scroll */
    overflow-y: auto;
    overflow-x: hidden;
}
body.vip-cockpit .has-titlebar.layout-sidebar .overlay_wrapper {
    flex: 1 1 auto;                /* main view fills the space beside the column */
    min-width: 0;
    overflow: hidden;              /* its inner .main owns the scroll */
}

/* ══════════════════════════════════════════════════════════════════════
   LAYERED FILTER PANEL  (cockpit)
   The filter overlay's body is a facet rail (coarse, positive-select
   categories) beside a main column that carries the ledger, the
   Included / Excluded A/B switch, a compact result table and the
   preview + apply/discard/reset footer. It fills the .has-titlebar card
   built in the shell migration; only the inner panel is styled here.
   Colours: navy from the brand token, greys/accents as literals to match
   the cockpit's fixed light theme.
   ══════════════════════════════════════════════════════════════════════ */
body.vip-cockpit .vip-filter { display: flex; height: 100%; min-height: 0; }

/* --- facet rail --- */
body.vip-cockpit .vip-filter-rail { flex: 0 0 250px; width: 250px; overflow: auto; padding: 8px 10px; border-right: .5px solid #e1e4ea; background: #f5f6f9; }
body.vip-cockpit .vip-filter-rail-head { font-size: 12px; font-weight: 600; color: var(--vip-navy); margin-bottom: 4px; }
body.vip-cockpit .vff-group { margin-bottom: 8px; }
body.vip-cockpit .vff-head { display: flex; align-items: center; justify-content: space-between; margin: 8px 0 3px; }
body.vip-cockpit .vff-head-name { font-size: 10px; text-transform: uppercase; letter-spacing: .4px; color: #8a8f99; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.vip-cockpit .vff-allnone button { border: none; background: transparent; color: #a3a8b2; cursor: pointer; padding: 0 2px; font-size: 12px; }
body.vip-cockpit .vff-allnone button:hover { color: var(--vip-navy); }
body.vip-cockpit .vff-cat { display: flex; align-items: center; gap: 7px; padding: 3px 4px; margin: 0; border-radius: 5px; font-size: 12px; cursor: pointer; }
body.vip-cockpit .vff-cat:hover { background: #fff; }
body.vip-cockpit .vff-cat input { position: absolute; opacity: 0; width: 0; height: 0; }
body.vip-cockpit .vff-box { flex: 0 0 auto; width: 15px; height: 15px; border-radius: 4px; border: 1.5px solid #c2c7d0; display: flex; align-items: center; justify-content: center; color: #fff; font-size: 9px; }
body.vip-cockpit .vff-cat input:checked ~ .vff-box { background: var(--vip-navy); border-color: var(--vip-navy); }
body.vip-cockpit .vff-cat input:not(:checked) ~ .vff-box i { display: none; }
body.vip-cockpit .vff-cat input:not(:checked) ~ .vff-name { color: #9aa0aa; }
body.vip-cockpit .vff-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.vip-cockpit .vff-count { flex: 0 0 auto; font-size: 10px; color: #9aa0aa; }

/* Cap each facet's category list at eight rows; longer clusters then scroll
   internally instead of stretching the facet rail. Mirrors the Cluster values
   list under Breakdown by cluster > Treemap. The .vff-head stays fixed above the
   scroll area. Row height: 18px checkbox mark (15px + 2x1.5px border) + 6px
   vertical padding = 24px. */
body.vip-cockpit .vff-cats {
	--vff-cat-row-h: 24px;
	max-height: calc(var(--vff-cat-row-h) * 8);
	overflow-y: auto;
	overscroll-behavior: contain;
	/* Firefox scrollbar styling. */
	scrollbar-width: thin;
	scrollbar-color: var(--vip-navy-40) transparent;
}
/* Slim scrollbar to keep the capped list visually quiet in the rail. */
body.vip-cockpit .vff-cats::-webkit-scrollbar {
	width: 8px;
}
body.vip-cockpit .vff-cats::-webkit-scrollbar-track {
	background: transparent;
}
body.vip-cockpit .vff-cats::-webkit-scrollbar-thumb {
	background: var(--vip-navy-40);
	border-radius: 6px;
	border: 2px solid transparent;
	background-clip: padding-box;
}
body.vip-cockpit .vff-cats::-webkit-scrollbar-thumb:hover {
	background: var(--vip-navy-60);
	background-clip: padding-box;
}

/* --- main column --- */
body.vip-cockpit .vip-filter-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
body.vip-cockpit .vip-filter-ledger { display: flex; align-items: center; gap: 5px; padding: 7px 12px; background: #eef1f6; font-size: 12px; color: var(--vip-navy); }
body.vip-cockpit .vip-filter-ledger b { font-weight: 600; }
body.vip-cockpit .vip-filter-ledger .vff-plus { color: #1d8f6a; font-weight: 600; }
body.vip-cockpit .vip-filter-ledger .vff-minus { color: #c0392b; font-weight: 600; }
body.vip-cockpit .vip-filter-ledger .vff-clear { margin-left: auto; font-size: 11px; color: var(--vip-navy); text-decoration: underline; cursor: pointer; }

body.vip-cockpit .vip-filter-switch { display: flex; align-items: center; gap: 10px; padding: 7px 12px; border-bottom: .5px solid #e1e4ea; }
body.vip-cockpit .vip-seg { display: inline-flex; border: .5px solid #c2c7d0; border-radius: 7px; overflow: hidden; }
body.vip-cockpit .vip-seg button { border: none; background: transparent; color: #5f6672; cursor: pointer; font-size: 12px; padding: 4px 12px; }
body.vip-cockpit .vip-seg button.on { background: var(--vip-navy); color: #fff; }
body.vip-cockpit .vip-seg-n { margin-left: 3px; opacity: .8; }
body.vip-cockpit .vip-filter-search { margin-left: auto; display: flex; align-items: center; gap: 5px; color: #9aa0aa; font-size: 12px; }
body.vip-cockpit .vip-filter-search input { border: .5px solid #d3d7de; border-radius: 5px; padding: 3px 7px; font-size: 12px; }

/* --- compact result table --- */
body.vip-cockpit .vip-filter-tablewrap { flex: 1 1 auto; min-height: 0; overflow: auto; }
body.vip-cockpit .vip-ftab { width: 100%; border-collapse: collapse; }
body.vip-cockpit .vip-ftab thead th { position: sticky; top: 0; z-index: 1; background: #fff; text-align: left; font-size: 12px; font-weight: 600; color: #5f6672; padding: 6px 6px; border-bottom: .5px solid #d3d7de; white-space: nowrap; }
body.vip-cockpit .vip-ftab td { font-size: 12px; padding: 6px 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 220px; }
/* Striped rows sourced from the DataTables theme tokens (--secondary_*). Painted
   on td so they sit above any tr-level tint; hover / selection layer on top. */
body.vip-cockpit .vip-ftab tbody tr:nth-child(even) td { background: var(--secondary_5); }
body.vip-cockpit .vip-ftab tbody tr:nth-child(odd) td  { background: var(--secondary_15); }
body.vip-cockpit .vip-ftab .vff-r { text-align: right; }
body.vip-cockpit .vip-ftab .vff-c-ctrl { width: 62px; white-space: nowrap; }
body.vip-cockpit .vip-ib { width: 22px; height: 22px; margin-right: 2px; border-radius: 5px; border: .5px solid #d3d7de; background: transparent; display: inline-flex; align-items: center; justify-content: center; color: #5f6672; font-size: 11px; cursor: pointer; }
body.vip-cockpit .vip-ib:hover { background: #f0f2f6; }
body.vip-cockpit .vip-ib.vff-remove:hover { color: #c0392b; border-color: #e3b5b0; }
body.vip-cockpit .vip-ib.vff-add:hover { color: #1d8f6a; border-color: #a9d8c6; }
/* Manual-move tint overrides striping; selection overrides tint; hover wins last.
   Kept opaque (not rgba) so frozen cells fully cover columns scrolling underneath. */
body.vip-cockpit .vip-ftab tbody tr.vff-row-inc td  { background: #e7f3ee; }
body.vip-cockpit .vip-ftab tbody tr.vff-row-exc td  { background: #f8e8e5; }
body.vip-cockpit .vip-ftab tbody tr.vff-selected td { background: var(--secondary_30); }
body.vip-cockpit .vip-ftab tbody tr:hover td        { background: var(--secondary_25); cursor: pointer; }

/* Pinned-left columns: control block + Property ID ... Address stay frozen to the
   left while the remaining columns scroll horizontally. The left offsets are set
   per cell in JS (applyFilterPinned) from the live column widths. */
body.vip-cockpit .vip-ftab .vff-pin { position: sticky; left: 0; }
body.vip-cockpit .vip-ftab thead th { z-index: 2; }              /* header above pinned body cells */
body.vip-cockpit .vip-ftab tbody td.vff-pin { z-index: 1; }
body.vip-cockpit .vip-ftab thead th.vff-pin { z-index: 3; }      /* frozen header corner above both */
body.vip-cockpit .vip-ftab .vff-pin-last { box-shadow: 6px 0 6px -6px rgba(0, 0, 0, .28); }
body.vip-cockpit .vff-tag { font-size: 10px; font-weight: 600; padding: 0 4px; border-radius: 4px; }
body.vip-cockpit .vff-tag-inc { color: #1d8f6a; border: .5px solid rgba(29, 143, 106, .4); }
body.vip-cockpit .vff-tag-exc { color: #c0392b; border: .5px solid rgba(192, 57, 43, .4); }
body.vip-cockpit .vff-empty { color: #9aa0aa; text-align: center; padding: 14px; }

/* --- preview + actions footer --- */
body.vip-cockpit .vip-filter-foot { border-top: .5px solid #e1e4ea; padding: 8px 12px; display: flex; align-items: stretch; flex-wrap: wrap; gap: 12px; }
body.vip-cockpit .vip-filter-preview { flex: 1 1 320px; min-width: 0; display: flex; flex-wrap: wrap; gap: 8px; margin: 0; }
body.vip-cockpit .vff-metric { flex: 1 1 auto; min-width: 96px; background: #f5f6f9; border-radius: 6px; padding: 5px 8px; }
body.vip-cockpit .vff-metric-l { font-size: 11px; color: #8a8f99; }
body.vip-cockpit .vff-metric-v { font-size: 14px; font-weight: 600; color: var(--vip-navy); line-height: 1.25; }
body.vip-cockpit .vff-metric-t { display: block; font-size: 11px; font-weight: 400; color: #9aa0aa; }
body.vip-cockpit .vip-filter-actions { flex: 0 0 auto; display: flex; align-items: stretch; justify-content: flex-end; gap: 6px; }

/* Footer buttons. 'Apply filter' is the primary action of the whole screen: a navy
   field carrying the brand yellow, colours swapping on hover. 'Discard' and
   'Reset - include all' echo the overlay-close chip (navy field, white glyph). */
body.vip-cockpit .vff-btn { display: inline-flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 600; line-height: 1; padding: 7px 14px; border-radius: 8px; cursor: pointer; transition: background .18s ease, color .18s ease, border-color .18s ease; }
body.vip-cockpit .vff-btn:focus-visible { outline: 2px solid var(--vip-yellow); outline-offset: 2px; }
body.vip-cockpit .vff-apply { background: var(--vip-navy); color: var(--vip-yellow); border: 1px solid var(--vip-navy); }
body.vip-cockpit .vff-apply:hover, body.vip-cockpit .vff-apply:focus { background: var(--vip-yellow); color: var(--vip-navy); border-color: var(--vip-yellow); outline: none; }
/* Nothing pending, nothing to apply: the button states that rather than accepting a
   press that would do nothing. */
body.vip-cockpit .vff-apply[disabled],
body.vip-cockpit .vff-apply[disabled]:hover {
    background: var(--box-bg);
    border-color: var(--box-border);
    color: var(--vip-navy-40);
    cursor: default;
}
body.vip-cockpit .vff-chip { background: var(--vip-navy); color: var(--vip-white); border: 1px solid rgba(255, 255, 255, .28); box-shadow: 0 1px 3px rgba(0, 0, 0, .25); }
body.vip-cockpit .vff-chip:hover, body.vip-cockpit .vff-chip:focus { background: var(--vip-navy-90); border-color: var(--vip-white); outline: none; }

/* Sortable column headers (single-column / singleton sort). */
body.vip-cockpit .vip-ftab thead th.vff-sortable { cursor: pointer; user-select: none; }
body.vip-cockpit .vip-ftab thead th.vff-sortable:hover { color: var(--vip-navy); }
body.vip-cockpit .vip-ftab thead th.vff-sorted { color: var(--vip-navy); }

/* (a) This overlay runs its own two-column layout edge-to-edge: drop the card body padding. */
body.vip-cockpit #overlay_filter.has-titlebar.layout-full .overlay_body { padding: 0; }

/* ══════════════════════════════════════════════════════════════════════
   PROPERTY FILES — valuation-date timeline  (cockpit only)
   Files in the property popup are grouped by valuation reference date. Each
   group puts a date node on a vertical rail; to its right sits one media row
   per file, each carrying an inline preview thumbnail (painted onto the cell
   by fetchPreviewImage) or a file-type glyph fallback. Replaces the former
   two-column #file_table + hover-popover preview.
   ══════════════════════════════════════════════════════════════════════ */
body.vip-cockpit .vip-files {
    display: flex;
    flex-direction: column;
    /* Shrink to the content width and centre inside .inner-wrapper (which is
       w-100 and text-centre): a single card per date reads as centred, while a
       busy date widens the block up to the two-column cap below. */
    width: fit-content;
    max-width: 100%;
    margin: 0 auto;
    text-align: left;
}
/* One reference-date group: a fixed date rail on the left, cards on the right;
   the "auto" track lets the group size to its cards so the block can centre. */
body.vip-cockpit .vip-fgroup {
    display: grid;
    grid-template-columns: 104px auto;
    gap: 6px;
    margin-bottom: 12px;
}
body.vip-cockpit .vip-fgroup:last-child { margin-bottom: 0; }
/* Date node: right-aligned label with a dot sitting on the rail. */
body.vip-cockpit .vip-fdate {
    position: relative;
    text-align: right;
    padding: 14px 16px 0 0;
}
body.vip-cockpit .vip-fdate-label {
    font-size: 12px;
    font-weight: 600;
    color: var(--box-head-bg);
    line-height: 1.2;
}
body.vip-cockpit .vip-fdate-dot {
    position: absolute;
    right: -5px;
    top: 17px;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--box-head-bg);
    border: 2px solid var(--box-bg);
    box-shadow: 0 0 0 1px var(--dt-col-border);
}
/* Files with no reference date group under a muted "Unallocated" node. */
body.vip-cockpit .vip-fdate.is-unallocated .vip-fdate-label { color: var(--vip-steel); }
body.vip-cockpit .vip-fdate.is-unallocated .vip-fdate-dot { background: var(--vip-steel); }

/* Portfolio documents reuse this timeline, but the node is an instruction (with
   its valuation date range) rather than a single date, so the rail is wider and
   carries a muted range line beneath the instruction name. */
body.vip-cockpit .vip-files-portfolio .vip-fgroup { grid-template-columns: 190px auto; }
body.vip-cockpit .vip-fdate-range {
    display: block;
    margin-top: 2px;
    font-size: 11px;
    line-height: 1.2;
    color: var(--vip-steel);
}

/* Right column: the rail (left border) holds fixed-width cards that wrap. One
   file per date is a single compact card; several wrap into two columns (the
   max-width caps the block so it never sprawls, keeping the whole thing centred). */
body.vip-cockpit .vip-frows {
    /* Group band: cards of one date/instruction sit on a light strip with a
       coloured left edge (matching the node label + dot). This chunks the
       timeline so the eye can navigate between groups. */
    border-left: 3px solid var(--box-head-bg);
    background: var(--box-bg);
    border-radius: 0 10px 10px 0;
    padding: 12px 14px;
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    max-width: 812px;
}

/* Media row: thumbnail | body | action, on a rounded white surface. Fixed width
   so single and multi-file dates share one card size and wrap predictably. */
body.vip-cockpit .vip-frow {
    flex: 0 0 380px;
    display: flex;
    align-items: stretch;
    background: var(--vip-white);
    border: 1px solid var(--box-border);
    border-radius: 10px;
    overflow: hidden;
}
/* Thumbnail cell: glyph fallback centred; a fetched preview paints over it. */
body.vip-cockpit .vip-fthumb {
    position: relative;
    flex: 0 0 118px;
    min-height: 78px;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--box-chart-bg);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}
/* Portrait previews (e.g. an A4 first page) are shown in full via "contain" so
   they are not cropped top and bottom; landscape previews keep filling the box.
   The orientation class is set by fetchPreviewImage once the image has loaded. */
body.vip-cockpit .vip-fthumb.is-portrait {
    background-size: contain;
}
body.vip-cockpit .vip-fthumb-glyph {
    font-size: 26px;
    color: var(--vip-steel);
}
/* Once a preview is painted, drop the fallback glyph. */
body.vip-cockpit .vip-fthumb.is-loaded .vip-fthumb-glyph { display: none; }
/* File-type tag, bottom-left of the thumbnail. */
body.vip-cockpit .vip-ftype {
    position: absolute;
    left: 6px;
    bottom: 6px;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .4px;
    text-transform: uppercase;
    padding: 2px 6px;
    border-radius: 4px;
    background: var(--box-head-rule);
    color: var(--vip-white);
}
/* Body: title + metadata, vertically centred; the title is shown in full. */
body.vip-cockpit .vip-fbody {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    padding: 10px 12px;
}
body.vip-cockpit .vip-ftitle {
    font-size: 13px;
    font-weight: 600;
    color: var(--vip-navy);
    /* Shown in full, as it may carry essential information; long unbroken
       names (e.g. file names without spaces) still break. */
    white-space: normal;
    overflow-wrap: anywhere;
}
body.vip-cockpit .vip-fmeta {
    font-size: 11px;
    color: var(--vip-steel);
}
body.vip-cockpit .vip-fmeta-dot { color: var(--dt-col-border); margin: 0 4px; }

/* Action cell: the download/open button, vertically centred. */
body.vip-cockpit .vip-fact {
    display: flex;
    align-items: center;
    padding: 0 12px;
}
/* Download button: steel outline filling yellow on hover, matching the box
   accent. Scoped here so the legacy Bootstrap .btn styles stay untouched. */
body.vip-cockpit .vip-fact .downloadbutton,
body.vip-cockpit .vip-dfoot .downloadbutton {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
    padding: 7px 12px;
    border-radius: 8px;
    border: 1px solid var(--box-head-rule);
    background: var(--vip-white);
    color: var(--box-head-rule);
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}
body.vip-cockpit .vip-fact .downloadbutton:hover,
body.vip-cockpit .vip-fact .downloadbutton:focus,
body.vip-cockpit .vip-dfoot .downloadbutton:hover,
body.vip-cockpit .vip-dfoot .downloadbutton:focus {
    background: var(--vip-yellow);
    border-color: var(--vip-yellow);
    color: var(--vip-navy);
    outline: none;
}

/* Narrow popups: tighten the rail and let the thumbnail shrink. */
@media (max-width: 560px) {
    body.vip-cockpit .vip-files { width: 100%; }
    body.vip-cockpit .vip-fgroup { grid-template-columns: 84px auto; }
    body.vip-cockpit .vip-files-portfolio .vip-fgroup { grid-template-columns: 120px auto; }
    body.vip-cockpit .vip-fthumb { flex: 0 0 92px; }
    body.vip-cockpit .vip-frows { max-width: none; padding-left: 14px; }
    body.vip-cockpit .vip-frow { flex: 0 0 100%; }
}

/* ══════════════════════════════════════════════════════════════════════
   DATA HUB — dataset cards + API call-out  (cockpit only)
   #data_interface lists the downloadable datasets (property data, valuations,
   rent roll, capex). Each dataset is a card carrying a coloured head (icon +
   title, matching .box-header), a description and a footer that pairs the
   record count with the button opening the datatable overlay. The cards sit in
   a wrapping grid, so adding or removing a dataset needs no row bookkeeping.
   ══════════════════════════════════════════════════════════════════════ */
body.vip-cockpit .vip-dhub {
    width: 100%;
    max-width: 860px;
    margin: 0 auto;
    text-align: left;
}
/* Two columns on desktop; the auto-fit collapses to one on narrow viewports. */
body.vip-cockpit .vip-dcards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 14px;
}
body.vip-cockpit .vip-dcard {
    display: flex;
    flex-direction: column;
    background: var(--vip-white);
    border: 1px solid var(--box-border);
    border-radius: 10px;
    overflow: hidden;
}
/* Card head: mirrors .box-header (same surface, same foreground) so the hub
   reads as part of the same family of boxes. */
body.vip-cockpit .vip-dhead {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 9px 14px;
    background: var(--box-head-bg);
    color: var(--box-head-fg);
    border-bottom: 1px solid var(--box-head-rule);
}
body.vip-cockpit .vip-dicon {
    font-size: 15px;    
}
body.vip-cockpit .vip-dtitle {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .5px;
    text-transform: uppercase;
}
/* Description: fills the free space so all footers line up across the row. */
body.vip-cockpit .vip-dbody {
    flex: 1 1 auto;
    padding: 12px 14px;
    font-size: 12px;
    line-height: 1.45;
    color: var(--vip-steel);
}
body.vip-cockpit .vip-dfoot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 10px 14px;
    border-top: 1px solid var(--box-border);
}
/* Record count as the card's headline figure rather than a footer table row. */
body.vip-cockpit .vip-dcount {
    display: flex;
    align-items: baseline;
    gap: 5px;
    min-width: 0;
}
body.vip-cockpit .vip-dcount-value {
    font-size: 19px;
    font-weight: 600;
    color: var(--vip-navy);
}
body.vip-cockpit .vip-dcount-label {
    font-size: 11px;
    color: var(--vip-steel);
}
/* Empty dataset: the whole card recedes and the button reads as unavailable. */
body.vip-cockpit .vip-dcard.is-empty .vip-dhead {
    background: var(--vip-steel);
    border-bottom-color: var(--vip-steel);
}
body.vip-cockpit .vip-dcard.is-empty .vip-dicon { color: var(--vip-white); }
body.vip-cockpit .vip-dcard.is-empty .vip-dcount-value { color: var(--vip-steel); }
body.vip-cockpit .vip-dfoot .downloadbutton[disabled] {
    background: var(--box-bg);
    border-color: var(--box-border);
    color: var(--vip-navy-40);
    cursor: default;
}
body.vip-cockpit .vip-dfoot .downloadbutton[disabled]:hover,
body.vip-cockpit .vip-dfoot .downloadbutton[disabled]:focus {
    background: var(--box-bg);
    border-color: var(--box-border);
    color: var(--vip-navy-40);
}

/* ══════════════════════════════════════════════════════════════════════
   SHARED CALL-OUT  — .vip-callout
   The one way the cockpit highlights a notice: a quiet strip on the warm
   surface with a yellow edge, an icon, an optional title and one or more
   paragraphs. Every place that needs to qualify what a figure means uses
   it — the Data hub, the analysis-date dialogue, the two reversion panels
   of the property overlay, and the vacancy board. Carries no outer margin
   of its own; the surrounding layout owns the spacing.
   ══════════════════════════════════════════════════════════════════════ */
body.vip-cockpit .vip-callout {
    padding: 8px 16px;
    background: var(--vip-cream-50);
    border: 1px solid var(--box-border);
    border-left: 3px solid var(--vip-yellow);
    /* Same radius as the boxes it sits among, so a notice reads as part of the
       surface rather than as something pasted onto it. */
    border-radius: 8px;
    text-align: left;
}
/* The permanently visible line: icon, title, and the close control pushed to the
   far edge. Centred against each other, because collapsed this row is the whole
   notice and anything else reads as a misalignment. */
body.vip-cockpit .vip-callout-head {
    display: flex;
    align-items: center;
    gap: 12px;
}
body.vip-cockpit .vip-callout-icon {
    /* Fixed column rather than intrinsic width: the icons differ in aspect
       (fa-layer-group is half again as wide as fa-plug) and the copy below has to
       start at the same place whichever one a notice carries. */
    flex: 0 0 20px;
    text-align: center;
    font-size: 17px;
    color: var(--box-head-bg);
}
body.vip-cockpit .vip-callout-title {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--vip-navy);
}
body.vip-cockpit .vip-callout-body { min-width: 0; }
body.vip-cockpit .vip-callout-body p {
    margin: 0 0 6px;
    font-size: 12px;
    line-height: 1.5;
    color: var(--vip-navy-70);
}
body.vip-cockpit .vip-callout-body p:last-child { margin-bottom: 0; }
/* Some notices enumerate the members of a category. The list inherits the paragraph
   voice and sits indented under the sentence that introduces it. */
body.vip-cockpit .vip-callout-body ul {
    margin: 0 0 6px;
    padding-left: 16px;
    font-size: 12px;
    line-height: 1.5;
    color: var(--vip-navy-70);
}
body.vip-cockpit .vip-callout-body ul:last-child { margin-bottom: 0; }
body.vip-cockpit .vip-callout-body ul li { margin: 0 0 2px; }
body.vip-cockpit .vip-callout-body ul li:last-child { margin-bottom: 0; }
/* Two definitions that are read against each other rather than in sequence sit in a
   pair of columns, divided by a hairline. Below the cockpit's narrow breakpoint the
   pair stacks and the divider moves above the second block. */
body.vip-cockpit .vip-callout-cols {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: 22px;
    margin: 0 0 6px;
}
body.vip-cockpit .vip-callout-cols:last-child { margin-bottom: 0; }
body.vip-cockpit .vip-callout-col { min-width: 0; }
body.vip-cockpit .vip-callout-cols > .vip-callout-col + .vip-callout-col {
    padding-left: 22px;
    border-left: 1px solid var(--vip-navy-20);
}
body.vip-cockpit .vip-callout-colhead {
    margin: 0 0 4px;
    font-size: 12px;
    font-weight: 600;
    color: var(--vip-navy);
}
@media (max-width: 767.98px) {
    body.vip-cockpit .vip-callout-cols { grid-template-columns: 1fr; row-gap: 10px; }
    body.vip-cockpit .vip-callout-cols > .vip-callout-col + .vip-callout-col {
        padding-left: 0;
        padding-top: 10px;
        border-left: 0;
        border-top: 1px solid var(--vip-navy-20);
    }
}
body.vip-cockpit .vip-callout-body a {
    color: var(--vip-navy);
    font-weight: 600;
    text-decoration: underline;
}
body.vip-cockpit .vip-callout-body a:hover { color: var(--box-head-rule); }
/* A call-out link that acts on the cockpit rather than navigating carries no href —
   an href would announce a URL in the status bar that leads nowhere — so the pointer
   has to be restored by hand. */
body.vip-cockpit .vip-callout-body a[role="button"] { cursor: pointer; }
/* Data hub: the call-out follows the dataset cards and needs air above it. */
body.vip-cockpit .vip-dhub .vip-callout { margin-top: 16px; }
/* Modifier for a call-out that leads the block it qualifies rather than closing it. */
body.vip-cockpit .vip-callout.vip-callout-lead { margin-bottom: 10px; }

/* ── Collapsed until asked for ─────────────────────────────────────────
   A notice earns its place by being available, not by being read every time.
   Collapsed, only the head is on show; a click on the notice opens it and a
   second click closes it again. Hover was the first attempt and was wrong: a
   notice that opens on the way past reflows the screen under a cursor that was
   going somewhere else.

   The detail is a single-row grid animated from 0fr to 1fr rather than through a
   max-height: it opens to its own height, so the timing is the same whether the
   notice runs to one paragraph or five, and no guessed ceiling can clip a longer
   one. The child needs overflow:hidden and min-height:0 or the row refuses to go
   below its content height.

   The copy is indented to the icon column so it starts under the title rather
   than under the icon. The horizontal padding sits on the grid container, where
   it is outside the animated track; the gap above the copy sits on the child,
   where it is clipped away with the copy itself. */
body.vip-cockpit .vip-callout-detail {
    display: grid;
    grid-template-rows: 0fr;
    padding-left: 32px;
    transition: grid-template-rows 220ms ease;
}
body.vip-cockpit .vip-callout-detail > * {
    overflow: hidden;
    min-height: 0;    
}
body.vip-cockpit .vip-callout.is-open .vip-callout-detail { grid-template-rows: 1fr; }
body.vip-cockpit .vip-callout[tabindex] { cursor: pointer; }
body.vip-cockpit .vip-callout[tabindex]:focus { outline: none; }
body.vip-cockpit .vip-callout[tabindex]:focus-visible {
    outline: 2px solid var(--vip-yellow);
    outline-offset: 2px;
}

/* The affordance, trailing the title. The shared .vip-chevron ornament of the
   drill-down boards and the map viewer, so one convention covers everything that
   folds open. */
body.vip-cockpit .vip-callout-title .vip-chevron {
    margin-left: 8px;
    color: var(--vip-navy);
    font-size: 11px;
}

/* Session dismissal. The control is quiet until the notice is looked at, then
   states itself - it removes something the reader may want back, and the way back
   is a page reload. */
body.vip-cockpit .vip-callout-close {
    flex: 0 0 auto;
    padding: 0 4px;
    border: 0;
    background: none;
    color: var(--vip-navy-40);
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    transition: color 120ms ease;
}
body.vip-cockpit .vip-callout:hover .vip-callout-close { color: var(--vip-navy-70); }
body.vip-cockpit .vip-callout-close:hover { color: var(--vip-navy); }
body.vip-cockpit .vip-callout-close:focus { outline: none; }
body.vip-cockpit .vip-callout-close:focus-visible {
    outline: 2px solid var(--vip-yellow);
    outline-offset: 1px;
}

/* Modifier for a notice that must be legible the moment it appears: the Data hub
   card list, where the notice is the only prose on the screen, and the
   analysis-date dialogue, where it is the answer the reader opened the dialogue
   to find. Neither carries a caret, a close control or a tabindex. */
body.vip-cockpit .vip-callout.vip-callout-static .vip-callout-detail {
    grid-template-rows: 1fr;
    transition: none;
}

@media (prefers-reduced-motion: reduce) {
    body.vip-cockpit .vip-callout-detail { transition: none; }
}

/* ============================================================
   Sub-sidebar settings — grouped sections
   The four controls split into "What to compare" / "How to
   show it", with a directional cue between the two dates.
   ============================================================ */
body.vip-cockpit .vip-subsidebar .vip-settings-section {
	margin: 0 0 8px;
	text-align: left;
	font-size: 10px;
	font-weight: 600;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--vip-steel);
}

/* Section headers that carry an inline all/none control (Cluster values panels): the
   .vff-allnone pair reuses the same select-all/select-none buttons the global multi-filter
   facet rail already carries, so a "Select all" / "Select none" click here means the same
   thing it means there. Un-set text-transform/letter-spacing on the icons only - the label
   itself keeps the section heading's own styling. */
body.vip-cockpit .vip-subsidebar .vip-settings-section.vip-settings-section-actions {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
}
body.vip-cockpit .vip-subsidebar .vip-settings-section-actions .vff-allnone {
	text-transform: none;
	letter-spacing: normal;
	flex: 0 0 auto;
}

body.vip-cockpit .vip-subsidebar .vip-settings-arrow {
	display: flex;
	align-items: center;
	gap: 8px;
	margin: 10px 0;
	padding: 0 28px;
	font-size: 10px;
	color: var(--vip-steel);
}

body.vip-cockpit .vip-subsidebar .vip-settings-arrow::before,
body.vip-cockpit .vip-subsidebar .vip-settings-arrow::after {
	content: "";
	flex: 1;
	height: 1px;
	background: #b7bdc6;
}

body.vip-cockpit .vip-subsidebar .vip-settings-arrow-glyph {
	margin-right: 4px;
	font-weight: 600;
	color: var(--vip-steel);
}

body.vip-cockpit .vip-subsidebar .vip-settings-divider {
	margin: 16px -12px 12px;
	border-top: 1px solid #c6cbd3;
}

/* Lighten and left-align the field labels so the panel reads as a calm
   single column rather than four centred, bold blocks. */
body.vip-cockpit .vip-subsidebar .btn-group-header {
	margin: 0 0 4px;
	text-align: left;
	font-size: 12px;
	font-weight: 500;
	color: var(--vip-navy);
}

/* Central rule: every dropdown in the sub-sidebar shares the panel's
   light language, whatever legacy class the select carries. */
body.vip-cockpit .vip-subsidebar select.form-control {
	background-color: #ffffff;
	border: 1px solid var(--box-border);
	border-radius: 6px;
	color: var(--vip-navy);
}

body.vip-cockpit .vip-subsidebar select.form-control:focus {
	background-color: #ffffff;
	color: var(--vip-navy);
}

/* Grouped options: the engine's flat optgroup headers render inside the
   native list; keep them upright and quiet (deeper styling of the open
   list is browser-limited). */
body.vip-cockpit .vip-subsidebar select.form-control optgroup {
	font-style: normal;
	font-weight: 600;
	color: var(--vip-steel);
}


body.vip-cockpit .vip-subsidebar .btn-group-header .text-muted {
	margin-top: 1px;
}


/* ============================================================
   Generic dashboard sub-sidebar
   A light-grey panel that docks flush against the navy main nav
   (below the top bar) and fills the full height, carrying a
   dashboard's sub-navigation and settings. The section itself is
   the flex row; the content area scrolls independently. Collapses
   fully behind a slim reveal handle; reusable via the .vip-* names.
   ============================================================ */
body.vip-cockpit .vip-dashboard {
	display: flex;
	align-items: stretch;
	height: 100%;
	min-height: 100%;
	margin: 0;
	padding: 0;
	overflow: hidden;
}

body.vip-cockpit .vip-subsidebar {
	display: flex;
	flex-direction: column;
	flex: 0 0 var(--vip-subsidebar-w);
	width: var(--vip-subsidebar-w);
	margin-left: 0;
	background: var(--vip-subsidebar-bg);
	border-right: 1px solid var(--box-border);
	padding: 12px;
	overflow-y: auto;
	-webkit-transition: margin-left .3s ease;
	transition: margin-left .3s ease;
}

/* Footnote pinned to the lower end of the panel (scrolls with the
   content when the settings exceed the viewport). */
body.vip-cockpit .vip-subsidebar-note {
	margin-top: auto;
	padding-top: 12px;
	font-size: 11px;
	line-height: 1.4;
	color: var(--secondary);
}

body.vip-cockpit .vip-dashboard-main {
	flex: 1 1 auto;
	min-width: 0;
	height: 100%;
	overflow-y: auto;
	display: flex;
	justify-content: safe center;
	align-items: safe center;
    padding: 16px;
}

/* Property list panes: stretch to the dashboard's content height (inside
   its padding) and let the grid take whatever the toolbar leaves over.
   The hidden state must beat the flex display, hence the explicit rule. */
body.vip-cockpit #properties_list .vip-plist-pane {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    align-self: stretch;
    min-width: 0;
    min-height: 0;
}
body.vip-cockpit #properties_list .vip-plist-pane.hidden { display: none; }
body.vip-cockpit #properties_list .vip-plist-grid {
    flex: 1 1 auto;
    min-height: 0;
    width: 100%;
}

/* The head opens the darker navigation band; it bleeds into the panel's
   padding so the tint runs edge to edge. A following .vip-subnav continues
   the band seamlessly; without one the band closes as a plain header strip
   above the settings' hairline. */
body.vip-cockpit .vip-subsidebar-head {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 8px;
	margin: -12px -12px 0;
	padding: 12px 12px 10px;
	background: var(--vip-subnav-bg);
}

body.vip-cockpit .vip-subsidebar-title {
	font-size: 13px;
	font-weight: 600;
	line-height: 1.3;
	color: var(--vip-navy);
}

body.vip-cockpit .vip-subsidebar-toggle {
	background: transparent;
	border: 0;
	padding: 2px 4px;
	color: var(--vip-steel); /* readable on the darker band */
	cursor: pointer;
	font-size: 13px;
}

body.vip-cockpit .vip-subsidebar-toggle:hover {
	color: var(--vip-navy);
}

/* Sub-navigation: one calm column on the darker band, active item lifted
   to white with the Savills yellow accent bar to echo the main nav's
   active state. Continues the head's tint without a visible seam. */
body.vip-cockpit .vip-subnav {
	display: flex;
	flex-direction: column;
	gap: 2px;
	margin: 0 -12px 12px;	
	background: var(--vip-subnav-bg);
	border-bottom: 1px solid #c6cbd3;
}

body.vip-cockpit .vip-subnav-item {
	display: block;
	width: 100%;
	text-align: left;
	background: transparent;
	border: 0;
	border-left: 3px solid transparent;	
	padding: 7px 10px;
	font-size: 13px;
	color: var(--vip-navy-70); /* darker than steel for contrast on the band */
	cursor: pointer;
}

/* Leading chevron before the label, echoing the main nav's submenu
   endpoints (.sidebar-submenu li a:before). The glyph carries no colour of
   its own, so it inherits the row colour and turns yellow on the active item
   alongside the label. A Unicode ornament is used rather than a Font Awesome
   glyph because the kit runs in SVG+JS mode (js/all.js), where CSS
   pseudo-element icons would not render. */
body.vip-cockpit .vip-subnav-item::before {
	content: "\2771"; /* heavy right-pointing angle bracket ornament */
	display: inline-block;
	margin-right: 10px;
	font-size: 16px;
	font-style: normal;
	text-align: center;
	text-decoration: none;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

body.vip-cockpit .vip-subnav-item:hover {
	background: rgba(255, 255, 255, 0.5);
}

body.vip-cockpit .vip-subnav-item.active {
	background:var(--vip-navy-90);
	border-left-color: var(--vip-yellow);
	color: var(--vip-yellow);	
}

/* Settings sit below the sub-nav, separated by a hairline. */
body.vip-cockpit .vip-subsidebar-settings {	
	padding-top: 10px;
}

/* Contextual controls (e.g. Group by) are hidden on tabs that do not use them. */
body.vip-cockpit .vip-setting-contextual.hidden {
	display: none !important;
}

/* Only the active tab panel is shown; the rest stay in the DOM. */
body.vip-cockpit .vip-tab-panel[hidden] {
	display: none;
}

/* Tab panels are flex items of the centring dashboard body; without a
   width they shrink-wrap and auto-fill grids collapse to one column,
   so panels always claim the full content width. */
body.vip-cockpit .vip-tab-panel {
	width: 100%;
	min-width: 0;
}

/* Reveal handle: hidden until the sub-sidebar is collapsed. */
body.vip-cockpit .vip-subsidebar-reveal {
	display: none;
	flex: 0 0 22px;
	width: 22px;
	align-self: stretch;
	align-items: center;
	justify-content: center;
	background: var(--vip-subsidebar-bg);
	border: 0;	
	color: var(--vip-muted);
	cursor: pointer;
	font-size: 13px;
}

body.vip-cockpit .vip-subsidebar-reveal:hover {
	color: var(--vip-navy);
}

body.vip-cockpit .vip-dashboard.subsidebar-collapsed .vip-subsidebar {
	margin-left: calc(-1 * var(--vip-subsidebar-w) - 1px); /* incl. border */
}

body.vip-cockpit .vip-dashboard.subsidebar-collapsed .vip-subsidebar-reveal {
	display: flex;
}

/* ============================================================
   Sidebar option lists — stacked single / multi selects
   Comparability renders as checkboxes, Group by as radio options,
   built from and driving the native selects, which stay hidden as
   the source of truth.
   ============================================================ */
/* Central rule: any source control mirrored by a .vip-optionlist carries
   .vip-source-hidden and stays in the DOM as the source of truth. The
   selectpicker pair covers the engine-created bootstrap-select wrapper. */
body.vip-cockpit .vip-subsidebar select.selectpicker,
body.vip-cockpit .vip-subsidebar .bootstrap-select,
body.vip-cockpit .vip-subsidebar .vip-source-hidden,
body.vip-cockpit .vip-cmx-dim .vip-source-hidden {
	display: none !important;
}

body.vip-cockpit .vip-optionlist {
	margin-top: 2px;
}

/* Cap the Cluster values checkbox list at eight rows; longer cluster
   lists then scroll internally instead of overrunning the sub-sidebar.
   Row height mirrors .vip-option: 16px mark + 14px vertical padding +
   2px border + 4px row gap = 36px. */
body.vip-cockpit .vip-optionlist[data-vip-mode="checkbox"][data-vip-optionlist-for$="clustervalues"] {
	--vip-option-row-h: 36px;
	max-height: calc(var(--vip-option-row-h) * 8);
	overflow-y: auto;
	overscroll-behavior: contain;
	/* Firefox scrollbar styling. */
	scrollbar-width: thin;
	scrollbar-color: var(--vip-navy-40) transparent;
}

/* Slim scrollbar so the capped list stays visually quiet in the rail. The
   thumb sits a few steps darker than the row borders so it reads clearly
   against the light sub-sidebar surface. */
body.vip-cockpit .vip-optionlist[data-vip-mode="checkbox"][data-vip-optionlist-for$="clustervalues"]::-webkit-scrollbar {
	width: 8px;
}
body.vip-cockpit .vip-optionlist[data-vip-mode="checkbox"][data-vip-optionlist-for$="clustervalues"]::-webkit-scrollbar-track {
	background: transparent;
}
body.vip-cockpit .vip-optionlist[data-vip-mode="checkbox"][data-vip-optionlist-for$="clustervalues"]::-webkit-scrollbar-thumb {
	background: var(--vip-navy-40);
	border-radius: 6px;
	border: 2px solid transparent;
	background-clip: padding-box;
}
body.vip-cockpit .vip-optionlist[data-vip-mode="checkbox"][data-vip-optionlist-for$="clustervalues"]::-webkit-scrollbar-thumb:hover {
	background: var(--vip-navy-60);
	background-clip: padding-box;
}

body.vip-cockpit .vip-optiongroup-header {
	margin: 8px 0 4px 2px;
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.04em;
	color: var(--vip-muted);
}

body.vip-cockpit .vip-optiongroup-header:first-child {
	margin-top: 0;
}

body.vip-cockpit .vip-option {
	display: flex;
	align-items: center;
	gap: 9px;
	margin-bottom: 4px;
	padding: 7px 9px;
	border: 1px solid var(--box-border);
	border-radius: 6px;
	background: transparent;
	font-size: 11px;
	color: var(--vip-steel);
	cursor: pointer;
}

body.vip-cockpit .vip-option:hover {
	background: rgba(255, 255, 255, 0.5);
}

body.vip-cockpit .vip-option.is-selected {
	background: #ffffff;
	color: var(--vip-navy);
}

body.vip-cockpit .vip-option:focus-visible {
	outline: 2px solid var(--vip-steel);
	outline-offset: 1px;
}

body.vip-cockpit .vip-option-label {
	flex: 1 1 auto;
}

body.vip-cockpit .vip-option-count {
	font-size: 11px;
	color: var(--vip-muted);
}

/* Mark: checkbox square (multi-select) */
body.vip-cockpit .vip-optionlist[data-vip-mode="checkbox"] .vip-option-mark {
	position: relative;
	width: 16px;
	height: 16px;
	flex-shrink: 0;
	border: 1px solid #b7bdc6;
	border-radius: 4px;
	background: #ffffff;
}

body.vip-cockpit .vip-optionlist[data-vip-mode="checkbox"] .vip-option.is-selected .vip-option-mark {
	border-color: var(--vip-navy);
	background: var(--vip-navy);
}

body.vip-cockpit .vip-optionlist[data-vip-mode="checkbox"] .vip-option.is-selected .vip-option-mark::after {
	content: "";
	position: absolute;
	left: 5px;
	top: 2px;
	width: 4px;
	height: 8px;
	border: solid #ffffff;
	border-width: 0 2px 2px 0;
	transform: rotate(45deg);
}

/* Mark: radio circle (single-select) */
body.vip-cockpit .vip-optionlist[data-vip-mode="radio"] .vip-option-mark {
	position: relative;
	width: 16px;
	height: 16px;
	flex-shrink: 0;
	border: 1px solid #b7bdc6;
	border-radius: 50%;
	background: #ffffff;
}

body.vip-cockpit .vip-optionlist[data-vip-mode="radio"] .vip-option.is-selected .vip-option-mark {
	border-color: var(--vip-navy);
}

body.vip-cockpit .vip-optionlist[data-vip-mode="radio"] .vip-option.is-selected .vip-option-mark::after {
	content: "";
	position: absolute;
	left: 4px;
	top: 4px;
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--vip-navy);
}

/* ============================================================
   Sidebar custom dropdowns — vip-dropdown
   A light trigger plus a grouped popover panel, mirroring a
   hidden native select (the source of truth). Group headers
   reuse the option-list header language.
   ============================================================ */
body.vip-cockpit .vip-dropdown {
	position: relative;
}

body.vip-cockpit .vip-dropdown-trigger {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	width: 100%;
	padding: 7px 10px;
	background: #ffffff;
	border: 1px solid var(--box-border);
	border-radius: 6px;
	font-size: 11px;
	color: var(--vip-navy);
	cursor: pointer;
	text-align: left;
}

body.vip-cockpit .vip-dropdown-trigger .fas,
body.vip-cockpit .vip-dropdown-trigger .svg-inline--fa {
	font-size: 11px;
	color: var(--vip-muted);
}

body.vip-cockpit .vip-dropdown-trigger[aria-expanded="true"] {
	border-color: var(--vip-navy);
}

body.vip-cockpit .vip-dropdown-trigger[aria-expanded="true"] .fa-chevron-down {
	transform: rotate(180deg);
}

body.vip-cockpit .vip-dropdown-trigger:focus-visible {
	outline: 2px solid var(--vip-steel);
	outline-offset: 1px;
}

body.vip-cockpit .vip-dropdown-panel {
	position: absolute;
	left: 0;
	right: 0;
	top: calc(100% + 4px);
	z-index: 40;
	padding: 4px;
	background: #ffffff;
	border: 1px solid #c6cbd3;
	border-radius: 8px;
	max-height: 280px;
	overflow-y: auto;
}

body.vip-cockpit .vip-dropdown-panel .vip-optiongroup-header {
	margin: 4px 0 2px;
	padding: 4px 8px 2px;
}

body.vip-cockpit .vip-dropdown-panel .vip-optiongroup-header:not(:first-child) {
	margin-top: 6px;
	padding-top: 8px;
	border-top: 1px solid #e4e7eb;
}

body.vip-cockpit .vip-dropdown-option {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 8px;
	padding: 6px 8px;
	border-radius: 5px;
	font-size: 11px;
	color: var(--vip-steel);
	cursor: pointer;
}

body.vip-cockpit .vip-dropdown-option:hover {
	background: #f1f3f5;
}

body.vip-cockpit .vip-dropdown-option:focus-visible {
	outline: 2px solid var(--vip-steel);
	outline-offset: -2px;
}

body.vip-cockpit .vip-dropdown-option.is-selected {
	background: #E8EBEF; /* kept at the former panel grey for contrast on white */	
	border-radius: 5px;
	color: var(--vip-navy);	
}

/* Every row carries a tick; only the selected row shows it. Font
   Awesome's svg framework replaces the <i>, hence the paired selector. */
body.vip-cockpit .vip-dropdown-option .fas,
body.vip-cockpit .vip-dropdown-option .svg-inline--fa {
	display: none;
	font-size: 11px;
}

body.vip-cockpit .vip-dropdown-option.is-selected .fas,
body.vip-cockpit .vip-dropdown-option.is-selected .svg-inline--fa {
	display: inline-block;
}

/* Preset action rows: applied / modified status on the active preset. */
body.vip-cockpit .vip-preset-status {
	font-size: 10px;
	color: #0f6e56;
}

body.vip-cockpit .vip-preset-status.is-modified {
	color: var(--vip-muted);
	font-style: italic;
}

body.vip-cockpit .vip-preset-status .fas,
body.vip-cockpit .vip-preset-status .svg-inline--fa {
	font-size: 10px;
	margin-right: 3px;
}

/* -----------------------------------------------------------------------------
   RELETTING ASSUMPTIONS TABS  (cockpit only)
   -------------------------------------------------------------------------- */
body.vip-cockpit .vip-rla-center {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100%;
}
body.vip-cockpit .vip-rla-scroll { width: 100%; overflow-x: auto; }
body.vip-cockpit .vip-rla-placeholder,
body.vip-cockpit .vip-rla-empty {    
    text-align: center;    
    color: var(--vip-navy-60);
}
body.vip-cockpit .vip-rla-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
    table-layout: fixed;
}
body.vip-cockpit .vip-rla-table thead th {
    padding: 6px 8px;
    font-weight: 400;
    text-align: right;
    white-space: nowrap;
    vertical-align: bottom;
    background: var(--box-head-bg);
    color: var(--box-head-fg);
}
body.vip-cockpit .vip-rla-table thead th.vip-rla-use { text-align: left; }
body.vip-cockpit .vip-rla-table thead tr.vip-rla-units th {
    padding-top: 0;
    font-size: 11px;
    font-weight: 400;
    white-space: normal;
}
/* (d) the long factor label wraps within its column instead of overrunning the panel edge */
body.vip-cockpit .vip-rla-table thead th.vip-rla-total { white-space: normal; }
body.vip-cockpit .vip-rla-table tbody td {
    padding: 4px 8px;
    text-align: right;
    vertical-align: middle;
    border-bottom: 1px solid var(--box-border);
    background: var(--secondary_5);
}
body.vip-cockpit .vip-rla-table tbody tr:hover td { background: var(--dt-row-hover); }
body.vip-cockpit .vip-rla-table td.vip-rla-use {
    text-align: left;
    font-weight: 500;
    color: var(--vip-navy);
}
body.vip-cockpit .vip-rla-table tr.vip-rla-block td,
body.vip-cockpit .vip-rla-table tr.vip-rla-block:hover td {
    padding: 6px 8px;
    text-align: left;
    font-weight: 600;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: white;
    background: var(--secondary_50);
    border-bottom: 1px solid var(--box-border);
}
body.vip-cockpit .vip-rla-avg { font-weight: 600; color: var(--raspberry); }
body.vip-cockpit .vip-rla-mm {
    display: block;
    margin-top: 2px;
    font-size: 11px;
    color: var(--vip-navy-60);
}
body.vip-cockpit .vip-rla-cov {
    margin-left: 4px;
    font-size: 11px;
    color: var(--vip-steel);
}
body.vip-cockpit .vip-rla-total { color: var(--vip-navy); }
/* min-max range bar - dominant palette */
body.vip-cockpit .vip-rla-bar {
    position: relative;
    width: 72px;
    height: 6px;
    margin-top: 2px;
    margin-left: auto;
    border-radius: 3px;
    background: var(--vip-navy-20);
}
body.vip-cockpit .vip-rla-span {
    position: absolute;
    top: 0;
    height: 6px;
    border-radius: 3px;
    background: var(--vip-steel);
}
body.vip-cockpit .vip-rla-dot {
    position: absolute;
    top: -1.5px;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--raspberry);
    border: 1.5px solid var(--vip-white);
    transform: translateX(-4.5px);
}

body.vip-cockpit .vip-rla-na {
    font-size: 11px;
    font-style: italic;
    color: var(--vip-navy-40);
}

body.vip-cockpit .vip-rla-months {
    font-weight: 400;
    color: var(--vip-navy-60);
}

/* Open state of a notice; the ornament itself is the shared .vip-chevron of
   shared_base.css. */
body.vip-cockpit .vip-callout.is-open .vip-callout-title .vip-chevron { transform: rotate(90deg); }

/* drill-down: use type -> tenant -> rental unit (cockpit only) */
body.vip-cockpit .vip-rla-hide { display: none; }
body.vip-cockpit .vip-rla-caret {
    display: inline-block;
    width: 16px;
    margin-right: 4px;
    color: var(--vip-navy);
    font-size: 13px;
}
/* (c) 'Market rent' spanning group header (like 'Current rent' in Element 2) */
body.vip-cockpit .vip-rla-table thead th.vip-rla-grp { text-align: center; font-weight: 600;}
/* (g) property-total aggregate row */
body.vip-cockpit .vip-rla-table tr.vip-rla-grand td,
body.vip-cockpit .vip-rla-table tr.vip-rla-grand:hover td { background: var(--secondary_25); font-weight: 600; border-bottom: 2px solid var(--box-border); }
body.vip-cockpit .vip-rla-table tr.vip-rla-grand td.vip-rla-use { font-weight: 700; color: var(--vip-navy); }
body.vip-cockpit .vip-rla-table tr.vip-rla-usetop { cursor: pointer; }
body.vip-cockpit .vip-rla-table tr.vip-rla-usetop td {background: var(--secondary_10); }
body.vip-cockpit .vip-rla-table tr.vip-rla-usetop:hover td { background: var(--dt-row-hover); }
body.vip-cockpit .vip-rla-table tr.vip-rla-tenant { cursor: pointer; }
body.vip-cockpit .vip-rla-table tr.vip-rla-tenant td { background: var(--secondary_5); }
body.vip-cockpit .vip-rla-table tr.vip-rla-tenant:hover td { background: var(--dt-row-hover); }
body.vip-cockpit .vip-rla-table td.vip-rla-l1 { padding-left: 22px; font-weight: 500; }
body.vip-cockpit .vip-rla-table tr.vip-rla-unit td {
    background: var(--vip-white);
    border-bottom: 1px dashed var(--box-border);
}
body.vip-cockpit .vip-rla-table tr.vip-rla-unit:hover td { background: var(--dt-row-hover); }
body.vip-cockpit .vip-rla-table td.vip-rla-l2 {
    padding-left: 44px;
    font-weight: 400;
    color: var(--vip-navy-70);
}
body.vip-cockpit .vip-rla-unit-note {
    margin-left: 4px;
    font-size: 11px;
    font-style: italic;
    color: var(--vip-steel);
}
body.vip-cockpit .vip-rla-tip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 13px;
    height: 13px;
    margin-left: 6px;
    border-radius: 50%;
    border: 1px solid var(--vip-navy-40);
    color: var(--vip-navy);
    font-size: 9px;
    font-weight: 600;
    line-height: 1;
    cursor: help;
    vertical-align: middle;
}

/* market-rent share bar, tooltip surface, host & sticky two-line header (cockpit only) */
body.vip-cockpit .vip-rla-host { height: 100%; display: flex; flex-direction: column; padding: 12px 15px 6px; text-align: left; }
body.vip-cockpit .vip-rla-num { font-weight: 500; color: var(--vip-navy); white-space: nowrap; }
body.vip-cockpit .vip-rla-table thead th { position: sticky; top: 0; z-index: 5; }
body.vip-cockpit .vip-rla-table thead tr.vip-rla-units th { z-index: 4; }
body.vip-cockpit .vip-rla-table thead th.vip-rla-pct-head { text-align: center; }
body.vip-cockpit .vip-rla-table td.vip-rla-pcttd { min-width: 130px; }
/* footer aggregate row - shared by every table built on the vip-rla-* vocabulary
   (board, pooled-tenant modal). It closes the table the way the head opens it, so it
   adopts the head's surface and repeats its per-column alignment rather than the body's. */
body.vip-cockpit .vip-rla-table tfoot td {
    padding: 6px 8px;
    font-weight: 500;
    text-align: right;
    white-space: nowrap;
    vertical-align: middle;
    border-top: 1px solid var(--vip-steel);
    border-bottom: 0;
    background: var(--box-head-bg);
    color: var(--box-head-fg);
}
body.vip-cockpit .vip-rla-table tfoot td.vip-rla-use { text-align: left; color: var(--box-head-fg); }
body.vip-cockpit .vip-rla-pct { display: flex; align-items: center; gap: 6px; cursor: help; }
body.vip-cockpit .vip-rla-info { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 13px; height: 13px; border-radius: 50%; border: 1px solid var(--vip-navy-40); color: black; font-size: 9px; font-weight: 600; line-height: 1; }
body.vip-cockpit .vip-rla-pct-val { flex: 0 0 auto; min-width: 34px; text-align: left; font-size: 11px; color: var(--vip-navy-60); }
body.vip-cockpit .vip-rla-pct-bar { position: relative; flex: 1; min-width: 24px; height: 6px; background: var(--vip-navy-10); border-radius: 3px; overflow: hidden; }
body.vip-cockpit .vip-rla-pct-bar > span { position: absolute; right: 0; top: 0; display: block; height: 100%; background: var(--vip-navy-40); border-radius: 3px; }
body.vip-cockpit .vip-rla-pct-bar.lvl-total > span { background: var(--vip-navy); }
body.vip-cockpit .vip-rla-pct-bar.lvl-usetop > span { background: var(--vip-navy-70); }
body.vip-cockpit .vip-rla-pct-bar.lvl-tenant > span { background: var(--vip-navy-40); }
body.vip-cockpit .vip-rla-pct-bar.lvl-unit > span { background: var(--vip-navy-10); }

/* -----------------------------------------------------------------------------
   RELETTING ASSUMPTIONS - LEASE PANEL (Element 2, cockpit only)
   -------------------------------------------------------------------------- */
/* Element 2 fills the popup pane (.inner-wrapper) directly. The former chart/table tab
   wrapper was removed because the table view (Element 3) is not being built, so the panel
   is the only child and scrolls inside a simple padded flex host. */
body.vip-cockpit #nav-tab-content-reletting-leases .inner-wrapper { overflow:hidden; }
body.vip-cockpit .vip-rll-host { height:100%; display:flex; flex-direction:column; padding:12px 15px 6px; text-align:left; }
body.vip-cockpit #reletting_panel_leases { flex:1 1 auto; min-height:0; overflow:auto;}
/* legend, header and the property-total row travel together as one sticky block so the
   whole aggregate context stays in view while the tenant rows scroll underneath */
body.vip-cockpit .vip-rll-stickytop { position:sticky; top:0; z-index:6; min-width:max-content; background-color: white; }
body.vip-cockpit .vip-rll-legend { display:flex; flex-wrap:wrap; align-items:center; gap:6px 14px; font-size:11px; color:var(--vip-navy-70); margin-bottom:10px; }
body.vip-cockpit .vip-rll-legend span { display:inline-flex; align-items:center; gap:6px; }
body.vip-cockpit .vip-rll-sw { display:inline-block; width:16px; height:8px; border-radius:4px; }
body.vip-cockpit .vip-rll-vm { display:inline-block; width:2px; height:11px; border-radius:1px; }
/* 13-column grid: use | let area/units | lease expiry | applied expiry | % of asset | current rent | market rent | void | rent-free | TI | agency | cost factor | projection */
body.vip-cockpit .vip-rll-row { display:grid; grid-template-columns:128px 62px 70px 70px 104px 76px 76px 60px 60px 80px 66px 66px minmax(320px,1fr); gap:2px; align-items:center; min-width:max-content; }
/* two-level header: main row spans groups, sub row carries units/labels */
body.vip-cockpit .vip-rll-headwrap { position:sticky; top:0; z-index:5; background:var(--box-head-bg); }
body.vip-cockpit .vip-rll-head { font-size:11px; color:var(--box-head-fg); font-weight:400; }
body.vip-cockpit .vip-rll-head-main { padding:6px 0 0; }
body.vip-cockpit .vip-rll-head-sub { padding:2px 0 6px; align-items:end; }
/* header cells inherit the head foreground; unit-style labels step back like the
   rla units row */
body.vip-cockpit .vip-rll-head .vip-rll-lbl,
body.vip-cockpit .vip-rll-head .vip-rll-use,
body.vip-cockpit .vip-rll-head .vip-rll-area,
body.vip-cockpit .vip-rll-head .vip-rll-date,
body.vip-cockpit .vip-rll-head .vip-rll-num { color:var(--box-head-fg); }
body.vip-cockpit .vip-rll-head .vip-rll-unit,
body.vip-cockpit .vip-rll-head .vip-rll-pct-head,
body.vip-cockpit .vip-rll-head .vip-rll-yl,
body.vip-cockpit .vip-rll-head .vip-rll-yb { color:var(--vip-navy-30); }
body.vip-cockpit .vip-rll-head-sub .vip-rll-track { height:16px; }
body.vip-cockpit .vip-rll-h-tenant { grid-column:1 / 5; text-align:left; font-weight:600; color:var(--box-head-fg); padding-left:4px; }
body.vip-cockpit .vip-rll-h-cur { grid-column:5 / 7; text-align:center; font-weight:600; color:var(--box-head-fg); }
body.vip-cockpit .vip-rll-h-mkt { grid-column:7 / 8; text-align:right; font-weight:600; color:var(--box-head-fg); }
body.vip-cockpit .vip-rll-h-cost { text-align:center; font-weight:600; font-size:11px; line-height:1.1; color:var(--box-head-fg); white-space:normal; }
body.vip-cockpit .vip-rll-h-proj { grid-column:13 / 14; text-align:left; font-weight:600; color:var(--box-head-fg); padding-left:4px; }
body.vip-cockpit .vip-rll-h-factor { grid-column:12 / 13; text-align:center; font-weight:600; color:var(--box-head-fg); }
body.vip-cockpit .vip-rll-h-factor-sub { text-align:center; font-size:11px; }
body.vip-cockpit .vip-rll-lbl { font-size:12px; text-align:left; color:var(--vip-navy); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
body.vip-cockpit .vip-rll-lbl-span { grid-column:1 / 5; padding-left:4px; }
body.vip-cockpit .vip-rll-use { font-size:11px; text-align:left; color:var(--vip-navy-80); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; padding-left: 20px;}
body.vip-cockpit .vip-rll-area { font-size:11px; color:var(--vip-navy-60); text-align:right; white-space:nowrap; }
body.vip-cockpit .vip-rll-date { font-size:11px; color:var(--vip-navy-60); text-align:right; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; text-align: center;}
body.vip-cockpit .vip-rll-num { font-size:12px; text-align:right; color:var(--vip-navy); white-space:nowrap; }
body.vip-cockpit .vip-rll-unit { font-weight:400; font-size:11px; color:var(--vip-navy-40); }
body.vip-cockpit .vip-rll-total { font-weight:600; text-align:center; }
/* current rent % of asset: share label (left) + right-to-left range-bar, normalised to the largest tenant */
body.vip-cockpit .vip-rll-pct { display:flex; align-items:center; gap:6px; cursor:help; }
body.vip-cockpit .vip-rll-info { flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center; width:13px; height:13px; border-radius:50%; border:1px solid var(--vip-navy-40); color:black; font-size:9px; font-weight:600; line-height:1; }
body.vip-cockpit .vip-rll-pct-head { font-size:11px; text-align:center; }
body.vip-cockpit .vip-rll-pct-val { font-size:11px; color:var(--vip-navy-60); min-width:34px; text-align:left; }
body.vip-cockpit .vip-rll-pct-bar { position:relative; flex:1; min-width:20px; height:6px; background:var(--vip-navy-10); border-radius:3px; overflow:hidden; }
body.vip-cockpit .vip-rll-pct-bar > span { position:absolute; right:0; top:0; display:block; height:100%; background:var(--vip-navy-40); border-radius:3px; }
body.vip-cockpit .vip-rll-pct-bar.lvl-object > span { background:var(--vip-navy); }
body.vip-cockpit .vip-rll-pct-bar.lvl-tenant > span { background:var(--vip-navy-70); }
body.vip-cockpit .vip-rll-pct-bar.lvl-lease > span { background:var(--vip-navy-40); }
body.vip-cockpit .vip-rll-pct-bar.lvl-unit > span { background:var(--vip-navy-20); }
body.vip-cockpit .vip-rll-object { padding:4px 0; background:var(--secondary_25); border-top:1px solid var(--box-border); border-bottom:1px solid var(--box-border); }
body.vip-cockpit .vip-rll-object .vip-rll-lbl { font-weight:600; }
body.vip-cockpit .vip-rll-object .vip-rll-track { height:auto; }  /* grows with the loss axis; curve height lives on .vip-rll-track-inner */
body.vip-cockpit .vip-rll-group { border-top:1px solid var(--box-border); }
body.vip-cockpit .vip-rll-tenant { padding:4px 0 6px; cursor:pointer; background:var(--secondary_10); }
body.vip-cockpit .vip-rll-tenant .vip-rll-lbl { font-weight:600; color:var(--vip-navy); }
body.vip-cockpit .vip-rll-caret { display:inline-block; width:16px; color:var(--vip-navy); font-size:13px; margin-right:4px; }
body.vip-cockpit .vip-rll-lease { padding:4px 0; border-top:0.5px solid var(--box-border); position:relative; cursor:pointer; }
body.vip-cockpit .vip-rll-lease::before { content:""; position:absolute; left:6px; top:0; bottom:0; width:2px; }
body.vip-cockpit .vip-rll-lease .vip-rll-use { padding-left:20px; }
body.vip-cockpit .vip-rll-unit-row .vip-rll-track { height:20px; }
body.vip-cockpit .vip-rll-group.is-collapsed .vip-rll-lgroup { display:none; }
/* third level: rental units under a lease. The lease row's own caret toggles them; no
   timeline is drawn (fields only) and the rows sit deeper than the lease row. */
body.vip-cockpit .vip-rll-lgroup.is-collapsed .vip-rll-unit-row { display:none; }
body.vip-cockpit .vip-rll-unit-row { padding:3px 0; border-top:0.5px dashed var(--box-border); background: white;}
body.vip-cockpit .vip-rll-unit-row .vip-rll-use { padding-left:44px; color:var(--vip-navy-70); }
body.vip-cockpit .vip-rll-tt:hover { background:var(--dt-row-hover); }
body.vip-cockpit .vip-rll-track { position:relative; height:26px; }
body.vip-cockpit .vip-rll-exp { position:absolute; left:0; top:0; width:100%; height:100%; }
body.vip-cockpit .vip-rll-exp-void { fill:var(--raspberry); }
body.vip-cockpit .vip-rll-exp-rf { fill:var(--raspberry_light); }
body.vip-cockpit .vip-rll-exp-vacvoid { fill:var(--menthol); }
body.vip-cockpit .vip-rll-exp-vacrf { fill:var(--fern); }
body.vip-cockpit .vip-rll-asof { position:absolute; top:0; bottom:0; border-left:1.5px dashed var(--vip-steel); }
/* per-calendar-year hover surfaces over the property-total projection (Element 2 tooltip b) */
body.vip-cockpit .vip-rll-yzone { position:absolute; top:0; bottom:0; cursor:help; z-index:2; }
/* (a) highlight the hovered calendar-year label in the sticky sub-header while its tooltip shows */
body.vip-cockpit .vip-rll-head .vip-rll-yl.is-hl,
body.vip-cockpit .vip-rll-head .vip-rll-yb.is-hl { color:var(--vip-yellow); font-weight:700; }
/* (b) highlight the hovered year column itself (test styling: --vip-yellow tint) */
body.vip-cockpit .vip-rll-yzone:hover { background:var(--vip-yellow); opacity:.3; }
/* (d) property-total projection: loss-axis band above the exposure curve (row grows taller) */
body.vip-cockpit .vip-rll-track-obj { display:flex; flex-direction:column; }
body.vip-cockpit .vip-rll-lossaxis { position:relative; height:15px; }
body.vip-cockpit .vip-rll-la-v { position:absolute; z-index:3; transform:translateX(-50%); top:1px; font-size:10px; color:var(--vip-navy-70); white-space:nowrap; }
body.vip-cockpit .vip-rll-track-inner { position:relative; flex:1 1 auto; height:60px; }
/* annual rental-loss overlay: dashed navy line with a soft white halo for legibility over the bands */
body.vip-cockpit .vip-rll-lossovl { position:absolute; left:0; top:0; width:100%; height:100%; z-index:3; pointer-events:none; }
body.vip-cockpit .vip-rll-lossline-halo { fill:none; stroke:var(--vip-white); stroke-width:3; stroke-opacity:.6; stroke-linejoin:round; stroke-linecap:round; }
body.vip-cockpit .vip-rll-lossline { fill:none; stroke:var(--vip-navy); stroke-width:1.5; stroke-dasharray:4 3; stroke-linejoin:round; stroke-linecap:round; }
/* muted sub-count appended to tenant / use-type labels (count of leases / tenant rows below) */
body.vip-cockpit .vip-rll-cnt { font-weight:400; color:var(--vip-navy-50); }
body.vip-cockpit .vip-rla-cnt { font-weight:400; color:var(--vip-navy-50); }
body.vip-cockpit .vip-rll-rail { position:absolute; bottom:6px; height:2.5px; border-radius:1px; }
body.vip-cockpit .vip-rll-cap { position:absolute; bottom:3px; width:2px; height:9px; transform:translateX(-50%); border-radius:1px; }
body.vip-cockpit .vip-rll-cap-s { transform:translateX(0); }
body.vip-cockpit .vip-rll-cap-e { transform:translateX(-100%); }
body.vip-cockpit .vip-rll-mk { position:absolute; top:3px; bottom:3px; width:2px; border-radius:1px; }
body.vip-cockpit .vip-rll-yl { position:absolute; top:0; transform:translateX(-50%); font-size:11px; color:var(--vip-navy-40); }
body.vip-cockpit .vip-rll-yb { position:absolute; top:0; transform:translateX(-50%); font-size:11px; font-style:italic; color:var(--vip-navy-40); }
body.vip-cockpit .vip-rll-mixed { color:var(--raspberry); font-weight:600; margin-left:1px; }
body.vip-cockpit .vip-rll-foot { margin-top:8px; font-size:11px; color:var(--vip-navy-60); }

/* Reversion cost grid (AG-Grid) - magnitude bar painted into the re-letting parameter
   cells. The bar fills from the right in proportion to the largest visible value; only
   the fill width is set inline (per row, background-image). Adjust the bar colour and
   its height here via the two custom properties. */
body.vip-cockpit {
    --vip-rev-barcolor: var(--vip-navy-50);
    --vip-rev-barheight: 8px;
}
body.vip-cockpit .vip-rev-bar {
    background-repeat: no-repeat;
    background-position: right bottom;
    background-size: 100% var(--vip-rev-barheight);
}

/* ══════════════════════════════════════════════════════════════════════
   MAP VIEWER EMBEDDING
   The map viewer iframe is injected by the cockpit with width/height only;
   without an explicit reset the browser default (2px inset border) draws a
   visible frame around the embedded application. display: block removes
   the trailing inline baseline gap as well.
   ══════════════════════════════════════════════════════════════════════ */
body.vip-cockpit #iframe_map_viewer {
    border: 0;
    display: block;
}

/* ══════════════════════════════════════════════════════════════════════
   UNIFIED TENANT TABLE (tenants tab)
   One DataTable with three blocks: current tenants, pre-let, and vacant
   use types. Band rows are injected per draw (see loadTenantElements);
   pre-let rows read muted since their figures describe an outlook, not
   the current state.
   ══════════════════════════════════════════════════════════════════════ */
/* band header mirrors .vip-rla-block so both block headers read as one family */
body.vip-cockpit #datatable_property_tenants tr.vip-tenant-band td,
body.vip-cockpit #datatable_property_tenants tr.vip-tenant-band:hover td {
    padding: 6px 8px;
    text-align: left;
    font-weight: 600;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: white;
    background: var(--secondary_50);
    border-bottom: 1px solid var(--box-border);
}
body.vip-cockpit #datatable_property_tenants tr.vip-tenant-band td.text-right {
    text-align: right;
}

/* second-level sub-band headers (pre-let / vacant use types) inside 'Current vacancy' */
body.vip-cockpit #datatable_property_tenants tr.vip-tenant-subband td,
body.vip-cockpit #datatable_property_tenants tr.vip-tenant-subband:hover td {
    padding: 4px 8px 4px 16px;
    text-align: left;
    font-weight: 600;
    font-size: 11px;
    color: var(--vip-navy, #25273A);
    background: var(--vip-navy-10, #E8E8EB);
    border-bottom: 1px solid var(--box-border);
}
body.vip-cockpit #datatable_property_tenants tr.vip-tenant-subband td.text-right {
    text-align: right;
    padding-left: 8px;
}

/* hover shading only on rows that open a tenant profile; nohover rows keep
   their zebra stripe (nth-of-type mirrors Bootstrap's own striping rule) */
body.vip-cockpit #datatable_property_tenants tbody tr.vip-row-nohover:hover {
    background-color: transparent;
}
body.vip-cockpit #datatable_property_tenants.table-striped tbody tr.vip-row-nohover:nth-of-type(odd):hover {
    background-color: rgba(0, 0, 0, .05);
}
body.vip-cockpit .vip-row-muted td { color: var(--vip-navy-50, #91929C); }

/* status pills inside the tenant column */
body.vip-cockpit .vip-pill {
    display: inline-block;
    font-size: .78em;
    line-height: 1.5;
    padding: 0 5px;
    border-radius: 2px;
    margin-left: 4px;
    vertical-align: 1px;
    font-weight: 600;
}
body.vip-cockpit .vip-pill--prelet {
    background: var(--vip-navy-10, #E8E8EB);
    color: var(--raspberry, #c6007e);
}
body.vip-cockpit .vip-pill--vacant {
    background: rgba(198, 0, 126, .08);
    color: var(--vip-navy, #25273A);
}

/* ══════════════════════════════════════════════════════════════════════
   GLOBAL SETTINGS MODAL — two-pane preferences dialog  (cockpit only)
   The dialog body is rebuilt on every open by createSettingsModal() into a
   left rail (category switch) and a set of panes. These rules re-skin the
   Bootstrap modal chrome and the bespoke .vip-set-* / .vip-fxr-* controls to
   match the cockpit surfaces (steel heads, navy/yellow accents). Scoped to
   #settings_modal so no other modal is affected.
   ══════════════════════════════════════════════════════════════════════ */
body.vip-cockpit #settings_modal .modal-content {
    border: 0;
    padding: 0;
    border-radius: 10px;
    overflow: hidden;
}
body.vip-cockpit #settings_modal .modal-header {
    background: var(--box-head-bg);
    border-bottom: 1px solid var(--box-head-rule);
    padding: 11px 16px;
}
body.vip-cockpit #settings_modal .modal-title {
    color: var(--box-head-fg);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .5px;
    text-transform: uppercase;
}
body.vip-cockpit #settings_modal .modal-header .close {
    color: var(--box-head-fg);
    opacity: .85;
    text-shadow: none;
}
body.vip-cockpit #settings_modal .modal-body.vip-settings-body { padding: 0; }
body.vip-cockpit #settings_modal .modal-footer {
    background: var(--box-chart-bg);
    border-top: 1px solid var(--box-border);
    padding: 10px 16px;
}
body.vip-cockpit #settings_modal .modal-footer .btn-primary {
    background: var(--vip-navy);
    border-color: var(--vip-navy);
    color: var(--vip-yellow);
    font-weight: 700;
}
body.vip-cockpit #settings_modal .modal-footer .btn-primary:hover { background: var(--vip-navy-90); }
body.vip-cockpit #settings_modal .modal-footer .btn-secondary {
    background: var(--vip-white);
    border-color: var(--vip-navy-20);
    color: var(--vip-navy-80);
}

/* ── shell: rail + panes ─────────────────────────────────────────────── */
body.vip-cockpit #settings_modal .vip-settings-shell {
    display: flex;
    margin: 0;
    height: 340px; /* fixed: switching panes must not resize the dialog */
}
body.vip-cockpit #settings_modal .vip-settings-rail {
    width: 176px;
    flex: 0 0 176px;
    background: var(--vip-subsidebar-bg);
    border-right: 1px solid var(--box-border);
    padding: 8px 0;
}
body.vip-cockpit #settings_modal .vip-settings-railitem {
    display: block;
    width: 100%;
    text-align: left;
    background: transparent;
    border: 0;
    padding: 9px 16px;
    font-size: 12px;
    color: var(--vip-navy-80);
    cursor: pointer;
}
body.vip-cockpit #settings_modal .vip-settings-railitem:hover { background: rgba(255, 255, 255, .55); }
body.vip-cockpit #settings_modal .vip-settings-railitem:focus,
body.vip-cockpit #settings_modal .vip-settings-railitem:focus-visible { outline: none; box-shadow: none; }
body.vip-cockpit #settings_modal .vip-settings-railitem.active {
    background: var(--vip-subnav-bg);
    color: var(--vip-navy);
    font-weight: 700;
    box-shadow: inset 3px 0 0 var(--vip-yellow);
}
body.vip-cockpit #settings_modal .vip-settings-panes { flex: 1 1 auto; padding: 16px; overflow-y: auto; }
body.vip-cockpit #settings_modal .vip-settings-pane { display: none; }
body.vip-cockpit #settings_modal .vip-settings-pane.active { display: block; }

/* ── generic control row ─────────────────────────────────────────────── */
body.vip-cockpit #settings_modal .vip-set-row {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
}
body.vip-cockpit #settings_modal .vip-set-label {
    width: 150px;
    flex: 0 0 150px;
    color: var(--vip-steel);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .3px;
    text-transform: uppercase;
}
body.vip-cockpit #settings_modal .vip-set-control { flex: 1 1 auto; }

/* ── segmented control (overrides Bootstrap .btn) ────────────────────── */
body.vip-cockpit #settings_modal .vip-seg {
    display: inline-flex;
    background: var(--vip-navy-10);
    border-radius: 6px;
    padding: 2px;
}
body.vip-cockpit #settings_modal .vip-seg.vip-seg-wrap { flex-wrap: wrap; gap: 2px; }

/* ── comparison dates stacked by year: one row per year ───────────────── */
body.vip-cockpit #settings_modal .vip-set-row:has(.vip-seg-years) { align-items: flex-start; }
body.vip-cockpit #settings_modal .vip-set-row:has(.vip-seg-years) .vip-set-label { padding-top: 5px; }
body.vip-cockpit #settings_modal .vip-seg-yearrow {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 6px;
}
body.vip-cockpit #settings_modal .vip-seg-year {
    width: 36px;
    flex: 0 0 36px;
    text-align: right;
    color: var(--vip-navy-80);
    font-size: 11px;
    font-weight: 700;
}
body.vip-cockpit #settings_modal .vip-seg-btn {
    margin: 0;
    border: 0;
    box-shadow: none;
    border-radius: 4px;
    min-width: 80px; /* uniform width across all segmented toggles */
    text-align: center;
    padding: 4px 14px;
    font-size: 12px;
    font-weight: 400;
    color: var(--vip-navy-80);
    background: transparent;
}
body.vip-cockpit #settings_modal .vip-seg-btn:hover { color: var(--vip-navy); background: rgba(255, 255, 255, .5); }
body.vip-cockpit #settings_modal .vip-seg-btn.active,
body.vip-cockpit #settings_modal .vip-seg-btn.active:hover {
    background: var(--box-head-bg);
    color: var(--vip-white);
    font-weight: 700;
    box-shadow: none;
}
body.vip-cockpit #settings_modal .vip-seg-btn:focus,
body.vip-cockpit #settings_modal .vip-seg-btn.focus,
body.vip-cockpit #settings_modal .vip-seg-btn:active { box-shadow: none; outline: none; }
/* Bootstrap hides toggle radios only for direct children of the data-toggle
   container (.btn-group-toggle > .btn input); the year-stacked comparison rows
   nest their labels one level deeper, so the inputs must be hidden here
   independently of nesting depth. */
body.vip-cockpit #settings_modal .vip-seg-btn input[type="radio"],
body.vip-cockpit #settings_modal .vip-fxr-chip input[type="radio"] {
    position: absolute;
    clip: rect(0, 0, 0, 0);
    pointer-events: none;
}
/* Every actionable element in the dialog carries the pointer cursor. */
body.vip-cockpit #settings_modal .vip-seg-btn,
body.vip-cockpit #settings_modal .vip-fxr-chip,
body.vip-cockpit #settings_modal .vip-settings-railitem,
body.vip-cockpit #settings_modal .vip-linkbtn,
body.vip-cockpit #settings_modal .modal-header .close,
body.vip-cockpit #settings_modal .modal-footer .btn { cursor: pointer; }
body.vip-cockpit #settings_modal .vip-seg-stack .vip-seg-btn { text-align: left; line-height: 1.35; }

/* ── currency: provided grid ─────────────────────────────────────────── */
body.vip-cockpit #settings_modal .vip-fxr-hint {
    color: var(--vip-steel);
    font-size: 11px;
    margin-bottom: 10px;
}
body.vip-cockpit #settings_modal .vip-fxr-chips {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
}
body.vip-cockpit #settings_modal .vip-fxr-chip {
    margin: 0;
    border: 1px solid var(--vip-navy-20);
    box-shadow: none;
    border-radius: 6px;
    padding: 8px 6px;
    text-align: center;
    background: var(--vip-white);
    color: var(--vip-navy-80);
    line-height: 1.3;
}
body.vip-cockpit #settings_modal .vip-fxr-chip .vip-fxr-ccy { display: block; font-weight: 700; }
body.vip-cockpit #settings_modal .vip-fxr-chip .fxr_detail { font-size: 10px; color: var(--vip-steel); }
body.vip-cockpit #settings_modal .vip-fxr-chip.active,
body.vip-cockpit #settings_modal .vip-fxr-chip.active:hover {
    background: var(--box-head-bg);
    border-color: var(--box-head-bg);
    color: var(--vip-white);
    box-shadow: none;
}
body.vip-cockpit #settings_modal .vip-fxr-chip.active .fxr_detail { color: rgba(255, 255, 255, .8); }

/* ── currency: custom editor ─────────────────────────────────────────── */
body.vip-cockpit #settings_modal .vip-fxr-editor-head,
body.vip-cockpit #settings_modal .vip-fxr-erow {
    display: grid;
    grid-template-columns: 1fr 130px;
    gap: 8px;
    align-items: center;
}
body.vip-cockpit #settings_modal .vip-fxr-editor-head {
    color: var(--vip-steel);
    font-size: 10px;
    letter-spacing: .3px;
    text-transform: uppercase;
    padding: 0 4px 6px;
}
body.vip-cockpit #settings_modal .vip-fxr-editor-head .vip-fxr-col-rate { text-align: right; }
body.vip-cockpit #settings_modal .vip-fxr-erow {
    background: var(--vip-white);
    border: 1px solid var(--vip-navy-20);
    border-radius: 6px;
    padding: 5px 8px;
    margin-bottom: 5px;
}
body.vip-cockpit #settings_modal .vip-fxr-erow .vip-fxr-col-ccy { font-weight: 400; }
body.vip-cockpit #settings_modal .vip-fxr-erow.is-base .vip-fxr-col-ccy { font-weight: 700; }
body.vip-cockpit #settings_modal .vip-fxr-basetag {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 6px;
    border-radius: 4px;
    background: var(--vip-navy-10);
    color: var(--vip-navy-80);
    font-size: 9px;
    font-weight: 700;
    letter-spacing: .3px;
    text-transform: uppercase;
    vertical-align: 1px;
}
body.vip-cockpit #settings_modal .vip-fxr-col-rate { text-align: right; }
body.vip-cockpit #settings_modal .vip-fxr-locked { color: var(--vip-steel); padding-right: 6px; }
body.vip-cockpit #settings_modal .vip-fxr-input {
    width: 112px;
    padding: 4px 8px;
    text-align: right;
    background: var(--vip-cream-25);
    border: 1px solid var(--vip-navy-40);
    border-radius: 4px;
    font-size: 12px;
    color: var(--vip-navy);
}
body.vip-cockpit #settings_modal .vip-fxr-input:focus {
    outline: none;
    border-color: var(--vip-navy-80);
    box-shadow: var(--vip-focus-ring);
}
body.vip-cockpit #settings_modal .vip-fxr-note {
    display: flex;
    gap: 8px;
    align-items: flex-start;
    background: #FFF9DE;
    border: 1px solid var(--vip-yellow-d);
    border-radius: 6px;
    padding: 8px 10px;
    margin-top: 12px;
    color: #6b5c00;
    font-size: 11px;
    line-height: 1.5;
}
body.vip-cockpit #settings_modal .vip-fxr-note i { color: #8a7400; margin-top: 1px; }
body.vip-cockpit #settings_modal .vip-fxr-reset { margin-top: 10px; }
body.vip-cockpit #settings_modal .vip-linkbtn {
    background: transparent;
    border: 0;
    padding: 0;
    color: var(--box-head-bg);
    font-size: 11px;
    cursor: pointer;
}
body.vip-cockpit #settings_modal .vip-linkbtn:hover { color: var(--vip-navy); text-decoration: underline; }

/* ── help icon + the dialog's self-contained popover ──────────────────── */
/* The icon is shared: the settings dialog and every sub-sidebar section
   heading use the same affordance. Only the presentation differs — the
   dialog draws its own bubble because the modal's stacking context sits
   above the central tooltip layer, while sub-sidebars route through
   bindGenericTooltip() like everything else. */
body.vip-cockpit #settings_modal .modal-content { position: relative; /* anchor for .vip-hint-pop */ }
body.vip-cockpit .vip-hint {
    display: inline-block;
    margin-left: 5px;
    color: var(--vip-navy-40);
    cursor: help;
    text-transform: none;
}
body.vip-cockpit .vip-hint:hover { color: var(--box-head-bg); }
body.vip-cockpit .vip-hint:focus,
body.vip-cockpit .vip-hint:focus-visible { outline: none; color: var(--box-head-bg); }
/* Prose body of a hint card drawn by the central tooltip. */
body.vip-cockpit .vip-hint-body { font-size: 11.5px; line-height: 1.55; }
body.vip-cockpit #settings_modal .vip-hint-pop {
    position: absolute;
    z-index: 10;
    max-width: 280px;
    background: var(--vip-navy);
    color: var(--vip-white);
    border-radius: 6px;
    padding: 7px 10px;
    font-size: 11px;
    font-weight: 400;
    letter-spacing: 0;
    text-transform: none;
    line-height: 1.5;
    box-shadow: 0 2px 8px rgba(37, 39, 58, .25);
    pointer-events: none;
}

/* ══════════════════════════════════════════════════════════════════════
   YOUR CONTACTS MODAL — role-grouped contact directory  (cockpit only)
   The body is rebuilt on open by getContacts() into role sections (valuation
   team / engagement lead / technical support). Chrome mirrors the settings
   modal; cards use initials avatars instead of photos. Scoped to
   #contacts_modal so no other modal is affected.
   ══════════════════════════════════════════════════════════════════════ */
body.vip-cockpit #contacts_modal .modal-content {
    border: 0;
    padding: 0;
    border-radius: 10px;
    overflow: hidden;
}
body.vip-cockpit #contacts_modal .modal-header {
    background: var(--box-head-bg);
    border-bottom: 1px solid var(--box-head-rule);
    padding: 11px 16px;
}
body.vip-cockpit #contacts_modal .modal-title {
    color: var(--box-head-fg);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .5px;
    text-transform: uppercase;
}
body.vip-cockpit #contacts_modal .modal-header .close {
    color: var(--box-head-fg);
    opacity: .85;
    text-shadow: none;
    cursor: pointer;
}
body.vip-cockpit #contacts_modal .modal-body { padding: 16px; }
body.vip-cockpit #contacts_modal .modal-footer {
    background: var(--box-chart-bg);
    border-top: 1px solid var(--box-border);
    padding: 10px 16px;
}
body.vip-cockpit #contacts_modal .modal-footer .btn-secondary {
    background: var(--vip-white);
    border-color: var(--vip-navy-20);
    color: var(--vip-navy-80);
    cursor: pointer;
}

/* ── section + group heading ─────────────────────────────────────────── */
body.vip-cockpit #contacts_modal .vip-contact-section + .vip-contact-section { margin-top: 16px; }
body.vip-cockpit #contacts_modal .vip-contact-group {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .5px;
    text-transform: uppercase;
    color: var(--vip-steel);
    margin: 0 0 8px;
}
body.vip-cockpit #contacts_modal .vip-contact-group::after {
    content: "";
    flex: 1 1 auto;
    height: 1px;
    background: var(--vip-navy-10);
}

/* ── contact card ────────────────────────────────────────────────────── */
body.vip-cockpit #contacts_modal .vip-contact-card {
    display: flex;
    gap: 12px;
    align-items: flex-start;
    padding: 11px 12px;
    border: 1px solid var(--vip-navy-10);
    border-radius: 10px;
    background: var(--vip-white);
}
body.vip-cockpit #contacts_modal .vip-contact-card + .vip-contact-card { margin-top: 8px; }
body.vip-cockpit #contacts_modal .vip-contact-card.is-lead {
    border-color: var(--box-head-bg);
    box-shadow: inset 3px 0 0 var(--vip-yellow);
}
body.vip-cockpit #contacts_modal .vip-contact-card.is-tech { background: var(--vip-subsidebar-bg); }

/* ── avatar ──────────────────────────────────────────────────────────── */
body.vip-cockpit #contacts_modal .vip-contact-av {
    width: 40px;
    height: 40px;
    flex: 0 0 40px;
    border-radius: 50%;
    background: var(--box-head-bg);
    color: var(--vip-white);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 700;
}
body.vip-cockpit #contacts_modal .vip-contact-av.is-tech {
    background: var(--vip-navy);
    color: var(--vip-yellow);
    font-size: 15px;
}

/* ── card body ───────────────────────────────────────────────────────── */
body.vip-cockpit #contacts_modal .vip-contact-body { min-width: 0; }
body.vip-cockpit #contacts_modal .contact_name {
    font-size: 13px;
    font-weight: 700;
    color: var(--vip-navy);
    margin: 0;
}
body.vip-cockpit #contacts_modal .contact_role {
    font-size: 12px;
    color: var(--vip-navy-80);
    margin: 1px 0 0;
}
body.vip-cockpit #contacts_modal .contact_officelocation {
    font-size: 11px;
    color: var(--vip-steel);
    margin: 2px 0 0;
}
body.vip-cockpit #contacts_modal .vip-contact-links {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 16px;
    margin-top: 6px;
}
body.vip-cockpit #contacts_modal .vip-contact-links a {
    display: inline-flex;
    align-items: center;
    font-size: 11px;
    color: var(--box-head-bg);
}
body.vip-cockpit #contacts_modal .vip-contact-links a:hover { color: var(--vip-navy); text-decoration: underline; }
body.vip-cockpit #contacts_modal .vip-contact-links i { font-size: 12px; }

/* ---------------------------------------------------------------------------
   Detail modals (.vip-detail-modal): the second-level dialogs opened out of a
   property view or out of a cross-property list (Top tenants, lease expiries).
   The header carries two slots: the subject on the left (context chip + name)
   and the owning property on the right, in the same identity vocabulary as the
   overlay title strip. Splitting them by position rather than by extra markup
   keeps the strip to one line and keeps the object visibly subordinate to the
   subject - the reverse of the overlay, where the object IS the subject.
   Chrome matches #settings_modal / #contacts_modal, so the steel ground gives
   the yellow PID the contrast it needs. Desktop-only, as the platform is.
   --------------------------------------------------------------------------- */
body.vip-cockpit .vip-detail-modal .modal-content {
    border: 0;
    padding: 0;
    border-radius: 10px;
    overflow: hidden;
}
body.vip-cockpit .vip-detail-modal .modal-header {
    display: flex;
    align-items: center;
    justify-content: flex-start;   /* the subject slot's flex-grow does the spacing */
    gap: 10px;
    background: var(--box-head-bg);
    border-bottom: 1px solid var(--box-head-rule);
    padding: 11px 16px;
    color: var(--box-head-fg);
    font-size: 13px;
    font-weight: 500;
    letter-spacing: .5px;
    line-height: 1.2;
    text-transform: uppercase;
    white-space: nowrap;
    overflow: hidden;
}
/* Subject slot: grows into all free space, which is what pushes the object slot and
   the close control to the far side. Doing the spacing by flex-grow rather than by an
   auto margin is deliberate: Bootstrap ships its own `margin-left: auto` on
   `.modal-header .close`, and competing auto margins share the free space between
   them instead of handing it to one side. With no free space left over, that margin
   resolves to zero on its own and the layout no longer depends on out-specifying a
   shorthand. The subject also truncates first under pressure. */
body.vip-cockpit .vip-detail-modal .modal-title {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1 1 auto;
    min-width: 0;
    color: var(--box-head-fg);
    overflow: hidden;
}
/* Object slot: sized to its content, so the subject keeps the remaining width. */
body.vip-cockpit .vip-detail-modal .modal-header-context {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
}
/* The negative margins Bootstrap sets stay - they give the control its enlarged hit
   area and pull it flush to the header padding. Only the auto left margin is pinned
   to zero, so the strip reads the same even if a caller leaves the subject empty. */
body.vip-cockpit .vip-detail-modal .modal-header .close {
    flex: 0 0 auto;
    margin-left: 0;
    color: var(--box-head-fg);
    opacity: .85;
    text-shadow: none;
    cursor: pointer;
}
/* Wide variant for the content-heavy detail views. Bootstrap's own modal-xl caps at
   1140px, which is too narrow once the body carries a KPI band beside a timeline and a
   schedule. The viewport clamp keeps the card off the screen edges on smaller displays;
   the platform is desktop-only, so no breakpoint below that is provided. */
body.vip-cockpit .vip-detail-modal .modal-dialog.vip-modal-wide {
    max-width: min(1200px, 96vw);
}
/* The card owns the height: header and footer stay put and only the body scrolls, so a
   long schedule never pushes the close control off-screen. Replaces the per-table
   scrollY arithmetic the detail tables used to carry. */
body.vip-cockpit .vip-detail-modal .modal-body {
    max-height: calc(100vh - 190px);
    overflow-y: auto;
    overflow-x: hidden;
}
body.vip-cockpit .vip-detail-modal .modal-footer {
    background: var(--box-chart-bg);
    border-top: 1px solid var(--box-border);
    padding: 10px 16px;
}

/* The tenant profile is the one detail view without a footer: its header close is the
   only exit, so the height the footer would take is handed to the body instead. */
body.vip-cockpit #tenant_modal .modal-body {
    max-height: calc(100vh - 120px);
}

/* Stacked modals: with the tiering in place the raised backdrop already dims whatever
   sits below, so the covered card only needs to stop competing for attention. It scales
   back and loses a little opacity, which reads as depth and keeps two headers from
   looking like one collided strip.

   The transition lives on the dialog itself, NOT on the covered state - declaring it
   only in the covered rule takes it away again the moment the class is removed, so the
   card would snap forward instead of easing back. Bootstrap's own .3s ease-out on
   .modal.fade .modal-dialog is widened here to cover opacity as well; the symmetric
   ease-in-out curve is what makes the return read as the reverse of the recede rather
   than as a separate motion. */
body.vip-cockpit .modal.fade .modal-dialog {
    transition: transform .32s cubic-bezier(.4, 0, .2, 1),
                opacity .32s cubic-bezier(.4, 0, .2, 1);
}
/* Overrides Bootstrap's `.modal.show .modal-dialog { transform: none }`, which sits at
   equal specificity - the body scope is what settles it. */
body.vip-cockpit .modal.vip-modal-covered .modal-dialog {
    transform: scale(.96);
    opacity: .88;
}
@media (prefers-reduced-motion: reduce) {
    body.vip-cockpit .modal.fade .modal-dialog { transition: none; }
}

/* ---------------------------------------------------------------------------
   Rent profile (#tenancies_rentprofile). Chart and lease-event detail as one
   functional group, the same pattern the Tenant risk map box carries - but
   freed of the box card and the container/row/col-8/col-4 split entirely,
   since a merged single element no longer needs either: the fluid container
   gives it the full row width and the plain heading carries what the box
   header used to. Widths are governed by flex-basis below, independent of
   the Bootstrap grid.
   --------------------------------------------------------------------------- */
body.vip-cockpit .vip-rp-head {
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .5px;
    text-transform: uppercase;
    color: var(--vip-navy);
    padding-bottom: 8px;
    margin-bottom: 12px;
    border-bottom: 1px solid var(--box-border);
}
body.vip-cockpit .vip-rp {
    display: flex;
    gap: 16px;
    align-items: stretch;
}
body.vip-cockpit .vip-rp-chart { flex: 0 0 50%; min-width: 0; }
/* The chart alone governs how tall the row is. The detail panel is a positioning
   context whose content is lifted out of the flow, so however long the lease list
   runs it contributes nothing to the row's height and cannot push the section into
   a scroll of its own - the list scrolls inside its own frame instead. Stretch then
   gives the empty panel exactly the chart's height to fill. */
body.vip-cockpit .vip-rp-detail {
    flex: 1 1 auto;
    min-width: 0;
    position: relative;
}
body.vip-cockpit .vip-rp-detail-stack {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    display: flex;
    flex-direction: column;
    min-height: 0;
    border-left: 1px solid var(--box-border);
    padding-left: 16px;
}
body.vip-cockpit .vip-rp-detail-head {
    flex: 0 0 auto;
    font-size: 12px;
    letter-spacing: .4px;
    text-transform: uppercase;
    color: var(--vip-steel);
    padding-bottom: 6px;
    margin-bottom: 6px;
    border-bottom: 1px solid var(--box-border);
}
/* Takes whatever height the heading leaves over; the list inside it owns the scroll.
   A flex column rather than a percentage height, so the list never depends on the
   parent resolving to a definite value. */
body.vip-cockpit .vip-rp-detail-body {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}
/* Stacked below the split: the panel returns to the flow, since there is no longer a
   chart beside it to take its height from. */
@media (max-width: 991.98px) {
    body.vip-cockpit .vip-rp { flex-direction: column; }
    body.vip-cockpit .vip-rp-detail { position: static; }
    body.vip-cockpit .vip-rp-detail-stack {
        position: static;
        border-left: none;
        border-top: 1px solid var(--box-border);
        padding-left: 0;
        padding-top: 12px;
    }
    body.vip-cockpit .vip-rp-detail-body { max-height: 420px; }
}

/* ---------------------------------------------------------------------------
   Rent profile detail (#tenancies_rentprofile_detail). Lists the leases behind
   the segment currently selected in the chart. Where the tooltip is capped at ten
   entries because it has to fit under the cursor, this panel is uncapped and
   scrolls - the two divide the work rather than repeat it.
   --------------------------------------------------------------------------- */
body.vip-cockpit .vip-rpd-empty {
    padding: 28px 12px;
    text-align: center;
    font-size: 12px;
    color: var(--vip-steel);
}
body.vip-cockpit .vip-rpd {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    /* the panel has as many columns as a lease event has facts to carry (object jump,
       tenant profile jump, tenant, property, date, exit, rent, share, area, use) - at
       narrower widths that is wider than the panel itself, and a horizontal scrollbar
       is how the surplus columns stay reachable instead of silently disappearing */
    overflow-x: auto;
}
body.vip-cockpit .vip-rpd-table { width: 100%; border-collapse: collapse; font-size: 12px; }
/* Same head vocabulary as the profile and board tables - the AG-Grid header surface, no
   uppercase, no letter-spacing - so a hand-built table reads as part of the application.
   It stays put while the list scrolls under it. */
body.vip-cockpit .vip-rpd-table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    font-size: 12px;
    font-weight: 400;
    text-transform: none;
    letter-spacing: normal;
    padding: 5px 8px;
    vertical-align: bottom;
    white-space: nowrap;
    background: var(--ag-header-background-color, var(--secondary_25));
    color: var(--vip-navy);
    border-bottom: 1px solid var(--box-border);
}
body.vip-cockpit .vip-rpd-table td {
    padding: 3px 4px;
    border-bottom: 1px solid var(--box-border);
    vertical-align: middle;
    white-space: nowrap;
}
body.vip-cockpit .vip-rpd-table tbody tr:hover td { background: var(--box-chart-bg); }
body.vip-cockpit .vip-rpd-table tfoot td {
    position: sticky;
    bottom: 0;
    background: var(--vip-white);
    font-weight: 500;
    border-top: 1px solid var(--vip-steel);
    border-bottom: 0;
}
/* PID above, asset name beneath and quieter: the column has to stay narrow */
body.vip-cockpit .vip-rpd-pid { display: block; font-weight: 500; }
body.vip-cockpit .vip-rpd-pname {
    display: block;
    font-size: 11px;
    color: var(--vip-steel);
    max-width: 150px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Free-form and just as capable of running long as the property name beside it, so it
   gets the same single-line clip rather than the wrap that would otherwise inflate the
   row and push the value columns wherever the browser had room left. The clip has to
   live on an inner span: a `display: block` on the cell itself drops it out of the
   table row model and takes the row's alignment with it. */
body.vip-cockpit .vip-rpd-tenant {
    display: block;
    max-width: 160px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Two actions now (object jump, tenant profile), so the fixed width grows with them. */
body.vip-cockpit .vip-rpd-act { width: 60px; text-align: right; white-space: nowrap; }

/* Reduced row action: the glyph alone, with the hit area only becoming visible on hover.
   A dense list is mostly figures, and a full button chrome per row competes with them for
   attention even though the jump-off is secondary to the data. Declared free of the list
   it first appears in, so any other dense table can adopt it as-is. */
body.vip-cockpit .vip-iconaction {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: 0;
    border-radius: 4px;
    background: none;
    color: var(--vip-steel);
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
}
body.vip-cockpit .vip-iconaction + .vip-iconaction { margin-left: 2px; }
body.vip-cockpit .vip-iconaction:hover {
    color: var(--vip-navy);
    background: var(--box-chart-bg);
}
/* Keyboard focus keeps its ring; a mouse press does not leave one behind. */
body.vip-cockpit .vip-iconaction:focus:not(:focus-visible) { outline: none; }

/* ---------------------------------------------------------------------------
   Tenant profile (#tenant_modal body). Answer order rather than table order: the
   headline figures first, then the shape of the income over time beside the use
   mix, then the lease detail, with the raw unit level folded away. Everything is
   plain markup - only the rent roll is a grid, because only it can run long.
   --------------------------------------------------------------------------- */
body.vip-cockpit .vip-tp { display: flex; flex-direction: column; gap: 12px; }

/* --- object navigation: only present where the tenant spans more than one asset ---
   The entries scroll horizontally rather than wrapping, because a tenant may occupy
   dozens of assets and a wrapping bar would push the profile itself off the screen. The
   roll-up sits outside the scroller as a fixed anchor and the search button closes the
   row on the right, so both stay reachable at any scroll position. */
body.vip-cockpit .vip-tp-nav {
    position: sticky;
    top: 0;
    z-index: 20;
    display: flex;
    align-items: flex-end;
    gap: 2px;
    background: var(--vip-white);
    border-bottom: 1px solid var(--box-border);
    /* The body scrolls behind the strip and its own padding would leave a sliver above it
       for rows to show through; the offset shadow paints that band in the same ground
       without taking layout, so the dropdown stays anchored to the button. */
    box-shadow: 0 -16px 0 var(--vip-white);
}
body.vip-cockpit .vip-tp-nav-strip {
    display: flex;
    align-items: flex-end;
    gap: 2px;
    flex: 1 1 auto;
    min-width: 0;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: thin;
}
body.vip-cockpit .vip-tp-nav-strip::-webkit-scrollbar { height: 6px; }
body.vip-cockpit .vip-tp-nav-strip::-webkit-scrollbar-thumb {
    background: var(--vip-navy-30);
    border-radius: 3px;
}
body.vip-cockpit .vip-tp-nav-anchor,
body.vip-cockpit .vip-tp-nav-strip .vip-tp-tab { flex: 0 0 auto; }
body.vip-cockpit .vip-tp-nav-more {
    flex: 0 0 auto;
    margin-bottom: 3px;
    padding: 4px 9px;
    border: 1px solid var(--box-border);
    border-radius: 6px;
    background: var(--vip-white);
    color: var(--vip-steel);
    font-size: 12px;
    line-height: 1;
    cursor: pointer;
}
body.vip-cockpit .vip-tp-nav-more:hover { color: var(--vip-navy); background: var(--box-chart-bg); }
body.vip-cockpit .vip-tp-nav-more:focus:not(:focus-visible) { outline: 0; }
body.vip-cockpit .vip-tp-nav.is-open .vip-tp-nav-more { color: var(--vip-navy); border-color: var(--vip-navy-40); }
/* deliberately narrower than the strip: a plain index reads better in a column than
   stretched across the dialog */
body.vip-cockpit .vip-tp-nav-panel {
    position: absolute;
    top: 100%;
    right: 0;
    z-index: 30;
    width: 340px;
    margin-top: 3px;
    padding: 8px;
    background: var(--vip-white);
    border: 1px solid var(--box-border);
    border-radius: 6px;
    box-shadow: 0 6px 18px rgba(0, 0, 0, .15);
}
body.vip-cockpit .vip-tp-nav-search {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 5px 8px;
    margin-bottom: 6px;
    border: 1px solid var(--box-border);
    border-radius: 6px;
    color: var(--vip-steel);
}
body.vip-cockpit .vip-tp-nav-input {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    outline: 0;
    background: transparent;
    font-size: 12px;
    color: var(--vip-navy);
}
body.vip-cockpit .vip-tp-nav-list { max-height: 280px; overflow-y: auto; }
body.vip-cockpit .vip-tp-nav-item {
    display: flex;
    align-items: baseline;
    gap: 5px;
    padding: 5px 8px;
    border-radius: 4px;
    font-size: 12px;
    color: var(--vip-navy);
    cursor: pointer;
    white-space: nowrap;
}
body.vip-cockpit .vip-tp-nav-item:hover,
body.vip-cockpit .vip-tp-nav-item.is-option-active { background: var(--box-chart-bg); }
/* The shared hide utility and the rule above carry identical specificity, so the later
   `display: flex` would silently win and the search would filter nothing. Restated here,
   after it, rather than escalated with !important. */
body.vip-cockpit .vip-tp-nav-item.vip-rla-hide { display: none; }
body.vip-cockpit .vip-tp-nav-item.is-current { font-weight: 500; }
body.vip-cockpit .vip-tp-nav-pid { flex: 0 0 auto; font-weight: 500; }
/* the label takes whatever width is left; the shortener already trims block addresses,
   this is the fallback for a narrow panel */
body.vip-cockpit .vip-tp-nav-sub {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--vip-steel);
}
body.vip-cockpit .vip-tp-nav-empty { padding: 8px; font-size: 12px; color: var(--vip-steel); }
body.vip-cockpit .vip-tp-tab {
    padding: 6px 12px;
    border-radius: 6px 6px 0 0;
    font-size: 12px;
    color: var(--vip-steel);
    cursor: pointer;
    white-space: nowrap;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    /* No negative overlap onto the rail: the strip is a horizontal scroller, and a box
       overhanging its scrollport is clipped there - which is what cut the top edge off
       the active entry. The entries sit ON the rail instead of merging into it. */
    border: 1px solid transparent;
    border-bottom: 0;
}
body.vip-cockpit .vip-tp-tab:hover { color: var(--vip-navy); background: var(--box-chart-bg); }
/* The active entry is the one thing in the strip that has to be findable at a glance
   while scrolling past twenty others, so it carries the accent as well as the frame. */
body.vip-cockpit .vip-tp-tab.is-active {
    color: var(--vip-navy);
    background: var(--vip-white);
    border-color: var(--box-border);
    border-top: 2px solid var(--vip-yellow);
    padding-top: 5px;
    font-weight: 600;
}
body.vip-cockpit .vip-tp-tab.is-active:hover { background: var(--vip-white); }
/* an asset the current filter excludes still gets an entry, marked as out of scope in
   the strip and in the list alike */
body.vip-cockpit .vip-tp-tab.is-filtered,
body.vip-cockpit .vip-tp-nav-item.is-filtered { opacity: .55; font-style: italic; }

/* --- hover preview: the row currently narrowing the two charting panels --- */
body.vip-cockpit .vip-tp-presence tbody tr.is-preview td,
body.vip-cockpit .vip-tp-schedule tbody tr.is-preview td { background: var(--box-chart-bg); }

/* --- context notices: the date the profile is read on, always, and the conversion
       notice where figures are actually converted --- */
body.vip-cockpit .vip-tp-note {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 6px 11px;
    border-radius: 6px;
    background: var(--box-chart-bg);
    color: var(--vip-steel);
    font-size: 12px;
}
body.vip-cockpit .vip-tp-mixed { color: var(--vip-steel); font-style: italic; }
body.vip-cockpit .vip-tp-table tr.is-clickable { cursor: pointer; }
body.vip-cockpit .vip-tp-table tr.is-clickable:hover td { background: var(--box-chart-bg); }

/* --- headline band --- */
body.vip-cockpit .vip-tp-kpi {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: 8px;
}
body.vip-cockpit .vip-tp-tile {
    background: var(--box-chart-bg);
    border: 1px solid var(--box-border);
    border-radius: 8px;
    padding: 9px 11px;
    min-width: 0;
}
body.vip-cockpit .vip-tp-tile-lbl {
    font-size: 11px;
    color: var(--vip-steel);
    text-transform: uppercase;
    letter-spacing: .4px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
body.vip-cockpit .vip-tp-tile-val {
    font-size: 19px;
    font-weight: 500;
    line-height: 1.25;
    color: var(--vip-navy);
}
/* createShowValue emits a block wrapper; it has to sit on the figure's own line */
body.vip-cockpit .vip-tp-tile-val > div { display: flex; align-items: baseline; gap: 6px; }
body.vip-cockpit .vip-tp-tile-sub {
    font-size: 11px;
    color: var(--vip-steel);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* --- panel chrome, shared by every block below the band --- */
body.vip-cockpit .vip-tp-panel {
    border: 1px solid var(--box-border);
    border-radius: 8px;
    overflow: hidden;
    min-width: 0;
}
body.vip-cockpit .vip-tp-panel-head {
    background: var(--box-head-bg);
    color: var(--box-head-fg);
    border-bottom: 1px solid var(--box-head-rule);
    padding: 6px 11px;
    font-size: 12px;
    letter-spacing: .4px;
    text-transform: uppercase;
    display: flex;
    align-items: center;
    gap: 8px;
}
body.vip-cockpit .vip-tp-panel-body { padding: 11px; }
body.vip-cockpit .vip-tp-split {
    display: grid;
    grid-template-columns: minmax(0, 2.2fr) minmax(0, 1fr);
    gap: 10px;
    align-items: start;
}

/* --- use mix: what the tenant occupies, in the measure each use is counted in --- */
body.vip-cockpit .vip-tp-usehead,
body.vip-cockpit .vip-tp-userow {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 96px 44px;
    gap: 6px;
    align-items: baseline;
}
body.vip-cockpit .vip-tp-usehead {
    font-size: 11px;
    color: var(--vip-steel);
    text-transform: uppercase;
    letter-spacing: .4px;
    padding-bottom: 5px;
    border-bottom: 1px solid var(--box-border);
    margin-bottom: 7px;
}
body.vip-cockpit .vip-tp-usehead span:not(:first-child) { text-align: right; }
body.vip-cockpit .vip-tp-userow + .vip-tp-userow { margin-top: 9px; }
body.vip-cockpit .vip-tp-uselbl {
    font-size: 12px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
body.vip-cockpit .vip-tp-usenum { font-size: 12px; text-align: right; color: var(--vip-steel); }
body.vip-cockpit .vip-tp-usenum.is-strong { color: var(--vip-navy); font-weight: 500; }
/* The measure the quantity is counted in, kept quiet so the figures stay the read line. */
body.vip-cockpit .vip-tp-usesuffix { color: var(--vip-steel); font-size: 10px; margin-left: 4px; }
/* A use counted in units shares no denominator with the area-bearing ones, so it carries
   neither a share nor a bar and only needs the breathing room the bar would have given. */
body.vip-cockpit .vip-tp-userow.is-count { padding-bottom: 5px; }
body.vip-cockpit .vip-tp-usebars { grid-column: 1 / -1; }
body.vip-cockpit .vip-tp-bar {
    display: block;
    height: 5px;
    border-radius: 2px;
    background: var(--box-chart-bg);
}
body.vip-cockpit .vip-tp-bar > span { display: block; height: 100%; border-radius: 2px; }
body.vip-cockpit .vip-tp-bar.is-area > span { background: var(--vip-steel); opacity: .45; }

/* --- lease schedule / presence table ---
   The head adopts the AG-Grid header surface, the same vocabulary the board and the
   risk-map detail table already use, so a hand-built table inside the profile reads as
   part of the application rather than as its own device. Alignment stays with the
   `text-right` classes the markup sets per column. */
body.vip-cockpit .vip-tp-table { width: 100%; border-collapse: collapse; font-size: 12px; }
body.vip-cockpit .vip-tp-table th {
    font-size: 12px;
    font-weight: 400;
    text-transform: none;
    letter-spacing: normal;
    padding: 5px 9px;
    vertical-align: bottom;
    white-space: nowrap;
    background: var(--ag-header-background-color, var(--secondary_25));
    color: var(--vip-navy);
    border-bottom: 1px solid var(--box-border);
}
body.vip-cockpit .vip-tp-table td { padding: 6px 9px; border-bottom: 1px solid var(--box-border); }
/* Predominant use is rendered by the shared ttUseCell helper, so the cell gets the same
   clipping the board's use column has - the label is free text and can run long. */
body.vip-cockpit .vip-tp-table td.vip-rla-use {
    max-width: 180px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Drill-down affordance: only a lease with units behind it toggles, so only that row
   offers a pointer - the same convention the board and the reversion table follow. */
body.vip-cockpit .vip-tp-table tr.vip-tp-lease { cursor: default; }
body.vip-cockpit .vip-tp-table tr.vip-tp-lease.is-drill { cursor: pointer; }
/* Hover feedback matches the reversion table: every row of the schedule answers the
   pointer, not only the ones that drill, and on the same tone. The lease row and its unit
   rows sit on different grounds, so a single shared hover colour is what makes them read
   as one hovered line. */
body.vip-cockpit .vip-tp-schedule tbody tr:hover td { background: var(--dt-row-hover); }
/* The break date rides under the term rather than claiming a column: the two Walt columns
   state terms in years, and a date among them would read as one of those. */
body.vip-cockpit .vip-tp-term-brk {
    display: block;
    font-size: 11px;
    color: var(--vip-steel);
}
/* Unit level: indented against its lease and quieter, since the lease row carries the
   figures that matter and the units only break them down. */
body.vip-cockpit .vip-tp-table td.vip-tp-l1 { padding-left: 24px; }
/* Drill levels run dark to light, the same order the reversion table uses: the lease row
   carries the figures that matter and sits on the stronger ground, the units that break it
   down step back to plain white with a dashed rule. */
body.vip-cockpit .vip-tp-table tr.vip-tp-lease td { background: var(--secondary_10); }
body.vip-cockpit .vip-tp-table tr.vip-tp-leaseunit td {
    background: var(--vip-white);
    border-bottom: 1px dashed var(--box-border);
    color: var(--vip-navy-70, var(--vip-steel));
}
body.vip-cockpit .vip-tp-table tfoot td {
    font-weight: 500;
    border-top: 1px solid var(--vip-steel);
    border-bottom: 0;
    background: var(--vip-white);
    color: var(--vip-navy);
}
/* createShowValue wraps its figure in a block; keep it on the cell's baseline */
body.vip-cockpit .vip-tp-table td > div { display: inline-flex; align-items: baseline; gap: 5px; }

/* --- lease exit pills ---
   Modifiers on the existing .vip-pill family defined further up; the base and the
   --prelet / --vacant variants are shared with the tenant column, so only the two states
   the rent-profile detail list distinguishes are declared here. */
body.vip-cockpit .vip-pill--fixed {
    background: var(--vip-navy-10, #E8E8EB);
    color: var(--vip-steel);
}
body.vip-cockpit .vip-pill--break {
    background: rgba(255, 223, 0, .28);
    color: var(--vip-navy, #25273A);
}

/* --- reversion by use type ---
   Re-uses the .vip-rla-* table vocabulary of the property overlay's element, so the two
   stay visually identical where they show the same figures. Only what the narrower
   surface demands is overridden: the overlay drives its column raster from a colgroup
   that this table does not carry, and its two-line head is folded into one row with the
   unit label as a quiet second line. */
body.vip-cockpit .vip-tp-revtable { table-layout: auto; }
/* a use type with no units behind it carries no drilldown, so it must not offer one */
body.vip-cockpit .vip-tp-revtable tr.vip-rla-usetop { cursor: default; }
body.vip-cockpit .vip-tp-revtable tr.vip-rla-usetop.is-drill { cursor: pointer; }
/* The unit of a column is stated on its own quiet line rather than in brackets beside the
   label, so the head reads as caption over measure. Shared by every hand-built table in
   the platform: reversion, lease schedule, presence, tenant board and rent-profile detail. */
body.vip-cockpit .vip-rla-table thead th .sub,
body.vip-cockpit .vip-tp-table thead th .sub,
body.vip-cockpit .vip-rpd-table thead th .sub {
    display: block;
    font-size: 11px;
    font-weight: 400;
    opacity: .8;
    white-space: nowrap;
}
/* Head and footer are aligned with the lease schedule so the object tab reads as two
   panels of one family: same header ground, same closing total row. The figures differ,
   the frame does not. */
body.vip-cockpit .vip-tp-revtable thead th {
    background: var(--ag-header-background-color, var(--secondary_25));
    color: var(--vip-navy);
    border-bottom: 1px solid var(--box-border);
}
body.vip-cockpit .vip-tp-revtable tfoot td {
    padding: 6px 8px;
    text-align: right;
    vertical-align: middle;
    font-weight: 500;
    border-top: 1px solid var(--vip-steel);
    background: var(--vip-white);
    /* stated rather than inherited: the platform's other stylesheets carry generic tfoot
       rules for the dark DataTables footers, and a footer that inherits its colour from
       those renders white on white - the figures are there but invisible */
    color: var(--vip-navy);
}
body.vip-cockpit .vip-tp-revtable tfoot td.vip-rla-use {
    text-align: left;
    color: var(--vip-navy);
}
/* the hover surface for the row tooltip: the label itself, since this table carries no
   share cell to hang it on */
body.vip-cockpit .vip-tp-revtable .vip-tpr-tip { cursor: help; }
body.vip-cockpit .vip-tp-revtable .vip-tpr-tip:hover { text-decoration: underline dotted; }

/* ---------------------------------------------------------------------------
   Properties gallery: filterable, paginated tile grid (sub-sidebar driven).
   The pane stretches inside the dashboard main and lets the page scroll;
   tile density is switched via the grid's data-size attribute.
   --------------------------------------------------------------------------- */
/* The pane spans the main's width and keeps its content height, so the
   dashboard's safe centring places a short gallery in the middle of the
   viewport and pins an overflowing one to the top. */
body.vip-cockpit .vip-gallery-pane {
    width: 100%;
    min-width: 0;
}
body.vip-cockpit .vip-gallery-head {
    display: flex;
    justify-content: flex-end;
    padding-bottom: 8px;
    font-size: 12px;
    color: var(--vip-steel);
}
/* Balanced, centred rows; tile width, row length and line breaks are set by
   vipGalleryBalance() (minimum tile width per size: 175 / 230 / 300 px). */
body.vip-cockpit .vip-gallery-grid {
    --vip-gallery-gap: 16px;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--vip-gallery-gap);
    margin: 0 auto;
}
body.vip-cockpit .vip-gallery-grid > .vip-gtile { flex: 0 0 var(--vip-gtile-w, 230px); min-width: 0; box-sizing: border-box; }
/* explicit line break between two rows; the negative margin cancels the row gap of its own line */
body.vip-cockpit .vip-gallery-break {
    flex-basis: 100%;
    height: 0;
    margin-top: calc(-1 * var(--vip-gallery-gap));
}
body.vip-cockpit .vip-gallery-grid[data-size="s"] .vip-gtile-metrics { display: none; }
body.vip-cockpit .vip-gallery-empty {
    flex-basis: 100%;
    padding: 60px 0;
    text-align: center;
    color: var(--vip-steel);
}

/* Tile: image on top, key figures below, market value as the closing row. */
body.vip-cockpit .vip-gtile {
    display: flex;
    flex-direction: column;
    background: var(--vip-white);
    border: 1px solid var(--box-border);
    border-radius: 6px;
    box-shadow: var(--box-shadow);
    overflow: hidden;
    cursor: pointer;
    transition: box-shadow .12s ease;
}
body.vip-cockpit .vip-gtile:hover {
    box-shadow: 0 4px 12px rgba(37, 39, 58, .18);
}
body.vip-cockpit .vip-gtile-img {
    position: relative;
    aspect-ratio: 16 / 10;
    background-color: var(--box-chart-bg);
    background-size: cover;
    background-position: center;
}
body.vip-cockpit .vip-gtile-pid {
    position: absolute;
    top: 8px;
    left: 8px;
    padding: 3px 8px;
    border-radius: 3px;
    background: rgba(37, 39, 58, .82);
    color: var(--vip-white);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .03em;
}
body.vip-cockpit .vip-gtile-tag {
    position: absolute;
    top: 8px;
    right: 8px;
    max-width: 60%;
    padding: 3px 8px;
    border-radius: 3px;
    background: rgba(255, 255, 255, .88);
    color: var(--vip-navy);
    font-size: 11px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
body.vip-cockpit .vip-gtile-body {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    gap: 6px;
    padding: 10px 12px 12px;
    text-align: left;
}
body.vip-cockpit .vip-gtile-name {
    font-size: 13px;
    font-weight: 600;
    line-height: 1.25;
    color: var(--vip-navy);
}
/* name and valuation date share the first line of the tile body */
body.vip-cockpit .vip-gtile-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 8px;
}
body.vip-cockpit .vip-gtile-head .vip-gtile-name { min-width: 0; }
body.vip-cockpit .vip-gtile-refdate {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: baseline;
    gap: 4px;
    font-size: 11px;
    color: var(--vip-steel);
    white-space: nowrap;
}
body.vip-cockpit .vip-gtile-muni {
    font-size: 12px;
    color: var(--vip-steel);
}
body.vip-cockpit .vip-gtile-metrics {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: 2px;
}
body.vip-cockpit .vip-gtile-metric {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    font-size: 12px;
}
body.vip-cockpit .vip-gtile-metric span:first-child { color: var(--vip-steel); }
body.vip-cockpit .vip-gtile-metric span:last-child { font-weight: 600; color: var(--vip-navy); white-space: nowrap; }
body.vip-cockpit .vip-gtile-value {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 10px;
    margin-top: auto;
    padding-top: 8px;
    border-top: 1px solid var(--vip-navy-10);
    font-size: 13px;
    font-weight: 700;
    color: var(--vip-navy);
}
body.vip-cockpit .vip-gtile-delta {
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
}
body.vip-cockpit .vip-gtile-delta.pos { color: var(--compare_positive); }
body.vip-cockpit .vip-gtile-delta.neg { color: var(--compare_negative); }
body.vip-cockpit .vip-gtile-delta.flat { color: var(--compare_neutral); }

/* Pagination: quiet buttons, current page lifted to the navy fill. */
body.vip-cockpit .vip-gallery-pagination {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 4px;
    padding: 18px 0 4px;
}
body.vip-cockpit .vip-gpage {
    min-width: 30px;
    padding: 4px 8px;
    border: 1px solid var(--vip-navy-20);
    border-radius: 4px;
    background: var(--vip-white);
    color: var(--vip-navy);
    font-size: 13px;
    cursor: pointer;
}
body.vip-cockpit .vip-gpage:hover:not(:disabled):not(.is-current) {
    background: var(--vip-navy-10);
}
body.vip-cockpit .vip-gpage.is-current {
    background: var(--vip-navy);
    border-color: var(--vip-navy);
    color: var(--vip-white);
    cursor: default;
}
body.vip-cockpit .vip-gpage:disabled {
    color: var(--vip-navy-40);
    cursor: default;
}
body.vip-cockpit .vip-gpage-gap {
    padding: 0 2px;
    color: var(--vip-navy-40);
}

/* ==========================================================================
   Breakdown by cluster - value-weighted treemap
   d3.treemap computes the rectangles; divs are positioned absolutely inside
   the relative canvas. Each cluster carries a colour: dark for the band and
   the frames, a light tint for the group body, so groups read as clearly
   delimited blocks. Tiles are pure colour fields (trend-coloured on tracker
   portfolios); metric detail lives in the shared asset tooltip.
   ========================================================================== */
body.vip-cockpit .vip-treemap {
    position: relative;
    width: 100%;
}
/* Group body: light tint framed in the group colour, behind the tiles.
   Background and border colour are set inline per group. */
body.vip-cockpit .vip-treemap-groupbg {
    position: absolute;
    z-index: 0;
    border: 1px solid;
    border-radius: 6px;
}
/* Band hover: ring the group frame in its own colour (set inline as the
   element's color, picked up via currentColor). */
body.vip-cockpit .vip-treemap-groupbg.is-hot {
    box-shadow: 0 0 0 2px currentColor;
}
/* Group band: colour-coded header strip (background set inline). */
body.vip-cockpit .vip-treemap-band {
    position: absolute;
    z-index: 2;
    cursor: pointer;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    height: 26px;
    padding: 4px 10px;
    border-radius: 4px;
    color: var(--vip-white);
    overflow: hidden;
}
/* On narrow bands the label wins the space; the meta truncates away. */
body.vip-cockpit .vip-treemap-band-label {
    flex: 0 0 auto;
    max-width: 100%;
    font-size: 13px;
    font-weight: 600;
    letter-spacing: .01em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
body.vip-cockpit .vip-treemap-band-meta {
    flex: 0 1 auto;
    min-width: 0;
    font-size: 12px;
    color: rgba(255, 255, 255, .82);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Tile: calm white card framed in the group colour; area is strictly
   proportional to the chosen weight (border colour set inline). */
body.vip-cockpit .vip-ttile {
    position: absolute;
    z-index: 1;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    background: var(--vip-white);
    border: 1px solid;
    border-radius: 4px;
    box-shadow: var(--box-shadow);
    cursor: pointer;
    transition: box-shadow .12s ease;
}
body.vip-cockpit .vip-ttile:hover {
    box-shadow: 0 4px 12px rgba(37, 39, 58, .22);
    z-index: 3;
}
body.vip-cockpit .vip-ttile .vip-gtile-pid {
    /* colour comes inline from the group; geometry from the gallery badge */
    z-index: 1;
}
/* Label: dark text on the light card, no scrim needed. */
body.vip-cockpit .vip-ttile-label {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 6px 8px;
    min-width: 0;
}
body.vip-cockpit .vip-ttile-name {
    font-size: 12px;
    font-weight: 600;
    line-height: 1.2;
    color: var(--vip-navy);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
body.vip-cockpit .vip-ttile-value {
    font-size: 11px;
    font-weight: 700;
    color: var(--vip-navy);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
body.vip-cockpit .vip-ttile-value .vip-gtile-delta {
    margin-left: 6px;
    font-size: 11px;
    font-weight: 600;
}

/* Extreme changes (>=+-50%): a corner flag in the trend colour, drawn via
   currentColor which the tile sets inline together with its border. */
body.vip-cockpit .vip-ttile.is-extreme::after {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    border-style: solid;
    border-width: 0 10px 10px 0;
    border-color: transparent currentColor transparent transparent;
}

/* Tracker delta as a tag in the tile's top-right corner (white chip, the
   pos/neg/flat colours come from the shared delta classes). */
body.vip-cockpit .vip-ttile-deltatag {
    position: absolute;
    top: 8px;
    right: 8px;
    z-index: 1;
    padding: 2px 6px;
    border-radius: 3px;
    background: rgba(255, 255, 255, .88);
    font-size: 11px;
    font-weight: 600;
    white-space: nowrap;
}

/* Sidebar footer legend: the live trend scale. Invisible while empty. */
body.vip-cockpit .vip-treemap-legend:not(:empty) {
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--vip-navy-20);
}
body.vip-cockpit .vip-tlegend-title {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--vip-steel);
    margin-bottom: 6px;
}
body.vip-cockpit .vip-tlegend-bar {
    height: 10px;
    border-radius: 5px;
    border: 1px solid var(--vip-navy-20);
}
body.vip-cockpit .vip-tlegend-scale {
    display: flex;
    justify-content: space-between;
    margin-top: 4px;
    font-size: 11px;
    color: var(--vip-navy-70);
    white-space: nowrap;
}

/* Boxed legend variant (comparison treemap): inside a full-width box the
   gradient bar is capped and centred, so the scale stays readable at a
   glance. */
body.vip-cockpit .box .vip-treemap-legend:not(:empty) {
    max-width: 320px;
    margin: 12px auto 0;
    border-top: none;
    padding-top: 0;
    text-align: center;
}

/* Box header hosting inline tools (e.g. the treemap weight switch): title
   left, tools right; the segmented control drops the header's caps styling
   and flips to light-on-steel so it reads on the header band. */
body.vip-cockpit .box-header.vip-boxhead-flex {
    display: flex;
    align-items: center;
    justify-content: space-between;
    text-align: left;
}
body.vip-cockpit .vip-boxhead-flex .vip-seg {
    border-color: rgba(255, 255, 255, .45);
}
body.vip-cockpit .vip-boxhead-flex .vip-seg button {
    color: rgba(255, 255, 255, .85);
    text-transform: none;
    letter-spacing: normal;
    font-weight: 400;
}
body.vip-cockpit .vip-boxhead-flex .vip-seg button.on {
    background: var(--vip-white);
    color: var(--vip-navy);
    font-weight: 600;
}

/* Box-header dropdown host (movements cross-analysis): the vip-dropdown
   anchors its panel inside the host; the header's caps styling must not
   bleed into the trigger or the options. */
body.vip-cockpit .vip-boxhead-flex .vip-cmx-dim {
    position: relative;
    min-width: 220px;
    text-align: left;
    text-transform: none;
    letter-spacing: normal;
    font-weight: 400;
}

/* Small rectangles drop the label strip; the PID badge and the shared asset
   tooltip keep every tile identifiable. */
body.vip-cockpit .vip-ttile.is-tiny .vip-ttile-label { display: none; }
body.vip-cockpit .vip-ttile.is-tiny .vip-gtile-pid { top: 4px; left: 4px; padding: 1px 5px; font-size: 10px; }

/* ══════════════════════════════════════════════════════════════════════
   LIKE-FOR-LIKE COMPARISON — PORTFOLIO VIEW  (cockpit only)
   KPI headline band above a full-width valuation bridge, with the summary
   rendered as one grouped schedule below. Consumes the shared tokens.
   ══════════════════════════════════════════════════════════════════════ */

/* Headline band: a responsive row of metric cards that collapses to fewer
   columns as the dashboard narrows. */
body.vip-cockpit .vip-comp-kpiband {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 12px;
    margin-bottom: 14px;
}
@media (max-width: 1100px) {
    body.vip-cockpit .vip-comp-kpiband { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
body.vip-cockpit .vip-comp-kpi {
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 12px 14px;
    background: var(--box-bg);
    border: 1px solid var(--box-border);
    border-radius: 10px;
    box-shadow: var(--box-shadow);
}
body.vip-cockpit .vip-comp-kpi-label {
    font-size: 12px;
    color: var(--vip-muted);
}
body.vip-cockpit .vip-comp-kpi-value {
    font-size: 21px;
    font-weight: 700;
    line-height: 1.15;
    color: var(--vip-navy);
}
body.vip-cockpit .vip-comp-kpi-value.vip-comp-kpi-up   { color: var(--vip-green); }
body.vip-cockpit .vip-comp-kpi-value.vip-comp-kpi-down { color: var(--vip-red); }
body.vip-cockpit .vip-comp-kpi-sub {
    font-size: 12px;
    color: var(--vip-navy-50);
}

/* Grouped schedule: section dividers span the full width; the first column
   carries the row label left-aligned while figures stay right-aligned. */
body.vip-cockpit .vip-comp-schedule { margin-bottom: 0; }
body.vip-cockpit .vip-comp-schedule td.text-left,
body.vip-cockpit .vip-comp-schedule th:first-child { text-align: left; }
body.vip-cockpit .vip-comp-schedule tr.vip-comp-section td {
    padding: 8px 8px 4px;
    background: var(--vip-cream-50);
    color: var(--vip-navy);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .5px;
    text-transform: uppercase;
    border-top: 1px solid var(--box-border);
}

/* ══════════════════════════════════════════════════════════════════════
   CLUSTER CROSS-ANALYSIS — heatmap cross table  (cockpit only)
   loadMatrixCluster() renders a plain HTML table into #matrix_cluster_chart:
   one cell per cluster combination with its figure and share of the total;
   the movements cross-analysis (vipCompMatrixRender) shares the table and
   tints its cells by the diverging trend scale of the movements treemap.
   Subtotal margins frame the grid; the row-label
   column stays pinned while wide matrices scroll horizontally.
   ══════════════════════════════════════════════════════════════════════ */

body.vip-cockpit .vip-cmatrix-wrap {
    overflow-x: auto;
    padding-bottom: 6px;
}
body.vip-cockpit table.vip-cmatrix {
    border-collapse: separate;
    border-spacing: 3px;
    width: 100%;
    table-layout: fixed; /* identical column widths keep the cell fills comparable */
    font-family: var(--vip-font-stack);
}
/* Column heads: bottom-aligned, wrapping where category names run long. */
body.vip-cockpit .vip-cmatrix th.vip-cmatrix-colhead {
    padding: 4px 6px;
    vertical-align: bottom;
    text-align: center;
    font-size: 12px;
    font-weight: 600;
    color: var(--vip-navy-70);
    max-width: 160px;
    overflow-wrap: break-word;
}

/* Row heads: pinned to the left edge over the horizontal scroll. */
body.vip-cockpit .vip-cmatrix th.vip-cmatrix-rowhead {
    position: sticky;
    left: 0;
    z-index: 3;
    background: var(--vip-white);
    padding: 4px 10px 4px 0;
    text-align: right;
    vertical-align: middle;
    font-size: 12px;
    font-weight: 600;
    color: var(--vip-navy-70);
    width: 150px;
    overflow-wrap: break-word;
}
body.vip-cockpit .vip-cmatrix th.vip-cmatrix-subhead { color: var(--vip-navy); }

/* Data cells: identical dimensions across the grid; the main figure with
   the share centred below it, a full tint per cell in change mode. */
body.vip-cockpit .vip-cmatrix td.vip-cmatrix-cell {
    position: relative;
    height: 56px;
    padding: 0;
    text-align: center;
    vertical-align: middle;
    border-radius: 4px;
    border: 1px solid var(--vip-navy-10);
    overflow: hidden; /* clips the cell content to the rounded corners */
    cursor: pointer;
}
body.vip-cockpit .vip-cmatrix-label {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    height: 100%;
    padding: 4px 8px;
}
body.vip-cockpit .vip-cmatrix-main {
    display: block;
    font-size: 13px;
    font-weight: 600;
    color: var(--vip-navy);
    white-space: nowrap;
}
body.vip-cockpit .vip-cmatrix-meta {
    display: block;
    margin-top: 1px;
    font-size: 11px;
    color: var(--vip-navy-70);
    white-space: nowrap;
}

/* Combinations absent from the portfolio stay visible as flat gaps. */
body.vip-cockpit .vip-cmatrix td.vip-cmatrix-cell.is-empty {
    background: none;
    border: 1px dashed var(--vip-navy-20);
    color: var(--vip-navy-40);
    cursor: default;
}

/* Subtotal margins: neutral fill so they read as a frame, not as heat.
   They arrange along the headers - the Sigma column pins beside the row
   heads with their exact recipe (sticky, opaque fill, one z-layer above
   the badge/label layer of the data cells, which carries z-index 1); the
   stronger trailing edges separate the totals band from the data cells. */
body.vip-cockpit .vip-cmatrix td.vip-cmatrix-sub {
    background: var(--vip-cream-50);
    border-color: var(--vip-navy-20);
    cursor: default;
}
body.vip-cockpit .vip-cmatrix th.vip-cmatrix-subcol,
body.vip-cockpit .vip-cmatrix td.vip-cmatrix-cell.vip-cmatrix-subcol {
    position: sticky;
    left: 153px; /* row-head width plus the border-spacing gap */
    z-index: 2;
    background: var(--vip-cream-50);
    border-right-color: var(--vip-navy-40);
}
body.vip-cockpit .vip-cmatrix th.vip-cmatrix-subcol { background: var(--vip-white); }
body.vip-cockpit .vip-cmatrix tr.vip-cmatrix-subrow td { border-bottom-color: var(--vip-navy-40); }
body.vip-cockpit .vip-cmatrix td.vip-cmatrix-grand { border-color: var(--vip-navy-40); }

/* Badge variant (both cross-analyses): every cell carries the property
   count top-left and, in the movements matrix, the movement tag top-right,
   so the label block drops below the tag band and the cells grow accordingly. */
body.vip-cockpit .vip-cmatrix--tags td.vip-cmatrix-cell { height: 62px; }
body.vip-cockpit .vip-cmatrix--tags .vip-cmatrix-label {
    padding: 19px 6px 3px;
    line-height: 1.2;
}
body.vip-cockpit .vip-cmatrix--tags .vip-cmatrix-main { line-height: 1.2; }
body.vip-cockpit .vip-cmatrix--tags .vip-cmatrix-meta {
    margin-top: 0;
    line-height: 1.2;
}
/* Property count: a secondary note, deliberately quieter than the PID chip
   that marks object identity - plain muted text with a small house glyph. */
body.vip-cockpit .vip-cmatrix-count {
    position: absolute;
    top: 4px;
    left: 6px;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: 10px;
    line-height: 16px;
    color: var(--vip-navy-50);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
body.vip-cockpit .vip-cmatrix-count svg,
body.vip-cockpit .vip-cmatrix-count i { font-size: 8px; opacity: .8; }
body.vip-cockpit .vip-cmatrix--tags td .vip-ttile-deltatag {
    top: 4px;
    right: 4px;
}

/* Legend below the grid (both modes): shared vip-tlegend markup, centred
   and wide enough for the three-point scale to breathe. */
body.vip-cockpit .vip-cmatrix-legend:not(:empty) {
    max-width: 460px;
    margin: 14px auto 0;
    border-top: none;
    padding-top: 0;
    text-align: center;
}
body.vip-cockpit .vip-cmatrix-legend .vip-tlegend-scale { gap: 16px; }

/* ---- Top-tenants screen: two-level tenant board, risk map selection, detail panel ---- */
/* The board shares the vip-rla-* table vocabulary of the reversion-by-tenancy panel; the
   rules below only add the deltas (layout, selection, share sub-lines, detail panel). */
body.vip-cockpit .vip-ttb-toolbar {
    display: flex;
    justify-content: flex-start;
    margin-bottom: 6px;
}
body.vip-cockpit .vip-ttb-scroll { overflow-x: auto; }
/* board width follows the colgroup raster; min-width is set inline (depends on walb column) */
/* table heads adopt the AG-Grid header surface; the fallback mirrors the grid theme parameter */
body.vip-cockpit .vip-ttb-table thead th,
body.vip-cockpit .vip-ttb-table tfoot td,
body.vip-cockpit .vip-ttb-table tfoot td.vip-rla-use {
    background: var(--ag-header-background-color, var(--secondary_25));
    color: var(--vip-navy);
}
/* Pooled-tenant continuation modal (showPooledTenantsModal): .modal-body forbids its own
   horizontal scroll (see above), so the wide table already needed .vip-ttb-scroll as an
   independent horizontal scroller. Setting `overflow-x: auto` there computes `overflow-y`
   to `auto` as well per the CSS overflow spec, regardless of what overflow-y is declared
   as - so it silently becomes a second, unbounded vertical scroll container that a sticky
   header inside it can never actually stick against (nothing ever scrolls WITHIN it; the
   whole box just scrolls as one block within .modal-body). Bounding its height here turns
   that already-unavoidable container into a deliberate one, so the header can stick to
   the top of it while the rows scroll underneath - scoped to the modal so the on-page
   board (which has no height constraint of its own and scrolls with the surrounding
   page) is unaffected. */
#tmp_modal .vip-ttb-scroll {
    max-height: calc(100vh - 230px);
    overflow-y: auto;
}
#tmp_modal .vip-ttb-table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
}
/* The footer closes the bounded scroller the way the head opens it: the rows scroll
   between the two while both stay in view. `border-collapse: collapse` hands a collapsed
   cell border to the table rather than to the sticky cell itself, so the separator would
   scroll away with the rows - it is redrawn as a shadow on the cell, which travels with it. */
#tmp_modal .vip-ttb-table tfoot td {
    position: sticky;
    bottom: 0;
    z-index: 1;
    border-top: 0;
    box-shadow: 0 -1px 0 var(--vip-steel);
}
body.vip-cockpit .vip-ttb-table td.vip-rla-use,
body.vip-cockpit .vip-ttb-table th.vip-rla-use {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* drill-down affordance: the whole tenant row toggles the property rows */
body.vip-cockpit tr.vip-ttb-tenant.is-drill { cursor: pointer; }
body.vip-cockpit tr.vip-ttb-prop { cursor: default; }
body.vip-cockpit .vip-ttb-pid { font-weight: 500; color: var(--vip-navy); }
body.vip-cockpit .vip-ttb-address { color: var(--vip-navy-50); font-weight: 400; }
body.vip-cockpit .vip-ttb-actions { margin-right: 8px; white-space: nowrap; }
body.vip-cockpit .vip-ttb-sub {
    display: block;
    font-size: 11px;
    color: var(--vip-navy-30);
}
body.vip-cockpit .vip-ttb-uselabel { white-space: nowrap; }

/* predominant-use completion ring: the base stylesheet scopes svg.completion to the overlays
   at its own size and floats it to the right, which puts it after the label whatever the
   markup says. Every surface sharing the ttUseCell order therefore states size, position and
   float itself - the board, the tenant profile's tables, the overlay's tenant list and the
   valuation comparison. An unstyled ring has neither a size nor a fill rule and renders as a
   large black disc. */
body.vip-cockpit .vip-ttb-table svg.completion,
body.vip-cockpit .vip-tp-table svg.completion,
body.vip-cockpit #tenantlist_current svg.completion,
body.vip-cockpit #overlay_valuation svg.completion,
body.vip-cockpit #tmp_modal svg.completion {
    float: none;
    width: 14px;
    height: 14px;
    vertical-align: -2px;
    margin-right: 4px;
    transform: rotate(-90deg);
}
body.vip-cockpit .vip-ttb-table svg.completion circle.base,
body.vip-cockpit .vip-tp-table svg.completion circle.base,
body.vip-cockpit #tenantlist_current svg.completion circle.base,
body.vip-cockpit #overlay_valuation svg.completion circle.base,
body.vip-cockpit #tmp_modal svg.completion circle.base {
    fill: none;
    stroke: var(--vip-navy-20);
    stroke-width: 16;
}
body.vip-cockpit .vip-ttb-table svg.completion circle.percent,
body.vip-cockpit .vip-tp-table svg.completion circle.percent,
body.vip-cockpit #tenantlist_current svg.completion circle.percent,
body.vip-cockpit #overlay_valuation svg.completion circle.percent,
body.vip-cockpit #tmp_modal svg.completion circle.percent {
    fill: none;
    stroke: var(--vip-navy-70);
    stroke-width: 16;
    stroke-dasharray: 100;
}

/* the board's pooled "Remaining" row opens the continuation table in the shared modal */
body.vip-cockpit tr.vip-ttb-remaining { cursor: pointer; }
/* pooled-tenant continuation modal (showPooledTenantsModal): the whole row jumps to that
   tenant's profile, replacing the old per-row action button */
body.vip-cockpit tr.vip-ttb-pooledrow { cursor: pointer; }

/* Walt-range stack: portfolio reference bar plus width-normalised tenant bars */
body.vip-cockpit .vip-ttws { font-size: 11px; }
body.vip-cockpit .vip-ttws-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin: 2px 0 10px;
    color: var(--vip-navy-70);
}
body.vip-cockpit .vip-ttws-lgitem { display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
body.vip-cockpit .vip-ttws-lgswatch { display: inline-block; width: 9px; height: 9px; border-radius: 2px; }
body.vip-cockpit .vip-ttws-grid {
    display: grid;
    grid-template-columns: 170px 1fr 56px;
    gap: 6px 8px;
    align-items: center;
}
body.vip-cockpit .vip-ttws-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
body.vip-cockpit .vip-ttws-portfolio { font-weight: 600; }
/* relative anchor for the outside walt label, which is positioned at the bar's own width */
body.vip-cockpit .vip-ttws-track { position: relative; min-width: 0; }
body.vip-cockpit .vip-ttws-bar { position: relative; display: flex; height: 18px; border-radius: 3px; overflow: hidden; }
body.vip-cockpit .vip-ttws-seg { height: 100%; }
/* Expired bar: rent whose contractual term has ended. It has no remaining term to
   band, so it carries no segments — and it is not part of the population beneath it,
   so it recedes into the same muted, dashed register the walt range matrix uses for
   its own expired column. */
body.vip-cockpit .vip-ttws-bar.vip-ttws-expiredbar {
    background: var(--vip-cream-50);
    border: 1px dashed var(--vip-navy-20);
}
body.vip-cockpit .vip-ttws-expiredlabel { color: var(--vip-navy-50); font-style: italic; }
/* combined walt-to-break / walt-to-expiry label: inside the bar (white, overlaid on the
   fill, right-aligned) when there is room, outside it (same colour as vip-ttws-value,
   left-aligned) otherwise - the JS toggles between the two once it knows which one fits */
body.vip-cockpit .vip-ttws-waltlabel {
    position: absolute;
    top: 0;
    height: 100%;
    display: flex;
    align-items: center;
    font-size: 10px;
    white-space: nowrap;
    pointer-events: none;
}
body.vip-cockpit .vip-ttws-waltlabel-in { right: 4px; color: #fff; }
body.vip-cockpit .vip-ttws-waltlabel-out { left: 0; margin-left: 4px; color: var(--vip-navy-70); display: none; }
/* separator between the portfolio reference and the tenant bars */
body.vip-cockpit .vip-ttws-sep {
    grid-column: 1 / -1;
    border-top: 1px solid var(--box-border);
    margin: 2px 0 4px;
}
body.vip-cockpit .vip-ttws-value { text-align: right; color: var(--vip-navy-70); }
body.vip-cockpit .vip-ttws-empty { color: var(--vip-navy-50); padding: 8px 0; }
/* Risk map: diverging rent-gap legend above the chart (shared vip-tlegend vocabulary); the
   chart itself spans the full box width and needs no further layout rules of its own */
body.vip-cockpit .vip-ttrm-legend { max-width: 260px; margin: 0 0 6px 2px; }

/* Walt range risk matrix: rent gap class (rows) against remaining lease term (columns).
   Margins lead - the total row is the first body row, the total column the first data
   column - so the eye enters at the aggregate and works outward into the detail. */
body.vip-cockpit .vip-ttrx-wrap { overflow-x: auto; padding-bottom: 4px; }
body.vip-cockpit table.vip-ttrx {
    border-collapse: separate;
    border-spacing: 3px;
    width: 100%;
    table-layout: fixed; /* equal data columns keep the cell fills comparable */
    font-family: var(--vip-font-stack);
}
body.vip-cockpit .vip-ttrx th.vip-ttrx-colhead {
    padding: 2px 4px;
    vertical-align: bottom;
    text-align: center;
    font-size: 11px;
    font-weight: 600;
    color: var(--vip-navy-70);
    overflow-wrap: break-word;
}
/* Row heads stay put over the horizontal scroll of the wrap. */
body.vip-cockpit .vip-ttrx th.vip-ttrx-rowhead {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--vip-white);
    padding: 2px 8px 2px 0;
    text-align: right;
    vertical-align: middle;
    font-size: 11px;
    font-weight: 600;
    color: var(--vip-navy-70);
    overflow-wrap: break-word;
}
body.vip-cockpit .vip-ttrx th.vip-ttrx-corner { background: var(--vip-white); position: sticky; left: 0; z-index: 2; }
body.vip-cockpit .vip-ttrx .vip-ttrx-marginhead { color: var(--vip-navy); }
body.vip-cockpit .vip-ttrx td.vip-ttrx-cell {
    height: 46px;
    padding: 2px 4px;
    text-align: center;
    vertical-align: middle;
    border-radius: 4px;
    border: 1px solid var(--vip-navy-10);
    cursor: default;
    /* Data cells rest on a neutral surface: the rows already separate the population by
       gap class, so the heat is reserved for the margins, where it still says something. */
    background: var(--vip-cream-50);
}
body.vip-cockpit .vip-ttrx-main {
    display: block;
    font-size: 13px;
    font-weight: 600;
    color: var(--vip-navy);
    white-space: nowrap;
}
body.vip-cockpit .vip-ttrx-meta {
    display: block;
    margin-top: 1px;
    font-size: 10px;
    color: var(--vip-navy-70);
    white-space: nowrap;
}
/* Margins carry the diverging fill and a firmer outline; that contrast against the
   neutral data cells is what separates the totals from the grid. */
body.vip-cockpit .vip-ttrx td.vip-ttrx-margin { border-color: var(--vip-navy-40); }
/* Combinations absent from the portfolio stay visible as flat gaps. */
body.vip-cockpit .vip-ttrx td.vip-ttrx-cell:not(.is-empty) { cursor: pointer; }
body.vip-cockpit .vip-ttrx td.vip-ttrx-cell.is-empty {
    background: none;
    border: 1px dashed var(--vip-navy-20);
}
/* Expired column: leases whose term ended before the analysis date. It stands beside
   the standing rent roll rather than inside it and feeds no total, so it is set apart
   by a rule on the Total column that follows it and reads in a muted register - the
   figure is context, not part of the population the grid analyses. */
body.vip-cockpit .vip-ttrx .vip-ttrx-expiredhead { color: var(--vip-navy-50); font-style: italic; }
body.vip-cockpit .vip-ttrx td.vip-ttrx-expired {
    background: var(--vip-cream-50);
    border: 1px dashed var(--vip-navy-20);
}
/* The figures themselves recede too - frame alone is not enough to keep the column from
   reading as part of the population beside it. */
body.vip-cockpit .vip-ttrx td.vip-ttrx-expired .vip-ttrx-main { color: var(--vip-navy-50); font-weight: 500; }
body.vip-cockpit .vip-ttrx td.vip-ttrx-expired .vip-ttrx-meta { color: var(--vip-navy-40); }
/* Gutter between the expired column and the totals. The rule lives inside a single cell
   spanning every body row, because the table is border-collapse:separate with 3px
   spacing - a border repeated per cell would break into dashes at every row gap. */
body.vip-cockpit .vip-ttrx .vip-ttrx-gutter {
    padding: 0;
    border: 0;
    background: none;
    position: relative;
}
body.vip-cockpit .vip-ttrx .vip-ttrx-rule {
    position: absolute;
    top: 2px;
    bottom: 2px;
    left: 50%;
    width: 2px;
    margin-left: -1px;
    background: var(--vip-navy-40);
}
/* Legend and scope note below the grid, on the shared vip-tlegend vocabulary. */
/* Legend above the grid: the colour scale has to be readable before the cells are. */
body.vip-cockpit .vip-ttrx-head { max-width: 260px; margin: 0 0 8px 2px; }

/* D3 axis text defaults to a presentation attribute (font-family="sans-serif") of
   zero CSS specificity, so this plain element selector overrides it without
   touching the vendor bundle. Covers raw D3 charts outside dimple (cross-cluster
   heatmap, value movements treemap); dimple's own axes already carry their own
   font-family via .style(), which this cannot and does not need to beat. */
body.vip-cockpit .tick text { font-family: var(--vip-font-stack); }

/* ══════════════════════════════════════════════════════════════════════
   TENANCY ANALYSIS DATE  (Tenancies)  — .vip-adate-* / .vip-adm-*
   The analysis date is one setting for the whole of Tenancies and is set
   in exactly one place. Two vocabularies serve it: .vip-adate-* is the
   sub-sidebar statement of the date in force, which only opens the
   dialogue, and .vip-adm-* is that dialogue itself — the date field, its
   presets, the reconciliation that previews the draft, and the explanation
   of what the setting does and does not do.
   ══════════════════════════════════════════════════════════════════════ */
/* Sub-sidebar statement. It reads as a value with an action attached rather
   than as a form control, because on this panel it no longer is one. */
body.vip-cockpit .vip-adate-box {
    display: block;
    width: 100%;
    padding: 8px 10px;
    border: 1px solid var(--box-border);
    border-radius: 6px;
    background-color: #ffffff;
    color: var(--vip-navy);
    text-align: left;
    cursor: pointer;
}
body.vip-cockpit .vip-adate-box:hover { border-color: var(--vip-steel); }
body.vip-cockpit .vip-adate-box:focus:not(:focus-visible) { outline: none; }
/* A date that has left its default is the exception, not the norm, and says so. */
body.vip-cockpit .vip-adate-box.is-custom { border-left: 3px solid var(--vip-yellow); }
body.vip-cockpit .vip-adate-value {
    display: block;
    font-size: 13px;
    font-weight: 600;
    line-height: 1.3;
}
body.vip-cockpit .vip-adate-note {
    display: block;
    font-size: 10px;
    color: var(--vip-navy-50);
}
body.vip-cockpit .vip-adate-action {
    display: block;
    margin-top: 4px;
    font-size: 11px;
    color: var(--vip-steel);
}

/* ── the dialogue ─────────────────────────────────────────────────────── */
body.vip-cockpit .vip-adm {
    display: flex;
    flex-direction: column;
    gap: 12px;
    font-size: 12px;
    color: var(--vip-navy);
}
/* Setting the date and seeing what it costs sit side by side: the reconciliation
   is the consequence of the control beside it, not a separate topic. */
body.vip-cockpit .vip-adm-row {
    display: grid;
    grid-template-columns: minmax(0, 4fr) minmax(0, 8fr);
    gap: 12px;
    align-items: start;
}
body.vip-cockpit .vip-adm-cols {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 6px 24px;
    margin-top: 12px;
}
body.vip-cockpit .vip-adm-subhead {
    margin-bottom: 4px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--vip-navy-70);
}
body.vip-cockpit ul.vip-adm-bullets {
    margin: 0;
    padding-left: 16px;
    line-height: 1.55;
    color: var(--vip-navy-70);
}
body.vip-cockpit .vip-adm-conclusion {
    margin-top: 12px;
    font-weight: 600;
}
body.vip-cockpit .vip-adm-note-plain {
    margin-top: 6px;
    font-size: 11px;
    line-height: 1.5;
    color: var(--vip-navy-50);
}
/* The one control the dialogue exists for. It is sized and weighted as the primary
   decision on the panel rather than as one form field among several, because every
   other element here either feeds it or reports on it. */
body.vip-cockpit input.vip-adm-input {
    width: 100%;
    background-color: #ffffff;
    border: 2px solid var(--vip-navy);
    border-radius: 8px;
    color: var(--vip-navy);
    font-size: 20px;
    font-weight: 600;
    letter-spacing: .01em;
    height: auto;
    padding: 10px 12px;
}
body.vip-cockpit input.vip-adm-input:focus {
    background-color: #ffffff;
    border-color: var(--vip-navy);
    box-shadow: 0 0 0 3px var(--vip-yellow);
    color: var(--vip-navy);
}
/* The panel that holds it takes the same accent, so the eye lands on the pair. */
body.vip-cockpit .vip-adm-setpanel { border-color: var(--vip-navy); }
body.vip-cockpit .vip-adm-span {
    margin-top: 6px;
    font-size: 11px;
    color: var(--vip-navy-50);
    line-height: 1.5;
}
body.vip-cockpit .vip-adm-recommend { color: var(--vip-navy-70); font-weight: 600; }

/* Presets stacked, not wrapped: the column is narrow and four offers read as a
   list of choices rather than as a row of buttons that happened to break. The
   first of them is also the reset — it re-attaches the date to the latest
   valuation date rather than pinning it to a day — so it needs no control of
   its own beside them. */
body.vip-cockpit .vip-adm-presets {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 12px;
}
body.vip-cockpit .vip-adm-preset {
    padding: 5px 10px;
    border: 1px solid var(--box-border);
    border-radius: 4px;
    background: #ffffff;
    color: var(--vip-navy);
    text-align: left;
    cursor: pointer;
}
body.vip-cockpit .vip-adm-preset:hover { border-color: var(--vip-steel); }
body.vip-cockpit .vip-adm-preset:focus:not(:focus-visible) { outline: none; }
body.vip-cockpit .vip-adm-preset.is-on {
    border-color: var(--vip-navy);
    background: var(--vip-navy);
    color: #ffffff;
}
body.vip-cockpit .vip-adm-presetlabel { display: block; font-size: 11px; font-weight: 600; }
body.vip-cockpit .vip-adm-presetdate { display: block; font-size: 10px; opacity: 0.75; }

/* Reconciliation. The operator sits in its own fixed column so the
   movements line up under one another and read as a bridge. */
body.vip-cockpit table.vip-adm-bridge { width: 100%; border-collapse: collapse; }
body.vip-cockpit table.vip-adm-bridge th,
body.vip-cockpit table.vip-adm-bridge td {
    padding: 4px 6px;
    font-weight: 400;
    text-align: left;
    vertical-align: top;
}
body.vip-cockpit table.vip-adm-bridge thead th {
    font-size: 10px;
    color: var(--vip-navy-50);
    border-bottom: 1px solid var(--box-border);
}
body.vip-cockpit table.vip-adm-bridge .vip-adm-amount { text-align: right; width: 130px; white-space: nowrap; }
/* The operator is repeated on the figure itself, held left of the digits so the
   numerals stay right-aligned against one another. */
body.vip-cockpit .vip-adm-sign {
    float: left;
    color: var(--vip-navy-50);
}
/* An amount with the contracts behind it advertises the fact, in the same
   register the vacancy board uses for its own tenant lists. */
body.vip-cockpit table.vip-adm-bridge td.vip-adm-amount.has-detail {
    cursor: help;
    text-decoration: underline dotted var(--vip-navy-40);
    text-underline-offset: 3px;
}
body.vip-cockpit table.vip-adm-bridge td.vip-adm-amount.has-detail:hover {
    text-decoration-color: var(--vip-navy);
}
body.vip-cockpit .vip-adm-op {
    display: inline-block;
    width: 14px;
    color: var(--vip-navy-50);
}
body.vip-cockpit table.vip-adm-bridge tr.vip-adm-bridge-open th,
body.vip-cockpit table.vip-adm-bridge tr.vip-adm-bridge-open td { font-weight: 600; }
/* Subtotal between the data corrections and the timing movements. It carries a
   rule of its own rather than the closing line's weight: it separates two
   groups, it does not end the bridge. */
body.vip-cockpit table.vip-adm-bridge tr.vip-adm-bridge-sub th,
body.vip-cockpit table.vip-adm-bridge tr.vip-adm-bridge-sub td {
    border-top: 1px solid var(--box-border);
    border-bottom: 1px solid var(--box-border);
    font-weight: 600;
}
body.vip-cockpit table.vip-adm-bridge tfoot tr.vip-adm-bridge-close th,
body.vip-cockpit table.vip-adm-bridge tfoot tr.vip-adm-bridge-close td {
    border-top: 1px solid var(--vip-navy);
    font-weight: 600;
}

/* ══════════════════════════════════════════════════════════════════════
   VACANCY & RE-LETTING  (Tenancies)  — .vip-vac-*
   Two-level vacancy board at the tenancy analysis date, pivoted either
   way from the segmented control in the box header. Built on the shared
   .vip-rla-* vocabulary; only what is specific to this screen lives here:
   the scroll frame that pins header and footer, the column-block
   separators, the sort affordance and the scope-cell treatment.
   ══════════════════════════════════════════════════════════════════════ */
body.vip-cockpit {
    /* Height of the scrolling row area. Header, footer and the surrounding
       box stay put; only the rows move, so the element keeps a predictable
       footprint however many assets are in the selection. */
    --vip-vac-bodyheight: calc(100vh - 360px);
}

/* Conditional control: the expiry basis only appears where a break option can
   change a band. */

/* ── Scroll frame: only the rows move ─────────────────────────────────── */
body.vip-cockpit .vip-vac-scroll {
    width: 100%;
    max-height: var(--vip-vac-bodyheight);
    overflow: auto;
    overscroll-behavior: contain;
}
/* thead already sticks via .vip-rla-table; the second row is offset in JS
   once the first row's height is known. Neither header row carries a
   rowspan, so that height is a single line and the offset is exact. */
body.vip-cockpit .vip-vac-table thead th { z-index: 6; }
body.vip-cockpit .vip-vac-table thead tr.vip-rla-units th { z-index: 5; }
body.vip-cockpit .vip-vac-table tfoot td {
    position: sticky;
    bottom: 0;
    z-index: 5;
}

/* ── Density ──────────────────────────────────────────────────────────── */
/* Twelve columns need every pixel the identity column can spare; the shared
   .vip-rla-table padding is comfortable for a shorter table but not here. */
body.vip-cockpit .vip-vac-table thead th { padding: 6px 6px; }
body.vip-cockpit .vip-vac-table tbody td,
body.vip-cockpit .vip-vac-table tfoot td { padding: 4px 6px; }

/* ── Column blocks ────────────────────────────────────────────────────── */
/* A leading rule on the first cell of each block, carried by the same column
   ordinal in the header, the body and the footer. Painted as an inset shadow
   rather than a border: the table collapses its borders, and a collapsed
   border on a sticky cell is dropped by the browser. */
body.vip-cockpit .vip-vac-table th.vip-vac-grpstart,
body.vip-cockpit .vip-vac-table td.vip-vac-grpstart {
    box-shadow: inset 2px 0 0 var(--vip-navy-30);
}
body.vip-cockpit .vip-vac-table thead th.vip-vac-grpstart {
    box-shadow: inset 2px 0 0 rgba(255, 255, 255, .45);
}

/* ── Sortable headers ─────────────────────────────────────────────────── */
body.vip-cockpit .vip-vac-table th.vip-vac-sortable { cursor: pointer; user-select: none; }
/* The direction mark is reserved on every sortable column and only inked on
   the active one, so a sort change never shifts the header text. */
body.vip-cockpit .vip-vac-sortmark { margin-left: 5px; font-size: 10px; opacity: 0; }
body.vip-cockpit .vip-vac-table th.vip-vac-sortable:hover .vip-vac-sortmark { opacity: .55; }
body.vip-cockpit .vip-vac-table th.vip-vac-sortable.is-sorted .vip-vac-sortmark { opacity: 1; }

/* ── Rows ─────────────────────────────────────────────────────────────── */
/* Nothing in the body is emphasised except the asset id — it is the handle
   the reader navigates by, and weight anywhere else competes with it. */
body.vip-cockpit .vip-vac-table tbody td,
body.vip-cockpit .vip-vac-table tbody tr.vip-rla-usetop td { font-weight: 400; }
body.vip-cockpit .vip-vac-table .vip-vac-pid { font-weight: 600; }
body.vip-cockpit .vip-vac-table .vip-vac-actions { margin-right: 6px; }
body.vip-cockpit .vip-vac-table td.vip-vac-valdate { color: var(--vip-navy-60); }
/* The drill-down level carries no click handler, so it must not offer one. */
body.vip-cockpit .vip-vac-table tbody tr.vip-vac-child,
body.vip-cockpit .vip-vac-table tbody tr.vip-vac-child td { cursor: default; }
/* Secondary text inside a cell: the months behind a per-area cost, the
   percentage behind the agency months, or the measure of a unit-based row. */
body.vip-cockpit .vip-vac-table .vip-vac-sub {
    margin-left: 4px;
    font-size: 11px;
    color: var(--vip-navy-50);
}

/* ── Scope cells ──────────────────────────────────────────────────────── */
/* In-place vacancy and the two expiry bands. In scope the figure reads behind
   a tick; out of scope it recedes. This pair is the only place the analysis
   scope shows. */
body.vip-cockpit .vip-vac-table td.vip-vac-scopecell.is-out { color: var(--vip-navy-40); }
body.vip-cockpit .vip-vac-tick {
    margin-right: 5px;
    font-size: 10px;
    color: var(--raspberry);
}
/* A cell with a tenant list behind it advertises the fact: the figure carries
   a dotted rule and the cursor changes. */
body.vip-cockpit .vip-vac-table td.vip-vac-scopecell.has-detail { cursor: help; }
body.vip-cockpit .vip-vac-table td.vip-vac-scopecell.has-detail .vip-vac-figure {
    border-bottom: 1px dotted var(--vip-navy-40);
    padding-bottom: 1px;
}
body.vip-cockpit .vip-vac-table td.vip-vac-scopecell.has-detail:hover .vip-vac-figure {
    border-bottom-color: var(--vip-navy);
}

/* Portfolio roll-up. Every field of a base-normalised node is additive, so the
   row is a plain sum of the rows above it. Styled like the tenant list's own
   footer (.vip-ttb-table): it closes the table on the header's surface, not
   the body's, so it reads as a frame rather than one more row. */
body.vip-cockpit .vip-vac-table tfoot tr.vip-vac-total td {
    background: var(--ag-header-background-color, var(--secondary_25));
    color: var(--vip-navy);
    font-weight: 600;
    border-bottom: 0;
}
body.vip-cockpit .vip-vac-table tfoot tr.vip-vac-total td.vip-rla-use { text-align: left; }
body.vip-cockpit .vip-vac-table tfoot tr.vip-vac-total .vip-vac-grpstart { box-shadow: inset 2px 0 0 var(--vip-navy-30); }

