html {
  font-size: 14px;
}

@media (min-width: 768px) {
  html {
    font-size: 16px;
  }
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

html {
  position: relative;
  min-height: 100%;
}

/* Always reserve room for a vertical scrollbar - on the page and inside every Tabulator grid - so
   a scrollbar appearing never changes the available width. Without it, a grid using fitColumns at
   just the wrong window width got stuck in a loop: scrollbar appears -> columns narrow -> text
   rewraps and the content gets shorter -> scrollbar not needed, disappears -> columns widen ->
   content taller -> scrollbar back... (2026-10-04; sorting a column happened to break the loop).
   Phones/iPad use overlay scrollbars, so this reserves nothing there. */
html {
  scrollbar-gutter: stable;
}
.tabulator .tabulator-tableholder {
  scrollbar-gutter: stable;
}

/* The HORIZONTAL half of the same problem: every grid in this app uses layout "fitColumns" (sized
   to exactly fill its width, never meant to scroll sideways). At some window widths rounding leaves
   the columns a pixel or two too wide, so a horizontal scrollbar appears, steals height, the grid
   re-measures, the scrollbar goes, and it repeats. On desktop widths sideways scrolling is turned off
   for fitColumns grids; below that (narrow windows, where fixed column widths can genuinely exceed
   the space) it's left alone. */
@media (min-width: 992px) {
  .tabulator[tabulator-layout="fitColumns"] .tabulator-tableholder {
    overflow-x: hidden;
  }
}

body {
  margin-bottom: 60px;
  background-color: #f7f6f2;
  /* Offsets the fixed header (nav + breadcrumb bar) so page content doesn't start underneath it -
     approximates their combined height; the header's own content never changes enough between
     pages to need a precise/JS-measured value. */
  padding-top: 100px;
}

/* Nav + breadcrumb bar together, pinned to the viewport top so they're always visible instead of
   scrolling away - the whole reason a fixed header needs body's padding-top above. */
.site-header-fixed {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1030;
}

.navbar-eve {
  background: linear-gradient(135deg, #1b3a5c 0%, #2f5f8f 55%, #4a80b0 100%);
}

.navbar-eve .nav-link {
  color: #fff;
}

.navbar-eve .nav-link:hover,
.navbar-eve .nav-link:focus {
  color: #d3d3d3;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
  color: var(--bs-secondary-color);
  text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
  text-align: start;
}

/* Settings screens (Organization/Phone/Request Settings): the whole stack of cards scrolls
   as one unit instead of growing the page, and Save stays fixed to the viewport bottom so
   it's never scrolled out of reach no matter how far down the card list you are. */
.settings-scroll-outer {
  max-height: 65vh;
  overflow-y: auto;
  padding-right: 0.5rem;
}

.settings-save-bar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 1030;
  background: #fff;
  border-top: 1px solid #dee2e6;
  box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.08);
  padding: 0.75rem 0;
}

.settings-save-bar .settings-save-inner {
  max-width: 90%;
  margin: 0 auto;
  padding: 0 0.75rem;
  display: flex;
  gap: 0.5rem;
}

/* Reserves room below the scroll area so the fixed bar never covers the last card. */
.settings-form-spacer {
  height: 4.5rem;
}

/* Long self-contained forms (My Volunteer Form, New Member, and the Volunteers/Members "flyout"
   edit/view offcanvas panels, which share the same partial views as the full pages) - the field
   list scrolls in its own bounded region so Save stays in view without scrolling through the whole
   form first. Unlike .settings-save-bar above, this uses position:sticky (not fixed): a full page
   needs .form-scroll-outer to actually create a bounded scrolling region (the page itself has none
   on its own), but the offcanvas flyout already provides one via Bootstrap's own
   .offcanvas-body (flex-grow + overflow-y:auto) - sticky automatically pins to whichever of the two
   is this button's actual nearest scrolling ancestor, so the same .form-save-bar markup in the
   shared partial works correctly in both places with no per-context CSS needed. */
.form-scroll-outer {
  max-height: 65vh;
  overflow-y: auto;
  padding-right: 0.5rem;
}

