/* ==========================================================================
   Single design-system source of truth for the Blazor Hybrid app — the CSS
   equivalent of the old Resources/Styles/Colors.xaml + Styles.xaml. Every
   .razor page should use these variables/classes, never a hardcoded hex,
   so the whole app still reads as one consistent visual language.
   ========================================================================== */

:root {
    /* Multi-theme system (2026-08-15): requirement: "implement theme so the user can select based
       on their choice" — 4 named palettes, user-selectable in Profile (see ThemeService.cs +
       Profile.razor's Appearance section), applied at runtime by toggling a data-theme attribute
       on <html> (interop.js's appInterop.setTheme). Retires the prior single hardcoded
       "Peacock & Saffron" theme entirely — every page already pulls colors from these variables
       rather than hardcoding hex (see file header), so swapping/adding theme blocks is the whole
       mechanism; no other CSS changes needed.

       This :root block IS the "Modern Neo-Indigo" theme and doubles as the fallback shown before
       ThemeService has applied the user's stored choice (e.g. very first paint). The other three
       themes below override these same variables under :root[data-theme="..."] — higher
       specificity than a bare :root, so they win once interop.js sets the attribute. A
       :root[data-theme="neo-indigo"] block is intentionally NOT duplicated here since it would be
       identical to this default. */

    /* Theme 1/4 — Modern Neo-Indigo (default). Clean, modern, trustworthy; best all-rounder. */
    --color-primary: #4F46E5;
    --color-primary-dark: #4338CA;
    --color-primary-light: #EEF2FF;
    --color-accent: #F97316;
    --color-accent-light: #FFEDD5;
    --color-warning: #F59E0B;
    --color-danger: #DC2626;
    --color-success: #16A34A;

    --color-background: #F8FAFC;
    --color-surface: #FFFFFF;
    --color-border: #E2E8F0;
    --color-text-primary: #0F172A;
    --color-text-secondary: #64748B;
    --color-text-on-primary: #FFFFFF;

    /* Status colors — reused across appointment cards, notification badges, concern/issue status.
       Pending reuses the accent (an item awaiting action); confirmed/cancelled reuse the semantic
       success/danger tokens above; completed uses primary-dark so it reads as "settled" without
       repeating the exact button-primary color used everywhere else on a page. Same mapping is
       reused by all 4 themes below, just pointing at that theme's own primary/accent/success/danger. */
    --color-status-pending: var(--color-accent);
    --color-status-confirmed: var(--color-success);
    --color-status-cancelled: var(--color-danger);
    --color-status-completed: var(--color-primary-dark);

    --radius-card: 16px;
    --radius-control: 12px;

    /* Requirement (2026-08-15): "for Indian people prospective ... change the page background
       theme based on graphical image that show some concept", then "something modern" — soft
       gradient "aurora" blobs (Stripe/Linear/PhonePe/CRED-style ambient glow, not a literal
       pattern) behind Landing/Login/Signup/BusinessLogin (.pattern-bg below), tinted to whichever
       theme is active. Two tint variables (primary-hued + accent-hued) so the blobs read as a
       gentle two-tone glow rather than one flat wash. Kept as their own low-alpha variables per
       theme (rather than deriving from --color-primary/--color-accent at render time) since this
       codebase's CSS custom properties don't carry alpha separately, and color-mix() isn't
       reliably supported on every Android WebView version this app targets. */
    --color-pattern: rgba(79, 70, 229, 0.14);
    --color-pattern-2: rgba(249, 115, 22, 0.12);
}

/* Theme 2/4 — Velvet Noir / Luxury Dark Glow. Exclusive, sleek, high-end; premium grooming/spas. */
:root[data-theme="velvet-noir"] {
    --color-primary: #D4AF37;
    --color-primary-dark: #B8952E;
    --color-primary-light: #332B14;
    --color-accent: #10B981;
    --color-accent-light: #0F2D22;
    --color-warning: #F59E0B;
    --color-danger: #EF4444;
    --color-success: #10B981;

    --color-background: #0D1117;
    --color-surface: #1E222B;
    --color-border: #2D333B;
    --color-text-primary: #F8FAFC;
    --color-text-secondary: #94A3B8;
    /* Gold is light/mid-tone — dark text reads correctly on primary-colored buttons/badges, unlike
       the other 3 themes where primary is dark/saturated enough for white text. */
    --color-text-on-primary: #0D1117;
    /* Brighter alpha than the other 3 themes — a faint gold tint barely registers against the
       near-black background, so the glow needs more contrast here to actually read. Emerald as
       the second blob tone (this theme's accent), for a gold + emerald aurora pairing. */
    --color-pattern: rgba(212, 175, 55, 0.18);
    --color-pattern-2: rgba(16, 185, 129, 0.14);
}

