html {
  font-size: 14px;
}

@media (min-width: 768px) {
  html {
    font-size: 16px;
  }
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

html {
  position: relative;
  min-height: 100%;
  /* A wide inner element (the Timeline Gantt's fixed-width rows, mainly) can force the whole
     page to scroll sideways even though that element has its own overflow-x:auto, if any
     ancestor is a flexbox that lets it grow instead of containing it. Blocking it here at the
     page root guarantees only the inner box ever scrolls horizontally, never the page itself. */
  overflow-x: hidden;
}

body {
  margin-bottom: 60px;
  overflow-x: hidden;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
  color: var(--bs-secondary-color);
  text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
  text-align: start;
}

/* ===== GAB PMS theme - palette matched to the SmartHR admin dashboard reference ===== */
:root {
  --tvp-font: "Plus Jakarta Sans", system-ui, -apple-system, "Segoe UI", sans-serif;

  --tvp-bg: #f6f7fb;
  --tvp-surface: #ffffff;
  --tvp-surface-2: #fbfbfd;
  --tvp-border: #eaecf1;
  --tvp-text: #1a2130;
  --tvp-text-muted: #6b7280;
  /* Layered, soft shadows (thin ambient + a longer diffuse throw) read as more
     premium than a single flat drop-shadow, and give a glass card enough lift to
     separate from the colorful wallpaper behind it instead of blending flat into it. */
  --tvp-shadow: 0 2px 6px rgba(16, 24, 40, .06), 0 10px 26px -10px rgba(16, 24, 40, .18);
  --tvp-shadow-hover: 0 4px 10px rgba(16, 24, 40, .08), 0 16px 36px -8px rgba(16, 24, 40, .22);
  --tvp-shadow-lg: 0 8px 16px rgba(16, 24, 40, .05), 0 24px 48px -12px rgba(16, 24, 40, .18);
  --tvp-radius: 12px;
  --tvp-radius-sm: 8px;

  --tvp-primary: #f26522;
  --tvp-primary-soft: #fdeee5;
  --tvp-info: #1b84ff;
  --tvp-info-soft: #e7f1ff;
  --tvp-success: #03c95a;
  --tvp-success-soft: #e1f9ea;
  /* Richer amber than Bootstrap's default #ffc107 - the original read washed-out
     and had weak contrast against a white surface. */
  --tvp-warning: #d68a00;
  --tvp-warning-soft: #fbf0da;
  --tvp-danger: #e63757;
  --tvp-danger-soft: #fde9ed;
  --tvp-teal: #0c4b5e;
  --tvp-teal-soft: #e3edf0;
  --tvp-purple: #ab47bc;
  --tvp-purple-soft: #f5e9f7;
  --tvp-pink: #fd3995;
  --tvp-pink-soft: #ffe6f2;

  /* iOS Control Center glass: one calm wallpaper-style gradient, blurred behind
     near-white panels - color comes from the wallpaper, not from tinting the
     cards. -weak is for small chrome (inputs, badges) sitting ON TOP of an
     already-glass card - needs to stay a bit more transparent than the card itself. */
  --tvp-glass-bg: rgba(255, 255, 255, .62);
  --tvp-glass-bg-weak: rgba(255, 255, 255, .5);
  --tvp-glass-bg-strong: rgba(255, 255, 255, .8);
  --tvp-glass-border: rgba(255, 255, 255, .55);
  --tvp-glass-blur: blur(24px) saturate(140%);
  --tvp-glass-blur-sm: blur(14px) saturate(130%);
  /* The specular highlight every "liquid glass" surface has - a soft light streak
     along the top edge, like light catching the curved top of a glass pill/tube.
     Layered as an extra background-image on top of the flat glass tint. */
  --tvp-glass-gloss: linear-gradient(180deg, rgba(255, 255, 255, .55) 0%, rgba(255, 255, 255, 0) 50%);
}

/* Fixed so the wallpaper stays put while content scrolls past/over it - the whole
   point of the effect is a stable backdrop for translucent cards to blur into. */
body {
  color: var(--tvp-text); font-family: var(--tvp-font);
  background: linear-gradient(135deg, #ffb27a 0%, #f0888f 32%, #8f8fd6 68%, #6fb0e0 100%);
  background-attachment: fixed;
  /* A light, universal "engraved glass" quality on every letterform - a bright
     edge below catching light, a faint dark line above where it's recessed -
     layered on top of each element's own ink color rather than replacing it, so
     base contrast (and therefore legibility) never changes, only its dimensionality. */
  text-shadow: 0 1px 0 rgba(255, 255, 255, .6), 0 -1px 0 rgba(0, 0, 0, .05);
}
h1, h2, h3, h4, h5, h6, .card-title, .fw-semibold, .fw-bold { letter-spacing: -.01em; }
/* The hero figures - the big glanceable numbers - get the effect turned up, closer
   to the reference: a taller highlight and a deeper, softer shadow underneath for
   real lift off the page, the way a glossy app-icon numeral sits proud of its tile. */
.tvp-stat-value, .tvp-page-title, .tvp-cost-hero-value, .tvp-donut-value {
  text-shadow: 0 1.5px 0 rgba(255, 255, 255, .85), 0 -1px 1px rgba(0, 0, 0, .1), 0 4px 10px rgba(0, 0, 0, .12);
}

/* Glass surfaces - applied broadly across the chrome (border color is set directly on
   a couple of rules further down, since they use !important shorthand `border` that
   would otherwise win the cascade over a rule declared up here); falls back to a
   solid surface on browsers without backdrop-filter support so content stays readable.
   Every surface gets the same top-edge gloss streak layered over its flat tint -
   the specular highlight that makes a flat translucent panel read as glass/liquid
   rather than just "semi-transparent". */
.tvp-card, .tvp-stat, .project-card, .navbar .dropdown-menu, .modal-content, .tvp-costbar {
  background: var(--tvp-glass-gloss), var(--tvp-glass-bg) !important;
  backdrop-filter: var(--tvp-glass-blur);
  -webkit-backdrop-filter: var(--tvp-glass-blur);
}
/* NOT .tvp-bubble, .tvp-plantrow or .tvp-donut-wrap: their own per-instance inline
   color IS the data encoding (category identity / plant avatar), or in the donut's
   case there's no background at all today - forcing one on would print a translucent
   square behind a round chart. */
.form-select, .form-control, .badge.text-bg-light, .tvp-table-head th {
  background: var(--tvp-glass-gloss), var(--tvp-glass-bg-weak) !important;
  backdrop-filter: var(--tvp-glass-blur-sm);
  -webkit-backdrop-filter: var(--tvp-glass-blur-sm);
  border-color: var(--tvp-glass-border);
}
/* Real bug: --tvp-glass-border is near-white (rgba(255,255,255,.55)), tuned for
   sitting on the colorful gradient wallpaper - on an ordinary white/light Bootstrap
   card (every plain Create/Edit form, not just Dashboard's own glass cards), that's
   a white border on a white field on a white card: completely invisible, so an
   input box couldn't be told apart from empty space. Every actual form field
   (as opposed to read-only chrome like table headers) needs a border with real,
   surface-independent contrast - this overrides the near-white one specifically
   for .form-select/.form-control, keeping the glass tint/blur but restoring a
   visible edge. */
.form-select, .form-control {
  border-color: rgba(26, 33, 48, .18) !important;
  box-shadow: inset 0 1px 2px rgba(16, 24, 40, .03);
}
/* Pill-shaped chrome (controls, buttons) - the capsule shape every Liquid Glass
   control shares (search field, slider track, button). Cards stay rounded-rect
   (they hold layout, not a single control), but anything you click or type into
   goes full pill. The outline buttons get the SAME gloss+glass treatment as
   everything else, but deliberately without !important: Bootstrap's own
   `.btn:hover` rule (0,0,2,0 - a class plus a pseudo-class) already outranks a
   plain `.btn-outline-primary` (0,0,1,0) on specificity alone, so the resting glass
   tint here never has to fight the hover fill - it just loses automatically the
   moment :hover applies, exactly when it should. */
.form-select, .form-control, .btn, .badge.text-bg-light {
  border-radius: 999px !important;
}
/* Tactile press feedback on every button - Bootstrap already handles the hover/
   active color swap; this adds the physical "give" (a slight scale-down) that
   makes a press feel like it landed, on top of that color change rather than
   instead of it. */
.btn { transition: transform .12s ease, background-color .15s ease, border-color .15s ease, color .15s ease; }
.btn:active { transform: scale(.96); }
.btn-outline-primary, .btn-outline-secondary, .btn-outline-light {
  background: var(--tvp-glass-gloss), var(--tvp-glass-bg-weak);
  backdrop-filter: var(--tvp-glass-blur-sm);
  -webkit-backdrop-filter: var(--tvp-glass-blur-sm);
}
/* .btn-primary is deliberately NOT given the same `background` shorthand: it
   already resolves its solid brand-orange fill through Bootstrap's own
   `background-color: var(--bs-btn-bg)` at equal specificity, and a shorthand
   `background` here (same specificity, later in this file) would silently replace
   that solid color with the translucent white gloss tint instead of adding gloss
   ON TOP of it. Adding only `background-image` leaves `background-color` (and so
   `--bs-btn-bg`, still swappable for hover/active) alone and layers the same
   highlight streak over whatever solid color is underneath. */
.btn-primary {
  background-image: var(--tvp-glass-gloss);
  backdrop-filter: var(--tvp-glass-blur-sm);
  -webkit-backdrop-filter: var(--tvp-glass-blur-sm);
}
.navbar.bg-dark .btn-outline-light { border-color: var(--tvp-primary); }
/* Icon chips keep their own tint (color IS the identity - primary vs. success vs.
   danger) but blended toward transparent + blurred, so they read as "tinted glass"
   rather than losing their color entirely; rounder than the old 12px, closer to
   the pill/blob shapes in the reference, and the glyph itself gets a soft glow
   instead of a hard flat outline. */
.tvp-stat-icon { border-radius: 16px; }
.tvp-stat-icon svg { filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .12)); }
.tvp-icon-primary, .tvp-icon-info, .tvp-icon-success, .tvp-icon-danger,
.tvp-icon-teal, .tvp-icon-purple, .tvp-icon-pink {
  background-image: var(--tvp-glass-gloss);
  backdrop-filter: var(--tvp-glass-blur-sm);
  -webkit-backdrop-filter: var(--tvp-glass-blur-sm);
}
.tvp-icon-primary { background-color: color-mix(in srgb, var(--tvp-primary-soft) 55%, transparent) !important; }
.tvp-icon-info { background-color: color-mix(in srgb, var(--tvp-info-soft) 55%, transparent) !important; }
.tvp-icon-success { background-color: color-mix(in srgb, var(--tvp-success-soft) 55%, transparent) !important; }
.tvp-icon-danger { background-color: color-mix(in srgb, var(--tvp-danger-soft) 55%, transparent) !important; }
.tvp-icon-teal { background-color: color-mix(in srgb, var(--tvp-teal-soft) 55%, transparent) !important; }
.tvp-icon-purple { background-color: color-mix(in srgb, var(--tvp-purple-soft) 55%, transparent) !important; }
.tvp-icon-pink { background-color: color-mix(in srgb, var(--tvp-pink-soft) 55%, transparent) !important; }
/* The navbar brand mark and every stat-card/nav SVG glyph get the same soft glow
   the reference's arrows/sliders have, instead of a flat, crisp icon outline. */
