/* ===================== Search (search.postcss “SEARCH RESULTS PAGE”) ===================== */
/* Large search field (search--large): bold h-16 field with an icon button on the right. */
.search--large .search__group { display:flex; align-items:stretch; position:relative; height:var(--sp-16); max-width:52rem; }
/* CD search.postcss:135-152: static text-xl (1.25rem, NO responsive scale — bold
   2rem at 1920 would visibly depart from CD), input border color, no field shadow;
   3.5rem of room on the right prevents text and the native clear button from
   running beneath the absolutely positioned search button (the same pr-12
   principle as the header search). */
.search__field { flex:1 1 auto; min-width:0; width:100%; padding:var(--sp-3) var(--sp-14) var(--sp-3) var(--sp-4);
  border:var(--stroke-1) solid var(--color-border-input); box-shadow:none;
  background:var(--color-bg); font-size:var(--fs-xl); font-weight:var(--fw-bold); }
/* --color-secondary-300 #828e9a on white was 3.34:1 (target 4.5:1) on the most
   prominent input in the product; --color-placeholder is 5.65:1. */
.search__field::placeholder { color:var(--color-placeholder); font-weight:var(--fw-regular); }
.search--large .search__submit { position:absolute; top:0; right:0; bottom:0; z-index:var(--z-local-control); }
.search--page-result { padding-top:var(--sp-4); }
@media (min-width:1544px){ .search--page-result { padding-top:var(--sp-8); } }

/* Results list (SearchResultsList): lean rows with metadata, title, and text.
   The `.search-group*` rules were removed when the page changed to a single
   results stream with a type facet (there is no remaining JS consumer). */
/* KEIN oberer Abstand auf `.search-results` selbst. Die Referenz misst zwischen
   der Unterkante des getönten Bandes und der Oberkante von `.search-results`
   exakt 0 px (www.bbl.admin.ch/de/search, 1440 px), und ebenso 0 px bis zum
   ersten Kind: den Abstand trägt dort das erste Kind INNEN, als
   `.search-results__header { pt-4 sm:pt-6 2xl:pt-8 }`
   (designsystem css/components/search.postcss:208-217). Eine Polsterung hier
   oben wäre ein 24-px-Streifen, den die Referenz nicht hat. */
.search-results .catbar:first-child { padding-top:var(--sp-4); }
@media (min-width:640px)  { .search-results .catbar:first-child { padding-top:var(--sp-6); } }
@media (min-width:1544px) { .search-results .catbar:first-child { padding-top:var(--sp-8); } }

.search-results-list { list-style:none; margin:0; padding:0; }
/* A rule ONLY at the bottom, like the CD card containing these rows
   (card.postcss:106-112 `.card--list`: `border-b border-secondary-200`). Before,
   every row had a TOP rule and the last also had a bottom rule. That created a
   double divider directly below the catalogue bar, which already has
   `border-bottom`; the first row looked as though it occupied a separate box. */
.search-result { border-bottom:var(--stroke-1) solid var(--color-secondary-200); }
/* Inner spacing matches `.card--list` (py-4 lg:py-6 2xl:py-8, px-1). */
.search-result__link { display:block; padding:var(--sp-4) var(--sp-1); color:inherit; text-decoration:none; }
@media (min-width:1024px){ .search-result__link { padding-top:var(--sp-6); padding-bottom:var(--sp-6); } }
@media (min-width:1544px){ .search-result__link { padding-top:var(--sp-8); padding-bottom:var(--sp-8); } }
.search-result__link:hover .search-result__title,
.search-result__link:focus-visible .search-result__title { color:var(--color-primary-700); }
.search-result__meta { margin:0 0 var(--sp-0-5); }
.search-result__title { font-size:var(--text-base); font-weight:var(--fw-bold); line-height:var(--lh-snug); margin:0; }
.search-result__desc { margin:var(--sp-1) 0 0; font-size:var(--text-sm); color:var(--color-text-muted); overflow-wrap:anywhere; }
/* No-results block follows search.postcss:240-247: its own vertical padding,
   while spacing BELOW the heading and list grows with the viewport
   (`mb-6 lg:mb-8 2xl:mb-10`). Previously only h2 had a fixed value and the list
   had none, so the tips and notification band touched. */