/* Theme 3/4 — Clean Sage & Botanical Warmth. Calming, organic; wellness/skin/spa. */
:root[data-theme="sage-botanical"] {
    --color-primary: #15803D;
    --color-primary-dark: #166534;
    --color-primary-light: #DCFCE7;
    --color-accent: #CA8A04;
    --color-accent-light: #FEF3C7;
    --color-warning: #D97706;
    --color-danger: #DC2626;
    --color-success: #16A34A;

    --color-background: #FAFAF9;
    --color-surface: #FFFFFF;
    --color-border: #E7E5E4;
    --color-text-primary: #1C1917;
    --color-text-secondary: #78716C;
    --color-text-on-primary: #FFFFFF;
    --color-pattern: rgba(21, 128, 61, 0.14);
    --color-pattern-2: rgba(202, 138, 4, 0.12);
}

/* Theme 4/4 — Vibrant Sunset Peach & Plum. Energetic, chic; youthful/trendy salons, nail spas. */
:root[data-theme="sunset-plum"] {
    --color-primary: #BE185D;
    --color-primary-dark: #9D174D;
    --color-primary-light: #FFE4E6;
    --color-accent: #FB7185;
    --color-accent-light: #EDE9FE;
    --color-warning: #F59E0B;
    --color-danger: #E11D48;
    --color-success: #16A34A;

    --color-background: #FFF1F2;
    --color-surface: #FFFFFF;
    --color-border: #F5D0D7;
    --color-text-primary: #1F1123;
    --color-text-secondary: #705E6B;
    --color-text-on-primary: #FFFFFF;
    --color-pattern: rgba(190, 24, 93, 0.14);
    --color-pattern-2: rgba(251, 113, 133, 0.14);
}

* { box-sizing: border-box; }

html, body {
    height: 100%;
    margin: 0;
    background: var(--color-background);
    color: var(--color-text-primary);
    font-family: 'OpenSansRegular', -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
    font-size: 14px;
}

#app { height: 100%; }

a { color: var(--color-primary); text-decoration: none; }

/* ----- Notch / status bar / gesture bar safe areas -----
   Requirement: fix content and the menu rendering under the mobile notch/status bar. Both Android
   15+ (API 35+, edge-to-edge is mandatory) and iOS draw the WebView behind the status bar/notch by
   default, so anything pinned to the very top of the screen needs real reserved space — plain
   padding can't do this since the inset size varies by device, only env(safe-area-inset-*) knows
   it (and only reports a real value because index.html's viewport meta has viewport-fit=cover).

   Applied two ways:
   1. #status-bar-safe-area (wwwroot/index.html) sits outside #app, so it reserves top space for
      BOTH the pre-Blazor "Loading HzVela…" spinner and, once mounted, the in-flow .app-topbar
      (mobile header) that follows right after it in the document.
   2. .app-sidebar/.app-sidebar-header get their own padding directly below, because on mobile the
      sidebar is `position: fixed` (see the @media block further down) — fixed elements escape
      normal document flow, so they render at the true top of the screen regardless of any
      sibling's height and need the inset applied to their own padding instead. */
.status-bar-safe-area {
    display: block;
    position: sticky;
    top: 0;
    height: env(safe-area-inset-top);
    background: var(--color-surface);
    width: 100%;
    z-index: 2500;
}

/* ----- App shell layout (replaces AppShell's flyout) ----- */

.app-shell { display: flex; height: 100%; min-height: 100vh; }

.app-sidebar {
    width: 260px;
    flex-shrink: 0;
    background: var(--color-surface);
    border-right: 1px solid var(--color-border);
    display: flex;
    flex-direction: column;
    transition: transform 0.2s ease;
}

.app-sidebar-header {
    background: var(--color-primary);
    color: var(--color-text-on-primary);
    padding: 20px;
    min-height: 100px;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    gap: 2px;
}

