/* Cards (card.postcss:5-248) — the base carries no shadow */
/* NO overflow:hidden (card.postcss:5-8): only .card__image clips its media;
   line-clamp clips text. Clipping the card itself forced focus rings 2px inward
   and cut off pills at 200% zoom. */
.card { background:var(--surface-bg); border:0; border-radius:var(--radius-none); box-shadow:var(--shadow-none);
  display:flex; flex-direction:column; height:100%; position:relative; container-type:inline-size; }
/* Deliberate CD surface tier: card.postcss keeps default/universal cards
   square and borderless, one elevation step above tables at shadow-lg. */
.card--default, .card--universal { box-shadow:var(--shadow-lg); }
/* card--universal: image is fitted (object-contain), not cropped (CD card.postcss). */
.card--universal .card__image > img, .card--universal .card__image > .photo { object-fit:contain; }
/* card.postcss:165: CD creates 16:9 through `pb-[56.25%]`, not aspect-ratio.
   That matters here: as a flex item in the column, an aspect-ratio box shrinks
   once `.card__content { height:100% }` demands the full card height. The image
   then lost its proportion and the difference became empty space above the
   footer. With padding-bottom the box has zero height and cannot be compressed. */
.card__image { position:relative; height:0; padding-bottom:56.25%; flex:none;
  background:var(--color-secondary-50); overflow:hidden; }
/* Placeholder frame for an empty/loading image box (card.postcss:173-179:
   bg-secondary-50 + 2px white inner border at 70% opacity). A loaded image
   paints over it, so the frame is visible only without an image. */
.card__image::before { content:""; position:absolute; inset:0;
  background:var(--color-secondary-50); border:var(--stroke-2) solid var(--color-bg); opacity:.7; }