.search-results__no-results { padding-top:var(--sp-8); padding-bottom:var(--sp-8); }
@media (min-width:1024px){ .search-results__no-results { padding-top:var(--sp-12); padding-bottom:var(--sp-12); } }
.search-results__no-results h2,
.search-results__no-results ul { margin-bottom:var(--sp-6); }
@media (min-width:1024px){ .search-results__no-results h2, .search-results__no-results ul { margin-bottom:var(--sp-8); } }
@media (min-width:1544px){ .search-results__no-results h2, .search-results__no-results ul { margin-bottom:var(--sp-10); } }
.search-results__no-results h3 { margin-top:var(--sp-8); }
/* REGULAR weight, with `<strong>` on the query and the site name only — the
   live agency search sets the sentence in body weight and bolds the two names
   inside it. A fully bold heading made the page shout its one piece of bad
   news; this way the two variable parts are what the eye lands on. */
.search-results__no-results-title { font-weight:var(--fw-regular); }
.search-results__no-results-title strong { font-weight:var(--fw-bold); }
/* `.search-results .notification { my-16 }` (search.postcss:236-238) — the
   notification band deliberately stands apart instead of trailing the tips list. */
.search-results .notification { margin-top:var(--sp-16); margin-bottom:var(--sp-16); }

/* Schematic portfolio map (the dead `.map` placeholder with a fixed 420px height
   was deleted — it had no emitter, and the fixed height opposed the clamp scale). */
/* Fixed map heights ignored the viewport: 440px/34rem/30rem also applied on an
   844px-high phone screen. clamp() scales with it while preserving the desktop
   height (Item 2.27). NOTE: .pf-map is declared twice — this is the base; the
   second declaration near ~2127 adds only the radius. */
/* The explorer map carries BOTH classes (`pf-map dash-map`) in the markup, and
   `.dash-map` appears later in the sheet, so its 30rem upper bound overrode this
   height (measured at 480px instead of the intended value). Also lock it in as
   `.pf-map.dash-map` (0,2,0). The map is the main content on this page and may
   be taller than a dashboard tile. */
.pf-map, .pf-map.dash-map { position:relative; height:clamp(20rem, 78vw, 44rem); overflow:hidden; background:var(--color-secondary-50); }

/* Active filter pills — a portal composition of CD badges. CD ships
   badge-filter, but that is a filter *chooser*; it has no removable pill. */
/* Spacing above the pill row follows CD's scale (search.postcss:266-269
   `.search__filters__tags { pt-4 sm:pt-6 2xl:pt-8 }`). The component rule owns
   it, not an mt utility at the call site (components.js points here). */
.active-filters { display:flex; flex-wrap:wrap; align-items:center; gap:var(--sp-2); padding-top:var(--sp-4); }
@media (min-width:640px){ .active-filters { padding-top:var(--sp-6); } }
@media (min-width:1544px){ .active-filters { padding-top:var(--sp-8); } }
/* Since the refactor, removable pills are genuine .tag-item components (CD's
   interactive pill with a full 44px scale, tag-item.postcss:7-42); only the inner
   pill's tint remains here. The cancel X stays fully opaque: CD never dims pill
   icons (badge.postcss:85-90 / tag-item.postcss:80-85). */
.active-filter { text-decoration:none; }
.active-filter .tag-item__inner { background:var(--color-secondary-100); }
.active-filter:hover .tag-item__inner { background:var(--color-secondary-200); }

/* Topic tile icon (data portal) */
.domain-tile__icon { display:block; color:var(--color-primary-600); margin-bottom:var(--sp-1); }
.card--clickable:hover .domain-tile__icon { color:var(--color-primary-700); }


/* ================== Source selection (js/search/search-ui.js) ================= */
/* One component, one spacing, the same in both places it appears. Trigger and
   panel are ONE element: as siblings each picked up the surrounding column gap,
   so on the home page the line stood 2rem below the field and the panel another
   2rem below the line, while on the results page the same line sat flush.
   The width is inherited from its field — .hero__search caps the home page at
   the width of .home-search, the results page uses .search--large's own cap. */