.app-sidebar-header-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.app-sidebar-header .app-subtitle { font-size: 13px; color: rgba(255, 255, 255, 0.85); }

/* Requirement (2026-08-16): "change the name of Samayo in better font or svg image on top of
   each" — BrandLogo.razor's icon-mark + wordmark lockup, used in place of the old plain
   <span class="app-title">HzVela</span>/<h1 class="screen-title">HzVela</h1>. No color is set
   here on purpose: the sidebar header already sets color: var(--color-text-on-primary) on itself
   (see .app-sidebar-header above), so the lockup's `currentColor` SVG strokes and inherited text
   color both resolve to white there automatically, with zero extra rules needed. The topbar and
   Landing hero contexts below explicitly override to the brand primary color instead, since
   neither of those has a colored background to inherit white from. */
.brand-lockup { display: inline-flex; align-items: center; gap: 8px; }
.brand-lockup-icon { flex-shrink: 0; }
.brand-wordmark {
    font-family: 'Poppins', 'OpenSansRegular', -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
    font-weight: 800;
    letter-spacing: 0.4px;
    line-height: 1;
}
.brand-wordmark-sm { font-size: 19px; }
.brand-wordmark-lg { font-size: 34px; }

.app-topbar .brand-lockup-icon, .app-topbar .brand-wordmark { color: var(--color-primary); }
.landing-hero-brand .brand-lockup-icon, .landing-hero-brand .brand-wordmark { color: var(--color-primary); }
.landing-hero-brand { justify-content: center; }

/* Requirement: "one bell icon on top and show no of unread notification" — rendered once in the
   mobile topbar and once in the desktop sidebar header (see MainLayout.razor); colors adapt to
   whichever background it's sitting on (the primary-colored sidebar header vs. the plain topbar). */
.notification-bell {
    position: relative;
    background: none;
    border: none;
    cursor: pointer;
    font-size: 20px;
    color: inherit;
    padding: 4px;
    line-height: 1;
}
.app-topbar .notification-bell { margin-left: auto; color: var(--color-text-primary); }
.notification-badge {
    position: absolute;
    top: -4px;
    right: -6px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: 8px;
    background: var(--color-danger);
    color: #fff;
    font-size: 10px;
    font-weight: bold;
    line-height: 16px;
    text-align: center;
}

