/* Quick search widget for the dark header bar (Phase 2, P2-L1). Round 3
   (Nick: "we want to have the full searchbar not just an icon or anything.
   Make it a good size"): an always-visible field, not a collapsed magnifier
   (interface/templates/shared/components/topbar_search.html) — focusing the
   field opens a panel of matches under it. Sized off a live HubSpot
   capture (scratchpad/hubspot-ref/ui-2026-07-24/geometry.json): a 550px rest
   width and an 8px radius, both wider/rounder than this bar's other controls
   (.global-filter-control's sitewide default is 6px) — scoped to this widget only,
   not a sitewide radius change. Reuses .global-filter-control / .global-search-icon
   (filter-bar.css) for the field itself, so it inherits the header's
   white-veil palette via the existing .app-header-scoped overrides and
   already sits inside the SB control height idiom (28px on this bar) — this
   file only adds the layout wrapper and the results panel those page-level
   classes don't already cover.

   .global-topbar-search deliberately does NOT use .global-search-input-wrapper: that
   class carries a page-level flex:1 1 260px/min-width:220px sizing meant for
   a list page's own full-width search bar. The bar widget instead shrinks to
   as little as 240px on narrow viewports and never drops to an icon-only
   state — do not reintroduce one. No focus-widening: the results panel
   already covers the focused state, so the field itself stays a fixed size.

   The flex-basis lives on .global-topbar-search (the actual flex item inside
   .global-app-header), not on .global-topbar-search__form: a nested flex container
   with its own flex-basis does not reliably propagate that size out to its
   parent's flex layout, so the outer wrapper needs the explicit sizing and
   the form just fills it (width: 100%). */
.global-topbar-search {
    display: flex;
    align-items: center;
    flex: 0 1 550px;
    max-width: 560px;
    min-width: 240px;
}

/* On a phone the 240px floor is the whole problem: the bar's other two items
   (the menu toggle and the never-compressing action cluster) leave less than
   that, and a flex item cannot shrink past its min-width, so the search was
   pushed onto a line of its own and the actions onto a third. Below 600px it
   gives instead — the field takes the space that is left, however little that
   is, and the bar stays the single 40px row everything else is measured
   against. Same idiom the page-level filter bar uses at this breakpoint. */
@media (max-width: 600px) {
    .global-topbar-search {
        flex: 1 1 auto;
        min-width: 0;
    }
}

.global-topbar-search__form {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--spacing-xs);
    width: 100%;
}

.global-topbar-search__form .global-filter-control {
    flex: 1 1 auto;
    min-width: 0;
    padding-left: var(--spacing-2xl);
    padding-right: var(--spacing-md);
}

/* ── In-flight spinner ───────────────────────────────────────────────────────
   Same handoff the list pages use (.global-search-spinner in filter-bar.css): the
   input carries hx-indicator="closest .global-topbar-search__form", so htmx
   marks the form .htmx-request. Every rule filter-bar.css scopes to
   .global-search-input-wrapper has to be spelled again here, because this widget
   deliberately does not use that class (see the note at the top of this file).
   White like the rest of the bar's chrome.

   The dim opt-out comes first. The global .htmx-request rule (main.css) fades
   and deadens whatever htmx marks; here that is the form, and the results
   panel is inside it, so a person reading the matches watched them go
   translucent and unclickable on every keystroke's request. The spinner alone
   says a request is out. */
.global-topbar-search__form.htmx-request {
    opacity: 1;
    pointer-events: auto;
}

/* No-flash magnifier ↔ spinner handoff. Both glyphs stay in the layout and
   swap by opacity on a 0.4s delay (instant on release), so a search that
   answers inside 0.4s — the normal case — flips nothing at all. Matched to the
   magnifier's exact size and position so the swap never jumps. */
.global-topbar-search__form .global-search-spinner {
    display: inline-block;
    left: 0.875rem;
    width: 18px;
    height: 18px;
    margin-top: -9px;
    color: var(--header-control-fg);
    opacity: 0;
    transition: opacity 0s;
}

.global-topbar-search__form.htmx-request .global-search-spinner {
    opacity: 1;
    transition: opacity 0s linear 0.4s;
}