.card__image > img, .card__image > .photo {
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
/* 2:1 instead of 16:9 in a two-column grid: CD's card contract
   (card.postcss:169-171 `.grid--responsive-cols-2 & { pb-[50%] }`). */
.grid--responsive-cols-2 .card__image { padding-bottom:50%; }

.card__content { display:flex; flex-direction:column; justify-content:space-between; height:100%; background:transparent; }
/* card.postcss:210: px-6 py-10, space-y-4. No flex:1 on body or description: it
   made the text box absorb the grid row's remaining-height adjustment instead
   of leaving it as a gap above the footer. */
.card__body { padding:var(--sp-10) var(--sp-6); display:flex; flex-direction:column; gap:var(--sp-4); }
.card__title { margin:0; font-size:var(--text-lg); font-weight:var(--fw-bold); line-height:1.375; transition:color var(--duration) var(--ease-in-out); overflow-wrap:anywhere; }
.card__description { color:var(--color-text-muted); overflow-wrap:anywhere; }
/* card.postcss:226-234: long descriptions are clamped so one card cannot blow
   out the row height. */
.card__description, .card__body p { display:-webkit-box; -webkit-box-orient:vertical;
  -webkit-line-clamp:6; overflow:hidden; }
@container (min-width:500px){
  .card__description, .card__body p { -webkit-line-clamp:4; }
}
/* Extension beyond CD: in a three-column grid a card is ~400px wide; py-10
   (2×40px) is 14% of its height. CD itself uses 500px as the threshold for a
   "narrow card" (card.postcss:230). */
@container (max-width:499px){
  .card__body { padding:var(--sp-6); }
  .card__footer { padding:0 var(--sp-6) var(--sp-6); }
}
.card__footer { padding:0 var(--sp-6) var(--sp-6); border-top:0;
  display:flex; align-items:center; justify-content:space-between; gap:var(--sp-2); font-size:var(--text-sm); color:var(--color-text-muted); }
/* card.postcss:250-257: without an info slot, the action moves right. */
.card__footer--icon-only { justify-content:flex-end; }
.card__footer__info { min-width:0; padding-right:var(--sp-6); color:var(--color-secondary-500); }
.card__footer__action { flex:none; }
/* The arrow in the card footer is decorative (the card link carries the action).
   NO hover inversion: CD card hover is shadow + title color + edge overlay
   (card.postcss:27-40); the button keeps its resting state. */
.card__footer__action > .btn { pointer-events:none; }
.card--clickable { cursor:pointer; transition:box-shadow var(--duration-slow) var(--ease-in-out); text-decoration:none; color:inherit; }
.card--clickable::after { content:""; position:absolute; inset:0; border:var(--stroke-2) solid transparent; transition:border-color var(--duration-slow) var(--ease-in-out); pointer-events:none; }
/* Stretched link: the title <a> covers the whole clickable card without making
   the card itself a link, preserving document outline + valid nesting. */
.card--clickable .card__link { text-decoration:none; color:inherit; }
.card--clickable .card__link::after { content:""; position:absolute; inset:0; z-index:var(--z-local-content); }
.card--clickable .card__link:focus-visible { outline:none; }
.card--clickable .card__link:focus-visible::after { outline:var(--stroke-2) solid var(--color-focus-ring); outline-offset:var(--stroke-2); }   /* Standard offset; the card no longer clips */
.card--clickable:not(:where(.card--disabled,.card--loading,[aria-disabled="true"],[aria-busy="true"])):where(:hover,:focus-within) {
  box-shadow:var(--shadow-2xl); text-decoration:none; }
/* NO edge ring on hover (user decision, 2026-08-12). CD does draw one —
   card.postcss:20-40 puts `border-2 border-text-50 border-opacity-0` on the
   card's link overlay and raises it to `border-opacity-90` on hover, so a
   near-white (#f9fafb) edge at 90%. That reads as a highlight only because every
   CD card sits on `bg-white` (card.postcss:5-9): white-on-white is a sheen.
   Half the card surfaces here sit on the tinted band instead (#f0f4f7, the
   alternating home-page sections), and there the same edge reads as a stray
   white border — which is how it was reported. A ground-dependent effect cannot
   be made correct by choosing a third colour, so the hover state keeps the two
   signals CD gives it that DO work on any ground: shadow-2xl and the title
   colour. The ::after box stays for :active below. */
.card--clickable:not(:where(.card--disabled,.card--loading,[aria-disabled="true"],[aria-busy="true"])):where(:hover,:focus-within) .card__title {
  color:var(--color-primary-700); }
.card--clickable:not(:where(.card--disabled,.card--loading,[aria-disabled="true"],[aria-busy="true"])):active {
  box-shadow:var(--shadow-md); }
.card--clickable:not(:where(.card--disabled,.card--loading,[aria-disabled="true"],[aria-busy="true"])):active::after {
  border-color:var(--color-primary-100); }

/* CD highlight card (card.postcss:43-80). The offset secondary panel identifies
   curated hub navigation; the white content remains the reading surface. */
.card--highlight { position:relative; padding-top:var(--sp-5); padding-left:var(--sp-5);
  background:transparent; box-shadow:none; }
.card--highlight::before { content:""; position:absolute; inset:0 var(--sp-5) var(--sp-5) 0;
  z-index:var(--z-local-base); background:var(--color-secondary-300);
  transition:transform var(--duration-slow) var(--ease-in-out); }
.card--highlight .card__content { position:relative; z-index:var(--z-local-content);
  gap:var(--sp-5); background:var(--color-bg); }
.card--highlight .card__body { padding-top:var(--sp-8); padding-bottom:0; }
.card--highlight.card--clickable:where(:hover,:focus-within) { background:transparent; box-shadow:none; }
.card--highlight.card--clickable:where(:hover,:focus-within)::before { transform:scale(var(--scale-hover)); }
@media (min-width:1024px){
  .card--highlight { padding-top:var(--sp-7); padding-left:var(--sp-7); }
  .card--highlight::before { right:var(--sp-7); bottom:var(--sp-7); }
}

/* State contract. Cards remain structurally identical; callers expose status
   through ARIA where one exists and may use the BEM aliases while rendering a
   skeleton, validation failure, or empty result. Disabled cards must omit the
   title link's href so the visual state cannot leave a keyboard-active target. */
.card:where(.card--empty,[data-empty="true"]) { background:var(--color-bg-subtle); }
.card:where(.card--error,[aria-invalid="true"]) {
  outline:var(--stroke-2) solid var(--color-error); outline-offset:var(--stroke-2); }
.card:where(.card--loading,[aria-busy="true"]) {
  cursor:progress; box-shadow:var(--state-ring-primary-soft); }
.card:where(.card--disabled,[aria-disabled="true"]) {
  color:var(--color-text-muted); cursor:not-allowed; box-shadow:var(--shadow-none); }
.card:where(.card--disabled,[aria-disabled="true"]) .card__link { pointer-events:none; text-decoration:none; }
/* flat card (card.postcss:109-141) — replaces the old .tile */
/* .card--flat/.card--list (card.postcss:331-400) were EXCISED: zero consumers
   across two waves; the announced adoption (Item 5.10) never happened. The
   .catalogue-grid below handles mobile result-list density, while .search-result
   carries its own padding ramp (Design Review C10). */

/* Catalogue result grid: below md, use the same density as CD's card--list
   without touching card markup (the `.card` class remains everywhere). */
@media (max-width:767.98px){
  .catalogue-grid { gap:0; }
  .catalogue-grid .card__image { display:none; }
  .catalogue-grid .card--default { box-shadow:var(--shadow-none);
    border-bottom:var(--stroke-1) solid var(--color-secondary-200); }
  .catalogue-grid .card__body   { padding:var(--sp-4) var(--sp-1); }
  .catalogue-grid .card__footer { padding:0 var(--sp-1) var(--sp-4); }
}

/* Placeholder photography (C.photo) */
.photo { position:relative; overflow:hidden; }
.photo > img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; display:block; }
.photo > :not(img) { position:relative; z-index:var(--z-local-content); }
/* (Dead rules removed in the 2026-08 code review, F-S30 — no emitter in any template.) */
.photo--16x9 { aspect-ratio:16/9; } .photo--4x3 { aspect-ratio:4/3; }
.photo--21x9 { aspect-ratio:21/9; max-height:22rem; }
/* SQUARE — CD article media carries no radius anywhere (card.postcss,
   hero.postcss); the 5px was this photo's lone rounding (alignment D46). */