.search-sources { margin-top:var(--sp-2); max-width:52rem; }
.search-sources__line { margin:0; }
/* `display:flex` with `width:100%`, NOT inline-flex. As inline-flex the button
   sized itself to its content and grew past the field it belongs under — across
   the whole page once ten kinds were listed. Now the parent sets the width and
   the sentence wraps inside it. */
.search-sources__toggle { display:flex; flex-wrap:wrap; align-items:baseline;
  gap:var(--sp-1) var(--sp-2); width:100%; max-width:100%;
  padding:var(--sp-1) 0; background:none; border:0;
  font:inherit; font-size:var(--text-sm); color:var(--color-text-muted);
  text-align:left; cursor:pointer; }
/* `min-width:0` lifts the minimum a flex child otherwise keeps; without it the
   longest run of text pushes the button open again. */
.search-sources__text { min-width:0; overflow-wrap:anywhere; }
.search-sources__toggle:hover { color:var(--color-text); }
.search-sources__toggle strong { color:var(--color-text); font-weight:var(--fw-bold); }
.search-sources__action { display:inline-flex; align-items:center; gap:var(--sp-1);
  flex:none; color:var(--color-primary-600); text-decoration:underline;
  text-underline-offset:var(--stroke-2); white-space:nowrap; }
.search-sources__toggle:hover .search-sources__action { color:var(--color-primary-800); }
/* Scoped to the trigger, or the rule would catch every chevron in the document. */
.search-sources__chev { width:1em; height:1em; transition:transform var(--duration); }
.search-sources__toggle[aria-expanded="true"] .search-sources__chev { transform:rotate(180deg); }

/* The panel uses the same surface recipe as the facet panel, so the two read as
   related — related, not identical: this one has no result counts, because it
   cannot have any without an aggregate query on every open. */
.search-sources__panel { max-width:100%; padding:var(--sp-4) var(--sp-5);
  margin:var(--sp-3) 0 0; background:var(--surface-bg-muted);
  border:var(--stroke-1) solid var(--surface-border);
  border-radius:var(--surface-radius); box-shadow:var(--surface-elevation); }
/* The MULTICOLUMN sits on the fieldset, not on the panel. On the panel it went
   nowhere: its only content child IS the fieldset, and a block that may not
   break stays in column one — eleven checkboxes under one another despite three
   declared columns. Column WIDTH rather than a column count and no media query:
   the panel sits in a half-width hero cell on one page and over the full field
   width on the other, so the space it actually has decides. */
.search-sources__group { margin:0; columns:14rem; column-gap:var(--sp-4); }
.search-sources__group .filter-group__legend { column-span:all; margin-bottom:var(--sp-2); }
.search-sources__panel .filter-check { break-inside:avoid; }
/* ONE divider, not two. The buttons hang off the list they act on without a
   rule; the rule below separates the second group. Their scope is therefore
   visible instead of needing to be explained. */
.search-sources__actions { display:flex; flex-wrap:wrap; gap:var(--sp-2);
  align-items:center; justify-content:flex-end; margin-top:var(--sp-2); }
/* The jumps are text links, not surfaces: without this they would carry CD's
   button height and the band would be twice as tall as the line inside it. */
.search-sources__actions .btn { min-height:0; padding-block:var(--sp-1); }
.search-sources__extra { margin:var(--sp-3) 0 0; padding-top:var(--sp-3);
  border-top:var(--stroke-1) solid var(--color-secondary-200); }

/* ====================== Answer block (js/search/search-ui.js) ================= */
/* The block owns its spacing — the call site does not (css/README.md).
   The doubled selector is deliberate: `.search-results .notification` above
   gives every notification in this block CD's 4rem stand-off, which is right for
   the contact note trailing the no-results tips and too large here. The answer
   is the FIRST thing in the results section, so it uses the same compact start
   offset as the catalogue bar rather than the generic notification spacing. */