.global-topbar-search__form .global-search-icon {
    opacity: 1;
    transition: opacity 0s;
}

.global-topbar-search__form.htmx-request .global-search-icon {
    display: block;
    opacity: 0;
    transition: opacity 0s linear 0.4s;
}

/* ── Results panel ───────────────────────────────────────────────────────────
   The typeahead's matches, swapped in by htmx and shown while the field has
   focus (interface/templates/shared/components/topbar_search.html). It hangs
   off .global-topbar-search__form, which is the position:relative box, so it
   lines up with the field and matches its width on every screen size. Same
   popup treatment as .global-cs-menu (main.css): white card, hairline border,
   --radius-md corners, soft shadow, and its own scroll once the list gets tall — so a long
   list never grows the bar or runs off a short screen. The bar's own controls
   are painted from the dark --header-control-* palette, but this panel sits
   BELOW the bar on the page surface, so it uses the page's light tokens like
   every other popup. */
.global-topbar-search__panel {
    position: absolute;
    top: calc(100% + 0.375rem);
    left: 0;
    right: 0;
    z-index: var(--z-dropdown);
    /* Rows carry a name, a status, and a meta line now, so the old 26rem cut
       the second group in half. */
    max-height: min(70vh, 40rem);
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: var(--spacing-xs);
    background: var(--color-card-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
}

.global-topbar-search__group + .global-topbar-search__group {
    border-top: 1px solid var(--color-border);
    margin-top: var(--spacing-xs);
    padding-top: var(--spacing-xs);
}

.global-topbar-search__group-label {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--spacing-sm);
    margin: 0;
    padding: var(--spacing-xsm) var(--spacing-sm) var(--spacing-xs);
    font-size: var(--font-size-2xs);
    font-weight: var(--font-weight-medium);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

/* "3 of 12" — how much of the group the panel is showing. Quieter than the
   label it sits beside, and never uppercase-tracked like it. */
.global-topbar-search__group-count {
    font-weight: var(--font-weight-medium);
    letter-spacing: normal;
    text-transform: none;
    font-variant-numeric: tabular-nums;
}

.global-topbar-search__row {
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
    padding: var(--spacing-xsm) var(--spacing-sm);
    border-radius: var(--radius-sm);
    color: var(--color-text);
    text-decoration: none;
}

/* Hover and the keyboard highlight (.is-active, set by the Alpine component
   and by the server on the first row) read as one state on purpose: the mouse
   and the arrow keys are pointing at the same thing. */
.global-topbar-search__row:hover,
.global-topbar-search__row.is-active {
    background: var(--color-surface-hover);
    color: var(--color-text);
}

.global-topbar-search__row-icon {
    flex-shrink: 0;
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
}

.global-topbar-search__row-avatar {
    flex-shrink: 0;
}

/* min-width: 0 lets the long-text truncation below actually kick in — without
   it the flex child refuses to shrink past its content. The row is two stacked
   lines now (name + status, then the meta line), so this column stacks and the
   old baseline alignment moves up to the title line. */
.global-topbar-search__row-text {
    display: flex;
    flex-direction: column;
    gap: 0.0625rem;
    min-width: 0;
    flex: 1 1 auto;
}

.global-topbar-search__row-line {
    display: flex;
    align-items: center;
    gap: var(--spacing-xsm);
    min-width: 0;
}

.global-topbar-search__row-title {
    font-size: var(--font-size-sm);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The matched part of a row, in the title or down in the meta line. A <mark> is
   the honest element for "this is why you see this row", but its yellow crayon
   default has no place on the panel — weight carries it instead. Bootstrap also
   pads .mark/mark, which pushed the matched letters apart and split words
   ("Ka rla"), so that padding goes too. */
.global-topbar-search__panel mark {
    background: none;
    color: inherit;
    padding: 0;
    font-weight: var(--font-weight-medium);
}

.global-topbar-search__row-line .global-badge {
    flex-shrink: 0;
}

.global-topbar-search__row-meta {
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The money on a bill row, held to the right edge so a column of amounts lines
   up no matter how long the names are. */
.global-topbar-search__row-amount {
    flex-shrink: 0;
    margin-left: auto;
    font-size: var(--font-size-xs);
}

.global-topbar-search__see-all {
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
}