.news-detail__photo { aspect-ratio:21/9; max-height:20rem; }
/* No image zoom on card hover: CD defines no transform on card images (the only
   scale in the system is card--highlight's background layer, scale-102). */
/* image-not-available (image-not-available.postcss:5-17) */
.image__not-available { display:flex; flex-direction:column; align-items:center; justify-content:center;
  width:100%; height:100%; text-align:center; color:var(--color-text-muted); background:var(--color-secondary-50); }
.image__not-available .icon { width:var(--sp-12); height:var(--sp-12); }
.image__not-available-text { margin:var(--sp-2) 5px 0; font-size:var(--text-sm); }

/* Badges (badge.postcss:5-82): the base defines neither gap nor nowrap (CD
   badge.postcss:5-9: only inline-flex, items-center, padding, rounded-full), so
   pills may wrap everywhere, including at 200% text zoom (WCAG 1.4.4). */
.badge { display:inline-flex; align-items:center; padding:.219em 1em;
  border-radius:var(--radius-full); font-weight:var(--fw-regular);
  background:var(--color-secondary-100); color:var(--color-gray-800); }
/* Dynamic badge--${variant} producers can opt into the CD clickable badge.
   The portal raises its target to 44px and supplies the same state contract as
   other controls without changing each semantic colour variant. */
/* (Dead rules removed in the 2026-08 code review, F-S30 — no emitter in any template.) */
/* Card pills (.pill-row) must be allowed to wrap at 200% text zoom (WCAG 1.4.4).
   Measured on #/data/katalog: a long German compound topic badge grew to 439px
   inside a 347px card and was clipped by its `overflow:hidden`. Merely allowing
   wrapping cannot split one compound, hence `overflow-wrap`. At normal text
   size the pills fit, so the rule is invisible there. */
