/* ==========================================================================
   vryno-platform.css  —  the platform layer.
   ==========================================================================

   Everything here is NEW surface: the scrollable app strip, the command
   palette, global search results, toasts, skeletons, empty states, the AI
   panel, the related-records panel and the Needs-you card. Nothing in this
   file restyles an existing component, so it cannot regress a page.

   RULES THIS FILE FOLLOWS (they are the ones the audit asked for)

   1. No hex literal for anything themeable. Every colour resolves from a
      token already defined in style.css, with a hard fallback so this file
      still renders if a token is missing.
   2. Dark mode is expressed the way this app already expresses it --
      [data-bs-theme="dark"] on <html> -- not prefers-color-scheme. Matching
      the existing mechanism is what stops dark-theme-fixes.css from growing.
   3. Layout uses flex/grid + gap. No per-element margins that collapse.
   4. Every interactive element gets a visible :focus-visible ring.
   5. Anything wide scrolls inside its own container. The page body never
      scrolls sideways.
   ========================================================================== */

:root {
  --vp-radius: 10px;
  --vp-radius-lg: 14px;
  --vp-radius-pill: 999px;
  --vp-surface: var(--white, #fff);
  --vp-surface-2: var(--light-100, #f7f7fb);
  --vp-ink: var(--heading-color, #1a1535);
  --vp-ink-soft: var(--gray-600, #5c5875);
  --vp-ink-faint: var(--gray-400, #9b95c9);
  --vp-line: var(--border-color, #e6e3f3);
  --vp-accent: var(--primary, #5235ef);
  --vp-accent-soft: var(--primary-transparent, #ece7fb);
  --vp-shadow: 0 1px 2px rgba(20, 18, 48, .06), 0 10px 30px -18px rgba(20, 18, 48, .28);
  --vp-shadow-lg: 0 24px 70px -24px rgba(20, 18, 48, .45);
  --vp-strip-h: 52px;
  --vp-z-strip: 9;
  --vp-z-panel: 1085;
  --vp-z-overlay: 1090;
  --vp-z-toast: 1100;
}

[data-bs-theme="dark"] {
  /* Map onto the app's OWN dark tokens rather than inventing a second dark
     palette. dark-theme-fixes.css already defines --dtf-*; reusing them is what
     keeps this file from becoming another override layer, and it is why a card
     here sits on the same violet-black as the page instead of drifting grey. */
  --vp-surface: var(--dtf-surface, #0d0d26);
  --vp-surface-2: var(--dtf-surface-2, #13132e);
  --vp-ink: var(--dtf-heading, #e8eaff);
  --vp-ink-soft: var(--dtf-text, #a9b1c7);
  --vp-ink-faint: var(--dtf-muted, #7f869e);
  --vp-line: var(--dtf-border, #26264f);
  --vp-accent-soft: rgba(103, 76, 226, .26);
  --vp-shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 10px 30px -18px rgba(0, 0, 0, .85);
  --vp-shadow-lg: 0 24px 70px -24px rgba(0, 0, 0, .92);
}

.vp-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Keyboard users could not see where they were anywhere in the app. */
/* .vp-scope :focus-visible,
.vp-btn:focus-visible,
.vp-strip-arrow:focus-visible,
.vp-result:focus-visible,
.vp-cmd-row:focus-visible {
  outline: 2px solid var(--vp-accent);
  outline-offset: 2px;
  border-radius: 6px;
} */

/* Skip link — there was no way to bypass the topbar with a keyboard. */
.vp-skip {
  position: fixed;
  top: -60px;
  left: 12px;
  z-index: 2000;
  background: var(--vp-accent);
  color: #fff;
  padding: 10px 16px;
  border-radius: var(--vp-radius);
  font-size: 13px;
  font-weight: 600;
  text-decoration: none;
  transition: top .15s ease;
}

.vp-skip:focus {
  top: 12px;
  color: #fff;
}

/* ==========================================================================
   1. BOTTOM APP STRIP  —  scrollable, three fixed zones
   ==========================================================================

   The strip was `position:fixed; bottom:-14px; overflow-x:auto` with the
   Add Apps button `position:absolute; right:0` INSIDE that scrolling box --
   so the button drifted with the scroll and overlapped the tabs, and there
   was no affordance telling the user more apps existed off-screen.

   New structure, built at runtime by vryno-platform.js so no page markup
   changes:

   APPS ONLY. Workspace destinations (Home, My Work, Search, Calendar, Files,
   Activity) live at the top of the sidebar, and every settings destination is
   in the profile dropdown. Nothing but apps goes in this bar.

     .vr-module-strip            grid: [scroller] [add]
       .vp-strip-scroll-wrap     the scrolling region
         .vp-strip-scroll        the original <ul> lives in here
         arrows + off-screen count
       .vp-strip-tail            Add Apps, pinned right, never scrolls
   ========================================================================== */

.vr-module-strip.vp-strip {
  /* bottom was -14px, which cropped the strip against the viewport edge */
  bottom: 0 !important;
  display: grid !important;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: stretch;
  gap: 0 !important;
  /* height: var(--vp-strip-h); */

  overflow: visible !important;
  background: var(--vp-surface);
  border-top: 1px solid var(--vp-line);
  box-shadow: 0 -2px 14px -6px rgba(20, 18, 48, .28);
}

/* ---- zone 1: the scrolling app rail ------------------------------------ */

.vp-strip-scroll-wrap {
  position: relative;
  min-width: 0;
  /* lets the grid column actually shrink */
  display: flex;
  align-items: stretch;
}

.vp-strip-scroll {
  flex: 1 1 auto;
  min-width: 0;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-behavior: smooth;
  scrollbar-width: none;
  /* Firefox */
  -ms-overflow-style: none;
  /* old Edge */
  overscroll-behavior-x: contain;
  /* don't trigger page/back gestures */
  scroll-snap-type: x proximity;
}

.vp-strip-scroll::-webkit-scrollbar {
  height: 0;
}

/* The original <ul class="nav nav-tabs"> keeps all its classes; it just has
   to stop wrapping and stop adding a bottom margin inside a 52px bar. */
.vp-strip-scroll>.nav {
  display: flex !important;
  flex-wrap: nowrap !important;
  align-items: center;
  margin: 0 !important;
  /* padding: 0 4px; */
  border: 0 !important;
  height: 100%;
  min-width: max-content;
}

.vp-strip-scroll>.nav>.nav-item {
  flex: 0 0 auto;
  scroll-snap-align: start;
}

/* Tabs were `padding: 8px 34px` — 8 apps filled a 1440px bar, so the ninth
   was already off-screen with nothing to indicate it. Tighter, and they now
   scroll properly instead of being clipped. */
.vp-strip-scroll>.nav .nav-link {
  display: inline-flex !important;
  align-items: center;
  gap: 7px;
  height: 36px;
  padding: 0 14px !important;
  border: 0 !important;
  border-radius: var(--vp-radius) !important;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--vp-ink-soft);
  white-space: nowrap;
}

.vp-strip-scroll>.nav .nav-link:hover {
  background: var(--vp-accent-soft);
  color: var(--vp-accent);
}

.vp-strip-scroll>.nav .nav-link.active {
  background: var(--vp-accent) !important;
  color: #fff !important;
}

.vp-strip-scroll>.nav .nav-link .tab-icon {
    width: 22px;
    height: 22px;
    flex: 0 0 22px;
    border-radius: 6px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: #f1eeff;
    border: 1px solid #baadff;

}

.vp-strip-scroll>.nav .nav-link .tab-icon img {
  max-width: 14px;
  max-height: 14px;
}

/* Scroll affordance: arrows appear only when there is somewhere to scroll. */
.vp-strip-arrow {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 30px;
  display: none;
  align-items: center;
  justify-content: center;
  border: 0;
  background: var(--vp-surface);
  color: var(--vp-ink-soft);
  cursor: pointer;
  z-index: 2;
}

.vp-strip-arrow.is-on {
  display: flex;
}

.vp-strip-arrow:hover {
  color: var(--vp-accent);
  background: var(--vp-accent-soft);
}

.vp-strip-arrow.prev {
  left: 0;
  box-shadow: 10px 0 12px -8px rgba(20, 18, 48, .3);
}

.vp-strip-arrow.next {
  right: 0;
  box-shadow: -10px 0 12px -8px rgba(20, 18, 48, .3);
}

.vp-strip-arrow i {
  font-size: 17px;
}

/* A count of what is off-screen, so "more apps exist" is stated, not implied. */
.vp-strip-more {
  position: absolute;
  right: 34px;
  top: 50%;
  transform: translateY(-50%);
  display: none;
  padding: 4px 5px;
  border-radius: var(--vp-radius-pill);
    background: #25bd67;
  color: #fff;
  font-size: 10.5px;
  font-weight: 700;
  pointer-events: none;
  z-index: 3;
}

.vp-strip-more.is-on {
  display: block;
}

/* ---- zone 2: Add Apps, pinned right ----------------------------------- */

.vp-strip-tail {
  display: flex;
  align-items: center;
  padding: 0 6px 0 0;
  border-left: 1px solid var(--vp-line);
  background: var(--vp-surface-2);
  flex-shrink: 0;
}

/* Neutralise the old absolute positioning without editing 909 pages. */
.vp-strip-tail .vr-strip-add {
  position: static !important;
  height: 34px !important;
  margin: 0 0 0 6px !important;
padding: 18px 11px !important;
  /* border-radius: var(--vp-radius) !important; */
  white-space: nowrap;
  font-size: 12.5px;
}

@media (max-width: 900px) {
  .vp-strip-tail .vr-strip-add .vp-add-text {
    display: none;
  }
}

/* The strip is fixed, so page content needs room underneath it. */
body.vp-has-strip .page-wrapper,
body.vp-has-strip #contentContainer {
  padding-bottom: calc(var(--vp-strip-h) + 8px);
}

/* ---- small screens ----------------------------------------------------- */
/* Below Bootstrap's lg breakpoint the sidebar is a slide-in overlay, not a
   static column -- the app's own topbar proves it, with #mobile_btn marked
   d-lg-none. style.css nonetheless leaves padding-left: var(--sidenav-width)
   on the strip, so at 390px the reserved 200px plus the platform zone's
   224px plus Add Apps' 49px exceeded the viewport: the middle track collapsed
   to 0 and NOT ONE app tab was reachable. Measured, not guessed. */
@media (max-width: 991.98px) {
  .vr-module-strip.vp-strip {
    padding-left: 0 !important;
    padding-right: 0 !important;
  }
}

/* Tighter tabs on a phone so more than one app is visible at a time. */
@media (max-width: 600px) {
  .vp-strip-scroll>.nav .nav-link {
    padding: 0 11px !important;
  }
}

/* ==========================================================================
   2. COMMAND PALETTE + GLOBAL SEARCH
   ==========================================================================
   One overlay serves both. Ctrl/Cmd+K opens it in command mode; typing in the
   topbar search field opens it in search mode with the query carried across.
   ========================================================================== */

.vp-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--vp-z-overlay);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 10vh 16px 16px;
  background: rgba(16, 14, 40, .46);
  backdrop-filter: blur(3px);
  opacity: 0;
  pointer-events: none;
  transition: opacity .16s ease;
}

.vp-overlay.is-open {
  opacity: 1;
  pointer-events: auto;
}

.vp-cmd {
  width: 100%;
  max-width: 680px;
  max-height: 72vh;
  display: flex;
  flex-direction: column;
  background: var(--vp-surface);
  border: 1px solid var(--vp-line);
  border-radius: var(--vp-radius-lg);
  box-shadow: var(--vp-shadow-lg);
  overflow: hidden;
  transform: translateY(-8px) scale(.985);
  transition: transform .16s cubic-bezier(.22, 1, .36, 1);
}

.vp-overlay.is-open .vp-cmd {
  transform: none;
}

.vp-cmd-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 13px 16px;
  border-bottom: 1px solid var(--vp-line);
  flex-shrink: 0;
}

.vp-cmd-head>i {
  font-size: 19px;
  color: var(--vp-ink-faint);
}

.vp-cmd-input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  outline: 0;
  background: transparent;
  font-size: 15.5px;
  color: var(--vp-ink);
}

.vp-cmd-input::placeholder {
  color: var(--vp-ink-faint);
}

.vp-cmd-scope {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 9px;
  border-radius: var(--vp-radius-pill);
  background: var(--vp-accent-soft);
  color: var(--vp-accent);
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
  border: 0;
  cursor: pointer;
}

.vp-cmd-esc {
  padding: 3px 7px;
  border-radius: 5px;
  background: var(--vp-surface-2);
  border: 1px solid var(--vp-line);
  color: var(--vp-ink-faint);
  font-size: 10.5px;
  font-weight: 700;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

.vp-cmd-body {
  overflow-y: auto;
  padding: 6px;
  flex: 1 1 auto;
}

.vp-cmd-group {
  padding: 9px 10px 4px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--vp-ink-faint);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.vp-cmd-row {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  padding: 9px 10px;
  border: 0;
  border-radius: var(--vp-radius);
  background: transparent;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.vp-cmd-row:hover,
.vp-cmd-row.is-active {
  background: var(--vp-accent-soft);
}

.vp-cmd-ico {
  width: 30px;
  height: 30px;
  flex: 0 0 30px;
  border-radius: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: #f4f4f4;
  color: var(--vp-accent);
  font-size: 15px;
}

.vp-cmd-main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.vp-cmd-title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--vp-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.vp-cmd-title mark {
  background: transparent;
  color: var(--vp-accent);
  font-weight: 700;
  padding: 0;
}

.vp-cmd-sub {
  font-size: 11.5px;
  color: var(--vp-ink-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.vp-cmd-tag {
  flex: 0 0 auto;
  padding: 2px 8px;
  border-radius: var(--vp-radius-pill);
  background: var(--vp-surface-2);
  border: 1px solid var(--vp-line);
  color: var(--vp-ink-soft);
  font-size: 10.5px;
  font-weight: 700;
  white-space: nowrap;
}

.vp-cmd-kbd {
  flex: 0 0 auto;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10.5px;
  color: var(--vp-ink-faint);
}

.vp-cmd-foot {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  padding: 9px 16px;
  border-top: 1px solid var(--vp-line);
  background: #f3f3f3;
  font-size: 11px;
  color: var(--vp-ink-faint);
  flex-shrink: 0;
  
}

.vp-cmd-foot b {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--vp-ink-soft);
  font-weight: 700;
}

/* ==========================================================================
   3. TOASTS  —  there was no way for the app to say what just happened
   ========================================================================== */

.vp-toasts {
  position: fixed;
  right: 18px;
  bottom: calc(var(--vp-strip-h) + 18px);
  z-index: var(--vp-z-toast);
  display: flex;
  flex-direction: column;
  gap: 9px;
  width: min(380px, calc(100vw - 36px));
  pointer-events: none;
}

.vp-toast {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  padding: 12px 13px;
  border-radius: var(--vp-radius);
  border: 1px solid var(--vp-line);
  border-left: 3px solid var(--vp-accent);
  background: var(--vp-surface);
  box-shadow: var(--vp-shadow);
  pointer-events: auto;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity .18s ease, transform .18s ease;
}

.vp-toast.is-in {
  opacity: 1;
  transform: none;
}

.vp-toast.is-out {
  opacity: 0;
  transform: translateX(12px);
}

.vp-toast.ok {
  border-left-color: var(--success, #22c55e);
}

.vp-toast.warn {
  border-left-color: var(--warning, #f59e0b);
}

.vp-toast.err {
  border-left-color: var(--danger, #ef4444);
}

.vp-toast-ico {
  font-size: 17px;
  color: var(--vp-accent);
  line-height: 1.3;
}

.vp-toast.ok .vp-toast-ico {
  color: var(--success, #22c55e);
}

.vp-toast.warn .vp-toast-ico {
  color: var(--warning, #f59e0b);
}

.vp-toast.err .vp-toast-ico {
  color: var(--danger, #ef4444);
}

.vp-toast-body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

.vp-toast-title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--vp-ink);
}

.vp-toast-sub {
  font-size: 12px;
  color: var(--vp-ink-faint);
  margin-top: 1px;
}

.vp-toast-undo {
  border: 0;
  background: transparent;
  color: var(--vp-accent);
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  padding: 3px 0;
  margin-top: 5px;
}

.vp-toast-x {
  border: 0;
  background: transparent;
  color: var(--vp-ink-faint);
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
  padding: 2px;
}

/* ==========================================================================
   4. SKELETONS  —  zero loading states existed anywhere
   ========================================================================== */

.vp-sk {
  display: block;
  border-radius: 6px;
  background: linear-gradient(90deg,
      var(--vp-surface-2) 0%,
      color-mix(in srgb, var(--vp-surface-2) 55%, var(--vp-line)) 50%,
      var(--vp-surface-2) 100%);
  background-size: 200% 100%;
  animation: vp-sheen 1.25s ease-in-out infinite;
}

.vp-sk-text {
  height: 11px;
  margin-bottom: 8px;
}

.vp-sk-text.w-40 {
  width: 40%;
}

.vp-sk-text.w-60 {
  width: 60%;
}

.vp-sk-text.w-80 {
  width: 80%;
}

.vp-sk-title {
  height: 17px;
  width: 45%;
  margin-bottom: 12px;
}

.vp-sk-tile {
  height: 84px;
  border-radius: var(--vp-radius);
}

.vp-sk-chart {
  height: 210px;
  border-radius: var(--vp-radius);
}

.vp-sk-avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
}

.vp-sk-row {
  height: 44px;
  margin-bottom: 6px;
  border-radius: 8px;
}

@keyframes vp-sheen {
  0% {
    background-position: 200% 0;
  }

  100% {
    background-position: -200% 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .vp-sk {
    animation: none;
  }
}

/* ==========================================================================
   5. EMPTY STATES  —  present on only 23 of 64 pages, all different
   ========================================================================== */

.vp-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 46px 24px;
  text-align: center;
}

.vp-empty-ico {
  width: 54px;
  height: 54px;
  border-radius: var(--vp-radius-lg);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--vp-accent-soft);
  color: var(--vp-accent);
  font-size: 25px;
  margin-bottom: 8px;
}

.vp-empty-title {
  font-size: 16px;
  font-weight: 700;
  color: var(--vp-ink);
}

.vp-empty-sub {
  font-size: 13.5px;
  color: var(--vp-ink-faint);
  max-width: 44ch;
  line-height: 1.55;
}

.vp-empty-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
  margin-top: 14px;
}

.vp-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 15px;
  border-radius: var(--vp-radius);
  border: 1px solid var(--vp-line);
  background: var(--vp-surface);
  color: var(--vp-ink);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
}

.vp-btn:hover {
  border-color: var(--vp-accent);
  color: var(--vp-accent);
}

.vp-btn.primary {
  background: var(--vp-accent);
  border-color: var(--vp-accent);
  color: #fff;
}

.vp-btn.primary:hover {
  filter: brightness(1.06);
  color: #fff;
}

.vp-btn.sm {
  padding: 5px 10px;
  font-size: 12px;
}

/* ==========================================================================
   6. AI ASSISTANT PANEL  —  "Ask Vee" used to be a link to faq.html
   ==========================================================================
   Deliberately plain. The first version led with a purple gradient header, a
   decorative orbit SVG and a row of eight coloured app chips; that read as a
   product banner rather than a place to work, and the chips spent a third of
   the panel saying in colour what one line of text says. What the assistant can
   reach is now stated in the header, and every surface here is the same one the
   rest of the app uses.
   ========================================================================== */

.vp-ai {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(420px, 100vw);
  z-index: var(--vp-z-panel);
  display: flex;
  flex-direction: column;
  background: var(--vp-surface);
  border-left: 1px solid var(--vp-line);
  box-shadow: var(--vp-shadow-lg);
  transform: translateX(100%);
  transition: transform .28s cubic-bezier(.22, 1, .36, 1);
}

.vp-ai.is-open {
  transform: none;
}

/* ---- header ------------------------------------------------------------- */
.vp-ai-head {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 16px 18px;
  background: var(--vp-surface);
  border-bottom: 1px solid var(--vp-line);
  color: var(--vp-ink);
  flex-shrink: 0;
}

.vp-ai-mark {
  width: 34px;
  height: 34px;
  flex: 0 0 34px;
  border-radius: 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--vp-accent-soft);
  color: var(--primary);
  font-size: 17px;
}

.vp-ai-id {
  min-width: 0;
}

.vp-ai-name {
  font-size: 15px;
  font-weight: 700;
  color: var(--vp-ink);
}

.vp-ai-ctx {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  color: var(--vp-ink-faint);
  margin-top: 1px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.vp-ai-close {
  margin-left: auto;
  width: 30px;
  height: 30px;
  flex: 0 0 30px;
  border: 1px solid var(--vp-line);
  border-radius: 8px;
  background: var(--vp-surface);
  color: var(--vp-ink-soft);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.vp-ai-close:hover {
  background: var(--vp-surface-2);
  color: var(--vp-ink);
}

/* ---- body -------------------------------------------------------------- */
.vp-ai-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 18px;
}

.vp-ai-note {
  display: flex;
  gap: 9px;
  border: 1px solid var(--vp-line);
  border-radius: var(--vp-radius);
  padding: 11px 12px;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--vp-ink-soft);
  background: var(--vp-surface-2);
  margin-bottom: 18px;
}

.vp-ai-note i {
  color: var(--vp-ink-faint);
  font-size: 15px;
  flex-shrink: 0;
  margin-top: 1px;
}

.vp-ai-note strong {
  color: var(--vp-ink);
}

.vp-ai-msg {
  display: flex;
  gap: 11px;
  margin-bottom: 16px;
}

.vp-ai-msg .who {
  width: 28px;
  height: 28px;
  flex: 0 0 28px;
  border-radius: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 11.5px;
  font-weight: 700;
  background: var(--vp-surface-2);
  color: var(--vp-ink-soft);
}

.vp-ai-msg.ai .who {
  background: var(--vp-accent-soft);
  color: var(--primary);
  font-size: 14px;
}

.vp-ai-msg .said {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13.5px;
  line-height: 1.65;
  color: var(--vp-ink-soft);
}

.vp-ai-msg .said strong {
  color: var(--vp-ink);
}

.vp-ai-msg .said a {
  color: var(--primary);
  font-weight: 600;
}

.vp-ai-msg.ai .said {
  background: var(--vp-surface-2);
  border: 1px solid var(--vp-line);
  border-radius: 10px;
  border-top-left-radius: 4px;
  padding: 11px 13px;
}

/* ---- suggestions -------------------------------------------------------
   Plain rows. The earlier version gave every row its own gradient tile and an
   uppercase app tag, which made a list of examples read as six unrelated
   coloured buttons. */
.vp-ai-sug-label {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--vp-ink-faint);
  margin: 0 0 9px;
}

.vp-ai-sugs {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.vp-ai-sug {
  display: flex;
  align-items: center;
  gap: 10px;
  text-align: left;
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--vp-line);
  border-radius: 10px;
  background: var(--vp-surface);
  cursor: pointer;
  transition: border-color .14s ease, background .14s ease;
}

.vp-ai-sug:hover {
  border-color: var(--primary);
  background: var(--vp-surface-2);
}

.vp-ai-sug-ico {
  width: 28px;
  height: 28px;
  flex: 0 0 28px;
  border-radius: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--vp-surface-2);
  color: var(--vp-ink-soft);
  font-size: 14px;
}

.vp-ai-sug:hover .vp-ai-sug-ico {
  background: var(--vp-accent-soft);
  color: var(--primary);
}

.vp-ai-sug-tx {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.vp-ai-sug-tx .q {
  font-size: 13px;
  font-weight: 600;
  color: var(--vp-ink);
  line-height: 1.35;
}

.vp-ai-sug-tx .a {
  font-size: 11px;
  color: var(--vp-ink-faint);
  margin-top: 1px;
}

.vp-ai-sug-go {
  color: var(--vp-ink-faint);
  font-size: 14px;
  flex-shrink: 0;
}

/* ---- composer ---------------------------------------------------------- */
.vp-ai-foot {
  padding: 13px 18px;
  border-top: 1px solid var(--vp-line);
  background: var(--vp-surface);
  flex-shrink: 0;
}

.vp-ai-inputwrap {
  display: flex;
  align-items: center;
  gap: 9px;
  border: 1px solid var(--vp-line);
  border-radius: 10px;
  padding: 7px 8px 7px 12px;
  background: var(--vp-surface);
  transition: border-color .14s ease, box-shadow .14s ease;
}

.vp-ai-inputwrap:focus-within {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--vp-accent-soft);
}

/* style.css styles bare inputs on focus, which drew a second rounded border
   inside the composer's own focus ring. */
.vp-ai-input,
.vp-ai-input:focus,
.vp-ai-input:focus-visible {
  flex: 1 1 auto;
  min-width: 0;
  border: 0 !important;
  outline: 0 !important;
  box-shadow: none !important;
  background: transparent !important;
  font-size: 13.5px;
  color: var(--vp-ink);
  padding: 0;
}

.vp-ai-send {
  border: 0;
  border-radius: 8px;
  width: 30px;
  height: 30px;
  flex: 0 0 30px;
  background: var(--primary);
  color: #fff;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.vp-ai-send:hover {
  filter: brightness(1.08);
}

.vp-ai-hint {
  font-size: 11px;
  color: var(--vp-ink-faint);
  margin: 7px 0 0;
}

@media (prefers-reduced-motion: reduce) {
  .vp-ai,
  .vp-ai-sug {
    transition: none;
  }
}

/* ==========================================================================
   7. RELATED RECORDS PANEL  —  the 360 view the audit said was missing
   ==========================================================================
   Rendered on a detail page from VRYNO.relatedFor(entity), so an Employee
   gets Attendance/Leave/Payslips/Reviews instead of Emails/Calls.
   ========================================================================== */

.vp-related {
  border: 1px solid var(--vp-line);
  border-radius: var(--vp-radius-lg);
  background: var(--vp-surface);
  overflow: hidden;
}

.vp-related-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 13px 16px;
  border-bottom: 1px solid var(--vp-line);
}

.vp-related-head h6 {
  margin: 0;
  font-size: 14px;
  font-weight: 700;
  color: var(--vp-ink);
}

.vp-related-head .sub {
  font-size: 11.5px;
  color: var(--vp-ink-faint);
}

/* The chain: upstream ancestor -> this record -> downstream children. */
.vp-chain {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--vp-line);
  background: var(--vp-surface-2);
  overflow-x: auto;
}

.vp-chain-node {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 10px;
  border-radius: var(--vp-radius-pill);
  border: 1px solid var(--vp-line);
  background: var(--vp-surface);
  color: var(--vp-ink-soft);
  font-size: 12px;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}

.vp-chain-node:hover {
  border-color: var(--vp-accent);
  color: var(--vp-accent);
}

.vp-chain-node.is-here {
  background: var(--vp-accent);
  border-color: var(--vp-accent);
  color: #fff;
}

.vp-chain-sep {
  color: var(--vp-ink-faint);
  font-size: 13px;
}

.vp-related-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 1px;
  background: var(--vp-line);
}

.vp-related-cell {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 13px 14px;
  background: var(--vp-surface);
  text-decoration: none;
}

.vp-related-cell:hover {
  background: var(--vp-accent-soft);
}

.vp-related-cell .ic {
  width: 34px;
  height: 34px;
  flex: 0 0 34px;
  border-radius: 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, var(--primary), #8169ff);
  color: #fff;
  font-size: 15px;
  box-shadow: 0 3px 8px -4px rgba(82, 53, 239, .55);
}

.vp-related-cell .tx {
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.vp-related-cell .nm {
  font-size: 13px;
  font-weight: 600;
  color: var(--vp-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.vp-related-cell .ct {
  font-size: 11px;
  color: var(--vp-ink-faint);
}

.vp-related-cell .app-tag {
  margin-left: auto;
  font-size: 10px;
  font-weight: 700;
  color: var(--vp-ink-faint);
  text-transform: uppercase;
  letter-spacing: .06em;
}

/* ==========================================================================
   8. BREADCRUMB TRAIL  —  was one label with no ancestors
   ========================================================================== */

.vp-crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  min-width: 0;
}

.vp-crumbs a,
.vp-crumbs span.leaf {
  color: #323232;
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 26ch;
}

.vp-crumbs a:hover {
  color: var(--vp-accent);
}

.vp-crumbs span.leaf {
  color: var(--vp-ink);
  font-weight: 600;
}

.vp-crumbs i.sep {
  color: var(--vp-line);
  font-size: 13px;
}

.vp-crumb-app {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border-radius: var(--vp-radius-pill);
  background: var(--vp-accent-soft);
  color: var(--vp-accent);
  font-size: 11px;
  font-weight: 700;
  text-decoration: none;
}

/* ==========================================================================
   9. "NEEDS YOU" CARD  —  promoted from a per-app card to a platform block
   ========================================================================== */

/* The wrapper is the theme's .card now; this is just the row stack, pulled out
   to the card's edges so each row's border-bottom meets the card border. */
.vp-needs-list {
  display: flex;
  flex-direction: column;
  margin: 0 -20px -20px;
}

.vp-needs-list .vp-need:last-child {
  border-bottom: 0;
}

/* Matches the theme's own list row: gap-3, py-3, border-bottom, 44px icon. */
.vp-need {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--vp-line);
  text-decoration: none;
  border-left: 3px solid transparent;
}

.vp-need:last-child {
  border-bottom: 0;
}

.vp-need:hover {
  background: var(--vp-surface-2);
}

.vp-need.crit {
  border-left-color: var(--danger, #ef4444);
}

.vp-need.high {
  border-left-color: var(--warning, #f59e0b);
}

.vp-need.med {
  border-left-color: var(--info, #3b82f6);
}

.vp-need.low {
  border-left-color: var(--vp-line);
}

/* Gradient icon chips, the same treatment leads-dashboard.html uses for its
   donut legend swatches. The severity is also stated in words on every row, so
   the colour reinforces rather than carries the meaning. */
.vp-need-ico {
  width: 44px;
  height: 44px;
  flex: 0 0 44px;
  border-radius: 13px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  color: #fff;
  background: linear-gradient(135deg, #c9c5e8, #a9a4d6);
  /* box-shadow: 0 3px 8px -3px rgba(20, 18, 48, .35); */
}

.vp-need.crit .vp-need-ico {
  background: linear-gradient(135deg, #ff9a9a, #ff7d7d);
}

.vp-need.high .vp-need-ico {
  background: linear-gradient(135deg, #ffcf7a, #ffb545);
}

.vp-need.med .vp-need-ico {
  background: linear-gradient(135deg, #5aa0ff, #2f6fed);
}

.vp-need.low .vp-need-ico {
  background: linear-gradient(135deg, #8ff0e6, #43c9b9);
}

/* A soft wash on the row so the severity reads before the text does. */
.vp-need.crit {
  background: color-mix(in srgb, #ffe7e7 34%, transparent);
}

.vp-need.high {
  background: color-mix(in srgb, #fff1e6 40%, transparent);
}

.vp-need:hover {
  background: var(--vp-surface-2) !important;
}

.vp-need-main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
  /* title above meta */
}

.vp-need-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--vp-ink);
}

.vp-need-meta {
  font-size: 12px;
  color: var(--vp-ink-faint);
  margin-top: 2px;
}

.vp-need-app {
  flex: 0 0 auto;
  padding: 2px 8px;
  border-radius: var(--vp-radius-pill);
  background: var(--vp-surface-2);
  border: 1px solid var(--vp-line);
  color: var(--vp-ink-soft);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
}

.vp-need-go {
  color: var(--vp-ink-faint);
  font-size: 16px;
  flex: 0 0 auto;
}

/* ==========================================================================
   10. PLATFORM PAGE FURNITURE  (Home, My Work, Search, Account 360)
   ========================================================================== */

/* ==========================================================================
   TINTED SURFACES
   ==========================================================================
   The palette is the one already used by crm/leads-module/leads-dashboard.html
   and hrms-dashboard.html, so a tinted card here matches a tinted card there.
   .vrd-kpi itself is already defined in style.css -- these only add the tints.
   ========================================================================== */

/* Each tint declares --vp-tint-ink. Everything inside the card that needs a
   saturated version of the card colour -- the label, the delta badge, the
   progress bar -- reads that one variable, so a bar can never be a colour that
   has nothing to do with the card it sits in. (It could before: a rust bar on a
   blue "Cash collected" card.) */
.vp-t-green {
  --vp-tint-ink: #1c9b4a;
  background: #d3efdd !important;
}
.vp-t-blue {
  --vp-tint-ink: #1f6fe0;
  background: #cae1fb !important;
}
.vp-t-lime {
  --vp-tint-ink: #6d840a;
  background: #e8f2b9 !important;
}
.vp-t-red {
  --vp-tint-ink: #e0451f;
  background: #ffe7e7 !important;
}
.vp-t-amber {
  --vp-tint-ink: #c2540a;
  background: #fff1e6 !important;
}
.vp-t-indigo {
  --vp-tint-ink: #4b3ac2;
  background: #dfdeff !important;
}
.vp-t-violet {
  --vp-tint-ink: #7a3fd4;
  background: #ede3fd !important;
}
.vp-t-teal {
  --vp-tint-ink: #0e8478;
  background: #e3f5f3 !important;
}
.vp-t-pink {
  --vp-tint-ink: #c72c72;
  background: #ffe6f4 !important;
}
.vp-t-sky {
  --vp-tint-ink: #0b7fa8;
  background: #e3f8ff !important;
}

.vp-t-green .vrd-kpi-label,
.vp-t-green .vp-tk,
.vp-t-blue .vrd-kpi-label,
.vp-t-blue .vp-tk,
.vp-t-lime .vrd-kpi-label,
.vp-t-lime .vp-tk,
.vp-t-red .vrd-kpi-label,
.vp-t-red .vp-tk,
.vp-t-amber .vrd-kpi-label,
.vp-t-amber .vp-tk,
.vp-t-indigo .vrd-kpi-label,
.vp-t-indigo .vp-tk,
.vp-t-violet .vrd-kpi-label,
.vp-t-violet .vp-tk,
.vp-t-teal .vrd-kpi-label,
.vp-t-teal .vp-tk,
.vp-t-pink .vrd-kpi-label,
.vp-t-pink .vp-tk,
.vp-t-sky .vrd-kpi-label,
.vp-t-sky .vp-tk {
  color: var(--vp-tint-ink);
}


.vrd-kpi.vp-kpi .vrd-kpi-foot {
  font-size: 12px;
  color: #4a4763;
  opacity: .85;
  margin-top: 4px;
  display: block;
}

.vrd-kpi.vp-kpi .vrd-kpi-label {
  font-size: 13px;
  font-weight: 600;
}

.vp-kpi {
  text-decoration: none;
  display: block;
}

.vp-kpi:hover {
  filter: brightness(1.02);
  box-shadow: 0 10px 24px -12px rgba(20, 18, 48, .45);
}

/* Two-up cards sit beside a list card rather than standing alone, so the value
   steps down again -- at 30px a half-width pair shouted over the queue that is
   the actual point of the first screen. */
/* .vrd-kpi.vp-kpi-2up .vrd-kpi-value {
  font-size: 26px;
}

.vrd-kpi.vp-kpi-2up {
  padding: 14px 16px;
} */

/* The track is the card colour knocked back with white; the fill is the card's
   own ink. No semantic override -- the state is always written in the foot text
   as well, so it never depended on the bar colour to be readable. */
.vp-kpi .progress {
  height: 6px !important;
  border-radius: 99px;
  background: rgba(255, 255, 255, .62) !important;
  overflow: hidden;
}

.vp-kpi .progress-bar {
  background: var(--vp-tint-ink, var(--primary)) !important;
  border-radius: 99px;
}

/* A bar that is nearly full on a card where full is bad still needs to read as
   a warning, so those cards carry .is-risk and get a hatch over the same ink
   rather than a different hue. */
.vp-kpi.is-risk .progress-bar {
  background-image: repeating-linear-gradient(135deg, rgba(255, 255, 255, .38) 0 4px, transparent 4px 8px);
}

/* Force a two-up grid where a KPI block sits in a half-width column: .vrd-kpi's
   own flex:1 would otherwise put all four on one line on a wide screen. */
.vp-kpi-2up {
  flex: 1 1 calc(50% - 8px) !important;
  min-width: 150px !important;
}

/* KPI pair column: the two rows share the column's height, so the block of
   colour fills the space beside the queue card instead of stopping short and
   leaving a band of empty page. */
.vp-kpi-pair {
  align-content: stretch;
}

/* .vp-kpi-pair > .vrd-kpi {
  display: flex;
  flex-direction: column;
  justify-content: center;
} */

/* A gradient wash on a section card's header strip, used sparingly. */
.vp-card-tint {
  border-top: 3px solid transparent;
}

/* .vp-card-tint.t-indigo { border-top-color: #8169ff; } */
.vp-card-tint.t-green {
  border-top-color: #57d9a3;
}

.vp-card-tint.t-amber {
  border-top-color: #ffb545;
}

.vp-card-tint.t-blue {
  border-top-color: #2f6fed;
}

/* Section eyebrow above a .row, sitting in the theme's own mb-2 / mb-4 rhythm.
   Everything else on these pages is .row / .col-* / .card / .card-body, i.e.
   the theme's structure, so gaps match the rest of the product instead of a
   parallel spacing system. */
.vp-band-top {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 14px;
}

.vp-band-top h2 {
  margin: 0;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--vp-ink-faint);
}

.vp-band-top .fresh {
  margin-left: auto;
  font-size: 11px;
  color: var(--primary);
}

/* ==========================================================================
   TABLES
   ==========================================================================
   The platform pages now use the project's own table shape --
   div.table-responsive > table.table.mb-0.align-middle.border-0 -- the one
   crm/leads-module/leads-list.html uses, so a table here behaves like a table
   there and scrolls sideways in its own box instead of pushing text out of the
   card. The old hand-rolled .vp-table is gone; these rules only style the head
   and tighten the last row, everything else comes from the theme.
   ========================================================================== */

tr.vp-thead > th {
  font-size: 10.5px !important;
  font-weight: 700 !important;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--vp-ink-faint) !important;
  background: var(--vp-surface-2) !important;
  border-bottom: 1px solid var(--vp-line) !important;
  padding: 9px 14px !important;
  white-space: nowrap;
}

.card-body > .table-responsive > .table > tbody > tr:last-child > td {
  border-bottom: 0 !important;
}

.card-body > .table-responsive > .table td {
  font-size: 13.5px;
}

.card-body > .table-responsive > .table td.text-end,
.card-body > .table-responsive > .table th.text-end {
  font-variant-numeric: tabular-nums;
}

/* The "Needs you" card sits in a half-width column beside a block of KPI
   cards. Ten queue items would make that column roughly twice the height of
   the one next to it, so the list scrolls inside the card instead. */
.vp-needs-scroll {
  max-height: 260px;
  overflow-y: auto;
}

.vp-needs-scroll::-webkit-scrollbar {
  width: 6px;
}

.vp-needs-scroll::-webkit-scrollbar-thumb {
  background: var(--vp-line);
  border-radius: 99px;
}

/* ---- narrow screens ----------------------------------------------------
   At 390px the queue row put a 44px icon, the title, two nowrap chips and a
   chevron on one line, which left the title about 90px and broke it as
   "Follow up with Acme Corporatio / n". Below the sm breakpoint the chips drop
   to a second line, indented to sit under the title, and the chevron goes --
   the whole row is the link, so it was never the affordance. */
@media (max-width: 575.98px) {
  .vp-need {
    flex-wrap: wrap;
    row-gap: 8px;
    padding: 12px 16px;
  }

  .vp-need-main {
    flex: 1 1 calc(100% - 60px);
  }

  .vp-need-go {
    display: none;
  }

  .vp-need-app {
    margin-left: 60px;
  }

  .vp-need-app + .vp-need-app {
    margin-left: 0;
  }

  /* Same shape for an insight: the action goes full width under the reason
     rather than squeezing the sentence into a column. */
  .vp-insight {
    flex-wrap: wrap;
    row-gap: 10px;
  }

  .vp-insight-main {
    flex: 1 1 calc(100% - 46px);
  }

  .vp-insight > .btn {
    flex: 1 1 100%;
  }
}

.vp-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border-radius: var(--vp-radius-pill);
  font-size: 10.5px;
  font-weight: 700;
  white-space: nowrap;
  background: var(--vp-surface-2);
  color: var(--vp-ink-soft);
  border: 1px solid var(--vp-line);
}

.vp-pill.ok {
  background: var(--success-transparent, #dcfce7);
  color: var(--success, #16a34a);
  border-color: transparent;
}

.vp-pill.warn {
  background: var(--warning-transparent, #fef3c7);
  color: var(--warning, #b45309);
  border-color: transparent;
}

.vp-pill.bad {
  background: var(--danger-transparent, #fee2e2);
  color: var(--danger, #dc2626);
  border-color: transparent;
}

.vp-pill.info {
  background: var(--info-transparent, #dbeafe);
  color: var(--info, #2563eb);
  border-color: transparent;
}

/* Role switcher on Home — one widget library, different default arrangement. */
.vp-roles {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.vp-role {
  padding: 6px 12px;
  border-radius: var(--vp-radius-pill);
  border: 1px solid var(--vp-line);
  background: var(--vp-surface);
  color: var(--vp-ink-soft);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
}

.vp-role:hover {
  border-color: var(--vp-accent);
  color: var(--vp-accent);
}

.vp-role.is-on {
  background: var(--vp-accent);
  border-color: var(--vp-accent);
  color: #fff;
}

/* AI insight strip — capped at three, each with a reason and an action. */
.vp-insight {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 13px 15px;
  border: 1px solid var(--vp-line);
  border-radius: var(--vp-radius);
  background: var(--vp-surface);
}

.vp-insight+.vp-insight {
  margin-top: 9px;
}

.vp-insight-ico {
  width: 34px;
  height: 34px;
  flex: 0 0 34px;
  border-radius: 11px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, #d1aeff, #b07dff);
  color: #fff;
  font-size: 16px;
  box-shadow: 0 3px 8px -4px rgba(122, 63, 212, .5);
}

.vp-insight {
  border-left: 3px solid transparent;
}

.vp-insight:nth-child(1) {
  border-left-color: #ff7d7d;
}

.vp-insight:nth-child(2) {
  border-left-color: #ffb545;
}

.vp-insight:nth-child(3) {
  border-left-color: #57d9a3;
}

.vp-insight:nth-child(1) .vp-insight-ico {
  background: linear-gradient(135deg, #ff9a9a, #ff7d7d);
}

.vp-insight:nth-child(2) .vp-insight-ico {
  background: linear-gradient(135deg, #ffcf7a, #ffb545);
}

.vp-insight:nth-child(3) .vp-insight-ico {
  background: linear-gradient(135deg, #8ff2d1, #57d9a3);
}

.vp-insight-main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.vp-insight-title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--vp-ink);
}

.vp-insight-why {
  font-size: 13px;
  color: var(--vp-ink-faint);
  margin-top: 2px;
  line-height: 1.5;
}

@media (max-width: 640px) {
  .vp-toasts {
    right: 10px;
    left: 10px;
    width: auto;
  }

  .vp-cmd {
    max-height: 82vh;
  }

  .vp-overlay {
    padding: 6vh 10px 10px;
  }
}
/* ==========================================================================
   QUICK-ADD MENU  (the + in the header)
   ==========================================================================
   One line per row. The icon sits in a fixed 26px slot so every label starts
   on the same x -- including the last row, which previously used an inline
   `me-1` icon and so began a few pixels off from the six above it.
   ========================================================================== */

.vp-qa-row {
  display: flex !important;
  align-items: center;
  gap: 10px;
  padding: 7px 10px !important;
  border-radius: 8px;
}

.vp-qa-ico {
  width: 26px;
  height: 26px;
  flex: 0 0 26px;
  border-radius: 7px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--vp-accent-soft);
  color: var(--primary);
}

/* The last row is a different kind of action, so its slot is neutral rather
   than accented -- same geometry, so the text still lines up. */
.vp-qa-ico.is-plain {
  background: transparent;
  color: var(--vp-ink-faint);
}

.vp-qa-label {
  font-size: 13px;
  font-weight: 500;
  color: var(--vp-ink);
  line-height: 1.35;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.vp-qa-any .vp-qa-label {
  color: var(--vp-ink-soft);
}

.vp-qa-row:hover .vp-qa-label { color: var(--primary); }

/* ==========================================================================
   13. DENSITY  —  html.vp-dense
   ==========================================================================
   Compact takes vertical padding out of the repeating things (rows, cells,
   card bodies) and leaves type size alone, so more fits on screen without
   anything becoming harder to read. Set by the header control or the d key.
   ========================================================================== */

.vp-dense .card-body { padding: 13px 14px !important; }
.vp-dense .card { margin-bottom: 0; }
.vp-dense .content { padding: 14px 14px 0 !important; }

.vp-dense .table > :not(caption) > * > * { padding-top: 5px !important; padding-bottom: 5px !important; }
.vp-dense tr.vp-thead > th { padding: 6px 14px !important; }

.vp-dense .vp-need { padding: 8px 14px; }
.vp-dense .vp-need-ico { width: 32px; height: 32px; flex: 0 0 32px; font-size: 16px; border-radius: 9px; }
.vp-dense .vp-needs-list { margin-left: 0; margin-right: 0; }

.vp-dense .vrd-kpi { padding: 12px 14px !important; }
.vp-dense .vrd-kpi.vp-kpi .vrd-kpi-value { font-size: 26px !important; }

.vp-dense .row { margin-bottom: 0; }
.vp-dense .mb-3, .vp-dense .mb-4 { margin-bottom: 10px !important; }

/* ==========================================================================
   14. KEYBOARD ROW HIGHLIGHT
   ==========================================================================
   Arrow keys move this. It is a bar plus a wash rather than an outline, so it
   survives a row that already has a left border for severity, and it never
   depends on colour alone -- the row also scrolls itself into view.
   ========================================================================== */

.vp-kbd-row {
  position: relative;
  background: var(--vp-accent-soft) !important;
  box-shadow: inset 3px 0 0 0 var(--primary);
}

.vp-kbd-row > td:first-child,
.vp-kbd-row > th:first-child { border-top-left-radius: 6px; border-bottom-left-radius: 6px; }

.vp-kbd-picked { outline: 1px dashed var(--primary); outline-offset: -2px; }

/* ==========================================================================
   15. SAVE STATE PILL
   ==========================================================================
   Fixed, top-right, under the header. Only present once a form has been
   touched, so a read-only page never shows it.
   ========================================================================== */

.vp-savestate {
  position: fixed;
    top: 68px;
    right: 46%;
  z-index: 1035;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px;
  border-radius: var(--vp-radius-pill);
  border: 1px solid var(--vp-line);
  background: var(--vp-surface);
  box-shadow: var(--vp-shadow);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--vp-ink-soft);
  opacity: 0;
  transform: translateY(-6px);
  pointer-events: none;
  transition: opacity .18s ease, transform .18s ease;
}

.vp-savestate.is-on { opacity: 1; transform: none; }

.vp-savestate i { font-size: 15px; color: var(--success, #1ABE17); }

/* Unsaved is a state to act on, so it is warm and says so in words. */
.vp-savestate.is-dirty {
  border-color: color-mix(in srgb, var(--warning, #f9b801) 55%, var(--vp-line));
  background: var(--vp-surface);
  color: var(--vp-ink);
}

.vp-savestate.is-dirty i { color: var(--warning, #c2540a); }

@media (max-width: 575.98px) {
  .vp-savestate { top: auto; bottom: calc(var(--vp-strip-h, 56px) + 12px); right: 12px; }
}

/* ==========================================================================
   16. UNDO WINDOW ON A TOAST
   ==========================================================================
   The bar drains over exactly as long as the Undo lasts, so the window is
   visible instead of guessed at.
   ========================================================================== */

.vp-toast { position: relative; overflow: hidden; }

.vp-toast-bar {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 100%;
  background: var(--primary);
  transform-origin: left center;
  animation: vp-drain linear forwards;
}

@keyframes vp-drain { from { transform: scaleX(1); } to { transform: scaleX(0); } }

@media (prefers-reduced-motion: reduce) {
  .vp-toast-bar { animation: none; opacity: .4; }
}

/* ==========================================================================
   17. PRINT
   ==========================================================================
   Business software gets printed -- quotes, invoices, payslips, reports go to
   a customer or into a file. On paper none of the app chrome is content, and
   ink is not free: everything becomes black on white, cards lose their fills,
   and a row is not allowed to break across two pages.
   ========================================================================== */

.vp-printhead { display: none; }

@media print {
  /* --- ink reset, first so everything below can still override it ---
     Page-level classes are unknowable from here: this project has .kpi-badge,
     .filter-chip, .lead-status and hundreds more, all with their own fills.
     Chasing them one by one across 909 pages does not converge, so paper
     starts from nothing -- no fills, no gradients, black text -- and the few
     things that genuinely need a tone ask for it further down. Forcing the
     text black in the same sweep is what makes this safe: a white-on-colour
     chip cannot end up white-on-white. */
  *,
  *::before,
  *::after {
    background-color: transparent !important;
    background-image: none !important;
    box-shadow: none !important;
    text-shadow: none !important;
    color: #000 !important;
    filter: none !important;
  }

  /* --- the app is not the document --- */
  .sidebar,
  .sidebar-horizontal,
  #sidebarContainer,
  .navbar-header,
  #topbarContainer,
  header,
  .vr-module-strip,
  .vr-module-strip.vp-strip,
  .vp-strip,
  .vp-ai,
  .vp-toasts,
  .vp-cmd-wrap,
  .vp-savestate,
  .vp-skip,
  .chat-left,
  .footer,
  .theme-customizer,
  #theme-settings-offcanvas,
  .offcanvas,
  .modal,
  .modal-backdrop,
  .dropdown-menu,
  .btn,
  .vp-figdata > summary,
  [data-vp-density],
  [data-bs-toggle],
  .pagination,
  .form-check-input,
  .vp-need-go,
  /* A search box is a tool, not content. Field inputs are left alone -- on a
     record they hold the values you are printing. */
  [type="search"],
  [placeholder*="Search" i],
  [placeholder*="search" i],
  /* The tick column and the row-actions column are both empty on paper. */
  th[data-col="select"],
  td[data-col="select"],
  th[data-col="actions"],
  td[data-col="actions"],
  /* Disclosure arrows have nothing to disclose once everything is expanded. */
  summary::-webkit-details-marker { display: none !important; }

  /* Anything collapsed on screen is opened, so nothing is silently missing. */
  details > *:not(summary) { display: revert !important; }

  html, body {
    background: #fff !important;
    color: #000 !important;
    font-size: 11pt;
  }

  .page-wrapper,
  #contentContainer,
  .content {
    margin: 0 !important;
    padding: 0 !important;
    width: auto !important;
  }

  /* A card is a grouping on screen; on paper it is a heading and its rows. */
  .card {
    border: 0 !important;
    box-shadow: none !important;
    background: #fff !important;
    margin: 0 0 14pt !important;
    break-inside: avoid;
  }

  .card-body { padding: 0 !important; }

  h4, h5, .page-name {
    color: #000 !important;
    margin: 0 0 6pt !important;
  }

  /* Tints are decoration; on paper they cost ink and gain nothing. */
  .vrd-kpi,
  .vp-kpi,
  .vp-need,
  .vp-insight,
  .vp-setup {
    background: #fff !important;
    border: 1px solid #bbb !important;
    box-shadow: none !important;
    break-inside: avoid;
  }

  .vrd-kpi::before { display: none !important; }

  /* Tables print as tables: visible rules, repeated head, unbroken rows. */
  .table-responsive { overflow: visible !important; max-height: none !important; }

  .table { width: 100% !important; border-collapse: collapse !important; }

  .table th,
  .table td {
    border: 1px solid #999 !important;
    padding: 4pt 6pt !important;
    color: #000 !important;
    background: #fff !important;
    white-space: normal !important;
  }

  tr.vp-thead > th {
    background: #eee !important;
    color: #000 !important;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  thead { display: table-header-group; }
  tr, .vp-need, .vp-insight { break-inside: avoid; }

  /* Scroll boxes have no meaning on paper. */
  .vp-needs-scroll,
  .vp-figdata .table-responsive { max-height: none !important; overflow: visible !important; }

  .vp-figdata[open] > summary { display: none !important; }

  /* A link is useless in print unless you can read where it goes -- but only
     for real destinations, not javascript: hooks. */
  a { color: #000 !important; text-decoration: none !important; }

  /* What it is, whose it is, when it was printed. */
  .vp-printhead {
    display: block !important;
    border-bottom: 1.5pt solid #000;
    padding-bottom: 5pt;
    margin-bottom: 12pt;
  }

  .vp-printhead-t { display: block; font-size: 15pt; font-weight: 700; color: #000; }
  .vp-printhead-m { display: block; font-size: 9pt; color: #444; margin-top: 2pt; }

  /* A chip that was only a coloured fill needs an outline to still read as a
     chip once the fill is gone. */
  .badge,
  .vp-pill,
  .vp-need-app,
  .filter-chip,
  .kpi-badge,
  .vrd-kpi-badge {
    border: 1px solid #999 !important;
    padding: 1pt 4pt !important;
    border-radius: 3pt !important;
  }

  /* Secondary text earns a grey back, so a printed page still has a hierarchy
     rather than one flat wall of black. */
  .text-muted,
  .fs-12,
  .fs-13,
  .vp-printhead-m,
  small { color: #444 !important; }

  /* Belt and braces: the strip's own rules use !important on a two-class
     selector, so it also gets an explicit override here. */
  body.vp-has-strip .vr-module-strip.vp-strip { display: none !important; }
  body.vp-has-strip .page-wrapper,
  body.vp-has-strip #contentContainer { padding-bottom: 0 !important; }

  @page { margin: 14mm; }
}

/* ==========================================================================
   18. INFO MARKERS  --  the small (i) beside a metric label
   ==========================================================================
   Recessive by default, assertive only on hover or focus: forty of these on one
   dashboard must not compete with the numbers they explain. Sized in em so one
   rule tracks every place it lands, from an 11px KPI label to a 16px heading.
   ========================================================================== */

.vp-info-marker {
  display: inline-block;
  margin-left: 0.3em;
  font-size: 0.86em;
  line-height: 1;
  vertical-align: baseline;
  color: var(--vp-ink-faint);
  cursor: help;
  opacity: 0.75;
  transition: color .12s ease, opacity .12s ease;
}

.vp-info-marker:hover,
.vp-info-marker:focus-visible { color: var(--primary); opacity: 1; }

.vp-info-marker:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: 3px;
}

/* On a tinted KPI card the faint ink sits too close to the tint to see. */
.vrd-kpi .vp-info-marker,
.kpi .vp-info-marker { color: var(--vp-tint-ink, var(--vp-ink-soft)); opacity: 0.6; }
.vrd-kpi .vp-info-marker:hover,
.kpi .vp-info-marker:hover { opacity: 1; }

/* The bubble itself: the theme's own tooltip, only wider. A definition is a
   sentence, and the default 200px breaks it into a narrow column. */
.tooltip .tooltip-inner {
  max-width: 320px;
  text-align: left;
  font-size: 12.5px;
  line-height: 1.55;
  padding: 8px 11px;
}

/* Two classes on purpose. bootstrap.min.css sets max-width from
   --bs-tooltip-max-width on .tooltip-inner -- one class -- and loads after this
   file, so a one-class rule here loses the tie and the bubble stays 200px wide,
   which turns a one-sentence definition into a tall narrow column. */

/* In a table head and on a field label the marker sits beside smaller, denser
   text than a KPI label, so it scales to that text instead of being fixed. */
/* A header that gained a marker stays on one line. Table headers are the
   tightest place a marker lands -- a column carrying sort arrows as well had
   just enough room for the text, so the icon wrapped onto a second line and
   pushed the header row taller than its neighbours. nowrap is scoped to the
   headers that actually have a marker, so no other column is affected. */
/* The last word of a label and its marker, kept together. The browser may
   break the line before this span; it can never break inside it, so the icon
   is never stranded on a line of its own. */
.vp-info-glue {
  white-space: nowrap;
}

th.vp-has-info,
p.text-uppercase.vp-has-info,
li.justify-content-between > span.vp-has-info:first-child,
.accordion-button > span.vp-has-info:first-child {
  white-space: nowrap;
}

/* A field label can legitimately be long enough to need two lines; the
   non-breaking space before the icon already stops it orphaning, so the label
   keeps wrapping where it should. */
label.form-label.vp-has-info {
  white-space: normal;
}

th .vp-info-marker,
label.form-label .vp-info-marker {
  font-size: 0.92em;
  opacity: 0.65;
}

th .vp-info-marker:hover,
label.form-label .vp-info-marker:hover {
  opacity: 1;
}

@media print { .vp-info-marker { display: none !important; } }

/* ==========================================================================
   LONG OPTION LISTS  —  .vp-scrolls
   ==========================================================================
   Applied by capLongLists() in vryno-platform.js, and only to a container that
   actually holds a long list. max-height only bites past the cap, so a tagged
   short menu renders exactly as it did; the same rule rescues the one a
   customer has grown to a hundred entries.
   ========================================================================== */

.vp-scrolls {
  max-height: min(60vh, 420px);
  overflow-y: auto;
  overflow-x: hidden;
  /* Reaching the end of the list must not start scrolling the page behind it. */
  overscroll-behavior: contain;
  scrollbar-width: thin;
}

.vp-scrolls::-webkit-scrollbar {
  width: 8px;
}

.vp-scrolls::-webkit-scrollbar-thumb {
  background: var(--vp-line);
  border-radius: 999px;
}

.vp-scrolls::-webkit-scrollbar-track {
  background: transparent;
}

/* A dropdown that scrolls needs its own padding, or the first and last rows
   sit flush against the cut. */
.dropdown-menu.vp-scrolls {
  padding-top: 4px;
  padding-bottom: 4px;
}