.app-nav { flex: 1; overflow-y: auto; padding: 8px 0; }
.app-nav-section-label {
    padding: 14px 20px 6px;
    font-size: 12px;
    font-weight: bold;
    color: var(--color-text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* Requirement: "add some modern logo for customer and business on menu bar instead of writing
   business and customer" — replaces the plain uppercase text divider (still used above for "App")
   with a small round icon badge next to a lighter label, for the two role sections a person can
   actually be signed into (Customer/Business/Host Admin). The label stays (a person can be signed
   into more than one role at once on this app — see NavMenu.razor's own header comment — so a bare
   icon alone could be ambiguous about which section is starting), but the icon badge is now the
   primary visual instead of the word. */
/* Requirement (2026-09-14): "business logo can be visible left side with menu for professional
   look" — sits just above the Business section badge, business-portal only (see NavMenu.razor). */
.app-nav-business-logo { display: flex; justify-content: flex-start; margin: 14px 20px 0; }
.app-nav-section-badge { display: flex; align-items: center; gap: 8px; margin: 14px 20px 6px; }
.app-nav-section-badge .badge-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--color-primary-light);
    color: var(--color-primary);
    font-size: 14px;
    flex-shrink: 0;
}
.app-nav-section-badge .badge-label {
    font-size: 12px;
    font-weight: bold;
    color: var(--color-text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.app-nav a {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 20px;
    color: var(--color-text-primary);
    font-size: 14px;
}
.app-nav a:hover { background: var(--color-primary-light); }
.app-nav a.active { background: var(--color-primary-light); color: var(--color-primary); font-weight: bold; border-right: 3px solid var(--color-primary); }
.app-nav a i.bi { font-size: 16px; width: 18px; text-align: center; flex-shrink: 0; color: var(--color-text-secondary); }
.app-nav a.active i.bi, .app-nav a:hover i.bi { color: var(--color-primary); }

.app-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.app-topbar {
    display: none;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    /* max() keeps the normal 16px gutter on devices with no notch, and grows only when the
       landscape-orientation side inset actually needs more room (a side notch, not the top one —
       env(safe-area-inset-top) is handled separately via .status-bar-safe-area above). */
    padding-left: max(16px, env(safe-area-inset-left));
    padding-right: max(16px, env(safe-area-inset-right));
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
}
.app-topbar .hamburger { font-size: 22px; background: none; border: none; cursor: pointer; color: var(--color-text-primary); }

.app-content {
    flex: 1;
    padding: 20px;
    max-width: 900px;
    width: 100%;
    margin: 0 auto;
    /* Requirement: bottom controls/buttons (e.g. BusinessWorkingHours.razor's "Save hours") were
       getting hidden behind the Android gesture nav bar / iOS home indicator — same edge-to-edge
       issue as the top notch, just at the bottom. calc() keeps the normal 20px gutter on devices
       with no bottom inset, and only adds extra room where a gesture bar actually needs it. Set
       after the padding shorthand above so it isn't clobbered by it. */
    padding-bottom: calc(20px + env(safe-area-inset-bottom));
}

@media (max-width: 860px) {
    .app-topbar { display: flex; }
    .app-sidebar {
        position: fixed; inset: 0 auto 0 0; z-index: 40;
        transform: translateX(-100%);
    }
    .app-sidebar.open { transform: translateX(0); box-shadow: 0 0 24px rgba(0,0,0,0.25); }
    .app-sidebar-backdrop {
        display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.35); z-index: 30;
    }
    .app-sidebar-backdrop.open { display: block; }

    /* The sidebar becomes `position: fixed` right above, which pins it to the true top of the
       screen regardless of the .status-bar-safe-area div's height (fixed elements escape normal
       document flow) — so its header needs the notch/status-bar inset added to its own padding
       directly, or the "HzVela" title + bell icon render underneath the status bar when the menu
       is opened. */
    .app-sidebar-header { padding-top: calc(20px + env(safe-area-inset-top)); }
}

/* ----- Typography ----- */

.screen-title { font-size: 24px; font-weight: bold; color: var(--color-text-primary); margin: 0 0 4px; }
.section-label { font-size: 14px; font-weight: bold; color: var(--color-text-secondary); margin: 0 0 8px; }
.body-text { font-size: 14px; color: var(--color-text-primary); }
.caption-text { font-size: 12px; color: var(--color-text-secondary); }
.error-text { color: var(--color-danger); font-size: 13px; margin: 4px 0; }

/* ----- Card ----- */

.card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-card);
    padding: 16px;
    margin-bottom: 12px;
}

/* ----- Buttons ----- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 50px;
    padding: 0 20px;
    border-radius: var(--radius-control);
    font-weight: bold;
    font-size: 15px;
    border: none;
    cursor: pointer;
    width: 100%;
    margin-bottom: 10px;
}
.btn:disabled { opacity: 0.6; cursor: not-allowed; }
.btn-primary { background: var(--color-primary); color: var(--color-text-on-primary); }
.btn-primary:hover:not(:disabled) { background: var(--color-primary-dark); }
.btn-secondary { background: var(--color-primary-light); color: var(--color-primary); }
.btn-sm { height: 36px; font-size: 13px; padding: 0 14px; width: auto; margin-bottom: 0; }
.btn-danger { background: var(--color-danger); color: white; }
/* Requirement: "UI basically build for mobile app so check some page design seems not fit for
   mobile so optimize that as well" — flex-wrap on by default so any .btn-row with 3+ buttons
   (several pages had this only via an ad-hoc inline style="flex-wrap:wrap" override, some had none
   at all) never clips/overflows horizontally on a narrow phone screen; a 2-button row still lays
   out side by side exactly as before since wrapping only kicks in once it actually needs to. */
.btn-row { display: flex; gap: 8px; flex-wrap: wrap; }
.btn-row .btn { margin-bottom: 0; width: auto; flex: 1; }

/* Icons inside buttons (Bootstrap Icons, <i class="bi bi-xxx">) — a bit of breathing room before
   the label, and a hair smaller than body text so it doesn't dominate a .btn-sm's compact label. */
.btn i.bi { margin-right: 8px; font-size: 1.05em; line-height: 1; }
.btn-sm i.bi { margin-right: 5px; font-size: 1em; }

