
.global-filter-bar {
    --filter-ctrl-h: 36px;
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--spacing-smd);
    padding: var(--spacing-md) var(--spacing-lg);
    background: var(--color-card-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-sm);
    margin-bottom: var(--spacing-md);
}

.global-filters-row {
    display: flex;
    align-items: center;
    gap: var(--spacing-smd);
    overflow-x: auto;
    overflow-y: visible;
    overscroll-behavior-x: contain;
    -ms-overflow-style: none;
    scrollbar-width: none;
}

.global-filters-row::-webkit-scrollbar {
    display: none;
}

/* Search input with icon. .global-page-search is the ADR-131 canonical name
   (controls-canonicalization Lane 0, shared/components/page_search.html); the
   legacy .global-search-input-wrapper name stays on the same rule so every existing
   consumer that still carries only the old class (not yet migrated onto
   page_search.html) keeps its layout unchanged. */
.global-search-input-wrapper,
.global-page-search {
    position: relative;
    flex: 1 1 260px;
    min-width: 220px;
}

.global-search-input-wrapper .global-filter-control {
    width: 100%;
    padding-left: var(--spacing-2xl);
    /* Tighten the right padding so the clear (×) sits hard against the
       right edge of the field instead of floating inward. */
    padding-right: var(--spacing-sm);
}

.global-search-icon {
    position: absolute;
    left: 0.875rem;
    top: 50%;
    transform: translateY(-50%);
    color: var(--color-text-muted);
    pointer-events: none;
    width: 18px;
    height: 18px;
}

/* Loading spinner that swaps in for the magnifier while a search request is
   in flight. Pair with hx-indicator="closest .global-search-input-wrapper" on the
   input: htmx then marks the wrapper .htmx-request, which shows this span
   via the global .htmx-request .htmx-indicator rule. Centered with a margin,
   not translateY — the spin keyframes own the transform property. */
.global-search-spinner {
    position: absolute;
    left: 0.9375rem;
    top: 50%;
    margin-top: -0.5rem;
    color: var(--color-text-muted);
    pointer-events: none;
}

.global-search-input-wrapper.htmx-request .global-search-icon {
    display: none;
}

/* Base control style. Search fields are pills, matching HubSpot's search
   inputs everywhere they appear (Nick 09-02). */
.global-filter-control {
    height: var(--filter-ctrl-h, 36px);
    min-width: 180px;
    padding: 0 var(--spacing-md);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    background: var(--color-grey-50);
    color: var(--color-text);
    font-size: var(--font-size-md);
    font-family: var(--font-family);
    line-height: 1;
    box-sizing: border-box;
    transition: background-color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}

.global-filter-control:hover {
    background: var(--color-grey-100);
}

/* Focus deliberately leaves the resting background alone — flipping it to
   white at the same time as the border and ring reads as a flash. */
.global-filter-control:focus {
    outline: none;
    border-color: var(--color-focus-ring);
    box-shadow: 0 0 0 3px var(--color-focus-ring-alpha);
}

.global-filter-control::placeholder {
    color: var(--color-text-muted);
}

/* Replace the inconsistent native clear (×) control on type="search" inputs
   with a masked icon matching the muted icon palette. Clearing already works:
   the native "search" event fires on clear/Escape and htmx listens for it. */
.global-filter-control[type="search"]::-webkit-search-cancel-button {
    -webkit-appearance: none;
    appearance: none;
    width: 14px;
    height: 14px;
    margin-left: auto;
    cursor: pointer;
    background: var(--color-text-muted);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M18 6 6 18M6 6l12 12' stroke='black' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M18 6 6 18M6 6l12 12' stroke='black' stroke-width='2.5' stroke-linecap='round'/%3E%3C/svg%3E") center/contain no-repeat;
}

.global-filter-control[type="search"]::-webkit-search-cancel-button:hover {
    background: var(--color-text);
}

/* Filter select dropdowns - custom chevron. Pill like the search field and
   the buttons beside them, matching HubSpot's filter triggers (Nick 09-02). */
.global-filter-select {
    height: var(--filter-ctrl-h, 36px);
    padding: 0 var(--spacing-xl) 0 var(--spacing-smd);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    font-size: var(--font-size-md);
    background: var(--color-card-bg) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E") right 0.5rem center/1.25rem no-repeat;
    appearance: none;
    cursor: pointer;
    min-width: 90px;
    color: var(--color-text);
    line-height: 1;
    box-sizing: border-box;
    transition: all 0.2s ease;
}

.global-filter-select:focus {
    outline: none;
    border-color: var(--color-focus-ring);
    box-shadow: 0 0 0 3px var(--color-focus-ring-alpha);
}

