/* Vygotanium Partner Ecosystem — Brand Design System */
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');

/* ============================================================
   Theming
   :root holds the light palette; html[data-theme="dark"] flips every
   token below to the dark palette. On a person's first visit (no saved
   choice yet) the theme follows their OS/browser dark-mode setting; the
   inline anti-flash snippet in each layout's <head> resolves that
   before first paint. Once they use the toggle, that explicit choice is
   saved to localStorage and always wins over the OS setting from then
   on — see /assets/js/theme.js.

   --ink is intentionally NOT a themed token: it's the fixed near-black
   used for text sitting on bright/colored surfaces (primary buttons,
   avatars, the currency-toggle active pill) and must stay dark in both
   themes for contrast, unlike --void which is the page background and
   does flip.
   ============================================================ */
:root {
  color-scheme: light;
  --void: #F3F4F7;
  --surface: #FFFFFF;
  --raised: #EDEFF3;
  --border: #E1E4EA;
  --border-hover: #CBCFD9;
  --cyan: #0090C7;
  --violet: #6A4CE0;
  --success: #009E70;
  --alert: #E0294F;
  --warning: #B37600;
  --text: #14161C;
  --muted: #6B7280;
  --muted-2: #454A56;
  --ink: #08090D;
  --display: 'Space Grotesk', sans-serif;
  --mono: 'JetBrains Mono', monospace;
  --sans: 'Inter', system-ui, sans-serif;

  --topbar-bg: rgba(255,255,255,.85);
  --bottomnav-bg: rgba(255,255,255,.92);
  --field-sheen-1: rgba(8,9,13,.025);
  --field-sheen-2: rgba(8,9,13,.006);
  --shadow-color: rgba(20,22,30,.14);
  --shadow-color-lg: rgba(20,22,30,.16);
  --grid-line: rgba(20,22,30,.07);
  --aurora: linear-gradient(135deg, #00D4FF 0%, #7C5CFF 100%);
}

html[data-theme="dark"] {
  color-scheme: dark;
  --void: #08090D;
  --surface: #0F1219;
  --raised: #151924;
  --border: #1E2230;
  --border-hover: #2A2F42;
  --cyan: #00D4FF;
  --violet: #7C5CFF;
  --success: #00E5A0;
  --alert: #FF3D71;
  --warning: #FFB800;
  --text: #E8EAF0;
  --muted: #5B6178;
  --muted-2: #8A90A6;

  --topbar-bg: rgba(8,9,13,.85);
  --bottomnav-bg: rgba(11,12,18,.95);
  --field-sheen-1: rgba(255,255,255,.05);
  --field-sheen-2: rgba(255,255,255,.012);
  --shadow-color: rgba(0,0,0,.4);
  --shadow-color-lg: rgba(0,0,0,.45);
  --grid-line: rgba(30,34,48,.55);
}

/* The wallet balance card previously sat on the bright aurora gradient
   with fixed --ink text, which needed a dark-mode override to read
   white. It now uses plain themed surface/text colors like every other
   card, so no override is needed here anymore. */

/* OS-preference fallback: only applies before any inline script has run
   (e.g. JS disabled) or if data-theme was never set for some other
   reason. Once a person has an explicit choice — their own toggle, or
   the OS-detected value the inline script already resolved on first
   visit — that choice is written to localStorage and the anti-flash
   snippet in each layout's <head> sets data-theme from it directly, so
   this media query no longer has any effect for them. */
@media (prefers-color-scheme: dark) {
  html:not([data-theme]) {
    color-scheme: dark;
    --void: #08090D;
    --surface: #0F1219;
    --raised: #151924;
    --border: #1E2230;
    --border-hover: #2A2F42;
    --cyan: #00D4FF;
    --violet: #7C5CFF;
    --success: #00E5A0;
    --alert: #FF3D71;
    --warning: #FFB800;
    --text: #E8EAF0;
    --muted: #5B6178;
    --muted-2: #8A90A6;
    --topbar-bg: rgba(8,9,13,.85);
    --bottomnav-bg: rgba(11,12,18,.95);
    --field-sheen-1: rgba(255,255,255,.05);
    --field-sheen-2: rgba(255,255,255,.012);
    --shadow-color: rgba(0,0,0,.4);
    --shadow-color-lg: rgba(0,0,0,.45);
    --grid-line: rgba(30,34,48,.55);
  }
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
/* Smooth color-swap on toggle, without animating layout-affecting
   properties (would cause jank on every hover/click elsewhere). */
body, .card, .stat-card, .btn, .input, .field input, .field select, .field textarea,
.sidebar, .topbar, .bottom-nav, .mobile-drawer, .vg-dropdown, .flash, .nav-item,
.pill, .vg-icon-btn {
  transition: background-color .18s ease, border-color .18s ease, color .18s ease;
}
html, body {
  /* Defensive safety net: nothing on the site should ever need horizontal
     page scroll on a phone. If any element momentarily overflows (e.g. a
     decorative blur circle, a wide inline grid before its own fix loads)
     this guarantees it can never drag the whole page sideways. */
  overflow-x: hidden;
  /* This is an app UI, not an article — tapping/long-pressing nav items,
     buttons, cards, and labels shouldn't drop the OS text cursor, show a
     selection highlight, or (on iOS) pop the copy/paste callout. Inputs,
     textareas, and anything explicitly marked .selectable opt back in
     below, since those genuinely need normal text selection/editing. */
  -webkit-user-select: none;
  -moz-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
input, textarea, select,
.selectable {
  -webkit-user-select: text;
  -moz-user-select: text;
  user-select: text;
  -webkit-touch-callout: default;
}
body {
  font-family: var(--sans);
  background: var(--void);
  color: var(--text);
  min-height: 100vh;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font-family: inherit; }
img { max-width: 100%; display: block; }

/* Scrollbar */
* { scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 8px; }

/* Focus */
button:focus-visible, input:focus-visible, select:focus-visible, a:focus-visible {
  outline: 2px solid rgba(0,212,255,.35);
  outline-offset: 2px;
}

/* Typography */
.font-display { font-family: var(--display); }
.font-mono { font-family: var(--mono); }
.text-muted { color: var(--muted); }
.text-muted-2 { color: var(--muted-2); }
.text-cyan { color: var(--cyan); }
.text-success { color: var(--success); }
.text-alert { color: var(--alert); }
.text-warning { color: var(--warning); }
.text-violet { color: var(--violet); }

/* Animations */
@keyframes vg-fade-up {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: translateY(0); }
}
.vg-fade-up { animation: vg-fade-up .5s cubic-bezier(.16,1,.3,1) both; }

@keyframes vg-pop {
  from { opacity: 0; transform: scale(.94); }
  to { opacity: 1; transform: scale(1); }
}
.vg-pop { animation: vg-pop .32s cubic-bezier(.16,1,.3,1) both; }

@keyframes vg-shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}
.vg-shimmer-text {
  background: linear-gradient(110deg, var(--text) 30%, var(--cyan) 50%, var(--text) 70%);
  background-size: 200% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: vg-shimmer 3.5s linear infinite;
}

@keyframes vg-float {
  0%,100% { transform: translateY(0); }
  50% { transform: translateY(-9px); }
}
.vg-float { animation: vg-float 6.5s ease-in-out infinite; }

@keyframes vg-spin { to { transform: rotate(360deg); } }
.vg-spin { animation: vg-spin .8s linear infinite; }

/* Login screen: when someone typed a password into a Google-created
   account, the Google button gets a soft pulsing ring + a small pointer
   label above it, so the fix is visually obvious, not just text they have
   to go read and then go hunting for the right button. */
@keyframes vg-google-ring {
  0%, 100% { box-shadow: 0 0 0 0 rgba(0,229,160,.55); }
  50% { box-shadow: 0 0 0 8px rgba(0,229,160,0); }
}
.google-hint-ring { animation: vg-google-ring 1.8s ease-out 3; border-radius: .9rem; }
@keyframes vg-hint-pop {
  0% { opacity: 0; transform: translate(-50%, 6px); }
  100% { opacity: 1; transform: translate(-50%, 0); }
}
.google-hint-pop { animation: vg-hint-pop .4s cubic-bezier(.16,1,.3,1) both; }

.vg-grid-bg {
  background-image:
    linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size: 44px 44px;
  -webkit-mask-image: radial-gradient(ellipse 90% 65% at 50% 0%, black 25%, transparent 78%);
  mask-image: radial-gradient(ellipse 90% 65% at 50% 0%, black 25%, transparent 78%);
}

.vg-lift { transition: transform .25s ease, border-color .25s ease, box-shadow .25s ease; }
.vg-lift:hover { transform: translateY(-2px); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
  }
}