.form-save-bar {
  position: sticky;
  bottom: 0;
  background: #fff;
  border-top: 1px solid #dee2e6;
  padding: 0.75rem 0;
  z-index: 10;
}

.breadcrumb-bar {
  background-color: #f8f9fa;
  border-bottom: 1px solid #dee2e6;
}

.breadcrumb-bar .breadcrumb-item + .breadcrumb-item::before {
  content: ">";
}

/* Tabulator grids (Voicemails/Businesses/Members/Volunteers) mark the selected row with
   Bootstrap's .table-active class, but Bootstrap 5's .table-active rule only paints a
   background when it's inside a real <table>/<tr> - Tabulator renders rows as plain <div>s,
   so the class was a no-op and row selection looked like it did nothing. Painting it directly
   here instead of relying on Bootstrap's table-scoped selector. */
.tabulator-row.table-active {
  /* !important: tabulator_bootstrap5.min.css's own zebra-striping rule
     (.tabulator-row.tabulator-row-even) has the same specificity and loads after this
     stylesheet, so without it the highlight would disappear on even-numbered rows. */
  background-color: var(--bs-table-active-bg, rgba(0, 0, 0, 0.1)) !important;
  /* Trial: the dark-grey highlight was hard to read against default/dark cell text - white text
     everywhere in a selected row, including cells (e.g. ServiceRequestList's rowFormatter) that set
     their own inline color for status-based row coloring, since !important here beats a plain
     (non-important) inline style. */
  color: #fff !important;
}

.tabulator-row.table-active * {
  color: #fff !important;
}

/* App-wide Tabulator grid behavior: every table sticks to whatever width its columns are given
   (fixed field width, set per-column via the "width" option in each view's own JS) rather than
   growing wider than its container - so the grid never gets a horizontal scrollbar. Cell/header
   text that doesn't fit wraps onto multiple lines instead of being clipped with an ellipsis; rows
   only use min-height (tabulator_bootstrap5.min.css), so they grow to fit wrapped content. This
   still requires each table's own `layout` option to be "fitColumns" (not "fitData"/
   "fitDataStretch", which size columns to their content and can exceed the container width). */
.tabulator .tabulator-cell,
.tabulator .tabulator-col-content .tabulator-col-title {
  white-space: normal !important;
  overflow-wrap: break-word;
  word-break: break-word;
}

.tabulator .tabulator-cell {
  overflow: visible;
  text-overflow: clip;
}

/* App-wide Tabulator font - matches legacy's grids exactly (Handsontable's "ht-theme-sv" theme,
   Content/handsontable/styles/ht-theme-sv.css in the legacy app: --ht-font-size/--ht-line-height/
   --ht-font-weight/--ht-header-font-weight), since a font mismatch against legacy was a specific
   tester complaint. Cell text is deliberately heavier (500) than header text (400) - that's
   legacy's own convention, not a typo. The font-family is Bootstrap's own default stack too, but
   set explicitly here so the grid's look doesn't depend on that staying true. */
.tabulator {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, "Helvetica Neue", Arial, sans-serif !important;
  font-size: 14px !important;
  line-height: 20px !important;
  font-weight: 500 !important;
}

.tabulator .tabulator-col-content .tabulator-col-title {
  font-weight: 400 !important;
}

/* App-wide Tabulator header-filter toggle (site.js: initTableFilterToggles) - every grid's
   per-column filter inputs are hidden by default and revealed with the injected "Search" button
   above the table. site.js calls table.redraw(true) right after toggling this class, so Tabulator
   recalculates its own header height/row offsets instead of leaving a stale gap. */
.tabulator.filters-hidden .tabulator-header-filter {
  display: none;
}

/* Hidden by default (mobile card view has no header-filter row to reveal), shown once a fine
   pointer is confirmed present - see the d-touch-none/d-mouse-none block just below, which this is
   grouped with since it's the same table-vs-cards switch applied to a third element (the injected
   Tabulator "Search" button, a sibling of the table rather than a child of it, so it needs its own
   copy of the same rule). Replaces what used to be a copy-pasted
   "@media (max-width: 767.98px) { .table-filter-toggle { display: none; } }" block on every page
   that has one. */