.tvp-brand-icon, .navbar .nav-link svg { filter: drop-shadow(0 0 3px rgba(255, 255, 255, .5)); }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .tvp-card, .tvp-stat, .project-card, .navbar .dropdown-menu, .modal-content, .tvp-costbar {
    background: var(--tvp-glass-bg-strong) !important;
  }
  .form-select, .form-control, .badge.text-bg-light, .tvp-table-head th {
    background: var(--tvp-glass-bg-strong) !important;
  }
}

/* Re-theme Bootstrap's primary color to the brand orange (buttons, links, active states)
   without touching Bootstrap's own compiled variables - narrow, explicit overrides. */
.btn-primary {
  --bs-btn-bg: var(--tvp-primary); --bs-btn-border-color: var(--tvp-primary);
  --bs-btn-hover-bg: #d9551a; --bs-btn-hover-border-color: #d9551a;
  --bs-btn-active-bg: #c24917; --bs-btn-active-border-color: #c24917;
  --bs-btn-disabled-bg: var(--tvp-primary); --bs-btn-disabled-border-color: var(--tvp-primary);
}
.btn-outline-primary {
  --bs-btn-color: var(--tvp-primary); --bs-btn-border-color: var(--tvp-primary);
  --bs-btn-hover-bg: var(--tvp-primary); --bs-btn-hover-border-color: var(--tvp-primary);
  --bs-btn-active-bg: var(--tvp-primary); --bs-btn-active-border-color: var(--tvp-primary);
}
a { color: var(--tvp-primary); }
a:hover { color: #d9551a; }
.text-primary { color: var(--tvp-primary) !important; }
.accordion-button:not(.collapsed) { background-color: var(--tvp-primary-soft); color: var(--tvp-primary); }
.accordion-button:focus { box-shadow: 0 0 0 .2rem var(--tvp-primary-soft); }
.form-control:focus, .form-select:focus { border-color: var(--tvp-primary); box-shadow: 0 0 0 .2rem var(--tvp-primary-soft); }

.navbar.bg-dark {
  background: var(--tvp-glass-bg-strong) !important;
  backdrop-filter: var(--tvp-glass-blur);
  -webkit-backdrop-filter: var(--tvp-glass-blur);
  border-bottom: 1px solid var(--tvp-glass-border);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .navbar.bg-dark { background: var(--tvp-surface) !important; }
}
.navbar.bg-dark .navbar-brand, .navbar.bg-dark .nav-link, .navbar.bg-dark .text-light { color: var(--tvp-text) !important; }
.navbar.bg-dark .text-info { color: var(--tvp-primary) !important; }
.navbar.bg-dark .btn-outline-light {
  color: var(--tvp-primary); border-color: var(--tvp-primary);
}
.navbar.bg-dark .btn-outline-light:hover { background: var(--tvp-primary); color: #fff; }
.tvp-brand-icon { color: var(--tvp-primary); filter: none; }

/* Navbar: sticky with a shadow that reads as elevation over scrolling content,
   a brand mark in its own tinted badge instead of a bare icon, and pill-style
   nav links so the active/hover state doesn't rely on color alone. */
.navbar {
  position: sticky; top: 0; z-index: 1030;
  box-shadow: 0 1px 0 rgba(16, 24, 40, .04), 0 8px 24px -12px rgba(16, 24, 40, .12);
  padding-top: .65rem; padding-bottom: .65rem;
}
.tvp-brand-badge {
  width: 36px; height: 36px; border-radius: 12px;
  display: flex; align-items: center; justify-content: center;
  background: var(--tvp-glass-gloss), linear-gradient(135deg, var(--tvp-primary), #ff8a4c);
  box-shadow: 0 4px 10px -2px rgba(242, 101, 34, .45);
}
.tvp-brand-badge .tvp-brand-icon { color: #fff; }
.tvp-brand-name { font-weight: 800; font-size: 1.08rem; line-height: 1.1; color: var(--tvp-text); letter-spacing: -.01em; }
.tvp-brand-tag { font-size: .68rem; font-weight: 600; color: var(--tvp-text-muted); text-transform: uppercase; letter-spacing: .06em; }
.navbar.bg-dark .nav-link {
  border-radius: 999px; padding: .4rem .9rem !important; font-weight: 500; font-size: .92rem;
  transition: background .15s ease, color .15s ease;
}
.navbar.bg-dark .nav-link:hover { background: var(--tvp-bg); }
.navbar.bg-dark .nav-item.active .nav-link,
.navbar.bg-dark .nav-link.tvp-nav-active { background: var(--tvp-primary-soft); color: var(--tvp-primary) !important; }

/* Shared: any text block that would otherwise sit directly on the gradient
   wallpaper - a page header, a "Cost Distribution"-style section heading, etc.
   Text-shadow tricks (a halo, an engraved edge) chased this across several rounds
   and kept breaking again at some position on the gradient, because the gradient
   itself isn't a fixed color - a shadow tuned for the orange corner reads wrong at
   the purple corner. The robust fix is the same one every other block of text on
   this page already uses successfully: put it on its own light glass panel, same
   as a card, so contrast is fixed and known regardless of what's behind it. Apply
   .tvp-on-glass to the wrapping element. */
/* Same fix, applied automatically site-wide: every page in the app opens with a
   bare heading (main > h3, "New Project", "User Management", ...) or a bare
   title+action header row (main > the standard Bootstrap
   d-flex/justify-content-between/align-items-center/mb-3 pattern) as the very
   first thing rendered inside <main> - a direct child, not wrapped in any card -
   so every one of them sat straight on the gradient with the exact same
   readability problem this page's own header had. Rather than hand-add
   .tvp-on-glass to a dozen+ view files (and every future one), targeting the
   handful of markup shapes those headers actually take, scoped to direct
   children of <main> so it can never catch a heading nested inside a real card. */
.tvp-on-glass,
main > h3, main > h2,
main > div.d-flex.justify-content-between.align-items-center.mb-3 {
  background: var(--tvp-glass-gloss), var(--tvp-glass-bg);
  backdrop-filter: var(--tvp-glass-blur);
  -webkit-backdrop-filter: var(--tvp-glass-blur);
  border: 1px solid var(--tvp-glass-border);
  border-radius: var(--tvp-radius);
  padding: .9rem 1.1rem;
  box-shadow: var(--tvp-shadow);
}

.tvp-page-header { display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.5rem; }
/* Now that .tvp-on-glass is a real panel (not raw gradient underneath), these go
   back to the plain brand/ink/muted colors every other card on the page already
   uses successfully - no more per-element halo/shadow special-casing needed. */
.tvp-page-eyebrow {
  font-size: .74rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--tvp-primary); display: flex; align-items: center; gap: .45rem; margin-bottom: .3rem;
}
.tvp-page-title { font-size: 1.7rem; font-weight: 800; letter-spacing: -.02em; color: var(--tvp-text); margin: 0; line-height: 1.15; }
.tvp-page-subtitle { color: var(--tvp-text-muted); font-size: .92rem; margin-top: .25rem; }
.tvp-live-dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--tvp-success); display: inline-block;
  position: relative;
}
.tvp-live-dot::after {
  content: ""; position: absolute; inset: -5px; border-radius: 50%;
  border: 2px solid var(--tvp-success); opacity: 0;
  animation: tvp-pulse-ring 2s ease-out infinite;
}

/* Stat tiles */
.tvp-stat {
  background: var(--tvp-surface);
  border: 1px solid var(--tvp-glass-border);
  border-radius: var(--tvp-radius);
  box-shadow: var(--tvp-shadow);
  padding: 1.1rem 1.25rem;
  display: flex;
  align-items: center;
  gap: .9rem;
  transition: transform .22s cubic-bezier(.22, .61, .36, 1), box-shadow .22s ease;
  opacity: 0;
  animation: tvp-rise .5s ease forwards;
}
.tvp-stat:hover { transform: translateY(-3px); box-shadow: var(--tvp-shadow-hover); }
.tvp-stat-link:active .tvp-stat { transform: translateY(-1px) scale(.985); }
.tvp-stat:hover .tvp-stat-icon { transform: scale(1.1) rotate(-4deg); }
.tvp-stat-icon {
  width: 46px; height: 46px; border-radius: 12px;
  display: flex; align-items: center; justify-content: center;
  flex: 0 0 46px;
  transition: transform .25s cubic-bezier(.34,1.56,.64,1);
}
.tvp-stat-icon svg { width: 22px; height: 22px; }
.tvp-stat-value { font-size: 1.65rem; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.tvp-stat-label { color: var(--tvp-text-muted); font-size: .82rem; font-weight: 500; }

.tvp-icon-primary { background: var(--tvp-primary-soft); color: var(--tvp-primary); }
.tvp-icon-info { background: var(--tvp-info-soft); color: var(--tvp-info); }
.tvp-icon-success { background: var(--tvp-success-soft); color: var(--tvp-success); }
.tvp-icon-danger { background: var(--tvp-danger-soft); color: var(--tvp-danger); }
.tvp-icon-teal { background: var(--tvp-teal-soft); color: var(--tvp-teal); }
.tvp-icon-purple { background: var(--tvp-purple-soft); color: var(--tvp-purple); }
.tvp-icon-pink { background: var(--tvp-pink-soft); color: var(--tvp-pink); }

/* Soft pulsing ring, in the icon's own color (currentColor), on every stat tile icon */
.tvp-pulse { position: relative; }
.tvp-pulse::after {
  content: "";
  position: absolute; inset: -4px;
  border-radius: 14px;
  border: 2px solid currentColor;
  opacity: 0;
  animation: tvp-pulse-ring 2.2s ease-out infinite;
}

@keyframes tvp-pulse-ring {
  0%   { opacity: .55; transform: scale(.9); }
  75%  { opacity: 0;   transform: scale(1.35); }
  100% { opacity: 0;   transform: scale(1.35); }
}

/* Stat tiles as filter links */
.tvp-stat-link { text-decoration: none; display: block; }
.tvp-stat-active {
  border-color: var(--tvp-primary);
  box-shadow: 0 0 0 3px var(--tvp-primary-soft), var(--tvp-shadow);
}

/* Generic surface card used for filters/charts.
   Bootstrap's .card-body/.card-title padding and spacing read from --bs-card-spacer-*,
   which Bootstrap only defines on .card - since this uses its own class, redeclare them
   here so nested .card-body/.card-title get real padding instead of resolving to 0. */
.tvp-card {
  --bs-card-spacer-y: 1.25rem;
  --bs-card-spacer-x: 1.25rem;
  --bs-card-title-spacer-y: .75rem;
  background: var(--tvp-surface);
  border: 1px solid var(--tvp-glass-border);
  border-radius: var(--tvp-radius);
  box-shadow: var(--tvp-shadow);
  opacity: 0;
  animation: tvp-rise .5s ease forwards;
  transition: transform .22s cubic-bezier(.22, .61, .36, 1), box-shadow .22s ease, border-color .22s ease;
}
/* Every card gets a gentle lift on hover, not just the ones with a click target
   inside (a filter form, a static chart) - a dashboard reads as "alive" when
   nothing on it is completely inert, and the lift is subtle enough not to imply
   the whole card is a button. */
.tvp-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--tvp-shadow-hover);
}