/* Layout helpers */
.container { width: 100%; max-width: 1200px; margin: 0 auto; padding: 0 1.25rem; }
.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.items-start { align-items: flex-start; }
.justify-between { justify-content: space-between; }
.justify-center { justify-content: center; }
.gap-1 { gap: .25rem; }
.gap-2 { gap: .5rem; }
.gap-3 { gap: .75rem; }
.gap-4 { gap: 1rem; }
.gap-5 { gap: 1.25rem; }
.gap-6 { gap: 1.5rem; }
.grid { display: grid; }
/* Belt-and-suspenders for the same min-width:auto trap fixed on .card
   and .nav-item above: this catches it for ANY element used as a grid
   item, not just those two, including future ones. Grid/flex items
   don't shrink below their content's natural size by default, so a
   direct grid child holding truncated/nowrap text anywhere inside it
   can silently force its whole column wider than the container -
   pushing content off the edge of the screen instead of letting the
   intended ellipsis/wrap handle it. */
.grid > *, .grid-split-2 > *, .stat-grid > *, .chat-split > * { min-width: 0; }
.grid-cols-2 { grid-template-columns: repeat(2, 1fr); }
.grid-cols-3 { grid-template-columns: repeat(3, 1fr); }
.grid-cols-4 { grid-template-columns: repeat(4, 1fr); }
.min-w-0 { min-width: 0; }
.w-full { width: 100%; }
.flex-1 { flex: 1 1 0%; }
.shrink-0 { flex-shrink: 0; }
/* min-width: 0 matters whenever this sits directly inside a flex/grid
   row (a title next to a badge, a filename next to a download icon):
   a flex item's default min-width is auto, meaning it refuses to
   shrink below its own unwrapped text width, so the ellipsis never
   actually kicks in and the row overflows instead. Harmless on a
   plain block element, where min-width:auto already behaves as 0. */
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.hidden { display: none; }
.relative { position: relative; }
.absolute { position: absolute; }
.fixed { position: fixed; }
.inset-0 { inset: 0; }
.z-10 { z-index: 10; }
.z-50 { z-index: 50; }
.overflow-hidden { overflow: hidden; }
.overflow-x-auto { overflow-x: auto; }
.overflow-y-auto { overflow-y: auto; }