.pill-row .badge { overflow-wrap:anywhere; }
.badge, .badge--base { font-size:var(--fs-xs); line-height:var(--line-5); }
@media (min-width:768px) { .badge, .badge--base { font-size:var(--fs-sm); } }
@media (min-width:1024px){ .badge, .badge--base { font-size:var(--fs-base); line-height:var(--line-6); } }
.badge--sm { font-size:10px; line-height:var(--line-4); }
@media (min-width:768px) { .badge--sm { font-size:var(--fs-xs); line-height:1.35rem; } }
@media (min-width:1024px){ .badge--sm { font-size:var(--fs-sm); } }
/* CD icon anatomy (badge.postcss:84-97 + Badge.vue:11-18): the icon is pulled
   optically 0.4em into the 1em padding; that offset alone supplies the gap to
   the label (.badge__text), with no flex gap. CD's stroke-* does not apply;
   our icons are masks, not stroked SVGs. `align-self:stretch` + height:auto
   replaces CD's h-full: `height:100%` resolves to `auto` in an auto-height flex
   container and collapsed the mask span to 0 (measured). Stretching reliably
   produces the same full pill height. */
.badge__icon      { align-self:stretch; height:auto; width:1.5em; position:relative; left:.4em;  flex:none; }
.badge__icon-left { align-self:stretch; height:auto; width:1.5em; position:relative; right:.4em; flex:none; }
.badge--gray    { background:var(--color-secondary-100); color:var(--color-gray-800); }
.badge--success, .badge--green { background:var(--color-success-bg); color:var(--color-success-text); }
.badge--warning, .badge--orange { background:var(--color-warning-bg); color:var(--color-warning-text); }
.badge--error, .badge--red { background:var(--color-error-bg); color:var(--color-error-text); }
.badge--info, .badge--blue { background:var(--color-info-bg); color:var(--color-info-text); }
/* badge--yellow/indigo/purple/pink/teal/negative/disabled were excised: zero
   producers, including badge--${variant}. Accent tokens in tokens.css remain
   documented as CD canon (Design Review C9). */

/* Tag items — CD's filter control (tag-item.postcss:7-85) */
/* min-height through --control-h: the token carries CD's exact 44/48/52 button
   ramp (min-h-[44px] xl:48 3xl:52). Previously the same steps appeared here a
   second time as literals. */
.tag-item { display:inline-flex; align-items:center; margin-right:var(--sp-3);
  min-height:var(--control-h); font:inherit; font-size:var(--text-base); line-height:var(--line-5);
  background:none; border:0; padding:0; color:inherit; text-decoration:none; cursor:pointer; }
/* Margin ramp below the pill: CD tag-item.postcss:9 `mr-3 md:mb-0.5 lg:mb-1`;
   below 768 there is NO bottom margin. */
@media (min-width:768px){ .tag-item { margin-bottom:var(--sp-0-5); } }
@media (min-width:1024px){ .tag-item { line-height:var(--line-6); margin-bottom:var(--sp-1); } }
.tag-item:where(:hover,:active) { color:inherit; text-decoration:none; }
.tag-item--sm { font-size:var(--text-sm); }
/* The hover transition is DELIBERATE portal-wide polish (CD switches tag/badge
   hover instantly), made reduced-motion-safe through --duration tokens. */
.tag-item__inner { display:inline-flex; align-items:center; gap:var(--sp-2); padding-inline:var(--sp-4); line-height:1.25;
  border-radius:var(--radius-full); background:var(--color-gray-200); transition:background var(--duration-fast),color var(--duration-fast); }
@media (min-width:1544px){ .tag-item__inner { padding-inline:var(--sp-5); } }
.tag-item__text { display:inline-block; padding-block:var(--sp-1-5); text-align:left; }
@media (min-width:1280px){ .tag-item__text { padding-block:var(--sp-2); } }
.tag-item:not(:where(:disabled,[aria-disabled="true"],.tag-item--disabled,.tag-item--loading,[aria-busy="true"],.tag-item--error,[aria-invalid="true"])):hover .tag-item__inner {
  background:var(--color-gray-300); }
.tag-item:not(:where(:disabled,[aria-disabled="true"],.tag-item--disabled,.tag-item--loading,[aria-busy="true"],.tag-item--error,[aria-invalid="true"])):active .tag-item__inner,
.tag-item:where(.tag-item--active,[aria-current="true"]) .tag-item__inner {
  background:var(--color-gray-800); color:var(--color-text-negative); }
/* CD tag-item.postcss:61-67: the active pill is inert (the state is the action).
   Keyboard: each consumer's JS guard intercepts Enter on the active chip
   (tenancies [data-floor]). */