.search-results .notification.answer-slot { margin-top:var(--sp-5); margin-bottom:var(--sp-6); }
@media (min-width:1024px){
  .search-results .notification.answer-slot { margin-bottom:var(--sp-8); }
}
/* AI answers carry the hint variant's quiet text semantics, but they are a
   content surface rather than another tinted status band. The white surface
   retains the notification's CD shadow, radius and padding; a border would
   make it read like another input or filter panel. Keep this doubled class
   scoped: `.notification--warning.answer-slot` is a real warning and must
   retain its semantic surface. */
.notification--hint.answer-slot {
  background:var(--color-bg);
}
/* NO speech-bubble glyph on a phone. The icon column costs 64px — a 40px glyph,
   CD's 12px `margin-right` and the notification's own 12px gap — out of the
   358px a 390px screen leaves the block, so nearly a fifth of the measure went
   to decoration. It is exactly that: `aria-hidden`, and the block already says
   «KI-Antwort» in words, so nothing is lost by dropping it where the space is
   scarce. It returns at 640, where the column costs a sixth as much of the line.
   Deliberately every HINT answer-slot and not the dashboard variant alone: the
   idle block and the answer that replaces it are the same component at two
   moments, and an icon that appeared in one and not the other would read as a
   rendering fault. `.notification--warning.answer-slot` is excluded for the
   same reason it keeps its own surface a few lines above — there the glyph is
   the warning, not decoration around it. */
@media (max-width:639.98px){
  .notification--hint.answer-slot > .notification__icon { display:none; }
}
.answer__head { display:flex; align-items:center; gap:var(--sp-3);
  flex-wrap:wrap; margin:0 0 var(--sp-2); }
.answer__title { font-weight:var(--fw-bold); }
/* Numeric citation. Inherits currentColor inside .notification (CD rule). */
.cite { font-size:var(--text-xs); font-weight:var(--fw-bold);
  padding:0 var(--sp-1); margin-left:var(--sp-0-5);
  border:var(--stroke-1) solid currentColor; border-radius:var(--radius-sm);
  text-decoration:none; vertical-align:.12em; }
.answer__sources { margin-top:var(--sp-4); padding-top:var(--sp-3);
  border-top:var(--stroke-1) solid var(--color-secondary-200); }
.answer__sources-label { font-size:var(--text-xs); color:var(--color-text-muted);
  margin:0 0 var(--sp-2); }
.source { display:grid; grid-template-columns:var(--sp-6) 1fr; gap:var(--sp-2);
  font-size:var(--text-sm); align-items:baseline; margin-bottom:var(--sp-3); }
.source:last-child { margin-bottom:0; }
.source__n { color:var(--color-text-muted); font-size:var(--text-xs); }
.source .meta-info { line-height:var(--lh-normal); }
.answer__foot { display:flex; flex-wrap:wrap; gap:var(--sp-2) var(--sp-4);
  align-items:center; margin-top:var(--sp-3); }
.answer__foot .legend { padding-top:0; flex:1 1 20rem; color:currentColor; }
/* The off switch sits ON the block: whoever sees an answer is the only person
   who can decide whether they want it. */
.answer__off { background:none; border:0; padding:var(--sp-1) 0;
  font:inherit; font-size:var(--text-xs); color:currentColor;
  text-decoration:underline; text-underline-offset:var(--stroke-2); cursor:pointer; }
/* Idle state: the same block, quieter. No change of frame and no other colour —
   it is the same component at an earlier moment, not a second one. */
.answer-slot--idle .muted { margin:0; font-size:var(--text-sm); }
.answer__examples { display:flex; flex-wrap:wrap; gap:var(--sp-1) var(--sp-4);
  margin:var(--sp-3) 0 0; font-size:var(--text-sm); }
/* The skill label beside an example. It is the only thing in the idle block
   that says a question can come back as something other than a paragraph, so it
   sits ON the link rather than in a legend nobody reads. */
.answer__example-skill { margin-left:var(--sp-2); padding:0 var(--sp-1-5);
  border:var(--stroke-1) solid var(--color-secondary-300); border-radius:var(--radius-sm);
  font-size:var(--text-xs); color:var(--color-text-muted); text-decoration:none;
  vertical-align:.08em; white-space:nowrap; }