/* Cards
   padding is a real default (not just a fallback): a full sweep of every
   view turned up 50+ cards across admin/partner pages that never set
   their own padding at all, so their heading/content sat flush against
   the rounded border on every screen size. Any card that wants a
   different amount (or an intentional padding:0 because its own
   children already manage edge-to-edge headers/dividers/tables) still
   wins via its inline style - inline always beats a class - so this is
   a safe, non-breaking default that only helps the cards that were
   missing one. */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 1.25rem;
  padding: 1.5rem;
  /* Grid/flex items default to min-width:auto, which refuses to shrink
     below the widest un-wrapped content inside them - even content
     that's meant to truncate. A .card sitting directly in a
     grid-cols-2/3/4 row with a `.truncate` name or code buried a few
     levels inside it would force the whole grid track wide enough to
     fit that name in full, pushing the card (and everything after it)
     past the edge of the screen instead of letting the ellipsis do its
     job. Same fix as the .min-w-0 utility already used throughout the
     flex rows - cards just never had it for their grid context. */
  min-width: 0;
}
.card-glow { box-shadow: 0 0 40px rgba(0,212,255,.07); }
.card-raised { background: var(--raised); }

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5rem;
  border-radius: 1rem;
  font-weight: 600;
  font-size: .875rem;
  padding: .8rem 1.4rem;
  border: none;
  cursor: pointer;
  transition: all .2s;
  text-decoration: none;
}
.btn:active { transform: scale(.98); }
.btn:disabled { opacity: .4; cursor: not-allowed; }
.btn-primary {
  background: var(--cyan);
  color: var(--ink);
}
.btn-primary:hover:not(:disabled) { box-shadow: 0 0 24px rgba(0,212,255,.45); }
.btn-secondary {
  background: var(--violet);
  color: #fff;
}
.btn-secondary:hover:not(:disabled) { box-shadow: 0 0 24px rgba(124,92,255,.45); }
.btn-ghost {
  background: var(--raised);
  color: var(--text);
  border: 1px solid var(--border);
}
.btn-ghost:hover:not(:disabled) { border-color: var(--border-hover); }
.btn-outline {
  background: transparent;
  border: 1px solid var(--border-hover);
  color: var(--text);
}
.btn-outline:hover:not(:disabled) { border-color: var(--cyan); color: var(--cyan); }
.btn-danger {
  background: rgba(255,61,113,.1);
  color: var(--alert);
  border: 1px solid rgba(255,61,113,.4);
}
.btn-sm { font-size: .75rem; padding: .55rem 1rem; border-radius: .8rem; }
.btn-full { width: 100%; }

/* Forms — modern, rounded, glassy fields (used by both `.field input` wrapper
   markup and the plain `class="input"` markup used across the app) */
.field { position: relative; }

.field input, .field select, .field textarea,
.input {
  width: 100%;
  background:
    linear-gradient(155deg, var(--field-sheen-1), var(--field-sheen-2) 60%),
    var(--raised);
  border: 1.5px solid var(--border);
  border-radius: 1.1rem;
  padding: .9rem 1.1rem .9rem 2.75rem;
  color: var(--text);
  font-size: .875rem;
  transition: border-color .2s ease, box-shadow .2s ease, background .2s ease, transform .12s ease;
  box-shadow: inset 0 1px 0 var(--field-sheen-1), 0 1px 3px var(--shadow-color);
  appearance: none;
  -webkit-appearance: none;
}
.field-noicon input, .field-noicon select, .field-noicon textarea,
.input {
  padding-left: 1.1rem;
}
.field input::placeholder, .field textarea::placeholder,
.input::placeholder { color: var(--muted); }

.field input:hover, .field select:hover, .field textarea:hover,
.input:hover {
  border-color: var(--border-hover);
}
.field input:focus, .field select:focus, .field textarea:focus,
.input:focus {
  outline: none;
  border-color: var(--cyan);
  background:
    linear-gradient(155deg, rgba(0,212,255,.08), rgba(124,92,255,.03) 60%),
    var(--raised);
  box-shadow: 0 0 0 4px rgba(0,212,255,.12), 0 6px 22px rgba(0,212,255,.08), inset 0 1px 0 var(--field-sheen-1);
}

