/* ==========================================================================
   amboi.my — Design System (canonical)
   --------------------------------------------------------------------------
   Single source of truth. Strict tokens only. NO improvisation outside this
   file. Every class lives here; Blade views compose them.
   ========================================================================== */

@import url('https://fonts.googleapis.com/css2?family=Lato:wght@400;500;700&display=swap');

/* ── Tokens ───────────────────────────────────────────────────────────── */
:root {
  /* ════════════════════════════════════════════════════════════════════
     COLOUR PALETTE — Refactoring UI compliant
     --------------------------------------------------------------------
     Each colour family has at least 6 shades (RUI says 5–9 is the
     working minimum). Naming follows Tailwind/Material convention:
     50  = lightest tint (just-off-white, for bg tints)
     100 = very light (subtle backgrounds, hover states on white)
     300 = muted (disabled states, borders on tinted bg)
     500 = base/default (the colour you'd reach for first)
     700 = deeper (hover state, active text)
     900 = darkest (heading text on coloured bg, deep surfaces)
     The 700 / 900 / 500 / 600 anchors keep the original Amboi values
     unchanged — existing usages of --primary-900, --primary-700,
     --accent-500, --accent-600 render identically before & after.
     ════════════════════════════════════════════════════════════════════ */

  /* Tokens are HSL — Refactoring UI's "Ditch hex for HSL". Hue +
     saturation stay roughly constant within a family while lightness
     walks; lets future shade tweaks be a single-number change.
     The HSL values are exact-equivalent renders of the previous hex
     anchors, so no pixel has shifted as a result of this migration. */

  /* Primary — Amboi indigo. Hue ≈228°, slowly desaturating as it
     lightens (RUI's "Don't let lightness kill your saturation"). */
  --primary-50:  hsl(227, 36%, 95%);  /* was #EEF0F7 */
  --primary-100: hsl(227, 38%, 88%);  /* was #D5DAEC */
  --primary-300: hsl(228, 35%, 65%);  /* was #8693C5 */
  --primary-500: hsl(229, 42%, 39%);  /* was #3A4A8E */
  --primary-700: hsl(228, 50%, 22%);  /* was #1C2755 — anchor */
  --primary-900: hsl(228, 53%, 15%);  /* was #121A3A — anchor */

  /* Accent — lime for CTAs. Hue ≈82°, locked saturation at the dark
     end so the brand pop stays vivid. */
  --accent-50:   hsl(81, 83%, 93%);   /* was #F2FCDF */
  --accent-100:  hsl(80, 83%, 84%);   /* was #E2F8B5 */
  --accent-300:  hsl(83, 78%, 68%);   /* was #BDED70 */
  --accent-500:  hsl(82, 100%, 44%);  /* was #8FE000 — anchor */
  --accent-600:  hsl(83, 100%, 38%);  /* was #7AC400 — anchor */
  --accent-700:  hsl(83, 100%, 30%);  /* was #5E9700 */
  --accent-900:  hsl(81, 100%, 14%);  /* was #2D4600 */

  /* Greys — currently pure (hue 0, saturation 0). Future polish:
     RUI's "greys don't have to be grey" → tint toward primary's
     hue. Keep pure for now so this commit changes no pixels. */
  --grey-50:  hsl(240, 11%, 98%);   /* was #FAFAFB */
  --grey-100: hsl(220, 12%, 95%);   /* was #F1F2F4 */
  --grey-200: hsl(223, 15%, 91%);   /* was #E4E6EB */
  --grey-300: hsl(220, 13%, 81%);   /* was #C9CDD5 */
  --grey-400: hsl(220, 10%, 65%);   /* was #9CA2AE */
  --grey-500: hsl(0, 0%, 42%);      /* was #6B6B6B — text-secondary */
  --grey-700: hsl(224, 9%, 25%);    /* was #3A3D45 */
  --grey-900: hsl(0, 0%, 10%);      /* was #1A1A1A — text-primary */

  /* Semantic — view code reaches for these instead of raw hex. */
  --danger-50:   hsl(0, 86%, 97%);    /* was #FEF2F2 */
  --danger-500:  hsl(0, 72%, 51%);    /* was #DC2626 */
  --danger-700:  hsl(0, 74%, 42%);    /* was #B91C1C */
  --warning-50:  hsl(48, 100%, 96%);  /* was #FFFBEB */
  --warning-500: hsl(38, 92%, 50%);   /* was #F59E0B */
  --warning-700: hsl(26, 90%, 37%);   /* was #B45309 */
  --success-50:  hsl(152, 81%, 96%);  /* was #ECFDF5 */
  --success-500: hsl(160, 84%, 39%);  /* was #10B981 */
  --success-700: hsl(163, 94%, 24%);  /* was #047857 */
  --info-50:     hsl(214, 100%, 97%); /* was #EFF6FF */
  --info-500:    hsl(199, 89%, 48%);  /* was #0EA5E9 */
  --info-700:    hsl(201, 96%, 32%);  /* was #0369A1 */

  /* Neutrals — surface aliases on top of the grey ramp + brand white. */
  --bg-main:    var(--grey-100);   /* page background */
  --bg-card:    #FFFFFF;            /* card / surface */
  --bg-subtle:  var(--grey-50);     /* nested subtle background */
  --border:     var(--grey-200);    /* hair-line dividers */
  --border-subtle: var(--grey-100); /* whisper-thin divider for chrome
                                       where bg-contrast already separates.
                                       Refactoring UI: "Use fewer borders".
                                       Pairs with --elev-1 below. */

  /* Elevation — subtle resting shadows so surfaces float rather than
     being fenced. Multi-layer per RUI's "Shadows can have two parts":
     a 1px crisp edge + a wider soft diffusion. Sized to suggest
     "above the page" without competing with content. */
  --elev-1: 0 1px 2px hsla(228, 53%, 15%, 0.04),
            0 1px 1px hsla(228, 53%, 15%, 0.02);
  --elev-2: 0 2px 4px hsla(228, 53%, 15%, 0.05),
            0 4px 12px hsla(228, 53%, 15%, 0.04);
  --elev-3: 0 4px 8px hsla(228, 53%, 15%, 0.06),
            0 12px 32px hsla(228, 53%, 15%, 0.08);

  /* Text — kept as semantic aliases. Pointing them at the grey ramp
     means anywhere you'd reach for --text-primary you can also reach
     for an alternative weight via --grey-700 / --grey-500. */
  --text-primary:   var(--grey-900);
  --text-secondary: var(--grey-500);

  /* Spacing (4-pt grid; skip-numbering at the top end keeps the scale
     dense at the small end + spread at the large end — RUI recipe). */
  --space-1: 4px;  --space-2: 8px;  --space-3: 12px; --space-4: 16px;
  --space-5: 20px; --space-6: 24px; --space-7: 28px; --space-8: 32px;
  --space-9: 36px; --space-10: 40px; --space-12: 48px; --space-16: 64px;

  /* Radii */
  --radius-input: 8px;
  --radius-card:  16px;
  --radius-pill:  999px;

  /* ════════════════════════════════════════════════════════════════════
     TYPE SCALE — rem-based (Refactoring UI compliant)
     --------------------------------------------------------------------
     RUI prescribes the 12-step scale: 12 / 14 / 16 / 18 / 20 / 24 / 30 /
     36 / 48 / 60 / 72. Stored as rem so a user who bumps their browser
     font-size gets proportional scaling (accessibility win).
     Naming uses Tailwind's xs..7xl idiom for familiarity.
     The legacy --fs-* names are aliased below so existing code keeps
     working — call sites can migrate as touched.
     ════════════════════════════════════════════════════════════════════ */
  --text-xs:   0.75rem;    /* 12px — small caps, fine print */
  --text-sm:   0.875rem;   /* 14px — body small, table cell */
  --text-base: 1rem;       /* 16px — body default */
  --text-lg:   1.125rem;   /* 18px — sub-headline */
  --text-xl:   1.25rem;    /* 20px — H3 */
  --text-2xl:  1.5rem;     /* 24px — H2 */
  --text-3xl:  1.875rem;   /* 30px — small hero */
  --text-4xl:  2.25rem;    /* 36px — H1 */
  --text-5xl:  3rem;       /* 48px — page hero */
  --text-6xl:  3.75rem;    /* 60px — marketing display */
  --text-7xl:  4.5rem;     /* 72px — landing-page heroes */

  /* Legacy aliases — kept so existing CSS keeps working unchanged. */
  --font: "Lato", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --fs-display: var(--text-5xl);    /* was 52px → 48px on the scale */
  --fs-h1:      var(--text-4xl);    /* was 36px */
  --fs-h2:      var(--text-2xl);    /* was 28px → 24px */
  --fs-h3:      var(--text-xl);     /* was 20px */
  --fs-body:    var(--text-sm);     /* was 14px */
  --fs-small:   var(--text-xs);     /* was 12px */
  --fs-stat:    2.5rem;             /* was 40px — kept off the standard scale */
  --lh-heading: 1.3; --lh-body: 1.5;

  /* Motion */
  --transition: 150ms ease-in-out;

  /* Bootstrap mapping (so legacy classes inherit our tokens) */
  --bs-body-font-family: var(--font);
  --bs-body-bg: var(--bg-main);
  --bs-body-color: var(--text-primary);
  --bs-body-font-size: var(--fs-body);
  --bs-body-line-height: var(--lh-body);
  --bs-primary: var(--accent-500);
  --bs-primary-rgb: 143, 224, 0;
  --bs-secondary: var(--text-secondary);
  --bs-link-color: var(--text-primary);
  --bs-link-hover-color: var(--primary-900);
  --bs-border-color: var(--border);
  --bs-border-radius: var(--radius-input);
  --bs-border-radius-lg: var(--radius-card);
}

/* ── Reset / foundation ──────────────────────────────────────────────── */
* { box-sizing: border-box; }
/* scrollbar-gutter: stable always reserves space for the vertical scrollbar
   so the centred .container (max-width: 1200px) sits at the same X position
   across every page, whether the page is tall enough to need a scrollbar or
   not. Without this, short pages (e.g. an empty /search) shift content
   ~15px right of long pages (e.g. the homepage) and the navbar buttons,
   nav links, plan-search bar, etc. all jump between pages. */
html {
  scroll-behavior: smooth;
  scrollbar-gutter: stable;
  /* iOS rubber-band overscroll — vertical edition. Earlier we fixed
     horizontal rubber-band via overflow-x: clip below. The VERTICAL
     bounce (page elastically continuing past the footer + top) was
     still active because overscroll-behavior defaults to `auto`.
     `contain` stops the elastic bounce + prevents scroll-chaining to
     parent contexts; the page now feels firmly anchored at the footer
     instead of "doesn't end". `contain` instead of `none` so pull-to-
     refresh still works in browsers that honor it inside the page. */
  overscroll-behavior: contain;
  /* iOS rubber-band overscroll fix — Mobile Safari + iOS Chrome attach
     their elastic horizontal-pan handler to the ROOT scroller (html).
     `overflow-x: hidden` on body alone (below) doesn't stop the iOS
     sideways bounce when a touch swipe drifts off-axis.
     CRITICAL: use `clip`, NOT `hidden`. `overflow-x: hidden` on <html>
     creates a new scrolling context, which BREAKS `position: sticky`
     on descendants (the .amboi-navbar `sticky-top` stops sticking on
     scroll). `overflow-x: clip` prevents horizontal scroll without
     establishing a scroll container, so sticky positioning still
     resolves up to the viewport. Supported in all browsers shipped
     since 2022 (Safari 16+, Chrome 90+, Firefox 81+). */
  overflow-x: clip;
}
body {
  font-family: var(--font);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-primary);
  background: var(--bg-main);
  -webkit-font-smoothing: antialiased;
  /* Belt-and-braces guard against horizontal overflow — the fixed filter
     rail and any other position: fixed/absolute children shouldn't push
     the document wider than the viewport, but if they do, clip rather
     than introducing a horizontal scrollbar that would further shift
     centred content.
     `clip` (not `hidden`) — same reason as the html rule above. `hidden`
     would create a scroll container, breaking position: sticky on the
     .amboi-navbar (sticky descendants resolve to the nearest overflow-
     not-visible ancestor, not the viewport). */
  overflow-x: clip;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font);
  font-weight: 700;
  color: var(--text-primary);
  line-height: var(--lh-heading);
  letter-spacing: -0.01em;
  margin: 0 0 var(--space-3);
}
/* Display + h1 get tighter letter-spacing (-0.02em) so they read as
   "premium" rather than just "big" — the move Apple uses for hero copy. */
h1, .h1, .display-1, .display-2, .display-3, .display-4 {
  font-size: var(--fs-h1);
  letter-spacing: -0.02em;
}
.amboi-display, .display { font-size: var(--fs-display); letter-spacing: -0.025em; line-height: 1.1; }
h2, .h2 { font-size: var(--fs-h2); letter-spacing: -0.015em; }
h3, .h3 { font-size: var(--fs-h3); }
h4, h5, h6, .h4, .h5, .h6 { font-size: var(--fs-h3); }

p, li, label, input, textarea, select, button, a, span, div { font-size: inherit; }
.lead { font-size: 16px; color: var(--text-secondary); font-weight: 400; }
small, .small { font-size: var(--fs-small); }

a { color: var(--text-primary); text-decoration: none; transition: color var(--transition); }
a:hover { color: var(--primary-900); }
::selection { background: var(--accent-500); color: #000; }

hr, .amboi-divider { border: 0; height: 1px; background: var(--border); margin: var(--space-6) 0; }

/* ── Buttons ─────────────────────────────────────────────────────────── */
.btn,
.btn-primary,
.btn-secondary,
.btn-outline-primary,
.btn-outline-secondary,
.btn-light,
.btn-ghost,
.vp-btn-primary,
.vp-btn-lime,
.vp-btn-ghost {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  height: 40px; padding: 0 var(--space-4);
  font-family: var(--font); font-size: var(--fs-body); font-weight: 500;
  /* Was --radius-pill (always fully rounded); switched to --radius-input
     so buttons respect the admin's Corner-radius setting (Theme &
     appearance → Corner radius). True pill components — status chips,
     "Sponsored" badges, response-time pills — keep their --radius-pill
     because they're named that way for a reason. */
  border-radius: var(--radius-input);
  border: 1px solid transparent;
  cursor: pointer; text-decoration: none;
  transition: background-color var(--transition), border-color var(--transition), color var(--transition);
  white-space: nowrap;
}
.btn-lg, .vp-btn-primary.btn-lg { height: 48px; padding: 0 var(--space-6); font-size: 16px; }
.btn-sm, .vp-btn-primary.btn-sm { height: 32px; padding: 0 var(--space-3); font-size: var(--fs-small); }
/* Bootstrap-icons inside our buttons render with their own 1.5em line-height,
   which left a phantom box taller than the button text and threw off the
   vertical centering (especially noticeable on bi-plus-lg). Forcing
   line-height:1 + flex-shrink:0 makes the glyph occupy exactly its glyph
   height so flex's align-items:center aligns it cleanly with the label. */
.btn > i,
.vp-btn-primary > i,
.vp-btn-lime > i,
.vp-btn-ghost > i {
  line-height: 1;
  flex-shrink: 0;
}

/* Marketing primary — lime accent for high-emphasis, top-of-funnel CTAs
   (Hero "Get Started", "Try for FREE Today!", marketing landing buttons).
   Keeps the lime accent within the strict 10% color-usage budget.
   Text colour is hard-coded near-black: we cannot use var(--text-primary)
   here because that token flips to near-white in dark mode, leaving
   white-on-lime which is unreadable. The lime background stays the same
   across themes, so the foreground must too — matching the "Sponsored"
   pill (line 261) for visual consistency. */
.btn-primary,
.vp-btn-lime {
  background: var(--accent-500) !important;
  /* Adaptive foreground — Setting::readableForeground emits #1A1A1A on
     light accents (lime, amber) and #FFFFFF on dark accents (purple,
     pink, navy). Hardcoded fallback covers the rare cold-cache miss. */
  color: var(--accent-fg, #1A1A1A) !important;
  border-color: var(--accent-500) !important;
}
.btn-primary:hover,
.vp-btn-lime:hover,
.btn-primary:focus,
.vp-btn-lime:focus {
  background: var(--accent-600) !important;
  border-color: var(--accent-600) !important;
  color: var(--accent-fg, #1A1A1A) !important;
}

/* System primary — dark navy + white for form submissions and confirmation
   actions (Login, Register, Save changes, Pay deposit, Resolve dispute…).
   This is the bulk of dashboard CTAs; keeps the UI calm and professional. */
.vp-btn-primary,
.btn-dark {
  background: var(--primary-900) !important;
  color: #fff !important;
  border-color: var(--primary-900) !important;
}
/* Hover / focus — lift to the brand accent instead of a lighter shade
   of `--primary-900`. The resting dark-ink looked "dark blue" on the
   Rekaizen palette (the ink #292B33 has a blue-dominant channel — R41
   G43 B51) and the old hover (--primary-700, derived 18% lighter)
   inherited the same blue undertone, so a hover read as "even more
   dark blue" instead of as a branded interaction. Flipping hover to
   accent makes the affordance unambiguous and palette-aligned across
   every theme (teal on Rekaizen, lime on Lime Classic, purple on
   Vibrant 5). Text stays white because every supported accent passes
   white-on-accent contrast — and we still hand-off to --accent-fg as
   a fallback if a future custom palette needs dark text. */
.vp-btn-primary:hover,
.btn-dark:hover,
.vp-btn-primary:focus,
.btn-dark:focus {
  background: var(--accent-500) !important;
  border-color: var(--accent-500) !important;
  color: var(--accent-fg, #fff) !important;
}

/* Secondary */
.btn-secondary,
.btn-outline-primary,
.btn-outline-secondary,
.btn-light,
.btn-ghost,
.vp-btn-ghost {
  background: transparent !important;
  border: 1px solid var(--border) !important;
  color: var(--text-primary) !important;
}
.btn-secondary:hover,
.btn-outline-primary:hover,
.btn-outline-secondary:hover,
.btn-light:hover,
.btn-ghost:hover,
.vp-btn-ghost:hover {
  background: var(--bg-subtle) !important;
  border-color: var(--text-secondary) !important;
}

/* Buttons that sit ON a dark surface (e.g. the home hero) need an inverted
   palette: white text and a translucent-white hover instead of the default
   light-grey hover (which produces white-on-light-grey, unreadable). */
.btn-on-dark {
  background: transparent !important;
  border: 1px solid rgba(255, 255, 255, 0.35) !important;
  color: #fff !important;
}
.btn-on-dark:hover,
.btn-on-dark:focus,
.btn-on-dark:focus-visible {
  background: rgba(255, 255, 255, 0.12) !important;
  border-color: rgba(255, 255, 255, 0.7) !important;
  color: #fff !important;
}

/* Dark variant (legacy red CTA buttons) */
.btn-danger, .btn-outline-danger { background: transparent !important; border: 1px solid var(--border) !important; color: var(--text-primary) !important; }

/* Disabled */
.btn:disabled, .btn.disabled { opacity: .5; cursor: not-allowed; }

/* ── Cards ───────────────────────────────────────────────────────────── */
.card-default,
.amboi-card,
.vp-card,
.vp-surface,
.amboi-cat-card,
.amboi-step,
.vp-progress-step,
.amboi-stat,
.vp-stat,
.amboi-empty,
.vp-banner {
  background: var(--bg-card);
  /* Refactoring UI's "use fewer borders": pair a softer border with a
     subtle resting shadow so cards float instead of being fenced. The
     border doubles as a 1px-precise edge in dark mode (where the
     shadow has lower contrast); the shadow does the elevation work in
     light mode. */
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-card);
  padding: var(--space-6);
  box-shadow: var(--elev-1);
  transition: box-shadow var(--transition), border-color var(--transition);
}
.amboi-card:hover, .vp-card:hover { box-shadow: var(--elev-2); }

.vp-surface { padding: 0; overflow: hidden; }
/* Defined AFTER .vp-surface so combined `<div class="vp-surface vp-surface-pad">` keeps padding. */
.amboi-card-body, .vp-surface-pad { padding: var(--space-6); }

/* `.amboi-card` is a media-card (image on top + body below). The image
   must sit flush against the rounded corners — override the shared
   24px padding from the cards rule above and clip the overflow so
   the image corners follow the card's border-radius. Padding is
   reintroduced inside `.amboi-card-body`.

   Equal-height behaviour: cards live in Bootstrap row/col grids where
   columns already stretch to the tallest sibling. Without `height:100%`
   on the card itself, the inner card collapses to its content and
   adjacent cards visibly mismatch when their chip-rows wrap differently
   (e.g. 2 compliance badges vs. 3). Forcing the card to fill its column
   AND turning the body into a flex-column with `margin-top:auto` on the
   bottom row anchors price + rating to a consistent baseline regardless
   of how many badges a service shows.

   `.position-relative` is the wrapper the partial puts the save-button
   inside; it also needs `height:100%` so it forwards the stretch down
   to `.amboi-card`. */
.amboi-card {
  padding: 0;
  overflow: hidden;
  height: 100%;
  display: flex;
  flex-direction: column;
}
.amboi-card > .amboi-card-body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
}
/* Reserve a fixed slot for the compliance-badges row so the price+rating
   row that follows it lands at a CONSISTENT Y position across every card
   in a grid — regardless of whether a service has 1, 2, or 3 chips. Why
   not just `margin-top:auto` on the price row? That puts price at the
   card BOTTOM, which fixes bottom-edge alignment but leaves a visible
   gap-jump between the chip slot and price as chip-counts differ (Card
   with 1 chip has a fat gap; cards with 3 chips have none). Reserving a
   2-row slot here gives every card the same chip → price spacing, and
   the price row sits on the same baseline visually.
   ~44px = 2 chip rows (chip height ~20px × 2 + 4px gap). */
.amboi-compliance-row {
  min-height: 44px;
  align-content: flex-start;
}
/* margin-top:auto on the price+rating row pushes it past the reserved
   chip slot AND anchors it against the card bottom — so cards still
   bottom-align even when their compliance slot is under-filled. */
.amboi-card > .amboi-card-body > :last-child {
  margin-top: auto;
}

/* ── Admin form pages — reset marketplace-grid behavior ──────────────
   .amboi-card is shared between (a) marketplace product cards that need
   equal heights + a CTA pushed to the bottom of each grid cell and (b)
   admin form sections (Homepage editor, Vendor landing editor, Theme
   editor) where the same class is just a wrapper around stacked form
   inputs.

   On the admin pages the height: 100% rule stretches each card to match
   the tallest sibling in its row, and the :last-child { margin-top: auto }
   rule pushes the LAST form input to the bottom of the over-stretched
   card. The result: visible "huge gaps" between the inputs above and
   the last input below, plus between the bottom of a short card and the
   end of its over-stretched column.

   Reset both behaviors when an .amboi-card sits inside the admin portal
   (body.vp-portal-admin). Customer + vendor dashboards keep the original
   marketplace-card behavior since data cards in those grids do want
   equal heights. */
.vp-portal-admin .amboi-card {
  height: auto;
}
.vp-portal-admin .amboi-card > .amboi-card-body > :last-child {
  margin-top: 0;
}
/* Also stop the Bootstrap .row from forcing equal column heights on admin
   form layouts — otherwise the shorter column (usually the one with
   fewer cards) shows visible empty space below its last card while the
   longer column still has content. align-items: flex-start lets each
   column collapse to its natural content height. Scoped narrowly to .row
   children of admin .vp-page to avoid touching marketplace product grids
   on other surfaces. */
.vp-portal-admin .vp-page > .row,
.vp-portal-admin .vp-page .tab-content > .tab-pane > form > .row,
.vp-portal-admin .vp-page > form > .row {
  align-items: flex-start;
}
/* Save-button wrapper used by `web/partials/service-card.blade.php`
   forwards the grid-column stretch down to the card via `h-100` on the
   wrapping <div>; see that partial. Don't switch to :has() on
   .position-relative — that class is shared with hundreds of other
   wrappers across the app and the side-effects are too wide. */
.vp-surface-head {
  padding: var(--space-4) var(--space-6);
  border-bottom: 1px solid var(--border);
}
.vp-surface-foot {
  padding: var(--space-4) var(--space-6);
  border-top: 1px solid var(--border);
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
}

/* Tabs inside a .vp-surface-head — Bootstrap's default `.nav-tabs` add
   a hard bottom border + active-tab "lift" that fights the surface
   chrome's own border. This variant flattens them: no own bottom
   border, active tab is a clean accent underline, inactive tabs are
   muted text without borders. Used by the admin activity card. */
.vp-surface-tabs.nav-tabs {
  border-bottom: 0;
  gap: var(--space-2);
  margin: 0;
}
.vp-surface-tabs.nav-tabs .nav-link {
  border: 0;
  background: transparent;
  color: var(--text-secondary);
  font-weight: 600;
  font-size: var(--fs-small);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-input);
  margin: 0;
  position: relative;
}
.vp-surface-tabs.nav-tabs .nav-link:hover {
  color: var(--text-primary);
  background: var(--bg-subtle);
}
.vp-surface-tabs.nav-tabs .nav-link.active {
  color: var(--text-primary);
  background: transparent;
  border: 0;
}
.vp-surface-tabs.nav-tabs .nav-link.active::after {
  content: "";
  position: absolute;
  left: var(--space-3); right: var(--space-3);
  bottom: -10px; /* visually rests on the surface-head's own bottom border */
  height: 2px;
  background: var(--accent-500);
  border-radius: 1px;
}
.vp-surface-tabs.nav-tabs .nav-link .badge {
  vertical-align: 1px;
}

/* Mini empty state — replaces single-line muted text inside activity
   tabs / list panes. Compact, friendly, with an icon + primary line +
   secondary line + optional CTA. Lives flush inside a .vp-surface
   (no extra border) so it reads as part of the card it sits in. */
.amboi-empty-mini {
  display: flex; flex-direction: column; align-items: center;
  text-align: center; gap: 6px;
  padding: var(--space-6) var(--space-5);
  color: var(--text-secondary);
}
.amboi-empty-mini i {
  font-size: 28px;
  color: var(--accent-500);
  margin-bottom: 4px;
  opacity: 0.7;
}
.amboi-empty-mini strong {
  color: var(--text-primary);
  font-size: var(--fs-body);
  font-weight: 700;
}
.amboi-empty-mini span {
  color: var(--text-secondary);
  font-size: var(--fs-small);
  max-width: 320px;
}
.amboi-empty-mini a {
  color: var(--accent-600, var(--accent-500));
  font-weight: 600;
  margin-top: var(--space-1);
}

/* Card image (services) */
.amboi-card-img {
  aspect-ratio: 4 / 3;
  background-color: var(--bg-subtle);
  background-size: cover; background-position: center;
}
.amboi-card-title { font-size: var(--fs-body); font-weight: 700; margin: 0 0 var(--space-1); color: var(--text-primary); line-height: var(--lh-heading); }
.amboi-card-meta { color: var(--text-secondary); font-size: var(--fs-small); }
.amboi-card-price { font-weight: 700; color: var(--text-primary); font-size: var(--fs-body); }

/* Hover card pill */
/* The .amboi-canvas-shell class was removed — applying the dashboard's
   rounded-card canvas to layouts.app pages (Stories, etc.) created a
   visible "card inside a page" mismatch because the public navbar +
   sticky plan-search bar stay edge-to-edge while the content card was
   rounded. The Canvas pattern only fits cleanly when it owns the whole
   viewport (i.e., `body.amboi-dashboard-app`). Don't reintroduce. */

/* ============================================================
   Tinted-icon container (.amboi-tinted-icon) — the universal motif from
   Hiresmi / Findom / Orifiopin dashboards. Soft-tinted rounded square +
   centered icon. Six tones drawn from the brand palette + four utility
   hues used at 12% opacity so navy + lime stay dominant.

   Sizes: sm (32px) for inline use, md (40px) for KPI cards, lg (48px) for
   page-section headers. Icon inherits the tone's "active" color (the
   solid version of the tint) so it reads strongly against the soft bg.
   ============================================================ */
.amboi-tinted-icon {
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 10px;
  flex-shrink: 0;
  transition: transform var(--transition);
}
.amboi-tinted-icon i { line-height: 1; }
.amboi-tinted-icon--sm { width: 32px; height: 32px; font-size: 16px; }
.amboi-tinted-icon--md { width: 40px; height: 40px; font-size: 20px; }
.amboi-tinted-icon--lg { width: 48px; height: 48px; font-size: 24px; }

/* Tones — bg at 12% opacity, icon at 100% (or close, where contrast allows).
   The "lime" tone is the brand-tracking one: it stays semantically
   "the brand accent" and follows palette swaps (lime → teal → custom).
   Other tones (coral/sky/navy/amber/purple) are intentional non-brand
   category colours and stay as literals. */
.amboi-tinted-icon--lime   { background: rgba(var(--accent-rgb, 81, 180, 190), 0.15); color: var(--accent-700, var(--accent-500)); }
.amboi-tinted-icon--coral  { background: rgba(255, 107, 107, 0.12); color: #c0392b; }
.amboi-tinted-icon--sky    { background: rgba(14, 165, 233, 0.12); color: #0369a1; }
.amboi-tinted-icon--navy   { background: rgba(18, 26, 58, 0.08); color: var(--primary-900); }
.amboi-tinted-icon--amber  { background: rgba(245, 158, 11, 0.13); color: #b45309; }
.amboi-tinted-icon--purple { background: rgba(139, 92, 246, 0.12); color: #7c3aed; }

/* Dark-mode adjustments — bg stays tinted, icon lifts to a brighter tone
   so it still reads on the darker page background. */
html[data-theme="dark"] .amboi-tinted-icon--lime   { color: var(--accent-500); }
html[data-theme="dark"] .amboi-tinted-icon--coral  { color: #fca5a5; }
html[data-theme="dark"] .amboi-tinted-icon--sky    { color: #7dd3fc; }
html[data-theme="dark"] .amboi-tinted-icon--navy   { background: rgba(255, 255, 255, 0.08); color: #c5d4ff; }
html[data-theme="dark"] .amboi-tinted-icon--amber  { color: #fcd34d; }
html[data-theme="dark"] .amboi-tinted-icon--purple { color: #c4b5fd; }

/* Response-time pill — surfaced on vendor cards, vendor profile header,
   and service-detail booking sidebars. Tells customers "how fast does
   this vendor respond?" — a top-3 buying signal on marketplaces (Shopee,
   Lazada, Etsy all surface this). Bucket modifier colors the pill:
     • .is-fast    → lime (≤6h)    — best, signals "highly responsive"
     • .is-normal  → navy (≤24h)   — solid, signals "professional"
     • .is-slow    → neutral grey  — informational only, no warning
   The bucketing happens in App\Models\Vendor::responseTimeBucket(). */
.amboi-response-pill {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px var(--space-3);
  border-radius: var(--radius-pill);
  font-size: var(--fs-small); font-weight: 600;
  background: var(--bg-subtle); color: var(--text-secondary);
  border: 1px solid var(--border);
  white-space: nowrap;
}
.amboi-response-pill i { font-size: 0.9em; line-height: 1; }
.amboi-response-pill.is-fast {
  /* Tinted-accent variant — soft 12% wash of the active accent for
     the background, deeper accent shade for the text. Drives off
     --accent-rgb + --accent-700 (both emitted by Setting::theme()),
     so the pill tracks Rekaizen teal, lime classic, Vibrant 5
     purple, or any custom palette without further edits. The
     fallback rgb(143,224,0) is the legacy lime — only kicks in on
     a cold cache miss. */
  background: rgba(var(--accent-rgb, 143, 224, 0), 0.12);
  color: var(--accent-700, var(--accent-500));
  border-color: var(--accent-500);
}
.amboi-response-pill.is-normal {
  background: rgba(18, 26, 58, 0.06);
  color: var(--primary-900);
  border-color: rgba(18, 26, 58, 0.15);
}

.amboi-card-pill {
  position: absolute; top: var(--space-3); left: var(--space-3);
  background: var(--bg-card); color: var(--text-primary);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  font-size: var(--fs-small); font-weight: 700;
  border: 1px solid var(--border);
}
/* Featured pill — flipped from accent (teal) to tertiary (logo
   orange). Orange/amber is the global "promoted/featured" convention
   (Amazon, Etsy, Google ads); teal here weakened the brand CTA by
   sharing colour with the primary action. `--tertiary-fg` resolves
   to white on the logo orange (luminance pick). */
.amboi-card-pill.featured {
    background: var(--tertiary-500, var(--accent-500));
    color: var(--tertiary-fg, #000);
    border-color: var(--tertiary-500, var(--accent-500));
}
/* ⭐ Top-rated pill — organic top-up in the home "Featured services" strip
   when fewer than 4 paid placements exist. Indigo tone keeps it distinct
   from green Sponsored (cool vs warm) so customers can tell which
   placements are paid at a glance. Never appears in the same slot as
   .featured (mutually exclusive in the partial). */
.amboi-card-pill.top-rated {
    background: rgba(99, 102, 241, .12);
    color: #4338ca;
    border-color: rgba(99, 102, 241, .35);
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
.amboi-card-pill.top-rated > i { font-size: 11px; }
/* ⚡ Instant Book card pill — lime-tinted to share visual family with the
   Instant badge on the service show page. Stacks vertically below .featured
   when both are present (vanishingly rare but covered). */
.amboi-card-pill.instant {
    background: rgba(var(--accent-rgb, 143, 224, 0), .15);
    color: var(--accent-600, var(--accent-500));
    border-color: rgba(var(--accent-rgb, 143, 224, 0), .45);
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
.amboi-card-pill.instant > i { font-size: 11px; }
.amboi-card-pill.featured ~ .amboi-card-pill.instant,
.amboi-card-pill.top-rated ~ .amboi-card-pill.instant {
    top: calc(var(--space-3) + 30px);
}

/* ── Top Vendor pill (Phase 3.2 reliability surface) ─────────────────
   Amber-gold accent reads "premium recognition" without claiming
   premium pricing. Stacks below the previous two pills via the same
   sibling-offset trick so all three can coexist on the rare card
   that's featured + instant-book + top-vendor. */
.amboi-card-pill.top-vendor {
    background: rgba(245, 158, 11, .15);
    color: #b45309;
    border-color: rgba(245, 158, 11, .45);
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
.amboi-card-pill.top-vendor > i { font-size: 11px; }
.amboi-card-pill.instant ~ .amboi-card-pill.top-vendor {
    top: calc(var(--space-3) + 30px);
}
.amboi-card-pill.featured ~ .amboi-card-pill.instant ~ .amboi-card-pill.top-vendor {
    top: calc(var(--space-3) + 60px);
}
.amboi-card-pill.featured ~ .amboi-card-pill.top-vendor:not(.amboi-card-pill.instant ~ *) {
    top: calc(var(--space-3) + 30px);
}

/* ── Vendor profile reliability badge (cv-name inline chip) ──────────
   Smaller than the avatar/header text so it reads as supporting
   metadata, not a competing title. Identical palette to the card
   pill so the visual language is consistent across surfaces. */
.cv-reliability-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-left: 8px;
    padding: 2px 8px;
    font-size: 11.5px;
    font-weight: 700;
    background: rgba(245, 158, 11, .15);
    color: #b45309;
    border: 1px solid rgba(245, 158, 11, .45);
    border-radius: 999px;
    vertical-align: middle;
    cursor: help;
}
.cv-reliability-badge > i { font-size: 12px; }

/* ── Layout shell ────────────────────────────────────────────────────── */
.amboi-shell { min-height: 100vh; display: flex; flex-direction: column; }
.container, .container-fluid { max-width: 1200px; }
/* Mobile edge-padding boost — Bootstrap's default --bs-gutter-x is
   1.5rem (= 12px each side), which feels edge-to-edge cramped on
   375-414px phones. Bump to 16px each side at <sm so public pages
   match the dashboard's .vp-page mobile padding (--space-4 = 16px)
   and gain a comfortable iOS-style breathing room. Desktops keep the
   12px default since they have plenty of horizontal room already. */
@media (max-width: 575.98px) {
  .container, .container-fluid {
    --bs-gutter-x: 2rem; /* 32px total → 16px each side */
  }
}

/* Desktop frame: cap the main content area between 1080px and 1600px while keeping
   the sidebar flush left and the topbar/navbar spanning the full viewport width.
   Below 1080px (but still at desktop breakpoint), the content overflows horizontally. */
@media (min-width: 992px) {
  .amboi-desktop-frame {
    width: 100%;
    max-width: 1600px;
    min-width: 1080px;
    margin-inline: auto;
  }
}

/* App-shell pattern: at desktop sizes, only the main content area scrolls.
   The sidebar and topbar remain fixed while the user scrolls the content.
   Applies uniformly to customer, vendor, and admin dashboards.

   App-in-a-rounded-card canvas (the Hiresmi / Findom / Orifiopin pattern):
   the body shows --bg-main, and the dashboard shell sits inside it as a
   white rounded card with a soft shadow — 16px margin around the shell,
   24px corner radius. Gives the dashboard a "floated" premium look that's
   the signature of modern admin UIs without changing any layout or flow.
   Mobile collapses to edge-to-edge for thumb reach. */
@media (min-width: 992px) {
  body.amboi-dashboard-app {
    height: 100vh;
    overflow: hidden;
    background: var(--bg-main);
    padding: 16px;
  }
  body.amboi-dashboard-app > .d-flex {
    height: calc(100vh - 32px);
    background: var(--bg-card);
    border-radius: 24px;
    overflow: hidden;
    box-shadow: 0 1px 3px rgba(15, 23, 42, .04), 0 8px 24px rgba(15, 23, 42, .05);
    /* Override the inline min-height: 100vh that's set on the .d-flex in
       layouts/dashboard.blade.php — we want the shell to be the card,
       not the entire viewport. */
    min-height: 0;
  }
  body.amboi-dashboard-app > .d-flex > .amboi-side {
    height: 100%;
    overflow: hidden; /* aside itself doesn't scroll — logo stays pinned */
  }
  /* Logo header locks to its natural height so it never gets squeezed
     when the nav grows tall (all dropdowns expanded). */
  body.amboi-dashboard-app > .d-flex > .amboi-side > .vp-side-brand {
    flex-shrink: 0;
  }
  /* Nav portion takes the remaining height and scrolls internally; the
     `min-height: 0` is required for flex children to allow shrinking
     below their content height. The foot (Sign Out, pinned via margin-top:auto)
     stays at the bottom when content fits, scrolls with content otherwise. */
  body.amboi-dashboard-app > .d-flex > .amboi-side > .vp-side-nav {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
  }
  body.amboi-dashboard-app > .d-flex > .flex-grow-1 {
    height: 100%;
    min-height: 0;
    overflow: hidden;
  }
  body.amboi-dashboard-app > .d-flex > .flex-grow-1 > .vp-page {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
  }
  /* Prevent direct children of the scrollable .vp-page from being flex-shrunk by their
     parent flex column — without this, sections at the bottom collapse to header-only
     when total content exceeds the viewport (instead of the page scrolling). */
  body.amboi-dashboard-app > .d-flex > .flex-grow-1 > .vp-page > * {
    flex-shrink: 0;
  }

  /* ── Fluid (edge-to-edge) dashboard chrome ─────────────────────────
     Admin opt-out for the Card-on-Canvas pattern above. Driven by the
     `.vp-shell-fluid` body class, which Blade adds when the theme
     setting `card_on_canvas` is OFF (see Setting::dashboardCardOnCanvas
     + layouts/dashboard.blade.php). Strips the canvas padding, the
     card's border-radius, and the shadow so the shell fills the
     viewport. Sidebar/topbar/page-scroll behaviour all stay identical —
     only the framing changes. */
  body.amboi-dashboard-app.vp-shell-fluid {
    padding: 0;
    background: var(--bg-card);
  }
  body.amboi-dashboard-app.vp-shell-fluid > .d-flex {
    height: 100vh;
    border-radius: 0;
    box-shadow: none;
  }
}
/* Section rhythm — tuned to --space-9 (36px) on desktop, --space-6 (24px)
   on mobile (see media query below). The earlier --space-16 (64px) value
   left ~128px of dead surface between stacked marketing sections on the
   home page and the become-a-vendor page, forcing the reader to scroll
   past empty space between Summary → How-to-use → Categories → Super-easy
   → Vendor → FAQ. 36 / 24 keeps stacked blocks breathing without
   dominating the scroll, ~44% tighter than the previous spacing. */
/* Section vertical rhythm — use longhand padding-top/padding-bottom so
   the horizontal padding from .container (16px on mobile / 12px desktop)
   is preserved. The earlier shorthand `padding: var(--space-9) 0` set
   horizontal padding to 0, overriding .container's gutter and rendering
   public pages edge-to-edge on mobile. */
.amboi-section { padding-top: var(--space-9); padding-bottom: var(--space-9); }
.amboi-section-eyebrow {
  display: inline-block;
  text-transform: uppercase; letter-spacing: 0.08em;
  font-size: var(--fs-small); font-weight: 700;
  color: var(--text-secondary);
  margin-bottom: var(--space-3);
}

/* ── Inputs ──────────────────────────────────────────────────────────── */
.form-control, .form-select, .vp-input-bare, .input-standard {
  height: 40px;
  padding: 0 var(--space-3);
  font-family: var(--font); font-size: var(--fs-body); color: var(--text-primary);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-input);
  transition: border-color var(--transition), box-shadow var(--transition);
}
textarea.form-control { height: auto; min-height: 80px; padding: var(--space-3); line-height: var(--lh-body); }
.form-control:focus, .form-select:focus, .vp-input-bare:focus, .input-standard:focus {
  outline: 0;
  border-color: var(--accent-500);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb, 143, 224, 0), .25);
}
.form-control::placeholder, .vp-input-bare::placeholder { color: var(--text-secondary); }
.form-label { font-size: var(--fs-small); font-weight: 600; margin-bottom: var(--space-2); color: var(--text-primary); display: block; }

/* Auth-style input with prefix icon */
.vp-input-group { position: relative; display: flex; }
.vp-input-group .vp-input-prefix {
  width: 40px; height: 40px;
  background: var(--bg-subtle);
  border: 1px solid var(--border); border-right: 0;
  border-radius: var(--radius-input) 0 0 var(--radius-input);
  display: grid; place-items: center;
  color: var(--text-secondary);
  flex-shrink: 0;
}
.vp-input-group .vp-input {
  flex: 1; height: 40px; padding: 0 var(--space-3);
  border: 1px solid var(--border);
  border-radius: 0 var(--radius-input) var(--radius-input) 0;
  font-family: var(--font); font-size: var(--fs-body); color: var(--text-primary);
  background: var(--bg-card);
}
.vp-input-group .vp-input:focus {
  outline: 0; border-color: var(--accent-500);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb, 143, 224, 0), .25);
}
.vp-input-group.has-suffix .vp-input { padding-right: 40px; }
.vp-input-group .vp-input-suffix {
  position: absolute; top: 0; right: 0;
  width: 40px; height: 40px;
  border: 0; background: transparent;
  color: var(--text-secondary);
  display: grid; place-items: center;
  cursor: pointer;
  border-radius: 0 var(--radius-input) var(--radius-input) 0;
  transition: color var(--transition);
}
.vp-input-group .vp-input-suffix:hover { color: var(--text-primary); }

.input-group-text { background: var(--bg-subtle); border-color: var(--border); color: var(--text-secondary); border-radius: var(--radius-input) 0 0 var(--radius-input); }

/* ── Icon container (line icons in circular surface) ────────────────── */
.icon-circle, .vp-list-avatar {
  display: inline-grid; place-items: center;
  width: 40px; height: 40px;
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: 50%;
  color: var(--text-primary);
  font-size: 16px;
  flex-shrink: 0;
}
.icon-circle.sm, .vp-list-avatar.sm { width: 32px; height: 32px; font-size: var(--fs-small); }
.icon-circle.lg, .vp-list-avatar.lg { width: 48px; height: 48px; font-size: 20px; }
.icon-circle.dark { background: var(--primary-900); color: #fff; border-color: var(--primary-900); }
/* Adaptive accent icon-circle: both numbers/initials AND icons inherit
   --accent-fg (dark on lime, white on purple). Single source of truth
   so icons + text never split paint. */
.icon-circle.accent { background: var(--accent-500); color: var(--accent-fg, #000); border-color: var(--accent-500); }

/* ── Status pills (calm neutral chrome + small status-coloured dot) ──
   Replaces full-fill coloured pills (which were too loud in lists).
   The pill itself is always a neutral chip; the leading dot signals
   the state — so accent stays inside the strict 10% colour budget. */
.amboi-status-badge, .vp-pill {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  font-size: var(--fs-small); font-weight: 500;
  background: var(--bg-subtle); color: var(--text-primary);
  border: 1px solid var(--border);
  text-transform: capitalize;
  line-height: 1.2;
}
.amboi-status-badge::before, .vp-pill::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--text-secondary);
  flex-shrink: 0;
}
/* Active / positive states → lime dot */
.amboi-status-badge.s-confirmed::before, .amboi-status-badge.s-completed::before,
.amboi-status-badge.s-active::before,    .amboi-status-badge.s-verified::before,
.amboi-status-badge.s-published::before, .amboi-status-badge.s-delivered::before,
.vp-pill.confirmed::before, .vp-pill.completed::before, .vp-pill.active::before,
.vp-pill.verified::before, .vp-pill.published::before, .vp-pill.delivered::before,
.vp-pill.new::before {
  background: var(--accent-500);
}
/* Closed / negative states → navy dot */
.amboi-status-badge.s-cancelled::before, .amboi-status-badge.s-declined::before,
.amboi-status-badge.s-suspended::before, .amboi-status-badge.s-banned::before,
.amboi-status-badge.s-disputed::before,  .amboi-status-badge.s-rejected::before,
.vp-pill.cancelled::before, .vp-pill.declined::before, .vp-pill.suspended::before,
.vp-pill.banned::before, .vp-pill.disputed::before, .vp-pill.rejected::before {
  background: var(--primary-900);
}
/* Featured pill keeps the full lime fill (it's a marketing highlight, not a status). */
.amboi-card-pill.featured::before { content: none; }

/* Status text colour (used inline in tables) */
.vp-text-status-on-going, .vp-text-status-published,
.vp-text-status-active, .vp-text-status-confirmed,
.vp-text-status-completed-ok { color: var(--primary-900); font-weight: 700; }
.vp-text-status-up-coming, .vp-text-status-pending,
.vp-text-status-pending-payment { color: var(--text-secondary); font-weight: 500; }
.vp-text-status-completed, .vp-text-status-paused,
.vp-text-status-draft { color: var(--text-secondary); font-weight: 500; }
.vp-text-status-cancelled, .vp-text-status-declined { color: var(--text-secondary); font-weight: 500; text-decoration: line-through; }

/* ── Public-site navbar ──────────────────────────────────────────────── */
.amboi-navbar {
  background: var(--bg-card);
  border-bottom: 1px solid var(--border);
  padding: var(--space-3) 0;
}
.amboi-navbar .navbar-brand { padding: 0; margin: 0; }
.amboi-navbar .nav-link {
  color: var(--text-primary); font-weight: 500;
  padding: var(--space-2) var(--space-3);
  font-size: var(--fs-body);
  /* Anchor the swipe-in underline pseudo-element below. */
  position: relative;
}
/* Text colour stays at --text-primary in every state — only the
   swipe-in underline below signals hover / active. Keeping the type
   colour neutral reads calmer than two simultaneous accent cues.

   Bootstrap 5 sets .navbar-nav .nav-link.active to its own
   --bs-navbar-active-color (defaults to #000) with specificity (0,3,0)
   which beats our .amboi-navbar .nav-link rule (0,2,0). The override
   below ties specificity and wins by source order so the active link
   inherits --text-primary in BOTH themes — light (black-ish) and dark
   (light grey). Without it, dark mode's active link renders #000 and
   disappears against the dark navbar. */
.amboi-navbar .nav-link.active { color: var(--text-primary); }

/* Animated underline on hover / active — pseudo-element swipes
   left→right matching the Rekaizen brand pattern. The bar sits inside
   the link's text-padding box (left/right pinned to --space-3 so it
   lines up with the text, not the full clickable area). Excluded on
   the avatar dropdown trigger so its hover affordance stays subtle. */
.amboi-navbar .nav-link::after {
  content: '';
  position: absolute;
  left: var(--space-3); right: var(--space-3);
  bottom: 4px;
  height: 2px;
  background: var(--accent-500);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .25s cubic-bezier(.22, 1, .36, 1);
  pointer-events: none;
}
.amboi-navbar .nav-link:hover::after,
.amboi-navbar .nav-link.active::after,
.amboi-navbar .nav-link:focus-visible::after {
  transform: scaleX(1);
}
.amboi-navbar .nav-link[data-bs-toggle="dropdown"]::after {
  display: none; /* dropdown trigger keeps its subtle hover only */
}
@media (prefers-reduced-motion: reduce) {
  .amboi-navbar .nav-link::after { transition: none; }
}
.amboi-navbar .form-control { background: var(--bg-subtle); border-radius: var(--radius-pill); }
.amboi-navbar .input-group .btn { border-radius: 0 var(--radius-pill) var(--radius-pill) 0; }

/* ── Mobile labeled menu (<lg only) ─────────────────────────────────────
   Replaces the prior icon-only stack inside the collapsed navbar with a
   structured menu: identity card → grouped sections (Account, Browse,
   Utilities, Preferences) → sign out. Every row is a 44+ px tap target
   with an icon, label, and (where relevant) badge / check.
   ───────────────────────────────────────────────────────────────────── */
.amboi-mobile-menu { padding: 8px 4px 16px; }
.amboi-mobile-menu-identity {
    display: flex; align-items: center; gap: 12px;
    padding: 12px 14px;
    border-radius: 14px;
    background: var(--bg-subtle);
    margin-bottom: 8px;
}
.amboi-mobile-menu-avatar {
    width: 44px; height: 44px;
    border-radius: 50%;
    overflow: hidden;
    background: #f1f5f9;
    display: inline-flex; align-items: center; justify-content: center;
    flex-shrink: 0;
}
.amboi-mobile-menu-avatar img { width: 100%; height: 100%; object-fit: cover; }
.amboi-mobile-menu-avatar-initials { font-weight: 700; font-size: 14px; color: #475569; }
.amboi-mobile-menu-identity-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.amboi-mobile-menu-identity-text strong { font-size: 14px; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.amboi-mobile-menu-identity-text small { font-size: 12px; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.amboi-mobile-menu-section-label {
    font-size: 11px; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.06em;
    color: var(--text-secondary);
    padding: 16px 12px 6px;
}
.amboi-mobile-menu-list { list-style: none; padding: 0; margin: 0; }
.amboi-mobile-menu-list li { margin: 0; }
.amboi-mobile-menu-list a,
.amboi-mobile-menu-row {
    display: flex; align-items: center; gap: 12px;
    width: 100%;
    padding: 12px;
    border-radius: 10px;
    color: var(--text-primary);
    text-decoration: none;
    background: transparent;
    border: 0;
    font-size: 15px; font-weight: 500;
    text-align: left;
    cursor: pointer;
}
.amboi-mobile-menu-list a:hover,
.amboi-mobile-menu-list a:focus,
.amboi-mobile-menu-list a.is-active,
.amboi-mobile-menu-row:hover,
.amboi-mobile-menu-row:focus {
    background: var(--bg-subtle);
    color: var(--text-primary);
}
.amboi-mobile-menu-list a > i,
.amboi-mobile-menu-row > i {
    font-size: 18px;
    width: 24px;
    text-align: center;
    color: var(--text-secondary);
    flex-shrink: 0;
    line-height: 1;
}
.amboi-mobile-menu-list a > span,
.amboi-mobile-menu-row > span {
    flex-grow: 1;
    min-width: 0;
}
.amboi-mobile-menu-badge {
    background: var(--accent-500);
    color: #fff;
    padding: 2px 9px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    line-height: 1.4;
    margin-left: auto;
}
.amboi-mobile-menu-row--danger { color: #B42318; }
.amboi-mobile-menu-row--danger > i { color: #B42318; }
.amboi-mobile-menu-signout { margin-top: 8px; padding-top: 12px; border-top: 1px solid var(--border); }

/* Theme-toggle row: swap which icon is visible based on the active theme. */
.amboi-mobile-menu-theme-sun { display: none; }
:root[data-theme="dark"] .amboi-mobile-menu-theme-moon { display: none; }
:root[data-theme="dark"] .amboi-mobile-menu-theme-sun { display: inline-block; }

/* Language picker — segmented pair of pills below the theme row. */
.amboi-mobile-menu-locales {
    display: flex; gap: 8px;
    padding: 6px 12px 4px;
}
.amboi-mobile-menu-locale-form { flex: 1; margin: 0; }
.amboi-mobile-menu-locale {
    display: inline-flex; align-items: center; gap: 8px;
    width: 100%;
    padding: 10px 12px;
    border-radius: 10px;
    background: var(--bg-subtle);
    border: 1px solid var(--border);
    color: var(--text-primary);
    font-size: 13px; font-weight: 600;
    cursor: pointer;
    min-height: 44px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.amboi-mobile-menu-locale > span:not(.amboi-mobile-menu-locale-flag) {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
.amboi-mobile-menu-locale.is-active {
    background: rgba(var(--accent-rgb, 81, 180, 190), 0.12);
    border-color: var(--accent-500);
    color: var(--accent-700, var(--accent-500));
}
.amboi-mobile-menu-locale-flag {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 28px;
    padding: 2px 6px;
    border-radius: 6px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    font-size: 10px; font-weight: 800;
    color: var(--text-secondary);
    letter-spacing: 0.04em;
}
.amboi-mobile-menu-locale.is-active .amboi-mobile-menu-locale-flag {
    background: var(--bg-card);
    color: var(--accent-700, var(--accent-500));
    border-color: var(--accent-500);
}

/* ── Hero (mandatory pattern: left image + right dark) ──────────────── */
.amboi-hero {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
  display: grid;
  grid-template-columns: 1fr 1fr;
  min-height: 480px;
}
/* `minmax(0, 1fr)` instead of bare `1fr` so the grid item's min-width
   resolves to 0 instead of its content's intrinsic width. Without it,
   text inside .amboi-hero-content (the h1 + lead paragraph) sets a
   min-content size larger than the container at narrow viewports, and
   the .amboi-hero's overflow:hidden clips the right edge — that's the
   "missing text" the user reported on mobile. minmax(0, 1fr) lets the
   inline text wrap normally inside the available width. */
@media (max-width: 992px) { .amboi-hero { grid-template-columns: minmax(0, 1fr); } }

.amboi-hero-image {
  /* Container for the slide stack. The old "set a background-image
     here" usage is gone — image rendering moved to .amboi-hero-slide
     children so the rotator can crossfade between them. Keeping the
     bg-subtle background so the slot reads as a placeholder while the
     first slide image lazy-loads. */
  position: relative;
  overflow: hidden;
  background-color: var(--bg-subtle);
  min-height: 320px;
}
.amboi-hero-slide {
  /* Each slide fills the parent and crossfades via opacity. The
     1.2 s ease gives a slow, broadcast-quality dissolve that doesn't
     fight with the rest of the marketing-page motion. */
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  opacity: 0;
  transition: opacity 1.2s ease;
  pointer-events: none;
}
.amboi-hero-slide.is-active {
  opacity: 1;
}
.amboi-hero-content {
  background: var(--primary-900);
  color: #fff;
  padding: var(--space-12);
  display: flex; flex-direction: column; justify-content: center;
}
/* Hero H1 uses the display token (52px desktop, 36px mobile) — earns the
   extra weight because it's the first thing every visitor sees. */
.amboi-hero-content h1, .amboi-hero h1 { color: #fff; font-size: var(--fs-display); letter-spacing: -0.025em; line-height: 1.1; }
.amboi-hero-content p, .amboi-hero p { color: rgba(255, 255, 255, 0.8); }
.amboi-hero-content .eyebrow {
  display: inline-block;
  background: var(--accent-500); color: #000;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  font-size: var(--fs-small); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.08em;
  margin-bottom: var(--space-4);
  /* `width: max-content` was forcing the eyebrow's intrinsic content
     width even when the parent was narrower, so the right edge got
     clipped by .amboi-hero's overflow:hidden on mobile. `max-width:
     100%` clamps it to the available width; the text wraps onto a
     second line at narrow viewports (the pill becomes a soft-cornered
     rectangle rather than truncating with no warning). */
  max-width: 100%;
}
.amboi-hero-content .form-control,
.amboi-hero-content .form-select,
.amboi-hero .form-control,
.amboi-hero .form-select {
  background: var(--bg-card); color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-input);
}

/* ── Trust strip ─────────────────────────────────────────────────────── */
.amboi-trust {
  display: flex; flex-wrap: wrap;
  gap: var(--space-4) var(--space-8);
  align-items: center; justify-content: center;
  padding: var(--space-4) var(--space-6);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
}
.amboi-trust .item {
  display: inline-flex; align-items: center; gap: var(--space-2);
  color: var(--text-secondary); font-weight: 500; font-size: var(--fs-body);
}
.amboi-trust .item i { color: var(--text-primary); font-size: 16px; }

/* ── How-it-works steps ──────────────────────────────────────────────── */
.amboi-steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-4); }
@media (max-width: 768px) { .amboi-steps { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px) { .amboi-steps { grid-template-columns: 1fr; } }
.amboi-step .step-no {
  width: 32px; height: 32px;
  display: grid; place-items: center;
  border-radius: var(--radius-pill);
  /* Tertiary (logo orange) instead of accent (brand teal). Step
     numbers are a non-CTA sequential surface — orange breaks up the
     teal-heavy homepage without competing with the primary action.
     `--tertiary-fg` is the adaptive luminance pick from
     readableForeground(); on #E08E50 that resolves to white. */
  background: var(--tertiary-500, var(--accent-500));
  color: var(--tertiary-fg, var(--accent-fg, #fff));
  font-weight: 700; font-size: var(--fs-body);
  margin-bottom: var(--space-3);
}
.amboi-step h6 { font-size: var(--fs-body); font-weight: 700; margin-bottom: var(--space-2); }
.amboi-step p { font-size: var(--fs-small); color: var(--text-secondary); margin: 0; }

/* ───────────────────────────────────────────────────────────────────────
   "How to use" step cards — palette V2 (replaces the v69 orange-fill
   trial). Treatment:
     • Surface  : warm cream — 8% logo-orange mixed into the active card
                  background so it auto-adapts (cream in light mode,
                  warm-dark in dark mode) and brand-tracks any palette.
     • Heading  : brand teal (var(--accent-500)) for emphasis.
     • Body     : default --text-secondary — auto-adapts to light/dark.
                  No force-override needed; the dark-mode .text-muted
                  rule plays nicely here because we WANT theme-aware
                  body copy on these cards now.
     • Bubble   : logo-orange (var(--tertiary-500)) with white digit.
   Revert by deleting this entire block to fall back to the plain white
   .amboi-card treatment.
   ─────────────────────────────────────────────────────────────────────── */
.amboi-step {
    background: #f5f4f1;
    border-color: var(--border);
    color: var(--text-primary);
}
/* Dark-mode falls back to the standard dark card surface — the cream
   hex above is for light mode only (would be near-white-on-dark and
   destroy legibility in dark mode). */
html[data-theme="dark"] .amboi-step {
    background: var(--bg-card);
}
.amboi-step h6 {
    color: var(--accent-500);
}
.amboi-step .step-no {
    background: var(--tertiary-500);
    color: #fff;
}

/* Category icon-card */
.amboi-cat-card {
  display: flex; align-items: flex-start; gap: var(--space-3);
  text-decoration: none; color: var(--text-primary);
  padding: var(--space-4);
}
.amboi-cat-card:hover { color: var(--text-primary); box-shadow: 0 4px 12px rgba(0, 0, 0, .04); }
.amboi-cat-card .icon {
  width: 40px; height: 40px;
  display: grid; place-items: center;
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  color: var(--text-primary);
  border-radius: var(--radius-pill);
  font-size: 20px; flex-shrink: 0;
}
.amboi-cat-card h6 { font-size: var(--fs-body); font-weight: 700; margin: 0 0 var(--space-1); }
.amboi-cat-card small { font-size: var(--fs-small); color: var(--text-secondary); }

/* ── CTA section (dark) ──────────────────────────────────────────────── */
.amboi-cta {
  background: var(--primary-900);
  color: #fff;
  padding: var(--space-12) var(--space-8);
  border-radius: var(--radius-card);
  text-align: center;
}
.amboi-cta h2 { color: #fff; }
.amboi-cta p { color: rgba(255, 255, 255, 0.8); margin-bottom: var(--space-6); }
.amboi-cta .amboi-section-eyebrow { color: rgba(255, 255, 255, 0.7); }
.amboi-cta .btn-secondary, .amboi-cta .btn-outline-light {
  background: transparent !important; color: #fff !important; border-color: rgba(255, 255, 255, 0.3) !important;
}
.amboi-cta .btn-outline-light:hover { background: rgba(255, 255, 255, 0.1) !important; border-color: #fff !important; }

/* .amboi-cta feature-icon contrast fix — global.
   The .text-primary rule lower in this file is semantically "primary
   body-text colour" (resolves to var(--text-primary): #1A1A1A on light,
   white-ish on dark), NOT Bootstrap's brand-primary. On the dark navy
   .amboi-cta card that means in light mode the icons paint near-black
   and disappear (dark squares against dark navy); in dark mode they
   accidentally look fine because text-primary is light. Force brand
   accent (teal) for these icons in BOTH modes so the brand-accent
   intent is honoured regardless of theme. Used by every page that
   renders .amboi-cta (homepage's "Calling All Vendors!", vendor-landing's
   "Ready when you are", future pages). */
.amboi-cta i.text-primary {
    color: var(--accent-500) !important;
    font-size: 18px;
    vertical-align: -2px;
    margin-right: 4px;
}

/* ── Empty state ──────────────────────────────────────────────────────────
   Canonical placeholder for "nothing here yet" surfaces. Always stacked
   and centered (icon → heading → description → button) so multi-line
   contexts (cart, lists, search results) lay out predictably and
   single-line contexts (dashboard widget bottoms) still read as a
   coherent vertical group. The full styling lives at the deduplicated
   block lower in this file — this short stub is kept as a comment marker
   so anyone grep-ing for ".amboi-empty" lands on the explanation. */

/* ── Tables ─────────────────────────────────────────────────────────── */
.table, .vp-table {
  width: 100%;
  background: var(--bg-card);
  border-collapse: separate; border-spacing: 0;
}
.table thead th, .vp-table thead th {
  font-size: var(--fs-small); text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--text-secondary); font-weight: 700;
  background: var(--bg-subtle);
  border-bottom: 1px solid var(--border);
  padding: var(--space-4) var(--space-6);
  text-align: left;
}
.table tbody td, .vp-table tbody td {
  padding: var(--space-4) var(--space-6);
  border-bottom: 1px solid var(--border);
  color: var(--text-primary);
  font-size: var(--fs-body);
  vertical-align: middle;
}
.table tbody tr:last-child td, .vp-table tbody tr:last-child td { border-bottom: 0; }
.vp-table-id { color: var(--primary-900); font-weight: 700; }

/* ─────────────────────────────────────────────────────────────────
   SORTABLE COLUMN HEADERS (x-sort-header component)

   Clickable th that toggles sort direction. Active column shows a
   solid chevron (▼/▲); inactive columns show a faint two-headed
   chevron to advertise that the entire row is sortable. Without
   the inactive-state hint, users only discover sort by accident
   when they hover the right header.
   ───────────────────────────────────────────────────────────────── */
.amboi-sort-th {
    user-select: none;
}
.amboi-sort-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: inherit;
    text-decoration: none;
    cursor: pointer;
    transition: color var(--transition);
    /* Match the surrounding <th>'s baseline so a sortable header doesn't
       sit 1-2px taller than a plain <th> in the same row (inline-flex
       otherwise resets the inline line-height stack). */
    line-height: inherit;
    vertical-align: baseline;
}
.amboi-sort-link > i {
    font-size: 11px;
    opacity: 0.35;
    transition: opacity var(--transition);
}
.amboi-sort-link:hover {
    color: var(--primary-900);
    text-decoration: none;
}
.amboi-sort-link:hover > i {
    opacity: 0.7;
}
.amboi-sort-link.is-active {
    color: var(--primary-900);
}
.amboi-sort-link.is-active > i {
    opacity: 1;
    color: var(--accent-500);
}
.amboi-sort-th.text-end .amboi-sort-link { flex-direction: row-reverse; }

/* "Sort by ..." dropdown for card-grid pages (Favourites, Saved Searches,
   Saved Lists) — same URL convention as the column-header sort but
   surfaced as a labelled select. Lives at the top of the grid surface,
   right-aligned by default via the row utility wrapper. */
.amboi-sort-dropdown {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}
.amboi-sort-dropdown-select {
    min-width: 180px;
}

/* ─────────────────────────────────────────────────────────────────
   BULK SELECT + STICKY ACTION BAR (x-bulk-* components)

   Per-row + select-all checkboxes look like standard form-checks
   but tighten the cell padding so the column doesn't feel bloated.
   The floating action bar pins to the bottom of the viewport, only
   appears when ≥1 row is selected, and centers itself horizontally.
   Matches Gmail / GitHub / Notion "selection toolbar" patterns.
   ───────────────────────────────────────────────────────────────── */
.amboi-bulk-th, .amboi-bulk-td {
    width: 36px;
    padding-left: var(--space-3) !important;
    padding-right: 0 !important;
    /* Pin vertical padding to the table's standard cell rhythm so the
       checkbox column row-height matches every other cell — without
       this it falls back to inherited cascade and can drift when
       multiple stylesheets compete for .vp-table tbody td. */
    padding-top: var(--space-4) !important;
    padding-bottom: var(--space-4) !important;
    text-align: center;
    vertical-align: middle;
}
/* Also align the sortable <th>'s vertical padding — keeps THEAD height
   uniform across mixed sort/non-sort header rows. */
.amboi-sort-th {
    padding-top: var(--space-4) !important;
    padding-bottom: var(--space-4) !important;
}
.amboi-bulk-th input, .amboi-bulk-td input {
    margin: 0;
    cursor: pointer;
}
.amboi-bulk-form {
    /* Reserve room at the bottom so the sticky bar doesn't permanently
       cover the last row of the table or the pagination footer. */
    padding-bottom: 72px;
}
.amboi-bulk-bar {
    position: fixed;
    left: 50%;
    bottom: 24px;
    transform: translateX(-50%) translateY(20px);
    opacity: 0;
    pointer-events: none;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: 10px 14px 10px 18px;
    background: var(--primary-900, #0d1733);
    color: #fff;
    border-radius: 999px;
    box-shadow:
        0 4px 12px rgba(15, 23, 42, .18),
        0 12px 32px rgba(15, 23, 42, .14);
    z-index: 1040;
    transition: opacity 0.15s ease, transform 0.15s ease;
    max-width: calc(100vw - 32px);
    flex-wrap: wrap;
    justify-content: center;
}
.amboi-bulk-bar.is-visible {
    opacity: 1;
    pointer-events: auto;
    transform: translateX(-50%) translateY(0);
}
.amboi-bulk-count {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13.5px;
    font-weight: 600;
    white-space: nowrap;
}
.amboi-bulk-count > i {
    color: var(--accent-500, #8FE000);
    font-size: 14px;
}
.amboi-bulk-count > strong {
    font-size: 14px;
    color: var(--accent-500, #8FE000);
}
/* Across-pages selection banner — surfaced inside the floating bar when
   the master checkbox selects all rows ON THIS PAGE and there are more
   rows on other pages. Same UX pattern as Gmail's "All 50 on this page
   selected. Select all 9,847 in Inbox?" */
.amboi-bulk-banner {
    display: none;
    align-items: center;
    gap: 8px;
    padding-left: var(--space-3);
    border-left: 1px solid rgba(255, 255, 255, .14);
    font-size: 12px;
    color: rgba(255, 255, 255, .75);
    white-space: nowrap;
}
.amboi-bulk-banner.is-visible {
    display: inline-flex;
}
.amboi-bulk-banner.is-active .amboi-bulk-banner-on-page {
    display: none;
}
.amboi-bulk-banner.is-active .amboi-bulk-banner-link {
    text-decoration: line-through;
    opacity: 0.55;
    cursor: default;
    pointer-events: none;
}
.amboi-bulk-banner-link {
    color: var(--accent-500, #8FE000);
    font-weight: 600;
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
}
.amboi-bulk-banner-link:hover {
    color: var(--accent-500, #8FE000);
    text-decoration: none;
}
.amboi-bulk-actions {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border-left: 1px solid rgba(255, 255, 255, .14);
    padding-left: var(--space-3);
}
.amboi-bulk-actions > button,
.amboi-bulk-actions > a {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    height: 32px;
    padding: 0 12px;
    border-radius: 999px;
    border: 1px solid rgba(255, 255, 255, .18);
    background: transparent;
    color: #fff;
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
    transition: background-color var(--transition), border-color var(--transition);
}
.amboi-bulk-actions > button:hover:not(:disabled),
.amboi-bulk-actions > a:hover {
    background: rgba(255, 255, 255, .10);
    border-color: rgba(255, 255, 255, .35);
}
.amboi-bulk-actions > button:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}
.amboi-bulk-actions > button.is-danger {
    background: rgba(239, 68, 68, .15);
    border-color: rgba(239, 68, 68, .35);
}
.amboi-bulk-actions > button.is-danger:hover:not(:disabled) {
    background: rgba(239, 68, 68, .28);
    border-color: rgba(239, 68, 68, .55);
}
.amboi-bulk-clear {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: 0;
    background: rgba(255, 255, 255, .08);
    color: #fff;
    font-size: 12px;
    cursor: pointer;
    transition: background-color var(--transition);
}
.amboi-bulk-clear:hover {
    background: rgba(255, 255, 255, .18);
}

/* ── Tabs ────────────────────────────────────────────────────────────── */
.vp-tabs { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.vp-tab {
  display: inline-flex; align-items: center; gap: var(--space-1);
  height: 32px; padding: 0 var(--space-4);
  border-radius: var(--radius-pill);
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  font-size: var(--fs-small); font-weight: 500;
  text-decoration: none;
  transition: all var(--transition);
}
.vp-tab:hover { color: var(--text-primary); }
.vp-tab.active { background: var(--accent-500); color: #000; border-color: var(--accent-500); }
.vp-tab .vp-tab-count {
  background: rgba(0, 0, 0, .1); border-radius: var(--radius-pill);
  padding: 0 var(--space-2); font-size: var(--fs-small); min-width: 20px; text-align: center;
}

/* Toolbar */
.vp-toolbar { display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: center; justify-content: space-between; }
.vp-search-inline { position: relative; flex: 1; min-width: 220px; max-width: 360px; }
.vp-search-inline input {
  width: 100%; height: 40px; padding: 0 var(--space-3) 0 var(--space-10);
  border: 1px solid var(--border); border-radius: var(--radius-input);
  background: var(--bg-card); color: var(--text-primary); font-size: var(--fs-body);
}
.vp-search-inline input:focus {
  outline: 0; border-color: var(--accent-500);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb, 143, 224, 0), .25);
}
.vp-search-inline i { position: absolute; left: var(--space-3); top: 50%; transform: translateY(-50%); color: var(--text-secondary); }

/* Pagination — both our custom .vp-pager and the Bootstrap .pagination
   (used by Laravel paginators by default) get the same flex-gap so the
   numbered cells read as separate buttons instead of one fused control. */
.vp-pager,
.pagination {
  display: inline-flex; align-items: center; gap: var(--space-2);
}
.pagination { padding-left: 0; margin: 0; list-style: none; }
.vp-pager .vp-page-btn,
.pagination .page-link {
  display: grid; place-items: center;
  width: 32px; height: 32px;
  border: 1px solid var(--border); border-radius: var(--radius-input);
  background: var(--bg-card); color: var(--text-primary); font-weight: 500; font-size: var(--fs-small);
  text-decoration: none;
}
.vp-pager .vp-page-btn:hover, .pagination .page-link:hover { border-color: var(--text-secondary); }
.vp-pager .vp-page-btn.active, .pagination .page-item.active .page-link {
  background: var(--accent-500); color: #000; border-color: var(--accent-500);
}

.vp-entries-select { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--text-secondary); font-size: var(--fs-small); }
.vp-entries-select select { height: 32px; }

/* ── Step indicator (multi-step forms / registration) ─────────────── */
.steps-indicator {
  position: relative;
  display: flex; align-items: center; justify-content: space-between;
  max-width: 360px;
  margin: 0 auto var(--space-8);
  isolation: isolate;        /* contain stacking — halos never bleed up */
}

/* One continuous baseline track behind every circle. The circles sit on
   top of it, so there are no visible seams or doubled lines. */
.steps-indicator::before {
  content: "";
  position: absolute;
  left: 20px; right: 20px;   /* inset by step radius so track stops at edges */
  top: 50%; transform: translateY(-50%);
  height: 2px;
  background: var(--border);
  z-index: 0;
}

.steps-indicator .step {
  width: 40px; height: 40px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--text-secondary);
  color: #fff;
  font-weight: 700;
  font-size: var(--fs-body);
  flex-shrink: 0;
  position: relative; z-index: 2;
  transition: background-color var(--transition), box-shadow var(--transition);
}

.steps-indicator .step.active {
  background: var(--accent-500);
  color: var(--text-primary);
  /* Page-bg ring carves a clean gap from the track, then a soft lime
     glow surrounds it (replaces the previous hard offset ring). */
  box-shadow:
    0 0 0 4px var(--bg-main),
    0 0 14px rgba(var(--accent-rgb, 143, 224, 0), 0.55);
}

.steps-indicator .step.done {
  background: var(--primary-900);
  color: #fff;
}
.steps-indicator .step.done::after {
  content: "\F26E"; /* bi-check */
  font-family: "bootstrap-icons"; font-size: 18px; font-weight: 400;
  position: absolute; inset: 0;
  display: grid; place-items: center;
  background: inherit; border-radius: 50%;
}

/* Cumulative-progress overlay — a navy line drawn on top of the base
   track up to (and only up to) the last completed step. */
.steps-indicator .step-line {
  flex: 1;
  height: 2px;
  background: transparent;   /* base track is the parent's ::before */
  position: relative; z-index: 1;
  transition: background-color var(--transition);
}
.steps-indicator .step-line.done { background: var(--primary-900); }

/* Inline action button (icon-only) */
.vp-btn-icon {
  display: inline-grid; place-items: center;
  width: 32px; height: 32px;
  border: 1px solid var(--border); border-radius: var(--radius-input);
  background: var(--bg-card); color: var(--text-secondary);
  text-decoration: none;
  transition: all var(--transition);
}
.vp-btn-icon:hover { color: var(--text-primary); border-color: var(--text-secondary); }

/* ── Stat hero cards (portal dashboards) ────────────────────────────── */
.vp-stat-grid {
  display: grid; gap: var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.vp-stat-card {
  background: var(--primary-900);
  color: #fff;
  border-radius: var(--radius-card);
  padding: var(--space-6);
  border: 0;
  display: flex; align-items: flex-start; justify-content: space-between;
  text-decoration: none;
  transition: background-color var(--transition);
}
.vp-stat-card:hover { background: var(--primary-700); color: #fff; }
.vp-stat-meta { flex: 1; }
.vp-stat-title { color: rgba(255, 255, 255, 0.7); font-size: var(--fs-small); font-weight: 600; margin-bottom: var(--space-2); letter-spacing: 0.01em; }
.vp-stat-value {
  /* Same responsive treatment as .vp-stat-value-lg, scaled for the
     Operations-grid context (h1 size at full, shrinks for narrower
     cards). Tabular-nums for the same alignment-stability reason. */
  font-size: clamp(24px, 2.6vw, 36px);
  font-weight: 700;
  line-height: 1;
  display: flex;
  align-items: baseline;
  gap: var(--space-1);
  font-variant-numeric: tabular-nums;
  min-width: 0;
  max-width: 100%;
}
.vp-stat-value .rm { font-size: var(--fs-body); font-weight: 500; color: rgba(255, 255, 255, 0.7); }

/* Hero KPI value — Findom / Orifiopin "$10,860.00" treatment. Use on the
   3-6 most important numbers per page (revenue, balance, total bookings,
   etc.) — never auto-applied; opt in per surface. The size-to-label ratio
   is what makes the number feel like *the* headline, not a stat. Pair with
   a small descriptor (e.g. <small class="text-muted">Total Balance</small>)
   for the Findom-style "huge number / tiny label" rhythm. */
.vp-stat-value-lg {
  /* Responsive sizing: card-width varies (col-md-3 in a 4-up grid is ~150px
     usable after the icon; standalone cards get 240px+). clamp() lets the
     number breathe in roomy cards and shrink gracefully in tight ones,
     instead of pushing past the card border at large amounts. */
  font-size: clamp(26px, 3.2vw, 40px);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-1);
  /* tabular-nums keeps all digits the same width — RM 1,234.00 and
     RM 31,517.00 align on every column, no visual jitter as values
     refresh. Essential polish for financial dashboards. */
  font-variant-numeric: tabular-nums;
  /* Allow the value to shrink inside flex parents instead of forcing
     overflow when the card is narrow. */
  min-width: 0;
  max-width: 100%;
}
.vp-stat-value-lg .rm,
.vp-stat-value-lg .currency {
  font-size: 0.55em;
  font-weight: 600;
  color: var(--text-secondary);
  letter-spacing: 0;
}
/* Money wrapper produced by <x-stat-money>.
   Crucially uses `display: contents` so the wrapper introduces NO new
   layout box — the currency span and number text appear as direct
   children of whichever flex container (.vp-stat-value-lg /
   .vp-stat-value) the component is dropped into. Otherwise we get
   nested inline-flex containers, and on narrow cards the inner one
   collapses to column-direction, pushing "RM" onto its own line above
   the number.
   tabular-nums and cursor still inherit cleanly through `display:
   contents` (both are inherited properties) so the visual + UX
   affordances survive the layout collapse. */
.vp-stat-money {
  display: contents;
  font-variant-numeric: tabular-nums;
}
.vp-stat-money[title] { cursor: help; }
.vp-stat-label-sm {
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
@media (max-width: 640px) {
  .vp-stat-value-lg { font-size: 30px; }
}
.vp-stat-sub { color: rgba(255, 255, 255, 0.7); font-size: var(--fs-small); margin-top: var(--space-2); }
.vp-stat-icon {
  width: 40px; height: 40px;
  background: var(--accent-500); color: #000;
  border-radius: var(--radius-input);
  display: grid; place-items: center;
  font-size: 18px;
}

/* Plain stat (light) for admin */
.amboi-stat { padding: var(--space-4); }
.amboi-stat-label { color: var(--text-secondary); font-size: var(--fs-small); letter-spacing: 0.01em; font-weight: 600; }
.amboi-stat-value { font-size: var(--fs-h2); font-weight: 700; margin-top: var(--space-1); color: var(--text-primary); }
.amboi-stat-trend { font-size: var(--fs-small); color: var(--text-secondary); font-weight: 500; }

/* ── Equal-bottom stat grid (Spend snapshot / Wallet overview / etc.) ──
   Opt-in modifier — add `.amboi-stat-grid` to the row that wraps a strip
   of stat tiles to force every caption to align to the SAME baseline at
   the bottom of the tallest sibling. Without this, captions float at
   whatever vertical position the content above them happens to end,
   producing a ragged bottom edge across an otherwise uniform row.

   How it works:
   1. Cols become flex containers (height: 100% via .h-100 stretch)
   2. .amboi-stat inside flexes column-wise to fill the col height
   3. The LAST child (caption / CTA link) gets `margin-top: auto` and
      pushes itself to the bottom — middle content stays anchored at
      the top, the caption locks to the bottom edge.

   Scoped via the parent .amboi-stat-grid so other `.amboi-stat`
   usages across the app (admin overview, dispute table, reviews
   index, etc.) keep their existing intrinsic-height layout. */
.amboi-stat-grid > [class*="col-"] {
    display: flex;
}
.amboi-stat-grid .amboi-stat {
    display: flex;
    flex-direction: column;
    width: 100%;
}
.amboi-stat-grid .amboi-stat > :last-child {
    margin-top: auto;
}

/* ── Portal sidebar + topbar ────────────────────────────────────────── */
.amboi-side {
  background: var(--bg-card);
  border-right: 1px solid var(--border);
  padding: 0;
  display: flex; flex-direction: column;
}
.vp-side-brand {
  min-height: 65px;
  background: var(--bg-card);
  border-bottom: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-3) var(--space-4);
  overflow: hidden;
}
.vp-side-brand .navbar-brand {
  /* Bootstrap's intrinsic .navbar-brand padding/margin clashes with our centered
     flex layout — reset to keep the logo cleanly centered/left-aligned. */
  padding: 0;
  margin: 0;
  max-width: 100%;
}
.vp-side-brand .amboi-logo,
.vp-side-brand .amboi-logo svg {
  /* Clamp width so the SVG never overflows the brand box on narrow viewports.
     The inline `height: 32px; width: auto` from <x-amboi-logo /> sets the
     visible size — we DON'T override it (otherwise the SVG collapses). */
  max-width: 100%;
  display: block;
}
.vp-side-nav { padding: var(--space-4); flex: 1; }
.vp-side-nav .nav-link {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3);
  border-radius: var(--radius-input);
  color: var(--text-secondary) !important;
  font-weight: 500; font-size: var(--fs-body);
  text-decoration: none;
  transition: all var(--transition);
  margin-bottom: var(--space-1);
  border: 0; background: transparent;
  width: 100%; text-align: left;
}
.vp-side-nav .nav-link:hover { background: var(--bg-subtle); color: var(--text-primary) !important; }
.vp-side-nav .nav-link.active {
  background: var(--bg-subtle); color: var(--text-primary) !important;
  font-weight: 700;
  position: relative;
}
.vp-side-nav .nav-link.active::before {
  content: ""; position: absolute; left: 0; top: 8px; bottom: 8px; width: 3px;
  background: var(--accent-500); border-radius: 0 var(--radius-input) var(--radius-input) 0;
}
.vp-side-nav .nav-link i { font-size: 16px; }

/* Section heading inside sidebar nav — small uppercase label that groups
   related links (Operations / Growth / Control / System on the admin side). */
.vp-side-nav .nav-section {
  text-transform: uppercase;
  letter-spacing: .08em;
  font-size: 11px;
  color: var(--text-secondary);
  font-weight: 700;
  padding: var(--space-3) var(--space-3) var(--space-1);
  margin-top: var(--space-2);
  opacity: .65;
}
.vp-side-nav .nav-section:first-child { margin-top: 0; }
.vp-side-sub {
  margin-left: var(--space-6);
  padding-left: var(--space-3);
  border-left: 1px solid var(--border);
}
.vp-side-sub .nav-link { padding: var(--space-2) var(--space-3); font-size: var(--fs-small); }

.vp-side-foot {
  padding: var(--space-4);
  border-top: 1px solid var(--border);
}
.vp-side-foot .signout {
  width: 100%; border: 0; background: transparent;
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3);
  border-radius: var(--radius-input);
  color: var(--text-secondary);
  font-weight: 500;
  transition: all var(--transition);
}
.vp-side-foot .signout:hover { background: var(--bg-subtle); color: var(--text-primary); }

.vp-topbar {
  background: var(--bg-card);
  border-bottom: 1px solid var(--border);
  padding: 0;
  position: sticky; top: 0; z-index: 10;
}
.vp-topbar-inner {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-6);
  width: 100%;
  max-width: 1600px;
  margin-inline: auto;
}
.vp-topbar-actions {
  display: flex; align-items: center; gap: var(--space-3);
  margin-left: auto;
}
.vp-search { flex: 1; max-width: 480px; position: relative; }
.vp-search input {
  width: 100%; height: 40px;
  padding: 0 var(--space-4) 0 var(--space-10);
  border: 1px solid var(--border); border-radius: var(--radius-input);
  background: var(--bg-card); color: var(--text-primary); font-size: var(--fs-body);
}
.vp-search input:focus { outline: 0; border-color: var(--accent-500); box-shadow: 0 0 0 3px rgba(var(--accent-rgb, 143, 224, 0), .25); }
.vp-search input::placeholder { color: var(--text-secondary); }
.vp-search i { position: absolute; left: var(--space-3); top: 50%; transform: translateY(-50%); color: var(--text-secondary); }

.vp-icon-btn {
  background: transparent; border: 1px solid transparent;
  width: 40px; height: 40px;
  border-radius: var(--radius-input);
  display: grid; place-items: center;
  color: var(--text-secondary);
  transition: all var(--transition);
  position: relative;
  text-decoration: none;
}
.vp-icon-btn:hover { background: var(--bg-subtle); color: var(--text-primary); }
.vp-icon-btn .vp-icon-dot {
  position: absolute; top: 6px; right: 6px;
  width: 8px; height: 8px;
  background: var(--accent-500); border: 2px solid var(--bg-card);
  border-radius: 50%;
}

.vp-divider { width: 1px; height: 24px; background: var(--border); }

.vp-avatar {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-input);
  background: transparent; border: 0;
  text-decoration: none;
  color: var(--text-primary);
  transition: background-color var(--transition);
}
.vp-avatar:hover { background: var(--bg-subtle); color: var(--text-primary); }
.vp-avatar-circle {
  width: 32px; height: 32px;
  background: var(--bg-subtle); border: 1px solid var(--border);
  color: var(--text-secondary);
  border-radius: 50%;
  display: grid; place-items: center;
  font-size: var(--fs-small); font-weight: 700;
  overflow: hidden; /* clip child <img> to the circle */
}
.vp-avatar-circle img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
/* Initials placeholder shown by the topbar avatar when the user hasn't
   uploaded a picture yet. Matches the profile-page circle so both
   surfaces look identical in the empty state. */
.vp-avatar-initials {
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.02em;
  color: var(--text-primary);
  text-transform: uppercase;
  line-height: 1;
}
.vp-avatar-meta { line-height: 1.2; text-align: left; }
.vp-avatar-name { font-weight: 500; color: var(--text-primary); font-size: var(--fs-small); }
.vp-avatar-email { color: var(--text-secondary); font-size: var(--fs-small); }

/* Page chrome */
.vp-page { padding: var(--space-6); display: flex; flex-direction: column; gap: var(--space-6); }
.vp-breadcrumb {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--fs-small); color: var(--text-secondary);
}
.vp-breadcrumb .sep { color: var(--text-secondary); opacity: 0.5; }
.vp-breadcrumb .current { color: var(--text-primary); font-weight: 500; }
.vp-breadcrumb a { color: var(--text-secondary); }
.vp-breadcrumb a:hover { color: var(--text-primary); }

/* List rows */
.vp-list-row {
  display: grid; grid-template-columns: 40px 1fr auto; gap: var(--space-3); align-items: center;
  padding: var(--space-4) var(--space-6);
  border-bottom: 1px solid var(--border);
  text-decoration: none; color: var(--text-primary);
}
.vp-list-row:last-child { border-bottom: 0; }
.vp-list-row:hover { background: var(--bg-subtle); color: var(--text-primary); }
.vp-list-title { font-weight: 700; color: var(--text-primary); font-size: var(--fs-body); margin-bottom: 2px; }
.vp-list-meta { color: var(--text-secondary); font-size: var(--fs-small); }

/* Block-layout list row — used by pages whose content needs the full row width
   (e.g. instalments plan card, where a 4-column grid of schedule parts has to
   span the whole surface). The default `.vp-list-row` is a 40-icon / 1fr / auto
   grid which would squash content into a 40px column. */
.vp-instalment-row {
  display: block;
  padding: var(--space-4) var(--space-6);
  border-bottom: 1px solid var(--border);
  color: var(--text-primary);
}
.vp-instalment-row:last-child { border-bottom: 0; }

/* Tabbed listing form — the vendor service edit page mixes field tabs (in one
   outer form) with action tabs (per-row forms posting to dedicated endpoints).
   The per-row forms can't be nested inside the outer form — HTML disallows it,
   the browser auto-closes — so action-tab panes live as direct children of
   `.tab-content`, while field-tab panes are inside the form which itself sits
   inside `.tab-content`. Force-hide every non-active pane so Bootstrap's
   default rule isn't outranked by other styles. */
.tab-content > .tab-pane:not(.active),
.tab-content form > .tab-pane:not(.active) { display: none !important; }

/* Step indicator (vendor progress) */
.vp-progress-steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-3); }
@media (max-width: 768px) { .vp-progress-steps { grid-template-columns: 1fr 1fr; } }
.vp-progress-step .vp-progress-no {
  display: inline-grid; place-items: center; width: 32px; height: 32px;
  border-radius: 50%; background: var(--bg-subtle); color: var(--text-secondary);
  font-weight: 700; font-size: var(--fs-small);
  margin-bottom: var(--space-3); border: 1px solid var(--border);
}
.vp-progress-step.done .vp-progress-no { background: var(--accent-500); color: #000; border-color: var(--accent-500); }
.vp-progress-step.current .vp-progress-no { background: var(--primary-900); color: #fff; border-color: var(--primary-900); }
.vp-progress-step h6 { font-size: var(--fs-body); margin: 0 0 var(--space-1); font-weight: 700; }
.vp-progress-step small { font-size: var(--fs-small); color: var(--text-secondary); }

/* Form helpers */
.vp-form-row { display: grid; gap: var(--space-1); margin-bottom: var(--space-4); }
.vp-form-row label { font-size: var(--fs-small); font-weight: 500; color: var(--text-primary); }
.vp-form-grid { display: grid; gap: var(--space-4); grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 768px) { .vp-form-grid { grid-template-columns: 1fr; } }
.vp-split { display: grid; grid-template-columns: 1fr 320px; gap: var(--space-6); }
@media (max-width: 992px) { .vp-split { grid-template-columns: 1fr; } }

.vp-banner {
  display: flex; gap: var(--space-3);
  background: var(--bg-subtle);
  border: 1px solid var(--border);
}
.vp-banner.lime { background: var(--accent-500); border-color: var(--accent-500); color: #000; }
.vp-banner.lime small { color: #000; }
.vp-banner.navy { background: var(--primary-900); color: #fff; border-color: var(--primary-900); }

.vp-actions { display: inline-flex; gap: var(--space-2); flex-wrap: wrap; }
.vp-verified { display: inline-flex; align-items: center; gap: var(--space-1); color: var(--text-primary); font-weight: 500; font-size: var(--fs-small); }

/* Auth screens */
.vp-auth {
  min-height: 100vh; background: var(--bg-main);
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-8) var(--space-4);
}
.vp-auth-card {
  width: 100%; max-width: 440px;
  margin-left: auto; margin-right: auto;   /* always centered within its column */
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-8);
}
.vp-auth-card a > .amboi-logo,
.vp-auth-card a:has(.amboi-logo) { transition: opacity .15s ease; }
.vp-auth-card a:hover > .amboi-logo,
.vp-auth-card a:hover:has(.amboi-logo) { opacity: .8; }
.vp-auth-title { text-align: center; margin-bottom: var(--space-8); }
.vp-auth-title h1 { font-size: var(--fs-h2); margin-bottom: var(--space-1); }
.vp-auth-title p { font-size: var(--fs-body); color: var(--text-secondary); margin: 0; }
.vp-auth-link { color: var(--text-primary); font-weight: 500; }
.vp-auth-link:hover { color: var(--primary-900); text-decoration: underline; }

/* Footer */
footer {
  background: var(--bg-card) !important;
  border-top: 1px solid var(--border) !important;
  padding: var(--space-8) 0;
}
footer a { color: var(--text-secondary); }
footer a:hover { color: var(--text-primary); }

/* Alerts (Bootstrap) */
.alert {
  border-radius: var(--radius-card);
  border: 1px solid var(--border);
  border-left-width: 4px;
  font-size: var(--fs-body); font-weight: 500;
  padding: var(--space-4) var(--space-6);
  background: var(--bg-subtle);
  color: var(--text-primary);
  margin: 0;
}
.alert-success { background: rgba(var(--accent-rgb, 143, 224, 0), 0.15); border-color: var(--accent-500); color: var(--text-primary); }
.alert-warning { background: rgba(245, 158, 11, 0.08); border-color: #f59e0b; color: var(--text-primary); }
.alert-danger  { background: rgba(220, 53, 69, 0.06);  border-color: #dc3545; color: var(--text-primary); }

/* Breadcrumb */
.breadcrumb { font-size: var(--fs-small); color: var(--text-secondary); margin: 0; }
.breadcrumb a { color: var(--text-secondary); }
.breadcrumb-item.active { color: var(--text-primary); font-weight: 500; }

/* Badge */
.badge { font-weight: 500; padding: var(--space-1) var(--space-2); border-radius: var(--radius-pill); font-size: var(--fs-small); }
.bg-light { background: var(--bg-subtle) !important; color: var(--text-primary) !important; }
.bg-warning { background: var(--bg-subtle) !important; color: var(--text-primary) !important; }
.bg-danger { background: var(--primary-900) !important; color: #fff !important; }
.bg-success { background: var(--accent-500) !important; color: #000 !important; }
.bg-secondary { background: var(--bg-subtle) !important; color: var(--text-secondary) !important; }
.bg-info { background: var(--bg-subtle) !important; color: var(--text-primary) !important; }
.bg-primary { background: var(--accent-500) !important; color: #000 !important; }

.text-warning, .text-danger, .text-info, .text-success { color: var(--text-primary) !important; }
.text-muted { color: var(--text-secondary) !important; }
.text-primary { color: var(--text-primary) !important; }
.text-secondary { color: var(--text-secondary) !important; }
.text-dark { color: var(--text-primary) !important; }

.amboi-rating { color: var(--text-primary); font-weight: 500; }
.amboi-rating i { font-size: var(--fs-small); }

/* Dropdowns */
.dropdown-menu {
  border-radius: var(--radius-input);
  border-color: var(--border);
  padding: var(--space-1);
  font-size: var(--fs-body);
}
.dropdown-item { border-radius: var(--radius-input); padding: var(--space-2) var(--space-3); color: var(--text-primary); }
.dropdown-item:active, .dropdown-item:hover { background: var(--bg-subtle); color: var(--text-primary); }
/* .dropdown-item.active = the *selected* item (e.g. current locale in
   the navbar profile dropdown's Language section). Bootstrap 5 hardcodes
   --bs-dropdown-link-active-bg to its default blue (#0d6efd) on .dropdown-menu,
   ignoring --bs-primary — so the selection painted vivid Bootstrap blue
   even though the app's primary is teal. Force brand accent across all
   states (idle + hover + focus while .active) so the pill stays on-brand
   and the hover rule above can't accidentally regress it to bg-subtle. */
.dropdown-item.active,
.dropdown-item.active:hover,
.dropdown-item.active:focus {
  background-color: var(--accent-500);
  color: var(--accent-fg, #fff);
}

/* Form-check (checkbox) */
.form-check-input:checked { background-color: var(--accent-500); border-color: var(--accent-500); }
.form-check-input:focus { border-color: var(--accent-500); box-shadow: 0 0 0 3px rgba(var(--accent-rgb, 143, 224, 0), .25); }

/* Accordion (FAQ) */
.accordion-item { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-input); }
.accordion-button { background: var(--bg-card); color: var(--text-primary); font-weight: 700; padding: var(--space-4); }
.accordion-button:not(.collapsed) { background: var(--bg-card); color: var(--text-primary); box-shadow: none; }
.accordion-button:focus { box-shadow: 0 0 0 3px rgba(var(--accent-rgb, 143, 224, 0), .25); border-color: var(--accent-500); }

/* ============================================================
   Plan-search (flight/hotel-style trip card)
   ------------------------------------------------------------
   Two variants share the same form partial:
     .plan-search--hero   — overlays the bottom of the home hero
     .plan-search--bar    — sticky filter bar on the search page
   The shell .plan-search-shell pulls the hero variant up so it
   sits half-overlapping the hero block, like Booking.com / KAYAK.
============================================================ */
.plan-search-shell {
  margin-top: calc(var(--space-8) * -1);
  position: relative; z-index: 5;
  padding: 0 var(--space-2);
}
@media (max-width: 992px) {
  .plan-search-shell { margin-top: var(--space-4); }
}

.plan-search {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1.2fr 0.8fr auto auto;
  align-items: stretch;
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: 0 12px 32px rgba(18, 26, 58, 0.10);
  overflow: hidden;
}
.plan-search--hero { background: var(--border); }
.plan-search--bar  { box-shadow: none; }

@media (max-width: 992px) {
  .plan-search { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 480px) {
  .plan-search { grid-template-columns: 1fr; }
}

/* Inline "Clear" cell — lives inside the pill between Guests and the CTA.
   Hidden by default; JS adds .is-visible when any field has a value. */
.plan-search-clear {
  display: none;
  align-items: center; justify-content: center; gap: 6px;
  border: 0;
  background: var(--bg-card);
  color: var(--text-secondary);
  cursor: pointer;
  padding: 0 var(--space-4);
  min-height: 64px;
  font-size: var(--fs-small); font-weight: 600;
  transition: background-color var(--transition), color var(--transition);
}
.plan-search-clear:hover { background: var(--bg-subtle); color: var(--text-primary); }
.plan-search-clear:focus-visible { outline: 2px solid var(--primary-900); outline-offset: -2px; }
.plan-search-clear.is-visible { display: inline-flex; }
.plan-search-clear i { font-size: 14px; }

@media (max-width: 992px) {
  .plan-search-clear.is-visible { grid-column: 1 / -1; min-height: 48px; border-top: 1px solid var(--border); }
  /* Hide the "Clear" text on stacked layouts — icon alone is enough. */
}
/* Tighter compact form on the small screens — keep label visible on desktop. */
@media (max-width: 480px) {
  .plan-search-clear-label { display: inline; }
}

.plan-search-field {
  background: var(--bg-card);
  /* Horizontal padding bumped from --space-4 (16px) to --space-5 (20px) so
     the selected value (e.g. "Selangor") and free-text inputs sit comfortably
     away from the cell border instead of feeling squished against it. */
  padding: var(--space-3) var(--space-5);
  display: flex; flex-direction: column; justify-content: center;
  min-height: 64px;
  transition: background-color var(--transition);
}
.plan-search-field:hover { background: var(--bg-subtle); }
.plan-search-field label {
  font-size: var(--fs-caption);
  font-weight: 700;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 2px;
  display: flex; align-items: center; gap: 6px;
}
.plan-search-field label i { color: var(--primary-900); }
.plan-search-field input,
.plan-search-field select {
  border: 0;
  background: transparent;
  padding: 0;
  font-size: var(--fs-body);
  font-weight: 500;
  color: var(--text-primary);
  outline: none;
  width: 100%;
  appearance: none;
  -webkit-appearance: none;
}
.plan-search-field select {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M1 1l4 4 4-4' stroke='%23121A3A' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right center;
  padding-right: 18px;
}
.plan-search-field input[type="date"]::-webkit-calendar-picker-indicator,
.plan-search-field input[type="time"]::-webkit-calendar-picker-indicator { opacity: .5; cursor: pointer; }

.plan-search-cta {
  border: 0;
  background: var(--primary-900);
  color: #fff;
  padding: 0 var(--space-6);
  font-weight: 700;
  font-size: var(--fs-body);
  display: inline-flex; align-items: center; gap: 8px;
  cursor: pointer;
  transition: background-color var(--transition);
  min-height: 64px;
}
/* Hover lifts the CTA into the brand accent — same pattern as
   .vp-btn-primary:hover. The old `#0c1230` was a hardcoded deep navy
   left over from the legacy lime-classic palette; on any newer theme
   (Rekaizen, Vibrant 5, custom) it read as off-brand "dark blue."
   `--accent-fg` is the luminance-picked foreground from Setting::
   theme() — dark text on bright accents (Rekaizen teal, Lime Classic
   lime), white on dark accents (Vibrant 5 purple, navy custom) — so
   the text label always meets WCAG. Resting state still uses the
   calm `--primary-900` dark ink. */
.plan-search-cta:hover {
  background: var(--accent-500);
  color: var(--accent-fg, #fff);
}
.plan-search-cta i { font-size: 1.1em; }

@media (max-width: 992px) {
  .plan-search-cta {
    grid-column: 1 / -1;
    justify-content: center;
  }
}

/* Sticky bar on the search page */
.plan-search-sticky {
  position: sticky; top: 0; z-index: 100;
  background: var(--bg-main);
  border-bottom: 1px solid var(--border);
  padding: var(--space-3) 0;
}
.plan-search-sticky .plan-search--bar {
  background: var(--border);
  box-shadow: 0 4px 12px rgba(0,0,0,0.04);
}

/* Sort / Save-search toolbar row — desktop = 180 px-wide select; mobile
   trims to 130 px so the Save-search button beside it never wraps to a
   second line at narrow viewports. The wrapping div uses flex-nowrap so
   nothing reflows even at the narrowest tested width. */
.search-toolbar-sort {
  min-width: 180px;
}
@media (max-width: 575.98px) {
  .search-toolbar-sort {
    min-width: 0;
    flex: 1 1 auto;
  }
}

/* ─────────────────────────────────────────────────────────────────────
   Mobile plan-search summary chip + bottom-sheet (xs only).

   At desktop the .plan-search-sticky bar holds 4 fields horizontally in
   ~95 px. At xs the same fields stack vertically into ~385 px,
   covering ~48% of an 800 px-tall viewport BEFORE any result is
   visible. The summary chip below replaces that whole 385 px band with
   a single 64 px tappable row that mirrors the current criteria; tap
   it to open a Bootstrap offcanvas-bottom with the full editable form.
   Same pattern Airbnb / Booking.com / Klook use for their mobile
   search surface.
   ───────────────────────────────────────────────────────────────────── */
.plan-search-mobile-stack {
  position: sticky; top: 0; z-index: 100;
  background: var(--bg-main);
  border-bottom: 1px solid var(--border);
  padding: var(--space-3) var(--space-4);
}
.plan-search-mobile-summary {
  width: 100%;
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  text-align: left;
  cursor: pointer;
  transition: background-color var(--transition), border-color var(--transition);
}
.plan-search-mobile-summary:hover,
.plan-search-mobile-summary:focus {
  background: var(--bg-subtle);
  border-color: var(--accent-500);
  outline: 0;
}
.plan-search-mobile-summary .psm-icon {
  width: 36px; height: 36px;
  display: grid; place-items: center;
  background: var(--accent-500);
  color: var(--accent-fg, #fff);
  border-radius: var(--radius-pill);
  flex-shrink: 0;
  font-size: 16px;
}
.plan-search-mobile-summary .psm-body {
  display: flex; flex-direction: column; gap: 2px;
  flex: 1; min-width: 0;
}
.plan-search-mobile-summary .psm-title {
  color: var(--text-primary);
  font-size: var(--fs-body); font-weight: 700;
  line-height: 1.2;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.plan-search-mobile-summary .psm-meta {
  color: var(--text-secondary);
  font-size: 12px; font-weight: 500;
  line-height: 1.3;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.plan-search-mobile-summary .psm-edit {
  color: var(--text-secondary);
  font-size: 18px;
  flex-shrink: 0;
}

/* Bottom-sheet offcanvas hosting the full plan-search form on mobile.
   Height capped at 80vh so the user can still see the page behind the
   sheet (visual context for what they're filtering). Form inside uses
   variant=hero which renders the fields stacked vertically — the
   layout the sheet's tall-narrow shape was designed for. */
.plan-search-sheet.offcanvas-bottom {
  height: auto;
  max-height: 85vh;
  border-top-left-radius: var(--radius-card);
  border-top-right-radius: var(--radius-card);
}
.plan-search-sheet .offcanvas-body {
  padding: var(--space-4) var(--space-5) var(--space-6);
}
.plan-search-sheet .plan-search {
  display: flex; flex-direction: column; gap: var(--space-3);
  background: transparent; padding: 0; border-radius: 0;
  /* Reset the base `.plan-search` chrome — it ships with a 1px border
     + soft drop-shadow + clipped overflow tuned for the homepage hero
     where the form sits as a floating pill over the burger photo. In
     the bottom-sheet that border + shadow read as a redundant frame
     inside an already-framed sheet, and the overflow:hidden clipped
     the native date-picker affordance. */
  border: 0;
  box-shadow: none;
  overflow: visible;
}
/* Field shell — matches the Filters sheet next door: bold black label
   ABOVE a plain Bootstrap-style input, no card chrome around each row.
   Earlier (v58) draft used card-style fields with uppercase labels
   inside — that gave the sheet a different visual language from the
   Filters sheet, which the user flagged as design discontinuity. */
.plan-search-sheet .plan-search-field {
  background: transparent;
  border: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.plan-search-sheet .plan-search-field label {
  font-size: var(--fs-small);
  font-weight: 700;
  color: var(--text-primary);
  text-transform: none;
  letter-spacing: 0;
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.plan-search-sheet .plan-search-field label i {
  color: var(--text-secondary);
  font-size: 14px;
}
/* Inputs styled as standard Bootstrap form-control / form-select clones
   — same border, radius, padding as the Filters sheet's Keyword input
   so the two sheets read as one design family. */
.plan-search-sheet .plan-search-field input,
.plan-search-sheet .plan-search-field select {
  width: 100%;
  background: var(--bg-card);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-input);
  padding: 10px 12px;
  font-size: var(--fs-body);
  font-weight: 500;
  line-height: 1.4;
  appearance: auto;
}
.plan-search-sheet .plan-search-field input:focus,
.plan-search-sheet .plan-search-field select:focus {
  outline: 0;
  border-color: var(--accent-500);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb, 81, 180, 190), 0.18);
}
/* Submit button — promoted to brand accent so it matches the
   Filters sheet's "Apply filters" button, not the legacy dark-ink CTA
   the desktop hero variant uses. */
.plan-search-sheet .plan-search-cta {
  margin-top: var(--space-3);
  width: 100%;
  min-height: 48px;
  background: var(--accent-500) !important;
  color: var(--accent-fg, #fff) !important;
  border: 0;
  font-size: var(--fs-body); font-weight: 700;
  border-radius: var(--radius-input);
}
.plan-search-sheet .plan-search-cta:hover {
  background: var(--accent-600) !important;
}
.plan-search-sheet .plan-search-clear {
  align-self: flex-end;
  background: transparent; border: 0;
  color: var(--text-secondary);
  font-size: var(--fs-small); font-weight: 600;
  padding: var(--space-2);
  cursor: pointer;
}
.plan-search-sheet .plan-search-clear:hover { color: var(--accent-500); }

/* ============================================================
   Checkout progress indicator (re-uses .step pattern in spirit)
============================================================ */
.checkout-progress {
  display: flex; align-items: center; justify-content: center;
  gap: 8px;
  padding: var(--space-4) 0;
}
.checkout-progress .step {
  width: 36px; height: 36px;
  border-radius: 999px;
  background: var(--bg-card);
  border: 2px solid var(--border);
  color: var(--text-secondary);
  font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center;
  position: relative;
  font-size: 14px;
}
.checkout-progress .step small {
  position: absolute; top: 100%; margin-top: 6px;
  font-size: var(--fs-caption);
  color: var(--text-secondary);
  text-transform: uppercase; letter-spacing: 0.04em; font-weight: 600;
  white-space: nowrap;
}
.checkout-progress .step.active {
  background: var(--primary-900); color: #fff; border-color: var(--primary-900);
}
.checkout-progress .step.done {
  background: var(--accent-500); color: var(--primary-900); border-color: var(--accent-500);
}
.checkout-progress .step-line {
  flex: 0 0 80px; height: 2px; background: var(--border);
}
.checkout-progress .step-line.done { background: var(--accent-500); }

/* ============================================================
   To-Do list card (shared customer + vendor)
============================================================ */
.todo-list { list-style: none; padding: 0; margin: 0; }
.todo-item {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-2);
  border-bottom: 1px solid var(--border);
}
.todo-item:last-child { border-bottom: 0; }
.todo-item.done .todo-title { text-decoration: line-through; color: var(--text-secondary); }
.todo-item.overdue .todo-title { color: #dc3545; }
.todo-title { font-weight: 600; font-size: var(--fs-body); }
.todo-meta { color: var(--text-secondary); }
.todo-priority {
  display: inline-block; padding: 1px 8px; border-radius: var(--radius-pill);
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
  background: var(--bg-subtle); color: var(--text-secondary);
}
.todo-priority.priority-high { background: rgba(220, 53, 69, 0.12); color: #b02a37; }
.todo-priority.priority-normal { background: rgba(var(--accent-rgb, 143, 224, 0), 0.18); color: var(--primary-900); }
.todo-priority.priority-low { background: rgba(108, 117, 125, 0.12); color: var(--text-secondary); }

/* ============================================================
   Confirm modal (used by Duplicate / Delete actions)
============================================================ */
.confirm-modal-icon {
  width: 48px; height: 48px;
  border-radius: 999px;
  display: grid; place-items: center;
  font-size: 22px;
  flex-shrink: 0;
}
.confirm-modal-icon.danger { background: rgba(220, 53, 69, 0.12); color: #b02a37; }
.confirm-modal-icon.primary { background: rgba(var(--accent-rgb, 143, 224, 0), 0.18); color: var(--primary-900); }

/* ============================================================
   Row action kebab menu
============================================================ */
.vp-row-actions {
  display: inline-flex; align-items: center;
}
.vp-row-actions .dropdown-toggle::after { display: none; }
.vp-row-actions .vp-btn-icon { background: transparent; border: 0; }
/* z-index above the table's overflow:hidden sibling and the sticky
   bulk action bar (1040) so the menu always floats on top. */
.vp-row-actions .dropdown-menu { min-width: 180px; z-index: 1050; }

/* Dark-mode polish for the kebab dropdown ─────────────────────────
   The base Bootstrap dropdown rules + the global `data-theme="dark"
   .dropdown-menu` block at the bottom of this file already swap the
   panel surface, but the per-item hover + danger-item rules use
   hard-coded RGB which loses contrast on dark surface. These rules
   restore it. */
/* Hardcoded-hex pills that fail contrast in dark mode — the deep
   indigo / deep amber chosen for light backgrounds disappears against
   the dark page bg. Lift to lighter mid-tones so the chip+text read.
   Background + border washes get a slight bump too so the pill keeps
   visible chrome (12% → 22% alpha). */
html[data-theme="dark"] .amboi-card-pill.top-rated {
    background: rgba(99, 102, 241, 0.22);
    color: #c7d2fe;
    border-color: rgba(165, 180, 252, 0.5);
}
html[data-theme="dark"] .amboi-card-pill.top-vendor {
    background: rgba(245, 158, 11, 0.22);
    color: #fcd34d;
    border-color: rgba(252, 211, 77, 0.5);
}

/* Neutral response pill ("Replies in ~1 day" etc.). Light mode uses
   `rgba(18,26,58,.06)` bg + `var(--primary-900)` text — both collapse
   to near-invisible in dark mode (dark text on a barely-tinted dark
   wash). Swap to a paler neutral wash + page text colour so the pill
   stays low-key but readable. Kept distinct from .is-fast (accent-
   tinted) so speed tiers still parse at a glance. */
html[data-theme="dark"] .amboi-response-pill.is-normal {
    background: rgba(255, 255, 255, 0.06);
    color: var(--text-primary);
    border-color: rgba(255, 255, 255, 0.18);
}

html[data-theme="dark"] .vp-row-actions .dropdown-item {
    color: var(--text-primary);
}
html[data-theme="dark"] .vp-row-actions .dropdown-item:hover,
html[data-theme="dark"] .vp-row-actions .dropdown-item:focus {
    background: var(--bg-subtle);
    color: var(--text-primary);
}
html[data-theme="dark"] .vp-row-actions .dropdown-item.text-danger {
    color: #ff6b6b !important;
}
html[data-theme="dark"] .vp-row-actions .dropdown-item.text-danger:hover {
    background: rgba(255, 107, 107, 0.12);
    color: #ff8585 !important;
}
html[data-theme="dark"] .vp-row-actions .dropdown-divider {
    border-top-color: var(--border);
}

/* Mobile / narrow-viewport polish for the kebab dropdown ──────────
   At <576px the dropdown menu can otherwise clip off the right edge
   when the kebab sits at the far-right of a table row. Constrain the
   max-width and let Popper's fixed strategy do the rest. */
@media (max-width: 575.98px) {
    .vp-row-actions .dropdown-menu {
        max-width: calc(100vw - 24px);
    }
}

/* Bulk action bar — give the floating chip extra breathing room on
   narrow viewports so it doesn't collide with the pagination footer
   or sit under the iOS Safari tab bar. */
@media (max-width: 575.98px) {
    .amboi-bulk-bar {
        bottom: 12px;
        padding: 8px 12px 8px 14px;
        gap: var(--space-2);
        font-size: 13px;
    }
    .amboi-bulk-bar .amboi-bulk-banner {
        display: none; /* across-pages banner hides on phones — long copy */
    }
    .amboi-bulk-form {
        padding-bottom: 88px; /* extra room so the bar can't cover pager */
    }
}
.vp-row-actions .dropdown-item {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px; font-size: var(--fs-body);
}
.vp-row-actions .dropdown-item.text-danger:hover { background: rgba(220, 53, 69, 0.08); color: #b02a37 !important; }

/* ============================================================
   Profile media (avatar + banner uploaders)
============================================================ */
.profile-banner {
  position: relative;
  height: 180px;
  background: var(--bg-subtle);
  background-size: cover; background-position: center;
  border-radius: var(--radius-card);
  overflow: hidden;
}
.profile-banner-overlay {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent 0%, rgba(18, 26, 58, 0.55) 100%);
}
/* Localised dark scrim behind the action pills only — keeps the photo readable
   while guaranteeing the pills sit on a darker patch even over bright photos. */
.profile-banner-scrim {
  position: absolute; right: 0; top: 0;
  width: 280px; height: 88px;
  background: linear-gradient(225deg, rgba(0,0,0,0.45) 0%, rgba(0,0,0,0) 70%);
  pointer-events: none;
  z-index: 0;
}
.profile-banner-actions {
  position: absolute; right: var(--space-4); top: var(--space-4);
  display: flex; gap: 8px;
  z-index: 1;
}
/* Solid white pill — full opacity + shadow guarantees contrast over any photo.
   Used for the "Change" / "Remove" actions on the banner.
   Text colour is HARD-CODED dark (not var(--text-primary)) because the bg is
   always fixed white — switching to dark mode would otherwise paint white text
   on white pill (invisible). */
.banner-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 12px;
  background: #fff; color: #1A1A1A;
  border: 1px solid rgba(0,0,0,0.06);
  border-radius: 999px;
  font-size: var(--fs-small); font-weight: 600; line-height: 1;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0,0,0,0.18);
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}
.banner-pill:hover {
  background: #f8fafc; color: #1A1A1A;
  box-shadow: 0 3px 12px rgba(0,0,0,0.22);
  transform: translateY(-1px);
}
.banner-pill i { font-size: 14px; line-height: 1; }
.banner-pill.danger { color: #dc2626; }
.banner-pill.danger:hover { background: #fef2f2; color: #b91c1c; }

/* Empty state — clean drop-zone CTA when no banner uploaded yet */
.profile-banner-empty {
  background: var(--bg-subtle);
  border: 1.5px dashed var(--border);
}
.profile-banner-empty-form,
.profile-banner-empty-cta {
  position: absolute; inset: 0;
}
.profile-banner-empty-cta {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px;
  cursor: pointer;
  color: var(--text-secondary);
  text-align: center;
  padding: var(--space-3);
  transition: background 0.15s ease, color 0.15s ease;
}
.profile-banner-empty-cta:hover {
  background: rgba(166, 230, 56, 0.06); /* faint accent tint */
  color: var(--text-primary);
}
.profile-banner-empty-cta i { font-size: 28px; color: var(--accent-600); }
.profile-banner-empty-title { font-weight: 700; font-size: var(--fs-body); color: var(--text-primary); }
.profile-banner-empty-hint  { font-size: var(--fs-small); color: var(--text-secondary); }
.profile-avatar {
  width: 96px; height: 96px;
  border-radius: 50%;
  background: var(--bg-card);
  border: 4px solid var(--bg-card);
  background-size: cover; background-position: center;
  /* `overflow: visible` so the camera edit button isn't clipped by the circular avatar.
     The avatar background image is still clipped to the circle by border-radius;
     `<img>` children get an explicit border-radius below for the same reason. */
  overflow: visible;
  display: grid; place-items: center;
  margin-top: -48px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.08);
  position: relative;
}
.profile-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
/* Initials placeholder for the empty state — paired with the topbar's
   .vp-avatar-initials so both surfaces share the same visual identity
   before the user uploads a picture. */
.profile-avatar-initials {
  font-size: 32px;
  font-weight: 700;
  color: var(--text-secondary);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  line-height: 1;
}
.profile-avatar-edit {
  position: absolute; right: -4px; bottom: -4px;
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--primary-900); color: #fff;
  display: grid; place-items: center;
  border: 2px solid #fff;
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(0,0,0,0.15);
  z-index: 2;
}
.profile-avatar-edit:hover { background: var(--primary-700); }
.upload-input { display: none; }

/* ============================================================
   Vendor gallery grid
============================================================ */
.gallery-grid {
  display: grid; gap: var(--space-3);
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
}
.gallery-tile {
  position: relative;
  aspect-ratio: 1;
  border-radius: var(--radius-card);
  overflow: hidden;
  background: var(--bg-subtle);
}
.gallery-tile img, .gallery-tile video {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.gallery-tile-badge {
  position: absolute; top: 6px; left: 6px;
  background: rgba(0,0,0,0.6); color: #fff;
  padding: 2px 8px; border-radius: 999px;
  font-size: 10px; font-weight: 700; text-transform: uppercase;
}
.gallery-tile-actions {
  position: absolute; top: 6px; right: 6px;
  opacity: 0; transition: opacity .15s;
}
.gallery-tile:hover .gallery-tile-actions { opacity: 1; }
.gallery-add {
  border: 2px dashed var(--border);
  display: grid; place-items: center;
  cursor: pointer;
  color: var(--text-secondary);
}
.gallery-add:hover { border-color: var(--accent-500); color: var(--primary-900); }

/* ============================================================
   Event Wizard (5-step) — hi-fed
============================================================ */
/* Event wizard stepper — visually aligned with the booking-next-steps stepper
   (resources/views/partials/booking-next-steps.blade.php) so the customer sees
   one consistent progress visual across the platform: lime accent for done +
   current, muted grey for upcoming, glow on the current step. */
.event-wizard-steps {
  display: flex; align-items: flex-start; justify-content: center;
  gap: 0; margin: var(--space-6) 0 var(--space-8);
  flex-wrap: nowrap; overflow-x: auto;
}
.event-wizard-steps .ews-step {
  display: inline-flex; flex-direction: column; align-items: center; gap: 8px;
  flex: 0 0 auto; min-width: 110px;
}
.event-wizard-steps .ews-marker {
  width: 36px; height: 36px;
  border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: #fff;
  border: 2px solid #E5E7EB;
  color: #9CA3AF;
  font-size: 16px; line-height: 1;
  transition: background .15s ease, color .15s ease, box-shadow .15s ease;
}
/* Done — filled lime, dark icon (checkmark substituted in markup). */
.event-wizard-steps .ews-step.done .ews-marker {
  background: var(--accent-500);
  border-color: var(--accent-500);
  color: var(--primary-900, #1A1A1A);
}
/* Current — white centre, lime ring + soft glow to draw the eye. */
.event-wizard-steps .ews-step.active .ews-marker {
  background: #fff;
  border-color: var(--accent-500);
  color: var(--primary-900, #1A1A1A);
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb, 143, 224, 0), .18);
}
.event-wizard-steps .ews-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-weight: 700;
  color: #9CA3AF;
  white-space: nowrap;
}
.event-wizard-steps .ews-step.done .ews-label,
.event-wizard-steps .ews-step.active .ews-label {
  color: var(--text-primary, #1A1A1A);
}
.event-wizard-steps .ews-line {
  flex: 1 1 auto;
  height: 2px; min-width: 40px;
  background: #E5E7EB;
  /* Align horizontally with the marker centre (marker height 36 / 2 - line 1 = 17px). */
  align-self: flex-start;
  margin-top: 17px;
  /* Tuck the line under the markers so the joined edges look continuous. */
  margin-left: -2px; margin-right: -2px;
  transition: background .15s ease;
}
.event-wizard-steps .ews-line.done { background: var(--accent-500); }
@media (max-width: 540px) {
  .event-wizard-steps .ews-step { min-width: 84px; }
  .event-wizard-steps .ews-label { font-size: 10px; }
}

.criteria-parents .criteria-parent {
  width: 100%;
  display: flex; align-items: center; gap: 8px;
  padding: 12px 16px;
  border: 0; background: transparent;
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-body);
  text-align: left;
  color: var(--text-primary);
  cursor: pointer;
  transition: background-color var(--transition);
}
.criteria-parents .criteria-parent:hover { background: var(--bg-subtle); }
.criteria-parents .criteria-parent[data-active="1"] {
  background: var(--accent-500);
  color: var(--primary-900);
  font-weight: 700;
}
.criteria-parents .criteria-parent[data-active="1"] .criteria-count { background: #fff !important; color: var(--primary-900) !important; }
.criteria-children { display: none; }
.criteria-children.active { display: block; }
.criteria-count { min-width: 22px; }

/* ============================================================
   Header notifications dropdown (.nav-bell)
   + Language switcher (.lang-switcher)
============================================================ */
/* The bell now uses .vp-icon-btn directly as the dropdown trigger; the
   `.nav-bell` wrapper just exists as the dropdown anchor. Unread state
   is shown via the small `.vp-icon-dot` indicator, not a numeric pill. */
.nav-bell { display: inline-flex; }
.nav-bell .vp-icon-btn::after { display: none; } /* suppress Bootstrap dropdown caret */
.notification-panel {
  width: 360px; max-width: 92vw;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
}
.notification-list {
  max-height: 360px;
  overflow-y: auto;
  padding: 4px 0;
}
.notification-item {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 10px 14px;
  text-decoration: none;
  color: var(--text-primary);
  border-left: 3px solid transparent;
  transition: background .15s;
}
.notification-item:hover { background: var(--bg-subtle); color: var(--text-primary); }
.notification-item.unread { background: var(--bg-subtle); border-left-color: var(--accent-500); }
.notification-item.read { opacity: .75; }
.notification-item i { color: var(--primary-900); margin-top: 3px; }
.notification-title { font-weight: 600; font-size: var(--fs-body); }
.notification-body { line-height: 1.35; }
.notification-time { margin-top: 2px; }

.lang-switcher .btn-link { color: var(--text-secondary); text-decoration: none; font-size: var(--fs-body); }
.lang-switcher .btn-link:hover { color: var(--primary-900); }
.lang-current { font-weight: 600; }
.lang-switcher .dropdown-item.active {
  background: var(--bg-subtle); color: var(--text-primary); font-weight: 600;
}

/* ============================================================
   Legal documents (Terms / Privacy / Vendor Agreement)
   ------------------------------------------------------------
   Reading-friendly screen layout + print stylesheet so users can
   "Print → Save as PDF" and get a clean, paginated document.
============================================================ */
.legal-shell { max-width: 880px; }

.legal-tabs {
  display: inline-flex;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px;
  gap: 2px;
}
.legal-tab {
  padding: 6px 16px;
  border-radius: 999px;
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--text-secondary);
  text-decoration: none;
  white-space: nowrap;
}
.legal-tab:hover { color: var(--text-primary); background: var(--bg-subtle); }
.legal-tab.active { background: var(--primary-900); color: #fff; }
@media (max-width: 640px) {
  .legal-tabs { flex-wrap: wrap; border-radius: var(--radius-card); }
}

.legal-document {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-12);
  font-size: 15px;
  line-height: 1.7;
  color: var(--text-primary);
}
.legal-header { margin-bottom: var(--space-8); padding-bottom: var(--space-4); border-bottom: 2px solid var(--primary-900); }
.legal-header h1 { font-size: 32px; font-weight: 700; margin: 0 0 var(--space-2) 0; color: var(--primary-900); }
.legal-meta { font-size: var(--fs-caption); margin: 0; color: var(--text-secondary); }
.legal-meta .badge { font-weight: 600; }

.legal-section { margin-bottom: var(--space-8); }
.legal-section h2 {
  font-size: 22px; font-weight: 700;
  margin: var(--space-8) 0 var(--space-3); color: var(--primary-900);
  scroll-margin-top: 80px;
}
.legal-section h3 {
  font-size: 17px; font-weight: 700;
  margin: var(--space-4) 0 var(--space-2);
  color: var(--text-primary);
}
.legal-section p { margin-bottom: var(--space-3); }
.legal-section .lead { font-size: 17px; color: var(--text-primary); }

.legal-list {
  margin: var(--space-2) 0 var(--space-3) 0;
  padding-left: var(--space-6);
}
.legal-list li { margin-bottom: 6px; }

.legal-table {
  width: 100%;
  border-collapse: collapse;
  margin: var(--space-3) 0;
  font-size: 14px;
}
.legal-table th, .legal-table td {
  border: 1px solid var(--border);
  padding: 10px 12px;
  text-align: left;
  vertical-align: top;
}
.legal-table th { background: var(--bg-subtle); font-weight: 700; }

.legal-acknowledgement {
  background: var(--bg-subtle);
  border-left: 4px solid var(--accent-500);
  padding: var(--space-3) var(--space-4);
  margin-top: var(--space-4);
  font-style: italic;
}

.legal-doc-footer { margin-top: var(--space-8); }

.legal-print-header { display: none; }

/* ============================================================
   Print stylesheet — used by "Print / Save as PDF" button
============================================================ */
@media print {
  @page { size: A4; margin: 18mm 14mm 18mm 14mm; }

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

  /* Hide everything except the document */
  .amboi-navbar, .amboi-shell > footer, footer.mt-5,
  .legal-toolbar, .no-print, nav.amboi-navbar { display: none !important; }

  .legal-shell { max-width: none; padding: 0 !important; }
  main { padding: 0 !important; margin: 0 !important; }

  /* Print-only letterhead */
  .print-only, .legal-print-header { display: flex !important; }
  .legal-print-header {
    justify-content: space-between; align-items: flex-end;
    border-bottom: 2px solid #000; padding-bottom: 6mm; margin-bottom: 6mm;
  }
  .legal-print-brand strong { font-size: 14pt; }

  /* Document styling for print */
  .legal-document {
    border: 0 !important;
    background: #fff !important;
    border-radius: 0 !important;
    padding: 0 !important;
    box-shadow: none !important;
  }
  .legal-header { border-bottom: 1px solid #000 !important; }
  .legal-header h1 { font-size: 20pt !important; color: #000 !important; }
  .legal-meta { font-size: 9pt; color: #555 !important; }
  .legal-meta .badge { background: #eee !important; color: #000 !important; border: 1px solid #999; }

  .legal-section h2 {
    font-size: 13pt !important; color: #000 !important;
    page-break-after: avoid; break-after: avoid;
  }
  .legal-section h3 { font-size: 11pt !important; color: #000 !important; page-break-after: avoid; }
  .legal-section { page-break-inside: avoid; break-inside: avoid; }
  .legal-list li { page-break-inside: avoid; }

  .legal-table { font-size: 10pt; }
  .legal-table th { background: #eee !important; }

  .legal-acknowledgement { background: #f7f7f7 !important; border-left: 3px solid #000; }

  a { color: #000 !important; text-decoration: none !important; }
  a[href^="http"]::after, a[href^="mailto:"]::after {
    content: " (" attr(href) ")";
    font-size: 9pt; color: #666;
  }
  /* Don't show URL after internal anchors / route() URLs */
  a[href^="/"]::after, a[href^="#"]::after { content: ""; }
}
.print-only { display: none; }

/* ============================================================
   Responsive overrides (existing)
============================================================ */
@media (max-width: 992px) {
  .amboi-side { min-height: auto; border-right: 0; border-bottom: 1px solid var(--border); }
}

/* Mobile sidebar (Bootstrap offcanvas) — slides in when the topbar hamburger is tapped. */
.amboi-side-mobile {
  --bs-offcanvas-width: 256px;
}
.amboi-side-mobile .offcanvas-close-btn {
  position: absolute;
  top: 18px;
  right: 14px;
  z-index: 2;
  background-color: transparent;
}
.amboi-side-mobile .vp-side-brand {
  justify-content: flex-start;
  /* Reserve space on the right for the absolutely-positioned close button (×)
     so the logo never collides with it, even on the narrowest viewport. */
  padding-right: 56px;
}
/* ─────────────────────────────────────────────────────────────────────────
   Card tightening at xs (< 576 px) — 2-up grid mode.

   Cards were designed for col-lg-3 (4-up desktop) and col-sm-6 (2-up
   tablet, where each card is ~280-350 px wide). With the v55 change to
   `col-6` at xs, mobile cards drop to ~180-190 px wide — too narrow
   for the full sextet of signals (breadcrumb + title + 2-3 compliance
   chips + response-time pill + price + rating-with-counts) to coexist
   without truncation or ugly wrap. Strategy here is buying-priority:
   show what drives the click, hide what the detail page covers.

   Mobile keeps: cover image + corner pills (Sponsored/Top-rated/Instant
   /Top vendor), category·vendor breadcrumb (single-line truncate),
   service or vendor title (2-line clamp instead of mid-word ellipsis),
   FIRST compliance chip (typically "Muslim-friendly" — the primary
   halal signal), price, and the headline ★ rating number.

   Mobile drops: 2nd + 3rd compliance chips (Alcohol-free, SST 8% — both
   on the service detail page), the (count) tail on the rating, the
   "· X booked" social-proof suffix, and the response-time pill on
   vendor cards (vendor profile page surfaces this).

   Everything reverts to the original layout at >= 576 px.
   ───────────────────────────────────────────────────────────────────── */
@media (max-width: 575.98px) {
  /* Title — switch from 1-line truncate (Bootstrap utility) to 2-line
     clamp. Two lines fits ~40 chars at this width, which clears the
     mid-word cuts seen on "Wedding Doorgi…" and "Suara Emas Live…". */
  .amboi-card-title.text-truncate {
    white-space: normal;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    max-height: 2.6em;
  }
  /* Breadcrumb — keep visible but cap to a single line with ellipsis so
     long "Photographers · Selera Nusantara Catering" stops wrapping to
     three lines and pushing the title down. */
  .amboi-card-meta {
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* Compliance chips — show only the first (Muslim-friendly when set),
     drop subsequent chips. Tooltip on the surviving chip still lists
     the full amenity set on long-press. */
  .amboi-compliance-row > :nth-child(n+2) {
    display: none;
  }
  /* Rating row — drop the parenthetical "(40)" review count on vendor
     cards and the "· 7 booked" social-proof tail on service cards. The
     ★ + headline number alone is what drives scan-decision at 2-up. */
  .amboi-rating .text-muted {
    display: none;
  }
  /* Response-time pill — eats a full vertical row by itself; replyed-in
     time is captured on the vendor profile page already. */
  .amboi-response-pill {
    display: none;
  }

  /* ──────── Card structure at xs (vs. the desktop card shrunk down) ────────
     v57 hid the noise; v58 reshapes the card's vertical rhythm so it
     stops looking like a "shrunken desktop card" and reads as native
     mobile. Specifically: shorter cover (less dominant when the image
     fails / is the placeholder), tighter body padding, smaller title,
     compact compliance chip, and a stacked price/rating row so neither
     signal has to wrap or fight for horizontal space. */

  /* Cover — 16:10 instead of 4:3. Cover area drops from ~135px to ~112px
     at a 180px-wide card, shifting more of the card surface to body
     content where the buying signals live. Also kills the awkward
     near-square shape that empty placeholder covers fell into. */
  .amboi-card-img {
    aspect-ratio: 16 / 10;
  }

  /* Body — tighter all-around padding so the cards don't feel like
     they have desktop chrome at mobile dimensions. */
  .amboi-card-body {
    padding: 12px 12px 14px;
  }

  /* Title — slightly smaller + tighter leading so a 2-line wrap costs
     ~36px of height instead of ~42px. Combined with the cover trim,
     two cards now fit a typical phone-portrait viewport above the
     fold for the first time. */
  .amboi-card-title {
    font-size: 14px;
    line-height: 1.3;
  }

  /* Compliance chip — drop one notch from "feels like a CTA" to
     "feels like a tag." Padding goes 2px·8px → 2px·6px, font 11px → 10.5px. */
  .amboi-compliance-row .amboi-chip {
    padding: 2px 6px !important;
    font-size: 10.5px !important;
    gap: 3px;
  }

  /* Price + rating row — desktop's horizontal split (price ← → rating)
     forces "MYR 9,500 – 15,200" to wrap mid-range at this width.
     Stacking vertically gives the full price one clean line and the
     star + number its own clean line beneath, both left-aligned. */
  .amboi-card-body > .d-flex.align-items-center.justify-content-between {
    flex-direction: column;
    align-items: flex-start !important;
    gap: 2px;
  }
  .amboi-card-price {
    font-size: 14px;
    line-height: 1.3;
    white-space: nowrap;
  }
  .amboi-rating {
    font-size: 12px;
    margin-top: 2px;
  }

  /* Kill the desktop equal-height behaviour at xs. The .amboi-card-body
     rule at line 437 (`> :last-child { margin-top: auto }`) anchors the
     price row to the card bottom so siblings in a row share a price
     baseline. At desktop that's nice; at xs it leaves a visible empty
     gap between the compliance pill and the price when the card body
     happens to be shorter than its sibling. Mobile cards can vary in
     height naturally — the gap is more visible than the slightly
     uneven bottom edge. */
  .amboi-card-body > :last-child {
    margin-top: 0;
  }
}

@media (max-width: 640px) {
  .vp-page { padding: var(--space-4); }
  /* Mobile section rhythm — 24px (--space-6) keeps the 40% tighter target
     consistent with the desktop value above. Previously 40px (--space-10). */
  /* Same longhand fix as the default rule — keep .container's 16px
     horizontal gutter intact on mobile. */
  .amboi-section { padding-top: var(--space-6); padding-bottom: var(--space-6); }
  .amboi-hero-content { padding: var(--space-8); }
  /* Responsive typography — display and h1 scale down on mobile so the
     hero doesn't blow out narrow viewports. Body stays consistent. */
  :root {
    --fs-display: 36px;
    --fs-h1: 28px;
    --fs-h2: 22px;
  }
  .legal-document { padding: var(--space-6); }
  .legal-header h1 { font-size: 26px; }
}

/* ============================================================
   Customer View drawer — vendor's "Preview as customer" offcanvas
============================================================ */
.cv-drawer {
  --bs-offcanvas-width: min(1100px, 92vw);
  background: var(--bg-subtle);
}
.cv-drawer-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  background: var(--bg-card);
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 2;
}
.cv-drawer-head-title {
  display: flex; flex-direction: column; gap: 4px;
  min-width: 0; /* allow truncation */
}
.cv-drawer-head-title h5 {
  font-size: 16px; font-weight: 700;
  color: var(--text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cv-drawer-head-actions {
  display: flex; align-items: center; gap: 6px;
  flex-shrink: 0;
}
/* Preview mode pill — calmer than the previous red eyebrow */
.cv-drawer-pill {
  display: inline-flex; align-items: center; gap: 5px;
  background: var(--accent-50, #f0fdf4);
  color: var(--accent-700, #15803d);
  font-size: 11px; font-weight: 700;
  padding: 3px 10px;
  border-radius: 999px;
  text-transform: uppercase; letter-spacing: 0.04em;
  border: 1px solid var(--accent-100, #dcfce7);
  width: max-content;
}
.cv-drawer-pill i { font-size: 12px; }
/* Reusable round icon button inside the drawer head */
.cv-drawer-iconbtn {
  display: inline-grid; place-items: center;
  width: 36px; height: 36px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--bg-card);
  color: var(--text-secondary);
  cursor: pointer;
  text-decoration: none;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.cv-drawer-iconbtn:hover {
  background: var(--bg-subtle);
  color: var(--text-primary);
  border-color: var(--text-secondary);
}
.cv-drawer-close {
  background-color: transparent;
  margin: 0; /* reset Bootstrap's default */
}
.cv-drawer-body {
  padding: var(--space-6);
  display: flex; flex-direction: column; gap: var(--space-5);
}
/* When the drawer body hosts a full-page preview iframe, kill its padding
   so the iframe can fill the full pane edge-to-edge. */
.cv-drawer-body.p-0 {
  padding: 0;
  display: block;
  gap: 0;
}
.cv-drawer-iframe {
  display: block;
  width: 100%;
  height: 100%;
  min-height: calc(100vh - 84px); /* drawer head ≈ 84px */
  border: 0;
  background: var(--bg-main);
}
/* Direct children of the drawer body must NOT be flex-shrunk — without this, the
   profile card with banner+header gets squashed by the parent flex column. */
.cv-drawer-body > * { flex-shrink: 0; }
/* Override the .vp-surface mb-3 utility so the gap is owned by the flex parent. */
.cv-drawer-body > .vp-surface { margin-bottom: 0; }

/* Banner + avatar header
   ------------------------------------------------------------------
   Layout intent: hero-style profile header where the avatar and the
   stats card anchor INTO the bottom edge of the banner photo, like
   a typical social-network profile (Facebook / LinkedIn). The amount
   of overlap is intentionally subtle — only the top quarter of the
   avatar (~24 of 96 px) sits on the banner, and only the top edge of
   the stats card (~12 px) clips the banner. This integrates the chrome
   visually without eating the banner photo.

   Mechanics: cv-header has padding-top: var(--space-6) (= 24 px).
   A negative margin-top on cv-avatar / cv-stats lifts them above that
   padding and beyond the cv-header's top edge — which sits flush with
   the cv-banner's bottom edge, so the negative margin amount minus
   24 px = the visible banner-overlap height. The .cv-profile-card has
   overflow: hidden so nothing escapes the card. DOM order keeps the
   avatar/stats painting on top of the banner naturally (no z-index
   needed), and the box-shadow / thick ring on the avatar makes them
   read cleanly against any banner photo. */
.cv-profile-card { padding: 0; overflow: hidden; }
.cv-banner {
  height: 220px;
  background: var(--bg-subtle) center/cover no-repeat;
  border-bottom: 1px solid var(--border);
}
.cv-header {
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: var(--space-6);
  align-items: start;
  padding: var(--space-6);
}
@media (max-width: 768px) {
  .cv-header { grid-template-columns: 1fr; }
}
.cv-header-meta {
  display: flex;
  gap: var(--space-5);
  align-items: flex-start;
  min-width: 0;
}
.cv-avatar {
  width: 96px; height: 96px;
  border-radius: 50%;
  background: var(--bg-card) center/cover no-repeat;
  /* Thick light-coloured ring + soft shadow so the avatar reads as a
     floating circle against ANY banner photo (busy / dark / pale).
     Replaces the previous 1px hairline border which disappeared on
     photos with light edges. */
  border: 4px solid var(--bg-card);
  box-shadow: 0 2px 8px rgba(15, 23, 42, .15);
  display: grid; place-items: center;
  flex-shrink: 0;
  /* Lift the avatar so its top quarter (24 of 96 px) overlaps the
     banner. Math: cv-header pads in by var(--space-6) (= 24 px), so
     -48 px places the top edge 24 px above the cv-banner bottom edge.
     If --space-6 ever changes, this value tracks via calc(). */
  margin-top: calc(-1 * var(--space-6) - 24px);
  /* Position relative makes the shadow render cleanly above anything
     beneath (paranoia — DOM order already does this). */
  position: relative;
}
.cv-avatar i { font-size: 36px; color: var(--text-secondary); }
.cv-header-text { min-width: 0; padding-top: 2px; }
.cv-name {
  font-size: 22px; font-weight: 700; color: var(--text-primary);
  margin: 0 0 4px;
  /* inline-flex so the verified tick sits ON the same baseline as the
     name, gap-spaced. flex-wrap allows the title text to wrap to a
     second line on narrow widths (cv-header-text column is ~600px on
     desktop; long names like a full company name + SSM suffix can
     exceed that). Without flex-wrap, the line would overflow the
     column. overflow-wrap: anywhere is the belt to flex-wrap's
     suspenders — it handles single-word names too long to fit on one
     line by breaking at any character (rare but real: brand names
     like "Supercalifragilisticexpialidocious" in test data). */
  display: inline-flex; align-items: center; gap: 6px;
  flex-wrap: wrap;
  overflow-wrap: anywhere;
  max-width: 100%;
}
.cv-verified { color: var(--accent-700, #1F8000); font-size: 18px; }
.cv-sub { color: var(--text-secondary); font-size: var(--fs-small); line-height: 1.5; }
.cv-actions {
  display: flex; gap: var(--space-2); flex-wrap: wrap;
  margin-top: var(--space-3);
}
.cv-actions .vp-btn-lime,
.cv-actions .vp-btn-ghost {
  padding: var(--space-2) var(--space-4); font-size: var(--fs-small);
}
.cv-stats {
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-5) var(--space-6);
  display: grid; gap: var(--space-3);
  /* Subtle banner overlap — lifts the card so its TOP edge clips
     about 12 px into the banner. -36 px = -(--space-6) - 12 px, so
     the top of the card sits 12 px above cv-header's top edge, which
     is the banner-bottom line. Less aggressive than the avatar's
     quarter-overlap because the card is much taller. */
  margin-top: calc(-1 * var(--space-6) - 12px);
  /* Soft shadow so the card reads as a floating panel where it
     overlaps the banner photo — without this it looks like a flat
     cutout over the image. */
  box-shadow: 0 4px 12px rgba(15, 23, 42, .08);
  position: relative;
}
/* On mobile the cv-header collapses to a single column, so the stats
   card moves BELOW the meta block — no more banner above it to overlap.
   Cancel the lift in that layout. */
@media (max-width: 768px) {
  .cv-stats { margin-top: 0; }
}
.cv-stat-row {
  display: grid; grid-template-columns: 24px 1fr auto;
  align-items: center; gap: var(--space-3);
  font-size: var(--fs-small);
}
.cv-stat-row i { color: var(--text-secondary); }
.cv-stat-row span { color: var(--text-secondary); }
.cv-stat-row strong { color: var(--text-primary); font-weight: 600; text-align: right; }
/* Secondary count in parentheses next to a stat — e.g. "4.7 (12)" */
.cv-stat-secondary {
  color: var(--text-secondary);
  font-weight: 500;
  font-size: 12px;
  margin-left: 2px;
}
/* Empty-state stat — friendly fallback when there's nothing to show. */
.cv-stat-empty {
  color: var(--text-secondary) !important;
  font-weight: 500 !important;
  font-style: italic;
  font-size: 12.5px;
}

/* Verification-status pill — replaces the previous "raw enum text" display
   with a coloured badge so customers parse the trust signal at a glance. */
.cv-status-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 9px;
  border-radius: var(--radius-pill);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.01em;
  white-space: nowrap;
  border: 1px solid transparent;
}
.cv-status-pill > i { font-size: 11px; }
.cv-status-verified {
  background: rgba(var(--accent-rgb, 143, 224, 0), 0.18);
  color: #2b5a00;
  border-color: rgba(var(--accent-rgb, 143, 224, 0), 0.45);
}
.cv-status-pending {
  background: rgba(255, 198, 0, 0.18);
  color: #806300;
  border-color: rgba(255, 198, 0, 0.4);
}
.cv-status-new {
  background: rgba(91, 108, 255, 0.10);
  color: var(--primary-900);
  border-color: rgba(91, 108, 255, 0.30);
}
.cv-status-rejected {
  background: rgba(220, 53, 69, 0.10);
  color: #b02a37;
  border-color: rgba(220, 53, 69, 0.30);
}

/* Section title — uniform h5 with leading icon, used across vendor-info /
   working-hours / specialties / services / gallery so the headers don't
   drift between h6 + inline-icon and h5 + no-icon variants. */
.cv-section-title {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 var(--space-3);
  font-size: 17px;
  font-weight: 700;
  color: var(--text-primary);
  line-height: 1.35;
}
.cv-section-title > i:first-child {
  color: var(--text-secondary);
  font-size: 18px;
  flex-shrink: 0;
}
/* Sub-section title — slightly tighter spacing for nested headings inside
   the same vp-surface (e.g. Working hours, Specialties inside Vendor info). */
.cv-section-title-sub {
  font-size: 15px;
  margin-top: var(--space-4);
  margin-bottom: var(--space-2);
}

/* Promoted vendor description — body-sized so the vendor's pitch reads
   as actual content, not a footnote. The previous treatment used
   `class="small text-muted"` which made the description blend into the
   page chrome. */
.cv-description {
  margin: 0 0 var(--space-4);
  font-size: var(--fs-body);
  line-height: var(--lh-body, 1.6);
  color: var(--text-primary);
  white-space: pre-line; /* honour line breaks in the vendor's input */
}
.cv-description-empty {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-small);
  font-style: italic;
}
.cv-description-empty > i { font-size: 14px; }

/* Working hours: "Open today / Closed today" indicator pill that sits
   next to the section heading. Communicates the most important fact
   ("can I call them today?") without scanning the 7-row schedule. */
.cv-hours-now-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  margin-left: auto; /* pushes to the right edge of the heading row */
  border-radius: var(--radius-pill);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.01em;
  border: 1px solid transparent;
  white-space: nowrap;
}
.cv-hours-now-pill.is-open {
  background: rgba(var(--accent-rgb, 143, 224, 0), 0.18);
  color: #2b5a00;
  border-color: rgba(var(--accent-rgb, 143, 224, 0), 0.4);
}
.cv-hours-now-pill.is-closed {
  background: var(--bg-subtle);
  color: var(--text-secondary);
  border-color: var(--border);
}
.cv-hours-dot {
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}
.cv-hours-now-pill.is-open .cv-hours-dot {
  background: #2b5a00;
  /* Subtle pulse so the "live" feel reinforces that this is real-time. */
  animation: cvHoursPulse 1.8s ease-in-out infinite;
}
@keyframes cvHoursPulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.5; transform: scale(0.85); }
}
@media (prefers-reduced-motion: reduce) {
  .cv-hours-now-pill.is-open .cv-hours-dot { animation: none; }
}

/* Background category pills */
.cv-cat-pills { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.cv-cat-pill {
  background: var(--accent-500); color: #000;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-pill);
  font-size: var(--fs-small); font-weight: 600;
}

/* Gallery — horizontal scroll carousel with prev/next arrow controls flanking the track. */
.cv-gallery-card { padding: 0; overflow: hidden; }
.cv-gallery-head {
  padding: var(--space-5) var(--space-6) var(--space-3);
}
.cv-gallery-carousel {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 0 var(--space-6) var(--space-6);
}
.cv-gallery-arrow {
  flex: 0 0 40px;
  width: 40px; height: 40px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-card);
  color: var(--text-primary);
  display: grid; place-items: center;
  cursor: pointer;
  font-size: 18px;
  transition: background-color var(--transition), color var(--transition), box-shadow var(--transition);
  box-shadow: 0 1px 3px rgba(0,0,0,0.06);
}
.cv-gallery-arrow:hover { background: var(--bg-subtle); box-shadow: 0 2px 6px rgba(0,0,0,0.1); }
.cv-gallery-arrow:disabled { opacity: 0.35; cursor: not-allowed; box-shadow: none; }
.cv-gallery-track {
  flex: 1 1 auto;
  min-width: 0;
  display: flex; gap: var(--space-3);
  overflow-x: auto;
  scroll-behavior: smooth;
  scroll-snap-type: x mandatory;
}
/* Hide default scrollbar — scroll is driven by the arrow buttons. */
.cv-gallery-track::-webkit-scrollbar { display: none; }
.cv-gallery-track { scrollbar-width: none; }
.cv-gallery-tile {
  flex: 0 0 160px;
  scroll-snap-align: start;
  text-decoration: none;
  /* When rendered as a <button> (vendor gallery, lightbox-wired) */
  border: 0; padding: 0; background: transparent;
  cursor: zoom-in;
}
.cv-gallery-tile:disabled { cursor: default; }
.cv-gallery-thumb {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  background: var(--bg-subtle) center/cover no-repeat;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
  transition: transform var(--transition);
}
.cv-gallery-tile:hover .cv-gallery-thumb { transform: translateY(-2px); }
.cv-gallery-play {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  background: rgba(0,0,0,0.25);
  color: #fff;
  font-size: 32px;
}
.cv-gallery-play i {
  background: rgba(0,0,0,0.55);
  width: 48px; height: 48px;
  border-radius: 999px;
  display: grid; place-items: center;
}
.cv-gallery-empty {
  display: flex; align-items: center; justify-content: center; gap: var(--space-2);
  margin: 0 var(--space-6) var(--space-6);
  padding: var(--space-8) var(--space-4);
  border: 2px dashed var(--border);
  border-radius: var(--radius-card);
  color: var(--text-secondary);
  font-size: var(--fs-small);
}
.cv-gallery-empty i { font-size: 18px; }

/* Product/service cards in the customer view */
.cv-product {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
  height: 100%;
  display: flex; flex-direction: column;
}
.cv-product-img {
  aspect-ratio: 4 / 3;
  background: var(--bg-subtle) center/cover no-repeat;
  display: grid; place-items: center;
  color: var(--text-secondary);
}
.cv-product-img i { font-size: 32px; }
.cv-product-body { padding: var(--space-3) var(--space-4); display: flex; flex-direction: column; gap: 2px; flex: 1 1 auto; }
.cv-product-title { font-weight: 700; color: var(--text-primary); font-size: var(--fs-body); }
.cv-product-price { color: var(--accent-700, #1F8000); font-weight: 700; font-size: var(--fs-body); margin-top: auto; }
/* "New" badge overlay on the cover image — replaces the "0 ordered · 0.0 ★"
   death-row display that previously made fresh listings look unloved. */
.cv-product-img { position: relative; }
.cv-product-new-badge {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  background: var(--primary-900);
  color: var(--accent-500);
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: var(--radius-pill);
  z-index: 1;
}
/* Meta row: rating + bookings inline. Wraps on narrow cards so we don't
   clip. The .cv-product-no-rating subdued state is shown when a listing
   has 0 reviews but >0 bookings (we don't want to display "★ 0.0"). */
.cv-product-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 4px 8px;
  margin-top: var(--space-2);
  font-size: 12px;
}
.cv-product-rating-count {
  color: var(--text-secondary);
  font-weight: 500;
  font-size: 11px;
  margin-left: 2px;
}
.cv-product-no-rating {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  opacity: 0.85;
}

/* Services-section header — gives the services grid a clear identity
   (was previously just a bare "Sort by" row above an unlabelled grid). */
.cv-services-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.cv-services-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px; height: 22px;
  padding: 0 8px;
  border-radius: var(--radius-pill);
  background: var(--primary-900);
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
}

/* Sort toolbar — buttons + price-select. Labelled rather than relying on
   a bare bold "Sort by" so the row reads as one continuous control group. */
.cv-sort-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
  padding: var(--space-3) var(--space-3);
  background: var(--bg-subtle);
  border-radius: var(--radius-input);
  border: 1px solid var(--border);
}
.cv-sort-label {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-secondary);
  margin-right: 4px;
}
.cv-sort-price {
  max-width: 220px;
  margin-left: auto;
}
@media (max-width: 575.98px) {
  .cv-sort-toolbar { padding: var(--space-2) var(--space-3); }
  .cv-sort-label { width: 100%; margin-bottom: 4px; }
  .cv-sort-price { max-width: none; width: 100%; margin-left: 0; margin-top: 4px; }
}

/* Preview-mode disabled buttons — properly dimmed so vendors can tell at a
   glance that Chat / Follow / Share / Book aren't actually clickable. The
   `disabled` HTML attribute alone leaves them looking active; this softens
   the styling and adds the not-allowed cursor + a subtle hatch overlay. */
.cv-actions [disabled],
.cv-actions button[disabled],
.cv-actions a[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed !important;
  pointer-events: none; /* don't trigger hover/focus changes */
  position: relative;
}
.cv-actions [disabled]::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  background: repeating-linear-gradient(
    45deg,
    rgba(0,0,0,0) 0,
    rgba(0,0,0,0) 4px,
    rgba(0,0,0,0.04) 4px,
    rgba(0,0,0,0.04) 8px
  );
  pointer-events: none;
}

/* Tighter buttons inside the customer view */
.cv-drawer .vp-btn-sm,
.cv-drawer .vp-btn-lime.vp-btn-sm,
.cv-drawer .vp-btn-ghost.vp-btn-sm,
.cv-public .vp-btn-sm,
.cv-public .vp-btn-lime.vp-btn-sm,
.cv-public .vp-btn-ghost.vp-btn-sm {
  padding: var(--space-2) var(--space-3);
  font-size: var(--fs-small);
}

/* Public page wrapper — gives the partial the same vertical rhythm as the drawer body. */
.cv-public {
  display: flex; flex-direction: column; gap: var(--space-5);
}
.cv-public > * { flex-shrink: 0; }
.cv-public .vp-surface { margin-bottom: 0; }

/* Reviews list (live page only) */
.cv-reviews { display: flex; flex-direction: column; gap: var(--space-3); }
.cv-review {
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--bg-card);
}
.cv-review-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.cv-review-date { display: block; margin-top: var(--space-2); font-size: 11px; }

/* Disabled service tile inside the preview drawer (no hover feedback, no link). */
.cv-product-disabled { cursor: default; pointer-events: none; }

/* Toast — used by Share / Coming-soon click handlers. */
.cv-toast {
  position: fixed; bottom: 24px; left: 50%;
  transform: translateX(-50%) translateY(20px);
  background: var(--text-primary); color: #fff;
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-pill);
  font-size: var(--fs-small);
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
  z-index: 9999;
}
.cv-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ──────────────────────────────────────────────────────────────────────
   Public detail-page DNA — shared by vendor / service / future product
   pages so the surface, breadcrumb, vendor strip, and review block all
   look identical regardless of entity type.
   ────────────────────────────────────────────────────────────────────── */

/* Breadcrumb (replaces Bootstrap .breadcrumb on public pages) */
.amboi-breadcrumb { margin: 0 0 var(--space-4); font-size: var(--fs-small); }
.amboi-breadcrumb ol {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2);
}
.amboi-breadcrumb-item { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--text-secondary); }
.amboi-breadcrumb-item a { color: var(--text-secondary); }
.amboi-breadcrumb-item a:hover { color: var(--text-primary); text-decoration: underline; }
.amboi-breadcrumb-item.active span { color: var(--text-primary); font-weight: 600; }
.amboi-breadcrumb-item i { color: var(--text-secondary); font-size: 11px; }

/* Smaller avatar variants used in vendor strip + review heads */
.cv-avatar-sm { width: 56px; height: 56px; }
.cv-avatar-sm i { font-size: 22px; }
.cv-avatar-xs {
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--bg-subtle) center/cover no-repeat;
  border: 1px solid var(--border);
  display: grid; place-items: center;
  flex-shrink: 0; overflow: hidden;
}
.cv-avatar-xs i { font-size: 14px; color: var(--text-secondary); }
.cv-avatar-xs img { width: 100%; height: 100%; object-fit: cover; }

/* ── Vendor strip — embedded vendor card on service/product detail ── */
.cv-vendor-strip {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: var(--space-5);
  align-items: center;
  padding: var(--space-5) var(--space-6);
}
@media (max-width: 768px) {
  .cv-vendor-strip { grid-template-columns: 1fr; }
}
.cv-vendor-strip-identity {
  display: flex; gap: var(--space-4); align-items: center;
  text-decoration: none; color: inherit; min-width: 0;
}
.cv-vendor-strip-identity:hover .cv-vendor-strip-name { color: var(--primary-900); }
.cv-vendor-strip-meta { min-width: 0; }
.cv-vendor-strip-name {
  font-weight: 700; color: var(--text-primary); font-size: var(--fs-h3);
  display: inline-flex; align-items: center; gap: 6px;
  transition: color var(--transition);
}
.cv-vendor-strip-cat { font-size: var(--fs-small); color: var(--text-secondary); margin-top: 2px; }
.cv-vendor-strip-sep { color: var(--border); margin: 0 4px; }
.cv-vendor-strip-loc {
  font-size: var(--fs-small); color: var(--text-secondary); margin-top: 2px;
  display: inline-flex; align-items: center; gap: 4px;
}
.cv-vendor-strip-actions { display: flex; gap: var(--space-2); }
.cv-vendor-strip-stats {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-3);
  grid-column: 1 / -1;
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}
@media (max-width: 768px) {
  .cv-vendor-strip-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.cv-vendor-strip-stat {
  display: flex; flex-direction: column; gap: 2px;
  padding: 0 var(--space-3);
  border-right: 1px solid var(--border);
}
.cv-vendor-strip-stat:last-child { border-right: 0; }
.cv-vendor-strip-stat i { color: var(--text-secondary); font-size: 14px; }
.cv-vendor-strip-stat span { font-size: 11px; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.04em; }
.cv-vendor-strip-stat strong { font-size: var(--fs-body); color: var(--text-primary); font-weight: 700; }

/* ── Detail-page hero (gallery + info card) ── */
.cv-detail-hero { align-items: stretch; }

/* Detail gallery — main image stage + thumbnail strip below */
.cv-gallery-detail { padding: 0; overflow: hidden; height: 100%; display: flex; flex-direction: column; }
.cv-gallery-detail-main {
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--bg-subtle);
  display: grid; place-items: center;
}
.cv-gallery-detail-main img {
  width: 100%; height: 100%; object-fit: cover;
  display: block;
}
.cv-gallery-detail-placeholder { color: var(--text-secondary); font-size: 64px; }
.cv-gallery-thumbs {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--border);
}
.cv-gallery-thumbs-track {
  flex: 1 1 auto; min-width: 0;
  display: flex; gap: var(--space-2);
  overflow-x: auto; scroll-behavior: smooth;
}
.cv-gallery-thumbs-track::-webkit-scrollbar { display: none; }
.cv-gallery-thumbs-track { scrollbar-width: none; }
.cv-gallery-thumb-btn {
  flex: 0 0 64px;
  width: 64px; height: 64px;
  padding: 0; border: 2px solid transparent; border-radius: var(--radius-input);
  background: transparent; cursor: pointer; overflow: hidden;
  transition: border-color var(--transition);
}
.cv-gallery-thumb-btn .cv-gallery-thumb {
  width: 100%; height: 100%; aspect-ratio: 1 / 1;
  border-radius: 6px; border: 1px solid var(--border);
}
.cv-gallery-thumb-btn:hover { border-color: var(--text-secondary); }
.cv-gallery-thumb-btn.is-active { border-color: var(--accent-600); }

/* Discount badge — top-right ribbon over the main image */
.cv-discount-badge {
  position: absolute; top: var(--space-3); right: var(--space-3);
  background: #C8102E; color: #fff;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-input);
  font-weight: 700; font-size: var(--fs-h3);
  line-height: 1;
  display: inline-flex; flex-direction: column; align-items: center;
  box-shadow: 0 2px 8px rgba(200, 16, 46, 0.3);
}
.cv-discount-badge small { font-size: 10px; font-weight: 600; letter-spacing: 0.04em; }

/* Detail info column — title / rating / price / specs / form */
.cv-detail-info { display: flex; flex-direction: column; gap: var(--space-3); height: 100%; }
.cv-detail-title { font-size: var(--fs-h2); font-weight: 700; margin: 0; }
.cv-detail-subtitle { color: var(--text-secondary); margin: 0; font-size: var(--fs-body); }

/* ⚡ Instant Book badge — pill-style call-out under the service title.
   Lime icon + bold name + muted explainer. Sets expectation BEFORE the
   customer scrolls to the booking CTA, so they know they're in the
   e-commerce flow (vs the inquiry flow). */
.cv-detail-instant-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: var(--space-2);
    padding: 4px var(--space-3);
    background: rgba(var(--accent-rgb, 143, 224, 0), .12);
    border: 1px solid rgba(var(--accent-rgb, 143, 224, 0), .35);
    border-radius: var(--radius-pill);
    font-size: var(--fs-small);
    color: var(--text-primary);
}
.cv-detail-instant-badge > i {
    color: var(--accent-600, var(--accent-500));
    font-size: 13px;
}
.cv-detail-instant-badge > small { font-size: 11.5px; }

.cv-detail-rating {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--fs-small);
}
.cv-detail-rating-sep { color: var(--border); }
.cv-detail-rating-link { color: var(--text-secondary); }
.cv-detail-rating-link:hover { color: var(--text-primary); text-decoration: underline; }
.cv-rating-stars { color: #F2B500; letter-spacing: 1px; }

.cv-detail-price {
  font-size: var(--fs-h2);
  font-weight: 700;
  color: var(--accent-600, #8FE000);
}

.cv-detail-specs {
  display: grid; grid-template-columns: 140px 1fr;
  gap: var(--space-2) var(--space-4);
  margin: 0;
  padding: var(--space-3) 0;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-small);
}
.cv-detail-specs dt { color: var(--text-secondary); font-weight: 400; margin: 0; }
.cv-detail-specs dd { color: var(--text-primary); font-weight: 600; margin: 0; }

.cv-detail-form { display: flex; flex-direction: column; gap: var(--space-3); margin-top: auto; }
.cv-detail-row, .cv-detail-row-grid { display: flex; flex-direction: column; gap: var(--space-1); }
.cv-detail-row-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
.cv-detail-label { font-size: var(--fs-small); color: var(--text-secondary); font-weight: 600; }
.cv-detail-actions { display: grid; grid-template-columns: auto 1fr auto; gap: var(--space-2); margin-top: var(--space-2); }
.cv-detail-actions .vp-btn-lime { padding: 0 var(--space-5); height: 44px; font-weight: 700; }
.cv-detail-actions .vp-btn-ghost { padding: 0 var(--space-3); height: 44px; }
.cv-detail-helper {
  display: block; text-align: center; color: var(--text-secondary);
  font-size: var(--fs-small);
}

/* Highlights bullets */
.cv-highlights { padding-left: var(--space-5); margin: 0; color: var(--text-primary); }
.cv-highlights li { padding: 2px 0; }

/* Package cards (replaces ad-hoc .amboi-card on service page) */
.cv-package {
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-5);
  display: flex; flex-direction: column; gap: var(--space-2);
}
.cv-package-head { display: flex; justify-content: space-between; align-items: center; gap: var(--space-2); }
.cv-package-desc { font-size: var(--fs-small); color: var(--text-secondary); margin: 0; }
.cv-package-price {
  font-size: var(--fs-h3); font-weight: 700;
  color: var(--accent-600, #8FE000);
  margin-top: var(--space-2);
}
.cv-package-inclusions {
  list-style: none; padding: 0; margin: var(--space-2) 0 0;
  font-size: var(--fs-small); color: var(--text-primary);
}
.cv-package-inclusions li { padding: 2px 0; display: flex; align-items: flex-start; gap: 6px; }
.cv-package-inclusions i { color: var(--accent-600, #8FE000); margin-top: 3px; }

/* ── Reviews block — shared rating summary + breakdown bars + review list ── */
.cv-rating-summary {
  display: grid; grid-template-columns: 200px 1fr;
  gap: var(--space-6);
  align-items: center;
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  background: var(--bg-subtle);
}
@media (max-width: 576px) {
  .cv-rating-summary { grid-template-columns: 1fr; gap: var(--space-4); }
}
.cv-rating-summary-score { text-align: center; border-right: 1px solid var(--border); padding-right: var(--space-4); }
@media (max-width: 576px) {
  .cv-rating-summary-score { border-right: 0; border-bottom: 1px solid var(--border); padding: 0 0 var(--space-3); }
}
.cv-rating-summary-num {
  font-size: 48px; font-weight: 700; color: var(--accent-600, #8FE000);
  line-height: 1;
}
.cv-rating-summary-num small { font-size: 16px; color: var(--text-secondary); font-weight: 400; }
.cv-rating-summary-count { font-size: var(--fs-small); color: var(--text-secondary); margin-top: var(--space-2); }
.cv-rating-summary-stars { color: #F2B500; margin-top: var(--space-2); letter-spacing: 2px; font-size: 18px; }

.cv-rating-bars { display: flex; flex-direction: column; gap: 6px; }
.cv-rating-bar {
  display: grid; grid-template-columns: auto 1fr auto;
  gap: var(--space-3);
  align-items: center;
  font-size: var(--fs-small);
}
.cv-rating-bar-label { color: #F2B500; letter-spacing: 1px; white-space: nowrap; }
.cv-rating-bar-track {
  height: 8px; border-radius: 999px; background: var(--border); overflow: hidden;
}
.cv-rating-bar-fill { height: 100%; background: #F2B500; transition: width 200ms ease; }
.cv-rating-bar-count { color: var(--text-secondary); min-width: 24px; text-align: right; }

/* Review card — shared between vendor + service pages */
.cv-review-stars { color: #F2B500; font-size: var(--fs-small); letter-spacing: 1px; margin-top: var(--space-1); }
.cv-review-body { margin: var(--space-3) 0 0; color: var(--text-primary); font-size: var(--fs-body); }
.cv-review-response {
  margin-top: var(--space-3);
  padding: var(--space-3);
  background: var(--bg-subtle);
  border-radius: var(--radius-input);
  font-size: var(--fs-small);
}
.cv-review-sep { margin: 0 4px; }
.cv-reviews-pager { display: flex; justify-content: center; }
.cv-reviews-pager .pagination { margin: 0; }

/* Canonical empty-state — see header comment up top. Stacked + centered so
   ANY combination of (icon, heading, description, button) reads as a coherent
   vertical group regardless of how many children the call-site emits.
   Previously there were two competing rules; the flex-row variant won the
   cascade and broke the cart / lists / search empty states. */
.amboi-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-10) var(--space-6);
  border: 2px dashed var(--border);
  border-radius: var(--radius-card);
  background: var(--bg-subtle);
  color: var(--text-secondary);
  font-size: var(--fs-small);
  text-align: center;
}
.amboi-empty > i {
  font-size: 32px;
  color: var(--text-secondary);
  margin-bottom: var(--space-1);
}

/* ──────────────────────────────────────────────────────────────────────
   Detail-page gallery enhancements — main image is clickable, thumb
   strip shows a play icon over video thumbs, lightbox opens on click.
   ────────────────────────────────────────────────────────────────────── */

/* Make the main "stage" a clickable button without button chrome */
button.cv-gallery-detail-main {
  border: 0; padding: 0; background: var(--bg-subtle);
  cursor: zoom-in; position: relative; width: 100%;
  display: grid; place-items: center;
  aspect-ratio: 1 / 1;
}
button.cv-gallery-detail-main:focus-visible { outline: 2px solid var(--accent-600); outline-offset: -2px; }

/* "Expand" affordance hint at top-right of main image (hidden by default, fades in on hover) */
.cv-gallery-expand {
  position: absolute; top: var(--space-3); left: var(--space-3);
  background: rgba(0, 0, 0, 0.55); color: #fff;
  padding: 6px 10px; border-radius: var(--radius-pill);
  font-size: var(--fs-small); font-weight: 600;
  display: inline-flex; align-items: center; gap: 6px;
  opacity: 0; transition: opacity var(--transition);
}
button.cv-gallery-detail-main:hover .cv-gallery-expand,
button.cv-gallery-detail-main:focus-visible .cv-gallery-expand { opacity: 1; }

/* Video play overlay for the main image when first item is a video */
.cv-gallery-detail-play {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  pointer-events: none;
}
.cv-gallery-detail-play i {
  background: rgba(0, 0, 0, 0.65); color: #fff;
  width: 72px; height: 72px;
  border-radius: 999px;
  display: grid; place-items: center;
  font-size: 36px;
}

/* Play icon on video thumbnails in the strip */
.cv-gallery-thumb-btn { position: relative; }
.cv-gallery-thumb-play {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  pointer-events: none;
  color: #fff; font-size: 18px;
}
.cv-gallery-thumb-play i {
  background: rgba(0, 0, 0, 0.55);
  width: 28px; height: 28px;
  border-radius: 999px;
  display: grid; place-items: center;
}

/* ──────────────────────────────────────────────────────────────────────
   Media lightbox — full-screen overlay used on service / product pages
   ────────────────────────────────────────────────────────────────────── */
.cv-lightbox {
  position: fixed; inset: 0;
  background: rgba(15, 19, 35, 0.92);
  z-index: 9050;
  display: none;
  padding: var(--space-6);
  overscroll-behavior: contain;
}
.cv-lightbox.is-open { display: flex; align-items: stretch; justify-content: center; }
body.cv-lightbox-locked { overflow: hidden; }

.cv-lightbox-dialog {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 280px;
  gap: var(--space-5);
  width: 100%; max-width: 1400px;
  max-height: 100%;
  align-self: center;
}
@media (max-width: 992px) {
  .cv-lightbox-dialog { grid-template-columns: 1fr; }
}

.cv-lightbox-stage {
  position: relative;
  background: rgba(0, 0, 0, 0.35);
  border-radius: var(--radius-card);
  display: grid; place-items: center;
  min-height: 50vh;
  overflow: hidden;
}
.cv-lightbox-media { width: 100%; height: 100%; display: grid; place-items: center; }
.cv-lightbox-image, .cv-lightbox-video {
  max-width: 100%; max-height: 80vh;
  object-fit: contain;
  display: block;
}
.cv-lightbox-video { width: 100%; }

.cv-lightbox-arrow {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 56px; height: 56px;
  border: 0; border-radius: 999px;
  background: rgba(255, 255, 255, 0.92); color: var(--text-primary);
  display: grid; place-items: center;
  font-size: 22px; cursor: pointer;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
  transition: background-color var(--transition), transform var(--transition);
  z-index: 2;
}
.cv-lightbox-arrow:hover { background: var(--bg-card); transform: translateY(-50%) scale(1.05); }
.cv-lightbox-prev { left: var(--space-4); }
.cv-lightbox-next { right: var(--space-4); }

.cv-lightbox-close {
  position: absolute; top: var(--space-5); right: var(--space-5);
  width: 44px; height: 44px;
  border: 0; border-radius: 999px;
  background: rgba(255, 255, 255, 0.15); color: #fff;
  display: grid; place-items: center;
  font-size: 18px; cursor: pointer;
  z-index: 3;
  transition: background-color var(--transition);
}
.cv-lightbox-close:hover { background: rgba(255, 255, 255, 0.28); }

.cv-lightbox-rail {
  background: var(--bg-card);
  border-radius: var(--radius-card);
  padding: var(--space-5);
  display: flex; flex-direction: column; gap: var(--space-3);
  overflow-y: auto;
  max-height: 100%;
}
.cv-lightbox-rail-title { font-size: var(--fs-h3); font-weight: 700; margin: 0; }
.cv-lightbox-rail-thumbs {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-3);
}
@media (max-width: 992px) {
  .cv-lightbox-rail { display: none; } /* On mobile, the stage owns full screen */
}

.cv-lightbox-rail-thumb {
  position: relative;
  border: 2px solid transparent; padding: 0;
  background: transparent; cursor: pointer;
  border-radius: var(--radius-input);
  overflow: hidden;
  aspect-ratio: 1 / 1;
  transition: border-color var(--transition);
}
.cv-lightbox-rail-thumb:hover { border-color: var(--text-secondary); }
.cv-lightbox-rail-thumb.is-active { border-color: var(--accent-600); }
.cv-lightbox-rail-thumb-img {
  display: block; width: 100%; height: 100%;
  background: var(--bg-subtle) center/cover no-repeat;
}
.cv-lightbox-rail-thumb-play {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  pointer-events: none; color: #fff;
}
.cv-lightbox-rail-thumb-play i {
  background: rgba(0, 0, 0, 0.6);
  width: 32px; height: 32px;
  border-radius: 999px;
  display: grid; place-items: center;
  font-size: 16px;
}

/* ──────────────────────────────────────────────────────────────────────
   Vendor availability — month calendar + weekly slot grid
   ────────────────────────────────────────────────────────────────────── */
.av-cal-head {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
}
.av-cal-month { font-size: var(--fs-h3); }

/* 7-column month grid */
.av-cal-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  background: var(--border);
  gap: 1px;
}
.av-cal-dow {
  background: var(--bg-subtle);
  padding: var(--space-2) var(--space-3);
  font-size: var(--fs-small); font-weight: 700;
  color: var(--text-secondary);
  text-transform: uppercase; letter-spacing: 0.04em;
  text-align: center;
}
.av-cal-cell {
  background: var(--bg-card);
  min-height: 110px;
  padding: var(--space-2) var(--space-3);
  display: flex; flex-direction: column; gap: 4px;
  position: relative;
}
.av-cal-cell.is-out  { background: var(--bg-subtle); color: var(--text-secondary); }
.av-cal-cell.is-past { opacity: 0.55; }
.av-cal-cell.is-today { box-shadow: inset 0 0 0 2px var(--accent-600); }
.av-cal-cell.is-blackout {
  background: repeating-linear-gradient(
    -45deg,
    rgba(200, 16, 46, 0.06),
    rgba(200, 16, 46, 0.06) 6px,
    transparent 6px,
    transparent 12px
  );
}
.av-cal-cell.has-bookings { /* visual hint via bookings list itself */ }

.av-cal-cell-head {
  display: flex; align-items: center; justify-content: space-between;
}
.av-cal-day { font-weight: 700; font-size: var(--fs-body); }
.av-cal-pill {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; padding: 2px 6px;
  border-radius: var(--radius-pill); font-weight: 600;
}
.av-cal-pill-blackout { background: rgba(200, 16, 46, 0.12); color: #C8102E; }

.av-cal-bookings {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 2px;
}
.av-cal-booking {
  display: flex; gap: 4px; align-items: baseline;
  padding: 2px 6px;
  background: var(--accent-500); color: #000;
  border-radius: 4px;
  font-size: 11px; font-weight: 600;
  text-decoration: none;
  overflow: hidden;
}
.av-cal-booking:hover { background: var(--accent-600); color: #000; }
.av-cal-time   { font-weight: 700; flex: 0 0 auto; }
.av-cal-title  { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.av-cal-more   { font-size: 11px; color: var(--text-secondary); padding: 0 6px; }
.av-cal-blackout-tag {
  font-size: 11px;
  padding: 2px 6px;
  background: rgba(200, 16, 46, 0.12); color: #C8102E;
  border-radius: 4px;
  display: inline-flex; align-items: center; gap: 4px;
  font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.av-cal-legend {
  display: flex; gap: var(--space-4);
  padding: var(--space-3) var(--space-5);
  border-top: 1px solid var(--border);
  font-size: var(--fs-small); color: var(--text-secondary);
}
.av-dot {
  display: inline-block; width: 12px; height: 12px;
  border-radius: 4px; margin-right: 6px; vertical-align: middle;
}
.av-dot-book     { background: var(--accent-500); }
.av-dot-blackout { background: rgba(200, 16, 46, 0.6); }
.av-dot-today    { background: transparent; box-shadow: inset 0 0 0 2px var(--accent-600); }

/* ── Weekly schedule editor ── */
.av-sched { display: grid; gap: var(--space-3); }
.av-sched-day {
  display: grid; grid-template-columns: 200px 1fr; gap: var(--space-4);
  padding: var(--space-3);
  border: 1px solid var(--border); border-radius: var(--radius-card);
  background: var(--bg-subtle);
}
@media (max-width: 768px) { .av-sched-day { grid-template-columns: 1fr; } }
.av-sched-day-head {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2);
}
.av-sched-rows { display: flex; flex-direction: column; gap: var(--space-2); }
.av-sched-row {
  display: flex; align-items: center; gap: var(--space-2);
  background: var(--bg-card); padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border); border-radius: var(--radius-input);
}
.av-sched-row input[type="time"] { width: 110px; }
.av-cap-input { width: 80px; }
.av-sched-empty { padding: var(--space-2); }

/* ── Weekly slot grid (Mon–Sun columns) ── */
.av-week-grid {
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 1px; background: var(--border);
}
@media (max-width: 768px) {
  .av-week-grid { grid-template-columns: 1fr 1fr; }
}
.av-week-day {
  background: var(--bg-card);
  padding: var(--space-3);
  min-height: 160px;
  display: flex; flex-direction: column; gap: var(--space-2);
}
.av-week-day.is-today { box-shadow: inset 0 0 0 2px var(--accent-600); }
.av-week-day-head {
  display: flex; flex-direction: column; gap: 2px; padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-small);
}
.av-week-day-body { display: flex; flex-direction: column; gap: 6px; }
.av-slot {
  display: flex; align-items: center; gap: var(--space-2);
  padding: 4px 8px;
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius-input);
  font-size: 11px;
}
.av-slot.is-full { background: rgba(200, 16, 46, 0.08); border-color: rgba(200, 16, 46, 0.3); }
.av-slot-time { font-weight: 700; flex: 1 1 auto; min-width: 0; }
.av-slot-cap  { color: var(--text-secondary); }
.av-slot.is-full .av-slot-cap { color: #C8102E; font-weight: 700; }
.vp-btn-icon-sm { padding: 0 4px; }

/* ── Customer-side slot picker (service-show right column) ── */
.cv-slot-picker {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: var(--space-2); margin-top: var(--space-2);
}
.cv-slot-pick {
  border: 1px solid var(--border);
  background: var(--bg-card); color: var(--text-primary);
  padding: 8px var(--space-3);
  border-radius: var(--radius-input);
  font-size: var(--fs-small); font-weight: 600;
  text-align: center;
  cursor: pointer; transition: all var(--transition);
}
.cv-slot-pick:hover { border-color: var(--text-secondary); }
.cv-slot-pick.is-active { background: var(--accent-500); border-color: var(--accent-600); color: #000; }
.cv-slot-pick:disabled { opacity: 0.45; cursor: not-allowed; text-decoration: line-through; }
.cv-slot-pick small { display: block; font-weight: 400; color: var(--text-secondary); }
.cv-slot-pick.is-active small { color: rgba(0,0,0,0.65); }

/* ──────────────────────────────────────────────────────────────────────
   Vendor listing media uploader (.lm-*) — 7 images + 1 video
   ────────────────────────────────────────────────────────────────────── */
.lm-warning {
  background: rgba(200, 16, 46, 0.06);
  border: 1px solid rgba(200, 16, 46, 0.25);
  border-radius: var(--radius-card);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-4);
}
.lm-warning-head {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--fs-body);
  margin-bottom: 6px;
}
.lm-warning-head i { color: #C8102E; font-size: 18px; }

.lm-section + .lm-section { margin-top: var(--space-5); padding-top: var(--space-5); border-top: 1px solid var(--border); }

.lm-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: var(--space-3);
  min-height: 120px;
}
.lm-tile {
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-input);
  overflow: hidden;
  background: var(--bg-subtle);
  border: 1px solid var(--border);
}
.lm-tile-img {
  width: 100%; height: 100%;
  background: center/cover no-repeat;
}
.lm-tile.is-uploading::after {
  content: 'Uploading…';
  position: absolute; inset: 0;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, 0.7);
  font-weight: 700; color: var(--text-primary);
}
.lm-tile-cover {
  position: absolute; top: 6px; left: 6px;
  background: var(--accent-500); color: #000;
  padding: 2px 8px; border-radius: var(--radius-pill);
  font-size: 10px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.04em;
}
.lm-tile-actions {
  position: absolute; bottom: 6px; right: 6px;
  display: flex; gap: 4px;
  opacity: 0; transition: opacity var(--transition);
}
.lm-tile:hover .lm-tile-actions { opacity: 1; }
.lm-tile-actions .vp-btn-icon {
  background: rgba(255,255,255,0.92);
  width: 28px; height: 28px; padding: 0;
  border-radius: 999px;
}
.lm-tile-actions .vp-btn-icon i { font-size: 13px; }

.lm-empty {
  grid-column: 1 / -1;
  padding: var(--space-6) var(--space-4);
  border: 2px dashed var(--border);
  border-radius: var(--radius-card);
  text-align: center;
  color: var(--text-secondary);
  font-size: var(--fs-small);
}

.lm-video-card {
  position: relative;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
  max-width: 480px;
}
.lm-video {
  width: 100%; height: auto; display: block;
  background: var(--bg-subtle);
}
.lm-video-meta {
  display: flex; justify-content: space-between; align-items: center;
  padding: var(--space-2) var(--space-3);
  background: var(--bg-card);
  border-top: 1px solid var(--border);
}

.lm-aup-check {
  border: 1px solid var(--border);
  border-radius: var(--radius-input);
  background: var(--bg-subtle);
  cursor: pointer;
  transition: background-color var(--transition);
}
.lm-aup-check:has(input:checked) { background: rgba(var(--accent-rgb, 143, 224, 0), 0.12); border-color: var(--accent-600); }

/* Locked state — preview of the uploader on create forms before draft is saved */
.lm-uploader-locked {
  opacity: 0.85;
}
.lm-locked-hint {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-4);
  background: rgba(var(--accent-rgb, 143, 224, 0), 0.08);
  border: 1px dashed var(--accent-600);
  border-radius: var(--radius-card);
  margin-bottom: var(--space-4);
}
.lm-locked-hint-icon {
  width: 40px; height: 40px;
  border-radius: 999px;
  background: var(--accent-500);
  color: #000;
  display: grid; place-items: center;
  font-size: 18px;
  flex-shrink: 0;
}

/* Flash highlight when arriving via post-save redirect */
@keyframes lm-flash-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(var(--accent-rgb, 143, 224, 0), 0.7); }
  50%  { box-shadow: 0 0 0 12px rgba(var(--accent-rgb, 143, 224, 0), 0); }
  100% { box-shadow: 0 0 0 0 rgba(var(--accent-rgb, 143, 224, 0), 0); }
}
.lm-flash {
  animation: lm-flash-pulse 1.1s ease-out 2;
  border: 2px solid var(--accent-600);
}

/* Status-action buttons on the edit page header — Publish/Pause/Submit-for-approval
   should keep a consistent footprint regardless of label length. */
.vp-btn-status {
  min-width: 200px;
  justify-content: center;
}

/* Vendor dashboard order-summary cards */
.vp-order-card {
  display: block;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-4) var(--space-5);
  text-decoration: none; color: var(--text-primary);
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
  border-left: 4px solid var(--border);
}
.vp-order-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(18, 26, 58, 0.08);
  color: var(--text-primary);
}
.vp-order-card-head {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--fs-small); color: var(--text-secondary);
  margin-bottom: var(--space-2);
}
.vp-order-card-icon {
  width: 28px; height: 28px;
  display: grid; place-items: center;
  border-radius: 999px;
  background: var(--bg-subtle);
  font-size: 14px;
}
.vp-order-card-label { font-weight: 600; letter-spacing: 0.01em; font-size: var(--fs-small); }
.vp-order-card-num   { font-size: 32px; font-weight: 700; line-height: 1; margin: 4px 0; }
.vp-order-card-value { font-size: var(--fs-body); color: var(--text-secondary); font-weight: 600; }
.vp-order-card-cta   {
  margin-top: var(--space-3); padding-top: var(--space-3);
  border-top: 1px solid var(--border);
  font-size: var(--fs-small); font-weight: 600; color: var(--primary-900);
  display: inline-flex; align-items: center; gap: 4px;
}

/* Status accents for the left border + icon background */
.vp-order-card-pending     { border-left-color: #FFA500; }
.vp-order-card-pending     .vp-order-card-icon { background: rgba(255, 165, 0, 0.15); color: #FFA500; }
.vp-order-card-accepted    { border-left-color: var(--accent-600); }
.vp-order-card-accepted    .vp-order-card-icon { background: rgba(var(--accent-rgb, 143, 224, 0), 0.18); color: var(--accent-600); }
.vp-order-card-in-progress { border-left-color: #2D72FF; }
.vp-order-card-in-progress .vp-order-card-icon { background: rgba(45, 114, 255, 0.15); color: #2D72FF; }
.vp-order-card-completed   { border-left-color: var(--primary-900); }
.vp-order-card-completed   .vp-order-card-icon { background: rgba(18, 26, 58, 0.10); color: var(--primary-900); }

/* ── Vendor dashboard v2 ───────────────────────────────────────────────
   Operational-first layout: greeting → action queue → pipeline → wallet
   → health → todo → quick actions. All cards token-driven so theme +
   dark-mode flow through cleanly. */

.vp-greeting {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--space-4); flex-wrap: wrap;
  margin-bottom: var(--space-4);
}
.vp-greeting-text { flex: 1; min-width: 240px; }
.vp-greeting-date {
  display: block;
  font-size: var(--fs-small);
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
  margin-bottom: 4px;
}
.vp-greeting-text h1 {
  font-size: 26px; font-weight: 800; color: var(--text-primary);
  margin: 0 0 var(--space-2);
  letter-spacing: -0.01em;
}
.vp-greeting-summary {
  margin: 0; color: var(--text-secondary); font-size: var(--fs-body);
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2);
}
.vp-greeting-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  font-size: var(--fs-small);
  font-weight: 600;
  text-decoration: none;
  border: 1px solid transparent;
  transition: all var(--transition);
}
.vp-greeting-pill i { font-size: 13px; }
.vp-greeting-pill.warning { background: rgba(255, 165, 0, .12); color: #B5710A; border-color: rgba(255, 165, 0, .35); }
.vp-greeting-pill.success { background: rgba(var(--accent-rgb, 143, 224, 0), .14); color: var(--accent-600); border-color: rgba(var(--accent-rgb, 143, 224, 0), .35); }
.vp-greeting-pill.info    { background: rgba(45, 114, 255, .10); color: #1E54C7; border-color: rgba(45, 114, 255, .30); }
.vp-greeting-pill:hover { transform: translateY(-1px); }

html[data-theme="dark"] .vp-greeting-pill.warning { color: #FFB350; }
html[data-theme="dark"] .vp-greeting-pill.info    { color: #7BA0FF; }

.vp-greeting-actions { display: flex; gap: var(--space-2); flex-shrink: 0; }

/* Action queue cards — fill the most common operational job first */
.vp-action-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-5);
  display: flex; flex-direction: column;
  height: 100%;
}
.vp-action-card-head {
  display: flex; align-items: center; gap: var(--space-3);
  margin-bottom: var(--space-3);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--border);
}
.vp-action-card-icon {
  width: 36px; height: 36px; border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--bg-subtle); color: var(--text-secondary);
  font-size: 18px;
}
.vp-action-card-icon.urgent { background: rgba(245, 158, 11, .12); color: #FFA500; }
.vp-action-card-icon.success { background: rgba(var(--accent-rgb, 143, 224, 0), .14); color: var(--accent-600); }
.vp-action-card-title { flex: 1; }
.vp-action-card-title h6 {
  margin: 0; font-size: 14px; font-weight: 700; color: var(--text-primary);
}
.vp-action-card-title small { color: var(--text-secondary); font-size: var(--fs-small); }

.vp-action-card-list { flex: 1; }
.vp-action-card-foot {
  display: flex; gap: var(--space-2); justify-content: flex-end; flex-wrap: wrap;
  padding-top: var(--space-3); margin-top: var(--space-3);
  border-top: 1px solid var(--border);
}

/* SLA badge — colour-coded countdown for pending inquiries */
.vp-sla-badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  font-size: 11px; font-weight: 600;
  white-space: nowrap;
}
.vp-sla-badge.ok      { background: rgba(var(--accent-rgb, 143, 224, 0), .14); color: var(--accent-600); }
.vp-sla-badge.warning { background: rgba(255, 165, 0, .14); color: #FFA500; }
.vp-sla-badge.danger  { background: rgba(220, 53, 69, .12);  color: #dc3545; }
.vp-sla-badge i { font-size: 11px; }

/* Order pipeline — hero stat strip: dark navy tiles with a lime icon badge.
   Matches the brand identity of the original "Account Balance / My Orders /
   My Customers / My Listings" cards. */
.vp-pipeline {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-3);
  position: relative;
}
@media (max-width: 992px) {
  .vp-pipeline { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 480px) {
  .vp-pipeline { grid-template-columns: 1fr; }
}
.vp-pipeline-step {
  position: relative;
  background: var(--primary-900);
  border: 1px solid var(--primary-900);
  border-radius: var(--radius-card);
  padding: var(--space-5);
  text-decoration: none;
  color: #fff;
  min-height: 132px;
  transition: transform var(--transition), background var(--transition);
}
.vp-pipeline-step:hover {
  transform: translateY(-2px);
  background: var(--primary-700);
  color: #fff;
}
.vp-pipeline-step .vp-pipeline-icon {
  position: absolute;
  top: var(--space-4); right: var(--space-4);
  width: 40px; height: 40px;
  border-radius: 10px;
  background: var(--accent-500);
  /* Adaptive foreground — dark on lime/yellow/amber, white on
     purple/pink/navy. Setting::readableForeground picks the right one
     based on the accent's luminance, no hardcoded value. */
  color: var(--accent-fg, #1A1A1A);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 18px;
}
.vp-pipeline-step .label {
  font-size: var(--fs-small); font-weight: 600;
  color: rgba(255, 255, 255, 0.72);
  margin: 0 56px var(--space-3) 0; /* room for icon */
  letter-spacing: 0.01em;
}
.vp-pipeline-step .count {
  font-size: 32px; font-weight: 800; line-height: 1;
  color: #fff;
  margin-bottom: 6px;
}
.vp-pipeline-step .count .currency {
  font-size: 14px; font-weight: 600; color: rgba(255, 255, 255, 0.72);
  margin-right: 4px; vertical-align: 6px;
}
.vp-pipeline-step .value {
  font-size: var(--fs-small);
  color: rgba(255, 255, 255, 0.6);
}

/* Health row — rating + revenue + conversion in a balanced grid */
.vp-health-grid {
  display: grid;
  grid-template-columns: minmax(280px, 1fr) 2fr;
  gap: var(--space-3);
}
@media (max-width: 992px) {
  .vp-health-grid { grid-template-columns: 1fr; }
}
.vp-rating-card { padding: var(--space-4); }
.vp-rating-card .display {
  font-size: 40px; font-weight: 800; line-height: 1; color: var(--text-primary);
}
.vp-rating-stars i { color: #FFB800; font-size: 14px; }
.vp-rating-stars .empty { color: var(--text-secondary); }
.vp-rating-bars { margin-top: var(--space-3); }
.vp-rating-row {
  display: grid;
  /* Original was `28px 1fr 28px` — that gave the LABEL column only 28px
     so "On-time", "Cancel rate", "Response p50" wrapped to two lines and
     broke the card layout. Wider label column + slightly wider value
     column so 100% / "—m" / "120m" don't truncate. */
  grid-template-columns: 84px 1fr 44px;
  align-items: center; gap: var(--space-2);
  font-size: var(--fs-small); color: var(--text-secondary);
  margin-bottom: 6px;
}
.vp-rating-row > :last-child { text-align: right; font-variant-numeric: tabular-nums; }
.vp-rating-row .bar {
  height: 6px; border-radius: 999px; background: var(--bg-subtle); overflow: hidden;
}
.vp-rating-row .bar > span {
  display: block; height: 100%; background: var(--accent-500);
  border-radius: inherit;
  transition: width .25s ease;
}

.vp-quick-actions {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-2);
  margin-top: var(--space-3);
}
@media (max-width: 768px) {
  .vp-quick-actions { grid-template-columns: repeat(2, 1fr); }
}
.vp-quick-actions a {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  color: var(--text-primary); text-decoration: none;
  font-weight: 500; font-size: var(--fs-body);
  transition: all var(--transition);
}
.vp-quick-actions a:hover {
  border-color: var(--accent-500);
  color: var(--text-primary);
}
.vp-quick-actions a i {
  color: var(--accent-600); font-size: 18px;
}

/* Filter toolbar second row (My Orders) — date range, sort, etc. */
.vp-toolbar-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2);
  width: 100%;
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
  margin-top: var(--space-3);
}
.vp-toolbar-row label { margin: 0; }

/* ── Compact filter bar (My Orders) ────────────────────────────────────
   Single-row toolbar with a collapsible advanced-filters panel. Saves
   vertical space vs. the legacy multi-row layout. */
.vp-filter-bar {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  margin-bottom: var(--space-3);
}
.vp-filter-bar > .collapse,
.vp-filter-bar > .collapsing { flex-basis: 100%; width: 100%; }
.vp-filter-bar .vp-search-inline { flex: 1 1 240px; max-width: 420px; min-width: 200px; }
.vp-filter-toggle {
  display: inline-flex; align-items: center; gap: 6px;
  height: 36px; padding: 0 var(--space-3);
  background: var(--bg-card); color: var(--text-primary);
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  font-size: var(--fs-body); font-weight: 500; cursor: pointer;
  transition: background var(--transition), border-color var(--transition);
}
.vp-filter-toggle:hover { background: var(--bg-subtle); border-color: var(--text-secondary); }
.vp-filter-toggle[aria-expanded="true"] { background: var(--bg-subtle); border-color: var(--primary-900); }
.vp-filter-toggle .vp-filter-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px; padding: 0 6px;
  background: var(--accent-500); color: var(--text-primary);
  border-radius: var(--radius-pill);
  font-size: 11px; font-weight: 700;
  margin-left: 2px;
}
.vp-filter-sort {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-small); color: var(--text-secondary);
}
.vp-filter-sort select {
  width: auto; min-width: 180px; height: 36px;
  border-radius: var(--radius-pill);
  font-size: var(--fs-body); font-weight: 500;
}
.vp-filter-actions {
  display: inline-flex; align-items: center; gap: var(--space-2);
  margin-left: auto;
}
.vp-filter-actions .vp-btn-primary,
.vp-filter-actions .vp-btn-ghost { height: 36px; }

.vp-filter-panel {
  width: 100%;
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: var(--space-4);
  margin-top: var(--space-1);
}
.vp-filter-panel-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-3);
}
.vp-filter-panel label {
  display: block;
  font-size: var(--fs-small); font-weight: 600;
  color: var(--text-secondary);
  margin-bottom: 6px;
  letter-spacing: 0.02em;
}
.vp-filter-panel .vp-filter-date-range {
  display: flex; gap: var(--space-2); align-items: center;
}
.vp-filter-panel .vp-filter-date-range input { flex: 1; min-width: 0; }
.vp-filter-panel .vp-filter-date-range .vp-filter-date-sep {
  font-size: var(--fs-small); color: var(--text-secondary);
}

/* Assigned-staff pill in the My Orders table */
.vp-staff-pill {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 10px;
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  font-size: 11px; font-weight: 600;
}
.vp-staff-pill i { font-size: 11px; color: var(--text-secondary); }

/* Legal-document modal — prevents the body of the legal article from
   inheriting page-level container width when injected into a Bootstrap modal. */
.legal-modal .modal-content { border-radius: var(--radius-card); }
.legal-modal .modal-body { padding: var(--space-6); max-height: 70vh; }
.legal-modal .legal-document-fragment { max-width: 100%; }
.legal-modal .legal-document-fragment .legal-section { margin-bottom: var(--space-5); }
.legal-modal .legal-document-fragment h2 { font-size: var(--fs-h3); margin-top: var(--space-3); }
.legal-modal .legal-document-fragment h3 { font-size: var(--fs-body); font-weight: 700; margin-top: var(--space-3); }
.legal-modal .legal-meta .badge { background: var(--bg-subtle) !important; color: var(--text-primary) !important; border: 1px solid var(--border); }

/* ── Dark mode tokens ────────────────────────────────────────────────────
   Activated by the html[data-theme="dark"] attribute. Brand primary, accent,
   font, and sizes stay shared with light mode (set via :root); only the
   neutral surfaces and text invert. The admin Theme editor still drives
   light-mode bg/card/brand picks; dark mode uses this curated palette. */
/* Plan-search icons use the dark-navy primary in light mode for brand
   continuity; on a dark surface that same navy disappears, so we swap to
   the lime accent which has comfortable contrast on bg-card / bg-subtle. */
html[data-theme="dark"] .plan-search-field label i {
  color: var(--accent-500);
}

/* Category-grid icons — same story: in light mode the dark text-primary
   reads great inside the subtle circle; in dark mode that flips to white
   which feels flat. Lime gives the grid a bit of personality and reuses
   the brand accent without straying off-palette. */
html[data-theme="dark"] .amboi-cat-card .icon {
  color: var(--accent-500);
}

/* Table reference IDs (booking refs, event order codes, dispute IDs,
   service titles in vendor tables, etc.) use --primary-900 in light mode
   for the strong dark-navy "this is a clickable identifier" treatment.
   In dark mode that token is remapped to a lifted navy (#2A3672) which
   has barely any contrast against the dark surfaces — so these cells
   become unreadable. Switching to the lime accent in dark mode keeps the
   "this stands out / is a link" intent while restoring legibility.
   Affects every table that uses .vp-table-id (~10 customer + vendor
   surfaces). */
html[data-theme="dark"] .vp-table-id {
  color: var(--accent-500);
}
html[data-theme="dark"] .vp-table-id:hover {
  color: var(--accent-600);
}

/* Notification dropdown — each row's leading icon uses --primary-900 in
   light mode (subtle navy on white). In dark mode that disappears against
   the dark dropdown surface, so we reuse the lime accent for visibility,
   matching the rest of the dark-mode icon adaptations. */
html[data-theme="dark"] .notification-item i {
  color: var(--accent-500);
}

html[data-theme="dark"] {
  color-scheme: dark;
  --bg-main:    #0F1117;
  --bg-card:    #1A1D24;
  --bg-subtle:  #22252D;
  --border:     #353A45;
  --text-primary:   #F0F1F4;
  /* Lifted from #A0A4AE → #C0C5CE so 12px secondary text clears WCAG AAA
     (~9.5:1 on bg-card) — fixes the "hard to read muted text" report */
  --text-secondary: #C0C5CE;
  /* Brand-derived dark surfaces — were hardcoded `#2A3672` / `#3B4892`
     (lifted navy tuned for Lime Classic). Those literals ignored the
     active palette, so under Rekaizen the hero panel + dashboard CTAs
     kept rendering as off-brand purple-navy.
     `color-mix` lets us derive a deep teal-tinted surface from the
     active accent without any new tokens or PHP plumbing — falls back
     gracefully on older browsers via the @supports guard below.
     For Rekaizen (#51B4BE) the result is ~#1B3036 / ~#2D5862 —
     visible against bg-main #0F1117, brand-on. Same maths gives deep
     purple under Vibrant 5 and deep lime under Lime Classic. */
  --primary-900: color-mix(in srgb, var(--accent-500) 25%, #0A0D14);
  --primary-700: color-mix(in srgb, var(--accent-500) 42%, #1A1D24);
  /* In light mode --accent-700 is a DARKER shade of accent (for text
     on white). In dark mode that darker shade becomes invisible
     against dark surfaces — flip it to a LIGHTER variant so accent-
     coloured text stays readable. 65% accent + 35% white lands a
     pale-saturated tint that reads cleanly on bg-card / bg-subtle
     for every palette (lime, teal, purple, custom). Affects every
     element that uses `color: var(--accent-700)` — response-time
     pill, top-vendor commission, mid-tier KPI accents, etc. */
  --accent-700: color-mix(in srgb, var(--accent-500) 65%, #FFFFFF);
  /* Bootstrap re-mapping under dark — body/border/link colors */
  --bs-body-bg: var(--bg-main);
  --bs-body-color: var(--text-primary);
  --bs-secondary-color: var(--text-secondary);
  --bs-tertiary-bg: var(--bg-subtle);
  --bs-border-color: var(--border);
  --bs-link-color: var(--text-primary);
  --bs-link-hover-color: var(--accent-500);
  --bs-emphasis-color: var(--text-primary);
}

/* Bootstrap component surfaces that don't read tokens by default */
html[data-theme="dark"] .modal-content,
html[data-theme="dark"] .dropdown-menu,
html[data-theme="dark"] .offcanvas,
html[data-theme="dark"] .accordion-item,
html[data-theme="dark"] .accordion-button,
html[data-theme="dark"] .list-group-item {
  background-color: var(--bg-card);
  color: var(--text-primary);
  border-color: var(--border);
}
html[data-theme="dark"] .accordion-button:not(.collapsed) {
  background-color: var(--bg-subtle);
  color: var(--text-primary);
  box-shadow: inset 0 calc(-1 * 1px) 0 var(--border);
}
html[data-theme="dark"] .accordion-button::after {
  filter: invert(0.85);
}
html[data-theme="dark"] .form-control,
html[data-theme="dark"] .form-select,
html[data-theme="dark"] .input-group-text {
  background-color: var(--bg-subtle);
  color: var(--text-primary);
  border-color: var(--border);
}
html[data-theme="dark"] .form-control::placeholder { color: var(--text-secondary); }

/* ── Native <select> popup options in dark mode ────────────────────────
   The plan-search hero form (Event type / Where) uses bare `<select>`
   styled by `.plan-search-field select`, not `.form-select` — so the
   dark-mode block above misses them. Without explicit option styling,
   the native popup falls back to the browser default (near-black text)
   which renders invisible on the dark popup background.

   We target BOTH the visible select (which is mostly transparent and
   inherits page color) AND the <option>/<optgroup> children explicitly,
   because the dropdown popup is a native widget and won't read the
   page's CSS tokens through inheritance reliably across Chromium /
   Firefox / Safari.

   Coverage: any bare <select> inside .plan-search-field, plus any
   <option> globally — option styling is safe to apply broadly because
   the dropdown popup is the same visual on every page. */
html[data-theme="dark"] .plan-search-field select,
html[data-theme="dark"] select {
  color-scheme: dark;
}
html[data-theme="dark"] option,
html[data-theme="dark"] optgroup {
  background-color: var(--bg-card);
  color: var(--text-primary);
}
html[data-theme="dark"] optgroup {
  /* optgroup labels render dimmer + italic by default; lift them to
     secondary so the "Personal" / "Religious" group headings in the
     event-type dropdown are still legible on the dark popup. */
  color: var(--text-secondary);
  font-style: italic;
}
/* Native <select> option highlight states — split into TWO intents:
     • :hover / :focus     → dark navy (var(--primary-900)). Calmer than
                              the OS-default electric blue, signals
                              "you're considering this" without shouting.
     • :checked            → brand teal (var(--accent-500)). Affirms
                              "this is the selected value."

   Source-order priority: :checked is listed LAST so a hover over the
   already-selected option keeps the teal cue (both rules carry the
   same specificity; the later rule wins). Both use !important so the
   OS / Bootstrap defaults can't reclaim them.

   Browser-support caveat: modern Chromium/Firefox/Safari respect
   option background-color in both inline <select size="N"> listboxes
   and popup pickers. Older Chrome (<115) and iOS/Android native pickers
   are OS-rendered and ignore the styling — pixel parity there needs a
   JS replacement (Tom Select / Choices.js). */
option:hover,
option:focus {
  background-color: var(--primary-900) !important;
  color: #fff !important;
}
option:checked {
  background-color: var(--accent-500) !important;
  color: var(--accent-fg, #fff) !important;
}

/* Plan-search dropdown chevron — the inline SVG hardcodes a dark-navy
   stroke (#121A3A) that disappears against the dark surface in dark
   mode. Swap to a light variant so the indicator stays visible. */
html[data-theme="dark"] .plan-search-field select {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path d='M1 1l4 4 4-4' stroke='%23C0C5CE' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>");
}
/* Bottom-sheet exception: the .plan-search-sheet variant uses
   appearance:auto so the OS draws the native dropdown chrome (matches
   the Filters sheet next door). The rule above re-adds a custom
   background-image arrow, which — when paired with appearance:auto on
   Chromium/Windows — triggers the hatched "broken control" fallback
   pattern. Drop the custom arrow inside the sheet so the native one
   renders cleanly. Specificity (0,3,2) beats the dark arrow rule (0,2,2). */
html[data-theme="dark"] .plan-search-sheet .plan-search-field select {
  background-image: none;
}
/* Bootstrap's .btn-close uses a baked-in dark SVG ("X" mark drawn in
   #000) that goes invisible against the dark surfaces in dark mode
   (offcanvas sheets, modals, dismissible alerts). Bootstrap ships a
   .btn-close-white modifier but it has to be added per-instance —
   easy to forget. Applied globally here so every close button paints
   visible in dark mode. invert(1) flips the black-on-transparent SVG
   to white-on-transparent; the opacity bumps make the resting state
   readable and the hover/focus states match Bootstrap's defaults. */
html[data-theme="dark"] .btn-close {
  filter: invert(1) grayscale(100%) brightness(200%);
}

/* ── Admin Revenue breakdown redesign ───────────────────────────────────
   Replaces the 2x2 grid + thin-progress-bar mishmash with three clean
   subsections: hero trend chart, by-source list, top-vendors list.
   Visual language matches the Platform GMV chart above so the two
   read as one coherent design language. */

/* Inline legend dots above the trend chart. */
.vp-revenue-dot {
  display: inline-block;
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--accent-500);
  margin-right: 6px;
  vertical-align: middle;
}
.vp-revenue-dot--dashed {
  width: 16px;
  height: 2px;
  border-radius: 0;
  background: transparent;
  border-top: 2px dashed var(--text-primary);
  vertical-align: middle;
  margin-right: 6px;
}

/* By-source list — replaces the prior <table> with thin progress bars.
   Each row reads as: dot · label · proportional bar · value. The bar
   sits on its own track so the value column aligns regardless of label
   length. */
.vp-revenue-source-list {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.vp-revenue-source-row {
  display: grid;
  grid-template-columns: 12px minmax(0, 1fr) minmax(80px, 200px) 70px;
  align-items: center;
  gap: 10px;
  font-size: 13px;
}
.vp-revenue-source-dot {
  width: 10px; height: 10px;
  border-radius: 50%;
  display: inline-block;
}
.vp-revenue-source-label {
  color: var(--text-primary);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.vp-revenue-source-bar {
  height: 6px;
  border-radius: 999px;
  background: var(--bg-subtle);
  overflow: hidden;
  position: relative;
}
.vp-revenue-source-bar > span {
  display: block;
  height: 100%;
  border-radius: 999px;
  transition: width .3s ease;
  min-width: 2px;
}
.vp-revenue-source-value {
  text-align: right;
  font-weight: 700;
  font-size: 13px;
  color: var(--text-primary);
}
.vp-revenue-source-row.is-outflow .vp-revenue-source-value { color: #DC2626; }
@media (max-width: 575.98px) {
  .vp-revenue-source-row {
    grid-template-columns: 12px minmax(0, 1fr) 70px;
    row-gap: 4px;
  }
  .vp-revenue-source-bar { grid-column: 2 / 4; }
}

/* Top vendors list — replaces the scrollable <table> with a clean
   linkable row treatment. Rank badge on the left, vendor name + meta
   in the middle, commission value right-aligned. */
.vp-revenue-vendor-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.vp-revenue-vendor-row {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-radius: var(--radius-input);
  text-decoration: none;
  color: var(--text-primary);
  transition: background-color .15s ease;
}
.vp-revenue-vendor-row:hover {
  background: var(--bg-subtle);
  color: var(--text-primary);
}
.vp-revenue-vendor-rank {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px;
  border-radius: 50%;
  background: rgba(var(--accent-rgb, 81, 180, 190), 0.12);
  color: var(--accent-700, var(--accent-500));
  font-size: 11px;
  font-weight: 800;
}
.vp-revenue-vendor-text { min-width: 0; }
.vp-revenue-vendor-name {
  font-weight: 600;
  font-size: 13.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.vp-revenue-vendor-meta {
  font-size: 11.5px;
  color: var(--text-secondary);
  margin-top: 1px;
}
.vp-revenue-vendor-value {
  font-weight: 700;
  font-size: 14px;
  color: var(--tertiary-500, #E08E50);
  white-space: nowrap;
}

/* ── Admin "System health" status bar ──────────────────────────────────
   Lives at the top of the admin dashboard now (was previously at the
   bottom). Treated as a GitHub-style status row: a single coloured LED
   in the header summarises the worst-status check below, so admins
   can scan the platform health in <2 seconds without reading every
   pill. Border-left tints match the LED for peripheral-vision habit. */
.admin-status-bar { border-left: 4px solid var(--border); }
.admin-status-bar.tone-ok       { border-left-color: #16A34A; }
.admin-status-bar.tone-warning  { border-left-color: #D97706; }
.admin-status-bar.tone-error    { border-left-color: #DC2626; }
.admin-status-led {
  display: inline-block;
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--border);
  margin-right: 6px;
  vertical-align: middle;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.04);
}
.admin-status-led.tone-ok       { background: #16A34A; box-shadow: 0 0 0 2px rgba(22, 163, 74, 0.18); }
.admin-status-led.tone-warning  { background: #D97706; box-shadow: 0 0 0 2px rgba(217, 119, 6, 0.18); }
.admin-status-led.tone-error    {
  background: #DC2626;
  box-shadow: 0 0 0 2px rgba(220, 38, 38, 0.18);
  animation: admin-status-pulse 2s ease-in-out infinite;
}
@keyframes admin-status-pulse {
  0%, 100% { box-shadow: 0 0 0 2px rgba(220, 38, 38, 0.18); }
  50%      { box-shadow: 0 0 0 6px rgba(220, 38, 38, 0.12); }
}
@media (prefers-reduced-motion: reduce) {
  .admin-status-led.tone-error { animation: none; }
}

/* ── Global "Back to top" floating button ─────────────────────────────────
   Mounts once per layout via partials/back-to-top.blade.php. CSS keeps it
   invisible by default; the partial's JS toggles `.is-visible` once the
   user has scrolled past 400px so short pages never see it. Sits in the
   Bootstrap fixed-layer (z-index: 1030) so offcanvas/modal backdrops cover
   it correctly when they open. Mobile breakpoint reduces size + margin so
   it doesn't crowd thumb-zone affordances. */
.amboi-back-to-top {
  position: fixed;
  /* Sits VERTICALLY above the right:20px column where support/chat
     widgets stack (support bubble at bottom:20px right:20px, vendor chat
     at right:96px, admin support at right:172px). Going up (bottom:84px)
     instead of left avoids the per-page conditional layout dance —
     this single position works on pages with no widgets too. */
  bottom: 84px;
  right: 20px;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 50%;
  background: var(--accent-500);
  color: var(--accent-fg, #fff);
  display: grid;
  place-items: center;
  font-size: 18px;
  line-height: 1;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
  cursor: pointer;
  opacity: 0;
  transform: translateY(8px);
  pointer-events: none;
  transition: opacity .2s ease, transform .2s ease, background-color .15s ease;
  z-index: 1030;
}
.amboi-back-to-top.is-visible {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.amboi-back-to-top:hover { background: var(--accent-600, var(--accent-500)); }
.amboi-back-to-top:focus-visible {
  outline: 0;
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb, 81, 180, 190), 0.4),
              0 6px 16px rgba(0, 0, 0, 0.15);
}
@media (max-width: 575.98px) {
  .amboi-back-to-top {
    bottom: 16px; right: 16px;
    width: 40px; height: 40px;
    font-size: 16px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .amboi-back-to-top { transition: opacity .1s ease; transform: none; }
}
html[data-theme="dark"] .table { --bs-table-bg: transparent; --bs-table-color: var(--text-primary); --bs-table-border-color: var(--border); }
html[data-theme="dark"] .table > :not(caption) > * > * { background-color: transparent; color: var(--text-primary); border-bottom-color: var(--border); }
html[data-theme="dark"] .alert { border-color: var(--border); }
html[data-theme="dark"] .alert-success { background-color: rgba(var(--accent-rgb, 143, 224, 0), 0.12); color: var(--text-primary); }
html[data-theme="dark"] .alert-warning { background-color: rgba(255, 196, 0, 0.12); color: var(--text-primary); }
html[data-theme="dark"] .alert-danger  { background-color: rgba(231, 76, 60, 0.12); color: var(--text-primary); }
html[data-theme="dark"] .badge.bg-light { background-color: var(--bg-subtle) !important; color: var(--text-primary) !important; }
html[data-theme="dark"] .text-muted { color: var(--text-secondary) !important; }

/* Subtle background helpers some views use */
html[data-theme="dark"] .bg-light { background-color: var(--bg-subtle) !important; }
html[data-theme="dark"] .bg-white { background-color: var(--bg-card) !important; }

/* ─────────────────────────────────────────────────────────────────────────
   Dark-mode hover/active contrast safety net.

   Pattern across the design system: hover and active states intentionally
   darken text to `var(--primary-900)` to lift it against the light card
   background — works perfectly in light mode where primary-900 is a deep
   ink against white. In dark mode `--primary-900` IS the page anchor (a
   near-black shade derived from the active accent), so the same rule
   silently renders text BLACK-ON-DARK and the element disappears.

   Specific cases this fixes:
     • Public-nav active link icon — user-flagged "selected icon barely
       visible." Bootstrap's `.nav-link.active` defaults to `color: black`
       and was never overridden for dark mode.
     • Story card headline on hover (`.stry-card-headline:hover`) —
       user-flagged "header topic on hover hard to read."
     • Generic anchor hover (`a:hover`) — affects every undecorated link
       on the page.
     • Public nav link hover, vp-auth-link hover, gallery uploader hover,
       lang-switcher button hover, vendor-strip identity name hover, etc.

   The fix lifts these to the brand accent on hover (visible on every
   palette: teal on Rekaizen, lime on Lime Classic, purple on Vibrant 5)
   and to accent on active state for the public nav. Light mode is
   untouched — the original "darken to primary-900" behaviour stays
   correct there.
   ───────────────────────────────────────────────────────────────────── */
/* .amboi-navbar .nav-link:hover removed from this safety net — text
   nav links now rely on the swipe-in underline alone (light + dark
   parity). The other selectors here still need the dark-mode flip
   because they don't have an underline affordance. */
html[data-theme="dark"] a:hover,
html[data-theme="dark"] .vp-auth-link:hover,
html[data-theme="dark"] .stry-card-headline:hover,
html[data-theme="dark"] .gallery-add:hover,
html[data-theme="dark"] .lang-switcher .btn-link:hover,
html[data-theme="dark"] .cv-vendor-strip-identity:hover .cv-vendor-strip-name {
    color: var(--accent-500);
}

/* Active public-nav link active treatment in dark mode — scoped to
   ICON-ONLY links (e.g. Stories <a><i></i></a>) via :has(). Text nav
   links already get the swipe-in underline as their active cue and
   don't need the colour-flip + pill bg, which would compete with the
   underline. Icon-only links have no text to underline, so the pill
   bg + accent glyph stays as their unambiguous "you're here" cue. */
html[data-theme="dark"] .amboi-navbar .nav-link.active:has(> i:only-child),
html[data-theme="dark"] .amboi-navbar .nav-link.active:has(> i:only-child) > i {
    color: var(--accent-500) !important;
}
html[data-theme="dark"] .amboi-navbar .nav-link.active:has(> i:only-child) {
    background: rgba(var(--accent-rgb, 81, 180, 190), 0.12);
    border-radius: var(--radius-input);
}

/* Availability calendar booking row — hover hardcodes `color: #000`
   (good on the lime resting bg of light mode, invisible in dark mode
   where the resting bg is teal-tinted on the dark page). Switch dark
   mode to the adaptive --accent-fg which the theme resolver computes
   from accent luminance. */
html[data-theme="dark"] .av-cal-booking:hover {
    color: var(--accent-fg, #fff);
}

/* Mobile hamburger toggler — Bootstrap bakes the SVG stroke colour into
   the .navbar-toggler-icon background-image. Light mode default is a
   near-black stroke (rgba(33,37,41,0.75)) which vanishes on the dark
   page bg. We swap to a white-stroked SVG in dark mode + drop the
   default dark hairline border so the button doesn't draw a faint
   square around an invisible icon. */
html[data-theme="dark"] .amboi-navbar .navbar-toggler {
    border-color: rgba(255, 255, 255, 0.18);
    color: var(--text-primary);
}
html[data-theme="dark"] .amboi-navbar .navbar-toggler:focus {
    box-shadow: 0 0 0 0.2rem rgba(var(--accent-rgb, 81, 180, 190), 0.25);
}
html[data-theme="dark"] .amboi-navbar .navbar-toggler-icon {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3E%3Cpath stroke='rgba(255, 255, 255, 0.85)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E");
}

/* Sidebars / topbar / cards already read the tokens, so the swap above
   propagates automatically. The dark-themed compositions (navy hero,
   .amboi-cta, .vp-banner.navy, vp-progress steps, btn-dark hover) keep
   white text against navy in both modes — that's intentional. */

/* ── Revenue card ───────────────────────────────────────────────────────
   Vendor dashboard's headline analytic surface. Period tabs (daily / weekly
   / monthly / yearly) refresh the chart + KPIs in place via AJAX. */
.vp-revenue-card { display: flex; flex-direction: column; }
.vp-revenue-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3); flex-wrap: wrap;
}
.vp-revenue-title { display: flex; flex-direction: column; gap: 2px; }
.vp-revenue-title small { font-size: 12px; }
/* Inline legend chip explaining the trend line on the revenue/spend chart.
   Renders as a solid amber stroke with a soft amber-to-transparent fade
   below it — same vibe as the chart's gradient area fill. */
.vp-trend-key {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; color: var(--text-secondary);
  white-space: nowrap;
}
.vp-trend-key-dash {
  display: inline-block;
  width: 22px; height: 8px;
  border-radius: 4px;
  background: linear-gradient(180deg, #F59E0B 0%, #F59E0B 28%, rgba(245, 158, 11, 0.18) 28%, rgba(245, 158, 11, 0) 100%);
}

.vp-revenue-tabs {
  display: inline-flex; padding: 3px; gap: 2px;
  background: var(--bg-subtle); border-radius: var(--radius-pill);
}
.vp-revenue-tab {
  appearance: none; border: 0; background: transparent;
  padding: 5px 12px; font-size: 12px; font-weight: 600;
  color: var(--text-secondary); border-radius: var(--radius-pill);
  cursor: pointer; transition: background-color var(--transition), color var(--transition);
}
.vp-revenue-tab:hover { color: var(--text-primary); }
.vp-revenue-tab.active {
  background: var(--bg-card); color: var(--text-primary);
  box-shadow: 0 1px 2px rgba(15, 23, 42, .08);
}

.vp-revenue-kpis {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-3); margin-bottom: var(--space-3);
}
@media (max-width: 720px) {
  .vp-revenue-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.vp-revenue-kpi {
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--space-2) 0;
  border-left: 2px solid var(--border);
  padding-left: var(--space-3);
}
.vp-revenue-kpi:first-child { border-left-color: var(--accent-500); }
.vp-revenue-kpi-label {
  font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-secondary); font-weight: 600;
}
.vp-revenue-kpi-value {
  font-size: 22px; font-weight: 800; line-height: 1.1;
  color: var(--text-primary);
}
.vp-revenue-kpi-value.tone-accent { color: var(--accent-600); }
.vp-revenue-kpi-value.tone-muted  { color: var(--text-primary); }
.vp-revenue-kpi-delta { font-size: 11px; margin-top: 2px; }
.vp-revenue-kpi-delta .up    { color: var(--accent-600); font-weight: 600; }
.vp-revenue-kpi-delta .down  { color: #DC2626; font-weight: 600; }
.vp-revenue-kpi-delta .muted { color: var(--text-secondary); }
.vp-revenue-kpi-delta i { font-size: 14px; vertical-align: -2px; }

.vp-revenue-chart-wrap {
  position: relative;
  min-height: 220px;
  height: 220px;
}
.vp-revenue-chart-wrap canvas { transition: opacity .2s ease; }
.vp-revenue-empty {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; pointer-events: none; gap: 4px;
}
.vp-revenue-empty i { font-size: 26px; color: var(--text-secondary); opacity: .6; }
.vp-revenue-empty-title { font-weight: 600; color: var(--text-primary); font-size: 13px; }
.vp-revenue-empty-sub { color: var(--text-secondary); font-size: 12px; max-width: 280px; }

.vp-revenue-loading {
  position: absolute; top: 8px; right: 8px;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius-pill); padding: 4px 10px;
  display: inline-flex; align-items: center;
}

.vp-revenue-foot {
  display: flex; align-items: center; justify-content: space-between;
  margin-top: var(--space-3); padding-top: var(--space-3);
  border-top: 1px solid var(--border); gap: var(--space-2); flex-wrap: wrap;
}

/* Theme toggle button — sun in dark mode, moon in light mode */
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: var(--radius-pill);
  background: transparent; border: 1px solid transparent;
  color: var(--text-primary); cursor: pointer;
  transition: background-color var(--transition), color var(--transition);
}

/* Floating corner for auth/legal/preview pages — these standalone
   layouts have no navbar to host the theme toggle, so we anchor it
   top-right of the viewport. 1px hairline border + subtle bg give it
   enough chrome to be discoverable without competing with the auth
   card. Tap target stays 36px (WCAG min); padding around the wrapper
   is for the safe area only. */
.amboi-auth-corner {
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: 10;
}
.amboi-auth-corner .theme-toggle {
  background: var(--bg-card);
  border-color: var(--border);
  box-shadow: 0 1px 2px rgba(15, 23, 42, .04);
}
.amboi-auth-corner .theme-toggle:hover {
  background: var(--bg-subtle);
}
@media (max-width: 575.98px) {
  .amboi-auth-corner { top: 12px; right: 12px; }
}
.theme-toggle:hover { background: var(--bg-subtle); }
.theme-toggle .bi-moon-stars-fill { display: inline-block; }
.theme-toggle .bi-sun-fill { display: none; }
html[data-theme="dark"] .theme-toggle .bi-moon-stars-fill { display: none; }
html[data-theme="dark"] .theme-toggle .bi-sun-fill { display: inline-block; }

/* ─────────────────────────────────────────────────────────────────────────
   Bootstrap subtle-badge readability safety net.
   Bootstrap's .badge defaults to color: #fff, which is invisible against
   the pastel `bg-*-subtle` backgrounds — readable only when the matching
   `text-*-emphasis` class is also applied. This safety net auto-pairs the
   emphasis colour for any subtle-bg badge that forgot it.

   :where() keeps specificity at 0,0,0 so explicit text-* utilities (e.g.
   text-info-emphasis, text-white, text-body) always win when authored.
   ───────────────────────────────────────────────────────────────────────── */
:where(.badge.bg-primary-subtle)   { color: var(--bs-primary-text-emphasis); }
:where(.badge.bg-secondary-subtle) { color: var(--bs-secondary-text-emphasis); }
:where(.badge.bg-success-subtle)   { color: var(--bs-success-text-emphasis); }
:where(.badge.bg-danger-subtle)    { color: var(--bs-danger-text-emphasis); }
:where(.badge.bg-warning-subtle)   { color: var(--bs-warning-text-emphasis); }
:where(.badge.bg-info-subtle)      { color: var(--bs-info-text-emphasis); }

/* ─────────────────────────────────────────────────────────────────────────
   Vendor profile editor — chip multi-select + working-hours grid.
   Used by vendor/onboarding/profile.blade.php for service-areas (state
   chips), languages-spoken, and the day-of-week operating-hours matrix.
   ───────────────────────────────────────────────────────────────────────── */

/* Chip multi-select — checkboxes rendered as toggle pills. The native
   <input> is hidden but stays focusable so keyboard nav and screen readers
   work; the visible <span> is what the user sees. */
.vp-chip-group {
    display: flex; flex-wrap: wrap; gap: 8px;
    margin-top: 6px;
}
.vp-chip {
    position: relative;
    display: inline-flex; align-items: center; gap: 6px;
    padding: 6px 12px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 999px;
    font-size: 13px; font-weight: 500;
    color: var(--text-primary);
    cursor: pointer;
    transition: background-color .12s ease, border-color .12s ease, color .12s ease;
    user-select: none;
}
.vp-chip:hover { background: var(--bg-subtle); border-color: var(--text-secondary); }
.vp-chip input[type="checkbox"] {
    position: absolute; opacity: 0; pointer-events: none;
    width: 1px; height: 1px;   /* keep focusable but invisible */
}
.vp-chip:has(input:checked),
.vp-chip.is-active {
    background: var(--accent-500);
    border-color: var(--accent-500);
    color: var(--accent-fg, #1A1A1A);
}
.vp-chip:has(input:focus-visible) {
    outline: 2px solid var(--accent-500);
    outline-offset: 2px;
}

/* Inputs with a small icon prefix in the label (used for social platforms) */
.vp-input-with-prefix label i {
    margin-right: 6px;
    color: var(--accent-600, var(--accent-500));
}

/* Working-hours weekly grid — one row per day with a checkbox toggle and
   open/close time inputs. Times are dimmed when the day is unticked. */
.vp-hours-grid {
    display: flex; flex-direction: column; gap: 8px;
}
.vp-hours-row {
    display: grid;
    grid-template-columns: 160px 1fr;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 10px;
}
.vp-hours-toggle {
    display: inline-flex; align-items: center; gap: 8px;
    cursor: pointer;
    font-size: 14px;
    color: var(--text-primary);
    margin: 0;
}
.vp-hours-toggle input[type="checkbox"] {
    width: 16px; height: 16px;
    accent-color: var(--accent-500);
    cursor: pointer;
}
.vp-hours-day { font-weight: 600; }
.vp-hours-times {
    display: flex; align-items: center; gap: 8px;
    min-width: 0;
}
.vp-hours-times input[type="time"] {
    border: 1px solid var(--border);
    background: var(--bg-subtle);
    color: var(--text-primary);
    border-radius: 6px;
    padding: 6px 10px;
    font-size: 13px;
    font-family: inherit;
    min-width: 110px;
}
.vp-hours-sep { color: var(--text-secondary); font-size: 12px; }

/* Closed-day visual — when the toggle is off, dim the time inputs so the
   row reads as inactive. :has() lets us style siblings based on the
   checkbox state without JS. */
.vp-hours-row:has(input[data-hours-toggle]:not(:checked)) .vp-hours-times {
    opacity: 0.45;
    pointer-events: none;
}

@media (max-width: 600px) {
    .vp-hours-row {
        grid-template-columns: 1fr;
        gap: 8px;
    }
    .vp-hours-times input[type="time"] { flex: 1; min-width: 0; }
}

/* ─────────────────────────────────────────────────────────────────────────
   Customer-view (public vendor profile) extensions for the new fields:
   contact info grid, working-hours list, social-link row.
   Used by vendor/partials/customer-view.blade.php.
   ───────────────────────────────────────────────────────────────────────── */

/* Contact + reach grid — 2 columns on desktop, 1 on mobile. */
.cv-info-grid {
    display: grid;
    /* auto-fit lets the grid pack as many columns as fit (min 240px wide).
       On wide cards (~700px+) this gives 3 columns → Replies + Lead time +
       Travel sit on one row cleanly instead of one being stranded with an
       empty cell beside it. On narrower cards it falls back to 2 then 1
       column gracefully. Wide rows (.cv-info-row-wide) keep spanning the
       full available width via grid-column: 1 / -1. */
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 10px 24px;
    margin: 8px 0 4px;
}
.cv-info-row {
    /* Refactoring UI "Labels are a last resort": the icon doubles as
       the semantic label (envelope = email, phone = phone, geo-alt =
       address). Visible text label was redundant chrome — collapsed
       the grid to 2 columns (icon + value). The .cv-info-label markup
       stays in the DOM for screen readers via visually-hidden — keeps
       semantic context without visual weight. */
    display: grid;
    grid-template-columns: 24px 1fr;
    align-items: center;
    gap: 10px;
    font-size: 14px;
    min-width: 0;
}
.cv-info-row > i { color: var(--accent-600, var(--accent-500)); font-size: 16px; }
.cv-info-row-wide { grid-column: 1 / -1; }
/* Visually-hidden — preserved for screen-reader announcement of
   the field meaning, removed from visual layout. Matches Bootstrap's
   .visually-hidden pattern. */
.cv-info-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}
.cv-info-val { color: var(--text-primary); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cv-info-val a { color: var(--text-primary); text-decoration: none; border-bottom: 1px dotted var(--text-secondary); }
.cv-info-val a:hover { color: var(--accent-600, var(--accent-500)); border-bottom-color: var(--accent-500); }
@media (max-width: 600px) {
    .cv-info-grid { grid-template-columns: 1fr; }
    .cv-info-row, .cv-info-row-wide { grid-column: auto; }
}

/* Locked-contact notice — replaces the leaked email/phone/WhatsApp/website
   rows for public viewers and not-yet-customers. Soft navy panel with a
   shield icon to communicate the "this protects you" framing rather than
   the "we're hiding things from you" framing. */
.cv-locked-contact {
    display: flex; align-items: flex-start; gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
    background: rgba(18, 26, 58, 0.05);
    border: 1px solid rgba(18, 26, 58, 0.10);
    border-radius: var(--radius-card);
}
.cv-locked-contact-icon {
    flex: 0 0 36px; width: 36px; height: 36px;
    display: grid; place-items: center;
    background: var(--primary-900); color: var(--accent-500);
    border-radius: var(--radius-input);
    font-size: 18px;
}
.cv-locked-contact-body { flex: 1; }
.cv-locked-contact-body strong { display: block; color: var(--text-primary); margin-bottom: var(--space-1); }
html[data-theme="dark"] .cv-locked-contact {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.08);
}

/* ──────────  Direct-contact disclosure (tiered reveal)  ──────────
   Wraps the email/phone/WhatsApp/address rows in a <details>/<summary>
   that's CLOSED by default even when the deposit-gate has cleared.
   Friction here is intentional: even after unlock, contact info stays
   one click away to protect the vendor on shared screens, in
   screenshots, and during accidental scrolling. */
.cv-contact-disclose {
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    background: var(--bg-card);
    overflow: hidden;
}
/* Summary acts as the toggle row — full-width clickable, lime icon
   on the left to mirror the locked-state lock icon, chevron on the
   right rotates when open. */
.cv-contact-disclose-summary {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    cursor: pointer;
    list-style: none;
    user-select: none;
    transition: background-color var(--transition);
}
.cv-contact-disclose-summary::-webkit-details-marker { display: none; }
.cv-contact-disclose-summary::marker { content: ''; }
.cv-contact-disclose-summary:hover { background: var(--bg-subtle); }
.cv-contact-disclose-summary:focus-visible {
    outline: 2px solid var(--accent-500);
    outline-offset: -2px;
}
.cv-contact-disclose-summary > i:first-child {
    flex: 0 0 32px;
    width: 32px; height: 32px;
    display: grid;
    place-items: center;
    background: var(--primary-900);
    color: var(--accent-500);
    border-radius: var(--radius-input);
    font-size: 16px;
}
.cv-contact-disclose-label {
    flex: 1 1 auto;
    line-height: 1.25;
    min-width: 0;
}
.cv-contact-disclose-label strong {
    display: block;
    color: var(--text-primary);
    font-size: var(--fs-body);
    font-weight: 600;
}
.cv-contact-disclose-label small { font-size: 12px; }

.cv-contact-disclose-chev {
    color: var(--text-secondary);
    transition: transform 220ms ease;
    flex-shrink: 0;
}
.cv-contact-disclose[open] .cv-contact-disclose-chev {
    transform: rotate(180deg);
}

/* Inner grid + footnote — separated from the summary by a 1px line so
   the open state reads as a panel reveal, not a continuation of the
   summary row. */
.cv-contact-disclose .cv-info-grid-contact {
    padding: var(--space-4);
    border-top: 1px solid var(--border);
    background: var(--bg-subtle);
}
.cv-contact-disclose-foot {
    padding: var(--space-2) var(--space-4) var(--space-3);
    background: var(--bg-subtle);
    border-top: 1px dashed var(--border);
    color: var(--text-secondary);
}
.cv-contact-disclose-foot > i {
    color: var(--accent-600, var(--accent-500));
    margin-right: 4px;
}

/* Honour reduced-motion users — disable chevron rotation. */
@media (prefers-reduced-motion: reduce) {
    .cv-contact-disclose-chev { transition: none; }
}

html[data-theme="dark"] .cv-contact-disclose {
    background: var(--bg-card);
}
html[data-theme="dark"] .cv-contact-disclose .cv-info-grid-contact,
html[data-theme="dark"] .cv-contact-disclose-foot {
    background: rgba(255, 255, 255, .03);
}

/* Working-hours list — clean two-column rows, faded for closed days. */
.cv-hours-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 4px 24px;
}
.cv-hours-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 6px 0;
    border-bottom: 1px dashed var(--border);
    font-size: 13.5px;
}
.cv-hours-item:last-child,
.cv-hours-item:nth-last-child(2) { border-bottom: 0; }
.cv-hours-day { color: var(--text-primary); font-weight: 500; }
.cv-hours-val { color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.cv-hours-item.is-closed .cv-hours-day,
.cv-hours-item.is-closed .cv-hours-val { color: var(--text-secondary); opacity: 0.7; font-style: italic; }
@media (max-width: 600px) {
    .cv-hours-list { grid-template-columns: 1fr; }
    .cv-hours-item:nth-last-child(2) { border-bottom: 1px dashed var(--border); }
}

/* Social-link row — small pill links per platform. */
.cv-social-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 8px;
}
.cv-social-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border-radius: 999px;
    background: var(--bg-subtle);
    border: 1px solid var(--border);
    color: var(--text-primary);
    font-size: 13px;
    font-weight: 500;
    text-decoration: none;
    transition: background-color .12s ease, border-color .12s ease, color .12s ease;
}
.cv-social-link:hover {
    background: var(--accent-500);
    border-color: var(--accent-500);
    color: var(--accent-fg, #1A1A1A);
}
.cv-social-link i { font-size: 15px; }

/* ─────────────────────────────────────────────────────────────────────────
   Customer profile — verification pills + completeness widget.
   Used by customer/profile/show.blade.php.
   ───────────────────────────────────────────────────────────────────────── */

/* Tiny pill that sits next to a label to show verified/unverified state.
   Uses Bootstrap's ok/warn semantic palette so dark mode works for free. */
.vp-verified-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-left: 8px;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--bs-success-bg-subtle);
    color: var(--bs-success-text-emphasis);
    border: 1px solid transparent;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.4;
    vertical-align: middle;
}
.vp-verified-pill i { font-size: 12px; }
.vp-verified-pill.is-pending {
    background: var(--bs-warning-bg-subtle);
    color: var(--bs-warning-text-emphasis);
}

/* Profile completeness card — a slim progress bar with motivating colours
   that climb from amber → lime as completeness improves. */
.cp-completeness .cp-pct {
    color: var(--accent-700, var(--accent-600));
    font-size: 18px;
    font-variant-numeric: tabular-nums;
}
.cp-completeness.is-complete { border-color: var(--accent-500); }
.cp-bar {
    width: 100%;
    height: 8px;
    background: var(--bg-subtle);
    border-radius: 999px;
    overflow: hidden;
}
.cp-bar-fill {
    height: 100%;
    background: linear-gradient(90deg, #ffd166 0%, var(--accent-500) 100%);
    border-radius: 999px;
    transition: width 0.4s ease;
}
.cp-checklist {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.cp-checklist li {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 10px;
    background: var(--bg-subtle);
    border-radius: 8px;
    color: var(--text-primary);
    font-size: 13px;
}
.cp-checklist li i { color: var(--text-secondary); font-size: 14px; }

/* Security at-a-glance — link is the same colour as Stats card label rows. */
.cp-security-card a { color: var(--accent-600, var(--accent-500)); text-decoration: none; font-weight: 600; }
.cp-security-card a:hover { text-decoration: underline; }

/* =================================================================
   <x-list-page> chrome — canonical layout for every index page.
   Filter tabs (left) + primary action (right) on the first row;
   optional search bar on the second row; surface body holds the
   table/grid; surface foot holds pagination.

   Used by: customer/{events,bookings,vendors,guests,disputes}/index,
   vendor/{services,bookings,payouts,customers,reviews}/index,
   admin/{vendors,bookings,services,disputes,compliance}/index.
   ================================================================= */
.vp-list-head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-3);
}
.vp-list-head .vp-tabs { flex: 1 1 auto; }
.vp-list-action {
    flex: 0 0 auto;
    white-space: nowrap;
}
@media (max-width: 575.98px) {
    /* On mobile, action button drops below the tabs so it doesn't squash them. */
    .vp-list-head { gap: var(--space-2); }
    .vp-list-head .vp-list-action {
        margin-left: 0 !important;
        width: 100%;
        justify-content: center;
    }
}

.vp-list-toolbar {
    padding: var(--space-3) var(--space-6);
}
.vp-list-search-form { margin: 0; width: 100%; }
.vp-list-search {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    height: 40px;
    padding: 0 var(--space-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--bg-subtle);
    transition: border-color 120ms ease, background 120ms ease;
}
.vp-list-search:focus-within {
    border-color: var(--accent-500);
    background: var(--surface);
}
.vp-list-search > i {
    color: var(--text-secondary);
    font-size: 16px;
    flex: 0 0 auto;
}
.vp-list-search > input {
    flex: 1 1 auto;
    border: 0;
    background: transparent;
    color: var(--text-primary);
    font-size: var(--fs-body);
    outline: none;
    padding: 0;
}
.vp-list-search > input::placeholder { color: var(--text-muted); }

/* Tighten the surface-foot pager on list pages — pagination links plus the
   "Showing N–M of Z" count, side-by-side, with the count muted. */
.vp-surface-foot .vp-pager .pagination {
    margin: 0;
    --bs-pagination-padding-x: 0.6rem;
    --bs-pagination-padding-y: 0.3rem;
    --bs-pagination-font-size: var(--fs-small);
}

/* =================================================================
   Event profile page (.evp-*) — customer.events.show

   Layout zones, top→bottom:
     1. .evp-hero            — identity + countdown + status + actions
     2. .evp-progress-banner — wizard-completion CTA (conditional)
     3. .evp-kpi-row         — 4 dashboard tiles (vendors/guests/budget/tasks)
     4. .evp-section         — vendor discovery (tabs + content)
     5. .evp-checklist       — plan to-do (conditional)

   Design intent: the customer should grok where they are at a glance.
   The hero answers "when is the event?", the KPI row answers "where
   am I across the four dimensions?", the progress banner answers
   "what's the next thing I should do?". No duplicated UI; every
   piece of info lives in exactly one canonical surface.
   ================================================================= */

/* ── 1. Hero ────────────────────────────────────────────────────── */
.evp-hero {
    display: flex;
    align-items: stretch;
    gap: var(--space-5);
    padding: var(--space-6);
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 4px solid var(--accent-500);
    border-radius: var(--radius-card);
    flex-wrap: wrap;
}
.evp-hero-main {
    flex: 1 1 320px;
    min-width: 0;
}
.evp-hero-meta {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
    margin-bottom: var(--space-2);
}
.evp-ref {
    font-size: 11px;
    letter-spacing: .08em;
    color: var(--text-secondary);
    font-weight: 500;
    text-transform: uppercase;
}
.evp-hero-title {
    font-size: var(--fs-h1, 36px);
    font-weight: 800;
    line-height: 1.1;
    margin: 0;
    color: var(--text-primary);
    letter-spacing: -.02em;
}
.evp-hero-facts {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-2);
    font-size: var(--fs-small);
    color: var(--text-secondary);
}
.evp-hero-facts .sep { color: var(--text-muted); }
.evp-hero-facts i { color: var(--text-muted); }
.evp-hero-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-4);
}

/* Countdown — the single most important glance on the page. */
.evp-countdown {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-width: 140px;
    padding: var(--space-4) var(--space-5);
    border-radius: var(--radius-card);
    background: var(--bg-subtle);
    border: 1px solid var(--border);
    text-align: center;
}
.evp-countdown-num {
    font-size: 56px;
    font-weight: 800;
    line-height: 1;
    letter-spacing: -.04em;
    color: var(--text-primary);
}
.evp-countdown-label {
    font-size: var(--fs-small);
    color: var(--text-secondary);
    margin-top: 2px;
    font-weight: 500;
}
.evp-countdown-date {
    font-size: 11px;
    color: var(--text-muted);
    margin-top: 4px;
    text-transform: uppercase;
    letter-spacing: .04em;
}
/* Tier colours — soft to urgent. */
.evp-countdown-planning { background: rgba(var(--accent-rgb, 143, 224, 0), .08); border-color: rgba(var(--accent-rgb, 143, 224, 0), .25); }
.evp-countdown-planning .evp-countdown-num { color: var(--accent-600, var(--accent-500)); }
.evp-countdown-soon     { background: rgba(245, 158, 11, .08); border-color: rgba(245, 158, 11, .30); }
.evp-countdown-soon     .evp-countdown-num { color: #d97706; }
.evp-countdown-urgent   { background: rgba(220, 53, 69, .08); border-color: rgba(220, 53, 69, .35); }
.evp-countdown-urgent   .evp-countdown-num { color: #dc3545; }

/* ── 2. Setup-progress banner (conditional) ──────────────────────── */
.evp-progress-banner {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-4) var(--space-5);
    background: linear-gradient(90deg, rgba(var(--accent-rgb, 143, 224, 0), .12), rgba(var(--accent-rgb, 143, 224, 0), .04));
    border: 1px solid rgba(var(--accent-rgb, 143, 224, 0), .35);
    border-radius: var(--radius-card);
    flex-wrap: wrap;
}
.evp-progress-icon {
    flex: 0 0 auto;
    width: 40px; height: 40px;
    border-radius: 50%;
    background: var(--accent-500);
    color: var(--primary-900, #121A3A);
    display: flex; align-items: center; justify-content: center;
    font-size: 18px;
}
.evp-progress-body {
    flex: 1 1 240px;
    min-width: 0;
}
.evp-progress-title {
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 6px;
}
.evp-progress-bar {
    height: 6px;
    background: rgba(18, 26, 58, .08);
    border-radius: 3px;
    overflow: hidden;
}
.evp-progress-fill {
    height: 100%;
    background: var(--accent-500);
    border-radius: 3px;
    transition: width .3s ease;
}
.evp-progress-pct {
    color: var(--text-secondary);
    margin-top: 4px;
    display: block;
}
.evp-progress-cta {
    flex: 0 0 auto;
    white-space: nowrap;
}

/* ── 3. KPI tiles ──────────────────────────────────────────────── */
.evp-kpi-row {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--space-3);
}
@media (max-width: 767.98px) {
    .evp-kpi-row { grid-template-columns: repeat(2, 1fr); }
}
.evp-kpi {
    display: flex;
    flex-direction: column;
    padding: var(--space-4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    text-decoration: none;
    color: inherit;
    transition: transform 120ms ease, border-color 120ms ease, box-shadow 120ms ease;
}
.evp-kpi:hover {
    border-color: var(--accent-500);
    transform: translateY(-1px);
    box-shadow: 0 4px 12px rgba(18, 26, 58, .06);
    text-decoration: none;
    color: inherit;
}
.evp-kpi-label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    color: var(--text-secondary);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    margin-bottom: var(--space-2);
}
.evp-kpi-label i { color: var(--text-muted); font-size: 13px; }
.evp-kpi-value {
    display: flex;
    align-items: baseline;
    gap: 4px;
    color: var(--text-primary);
}
.evp-kpi-value strong {
    font-size: 24px;
    font-weight: 800;
    letter-spacing: -.02em;
}
.evp-kpi-of {
    font-size: var(--fs-small);
    color: var(--text-muted);
}
.evp-kpi-sub {
    color: var(--text-secondary);
    margin-top: 4px;
}
.evp-kpi-bar {
    height: 4px;
    background: var(--bg-subtle);
    border-radius: 2px;
    margin-top: 6px;
    overflow: hidden;
}
.evp-kpi-bar-fill {
    height: 100%;
    background: var(--accent-500);
    border-radius: 2px;
}

/* ── 3b. Readiness composite + Stats sidebar ──────────────────────
   Mirrors the customer profile-page pattern (.cp-completeness + Stats):
   single composite score, tagline, one next-action chip. No breakdown,
   no instructions — the customer already knows this pattern from
   /me/profile and applies the same mental model here.
   ──────────────────────────────────────────────────────────────── */
.evp-readiness {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    padding: var(--space-5);
    margin-bottom: var(--space-3);
}
.evp-readiness-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin-bottom: var(--space-3);
}
.evp-readiness-title {
    margin: 0;
    font-size: var(--fs-body);
    font-weight: 700;
    color: var(--text-primary);
    display: flex;
    align-items: center;
    gap: 6px;
}
.evp-readiness-title i { color: var(--text-secondary); }
.evp-readiness-score {
    font-size: 20px;
    font-weight: 800;
    color: var(--accent-600, var(--accent-500));
    letter-spacing: -.02em;
}
.evp-readiness-bar {
    height: 8px;
    background: var(--bg-subtle);
    border-radius: 4px;
    overflow: hidden;
    margin-bottom: var(--space-3);
}
.evp-readiness-fill {
    height: 100%;
    /* Yellow → lime gradient, mapped to the FULL bar width so the colour
       reflects progression (low scores read warm, high scores read healthy)
       and matches the profile-completeness colour story. */
    background: linear-gradient(90deg, #f59e0b 0%, var(--accent-500) 100%);
    background-size: 100% 100%;
    border-radius: 4px;
    transition: width .4s ease;
}
.evp-readiness-tagline {
    font-size: var(--fs-small);
    color: var(--text-secondary);
    line-height: 1.5;
    margin: 0 0 var(--space-3);
}
.evp-readiness-next {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    background: var(--bg-subtle);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    color: var(--text-primary);
    text-decoration: none;
    font-weight: 600;
    font-size: var(--fs-small);
    transition: border-color 120ms ease, background 120ms ease;
}
.evp-readiness-next:hover {
    border-color: var(--accent-500);
    background: rgba(var(--accent-rgb, 143, 224, 0), .06);
    color: var(--text-primary);
    text-decoration: none;
}
.evp-readiness-next i {
    color: var(--text-secondary);
    font-size: 16px;
}
.evp-readiness-next.is-done {
    border-color: rgba(var(--accent-rgb, 143, 224, 0), .35);
    background: rgba(var(--accent-rgb, 143, 224, 0), .08);
}
.evp-readiness-next.is-done i {
    color: var(--accent-600, var(--accent-500));
}

.evp-stats {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    padding: var(--space-5);
}
.evp-stats-title {
    margin: 0 0 var(--space-3);
    font-size: var(--fs-h3, 18px);
    font-weight: 700;
    color: var(--text-primary);
}
.evp-stats-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--border);
    font-size: var(--fs-small);
}
.evp-stats-row:last-child { border-bottom: 0; padding-bottom: 0; }
.evp-stats-row:first-child { padding-top: 0; }
.evp-stats-row span { color: var(--text-secondary); }
.evp-stats-row strong { color: var(--text-primary); font-weight: 600; }

/* ── 4. Vendor discovery section ─────────────────────────────────── */
.evp-section {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    overflow: hidden;
}
.evp-section-head {
    padding: var(--space-5) var(--space-6);
    border-bottom: 1px solid var(--border);
}
.evp-section-title {
    margin: 0 0 4px;
    font-size: var(--fs-h2, 22px);
    font-weight: 700;
    color: var(--text-primary);
}

/* Tabs — bigger, more clickable than the previous "Discover 8" tiny text */
.evp-tabs {
    display: flex;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-6) 0;
    border-bottom: 1px solid var(--border);
    flex-wrap: wrap;
}
.evp-tab {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: var(--space-3) var(--space-4);
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    color: var(--text-secondary);
    text-decoration: none;
    font-weight: 600;
    font-size: var(--fs-body);
    transition: color 120ms ease, border-color 120ms ease;
}
.evp-tab:hover {
    color: var(--text-primary);
    text-decoration: none;
}
.evp-tab.is-active {
    color: var(--text-primary);
    border-bottom-color: var(--accent-500);
}
.evp-tab i { font-size: 16px; }
.evp-tab-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 22px;
    padding: 0 6px;
    border-radius: 11px;
    background: var(--bg-subtle);
    color: var(--text-secondary);
    font-size: 11px;
    font-weight: 700;
}
.evp-tab.is-active .evp-tab-count { background: var(--accent-500); color: var(--primary-900, #121A3A); }
.evp-tab-count.is-blue  { background: #dbeafe; color: #1d4ed8; }
.evp-tab-count.is-green { background: #dcfce7; color: #15803d; }

.evp-tab-body {
    padding: var(--space-5) var(--space-6);
}

/* ── 5. Plan checklist (bottom, conditional) ─────────────────────── */
.evp-checklist {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    overflow: hidden;
}
.evp-checklist-body {
    padding: var(--space-2) var(--space-6) var(--space-4);
}
.evp-task {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--border);
    cursor: pointer;
}
.evp-task:last-child { border-bottom: 0; }
.evp-task input[type="checkbox"] {
    width: 18px; height: 18px;
    accent-color: var(--accent-500);
    flex: 0 0 auto;
}
.evp-task-label {
    color: var(--text-primary);
    font-size: var(--fs-body);
}
.evp-task.is-done .evp-task-label {
    color: var(--text-muted);
    text-decoration: line-through;
}

/* =================================================================
   Event wizard step 1 — Create Event form
   Sectioned form chrome with lime-accent intro + grouped fieldsets.
   ================================================================= */

/* Hero intro band — explains why we ask, sets tone */
.evp-form-intro {
    display: flex;
    align-items: flex-start;
    gap: var(--space-4);
    padding: var(--space-4) var(--space-5);
    background: linear-gradient(90deg, rgba(var(--accent-rgb, 143, 224, 0), .08), rgba(var(--accent-rgb, 143, 224, 0), .02));
    border: 1px solid rgba(var(--accent-rgb, 143, 224, 0), .25);
    border-left: 4px solid var(--accent-500);
    border-radius: var(--radius-card);
}
.evp-form-intro-icon {
    flex: 0 0 auto;
    width: 36px; height: 36px;
    border-radius: 50%;
    background: var(--accent-500);
    color: var(--primary-900, #121A3A);
    display: flex; align-items: center; justify-content: center;
    font-size: 16px;
}

/* Form sections — visually group related fields with a quiet header band */
.evp-form-section {
    border-bottom: 1px solid var(--border);
}
.evp-form-section:last-of-type { border-bottom: 0; }
.evp-form-section-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: var(--space-4) var(--space-6) var(--space-3);
    background: var(--bg-subtle);
    border-bottom: 1px solid var(--border);
}
.evp-form-section-head i {
    color: var(--accent-600, var(--accent-500));
    font-size: 18px;
}
.evp-form-section-body {
    padding: var(--space-5) var(--space-6);
}

/* Tighten form labels + hints for the create-event form */
.evp-form-section .form-label {
    font-size: var(--fs-small);
    margin-bottom: 6px;
    color: var(--text-primary);
}
.evp-form-section .form-text {
    font-size: 12px;
    margin-top: 4px;
    line-height: 1.4;
}
.evp-form-section .form-control,
.evp-form-section .form-select {
    border-radius: var(--radius-md);
    border-color: var(--border);
}
.evp-form-section .form-control:focus,
.evp-form-section .form-select:focus {
    border-color: var(--accent-500);
    box-shadow: 0 0 0 .2rem rgba(var(--accent-rgb, 143, 224, 0), .15);
}
.evp-form-section .input-group-text {
    background: var(--bg-subtle);
    color: var(--text-secondary);
    border-color: var(--border);
}

/* ── Event-wizard step 2: criteria selection pool ──────────────────
   Familiar pattern: shopping-cart-style chip summary above the browse
   area. Every checkbox flip rebuilds the chips and bumps parent count
   pills, so customers always see their cumulative selection without
   having to re-navigate the categories list.
   ──────────────────────────────────────────────────────────────── */
.evp-pool {
    min-height: 56px;
}
.evp-pool-empty {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: var(--space-3) 0;
    color: var(--text-secondary);
    font-size: var(--fs-small);
}
.evp-pool-empty i { font-size: 18px; }
.evp-pool-total {
    color: var(--text-secondary);
    font-weight: 500;
    margin-left: 4px;
}
.evp-pool-group + .evp-pool-group {
    margin-top: var(--space-3);
    padding-top: var(--space-3);
    border-top: 1px solid var(--border);
}
.evp-pool-group-label {
    font-size: 11px;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: .05em;
    font-weight: 600;
    margin-bottom: 6px;
}
.evp-pool-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}
.evp-pool-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 8px 4px 12px;
    background: var(--bg-subtle);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    font-size: var(--fs-small);
    color: var(--text-primary);
    line-height: 1.4;
}
.evp-pool-chip-remove {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px; height: 20px;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--text-secondary);
    border-radius: 50%;
    cursor: pointer;
    font-size: 16px;
    line-height: 1;
    transition: background 120ms ease, color 120ms ease;
}
.evp-pool-chip-remove:hover {
    background: #dc3545;
    color: #fff;
}

/* Count pill turns lime when the parent has any selection — visual echo
   of the chip count in the pool above. */
.criteria-count {
    background: var(--bg-subtle);
    color: var(--text-secondary);
    font-weight: 700;
}
.criteria-count.has-selection {
    background: var(--accent-500);
    color: var(--primary-900, #121A3A);
}

/* Disabled Next button — soft visual cue, not a hard block. */
.vp-btn-lime.is-disabled,
.vp-btn-lime:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* =================================================================
   Admin "Mission Control" dashboard (/admin/system-health) — `.sh-*`
   Reuses the existing admin chrome (.vp-surface, .amboi-stat, .vp-tabs)
   so the page sits naturally beside the business Overview. Only the
   bits unique to operational monitoring (status dots, probe pills,
   signal rows, live activity feed) get dedicated classes below.
   ================================================================= */

/* Health band — sits at the top, lime accent so green-state reads
   instantly without parsing the dot's colour. Adapts to degraded /
   down by swapping the dot CSS only — band chrome stays calm. */
.sh-health-band { border-left: 3px solid var(--accent-500); }
.sh-health-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-4);
}
.sh-health-status {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex: 0 0 auto;
}
.sh-status-label {
    font-weight: 700;
    color: var(--text-primary);
    line-height: 1.2;
}

/* Status dot — the visual signal language. Green = ok, amber = degraded,
   red = down. Used for the overall status AND each individual probe. */
.sh-status-dot {
    display: inline-block;
    width: 10px; height: 10px;
    border-radius: 50%;
    background: var(--accent-500);
    box-shadow: 0 0 0 4px rgba(var(--accent-rgb, 143, 224, 0), .15);
    flex: 0 0 auto;
    transition: background 200ms ease, box-shadow 200ms ease;
}
.sh-status-dot.is-ok { background: var(--accent-500); box-shadow: 0 0 0 4px rgba(var(--accent-rgb, 143, 224, 0), .15); }
.sh-status-dot.is-degraded { background: #f59e0b; box-shadow: 0 0 0 4px rgba(245, 158, 11, .18); }
.sh-status-dot.is-down { background: #dc3545; box-shadow: 0 0 0 4px rgba(220, 53, 69, .18); }
.sh-status-dot.is-loading {
    background: var(--text-muted);
    box-shadow: 0 0 0 4px rgba(127, 127, 127, .12);
    animation: shPulse 1s ease-in-out infinite;
}
@keyframes shPulse {
    0%, 100% { opacity: 1; }
    50% { opacity: .35; }
}

/* Probe pills — one per dependency (DB, cache, storage, queue). */
.sh-probes {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    flex: 1 1 auto;
    min-width: 0;
}
.sh-probe {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    background: var(--bg-subtle);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    font-size: var(--fs-small);
    color: var(--text-primary);
}
.sh-probe.is-loading { opacity: .65; }
.sh-probe-name { font-weight: 600; }
.sh-probe-detail { font-size: 12px; }

/* Risk signals — single column of rows, each showing tone + count +
   label + action. Tone colour stripes the left edge so admins scanning
   the panel land on the red ones first. */
.sh-signals { padding: 0; }
.sh-signal-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-5);
    border-bottom: 1px solid var(--border);
    border-left: 3px solid transparent;
}
.sh-signal-row:last-child { border-bottom: 0; }
.sh-signal-row.sh-signal-danger  { border-left-color: #dc3545; background: rgba(220, 53, 69, .03); }
.sh-signal-row.sh-signal-warning { border-left-color: #f59e0b; background: rgba(245, 158, 11, .03); }
.sh-signal-row.sh-signal-ok      { border-left-color: var(--accent-500); }
.sh-signal-icon {
    flex: 0 0 auto;
    width: 24px;
    font-size: 18px;
    text-align: center;
}
.sh-signal-row.sh-signal-danger  .sh-signal-icon { color: #dc3545; }
.sh-signal-row.sh-signal-warning .sh-signal-icon { color: #f59e0b; }
.sh-signal-row.sh-signal-ok      .sh-signal-icon { color: var(--accent-600, var(--accent-500)); }
.sh-signal-count {
    flex: 0 0 auto;
    min-width: 36px;
    font-weight: 800;
    font-size: 18px;
    color: var(--text-primary);
    text-align: right;
}
.sh-signal-label {
    flex: 1 1 auto;
    color: var(--text-primary);
    min-width: 0;
}
.sh-signal-action {
    flex: 0 0 auto;
    font-size: var(--fs-small);
    color: var(--text-secondary);
    text-decoration: none;
    font-weight: 600;
}
.sh-signal-action:hover { color: var(--accent-600, var(--accent-500)); text-decoration: none; }
.sh-signal-action-clean {
    color: var(--text-muted);
    font-style: italic;
    font-weight: 500;
}

/* Activity feed — left-icon, two-line body, right-side timestamp. */
.sh-feed-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-5);
    border-bottom: 1px solid var(--border);
    border-left: 3px solid transparent;
}
.sh-feed-row:last-child { border-bottom: 0; }
.sh-feed-row.sh-feed-danger  { border-left-color: #dc3545; }
.sh-feed-row.sh-feed-warning { border-left-color: #f59e0b; }
.sh-feed-row.sh-feed-success { border-left-color: #16a34a; }
.sh-feed-row.sh-feed-muted   { border-left-color: var(--border); }
.sh-feed-icon {
    flex: 0 0 auto;
    width: 28px; height: 28px;
    border-radius: 50%;
    background: var(--bg-subtle);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--text-secondary);
    font-size: 14px;
}
.sh-feed-body { flex: 1 1 auto; min-width: 0; }
.sh-feed-action {
    font-size: var(--fs-small);
    font-weight: 600;
    color: var(--text-primary);
    text-transform: lowercase;
    letter-spacing: 0;
}
.sh-feed-ago {
    flex: 0 0 auto;
    white-space: nowrap;
}

/* Live-update pulse dot — green dot in the feed header indicating the
   poller is alive. Pulses slowly so it doesn't compete with the data. */
.sh-feed-pulse {
    display: inline-block;
    width: 8px; height: 8px;
    border-radius: 50%;
    background: var(--accent-500);
    box-shadow: 0 0 0 3px rgba(var(--accent-rgb, 143, 224, 0), .25);
    animation: shFeedPulse 2.5s ease-in-out infinite;
}
@keyframes shFeedPulse {
    0%, 100% { box-shadow: 0 0 0 3px rgba(var(--accent-rgb, 143, 224, 0), .25); }
    50%      { box-shadow: 0 0 0 6px rgba(var(--accent-rgb, 143, 224, 0), .05); }
}

/* Make the action-queue tiles clickable as a whole. Echoes how the
   customer-dashboard KPI tiles work — entire card is the hit target. */
.amboi-stat-link {
    display: block;
    text-decoration: none;
    color: inherit;
    transition: transform 120ms ease, box-shadow 120ms ease;
}
.amboi-stat-link:hover {
    transform: translateY(-1px);
    text-decoration: none;
    color: inherit;
}
.amboi-stat-link:hover .amboi-stat,
.amboi-stat.amboi-stat-link:hover {
    box-shadow: 0 4px 12px rgba(18, 26, 58, .06);
}
/* Combined form: <a class="amboi-stat amboi-stat-link"> — used by the
   admin x-admin.kpi-card so the anchor IS the surface (no extra
   wrapping div). Hover lift + shadow + accent border applies directly. */
a.amboi-stat.amboi-stat-link {
    color: inherit;
}
a.amboi-stat.amboi-stat-link:hover {
    color: inherit;
    border-color: var(--accent-500);
}

/* ── Form auto-save restore toast — surfaces ONCE on page load when
   the <x-form-autosave> component hydrates a saved draft. Polite,
   dismissable, auto-fades after 6s. */
.form-autosave-toast {
    position: fixed;
    inset: auto 16px 16px auto;
    z-index: 1050;
    background: var(--text-primary);
    color: #fff;
    padding: 10px 14px;
    border-radius: var(--radius-pill);
    font-size: var(--fs-small);
    box-shadow: 0 4px 14px rgba(18, 26, 58, .25);
    display: inline-flex;
    align-items: center;
    gap: 10px;
    max-width: calc(100vw - 32px);
    animation: shFadeUp 250ms ease-out;
}
.form-autosave-toast.is-fading {
    opacity: 0;
    transform: translateY(8px);
    transition: opacity 350ms ease, transform 350ms ease;
}
.form-autosave-toast i { color: var(--accent-500); font-size: 16px; }
.form-autosave-toast button {
    background: transparent;
    border: 0;
    color: #fff;
    text-decoration: underline;
    font-size: var(--fs-small);
    cursor: pointer;
    padding: 0;
    margin-left: 6px;
    opacity: .85;
}
.form-autosave-toast button:hover { opacity: 1; }
@keyframes shFadeUp {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* ── Confirm-before-pay modal — reduces anxiety + accidental charges
   on the booking deposit / balance flows. Used in customer/bookings/
   partials/confirm-pay.blade.php. */
.confirm-pay-amount {
    text-align: center;
    padding: var(--space-4) var(--space-3);
    background: linear-gradient(135deg, rgba(var(--accent-rgb, 143, 224, 0), .12), rgba(var(--accent-rgb, 143, 224, 0), .03));
    border: 1px solid rgba(var(--accent-rgb, 143, 224, 0), .35);
    border-radius: var(--radius-card);
}
.confirm-pay-amount-label {
    font-size: var(--fs-small);
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: .05em;
    font-weight: 600;
}
.confirm-pay-amount-value {
    font-size: 38px;
    font-weight: 800;
    color: var(--text-primary);
    letter-spacing: -.02em;
    line-height: 1.1;
    margin-top: 4px;
}
.confirm-pay-breakdown {
    padding: var(--space-3) 0;
    border-top: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
}
.confirm-pay-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 4px 0;
    font-size: var(--fs-small);
}
.confirm-pay-row span { color: var(--text-secondary); }
.confirm-pay-row strong { color: var(--text-primary); font-weight: 600; }
.confirm-pay-row-total {
    margin-top: 6px;
    padding-top: 6px;
    border-top: 1px dashed var(--border);
}
.confirm-pay-row-total span,
.confirm-pay-row-total strong {
    color: var(--text-primary);
    font-weight: 700;
}
.confirm-pay-trust {
    list-style: none;
    padding: 0;
    margin: 0;
}
.confirm-pay-trust li {
    display: flex;
    gap: var(--space-3);
    padding: var(--space-2) 0;
    align-items: flex-start;
}
.confirm-pay-trust li i {
    flex: 0 0 auto;
    font-size: 20px;
    margin-top: 2px;
}
.confirm-pay-trust li strong {
    display: block;
    color: var(--text-primary);
    font-size: var(--fs-small);
    font-weight: 600;
    margin-bottom: 2px;
}
.confirm-pay-trust li small {
    display: block;
    color: var(--text-secondary);
    font-size: 12px;
    line-height: 1.45;
}
.confirm-pay-trust li small a { color: var(--text-secondary); text-decoration: underline; }

/* ── PDPA cookie consent banner + customize sheet ──────────────────
   Fixed-bottom banner on first visit; surfaces a customize sheet for
   per-category opt-in (analytics, marketing). Persists choice for
   365 days via a first-party cookie. */
.cc-banner {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 1080;
    /* Opaque background — must remain readable over hero imagery,
       video, or any page content scrolling beneath. `--bg-card` is
       defined for both light + dark themes; `var(--surface)` was an
       undefined token that rendered as transparent. */
    background: var(--bg-card, #FFFFFF);
    border-top: 2px solid var(--accent-500);
    box-shadow: 0 -4px 16px rgba(18, 26, 58, .12);
    padding: var(--space-4) var(--space-5);
}
.cc-banner-inner {
    max-width: 1100px;
    margin: 0 auto;
    display: flex;
    align-items: flex-start;
    gap: var(--space-4);
    flex-wrap: wrap;
}
.cc-banner-copy {
    flex: 1 1 380px;
    min-width: 0;
}
.cc-banner-copy strong { font-size: var(--fs-body); color: var(--text-primary); }
.cc-banner-copy p {
    margin: 4px 0 0;
    font-size: var(--fs-small);
    color: var(--text-secondary);
    line-height: 1.5;
}
.cc-banner-actions {
    display: flex;
    gap: var(--space-2);
    flex-wrap: wrap;
    flex: 0 0 auto;
}
@media (max-width: 575.98px) {
    .cc-banner-actions { width: 100%; }
    .cc-banner-actions > button { flex: 1 1 100px; }
}

/* Customize sheet — modal overlay with per-category toggles. */
.cc-sheet {
    position: fixed;
    inset: 0;
    z-index: 1090;
    background: rgba(18, 26, 58, .45);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
}
.cc-sheet-card {
    /* Solid panel — see note on .cc-banner above for why we avoid
       `var(--surface)` (undefined) and use `--bg-card` instead. */
    background: var(--bg-card, #FFFFFF);
    border-radius: var(--radius-card);
    max-width: 480px;
    width: 100%;
    overflow: hidden;
    box-shadow: 0 12px 40px rgba(18, 26, 58, .25);
}
.cc-sheet-head,
.cc-sheet-foot {
    display: flex;
    align-items: center;
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--border);
}
.cc-sheet-head { justify-content: space-between; }
.cc-sheet-foot {
    justify-content: flex-end;
    gap: var(--space-2);
    border-bottom: 0;
    border-top: 1px solid var(--border);
    background: var(--bg-subtle);
}
.cc-sheet-body { padding: var(--space-5); }
.cc-cat { padding: var(--space-3) 0; border-bottom: 1px solid var(--border); }
.cc-cat:last-child { border-bottom: 0; }
.cc-cat-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 4px;
}
.cc-cat-head strong { color: var(--text-primary); }
.cc-cat-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: var(--fs-small);
    color: var(--text-secondary);
    cursor: pointer;
}
.cc-cat-toggle input[type="checkbox"] {
    accent-color: var(--accent-500);
    width: 18px;
    height: 18px;
}
.cc-cat-locked {
    font-style: italic;
    color: var(--text-muted);
    cursor: default;
}

/* ─────────────────────────────────────────────────────────────────
   Public homepage trust-promise row.

   Replaces the previous count-based ".amboi-trust-strip" (10+ vendors,
   10+ stories) which read as a vanity metric and looked weak at small
   marketplace size. The promise row instead surfaces three concrete
   benefits — escrow / verification / halal-tracking — each with a
   filled rounded-square lime-gradient badge matching the verified-pill
   pattern used elsewhere in the design system.
   ───────────────────────────────────────────────────────────────── */
.amboi-trust-promise {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-4);
    padding: var(--space-4) var(--space-5);
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
}
.amboi-trust-promise .atp-item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-width: 0;
}
.amboi-trust-promise .atp-badge {
    /* Filled rounded-square badge with the brand's accent gradient.
       12px radius keeps it soft without going fully circle (which
       would lose the "stamp / certification" feel). No drop shadow —
       a coloured halo behind the tile read as fake "glow" against the
       Rekaizen palette; flat reads cleaner. */
    flex-shrink: 0;
    width: 44px;
    height: 44px;
    border-radius: 12px;
    background: linear-gradient(135deg, var(--accent-500) 0%, var(--accent-600) 100%);
    display: grid;
    place-items: center;
}
.amboi-trust-promise .atp-badge i {
    font-size: 22px;
    line-height: 1;
    /* Adaptive foreground — dark on lime/yellow/amber, white on
        purple/pink/navy. Same --accent-fg pattern used across every
        icon-on-accent tile in the codebase. */
    color: var(--accent-fg, #1A1A1A);
}
.amboi-trust-promise .atp-text {
    min-width: 0; /* allow text wrap inside flex children */
}
.amboi-trust-promise .atp-text strong {
    display: block;
    font-size: 15px;
    font-weight: 700;
    color: var(--text-primary);
    letter-spacing: -.005em;
    margin-bottom: 2px;
}
.amboi-trust-promise .atp-text span {
    display: block;
    font-size: 13px;
    color: var(--text-secondary);
    line-height: 1.4;
}

/* Tablet: keep 3 columns but tighter padding so it still fits comfortably. */
@media (max-width: 991.98px) {
    .amboi-trust-promise {
        padding: var(--space-3) var(--space-4);
        gap: var(--space-3);
    }
    .amboi-trust-promise .atp-text strong { font-size: 14px; }
    .amboi-trust-promise .atp-text span { font-size: 12px; }
}

/* Mobile: stack to a single column. Each row becomes a horizontal
   badge + 2-line text block. Better than squeezing 3 abreast which
   would clip the descriptions. */
@media (max-width: 575.98px) {
    .amboi-trust-promise {
        grid-template-columns: 1fr;
        gap: var(--space-3);
    }
    .amboi-trust-promise .atp-badge { width: 40px; height: 40px; }
    .amboi-trust-promise .atp-badge i { font-size: 19px; }
}

/* Dark mode — gradient + dark icon read fine against the dark page
   bg. Strip-level hairline kept (gives the row a faint edge); badge
   drop-shadow removed for parity with light mode. */
html[data-theme="dark"] .amboi-trust-promise {
    box-shadow: 0 0 0 1px rgba(255, 255, 255, .04);
}

/* Range slider — show the live value as a prominent strong number */
.evp-range-wrap { padding: var(--space-2) 0; }
.evp-range-wrap .form-range::-webkit-slider-thumb { background: var(--accent-500); }
.evp-range-wrap .form-range::-moz-range-thumb { background: var(--accent-500); }
.evp-range-scale {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-top: 8px;
    font-size: 12px;
    color: var(--text-muted);
}
.evp-range-value {
    color: var(--text-primary);
}
.evp-range-value strong {
    font-size: 18px;
    font-weight: 800;
    color: var(--accent-600, var(--accent-500));
    letter-spacing: -.02em;
}

/* ── Bundle card "Top match" pill (replaces mystery score numbers) ── */
.amboi-candidate-rank {
    position: absolute;
    top: 8px; right: 8px;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 8px;
    border-radius: var(--radius-pill);
    background: #1d4ed8;
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
}
.amboi-candidate-rank.is-full {
    background: var(--accent-500);
    color: var(--primary-900, #121A3A);
}
.amboi-bundle-card { position: relative; }

/* ── Mobile tightening ──────────────────────────────────────────── */
@media (max-width: 575.98px) {
    .evp-hero { padding: var(--space-4); gap: var(--space-3); }
    .evp-hero-title { font-size: 26px; }
    .evp-countdown { width: 100%; min-width: 0; padding: var(--space-3); flex-direction: row; gap: var(--space-3); justify-content: flex-start; }
    .evp-countdown-num { font-size: 40px; }
    .evp-countdown-label, .evp-countdown-date { text-align: left; }
    .evp-section-head, .evp-tab-body, .evp-checklist-body { padding-left: var(--space-4); padding-right: var(--space-4); }
    .evp-tabs { padding-left: var(--space-4); padding-right: var(--space-4); }
}

/* ─────────────────────────────────────────────────────────────────
   VENDOR PROFILE — "Stories featuring …" rail
   Compact horizontal-scroll carousel that replaces the previous full
   feed-card render. Lives in vendor/partials/stories-rail.blade.php.

   Design rationale:
   • Horizontal scroll keeps the section height bounded — a vendor with
     20 tagged stories no longer turns the profile page into a wall.
   • Compact cards (cover + title + byline + reading time + reactions)
     surface enough signal to draw a click without recreating the full
     feed-page card density.
   • Sits in a real vp-surface section so it reads as a "Stories block",
     not loose orphan content beneath the reviews.
   ───────────────────────────────────────────────────────────────── */
.vendor-stories-rail {
    margin: var(--space-5) 0;
    padding: var(--space-5) var(--space-5) var(--space-4);
    /* vp-surface already supplies background, border, radius — these
       overrides are scoped to the rail block only. */
}
.vendor-stories-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-3);
    margin-bottom: var(--space-3);
    flex-wrap: wrap;
}
.vendor-stories-title-wrap { flex: 1 1 auto; min-width: 0; }
.vendor-stories-title {
    margin: 0;
    font-size: var(--fs-h4, 18px);
    font-weight: 700;
    color: var(--text-primary);
    display: flex;
    align-items: center;
    gap: 8px;
    line-height: 1.3;
    flex-wrap: wrap;
}
.vendor-stories-title > i { color: var(--accent-600, var(--accent-500)); flex-shrink: 0; }
.vendor-stories-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 26px;
    height: 22px;
    padding: 0 8px;
    border-radius: var(--radius-pill);
    background: var(--primary-900);
    color: #fff;
    font-size: 12px;
    font-weight: 700;
    line-height: 1;
}
.vendor-stories-sub {
    margin: 4px 0 0;
    font-size: var(--fs-small);
    color: var(--text-secondary);
}
.vendor-stories-more {
    font-size: var(--fs-small);
    font-weight: 600;
    color: var(--primary-900);
    text-decoration: none;
    white-space: nowrap;
    transition: color var(--transition);
}
.vendor-stories-more:hover {
    color: var(--primary-900);
    text-decoration: underline;
}

/* Horizontal-scroll track. grid-auto-flow:column with fixed card width
   keeps cards predictable; scroll-snap pulls them into nice positions. */
.vendor-stories-rail-track {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(260px, 280px);
    gap: var(--space-3);
    overflow-x: auto;
    scroll-snap-type: x proximity;
    padding-bottom: var(--space-2);
    /* Pull-out edges so cards can scroll right to the section border. */
    margin: 0 calc(var(--space-5) * -1);
    padding-left: var(--space-5);
    padding-right: var(--space-5);
}
.vendor-stories-rail-track::-webkit-scrollbar { height: 6px; }
.vendor-stories-rail-track::-webkit-scrollbar-thumb {
    background: var(--border);
    border-radius: 3px;
}
.vendor-stories-rail-track::-webkit-scrollbar-thumb:hover {
    background: var(--text-secondary);
}

.vendor-stories-card {
    display: flex;
    flex-direction: column;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-input);
    overflow: hidden;
    text-decoration: none;
    color: var(--text-primary);
    scroll-snap-align: start;
    transition: border-color var(--transition), transform var(--transition), box-shadow var(--transition);
}
.vendor-stories-card:hover {
    border-color: var(--text-secondary);
    color: var(--text-primary);
    transform: translateY(-2px);
    box-shadow: 0 8px 20px rgba(15, 23, 42, .08);
}

.vendor-stories-cover {
    position: relative;
    aspect-ratio: 16 / 10;
    background: var(--bg-subtle);
    overflow: hidden;
}
.vendor-stories-cover img {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
}
.vendor-stories-cover-empty {
    width: 100%; height: 100%;
    display: grid;
    place-items: center;
    font-size: 1.75rem;
    color: var(--text-secondary);
}
.vendor-stories-engagement {
    position: absolute;
    top: var(--space-2);
    right: var(--space-2);
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: rgba(15, 23, 42, .82);
    color: #fff;
    font-size: 11.5px;
    font-weight: 700;
    padding: 3px 8px;
    border-radius: var(--radius-pill);
    backdrop-filter: blur(4px);
}
.vendor-stories-engagement > i { color: #ef4444; font-size: 10px; }

.vendor-stories-body {
    padding: var(--space-3) var(--space-4) var(--space-4);
    display: flex;
    flex-direction: column;
    gap: 6px;
    flex: 1 1 auto;
}
.vendor-stories-headline {
    margin: 0;
    font-weight: 700;
    font-size: var(--fs-body);
    line-height: 1.35;
    color: var(--text-primary);
    /* Two-line clamp so cards stay uniform height regardless of title length. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.vendor-stories-byline {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: var(--fs-small);
    color: var(--text-secondary);
    min-width: 0;
}
.vendor-stories-byline img {
    width: 22px; height: 22px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}
.vendor-stories-byline-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}
.vendor-stories-vendor-pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px; height: 18px;
    border-radius: 50%;
    background: var(--primary-900);
    color: var(--accent-500);
    font-size: 10px;
    flex-shrink: 0;
}
.vendor-stories-meta {
    font-size: 12px;
    color: var(--text-secondary);
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    margin-top: auto; /* push to bottom of card for consistent layout */
}
.vendor-stories-meta-dot {
    color: var(--border);
    font-weight: 700;
}

@media (max-width: 575.98px) {
    .vendor-stories-rail {
        padding: var(--space-4) var(--space-4) var(--space-3);
        margin: var(--space-4) 0;
    }
    .vendor-stories-rail-track {
        grid-auto-columns: minmax(220px, 240px);
        margin: 0 calc(var(--space-4) * -1);
        padding-left: var(--space-4);
        padding-right: var(--space-4);
    }
    .vendor-stories-title { font-size: 16px; }
    .vendor-stories-more { font-size: 12.5px; }
}

/* Dark-mode: vp-surface already swaps to var(--bg-card) which resolves to
   #1A1D24 in dark, so the rail's section background is correct. Each card
   inside it also uses --bg-card (same value), so they'd visually disappear
   into the parent without a contrasting border. The light-mode border is
   already var(--border) which the DS lifts in dark — but the resting shadow
   on hover is pointless on a dark-on-dark surface, so soften it. */
html[data-theme="dark"] .vendor-stories-card:hover {
    box-shadow: 0 0 0 1px rgba(255, 255, 255, .06), 0 8px 24px rgba(0, 0, 0, .35);
}

/* ─────────────────────────────────────────────────────────────────
   WHATSAPP SHARE BUTTON
   Used by <x-whatsapp-share> on service pages, vendor profiles, and
   saved list pages. Malaysian users live in WhatsApp; direct share
   beats the system sheet for the common case. */
.wa-share-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: #25D366 !important; /* WhatsApp green — universally recognised */
    border-color: rgba(37, 211, 102, 0.3) !important;
    text-decoration: none;
}
.wa-share-btn:hover {
    background: rgba(37, 211, 102, 0.08) !important;
    border-color: rgba(37, 211, 102, 0.5) !important;
}
.wa-share-btn i { font-size: 16px; }
.wa-share-pill {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    display: inline-grid;
    place-items: center;
    background: rgba(37, 211, 102, 0.1);
    color: #25D366;
    text-decoration: none;
    transition: background-color .15s ease, transform .1s ease;
}
.wa-share-pill:hover {
    background: rgba(37, 211, 102, 0.18);
    color: #1da955;
    transform: translateY(-1px);
}
.wa-share-pill i { font-size: 17px; }

/* ─────────────────────────────────────────────────────────────────
   PAYMENT-METHOD AFFORDANCE on confirm-pay modal
   Text badges (not licensed logos) styled with each brand's hint
   colour. Signals what payment rails are available before clicking
   through to the gateway — important for first-time Malaysian
   customers who associate "safe to pay" with seeing their bank. */
.confirm-pay-methods {
    margin-top: 16px;
    padding-top: 14px;
    border-top: 1px solid var(--border-100, #eef0f5);
}
.confirm-pay-methods-label {
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-secondary, #64748b);
    font-weight: 600;
    display: block;
    margin-bottom: 8px;
}
.confirm-pay-methods-list {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}
.pm-badge {
    display: inline-flex;
    align-items: center;
    height: 26px;
    padding: 0 10px;
    border-radius: 6px;
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .02em;
    border: 1px solid transparent;
    user-select: none;
}
.pm-badge.pm-fpx {
    background: rgba(0, 102, 204, 0.08);
    color: #0066cc;
    border-color: rgba(0, 102, 204, 0.2);
}
.pm-badge.pm-bank {
    background: rgba(255, 198, 0, 0.12);
    color: #806300;
    border-color: rgba(255, 198, 0, 0.3);
}
.pm-badge.pm-ewallet {
    background: rgba(255, 87, 34, 0.08);
    color: #c84a18;
    border-color: rgba(255, 87, 34, 0.2);
}
.pm-badge.pm-card {
    background: rgba(99, 102, 241, 0.08);
    color: #4f46e5;
    border-color: rgba(99, 102, 241, 0.2);
}

/* ─────────────────────────────────────────────────────────────────
   POST-INQUIRY "WHAT HAPPENS NEXT?" CARD
   Surfaced on the customer booking show page when ?just_sent=1.
   Calms anxious first-time inquirers, sets timeline expectations,
   and gives them something productive to do while they wait. */
.inquiry-sent-card {
    border-left: 4px solid var(--accent-500, #8FE000);
    background: linear-gradient(180deg, rgba(var(--accent-rgb, 143, 224, 0), 0.06) 0%, var(--bg-card, #FFFFFF) 60%);
}
.inquiry-sent-icon {
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: var(--accent-500, #8FE000);
    color: #0d1733;
    display: grid;
    place-items: center;
    font-size: 22px;
}
.inquiry-sent-timeline {
    list-style: none;
    padding-left: 0;
    margin: 0 0 14px;
    counter-reset: stepNum;
}
.inquiry-sent-timeline > li {
    counter-increment: stepNum;
    position: relative;
    padding: 6px 0 6px 36px;
    color: var(--text-secondary, #475569);
    font-size: 13.5px;
    line-height: 1.5;
    border-left: 2px dashed var(--border-200, #d8deea);
    margin-left: 10px;
}
.inquiry-sent-timeline > li:last-child { border-left-color: transparent; }
.inquiry-sent-timeline > li::before {
    content: counter(stepNum);
    position: absolute;
    left: -12px;
    top: 6px;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--brand-500, #5b6cff);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    display: grid;
    place-items: center;
}
.inquiry-sent-timeline > li > strong {
    display: block;
    color: var(--text-primary, var(--ink-900, #0d1733));
    font-weight: 700;
    font-size: 14px;
    margin-bottom: 2px;
}
.inquiry-sent-tips {
    padding-top: 12px;
    border-top: 1px solid var(--border-100, #eef0f5);
}

/* ─────────────────────────────────────────────────────────────────
   PLAN-B BANNER — Phase 2.3 customer trust guarantee.

   Shown on a cancelled booking's detail page when the vendor cancelled
   inside the harm window AND we found alternates. Visual intent:
   honest about the disruption (subtle red left-bar) but immediately
   solution-oriented (3-up card grid with one-click rebook CTAs). NOT
   a celebration — a recovery surface. The amber accent reads "active
   alert with a path forward" rather than "good news!".
   ───────────────────────────────────────────────────────────────── */
.plan-b-banner {
    border-left: 4px solid #f59e0b;
    background: linear-gradient(180deg, rgba(245, 158, 11, 0.06) 0%, var(--bg-card, #FFFFFF) 60%);
}
.plan-b-icon {
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: #f59e0b;
    color: #fff;
    display: grid;
    place-items: center;
    font-size: 22px;
}
.plan-b-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 14px;
}
.plan-b-card {
    background: var(--bg-card, #fff);
    border: 1px solid var(--border-100, #eef0f5);
    border-radius: 12px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.plan-b-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 18px rgba(13, 23, 51, 0.08);
    border-color: var(--accent-500, #8FE000);
}
.plan-b-thumb {
    width: 100%;
    aspect-ratio: 16 / 10;
    background-color: #f1f5f9;
    background-size: cover;
    background-position: center;
}
.plan-b-thumb-empty {
    display: grid;
    place-items: center;
    color: #94a3b8;
    font-size: 28px;
}
.plan-b-body {
    padding: 12px 14px 14px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    flex: 1 1 auto;
}
.plan-b-title {
    font-weight: 700;
    color: var(--text-primary, #0d1733);
    font-size: 14.5px;
    line-height: 1.35;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.plan-b-vendor {
    font-size: 12px;
    color: var(--text-secondary, #475569);
}
.plan-b-vendor > i {
    margin-right: 3px;
    color: #94a3b8;
}
.plan-b-price {
    margin-top: 4px;
    display: flex;
    align-items: baseline;
    gap: 6px;
}
.plan-b-price > strong {
    color: var(--accent-500, #8FE000);
    font-size: 15px;
}

/* ─────────────────────────────────────────────────────────────────
   INLINE FIELD-ERROR component output
   Used by <x-field-error name="…"/> — renders below an input with the
   server's validation message. Small, scannable, no extra spacing
   that would push the form layout around when it appears/disappears. */
.field-error {
    color: #b42318;
    font-weight: 500;
    font-size: 12.5px;
    margin-top: 4px;
    display: flex;
    align-items: flex-start;
    gap: 5px;
    line-height: 1.4;
}
.field-error > i { flex: 0 0 auto; padding-top: 2px; font-size: 13px; }

/* ─────────────────────────────────────────────────────────────────
   BRANDED ERROR PAGES (404 / 403)
   Friendly recovery-oriented layout — search box, top action links,
   support email. The 500 page is intentionally standalone HTML
   (errors/500.blade.php) so it doesn't depend on this CSS file.
   ───────────────────────────────────────────────────────────────── */
.amboi-errpage {
    max-width: 720px;
    margin: 48px auto;
    padding: 0 16px;
}
.amboi-errpage-card {
    background: var(--bg-card, #FFFFFF);
    border-radius: 20px;
    padding: 40px 32px;
    box-shadow: 0 12px 40px rgba(18, 26, 58, .08);
    border: 1px solid var(--border-100, #eef0f5);
}
.amboi-errpage-art {
    position: relative;
    width: 140px;
    height: 140px;
    margin: 0 auto 24px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--brand-500, #5b6cff) 0%, var(--brand-700, #2f3da8) 100%);
    color: #fff;
    display: grid;
    place-items: center;
}
.amboi-errpage-art.is-warning {
    background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
}
.amboi-errpage-code {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    font-size: 44px;
    font-weight: 800;
    letter-spacing: -.04em;
    opacity: .14;
    color: #fff;
}
.amboi-errpage-glyph {
    font-size: 56px;
    line-height: 1;
    position: relative;
    z-index: 1;
}
.amboi-errpage-title {
    margin: 0 0 12px;
    font-weight: 800;
    font-size: 26px;
    letter-spacing: -.01em;
    color: var(--text-primary, var(--ink-900, #0d1733));
    text-align: center;
}
.amboi-errpage-body {
    margin: 0 0 24px;
    color: var(--text-secondary, var(--ink-600, #475569));
    line-height: 1.6;
    text-align: center;
    max-width: 480px;
    margin-left: auto;
    margin-right: auto;
}
.amboi-errpage-search {
    display: flex;
    align-items: center;
    gap: 8px;
    background: var(--bg-subtle, #fafbfd);
    border: 1px solid var(--border-100, #eef0f5);
    border-radius: 14px;
    padding: 6px 6px 6px 14px;
    margin: 0 auto 24px;
    max-width: 480px;
}
.amboi-errpage-search > i.bi-search {
    color: var(--text-secondary, #64748b);
    font-size: 16px;
}
.amboi-errpage-search input {
    flex: 1 1 auto;
    border: 0;
    background: transparent;
    padding: 10px 4px;
    font-size: 15px;
    outline: none;
    color: inherit;
    min-width: 0;
}
.amboi-errpage-links {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 10px;
    margin-bottom: 24px;
}
.amboi-errpage-link {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
    background: var(--bg-subtle, #fafbfd);
    border: 1px solid var(--border-100, #eef0f5);
    border-radius: 12px;
    text-decoration: none;
    color: var(--text-primary, var(--ink-900, #0d1733));
    transition: background-color .15s ease, transform .1s ease, border-color .15s ease;
}
.amboi-errpage-link:hover {
    background: var(--bg-card, #fff);
    border-color: var(--brand-500, #5b6cff);
    transform: translateY(-1px);
    color: var(--text-primary, var(--ink-900, #0d1733));
}
.amboi-errpage-link > i {
    font-size: 22px;
    color: var(--brand-500, #5b6cff);
    flex: 0 0 auto;
}
.amboi-errpage-link strong {
    display: block;
    font-weight: 600;
    font-size: 14px;
    margin-bottom: 2px;
}
.amboi-errpage-link small {
    color: var(--text-secondary, #64748b);
    font-size: 12.5px;
}
.amboi-errpage-foot {
    margin: 0;
    padding-top: 20px;
    border-top: 1px solid var(--border-100, #eef0f5);
    text-align: center;
    font-size: 13.5px;
    color: var(--text-secondary, #64748b);
}
.amboi-errpage-foot a {
    color: var(--brand-500, #5b6cff);
    font-weight: 600;
}
@media (max-width: 575.98px) {
    .amboi-errpage-card { padding: 28px 20px; }
    .amboi-errpage-art { width: 100px; height: 100px; }
    .amboi-errpage-glyph { font-size: 40px; }
    .amboi-errpage-code { font-size: 30px; }
    .amboi-errpage-title { font-size: 22px; }
}

/* ─────────────────────────────────────────────────────────────────
   GLOBAL CONFIRM-ACTION MODAL
   Branded replacement for native confirm() dialogs. Activated by
   [data-confirm] attributes on forms and links. Three tones:
     • default   — neutral question (blue)
     • danger    — destructive action (red)
     • financial — money movement (lime accent, more weighty)
   ───────────────────────────────────────────────────────────────── */
.amboi-confirm .modal-dialog { max-width: 460px; }
.amboi-confirm-content {
    border-radius: var(--radius-xl, 18px);
    border: 0;
    overflow: hidden;
    box-shadow: 0 24px 60px rgba(13, 23, 51, .35);
    background: var(--bg-card, #FFFFFF);
}
.amboi-confirm-head {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    padding: 22px 22px 8px;
}
.amboi-confirm-icon {
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    font-size: 22px;
    /* Tone-specific backgrounds applied via parent class. */
}
.amboi-confirm.is-default .amboi-confirm-icon {
    background: rgba(91, 108, 255, .12);
    color: var(--brand-500, #5b6cff);
}
.amboi-confirm.is-danger .amboi-confirm-icon {
    background: rgba(220, 53, 69, .12);
    color: #dc3545;
}
.amboi-confirm.is-financial .amboi-confirm-icon {
    background: rgba(198, 242, 78, .25);
    color: #5b7a1a;
}
.amboi-confirm-title-wrap { flex: 1 1 auto; min-width: 0; }
.amboi-confirm-title {
    margin: 0 0 4px;
    font-weight: 700;
    font-size: 17px;
    color: var(--text-primary, var(--ink-900, #0d1733));
}
.amboi-confirm-body {
    margin: 0;
    color: var(--text-secondary, var(--ink-600, #475569));
    font-size: 14.5px;
    line-height: 1.5;
    white-space: pre-line; /* honor \n in passed strings */
}
.amboi-confirm-typed {
    padding: 0 22px 4px;
}
.amboi-confirm-typed input {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    letter-spacing: .02em;
}
.amboi-confirm-foot {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    padding: 16px 22px 18px;
    border-top: 1px solid var(--border-100, #eef0f5);
    margin-top: 14px;
    background: var(--bg-subtle, #fafbfd);
}
/* Reuse danger button styling for tone-danger primary confirms. */
.vp-btn-danger {
    background: #dc3545;
    color: #fff;
    border: 0;
    padding: 8px 16px;
    border-radius: 10px;
    font-weight: 600;
    cursor: pointer;
    transition: background-color .15s ease;
}
.vp-btn-danger:hover:not(:disabled) { background: #b02a37; }
.vp-btn-danger:disabled { opacity: .55; cursor: not-allowed; }

/* ─────────────────────────────────────────────────────────────────
   FIRST-RUN CUSTOMER TOUR
   Three-slide intro modal that fires once on the first customer
   dashboard visit. Friendly, branded, and skippable. Lives in the
   customer/partials/first-run-tour.blade.php partial.
   ───────────────────────────────────────────────────────────────── */
.amboi-tour .modal-dialog { max-width: 460px; }
.amboi-tour-content {
    border-radius: var(--radius-xl, 20px);
    border: 0;
    overflow: hidden;
    box-shadow: 0 24px 60px rgba(13, 23, 51, .35);
}
.amboi-tour-deck {
    position: relative;
    padding: 32px 28px 20px;
    min-height: 360px;
    background: linear-gradient(180deg, var(--surface-0, #fff) 0%, var(--surface-1, #f8fafc) 100%);
}
.amboi-tour-slide {
    display: none;
    text-align: center;
    animation: amboiTourFade 280ms ease-out;
}
.amboi-tour-slide.is-active { display: block; }
@keyframes amboiTourFade {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}
.amboi-tour-art {
    position: relative;
    width: 120px;
    height: 120px;
    margin: 0 auto 20px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--brand-500, #5b6cff) 0%, var(--brand-700, #2f3da8) 100%);
    display: grid;
    place-items: center;
    color: #fff;
}
.amboi-tour-art.amboi-tour-art-match {
    background: linear-gradient(135deg, #ff9966 0%, #ff5e62 100%);
}
.amboi-tour-art.amboi-tour-art-shield {
    /* Both gradient stops use accent tokens — fallback hexes updated
       from legacy lime (#c6f24e / #7eb83b) to Rekaizen teal so the
       first-paint frame (before CSS vars resolve) matches the brand
       instead of flashing lime. */
    background: linear-gradient(135deg, var(--accent-500, #51B4BE) 0%, var(--accent-700, #2D7E87) 100%);
    color: var(--accent-fg, #FFFFFF);
}
.amboi-tour-art-glyph i { font-size: 52px; line-height: 1; }
.amboi-tour-art-dot {
    position: absolute;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: rgba(255, 255, 255, .6);
    animation: amboiTourBob 2.2s ease-in-out infinite;
    animation-delay: var(--d, 0s);
}
.amboi-tour-art-dot:nth-child(2) { top: 10px;  right: 14px; }
.amboi-tour-art-dot:nth-child(3) { bottom: 18px; left: 8px; }
.amboi-tour-art-dot:nth-child(4) { top: 50%; right: -2px; }
@keyframes amboiTourBob {
    0%, 100% { transform: translateY(0); opacity: .7; }
    50%      { transform: translateY(-6px); opacity: 1; }
}
.amboi-tour-title {
    margin: 0 0 10px;
    font-weight: 700;
    font-size: 20px;
    color: var(--ink-900, #0d1733);
}
.amboi-tour-body {
    margin: 0;
    color: var(--ink-600, #475569);
    font-size: 14.5px;
    line-height: 1.55;
}
.amboi-tour-fineprint {
    margin: 16px 0 0;
    font-size: 12.5px;
    color: var(--ink-500, #64748b);
}
.amboi-tour-fineprint i { color: var(--accent-700, #2D7E87); margin-right: 4px; }

.amboi-tour-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 20px 18px;
    background: var(--surface-0, #fff);
    border-top: 1px solid var(--border-100, #eef0f5);
}
.amboi-tour-dots {
    display: inline-flex;
    gap: 8px;
}
.amboi-tour-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--border-200, #d8deea);
    border: 0;
    padding: 0;
    cursor: pointer;
    transition: background-color .2s ease, transform .2s ease;
}
.amboi-tour-dot.is-active {
    background: var(--brand-500, #5b6cff);
    transform: scale(1.25);
}
.amboi-tour-actions {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}
@media (max-width: 575.98px) {
    .amboi-tour .modal-dialog { margin: 12px; }
    .amboi-tour-deck { min-height: 320px; padding: 24px 18px 14px; }
    .amboi-tour-art { width: 92px; height: 92px; }
    .amboi-tour-art-glyph i { font-size: 40px; }
    .amboi-tour-title { font-size: 18px; }
    .amboi-tour-foot { padding: 12px 14px; flex-wrap: wrap; }
    .amboi-tour-actions { width: 100%; justify-content: flex-end; }
}

/* ── Floating support widget ─────────────────────────────────────────
   Chat-bubble entry into the support-ticket system. Lives in the DS
   sheet (not @push'd from the component) because layouts emit
   @stack('styles') in the <head> and the component renders late in
   the body — @push'd styles would arrive AFTER the stack flushed.
   See app/Http/Controllers/SupportWidgetController.php for the JSON
   API the JS in the component talks to. */
#support-widget {
    position: fixed;
    bottom: 20px;
    right: 20px;
    z-index: 1040;       /* above most content, below Bootstrap modals (1050) */
    font-family: var(--font);
}
body[data-hide-support-widget] #support-widget { display: none; }

.sw-bubble {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    border: 0;
    background: var(--accent-500, #8FE000);
    color: var(--text-primary, #0d1733);
    display: grid;
    place-items: center;
    font-size: 24px;
    cursor: pointer;
    box-shadow: 0 6px 20px rgba(13, 23, 51, 0.18);
    transition: transform .15s ease, box-shadow .15s ease;
    position: relative;
}
.sw-bubble:hover {
    transform: translateY(-2px);
    box-shadow: 0 10px 24px rgba(13, 23, 51, 0.24);
}
.sw-bubble:focus-visible {
    outline: 3px solid var(--accent-600, #6FB000);
    outline-offset: 3px;
}
.sw-badge {
    position: absolute;
    /* Same circle-edge anchoring as .asw-badge — see the comment there
       for the geometry rationale. The two widgets render identical-size
       bubbles (52-56px circles), so the same translate keeps the badge
       attached to the 1-2 o'clock curve on both. */
    top: 0;
    right: 0;
    transform: translate(35%, -35%);
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 999px;
    background: #ef4444;
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    display: grid;
    place-items: center;
    border: 2px solid var(--bg-card, #fff);
}

/* ── Admin mini-reply widget ─────────────────────────────────────────
   Floating triage panel for admins, mounted on every admin page. Visually
   parallel to .sw-* (customer support) and .vc-* (vendor chat) so the
   three floating surfaces feel like siblings. Distinguished by a navy
   bubble (vs the customer widget's lime) so admins can tell at a glance
   which widget they're looking at. */
#admin-support-widget {
    position: fixed;
    bottom: 20px;
    right: 172px;        /* leaves room for vendor chat at right:96px AND support bubble at right:20px */
    z-index: 1039;       /* one below vendor chat so its pills can overlay if both appear */
    font-family: var(--font);
}
.asw-bubble {
    width: 52px; height: 52px;
    border-radius: 50%;
    border: 0;
    background: var(--primary-900, #121A3A);
    color: #fff;
    display: grid;
    place-items: center;
    font-size: 22px;
    cursor: pointer;
    box-shadow: 0 6px 20px rgba(13, 23, 51, 0.18);
    transition: transform .15s ease, box-shadow .15s ease;
    position: relative;
}
.asw-bubble:hover {
    transform: translateY(-2px);
    box-shadow: 0 10px 24px rgba(13, 23, 51, 0.24);
}
.asw-bubble:focus-visible {
    outline: 3px solid var(--accent-500, #8FE000);
    outline-offset: 3px;
}
.asw-badge {
    position: absolute;
    /* Position the badge so its CENTER sits on the visible circle's
       1-2 o'clock arc — using top: 0 + right: 0 with translate(35%, -35%)
       anchors to the bounding-box corner and shifts the badge half-out
       diagonally, which lands its centre right on the curve of a circular
       bubble. The earlier top: -2; right: -2 sat at the bounding-box
       corner itself, which for a circle is ~7.6px outside the visible
       edge, leaving the badge looking detached / floating. */
    top: 0;
    right: 0;
    transform: translate(35%, -35%);
    min-width: 18px; height: 18px;
    padding: 0 5px;
    border-radius: 999px;
    background: #ef4444;
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    display: grid;
    place-items: center;
    border: 2px solid var(--bg-card, #fff);
}
.asw-panel {
    position: absolute;
    bottom: 70px;
    right: 0;
    width: 380px;
    max-width: calc(100vw - 40px);
    height: 540px;
    max-height: calc(100vh - 100px);
    background: var(--bg-card, #fff);
    border: 1px solid var(--border, rgba(13,23,51,.08));
    border-radius: 16px;
    box-shadow: 0 18px 42px rgba(13, 23, 51, 0.18);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    animation: asw-slide-in .18s ease-out;
}
@keyframes asw-slide-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}
@media (max-width: 480px) {
    #admin-support-widget { right: 0; bottom: 0; left: 0; }
    .asw-panel {
        position: fixed; inset: 0;
        width: auto; height: auto; max-width: none; max-height: none;
        border-radius: 0;
    }
}
.asw-head {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 14px;
    background: var(--primary-900, #121A3A);
    color: #fff;
}
.asw-back, .asw-close, .asw-link {
    border: 0; background: transparent;
    color: inherit;
    display: grid; place-items: center;
    width: 32px; height: 32px;
    border-radius: 8px;
    cursor: pointer;
    transition: background .12s ease;
}
.asw-back:hover, .asw-close:hover, .asw-link:hover { background: rgba(255,255,255,.12); }
.asw-link { color: inherit; text-decoration: none; }
.asw-head-icon { font-size: 20px; flex-shrink: 0; }
.asw-head-text { flex: 1; line-height: 1.25; min-width: 0; }
.asw-head-text strong { display: block; font-size: 14px; }
.asw-head-text small { color: rgba(255,255,255,.7); font-size: 11px; }

.asw-body, .asw-thread {
    flex: 1;
    overflow-y: auto;
    background: var(--bg-app, #f8f9fa);
}
.asw-empty { text-align: center; padding: 32px 12px; }

/* Queue list */
.asw-list { padding: 8px; display: flex; flex-direction: column; gap: 6px; }
.asw-row {
    text-align: left;
    border: 1px solid var(--border);
    background: var(--bg-card, #fff);
    border-radius: 10px;
    padding: 10px 12px;
    cursor: pointer;
    display: flex; flex-direction: column; gap: 4px;
    transition: background .12s ease, transform .1s ease;
}
.asw-row:hover { background: var(--bg-subtle, #f1f3f5); }
.asw-row:active { transform: scale(0.99); }
.asw-row-meta strong { font-size: 13px; display: block; }
.asw-row-meta small { color: var(--text-secondary); font-size: 11px; display: block; }
.asw-row-preview {
    font-size: 12px;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}
.asw-prio {
    display: inline-block;
    padding: 1px 6px;
    border-radius: 999px;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    margin-left: 4px;
    background: var(--bg-subtle);
}
.asw-prio-urgent { background: #fee2e2; color: #b91c1c; }
.asw-prio-high   { background: #fef3c7; color: #b45309; }

/* Thread view */
.asw-thread { display: flex; flex-direction: column; }
.asw-thread-meta {
    padding: 10px 14px;
    border-bottom: 1px solid var(--border);
    background: var(--bg-card);
}
.asw-thread-meta strong { display: block; font-size: 13px; }
.asw-thread-meta small { display: block; color: var(--text-secondary); font-size: 11px; }
.asw-thread-body { flex: 1; padding: 14px; overflow-y: auto; }
.asw-form {
    display: flex; gap: 8px; align-items: flex-end;
    padding: 10px 12px;
    border-top: 1px solid var(--border);
    background: var(--bg-card, #fff);
}
.asw-form textarea {
    flex: 1; resize: none;
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 8px 10px;
    font: inherit; font-size: 13.5px; line-height: 1.4;
    max-height: 120px;
}
.asw-form textarea:focus { outline: 2px solid var(--accent-500); outline-offset: 0; }
.asw-send {
    width: 36px; height: 36px;
    border-radius: 50%;
    border: 0;
    background: var(--accent-500);
    color: var(--text-primary);
    display: grid; place-items: center;
    cursor: pointer;
    transition: transform .1s ease, background .12s ease;
}
.asw-send:hover { transform: scale(1.05); }
.asw-send:disabled { opacity: .5; cursor: not-allowed; }

/* Help Center content shell — single-column centered layout.
   Used by all 4 /help/* views (index, category, article, search).
   The 720px cap matches typical knowledge-base optical width (Notion,
   Intercom, Zendesk): wide enough for two-up category cards via the
   inner .col-md-6 grid, narrow enough that the article body stays
   readable. Floating support widget sits outside this shell. */
.help-center-shell {
    max-width: 720px;
}

.sw-panel {
    position: absolute;
    bottom: 70px;
    right: 0;
    width: 360px;
    max-width: calc(100vw - 40px);
    height: 520px;
    max-height: calc(100vh - 100px);
    background: var(--bg-card, #fff);
    border: 1px solid var(--border, rgba(13,23,51,.08));
    border-radius: 16px;
    box-shadow: 0 18px 42px rgba(13, 23, 51, 0.18);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    animation: sw-slide-in .18s ease-out;
}
@keyframes sw-slide-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}
@media (max-width: 480px) {
    .sw-panel {
        position: fixed;
        inset: 0;
        width: auto;
        height: auto;
        max-width: none;
        max-height: none;
        border-radius: 0;
    }
}

/* Inline mode — retained for potential future embedding (e.g. admin
   support inbox preview). The Help Center used to use this in a right-
   rail column but switched to a single-column centered layout with the
   floating widget instead (2026-05-20). Reset the floating positioning
   so the panel sits in normal document flow, sticky at the top of the
   viewport. The surrounding host page is responsible for putting it in
   a containing column. */
.sw-panel-inline {
    position: sticky;
    top: var(--space-3, 16px);
    bottom: auto;
    right: auto;
    /* Fill the column. The wrapping aside (col-lg-4) already constrains
       us to ~30% of the container; no need for an extra max-width that
       leaves dead space inside the column. */
    width: 100%;
    max-width: none;
    height: clamp(440px, 70vh, 600px);
    max-height: calc(100vh - 120px);
    animation: none;
}
@media (max-width: 991.98px) {
    /* Below the lg breakpoint the col-lg-4 wraps under the col-lg-8
       content. Stop being sticky and become a normal card. */
    .sw-panel-inline {
        position: relative;
        top: auto;
        height: 440px;
    }
}

.sw-head-icon {
    color: var(--accent-500);
    font-size: 18px;
    margin-right: 4px;
}

.sw-head {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
    border-bottom: 1px solid var(--border);
    background: var(--primary-900, #121A3A);
    color: #fff;
}
.sw-head-text { flex: 1; line-height: 1.25; }
.sw-head-text strong { display: block; font-size: 15px; }
.sw-head-text small { color: rgba(255,255,255,.7); font-size: 11px; }
.sw-close {
    background: transparent; border: 0; color: #fff;
    width: 28px; height: 28px; border-radius: 6px;
    display: grid; place-items: center; cursor: pointer;
    transition: background .12s ease;
}
.sw-close:hover { background: rgba(255,255,255,.12); }

.sw-body {
    flex: 1;
    overflow-y: auto;
    padding: 16px;
    background: var(--bg-main, #f5f5f5);
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.sw-empty { text-align: center; padding: 32px 8px; }

.sw-msg {
    max-width: 80%;
    padding: 8px 12px;
    border-radius: 14px;
    font-size: 13.5px;
    line-height: 1.45;
    word-wrap: break-word;
    white-space: pre-wrap;
}
.sw-msg-mine {
    align-self: flex-end;
    background: var(--accent-500, #8FE000);
    color: var(--text-primary);
    border-bottom-right-radius: 4px;
}
.sw-msg-admin {
    align-self: flex-start;
    background: #fff;
    border: 1px solid var(--border);
    border-bottom-left-radius: 4px;
}
.sw-msg-meta {
    font-size: 10.5px;
    color: var(--text-secondary);
    margin-top: 2px;
}

.sw-suggestions {
    border-top: 1px solid var(--border);
    background: #fffbeb;
    padding: 8px 12px;
    font-size: 12px;
}
.sw-suggestion {
    display: block;
    padding: 6px 8px;
    border-radius: 6px;
    color: var(--text-primary);
    text-decoration: none;
    transition: background .12s ease;
}
.sw-suggestion:hover { background: rgba(255, 235, 100, .25); color: var(--text-primary); }
.sw-suggestion strong { display: block; font-size: 12.5px; }
.sw-suggestion small { color: var(--text-secondary); }

.sw-chips {
    padding: 8px 12px;
    border-top: 1px solid var(--border);
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    background: var(--bg-card, #fff);
}
.sw-chip {
    font-size: 11px;
    padding: 4px 10px;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: var(--bg-main);
    cursor: pointer;
    transition: background .12s ease, border-color .12s ease;
}
.sw-chip:hover { background: var(--accent-500); border-color: var(--accent-500); }
.sw-chip.active {
    background: var(--accent-500);
    border-color: var(--accent-600);
}

.sw-form {
    display: flex;
    gap: 8px;
    align-items: flex-end;
    padding: 10px 12px;
    border-top: 1px solid var(--border);
    background: var(--bg-card, #fff);
}
.sw-form textarea {
    flex: 1;
    resize: none;
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 8px 10px;
    font: inherit;
    font-size: 13.5px;
    line-height: 1.4;
    max-height: 120px;
}
.sw-form textarea:focus { outline: 2px solid var(--accent-500); outline-offset: 0; }
.sw-send {
    width: 36px; height: 36px;
    border-radius: 50%;
    border: 0;
    background: var(--accent-500);
    color: var(--text-primary);
    display: grid; place-items: center;
    cursor: pointer;
    transition: transform .1s ease, background .12s ease;
}
.sw-send:hover { transform: scale(1.05); }
.sw-send:disabled { opacity: .5; cursor: not-allowed; }

/* Image-attachment chrome for the support widget — mirrors the vendor
   chat counterparts (.vc-attach, .vc-pending, .vc-drop-overlay, etc.)
   so the two surfaces feel like siblings. Distinguished only by the
   .sw- prefix; visual styling is intentionally identical. */
.sw-attach {
    width: 36px; height: 36px;
    border-radius: 50%;
    border: 0;
    background: var(--bg-subtle, #f1f3f5);
    color: var(--text-secondary, #6c757d);
    display: grid; place-items: center;
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
    flex-shrink: 0;
}
.sw-attach:hover { background: var(--bg-card-hover, #e9ecef); color: var(--text-primary); }

.sw-pending {
    display: flex; flex-wrap: wrap; gap: 6px;
    padding: 8px 12px 0 12px;
    background: var(--bg-card, #fff);
}
.sw-pending-chip {
    position: relative;
    width: 56px; height: 56px;
    border-radius: 8px;
    overflow: hidden;
    background: var(--bg-subtle, #f1f3f5);
    border: 1px solid var(--border);
}
.sw-pending-chip img {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
}
.sw-pending-remove {
    position: absolute; top: 2px; right: 2px;
    width: 18px; height: 18px;
    border-radius: 50%;
    border: 0;
    background: rgba(0,0,0,.65);
    color: #fff;
    font-size: 11px;
    line-height: 1;
    display: grid; place-items: center;
    cursor: pointer;
}
.sw-pending-remove:hover { background: rgba(0,0,0,.85); }

.sw-drop-overlay {
    position: absolute; inset: 0;
    background: rgba(var(--accent-500-rgb, 207, 220, 88), .12);
    border: 2px dashed var(--accent-500);
    border-radius: inherit;
    opacity: 0;
    pointer-events: none;
    transition: opacity .12s ease;
    display: grid; place-items: center;
    z-index: 20;
}
.sw-drop-overlay.is-active { opacity: 1; }
.sw-drop-inner {
    padding: 16px 24px;
    background: rgba(255,255,255,.92);
    border-radius: 12px;
    color: var(--text-primary);
    text-align: center;
}

.sw-msg-attachments {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));
    gap: 4px;
    margin-top: 6px;
}
.sw-msg-attachments a {
    display: block;
    border-radius: 6px;
    overflow: hidden;
    line-height: 0;
}
.sw-msg-attachments img {
    width: 100%; height: 100px;
    object-fit: cover;
    display: block;
    transition: transform .12s ease;
}
.sw-msg-attachments a:hover img { transform: scale(1.03); }

/* ── Floating vendor chat widget ────────────────────────────────────
   Customer's in-page chat panel for messaging a specific vendor.
   Same chrome as the support widget but with a `vc-` prefix and
   positioned LEFT of the support bubble (right: 96px vs 20px) so the
   two coexist. See vendor-chat-widget.blade.php + Customer\ChatWidgetController. */
#vendor-chat-widget {
    position: fixed;
    bottom: 20px;
    right: 96px;          /* leaves room for the support bubble at right:20px */
    z-index: 1040;
    font-family: var(--font);
    /* v6 multi-tab: the widget is a horizontal stack of [pills-row][panel].
       Pills sit LEFT of where the panel anchors. align-items:flex-end keeps
       short pills bottom-aligned with the taller panel so the visual baseline
       reads cleanly. */
    display: flex;
    flex-direction: row;
    align-items: flex-end;
    gap: 8px;
}

/* Pills rail — one pill per minimized chat. Most-recently-touched chat
   appears on the right (closest to the open panel), oldest on the left.
   See vendor-chat-widget.blade.php docblock for the eviction policy. */
.vc-pills {
    display: flex;
    flex-direction: row;
    gap: 6px;
    align-items: flex-end;
    /* Don't push the panel off-screen if 3 pills materialise — they shrink
       to fit. Each pill enforces its own min-width via .vc-pill below. */
    overflow: visible;
}

/* Pill — minimized state of the vendor chat. Shows vendor avatar + name
   so the customer can tell at a glance which vendor's conversation is
   pending (distinct from the support widget's generic chat icon). Two
   clickable children: .vc-pill-expand (most of the body, reopens the
   panel) and .vc-pill-dismiss (small × at the right, fully closes). */
.vc-pill {
    display: inline-flex; align-items: stretch;
    border: 0; border-radius: 999px;
    background: var(--accent-500, #8FE000);
    color: var(--text-primary);
    box-shadow: 0 6px 20px rgba(13, 23, 51, 0.18);
    overflow: hidden;
    font-weight: 600; font-size: 13px;
}
.vc-pill-expand {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 6px 4px 6px 6px;
    background: transparent;
    border: 0;
    cursor: pointer;
    color: inherit;
    font: inherit;
    transition: background .12s ease;
}
.vc-pill-expand:hover { background: rgba(0, 0, 0, .06); }
.vc-pill-avatar {
    width: 28px; height: 28px;
    border-radius: 50%;
    object-fit: cover;
    background: rgba(0, 0, 0, .1);
    flex-shrink: 0;
}
.vc-pill-name {
    max-width: 140px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    padding-right: 4px;
}
.vc-pill-dismiss {
    display: grid; place-items: center;
    width: 32px;
    background: transparent;
    border: 0; border-left: 1px solid rgba(0, 0, 0, .12);
    cursor: pointer;
    color: inherit;
    font-size: 14px;
    transition: background .12s ease;
}
.vc-pill-dismiss:hover { background: rgba(0, 0, 0, .12); }

/* Unread-dot indicator — a small red bubble overlaid on the pill avatar
   when the conversation has new activity since the customer last opened
   it. Hidden by default; vendor-chat-widget JS toggles `hidden` per pill
   off the chat.unread flag the state poll updates. */
.vc-pill-unread {
    display: inline-block;
    width: 8px; height: 8px;
    border-radius: 50%;
    background: #ef4444;
    margin: 0 4px 0 0;
    box-shadow: 0 0 0 2px var(--accent-500, #8FE000);
    align-self: center;
}

.vc-panel {
    position: relative;       /* positioning context for .vc-drop-overlay */
    width: 360px;
    max-width: calc(100vw - 116px);
    height: 520px;
    max-height: calc(100vh - 100px);
    background: var(--bg-card, #fff);
    border: 1px solid var(--border, rgba(13,23,51,.08));
    border-radius: 16px;
    box-shadow: 0 18px 42px rgba(13, 23, 51, 0.18);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    animation: vc-slide-in .18s ease-out;
}
@keyframes vc-slide-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}
@media (max-width: 480px) {
    #vendor-chat-widget { right: 0; bottom: 0; left: 0; }
    .vc-panel {
        position: fixed; inset: 0;
        width: auto; height: auto; max-width: none; max-height: none;
        border-radius: 0;
    }
    /* Multi-pill rail is hidden on mobile — full-screen panel can only
       show one conversation at a time, so the rail wouldn't visually fit
       above a 100vh panel. Mobile customers manage one chat at a time via
       Chat-button clicks + the pill restoration on landing pages. */
    .vc-pills { display: none; }
}

.vc-head {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 14px;
    background: var(--primary-900, #121A3A);
    color: #fff;
}
.vc-avatar {
    width: 32px; height: 32px;
    border-radius: 50%;
    object-fit: cover;
    background: rgba(255,255,255,.1);
    flex-shrink: 0;
}
.vc-head-text { flex: 1; line-height: 1.25; min-width: 0; }
.vc-head-text strong {
    display: block; font-size: 14px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.vc-head-text small { color: rgba(255,255,255,.7); font-size: 11px; }
.vc-link, .vc-close {
    background: transparent; border: 0; color: #fff;
    width: 28px; height: 28px; border-radius: 6px;
    display: grid; place-items: center; cursor: pointer;
    text-decoration: none;
    transition: background .12s ease;
}
.vc-link:hover, .vc-close:hover { background: rgba(255,255,255,.12); color: #fff; }

.vc-body {
    flex: 1;
    overflow-y: auto;
    padding: 16px;
    background: var(--bg-main, #f5f5f5);
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.vc-empty { text-align: center; padding: 32px 8px; }
.vc-templates {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    justify-content: center;
    margin-top: 16px;
}
.vc-template {
    background: var(--bg-card, #fff);
    border: 1px solid var(--border, rgba(13,23,51,.12));
    color: var(--text-primary);
    font-size: 12px;
    padding: 6px 10px;
    border-radius: 999px;
    cursor: pointer;
    transition: background .12s ease, border-color .12s ease;
}
.vc-template:hover {
    background: var(--accent-50, #f5fae6);
    border-color: var(--accent-300, #c8e88c);
}

.vc-msg {
    max-width: 80%;
    padding: 8px 12px;
    border-radius: 14px;
    font-size: 13.5px;
    line-height: 1.45;
    word-wrap: break-word;
    white-space: pre-wrap;
}
.vc-msg-mine {
    align-self: flex-end;
    background: var(--accent-500, #8FE000);
    color: var(--text-primary);
    border-bottom-right-radius: 4px;
}
.vc-msg-theirs {
    align-self: flex-start;
    background: #fff;
    border: 1px solid var(--border);
    border-bottom-left-radius: 4px;
}
.vc-msg-meta { font-size: 10.5px; color: var(--text-secondary); margin-top: 2px; }

.vc-form {
    display: flex; gap: 8px; align-items: flex-end;
    padding: 10px 12px;
    border-top: 1px solid var(--border);
    background: var(--bg-card, #fff);
}
.vc-form textarea {
    flex: 1; resize: none;
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 8px 10px;
    font: inherit; font-size: 13.5px; line-height: 1.4;
    max-height: 120px;
}
.vc-form textarea:focus { outline: 2px solid var(--accent-500); outline-offset: 0; }
.vc-send {
    width: 36px; height: 36px;
    border-radius: 50%;
    border: 0;
    background: var(--accent-500);
    color: var(--text-primary);
    display: grid; place-items: center;
    cursor: pointer;
    transition: transform .1s ease, background .12s ease;
}
.vc-send:hover { transform: scale(1.05); }
.vc-send:disabled { opacity: .5; cursor: not-allowed; }

/* Image-attachment chrome — paperclip button mirrors .vc-send's circular
   chip but uses the subtle surface so the lime send button stays the
   primary call-to-action. */
.vc-attach {
    width: 36px; height: 36px;
    border-radius: 50%;
    border: 0;
    background: var(--bg-subtle, #f1f3f5);
    color: var(--text-secondary, #6c757d);
    display: grid; place-items: center;
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
    flex-shrink: 0;
}
.vc-attach:hover { background: var(--bg-card-hover, #e9ecef); color: var(--text-primary); }

/* Pre-send thumbnail strip — chip carries the preview, the × button
   removes a single pending file without nuking the rest of the queue. */
.vc-pending {
    display: flex; flex-wrap: wrap; gap: 6px;
    padding: 8px 12px 0 12px;
    background: var(--bg-card, #fff);
}
.vc-pending-chip {
    position: relative;
    width: 56px; height: 56px;
    border-radius: 8px;
    overflow: hidden;
    background: var(--bg-subtle, #f1f3f5);
    border: 1px solid var(--border);
}
.vc-pending-chip img {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
}
.vc-pending-remove {
    position: absolute; top: 2px; right: 2px;
    width: 18px; height: 18px;
    border-radius: 50%;
    border: 0;
    background: rgba(0,0,0,.65);
    color: #fff;
    font-size: 11px;
    line-height: 1;
    display: grid; place-items: center;
    cursor: pointer;
}
.vc-pending-remove:hover { background: rgba(0,0,0,.85); }

/* Drag-over overlay — full-panel curtain. Hidden by default; the JS adds
   `.is-active` on dragover. Pointer-events: none so the drop fires on
   the original target underneath. */
.vc-drop-overlay {
    position: absolute; inset: 0;
    background: rgba(var(--accent-500-rgb, 207, 220, 88), .12);
    border: 2px dashed var(--accent-500);
    border-radius: inherit;
    opacity: 0;
    pointer-events: none;
    transition: opacity .12s ease;
    display: grid; place-items: center;
    z-index: 20;
}
.vc-drop-overlay.is-active { opacity: 1; }
.vc-drop-inner {
    padding: 16px 24px;
    background: rgba(255,255,255,.92);
    border-radius: 12px;
    color: var(--text-primary);
    text-align: center;
}

/* In-bubble image rendering — single thumbnail per attachment, clicking
   opens the full-size in a new tab. Grid handles 1, 2, 3, or 4 photos. */
.vc-msg-attachments {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));
    gap: 4px;
    margin-top: 6px;
}
.vc-msg-attachments a {
    display: block;
    border-radius: 6px;
    overflow: hidden;
    line-height: 0;
}
.vc-msg-attachments img {
    width: 100%; height: 100px;
    object-fit: cover;
    display: block;
    transition: transform .12s ease;
}
.vc-msg-attachments a:hover img { transform: scale(1.03); }

/* ============================================================
   Aspect-ratio image containers — Refactoring UI T4.2 (CLS prevention)
   ────────────────────────────────────────────────────────────
   User uploads arrive at arbitrary dimensions. Wrapping each img
   in an aspect-ratio container reserves the layout slot before
   the bytes arrive, so the page doesn't shift when the image
   resolves. Use object-fit:cover on the img child to crop to
   the slot (focal-point UX deferred — see docs/rui-image-policy.md
   Tier D).

   Usage:
       <div class="amboi-aspect amboi-aspect-16x9">
           <img src="..." alt="...">
       </div>

   Browser support: `aspect-ratio` shipped in every modern browser
   in 2021. Pre-2021 fallbacks (padding-bottom hack) intentionally
   omitted; Amboi targets evergreen browsers.
============================================================ */
.amboi-aspect {
    position: relative;
    width: 100%;
    overflow: hidden;
    background: var(--bg-subtle);   /* neutral skeleton while img loads */
}
.amboi-aspect > img,
.amboi-aspect > video,
.amboi-aspect > picture > img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.amboi-aspect-square  { aspect-ratio: 1 / 1; }
.amboi-aspect-16x9    { aspect-ratio: 16 / 9; }
.amboi-aspect-4x3     { aspect-ratio: 4 / 3; }
.amboi-aspect-3x4     { aspect-ratio: 3 / 4; }   /* portrait story covers */
.amboi-aspect-5x4     { aspect-ratio: 5 / 4; }   /* vendor service cards */
.amboi-aspect-21x9    { aspect-ratio: 21 / 9; }  /* cinematic hero */

/* ============================================================
   Touch-target accommodations — Refactoring UI accessibility
   ────────────────────────────────────────────────────────────
   WCAG 2.5.5 (Target Size) + Apple HIG say 44×44 minimum for
   primary controls on touch surfaces. Desktop pointers can
   reliably hit 32px targets; coarse pointers (fingers) can't.
   `(pointer: coarse)` is the correct media query — it fires for
   touch + stylus regardless of viewport width, so a desktop
   user shrinking their browser window doesn't suddenly get
   chunky mobile buttons.
============================================================ */
@media (pointer: coarse) {
    /* Small buttons + icon controls bump to the 44px floor without
       changing their visual style otherwise. min- prefix so larger
       contexts (a bigger icon-circle variant) aren't shrunk. */
    .btn-sm,
    .vp-btn-primary.btn-sm,
    .vp-btn-ghost.vp-btn-sm,
    .vp-btn-icon,
    .amboi-tinted-icon--sm,
    .icon-circle.sm,
    .vp-list-avatar.sm {
        min-width: 44px;
        min-height: 44px;
    }

    /* Row-action kebab — same rule covers it via .vp-btn-icon, but
       call out the dropdown-toggle explicitly so the entire tap
       target (button + chevron padding) honours the floor. */
    .vp-row-actions .dropdown-toggle {
        min-width: 44px;
        min-height: 44px;
    }

    /* Form controls — Bootstrap's form-control-sm defaults to ~31px.
       Bump so touch users don't mis-tap an adjacent field. */
    .form-control-sm,
    .form-select-sm {
        min-height: 44px;
    }

    /* Pagination links — Bootstrap pagination links are notoriously
       tiny. WCAG target-size covers them too. */
    .page-link {
        min-width: 44px;
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}

/* ═══════════════════════════════════════════════════════════════════
   Admin dashboard v2 — KPI cards, activity rows, punch-list, system
   health. Used by /admin (resources/views/admin/dashboard.blade.php)
   and the related Blade components (x-admin.kpi-card,
   x-admin.activity-row). Tokens follow the same vp-*/cv-* naming
   discipline as the rest of the design system so theme swaps cascade.
═══════════════════════════════════════════════════════════════════ */

/* KPI card — used by Money/Health/Scale strips on /admin.
   The tone is set via the --kpi-stripe custom property on the wrapper
   (driven by the `tone` prop on the component). Stripe is a 3px
   coloured edge on the left; everything else stays neutral. */
.kpi-card {
    display: flex;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    overflow: hidden;
    transition: transform 0.12s ease, box-shadow 0.12s ease;
    color: inherit;
    text-decoration: none;
}
a.kpi-card:hover {
    transform: translateY(-1px);
    box-shadow: var(--elev-2);
}
.kpi-card-stripe {
    width: 3px;
    background: var(--kpi-stripe, transparent);
    flex-shrink: 0;
}
.kpi-card-body {
    padding: var(--space-4) var(--space-5);
    flex-grow: 1;
    min-width: 0;
}
.kpi-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
}
.kpi-card-label {
    color: var(--text-secondary);
    font-size: var(--fs-small);
    font-weight: 600;
    letter-spacing: 0.01em;
}
.kpi-card-label i { margin-right: 4px; opacity: 0.7; }
.kpi-card-change {
    font-size: 12px;
    font-weight: 700;
    display: inline-flex;
    align-items: center;
    gap: 2px;
}
.kpi-card-value {
    font-size: 24px;
    font-weight: 700;
    color: var(--text-primary);
    line-height: 1.1;
}
.kpi-card-sublabel {
    margin-top: 4px;
    color: var(--text-secondary);
    font-size: 11px;
}
.kpi-card-spark {
    display: block;
    width: 100%;
    height: 24px;
    margin-top: var(--space-3);
}

/* Punch-list — "do this now" banner above the KPI strips. Each item
   is a clickable pill so admin can jump to the queue in one tap. */
.punchlist {
    background: linear-gradient(135deg, rgba(239, 68, 68, 0.06), rgba(245, 158, 11, 0.06));
    border: 1px solid rgba(239, 68, 68, 0.18);
    border-radius: var(--radius-card);
    padding: var(--space-4) var(--space-5);
    margin-bottom: var(--space-4);
}
.punchlist-head {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
    font-weight: 700;
    color: var(--text-primary);
}
.punchlist-head i { color: #EF4444; }
.punchlist-items {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}
.punchlist-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    font-size: var(--fs-small);
    color: var(--text-primary);
    text-decoration: none;
    transition: border-color 0.12s ease, background 0.12s ease;
}
.punchlist-item:hover {
    border-color: var(--text-secondary);
    color: var(--text-primary);
}
.punchlist-item-count {
    display: inline-grid;
    place-items: center;
    min-width: 22px;
    height: 22px;
    padding: 0 6px;
    background: #EF4444;
    color: #fff;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
}
.punchlist-item-count.warning { background: #F59E0B; }
.punchlist-item-empty {
    color: var(--text-secondary);
    font-size: var(--fs-small);
}

/* Activity row — unified subject+meta+status pattern across the
   tabbed activity feed (signups / bookings / disputes / reviews). */
.activity-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border-subtle);
    transition: background 0.1s ease;
}
.activity-row:last-child { border-bottom: 0; }
.activity-row:hover { background: var(--bg-subtle); }
.activity-row-avatar {
    width: 36px; height: 36px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
    background: var(--bg-subtle);
    border: 1px solid var(--border);
}
.activity-row-initials {
    display: inline-grid;
    place-items: center;
    color: var(--text-primary);
    font-weight: 700;
    font-size: var(--fs-small);
}
.activity-row-body {
    flex-grow: 1;
    min-width: 0;
}
.activity-row-title {
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 2px;
}
.activity-row-title a { color: inherit; }
.activity-row-meta {
    font-size: var(--fs-small);
    color: var(--text-secondary);
}

/* System health strip — bottom-of-page status row. Each pill shows a
   coloured dot + label + optional meta. */
.health-strip {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
}
.health-pill {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: var(--fs-small);
    color: var(--text-secondary);
}
.health-pill-dot {
    width: 8px; height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
}
.health-pill-dot.ok { background: #16A34A; box-shadow: 0 0 0 2px rgba(22, 163, 74, 0.15); }
.health-pill-dot.warn { background: #F59E0B; box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.15); }
.health-pill-dot.down { background: #EF4444; box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.15); }
.health-pill-dot.idle { background: #94A3B8; }
.health-pill strong { color: var(--text-primary); }
.health-pill-meta {
    opacity: 0.75;
    margin-left: 2px;
}

/* ═══════════════════════════════════════════════════════════════════
   Footer — dark variant (Rekaizen-style)
   ───────────────────────────────────────────────────────────────────
   Reads as the brand's dark anchor — uses the primary token so any
   palette swap (Rekaizen / Lime Classic / Vibrant 5 / custom) re-tints
   the footer automatically. Bottom-of-page weight grounds the layout
   AND moves brand-trust signals (copyright, legal links, business reg
   number) onto the strongest-read surface on the page. Apply by adding
   `amboi-footer-dark` to the <footer> element.
═══════════════════════════════════════════════════════════════════ */
.amboi-footer-dark {
    /* !important to beat the global `footer { background: ...!important }`
       earlier in this file. Without it, the .mt-5-era light footer rule
       wins by virtue of higher specificity + !important. */
    background: var(--primary-900) !important;
    border-top: 0 !important;
    color: rgba(255, 255, 255, 0.62);
    padding-top: var(--space-12);
    padding-bottom: var(--space-8);
    margin-top: var(--space-16);
}
/* Headings — uppercase, letter-spaced, muted-bright. Matches the
   "SERVICES / STUDIO / CONTACT" rhythm in the Rekaizen footer. */
.amboi-footer-dark h6 {
    color: rgba(255, 255, 255, 0.55);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    margin-bottom: var(--space-3);
}
/* Body text + paragraphs read off the same muted-white. */
.amboi-footer-dark p,
.amboi-footer-dark .text-muted,
.amboi-footer-dark small {
    color: rgba(255, 255, 255, 0.62) !important;
}
/* Links — brighten to near-white. Hover lifts to pure white + a
   subtle teal underline so the brand still peeks through. */
.amboi-footer-dark a {
    color: rgba(255, 255, 255, 0.88);
    text-decoration: none;
    transition: color 0.12s ease, border-color 0.12s ease;
    border-bottom: 1px solid transparent;
}
.amboi-footer-dark a:hover,
.amboi-footer-dark a:focus-visible {
    color: #FFFFFF;
    border-bottom-color: var(--accent-500);
}
/* Override the .text-muted class on links since Bootstrap's specificity
   would otherwise paint them grey. Without !important the markup-level
   .text-muted utility wins over our scoped rule. */
.amboi-footer-dark a.text-muted {
    color: rgba(255, 255, 255, 0.78) !important;
}
.amboi-footer-dark a.text-muted:hover {
    color: #FFFFFF !important;
}
/* Inline icons inside body copy (envelope, chat-dots) — slightly
   accented so they don't disappear into the muted body text. */
.amboi-footer-dark p > .bi,
.amboi-footer-dark .bi {
    color: var(--accent-500);
    opacity: 0.85;
}
/* Lists — pull the gutter in slightly so each column reads tighter. */
.amboi-footer-dark ul.list-unstyled li {
    padding: 4px 0;
}
/* Newsletter input — kept light-on-dark with a subtle border so the
   contrast against the dark footer is clean. Button uses the accent
   automatically. */
.amboi-footer-dark .form-control {
    background: rgba(255, 255, 255, 0.06);
    border-color: rgba(255, 255, 255, 0.18);
    color: #FFFFFF;
}
.amboi-footer-dark .form-control::placeholder {
    color: rgba(255, 255, 255, 0.45);
}
.amboi-footer-dark .form-control:focus {
    background: rgba(255, 255, 255, 0.10);
    border-color: var(--accent-500);
    color: #FFFFFF;
    box-shadow: 0 0 0 3px rgba(var(--accent-rgb, 81, 180, 190), 0.18);
}
/* Divider before the bottom row — subtle white instead of the default
   grey border (which would disappear against the dark bg). */
.amboi-footer-dark .amboi-divider,
.amboi-footer-dark hr {
    border-color: rgba(255, 255, 255, 0.10);
    opacity: 1;
}
/* Brand logo strip — keep the existing logo asset readable. If the
   logo SVG has a dark fill, this is where to invert it; for now we
   just nudge contrast via filter so a single asset works on both
   light and dark footers. The lime-mark logo reads fine on dark
   without this; comment in if you swap to a dark-ink logo. */
/* .amboi-footer-dark .navbar-brand img,
   .amboi-footer-dark .navbar-brand svg { filter: brightness(1.15); } */