/* ── Skill results (js/search/insight-view.js) ─────────────────────────────
   The block borrows the data portal's tiles, chart cards and map wholesale
   (css/apps/dataportal.css, loaded on demand). What is defined here is only
   what the SEARCH context adds: the tool trace, the lead, the provenance line
   and the spacing that keeps a dashboard from bursting the notification it
   sits in. */
/* ONE RHYTHM FOR THE WHOLE BLOCK. The parts arrive from three stylesheets —
   `.kpi-row` brings 24px from dataportal.css, `.chart` brings its own card
   padding, the prose here brought 12-16px — so stacked they produced four
   different distances in one block. The container owns the gap and every child's
   own margin is switched off; `--answer-gap` is the single value to change.
   Scoped by `.answer-slot--insight` so it outranks `.kpi-row`'s own margin
   without touching the data portal. */
.answer__insight { --answer-gap:var(--sp-6);
  display:flex; flex-direction:column; gap:var(--answer-gap); }
.answer-slot--insight .answer__insight > * { margin:0; }

/* The trace and the sentence under it are one unit, so they sit closer to each
   other than the gap above. */
.answer__intro { display:flex; flex-direction:column; gap:var(--sp-1); }
.answer__skill { display:flex; align-items:flex-start; gap:var(--sp-2);
  margin:0; font-size:var(--text-sm); color:var(--color-text-muted); }
.answer__skill-icon { flex:none; width:1.125rem; height:1.125rem; margin-top:.15em;
  color:var(--color-primary-600); }
.answer__skill-name { font-weight:var(--fw-bold); color:var(--color-text); }
/* An em dash rather than a middle dot: the two halves are a name and a
   description, not two items of a list. */
.answer__skill-detail::before { content:" — "; }
.answer-slot--insight .answer__lead { margin:0; }

/* `.notification__content` is shrink-to-fit (css/components/feedback.css) —
   right for a one-line status, wrong for a dashboard: measured, a three-tile
   answer stopped 400 px short of the block's own right edge while the block
   stayed full width, so the white surface ran on past the content. Scoped to
   this variant, because the plain notification's narrower measure is correct
   for prose. */
.answer-slot--insight .notification__content { flex:1 1 auto; }

/* The chart grid uses the SAME gap as the column it sits in, so the distance
   between two cards side by side and between a card and the tiles above is one
   number rather than two. `align-items:start` stops a short card from being
   stretched to its taller neighbour and ending in a panel of empty white. */
/* Scoped by the block, because `.dash-grid` widens its own gap to 32px from
   1280px up and would otherwise win on source order — leaving the one distance
   in the block that did not match the others. */
.answer-slot--insight .answer__charts,
.answer-slot--insight .answer__kpis { gap:var(--answer-gap); }
.answer__charts { align-items:start; }
/* NOTHING FLOATS INSIDE THE ANSWER BLOCK. The cards used to keep CD's card
   shadow, on the reasoning that `.chart` carries no border and no radius, so
   without it two diagrams on the block's white surface dissolved into one long
   strip of loose marks. That reasoning was about SEPARATION, and a border does
   that job — while the shadow was the wrong instrument for it here: the block
   IS a notification and carries a shadow of its own, so every card inside it
   produced a drop shadow within a drop shadow (user finding, 2026-08). The
   border is the same one the dashboard layout gives these cards, and it is the
   treatment `.kpi` already gets below; the map is a `.chart` card too
   (js/search/insight-view.js), so it is covered by the same rule rather than a
   second one beside it. */
.answer-slot--insight .chart { box-shadow:none;
  border:var(--stroke-1) solid var(--panel-border); }
.answer-slot--insight .dash-map { height:clamp(14rem, 42vw, 22rem); }

/* Inside the answer block the tiles sit on the same white surface as the block
   itself, so the card shadow would float them; the border alone separates
   them. */
.answer-slot--insight .kpi { box-shadow:none; background:var(--color-secondary-50); }

/* Collapsed by default — the table repeats the chart. The summary is a control,
   so it keeps a focus ring and a hit area rather than reading as a caption. */
.answer__data-summary { cursor:pointer; font-size:var(--text-sm);
  padding:var(--sp-2) 0; color:var(--color-primary-600); }
.answer__data-summary:hover { text-decoration:underline; }
.answer__data[open] .answer__data-summary { margin-bottom:var(--sp-2); }