select.input, .field select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%235B6178' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1.1rem center;
  padding-right: 2.6rem;
  cursor: pointer;
}
textarea.input, .field textarea {
  padding-top: .9rem;
  resize: vertical;
  min-height: 5.5rem;
  border-radius: 1.25rem;
}
.field .icon {
  position: absolute;
  left: 1.05rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--muted);
  pointer-events: none;
  z-index: 1;
}
/* Fields wrapped in .field-noicon (no leading icon) never need the icon gutter */
.field-noicon .icon { display: none; }

/* Pills & badges */
.pill {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  font-size: .625rem;
  font-weight: 700;
  padding: .25rem .625rem;
  border-radius: 9999px;
  text-transform: uppercase;
  letter-spacing: .03em;
}
.pill-dot {
  width: .375rem;
  height: .375rem;
  border-radius: 50%;
}

/* Avatar */
.avatar {
  border-radius: 50%;
  background: linear-gradient(135deg, var(--cyan), var(--violet));
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-family: var(--display);
  flex-shrink: 0;
  overflow: hidden;
}
.avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Avatar picker (KYC / signup) */
.avatar-picker {
  position: relative;
  width: 96px;
  height: 96px;
  border-radius: 50%;
  flex-shrink: 0;
  cursor: pointer;
}
.avatar-picker .avatar {
  width: 96px;
  height: 96px;
  font-size: 1.75rem;
  border: 2px solid var(--border);
}
.avatar-picker input[type="file"] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}
.avatar-picker .avatar-picker-badge {
  position: absolute;
  bottom: -2px;
  right: -2px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--cyan);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  border: 2px solid var(--void);
  pointer-events: none;
}

/* Logo */
.logo {
  display: flex;
  align-items: center;
  gap: .625rem;
}
.logo-mark {
  height: 28px;
  width: auto;
  display: block;
  object-fit: contain;
  flex-shrink: 0;
}
.logo-full {
  height: 40px;
  width: auto;
  display: block;
  object-fit: contain;
}
.logo-text {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: -0.02em;
}

/* Nav */
.sidebar {
  width: 240px;
  min-height: 100vh;
  background: var(--void);
  border-right: 1px solid var(--border);
  padding: 1.5rem .75rem;
  display: flex;
  flex-direction: column;
  gap: .25rem;
  position: sticky;
  top: 0;
}
.nav-item {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .625rem .875rem;
  border-radius: .75rem;
  font-size: .875rem;
  font-weight: 500;
  color: var(--muted-2);
  transition: all .15s;
  /* Same min-width:auto grid/flex-item trap as .card above - .nav-item
     is used both as a flex row internally AND as a direct grid item
     (e.g. the "Newest Partners" grid-cols-2 list), so it needs this in
     its own right rather than inheriting the .card fix. */
  min-width: 0;
}
.nav-item:hover { background: var(--raised); color: var(--text); }
.nav-item.active {
  background: rgba(0,212,255,.1);
  color: var(--cyan);
}
.nav-item.admin-active {
  background: rgba(124,92,255,.1);
  color: var(--violet);
}
.nav-badge {
  margin-left: auto;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 9999px;
  background: var(--alert);
  color: #fff;
  font-size: .65rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
}

/* Top bar
   NOTE: the blurred backdrop lives on ::before, not on .topbar itself.
   `backdrop-filter` (like `transform`/`filter`) makes the element it's
   set on a *containing block* for any `position: fixed` descendant —
   so a fixed-position descendant no longer positions relative to the
   viewport, it gets trapped inside .topbar's own box instead. That was
   clipping the notification/profile .vg-dropdown panel on mobile (it
   uses position:fixed there — see the media query further down) since
   it lives inside this header. Putting the blur on a pseudo-element
   behind the content keeps the exact same visual effect without .topbar
   itself carrying the filter, so descendants are unaffected. */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 1.5rem;
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 40;
}
.topbar::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--topbar-bg);
  backdrop-filter: blur(12px);
  z-index: -1;
}

/* Stats */
.stat-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 1rem;
  padding: 1rem;
  display: flex;
  flex-direction: column;
  gap: .75rem;
  min-width: 0;
}
.stat-label {
  font-size: .6875rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--muted);
  font-weight: 500;
}
.stat-value {
  font-size: 1.25rem;
  font-weight: 700;
  font-family: var(--display);
}

/* Toast / Flash */
.flash {
  position: fixed;
  bottom: 1.5rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 100;
  display: flex;
  align-items: center;
  gap: .5rem;
  background: var(--raised);
  border: 1px solid var(--border);
  padding: .625rem 1.25rem;
  border-radius: 9999px;
  font-size: .75rem;
  font-weight: 600;
  box-shadow: 0 8px 32px var(--shadow-color);
  animation: vg-pop .32s cubic-bezier(.16,1,.3,1) both;
}

/* Tables */
.table-wrap { overflow-x: auto; }
table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: .8125rem;
}
table.data th {
  text-align: left;
  font-size: .625rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--muted);
  font-weight: 700;
  padding: .75rem 1rem;
  border-bottom: 1px solid var(--border);
}
table.data td {
  padding: .75rem 1rem;
  border-bottom: 1px solid var(--border);
}
table.data tr:hover td { background: var(--raised); }