/* ----- Forms ----- */

.field { margin-bottom: 14px; }
.field label { display: block; font-size: 13px; color: var(--color-text-secondary); margin-bottom: 6px; }
.input-field, select.input-field, textarea.input-field {
    width: 100%;
    height: 48px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-control);
    background: var(--color-surface);
    color: var(--color-text-primary);
    padding: 0 14px;
    font-size: 14px;
    font-family: inherit;
}
textarea.input-field { height: auto; min-height: 96px; padding: 10px 14px; resize: vertical; }
.input-field:focus { outline: 2px solid var(--color-primary); outline-offset: 1px; }

/* ----- Status badges ----- */

.badge { display: inline-block; padding: 3px 10px; border-radius: 999px; font-size: 12px; font-weight: bold; color: white; }
.badge-pending { background: var(--color-status-pending); }
.badge-confirmed { background: var(--color-status-confirmed); }
.badge-cancelled { background: var(--color-status-cancelled); }
.badge-completed { background: var(--color-status-completed); }
.badge-noshow { background: var(--color-text-secondary); }

/* Requirement (2026-08-16): "remove the search option and category [dropdown]. just add small
   icon to related service to click and filter the relevant service" — Home.razor's category
   filter, a horizontally-scrollable strip of round icon badges with a tiny label underneath (the
   familiar "discovery app" category-strip look), replacing the old search box + <select> dropdown.
   Scrollbar hidden (still fully scrollable via touch/trackpad) to keep the compact, icon-first look
   the requirement asked for rather than a visible scrollbar rail undercutting it. */
.category-chip-row {
    display: flex;
    gap: 14px;
    overflow-x: auto;
    padding: 4px 2px 10px;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}
.category-chip-row::-webkit-scrollbar { display: none; }
.category-chip {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    width: 74px;
    background: none;
    border: none;
    cursor: pointer;
    padding: 0;
}
.category-chip i.bi {
    width: 52px;
    height: 52px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--color-primary-light);
    color: var(--color-primary);
    font-size: 21px;
    transition: background 0.15s ease, color 0.15s ease;
}
/* Requirement (2026-08-16): "training icon is not fit on screen" — the old fixed-width label
   could overflow/clip on longer category names (e.g. "Consultant/Legal"). overflow-wrap lets the
   label wrap at whatever point actually fits (including at a "/" with no surrounding spaces,
   which plain word-wrapping wouldn't break on) instead of overflowing the chip. */
.category-chip span {
    font-size: 10.5px;
    font-weight: 600;
    color: var(--color-text-secondary);
    text-align: center;
    line-height: 1.2;
    overflow-wrap: anywhere;
}
.category-chip.active i.bi { background: var(--color-primary); color: var(--color-text-on-primary); }
.category-chip.active span { color: var(--color-primary); }

/* ----- Misc ----- */

.spinner {
    width: 28px; height: 28px;
    border: 3px solid var(--color-primary-light);
    border-top-color: var(--color-primary);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
    margin: 12px auto;
}
@keyframes spin { to { transform: rotate(360deg); } }

.app-loading { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; color: var(--color-text-secondary); }

.empty-text { color: var(--color-text-secondary); font-size: 13px; padding: 8px 0; }

/* Bug fix (2026-08-14): "when map open and click on menu, menu showing under the map" — Leaflet's
   own CSS gives its internal controls (.leaflet-top/.leaflet-bottom, attribution, zoom buttons)
   z-index values up to 1000. Leaflet's container div has `position: relative` but no z-index of
   its own, which per the CSS stacking spec does NOT create a new stacking context — so those
   z-index:1000 children escape upward and stack against the whole PAGE's root context instead of
   staying contained inside .map-container's box, floating above the sidebar (z-index: 40) and its
   backdrop (z-index: 30) whenever the menu is opened over a map screen (Map.razor,
   BusinessWorkingHours.razor's location picker, BusinessDetail.razor's read-only pin). Giving
   .map-container its own `position` + low `z-index` creates a local stacking context that
   contains every Leaflet z-index inside it, so the whole map (controls included) now stacks below
   the sidebar/backdrop instead of above them. */
.map-container { width: 100%; height: 70vh; border-radius: var(--radius-card); overflow: hidden; position: relative; z-index: 0; }

