/* Shared ARIA combobox popup. The controller keeps focus in the input and
   exposes the current option through aria-activedescendant, so .is-active is
   the visible keyboard-focus state. Positioning belongs to named modifiers. */
.listbox-anchor { position:relative; }

/* Surface = the ONE cross-portal dropdown recipe (2026-08 alignment D26):
   SQUARE box on the text-500 field border — CD's multiselect dropdown skin
   (multiselect.postcss `--vs-border-radius: 0`, `--vs-border-color:
   text.500`). Was 3px radius on secondary-300, a third recipe beside the
   tenant portal's two. */
.listbox {
  margin:0;
  padding:var(--sp-1);
  list-style:none;
  overflow-y:auto;
  background:var(--color-bg);
  border:var(--stroke-1) solid var(--color-border-input);
  border-radius:var(--radius-none);
  box-shadow:var(--shadow-lg);
}
/* The popup FLOATS at every width. It used to switch to `position:static` below
   640, which turned it into a flex ITEM of its own anchor: the home field is
   `.home-search__field { display:flex }`, so the list lined up BESIDE the input
   instead of under it — measured at 390px, input 162px wide and 370px tall,
   popup 196px, placeholder cut off mid-word. The static variant depended on
   whichever `display` the anchor happened to carry, which is exactly how it
   broke; absolute positioning resolves against `.listbox-anchor`, which is
   `position:relative` by definition, and holds whatever the anchor is.
   The height cap is bound to the VIEWPORT, not to a width breakpoint: what
   makes 22rem too much is a short window, and that arrives on a landscape phone
   (844x390: the list ran 197px past the fold) as readily as on a narrow one.
   `dvh` follows the collapsing browser chrome; the plain `vh` line is the
   fallback where it is unsupported (the tenant portal's modal uses the pair).
   On any normal desktop window 22rem still wins, so nothing there changes. */
.listbox--suggest {
  position:absolute;
  z-index:var(--z-header);
  top:calc(100% + var(--sp-1));
  left:0;
  right:0;
  max-height:min(22rem, 60vh);
  max-height:min(22rem, 60dvh);
  padding-inline:0;
}
.listbox--map { order:-1; max-height:13rem; }

.listbox__option {
  display:flex;
  align-items:center;
  gap:var(--sp-2);
  min-height:var(--target-min);
  padding:var(--sp-2) var(--sp-3);
  color:var(--color-text);
  font-size:var(--text-sm);
  line-height:var(--lh-snug);
  cursor:pointer;
}
.listbox--suggest .listbox__option {
  flex-direction:column;
  align-items:flex-start;
  gap:var(--sp-0-5);
  padding-inline:var(--sp-3-5);
}
.listbox--suggest .listbox__option + .listbox__option {
  border-top:var(--stroke-1) solid var(--color-secondary-200);
}
.listbox--map .listbox__option { border-radius:var(--radius-sm); }
.listbox__option > .icon { flex:none; color:var(--color-text-muted); }

.listbox__option:hover { background:var(--color-secondary-50); }
.listbox__option:active { background:var(--color-primary-200); }
.listbox__option[aria-selected="true"] { background:var(--color-primary-100); }
.listbox__option:focus-visible {
  outline:var(--stroke-2) solid var(--color-focus-ring);
  outline-offset:calc(-1 * var(--stroke-2));
}
.listbox__option.is-active {
  background:var(--color-gray-600);
  color:var(--color-text-negative);
}
.listbox__option.is-active :is(.listbox__title, .listbox__meta, .icon) { color:inherit; }
.listbox__option:is([aria-disabled="true"], .is-disabled) {
  opacity:.55;
  cursor:not-allowed;
  pointer-events:none;
}
.listbox__option:is([aria-busy="true"], .is-loading),
.listbox:is([aria-busy="true"], .is-loading) { cursor:progress; }
.listbox:is([aria-busy="true"], .is-loading) .listbox__option { opacity:.7; pointer-events:none; }
.listbox:is([aria-invalid="true"], .is-error) { border-color:var(--color-error); }
.listbox.is-empty { min-height:var(--target-min); background:var(--color-bg-subtle); }

.listbox__title { font-weight:var(--fw-bold); color:var(--color-text); }
.listbox__meta { font-size:var(--text-xs); color:var(--color-text-muted); }

@media (forced-colors:active){
  .listbox__option.is-active { outline:var(--stroke-2) solid Highlight; outline-offset:calc(-1 * var(--stroke-2)); }
}

/* Grouped suggestions (js/search/search-suggest.js). Section headings per
   content kind with a count, following map.geo.admin.ch: the grouping answers
   «I only want services» before anyone looks for a control. */
.suggest__group + .suggest__group { margin-top:var(--sp-1); }
/* NOT sticky. A heading pinned to the top stayed put after its own group had
   scrolled away, so it labelled rows it no longer covered and its count claimed
   something about rows nobody could see; it also covered the first row of its
   own group, and the listbox's own padding-block left a sliced row bleeding
   above it. The size cap already prevents what sticky was for: at most four
   groups of four rows, so a heading is never more than four rows away. */
.suggest__grouptitle { display:flex; justify-content:space-between; gap:var(--sp-3);
  padding:var(--sp-1-5) var(--sp-3-5);
  background:var(--color-secondary-100); color:var(--color-text-700);
  font-size:var(--text-xs); font-weight:var(--fw-bold); }
.suggest__groupcount { color:var(--color-text-muted); font-weight:var(--fw-regular); }
/* A group starts with a heading, not with a divider: the sibling rule above
   would otherwise draw a second line under every section heading. */
.suggest__group .listbox__option:first-of-type { border-top:0; }
/* The meta line stays on ONE line. German UI: a row reading «Unterlage ·
   Bedarfsmeldung und HBB-Prozess · Informatik und IKT-Beschaffung» wrapped and
   made the row nearly twice as tall,
   leaving four suggestions visible instead of seven — which is what made
   scrolling necessary in the first place. Truncated at the end, where the most
   general part sits; the distinguishing part comes first. */
.suggest__group .listbox__meta {
  max-width:100%; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }

/* The popup shell. It carries border and shadow because the action row below
   the list has to sit inside the same box, and because that row must remain
   visible when the list itself is empty — which is the case the row exists for. */
.suggest__shell { position:absolute; z-index:var(--z-header);
  top:calc(100% + var(--sp-1)); left:0; right:0;
  background:var(--color-bg); border:var(--stroke-1) solid var(--color-border-input);
  border-radius:var(--radius-none); box-shadow:var(--shadow-lg); overflow:hidden; }
.suggest__shell .listbox { position:static; border:0; border-radius:0;
  box-shadow:none; max-height:min(22rem, 60vh); max-height:min(22rem, 60dvh); }
/* Without results the shell carries only the action row. An empty list would
   otherwise keep its padding and leave a strip above it. */
.suggest__shell .listbox:empty { display:none; }

/* «… als Frage stellen»: not a result, a way out. It sits OUTSIDE the
   role="listbox" element — an option may contain neither prose nor links, and
   putting it inside would break the option contract. */
.suggest__action { display:flex; align-items:center; gap:var(--sp-2);
  width:100%; min-height:var(--target-min); padding:var(--sp-2-5) var(--sp-3-5);
  border:0; border-top:var(--stroke-1) solid var(--color-secondary-200);
  background:var(--color-secondary-50); color:var(--color-primary-700);
  font:inherit; font-size:var(--text-sm); text-align:left; cursor:pointer; }
.suggest__action:hover { background:var(--color-primary-50); }
.suggest__action .icon { flex:none; }
.suggest__action strong { font-weight:var(--fw-bold); }