/* Responsive */
@media (max-width: 768px) {
  .sidebar { display: none; }
  .grid-cols-2, .grid-cols-3, .grid-cols-4 { grid-template-columns: 1fr; }
  /* A child using grid-column:span N (e.g. admin/dashboard.php's
     "Newest Partners" card spanning both columns on desktop) forces the
     grid to implicitly create extra columns to satisfy that span, even
     though grid-template-columns above says "1fr" - the explicit
     1-column template doesn't stop an implicit 2nd column from being
     auto-generated (sized by content) just to fit the span request.
     Net effect: the grid silently goes back to 2 uneven columns and the
     two cards meant to stack were instead overlapping each other. Reset
     the span here so nothing asks for more columns than the template
     defines. */
  .grid-cols-2 > *, .grid-cols-3 > *, .grid-cols-4 > * { grid-column: span 1 !important; }
  .hide-mobile { display: none !important; }
  .show-mobile { display: flex !important; }

  /* iOS Safari auto-zooms the page on focus for any input/select/textarea
     rendered under 16px, to keep the text legible — that's what was
     cropping the header/bottom nav out of view when tapping into a field.
     Forcing 16px here (overriding any inline font-size on the element)
     stops the browser from ever triggering that zoom in the first place. */
  input, select, textarea { font-size: 16px !important; }

  /* .topbar packs hamburger+logo, the EN/SW language toggle, and the
     currency/notif/chat/profile widgets into one row. On phone-width
     screens that's more content than fits, and with no wrap the language
     and currency toggles were getting squeezed off past the edge of the
     screen instead of staying visible. Wrapping + tighter spacing here
     keeps everything on-screen, dropping to a second line if needed. */
  .topbar {
    flex-wrap: wrap;
    row-gap: .5rem;
    padding: .75rem 1rem;
  }
  .topbar > div.flex.items-center[style*="margin-right"] {
    margin-right: 0 !important;
  }
}
@media (min-width: 769px) {
  .show-mobile { display: none !important; }
}

/* Mobile "more" drawer — off-canvas panel exposing every desktop sidebar
   link that isn't reachable from the bottom nav / topbar on mobile. */
.mobile-drawer-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(4,5,8,.6);
  backdrop-filter: blur(2px);
  z-index: 90;
  opacity: 0;
  visibility: hidden;
  transition: opacity .2s ease;
}
.mobile-drawer-backdrop.open { opacity: 1; visibility: visible; }
.mobile-drawer {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  width: 280px;
  max-width: 82vw;
  background: var(--void);
  border-right: 1px solid var(--border);
  z-index: 95;
  display: flex;
  flex-direction: column;
  transform: translateX(-100%);
  transition: transform .25s cubic-bezier(.16,1,.3,1);
  overflow-y: auto;
  padding: 1rem .75rem 1.25rem;
  padding-top: calc(1rem + env(safe-area-inset-top));
}
.mobile-drawer.open { transform: translateX(0); }
.mobile-drawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 .375rem .875rem;
  margin-bottom: .5rem;
  border-bottom: 1px solid var(--border);
}
.mobile-drawer-title {
  font-family: var(--display);
  font-size: .8125rem;
  font-weight: 700;
  color: var(--muted-2);
  text-transform: uppercase;
  letter-spacing: .05em;
}
.mobile-drawer-close {
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: .625rem;
  background: var(--raised);
  border: 1px solid var(--border);
  color: var(--muted-2);
}
@media (min-width: 769px) {
  .mobile-drawer, .mobile-drawer-backdrop, .hamburger-btn { display: none !important; }
}

/* Bottom nav mobile */
.bottom-nav {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background: var(--bottomnav-bg);
  backdrop-filter: blur(12px);
  border-top: 1px solid var(--border);
  display: flex;
  z-index: 50;
  padding-bottom: env(safe-area-inset-bottom);
}
.bottom-nav a {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .25rem;
  padding: .625rem .25rem;
  font-size: .5625rem;
  font-weight: 500;
  color: var(--muted);
}
.bottom-nav a.active { color: var(--cyan); }

