/* FigBloom brand override for the Star Admin base theme.
   Loaded AFTER assets/css/style.css so it wins normal cascade ties — but
   style.css bundles several internally-conflicting hardcoded skin blocks
   for the same classes (e.g. .btn-primary is defined once at ~L11440 and
   again at ~L23764 — the later one wins regardless of specificity), and
   the "Sophie" chat widget injects its own <style> tag at runtime, later
   in the DOM than this file. Every rule here uses !important so it wins
   regardless of which block a future style.css regeneration leaves "on
   top," or when a runtime-injected <style> lands after this <link>.
   Do not hand-edit style.css itself — it's a generated vendor file. */

:root {
  /* #1B3611 is the brand's updated default green (2026-10-16 swatch,
     superseding the figbloom-logo.svg .st1 value #2C5322 this block used
     before — the logo file hasn't been reconciled with the new swatch
     yet). #FF9400 (.st0, unchanged) is still the sparing-accent orange.
     The derived shades below (text-emphasis/hover, bg/border-subtle,
     sidebar-active) are the same blend ratios the old #2C5322 palette
     used, just re-applied to this color: darken-to-~88% for hover/
     emphasis, blend-toward-white at ~8%/~20% for the two subtle tints,
     and scale-by-~1.8x for the on-dark-sidebar active tint. */
  --bs-primary: #1B3611;
  --bs-primary-rgb: 27, 54, 17;
  --bs-primary-text-emphasis: #18300F;
  --bs-primary-bg-subtle: #EDEFEC;
  --bs-primary-border-subtle: #D2D7D0;

  /* Theme Customization (Settings > Appearance) — set at runtime by
     assets/js/theme.js's applyColors(), synchronously before first paint,
     from the signed-in user's theme_preset/sidebar_color/accent_color/
     highlight_color/page_color_light/page_color_dark. These are the ONLY
     brand-color concepts a preset or the "Your own colours" pickers can
     change — defaults below match the FigBloom preset exactly. Never add
     !important to a definition here: a plain inline style="--fb-accent:#…"
     JS sets on <html> must keep outranking both this block and the
     [data-theme="dark"] block below regardless of specificity, which is
     the entire mechanism that makes a runtime color change repaint the
     app. Only the rules that *consume* these (below) carry !important. */
  /* FigBloom's sidebar is the logo's own dark green (#2C5322) — the accent
     (nav active-state/buttons) can't reuse that same value on top of it
     (invisible), so it's a brighter tint of the same green instead, the
     same light-accent-on-dark-sidebar shape as the Forest/Ocean/Midnight/
     Aubergine presets. Orange (--fb-highlight) is still the logo's exact
     second color, used only for small details per the "sparing accent"
     brand rule — it was never promoted to the main interactive color. */
  --fb-sidebar-bg: #1B3611;
  --fb-sidebar-text: #F3F2EF;
  /* Deep, saturated green — not a lightened tint. It reads as rich on
     white (even higher contrast than the #2C5322 it replaces, since it's
     darker) everywhere this is actually used: buttons, links, badges, form
     controls. A lighter tint (previously used here) traded that vividness
     away for no benefit, since nothing on a *light* background needed it. */
  --fb-accent: #1B3611;
  --fb-accent-rgb: 27, 54, 17;
  --fb-accent-safe: #1B3611;
  --fb-accent-hover: #18300F;
  /* The one spot that genuinely needs a brighter color: the sidebar's own
     active/hover nav-item text, sitting ON TOP of that same deep green
     background, where --fb-accent itself would be invisible. Computed by
     assets/js/theme.js's sidebarActiveColor() for every theme (preset or
     custom), not just FigBloom's — this default is what FigBloom's own
     computation resolves to. */
  --fb-sidebar-active: #30611F;
  --fb-highlight: #FF9400;
  --fb-page-bg: #FAF9F6;
  --fb-page-text: #1F2933;
  --fb-card-bg: #ffffff;
  /* FIG-867: Settings > Appearance font picker. style.css hardcodes
     font-family: "Plus Jakarta Sans", sans-serif on dozens of individual selectors
     (headings, buttons, form controls, ...) rather than one global rule,
     so swapping fonts needs a broad !important override (below) rather
     than redefining a single Bootstrap variable the way the colors above
     do. Default matches style.css's own hardcoded value exactly. */
  --fb-font-family: "Plus Jakarta Sans", sans-serif;
}

/* Dropdown-item active/selected state (e.g. the navbar's workspace
   switcher — layout.js/layout-tenant.js's `.dropdown-item.active` for
   the currently-active workspace) — style.css hardcodes stock Bootstrap
   blue (#0d6efd) as its own --bs-dropdown-link-active-bg var, separate
   from --bs-primary above, so overriding --bs-primary alone doesn't
   reach it. */
.dropdown-menu {
  --bs-dropdown-link-active-bg: var(--bs-primary-bg-subtle) !important;
  --bs-dropdown-link-active-color: var(--bs-primary-text-emphasis) !important;
}

/* FIG-867: one broad override beats style.css's dozens of individual
   `font-family: "Plus Jakarta Sans", sans-serif` selectors regardless of their own
   specificity, same !important rationale as the rest of this file. */
body, button, input, select, textarea, .btn,
h1, h2, h3, h4, h5, h6 {
  font-family: var(--fb-font-family) !important;
}

