/* ============================== LAYOUT ============================== */
/* Container side padding: its own ramp (--container-px), which converges with
   the column ramp from xs upward; only the base step follows CD at 1rem instead
   of 1.25rem (container.postcss:7 px-4 vs grids.postcss:10 gap-5). */
.container { width:100%; max-width:var(--container-max); margin:0 auto;
  padding:0 var(--container-px); overflow-x:clip; }
/* CD-neutral (container.postcss:23-25: flex + justify-between only). Shell
   contexts set alignment and gap themselves (top bar, header, breadcrumb), so
   the former align-items:stretch countermeasure in .desktop-menu disappears. */
.container--flex { display:flex; justify-content:space-between; }

/* CD 12-column container grid (container.postcss:53-146) */
.container--grid { display:grid; grid-template-columns:repeat(12,minmax(0,1fr)); }
.container__full, .container__center--xs, .container__center--sm, .container__center--md,
.container__main, .container__aside { grid-column:span 12; }
/* THE portal reading measure belongs to the COLUMN, never to text classes
   (user decision 2026-08-04, Design Review C25): .container__main measures
   60rem, the same width as .hero__content above it, so header and column end
   flush on detail pages. Below a ~1300px viewport the grid column is narrower
   anyway; the cap applies only on wide screens. Children (boxes, lists, prose)
   fill the measured column; the former individual 70ch caps on p/ul are gone.
   Data tables and tab panels are NOT in container__main and intentionally run
   full width. */
.container__main { max-width:60rem; }
@media (min-width:768px){
  .container__center--xs, .container__center--sm { grid-column:2 / span 10; }
  .container__main  { grid-column:span 7; }
  .container__aside { grid-column:8 / span 5; }
}
@media (min-width:1024px){
  .container__center--xs { grid-column:3 / span 8; }
  /* DELIBERATE DEVIATION FROM CD: CD uses `lg:col-span-6 lg:col-start-2`
     (container.postcss:94-98), so the main column starts in column 2. Those are
     article pages in CD, where indented body copy below a hero forms an
     intentional optical margin.
     Our detail pages are application pages: page header, tab bar and catalogue
     bar above all start in column 1. The indentation read as misalignment, not
     as a margin; content looked shifted from everything above. The main column
     therefore starts flush left and takes the released column with it.
     SECOND DEVIATION: it also takes the eighth column. In CD, one entire empty
     grid column separates main and aside (main 2-7, aside 9-12). Once main
     started flush left, this left a dead zone around 150px wide in the center;
     tables wrapped there or ran off the right while space stood unused beside
     them. The columns now meet, with the same gap as every other grid column
     (the call sites' `gap--responsive`). */
  .container__main  { grid-column:1 / span 8; }
  .container__aside { grid-column:9 / span 4; }
}
@media (min-width:1280px){
  .container__center--xs { grid-column:4 / span 6; }
  .container__center--sm { grid-column:3 / span 8; }
  .container__center--md { grid-column:2 / span 10; }
}
.container__aside > * { margin-bottom:var(--sp-7); }
@media (min-width:1024px){ .container__aside > * { margin-bottom:var(--sp-8); } }

/* The `container__center--*` classes are pure `grid-column` rules and do
   NOTHING outside `.container--grid`, an omission invisible in code review
   (measured: p.container__center--xs at 148ch on 1440). A simple `max-width`
   fallback makes the oversight impossible; in a grid, the column rule takes
   over again. Call sites: Item 7.2. */
.container__center--xs { max-width:46rem; }
.container__center--sm { max-width:60rem; }
.container__center--md { max-width:76rem; }
.container--grid > .container__center--xs,
.container--grid > .container__center--sm,
.container--grid > .container__center--md { max-width:none; }

/* CD container.postcss:100-101 / :135-141 (detailPageAnchorNav.vue:48): on
   mobile, the table of contents comes BEFORE the content it indexes.
   The directory itself remains fully visible at every width. */