.tag-item:where(.tag-item--active,[aria-current="true"]) { pointer-events:none; }
.tag-item:focus-visible { outline:none; }
/* Offset 0: CD's ring sits AGAINST the pill (tag-item.postcss:17-19 ring-2 with
   no offset) and follows its rounding through outline-radius behavior. */
.tag-item:focus-visible .tag-item__inner { outline:var(--stroke-2) solid var(--color-focus-ring); outline-offset:0; }
/* The same semantic/BEM state vocabulary as buttons and cards. `empty` means
   an applicable filter currently has no value; it remains reachable so the
   user can change it. */
.tag-item:where(.tag-item--empty,[data-empty="true"]) .tag-item__inner {
  color:var(--color-text-muted); background:var(--color-secondary-50); }
.tag-item:where(.tag-item--error,[aria-invalid="true"]) .tag-item__inner {
  color:var(--color-error-text); background:var(--color-error-bg-subtle);
  outline:var(--stroke-1) solid var(--color-error); }
.tag-item:where(.tag-item--loading,[aria-busy="true"]) { cursor:progress; pointer-events:none; }
.tag-item:where(.tag-item--loading,[aria-busy="true"]) .tag-item__inner {
  box-shadow:var(--state-ring-primary-soft); }
.tag-item:where(:disabled,[aria-disabled="true"],.tag-item--disabled) {
  color:var(--color-secondary-300); cursor:not-allowed; pointer-events:none; }
.tag-item:where(:disabled,[aria-disabled="true"],.tag-item--disabled) .tag-item__inner {
  color:inherit; background:var(--color-secondary-50); }
/* CD tag-item.postcss:80-85: icon slot (e.g. cancel X on a removal pill).
   `align-self:stretch` + height:auto replaces CD's h-full: `height:100%`
   resolves to `auto` in an auto-height flex container and collapsed the mask
   span to 0; the removal pill's X was invisible (measured 27x0px). */
.tag-item__icon { align-self:stretch; height:auto; width:1.5em; position:relative; left:.4em; flex:none; }
@media (forced-colors: active) { .tag-item--active .tag-item__inner { outline:var(--stroke-2) solid CanvasText; outline-offset:calc(-1 * var(--stroke-3)); } }

/* Reusable media annotations and identifier line. They belong to the card
   block; portfolio's historic pf-card equivalents remain scoped to its custom
   media renderer. */
/* ONE inset for everything that sits ON card media: --sp-3, top and left, for
   the chip row here, the portfolio's own chip row (.pf-card__chips) and the
   «gemerkt» mark below. They appear on the same galleries and at different
   insets they read as three unrelated decisions rather than one rule (user
   decision, 2026-08-12). DELIBERATE DEVIATION: CD sets top-2/left-2 on this one
   (card.postcss:236-237) and has no equivalent for the other two, so following
   it here would mean keeping the inconsistency to match a value CD only ever
   applies to a third of the cases. */
.card__chips { position:absolute; top:var(--sp-3); left:var(--sp-3); right:var(--sp-3);
  display:flex; flex-wrap:wrap; gap:var(--sp-1); }
/* The «gemerkt» mark on the card picture (C.card `mark`). Same corner, same disc
   and same edge as the star over a detail hero (css/layouts/shell.css), one step
   smaller because it labels a tile rather than a page. `.card__image` is already
   the positioning context. It follows .card__chips in source order so that a
   card using both keeps the mark on top of a chip row that spans the full width. */
/* --sp-3, the shared overlay inset — see .card__chips above. */
.card__mark { position:absolute; top:var(--sp-3); left:var(--sp-3);
  display:flex; padding:var(--sp-1-5); border-radius:var(--radius-full);
  background:var(--color-bg); box-shadow:var(--shadow-overlay-control); }
/* The body twin, for the card shapes that have no corner to put a mark in.
   `--only` is the card WITHOUT a picture (a service card): there the pill row is
   the sole placement and is never hidden. The plain class is the fallback for a
   card that HAS a picture in a grid that drops it — .catalogue-grid does exactly
   that below 768px, three rules above; without this pairing the mark would
   disappear with the image it sits on. Exactly one copy is ever displayed, so
   exactly one is ever in the accessibility tree. */