/* Buttons (Invite Tenant, Send Invite; modal Cancel is .btn-light/unaffected) */
.btn-primary { background: var(--fb-accent-safe) !important; border-color: var(--fb-accent-safe) !important; color: #ffffff !important; }
.btn-primary:hover, .btn-primary:focus { background: var(--fb-accent-hover) !important; border-color: var(--fb-accent-hover) !important; color: #ffffff !important; }
.btn-outline-primary { border-color: var(--fb-accent) !important; color: var(--fb-accent) !important; }
.btn-outline-primary:hover { background: var(--fb-accent-safe) !important; border-color: var(--fb-accent-safe) !important; color: #ffffff !important; }

/* Bootstrap utility classes style.css re-hardcodes later in its own cascade */
.bg-primary { background-color: var(--fb-accent-safe) !important; }
.text-primary { color: var(--fb-accent) !important; }
.border-primary { border-color: var(--fb-accent) !important; }
.badge-primary { background: var(--fb-accent-safe) !important; border-color: var(--fb-accent-safe) !important; }
/* .badge-secondary (e.g. the workspace switcher's role pill — layout.js/
   layout-tenant.js's ROLE_LABEL badges) — style.css hardcodes this to a
   cyan/blue (#05C3FB), unrelated to --bs-primary above. */
.badge-secondary,
.badge-secondary:not(.badge-primary) {
  background: var(--bs-primary-bg-subtle) !important;
  border-color: var(--bs-primary-border-subtle) !important;
  color: var(--bs-primary-text-emphasis) !important;
}

/* Sidebar + active/hover nav item (theming layout.js's injected markup —
   no changes needed in layout.js itself; CSS matches the live DOM
   regardless of when document.write() inserted these elements) */
.sidebar {
  background: var(--fb-sidebar-bg);
  /* Separates the sidebar from the main content column now that both sit
     on the same off-white/white background — without it the boundary
     between them was only a 1px border, easy to miss at a glance. */
  box-shadow: 3px 0 10px rgba(15, 23, 20, 0.06);
}

/* template.js toggles `.headerLight` on `.fixed-top` once scrollTop >= 97
   (see assets/js/template.js), and style.css's `.navbar.headerLight
   .navbar-brand-wrapper/.navbar-menu-wrapper` shrinks the navbar from 97px
   to 70px on scroll (L29970). The sidebar below is force-fixed at a
   hardcoded `top: 97px` (next rule) and never moves, so once scrolled the
   navbar's bottom edge sits at 70px while the sidebar still starts at
   97px — a 27px strip of bare page background between them. Locking the
   navbar's height keeps it flush with the sidebar's fixed offset at every
   scroll position; headerLight's background/box-shadow-on-scroll styling
   is unaffected. */
.navbar.headerLight .navbar-brand-wrapper,
.navbar.headerLight .navbar-menu-wrapper {
  height: 97px !important;
}

/* Keep the sidebar pinned in the viewport instead of scrolling away with
   the page. style.css already ships this exact behavior as an opt-in
   `.sidebar-fixed` body class (~L30299), but no page sets it, so the
   sidebar is just a normal-flow flex child of .page-body-wrapper today and
   scrolls off with .main-panel's content. Applying it unconditionally here
   avoids editing every page's <body> tag. Gated to the same >=992px
   breakpoint style.css itself uses for `.sidebar-fixed` — below that, the
   sidebar is already `.sidebar-offcanvas`'s own fixed overlay (L28789),
   untouched by this. */
@media (min-width: 992px) {
  .sidebar {
    position: fixed !important;
    top: 0;
    left: 0;
    height: 100vh;
    /* Bootstrap's .fixed-top (the navbar) sits at z-index 1030 — this must
       clear it so the sidebar visually covers the navbar's brand-wrapper
       corner underneath (hidden, not removed, just below) rather than
       being covered by it. */
    z-index: 1031;
  }
  /* The sidebar's own brand header (below) now occupies the first 97px —
     same height the old top:97px offset used to reserve, so this scroll
     area's size is unchanged even though the sidebar itself now starts
     higher. */
  .sidebar .nav {
    max-height: calc(100vh - 97px);
    overflow-y: auto;
  }
  .main-panel {
    margin-left: 220px;
  }
  .sidebar-icon-only .main-panel {
    margin-left: 70px;
  }
  /* The navbar's own brand-wrapper (logo + collapse toggle) sits exactly
     where the sidebar now draws over it — hidden rather than restyled,
     since the sidebar's own brand header (below) replaces both the logo
     and the toggle button (template.js's `[data-bs-toggle="minimize"]`
     binding runs after mountSidebar()/mountNavbar() have already written
     both into the DOM, so binding a second such button here works the
     same as the original). Content-area header shifts right to start
     exactly where the sidebar ends, same width the toggle uses below. */
  .navbar-brand-wrapper {
    display: none !important;
  }
  .navbar-menu-wrapper {
    margin-left: 220px;
  }
  .sidebar-icon-only .navbar-menu-wrapper {
    margin-left: 70px;
  }
}

/* Sidebar's own brand header, replacing the navbar-brand-wrapper logo at
   >=992px (see above) — lives inside every sidebar template (SIDEBAR_HTML/
   CARETAKER_SIDEBAR_HTML/OWNER_SIDEBAR_HTML/ADMIN_SIDEBAR_HTML in
   layout.js, and layout-tenant.js's SIDEBAR_HTML) right after the opening
   <nav>, so it scrolls with nothing and always sits at the sidebar's own
   top regardless of role. figbloom-mark.png (not the wordmark logo) is
   deliberate: the mark's white center disc keeps it legible even when the
   sidebar itself is the FigBloom preset's own brand green — the wordmark
   image's green lettering would disappear into a same-colored background. */
.sidebar-brand-header {
  /* Hidden below 992px: style.css's own `.sidebar-offcanvas` (L28794)
     already starts its mobile slide-in panel at `top: 97px`, i.e. below
     the persistent mobile top bar — this header would just duplicate
     that bar immediately beneath it. The full-height-sidebar redesign
     this header belongs to is a >=992px layout change only; mobile's
     pre-existing offcanvas behavior is untouched by it. */
  display: none;
}
@media (min-width: 992px) {
  .sidebar-brand-header {
    height: 97px;
    padding: 0 1.25rem;
    display: flex;
    align-items: center;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    flex: 0 0 auto;
  }
}
.sidebar-brand-header a {
  display: flex;
  align-items: center;
  min-width: 0;
  flex: 1 1 auto;
  text-decoration: none;
}
.sidebar-brand-mark {
  width: 38px;
  height: 38px;
  object-fit: contain;
  flex: 0 0 auto;
}
.sidebar-brand-text {
  color: var(--fb-sidebar-text);
  font-size: 1.1rem;
  font-weight: 700;
  margin-left: 0.6rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sidebar-brand-toggle {
  background: none;
  border: none;
  color: var(--fb-sidebar-text);
  opacity: 0.8;
  flex: 0 0 auto;
  padding: 0.25rem;
}
.sidebar-brand-toggle:hover {
  opacity: 1;
}
@media (min-width: 992px) {
  /* The collapsed rail is 70px wide — only ~38px of content width left after
     the side padding, which can't hold the 30px mark and the toggle button
     side by side (they overlapped). The header is 97px tall, so stack them
     instead: mark on top, toggle underneath, both centered. The link must
     stop flex-growing (its `flex: 1 1 auto` would otherwise stretch it
     vertically in a column and push the toggle out of the header). */
  .sidebar-icon-only .sidebar-brand-header {
    padding: 0;
    flex-direction: column;
    justify-content: center;
    gap: 6px;
  }
  .sidebar-icon-only .sidebar-brand-header a {
    flex: 0 0 auto;
  }
  .sidebar-icon-only .sidebar-brand-text {
    display: none;
  }
  .sidebar-icon-only .sidebar-brand-mark {
    width: 30px;
    height: 30px;
  }
}

/* Collapsed-sidebar (icon-only) mini logo. The brand-wrapper rail is only
   70px wide (style.css), and with its stock 26px left padding + the
   toggler's 16px width + 1rem gutter, anything past ~12px wide gets
   pushed past x=70 — .main-panel starts exactly there and paints over
   the overflow (wrapper itself has no overflow:hidden), so the mini
   logo's right portion was getting visually cut off. Tightening the
   padding/gutter here keeps the whole mark inside the rail. */
@media (min-width: 992px) {
  .sidebar-icon-only .navbar .navbar-brand-wrapper {
    padding-left: 14px !important;
  }
  .sidebar-icon-only .navbar .navbar-brand-wrapper .me-3 {
    margin-right: 6px !important;
  }
  .sidebar-icon-only .navbar .navbar-brand-wrapper .brand-logo-mini img {
    width: 28px !important;
    height: 28px !important;
  }
}
.sidebar .nav .nav-item .nav-link,
.sidebar .nav .nav-item .nav-link i.menu-icon { color: var(--fb-sidebar-text); }

/* Sidebar section labels (Overview/Property & Tenants/Billing & Finance/...
   — see layout.js's SIDEBAR_HTML) group the PM sidebar's 12 top-level items
   so they scan as related clusters instead of one long flat list. style.css
   ships `.nav-category` already styled for a LIGHT sidebar with a
   `.sidebar-dark` dark variant (L28622/28631) — this app's sidebar is
   always the brand's dark green via --fb-sidebar-bg, and (per the sub-menu
   comment above) never sets that `.sidebar-dark` body class, so the stock
   rule's #404040 text would go near-invisible against it. Overridden here
   the same way the nav-link text itself is, reusing --fb-sidebar-text so
   light/dark-mode both just work; dimmed via opacity rather than a second
   color token since no "muted sidebar text" variable exists yet. The first
   category (Overview) sits directly under the brand header rather than
   this rule's default 20px top gap — fine visually, not worth a :first-
   child special case. */
.sidebar .nav .nav-item.nav-category {
  color: var(--fb-sidebar-text) !important;
  opacity: 0.55;
  border-bottom: none;
}
[data-theme="dark"] .sidebar .nav .nav-item.nav-category {
  color: #C7CBCE !important;
}
.sidebar .nav .nav-item.active > .nav-link i,
.sidebar .nav .nav-item.active > .nav-link .menu-title,
.sidebar .nav .nav-item.active > .nav-link .menu-arrow,
.sidebar .nav .nav-item:hover > .nav-link i,
.sidebar .nav .nav-item:hover > .nav-link .menu-title,
.sidebar .nav .nav-item:hover > .nav-link .menu-arrow { color: var(--fb-sidebar-text) !important; }

/* The active/expanded/hovered nav-link itself (not just its icon/label)
   — style.css paints this as a solid white tab cut into the sidebar
   (L28585/28608/28611/28650), a shape meant to pop against the stock
   template's light sidebar. On this app's always-dark green sidebar it
   reads as a jarring opaque white box instead of a highlight. Worse, its
   own hover rule (L28643) sets `color: #fff` on that same white
   background — text that's invisible against its own backdrop. Replacing
   the fill with a translucent white overlay (readable on any sidebar
   shade, including a custom user theme) and a uniform rounded corner
   fixes both: a subtle "selected" tint instead of a hard white panel.
   The label itself stays --fb-sidebar-text (not --fb-sidebar-active) —
   that accent green was tuned to contrast against a SOLID white tab;
   against this translucent overlay the backdrop is still mostly the
   sidebar's own dark green, so accent-green text on it was reading as
   green-on-green, near-illegible the moment an item was clicked/hovered. */
.sidebar .nav .nav-item.active > .nav-link,
.sidebar .nav .nav-item.active > .nav-link[aria-expanded="true"],
.sidebar .nav .nav-item.active > .nav-link[aria-expanded="false"],
.sidebar .nav .nav-item.active > .nav-link.collapsed,
.sidebar .nav:not(.sub-menu) > .nav-item > .nav-link[aria-expanded="true"] {
  background: rgba(255, 255, 255, 0.12) !important;
  border-radius: 8px !important;
}
.sidebar .nav:not(.sub-menu) > .nav-item:hover > .nav-link,
.sidebar .nav:not(.sub-menu) > .nav-item:hover[aria-expanded="true"] {
  background: rgba(255, 255, 255, 0.07) !important;
  color: var(--fb-sidebar-text) !important;
  border-radius: 8px !important;
}

/* Collapsed (icon-only) sidebar — style.css's own `.sidebar-icon-only
   .sidebar` (L30109) hardcodes `background: #fff` and its nav-icon color
   rule (L30150) hardcodes a stock blue, both MORE specific than the plain
   `.sidebar`/`.sidebar .nav .nav-item .nav-link` rules above (an extra
   `.sidebar-icon-only` ancestor class beats them on specificity even
   without !important), so collapsing the sidebar was silently reverting
   both back to vendor defaults regardless of the active theme. Mirrors
   the same three rules above, scoped to collapsed mode, with !important
   to actually out-specify style.css. */
@media (min-width: 992px) {
  .sidebar-icon-only .sidebar {
    background: var(--fb-sidebar-bg) !important;
  }
  .sidebar-icon-only .sidebar .nav .nav-item .nav-link,
  .sidebar-icon-only .sidebar .nav .nav-item .nav-link i.menu-icon {
    color: var(--fb-sidebar-text) !important;
  }
  /* --fb-sidebar-text, not --fb-sidebar-active: same reason as the
     expanded sidebar below — this icon sits directly on the dark sidebar
     background, where the active-green was unreadably close in tone. */
  .sidebar-icon-only .sidebar .nav .nav-item.active > .nav-link i,
  .sidebar-icon-only .sidebar .nav .nav-item.hover-open > .nav-link i,
  .sidebar-icon-only .sidebar .nav .nav-item:hover > .nav-link i {
    color: var(--fb-sidebar-text) !important;
  }
}

/* Expanded sub-menu (e.g. Property/Unit > All Properties/My Property/...)
   — style.css's `.sidebar .nav.sub-menu` (L28668) hardcodes a WHITE
   background (its `.sidebar-dark .nav.sub-menu` dark variant, L28771,
   never applies here — this app never sets a `sidebar-dark` body class),
   plus dark-gray/black/stock-blue link text (L28690-28704) meant to read
   on that white panel. More specific than the plain `.sidebar` rule above,
   so every theme's sub-menu was a bright white patch regardless of the
   sidebar's own color. */
.sidebar .nav.sub-menu {
  background: var(--fb-sidebar-bg) !important;
}
.sidebar .nav.sub-menu .nav-item::before {
  background: var(--fb-sidebar-text) !important;
  opacity: 0.45;
}
.sidebar .nav.sub-menu .nav-item .nav-link {
  color: var(--fb-sidebar-text) !important;
  opacity: 0.8;
}
.sidebar .nav.sub-menu .nav-item .nav-link:hover,
.sidebar .nav.sub-menu .nav-item .nav-link.active {
  /* --fb-sidebar-text (not --fb-sidebar-active) and a translucent
     highlight, matching the top-level nav-link fix above and for the
     same reason: this sits directly on the dark sidebar background
     (sub-menus never get style.css's white panel here — see above), and
     --fb-sidebar-active is too close in tone to it to read. */
  background: rgba(255, 255, 255, 0.08) !important;
  border-radius: 6px !important;
  color: var(--fb-sidebar-text) !important;
  opacity: 1;
}

/* Form focus rings — stock unmodified Bootstrap blue in style.css, never
   reskinned by Star Admin itself either; affects the tenant-search box
   and every Invite Tenant modal field. */
.form-control:focus, .form-select:focus {
  border-color: #5C9573 !important;
  box-shadow: 0 0 0 0.25rem rgba(var(--fb-accent-rgb), 0.25) !important;
}

/* "Sophie" chat FAB (assets/js/chat-widget.js) — it injects its own
   <style> tag at runtime, later in the DOM than this <link>, so
   !important is required to win against it. Matches the real app's own
   ChatWidget.jsx exactly (bg-teal-600 / hover:bg-teal-700). */
#chat-widget-toggle { background-color: var(--fb-accent-safe) !important; }
#chat-widget-toggle:hover { background-color: var(--fb-accent-hover) !important; }
#chat-widget-header { background-color: var(--fb-accent-safe) !important; }
#chat-widget-messages .cw-user .cw-bubble { background-color: var(--fb-accent-safe) !important; }
#chat-widget-messages .cw-bubble a { color: var(--fb-accent) !important; }
#chat-widget-messages .cw-bubble code { color: var(--fb-accent-hover) !important; }
#chat-widget-input:focus { border-color: var(--fb-accent-safe) !important; box-shadow: 0 0 0 .15rem rgba(var(--fb-accent-rgb),.25) !important; }
#chat-widget-send { background-color: var(--fb-accent-safe) !important; }
#chat-widget-send:not(:disabled):hover { background-color: var(--fb-accent-hover) !important; }

/* Import Tenants modal — drag-and-drop CSV drop zone (properties.js/
   units.js's wireImportTenants/handleSelectedFile). Plain custom class,
   not a Star Admin component. */
.import-dropzone {
  border: 2px dashed var(--bs-primary-border-subtle);
  border-radius: 0.5rem;
  padding: 2rem 1rem;
  text-align: center;
  cursor: pointer;
  color: #5C9573;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.import-dropzone:hover,
.import-dropzone.import-dropzone-active {
  border-color: var(--fb-accent);
  background: var(--bs-primary-bg-subtle);
}

/* Danger/error stays red — matches the real app's OWN current precedent
   (Banner type="error", the "Expired" tenancy badge both use red today),
   so "no red in brand/decorative UI" is read as scoped to brand accents,
   not functional error states. Revisit if that reading is wrong. */
.alert-danger {
  --bs-alert-color: #991b1b;
  --bs-alert-bg: #fef2f2;
  --bs-alert-border-color: #fecaca;
}
.text-danger { color: #991b1b !important; }

/* Exact-brand success green, replacing Star Admin's own muted sage
   (#4DA761) — used for both real computed "up" trends (Rent Collections
   month-over-month) and the dashboard's decorative stat-tile arrows. */
.text-success { color: #005A24 !important; }

/* Decorative/informational "down" trend indicators — NOT the same thing
   as .text-danger (real errors). Used by: the dashboard's stat-tile
   down-arrows (dashboard.html — some are static placeholders per the
   page's own comments, not computed) and the real computed Rent
   Collections month-over-month decline (dashboard.js
   renderCollectionsSummary). Brand rule is no red in decorative UI, so
   this gets the accent orange's AA-safe text shade (gold-800) instead of
   Bootstrap's danger red — never gold-500 (#FF8000) directly as text,
   see tailwind.config.js's own accessibility note. */
.trend-down { color: #984E03 !important; }

/* Page background/text — the "Page colour" picker (Settings > Appearance).
   Light mode had no such rule anywhere before this feature (style.css's
   own body { background: #F4F5F7 } was the only thing painting it) — this
   is a genuine, intentional first-time visual change, not a refactor.
   Dark mode keeps its own explicit, more-specific override just below
   ([data-theme="dark"] body) so the cascade stays unambiguous; both read
   the same --fb-page-bg/--fb-page-text custom properties, so no value
   actually differs between the two rules in dark mode. */
body { background-color: var(--fb-page-bg) !important; color: var(--fb-page-text) !important; }
.content-wrapper, .footer { background-color: var(--fb-page-bg) !important; }
.card, .modal-content, .dropdown-menu { background-color: var(--fb-card-bg) !important; }

/* --- Dark mode (Settings > Appearance) --------------------------------
   Applied via data-theme="dark" on <html>, set synchronously by
   assets/js/theme.js before first paint (see that file for the
   localStorage cache + system-preference resolution). Minimal overrides
   scoped to the layout chrome and generic Bootstrap components everywhere
   across the app — body/card backgrounds, text, tables, form controls,
   sidebar/navbar — enough that toggling visibly changes every page, not a
   pixel-perfect dark skin for every Star Admin widget. */
[data-theme="dark"] {
  --fb-sidebar-bg: #191D20;
  --fb-sidebar-text: #C7CBCE;
  --fb-page-bg: #14181B;
  --fb-page-text: #E6E8EA;
  --fb-card-bg: #1E2327;
}
[data-theme="dark"] body {
  background-color: var(--fb-page-bg) !important;
  color: var(--fb-page-text) !important;
}
[data-theme="dark"] .content-wrapper,
[data-theme="dark"] .footer {
  background-color: var(--fb-page-bg) !important;
  color: #9AA1A6 !important;
}
[data-theme="dark"] .card,
[data-theme="dark"] .modal-content,
[data-theme="dark"] .dropdown-menu {
  background-color: var(--fb-card-bg) !important;
  color: var(--fb-page-text) !important;
  border-color: #2B3136 !important;
}
[data-theme="dark"] .card-title,
[data-theme="dark"] .card h1, [data-theme="dark"] .card h2, [data-theme="dark"] .card h3,
[data-theme="dark"] .card h4, [data-theme="dark"] .card h5, [data-theme="dark"] .card h6,
[data-theme="dark"] h4.mb-0 {
  color: #E6E8EA !important;
}
[data-theme="dark"] .table {
  color: #E6E8EA !important;
  background-color: transparent !important;
}
[data-theme="dark"] .table td,
[data-theme="dark"] .table th {
  border-color: #2B3136 !important;
}
[data-theme="dark"] .form-control,
[data-theme="dark"] .form-select {
  background-color: #262C31 !important;
  border-color: #3A4247 !important;
  color: #E6E8EA !important;
}
[data-theme="dark"] .form-control::placeholder {
  color: #7A828A !important;
}
[data-theme="dark"] .border-bottom {
  border-color: #2B3136 !important;
}
[data-theme="dark"] .navbar {
  background-color: #1E2327 !important;
  border-color: #2B3136 !important;
}
[data-theme="dark"] .sidebar {
  background-color: #191D20 !important;
}
[data-theme="dark"] .sidebar .nav .nav-item .nav-link,
[data-theme="dark"] .sidebar .nav .nav-item .nav-link i.menu-icon {
  color: #C7CBCE !important;
}
[data-theme="dark"] .text-muted {
  color: #9AA1A6 !important;
}
[data-theme="dark"] .badge-secondary,
[data-theme="dark"] .badge-secondary:not(.badge-primary) {
  background: #2B3136 !important;
  border-color: #3A4247 !important;
  color: #C7CBCE !important;
}
[data-theme="dark"] .import-dropzone {
  border-color: #3A4247;
  color: #9AA1A6;
}
[data-theme="dark"] .import-dropzone:hover,
[data-theme="dark"] .import-dropzone.import-dropzone-active {
  border-color: #3E8E5F;
  background: #1B2420;
}

/* style.css gives the navbar's two inner wrappers their own explicit
   light background (#F4F5F7 on both .navbar-brand-wrapper and
   .navbar-menu-wrapper), independent of .navbar's own background above —
   a child's own background always paints over its parent's, so
   overriding .navbar alone left the entire top bar looking unstyled. */
[data-theme="dark"] .navbar .navbar-brand-wrapper,
[data-theme="dark"] .navbar .navbar-menu-wrapper {
  background-color: #1E2327 !important;
  color: #C7CBCE !important;
}
[data-theme="dark"] .navbar .welcome-text,
[data-theme="dark"] .navbar .text-black {
  color: #E6E8EA !important;
}
[data-theme="dark"] .navbar .welcome-sub-text {
  color: #9AA1A6 !important;
}

/* Toolbar buttons (Share/Print/Export-style) — btn-outline-dark's
   near-black default text/border is nearly invisible on a dark page;
   btn-light's white fill is the same "glaring box" problem as bg-white
   below. */
[data-theme="dark"] .btn-outline-dark {
  color: #C7CBCE !important;
  border-color: #3A4247 !important;
}
[data-theme="dark"] .btn-outline-dark:hover {
  color: #14181B !important;
  background-color: #C7CBCE !important;
  border-color: #C7CBCE !important;
}
[data-theme="dark"] .btn-light {
  background-color: #262C31 !important;
  border-color: #3A4247 !important;
  color: #E6E8EA !important;
}
[data-theme="dark"] .btn-light:hover {
  background-color: #3A4247 !important;
}

/* Pill tabs (Settings' General/Billing/Integrations/... row) — inactive
   labels default to a mid-gray that reads as barely-there on the dark
   body background. */
[data-theme="dark"] .nav-tabs {
  border-bottom-color: #2B3136 !important;
}
[data-theme="dark"] .nav-tabs .nav-link {
  color: #9AA1A6 !important;
}
[data-theme="dark"] .nav-tabs .nav-link:hover {
  color: #E6E8EA !important;
  border-color: #2B3136 #2B3136 transparent !important;
}
[data-theme="dark"] .nav-tabs .nav-link.active {
  background-color: transparent !important;
  border-color: transparent transparent var(--fb-accent) !important;
}

/* Generic Bootstrap alerts, same --bs-alert-* override pattern as
   .alert-danger above (which stays red — real error/negative-value
   semantics are the documented exception to "no red," not a dark-mode
   concern). warning/info/success get the same "readable tint on a dark
   card" treatment instead of their light-mode pastel defaults, which
   clash hard against a dark page wherever these appear (Settings,
   Support, import/export flows, etc). */
[data-theme="dark"] .alert-danger {
  --bs-alert-color: #FCA5A5;
  --bs-alert-bg: #3B1719;
  --bs-alert-border-color: #5C2327;
}
[data-theme="dark"] .alert-warning {
  --bs-alert-color: #FBBF77;
  --bs-alert-bg: #3A2A12;
  --bs-alert-border-color: #5C4419;
}
[data-theme="dark"] .alert-info {
  --bs-alert-color: #93C5FD;
  --bs-alert-bg: #14243B;
  --bs-alert-border-color: #1E3A5C;
}
[data-theme="dark"] .alert-success {
  --bs-alert-color: #86D9A4;
  --bs-alert-bg: #12291B;
  --bs-alert-border-color: #1B4029;
}

/* Search-icon boxes (input-group-text, e.g. every list page's search
   bar) and any other bg-white utility box sitting next to a dark form
   control — otherwise a glaring white square. */
[data-theme="dark"] .input-group-text,
[data-theme="dark"] .bg-white {
  background-color: #262C31 !important;
  border-color: #3A4247 !important;
  color: #9AA1A6 !important;
}

/* Bootstrap's modal close "X" is a dark SVG baked in as a background-
   image — invisible on our dark modal header without inverting it. */
[data-theme="dark"] .btn-close {
  filter: invert(1) grayscale(100%) brightness(180%);
}

/* style.css defines .form-check TWICE: Bootstrap's own generated rule
   (padding-left: 1.5em, so the input's own margin-left: -1.5em pulls it
   back to sit flush against the label) and, later in the same file,
   StarAdmin's older custom-checkbox widget rule (padding-left: 0), built
   for a DIFFERENT markup shape where <input> is nested INSIDE
   .form-check-label — absolutely positioned there and invisible, with
   .form-check-label's own margin-left supplying the offset instead (see
   the dashboard task-checkbox comment further down). The later rule wins
   the cascade for every .form-check in the app, including the ones that
   use Bootstrap's own sibling structure (<input> and <label> as
   siblings — what every checkbox/radio outside the dashboard task list
   actually uses). With padding-left back at 0, the input's -1.5em margin
   has nothing to pull against and drags it outside its own container to
   the left — this is what made the Import Tenants radios render outside
   the modal. Restoring the padding only where the input is a direct
   child of .form-check (the sibling structure) leaves the dashboard's
   own nested-label widget untouched.

   .form-switch is excluded from the rule above and given its own below:
   Bootstrap's .form-switch rule (padding-left: 2.5em, a wider value for
   the pill-shaped toggle) has the exact same specificity as StarAdmin's
   .form-check override and loses to it by source order alone — so every
   toggle switch in the app (settings.html, property-settings.html) was
   *already* getting padding-left: 0 before any of this, independently of
   the plain-checkbox/radio bug above. Needs :has() too, to out-specify
   StarAdmin's rule rather than just tie and lose again. */
.form-check:has(> .form-check-input):not(.form-switch) {
  padding-left: 1.5em;
}
.form-check.form-switch:has(> .form-check-input) {
  padding-left: 2.5em;
}

/* Checkbox/radio/switch inputs (e.g. the OTP-channel Email/SMS radios,
   onboarding.html's Terms checkbox) — Bootstrap hardcodes its own blue
   for the checked fill AND the focus ring, neither of which reads
   --bs-primary above, so both need an explicit override the same way
   .btn-outline-primary etc. did above. Dark mode already had the
   checked-fill color further down; this is the light-mode (default)
   equivalent that was missing. */
.form-check-input:checked {
  background-color: var(--fb-accent-safe) !important;
  border-color: var(--fb-accent-safe) !important;
}
/* Unchecked state had no override at all — a plain white box on a white
   card/modal (confirmed via computed style: #fff fill, a near-white
   #DEE2E6 border), so every switch read as barely-there until checked.
   Ironic for the Accessibility modal's own toggles specifically, which is
   where this was reported. */
.form-check-input:not(:checked) {
  background-color: #E4E4E2 !important;
  border-color: #A2A6A8 !important;
}

/* Radio-styled-as-button pills (Settings > Appearance's Light/Dark/System
   and Comfortable/Compact, the Accessibility modal's Text size row) all
   use Bootstrap's native .btn-check + .btn-outline-dark pattern, whose
   stock "checked" fill is Bootstrap's own --bs-dark (a near-black navy) —
   unrelated to the brand accent every other selected/active/filled state
   in the app already uses, so the one selected pill in a row stood out as
   a jarring, differently-colored navy block (reported from the
   Accessibility modal's "Largest" pill specifically, but the same stock
   rule is what Settings' own Light/Dark/Comfortable/Compact buttons use
   too). */
.btn-check:checked + .btn-outline-dark {
  background-color: var(--fb-accent-safe) !important;
  border-color: var(--fb-accent-safe) !important;
  color: #fff !important;
}
/* A checked radio's default Bootstrap dot SVG (--bs-form-check-bg-image,
   set by .form-check-input:checked[type=radio] in style.css) is a plain
   white circle covering about a quarter of the input's area — on any
   solid fill color that reads as a hollow ring/donut rather than a
   filled, obviously-selected indicator (unlike a checkbox's small
   checkmark glyph, which doesn't have this problem). Suppressing it
   leaves the green fill above as a clean solid dot instead. */
.form-check-input:checked[type="radio"] {
  --bs-form-check-bg-image: none !important;
}
.form-check-input:focus {
  border-color: var(--fb-accent-safe) !important;
  box-shadow: 0 0 0 0.25rem rgba(var(--fb-accent-rgb), 0.25) !important;
}

/* Checkbox/radio/switch inputs default to a white box + light border,
   same problem as text inputs above. */
[data-theme="dark"] .form-check-input {
  background-color: #262C31 !important;
  border-color: #3A4247 !important;
}
[data-theme="dark"] .form-check-input:checked {
  background-color: var(--fb-accent-safe) !important;
  border-color: var(--fb-accent-safe) !important;
}

/* Dashboard's task-list checkbox (dashboard.js) uses StarAdmin's older
   custom-checkbox widget — a real <input> hidden behind
   a drawn <i class="input-helper"> box via :before/:after — instead of
   plain .form-check-input, so the override above never reaches it. Same
   vendor-hardcoded-blue problem, different selector.

   The dashboard's own .todo-list-rounded variant (dashboard.html's Todo
   list card) draws the circle AND the checkmark glyph with its own more
   specific rule, still hardcoding the same blue for the circle's border
   and the checkmark's color — the background-only fix above left a
   green-filled circle with a blue ring and a blue checkmark on top of it,
   reading as a muddy dark blob instead of a clean checked state. */
.form-check .form-check-label input[type=checkbox]:checked + .input-helper:before {
  background: var(--fb-accent-safe) !important;
}
.todo-list-rounded .form-check .form-check-label input[type=checkbox]:checked + .input-helper:before {
  background: var(--fb-accent-safe) !important;
  border-color: var(--fb-accent-safe) !important;
}
.todo-list-rounded .form-check .form-check-label input[type=checkbox]:checked + .input-helper:after {
  color: #ffffff !important;
}

/* Table row hover (every list page's .table-hover) and dropdown-item
   hover/focus (workspace switcher, notifications, property picker,
   row action menus) both default to a light-gray tint that reads as a
   bright flash against dark rows/menus. */
[data-theme="dark"] .table-hover > tbody > tr:hover > * {
  background-color: #262C31 !important;
  color: #E6E8EA !important;
}
[data-theme="dark"] .dropdown-item:hover,
[data-theme="dark"] .dropdown-item:focus {
  background-color: #262C31 !important;
  color: #E6E8EA !important;
}
[data-theme="dark"] .dropdown-divider {
  border-color: #2B3136 !important;
}

/* Star Admin's own zebra-stripe rule hardcodes a literal background
   color (not a CSS variable), so it wins over Bootstrap's native
   [data-bs-theme=dark] --bs-table-striped-bg regardless — every odd row
   of any .table-striped table (e.g. properties.html's Units list) was
   still rendering solid light-gray. Match the template's own selector
   exactly so specificity/order isn't a fight. */
[data-theme="dark"] .table.table-striped > tbody > tr:nth-of-type(odd) > * {
  background-color: #1B2024 !important;
}

/* Generic border utilities beyond border-bottom (already covered above)
   — cards/panels using .border/.border-top/-end/-start still draw a
   light-mode line otherwise. */
[data-theme="dark"] .border,
[data-theme="dark"] .border-top,
[data-theme="dark"] .border-end,
[data-theme="dark"] .border-start,
[data-theme="dark"] .border-right,
[data-theme="dark"] .border-left {
  border-color: #2B3136 !important;
}

/* Notification bell unread count — style.css's own `.count-indicator
   .count` (used here before this fix) is a fixed 7x7px presence dot, not
   a numeric badge, so any real count clipped/overflowed it. Same red
   (#F95F53) as style.css's .badge-danger for visual consistency with the
   sidebar payment badges below, just sized to actually hold a number. */
.notification-badge {
  position: absolute;
  top: -4px;
  right: 2px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: #F95F53;
  color: #ffffff;
  font-size: 10px;
  font-weight: 600;
  line-height: 16px;
  text-align: center;
}

/* Sidebar Payments / Payment Disputes counts — reuses style.css's own
   .badge-danger for color, just tightened for an inline sidebar spot
   (that class's default padding is sized for a standalone badge, not one
   sitting between a menu title and the collapse arrow). */
.nav-badge {
  margin-left: 8px;
  padding: 1px 6px;
  font-size: 10px;
  font-weight: 600;
  border-radius: 999px;
  line-height: 1.4;
}

/* Print — every page's Print button just calls window.print() with no
   print-specific layout, so it captured the entire app chrome (navbar,
   sidebar, mobile bottom nav, the floating chat widget, footer, and the
   page's own action buttons) around whatever the person actually wanted
   printed. This hides the chrome and lets .main-panel/.content-wrapper
   take the full page width instead of leaving the sidebar's gutter blank. */
@media print {
  .navbar,
  .sidebar,
  .mobile-bottom-nav,
  #chat-widget-toggle,
  #chat-widget-panel,
  .footer,
  .btn-wrapper,
  #share-btn,
  #print-btn,
  #export-btn,
  .modal {
    display: none !important;
  }

  .main-panel,
  .sidebar-icon-only .main-panel {
    margin-left: 0 !important;
    width: 100% !important;
  }

  .content-wrapper {
    padding: 0 !important;
  }
}

/* --- Accessibility (navbar icon, assets/js/accessibility.js) ----------
   Driven by data-a11y-* attributes on <html>, set client-side only —
   these never come from the account/backend, see accessibility.js's own
   header comment. Text size scales the root font-size, which only moves
   anything sized in rem/em (most Bootstrap base typography is) — the
   app's own many explicit px values are a known, accepted gap, not
   something this pass rewrites. */
html[data-a11y-text-size="large"] { font-size: 112.5%; }
html[data-a11y-text-size="larger"] { font-size: 125%; }
html[data-a11y-text-size="largest"] { font-size: 137.5%; }

html[data-a11y-bold] body,
html[data-a11y-bold] p,
html[data-a11y-bold] span,
html[data-a11y-bold] label,
html[data-a11y-bold] td,
html[data-a11y-bold] th {
  font-weight: 600 !important;
}

html[data-a11y-contrast="high"] a { text-decoration: underline !important; }
html[data-a11y-contrast="high"]:not([data-theme="dark"]) .text-muted { color: #3A4247 !important; }
html[data-a11y-contrast="high"]:not([data-theme="dark"]) .card,
html[data-a11y-contrast="high"]:not([data-theme="dark"]) .table td,
html[data-a11y-contrast="high"]:not([data-theme="dark"]) .table th,
html[data-a11y-contrast="high"]:not([data-theme="dark"]) .border,
html[data-a11y-contrast="high"]:not([data-theme="dark"]) .form-control,
html[data-a11y-contrast="high"]:not([data-theme="dark"]) .form-select {
  border-color: #8A9095 !important;
}
html[data-a11y-contrast="high"][data-theme="dark"] .text-muted { color: #D4D8DB !important; }
html[data-a11y-contrast="high"][data-theme="dark"] .card,
html[data-a11y-contrast="high"][data-theme="dark"] .table td,
html[data-a11y-contrast="high"][data-theme="dark"] .table th,
html[data-a11y-contrast="high"][data-theme="dark"] .border,
html[data-a11y-contrast="high"][data-theme="dark"] .form-control,
html[data-a11y-contrast="high"][data-theme="dark"] .form-select {
  border-color: #4A545C !important;
}

/* --- Density (Settings > Appearance) -----------------------------------
   Driven by data-density on <html>, set by assets/js/theme.js's
   applyDensity() the same synchronous, cached-before-paint way as theme.
   Previously a persisted field with no visual effect at all — "Compact"
   saved to the account but changed nothing on screen. Scoped to the
   highest-traffic spacing (cards, tables, sidebar nav, the main content
   gutter) rather than Bootstrap's generic .mb-3-style utility classes,
   which are used for far more than just form/section spacing and would
   be too broad a surface to retune safely here. */
[data-density="compact"] .card-body {
  --bs-card-spacer-y: 0.65rem;
  --bs-card-spacer-x: 0.85rem;
}
[data-density="compact"] .table td,
[data-density="compact"] .table th {
  padding: 0.3rem 0.5rem !important;
}
[data-density="compact"] .content-wrapper {
  padding: 1rem 1.25rem 1rem 1.75rem !important;
}
[data-density="compact"] .sidebar .nav .nav-item .nav-link {
  padding: 6px 24px 6px 24px !important;
}
[data-density="compact"] .form-label {
  margin-bottom: 0.25rem !important;
}

/* Near-zero rather than 0/none so anything listening for transitionend/
   animationend (e.g. Bootstrap's own collapse/modal/dropdown JS) still
   gets its event, just immediately instead of after a real animation. */
html[data-a11y-motion="reduce"] *,
html[data-a11y-motion="reduce"] *::before,
html[data-a11y-motion="reduce"] *::after {
  animation-duration: 0.001ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.001ms !important;
  scroll-behavior: auto !important;
}

/* Brand spinner — replaces Font Awesome's fa-spinner fa-spin everywhere
   (inline loading text, card placeholders, button busy state). Recreates
   the FigBloom mark's own loading animation: the ring stays put, the leaf
   and the bottom "smile" retract and disappear just before the spin (the
   ring briefly stands alone), then an orange arc sweeps clockwise from
   the top, shrinking and fading toward the brand green as it goes, before
   the leaf and smile redraw and the mark holds again. One continuous
   loop — the source clip only played this once per long idle stretch,
   but a spinner needs to keep moving the whole time it's shown.
   `--fb-accent`/`--fb-highlight` are figbloom-theme.css's own brand
   tokens (defined above) so this tracks any future rebrand automatically
   instead of hardcoding figbloom-logo.svg's not-yet-reconciled color. */
.brand-spinner-icon {
  /* No !important here (unlike the rest of this file) — a class selector
     already beats style.css's stray `svg { width: 100% }` chart-SVG rule
     on specificity alone, and leaving it off lets a call site override
     the size with an inline style (e.g. width:2.5em for a standalone
     full-page spinner) without fighting this rule. */
  width: 1.25em;
  height: 1.25em;
  flex: none;
  vertical-align: -0.25em;
}
.brand-spinner-mark { fill: var(--fb-accent) !important; }
/* Used inside a colored button (withButtonSpinner) so the mark matches
   the button's own label color instead of fighting it. */
.brand-spinner-icon--inherit .brand-spinner-mark { fill: currentColor !important; }
.brand-spinner-leaf {
  transform-box: fill-box;
  transform-origin: center;
  animation: brand-spinner-leaf-fade 3s ease-in-out infinite !important;
}
@keyframes brand-spinner-leaf-fade {
  0%, 18%   { opacity: 1; transform: scale(1); }
  24%       { opacity: 0; transform: scale(0.5); }
  76%       { opacity: 0; transform: scale(0.5); }
  82%, 100% { opacity: 1; transform: scale(1); }
}
.brand-spinner-smile {
  transform-box: fill-box;
  transform-origin: right center;
  animation: brand-spinner-smile-fade 3s ease-in-out infinite !important;
}
@keyframes brand-spinner-smile-fade {
  0%, 18%   { opacity: 1; transform: scaleX(1); }
  24%       { opacity: 0; transform: scaleX(0); }
  76%       { opacity: 0; transform: scaleX(0); }
  82%, 100% { opacity: 1; transform: scaleX(1); }
}
.brand-spinner-base { transform-box: fill-box; transform-origin: center; transform: rotate(-90deg); }
.brand-spinner-rotor {
  transform-box: fill-box;
  transform-origin: center;
  animation: brand-spinner-rotate 3s linear infinite !important;
}
@keyframes brand-spinner-rotate {
  0%, 24%   { transform: rotate(0deg); }
  76%, 100% { transform: rotate(360deg); }
}
.brand-spinner-arc {
  stroke-dasharray: 138;
  animation: brand-spinner-sweep 3s cubic-bezier(.4, 0, .2, 1) infinite !important;
}
@keyframes brand-spinner-sweep {
  0%, 24%   { stroke-dashoffset: 138; stroke: var(--fb-accent); }
  28.16%    { stroke-dashoffset: 138; stroke: var(--fb-highlight); }
  40.64%    { stroke-dashoffset: 42;  stroke: var(--fb-highlight); }
  52.6%     { stroke-dashoffset: 85;  stroke: var(--fb-highlight); }
  64.56%    { stroke-dashoffset: 125; stroke: #5a7a2e; }
  76%, 100% { stroke-dashoffset: 138; stroke: var(--fb-accent); }
}