.table-filter-toggle {
  display: none;
  justify-content: flex-end;
  margin-bottom: 0.25rem;
}

/* App-wide "desktop table vs. mobile card list" switch - a drop-in replacement for
   d-none/d-md-block (or d-none/d-md-flex, for a toolbar row) + d-md-none on this exact pairing
   everywhere it's used (roster/log/case grids across the app). Previously driven by Bootstrap's md
   width breakpoint alone, which a phone rotated to landscape can easily report a viewport wide
   enough to trip (some iPhones exceed 900px in landscape) - showing the dense, small-tap-target
   desktop table (and its mouse-oriented toolbar) on a touchscreen. Moved to (hover: hover) and
   (pointer: fine) instead (true only for a real mouse/trackpad) - but that alone swept every
   touch-only device into the phone/card bucket, including a real iPad/tablet, whose screen is
   plenty wide enough to use the table comfortably and shouldn't be stuck with the cut-down mobile
   card view (confirmed real feedback: "the iPad poses an issue as it is wide enough to view
   tables effectively... any tablet, where the screen size is used"). Second query below adds a
   screen-size-aware path for touch devices specifically: (min-width: 600px) and
   (min-height: 600px) both have to hold, not just one - a phone's short axis stays under ~500px
   in CSS pixels no matter which way it's held (even large "phablets"), while a real tablet's short
   axis is comfortably above 600px in either orientation (iPad Mini's narrower dimension is
   744px), so requiring BOTH viewport axes to clear the threshold is what makes this orientation-
   independent without needing the phone-landscape's wide-but-short viewport to slip through the
   way the old single min-width breakpoint did. Use d-touch-none on a block element (a table) or
   d-touch-none-flex on a flex row (a toolbar) - both hidden unless one of the two paths above
   matches; d-mouse-none on the card-list element/its mobile-only search input/toolbar (hidden
   once one does). Same roles d-none/d-md-block(/flex) and d-md-none played before, just gated on
   input capability + real screen size instead of raw viewport width. !important throughout
   matches Bootstrap's own d-* utilities' specificity, since several call sites combine this with
   a Bootstrap display utility (e.g. d-flex) that would otherwise win. */
.d-touch-none, .d-touch-none-flex {
  display: none !important;
}
.d-mouse-none {
  /* visible by default (touch) */
}
@media (hover: hover) and (pointer: fine), (pointer: coarse) and (min-width: 600px) and (min-height: 600px) {
  .d-touch-none {
    display: block !important;
  }
  .d-touch-none-flex {
    display: flex !important;
  }
  .d-mouse-none {
    display: none !important;
  }
  .table-filter-toggle {
    display: flex;
  }
}

/* App-wide page titles: every page's <h1> (including Home's greeting, which uses Bootstrap's
   .display-5 utility - hence !important, since a class selector otherwise beats a bare tag one) at
   60% of Bootstrap's default size (its own default caps at 2.5rem on desktop - 1.5rem is 60% of
   that). The descriptive line most pages show directly under the title (a text-muted paragraph, or
   Home's lead-styled tagline) is hidden outright - only the paragraph immediately following the
   <h1> is targeted, so unrelated .text-muted/.lead text elsewhere on a page is untouched. */
h1 {
  font-size: 1.5rem !important;
}

h1 + p.text-muted,
h1 + p.lead {
  display: none !important;
}

/* App-wide FullCalendar sizing (Service Requests/Service Requests Calendar/Pending Requests
   Calendar timeGrid views): a more compact day-header row (the "Mon 9/1" strip) and 10% shorter
   time-slot rows (FullCalendar's own default is 1.5em) than the library defaults.
   !important: FullCalendar injects its own <style> tag at runtime when its script executes, which
   can land after this stylesheet in the DOM - without it, same-specificity rules would let the
   library's defaults win depending on load order. */