/* Header widgets: notification bell / chat icon / dropdown */
.vg-icon-btn {
  position: relative;
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: .75rem;
  background: var(--raised);
  border: 1px solid var(--border);
  color: var(--muted-2);
  cursor: pointer;
  transition: all .15s;
}
.vg-icon-btn:hover { color: var(--cyan); border-color: var(--border-hover); }
.vg-badge {
  position: absolute;
  top: -5px;
  right: -5px;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  border-radius: 9999px;
  background: var(--alert);
  color: #fff;
  font-size: .6rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
  box-shadow: 0 0 0 2px var(--void);
}
.vg-dropdown {
  display: none;
  position: absolute;
  top: calc(100% + .6rem);
  right: 0;
  width: 320px;
  max-width: 86vw;
  max-height: 420px;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 1rem;
  box-shadow: 0 16px 40px var(--shadow-color-lg);
  z-index: 60;
}
.vg-dropdown.open { display: block; animation: vg-pop .18s ease both; }
.vg-dropdown-head {
  padding: .85rem 1rem;
  font-size: .75rem;
  font-weight: 700;
  border-bottom: 1px solid var(--border);
  color: var(--text);
}
.vg-dropdown-empty {
  padding: 1.5rem 1rem;
  text-align: center;
  color: var(--muted);
  font-size: .75rem;
}
.vg-dropdown-item {
  display: flex;
  gap: .6rem;
  align-items: flex-start;
  padding: .75rem 1rem;
  border-bottom: 1px solid var(--border);
}
.vg-dropdown-item.unread { background: rgba(0,212,255,.04); }
.vg-dropdown-dot { width: .5rem; height: .5rem; border-radius: 50%; margin-top: .35rem; flex-shrink: 0; }
.vg-dropdown-title { font-size: .78rem; font-weight: 600; color: var(--text); }
.vg-dropdown-body { font-size: .7rem; color: var(--muted); margin-top: .15rem; line-height: 1.4; }
.vg-dropdown-foot {
  display: block;
  text-align: center;
  padding: .7rem;
  font-size: .72rem;
  font-weight: 600;
  color: var(--cyan);
}
.vg-dropdown-foot:hover { background: var(--raised); }
.vg-menu-item {
  display: flex;
  align-items: center;
  gap: .65rem;
  padding: .65rem 1rem;
  font-size: .8rem;
  font-weight: 500;
  color: var(--text);
  text-decoration: none;
  transition: background .12s;
}
.vg-menu-item:hover { background: var(--raised); }
.vg-menu-item svg { flex-shrink: 0; color: var(--muted-2); }
.vg-menu-divider { height: 1px; background: var(--border); margin: .25rem 0; }

/* File upload control (used for resource uploads etc) */
.vg-file-drop {
  position: relative;
  width: 100%;
}
.vg-file-drop input[type="file"] {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
  width: 100%;
  height: 100%;
}
.vg-file-drop-label {
  display: flex;
  align-items: center;
  gap: .5rem;
  width: 100%;
  background: linear-gradient(155deg, var(--field-sheen-1), var(--field-sheen-2) 60%), var(--raised);
  border: 1.5px dashed var(--border-hover);
  border-radius: 1.1rem;
  padding: .9rem 1.1rem;
  color: var(--muted-2);
  font-size: .8rem;
}

/* ============================================================
   Mobile compliance pass — additional components/utilities.
   These are used by specific views (see comments) to fix layouts
   that don't collapse correctly on phone-width screens because
   they set grid-template-columns / fixed widths inline, which a
   plain CSS media query on a class can't override, or because
   they had no mobile-specific behaviour at all (split chat panes,
   the add-product modal).
   ============================================================ */

/* Two-column form-field pairs (e.g. Country/Region, Phone/Email,
   the KYC and lead forms) that must become a single column on
   phones instead of squeezing two inputs into ~140px each. */
.grid-split-2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .75rem;
}
@media (max-width: 768px) {
  .grid-split-2 { grid-template-columns: 1fr; }
}

/* Any dialog/overlay card: caps height and scrolls internally so a
   form with many fields never gets clipped above/below the visible
   viewport (this was possible on the Add Product modal, which had
   no max-height, on short phone screens or with the keyboard open). */
.modal-card {
  max-height: 85vh;
  overflow-y: auto;
}

/* Notification / profile dropdown: on desktop these are anchored via
   `right:0` relative to the icon button that opens them. On a phone
   that button can sit close to the left edge, so a 320px-wide panel
   anchored that way mostly renders off-screen to the left. Below the
   sidebar breakpoint we instead pin it to the viewport, spanning the
   width under the top bar. `!important` is required here because the
   profile dropdown also carries an inline `width:230px` for desktop,
   which otherwise always wins over a class rule regardless of media
   query. --vg-topbar-h is set by a small script in header_widgets.php
   to the topbar's real rendered height (it can wrap to two lines). */
@media (max-width: 768px) {
  .vg-dropdown {
    position: fixed !important;
    top: calc(var(--vg-topbar-h, 64px) + .5rem) !important;
    left: .75rem !important;
    right: .75rem !important;
    width: auto !important;
    max-width: none !important;
    max-height: calc(100vh - var(--vg-topbar-h, 64px) - 1.5rem) !important;
  }
}

/* Admin support-chat split view (thread list + conversation). On
   desktop it's a fixed 280px list beside a flexible conversation
   pane; that fixed column doesn't fit phone widths at all. On mobile
   it becomes a single column and only one pane shows at a time —
   the list, or (once a thread is picked) the conversation — with a
   "Threads" back-link (added in the view) to switch back. */
.chat-split {
  display: grid;
  grid-template-columns: 280px 1fr;
  gap: 1rem;
  min-height: 60vh;
}
@media (max-width: 768px) {
  .chat-split { grid-template-columns: 1fr; min-height: 0; }
  .chat-split .chat-list-pane,
  .chat-split .chat-detail-pane { display: block; }
  .chat-split.has-thread .chat-list-pane { display: none; }
  .chat-split:not(.has-thread) .chat-detail-pane { display: none; }
}