/* Where the numbers came from. Deliberately the same size and colour as the
   disclaimer in the foot: both are statements about the answer, not part of it. */
.answer__basis { font-size:var(--text-xs); color:var(--color-text-muted); }



/* ── CD RESULTS CHROME (news + search; alignment D46/D47) ───────────────────
   Verified against the design-system source: the filter drawer is
   search.postcss:249-269 (md:2/lg:3 column grid, gap 12→16; the CD's
   toggle-row/[hidden] machinery is deliberately not used — see below) and the
   results header is search.postcss:204-234 (flex wrap gap-16, text--sm,
   pt 16/24@640/32@1544 + pb 8/12/16, secondary-300 bottom rule, right cluster
   with a right-aligned bare select). The tenant portal renders the same
   classes. */
.search__filters { margin-top:var(--sp-4); }
/* No `__actions` toggle row and no [hidden]/border-top drawer states: the CD
   collapses the drawer behind a «Filter anzeigen» button (newsList.vue:60-88);
   here the filters are ALWAYS visible (user decision, 2026-08 — kept-deviation
   under D46; the tenant portal matches). The field grid itself is unchanged. */
.search__filters__drawer { display:grid; gap:var(--sp-3);
  padding:var(--sp-2) 0; }
@media (min-width:640px){ .search__filters__drawer { padding:var(--sp-3) 0; } }
@media (min-width:768px){ .search__filters__drawer { grid-template-columns:repeat(2,minmax(0,1fr)); } }
@media (min-width:1024px){ .search__filters__drawer { grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--sp-4); } }
@media (min-width:1544px){ .search__filters__drawer { padding:var(--sp-4) 0; } }
/* The apply action sits at the end of the field grid, bottom-aligned with the
   fields (a submit is part of the drawer: filters apply on request, not on
   every keystroke — and Enter in any field submits). */
.search__filters__apply { display:flex; align-items:flex-end; }
.search__filters__tags { display:flex; flex-wrap:wrap; align-items:center; padding-top:var(--sp-4); }
@media (min-width:640px){ .search__filters__tags { padding-top:var(--sp-6); } }
@media (min-width:1544px){ .search__filters__tags { padding-top:var(--sp-8); } }
.search__filters__tags .tag-item { text-decoration:none; color:inherit; }

.search-results__header { display:flex; flex-wrap:wrap; gap:var(--sp-4); align-items:center;
  justify-content:space-between; font-size:var(--text-sm);
  padding:var(--sp-4) 0 var(--sp-2); border-bottom:var(--stroke-1) solid var(--color-secondary-300); }
@media (min-width:640px){ .search-results__header { padding:var(--sp-6) 0 var(--sp-3); } }
@media (min-width:1544px){ .search-results__header { padding:var(--sp-8) 0 var(--sp-4); } }
.search-results__header__left { display:flex; align-items:center; gap:var(--sp-3); }
.search-results__occurences { display:flex; align-items:center; }
.search-results__header__right { display:flex; align-items:center; gap:var(--sp-2); margin-left:auto; }
.search-results__header__right select { text-align:right; }
/* Display toggle — the portal-wide `.view-switch` control (components/
   content.css) since the 2026-08 view-toggle unification: one active style
   (tint + ink + bottom rail) everywhere. Only the phone behaviour is local:
   the results pages hide the whole switch below 640 — the list view works
   everywhere, the gallery is a preference (the tenant portal does the same). */
@media (max-width:639.98px){ .search-results__views { display:none; } }

/* List rows WITH a right-hand thumbnail (CD card--list image slot,
   card.postcss:352-373; hidden below md as in CD). object-cover at a fixed
   180×120 is the shared cross-portal recipe — both portals crop photographic
   thumbnails identically. */
.search-result__link--media { display:flex; align-items:flex-start; gap:var(--sp-6); }
.search-result__link--media .search-result__body { flex:1 1 auto; min-width:0; }
.search-result__image { flex:none; width:180px; height:120px; object-fit:cover; }
@media (max-width:767.98px){ .search-result__image { width:96px; height:72px; } }
@media (max-width:479.98px){ .search-result__image { display:none; } }