.card__mark-inline { display:none; }
.card__mark-inline--only { display:inline-flex; }
@media (max-width:767.98px){
  .catalogue-grid .card__mark-inline { display:inline-flex; }
  .catalogue-grid .card__mark { display:none; }
}
/* `.card__chip` was excised with the change above: the chip row holds ordinary
   badges now, so the dark scrim capsule had no producer left. --scrim-chip
   itself stays — the portfolio mosaic still uses it for labels laid directly on
   a photograph, where there is no badge surface to sit on. */
.card__identifier { margin:var(--sp-1-5) 0 0; font-family:var(--font-mono);
  font-size:var(--text-sm); font-weight:var(--fw-regular); color:var(--color-text-muted);
  letter-spacing:.01em; }

/* ── QUICK CARD (home «Häufig gebraucht») ──────────────────────────────────
   The SHARED cross-portal quick-link card (2026-08 alignment D41): the CD has
   NO grey icon tile — .box is a stacked commerce summary (box.postcss:5-12,
   OrderBox/ShoppingCart), and bg-secondary-50 otherwise appears only on
   hovers, bands and placeholders. The CD teaser is the WHITE card:
   card--default shadow-lg lifting to shadow-2xl with the title turning
   primary-700 (card.postcss:11-41). Class names and geometry match the
   tenant portal's `card--quick` byte for byte, so the markup is portable;
   the arrow square speaks the DS back-to-top vocabulary (white ground,
   primary border + glyph, back-to-top-btn.postcss:79-88). The former
   `.quick-tile` stays for the my-cases bookmark band (icon tiles on a
   tinted band — a different surface with its own test contract). */
.card--quick { position:relative; display:flex; flex-direction:column;
  background:var(--color-bg); color:inherit; text-decoration:none;
  padding:var(--sp-8); padding-bottom:var(--sp-20);   /* text clear of the corner arrow */
  box-shadow:var(--shadow-lg); min-height:200px; cursor:pointer;
  transition:box-shadow var(--duration-slow) var(--ease-in-out); }
.card--quick:hover { box-shadow:var(--shadow-2xl); text-decoration:none; }
.card--quick__title { margin:0 0 var(--sp-4); font-size:var(--text-lg);
  font-weight:var(--fw-bold); color:var(--color-text); line-height:var(--lh-snug);
  overflow-wrap:anywhere; transition:color var(--duration); }
.card--quick:hover .card--quick__title { color:var(--color-primary-700); }
.card--quick__desc { margin:0; font-size:var(--text-sm); color:var(--color-text-muted);
  line-height:var(--lh-relaxed); overflow-wrap:anywhere; }
.arrow-btn { display:flex; align-items:center; justify-content:center;
  width:var(--target-min); height:var(--target-min);
  border:var(--stroke-1) solid var(--color-primary-600); border-radius:var(--radius);
  background:transparent; transition:background var(--duration-fast); }
.arrow-btn .icon { width:var(--sp-6); height:var(--sp-6); color:var(--color-primary-600);
  transition:color var(--duration-fast); }
.card--quick__arrow-btn { position:absolute; bottom:var(--sp-6); right:var(--sp-6); }
.card--quick:hover .arrow-btn { background:var(--color-primary-600); }
.card--quick:hover .arrow-btn .icon { color:var(--color-text-negative); }
/* Touch guard — no sticky hover after a tap (house rule, as on .quick-tile). */
@media (hover:none){
  .card--quick:hover { box-shadow:var(--shadow-lg); }
  .card--quick:hover .card--quick__title { color:var(--color-text); }
  .card--quick:hover .arrow-btn { background:transparent; }
  .card--quick:hover .arrow-btn .icon { color:var(--color-primary-600); }
}
/* Shared quick-card grid — the tenant portal's `.card-grid` (auto-fit on a
   280px floor with the responsive gutter), so both home bands break to the
   same column counts at every width. min() keeps sub-320px viewports from
   overflowing, as on .quick-tile. */
.card-grid { display:grid; gap:var(--gap-responsive);
  grid-template-columns:repeat(auto-fit, minmax(min(17.5rem, 100%), 1fr)); }