/* Installer specific */
.install-steps {
  display: flex;
  flex-direction: column;
  gap: .375rem;
}
.install-step {
  display: flex;
  align-items: center;
  gap: .625rem;
  padding: .625rem .75rem;
  border-radius: .75rem;
  font-size: .75rem;
  font-weight: 600;
}
.install-step.active { background: rgba(0,212,255,.1); color: var(--cyan); }
.install-step.done { color: var(--text); }
.install-step.pending { color: var(--muted); }

/* ============================================================
   World-Class additions (carried over from the update)
   New accent components used by the redesigned admin/partner
   views. Added on top of the full working stylesheet above
   instead of replacing it, since the update's own stylesheet
   was missing ~100 base utility/component classes it still
   depended on (buttons, badges, cards, forms, mobile nav, etc.)
   ============================================================ */
.text-gradient {
  background: var(--aurora);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.btn-aurora {
  background: var(--aurora);
  color: var(--ink);
  box-shadow: 0 4px 15px rgba(0,212,255,0.25);
}
.btn-aurora:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(0,212,255,0.35);
}
.btn-aurora:active {
  transform: scale(0.97);
}

/* Modal magic */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.4);
  backdrop-filter: blur(4px);
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}
.modal-content {
  background: var(--surface);
  border-radius: 2rem;
  max-width: 600px;
  width: 100%;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: 0 30px 60px rgba(0,0,0,0.2);
  animation: modal-enter 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes modal-enter {
  from { opacity: 0; transform: scale(0.9) translateY(20px); }
  to { opacity: 1; transform: scale(1) translateY(0); }
}

/* Modern table design (opt-in via .vg-table; existing .table-wrap tables are untouched) */
.vg-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0 0.5rem;
}
.vg-table th {
  padding: 1rem;
  text-align: left;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.vg-table td {
  padding: 1.25rem 1rem;
  background: var(--surface);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.vg-table td:first-child {
  border-left: 1px solid var(--border);
  border-top-left-radius: 1rem;
  border-bottom-left-radius: 1rem;
}
.vg-table td:last-child {
  border-right: 1px solid var(--border);
  border-top-right-radius: 1rem;
  border-bottom-right-radius: 1rem;
}
.vg-table tr:hover td {
  border-color: var(--border-hover);
  background: var(--raised);
}

/* Pill-shaped status label; colors are supplied inline per-status by the view */
.status-badge {
  padding: 0.25rem 0.75rem;
  border-radius: 2rem;
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
}

/* KPI card grid used by the redesigned dashboards */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1.5rem;
  margin-bottom: 2.5rem;
}
@media (max-width: 640px) {
  .stat-grid { grid-template-columns: 1fr; }
}

/* Padding utilities used by a few of the redesigned views */
.p-1 { padding: .25rem; }
.p-3 { padding: .75rem; }

/* Additional spacing/layout utilities used by the redesigned views */
.flex-wrap { flex-wrap: wrap; }
.items-end { align-items: flex-end; }
.justify-end { justify-content: flex-end; }
.mb-1 { margin-bottom: .25rem; }
.mb-4 { margin-bottom: 1rem; }
.mb-6 { margin-bottom: 1.5rem; }
.mt-2 { margin-top: .5rem; }
.bg-raised { background: var(--raised); }
.bg-void { background: var(--void); }
.border { border: 1px solid var(--border); }
.border-border { border-color: var(--border); }

/* Static (non-link) list row sharing the nav-item look, e.g. audit-log rows */
.nav-item-static:hover { background: var(--raised); }

/* Form field wrapper/label used throughout the redesigned forms */
.input-group { margin-bottom: 1.5rem; }
.input-label {
  display: block;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--muted);
  margin-bottom: 0.5rem;
  text-transform: uppercase;
  letter-spacing: 0.025em;
}

/* Soft gradient hairline border effect used on a few highlighted cards */
.glass-glow { position: relative; }
.glass-glow::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(135deg, rgba(255,255,255,0.2), transparent, rgba(255,255,255,0.1));
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}

/* ============================================================
   Site-wide responsive audit pass.
   Went through every view looking for content that can crop off
   the edge of the screen or collide with something else at small
   widths. Three recurring patterns, fixed once here instead of
   per-view so nothing gets missed and nothing drifts out of sync
   later:
     1. Card grids with a fixed pixel minimum column width
        (repeat(auto-fit, minmax(400px, 1fr)) etc.) — on any
        viewport narrower than that minimum, the single remaining
        column still refuses to shrink below it, so the card
        overflows the screen instead of the grid dropping to a
        true single column.
     2. A couple of floating popovers (permission/edit panels
        opened from a table row) that only knew how to anchor
        themselves via `right: 0` off a positioned ancestor —
        fine on desktop, but on a phone that ancestor can sit near
        the left edge, so the panel opens mostly off-screen. Same
        class of bug .vg-dropdown already had a fix for; this
        reuses that approach for these.
     3. Label/value and header/action rows built with
        `.flex.justify-between` (in any class order, with any
        other alignment classes) that never had explicit wrap
        behaviour. These hold user-entered or variable-length
        content (names, product names, badges) so on a narrow
        screen the two sides can be pushed edge-to-edge or
        overlap. Wrapping is always a safe fallback here — it
        only ever activates when a row doesn't fit, and drops the
        right-hand side to its own line instead of crowding it.
   ============================================================ */