.container--reverse-mobile .container__main  { order:2; }
.container--reverse-mobile .container__aside { order:1; }
@media (min-width:768px){
  .container--reverse-mobile .container__main  { order:1; }
  .container--reverse-mobile .container__aside { order:2; }
}

/* Spacing utilities (CD grids.postcss:9-19, :222-224, :56-58 and
   container.postcss:29-49). `gap--responsive` was narrowed here to
   `.container--grid` AND used a shorter ramp than CD, even though `.grid`
   below carries the same CD ramp correctly. Both now use one source. */
.gap--responsive { gap:var(--gap-responsive); }
.gap--top    { padding-top:var(--gap-responsive); }
.gap--bottom { padding-bottom:var(--gap-responsive); }
.grid + .grid { margin-top:var(--gap-responsive); }
.container--grid + .container--grid { padding-top:var(--gap-responsive); }   /* CD container.postcss:56-58 `& + &` = gap--top ramp, not a literal */

.container--py      { padding-block:var(--section-py); }
.container--pt      { padding-block:var(--section-py) 0; }
.container--pb      { padding-block:0 var(--section-py); }
.container--py-half { padding-block:var(--section-py-half); }
/* Sticky anchor navigation (sticky.postcss: top-4 md:top-10 lg:top-12). */
.sticky--top { position:sticky; top:var(--sp-4); }
@media (min-width:768px){ .sticky--top { top:var(--sp-10); } }
@media (min-width:1024px){ .sticky--top { top:var(--sp-12); } }
/* Table of contents on the right: beside content from lg upward, normal below. */
.anchor-nav .card__title { font-size:var(--text-lg); }
.anchor-nav .menu { margin-top:var(--sp-2); }
/* Table-of-contents section titles include long German compound UI terms. At
   200% text zoom, the line escaped the card and was clipped by its
   `overflow:hidden` (WCAG 1.4.4). */
.anchor-nav .menu__item { overflow-wrap:anywhere; }
/* Anchor-navigation page (CD detailPageAnchorNav): the header spans the full
   grid width. The former lg indentation (column 2/12) targeted CD's
   col-start-2 main column, which this sheet DELIBERATELY places in column 1;
   the header must share that alignment or it appears offset. */
.anchor-page__header { grid-column:1 / -1; }
/* Clear topic sections (bk.admin.ch guidance): heading + visible list; spacing
   between sections comes from .vertical-spacing. */
.anchor-section__title { margin-bottom:var(--sp-4); }

/* Section rhythm (section.postcss:5-51) — bare .section has no padding */
.section { width:100%; }

/* Page root: every page renders <div class="container section">. In CD, a bare
   .section intentionally has no spacing; a hero or .section--default supplies
   it. Because container and section are the same element here, this combination
   receives the values directly.
   SYMMETRIC 56/80/128 — CD gives content after the breadcrumb the FULL
   `pt-14 lg:pt-20 3xl:pt-32` (container.postcss:19-21); only `.hero` itself
   is tighter (pt-12 lg:pt-14 3xl:pt-20). The former asymmetric top
   (--section-pt) applied the hero ramp to every page and sat 24px above the
   tenant portal's page starts at ≥1024 (2026-08 alignment D21). Hero pages
   keep the hero ramp via the negative margin below. */
.container.section { padding-top:var(--section-py); padding-bottom:var(--section-py); }
/* At the page start, including directly below the back button, the hero no
   longer needs its own top spacing; the page root already supplies it — and
   the difference between the page ramp (56/80/128) and the hero ramp
   (48/56/80) is taken back so a hero page still starts at CD's hero top. */
.container.section > .hero:first-child,
.container.section > .back-link-row:first-child + .hero {
  padding-top:0;
  margin-top:calc(var(--section-pt) - var(--section-py));
}