/* Controls hosted in the app header share the standardized control palette
   (see --header-control-* in main.css): solid white, dark text, neutral
   border — org-independent so every tenant's top bar reads identically. The
   base .global-filter-control resting fill is a faint gray; on the dark header that
   reads as muddy, so force pure white here. */
.global-app-header .global-filter-control {
    background-color: var(--header-control-bg);
    color: var(--header-control-fg);
    border-color: var(--header-control-border);
}

.global-app-header .global-filter-control:hover {
    background-color: var(--header-control-hover-bg);
    border-color: var(--header-control-hover-bg);
}

/* ── Header search affordances: white icon, white clear (×), no-flash spinner ──
   On the glass header every search affordance is the same white as the
   placeholder and chrome (var(--header-control-fg)), not the list-page gray.
   All scoped to .global-app-header so the list-page (light background) search keeps
   its gray icon / × / spinner. */
.global-app-header .global-search-icon {
    color: var(--header-control-fg);
}

/* Clear (×) button — white on the header (base mask fill is gray #9ca3af). */
.global-app-header .global-filter-control[type="search"]::-webkit-search-cancel-button,
.global-app-header .global-filter-control[type="search"]::-webkit-search-cancel-button:hover {
    background: var(--header-control-fg);
}

/* The search field's placeholder ("Search") inherits the base muddy-gray
   .global-filter-control::placeholder (#9ca3af) — fine on the old opaque-white pill,
   but low-contrast/hard-to-read over the translucent wine glass. Bring it into
   the same white-frost family as the chrome, i.e. the solid header-control
   foreground. Scoped to .global-app-header so the list-page (light-background) search
   keeps its gray placeholder. */
.global-app-header .global-filter-control::placeholder {
    color: var(--header-control-fg);
    opacity: 1;
}

/* Focus uses the fixed neutral ring, not the brand ring (invisible on a
   brand-colored bar and inconsistent across orgs). */
.global-app-header .global-filter-control:focus {
    border-color: var(--header-control-hover-bg);
    box-shadow: 0 0 0 3px var(--header-control-focus-ring);
}

/* ── Date-filter cluster in a page header ────────────────────────────────────
   The month/year controls and their prev/next arrows, laid out as one evenly
   spaced row. Unscoped on purpose: these render in the page-header toolbar
   slots (My Assignments, My Pay), not in the dark app bar. */
.global-header-date-filters {
    display: inline-flex;
    align-items: center;
    gap: var(--spacing-sm);
}

/* Phone: three selects side by side overrun a 361px header, so the cluster
   wraps onto a second line instead of pushing the page sideways. */
@media (max-width: 767.98px) {
    .global-header-date-filters {
        flex-wrap: wrap;
    }
}

/* Header arrows are not styled here — the calendar's are .global-icon-btn in
   components/date-nav.css. */

/* ── Light-header pickers ──────────────────────────────────────────────────
   The month/year/status/view comboboxes in a page_header.html card or toolbar.
   Their skin is the shared .global-cs--compact (main.css); this sheet only
   sizes them and anchors their menus. */

/* flex: 0 0 auto pins each pill to its own width below. Without it the pills
   stretch to fill the bar, which is what the titleless toolbar shape shows:
   there is no title taking up the left side. */
.global-page-header--card .global-header-cs,
.global-page-header--toolbar .global-header-cs {
    flex: 0 0 auto;
}

/* Sized to the longest label — "All Months", the resting placeholder — with the
   visible chevron (matching .global-filter-select's look) eating into the right side.
   8.5rem is what stops it clipping to "All Month".

   MEASURE THESE IN THE REAL DEPLOYED FONT (Poppins 500 14px, loaded via Google
   Fonts), never a system-font fallback: the fallback under-reports width by
   ~10px, and trusting it once shipped a month pill 6px too tight with
   "September" clipped to "Septembe". */
.global-page-header--card .global-header-cs--month,
.global-page-header--toolbar .global-header-cs--month {
    width: 8.5rem;
}

/* "All Years" (60px) is shorter than month's "All Months" (73px) but still
   needs room for the visible chevron — 6rem clipped it to "All Yea"; 7.5rem
   shows it in full. */
.global-page-header--card .global-header-cs--year,
.global-page-header--toolbar .global-header-cs--year {
    width: 7.5rem;
}

/* Calendar view switcher (Month / Week / Events), declared in
   app/assignments/_calendar_toolbar.html and teleported into this card's row-2
   slot. It is the one .global-header-cs modifier whose width was only ever written
   .app-header-scoped, where it never matched — so it alone fell back to an
   <input>'s intrinsic size and rendered 318px, while --month, --year and
   --status each pin one below. "Events" is shorter than the "All Years" that
   7.5rem already fits with the chevron, so it takes year's width. */