/* (1) Auto-fit card grids: each view's inline grid-template-columns
   was edited directly (repeat(auto-fit, minmax(400px,1fr)) etc. ->
   minmax(min(400px,100%),1fr)) so a lone column on a narrow phone
   shrinks to fit instead of refusing to go below that px minimum
   and overflowing the screen. Nothing to add here — noted for
   context alongside the other two fixes below. */

/* (2) Popovers opened from a table row / list item. Same fix as
   .vg-dropdown: pin to the viewport below the topbar on mobile
   instead of trying to anchor off the row that opened it. */
@media (max-width: 768px) {
  .vg-popover {
    position: fixed !important;
    top: calc(var(--vg-topbar-h, 64px) + .5rem) !important;
    left: .75rem !important;
    right: .75rem !important;
    width: auto !important;
    max-width: none !important;
    max-height: calc(100vh - var(--vg-topbar-h, 64px) - 1.5rem) !important;
    overflow-y: auto;
  }
}

/* (3) Label/value + header/action rows: let them wrap instead of
   forcing two variable-width sides onto one line. `.flex.justify-
   between` matches regardless of what other classes (items-center,
   items-start, gap-4, mb-4, ...) sit alongside it. Opt out with
   .no-wrap on a specific row if it must always stay single-line. */
@media (max-width: 540px) {
  .flex.justify-between:not(.no-wrap) {
    flex-wrap: wrap;
    row-gap: .375rem;
  }
}

/* Long unbroken tokens (emails, referral links/codes, tx hashes)
   shown in a table cell, badge, or info row: let them break instead
   of forcing the row wider than the screen. */
.vg-break-anywhere { overflow-wrap: anywhere; }

/* Homepage hero header (home.php): logo + language toggle + CTA button +
   theme toggle all sit in one no-wrap flex row. The header's own padding
   (1.5rem/2rem) plus .container's padding (1.25rem/side) stack to
   3.25rem of horizontal padding per side, and the right-hand button
   group doesn't shrink below its own content width by default - on any
   phone under ~560px wide the combined content needed more room than was
   left, so the "Become a partner" button/theme toggle got pushed past
   the edge of the screen and clipped by the site-wide overflow-x:hidden
   safety net instead of staying visible. (.container's own padding is
   redundant once the header supplies edge padding directly, so it's
   zeroed here rather than doubled up.) The row itself already wraps at
   <=540px via the site-wide .flex.justify-between rule above, but on the
   narrowest phones (~320-360px) the button group alone still needed a
   touch more room, hence the tighter gap. */
@media (max-width: 640px) {
  .vg-home-header { padding: 1.1rem 1.25rem !important; }
  .vg-home-header .container {
    padding: 0;
    flex-wrap: wrap;
    row-gap: .75rem;
    justify-content: center;
  }
  .vg-home-header .vg-home-actions { gap: .75rem; }
}

/* Homepage product grid: 4 equal columns look great at desktop widths
   and correctly collapse to 1 column below 768px (site-wide grid-cols-4
   rule), but the gap in between - roughly tablet width, e.g. an iPad in
   portrait - was forcing 4 cards with 2rem of internal padding each into
   a cramped strip. A 2-column step in that range gives each card room
   to breathe before dropping to a single column on phones. */
@media (min-width: 641px) and (max-width: 900px) {
  .vg-home-header ~ main .grid.grid-cols-4 { grid-template-columns: repeat(2, 1fr); }
}

/* Lead pipeline stepper (partner/leads/show.php): each stage is a
   flex:1 column holding a fixed circle + an uppercase label. flex:1
   items default to min-width:auto, meaning they refuse to shrink below
   their own label's width - with 6 stages and labels like "PENDING
   APPROVAL", that's wider than the per-column share on any phone, so
   the row was pushing past the card edge instead of the labels simply
   wrapping onto a second line. */
.vg-stepper-item { min-width: 0; }
.vg-stepper-item span { overflow-wrap: break-word; }

/* Admin chat detail pane (admin/chat.php): the thread header, message
   list, and reply footer all carry 2rem of horizontal padding meant for
   a wide desktop panel. Stacked on top of the page's own 1.5rem main
   padding, that leaves very little room on a phone - the reply row in
   particular (attach icon + input + a fixed 50x50 send button) was
   getting so squeezed that flex-shrink (default on every flex child
   unless opted out) could compress the round send button into a
   distorted non-square shape instead of just narrowing the text input.
   Trimming the padding on phones fixes both the cramped feel and the
   squish risk. */
@media (max-width: 768px) {
  .vg-chat-thread-header,
  .vg-chat-footer {
    padding: 1rem 1.25rem !important;
  }
  .vg-chat-messages {
    padding: 1.25rem !important;
  }
}