.section--default, .section--py, .section[class*="bg--"] { padding-block:var(--section-py); }
.section--py-half { padding-block:var(--section-py-half); }
/* CD pair list (section.postcss:31-51), LITERAL — the second section of a pair
   drops its top spacing.
   The `:not([class*="bg--"])` guards that used to sit on these selectors are
   gone. They were added to read the rule as «same BACKGROUND collapses», which
   is what CD's comment says — but not what CD's code does. CD matches on
   CLASSES, and the reference implementation depends on the difference:
     · www.bbl.admin.ch/de          tinted bands are `.section.bg--secondary-50`
                                    WITHOUT `section--default`, so no pair
                                    matches and both neighbours keep 80 px;
     · www.bbl.admin.ch/de/search   the tinted band carries
                                    `.section.section--default.bg--secondary-50`,
                                    so `.section--default + .section--default`
                                    DOES match and the results section measures
                                    `padding-top: 0` (verified at 1440 px).
   The band therefore declares whether it forms a unit with what follows, by
   carrying `section--default` or not. `C.pageSection({ collapseNext })` sets it;
   the guards made that declaration unreachable and left the search results with
   a full 80 px gap where the reference has none. */
.section--default + .section--default,
.section--default + .section--py,
.section--py + .section--default,
.section--default + .section--py-half,
.section--py-half + .section--default,
.section--default + .bg--white,
.bg--white + .section--default,
.bg--white + .bg--white,
.bg--secondary-50 + .bg--secondary-50,
.bg--secondary-100 + .bg--secondary-100,
.bg--secondary-200 + .bg--secondary-200,
.bg--secondary-300 + .bg--secondary-300,
.bg--secondary-400 + .bg--secondary-400,
.bg--secondary-500 + .bg--secondary-500,
.bg--secondary-600 + .bg--secondary-600,
.bg--secondary-700 + .bg--secondary-700,
.bg--secondary-800 + .bg--secondary-800,
.bg--secondary-900 + .bg--secondary-900,
.hero + .section--default,
.hero + .section--py,
.hero + .bg--white { padding-top:0; }

/* The fixed strip retains exactly its measured height in document flow. This
   lets the footer, card bottoms and viewer scroll fully above it. */
body.body--banner-visible { padding-bottom:var(--banner-offset, 0px); }
.body--banner-visible .back-to-top-btn { bottom:calc(var(--sp-6) + var(--banner-offset, 0px)); }
/* CD declares `.notification-banner__wrapper { @apply container }`
   (notification-banner.postcss:20), so the strip aligns its content to the same
   edge as every other page element. This used fixed `padding:0 1rem` instead:
   on wide windows text hugged the window edge while 2.5rem of space stood above
   and below, and it did not align with the page text edge. It now uses the same
   ramp as .container. */
.notification-banner__wrapper { width:100%; max-width:var(--container-max); margin:0 auto;
  padding:0 var(--container-px);
  /* 16px between the copy and the action while the wrapper is a column —
     the tenant portal's banner gap; without it the button sat flush under
     the text (user finding 2026-08-22; alignment D42). */
  gap:var(--sp-4);
  display:flex; flex-direction:column; align-items:flex-start; }
@media (min-width:1024px){
  .notification-banner__wrapper { flex-direction:row; flex-wrap:nowrap; align-items:flex-start;
    justify-content:space-between; }
}
.notification-banner__infos { margin:0; overflow-wrap:break-word; }
/* CD notification-banner.postcss:24-27: the BUTTON carries spacing (mt-4
   sm:mt-8 lg:mt-0 lg:ml-6), not a wrapper gap; both together accumulated.
   No nowrap on .btn__text: CD button text may wrap anywhere
   (btn.postcss:133-137), while the column/row anatomy already keeps the button
   on its own line or on the right. */
.notification-banner .btn { flex:none; margin-top:var(--sp-4); }
@media (min-width:640px){ .notification-banner .btn { margin-top:var(--sp-8); } }
@media (min-width:1024px){ .notification-banner .btn { margin-top:0; margin-left:var(--sp-6); } }