.global-page-header--card .global-header-cs--view,
.global-page-header--toolbar .global-header-cs--view {
    width: 7.5rem;
}

/* The ledger's status filter (payments/partials/_ledger_filters.html). The
   longest label is a comparable length to "All Months" and pays for the same
   visible chevron, so it takes month's 8.5rem.

   The workers roster sized this rule first and no longer draws it: that filter
   became a segmented control on 08-10 and an ordinary saved-view filter on
   08-26 — a Status quick chip on the shared toolbar row, the segmented
   template deleted with it. The bills dashboard drew it too, with a wider
   --status-ramp box for the Ramp rail's "Awaiting Payment", until its status
   combobox was retired for the saved-view chip (08-29). Left at 8.5rem — the
   caller that stayed was measured against it, so re-fitting the width to it is
   its own change. */
.global-page-header--card .global-header-cs--status,
.global-page-header--toolbar .global-header-cs--status {
    width: 8.5rem;
}

/* Panels grow to fit their widest option and anchor to the RIGHT edge of the
   trigger so extra width grows leftward. These pills sit at the right of their
   row, where the base .global-cs-menu's trigger-width panel (left:0;right:0) would
   clip. */
.global-page-header--card .global-header-cs .global-cs-menu,
.global-page-header--toolbar .global-header-cs .global-cs-menu {
    left: auto;
    right: 0;
    width: max-content;
    min-width: 100%;
    overflow-x: hidden;
}

/* ── The dark bar's control height ───────────────────────────────────────────
   28px matches the sidebar org-toggle, so everything across the top strip reads
   as the same control rank. Set as a variable rather than a height because two
   files size the bar's controls from it: .global-filter-control above (the topbar
   search input), and .global-header-btn / .global-header-btn-icon / .global-sidebar-toggle in
   main.css. Those are the only var() readers. tokens.css and notifications.css
   quote the 28px in prose but hardcode their own numbers, so they do NOT follow
   a change made here — grep `var(--filter-ctrl-h`, not the bare name. */
.global-app-header {
    --filter-ctrl-h: 28px;
}

/* No narrow-viewport rule for .global-app-header lives here any more, and none should:
   this file loads BEFORE main.css, which declares `height: var(--header-height)`
   on the same selector at the same specificity, so anything about the bar's
   height written here is silently overruled. The block that used to sit here set
   `flex-wrap: wrap; height: auto` and was half-applied because of exactly that —
   the wrap took, the height did not, so on a phone the bar reflowed onto three
   rows while its box stayed pinned at 40px and rows two and three spilled out
   over whatever was underneath. The header is a single fixed-height row at every
   width (see .global-app-header-actions in main.css); the narrow-viewport give is the
   search box shrinking, which now lives with the rest of its sizing in
   topbar-search.css. */

.global-filter-select:hover {
    border-color: var(--color-text-muted);
}

.global-toggle-switch {
    position: relative;
    display: inline-flex;
    align-items: center;
}

.global-toggle-switch input[type="checkbox"] {
    -webkit-appearance: none;
    appearance: none;
    cursor: pointer;
    width: 46px;
    height: 26px;
    border-radius: var(--radius-pill);
    border: 1px solid var(--color-border);
    background: var(--color-grey-200);
    position: relative;
    transition: background 0.2s, border-color 0.2s, box-shadow 0.2s;
    outline: none;
}

.global-toggle-switch input[type="checkbox"]::after {
    content: "";
    position: absolute;
    top: 3px;
    left: 3px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--color-card-bg);
    transition: transform 0.2s, background 0.2s, box-shadow 0.2s;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
}

.global-toggle-switch input[type="checkbox"]:checked {
    background: var(--color-action);
    border-color: transparent;
}

.global-toggle-switch input[type="checkbox"]:checked::after {
    transform: translateX(20px);
}

.global-toggle-switch input[type="checkbox"]:focus-visible {
    box-shadow: 0 0 0 3px var(--color-focus-ring-alpha);
}

@media (max-width: 600px) {
    .global-filter-bar {
        padding: var(--spacing-md);
    }

    .global-filters-row {
        flex-wrap: wrap;
        justify-content: center;
        text-align: center;
    }

    .global-search-input-wrapper {
        flex: 1 1 auto;
        min-width: 0;
    }

    .global-status-toggle {
        width: 100%;
        margin-left: 0;
    }

    .global-status-toggle__btn {
        flex: 1;
    }
}

/* Legacy support - map old class names */
.global-filter-bar .form-control,
.global-filter-bar .form-select {
    max-width: 200px;
}

.global-filter-bar .btn {
    white-space: nowrap;
}