.chart-container { position: relative; height: 220px; margin-bottom: 16px; }

/* Requirement: "total booking, completed, favorites showing in row create it like a small icon
   size and set to 2 column or 3 column" — replaces the old big full-width stat cards (which is why
   a narrow phone used to collapse this to a single column, one giant card per row — see the removed
   @media override below) with small icon+value+label tiles that comfortably fit 2 across even on a
   narrow phone, so the grid never needs to collapse to 1 column at all. */
.stat-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin-bottom: 16px; }
.stat-grid .card { margin-bottom: 0; }
.stat-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 12px 8px;
    gap: 2px;
}
.stat-card .caption-text { margin: 2px 0 0; }
.stat-icon { font-size: 20px; color: var(--color-primary); margin-bottom: 2px; }
.stat-value { font-size: 20px; font-weight: bold; }
.stat-value.accent { color: var(--color-accent); }
.stat-value.primary { color: var(--color-primary); }

/* A wider screen (tablet/desktop-in-browser) has room to go to 3 columns, which reads better for
   the 6-tile admin overview grid in particular. */
@media (min-width: 700px) {
    .stat-grid { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 420px) {
    .app-content { padding: 14px; }
    /* The shorthand above resets all four sides including bottom — redeclare it after so the
       gesture-bar safe-area room from the base .app-content rule isn't lost on narrow phones,
       which is exactly the device class this was reported on. */
    .app-content { padding-bottom: calc(14px + env(safe-area-inset-bottom)); }
}

/* Requirement: same as above — a QuickGrid (e.g. AdminBusinessReports.razor's 7-column business
   table) has no built-in mobile treatment: it renders a plain <table> that will overflow/clip with
   no way to see the cut-off columns. Wrapping the grid in <div class="table-scroll"> lets it
   scroll horizontally within its own box instead of blowing out the page layout. */
.table-scroll { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; margin-bottom: 12px; }
.table-scroll table { min-width: 640px; }

/* ----- Offline mini-game overlay -----
   Requirement: "If there is no internet its opens some engaging activity to play for customer" —
   see Components/Layout/NoInternetOverlay.razor + wwwroot/js/offlineGame.js. */
.offline-overlay {
    position: fixed;
    inset: 0;
    z-index: 2000;
    background: rgba(17, 24, 39, 0.92);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
}
.offline-overlay.hidden { display: none; }
.offline-panel {
    background: var(--color-surface);
    border-radius: var(--radius-card);
    padding: 20px;
    max-width: 360px;
    width: 100%;
    text-align: center;
}
.offline-panel canvas {
    display: block;
    width: 100%;
    max-width: 320px;
    height: 160px;
    background: var(--color-primary-light);
    border-radius: var(--radius-control);
    margin: 10px auto;
    touch-action: manipulation;
}

/* ----- Crop-before-upload modal -----
   Requirement: "there should be option to crop the uploaded image" — see
   Components/Layout/ImageCropModal.razor + wwwroot/js/interop.js's Cropper.js interop. Higher
   z-index than .offline-overlay so a crop in progress still wins if connectivity drops mid-crop
   (unlikely, but keeps the two overlays from fighting for the top slot). */
.crop-overlay {
    position: fixed;
    inset: 0;
    z-index: 2100;
    background: rgba(23, 33, 36, 0.92);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
}
.crop-panel {
    background: var(--color-surface);
    border-radius: var(--radius-card);
    padding: 16px;
    max-width: 420px;
    width: 100%;
}
.crop-image-wrap {
    height: 320px;
    background: #000;
    border-radius: var(--radius-control);
    overflow: hidden;
}
.crop-image-wrap img { display: block; max-width: 100%; }

/* Requirement: "Add one more feature on business and service image if user click on it preview it
   some larger for better visibility" — full-screen click-to-enlarge overlay (ImagePreviewService /
   ImagePreviewModal.razor). Higher z-index than .crop-overlay (2100) so it always wins if somehow
   both are ever open, though that shouldn't normally happen. */
.preview-overlay {
    position: fixed;
    inset: 0;
    z-index: 2200;
    background: rgba(0, 0, 0, 0.92);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    cursor: zoom-out;
}
.preview-image {
    max-width: 100%;
    max-height: 100%;
    border-radius: var(--radius-card);
    object-fit: contain;
    cursor: default;
}
.preview-close-btn {
    position: absolute;
    top: 16px;
    right: 16px;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: none;
    background: rgba(255, 255, 255, 0.15);
    color: #FFFFFF;
    font-size: 18px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}
.preview-close-btn:hover { background: rgba(255, 255, 255, 0.28); }

/* Thumbnails that opt into click-to-preview (BusinessDetail's logo/gallery/service photos,
   BusinessServices'/AdminServices' service-list thumbnails) get this so it's obvious they're
   clickable, same affordance convention as other clickable cards in this app. */
.preview-thumb { cursor: zoom-in; }

/* Requirement: "Implement business to user one 2 one in app chat with respect to appointment" —
   standard messaging-app bubble layout: my own messages right-aligned/primary color, the other
   party's left-aligned/surface color. */
.chat-thread {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 4px 2px 90px;
    min-height: 200px;
}
.chat-bubble-row { display: flex; }
.chat-bubble-row.mine { justify-content: flex-end; }
.chat-bubble-row.theirs { justify-content: flex-start; }
.chat-bubble {
    max-width: 78%;
    padding: 9px 13px;
    border-radius: 14px;
    word-break: break-word;
    white-space: pre-wrap;
}
.chat-bubble-row.mine .chat-bubble {
    background: var(--color-primary);
    color: var(--color-text-on-primary);
    border-bottom-right-radius: 4px;
}
.chat-bubble-row.theirs .chat-bubble {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    color: var(--color-text-primary);
    border-bottom-left-radius: 4px;
}
.chat-bubble-time {
    display: block;
    font-size: 0.7rem;
    margin-top: 4px;
    opacity: 0.75;
}
.chat-composer {
    position: sticky;
    bottom: 0;
    display: flex;
    gap: 8px;
    align-items: flex-end;
    padding: 10px 0 calc(10px + env(safe-area-inset-bottom));
    background: var(--color-background);
}
.chat-composer textarea {
    flex: 1;
    resize: none;
    max-height: 96px;
}

#blazor-error-ui {
    background: var(--color-accent-light);
    color: var(--color-text-primary);
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0,0,0,0.2);
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}
#blazor-error-ui .dismiss { cursor: pointer; position: absolute; right: 0.75rem; top: 0.5rem; }