/* Backgrounds (backgrounds.postcss:5-38) */
.bg--white { background:var(--color-bg); }
.bg--secondary-50 { background:var(--color-secondary-50); }
.bg--secondary-100{ background:var(--color-secondary-100); }
.bg--secondary-200{ background:var(--color-secondary-200); }
.bg--secondary-300{ background:var(--color-secondary-300); }
/* DELIBERATE DEVIATION (Review 2026-07, layout/bg-1): CD's negative threshold
   begins at 500 (section.postcss:58-70); CD keeps dark ink on 400, but measured
   contrast fails AA: text-800 on #596978 = 2.6:1, on intranet #5076b3 = 3.2:1.
   White measures 5.65:1 (default) and 4.58:1 (intranet), so negative begins at 400. */
.bg--secondary-400{ background:var(--color-secondary-400); color:var(--color-text-negative); }
.bg--secondary-500{ background:var(--color-secondary-500); color:var(--color-text-negative); }
.bg--secondary-600{ background:var(--color-secondary-600); color:var(--color-text-negative); }
.bg--secondary-700{ background:var(--color-secondary-700); color:var(--color-text-negative); }
.bg--secondary-800{ background:var(--color-secondary-800); color:var(--color-text-negative); }
.bg--secondary-900{ background:var(--color-secondary-900); color:var(--color-text-negative); }

.vertical-spacing > * { margin-top:var(--stack-gap); }
.vertical-spacing > *:first-child { margin-top:0; }
/* The 3.5rem step from 1544px comes from --stack-gap (tokens.css) */
/* CD spacings.postcss:17-41: the INTRA-block rules were missing. Without them,
   3rem separates an <h2> from its own first paragraph, so the class previously
   worked only where every child is a <section> wrapper. Adoption: Item 7.5. */
.vertical-spacing > :is(h1,h2,h3,h4,h5) { margin-bottom:0; }
.vertical-spacing > :is(h1,h2,h3,h4,h5) + * { margin-top:1.5em; }
.vertical-spacing > :is(h1,h2,h3,h4,h5) + p { margin-top:1em; }
.vertical-spacing > p + p, .vertical-spacing > p + ul, .vertical-spacing > p + ol,
.vertical-spacing > ul + p, .vertical-spacing > ol + p { margin-top:1em; }
.stack > * + * { margin-top:var(--sp-4); }
/* .muted is DELIBERATELY darker than CD's .text--light (text-600 instead of
   text-500): text-500 would fall below AA at ~4.1:1 on secondary-50 surfaces.
   .text--light itself uses the CD value in the CD utilities above. */
.muted { color:var(--color-text-muted); }
/* Identifiers, paths and schema names use one default monospace recipe. Property
   grids intentionally override it so all values share the grid's typography. */
code { font-family:var(--font-mono); }
.text-center { text-align:center; }
.text-right { text-align:right; }
.text-left { text-align:left; }

/* Grids — mobile-first, CD breakpoints and responsive gaps (grids.postcss:10,25-56) */
.grid { display:grid; gap:var(--gap-responsive); }
/* Grid tracks already use minmax(0,1fr), but their children otherwise retain
   the min-content floor. Let long identifiers and locally scrollable tables
   shrink with the track instead of widening a 320px page. */
:where(.grid, .container--grid) > * { min-width:0; }

/* A list header that carries its own primary action: title and lead on the
   left, the action on the right, wrapping under the text when the row runs out
   of width. `align-items:flex-start` keeps the button on the title's line
   rather than centred against a two-line lead. The inner `.page-header` keeps
   its own margins, so a header WITHOUT an action is laid out exactly as before.
   Shared with the tenant portal's inbox header (docs/case-view-alignment.md L1). */
.page-head-row { display:flex; flex-wrap:wrap; align-items:flex-start;
  justify-content:space-between; gap:var(--sp-4); }
.page-head-row > .page-header { flex:1 1 24rem; min-width:0; }
.page-head-row__actions { display:flex; flex-wrap:wrap; gap:var(--sp-3); padding-top:var(--sp-2); }