/* Project cards */
.project-card {
  border: 1px solid var(--tvp-glass-border) !important;
  border-radius: var(--tvp-radius);
  background: var(--tvp-surface);
  box-shadow: var(--tvp-shadow);
  transition: transform .22s cubic-bezier(.22, .61, .36, 1), box-shadow .22s ease, border-color .22s ease;
  opacity: 0;
  animation: tvp-rise .45s ease forwards;
  position: relative;
  overflow: hidden;
}
.project-card::before {
  content: "";
  position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
  background: var(--accent, var(--tvp-primary));
}
.project-card:hover {
  transform: translateY(-5px) scale(1.012);
  box-shadow: var(--tvp-shadow-hover);
  border-color: var(--accent, var(--tvp-primary)) !important;
}
/* Press feedback - the card (and any clickable pill/button) settles slightly on
   the actual click, not just the hover lift beforehand, so it feels pressed
   rather than just "reactive to the cursor". */
.project-card:active { transform: translateY(-2px) scale(.99); }
.project-card .badge { transition: transform .2s ease; }
.project-card:hover .badge { transform: scale(1.08); }

/* Animated progress fill */
.progress { background: #eef0f4; overflow: hidden; }
.progress-bar {
  transition: width 1s cubic-bezier(.22,.61,.36,1);
  width: 0;
}

/* Section title with a small colored lead-in dot */
.tvp-title-dot {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: var(--tvp-primary); margin-right: 8px; vertical-align: middle;
  box-shadow: 0 0 0 4px var(--tvp-primary-soft);
}

/* Chart bars brighten on hover for a livelier feel (no transform - these sit inside
   overflow:hidden tracks so scaling would just get clipped) */
.viz-seg, .viz-bar {
  transition: width 1s cubic-bezier(.22,.61,.36,1), filter .15s ease;
}
.viz-seg:hover, .viz-bar:hover { filter: brightness(1.15) saturate(1.1); }

@keyframes tvp-rise {
  from { opacity: 0; transform: translateY(14px) scale(.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes tvp-pop {
  0%   { transform: scale(.92); opacity: 0; }
  100% { transform: scale(1); opacity: 1; }
}

.tvp-badge-pop { animation: tvp-pop .35s ease backwards; }

/* Toast notification - floats over the page instead of pushing content down like
   the old inline Bootstrap alert did, slides/scales in, and auto-dismisses (see
   site.js) rather than sitting there until someone notices the close button. */
.tvp-toast-wrap {
  position: fixed; top: 84px; right: 1.25rem; z-index: 1085;
  display: flex; flex-direction: column; gap: .5rem;
  pointer-events: none;
  max-width: min(380px, calc(100vw - 2.5rem));
}
.tvp-toast {
  pointer-events: auto;
  display: flex; align-items: flex-start; gap: .65rem;
  background: var(--tvp-glass-gloss), var(--tvp-glass-bg-strong);
  backdrop-filter: var(--tvp-glass-blur);
  -webkit-backdrop-filter: var(--tvp-glass-blur);
  border: 1px solid var(--tvp-glass-border);
  border-radius: var(--tvp-radius);
  box-shadow: var(--tvp-shadow-hover);
  padding: .8rem .9rem;
  animation: tvp-toast-in .4s cubic-bezier(.34, 1.56, .64, 1) forwards;
}
.tvp-toast.tvp-toast-out { animation: tvp-toast-out .25s ease forwards; }
@keyframes tvp-toast-in {
  from { opacity: 0; transform: translateX(28px) scale(.94); }
  to   { opacity: 1; transform: translateX(0) scale(1); }
}
@keyframes tvp-toast-out {
  from { opacity: 1; transform: translateX(0) scale(1); }
  to   { opacity: 0; transform: translateX(28px) scale(.94); }
}
.tvp-toast-icon {
  flex: 0 0 auto; width: 22px; height: 22px; border-radius: 50%; margin-top: 1px;
  background: var(--tvp-success-soft); color: var(--tvp-success);
  display: flex; align-items: center; justify-content: center;
}
.tvp-toast-icon svg { width: 13px; height: 13px; }
.tvp-toast-text { flex: 1; font-size: .87rem; color: var(--tvp-text); line-height: 1.4; }
.tvp-toast-close {
  flex: 0 0 auto; background: none; border: none; color: var(--tvp-text-muted);
  font-size: 1.15rem; line-height: 1; cursor: pointer; padding: 0; margin-top: -2px;
  transition: color .15s ease;
}
.tvp-toast-close:hover { color: var(--tvp-text); }

@media (prefers-reduced-motion: reduce) {
  .tvp-stat, .tvp-card, .project-card, .progress-bar, .tvp-badge-pop,
  .tvp-stat-icon, .viz-seg, .viz-bar, .tvp-pulse::after, .tvp-live-dot::after, .tvp-toast {
    animation: none !important;
    transition: none !important;
    opacity: 1 !important;
  }
}