/* Requirement (2026-08-15): animated splash intro (Components/Pages/SplashIntro.razor) — the "/"
   route shown once per cold start, right after the native MauiSplashScreen. Deliberately hardcoded
   (not var(--color-*)) regardless of the user's selected theme — see that file's header comment for
   why. Timings: logo scale/fade 600ms, wordmark follows at 350ms, tagline at 800ms (600ms logo +
   200ms delay, per the design brief), loader at 1100ms, whole page navigates on to Landing at
   1900ms (SplashIntro.razor's TotalDisplayMs).
   Requirement (2026-09-16): rebrand SAMAYO -> HzVela, "based on" the uploaded HzMinds logo —
   background is now near-black (was Neo-Indigo #4F46E5) to match that logo's icon. */
.splash-intro {
    position: fixed;
    inset: 0;
    z-index: 5000;
    background: #15120E;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    overflow: hidden;
}
.splash-intro-glow {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 340px;
    height: 340px;
    transform: translate(-50%, -60%);
    border-radius: 50%;
    background: radial-gradient(circle, rgba(255,255,255,0.22) 0%, rgba(255,255,255,0.06) 60%, rgba(255,255,255,0) 100%);
    pointer-events: none;
}
.splash-intro-mark {
    opacity: 0;
    transform: scale(0.7);
    animation: splashMarkIn 600ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
    margin-bottom: 8px;
}
.splash-intro-wordmark {
    opacity: 0;
    transform: translateY(10px);
    animation: splashFadeUp 500ms ease-out 350ms forwards;
    color: #F3ECDD;
    /* Requirement (2026-08-16): matches the Poppins wordmark font now used everywhere else
       (.brand-wordmark) so the animated intro and the in-app header/hero read as the same
       logotype instead of two different fonts. */
    font-family: 'Poppins', 'OpenSansRegular', -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
    font-size: 34px;
    font-weight: 800;
    letter-spacing: 3px;
    margin: 0;
}
.splash-intro-tagline {
    opacity: 0;
    animation: splashTaglineIn 400ms ease-out 800ms forwards;
    color: #F3ECDD;
    font-size: 14px;
    letter-spacing: 0.5px;
    margin: 2px 0 0;
}
.splash-intro-loader {
    opacity: 0;
    animation: splashFadeUp 300ms ease-out 1100ms forwards;
    display: flex;
    gap: 6px;
    margin-top: 28px;
}
.splash-intro-loader span {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #E4572E;
    animation: splashDotPulse 1.1s ease-in-out infinite;
}
.splash-intro-loader span:nth-child(2) { animation-delay: 0.15s; }
.splash-intro-loader span:nth-child(3) { animation-delay: 0.3s; }