.fc .fc-col-header-cell-cushion {
  font-size: 0.8em !important;
  padding: 2px 4px !important;
  /* FullCalendar renders the weekday/date header as an <a> (for navLinks), which picks up
     Bootstrap's default underlined-link styling otherwise. */
  text-decoration: none !important;
}

.fc .fc-daygrid-day-number {
  text-decoration: none !important;
}

.fc .fc-timegrid-slot {
  height: 1.35em !important;
}

/* Controls row (prev/next/today, the "Aug 30 - Sept 5, 2026" title, view-switcher buttons) - smaller
   buttons and much tighter title text than FullCalendar's defaults (title default is 1.75em,
   buttons 1em/.4em .65em padding, toolbar margin-bottom 1.5em). */
.fc .fc-header-toolbar {
  margin-bottom: 0.5em !important;
}

.fc .fc-toolbar-title {
  font-size: 1em !important;
}

.fc .fc-button {
  font-size: 0.8em !important;
  padding: 0.25em 0.5em !important;
}

/* Light button background (both the Month/Week/Day switcher and the Today/prev/next controls) -
   FullCalendar's own default is a dark navy (#2c3e50) with white text. Overriding its CSS custom
   properties rather than .fc-button-primary directly, since FullCalendar's own rules already read
   colors from these variables - no !important needed, an element's own declared custom property
   always wins over one it would otherwise inherit from FullCalendar's injected :root default. */
.fc {
  --fc-button-bg-color: #f8f9fa;
  --fc-button-border-color: #ced4da;
  --fc-button-text-color: #212529;
  --fc-button-hover-bg-color: #e2e6ea;
  --fc-button-hover-border-color: #adb5bd;
  --fc-button-active-bg-color: #dae0e5;
  --fc-button-active-border-color: #adb5bd;
}

/* A long nav dropdown (Settings especially, ~19 items now that everything under it is reachable
   on mobile) can be taller than the viewport itself. Simply capping .dropdown-menu's own height
   (an earlier attempt) didn't fix it: .site-header-fixed is position:fixed, and Bootstrap's
   dropdown-menu is Popper-positioned position:absolute inside it - a fixed ancestor's own overflow
   is clipped by the viewport with no page scroll able to reach past it, and an absolutely
   positioned descendant doesn't escape that. Forcing the dropdown to abandon Popper's absolute
   positioning (position:static !important overrides the inline style Popper sets, which a plain
   CSS class can't otherwise beat) makes it lay out inline in .navbar-collapse's own normal flow
   instead - exactly the standard fix for "dropdown inside a fixed mobile navbar can't be reached."
   .navbar-collapse then gets the actual scrollable region: capped to the viewport minus roughly
   the fixed header's own collapsed height (matches body's padding-top below, which exists for the
   same reason), so the whole expanded menu - top-level items plus any open dropdown, together -
   scrolls as one, instead of splitting scroll behavior between two different mechanisms. */
@media (max-width: 767.98px) {
  .navbar .dropdown-menu {
    position: static !important;
    float: none;
    transform: none !important;
  }
  .navbar-collapse.show {
    max-height: calc(100vh - 110px);
    overflow-y: auto;
  }
}

/* App-wide (site.js: enhanceDateInputClearButtons) - every native <input type="date"> gets a small
   "x" clear button next to it. Desktop Chrome/Edge already show one inside the field itself, but
   mobile's native picker UI (iOS Safari's wheel picker especially) has no way to clear a date once
   set short of scrolling every wheel back to a placeholder state - this is the actual fix for that,
   not a cosmetic addition. Wrapping the input (instead of overlaying the button on top of it) avoids
   fighting each platform's own differently-positioned native calendar icon. */
.date-input-clear-wrap {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  width: 100%;
}

.date-input-clear-wrap input[type="date"] {
  flex: 1 1 auto;
  min-width: 0;
}

.date-input-clear-btn {
  flex: 0 0 auto;
  border: none;
  background: transparent;
  color: #6c757d;
  font-size: 1.1rem;
  line-height: 1;
  padding: 0 0.25rem;
  cursor: pointer;
}

.date-input-clear-btn:hover {
  color: #212529;
}