@keyframes splashMarkIn {
    from { opacity: 0; transform: scale(0.7); }
    to { opacity: 1; transform: scale(1); }
}
@keyframes splashFadeUp {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: translateY(0); }
}
@keyframes splashTaglineIn {
    from { opacity: 0; }
    to { opacity: 0.8; }
}
@keyframes splashDotPulse {
    0%, 80%, 100% { transform: scale(0.6); opacity: 0.4; }
    40% { transform: scale(1); opacity: 1; }
}

/* Requirement: "implement theme so the user can select based on their choice" — Profile.razor's
   Appearance section. Cards themselves use the theme-neutral var(--color-*) tokens (so the picker
   itself always matches whichever theme is currently active); only the small preview dots inside
   each card are hardcoded per-theme (see Profile.razor's inline styles) since they need to show
   what each NON-active theme actually looks like. */
.theme-picker {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.theme-swatch {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 10px 12px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-control);
    cursor: pointer;
    text-align: left;
    font: inherit;
    color: inherit;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.theme-swatch.selected {
    border-color: var(--color-primary);
    box-shadow: 0 0 0 1px var(--color-primary);
}
.theme-swatch-dots {
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    border-radius: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    border: 1px solid rgba(0, 0, 0, 0.08);
}
.theme-swatch-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    border: 1px solid rgba(0, 0, 0, 0.1);
}
.theme-swatch-text {
    display: flex;
    flex-direction: column;
    gap: 1px;
    flex: 1;
    min-width: 0;
}
.theme-swatch-name {
    font-weight: 600;
    font-size: 14px;
    color: var(--color-text-primary);
}
.theme-swatch-check {
    flex-shrink: 0;
    color: var(--color-primary);
    font-size: 18px;
}

/* Requirement (2026-08-15): "for Indian people prospective ... change the page background theme
   based on graphical image that show some concept", then "something modern" — soft blurred
   gradient "aurora" blobs (Stripe/Linear/PhonePe/CRED-style ambient glow, no literal pattern)
   behind the first-impression screens (Landing, Login, Signup, BusinessLogin). Originally a
   jali-lattice (Indian geometric latticework) pattern; replaced with this per user feedback.
   Built as layered CSS radial-gradients rather than an image asset — scales perfectly at any
   resolution/DPI, adds no download weight, and automatically re-tints per theme via
   --color-pattern / --color-pattern-2 (set per theme block above) instead of needing 4 separate
   exported images. Four large soft-edged blobs positioned at different corners, each fading to
   transparent well before it reaches the opposite side of the screen, so they read as ambient
   glow rather than a hard shape.

   Applied via negative margins that exactly cancel .app-content's own padding (20px desktop /
   14px mobile, matching the breakpoint below), so the glow bleeds edge-to-edge instead of
   leaving a plain-background gap around it — deliberately not position:fixed (would need z-index
   layering to sit behind content, more fragile) since a plain in-flow block with cancelled margins
   achieves the same full-bleed look with no stacking-context risk. */
.pattern-bg {
    margin: -20px -20px 0;
    padding: 20px;
    background-color: var(--color-background);
    background-image:
        radial-gradient(circle at 12% 8%, var(--color-pattern) 0%, transparent 45%),
        radial-gradient(circle at 88% 18%, var(--color-pattern-2) 0%, transparent 42%),
        radial-gradient(circle at 15% 78%, var(--color-pattern-2) 0%, transparent 40%),
        radial-gradient(circle at 92% 88%, var(--color-pattern) 0%, transparent 45%);
    background-repeat: no-repeat;
}
@media (max-width: 420px) {
    .pattern-bg { margin: -14px -14px 0; padding: 14px; }
}
